Discussion:
[Intel-gfx] [PATCH v8 00/35] drm/i915: Implement HDCP2.2
Ramalingam C
2018-11-27 10:42:58 UTC
Permalink
This series enables the HDCP2.2 for I915. The sequence for HDCP2.2
authentication and encryption is implemented as a generic flow
between HDMI and DP. Encoder specific implementations are moved
into hdcp_shim.

Intel HWs supports HDCP2.2 through ME FW. Hence this series
introduces a client driver for mei bus, so that for HDCP2.2
authentication, HDCP2.2 stack in I915 can avail the services from
ME FW. To enable this client driver set the config variable
CONFIG_INTEL_MEI_HDCP.

Userspace interface remains unchanged as version agnostic. When
userspace request for HDCP enable, Kernel will detect the HDCP source
and sink's HDCP version(1.4/2.2)capability and enable the best capable
version for that combination.

This series enables the HDCP2.2 for Type0 content strams.

Major changes in v8:
- Mei_hdcp component binding status will impact HDCP2.2 alone.
- hdcp2_check_work is cancelled only when hdcp2 is supported.
- Errors due to sinks are reported as DEBUG logs.
- HDCP2.2 auth will be attempted only if the mei_hdcp interface is up.
- Maintainability ease MEI i/f changes are gathered in single patch.
- intel_hdcp_exit is defined and used.

To ease the review process, series is hosted at
https://github.com/ramalingampc2008/drm-tip.git hdcp2_2_v8

Ramalingam C (33):
drm/i915: debug log for REPLY_ACK missing
drm/i915: Increase timeout for Encrypt status change
linux/mei: Header for mei_hdcp driver interface
drm/i915: Initialize HDCP2.2
drm/i915: MEI interface definition
drm/i915: Enable and Disable of HDCP2.2
drm/i915: Implement HDCP2.2 receiver authentication
drm/i915: Implement HDCP2.2 repeater authentication
drm/i915: Implement HDCP2.2 link integrity check
drm/i915: Handle HDCP2.2 downstream topology change
drm/i915: Check HDCP 1.4 and 2.2 link on CP_IRQ
drm/i915: Implement the HDCP2.2 support for DP
drm/i915: Implement the HDCP2.2 support for HDMI
drm/i915: Add HDCP2.2 support for DP connectors
drm/i915: Add HDCP2.2 support for HDMI connectors
misc/mei/hdcp: Client driver for HDCP application
misc/mei/hdcp: Define ME FW interface for HDCP2.2
misc/mei/hdcp: Initiate Wired HDCP2.2 Tx Session
misc/mei/hdcp: Verify Receiver Cert and prepare km
misc/mei/hdcp: Verify H_prime
misc/mei/hdcp: Store the HDCP Pairing info
misc/mei/hdcp: Initiate Locality check
misc/mei/hdcp: Verify L_prime
misc/mei/hdcp: Prepare Session Key
misc/mei/hdcp: Repeater topology verification and ack
misc/mei/hdcp: Verify M_prime
misc/mei/hdcp: Enabling the HDCP authentication
misc/mei/hdcp: Closing wired HDCP2.2 Tx Session
misc/mei/hdcp: Component framework for I915 Interface
drm/i915: Commit CP without modeset
drm/i915: Fix KBL HDCP2.2 encrypt status signalling
FOR_TEST: i915/Kconfig: Select mei_hdcp by I915
FOR_TESTING_ONLY: debugfs: Excluding the LSPCon for HDCP1.4

Tomas Winkler (2):
mei: bus: whitelist hdcp client
mei: bus: export to_mei_cl_device for mei client device drivers

MAINTAINERS | 1 +
drivers/gpu/drm/i915/i915_debugfs.c | 10 +-
drivers/gpu/drm/i915/i915_drv.c | 2 +
drivers/gpu/drm/i915/i915_drv.h | 2 +
drivers/gpu/drm/i915/intel_ddi.c | 7 -
drivers/gpu/drm/i915/intel_display.c | 21 +-
drivers/gpu/drm/i915/intel_dp.c | 349 ++++++++-
drivers/gpu/drm/i915/intel_drv.h | 81 +-
drivers/gpu/drm/i915/intel_hdcp.c | 1390 +++++++++++++++++++++++++++++++---
drivers/gpu/drm/i915/intel_hdmi.c | 193 ++++-
drivers/misc/mei/Kconfig | 8 +
drivers/misc/mei/Makefile | 2 +
drivers/misc/mei/bus-fixup.c | 16 +
drivers/misc/mei/bus.c | 1 -
drivers/misc/mei/hdcp/Makefile | 7 +
drivers/misc/mei/hdcp/mei_hdcp.c | 825 ++++++++++++++++++++
drivers/misc/mei/hdcp/mei_hdcp.h | 366 +++++++++
include/drm/drm_hdcp.h | 23 +
include/drm/i915_component.h | 71 ++
include/linux/mei_cl_bus.h | 2 +
include/linux/mei_hdcp.h | 91 +++
21 files changed, 3340 insertions(+), 128 deletions(-)
create mode 100644 drivers/misc/mei/hdcp/Makefile
create mode 100644 drivers/misc/mei/hdcp/mei_hdcp.c
create mode 100644 drivers/misc/mei/hdcp/mei_hdcp.h
create mode 100644 include/linux/mei_hdcp.h
--
2.7.4
Ramalingam C
2018-11-27 10:42:59 UTC
Permalink
Adding a DRM_DEBUG log when the DP_AUX_NATIVE_REPLY_ACK is missing
for aksv write.

Signed-off-by: Ramalingam C <***@intel.com>
---
drivers/gpu/drm/i915/intel_dp.c | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)

diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c
index 70ae3d57316b..18e3a5a3d873 100644
--- a/drivers/gpu/drm/i915/intel_dp.c
+++ b/drivers/gpu/drm/i915/intel_dp.c
@@ -5390,7 +5390,11 @@ int intel_dp_hdcp_write_an_aksv(struct intel_digital_port *intel_dig_port,
}

reply = (rxbuf[0] >> 4) & DP_AUX_NATIVE_REPLY_MASK;
- return reply == DP_AUX_NATIVE_REPLY_ACK ? 0 : -EIO;
+ ret = reply == DP_AUX_NATIVE_REPLY_ACK ? 0 : -EIO;
+ if (ret)
+ DRM_DEBUG_KMS("Aksv write: DP_AUX_NATIVE_REPLY_ACK missing\n");
+
+ return ret;
}

static int intel_dp_hdcp_read_bksv(struct intel_digital_port *intel_dig_port,
--
2.7.4
Ramalingam C
2018-11-27 10:43:01 UTC
Permalink
Data structures and Enum for the I915-MEI_HDCP interface are defined
at <linux/mei_hdcp.h>

v2:
Rebased.
v3:
mei_cl_device is removed from mei_hdcp_data [Tomas]
v4:
Comment style and typo fixed [Uma]
v5:
Rebased.
v6:
No changes.
v7:
Remove redundant text from the License header
Change uintXX_t type to uXX_t types
Remove unneeded include to mei_cl_bus.h
Coding style fixed [Uma]
V8:
Tab cleanup
Fix kdoc and namespaces
Update MAINTAINERS

Signed-off-by: Ramalingam C <***@intel.com>
Signed-off-by: Tomas Winkler <***@intel.com>
Reviewed-by: Uma Shankar <***@intel.com>
---
MAINTAINERS | 1 +
include/linux/mei_hdcp.h | 91 ++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 92 insertions(+)
create mode 100644 include/linux/mei_hdcp.h

diff --git a/MAINTAINERS b/MAINTAINERS
index 1026150ae90f..2fd6555bf040 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -7540,6 +7540,7 @@ L: linux-***@vger.kernel.org
S: Supported
F: include/uapi/linux/mei.h
F: include/linux/mei_cl_bus.h
+F: include/linux/mei_hdcp.h
F: drivers/misc/mei/*
F: drivers/watchdog/mei_wdt.c
F: Documentation/misc-devices/mei/*
diff --git a/include/linux/mei_hdcp.h b/include/linux/mei_hdcp.h
new file mode 100644
index 000000000000..716123003dd1
--- /dev/null
+++ b/include/linux/mei_hdcp.h
@@ -0,0 +1,91 @@
+/* SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) */
+/*
+ * Copyright © 2017-2018 Intel Corporation
+ *
+ * Authors:
+ * Ramalingam C <***@intel.com>
+ */
+
+#ifndef _LINUX_MEI_HDCP_H
+#define _LINUX_MEI_HDCP_H
+
+/**
+ * enum mei_hdcp_ddi - The physical digital display interface (DDI)
+ * available on the platform
+ * @MEI_DDI_INVALID_PORT: Not a valid port
+ * @MEI_DDI_RANGE_BEGIN: Beginning of the valid DDI port range
+ * @MEI_DDI_B: Port DDI B
+ * @MEI_DDI_C: Port DDI C
+ * @MEI_DDI_D: Port DDI D
+ * @MEI_DDI_E: Port DDI E
+ * @MEI_DDI_F: Port DDI F
+ * @MEI_DDI_A: Port DDI A
+ * @MEI_DDI_RANGE_END: End of the valid DDI port range
+ */
+enum mei_hdcp_ddi {
+ MEI_DDI_INVALID_PORT = 0x00,
+
+ MEI_DDI_RANGE_BEGIN = 0x01,
+ MEI_DDI_B = 0x01,
+ MEI_DDI_C = 0x02,
+ MEI_DDI_D = 0x03,
+ MEI_DDI_E = 0x04,
+ MEI_DDI_F = 0x05,
+ MEI_DDI_A = 0x07,
+ MEI_DDI_RANGE_END = MEI_DDI_A,
+};
+
+/**
+ * enum mei_hdcp_port_type - The types of HDCP 2.2 ports supported
+ *
+ * @MEI_HDCP_PORT_TYPE_INVALID: Invalid port
+ * @MEI_HDCP_PORT_TYPE_INTEGRATED: ports that are integrated into Intel HW
+ * @MEI_HDCP_PORT_TYPE_LSPCON: discrete wired Tx port with LSPCON (HDMI 2.0)
+ * @MEI_HDCP_PORT_TYPE_CPDP: discrete wired Tx port using the CPDP (DP 1.3)
+ */
+enum mei_hdcp_port_type {
+ MEI_HDCP_PORT_TYPE_INVALID = 0x00,
+ MEI_HDCP_PORT_TYPE_INTEGRATED = 0x01,
+ MEI_HDCP_PORT_TYPE_LSPCON = 0x02,
+ MEI_HDCP_PORT_TYPE_CPDP = 0x03,
+};
+
+/*
+ * enum mei_hdcp_wired_protocol - Supported integrated wired HDCP protocol.
+ * @HDCP_PROTOCOL_INVALID: invalid type
+ * @HDCP_PROTOCOL_HDMI: HDMI
+ * @HDCP_PROTOCOL_DP: DP
+ *
+ * Based on this value, Minor difference needed between wired specifications
+ * are handled.
+ */
+enum mei_hdcp_wired_protocol {
+ MEI_HDCP_PROTOCOL_INVALID,
+ MEI_HDCP_PROTOCOL_HDMI,
+ MEI_HDCP_PROTOCOL_DP
+};
+
+/**
+ * struct mei_hdcp_data - Input data to the mei_hdcp APIs
+ * @port: The physical port (ddi).
+ * @port_type: The port type.
+ * @protocol: The Protocol on the port.
+ * @k: Number of streams transmitted on the port.
+ * In case of HDMI & DP SST, a single stream will be
+ * transmitted on the port.
+ * @seq_num_m: A sequence number of RepeaterAuth_Stream_Manage msg propagated.
+ * Initialized to 0 on AKE_INIT. Incremented after every successful
+ * transmission of RepeaterAuth_Stream_Manage message. When it rolls
+ * over re-Auth has to be triggered.
+ * @streams: array[k] of streamid
+ */
+struct mei_hdcp_data {
+ enum mei_hdcp_ddi port;
+ enum mei_hdcp_port_type port_type;
+ enum mei_hdcp_wired_protocol protocol;
+ u16 k;
+ u32 seq_num_m;
+ struct hdcp2_streamid_type *streams;
+};
+
+#endif /* !_LINUX_MEI_HDCP_H */
--
2.7.4
C, Ramalingam
2018-12-07 13:53:06 UTC
Permalink
Hi,

In one of the offline discussion Tomas has shared his review comments on v8.
So I am sharing the abstract of his suggestions here for the discussion and for the agreement of interface in the community.
Tomas please correct/add if I am missing any points.

1. Remove the include/linux/mei_hdcp.h to make the i915-mei interface
more generic.
1. Move the definition of the struct mei_hdcp_data to i915 and
mei_hdcp.c and pass the void* in the ops' functions.
2. Move the conversion of enum port value to mei_ddi_port value
into mei_hdcp.c. Let I915 pass the enum port value as such.
3. Modified local definition of the struct mei_hdcp_data will looks
like
4.

+/* hdcp data per port */
+struct hdcp_port_data {
+ short int port;
+ u8 port_type;
+ u8 protocol;
+ u16 k;
+ u32 seq_num_m;
+ struct hdcp2_streamid_type *streams;
};

2. Add K-Doc compliant commenting in the mei_hdcp.c

I have implemented these changes and posted for intel-gfx-trybot. Just incase anyone wants to
refer the code please look at https://patchwork.freedesktop.org/series/53655/ .
Not shared on #intel-gfx as further review discussions are on-going on intel-gfx.

--Ram
Post by Ramalingam C
Data structures and Enum for the I915-MEI_HDCP interface are defined
at <linux/mei_hdcp.h>
Rebased.
mei_cl_device is removed from mei_hdcp_data [Tomas]
Comment style and typo fixed [Uma]
Rebased.
No changes.
Remove redundant text from the License header
Change uintXX_t type to uXX_t types
Remove unneeded include to mei_cl_bus.h
Coding style fixed [Uma]
Tab cleanup
Fix kdoc and namespaces
Update MAINTAINERS
---
MAINTAINERS | 1 +
include/linux/mei_hdcp.h | 91 ++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 92 insertions(+)
create mode 100644 include/linux/mei_hdcp.h
diff --git a/MAINTAINERS b/MAINTAINERS
index 1026150ae90f..2fd6555bf040 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -7540,6 +7540,7 @@ L: linux-***@vger.kernel.org
S: Supported
F: include/uapi/linux/mei.h
F: include/linux/mei_cl_bus.h
+F: include/linux/mei_hdcp.h
F: drivers/misc/mei/*
F: drivers/watchdog/mei_wdt.c
F: Documentation/misc-devices/mei/*
diff --git a/include/linux/mei_hdcp.h b/include/linux/mei_hdcp.h
new file mode 100644
index 000000000000..716123003dd1
--- /dev/null
+++ b/include/linux/mei_hdcp.h
@@ -0,0 +1,91 @@
+/* SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) */
+/*
+ * Copyright © 2017-2018 Intel Corporation
+ *
+ */
+
+#ifndef _LINUX_MEI_HDCP_H
+#define _LINUX_MEI_HDCP_H
+
+/**
+ * enum mei_hdcp_ddi - The physical digital display interface (DDI)
+ * available on the platform
+ */
+enum mei_hdcp_ddi {
+ MEI_DDI_INVALID_PORT = 0x00,
+
+ MEI_DDI_RANGE_BEGIN = 0x01,
+ MEI_DDI_B = 0x01,
+ MEI_DDI_C = 0x02,
+ MEI_DDI_D = 0x03,
+ MEI_DDI_E = 0x04,
+ MEI_DDI_F = 0x05,
+ MEI_DDI_A = 0x07,
+ MEI_DDI_RANGE_END = MEI_DDI_A,
+};
+
+/**
+ * enum mei_hdcp_port_type - The types of HDCP 2.2 ports supported
+ *
+ */
+enum mei_hdcp_port_type {
+ MEI_HDCP_PORT_TYPE_INVALID = 0x00,
+ MEI_HDCP_PORT_TYPE_INTEGRATED = 0x01,
+ MEI_HDCP_PORT_TYPE_LSPCON = 0x02,
+ MEI_HDCP_PORT_TYPE_CPDP = 0x03,
+};
+
+/*
+ * enum mei_hdcp_wired_protocol - Supported integrated wired HDCP protocol.
+ *
+ * Based on this value, Minor difference needed between wired specifications
+ * are handled.
+ */
+enum mei_hdcp_wired_protocol {
+ MEI_HDCP_PROTOCOL_INVALID,
+ MEI_HDCP_PROTOCOL_HDMI,
+ MEI_HDCP_PROTOCOL_DP
+};
+
+/**
+ * struct mei_hdcp_data - Input data to the mei_hdcp APIs
+ * In case of HDMI & DP SST, a single stream will be
+ * transmitted on the port.
+ * Initialized to 0 on AKE_INIT. Incremented after every successful
+ * transmission of RepeaterAuth_Stream_Manage message. When it rolls
+ * over re-Auth has to be triggered.
+ */
+struct mei_hdcp_data {
+ enum mei_hdcp_ddi port;
+ enum mei_hdcp_port_type port_type;
+ enum mei_hdcp_wired_protocol protocol;
+ u16 k;
+ u32 seq_num_m;
+ struct hdcp2_streamid_type *streams;
+};
+
+#endif /* !_LINUX_MEI_HDCP_H */
Daniel Vetter
2018-12-07 14:10:29 UTC
Permalink
Post by C, Ramalingam
Hi,
In one of the offline discussion Tomas has shared his review comments on v8.
Let's please have all review here on the mailing list for better
coordination. Playing a game of telephone isn't efficient.
Post by C, Ramalingam
So I am sharing the abstract of his suggestions here for the discussion and for the agreement of interface in the community.
Tomas please correct/add if I am missing any points.
1. Remove the include/linux/mei_hdcp.h to make the i915-mei interface
more generic.
1. Move the definition of the struct mei_hdcp_data to i915 and
mei_hdcp.c and pass the void* in the ops' functions.
I don't get this. Using void * instead of the actual type we're passing
isn't more generic, it's just less safe. If we later on need to extend the
api contract between mei_hdcp and i915 we can always do that. Like we
already do with the i915/snd-hda-intel component contract in
i915_component.h and drm_audio_component.h.

Aside: Header names for the audio interface are maybe not the best, this
isn't primarily a component thing. So maybe call it
i915_mei_hdcp_interface.h and stuff all the structures/function pointers
that define the interface between the two drivers in there. Or some other
suitable name you like better.
Post by C, Ramalingam
2. Move the conversion of enum port value to mei_ddi_port value
into mei_hdcp.c. Let I915 pass the enum port value as such.
logical port 2 physical register index mapping tends to shift around and
is always highly machine specific. As long as we do it consistently
somewhere we should be good. Seems fine to me.
Post by C, Ramalingam
3. Modified local definition of the struct mei_hdcp_data will looks
like
No local defintions of structures please. Otherwise I'm ok with whatever
gets the job done.
Post by C, Ramalingam
4.
+/* hdcp data per port */
+struct hdcp_port_data {
+ short int port;
+ u8 port_type;
+ u8 protocol;
+ u16 k;
+ u32 seq_num_m;
+ struct hdcp2_streamid_type *streams;
};
2. Add K-Doc compliant commenting in the mei_hdcp.c
If you do that, please include the relevant comments into the drm/i915
docbook, like we do already with the audio stuff.
Post by C, Ramalingam
I have implemented these changes and posted for intel-gfx-trybot. Just incase anyone wants to
refer the code please look at https://patchwork.freedesktop.org/series/53655/ .
Not shared on #intel-gfx as further review discussions are on-going on intel-gfx.
As discussed, no void * in the interface, and we definitely need a shared
header for ops/data structures. We want the compiler to help us catch when
one side of this i915/mei_hdcp api contract changes as much as possible.
All the other changes seem reasonable.

Thanks, Daniel
Post by C, Ramalingam
--Ram
Post by Ramalingam C
Data structures and Enum for the I915-MEI_HDCP interface are defined
at <linux/mei_hdcp.h>
Rebased.
mei_cl_device is removed from mei_hdcp_data [Tomas]
Comment style and typo fixed [Uma]
Rebased.
No changes.
Remove redundant text from the License header
Change uintXX_t type to uXX_t types
Remove unneeded include to mei_cl_bus.h
Coding style fixed [Uma]
Tab cleanup
Fix kdoc and namespaces
Update MAINTAINERS
---
MAINTAINERS | 1 +
include/linux/mei_hdcp.h | 91 ++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 92 insertions(+)
create mode 100644 include/linux/mei_hdcp.h
diff --git a/MAINTAINERS b/MAINTAINERS
index 1026150ae90f..2fd6555bf040 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -7540,6 +7540,7 @@ L: linux-***@vger.kernel.org
S: Supported
F: include/uapi/linux/mei.h
F: include/linux/mei_cl_bus.h
+F: include/linux/mei_hdcp.h
F: drivers/misc/mei/*
F: drivers/watchdog/mei_wdt.c
F: Documentation/misc-devices/mei/*
diff --git a/include/linux/mei_hdcp.h b/include/linux/mei_hdcp.h
new file mode 100644
index 000000000000..716123003dd1
--- /dev/null
+++ b/include/linux/mei_hdcp.h
@@ -0,0 +1,91 @@
+/* SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) */
+/*
+ * Copyright © 2017-2018 Intel Corporation
+ *
+ */
+
+#ifndef _LINUX_MEI_HDCP_H
+#define _LINUX_MEI_HDCP_H
+
+/**
+ * enum mei_hdcp_ddi - The physical digital display interface (DDI)
+ * available on the platform
+ */
+enum mei_hdcp_ddi {
+ MEI_DDI_INVALID_PORT = 0x00,
+
+ MEI_DDI_RANGE_BEGIN = 0x01,
+ MEI_DDI_B = 0x01,
+ MEI_DDI_C = 0x02,
+ MEI_DDI_D = 0x03,
+ MEI_DDI_E = 0x04,
+ MEI_DDI_F = 0x05,
+ MEI_DDI_A = 0x07,
+ MEI_DDI_RANGE_END = MEI_DDI_A,
+};
+
+/**
+ * enum mei_hdcp_port_type - The types of HDCP 2.2 ports supported
+ *
+ */
+enum mei_hdcp_port_type {
+ MEI_HDCP_PORT_TYPE_INVALID = 0x00,
+ MEI_HDCP_PORT_TYPE_INTEGRATED = 0x01,
+ MEI_HDCP_PORT_TYPE_LSPCON = 0x02,
+ MEI_HDCP_PORT_TYPE_CPDP = 0x03,
+};
+
+/*
+ * enum mei_hdcp_wired_protocol - Supported integrated wired HDCP protocol.
+ *
+ * Based on this value, Minor difference needed between wired specifications
+ * are handled.
+ */
+enum mei_hdcp_wired_protocol {
+ MEI_HDCP_PROTOCOL_INVALID,
+ MEI_HDCP_PROTOCOL_HDMI,
+ MEI_HDCP_PROTOCOL_DP
+};
+
+/**
+ * struct mei_hdcp_data - Input data to the mei_hdcp APIs
+ * In case of HDMI & DP SST, a single stream will be
+ * transmitted on the port.
+ * Initialized to 0 on AKE_INIT. Incremented after every successful
+ * transmission of RepeaterAuth_Stream_Manage message. When it rolls
+ * over re-Auth has to be triggered.
+ */
+struct mei_hdcp_data {
+ enum mei_hdcp_ddi port;
+ enum mei_hdcp_port_type port_type;
+ enum mei_hdcp_wired_protocol protocol;
+ u16 k;
+ u32 seq_num_m;
+ struct hdcp2_streamid_type *streams;
+};
+
+#endif /* !_LINUX_MEI_HDCP_H */
--
Daniel Vetter
Software Engineer, Intel Corporation
http://blog.ffwll.ch
Winkler, Tomas
2018-12-08 20:15:52 UTC
Permalink
Post by C, Ramalingam
Hi,
In one of the offline discussion Tomas has shared his review comments on v8.
Let's please have all review here on the mailing list for better coordination.
Playing a game of telephone isn't efficient.
It's not different from IRC or meeting on a conference, after all we end up with code we can comment on.
Post by C, Ramalingam
So I am sharing the abstract of his suggestions here for the discussion and for
the agreement of interface in the community.
Post by C, Ramalingam
Tomas please correct/add if I am missing any points.
1. Remove the include/linux/mei_hdcp.h to make the i915-mei interface
more generic.
1. Move the definition of the struct mei_hdcp_data to i915 and
mei_hdcp.c and pass the void* in the ops' functions.
I don't get this. Using void * instead of the actual type we're passing isn't more
generic, it's just less safe. If we later on need to extend the api contract
between mei_hdcp and i915 we can always do that. Like we already do with the
i915/snd-hda-intel component contract in i915_component.h and
drm_audio_component.h.
No I haven't suggesting using void *, what I've suggest is to use HDCP construct instead of mei specific types.
Aside: Header names for the audio interface are maybe not the best, this isn't
primarily a component thing. So maybe call it i915_mei_hdcp_interface.h and
stuff all the structures/function pointers that define the interface between the
two drivers in there. Or some other suitable name you like better.
Post by C, Ramalingam
2. Move the conversion of enum port value to mei_ddi_port value
into mei_hdcp.c. Let I915 pass the enum port value as such.
logical port 2 physical register index mapping tends to shift around and is
always highly machine specific. As long as we do it consistently somewhere we
should be good. Seems fine to me.
Post by C, Ramalingam
3. Modified local definition of the struct mei_hdcp_data will looks
like
No local defintions of structures please. Otherwise I'm ok with whatever gets
the job done.
Post by C, Ramalingam
4.
+/* hdcp data per port */
+struct hdcp_port_data {
+ short int port;
+ u8 port_type;
+ u8 protocol;
+ u16 k;
+ u32 seq_num_m;
+ struct hdcp2_streamid_type *streams;
};
2. Add K-Doc compliant commenting in the mei_hdcp.c
If you do that, please include the relevant comments into the drm/i915
docbook, like we do already with the audio stuff.
Post by C, Ramalingam
I have implemented these changes and posted for intel-gfx-trybot. Just
incase anyone wants to refer the code please look at
https://patchwork.freedesktop.org/series/53655/ .
Post by C, Ramalingam
Not shared on #intel-gfx as further review discussions are on-going on intel-
gfx.
As discussed, no void * in the interface, and we definitely need a shared header
for ops/data structures. We want the compiler to help us catch when one side
of this i915/mei_hdcp api contract changes as much as possible.
All the other changes seem reasonable.
Thanks, Daniel
I agree with no void *, that was not my intention.
It will be better to comment on v9 series.
Post by C, Ramalingam
--Ram
Post by Ramalingam C
Data structures and Enum for the I915-MEI_HDCP interface are defined
at <linux/mei_hdcp.h>
Rebased.
mei_cl_device is removed from mei_hdcp_data [Tomas]
Comment style and typo fixed [Uma]
Rebased.
No changes.
Remove redundant text from the License header
Change uintXX_t type to uXX_t types
Remove unneeded include to mei_cl_bus.h
Coding style fixed [Uma]
Tab cleanup
Fix kdoc and namespaces
Update MAINTAINERS
---
MAINTAINERS | 1 +
include/linux/mei_hdcp.h | 91
++++++++++++++++++++++++++++++++++++++++++++++++
Post by C, Ramalingam
Post by Ramalingam C
2 files changed, 92 insertions(+)
create mode 100644 include/linux/mei_hdcp.h
diff --git a/MAINTAINERS b/MAINTAINERS index
1026150ae90f..2fd6555bf040 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -7540,6 +7540,7 @@ L: linux-***@vger.kernel.org
S: Supported
F: include/uapi/linux/mei.h
F: include/linux/mei_cl_bus.h
+F: include/linux/mei_hdcp.h
F: drivers/misc/mei/*
F: drivers/watchdog/mei_wdt.c
F: Documentation/misc-devices/mei/*
diff --git a/include/linux/mei_hdcp.h b/include/linux/mei_hdcp.h new
file mode 100644 index 000000000000..716123003dd1
--- /dev/null
+++ b/include/linux/mei_hdcp.h
@@ -0,0 +1,91 @@
+/* SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) */
+/*
+ * Copyright (c) 2017-2018 Intel Corporation
+ *
+
+#ifndef _LINUX_MEI_HDCP_H
+#define _LINUX_MEI_HDCP_H
+
+/**
+ * enum mei_hdcp_ddi - The physical digital display interface (DDI)
+ * available on the platform
+mei_hdcp_ddi {
+ MEI_DDI_INVALID_PORT = 0x00,
+
+ MEI_DDI_RANGE_BEGIN = 0x01,
+ MEI_DDI_B = 0x01,
+ MEI_DDI_C = 0x02,
+ MEI_DDI_D = 0x03,
+ MEI_DDI_E = 0x04,
+ MEI_DDI_F = 0x05,
+ MEI_DDI_A = 0x07,
+ MEI_DDI_RANGE_END = MEI_DDI_A,
+};
+
+/**
+ * enum mei_hdcp_port_type - The types of HDCP 2.2 ports supported
+ *
+Intel HW
+(HDMI 2.0)
+(DP 1.3) */ enum mei_hdcp_port_type {
+ MEI_HDCP_PORT_TYPE_INVALID = 0x00,
+ MEI_HDCP_PORT_TYPE_INTEGRATED = 0x01,
+ MEI_HDCP_PORT_TYPE_LSPCON = 0x02,
+ MEI_HDCP_PORT_TYPE_CPDP = 0x03,
+};
+
+/*
+ * enum mei_hdcp_wired_protocol - Supported integrated wired HDCP
protocol.
Post by C, Ramalingam
Post by Ramalingam C
+ *
+ * Based on this value, Minor difference needed between wired
+specifications
+ * are handled.
+ */
+enum mei_hdcp_wired_protocol {
+ MEI_HDCP_PROTOCOL_INVALID,
+ MEI_HDCP_PROTOCOL_HDMI,
+ MEI_HDCP_PROTOCOL_DP
+};
+
+/**
+ * struct mei_hdcp_data - Input data to the mei_hdcp APIs
+ * In case of HDMI & DP SST, a single stream will be
+ * transmitted on the port.
msg propagated.
Post by C, Ramalingam
Post by Ramalingam C
+ * Initialized to 0 on AKE_INIT. Incremented after every successful
+ * transmission of RepeaterAuth_Stream_Manage message. When it
rolls
Post by C, Ramalingam
Post by Ramalingam C
+ * over re-Auth has to be triggered.
+ */
+struct mei_hdcp_data {
+ enum mei_hdcp_ddi port;
+ enum mei_hdcp_port_type port_type;
+ enum mei_hdcp_wired_protocol protocol;
+ u16 k;
+ u32 seq_num_m;
+ struct hdcp2_streamid_type *streams; };
+
+#endif /* !_LINUX_MEI_HDCP_H */
--
Daniel Vetter
Software Engineer, Intel Corporation
http://blog.ffwll.ch
Daniel Vetter
2018-12-10 09:31:24 UTC
Permalink
Post by Winkler, Tomas
Post by C, Ramalingam
Hi,
In one of the offline discussion Tomas has shared his review comments on v8.
Let's please have all review here on the mailing list for better coordination.
Playing a game of telephone isn't efficient.
It's not different from IRC or meeting on a conference, after all we end up with code we can comment on.
For IRC we try to summarize discussions on the mailing list too. Same for
conferences (if that ever happens for public stuff).
Post by Winkler, Tomas
Post by C, Ramalingam
So I am sharing the abstract of his suggestions here for the discussion and for
the agreement of interface in the community.
Post by C, Ramalingam
Tomas please correct/add if I am missing any points.
1. Remove the include/linux/mei_hdcp.h to make the i915-mei interface
more generic.
1. Move the definition of the struct mei_hdcp_data to i915 and
mei_hdcp.c and pass the void* in the ops' functions.
I don't get this. Using void * instead of the actual type we're passing isn't more
generic, it's just less safe. If we later on need to extend the api contract
between mei_hdcp and i915 we can always do that. Like we already do with the
i915/snd-hda-intel component contract in i915_component.h and
drm_audio_component.h.
No I haven't suggesting using void *, what I've suggest is to use HDCP construct instead of mei specific types.
Ah, makes sense. I thought v7 pretty much does that already though, at
least as far as we define these structures on the drm side (it's just a
register file in an i2c target address after all).
-Daniel
Post by Winkler, Tomas
Aside: Header names for the audio interface are maybe not the best, this isn't
primarily a component thing. So maybe call it i915_mei_hdcp_interface.h and
stuff all the structures/function pointers that define the interface between the
two drivers in there. Or some other suitable name you like better.
Post by C, Ramalingam
2. Move the conversion of enum port value to mei_ddi_port value
into mei_hdcp.c. Let I915 pass the enum port value as such.
logical port 2 physical register index mapping tends to shift around and is
always highly machine specific. As long as we do it consistently somewhere we
should be good. Seems fine to me.
Post by C, Ramalingam
3. Modified local definition of the struct mei_hdcp_data will looks
like
No local defintions of structures please. Otherwise I'm ok with whatever gets
the job done.
Post by C, Ramalingam
4.
+/* hdcp data per port */
+struct hdcp_port_data {
+ short int port;
+ u8 port_type;
+ u8 protocol;
+ u16 k;
+ u32 seq_num_m;
+ struct hdcp2_streamid_type *streams;
};
2. Add K-Doc compliant commenting in the mei_hdcp.c
If you do that, please include the relevant comments into the drm/i915
docbook, like we do already with the audio stuff.
Post by C, Ramalingam
I have implemented these changes and posted for intel-gfx-trybot. Just
incase anyone wants to refer the code please look at
https://patchwork.freedesktop.org/series/53655/ .
Post by C, Ramalingam
Not shared on #intel-gfx as further review discussions are on-going on intel-
gfx.
As discussed, no void * in the interface, and we definitely need a shared header
for ops/data structures. We want the compiler to help us catch when one side
of this i915/mei_hdcp api contract changes as much as possible.
All the other changes seem reasonable.
Thanks, Daniel
I agree with no void *, that was not my intention.
It will be better to comment on v9 series.
Post by C, Ramalingam
--Ram
Post by Ramalingam C
Data structures and Enum for the I915-MEI_HDCP interface are defined
at <linux/mei_hdcp.h>
Rebased.
mei_cl_device is removed from mei_hdcp_data [Tomas]
Comment style and typo fixed [Uma]
Rebased.
No changes.
Remove redundant text from the License header
Change uintXX_t type to uXX_t types
Remove unneeded include to mei_cl_bus.h
Coding style fixed [Uma]
Tab cleanup
Fix kdoc and namespaces
Update MAINTAINERS
---
MAINTAINERS | 1 +
include/linux/mei_hdcp.h | 91
++++++++++++++++++++++++++++++++++++++++++++++++
Post by C, Ramalingam
Post by Ramalingam C
2 files changed, 92 insertions(+)
create mode 100644 include/linux/mei_hdcp.h
diff --git a/MAINTAINERS b/MAINTAINERS index
1026150ae90f..2fd6555bf040 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -7540,6 +7540,7 @@ L: linux-***@vger.kernel.org
S: Supported
F: include/uapi/linux/mei.h
F: include/linux/mei_cl_bus.h
+F: include/linux/mei_hdcp.h
F: drivers/misc/mei/*
F: drivers/watchdog/mei_wdt.c
F: Documentation/misc-devices/mei/*
diff --git a/include/linux/mei_hdcp.h b/include/linux/mei_hdcp.h new
file mode 100644 index 000000000000..716123003dd1
--- /dev/null
+++ b/include/linux/mei_hdcp.h
@@ -0,0 +1,91 @@
+/* SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) */
+/*
+ * Copyright (c) 2017-2018 Intel Corporation
+ *
+
+#ifndef _LINUX_MEI_HDCP_H
+#define _LINUX_MEI_HDCP_H
+
+/**
+ * enum mei_hdcp_ddi - The physical digital display interface (DDI)
+ * available on the platform
+mei_hdcp_ddi {
+ MEI_DDI_INVALID_PORT = 0x00,
+
+ MEI_DDI_RANGE_BEGIN = 0x01,
+ MEI_DDI_B = 0x01,
+ MEI_DDI_C = 0x02,
+ MEI_DDI_D = 0x03,
+ MEI_DDI_E = 0x04,
+ MEI_DDI_F = 0x05,
+ MEI_DDI_A = 0x07,
+ MEI_DDI_RANGE_END = MEI_DDI_A,
+};
+
+/**
+ * enum mei_hdcp_port_type - The types of HDCP 2.2 ports supported
+ *
+Intel HW
+(HDMI 2.0)
+(DP 1.3) */ enum mei_hdcp_port_type {
+ MEI_HDCP_PORT_TYPE_INVALID = 0x00,
+ MEI_HDCP_PORT_TYPE_INTEGRATED = 0x01,
+ MEI_HDCP_PORT_TYPE_LSPCON = 0x02,
+ MEI_HDCP_PORT_TYPE_CPDP = 0x03,
+};
+
+/*
+ * enum mei_hdcp_wired_protocol - Supported integrated wired HDCP
protocol.
Post by C, Ramalingam
Post by Ramalingam C
+ *
+ * Based on this value, Minor difference needed between wired
+specifications
+ * are handled.
+ */
+enum mei_hdcp_wired_protocol {
+ MEI_HDCP_PROTOCOL_INVALID,
+ MEI_HDCP_PROTOCOL_HDMI,
+ MEI_HDCP_PROTOCOL_DP
+};
+
+/**
+ * struct mei_hdcp_data - Input data to the mei_hdcp APIs
+ * In case of HDMI & DP SST, a single stream will be
+ * transmitted on the port.
msg propagated.
Post by C, Ramalingam
Post by Ramalingam C
+ * Initialized to 0 on AKE_INIT. Incremented after every successful
+ * transmission of RepeaterAuth_Stream_Manage message. When it
rolls
Post by C, Ramalingam
Post by Ramalingam C
+ * over re-Auth has to be triggered.
+ */
+struct mei_hdcp_data {
+ enum mei_hdcp_ddi port;
+ enum mei_hdcp_port_type port_type;
+ enum mei_hdcp_wired_protocol protocol;
+ u16 k;
+ u32 seq_num_m;
+ struct hdcp2_streamid_type *streams; };
+
+#endif /* !_LINUX_MEI_HDCP_H */
--
Daniel Vetter
Software Engineer, Intel Corporation
http://blog.ffwll.ch
--
Daniel Vetter
Software Engineer, Intel Corporation
http://blog.ffwll.ch
Ramalingam C
2018-11-27 10:43:00 UTC
Permalink
At enable/disable of the HDCP encryption, for encryption status change
we need minimum one frame duration. And we might program this bit any
point(start/End) in the previous frame.

With 20mSec, observed the timeout for change in encryption status.
Since this is not time critical operation and we need to hold on
until the status is changed, fixing the timeout to 50mSec. (Based on
trial and error method!)

Signed-off-by: Ramalingam C <***@intel.com>
---
drivers/gpu/drm/i915/intel_hdcp.c | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)

diff --git a/drivers/gpu/drm/i915/intel_hdcp.c b/drivers/gpu/drm/i915/intel_hdcp.c
index 1bf487f94254..04ba6c13e715 100644
--- a/drivers/gpu/drm/i915/intel_hdcp.c
+++ b/drivers/gpu/drm/i915/intel_hdcp.c
@@ -15,6 +15,7 @@
#include "i915_reg.h"

#define KEY_LOAD_TRIES 5
+#define TIME_FOR_ENCRYPT_STATUS_CHANGE 50

static
bool intel_hdcp_is_ksv_valid(u8 *ksv)
@@ -636,7 +637,8 @@ static int intel_hdcp_auth(struct intel_digital_port *intel_dig_port,

/* Wait for encryption confirmation */
if (intel_wait_for_register(dev_priv, PORT_HDCP_STATUS(port),
- HDCP_STATUS_ENC, HDCP_STATUS_ENC, 20)) {
+ HDCP_STATUS_ENC, HDCP_STATUS_ENC,
+ TIME_FOR_ENCRYPT_STATUS_CHANGE)) {
DRM_ERROR("Timed out waiting for encryption\n");
return -ETIMEDOUT;
}
@@ -666,7 +668,7 @@ static int _intel_hdcp_disable(struct intel_connector *connector)

I915_WRITE(PORT_HDCP_CONF(port), 0);
if (intel_wait_for_register(dev_priv, PORT_HDCP_STATUS(port), ~0, 0,
- 20)) {
+ TIME_FOR_ENCRYPT_STATUS_CHANGE)) {
DRM_ERROR("Failed to disable HDCP, timeout clearing status\n");
return -ETIMEDOUT;
}
--
2.7.4
Ramalingam C
2018-11-27 10:43:02 UTC
Permalink
Add the HDCP2.2 initialization to the existing HDCP1.4 stack.

v2:
mei interface handle is protected with mutex. [Chris Wilson]
v3:
Notifiers are used for the mei interface state.
v4:
Poll for mei client device state
Error msg for out of mem [Uma]
Inline req for init function removed [Uma]
v5:
Rebase as Part of reordering.
Component is used for the I915 and MEI_HDCP interface [Daniel]
v6:
HDCP2.2 uses the I915 component master to communicate with mei_hdcp
- [Daniel]
Required HDCP2.2 variables defined [Sean Paul]
v7:
intel_hdcp2.2_init returns void [Uma]
Realigning the codes.
v8:
Avoid using bool structure members.
MEI interface related changes are moved into separate patch.
Commit msg is updated accordingly.
intel_hdcp_exit is defined and used from i915_unload

Signed-off-by: Ramalingam C <***@intel.com>
---
drivers/gpu/drm/i915/i915_drv.c | 2 +
drivers/gpu/drm/i915/intel_dp.c | 3 +-
drivers/gpu/drm/i915/intel_drv.h | 16 +++-
drivers/gpu/drm/i915/intel_hdcp.c | 172 ++++++++++++++++++++++++--------------
drivers/gpu/drm/i915/intel_hdmi.c | 2 +-
5 files changed, 130 insertions(+), 65 deletions(-)

diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c
index b1d23c73c147..fbedd5024afe 100644
--- a/drivers/gpu/drm/i915/i915_drv.c
+++ b/drivers/gpu/drm/i915/i915_drv.c
@@ -1755,6 +1755,8 @@ void i915_driver_unload(struct drm_device *dev)

disable_rpm_wakeref_asserts(dev_priv);

+ intel_hdcp_exit(dev_priv);
+
i915_driver_unregister(dev_priv);

if (i915_gem_suspend(dev_priv))
diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c
index 18e3a5a3d873..ac62af073688 100644
--- a/drivers/gpu/drm/i915/intel_dp.c
+++ b/drivers/gpu/drm/i915/intel_dp.c
@@ -6675,7 +6675,8 @@ intel_dp_init_connector(struct intel_digital_port *intel_dig_port,
intel_dp_add_properties(intel_dp, connector);

if (is_hdcp_supported(dev_priv, port) && !intel_dp_is_edp(intel_dp)) {
- int ret = intel_hdcp_init(intel_connector, &intel_dp_hdcp_shim);
+ int ret = intel_hdcp_init(intel_connector, &intel_dp_hdcp_shim,
+ false);
if (ret)
DRM_DEBUG_KMS("HDCP init failed, skipping.\n");
}
diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h
index a62d77b76291..85a526598096 100644
--- a/drivers/gpu/drm/i915/intel_drv.h
+++ b/drivers/gpu/drm/i915/intel_drv.h
@@ -388,6 +388,17 @@ struct intel_hdcp {
u64 value;
struct delayed_work check_work;
struct work_struct prop_work;
+
+ /* HDCP2.2 related definitions */
+ /* Flag indicates whether this connector supports HDCP2.2 or not. */
+ u8 hdcp2_supported;
+
+ /*
+ * Content Stream Type defined by content owner. TYPE0(0x0) content can
+ * flow in the link protected by HDCP2.2 or HDCP1.4, where as TYPE1(0x1)
+ * content can flow only through a link protected by HDCP2.2.
+ */
+ u8 content_type;
};

struct intel_connector {
@@ -2016,12 +2027,15 @@ void intel_hdcp_atomic_check(struct drm_connector *connector,
struct drm_connector_state *old_state,
struct drm_connector_state *new_state);
int intel_hdcp_init(struct intel_connector *connector,
- const struct intel_hdcp_shim *hdcp_shim);
+ const struct intel_hdcp_shim *hdcp_shim,
+ bool hdcp2_supported);
int intel_hdcp_enable(struct intel_connector *connector);
int intel_hdcp_disable(struct intel_connector *connector);
int intel_hdcp_check_link(struct intel_connector *connector);
bool is_hdcp_supported(struct drm_i915_private *dev_priv, enum port port);
bool intel_hdcp_capable(struct intel_connector *connector);
+bool is_hdcp2_supported(struct drm_i915_private *dev_priv);
+void intel_hdcp_exit(struct drm_i915_private *dev_priv);

/* intel_psr.c */
#define CAN_PSR(dev_priv) (HAS_PSR(dev_priv) && dev_priv->psr.sink_support)
diff --git a/drivers/gpu/drm/i915/intel_hdcp.c b/drivers/gpu/drm/i915/intel_hdcp.c
index 04ba6c13e715..99dddb540958 100644
--- a/drivers/gpu/drm/i915/intel_hdcp.c
+++ b/drivers/gpu/drm/i915/intel_hdcp.c
@@ -730,6 +730,65 @@ struct intel_connector *intel_hdcp_to_connector(struct intel_hdcp *hdcp)
return container_of(hdcp, struct intel_connector, hdcp);
}

+/* Implements Part 3 of the HDCP authorization procedure */
+int intel_hdcp_check_link(struct intel_connector *connector)
+{
+ struct intel_hdcp *hdcp = &connector->hdcp;
+ 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 (!hdcp->shim)
+ return -ENOENT;
+
+ mutex_lock(&hdcp->mutex);
+
+ if (hdcp->value == DRM_MODE_CONTENT_PROTECTION_UNDESIRED)
+ goto out;
+
+ if (!(I915_READ(PORT_HDCP_STATUS(port)) & HDCP_STATUS_ENC)) {
+ DRM_ERROR("%s:%d HDCP check failed: link is not encrypted,%x\n",
+ connector->base.name, connector->base.base.id,
+ I915_READ(PORT_HDCP_STATUS(port)));
+ ret = -ENXIO;
+ hdcp->value = DRM_MODE_CONTENT_PROTECTION_DESIRED;
+ schedule_work(&hdcp->prop_work);
+ goto out;
+ }
+
+ if (hdcp->shim->check_link(intel_dig_port)) {
+ if (hdcp->value != DRM_MODE_CONTENT_PROTECTION_UNDESIRED) {
+ hdcp->value = DRM_MODE_CONTENT_PROTECTION_ENABLED;
+ schedule_work(&hdcp->prop_work);
+ }
+ goto out;
+ }
+
+ DRM_DEBUG_KMS("[%s:%d] HDCP link failed, retrying authentication\n",
+ connector->base.name, connector->base.base.id);
+
+ ret = _intel_hdcp_disable(connector);
+ if (ret) {
+ DRM_ERROR("Failed to disable hdcp (%d)\n", ret);
+ hdcp->value = DRM_MODE_CONTENT_PROTECTION_DESIRED;
+ schedule_work(&hdcp->prop_work);
+ goto out;
+ }
+
+ ret = _intel_hdcp_enable(connector);
+ if (ret) {
+ DRM_ERROR("Failed to enable hdcp (%d)\n", ret);
+ hdcp->value = DRM_MODE_CONTENT_PROTECTION_DESIRED;
+ schedule_work(&hdcp->prop_work);
+ goto out;
+ }
+
+out:
+ mutex_unlock(&hdcp->mutex);
+ return ret;
+}
+
static void intel_hdcp_check_work(struct work_struct *work)
{
struct intel_hdcp *hdcp = container_of(to_delayed_work(work),
@@ -774,14 +833,34 @@ bool is_hdcp_supported(struct drm_i915_private *dev_priv, enum port port)
!IS_CHERRYVIEW(dev_priv) && port < PORT_E);
}

+bool is_hdcp2_supported(struct drm_i915_private *dev_priv)
+{
+ return ((INTEL_GEN(dev_priv) >= 10 || IS_GEMINILAKE(dev_priv) ||
+ IS_KABYLAKE(dev_priv)) && IS_ENABLED(CONFIG_INTEL_MEI_HDCP));
+}
+
+static void intel_hdcp2_init(struct intel_connector *connector)
+{
+ struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
+ struct intel_hdcp *hdcp = &connector->hdcp;
+
+ WARN_ON(!is_hdcp2_supported(dev_priv));
+
+ /* TODO: MEI interface needs to be initialized here */
+ hdcp->hdcp2_supported = 1;
+}
+
int intel_hdcp_init(struct intel_connector *connector,
- const struct intel_hdcp_shim *shim)
+ const struct intel_hdcp_shim *shim,
+ bool hdcp2_supported)
{
struct intel_hdcp *hdcp = &connector->hdcp;
int ret;

- ret = drm_connector_attach_content_protection_property(
- &connector->base);
+ if (!shim)
+ return -EINVAL;
+
+ ret = drm_connector_attach_content_protection_property(&connector->base);
if (ret)
return ret;

@@ -789,9 +868,37 @@ int intel_hdcp_init(struct intel_connector *connector,
mutex_init(&hdcp->mutex);
INIT_DELAYED_WORK(&hdcp->check_work, intel_hdcp_check_work);
INIT_WORK(&hdcp->prop_work, intel_hdcp_prop_work);
+
+ if (hdcp2_supported)
+ intel_hdcp2_init(connector);
+
return 0;
}

+void intel_hdcp_exit(struct drm_i915_private *dev_priv)
+{
+ struct drm_device *dev = &dev_priv->drm;
+ struct intel_connector *intel_connector;
+ struct drm_connector *connector;
+ struct drm_connector_list_iter conn_iter;
+
+ drm_connector_list_iter_begin(dev, &conn_iter);
+ drm_for_each_connector_iter(connector, &conn_iter) {
+ intel_connector = to_intel_connector(connector);
+
+ if (!intel_connector->hdcp.shim)
+ continue;
+
+ intel_hdcp_disable(intel_connector);
+
+ mutex_lock(&intel_connector->hdcp.mutex);
+ intel_connector->hdcp.hdcp2_supported = 0;
+ intel_connector->hdcp.shim = NULL;
+ mutex_unlock(&intel_connector->hdcp.mutex);
+ }
+ drm_connector_list_iter_end(&conn_iter);
+}
+
int intel_hdcp_enable(struct intel_connector *connector)
{
struct intel_hdcp *hdcp = &connector->hdcp;
@@ -867,62 +974,3 @@ void intel_hdcp_atomic_check(struct drm_connector *connector,
new_state->crtc);
crtc_state->mode_changed = true;
}
-
-/* Implements Part 3 of the HDCP authorization procedure */
-int intel_hdcp_check_link(struct intel_connector *connector)
-{
- struct intel_hdcp *hdcp = &connector->hdcp;
- 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 (!hdcp->shim)
- return -ENOENT;
-
- mutex_lock(&hdcp->mutex);
-
- if (hdcp->value == DRM_MODE_CONTENT_PROTECTION_UNDESIRED)
- goto out;
-
- if (!(I915_READ(PORT_HDCP_STATUS(port)) & HDCP_STATUS_ENC)) {
- DRM_ERROR("%s:%d HDCP check failed: link is not encrypted,%x\n",
- connector->base.name, connector->base.base.id,
- I915_READ(PORT_HDCP_STATUS(port)));
- ret = -ENXIO;
- hdcp->value = DRM_MODE_CONTENT_PROTECTION_DESIRED;
- schedule_work(&hdcp->prop_work);
- goto out;
- }
-
- if (hdcp->shim->check_link(intel_dig_port)) {
- if (hdcp->value != DRM_MODE_CONTENT_PROTECTION_UNDESIRED) {
- hdcp->value = DRM_MODE_CONTENT_PROTECTION_ENABLED;
- schedule_work(&hdcp->prop_work);
- }
- goto out;
- }
-
- DRM_DEBUG_KMS("[%s:%d] HDCP link failed, retrying authentication\n",
- connector->base.name, connector->base.base.id);
-
- ret = _intel_hdcp_disable(connector);
- if (ret) {
- DRM_ERROR("Failed to disable hdcp (%d)\n", ret);
- hdcp->value = DRM_MODE_CONTENT_PROTECTION_DESIRED;
- schedule_work(&hdcp->prop_work);
- goto out;
- }
-
- ret = _intel_hdcp_enable(connector);
- if (ret) {
- DRM_DEBUG_KMS("Failed to enable hdcp (%d)\n", ret);
- hdcp->value = DRM_MODE_CONTENT_PROTECTION_DESIRED;
- schedule_work(&hdcp->prop_work);
- goto out;
- }
-
-out:
- mutex_unlock(&hdcp->mutex);
- return ret;
-}
diff --git a/drivers/gpu/drm/i915/intel_hdmi.c b/drivers/gpu/drm/i915/intel_hdmi.c
index e2c6a2b3e8f2..e755a3370bca 100644
--- a/drivers/gpu/drm/i915/intel_hdmi.c
+++ b/drivers/gpu/drm/i915/intel_hdmi.c
@@ -2417,7 +2417,7 @@ void intel_hdmi_init_connector(struct intel_digital_port *intel_dig_port,

if (is_hdcp_supported(dev_priv, port)) {
int ret = intel_hdcp_init(intel_connector,
- &intel_hdmi_hdcp_shim);
+ &intel_hdmi_hdcp_shim, false);
if (ret)
DRM_DEBUG_KMS("HDCP init failed, skipping.\n");
}
--
2.7.4
Daniel Vetter
2018-12-06 10:03:17 UTC
Permalink
Post by Ramalingam C
Add the HDCP2.2 initialization to the existing HDCP1.4 stack.
With the comments below addressed the commit message is a bit untrue,
since this just wires up a basic hdcp2_supported flag in a few places.
Please make that clear.
Post by Ramalingam C
mei interface handle is protected with mutex. [Chris Wilson]
Notifiers are used for the mei interface state.
Poll for mei client device state
Error msg for out of mem [Uma]
Inline req for init function removed [Uma]
Rebase as Part of reordering.
Component is used for the I915 and MEI_HDCP interface [Daniel]
HDCP2.2 uses the I915 component master to communicate with mei_hdcp
- [Daniel]
Required HDCP2.2 variables defined [Sean Paul]
intel_hdcp2.2_init returns void [Uma]
Realigning the codes.
Avoid using bool structure members.
MEI interface related changes are moved into separate patch.
Commit msg is updated accordingly.
intel_hdcp_exit is defined and used from i915_unload
---
drivers/gpu/drm/i915/i915_drv.c | 2 +
drivers/gpu/drm/i915/intel_dp.c | 3 +-
drivers/gpu/drm/i915/intel_drv.h | 16 +++-
drivers/gpu/drm/i915/intel_hdcp.c | 172 ++++++++++++++++++++++++--------------
drivers/gpu/drm/i915/intel_hdmi.c | 2 +-
5 files changed, 130 insertions(+), 65 deletions(-)
diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c
index b1d23c73c147..fbedd5024afe 100644
--- a/drivers/gpu/drm/i915/i915_drv.c
+++ b/drivers/gpu/drm/i915/i915_drv.c
@@ -1755,6 +1755,8 @@ void i915_driver_unload(struct drm_device *dev)
disable_rpm_wakeref_asserts(dev_priv);
+ intel_hdcp_exit(dev_priv);
This smells like a separate patch. Needs to be split out. Looking at the
implementation of intel_hdcp_exit I think it's papering over some unload
trouble. We should be shutting down all the outputs on driver unload,
which mei should be triggering (with the component stuff), which means
this code should be unecessary. But I'm not sure.

Either way needs to be split out, but I think proper solution is to drop
it.
Post by Ramalingam C
+
i915_driver_unregister(dev_priv);
if (i915_gem_suspend(dev_priv))
diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c
index 18e3a5a3d873..ac62af073688 100644
--- a/drivers/gpu/drm/i915/intel_dp.c
+++ b/drivers/gpu/drm/i915/intel_dp.c
@@ -6675,7 +6675,8 @@ intel_dp_init_connector(struct intel_digital_port *intel_dig_port,
intel_dp_add_properties(intel_dp, connector);
if (is_hdcp_supported(dev_priv, port) && !intel_dp_is_edp(intel_dp)) {
- int ret = intel_hdcp_init(intel_connector, &intel_dp_hdcp_shim);
+ int ret = intel_hdcp_init(intel_connector, &intel_dp_hdcp_shim,
+ false);
if (ret)
DRM_DEBUG_KMS("HDCP init failed, skipping.\n");
}
diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h
index a62d77b76291..85a526598096 100644
--- a/drivers/gpu/drm/i915/intel_drv.h
+++ b/drivers/gpu/drm/i915/intel_drv.h
@@ -388,6 +388,17 @@ struct intel_hdcp {
u64 value;
struct delayed_work check_work;
struct work_struct prop_work;
+
+ /* HDCP2.2 related definitions */
+ /* Flag indicates whether this connector supports HDCP2.2 or not. */
+ u8 hdcp2_supported;
+
+ /*
+ * Content Stream Type defined by content owner. TYPE0(0x0) content can
+ * flow in the link protected by HDCP2.2 or HDCP1.4, where as TYPE1(0x1)
+ * content can flow only through a link protected by HDCP2.2.
+ */
+ u8 content_type;
};
struct intel_connector {
@@ -2016,12 +2027,15 @@ void intel_hdcp_atomic_check(struct drm_connector *connector,
struct drm_connector_state *old_state,
struct drm_connector_state *new_state);
int intel_hdcp_init(struct intel_connector *connector,
- const struct intel_hdcp_shim *hdcp_shim);
+ const struct intel_hdcp_shim *hdcp_shim,
+ bool hdcp2_supported);
int intel_hdcp_enable(struct intel_connector *connector);
int intel_hdcp_disable(struct intel_connector *connector);
int intel_hdcp_check_link(struct intel_connector *connector);
bool is_hdcp_supported(struct drm_i915_private *dev_priv, enum port port);
bool intel_hdcp_capable(struct intel_connector *connector);
+bool is_hdcp2_supported(struct drm_i915_private *dev_priv);
+void intel_hdcp_exit(struct drm_i915_private *dev_priv);
/* intel_psr.c */
#define CAN_PSR(dev_priv) (HAS_PSR(dev_priv) && dev_priv->psr.sink_support)
diff --git a/drivers/gpu/drm/i915/intel_hdcp.c b/drivers/gpu/drm/i915/intel_hdcp.c
index 04ba6c13e715..99dddb540958 100644
--- a/drivers/gpu/drm/i915/intel_hdcp.c
+++ b/drivers/gpu/drm/i915/intel_hdcp.c
@@ -730,6 +730,65 @@ struct intel_connector *intel_hdcp_to_connector(struct intel_hdcp *hdcp)
return container_of(hdcp, struct intel_connector, hdcp);
}
+/* Implements Part 3 of the HDCP authorization procedure */
+int intel_hdcp_check_link(struct intel_connector *connector)
Spurious movement of this function. Please drop, or if you need this
function moved, split it out into a separate patch that only moves the
function around.
Post by Ramalingam C
+{
+ struct intel_hdcp *hdcp = &connector->hdcp;
+ 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 (!hdcp->shim)
+ return -ENOENT;
+
+ mutex_lock(&hdcp->mutex);
+
+ if (hdcp->value == DRM_MODE_CONTENT_PROTECTION_UNDESIRED)
+ goto out;
+
+ if (!(I915_READ(PORT_HDCP_STATUS(port)) & HDCP_STATUS_ENC)) {
+ DRM_ERROR("%s:%d HDCP check failed: link is not encrypted,%x\n",
+ connector->base.name, connector->base.base.id,
+ I915_READ(PORT_HDCP_STATUS(port)));
+ ret = -ENXIO;
+ hdcp->value = DRM_MODE_CONTENT_PROTECTION_DESIRED;
+ schedule_work(&hdcp->prop_work);
+ goto out;
+ }
+
+ if (hdcp->shim->check_link(intel_dig_port)) {
+ if (hdcp->value != DRM_MODE_CONTENT_PROTECTION_UNDESIRED) {
+ hdcp->value = DRM_MODE_CONTENT_PROTECTION_ENABLED;
+ schedule_work(&hdcp->prop_work);
+ }
+ goto out;
+ }
+
+ DRM_DEBUG_KMS("[%s:%d] HDCP link failed, retrying authentication\n",
+ connector->base.name, connector->base.base.id);
+
+ ret = _intel_hdcp_disable(connector);
+ if (ret) {
+ DRM_ERROR("Failed to disable hdcp (%d)\n", ret);
+ hdcp->value = DRM_MODE_CONTENT_PROTECTION_DESIRED;
+ schedule_work(&hdcp->prop_work);
+ goto out;
+ }
+
+ ret = _intel_hdcp_enable(connector);
+ if (ret) {
+ DRM_ERROR("Failed to enable hdcp (%d)\n", ret);
+ hdcp->value = DRM_MODE_CONTENT_PROTECTION_DESIRED;
+ schedule_work(&hdcp->prop_work);
+ goto out;
+ }
+
+ mutex_unlock(&hdcp->mutex);
+ return ret;
+}
+
static void intel_hdcp_check_work(struct work_struct *work)
{
struct intel_hdcp *hdcp = container_of(to_delayed_work(work),
@@ -774,14 +833,34 @@ bool is_hdcp_supported(struct drm_i915_private *dev_priv, enum port port)
!IS_CHERRYVIEW(dev_priv) && port < PORT_E);
}
+bool is_hdcp2_supported(struct drm_i915_private *dev_priv)
+{
+ return ((INTEL_GEN(dev_priv) >= 10 || IS_GEMINILAKE(dev_priv) ||
+ IS_KABYLAKE(dev_priv)) && IS_ENABLED(CONFIG_INTEL_MEI_HDCP));
+}
+
+static void intel_hdcp2_init(struct intel_connector *connector)
+{
+ struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
+ struct intel_hdcp *hdcp = &connector->hdcp;
+
+ WARN_ON(!is_hdcp2_supported(dev_priv));
+
+ /* TODO: MEI interface needs to be initialized here */
+ hdcp->hdcp2_supported = 1;
+}
+
int intel_hdcp_init(struct intel_connector *connector,
- const struct intel_hdcp_shim *shim)
+ const struct intel_hdcp_shim *shim,
+ bool hdcp2_supported)
{
struct intel_hdcp *hdcp = &connector->hdcp;
int ret;
- ret = drm_connector_attach_content_protection_property(
- &connector->base);
+ if (!shim)
+ return -EINVAL;
+
+ ret = drm_connector_attach_content_protection_property(&connector->base);
if (ret)
return ret;
@@ -789,9 +868,37 @@ int intel_hdcp_init(struct intel_connector *connector,
mutex_init(&hdcp->mutex);
INIT_DELAYED_WORK(&hdcp->check_work, intel_hdcp_check_work);
INIT_WORK(&hdcp->prop_work, intel_hdcp_prop_work);
+
+ if (hdcp2_supported)
+ intel_hdcp2_init(connector);
+
return 0;
}
+void intel_hdcp_exit(struct drm_i915_private *dev_priv)
+{
+ struct drm_device *dev = &dev_priv->drm;
+ struct intel_connector *intel_connector;
+ struct drm_connector *connector;
+ struct drm_connector_list_iter conn_iter;
+
+ drm_connector_list_iter_begin(dev, &conn_iter);
+ drm_for_each_connector_iter(connector, &conn_iter) {
+ intel_connector = to_intel_connector(connector);
+
+ if (!intel_connector->hdcp.shim)
+ continue;
+
+ intel_hdcp_disable(intel_connector);
+
+ mutex_lock(&intel_connector->hdcp.mutex);
+ intel_connector->hdcp.hdcp2_supported = 0;
+ intel_connector->hdcp.shim = NULL;
+ mutex_unlock(&intel_connector->hdcp.mutex);
+ }
+ drm_connector_list_iter_end(&conn_iter);
+}
+
int intel_hdcp_enable(struct intel_connector *connector)
{
struct intel_hdcp *hdcp = &connector->hdcp;
@@ -867,62 +974,3 @@ void intel_hdcp_atomic_check(struct drm_connector *connector,
new_state->crtc);
crtc_state->mode_changed = true;
}
-
-/* Implements Part 3 of the HDCP authorization procedure */
-int intel_hdcp_check_link(struct intel_connector *connector)
-{
- struct intel_hdcp *hdcp = &connector->hdcp;
- 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 (!hdcp->shim)
- return -ENOENT;
-
- mutex_lock(&hdcp->mutex);
-
- if (hdcp->value == DRM_MODE_CONTENT_PROTECTION_UNDESIRED)
- goto out;
-
- if (!(I915_READ(PORT_HDCP_STATUS(port)) & HDCP_STATUS_ENC)) {
- DRM_ERROR("%s:%d HDCP check failed: link is not encrypted,%x\n",
- connector->base.name, connector->base.base.id,
- I915_READ(PORT_HDCP_STATUS(port)));
- ret = -ENXIO;
- hdcp->value = DRM_MODE_CONTENT_PROTECTION_DESIRED;
- schedule_work(&hdcp->prop_work);
- goto out;
- }
-
- if (hdcp->shim->check_link(intel_dig_port)) {
- if (hdcp->value != DRM_MODE_CONTENT_PROTECTION_UNDESIRED) {
- hdcp->value = DRM_MODE_CONTENT_PROTECTION_ENABLED;
- schedule_work(&hdcp->prop_work);
- }
- goto out;
- }
-
- DRM_DEBUG_KMS("[%s:%d] HDCP link failed, retrying authentication\n",
- connector->base.name, connector->base.base.id);
-
- ret = _intel_hdcp_disable(connector);
- if (ret) {
- DRM_ERROR("Failed to disable hdcp (%d)\n", ret);
- hdcp->value = DRM_MODE_CONTENT_PROTECTION_DESIRED;
- schedule_work(&hdcp->prop_work);
- goto out;
- }
-
- ret = _intel_hdcp_enable(connector);
- if (ret) {
- DRM_DEBUG_KMS("Failed to enable hdcp (%d)\n", ret);
- hdcp->value = DRM_MODE_CONTENT_PROTECTION_DESIRED;
- schedule_work(&hdcp->prop_work);
- goto out;
- }
-
- mutex_unlock(&hdcp->mutex);
- return ret;
-}
diff --git a/drivers/gpu/drm/i915/intel_hdmi.c b/drivers/gpu/drm/i915/intel_hdmi.c
index e2c6a2b3e8f2..e755a3370bca 100644
--- a/drivers/gpu/drm/i915/intel_hdmi.c
+++ b/drivers/gpu/drm/i915/intel_hdmi.c
@@ -2417,7 +2417,7 @@ void intel_hdmi_init_connector(struct intel_digital_port *intel_dig_port,
if (is_hdcp_supported(dev_priv, port)) {
int ret = intel_hdcp_init(intel_connector,
- &intel_hdmi_hdcp_shim);
+ &intel_hdmi_hdcp_shim, false);
if (ret)
DRM_DEBUG_KMS("HDCP init failed, skipping.\n");
The actual change left to wire hdcp2_supported seems ok with me, but hard
to tell without seeing more yet :-)
-Daniel
Post by Ramalingam C
}
--
2.7.4
--
Daniel Vetter
Software Engineer, Intel Corporation
http://blog.ffwll.ch
C, Ramalingam
2018-12-07 04:54:26 UTC
Permalink
Post by Daniel Vetter
Post by Ramalingam C
Add the HDCP2.2 initialization to the existing HDCP1.4 stack.
With the comments below addressed the commit message is a bit untrue,
since this just wires up a basic hdcp2_supported flag in a few places.
Please make that clear.
Post by Ramalingam C
mei interface handle is protected with mutex. [Chris Wilson]
Notifiers are used for the mei interface state.
Poll for mei client device state
Error msg for out of mem [Uma]
Inline req for init function removed [Uma]
Rebase as Part of reordering.
Component is used for the I915 and MEI_HDCP interface [Daniel]
HDCP2.2 uses the I915 component master to communicate with mei_hdcp
- [Daniel]
Required HDCP2.2 variables defined [Sean Paul]
intel_hdcp2.2_init returns void [Uma]
Realigning the codes.
Avoid using bool structure members.
MEI interface related changes are moved into separate patch.
Commit msg is updated accordingly.
intel_hdcp_exit is defined and used from i915_unload
---
drivers/gpu/drm/i915/i915_drv.c | 2 +
drivers/gpu/drm/i915/intel_dp.c | 3 +-
drivers/gpu/drm/i915/intel_drv.h | 16 +++-
drivers/gpu/drm/i915/intel_hdcp.c | 172 ++++++++++++++++++++++++--------------
drivers/gpu/drm/i915/intel_hdmi.c | 2 +-
5 files changed, 130 insertions(+), 65 deletions(-)
diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c
index b1d23c73c147..fbedd5024afe 100644
--- a/drivers/gpu/drm/i915/i915_drv.c
+++ b/drivers/gpu/drm/i915/i915_drv.c
@@ -1755,6 +1755,8 @@ void i915_driver_unload(struct drm_device *dev)
disable_rpm_wakeref_asserts(dev_priv);
+ intel_hdcp_exit(dev_priv);
This smells like a separate patch. Needs to be split out. Looking at the
implementation of intel_hdcp_exit I think it's papering over some unload
trouble. We should be shutting down all the outputs on driver unload,
which mei should be triggering (with the component stuff), which means
this code should be unecessary. But I'm not sure.
Either way needs to be split out, but I think proper solution is to drop
it.
As we discussed, during v7-->v8 i changed the component usage such that it wont affect i915 load/unload.
During the first connector init, component master will be added. And during the mei_client dev and driver binding,
component will be added hence the binding will happen with interface initialization from mei.

Upon HDCP2.2 request, component binding will be checked before attempting for HDCP2.2 auth.
So component master unbind triggered due to mei component_del, will teardown the HDCP2.2 capability of the I915.

So in case of I915 unload trigger, from whatsoever reason, we need to clear the HDCP activities and bring down
the i915_hdcp_component_master and the interface with mei. For this purpose only intel_hdcp_exit is written here.
Post by Daniel Vetter
Post by Ramalingam C
+
i915_driver_unregister(dev_priv);
if (i915_gem_suspend(dev_priv))
diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c
index 18e3a5a3d873..ac62af073688 100644
--- a/drivers/gpu/drm/i915/intel_dp.c
+++ b/drivers/gpu/drm/i915/intel_dp.c
@@ -6675,7 +6675,8 @@ intel_dp_init_connector(struct intel_digital_port *intel_dig_port,
intel_dp_add_properties(intel_dp, connector);
if (is_hdcp_supported(dev_priv, port) && !intel_dp_is_edp(intel_dp)) {
- int ret = intel_hdcp_init(intel_connector, &intel_dp_hdcp_shim);
+ int ret = intel_hdcp_init(intel_connector, &intel_dp_hdcp_shim,
+ false);
if (ret)
DRM_DEBUG_KMS("HDCP init failed, skipping.\n");
}
diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h
index a62d77b76291..85a526598096 100644
--- a/drivers/gpu/drm/i915/intel_drv.h
+++ b/drivers/gpu/drm/i915/intel_drv.h
@@ -388,6 +388,17 @@ struct intel_hdcp {
u64 value;
struct delayed_work check_work;
struct work_struct prop_work;
+
+ /* HDCP2.2 related definitions */
+ /* Flag indicates whether this connector supports HDCP2.2 or not. */
+ u8 hdcp2_supported;
+
+ /*
+ * Content Stream Type defined by content owner. TYPE0(0x0) content can
+ * flow in the link protected by HDCP2.2 or HDCP1.4, where as TYPE1(0x1)
+ * content can flow only through a link protected by HDCP2.2.
+ */
+ u8 content_type;
};
struct intel_connector {
@@ -2016,12 +2027,15 @@ void intel_hdcp_atomic_check(struct drm_connector *connector,
struct drm_connector_state *old_state,
struct drm_connector_state *new_state);
int intel_hdcp_init(struct intel_connector *connector,
- const struct intel_hdcp_shim *hdcp_shim);
+ const struct intel_hdcp_shim *hdcp_shim,
+ bool hdcp2_supported);
int intel_hdcp_enable(struct intel_connector *connector);
int intel_hdcp_disable(struct intel_connector *connector);
int intel_hdcp_check_link(struct intel_connector *connector);
bool is_hdcp_supported(struct drm_i915_private *dev_priv, enum port port);
bool intel_hdcp_capable(struct intel_connector *connector);
+bool is_hdcp2_supported(struct drm_i915_private *dev_priv);
+void intel_hdcp_exit(struct drm_i915_private *dev_priv);
/* intel_psr.c */
#define CAN_PSR(dev_priv) (HAS_PSR(dev_priv) && dev_priv->psr.sink_support)
diff --git a/drivers/gpu/drm/i915/intel_hdcp.c b/drivers/gpu/drm/i915/intel_hdcp.c
index 04ba6c13e715..99dddb540958 100644
--- a/drivers/gpu/drm/i915/intel_hdcp.c
+++ b/drivers/gpu/drm/i915/intel_hdcp.c
@@ -730,6 +730,65 @@ struct intel_connector *intel_hdcp_to_connector(struct intel_hdcp *hdcp)
return container_of(hdcp, struct intel_connector, hdcp);
}
+/* Implements Part 3 of the HDCP authorization procedure */
+int intel_hdcp_check_link(struct intel_connector *connector)
Spurious movement of this function. Please drop, or if you need this
function moved, split it out into a separate patch that only moves the
function around.
Intention is gathering the hdcp1.4 functions together before implementing the hdcp2.2 code.
But as you mentioned i will do it as a separate patch.

--Ram
Post by Daniel Vetter
Post by Ramalingam C
+{
+ struct intel_hdcp *hdcp = &connector->hdcp;
+ 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 (!hdcp->shim)
+ return -ENOENT;
+
+ mutex_lock(&hdcp->mutex);
+
+ if (hdcp->value == DRM_MODE_CONTENT_PROTECTION_UNDESIRED)
+ goto out;
+
+ if (!(I915_READ(PORT_HDCP_STATUS(port)) & HDCP_STATUS_ENC)) {
+ DRM_ERROR("%s:%d HDCP check failed: link is not encrypted,%x\n",
+ connector->base.name, connector->base.base.id,
+ I915_READ(PORT_HDCP_STATUS(port)));
+ ret = -ENXIO;
+ hdcp->value = DRM_MODE_CONTENT_PROTECTION_DESIRED;
+ schedule_work(&hdcp->prop_work);
+ goto out;
+ }
+
+ if (hdcp->shim->check_link(intel_dig_port)) {
+ if (hdcp->value != DRM_MODE_CONTENT_PROTECTION_UNDESIRED) {
+ hdcp->value = DRM_MODE_CONTENT_PROTECTION_ENABLED;
+ schedule_work(&hdcp->prop_work);
+ }
+ goto out;
+ }
+
+ DRM_DEBUG_KMS("[%s:%d] HDCP link failed, retrying authentication\n",
+ connector->base.name, connector->base.base.id);
+
+ ret = _intel_hdcp_disable(connector);
+ if (ret) {
+ DRM_ERROR("Failed to disable hdcp (%d)\n", ret);
+ hdcp->value = DRM_MODE_CONTENT_PROTECTION_DESIRED;
+ schedule_work(&hdcp->prop_work);
+ goto out;
+ }
+
+ ret = _intel_hdcp_enable(connector);
+ if (ret) {
+ DRM_ERROR("Failed to enable hdcp (%d)\n", ret);
+ hdcp->value = DRM_MODE_CONTENT_PROTECTION_DESIRED;
+ schedule_work(&hdcp->prop_work);
+ goto out;
+ }
+
+ mutex_unlock(&hdcp->mutex);
+ return ret;
+}
+
static void intel_hdcp_check_work(struct work_struct *work)
{
struct intel_hdcp *hdcp = container_of(to_delayed_work(work),
@@ -774,14 +833,34 @@ bool is_hdcp_supported(struct drm_i915_private *dev_priv, enum port port)
!IS_CHERRYVIEW(dev_priv) && port < PORT_E);
}
+bool is_hdcp2_supported(struct drm_i915_private *dev_priv)
+{
+ return ((INTEL_GEN(dev_priv) >= 10 || IS_GEMINILAKE(dev_priv) ||
+ IS_KABYLAKE(dev_priv)) && IS_ENABLED(CONFIG_INTEL_MEI_HDCP));
+}
+
+static void intel_hdcp2_init(struct intel_connector *connector)
+{
+ struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
+ struct intel_hdcp *hdcp = &connector->hdcp;
+
+ WARN_ON(!is_hdcp2_supported(dev_priv));
+
+ /* TODO: MEI interface needs to be initialized here */
+ hdcp->hdcp2_supported = 1;
+}
+
int intel_hdcp_init(struct intel_connector *connector,
- const struct intel_hdcp_shim *shim)
+ const struct intel_hdcp_shim *shim,
+ bool hdcp2_supported)
{
struct intel_hdcp *hdcp = &connector->hdcp;
int ret;
- ret = drm_connector_attach_content_protection_property(
- &connector->base);
+ if (!shim)
+ return -EINVAL;
+
+ ret = drm_connector_attach_content_protection_property(&connector->base);
if (ret)
return ret;
@@ -789,9 +868,37 @@ int intel_hdcp_init(struct intel_connector *connector,
mutex_init(&hdcp->mutex);
INIT_DELAYED_WORK(&hdcp->check_work, intel_hdcp_check_work);
INIT_WORK(&hdcp->prop_work, intel_hdcp_prop_work);
+
+ if (hdcp2_supported)
+ intel_hdcp2_init(connector);
+
return 0;
}
+void intel_hdcp_exit(struct drm_i915_private *dev_priv)
+{
+ struct drm_device *dev = &dev_priv->drm;
+ struct intel_connector *intel_connector;
+ struct drm_connector *connector;
+ struct drm_connector_list_iter conn_iter;
+
+ drm_connector_list_iter_begin(dev, &conn_iter);
+ drm_for_each_connector_iter(connector, &conn_iter) {
+ intel_connector = to_intel_connector(connector);
+
+ if (!intel_connector->hdcp.shim)
+ continue;
+
+ intel_hdcp_disable(intel_connector);
+
+ mutex_lock(&intel_connector->hdcp.mutex);
+ intel_connector->hdcp.hdcp2_supported = 0;
+ intel_connector->hdcp.shim = NULL;
+ mutex_unlock(&intel_connector->hdcp.mutex);
+ }
+ drm_connector_list_iter_end(&conn_iter);
+}
+
int intel_hdcp_enable(struct intel_connector *connector)
{
struct intel_hdcp *hdcp = &connector->hdcp;
@@ -867,62 +974,3 @@ void intel_hdcp_atomic_check(struct drm_connector *connector,
new_state->crtc);
crtc_state->mode_changed = true;
}
-
-/* Implements Part 3 of the HDCP authorization procedure */
-int intel_hdcp_check_link(struct intel_connector *connector)
-{
- struct intel_hdcp *hdcp = &connector->hdcp;
- 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 (!hdcp->shim)
- return -ENOENT;
-
- mutex_lock(&hdcp->mutex);
-
- if (hdcp->value == DRM_MODE_CONTENT_PROTECTION_UNDESIRED)
- goto out;
-
- if (!(I915_READ(PORT_HDCP_STATUS(port)) & HDCP_STATUS_ENC)) {
- DRM_ERROR("%s:%d HDCP check failed: link is not encrypted,%x\n",
- connector->base.name, connector->base.base.id,
- I915_READ(PORT_HDCP_STATUS(port)));
- ret = -ENXIO;
- hdcp->value = DRM_MODE_CONTENT_PROTECTION_DESIRED;
- schedule_work(&hdcp->prop_work);
- goto out;
- }
-
- if (hdcp->shim->check_link(intel_dig_port)) {
- if (hdcp->value != DRM_MODE_CONTENT_PROTECTION_UNDESIRED) {
- hdcp->value = DRM_MODE_CONTENT_PROTECTION_ENABLED;
- schedule_work(&hdcp->prop_work);
- }
- goto out;
- }
-
- DRM_DEBUG_KMS("[%s:%d] HDCP link failed, retrying authentication\n",
- connector->base.name, connector->base.base.id);
-
- ret = _intel_hdcp_disable(connector);
- if (ret) {
- DRM_ERROR("Failed to disable hdcp (%d)\n", ret);
- hdcp->value = DRM_MODE_CONTENT_PROTECTION_DESIRED;
- schedule_work(&hdcp->prop_work);
- goto out;
- }
-
- ret = _intel_hdcp_enable(connector);
- if (ret) {
- DRM_DEBUG_KMS("Failed to enable hdcp (%d)\n", ret);
- hdcp->value = DRM_MODE_CONTENT_PROTECTION_DESIRED;
- schedule_work(&hdcp->prop_work);
- goto out;
- }
-
- mutex_unlock(&hdcp->mutex);
- return ret;
-}
diff --git a/drivers/gpu/drm/i915/intel_hdmi.c b/drivers/gpu/drm/i915/intel_hdmi.c
index e2c6a2b3e8f2..e755a3370bca 100644
--- a/drivers/gpu/drm/i915/intel_hdmi.c
+++ b/drivers/gpu/drm/i915/intel_hdmi.c
@@ -2417,7 +2417,7 @@ void intel_hdmi_init_connector(struct intel_digital_port *intel_dig_port,
if (is_hdcp_supported(dev_priv, port)) {
int ret = intel_hdcp_init(intel_connector,
- &intel_hdmi_hdcp_shim);
+ &intel_hdmi_hdcp_shim, false);
if (ret)
DRM_DEBUG_KMS("HDCP init failed, skipping.\n");
The actual change left to wire hdcp2_supported seems ok with me, but hard
to tell without seeing more yet :-)
-Daniel
Post by Ramalingam C
}
--
2.7.4
Daniel Vetter
2018-12-07 14:16:30 UTC
Permalink
Post by C, Ramalingam
Post by Daniel Vetter
Post by Ramalingam C
Add the HDCP2.2 initialization to the existing HDCP1.4 stack.
With the comments below addressed the commit message is a bit untrue,
since this just wires up a basic hdcp2_supported flag in a few places.
Please make that clear.
Post by Ramalingam C
mei interface handle is protected with mutex. [Chris Wilson]
Notifiers are used for the mei interface state.
Poll for mei client device state
Error msg for out of mem [Uma]
Inline req for init function removed [Uma]
Rebase as Part of reordering.
Component is used for the I915 and MEI_HDCP interface [Daniel]
HDCP2.2 uses the I915 component master to communicate with mei_hdcp
- [Daniel]
Required HDCP2.2 variables defined [Sean Paul]
intel_hdcp2.2_init returns void [Uma]
Realigning the codes.
Avoid using bool structure members.
MEI interface related changes are moved into separate patch.
Commit msg is updated accordingly.
intel_hdcp_exit is defined and used from i915_unload
---
drivers/gpu/drm/i915/i915_drv.c | 2 +
drivers/gpu/drm/i915/intel_dp.c | 3 +-
drivers/gpu/drm/i915/intel_drv.h | 16 +++-
drivers/gpu/drm/i915/intel_hdcp.c | 172 ++++++++++++++++++++++++--------------
drivers/gpu/drm/i915/intel_hdmi.c | 2 +-
5 files changed, 130 insertions(+), 65 deletions(-)
diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c
index b1d23c73c147..fbedd5024afe 100644
--- a/drivers/gpu/drm/i915/i915_drv.c
+++ b/drivers/gpu/drm/i915/i915_drv.c
@@ -1755,6 +1755,8 @@ void i915_driver_unload(struct drm_device *dev)
disable_rpm_wakeref_asserts(dev_priv);
+ intel_hdcp_exit(dev_priv);
This smells like a separate patch. Needs to be split out. Looking at the
implementation of intel_hdcp_exit I think it's papering over some unload
trouble. We should be shutting down all the outputs on driver unload,
which mei should be triggering (with the component stuff), which means
this code should be unecessary. But I'm not sure.
Either way needs to be split out, but I think proper solution is to drop
it.
As we discussed, during v7-->v8 i changed the component usage such that it wont affect i915 load/unload.
During the first connector init, component master will be added. And during the mei_client dev and driver binding,
component will be added hence the binding will happen with interface initialization from mei.
Upon HDCP2.2 request, component binding will be checked before attempting for HDCP2.2 auth.
So component master unbind triggered due to mei component_del, will teardown the HDCP2.2 capability of the I915.
So in case of I915 unload trigger, from whatsoever reason, we need to clear the HDCP activities and bring down
the i915_hdcp_component_master and the interface with mei. For this purpose only intel_hdcp_exit is written here.
Summarizing our irc discussion:

- I like the component usage of v7 much more.

- I still don't think we have a use-case for loading/unloading mei_hdcp on
demand, or at least not in lockstep with i915.ko:
- CrOS won't use ME
- usual linux distros don't care about content protection, so won't even
enable the MEI_HDCP driver
- iotg/embedded either wants it (and then probably always) or doesn't
want it (in which case it won't be built in). Plus embedded tends to
have all built-in drivers anyway, so they all load in order.

And I don't want to review the validation coverage and locking and
runtime consequences of making this possible, since I'm lazy :-)

- I looked lockdep splat in v7 again in more detail. It's not actually an
issue with component usage, but just unlucky to now run into a
preexisting issue: Any driver you unbind through the sysfs file will
generate a lockdep splat if it removes any sysfs files itself. That's a
pre-existing bug, easily reproduced by unbinding i915.

Only thing new is that component connects the snd-hda-intel and i915
unload sequence through the component lock (but does _not_ create a
loop), and since we unbind snd-hda-intel through sysfs and i919
unregisters _lots_ of sysfs files lockdep now sees the cycle. It's
always been there.

I'm working on a separate patch to fix this.

Cheers, Daniel
Post by C, Ramalingam
Post by Daniel Vetter
Post by Ramalingam C
+
i915_driver_unregister(dev_priv);
if (i915_gem_suspend(dev_priv))
diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c
index 18e3a5a3d873..ac62af073688 100644
--- a/drivers/gpu/drm/i915/intel_dp.c
+++ b/drivers/gpu/drm/i915/intel_dp.c
@@ -6675,7 +6675,8 @@ intel_dp_init_connector(struct intel_digital_port *intel_dig_port,
intel_dp_add_properties(intel_dp, connector);
if (is_hdcp_supported(dev_priv, port) && !intel_dp_is_edp(intel_dp)) {
- int ret = intel_hdcp_init(intel_connector, &intel_dp_hdcp_shim);
+ int ret = intel_hdcp_init(intel_connector, &intel_dp_hdcp_shim,
+ false);
if (ret)
DRM_DEBUG_KMS("HDCP init failed, skipping.\n");
}
diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h
index a62d77b76291..85a526598096 100644
--- a/drivers/gpu/drm/i915/intel_drv.h
+++ b/drivers/gpu/drm/i915/intel_drv.h
@@ -388,6 +388,17 @@ struct intel_hdcp {
u64 value;
struct delayed_work check_work;
struct work_struct prop_work;
+
+ /* HDCP2.2 related definitions */
+ /* Flag indicates whether this connector supports HDCP2.2 or not. */
+ u8 hdcp2_supported;
+
+ /*
+ * Content Stream Type defined by content owner. TYPE0(0x0) content can
+ * flow in the link protected by HDCP2.2 or HDCP1.4, where as TYPE1(0x1)
+ * content can flow only through a link protected by HDCP2.2.
+ */
+ u8 content_type;
};
struct intel_connector {
@@ -2016,12 +2027,15 @@ void intel_hdcp_atomic_check(struct drm_connector *connector,
struct drm_connector_state *old_state,
struct drm_connector_state *new_state);
int intel_hdcp_init(struct intel_connector *connector,
- const struct intel_hdcp_shim *hdcp_shim);
+ const struct intel_hdcp_shim *hdcp_shim,
+ bool hdcp2_supported);
int intel_hdcp_enable(struct intel_connector *connector);
int intel_hdcp_disable(struct intel_connector *connector);
int intel_hdcp_check_link(struct intel_connector *connector);
bool is_hdcp_supported(struct drm_i915_private *dev_priv, enum port port);
bool intel_hdcp_capable(struct intel_connector *connector);
+bool is_hdcp2_supported(struct drm_i915_private *dev_priv);
+void intel_hdcp_exit(struct drm_i915_private *dev_priv);
/* intel_psr.c */
#define CAN_PSR(dev_priv) (HAS_PSR(dev_priv) && dev_priv->psr.sink_support)
diff --git a/drivers/gpu/drm/i915/intel_hdcp.c b/drivers/gpu/drm/i915/intel_hdcp.c
index 04ba6c13e715..99dddb540958 100644
--- a/drivers/gpu/drm/i915/intel_hdcp.c
+++ b/drivers/gpu/drm/i915/intel_hdcp.c
@@ -730,6 +730,65 @@ struct intel_connector *intel_hdcp_to_connector(struct intel_hdcp *hdcp)
return container_of(hdcp, struct intel_connector, hdcp);
}
+/* Implements Part 3 of the HDCP authorization procedure */
+int intel_hdcp_check_link(struct intel_connector *connector)
Spurious movement of this function. Please drop, or if you need this
function moved, split it out into a separate patch that only moves the
function around.
Intention is gathering the hdcp1.4 functions together before implementing the hdcp2.2 code.
But as you mentioned i will do it as a separate patch.
Ah yeah that makes sense, but commit message didn't explain that. Separate
patch sounds good.
-Daniel
Post by C, Ramalingam
--Ram
Post by Daniel Vetter
Post by Ramalingam C
+{
+ struct intel_hdcp *hdcp = &connector->hdcp;
+ 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 (!hdcp->shim)
+ return -ENOENT;
+
+ mutex_lock(&hdcp->mutex);
+
+ if (hdcp->value == DRM_MODE_CONTENT_PROTECTION_UNDESIRED)
+ goto out;
+
+ if (!(I915_READ(PORT_HDCP_STATUS(port)) & HDCP_STATUS_ENC)) {
+ DRM_ERROR("%s:%d HDCP check failed: link is not encrypted,%x\n",
+ connector->base.name, connector->base.base.id,
+ I915_READ(PORT_HDCP_STATUS(port)));
+ ret = -ENXIO;
+ hdcp->value = DRM_MODE_CONTENT_PROTECTION_DESIRED;
+ schedule_work(&hdcp->prop_work);
+ goto out;
+ }
+
+ if (hdcp->shim->check_link(intel_dig_port)) {
+ if (hdcp->value != DRM_MODE_CONTENT_PROTECTION_UNDESIRED) {
+ hdcp->value = DRM_MODE_CONTENT_PROTECTION_ENABLED;
+ schedule_work(&hdcp->prop_work);
+ }
+ goto out;
+ }
+
+ DRM_DEBUG_KMS("[%s:%d] HDCP link failed, retrying authentication\n",
+ connector->base.name, connector->base.base.id);
+
+ ret = _intel_hdcp_disable(connector);
+ if (ret) {
+ DRM_ERROR("Failed to disable hdcp (%d)\n", ret);
+ hdcp->value = DRM_MODE_CONTENT_PROTECTION_DESIRED;
+ schedule_work(&hdcp->prop_work);
+ goto out;
+ }
+
+ ret = _intel_hdcp_enable(connector);
+ if (ret) {
+ DRM_ERROR("Failed to enable hdcp (%d)\n", ret);
+ hdcp->value = DRM_MODE_CONTENT_PROTECTION_DESIRED;
+ schedule_work(&hdcp->prop_work);
+ goto out;
+ }
+
+ mutex_unlock(&hdcp->mutex);
+ return ret;
+}
+
static void intel_hdcp_check_work(struct work_struct *work)
{
struct intel_hdcp *hdcp = container_of(to_delayed_work(work),
@@ -774,14 +833,34 @@ bool is_hdcp_supported(struct drm_i915_private *dev_priv, enum port port)
!IS_CHERRYVIEW(dev_priv) && port < PORT_E);
}
+bool is_hdcp2_supported(struct drm_i915_private *dev_priv)
+{
+ return ((INTEL_GEN(dev_priv) >= 10 || IS_GEMINILAKE(dev_priv) ||
+ IS_KABYLAKE(dev_priv)) && IS_ENABLED(CONFIG_INTEL_MEI_HDCP));
+}
+
+static void intel_hdcp2_init(struct intel_connector *connector)
+{
+ struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
+ struct intel_hdcp *hdcp = &connector->hdcp;
+
+ WARN_ON(!is_hdcp2_supported(dev_priv));
+
+ /* TODO: MEI interface needs to be initialized here */
+ hdcp->hdcp2_supported = 1;
+}
+
int intel_hdcp_init(struct intel_connector *connector,
- const struct intel_hdcp_shim *shim)
+ const struct intel_hdcp_shim *shim,
+ bool hdcp2_supported)
{
struct intel_hdcp *hdcp = &connector->hdcp;
int ret;
- ret = drm_connector_attach_content_protection_property(
- &connector->base);
+ if (!shim)
+ return -EINVAL;
+
+ ret = drm_connector_attach_content_protection_property(&connector->base);
if (ret)
return ret;
@@ -789,9 +868,37 @@ int intel_hdcp_init(struct intel_connector *connector,
mutex_init(&hdcp->mutex);
INIT_DELAYED_WORK(&hdcp->check_work, intel_hdcp_check_work);
INIT_WORK(&hdcp->prop_work, intel_hdcp_prop_work);
+
+ if (hdcp2_supported)
+ intel_hdcp2_init(connector);
+
return 0;
}
+void intel_hdcp_exit(struct drm_i915_private *dev_priv)
+{
+ struct drm_device *dev = &dev_priv->drm;
+ struct intel_connector *intel_connector;
+ struct drm_connector *connector;
+ struct drm_connector_list_iter conn_iter;
+
+ drm_connector_list_iter_begin(dev, &conn_iter);
+ drm_for_each_connector_iter(connector, &conn_iter) {
+ intel_connector = to_intel_connector(connector);
+
+ if (!intel_connector->hdcp.shim)
+ continue;
+
+ intel_hdcp_disable(intel_connector);
+
+ mutex_lock(&intel_connector->hdcp.mutex);
+ intel_connector->hdcp.hdcp2_supported = 0;
+ intel_connector->hdcp.shim = NULL;
+ mutex_unlock(&intel_connector->hdcp.mutex);
+ }
+ drm_connector_list_iter_end(&conn_iter);
+}
+
int intel_hdcp_enable(struct intel_connector *connector)
{
struct intel_hdcp *hdcp = &connector->hdcp;
@@ -867,62 +974,3 @@ void intel_hdcp_atomic_check(struct drm_connector *connector,
new_state->crtc);
crtc_state->mode_changed = true;
}
-
-/* Implements Part 3 of the HDCP authorization procedure */
-int intel_hdcp_check_link(struct intel_connector *connector)
-{
- struct intel_hdcp *hdcp = &connector->hdcp;
- 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 (!hdcp->shim)
- return -ENOENT;
-
- mutex_lock(&hdcp->mutex);
-
- if (hdcp->value == DRM_MODE_CONTENT_PROTECTION_UNDESIRED)
- goto out;
-
- if (!(I915_READ(PORT_HDCP_STATUS(port)) & HDCP_STATUS_ENC)) {
- DRM_ERROR("%s:%d HDCP check failed: link is not encrypted,%x\n",
- connector->base.name, connector->base.base.id,
- I915_READ(PORT_HDCP_STATUS(port)));
- ret = -ENXIO;
- hdcp->value = DRM_MODE_CONTENT_PROTECTION_DESIRED;
- schedule_work(&hdcp->prop_work);
- goto out;
- }
-
- if (hdcp->shim->check_link(intel_dig_port)) {
- if (hdcp->value != DRM_MODE_CONTENT_PROTECTION_UNDESIRED) {
- hdcp->value = DRM_MODE_CONTENT_PROTECTION_ENABLED;
- schedule_work(&hdcp->prop_work);
- }
- goto out;
- }
-
- DRM_DEBUG_KMS("[%s:%d] HDCP link failed, retrying authentication\n",
- connector->base.name, connector->base.base.id);
-
- ret = _intel_hdcp_disable(connector);
- if (ret) {
- DRM_ERROR("Failed to disable hdcp (%d)\n", ret);
- hdcp->value = DRM_MODE_CONTENT_PROTECTION_DESIRED;
- schedule_work(&hdcp->prop_work);
- goto out;
- }
-
- ret = _intel_hdcp_enable(connector);
- if (ret) {
- DRM_DEBUG_KMS("Failed to enable hdcp (%d)\n", ret);
- hdcp->value = DRM_MODE_CONTENT_PROTECTION_DESIRED;
- schedule_work(&hdcp->prop_work);
- goto out;
- }
-
- mutex_unlock(&hdcp->mutex);
- return ret;
-}
diff --git a/drivers/gpu/drm/i915/intel_hdmi.c b/drivers/gpu/drm/i915/intel_hdmi.c
index e2c6a2b3e8f2..e755a3370bca 100644
--- a/drivers/gpu/drm/i915/intel_hdmi.c
+++ b/drivers/gpu/drm/i915/intel_hdmi.c
@@ -2417,7 +2417,7 @@ void intel_hdmi_init_connector(struct intel_digital_port *intel_dig_port,
if (is_hdcp_supported(dev_priv, port)) {
int ret = intel_hdcp_init(intel_connector,
- &intel_hdmi_hdcp_shim);
+ &intel_hdmi_hdcp_shim, false);
if (ret)
DRM_DEBUG_KMS("HDCP init failed, skipping.\n");
The actual change left to wire hdcp2_supported seems ok with me, but hard
to tell without seeing more yet :-)
-Daniel
Post by Ramalingam C
}
--
2.7.4
--
Daniel Vetter
Software Engineer, Intel Corporation
http://blog.ffwll.ch
Winkler, Tomas
2018-12-08 18:47:40 UTC
Permalink
-----Original Message-----
Sent: Friday, December 07, 2018 16:17
Subject: Re: [PATCH v8 04/35] drm/i915: Initialize HDCP2.2
Post by C, Ramalingam
Post by Daniel Vetter
Post by Ramalingam C
Add the HDCP2.2 initialization to the existing HDCP1.4 stack.
With the comments below addressed the commit message is a bit
untrue, since this just wires up a basic hdcp2_supported flag in a few
places.
Post by C, Ramalingam
Post by Daniel Vetter
Please make that clear.
Post by Ramalingam C
mei interface handle is protected with mutex. [Chris Wilson]
Notifiers are used for the mei interface state.
Poll for mei client device state
Error msg for out of mem [Uma]
Inline req for init function removed [Uma]
Rebase as Part of reordering.
Component is used for the I915 and MEI_HDCP interface [Daniel]
HDCP2.2 uses the I915 component master to communicate with
mei_hdcp
Post by C, Ramalingam
Post by Daniel Vetter
Post by Ramalingam C
- [Daniel]
Required HDCP2.2 variables defined [Sean Paul]
intel_hdcp2.2_init returns void [Uma]
Realigning the codes.
Avoid using bool structure members.
MEI interface related changes are moved into separate patch.
Commit msg is updated accordingly.
intel_hdcp_exit is defined and used from i915_unload
---
drivers/gpu/drm/i915/i915_drv.c | 2 +
drivers/gpu/drm/i915/intel_dp.c | 3 +-
drivers/gpu/drm/i915/intel_drv.h | 16 +++-
drivers/gpu/drm/i915/intel_hdcp.c | 172 ++++++++++++++++++++++++--
------------
Post by C, Ramalingam
Post by Daniel Vetter
Post by Ramalingam C
drivers/gpu/drm/i915/intel_hdmi.c | 2 +-
5 files changed, 130 insertions(+), 65 deletions(-)
diff --git a/drivers/gpu/drm/i915/i915_drv.c
b/drivers/gpu/drm/i915/i915_drv.c index b1d23c73c147..fbedd5024afe
100644
--- a/drivers/gpu/drm/i915/i915_drv.c
+++ b/drivers/gpu/drm/i915/i915_drv.c
@@ -1755,6 +1755,8 @@ void i915_driver_unload(struct drm_device
*dev)
Post by C, Ramalingam
Post by Daniel Vetter
Post by Ramalingam C
disable_rpm_wakeref_asserts(dev_priv);
+ intel_hdcp_exit(dev_priv);
This smells like a separate patch. Needs to be split out. Looking at
the implementation of intel_hdcp_exit I think it's papering over
some unload trouble. We should be shutting down all the outputs on
driver unload, which mei should be triggering (with the component
stuff), which means this code should be unecessary. But I'm not sure.
Either way needs to be split out, but I think proper solution is to
drop it.
As we discussed, during v7-->v8 i changed the component usage such that it
wont affect i915 load/unload.
Post by C, Ramalingam
During the first connector init, component master will be added. And
during the mei_client dev and driver binding, component will be added hence
the binding will happen with interface initialization from mei.
Post by C, Ramalingam
Upon HDCP2.2 request, component binding will be checked before
attempting for HDCP2.2 auth.
Post by C, Ramalingam
So component master unbind triggered due to mei component_del, will
teardown the HDCP2.2 capability of the I915.
Post by C, Ramalingam
So in case of I915 unload trigger, from whatsoever reason, we need to
clear the HDCP activities and bring down the i915_hdcp_component_master
and the interface with mei. For this purpose only intel_hdcp_exit is written
here.
- I like the component usage of v7 much more.
- I still don't think we have a use-case for loading/unloading mei_hdcp on
We are testing all the MEI modules reloading, this would be suddenly an exception.
- CrOS won't use ME
I'm not so sure, we are working on it.
- usual linux distros don't care about content protection, so won't even
enable the MEI_HDCP driver
- iotg/embedded either wants it (and then probably always) or doesn't
want it (in which case it won't be built in). Plus embedded tends to
have all built-in drivers anyway, so they all load in order.
It really depends, there product range si really wide.
And I don't want to review the validation coverage and locking and
runtime consequences of making this possible, since I'm lazy :-)
- I looked lockdep splat in v7 again in more detail. It's not actually an
issue with component usage, but just unlucky to now run into a
preexisting issue: Any driver you unbind through the sysfs file will
generate a lockdep splat if it removes any sysfs files itself. That's a
pre-existing bug, easily reproduced by unbinding i915.
Only thing new is that component connects the snd-hda-intel and i915
unload sequence through the component lock (but does _not_ create a
loop), and since we unbind snd-hda-intel through sysfs and i919
unregisters _lots_ of sysfs files lockdep now sees the cycle. It's
always been there.
I'm working on a separate patch to fix this.
Cheers, Daniel
Post by C, Ramalingam
Post by Daniel Vetter
Post by Ramalingam C
+
i915_driver_unregister(dev_priv);
if (i915_gem_suspend(dev_priv)) diff --git
a/drivers/gpu/drm/i915/intel_dp.c
b/drivers/gpu/drm/i915/intel_dp.c index 18e3a5a3d873..ac62af073688
100644
--- a/drivers/gpu/drm/i915/intel_dp.c
+++ b/drivers/gpu/drm/i915/intel_dp.c
@@ -6675,7 +6675,8 @@ intel_dp_init_connector(struct
intel_digital_port *intel_dig_port,
Post by C, Ramalingam
Post by Daniel Vetter
Post by Ramalingam C
intel_dp_add_properties(intel_dp, connector);
if (is_hdcp_supported(dev_priv, port) && !intel_dp_is_edp(intel_dp)) {
- int ret = intel_hdcp_init(intel_connector,
&intel_dp_hdcp_shim);
Post by C, Ramalingam
Post by Daniel Vetter
Post by Ramalingam C
+ int ret = intel_hdcp_init(intel_connector,
&intel_dp_hdcp_shim,
Post by C, Ramalingam
Post by Daniel Vetter
Post by Ramalingam C
+ false);
if (ret)
DRM_DEBUG_KMS("HDCP init failed, skipping.\n");
}
diff --git a/drivers/gpu/drm/i915/intel_drv.h
b/drivers/gpu/drm/i915/intel_drv.h
index a62d77b76291..85a526598096 100644
--- a/drivers/gpu/drm/i915/intel_drv.h
+++ b/drivers/gpu/drm/i915/intel_drv.h
@@ -388,6 +388,17 @@ struct intel_hdcp {
u64 value;
struct delayed_work check_work;
struct work_struct prop_work;
+
+ /* HDCP2.2 related definitions */
+ /* Flag indicates whether this connector supports HDCP2.2 or not. */
+ u8 hdcp2_supported;
+
+ /*
+ * Content Stream Type defined by content owner. TYPE0(0x0) content
can
Post by C, Ramalingam
Post by Daniel Vetter
Post by Ramalingam C
+ * flow in the link protected by HDCP2.2 or HDCP1.4, where as
TYPE1(0x1)
Post by C, Ramalingam
Post by Daniel Vetter
Post by Ramalingam C
+ * content can flow only through a link protected by HDCP2.2.
+ */
+ u8 content_type;
};
struct intel_connector {
@@ -2016,12 +2027,15 @@ void intel_hdcp_atomic_check(struct
drm_connector *connector,
Post by C, Ramalingam
Post by Daniel Vetter
Post by Ramalingam C
struct drm_connector_state *old_state,
struct drm_connector_state *new_state);
int intel_hdcp_init(struct intel_connector *connector,
- const struct intel_hdcp_shim *hdcp_shim);
+ const struct intel_hdcp_shim *hdcp_shim,
+ bool hdcp2_supported);
int intel_hdcp_enable(struct intel_connector *connector);
int intel_hdcp_disable(struct intel_connector *connector);
int intel_hdcp_check_link(struct intel_connector *connector);
bool is_hdcp_supported(struct drm_i915_private *dev_priv, enum port
port);
Post by C, Ramalingam
Post by Daniel Vetter
Post by Ramalingam C
bool intel_hdcp_capable(struct intel_connector *connector);
+bool is_hdcp2_supported(struct drm_i915_private *dev_priv); void
+intel_hdcp_exit(struct drm_i915_private *dev_priv);
/* intel_psr.c */
#define CAN_PSR(dev_priv) (HAS_PSR(dev_priv) &&
dev_priv->psr.sink_support) diff --git
a/drivers/gpu/drm/i915/intel_hdcp.c
b/drivers/gpu/drm/i915/intel_hdcp.c
index 04ba6c13e715..99dddb540958 100644
--- a/drivers/gpu/drm/i915/intel_hdcp.c
+++ b/drivers/gpu/drm/i915/intel_hdcp.c
@@ -730,6 +730,65 @@ struct intel_connector
*intel_hdcp_to_connector(struct intel_hdcp *hdcp)
Post by C, Ramalingam
Post by Daniel Vetter
Post by Ramalingam C
return container_of(hdcp, struct intel_connector, hdcp);
}
+/* Implements Part 3 of the HDCP authorization procedure */ int
+intel_hdcp_check_link(struct intel_connector *connector)
Spurious movement of this function. Please drop, or if you need this
function moved, split it out into a separate patch that only moves
the function around.
Intention is gathering the hdcp1.4 functions together before implementing
the hdcp2.2 code.
Post by C, Ramalingam
But as you mentioned i will do it as a separate patch.
Ah yeah that makes sense, but commit message didn't explain that. Separate
patch sounds good.
-Daniel
Post by C, Ramalingam
--Ram
Post by Daniel Vetter
Post by Ramalingam C
+{
+ struct intel_hdcp *hdcp = &connector->hdcp;
+ struct drm_i915_private *dev_priv = connector->base.dev-
dev_private;
Post by Daniel Vetter
Post by Ramalingam C
+ struct intel_digital_port *intel_dig_port =
conn_to_dig_port(connector);
Post by C, Ramalingam
Post by Daniel Vetter
Post by Ramalingam C
+ enum port port = intel_dig_port->base.port;
+ int ret = 0;
+
+ if (!hdcp->shim)
+ return -ENOENT;
+
+ mutex_lock(&hdcp->mutex);
+
+ if (hdcp->value == DRM_MODE_CONTENT_PROTECTION_UNDESIRED)
+ goto out;
+
+ if (!(I915_READ(PORT_HDCP_STATUS(port)) & HDCP_STATUS_ENC)) {
+ DRM_ERROR("%s:%d HDCP check failed: link is not
encrypted,%x\n",
Post by C, Ramalingam
Post by Daniel Vetter
Post by Ramalingam C
+ connector->base.name, connector->base.base.id,
+ I915_READ(PORT_HDCP_STATUS(port)));
+ ret = -ENXIO;
+ hdcp->value = DRM_MODE_CONTENT_PROTECTION_DESIRED;
+ schedule_work(&hdcp->prop_work);
+ goto out;
+ }
+
+ if (hdcp->shim->check_link(intel_dig_port)) {
+ if (hdcp->value !=
DRM_MODE_CONTENT_PROTECTION_UNDESIRED) {
Post by C, Ramalingam
Post by Daniel Vetter
Post by Ramalingam C
+ hdcp->value =
DRM_MODE_CONTENT_PROTECTION_ENABLED;
Post by C, Ramalingam
Post by Daniel Vetter
Post by Ramalingam C
+ schedule_work(&hdcp->prop_work);
+ }
+ goto out;
+ }
+
+ DRM_DEBUG_KMS("[%s:%d] HDCP link failed, retrying
authentication\n",
Post by C, Ramalingam
Post by Daniel Vetter
Post by Ramalingam C
+ connector->base.name, connector->base.base.id);
+
+ ret = _intel_hdcp_disable(connector);
+ if (ret) {
+ DRM_ERROR("Failed to disable hdcp (%d)\n", ret);
+ hdcp->value = DRM_MODE_CONTENT_PROTECTION_DESIRED;
+ schedule_work(&hdcp->prop_work);
+ goto out;
+ }
+
+ ret = _intel_hdcp_enable(connector);
+ if (ret) {
+ DRM_ERROR("Failed to enable hdcp (%d)\n", ret);
+ hdcp->value = DRM_MODE_CONTENT_PROTECTION_DESIRED;
+ schedule_work(&hdcp->prop_work);
+ goto out;
+ }
+
+ mutex_unlock(&hdcp->mutex);
+ return ret;
+}
+
static void intel_hdcp_check_work(struct work_struct *work)
{
struct intel_hdcp *hdcp = container_of(to_delayed_work(work),
@@ -774,14 +833,34 @@ bool is_hdcp_supported(struct
drm_i915_private *dev_priv, enum port port)
Post by C, Ramalingam
Post by Daniel Vetter
Post by Ramalingam C
!IS_CHERRYVIEW(dev_priv) && port < PORT_E);
}
+bool is_hdcp2_supported(struct drm_i915_private *dev_priv) {
+ return ((INTEL_GEN(dev_priv) >= 10 || IS_GEMINILAKE(dev_priv) ||
+ IS_KABYLAKE(dev_priv)) &&
IS_ENABLED(CONFIG_INTEL_MEI_HDCP));
Post by C, Ramalingam
Post by Daniel Vetter
Post by Ramalingam C
+}
+
+static void intel_hdcp2_init(struct intel_connector *connector) {
+ struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
+ struct intel_hdcp *hdcp = &connector->hdcp;
+
+ WARN_ON(!is_hdcp2_supported(dev_priv));
+
+ /* TODO: MEI interface needs to be initialized here */
+ hdcp->hdcp2_supported = 1;
+}
+
int intel_hdcp_init(struct intel_connector *connector,
- const struct intel_hdcp_shim *shim)
+ const struct intel_hdcp_shim *shim,
+ bool hdcp2_supported)
{
struct intel_hdcp *hdcp = &connector->hdcp;
int ret;
- ret = drm_connector_attach_content_protection_property(
- &connector->base);
+ if (!shim)
+ return -EINVAL;
+
+ ret =
+drm_connector_attach_content_protection_property(&connector->base
+);
if (ret)
return ret;
@@ -789,9 +868,37 @@ int intel_hdcp_init(struct intel_connector
*connector,
Post by C, Ramalingam
Post by Daniel Vetter
Post by Ramalingam C
mutex_init(&hdcp->mutex);
INIT_DELAYED_WORK(&hdcp->check_work, intel_hdcp_check_work);
INIT_WORK(&hdcp->prop_work, intel_hdcp_prop_work);
+
+ if (hdcp2_supported)
+ intel_hdcp2_init(connector);
+
return 0;
}
+void intel_hdcp_exit(struct drm_i915_private *dev_priv) {
+ struct drm_device *dev = &dev_priv->drm;
+ struct intel_connector *intel_connector;
+ struct drm_connector *connector;
+ struct drm_connector_list_iter conn_iter;
+
+ drm_connector_list_iter_begin(dev, &conn_iter);
+ drm_for_each_connector_iter(connector, &conn_iter) {
+ intel_connector = to_intel_connector(connector);
+
+ if (!intel_connector->hdcp.shim)
+ continue;
+
+ intel_hdcp_disable(intel_connector);
+
+ mutex_lock(&intel_connector->hdcp.mutex);
+ intel_connector->hdcp.hdcp2_supported = 0;
+ intel_connector->hdcp.shim = NULL;
+ mutex_unlock(&intel_connector->hdcp.mutex);
+ }
+ drm_connector_list_iter_end(&conn_iter);
+}
+
int intel_hdcp_enable(struct intel_connector *connector)
{
@@ void intel_hdcp_atomic_check(struct drm_connector *connector,
new_state->crtc);
crtc_state->mode_changed = true;
}
-
-/* Implements Part 3 of the HDCP authorization procedure */ -int
intel_hdcp_check_link(struct intel_connector *connector) -{
- struct intel_hdcp *hdcp = &connector->hdcp;
- struct drm_i915_private *dev_priv = connector->base.dev-
dev_private;
Post by Daniel Vetter
Post by Ramalingam C
- struct intel_digital_port *intel_dig_port =
conn_to_dig_port(connector);
Post by C, Ramalingam
Post by Daniel Vetter
Post by Ramalingam C
- enum port port = intel_dig_port->base.port;
- int ret = 0;
-
- if (!hdcp->shim)
- return -ENOENT;
-
- mutex_lock(&hdcp->mutex);
-
- if (hdcp->value == DRM_MODE_CONTENT_PROTECTION_UNDESIRED)
- goto out;
-
- if (!(I915_READ(PORT_HDCP_STATUS(port)) & HDCP_STATUS_ENC)) {
- DRM_ERROR("%s:%d HDCP check failed: link is not
encrypted,%x\n",
Post by C, Ramalingam
Post by Daniel Vetter
Post by Ramalingam C
- connector->base.name, connector->base.base.id,
- I915_READ(PORT_HDCP_STATUS(port)));
- ret = -ENXIO;
- hdcp->value = DRM_MODE_CONTENT_PROTECTION_DESIRED;
- schedule_work(&hdcp->prop_work);
- goto out;
- }
-
- if (hdcp->shim->check_link(intel_dig_port)) {
- if (hdcp->value !=
DRM_MODE_CONTENT_PROTECTION_UNDESIRED) {
Post by C, Ramalingam
Post by Daniel Vetter
Post by Ramalingam C
- hdcp->value =
DRM_MODE_CONTENT_PROTECTION_ENABLED;
Post by C, Ramalingam
Post by Daniel Vetter
Post by Ramalingam C
- schedule_work(&hdcp->prop_work);
- }
- goto out;
- }
-
- DRM_DEBUG_KMS("[%s:%d] HDCP link failed, retrying
authentication\n",
Post by C, Ramalingam
Post by Daniel Vetter
Post by Ramalingam C
- connector->base.name, connector->base.base.id);
-
- ret = _intel_hdcp_disable(connector);
- if (ret) {
- DRM_ERROR("Failed to disable hdcp (%d)\n", ret);
- hdcp->value = DRM_MODE_CONTENT_PROTECTION_DESIRED;
- schedule_work(&hdcp->prop_work);
- goto out;
- }
-
- ret = _intel_hdcp_enable(connector);
- if (ret) {
- DRM_DEBUG_KMS("Failed to enable hdcp (%d)\n", ret);
- hdcp->value = DRM_MODE_CONTENT_PROTECTION_DESIRED;
- schedule_work(&hdcp->prop_work);
- goto out;
- }
-
- mutex_unlock(&hdcp->mutex);
- return ret;
-}
diff --git a/drivers/gpu/drm/i915/intel_hdmi.c
b/drivers/gpu/drm/i915/intel_hdmi.c
index e2c6a2b3e8f2..e755a3370bca 100644
--- a/drivers/gpu/drm/i915/intel_hdmi.c
+++ b/drivers/gpu/drm/i915/intel_hdmi.c
@@ -2417,7 +2417,7 @@ void intel_hdmi_init_connector(struct
intel_digital_port *intel_dig_port,
Post by C, Ramalingam
Post by Daniel Vetter
Post by Ramalingam C
if (is_hdcp_supported(dev_priv, port)) {
int ret = intel_hdcp_init(intel_connector,
- &intel_hdmi_hdcp_shim);
+ &intel_hdmi_hdcp_shim, false);
if (ret)
DRM_DEBUG_KMS("HDCP init failed, skipping.\n");
The actual change left to wire hdcp2_supported seems ok with me, but
hard to tell without seeing more yet :-) -Daniel
Post by Ramalingam C
}
--
2.7.4
--
Daniel Vetter
Software Engineer, Intel Corporation
http://blog.ffwll.ch
Daniel Vetter
2018-12-10 09:28:24 UTC
Permalink
Post by Winkler, Tomas
-----Original Message-----
Sent: Friday, December 07, 2018 16:17
Subject: Re: [PATCH v8 04/35] drm/i915: Initialize HDCP2.2
Post by C, Ramalingam
Post by Daniel Vetter
Post by Ramalingam C
Add the HDCP2.2 initialization to the existing HDCP1.4 stack.
With the comments below addressed the commit message is a bit
untrue, since this just wires up a basic hdcp2_supported flag in a few
places.
Post by C, Ramalingam
Post by Daniel Vetter
Please make that clear.
Post by Ramalingam C
mei interface handle is protected with mutex. [Chris Wilson]
Notifiers are used for the mei interface state.
Poll for mei client device state
Error msg for out of mem [Uma]
Inline req for init function removed [Uma]
Rebase as Part of reordering.
Component is used for the I915 and MEI_HDCP interface [Daniel]
HDCP2.2 uses the I915 component master to communicate with
mei_hdcp
Post by C, Ramalingam
Post by Daniel Vetter
Post by Ramalingam C
- [Daniel]
Required HDCP2.2 variables defined [Sean Paul]
intel_hdcp2.2_init returns void [Uma]
Realigning the codes.
Avoid using bool structure members.
MEI interface related changes are moved into separate patch.
Commit msg is updated accordingly.
intel_hdcp_exit is defined and used from i915_unload
---
drivers/gpu/drm/i915/i915_drv.c | 2 +
drivers/gpu/drm/i915/intel_dp.c | 3 +-
drivers/gpu/drm/i915/intel_drv.h | 16 +++-
drivers/gpu/drm/i915/intel_hdcp.c | 172 ++++++++++++++++++++++++--
------------
Post by C, Ramalingam
Post by Daniel Vetter
Post by Ramalingam C
drivers/gpu/drm/i915/intel_hdmi.c | 2 +-
5 files changed, 130 insertions(+), 65 deletions(-)
diff --git a/drivers/gpu/drm/i915/i915_drv.c
b/drivers/gpu/drm/i915/i915_drv.c index b1d23c73c147..fbedd5024afe
100644
--- a/drivers/gpu/drm/i915/i915_drv.c
+++ b/drivers/gpu/drm/i915/i915_drv.c
@@ -1755,6 +1755,8 @@ void i915_driver_unload(struct drm_device
*dev)
Post by C, Ramalingam
Post by Daniel Vetter
Post by Ramalingam C
disable_rpm_wakeref_asserts(dev_priv);
+ intel_hdcp_exit(dev_priv);
This smells like a separate patch. Needs to be split out. Looking at
the implementation of intel_hdcp_exit I think it's papering over
some unload trouble. We should be shutting down all the outputs on
driver unload, which mei should be triggering (with the component
stuff), which means this code should be unecessary. But I'm not sure.
Either way needs to be split out, but I think proper solution is to
drop it.
As we discussed, during v7-->v8 i changed the component usage such that it
wont affect i915 load/unload.
Post by C, Ramalingam
During the first connector init, component master will be added. And
during the mei_client dev and driver binding, component will be added hence
the binding will happen with interface initialization from mei.
Post by C, Ramalingam
Upon HDCP2.2 request, component binding will be checked before
attempting for HDCP2.2 auth.
Post by C, Ramalingam
So component master unbind triggered due to mei component_del, will
teardown the HDCP2.2 capability of the I915.
Post by C, Ramalingam
So in case of I915 unload trigger, from whatsoever reason, we need to
clear the HDCP activities and bring down the i915_hdcp_component_master
and the interface with mei. For this purpose only intel_hdcp_exit is written
here.
- I like the component usage of v7 much more.
- I still don't think we have a use-case for loading/unloading mei_hdcp on
We are testing all the MEI modules reloading, this would be suddenly an exception.
We're doing the same. And with the component stuff you can do that too in
a clean way. But there's a difference between a useful feature for
developers and something we need in production.

What I mean here is that I don't see a use-case for unloading mei_hdcp,
while i915 still needs to keep working. That's the only difference between
v7 and v8 in features support wrt driver loading/unloading.
Post by Winkler, Tomas
- CrOS won't use ME
I'm not so sure, we are working on it.
Hm, that's interesting. We can't support HDCP2 on CrOS because of the ME
thing, and I haven't heard anything new from CrOS kernel engineers.
-Daniel
Post by Winkler, Tomas
- usual linux distros don't care about content protection, so won't even
enable the MEI_HDCP driver
- iotg/embedded either wants it (and then probably always) or doesn't
want it (in which case it won't be built in). Plus embedded tends to
have all built-in drivers anyway, so they all load in order.
It really depends, there product range si really wide.
And I don't want to review the validation coverage and locking and
runtime consequences of making this possible, since I'm lazy :-)
- I looked lockdep splat in v7 again in more detail. It's not actually an
issue with component usage, but just unlucky to now run into a
preexisting issue: Any driver you unbind through the sysfs file will
generate a lockdep splat if it removes any sysfs files itself. That's a
pre-existing bug, easily reproduced by unbinding i915.
Only thing new is that component connects the snd-hda-intel and i915
unload sequence through the component lock (but does _not_ create a
loop), and since we unbind snd-hda-intel through sysfs and i919
unregisters _lots_ of sysfs files lockdep now sees the cycle. It's
always been there.
I'm working on a separate patch to fix this.
Cheers, Daniel
Post by C, Ramalingam
Post by Daniel Vetter
Post by Ramalingam C
+
i915_driver_unregister(dev_priv);
if (i915_gem_suspend(dev_priv)) diff --git
a/drivers/gpu/drm/i915/intel_dp.c
b/drivers/gpu/drm/i915/intel_dp.c index 18e3a5a3d873..ac62af073688
100644
--- a/drivers/gpu/drm/i915/intel_dp.c
+++ b/drivers/gpu/drm/i915/intel_dp.c
@@ -6675,7 +6675,8 @@ intel_dp_init_connector(struct
intel_digital_port *intel_dig_port,
Post by C, Ramalingam
Post by Daniel Vetter
Post by Ramalingam C
intel_dp_add_properties(intel_dp, connector);
if (is_hdcp_supported(dev_priv, port) && !intel_dp_is_edp(intel_dp)) {
- int ret = intel_hdcp_init(intel_connector,
&intel_dp_hdcp_shim);
Post by C, Ramalingam
Post by Daniel Vetter
Post by Ramalingam C
+ int ret = intel_hdcp_init(intel_connector,
&intel_dp_hdcp_shim,
Post by C, Ramalingam
Post by Daniel Vetter
Post by Ramalingam C
+ false);
if (ret)
DRM_DEBUG_KMS("HDCP init failed, skipping.\n");
}
diff --git a/drivers/gpu/drm/i915/intel_drv.h
b/drivers/gpu/drm/i915/intel_drv.h
index a62d77b76291..85a526598096 100644
--- a/drivers/gpu/drm/i915/intel_drv.h
+++ b/drivers/gpu/drm/i915/intel_drv.h
@@ -388,6 +388,17 @@ struct intel_hdcp {
u64 value;
struct delayed_work check_work;
struct work_struct prop_work;
+
+ /* HDCP2.2 related definitions */
+ /* Flag indicates whether this connector supports HDCP2.2 or not. */
+ u8 hdcp2_supported;
+
+ /*
+ * Content Stream Type defined by content owner. TYPE0(0x0) content
can
Post by C, Ramalingam
Post by Daniel Vetter
Post by Ramalingam C
+ * flow in the link protected by HDCP2.2 or HDCP1.4, where as
TYPE1(0x1)
Post by C, Ramalingam
Post by Daniel Vetter
Post by Ramalingam C
+ * content can flow only through a link protected by HDCP2.2.
+ */
+ u8 content_type;
};
struct intel_connector {
@@ -2016,12 +2027,15 @@ void intel_hdcp_atomic_check(struct
drm_connector *connector,
Post by C, Ramalingam
Post by Daniel Vetter
Post by Ramalingam C
struct drm_connector_state *old_state,
struct drm_connector_state *new_state);
int intel_hdcp_init(struct intel_connector *connector,
- const struct intel_hdcp_shim *hdcp_shim);
+ const struct intel_hdcp_shim *hdcp_shim,
+ bool hdcp2_supported);
int intel_hdcp_enable(struct intel_connector *connector);
int intel_hdcp_disable(struct intel_connector *connector);
int intel_hdcp_check_link(struct intel_connector *connector);
bool is_hdcp_supported(struct drm_i915_private *dev_priv, enum port
port);
Post by C, Ramalingam
Post by Daniel Vetter
Post by Ramalingam C
bool intel_hdcp_capable(struct intel_connector *connector);
+bool is_hdcp2_supported(struct drm_i915_private *dev_priv); void
+intel_hdcp_exit(struct drm_i915_private *dev_priv);
/* intel_psr.c */
#define CAN_PSR(dev_priv) (HAS_PSR(dev_priv) &&
dev_priv->psr.sink_support) diff --git
a/drivers/gpu/drm/i915/intel_hdcp.c
b/drivers/gpu/drm/i915/intel_hdcp.c
index 04ba6c13e715..99dddb540958 100644
--- a/drivers/gpu/drm/i915/intel_hdcp.c
+++ b/drivers/gpu/drm/i915/intel_hdcp.c
@@ -730,6 +730,65 @@ struct intel_connector
*intel_hdcp_to_connector(struct intel_hdcp *hdcp)
Post by C, Ramalingam
Post by Daniel Vetter
Post by Ramalingam C
return container_of(hdcp, struct intel_connector, hdcp);
}
+/* Implements Part 3 of the HDCP authorization procedure */ int
+intel_hdcp_check_link(struct intel_connector *connector)
Spurious movement of this function. Please drop, or if you need this
function moved, split it out into a separate patch that only moves
the function around.
Intention is gathering the hdcp1.4 functions together before implementing
the hdcp2.2 code.
Post by C, Ramalingam
But as you mentioned i will do it as a separate patch.
Ah yeah that makes sense, but commit message didn't explain that. Separate
patch sounds good.
-Daniel
Post by C, Ramalingam
--Ram
Post by Daniel Vetter
Post by Ramalingam C
+{
+ struct intel_hdcp *hdcp = &connector->hdcp;
+ struct drm_i915_private *dev_priv = connector->base.dev-
dev_private;
Post by Daniel Vetter
Post by Ramalingam C
+ struct intel_digital_port *intel_dig_port =
conn_to_dig_port(connector);
Post by C, Ramalingam
Post by Daniel Vetter
Post by Ramalingam C
+ enum port port = intel_dig_port->base.port;
+ int ret = 0;
+
+ if (!hdcp->shim)
+ return -ENOENT;
+
+ mutex_lock(&hdcp->mutex);
+
+ if (hdcp->value == DRM_MODE_CONTENT_PROTECTION_UNDESIRED)
+ goto out;
+
+ if (!(I915_READ(PORT_HDCP_STATUS(port)) & HDCP_STATUS_ENC)) {
+ DRM_ERROR("%s:%d HDCP check failed: link is not
encrypted,%x\n",
Post by C, Ramalingam
Post by Daniel Vetter
Post by Ramalingam C
+ connector->base.name, connector->base.base.id,
+ I915_READ(PORT_HDCP_STATUS(port)));
+ ret = -ENXIO;
+ hdcp->value = DRM_MODE_CONTENT_PROTECTION_DESIRED;
+ schedule_work(&hdcp->prop_work);
+ goto out;
+ }
+
+ if (hdcp->shim->check_link(intel_dig_port)) {
+ if (hdcp->value !=
DRM_MODE_CONTENT_PROTECTION_UNDESIRED) {
Post by C, Ramalingam
Post by Daniel Vetter
Post by Ramalingam C
+ hdcp->value =
DRM_MODE_CONTENT_PROTECTION_ENABLED;
Post by C, Ramalingam
Post by Daniel Vetter
Post by Ramalingam C
+ schedule_work(&hdcp->prop_work);
+ }
+ goto out;
+ }
+
+ DRM_DEBUG_KMS("[%s:%d] HDCP link failed, retrying
authentication\n",
Post by C, Ramalingam
Post by Daniel Vetter
Post by Ramalingam C
+ connector->base.name, connector->base.base.id);
+
+ ret = _intel_hdcp_disable(connector);
+ if (ret) {
+ DRM_ERROR("Failed to disable hdcp (%d)\n", ret);
+ hdcp->value = DRM_MODE_CONTENT_PROTECTION_DESIRED;
+ schedule_work(&hdcp->prop_work);
+ goto out;
+ }
+
+ ret = _intel_hdcp_enable(connector);
+ if (ret) {
+ DRM_ERROR("Failed to enable hdcp (%d)\n", ret);
+ hdcp->value = DRM_MODE_CONTENT_PROTECTION_DESIRED;
+ schedule_work(&hdcp->prop_work);
+ goto out;
+ }
+
+ mutex_unlock(&hdcp->mutex);
+ return ret;
+}
+
static void intel_hdcp_check_work(struct work_struct *work)
{
struct intel_hdcp *hdcp = container_of(to_delayed_work(work),
@@ -774,14 +833,34 @@ bool is_hdcp_supported(struct
drm_i915_private *dev_priv, enum port port)
Post by C, Ramalingam
Post by Daniel Vetter
Post by Ramalingam C
!IS_CHERRYVIEW(dev_priv) && port < PORT_E);
}
+bool is_hdcp2_supported(struct drm_i915_private *dev_priv) {
+ return ((INTEL_GEN(dev_priv) >= 10 || IS_GEMINILAKE(dev_priv) ||
+ IS_KABYLAKE(dev_priv)) &&
IS_ENABLED(CONFIG_INTEL_MEI_HDCP));
Post by C, Ramalingam
Post by Daniel Vetter
Post by Ramalingam C
+}
+
+static void intel_hdcp2_init(struct intel_connector *connector) {
+ struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
+ struct intel_hdcp *hdcp = &connector->hdcp;
+
+ WARN_ON(!is_hdcp2_supported(dev_priv));
+
+ /* TODO: MEI interface needs to be initialized here */
+ hdcp->hdcp2_supported = 1;
+}
+
int intel_hdcp_init(struct intel_connector *connector,
- const struct intel_hdcp_shim *shim)
+ const struct intel_hdcp_shim *shim,
+ bool hdcp2_supported)
{
struct intel_hdcp *hdcp = &connector->hdcp;
int ret;
- ret = drm_connector_attach_content_protection_property(
- &connector->base);
+ if (!shim)
+ return -EINVAL;
+
+ ret =
+drm_connector_attach_content_protection_property(&connector->base
+);
if (ret)
return ret;
@@ -789,9 +868,37 @@ int intel_hdcp_init(struct intel_connector
*connector,
Post by C, Ramalingam
Post by Daniel Vetter
Post by Ramalingam C
mutex_init(&hdcp->mutex);
INIT_DELAYED_WORK(&hdcp->check_work, intel_hdcp_check_work);
INIT_WORK(&hdcp->prop_work, intel_hdcp_prop_work);
+
+ if (hdcp2_supported)
+ intel_hdcp2_init(connector);
+
return 0;
}
+void intel_hdcp_exit(struct drm_i915_private *dev_priv) {
+ struct drm_device *dev = &dev_priv->drm;
+ struct intel_connector *intel_connector;
+ struct drm_connector *connector;
+ struct drm_connector_list_iter conn_iter;
+
+ drm_connector_list_iter_begin(dev, &conn_iter);
+ drm_for_each_connector_iter(connector, &conn_iter) {
+ intel_connector = to_intel_connector(connector);
+
+ if (!intel_connector->hdcp.shim)
+ continue;
+
+ intel_hdcp_disable(intel_connector);
+
+ mutex_lock(&intel_connector->hdcp.mutex);
+ intel_connector->hdcp.hdcp2_supported = 0;
+ intel_connector->hdcp.shim = NULL;
+ mutex_unlock(&intel_connector->hdcp.mutex);
+ }
+ drm_connector_list_iter_end(&conn_iter);
+}
+
int intel_hdcp_enable(struct intel_connector *connector)
{
@@ void intel_hdcp_atomic_check(struct drm_connector *connector,
new_state->crtc);
crtc_state->mode_changed = true;
}
-
-/* Implements Part 3 of the HDCP authorization procedure */ -int
intel_hdcp_check_link(struct intel_connector *connector) -{
- struct intel_hdcp *hdcp = &connector->hdcp;
- struct drm_i915_private *dev_priv = connector->base.dev-
dev_private;
Post by Daniel Vetter
Post by Ramalingam C
- struct intel_digital_port *intel_dig_port =
conn_to_dig_port(connector);
Post by C, Ramalingam
Post by Daniel Vetter
Post by Ramalingam C
- enum port port = intel_dig_port->base.port;
- int ret = 0;
-
- if (!hdcp->shim)
- return -ENOENT;
-
- mutex_lock(&hdcp->mutex);
-
- if (hdcp->value == DRM_MODE_CONTENT_PROTECTION_UNDESIRED)
- goto out;
-
- if (!(I915_READ(PORT_HDCP_STATUS(port)) & HDCP_STATUS_ENC)) {
- DRM_ERROR("%s:%d HDCP check failed: link is not
encrypted,%x\n",
Post by C, Ramalingam
Post by Daniel Vetter
Post by Ramalingam C
- connector->base.name, connector->base.base.id,
- I915_READ(PORT_HDCP_STATUS(port)));
- ret = -ENXIO;
- hdcp->value = DRM_MODE_CONTENT_PROTECTION_DESIRED;
- schedule_work(&hdcp->prop_work);
- goto out;
- }
-
- if (hdcp->shim->check_link(intel_dig_port)) {
- if (hdcp->value !=
DRM_MODE_CONTENT_PROTECTION_UNDESIRED) {
Post by C, Ramalingam
Post by Daniel Vetter
Post by Ramalingam C
- hdcp->value =
DRM_MODE_CONTENT_PROTECTION_ENABLED;
Post by C, Ramalingam
Post by Daniel Vetter
Post by Ramalingam C
- schedule_work(&hdcp->prop_work);
- }
- goto out;
- }
-
- DRM_DEBUG_KMS("[%s:%d] HDCP link failed, retrying
authentication\n",
Post by C, Ramalingam
Post by Daniel Vetter
Post by Ramalingam C
- connector->base.name, connector->base.base.id);
-
- ret = _intel_hdcp_disable(connector);
- if (ret) {
- DRM_ERROR("Failed to disable hdcp (%d)\n", ret);
- hdcp->value = DRM_MODE_CONTENT_PROTECTION_DESIRED;
- schedule_work(&hdcp->prop_work);
- goto out;
- }
-
- ret = _intel_hdcp_enable(connector);
- if (ret) {
- DRM_DEBUG_KMS("Failed to enable hdcp (%d)\n", ret);
- hdcp->value = DRM_MODE_CONTENT_PROTECTION_DESIRED;
- schedule_work(&hdcp->prop_work);
- goto out;
- }
-
- mutex_unlock(&hdcp->mutex);
- return ret;
-}
diff --git a/drivers/gpu/drm/i915/intel_hdmi.c
b/drivers/gpu/drm/i915/intel_hdmi.c
index e2c6a2b3e8f2..e755a3370bca 100644
--- a/drivers/gpu/drm/i915/intel_hdmi.c
+++ b/drivers/gpu/drm/i915/intel_hdmi.c
@@ -2417,7 +2417,7 @@ void intel_hdmi_init_connector(struct
intel_digital_port *intel_dig_port,
Post by C, Ramalingam
Post by Daniel Vetter
Post by Ramalingam C
if (is_hdcp_supported(dev_priv, port)) {
int ret = intel_hdcp_init(intel_connector,
- &intel_hdmi_hdcp_shim);
+ &intel_hdmi_hdcp_shim, false);
if (ret)
DRM_DEBUG_KMS("HDCP init failed, skipping.\n");
The actual change left to wire hdcp2_supported seems ok with me, but
hard to tell without seeing more yet :-) -Daniel
Post by Ramalingam C
}
--
2.7.4
--
Daniel Vetter
Software Engineer, Intel Corporation
http://blog.ffwll.ch
--
Daniel Vetter
Software Engineer, Intel Corporation
http://blog.ffwll.ch
Ramalingam C
2018-11-27 10:43:03 UTC
Permalink
Defining the mei-i915 interface functions and initialization of
the interface.

Signed-off-by: Ramalingam C <***@intel.com>
Signed-off-by: Tomas Winkler <***@intel.com>
---
drivers/gpu/drm/i915/i915_drv.h | 2 +
drivers/gpu/drm/i915/intel_drv.h | 7 +
drivers/gpu/drm/i915/intel_hdcp.c | 442 +++++++++++++++++++++++++++++++++++++-
include/drm/i915_component.h | 71 ++++++
4 files changed, 521 insertions(+), 1 deletion(-)

diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h
index f763b30f98d9..b68bc980b7cd 100644
--- a/drivers/gpu/drm/i915/i915_drv.h
+++ b/drivers/gpu/drm/i915/i915_drv.h
@@ -2015,6 +2015,8 @@ struct drm_i915_private {

struct i915_pmu pmu;

+ struct i915_hdcp_component_master *hdcp_comp;
+
/*
* NOTE: This is the dri1/ums dungeon, don't add stuff here. Your patch
* will be rejected. Instead look for a better place.
diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h
index 85a526598096..bde82f3ada85 100644
--- a/drivers/gpu/drm/i915/intel_drv.h
+++ b/drivers/gpu/drm/i915/intel_drv.h
@@ -29,6 +29,7 @@
#include <linux/i2c.h>
#include <linux/hdmi.h>
#include <linux/sched/clock.h>
+#include <linux/mei_hdcp.h>
#include <drm/i915_drm.h>
#include "i915_drv.h"
#include <drm/drm_crtc.h>
@@ -379,6 +380,9 @@ struct intel_hdcp_shim {
/* Detects panel's hdcp capability. This is optional for HDMI. */
int (*hdcp_capable)(struct intel_digital_port *intel_dig_port,
bool *hdcp_capable);
+
+ /* Detects the HDCP protocol(DP/HDMI) required on the port */
+ enum mei_hdcp_wired_protocol (*hdcp_protocol)(void);
};

struct intel_hdcp {
@@ -399,6 +403,9 @@ struct intel_hdcp {
* content can flow only through a link protected by HDCP2.2.
*/
u8 content_type;
+
+ /* mei interface related information */
+ struct mei_hdcp_data mei_data;
};

struct intel_connector {
diff --git a/drivers/gpu/drm/i915/intel_hdcp.c b/drivers/gpu/drm/i915/intel_hdcp.c
index 99dddb540958..760780f1105c 100644
--- a/drivers/gpu/drm/i915/intel_hdcp.c
+++ b/drivers/gpu/drm/i915/intel_hdcp.c
@@ -8,14 +8,20 @@

#include <drm/drmP.h>
#include <drm/drm_hdcp.h>
+#include <drm/i915_component.h>
#include <linux/i2c.h>
#include <linux/random.h>
+#include <linux/component.h>

#include "intel_drv.h"
#include "i915_reg.h"

#define KEY_LOAD_TRIES 5
#define TIME_FOR_ENCRYPT_STATUS_CHANGE 50
+#define GET_MEI_DDI_INDEX(p) ({ \
+ typeof(p) __p = (p); \
+ __p == PORT_A ? MEI_DDI_A : (enum mei_hdcp_ddi)__p;\
+})

static
bool intel_hdcp_is_ksv_valid(u8 *ksv)
@@ -833,6 +839,417 @@ bool is_hdcp_supported(struct drm_i915_private *dev_priv, enum port port)
!IS_CHERRYVIEW(dev_priv) && port < PORT_E);
}

+static __attribute__((unused)) int
+hdcp2_prepare_ake_init(struct intel_connector *connector,
+ struct hdcp2_ake_init *ake_data)
+{
+ struct mei_hdcp_data *data = &connector->hdcp.mei_data;
+ struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
+ struct i915_hdcp_component_master *comp = dev_priv->hdcp_comp;
+ int ret;
+
+ if (!comp)
+ return -EINVAL;
+
+ mutex_lock(&comp->mutex);
+ if (!comp->ops || !comp->dev) {
+ mutex_unlock(&comp->mutex);
+ return -EINVAL;
+ }
+
+ if (data->port == MEI_DDI_INVALID_PORT && connector->encoder)
+ data->port = GET_MEI_DDI_INDEX(connector->encoder->port);
+
+ /* Clear ME FW instance for the port, just incase */
+ comp->ops->close_hdcp_session(comp->dev, data);
+
+ ret = comp->ops->initiate_hdcp2_session(comp->dev,
+ data, ake_data);
+ mutex_unlock(&comp->mutex);
+
+ return ret;
+}
+
+static __attribute__((unused)) int
+hdcp2_verify_rx_cert_prepare_km(struct intel_connector *connector,
+ struct hdcp2_ake_send_cert *rx_cert,
+ bool *paired,
+ struct hdcp2_ake_no_stored_km *ek_pub_km,
+ size_t *msg_sz)
+{
+ struct mei_hdcp_data *data = &connector->hdcp.mei_data;
+ struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
+ struct i915_hdcp_component_master *comp = dev_priv->hdcp_comp;
+ int ret;
+
+ if (!comp)
+ return -EINVAL;
+
+ mutex_lock(&comp->mutex);
+ if (!comp->ops || !comp->dev || data->port == MEI_DDI_INVALID_PORT) {
+ mutex_unlock(&comp->mutex);
+ return -EINVAL;
+ }
+
+ ret = comp->ops->verify_receiver_cert_prepare_km(comp->dev, data,
+ rx_cert, paired,
+ ek_pub_km, msg_sz);
+ if (ret < 0)
+ comp->ops->close_hdcp_session(comp->dev, data);
+ mutex_unlock(&comp->mutex);
+
+ return ret;
+}
+
+static __attribute__((unused)) int
+hdcp2_verify_hprime(struct intel_connector *connector,
+ struct hdcp2_ake_send_hprime *rx_hprime)
+{
+ struct mei_hdcp_data *data = &connector->hdcp.mei_data;
+ struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
+ struct i915_hdcp_component_master *comp = dev_priv->hdcp_comp;
+ int ret;
+
+ if (!comp)
+ return -EINVAL;
+
+ mutex_lock(&comp->mutex);
+ if (!comp->ops || !comp->dev || data->port == MEI_DDI_INVALID_PORT) {
+ mutex_unlock(&comp->mutex);
+ return -EINVAL;
+ }
+
+ ret = comp->ops->verify_hprime(comp->dev, data, rx_hprime);
+ if (ret < 0)
+ comp->ops->close_hdcp_session(comp->dev, data);
+ mutex_unlock(&comp->mutex);
+
+ return ret;
+}
+
+static __attribute__((unused)) int
+hdcp2_store_pairing_info(struct intel_connector *connector,
+ struct hdcp2_ake_send_pairing_info *pairing_info)
+{
+ struct mei_hdcp_data *data = &connector->hdcp.mei_data;
+ struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
+ struct i915_hdcp_component_master *comp = dev_priv->hdcp_comp;
+ int ret;
+
+ if (!comp)
+ return -EINVAL;
+
+ mutex_lock(&comp->mutex);
+ if (!comp->ops || !comp->dev || data->port == MEI_DDI_INVALID_PORT) {
+ mutex_unlock(&comp->mutex);
+ return -EINVAL;
+ }
+
+ ret = comp->ops->store_pairing_info(comp->dev,
+ data, pairing_info);
+ if (ret < 0)
+ comp->ops->close_hdcp_session(comp->dev, data);
+ mutex_unlock(&comp->mutex);
+
+ return ret;
+}
+
+static __attribute__((unused)) int
+hdcp2_prepare_lc_init(struct intel_connector *connector,
+ struct hdcp2_lc_init *lc_init)
+{
+ struct mei_hdcp_data *data = &connector->hdcp.mei_data;
+ struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
+ struct i915_hdcp_component_master *comp = dev_priv->hdcp_comp;
+ int ret;
+
+ if (!comp)
+ return -EINVAL;
+
+ mutex_lock(&comp->mutex);
+ if (!comp->ops || !comp->dev || data->port == MEI_DDI_INVALID_PORT) {
+ mutex_unlock(&comp->mutex);
+ return -EINVAL;
+ }
+
+ ret = comp->ops->initiate_locality_check(comp->dev,
+ data, lc_init);
+ if (ret < 0)
+ comp->ops->close_hdcp_session(comp->dev, data);
+ mutex_unlock(&comp->mutex);
+
+ return ret;
+}
+
+static __attribute__((unused)) int
+hdcp2_verify_lprime(struct intel_connector *connector,
+ struct hdcp2_lc_send_lprime *rx_lprime)
+{
+ struct mei_hdcp_data *data = &connector->hdcp.mei_data;
+ struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
+ struct i915_hdcp_component_master *comp = dev_priv->hdcp_comp;
+ int ret;
+
+ if (!comp)
+ return -EINVAL;
+
+ mutex_lock(&comp->mutex);
+ if (!comp->ops || !comp->dev || data->port == MEI_DDI_INVALID_PORT) {
+ mutex_unlock(&comp->mutex);
+ return -EINVAL;
+ }
+
+ ret = comp->ops->verify_lprime(comp->dev, data, rx_lprime);
+ if (ret < 0)
+ comp->ops->close_hdcp_session(comp->dev, data);
+ mutex_unlock(&comp->mutex);
+
+ return ret;
+}
+
+static __attribute__((unused))
+int hdcp2_prepare_skey(struct intel_connector *connector,
+ struct hdcp2_ske_send_eks *ske_data)
+{
+ struct mei_hdcp_data *data = &connector->hdcp.mei_data;
+ struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
+ struct i915_hdcp_component_master *comp = dev_priv->hdcp_comp;
+ int ret;
+
+ if (!comp)
+ return -EINVAL;
+
+ mutex_lock(&comp->mutex);
+ if (!comp->ops || !comp->dev || data->port == MEI_DDI_INVALID_PORT) {
+ mutex_unlock(&comp->mutex);
+ return -EINVAL;
+ }
+
+ ret = comp->ops->get_session_key(comp->dev, data, ske_data);
+ if (ret < 0)
+ comp->ops->close_hdcp_session(comp->dev, data);
+ mutex_unlock(&comp->mutex);
+
+ return ret;
+}
+
+static __attribute__((unused)) int
+hdcp2_verify_rep_topology_prepare_ack(struct intel_connector *connector,
+ struct hdcp2_rep_send_receiverid_list
+ *rep_topology,
+ struct hdcp2_rep_send_ack *rep_send_ack)
+{
+ struct mei_hdcp_data *data = &connector->hdcp.mei_data;
+ struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
+ struct i915_hdcp_component_master *comp = dev_priv->hdcp_comp;
+ int ret;
+
+ if (!comp)
+ return -EINVAL;
+
+ mutex_lock(&comp->mutex);
+ if (!comp->ops || !comp->dev || data->port == MEI_DDI_INVALID_PORT) {
+ mutex_unlock(&comp->mutex);
+ return -EINVAL;
+ }
+
+ ret = comp->ops->repeater_check_flow_prepare_ack(comp->dev,
+ data, rep_topology,
+ rep_send_ack);
+ if (ret < 0)
+ comp->ops->close_hdcp_session(comp->dev, data);
+ mutex_unlock(&comp->mutex);
+
+ return ret;
+}
+
+static __attribute__((unused)) int
+hdcp2_verify_mprime(struct intel_connector *connector,
+ struct hdcp2_rep_stream_ready *stream_ready)
+{
+ struct mei_hdcp_data *data = &connector->hdcp.mei_data;
+ struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
+ struct i915_hdcp_component_master *comp = dev_priv->hdcp_comp;
+ int ret;
+
+ if (!comp)
+ return -EINVAL;
+
+ mutex_lock(&comp->mutex);
+ if (!comp->ops || !comp->dev || data->port == MEI_DDI_INVALID_PORT) {
+ mutex_unlock(&comp->mutex);
+ return -EINVAL;
+ }
+
+ ret = comp->ops->verify_mprime(comp->dev, data, stream_ready);
+ if (ret < 0)
+ comp->ops->close_hdcp_session(comp->dev, data);
+ mutex_unlock(&comp->mutex);
+
+ return ret;
+}
+
+static __attribute__((unused))
+int hdcp2_authenticate_port(struct intel_connector *connector)
+{
+ struct mei_hdcp_data *data = &connector->hdcp.mei_data;
+ struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
+ struct i915_hdcp_component_master *comp = dev_priv->hdcp_comp;
+ int ret;
+
+ if (!comp)
+ return -EINVAL;
+
+ mutex_lock(&comp->mutex);
+ if (!comp->ops || !comp->dev || data->port == MEI_DDI_INVALID_PORT) {
+ mutex_unlock(&comp->mutex);
+ return -EINVAL;
+ }
+
+ ret = comp->ops->enable_hdcp_authentication(comp->dev, data);
+ if (ret < 0)
+ comp->ops->close_hdcp_session(comp->dev, data);
+ mutex_unlock(&comp->mutex);
+
+ return ret;
+}
+
+static __attribute__((unused))
+int hdcp2_close_mei_session(struct intel_connector *connector)
+{
+ struct mei_hdcp_data *data = &connector->hdcp.mei_data;
+ struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
+ struct i915_hdcp_component_master *comp = dev_priv->hdcp_comp;
+ int ret;
+
+ if (!comp)
+ return -EINVAL;
+
+ mutex_lock(&comp->mutex);
+ if (!comp->ops || !comp->dev ||
+ data->port == MEI_DDI_INVALID_PORT) {
+ mutex_unlock(&comp->mutex);
+ return -EINVAL;
+ }
+
+ ret = comp->ops->close_hdcp_session(comp->dev, data);
+ mutex_unlock(&comp->mutex);
+ return ret;
+}
+
+static __attribute__((unused))
+int hdcp2_deauthenticate_port(struct intel_connector *connector)
+{
+ return hdcp2_close_mei_session(connector);
+}
+
+static int i915_hdcp_component_master_bind(struct device *dev)
+{
+ struct drm_i915_private *dev_priv = kdev_to_i915(dev);
+
+ return component_bind_all(dev, dev_priv->hdcp_comp);
+}
+
+static void intel_connectors_hdcp_disable(struct drm_i915_private *dev_priv)
+{
+ struct drm_device *dev = &dev_priv->drm;
+ struct intel_connector *intel_connector;
+ struct drm_connector *connector;
+ struct drm_connector_list_iter conn_iter;
+
+ drm_connector_list_iter_begin(dev, &conn_iter);
+ drm_for_each_connector_iter(connector, &conn_iter) {
+ intel_connector = to_intel_connector(connector);
+ if (!(intel_connector->hdcp.shim))
+ continue;
+
+ intel_hdcp_disable(intel_connector);
+ }
+ drm_connector_list_iter_end(&conn_iter);
+}
+
+static void i915_hdcp_component_master_unbind(struct device *dev)
+{
+ struct drm_i915_private *dev_priv = kdev_to_i915(dev);
+
+ intel_connectors_hdcp_disable(dev_priv);
+ component_unbind_all(dev, dev_priv->hdcp_comp);
+}
+
+static const struct component_master_ops i915_hdcp_component_master_ops = {
+ .bind = i915_hdcp_component_master_bind,
+ .unbind = i915_hdcp_component_master_unbind,
+};
+
+static int i915_hdcp_component_match(struct device *dev, void *data)
+{
+ return !strcmp(dev->driver->name, "mei_hdcp");
+}
+
+static int intel_hdcp_component_init(struct intel_connector *connector)
+{
+ struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
+ struct i915_hdcp_component_master *comp;
+ struct component_match *match = NULL;
+ int ret;
+
+ comp = kzalloc(sizeof(*comp), GFP_KERNEL);
+ if (!comp)
+ return -ENOMEM;
+
+ mutex_init(&comp->mutex);
+ dev_priv->hdcp_comp = comp;
+
+ component_match_add(dev_priv->drm.dev, &match,
+ i915_hdcp_component_match, dev_priv);
+ ret = component_master_add_with_match(dev_priv->drm.dev,
+ &i915_hdcp_component_master_ops,
+ match);
+ if (ret < 0)
+ goto out_err;
+
+ DRM_INFO("I915 hdcp component master added.\n");
+ return ret;
+
+out_err:
+ component_master_del(dev_priv->drm.dev,
+ &i915_hdcp_component_master_ops);
+ kfree(comp);
+ dev_priv->hdcp_comp = NULL;
+ DRM_ERROR("Failed to add i915 hdcp component master (%d)\n", ret);
+
+ return ret;
+}
+
+static int initialize_mei_hdcp_data(struct intel_connector *connector)
+{
+ struct intel_hdcp *hdcp = &connector->hdcp;
+ struct mei_hdcp_data *data = &hdcp->mei_data;
+ enum port port;
+
+ if (connector->encoder) {
+ port = connector->encoder->port;
+ data->port = GET_MEI_DDI_INDEX(port);
+ }
+
+ data->port_type = MEI_HDCP_PORT_TYPE_INTEGRATED;
+ data->protocol = hdcp->shim->hdcp_protocol();
+
+ data->k = 1;
+ if (!data->streams)
+ data->streams = kcalloc(data->k, sizeof(data->streams[0]),
+ GFP_KERNEL);
+ if (!data->streams) {
+ DRM_ERROR("Out of Memory\n");
+ return -ENOMEM;
+ }
+
+ data->streams[0].stream_id = 0;
+ data->streams[0].stream_type = hdcp->content_type;
+
+ return 0;
+}
+
bool is_hdcp2_supported(struct drm_i915_private *dev_priv)
{
return ((INTEL_GEN(dev_priv) >= 10 || IS_GEMINILAKE(dev_priv) ||
@@ -843,10 +1260,25 @@ static void intel_hdcp2_init(struct intel_connector *connector)
{
struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
struct intel_hdcp *hdcp = &connector->hdcp;
+ int ret;

WARN_ON(!is_hdcp2_supported(dev_priv));

- /* TODO: MEI interface needs to be initialized here */
+ ret = initialize_mei_hdcp_data(connector);
+ if (ret) {
+ DRM_DEBUG_KMS("Mei hdcp data init failed\n");
+ return;
+ }
+
+ if (!dev_priv->hdcp_comp)
+ ret = intel_hdcp_component_init(connector);
+
+ if (ret) {
+ DRM_DEBUG_KMS("HDCP comp init failed\n");
+ kfree(hdcp->mei_data.streams);
+ return;
+ }
+
hdcp->hdcp2_supported = 1;
}

@@ -894,9 +1326,17 @@ void intel_hdcp_exit(struct drm_i915_private *dev_priv)
mutex_lock(&intel_connector->hdcp.mutex);
intel_connector->hdcp.hdcp2_supported = 0;
intel_connector->hdcp.shim = NULL;
+ kfree(intel_connector->hdcp.mei_data.streams);
mutex_unlock(&intel_connector->hdcp.mutex);
}
drm_connector_list_iter_end(&conn_iter);
+
+ if (dev_priv->hdcp_comp) {
+ component_master_del(dev_priv->drm.dev,
+ &i915_hdcp_component_master_ops);
+ kfree(dev_priv->hdcp_comp);
+ dev_priv->hdcp_comp = NULL;
+ }
}

int intel_hdcp_enable(struct intel_connector *connector)
diff --git a/include/drm/i915_component.h b/include/drm/i915_component.h
index fca22d463e1b..12268228f4dc 100644
--- a/include/drm/i915_component.h
+++ b/include/drm/i915_component.h
@@ -24,8 +24,12 @@
#ifndef _I915_COMPONENT_H_
#define _I915_COMPONENT_H_

+#include <linux/mutex.h>
+
#include "drm_audio_component.h"

+#include <drm/drm_hdcp.h>
+
/* MAX_PORT is the number of port
* It must be sync with I915_MAX_PORTS defined i915_drv.h
*/
@@ -46,4 +50,71 @@ struct i915_audio_component {
int aud_sample_rate[MAX_PORTS];
};

+struct i915_hdcp_component_ops {
+ /**
+ * @owner: mei_hdcp module
+ */
+ struct module *owner;
+
+ int (*initiate_hdcp2_session)(struct device *dev,
+ void *hdcp_data,
+ struct hdcp2_ake_init *ake_data);
+ int (*verify_receiver_cert_prepare_km)(struct device *dev,
+ void *hdcp_data,
+ struct hdcp2_ake_send_cert
+ *rx_cert,
+ bool *km_stored,
+ struct hdcp2_ake_no_stored_km
+ *ek_pub_km,
+ size_t *msg_sz);
+ int (*verify_hprime)(struct device *dev,
+ void *hdcp_data,
+ struct hdcp2_ake_send_hprime *rx_hprime);
+ int (*store_pairing_info)(struct device *dev,
+ void *hdcp_data,
+ struct hdcp2_ake_send_pairing_info
+ *pairing_info);
+ int (*initiate_locality_check)(struct device *dev,
+ void *hdcp_data,
+ struct hdcp2_lc_init *lc_init_data);
+ int (*verify_lprime)(struct device *dev,
+ void *hdcp_data,
+ struct hdcp2_lc_send_lprime *rx_lprime);
+ int (*get_session_key)(struct device *dev,
+ void *hdcp_data,
+ struct hdcp2_ske_send_eks *ske_data);
+ int (*repeater_check_flow_prepare_ack)(struct device *dev,
+ void *hdcp_data,
+ struct hdcp2_rep_send_receiverid_list
+ *rep_topology,
+ struct hdcp2_rep_send_ack
+ *rep_send_ack);
+ int (*verify_mprime)(struct device *dev,
+ void *hdcp_data,
+ struct hdcp2_rep_stream_ready *stream_ready);
+ int (*enable_hdcp_authentication)(struct device *dev,
+ void *hdcp_data);
+ int (*close_hdcp_session)(struct device *dev,
+ void *hdcp_data);
+};
+
+/**
+ * struct i915_hdcp_component_master - Used for communication between i915
+ * and mei_hdcp for HDCP2.2 services.
+ */
+struct i915_hdcp_component_master {
+ /**
+ * @dev: a device providing hdcp
+ */
+ struct device *dev;
+ /**
+ * @mutex: Mutex to protect the state of dev
+ */
+ struct mutex mutex;
+ /**
+ * @ops: Ops implemented by mei_hdcp driver, used by i915 driver.
+ */
+ const struct i915_hdcp_component_ops *ops;
+};
+
#endif /* _I915_COMPONENT_H_ */
--
2.7.4
Daniel Vetter
2018-12-06 10:23:03 UTC
Permalink
Post by Ramalingam C
Defining the mei-i915 interface functions and initialization of
the interface.
---
drivers/gpu/drm/i915/i915_drv.h | 2 +
drivers/gpu/drm/i915/intel_drv.h | 7 +
drivers/gpu/drm/i915/intel_hdcp.c | 442 +++++++++++++++++++++++++++++++++++++-
include/drm/i915_component.h | 71 ++++++
4 files changed, 521 insertions(+), 1 deletion(-)
diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h
index f763b30f98d9..b68bc980b7cd 100644
--- a/drivers/gpu/drm/i915/i915_drv.h
+++ b/drivers/gpu/drm/i915/i915_drv.h
@@ -2015,6 +2015,8 @@ struct drm_i915_private {
struct i915_pmu pmu;
+ struct i915_hdcp_component_master *hdcp_comp;
+
/*
* NOTE: This is the dri1/ums dungeon, don't add stuff here. Your patch
* will be rejected. Instead look for a better place.
diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h
index 85a526598096..bde82f3ada85 100644
--- a/drivers/gpu/drm/i915/intel_drv.h
+++ b/drivers/gpu/drm/i915/intel_drv.h
@@ -29,6 +29,7 @@
#include <linux/i2c.h>
#include <linux/hdmi.h>
#include <linux/sched/clock.h>
+#include <linux/mei_hdcp.h>
#include <drm/i915_drm.h>
#include "i915_drv.h"
#include <drm/drm_crtc.h>
@@ -379,6 +380,9 @@ struct intel_hdcp_shim {
/* Detects panel's hdcp capability. This is optional for HDMI. */
int (*hdcp_capable)(struct intel_digital_port *intel_dig_port,
bool *hdcp_capable);
+
+ /* Detects the HDCP protocol(DP/HDMI) required on the port */
+ enum mei_hdcp_wired_protocol (*hdcp_protocol)(void);
Looking ahead, this seems hardwired to constant return value? Or why do we
need a function here?
Post by Ramalingam C
};
struct intel_hdcp {
@@ -399,6 +403,9 @@ struct intel_hdcp {
* content can flow only through a link protected by HDCP2.2.
*/
u8 content_type;
+
+ /* mei interface related information */
+ struct mei_hdcp_data mei_data;
};
struct intel_connector {
diff --git a/drivers/gpu/drm/i915/intel_hdcp.c b/drivers/gpu/drm/i915/intel_hdcp.c
index 99dddb540958..760780f1105c 100644
--- a/drivers/gpu/drm/i915/intel_hdcp.c
+++ b/drivers/gpu/drm/i915/intel_hdcp.c
@@ -8,14 +8,20 @@
#include <drm/drmP.h>
#include <drm/drm_hdcp.h>
+#include <drm/i915_component.h>
#include <linux/i2c.h>
#include <linux/random.h>
+#include <linux/component.h>
#include "intel_drv.h"
#include "i915_reg.h"
#define KEY_LOAD_TRIES 5
#define TIME_FOR_ENCRYPT_STATUS_CHANGE 50
+#define GET_MEI_DDI_INDEX(p) ({ \
+ typeof(p) __p = (p); \
+ __p == PORT_A ? MEI_DDI_A : (enum mei_hdcp_ddi)__p;\
+})
static
bool intel_hdcp_is_ksv_valid(u8 *ksv)
@@ -833,6 +839,417 @@ bool is_hdcp_supported(struct drm_i915_private *dev_priv, enum port port)
!IS_CHERRYVIEW(dev_priv) && port < PORT_E);
}
+static __attribute__((unused)) int
+hdcp2_prepare_ake_init(struct intel_connector *connector,
+ struct hdcp2_ake_init *ake_data)
+{
+ struct mei_hdcp_data *data = &connector->hdcp.mei_data;
+ struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
+ struct i915_hdcp_component_master *comp = dev_priv->hdcp_comp;
+ int ret;
+
+ if (!comp)
+ return -EINVAL;
+
+ mutex_lock(&comp->mutex);
+ if (!comp->ops || !comp->dev) {
+ mutex_unlock(&comp->mutex);
+ return -EINVAL;
+ }
+
+ if (data->port == MEI_DDI_INVALID_PORT && connector->encoder)
+ data->port = GET_MEI_DDI_INDEX(connector->encoder->port);
+
+ /* Clear ME FW instance for the port, just incase */
+ comp->ops->close_hdcp_session(comp->dev, data);
Sounds like a bug somewhere if we need this? I'd put this code into the
->initiate_hdcp2_session, with a big WARN_ON if it's actually needed.

"Just in case" papering over programming bugs of our own just makes
debugging harder.
Post by Ramalingam C
+
+ ret = comp->ops->initiate_hdcp2_session(comp->dev,
+ data, ake_data);
We should set the port only after successfully initializing this.
Post by Ramalingam C
+ mutex_unlock(&comp->mutex);
+
+ return ret;
+}
+
+static __attribute__((unused)) int
+hdcp2_verify_rx_cert_prepare_km(struct intel_connector *connector,
+ struct hdcp2_ake_send_cert *rx_cert,
+ bool *paired,
+ struct hdcp2_ake_no_stored_km *ek_pub_km,
+ size_t *msg_sz)
+{
+ struct mei_hdcp_data *data = &connector->hdcp.mei_data;
+ struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
+ struct i915_hdcp_component_master *comp = dev_priv->hdcp_comp;
+ int ret;
+
+ if (!comp)
+ return -EINVAL;
+
+ mutex_lock(&comp->mutex);
+ if (!comp->ops || !comp->dev || data->port == MEI_DDI_INVALID_PORT) {
These all look like programming mistakes that should result in a WARN_ON.
Post by Ramalingam C
+ mutex_unlock(&comp->mutex);
+ return -EINVAL;
+ }
+
+ ret = comp->ops->verify_receiver_cert_prepare_km(comp->dev, data,
+ rx_cert, paired,
+ ek_pub_km, msg_sz);
+ if (ret < 0)
+ comp->ops->close_hdcp_session(comp->dev, data);
Error handling here seems a bit strange. You close the session, but don't
reset the port. So next op will be totally unaware that things have blown
up. Also no warning.

If we want to close the session, then I think that should be a decision
made higher up.
Post by Ramalingam C
+ mutex_unlock(&comp->mutex);
With component do we still need this mutex stuff here?

Exact same comments everywhere below.
Post by Ramalingam C
+
+ return ret;
+}
+
+static __attribute__((unused)) int
+hdcp2_verify_hprime(struct intel_connector *connector,
+ struct hdcp2_ake_send_hprime *rx_hprime)
+{
+ struct mei_hdcp_data *data = &connector->hdcp.mei_data;
+ struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
+ struct i915_hdcp_component_master *comp = dev_priv->hdcp_comp;
+ int ret;
+
+ if (!comp)
+ return -EINVAL;
+
+ mutex_lock(&comp->mutex);
+ if (!comp->ops || !comp->dev || data->port == MEI_DDI_INVALID_PORT) {
+ mutex_unlock(&comp->mutex);
+ return -EINVAL;
+ }
+
+ ret = comp->ops->verify_hprime(comp->dev, data, rx_hprime);
+ if (ret < 0)
+ comp->ops->close_hdcp_session(comp->dev, data);
+ mutex_unlock(&comp->mutex);
+
+ return ret;
+}
+
+static __attribute__((unused)) int
+hdcp2_store_pairing_info(struct intel_connector *connector,
+ struct hdcp2_ake_send_pairing_info *pairing_info)
+{
+ struct mei_hdcp_data *data = &connector->hdcp.mei_data;
+ struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
+ struct i915_hdcp_component_master *comp = dev_priv->hdcp_comp;
+ int ret;
+
+ if (!comp)
+ return -EINVAL;
+
+ mutex_lock(&comp->mutex);
+ if (!comp->ops || !comp->dev || data->port == MEI_DDI_INVALID_PORT) {
+ mutex_unlock(&comp->mutex);
+ return -EINVAL;
+ }
+
+ ret = comp->ops->store_pairing_info(comp->dev,
+ data, pairing_info);
+ if (ret < 0)
+ comp->ops->close_hdcp_session(comp->dev, data);
+ mutex_unlock(&comp->mutex);
+
+ return ret;
+}
+
+static __attribute__((unused)) int
+hdcp2_prepare_lc_init(struct intel_connector *connector,
+ struct hdcp2_lc_init *lc_init)
+{
+ struct mei_hdcp_data *data = &connector->hdcp.mei_data;
+ struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
+ struct i915_hdcp_component_master *comp = dev_priv->hdcp_comp;
+ int ret;
+
+ if (!comp)
+ return -EINVAL;
+
+ mutex_lock(&comp->mutex);
+ if (!comp->ops || !comp->dev || data->port == MEI_DDI_INVALID_PORT) {
+ mutex_unlock(&comp->mutex);
+ return -EINVAL;
+ }
+
+ ret = comp->ops->initiate_locality_check(comp->dev,
+ data, lc_init);
+ if (ret < 0)
+ comp->ops->close_hdcp_session(comp->dev, data);
+ mutex_unlock(&comp->mutex);
+
+ return ret;
+}
+
+static __attribute__((unused)) int
+hdcp2_verify_lprime(struct intel_connector *connector,
+ struct hdcp2_lc_send_lprime *rx_lprime)
+{
+ struct mei_hdcp_data *data = &connector->hdcp.mei_data;
+ struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
+ struct i915_hdcp_component_master *comp = dev_priv->hdcp_comp;
+ int ret;
+
+ if (!comp)
+ return -EINVAL;
+
+ mutex_lock(&comp->mutex);
+ if (!comp->ops || !comp->dev || data->port == MEI_DDI_INVALID_PORT) {
+ mutex_unlock(&comp->mutex);
+ return -EINVAL;
+ }
+
+ ret = comp->ops->verify_lprime(comp->dev, data, rx_lprime);
+ if (ret < 0)
+ comp->ops->close_hdcp_session(comp->dev, data);
+ mutex_unlock(&comp->mutex);
+
+ return ret;
+}
+
+static __attribute__((unused))
+int hdcp2_prepare_skey(struct intel_connector *connector,
+ struct hdcp2_ske_send_eks *ske_data)
+{
+ struct mei_hdcp_data *data = &connector->hdcp.mei_data;
+ struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
+ struct i915_hdcp_component_master *comp = dev_priv->hdcp_comp;
+ int ret;
+
+ if (!comp)
+ return -EINVAL;
+
+ mutex_lock(&comp->mutex);
+ if (!comp->ops || !comp->dev || data->port == MEI_DDI_INVALID_PORT) {
+ mutex_unlock(&comp->mutex);
+ return -EINVAL;
+ }
+
+ ret = comp->ops->get_session_key(comp->dev, data, ske_data);
+ if (ret < 0)
+ comp->ops->close_hdcp_session(comp->dev, data);
+ mutex_unlock(&comp->mutex);
+
+ return ret;
+}
+
+static __attribute__((unused)) int
+hdcp2_verify_rep_topology_prepare_ack(struct intel_connector *connector,
+ struct hdcp2_rep_send_receiverid_list
+ *rep_topology,
+ struct hdcp2_rep_send_ack *rep_send_ack)
+{
+ struct mei_hdcp_data *data = &connector->hdcp.mei_data;
+ struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
+ struct i915_hdcp_component_master *comp = dev_priv->hdcp_comp;
+ int ret;
+
+ if (!comp)
+ return -EINVAL;
+
+ mutex_lock(&comp->mutex);
+ if (!comp->ops || !comp->dev || data->port == MEI_DDI_INVALID_PORT) {
+ mutex_unlock(&comp->mutex);
+ return -EINVAL;
+ }
+
+ ret = comp->ops->repeater_check_flow_prepare_ack(comp->dev,
+ data, rep_topology,
+ rep_send_ack);
+ if (ret < 0)
+ comp->ops->close_hdcp_session(comp->dev, data);
+ mutex_unlock(&comp->mutex);
+
+ return ret;
+}
+
+static __attribute__((unused)) int
+hdcp2_verify_mprime(struct intel_connector *connector,
+ struct hdcp2_rep_stream_ready *stream_ready)
+{
+ struct mei_hdcp_data *data = &connector->hdcp.mei_data;
+ struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
+ struct i915_hdcp_component_master *comp = dev_priv->hdcp_comp;
+ int ret;
+
+ if (!comp)
+ return -EINVAL;
+
+ mutex_lock(&comp->mutex);
+ if (!comp->ops || !comp->dev || data->port == MEI_DDI_INVALID_PORT) {
+ mutex_unlock(&comp->mutex);
+ return -EINVAL;
+ }
+
+ ret = comp->ops->verify_mprime(comp->dev, data, stream_ready);
+ if (ret < 0)
+ comp->ops->close_hdcp_session(comp->dev, data);
+ mutex_unlock(&comp->mutex);
+
+ return ret;
+}
+
+static __attribute__((unused))
+int hdcp2_authenticate_port(struct intel_connector *connector)
+{
+ struct mei_hdcp_data *data = &connector->hdcp.mei_data;
+ struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
+ struct i915_hdcp_component_master *comp = dev_priv->hdcp_comp;
+ int ret;
+
+ if (!comp)
+ return -EINVAL;
+
+ mutex_lock(&comp->mutex);
+ if (!comp->ops || !comp->dev || data->port == MEI_DDI_INVALID_PORT) {
+ mutex_unlock(&comp->mutex);
+ return -EINVAL;
+ }
+
+ ret = comp->ops->enable_hdcp_authentication(comp->dev, data);
+ if (ret < 0)
+ comp->ops->close_hdcp_session(comp->dev, data);
+ mutex_unlock(&comp->mutex);
+
+ return ret;
+}
+
+static __attribute__((unused))
+int hdcp2_close_mei_session(struct intel_connector *connector)
+{
+ struct mei_hdcp_data *data = &connector->hdcp.mei_data;
+ struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
+ struct i915_hdcp_component_master *comp = dev_priv->hdcp_comp;
+ int ret;
+
+ if (!comp)
+ return -EINVAL;
+
+ mutex_lock(&comp->mutex);
+ if (!comp->ops || !comp->dev ||
+ data->port == MEI_DDI_INVALID_PORT) {
+ mutex_unlock(&comp->mutex);
+ return -EINVAL;
+ }
+
+ ret = comp->ops->close_hdcp_session(comp->dev, data);
Need to reset the port here I think.
Post by Ramalingam C
+ mutex_unlock(&comp->mutex);
+ return ret;
+}
+
+static __attribute__((unused))
+int hdcp2_deauthenticate_port(struct intel_connector *connector)
+{
+ return hdcp2_close_mei_session(connector);
+}
+
+static int i915_hdcp_component_master_bind(struct device *dev)
+{
+ struct drm_i915_private *dev_priv = kdev_to_i915(dev);
+
+ return component_bind_all(dev, dev_priv->hdcp_comp);
+}
+
+static void intel_connectors_hdcp_disable(struct drm_i915_private *dev_priv)
+{
+ struct drm_device *dev = &dev_priv->drm;
+ struct intel_connector *intel_connector;
+ struct drm_connector *connector;
+ struct drm_connector_list_iter conn_iter;
+
+ drm_connector_list_iter_begin(dev, &conn_iter);
+ drm_for_each_connector_iter(connector, &conn_iter) {
+ intel_connector = to_intel_connector(connector);
+ if (!(intel_connector->hdcp.shim))
+ continue;
+
+ intel_hdcp_disable(intel_connector);
+ }
+ drm_connector_list_iter_end(&conn_iter);
+}
+
+static void i915_hdcp_component_master_unbind(struct device *dev)
+{
+ struct drm_i915_private *dev_priv = kdev_to_i915(dev);
+
+ intel_connectors_hdcp_disable(dev_priv);
Why this code? Once we've unregistered the driver, we should have shut
down the hardware completely. Which should have shut down all the hdcp
users too.
Post by Ramalingam C
+ component_unbind_all(dev, dev_priv->hdcp_comp);
+}
+
+static const struct component_master_ops i915_hdcp_component_master_ops = {
+ .bind = i915_hdcp_component_master_bind,
+ .unbind = i915_hdcp_component_master_unbind,
+};
+
+static int i915_hdcp_component_match(struct device *dev, void *data)
+{
+ return !strcmp(dev->driver->name, "mei_hdcp");
+}
+
+static int intel_hdcp_component_init(struct intel_connector *connector)
+{
+ struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
+ struct i915_hdcp_component_master *comp;
+ struct component_match *match = NULL;
+ int ret;
+
+ comp = kzalloc(sizeof(*comp), GFP_KERNEL);
+ if (!comp)
+ return -ENOMEM;
+
+ mutex_init(&comp->mutex);
+ dev_priv->hdcp_comp = comp;
+
+ component_match_add(dev_priv->drm.dev, &match,
+ i915_hdcp_component_match, dev_priv);
+ ret = component_master_add_with_match(dev_priv->drm.dev,
+ &i915_hdcp_component_master_ops,
+ match);
So I'm not sure this will work out well, hiding the master_ops here in
hdcp. Definitely needs to be rewritten as soon as i915 needs another
component. And might make the load/unload split complicated.
Post by Ramalingam C
+ if (ret < 0)
+ goto out_err;
+
+ DRM_INFO("I915 hdcp component master added.\n");
You add both the master and the hdcp component here. Output is a bit
confusing.
Post by Ramalingam C
+ return ret;
+
+ component_master_del(dev_priv->drm.dev,
+ &i915_hdcp_component_master_ops);
+ kfree(comp);
+ dev_priv->hdcp_comp = NULL;
+ DRM_ERROR("Failed to add i915 hdcp component master (%d)\n", ret);
+
+ return ret;
+}
+
+static int initialize_mei_hdcp_data(struct intel_connector *connector)
+{
+ struct intel_hdcp *hdcp = &connector->hdcp;
+ struct mei_hdcp_data *data = &hdcp->mei_data;
+ enum port port;
+
+ if (connector->encoder) {
+ port = connector->encoder->port;
+ data->port = GET_MEI_DDI_INDEX(port);
+ }
+
+ data->port_type = MEI_HDCP_PORT_TYPE_INTEGRATED;
+ data->protocol = hdcp->shim->hdcp_protocol();
+
+ data->k = 1;
+ if (!data->streams)
+ data->streams = kcalloc(data->k, sizeof(data->streams[0]),
+ GFP_KERNEL);
+ if (!data->streams) {
+ DRM_ERROR("Out of Memory\n");
+ return -ENOMEM;
+ }
+
+ data->streams[0].stream_id = 0;
+ data->streams[0].stream_type = hdcp->content_type;
+
+ return 0;
+}
+
bool is_hdcp2_supported(struct drm_i915_private *dev_priv)
{
return ((INTEL_GEN(dev_priv) >= 10 || IS_GEMINILAKE(dev_priv) ||
@@ -843,10 +1260,25 @@ static void intel_hdcp2_init(struct intel_connector *connector)
{
struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
struct intel_hdcp *hdcp = &connector->hdcp;
+ int ret;
WARN_ON(!is_hdcp2_supported(dev_priv));
- /* TODO: MEI interface needs to be initialized here */
+ ret = initialize_mei_hdcp_data(connector);
+ if (ret) {
+ DRM_DEBUG_KMS("Mei hdcp data init failed\n");
+ return;
+ }
+
+ if (!dev_priv->hdcp_comp)
+ ret = intel_hdcp_component_init(connector);
+
+ if (ret) {
+ DRM_DEBUG_KMS("HDCP comp init failed\n");
+ kfree(hdcp->mei_data.streams);
+ return;
+ }
+
hdcp->hdcp2_supported = 1;
}
@@ -894,9 +1326,17 @@ void intel_hdcp_exit(struct drm_i915_private *dev_priv)
mutex_lock(&intel_connector->hdcp.mutex);
intel_connector->hdcp.hdcp2_supported = 0;
intel_connector->hdcp.shim = NULL;
+ kfree(intel_connector->hdcp.mei_data.streams);
We need to push this into a per-connector hdcp cleanup function. Or just
into the generic connector cleanup.
Post by Ramalingam C
mutex_unlock(&intel_connector->hdcp.mutex);
}
drm_connector_list_iter_end(&conn_iter);
+
+ if (dev_priv->hdcp_comp) {
+ component_master_del(dev_priv->drm.dev,
+ &i915_hdcp_component_master_ops);
+ kfree(dev_priv->hdcp_comp);
+ dev_priv->hdcp_comp = NULL;
+ }
}
int intel_hdcp_enable(struct intel_connector *connector)
diff --git a/include/drm/i915_component.h b/include/drm/i915_component.h
index fca22d463e1b..12268228f4dc 100644
--- a/include/drm/i915_component.h
+++ b/include/drm/i915_component.h
@@ -24,8 +24,12 @@
#ifndef _I915_COMPONENT_H_
#define _I915_COMPONENT_H_
+#include <linux/mutex.h>
+
#include "drm_audio_component.h"
+#include <drm/drm_hdcp.h>
+
/* MAX_PORT is the number of port
* It must be sync with I915_MAX_PORTS defined i915_drv.h
*/
@@ -46,4 +50,71 @@ struct i915_audio_component {
int aud_sample_rate[MAX_PORTS];
};
+struct i915_hdcp_component_ops {
Imo that should be called mei_hdcp_component_ops and put into the
linux/mei_hdcp.h header. Or was that Thomas' review comment?

Aside: Review here in public channels instead of in private would be much
better for coordination.
Post by Ramalingam C
+ /**
+ */
+ struct module *owner;
+
+ int (*initiate_hdcp2_session)(struct device *dev,
+ void *hdcp_data,
+ struct hdcp2_ake_init *ake_data);
+ int (*verify_receiver_cert_prepare_km)(struct device *dev,
+ void *hdcp_data,
+ struct hdcp2_ake_send_cert
+ *rx_cert,
+ bool *km_stored,
+ struct hdcp2_ake_no_stored_km
+ *ek_pub_km,
+ size_t *msg_sz);
+ int (*verify_hprime)(struct device *dev,
+ void *hdcp_data,
+ struct hdcp2_ake_send_hprime *rx_hprime);
+ int (*store_pairing_info)(struct device *dev,
+ void *hdcp_data,
+ struct hdcp2_ake_send_pairing_info
+ *pairing_info);
+ int (*initiate_locality_check)(struct device *dev,
+ void *hdcp_data,
+ struct hdcp2_lc_init *lc_init_data);
+ int (*verify_lprime)(struct device *dev,
+ void *hdcp_data,
+ struct hdcp2_lc_send_lprime *rx_lprime);
+ int (*get_session_key)(struct device *dev,
+ void *hdcp_data,
+ struct hdcp2_ske_send_eks *ske_data);
+ int (*repeater_check_flow_prepare_ack)(struct device *dev,
+ void *hdcp_data,
+ struct hdcp2_rep_send_receiverid_list
+ *rep_topology,
+ struct hdcp2_rep_send_ack
+ *rep_send_ack);
+ int (*verify_mprime)(struct device *dev,
+ void *hdcp_data,
+ struct hdcp2_rep_stream_ready *stream_ready);
+ int (*enable_hdcp_authentication)(struct device *dev,
+ void *hdcp_data);
+ int (*close_hdcp_session)(struct device *dev,
+ void *hdcp_data);
+};
+
+/**
+ * struct i915_hdcp_component_master - Used for communication between i915
+ * and mei_hdcp for HDCP2.2 services.
+ */
+struct i915_hdcp_component_master {
+ /**
+ */
+ struct device *dev;
+ /**
+ */
+ struct mutex mutex;
+ /**
+ */
+ const struct i915_hdcp_component_ops *ops;
+};
+
#endif /* _I915_COMPONENT_H_ */
--
2.7.4
--
Daniel Vetter
Software Engineer, Intel Corporation
http://blog.ffwll.ch
C, Ramalingam
2018-12-07 05:52:44 UTC
Permalink
Post by Daniel Vetter
Post by Ramalingam C
Defining the mei-i915 interface functions and initialization of
the interface.
---
drivers/gpu/drm/i915/i915_drv.h | 2 +
drivers/gpu/drm/i915/intel_drv.h | 7 +
drivers/gpu/drm/i915/intel_hdcp.c | 442 +++++++++++++++++++++++++++++++++++++-
include/drm/i915_component.h | 71 ++++++
4 files changed, 521 insertions(+), 1 deletion(-)
diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h
index f763b30f98d9..b68bc980b7cd 100644
--- a/drivers/gpu/drm/i915/i915_drv.h
+++ b/drivers/gpu/drm/i915/i915_drv.h
@@ -2015,6 +2015,8 @@ struct drm_i915_private {
struct i915_pmu pmu;
+ struct i915_hdcp_component_master *hdcp_comp;
+
/*
* NOTE: This is the dri1/ums dungeon, don't add stuff here. Your patch
* will be rejected. Instead look for a better place.
diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h
index 85a526598096..bde82f3ada85 100644
--- a/drivers/gpu/drm/i915/intel_drv.h
+++ b/drivers/gpu/drm/i915/intel_drv.h
@@ -29,6 +29,7 @@
#include <linux/i2c.h>
#include <linux/hdmi.h>
#include <linux/sched/clock.h>
+#include <linux/mei_hdcp.h>
#include <drm/i915_drm.h>
#include "i915_drv.h"
#include <drm/drm_crtc.h>
@@ -379,6 +380,9 @@ struct intel_hdcp_shim {
/* Detects panel's hdcp capability. This is optional for HDMI. */
int (*hdcp_capable)(struct intel_digital_port *intel_dig_port,
bool *hdcp_capable);
+
+ /* Detects the HDCP protocol(DP/HDMI) required on the port */
+ enum mei_hdcp_wired_protocol (*hdcp_protocol)(void);
Looking ahead, this seems hardwired to constant return value? Or why do we
need a function here?
This is hardwired based on the connector type(DP/HDMI).
Since we have the shim for hdcp's connector based work, I have added this function.

Could have done this just with connector_type check, but in that way whole hdcp_shim
can be done in that way. So going with the larger design here.
Post by Daniel Vetter
Post by Ramalingam C
};
struct intel_hdcp {
@@ -399,6 +403,9 @@ struct intel_hdcp {
* content can flow only through a link protected by HDCP2.2.
*/
u8 content_type;
+
+ /* mei interface related information */
+ struct mei_hdcp_data mei_data;
};
struct intel_connector {
diff --git a/drivers/gpu/drm/i915/intel_hdcp.c b/drivers/gpu/drm/i915/intel_hdcp.c
index 99dddb540958..760780f1105c 100644
--- a/drivers/gpu/drm/i915/intel_hdcp.c
+++ b/drivers/gpu/drm/i915/intel_hdcp.c
@@ -8,14 +8,20 @@
#include <drm/drmP.h>
#include <drm/drm_hdcp.h>
+#include <drm/i915_component.h>
#include <linux/i2c.h>
#include <linux/random.h>
+#include <linux/component.h>
#include "intel_drv.h"
#include "i915_reg.h"
#define KEY_LOAD_TRIES 5
#define TIME_FOR_ENCRYPT_STATUS_CHANGE 50
+#define GET_MEI_DDI_INDEX(p) ({ \
+ typeof(p) __p = (p); \
+ __p == PORT_A ? MEI_DDI_A : (enum mei_hdcp_ddi)__p;\
+})
static
bool intel_hdcp_is_ksv_valid(u8 *ksv)
@@ -833,6 +839,417 @@ bool is_hdcp_supported(struct drm_i915_private *dev_priv, enum port port)
!IS_CHERRYVIEW(dev_priv) && port < PORT_E);
}
+static __attribute__((unused)) int
+hdcp2_prepare_ake_init(struct intel_connector *connector,
+ struct hdcp2_ake_init *ake_data)
+{
+ struct mei_hdcp_data *data = &connector->hdcp.mei_data;
+ struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
+ struct i915_hdcp_component_master *comp = dev_priv->hdcp_comp;
+ int ret;
+
+ if (!comp)
+ return -EINVAL;
+
+ mutex_lock(&comp->mutex);
+ if (!comp->ops || !comp->dev) {
+ mutex_unlock(&comp->mutex);
+ return -EINVAL;
+ }
+
+ if (data->port == MEI_DDI_INVALID_PORT && connector->encoder)
+ data->port = GET_MEI_DDI_INDEX(connector->encoder->port);
+
+ /* Clear ME FW instance for the port, just incase */
+ comp->ops->close_hdcp_session(comp->dev, data);
Sounds like a bug somewhere if we need this? I'd put this code into the
->initiate_hdcp2_session, with a big WARN_ON if it's actually needed.
Not really. Lets say you have progressed beyond the first step of HDCP2.2 auth along with ME FW.
Now if authentication failed due to some reason, then the HDCP2.2 season created with
ME FW for that port is not closed yet.

So we need to call close_hdcp_session() explicitly on authentication failures.
Session has to be closed before restarting the auth on that port with initialite_hdcp_session.
If we are closing the hdcp session of the port on all auth errors, we dont need this just before
start of the hdcp session.
Post by Daniel Vetter
"Just in case" papering over programming bugs of our own just makes
debugging harder.
Post by Ramalingam C
+
+ ret = comp->ops->initiate_hdcp2_session(comp->dev,
+ data, ake_data);
We should set the port only after successfully initializing this.
hdcp2_session is created for each port at ME FW. Hence we need the port
initialized even before calling the initiate_hdcp2_session.
Post by Daniel Vetter
Post by Ramalingam C
+ mutex_unlock(&comp->mutex);
+
+ return ret;
+}
+
+static __attribute__((unused)) int
+hdcp2_verify_rx_cert_prepare_km(struct intel_connector *connector,
+ struct hdcp2_ake_send_cert *rx_cert,
+ bool *paired,
+ struct hdcp2_ake_no_stored_km *ek_pub_km,
+ size_t *msg_sz)
+{
+ struct mei_hdcp_data *data = &connector->hdcp.mei_data;
+ struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
+ struct i915_hdcp_component_master *comp = dev_priv->hdcp_comp;
+ int ret;
+
+ if (!comp)
+ return -EINVAL;
+
+ mutex_lock(&comp->mutex);
+ if (!comp->ops || !comp->dev || data->port == MEI_DDI_INVALID_PORT) {
These all look like programming mistakes that should result in a WARN_ON.
We are using the comp->mutex for protecting the interface during the interface
init, usage for mei communication and interface teardown.

But what if mei interface teardown happens between mei communications?
So when we try to access the mei interface after such possible tear down scenario,
we are checking the integrity of the interface.

Possible alternate solution is hold the comp->mutex across the authentication steps.
But consequence is that mei module removal will be blocked for authentication duration
and even if the mei_dev is removed, component unbind will be blocked due to this mutex dependency.
Post by Daniel Vetter
Post by Ramalingam C
+ mutex_unlock(&comp->mutex);
+ return -EINVAL;
+ }
+
+ ret = comp->ops->verify_receiver_cert_prepare_km(comp->dev, data,
+ rx_cert, paired,
+ ek_pub_km, msg_sz);
+ if (ret < 0)
+ comp->ops->close_hdcp_session(comp->dev, data);
Error handling here seems a bit strange. You close the session, but don't
reset the port. So next op will be totally unaware that things have blown
up. Also no warning.
If we want to close the session, then I think that should be a decision
made higher up.
This is needed as explained above. But as you have mentioned this can be moved
to the end of the authentication on error scenario. I will work on that.
Post by Daniel Vetter
Post by Ramalingam C
+ mutex_unlock(&comp->mutex);
With component do we still need this mutex stuff here?
Exact same comments everywhere below.
Post by Ramalingam C
+
+ return ret;
+}
+
+static __attribute__((unused)) int
+hdcp2_verify_hprime(struct intel_connector *connector,
+ struct hdcp2_ake_send_hprime *rx_hprime)
+{
+ struct mei_hdcp_data *data = &connector->hdcp.mei_data;
+ struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
+ struct i915_hdcp_component_master *comp = dev_priv->hdcp_comp;
+ int ret;
+
+ if (!comp)
+ return -EINVAL;
+
+ mutex_lock(&comp->mutex);
+ if (!comp->ops || !comp->dev || data->port == MEI_DDI_INVALID_PORT) {
+ mutex_unlock(&comp->mutex);
+ return -EINVAL;
+ }
+
+ ret = comp->ops->verify_hprime(comp->dev, data, rx_hprime);
+ if (ret < 0)
+ comp->ops->close_hdcp_session(comp->dev, data);
+ mutex_unlock(&comp->mutex);
+
+ return ret;
+}
+
+static __attribute__((unused)) int
+hdcp2_store_pairing_info(struct intel_connector *connector,
+ struct hdcp2_ake_send_pairing_info *pairing_info)
+{
+ struct mei_hdcp_data *data = &connector->hdcp.mei_data;
+ struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
+ struct i915_hdcp_component_master *comp = dev_priv->hdcp_comp;
+ int ret;
+
+ if (!comp)
+ return -EINVAL;
+
+ mutex_lock(&comp->mutex);
+ if (!comp->ops || !comp->dev || data->port == MEI_DDI_INVALID_PORT) {
+ mutex_unlock(&comp->mutex);
+ return -EINVAL;
+ }
+
+ ret = comp->ops->store_pairing_info(comp->dev,
+ data, pairing_info);
+ if (ret < 0)
+ comp->ops->close_hdcp_session(comp->dev, data);
+ mutex_unlock(&comp->mutex);
+
+ return ret;
+}
+
+static __attribute__((unused)) int
+hdcp2_prepare_lc_init(struct intel_connector *connector,
+ struct hdcp2_lc_init *lc_init)
+{
+ struct mei_hdcp_data *data = &connector->hdcp.mei_data;
+ struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
+ struct i915_hdcp_component_master *comp = dev_priv->hdcp_comp;
+ int ret;
+
+ if (!comp)
+ return -EINVAL;
+
+ mutex_lock(&comp->mutex);
+ if (!comp->ops || !comp->dev || data->port == MEI_DDI_INVALID_PORT) {
+ mutex_unlock(&comp->mutex);
+ return -EINVAL;
+ }
+
+ ret = comp->ops->initiate_locality_check(comp->dev,
+ data, lc_init);
+ if (ret < 0)
+ comp->ops->close_hdcp_session(comp->dev, data);
+ mutex_unlock(&comp->mutex);
+
+ return ret;
+}
+
+static __attribute__((unused)) int
+hdcp2_verify_lprime(struct intel_connector *connector,
+ struct hdcp2_lc_send_lprime *rx_lprime)
+{
+ struct mei_hdcp_data *data = &connector->hdcp.mei_data;
+ struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
+ struct i915_hdcp_component_master *comp = dev_priv->hdcp_comp;
+ int ret;
+
+ if (!comp)
+ return -EINVAL;
+
+ mutex_lock(&comp->mutex);
+ if (!comp->ops || !comp->dev || data->port == MEI_DDI_INVALID_PORT) {
+ mutex_unlock(&comp->mutex);
+ return -EINVAL;
+ }
+
+ ret = comp->ops->verify_lprime(comp->dev, data, rx_lprime);
+ if (ret < 0)
+ comp->ops->close_hdcp_session(comp->dev, data);
+ mutex_unlock(&comp->mutex);
+
+ return ret;
+}
+
+static __attribute__((unused))
+int hdcp2_prepare_skey(struct intel_connector *connector,
+ struct hdcp2_ske_send_eks *ske_data)
+{
+ struct mei_hdcp_data *data = &connector->hdcp.mei_data;
+ struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
+ struct i915_hdcp_component_master *comp = dev_priv->hdcp_comp;
+ int ret;
+
+ if (!comp)
+ return -EINVAL;
+
+ mutex_lock(&comp->mutex);
+ if (!comp->ops || !comp->dev || data->port == MEI_DDI_INVALID_PORT) {
+ mutex_unlock(&comp->mutex);
+ return -EINVAL;
+ }
+
+ ret = comp->ops->get_session_key(comp->dev, data, ske_data);
+ if (ret < 0)
+ comp->ops->close_hdcp_session(comp->dev, data);
+ mutex_unlock(&comp->mutex);
+
+ return ret;
+}
+
+static __attribute__((unused)) int
+hdcp2_verify_rep_topology_prepare_ack(struct intel_connector *connector,
+ struct hdcp2_rep_send_receiverid_list
+ *rep_topology,
+ struct hdcp2_rep_send_ack *rep_send_ack)
+{
+ struct mei_hdcp_data *data = &connector->hdcp.mei_data;
+ struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
+ struct i915_hdcp_component_master *comp = dev_priv->hdcp_comp;
+ int ret;
+
+ if (!comp)
+ return -EINVAL;
+
+ mutex_lock(&comp->mutex);
+ if (!comp->ops || !comp->dev || data->port == MEI_DDI_INVALID_PORT) {
+ mutex_unlock(&comp->mutex);
+ return -EINVAL;
+ }
+
+ ret = comp->ops->repeater_check_flow_prepare_ack(comp->dev,
+ data, rep_topology,
+ rep_send_ack);
+ if (ret < 0)
+ comp->ops->close_hdcp_session(comp->dev, data);
+ mutex_unlock(&comp->mutex);
+
+ return ret;
+}
+
+static __attribute__((unused)) int
+hdcp2_verify_mprime(struct intel_connector *connector,
+ struct hdcp2_rep_stream_ready *stream_ready)
+{
+ struct mei_hdcp_data *data = &connector->hdcp.mei_data;
+ struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
+ struct i915_hdcp_component_master *comp = dev_priv->hdcp_comp;
+ int ret;
+
+ if (!comp)
+ return -EINVAL;
+
+ mutex_lock(&comp->mutex);
+ if (!comp->ops || !comp->dev || data->port == MEI_DDI_INVALID_PORT) {
+ mutex_unlock(&comp->mutex);
+ return -EINVAL;
+ }
+
+ ret = comp->ops->verify_mprime(comp->dev, data, stream_ready);
+ if (ret < 0)
+ comp->ops->close_hdcp_session(comp->dev, data);
+ mutex_unlock(&comp->mutex);
+
+ return ret;
+}
+
+static __attribute__((unused))
+int hdcp2_authenticate_port(struct intel_connector *connector)
+{
+ struct mei_hdcp_data *data = &connector->hdcp.mei_data;
+ struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
+ struct i915_hdcp_component_master *comp = dev_priv->hdcp_comp;
+ int ret;
+
+ if (!comp)
+ return -EINVAL;
+
+ mutex_lock(&comp->mutex);
+ if (!comp->ops || !comp->dev || data->port == MEI_DDI_INVALID_PORT) {
+ mutex_unlock(&comp->mutex);
+ return -EINVAL;
+ }
+
+ ret = comp->ops->enable_hdcp_authentication(comp->dev, data);
+ if (ret < 0)
+ comp->ops->close_hdcp_session(comp->dev, data);
+ mutex_unlock(&comp->mutex);
+
+ return ret;
+}
+
+static __attribute__((unused))
+int hdcp2_close_mei_session(struct intel_connector *connector)
+{
+ struct mei_hdcp_data *data = &connector->hdcp.mei_data;
+ struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
+ struct i915_hdcp_component_master *comp = dev_priv->hdcp_comp;
+ int ret;
+
+ if (!comp)
+ return -EINVAL;
+
+ mutex_lock(&comp->mutex);
+ if (!comp->ops || !comp->dev ||
+ data->port == MEI_DDI_INVALID_PORT) {
+ mutex_unlock(&comp->mutex);
+ return -EINVAL;
+ }
+
+ ret = comp->ops->close_hdcp_session(comp->dev, data);
Need to reset the port here I think.
What is the reset of the port you are referring to? port is not
configured for encryption. And this is the call for marking the port as de-authenticated too.
Post by Daniel Vetter
Post by Ramalingam C
+ mutex_unlock(&comp->mutex);
+ return ret;
+}
+
+static __attribute__((unused))
+int hdcp2_deauthenticate_port(struct intel_connector *connector)
+{
+ return hdcp2_close_mei_session(connector);
+}
+
+static int i915_hdcp_component_master_bind(struct device *dev)
+{
+ struct drm_i915_private *dev_priv = kdev_to_i915(dev);
+
+ return component_bind_all(dev, dev_priv->hdcp_comp);
+}
+
+static void intel_connectors_hdcp_disable(struct drm_i915_private *dev_priv)
+{
+ struct drm_device *dev = &dev_priv->drm;
+ struct intel_connector *intel_connector;
+ struct drm_connector *connector;
+ struct drm_connector_list_iter conn_iter;
+
+ drm_connector_list_iter_begin(dev, &conn_iter);
+ drm_for_each_connector_iter(connector, &conn_iter) {
+ intel_connector = to_intel_connector(connector);
+ if (!(intel_connector->hdcp.shim))
+ continue;
+
+ intel_hdcp_disable(intel_connector);
+ }
+ drm_connector_list_iter_end(&conn_iter);
+}
+
+static void i915_hdcp_component_master_unbind(struct device *dev)
+{
+ struct drm_i915_private *dev_priv = kdev_to_i915(dev);
+
+ intel_connectors_hdcp_disable(dev_priv);
Why this code? Once we've unregistered the driver, we should have shut
down the hardware completely. Which should have shut down all the hdcp
users too.
This unbind might be triggered either due to master_del or component_del.
if its triggered from mei through component_del, then before teardown of
the i/f in component_unbind(), disable the ongoing HDCP session through
hdcp_disable() for each connectors.
Post by Daniel Vetter
Post by Ramalingam C
+ component_unbind_all(dev, dev_priv->hdcp_comp);
+}
+
+static const struct component_master_ops i915_hdcp_component_master_ops = {
+ .bind = i915_hdcp_component_master_bind,
+ .unbind = i915_hdcp_component_master_unbind,
+};
+
+static int i915_hdcp_component_match(struct device *dev, void *data)
+{
+ return !strcmp(dev->driver->name, "mei_hdcp");
+}
+
+static int intel_hdcp_component_init(struct intel_connector *connector)
+{
+ struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
+ struct i915_hdcp_component_master *comp;
+ struct component_match *match = NULL;
+ int ret;
+
+ comp = kzalloc(sizeof(*comp), GFP_KERNEL);
+ if (!comp)
+ return -ENOMEM;
+
+ mutex_init(&comp->mutex);
+ dev_priv->hdcp_comp = comp;
+
+ component_match_add(dev_priv->drm.dev, &match,
+ i915_hdcp_component_match, dev_priv);
+ ret = component_master_add_with_match(dev_priv->drm.dev,
+ &i915_hdcp_component_master_ops,
+ match);
So I'm not sure this will work out well, hiding the master_ops here in
hdcp. Definitely needs to be rewritten as soon as i915 needs another
component. And might make the load/unload split complicated.
we have already discussed this.
Post by Daniel Vetter
Post by Ramalingam C
+ if (ret < 0)
+ goto out_err;
+
+ DRM_INFO("I915 hdcp component master added.\n");
You add both the master and the hdcp component here. Output is a bit
confusing.
we have component master and a component from mei which will match to
the master.

Here we are adding the component master.
Post by Daniel Vetter
Post by Ramalingam C
+ return ret;
+
+ component_master_del(dev_priv->drm.dev,
+ &i915_hdcp_component_master_ops);
+ kfree(comp);
+ dev_priv->hdcp_comp = NULL;
+ DRM_ERROR("Failed to add i915 hdcp component master (%d)\n", ret);
+
+ return ret;
+}
+
+static int initialize_mei_hdcp_data(struct intel_connector *connector)
+{
+ struct intel_hdcp *hdcp = &connector->hdcp;
+ struct mei_hdcp_data *data = &hdcp->mei_data;
+ enum port port;
+
+ if (connector->encoder) {
+ port = connector->encoder->port;
+ data->port = GET_MEI_DDI_INDEX(port);
+ }
+
+ data->port_type = MEI_HDCP_PORT_TYPE_INTEGRATED;
+ data->protocol = hdcp->shim->hdcp_protocol();
+
+ data->k = 1;
+ if (!data->streams)
+ data->streams = kcalloc(data->k, sizeof(data->streams[0]),
+ GFP_KERNEL);
+ if (!data->streams) {
+ DRM_ERROR("Out of Memory\n");
+ return -ENOMEM;
+ }
+
+ data->streams[0].stream_id = 0;
+ data->streams[0].stream_type = hdcp->content_type;
+
+ return 0;
+}
+
bool is_hdcp2_supported(struct drm_i915_private *dev_priv)
{
return ((INTEL_GEN(dev_priv) >= 10 || IS_GEMINILAKE(dev_priv) ||
@@ -843,10 +1260,25 @@ static void intel_hdcp2_init(struct intel_connector *connector)
{
struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
struct intel_hdcp *hdcp = &connector->hdcp;
+ int ret;
WARN_ON(!is_hdcp2_supported(dev_priv));
- /* TODO: MEI interface needs to be initialized here */
+ ret = initialize_mei_hdcp_data(connector);
+ if (ret) {
+ DRM_DEBUG_KMS("Mei hdcp data init failed\n");
+ return;
+ }
+
+ if (!dev_priv->hdcp_comp)
+ ret = intel_hdcp_component_init(connector);
+
+ if (ret) {
+ DRM_DEBUG_KMS("HDCP comp init failed\n");
+ kfree(hdcp->mei_data.streams);
+ return;
+ }
+
hdcp->hdcp2_supported = 1;
}
@@ -894,9 +1326,17 @@ void intel_hdcp_exit(struct drm_i915_private *dev_priv)
mutex_lock(&intel_connector->hdcp.mutex);
intel_connector->hdcp.hdcp2_supported = 0;
intel_connector->hdcp.shim = NULL;
+ kfree(intel_connector->hdcp.mei_data.streams);
We need to push this into a per-connector hdcp cleanup function. Or just
into the generic connector cleanup.
We could move it to per connector  hdcp cleanup function.
Post by Daniel Vetter
Post by Ramalingam C
mutex_unlock(&intel_connector->hdcp.mutex);
}
drm_connector_list_iter_end(&conn_iter);
+
+ if (dev_priv->hdcp_comp) {
+ component_master_del(dev_priv->drm.dev,
+ &i915_hdcp_component_master_ops);
+ kfree(dev_priv->hdcp_comp);
+ dev_priv->hdcp_comp = NULL;
+ }
}
int intel_hdcp_enable(struct intel_connector *connector)
diff --git a/include/drm/i915_component.h b/include/drm/i915_component.h
index fca22d463e1b..12268228f4dc 100644
--- a/include/drm/i915_component.h
+++ b/include/drm/i915_component.h
@@ -24,8 +24,12 @@
#ifndef _I915_COMPONENT_H_
#define _I915_COMPONENT_H_
+#include <linux/mutex.h>
+
#include "drm_audio_component.h"
+#include <drm/drm_hdcp.h>
+
/* MAX_PORT is the number of port
* It must be sync with I915_MAX_PORTS defined i915_drv.h
*/
@@ -46,4 +50,71 @@ struct i915_audio_component {
int aud_sample_rate[MAX_PORTS];
};
+struct i915_hdcp_component_ops {
Imo that should be called mei_hdcp_component_ops and put into the
linux/mei_hdcp.h header. Or was that Thomas' review comment?
Nope this is there for many versions. i915_hdcp_component_ops are
implemented by mei_hdcp.c and initializes the component with the &ops.
Post by Daniel Vetter
Aside: Review here in public channels instead of in private would be much
better for coordination.
Tomas,

Could you please help on this ask.?

--Ram
Post by Daniel Vetter
Post by Ramalingam C
+ /**
+ */
+ struct module *owner;
+
+ int (*initiate_hdcp2_session)(struct device *dev,
+ void *hdcp_data,
+ struct hdcp2_ake_init *ake_data);
+ int (*verify_receiver_cert_prepare_km)(struct device *dev,
+ void *hdcp_data,
+ struct hdcp2_ake_send_cert
+ *rx_cert,
+ bool *km_stored,
+ struct hdcp2_ake_no_stored_km
+ *ek_pub_km,
+ size_t *msg_sz);
+ int (*verify_hprime)(struct device *dev,
+ void *hdcp_data,
+ struct hdcp2_ake_send_hprime *rx_hprime);
+ int (*store_pairing_info)(struct device *dev,
+ void *hdcp_data,
+ struct hdcp2_ake_send_pairing_info
+ *pairing_info);
+ int (*initiate_locality_check)(struct device *dev,
+ void *hdcp_data,
+ struct hdcp2_lc_init *lc_init_data);
+ int (*verify_lprime)(struct device *dev,
+ void *hdcp_data,
+ struct hdcp2_lc_send_lprime *rx_lprime);
+ int (*get_session_key)(struct device *dev,
+ void *hdcp_data,
+ struct hdcp2_ske_send_eks *ske_data);
+ int (*repeater_check_flow_prepare_ack)(struct device *dev,
+ void *hdcp_data,
+ struct hdcp2_rep_send_receiverid_list
+ *rep_topology,
+ struct hdcp2_rep_send_ack
+ *rep_send_ack);
+ int (*verify_mprime)(struct device *dev,
+ void *hdcp_data,
+ struct hdcp2_rep_stream_ready *stream_ready);
+ int (*enable_hdcp_authentication)(struct device *dev,
+ void *hdcp_data);
+ int (*close_hdcp_session)(struct device *dev,
+ void *hdcp_data);
+};
+
+/**
+ * struct i915_hdcp_component_master - Used for communication between i915
+ * and mei_hdcp for HDCP2.2 services.
+ */
+struct i915_hdcp_component_master {
+ /**
+ */
+ struct device *dev;
+ /**
+ */
+ struct mutex mutex;
+ /**
+ */
+ const struct i915_hdcp_component_ops *ops;
+};
+
#endif /* _I915_COMPONENT_H_ */
--
2.7.4
C, Ramalingam
2018-12-07 10:48:07 UTC
Permalink
Post by C, Ramalingam
Post by Daniel Vetter
Post by Ramalingam C
Defining the mei-i915 interface functions and initialization of
the interface.
---
drivers/gpu/drm/i915/i915_drv.h | 2 +
drivers/gpu/drm/i915/intel_drv.h | 7 +
drivers/gpu/drm/i915/intel_hdcp.c | 442 +++++++++++++++++++++++++++++++++++++-
include/drm/i915_component.h | 71 ++++++
4 files changed, 521 insertions(+), 1 deletion(-)
diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h
index f763b30f98d9..b68bc980b7cd 100644
--- a/drivers/gpu/drm/i915/i915_drv.h
+++ b/drivers/gpu/drm/i915/i915_drv.h
@@ -2015,6 +2015,8 @@ struct drm_i915_private {
struct i915_pmu pmu;
+ struct i915_hdcp_component_master *hdcp_comp;
+
/*
* NOTE: This is the dri1/ums dungeon, don't add stuff here. Your patch
* will be rejected. Instead look for a better place.
diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h
index 85a526598096..bde82f3ada85 100644
--- a/drivers/gpu/drm/i915/intel_drv.h
+++ b/drivers/gpu/drm/i915/intel_drv.h
@@ -29,6 +29,7 @@
#include <linux/i2c.h>
#include <linux/hdmi.h>
#include <linux/sched/clock.h>
+#include <linux/mei_hdcp.h>
#include <drm/i915_drm.h>
#include "i915_drv.h"
#include <drm/drm_crtc.h>
@@ -379,6 +380,9 @@ struct intel_hdcp_shim {
/* Detects panel's hdcp capability. This is optional for HDMI. */
int (*hdcp_capable)(struct intel_digital_port *intel_dig_port,
bool *hdcp_capable);
+
+ /* Detects the HDCP protocol(DP/HDMI) required on the port */
+ enum mei_hdcp_wired_protocol (*hdcp_protocol)(void);
Looking ahead, this seems hardwired to constant return value? Or why do we
need a function here?
This is hardwired based on the connector type(DP/HDMI).
Since we have the shim for hdcp's connector based work, I have added this function.
Could have done this just with connector_type check, but in that way whole hdcp_shim
can be done in that way. So going with the larger design here.
Post by Daniel Vetter
Post by Ramalingam C
};
struct intel_hdcp {
@@ -399,6 +403,9 @@ struct intel_hdcp {
* content can flow only through a link protected by HDCP2.2.
*/
u8 content_type;
+
+ /* mei interface related information */
+ struct mei_hdcp_data mei_data;
};
struct intel_connector {
diff --git a/drivers/gpu/drm/i915/intel_hdcp.c b/drivers/gpu/drm/i915/intel_hdcp.c
index 99dddb540958..760780f1105c 100644
--- a/drivers/gpu/drm/i915/intel_hdcp.c
+++ b/drivers/gpu/drm/i915/intel_hdcp.c
@@ -8,14 +8,20 @@
#include <drm/drmP.h>
#include <drm/drm_hdcp.h>
+#include <drm/i915_component.h>
#include <linux/i2c.h>
#include <linux/random.h>
+#include <linux/component.h>
#include "intel_drv.h"
#include "i915_reg.h"
#define KEY_LOAD_TRIES 5
#define TIME_FOR_ENCRYPT_STATUS_CHANGE 50
+#define GET_MEI_DDI_INDEX(p) ({ \
+ typeof(p) __p = (p); \
+ __p == PORT_A ? MEI_DDI_A : (enum mei_hdcp_ddi)__p;\
+})
static
bool intel_hdcp_is_ksv_valid(u8 *ksv)
@@ -833,6 +839,417 @@ bool is_hdcp_supported(struct drm_i915_private *dev_priv, enum port port)
!IS_CHERRYVIEW(dev_priv) && port < PORT_E);
}
+static __attribute__((unused)) int
+hdcp2_prepare_ake_init(struct intel_connector *connector,
+ struct hdcp2_ake_init *ake_data)
+{
+ struct mei_hdcp_data *data = &connector->hdcp.mei_data;
+ struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
+ struct i915_hdcp_component_master *comp = dev_priv->hdcp_comp;
+ int ret;
+
+ if (!comp)
+ return -EINVAL;
+
+ mutex_lock(&comp->mutex);
+ if (!comp->ops || !comp->dev) {
+ mutex_unlock(&comp->mutex);
+ return -EINVAL;
+ }
+
+ if (data->port == MEI_DDI_INVALID_PORT && connector->encoder)
+ data->port = GET_MEI_DDI_INDEX(connector->encoder->port);
+
+ /* Clear ME FW instance for the port, just incase */
+ comp->ops->close_hdcp_session(comp->dev, data);
Sounds like a bug somewhere if we need this? I'd put this code into the
->initiate_hdcp2_session, with a big WARN_ON if it's actually needed.
Not really. Lets say you have progressed beyond the first step of HDCP2.2 auth along with ME FW.
Now if authentication failed due to some reason, then the HDCP2.2 season created with
ME FW for that port is not closed yet.
So we need to call close_hdcp_session() explicitly on authentication failures.
Session has to be closed before restarting the auth on that port with initialite_hdcp_session.
If we are closing the hdcp session of the port on all auth errors, we dont need this just before
start of the hdcp session.
Post by Daniel Vetter
"Just in case" papering over programming bugs of our own just makes
debugging harder.
Post by Ramalingam C
+
+ ret = comp->ops->initiate_hdcp2_session(comp->dev,
+ data, ake_data);
We should set the port only after successfully initializing this.
hdcp2_session is created for each port at ME FW. Hence we need the port
initialized even before calling the initiate_hdcp2_session.
Post by Daniel Vetter
Post by Ramalingam C
+ mutex_unlock(&comp->mutex);
+
+ return ret;
+}
+
+static __attribute__((unused)) int
+hdcp2_verify_rx_cert_prepare_km(struct intel_connector *connector,
+ struct hdcp2_ake_send_cert *rx_cert,
+ bool *paired,
+ struct hdcp2_ake_no_stored_km *ek_pub_km,
+ size_t *msg_sz)
+{
+ struct mei_hdcp_data *data = &connector->hdcp.mei_data;
+ struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
+ struct i915_hdcp_component_master *comp = dev_priv->hdcp_comp;
+ int ret;
+
+ if (!comp)
+ return -EINVAL;
+
+ mutex_lock(&comp->mutex);
+ if (!comp->ops || !comp->dev || data->port == MEI_DDI_INVALID_PORT) {
These all look like programming mistakes that should result in a WARN_ON.
We are using the comp->mutex for protecting the interface during the interface
init, usage for mei communication and interface teardown.
But what if mei interface teardown happens between mei communications?
So when we try to access the mei interface after such possible tear down scenario,
we are checking the integrity of the interface.
Possible alternate solution is hold the comp->mutex across the authentication steps.
But consequence is that mei module removal will be blocked for authentication duration
and even if the mei_dev is removed, component unbind will be blocked due to this mutex dependency.
Post by Daniel Vetter
Post by Ramalingam C
+ mutex_unlock(&comp->mutex);
+ return -EINVAL;
+ }
+
+ ret = comp->ops->verify_receiver_cert_prepare_km(comp->dev, data,
+ rx_cert, paired,
+ ek_pub_km, msg_sz);
+ if (ret < 0)
+ comp->ops->close_hdcp_session(comp->dev, data);
Error handling here seems a bit strange. You close the session, but don't
reset the port. So next op will be totally unaware that things have blown
up. Also no warning.
If we want to close the session, then I think that should be a decision
made higher up.
This is needed as explained above. But as you have mentioned this can be moved
to the end of the authentication on error scenario. I will work on that.
Post by Daniel Vetter
Post by Ramalingam C
+ mutex_unlock(&comp->mutex);
With component do we still need this mutex stuff here?
Exact same comments everywhere below.
Post by Ramalingam C
+
+ return ret;
+}
+
+static __attribute__((unused)) int
+hdcp2_verify_hprime(struct intel_connector *connector,
+ struct hdcp2_ake_send_hprime *rx_hprime)
+{
+ struct mei_hdcp_data *data = &connector->hdcp.mei_data;
+ struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
+ struct i915_hdcp_component_master *comp = dev_priv->hdcp_comp;
+ int ret;
+
+ if (!comp)
+ return -EINVAL;
+
+ mutex_lock(&comp->mutex);
+ if (!comp->ops || !comp->dev || data->port == MEI_DDI_INVALID_PORT) {
+ mutex_unlock(&comp->mutex);
+ return -EINVAL;
+ }
+
+ ret = comp->ops->verify_hprime(comp->dev, data, rx_hprime);
+ if (ret < 0)
+ comp->ops->close_hdcp_session(comp->dev, data);
+ mutex_unlock(&comp->mutex);
+
+ return ret;
+}
+
+static __attribute__((unused)) int
+hdcp2_store_pairing_info(struct intel_connector *connector,
+ struct hdcp2_ake_send_pairing_info *pairing_info)
+{
+ struct mei_hdcp_data *data = &connector->hdcp.mei_data;
+ struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
+ struct i915_hdcp_component_master *comp = dev_priv->hdcp_comp;
+ int ret;
+
+ if (!comp)
+ return -EINVAL;
+
+ mutex_lock(&comp->mutex);
+ if (!comp->ops || !comp->dev || data->port == MEI_DDI_INVALID_PORT) {
+ mutex_unlock(&comp->mutex);
+ return -EINVAL;
+ }
+
+ ret = comp->ops->store_pairing_info(comp->dev,
+ data, pairing_info);
+ if (ret < 0)
+ comp->ops->close_hdcp_session(comp->dev, data);
+ mutex_unlock(&comp->mutex);
+
+ return ret;
+}
+
+static __attribute__((unused)) int
+hdcp2_prepare_lc_init(struct intel_connector *connector,
+ struct hdcp2_lc_init *lc_init)
+{
+ struct mei_hdcp_data *data = &connector->hdcp.mei_data;
+ struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
+ struct i915_hdcp_component_master *comp = dev_priv->hdcp_comp;
+ int ret;
+
+ if (!comp)
+ return -EINVAL;
+
+ mutex_lock(&comp->mutex);
+ if (!comp->ops || !comp->dev || data->port == MEI_DDI_INVALID_PORT) {
+ mutex_unlock(&comp->mutex);
+ return -EINVAL;
+ }
+
+ ret = comp->ops->initiate_locality_check(comp->dev,
+ data, lc_init);
+ if (ret < 0)
+ comp->ops->close_hdcp_session(comp->dev, data);
+ mutex_unlock(&comp->mutex);
+
+ return ret;
+}
+
+static __attribute__((unused)) int
+hdcp2_verify_lprime(struct intel_connector *connector,
+ struct hdcp2_lc_send_lprime *rx_lprime)
+{
+ struct mei_hdcp_data *data = &connector->hdcp.mei_data;
+ struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
+ struct i915_hdcp_component_master *comp = dev_priv->hdcp_comp;
+ int ret;
+
+ if (!comp)
+ return -EINVAL;
+
+ mutex_lock(&comp->mutex);
+ if (!comp->ops || !comp->dev || data->port == MEI_DDI_INVALID_PORT) {
+ mutex_unlock(&comp->mutex);
+ return -EINVAL;
+ }
+
+ ret = comp->ops->verify_lprime(comp->dev, data, rx_lprime);
+ if (ret < 0)
+ comp->ops->close_hdcp_session(comp->dev, data);
+ mutex_unlock(&comp->mutex);
+
+ return ret;
+}
+
+static __attribute__((unused))
+int hdcp2_prepare_skey(struct intel_connector *connector,
+ struct hdcp2_ske_send_eks *ske_data)
+{
+ struct mei_hdcp_data *data = &connector->hdcp.mei_data;
+ struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
+ struct i915_hdcp_component_master *comp = dev_priv->hdcp_comp;
+ int ret;
+
+ if (!comp)
+ return -EINVAL;
+
+ mutex_lock(&comp->mutex);
+ if (!comp->ops || !comp->dev || data->port == MEI_DDI_INVALID_PORT) {
+ mutex_unlock(&comp->mutex);
+ return -EINVAL;
+ }
+
+ ret = comp->ops->get_session_key(comp->dev, data, ske_data);
+ if (ret < 0)
+ comp->ops->close_hdcp_session(comp->dev, data);
+ mutex_unlock(&comp->mutex);
+
+ return ret;
+}
+
+static __attribute__((unused)) int
+hdcp2_verify_rep_topology_prepare_ack(struct intel_connector *connector,
+ struct hdcp2_rep_send_receiverid_list
+ *rep_topology,
+ struct hdcp2_rep_send_ack *rep_send_ack)
+{
+ struct mei_hdcp_data *data = &connector->hdcp.mei_data;
+ struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
+ struct i915_hdcp_component_master *comp = dev_priv->hdcp_comp;
+ int ret;
+
+ if (!comp)
+ return -EINVAL;
+
+ mutex_lock(&comp->mutex);
+ if (!comp->ops || !comp->dev || data->port == MEI_DDI_INVALID_PORT) {
+ mutex_unlock(&comp->mutex);
+ return -EINVAL;
+ }
+
+ ret = comp->ops->repeater_check_flow_prepare_ack(comp->dev,
+ data, rep_topology,
+ rep_send_ack);
+ if (ret < 0)
+ comp->ops->close_hdcp_session(comp->dev, data);
+ mutex_unlock(&comp->mutex);
+
+ return ret;
+}
+
+static __attribute__((unused)) int
+hdcp2_verify_mprime(struct intel_connector *connector,
+ struct hdcp2_rep_stream_ready *stream_ready)
+{
+ struct mei_hdcp_data *data = &connector->hdcp.mei_data;
+ struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
+ struct i915_hdcp_component_master *comp = dev_priv->hdcp_comp;
+ int ret;
+
+ if (!comp)
+ return -EINVAL;
+
+ mutex_lock(&comp->mutex);
+ if (!comp->ops || !comp->dev || data->port == MEI_DDI_INVALID_PORT) {
+ mutex_unlock(&comp->mutex);
+ return -EINVAL;
+ }
+
+ ret = comp->ops->verify_mprime(comp->dev, data, stream_ready);
+ if (ret < 0)
+ comp->ops->close_hdcp_session(comp->dev, data);
+ mutex_unlock(&comp->mutex);
+
+ return ret;
+}
+
+static __attribute__((unused))
+int hdcp2_authenticate_port(struct intel_connector *connector)
+{
+ struct mei_hdcp_data *data = &connector->hdcp.mei_data;
+ struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
+ struct i915_hdcp_component_master *comp = dev_priv->hdcp_comp;
+ int ret;
+
+ if (!comp)
+ return -EINVAL;
+
+ mutex_lock(&comp->mutex);
+ if (!comp->ops || !comp->dev || data->port == MEI_DDI_INVALID_PORT) {
+ mutex_unlock(&comp->mutex);
+ return -EINVAL;
+ }
+
+ ret = comp->ops->enable_hdcp_authentication(comp->dev, data);
+ if (ret < 0)
+ comp->ops->close_hdcp_session(comp->dev, data);
+ mutex_unlock(&comp->mutex);
+
+ return ret;
+}
+
+static __attribute__((unused))
+int hdcp2_close_mei_session(struct intel_connector *connector)
+{
+ struct mei_hdcp_data *data = &connector->hdcp.mei_data;
+ struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
+ struct i915_hdcp_component_master *comp = dev_priv->hdcp_comp;
+ int ret;
+
+ if (!comp)
+ return -EINVAL;
+
+ mutex_lock(&comp->mutex);
+ if (!comp->ops || !comp->dev ||
+ data->port == MEI_DDI_INVALID_PORT) {
+ mutex_unlock(&comp->mutex);
+ return -EINVAL;
+ }
+
+ ret = comp->ops->close_hdcp_session(comp->dev, data);
Need to reset the port here I think.
What is the reset of the port you are referring to? port is not
configured for encryption. And this is the call for marking the port as de-authenticated too.
Post by Daniel Vetter
Post by Ramalingam C
+ mutex_unlock(&comp->mutex);
+ return ret;
+}
+
+static __attribute__((unused))
+int hdcp2_deauthenticate_port(struct intel_connector *connector)
+{
+ return hdcp2_close_mei_session(connector);
+}
+
+static int i915_hdcp_component_master_bind(struct device *dev)
+{
+ struct drm_i915_private *dev_priv = kdev_to_i915(dev);
+
+ return component_bind_all(dev, dev_priv->hdcp_comp);
+}
+
+static void intel_connectors_hdcp_disable(struct drm_i915_private *dev_priv)
+{
+ struct drm_device *dev = &dev_priv->drm;
+ struct intel_connector *intel_connector;
+ struct drm_connector *connector;
+ struct drm_connector_list_iter conn_iter;
+
+ drm_connector_list_iter_begin(dev, &conn_iter);
+ drm_for_each_connector_iter(connector, &conn_iter) {
+ intel_connector = to_intel_connector(connector);
+ if (!(intel_connector->hdcp.shim))
+ continue;
+
+ intel_hdcp_disable(intel_connector);
+ }
+ drm_connector_list_iter_end(&conn_iter);
+}
+
+static void i915_hdcp_component_master_unbind(struct device *dev)
+{
+ struct drm_i915_private *dev_priv = kdev_to_i915(dev);
+
+ intel_connectors_hdcp_disable(dev_priv);
Why this code? Once we've unregistered the driver, we should have shut
down the hardware completely. Which should have shut down all the hdcp
users too.
This unbind might be triggered either due to master_del or component_del.
if its triggered from mei through component_del, then before teardown of
the i/f in component_unbind(), disable the ongoing HDCP session through
hdcp_disable() for each connectors.
Post by Daniel Vetter
Post by Ramalingam C
+ component_unbind_all(dev, dev_priv->hdcp_comp);
+}
+
+static const struct component_master_ops i915_hdcp_component_master_ops = {
+ .bind = i915_hdcp_component_master_bind,
+ .unbind = i915_hdcp_component_master_unbind,
+};
+
+static int i915_hdcp_component_match(struct device *dev, void *data)
+{
+ return !strcmp(dev->driver->name, "mei_hdcp");
+}
+
+static int intel_hdcp_component_init(struct intel_connector *connector)
+{
+ struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
+ struct i915_hdcp_component_master *comp;
+ struct component_match *match = NULL;
+ int ret;
+
+ comp = kzalloc(sizeof(*comp), GFP_KERNEL);
+ if (!comp)
+ return -ENOMEM;
+
+ mutex_init(&comp->mutex);
+ dev_priv->hdcp_comp = comp;
+
+ component_match_add(dev_priv->drm.dev, &match,
+ i915_hdcp_component_match, dev_priv);
+ ret = component_master_add_with_match(dev_priv->drm.dev,
+ &i915_hdcp_component_master_ops,
+ match);
So I'm not sure this will work out well, hiding the master_ops here in
hdcp. Definitely needs to be rewritten as soon as i915 needs another
component. And might make the load/unload split complicated.
we have already discussed this.
Post by Daniel Vetter
Post by Ramalingam C
+ if (ret < 0)
+ goto out_err;
+
+ DRM_INFO("I915 hdcp component master added.\n");
You add both the master and the hdcp component here. Output is a bit
confusing.
we have component master and a component from mei which will match to
the master.
Here we are adding the component master.
Post by Daniel Vetter
Post by Ramalingam C
+ return ret;
+
+ component_master_del(dev_priv->drm.dev,
+ &i915_hdcp_component_master_ops);
+ kfree(comp);
+ dev_priv->hdcp_comp = NULL;
+ DRM_ERROR("Failed to add i915 hdcp component master (%d)\n", ret);
+
+ return ret;
+}
+
+static int initialize_mei_hdcp_data(struct intel_connector *connector)
+{
+ struct intel_hdcp *hdcp = &connector->hdcp;
+ struct mei_hdcp_data *data = &hdcp->mei_data;
+ enum port port;
+
+ if (connector->encoder) {
+ port = connector->encoder->port;
+ data->port = GET_MEI_DDI_INDEX(port);
+ }
+
+ data->port_type = MEI_HDCP_PORT_TYPE_INTEGRATED;
+ data->protocol = hdcp->shim->hdcp_protocol();
+
+ data->k = 1;
+ if (!data->streams)
+ data->streams = kcalloc(data->k, sizeof(data->streams[0]),
+ GFP_KERNEL);
+ if (!data->streams) {
+ DRM_ERROR("Out of Memory\n");
+ return -ENOMEM;
+ }
+
+ data->streams[0].stream_id = 0;
+ data->streams[0].stream_type = hdcp->content_type;
+
+ return 0;
+}
+
bool is_hdcp2_supported(struct drm_i915_private *dev_priv)
{
return ((INTEL_GEN(dev_priv) >= 10 || IS_GEMINILAKE(dev_priv) ||
@@ -843,10 +1260,25 @@ static void intel_hdcp2_init(struct intel_connector *connector)
{
struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
struct intel_hdcp *hdcp = &connector->hdcp;
+ int ret;
WARN_ON(!is_hdcp2_supported(dev_priv));
- /* TODO: MEI interface needs to be initialized here */
+ ret = initialize_mei_hdcp_data(connector);
+ if (ret) {
+ DRM_DEBUG_KMS("Mei hdcp data init failed\n");
+ return;
+ }
+
+ if (!dev_priv->hdcp_comp)
+ ret = intel_hdcp_component_init(connector);
+
+ if (ret) {
+ DRM_DEBUG_KMS("HDCP comp init failed\n");
+ kfree(hdcp->mei_data.streams);
+ return;
+ }
+
hdcp->hdcp2_supported = 1;
}
@@ -894,9 +1326,17 @@ void intel_hdcp_exit(struct drm_i915_private *dev_priv)
mutex_lock(&intel_connector->hdcp.mutex);
intel_connector->hdcp.hdcp2_supported = 0;
intel_connector->hdcp.shim = NULL;
+ kfree(intel_connector->hdcp.mei_data.streams);
We need to push this into a per-connector hdcp cleanup function. Or just
into the generic connector cleanup.
We could move it to per connector  hdcp cleanup function.
Post by Daniel Vetter
Post by Ramalingam C
mutex_unlock(&intel_connector->hdcp.mutex);
}
drm_connector_list_iter_end(&conn_iter);
+
+ if (dev_priv->hdcp_comp) {
+ component_master_del(dev_priv->drm.dev,
+ &i915_hdcp_component_master_ops);
+ kfree(dev_priv->hdcp_comp);
+ dev_priv->hdcp_comp = NULL;
+ }
}
int intel_hdcp_enable(struct intel_connector *connector)
diff --git a/include/drm/i915_component.h b/include/drm/i915_component.h
index fca22d463e1b..12268228f4dc 100644
--- a/include/drm/i915_component.h
+++ b/include/drm/i915_component.h
@@ -24,8 +24,12 @@
#ifndef _I915_COMPONENT_H_
#define _I915_COMPONENT_H_
+#include <linux/mutex.h>
+
#include "drm_audio_component.h"
+#include <drm/drm_hdcp.h>
+
/* MAX_PORT is the number of port
* It must be sync with I915_MAX_PORTS defined i915_drv.h
*/
@@ -46,4 +50,71 @@ struct i915_audio_component {
int aud_sample_rate[MAX_PORTS];
};
+struct i915_hdcp_component_ops {
Imo that should be called mei_hdcp_component_ops and put into the
linux/mei_hdcp.h header. Or was that Thomas' review comment?
Nope this is there for many versions. i915_hdcp_component_ops are
implemented by mei_hdcp.c and initializes the component with the &ops.
Tomas and Daniel,

Is this fine? Defining the ops at i915_component.h and I915 and mei_hdcp
using it for their purpose?

If it has to be compulsorily defined by the service provider then we need
to move this into the include/linux/mei_hdcp.h. In that we can avoid the
global header from the mei_hdcp Tomas. Please help here to clarify the direction.

-Ram
Post by C, Ramalingam
Post by Daniel Vetter
Aside: Review here in public channels instead of in private would be much
better for coordination.
Tomas,
Could you please help on this ask.?
--Ram
Post by Daniel Vetter
Post by Ramalingam C
+ /**
+ */
+ struct module *owner;
+
+ int (*initiate_hdcp2_session)(struct device *dev,
+ void *hdcp_data,
+ struct hdcp2_ake_init *ake_data);
+ int (*verify_receiver_cert_prepare_km)(struct device *dev,
+ void *hdcp_data,
+ struct hdcp2_ake_send_cert
+ *rx_cert,
+ bool *km_stored,
+ struct hdcp2_ake_no_stored_km
+ *ek_pub_km,
+ size_t *msg_sz);
+ int (*verify_hprime)(struct device *dev,
+ void *hdcp_data,
+ struct hdcp2_ake_send_hprime *rx_hprime);
+ int (*store_pairing_info)(struct device *dev,
+ void *hdcp_data,
+ struct hdcp2_ake_send_pairing_info
+ *pairing_info);
+ int (*initiate_locality_check)(struct device *dev,
+ void *hdcp_data,
+ struct hdcp2_lc_init *lc_init_data);
+ int (*verify_lprime)(struct device *dev,
+ void *hdcp_data,
+ struct hdcp2_lc_send_lprime *rx_lprime);
+ int (*get_session_key)(struct device *dev,
+ void *hdcp_data,
+ struct hdcp2_ske_send_eks *ske_data);
+ int (*repeater_check_flow_prepare_ack)(struct device *dev,
+ void *hdcp_data,
+ struct hdcp2_rep_send_receiverid_list
+ *rep_topology,
+ struct hdcp2_rep_send_ack
+ *rep_send_ack);
+ int (*verify_mprime)(struct device *dev,
+ void *hdcp_data,
+ struct hdcp2_rep_stream_ready *stream_ready);
+ int (*enable_hdcp_authentication)(struct device *dev,
+ void *hdcp_data);
+ int (*close_hdcp_session)(struct device *dev,
+ void *hdcp_data);
+};
+
+/**
+ * struct i915_hdcp_component_master - Used for communication between i915
+ * and mei_hdcp for HDCP2.2 services.
+ */
+struct i915_hdcp_component_master {
+ /**
+ */
+ struct device *dev;
+ /**
+ */
+ struct mutex mutex;
+ /**
+ */
+ const struct i915_hdcp_component_ops *ops;
+};
+
#endif /* _I915_COMPONENT_H_ */
--
2.7.4
_______________________________________________
Intel-gfx mailing list
https://lists.freedesktop.org/mailman/listinfo/intel-gfx
C, Ramalingam
2018-12-07 10:48:25 UTC
Permalink
Post by C, Ramalingam
Post by Daniel Vetter
Post by Ramalingam C
Defining the mei-i915 interface functions and initialization of
the interface.
---
drivers/gpu/drm/i915/i915_drv.h | 2 +
drivers/gpu/drm/i915/intel_drv.h | 7 +
drivers/gpu/drm/i915/intel_hdcp.c | 442 +++++++++++++++++++++++++++++++++++++-
include/drm/i915_component.h | 71 ++++++
4 files changed, 521 insertions(+), 1 deletion(-)
diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h
index f763b30f98d9..b68bc980b7cd 100644
--- a/drivers/gpu/drm/i915/i915_drv.h
+++ b/drivers/gpu/drm/i915/i915_drv.h
@@ -2015,6 +2015,8 @@ struct drm_i915_private {
struct i915_pmu pmu;
+ struct i915_hdcp_component_master *hdcp_comp;
+
/*
* NOTE: This is the dri1/ums dungeon, don't add stuff here. Your patch
* will be rejected. Instead look for a better place.
diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h
index 85a526598096..bde82f3ada85 100644
--- a/drivers/gpu/drm/i915/intel_drv.h
+++ b/drivers/gpu/drm/i915/intel_drv.h
@@ -29,6 +29,7 @@
#include <linux/i2c.h>
#include <linux/hdmi.h>
#include <linux/sched/clock.h>
+#include <linux/mei_hdcp.h>
#include <drm/i915_drm.h>
#include "i915_drv.h"
#include <drm/drm_crtc.h>
@@ -379,6 +380,9 @@ struct intel_hdcp_shim {
/* Detects panel's hdcp capability. This is optional for HDMI. */
int (*hdcp_capable)(struct intel_digital_port *intel_dig_port,
bool *hdcp_capable);
+
+ /* Detects the HDCP protocol(DP/HDMI) required on the port */
+ enum mei_hdcp_wired_protocol (*hdcp_protocol)(void);
Looking ahead, this seems hardwired to constant return value? Or why do we
need a function here?
This is hardwired based on the connector type(DP/HDMI).
Since we have the shim for hdcp's connector based work, I have added this function.
Could have done this just with connector_type check, but in that way whole hdcp_shim
can be done in that way. So going with the larger design here.
Post by Daniel Vetter
Post by Ramalingam C
};
struct intel_hdcp {
@@ -399,6 +403,9 @@ struct intel_hdcp {
* content can flow only through a link protected by HDCP2.2.
*/
u8 content_type;
+
+ /* mei interface related information */
+ struct mei_hdcp_data mei_data;
};
struct intel_connector {
diff --git a/drivers/gpu/drm/i915/intel_hdcp.c b/drivers/gpu/drm/i915/intel_hdcp.c
index 99dddb540958..760780f1105c 100644
--- a/drivers/gpu/drm/i915/intel_hdcp.c
+++ b/drivers/gpu/drm/i915/intel_hdcp.c
@@ -8,14 +8,20 @@
#include <drm/drmP.h>
#include <drm/drm_hdcp.h>
+#include <drm/i915_component.h>
#include <linux/i2c.h>
#include <linux/random.h>
+#include <linux/component.h>
#include "intel_drv.h"
#include "i915_reg.h"
#define KEY_LOAD_TRIES 5
#define TIME_FOR_ENCRYPT_STATUS_CHANGE 50
+#define GET_MEI_DDI_INDEX(p) ({ \
+ typeof(p) __p = (p); \
+ __p == PORT_A ? MEI_DDI_A : (enum mei_hdcp_ddi)__p;\
+})
static
bool intel_hdcp_is_ksv_valid(u8 *ksv)
@@ -833,6 +839,417 @@ bool is_hdcp_supported(struct drm_i915_private *dev_priv, enum port port)
!IS_CHERRYVIEW(dev_priv) && port < PORT_E);
}
+static __attribute__((unused)) int
+hdcp2_prepare_ake_init(struct intel_connector *connector,
+ struct hdcp2_ake_init *ake_data)
+{
+ struct mei_hdcp_data *data = &connector->hdcp.mei_data;
+ struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
+ struct i915_hdcp_component_master *comp = dev_priv->hdcp_comp;
+ int ret;
+
+ if (!comp)
+ return -EINVAL;
+
+ mutex_lock(&comp->mutex);
+ if (!comp->ops || !comp->dev) {
+ mutex_unlock(&comp->mutex);
+ return -EINVAL;
+ }
+
+ if (data->port == MEI_DDI_INVALID_PORT && connector->encoder)
+ data->port = GET_MEI_DDI_INDEX(connector->encoder->port);
+
+ /* Clear ME FW instance for the port, just incase */
+ comp->ops->close_hdcp_session(comp->dev, data);
Sounds like a bug somewhere if we need this? I'd put this code into the
->initiate_hdcp2_session, with a big WARN_ON if it's actually needed.
Not really. Lets say you have progressed beyond the first step of HDCP2.2 auth along with ME FW.
Now if authentication failed due to some reason, then the HDCP2.2 season created with
ME FW for that port is not closed yet.
So we need to call close_hdcp_session() explicitly on authentication failures.
Session has to be closed before restarting the auth on that port with initialite_hdcp_session.
If we are closing the hdcp session of the port on all auth errors, we dont need this just before
start of the hdcp session.
Post by Daniel Vetter
"Just in case" papering over programming bugs of our own just makes
debugging harder.
Post by Ramalingam C
+
+ ret = comp->ops->initiate_hdcp2_session(comp->dev,
+ data, ake_data);
We should set the port only after successfully initializing this.
hdcp2_session is created for each port at ME FW. Hence we need the port
initialized even before calling the initiate_hdcp2_session.
Post by Daniel Vetter
Post by Ramalingam C
+ mutex_unlock(&comp->mutex);
+
+ return ret;
+}
+
+static __attribute__((unused)) int
+hdcp2_verify_rx_cert_prepare_km(struct intel_connector *connector,
+ struct hdcp2_ake_send_cert *rx_cert,
+ bool *paired,
+ struct hdcp2_ake_no_stored_km *ek_pub_km,
+ size_t *msg_sz)
+{
+ struct mei_hdcp_data *data = &connector->hdcp.mei_data;
+ struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
+ struct i915_hdcp_component_master *comp = dev_priv->hdcp_comp;
+ int ret;
+
+ if (!comp)
+ return -EINVAL;
+
+ mutex_lock(&comp->mutex);
+ if (!comp->ops || !comp->dev || data->port == MEI_DDI_INVALID_PORT) {
These all look like programming mistakes that should result in a WARN_ON.
We are using the comp->mutex for protecting the interface during the interface
init, usage for mei communication and interface teardown.
But what if mei interface teardown happens between mei communications?
So when we try to access the mei interface after such possible tear down scenario,
we are checking the integrity of the interface.
Possible alternate solution is hold the comp->mutex across the authentication steps.
But consequence is that mei module removal will be blocked for authentication duration
and even if the mei_dev is removed, component unbind will be blocked due to this mutex dependency.
Post by Daniel Vetter
Post by Ramalingam C
+ mutex_unlock(&comp->mutex);
+ return -EINVAL;
+ }
+
+ ret = comp->ops->verify_receiver_cert_prepare_km(comp->dev, data,
+ rx_cert, paired,
+ ek_pub_km, msg_sz);
+ if (ret < 0)
+ comp->ops->close_hdcp_session(comp->dev, data);
Error handling here seems a bit strange. You close the session, but don't
reset the port. So next op will be totally unaware that things have blown
up. Also no warning.
If we want to close the session, then I think that should be a decision
made higher up.
This is needed as explained above. But as you have mentioned this can be moved
to the end of the authentication on error scenario. I will work on that.
Post by Daniel Vetter
Post by Ramalingam C
+ mutex_unlock(&comp->mutex);
With component do we still need this mutex stuff here?
Exact same comments everywhere below.
Post by Ramalingam C
+
+ return ret;
+}
+
+static __attribute__((unused)) int
+hdcp2_verify_hprime(struct intel_connector *connector,
+ struct hdcp2_ake_send_hprime *rx_hprime)
+{
+ struct mei_hdcp_data *data = &connector->hdcp.mei_data;
+ struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
+ struct i915_hdcp_component_master *comp = dev_priv->hdcp_comp;
+ int ret;
+
+ if (!comp)
+ return -EINVAL;
+
+ mutex_lock(&comp->mutex);
+ if (!comp->ops || !comp->dev || data->port == MEI_DDI_INVALID_PORT) {
+ mutex_unlock(&comp->mutex);
+ return -EINVAL;
+ }
+
+ ret = comp->ops->verify_hprime(comp->dev, data, rx_hprime);
+ if (ret < 0)
+ comp->ops->close_hdcp_session(comp->dev, data);
+ mutex_unlock(&comp->mutex);
+
+ return ret;
+}
+
+static __attribute__((unused)) int
+hdcp2_store_pairing_info(struct intel_connector *connector,
+ struct hdcp2_ake_send_pairing_info *pairing_info)
+{
+ struct mei_hdcp_data *data = &connector->hdcp.mei_data;
+ struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
+ struct i915_hdcp_component_master *comp = dev_priv->hdcp_comp;
+ int ret;
+
+ if (!comp)
+ return -EINVAL;
+
+ mutex_lock(&comp->mutex);
+ if (!comp->ops || !comp->dev || data->port == MEI_DDI_INVALID_PORT) {
+ mutex_unlock(&comp->mutex);
+ return -EINVAL;
+ }
+
+ ret = comp->ops->store_pairing_info(comp->dev,
+ data, pairing_info);
+ if (ret < 0)
+ comp->ops->close_hdcp_session(comp->dev, data);
+ mutex_unlock(&comp->mutex);
+
+ return ret;
+}
+
+static __attribute__((unused)) int
+hdcp2_prepare_lc_init(struct intel_connector *connector,
+ struct hdcp2_lc_init *lc_init)
+{
+ struct mei_hdcp_data *data = &connector->hdcp.mei_data;
+ struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
+ struct i915_hdcp_component_master *comp = dev_priv->hdcp_comp;
+ int ret;
+
+ if (!comp)
+ return -EINVAL;
+
+ mutex_lock(&comp->mutex);
+ if (!comp->ops || !comp->dev || data->port == MEI_DDI_INVALID_PORT) {
+ mutex_unlock(&comp->mutex);
+ return -EINVAL;
+ }
+
+ ret = comp->ops->initiate_locality_check(comp->dev,
+ data, lc_init);
+ if (ret < 0)
+ comp->ops->close_hdcp_session(comp->dev, data);
+ mutex_unlock(&comp->mutex);
+
+ return ret;
+}
+
+static __attribute__((unused)) int
+hdcp2_verify_lprime(struct intel_connector *connector,
+ struct hdcp2_lc_send_lprime *rx_lprime)
+{
+ struct mei_hdcp_data *data = &connector->hdcp.mei_data;
+ struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
+ struct i915_hdcp_component_master *comp = dev_priv->hdcp_comp;
+ int ret;
+
+ if (!comp)
+ return -EINVAL;
+
+ mutex_lock(&comp->mutex);
+ if (!comp->ops || !comp->dev || data->port == MEI_DDI_INVALID_PORT) {
+ mutex_unlock(&comp->mutex);
+ return -EINVAL;
+ }
+
+ ret = comp->ops->verify_lprime(comp->dev, data, rx_lprime);
+ if (ret < 0)
+ comp->ops->close_hdcp_session(comp->dev, data);
+ mutex_unlock(&comp->mutex);
+
+ return ret;
+}
+
+static __attribute__((unused))
+int hdcp2_prepare_skey(struct intel_connector *connector,
+ struct hdcp2_ske_send_eks *ske_data)
+{
+ struct mei_hdcp_data *data = &connector->hdcp.mei_data;
+ struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
+ struct i915_hdcp_component_master *comp = dev_priv->hdcp_comp;
+ int ret;
+
+ if (!comp)
+ return -EINVAL;
+
+ mutex_lock(&comp->mutex);
+ if (!comp->ops || !comp->dev || data->port == MEI_DDI_INVALID_PORT) {
+ mutex_unlock(&comp->mutex);
+ return -EINVAL;
+ }
+
+ ret = comp->ops->get_session_key(comp->dev, data, ske_data);
+ if (ret < 0)
+ comp->ops->close_hdcp_session(comp->dev, data);
+ mutex_unlock(&comp->mutex);
+
+ return ret;
+}
+
+static __attribute__((unused)) int
+hdcp2_verify_rep_topology_prepare_ack(struct intel_connector *connector,
+ struct hdcp2_rep_send_receiverid_list
+ *rep_topology,
+ struct hdcp2_rep_send_ack *rep_send_ack)
+{
+ struct mei_hdcp_data *data = &connector->hdcp.mei_data;
+ struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
+ struct i915_hdcp_component_master *comp = dev_priv->hdcp_comp;
+ int ret;
+
+ if (!comp)
+ return -EINVAL;
+
+ mutex_lock(&comp->mutex);
+ if (!comp->ops || !comp->dev || data->port == MEI_DDI_INVALID_PORT) {
+ mutex_unlock(&comp->mutex);
+ return -EINVAL;
+ }
+
+ ret = comp->ops->repeater_check_flow_prepare_ack(comp->dev,
+ data, rep_topology,
+ rep_send_ack);
+ if (ret < 0)
+ comp->ops->close_hdcp_session(comp->dev, data);
+ mutex_unlock(&comp->mutex);
+
+ return ret;
+}
+
+static __attribute__((unused)) int
+hdcp2_verify_mprime(struct intel_connector *connector,
+ struct hdcp2_rep_stream_ready *stream_ready)
+{
+ struct mei_hdcp_data *data = &connector->hdcp.mei_data;
+ struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
+ struct i915_hdcp_component_master *comp = dev_priv->hdcp_comp;
+ int ret;
+
+ if (!comp)
+ return -EINVAL;
+
+ mutex_lock(&comp->mutex);
+ if (!comp->ops || !comp->dev || data->port == MEI_DDI_INVALID_PORT) {
+ mutex_unlock(&comp->mutex);
+ return -EINVAL;
+ }
+
+ ret = comp->ops->verify_mprime(comp->dev, data, stream_ready);
+ if (ret < 0)
+ comp->ops->close_hdcp_session(comp->dev, data);
+ mutex_unlock(&comp->mutex);
+
+ return ret;
+}
+
+static __attribute__((unused))
+int hdcp2_authenticate_port(struct intel_connector *connector)
+{
+ struct mei_hdcp_data *data = &connector->hdcp.mei_data;
+ struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
+ struct i915_hdcp_component_master *comp = dev_priv->hdcp_comp;
+ int ret;
+
+ if (!comp)
+ return -EINVAL;
+
+ mutex_lock(&comp->mutex);
+ if (!comp->ops || !comp->dev || data->port == MEI_DDI_INVALID_PORT) {
+ mutex_unlock(&comp->mutex);
+ return -EINVAL;
+ }
+
+ ret = comp->ops->enable_hdcp_authentication(comp->dev, data);
+ if (ret < 0)
+ comp->ops->close_hdcp_session(comp->dev, data);
+ mutex_unlock(&comp->mutex);
+
+ return ret;
+}
+
+static __attribute__((unused))
+int hdcp2_close_mei_session(struct intel_connector *connector)
+{
+ struct mei_hdcp_data *data = &connector->hdcp.mei_data;
+ struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
+ struct i915_hdcp_component_master *comp = dev_priv->hdcp_comp;
+ int ret;
+
+ if (!comp)
+ return -EINVAL;
+
+ mutex_lock(&comp->mutex);
+ if (!comp->ops || !comp->dev ||
+ data->port == MEI_DDI_INVALID_PORT) {
+ mutex_unlock(&comp->mutex);
+ return -EINVAL;
+ }
+
+ ret = comp->ops->close_hdcp_session(comp->dev, data);
Need to reset the port here I think.
What is the reset of the port you are referring to? port is not
configured for encryption. And this is the call for marking the port as de-authenticated too.
Post by Daniel Vetter
Post by Ramalingam C
+ mutex_unlock(&comp->mutex);
+ return ret;
+}
+
+static __attribute__((unused))
+int hdcp2_deauthenticate_port(struct intel_connector *connector)
+{
+ return hdcp2_close_mei_session(connector);
+}
+
+static int i915_hdcp_component_master_bind(struct device *dev)
+{
+ struct drm_i915_private *dev_priv = kdev_to_i915(dev);
+
+ return component_bind_all(dev, dev_priv->hdcp_comp);
+}
+
+static void intel_connectors_hdcp_disable(struct drm_i915_private *dev_priv)
+{
+ struct drm_device *dev = &dev_priv->drm;
+ struct intel_connector *intel_connector;
+ struct drm_connector *connector;
+ struct drm_connector_list_iter conn_iter;
+
+ drm_connector_list_iter_begin(dev, &conn_iter);
+ drm_for_each_connector_iter(connector, &conn_iter) {
+ intel_connector = to_intel_connector(connector);
+ if (!(intel_connector->hdcp.shim))
+ continue;
+
+ intel_hdcp_disable(intel_connector);
+ }
+ drm_connector_list_iter_end(&conn_iter);
+}
+
+static void i915_hdcp_component_master_unbind(struct device *dev)
+{
+ struct drm_i915_private *dev_priv = kdev_to_i915(dev);
+
+ intel_connectors_hdcp_disable(dev_priv);
Why this code? Once we've unregistered the driver, we should have shut
down the hardware completely. Which should have shut down all the hdcp
users too.
This unbind might be triggered either due to master_del or component_del.
if its triggered from mei through component_del, then before teardown of
the i/f in component_unbind(), disable the ongoing HDCP session through
hdcp_disable() for each connectors.
Post by Daniel Vetter
Post by Ramalingam C
+ component_unbind_all(dev, dev_priv->hdcp_comp);
+}
+
+static const struct component_master_ops i915_hdcp_component_master_ops = {
+ .bind = i915_hdcp_component_master_bind,
+ .unbind = i915_hdcp_component_master_unbind,
+};
+
+static int i915_hdcp_component_match(struct device *dev, void *data)
+{
+ return !strcmp(dev->driver->name, "mei_hdcp");
+}
+
+static int intel_hdcp_component_init(struct intel_connector *connector)
+{
+ struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
+ struct i915_hdcp_component_master *comp;
+ struct component_match *match = NULL;
+ int ret;
+
+ comp = kzalloc(sizeof(*comp), GFP_KERNEL);
+ if (!comp)
+ return -ENOMEM;
+
+ mutex_init(&comp->mutex);
+ dev_priv->hdcp_comp = comp;
+
+ component_match_add(dev_priv->drm.dev, &match,
+ i915_hdcp_component_match, dev_priv);
+ ret = component_master_add_with_match(dev_priv->drm.dev,
+ &i915_hdcp_component_master_ops,
+ match);
So I'm not sure this will work out well, hiding the master_ops here in
hdcp. Definitely needs to be rewritten as soon as i915 needs another
component. And might make the load/unload split complicated.
we have already discussed this.
Post by Daniel Vetter
Post by Ramalingam C
+ if (ret < 0)
+ goto out_err;
+
+ DRM_INFO("I915 hdcp component master added.\n");
You add both the master and the hdcp component here. Output is a bit
confusing.
we have component master and a component from mei which will match to
the master.
Here we are adding the component master.
Post by Daniel Vetter
Post by Ramalingam C
+ return ret;
+
+ component_master_del(dev_priv->drm.dev,
+ &i915_hdcp_component_master_ops);
+ kfree(comp);
+ dev_priv->hdcp_comp = NULL;
+ DRM_ERROR("Failed to add i915 hdcp component master (%d)\n", ret);
+
+ return ret;
+}
+
+static int initialize_mei_hdcp_data(struct intel_connector *connector)
+{
+ struct intel_hdcp *hdcp = &connector->hdcp;
+ struct mei_hdcp_data *data = &hdcp->mei_data;
+ enum port port;
+
+ if (connector->encoder) {
+ port = connector->encoder->port;
+ data->port = GET_MEI_DDI_INDEX(port);
+ }
+
+ data->port_type = MEI_HDCP_PORT_TYPE_INTEGRATED;
+ data->protocol = hdcp->shim->hdcp_protocol();
+
+ data->k = 1;
+ if (!data->streams)
+ data->streams = kcalloc(data->k, sizeof(data->streams[0]),
+ GFP_KERNEL);
+ if (!data->streams) {
+ DRM_ERROR("Out of Memory\n");
+ return -ENOMEM;
+ }
+
+ data->streams[0].stream_id = 0;
+ data->streams[0].stream_type = hdcp->content_type;
+
+ return 0;
+}
+
bool is_hdcp2_supported(struct drm_i915_private *dev_priv)
{
return ((INTEL_GEN(dev_priv) >= 10 || IS_GEMINILAKE(dev_priv) ||
@@ -843,10 +1260,25 @@ static void intel_hdcp2_init(struct intel_connector *connector)
{
struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
struct intel_hdcp *hdcp = &connector->hdcp;
+ int ret;
WARN_ON(!is_hdcp2_supported(dev_priv));
- /* TODO: MEI interface needs to be initialized here */
+ ret = initialize_mei_hdcp_data(connector);
+ if (ret) {
+ DRM_DEBUG_KMS("Mei hdcp data init failed\n");
+ return;
+ }
+
+ if (!dev_priv->hdcp_comp)
+ ret = intel_hdcp_component_init(connector);
+
+ if (ret) {
+ DRM_DEBUG_KMS("HDCP comp init failed\n");
+ kfree(hdcp->mei_data.streams);
+ return;
+ }
+
hdcp->hdcp2_supported = 1;
}
@@ -894,9 +1326,17 @@ void intel_hdcp_exit(struct drm_i915_private *dev_priv)
mutex_lock(&intel_connector->hdcp.mutex);
intel_connector->hdcp.hdcp2_supported = 0;
intel_connector->hdcp.shim = NULL;
+ kfree(intel_connector->hdcp.mei_data.streams);
We need to push this into a per-connector hdcp cleanup function. Or just
into the generic connector cleanup.
We could move it to per connector  hdcp cleanup function.
Post by Daniel Vetter
Post by Ramalingam C
mutex_unlock(&intel_connector->hdcp.mutex);
}
drm_connector_list_iter_end(&conn_iter);
+
+ if (dev_priv->hdcp_comp) {
+ component_master_del(dev_priv->drm.dev,
+ &i915_hdcp_component_master_ops);
+ kfree(dev_priv->hdcp_comp);
+ dev_priv->hdcp_comp = NULL;
+ }
}
int intel_hdcp_enable(struct intel_connector *connector)
diff --git a/include/drm/i915_component.h b/include/drm/i915_component.h
index fca22d463e1b..12268228f4dc 100644
--- a/include/drm/i915_component.h
+++ b/include/drm/i915_component.h
@@ -24,8 +24,12 @@
#ifndef _I915_COMPONENT_H_
#define _I915_COMPONENT_H_
+#include <linux/mutex.h>
+
#include "drm_audio_component.h"
+#include <drm/drm_hdcp.h>
+
/* MAX_PORT is the number of port
* It must be sync with I915_MAX_PORTS defined i915_drv.h
*/
@@ -46,4 +50,71 @@ struct i915_audio_component {
int aud_sample_rate[MAX_PORTS];
};
+struct i915_hdcp_component_ops {
Imo that should be called mei_hdcp_component_ops and put into the
linux/mei_hdcp.h header. Or was that Thomas' review comment?
Nope this is there for many versions. i915_hdcp_component_ops are
implemented by mei_hdcp.c and initializes the component with the &ops.
Tomas and Daniel,

Is this fine? Defining the ops at i915_component.h and I915 and mei_hdcp
using it for their purpose?

If it has to be compulsorily defined by the service provider then we need
to move this into the include/linux/mei_hdcp.h. In that we can avoid the
global header from the mei_hdcp Tomas. Please help here to clarify the direction.

-Ram
Post by C, Ramalingam
Post by Daniel Vetter
Aside: Review here in public channels instead of in private would be much
better for coordination.
Tomas,
Could you please help on this ask.?
--Ram
Post by Daniel Vetter
Post by Ramalingam C
+ /**
+ */
+ struct module *owner;
+
+ int (*initiate_hdcp2_session)(struct device *dev,
+ void *hdcp_data,
+ struct hdcp2_ake_init *ake_data);
+ int (*verify_receiver_cert_prepare_km)(struct device *dev,
+ void *hdcp_data,
+ struct hdcp2_ake_send_cert
+ *rx_cert,
+ bool *km_stored,
+ struct hdcp2_ake_no_stored_km
+ *ek_pub_km,
+ size_t *msg_sz);
+ int (*verify_hprime)(struct device *dev,
+ void *hdcp_data,
+ struct hdcp2_ake_send_hprime *rx_hprime);
+ int (*store_pairing_info)(struct device *dev,
+ void *hdcp_data,
+ struct hdcp2_ake_send_pairing_info
+ *pairing_info);
+ int (*initiate_locality_check)(struct device *dev,
+ void *hdcp_data,
+ struct hdcp2_lc_init *lc_init_data);
+ int (*verify_lprime)(struct device *dev,
+ void *hdcp_data,
+ struct hdcp2_lc_send_lprime *rx_lprime);
+ int (*get_session_key)(struct device *dev,
+ void *hdcp_data,
+ struct hdcp2_ske_send_eks *ske_data);
+ int (*repeater_check_flow_prepare_ack)(struct device *dev,
+ void *hdcp_data,
+ struct hdcp2_rep_send_receiverid_list
+ *rep_topology,
+ struct hdcp2_rep_send_ack
+ *rep_send_ack);
+ int (*verify_mprime)(struct device *dev,
+ void *hdcp_data,
+ struct hdcp2_rep_stream_ready *stream_ready);
+ int (*enable_hdcp_authentication)(struct device *dev,
+ void *hdcp_data);
+ int (*close_hdcp_session)(struct device *dev,
+ void *hdcp_data);
+};
+
+/**
+ * struct i915_hdcp_component_master - Used for communication between i915
+ * and mei_hdcp for HDCP2.2 services.
+ */
+struct i915_hdcp_component_master {
+ /**
+ */
+ struct device *dev;
+ /**
+ */
+ struct mutex mutex;
+ /**
+ */
+ const struct i915_hdcp_component_ops *ops;
+};
+
#endif /* _I915_COMPONENT_H_ */
--
2.7.4
_______________________________________________
Intel-gfx mailing list
https://lists.freedesktop.org/mailman/listinfo/intel-gfx
Daniel Vetter
2018-12-07 14:32:09 UTC
Permalink
Post by C, Ramalingam
Post by C, Ramalingam
Post by Daniel Vetter
Post by Ramalingam C
Defining the mei-i915 interface functions and initialization of
the interface.
---
drivers/gpu/drm/i915/i915_drv.h | 2 +
drivers/gpu/drm/i915/intel_drv.h | 7 +
drivers/gpu/drm/i915/intel_hdcp.c | 442 +++++++++++++++++++++++++++++++++++++-
include/drm/i915_component.h | 71 ++++++
4 files changed, 521 insertions(+), 1 deletion(-)
diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h
index f763b30f98d9..b68bc980b7cd 100644
--- a/drivers/gpu/drm/i915/i915_drv.h
+++ b/drivers/gpu/drm/i915/i915_drv.h
@@ -2015,6 +2015,8 @@ struct drm_i915_private {
struct i915_pmu pmu;
+ struct i915_hdcp_component_master *hdcp_comp;
+
/*
* NOTE: This is the dri1/ums dungeon, don't add stuff here. Your patch
* will be rejected. Instead look for a better place.
diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h
index 85a526598096..bde82f3ada85 100644
--- a/drivers/gpu/drm/i915/intel_drv.h
+++ b/drivers/gpu/drm/i915/intel_drv.h
@@ -29,6 +29,7 @@
#include <linux/i2c.h>
#include <linux/hdmi.h>
#include <linux/sched/clock.h>
+#include <linux/mei_hdcp.h>
#include <drm/i915_drm.h>
#include "i915_drv.h"
#include <drm/drm_crtc.h>
@@ -379,6 +380,9 @@ struct intel_hdcp_shim {
/* Detects panel's hdcp capability. This is optional for HDMI. */
int (*hdcp_capable)(struct intel_digital_port *intel_dig_port,
bool *hdcp_capable);
+
+ /* Detects the HDCP protocol(DP/HDMI) required on the port */
+ enum mei_hdcp_wired_protocol (*hdcp_protocol)(void);
Looking ahead, this seems hardwired to constant return value? Or why do we
need a function here?
This is hardwired based on the connector type(DP/HDMI).
Since we have the shim for hdcp's connector based work, I have added this function.
Could have done this just with connector_type check, but in that way whole hdcp_shim
can be done in that way. So going with the larger design here.
Post by Daniel Vetter
Post by Ramalingam C
};
struct intel_hdcp {
@@ -399,6 +403,9 @@ struct intel_hdcp {
* content can flow only through a link protected by HDCP2.2.
*/
u8 content_type;
+
+ /* mei interface related information */
+ struct mei_hdcp_data mei_data;
};
struct intel_connector {
diff --git a/drivers/gpu/drm/i915/intel_hdcp.c b/drivers/gpu/drm/i915/intel_hdcp.c
index 99dddb540958..760780f1105c 100644
--- a/drivers/gpu/drm/i915/intel_hdcp.c
+++ b/drivers/gpu/drm/i915/intel_hdcp.c
@@ -8,14 +8,20 @@
#include <drm/drmP.h>
#include <drm/drm_hdcp.h>
+#include <drm/i915_component.h>
#include <linux/i2c.h>
#include <linux/random.h>
+#include <linux/component.h>
#include "intel_drv.h"
#include "i915_reg.h"
#define KEY_LOAD_TRIES 5
#define TIME_FOR_ENCRYPT_STATUS_CHANGE 50
+#define GET_MEI_DDI_INDEX(p) ({ \
+ typeof(p) __p = (p); \
+ __p == PORT_A ? MEI_DDI_A : (enum mei_hdcp_ddi)__p;\
+})
static
bool intel_hdcp_is_ksv_valid(u8 *ksv)
@@ -833,6 +839,417 @@ bool is_hdcp_supported(struct drm_i915_private *dev_priv, enum port port)
!IS_CHERRYVIEW(dev_priv) && port < PORT_E);
}
+static __attribute__((unused)) int
+hdcp2_prepare_ake_init(struct intel_connector *connector,
+ struct hdcp2_ake_init *ake_data)
+{
+ struct mei_hdcp_data *data = &connector->hdcp.mei_data;
+ struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
+ struct i915_hdcp_component_master *comp = dev_priv->hdcp_comp;
+ int ret;
+
+ if (!comp)
+ return -EINVAL;
+
+ mutex_lock(&comp->mutex);
+ if (!comp->ops || !comp->dev) {
+ mutex_unlock(&comp->mutex);
+ return -EINVAL;
+ }
+
+ if (data->port == MEI_DDI_INVALID_PORT && connector->encoder)
+ data->port = GET_MEI_DDI_INDEX(connector->encoder->port);
+
+ /* Clear ME FW instance for the port, just incase */
+ comp->ops->close_hdcp_session(comp->dev, data);
Sounds like a bug somewhere if we need this? I'd put this code into the
->initiate_hdcp2_session, with a big WARN_ON if it's actually needed.
Not really. Lets say you have progressed beyond the first step of HDCP2.2 auth along with ME FW.
Now if authentication failed due to some reason, then the HDCP2.2 season created with
ME FW for that port is not closed yet.
So we need to call close_hdcp_session() explicitly on authentication failures.
Session has to be closed before restarting the auth on that port with initialite_hdcp_session.
If we are closing the hdcp session of the port on all auth errors, we dont need this just before
start of the hdcp session.
Post by Daniel Vetter
"Just in case" papering over programming bugs of our own just makes
debugging harder.
Post by Ramalingam C
+
+ ret = comp->ops->initiate_hdcp2_session(comp->dev,
+ data, ake_data);
We should set the port only after successfully initializing this.
hdcp2_session is created for each port at ME FW. Hence we need the port
initialized even before calling the initiate_hdcp2_session.
Post by Daniel Vetter
Post by Ramalingam C
+ mutex_unlock(&comp->mutex);
+
+ return ret;
+}
+
+static __attribute__((unused)) int
+hdcp2_verify_rx_cert_prepare_km(struct intel_connector *connector,
+ struct hdcp2_ake_send_cert *rx_cert,
+ bool *paired,
+ struct hdcp2_ake_no_stored_km *ek_pub_km,
+ size_t *msg_sz)
+{
+ struct mei_hdcp_data *data = &connector->hdcp.mei_data;
+ struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
+ struct i915_hdcp_component_master *comp = dev_priv->hdcp_comp;
+ int ret;
+
+ if (!comp)
+ return -EINVAL;
+
+ mutex_lock(&comp->mutex);
+ if (!comp->ops || !comp->dev || data->port == MEI_DDI_INVALID_PORT) {
These all look like programming mistakes that should result in a WARN_ON.
We are using the comp->mutex for protecting the interface during the interface
init, usage for mei communication and interface teardown.
But what if mei interface teardown happens between mei communications?
So when we try to access the mei interface after such possible tear down scenario,
we are checking the integrity of the interface.
Possible alternate solution is hold the comp->mutex across the authentication steps.
But consequence is that mei module removal will be blocked for authentication duration
and even if the mei_dev is removed, component unbind will be blocked due to this mutex dependency.
Post by Daniel Vetter
Post by Ramalingam C
+ mutex_unlock(&comp->mutex);
+ return -EINVAL;
+ }
+
+ ret = comp->ops->verify_receiver_cert_prepare_km(comp->dev, data,
+ rx_cert, paired,
+ ek_pub_km, msg_sz);
+ if (ret < 0)
+ comp->ops->close_hdcp_session(comp->dev, data);
Error handling here seems a bit strange. You close the session, but don't
reset the port. So next op will be totally unaware that things have blown
up. Also no warning.
If we want to close the session, then I think that should be a decision
made higher up.
This is needed as explained above. But as you have mentioned this can be moved
to the end of the authentication on error scenario. I will work on that.
Post by Daniel Vetter
Post by Ramalingam C
+ mutex_unlock(&comp->mutex);
With component do we still need this mutex stuff here?
Exact same comments everywhere below.
Post by Ramalingam C
+
+ return ret;
+}
+
+static __attribute__((unused)) int
+hdcp2_verify_hprime(struct intel_connector *connector,
+ struct hdcp2_ake_send_hprime *rx_hprime)
+{
+ struct mei_hdcp_data *data = &connector->hdcp.mei_data;
+ struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
+ struct i915_hdcp_component_master *comp = dev_priv->hdcp_comp;
+ int ret;
+
+ if (!comp)
+ return -EINVAL;
+
+ mutex_lock(&comp->mutex);
+ if (!comp->ops || !comp->dev || data->port == MEI_DDI_INVALID_PORT) {
+ mutex_unlock(&comp->mutex);
+ return -EINVAL;
+ }
+
+ ret = comp->ops->verify_hprime(comp->dev, data, rx_hprime);
+ if (ret < 0)
+ comp->ops->close_hdcp_session(comp->dev, data);
+ mutex_unlock(&comp->mutex);
+
+ return ret;
+}
+
+static __attribute__((unused)) int
+hdcp2_store_pairing_info(struct intel_connector *connector,
+ struct hdcp2_ake_send_pairing_info *pairing_info)
+{
+ struct mei_hdcp_data *data = &connector->hdcp.mei_data;
+ struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
+ struct i915_hdcp_component_master *comp = dev_priv->hdcp_comp;
+ int ret;
+
+ if (!comp)
+ return -EINVAL;
+
+ mutex_lock(&comp->mutex);
+ if (!comp->ops || !comp->dev || data->port == MEI_DDI_INVALID_PORT) {
+ mutex_unlock(&comp->mutex);
+ return -EINVAL;
+ }
+
+ ret = comp->ops->store_pairing_info(comp->dev,
+ data, pairing_info);
+ if (ret < 0)
+ comp->ops->close_hdcp_session(comp->dev, data);
+ mutex_unlock(&comp->mutex);
+
+ return ret;
+}
+
+static __attribute__((unused)) int
+hdcp2_prepare_lc_init(struct intel_connector *connector,
+ struct hdcp2_lc_init *lc_init)
+{
+ struct mei_hdcp_data *data = &connector->hdcp.mei_data;
+ struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
+ struct i915_hdcp_component_master *comp = dev_priv->hdcp_comp;
+ int ret;
+
+ if (!comp)
+ return -EINVAL;
+
+ mutex_lock(&comp->mutex);
+ if (!comp->ops || !comp->dev || data->port == MEI_DDI_INVALID_PORT) {
+ mutex_unlock(&comp->mutex);
+ return -EINVAL;
+ }
+
+ ret = comp->ops->initiate_locality_check(comp->dev,
+ data, lc_init);
+ if (ret < 0)
+ comp->ops->close_hdcp_session(comp->dev, data);
+ mutex_unlock(&comp->mutex);
+
+ return ret;
+}
+
+static __attribute__((unused)) int
+hdcp2_verify_lprime(struct intel_connector *connector,
+ struct hdcp2_lc_send_lprime *rx_lprime)
+{
+ struct mei_hdcp_data *data = &connector->hdcp.mei_data;
+ struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
+ struct i915_hdcp_component_master *comp = dev_priv->hdcp_comp;
+ int ret;
+
+ if (!comp)
+ return -EINVAL;
+
+ mutex_lock(&comp->mutex);
+ if (!comp->ops || !comp->dev || data->port == MEI_DDI_INVALID_PORT) {
+ mutex_unlock(&comp->mutex);
+ return -EINVAL;
+ }
+
+ ret = comp->ops->verify_lprime(comp->dev, data, rx_lprime);
+ if (ret < 0)
+ comp->ops->close_hdcp_session(comp->dev, data);
+ mutex_unlock(&comp->mutex);
+
+ return ret;
+}
+
+static __attribute__((unused))
+int hdcp2_prepare_skey(struct intel_connector *connector,
+ struct hdcp2_ske_send_eks *ske_data)
+{
+ struct mei_hdcp_data *data = &connector->hdcp.mei_data;
+ struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
+ struct i915_hdcp_component_master *comp = dev_priv->hdcp_comp;
+ int ret;
+
+ if (!comp)
+ return -EINVAL;
+
+ mutex_lock(&comp->mutex);
+ if (!comp->ops || !comp->dev || data->port == MEI_DDI_INVALID_PORT) {
+ mutex_unlock(&comp->mutex);
+ return -EINVAL;
+ }
+
+ ret = comp->ops->get_session_key(comp->dev, data, ske_data);
+ if (ret < 0)
+ comp->ops->close_hdcp_session(comp->dev, data);
+ mutex_unlock(&comp->mutex);
+
+ return ret;
+}
+
+static __attribute__((unused)) int
+hdcp2_verify_rep_topology_prepare_ack(struct intel_connector *connector,
+ struct hdcp2_rep_send_receiverid_list
+ *rep_topology,
+ struct hdcp2_rep_send_ack *rep_send_ack)
+{
+ struct mei_hdcp_data *data = &connector->hdcp.mei_data;
+ struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
+ struct i915_hdcp_component_master *comp = dev_priv->hdcp_comp;
+ int ret;
+
+ if (!comp)
+ return -EINVAL;
+
+ mutex_lock(&comp->mutex);
+ if (!comp->ops || !comp->dev || data->port == MEI_DDI_INVALID_PORT) {
+ mutex_unlock(&comp->mutex);
+ return -EINVAL;
+ }
+
+ ret = comp->ops->repeater_check_flow_prepare_ack(comp->dev,
+ data, rep_topology,
+ rep_send_ack);
+ if (ret < 0)
+ comp->ops->close_hdcp_session(comp->dev, data);
+ mutex_unlock(&comp->mutex);
+
+ return ret;
+}
+
+static __attribute__((unused)) int
+hdcp2_verify_mprime(struct intel_connector *connector,
+ struct hdcp2_rep_stream_ready *stream_ready)
+{
+ struct mei_hdcp_data *data = &connector->hdcp.mei_data;
+ struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
+ struct i915_hdcp_component_master *comp = dev_priv->hdcp_comp;
+ int ret;
+
+ if (!comp)
+ return -EINVAL;
+
+ mutex_lock(&comp->mutex);
+ if (!comp->ops || !comp->dev || data->port == MEI_DDI_INVALID_PORT) {
+ mutex_unlock(&comp->mutex);
+ return -EINVAL;
+ }
+
+ ret = comp->ops->verify_mprime(comp->dev, data, stream_ready);
+ if (ret < 0)
+ comp->ops->close_hdcp_session(comp->dev, data);
+ mutex_unlock(&comp->mutex);
+
+ return ret;
+}
+
+static __attribute__((unused))
+int hdcp2_authenticate_port(struct intel_connector *connector)
+{
+ struct mei_hdcp_data *data = &connector->hdcp.mei_data;
+ struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
+ struct i915_hdcp_component_master *comp = dev_priv->hdcp_comp;
+ int ret;
+
+ if (!comp)
+ return -EINVAL;
+
+ mutex_lock(&comp->mutex);
+ if (!comp->ops || !comp->dev || data->port == MEI_DDI_INVALID_PORT) {
+ mutex_unlock(&comp->mutex);
+ return -EINVAL;
+ }
+
+ ret = comp->ops->enable_hdcp_authentication(comp->dev, data);
+ if (ret < 0)
+ comp->ops->close_hdcp_session(comp->dev, data);
+ mutex_unlock(&comp->mutex);
+
+ return ret;
+}
+
+static __attribute__((unused))
+int hdcp2_close_mei_session(struct intel_connector *connector)
+{
+ struct mei_hdcp_data *data = &connector->hdcp.mei_data;
+ struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
+ struct i915_hdcp_component_master *comp = dev_priv->hdcp_comp;
+ int ret;
+
+ if (!comp)
+ return -EINVAL;
+
+ mutex_lock(&comp->mutex);
+ if (!comp->ops || !comp->dev ||
+ data->port == MEI_DDI_INVALID_PORT) {
+ mutex_unlock(&comp->mutex);
+ return -EINVAL;
+ }
+
+ ret = comp->ops->close_hdcp_session(comp->dev, data);
Need to reset the port here I think.
What is the reset of the port you are referring to? port is not
configured for encryption. And this is the call for marking the port as de-authenticated too.
Post by Daniel Vetter
Post by Ramalingam C
+ mutex_unlock(&comp->mutex);
+ return ret;
+}
+
+static __attribute__((unused))
+int hdcp2_deauthenticate_port(struct intel_connector *connector)
+{
+ return hdcp2_close_mei_session(connector);
+}
+
+static int i915_hdcp_component_master_bind(struct device *dev)
+{
+ struct drm_i915_private *dev_priv = kdev_to_i915(dev);
+
+ return component_bind_all(dev, dev_priv->hdcp_comp);
+}
+
+static void intel_connectors_hdcp_disable(struct drm_i915_private *dev_priv)
+{
+ struct drm_device *dev = &dev_priv->drm;
+ struct intel_connector *intel_connector;
+ struct drm_connector *connector;
+ struct drm_connector_list_iter conn_iter;
+
+ drm_connector_list_iter_begin(dev, &conn_iter);
+ drm_for_each_connector_iter(connector, &conn_iter) {
+ intel_connector = to_intel_connector(connector);
+ if (!(intel_connector->hdcp.shim))
+ continue;
+
+ intel_hdcp_disable(intel_connector);
+ }
+ drm_connector_list_iter_end(&conn_iter);
+}
+
+static void i915_hdcp_component_master_unbind(struct device *dev)
+{
+ struct drm_i915_private *dev_priv = kdev_to_i915(dev);
+
+ intel_connectors_hdcp_disable(dev_priv);
Why this code? Once we've unregistered the driver, we should have shut
down the hardware completely. Which should have shut down all the hdcp
users too.
This unbind might be triggered either due to master_del or component_del.
if its triggered from mei through component_del, then before teardown of
the i/f in component_unbind(), disable the ongoing HDCP session through
hdcp_disable() for each connectors.
Post by Daniel Vetter
Post by Ramalingam C
+ component_unbind_all(dev, dev_priv->hdcp_comp);
+}
+
+static const struct component_master_ops i915_hdcp_component_master_ops = {
+ .bind = i915_hdcp_component_master_bind,
+ .unbind = i915_hdcp_component_master_unbind,
+};
+
+static int i915_hdcp_component_match(struct device *dev, void *data)
+{
+ return !strcmp(dev->driver->name, "mei_hdcp");
+}
+
+static int intel_hdcp_component_init(struct intel_connector *connector)
+{
+ struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
+ struct i915_hdcp_component_master *comp;
+ struct component_match *match = NULL;
+ int ret;
+
+ comp = kzalloc(sizeof(*comp), GFP_KERNEL);
+ if (!comp)
+ return -ENOMEM;
+
+ mutex_init(&comp->mutex);
+ dev_priv->hdcp_comp = comp;
+
+ component_match_add(dev_priv->drm.dev, &match,
+ i915_hdcp_component_match, dev_priv);
+ ret = component_master_add_with_match(dev_priv->drm.dev,
+ &i915_hdcp_component_master_ops,
+ match);
So I'm not sure this will work out well, hiding the master_ops here in
hdcp. Definitely needs to be rewritten as soon as i915 needs another
component. And might make the load/unload split complicated.
we have already discussed this.
Post by Daniel Vetter
Post by Ramalingam C
+ if (ret < 0)
+ goto out_err;
+
+ DRM_INFO("I915 hdcp component master added.\n");
You add both the master and the hdcp component here. Output is a bit
confusing.
we have component master and a component from mei which will match to
the master.
Here we are adding the component master.
Post by Daniel Vetter
Post by Ramalingam C
+ return ret;
+
+ component_master_del(dev_priv->drm.dev,
+ &i915_hdcp_component_master_ops);
+ kfree(comp);
+ dev_priv->hdcp_comp = NULL;
+ DRM_ERROR("Failed to add i915 hdcp component master (%d)\n", ret);
+
+ return ret;
+}
+
+static int initialize_mei_hdcp_data(struct intel_connector *connector)
+{
+ struct intel_hdcp *hdcp = &connector->hdcp;
+ struct mei_hdcp_data *data = &hdcp->mei_data;
+ enum port port;
+
+ if (connector->encoder) {
+ port = connector->encoder->port;
+ data->port = GET_MEI_DDI_INDEX(port);
+ }
+
+ data->port_type = MEI_HDCP_PORT_TYPE_INTEGRATED;
+ data->protocol = hdcp->shim->hdcp_protocol();
+
+ data->k = 1;
+ if (!data->streams)
+ data->streams = kcalloc(data->k, sizeof(data->streams[0]),
+ GFP_KERNEL);
+ if (!data->streams) {
+ DRM_ERROR("Out of Memory\n");
+ return -ENOMEM;
+ }
+
+ data->streams[0].stream_id = 0;
+ data->streams[0].stream_type = hdcp->content_type;
+
+ return 0;
+}
+
bool is_hdcp2_supported(struct drm_i915_private *dev_priv)
{
return ((INTEL_GEN(dev_priv) >= 10 || IS_GEMINILAKE(dev_priv) ||
@@ -843,10 +1260,25 @@ static void intel_hdcp2_init(struct intel_connector *connector)
{
struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
struct intel_hdcp *hdcp = &connector->hdcp;
+ int ret;
WARN_ON(!is_hdcp2_supported(dev_priv));
- /* TODO: MEI interface needs to be initialized here */
+ ret = initialize_mei_hdcp_data(connector);
+ if (ret) {
+ DRM_DEBUG_KMS("Mei hdcp data init failed\n");
+ return;
+ }
+
+ if (!dev_priv->hdcp_comp)
+ ret = intel_hdcp_component_init(connector);
+
+ if (ret) {
+ DRM_DEBUG_KMS("HDCP comp init failed\n");
+ kfree(hdcp->mei_data.streams);
+ return;
+ }
+
hdcp->hdcp2_supported = 1;
}
@@ -894,9 +1326,17 @@ void intel_hdcp_exit(struct drm_i915_private *dev_priv)
mutex_lock(&intel_connector->hdcp.mutex);
intel_connector->hdcp.hdcp2_supported = 0;
intel_connector->hdcp.shim = NULL;
+ kfree(intel_connector->hdcp.mei_data.streams);
We need to push this into a per-connector hdcp cleanup function. Or just
into the generic connector cleanup.
We could move it to per connector  hdcp cleanup function.
Post by Daniel Vetter
Post by Ramalingam C
mutex_unlock(&intel_connector->hdcp.mutex);
}
drm_connector_list_iter_end(&conn_iter);
+
+ if (dev_priv->hdcp_comp) {
+ component_master_del(dev_priv->drm.dev,
+ &i915_hdcp_component_master_ops);
+ kfree(dev_priv->hdcp_comp);
+ dev_priv->hdcp_comp = NULL;
+ }
}
int intel_hdcp_enable(struct intel_connector *connector)
diff --git a/include/drm/i915_component.h b/include/drm/i915_component.h
index fca22d463e1b..12268228f4dc 100644
--- a/include/drm/i915_component.h
+++ b/include/drm/i915_component.h
@@ -24,8 +24,12 @@
#ifndef _I915_COMPONENT_H_
#define _I915_COMPONENT_H_
+#include <linux/mutex.h>
+
#include "drm_audio_component.h"
+#include <drm/drm_hdcp.h>
+
/* MAX_PORT is the number of port
* It must be sync with I915_MAX_PORTS defined i915_drv.h
*/
@@ -46,4 +50,71 @@ struct i915_audio_component {
int aud_sample_rate[MAX_PORTS];
};
+struct i915_hdcp_component_ops {
Imo that should be called mei_hdcp_component_ops and put into the
linux/mei_hdcp.h header. Or was that Thomas' review comment?
Nope this is there for many versions. i915_hdcp_component_ops are
implemented by mei_hdcp.c and initializes the component with the &ops.
Tomas and Daniel,
Is this fine? Defining the ops at i915_component.h and I915 and mei_hdcp
using it for their purpose?
If it has to be compulsorily defined by the service provider then we need
to move this into the include/linux/mei_hdcp.h. In that we can avoid the
global header from the mei_hdcp Tomas. Please help here to clarify the direction.
Let's move this discussion to the other reply chain, to avoid splitting
discussion up too much.
-Daniel
Post by C, Ramalingam
-Ram
Post by C, Ramalingam
Post by Daniel Vetter
Aside: Review here in public channels instead of in private would be much
better for coordination.
Tomas,
Could you please help on this ask.?
--Ram
Post by Daniel Vetter
Post by Ramalingam C
+ /**
+ */
+ struct module *owner;
+
+ int (*initiate_hdcp2_session)(struct device *dev,
+ void *hdcp_data,
+ struct hdcp2_ake_init *ake_data);
+ int (*verify_receiver_cert_prepare_km)(struct device *dev,
+ void *hdcp_data,
+ struct hdcp2_ake_send_cert
+ *rx_cert,
+ bool *km_stored,
+ struct hdcp2_ake_no_stored_km
+ *ek_pub_km,
+ size_t *msg_sz);
+ int (*verify_hprime)(struct device *dev,
+ void *hdcp_data,
+ struct hdcp2_ake_send_hprime *rx_hprime);
+ int (*store_pairing_info)(struct device *dev,
+ void *hdcp_data,
+ struct hdcp2_ake_send_pairing_info
+ *pairing_info);
+ int (*initiate_locality_check)(struct device *dev,
+ void *hdcp_data,
+ struct hdcp2_lc_init *lc_init_data);
+ int (*verify_lprime)(struct device *dev,
+ void *hdcp_data,
+ struct hdcp2_lc_send_lprime *rx_lprime);
+ int (*get_session_key)(struct device *dev,
+ void *hdcp_data,
+ struct hdcp2_ske_send_eks *ske_data);
+ int (*repeater_check_flow_prepare_ack)(struct device *dev,
+ void *hdcp_data,
+ struct hdcp2_rep_send_receiverid_list
+ *rep_topology,
+ struct hdcp2_rep_send_ack
+ *rep_send_ack);
+ int (*verify_mprime)(struct device *dev,
+ void *hdcp_data,
+ struct hdcp2_rep_stream_ready *stream_ready);
+ int (*enable_hdcp_authentication)(struct device *dev,
+ void *hdcp_data);
+ int (*close_hdcp_session)(struct device *dev,
+ void *hdcp_data);
+};
+
+/**
+ * struct i915_hdcp_component_master - Used for communication between i915
+ * and mei_hdcp for HDCP2.2 services.
+ */
+struct i915_hdcp_component_master {
+ /**
+ */
+ struct device *dev;
+ /**
+ */
+ struct mutex mutex;
+ /**
+ */
+ const struct i915_hdcp_component_ops *ops;
+};
+
#endif /* _I915_COMPONENT_H_ */
--
2.7.4
_______________________________________________
Intel-gfx mailing list
https://lists.freedesktop.org/mailman/listinfo/intel-gfx
--
Daniel Vetter
Software Engineer, Intel Corporation
http://blog.ffwll.ch
Daniel Vetter
2018-12-07 14:29:50 UTC
Permalink
Post by C, Ramalingam
Post by Daniel Vetter
Post by Ramalingam C
Defining the mei-i915 interface functions and initialization of
the interface.
---
drivers/gpu/drm/i915/i915_drv.h | 2 +
drivers/gpu/drm/i915/intel_drv.h | 7 +
drivers/gpu/drm/i915/intel_hdcp.c | 442 +++++++++++++++++++++++++++++++++++++-
include/drm/i915_component.h | 71 ++++++
4 files changed, 521 insertions(+), 1 deletion(-)
diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h
index f763b30f98d9..b68bc980b7cd 100644
--- a/drivers/gpu/drm/i915/i915_drv.h
+++ b/drivers/gpu/drm/i915/i915_drv.h
@@ -2015,6 +2015,8 @@ struct drm_i915_private {
struct i915_pmu pmu;
+ struct i915_hdcp_component_master *hdcp_comp;
+
/*
* NOTE: This is the dri1/ums dungeon, don't add stuff here. Your patch
* will be rejected. Instead look for a better place.
diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h
index 85a526598096..bde82f3ada85 100644
--- a/drivers/gpu/drm/i915/intel_drv.h
+++ b/drivers/gpu/drm/i915/intel_drv.h
@@ -29,6 +29,7 @@
#include <linux/i2c.h>
#include <linux/hdmi.h>
#include <linux/sched/clock.h>
+#include <linux/mei_hdcp.h>
#include <drm/i915_drm.h>
#include "i915_drv.h"
#include <drm/drm_crtc.h>
@@ -379,6 +380,9 @@ struct intel_hdcp_shim {
/* Detects panel's hdcp capability. This is optional for HDMI. */
int (*hdcp_capable)(struct intel_digital_port *intel_dig_port,
bool *hdcp_capable);
+
+ /* Detects the HDCP protocol(DP/HDMI) required on the port */
+ enum mei_hdcp_wired_protocol (*hdcp_protocol)(void);
Looking ahead, this seems hardwired to constant return value? Or why do we
need a function here?
This is hardwired based on the connector type(DP/HDMI).
Since we have the shim for hdcp's connector based work, I have added this function.
Could have done this just with connector_type check, but in that way whole hdcp_shim
can be done in that way. So going with the larger design here.
If it's hardwired then just make it a hardwired struct member. As long as
it's all const, we can mix data an function pointers. If you have runtime
variable data, then it's better to split it out from the ops structure, so
that we can keep ops read-only and protected against possible exploits
(any function pointers are a high-value target in the kernel).
Post by C, Ramalingam
Post by Daniel Vetter
Post by Ramalingam C
};
struct intel_hdcp {
@@ -399,6 +403,9 @@ struct intel_hdcp {
* content can flow only through a link protected by HDCP2.2.
*/
u8 content_type;
+
+ /* mei interface related information */
+ struct mei_hdcp_data mei_data;
};
struct intel_connector {
diff --git a/drivers/gpu/drm/i915/intel_hdcp.c b/drivers/gpu/drm/i915/intel_hdcp.c
index 99dddb540958..760780f1105c 100644
--- a/drivers/gpu/drm/i915/intel_hdcp.c
+++ b/drivers/gpu/drm/i915/intel_hdcp.c
@@ -8,14 +8,20 @@
#include <drm/drmP.h>
#include <drm/drm_hdcp.h>
+#include <drm/i915_component.h>
#include <linux/i2c.h>
#include <linux/random.h>
+#include <linux/component.h>
#include "intel_drv.h"
#include "i915_reg.h"
#define KEY_LOAD_TRIES 5
#define TIME_FOR_ENCRYPT_STATUS_CHANGE 50
+#define GET_MEI_DDI_INDEX(p) ({ \
+ typeof(p) __p = (p); \
+ __p == PORT_A ? MEI_DDI_A : (enum mei_hdcp_ddi)__p;\
+})
static
bool intel_hdcp_is_ksv_valid(u8 *ksv)
@@ -833,6 +839,417 @@ bool is_hdcp_supported(struct drm_i915_private *dev_priv, enum port port)
!IS_CHERRYVIEW(dev_priv) && port < PORT_E);
}
+static __attribute__((unused)) int
+hdcp2_prepare_ake_init(struct intel_connector *connector,
+ struct hdcp2_ake_init *ake_data)
+{
+ struct mei_hdcp_data *data = &connector->hdcp.mei_data;
+ struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
+ struct i915_hdcp_component_master *comp = dev_priv->hdcp_comp;
+ int ret;
+
+ if (!comp)
+ return -EINVAL;
+
+ mutex_lock(&comp->mutex);
+ if (!comp->ops || !comp->dev) {
+ mutex_unlock(&comp->mutex);
+ return -EINVAL;
+ }
+
+ if (data->port == MEI_DDI_INVALID_PORT && connector->encoder)
+ data->port = GET_MEI_DDI_INDEX(connector->encoder->port);
+
+ /* Clear ME FW instance for the port, just incase */
+ comp->ops->close_hdcp_session(comp->dev, data);
Sounds like a bug somewhere if we need this? I'd put this code into the
->initiate_hdcp2_session, with a big WARN_ON if it's actually needed.
Not really. Lets say you have progressed beyond the first step of HDCP2.2 auth along with ME FW.
Now if authentication failed due to some reason, then the HDCP2.2 season created with
ME FW for that port is not closed yet.
So we need to call close_hdcp_session() explicitly on authentication failures.
Session has to be closed before restarting the auth on that port with initialite_hdcp_session.
If we are closing the hdcp session of the port on all auth errors, we dont need this just before
start of the hdcp session.
Yeah, makes sense. But right now the close_session stuff is sprinkled all
over, so it's hard to see what's going on and whether there's bugs
somewhere. I much prefer code that looks like it knows what it's doing. I
think one top-level close_session call in the main hdcp2 functions (called
when everything is done, or on failure) would be much cleaner.

Plus then some WARN_ON to make sure we never forget to close a session.
Sprinkling cleanup code all over the place looks like it's easier, but
long term it's much harder code to maintain and refactor because you never
sure which cleanup code is actually doing the cleanup, and which cleanup
code is just there for nothing.
Post by C, Ramalingam
Post by Daniel Vetter
"Just in case" papering over programming bugs of our own just makes
debugging harder.
Post by Ramalingam C
+
+ ret = comp->ops->initiate_hdcp2_session(comp->dev,
+ data, ake_data);
We should set the port only after successfully initializing this.
hdcp2_session is created for each port at ME FW. Hence we need the port
initialized even before calling the initiate_hdcp2_session.
Post by Daniel Vetter
Post by Ramalingam C
+ if (data->port == MEI_DDI_INVALID_PORT && connector->encoder)
+ data->port = GET_MEI_DDI_INDEX(connector->encoder->port);
This line right above in your patch should only run on success. I think.

Also, looking at this again: Why could connector->encoder ever be NULL?
That smells like another possible bug in our setup code. Better to wrap
this in a WARN_ON and bail out.
Post by C, Ramalingam
Post by Daniel Vetter
Post by Ramalingam C
+ mutex_unlock(&comp->mutex);
+
+ return ret;
+}
+
+static __attribute__((unused)) int
+hdcp2_verify_rx_cert_prepare_km(struct intel_connector *connector,
+ struct hdcp2_ake_send_cert *rx_cert,
+ bool *paired,
+ struct hdcp2_ake_no_stored_km *ek_pub_km,
+ size_t *msg_sz)
+{
+ struct mei_hdcp_data *data = &connector->hdcp.mei_data;
+ struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
+ struct i915_hdcp_component_master *comp = dev_priv->hdcp_comp;
+ int ret;
+
+ if (!comp)
+ return -EINVAL;
+
+ mutex_lock(&comp->mutex);
+ if (!comp->ops || !comp->dev || data->port == MEI_DDI_INVALID_PORT) {
These all look like programming mistakes that should result in a WARN_ON.
We are using the comp->mutex for protecting the interface during the interface
init, usage for mei communication and interface teardown.
But what if mei interface teardown happens between mei communications?
So when we try to access the mei interface after such possible tear down scenario,
we are checking the integrity of the interface.
Possible alternate solution is hold the comp->mutex across the authentication steps.
But consequence is that mei module removal will be blocked for authentication duration
and even if the mei_dev is removed, component unbind will be blocked due to this mutex dependency.
I think with the v7 component code this should all be impossible and we
can remove it. With the v8 component code it's definitely necessary (which
is why I don't like the v8 component code). This is all because I didn't
fully realize the changes with v8 when reading the patches again ...
Post by C, Ramalingam
Post by Daniel Vetter
Post by Ramalingam C
+ mutex_unlock(&comp->mutex);
+ return -EINVAL;
+ }
+
+ ret = comp->ops->verify_receiver_cert_prepare_km(comp->dev, data,
+ rx_cert, paired,
+ ek_pub_km, msg_sz);
+ if (ret < 0)
+ comp->ops->close_hdcp_session(comp->dev, data);
Error handling here seems a bit strange. You close the session, but don't
reset the port. So next op will be totally unaware that things have blown
up. Also no warning.
If we want to close the session, then I think that should be a decision
made higher up.
This is needed as explained above. But as you have mentioned this can be moved
to the end of the authentication on error scenario. I will work on that.
Yeah, I think that'll lead to cleaner code.
Post by C, Ramalingam
Post by Daniel Vetter
Post by Ramalingam C
+ mutex_unlock(&comp->mutex);
With component do we still need this mutex stuff here?
Exact same comments everywhere below.
Post by Ramalingam C
+
+ return ret;
+}
+
+static __attribute__((unused)) int
+hdcp2_verify_hprime(struct intel_connector *connector,
+ struct hdcp2_ake_send_hprime *rx_hprime)
+{
+ struct mei_hdcp_data *data = &connector->hdcp.mei_data;
+ struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
+ struct i915_hdcp_component_master *comp = dev_priv->hdcp_comp;
+ int ret;
+
+ if (!comp)
+ return -EINVAL;
+
+ mutex_lock(&comp->mutex);
+ if (!comp->ops || !comp->dev || data->port == MEI_DDI_INVALID_PORT) {
+ mutex_unlock(&comp->mutex);
+ return -EINVAL;
+ }
+
+ ret = comp->ops->verify_hprime(comp->dev, data, rx_hprime);
+ if (ret < 0)
+ comp->ops->close_hdcp_session(comp->dev, data);
+ mutex_unlock(&comp->mutex);
+
+ return ret;
+}
+
+static __attribute__((unused)) int
+hdcp2_store_pairing_info(struct intel_connector *connector,
+ struct hdcp2_ake_send_pairing_info *pairing_info)
+{
+ struct mei_hdcp_data *data = &connector->hdcp.mei_data;
+ struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
+ struct i915_hdcp_component_master *comp = dev_priv->hdcp_comp;
+ int ret;
+
+ if (!comp)
+ return -EINVAL;
+
+ mutex_lock(&comp->mutex);
+ if (!comp->ops || !comp->dev || data->port == MEI_DDI_INVALID_PORT) {
+ mutex_unlock(&comp->mutex);
+ return -EINVAL;
+ }
+
+ ret = comp->ops->store_pairing_info(comp->dev,
+ data, pairing_info);
+ if (ret < 0)
+ comp->ops->close_hdcp_session(comp->dev, data);
+ mutex_unlock(&comp->mutex);
+
+ return ret;
+}
+
+static __attribute__((unused)) int
+hdcp2_prepare_lc_init(struct intel_connector *connector,
+ struct hdcp2_lc_init *lc_init)
+{
+ struct mei_hdcp_data *data = &connector->hdcp.mei_data;
+ struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
+ struct i915_hdcp_component_master *comp = dev_priv->hdcp_comp;
+ int ret;
+
+ if (!comp)
+ return -EINVAL;
+
+ mutex_lock(&comp->mutex);
+ if (!comp->ops || !comp->dev || data->port == MEI_DDI_INVALID_PORT) {
+ mutex_unlock(&comp->mutex);
+ return -EINVAL;
+ }
+
+ ret = comp->ops->initiate_locality_check(comp->dev,
+ data, lc_init);
+ if (ret < 0)
+ comp->ops->close_hdcp_session(comp->dev, data);
+ mutex_unlock(&comp->mutex);
+
+ return ret;
+}
+
+static __attribute__((unused)) int
+hdcp2_verify_lprime(struct intel_connector *connector,
+ struct hdcp2_lc_send_lprime *rx_lprime)
+{
+ struct mei_hdcp_data *data = &connector->hdcp.mei_data;
+ struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
+ struct i915_hdcp_component_master *comp = dev_priv->hdcp_comp;
+ int ret;
+
+ if (!comp)
+ return -EINVAL;
+
+ mutex_lock(&comp->mutex);
+ if (!comp->ops || !comp->dev || data->port == MEI_DDI_INVALID_PORT) {
+ mutex_unlock(&comp->mutex);
+ return -EINVAL;
+ }
+
+ ret = comp->ops->verify_lprime(comp->dev, data, rx_lprime);
+ if (ret < 0)
+ comp->ops->close_hdcp_session(comp->dev, data);
+ mutex_unlock(&comp->mutex);
+
+ return ret;
+}
+
+static __attribute__((unused))
+int hdcp2_prepare_skey(struct intel_connector *connector,
+ struct hdcp2_ske_send_eks *ske_data)
+{
+ struct mei_hdcp_data *data = &connector->hdcp.mei_data;
+ struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
+ struct i915_hdcp_component_master *comp = dev_priv->hdcp_comp;
+ int ret;
+
+ if (!comp)
+ return -EINVAL;
+
+ mutex_lock(&comp->mutex);
+ if (!comp->ops || !comp->dev || data->port == MEI_DDI_INVALID_PORT) {
+ mutex_unlock(&comp->mutex);
+ return -EINVAL;
+ }
+
+ ret = comp->ops->get_session_key(comp->dev, data, ske_data);
+ if (ret < 0)
+ comp->ops->close_hdcp_session(comp->dev, data);
+ mutex_unlock(&comp->mutex);
+
+ return ret;
+}
+
+static __attribute__((unused)) int
+hdcp2_verify_rep_topology_prepare_ack(struct intel_connector *connector,
+ struct hdcp2_rep_send_receiverid_list
+ *rep_topology,
+ struct hdcp2_rep_send_ack *rep_send_ack)
+{
+ struct mei_hdcp_data *data = &connector->hdcp.mei_data;
+ struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
+ struct i915_hdcp_component_master *comp = dev_priv->hdcp_comp;
+ int ret;
+
+ if (!comp)
+ return -EINVAL;
+
+ mutex_lock(&comp->mutex);
+ if (!comp->ops || !comp->dev || data->port == MEI_DDI_INVALID_PORT) {
+ mutex_unlock(&comp->mutex);
+ return -EINVAL;
+ }
+
+ ret = comp->ops->repeater_check_flow_prepare_ack(comp->dev,
+ data, rep_topology,
+ rep_send_ack);
+ if (ret < 0)
+ comp->ops->close_hdcp_session(comp->dev, data);
+ mutex_unlock(&comp->mutex);
+
+ return ret;
+}
+
+static __attribute__((unused)) int
+hdcp2_verify_mprime(struct intel_connector *connector,
+ struct hdcp2_rep_stream_ready *stream_ready)
+{
+ struct mei_hdcp_data *data = &connector->hdcp.mei_data;
+ struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
+ struct i915_hdcp_component_master *comp = dev_priv->hdcp_comp;
+ int ret;
+
+ if (!comp)
+ return -EINVAL;
+
+ mutex_lock(&comp->mutex);
+ if (!comp->ops || !comp->dev || data->port == MEI_DDI_INVALID_PORT) {
+ mutex_unlock(&comp->mutex);
+ return -EINVAL;
+ }
+
+ ret = comp->ops->verify_mprime(comp->dev, data, stream_ready);
+ if (ret < 0)
+ comp->ops->close_hdcp_session(comp->dev, data);
+ mutex_unlock(&comp->mutex);
+
+ return ret;
+}
+
+static __attribute__((unused))
+int hdcp2_authenticate_port(struct intel_connector *connector)
+{
+ struct mei_hdcp_data *data = &connector->hdcp.mei_data;
+ struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
+ struct i915_hdcp_component_master *comp = dev_priv->hdcp_comp;
+ int ret;
+
+ if (!comp)
+ return -EINVAL;
+
+ mutex_lock(&comp->mutex);
+ if (!comp->ops || !comp->dev || data->port == MEI_DDI_INVALID_PORT) {
+ mutex_unlock(&comp->mutex);
+ return -EINVAL;
+ }
+
+ ret = comp->ops->enable_hdcp_authentication(comp->dev, data);
+ if (ret < 0)
+ comp->ops->close_hdcp_session(comp->dev, data);
+ mutex_unlock(&comp->mutex);
+
+ return ret;
+}
+
+static __attribute__((unused))
+int hdcp2_close_mei_session(struct intel_connector *connector)
+{
+ struct mei_hdcp_data *data = &connector->hdcp.mei_data;
+ struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
+ struct i915_hdcp_component_master *comp = dev_priv->hdcp_comp;
+ int ret;
+
+ if (!comp)
+ return -EINVAL;
+
+ mutex_lock(&comp->mutex);
+ if (!comp->ops || !comp->dev ||
+ data->port == MEI_DDI_INVALID_PORT) {
+ mutex_unlock(&comp->mutex);
+ return -EINVAL;
+ }
+
+ ret = comp->ops->close_hdcp_session(comp->dev, data);
Need to reset the port here I think.
What is the reset of the port you are referring to? port is not
configured for encryption. And this is the call for marking the port as de-authenticated too.
Post by Daniel Vetter
Post by Ramalingam C
+ mutex_unlock(&comp->mutex);
+ return ret;
+}
+
+static __attribute__((unused))
+int hdcp2_deauthenticate_port(struct intel_connector *connector)
+{
+ return hdcp2_close_mei_session(connector);
+}
+
+static int i915_hdcp_component_master_bind(struct device *dev)
+{
+ struct drm_i915_private *dev_priv = kdev_to_i915(dev);
+
+ return component_bind_all(dev, dev_priv->hdcp_comp);
+}
+
+static void intel_connectors_hdcp_disable(struct drm_i915_private *dev_priv)
+{
+ struct drm_device *dev = &dev_priv->drm;
+ struct intel_connector *intel_connector;
+ struct drm_connector *connector;
+ struct drm_connector_list_iter conn_iter;
+
+ drm_connector_list_iter_begin(dev, &conn_iter);
+ drm_for_each_connector_iter(connector, &conn_iter) {
+ intel_connector = to_intel_connector(connector);
+ if (!(intel_connector->hdcp.shim))
+ continue;
+
+ intel_hdcp_disable(intel_connector);
+ }
+ drm_connector_list_iter_end(&conn_iter);
+}
+
+static void i915_hdcp_component_master_unbind(struct device *dev)
+{
+ struct drm_i915_private *dev_priv = kdev_to_i915(dev);
+
+ intel_connectors_hdcp_disable(dev_priv);
Why this code? Once we've unregistered the driver, we should have shut
down the hardware completely. Which should have shut down all the hdcp
users too.
This unbind might be triggered either due to master_del or component_del.
if its triggered from mei through component_del, then before teardown of
the i/f in component_unbind(), disable the ongoing HDCP session through
hdcp_disable() for each connectors.
I looked at your v7 component code again. I think if we put the
drm_atomic_helper_shutdown() call into master_unbind, then we'll have taken care
of that. Essentially what you're doing here is open-coding part of that
function. Better to just move the existing call to the right place.

And shutting down the entire hw is kinda what master_unbind should be
doing I think. We might also need to move the general hw quiescent into
master_unbind, that should work too.
Post by C, Ramalingam
Post by Daniel Vetter
Post by Ramalingam C
+ component_unbind_all(dev, dev_priv->hdcp_comp);
+}
+
+static const struct component_master_ops i915_hdcp_component_master_ops = {
+ .bind = i915_hdcp_component_master_bind,
+ .unbind = i915_hdcp_component_master_unbind,
+};
+
+static int i915_hdcp_component_match(struct device *dev, void *data)
+{
+ return !strcmp(dev->driver->name, "mei_hdcp");
+}
+
+static int intel_hdcp_component_init(struct intel_connector *connector)
+{
+ struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
+ struct i915_hdcp_component_master *comp;
+ struct component_match *match = NULL;
+ int ret;
+
+ comp = kzalloc(sizeof(*comp), GFP_KERNEL);
+ if (!comp)
+ return -ENOMEM;
+
+ mutex_init(&comp->mutex);
+ dev_priv->hdcp_comp = comp;
+
+ component_match_add(dev_priv->drm.dev, &match,
+ i915_hdcp_component_match, dev_priv);
+ ret = component_master_add_with_match(dev_priv->drm.dev,
+ &i915_hdcp_component_master_ops,
+ match);
So I'm not sure this will work out well, hiding the master_ops here in
hdcp. Definitely needs to be rewritten as soon as i915 needs another
component. And might make the load/unload split complicated.
we have already discussed this.
Post by Daniel Vetter
Post by Ramalingam C
+ if (ret < 0)
+ goto out_err;
+
+ DRM_INFO("I915 hdcp component master added.\n");
You add both the master and the hdcp component here. Output is a bit
confusing.
we have component master and a component from mei which will match to the
master.
Here we are adding the component master.
Post by Daniel Vetter
Post by Ramalingam C
+ return ret;
+
+ component_master_del(dev_priv->drm.dev,
+ &i915_hdcp_component_master_ops);
+ kfree(comp);
+ dev_priv->hdcp_comp = NULL;
+ DRM_ERROR("Failed to add i915 hdcp component master (%d)\n", ret);
+
+ return ret;
+}
+
+static int initialize_mei_hdcp_data(struct intel_connector *connector)
+{
+ struct intel_hdcp *hdcp = &connector->hdcp;
+ struct mei_hdcp_data *data = &hdcp->mei_data;
+ enum port port;
+
+ if (connector->encoder) {
+ port = connector->encoder->port;
+ data->port = GET_MEI_DDI_INDEX(port);
+ }
+
+ data->port_type = MEI_HDCP_PORT_TYPE_INTEGRATED;
+ data->protocol = hdcp->shim->hdcp_protocol();
+
+ data->k = 1;
+ if (!data->streams)
+ data->streams = kcalloc(data->k, sizeof(data->streams[0]),
+ GFP_KERNEL);
+ if (!data->streams) {
+ DRM_ERROR("Out of Memory\n");
+ return -ENOMEM;
+ }
+
+ data->streams[0].stream_id = 0;
+ data->streams[0].stream_type = hdcp->content_type;
+
+ return 0;
+}
+
bool is_hdcp2_supported(struct drm_i915_private *dev_priv)
{
return ((INTEL_GEN(dev_priv) >= 10 || IS_GEMINILAKE(dev_priv) ||
@@ -843,10 +1260,25 @@ static void intel_hdcp2_init(struct intel_connector *connector)
{
struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
struct intel_hdcp *hdcp = &connector->hdcp;
+ int ret;
WARN_ON(!is_hdcp2_supported(dev_priv));
- /* TODO: MEI interface needs to be initialized here */
+ ret = initialize_mei_hdcp_data(connector);
+ if (ret) {
+ DRM_DEBUG_KMS("Mei hdcp data init failed\n");
+ return;
+ }
+
+ if (!dev_priv->hdcp_comp)
+ ret = intel_hdcp_component_init(connector);
+
+ if (ret) {
+ DRM_DEBUG_KMS("HDCP comp init failed\n");
+ kfree(hdcp->mei_data.streams);
+ return;
+ }
+
hdcp->hdcp2_supported = 1;
}
@@ -894,9 +1326,17 @@ void intel_hdcp_exit(struct drm_i915_private *dev_priv)
mutex_lock(&intel_connector->hdcp.mutex);
intel_connector->hdcp.hdcp2_supported = 0;
intel_connector->hdcp.shim = NULL;
+ kfree(intel_connector->hdcp.mei_data.streams);
We need to push this into a per-connector hdcp cleanup function. Or just
into the generic connector cleanup.
We could move it to per connector  hdcp cleanup function.
Yeah I think that would be cleaner.
Post by C, Ramalingam
Post by Daniel Vetter
Post by Ramalingam C
mutex_unlock(&intel_connector->hdcp.mutex);
}
drm_connector_list_iter_end(&conn_iter);
+
+ if (dev_priv->hdcp_comp) {
+ component_master_del(dev_priv->drm.dev,
+ &i915_hdcp_component_master_ops);
+ kfree(dev_priv->hdcp_comp);
+ dev_priv->hdcp_comp = NULL;
+ }
}
int intel_hdcp_enable(struct intel_connector *connector)
diff --git a/include/drm/i915_component.h b/include/drm/i915_component.h
index fca22d463e1b..12268228f4dc 100644
--- a/include/drm/i915_component.h
+++ b/include/drm/i915_component.h
@@ -24,8 +24,12 @@
#ifndef _I915_COMPONENT_H_
#define _I915_COMPONENT_H_
+#include <linux/mutex.h>
+
#include "drm_audio_component.h"
+#include <drm/drm_hdcp.h>
+
/* MAX_PORT is the number of port
* It must be sync with I915_MAX_PORTS defined i915_drv.h
*/
@@ -46,4 +50,71 @@ struct i915_audio_component {
int aud_sample_rate[MAX_PORTS];
};
+struct i915_hdcp_component_ops {
Imo that should be called mei_hdcp_component_ops and put into the
linux/mei_hdcp.h header. Or was that Thomas' review comment?
Nope this is there for many versions. i915_hdcp_component_ops are
implemented by mei_hdcp.c and initializes the component with the &ops.
I summarized all the discussion around the i915/mei_hdcp interface in the
thread in an earlier patch in this series.

Cheers, Daniel
Post by C, Ramalingam
Post by Daniel Vetter
Aside: Review here in public channels instead of in private would be much
better for coordination.
Tomas,
Could you please help on this ask.?
--Ram
Post by Daniel Vetter
Post by Ramalingam C
+ /**
+ */
+ struct module *owner;
+
+ int (*initiate_hdcp2_session)(struct device *dev,
+ void *hdcp_data,
+ struct hdcp2_ake_init *ake_data);
+ int (*verify_receiver_cert_prepare_km)(struct device *dev,
+ void *hdcp_data,
+ struct hdcp2_ake_send_cert
+ *rx_cert,
+ bool *km_stored,
+ struct hdcp2_ake_no_stored_km
+ *ek_pub_km,
+ size_t *msg_sz);
+ int (*verify_hprime)(struct device *dev,
+ void *hdcp_data,
+ struct hdcp2_ake_send_hprime *rx_hprime);
+ int (*store_pairing_info)(struct device *dev,
+ void *hdcp_data,
+ struct hdcp2_ake_send_pairing_info
+ *pairing_info);
+ int (*initiate_locality_check)(struct device *dev,
+ void *hdcp_data,
+ struct hdcp2_lc_init *lc_init_data);
+ int (*verify_lprime)(struct device *dev,
+ void *hdcp_data,
+ struct hdcp2_lc_send_lprime *rx_lprime);
+ int (*get_session_key)(struct device *dev,
+ void *hdcp_data,
+ struct hdcp2_ske_send_eks *ske_data);
+ int (*repeater_check_flow_prepare_ack)(struct device *dev,
+ void *hdcp_data,
+ struct hdcp2_rep_send_receiverid_list
+ *rep_topology,
+ struct hdcp2_rep_send_ack
+ *rep_send_ack);
+ int (*verify_mprime)(struct device *dev,
+ void *hdcp_data,
+ struct hdcp2_rep_stream_ready *stream_ready);
+ int (*enable_hdcp_authentication)(struct device *dev,
+ void *hdcp_data);
+ int (*close_hdcp_session)(struct device *dev,
+ void *hdcp_data);
+};
+
+/**
+ * struct i915_hdcp_component_master - Used for communication between i915
+ * and mei_hdcp for HDCP2.2 services.
+ */
+struct i915_hdcp_component_master {
+ /**
+ */
+ struct device *dev;
+ /**
+ */
+ struct mutex mutex;
+ /**
+ */
+ const struct i915_hdcp_component_ops *ops;
+};
+
#endif /* _I915_COMPONENT_H_ */
--
2.7.4
--
Daniel Vetter
Software Engineer, Intel Corporation
http://blog.ffwll.ch
Ramalingam C
2018-11-27 10:43:05 UTC
Permalink
Implements HDCP2.2 authentication for hdcp2.2 receivers, with
following steps:
Authentication and Key exchange (AKE).
Locality Check (LC).
Session Key Exchange(SKE).
DP Errata for stream type configuration for receivers.

At AKE, the HDCP Receiver’s public key certificate is verified by the
HDCP Transmitter. A Master Key k m is exchanged.

At LC, the HDCP Transmitter enforces locality on the content by
requiring that the Round Trip Time (RTT) between a pair of messages
is not more than 20 ms.

At SKE, The HDCP Transmitter exchanges Session Key ks with
the HDCP Receiver.

In DP HDCP2.2 encryption and decryption logics use the stream type as
one of the parameter. So Before enabling the Encryption DP HDCP2.2
receiver needs to be communicated with stream type. This is added to
spec as ERRATA.

This generic implementation is complete only with the hdcp2_shim
defined.

v2:
Rebased.
v3:
No Changes.
v4:
%s/PARING/PAIRING
Coding style fixing [Uma]
v5:
Rebased as part of patch reordering.
Defined the functions for mei services. [Daniel]
v6:
Redefined the mei service functions as per comp redesign.
Required intel_hdcp members are defined [Sean Paul]
v7:
Typo of cipher is Fixed [Uma]
%s/uintxx_t/uxx
Check for comp_master is removed.
v8:
Adjust to the new interface.
Avoid using bool structure members. [Tomas]

Signed-off-by: Ramalingam C <***@intel.com>
---
drivers/gpu/drm/i915/intel_drv.h | 34 ++++++
drivers/gpu/drm/i915/intel_hdcp.c | 211 +++++++++++++++++++++++++++++++++++---
2 files changed, 230 insertions(+), 15 deletions(-)

diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h
index 3e9f21d23442..24d258488efe 100644
--- a/drivers/gpu/drm/i915/intel_drv.h
+++ b/drivers/gpu/drm/i915/intel_drv.h
@@ -387,6 +387,22 @@ struct intel_hdcp_shim {
/* Detects whether Panel is HDCP2.2 capable */
int (*hdcp_2_2_capable)(struct intel_digital_port *intel_dig_port,
bool *capable);
+
+ /* Write HDCP2.2 messages */
+ int (*write_2_2_msg)(struct intel_digital_port *intel_dig_port,
+ void *buf, size_t size);
+
+ /* Read HDCP2.2 messages */
+ int (*read_2_2_msg)(struct intel_digital_port *intel_dig_port,
+ u8 msg_id, void *buf, size_t size);
+
+ /*
+ * Implementation of DP HDCP2.2 Errata for the communication of stream
+ * type to Receivers. In DP HDCP2.2 Stream type is one of the input to
+ * the HDCP2.2 Cipher for En/De-Cryption. Not applicable for HDMI.
+ */
+ int (*config_stream_type)(struct intel_digital_port *intel_dig_port,
+ void *buf, size_t size);
};

struct intel_hdcp {
@@ -411,6 +427,24 @@ struct intel_hdcp {

/* mei interface related information */
struct mei_hdcp_data mei_data;
+
+ u8 is_paired;
+ u8 is_repeater;
+
+ /*
+ * Count of ReceiverID_List received. Initialized to 0 at AKE_INIT.
+ * Incremented after processing the RepeaterAuth_Send_ReceiverID_List.
+ * When it rolls over re-auth has to be triggered.
+ */
+ u32 seq_num_v;
+
+ /*
+ * Count of RepeaterAuth_Stream_Manage msg propagated.
+ * Initialized to 0 on AKE_INIT. Incremented after every successful
+ * transmission of RepeaterAuth_Stream_Manage message. When it rolls
+ * over re-Auth has to be triggered.
+ */
+ u32 seq_num_m;
};

struct intel_connector {
diff --git a/drivers/gpu/drm/i915/intel_hdcp.c b/drivers/gpu/drm/i915/intel_hdcp.c
index c1bd1ccd47cd..0d7fea9c9bb1 100644
--- a/drivers/gpu/drm/i915/intel_hdcp.c
+++ b/drivers/gpu/drm/i915/intel_hdcp.c
@@ -18,6 +18,7 @@

#define KEY_LOAD_TRIES 5
#define TIME_FOR_ENCRYPT_STATUS_CHANGE 50
+#define HDCP2_LC_RETRY_CNT 3
#define GET_MEI_DDI_INDEX(p) ({ \
typeof(p) __p = (p); \
__p == PORT_A ? MEI_DDI_A : (enum mei_hdcp_ddi)__p;\
@@ -876,7 +877,7 @@ bool is_hdcp_supported(struct drm_i915_private *dev_priv, enum port port)
!IS_CHERRYVIEW(dev_priv) && port < PORT_E);
}

-static __attribute__((unused)) int
+static int
hdcp2_prepare_ake_init(struct intel_connector *connector,
struct hdcp2_ake_init *ake_data)
{
@@ -907,7 +908,7 @@ hdcp2_prepare_ake_init(struct intel_connector *connector,
return ret;
}

-static __attribute__((unused)) int
+static int
hdcp2_verify_rx_cert_prepare_km(struct intel_connector *connector,
struct hdcp2_ake_send_cert *rx_cert,
bool *paired,
@@ -938,9 +939,8 @@ hdcp2_verify_rx_cert_prepare_km(struct intel_connector *connector,
return ret;
}

-static __attribute__((unused)) int
-hdcp2_verify_hprime(struct intel_connector *connector,
- struct hdcp2_ake_send_hprime *rx_hprime)
+static int hdcp2_verify_hprime(struct intel_connector *connector,
+ struct hdcp2_ake_send_hprime *rx_hprime)
{
struct mei_hdcp_data *data = &connector->hdcp.mei_data;
struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
@@ -964,7 +964,7 @@ hdcp2_verify_hprime(struct intel_connector *connector,
return ret;
}

-static __attribute__((unused)) int
+static int
hdcp2_store_pairing_info(struct intel_connector *connector,
struct hdcp2_ake_send_pairing_info *pairing_info)
{
@@ -991,7 +991,7 @@ hdcp2_store_pairing_info(struct intel_connector *connector,
return ret;
}

-static __attribute__((unused)) int
+static int
hdcp2_prepare_lc_init(struct intel_connector *connector,
struct hdcp2_lc_init *lc_init)
{
@@ -1018,7 +1018,7 @@ hdcp2_prepare_lc_init(struct intel_connector *connector,
return ret;
}

-static __attribute__((unused)) int
+static int
hdcp2_verify_lprime(struct intel_connector *connector,
struct hdcp2_lc_send_lprime *rx_lprime)
{
@@ -1044,9 +1044,8 @@ hdcp2_verify_lprime(struct intel_connector *connector,
return ret;
}

-static __attribute__((unused))
-int hdcp2_prepare_skey(struct intel_connector *connector,
- struct hdcp2_ske_send_eks *ske_data)
+static int hdcp2_prepare_skey(struct intel_connector *connector,
+ struct hdcp2_ske_send_eks *ske_data)
{
struct mei_hdcp_data *data = &connector->hdcp.mei_data;
struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
@@ -1126,8 +1125,7 @@ hdcp2_verify_mprime(struct intel_connector *connector,
return ret;
}

-static __attribute__((unused))
-int hdcp2_authenticate_port(struct intel_connector *connector)
+static int hdcp2_authenticate_port(struct intel_connector *connector)
{
struct mei_hdcp_data *data = &connector->hdcp.mei_data;
struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
@@ -1178,11 +1176,194 @@ static int hdcp2_deauthenticate_port(struct intel_connector *connector)
return hdcp2_close_mei_session(connector);
}

+/* Authentication flow starts from here */
+static int hdcp2_authentication_key_exchange(struct intel_connector *connector)
+{
+ struct intel_digital_port *intel_dig_port = conn_to_dig_port(connector);
+ struct intel_hdcp *hdcp = &connector->hdcp;
+ union {
+ struct hdcp2_ake_init ake_init;
+ struct hdcp2_ake_send_cert send_cert;
+ struct hdcp2_ake_no_stored_km no_stored_km;
+ struct hdcp2_ake_send_hprime send_hprime;
+ struct hdcp2_ake_send_pairing_info pairing_info;
+ } msgs;
+ const struct intel_hdcp_shim *shim = hdcp->shim;
+ size_t size;
+ int ret;
+ bool is_paired;
+
+ /* Init for seq_num */
+ hdcp->seq_num_v = 0;
+ hdcp->seq_num_m = 0;
+
+ ret = hdcp2_prepare_ake_init(connector, &msgs.ake_init);
+ if (ret < 0)
+ return ret;
+
+ ret = shim->write_2_2_msg(intel_dig_port, &msgs.ake_init,
+ sizeof(msgs.ake_init));
+ if (ret < 0)
+ return ret;
+
+ ret = shim->read_2_2_msg(intel_dig_port, HDCP_2_2_AKE_SEND_CERT,
+ &msgs.send_cert, sizeof(msgs.send_cert));
+ if (ret < 0)
+ return ret;
+
+ if (msgs.send_cert.rx_caps[0] != HDCP_2_2_RX_CAPS_VERSION_VAL)
+ return -EINVAL;
+
+ hdcp->is_repeater = HDCP_2_2_RX_REPEATER(msgs.send_cert.rx_caps[2]) ?
+ 1 : 0;
+
+ /*
+ * Here msgs.no_stored_km will hold msgs corresponding to the km
+ * stored also.
+ */
+ ret = hdcp2_verify_rx_cert_prepare_km(connector, &msgs.send_cert,
+ &is_paired,
+ &msgs.no_stored_km, &size);
+ if (ret < 0)
+ return ret;
+
+ hdcp->is_paired = is_paired ? 1 : 0;
+
+ ret = shim->write_2_2_msg(intel_dig_port, &msgs.no_stored_km, size);
+ if (ret < 0)
+ return ret;
+
+ ret = shim->read_2_2_msg(intel_dig_port, HDCP_2_2_AKE_SEND_HPRIME,
+ &msgs.send_hprime, sizeof(msgs.send_hprime));
+ if (ret < 0)
+ return ret;
+
+ ret = hdcp2_verify_hprime(connector, &msgs.send_hprime);
+ if (ret < 0)
+ return ret;
+
+ if (!hdcp->is_paired) {
+ /* Pairing is required */
+ ret = shim->read_2_2_msg(intel_dig_port,
+ HDCP_2_2_AKE_SEND_PAIRING_INFO,
+ &msgs.pairing_info,
+ sizeof(msgs.pairing_info));
+ if (ret < 0)
+ return ret;
+
+ ret = hdcp2_store_pairing_info(connector, &msgs.pairing_info);
+ if (ret < 0)
+ return ret;
+ hdcp->is_paired = 1;
+ }
+
+ return 0;
+}
+
+static int hdcp2_locality_check(struct intel_connector *connector)
+{
+ struct intel_digital_port *intel_dig_port = conn_to_dig_port(connector);
+ struct intel_hdcp *hdcp = &connector->hdcp;
+ union {
+ struct hdcp2_lc_init lc_init;
+ struct hdcp2_lc_send_lprime send_lprime;
+ } msgs;
+ const struct intel_hdcp_shim *shim = hdcp->shim;
+ int tries = HDCP2_LC_RETRY_CNT, ret, i;
+
+ for (i = 0; i < tries; i++) {
+ ret = hdcp2_prepare_lc_init(connector, &msgs.lc_init);
+ if (ret < 0)
+ continue;
+
+ ret = shim->write_2_2_msg(intel_dig_port, &msgs.lc_init,
+ sizeof(msgs.lc_init));
+ if (ret < 0)
+ continue;
+
+ ret = shim->read_2_2_msg(intel_dig_port,
+ HDCP_2_2_LC_SEND_LPRIME,
+ &msgs.send_lprime,
+ sizeof(msgs.send_lprime));
+ if (ret < 0)
+ continue;
+
+ ret = hdcp2_verify_lprime(connector, &msgs.send_lprime);
+ if (!ret)
+ break;
+ }
+
+ return ret;
+}
+
+static int hdcp2_session_key_exchange(struct intel_connector *connector)
+{
+ struct intel_digital_port *intel_dig_port = conn_to_dig_port(connector);
+ struct intel_hdcp *hdcp = &connector->hdcp;
+ struct hdcp2_ske_send_eks send_eks;
+ int ret;
+
+ ret = hdcp2_prepare_skey(connector, &send_eks);
+ if (ret < 0)
+ return ret;
+
+ ret = hdcp->shim->write_2_2_msg(intel_dig_port, &send_eks,
+ sizeof(send_eks));
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
static int hdcp2_authenticate_sink(struct intel_connector *connector)
{
- DRM_ERROR("Sink authentication is done in subsequent patches\n");
+ struct intel_digital_port *intel_dig_port = conn_to_dig_port(connector);
+ struct intel_hdcp *hdcp = &connector->hdcp;
+ const struct intel_hdcp_shim *shim = hdcp->shim;
+ struct hdcp2_dp_errata_stream_type stream_type_msg;
+ int ret;

- return -EINVAL;
+ ret = hdcp2_authentication_key_exchange(connector);
+ if (ret < 0) {
+ DRM_DEBUG_KMS("AKE Failed. Err : %d\n", ret);
+ return ret;
+ }
+
+ ret = hdcp2_locality_check(connector);
+ if (ret < 0) {
+ DRM_DEBUG_KMS("Locality Check failed. Err : %d\n", ret);
+ return ret;
+ }
+
+ ret = hdcp2_session_key_exchange(connector);
+ if (ret < 0) {
+ DRM_DEBUG_KMS("SKE Failed. Err : %d\n", ret);
+ return ret;
+ }
+
+ if (!hdcp->is_repeater && shim->config_stream_type) {
+ /*
+ * Errata for DP: As Stream type is used for encryption,
+ * Receiver should be communicated with stream type for the
+ * decryption of the content.
+ * Repeater will be communicated with stream type as a
+ * part of it's auth later in time.
+ */
+ stream_type_msg.msg_id = HDCP_2_2_ERRATA_DP_STREAM_TYPE;
+ stream_type_msg.stream_type = hdcp->content_type;
+
+ ret = shim->config_stream_type(intel_dig_port, &stream_type_msg,
+ sizeof(stream_type_msg));
+ if (ret < 0)
+ return ret;
+ }
+
+ hdcp->mei_data.streams[0].stream_type = hdcp->content_type;
+ ret = hdcp2_authenticate_port(connector);
+ if (ret < 0)
+ return ret;
+
+ return ret;
}

static int hdcp2_enable_encryption(struct intel_connector *connector)
--
2.7.4
Ramalingam C
2018-11-27 10:43:06 UTC
Permalink
Implements the HDCP2.2 repeaters authentication steps such as verifying
the downstream topology and sending stream management information.

v2:
Rebased.
v3:
No Changes.
v4:
-EINVAL is returned for topology error and rollover scenario.
Endianness conversion func from drm_hdcp.h is used [Uma]
v5:
Rebased as part of patches reordering.
Defined the mei service functions [Daniel]
v6:
Redefined the mei service functions as per comp redesign.
v7:
%s/uintxx_t/uxx
Check for comp_master is removed.
v8:
Adjust to the new mei interface.
style issue fixed.

Signed-off-by: Ramalingam C <***@intel.com>
---
drivers/gpu/drm/i915/intel_hdcp.c | 125 +++++++++++++++++++++++++++++++++++++-
include/drm/drm_hdcp.h | 15 +++++
2 files changed, 138 insertions(+), 2 deletions(-)

diff --git a/drivers/gpu/drm/i915/intel_hdcp.c b/drivers/gpu/drm/i915/intel_hdcp.c
index 0d7fea9c9bb1..679f3c164582 100644
--- a/drivers/gpu/drm/i915/intel_hdcp.c
+++ b/drivers/gpu/drm/i915/intel_hdcp.c
@@ -1069,7 +1069,7 @@ static int hdcp2_prepare_skey(struct intel_connector *connector,
return ret;
}

-static __attribute__((unused)) int
+static int
hdcp2_verify_rep_topology_prepare_ack(struct intel_connector *connector,
struct hdcp2_rep_send_receiverid_list
*rep_topology,
@@ -1099,7 +1099,7 @@ hdcp2_verify_rep_topology_prepare_ack(struct intel_connector *connector,
return ret;
}

-static __attribute__((unused)) int
+static int
hdcp2_verify_mprime(struct intel_connector *connector,
struct hdcp2_rep_stream_ready *stream_ready)
{
@@ -1315,6 +1315,121 @@ static int hdcp2_session_key_exchange(struct intel_connector *connector)
return 0;
}

+static
+int hdcp2_propagate_stream_management_info(struct intel_connector *connector)
+{
+ struct intel_digital_port *intel_dig_port = conn_to_dig_port(connector);
+ struct intel_hdcp *hdcp = &connector->hdcp;
+ union {
+ struct hdcp2_rep_stream_manage stream_manage;
+ struct hdcp2_rep_stream_ready stream_ready;
+ } msgs;
+ const struct intel_hdcp_shim *shim = hdcp->shim;
+ int ret;
+
+ /* Prepare RepeaterAuth_Stream_Manage msg */
+ msgs.stream_manage.msg_id = HDCP_2_2_REP_STREAM_MANAGE;
+ reverse_endianness(msgs.stream_manage.seq_num_m, HDCP_2_2_SEQ_NUM_LEN,
+ (u8 *)&hdcp->seq_num_m);
+
+ /* K no of streams is fixed as 1. Stored as big-endian. */
+ msgs.stream_manage.k = __swab16(1);
+
+ /* For HDMI this is forced to be 0x0. For DP SST also this is 0x0. */
+ msgs.stream_manage.streams[0].stream_id = 0;
+ msgs.stream_manage.streams[0].stream_type = hdcp->content_type;
+
+ /* Send it to Repeater */
+ ret = shim->write_2_2_msg(intel_dig_port, &msgs.stream_manage,
+ sizeof(msgs.stream_manage));
+ if (ret < 0)
+ return ret;
+
+ ret = shim->read_2_2_msg(intel_dig_port, HDCP_2_2_REP_STREAM_READY,
+ &msgs.stream_ready, sizeof(msgs.stream_ready));
+ if (ret < 0)
+ return ret;
+
+ hdcp->mei_data.seq_num_m = hdcp->seq_num_m;
+ hdcp->mei_data.streams[0].stream_type = hdcp->content_type;
+
+ ret = hdcp2_verify_mprime(connector, &msgs.stream_ready);
+ if (ret < 0)
+ return ret;
+
+ hdcp->seq_num_m++;
+
+ if (hdcp->seq_num_m > HDCP_2_2_SEQ_NUM_MAX) {
+ DRM_DEBUG_KMS("seq_num_m roll over.\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+static
+int hdcp2_authenticate_repeater_topology(struct intel_connector *connector)
+{
+ struct intel_digital_port *intel_dig_port = conn_to_dig_port(connector);
+ struct intel_hdcp *hdcp = &connector->hdcp;
+ union {
+ struct hdcp2_rep_send_receiverid_list recvid_list;
+ struct hdcp2_rep_send_ack rep_ack;
+ } msgs;
+ const struct intel_hdcp_shim *shim = hdcp->shim;
+ u8 *rx_info;
+ u32 seq_num_v;
+ int ret;
+
+ ret = shim->read_2_2_msg(intel_dig_port, HDCP_2_2_REP_SEND_RECVID_LIST,
+ &msgs.recvid_list, sizeof(msgs.recvid_list));
+ if (ret < 0)
+ return ret;
+
+ rx_info = msgs.recvid_list.rx_info;
+
+ if (HDCP_2_2_MAX_CASCADE_EXCEEDED(rx_info[1]) ||
+ HDCP_2_2_MAX_DEVS_EXCEEDED(rx_info[1])) {
+ DRM_DEBUG_KMS("Topology Max Size Exceeded\n");
+ return -EINVAL;
+ }
+
+ /* Converting and Storing the seq_num_v to local variable as DWORD */
+ reverse_endianness((u8 *)&seq_num_v, HDCP_2_2_SEQ_NUM_LEN,
+ msgs.recvid_list.seq_num_v);
+
+ if (seq_num_v < hdcp->seq_num_v) {
+ /* Roll over of the seq_num_v from repeater. Reauthenticate. */
+ DRM_DEBUG_KMS("Seq_num_v roll over.\n");
+ return -EINVAL;
+ }
+
+ ret = hdcp2_verify_rep_topology_prepare_ack(connector,
+ &msgs.recvid_list,
+ &msgs.rep_ack);
+ if (ret < 0)
+ return ret;
+
+ hdcp->seq_num_v = seq_num_v;
+ ret = shim->write_2_2_msg(intel_dig_port, &msgs.rep_ack,
+ sizeof(msgs.rep_ack));
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static int hdcp2_authenticate_repeater(struct intel_connector *connector)
+{
+ int ret;
+
+ ret = hdcp2_authenticate_repeater_topology(connector);
+ if (ret < 0)
+ return ret;
+
+ return hdcp2_propagate_stream_management_info(connector);
+}
+
static int hdcp2_authenticate_sink(struct intel_connector *connector)
{
struct intel_digital_port *intel_dig_port = conn_to_dig_port(connector);
@@ -1356,6 +1471,12 @@ static int hdcp2_authenticate_sink(struct intel_connector *connector)
sizeof(stream_type_msg));
if (ret < 0)
return ret;
+ } else if (hdcp->is_repeater) {
+ ret = hdcp2_authenticate_repeater(connector);
+ if (ret < 0) {
+ DRM_DEBUG_KMS("Repeater Auth Failed. Err: %d\n", ret);
+ return ret;
+ }
}

hdcp->mei_data.streams[0].stream_type = hdcp->content_type;
diff --git a/include/drm/drm_hdcp.h b/include/drm/drm_hdcp.h
index a6de09c5e47f..3e45a7618ab3 100644
--- a/include/drm/drm_hdcp.h
+++ b/include/drm/drm_hdcp.h
@@ -250,4 +250,19 @@ struct hdcp2_dp_errata_stream_type {
#define HDCP_2_2_HDMI_RXSTATUS_READY(x) ((x) & BIT(2))
#define HDCP_2_2_HDMI_RXSTATUS_REAUTH_REQ(x) ((x) & BIT(3))

+/*
+ * Library functions for endianness are aligned for 16/32/64 bits.
+ * But hdcp sequence numbers are 24bits. So for their Byte swapping,
+ * a conversion function is developed.
+ */
+static inline void reverse_endianness(u8 *dest, size_t sz, u8 *src)
+{
+ u32 index;
+
+ if (!sz || sz > sizeof(u32))
+ return;
+ for (index = 0; index < sz; index++)
+ dest[sz - index - 1] = src[index];
+}
+
#endif
--
2.7.4
Daniel Vetter
2018-12-06 10:45:49 UTC
Permalink
Post by Ramalingam C
Implements the HDCP2.2 repeaters authentication steps such as verifying
the downstream topology and sending stream management information.
Rebased.
No Changes.
-EINVAL is returned for topology error and rollover scenario.
Endianness conversion func from drm_hdcp.h is used [Uma]
Rebased as part of patches reordering.
Defined the mei service functions [Daniel]
Redefined the mei service functions as per comp redesign.
%s/uintxx_t/uxx
Check for comp_master is removed.
Adjust to the new mei interface.
style issue fixed.
---
drivers/gpu/drm/i915/intel_hdcp.c | 125 +++++++++++++++++++++++++++++++++++++-
include/drm/drm_hdcp.h | 15 +++++
2 files changed, 138 insertions(+), 2 deletions(-)
diff --git a/drivers/gpu/drm/i915/intel_hdcp.c b/drivers/gpu/drm/i915/intel_hdcp.c
index 0d7fea9c9bb1..679f3c164582 100644
--- a/drivers/gpu/drm/i915/intel_hdcp.c
+++ b/drivers/gpu/drm/i915/intel_hdcp.c
@@ -1069,7 +1069,7 @@ static int hdcp2_prepare_skey(struct intel_connector *connector,
return ret;
}
-static __attribute__((unused)) int
+static int
hdcp2_verify_rep_topology_prepare_ack(struct intel_connector *connector,
struct hdcp2_rep_send_receiverid_list
*rep_topology,
@@ -1099,7 +1099,7 @@ hdcp2_verify_rep_topology_prepare_ack(struct intel_connector *connector,
return ret;
}
-static __attribute__((unused)) int
+static int
hdcp2_verify_mprime(struct intel_connector *connector,
struct hdcp2_rep_stream_ready *stream_ready)
{
@@ -1315,6 +1315,121 @@ static int hdcp2_session_key_exchange(struct intel_connector *connector)
return 0;
}
+static
+int hdcp2_propagate_stream_management_info(struct intel_connector *connector)
+{
+ struct intel_digital_port *intel_dig_port = conn_to_dig_port(connector);
+ struct intel_hdcp *hdcp = &connector->hdcp;
+ union {
+ struct hdcp2_rep_stream_manage stream_manage;
+ struct hdcp2_rep_stream_ready stream_ready;
+ } msgs;
+ const struct intel_hdcp_shim *shim = hdcp->shim;
+ int ret;
+
+ /* Prepare RepeaterAuth_Stream_Manage msg */
+ msgs.stream_manage.msg_id = HDCP_2_2_REP_STREAM_MANAGE;
+ reverse_endianness(msgs.stream_manage.seq_num_m, HDCP_2_2_SEQ_NUM_LEN,
+ (u8 *)&hdcp->seq_num_m);
+
+ /* K no of streams is fixed as 1. Stored as big-endian. */
+ msgs.stream_manage.k = __swab16(1);
+
+ /* For HDMI this is forced to be 0x0. For DP SST also this is 0x0. */
+ msgs.stream_manage.streams[0].stream_id = 0;
+ msgs.stream_manage.streams[0].stream_type = hdcp->content_type;
+
+ /* Send it to Repeater */
+ ret = shim->write_2_2_msg(intel_dig_port, &msgs.stream_manage,
+ sizeof(msgs.stream_manage));
+ if (ret < 0)
+ return ret;
+
+ ret = shim->read_2_2_msg(intel_dig_port, HDCP_2_2_REP_STREAM_READY,
+ &msgs.stream_ready, sizeof(msgs.stream_ready));
+ if (ret < 0)
+ return ret;
+
+ hdcp->mei_data.seq_num_m = hdcp->seq_num_m;
+ hdcp->mei_data.streams[0].stream_type = hdcp->content_type;
+
+ ret = hdcp2_verify_mprime(connector, &msgs.stream_ready);
+ if (ret < 0)
+ return ret;
+
+ hdcp->seq_num_m++;
+
+ if (hdcp->seq_num_m > HDCP_2_2_SEQ_NUM_MAX) {
+ DRM_DEBUG_KMS("seq_num_m roll over.\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+static
+int hdcp2_authenticate_repeater_topology(struct intel_connector *connector)
+{
+ struct intel_digital_port *intel_dig_port = conn_to_dig_port(connector);
+ struct intel_hdcp *hdcp = &connector->hdcp;
+ union {
+ struct hdcp2_rep_send_receiverid_list recvid_list;
+ struct hdcp2_rep_send_ack rep_ack;
+ } msgs;
+ const struct intel_hdcp_shim *shim = hdcp->shim;
+ u8 *rx_info;
+ u32 seq_num_v;
+ int ret;
+
+ ret = shim->read_2_2_msg(intel_dig_port, HDCP_2_2_REP_SEND_RECVID_LIST,
+ &msgs.recvid_list, sizeof(msgs.recvid_list));
+ if (ret < 0)
+ return ret;
+
+ rx_info = msgs.recvid_list.rx_info;
+
+ if (HDCP_2_2_MAX_CASCADE_EXCEEDED(rx_info[1]) ||
+ HDCP_2_2_MAX_DEVS_EXCEEDED(rx_info[1])) {
+ DRM_DEBUG_KMS("Topology Max Size Exceeded\n");
+ return -EINVAL;
+ }
+
+ /* Converting and Storing the seq_num_v to local variable as DWORD */
+ reverse_endianness((u8 *)&seq_num_v, HDCP_2_2_SEQ_NUM_LEN,
+ msgs.recvid_list.seq_num_v);
+
+ if (seq_num_v < hdcp->seq_num_v) {
+ /* Roll over of the seq_num_v from repeater. Reauthenticate. */
+ DRM_DEBUG_KMS("Seq_num_v roll over.\n");
+ return -EINVAL;
+ }
+
+ ret = hdcp2_verify_rep_topology_prepare_ack(connector,
+ &msgs.recvid_list,
+ &msgs.rep_ack);
+ if (ret < 0)
+ return ret;
+
+ hdcp->seq_num_v = seq_num_v;
+ ret = shim->write_2_2_msg(intel_dig_port, &msgs.rep_ack,
+ sizeof(msgs.rep_ack));
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static int hdcp2_authenticate_repeater(struct intel_connector *connector)
+{
+ int ret;
+
+ ret = hdcp2_authenticate_repeater_topology(connector);
+ if (ret < 0)
+ return ret;
+
+ return hdcp2_propagate_stream_management_info(connector);
+}
+
static int hdcp2_authenticate_sink(struct intel_connector *connector)
{
struct intel_digital_port *intel_dig_port = conn_to_dig_port(connector);
@@ -1356,6 +1471,12 @@ static int hdcp2_authenticate_sink(struct intel_connector *connector)
sizeof(stream_type_msg));
if (ret < 0)
return ret;
+ } else if (hdcp->is_repeater) {
+ ret = hdcp2_authenticate_repeater(connector);
+ if (ret < 0) {
+ DRM_DEBUG_KMS("Repeater Auth Failed. Err: %d\n", ret);
+ return ret;
+ }
}
hdcp->mei_data.streams[0].stream_type = hdcp->content_type;
diff --git a/include/drm/drm_hdcp.h b/include/drm/drm_hdcp.h
index a6de09c5e47f..3e45a7618ab3 100644
--- a/include/drm/drm_hdcp.h
+++ b/include/drm/drm_hdcp.h
@@ -250,4 +250,19 @@ struct hdcp2_dp_errata_stream_type {
#define HDCP_2_2_HDMI_RXSTATUS_READY(x) ((x) & BIT(2))
#define HDCP_2_2_HDMI_RXSTATUS_REAUTH_REQ(x) ((x) & BIT(3))
+/*
+ * Library functions for endianness are aligned for 16/32/64 bits.
+ * But hdcp sequence numbers are 24bits. So for their Byte swapping,
+ * a conversion function is developed.
+ */
+static inline void reverse_endianness(u8 *dest, size_t sz, u8 *src)
This feels a bit fake-generic and doesn't fit that well into what you want
it to do in the code actually. What about:

u32 drm_hdcp2_seq_num_to_u32(u8 seq_num[HDCP_2_2_SEQ_NUM_LEN])
{
return seq_num[2] | seq_num[1] << 8 | seq_num[0] << 16;
}

(I didn't check the math).

I think this would both make the code more clearer and avoid the problem
of "shouldn't we make sure this works for everything and put it into
linux/string.h?"

Again I'll check the actual hdcp2 flow later on.

In general I'm kinda not happy with the shim midlayer design, but I wasn't
happy with that with hdcp1 either, and hdcp2 is just an extension, and I
don't really have a better idea either so seems all good to me.
-Daniel
Post by Ramalingam C
+{
+ u32 index;
+
+ if (!sz || sz > sizeof(u32))
+ return;
+ for (index = 0; index < sz; index++)
+ dest[sz - index - 1] = src[index];
+}
+
#endif
--
2.7.4
--
Daniel Vetter
Software Engineer, Intel Corporation
http://blog.ffwll.ch
Ramalingam C
2018-11-27 10:43:10 UTC
Permalink
Implements the DP adaptation specific HDCP2.2 functions.

These functions perform the DPCD read and write for communicating the
HDCP2.2 auth message back and forth.

v2:
wait for cp_irq is merged with this patch. Rebased.
v3:
wait_queue is used for wait for cp_irq [Chris Wilson]
v4:
Style fixed.
%s/PARING/PAIRING
Few style fixes [Uma]
v5:
Lookup table for DP HDCP2.2 msg details [Daniel].
Extra lines are removed.
v6:
Rebased.
v7:
Fixed some regression introduced at v5. [Ankit]
Macro HDCP_2_2_RX_CAPS_VERSION_VAL is reused [Uma]
Converted a function to inline [Uma]
%s/uintxx_t/uxx
v8:
Error due to the sinks are reported as DEBUG logs.
Adjust to the new mei interface.

Signed-off-by: Ramalingam C <***@intel.com>
Signed-off-by: Ankit K Nautiyal <***@intel.com>
Reviewed-by: Uma Shankar <***@intel.com>
---
drivers/gpu/drm/i915/intel_dp.c | 338 ++++++++++++++++++++++++++++++++++++++
drivers/gpu/drm/i915/intel_drv.h | 7 +
drivers/gpu/drm/i915/intel_hdcp.c | 6 +
3 files changed, 351 insertions(+)

diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c
index ecc4706db7dc..1cc82e490999 100644
--- a/drivers/gpu/drm/i915/intel_dp.c
+++ b/drivers/gpu/drm/i915/intel_dp.c
@@ -31,6 +31,7 @@
#include <linux/types.h>
#include <linux/notifier.h>
#include <linux/reboot.h>
+#include <linux/mei_hdcp.h>
#include <asm/byteorder.h>
#include <drm/drmP.h>
#include <drm/drm_atomic_helper.h>
@@ -5347,6 +5348,27 @@ void intel_dp_encoder_suspend(struct intel_encoder *intel_encoder)
pps_unlock(intel_dp);
}

+static int intel_dp_hdcp_wait_for_cp_irq(struct intel_hdcp *hdcp,
+ int timeout)
+{
+ long ret;
+
+ /* Reinit */
+ atomic_set(&hdcp->cp_irq_recved, 0);
+
+#define C (atomic_read(&hdcp->cp_irq_recved) > 0)
+ ret = wait_event_interruptible_timeout(hdcp->cp_irq_queue, C,
+ msecs_to_jiffies(timeout));
+
+ if (ret > 0) {
+ atomic_set(&hdcp->cp_irq_recved, 0);
+ return 0;
+ } else if (!ret) {
+ return -ETIMEDOUT;
+ }
+ return (int)ret;
+}
+
static
int intel_dp_hdcp_write_an_aksv(struct intel_digital_port *intel_dig_port,
u8 *an)
@@ -5570,6 +5592,316 @@ int intel_dp_hdcp_capable(struct intel_digital_port *intel_dig_port,
return 0;
}

+static struct hdcp2_dp_msg_data {
+ u8 msg_id;
+ u32 offset;
+ bool msg_detectable;
+ u32 timeout;
+ u32 timeout2; /* Added for non_paired situation */
+ } hdcp2_msg_data[] = {
+ {HDCP_2_2_AKE_INIT, DP_HDCP_2_2_AKE_INIT_OFFSET, false, 0, 0},
+ {HDCP_2_2_AKE_SEND_CERT, DP_HDCP_2_2_AKE_SEND_CERT_OFFSET,
+ false, HDCP_2_2_CERT_TIMEOUT_MS, 0},
+ {HDCP_2_2_AKE_NO_STORED_KM, DP_HDCP_2_2_AKE_NO_STORED_KM_OFFSET,
+ false, 0, 0},
+ {HDCP_2_2_AKE_STORED_KM, DP_HDCP_2_2_AKE_STORED_KM_OFFSET,
+ false, 0, 0},
+ {HDCP_2_2_AKE_SEND_HPRIME, DP_HDCP_2_2_AKE_SEND_HPRIME_OFFSET,
+ true, HDCP_2_2_HPRIME_PAIRED_TIMEOUT_MS,
+ HDCP_2_2_HPRIME_NO_PAIRED_TIMEOUT_MS},
+ {HDCP_2_2_AKE_SEND_PAIRING_INFO,
+ DP_HDCP_2_2_AKE_SEND_PAIRING_INFO_OFFSET, true,
+ HDCP_2_2_PAIRING_TIMEOUT_MS, 0},
+ {HDCP_2_2_LC_INIT, DP_HDCP_2_2_LC_INIT_OFFSET, false, 0, 0},
+ {HDCP_2_2_LC_SEND_LPRIME, DP_HDCP_2_2_LC_SEND_LPRIME_OFFSET,
+ false, HDCP_2_2_DP_LPRIME_TIMEOUT_MS, 0},
+ {HDCP_2_2_SKE_SEND_EKS, DP_HDCP_2_2_SKE_SEND_EKS_OFFSET, false,
+ 0, 0},
+ {HDCP_2_2_REP_SEND_RECVID_LIST,
+ DP_HDCP_2_2_REP_SEND_RECVID_LIST_OFFSET, true,
+ HDCP_2_2_RECVID_LIST_TIMEOUT_MS, 0},
+ {HDCP_2_2_REP_SEND_ACK, DP_HDCP_2_2_REP_SEND_ACK_OFFSET, false,
+ 0, 0},
+ {HDCP_2_2_REP_STREAM_MANAGE,
+ DP_HDCP_2_2_REP_STREAM_MANAGE_OFFSET, false,
+ 0, 0},
+ {HDCP_2_2_REP_STREAM_READY, DP_HDCP_2_2_REP_STREAM_READY_OFFSET,
+ false, HDCP_2_2_STREAM_READY_TIMEOUT_MS, 0},
+ {HDCP_2_2_ERRATA_DP_STREAM_TYPE,
+ DP_HDCP_2_2_REG_STREAM_TYPE_OFFSET, false,
+ 0, 0},
+ };
+
+static inline
+int intel_dp_hdcp2_read_rx_status(struct intel_digital_port *intel_dig_port,
+ u8 *rx_status)
+{
+ ssize_t ret;
+
+ ret = drm_dp_dpcd_read(&intel_dig_port->dp.aux,
+ DP_HDCP_2_2_REG_RXSTATUS_OFFSET, rx_status,
+ HDCP_2_2_DP_RXSTATUS_LEN);
+ if (ret != HDCP_2_2_DP_RXSTATUS_LEN) {
+ DRM_DEBUG_KMS("Read bstatus from DP/AUX failed (%zd)\n", ret);
+ return ret >= 0 ? -EIO : ret;
+ }
+
+ return 0;
+}
+
+static
+int hdcp2_detect_msg_availability(struct intel_digital_port *intel_dig_port,
+ u8 msg_id, bool *msg_ready)
+{
+ u8 rx_status;
+ int ret;
+
+ *msg_ready = false;
+ ret = intel_dp_hdcp2_read_rx_status(intel_dig_port, &rx_status);
+ if (ret < 0)
+ return ret;
+
+ switch (msg_id) {
+ case HDCP_2_2_AKE_SEND_HPRIME:
+ if (HDCP_2_2_DP_RXSTATUS_H_PRIME(rx_status))
+ *msg_ready = true;
+ break;
+ case HDCP_2_2_AKE_SEND_PAIRING_INFO:
+ if (HDCP_2_2_DP_RXSTATUS_PAIRING(rx_status))
+ *msg_ready = true;
+ break;
+ case HDCP_2_2_REP_SEND_RECVID_LIST:
+ if (HDCP_2_2_DP_RXSTATUS_READY(rx_status))
+ *msg_ready = true;
+ break;
+ default:
+ DRM_ERROR("Unidentified msg_id: %d\n", msg_id);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static ssize_t
+intel_dp_hdcp2_wait_for_msg(struct intel_digital_port *intel_dig_port,
+ struct hdcp2_dp_msg_data *hdcp2_msg_data)
+{
+ struct intel_dp *dp = &intel_dig_port->dp;
+ struct intel_hdcp *hdcp = &dp->attached_connector->hdcp;
+ int ret, timeout;
+ bool msg_ready = false;
+
+ if (hdcp2_msg_data->msg_id == HDCP_2_2_AKE_SEND_HPRIME &&
+ !hdcp->is_paired)
+ timeout = hdcp2_msg_data->timeout2;
+ else
+ timeout = hdcp2_msg_data->timeout;
+
+ /*
+ * There is no way to detect the CERT, LPRIME and STREAM_READY
+ * availability. So Wait for timeout and read the msg.
+ */
+ if (!hdcp2_msg_data->msg_detectable) {
+ mdelay(timeout);
+ ret = 0;
+ } else {
+ /*
+ * Ignoring the return of the intel_dp_hdcp_wait_for_cp_irq,
+ * Just to detect the msg availability before failing it.
+ */
+ intel_dp_hdcp_wait_for_cp_irq(hdcp, timeout);
+ ret = hdcp2_detect_msg_availability(intel_dig_port,
+ hdcp2_msg_data->msg_id,
+ &msg_ready);
+ if (!msg_ready)
+ ret = -ETIMEDOUT;
+ }
+
+ if (ret)
+ DRM_DEBUG_KMS("msg_id %d, ret %d, timeout(mSec): %d\n",
+ hdcp2_msg_data->msg_id, ret, timeout);
+
+ return ret;
+}
+
+static struct hdcp2_dp_msg_data *get_hdcp2_dp_msg_data(u8 msg_id)
+{
+ int i;
+
+ for (i = 0; i < sizeof(hdcp2_msg_data); i++)
+ if (hdcp2_msg_data[i].msg_id == msg_id)
+ return &hdcp2_msg_data[i];
+
+ return NULL;
+}
+
+static
+int intel_dp_hdcp2_write_msg(struct intel_digital_port *intel_dig_port,
+ void *buf, size_t size)
+{
+ unsigned int offset;
+ u8 *byte = buf;
+ ssize_t ret, bytes_to_write, len;
+ struct hdcp2_dp_msg_data *hdcp2_msg_data;
+
+ hdcp2_msg_data = get_hdcp2_dp_msg_data(*byte);
+ if (!hdcp2_msg_data)
+ return -EINVAL;
+
+ offset = hdcp2_msg_data->offset;
+
+ /* No msg_id in DP HDCP2.2 msgs */
+ bytes_to_write = size - 1;
+ byte++;
+
+ while (bytes_to_write) {
+ len = bytes_to_write > DP_AUX_MAX_PAYLOAD_BYTES ?
+ DP_AUX_MAX_PAYLOAD_BYTES : bytes_to_write;
+
+ ret = drm_dp_dpcd_write(&intel_dig_port->dp.aux,
+ offset, (void *)byte, len);
+ if (ret < 0)
+ return ret;
+
+ bytes_to_write -= ret;
+ byte += ret;
+ offset += ret;
+ }
+
+ return size;
+}
+
+static
+ssize_t get_receiver_id_list_size(struct intel_digital_port *intel_dig_port)
+{
+ u8 rx_info[HDCP_2_2_RXINFO_LEN];
+ u32 dev_cnt;
+ ssize_t ret;
+
+ ret = drm_dp_dpcd_read(&intel_dig_port->dp.aux,
+ DP_HDCP_2_2_REG_RXINFO_OFFSET,
+ (void *)rx_info, HDCP_2_2_RXINFO_LEN);
+ if (ret != HDCP_2_2_RXINFO_LEN)
+ return ret >= 0 ? -EIO : ret;
+
+ dev_cnt = (HDCP_2_2_DEV_COUNT_HI(rx_info[0]) << 4 |
+ HDCP_2_2_DEV_COUNT_LO(rx_info[1]));
+
+ if (dev_cnt > HDCP_2_2_MAX_DEVICE_COUNT)
+ dev_cnt = HDCP_2_2_MAX_DEVICE_COUNT;
+
+ ret = sizeof(struct hdcp2_rep_send_receiverid_list) -
+ HDCP_2_2_RECEIVER_IDS_MAX_LEN +
+ (dev_cnt * HDCP_2_2_RECEIVER_ID_LEN);
+
+ return ret;
+}
+
+static
+int intel_dp_hdcp2_read_msg(struct intel_digital_port *intel_dig_port,
+ u8 msg_id, void *buf, size_t size)
+{
+ unsigned int offset;
+ u8 *byte = buf;
+ ssize_t ret, bytes_to_recv, len;
+ struct hdcp2_dp_msg_data *hdcp2_msg_data;
+
+ hdcp2_msg_data = get_hdcp2_dp_msg_data(msg_id);
+ if (!hdcp2_msg_data)
+ return -EINVAL;
+ offset = hdcp2_msg_data->offset;
+
+ ret = intel_dp_hdcp2_wait_for_msg(intel_dig_port, hdcp2_msg_data);
+ if (ret < 0)
+ return ret;
+
+ if (msg_id == HDCP_2_2_REP_SEND_RECVID_LIST) {
+ ret = get_receiver_id_list_size(intel_dig_port);
+ if (ret < 0)
+ return ret;
+
+ size = ret;
+ }
+ bytes_to_recv = size - 1;
+
+ /* DP adaptation msgs has no msg_id */
+ byte++;
+
+ while (bytes_to_recv) {
+ len = bytes_to_recv > DP_AUX_MAX_PAYLOAD_BYTES ?
+ DP_AUX_MAX_PAYLOAD_BYTES : bytes_to_recv;
+
+ ret = drm_dp_dpcd_read(&intel_dig_port->dp.aux, offset,
+ (void *)byte, len);
+ if (ret < 0) {
+ DRM_DEBUG_KMS("msg_id %d, ret %zd\n", msg_id, ret);
+ return ret;
+ }
+
+ bytes_to_recv -= ret;
+ byte += ret;
+ offset += ret;
+ }
+ byte = buf;
+ *byte = msg_id;
+
+ return size;
+}
+
+static
+int intel_dp_hdcp2_config_stream_type(struct intel_digital_port *intel_dig_port,
+ void *buf, size_t size)
+{
+ return intel_dp_hdcp2_write_msg(intel_dig_port, buf, size);
+}
+
+static
+int intel_dp_hdcp2_check_link(struct intel_digital_port *intel_dig_port)
+{
+ u8 rx_status;
+ int ret;
+
+ ret = intel_dp_hdcp2_read_rx_status(intel_dig_port, &rx_status);
+ if (ret)
+ return ret;
+
+ if (HDCP_2_2_DP_RXSTATUS_REAUTH_REQ(rx_status))
+ ret = DRM_HDCP_REAUTH_REQUEST;
+ else if (HDCP_2_2_DP_RXSTATUS_LINK_FAILED(rx_status))
+ ret = DRM_HDCP_LINK_INTEGRITY_FAILURE;
+ else if (HDCP_2_2_DP_RXSTATUS_READY(rx_status))
+ ret = DRM_HDCP_TOPOLOGY_CHANGE;
+
+ return ret;
+}
+
+static
+int intel_dp_hdcp2_capable(struct intel_digital_port *intel_dig_port,
+ bool *capable)
+{
+ u8 rx_caps[3];
+ int ret;
+
+ *capable = false;
+ ret = drm_dp_dpcd_read(&intel_dig_port->dp.aux,
+ DP_HDCP_2_2_REG_RX_CAPS_OFFSET,
+ rx_caps, HDCP_2_2_RXCAPS_LEN);
+ if (ret != HDCP_2_2_RXCAPS_LEN)
+ return ret >= 0 ? -EIO : ret;
+
+ if (rx_caps[0] == HDCP_2_2_RX_CAPS_VERSION_VAL &&
+ HDCP_2_2_DP_HDCP_CAPABLE(rx_caps[2]))
+ *capable = true;
+
+ return 0;
+}
+
+static inline
+enum mei_hdcp_wired_protocol intel_dp_hdcp2_protocol(void)
+{
+ return MEI_HDCP_PROTOCOL_DP;
+}
+
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,
@@ -5582,6 +5914,12 @@ static const struct intel_hdcp_shim intel_dp_hdcp_shim = {
.toggle_signalling = intel_dp_hdcp_toggle_signalling,
.check_link = intel_dp_hdcp_check_link,
.hdcp_capable = intel_dp_hdcp_capable,
+ .write_2_2_msg = intel_dp_hdcp2_write_msg,
+ .read_2_2_msg = intel_dp_hdcp2_read_msg,
+ .config_stream_type = intel_dp_hdcp2_config_stream_type,
+ .check_2_2_link = intel_dp_hdcp2_check_link,
+ .hdcp_2_2_capable = intel_dp_hdcp2_capable,
+ .hdcp_protocol = intel_dp_hdcp2_protocol,
};

static void intel_edp_panel_vdd_sanitize(struct intel_dp *intel_dp)
diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h
index d5ab1ff9cc69..2b7181b76475 100644
--- a/drivers/gpu/drm/i915/intel_drv.h
+++ b/drivers/gpu/drm/i915/intel_drv.h
@@ -450,6 +450,13 @@ struct intel_hdcp {
u32 seq_num_m;

struct delayed_work hdcp2_check_work;
+
+ /*
+ * Work queue to signal the CP_IRQ. Used for the waiters to read the
+ * available information from HDCP DP sink.
+ */
+ wait_queue_head_t cp_irq_queue;
+ atomic_t cp_irq_recved;
};

struct intel_connector {
diff --git a/drivers/gpu/drm/i915/intel_hdcp.c b/drivers/gpu/drm/i915/intel_hdcp.c
index b9e5f73c640d..be475914c3cd 100644
--- a/drivers/gpu/drm/i915/intel_hdcp.c
+++ b/drivers/gpu/drm/i915/intel_hdcp.c
@@ -1899,6 +1899,9 @@ int intel_hdcp_init(struct intel_connector *connector,
if (hdcp2_supported)
intel_hdcp2_init(connector);

+ atomic_set(&hdcp->cp_irq_recved, 0);
+ init_waitqueue_head(&hdcp->cp_irq_queue);
+
return 0;
}

@@ -2036,4 +2039,7 @@ void intel_hdcp_handle_cp_irq(struct intel_connector *connector)
intel_hdcp_check_link(connector);
else if (intel_hdcp2_in_use(connector))
intel_hdcp2_check_link(connector);
+
+ atomic_set(&connector->hdcp.cp_irq_recved, 1);
+ wake_up_all(&connector->hdcp.cp_irq_queue);
}
--
2.7.4
Bloomfield, Jon
2018-11-27 16:54:15 UTC
Permalink
I'm not formally reviewing this series, but while glancing at it, I noticed....
-----Original Message-----
Ramalingam C
Sent: Tuesday, November 27, 2018 2:43 AM
Subject: [Intel-gfx] [PATCH v8 12/35] drm/i915: Implement the HDCP2.2
support for DP
Implements the DP adaptation specific HDCP2.2 functions.
These functions perform the DPCD read and write for communicating the
HDCP2.2 auth message back and forth.
wait for cp_irq is merged with this patch. Rebased.
wait_queue is used for wait for cp_irq [Chris Wilson]
Style fixed.
%s/PARING/PAIRING
Few style fixes [Uma]
Lookup table for DP HDCP2.2 msg details [Daniel].
Extra lines are removed.
Rebased.
Fixed some regression introduced at v5. [Ankit]
Macro HDCP_2_2_RX_CAPS_VERSION_VAL is reused [Uma]
Converted a function to inline [Uma]
%s/uintxx_t/uxx
Error due to the sinks are reported as DEBUG logs.
Adjust to the new mei interface.
---
drivers/gpu/drm/i915/intel_dp.c | 338
++++++++++++++++++++++++++++++++++++++
drivers/gpu/drm/i915/intel_drv.h | 7 +
drivers/gpu/drm/i915/intel_hdcp.c | 6 +
3 files changed, 351 insertions(+)
diff --git a/drivers/gpu/drm/i915/intel_dp.c
b/drivers/gpu/drm/i915/intel_dp.c
index ecc4706db7dc..1cc82e490999 100644
--- a/drivers/gpu/drm/i915/intel_dp.c
+++ b/drivers/gpu/drm/i915/intel_dp.c
@@ -31,6 +31,7 @@
#include <linux/types.h>
#include <linux/notifier.h>
#include <linux/reboot.h>
+#include <linux/mei_hdcp.h>
#include <asm/byteorder.h>
#include <drm/drmP.h>
#include <drm/drm_atomic_helper.h>
@@ -5347,6 +5348,27 @@ void intel_dp_encoder_suspend(struct
intel_encoder *intel_encoder)
pps_unlock(intel_dp);
}
<SNIP>
+
+static struct hdcp2_dp_msg_data *get_hdcp2_dp_msg_data(u8 msg_id)
+{
+ int i;
+
+ for (i = 0; i < sizeof(hdcp2_msg_data); i++)
Should be
sizeof(hdcp2_msg_data) / sizeof(*hdcp2_msg_data)
or equivalent.

Has the failure return been tested?
+ if (hdcp2_msg_data[i].msg_id == msg_id)
+ return &hdcp2_msg_data[i];
+
+ return NULL;
+}
+
<SNIP>
--
2.7.4
_______________________________________________
Intel-gfx mailing list
https://lists.freedesktop.org/mailman/listinfo/intel-gfx
Daniel Vetter
2018-11-27 17:37:12 UTC
Permalink
Post by Bloomfield, Jon
I'm not formally reviewing this series, but while glancing at it, I noticed....
-----Original Message-----
Ramalingam C
Sent: Tuesday, November 27, 2018 2:43 AM
Subject: [Intel-gfx] [PATCH v8 12/35] drm/i915: Implement the HDCP2.2
support for DP
Implements the DP adaptation specific HDCP2.2 functions.
These functions perform the DPCD read and write for communicating the
HDCP2.2 auth message back and forth.
wait for cp_irq is merged with this patch. Rebased.
wait_queue is used for wait for cp_irq [Chris Wilson]
Style fixed.
%s/PARING/PAIRING
Few style fixes [Uma]
Lookup table for DP HDCP2.2 msg details [Daniel].
Extra lines are removed.
Rebased.
Fixed some regression introduced at v5. [Ankit]
Macro HDCP_2_2_RX_CAPS_VERSION_VAL is reused [Uma]
Converted a function to inline [Uma]
%s/uintxx_t/uxx
Error due to the sinks are reported as DEBUG logs.
Adjust to the new mei interface.
---
drivers/gpu/drm/i915/intel_dp.c | 338
++++++++++++++++++++++++++++++++++++++
drivers/gpu/drm/i915/intel_drv.h | 7 +
drivers/gpu/drm/i915/intel_hdcp.c | 6 +
3 files changed, 351 insertions(+)
diff --git a/drivers/gpu/drm/i915/intel_dp.c
b/drivers/gpu/drm/i915/intel_dp.c
index ecc4706db7dc..1cc82e490999 100644
--- a/drivers/gpu/drm/i915/intel_dp.c
+++ b/drivers/gpu/drm/i915/intel_dp.c
@@ -31,6 +31,7 @@
#include <linux/types.h>
#include <linux/notifier.h>
#include <linux/reboot.h>
+#include <linux/mei_hdcp.h>
#include <asm/byteorder.h>
#include <drm/drmP.h>
#include <drm/drm_atomic_helper.h>
@@ -5347,6 +5348,27 @@ void intel_dp_encoder_suspend(struct
intel_encoder *intel_encoder)
pps_unlock(intel_dp);
}
<SNIP>
+
+static struct hdcp2_dp_msg_data *get_hdcp2_dp_msg_data(u8 msg_id)
+{
+ int i;
+
+ for (i = 0; i < sizeof(hdcp2_msg_data); i++)
Should be
sizeof(hdcp2_msg_data) / sizeof(*hdcp2_msg_data)
or equivalent.
ARRAY_SIZE is probably what you want here.
-Daniel
Post by Bloomfield, Jon
Has the failure return been tested?
+ if (hdcp2_msg_data[i].msg_id == msg_id)
+ return &hdcp2_msg_data[i];
+
+ return NULL;
+}
+
<SNIP>
--
2.7.4
_______________________________________________
Intel-gfx mailing list
https://lists.freedesktop.org/mailman/listinfo/intel-gfx
--
Daniel Vetter
Software Engineer, Intel Corporation
http://blog.ffwll.ch
C, Ramalingam
2018-11-28 05:15:00 UTC
Permalink
Post by Daniel Vetter
Post by Bloomfield, Jon
I'm not formally reviewing this series, but while glancing at it, I noticed....
-----Original Message-----
Ramalingam C
Sent: Tuesday, November 27, 2018 2:43 AM
Subject: [Intel-gfx] [PATCH v8 12/35] drm/i915: Implement the HDCP2.2
support for DP
Implements the DP adaptation specific HDCP2.2 functions.
These functions perform the DPCD read and write for communicating the
HDCP2.2 auth message back and forth.
wait for cp_irq is merged with this patch. Rebased.
wait_queue is used for wait for cp_irq [Chris Wilson]
Style fixed.
%s/PARING/PAIRING
Few style fixes [Uma]
Lookup table for DP HDCP2.2 msg details [Daniel].
Extra lines are removed.
Rebased.
Fixed some regression introduced at v5. [Ankit]
Macro HDCP_2_2_RX_CAPS_VERSION_VAL is reused [Uma]
Converted a function to inline [Uma]
%s/uintxx_t/uxx
Error due to the sinks are reported as DEBUG logs.
Adjust to the new mei interface.
---
drivers/gpu/drm/i915/intel_dp.c | 338
++++++++++++++++++++++++++++++++++++++
drivers/gpu/drm/i915/intel_drv.h | 7 +
drivers/gpu/drm/i915/intel_hdcp.c | 6 +
3 files changed, 351 insertions(+)
diff --git a/drivers/gpu/drm/i915/intel_dp.c
b/drivers/gpu/drm/i915/intel_dp.c
index ecc4706db7dc..1cc82e490999 100644
--- a/drivers/gpu/drm/i915/intel_dp.c
+++ b/drivers/gpu/drm/i915/intel_dp.c
@@ -31,6 +31,7 @@
#include <linux/types.h>
#include <linux/notifier.h>
#include <linux/reboot.h>
+#include <linux/mei_hdcp.h>
#include <asm/byteorder.h>
#include <drm/drmP.h>
#include <drm/drm_atomic_helper.h>
@@ -5347,6 +5348,27 @@ void intel_dp_encoder_suspend(struct
intel_encoder *intel_encoder)
pps_unlock(intel_dp);
}
<SNIP>
+
+static struct hdcp2_dp_msg_data *get_hdcp2_dp_msg_data(u8 msg_id)
+{
+ int i;
+
+ for (i = 0; i < sizeof(hdcp2_msg_data); i++)
Should be
sizeof(hdcp2_msg_data) / sizeof(*hdcp2_msg_data)
or equivalent.
ARRAY_SIZE is probably what you want here.
yes. missed it as authentication flow always provides the valid msg_id.
Will fix it.

--Ram
Post by Daniel Vetter
-Daniel
Post by Bloomfield, Jon
Has the failure return been tested?
+ if (hdcp2_msg_data[i].msg_id == msg_id)
+ return &hdcp2_msg_data[i];
+
+ return NULL;
+}
+
<SNIP>
--
2.7.4
_______________________________________________
Intel-gfx mailing list
https://lists.freedesktop.org/mailman/listinfo/intel-gfx
Stéphane Marchesin
2018-11-28 05:26:29 UTC
Permalink
Hi,

Just a drive-by comment, but did you check that this fails gracefully
on platforms which don't enable the ME? For example Chrome OS :)

Stéphane
Post by Ramalingam C
Implements the DP adaptation specific HDCP2.2 functions.
These functions perform the DPCD read and write for communicating the
HDCP2.2 auth message back and forth.
wait for cp_irq is merged with this patch. Rebased.
wait_queue is used for wait for cp_irq [Chris Wilson]
Style fixed.
%s/PARING/PAIRING
Few style fixes [Uma]
Lookup table for DP HDCP2.2 msg details [Daniel].
Extra lines are removed.
Rebased.
Fixed some regression introduced at v5. [Ankit]
Macro HDCP_2_2_RX_CAPS_VERSION_VAL is reused [Uma]
Converted a function to inline [Uma]
%s/uintxx_t/uxx
Error due to the sinks are reported as DEBUG logs.
Adjust to the new mei interface.
---
drivers/gpu/drm/i915/intel_dp.c | 338 ++++++++++++++++++++++++++++++++++++++
drivers/gpu/drm/i915/intel_drv.h | 7 +
drivers/gpu/drm/i915/intel_hdcp.c | 6 +
3 files changed, 351 insertions(+)
diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c
index ecc4706db7dc..1cc82e490999 100644
--- a/drivers/gpu/drm/i915/intel_dp.c
+++ b/drivers/gpu/drm/i915/intel_dp.c
@@ -31,6 +31,7 @@
#include <linux/types.h>
#include <linux/notifier.h>
#include <linux/reboot.h>
+#include <linux/mei_hdcp.h>
#include <asm/byteorder.h>
#include <drm/drmP.h>
#include <drm/drm_atomic_helper.h>
@@ -5347,6 +5348,27 @@ void intel_dp_encoder_suspend(struct intel_encoder *intel_encoder)
pps_unlock(intel_dp);
}
+static int intel_dp_hdcp_wait_for_cp_irq(struct intel_hdcp *hdcp,
+ int timeout)
+{
+ long ret;
+
+ /* Reinit */
+ atomic_set(&hdcp->cp_irq_recved, 0);
+
+#define C (atomic_read(&hdcp->cp_irq_recved) > 0)
+ ret = wait_event_interruptible_timeout(hdcp->cp_irq_queue, C,
+ msecs_to_jiffies(timeout));
+
+ if (ret > 0) {
+ atomic_set(&hdcp->cp_irq_recved, 0);
+ return 0;
+ } else if (!ret) {
+ return -ETIMEDOUT;
+ }
+ return (int)ret;
+}
+
static
int intel_dp_hdcp_write_an_aksv(struct intel_digital_port *intel_dig_port,
u8 *an)
@@ -5570,6 +5592,316 @@ int intel_dp_hdcp_capable(struct intel_digital_port *intel_dig_port,
return 0;
}
+static struct hdcp2_dp_msg_data {
+ u8 msg_id;
+ u32 offset;
+ bool msg_detectable;
+ u32 timeout;
+ u32 timeout2; /* Added for non_paired situation */
+ } hdcp2_msg_data[] = {
+ {HDCP_2_2_AKE_INIT, DP_HDCP_2_2_AKE_INIT_OFFSET, false, 0, 0},
+ {HDCP_2_2_AKE_SEND_CERT, DP_HDCP_2_2_AKE_SEND_CERT_OFFSET,
+ false, HDCP_2_2_CERT_TIMEOUT_MS, 0},
+ {HDCP_2_2_AKE_NO_STORED_KM, DP_HDCP_2_2_AKE_NO_STORED_KM_OFFSET,
+ false, 0, 0},
+ {HDCP_2_2_AKE_STORED_KM, DP_HDCP_2_2_AKE_STORED_KM_OFFSET,
+ false, 0, 0},
+ {HDCP_2_2_AKE_SEND_HPRIME, DP_HDCP_2_2_AKE_SEND_HPRIME_OFFSET,
+ true, HDCP_2_2_HPRIME_PAIRED_TIMEOUT_MS,
+ HDCP_2_2_HPRIME_NO_PAIRED_TIMEOUT_MS},
+ {HDCP_2_2_AKE_SEND_PAIRING_INFO,
+ DP_HDCP_2_2_AKE_SEND_PAIRING_INFO_OFFSET, true,
+ HDCP_2_2_PAIRING_TIMEOUT_MS, 0},
+ {HDCP_2_2_LC_INIT, DP_HDCP_2_2_LC_INIT_OFFSET, false, 0, 0},
+ {HDCP_2_2_LC_SEND_LPRIME, DP_HDCP_2_2_LC_SEND_LPRIME_OFFSET,
+ false, HDCP_2_2_DP_LPRIME_TIMEOUT_MS, 0},
+ {HDCP_2_2_SKE_SEND_EKS, DP_HDCP_2_2_SKE_SEND_EKS_OFFSET, false,
+ 0, 0},
+ {HDCP_2_2_REP_SEND_RECVID_LIST,
+ DP_HDCP_2_2_REP_SEND_RECVID_LIST_OFFSET, true,
+ HDCP_2_2_RECVID_LIST_TIMEOUT_MS, 0},
+ {HDCP_2_2_REP_SEND_ACK, DP_HDCP_2_2_REP_SEND_ACK_OFFSET, false,
+ 0, 0},
+ {HDCP_2_2_REP_STREAM_MANAGE,
+ DP_HDCP_2_2_REP_STREAM_MANAGE_OFFSET, false,
+ 0, 0},
+ {HDCP_2_2_REP_STREAM_READY, DP_HDCP_2_2_REP_STREAM_READY_OFFSET,
+ false, HDCP_2_2_STREAM_READY_TIMEOUT_MS, 0},
+ {HDCP_2_2_ERRATA_DP_STREAM_TYPE,
+ DP_HDCP_2_2_REG_STREAM_TYPE_OFFSET, false,
+ 0, 0},
+ };
+
+static inline
+int intel_dp_hdcp2_read_rx_status(struct intel_digital_port *intel_dig_port,
+ u8 *rx_status)
+{
+ ssize_t ret;
+
+ ret = drm_dp_dpcd_read(&intel_dig_port->dp.aux,
+ DP_HDCP_2_2_REG_RXSTATUS_OFFSET, rx_status,
+ HDCP_2_2_DP_RXSTATUS_LEN);
+ if (ret != HDCP_2_2_DP_RXSTATUS_LEN) {
+ DRM_DEBUG_KMS("Read bstatus from DP/AUX failed (%zd)\n", ret);
+ return ret >= 0 ? -EIO : ret;
+ }
+
+ return 0;
+}
+
+static
+int hdcp2_detect_msg_availability(struct intel_digital_port *intel_dig_port,
+ u8 msg_id, bool *msg_ready)
+{
+ u8 rx_status;
+ int ret;
+
+ *msg_ready = false;
+ ret = intel_dp_hdcp2_read_rx_status(intel_dig_port, &rx_status);
+ if (ret < 0)
+ return ret;
+
+ switch (msg_id) {
+ if (HDCP_2_2_DP_RXSTATUS_H_PRIME(rx_status))
+ *msg_ready = true;
+ break;
+ if (HDCP_2_2_DP_RXSTATUS_PAIRING(rx_status))
+ *msg_ready = true;
+ break;
+ if (HDCP_2_2_DP_RXSTATUS_READY(rx_status))
+ *msg_ready = true;
+ break;
+ DRM_ERROR("Unidentified msg_id: %d\n", msg_id);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static ssize_t
+intel_dp_hdcp2_wait_for_msg(struct intel_digital_port *intel_dig_port,
+ struct hdcp2_dp_msg_data *hdcp2_msg_data)
+{
+ struct intel_dp *dp = &intel_dig_port->dp;
+ struct intel_hdcp *hdcp = &dp->attached_connector->hdcp;
+ int ret, timeout;
+ bool msg_ready = false;
+
+ if (hdcp2_msg_data->msg_id == HDCP_2_2_AKE_SEND_HPRIME &&
+ !hdcp->is_paired)
+ timeout = hdcp2_msg_data->timeout2;
+ else
+ timeout = hdcp2_msg_data->timeout;
+
+ /*
+ * There is no way to detect the CERT, LPRIME and STREAM_READY
+ * availability. So Wait for timeout and read the msg.
+ */
+ if (!hdcp2_msg_data->msg_detectable) {
+ mdelay(timeout);
+ ret = 0;
+ } else {
+ /*
+ * Ignoring the return of the intel_dp_hdcp_wait_for_cp_irq,
+ * Just to detect the msg availability before failing it.
+ */
+ intel_dp_hdcp_wait_for_cp_irq(hdcp, timeout);
+ ret = hdcp2_detect_msg_availability(intel_dig_port,
+ hdcp2_msg_data->msg_id,
+ &msg_ready);
+ if (!msg_ready)
+ ret = -ETIMEDOUT;
+ }
+
+ if (ret)
+ DRM_DEBUG_KMS("msg_id %d, ret %d, timeout(mSec): %d\n",
+ hdcp2_msg_data->msg_id, ret, timeout);
+
+ return ret;
+}
+
+static struct hdcp2_dp_msg_data *get_hdcp2_dp_msg_data(u8 msg_id)
+{
+ int i;
+
+ for (i = 0; i < sizeof(hdcp2_msg_data); i++)
+ if (hdcp2_msg_data[i].msg_id == msg_id)
+ return &hdcp2_msg_data[i];
+
+ return NULL;
+}
+
+static
+int intel_dp_hdcp2_write_msg(struct intel_digital_port *intel_dig_port,
+ void *buf, size_t size)
+{
+ unsigned int offset;
+ u8 *byte = buf;
+ ssize_t ret, bytes_to_write, len;
+ struct hdcp2_dp_msg_data *hdcp2_msg_data;
+
+ hdcp2_msg_data = get_hdcp2_dp_msg_data(*byte);
+ if (!hdcp2_msg_data)
+ return -EINVAL;
+
+ offset = hdcp2_msg_data->offset;
+
+ /* No msg_id in DP HDCP2.2 msgs */
+ bytes_to_write = size - 1;
+ byte++;
+
+ while (bytes_to_write) {
+ len = bytes_to_write > DP_AUX_MAX_PAYLOAD_BYTES ?
+ DP_AUX_MAX_PAYLOAD_BYTES : bytes_to_write;
+
+ ret = drm_dp_dpcd_write(&intel_dig_port->dp.aux,
+ offset, (void *)byte, len);
+ if (ret < 0)
+ return ret;
+
+ bytes_to_write -= ret;
+ byte += ret;
+ offset += ret;
+ }
+
+ return size;
+}
+
+static
+ssize_t get_receiver_id_list_size(struct intel_digital_port *intel_dig_port)
+{
+ u8 rx_info[HDCP_2_2_RXINFO_LEN];
+ u32 dev_cnt;
+ ssize_t ret;
+
+ ret = drm_dp_dpcd_read(&intel_dig_port->dp.aux,
+ DP_HDCP_2_2_REG_RXINFO_OFFSET,
+ (void *)rx_info, HDCP_2_2_RXINFO_LEN);
+ if (ret != HDCP_2_2_RXINFO_LEN)
+ return ret >= 0 ? -EIO : ret;
+
+ dev_cnt = (HDCP_2_2_DEV_COUNT_HI(rx_info[0]) << 4 |
+ HDCP_2_2_DEV_COUNT_LO(rx_info[1]));
+
+ if (dev_cnt > HDCP_2_2_MAX_DEVICE_COUNT)
+ dev_cnt = HDCP_2_2_MAX_DEVICE_COUNT;
+
+ ret = sizeof(struct hdcp2_rep_send_receiverid_list) -
+ HDCP_2_2_RECEIVER_IDS_MAX_LEN +
+ (dev_cnt * HDCP_2_2_RECEIVER_ID_LEN);
+
+ return ret;
+}
+
+static
+int intel_dp_hdcp2_read_msg(struct intel_digital_port *intel_dig_port,
+ u8 msg_id, void *buf, size_t size)
+{
+ unsigned int offset;
+ u8 *byte = buf;
+ ssize_t ret, bytes_to_recv, len;
+ struct hdcp2_dp_msg_data *hdcp2_msg_data;
+
+ hdcp2_msg_data = get_hdcp2_dp_msg_data(msg_id);
+ if (!hdcp2_msg_data)
+ return -EINVAL;
+ offset = hdcp2_msg_data->offset;
+
+ ret = intel_dp_hdcp2_wait_for_msg(intel_dig_port, hdcp2_msg_data);
+ if (ret < 0)
+ return ret;
+
+ if (msg_id == HDCP_2_2_REP_SEND_RECVID_LIST) {
+ ret = get_receiver_id_list_size(intel_dig_port);
+ if (ret < 0)
+ return ret;
+
+ size = ret;
+ }
+ bytes_to_recv = size - 1;
+
+ /* DP adaptation msgs has no msg_id */
+ byte++;
+
+ while (bytes_to_recv) {
+ len = bytes_to_recv > DP_AUX_MAX_PAYLOAD_BYTES ?
+ DP_AUX_MAX_PAYLOAD_BYTES : bytes_to_recv;
+
+ ret = drm_dp_dpcd_read(&intel_dig_port->dp.aux, offset,
+ (void *)byte, len);
+ if (ret < 0) {
+ DRM_DEBUG_KMS("msg_id %d, ret %zd\n", msg_id, ret);
+ return ret;
+ }
+
+ bytes_to_recv -= ret;
+ byte += ret;
+ offset += ret;
+ }
+ byte = buf;
+ *byte = msg_id;
+
+ return size;
+}
+
+static
+int intel_dp_hdcp2_config_stream_type(struct intel_digital_port *intel_dig_port,
+ void *buf, size_t size)
+{
+ return intel_dp_hdcp2_write_msg(intel_dig_port, buf, size);
+}
+
+static
+int intel_dp_hdcp2_check_link(struct intel_digital_port *intel_dig_port)
+{
+ u8 rx_status;
+ int ret;
+
+ ret = intel_dp_hdcp2_read_rx_status(intel_dig_port, &rx_status);
+ if (ret)
+ return ret;
+
+ if (HDCP_2_2_DP_RXSTATUS_REAUTH_REQ(rx_status))
+ ret = DRM_HDCP_REAUTH_REQUEST;
+ else if (HDCP_2_2_DP_RXSTATUS_LINK_FAILED(rx_status))
+ ret = DRM_HDCP_LINK_INTEGRITY_FAILURE;
+ else if (HDCP_2_2_DP_RXSTATUS_READY(rx_status))
+ ret = DRM_HDCP_TOPOLOGY_CHANGE;
+
+ return ret;
+}
+
+static
+int intel_dp_hdcp2_capable(struct intel_digital_port *intel_dig_port,
+ bool *capable)
+{
+ u8 rx_caps[3];
+ int ret;
+
+ *capable = false;
+ ret = drm_dp_dpcd_read(&intel_dig_port->dp.aux,
+ DP_HDCP_2_2_REG_RX_CAPS_OFFSET,
+ rx_caps, HDCP_2_2_RXCAPS_LEN);
+ if (ret != HDCP_2_2_RXCAPS_LEN)
+ return ret >= 0 ? -EIO : ret;
+
+ if (rx_caps[0] == HDCP_2_2_RX_CAPS_VERSION_VAL &&
+ HDCP_2_2_DP_HDCP_CAPABLE(rx_caps[2]))
+ *capable = true;
+
+ return 0;
+}
+
+static inline
+enum mei_hdcp_wired_protocol intel_dp_hdcp2_protocol(void)
+{
+ return MEI_HDCP_PROTOCOL_DP;
+}
+
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,
@@ -5582,6 +5914,12 @@ static const struct intel_hdcp_shim intel_dp_hdcp_shim = {
.toggle_signalling = intel_dp_hdcp_toggle_signalling,
.check_link = intel_dp_hdcp_check_link,
.hdcp_capable = intel_dp_hdcp_capable,
+ .write_2_2_msg = intel_dp_hdcp2_write_msg,
+ .read_2_2_msg = intel_dp_hdcp2_read_msg,
+ .config_stream_type = intel_dp_hdcp2_config_stream_type,
+ .check_2_2_link = intel_dp_hdcp2_check_link,
+ .hdcp_2_2_capable = intel_dp_hdcp2_capable,
+ .hdcp_protocol = intel_dp_hdcp2_protocol,
};
static void intel_edp_panel_vdd_sanitize(struct intel_dp *intel_dp)
diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h
index d5ab1ff9cc69..2b7181b76475 100644
--- a/drivers/gpu/drm/i915/intel_drv.h
+++ b/drivers/gpu/drm/i915/intel_drv.h
@@ -450,6 +450,13 @@ struct intel_hdcp {
u32 seq_num_m;
struct delayed_work hdcp2_check_work;
+
+ /*
+ * Work queue to signal the CP_IRQ. Used for the waiters to read the
+ * available information from HDCP DP sink.
+ */
+ wait_queue_head_t cp_irq_queue;
+ atomic_t cp_irq_recved;
};
struct intel_connector {
diff --git a/drivers/gpu/drm/i915/intel_hdcp.c b/drivers/gpu/drm/i915/intel_hdcp.c
index b9e5f73c640d..be475914c3cd 100644
--- a/drivers/gpu/drm/i915/intel_hdcp.c
+++ b/drivers/gpu/drm/i915/intel_hdcp.c
@@ -1899,6 +1899,9 @@ int intel_hdcp_init(struct intel_connector *connector,
if (hdcp2_supported)
intel_hdcp2_init(connector);
+ atomic_set(&hdcp->cp_irq_recved, 0);
+ init_waitqueue_head(&hdcp->cp_irq_queue);
+
return 0;
}
@@ -2036,4 +2039,7 @@ void intel_hdcp_handle_cp_irq(struct intel_connector *connector)
intel_hdcp_check_link(connector);
else if (intel_hdcp2_in_use(connector))
intel_hdcp2_check_link(connector);
+
+ atomic_set(&connector->hdcp.cp_irq_recved, 1);
+ wake_up_all(&connector->hdcp.cp_irq_queue);
}
--
2.7.4
_______________________________________________
dri-devel mailing list
https://lists.freedesktop.org/mailman/listinfo/dri-devel
C, Ramalingam
2018-11-28 07:24:47 UTC
Permalink
Post by Stéphane Marchesin
Hi,
Just a drive-by comment, but did you check that this fails gracefully
on platforms which don't enable the ME? For example Chrome OS :)
That is taken care :) HDCP2.2 is attempted only if platform enables the
ME and its required kernel drivers.

--Ram
Post by Stéphane Marchesin
Stéphane
Post by Ramalingam C
Implements the DP adaptation specific HDCP2.2 functions.
These functions perform the DPCD read and write for communicating the
HDCP2.2 auth message back and forth.
wait for cp_irq is merged with this patch. Rebased.
wait_queue is used for wait for cp_irq [Chris Wilson]
Style fixed.
%s/PARING/PAIRING
Few style fixes [Uma]
Lookup table for DP HDCP2.2 msg details [Daniel].
Extra lines are removed.
Rebased.
Fixed some regression introduced at v5. [Ankit]
Macro HDCP_2_2_RX_CAPS_VERSION_VAL is reused [Uma]
Converted a function to inline [Uma]
%s/uintxx_t/uxx
Error due to the sinks are reported as DEBUG logs.
Adjust to the new mei interface.
---
drivers/gpu/drm/i915/intel_dp.c | 338 ++++++++++++++++++++++++++++++++++++++
drivers/gpu/drm/i915/intel_drv.h | 7 +
drivers/gpu/drm/i915/intel_hdcp.c | 6 +
3 files changed, 351 insertions(+)
diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c
index ecc4706db7dc..1cc82e490999 100644
--- a/drivers/gpu/drm/i915/intel_dp.c
+++ b/drivers/gpu/drm/i915/intel_dp.c
@@ -31,6 +31,7 @@
#include <linux/types.h>
#include <linux/notifier.h>
#include <linux/reboot.h>
+#include <linux/mei_hdcp.h>
#include <asm/byteorder.h>
#include <drm/drmP.h>
#include <drm/drm_atomic_helper.h>
@@ -5347,6 +5348,27 @@ void intel_dp_encoder_suspend(struct intel_encoder *intel_encoder)
pps_unlock(intel_dp);
}
+static int intel_dp_hdcp_wait_for_cp_irq(struct intel_hdcp *hdcp,
+ int timeout)
+{
+ long ret;
+
+ /* Reinit */
+ atomic_set(&hdcp->cp_irq_recved, 0);
+
+#define C (atomic_read(&hdcp->cp_irq_recved) > 0)
+ ret = wait_event_interruptible_timeout(hdcp->cp_irq_queue, C,
+ msecs_to_jiffies(timeout));
+
+ if (ret > 0) {
+ atomic_set(&hdcp->cp_irq_recved, 0);
+ return 0;
+ } else if (!ret) {
+ return -ETIMEDOUT;
+ }
+ return (int)ret;
+}
+
static
int intel_dp_hdcp_write_an_aksv(struct intel_digital_port *intel_dig_port,
u8 *an)
@@ -5570,6 +5592,316 @@ int intel_dp_hdcp_capable(struct intel_digital_port *intel_dig_port,
return 0;
}
+static struct hdcp2_dp_msg_data {
+ u8 msg_id;
+ u32 offset;
+ bool msg_detectable;
+ u32 timeout;
+ u32 timeout2; /* Added for non_paired situation */
+ } hdcp2_msg_data[] = {
+ {HDCP_2_2_AKE_INIT, DP_HDCP_2_2_AKE_INIT_OFFSET, false, 0, 0},
+ {HDCP_2_2_AKE_SEND_CERT, DP_HDCP_2_2_AKE_SEND_CERT_OFFSET,
+ false, HDCP_2_2_CERT_TIMEOUT_MS, 0},
+ {HDCP_2_2_AKE_NO_STORED_KM, DP_HDCP_2_2_AKE_NO_STORED_KM_OFFSET,
+ false, 0, 0},
+ {HDCP_2_2_AKE_STORED_KM, DP_HDCP_2_2_AKE_STORED_KM_OFFSET,
+ false, 0, 0},
+ {HDCP_2_2_AKE_SEND_HPRIME, DP_HDCP_2_2_AKE_SEND_HPRIME_OFFSET,
+ true, HDCP_2_2_HPRIME_PAIRED_TIMEOUT_MS,
+ HDCP_2_2_HPRIME_NO_PAIRED_TIMEOUT_MS},
+ {HDCP_2_2_AKE_SEND_PAIRING_INFO,
+ DP_HDCP_2_2_AKE_SEND_PAIRING_INFO_OFFSET, true,
+ HDCP_2_2_PAIRING_TIMEOUT_MS, 0},
+ {HDCP_2_2_LC_INIT, DP_HDCP_2_2_LC_INIT_OFFSET, false, 0, 0},
+ {HDCP_2_2_LC_SEND_LPRIME, DP_HDCP_2_2_LC_SEND_LPRIME_OFFSET,
+ false, HDCP_2_2_DP_LPRIME_TIMEOUT_MS, 0},
+ {HDCP_2_2_SKE_SEND_EKS, DP_HDCP_2_2_SKE_SEND_EKS_OFFSET, false,
+ 0, 0},
+ {HDCP_2_2_REP_SEND_RECVID_LIST,
+ DP_HDCP_2_2_REP_SEND_RECVID_LIST_OFFSET, true,
+ HDCP_2_2_RECVID_LIST_TIMEOUT_MS, 0},
+ {HDCP_2_2_REP_SEND_ACK, DP_HDCP_2_2_REP_SEND_ACK_OFFSET, false,
+ 0, 0},
+ {HDCP_2_2_REP_STREAM_MANAGE,
+ DP_HDCP_2_2_REP_STREAM_MANAGE_OFFSET, false,
+ 0, 0},
+ {HDCP_2_2_REP_STREAM_READY, DP_HDCP_2_2_REP_STREAM_READY_OFFSET,
+ false, HDCP_2_2_STREAM_READY_TIMEOUT_MS, 0},
+ {HDCP_2_2_ERRATA_DP_STREAM_TYPE,
+ DP_HDCP_2_2_REG_STREAM_TYPE_OFFSET, false,
+ 0, 0},
+ };
+
+static inline
+int intel_dp_hdcp2_read_rx_status(struct intel_digital_port *intel_dig_port,
+ u8 *rx_status)
+{
+ ssize_t ret;
+
+ ret = drm_dp_dpcd_read(&intel_dig_port->dp.aux,
+ DP_HDCP_2_2_REG_RXSTATUS_OFFSET, rx_status,
+ HDCP_2_2_DP_RXSTATUS_LEN);
+ if (ret != HDCP_2_2_DP_RXSTATUS_LEN) {
+ DRM_DEBUG_KMS("Read bstatus from DP/AUX failed (%zd)\n", ret);
+ return ret >= 0 ? -EIO : ret;
+ }
+
+ return 0;
+}
+
+static
+int hdcp2_detect_msg_availability(struct intel_digital_port *intel_dig_port,
+ u8 msg_id, bool *msg_ready)
+{
+ u8 rx_status;
+ int ret;
+
+ *msg_ready = false;
+ ret = intel_dp_hdcp2_read_rx_status(intel_dig_port, &rx_status);
+ if (ret < 0)
+ return ret;
+
+ switch (msg_id) {
+ if (HDCP_2_2_DP_RXSTATUS_H_PRIME(rx_status))
+ *msg_ready = true;
+ break;
+ if (HDCP_2_2_DP_RXSTATUS_PAIRING(rx_status))
+ *msg_ready = true;
+ break;
+ if (HDCP_2_2_DP_RXSTATUS_READY(rx_status))
+ *msg_ready = true;
+ break;
+ DRM_ERROR("Unidentified msg_id: %d\n", msg_id);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static ssize_t
+intel_dp_hdcp2_wait_for_msg(struct intel_digital_port *intel_dig_port,
+ struct hdcp2_dp_msg_data *hdcp2_msg_data)
+{
+ struct intel_dp *dp = &intel_dig_port->dp;
+ struct intel_hdcp *hdcp = &dp->attached_connector->hdcp;
+ int ret, timeout;
+ bool msg_ready = false;
+
+ if (hdcp2_msg_data->msg_id == HDCP_2_2_AKE_SEND_HPRIME &&
+ !hdcp->is_paired)
+ timeout = hdcp2_msg_data->timeout2;
+ else
+ timeout = hdcp2_msg_data->timeout;
+
+ /*
+ * There is no way to detect the CERT, LPRIME and STREAM_READY
+ * availability. So Wait for timeout and read the msg.
+ */
+ if (!hdcp2_msg_data->msg_detectable) {
+ mdelay(timeout);
+ ret = 0;
+ } else {
+ /*
+ * Ignoring the return of the intel_dp_hdcp_wait_for_cp_irq,
+ * Just to detect the msg availability before failing it.
+ */
+ intel_dp_hdcp_wait_for_cp_irq(hdcp, timeout);
+ ret = hdcp2_detect_msg_availability(intel_dig_port,
+ hdcp2_msg_data->msg_id,
+ &msg_ready);
+ if (!msg_ready)
+ ret = -ETIMEDOUT;
+ }
+
+ if (ret)
+ DRM_DEBUG_KMS("msg_id %d, ret %d, timeout(mSec): %d\n",
+ hdcp2_msg_data->msg_id, ret, timeout);
+
+ return ret;
+}
+
+static struct hdcp2_dp_msg_data *get_hdcp2_dp_msg_data(u8 msg_id)
+{
+ int i;
+
+ for (i = 0; i < sizeof(hdcp2_msg_data); i++)
+ if (hdcp2_msg_data[i].msg_id == msg_id)
+ return &hdcp2_msg_data[i];
+
+ return NULL;
+}
+
+static
+int intel_dp_hdcp2_write_msg(struct intel_digital_port *intel_dig_port,
+ void *buf, size_t size)
+{
+ unsigned int offset;
+ u8 *byte = buf;
+ ssize_t ret, bytes_to_write, len;
+ struct hdcp2_dp_msg_data *hdcp2_msg_data;
+
+ hdcp2_msg_data = get_hdcp2_dp_msg_data(*byte);
+ if (!hdcp2_msg_data)
+ return -EINVAL;
+
+ offset = hdcp2_msg_data->offset;
+
+ /* No msg_id in DP HDCP2.2 msgs */
+ bytes_to_write = size - 1;
+ byte++;
+
+ while (bytes_to_write) {
+ len = bytes_to_write > DP_AUX_MAX_PAYLOAD_BYTES ?
+ DP_AUX_MAX_PAYLOAD_BYTES : bytes_to_write;
+
+ ret = drm_dp_dpcd_write(&intel_dig_port->dp.aux,
+ offset, (void *)byte, len);
+ if (ret < 0)
+ return ret;
+
+ bytes_to_write -= ret;
+ byte += ret;
+ offset += ret;
+ }
+
+ return size;
+}
+
+static
+ssize_t get_receiver_id_list_size(struct intel_digital_port *intel_dig_port)
+{
+ u8 rx_info[HDCP_2_2_RXINFO_LEN];
+ u32 dev_cnt;
+ ssize_t ret;
+
+ ret = drm_dp_dpcd_read(&intel_dig_port->dp.aux,
+ DP_HDCP_2_2_REG_RXINFO_OFFSET,
+ (void *)rx_info, HDCP_2_2_RXINFO_LEN);
+ if (ret != HDCP_2_2_RXINFO_LEN)
+ return ret >= 0 ? -EIO : ret;
+
+ dev_cnt = (HDCP_2_2_DEV_COUNT_HI(rx_info[0]) << 4 |
+ HDCP_2_2_DEV_COUNT_LO(rx_info[1]));
+
+ if (dev_cnt > HDCP_2_2_MAX_DEVICE_COUNT)
+ dev_cnt = HDCP_2_2_MAX_DEVICE_COUNT;
+
+ ret = sizeof(struct hdcp2_rep_send_receiverid_list) -
+ HDCP_2_2_RECEIVER_IDS_MAX_LEN +
+ (dev_cnt * HDCP_2_2_RECEIVER_ID_LEN);
+
+ return ret;
+}
+
+static
+int intel_dp_hdcp2_read_msg(struct intel_digital_port *intel_dig_port,
+ u8 msg_id, void *buf, size_t size)
+{
+ unsigned int offset;
+ u8 *byte = buf;
+ ssize_t ret, bytes_to_recv, len;
+ struct hdcp2_dp_msg_data *hdcp2_msg_data;
+
+ hdcp2_msg_data = get_hdcp2_dp_msg_data(msg_id);
+ if (!hdcp2_msg_data)
+ return -EINVAL;
+ offset = hdcp2_msg_data->offset;
+
+ ret = intel_dp_hdcp2_wait_for_msg(intel_dig_port, hdcp2_msg_data);
+ if (ret < 0)
+ return ret;
+
+ if (msg_id == HDCP_2_2_REP_SEND_RECVID_LIST) {
+ ret = get_receiver_id_list_size(intel_dig_port);
+ if (ret < 0)
+ return ret;
+
+ size = ret;
+ }
+ bytes_to_recv = size - 1;
+
+ /* DP adaptation msgs has no msg_id */
+ byte++;
+
+ while (bytes_to_recv) {
+ len = bytes_to_recv > DP_AUX_MAX_PAYLOAD_BYTES ?
+ DP_AUX_MAX_PAYLOAD_BYTES : bytes_to_recv;
+
+ ret = drm_dp_dpcd_read(&intel_dig_port->dp.aux, offset,
+ (void *)byte, len);
+ if (ret < 0) {
+ DRM_DEBUG_KMS("msg_id %d, ret %zd\n", msg_id, ret);
+ return ret;
+ }
+
+ bytes_to_recv -= ret;
+ byte += ret;
+ offset += ret;
+ }
+ byte = buf;
+ *byte = msg_id;
+
+ return size;
+}
+
+static
+int intel_dp_hdcp2_config_stream_type(struct intel_digital_port *intel_dig_port,
+ void *buf, size_t size)
+{
+ return intel_dp_hdcp2_write_msg(intel_dig_port, buf, size);
+}
+
+static
+int intel_dp_hdcp2_check_link(struct intel_digital_port *intel_dig_port)
+{
+ u8 rx_status;
+ int ret;
+
+ ret = intel_dp_hdcp2_read_rx_status(intel_dig_port, &rx_status);
+ if (ret)
+ return ret;
+
+ if (HDCP_2_2_DP_RXSTATUS_REAUTH_REQ(rx_status))
+ ret = DRM_HDCP_REAUTH_REQUEST;
+ else if (HDCP_2_2_DP_RXSTATUS_LINK_FAILED(rx_status))
+ ret = DRM_HDCP_LINK_INTEGRITY_FAILURE;
+ else if (HDCP_2_2_DP_RXSTATUS_READY(rx_status))
+ ret = DRM_HDCP_TOPOLOGY_CHANGE;
+
+ return ret;
+}
+
+static
+int intel_dp_hdcp2_capable(struct intel_digital_port *intel_dig_port,
+ bool *capable)
+{
+ u8 rx_caps[3];
+ int ret;
+
+ *capable = false;
+ ret = drm_dp_dpcd_read(&intel_dig_port->dp.aux,
+ DP_HDCP_2_2_REG_RX_CAPS_OFFSET,
+ rx_caps, HDCP_2_2_RXCAPS_LEN);
+ if (ret != HDCP_2_2_RXCAPS_LEN)
+ return ret >= 0 ? -EIO : ret;
+
+ if (rx_caps[0] == HDCP_2_2_RX_CAPS_VERSION_VAL &&
+ HDCP_2_2_DP_HDCP_CAPABLE(rx_caps[2]))
+ *capable = true;
+
+ return 0;
+}
+
+static inline
+enum mei_hdcp_wired_protocol intel_dp_hdcp2_protocol(void)
+{
+ return MEI_HDCP_PROTOCOL_DP;
+}
+
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,
@@ -5582,6 +5914,12 @@ static const struct intel_hdcp_shim intel_dp_hdcp_shim = {
.toggle_signalling = intel_dp_hdcp_toggle_signalling,
.check_link = intel_dp_hdcp_check_link,
.hdcp_capable = intel_dp_hdcp_capable,
+ .write_2_2_msg = intel_dp_hdcp2_write_msg,
+ .read_2_2_msg = intel_dp_hdcp2_read_msg,
+ .config_stream_type = intel_dp_hdcp2_config_stream_type,
+ .check_2_2_link = intel_dp_hdcp2_check_link,
+ .hdcp_2_2_capable = intel_dp_hdcp2_capable,
+ .hdcp_protocol = intel_dp_hdcp2_protocol,
};
static void intel_edp_panel_vdd_sanitize(struct intel_dp *intel_dp)
diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h
index d5ab1ff9cc69..2b7181b76475 100644
--- a/drivers/gpu/drm/i915/intel_drv.h
+++ b/drivers/gpu/drm/i915/intel_drv.h
@@ -450,6 +450,13 @@ struct intel_hdcp {
u32 seq_num_m;
struct delayed_work hdcp2_check_work;
+
+ /*
+ * Work queue to signal the CP_IRQ. Used for the waiters to read the
+ * available information from HDCP DP sink.
+ */
+ wait_queue_head_t cp_irq_queue;
+ atomic_t cp_irq_recved;
};
struct intel_connector {
diff --git a/drivers/gpu/drm/i915/intel_hdcp.c b/drivers/gpu/drm/i915/intel_hdcp.c
index b9e5f73c640d..be475914c3cd 100644
--- a/drivers/gpu/drm/i915/intel_hdcp.c
+++ b/drivers/gpu/drm/i915/intel_hdcp.c
@@ -1899,6 +1899,9 @@ int intel_hdcp_init(struct intel_connector *connector,
if (hdcp2_supported)
intel_hdcp2_init(connector);
+ atomic_set(&hdcp->cp_irq_recved, 0);
+ init_waitqueue_head(&hdcp->cp_irq_queue);
+
return 0;
}
@@ -2036,4 +2039,7 @@ void intel_hdcp_handle_cp_irq(struct intel_connector *connector)
intel_hdcp_check_link(connector);
else if (intel_hdcp2_in_use(connector))
intel_hdcp2_check_link(connector);
+
+ atomic_set(&connector->hdcp.cp_irq_recved, 1);
+ wake_up_all(&connector->hdcp.cp_irq_queue);
}
--
2.7.4
_______________________________________________
dri-devel mailing list
https://lists.freedesktop.org/mailman/listinfo/dri-devel
Daniel Vetter
2018-12-06 13:58:01 UTC
Permalink
Post by Ramalingam C
Implements the DP adaptation specific HDCP2.2 functions.
These functions perform the DPCD read and write for communicating the
HDCP2.2 auth message back and forth.
wait for cp_irq is merged with this patch. Rebased.
wait_queue is used for wait for cp_irq [Chris Wilson]
Style fixed.
%s/PARING/PAIRING
Few style fixes [Uma]
Lookup table for DP HDCP2.2 msg details [Daniel].
Extra lines are removed.
Rebased.
Fixed some regression introduced at v5. [Ankit]
Macro HDCP_2_2_RX_CAPS_VERSION_VAL is reused [Uma]
Converted a function to inline [Uma]
%s/uintxx_t/uxx
Error due to the sinks are reported as DEBUG logs.
Adjust to the new mei interface.
---
drivers/gpu/drm/i915/intel_dp.c | 338 ++++++++++++++++++++++++++++++++++++++
drivers/gpu/drm/i915/intel_drv.h | 7 +
drivers/gpu/drm/i915/intel_hdcp.c | 6 +
3 files changed, 351 insertions(+)
diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c
index ecc4706db7dc..1cc82e490999 100644
--- a/drivers/gpu/drm/i915/intel_dp.c
+++ b/drivers/gpu/drm/i915/intel_dp.c
@@ -31,6 +31,7 @@
#include <linux/types.h>
#include <linux/notifier.h>
#include <linux/reboot.h>
+#include <linux/mei_hdcp.h>
#include <asm/byteorder.h>
#include <drm/drmP.h>
#include <drm/drm_atomic_helper.h>
@@ -5347,6 +5348,27 @@ void intel_dp_encoder_suspend(struct intel_encoder *intel_encoder)
pps_unlock(intel_dp);
}
+static int intel_dp_hdcp_wait_for_cp_irq(struct intel_hdcp *hdcp,
+ int timeout)
+{
+ long ret;
+
+ /* Reinit */
+ atomic_set(&hdcp->cp_irq_recved, 0);
This here doesn't fix any race here, you might as well use

#define C true

here (Except of course it rechecks that).
Post by Ramalingam C
+
+#define C (atomic_read(&hdcp->cp_irq_recved) > 0)
+ ret = wait_event_interruptible_timeout(hdcp->cp_irq_queue, C,
+ msecs_to_jiffies(timeout));
What you really need/want to do to check whether the wait succeeded is
call hdcp2_detect_msg_availability(). Might be better to open-code the
wait_event for clarity.


Also debug ouptut to complain when we timed out here would be good (since
you ignore the error later on)
Post by Ramalingam C
+
+ if (ret > 0) {
+ atomic_set(&hdcp->cp_irq_recved, 0);
+ return 0;
+ } else if (!ret) {
+ return -ETIMEDOUT;
+ }
+ return (int)ret;
+}
+
static
int intel_dp_hdcp_write_an_aksv(struct intel_digital_port *intel_dig_port,
u8 *an)
@@ -5570,6 +5592,316 @@ int intel_dp_hdcp_capable(struct intel_digital_port *intel_dig_port,
return 0;
}
+static struct hdcp2_dp_msg_data {
+ u8 msg_id;
+ u32 offset;
+ bool msg_detectable;
+ u32 timeout;
+ u32 timeout2; /* Added for non_paired situation */
+ } hdcp2_msg_data[] = {
+ {HDCP_2_2_AKE_INIT, DP_HDCP_2_2_AKE_INIT_OFFSET, false, 0, 0},
+ {HDCP_2_2_AKE_SEND_CERT, DP_HDCP_2_2_AKE_SEND_CERT_OFFSET,
+ false, HDCP_2_2_CERT_TIMEOUT_MS, 0},
+ {HDCP_2_2_AKE_NO_STORED_KM, DP_HDCP_2_2_AKE_NO_STORED_KM_OFFSET,
+ false, 0, 0},
+ {HDCP_2_2_AKE_STORED_KM, DP_HDCP_2_2_AKE_STORED_KM_OFFSET,
+ false, 0, 0},
+ {HDCP_2_2_AKE_SEND_HPRIME, DP_HDCP_2_2_AKE_SEND_HPRIME_OFFSET,
+ true, HDCP_2_2_HPRIME_PAIRED_TIMEOUT_MS,
+ HDCP_2_2_HPRIME_NO_PAIRED_TIMEOUT_MS},
+ {HDCP_2_2_AKE_SEND_PAIRING_INFO,
+ DP_HDCP_2_2_AKE_SEND_PAIRING_INFO_OFFSET, true,
+ HDCP_2_2_PAIRING_TIMEOUT_MS, 0},
+ {HDCP_2_2_LC_INIT, DP_HDCP_2_2_LC_INIT_OFFSET, false, 0, 0},
+ {HDCP_2_2_LC_SEND_LPRIME, DP_HDCP_2_2_LC_SEND_LPRIME_OFFSET,
+ false, HDCP_2_2_DP_LPRIME_TIMEOUT_MS, 0},
+ {HDCP_2_2_SKE_SEND_EKS, DP_HDCP_2_2_SKE_SEND_EKS_OFFSET, false,
+ 0, 0},
+ {HDCP_2_2_REP_SEND_RECVID_LIST,
+ DP_HDCP_2_2_REP_SEND_RECVID_LIST_OFFSET, true,
+ HDCP_2_2_RECVID_LIST_TIMEOUT_MS, 0},
+ {HDCP_2_2_REP_SEND_ACK, DP_HDCP_2_2_REP_SEND_ACK_OFFSET, false,
+ 0, 0},
+ {HDCP_2_2_REP_STREAM_MANAGE,
+ DP_HDCP_2_2_REP_STREAM_MANAGE_OFFSET, false,
+ 0, 0},
+ {HDCP_2_2_REP_STREAM_READY, DP_HDCP_2_2_REP_STREAM_READY_OFFSET,
+ false, HDCP_2_2_STREAM_READY_TIMEOUT_MS, 0},
+ {HDCP_2_2_ERRATA_DP_STREAM_TYPE,
+ DP_HDCP_2_2_REG_STREAM_TYPE_OFFSET, false,
+ 0, 0},
+ };
+
+static inline
+int intel_dp_hdcp2_read_rx_status(struct intel_digital_port *intel_dig_port,
+ u8 *rx_status)
+{
+ ssize_t ret;
+
+ ret = drm_dp_dpcd_read(&intel_dig_port->dp.aux,
+ DP_HDCP_2_2_REG_RXSTATUS_OFFSET, rx_status,
+ HDCP_2_2_DP_RXSTATUS_LEN);
+ if (ret != HDCP_2_2_DP_RXSTATUS_LEN) {
+ DRM_DEBUG_KMS("Read bstatus from DP/AUX failed (%zd)\n", ret);
+ return ret >= 0 ? -EIO : ret;
+ }
+
+ return 0;
+}
+
+static
+int hdcp2_detect_msg_availability(struct intel_digital_port *intel_dig_port,
+ u8 msg_id, bool *msg_ready)
+{
+ u8 rx_status;
+ int ret;
+
+ *msg_ready = false;
+ ret = intel_dp_hdcp2_read_rx_status(intel_dig_port, &rx_status);
+ if (ret < 0)
+ return ret;
+
+ switch (msg_id) {
+ if (HDCP_2_2_DP_RXSTATUS_H_PRIME(rx_status))
+ *msg_ready = true;
+ break;
+ if (HDCP_2_2_DP_RXSTATUS_PAIRING(rx_status))
+ *msg_ready = true;
+ break;
+ if (HDCP_2_2_DP_RXSTATUS_READY(rx_status))
+ *msg_ready = true;
+ break;
+ DRM_ERROR("Unidentified msg_id: %d\n", msg_id);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static ssize_t
+intel_dp_hdcp2_wait_for_msg(struct intel_digital_port *intel_dig_port,
+ struct hdcp2_dp_msg_data *hdcp2_msg_data)
+{
+ struct intel_dp *dp = &intel_dig_port->dp;
+ struct intel_hdcp *hdcp = &dp->attached_connector->hdcp;
+ int ret, timeout;
+ bool msg_ready = false;
+
+ if (hdcp2_msg_data->msg_id == HDCP_2_2_AKE_SEND_HPRIME &&
+ !hdcp->is_paired)
+ timeout = hdcp2_msg_data->timeout2;
+ else
+ timeout = hdcp2_msg_data->timeout;
+
+ /*
+ * There is no way to detect the CERT, LPRIME and STREAM_READY
+ * availability. So Wait for timeout and read the msg.
+ */
+ if (!hdcp2_msg_data->msg_detectable) {
+ mdelay(timeout);
+ ret = 0;
+ } else {
+ /*
+ * Ignoring the return of the intel_dp_hdcp_wait_for_cp_irq,
+ * Just to detect the msg availability before failing it.
+ */
+ intel_dp_hdcp_wait_for_cp_irq(hdcp, timeout);
If you ignore the error of a static function, make it return void.
Otherwise just confusing to the reader.
Post by Ramalingam C
+ ret = hdcp2_detect_msg_availability(intel_dig_port,
+ hdcp2_msg_data->msg_id,
+ &msg_ready);
+ if (!msg_ready)
+ ret = -ETIMEDOUT;
+ }
+
+ if (ret)
+ DRM_DEBUG_KMS("msg_id %d, ret %d, timeout(mSec): %d\n",
+ hdcp2_msg_data->msg_id, ret, timeout);
+
+ return ret;
+}
+
+static struct hdcp2_dp_msg_data *get_hdcp2_dp_msg_data(u8 msg_id)
+{
+ int i;
+
+ for (i = 0; i < sizeof(hdcp2_msg_data); i++)
+ if (hdcp2_msg_data[i].msg_id == msg_id)
+ return &hdcp2_msg_data[i];
+
+ return NULL;
+}
+
+static
+int intel_dp_hdcp2_write_msg(struct intel_digital_port *intel_dig_port,
+ void *buf, size_t size)
+{
+ unsigned int offset;
+ u8 *byte = buf;
+ ssize_t ret, bytes_to_write, len;
+ struct hdcp2_dp_msg_data *hdcp2_msg_data;
+
+ hdcp2_msg_data = get_hdcp2_dp_msg_data(*byte);
+ if (!hdcp2_msg_data)
+ return -EINVAL;
+
+ offset = hdcp2_msg_data->offset;
+
+ /* No msg_id in DP HDCP2.2 msgs */
+ bytes_to_write = size - 1;
+ byte++;
+
+ while (bytes_to_write) {
+ len = bytes_to_write > DP_AUX_MAX_PAYLOAD_BYTES ?
+ DP_AUX_MAX_PAYLOAD_BYTES : bytes_to_write;
+
+ ret = drm_dp_dpcd_write(&intel_dig_port->dp.aux,
+ offset, (void *)byte, len);
+ if (ret < 0)
+ return ret;
+
+ bytes_to_write -= ret;
+ byte += ret;
+ offset += ret;
+ }
+
+ return size;
+}
+
+static
+ssize_t get_receiver_id_list_size(struct intel_digital_port *intel_dig_port)
+{
+ u8 rx_info[HDCP_2_2_RXINFO_LEN];
+ u32 dev_cnt;
+ ssize_t ret;
+
+ ret = drm_dp_dpcd_read(&intel_dig_port->dp.aux,
+ DP_HDCP_2_2_REG_RXINFO_OFFSET,
+ (void *)rx_info, HDCP_2_2_RXINFO_LEN);
+ if (ret != HDCP_2_2_RXINFO_LEN)
+ return ret >= 0 ? -EIO : ret;
+
+ dev_cnt = (HDCP_2_2_DEV_COUNT_HI(rx_info[0]) << 4 |
+ HDCP_2_2_DEV_COUNT_LO(rx_info[1]));
+
+ if (dev_cnt > HDCP_2_2_MAX_DEVICE_COUNT)
+ dev_cnt = HDCP_2_2_MAX_DEVICE_COUNT;
+
+ ret = sizeof(struct hdcp2_rep_send_receiverid_list) -
+ HDCP_2_2_RECEIVER_IDS_MAX_LEN +
+ (dev_cnt * HDCP_2_2_RECEIVER_ID_LEN);
+
+ return ret;
+}
+
+static
+int intel_dp_hdcp2_read_msg(struct intel_digital_port *intel_dig_port,
+ u8 msg_id, void *buf, size_t size)
+{
+ unsigned int offset;
+ u8 *byte = buf;
+ ssize_t ret, bytes_to_recv, len;
+ struct hdcp2_dp_msg_data *hdcp2_msg_data;
+
+ hdcp2_msg_data = get_hdcp2_dp_msg_data(msg_id);
+ if (!hdcp2_msg_data)
+ return -EINVAL;
+ offset = hdcp2_msg_data->offset;
+
+ ret = intel_dp_hdcp2_wait_for_msg(intel_dig_port, hdcp2_msg_data);
+ if (ret < 0)
+ return ret;
+
+ if (msg_id == HDCP_2_2_REP_SEND_RECVID_LIST) {
+ ret = get_receiver_id_list_size(intel_dig_port);
+ if (ret < 0)
+ return ret;
+
+ size = ret;
+ }
+ bytes_to_recv = size - 1;
+
+ /* DP adaptation msgs has no msg_id */
+ byte++;
+
+ while (bytes_to_recv) {
+ len = bytes_to_recv > DP_AUX_MAX_PAYLOAD_BYTES ?
+ DP_AUX_MAX_PAYLOAD_BYTES : bytes_to_recv;
+
+ ret = drm_dp_dpcd_read(&intel_dig_port->dp.aux, offset,
+ (void *)byte, len);
+ if (ret < 0) {
+ DRM_DEBUG_KMS("msg_id %d, ret %zd\n", msg_id, ret);
+ return ret;
+ }
+
+ bytes_to_recv -= ret;
+ byte += ret;
+ offset += ret;
+ }
+ byte = buf;
+ *byte = msg_id;
+
+ return size;
+}
+
+static
+int intel_dp_hdcp2_config_stream_type(struct intel_digital_port *intel_dig_port,
+ void *buf, size_t size)
+{
+ return intel_dp_hdcp2_write_msg(intel_dig_port, buf, size);
+}
+
+static
+int intel_dp_hdcp2_check_link(struct intel_digital_port *intel_dig_port)
+{
+ u8 rx_status;
+ int ret;
+
+ ret = intel_dp_hdcp2_read_rx_status(intel_dig_port, &rx_status);
+ if (ret)
+ return ret;
+
+ if (HDCP_2_2_DP_RXSTATUS_REAUTH_REQ(rx_status))
+ ret = DRM_HDCP_REAUTH_REQUEST;
+ else if (HDCP_2_2_DP_RXSTATUS_LINK_FAILED(rx_status))
+ ret = DRM_HDCP_LINK_INTEGRITY_FAILURE;
+ else if (HDCP_2_2_DP_RXSTATUS_READY(rx_status))
+ ret = DRM_HDCP_TOPOLOGY_CHANGE;
+
+ return ret;
+}
+
+static
+int intel_dp_hdcp2_capable(struct intel_digital_port *intel_dig_port,
+ bool *capable)
+{
+ u8 rx_caps[3];
+ int ret;
+
+ *capable = false;
+ ret = drm_dp_dpcd_read(&intel_dig_port->dp.aux,
+ DP_HDCP_2_2_REG_RX_CAPS_OFFSET,
+ rx_caps, HDCP_2_2_RXCAPS_LEN);
+ if (ret != HDCP_2_2_RXCAPS_LEN)
+ return ret >= 0 ? -EIO : ret;
+
+ if (rx_caps[0] == HDCP_2_2_RX_CAPS_VERSION_VAL &&
+ HDCP_2_2_DP_HDCP_CAPABLE(rx_caps[2]))
+ *capable = true;
+
+ return 0;
+}
+
+static inline
+enum mei_hdcp_wired_protocol intel_dp_hdcp2_protocol(void)
+{
+ return MEI_HDCP_PROTOCOL_DP;
+}
+
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,
@@ -5582,6 +5914,12 @@ static const struct intel_hdcp_shim intel_dp_hdcp_shim = {
.toggle_signalling = intel_dp_hdcp_toggle_signalling,
.check_link = intel_dp_hdcp_check_link,
.hdcp_capable = intel_dp_hdcp_capable,
+ .write_2_2_msg = intel_dp_hdcp2_write_msg,
+ .read_2_2_msg = intel_dp_hdcp2_read_msg,
+ .config_stream_type = intel_dp_hdcp2_config_stream_type,
+ .check_2_2_link = intel_dp_hdcp2_check_link,
+ .hdcp_2_2_capable = intel_dp_hdcp2_capable,
+ .hdcp_protocol = intel_dp_hdcp2_protocol,
};
static void intel_edp_panel_vdd_sanitize(struct intel_dp *intel_dp)
diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h
index d5ab1ff9cc69..2b7181b76475 100644
--- a/drivers/gpu/drm/i915/intel_drv.h
+++ b/drivers/gpu/drm/i915/intel_drv.h
@@ -450,6 +450,13 @@ struct intel_hdcp {
u32 seq_num_m;
struct delayed_work hdcp2_check_work;
+
+ /*
+ * Work queue to signal the CP_IRQ. Used for the waiters to read the
+ * available information from HDCP DP sink.
+ */
+ wait_queue_head_t cp_irq_queue;
+ atomic_t cp_irq_recved;
};
struct intel_connector {
diff --git a/drivers/gpu/drm/i915/intel_hdcp.c b/drivers/gpu/drm/i915/intel_hdcp.c
index b9e5f73c640d..be475914c3cd 100644
--- a/drivers/gpu/drm/i915/intel_hdcp.c
+++ b/drivers/gpu/drm/i915/intel_hdcp.c
@@ -1899,6 +1899,9 @@ int intel_hdcp_init(struct intel_connector *connector,
if (hdcp2_supported)
intel_hdcp2_init(connector);
+ atomic_set(&hdcp->cp_irq_recved, 0);
+ init_waitqueue_head(&hdcp->cp_irq_queue);
+
return 0;
}
@@ -2036,4 +2039,7 @@ void intel_hdcp_handle_cp_irq(struct intel_connector *connector)
intel_hdcp_check_link(connector);
else if (intel_hdcp2_in_use(connector))
intel_hdcp2_check_link(connector);
+
+ atomic_set(&connector->hdcp.cp_irq_recved, 1);
+ wake_up_all(&connector->hdcp.cp_irq_queue);
This deadlocks the waiter, and you'll time out (which saves the day). If
check_link needs to do some HDCP2 writes, which needs to wait for the cp
interrupt, then this will never happened because the exact same worker is
waiting for that cp interrupt to be signaled.

You need 2 workers to make this work:
1. Handles the CP interrupt and calls wake_up_all(), plus schedules the
check_link worker (if needed .... how should we decide that exactly?)

2. 2nd worker runs the check_link work.

This way the 2nd worker can go through the entire sequence, while the 1st
worker handles interrupts and does wake_up_all calls as needed. Problem
is, if the first worker also schedules the 2nd one, you're now in an
endless loop.

Since the entire thing seems to be not that well thought out: What about
just going with the msleep(timeout) for now, and figuring this out later?
Defacto this is what your code already does.

For the hdcp2 logic itself I'm going to trust Uma again.
-Daniel
--
Daniel Vetter
Software Engineer, Intel Corporation
http://blog.ffwll.ch
Ramalingam C
2018-11-27 10:43:08 UTC
Permalink
When repeater notifies a downstream topology change, this patch
reauthenticate the repeater alone without disabling the hdcp
encryption. If that fails then complete reauthentication is executed.

v2:
Rebased.
v3:
No Changes.
v4:
Typo in commit msg is fixed [Uma]
v5:
Rebased as part of patch reordering.
Minor style fixes.
v6:
Rebased.
v7:
Rebased.
v8:
Errors due to sinks are reported as DEBUG logs.

Signed-off-by: Ramalingam C <***@intel.com>
Reviewed-by: Uma Shankar <***@intel.com>
---
drivers/gpu/drm/i915/intel_hdcp.c | 20 ++++++++++++++++++--
1 file changed, 18 insertions(+), 2 deletions(-)

diff --git a/drivers/gpu/drm/i915/intel_hdcp.c b/drivers/gpu/drm/i915/intel_hdcp.c
index 98b112395a5a..df0fb6d9200b 100644
--- a/drivers/gpu/drm/i915/intel_hdcp.c
+++ b/drivers/gpu/drm/i915/intel_hdcp.c
@@ -1662,8 +1662,24 @@ static int intel_hdcp2_check_link(struct intel_connector *connector)
goto out;
}

- DRM_DEBUG_KMS("[%s:%d] HDCP2.2 link failed, retrying auth\n",
- connector->base.name, connector->base.base.id);
+ if (ret == DRM_HDCP_TOPOLOGY_CHANGE) {
+ if (hdcp->value == DRM_MODE_CONTENT_PROTECTION_UNDESIRED)
+ goto out;
+
+ DRM_DEBUG_KMS("HDCP2.2 Downstream topology change\n");
+ ret = hdcp2_authenticate_repeater_topology(connector);
+ if (!ret) {
+ hdcp->value = DRM_MODE_CONTENT_PROTECTION_ENABLED;
+ schedule_work(&hdcp->prop_work);
+ goto out;
+ }
+ DRM_DEBUG_KMS("[%s:%d] Repeater topology auth failed.(%d)\n",
+ connector->base.name, connector->base.base.id,
+ ret);
+ } else {
+ DRM_DEBUG_KMS("[%s:%d] HDCP2.2 link failed, retrying auth\n",
+ connector->base.name, connector->base.base.id);
+ }

ret = _intel_hdcp2_disable(connector);
if (ret) {
--
2.7.4
Daniel Vetter
2018-12-06 13:42:00 UTC
Permalink
Post by Ramalingam C
When repeater notifies a downstream topology change, this patch
reauthenticate the repeater alone without disabling the hdcp
encryption. If that fails then complete reauthentication is executed.
Rebased.
No Changes.
Typo in commit msg is fixed [Uma]
Rebased as part of patch reordering.
Minor style fixes.
Rebased.
Rebased.
Errors due to sinks are reported as DEBUG logs.
Trusting Uma's hdcp knowledge here.
-Daniel
Post by Ramalingam C
---
drivers/gpu/drm/i915/intel_hdcp.c | 20 ++++++++++++++++++--
1 file changed, 18 insertions(+), 2 deletions(-)
diff --git a/drivers/gpu/drm/i915/intel_hdcp.c b/drivers/gpu/drm/i915/intel_hdcp.c
index 98b112395a5a..df0fb6d9200b 100644
--- a/drivers/gpu/drm/i915/intel_hdcp.c
+++ b/drivers/gpu/drm/i915/intel_hdcp.c
@@ -1662,8 +1662,24 @@ static int intel_hdcp2_check_link(struct intel_connector *connector)
goto out;
}
- DRM_DEBUG_KMS("[%s:%d] HDCP2.2 link failed, retrying auth\n",
- connector->base.name, connector->base.base.id);
+ if (ret == DRM_HDCP_TOPOLOGY_CHANGE) {
+ if (hdcp->value == DRM_MODE_CONTENT_PROTECTION_UNDESIRED)
+ goto out;
+
+ DRM_DEBUG_KMS("HDCP2.2 Downstream topology change\n");
+ ret = hdcp2_authenticate_repeater_topology(connector);
+ if (!ret) {
+ hdcp->value = DRM_MODE_CONTENT_PROTECTION_ENABLED;
+ schedule_work(&hdcp->prop_work);
+ goto out;
+ }
+ DRM_DEBUG_KMS("[%s:%d] Repeater topology auth failed.(%d)\n",
+ connector->base.name, connector->base.base.id,
+ ret);
+ } else {
+ DRM_DEBUG_KMS("[%s:%d] HDCP2.2 link failed, retrying auth\n",
+ connector->base.name, connector->base.base.id);
+ }
ret = _intel_hdcp2_disable(connector);
if (ret) {
--
2.7.4
--
Daniel Vetter
Software Engineer, Intel Corporation
http://blog.ffwll.ch
Ramalingam C
2018-11-27 10:43:09 UTC
Permalink
On DP HDCP1.4 and 2.2, when CP_IRQ is received, start the link
integrity check for the HDCP version that is enabled.

v2:
Rebased. Function name is changed.
v3:
No Changes.
v4:
No Changes.
v5:
No Changes.
v6:
%s/_in_force/_in_use [Sean Paul]
v7:
Rebased.
v8:
Rebased.

Signed-off-by: Ramalingam C <***@intel.com>
cc: Sean Paul <***@chromium.org>
Reviewed-by: Sean Paul <***@chromium.org>
---
drivers/gpu/drm/i915/intel_dp.c | 2 +-
drivers/gpu/drm/i915/intel_drv.h | 2 +-
drivers/gpu/drm/i915/intel_hdcp.c | 29 +++++++++++++++++++++++++++++
3 files changed, 31 insertions(+), 2 deletions(-)

diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c
index ac62af073688..ecc4706db7dc 100644
--- a/drivers/gpu/drm/i915/intel_dp.c
+++ b/drivers/gpu/drm/i915/intel_dp.c
@@ -4532,7 +4532,7 @@ static void intel_dp_check_service_irq(struct intel_dp *intel_dp)
intel_dp_handle_test_request(intel_dp);

if (val & DP_CP_IRQ)
- intel_hdcp_check_link(intel_dp->attached_connector);
+ intel_hdcp_handle_cp_irq(intel_dp->attached_connector);

if (val & DP_SINK_SPECIFIC_IRQ)
DRM_DEBUG_DRIVER("Sink specific irq unhandled\n");
diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h
index e6e32bf52568..d5ab1ff9cc69 100644
--- a/drivers/gpu/drm/i915/intel_drv.h
+++ b/drivers/gpu/drm/i915/intel_drv.h
@@ -2082,11 +2082,11 @@ int intel_hdcp_init(struct intel_connector *connector,
bool hdcp2_supported);
int intel_hdcp_enable(struct intel_connector *connector);
int intel_hdcp_disable(struct intel_connector *connector);
-int intel_hdcp_check_link(struct intel_connector *connector);
bool is_hdcp_supported(struct drm_i915_private *dev_priv, enum port port);
bool intel_hdcp_capable(struct intel_connector *connector);
bool is_hdcp2_supported(struct drm_i915_private *dev_priv);
void intel_hdcp_exit(struct drm_i915_private *dev_priv);
+void intel_hdcp_handle_cp_irq(struct intel_connector *connector);

/* intel_psr.c */
#define CAN_PSR(dev_priv) (HAS_PSR(dev_priv) && dev_priv->psr.sink_support)
diff --git a/drivers/gpu/drm/i915/intel_hdcp.c b/drivers/gpu/drm/i915/intel_hdcp.c
index df0fb6d9200b..b9e5f73c640d 100644
--- a/drivers/gpu/drm/i915/intel_hdcp.c
+++ b/drivers/gpu/drm/i915/intel_hdcp.c
@@ -117,6 +117,26 @@ static bool intel_hdcp2_capable(struct intel_connector *connector)
return capable;
}

+static inline bool intel_hdcp_in_use(struct intel_connector *connector)
+{
+ struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
+ enum port port = connector->encoder->port;
+ u32 reg;
+
+ reg = I915_READ(PORT_HDCP_STATUS(port));
+ return reg & (HDCP_STATUS_AUTH | HDCP_STATUS_ENC);
+}
+
+static inline bool intel_hdcp2_in_use(struct intel_connector *connector)
+{
+ struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
+ enum port port = connector->encoder->port;
+ u32 reg;
+
+ reg = I915_READ(HDCP2_STATUS_DDI(port));
+ return reg & (LINK_ENCRYPTION_STATUS | LINK_AUTH_STATUS);
+}
+
static int intel_hdcp_poll_ksv_fifo(struct intel_digital_port *intel_dig_port,
const struct intel_hdcp_shim *shim)
{
@@ -2008,3 +2028,12 @@ void intel_hdcp_atomic_check(struct drm_connector *connector,
new_state->crtc);
crtc_state->mode_changed = true;
}
+
+/* Handles the CP_IRQ raised from the DP HDCP sink */
+void intel_hdcp_handle_cp_irq(struct intel_connector *connector)
+{
+ if (intel_hdcp_in_use(connector))
+ intel_hdcp_check_link(connector);
+ else if (intel_hdcp2_in_use(connector))
+ intel_hdcp2_check_link(connector);
+}
--
2.7.4
Daniel Vetter
2018-12-06 13:44:33 UTC
Permalink
Post by Ramalingam C
On DP HDCP1.4 and 2.2, when CP_IRQ is received, start the link
integrity check for the HDCP version that is enabled.
Rebased. Function name is changed.
No Changes.
No Changes.
No Changes.
%s/_in_force/_in_use [Sean Paul]
Rebased.
Rebased.
---
drivers/gpu/drm/i915/intel_dp.c | 2 +-
drivers/gpu/drm/i915/intel_drv.h | 2 +-
drivers/gpu/drm/i915/intel_hdcp.c | 29 +++++++++++++++++++++++++++++
3 files changed, 31 insertions(+), 2 deletions(-)
diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c
index ac62af073688..ecc4706db7dc 100644
--- a/drivers/gpu/drm/i915/intel_dp.c
+++ b/drivers/gpu/drm/i915/intel_dp.c
@@ -4532,7 +4532,7 @@ static void intel_dp_check_service_irq(struct intel_dp *intel_dp)
intel_dp_handle_test_request(intel_dp);
if (val & DP_CP_IRQ)
- intel_hdcp_check_link(intel_dp->attached_connector);
+ intel_hdcp_handle_cp_irq(intel_dp->attached_connector);
if (val & DP_SINK_SPECIFIC_IRQ)
DRM_DEBUG_DRIVER("Sink specific irq unhandled\n");
diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h
index e6e32bf52568..d5ab1ff9cc69 100644
--- a/drivers/gpu/drm/i915/intel_drv.h
+++ b/drivers/gpu/drm/i915/intel_drv.h
@@ -2082,11 +2082,11 @@ int intel_hdcp_init(struct intel_connector *connector,
bool hdcp2_supported);
int intel_hdcp_enable(struct intel_connector *connector);
int intel_hdcp_disable(struct intel_connector *connector);
-int intel_hdcp_check_link(struct intel_connector *connector);
bool is_hdcp_supported(struct drm_i915_private *dev_priv, enum port port);
bool intel_hdcp_capable(struct intel_connector *connector);
bool is_hdcp2_supported(struct drm_i915_private *dev_priv);
void intel_hdcp_exit(struct drm_i915_private *dev_priv);
+void intel_hdcp_handle_cp_irq(struct intel_connector *connector);
Renaming doesn't seem to gain us anything, pls drop.
Post by Ramalingam C
/* intel_psr.c */
#define CAN_PSR(dev_priv) (HAS_PSR(dev_priv) && dev_priv->psr.sink_support)
diff --git a/drivers/gpu/drm/i915/intel_hdcp.c b/drivers/gpu/drm/i915/intel_hdcp.c
index df0fb6d9200b..b9e5f73c640d 100644
--- a/drivers/gpu/drm/i915/intel_hdcp.c
+++ b/drivers/gpu/drm/i915/intel_hdcp.c
@@ -117,6 +117,26 @@ static bool intel_hdcp2_capable(struct intel_connector *connector)
return capable;
}
+static inline bool intel_hdcp_in_use(struct intel_connector *connector)
+{
+ struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
+ enum port port = connector->encoder->port;
+ u32 reg;
+
+ reg = I915_READ(PORT_HDCP_STATUS(port));
+ return reg & (HDCP_STATUS_AUTH | HDCP_STATUS_ENC);
+}
+
+static inline bool intel_hdcp2_in_use(struct intel_connector *connector)
+{
+ struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
+ enum port port = connector->encoder->port;
+ u32 reg;
+
+ reg = I915_READ(HDCP2_STATUS_DDI(port));
+ return reg & (LINK_ENCRYPTION_STATUS | LINK_AUTH_STATUS);
+}
+
static int intel_hdcp_poll_ksv_fifo(struct intel_digital_port *intel_dig_port,
const struct intel_hdcp_shim *shim)
{
@@ -2008,3 +2028,12 @@ void intel_hdcp_atomic_check(struct drm_connector *connector,
new_state->crtc);
crtc_state->mode_changed = true;
}
+
+/* Handles the CP_IRQ raised from the DP HDCP sink */
+void intel_hdcp_handle_cp_irq(struct intel_connector *connector)
+{
+ if (intel_hdcp_in_use(connector))
Reading registers to figure out what we're doing is bad style. I think
reading register to double-check that our sw-side tracking is correct is
good, so maybe put these checks into hdcp1/2_check_link in a WARN_ON. But
here we should take the hdcp mutex and look at the sw state.

This will naturally fall out of the check_link worker merging in the
earlier patch I think.
-Daniel
Post by Ramalingam C
+ intel_hdcp_check_link(connector);
+ else if (intel_hdcp2_in_use(connector))
+ intel_hdcp2_check_link(connector);
+}
--
2.7.4
--
Daniel Vetter
Software Engineer, Intel Corporation
http://blog.ffwll.ch
Ramalingam C
2018-11-27 10:43:17 UTC
Permalink
Defines the HDCP specific ME FW interfaces such as Request CMDs,
payload structure for CMDs and their response status codes.

This patch defines payload size(Excluding the Header)for each WIRED
HDCP2.2 CMDs.

v2:
Rebased.
v3:
Extra comments are removed.
v4:
%s/\/\*\*/\/\*
v5:
Extra lines are removed.
v6:
No change.
v7:
Remove redundant text from the License header
%s/LPRIME_HALF/V_PRIME_HALF
%s/uintxx_t/uxx
v8:
Extra taps removed.

Signed-off-by: Ramalingam C <***@intel.com>
---
drivers/misc/mei/hdcp/mei_hdcp.h | 366 +++++++++++++++++++++++++++++++++++++++
1 file changed, 366 insertions(+)
create mode 100644 drivers/misc/mei/hdcp/mei_hdcp.h

diff --git a/drivers/misc/mei/hdcp/mei_hdcp.h b/drivers/misc/mei/hdcp/mei_hdcp.h
new file mode 100644
index 000000000000..d1456e3dbf22
--- /dev/null
+++ b/drivers/misc/mei/hdcp/mei_hdcp.h
@@ -0,0 +1,366 @@
+/* SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) */
+/*
+ * Copyright © 2017-2018 Intel Corporation
+ *
+ * Authors:
+ * Ramalingam C <***@intel.com>
+ */
+
+#ifndef __MEI_HDCP_H__
+#define __MEI_HDCP_H__
+
+#include <drm/drm_hdcp.h>
+
+/* me_hdcp_status: Enumeration of all HDCP Status Codes */
+enum me_hdcp_status {
+ ME_HDCP_STATUS_SUCCESS = 0x0000,
+
+ /* WiDi Generic Status Codes */
+ ME_HDCP_STATUS_INTERNAL_ERROR = 0x1000,
+ ME_HDCP_STATUS_UNKNOWN_ERROR = 0x1001,
+ ME_HDCP_STATUS_INCORRECT_API_VERSION = 0x1002,
+ ME_HDCP_STATUS_INVALID_FUNCTION = 0x1003,
+ ME_HDCP_STATUS_INVALID_BUFFER_LENGTH = 0x1004,
+ ME_HDCP_STATUS_INVALID_PARAMS = 0x1005,
+ ME_HDCP_STATUS_AUTHENTICATION_FAILED = 0x1006,
+
+ /* WiDi Status Codes */
+ ME_HDCP_INVALID_SESSION_STATE = 0x6000,
+ ME_HDCP_SRM_FRAGMENT_UNEXPECTED = 0x6001,
+ ME_HDCP_SRM_INVALID_LENGTH = 0x6002,
+ ME_HDCP_SRM_FRAGMENT_OFFSET_INVALID = 0x6003,
+ ME_HDCP_SRM_VERIFICATION_FAILED = 0x6004,
+ ME_HDCP_SRM_VERSION_TOO_OLD = 0x6005,
+ ME_HDCP_RX_CERT_VERIFICATION_FAILED = 0x6006,
+ ME_HDCP_RX_REVOKED = 0x6007,
+ ME_HDCP_H_VERIFICATION_FAILED = 0x6008,
+ ME_HDCP_REPEATER_CHECK_UNEXPECTED = 0x6009,
+ ME_HDCP_TOPOLOGY_MAX_EXCEEDED = 0x600A,
+ ME_HDCP_V_VERIFICATION_FAILED = 0x600B,
+ ME_HDCP_L_VERIFICATION_FAILED = 0x600C,
+ ME_HDCP_STREAM_KEY_ALLOC_FAILED = 0x600D,
+ ME_HDCP_BASE_KEY_RESET_FAILED = 0x600E,
+ ME_HDCP_NONCE_GENERATION_FAILED = 0x600F,
+ ME_HDCP_STATUS_INVALID_E_KEY_STATE = 0x6010,
+ ME_HDCP_STATUS_INVALID_CS_ICV = 0x6011,
+ ME_HDCP_STATUS_INVALID_KB_KEY_STATE = 0x6012,
+ ME_HDCP_STATUS_INVALID_PAVP_MODE_ICV = 0x6013,
+ ME_HDCP_STATUS_INVALID_PAVP_MODE = 0x6014,
+ ME_HDCP_STATUS_LC_MAX_ATTEMPTS = 0x6015,
+
+ /* New status for HDCP 2.1 */
+ ME_HDCP_STATUS_MISMATCH_IN_M = 0x6016,
+
+ /* New status code for HDCP 2.2 Rx */
+ ME_HDCP_STATUS_RX_PROV_NOT_ALLOWED = 0x6017,
+ ME_HDCP_STATUS_RX_PROV_WRONG_SUBJECT = 0x6018,
+ ME_HDCP_RX_NEEDS_PROVISIONING = 0x6019,
+ ME_HDCP_BKSV_ICV_AUTH_FAILED = 0x6020,
+ ME_HDCP_STATUS_INVALID_STREAM_ID = 0x6021,
+ ME_HDCP_STATUS_CHAIN_NOT_INITIALIZED = 0x6022,
+ ME_HDCP_FAIL_NOT_EXPECTED = 0x6023,
+ ME_HDCP_FAIL_HDCP_OFF = 0x6024,
+ ME_HDCP_FAIL_INVALID_PAVP_MEMORY_MODE = 0x6025,
+ ME_HDCP_FAIL_AES_ECB_FAILURE = 0x6026,
+ ME_HDCP_FEATURE_NOT_SUPPORTED = 0x6027,
+ ME_HDCP_DMA_READ_ERROR = 0x6028,
+ ME_HDCP_DMA_WRITE_ERROR = 0x6029,
+ ME_HDCP_FAIL_INVALID_PACKET_SIZE = 0x6030,
+ ME_HDCP_H264_PARSING_ERROR = 0x6031,
+ ME_HDCP_HDCP2_ERRATA_VIDEO_VIOLATION = 0x6032,
+ ME_HDCP_HDCP2_ERRATA_AUDIO_VIOLATION = 0x6033,
+ ME_HDCP_TX_ACTIVE_ERROR = 0x6034,
+ ME_HDCP_MODE_CHANGE_ERROR = 0x6035,
+ ME_HDCP_STREAM_TYPE_ERROR = 0x6036,
+ ME_HDCP_STREAM_MANAGE_NOT_POSSIBLE = 0x6037,
+
+ ME_HDCP_STATUS_PORT_INVALID_COMMAND = 0x6038,
+ ME_HDCP_STATUS_UNSUPPORTED_PROTOCOL = 0x6039,
+ ME_HDCP_STATUS_INVALID_PORT_INDEX = 0x603a,
+ ME_HDCP_STATUS_TX_AUTH_NEEDED = 0x603b,
+ ME_HDCP_STATUS_NOT_INTEGRATED_PORT = 0x603c,
+ ME_HDCP_STATUS_SESSION_MAX_REACHED = 0x603d,
+
+ /* hdcp capable bit is not set in rx_caps(error is unique to DP) */
+ ME_HDCP_STATUS_NOT_HDCP_CAPABLE = 0x6041,
+
+ ME_HDCP_STATUS_INVALID_STREAM_COUNT = 0x6042,
+};
+
+#define HDCP_API_VERSION 0x00010000
+
+#define HDCP_M_LEN 16
+#define HDCP_KH_LEN 16
+
+/* Payload Buffer size(Excluding Header) for CMDs and corresponding response */
+/* Wired_Tx_AKE */
+#define WIRED_CMD_BUF_LEN_INITIATE_HDCP2_SESSION_IN (4 + 1)
+#define WIRED_CMD_BUF_LEN_INITIATE_HDCP2_SESSION_OUT (4 + 8 + 3)
+
+#define WIRED_CMD_BUF_LEN_VERIFY_RECEIVER_CERT_IN (4 + 522 + 8 + 3)
+#define WIRED_CMD_BUF_LEN_VERIFY_RECEIVER_CERT_MIN_OUT (4 + 1 + 3 + 16 + 16)
+#define WIRED_CMD_BUF_LEN_VERIFY_RECEIVER_CERT_MAX_OUT (4 + 1 + 3 + 128)
+
+#define WIRED_CMD_BUF_LEN_AKE_SEND_HPRIME_IN (4 + 32)
+#define WIRED_CMD_BUF_LEN_AKE_SEND_HPRIME_OUT (4)
+
+#define WIRED_CMD_BUF_LEN_SEND_PAIRING_INFO_IN (4 + 16)
+#define WIRED_CMD_BUF_LEN_SEND_PAIRING_INFO_OUT (4)
+
+#define WIRED_CMD_BUF_LEN_CLOSE_SESSION_IN (4)
+#define WIRED_CMD_BUF_LEN_CLOSE_SESSION_OUT (4)
+
+/* Wired_Tx_LC */
+#define WIRED_CMD_BUF_LEN_INIT_LOCALITY_CHECK_IN (4)
+#define WIRED_CMD_BUF_LEN_INIT_LOCALITY_CHECK_OUT (4 + 8)
+
+#define WIRED_CMD_BUF_LEN_VALIDATE_LOCALITY_IN (4 + 32)
+#define WIRED_CMD_BUF_LEN_VALIDATE_LOCALITY_OUT (4)
+
+/* Wired_Tx_SKE */
+#define WIRED_CMD_BUF_LEN_GET_SESSION_KEY_IN (4)
+#define WIRED_CMD_BUF_LEN_GET_SESSION_KEY_OUT (4 + 16 + 8)
+
+/* Wired_Tx_SKE */
+#define WIRED_CMD_BUF_LEN_ENABLE_AUTH_IN (4 + 1)
+#define WIRED_CMD_BUF_LEN_ENABLE_AUTH_OUT (4)
+
+/* Wired_Tx_Repeater */
+#define WIRED_CMD_BUF_LEN_VERIFY_REPEATER_IN (4 + 2 + 3 + 16 + 155)
+#define WIRED_CMD_BUF_LEN_VERIFY_REPEATER_OUT (4 + 1 + 16)
+
+#define WIRED_CMD_BUF_LEN_REPEATER_AUTH_STREAM_REQ_MIN_IN (4 + 3 + \
+ 32 + 2 + 2)
+
+#define WIRED_CMD_BUF_LEN_REPEATER_AUTH_STREAM_REQ_OUT (4)
+
+/* hdcp_command_id: Enumeration of all WIRED HDCP Command IDs */
+enum hdcp_command_id {
+ _WIDI_COMMAND_BASE = 0x00030000,
+ WIDI_INITIATE_HDCP2_SESSION = _WIDI_COMMAND_BASE,
+ HDCP_GET_SRM_STATUS,
+ HDCP_SEND_SRM_FRAGMENT,
+
+ /* The wired HDCP Tx commands */
+ _WIRED_COMMAND_BASE = 0x00031000,
+ WIRED_INITIATE_HDCP2_SESSION = _WIRED_COMMAND_BASE,
+ WIRED_VERIFY_RECEIVER_CERT,
+ WIRED_AKE_SEND_HPRIME,
+ WIRED_AKE_SEND_PAIRING_INFO,
+ WIRED_INIT_LOCALITY_CHECK,
+ WIRED_VALIDATE_LOCALITY,
+ WIRED_GET_SESSION_KEY,
+ WIRED_ENABLE_AUTH,
+ WIRED_VERIFY_REPEATER,
+ WIRED_REPEATER_AUTH_STREAM_REQ,
+ WIRED_CLOSE_SESSION,
+
+ _WIRED_COMMANDS_COUNT,
+};
+
+union encrypted_buff {
+ u8 e_kpub_km[HDCP_2_2_E_KPUB_KM_LEN];
+ u8 e_kh_km_m[HDCP_2_2_E_KH_KM_M_LEN];
+ struct {
+ u8 e_kh_km[HDCP_KH_LEN];
+ u8 m[HDCP_M_LEN];
+ } __packed;
+};
+
+/* HDCP HECI message header. All header values are little endian. */
+struct hdcp_cmd_header {
+ u32 api_version;
+ u32 command_id;
+ enum me_hdcp_status status;
+ /* Length of the HECI message (excluding the header) */
+ u32 buffer_len;
+} __packed;
+
+/* Empty command request or response. No data follows the header. */
+struct hdcp_cmd_no_data {
+ struct hdcp_cmd_header header;
+} __packed;
+
+/* Uniquely identifies the hdcp port being addressed for a given command. */
+struct hdcp_port_id {
+ u8 integrated_port_type;
+ u8 physical_port;
+ u16 reserved;
+} __packed;
+
+/*
+ * Data structures for integrated wired HDCP2 Tx in
+ * support of the AKE protocol
+ */
+/* HECI struct for integrated wired HDCP Tx session initiation. */
+struct wired_cmd_initiate_hdcp2_session_in {
+ struct hdcp_cmd_header header;
+ struct hdcp_port_id port;
+ u8 protocol; /* for HDMI vs DP */
+} __packed;
+
+struct wired_cmd_initiate_hdcp2_session_out {
+ struct hdcp_cmd_header header;
+ struct hdcp_port_id port;
+ u8 r_tx[HDCP_2_2_RTX_LEN];
+ struct hdcp2_tx_caps tx_caps;
+} __packed;
+
+/* HECI struct for ending an integrated wired HDCP Tx session. */
+struct wired_cmd_close_session_in {
+ struct hdcp_cmd_header header;
+ struct hdcp_port_id port;
+} __packed;
+
+struct wired_cmd_close_session_out {
+ struct hdcp_cmd_header header;
+ struct hdcp_port_id port;
+} __packed;
+
+/* HECI struct for integrated wired HDCP Tx Rx Cert verification. */
+struct wired_cmd_verify_receiver_cert_in {
+ struct hdcp_cmd_header header;
+ struct hdcp_port_id port;
+ struct hdcp2_cert_rx cert_rx;
+ u8 r_rx[HDCP_2_2_RRX_LEN];
+ u8 rx_caps[HDCP_2_2_RXCAPS_LEN];
+} __packed;
+
+struct wired_cmd_verify_receiver_cert_out {
+ struct hdcp_cmd_header header;
+ struct hdcp_port_id port;
+ u8 km_stored;
+ u8 reserved[3];
+ union encrypted_buff ekm_buff;
+} __packed;
+
+/* HECI struct for verification of Rx's Hprime in a HDCP Tx session */
+struct wired_cmd_ake_send_hprime_in {
+ struct hdcp_cmd_header header;
+ struct hdcp_port_id port;
+ u8 h_prime[HDCP_2_2_H_PRIME_LEN];
+} __packed;
+
+struct wired_cmd_ake_send_hprime_out {
+ struct hdcp_cmd_header header;
+ struct hdcp_port_id port;
+} __packed;
+
+/*
+ * HECI struct for sending in AKE pairing data generated by the Rx in an
+ * integrated wired HDCP Tx session.
+ */
+struct wired_cmd_ake_send_pairing_info_in {
+ struct hdcp_cmd_header header;
+ struct hdcp_port_id port;
+ u8 e_kh_km[HDCP_2_2_E_KH_KM_LEN];
+} __packed;
+
+struct wired_cmd_ake_send_pairing_info_out {
+ struct hdcp_cmd_header header;
+ struct hdcp_port_id port;
+} __packed;
+
+/* Data structures for integrated wired HDCP2 Tx in support of the LC protocol*/
+/*
+ * HECI struct for initiating locality check with an
+ * integrated wired HDCP Tx session.
+ */
+struct wired_cmd_init_locality_check_in {
+ struct hdcp_cmd_header header;
+ struct hdcp_port_id port;
+} __packed;
+
+struct wired_cmd_init_locality_check_out {
+ struct hdcp_cmd_header header;
+ struct hdcp_port_id port;
+ u8 r_n[HDCP_2_2_RN_LEN];
+} __packed;
+
+/*
+ * HECI struct for validating an Rx's LPrime value in an
+ * integrated wired HDCP Tx session.
+ */
+struct wired_cmd_validate_locality_in {
+ struct hdcp_cmd_header header;
+ struct hdcp_port_id port;
+ u8 l_prime[HDCP_2_2_L_PRIME_LEN];
+} __packed;
+
+struct wired_cmd_validate_locality_out {
+ struct hdcp_cmd_header header;
+ struct hdcp_port_id port;
+} __packed;
+
+/*
+ * Data structures for integrated wired HDCP2 Tx in support of the
+ * SKE protocol
+ */
+/* HECI struct for creating session key */
+struct wired_cmd_get_session_key_in {
+ struct hdcp_cmd_header header;
+ struct hdcp_port_id port;
+} __packed;
+
+struct wired_cmd_get_session_key_out {
+ struct hdcp_cmd_header header;
+ struct hdcp_port_id port;
+ u8 e_dkey_ks[HDCP_2_2_E_DKEY_KS_LEN];
+ u8 r_iv[HDCP_2_2_RIV_LEN];
+} __packed;
+
+/* HECI struct for the Tx enable authentication command */
+struct wired_cmd_enable_auth_in {
+ struct hdcp_cmd_header header;
+ struct hdcp_port_id port;
+ u8 stream_type;
+} __packed;
+
+struct wired_cmd_enable_auth_out {
+ struct hdcp_cmd_header header;
+ struct hdcp_port_id port;
+} __packed;
+
+/*
+ * Data structures for integrated wired HDCP2 Tx in support of
+ * the repeater protocols
+ */
+/*
+ * HECI struct for verifying the downstream repeater's HDCP topology in an
+ * integrated wired HDCP Tx session.
+ */
+struct wired_cmd_verify_repeater_in {
+ struct hdcp_cmd_header header;
+ struct hdcp_port_id port;
+ u8 rx_info[HDCP_2_2_RXINFO_LEN];
+ u8 seq_num_v[HDCP_2_2_SEQ_NUM_LEN];
+ u8 v_prime[HDCP_2_2_V_PRIME_HALF_LEN];
+ u8 receiver_ids[HDCP_2_2_RECEIVER_IDS_MAX_LEN];
+} __packed;
+
+struct wired_cmd_verify_repeater_out {
+ struct hdcp_cmd_header header;
+ struct hdcp_port_id port;
+ u8 content_type_supported;
+ u8 v[HDCP_2_2_V_PRIME_HALF_LEN];
+} __packed;
+
+/*
+ * HECI struct in support of stream management in an
+ * integrated wired HDCP Tx session.
+ */
+struct wired_cmd_repeater_auth_stream_req_in {
+ struct hdcp_cmd_header header;
+ struct hdcp_port_id port;
+ u8 seq_num_m[HDCP_2_2_SEQ_NUM_LEN];
+ u8 m_prime[HDCP_2_2_MPRIME_LEN];
+ u16 k;
+ struct hdcp2_streamid_type streams[1];
+} __packed;
+
+struct wired_cmd_repeater_auth_stream_req_out {
+ struct hdcp_cmd_header header;
+ struct hdcp_port_id port;
+} __packed;
+
+#endif /* __MEI_HDCP_H__ */
--
2.7.4
Ramalingam C
2018-11-27 10:43:11 UTC
Permalink
Implements the HDMI adaptation specific HDCP2.2 operations.

Basically these are DDC read and write for authenticating through
HDCP2.2 messages.

v2:
Rebased.
v3:
No Changes.
v4:
No more special handling of Gmbus burst read for AKE_SEND_CERT.
Style fixed with few naming. [Uma]
%s/PARING/PAIRING
v5:
msg_sz is initialized at definition.
Lookup table is defined for HDMI HDCP2.2 msgs [Daniel].
v6:
Rebased.
v7:
Make a function as inline [Uma]
%s/uintxx_t/uxx
v8:
Errors due to sinks are reported as DEBUG logs.
Adjust to the new mei interface.

Signed-off-by: Ramalingam C <***@intel.com>
Reviewed-by: Uma Shankar <***@intel.com>
---
drivers/gpu/drm/i915/intel_hdmi.c | 190 ++++++++++++++++++++++++++++++++++++++
1 file changed, 190 insertions(+)

diff --git a/drivers/gpu/drm/i915/intel_hdmi.c b/drivers/gpu/drm/i915/intel_hdmi.c
index e755a3370bca..912f50133645 100644
--- a/drivers/gpu/drm/i915/intel_hdmi.c
+++ b/drivers/gpu/drm/i915/intel_hdmi.c
@@ -30,6 +30,7 @@
#include <linux/slab.h>
#include <linux/delay.h>
#include <linux/hdmi.h>
+#include <linux/mei_hdcp.h>
#include <drm/drmP.h>
#include <drm/drm_atomic_helper.h>
#include <drm/drm_crtc.h>
@@ -1117,6 +1118,190 @@ bool intel_hdmi_hdcp_check_link(struct intel_digital_port *intel_dig_port)
return true;
}

+static struct hdcp2_hdmi_msg_data {
+ u8 msg_id;
+ u32 timeout;
+ u32 timeout2;
+ } hdcp2_msg_data[] = {
+ {HDCP_2_2_AKE_INIT, 0, 0},
+ {HDCP_2_2_AKE_SEND_CERT, HDCP_2_2_CERT_TIMEOUT_MS, 0},
+ {HDCP_2_2_AKE_NO_STORED_KM, 0, 0},
+ {HDCP_2_2_AKE_STORED_KM, 0, 0},
+ {HDCP_2_2_AKE_SEND_HPRIME, HDCP_2_2_HPRIME_PAIRED_TIMEOUT_MS,
+ HDCP_2_2_HPRIME_NO_PAIRED_TIMEOUT_MS},
+ {HDCP_2_2_AKE_SEND_PAIRING_INFO, HDCP_2_2_PAIRING_TIMEOUT_MS,
+ 0},
+ {HDCP_2_2_LC_INIT, 0, 0},
+ {HDCP_2_2_LC_SEND_LPRIME, HDCP_2_2_HDMI_LPRIME_TIMEOUT_MS, 0},
+ {HDCP_2_2_SKE_SEND_EKS, 0, 0},
+ {HDCP_2_2_REP_SEND_RECVID_LIST,
+ HDCP_2_2_RECVID_LIST_TIMEOUT_MS, 0},
+ {HDCP_2_2_REP_SEND_ACK, 0, 0},
+ {HDCP_2_2_REP_STREAM_MANAGE, 0, 0},
+ {HDCP_2_2_REP_STREAM_READY, HDCP_2_2_STREAM_READY_TIMEOUT_MS,
+ 0},
+ };
+
+static
+int intel_hdmi_hdcp2_read_rx_status(struct intel_digital_port *intel_dig_port,
+ uint8_t *rx_status)
+{
+ return intel_hdmi_hdcp_read(intel_dig_port,
+ HDCP_2_2_HDMI_REG_RXSTATUS_OFFSET,
+ rx_status,
+ HDCP_2_2_HDMI_RXSTATUS_LEN);
+}
+
+static int get_hdcp2_msg_timeout(u8 msg_id, bool is_paired)
+{
+ int i;
+
+ for (i = 0; i < sizeof(hdcp2_msg_data); i++)
+ if (hdcp2_msg_data[i].msg_id == msg_id &&
+ (msg_id != HDCP_2_2_AKE_SEND_HPRIME || is_paired))
+ return hdcp2_msg_data[i].timeout;
+ else if (hdcp2_msg_data[i].msg_id == msg_id)
+ return hdcp2_msg_data[i].timeout2;
+
+ return -EINVAL;
+}
+
+static inline
+int hdcp2_detect_msg_availability(struct intel_digital_port *intel_digital_port,
+ u8 msg_id, bool *msg_ready,
+ ssize_t *msg_sz)
+{
+ u8 rx_status[HDCP_2_2_HDMI_RXSTATUS_LEN];
+ int ret;
+
+ ret = intel_hdmi_hdcp2_read_rx_status(intel_digital_port, rx_status);
+ if (ret < 0) {
+ DRM_DEBUG_KMS("rx_status read failed. Err %d\n", ret);
+ return ret;
+ }
+
+ *msg_sz = ((HDCP_2_2_HDMI_RXSTATUS_MSG_SZ_HI(rx_status[1]) << 8) |
+ rx_status[0]);
+
+ if (msg_id == HDCP_2_2_REP_SEND_RECVID_LIST)
+ *msg_ready = (HDCP_2_2_HDMI_RXSTATUS_READY(rx_status[1]) &&
+ *msg_sz);
+ else
+ *msg_ready = *msg_sz;
+
+ return 0;
+}
+
+static ssize_t
+intel_hdmi_hdcp2_wait_for_msg(struct intel_digital_port *intel_dig_port,
+ u8 msg_id, bool paired)
+{
+ bool msg_ready = false;
+ int timeout, ret;
+ ssize_t msg_sz = 0;
+
+ timeout = get_hdcp2_msg_timeout(msg_id, paired);
+ if (timeout < 0)
+ return timeout;
+
+ ret = __wait_for(ret = hdcp2_detect_msg_availability(intel_dig_port,
+ msg_id, &msg_ready,
+ &msg_sz),
+ !ret && msg_ready && msg_sz, timeout * 1000,
+ 1000, 5 * 1000);
+ if (ret)
+ DRM_DEBUG_KMS("msg_id: %d, ret: %d, timeout: %d\n",
+ msg_id, ret, timeout);
+
+ return ret ? ret : msg_sz;
+}
+
+static
+int intel_hdmi_hdcp2_write_msg(struct intel_digital_port *intel_dig_port,
+ void *buf, size_t size)
+{
+ unsigned int offset;
+
+ offset = HDCP_2_2_HDMI_REG_WR_MSG_OFFSET;
+ return intel_hdmi_hdcp_write(intel_dig_port, offset, buf, size);
+}
+
+static
+int intel_hdmi_hdcp2_read_msg(struct intel_digital_port *intel_dig_port,
+ u8 msg_id, void *buf, size_t size)
+{
+ struct intel_hdmi *hdmi = &intel_dig_port->hdmi;
+ struct intel_hdcp *hdcp = &hdmi->attached_connector->hdcp;
+ unsigned int offset;
+ ssize_t ret;
+
+ ret = intel_hdmi_hdcp2_wait_for_msg(intel_dig_port, msg_id,
+ hdcp->is_paired);
+ if (ret < 0)
+ return ret;
+
+ /*
+ * Available msg size should be equal to or lesser than the
+ * available buffer.
+ */
+ if (ret > size) {
+ DRM_DEBUG_KMS("msg_sz(%zd) is more than exp size(%zu)\n",
+ ret, size);
+ return -1;
+ }
+
+ offset = HDCP_2_2_HDMI_REG_RD_MSG_OFFSET;
+ ret = intel_hdmi_hdcp_read(intel_dig_port, offset, buf, ret);
+ if (ret)
+ DRM_DEBUG_KMS("Failed to read msg_id: %d(%zd)\n", msg_id, ret);
+
+ return ret;
+}
+
+static
+int intel_hdmi_hdcp2_check_link(struct intel_digital_port *intel_dig_port)
+{
+ u8 rx_status[HDCP_2_2_HDMI_RXSTATUS_LEN];
+ int ret;
+
+ ret = intel_hdmi_hdcp2_read_rx_status(intel_dig_port, rx_status);
+ if (ret)
+ return ret;
+
+ /*
+ * Re-auth request and Link Integrity Failures are represented by
+ * same bit. i.e reauth_req.
+ */
+ if (HDCP_2_2_HDMI_RXSTATUS_REAUTH_REQ(rx_status[1]))
+ ret = DRM_HDCP_REAUTH_REQUEST;
+ else if (HDCP_2_2_HDMI_RXSTATUS_READY(rx_status[1]))
+ ret = DRM_HDCP_TOPOLOGY_CHANGE;
+
+ return ret;
+}
+
+static
+int intel_hdmi_hdcp2_capable(struct intel_digital_port *intel_dig_port,
+ bool *capable)
+{
+ u8 hdcp2_version;
+ int ret;
+
+ *capable = false;
+ ret = intel_hdmi_hdcp_read(intel_dig_port, HDCP_2_2_HDMI_REG_VER_OFFSET,
+ &hdcp2_version, sizeof(hdcp2_version));
+ if (!ret && hdcp2_version & HDCP_2_2_HDMI_SUPPORT_MASK)
+ *capable = true;
+
+ return ret;
+}
+
+static inline
+enum mei_hdcp_wired_protocol intel_hdmi_hdcp2_protocol(void)
+{
+ return MEI_HDCP_PROTOCOL_HDMI;
+}
+
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,
@@ -1128,6 +1313,11 @@ static const struct intel_hdcp_shim intel_hdmi_hdcp_shim = {
.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,
+ .write_2_2_msg = intel_hdmi_hdcp2_write_msg,
+ .read_2_2_msg = intel_hdmi_hdcp2_read_msg,
+ .check_2_2_link = intel_hdmi_hdcp2_check_link,
+ .hdcp_2_2_capable = intel_hdmi_hdcp2_capable,
+ .hdcp_protocol = intel_hdmi_hdcp2_protocol,
};

static void intel_hdmi_prepare(struct intel_encoder *encoder,
--
2.7.4
Daniel Vetter
2018-12-06 14:04:05 UTC
Permalink
Post by Ramalingam C
Implements the HDMI adaptation specific HDCP2.2 operations.
Basically these are DDC read and write for authenticating through
HDCP2.2 messages.
Rebased.
No Changes.
No more special handling of Gmbus burst read for AKE_SEND_CERT.
Style fixed with few naming. [Uma]
%s/PARING/PAIRING
msg_sz is initialized at definition.
Lookup table is defined for HDMI HDCP2.2 msgs [Daniel].
Rebased.
Make a function as inline [Uma]
%s/uintxx_t/uxx
Errors due to sinks are reported as DEBUG logs.
Adjust to the new mei interface.
---
drivers/gpu/drm/i915/intel_hdmi.c | 190 ++++++++++++++++++++++++++++++++++++++
1 file changed, 190 insertions(+)
diff --git a/drivers/gpu/drm/i915/intel_hdmi.c b/drivers/gpu/drm/i915/intel_hdmi.c
index e755a3370bca..912f50133645 100644
--- a/drivers/gpu/drm/i915/intel_hdmi.c
+++ b/drivers/gpu/drm/i915/intel_hdmi.c
@@ -30,6 +30,7 @@
#include <linux/slab.h>
#include <linux/delay.h>
#include <linux/hdmi.h>
+#include <linux/mei_hdcp.h>
#include <drm/drmP.h>
#include <drm/drm_atomic_helper.h>
#include <drm/drm_crtc.h>
@@ -1117,6 +1118,190 @@ bool intel_hdmi_hdcp_check_link(struct intel_digital_port *intel_dig_port)
return true;
}
+static struct hdcp2_hdmi_msg_data {
+ u8 msg_id;
+ u32 timeout;
+ u32 timeout2;
+ } hdcp2_msg_data[] = {
+ {HDCP_2_2_AKE_INIT, 0, 0},
+ {HDCP_2_2_AKE_SEND_CERT, HDCP_2_2_CERT_TIMEOUT_MS, 0},
+ {HDCP_2_2_AKE_NO_STORED_KM, 0, 0},
+ {HDCP_2_2_AKE_STORED_KM, 0, 0},
+ {HDCP_2_2_AKE_SEND_HPRIME, HDCP_2_2_HPRIME_PAIRED_TIMEOUT_MS,
+ HDCP_2_2_HPRIME_NO_PAIRED_TIMEOUT_MS},
+ {HDCP_2_2_AKE_SEND_PAIRING_INFO, HDCP_2_2_PAIRING_TIMEOUT_MS,
+ 0},
+ {HDCP_2_2_LC_INIT, 0, 0},
+ {HDCP_2_2_LC_SEND_LPRIME, HDCP_2_2_HDMI_LPRIME_TIMEOUT_MS, 0},
+ {HDCP_2_2_SKE_SEND_EKS, 0, 0},
+ {HDCP_2_2_REP_SEND_RECVID_LIST,
+ HDCP_2_2_RECVID_LIST_TIMEOUT_MS, 0},
+ {HDCP_2_2_REP_SEND_ACK, 0, 0},
+ {HDCP_2_2_REP_STREAM_MANAGE, 0, 0},
+ {HDCP_2_2_REP_STREAM_READY, HDCP_2_2_STREAM_READY_TIMEOUT_MS,
+ 0},
+ };
+
+static
+int intel_hdmi_hdcp2_read_rx_status(struct intel_digital_port *intel_dig_port,
+ uint8_t *rx_status)
+{
+ return intel_hdmi_hdcp_read(intel_dig_port,
+ HDCP_2_2_HDMI_REG_RXSTATUS_OFFSET,
+ rx_status,
+ HDCP_2_2_HDMI_RXSTATUS_LEN);
+}
+
+static int get_hdcp2_msg_timeout(u8 msg_id, bool is_paired)
+{
+ int i;
+
+ for (i = 0; i < sizeof(hdcp2_msg_data); i++)
+ if (hdcp2_msg_data[i].msg_id == msg_id &&
+ (msg_id != HDCP_2_2_AKE_SEND_HPRIME || is_paired))
+ return hdcp2_msg_data[i].timeout;
+ else if (hdcp2_msg_data[i].msg_id == msg_id)
+ return hdcp2_msg_data[i].timeout2;
+
+ return -EINVAL;
+}
+
+static inline
+int hdcp2_detect_msg_availability(struct intel_digital_port *intel_digital_port,
+ u8 msg_id, bool *msg_ready,
+ ssize_t *msg_sz)
+{
+ u8 rx_status[HDCP_2_2_HDMI_RXSTATUS_LEN];
+ int ret;
+
+ ret = intel_hdmi_hdcp2_read_rx_status(intel_digital_port, rx_status);
+ if (ret < 0) {
+ DRM_DEBUG_KMS("rx_status read failed. Err %d\n", ret);
+ return ret;
+ }
+
+ *msg_sz = ((HDCP_2_2_HDMI_RXSTATUS_MSG_SZ_HI(rx_status[1]) << 8) |
+ rx_status[0]);
+
+ if (msg_id == HDCP_2_2_REP_SEND_RECVID_LIST)
+ *msg_ready = (HDCP_2_2_HDMI_RXSTATUS_READY(rx_status[1]) &&
+ *msg_sz);
+ else
+ *msg_ready = *msg_sz;
+
+ return 0;
+}
+
+static ssize_t
+intel_hdmi_hdcp2_wait_for_msg(struct intel_digital_port *intel_dig_port,
+ u8 msg_id, bool paired)
+{
+ bool msg_ready = false;
+ int timeout, ret;
+ ssize_t msg_sz = 0;
+
+ timeout = get_hdcp2_msg_timeout(msg_id, paired);
+ if (timeout < 0)
+ return timeout;
+
+ ret = __wait_for(ret = hdcp2_detect_msg_availability(intel_dig_port,
+ msg_id, &msg_ready,
+ &msg_sz),
+ !ret && msg_ready && msg_sz, timeout * 1000,
+ 1000, 5 * 1000);
Yeah, doing this also for DP is imo much simpler.

lgtm.
-Daniel
Post by Ramalingam C
+ if (ret)
+ DRM_DEBUG_KMS("msg_id: %d, ret: %d, timeout: %d\n",
+ msg_id, ret, timeout);
+
+ return ret ? ret : msg_sz;
+}
+
+static
+int intel_hdmi_hdcp2_write_msg(struct intel_digital_port *intel_dig_port,
+ void *buf, size_t size)
+{
+ unsigned int offset;
+
+ offset = HDCP_2_2_HDMI_REG_WR_MSG_OFFSET;
+ return intel_hdmi_hdcp_write(intel_dig_port, offset, buf, size);
+}
+
+static
+int intel_hdmi_hdcp2_read_msg(struct intel_digital_port *intel_dig_port,
+ u8 msg_id, void *buf, size_t size)
+{
+ struct intel_hdmi *hdmi = &intel_dig_port->hdmi;
+ struct intel_hdcp *hdcp = &hdmi->attached_connector->hdcp;
+ unsigned int offset;
+ ssize_t ret;
+
+ ret = intel_hdmi_hdcp2_wait_for_msg(intel_dig_port, msg_id,
+ hdcp->is_paired);
+ if (ret < 0)
+ return ret;
+
+ /*
+ * Available msg size should be equal to or lesser than the
+ * available buffer.
+ */
+ if (ret > size) {
+ DRM_DEBUG_KMS("msg_sz(%zd) is more than exp size(%zu)\n",
+ ret, size);
+ return -1;
+ }
+
+ offset = HDCP_2_2_HDMI_REG_RD_MSG_OFFSET;
+ ret = intel_hdmi_hdcp_read(intel_dig_port, offset, buf, ret);
+ if (ret)
+ DRM_DEBUG_KMS("Failed to read msg_id: %d(%zd)\n", msg_id, ret);
+
+ return ret;
+}
+
+static
+int intel_hdmi_hdcp2_check_link(struct intel_digital_port *intel_dig_port)
+{
+ u8 rx_status[HDCP_2_2_HDMI_RXSTATUS_LEN];
+ int ret;
+
+ ret = intel_hdmi_hdcp2_read_rx_status(intel_dig_port, rx_status);
+ if (ret)
+ return ret;
+
+ /*
+ * Re-auth request and Link Integrity Failures are represented by
+ * same bit. i.e reauth_req.
+ */
+ if (HDCP_2_2_HDMI_RXSTATUS_REAUTH_REQ(rx_status[1]))
+ ret = DRM_HDCP_REAUTH_REQUEST;
+ else if (HDCP_2_2_HDMI_RXSTATUS_READY(rx_status[1]))
+ ret = DRM_HDCP_TOPOLOGY_CHANGE;
+
+ return ret;
+}
+
+static
+int intel_hdmi_hdcp2_capable(struct intel_digital_port *intel_dig_port,
+ bool *capable)
+{
+ u8 hdcp2_version;
+ int ret;
+
+ *capable = false;
+ ret = intel_hdmi_hdcp_read(intel_dig_port, HDCP_2_2_HDMI_REG_VER_OFFSET,
+ &hdcp2_version, sizeof(hdcp2_version));
+ if (!ret && hdcp2_version & HDCP_2_2_HDMI_SUPPORT_MASK)
+ *capable = true;
+
+ return ret;
+}
+
+static inline
+enum mei_hdcp_wired_protocol intel_hdmi_hdcp2_protocol(void)
+{
+ return MEI_HDCP_PROTOCOL_HDMI;
+}
+
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,
@@ -1128,6 +1313,11 @@ static const struct intel_hdcp_shim intel_hdmi_hdcp_shim = {
.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,
+ .write_2_2_msg = intel_hdmi_hdcp2_write_msg,
+ .read_2_2_msg = intel_hdmi_hdcp2_read_msg,
+ .check_2_2_link = intel_hdmi_hdcp2_check_link,
+ .hdcp_2_2_capable = intel_hdmi_hdcp2_capable,
+ .hdcp_protocol = intel_hdmi_hdcp2_protocol,
};
static void intel_hdmi_prepare(struct intel_encoder *encoder,
--
2.7.4
--
Daniel Vetter
Software Engineer, Intel Corporation
http://blog.ffwll.ch
Ramalingam C
2018-11-27 10:43:15 UTC
Permalink
From: Tomas Winkler <***@intel.com>

Export to_mei_cl_device macro, it is needed also in mei client drivers.

Signed-off-by: Tomas Winkler <***@intel.com>
---
drivers/misc/mei/bus.c | 1 -
include/linux/mei_cl_bus.h | 2 ++
2 files changed, 2 insertions(+), 1 deletion(-)

diff --git a/drivers/misc/mei/bus.c b/drivers/misc/mei/bus.c
index fc3872fe7b25..e5456faf00e6 100644
--- a/drivers/misc/mei/bus.c
+++ b/drivers/misc/mei/bus.c
@@ -28,7 +28,6 @@
#include "client.h"

#define to_mei_cl_driver(d) container_of(d, struct mei_cl_driver, driver)
-#define to_mei_cl_device(d) container_of(d, struct mei_cl_device, dev)

/**
* __mei_cl_send - internal client send (write)
diff --git a/include/linux/mei_cl_bus.h b/include/linux/mei_cl_bus.h
index 7fde40e17c8b..03b6ba2a63f8 100644
--- a/include/linux/mei_cl_bus.h
+++ b/include/linux/mei_cl_bus.h
@@ -55,6 +55,8 @@ struct mei_cl_device {
void *priv_data;
};

+#define to_mei_cl_device(d) container_of(d, struct mei_cl_device, dev)
+
struct mei_cl_driver {
struct device_driver driver;
const char *name;
--
2.7.4
Ramalingam C
2018-11-27 10:43:04 UTC
Permalink
Considering that HDCP2.2 is more secure than HDCP1.4, When a setup
supports HDCP2.2 and HDCP1.4, HDCP2.2 will be enabled.

When HDCP2.2 enabling fails and HDCP1.4 is supported, HDCP1.4 is
enabled.

This change implements a sequence of enabling and disabling of
HDCP2.2 authentication and HDCP2.2 port encryption.

v2:
Included few optimization suggestions [Chris Wilson]
Commit message is updated as per the rebased version.
intel_wait_for_register is used instead of wait_for. [Chris Wilson]
v3:
No changes.
v4:
Extra comment added and Style issue fixed [Uma]
v5:
Rebased as part of patch reordering.
HDCP2 encryption status is tracked.
HW state check is moved into WARN_ON [Daniel]
v6:
Redefined the mei service functions as per comp redesign.
Merged patches related to hdcp2.2 enabling and disabling [Sean Paul].
Required shim functionality is defined [Sean Paul]
v7:
Return values are handles [Uma]
Realigned the code.
Check for comp_master is removed.
v8:
HDCP2.2 is attempted only if mei interface is up.
Adjust to the new interface
Avoid bool usage in struct [Tomas]

Signed-off-by: Ramalingam C <***@intel.com>
---
drivers/gpu/drm/i915/intel_drv.h | 5 +
drivers/gpu/drm/i915/intel_hdcp.c | 223 +++++++++++++++++++++++++++++++++++---
2 files changed, 214 insertions(+), 14 deletions(-)

diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h
index bde82f3ada85..3e9f21d23442 100644
--- a/drivers/gpu/drm/i915/intel_drv.h
+++ b/drivers/gpu/drm/i915/intel_drv.h
@@ -383,6 +383,10 @@ struct intel_hdcp_shim {

/* Detects the HDCP protocol(DP/HDMI) required on the port */
enum mei_hdcp_wired_protocol (*hdcp_protocol)(void);
+
+ /* Detects whether Panel is HDCP2.2 capable */
+ int (*hdcp_2_2_capable)(struct intel_digital_port *intel_dig_port,
+ bool *capable);
};

struct intel_hdcp {
@@ -396,6 +400,7 @@ struct intel_hdcp {
/* HDCP2.2 related definitions */
/* Flag indicates whether this connector supports HDCP2.2 or not. */
u8 hdcp2_supported;
+ u8 hdcp2_in_use;

/*
* Content Stream Type defined by content owner. TYPE0(0x0) content can
diff --git a/drivers/gpu/drm/i915/intel_hdcp.c b/drivers/gpu/drm/i915/intel_hdcp.c
index 760780f1105c..c1bd1ccd47cd 100644
--- a/drivers/gpu/drm/i915/intel_hdcp.c
+++ b/drivers/gpu/drm/i915/intel_hdcp.c
@@ -79,6 +79,43 @@ bool intel_hdcp_capable(struct intel_connector *connector)
return capable;
}

+/* At present whether mei_hdcp component is binded with i915 master component */
+static bool intel_hdcp2_mei_binded(struct intel_connector *connector)
+{
+ struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
+ struct i915_hdcp_component_master *comp = dev_priv->hdcp_comp;
+
+ mutex_lock(&comp->mutex);
+ if (!comp->ops || !comp->dev) {
+ mutex_unlock(&comp->mutex);
+ return false;
+ }
+ mutex_unlock(&comp->mutex);
+
+ return true;
+}
+
+/* Is HDCP2.2 capable on Platform and Sink */
+static bool intel_hdcp2_capable(struct intel_connector *connector)
+{
+ struct intel_digital_port *intel_dig_port = conn_to_dig_port(connector);
+ struct intel_hdcp *hdcp = &connector->hdcp;
+ bool capable = false;
+
+ /* I915 support for HDCP2.2 */
+ if (!hdcp->hdcp2_supported)
+ return false;
+
+ /* MEI services for HDCP2.2 */
+ if (!intel_hdcp2_mei_binded(connector))
+ return false;
+
+ /* Sink's capability for HDCP2.2 */
+ hdcp->shim->hdcp_2_2_capable(intel_dig_port, &capable);
+
+ return capable;
+}
+
static int intel_hdcp_poll_ksv_fifo(struct intel_digital_port *intel_dig_port,
const struct intel_hdcp_shim *shim)
{
@@ -1114,8 +1151,7 @@ int hdcp2_authenticate_port(struct intel_connector *connector)
return ret;
}

-static __attribute__((unused))
-int hdcp2_close_mei_session(struct intel_connector *connector)
+static int hdcp2_close_mei_session(struct intel_connector *connector)
{
struct mei_hdcp_data *data = &connector->hdcp.mei_data;
struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
@@ -1137,12 +1173,157 @@ int hdcp2_close_mei_session(struct intel_connector *connector)
return ret;
}

-static __attribute__((unused))
-int hdcp2_deauthenticate_port(struct intel_connector *connector)
+static int hdcp2_deauthenticate_port(struct intel_connector *connector)
{
return hdcp2_close_mei_session(connector);
}

+static int hdcp2_authenticate_sink(struct intel_connector *connector)
+{
+ DRM_ERROR("Sink authentication is done in subsequent patches\n");
+
+ return -EINVAL;
+}
+
+static int hdcp2_enable_encryption(struct intel_connector *connector)
+{
+ struct intel_digital_port *intel_dig_port = conn_to_dig_port(connector);
+ struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
+ struct intel_hdcp *hdcp = &connector->hdcp;
+ enum port port = connector->encoder->port;
+ int ret;
+
+ WARN_ON(I915_READ(HDCP2_STATUS_DDI(port)) & LINK_ENCRYPTION_STATUS);
+
+ if (hdcp->shim->toggle_signalling) {
+ ret = hdcp->shim->toggle_signalling(intel_dig_port, true);
+ if (ret) {
+ DRM_ERROR("Failed to enable HDCP signalling. %d\n",
+ ret);
+ return ret;
+ }
+ }
+
+ if (I915_READ(HDCP2_STATUS_DDI(port)) & LINK_AUTH_STATUS) {
+ /* Link is Authenticated. Now set for Encryption */
+ I915_WRITE(HDCP2_CTL_DDI(port),
+ I915_READ(HDCP2_CTL_DDI(port)) |
+ CTL_LINK_ENCRYPTION_REQ);
+ }
+
+ ret = intel_wait_for_register(dev_priv, HDCP2_STATUS_DDI(port),
+ LINK_ENCRYPTION_STATUS,
+ LINK_ENCRYPTION_STATUS,
+ TIME_FOR_ENCRYPT_STATUS_CHANGE);
+
+ return ret;
+}
+
+static int hdcp2_disable_encryption(struct intel_connector *connector)
+{
+ struct intel_digital_port *intel_dig_port = conn_to_dig_port(connector);
+ struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
+ struct intel_hdcp *hdcp = &connector->hdcp;
+ enum port port = connector->encoder->port;
+ int ret;
+
+ WARN_ON(!(I915_READ(HDCP2_STATUS_DDI(port)) & LINK_ENCRYPTION_STATUS));
+
+ I915_WRITE(HDCP2_CTL_DDI(port),
+ I915_READ(HDCP2_CTL_DDI(port)) & ~CTL_LINK_ENCRYPTION_REQ);
+
+ ret = intel_wait_for_register(dev_priv, HDCP2_STATUS_DDI(port),
+ LINK_ENCRYPTION_STATUS, 0x0,
+ TIME_FOR_ENCRYPT_STATUS_CHANGE);
+ if (ret == -ETIMEDOUT)
+ DRM_DEBUG_KMS("Disable Encryption Timedout");
+
+ if (hdcp->shim->toggle_signalling) {
+ ret = hdcp->shim->toggle_signalling(intel_dig_port, false);
+ if (ret) {
+ DRM_ERROR("Failed to disable HDCP signalling. %d\n",
+ ret);
+ return ret;
+ }
+ }
+
+ return ret;
+}
+
+static int hdcp2_authenticate_and_encrypt(struct intel_connector *connector)
+{
+ int ret, i, tries = 3;
+
+ for (i = 0; i < tries; i++) {
+ ret = hdcp2_authenticate_sink(connector);
+ if (!ret)
+ break;
+
+ /* Clearing the mei hdcp session */
+ DRM_DEBUG_KMS("HDCP2.2 Auth %d of %d Failed.(%d)\n",
+ i + 1, tries, ret);
+ if (hdcp2_deauthenticate_port(connector) < 0)
+ DRM_DEBUG_KMS("Port deauth failed.\n");
+ }
+
+ if (i != tries) {
+ /*
+ * Ensuring the required 200mSec min time interval between
+ * Session Key Exchange and encryption.
+ */
+ msleep(HDCP_2_2_DELAY_BEFORE_ENCRYPTION_EN);
+ ret = hdcp2_enable_encryption(connector);
+ if (ret < 0) {
+ DRM_DEBUG_KMS("Encryption Enable Failed.(%d)\n", ret);
+ if (hdcp2_deauthenticate_port(connector) < 0)
+ DRM_DEBUG_KMS("Port deauth failed.\n");
+ }
+ }
+
+ return ret;
+}
+
+static int _intel_hdcp2_enable(struct intel_connector *connector)
+{
+ struct intel_hdcp *hdcp = &connector->hdcp;
+ int ret;
+
+ DRM_DEBUG_KMS("[%s:%d] HDCP2.2 is being enabled. Type: %d\n",
+ connector->base.name, connector->base.base.id,
+ hdcp->content_type);
+
+ ret = hdcp2_authenticate_and_encrypt(connector);
+ if (ret) {
+ DRM_DEBUG_KMS("HDCP2 Type%d Enabling Failed. (%d)\n",
+ hdcp->content_type, ret);
+ return ret;
+ }
+
+ DRM_DEBUG_KMS("[%s:%d] HDCP2.2 is enabled. Type %d\n",
+ connector->base.name, connector->base.base.id,
+ hdcp->content_type);
+
+ hdcp->hdcp2_in_use = true;
+ return 0;
+}
+
+static int _intel_hdcp2_disable(struct intel_connector *connector)
+{
+ int ret;
+
+ DRM_DEBUG_KMS("[%s:%d] HDCP2.2 is being Disabled\n",
+ connector->base.name, connector->base.base.id);
+
+ ret = hdcp2_disable_encryption(connector);
+
+ if (hdcp2_deauthenticate_port(connector) < 0)
+ DRM_DEBUG_KMS("Port deauth failed.\n");
+
+ connector->hdcp.hdcp2_in_use = false;
+
+ return ret;
+}
+
static int i915_hdcp_component_master_bind(struct device *dev)
{
struct drm_i915_private *dev_priv = kdev_to_i915(dev);
@@ -1342,22 +1523,33 @@ void intel_hdcp_exit(struct drm_i915_private *dev_priv)
int intel_hdcp_enable(struct intel_connector *connector)
{
struct intel_hdcp *hdcp = &connector->hdcp;
- int ret;
+ int ret = -EINVAL;

if (!hdcp->shim)
return -ENOENT;

mutex_lock(&hdcp->mutex);

- ret = _intel_hdcp_enable(connector);
- if (ret)
- goto out;
+ /*
+ * Considering that HDCP2.2 is more secure than HDCP1.4, If the setup
+ * is capable of HDCP2.2, it is preferred to use HDCP2.2.
+ */
+ if (intel_hdcp2_capable(connector))
+ ret = _intel_hdcp2_enable(connector);
+
+ /* When HDCP2.2 fails, HDCP1.4 will be attempted */
+ if (ret && intel_hdcp_capable(connector)) {
+ ret = _intel_hdcp_enable(connector);
+ if (!ret)
+ schedule_delayed_work(&hdcp->check_work,
+ DRM_HDCP_CHECK_PERIOD_MS);
+ }
+
+ if (!ret) {
+ hdcp->value = DRM_MODE_CONTENT_PROTECTION_ENABLED;
+ schedule_work(&hdcp->prop_work);
+ }

- hdcp->value = DRM_MODE_CONTENT_PROTECTION_ENABLED;
- schedule_work(&hdcp->prop_work);
- schedule_delayed_work(&hdcp->check_work,
- DRM_HDCP_CHECK_PERIOD_MS);
-out:
mutex_unlock(&hdcp->mutex);
return ret;
}
@@ -1374,7 +1566,10 @@ int intel_hdcp_disable(struct intel_connector *connector)

if (hdcp->value != DRM_MODE_CONTENT_PROTECTION_UNDESIRED) {
hdcp->value = DRM_MODE_CONTENT_PROTECTION_UNDESIRED;
- ret = _intel_hdcp_disable(connector);
+ if (hdcp->hdcp2_in_use)
+ ret = _intel_hdcp2_disable(connector);
+ else
+ ret = _intel_hdcp_disable(connector);
}

mutex_unlock(&hdcp->mutex);
--
2.7.4
Daniel Vetter
2018-12-06 10:30:42 UTC
Permalink
Post by Ramalingam C
Considering that HDCP2.2 is more secure than HDCP1.4, When a setup
supports HDCP2.2 and HDCP1.4, HDCP2.2 will be enabled.
When HDCP2.2 enabling fails and HDCP1.4 is supported, HDCP1.4 is
enabled.
This change implements a sequence of enabling and disabling of
HDCP2.2 authentication and HDCP2.2 port encryption.
Patch series suggestion for next time around: First build out the helper
functions, then time them into the big picture like here. Personally I
think that makes it easier to understand, but it's kinda personal choice.
I guess this here works too.
Post by Ramalingam C
Included few optimization suggestions [Chris Wilson]
Commit message is updated as per the rebased version.
intel_wait_for_register is used instead of wait_for. [Chris Wilson]
No changes.
Extra comment added and Style issue fixed [Uma]
Rebased as part of patch reordering.
HDCP2 encryption status is tracked.
HW state check is moved into WARN_ON [Daniel]
Redefined the mei service functions as per comp redesign.
Merged patches related to hdcp2.2 enabling and disabling [Sean Paul].
Required shim functionality is defined [Sean Paul]
Return values are handles [Uma]
Realigned the code.
Check for comp_master is removed.
HDCP2.2 is attempted only if mei interface is up.
Adjust to the new interface
Avoid bool usage in struct [Tomas]
---
drivers/gpu/drm/i915/intel_drv.h | 5 +
drivers/gpu/drm/i915/intel_hdcp.c | 223 +++++++++++++++++++++++++++++++++++---
2 files changed, 214 insertions(+), 14 deletions(-)
diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h
index bde82f3ada85..3e9f21d23442 100644
--- a/drivers/gpu/drm/i915/intel_drv.h
+++ b/drivers/gpu/drm/i915/intel_drv.h
@@ -383,6 +383,10 @@ struct intel_hdcp_shim {
/* Detects the HDCP protocol(DP/HDMI) required on the port */
enum mei_hdcp_wired_protocol (*hdcp_protocol)(void);
+
+ /* Detects whether Panel is HDCP2.2 capable */
+ int (*hdcp_2_2_capable)(struct intel_digital_port *intel_dig_port,
+ bool *capable);
};
struct intel_hdcp {
@@ -396,6 +400,7 @@ struct intel_hdcp {
/* HDCP2.2 related definitions */
/* Flag indicates whether this connector supports HDCP2.2 or not. */
u8 hdcp2_supported;
+ u8 hdcp2_in_use;
/*
* Content Stream Type defined by content owner. TYPE0(0x0) content can
diff --git a/drivers/gpu/drm/i915/intel_hdcp.c b/drivers/gpu/drm/i915/intel_hdcp.c
index 760780f1105c..c1bd1ccd47cd 100644
--- a/drivers/gpu/drm/i915/intel_hdcp.c
+++ b/drivers/gpu/drm/i915/intel_hdcp.c
@@ -79,6 +79,43 @@ bool intel_hdcp_capable(struct intel_connector *connector)
return capable;
}
+/* At present whether mei_hdcp component is binded with i915 master component */
+static bool intel_hdcp2_mei_binded(struct intel_connector *connector)
+{
+ struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
+ struct i915_hdcp_component_master *comp = dev_priv->hdcp_comp;
+
+ mutex_lock(&comp->mutex);
+ if (!comp->ops || !comp->dev) {
+ mutex_unlock(&comp->mutex);
+ return false;
+ }
+ mutex_unlock(&comp->mutex);
+
+ return true;
+}
+
+/* Is HDCP2.2 capable on Platform and Sink */
+static bool intel_hdcp2_capable(struct intel_connector *connector)
+{
+ struct intel_digital_port *intel_dig_port = conn_to_dig_port(connector);
+ struct intel_hdcp *hdcp = &connector->hdcp;
+ bool capable = false;
+
+ /* I915 support for HDCP2.2 */
+ if (!hdcp->hdcp2_supported)
+ return false;
+
+ /* MEI services for HDCP2.2 */
+ if (!intel_hdcp2_mei_binded(connector))
+ return false;
Why do we still need this with component? Driver load should be stalled
out until it's all there, that was kinda the entire point of component,
so we don't have to recheck all the time whether hdcp2 is still there or
not.
Post by Ramalingam C
+
+ /* Sink's capability for HDCP2.2 */
+ hdcp->shim->hdcp_2_2_capable(intel_dig_port, &capable);
+
+ return capable;
+}
+
static int intel_hdcp_poll_ksv_fifo(struct intel_digital_port *intel_dig_port,
const struct intel_hdcp_shim *shim)
{
@@ -1114,8 +1151,7 @@ int hdcp2_authenticate_port(struct intel_connector *connector)
return ret;
}
-static __attribute__((unused))
-int hdcp2_close_mei_session(struct intel_connector *connector)
+static int hdcp2_close_mei_session(struct intel_connector *connector)
{
struct mei_hdcp_data *data = &connector->hdcp.mei_data;
struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
@@ -1137,12 +1173,157 @@ int hdcp2_close_mei_session(struct intel_connector *connector)
return ret;
}
-static __attribute__((unused))
-int hdcp2_deauthenticate_port(struct intel_connector *connector)
+static int hdcp2_deauthenticate_port(struct intel_connector *connector)
{
return hdcp2_close_mei_session(connector);
}
+static int hdcp2_authenticate_sink(struct intel_connector *connector)
+{
+ DRM_ERROR("Sink authentication is done in subsequent patches\n");
+
+ return -EINVAL;
+}
+
+static int hdcp2_enable_encryption(struct intel_connector *connector)
+{
+ struct intel_digital_port *intel_dig_port = conn_to_dig_port(connector);
+ struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
+ struct intel_hdcp *hdcp = &connector->hdcp;
+ enum port port = connector->encoder->port;
+ int ret;
+
+ WARN_ON(I915_READ(HDCP2_STATUS_DDI(port)) & LINK_ENCRYPTION_STATUS);
+
+ if (hdcp->shim->toggle_signalling) {
+ ret = hdcp->shim->toggle_signalling(intel_dig_port, true);
+ if (ret) {
+ DRM_ERROR("Failed to enable HDCP signalling. %d\n",
+ ret);
+ return ret;
+ }
+ }
+
+ if (I915_READ(HDCP2_STATUS_DDI(port)) & LINK_AUTH_STATUS) {
+ /* Link is Authenticated. Now set for Encryption */
+ I915_WRITE(HDCP2_CTL_DDI(port),
+ I915_READ(HDCP2_CTL_DDI(port)) |
+ CTL_LINK_ENCRYPTION_REQ);
+ }
+
+ ret = intel_wait_for_register(dev_priv, HDCP2_STATUS_DDI(port),
+ LINK_ENCRYPTION_STATUS,
+ LINK_ENCRYPTION_STATUS,
+ TIME_FOR_ENCRYPT_STATUS_CHANGE);
+
+ return ret;
+}
+
+static int hdcp2_disable_encryption(struct intel_connector *connector)
+{
+ struct intel_digital_port *intel_dig_port = conn_to_dig_port(connector);
+ struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
+ struct intel_hdcp *hdcp = &connector->hdcp;
+ enum port port = connector->encoder->port;
+ int ret;
+
+ WARN_ON(!(I915_READ(HDCP2_STATUS_DDI(port)) & LINK_ENCRYPTION_STATUS));
+
+ I915_WRITE(HDCP2_CTL_DDI(port),
+ I915_READ(HDCP2_CTL_DDI(port)) & ~CTL_LINK_ENCRYPTION_REQ);
+
+ ret = intel_wait_for_register(dev_priv, HDCP2_STATUS_DDI(port),
+ LINK_ENCRYPTION_STATUS, 0x0,
+ TIME_FOR_ENCRYPT_STATUS_CHANGE);
+ if (ret == -ETIMEDOUT)
+ DRM_DEBUG_KMS("Disable Encryption Timedout");
+
+ if (hdcp->shim->toggle_signalling) {
+ ret = hdcp->shim->toggle_signalling(intel_dig_port, false);
+ if (ret) {
+ DRM_ERROR("Failed to disable HDCP signalling. %d\n",
+ ret);
+ return ret;
+ }
+ }
+
+ return ret;
+}
+
+static int hdcp2_authenticate_and_encrypt(struct intel_connector *connector)
+{
+ int ret, i, tries = 3;
+
+ for (i = 0; i < tries; i++) {
+ ret = hdcp2_authenticate_sink(connector);
+ if (!ret)
+ break;
+
+ /* Clearing the mei hdcp session */
+ DRM_DEBUG_KMS("HDCP2.2 Auth %d of %d Failed.(%d)\n",
+ i + 1, tries, ret);
+ if (hdcp2_deauthenticate_port(connector) < 0)
+ DRM_DEBUG_KMS("Port deauth failed.\n");
+ }
+
+ if (i != tries) {
+ /*
+ * Ensuring the required 200mSec min time interval between
+ * Session Key Exchange and encryption.
+ */
+ msleep(HDCP_2_2_DELAY_BEFORE_ENCRYPTION_EN);
+ ret = hdcp2_enable_encryption(connector);
+ if (ret < 0) {
+ DRM_DEBUG_KMS("Encryption Enable Failed.(%d)\n", ret);
+ if (hdcp2_deauthenticate_port(connector) < 0)
+ DRM_DEBUG_KMS("Port deauth failed.\n");
+ }
+ }
+
+ return ret;
+}
+
+static int _intel_hdcp2_enable(struct intel_connector *connector)
+{
+ struct intel_hdcp *hdcp = &connector->hdcp;
+ int ret;
+
+ DRM_DEBUG_KMS("[%s:%d] HDCP2.2 is being enabled. Type: %d\n",
+ connector->base.name, connector->base.base.id,
+ hdcp->content_type);
+
+ ret = hdcp2_authenticate_and_encrypt(connector);
+ if (ret) {
+ DRM_DEBUG_KMS("HDCP2 Type%d Enabling Failed. (%d)\n",
+ hdcp->content_type, ret);
+ return ret;
+ }
+
+ DRM_DEBUG_KMS("[%s:%d] HDCP2.2 is enabled. Type %d\n",
+ connector->base.name, connector->base.base.id,
+ hdcp->content_type);
+
+ hdcp->hdcp2_in_use = true;
+ return 0;
+}
+
+static int _intel_hdcp2_disable(struct intel_connector *connector)
+{
+ int ret;
+
+ DRM_DEBUG_KMS("[%s:%d] HDCP2.2 is being Disabled\n",
+ connector->base.name, connector->base.base.id);
+
+ ret = hdcp2_disable_encryption(connector);
+
+ if (hdcp2_deauthenticate_port(connector) < 0)
+ DRM_DEBUG_KMS("Port deauth failed.\n");
+
+ connector->hdcp.hdcp2_in_use = false;
+
+ return ret;
+}
+
static int i915_hdcp_component_master_bind(struct device *dev)
{
struct drm_i915_private *dev_priv = kdev_to_i915(dev);
@@ -1342,22 +1523,33 @@ void intel_hdcp_exit(struct drm_i915_private *dev_priv)
int intel_hdcp_enable(struct intel_connector *connector)
{
struct intel_hdcp *hdcp = &connector->hdcp;
- int ret;
+ int ret = -EINVAL;
if (!hdcp->shim)
return -ENOENT;
mutex_lock(&hdcp->mutex);
- ret = _intel_hdcp_enable(connector);
- if (ret)
- goto out;
+ /*
+ * Considering that HDCP2.2 is more secure than HDCP1.4, If the setup
+ * is capable of HDCP2.2, it is preferred to use HDCP2.2.
+ */
+ if (intel_hdcp2_capable(connector))
+ ret = _intel_hdcp2_enable(connector);
+
+ /* When HDCP2.2 fails, HDCP1.4 will be attempted */
+ if (ret && intel_hdcp_capable(connector)) {
+ ret = _intel_hdcp_enable(connector);
+ if (!ret)
+ schedule_delayed_work(&hdcp->check_work,
+ DRM_HDCP_CHECK_PERIOD_MS);
+ }
+
+ if (!ret) {
+ hdcp->value = DRM_MODE_CONTENT_PROTECTION_ENABLED;
+ schedule_work(&hdcp->prop_work);
+ }
- hdcp->value = DRM_MODE_CONTENT_PROTECTION_ENABLED;
- schedule_work(&hdcp->prop_work);
- schedule_delayed_work(&hdcp->check_work,
- DRM_HDCP_CHECK_PERIOD_MS);
mutex_unlock(&hdcp->mutex);
return ret;
}
@@ -1374,7 +1566,10 @@ int intel_hdcp_disable(struct intel_connector *connector)
if (hdcp->value != DRM_MODE_CONTENT_PROTECTION_UNDESIRED) {
hdcp->value = DRM_MODE_CONTENT_PROTECTION_UNDESIRED;
- ret = _intel_hdcp_disable(connector);
+ if (hdcp->hdcp2_in_use)
+ ret = _intel_hdcp2_disable(connector);
+ else
+ ret = _intel_hdcp_disable(connector);
Looks reasonable. So one thing we could be doing is already internally
have a DRM_MODE_CONTENT_PROTECTION_ENABLED_TYPE1 for hdcp2. But the uapi
would only return _ENABLED to userspace. But for internal tracking this
would avoid the need for the separate hdcp2_in_use variable.

Probably good cleanup/prep patch for the type0/1 uapi extension, once we
get there.

Aside from the component intergration question seems all reasonable. But I
didn't yet review the hdcp2 flow, will do that once it's more complete in
later patches.
-Daniel
Post by Ramalingam C
}
mutex_unlock(&hdcp->mutex);
--
2.7.4
--
Daniel Vetter
Software Engineer, Intel Corporation
http://blog.ffwll.ch
C, Ramalingam
2018-12-07 06:22:21 UTC
Permalink
Post by Daniel Vetter
Post by Ramalingam C
Considering that HDCP2.2 is more secure than HDCP1.4, When a setup
supports HDCP2.2 and HDCP1.4, HDCP2.2 will be enabled.
When HDCP2.2 enabling fails and HDCP1.4 is supported, HDCP1.4 is
enabled.
This change implements a sequence of enabling and disabling of
HDCP2.2 authentication and HDCP2.2 port encryption.
Patch series suggestion for next time around: First build out the helper
functions, then time them into the big picture like here. Personally I
think that makes it easier to understand, but it's kinda personal choice.
Tried that already. But bisecting will give warnings as "defined but
unused functions"

Hence moved to this approach.
Post by Daniel Vetter
I guess this here works too.
Post by Ramalingam C
Included few optimization suggestions [Chris Wilson]
Commit message is updated as per the rebased version.
intel_wait_for_register is used instead of wait_for. [Chris Wilson]
No changes.
Extra comment added and Style issue fixed [Uma]
Rebased as part of patch reordering.
HDCP2 encryption status is tracked.
HW state check is moved into WARN_ON [Daniel]
Redefined the mei service functions as per comp redesign.
Merged patches related to hdcp2.2 enabling and disabling [Sean Paul].
Required shim functionality is defined [Sean Paul]
Return values are handles [Uma]
Realigned the code.
Check for comp_master is removed.
HDCP2.2 is attempted only if mei interface is up.
Adjust to the new interface
Avoid bool usage in struct [Tomas]
---
drivers/gpu/drm/i915/intel_drv.h | 5 +
drivers/gpu/drm/i915/intel_hdcp.c | 223 +++++++++++++++++++++++++++++++++++---
2 files changed, 214 insertions(+), 14 deletions(-)
diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h
index bde82f3ada85..3e9f21d23442 100644
--- a/drivers/gpu/drm/i915/intel_drv.h
+++ b/drivers/gpu/drm/i915/intel_drv.h
@@ -383,6 +383,10 @@ struct intel_hdcp_shim {
/* Detects the HDCP protocol(DP/HDMI) required on the port */
enum mei_hdcp_wired_protocol (*hdcp_protocol)(void);
+
+ /* Detects whether Panel is HDCP2.2 capable */
+ int (*hdcp_2_2_capable)(struct intel_digital_port *intel_dig_port,
+ bool *capable);
};
struct intel_hdcp {
@@ -396,6 +400,7 @@ struct intel_hdcp {
/* HDCP2.2 related definitions */
/* Flag indicates whether this connector supports HDCP2.2 or not. */
u8 hdcp2_supported;
+ u8 hdcp2_in_use;
/*
* Content Stream Type defined by content owner. TYPE0(0x0) content can
diff --git a/drivers/gpu/drm/i915/intel_hdcp.c b/drivers/gpu/drm/i915/intel_hdcp.c
index 760780f1105c..c1bd1ccd47cd 100644
--- a/drivers/gpu/drm/i915/intel_hdcp.c
+++ b/drivers/gpu/drm/i915/intel_hdcp.c
@@ -79,6 +79,43 @@ bool intel_hdcp_capable(struct intel_connector *connector)
return capable;
}
+/* At present whether mei_hdcp component is binded with i915 master component */
+static bool intel_hdcp2_mei_binded(struct intel_connector *connector)
+{
+ struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
+ struct i915_hdcp_component_master *comp = dev_priv->hdcp_comp;
+
+ mutex_lock(&comp->mutex);
+ if (!comp->ops || !comp->dev) {
+ mutex_unlock(&comp->mutex);
+ return false;
+ }
+ mutex_unlock(&comp->mutex);
+
+ return true;
+}
+
+/* Is HDCP2.2 capable on Platform and Sink */
+static bool intel_hdcp2_capable(struct intel_connector *connector)
+{
+ struct intel_digital_port *intel_dig_port = conn_to_dig_port(connector);
+ struct intel_hdcp *hdcp = &connector->hdcp;
+ bool capable = false;
+
+ /* I915 support for HDCP2.2 */
+ if (!hdcp->hdcp2_supported)
+ return false;
+
+ /* MEI services for HDCP2.2 */
+ if (!intel_hdcp2_mei_binded(connector))
+ return false;
Why do we still need this with component? Driver load should be stalled
out until it's all there, that was kinda the entire point of component,
so we don't have to recheck all the time whether hdcp2 is still there or
not.
We discussed this in previous patches. Lets decide whether this approach
is good enough or not.

--Ram
Post by Daniel Vetter
Post by Ramalingam C
+
+ /* Sink's capability for HDCP2.2 */
+ hdcp->shim->hdcp_2_2_capable(intel_dig_port, &capable);
+
+ return capable;
+}
+
static int intel_hdcp_poll_ksv_fifo(struct intel_digital_port *intel_dig_port,
const struct intel_hdcp_shim *shim)
{
@@ -1114,8 +1151,7 @@ int hdcp2_authenticate_port(struct intel_connector *connector)
return ret;
}
-static __attribute__((unused))
-int hdcp2_close_mei_session(struct intel_connector *connector)
+static int hdcp2_close_mei_session(struct intel_connector *connector)
{
struct mei_hdcp_data *data = &connector->hdcp.mei_data;
struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
@@ -1137,12 +1173,157 @@ int hdcp2_close_mei_session(struct intel_connector *connector)
return ret;
}
-static __attribute__((unused))
-int hdcp2_deauthenticate_port(struct intel_connector *connector)
+static int hdcp2_deauthenticate_port(struct intel_connector *connector)
{
return hdcp2_close_mei_session(connector);
}
+static int hdcp2_authenticate_sink(struct intel_connector *connector)
+{
+ DRM_ERROR("Sink authentication is done in subsequent patches\n");
+
+ return -EINVAL;
+}
+
+static int hdcp2_enable_encryption(struct intel_connector *connector)
+{
+ struct intel_digital_port *intel_dig_port = conn_to_dig_port(connector);
+ struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
+ struct intel_hdcp *hdcp = &connector->hdcp;
+ enum port port = connector->encoder->port;
+ int ret;
+
+ WARN_ON(I915_READ(HDCP2_STATUS_DDI(port)) & LINK_ENCRYPTION_STATUS);
+
+ if (hdcp->shim->toggle_signalling) {
+ ret = hdcp->shim->toggle_signalling(intel_dig_port, true);
+ if (ret) {
+ DRM_ERROR("Failed to enable HDCP signalling. %d\n",
+ ret);
+ return ret;
+ }
+ }
+
+ if (I915_READ(HDCP2_STATUS_DDI(port)) & LINK_AUTH_STATUS) {
+ /* Link is Authenticated. Now set for Encryption */
+ I915_WRITE(HDCP2_CTL_DDI(port),
+ I915_READ(HDCP2_CTL_DDI(port)) |
+ CTL_LINK_ENCRYPTION_REQ);
+ }
+
+ ret = intel_wait_for_register(dev_priv, HDCP2_STATUS_DDI(port),
+ LINK_ENCRYPTION_STATUS,
+ LINK_ENCRYPTION_STATUS,
+ TIME_FOR_ENCRYPT_STATUS_CHANGE);
+
+ return ret;
+}
+
+static int hdcp2_disable_encryption(struct intel_connector *connector)
+{
+ struct intel_digital_port *intel_dig_port = conn_to_dig_port(connector);
+ struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
+ struct intel_hdcp *hdcp = &connector->hdcp;
+ enum port port = connector->encoder->port;
+ int ret;
+
+ WARN_ON(!(I915_READ(HDCP2_STATUS_DDI(port)) & LINK_ENCRYPTION_STATUS));
+
+ I915_WRITE(HDCP2_CTL_DDI(port),
+ I915_READ(HDCP2_CTL_DDI(port)) & ~CTL_LINK_ENCRYPTION_REQ);
+
+ ret = intel_wait_for_register(dev_priv, HDCP2_STATUS_DDI(port),
+ LINK_ENCRYPTION_STATUS, 0x0,
+ TIME_FOR_ENCRYPT_STATUS_CHANGE);
+ if (ret == -ETIMEDOUT)
+ DRM_DEBUG_KMS("Disable Encryption Timedout");
+
+ if (hdcp->shim->toggle_signalling) {
+ ret = hdcp->shim->toggle_signalling(intel_dig_port, false);
+ if (ret) {
+ DRM_ERROR("Failed to disable HDCP signalling. %d\n",
+ ret);
+ return ret;
+ }
+ }
+
+ return ret;
+}
+
+static int hdcp2_authenticate_and_encrypt(struct intel_connector *connector)
+{
+ int ret, i, tries = 3;
+
+ for (i = 0; i < tries; i++) {
+ ret = hdcp2_authenticate_sink(connector);
+ if (!ret)
+ break;
+
+ /* Clearing the mei hdcp session */
+ DRM_DEBUG_KMS("HDCP2.2 Auth %d of %d Failed.(%d)\n",
+ i + 1, tries, ret);
+ if (hdcp2_deauthenticate_port(connector) < 0)
+ DRM_DEBUG_KMS("Port deauth failed.\n");
+ }
+
+ if (i != tries) {
+ /*
+ * Ensuring the required 200mSec min time interval between
+ * Session Key Exchange and encryption.
+ */
+ msleep(HDCP_2_2_DELAY_BEFORE_ENCRYPTION_EN);
+ ret = hdcp2_enable_encryption(connector);
+ if (ret < 0) {
+ DRM_DEBUG_KMS("Encryption Enable Failed.(%d)\n", ret);
+ if (hdcp2_deauthenticate_port(connector) < 0)
+ DRM_DEBUG_KMS("Port deauth failed.\n");
+ }
+ }
+
+ return ret;
+}
+
+static int _intel_hdcp2_enable(struct intel_connector *connector)
+{
+ struct intel_hdcp *hdcp = &connector->hdcp;
+ int ret;
+
+ DRM_DEBUG_KMS("[%s:%d] HDCP2.2 is being enabled. Type: %d\n",
+ connector->base.name, connector->base.base.id,
+ hdcp->content_type);
+
+ ret = hdcp2_authenticate_and_encrypt(connector);
+ if (ret) {
+ DRM_DEBUG_KMS("HDCP2 Type%d Enabling Failed. (%d)\n",
+ hdcp->content_type, ret);
+ return ret;
+ }
+
+ DRM_DEBUG_KMS("[%s:%d] HDCP2.2 is enabled. Type %d\n",
+ connector->base.name, connector->base.base.id,
+ hdcp->content_type);
+
+ hdcp->hdcp2_in_use = true;
+ return 0;
+}
+
+static int _intel_hdcp2_disable(struct intel_connector *connector)
+{
+ int ret;
+
+ DRM_DEBUG_KMS("[%s:%d] HDCP2.2 is being Disabled\n",
+ connector->base.name, connector->base.base.id);
+
+ ret = hdcp2_disable_encryption(connector);
+
+ if (hdcp2_deauthenticate_port(connector) < 0)
+ DRM_DEBUG_KMS("Port deauth failed.\n");
+
+ connector->hdcp.hdcp2_in_use = false;
+
+ return ret;
+}
+
static int i915_hdcp_component_master_bind(struct device *dev)
{
struct drm_i915_private *dev_priv = kdev_to_i915(dev);
@@ -1342,22 +1523,33 @@ void intel_hdcp_exit(struct drm_i915_private *dev_priv)
int intel_hdcp_enable(struct intel_connector *connector)
{
struct intel_hdcp *hdcp = &connector->hdcp;
- int ret;
+ int ret = -EINVAL;
if (!hdcp->shim)
return -ENOENT;
mutex_lock(&hdcp->mutex);
- ret = _intel_hdcp_enable(connector);
- if (ret)
- goto out;
+ /*
+ * Considering that HDCP2.2 is more secure than HDCP1.4, If the setup
+ * is capable of HDCP2.2, it is preferred to use HDCP2.2.
+ */
+ if (intel_hdcp2_capable(connector))
+ ret = _intel_hdcp2_enable(connector);
+
+ /* When HDCP2.2 fails, HDCP1.4 will be attempted */
+ if (ret && intel_hdcp_capable(connector)) {
+ ret = _intel_hdcp_enable(connector);
+ if (!ret)
+ schedule_delayed_work(&hdcp->check_work,
+ DRM_HDCP_CHECK_PERIOD_MS);
+ }
+
+ if (!ret) {
+ hdcp->value = DRM_MODE_CONTENT_PROTECTION_ENABLED;
+ schedule_work(&hdcp->prop_work);
+ }
- hdcp->value = DRM_MODE_CONTENT_PROTECTION_ENABLED;
- schedule_work(&hdcp->prop_work);
- schedule_delayed_work(&hdcp->check_work,
- DRM_HDCP_CHECK_PERIOD_MS);
mutex_unlock(&hdcp->mutex);
return ret;
}
@@ -1374,7 +1566,10 @@ int intel_hdcp_disable(struct intel_connector *connector)
if (hdcp->value != DRM_MODE_CONTENT_PROTECTION_UNDESIRED) {
hdcp->value = DRM_MODE_CONTENT_PROTECTION_UNDESIRED;
- ret = _intel_hdcp_disable(connector);
+ if (hdcp->hdcp2_in_use)
+ ret = _intel_hdcp2_disable(connector);
+ else
+ ret = _intel_hdcp_disable(connector);
Looks reasonable. So one thing we could be doing is already internally
have a DRM_MODE_CONTENT_PROTECTION_ENABLED_TYPE1 for hdcp2.But the uapi
would only return _ENABLED to userspace. But for internal tracking this
would avoid the need for the separate hdcp2_in_use variable.
Probably good cleanup/prep patch for the type0/1 uapi extension, once we
get there.
Aside from the component intergration question seems all reasonable. But I
didn't yet review the hdcp2 flow, will do that once it's more complete in
later patches.
-Daniel
Post by Ramalingam C
}
mutex_unlock(&hdcp->mutex);
--
2.7.4
Daniel Vetter
2018-12-07 14:33:25 UTC
Permalink
Post by Daniel Vetter
Post by Ramalingam C
Considering that HDCP2.2 is more secure than HDCP1.4, When a setup
supports HDCP2.2 and HDCP1.4, HDCP2.2 will be enabled.
When HDCP2.2 enabling fails and HDCP1.4 is supported, HDCP1.4 is
enabled.
This change implements a sequence of enabling and disabling of
HDCP2.2 authentication and HDCP2.2 port encryption.
Patch series suggestion for next time around: First build out the helper
functions, then time them into the big picture like here. Personally I
think that makes it easier to understand, but it's kinda personal choice.
Tried that already. But bisecting will give warnings as "defined but unused
functions"
Hence moved to this approach.
Ah right, that's the annoying part with that approach. I tend to just
ignore that until the series is built up (it's just a warning), but good
point. As mentioned, there's pro/cons to each approach.
-Daniel
Post by Daniel Vetter
I guess this here works too.
Post by Ramalingam C
Included few optimization suggestions [Chris Wilson]
Commit message is updated as per the rebased version.
intel_wait_for_register is used instead of wait_for. [Chris Wilson]
No changes.
Extra comment added and Style issue fixed [Uma]
Rebased as part of patch reordering.
HDCP2 encryption status is tracked.
HW state check is moved into WARN_ON [Daniel]
Redefined the mei service functions as per comp redesign.
Merged patches related to hdcp2.2 enabling and disabling [Sean Paul].
Required shim functionality is defined [Sean Paul]
Return values are handles [Uma]
Realigned the code.
Check for comp_master is removed.
HDCP2.2 is attempted only if mei interface is up.
Adjust to the new interface
Avoid bool usage in struct [Tomas]
---
drivers/gpu/drm/i915/intel_drv.h | 5 +
drivers/gpu/drm/i915/intel_hdcp.c | 223 +++++++++++++++++++++++++++++++++++---
2 files changed, 214 insertions(+), 14 deletions(-)
diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h
index bde82f3ada85..3e9f21d23442 100644
--- a/drivers/gpu/drm/i915/intel_drv.h
+++ b/drivers/gpu/drm/i915/intel_drv.h
@@ -383,6 +383,10 @@ struct intel_hdcp_shim {
/* Detects the HDCP protocol(DP/HDMI) required on the port */
enum mei_hdcp_wired_protocol (*hdcp_protocol)(void);
+
+ /* Detects whether Panel is HDCP2.2 capable */
+ int (*hdcp_2_2_capable)(struct intel_digital_port *intel_dig_port,
+ bool *capable);
};
struct intel_hdcp {
@@ -396,6 +400,7 @@ struct intel_hdcp {
/* HDCP2.2 related definitions */
/* Flag indicates whether this connector supports HDCP2.2 or not. */
u8 hdcp2_supported;
+ u8 hdcp2_in_use;
/*
* Content Stream Type defined by content owner. TYPE0(0x0) content can
diff --git a/drivers/gpu/drm/i915/intel_hdcp.c b/drivers/gpu/drm/i915/intel_hdcp.c
index 760780f1105c..c1bd1ccd47cd 100644
--- a/drivers/gpu/drm/i915/intel_hdcp.c
+++ b/drivers/gpu/drm/i915/intel_hdcp.c
@@ -79,6 +79,43 @@ bool intel_hdcp_capable(struct intel_connector *connector)
return capable;
}
+/* At present whether mei_hdcp component is binded with i915 master component */
+static bool intel_hdcp2_mei_binded(struct intel_connector *connector)
+{
+ struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
+ struct i915_hdcp_component_master *comp = dev_priv->hdcp_comp;
+
+ mutex_lock(&comp->mutex);
+ if (!comp->ops || !comp->dev) {
+ mutex_unlock(&comp->mutex);
+ return false;
+ }
+ mutex_unlock(&comp->mutex);
+
+ return true;
+}
+
+/* Is HDCP2.2 capable on Platform and Sink */
+static bool intel_hdcp2_capable(struct intel_connector *connector)
+{
+ struct intel_digital_port *intel_dig_port = conn_to_dig_port(connector);
+ struct intel_hdcp *hdcp = &connector->hdcp;
+ bool capable = false;
+
+ /* I915 support for HDCP2.2 */
+ if (!hdcp->hdcp2_supported)
+ return false;
+
+ /* MEI services for HDCP2.2 */
+ if (!intel_hdcp2_mei_binded(connector))
+ return false;
Why do we still need this with component? Driver load should be stalled
out until it's all there, that was kinda the entire point of component,
so we don't have to recheck all the time whether hdcp2 is still there or
not.
We discussed this in previous patches. Lets decide whether this approach is
good enough or not.
--Ram
Post by Daniel Vetter
Post by Ramalingam C
+
+ /* Sink's capability for HDCP2.2 */
+ hdcp->shim->hdcp_2_2_capable(intel_dig_port, &capable);
+
+ return capable;
+}
+
static int intel_hdcp_poll_ksv_fifo(struct intel_digital_port *intel_dig_port,
const struct intel_hdcp_shim *shim)
{
@@ -1114,8 +1151,7 @@ int hdcp2_authenticate_port(struct intel_connector *connector)
return ret;
}
-static __attribute__((unused))
-int hdcp2_close_mei_session(struct intel_connector *connector)
+static int hdcp2_close_mei_session(struct intel_connector *connector)
{
struct mei_hdcp_data *data = &connector->hdcp.mei_data;
struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
@@ -1137,12 +1173,157 @@ int hdcp2_close_mei_session(struct intel_connector *connector)
return ret;
}
-static __attribute__((unused))
-int hdcp2_deauthenticate_port(struct intel_connector *connector)
+static int hdcp2_deauthenticate_port(struct intel_connector *connector)
{
return hdcp2_close_mei_session(connector);
}
+static int hdcp2_authenticate_sink(struct intel_connector *connector)
+{
+ DRM_ERROR("Sink authentication is done in subsequent patches\n");
+
+ return -EINVAL;
+}
+
+static int hdcp2_enable_encryption(struct intel_connector *connector)
+{
+ struct intel_digital_port *intel_dig_port = conn_to_dig_port(connector);
+ struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
+ struct intel_hdcp *hdcp = &connector->hdcp;
+ enum port port = connector->encoder->port;
+ int ret;
+
+ WARN_ON(I915_READ(HDCP2_STATUS_DDI(port)) & LINK_ENCRYPTION_STATUS);
+
+ if (hdcp->shim->toggle_signalling) {
+ ret = hdcp->shim->toggle_signalling(intel_dig_port, true);
+ if (ret) {
+ DRM_ERROR("Failed to enable HDCP signalling. %d\n",
+ ret);
+ return ret;
+ }
+ }
+
+ if (I915_READ(HDCP2_STATUS_DDI(port)) & LINK_AUTH_STATUS) {
+ /* Link is Authenticated. Now set for Encryption */
+ I915_WRITE(HDCP2_CTL_DDI(port),
+ I915_READ(HDCP2_CTL_DDI(port)) |
+ CTL_LINK_ENCRYPTION_REQ);
+ }
+
+ ret = intel_wait_for_register(dev_priv, HDCP2_STATUS_DDI(port),
+ LINK_ENCRYPTION_STATUS,
+ LINK_ENCRYPTION_STATUS,
+ TIME_FOR_ENCRYPT_STATUS_CHANGE);
+
+ return ret;
+}
+
+static int hdcp2_disable_encryption(struct intel_connector *connector)
+{
+ struct intel_digital_port *intel_dig_port = conn_to_dig_port(connector);
+ struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
+ struct intel_hdcp *hdcp = &connector->hdcp;
+ enum port port = connector->encoder->port;
+ int ret;
+
+ WARN_ON(!(I915_READ(HDCP2_STATUS_DDI(port)) & LINK_ENCRYPTION_STATUS));
+
+ I915_WRITE(HDCP2_CTL_DDI(port),
+ I915_READ(HDCP2_CTL_DDI(port)) & ~CTL_LINK_ENCRYPTION_REQ);
+
+ ret = intel_wait_for_register(dev_priv, HDCP2_STATUS_DDI(port),
+ LINK_ENCRYPTION_STATUS, 0x0,
+ TIME_FOR_ENCRYPT_STATUS_CHANGE);
+ if (ret == -ETIMEDOUT)
+ DRM_DEBUG_KMS("Disable Encryption Timedout");
+
+ if (hdcp->shim->toggle_signalling) {
+ ret = hdcp->shim->toggle_signalling(intel_dig_port, false);
+ if (ret) {
+ DRM_ERROR("Failed to disable HDCP signalling. %d\n",
+ ret);
+ return ret;
+ }
+ }
+
+ return ret;
+}
+
+static int hdcp2_authenticate_and_encrypt(struct intel_connector *connector)
+{
+ int ret, i, tries = 3;
+
+ for (i = 0; i < tries; i++) {
+ ret = hdcp2_authenticate_sink(connector);
+ if (!ret)
+ break;
+
+ /* Clearing the mei hdcp session */
+ DRM_DEBUG_KMS("HDCP2.2 Auth %d of %d Failed.(%d)\n",
+ i + 1, tries, ret);
+ if (hdcp2_deauthenticate_port(connector) < 0)
+ DRM_DEBUG_KMS("Port deauth failed.\n");
+ }
+
+ if (i != tries) {
+ /*
+ * Ensuring the required 200mSec min time interval between
+ * Session Key Exchange and encryption.
+ */
+ msleep(HDCP_2_2_DELAY_BEFORE_ENCRYPTION_EN);
+ ret = hdcp2_enable_encryption(connector);
+ if (ret < 0) {
+ DRM_DEBUG_KMS("Encryption Enable Failed.(%d)\n", ret);
+ if (hdcp2_deauthenticate_port(connector) < 0)
+ DRM_DEBUG_KMS("Port deauth failed.\n");
+ }
+ }
+
+ return ret;
+}
+
+static int _intel_hdcp2_enable(struct intel_connector *connector)
+{
+ struct intel_hdcp *hdcp = &connector->hdcp;
+ int ret;
+
+ DRM_DEBUG_KMS("[%s:%d] HDCP2.2 is being enabled. Type: %d\n",
+ connector->base.name, connector->base.base.id,
+ hdcp->content_type);
+
+ ret = hdcp2_authenticate_and_encrypt(connector);
+ if (ret) {
+ DRM_DEBUG_KMS("HDCP2 Type%d Enabling Failed. (%d)\n",
+ hdcp->content_type, ret);
+ return ret;
+ }
+
+ DRM_DEBUG_KMS("[%s:%d] HDCP2.2 is enabled. Type %d\n",
+ connector->base.name, connector->base.base.id,
+ hdcp->content_type);
+
+ hdcp->hdcp2_in_use = true;
+ return 0;
+}
+
+static int _intel_hdcp2_disable(struct intel_connector *connector)
+{
+ int ret;
+
+ DRM_DEBUG_KMS("[%s:%d] HDCP2.2 is being Disabled\n",
+ connector->base.name, connector->base.base.id);
+
+ ret = hdcp2_disable_encryption(connector);
+
+ if (hdcp2_deauthenticate_port(connector) < 0)
+ DRM_DEBUG_KMS("Port deauth failed.\n");
+
+ connector->hdcp.hdcp2_in_use = false;
+
+ return ret;
+}
+
static int i915_hdcp_component_master_bind(struct device *dev)
{
struct drm_i915_private *dev_priv = kdev_to_i915(dev);
@@ -1342,22 +1523,33 @@ void intel_hdcp_exit(struct drm_i915_private *dev_priv)
int intel_hdcp_enable(struct intel_connector *connector)
{
struct intel_hdcp *hdcp = &connector->hdcp;
- int ret;
+ int ret = -EINVAL;
if (!hdcp->shim)
return -ENOENT;
mutex_lock(&hdcp->mutex);
- ret = _intel_hdcp_enable(connector);
- if (ret)
- goto out;
+ /*
+ * Considering that HDCP2.2 is more secure than HDCP1.4, If the setup
+ * is capable of HDCP2.2, it is preferred to use HDCP2.2.
+ */
+ if (intel_hdcp2_capable(connector))
+ ret = _intel_hdcp2_enable(connector);
+
+ /* When HDCP2.2 fails, HDCP1.4 will be attempted */
+ if (ret && intel_hdcp_capable(connector)) {
+ ret = _intel_hdcp_enable(connector);
+ if (!ret)
+ schedule_delayed_work(&hdcp->check_work,
+ DRM_HDCP_CHECK_PERIOD_MS);
+ }
+
+ if (!ret) {
+ hdcp->value = DRM_MODE_CONTENT_PROTECTION_ENABLED;
+ schedule_work(&hdcp->prop_work);
+ }
- hdcp->value = DRM_MODE_CONTENT_PROTECTION_ENABLED;
- schedule_work(&hdcp->prop_work);
- schedule_delayed_work(&hdcp->check_work,
- DRM_HDCP_CHECK_PERIOD_MS);
mutex_unlock(&hdcp->mutex);
return ret;
}
@@ -1374,7 +1566,10 @@ int intel_hdcp_disable(struct intel_connector *connector)
if (hdcp->value != DRM_MODE_CONTENT_PROTECTION_UNDESIRED) {
hdcp->value = DRM_MODE_CONTENT_PROTECTION_UNDESIRED;
- ret = _intel_hdcp_disable(connector);
+ if (hdcp->hdcp2_in_use)
+ ret = _intel_hdcp2_disable(connector);
+ else
+ ret = _intel_hdcp_disable(connector);
Looks reasonable. So one thing we could be doing is already internally
have a DRM_MODE_CONTENT_PROTECTION_ENABLED_TYPE1 for hdcp2.But the uapi
would only return _ENABLED to userspace. But for internal tracking this
would avoid the need for the separate hdcp2_in_use variable.
Probably good cleanup/prep patch for the type0/1 uapi extension, once we
get there.
Aside from the component intergration question seems all reasonable. But I
didn't yet review the hdcp2 flow, will do that once it's more complete in
later patches.
-Daniel
Post by Ramalingam C
}
mutex_unlock(&hdcp->mutex);
--
2.7.4
--
Daniel Vetter
Software Engineer, Intel Corporation
http://blog.ffwll.ch
Ramalingam C
2018-11-27 10:43:12 UTC
Permalink
On DP connector init, intel_hdcp_init is passed with a flag for hdcp2.2
support based on the platform capability.

v2:
Rebased.
v3:
No Changes.
v4:
Collected the reviewed-by received.
v5:
No change.
v6:
No change.
v7:
No change.
v8:
No change.

Signed-off-by: Ramalingam C <***@intel.com>
Reviewed-by: Uma Shankar <***@intel.com>
---
drivers/gpu/drm/i915/intel_dp.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c
index 1cc82e490999..b907ce28adbd 100644
--- a/drivers/gpu/drm/i915/intel_dp.c
+++ b/drivers/gpu/drm/i915/intel_dp.c
@@ -7014,7 +7014,7 @@ intel_dp_init_connector(struct intel_digital_port *intel_dig_port,

if (is_hdcp_supported(dev_priv, port) && !intel_dp_is_edp(intel_dp)) {
int ret = intel_hdcp_init(intel_connector, &intel_dp_hdcp_shim,
- false);
+ is_hdcp2_supported(dev_priv));
if (ret)
DRM_DEBUG_KMS("HDCP init failed, skipping.\n");
}
--
2.7.4
Ramalingam C
2018-11-27 10:43:13 UTC
Permalink
On HDMI connector init, intel_hdcp_init is passed with a flag for hdcp2.2
support based on the platform capability.

v2:
Rebased.
v3:
No Changes.
v4:
Collected the reviewed-by received.
v5:
No change.
v6:
No change.
v7:
No change.
v8:
No change.

Signed-off-by: Ramalingam C <***@intel.com>
Reviewed-by: Uma Shankar <***@intel.com>
---
drivers/gpu/drm/i915/intel_hdmi.c | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/drivers/gpu/drm/i915/intel_hdmi.c b/drivers/gpu/drm/i915/intel_hdmi.c
index 912f50133645..2baf1d15b63a 100644
--- a/drivers/gpu/drm/i915/intel_hdmi.c
+++ b/drivers/gpu/drm/i915/intel_hdmi.c
@@ -2607,7 +2607,8 @@ void intel_hdmi_init_connector(struct intel_digital_port *intel_dig_port,

if (is_hdcp_supported(dev_priv, port)) {
int ret = intel_hdcp_init(intel_connector,
- &intel_hdmi_hdcp_shim, false);
+ &intel_hdmi_hdcp_shim,
+ is_hdcp2_supported(dev_priv));
if (ret)
DRM_DEBUG_KMS("HDCP init failed, skipping.\n");
}
--
2.7.4
Ramalingam C
2018-11-27 10:43:16 UTC
Permalink
ME FW is contributes a vital role in HDCP2.2 authentication.
HDCP2.2 driver needs to communicate to ME FW for each step of the
HDCP2.2 authentication.

ME FW prepare and HDCP2.2 authentication parameters and encrypt them
as per spec. With such parameter Driver prepares HDCP2.2 auth messages
and communicate with HDCP2.2 sink.

Similarly HDCP2. sink's response is shared with ME FW for decrypt and
verification.

Once All the steps of HDCP2.2 authentications are complete on driver's
request ME FW will configure the port as authenticated and supply the
HDCP keys to the Gen HW for encryption.

Only after this stage HDCP2.2 driver can start the HDCP2.2 encryption
for a port.

ME FW is interfaced to kernel through MEI Bus Driver. To obtain the
HDCP2.2 services from the ME FW through MEI Bus driver MEI Client
Driver is developed.

With this change MEI_HDCP drivers selected by I915 by default.
In case if we want to exclude the mei_hdcp compilation, we need to
introduce a new config called HDCP2.2. Using such CONFIG var we could
control the compilation of the HDCP2.2 code in I915 and also the
compilation of the MEI_HDCP.

v2:
hdcp files are moved to drivers/misc/mei/hdcp/ [Tomas]
v3:
Squashed the Kbuild support [Tomas]
UUID renamed and Module License is modified [Tomas]
drv_data is set to null at remove [Tomas]
v4:
Module name is changed to "MEI HDCP"
I915 Selects the MEI_HDCP
v5:
No changes.
v6:
No changes.
v7:
Remove redundant text from the License header
Fix malformed licence
Removed the drv_data resetting.
v8:
No changes.

Signed-off-by: Ramalingam C <***@intel.com>
Signed-off-by: Tomas Winkler <***@intel.com>
---
drivers/misc/mei/Kconfig | 7 ++++++
drivers/misc/mei/Makefile | 2 ++
drivers/misc/mei/hdcp/Makefile | 7 ++++++
drivers/misc/mei/hdcp/mei_hdcp.c | 54 ++++++++++++++++++++++++++++++++++++++++
4 files changed, 70 insertions(+)
create mode 100644 drivers/misc/mei/hdcp/Makefile
create mode 100644 drivers/misc/mei/hdcp/mei_hdcp.c

diff --git a/drivers/misc/mei/Kconfig b/drivers/misc/mei/Kconfig
index c49e1d2269af..9c518b7f0011 100644
--- a/drivers/misc/mei/Kconfig
+++ b/drivers/misc/mei/Kconfig
@@ -43,3 +43,10 @@ config INTEL_MEI_TXE

Supported SoCs:
Intel Bay Trail
+
+config INTEL_MEI_HDCP
+ tristate "Intel HDCP2.2 services of ME Interface"
+ select INTEL_MEI_ME
+ depends on DRM_I915
+ help
+ MEI Support for HDCP2.2 Services on Intel SoCs.
diff --git a/drivers/misc/mei/Makefile b/drivers/misc/mei/Makefile
index cd6825afa8e1..e64d1212fb85 100644
--- a/drivers/misc/mei/Makefile
+++ b/drivers/misc/mei/Makefile
@@ -23,3 +23,5 @@ mei-txe-objs += hw-txe.o

mei-$(CONFIG_EVENT_TRACING) += mei-trace.o
CFLAGS_mei-trace.o = -I$(src)
+
+obj-$(CONFIG_INTEL_MEI_HDCP) += hdcp/
diff --git a/drivers/misc/mei/hdcp/Makefile b/drivers/misc/mei/hdcp/Makefile
new file mode 100644
index 000000000000..c1a86dd9782b
--- /dev/null
+++ b/drivers/misc/mei/hdcp/Makefile
@@ -0,0 +1,7 @@
+# SPDX-License-Identifier: GPL-2.0+ OR BSD-3-Clause
+#
+# Copyright (c) 2017-2018, Intel Corporation.
+#
+# Makefile - HDCP client driver for Intel MEI Bus Driver.
+
+obj-$(CONFIG_INTEL_MEI_HDCP) += mei_hdcp.o
diff --git a/drivers/misc/mei/hdcp/mei_hdcp.c b/drivers/misc/mei/hdcp/mei_hdcp.c
new file mode 100644
index 000000000000..d4067d17f382
--- /dev/null
+++ b/drivers/misc/mei/hdcp/mei_hdcp.c
@@ -0,0 +1,54 @@
+/* SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) */
+/*
+ * Copyright © 2017-2018 Intel Corporation
+ *
+ * Mei_hdcp.c: HDCP client driver for mei bus
+ *
+ * Authors:
+ * Ramalingam C <***@intel.com>
+ */
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/uuid.h>
+#include <linux/mei_cl_bus.h>
+
+static int mei_hdcp_probe(struct mei_cl_device *cldev,
+ const struct mei_cl_device_id *id)
+{
+ int ret;
+
+ ret = mei_cldev_enable(cldev);
+ if (ret < 0)
+ dev_err(&cldev->dev, "mei_cldev_enable Failed. %d\n", ret);
+
+ return ret;
+}
+
+static int mei_hdcp_remove(struct mei_cl_device *cldev)
+{
+ return mei_cldev_disable(cldev);
+}
+
+#define MEI_UUID_HDCP UUID_LE(0xB638AB7E, 0x94E2, 0x4EA2, 0xA5, \
+ 0x52, 0xD1, 0xC5, 0x4B, \
+ 0x62, 0x7F, 0x04)
+
+static struct mei_cl_device_id mei_hdcp_tbl[] = {
+ { .uuid = MEI_UUID_HDCP, .version = MEI_CL_VERSION_ANY },
+ { }
+};
+MODULE_DEVICE_TABLE(mei, mei_hdcp_tbl);
+
+static struct mei_cl_driver mei_hdcp_driver = {
+ .id_table = mei_hdcp_tbl,
+ .name = KBUILD_MODNAME,
+ .probe = mei_hdcp_probe,
+ .remove = mei_hdcp_remove,
+};
+
+module_mei_cl_driver(mei_hdcp_driver);
+
+MODULE_AUTHOR("Intel Corporation");
+MODULE_LICENSE("Dual BSD/GPL");
+MODULE_DESCRIPTION("MEI HDCP");
--
2.7.4
Ramalingam C
2018-11-27 10:43:19 UTC
Permalink
Requests for verification for receiver certification and also the
preparation for next AKE auth message with km.

On Success ME FW validate the HDCP2.2 receivers certificate and do the
revocation check on the receiver ID. AKE_Stored_Km will be prepared if
the receiver is already paired, else AKE_No_Stored_Km will be prepared.

Here AKE_Stored_Km and AKE_No_Stored_Km are HDCP2.2 protocol msgs.

v2:
Rebased.
v3:
cldev is passed as first parameter [Tomas]
Redundant comments and cast are removed [Tomas]
v4:
%zd is used for ssize_t [Alexander]
%s/return -1/return -EIO [Alexander]
v5:
Rebased.
v6:
No change.
v7:
Collected the Rb-ed by.
Rebasing.
v8:
Adjust to the new mei interface.
Fix for Kdoc.

Signed-off-by: Ramalingam C <***@intel.com>
Reviewed-by: Uma Shankar <***@intel.com>
---
drivers/misc/mei/hdcp/mei_hdcp.c | 84 +++++++++++++++++++++++++++++++++++++++-
1 file changed, 83 insertions(+), 1 deletion(-)

diff --git a/drivers/misc/mei/hdcp/mei_hdcp.c b/drivers/misc/mei/hdcp/mei_hdcp.c
index ca8f82d25343..b38836dbdb66 100644
--- a/drivers/misc/mei/hdcp/mei_hdcp.c
+++ b/drivers/misc/mei/hdcp/mei_hdcp.c
@@ -82,11 +82,93 @@ static int mei_initiate_hdcp2_session(struct device *dev, void *hdcp_data,
return 0;
}

+/*
+ * mei_verify_receiver_cert_prepare_km :
+ * Function to verify the Receiver Certificate AKE_Send_Cert
+ * and prepare AKE_Stored_Km or AKE_No_Stored_Km
+ *
+ * @dev : device corresponding to the mei_cl_device
+ * @hdcp_data : Intel HW specific hdcp data
+ * @rx_cert : AKE_Send_Cert for verification
+ * @km_stored : Pairing status flag output
+ * @ek_pub_km : AKE_XXXXX_msg output
+ * @msg_sz : size of AKE_XXXXX_Km output
+ *
+ * Returns 0 on Success, <0 on Failure
+ */
+static int
+mei_verify_receiver_cert_prepare_km(struct device *dev, void *hdcp_data,
+ struct hdcp2_ake_send_cert *rx_cert,
+ bool *km_stored,
+ struct hdcp2_ake_no_stored_km *ek_pub_km,
+ size_t *msg_sz)
+{
+ struct wired_cmd_verify_receiver_cert_in verify_rxcert_in = { { 0 } };
+ struct wired_cmd_verify_receiver_cert_out verify_rxcert_out = { { 0 } };
+ struct mei_cl_device *cldev;
+ struct mei_hdcp_data *data = hdcp_data;
+ ssize_t byte;
+
+ if (!dev || !data || !rx_cert || !km_stored || !ek_pub_km || !msg_sz)
+ return -EINVAL;
+
+ cldev = to_mei_cl_device(dev);
+
+ verify_rxcert_in.header.api_version = HDCP_API_VERSION;
+ verify_rxcert_in.header.command_id = WIRED_VERIFY_RECEIVER_CERT;
+ verify_rxcert_in.header.status = ME_HDCP_STATUS_SUCCESS;
+ verify_rxcert_in.header.buffer_len =
+ WIRED_CMD_BUF_LEN_VERIFY_RECEIVER_CERT_IN;
+
+ verify_rxcert_in.port.integrated_port_type = data->port_type;
+ verify_rxcert_in.port.physical_port = data->port;
+
+ memcpy(&verify_rxcert_in.cert_rx, &rx_cert->cert_rx,
+ sizeof(rx_cert->cert_rx));
+ memcpy(verify_rxcert_in.r_rx, &rx_cert->r_rx, sizeof(rx_cert->r_rx));
+ memcpy(verify_rxcert_in.rx_caps, rx_cert->rx_caps, HDCP_2_2_RXCAPS_LEN);
+
+ byte = mei_cldev_send(cldev, (u8 *)&verify_rxcert_in,
+ sizeof(verify_rxcert_in));
+ if (byte < 0) {
+ dev_dbg(dev, "mei_cldev_send failed: %zd\n", byte);
+ return byte;
+ }
+
+ byte = mei_cldev_recv(cldev, (u8 *)&verify_rxcert_out,
+ sizeof(verify_rxcert_out));
+ if (byte < 0) {
+ dev_dbg(dev, "mei_cldev_recv failed: %zd\n", byte);
+ return byte;
+ }
+
+ if (verify_rxcert_out.header.status != ME_HDCP_STATUS_SUCCESS) {
+ dev_dbg(dev, "ME cmd 0x%08X Failed. Status: 0x%X\n",
+ WIRED_VERIFY_RECEIVER_CERT,
+ verify_rxcert_out.header.status);
+ return -EIO;
+ }
+
+ *km_stored = verify_rxcert_out.km_stored;
+ if (verify_rxcert_out.km_stored) {
+ ek_pub_km->msg_id = HDCP_2_2_AKE_STORED_KM;
+ *msg_sz = sizeof(struct hdcp2_ake_stored_km);
+ } else {
+ ek_pub_km->msg_id = HDCP_2_2_AKE_NO_STORED_KM;
+ *msg_sz = sizeof(struct hdcp2_ake_no_stored_km);
+ }
+
+ memcpy(ek_pub_km->e_kpub_km, &verify_rxcert_out.ekm_buff,
+ sizeof(verify_rxcert_out.ekm_buff));
+
+ return 0;
+}
+
static __attribute__((unused))
struct i915_hdcp_component_ops mei_hdcp_ops = {
.owner = THIS_MODULE,
.initiate_hdcp2_session = mei_initiate_hdcp2_session,
- .verify_receiver_cert_prepare_km = NULL,
+ .verify_receiver_cert_prepare_km = mei_verify_receiver_cert_prepare_km,
.verify_hprime = NULL,
.store_pairing_info = NULL,
.initiate_locality_check = NULL,
--
2.7.4
Ramalingam C
2018-11-27 10:43:14 UTC
Permalink
From: Tomas Winkler <***@intel.com>

Whitelist HDCP client for in kernel drm use

v2:
Rebased.
v3:
No changes.
v4:
No changes.
v5:
No changes.
v6:
No changes.
v7:
No changes.
v8:
No changes.

Signed-off-by: Tomas Winkler <***@intel.com>
---
drivers/misc/mei/bus-fixup.c | 16 ++++++++++++++++
1 file changed, 16 insertions(+)

diff --git a/drivers/misc/mei/bus-fixup.c b/drivers/misc/mei/bus-fixup.c
index 80215c312f0e..5fcac02233af 100644
--- a/drivers/misc/mei/bus-fixup.c
+++ b/drivers/misc/mei/bus-fixup.c
@@ -40,6 +40,9 @@ static const uuid_le mei_nfc_info_guid = MEI_UUID_NFC_INFO;
#define MEI_UUID_MKHIF_FIX UUID_LE(0x55213584, 0x9a29, 0x4916, \
0xba, 0xdf, 0xf, 0xb7, 0xed, 0x68, 0x2a, 0xeb)

+#define MEI_UUID_HDCP UUID_LE(0xB638AB7E, 0x94E2, 0x4EA2, \
+ 0xA5, 0x52, 0xD1, 0xC5, 0x4B, 0x62, 0x7F, 0x04)
+
#define MEI_UUID_ANY NULL_UUID_LE

/**
@@ -71,6 +74,18 @@ static void blacklist(struct mei_cl_device *cldev)
cldev->do_match = 0;
}

+/**
+ * whitelist - forcefully whitelist client
+ *
+ * @cldev: me clients device
+ */
+static void whitelist(struct mei_cl_device *cldev)
+{
+ dev_dbg(&cldev->dev, "running hook %s\n", __func__);
+
+ cldev->do_match = 1;
+}
+
#define OSTYPE_LINUX 2
struct mei_os_ver {
__le16 build;
@@ -472,6 +487,7 @@ static struct mei_fixup {
MEI_FIXUP(MEI_UUID_NFC_HCI, mei_nfc),
MEI_FIXUP(MEI_UUID_WD, mei_wd),
MEI_FIXUP(MEI_UUID_MKHIF_FIX, mei_mkhi_fix),
+ MEI_FIXUP(MEI_UUID_HDCP, whitelist),
};

/**
--
2.7.4
Ramalingam C
2018-11-27 10:43:20 UTC
Permalink
Requests for the verification of AKE_Send_H_prime.

ME will calculate the H and comparing it with received H_Prime.
The result will be returned as status.

Here AKE_Send_H_prime is a HDCP2.2 Authentication msg.

v2:
Rebased.
v3:
cldev is passed as first parameter [Tomas]
Redundant comments and cast are removed [Tomas]
v4:
%zd for ssize_t [Alexander]
%s/return -1/return -EIO [Alexander]
Styles and typos fixed [Uma]
v5:
Rebased.
v6:
No change.
v7:
Collected the Rb-ed by.
Rebasing.
v8:
Adjust to the new mei interface.
Fix for Kdoc.

Signed-off-by: Ramalingam C <***@intel.com>
Reviewed-by: Uma Shankar <***@intel.com>
---
drivers/misc/mei/hdcp/mei_hdcp.c | 60 +++++++++++++++++++++++++++++++++++++++-
1 file changed, 59 insertions(+), 1 deletion(-)

diff --git a/drivers/misc/mei/hdcp/mei_hdcp.c b/drivers/misc/mei/hdcp/mei_hdcp.c
index b38836dbdb66..f5e6db9c0026 100644
--- a/drivers/misc/mei/hdcp/mei_hdcp.c
+++ b/drivers/misc/mei/hdcp/mei_hdcp.c
@@ -164,12 +164,70 @@ mei_verify_receiver_cert_prepare_km(struct device *dev, void *hdcp_data,
return 0;
}

+/*
+ * mei_verify_hprime :
+ * Function to verify AKE_Send_H_prime received, through ME FW.
+ *
+ * @dev : device corresponding to the mei_cl_device
+ * @hdcp_data : Intel HW specific hdcp data
+ * @rx_hprime : AKE_Send_H_prime msg for mei verification
+ *
+ * Returns 0 on Success, <0 on Failure
+ */
+static int mei_verify_hprime(struct device *dev, void *hdcp_data,
+ struct hdcp2_ake_send_hprime *rx_hprime)
+{
+ struct wired_cmd_ake_send_hprime_in send_hprime_in = { { 0 } };
+ struct wired_cmd_ake_send_hprime_out send_hprime_out = { { 0 } };
+ struct mei_cl_device *cldev;
+ struct mei_hdcp_data *data = hdcp_data;
+ ssize_t byte;
+
+ if (!dev || !data || !rx_hprime)
+ return -EINVAL;
+
+ cldev = to_mei_cl_device(dev);
+
+ send_hprime_in.header.api_version = HDCP_API_VERSION;
+ send_hprime_in.header.command_id = WIRED_AKE_SEND_HPRIME;
+ send_hprime_in.header.status = ME_HDCP_STATUS_SUCCESS;
+ send_hprime_in.header.buffer_len = WIRED_CMD_BUF_LEN_AKE_SEND_HPRIME_IN;
+
+ send_hprime_in.port.integrated_port_type = data->port_type;
+ send_hprime_in.port.physical_port = data->port;
+
+ memcpy(send_hprime_in.h_prime, rx_hprime->h_prime,
+ sizeof(rx_hprime->h_prime));
+
+ byte = mei_cldev_send(cldev, (u8 *)&send_hprime_in,
+ sizeof(send_hprime_in));
+ if (byte < 0) {
+ dev_dbg(dev, "mei_cldev_send failed. %zd\n", byte);
+ return byte;
+ }
+
+ byte = mei_cldev_recv(cldev, (u8 *)&send_hprime_out,
+ sizeof(send_hprime_out));
+ if (byte < 0) {
+ dev_dbg(dev, "mei_cldev_recv failed. %zd\n", byte);
+ return byte;
+ }
+
+ if (send_hprime_out.header.status != ME_HDCP_STATUS_SUCCESS) {
+ dev_dbg(dev, "ME cmd 0x%08X Failed. Status: 0x%X\n",
+ WIRED_AKE_SEND_HPRIME, send_hprime_out.header.status);
+ return -EIO;
+ }
+
+ return 0;
+}
+
static __attribute__((unused))
struct i915_hdcp_component_ops mei_hdcp_ops = {
.owner = THIS_MODULE,
.initiate_hdcp2_session = mei_initiate_hdcp2_session,
.verify_receiver_cert_prepare_km = mei_verify_receiver_cert_prepare_km,
- .verify_hprime = NULL,
+ .verify_hprime = mei_verify_hprime,
.store_pairing_info = NULL,
.initiate_locality_check = NULL,
.verify_lprime = NULL,
--
2.7.4
Ramalingam C
2018-11-27 10:43:23 UTC
Permalink
Request to ME to verify the LPrime received from HDCP sink.

On Success, ME FW will verify the received Lprime by calculating and
comparing with L.

This represents the completion of Locality Check.

v2:
Rebased.
v3:
cldev is passed as first parameter [Tomas]
Redundant comments and cast are removed [Tomas]
v4:
%zd for ssize_t [Alexander]
%s/return -1/return -EIO [Alexander]
Style fixed [Uma]
v5:
Rebased.
v6:
No change.
v7:
Collected the Rb-ed by.
Rebasing.
v8:
Adjust to the new mei interface.
Fix for Kdoc.

Signed-off-by: Ramalingam C <***@intel.com>
Reviewed-by: Uma Shankar <***@intel.com>
---
drivers/misc/mei/hdcp/mei_hdcp.c | 61 +++++++++++++++++++++++++++++++++++++++-
1 file changed, 60 insertions(+), 1 deletion(-)

diff --git a/drivers/misc/mei/hdcp/mei_hdcp.c b/drivers/misc/mei/hdcp/mei_hdcp.c
index 6db98a51fce1..18387669ff15 100644
--- a/drivers/misc/mei/hdcp/mei_hdcp.c
+++ b/drivers/misc/mei/hdcp/mei_hdcp.c
@@ -338,6 +338,65 @@ static int mei_initiate_locality_check(struct device *dev, void *hdcp_data,
return 0;
}

+/*
+ * mei_verify_lprime : Function to verify lprime.
+ *
+ * @dev : device corresponding to the mei_cl_device
+ * @hdcp_data : Intel HW specific hdcp data
+ * @rx_lprime : LC_Send_L_prime msg for mei verification
+ *
+ * Returns 0 on Success, <0 on Failure
+ */
+static int mei_verify_lprime(struct device *dev, void *hdcp_data,
+ struct hdcp2_lc_send_lprime *rx_lprime)
+{
+ struct wired_cmd_validate_locality_in verify_lprime_in = { { 0 } };
+ struct wired_cmd_validate_locality_out verify_lprime_out = { { 0 } };
+ struct mei_cl_device *cldev;
+ struct mei_hdcp_data *data = hdcp_data;
+ ssize_t byte;
+
+ if (!dev || !data || !rx_lprime)
+ return -EINVAL;
+
+ cldev = to_mei_cl_device(dev);
+
+ verify_lprime_in.header.api_version = HDCP_API_VERSION;
+ verify_lprime_in.header.command_id = WIRED_VALIDATE_LOCALITY;
+ verify_lprime_in.header.status = ME_HDCP_STATUS_SUCCESS;
+ verify_lprime_in.header.buffer_len =
+ WIRED_CMD_BUF_LEN_VALIDATE_LOCALITY_IN;
+
+ verify_lprime_in.port.integrated_port_type = data->port_type;
+ verify_lprime_in.port.physical_port = data->port;
+
+ memcpy(verify_lprime_in.l_prime, rx_lprime->l_prime,
+ sizeof(rx_lprime->l_prime));
+
+ byte = mei_cldev_send(cldev, (u8 *)&verify_lprime_in,
+ sizeof(verify_lprime_in));
+ if (byte < 0) {
+ dev_dbg(dev, "mei_cldev_send failed. %zd\n", byte);
+ return byte;
+ }
+
+ byte = mei_cldev_recv(cldev, (u8 *)&verify_lprime_out,
+ sizeof(verify_lprime_out));
+ if (byte < 0) {
+ dev_dbg(dev, "mei_cldev_recv failed. %zd\n", byte);
+ return byte;
+ }
+
+ if (verify_lprime_out.header.status != ME_HDCP_STATUS_SUCCESS) {
+ dev_dbg(dev, "ME cmd 0x%08X failed. status: 0x%X\n",
+ WIRED_VALIDATE_LOCALITY,
+ verify_lprime_out.header.status);
+ return -EIO;
+ }
+
+ return 0;
+}
+
static __attribute__((unused))
struct i915_hdcp_component_ops mei_hdcp_ops = {
.owner = THIS_MODULE,
@@ -346,7 +405,7 @@ struct i915_hdcp_component_ops mei_hdcp_ops = {
.verify_hprime = mei_verify_hprime,
.store_pairing_info = mei_store_pairing_info,
.initiate_locality_check = mei_initiate_locality_check,
- .verify_lprime = NULL,
+ .verify_lprime = mei_verify_lprime,
.get_session_key = NULL,
.repeater_check_flow_prepare_ack = NULL,
.verify_mprime = NULL,
--
2.7.4
Ramalingam C
2018-11-27 10:43:18 UTC
Permalink
Request ME FW to start the HDCP2.2 session for an intel port.
Prepares payloads for command WIRED_INITIATE_HDCP2_SESSION and sends
to ME FW.

On Success, ME FW will start a HDCP2.2 session for the port and
provides the content for HDCP2.2 AKE_Init message.

v2:
Rebased.
v3:
cldev is add as a separate parameter [Tomas]
Redundant comment and typecast are removed [Tomas]
v4:
%zd is used for size [Alexander]
%s/return -1/return -EIO [Alexander]
Spellings in commit msg is fixed [Uma]
v5:
Rebased.
v6:
No change.
v7:
Collected the rb-ed by.
Realigning the patches in the series.
v8:
Adjust to the new mei interface.
Fix for kdoc.

Signed-off-by: Ramalingam C <***@intel.com>
Reviewed-by: Uma Shankar <***@intel.com>
---
drivers/misc/mei/hdcp/mei_hdcp.c | 85 ++++++++++++++++++++++++++++++++++++++++
1 file changed, 85 insertions(+)

diff --git a/drivers/misc/mei/hdcp/mei_hdcp.c b/drivers/misc/mei/hdcp/mei_hdcp.c
index d4067d17f382..ca8f82d25343 100644
--- a/drivers/misc/mei/hdcp/mei_hdcp.c
+++ b/drivers/misc/mei/hdcp/mei_hdcp.c
@@ -12,6 +12,91 @@
#include <linux/slab.h>
#include <linux/uuid.h>
#include <linux/mei_cl_bus.h>
+#include <linux/mei_hdcp.h>
+#include <drm/drm_connector.h>
+#include <drm/i915_component.h>
+
+#include "mei_hdcp.h"
+
+/*
+ * mei_initiate_hdcp2_session:
+ * Function to start a Wired HDCP2.2 Tx Session with ME FW
+ *
+ * @dev : device corresponding to the mei_cl_device
+ * @hdcp_data : Intel HW specific hdcp data
+ * @ake_data : AKE_Init msg from mei.
+ *
+ * Returns 0 on Success, <0 on Failure.
+ */
+static int mei_initiate_hdcp2_session(struct device *dev, void *hdcp_data,
+ struct hdcp2_ake_init *ake_data)
+{
+ struct wired_cmd_initiate_hdcp2_session_in session_init_in = { { 0 } };
+ struct wired_cmd_initiate_hdcp2_session_out
+ session_init_out = { { 0 } };
+ struct mei_cl_device *cldev;
+ struct mei_hdcp_data *data = hdcp_data;
+ ssize_t byte;
+
+ if (!dev || !data || !ake_data)
+ return -EINVAL;
+
+ cldev = to_mei_cl_device(dev);
+
+ session_init_in.header.api_version = HDCP_API_VERSION;
+ session_init_in.header.command_id = WIRED_INITIATE_HDCP2_SESSION;
+ session_init_in.header.status = ME_HDCP_STATUS_SUCCESS;
+ session_init_in.header.buffer_len =
+ WIRED_CMD_BUF_LEN_INITIATE_HDCP2_SESSION_IN;
+
+ session_init_in.port.integrated_port_type = data->port_type;
+ session_init_in.port.physical_port = data->port;
+ session_init_in.protocol = (uint8_t)data->protocol;
+
+ byte = mei_cldev_send(cldev, (u8 *)&session_init_in,
+ sizeof(session_init_in));
+ if (byte < 0) {
+ dev_dbg(dev, "mei_cldev_send failed. %zd\n", byte);
+ return byte;
+ }
+
+ byte = mei_cldev_recv(cldev, (u8 *)&session_init_out,
+ sizeof(session_init_out));
+ if (byte < 0) {
+ dev_dbg(dev, "mei_cldev_recv failed. %zd\n", byte);
+ return byte;
+ }
+
+ if (session_init_out.header.status != ME_HDCP_STATUS_SUCCESS) {
+ dev_dbg(dev, "ME cmd 0x%08X Failed. Status: 0x%X\n",
+ WIRED_INITIATE_HDCP2_SESSION,
+ session_init_out.header.status);
+ return -EIO;
+ }
+
+ ake_data->msg_id = HDCP_2_2_AKE_INIT;
+ ake_data->tx_caps = session_init_out.tx_caps;
+ memcpy(ake_data->r_tx, session_init_out.r_tx,
+ sizeof(session_init_out.r_tx));
+
+ return 0;
+}
+
+static __attribute__((unused))
+struct i915_hdcp_component_ops mei_hdcp_ops = {
+ .owner = THIS_MODULE,
+ .initiate_hdcp2_session = mei_initiate_hdcp2_session,
+ .verify_receiver_cert_prepare_km = NULL,
+ .verify_hprime = NULL,
+ .store_pairing_info = NULL,
+ .initiate_locality_check = NULL,
+ .verify_lprime = NULL,
+ .get_session_key = NULL,
+ .repeater_check_flow_prepare_ack = NULL,
+ .verify_mprime = NULL,
+ .enable_hdcp_authentication = NULL,
+ .close_hdcp_session = NULL,
+};

static int mei_hdcp_probe(struct mei_cl_device *cldev,
const struct mei_cl_device_id *id)
--
2.7.4
Ramalingam C
2018-11-27 10:43:07 UTC
Permalink
Implements the link integrity check once in 500mSec.

Once encryption is enabled, an ongoing Link Integrity Check is
performed by the HDCP Receiver to check that cipher synchronization
is maintained between the HDCP Transmitter and the HDCP Receiver.

On the detection of synchronization lost, the HDCP Receiver must assert
the corresponding bits of the RxStatus register. The Transmitter polls
the RxStatus register and it may initiate re-authentication.

v2:
Rebased.
v3:
No Changes.
v4:
enum check_link_response is used check the link status [Uma]
v5:
Rebased as part of patch reordering.
v6:
Required members of intel_hdcp is defined [Sean Paul]
v7:
hdcp2_check_link is cancelled at required places.
v8:
Rebased for the component i/f changes.
Errors due to the sinks are reported as DEBUG logs.

Signed-off-by: Ramalingam C <***@intel.com>
Reviewed-by: Uma Shankar <***@intel.com>
---
drivers/gpu/drm/i915/intel_display.c | 11 +++--
drivers/gpu/drm/i915/intel_drv.h | 5 +++
drivers/gpu/drm/i915/intel_hdcp.c | 83 +++++++++++++++++++++++++++++++++++-
include/drm/drm_hdcp.h | 8 ++++
4 files changed, 103 insertions(+), 4 deletions(-)

diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c
index e9f4e22b2a4e..fc63babce165 100644
--- a/drivers/gpu/drm/i915/intel_display.c
+++ b/drivers/gpu/drm/i915/intel_display.c
@@ -15833,15 +15833,20 @@ static void intel_hpd_poll_fini(struct drm_device *dev)
{
struct intel_connector *connector;
struct drm_connector_list_iter conn_iter;
+ struct intel_hdcp *hdcp;

/* Kill all the work that may have been queued by hpd. */
drm_connector_list_iter_begin(dev, &conn_iter);
for_each_intel_connector_iter(connector, &conn_iter) {
+ hdcp = &connector->hdcp;
+
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);
+ if (hdcp->shim) {
+ cancel_delayed_work_sync(&hdcp->check_work);
+ cancel_work_sync(&hdcp->prop_work);
+ if (hdcp->hdcp2_supported)
+ cancel_delayed_work_sync(&hdcp->hdcp2_check_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 24d258488efe..e6e32bf52568 100644
--- a/drivers/gpu/drm/i915/intel_drv.h
+++ b/drivers/gpu/drm/i915/intel_drv.h
@@ -403,6 +403,9 @@ struct intel_hdcp_shim {
*/
int (*config_stream_type)(struct intel_digital_port *intel_dig_port,
void *buf, size_t size);
+
+ /* HDCP2.2 Link Integrity Check */
+ int (*check_2_2_link)(struct intel_digital_port *intel_dig_port);
};

struct intel_hdcp {
@@ -445,6 +448,8 @@ struct intel_hdcp {
* over re-Auth has to be triggered.
*/
u32 seq_num_m;
+
+ struct delayed_work hdcp2_check_work;
};

struct intel_connector {
diff --git a/drivers/gpu/drm/i915/intel_hdcp.c b/drivers/gpu/drm/i915/intel_hdcp.c
index 679f3c164582..98b112395a5a 100644
--- a/drivers/gpu/drm/i915/intel_hdcp.c
+++ b/drivers/gpu/drm/i915/intel_hdcp.c
@@ -1626,6 +1626,81 @@ static int _intel_hdcp2_disable(struct intel_connector *connector)
return ret;
}

+/* Implements the Link Integrity Check for HDCP2.2 */
+static int intel_hdcp2_check_link(struct intel_connector *connector)
+{
+ struct intel_digital_port *intel_dig_port = conn_to_dig_port(connector);
+ struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
+ struct intel_hdcp *hdcp = &connector->hdcp;
+ enum port port = connector->encoder->port;
+
+ int ret = 0;
+
+ if (!hdcp->shim)
+ return -ENOENT;
+
+ mutex_lock(&hdcp->mutex);
+
+ if (hdcp->value == DRM_MODE_CONTENT_PROTECTION_UNDESIRED)
+ goto out;
+
+ if (!(I915_READ(HDCP2_STATUS_DDI(port)) & LINK_ENCRYPTION_STATUS)) {
+ DRM_ERROR("HDCP2.2 check failed: link is not encrypted, %x\n",
+ I915_READ(HDCP2_STATUS_DDI(port)));
+ ret = -ENXIO;
+ hdcp->value = DRM_MODE_CONTENT_PROTECTION_DESIRED;
+ schedule_work(&hdcp->prop_work);
+ goto out;
+ }
+
+ ret = hdcp->shim->check_2_2_link(intel_dig_port);
+ if (ret == DRM_HDCP_LINK_PROTECTED) {
+ if (hdcp->value != DRM_MODE_CONTENT_PROTECTION_UNDESIRED) {
+ hdcp->value = DRM_MODE_CONTENT_PROTECTION_ENABLED;
+ schedule_work(&hdcp->prop_work);
+ }
+ goto out;
+ }
+
+ DRM_DEBUG_KMS("[%s:%d] HDCP2.2 link failed, retrying auth\n",
+ connector->base.name, connector->base.base.id);
+
+ ret = _intel_hdcp2_disable(connector);
+ if (ret) {
+ DRM_ERROR("[%s:%d] Failed to disable hdcp2.2 (%d)\n",
+ connector->base.name, connector->base.base.id, ret);
+ hdcp->value = DRM_MODE_CONTENT_PROTECTION_DESIRED;
+ schedule_work(&hdcp->prop_work);
+ goto out;
+ }
+
+ ret = _intel_hdcp2_enable(connector);
+ if (ret) {
+ DRM_DEBUG_KMS("[%s:%d] Failed to enable hdcp2.2 (%d)\n",
+ connector->base.name, connector->base.base.id,
+ ret);
+ hdcp->value = DRM_MODE_CONTENT_PROTECTION_DESIRED;
+ schedule_work(&hdcp->prop_work);
+ goto out;
+ }
+
+out:
+ mutex_unlock(&hdcp->mutex);
+ return ret;
+}
+
+static void intel_hdcp2_check_work(struct work_struct *work)
+{
+ struct intel_hdcp *hdcp = container_of(to_delayed_work(work),
+ struct intel_hdcp,
+ hdcp2_check_work);
+ struct intel_connector *connector = intel_hdcp_to_connector(hdcp);
+
+ if (!intel_hdcp2_check_link(connector))
+ schedule_delayed_work(&hdcp->hdcp2_check_work,
+ DRM_HDCP2_CHECK_PERIOD_MS);
+}
+
static int i915_hdcp_component_master_bind(struct device *dev)
{
struct drm_i915_private *dev_priv = kdev_to_i915(dev);
@@ -1762,6 +1837,7 @@ static void intel_hdcp2_init(struct intel_connector *connector)
return;
}

+ INIT_DELAYED_WORK(&hdcp->hdcp2_check_work, intel_hdcp2_check_work);
hdcp->hdcp2_supported = 1;
}

@@ -1836,8 +1912,12 @@ int intel_hdcp_enable(struct intel_connector *connector)
* Considering that HDCP2.2 is more secure than HDCP1.4, If the setup
* is capable of HDCP2.2, it is preferred to use HDCP2.2.
*/
- if (intel_hdcp2_capable(connector))
+ if (intel_hdcp2_capable(connector)) {
ret = _intel_hdcp2_enable(connector);
+ if (!ret)
+ schedule_delayed_work(&hdcp->hdcp2_check_work,
+ DRM_HDCP2_CHECK_PERIOD_MS);
+ }

/* When HDCP2.2 fails, HDCP1.4 will be attempted */
if (ret && intel_hdcp_capable(connector)) {
@@ -1876,6 +1956,7 @@ int intel_hdcp_disable(struct intel_connector *connector)

mutex_unlock(&hdcp->mutex);
cancel_delayed_work_sync(&hdcp->check_work);
+ cancel_delayed_work_sync(&hdcp->hdcp2_check_work);
return ret;
}

diff --git a/include/drm/drm_hdcp.h b/include/drm/drm_hdcp.h
index 3e45a7618ab3..be07a2b56d23 100644
--- a/include/drm/drm_hdcp.h
+++ b/include/drm/drm_hdcp.h
@@ -11,6 +11,14 @@

/* Period of hdcp checks (to ensure we're still authenticated) */
#define DRM_HDCP_CHECK_PERIOD_MS (128 * 16)
+#define DRM_HDCP2_CHECK_PERIOD_MS 500
+
+enum check_link_response {
+ DRM_HDCP_LINK_PROTECTED = 0,
+ DRM_HDCP_TOPOLOGY_CHANGE,
+ DRM_HDCP_LINK_INTEGRITY_FAILURE,
+ DRM_HDCP_REAUTH_REQUEST
+};

/* Shared lengths/masks between HDMI/DVI/DisplayPort */
#define DRM_HDCP_AN_LEN 8
--
2.7.4
Daniel Vetter
2018-12-06 13:27:21 UTC
Permalink
Post by Ramalingam C
Implements the link integrity check once in 500mSec.
Once encryption is enabled, an ongoing Link Integrity Check is
performed by the HDCP Receiver to check that cipher synchronization
is maintained between the HDCP Transmitter and the HDCP Receiver.
On the detection of synchronization lost, the HDCP Receiver must assert
the corresponding bits of the RxStatus register. The Transmitter polls
the RxStatus register and it may initiate re-authentication.
Rebased.
No Changes.
enum check_link_response is used check the link status [Uma]
Rebased as part of patch reordering.
Required members of intel_hdcp is defined [Sean Paul]
hdcp2_check_link is cancelled at required places.
Rebased for the component i/f changes.
Errors due to the sinks are reported as DEBUG logs.
---
drivers/gpu/drm/i915/intel_display.c | 11 +++--
drivers/gpu/drm/i915/intel_drv.h | 5 +++
drivers/gpu/drm/i915/intel_hdcp.c | 83 +++++++++++++++++++++++++++++++++++-
include/drm/drm_hdcp.h | 8 ++++
4 files changed, 103 insertions(+), 4 deletions(-)
diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c
index e9f4e22b2a4e..fc63babce165 100644
--- a/drivers/gpu/drm/i915/intel_display.c
+++ b/drivers/gpu/drm/i915/intel_display.c
@@ -15833,15 +15833,20 @@ static void intel_hpd_poll_fini(struct drm_device *dev)
{
struct intel_connector *connector;
struct drm_connector_list_iter conn_iter;
+ struct intel_hdcp *hdcp;
/* Kill all the work that may have been queued by hpd. */
drm_connector_list_iter_begin(dev, &conn_iter);
for_each_intel_connector_iter(connector, &conn_iter) {
+ hdcp = &connector->hdcp;
+
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);
+ if (hdcp->shim) {
+ cancel_delayed_work_sync(&hdcp->check_work);
+ cancel_work_sync(&hdcp->prop_work);
+ if (hdcp->hdcp2_supported)
+ cancel_delayed_work_sync(&hdcp->hdcp2_check_work);
Locking of these workers is always tricky ... why can't we use the same
worker for both checking hdcp2 and hdcp1 link status?

Of course need to use the right timeout and call the right functions, but
I think gives us less duplication in the complicated code.
Post by Ramalingam C
}
}
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 24d258488efe..e6e32bf52568 100644
--- a/drivers/gpu/drm/i915/intel_drv.h
+++ b/drivers/gpu/drm/i915/intel_drv.h
@@ -403,6 +403,9 @@ struct intel_hdcp_shim {
*/
int (*config_stream_type)(struct intel_digital_port *intel_dig_port,
void *buf, size_t size);
+
+ /* HDCP2.2 Link Integrity Check */
+ int (*check_2_2_link)(struct intel_digital_port *intel_dig_port);
};
struct intel_hdcp {
@@ -445,6 +448,8 @@ struct intel_hdcp {
* over re-Auth has to be triggered.
*/
u32 seq_num_m;
+
+ struct delayed_work hdcp2_check_work;
};
struct intel_connector {
diff --git a/drivers/gpu/drm/i915/intel_hdcp.c b/drivers/gpu/drm/i915/intel_hdcp.c
index 679f3c164582..98b112395a5a 100644
--- a/drivers/gpu/drm/i915/intel_hdcp.c
+++ b/drivers/gpu/drm/i915/intel_hdcp.c
@@ -1626,6 +1626,81 @@ static int _intel_hdcp2_disable(struct intel_connector *connector)
return ret;
}
+/* Implements the Link Integrity Check for HDCP2.2 */
+static int intel_hdcp2_check_link(struct intel_connector *connector)
+{
+ struct intel_digital_port *intel_dig_port = conn_to_dig_port(connector);
+ struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
+ struct intel_hdcp *hdcp = &connector->hdcp;
+ enum port port = connector->encoder->port;
+
+ int ret = 0;
+
+ if (!hdcp->shim)
+ return -ENOENT;
Why is this possible? Should we wrap it into at least a WARN_ON?
Post by Ramalingam C
+
+ mutex_lock(&hdcp->mutex);
+
+ if (hdcp->value == DRM_MODE_CONTENT_PROTECTION_UNDESIRED)
+ goto out;
+
+ if (!(I915_READ(HDCP2_STATUS_DDI(port)) & LINK_ENCRYPTION_STATUS)) {
+ DRM_ERROR("HDCP2.2 check failed: link is not encrypted, %x\n",
+ I915_READ(HDCP2_STATUS_DDI(port)));
+ ret = -ENXIO;
+ hdcp->value = DRM_MODE_CONTENT_PROTECTION_DESIRED;
+ schedule_work(&hdcp->prop_work);
+ goto out;
+ }
+
+ ret = hdcp->shim->check_2_2_link(intel_dig_port);
+ if (ret == DRM_HDCP_LINK_PROTECTED) {
+ if (hdcp->value != DRM_MODE_CONTENT_PROTECTION_UNDESIRED) {
+ hdcp->value = DRM_MODE_CONTENT_PROTECTION_ENABLED;
+ schedule_work(&hdcp->prop_work);
+ }
+ goto out;
+ }
+
+ DRM_DEBUG_KMS("[%s:%d] HDCP2.2 link failed, retrying auth\n",
+ connector->base.name, connector->base.base.id);
+
+ ret = _intel_hdcp2_disable(connector);
+ if (ret) {
+ DRM_ERROR("[%s:%d] Failed to disable hdcp2.2 (%d)\n",
+ connector->base.name, connector->base.base.id, ret);
+ hdcp->value = DRM_MODE_CONTENT_PROTECTION_DESIRED;
+ schedule_work(&hdcp->prop_work);
+ goto out;
+ }
+
+ ret = _intel_hdcp2_enable(connector);
+ if (ret) {
+ DRM_DEBUG_KMS("[%s:%d] Failed to enable hdcp2.2 (%d)\n",
+ connector->base.name, connector->base.base.id,
+ ret);
+ hdcp->value = DRM_MODE_CONTENT_PROTECTION_DESIRED;
+ schedule_work(&hdcp->prop_work);
+ goto out;
+ }
+
+ mutex_unlock(&hdcp->mutex);
+ return ret;
+}
+
+static void intel_hdcp2_check_work(struct work_struct *work)
+{
+ struct intel_hdcp *hdcp = container_of(to_delayed_work(work),
+ struct intel_hdcp,
+ hdcp2_check_work);
+ struct intel_connector *connector = intel_hdcp_to_connector(hdcp);
+
+ if (!intel_hdcp2_check_link(connector))
+ schedule_delayed_work(&hdcp->hdcp2_check_work,
+ DRM_HDCP2_CHECK_PERIOD_MS);
+}
+
static int i915_hdcp_component_master_bind(struct device *dev)
{
struct drm_i915_private *dev_priv = kdev_to_i915(dev);
@@ -1762,6 +1837,7 @@ static void intel_hdcp2_init(struct intel_connector *connector)
return;
}
+ INIT_DELAYED_WORK(&hdcp->hdcp2_check_work, intel_hdcp2_check_work);
hdcp->hdcp2_supported = 1;
}
@@ -1836,8 +1912,12 @@ int intel_hdcp_enable(struct intel_connector *connector)
* Considering that HDCP2.2 is more secure than HDCP1.4, If the setup
* is capable of HDCP2.2, it is preferred to use HDCP2.2.
*/
- if (intel_hdcp2_capable(connector))
+ if (intel_hdcp2_capable(connector)) {
ret = _intel_hdcp2_enable(connector);
+ if (!ret)
+ schedule_delayed_work(&hdcp->hdcp2_check_work,
+ DRM_HDCP2_CHECK_PERIOD_MS);
+ }
/* When HDCP2.2 fails, HDCP1.4 will be attempted */
if (ret && intel_hdcp_capable(connector)) {
@@ -1876,6 +1956,7 @@ int intel_hdcp_disable(struct intel_connector *connector)
mutex_unlock(&hdcp->mutex);
cancel_delayed_work_sync(&hdcp->check_work);
+ cancel_delayed_work_sync(&hdcp->hdcp2_check_work);
return ret;
}
diff --git a/include/drm/drm_hdcp.h b/include/drm/drm_hdcp.h
index 3e45a7618ab3..be07a2b56d23 100644
--- a/include/drm/drm_hdcp.h
+++ b/include/drm/drm_hdcp.h
@@ -11,6 +11,14 @@
/* Period of hdcp checks (to ensure we're still authenticated) */
#define DRM_HDCP_CHECK_PERIOD_MS (128 * 16)
+#define DRM_HDCP2_CHECK_PERIOD_MS 500
+
+enum check_link_response {
+ DRM_HDCP_LINK_PROTECTED = 0,
+ DRM_HDCP_TOPOLOGY_CHANGE,
+ DRM_HDCP_LINK_INTEGRITY_FAILURE,
+ DRM_HDCP_REAUTH_REQUEST
+};
The drm_hdcp.h changes need to be all split out into sepearate patches I
think. Just for highlighting of the new shared bits. Also applies to the
previous one.

Otherwise looks all reasonable.
-Daniel
Post by Ramalingam C
/* Shared lengths/masks between HDMI/DVI/DisplayPort */
#define DRM_HDCP_AN_LEN 8
--
2.7.4
--
Daniel Vetter
Software Engineer, Intel Corporation
http://blog.ffwll.ch
Daniel Vetter
2018-12-06 13:41:19 UTC
Permalink
Post by Daniel Vetter
Post by Ramalingam C
Implements the link integrity check once in 500mSec.
Once encryption is enabled, an ongoing Link Integrity Check is
performed by the HDCP Receiver to check that cipher synchronization
is maintained between the HDCP Transmitter and the HDCP Receiver.
On the detection of synchronization lost, the HDCP Receiver must assert
the corresponding bits of the RxStatus register. The Transmitter polls
the RxStatus register and it may initiate re-authentication.
Rebased.
No Changes.
enum check_link_response is used check the link status [Uma]
Rebased as part of patch reordering.
Required members of intel_hdcp is defined [Sean Paul]
hdcp2_check_link is cancelled at required places.
Rebased for the component i/f changes.
Errors due to the sinks are reported as DEBUG logs.
---
drivers/gpu/drm/i915/intel_display.c | 11 +++--
drivers/gpu/drm/i915/intel_drv.h | 5 +++
drivers/gpu/drm/i915/intel_hdcp.c | 83 +++++++++++++++++++++++++++++++++++-
include/drm/drm_hdcp.h | 8 ++++
4 files changed, 103 insertions(+), 4 deletions(-)
diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c
index e9f4e22b2a4e..fc63babce165 100644
--- a/drivers/gpu/drm/i915/intel_display.c
+++ b/drivers/gpu/drm/i915/intel_display.c
@@ -15833,15 +15833,20 @@ static void intel_hpd_poll_fini(struct drm_device *dev)
{
struct intel_connector *connector;
struct drm_connector_list_iter conn_iter;
+ struct intel_hdcp *hdcp;
/* Kill all the work that may have been queued by hpd. */
drm_connector_list_iter_begin(dev, &conn_iter);
for_each_intel_connector_iter(connector, &conn_iter) {
+ hdcp = &connector->hdcp;
+
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);
+ if (hdcp->shim) {
+ cancel_delayed_work_sync(&hdcp->check_work);
+ cancel_work_sync(&hdcp->prop_work);
+ if (hdcp->hdcp2_supported)
+ cancel_delayed_work_sync(&hdcp->hdcp2_check_work);
Locking of these workers is always tricky ... why can't we use the same
worker for both checking hdcp2 and hdcp1 link status?
Of course need to use the right timeout and call the right functions, but
I think gives us less duplication in the complicated code.
Locking and also the logic to update the content protection property. I
think sharing the entire main function, and splitting to hdcp1 vs. hdcp2
in the check/enable/disable functions would be neat.
-Daniel
Post by Daniel Vetter
Post by Ramalingam C
}
}
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 24d258488efe..e6e32bf52568 100644
--- a/drivers/gpu/drm/i915/intel_drv.h
+++ b/drivers/gpu/drm/i915/intel_drv.h
@@ -403,6 +403,9 @@ struct intel_hdcp_shim {
*/
int (*config_stream_type)(struct intel_digital_port *intel_dig_port,
void *buf, size_t size);
+
+ /* HDCP2.2 Link Integrity Check */
+ int (*check_2_2_link)(struct intel_digital_port *intel_dig_port);
};
struct intel_hdcp {
@@ -445,6 +448,8 @@ struct intel_hdcp {
* over re-Auth has to be triggered.
*/
u32 seq_num_m;
+
+ struct delayed_work hdcp2_check_work;
};
struct intel_connector {
diff --git a/drivers/gpu/drm/i915/intel_hdcp.c b/drivers/gpu/drm/i915/intel_hdcp.c
index 679f3c164582..98b112395a5a 100644
--- a/drivers/gpu/drm/i915/intel_hdcp.c
+++ b/drivers/gpu/drm/i915/intel_hdcp.c
@@ -1626,6 +1626,81 @@ static int _intel_hdcp2_disable(struct intel_connector *connector)
return ret;
}
+/* Implements the Link Integrity Check for HDCP2.2 */
+static int intel_hdcp2_check_link(struct intel_connector *connector)
+{
+ struct intel_digital_port *intel_dig_port = conn_to_dig_port(connector);
+ struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
+ struct intel_hdcp *hdcp = &connector->hdcp;
+ enum port port = connector->encoder->port;
+
+ int ret = 0;
+
+ if (!hdcp->shim)
+ return -ENOENT;
Why is this possible? Should we wrap it into at least a WARN_ON?
Post by Ramalingam C
+
+ mutex_lock(&hdcp->mutex);
+
+ if (hdcp->value == DRM_MODE_CONTENT_PROTECTION_UNDESIRED)
+ goto out;
+
+ if (!(I915_READ(HDCP2_STATUS_DDI(port)) & LINK_ENCRYPTION_STATUS)) {
+ DRM_ERROR("HDCP2.2 check failed: link is not encrypted, %x\n",
+ I915_READ(HDCP2_STATUS_DDI(port)));
+ ret = -ENXIO;
+ hdcp->value = DRM_MODE_CONTENT_PROTECTION_DESIRED;
+ schedule_work(&hdcp->prop_work);
+ goto out;
+ }
+
+ ret = hdcp->shim->check_2_2_link(intel_dig_port);
+ if (ret == DRM_HDCP_LINK_PROTECTED) {
+ if (hdcp->value != DRM_MODE_CONTENT_PROTECTION_UNDESIRED) {
+ hdcp->value = DRM_MODE_CONTENT_PROTECTION_ENABLED;
+ schedule_work(&hdcp->prop_work);
+ }
+ goto out;
+ }
+
+ DRM_DEBUG_KMS("[%s:%d] HDCP2.2 link failed, retrying auth\n",
+ connector->base.name, connector->base.base.id);
+
+ ret = _intel_hdcp2_disable(connector);
+ if (ret) {
+ DRM_ERROR("[%s:%d] Failed to disable hdcp2.2 (%d)\n",
+ connector->base.name, connector->base.base.id, ret);
+ hdcp->value = DRM_MODE_CONTENT_PROTECTION_DESIRED;
+ schedule_work(&hdcp->prop_work);
+ goto out;
+ }
+
+ ret = _intel_hdcp2_enable(connector);
+ if (ret) {
+ DRM_DEBUG_KMS("[%s:%d] Failed to enable hdcp2.2 (%d)\n",
+ connector->base.name, connector->base.base.id,
+ ret);
+ hdcp->value = DRM_MODE_CONTENT_PROTECTION_DESIRED;
+ schedule_work(&hdcp->prop_work);
+ goto out;
+ }
+
+ mutex_unlock(&hdcp->mutex);
+ return ret;
+}
+
+static void intel_hdcp2_check_work(struct work_struct *work)
+{
+ struct intel_hdcp *hdcp = container_of(to_delayed_work(work),
+ struct intel_hdcp,
+ hdcp2_check_work);
+ struct intel_connector *connector = intel_hdcp_to_connector(hdcp);
+
+ if (!intel_hdcp2_check_link(connector))
+ schedule_delayed_work(&hdcp->hdcp2_check_work,
+ DRM_HDCP2_CHECK_PERIOD_MS);
+}
+
static int i915_hdcp_component_master_bind(struct device *dev)
{
struct drm_i915_private *dev_priv = kdev_to_i915(dev);
@@ -1762,6 +1837,7 @@ static void intel_hdcp2_init(struct intel_connector *connector)
return;
}
+ INIT_DELAYED_WORK(&hdcp->hdcp2_check_work, intel_hdcp2_check_work);
hdcp->hdcp2_supported = 1;
}
@@ -1836,8 +1912,12 @@ int intel_hdcp_enable(struct intel_connector *connector)
* Considering that HDCP2.2 is more secure than HDCP1.4, If the setup
* is capable of HDCP2.2, it is preferred to use HDCP2.2.
*/
- if (intel_hdcp2_capable(connector))
+ if (intel_hdcp2_capable(connector)) {
ret = _intel_hdcp2_enable(connector);
+ if (!ret)
+ schedule_delayed_work(&hdcp->hdcp2_check_work,
+ DRM_HDCP2_CHECK_PERIOD_MS);
+ }
/* When HDCP2.2 fails, HDCP1.4 will be attempted */
if (ret && intel_hdcp_capable(connector)) {
@@ -1876,6 +1956,7 @@ int intel_hdcp_disable(struct intel_connector *connector)
mutex_unlock(&hdcp->mutex);
cancel_delayed_work_sync(&hdcp->check_work);
+ cancel_delayed_work_sync(&hdcp->hdcp2_check_work);
return ret;
}
diff --git a/include/drm/drm_hdcp.h b/include/drm/drm_hdcp.h
index 3e45a7618ab3..be07a2b56d23 100644
--- a/include/drm/drm_hdcp.h
+++ b/include/drm/drm_hdcp.h
@@ -11,6 +11,14 @@
/* Period of hdcp checks (to ensure we're still authenticated) */
#define DRM_HDCP_CHECK_PERIOD_MS (128 * 16)
+#define DRM_HDCP2_CHECK_PERIOD_MS 500
+
+enum check_link_response {
+ DRM_HDCP_LINK_PROTECTED = 0,
+ DRM_HDCP_TOPOLOGY_CHANGE,
+ DRM_HDCP_LINK_INTEGRITY_FAILURE,
+ DRM_HDCP_REAUTH_REQUEST
+};
The drm_hdcp.h changes need to be all split out into sepearate patches I
think. Just for highlighting of the new shared bits. Also applies to the
previous one.
Otherwise looks all reasonable.
-Daniel
Post by Ramalingam C
/* Shared lengths/masks between HDMI/DVI/DisplayPort */
#define DRM_HDCP_AN_LEN 8
--
2.7.4
--
Daniel Vetter
Software Engineer, Intel Corporation
http://blog.ffwll.ch
--
Daniel Vetter
Software Engineer, Intel Corporation
http://blog.ffwll.ch
C, Ramalingam
2018-12-07 06:46:11 UTC
Permalink
Post by Daniel Vetter
Post by Ramalingam C
Implements the link integrity check once in 500mSec.
Once encryption is enabled, an ongoing Link Integrity Check is
performed by the HDCP Receiver to check that cipher synchronization
is maintained between the HDCP Transmitter and the HDCP Receiver.
On the detection of synchronization lost, the HDCP Receiver must assert
the corresponding bits of the RxStatus register. The Transmitter polls
the RxStatus register and it may initiate re-authentication.
Rebased.
No Changes.
enum check_link_response is used check the link status [Uma]
Rebased as part of patch reordering.
Required members of intel_hdcp is defined [Sean Paul]
hdcp2_check_link is cancelled at required places.
Rebased for the component i/f changes.
Errors due to the sinks are reported as DEBUG logs.
---
drivers/gpu/drm/i915/intel_display.c | 11 +++--
drivers/gpu/drm/i915/intel_drv.h | 5 +++
drivers/gpu/drm/i915/intel_hdcp.c | 83 +++++++++++++++++++++++++++++++++++-
include/drm/drm_hdcp.h | 8 ++++
4 files changed, 103 insertions(+), 4 deletions(-)
diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c
index e9f4e22b2a4e..fc63babce165 100644
--- a/drivers/gpu/drm/i915/intel_display.c
+++ b/drivers/gpu/drm/i915/intel_display.c
@@ -15833,15 +15833,20 @@ static void intel_hpd_poll_fini(struct drm_device *dev)
{
struct intel_connector *connector;
struct drm_connector_list_iter conn_iter;
+ struct intel_hdcp *hdcp;
/* Kill all the work that may have been queued by hpd. */
drm_connector_list_iter_begin(dev, &conn_iter);
for_each_intel_connector_iter(connector, &conn_iter) {
+ hdcp = &connector->hdcp;
+
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);
+ if (hdcp->shim) {
+ cancel_delayed_work_sync(&hdcp->check_work);
+ cancel_work_sync(&hdcp->prop_work);
+ if (hdcp->hdcp2_supported)
+ cancel_delayed_work_sync(&hdcp->hdcp2_check_work);
Locking of these workers is always tricky ... why can't we use the same
worker for both checking hdcp2 and hdcp1 link status?
Doable similar to how we are doing hdcp_enable. Needs the tracking of the spec in
use to pick proper timeout and functions.

I will look into it.
Post by Daniel Vetter
Of course need to use the right timeout and call the right functions, but
I think gives us less duplication in the complicated code.
Post by Ramalingam C
}
}
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 24d258488efe..e6e32bf52568 100644
--- a/drivers/gpu/drm/i915/intel_drv.h
+++ b/drivers/gpu/drm/i915/intel_drv.h
@@ -403,6 +403,9 @@ struct intel_hdcp_shim {
*/
int (*config_stream_type)(struct intel_digital_port *intel_dig_port,
void *buf, size_t size);
+
+ /* HDCP2.2 Link Integrity Check */
+ int (*check_2_2_link)(struct intel_digital_port *intel_dig_port);
};
struct intel_hdcp {
@@ -445,6 +448,8 @@ struct intel_hdcp {
* over re-Auth has to be triggered.
*/
u32 seq_num_m;
+
+ struct delayed_work hdcp2_check_work;
};
struct intel_connector {
diff --git a/drivers/gpu/drm/i915/intel_hdcp.c b/drivers/gpu/drm/i915/intel_hdcp.c
index 679f3c164582..98b112395a5a 100644
--- a/drivers/gpu/drm/i915/intel_hdcp.c
+++ b/drivers/gpu/drm/i915/intel_hdcp.c
@@ -1626,6 +1626,81 @@ static int _intel_hdcp2_disable(struct intel_connector *connector)
return ret;
}
+/* Implements the Link Integrity Check for HDCP2.2 */
+static int intel_hdcp2_check_link(struct intel_connector *connector)
+{
+ struct intel_digital_port *intel_dig_port = conn_to_dig_port(connector);
+ struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
+ struct intel_hdcp *hdcp = &connector->hdcp;
+ enum port port = connector->encoder->port;
+
+ int ret = 0;
+
+ if (!hdcp->shim)
+ return -ENOENT;
Why is this possible? Should we wrap it into at least a WARN_ON?
we could land here from CP_IRQ, with hdcp_exit called for shim removal.
But could avoid if we assert on the HDCP auth/encrypt status of the port
before check_link.
Post by Daniel Vetter
Post by Ramalingam C
+
+ mutex_lock(&hdcp->mutex);
+
+ if (hdcp->value == DRM_MODE_CONTENT_PROTECTION_UNDESIRED)
+ goto out;
+
+ if (!(I915_READ(HDCP2_STATUS_DDI(port)) & LINK_ENCRYPTION_STATUS)) {
+ DRM_ERROR("HDCP2.2 check failed: link is not encrypted, %x\n",
+ I915_READ(HDCP2_STATUS_DDI(port)));
+ ret = -ENXIO;
+ hdcp->value = DRM_MODE_CONTENT_PROTECTION_DESIRED;
+ schedule_work(&hdcp->prop_work);
+ goto out;
+ }
+
+ ret = hdcp->shim->check_2_2_link(intel_dig_port);
+ if (ret == DRM_HDCP_LINK_PROTECTED) {
+ if (hdcp->value != DRM_MODE_CONTENT_PROTECTION_UNDESIRED) {
+ hdcp->value = DRM_MODE_CONTENT_PROTECTION_ENABLED;
+ schedule_work(&hdcp->prop_work);
+ }
+ goto out;
+ }
+
+ DRM_DEBUG_KMS("[%s:%d] HDCP2.2 link failed, retrying auth\n",
+ connector->base.name, connector->base.base.id);
+
+ ret = _intel_hdcp2_disable(connector);
+ if (ret) {
+ DRM_ERROR("[%s:%d] Failed to disable hdcp2.2 (%d)\n",
+ connector->base.name, connector->base.base.id, ret);
+ hdcp->value = DRM_MODE_CONTENT_PROTECTION_DESIRED;
+ schedule_work(&hdcp->prop_work);
+ goto out;
+ }
+
+ ret = _intel_hdcp2_enable(connector);
+ if (ret) {
+ DRM_DEBUG_KMS("[%s:%d] Failed to enable hdcp2.2 (%d)\n",
+ connector->base.name, connector->base.base.id,
+ ret);
+ hdcp->value = DRM_MODE_CONTENT_PROTECTION_DESIRED;
+ schedule_work(&hdcp->prop_work);
+ goto out;
+ }
+
+ mutex_unlock(&hdcp->mutex);
+ return ret;
+}
+
+static void intel_hdcp2_check_work(struct work_struct *work)
+{
+ struct intel_hdcp *hdcp = container_of(to_delayed_work(work),
+ struct intel_hdcp,
+ hdcp2_check_work);
+ struct intel_connector *connector = intel_hdcp_to_connector(hdcp);
+
+ if (!intel_hdcp2_check_link(connector))
+ schedule_delayed_work(&hdcp->hdcp2_check_work,
+ DRM_HDCP2_CHECK_PERIOD_MS);
+}
+
static int i915_hdcp_component_master_bind(struct device *dev)
{
struct drm_i915_private *dev_priv = kdev_to_i915(dev);
@@ -1762,6 +1837,7 @@ static void intel_hdcp2_init(struct intel_connector *connector)
return;
}
+ INIT_DELAYED_WORK(&hdcp->hdcp2_check_work, intel_hdcp2_check_work);
hdcp->hdcp2_supported = 1;
}
@@ -1836,8 +1912,12 @@ int intel_hdcp_enable(struct intel_connector *connector)
* Considering that HDCP2.2 is more secure than HDCP1.4, If the setup
* is capable of HDCP2.2, it is preferred to use HDCP2.2.
*/
- if (intel_hdcp2_capable(connector))
+ if (intel_hdcp2_capable(connector)) {
ret = _intel_hdcp2_enable(connector);
+ if (!ret)
+ schedule_delayed_work(&hdcp->hdcp2_check_work,
+ DRM_HDCP2_CHECK_PERIOD_MS);
+ }
/* When HDCP2.2 fails, HDCP1.4 will be attempted */
if (ret && intel_hdcp_capable(connector)) {
@@ -1876,6 +1956,7 @@ int intel_hdcp_disable(struct intel_connector *connector)
mutex_unlock(&hdcp->mutex);
cancel_delayed_work_sync(&hdcp->check_work);
+ cancel_delayed_work_sync(&hdcp->hdcp2_check_work);
return ret;
}
diff --git a/include/drm/drm_hdcp.h b/include/drm/drm_hdcp.h
index 3e45a7618ab3..be07a2b56d23 100644
--- a/include/drm/drm_hdcp.h
+++ b/include/drm/drm_hdcp.h
@@ -11,6 +11,14 @@
/* Period of hdcp checks (to ensure we're still authenticated) */
#define DRM_HDCP_CHECK_PERIOD_MS (128 * 16)
+#define DRM_HDCP2_CHECK_PERIOD_MS 500
+
+enum check_link_response {
+ DRM_HDCP_LINK_PROTECTED = 0,
+ DRM_HDCP_TOPOLOGY_CHANGE,
+ DRM_HDCP_LINK_INTEGRITY_FAILURE,
+ DRM_HDCP_REAUTH_REQUEST
+};
The drm_hdcp.h changes need to be all split out into sepearate patches I
think. Just for highlighting of the new shared bits. Also applies to the
previous one.
Is this specific to drm_hdcp.h? In the previous version i had all header
changes in a single patch. But Seanpaul suggested for better understanding
of the changes, to introduce the changes at the patches where they are used.

Still should we go back on this?

--Ram
Post by Daniel Vetter
Otherwise looks all reasonable.
-Daniel
Post by Ramalingam C
/* Shared lengths/masks between HDMI/DVI/DisplayPort */
#define DRM_HDCP_AN_LEN 8
--
2.7.4
Daniel Vetter
2018-12-07 14:36:43 UTC
Permalink
Post by C, Ramalingam
Post by Daniel Vetter
Post by Ramalingam C
Implements the link integrity check once in 500mSec.
Once encryption is enabled, an ongoing Link Integrity Check is
performed by the HDCP Receiver to check that cipher synchronization
is maintained between the HDCP Transmitter and the HDCP Receiver.
On the detection of synchronization lost, the HDCP Receiver must assert
the corresponding bits of the RxStatus register. The Transmitter polls
the RxStatus register and it may initiate re-authentication.
Rebased.
No Changes.
enum check_link_response is used check the link status [Uma]
Rebased as part of patch reordering.
Required members of intel_hdcp is defined [Sean Paul]
hdcp2_check_link is cancelled at required places.
Rebased for the component i/f changes.
Errors due to the sinks are reported as DEBUG logs.
---
drivers/gpu/drm/i915/intel_display.c | 11 +++--
drivers/gpu/drm/i915/intel_drv.h | 5 +++
drivers/gpu/drm/i915/intel_hdcp.c | 83 +++++++++++++++++++++++++++++++++++-
include/drm/drm_hdcp.h | 8 ++++
4 files changed, 103 insertions(+), 4 deletions(-)
diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c
index e9f4e22b2a4e..fc63babce165 100644
--- a/drivers/gpu/drm/i915/intel_display.c
+++ b/drivers/gpu/drm/i915/intel_display.c
@@ -15833,15 +15833,20 @@ static void intel_hpd_poll_fini(struct drm_device *dev)
{
struct intel_connector *connector;
struct drm_connector_list_iter conn_iter;
+ struct intel_hdcp *hdcp;
/* Kill all the work that may have been queued by hpd. */
drm_connector_list_iter_begin(dev, &conn_iter);
for_each_intel_connector_iter(connector, &conn_iter) {
+ hdcp = &connector->hdcp;
+
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);
+ if (hdcp->shim) {
+ cancel_delayed_work_sync(&hdcp->check_work);
+ cancel_work_sync(&hdcp->prop_work);
+ if (hdcp->hdcp2_supported)
+ cancel_delayed_work_sync(&hdcp->hdcp2_check_work);
Locking of these workers is always tricky ... why can't we use the same
worker for both checking hdcp2 and hdcp1 link status?
Doable similar to how we are doing hdcp_enable. Needs the tracking of the spec in
use to pick proper timeout and functions.
Yeah we definitely want separate enable/disable functions for hdcp1 and
hdcp2. I was thinking of only sharing the content_protection status logic,
and the worker scheduling logic. That looks the same, and imo that's the
tricky part. I.e. separate the drm interfacing side more from the hdcp hw
flow.
Post by C, Ramalingam
I will look into it.
Post by Daniel Vetter
Of course need to use the right timeout and call the right functions, but
I think gives us less duplication in the complicated code.
Post by Ramalingam C
}
}
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 24d258488efe..e6e32bf52568 100644
--- a/drivers/gpu/drm/i915/intel_drv.h
+++ b/drivers/gpu/drm/i915/intel_drv.h
@@ -403,6 +403,9 @@ struct intel_hdcp_shim {
*/
int (*config_stream_type)(struct intel_digital_port *intel_dig_port,
void *buf, size_t size);
+
+ /* HDCP2.2 Link Integrity Check */
+ int (*check_2_2_link)(struct intel_digital_port *intel_dig_port);
};
struct intel_hdcp {
@@ -445,6 +448,8 @@ struct intel_hdcp {
* over re-Auth has to be triggered.
*/
u32 seq_num_m;
+
+ struct delayed_work hdcp2_check_work;
};
struct intel_connector {
diff --git a/drivers/gpu/drm/i915/intel_hdcp.c b/drivers/gpu/drm/i915/intel_hdcp.c
index 679f3c164582..98b112395a5a 100644
--- a/drivers/gpu/drm/i915/intel_hdcp.c
+++ b/drivers/gpu/drm/i915/intel_hdcp.c
@@ -1626,6 +1626,81 @@ static int _intel_hdcp2_disable(struct intel_connector *connector)
return ret;
}
+/* Implements the Link Integrity Check for HDCP2.2 */
+static int intel_hdcp2_check_link(struct intel_connector *connector)
+{
+ struct intel_digital_port *intel_dig_port = conn_to_dig_port(connector);
+ struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
+ struct intel_hdcp *hdcp = &connector->hdcp;
+ enum port port = connector->encoder->port;
+
+ int ret = 0;
+
+ if (!hdcp->shim)
+ return -ENOENT;
Why is this possible? Should we wrap it into at least a WARN_ON?
we could land here from CP_IRQ, with hdcp_exit called for shim removal.
But could avoid if we assert on the HDCP auth/encrypt status of the port
before check_link.
Post by Daniel Vetter
Post by Ramalingam C
+
+ mutex_lock(&hdcp->mutex);
+
+ if (hdcp->value == DRM_MODE_CONTENT_PROTECTION_UNDESIRED)
+ goto out;
+
+ if (!(I915_READ(HDCP2_STATUS_DDI(port)) & LINK_ENCRYPTION_STATUS)) {
+ DRM_ERROR("HDCP2.2 check failed: link is not encrypted, %x\n",
+ I915_READ(HDCP2_STATUS_DDI(port)));
+ ret = -ENXIO;
+ hdcp->value = DRM_MODE_CONTENT_PROTECTION_DESIRED;
+ schedule_work(&hdcp->prop_work);
+ goto out;
+ }
+
+ ret = hdcp->shim->check_2_2_link(intel_dig_port);
+ if (ret == DRM_HDCP_LINK_PROTECTED) {
+ if (hdcp->value != DRM_MODE_CONTENT_PROTECTION_UNDESIRED) {
+ hdcp->value = DRM_MODE_CONTENT_PROTECTION_ENABLED;
+ schedule_work(&hdcp->prop_work);
+ }
+ goto out;
+ }
+
+ DRM_DEBUG_KMS("[%s:%d] HDCP2.2 link failed, retrying auth\n",
+ connector->base.name, connector->base.base.id);
+
+ ret = _intel_hdcp2_disable(connector);
+ if (ret) {
+ DRM_ERROR("[%s:%d] Failed to disable hdcp2.2 (%d)\n",
+ connector->base.name, connector->base.base.id, ret);
+ hdcp->value = DRM_MODE_CONTENT_PROTECTION_DESIRED;
+ schedule_work(&hdcp->prop_work);
+ goto out;
+ }
+
+ ret = _intel_hdcp2_enable(connector);
+ if (ret) {
+ DRM_DEBUG_KMS("[%s:%d] Failed to enable hdcp2.2 (%d)\n",
+ connector->base.name, connector->base.base.id,
+ ret);
+ hdcp->value = DRM_MODE_CONTENT_PROTECTION_DESIRED;
+ schedule_work(&hdcp->prop_work);
+ goto out;
+ }
+
+ mutex_unlock(&hdcp->mutex);
+ return ret;
+}
+
+static void intel_hdcp2_check_work(struct work_struct *work)
+{
+ struct intel_hdcp *hdcp = container_of(to_delayed_work(work),
+ struct intel_hdcp,
+ hdcp2_check_work);
+ struct intel_connector *connector = intel_hdcp_to_connector(hdcp);
+
+ if (!intel_hdcp2_check_link(connector))
+ schedule_delayed_work(&hdcp->hdcp2_check_work,
+ DRM_HDCP2_CHECK_PERIOD_MS);
+}
+
static int i915_hdcp_component_master_bind(struct device *dev)
{
struct drm_i915_private *dev_priv = kdev_to_i915(dev);
@@ -1762,6 +1837,7 @@ static void intel_hdcp2_init(struct intel_connector *connector)
return;
}
+ INIT_DELAYED_WORK(&hdcp->hdcp2_check_work, intel_hdcp2_check_work);
hdcp->hdcp2_supported = 1;
}
@@ -1836,8 +1912,12 @@ int intel_hdcp_enable(struct intel_connector *connector)
* Considering that HDCP2.2 is more secure than HDCP1.4, If the setup
* is capable of HDCP2.2, it is preferred to use HDCP2.2.
*/
- if (intel_hdcp2_capable(connector))
+ if (intel_hdcp2_capable(connector)) {
ret = _intel_hdcp2_enable(connector);
+ if (!ret)
+ schedule_delayed_work(&hdcp->hdcp2_check_work,
+ DRM_HDCP2_CHECK_PERIOD_MS);
+ }
/* When HDCP2.2 fails, HDCP1.4 will be attempted */
if (ret && intel_hdcp_capable(connector)) {
@@ -1876,6 +1956,7 @@ int intel_hdcp_disable(struct intel_connector *connector)
mutex_unlock(&hdcp->mutex);
cancel_delayed_work_sync(&hdcp->check_work);
+ cancel_delayed_work_sync(&hdcp->hdcp2_check_work);
return ret;
}
diff --git a/include/drm/drm_hdcp.h b/include/drm/drm_hdcp.h
index 3e45a7618ab3..be07a2b56d23 100644
--- a/include/drm/drm_hdcp.h
+++ b/include/drm/drm_hdcp.h
@@ -11,6 +11,14 @@
/* Period of hdcp checks (to ensure we're still authenticated) */
#define DRM_HDCP_CHECK_PERIOD_MS (128 * 16)
+#define DRM_HDCP2_CHECK_PERIOD_MS 500
+
+enum check_link_response {
+ DRM_HDCP_LINK_PROTECTED = 0,
+ DRM_HDCP_TOPOLOGY_CHANGE,
+ DRM_HDCP_LINK_INTEGRITY_FAILURE,
+ DRM_HDCP_REAUTH_REQUEST
+};
The drm_hdcp.h changes need to be all split out into sepearate patches I
think. Just for highlighting of the new shared bits. Also applies to the
previous one.
Is this specific to drm_hdcp.h? In the previous version i had all header
changes in a single patch. But Seanpaul suggested for better understanding
of the changes, to introduce the changes at the patches where they are used.
Hm, conflicting review comments :-/
Post by C, Ramalingam
Still should we go back on this?
We need acks for all drm changes when we want to merge them through
drm-intel (which I think is the best tree for this series). So it helps to
split them out. You can still have them all just split out right before
the i915 patch that uses the new functions (instead of one overall
drm_hdcp.h patch, which I agree with Sean can be tricky to understand).

Separate patch is more to highlight to non-intel people that we're
touching core code here too.
-Daniel
Post by C, Ramalingam
--Ram
Post by Daniel Vetter
Otherwise looks all reasonable.
-Daniel
Post by Ramalingam C
/* Shared lengths/masks between HDMI/DVI/DisplayPort */
#define DRM_HDCP_AN_LEN 8
--
2.7.4
--
Daniel Vetter
Software Engineer, Intel Corporation
http://blog.ffwll.ch
Ramalingam C
2018-11-27 10:43:26 UTC
Permalink
Request to ME to verify the M_Prime received from the HDCP sink.

ME FW will calculate the M and compare with M_prime received
as part of RepeaterAuth_Stream_Ready, which is HDCP2.2 protocol msg.

On successful completion of this stage, downstream propagation of
the stream management info is completed.

v2:
Rebased.
v3:
cldev is passed as first parameter [Tomas]
Redundant comments and cast are removed [Tomas]
v4:
%zd for ssize_t [Alexander]
%s/return -1/return -EIO [Alexander]
endianness conversion func is moved to drm_hdcp.h [Uma]
v5:
Rebased.
v6:
No change.
v7:
Collected the Rb-ed by.
Rebasing.
v8:
Adjust to the new mei interface.
Fix for Kdoc.

Signed-off-by: Ramalingam C <***@intel.com>
Reviewed-by: Uma Shankar <***@intel.com>
---
drivers/misc/mei/hdcp/mei_hdcp.c | 69 +++++++++++++++++++++++++++++++++++++++-
1 file changed, 68 insertions(+), 1 deletion(-)

diff --git a/drivers/misc/mei/hdcp/mei_hdcp.c b/drivers/misc/mei/hdcp/mei_hdcp.c
index 6027b0ce39ed..5c7f1e822ed2 100644
--- a/drivers/misc/mei/hdcp/mei_hdcp.c
+++ b/drivers/misc/mei/hdcp/mei_hdcp.c
@@ -529,6 +529,73 @@ mei_repeater_check_flow_prepare_ack(struct device *dev, void *hdcp_data,
return 0;
}

+/*
+ * mei_verify_mprime : Function to verify mprime.
+ *
+ * @dev : device corresponding to the mei_cl_device
+ * @hdcp_data : Intel HW specific hdcp data
+ * @stream_ready : RepeaterAuth_Stream_Ready msg for mei verification.
+ *
+ * Returns 0 on Success, <0 on Failure
+ */
+static int mei_verify_mprime(struct device *dev, void *hdcp_data,
+ struct hdcp2_rep_stream_ready *stream_ready)
+{
+ struct wired_cmd_repeater_auth_stream_req_in
+ verify_mprime_in = { { 0 } };
+ struct wired_cmd_repeater_auth_stream_req_out
+ verify_mprime_out = { { 0 } };
+ struct mei_cl_device *cldev;
+ struct mei_hdcp_data *data = hdcp_data;
+ ssize_t byte;
+
+ if (!dev || !stream_ready || !data)
+ return -EINVAL;
+
+ cldev = to_mei_cl_device(dev);
+
+ verify_mprime_in.header.api_version = HDCP_API_VERSION;
+ verify_mprime_in.header.command_id = WIRED_REPEATER_AUTH_STREAM_REQ;
+ verify_mprime_in.header.status = ME_HDCP_STATUS_SUCCESS;
+ verify_mprime_in.header.buffer_len =
+ WIRED_CMD_BUF_LEN_REPEATER_AUTH_STREAM_REQ_MIN_IN;
+
+ verify_mprime_in.port.integrated_port_type = data->port_type;
+ verify_mprime_in.port.physical_port = data->port;
+
+ memcpy(verify_mprime_in.m_prime, stream_ready->m_prime,
+ HDCP_2_2_MPRIME_LEN);
+ reverse_endianness((u8 *)&verify_mprime_in.seq_num_m,
+ HDCP_2_2_SEQ_NUM_LEN, (u8 *)&data->seq_num_m);
+ memcpy(verify_mprime_in.streams, data->streams,
+ (data->k * sizeof(struct hdcp2_streamid_type)));
+
+ verify_mprime_in.k = __swab16(data->k);
+
+ byte = mei_cldev_send(cldev, (u8 *)&verify_mprime_in,
+ sizeof(verify_mprime_in));
+ if (byte < 0) {
+ dev_dbg(dev, "mei_cldev_send failed. %zd\n", byte);
+ return byte;
+ }
+
+ byte = mei_cldev_recv(cldev, (u8 *)&verify_mprime_out,
+ sizeof(verify_mprime_out));
+ if (byte < 0) {
+ dev_dbg(dev, "mei_cldev_recv failed. %zd\n", byte);
+ return byte;
+ }
+
+ if (verify_mprime_out.header.status != ME_HDCP_STATUS_SUCCESS) {
+ dev_dbg(dev, "ME cmd 0x%08X failed. status: 0x%X\n",
+ WIRED_REPEATER_AUTH_STREAM_REQ,
+ verify_mprime_out.header.status);
+ return -EIO;
+ }
+
+ return 0;
+}
+
static __attribute__((unused))
struct i915_hdcp_component_ops mei_hdcp_ops = {
.owner = THIS_MODULE,
@@ -540,7 +607,7 @@ struct i915_hdcp_component_ops mei_hdcp_ops = {
.verify_lprime = mei_verify_lprime,
.get_session_key = mei_get_session_key,
.repeater_check_flow_prepare_ack = mei_repeater_check_flow_prepare_ack,
- .verify_mprime = NULL,
+ .verify_mprime = mei_verify_mprime,
.enable_hdcp_authentication = NULL,
.close_hdcp_session = NULL,
};
--
2.7.4
Ramalingam C
2018-11-27 10:43:21 UTC
Permalink
Provides Pairing info to ME to store.

Pairing is a process to fast track the subsequent authentication
with the same HDCP sink.

On Success, received HDCP pairing info is stored in non-volatile
memory of ME.

v2:
Rebased.
v3:
cldev is passed as first parameter [Tomas]
Redundant comments and cast are removed [Tomas]
v4:
%zd for ssize_t [Alexander]
%s/return -1/return -EIO [Alexander]
Style fixed [Uma]
v5:
Rebased.
v6:
No change.
v7:
Collected the Rb-ed by.
Rebasing.
v8:
Adjust to the new mei interface.
Fix for Kdoc.

Signed-off-by: Ramalingam C <***@intel.com>
Reviewed-by: Uma Shankar <***@intel.com>
---
drivers/misc/mei/hdcp/mei_hdcp.c | 63 +++++++++++++++++++++++++++++++++++++++-
1 file changed, 62 insertions(+), 1 deletion(-)

diff --git a/drivers/misc/mei/hdcp/mei_hdcp.c b/drivers/misc/mei/hdcp/mei_hdcp.c
index f5e6db9c0026..27c94d883386 100644
--- a/drivers/misc/mei/hdcp/mei_hdcp.c
+++ b/drivers/misc/mei/hdcp/mei_hdcp.c
@@ -222,13 +222,74 @@ static int mei_verify_hprime(struct device *dev, void *hdcp_data,
return 0;
}

+/*
+ * mei_store_pairing_info :
+ * Function to store pairing info received from panel
+ *
+ * @dev : device corresponding to the mei_cl_device
+ * @hdcp_data : Intel HW specific hdcp data
+ * @pairing_info : AKE_Send_Pairing_Info msg to mei storage
+ *
+ * Returns 0 on Success, <0 on Failure
+ */
+static int
+mei_store_pairing_info(struct device *dev, void *hdcp_data,
+ struct hdcp2_ake_send_pairing_info *pairing_info)
+{
+ struct wired_cmd_ake_send_pairing_info_in pairing_info_in = { { 0 } };
+ struct wired_cmd_ake_send_pairing_info_out pairing_info_out = { { 0 } };
+ struct mei_cl_device *cldev;
+ struct mei_hdcp_data *data = hdcp_data;
+ ssize_t byte;
+
+ if (!dev || !data || !pairing_info)
+ return -EINVAL;
+
+ cldev = to_mei_cl_device(dev);
+
+ pairing_info_in.header.api_version = HDCP_API_VERSION;
+ pairing_info_in.header.command_id = WIRED_AKE_SEND_PAIRING_INFO;
+ pairing_info_in.header.status = ME_HDCP_STATUS_SUCCESS;
+ pairing_info_in.header.buffer_len =
+ WIRED_CMD_BUF_LEN_SEND_PAIRING_INFO_IN;
+
+ pairing_info_in.port.integrated_port_type = data->port_type;
+ pairing_info_in.port.physical_port = data->port;
+
+ memcpy(pairing_info_in.e_kh_km, pairing_info->e_kh_km,
+ sizeof(pairing_info_in.e_kh_km));
+
+ byte = mei_cldev_send(cldev, (u8 *)&pairing_info_in,
+ sizeof(pairing_info_in));
+ if (byte < 0) {
+ dev_dbg(dev, "mei_cldev_send failed. %zd\n", byte);
+ return byte;
+ }
+
+ byte = mei_cldev_recv(cldev, (u8 *)&pairing_info_out,
+ sizeof(pairing_info_out));
+ if (byte < 0) {
+ dev_dbg(dev, "mei_cldev_recv failed. %zd\n", byte);
+ return byte;
+ }
+
+ if (pairing_info_out.header.status != ME_HDCP_STATUS_SUCCESS) {
+ dev_dbg(dev, "ME cmd 0x%08X failed. Status: 0x%X\n",
+ WIRED_AKE_SEND_PAIRING_INFO,
+ pairing_info_out.header.status);
+ return -EIO;
+ }
+
+ return 0;
+}
+
static __attribute__((unused))
struct i915_hdcp_component_ops mei_hdcp_ops = {
.owner = THIS_MODULE,
.initiate_hdcp2_session = mei_initiate_hdcp2_session,
.verify_receiver_cert_prepare_km = mei_verify_receiver_cert_prepare_km,
.verify_hprime = mei_verify_hprime,
- .store_pairing_info = NULL,
+ .store_pairing_info = mei_store_pairing_info,
.initiate_locality_check = NULL,
.verify_lprime = NULL,
.get_session_key = NULL,
--
2.7.4
Ramalingam C
2018-11-27 10:43:28 UTC
Permalink
Request the ME to terminate the HDCP2.2 session for a port.

On Success, ME FW will mark the intel port as Deauthenticated and
terminate the wired HDCP2.2 Tx session started due to the cmd
WIRED_INITIATE_HDCP2_SESSION.

v2:
Rebased.
v3:
cldev is passed as first parameter [Tomas]
Redundant comments and cast are removed [Tomas]
v4:
%zd for ssize_t [Alexander]
%s/return -1/return -EIO [Alexander]
Style and typos fixed [Uma]
v5:
Extra line is removed.
v6:
No change.
v7:
Collected the Rb-ed by.
Rebased.
v8:
Adjust to the new mei interface.
Fix for Kdoc.

Signed-off-by: Ramalingam C <***@intel.com>
Reviewed-by: Uma Shankar <***@intel.com>
---
drivers/misc/mei/hdcp/mei_hdcp.c | 57 +++++++++++++++++++++++++++++++++++++++-
1 file changed, 56 insertions(+), 1 deletion(-)

diff --git a/drivers/misc/mei/hdcp/mei_hdcp.c b/drivers/misc/mei/hdcp/mei_hdcp.c
index 08f7310edd68..6e14e7cb49a5 100644
--- a/drivers/misc/mei/hdcp/mei_hdcp.c
+++ b/drivers/misc/mei/hdcp/mei_hdcp.c
@@ -650,6 +650,61 @@ static int mei_enable_hdcp_authentication(struct device *dev, void *hdcp_data)
return 0;
}

+/*
+ * mei_close_hdcp_session :
+ * Function to close the Wired HDCP Tx session of ME FW.
+ * This also disables the authenticated state of the port.
+ *
+ * @dev : device corresponding to the mei_cl_device
+ * @hdcp_data : Intel HW specific hdcp data
+ *
+ * Returns 0 on Success, <0 on Failure
+ */
+static int mei_close_hdcp_session(struct device *dev, void *hdcp_data)
+{
+ struct wired_cmd_close_session_in session_close_in = { { 0 } };
+ struct wired_cmd_close_session_out session_close_out = { { 0 } };
+ struct mei_cl_device *cldev;
+ struct mei_hdcp_data *data = hdcp_data;
+ ssize_t byte;
+
+ if (!dev || !data)
+ return -EINVAL;
+
+ cldev = to_mei_cl_device(dev);
+
+ session_close_in.header.api_version = HDCP_API_VERSION;
+ session_close_in.header.command_id = WIRED_CLOSE_SESSION;
+ session_close_in.header.status = ME_HDCP_STATUS_SUCCESS;
+ session_close_in.header.buffer_len =
+ WIRED_CMD_BUF_LEN_CLOSE_SESSION_IN;
+
+ session_close_in.port.integrated_port_type = data->port_type;
+ session_close_in.port.physical_port = data->port;
+
+ byte = mei_cldev_send(cldev, (u8 *)&session_close_in,
+ sizeof(session_close_in));
+ if (byte < 0) {
+ dev_dbg(dev, "mei_cldev_send failed. %zd\n", byte);
+ return byte;
+ }
+
+ byte = mei_cldev_recv(cldev, (u8 *)&session_close_out,
+ sizeof(session_close_out));
+ if (byte < 0) {
+ dev_dbg(dev, "mei_cldev_recv failed. %zd\n", byte);
+ return byte;
+ }
+
+ if (session_close_out.header.status != ME_HDCP_STATUS_SUCCESS) {
+ dev_dbg(dev, "Session Close Failed. status: 0x%X\n",
+ session_close_out.header.status);
+ return -EIO;
+ }
+
+ return 0;
+}
+
static __attribute__((unused))
struct i915_hdcp_component_ops mei_hdcp_ops = {
.owner = THIS_MODULE,
@@ -663,7 +718,7 @@ struct i915_hdcp_component_ops mei_hdcp_ops = {
.repeater_check_flow_prepare_ack = mei_repeater_check_flow_prepare_ack,
.verify_mprime = mei_verify_mprime,
.enable_hdcp_authentication = mei_enable_hdcp_authentication,
- .close_hdcp_session = NULL,
+ .close_hdcp_session = mei_close_hdcp_session,
};

static int mei_hdcp_probe(struct mei_cl_device *cldev,
--
2.7.4
Ramalingam C
2018-11-27 10:43:27 UTC
Permalink
Request to ME to configure a port as authenticated.

On Success, ME FW will mark the port as authenticated and provides
HDCP cipher with the encryption keys.

Enabling the Authentication can be requested once all stages of
HDCP2.2 authentication is completed by interacting with ME FW.

Only after this stage, driver can enable the HDCP encryption for
the port, through HW registers.

v2:
Rebased.
v3:
cldev is passed as first parameter [Tomas]
Redundant comments and cast are removed [Tomas]
v4:
%zd for ssize_t [Alexander]
%s/return -1/return -EIO [Alexander]
Style and typos fixed [Uma]
v5:
Rebased.
v6:
No change.
v7:
Collected the Rb-ed by.
Rebased.
v8:
Adjust to the new mei interface.
Fix for Kdoc.

Signed-off-by: Ramalingam C <***@intel.com>
Reviewed-by: Uma Shankar <***@intel.com>
---
drivers/misc/mei/hdcp/mei_hdcp.c | 56 +++++++++++++++++++++++++++++++++++++++-
1 file changed, 55 insertions(+), 1 deletion(-)

diff --git a/drivers/misc/mei/hdcp/mei_hdcp.c b/drivers/misc/mei/hdcp/mei_hdcp.c
index 5c7f1e822ed2..08f7310edd68 100644
--- a/drivers/misc/mei/hdcp/mei_hdcp.c
+++ b/drivers/misc/mei/hdcp/mei_hdcp.c
@@ -596,6 +596,60 @@ static int mei_verify_mprime(struct device *dev, void *hdcp_data,
return 0;
}

+/*
+ * mei_enable_hdcp_authentication :
+ * Function to request ME FW to mark a port as authenticated.
+ *
+ * @dev : device corresponding to the mei_cl_device
+ * @hdcp_data : Intel HW specific hdcp data
+ *
+ * Returns 0 on Success, <0 on Failure
+ */
+static int mei_enable_hdcp_authentication(struct device *dev, void *hdcp_data)
+{
+ struct wired_cmd_enable_auth_in enable_auth_in = { { 0 } };
+ struct wired_cmd_enable_auth_out enable_auth_out = { { 0 } };
+ struct mei_cl_device *cldev;
+ struct mei_hdcp_data *data = hdcp_data;
+ ssize_t byte;
+
+ if (!dev || !data)
+ return -EINVAL;
+
+ cldev = to_mei_cl_device(dev);
+
+ enable_auth_in.header.api_version = HDCP_API_VERSION;
+ enable_auth_in.header.command_id = WIRED_ENABLE_AUTH;
+ enable_auth_in.header.status = ME_HDCP_STATUS_SUCCESS;
+ enable_auth_in.header.buffer_len = WIRED_CMD_BUF_LEN_ENABLE_AUTH_IN;
+
+ enable_auth_in.port.integrated_port_type = data->port_type;
+ enable_auth_in.port.physical_port = data->port;
+ enable_auth_in.stream_type = data->streams[0].stream_type;
+
+ byte = mei_cldev_send(cldev, (u8 *)&enable_auth_in,
+ sizeof(enable_auth_in));
+ if (byte < 0) {
+ dev_dbg(dev, "mei_cldev_send failed. %zd\n", byte);
+ return byte;
+ }
+
+ byte = mei_cldev_recv(cldev, (u8 *)&enable_auth_out,
+ sizeof(enable_auth_out));
+ if (byte < 0) {
+ dev_dbg(dev, "mei_cldev_recv failed. %zd\n", byte);
+ return byte;
+ }
+
+ if (enable_auth_out.header.status != ME_HDCP_STATUS_SUCCESS) {
+ dev_dbg(dev, "ME cmd 0x%08X failed. status: 0x%X\n",
+ WIRED_ENABLE_AUTH, enable_auth_out.header.status);
+ return -EIO;
+ }
+
+ return 0;
+}
+
static __attribute__((unused))
struct i915_hdcp_component_ops mei_hdcp_ops = {
.owner = THIS_MODULE,
@@ -608,7 +662,7 @@ struct i915_hdcp_component_ops mei_hdcp_ops = {
.get_session_key = mei_get_session_key,
.repeater_check_flow_prepare_ack = mei_repeater_check_flow_prepare_ack,
.verify_mprime = mei_verify_mprime,
- .enable_hdcp_authentication = NULL,
+ .enable_hdcp_authentication = mei_enable_hdcp_authentication,
.close_hdcp_session = NULL,
};
--
2.7.4
Ramalingam C
2018-11-27 10:43:30 UTC
Permalink
Commits the content protection change of a connector,
without crtc modeset. This improves the user experience.

Originally proposed by Sean Paul at v3 of HDCP1.4 framework
https://patchwork.freedesktop.org/patch/191759/. For some
reason this was dropped, but needed for the proper functionality
of HDCP encryption.

Signed-off-by: Ramalingam C <***@intel.com>
Suggested-by: Sean Paul <***@chromium.org>
---
drivers/gpu/drm/i915/intel_ddi.c | 7 -------
drivers/gpu/drm/i915/intel_display.c | 10 ++++++++++
drivers/gpu/drm/i915/intel_drv.h | 5 +++++
drivers/gpu/drm/i915/intel_hdcp.c | 32 ++++++++++++++++++++++++++++----
4 files changed, 43 insertions(+), 11 deletions(-)

diff --git a/drivers/gpu/drm/i915/intel_ddi.c b/drivers/gpu/drm/i915/intel_ddi.c
index ad11540ac436..128cc558d75f 100644
--- a/drivers/gpu/drm/i915/intel_ddi.c
+++ b/drivers/gpu/drm/i915/intel_ddi.c
@@ -3469,11 +3469,6 @@ static void intel_enable_ddi(struct intel_encoder *encoder,
intel_enable_ddi_hdmi(encoder, crtc_state, conn_state);
else
intel_enable_ddi_dp(encoder, crtc_state, conn_state);
-
- /* Enable hdcp if it's desired */
- if (conn_state->content_protection ==
- DRM_MODE_CONTENT_PROTECTION_DESIRED)
- intel_hdcp_enable(to_intel_connector(conn_state->connector));
}

static void intel_disable_ddi_dp(struct intel_encoder *encoder,
@@ -3513,8 +3508,6 @@ static void intel_disable_ddi(struct intel_encoder *encoder,
const struct intel_crtc_state *old_crtc_state,
const struct drm_connector_state *old_conn_state)
{
- intel_hdcp_disable(to_intel_connector(old_conn_state->connector));
-
if (intel_crtc_has_type(old_crtc_state, INTEL_OUTPUT_HDMI))
intel_disable_ddi_hdmi(encoder, old_crtc_state, old_conn_state);
else
diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c
index fc63babce165..98aaf536146f 100644
--- a/drivers/gpu/drm/i915/intel_display.c
+++ b/drivers/gpu/drm/i915/intel_display.c
@@ -12854,6 +12854,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 intel_crtc_state *new_intel_crtc_state, *old_intel_crtc_state;
+ struct drm_connector_state *old_conn_state, *new_conn_state;
+ struct drm_connector *connector;
struct drm_crtc *crtc;
struct intel_crtc *intel_crtc;
u64 put_domains[I915_MAX_PIPES] = {};
@@ -12947,9 +12949,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:
diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h
index 2b7181b76475..68eae1e569e4 100644
--- a/drivers/gpu/drm/i915/intel_drv.h
+++ b/drivers/gpu/drm/i915/intel_drv.h
@@ -2084,6 +2084,11 @@ static inline void intel_backlight_device_unregister(struct intel_connector *con
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_init(struct intel_connector *connector,
const struct intel_hdcp_shim *hdcp_shim,
bool hdcp2_supported);
diff --git a/drivers/gpu/drm/i915/intel_hdcp.c b/drivers/gpu/drm/i915/intel_hdcp.c
index be475914c3cd..9f4056e156ec 100644
--- a/drivers/gpu/drm/i915/intel_hdcp.c
+++ b/drivers/gpu/drm/i915/intel_hdcp.c
@@ -2005,7 +2005,6 @@ void intel_hdcp_atomic_check(struct drm_connector *connector,
{
uint64_t old_cp = old_state->content_protection;
uint64_t new_cp = new_state->content_protection;
- struct drm_crtc_state *crtc_state;

if (!new_state->crtc) {
/*
@@ -2026,10 +2025,35 @@ void intel_hdcp_atomic_check(struct drm_connector *connector,
(old_cp == DRM_MODE_CONTENT_PROTECTION_DESIRED &&
new_cp == DRM_MODE_CONTENT_PROTECTION_ENABLED))
return;
+}
+
+void intel_hdcp_atomic_pre_commit(struct drm_connector *connector,
+ struct drm_connector_state *old_state,
+ struct drm_connector_state *new_state)
+{
+ u64 old_cp = old_state->content_protection;
+ u64 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_UNDESIRED) ||
+ (new_state->crtc &&
+ old_cp != DRM_MODE_CONTENT_PROTECTION_UNDESIRED &&
+ new_cp == DRM_MODE_CONTENT_PROTECTION_UNDESIRED))
+ intel_hdcp_disable(to_intel_connector(connector));
+}
+
+void intel_hdcp_atomic_commit(struct drm_connector *connector,
+ struct drm_connector_state *new_state)
+{
+ u64 new_cp = new_state->content_protection;

- crtc_state = drm_atomic_get_new_crtc_state(new_state->state,
- new_state->crtc);
- crtc_state->mode_changed = true;
+ /* Enable hdcp if it's desired */
+ if (new_state->crtc && new_cp == DRM_MODE_CONTENT_PROTECTION_DESIRED)
+ intel_hdcp_enable(to_intel_connector(connector));
}

/* Handles the CP_IRQ raised from the DP HDCP sink */
--
2.7.4
Daniel Vetter
2018-12-06 14:19:54 UTC
Permalink
Post by Ramalingam C
Commits the content protection change of a connector,
without crtc modeset. This improves the user experience.
Originally proposed by Sean Paul at v3 of HDCP1.4 framework
https://patchwork.freedesktop.org/patch/191759/. For some
reason this was dropped, but needed for the proper functionality
of HDCP encryption.
I think best to split this out from this series, since it's a separate
concern really.

Wrt implementation, this problem here is pretty similar to other sink
setup stuff like infoframes, where atm we also require a full modeset. The
idea that iirc Ville&me toyed around with is a kind of
per-encoder/connector fixup function, where all this stuff could be set if
we did _not_ do a full modeset. For a full modeset I think it's best to
still keep all this in connectors.

Plus for anything where we do this kind of fast-modeset trick we need to
lock down the state tracking using the state readout code. Otherwise this
is very hard to validate properly.
-Daniel
Post by Ramalingam C
---
drivers/gpu/drm/i915/intel_ddi.c | 7 -------
drivers/gpu/drm/i915/intel_display.c | 10 ++++++++++
drivers/gpu/drm/i915/intel_drv.h | 5 +++++
drivers/gpu/drm/i915/intel_hdcp.c | 32 ++++++++++++++++++++++++++++----
4 files changed, 43 insertions(+), 11 deletions(-)
diff --git a/drivers/gpu/drm/i915/intel_ddi.c b/drivers/gpu/drm/i915/intel_ddi.c
index ad11540ac436..128cc558d75f 100644
--- a/drivers/gpu/drm/i915/intel_ddi.c
+++ b/drivers/gpu/drm/i915/intel_ddi.c
@@ -3469,11 +3469,6 @@ static void intel_enable_ddi(struct intel_encoder *encoder,
intel_enable_ddi_hdmi(encoder, crtc_state, conn_state);
else
intel_enable_ddi_dp(encoder, crtc_state, conn_state);
-
- /* Enable hdcp if it's desired */
- if (conn_state->content_protection ==
- DRM_MODE_CONTENT_PROTECTION_DESIRED)
- intel_hdcp_enable(to_intel_connector(conn_state->connector));
}
static void intel_disable_ddi_dp(struct intel_encoder *encoder,
@@ -3513,8 +3508,6 @@ static void intel_disable_ddi(struct intel_encoder *encoder,
const struct intel_crtc_state *old_crtc_state,
const struct drm_connector_state *old_conn_state)
{
- intel_hdcp_disable(to_intel_connector(old_conn_state->connector));
-
if (intel_crtc_has_type(old_crtc_state, INTEL_OUTPUT_HDMI))
intel_disable_ddi_hdmi(encoder, old_crtc_state, old_conn_state);
else
diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c
index fc63babce165..98aaf536146f 100644
--- a/drivers/gpu/drm/i915/intel_display.c
+++ b/drivers/gpu/drm/i915/intel_display.c
@@ -12854,6 +12854,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 intel_crtc_state *new_intel_crtc_state, *old_intel_crtc_state;
+ struct drm_connector_state *old_conn_state, *new_conn_state;
+ struct drm_connector *connector;
struct drm_crtc *crtc;
struct intel_crtc *intel_crtc;
u64 put_domains[I915_MAX_PIPES] = {};
@@ -12947,9 +12949,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
diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h
index 2b7181b76475..68eae1e569e4 100644
--- a/drivers/gpu/drm/i915/intel_drv.h
+++ b/drivers/gpu/drm/i915/intel_drv.h
@@ -2084,6 +2084,11 @@ static inline void intel_backlight_device_unregister(struct intel_connector *con
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_init(struct intel_connector *connector,
const struct intel_hdcp_shim *hdcp_shim,
bool hdcp2_supported);
diff --git a/drivers/gpu/drm/i915/intel_hdcp.c b/drivers/gpu/drm/i915/intel_hdcp.c
index be475914c3cd..9f4056e156ec 100644
--- a/drivers/gpu/drm/i915/intel_hdcp.c
+++ b/drivers/gpu/drm/i915/intel_hdcp.c
@@ -2005,7 +2005,6 @@ void intel_hdcp_atomic_check(struct drm_connector *connector,
{
uint64_t old_cp = old_state->content_protection;
uint64_t new_cp = new_state->content_protection;
- struct drm_crtc_state *crtc_state;
if (!new_state->crtc) {
/*
@@ -2026,10 +2025,35 @@ void intel_hdcp_atomic_check(struct drm_connector *connector,
(old_cp == DRM_MODE_CONTENT_PROTECTION_DESIRED &&
new_cp == DRM_MODE_CONTENT_PROTECTION_ENABLED))
return;
+}
+
+void intel_hdcp_atomic_pre_commit(struct drm_connector *connector,
+ struct drm_connector_state *old_state,
+ struct drm_connector_state *new_state)
+{
+ u64 old_cp = old_state->content_protection;
+ u64 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_UNDESIRED) ||
+ (new_state->crtc &&
+ old_cp != DRM_MODE_CONTENT_PROTECTION_UNDESIRED &&
+ new_cp == DRM_MODE_CONTENT_PROTECTION_UNDESIRED))
+ intel_hdcp_disable(to_intel_connector(connector));
+}
+
+void intel_hdcp_atomic_commit(struct drm_connector *connector,
+ struct drm_connector_state *new_state)
+{
+ u64 new_cp = new_state->content_protection;
- crtc_state = drm_atomic_get_new_crtc_state(new_state->state,
- new_state->crtc);
- crtc_state->mode_changed = true;
+ /* Enable hdcp if it's desired */
+ if (new_state->crtc && new_cp == DRM_MODE_CONTENT_PROTECTION_DESIRED)
+ intel_hdcp_enable(to_intel_connector(connector));
}
/* Handles the CP_IRQ raised from the DP HDCP sink */
--
2.7.4
--
Daniel Vetter
Software Engineer, Intel Corporation
http://blog.ffwll.ch
Ramalingam C
2018-11-27 10:43:24 UTC
Permalink
Request to ME to prepare the encrypted session key.

On Success, ME provides Encrypted session key. Function populates
the HDCP2.2 authentication msg SKE_Send_Eks.

v2:
Rebased.
v3:
cldev is passed as first parameter [Tomas]
Redundant comments and cast are removed [Tomas]
v4:
%zd for ssize_t [Alexander]
%s/return -1/return -EIO [Alexander]
Style fixed [Uma]
v5:
Rebased.
v6:
No change.
v7:
Collected the Rb-ed by.
Rebasing.
v8:
Adjust to the new mei interface.
Fix for Kdoc.

Signed-off-by: Ramalingam C <***@intel.com>
Reviewed-by: Uma Shankar <***@intel.com>
---
drivers/misc/mei/hdcp/mei_hdcp.c | 60 +++++++++++++++++++++++++++++++++++++++-
1 file changed, 59 insertions(+), 1 deletion(-)

diff --git a/drivers/misc/mei/hdcp/mei_hdcp.c b/drivers/misc/mei/hdcp/mei_hdcp.c
index 18387669ff15..1730c45690aa 100644
--- a/drivers/misc/mei/hdcp/mei_hdcp.c
+++ b/drivers/misc/mei/hdcp/mei_hdcp.c
@@ -397,6 +397,64 @@ static int mei_verify_lprime(struct device *dev, void *hdcp_data,
return 0;
}

+/*
+ * mei_get_session_key : Function to prepare SKE_Send_Eks.
+ *
+ * @dev : device corresponding to the mei_cl_device
+ * @hdcp_data : Intel HW specific hdcp data
+ * @ske_data : SKE_Send_Eks msg from mei.
+ *
+ * Returns 0 on Success, <0 on Failure
+ */
+static int mei_get_session_key(struct device *dev, void *hdcp_data,
+ struct hdcp2_ske_send_eks *ske_data)
+{
+ struct wired_cmd_get_session_key_in get_skey_in = { { 0 } };
+ struct wired_cmd_get_session_key_out get_skey_out = { { 0 } };
+ struct mei_cl_device *cldev;
+ struct mei_hdcp_data *data = hdcp_data;
+ ssize_t byte;
+
+ if (!dev || !data || !ske_data)
+ return -EINVAL;
+
+ cldev = to_mei_cl_device(dev);
+
+ get_skey_in.header.api_version = HDCP_API_VERSION;
+ get_skey_in.header.command_id = WIRED_GET_SESSION_KEY;
+ get_skey_in.header.status = ME_HDCP_STATUS_SUCCESS;
+ get_skey_in.header.buffer_len = WIRED_CMD_BUF_LEN_GET_SESSION_KEY_IN;
+
+ get_skey_in.port.integrated_port_type = data->port_type;
+ get_skey_in.port.physical_port = data->port;
+
+ byte = mei_cldev_send(cldev, (u8 *)&get_skey_in, sizeof(get_skey_in));
+ if (byte < 0) {
+ dev_dbg(dev, "mei_cldev_send failed. %zd\n", byte);
+ return byte;
+ }
+
+ byte = mei_cldev_recv(cldev, (u8 *)&get_skey_out, sizeof(get_skey_out));
+
+ if (byte < 0) {
+ dev_dbg(dev, "mei_cldev_recv failed. %zd\n", byte);
+ return byte;
+ }
+
+ if (get_skey_out.header.status != ME_HDCP_STATUS_SUCCESS) {
+ dev_dbg(dev, "ME cmd 0x%08X failed. status: 0x%X\n",
+ WIRED_GET_SESSION_KEY, get_skey_out.header.status);
+ return -EIO;
+ }
+
+ ske_data->msg_id = HDCP_2_2_SKE_SEND_EKS;
+ memcpy(ske_data->e_dkey_ks, get_skey_out.e_dkey_ks,
+ HDCP_2_2_E_DKEY_KS_LEN);
+ memcpy(ske_data->riv, get_skey_out.r_iv, HDCP_2_2_RIV_LEN);
+
+ return 0;
+}
+
static __attribute__((unused))
struct i915_hdcp_component_ops mei_hdcp_ops = {
.owner = THIS_MODULE,
@@ -406,7 +464,7 @@ struct i915_hdcp_component_ops mei_hdcp_ops = {
.store_pairing_info = mei_store_pairing_info,
.initiate_locality_check = mei_initiate_locality_check,
.verify_lprime = mei_verify_lprime,
- .get_session_key = NULL,
+ .get_session_key = mei_get_session_key,
.repeater_check_flow_prepare_ack = NULL,
.verify_mprime = NULL,
.enable_hdcp_authentication = NULL,
--
2.7.4
Ramalingam C
2018-11-27 10:43:25 UTC
Permalink
Request ME to verify the downstream topology information received.

ME FW will validate the Repeaters receiver id list and
downstream topology.

On Success ME FW will provide the Least Significant
128bits of VPrime, which forms the repeater ack.

v2:
Rebased.
v3:
cldev is passed as first parameter [Tomas]
Redundant comments and cast are removed [Tomas]
v4:
%zd for ssize_t [Alexander]
%s/return -1/return -EIO [Alexander]
Style and typos fixed [Uma]
v5:
Rebased.
v6:
No change.
v7:
Rebasing.
v8:
Adjust to the new mei interface.
Fix for Kdoc.

Signed-off-by: Ramalingam C <***@intel.com>
Reviewed-by: Uma Shankar <***@intel.com>
---
drivers/misc/mei/hdcp/mei_hdcp.c | 76 +++++++++++++++++++++++++++++++++++++++-
1 file changed, 75 insertions(+), 1 deletion(-)

diff --git a/drivers/misc/mei/hdcp/mei_hdcp.c b/drivers/misc/mei/hdcp/mei_hdcp.c
index 1730c45690aa..6027b0ce39ed 100644
--- a/drivers/misc/mei/hdcp/mei_hdcp.c
+++ b/drivers/misc/mei/hdcp/mei_hdcp.c
@@ -455,6 +455,80 @@ static int mei_get_session_key(struct device *dev, void *hdcp_data,
return 0;
}

+/*
+ * mei_repeater_check_flow_prepare_ack :
+ * Function to validate the Downstream topology and prepare rep_ack.
+ *
+ * @dev : device corresponding to the mei_cl_device
+ * @hdcp_data : Intel HW specific hdcp data
+ * @rep_topology : Receiver ID List to be validated
+ * @rep_send_ack : repeater ack from mei
+ *
+ * Returns 0 on Success, <0 on Failure
+ */
+static int
+mei_repeater_check_flow_prepare_ack(struct device *dev, void *hdcp_data,
+ struct hdcp2_rep_send_receiverid_list
+ *rep_topology,
+ struct hdcp2_rep_send_ack *rep_send_ack)
+{
+ struct wired_cmd_verify_repeater_in verify_repeater_in = { { 0 } };
+ struct wired_cmd_verify_repeater_out verify_repeater_out = { { 0 } };
+ struct mei_cl_device *cldev;
+ struct mei_hdcp_data *data = hdcp_data;
+ ssize_t byte;
+
+ if (!dev || !rep_topology || !rep_send_ack || !data)
+ return -EINVAL;
+
+ cldev = to_mei_cl_device(dev);
+
+ verify_repeater_in.header.api_version = HDCP_API_VERSION;
+ verify_repeater_in.header.command_id = WIRED_VERIFY_REPEATER;
+ verify_repeater_in.header.status = ME_HDCP_STATUS_SUCCESS;
+ verify_repeater_in.header.buffer_len =
+ WIRED_CMD_BUF_LEN_VERIFY_REPEATER_IN;
+
+ verify_repeater_in.port.integrated_port_type = data->port_type;
+ verify_repeater_in.port.physical_port = data->port;
+
+ memcpy(verify_repeater_in.rx_info, rep_topology->rx_info,
+ HDCP_2_2_RXINFO_LEN);
+ memcpy(verify_repeater_in.seq_num_v, rep_topology->seq_num_v,
+ HDCP_2_2_SEQ_NUM_LEN);
+ memcpy(verify_repeater_in.v_prime, rep_topology->v_prime,
+ HDCP_2_2_V_PRIME_HALF_LEN);
+ memcpy(verify_repeater_in.receiver_ids, rep_topology->receiver_ids,
+ HDCP_2_2_RECEIVER_IDS_MAX_LEN);
+
+ byte = mei_cldev_send(cldev, (u8 *)&verify_repeater_in,
+ sizeof(verify_repeater_in));
+ if (byte < 0) {
+ dev_dbg(dev, "mei_cldev_send failed. %zd\n", byte);
+ return byte;
+ }
+
+ byte = mei_cldev_recv(cldev, (u8 *)&verify_repeater_out,
+ sizeof(verify_repeater_out));
+ if (byte < 0) {
+ dev_dbg(dev, "mei_cldev_recv failed. %zd\n", byte);
+ return byte;
+ }
+
+ if (verify_repeater_out.header.status != ME_HDCP_STATUS_SUCCESS) {
+ dev_dbg(dev, "ME cmd 0x%08X failed. status: 0x%X\n",
+ WIRED_VERIFY_REPEATER,
+ verify_repeater_out.header.status);
+ return -EIO;
+ }
+
+ memcpy(rep_send_ack->v, verify_repeater_out.v,
+ HDCP_2_2_V_PRIME_HALF_LEN);
+ rep_send_ack->msg_id = HDCP_2_2_REP_SEND_ACK;
+
+ return 0;
+}
+
static __attribute__((unused))
struct i915_hdcp_component_ops mei_hdcp_ops = {
.owner = THIS_MODULE,
@@ -465,7 +539,7 @@ struct i915_hdcp_component_ops mei_hdcp_ops = {
.initiate_locality_check = mei_initiate_locality_check,
.verify_lprime = mei_verify_lprime,
.get_session_key = mei_get_session_key,
- .repeater_check_flow_prepare_ack = NULL,
+ .repeater_check_flow_prepare_ack = mei_repeater_check_flow_prepare_ack,
.verify_mprime = NULL,
.enable_hdcp_authentication = NULL,
.close_hdcp_session = NULL,
--
2.7.4
Ramalingam C
2018-11-27 10:43:22 UTC
Permalink
Requests ME to start the second stage of HDCP2.2 authentication,
called Locality Check.

On Success, ME FW will provide LC_Init message to send to hdcp sink.

v2:
Rebased.
v3:
cldev is passed as first parameter [Tomas]
Redundant comments and cast are removed [Tomas]
v4:
%zd used for ssize_t [Alexander]
%s/return -1/return -EIO [Alexander]
Style fixed [Uma]
v5:
Rebased.
v6:
No change.
v7:
Collected the Rb-ed by.
Rebasing.
v8:
Adjust to the new mei interface.
Fix for Kdoc.

Signed-off-by: Ramalingam C <***@intel.com>
Reviewed-by: Uma Shankar <***@intel.com>
---
drivers/misc/mei/hdcp/mei_hdcp.c | 57 +++++++++++++++++++++++++++++++++++++++-
1 file changed, 56 insertions(+), 1 deletion(-)

diff --git a/drivers/misc/mei/hdcp/mei_hdcp.c b/drivers/misc/mei/hdcp/mei_hdcp.c
index 27c94d883386..6db98a51fce1 100644
--- a/drivers/misc/mei/hdcp/mei_hdcp.c
+++ b/drivers/misc/mei/hdcp/mei_hdcp.c
@@ -283,6 +283,61 @@ mei_store_pairing_info(struct device *dev, void *hdcp_data,
return 0;
}

+/*
+ * mei_initiate_locality_check : Function to prepare LC_Init.
+ *
+ * @dev : device corresponding to the mei_cl_device
+ * @hdcp_data : Intel HW specific hdcp data
+ * @hdcp2_lc_init : LC_Init msg output
+ *
+ * Returns 0 on Success, <0 on Failure
+ */
+static int mei_initiate_locality_check(struct device *dev, void *hdcp_data,
+ struct hdcp2_lc_init *lc_init_data)
+{
+ struct wired_cmd_init_locality_check_in lc_init_in = { { 0 } };
+ struct wired_cmd_init_locality_check_out lc_init_out = { { 0 } };
+ struct mei_cl_device *cldev;
+ struct mei_hdcp_data *data = hdcp_data;
+ ssize_t byte;
+
+ if (!dev || !data || !lc_init_data)
+ return -EINVAL;
+
+ cldev = to_mei_cl_device(dev);
+
+ lc_init_in.header.api_version = HDCP_API_VERSION;
+ lc_init_in.header.command_id = WIRED_INIT_LOCALITY_CHECK;
+ lc_init_in.header.status = ME_HDCP_STATUS_SUCCESS;
+ lc_init_in.header.buffer_len = WIRED_CMD_BUF_LEN_INIT_LOCALITY_CHECK_IN;
+
+ lc_init_in.port.integrated_port_type = data->port_type;
+ lc_init_in.port.physical_port = data->port;
+
+ byte = mei_cldev_send(cldev, (u8 *)&lc_init_in, sizeof(lc_init_in));
+ if (byte < 0) {
+ dev_dbg(dev, "mei_cldev_send failed. %zd\n", byte);
+ return byte;
+ }
+
+ byte = mei_cldev_recv(cldev, (u8 *)&lc_init_out, sizeof(lc_init_out));
+ if (byte < 0) {
+ dev_dbg(dev, "mei_cldev_recv failed. %zd\n", byte);
+ return byte;
+ }
+
+ if (lc_init_out.header.status != ME_HDCP_STATUS_SUCCESS) {
+ dev_dbg(dev, "ME cmd 0x%08X Failed. status: 0x%X\n",
+ WIRED_INIT_LOCALITY_CHECK, lc_init_out.header.status);
+ return -EIO;
+ }
+
+ lc_init_data->msg_id = HDCP_2_2_LC_INIT;
+ memcpy(lc_init_data->r_n, lc_init_out.r_n, HDCP_2_2_RN_LEN);
+
+ return 0;
+}
+
static __attribute__((unused))
struct i915_hdcp_component_ops mei_hdcp_ops = {
.owner = THIS_MODULE,
@@ -290,7 +345,7 @@ struct i915_hdcp_component_ops mei_hdcp_ops = {
.verify_receiver_cert_prepare_km = mei_verify_receiver_cert_prepare_km,
.verify_hprime = mei_verify_hprime,
.store_pairing_info = mei_store_pairing_info,
- .initiate_locality_check = NULL,
+ .initiate_locality_check = mei_initiate_locality_check,
.verify_lprime = NULL,
.get_session_key = NULL,
.repeater_check_flow_prepare_ack = NULL,
--
2.7.4
Ramalingam C
2018-11-27 10:43:31 UTC
Permalink
Implement the required WA sequence for KBL to fix the
incorrect positioning of the window of oppurtunity and enc_en
signalling.

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

diff --git a/drivers/gpu/drm/i915/intel_hdcp.c b/drivers/gpu/drm/i915/intel_hdcp.c
index 9f4056e156ec..654a076c58e4 100644
--- a/drivers/gpu/drm/i915/intel_hdcp.c
+++ b/drivers/gpu/drm/i915/intel_hdcp.c
@@ -12,6 +12,7 @@
#include <linux/i2c.h>
#include <linux/random.h>
#include <linux/component.h>
+#include <linux/delay.h>

#include "intel_drv.h"
#include "i915_reg.h"
@@ -24,6 +25,27 @@
__p == PORT_A ? MEI_DDI_A : (enum mei_hdcp_ddi)__p;\
})

+static void kbl_repositioning_enc_en_signal(struct intel_connector *connector)
+{
+ struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
+ struct intel_digital_port *intel_dig_port = conn_to_dig_port(connector);
+ struct intel_hdcp *hdcp = &connector->hdcp;
+ struct drm_crtc *crtc = connector->base.state->crtc;
+ struct intel_crtc *intel_crtc = container_of(crtc,
+ struct intel_crtc, base);
+ u32 scanline;
+
+ for (;;) {
+ scanline = I915_READ(PIPEDSL(intel_crtc->pipe));
+ if (scanline > 100 && scanline < 200)
+ break;
+ usleep_range(25, 50);
+ }
+
+ hdcp->shim->toggle_signalling(intel_dig_port, false);
+ hdcp->shim->toggle_signalling(intel_dig_port, true);
+}
+
static
bool intel_hdcp_is_ksv_valid(u8 *ksv)
{
@@ -1527,6 +1549,13 @@ static int hdcp2_enable_encryption(struct intel_connector *connector)
}

if (I915_READ(HDCP2_STATUS_DDI(port)) & LINK_AUTH_STATUS) {
+ /*
+ * WA: To fix incorrect positioning of the window of
+ * opportunity and enc_en signalling in KABYLAKE.
+ */
+ if (IS_KABYLAKE(dev_priv) && hdcp->shim->toggle_signalling)
+ kbl_repositioning_enc_en_signal(connector);
+
/* Link is Authenticated. Now set for Encryption */
I915_WRITE(HDCP2_CTL_DDI(port),
I915_READ(HDCP2_CTL_DDI(port)) |
--
2.7.4
Daniel Vetter
2018-12-06 14:20:32 UTC
Permalink
Post by Ramalingam C
Implement the required WA sequence for KBL to fix the
incorrect positioning of the window of oppurtunity and enc_en
signalling.
---
drivers/gpu/drm/i915/intel_hdcp.c | 29 +++++++++++++++++++++++++++++
1 file changed, 29 insertions(+)
diff --git a/drivers/gpu/drm/i915/intel_hdcp.c b/drivers/gpu/drm/i915/intel_hdcp.c
index 9f4056e156ec..654a076c58e4 100644
--- a/drivers/gpu/drm/i915/intel_hdcp.c
+++ b/drivers/gpu/drm/i915/intel_hdcp.c
@@ -12,6 +12,7 @@
#include <linux/i2c.h>
#include <linux/random.h>
#include <linux/component.h>
+#include <linux/delay.h>
#include "intel_drv.h"
#include "i915_reg.h"
@@ -24,6 +25,27 @@
__p == PORT_A ? MEI_DDI_A : (enum mei_hdcp_ddi)__p;\
})
+static void kbl_repositioning_enc_en_signal(struct intel_connector *connector)
Is there a Bspec WA number/label so I can more easily find this?
-Daniel
Post by Ramalingam C
+{
+ struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
+ struct intel_digital_port *intel_dig_port = conn_to_dig_port(connector);
+ struct intel_hdcp *hdcp = &connector->hdcp;
+ struct drm_crtc *crtc = connector->base.state->crtc;
+ struct intel_crtc *intel_crtc = container_of(crtc,
+ struct intel_crtc, base);
+ u32 scanline;
+
+ for (;;) {
+ scanline = I915_READ(PIPEDSL(intel_crtc->pipe));
+ if (scanline > 100 && scanline < 200)
+ break;
+ usleep_range(25, 50);
+ }
+
+ hdcp->shim->toggle_signalling(intel_dig_port, false);
+ hdcp->shim->toggle_signalling(intel_dig_port, true);
+}
+
static
bool intel_hdcp_is_ksv_valid(u8 *ksv)
{
@@ -1527,6 +1549,13 @@ static int hdcp2_enable_encryption(struct intel_connector *connector)
}
if (I915_READ(HDCP2_STATUS_DDI(port)) & LINK_AUTH_STATUS) {
+ /*
+ * WA: To fix incorrect positioning of the window of
+ * opportunity and enc_en signalling in KABYLAKE.
+ */
+ if (IS_KABYLAKE(dev_priv) && hdcp->shim->toggle_signalling)
+ kbl_repositioning_enc_en_signal(connector);
+
/* Link is Authenticated. Now set for Encryption */
I915_WRITE(HDCP2_CTL_DDI(port),
I915_READ(HDCP2_CTL_DDI(port)) |
--
2.7.4
--
Daniel Vetter
Software Engineer, Intel Corporation
http://blog.ffwll.ch
C, Ramalingam
2018-12-07 07:03:04 UTC
Permalink
Post by Daniel Vetter
Post by Ramalingam C
Implement the required WA sequence for KBL to fix the
incorrect positioning of the window of oppurtunity and enc_en
signalling.
---
drivers/gpu/drm/i915/intel_hdcp.c | 29 +++++++++++++++++++++++++++++
1 file changed, 29 insertions(+)
diff --git a/drivers/gpu/drm/i915/intel_hdcp.c b/drivers/gpu/drm/i915/intel_hdcp.c
index 9f4056e156ec..654a076c58e4 100644
--- a/drivers/gpu/drm/i915/intel_hdcp.c
+++ b/drivers/gpu/drm/i915/intel_hdcp.c
@@ -12,6 +12,7 @@
#include <linux/i2c.h>
#include <linux/random.h>
#include <linux/component.h>
+#include <linux/delay.h>
#include "intel_drv.h"
#include "i915_reg.h"
@@ -24,6 +25,27 @@
__p == PORT_A ? MEI_DDI_A : (enum mei_hdcp_ddi)__p;\
})
+static void kbl_repositioning_enc_en_signal(struct intel_connector *connector)
Is there a Bspec WA number/label so I can more easily find this?
Referenced in WAs #0889 and #0890

--Ram
Post by Daniel Vetter
-Daniel
Post by Ramalingam C
+{
+ struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
+ struct intel_digital_port *intel_dig_port = conn_to_dig_port(connector);
+ struct intel_hdcp *hdcp = &connector->hdcp;
+ struct drm_crtc *crtc = connector->base.state->crtc;
+ struct intel_crtc *intel_crtc = container_of(crtc,
+ struct intel_crtc, base);
+ u32 scanline;
+
+ for (;;) {
+ scanline = I915_READ(PIPEDSL(intel_crtc->pipe));
+ if (scanline > 100 && scanline < 200)
+ break;
+ usleep_range(25, 50);
+ }
+
+ hdcp->shim->toggle_signalling(intel_dig_port, false);
+ hdcp->shim->toggle_signalling(intel_dig_port, true);
+}
+
static
bool intel_hdcp_is_ksv_valid(u8 *ksv)
{
@@ -1527,6 +1549,13 @@ static int hdcp2_enable_encryption(struct intel_connector *connector)
}
if (I915_READ(HDCP2_STATUS_DDI(port)) & LINK_AUTH_STATUS) {
+ /*
+ * WA: To fix incorrect positioning of the window of
+ * opportunity and enc_en signalling in KABYLAKE.
+ */
+ if (IS_KABYLAKE(dev_priv) && hdcp->shim->toggle_signalling)
+ kbl_repositioning_enc_en_signal(connector);
+
/* Link is Authenticated. Now set for Encryption */
I915_WRITE(HDCP2_CTL_DDI(port),
I915_READ(HDCP2_CTL_DDI(port)) |
--
2.7.4
Ramalingam C
2018-11-27 10:43:29 UTC
Permalink
Mei hdcp driver is designed as component slave for the I915 component
master.

v2:
Rebased.
v3:
Notifier chain is adopted for cldev state update [Tomas]
v4:
Made static dummy functions as inline in mei_hdcp.h
API for polling client device status
IS_ENABLED used in header, for config status for mei_hdcp.
v5:
Replacing the notifier with component framework. [Daniel]
v6:
Rebased on the I915 comp master redesign.
v7:
mei_hdcp_component_registered is made static [Uma]
Need for global static variable mei_cldev is removed.
v8:
Adjust to the new mei-i915 interface.

Signed-off-by: Ramalingam C <***@intel.com>
Reviewed-by: Uma Shankar <***@intel.com>
---
drivers/misc/mei/hdcp/mei_hdcp.c | 71 +++++++++++++++++++++++++++++++++++++---
1 file changed, 67 insertions(+), 4 deletions(-)

diff --git a/drivers/misc/mei/hdcp/mei_hdcp.c b/drivers/misc/mei/hdcp/mei_hdcp.c
index 6e14e7cb49a5..1941b9783011 100644
--- a/drivers/misc/mei/hdcp/mei_hdcp.c
+++ b/drivers/misc/mei/hdcp/mei_hdcp.c
@@ -13,11 +13,14 @@
#include <linux/uuid.h>
#include <linux/mei_cl_bus.h>
#include <linux/mei_hdcp.h>
+#include <linux/component.h>
#include <drm/drm_connector.h>
#include <drm/i915_component.h>

#include "mei_hdcp.h"

+static bool mei_hdcp_component_registered;
+
/*
* mei_initiate_hdcp2_session:
* Function to start a Wired HDCP2.2 Tx Session with ME FW
@@ -705,8 +708,7 @@ static int mei_close_hdcp_session(struct device *dev, void *hdcp_data)
return 0;
}

-static __attribute__((unused))
-struct i915_hdcp_component_ops mei_hdcp_ops = {
+static struct i915_hdcp_component_ops mei_hdcp_ops = {
.owner = THIS_MODULE,
.initiate_hdcp2_session = mei_initiate_hdcp2_session,
.verify_receiver_cert_prepare_km = mei_verify_receiver_cert_prepare_km,
@@ -721,20 +723,81 @@ struct i915_hdcp_component_ops mei_hdcp_ops = {
.close_hdcp_session = mei_close_hdcp_session,
};

+static int mei_hdcp_component_bind(struct device *mei_kdev,
+ struct device *i915_kdev, void *data)
+{
+ struct i915_hdcp_component_master *master_comp = data;
+
+ mutex_lock(&master_comp->mutex);
+ dev_info(mei_kdev, "MEI HDCP comp bind\n");
+ WARN_ON(master_comp->ops);
+ master_comp->ops = &mei_hdcp_ops;
+ master_comp->dev = mei_kdev;
+ mutex_unlock(&master_comp->mutex);
+
+ return 0;
+}
+
+static void mei_hdcp_component_unbind(struct device *mei_kdev,
+ struct device *i915_kdev, void *data)
+{
+ struct i915_hdcp_component_master *master_comp = data;
+
+ mutex_lock(&master_comp->mutex);
+ dev_info(mei_kdev, "MEI HDCP comp unbind\n");
+ master_comp->ops = NULL;
+ master_comp->dev = NULL;
+ mutex_unlock(&master_comp->mutex);
+}
+
+static const struct component_ops mei_hdcp_component_bind_ops = {
+ .bind = mei_hdcp_component_bind,
+ .unbind = mei_hdcp_component_unbind,
+};
+
+static void mei_hdcp_component_init(struct device *dev)
+{
+ int ret;
+
+ dev_info(dev, "MEI HDCP comp init\n");
+ ret = component_add(dev, &mei_hdcp_component_bind_ops);
+ if (ret < 0) {
+ dev_err(dev, "Failed to add MEI HDCP comp (%d)\n", ret);
+ return;
+ }
+
+ mei_hdcp_component_registered = true;
+}
+
+static void mei_hdcp_component_cleanup(struct device *dev)
+{
+ if (!mei_hdcp_component_registered)
+ return;
+
+ dev_info(dev, "MEI HDCP comp cleanup\n");
+ component_del(dev, &mei_hdcp_component_bind_ops);
+ mei_hdcp_component_registered = false;
+}
+
static int mei_hdcp_probe(struct mei_cl_device *cldev,
const struct mei_cl_device_id *id)
{
int ret;

ret = mei_cldev_enable(cldev);
- if (ret < 0)
+ if (ret < 0) {
dev_err(&cldev->dev, "mei_cldev_enable Failed. %d\n", ret);
+ return ret;
+ }
+ mei_hdcp_component_init(&cldev->dev);

- return ret;
+ return 0;
}

static int mei_hdcp_remove(struct mei_cl_device *cldev)
{
+ mei_hdcp_component_cleanup(&cldev->dev);
+
return mei_cldev_disable(cldev);
}
--
2.7.4
Ramalingam C
2018-11-27 10:43:33 UTC
Permalink
Just excluding the LSPCon HDMI ports from the HDCP1.4 testing.

Signed-off-by: Ramalingam C <***@intel.com>
---
drivers/gpu/drm/i915/i915_debugfs.c | 10 ++++++++--
1 file changed, 8 insertions(+), 2 deletions(-)

diff --git a/drivers/gpu/drm/i915/i915_debugfs.c b/drivers/gpu/drm/i915/i915_debugfs.c
index c120d87df594..098a622c81bc 100644
--- a/drivers/gpu/drm/i915/i915_debugfs.c
+++ b/drivers/gpu/drm/i915/i915_debugfs.c
@@ -5067,6 +5067,9 @@ static int i915_hdcp_sink_capability_show(struct seq_file *m, void *data)
{
struct drm_connector *connector = m->private;
struct intel_connector *intel_connector = to_intel_connector(connector);
+ struct intel_digital_port *intel_dig_port =
+ conn_to_dig_port(intel_connector);
+ bool is_hdcp14;

if (connector->status != connector_status_connected)
return -ENODEV;
@@ -5077,8 +5080,11 @@ static int i915_hdcp_sink_capability_show(struct seq_file *m, void *data)

seq_printf(m, "%s:%d HDCP version: ", connector->name,
connector->base.id);
- seq_printf(m, "%s ", !intel_hdcp_capable(intel_connector) ?
- "None" : "HDCP1.4");
+
+ /* Excluding the Lspcon for Testing Purpose */
+ is_hdcp14 = intel_hdcp_capable(intel_connector) &&
+ !intel_dig_port->lspcon.active;
+ seq_printf(m, "%s ", !is_hdcp14 ? "None" : "HDCP1.4");
seq_puts(m, "\n");

return 0;
--
2.7.4
Ramalingam C
2018-11-27 10:43:32 UTC
Permalink
FOR TESTING PURPOSE ONLY.

By default INTEL_MEI_HDCP is set to y. This patch is created to
test the interface between I915 and MEI_HDCP.

Signed-off-by: Ramalingam C <***@intel.com>
---
drivers/misc/mei/Kconfig | 1 +
1 file changed, 1 insertion(+)

diff --git a/drivers/misc/mei/Kconfig b/drivers/misc/mei/Kconfig
index 9c518b7f0011..90ed55210447 100644
--- a/drivers/misc/mei/Kconfig
+++ b/drivers/misc/mei/Kconfig
@@ -48,5 +48,6 @@ config INTEL_MEI_HDCP
tristate "Intel HDCP2.2 services of ME Interface"
select INTEL_MEI_ME
depends on DRM_I915
+ default y
help
MEI Support for HDCP2.2 Services on Intel SoCs.
--
2.7.4
Patchwork
2018-11-27 11:08:05 UTC
Permalink
== Series Details ==

Series: drm/i915: Implement HDCP2.2 (rev10)
URL : https://patchwork.freedesktop.org/series/38254/
State : warning

== Summary ==

$ dim checkpatch origin/drm-tip
43bb107ec923 drm/i915: debug log for REPLY_ACK missing
fc94706f0fa9 drm/i915: Increase timeout for Encrypt status change
4a859569bb3f linux/mei: Header for mei_hdcp driver interface
-:46: WARNING:FILE_PATH_CHANGES: added, moved or deleted file(s), does MAINTAINERS need updating?
#46:
new file mode 100644

total: 0 errors, 1 warnings, 0 checks, 98 lines checked
13ed685826da drm/i915: Initialize HDCP2.2
38112d3b8c80 drm/i915: MEI interface definition
4b5d4d0ecb62 drm/i915: Enable and Disable of HDCP2.2
6691d01aa6af drm/i915: Implement HDCP2.2 receiver authentication
a3bff0ef2e2e drm/i915: Implement HDCP2.2 repeater authentication
fd1655759c2c drm/i915: Implement HDCP2.2 link integrity check
60b9f7c9e835 drm/i915: Handle HDCP2.2 downstream topology change
01f664212929 drm/i915: Check HDCP 1.4 and 2.2 link on CP_IRQ
b7e437235931 drm/i915: Implement the HDCP2.2 support for DP
b42f0d59a7aa drm/i915: Implement the HDCP2.2 support for HDMI
f833be214609 drm/i915: Add HDCP2.2 support for DP connectors
ce08bb3dc54c drm/i915: Add HDCP2.2 support for HDMI connectors
f55b730e38f7 mei: bus: whitelist hdcp client
48eff7f10be0 mei: bus: export to_mei_cl_device for mei client device drivers
8089e452b63e misc/mei/hdcp: Client driver for HDCP application
-:83: WARNING:FILE_PATH_CHANGES: added, moved or deleted file(s), does MAINTAINERS need updating?
#83:
new file mode 100644

-:101: WARNING:SPDX_LICENSE_TAG: Missing or malformed SPDX-License-Identifier tag in line 1
#101: FILE: drivers/misc/mei/hdcp/mei_hdcp.c:1:
+/* SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) */

total: 0 errors, 2 warnings, 0 checks, 76 lines checked
1842fab1ea37 misc/mei/hdcp: Define ME FW interface for HDCP2.2
-:32: WARNING:FILE_PATH_CHANGES: added, moved or deleted file(s), does MAINTAINERS need updating?
#32:
new file mode 100644

total: 0 errors, 1 warnings, 0 checks, 366 lines checked
59d2df7c4e13 misc/mei/hdcp: Initiate Wired HDCP2.2 Tx Session
462499fed80f misc/mei/hdcp: Verify Receiver Cert and prepare km
18c9c7987e82 misc/mei/hdcp: Verify H_prime
309f221e62f8 misc/mei/hdcp: Store the HDCP Pairing info
406b6c588bb2 misc/mei/hdcp: Initiate Locality check
d8fbb9ae52ba misc/mei/hdcp: Verify L_prime
91bf89f609f6 misc/mei/hdcp: Prepare Session Key
60cca4082670 misc/mei/hdcp: Repeater topology verification and ack
6440f412314d misc/mei/hdcp: Verify M_prime
6ef072a38e8f misc/mei/hdcp: Enabling the HDCP authentication
4fbcaae7fef6 misc/mei/hdcp: Closing wired HDCP2.2 Tx Session
52e39df77fcf misc/mei/hdcp: Component framework for I915 Interface
50a5ed51c0b8 drm/i915: Commit CP without modeset
23d8e7002f01 drm/i915: Fix KBL HDCP2.2 encrypt status signalling
f6eac629a0ab FOR_TEST: i915/Kconfig: Select mei_hdcp by I915
c3998b3fc3db FOR_TESTING_ONLY: debugfs: Excluding the LSPCon for HDCP1.4
Patchwork
2018-11-27 11:16:47 UTC
Permalink
== Series Details ==

Series: drm/i915: Implement HDCP2.2 (rev10)
URL : https://patchwork.freedesktop.org/series/38254/
State : warning

== Summary ==

$ dim sparse origin/drm-tip
Sparse version: v0.5.2
Commit: drm/i915: debug log for REPLY_ACK missing
Okay!

Commit: drm/i915: Increase timeout for Encrypt status change
Okay!

Commit: linux/mei: Header for mei_hdcp driver interface
Okay!

Commit: drm/i915: Initialize HDCP2.2
Okay!

Commit: drm/i915: MEI interface definition
-drivers/gpu/drm/i915/selftests/../i915_drv.h:3569:16: warning: expression using sizeof(void)
+drivers/gpu/drm/i915/selftests/../i915_drv.h:3571:16: warning: expression using sizeof(void)
+./include/linux/slab.h:332:43: warning: dubious: x & !y
+./include/linux/slab.h:665:13: error: not a function <noident>

Commit: drm/i915: Enable and Disable of HDCP2.2
Okay!

Commit: drm/i915: Implement HDCP2.2 receiver authentication
Okay!

Commit: drm/i915: Implement HDCP2.2 repeater authentication
+drivers/gpu/drm/i915/intel_hdcp.c:1336:30: warning: incorrect type in assignment (different base types)

Commit: drm/i915: Implement HDCP2.2 link integrity check
Okay!

Commit: drm/i915: Handle HDCP2.2 downstream topology change
Okay!

Commit: drm/i915: Check HDCP 1.4 and 2.2 link on CP_IRQ
+drivers/gpu/drm/i915/intel_hdcp.c:798:5: warning: symbol 'intel_hdcp_check_link' was not declared. Should it be static?

Commit: drm/i915: Implement the HDCP2.2 support for DP
Okay!

Commit: drm/i915: Implement the HDCP2.2 support for HDMI
Okay!

Commit: drm/i915: Add HDCP2.2 support for DP connectors
Okay!

Commit: drm/i915: Add HDCP2.2 support for HDMI connectors
Okay!

Commit: mei: bus: whitelist hdcp client
Okay!

Commit: mei: bus: export to_mei_cl_device for mei client device drivers
Okay!

Commit: misc/mei/hdcp: Client driver for HDCP application
+Error in reading or end of file.

Commit: misc/mei/hdcp: Define ME FW interface for HDCP2.2
Okay!

Commit: misc/mei/hdcp: Initiate Wired HDCP2.2 Tx Session
Okay!

Commit: misc/mei/hdcp: Verify Receiver Cert and prepare km
Okay!

Commit: misc/mei/hdcp: Verify H_prime
Okay!

Commit: misc/mei/hdcp: Store the HDCP Pairing info
Okay!

Commit: misc/mei/hdcp: Initiate Locality check
Okay!

Commit: misc/mei/hdcp: Verify L_prime
Okay!

Commit: misc/mei/hdcp: Prepare Session Key
Okay!

Commit: misc/mei/hdcp: Repeater topology verification and ack
Okay!

Commit: misc/mei/hdcp: Verify M_prime
Okay!

Commit: misc/mei/hdcp: Enabling the HDCP authentication
Okay!

Commit: misc/mei/hdcp: Closing wired HDCP2.2 Tx Session
Okay!

Commit: misc/mei/hdcp: Component framework for I915 Interface
Okay!

Commit: drm/i915: Commit CP without modeset
Okay!

Commit: drm/i915: Fix KBL HDCP2.2 encrypt status signalling
Okay!

Commit: FOR_TEST: i915/Kconfig: Select mei_hdcp by I915
Okay!

Commit: FOR_TESTING_ONLY: debugfs: Excluding the LSPCon for HDCP1.4
Okay!
Patchwork
2018-11-27 11:36:54 UTC
Permalink
== Series Details ==

Series: drm/i915: Implement HDCP2.2 (rev10)
URL : https://patchwork.freedesktop.org/series/38254/
State : failure

== Summary ==

= CI Bug Log - changes from CI_DRM_5208 -> Patchwork_10911 =

== Summary - FAILURE ==

Serious unknown changes coming with Patchwork_10911 absolutely need to be
verified manually.

If you think the reported changes have nothing to do with the changes
introduced in Patchwork_10911, please notify your bug team to allow them
to document this new failure mode, which will reduce false positives in CI.

External URL: https://patchwork.freedesktop.org/api/1.0/series/38254/revisions/10/mbox/

== Possible new issues ==

Here are the unknown changes that may have been introduced in Patchwork_10911:

=== IGT changes ===

==== Possible regressions ====

***@i915_module_load@reload:
fi-kbl-r: PASS -> DMESG-WARN
fi-apl-guc: PASS -> DMESG-WARN
fi-whl-u: PASS -> DMESG-WARN
fi-skl-6600u: PASS -> DMESG-WARN
fi-skl-iommu: PASS -> DMESG-WARN
fi-hsw-4770r: PASS -> DMESG-WARN
fi-skl-6700k2: PASS -> DMESG-WARN
fi-skl-6700hq: NOTRUN -> DMESG-WARN
fi-bdw-5557u: PASS -> DMESG-WARN
fi-skl-guc: PASS -> DMESG-WARN
fi-kbl-guc: PASS -> DMESG-WARN
fi-cfl-8109u: PASS -> DMESG-WARN
fi-bdw-gvtdvm: PASS -> DMESG-WARN
{fi-kbl-7500u}: PASS -> DMESG-WARN
fi-cfl-8700k: PASS -> DMESG-WARN
fi-bxt-dsi: PASS -> DMESG-WARN
fi-hsw-4770: PASS -> DMESG-WARN
fi-cfl-guc: PASS -> DMESG-WARN
fi-skl-6770hq: PASS -> DMESG-WARN
fi-skl-gvtdvm: PASS -> DMESG-WARN
fi-hsw-peppy: PASS -> DMESG-WARN
fi-skl-6260u: PASS -> DMESG-WARN
fi-bxt-j4205: PASS -> DMESG-WARN
{fi-kbl-7567u}: PASS -> DMESG-WARN

***@i915_module_load@reload-with-fault-injection:
{fi-icl-u3}: PASS -> INCOMPLETE
fi-kbl-x1275: PASS -> INCOMPLETE

{***@runner@aborted}:
fi-bdw-gvtdvm: NOTRUN -> FAIL
fi-cfl-8109u: NOTRUN -> FAIL
fi-hsw-peppy: NOTRUN -> FAIL
fi-hsw-4770: NOTRUN -> FAIL
{fi-kbl-7500u}: NOTRUN -> FAIL
fi-bxt-j4205: NOTRUN -> FAIL
fi-skl-6700hq: NOTRUN -> FAIL
fi-whl-u: NOTRUN -> FAIL
fi-bxt-dsi: NOTRUN -> FAIL
fi-skl-iommu: NOTRUN -> FAIL
fi-cfl-guc: NOTRUN -> FAIL
{fi-kbl-7567u}: NOTRUN -> FAIL
fi-skl-guc: NOTRUN -> FAIL
fi-skl-6700k2: NOTRUN -> FAIL
fi-cfl-8700k: NOTRUN -> FAIL
fi-hsw-4770r: NOTRUN -> FAIL
fi-skl-6600u: NOTRUN -> FAIL
fi-apl-guc: NOTRUN -> FAIL
fi-kbl-r: NOTRUN -> FAIL
fi-bdw-5557u: NOTRUN -> FAIL
fi-skl-6770hq: NOTRUN -> FAIL
fi-kbl-guc: NOTRUN -> FAIL
fi-skl-gvtdvm: NOTRUN -> FAIL
fi-skl-6260u: NOTRUN -> FAIL


== Known issues ==

Here are the changes found in Patchwork_10911 that come from known issues:

=== IGT changes ===

==== Issues hit ====

***@gem_ctx_switch@basic-default:
fi-icl-u2: PASS -> DMESG-WARN (fdo#107724)

***@gem_exec_reloc@basic-cpu:
fi-icl-u2: PASS -> DMESG-WARN (fdo#108343)

{***@runner@aborted}:
{fi-icl-u3}: NOTRUN -> FAIL (fdo#105602)
fi-kbl-x1275: NOTRUN -> FAIL (fdo#105602)


==== Possible fixes ====

***@gem_ctx_create@basic-files:
fi-icl-u2: DMESG-WARN (fdo#107724) -> PASS

***@gem_exec_suspend@basic-s3:
fi-blb-e6850: INCOMPLETE (fdo#107718) -> PASS

***@kms_pipe_crc_basic@nonblocking-crc-pipe-a:
fi-byt-clapper: FAIL (fdo#107362) -> PASS

***@kms_pipe_crc_basic@suspend-read-crc-pipe-c:
fi-apl-guc: DMESG-WARN (fdo#108566) -> PASS


{name}: This element is suppressed. This means it is ignored when computing
the status of the difference (SUCCESS, WARNING, or FAILURE).

fdo#105602 https://bugs.freedesktop.org/show_bug.cgi?id=105602
fdo#107362 https://bugs.freedesktop.org/show_bug.cgi?id=107362
fdo#107718 https://bugs.freedesktop.org/show_bug.cgi?id=107718
fdo#107724 https://bugs.freedesktop.org/show_bug.cgi?id=107724
fdo#108343 https://bugs.freedesktop.org/show_bug.cgi?id=108343
fdo#108566 https://bugs.freedesktop.org/show_bug.cgi?id=108566


== Participating hosts (50 -> 46) ==

Additional (1): fi-skl-6700hq
Missing (5): fi-ilk-m540 fi-hsw-4200u fi-byt-squawks fi-bsw-cyan fi-ctg-p8600


== Build changes ==

* Linux: CI_DRM_5208 -> Patchwork_10911

CI_DRM_5208: e556a1602bef92792a069e4fe1cb2c851efb2294 @ git://anongit.freedesktop.org/gfx-ci/linux
IGT_4729: 2388bbd062c17b5912039101efd4603e8d876c88 @ git://anongit.freedesktop.org/xorg/app/intel-gpu-tools
Patchwork_10911: c3998b3fc3db38d819667e66a902b38243a73fcd @ git://anongit.freedesktop.org/gfx-ci/linux


== Linux commits ==

c3998b3fc3db FOR_TESTING_ONLY: debugfs: Excluding the LSPCon for HDCP1.4
f6eac629a0ab FOR_TEST: i915/Kconfig: Select mei_hdcp by I915
23d8e7002f01 drm/i915: Fix KBL HDCP2.2 encrypt status signalling
50a5ed51c0b8 drm/i915: Commit CP without modeset
52e39df77fcf misc/mei/hdcp: Component framework for I915 Interface
4fbcaae7fef6 misc/mei/hdcp: Closing wired HDCP2.2 Tx Session
6ef072a38e8f misc/mei/hdcp: Enabling the HDCP authentication
6440f412314d misc/mei/hdcp: Verify M_prime
60cca4082670 misc/mei/hdcp: Repeater topology verification and ack
91bf89f609f6 misc/mei/hdcp: Prepare Session Key
d8fbb9ae52ba misc/mei/hdcp: Verify L_prime
406b6c588bb2 misc/mei/hdcp: Initiate Locality check
309f221e62f8 misc/mei/hdcp: Store the HDCP Pairing info
18c9c7987e82 misc/mei/hdcp: Verify H_prime
462499fed80f misc/mei/hdcp: Verify Receiver Cert and prepare km
59d2df7c4e13 misc/mei/hdcp: Initiate Wired HDCP2.2 Tx Session
1842fab1ea37 misc/mei/hdcp: Define ME FW interface for HDCP2.2
8089e452b63e misc/mei/hdcp: Client driver for HDCP application
48eff7f10be0 mei: bus: export to_mei_cl_device for mei client device drivers
f55b730e38f7 mei: bus: whitelist hdcp client
ce08bb3dc54c drm/i915: Add HDCP2.2 support for HDMI connectors
f833be214609 drm/i915: Add HDCP2.2 support for DP connectors
b42f0d59a7aa drm/i915: Implement the HDCP2.2 support for HDMI
b7e437235931 drm/i915: Implement the HDCP2.2 support for DP
01f664212929 drm/i915: Check HDCP 1.4 and 2.2 link on CP_IRQ
60b9f7c9e835 drm/i915: Handle HDCP2.2 downstream topology change
fd1655759c2c drm/i915: Implement HDCP2.2 link integrity check
a3bff0ef2e2e drm/i915: Implement HDCP2.2 repeater authentication
6691d01aa6af drm/i915: Implement HDCP2.2 receiver authentication
4b5d4d0ecb62 drm/i915: Enable and Disable of HDCP2.2
38112d3b8c80 drm/i915: MEI interface definition
13ed685826da drm/i915: Initialize HDCP2.2
4a859569bb3f linux/mei: Header for mei_hdcp driver interface
fc94706f0fa9 drm/i915: Increase timeout for Encrypt status change
43bb107ec923 drm/i915: debug log for REPLY_ACK missing

== Logs ==

For more details see: https://intel-gfx-ci.01.org/tree/drm-tip/Patchwork_10911/
Daniel Vetter
2018-12-06 14:27:17 UTC
Permalink
Post by Ramalingam C
This series enables the HDCP2.2 for I915. The sequence for HDCP2.2
authentication and encryption is implemented as a generic flow
between HDMI and DP. Encoder specific implementations are moved
into hdcp_shim.
Intel HWs supports HDCP2.2 through ME FW. Hence this series
introduces a client driver for mei bus, so that for HDCP2.2
authentication, HDCP2.2 stack in I915 can avail the services from
ME FW. To enable this client driver set the config variable
CONFIG_INTEL_MEI_HDCP.
Userspace interface remains unchanged as version agnostic. When
userspace request for HDCP enable, Kernel will detect the HDCP source
and sink's HDCP version(1.4/2.2)capability and enable the best capable
version for that combination.
This series enables the HDCP2.2 for Type0 content strams.
- Mei_hdcp component binding status will impact HDCP2.2 alone.
Oh now I see why component side looked so totally different than the old
series. That's not how component works, and essentially we're back to
rolling all the synchronization ourselves.

If you have an optional component, you need to decide whether you want it
at component_match_add time: Either add the component as a match, or not.
All the other stuff needs to stay the same.

That also explains why I didn't find the special audio component
registration hack anymore. I think for the unload side we need to make the
symmetrical hack, i.e. unregister the audio component from the pci unload
function, not from component_master_unbind. To avoid the locking
recursion.

The proper fix for the recursion should be easy, we just need to add
_locked version of component functions called for the audio component,
which check that the component lock is already taken (like
component_bind_all and friends already do to break the recursion in other
places).
-Daniel
Post by Ramalingam C
- hdcp2_check_work is cancelled only when hdcp2 is supported.
- Errors due to sinks are reported as DEBUG logs.
- HDCP2.2 auth will be attempted only if the mei_hdcp interface is up.
- Maintainability ease MEI i/f changes are gathered in single patch.
- intel_hdcp_exit is defined and used.
To ease the review process, series is hosted at
https://github.com/ramalingampc2008/drm-tip.git hdcp2_2_v8
drm/i915: debug log for REPLY_ACK missing
drm/i915: Increase timeout for Encrypt status change
linux/mei: Header for mei_hdcp driver interface
drm/i915: Initialize HDCP2.2
drm/i915: MEI interface definition
drm/i915: Enable and Disable of HDCP2.2
drm/i915: Implement HDCP2.2 receiver authentication
drm/i915: Implement HDCP2.2 repeater authentication
drm/i915: Implement HDCP2.2 link integrity check
drm/i915: Handle HDCP2.2 downstream topology change
drm/i915: Check HDCP 1.4 and 2.2 link on CP_IRQ
drm/i915: Implement the HDCP2.2 support for DP
drm/i915: Implement the HDCP2.2 support for HDMI
drm/i915: Add HDCP2.2 support for DP connectors
drm/i915: Add HDCP2.2 support for HDMI connectors
misc/mei/hdcp: Client driver for HDCP application
misc/mei/hdcp: Define ME FW interface for HDCP2.2
misc/mei/hdcp: Initiate Wired HDCP2.2 Tx Session
misc/mei/hdcp: Verify Receiver Cert and prepare km
misc/mei/hdcp: Verify H_prime
misc/mei/hdcp: Store the HDCP Pairing info
misc/mei/hdcp: Initiate Locality check
misc/mei/hdcp: Verify L_prime
misc/mei/hdcp: Prepare Session Key
misc/mei/hdcp: Repeater topology verification and ack
misc/mei/hdcp: Verify M_prime
misc/mei/hdcp: Enabling the HDCP authentication
misc/mei/hdcp: Closing wired HDCP2.2 Tx Session
misc/mei/hdcp: Component framework for I915 Interface
drm/i915: Commit CP without modeset
drm/i915: Fix KBL HDCP2.2 encrypt status signalling
FOR_TEST: i915/Kconfig: Select mei_hdcp by I915
FOR_TESTING_ONLY: debugfs: Excluding the LSPCon for HDCP1.4
mei: bus: whitelist hdcp client
mei: bus: export to_mei_cl_device for mei client device drivers
MAINTAINERS | 1 +
drivers/gpu/drm/i915/i915_debugfs.c | 10 +-
drivers/gpu/drm/i915/i915_drv.c | 2 +
drivers/gpu/drm/i915/i915_drv.h | 2 +
drivers/gpu/drm/i915/intel_ddi.c | 7 -
drivers/gpu/drm/i915/intel_display.c | 21 +-
drivers/gpu/drm/i915/intel_dp.c | 349 ++++++++-
drivers/gpu/drm/i915/intel_drv.h | 81 +-
drivers/gpu/drm/i915/intel_hdcp.c | 1390 +++++++++++++++++++++++++++++++---
drivers/gpu/drm/i915/intel_hdmi.c | 193 ++++-
drivers/misc/mei/Kconfig | 8 +
drivers/misc/mei/Makefile | 2 +
drivers/misc/mei/bus-fixup.c | 16 +
drivers/misc/mei/bus.c | 1 -
drivers/misc/mei/hdcp/Makefile | 7 +
drivers/misc/mei/hdcp/mei_hdcp.c | 825 ++++++++++++++++++++
drivers/misc/mei/hdcp/mei_hdcp.h | 366 +++++++++
include/drm/drm_hdcp.h | 23 +
include/drm/i915_component.h | 71 ++
include/linux/mei_cl_bus.h | 2 +
include/linux/mei_hdcp.h | 91 +++
21 files changed, 3340 insertions(+), 128 deletions(-)
create mode 100644 drivers/misc/mei/hdcp/Makefile
create mode 100644 drivers/misc/mei/hdcp/mei_hdcp.c
create mode 100644 drivers/misc/mei/hdcp/mei_hdcp.h
create mode 100644 include/linux/mei_hdcp.h
--
2.7.4
--
Daniel Vetter
Software Engineer, Intel Corporation
http://blog.ffwll.ch
Loading...