Based on HDCP1.4 framework introduced by Sean Paul, this series implements the HDCP2.2 in I915.
The sequence for HDCP2.2 authentication and encryption is implemented in I915. 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.
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 streams.
Yes its bit lengthy series. Please tolerate. Thanks
Major Changes in v2: - Synchronous implementation of HDCP authentication [SeanPaul] - Removal of bit-fields usage.[Tomas and Jani] - Protecting the mei_interface handle with mutex [Chris] - Droped, added and squashed few patches - Extended hdcp_shim to support hdcp2.2 operations too. [SeanPaul] - Used Intel_wait_for_registers(), Where ever it is applicable.[Chris] - mei_hdcp driver is moved into drivers/misc/mei/hdcp/ [Tomas] - Adapted the static declaration for struct intel_hdcp and mei_hdcp_data. [SeanPaul]
Sincere thanks for Sean Paul, Jani Nikula, Chris Wilson and Tomas Winkler for the review comments on v1 series.
Ramalingam C (41): drm: hdcp2.2 authentication msg definitions drm: HDMI and DP specific HDCP2.2 defines misc/mei/hdcp: Client driver for HDCP application misc/mei/hdcp: Add KBuild for mei hdcp driver misc/mei/hdcp: Verify mei client device status misc/mei/hdcp: Get & Put for mei cl_device misc/mei/hdcp: Define ME FW interface for HDCP2.2 linux/mei: Header for mei_hdcp driver interface 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 verifcation and ack misc/mei/hdcp: Verify M_prime misc/mei/hdcp: Enabling the HDCP authentication misc/mei/hdcp: Closing wired HDCP2.2 Tx Session drm/i915: wrapping all hdcp var into intel_hdcp drm/i915: Define HDCP2.2 related variables drm/i915: Define Intel HDCP2.2 registers drm/i915: Wrappers for mei HDCP2.2 services drm/i915: Implement HDCP2.2 receiver authentication drm/i915: Implement HDCP2.2 repeater authentication drm/i915: Enable and Disable HDCP2.2 port encryption drm/i915: Implement HDCP2.2 En/Dis-able drm/i915: Implement HDCP2.2 link integrity check drm/i915: Handle HDCP2.2 downstream topology change drm/i915: Pullout the bksv read and validation drm/i915: Initialize HDCP2.2 and its MEI interface drm/i915: Schedule hdcp_check_link in _intel_hdcp_enable drm/i915: Enable superior HDCP ver that is capable drm/i915: Enable HDCP1.4 incase of HDCP2.2 failure drm/i915: hdcp_check_link only on CP_IRQ drm/i915: Check HDCP 1.4 and 2.2 link on CP_IRQ drm/i915: Implement gmbus burst read 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
Tomas Winkler (1): mei: bus: whitelist hdcp client
drivers/gpu/drm/i915/i915_drv.c | 1 + drivers/gpu/drm/i915/i915_drv.h | 9 + drivers/gpu/drm/i915/i915_reg.h | 35 ++ drivers/gpu/drm/i915/intel_display.c | 7 +- drivers/gpu/drm/i915/intel_dp.c | 362 ++++++++++- drivers/gpu/drm/i915/intel_drv.h | 81 ++- drivers/gpu/drm/i915/intel_hdcp.c | 1107 ++++++++++++++++++++++++++++++++-- drivers/gpu/drm/i915/intel_hdmi.c | 206 ++++++- drivers/gpu/drm/i915/intel_i2c.c | 124 +++- drivers/misc/mei/Kconfig | 6 + drivers/misc/mei/Makefile | 2 + drivers/misc/mei/bus-fixup.c | 16 + drivers/misc/mei/hdcp/Makefile | 6 + drivers/misc/mei/hdcp/mei_hdcp.c | 927 ++++++++++++++++++++++++++++ drivers/misc/mei/hdcp/mei_hdcp.h | 566 +++++++++++++++++ include/drm/drm_dp_helper.h | 54 ++ include/drm/drm_hdcp.h | 220 +++++++ include/linux/mei_hdcp.h | 215 +++++++ 18 files changed, 3846 insertions(+), 98 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
This patch defines the hdcp2.2 protocol messages for the HDCP2.2 authentication.
v2: bit_fields are removed. Instead bitmasking used. [Tomas and Jani] prefix HDCP_2_2_ is added to the macros. [Tomas]
Signed-off-by: Ramalingam C ramalingam.c@intel.com --- include/drm/drm_hdcp.h | 183 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 183 insertions(+)
diff --git a/include/drm/drm_hdcp.h b/include/drm/drm_hdcp.h index 562fa7df2637..5e0a5ed1a08e 100644 --- a/include/drm/drm_hdcp.h +++ b/include/drm/drm_hdcp.h @@ -38,4 +38,187 @@ #define DRM_HDCP_DDC_BSTATUS 0x41 #define DRM_HDCP_DDC_KSV_FIFO 0x43
+#define DRM_HDCP_1_4_SRM_ID 0x8 +#define DRM_HDCP_1_4_VRL_LENGTH_SIZE 3 +#define DRM_HDCP_1_4_DCP_SIG_SIZE 40 + +/** + * Protocol message definition for HDCP2.2 specification + */ + +#define HDCP_STREAM_TYPE0 0x00 +#define HDCP_STREAM_TYPE1 0x01 + +/* HDCP2.2 Msg IDs */ +#define HDCP_2_2_NULL_MSG 1 +#define HDCP_2_2_AKE_INIT 2 +#define HDCP_2_2_AKE_SEND_CERT 3 +#define HDCP_2_2_AKE_NO_STORED_KM 4 +#define HDCP_2_2_AKE_STORED_KM 5 +#define HDCP_2_2_AKE_SEND_HPRIME 7 +#define HDCP_2_2_AKE_SEND_PARING_INFO 8 +#define HDCP_2_2_LC_INIT 9 +#define HDCP_2_2_LC_SEND_LPRIME 10 +#define HDCP_2_2_SKE_SEND_EKS 11 +#define HDCP_2_2_REP_SEND_RECVID_LIST 12 +#define HDCP_2_2_REP_SEND_ACK 15 +#define HDCP_2_2_REP_STREAM_MANAGE 16 +#define HDCP_2_2_REP_STREAM_READY 17 +#define HDCP_2_2_ERRATA_DP_STREAM_TYPE 50 + +#define HDCP_2_2_RTX_LEN 8 +#define HDCP_2_2_RRX_LEN 8 + +#define HDCP_2_2_K_PUB_RX_MOD_N_LEN 128 +#define HDCP_2_2_K_PUB_RX_EXP_E_LEN 3 +#define HDCP_2_2_K_PUB_RX_LEN (HDCP_2_2_K_PUB_RX_MOD_N_LEN + \ + HDCP_2_2_K_PUB_RX_EXP_E_LEN) + +#define HDCP_2_2_DCP_LLC_SIG_LEN 384 + +#define HDCP_2_2_E_KPUB_KM_LEN 128 +#define HDCP_2_2_E_KH_KM_M_LEN (16 + 16) +#define HDCP_2_2_H_PRIME_LEN 32 +#define HDCP_2_2_E_KH_KM_LEN 16 +#define HDCP_2_2_RN_LEN 8 +#define HDCP_2_2_L_PRIME_LEN 32 +#define HDCP_2_2_E_DKEY_KS_LEN 16 +#define HDCP_2_2_RIV_LEN 8 +#define HDCP_2_2_SEQ_NUM_LEN 3 +#define HDCP_2_2_LPRIME_HALF_LEN (HDCP_2_2_L_PRIME_LEN / 2) +#define HDCP_2_2_RECEIVER_ID_LEN DRM_HDCP_KSV_LEN +#define HDCP_2_2_MAX_DEVICE_COUNT 31 +#define HDCP_2_2_RECEIVER_IDS_MAX_LEN (HDCP_2_2_RECEIVER_ID_LEN * \ + HDCP_2_2_MAX_DEVICE_COUNT) +#define HDCP_2_2_MPRIME_LEN 32 + +/** + * TODO: This has to be changed for DP MST, as multiple stream on + * same port is possible. + * For HDCP2.2 on HDMI and DP SST this value is always 1. + */ +#define HDCP_2_2_MAX_CONTENT_STREAMS_CNT 1 +#define HDCP_2_2_TXCAP_MASK_LEN 2 +#define HDCP_2_2_RXCAPS_LEN 3 +#define HDCP_2_2_RX_REPEATER(x) (x & BIT(0)) +#define HDCP_2_2_DP_HDCP_CAPABLE(x) (x & BIT(1)) +#define HDCP_2_2_RXINFO_LEN 2 + +/* HDCP1.x compliant device in downstream */ +#define HDCP_2_2_HDCP1_DEVICE_CONNECTED(x) (x & BIT(0)) + +/* HDCP2.0 Compliant repeater in downstream */ +#define HDCP_2_2_HDCP_2_0_REP_CONNECTED(x) (x & BIT(1)) +#define HDCP_2_2_MAX_CASCADE_EXCEEDED(x) (x & BIT(2)) +#define HDCP_2_2_MAX_DEVS_EXCEEDED(x) (x & BIT(3)) +#define HDCP_2_2_DEV_COUNT_LO(x) ((x & (0xF << 4)) >> 4) +#define HDCP_2_2_DEV_COUNT_HI(x) (x & BIT(0)) +#define HDCP_2_2_DEPTH(x) ((x & (0x7 << 1)) >> 1) + +struct hdcp2_cert_rx { + uint8_t receiver_id[HDCP_2_2_RECEIVER_ID_LEN]; + uint8_t kpub_rx[HDCP_2_2_K_PUB_RX_LEN]; + uint8_t reserved[2]; + uint8_t dcp_signature[HDCP_2_2_DCP_LLC_SIG_LEN]; +} __packed; + +struct hdcp2_streamid_type { + uint8_t stream_id; + uint8_t stream_type; +} __packed; + +/** + * The TxCaps field specified in the HDCP HDMI, DP specs + * This field is big endian as specified in the errata. + */ +struct hdcp2_tx_caps { + /* Transmitter must set this to 0x2 */ + uint8_t version; + + /* Reserved for HDCP and DP Spec. Read as Zero */ + uint8_t tx_cap_mask[HDCP_2_2_TXCAP_MASK_LEN]; +} __packed; + +/* + * Main structures for HDCP2.2 protocol communication + */ +struct hdcp2_ake_init { + uint8_t msg_id; + uint8_t r_tx[HDCP_2_2_RTX_LEN]; + struct hdcp2_tx_caps tx_caps; +} __packed; + +struct hdcp2_ake_send_cert { + uint8_t msg_id; + struct hdcp2_cert_rx cert_rx; + uint8_t r_rx[HDCP_2_2_RRX_LEN]; + uint8_t rx_caps[HDCP_2_2_RXCAPS_LEN]; +} __packed; + +struct hdcp2_ake_no_stored_km { + uint8_t msg_id; + uint8_t e_kpub_km[HDCP_2_2_E_KPUB_KM_LEN]; +} __packed; + +struct hdcp2_ake_stored_km { + uint8_t msg_id; + uint8_t e_kh_km_m[HDCP_2_2_E_KH_KM_M_LEN]; +} __packed; + +struct hdcp2_ake_send_hprime { + uint8_t msg_id; + uint8_t h_prime[HDCP_2_2_H_PRIME_LEN]; +} __packed; + +struct hdcp2_ake_send_pairing_info { + uint8_t msg_id; + uint8_t e_kh_km[HDCP_2_2_E_KH_KM_LEN]; +} __packed; + +struct hdcp2_lc_init { + uint8_t msg_id; + uint8_t r_n[HDCP_2_2_RN_LEN]; +} __packed; + +struct hdcp2_lc_send_lprime { + uint8_t msg_id; + uint8_t l_prime[HDCP_2_2_L_PRIME_LEN]; +} __packed; + +struct hdcp2_ske_send_eks { + uint8_t msg_id; + uint8_t e_dkey_ks[HDCP_2_2_E_DKEY_KS_LEN]; + uint8_t riv[HDCP_2_2_RIV_LEN]; +} __packed; + +struct hdcp2_rep_send_receiverid_list { + uint8_t msg_id; + uint8_t rx_info[HDCP_2_2_RXINFO_LEN]; + uint8_t seq_num_v[HDCP_2_2_SEQ_NUM_LEN]; + uint8_t v_prime[HDCP_2_2_LPRIME_HALF_LEN]; + uint8_t receiver_ids[HDCP_2_2_RECEIVER_IDS_MAX_LEN]; +} __packed; + +struct hdcp2_rep_send_ack { + uint8_t msg_id; + uint8_t v[HDCP_2_2_LPRIME_HALF_LEN]; +} __packed; + +struct hdcp2_rep_stream_manage { + uint8_t msg_id; + uint8_t seq_num_m[HDCP_2_2_SEQ_NUM_LEN]; + __be16 k; + struct hdcp2_streamid_type streams[HDCP_2_2_MAX_CONTENT_STREAMS_CNT]; +} __packed; + +struct hdcp2_rep_stream_ready { + uint8_t msg_id; + uint8_t m_prime[HDCP_2_2_MPRIME_LEN]; +} __packed; + +struct hdcp2_dp_errata_stream_type { + uint8_t msg_id; + uint8_t stream_type; +} __packed; + #endif
In preparation for implementing HDCP2.2 in I915, this patch adds HDCP register definitions for HDMI and DP HDCP adaptations.
HDMI specific HDCP2.2 register definitions are added into drm_hdcp.h, where are HDCP2.2 register offsets in DPCD offsets are defined at drm_dp_helper.h.
v2: bit_field definitions are replaced by macros. [Tomas and Jany]
Signed-off-by: Ramalingam C ramalingam.c@intel.com --- include/drm/drm_dp_helper.h | 54 +++++++++++++++++++++++++++++++++++++++++++++ include/drm/drm_hdcp.h | 29 ++++++++++++++++++++++++ 2 files changed, 83 insertions(+)
diff --git a/include/drm/drm_dp_helper.h b/include/drm/drm_dp_helper.h index 4de97e94ef9d..2185b3a88911 100644 --- a/include/drm/drm_dp_helper.h +++ b/include/drm/drm_dp_helper.h @@ -887,6 +887,60 @@ #define DP_AUX_HDCP_KSV_FIFO 0x6802C #define DP_AUX_HDCP_AINFO 0x6803B
+/** + * DP HDCP2.2 parameter offsets in DPCD address space + */ +#define DP_HDCP_2_2_REG_RTX_OFFSET 0x69000 +#define DP_HDCP_2_2_REG_TXCAPS_OFFSET 0x69008 +#define DP_HDCP_2_2_REG_CERT_RX_OFFSET 0x6900B +#define DP_HDCP_2_2_REG_RRX_OFFSET 0x69215 +#define DP_HDCP_2_2_REG_RX_CAPS_OFFSET 0x6921D +#define DP_HDCP_2_2_REG_EKPUB_KM_OFFSET 0x69220 +#define DP_HDCP_2_2_REG_EKH_KM_OFFSET 0x692A0 +#define DP_HDCP_2_2_REG_M_OFFSET 0x692B0 +#define DP_HDCP_2_2_REG_HPRIME_OFFSET 0x692C0 +#define DP_HDCP_2_2_REG_EKH_KM_RD_OFFSET 0x692E0 +#define DP_HDCP_2_2_REG_RN_OFFSET 0x692F0 +#define DP_HDCP_2_2_REG_LPRIME_OFFSET 0x692F8 +#define DP_HDCP_2_2_REG_EDKEY_KS_OFFSET 0x69318 +#define DP_HDCP_2_2_REG_RIV_OFFSET 0x69328 +#define DP_HDCP_2_2_REG_RXINFO_OFFSET 0x69330 +#define DP_HDCP_2_2_REG_SEQ_NUM_V_OFFSET 0x69332 +#define DP_HDCP_2_2_REG_VPRIME_OFFSET 0x69335 +#define DP_HDCP_2_2_REG_RECV_ID_LIST_OFFSET 0x69345 +#define DP_HDCP_2_2_REG_V_OFFSET 0x693E0 +#define DP_HDCP_2_2_REG_SEQ_NUM_M_OFFSET 0x693F0 +#define DP_HDCP_2_2_REG_K_OFFSET 0x693F3 +#define DP_HDCP_2_2_REG_STREAM_ID_TYPE_OFFSET 0x693F5 +#define DP_HDCP_2_2_REG_MPRIME_OFFSET 0x69473 +#define DP_HDCP_2_2_REG_RXSTATUS_OFFSET 0x69493 +#define DP_HDCP_2_2_REG_STREAM_TYPE_OFFSET 0x69494 +#define DP_HDCP_2_2_REG_DBG_OFFSET 0x69518 + +/** + * DP HDCP message start offsets in DPCD address space + */ +#define DP_HDCP_2_2_AKE_INIT_OFFSET DP_HDCP_2_2_REG_RTX_OFFSET +#define DP_HDCP_2_2_AKE_SEND_CERT_OFFSET DP_HDCP_2_2_REG_CERT_RX_OFFSET +#define DP_HDCP_2_2_AKE_NO_STORED_KM_OFFSET DP_HDCP_2_2_REG_EKPUB_KM_OFFSET +#define DP_HDCP_2_2_AKE_STORED_KM_OFFSET DP_HDCP_2_2_REG_EKH_KM_OFFSET +#define DP_HDCP_2_2_AKE_SEND_HPRIME_OFFSET DP_HDCP_2_2_REG_HPRIME_OFFSET +#define DP_HDCP_2_2_AKE_SEND_PARING_INFO_OFFSET DP_HDCP_2_2_REG_EKH_KM_RD_OFFSET +#define DP_HDCP_2_2_LC_INIT_OFFSET DP_HDCP_2_2_REG_RN_OFFSET +#define DP_HDCP_2_2_LC_SEND_LPRIME_OFFSET DP_HDCP_2_2_REG_LPRIME_OFFSET +#define DP_HDCP_2_2_SKE_SEND_EKS_OFFSET DP_HDCP_2_2_REG_EDKEY_KS_OFFSET +#define DP_HDCP_2_2_REP_SEND_RECVID_LIST_OFFSET DP_HDCP_2_2_REG_RXINFO_OFFSET +#define DP_HDCP_2_2_REP_SEND_ACK_OFFSET DP_HDCP_2_2_REG_V_OFFSET +#define DP_HDCP_2_2_REP_STREAM_MANAGE_OFFSET DP_HDCP_2_2_REG_SEQ_NUM_M_OFFSET +#define DP_HDCP_2_2_REP_STREAM_READY_OFFSET DP_HDCP_2_2_REG_MPRIME_OFFSET + +#define HDCP_2_2_DP_RXSTATUS_LEN 1 +#define HDCP_2_2_DP_RXSTATUS_READY(x) (x & BIT(0)) +#define HDCP_2_2_DP_RXSTATUS_H_PRIME(x) (x & BIT(1)) +#define HDCP_2_2_DP_RXSTATUS_PAIRING(x) (x & BIT(2)) +#define HDCP_2_2_DP_RXSTATUS_REAUTH_REQ(x) (x & BIT(3)) +#define HDCP_2_2_DP_RXSTATUS_LINK_FAILED(x) (x & BIT(4)) + /* DP 1.2 Sideband message defines */ /* peer device type - DP 1.2a Table 2-92 */ #define DP_PEER_DEVICE_NONE 0x0 diff --git a/include/drm/drm_hdcp.h b/include/drm/drm_hdcp.h index 5e0a5ed1a08e..f3f28414b189 100644 --- a/include/drm/drm_hdcp.h +++ b/include/drm/drm_hdcp.h @@ -221,4 +221,33 @@ struct hdcp2_dp_errata_stream_type { uint8_t stream_type; } __packed;
+/* HDCP2.2 TIMEOUTs in mSec */ +#define HDCP_2_2_CERT_TIMEOUT 100 +#define HDCP_2_2_HPRIME_NO_PAIRED_TIMEOUT 1000 +#define HDCP_2_2_HPRIME_PAIRED_TIMEOUT 200 +#define HDCP_2_2_PAIRING_TIMEOUT 200 +#define HDCP_2_2_HDMI_LPRIME_TIMEOUT 20 +#define HDCP_2_2_DP_LPRIME_TIMEOUT 7 +#define HDCP_2_2_RECVID_LIST_TIMEOUT 3000 +#define HDCP_2_2_STREAM_READY_TIMEOUT 100 + +/* HDMI HDCP2.2 Register Offsets */ +#define HDCP_2_2_HDMI_REG_VER_OFFSET 0x50 +#define HDCP_2_2_HDMI_REG_WR_MSG_OFFSET 0x60 +#define HDCP_2_2_HDMI_REG_RXSTATUS_OFFSET 0x70 +#define HDCP_2_2_HDMI_REG_RD_MSG_OFFSET 0x80 +#define HDCP_2_2_HDMI_REG_DBG_OFFSET 0xC0 + +#define HDCP_2_2_HDMI_SUPPORT_MASK BIT(2) +#define HDCP_2_2_RXCAPS_VERSION_VAL 0x2 + +#define HDCP_2_2_RX_CAPS_VERSION_VAL 0x02 +#define HDCP_2_2_SEQ_NUM_MAX 0xFFFFFF +#define HDCP_2_2_DELAY_BEFORE_ENCRYPTION_EN 200 + +#define HDCP_2_2_HDMI_RXSTATUS_LEN 2 +#define HDCP_2_2_HDMI_RXSTATUS_MSG_SZ_HI(x) (x & 0x3) +#define HDCP_2_2_HDMI_RXSTATUS_READY(x) (x & BIT(2)) +#define HDCP_2_2_HDMI_RXSTATUS_REAUTH_REQ(x) (x & BIT(3)) + #endif
From: Tomas Winkler tomas.winkler@intel.com
Whitelist HDCP client for in kernel drm use
v2: Rebased.
Signed-off-by: Tomas Winkler 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 0208c4b027c5..3df2a69fddfb 100644 --- a/drivers/misc/mei/bus-fixup.c +++ b/drivers/misc/mei/bus-fixup.c @@ -41,6 +41,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
/** @@ -72,6 +75,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; @@ -399,6 +414,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), };
/**
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.
v2: hdcp files are moved to drivers/misc/mei/hdcp/ [Tomas]
Signed-off-by: Ramalingam C ramalingam.c@intel.com Signed-off-by: Tomas Winkler tomas.winkler@intel.com --- drivers/misc/mei/hdcp/mei_hdcp.c | 80 ++++++++++++++++++++++++++++++++++++++++ drivers/misc/mei/hdcp/mei_hdcp.h | 32 ++++++++++++++++ 2 files changed, 112 insertions(+) create mode 100644 drivers/misc/mei/hdcp/mei_hdcp.c create mode 100644 drivers/misc/mei/hdcp/mei_hdcp.h
diff --git a/drivers/misc/mei/hdcp/mei_hdcp.c b/drivers/misc/mei/hdcp/mei_hdcp.c new file mode 100644 index 000000000000..aa211763e520 --- /dev/null +++ b/drivers/misc/mei/hdcp/mei_hdcp.c @@ -0,0 +1,80 @@ +/* + * Copyright © 2017 Intel Corporation + * + * Mei_hdcp.c: client driver for mei bus driver + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Authors: + * Ramalingam C ramalingam.c@intel.com + */ + +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/uuid.h> + +#include "mei_hdcp.h" + +struct mei_hdcp mei_hdcp; + +static int mei_hdcp_probe(struct mei_cl_device *cldev, + const struct mei_cl_device_id *id) +{ + int ret; + + mei_hdcp.cldev = cldev; + mei_cldev_set_drvdata(cldev, &mei_hdcp); + + 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) +{ + mei_cldev_disable(cldev); + return 0; +} + +#define WIDI_HECI_CLIENT_GUID UUID_LE(0xB638AB7E, 0x94E2, 0x4EA2, 0xA5, \ + 0x52, 0xD1, 0xC5, 0x4B, \ + 0x62, 0x7F, 0x04) +#define MEI_HDCP2_2_UUID WIDI_HECI_CLIENT_GUID + +static struct mei_cl_device_id mei_hdcp_tbl[] = { + { .uuid = MEI_HDCP2_2_UUID, .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("GPL"); +MODULE_DESCRIPTION("HDCP"); diff --git a/drivers/misc/mei/hdcp/mei_hdcp.h b/drivers/misc/mei/hdcp/mei_hdcp.h new file mode 100644 index 000000000000..c06c0d767c4f --- /dev/null +++ b/drivers/misc/mei/hdcp/mei_hdcp.h @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2017 Intel Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ +#ifndef __MEI_HDCP_H__ +#define __MEI_HDCP_H__ + +#include <linux/mei_cl_bus.h> + +struct mei_hdcp { + struct mei_cl_device *cldev; +}; + +#endif /* __MEI_HDCP_H__ */
Subject: [PATCH v2 04/42] misc/mei/hdcp: Client driver for HDCP application
I prefer this will be squashed together with [PATCH v2 05/42] misc/mei/hdcp: Add KBuild for mei hdcp driver so it can be compiled.
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.
v2: hdcp files are moved to drivers/misc/mei/hdcp/ [Tomas]
Signed-off-by: Ramalingam C ramalingam.c@intel.com Signed-off-by: Tomas Winkler tomas.winkler@intel.com
drivers/misc/mei/hdcp/mei_hdcp.c | 80 ++++++++++++++++++++++++++++++++++++++++ drivers/misc/mei/hdcp/mei_hdcp.h | 32 ++++++++++++++++ 2 files changed, 112 insertions(+) create mode 100644 drivers/misc/mei/hdcp/mei_hdcp.c create mode 100644 drivers/misc/mei/hdcp/mei_hdcp.h
diff --git a/drivers/misc/mei/hdcp/mei_hdcp.c b/drivers/misc/mei/hdcp/mei_hdcp.c new file mode 100644 index 000000000000..aa211763e520 --- /dev/null +++ b/drivers/misc/mei/hdcp/mei_hdcp.c
@@ -0,0 +1,80 @@ +/*
- Copyright © 2017 Intel Corporation
Please use SPDX notation, for HECI devices we force dual license
/* SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) */ /* * Copyright © 2017-2018 Intel Corporation */
- Mei_hdcp.c: client driver for mei bus driver
- Permission is hereby granted, free of charge, to any person
+obtaining a
- copy of this software and associated documentation files (the
+"Software"),
- to deal in the Software without restriction, including without
+limitation
- the rights to use, copy, modify, merge, publish, distribute,
+sublicense,
- and/or sell copies of the Software, and to permit persons to whom
+the
- Software is furnished to do so, subject to the following conditions:
- The above copyright notice and this permission notice (including the
+next
- paragraph) shall be included in all copies or substantial portions
+of the
- Software.
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY,
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO
EVENT +SHALL
- THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
DAMAGES OR +OTHER
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ARISING
- FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER
- DEALINGS IN THE SOFTWARE.
- Authors:
- Ramalingam C ramalingam.c@intel.com
- */
+#include <linux/module.h> +#include <linux/slab.h> +#include <linux/uuid.h>
+#include "mei_hdcp.h"
+struct mei_hdcp mei_hdcp;
+static int mei_hdcp_probe(struct mei_cl_device *cldev,
const struct mei_cl_device_id *id) {
- int ret;
- mei_hdcp.cldev = cldev;
- mei_cldev_set_drvdata(cldev, &mei_hdcp);
- 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) {
mei_cldev_set_drvdata(cldev, NULL);
- mei_cldev_disable(cldev);
return mei_cldev_disable(cldev);
- return 0;
+}
+#define WIDI_HECI_CLIENT_GUID UUID_LE(0xB638AB7E, 0x94E2, 0x4EA2, 0xA5, \
0x52, 0xD1, 0xC5, 0x4B, \
0x62, 0x7F, 0x04)
+#define MEI_HDCP2_2_UUID WIDI_HECI_CLIENT_GUID
Please use the same string as defined in whitelist patch
+static struct mei_cl_device_id mei_hdcp_tbl[] = {
- { .uuid = MEI_HDCP2_2_UUID, .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("GPL");
Dual License
+MODULE_DESCRIPTION("HDCP"); diff --git a/drivers/misc/mei/hdcp/mei_hdcp.h b/drivers/misc/mei/hdcp/mei_hdcp.h new file mode 100644 index 000000000000..c06c0d767c4f --- /dev/null +++ b/drivers/misc/mei/hdcp/mei_hdcp.h @@ -0,0 +1,32 @@ +/*
- Copyright (c) 2017 Intel Corporation
Please use SPDX notation, for HECI devices we force dual license
/* SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) */ /* * Copyright © 2017-2018 Intel Corporation
- Permission is hereby granted, free of charge, to any person
+obtaining a
- copy of this software and associated documentation files (the
+"Software"),
- to deal in the Software without restriction, including without
+limitation
- the rights to use, copy, modify, merge, publish, distribute,
+sublicense,
- and/or sell copies of the Software, and to permit persons to whom
+the
- Software is furnished to do so, subject to the following conditions:
- The above copyright notice and this permission notice (including the
+next
- paragraph) shall be included in all copies or substantial portions
+of the
- Software.
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY,
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO
EVENT +SHALL
- THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
DAMAGES OR +OTHER
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ARISING
- FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER +DEALINGS
- IN THE SOFTWARE.
- */
+#ifndef __MEI_HDCP_H__ +#define __MEI_HDCP_H__
+#include <linux/mei_cl_bus.h>
+struct mei_hdcp {
mei_hdcp_device - this structure represent a device
- struct mei_cl_device *cldev;
+};
+#endif /* __MEI_HDCP_H__ */
2.7.4
Thanks for review Tomas.
On Thursday 08 March 2018 06:37 PM, Winkler, Tomas wrote:
Subject: [PATCH v2 04/42] misc/mei/hdcp: Client driver for HDCP application
I prefer this will be squashed together with [PATCH v2 05/42] misc/mei/hdcp: Add KBuild for mei hdcp driver so it can be compiled.
Ok, I will squash.
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.
v2: hdcp files are moved to drivers/misc/mei/hdcp/ [Tomas]
Signed-off-by: Ramalingam C ramalingam.c@intel.com Signed-off-by: Tomas Winkler tomas.winkler@intel.com
drivers/misc/mei/hdcp/mei_hdcp.c | 80 ++++++++++++++++++++++++++++++++++++++++ drivers/misc/mei/hdcp/mei_hdcp.h | 32 ++++++++++++++++ 2 files changed, 112 insertions(+) create mode 100644 drivers/misc/mei/hdcp/mei_hdcp.c create mode 100644 drivers/misc/mei/hdcp/mei_hdcp.h
diff --git a/drivers/misc/mei/hdcp/mei_hdcp.c b/drivers/misc/mei/hdcp/mei_hdcp.c new file mode 100644 index 000000000000..aa211763e520 --- /dev/null +++ b/drivers/misc/mei/hdcp/mei_hdcp.c @@ -0,0 +1,80 @@ +/*
- Copyright © 2017 Intel Corporation
Please use SPDX notation, for HECI devices we force dual license
/* SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) */ /*
- Copyright © 2017-2018 Intel Corporation
*/
Sure
- Mei_hdcp.c: client driver for mei bus driver
- Permission is hereby granted, free of charge, to any person
+obtaining a
- copy of this software and associated documentation files (the
+"Software"),
- to deal in the Software without restriction, including without
+limitation
- the rights to use, copy, modify, merge, publish, distribute,
+sublicense,
- and/or sell copies of the Software, and to permit persons to whom
+the
- Software is furnished to do so, subject to the following conditions:
- The above copyright notice and this permission notice (including the
+next
- paragraph) shall be included in all copies or substantial portions
+of the
- Software.
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY,
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO
EVENT +SHALL
- THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
DAMAGES OR +OTHER
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ARISING
- FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER
- DEALINGS IN THE SOFTWARE.
- Authors:
- Ramalingam C ramalingam.c@intel.com
- */
+#include <linux/module.h> +#include <linux/slab.h> +#include <linux/uuid.h>
+#include "mei_hdcp.h"
+struct mei_hdcp mei_hdcp;
+static int mei_hdcp_probe(struct mei_cl_device *cldev,
const struct mei_cl_device_id *id) {
- int ret;
- mei_hdcp.cldev = cldev;
- mei_cldev_set_drvdata(cldev, &mei_hdcp);
- 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) {
mei_cldev_set_drvdata(cldev, NULL);
ok
- mei_cldev_disable(cldev);
return mei_cldev_disable(cldev);
- return 0;
+}
+#define WIDI_HECI_CLIENT_GUID UUID_LE(0xB638AB7E, 0x94E2, 0x4EA2, 0xA5, \
0x52, 0xD1, 0xC5, 0x4B, \
0x62, 0x7F, 0x04)
+#define MEI_HDCP2_2_UUID WIDI_HECI_CLIENT_GUID
Please use the same string as defined in whitelist patch
+static struct mei_cl_device_id mei_hdcp_tbl[] = {
- { .uuid = MEI_HDCP2_2_UUID, .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("GPL");
Dual License
MODULE_LICENSE("GPL-2.0+ OR BSD-3-Clause");
Will this work?
+MODULE_DESCRIPTION("HDCP"); diff --git a/drivers/misc/mei/hdcp/mei_hdcp.h b/drivers/misc/mei/hdcp/mei_hdcp.h new file mode 100644 index 000000000000..c06c0d767c4f --- /dev/null +++ b/drivers/misc/mei/hdcp/mei_hdcp.h @@ -0,0 +1,32 @@ +/*
- Copyright (c) 2017 Intel Corporation
Please use SPDX notation, for HECI devices we force dual license
/* SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) */ /*
- Copyright © 2017-2018 Intel Corporation
- Permission is hereby granted, free of charge, to any person
+obtaining a
- copy of this software and associated documentation files (the
+"Software"),
- to deal in the Software without restriction, including without
+limitation
- the rights to use, copy, modify, merge, publish, distribute,
+sublicense,
- and/or sell copies of the Software, and to permit persons to whom
+the
- Software is furnished to do so, subject to the following conditions:
- The above copyright notice and this permission notice (including the
+next
- paragraph) shall be included in all copies or substantial portions
+of the
- Software.
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY,
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO
EVENT +SHALL
- THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
DAMAGES OR +OTHER
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ARISING
- FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER +DEALINGS
- IN THE SOFTWARE.
- */
+#ifndef __MEI_HDCP_H__ +#define __MEI_HDCP_H__
+#include <linux/mei_cl_bus.h>
+struct mei_hdcp {
mei_hdcp_device - this structure represent a device
will rename it.
--Ram
- struct mei_cl_device *cldev;
+};
+#endif /* __MEI_HDCP_H__ */
2.7.4
On Monday 12 March 2018 04:52 PM, Winkler, Tomas wrote:
+MODULE_AUTHOR("Intel Corporation"); +MODULE_LICENSE("GPL");
Dual License
MODULE_LICENSE("GPL-2.0+ OR BSD-3-Clause");
Will this work?
No, it should be MODULE_LICENSE("Dual BSD/GPL");
Ok thank you!
--Ram
Tomas
v2: including the hdcp folder with a Makefile. [Tomas]
Signed-off-by: Ramalingam C ramalingam.c@intel.com --- drivers/misc/mei/Kconfig | 6 ++++++ drivers/misc/mei/Makefile | 2 ++ drivers/misc/mei/hdcp/Makefile | 6 ++++++ 3 files changed, 14 insertions(+) create mode 100644 drivers/misc/mei/hdcp/Makefile
diff --git a/drivers/misc/mei/Kconfig b/drivers/misc/mei/Kconfig index c49e1d2269af..90977132d1e2 100644 --- a/drivers/misc/mei/Kconfig +++ b/drivers/misc/mei/Kconfig @@ -43,3 +43,9 @@ config INTEL_MEI_TXE
Supported SoCs: Intel Bay Trail + +config INTEL_MEI_HDCP + tristate "Intel HDCP2.2 services of ME Interface" + depends on INTEL_MEI && 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..75ac50203223 --- /dev/null +++ b/drivers/misc/mei/hdcp/Makefile @@ -0,0 +1,6 @@ +# SPDX-License-Identifier: GPL-2.0 +# +# Makefile - HDCP client driver for Intel MEI Bus Driver. +# Copyright (c) 2010-2014, Intel Corporation. +# +obj-$(CONFIG_INTEL_MEI_HDCP) += mei_hdcp.o
Checks whether mei client device for hdcp2.2 is enabled?
v2: Rebased.
Signed-off-by: Ramalingam C ramalingam.c@intel.com --- drivers/misc/mei/hdcp/mei_hdcp.c | 12 ++++++++++++ 1 file changed, 12 insertions(+)
diff --git a/drivers/misc/mei/hdcp/mei_hdcp.c b/drivers/misc/mei/hdcp/mei_hdcp.c index aa211763e520..25df7034cfb4 100644 --- a/drivers/misc/mei/hdcp/mei_hdcp.c +++ b/drivers/misc/mei/hdcp/mei_hdcp.c @@ -34,6 +34,18 @@
struct mei_hdcp mei_hdcp;
+/** + * mei_cldev_active_and_enabled: + * Return: true if me client for HDCP is initialized and connected + */ +static inline bool mei_cldev_active_and_enabled(struct mei_cl_device *cldev) +{ + if (!cldev) + return false; + + return mei_cldev_enabled(cldev); +} + static int mei_hdcp_probe(struct mei_cl_device *cldev, const struct mei_cl_device_id *id) {
v2: Rebased.
Signed-off-by: Ramalingam C ramalingam.c@intel.com
drivers/misc/mei/hdcp/mei_hdcp.c | 12 ++++++++++++ 1 file changed, 12 insertions(+)
diff --git a/drivers/misc/mei/hdcp/mei_hdcp.c b/drivers/misc/mei/hdcp/mei_hdcp.c index aa211763e520..25df7034cfb4 100644 --- a/drivers/misc/mei/hdcp/mei_hdcp.c +++ b/drivers/misc/mei/hdcp/mei_hdcp.c @@ -34,6 +34,18 @@
struct mei_hdcp mei_hdcp;
+/**
- mei_cldev_active_and_enabled:
- Return: true if me client for HDCP is initialized and connected
- */
+static inline bool mei_cldev_active_and_enabled(struct mei_cl_device +*cldev) {
- if (!cldev)
return false;
- return mei_cldev_enabled(cldev);
+}
I think this is a useless wrapper, if you needed it there is something wrong with your code.
Please drop this patch, if needed open code it.
Tomas
On Thursday 08 March 2018 06:38 PM, Winkler, Tomas wrote:
v2: Rebased.
Signed-off-by: Ramalingam C ramalingam.c@intel.com
drivers/misc/mei/hdcp/mei_hdcp.c | 12 ++++++++++++ 1 file changed, 12 insertions(+)
diff --git a/drivers/misc/mei/hdcp/mei_hdcp.c b/drivers/misc/mei/hdcp/mei_hdcp.c index aa211763e520..25df7034cfb4 100644 --- a/drivers/misc/mei/hdcp/mei_hdcp.c +++ b/drivers/misc/mei/hdcp/mei_hdcp.c @@ -34,6 +34,18 @@
struct mei_hdcp mei_hdcp;
+/**
- mei_cldev_active_and_enabled:
- Return: true if me client for HDCP is initialized and connected
- */
+static inline bool mei_cldev_active_and_enabled(struct mei_cl_device +*cldev) {
- if (!cldev)
return false;
- return mei_cldev_enabled(cldev);
+}
I think this is a useless wrapper, if you needed it there is something wrong with your code.
Please drop this patch, if needed open code it.
Ok. Was validating the handle and device status before forwarding any request from the I915 to MEI Bus. As never hit this scenario till now, will drop this at present if needed will bring it back as open code. Thanks.
--Ram
Tomas
Interfaces to obtain and release the cl_device reference is developed. Using these interfaces intel hdcp driver will get the reference to the mei client devices, so that hdcp2.2 service calls can be routed to that client device.
During registration, call back function will be registered with mei_hdcp driver so that when the client device is removed intel hdcp driver can be informed.
At a time only one reference is allowed in this interfaces.
v2: Rebased.
Signed-off-by: Ramalingam C ramalingam.c@intel.com --- drivers/misc/mei/hdcp/mei_hdcp.c | 55 +++++++++++++++++++++++++++++++++++++++- drivers/misc/mei/hdcp/mei_hdcp.h | 9 +++++++ include/linux/mei_hdcp.h | 47 ++++++++++++++++++++++++++++++++++ 3 files changed, 110 insertions(+), 1 deletion(-) create mode 100644 include/linux/mei_hdcp.h
diff --git a/drivers/misc/mei/hdcp/mei_hdcp.c b/drivers/misc/mei/hdcp/mei_hdcp.c index 25df7034cfb4..63f77800a6f7 100644 --- a/drivers/misc/mei/hdcp/mei_hdcp.c +++ b/drivers/misc/mei/hdcp/mei_hdcp.c @@ -55,18 +55,71 @@ static int mei_hdcp_probe(struct mei_cl_device *cldev, mei_cldev_set_drvdata(cldev, &mei_hdcp);
ret = mei_cldev_enable(cldev); - if (ret < 0) + if (ret < 0) { dev_err(&cldev->dev, "mei_cldev_enable Failed. %d\n", ret); + goto err; + } + + if (mei_hdcp.notify_on_cldev_change) + mei_hdcp.notify_on_cldev_change(mei_hdcp.client, cldev); + + return 0; +err: + if (mei_hdcp.notify_on_cldev_change) + mei_hdcp.notify_on_cldev_change(mei_hdcp.client, NULL);
return ret; }
static int mei_hdcp_remove(struct mei_cl_device *cldev) { + struct mei_hdcp *mei_hdcp = mei_cldev_get_drvdata(cldev); + + if (mei_hdcp->notify_on_cldev_change) + mei_hdcp->notify_on_cldev_change(mei_hdcp->client, NULL); + mei_cldev_disable(cldev); + return 0; }
+int mei_hdcp_cldev_get_reference(void *client_data, + struct mei_cl_device **cldev, + void (*notify_change)(void *client, + struct mei_cl_device + *cldev)) +{ + if (!notify_change || !client_data) + return -EINVAL; + + if (mei_hdcp.ref_cnt) + return -EBUSY; + + if (!mei_cldev_active_and_enabled(mei_hdcp.cldev)) { + if (!notify_change) + return -EAGAIN; + } else { + *cldev = mei_hdcp.cldev; + } + + mei_hdcp.ref_cnt++; + mei_hdcp.client = client_data; + mei_hdcp.notify_on_cldev_change = notify_change; + + return 0; +} +EXPORT_SYMBOL(mei_hdcp_cldev_get_reference); + +void mei_hdcp_cldev_put_reference(struct mei_cl_device *cldev) +{ + if (cldev == mei_hdcp.cldev) { + mei_hdcp.ref_cnt--; + mei_hdcp.client = NULL; + mei_hdcp.notify_on_cldev_change = NULL; + } +} +EXPORT_SYMBOL(mei_hdcp_cldev_put_reference); + #define WIDI_HECI_CLIENT_GUID UUID_LE(0xB638AB7E, 0x94E2, 0x4EA2, 0xA5, \ 0x52, 0xD1, 0xC5, 0x4B, \ 0x62, 0x7F, 0x04) diff --git a/drivers/misc/mei/hdcp/mei_hdcp.h b/drivers/misc/mei/hdcp/mei_hdcp.h index c06c0d767c4f..7d792b5ad703 100644 --- a/drivers/misc/mei/hdcp/mei_hdcp.h +++ b/drivers/misc/mei/hdcp/mei_hdcp.h @@ -27,6 +27,15 @@
struct mei_hdcp { struct mei_cl_device *cldev; + + /* Reference to the HDCP2.2 service consumer */ + void *client; + + /* Callback function for the consumer on cl_device state change */ + void (*notify_on_cldev_change)(void *client, + struct mei_cl_device *cldev); + + int ref_cnt; };
#endif /* __MEI_HDCP_H__ */ diff --git a/include/linux/mei_hdcp.h b/include/linux/mei_hdcp.h new file mode 100644 index 000000000000..774b26da0c26 --- /dev/null +++ b/include/linux/mei_hdcp.h @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2017 Intel Corporation + * + * Permission to use, copy, modify, distribute, and sell this software and its + * documentation for any purpose is hereby granted without fee, provided that + * the above copyright notice appear in all copies and that both that copyright + * notice and this permission notice appear in supporting documentation, and + * that the name of the copyright holders not be used in advertising or + * publicity pertaining to distribution of the software without specific, + * written prior permission. The copyright holders make no representations + * about the suitability of this software for any purpose. It is provided "as + * is" without express or implied warranty. + * + * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, + * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE + * OF THIS SOFTWARE. + */ + +#ifndef _LINUX_MEI_HDCP_H +#define _LINUX_MEI_HDCP_H + +#ifdef CONFIG_INTEL_MEI_HDCP +int mei_hdcp_cldev_get_reference(void *client_data, + struct mei_cl_device **cldev, + void (*notify_change)(void *client, + struct mei_cl_device + *cldev)); +void mei_hdcp_cldev_put_reference(struct mei_cl_device *cldev); +#else +static inline +int mei_hdcp_cldev_get_reference(void *client_data, + struct mei_cl_device **cldev, + void (*notify_change)(void *client, + struct mei_cl_device + *cldev)) +{ + return -ENODEV; +} +static inline +void mei_hdcp_cldev_put_reference(struct mei_cl_device *cldev) +{} +#endif /* defined (CONFIG_INTEL_MEI_HDCP) */ +#endif /* defined (_LINUX_MEI_HDCP_H) */
Interfaces to obtain and release the cl_device reference is developed. Using these interfaces intel hdcp driver will get the reference to the mei client devices, so that hdcp2.2 service calls can be routed to that client device.
During registration, call back function will be registered with mei_hdcp driver so that when the client device is removed intel hdcp driver can be informed.
At a time only one reference is allowed in this interfaces.
v2: Rebased.
Linux kernel already provide notification chain, please use that. This is not needed and I'm not sure it will ever work.
Tomas
Signed-off-by: Ramalingam C ramalingam.c@intel.com
drivers/misc/mei/hdcp/mei_hdcp.c | 55 +++++++++++++++++++++++++++++++++++++++- drivers/misc/mei/hdcp/mei_hdcp.h | 9 +++++++ include/linux/mei_hdcp.h | 47 ++++++++++++++++++++++++++++++++++ 3 files changed, 110 insertions(+), 1 deletion(-) create mode 100644 include/linux/mei_hdcp.h
diff --git a/drivers/misc/mei/hdcp/mei_hdcp.c b/drivers/misc/mei/hdcp/mei_hdcp.c index 25df7034cfb4..63f77800a6f7 100644 --- a/drivers/misc/mei/hdcp/mei_hdcp.c +++ b/drivers/misc/mei/hdcp/mei_hdcp.c @@ -55,18 +55,71 @@ static int mei_hdcp_probe(struct mei_cl_device *cldev, mei_cldev_set_drvdata(cldev, &mei_hdcp);
ret = mei_cldev_enable(cldev);
- if (ret < 0)
- if (ret < 0) { dev_err(&cldev->dev, "mei_cldev_enable Failed. %d\n", ret);
goto err;
- }
- if (mei_hdcp.notify_on_cldev_change)
mei_hdcp.notify_on_cldev_change(mei_hdcp.client, cldev);
- return 0;
+err:
if (mei_hdcp.notify_on_cldev_change)
mei_hdcp.notify_on_cldev_change(mei_hdcp.client, NULL);
return ret;
}
static int mei_hdcp_remove(struct mei_cl_device *cldev) {
- struct mei_hdcp *mei_hdcp = mei_cldev_get_drvdata(cldev);
- if (mei_hdcp->notify_on_cldev_change)
mei_hdcp->notify_on_cldev_change(mei_hdcp->client, NULL);
- mei_cldev_disable(cldev);
- return 0;
}
+int mei_hdcp_cldev_get_reference(void *client_data,
struct mei_cl_device **cldev,
void (*notify_change)(void *client,
struct mei_cl_device
*cldev))
+{
- if (!notify_change || !client_data)
return -EINVAL;
- if (mei_hdcp.ref_cnt)
return -EBUSY;
- if (!mei_cldev_active_and_enabled(mei_hdcp.cldev)) {
if (!notify_change)
return -EAGAIN;
- } else {
*cldev = mei_hdcp.cldev;
- }
- mei_hdcp.ref_cnt++;
- mei_hdcp.client = client_data;
- mei_hdcp.notify_on_cldev_change = notify_change;
- return 0;
+} +EXPORT_SYMBOL(mei_hdcp_cldev_get_reference);
+void mei_hdcp_cldev_put_reference(struct mei_cl_device *cldev) {
- if (cldev == mei_hdcp.cldev) {
mei_hdcp.ref_cnt--;
mei_hdcp.client = NULL;
mei_hdcp.notify_on_cldev_change = NULL;
- }
+} +EXPORT_SYMBOL(mei_hdcp_cldev_put_reference);
#define WIDI_HECI_CLIENT_GUID UUID_LE(0xB638AB7E, 0x94E2, 0x4EA2, 0xA5, \ 0x52, 0xD1, 0xC5, 0x4B, \ 0x62, 0x7F, 0x04) diff --git a/drivers/misc/mei/hdcp/mei_hdcp.h b/drivers/misc/mei/hdcp/mei_hdcp.h index c06c0d767c4f..7d792b5ad703 100644 --- a/drivers/misc/mei/hdcp/mei_hdcp.h +++ b/drivers/misc/mei/hdcp/mei_hdcp.h @@ -27,6 +27,15 @@
struct mei_hdcp { struct mei_cl_device *cldev;
- /* Reference to the HDCP2.2 service consumer */
- void *client;
- /* Callback function for the consumer on cl_device state change */
- void (*notify_on_cldev_change)(void *client,
struct mei_cl_device *cldev);
- int ref_cnt;
};
#endif /* __MEI_HDCP_H__ */ diff --git a/include/linux/mei_hdcp.h b/include/linux/mei_hdcp.h new file mode 100644 index 000000000000..774b26da0c26 --- /dev/null +++ b/include/linux/mei_hdcp.h @@ -0,0 +1,47 @@ +/*
- Copyright (c) 2017 Intel Corporation
- Permission to use, copy, modify, distribute, and sell this software
+and its
- documentation for any purpose is hereby granted without fee,
+provided that
- the above copyright notice appear in all copies and that both that
+copyright
- notice and this permission notice appear in supporting
+documentation, and
- that the name of the copyright holders not be used in advertising or
- publicity pertaining to distribution of the software without
+specific,
- written prior permission. The copyright holders make no
+representations
- about the suitability of this software for any purpose. It is
+provided "as
- is" without express or implied warranty.
- THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO
THIS +SOFTWARE,
- INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS, IN +NO
- EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL,
+INDIRECT OR
- CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING
FROM LOSS +OF USE,
- DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
OR +OTHER
- TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE
OR +PERFORMANCE
- OF THIS SOFTWARE.
- */
+#ifndef _LINUX_MEI_HDCP_H +#define _LINUX_MEI_HDCP_H
+#ifdef CONFIG_INTEL_MEI_HDCP +int mei_hdcp_cldev_get_reference(void *client_data,
struct mei_cl_device **cldev,
void (*notify_change)(void *client,
struct mei_cl_device
*cldev));
+void mei_hdcp_cldev_put_reference(struct mei_cl_device *cldev); #else +static inline int mei_hdcp_cldev_get_reference(void *client_data,
struct mei_cl_device **cldev,
void (*notify_change)(void *client,
struct mei_cl_device
*cldev))
+{
- return -ENODEV;
+} +static inline +void mei_hdcp_cldev_put_reference(struct mei_cl_device *cldev) {} +#endif /* defined (CONFIG_INTEL_MEI_HDCP) */ #endif /* defined
+(_LINUX_MEI_HDCP_H) */
2.7.4
On Thursday 08 March 2018 06:40 PM, Winkler, Tomas wrote:
Interfaces to obtain and release the cl_device reference is developed. Using these interfaces intel hdcp driver will get the reference to the mei client devices, so that hdcp2.2 service calls can be routed to that client device.
During registration, call back function will be registered with mei_hdcp driver so that when the client device is removed intel hdcp driver can be informed.
At a time only one reference is allowed in this interfaces.
v2: Rebased.
Linux kernel already provide notification chain, please use that.
Sensing a problem here. Publisher module (mei_hdcp) for the notifier chain is loaded later than the I915's hdcp init (subscriber to the module). Events that we will be interested are enabled and disabled states of the mei_hdcp_device.
I will explore further. Thanks for the suggestion
This is not needed and I'm not sure it will ever work.
I am not sure, why do you think like that. This might not be in good shape but this is completely functional. This v2 series is tested for HDCP2.2 feature on drm-tip before publishing.
From starting I was not comfortable with this handshaking part between I915 and mei_hdcp. Thanks for the suggestions. I will work on them.
--Ram
Tomas
Signed-off-by: Ramalingam C ramalingam.c@intel.com
drivers/misc/mei/hdcp/mei_hdcp.c | 55 +++++++++++++++++++++++++++++++++++++++- drivers/misc/mei/hdcp/mei_hdcp.h | 9 +++++++ include/linux/mei_hdcp.h | 47 ++++++++++++++++++++++++++++++++++ 3 files changed, 110 insertions(+), 1 deletion(-) create mode 100644 include/linux/mei_hdcp.h
diff --git a/drivers/misc/mei/hdcp/mei_hdcp.c b/drivers/misc/mei/hdcp/mei_hdcp.c index 25df7034cfb4..63f77800a6f7 100644 --- a/drivers/misc/mei/hdcp/mei_hdcp.c +++ b/drivers/misc/mei/hdcp/mei_hdcp.c @@ -55,18 +55,71 @@ static int mei_hdcp_probe(struct mei_cl_device *cldev, mei_cldev_set_drvdata(cldev, &mei_hdcp);
ret = mei_cldev_enable(cldev);
- if (ret < 0)
- if (ret < 0) { dev_err(&cldev->dev, "mei_cldev_enable Failed. %d\n", ret);
goto err;
- }
- if (mei_hdcp.notify_on_cldev_change)
mei_hdcp.notify_on_cldev_change(mei_hdcp.client, cldev);
- return 0;
+err:
if (mei_hdcp.notify_on_cldev_change)
mei_hdcp.notify_on_cldev_change(mei_hdcp.client, NULL);
return ret; }
static int mei_hdcp_remove(struct mei_cl_device *cldev) {
struct mei_hdcp *mei_hdcp = mei_cldev_get_drvdata(cldev);
if (mei_hdcp->notify_on_cldev_change)
mei_hdcp->notify_on_cldev_change(mei_hdcp->client, NULL);
mei_cldev_disable(cldev);
return 0; }
+int mei_hdcp_cldev_get_reference(void *client_data,
struct mei_cl_device **cldev,
void (*notify_change)(void *client,
struct mei_cl_device
*cldev))
+{
- if (!notify_change || !client_data)
return -EINVAL;
- if (mei_hdcp.ref_cnt)
return -EBUSY;
- if (!mei_cldev_active_and_enabled(mei_hdcp.cldev)) {
if (!notify_change)
return -EAGAIN;
- } else {
*cldev = mei_hdcp.cldev;
- }
- mei_hdcp.ref_cnt++;
- mei_hdcp.client = client_data;
- mei_hdcp.notify_on_cldev_change = notify_change;
- return 0;
+} +EXPORT_SYMBOL(mei_hdcp_cldev_get_reference);
+void mei_hdcp_cldev_put_reference(struct mei_cl_device *cldev) {
- if (cldev == mei_hdcp.cldev) {
mei_hdcp.ref_cnt--;
mei_hdcp.client = NULL;
mei_hdcp.notify_on_cldev_change = NULL;
- }
+} +EXPORT_SYMBOL(mei_hdcp_cldev_put_reference);
- #define WIDI_HECI_CLIENT_GUID UUID_LE(0xB638AB7E, 0x94E2,
0x4EA2, 0xA5, \ 0x52, 0xD1, 0xC5, 0x4B, \ 0x62, 0x7F, 0x04) diff --git a/drivers/misc/mei/hdcp/mei_hdcp.h b/drivers/misc/mei/hdcp/mei_hdcp.h index c06c0d767c4f..7d792b5ad703 100644 --- a/drivers/misc/mei/hdcp/mei_hdcp.h +++ b/drivers/misc/mei/hdcp/mei_hdcp.h @@ -27,6 +27,15 @@
struct mei_hdcp { struct mei_cl_device *cldev;
/* Reference to the HDCP2.2 service consumer */
void *client;
/* Callback function for the consumer on cl_device state change */
void (*notify_on_cldev_change)(void *client,
struct mei_cl_device *cldev);
int ref_cnt; };
#endif /* __MEI_HDCP_H__ */
diff --git a/include/linux/mei_hdcp.h b/include/linux/mei_hdcp.h new file mode 100644 index 000000000000..774b26da0c26 --- /dev/null +++ b/include/linux/mei_hdcp.h @@ -0,0 +1,47 @@ +/*
- Copyright (c) 2017 Intel Corporation
- Permission to use, copy, modify, distribute, and sell this software
+and its
- documentation for any purpose is hereby granted without fee,
+provided that
- the above copyright notice appear in all copies and that both that
+copyright
- notice and this permission notice appear in supporting
+documentation, and
- that the name of the copyright holders not be used in advertising or
- publicity pertaining to distribution of the software without
+specific,
- written prior permission. The copyright holders make no
+representations
- about the suitability of this software for any purpose. It is
+provided "as
- is" without express or implied warranty.
- THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO
THIS +SOFTWARE,
- INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS, IN +NO
- EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL,
+INDIRECT OR
- CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING
FROM LOSS +OF USE,
- DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
OR +OTHER
- TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE
OR +PERFORMANCE
- OF THIS SOFTWARE.
- */
+#ifndef _LINUX_MEI_HDCP_H +#define _LINUX_MEI_HDCP_H
+#ifdef CONFIG_INTEL_MEI_HDCP +int mei_hdcp_cldev_get_reference(void *client_data,
struct mei_cl_device **cldev,
void (*notify_change)(void *client,
struct mei_cl_device
*cldev));
+void mei_hdcp_cldev_put_reference(struct mei_cl_device *cldev); #else +static inline int mei_hdcp_cldev_get_reference(void *client_data,
struct mei_cl_device **cldev,
void (*notify_change)(void *client,
struct mei_cl_device
*cldev))
+{
- return -ENODEV;
+} +static inline +void mei_hdcp_cldev_put_reference(struct mei_cl_device *cldev) {} +#endif /* defined (CONFIG_INTEL_MEI_HDCP) */ #endif /* defined
+(_LINUX_MEI_HDCP_H) */
2.7.4
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.
Signed-off-by: Ramalingam C ramalingam.c@intel.com --- drivers/misc/mei/hdcp/mei_hdcp.h | 525 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 525 insertions(+)
diff --git a/drivers/misc/mei/hdcp/mei_hdcp.h b/drivers/misc/mei/hdcp/mei_hdcp.h index 7d792b5ad703..32fb844554f5 100644 --- a/drivers/misc/mei/hdcp/mei_hdcp.h +++ b/drivers/misc/mei/hdcp/mei_hdcp.h @@ -24,6 +24,8 @@ #define __MEI_HDCP_H__
#include <linux/mei_cl_bus.h> +#include <linux/mei_hdcp.h> +#include <drm/drm_hdcp.h>
struct mei_hdcp { struct mei_cl_device *cldev; @@ -38,4 +40,527 @@ struct mei_hdcp { int ref_cnt; };
+/** + * @enum 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 each CMD 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) + + +/** + * @enum 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 { + uint8_t e_kpub_km[HDCP_2_2_E_KPUB_KM_LEN]; + uint8_t e_kh_km_m[HDCP_2_2_E_KH_KM_M_LEN]; + struct { + uint8_t e_kh_km[HDCP_KH_LEN]; + uint8_t m[HDCP_M_LEN]; + } __packed; +}; + +/** + * @brief HDCP HECI message header. + * @note All header values are little endian. + */ +struct hdcp_cmd_header { + uint32_t api_version; + uint32_t command_id; + enum me_hdcp_status status; + /* Length of the HECI message (excluding the header) */ + uint32_t buffer_len; +} __packed; + +/* @brief Empty command request or response. No data follows the header. */ +struct hdcp_cmd_no_data { + struct hdcp_cmd_header header; +} __packed; + +/* + * @brief Uniquely identifies the hdcp port being + * addressed for a given command. + */ +struct hdcp_port_id { + uint8_t integrated_port_type; + uint8_t physical_port; + uint16_t reserved; +} __packed; + + +/** -------------------------------------------------------------- + * Data structures for integrated wired HDCP2 Tx in + * support of the AKE protocol + */ + +/** + * HECI Integrated wired HDCP Tx session initiation command + * + * The command below is in support of Initiating an integrated + * wired HDCP Tx session. + * command ID: WIRED_INITIATE_HDCP2_SESSION + * + * Corresponds to the AKE_Init from the HDCP DP, HDMI specs. + * + * return: + * HDCP_STATUS_SUCCESS - Command completed correctly. + * HDCP_INVALID_SESSION_STATE - This command can not be called + * during an active session. + * A call to WIRED_CMD_CLOSE_SESSION_IN must be made to destroy + * an old HDCP Tx session. + */ + +/** + * HECI Input 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; + uint8_t protocol; /* for HDMI vs DP */ +} __packed; + +struct wired_cmd_initiate_hdcp2_session_out { + struct hdcp_cmd_header header; + struct hdcp_port_id port; + uint8_t r_tx[HDCP_2_2_RTX_LEN]; + struct hdcp2_tx_caps tx_caps; +} __packed; + +/** + * HECI Integrated wired HDCP Tx, close session command + * + * The command below is in support of ending an integrated + * wired HDCP Tx session. + * command ID: WIRED_CLOSE_SESSION + * + * return: + * HDCP_STATUS_SUCCESS - Command completed correctly. + */ + +/** + * 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 Integrated wired HDCP Tx verify receiver certificates command + * + * The command below is in support of verifying a Rx in an + * integrated wired HDCP Tx session. + * command ID: WIRED_VERIFY_RECEIVER_CERT + * + * Corresponds to the AKE_Send_Cert from the HDCP DP, HDMI specs. + * return: + * HDCP_STATUS_SUCCESS - Command completed correctly. + */ + +/** + * HECI Input struct for integrated wired HDCP Tx Rx verification. + */ +struct wired_cmd_verify_receiver_cert_in { + struct hdcp_cmd_header header; + struct hdcp_port_id port; + struct hdcp2_cert_rx cert_rx; + uint8_t r_rx[HDCP_2_2_RRX_LEN]; + uint8_t 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; + uint8_t km_stored; + uint8_t reserved[3]; + union encrypted_buff ekm_buff; +} __packed; + +/** + * HECI Integrated wired HDCP Tx, verify Rx's Hprime command + * + * The command below is in support of verifying an Hprime value + * generated by the Rx in an integrated wired HDCP Tx session. + * command ID: WIRED_AKE_SEND_HPRIME + * + * Corresponds to the AKE_Send_H_prime message from the HDCP DP, HDMI specs. + * + * return: + * HDCP_STATUS_SUCCESS - Command completed correctly. + */ + +/** + * 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; + uint8_t 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 Integrated wired HDCP Tx, send AKE pairing data command + * + * The command below is in support of sending in AKE pairing data generated + * by the Rx in an integrated wired HDCP Tx session. + * command ID: WIRED_AKE_SEND_PAIRING_INFO + * + * Corresponds to the AKE_Send_H_prime message from the HDCP DP, HDMI specs. + * + * return: + * HDCP_STATUS_SUCCESS - Command completed correctly. + */ + +/** + * 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; + uint8_t 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 Integrated wired HDCP Tx, initiate locality check + * + * The command below is in support of initiating locality check with an + * integrated wired HDCP Tx session. + * command ID: WIRED_INIT_LOCALITY_CHECK + * + * Corresponds to the LC_Init message from the HDCP DP, HDMI specs. + * + * return: HDCP_STATUS_SUCCESS - Command completed correctly. + */ + +/** + * 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; + uint8_t r_n[HDCP_2_2_RN_LEN]; +} __packed; + +/** + * HECI Integrated wired HDCP Tx, validate locality check + * + * The command below is in support of validating an Rx's LPrime value + * in an integrated wired HDCP Tx session. + * command ID: WIRED_VALIDATE_LOCALITY + * + * Corresponds to the LC_Send_L_prime message from the HDCP DP, HDMI specs. + * + * return: HDCP_STATUS_SUCCESS - Command completed correctly. + */ + +/** + * 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; + uint8_t 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 Integrated wired HDCP Tx, create session key command + * + * The command below is in support generating the session key + * in an integrated wired HDCP Tx session. + * command ID: WIRED_GET_SESSION_KEY + * + * Corresponds to the SKE_Send_Eks message from the HDCP DP, HDMI specs. + * + * return: HDCP_STATUS_SUCCESS - Command completed correctly. + */ + +/** + * HECI struct for create session key command + */ +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; + uint8_t e_dkey_ks[HDCP_2_2_E_DKEY_KS_LEN]; + uint8_t r_iv[HDCP_2_2_RIV_LEN]; +} __packed; + +/** + * HECI Integrated wired HDCP Tx, enable authentication command + * + * The command below is in support of assigning type 0, type 1 + * values to a non-repeater downstream Tx port and marking Authentication + * complete + * command ID: WIRED_ENABLE_AUTH + * + * return: HDCP_STATUS_SUCCESS - Command completed correctly. + */ + +/** + * HECI struct for the Tx enable authentication command + */ +struct wired_cmd_enable_auth_in { + struct hdcp_cmd_header header; + struct hdcp_port_id port; + uint8_t 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 Integrated wired HDCP Tx, verify downstream topology command + * + * The command below is in support verifying the downstream repeater's + * HDCP topology in an integrated wired HDCP Tx session. + * command ID: WIRED_VERIFY_REPEATER + * + * Corresponds to the RepeaterAuth_Send_ReceiverId_List message + * from the HDCP DP, HDMI specs. + * + * return: HDCP_STATUS_SUCCESS - Command completed correctly. + */ + +/** + * 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; + uint8_t rx_info[HDCP_2_2_RXINFO_LEN]; + uint8_t seq_num_v[HDCP_2_2_SEQ_NUM_LEN]; + uint8_t v_prime[HDCP_2_2_LPRIME_HALF_LEN]; + uint8_t 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; + uint8_t content_type_supported; + uint8_t v[HDCP_2_2_LPRIME_HALF_LEN]; +} __packed; + +/** + * HECI Integrated wired HDCP Tx, stream management command + * + * The command below is in support of stream management in an + * integrated wired HDCP Tx session. + * command ID: WIRED_REPEATER_AUTH_STREAM_REQ + * + * Corresponds to the RepeaterAuth_Stream_Ready message from + * the HDCP DP, HDMI specs. + * + * return: HDCP_STATUS_SUCCESS - Command completed correctly. + */ + +/** + * 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; + uint8_t seq_num_m[HDCP_2_2_SEQ_NUM_LEN]; + uint8_t m_prime[HDCP_2_2_MPRIME_LEN]; + uint16_t 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__ */
Data structures and Enum for the I915-MEI_HDCP interface are defined at <linux/mei_hdcp.h>
v2: Rebased.
Signed-off-by: Ramalingam C ramalingam.c@intel.com --- include/linux/mei_hdcp.h | 71 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 71 insertions(+)
diff --git a/include/linux/mei_hdcp.h b/include/linux/mei_hdcp.h index 774b26da0c26..9a869d1cbd5d 100644 --- a/include/linux/mei_hdcp.h +++ b/include/linux/mei_hdcp.h @@ -23,6 +23,77 @@ #ifndef _LINUX_MEI_HDCP_H #define _LINUX_MEI_HDCP_H
+#include <linux/mei_cl_bus.h> + +/* + * Enumeration of the physical DDI available on the platform + */ +enum physical_port { + INVALID_PORT = 0x00, /* Not a valid port */ + + DDI_RANGE_BEGIN = 0x01, /* Beginning of the valid DDI port range */ + DDI_B = 0x01, /* Port DDI B */ + DDI_C = 0x02, /* Port DDI C */ + DDI_D = 0x03, /* Port DDI D */ + DDI_E = 0x04, /* Port DDI E */ + DDI_F = 0x05, /* Port DDI F */ + DDI_A = 0x07, /* Port DDI A */ + DDI_RANGE_END = DDI_A,/* End of the valid DDI port range */ +}; + +/* The types of HDCP 2.2 ports supported */ +enum hdcp_integrated_port_type { + HDCP_INVALID_TYPE = 0x00, + + /* HDCP 2.x ports that are integrated into Intel HW */ + INTEGRATED = 0x01, + + /* HDCP2.2 discrete wired Tx port with LSPCON (HDMI 2.0) solution */ + LSPCON = 0x02, + + /* HDCP2.2 discrete wired Tx port using the CPDP (DP 1.3) solution */ + CPDP = 0x03, +}; + +/** + * wired_protocol: Supported integrated wired HDCP protocol. + * Based on this value, Minor differenceneeded between wired specifications + * are handled. + */ +enum hdcp_protocol { + HDCP_PROTOCOL_INVALID, + HDCP_PROTOCOL_HDMI, + HDCP_PROTOCOL_DP +}; + +/** + * mei_hdcp_data: Input data to the mei_hdcp APIs. + */ +struct mei_hdcp_data { + struct mei_cl_device *cldev; + enum physical_port port; + enum hdcp_integrated_port_type port_type; + enum hdcp_protocol protocol; + + /* + * No of streams transmitted on a port. + * In case of HDMI & DP SST, single stream will be + * transmitted on a port. + */ + uint16_t k; + + /* + * 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. + */ + uint32_t seq_num_m; + + /* k(No of Streams per port) x structure of wired_streamid_type */ + struct hdcp2_streamid_type *streams; +}; + #ifdef CONFIG_INTEL_MEI_HDCP int mei_hdcp_cldev_get_reference(void *client_data, struct mei_cl_device **cldev,
Data structures and Enum for the I915-MEI_HDCP interface are defined at <linux/mei_hdcp.h>
v2: Rebased.
Signed-off-by: Ramalingam C ramalingam.c@intel.com
include/linux/mei_hdcp.h | 71 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 71 insertions(+)
diff --git a/include/linux/mei_hdcp.h b/include/linux/mei_hdcp.h index 774b26da0c26..9a869d1cbd5d 100644 --- a/include/linux/mei_hdcp.h +++ b/include/linux/mei_hdcp.h @@ -23,6 +23,77 @@ #ifndef _LINUX_MEI_HDCP_H #define _LINUX_MEI_HDCP_H
Missing license.
+#include <linux/mei_cl_bus.h>
+/*
- Enumeration of the physical DDI available on the platform */ enum
+physical_port {
If this is public header you need a proper prefix for types.
- INVALID_PORT = 0x00, /* Not a valid port */
- DDI_RANGE_BEGIN = 0x01, /* Beginning of the valid DDI port
range */
- DDI_B = 0x01, /* Port DDI B */
- DDI_C = 0x02, /* Port DDI C */
- DDI_D = 0x03, /* Port DDI D */
- DDI_E = 0x04, /* Port DDI E */
- DDI_F = 0x05, /* Port DDI F */
- DDI_A = 0x07, /* Port DDI A */
- DDI_RANGE_END = DDI_A,/* End of the valid DDI port range */
+};
+/* The types of HDCP 2.2 ports supported */ enum +hdcp_integrated_port_type {
- HDCP_INVALID_TYPE = 0x00,
- /* HDCP 2.x ports that are integrated into Intel HW */
- INTEGRATED = 0x01,
- /* HDCP2.2 discrete wired Tx port with LSPCON (HDMI 2.0) solution
*/
- LSPCON = 0x02,
- /* HDCP2.2 discrete wired Tx port using the CPDP (DP 1.3) solution */
- CPDP = 0x03,
+};
+/**
- wired_protocol: Supported integrated wired HDCP protocol.
- Based on this value, Minor differenceneeded between wired
+specifications
- are handled.
- */
+enum hdcp_protocol {
- HDCP_PROTOCOL_INVALID,
- HDCP_PROTOCOL_HDMI,
- HDCP_PROTOCOL_DP
+};
+/**
- mei_hdcp_data: Input data to the mei_hdcp APIs.
- */
+struct mei_hdcp_data {
- struct mei_cl_device *cldev;
Not why this device is here?
+ enum physical_port port;
- enum hdcp_integrated_port_type port_type;
- enum hdcp_protocol protocol;
- /*
* No of streams transmitted on a port.
* In case of HDMI & DP SST, single stream will be
* transmitted on a port.
*/
- uint16_t k;
- /*
* 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.
*/
- uint32_t seq_num_m;
- /* k(No of Streams per port) x structure of wired_streamid_type */
- struct hdcp2_streamid_type *streams;
+};
#ifdef CONFIG_INTEL_MEI_HDCP int mei_hdcp_cldev_get_reference(void *client_data, struct mei_cl_device **cldev, -- 2.7.4
On Thursday 08 March 2018 06:43 PM, Winkler, Tomas wrote:
Data structures and Enum for the I915-MEI_HDCP interface are defined at <linux/mei_hdcp.h>
v2: Rebased.
Signed-off-by: Ramalingam C ramalingam.c@intel.com
include/linux/mei_hdcp.h | 71 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 71 insertions(+)
diff --git a/include/linux/mei_hdcp.h b/include/linux/mei_hdcp.h index 774b26da0c26..9a869d1cbd5d 100644 --- a/include/linux/mei_hdcp.h +++ b/include/linux/mei_hdcp.h @@ -23,6 +23,77 @@ #ifndef _LINUX_MEI_HDCP_H #define _LINUX_MEI_HDCP_H
Missing license.
Added in the [PATCH v2 07/42] misc/mei/hdcp: Get & Put for mei cl_device And do we need dual licence for this generic header also?
+#include <linux/mei_cl_bus.h>
+/*
- Enumeration of the physical DDI available on the platform */ enum
+physical_port {
If this is public header you need a proper prefix for types.
Ok I will add a prefix of HDCP_
- INVALID_PORT = 0x00, /* Not a valid port */
- DDI_RANGE_BEGIN = 0x01, /* Beginning of the valid DDI port
range */
- DDI_B = 0x01, /* Port DDI B */
- DDI_C = 0x02, /* Port DDI C */
- DDI_D = 0x03, /* Port DDI D */
- DDI_E = 0x04, /* Port DDI E */
- DDI_F = 0x05, /* Port DDI F */
- DDI_A = 0x07, /* Port DDI A */
- DDI_RANGE_END = DDI_A,/* End of the valid DDI port range */
+};
+/* The types of HDCP 2.2 ports supported */ enum +hdcp_integrated_port_type {
- HDCP_INVALID_TYPE = 0x00,
- /* HDCP 2.x ports that are integrated into Intel HW */
- INTEGRATED = 0x01,
- /* HDCP2.2 discrete wired Tx port with LSPCON (HDMI 2.0) solution
*/
- LSPCON = 0x02,
- /* HDCP2.2 discrete wired Tx port using the CPDP (DP 1.3) solution */
- CPDP = 0x03,
+};
+/**
- wired_protocol: Supported integrated wired HDCP protocol.
- Based on this value, Minor differenceneeded between wired
+specifications
- are handled.
- */
+enum hdcp_protocol {
- HDCP_PROTOCOL_INVALID,
- HDCP_PROTOCOL_HDMI,
- HDCP_PROTOCOL_DP
+};
+/**
- mei_hdcp_data: Input data to the mei_hdcp APIs.
- */
+struct mei_hdcp_data {
- struct mei_cl_device *cldev;
Not why this device is here?
I915 is passing the struct mei_hdcp_data as parameter for all requests. So the struct is populated withe the device handle also. As per your review comment if the cldev needs to be explicit parameter then that can be removed from this struct.
--Ram
- enum physical_port port;
- enum hdcp_integrated_port_type port_type;
- enum hdcp_protocol protocol;
- /*
* No of streams transmitted on a port.
* In case of HDMI & DP SST, single stream will be
* transmitted on a port.
*/
- uint16_t k;
- /*
* 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.
*/
- uint32_t seq_num_m;
- /* k(No of Streams per port) x structure of wired_streamid_type */
- struct hdcp2_streamid_type *streams;
+};
- #ifdef CONFIG_INTEL_MEI_HDCP int mei_hdcp_cldev_get_reference(void *client_data, struct mei_cl_device **cldev,
-- 2.7.4
Request ME FW to start the HDCP2.2 session for a intel port. Prepares payloads for command WIRED_INITIATE_HDCP2_SESSION and sent 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.
Signed-off-by: Ramalingam C ramalingam.c@intel.com --- drivers/misc/mei/hdcp/mei_hdcp.c | 73 ++++++++++++++++++++++++++++++++++++++++ include/linux/mei_hdcp.h | 11 ++++++ 2 files changed, 84 insertions(+)
diff --git a/drivers/misc/mei/hdcp/mei_hdcp.c b/drivers/misc/mei/hdcp/mei_hdcp.c index 63f77800a6f7..516ad6a40616 100644 --- a/drivers/misc/mei/hdcp/mei_hdcp.c +++ b/drivers/misc/mei/hdcp/mei_hdcp.c @@ -29,6 +29,7 @@ #include <linux/module.h> #include <linux/slab.h> #include <linux/uuid.h> +#include <drm/drm_connector.h>
#include "mei_hdcp.h"
@@ -46,6 +47,78 @@ static inline bool mei_cldev_active_and_enabled(struct mei_cl_device *cldev) return mei_cldev_enabled(cldev); }
+/** + * mei_initiate_hdcp2_session: + * Function to start a Wired HDCP2.2 Tx Session with ME FW + * + * @data : Intel HW specific Data + * @ake_data : ptr to store AKE_Init + * + * Returns 0 on Success, <0 on Failure. + */ +int mei_initiate_hdcp2_session(struct mei_hdcp_data *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 } }; + enum me_hdcp_status status; + struct device *dev; + ssize_t byte; + + if (!data || !ake_data) + return -EINVAL; + + /* check for the mei_device enabled or not */ + if (!mei_cldev_active_and_enabled(data->cldev)) + return -ENODEV; + + dev = &data->cldev->dev; + + /* Fill header details */ + 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; + + /* Fill in the In Data */ + 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; + + /* Request to ME */ + byte = mei_cldev_send(data->cldev, (u8 *)&session_init_in, + sizeof(session_init_in)); + if (byte < 0) { + dev_err(dev, "mei_cldev_send failed. %d\n", (int)byte); + return byte; + } + + /* Response from ME */ + byte = mei_cldev_recv(data->cldev, (u8 *)&session_init_out, + sizeof(session_init_out)); + if (byte < 0) { + dev_err(dev, "mei_cldev_recv failed. %d\n", (int)byte); + return byte; + } + + status = (enum me_hdcp_status)session_init_out.header.status; + if (status != ME_HDCP_STATUS_SUCCESS) { + dev_err(dev, "ME cmd 0x%08X Failed. Status: 0x%X\n", + WIRED_INITIATE_HDCP2_SESSION, status); + return -1; + } + + 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; +} +EXPORT_SYMBOL(mei_initiate_hdcp2_session); + static int mei_hdcp_probe(struct mei_cl_device *cldev, const struct mei_cl_device_id *id) { diff --git a/include/linux/mei_hdcp.h b/include/linux/mei_hdcp.h index 9a869d1cbd5d..c333528b9c1e 100644 --- a/include/linux/mei_hdcp.h +++ b/include/linux/mei_hdcp.h @@ -24,6 +24,7 @@ #define _LINUX_MEI_HDCP_H
#include <linux/mei_cl_bus.h> +#include <drm/drm_hdcp.h>
/* * Enumeration of the physical DDI available on the platform @@ -101,6 +102,9 @@ int mei_hdcp_cldev_get_reference(void *client_data, struct mei_cl_device *cldev)); void mei_hdcp_cldev_put_reference(struct mei_cl_device *cldev); + +int mei_initiate_hdcp2_session(struct mei_hdcp_data *data, + struct hdcp2_ake_init *ake_data); #else static inline int mei_hdcp_cldev_get_reference(void *client_data, @@ -114,5 +118,12 @@ int mei_hdcp_cldev_get_reference(void *client_data, static inline void mei_hdcp_cldev_put_reference(struct mei_cl_device *cldev) {} + +static inline +int mei_initiate_hdcp2_session(struct mei_hdcp_data *data, + struct hdcp2_ake_init *ake_data) +{ + return -ENODEV; +} #endif /* defined (CONFIG_INTEL_MEI_HDCP) */ #endif /* defined (_LINUX_MEI_HDCP_H) */
Request ME FW to start the HDCP2.2 session for a intel port. Prepares payloads for command WIRED_INITIATE_HDCP2_SESSION and sent 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.
Signed-off-by: Ramalingam C ramalingam.c@intel.com
drivers/misc/mei/hdcp/mei_hdcp.c | 73 ++++++++++++++++++++++++++++++++++++++++ include/linux/mei_hdcp.h | 11 ++++++ 2 files changed, 84 insertions(+)
diff --git a/drivers/misc/mei/hdcp/mei_hdcp.c b/drivers/misc/mei/hdcp/mei_hdcp.c index 63f77800a6f7..516ad6a40616 100644 --- a/drivers/misc/mei/hdcp/mei_hdcp.c +++ b/drivers/misc/mei/hdcp/mei_hdcp.c @@ -29,6 +29,7 @@ #include <linux/module.h> #include <linux/slab.h> #include <linux/uuid.h> +#include <drm/drm_connector.h>
#include "mei_hdcp.h"
@@ -46,6 +47,78 @@ static inline bool mei_cldev_active_and_enabled(struct mei_cl_device *cldev) return mei_cldev_enabled(cldev); }
+/**
- mei_initiate_hdcp2_session:
- Function to start a Wired HDCP2.2 Tx Session with ME FW
- @data : Intel HW specific Data
- @ake_data : ptr to store AKE_Init
- Returns 0 on Success, <0 on Failure.
- */
+int mei_initiate_hdcp2_session(struct mei_hdcp_data *data,
struct hdcp2_ake_init *ake_data) {
You should use cldev as a first argument for all those functions
- struct wired_cmd_initiate_hdcp2_session_in session_init_in = { { 0 }
};
- struct wired_cmd_initiate_hdcp2_session_out
session_init_out = { { 0 } };
- enum me_hdcp_status status;
- struct device *dev;
- ssize_t byte;
- if (!data || !ake_data)
return -EINVAL;
- /* check for the mei_device enabled or not */
- if (!mei_cldev_active_and_enabled(data->cldev))
return -ENODEV;
No reason cldev will be NULL here.
- dev = &data->cldev->dev;
- /* Fill header details */
Those types of comments are redundant.
- 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;
- /* Fill in the In Data */
Those types of comments are redundant.
- 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;
- /* Request to ME */
- byte = mei_cldev_send(data->cldev, (u8 *)&session_init_in,
sizeof(session_init_in));
- if (byte < 0) {
dev_err(dev, "mei_cldev_send failed. %d\n", (int)byte);
return byte;
- }
- /* Response from ME */
- byte = mei_cldev_recv(data->cldev, (u8 *)&session_init_out,
sizeof(session_init_out));
- if (byte < 0) {
dev_err(dev, "mei_cldev_recv failed. %d\n", (int)byte);
return byte;
- }
- status = (enum me_hdcp_status)session_init_out.header.status;
Useless cast
- if (status != ME_HDCP_STATUS_SUCCESS) {
dev_err(dev, "ME cmd 0x%08X Failed. Status: 0x%X\n",
WIRED_INITIATE_HDCP2_SESSION, status);
return -1;
- }
- 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;
+} +EXPORT_SYMBOL(mei_initiate_hdcp2_session);
static int mei_hdcp_probe(struct mei_cl_device *cldev, const struct mei_cl_device_id *id) { diff --git a/include/linux/mei_hdcp.h b/include/linux/mei_hdcp.h index 9a869d1cbd5d..c333528b9c1e 100644 --- a/include/linux/mei_hdcp.h +++ b/include/linux/mei_hdcp.h @@ -24,6 +24,7 @@ #define _LINUX_MEI_HDCP_H
#include <linux/mei_cl_bus.h> +#include <drm/drm_hdcp.h>
/*
- Enumeration of the physical DDI available on the platform @@ -101,6
+102,9 @@ int mei_hdcp_cldev_get_reference(void *client_data, struct mei_cl_device *cldev)); void mei_hdcp_cldev_put_reference(struct mei_cl_device *cldev);
+int mei_initiate_hdcp2_session(struct mei_hdcp_data *data,
struct hdcp2_ake_init *ake_data);
#else static inline int mei_hdcp_cldev_get_reference(void *client_data, @@ -114,5 +118,12 @@ int mei_hdcp_cldev_get_reference(void *client_data, static inline void mei_hdcp_cldev_put_reference(struct mei_cl_device *cldev) {}
+static inline +int mei_initiate_hdcp2_session(struct mei_hdcp_data *data,
struct hdcp2_ake_init *ake_data) {
- return -ENODEV;
+} #endif /* defined (CONFIG_INTEL_MEI_HDCP) */ #endif /* defined (_LINUX_MEI_HDCP_H) */ -- 2.7.4
On Thursday 08 March 2018 06:47 PM, Winkler, Tomas wrote:
Request ME FW to start the HDCP2.2 session for a intel port. Prepares payloads for command WIRED_INITIATE_HDCP2_SESSION and sent 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.
Signed-off-by: Ramalingam C ramalingam.c@intel.com
drivers/misc/mei/hdcp/mei_hdcp.c | 73 ++++++++++++++++++++++++++++++++++++++++ include/linux/mei_hdcp.h | 11 ++++++ 2 files changed, 84 insertions(+)
diff --git a/drivers/misc/mei/hdcp/mei_hdcp.c b/drivers/misc/mei/hdcp/mei_hdcp.c index 63f77800a6f7..516ad6a40616 100644 --- a/drivers/misc/mei/hdcp/mei_hdcp.c +++ b/drivers/misc/mei/hdcp/mei_hdcp.c @@ -29,6 +29,7 @@ #include <linux/module.h> #include <linux/slab.h> #include <linux/uuid.h> +#include <drm/drm_connector.h>
#include "mei_hdcp.h"
@@ -46,6 +47,78 @@ static inline bool mei_cldev_active_and_enabled(struct mei_cl_device *cldev) return mei_cldev_enabled(cldev); }
+/**
- mei_initiate_hdcp2_session:
- Function to start a Wired HDCP2.2 Tx Session with ME FW
- @data : Intel HW specific Data
- @ake_data : ptr to store AKE_Init
- Returns 0 on Success, <0 on Failure.
- */
+int mei_initiate_hdcp2_session(struct mei_hdcp_data *data,
struct hdcp2_ake_init *ake_data) {
You should use cldev as a first argument for all those functions
data has it as a member. Should it be explicit?
- struct wired_cmd_initiate_hdcp2_session_in session_init_in = { { 0 }
};
- struct wired_cmd_initiate_hdcp2_session_out
session_init_out = { { 0 } };
- enum me_hdcp_status status;
- struct device *dev;
- ssize_t byte;
- if (!data || !ake_data)
return -EINVAL;
- /* check for the mei_device enabled or not */
- if (!mei_cldev_active_and_enabled(data->cldev))
return -ENODEV;
No reason cldev will be NULL here.
Ok. But device could be disabled right? we might want to check the enabled state of the device?
- dev = &data->cldev->dev;
- /* Fill header details */
Those types of comments are redundant.
- 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;
- /* Fill in the In Data */
Those types of comments are redundant.
- 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;
- /* Request to ME */
- byte = mei_cldev_send(data->cldev, (u8 *)&session_init_in,
sizeof(session_init_in));
- if (byte < 0) {
dev_err(dev, "mei_cldev_send failed. %d\n", (int)byte);
return byte;
- }
- /* Response from ME */
- byte = mei_cldev_recv(data->cldev, (u8 *)&session_init_out,
sizeof(session_init_out));
- if (byte < 0) {
dev_err(dev, "mei_cldev_recv failed. %d\n", (int)byte);
return byte;
- }
- status = (enum me_hdcp_status)session_init_out.header.status;
Useless cast
Oops. Will remove it.
--Ram
- if (status != ME_HDCP_STATUS_SUCCESS) {
dev_err(dev, "ME cmd 0x%08X Failed. Status: 0x%X\n",
WIRED_INITIATE_HDCP2_SESSION, status);
return -1;
- }
- 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;
+} +EXPORT_SYMBOL(mei_initiate_hdcp2_session);
- static int mei_hdcp_probe(struct mei_cl_device *cldev, const struct mei_cl_device_id *id) { diff --git
a/include/linux/mei_hdcp.h b/include/linux/mei_hdcp.h index 9a869d1cbd5d..c333528b9c1e 100644 --- a/include/linux/mei_hdcp.h +++ b/include/linux/mei_hdcp.h @@ -24,6 +24,7 @@ #define _LINUX_MEI_HDCP_H
#include <linux/mei_cl_bus.h> +#include <drm/drm_hdcp.h>
/*
- Enumeration of the physical DDI available on the platform @@ -101,6
+102,9 @@ int mei_hdcp_cldev_get_reference(void *client_data, struct mei_cl_device *cldev)); void mei_hdcp_cldev_put_reference(struct mei_cl_device *cldev);
+int mei_initiate_hdcp2_session(struct mei_hdcp_data *data,
#else static inline int mei_hdcp_cldev_get_reference(void *client_data, @@ -114,5 +118,12struct hdcp2_ake_init *ake_data);
@@ int mei_hdcp_cldev_get_reference(void *client_data, static inline void mei_hdcp_cldev_put_reference(struct mei_cl_device *cldev) {}
+static inline +int mei_initiate_hdcp2_session(struct mei_hdcp_data *data,
struct hdcp2_ake_init *ake_data) {
- return -ENODEV;
+} #endif /* defined (CONFIG_INTEL_MEI_HDCP) */ #endif /* defined (_LINUX_MEI_HDCP_H) */ -- 2.7.4
On Thursday 08 March 2018 06:47 PM, Winkler, Tomas wrote:
Request ME FW to start the HDCP2.2 session for a intel port. Prepares payloads for command WIRED_INITIATE_HDCP2_SESSION and
sent
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.
Signed-off-by: Ramalingam C ramalingam.c@intel.com
drivers/misc/mei/hdcp/mei_hdcp.c | 73 ++++++++++++++++++++++++++++++++++++++++ include/linux/mei_hdcp.h | 11 ++++++ 2 files changed, 84 insertions(+)
diff --git a/drivers/misc/mei/hdcp/mei_hdcp.c b/drivers/misc/mei/hdcp/mei_hdcp.c index 63f77800a6f7..516ad6a40616 100644 --- a/drivers/misc/mei/hdcp/mei_hdcp.c +++ b/drivers/misc/mei/hdcp/mei_hdcp.c @@ -29,6 +29,7 @@ #include <linux/module.h> #include <linux/slab.h> #include <linux/uuid.h> +#include <drm/drm_connector.h>
#include "mei_hdcp.h"
@@ -46,6 +47,78 @@ static inline bool mei_cldev_active_and_enabled(struct mei_cl_device *cldev) return mei_cldev_enabled(cldev); }
+/**
- mei_initiate_hdcp2_session:
- Function to start a Wired HDCP2.2 Tx Session with ME FW
- @data : Intel HW specific Data
- @ake_data : ptr to store AKE_Init
- Returns 0 on Success, <0 on Failure.
- */
+int mei_initiate_hdcp2_session(struct mei_hdcp_data *data,
struct hdcp2_ake_init *ake_data) {
You should use cldev as a first argument for all those functions
data has it as a member. Should it be explicit?
I believe, you should. cldev is the object this function operates on so it should be explicitly and data provides the input paramterss
Thanks Tomas
- /* check for the mei_device enabled or not */
- if (!mei_cldev_active_and_enabled(data->cldev))
return -ENODEV;
No reason cldev will be NULL here.
Ok. But device could be disabled right? we might want to check the enabled state of the device?
The device might go under reset any time during operations. so it is useless to test at this point (unless you know some other flow to take) as the underlying service already checks that for you and will return appropriate error (ENODEV). Thanks Tomas
- dev = &data->cldev->dev;
- /* Fill header details */
Those types of comments are redundant.
- 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;
- /* Fill in the In Data */
Those types of comments are redundant.
- 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;
- /* Request to ME */
- byte = mei_cldev_send(data->cldev, (u8 *)&session_init_in,
sizeof(session_init_in));
- if (byte < 0) {
dev_err(dev, "mei_cldev_send failed. %d\n", (int)byte);
return byte;
- }
- /* Response from ME */
- byte = mei_cldev_recv(data->cldev, (u8 *)&session_init_out,
sizeof(session_init_out));
- if (byte < 0) {
dev_err(dev, "mei_cldev_recv failed. %d\n", (int)byte);
return byte;
- }
- status = (enum me_hdcp_status)session_init_out.header.status;
Useless cast
Oops. Will remove it.
--Ram
- if (status != ME_HDCP_STATUS_SUCCESS) {
dev_err(dev, "ME cmd 0x%08X Failed. Status: 0x%X\n",
WIRED_INITIATE_HDCP2_SESSION, status);
return -1;
- }
- 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;
+} +EXPORT_SYMBOL(mei_initiate_hdcp2_session);
- static int mei_hdcp_probe(struct mei_cl_device *cldev, const struct mei_cl_device_id *id) { diff --git
a/include/linux/mei_hdcp.h b/include/linux/mei_hdcp.h index 9a869d1cbd5d..c333528b9c1e 100644 --- a/include/linux/mei_hdcp.h +++ b/include/linux/mei_hdcp.h @@ -24,6 +24,7 @@ #define _LINUX_MEI_HDCP_H
#include <linux/mei_cl_bus.h> +#include <drm/drm_hdcp.h>
/*
- Enumeration of the physical DDI available on the platform @@
-101,6 +102,9 @@ int mei_hdcp_cldev_get_reference(void *client_data, struct mei_cl_device *cldev)); void mei_hdcp_cldev_put_reference(struct mei_cl_device *cldev);
+int mei_initiate_hdcp2_session(struct mei_hdcp_data *data,
#else static inline int mei_hdcp_cldev_get_reference(void *client_data, @@ -114,5struct hdcp2_ake_init *ake_data);
+118,12 @@ int mei_hdcp_cldev_get_reference(void *client_data, static inline void mei_hdcp_cldev_put_reference(struct mei_cl_device *cldev) {}
+static inline +int mei_initiate_hdcp2_session(struct mei_hdcp_data *data,
struct hdcp2_ake_init *ake_data) {
- return -ENODEV;
+} #endif /* defined (CONFIG_INTEL_MEI_HDCP) */ #endif /* defined (_LINUX_MEI_HDCP_H) */ -- 2.7.4
On Monday 12 March 2018 05:02 PM, Winkler, Tomas wrote:
On Thursday 08 March 2018 06:47 PM, Winkler, Tomas wrote:
Request ME FW to start the HDCP2.2 session for a intel port. Prepares payloads for command WIRED_INITIATE_HDCP2_SESSION and
sent
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.
Signed-off-by: Ramalingam C ramalingam.c@intel.com
drivers/misc/mei/hdcp/mei_hdcp.c | 73 ++++++++++++++++++++++++++++++++++++++++ include/linux/mei_hdcp.h | 11 ++++++ 2 files changed, 84 insertions(+)
diff --git a/drivers/misc/mei/hdcp/mei_hdcp.c b/drivers/misc/mei/hdcp/mei_hdcp.c index 63f77800a6f7..516ad6a40616 100644 --- a/drivers/misc/mei/hdcp/mei_hdcp.c +++ b/drivers/misc/mei/hdcp/mei_hdcp.c @@ -29,6 +29,7 @@ #include <linux/module.h> #include <linux/slab.h> #include <linux/uuid.h> +#include <drm/drm_connector.h>
#include "mei_hdcp.h"
@@ -46,6 +47,78 @@ static inline bool mei_cldev_active_and_enabled(struct mei_cl_device *cldev) return mei_cldev_enabled(cldev); }
+/**
- mei_initiate_hdcp2_session:
- Function to start a Wired HDCP2.2 Tx Session with ME FW
- @data : Intel HW specific Data
- @ake_data : ptr to store AKE_Init
- Returns 0 on Success, <0 on Failure.
- */
+int mei_initiate_hdcp2_session(struct mei_hdcp_data *data,
struct hdcp2_ake_init *ake_data) {
You should use cldev as a first argument for all those functions
data has it as a member. Should it be explicit?
I believe, you should. cldev is the object this function operates on so it should be explicitly and data provides the input paramterss
Sure. I will follow that.
Thanks Tomas
- /* check for the mei_device enabled or not */
- if (!mei_cldev_active_and_enabled(data->cldev))
return -ENODEV;
No reason cldev will be NULL here.
Ok. But device could be disabled right? we might want to check the enabled state of the device?
The device might go under reset any time during operations. so it is useless to test at this point (unless you know some other flow to take) as the underlying service already checks that for you and will return appropriate error (ENODEV). Thanks Tomas
Thanks for the explanation. I will drop the check here.
--Ram
- dev = &data->cldev->dev;
- /* Fill header details */
Those types of comments are redundant.
- 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;
- /* Fill in the In Data */
Those types of comments are redundant.
- 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;
- /* Request to ME */
- byte = mei_cldev_send(data->cldev, (u8 *)&session_init_in,
sizeof(session_init_in));
- if (byte < 0) {
dev_err(dev, "mei_cldev_send failed. %d\n", (int)byte);
return byte;
- }
- /* Response from ME */
- byte = mei_cldev_recv(data->cldev, (u8 *)&session_init_out,
sizeof(session_init_out));
- if (byte < 0) {
dev_err(dev, "mei_cldev_recv failed. %d\n", (int)byte);
return byte;
- }
- status = (enum me_hdcp_status)session_init_out.header.status;
Useless cast
Oops. Will remove it.
--Ram
- if (status != ME_HDCP_STATUS_SUCCESS) {
dev_err(dev, "ME cmd 0x%08X Failed. Status: 0x%X\n",
WIRED_INITIATE_HDCP2_SESSION, status);
return -1;
- }
- 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;
+} +EXPORT_SYMBOL(mei_initiate_hdcp2_session);
- static int mei_hdcp_probe(struct mei_cl_device *cldev, const struct mei_cl_device_id *id) { diff --git
a/include/linux/mei_hdcp.h b/include/linux/mei_hdcp.h index 9a869d1cbd5d..c333528b9c1e 100644 --- a/include/linux/mei_hdcp.h +++ b/include/linux/mei_hdcp.h @@ -24,6 +24,7 @@ #define _LINUX_MEI_HDCP_H
#include <linux/mei_cl_bus.h> +#include <drm/drm_hdcp.h>
/* * Enumeration of the physical DDI available on the platform @@ -101,6 +102,9 @@ int mei_hdcp_cldev_get_reference(void *client_data, struct mei_cl_device *cldev)); void mei_hdcp_cldev_put_reference(struct mei_cl_device *cldev);
+int mei_initiate_hdcp2_session(struct mei_hdcp_data *data,
#else static inline int mei_hdcp_cldev_get_reference(void *client_data, @@ -114,5struct hdcp2_ake_init *ake_data);
+118,12 @@ int mei_hdcp_cldev_get_reference(void *client_data, static inline void mei_hdcp_cldev_put_reference(struct mei_cl_device *cldev) {}
+static inline +int mei_initiate_hdcp2_session(struct mei_hdcp_data *data,
struct hdcp2_ake_init *ake_data) {
- return -ENODEV;
+} #endif /* defined (CONFIG_INTEL_MEI_HDCP) */ #endif /* defined (_LINUX_MEI_HDCP_H) */ -- 2.7.4
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.
Signed-off-by: Ramalingam C ramalingam.c@intel.com --- drivers/misc/mei/hdcp/mei_hdcp.c | 89 ++++++++++++++++++++++++++++++++++++++++ include/linux/mei_hdcp.h | 15 +++++++ 2 files changed, 104 insertions(+)
diff --git a/drivers/misc/mei/hdcp/mei_hdcp.c b/drivers/misc/mei/hdcp/mei_hdcp.c index 516ad6a40616..7c3f02c2e324 100644 --- a/drivers/misc/mei/hdcp/mei_hdcp.c +++ b/drivers/misc/mei/hdcp/mei_hdcp.c @@ -119,6 +119,95 @@ int mei_initiate_hdcp2_session(struct mei_hdcp_data *data, } EXPORT_SYMBOL(mei_initiate_hdcp2_session);
+/** + * 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 + * + * @data : Intel HW specific Data + * @rx_cert : Pointer for AKE_Send_Cert + * @km_stored : Pointer for pairing status flag + * @ek_pub_km : Pointer for output msg + * @msg_sz : Pointer for size of AKE_XXXXX_Km + * + * Returns 0 on Success, <0 on Failure + */ +int +mei_verify_receiver_cert_prepare_km(struct mei_hdcp_data *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 } }; + enum me_hdcp_status status; + struct device *dev; + ssize_t byte; + + if (!data || !rx_cert || !km_stored || !ek_pub_km || !msg_sz) + return -EINVAL; + + /* check for the mei_device enabled or not */ + if (!mei_cldev_active_and_enabled(data->cldev)) + return -ENODEV; + + dev = &data->cldev->dev; + + /* Fill header details */ + 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; + + /* Fill the data */ + 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); + + /* Request to ME */ + byte = mei_cldev_send(data->cldev, (u8 *)&verify_rxcert_in, + sizeof(verify_rxcert_in)); + if (byte < 0) { + dev_err(dev, "mei_cldev_send failed: %d\n", (int)byte); + return byte; + } + + /* Response from ME */ + byte = mei_cldev_recv(data->cldev, (u8 *)&verify_rxcert_out, + sizeof(verify_rxcert_out)); + if (byte < 0) { + dev_err(dev, "mei_cldev_recv failed: %d\n", (int)byte); + return byte; + } + + status = (enum me_hdcp_status)verify_rxcert_out.header.status; + if (status != ME_HDCP_STATUS_SUCCESS) { + dev_err(dev, "ME cmd 0x%08X Failed. Status: 0x%X\n", + WIRED_VERIFY_RECEIVER_CERT, status); + return -1; + } + + *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; +} +EXPORT_SYMBOL(mei_verify_receiver_cert_prepare_km); + static int mei_hdcp_probe(struct mei_cl_device *cldev, const struct mei_cl_device_id *id) { diff --git a/include/linux/mei_hdcp.h b/include/linux/mei_hdcp.h index c333528b9c1e..510a5c1ff1ff 100644 --- a/include/linux/mei_hdcp.h +++ b/include/linux/mei_hdcp.h @@ -105,6 +105,12 @@ void mei_hdcp_cldev_put_reference(struct mei_cl_device *cldev);
int mei_initiate_hdcp2_session(struct mei_hdcp_data *data, struct hdcp2_ake_init *ake_data); +int +mei_verify_receiver_cert_prepare_km(struct mei_hdcp_data *data, + struct hdcp2_ake_send_cert *rx_cert, + bool *km_stored, + struct hdcp2_ake_no_stored_km *ek_pub_km, + size_t *msg_sz); #else static inline int mei_hdcp_cldev_get_reference(void *client_data, @@ -125,5 +131,14 @@ int mei_initiate_hdcp2_session(struct mei_hdcp_data *data, { return -ENODEV; } +static inline int +mei_verify_receiver_cert_prepare_km(struct mei_hdcp_data *data, + struct hdcp2_ake_send_cert *rx_cert, + bool *km_stored, + struct hdcp2_ake_no_stored_km *ek_pub_km, + size_t *msg_sz) +{ + return -ENODEV; +} #endif /* defined (CONFIG_INTEL_MEI_HDCP) */ #endif /* defined (_LINUX_MEI_HDCP_H) */
Requests for the verifcation of AKE_Send_H_prime.
ME will calculation the H and comparing it with received H_Prime. Here AKE_Send_H_prime is a HDCP2.2 Authentication msg.
v2: Rebased.
Signed-off-by: Ramalingam C ramalingam.c@intel.com --- drivers/misc/mei/hdcp/mei_hdcp.c | 65 ++++++++++++++++++++++++++++++++++++++++ include/linux/mei_hdcp.h | 8 +++++ 2 files changed, 73 insertions(+)
diff --git a/drivers/misc/mei/hdcp/mei_hdcp.c b/drivers/misc/mei/hdcp/mei_hdcp.c index 7c3f02c2e324..37faca9a3cc8 100644 --- a/drivers/misc/mei/hdcp/mei_hdcp.c +++ b/drivers/misc/mei/hdcp/mei_hdcp.c @@ -208,6 +208,71 @@ mei_verify_receiver_cert_prepare_km(struct mei_hdcp_data *data, } EXPORT_SYMBOL(mei_verify_receiver_cert_prepare_km);
+/** + * mei_verify_hprime: + * Function to verify AKE_Send_H_prime received + * + * @data : Intel HW specific Data + * @rx_hprime : Pointer for AKE_Send_H_prime + * @hprime_sz : Input buffer size + * + * Returns 0 on Success, <0 on Failure + */ +int mei_verify_hprime(struct mei_hdcp_data *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 } }; + enum me_hdcp_status status; + struct device *dev; + ssize_t byte; + + if (!data || !rx_hprime) + return -EINVAL; + + /* check for the mei_device enabled or not */ + if (!mei_cldev_active_and_enabled(data->cldev)) + return -ENODEV; + + dev = &data->cldev->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)); + + /* Request to ME */ + byte = mei_cldev_send(data->cldev, (u8 *)&send_hprime_in, + sizeof(send_hprime_in)); + if (byte < 0) { + dev_err(dev, "mei_cldev_send failed. %d\n", (int)byte); + return byte; + } + + /* Response from ME */ + byte = mei_cldev_recv(data->cldev, (u8 *)&send_hprime_out, + sizeof(send_hprime_out)); + if (byte < 0) { + dev_err(dev, "mei_cldev_recv failed. %d\n", (int)byte); + return byte; + } + + status = (enum me_hdcp_status)send_hprime_out.header.status; + if (status != ME_HDCP_STATUS_SUCCESS) { + dev_err(dev, "ME cmd 0x%08X Failed. Status: 0x%X\n", + WIRED_AKE_SEND_HPRIME, status); + return -1; + } + return 0; +} +EXPORT_SYMBOL(mei_verify_hprime); + static int mei_hdcp_probe(struct mei_cl_device *cldev, const struct mei_cl_device_id *id) { diff --git a/include/linux/mei_hdcp.h b/include/linux/mei_hdcp.h index 510a5c1ff1ff..3590e3421134 100644 --- a/include/linux/mei_hdcp.h +++ b/include/linux/mei_hdcp.h @@ -111,6 +111,8 @@ mei_verify_receiver_cert_prepare_km(struct mei_hdcp_data *data, bool *km_stored, struct hdcp2_ake_no_stored_km *ek_pub_km, size_t *msg_sz); +int mei_verify_hprime(struct mei_hdcp_data *data, + struct hdcp2_ake_send_hprime *rx_hprime); #else static inline int mei_hdcp_cldev_get_reference(void *client_data, @@ -140,5 +142,11 @@ mei_verify_receiver_cert_prepare_km(struct mei_hdcp_data *data, { return -ENODEV; } +static inline +int mei_verify_hprime(struct mei_hdcp_data *data, + struct hdcp2_ake_send_hprime *rx_hprime) +{ + return -ENODEV; +} #endif /* defined (CONFIG_INTEL_MEI_HDCP) */ #endif /* defined (_LINUX_MEI_HDCP_H) */
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.
Signed-off-by: Ramalingam C ramalingam.c@intel.com --- drivers/misc/mei/hdcp/mei_hdcp.c | 66 ++++++++++++++++++++++++++++++++++++++++ include/linux/mei_hdcp.h | 8 +++++ 2 files changed, 74 insertions(+)
diff --git a/drivers/misc/mei/hdcp/mei_hdcp.c b/drivers/misc/mei/hdcp/mei_hdcp.c index 37faca9a3cc8..753a6a466611 100644 --- a/drivers/misc/mei/hdcp/mei_hdcp.c +++ b/drivers/misc/mei/hdcp/mei_hdcp.c @@ -273,6 +273,72 @@ int mei_verify_hprime(struct mei_hdcp_data *data, } EXPORT_SYMBOL(mei_verify_hprime);
+/** + * mei_store_pairing_info: + * Function to store pairing info received from panel + * + * @data : Intel HW specific Data + * @pairing_info : Pointer for AKE_Send_Pairing_Info + * + * Returns 0 on Success, <0 on Failure + */ + +int mei_store_pairing_info(struct mei_hdcp_data *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 } }; + enum me_hdcp_status status = ME_HDCP_STATUS_UNKNOWN_ERROR; + struct device *dev; + ssize_t byte; + + if (!data || !pairing_info) + return -EINVAL; + + /* check for the mei_device enabled or not */ + if (!mei_cldev_active_and_enabled(data->cldev)) + return -ENODEV; + + dev = &data->cldev->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)); + + /* Request to ME */ + byte = mei_cldev_send(data->cldev, (u8 *)&pairing_info_in, + sizeof(pairing_info_in)); + if (byte < 0) { + dev_err(dev, "mei_cldev_send failed. %d\n", (int)byte); + return byte; + } + + /* Response from ME */ + byte = mei_cldev_recv(data->cldev, (u8 *)&pairing_info_out, + sizeof(pairing_info_out)); + if (byte < 0) { + dev_err(dev, "mei_cldev_recv failed. %d\n", (int)byte); + return byte; + } + + status = (enum me_hdcp_status)pairing_info_out.header.status; + if (status != ME_HDCP_STATUS_SUCCESS) { + dev_err(dev, "ME cmd 0x%08X failed. Status: 0x%X\n", + WIRED_AKE_SEND_PAIRING_INFO, status); + return -1; + } + return 0; +} +EXPORT_SYMBOL(mei_store_pairing_info); + static int mei_hdcp_probe(struct mei_cl_device *cldev, const struct mei_cl_device_id *id) { diff --git a/include/linux/mei_hdcp.h b/include/linux/mei_hdcp.h index 3590e3421134..449ac3af4d53 100644 --- a/include/linux/mei_hdcp.h +++ b/include/linux/mei_hdcp.h @@ -113,6 +113,8 @@ mei_verify_receiver_cert_prepare_km(struct mei_hdcp_data *data, size_t *msg_sz); int mei_verify_hprime(struct mei_hdcp_data *data, struct hdcp2_ake_send_hprime *rx_hprime); +int mei_store_pairing_info(struct mei_hdcp_data *data, + struct hdcp2_ake_send_pairing_info *pairing_info); #else static inline int mei_hdcp_cldev_get_reference(void *client_data, @@ -148,5 +150,11 @@ int mei_verify_hprime(struct mei_hdcp_data *data, { return -ENODEV; } +static inline +int mei_store_pairing_info(struct mei_hdcp_data *data, + struct hdcp2_ake_send_pairing_info *pairing_info) +{ + return -ENODEV; +} #endif /* defined (CONFIG_INTEL_MEI_HDCP) */ #endif /* defined (_LINUX_MEI_HDCP_H) */
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.
Signed-off-by: Ramalingam C ramalingam.c@intel.com --- drivers/misc/mei/hdcp/mei_hdcp.c | 64 ++++++++++++++++++++++++++++++++++++++++ include/linux/mei_hdcp.h | 8 +++++ 2 files changed, 72 insertions(+)
diff --git a/drivers/misc/mei/hdcp/mei_hdcp.c b/drivers/misc/mei/hdcp/mei_hdcp.c index 753a6a466611..ff19de58222f 100644 --- a/drivers/misc/mei/hdcp/mei_hdcp.c +++ b/drivers/misc/mei/hdcp/mei_hdcp.c @@ -339,6 +339,70 @@ int mei_store_pairing_info(struct mei_hdcp_data *data, } EXPORT_SYMBOL(mei_store_pairing_info);
+/** + * mei_initiate_locality_check: + * Function to prepare LC_Init. + * + * @data : Intel HW specific Data + * @hdcp2_lc_init : Pointer for storing LC_Init + * + * Returns 0 on Success, <0 on Failure + */ +int mei_initiate_locality_check(struct mei_hdcp_data *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 } }; + enum me_hdcp_status status; + struct device *dev; + ssize_t byte; + + if (!data || !lc_init_data) + return -EINVAL; + + /* check for the mei_device enabled or not */ + if (!mei_cldev_active_and_enabled(data->cldev)) + return -ENODEV; + + dev = &data->cldev->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; + + /* Request to ME */ + byte = mei_cldev_send(data->cldev, (u8 *)&lc_init_in, + sizeof(lc_init_in)); + if (byte < 0) { + dev_err(dev, "mei_cldev_send failed. %d\n", (int)byte); + return byte; + } + + /* Response from ME */ + byte = mei_cldev_recv(data->cldev, (u8 *)&lc_init_out, + sizeof(lc_init_out)); + if (byte < 0) { + dev_err(dev, "mei_cldev_recv failed. %d\n", (int)byte); + return byte; + } + + status = (enum me_hdcp_status)lc_init_out.header.status; + if (status != ME_HDCP_STATUS_SUCCESS) { + dev_err(dev, "ME cmd 0x%08X Failed. status: 0x%X\n", + WIRED_INIT_LOCALITY_CHECK, status); + return -1; + } + + 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; +} +EXPORT_SYMBOL(mei_initiate_locality_check); + static int mei_hdcp_probe(struct mei_cl_device *cldev, const struct mei_cl_device_id *id) { diff --git a/include/linux/mei_hdcp.h b/include/linux/mei_hdcp.h index 449ac3af4d53..fd8a26dddacb 100644 --- a/include/linux/mei_hdcp.h +++ b/include/linux/mei_hdcp.h @@ -115,6 +115,8 @@ int mei_verify_hprime(struct mei_hdcp_data *data, struct hdcp2_ake_send_hprime *rx_hprime); int mei_store_pairing_info(struct mei_hdcp_data *data, struct hdcp2_ake_send_pairing_info *pairing_info); +int mei_initiate_locality_check(struct mei_hdcp_data *data, + struct hdcp2_lc_init *lc_init_data); #else static inline int mei_hdcp_cldev_get_reference(void *client_data, @@ -156,5 +158,11 @@ int mei_store_pairing_info(struct mei_hdcp_data *data, { return -ENODEV; } +static inline +int mei_initiate_locality_check(struct mei_hdcp_data *data, + struct hdcp2_lc_init *lc_init_data) +{ + return -ENODEV; +} #endif /* defined (CONFIG_INTEL_MEI_HDCP) */ #endif /* defined (_LINUX_MEI_HDCP_H) */
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.
Signed-off-by: Ramalingam C ramalingam.c@intel.com --- drivers/misc/mei/hdcp/mei_hdcp.c | 65 ++++++++++++++++++++++++++++++++++++++++ include/linux/mei_hdcp.h | 8 +++++ 2 files changed, 73 insertions(+)
diff --git a/drivers/misc/mei/hdcp/mei_hdcp.c b/drivers/misc/mei/hdcp/mei_hdcp.c index ff19de58222f..c3c8b9a28498 100644 --- a/drivers/misc/mei/hdcp/mei_hdcp.c +++ b/drivers/misc/mei/hdcp/mei_hdcp.c @@ -403,6 +403,71 @@ int mei_initiate_locality_check(struct mei_hdcp_data *data, } EXPORT_SYMBOL(mei_initiate_locality_check);
+/** + * mei_verify_lprime: + * Function to verify lprime. + * + * @data : Intel HW specific Data + * @rx_lprime : Pointer for LC_Send_L_prime + * + * Returns 0 on Success, <0 on Failure + */ +int mei_verify_lprime(struct mei_hdcp_data *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 } }; + enum me_hdcp_status status; + struct device *dev; + ssize_t byte; + + if (!data || !rx_lprime) + return -EINVAL; + + /* check for the mei_device enabled or not */ + if (!mei_cldev_active_and_enabled(data->cldev)) + return -ENODEV; + + dev = &data->cldev->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)); + + /* Request to ME */ + byte = mei_cldev_send(data->cldev, (u8 *)&verify_lprime_in, + sizeof(verify_lprime_in)); + if (byte < 0) { + dev_err(dev, "mei_cldev_send failed. %d\n", (int)byte); + return byte; + } + + /* Response from ME */ + byte = mei_cldev_recv(data->cldev, (u8 *)&verify_lprime_out, + sizeof(verify_lprime_out)); + if (byte < 0) { + dev_err(dev, "mei_cldev_recv failed. %d\n", (int)byte); + return byte; + } + + status = (enum me_hdcp_status)verify_lprime_out.header.status; + if (status != ME_HDCP_STATUS_SUCCESS) { + dev_err(dev, "ME cmd 0x%08X failed. status: 0x%X\n", + WIRED_VALIDATE_LOCALITY, status); + return -1; + } + return 0; +} +EXPORT_SYMBOL(mei_verify_lprime); + static int mei_hdcp_probe(struct mei_cl_device *cldev, const struct mei_cl_device_id *id) { diff --git a/include/linux/mei_hdcp.h b/include/linux/mei_hdcp.h index fd8a26dddacb..d8c2b440cd81 100644 --- a/include/linux/mei_hdcp.h +++ b/include/linux/mei_hdcp.h @@ -117,6 +117,8 @@ int mei_store_pairing_info(struct mei_hdcp_data *data, struct hdcp2_ake_send_pairing_info *pairing_info); int mei_initiate_locality_check(struct mei_hdcp_data *data, struct hdcp2_lc_init *lc_init_data); +int mei_verify_lprime(struct mei_hdcp_data *data, + struct hdcp2_lc_send_lprime *rx_lprime); #else static inline int mei_hdcp_cldev_get_reference(void *client_data, @@ -164,5 +166,11 @@ int mei_initiate_locality_check(struct mei_hdcp_data *data, { return -ENODEV; } +static inline +int mei_verify_lprime(struct mei_hdcp_data *data, + struct hdcp2_lc_send_lprime *rx_lprime) +{ + return -ENODEV; +} #endif /* defined (CONFIG_INTEL_MEI_HDCP) */ #endif /* defined (_LINUX_MEI_HDCP_H) */
Request to ME to prepare the encrypted session key.
On Success, ME provides Encrypted session key. Functions populates the HDCP2.2 authentication msg SKE_Send_Eks.
v2: Rebased.
Signed-off-by: Ramalingam C ramalingam.c@intel.com --- drivers/misc/mei/hdcp/mei_hdcp.c | 67 ++++++++++++++++++++++++++++++++++++++++ include/linux/mei_hdcp.h | 8 +++++ 2 files changed, 75 insertions(+)
diff --git a/drivers/misc/mei/hdcp/mei_hdcp.c b/drivers/misc/mei/hdcp/mei_hdcp.c index c3c8b9a28498..fbb88a56e10c 100644 --- a/drivers/misc/mei/hdcp/mei_hdcp.c +++ b/drivers/misc/mei/hdcp/mei_hdcp.c @@ -468,6 +468,73 @@ int mei_verify_lprime(struct mei_hdcp_data *data, } EXPORT_SYMBOL(mei_verify_lprime);
+/** + * mei_get_session_key: + * Function to prepare SKE_Send_Eks. + * + * @data : Intel HW specific Data + * @ske_data : Pointer for SKE_Send_Eks. + * + * Returns 0 on Success, <0 on Failure + */ +int mei_get_session_key(struct mei_hdcp_data *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 } }; + enum me_hdcp_status status; + struct device *dev; + ssize_t byte; + + if (!data || !ske_data) + return -EINVAL; + + /* check for the mei_device enabled or not */ + if (!mei_cldev_active_and_enabled(data->cldev)) + return -ENODEV; + + dev = &data->cldev->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; + + /* Request to ME */ + byte = mei_cldev_send(data->cldev, (u8 *)&get_skey_in, + sizeof(get_skey_in)); + if (byte < 0) { + dev_err(dev, "mei_cldev_send failed. %d\n", (int)byte); + return byte; + } + + /* Response from ME */ + byte = mei_cldev_recv(data->cldev, (u8 *)&get_skey_out, + sizeof(get_skey_out)); + + if (byte < 0) { + dev_err(dev, "mei_cldev_recv failed. %d\n", (int)byte); + return byte; + } + + status = (enum me_hdcp_status)get_skey_out.header.status; + if (status != ME_HDCP_STATUS_SUCCESS) { + dev_err(dev, "ME cmd 0x%08X failed. status: 0x%X\n", + WIRED_GET_SESSION_KEY, status); + return -1; + } + + 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; +} +EXPORT_SYMBOL(mei_get_session_key); + static int mei_hdcp_probe(struct mei_cl_device *cldev, const struct mei_cl_device_id *id) { diff --git a/include/linux/mei_hdcp.h b/include/linux/mei_hdcp.h index d8c2b440cd81..193f23ba8fbc 100644 --- a/include/linux/mei_hdcp.h +++ b/include/linux/mei_hdcp.h @@ -119,6 +119,8 @@ int mei_initiate_locality_check(struct mei_hdcp_data *data, struct hdcp2_lc_init *lc_init_data); int mei_verify_lprime(struct mei_hdcp_data *data, struct hdcp2_lc_send_lprime *rx_lprime); +int mei_get_session_key(struct mei_hdcp_data *data, + struct hdcp2_ske_send_eks *ske_data); #else static inline int mei_hdcp_cldev_get_reference(void *client_data, @@ -172,5 +174,11 @@ int mei_verify_lprime(struct mei_hdcp_data *data, { return -ENODEV; } +static inline +int mei_get_session_key(struct mei_hdcp_data *data, + struct hdcp2_ske_send_eks *ske_data) +{ + return -ENODEV; +} #endif /* defined (CONFIG_INTEL_MEI_HDCP) */ #endif /* defined (_LINUX_MEI_HDCP_H) */
Request ot ME to verify the downatream 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.
Signed-off-by: Ramalingam C ramalingam.c@intel.com --- drivers/misc/mei/hdcp/mei_hdcp.c | 84 ++++++++++++++++++++++++++++++++++++++++ include/linux/mei_hdcp.h | 13 +++++++ 2 files changed, 97 insertions(+)
diff --git a/drivers/misc/mei/hdcp/mei_hdcp.c b/drivers/misc/mei/hdcp/mei_hdcp.c index fbb88a56e10c..02c90770dcb6 100644 --- a/drivers/misc/mei/hdcp/mei_hdcp.c +++ b/drivers/misc/mei/hdcp/mei_hdcp.c @@ -535,6 +535,90 @@ int mei_get_session_key(struct mei_hdcp_data *data, } EXPORT_SYMBOL(mei_get_session_key);
+/** + * mei_repeater_check_flow_prepare_ack: + * Function to validate the Downstream topology and prepare rep_ack. + * + * @data : Intel HW specific Data + * @rep_topology : Pointer for Receiver Id List to be validated. + * @rep_send_ack : Pointer for repeater ack + * + * Returns 0 on Success, <0 on Failure + */ + +int +mei_repeater_check_flow_prepare_ack(struct mei_hdcp_data *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 } }; + enum me_hdcp_status status; + struct device *dev; + ssize_t byte; + + if (!rep_topology || !rep_send_ack || !data) + return -EINVAL; + + /* check for the mei_device enabled or not */ + if (!mei_cldev_active_and_enabled(data->cldev)) + return -ENODEV; + + dev = &data->cldev->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_LPRIME_HALF_LEN); + memcpy(verify_repeater_in.receiver_ids, rep_topology->receiver_ids, + HDCP_2_2_RECEIVER_IDS_MAX_LEN); + + /* Request to ME */ + byte = mei_cldev_send(data->cldev, (u8 *)&verify_repeater_in, + sizeof(verify_repeater_in)); + if (byte < 0) { + dev_err(dev, "mei_cldev_send failed. %d\n", (int)byte); + return byte; + } + + /* Response from ME */ + byte = mei_cldev_recv(data->cldev, (u8 *)&verify_repeater_out, + sizeof(verify_repeater_out)); + if (byte < 0) { + dev_err(dev, "mei_cldev_recv failed. %d\n", (int)byte); + return byte; + } + + status = (enum me_hdcp_status)verify_repeater_out.header.status; + if (status != ME_HDCP_STATUS_SUCCESS) { + dev_err(dev, "ME cmd 0x%08X failed. status: 0x%X\n", + WIRED_VERIFY_REPEATER, status); + return -1; + } + + /* + * Need to send the last byte of the V prime back to + * the Repeater + */ + memcpy(rep_send_ack->v, verify_repeater_out.v, + HDCP_2_2_LPRIME_HALF_LEN); + rep_send_ack->msg_id = HDCP_2_2_REP_SEND_ACK; + return 0; +} +EXPORT_SYMBOL(mei_repeater_check_flow_prepare_ack); + static int mei_hdcp_probe(struct mei_cl_device *cldev, const struct mei_cl_device_id *id) { diff --git a/include/linux/mei_hdcp.h b/include/linux/mei_hdcp.h index 193f23ba8fbc..c8f6fc90f475 100644 --- a/include/linux/mei_hdcp.h +++ b/include/linux/mei_hdcp.h @@ -121,6 +121,11 @@ int mei_verify_lprime(struct mei_hdcp_data *data, struct hdcp2_lc_send_lprime *rx_lprime); int mei_get_session_key(struct mei_hdcp_data *data, struct hdcp2_ske_send_eks *ske_data); +int +mei_repeater_check_flow_prepare_ack(struct mei_hdcp_data *data, + struct hdcp2_rep_send_receiverid_list + *rep_topology, + struct hdcp2_rep_send_ack *rep_send_ack); #else static inline int mei_hdcp_cldev_get_reference(void *client_data, @@ -180,5 +185,13 @@ int mei_get_session_key(struct mei_hdcp_data *data, { return -ENODEV; } +static inline int +mei_repeater_check_flow_prepare_ack(struct mei_hdcp_data *data, + struct hdcp2_rep_send_receiverid_list + *rep_topology, + struct hdcp2_rep_send_ack *rep_send_ack) +{ + return -ENODEV; +} #endif /* defined (CONFIG_INTEL_MEI_HDCP) */ #endif /* defined (_LINUX_MEI_HDCP_H) */
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.
Signed-off-by: Ramalingam C ramalingam.c@intel.com --- drivers/misc/mei/hdcp/mei_hdcp.c | 85 ++++++++++++++++++++++++++++++++++++++++ include/linux/mei_hdcp.h | 8 ++++ 2 files changed, 93 insertions(+)
diff --git a/drivers/misc/mei/hdcp/mei_hdcp.c b/drivers/misc/mei/hdcp/mei_hdcp.c index 02c90770dcb6..0e70213cded8 100644 --- a/drivers/misc/mei/hdcp/mei_hdcp.c +++ b/drivers/misc/mei/hdcp/mei_hdcp.c @@ -619,6 +619,91 @@ mei_repeater_check_flow_prepare_ack(struct mei_hdcp_data *data, } EXPORT_SYMBOL(mei_repeater_check_flow_prepare_ack);
+static inline void reverse_endianness(u8 *dest, size_t dst_sz, u8 *src) +{ + u32 index; + + if (dest != NULL && dst_sz != 0) { + for (index = 0; index < dst_sz && index < sizeof(u32); + index++) { + dest[dst_sz - index - 1] = src[index]; + } + } +} + +/** + * mei_verify_mprime: + * Function to verify mprime. + * + * @data : Intel HW specific Data + * @stream_ready : pointer for RepeaterAuth_Stream_Ready message. + * + * Returns 0 on Success, <0 on Failure + */ +int mei_verify_mprime(struct mei_hdcp_data *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 } }; + enum me_hdcp_status status; + struct device *dev; + ssize_t byte; + + if (!stream_ready || !data) + return -EINVAL; + + /* check for the mei_device enabled or not */ + if (!mei_cldev_active_and_enabled(data->cldev)) + return -ENODEV; + + dev = &data->cldev->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); + + /* Request to ME */ + byte = mei_cldev_send(data->cldev, (u8 *)&verify_mprime_in, + sizeof(verify_mprime_in)); + if (byte < 0) { + dev_err(dev, "mei_cldev_send failed. %d\n", (int)byte); + return byte; + } + + /* Response from ME */ + byte = mei_cldev_recv(data->cldev, (u8 *)&verify_mprime_out, + sizeof(verify_mprime_out)); + if (byte < 0) { + dev_err(dev, "mei_cldev_recv failed. %d\n", (int)byte); + return byte; + } + + status = (enum me_hdcp_status)verify_mprime_out.header.status; + if (status != ME_HDCP_STATUS_SUCCESS) { + dev_err(dev, "ME cmd 0x%08X failed. status: 0x%X\n", + WIRED_REPEATER_AUTH_STREAM_REQ, status); + return -1; + } + return 0; +} +EXPORT_SYMBOL(mei_verify_mprime); + static int mei_hdcp_probe(struct mei_cl_device *cldev, const struct mei_cl_device_id *id) { diff --git a/include/linux/mei_hdcp.h b/include/linux/mei_hdcp.h index c8f6fc90f475..560fc62b2b41 100644 --- a/include/linux/mei_hdcp.h +++ b/include/linux/mei_hdcp.h @@ -126,6 +126,8 @@ mei_repeater_check_flow_prepare_ack(struct mei_hdcp_data *data, struct hdcp2_rep_send_receiverid_list *rep_topology, struct hdcp2_rep_send_ack *rep_send_ack); +int mei_verify_mprime(struct mei_hdcp_data *data, + struct hdcp2_rep_stream_ready *stream_ready); #else static inline int mei_hdcp_cldev_get_reference(void *client_data, @@ -193,5 +195,11 @@ mei_repeater_check_flow_prepare_ack(struct mei_hdcp_data *data, { return -ENODEV; } +static inline +int mei_verify_mprime(struct mei_hdcp_data *data, + struct hdcp2_rep_stream_ready *stream_ready) +{ + return -ENODEV; +} #endif /* defined (CONFIG_INTEL_MEI_HDCP) */ #endif /* defined (_LINUX_MEI_HDCP_H) */
Request to ME to configure a port as authenticated.
On Success, ME FW will mark th eport as authenticated and provides HDCP chiper of the port with the encryption keys.
Enabling the Authentication can be requested once the all stages of HDCP2.2 authentication is completed by interating with ME FW.
Only after this stage, driver can enable the HDCP encryption for the port, through HW registers.
v2: Rebased.
Signed-off-by: Ramalingam C ramalingam.c@intel.com --- drivers/misc/mei/hdcp/mei_hdcp.c | 61 ++++++++++++++++++++++++++++++++++++++++ include/linux/mei_hdcp.h | 5 ++++ 2 files changed, 66 insertions(+)
diff --git a/drivers/misc/mei/hdcp/mei_hdcp.c b/drivers/misc/mei/hdcp/mei_hdcp.c index 0e70213cded8..c73715ba9693 100644 --- a/drivers/misc/mei/hdcp/mei_hdcp.c +++ b/drivers/misc/mei/hdcp/mei_hdcp.c @@ -704,6 +704,67 @@ int mei_verify_mprime(struct mei_hdcp_data *data, } EXPORT_SYMBOL(mei_verify_mprime);
+/** + * mei_enable_hdcp_authentication: + * Function to request ME FW to mark a port as authenticated. + * + * @data : Intel HW specific Data + * + * Returns 0 on Success, <0 on Failure + */ +int mei_enable_hdcp_authentication(struct mei_hdcp_data *data) +{ + struct wired_cmd_enable_auth_in enable_auth_in = { { 0 } }; + struct wired_cmd_enable_auth_out enable_auth_out = { { 0 } }; + enum me_hdcp_status status; + struct device *dev; + ssize_t byte; + + if (!data) + return -EINVAL; + + if (!mei_cldev_active_and_enabled(data->cldev)) + return -ENODEV; + + dev = &data->cldev->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; + + /* Fill in the In Data */ + 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; + + /* Request to ME */ + byte = mei_cldev_send(data->cldev, (u8 *)&enable_auth_in, + sizeof(enable_auth_in)); + if (byte < 0) { + dev_err(dev, "mei_cldev_send failed. %d\n", (int)byte); + return byte; + } + + /* Response from ME */ + byte = mei_cldev_recv(data->cldev, (u8 *)&enable_auth_out, + sizeof(enable_auth_out)); + if (byte < 0) { + dev_err(dev, "mei_cldev_recv failed. %d\n", (int)byte); + return byte; + } + + status = (enum me_hdcp_status)enable_auth_out.header.status; + if (status != ME_HDCP_STATUS_SUCCESS) { + dev_err(dev, "ME cmd 0x%08X failed. status: 0x%X\n", + WIRED_ENABLE_AUTH, status); + return -1; + } + return 0; +} +EXPORT_SYMBOL(mei_enable_hdcp_authentication); + static int mei_hdcp_probe(struct mei_cl_device *cldev, const struct mei_cl_device_id *id) { diff --git a/include/linux/mei_hdcp.h b/include/linux/mei_hdcp.h index 560fc62b2b41..cb8bf3b0f022 100644 --- a/include/linux/mei_hdcp.h +++ b/include/linux/mei_hdcp.h @@ -128,6 +128,7 @@ mei_repeater_check_flow_prepare_ack(struct mei_hdcp_data *data, struct hdcp2_rep_send_ack *rep_send_ack); int mei_verify_mprime(struct mei_hdcp_data *data, struct hdcp2_rep_stream_ready *stream_ready); +int mei_enable_hdcp_authentication(struct mei_hdcp_data *data); #else static inline int mei_hdcp_cldev_get_reference(void *client_data, @@ -201,5 +202,9 @@ int mei_verify_mprime(struct mei_hdcp_data *data, { return -ENODEV; } +static inline int mei_enable_hdcp_authentication(struct mei_hdcp_data *data) +{ + return -ENODEV; +} #endif /* defined (CONFIG_INTEL_MEI_HDCP) */ #endif /* defined (_LINUX_MEI_HDCP_H) */
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.
Signed-off-by: Ramalingam C ramalingam.c@intel.com --- drivers/misc/mei/hdcp/mei_hdcp.c | 63 ++++++++++++++++++++++++++++++++++++++++ include/linux/mei_hdcp.h | 5 ++++ 2 files changed, 68 insertions(+)
diff --git a/drivers/misc/mei/hdcp/mei_hdcp.c b/drivers/misc/mei/hdcp/mei_hdcp.c index c73715ba9693..e237ddb44b9d 100644 --- a/drivers/misc/mei/hdcp/mei_hdcp.c +++ b/drivers/misc/mei/hdcp/mei_hdcp.c @@ -765,6 +765,69 @@ int mei_enable_hdcp_authentication(struct mei_hdcp_data *data) } EXPORT_SYMBOL(mei_enable_hdcp_authentication);
+/** + * me_close_hdcp_session: + * Function to close the Wired HDCP Tx session of ME FW. + * This also disables the authenticated state of the port. + * + * @data : Intel HW specific Data + * + * Returns 0 on Success, <0 on Failure + */ +int mei_close_hdcp_session(struct mei_hdcp_data *data) +{ + struct wired_cmd_close_session_in session_close_in = { { 0 } }; + struct wired_cmd_close_session_out session_close_out = { { 0 } }; + enum me_hdcp_status status; + struct device *dev; + ssize_t byte; + + if (!data) + return -EINVAL; + + /* check for the mei_device enabled or not */ + if (!mei_cldev_active_and_enabled(data->cldev)) + return -ENODEV; + + dev = &data->cldev->dev; + + /* Fill header details */ + 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; + + /* Fill in the In Data */ + session_close_in.port.integrated_port_type = data->port_type; + session_close_in.port.physical_port = data->port; + + + /* Request to ME */ + byte = mei_cldev_send(data->cldev, (u8 *)&session_close_in, + sizeof(session_close_in)); + if (byte < 0) { + dev_err(dev, "mei_cldev_send failed. %d\n", (int)byte); + return byte; + } + + /* Response from ME */ + byte = mei_cldev_recv(data->cldev, (u8 *)&session_close_out, + sizeof(session_close_out)); + if (byte < 0) { + dev_err(dev, "mei_cldev_recv failed. %d\n", (int)byte); + return byte; + } + + status = (enum me_hdcp_status)session_close_out.header.status; + if (status != ME_HDCP_STATUS_SUCCESS) { + dev_err(dev, "Session Close Failed. status: 0x%X\n", status); + return -1; + } + return 0; +} +EXPORT_SYMBOL(mei_close_hdcp_session); + static int mei_hdcp_probe(struct mei_cl_device *cldev, const struct mei_cl_device_id *id) { diff --git a/include/linux/mei_hdcp.h b/include/linux/mei_hdcp.h index cb8bf3b0f022..031251a1424a 100644 --- a/include/linux/mei_hdcp.h +++ b/include/linux/mei_hdcp.h @@ -129,6 +129,7 @@ mei_repeater_check_flow_prepare_ack(struct mei_hdcp_data *data, int mei_verify_mprime(struct mei_hdcp_data *data, struct hdcp2_rep_stream_ready *stream_ready); int mei_enable_hdcp_authentication(struct mei_hdcp_data *data); +int mei_close_hdcp_session(struct mei_hdcp_data *data); #else static inline int mei_hdcp_cldev_get_reference(void *client_data, @@ -206,5 +207,9 @@ static inline int mei_enable_hdcp_authentication(struct mei_hdcp_data *data) { return -ENODEV; } +static inline int mei_close_hdcp_session(struct mei_hdcp_data *data) +{ + return -ENODEV; +} #endif /* defined (CONFIG_INTEL_MEI_HDCP) */ #endif /* defined (_LINUX_MEI_HDCP_H) */
Considering the upcoming significant no HDCP2.2 variables, it will be clean to have separate struct fo HDCP.
New structure called intel_hdcp is introduced.
v2: struct hdcp statically allocated. [Sean Paul] enable and disable function parameters are retained.[Sean Paul]
Signed-off-by: Ramalingam C ramalingam.c@intel.com --- drivers/gpu/drm/i915/intel_display.c | 7 +-- drivers/gpu/drm/i915/intel_drv.h | 14 ++++-- drivers/gpu/drm/i915/intel_hdcp.c | 94 ++++++++++++++++++++---------------- 3 files changed, 66 insertions(+), 49 deletions(-)
diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 65c8487be7c7..197639f39765 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -15396,9 +15396,10 @@ static void intel_hpd_poll_fini(struct drm_device *dev) for_each_intel_connector_iter(connector, &conn_iter) { if (connector->modeset_retry_work.func) cancel_work_sync(&connector->modeset_retry_work); - if (connector->hdcp_shim) { - cancel_delayed_work_sync(&connector->hdcp_check_work); - cancel_work_sync(&connector->hdcp_prop_work); + if (connector->hdcp.hdcp_shim) { + cancel_delayed_work_sync( + &connector->hdcp.hdcp_check_work); + cancel_work_sync(&connector->hdcp.hdcp_prop_work); } } drm_connector_list_iter_end(&conn_iter); diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index 8f38e584d375..1b2560ec52ab 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -376,6 +376,14 @@ struct intel_hdcp_shim { bool *hdcp_capable); };
+struct intel_hdcp { + const struct intel_hdcp_shim *hdcp_shim; + struct mutex hdcp_mutex; + uint64_t hdcp_value; /* protected by hdcp_mutex */ + struct delayed_work hdcp_check_work; + struct work_struct hdcp_prop_work; +}; + struct intel_connector { struct drm_connector base; /* @@ -408,11 +416,7 @@ struct intel_connector { /* Work struct to schedule a uevent on link train failure */ struct work_struct modeset_retry_work;
- const struct intel_hdcp_shim *hdcp_shim; - struct mutex hdcp_mutex; - uint64_t hdcp_value; /* protected by hdcp_mutex */ - struct delayed_work hdcp_check_work; - struct work_struct hdcp_prop_work; + struct intel_hdcp hdcp; };
struct intel_digital_connector_state { diff --git a/drivers/gpu/drm/i915/intel_hdcp.c b/drivers/gpu/drm/i915/intel_hdcp.c index 14ca5d3057a7..1cca4f349064 100644 --- a/drivers/gpu/drm/i915/intel_hdcp.c +++ b/drivers/gpu/drm/i915/intel_hdcp.c @@ -547,6 +547,7 @@ struct intel_digital_port *conn_to_dig_port(struct intel_connector *connector)
static int _intel_hdcp_disable(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; @@ -562,7 +563,7 @@ static int _intel_hdcp_disable(struct intel_connector *connector) return -ETIMEDOUT; }
- ret = connector->hdcp_shim->toggle_signalling(intel_dig_port, false); + ret = hdcp->hdcp_shim->toggle_signalling(intel_dig_port, false); if (ret) { DRM_ERROR("Failed to disable HDCP signalling\n"); return ret; @@ -574,6 +575,7 @@ static int _intel_hdcp_disable(struct intel_connector *connector)
static int _intel_hdcp_enable(struct intel_connector *connector) { + struct intel_hdcp *hdcp = &connector->hdcp; struct drm_i915_private *dev_priv = connector->base.dev->dev_private; int i, ret, tries = 3;
@@ -599,7 +601,7 @@ static int _intel_hdcp_enable(struct intel_connector *connector) /* Incase of authentication failures, HDCP spec expects reauth. */ for (i = 0; i < tries; i++) { ret = intel_hdcp_auth(conn_to_dig_port(connector), - connector->hdcp_shim); + hdcp->hdcp_shim); if (!ret) return 0;
@@ -615,36 +617,42 @@ static int _intel_hdcp_enable(struct intel_connector *connector)
static void intel_hdcp_check_work(struct work_struct *work) { - struct intel_connector *connector = container_of(to_delayed_work(work), + struct intel_hdcp *hdcp = container_of(to_delayed_work(work), + struct intel_hdcp, + hdcp_check_work); + struct intel_connector *connector = container_of(hdcp, struct intel_connector, - hdcp_check_work); + hdcp); + if (!intel_hdcp_check_link(connector)) - schedule_delayed_work(&connector->hdcp_check_work, + schedule_delayed_work(&hdcp->hdcp_check_work, DRM_HDCP_CHECK_PERIOD_MS); }
static void intel_hdcp_prop_work(struct work_struct *work) { - struct intel_connector *connector = container_of(work, + struct intel_hdcp *hdcp = container_of(work, struct intel_hdcp, + hdcp_prop_work); + struct intel_connector *connector = container_of(hdcp, struct intel_connector, - hdcp_prop_work); + hdcp); struct drm_device *dev = connector->base.dev; struct drm_connector_state *state;
drm_modeset_lock(&dev->mode_config.connection_mutex, NULL); - mutex_lock(&connector->hdcp_mutex); + mutex_lock(&hdcp->hdcp_mutex);
/* * This worker is only used to flip between ENABLED/DESIRED. Either of * those to UNDESIRED is handled by core. If hdcp_value == UNDESIRED, * we're running just after hdcp has been disabled, so just exit */ - if (connector->hdcp_value != DRM_MODE_CONTENT_PROTECTION_UNDESIRED) { + if (hdcp->hdcp_value != DRM_MODE_CONTENT_PROTECTION_UNDESIRED) { state = connector->base.state; - state->content_protection = connector->hdcp_value; + state->content_protection = hdcp->hdcp_value; }
- mutex_unlock(&connector->hdcp_mutex); + mutex_unlock(&hdcp->hdcp_mutex); drm_modeset_unlock(&dev->mode_config.connection_mutex); }
@@ -658,6 +666,7 @@ bool is_hdcp_supported(struct drm_i915_private *dev_priv, enum port port) int intel_hdcp_init(struct intel_connector *connector, const struct intel_hdcp_shim *hdcp_shim) { + struct intel_hdcp *hdcp = &connector->hdcp; int ret;
ret = drm_connector_attach_content_protection_property( @@ -665,51 +674,53 @@ int intel_hdcp_init(struct intel_connector *connector, if (ret) return ret;
- connector->hdcp_shim = hdcp_shim; - mutex_init(&connector->hdcp_mutex); - INIT_DELAYED_WORK(&connector->hdcp_check_work, intel_hdcp_check_work); - INIT_WORK(&connector->hdcp_prop_work, intel_hdcp_prop_work); + hdcp->hdcp_shim = hdcp_shim; + mutex_init(&hdcp->hdcp_mutex); + INIT_DELAYED_WORK(&hdcp->hdcp_check_work, intel_hdcp_check_work); + INIT_WORK(&hdcp->hdcp_prop_work, intel_hdcp_prop_work); return 0; }
int intel_hdcp_enable(struct intel_connector *connector) { + struct intel_hdcp *hdcp = &connector->hdcp; int ret;
- if (!connector->hdcp_shim) + if (!hdcp->hdcp_shim) return -ENOENT;
- mutex_lock(&connector->hdcp_mutex); + mutex_lock(&hdcp->hdcp_mutex);
ret = _intel_hdcp_enable(connector); if (ret) goto out;
- connector->hdcp_value = DRM_MODE_CONTENT_PROTECTION_ENABLED; - schedule_work(&connector->hdcp_prop_work); - schedule_delayed_work(&connector->hdcp_check_work, + hdcp->hdcp_value = DRM_MODE_CONTENT_PROTECTION_ENABLED; + schedule_work(&hdcp->hdcp_prop_work); + schedule_delayed_work(&hdcp->hdcp_check_work, DRM_HDCP_CHECK_PERIOD_MS); out: - mutex_unlock(&connector->hdcp_mutex); + mutex_unlock(&hdcp->hdcp_mutex); return ret; }
int intel_hdcp_disable(struct intel_connector *connector) { + struct intel_hdcp *hdcp = &connector->hdcp; int ret = 0;
- if (!connector->hdcp_shim) + if (!hdcp->hdcp_shim) return -ENOENT;
- mutex_lock(&connector->hdcp_mutex); + mutex_lock(&hdcp->hdcp_mutex);
- if (connector->hdcp_value != DRM_MODE_CONTENT_PROTECTION_UNDESIRED) { - connector->hdcp_value = DRM_MODE_CONTENT_PROTECTION_UNDESIRED; + if (hdcp->hdcp_value != DRM_MODE_CONTENT_PROTECTION_UNDESIRED) { + hdcp->hdcp_value = DRM_MODE_CONTENT_PROTECTION_UNDESIRED; ret = _intel_hdcp_disable(connector); }
- mutex_unlock(&connector->hdcp_mutex); - cancel_delayed_work_sync(&connector->hdcp_check_work); + mutex_unlock(&hdcp->hdcp_mutex); + cancel_delayed_work_sync(&hdcp->hdcp_check_work); return ret; }
@@ -749,17 +760,18 @@ void intel_hdcp_atomic_check(struct drm_connector *connector, /* 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 (!connector->hdcp_shim) + if (!hdcp->hdcp_shim) return -ENOENT;
- mutex_lock(&connector->hdcp_mutex); + mutex_lock(&hdcp->hdcp_mutex);
- if (connector->hdcp_value == DRM_MODE_CONTENT_PROTECTION_UNDESIRED) + if (hdcp->hdcp_value == DRM_MODE_CONTENT_PROTECTION_UNDESIRED) goto out;
if (!(I915_READ(PORT_HDCP_STATUS(port)) & HDCP_STATUS_ENC)) { @@ -767,17 +779,17 @@ int intel_hdcp_check_link(struct intel_connector *connector) connector->base.name, connector->base.base.id, I915_READ(PORT_HDCP_STATUS(port))); ret = -ENXIO; - connector->hdcp_value = DRM_MODE_CONTENT_PROTECTION_DESIRED; - schedule_work(&connector->hdcp_prop_work); + hdcp->hdcp_value = DRM_MODE_CONTENT_PROTECTION_DESIRED; + schedule_work(&hdcp->hdcp_prop_work); goto out; }
- if (connector->hdcp_shim->check_link(intel_dig_port)) { - if (connector->hdcp_value != + if (hdcp->hdcp_shim->check_link(intel_dig_port)) { + if (hdcp->hdcp_value != DRM_MODE_CONTENT_PROTECTION_UNDESIRED) { - connector->hdcp_value = + hdcp->hdcp_value = DRM_MODE_CONTENT_PROTECTION_ENABLED; - schedule_work(&connector->hdcp_prop_work); + schedule_work(&hdcp->hdcp_prop_work); } goto out; } @@ -788,20 +800,20 @@ int intel_hdcp_check_link(struct intel_connector *connector) ret = _intel_hdcp_disable(connector); if (ret) { DRM_ERROR("Failed to disable hdcp (%d)\n", ret); - connector->hdcp_value = DRM_MODE_CONTENT_PROTECTION_DESIRED; - schedule_work(&connector->hdcp_prop_work); + hdcp->hdcp_value = DRM_MODE_CONTENT_PROTECTION_DESIRED; + schedule_work(&hdcp->hdcp_prop_work); goto out; }
ret = _intel_hdcp_enable(connector); if (ret) { DRM_ERROR("Failed to enable hdcp (%d)\n", ret); - connector->hdcp_value = DRM_MODE_CONTENT_PROTECTION_DESIRED; - schedule_work(&connector->hdcp_prop_work); + hdcp->hdcp_value = DRM_MODE_CONTENT_PROTECTION_DESIRED; + schedule_work(&hdcp->hdcp_prop_work); goto out; }
out: - mutex_unlock(&connector->hdcp_mutex); + mutex_unlock(&hdcp->hdcp_mutex); return ret; }
For upcoming implementation of HDCP2.2 in I915, important variable required for HDCP2.2 are defined.
HDCP_shim is extended to support encoder specific HDCP2.2 flows.
v2: 1.4 shim is extended to support hdcp2.2. [Sean Paul] platform's/panel's hdcp ver capability is removed. [Sean Paul] mei references in i915_private are moved to later patches. [Chris Wilson]
Signed-off-by: Ramalingam C ramalingam.c@intel.com --- drivers/gpu/drm/i915/intel_drv.h | 60 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 60 insertions(+)
diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index 1b2560ec52ab..67f507bb4d0d 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> @@ -374,6 +375,32 @@ 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); + + /* 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, + uint8_t 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 Chiper for En/De-Cryption. Not applicable for HDMI. + */ + 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); + + /* Detects whether Panel is HDCP2.2 capable */ + int (*hdcp_2_2_capable)(struct intel_digital_port *intel_dig_port, + bool *capable); + + /* Detects the HDCP protocol(DP/HDMI) required on the port */ + enum hdcp_protocol (*hdcp_protocol)(void); };
struct intel_hdcp { @@ -382,6 +409,39 @@ struct intel_hdcp { uint64_t hdcp_value; /* protected by hdcp_mutex */ struct delayed_work hdcp_check_work; struct work_struct hdcp_prop_work; + + /** HDCP2.2 related definitions **/ + bool 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; + + bool is_paired; + bool 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. + */ + uint32_t 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. + */ + uint32_t seq_num_m; + + /* mei interface related information */ + struct mei_hdcp_data mei_data; + + struct delayed_work hdcp2_check_work; };
struct intel_connector {
Intel HDCP2.2 registers are defined with addr offsets and bit details.
v2: Replaced the arith calc with _PICK [Sean Paul]
Signed-off-by: Ramalingam C ramalingam.c@intel.com --- drivers/gpu/drm/i915/i915_reg.h | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+)
diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h index eea5b2c537d4..08dafd7e9278 100644 --- a/drivers/gpu/drm/i915/i915_reg.h +++ b/drivers/gpu/drm/i915/i915_reg.h @@ -8605,6 +8605,38 @@ enum skl_power_gate { #define HDCP_STATUS_CIPHER BIT(16) #define HDCP_STATUS_FRAME_CNT(x) ((x >> 8) & 0xff)
+/* HDCP2.2 Registers */ +#define _PORTA_HDCP2_BASE 0x66800 +#define _PORTB_HDCP2_BASE 0x66500 +#define _PORTC_HDCP2_BASE 0x66600 +#define _PORTD_HDCP2_BASE 0x66700 +#define _PORTE_HDCP2_BASE 0x66A00 +#define _PORTF_HDCP2_BASE 0x66900 +#define _PORT_HDCP2_BASE(port, x) _MMIO(_PICK(port, \ + _PORTA_HDCP2_BASE, \ + _PORTB_HDCP2_BASE, \ + _PORTC_HDCP2_BASE, \ + _PORTD_HDCP2_BASE, \ + _PORTE_HDCP2_BASE, \ + _PORTF_HDCP2_BASE) + x) + +#define HDCP2_AUTH_DDI(port) _PORT_HDCP2_BASE(port, 0x98) +#define AUTH_LINK_AUTHENTICATED BIT(31) +#define AUTH_LINK_TYPE BIT(30) +#define AUTH_FORCE_CLR_INPUTCTR BIT(19) +#define AUTH_CLR_KEYS BIT(18) + +#define HDCP2_CTR_DDI(port) _PORT_HDCP2_BASE(port, 0xB0) +#define CTL_LINK_ENCRYPTION_REQ BIT(31) + +#define HDCP2_STATUS_DDI(port) _PORT_HDCP2_BASE(port, 0xB4) +#define STREAM_ENCRYPTION_STATUS_A BIT(31) +#define STREAM_ENCRYPTION_STATUS_B BIT(30) +#define STREAM_ENCRYPTION_STATUS_C BIT(29) +#define LINK_TYPE_STATUS BIT(22) +#define LINK_AUTH_STATUS BIT(21) +#define LINK_ENCRYPTION_STATUS BIT(20) + /* Per-pipe DDI Function Control */ #define _TRANS_DDI_FUNC_CTL_A 0x60400 #define _TRANS_DDI_FUNC_CTL_B 0x61400
Adds the wrapper for all mei hdcp2.2 service functions.
v2: Rebased.
Signed-off-by: Ramalingam C ramalingam.c@intel.com --- drivers/gpu/drm/i915/intel_hdcp.c | 194 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 194 insertions(+)
diff --git a/drivers/gpu/drm/i915/intel_hdcp.c b/drivers/gpu/drm/i915/intel_hdcp.c index 1cca4f349064..8e4d281613f0 100644 --- a/drivers/gpu/drm/i915/intel_hdcp.c +++ b/drivers/gpu/drm/i915/intel_hdcp.c @@ -10,10 +10,13 @@ #include <drm/drm_hdcp.h> #include <linux/i2c.h> #include <linux/random.h> +#include <linux/mei_hdcp.h>
#include "intel_drv.h" #include "i915_reg.h"
+#define GET_MEI_DDI_INDEX(port) (((port) == PORT_A) ? DDI_A : \ + (enum physical_port) (port)) #define KEY_LOAD_TRIES 5
static int intel_hdcp_poll_ksv_fifo(struct intel_digital_port *intel_dig_port, @@ -817,3 +820,194 @@ int intel_hdcp_check_link(struct intel_connector *connector) mutex_unlock(&hdcp->hdcp_mutex); return ret; } + +static int +hdcp2_prepare_ake_init(struct intel_hdcp *hdcp, struct hdcp2_ake_init *ake_data) +{ + struct mei_hdcp_data *data = &hdcp->mei_data; + struct intel_connector *connector = container_of(hdcp, + struct intel_connector, + hdcp); + + if (!data->cldev) + return -EINVAL; + + if (data->port == INVALID_PORT && connector->encoder) + data->port = GET_MEI_DDI_INDEX(connector->encoder->port); + + /* Clear ME FW instance for the port, just incase */ + mei_close_hdcp_session(data); + + return mei_initiate_hdcp2_session(data, ake_data); +} + +static int hdcp2_close_mei_session(struct intel_hdcp *hdcp) +{ + struct mei_hdcp_data *data = &hdcp->mei_data; + + if (!data->cldev || data->port == INVALID_PORT) + return -EINVAL; + + return mei_close_hdcp_session(data); +} + +static int +hdcp2_verify_rx_cert_prepare_km(struct intel_hdcp *hdcp, + 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 = &hdcp->mei_data; + int ret; + + if (!data->cldev) + return -EINVAL; + + ret = mei_verify_receiver_cert_prepare_km(data, rx_cert, paired, + ek_pub_km, msg_sz); + if (ret < 0) + mei_close_hdcp_session(data); + + return ret; +} + +static int hdcp2_verify_hprime(struct intel_hdcp *hdcp, + struct hdcp2_ake_send_hprime *rx_hprime) +{ + struct mei_hdcp_data *data = &hdcp->mei_data; + int ret; + + if (!data->cldev) + return -EINVAL; + + ret = mei_verify_hprime(data, rx_hprime); + if (ret < 0) + mei_close_hdcp_session(data); + + return ret; +} + +static int +hdcp2_store_paring_info(struct intel_hdcp *hdcp, + struct hdcp2_ake_send_pairing_info *pairing_info) +{ + struct mei_hdcp_data *data = &hdcp->mei_data; + int ret; + + if (!data->cldev) + return -EINVAL; + + ret = mei_store_pairing_info(data, pairing_info); + if (ret < 0) + mei_close_hdcp_session(data); + + return ret; +} + +static int +hdcp2_prepare_lc_init(struct intel_hdcp *hdcp, struct hdcp2_lc_init *lc_init) +{ + struct mei_hdcp_data *data = &hdcp->mei_data; + int ret; + + if (!data->cldev) + return -EINVAL; + + ret = mei_initiate_locality_check(data, lc_init); + if (ret < 0) + mei_close_hdcp_session(data); + + return ret; +} + +static int +hdcp2_verify_lprime(struct intel_hdcp *hdcp, + struct hdcp2_lc_send_lprime *rx_lprime) +{ + struct mei_hdcp_data *data = &hdcp->mei_data; + int ret; + + if (!data->cldev) + return -EINVAL; + + ret = mei_verify_lprime(data, rx_lprime); + if (ret < 0) + mei_close_hdcp_session(data); + + return ret; +} + +static int hdcp2_prepare_skey(struct intel_hdcp *hdcp, + struct hdcp2_ske_send_eks *ske_data) +{ + struct mei_hdcp_data *data = &hdcp->mei_data; + int ret; + + if (!data->cldev) + return -EINVAL; + + ret = mei_get_session_key(data, ske_data); + if (ret < 0) + mei_close_hdcp_session(data); + + return ret; +} + +static int +hdcp2_verify_rep_topology_prepare_ack( + struct intel_hdcp *hdcp, + struct hdcp2_rep_send_receiverid_list *rep_topology, + struct hdcp2_rep_send_ack *rep_send_ack) +{ + struct mei_hdcp_data *data = &hdcp->mei_data; + int ret; + + if (!data->cldev) + return -EINVAL; + + ret = mei_repeater_check_flow_prepare_ack(data, rep_topology, + rep_send_ack); + if (ret < 0) + mei_close_hdcp_session(data); + + return ret; +} + +static int +hdcp2_verify_mprime(struct intel_hdcp *hdcp, + struct hdcp2_rep_stream_ready *stream_ready) +{ + struct mei_hdcp_data *data = &hdcp->mei_data; + int ret; + + if (!data->cldev) + return -EINVAL; + + ret = mei_verify_mprime(data, stream_ready); + if (ret < 0) + mei_close_hdcp_session(data); + + return ret; +} + + +static int hdcp2_authenticate_port(struct intel_hdcp *hdcp) +{ + struct mei_hdcp_data *data = &hdcp->mei_data; + int ret; + + if (!data->cldev) + return -EINVAL; + + ret = mei_enable_hdcp_authentication(data); + if (ret < 0) + mei_close_hdcp_session(data); + + return ret; +} + +static inline int hdcp2_deauthenticate_port(struct intel_hdcp *hdcp) +{ + return hdcp2_close_mei_session(hdcp); +}
Implements HDCP2.2 authentication for hdcp2.2 receivers, with following steps: Authentication and Key enchange (AKE). Locality Check (LC). Session Key Exchange(SKE). DP Errata for stream type confuguration 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.
Signed-off-by: Ramalingam C ramalingam.c@intel.com --- drivers/gpu/drm/i915/intel_hdcp.c | 184 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 184 insertions(+)
diff --git a/drivers/gpu/drm/i915/intel_hdcp.c b/drivers/gpu/drm/i915/intel_hdcp.c index 8e4d281613f0..6fb9fd4d6e1c 100644 --- a/drivers/gpu/drm/i915/intel_hdcp.c +++ b/drivers/gpu/drm/i915/intel_hdcp.c @@ -18,6 +18,7 @@ #define GET_MEI_DDI_INDEX(port) (((port) == PORT_A) ? DDI_A : \ (enum physical_port) (port)) #define KEY_LOAD_TRIES 5 +#define HDCP2_LC_RETRY_CNT 3
static int intel_hdcp_poll_ksv_fifo(struct intel_digital_port *intel_dig_port, const struct intel_hdcp_shim *shim) @@ -1011,3 +1012,186 @@ static inline int hdcp2_deauthenticate_port(struct intel_hdcp *hdcp) { return hdcp2_close_mei_session(hdcp); } + +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->hdcp_shim; + size_t size; + int ret; + + /* Init for seq_num */ + hdcp->seq_num_v = 0; + hdcp->seq_num_m = 0; + + ret = hdcp2_prepare_ake_init(hdcp, &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]); + + /* + * Here msgs.no_stored_km will hold msgs corresponding to the km + * stored also. + */ + ret = hdcp2_verify_rx_cert_prepare_km(hdcp, &msgs.send_cert, + &hdcp->is_paired, + &msgs.no_stored_km, &size); + if (ret < 0) + return ret; + + 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(hdcp, &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_PARING_INFO, + &msgs.pairing_info, + sizeof(msgs.pairing_info)); + if (ret < 0) + return ret; + + ret = hdcp2_store_paring_info(hdcp, &msgs.pairing_info); + if (ret < 0) + return ret; + hdcp->is_paired = true; + } + 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->hdcp_shim; + int tries = HDCP2_LC_RETRY_CNT, ret, i; + + for (i = 0; i < tries; i++) { + ret = hdcp2_prepare_lc_init(hdcp, &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(hdcp, &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(hdcp, &send_eks); + if (ret < 0) + return ret; + + ret = hdcp->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) +{ + 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->hdcp_shim; + struct hdcp2_dp_errata_stream_type stream_type_msg; + int ret; + + 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 in 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(hdcp); + if (ret < 0) + return ret; + + return ret; +}
Implements the HDCP2.2 repeaters authentication steps such as verifying the downstream topology and sending stream management information.
v2: Rebased.
Signed-off-by: Ramalingam C ramalingam.c@intel.com --- drivers/gpu/drm/i915/intel_hdcp.c | 135 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 135 insertions(+)
diff --git a/drivers/gpu/drm/i915/intel_hdcp.c b/drivers/gpu/drm/i915/intel_hdcp.c index 6fb9fd4d6e1c..53314f8e491a 100644 --- a/drivers/gpu/drm/i915/intel_hdcp.c +++ b/drivers/gpu/drm/i915/intel_hdcp.c @@ -1145,6 +1145,135 @@ static int hdcp2_session_key_exchange(struct intel_connector *connector) return 0; }
+/* + * Lib endianness functions are aligned for 16/32/64 bits. Since here sequence + * num is 24bits developed a small conversion function. + */ +static inline void reverse_endianness(u8 *dest, size_t dst_sz, u8 *src) +{ + u32 index; + + if (dest != NULL && dst_sz != 0) { + for (index = 0; index < dst_sz && index < sizeof(u32); + index++) { + dest[dst_sz - index - 1] = src[index]; + } + } +} + +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->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(hdcp, &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->hdcp_shim; + uint8_t *rx_info; + uint32_t 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 -1; + } + + /* 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 -1; + } + + ret = hdcp2_verify_rep_topology_prepare_ack(hdcp, &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); @@ -1186,6 +1315,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;
Implements the enable and disable functions for HDCP2.2 encryption of the PORT.
v2: intel_wait_for_register is used instead of wait_for. [Chris Wilson]
Signed-off-by: Ramalingam C ramalingam.c@intel.com --- drivers/gpu/drm/i915/intel_hdcp.c | 54 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+)
diff --git a/drivers/gpu/drm/i915/intel_hdcp.c b/drivers/gpu/drm/i915/intel_hdcp.c index 53314f8e491a..38cecf533cb9 100644 --- a/drivers/gpu/drm/i915/intel_hdcp.c +++ b/drivers/gpu/drm/i915/intel_hdcp.c @@ -19,6 +19,7 @@ (enum physical_port) (port)) #define KEY_LOAD_TRIES 5 #define HDCP2_LC_RETRY_CNT 3 +#define TIME_FOR_ENCRYPT_STATUS_CHANGE 32
static int intel_hdcp_poll_ksv_fifo(struct intel_digital_port *intel_dig_port, const struct intel_hdcp_shim *shim) @@ -1330,3 +1331,56 @@ static int hdcp2_authenticate_sink(struct intel_connector *connector)
return ret; } + +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; + + if (I915_READ(HDCP2_STATUS_DDI(port)) & LINK_ENCRYPTION_STATUS) + return 0; + + if (hdcp->hdcp_shim->toggle_signalling) + hdcp->hdcp_shim->toggle_signalling(intel_dig_port, true); + + if (I915_READ(HDCP2_STATUS_DDI(port)) & LINK_AUTH_STATUS) { + + /* Link is Authenticated. Now set for Encryption */ + I915_WRITE(HDCP2_CTR_DDI(port), + I915_READ(HDCP2_CTR_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; + + if (!(I915_READ(HDCP2_STATUS_DDI(port)) & LINK_ENCRYPTION_STATUS)) + return 0; + + I915_WRITE(HDCP2_CTR_DDI(port), + I915_READ(HDCP2_CTR_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 (hdcp->hdcp_shim->toggle_signalling) + hdcp->hdcp_shim->toggle_signalling(intel_dig_port, false); + + return ret; +}
Implements a sequence of enabling and disabling the HDCP2.2 (auth and encryption).
v2: Rebased.
Signed-off-by: Ramalingam C ramalingam.c@intel.com --- drivers/gpu/drm/i915/intel_hdcp.c | 75 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 75 insertions(+)
diff --git a/drivers/gpu/drm/i915/intel_hdcp.c b/drivers/gpu/drm/i915/intel_hdcp.c index 38cecf533cb9..41915d626d20 100644 --- a/drivers/gpu/drm/i915/intel_hdcp.c +++ b/drivers/gpu/drm/i915/intel_hdcp.c @@ -21,6 +21,9 @@ #define HDCP2_LC_RETRY_CNT 3 #define TIME_FOR_ENCRYPT_STATUS_CHANGE 32
+static int _intel_hdcp2_enable(struct intel_connector *connector); +static int _intel_hdcp2_disable(struct intel_connector *connector); + static int intel_hdcp_poll_ksv_fifo(struct intel_digital_port *intel_dig_port, const struct intel_hdcp_shim *shim) { @@ -1384,3 +1387,75 @@ static int hdcp2_disable_encryption(struct intel_connector *connector)
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 */ + hdcp2_deauthenticate_port(&connector->hdcp); + DRM_DEBUG_KMS("HDCP2.2 Auth %d of %d Failed.(%d)\n", + i + 1, tries, ret); + } + + 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); + hdcp2_deauthenticate_port(&connector->hdcp); + } + } + + return ret; +} + +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); + + hdcp2_deauthenticate_port(&connector->hdcp); + + 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_ERROR("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->hdcp_value = DRM_MODE_CONTENT_PROTECTION_ENABLED; + schedule_work(&hdcp->hdcp_prop_work); + + return 0; +}
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.
Signed-off-by: Ramalingam C ramalingam.c@intel.com --- drivers/gpu/drm/i915/intel_hdcp.c | 81 ++++++++++++++++++++++++++++++++++++++- include/drm/drm_hdcp.h | 8 ++++ 2 files changed, 88 insertions(+), 1 deletion(-)
diff --git a/drivers/gpu/drm/i915/intel_hdcp.c b/drivers/gpu/drm/i915/intel_hdcp.c index 41915d626d20..2bc952b828b2 100644 --- a/drivers/gpu/drm/i915/intel_hdcp.c +++ b/drivers/gpu/drm/i915/intel_hdcp.c @@ -23,6 +23,8 @@
static int _intel_hdcp2_enable(struct intel_connector *connector); static int _intel_hdcp2_disable(struct intel_connector *connector); +static void intel_hdcp2_check_work(struct work_struct *work); +static int intel_hdcp2_check_link(struct intel_connector *connector);
static int intel_hdcp_poll_ksv_fifo(struct intel_digital_port *intel_dig_port, const struct intel_hdcp_shim *shim) @@ -1456,6 +1458,83 @@ static int _intel_hdcp2_enable(struct intel_connector *connector)
hdcp->hdcp_value = DRM_MODE_CONTENT_PROTECTION_ENABLED; schedule_work(&hdcp->hdcp_prop_work); - + schedule_delayed_work(&hdcp->hdcp2_check_work, + DRM_HDCP2_CHECK_PERIOD_MS); return 0; } + +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->hdcp_shim) + return -ENOENT; + + mutex_lock(&hdcp->hdcp_mutex); + + if (hdcp->hdcp_value == DRM_MODE_CONTENT_PROTECTION_UNDESIRED) + goto out; + + if (!(I915_READ(HDCP2_STATUS_DDI(port)) & LINK_ENCRYPTION_STATUS)) { + DRM_ERROR("HDCP check failed: link is not encrypted, %x\n", + I915_READ(HDCP2_STATUS_DDI(port))); + ret = -ENXIO; + hdcp->hdcp_value = DRM_MODE_CONTENT_PROTECTION_DESIRED; + schedule_work(&hdcp->hdcp_prop_work); + goto out; + } + + ret = hdcp->hdcp_shim->check_2_2_link(intel_dig_port); + if (!ret) { + if (hdcp->hdcp_value != DRM_MODE_CONTENT_PROTECTION_UNDESIRED) { + hdcp->hdcp_value = DRM_MODE_CONTENT_PROTECTION_ENABLED; + schedule_work(&hdcp->hdcp_prop_work); + } + goto out; + } + + DRM_INFO("[%s:%d] HDCP2.2 link failed, retrying authentication\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->hdcp_value = DRM_MODE_CONTENT_PROTECTION_DESIRED; + schedule_work(&hdcp->hdcp_prop_work); + goto out; + } + + ret = _intel_hdcp2_enable(connector); + if (ret) { + DRM_ERROR("[%s:%d] Failed to enable hdcp2.2 (%d)\n", + connector->base.name, connector->base.base.id, ret); + + hdcp->hdcp_value = DRM_MODE_CONTENT_PROTECTION_DESIRED; + schedule_work(&hdcp->hdcp_prop_work); + goto out; + } + +out: + mutex_unlock(&hdcp->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 = container_of(hdcp, + struct intel_connector, + hdcp); + + if (!intel_hdcp2_check_link(connector)) + schedule_delayed_work(&hdcp->hdcp2_check_work, + DRM_HDCP2_CHECK_PERIOD_MS); +} diff --git a/include/drm/drm_hdcp.h b/include/drm/drm_hdcp.h index f3f28414b189..b0601215c798 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
When repeater notifies a downstream topology change, this patch reauthenticate the repeater alone with out disabling the hdcp encryption. If that fails then complete reauthentication is executed.
v2: Rebased.
Signed-off-by: Ramalingam C ramalingam.c@intel.com --- drivers/gpu/drm/i915/intel_hdcp.c | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-)
diff --git a/drivers/gpu/drm/i915/intel_hdcp.c b/drivers/gpu/drm/i915/intel_hdcp.c index 2bc952b828b2..a5e4678e54bc 100644 --- a/drivers/gpu/drm/i915/intel_hdcp.c +++ b/drivers/gpu/drm/i915/intel_hdcp.c @@ -1497,8 +1497,23 @@ static int intel_hdcp2_check_link(struct intel_connector *connector) goto out; }
- DRM_INFO("[%s:%d] HDCP2.2 link failed, retrying authentication\n", - connector->base.name, connector->base.base.id); + if (ret == DRM_HDCP_TOPOLOGY_CHANGE) { + if (hdcp->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->hdcp_value = DRM_MODE_CONTENT_PROTECTION_ENABLED; + schedule_work(&hdcp->hdcp_prop_work); + goto out; + } + DRM_ERROR("[%s:%d] Repeater topology auth failed.(%d)\n", + connector->base.name, connector->base.base.id, ret); + } else { + DRM_ERROR("[%s:%d] HDCP2.2 link failed, retrying auth\n", + connector->base.name, connector->base.base.id); + }
ret = _intel_hdcp2_disable(connector); if (ret) {
For reusability purpose, this patch implements the hdcp1.4 bksv's read and validation as a functions.
For detecting the HDMI panel's HDCP capability this fucntions will be used.
v2: Rebased.
Signed-off-by: Ramalingam C ramalingam.c@intel.com --- drivers/gpu/drm/i915/intel_hdcp.c | 38 +++++++++++++++++++++++++------------- 1 file changed, 25 insertions(+), 13 deletions(-)
diff --git a/drivers/gpu/drm/i915/intel_hdcp.c b/drivers/gpu/drm/i915/intel_hdcp.c index a5e4678e54bc..2cb50db3b063 100644 --- a/drivers/gpu/drm/i915/intel_hdcp.c +++ b/drivers/gpu/drm/i915/intel_hdcp.c @@ -152,6 +152,27 @@ bool intel_hdcp_is_ksv_valid(u8 *ksv) return true; }
+static inline +int intel_hdcp_read_valid_bksv(struct intel_digital_port *intel_dig_port, + const struct intel_hdcp_shim *shim, u8 *bksv) +{ + int ret, i, tries = 2; + + /* HDCP spec states that we must retry the bksv if it is invalid */ + for (i = 0; i < tries; i++) { + ret = shim->read_bksv(intel_dig_port, bksv); + if (ret) + return ret; + if (intel_hdcp_is_ksv_valid(bksv)) + break; + } + if (i == tries) { + DRM_ERROR("HDCP failed, Bksv is invalid\n"); + return -ENODEV; + } + return 0; +} + /* Implements Part 2 of the HDCP authorization procedure */ static int intel_hdcp_auth_downstream(struct intel_digital_port *intel_dig_port, @@ -411,7 +432,7 @@ static int intel_hdcp_auth(struct intel_digital_port *intel_dig_port, struct drm_i915_private *dev_priv; enum port port; unsigned long r0_prime_gen_start; - int ret, i, tries = 2; + int ret, i; union { u32 reg[2]; u8 shim[DRM_HDCP_AN_LEN]; @@ -469,18 +490,9 @@ static int intel_hdcp_auth(struct intel_digital_port *intel_dig_port,
memset(&bksv, 0, sizeof(bksv));
- /* HDCP spec states that we must retry the bksv if it is invalid */ - for (i = 0; i < tries; i++) { - ret = shim->read_bksv(intel_dig_port, bksv.shim); - if (ret) - return ret; - if (intel_hdcp_is_ksv_valid(bksv.shim)) - break; - } - if (i == tries) { - DRM_ERROR("HDCP failed, Bksv is invalid\n"); - return -ENODEV; - } + ret = intel_hdcp_read_valid_bksv(intel_dig_port, shim, bksv.shim); + if (ret < 0) + return ret;
I915_WRITE(PORT_HDCP_BKSVLO(port), bksv.reg[0]); I915_WRITE(PORT_HDCP_BKSVHI(port), bksv.reg[1]);
Initialize HDCP2.2 support. This includes the mei interface initialization also.
v2: mei interface handle is protected with mutex. [Chris Wilson]
Signed-off-by: Ramalingam C ramalingam.c@intel.com --- drivers/gpu/drm/i915/i915_drv.c | 1 + drivers/gpu/drm/i915/i915_drv.h | 7 +++ drivers/gpu/drm/i915/intel_dp.c | 3 +- drivers/gpu/drm/i915/intel_drv.h | 3 +- drivers/gpu/drm/i915/intel_hdcp.c | 123 +++++++++++++++++++++++++++++++++++++- drivers/gpu/drm/i915/intel_hdmi.c | 2 +- 6 files changed, 135 insertions(+), 4 deletions(-)
diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c index aaa861b51024..d178b3ec0a60 100644 --- a/drivers/gpu/drm/i915/i915_drv.c +++ b/drivers/gpu/drm/i915/i915_drv.c @@ -917,6 +917,7 @@ static int i915_driver_init_early(struct drm_i915_private *dev_priv, mutex_init(&dev_priv->av_mutex); mutex_init(&dev_priv->wm.wm_mutex); mutex_init(&dev_priv->pps_mutex); + mutex_init(&dev_priv->mei_interface_mutex);
intel_uc_init_early(dev_priv); i915_memcpy_init_early(dev_priv); diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 82a106b1bdbc..0e35e24948a3 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -1815,6 +1815,8 @@ struct intel_cdclk_state { u8 voltage_level; };
+struct mei_cl_device; + struct drm_i915_private { struct drm_device drm;
@@ -2375,6 +2377,11 @@ struct drm_i915_private {
struct i915_pmu pmu;
+ /* MEI Interface for HDCP2.2 */ + struct mei_cl_device *mei_cldev; + bool mei_init_deferred; + struct mutex mei_interface_mutex; + /* * 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_dp.c b/drivers/gpu/drm/i915/intel_dp.c index 2c3eb90b9499..42f3b3331e90 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -6421,7 +6421,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 67f507bb4d0d..b431e7027bc9 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -1922,7 +1922,8 @@ 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); diff --git a/drivers/gpu/drm/i915/intel_hdcp.c b/drivers/gpu/drm/i915/intel_hdcp.c index 2cb50db3b063..2265121e1102 100644 --- a/drivers/gpu/drm/i915/intel_hdcp.c +++ b/drivers/gpu/drm/i915/intel_hdcp.c @@ -25,6 +25,7 @@ static int _intel_hdcp2_enable(struct intel_connector *connector); static int _intel_hdcp2_disable(struct intel_connector *connector); static void intel_hdcp2_check_work(struct work_struct *work); static int intel_hdcp2_check_link(struct intel_connector *connector); +static int intel_hdcp2_init(struct intel_connector *connector);
static int intel_hdcp_poll_ksv_fifo(struct intel_digital_port *intel_dig_port, const struct intel_hdcp_shim *shim) @@ -686,11 +687,15 @@ bool is_hdcp_supported(struct drm_i915_private *dev_priv, enum port port) }
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) { struct intel_hdcp *hdcp = &connector->hdcp; int ret;
+ if (!hdcp_shim) + return -EINVAL; + ret = drm_connector_attach_content_protection_property( &connector->base); if (ret) @@ -699,7 +704,12 @@ int intel_hdcp_init(struct intel_connector *connector, hdcp->hdcp_shim = hdcp_shim; mutex_init(&hdcp->hdcp_mutex); INIT_DELAYED_WORK(&hdcp->hdcp_check_work, intel_hdcp_check_work); + INIT_DELAYED_WORK(&hdcp->hdcp2_check_work, intel_hdcp2_check_work); INIT_WORK(&hdcp->hdcp_prop_work, intel_hdcp_prop_work); + + if (hdcp2_supported) + intel_hdcp2_init(connector); + return 0; }
@@ -1565,3 +1575,114 @@ static void intel_hdcp2_check_work(struct work_struct *work) schedule_delayed_work(&hdcp->hdcp2_check_work, DRM_HDCP2_CHECK_PERIOD_MS); } + +static inline int initialize_mei_hdcp_data(struct intel_connector *connector) +{ + struct drm_i915_private *dev_priv = to_i915(connector->base.dev); + struct intel_hdcp *hdcp = &connector->hdcp; + struct mei_hdcp_data *data = &hdcp->mei_data; + enum port port; + + mutex_lock(&dev_priv->mei_interface_mutex); + data->cldev = dev_priv->mei_cldev; + mutex_unlock(&dev_priv->mei_interface_mutex); + + if (connector->encoder) { + port = connector->encoder->port; + data->port = GET_MEI_DDI_INDEX(port); + } + + data->port_type = INTEGRATED; + data->protocol = hdcp->hdcp_shim->hdcp_protocol(); + + data->k = 1; + if (!data->streams) + data->streams = kcalloc(data->k, + sizeof(struct hdcp2_streamid_type), + GFP_KERNEL); + if (!data->streams) + return -ENOMEM; + + data->streams[0].stream_id = 0; + data->streams[0].stream_type = hdcp->content_type; + + return 0; +} + +static void intel_hdcp2_exit(struct intel_connector *connector) +{ + intel_hdcp_disable(connector); + connector->hdcp.hdcp2_supported = false; + + kfree(connector->hdcp.mei_data.streams); +} + +void intel_mei_cldev_reference_notify(void *client, + struct mei_cl_device *cldev) +{ + struct drm_i915_private *dev_priv = client; + struct drm_device *dev = &dev_priv->drm; + struct intel_connector *intel_connector; + struct drm_connector *connector; + struct drm_connector_list_iter conn_iter; + + DRM_INFO("MEI_HDCP Notification. Interface: %s\n", + cldev ? "UP" : "Down"); + + mutex_lock(&dev_priv->mei_interface_mutex); + dev_priv->mei_cldev = cldev; + mutex_unlock(&dev_priv->mei_interface_mutex); + + 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.hdcp2_supported)) + continue; + if (cldev) + initialize_mei_hdcp_data(intel_connector); + else + intel_hdcp2_exit(intel_connector); + } + drm_connector_list_iter_end(&conn_iter); +} + +static inline +bool is_hdcp2_supported(struct drm_i915_private *dev_priv) +{ + return (INTEL_GEN(dev_priv) >= 10 || IS_GEMINILAKE(dev_priv) || + IS_KABYLAKE(dev_priv)); +} + +static int 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; + + if (!is_hdcp2_supported(dev_priv)) + return -EINVAL; + + mutex_lock(&dev_priv->mei_interface_mutex); + if (!dev_priv->mei_cldev && !dev_priv->mei_init_deferred) { + ret = mei_hdcp_cldev_get_reference((void *)dev_priv, + &dev_priv->mei_cldev, + intel_mei_cldev_reference_notify); + if (ret < 0) { + mutex_unlock(&dev_priv->mei_interface_mutex); + return ret; + } + } + + /* Get reference is success but still handle is NULL */ + if (!dev_priv->mei_cldev) + dev_priv->mei_init_deferred = true; + + mutex_unlock(&dev_priv->mei_interface_mutex); + + ret = initialize_mei_hdcp_data(connector); + if (ret) + return ret; + + hdcp->hdcp2_supported = true; + return 0; +} diff --git a/drivers/gpu/drm/i915/intel_hdmi.c b/drivers/gpu/drm/i915/intel_hdmi.c index f5d7bfb43006..41d2505a964b 100644 --- a/drivers/gpu/drm/i915/intel_hdmi.c +++ b/drivers/gpu/drm/i915/intel_hdmi.c @@ -2342,7 +2342,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"); }
As a preparation for making the intel_hdcp_enable as common function for both HDCP1.4 and HDCP2.2, HDCP1.4 check_link scheduling is moved into _intel_hdcp_enable() function.
Signed-off-by: Ramalingam C ramalingam.c@intel.com --- drivers/gpu/drm/i915/intel_hdcp.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-)
diff --git a/drivers/gpu/drm/i915/intel_hdcp.c b/drivers/gpu/drm/i915/intel_hdcp.c index 2265121e1102..0642c14126a1 100644 --- a/drivers/gpu/drm/i915/intel_hdcp.c +++ b/drivers/gpu/drm/i915/intel_hdcp.c @@ -626,7 +626,7 @@ static int _intel_hdcp_enable(struct intel_connector *connector) ret = intel_hdcp_auth(conn_to_dig_port(connector), hdcp->hdcp_shim); if (!ret) - return 0; + break;
DRM_DEBUG_KMS("HDCP Auth failure (%d)\n", ret);
@@ -634,7 +634,12 @@ static int _intel_hdcp_enable(struct intel_connector *connector) _intel_hdcp_disable(connector); }
- DRM_ERROR("HDCP authentication failed (%d tries/%d)\n", tries, ret); + if (i != tries) + schedule_delayed_work(&hdcp->hdcp_check_work, + DRM_HDCP_CHECK_PERIOD_MS); + else + DRM_ERROR("HDCP authentication failed (%d tries/%d)\n", + tries, ret); return ret; }
@@ -729,8 +734,6 @@ int intel_hdcp_enable(struct intel_connector *connector)
hdcp->hdcp_value = DRM_MODE_CONTENT_PROTECTION_ENABLED; schedule_work(&hdcp->hdcp_prop_work); - schedule_delayed_work(&hdcp->hdcp_check_work, - DRM_HDCP_CHECK_PERIOD_MS); out: mutex_unlock(&hdcp->hdcp_mutex); return ret;
Considering that HDCP2.2 is more secure than HDCP1.4, When a setup supports HDCP2.2 and HDCP1.4, HDCP2.2 will be enabled.
v2: Included few optimization suggestions [Chris Wilson] Commit message is updated as per the rebased version.
Signed-off-by: Ramalingam C ramalingam.c@intel.com --- drivers/gpu/drm/i915/intel_hdcp.c | 76 +++++++++++++++++++++++++++++++++++---- 1 file changed, 69 insertions(+), 7 deletions(-)
diff --git a/drivers/gpu/drm/i915/intel_hdcp.c b/drivers/gpu/drm/i915/intel_hdcp.c index 0642c14126a1..1f3b0db0b70d 100644 --- a/drivers/gpu/drm/i915/intel_hdcp.c +++ b/drivers/gpu/drm/i915/intel_hdcp.c @@ -26,6 +26,57 @@ static int _intel_hdcp2_disable(struct intel_connector *connector); static void intel_hdcp2_check_work(struct work_struct *work); static int intel_hdcp2_check_link(struct intel_connector *connector); static int intel_hdcp2_init(struct intel_connector *connector); +static inline +int intel_hdcp_read_valid_bksv(struct intel_digital_port *intel_dig_port, + const struct intel_hdcp_shim *shim, u8 *bksv); +static +struct intel_digital_port *conn_to_dig_port(struct intel_connector *connector); + +static inline +bool panel_supports_hdcp(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; + u8 bksv[5]; + + if (hdcp->hdcp_shim) { + if (hdcp->hdcp_shim->hdcp_capable) { + hdcp->hdcp_shim->hdcp_capable(intel_dig_port, &capable); + } else { + if (!intel_hdcp_read_valid_bksv(intel_dig_port, + hdcp->hdcp_shim, bksv)) + capable = true; + } + } + return capable; +} + +static inline +bool panel_supports_hdcp2(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; + + if (hdcp->hdcp2_supported) + hdcp->hdcp_shim->hdcp_2_2_capable(intel_dig_port, &capable); + + return capable; +} + +/* Is HDCP1.4 capable on Platform and Panel */ +static inline bool intel_hdcp_capable(struct intel_connector *connector) +{ + return (connector->hdcp.hdcp_shim && panel_supports_hdcp(connector)); +} + +/* Is HDCP2.2 capable on Platform and Panel */ +static inline bool intel_hdcp2_capable(struct intel_connector *connector) +{ + return (connector->hdcp.hdcp2_supported && + panel_supports_hdcp2(connector)); +}
static int intel_hdcp_poll_ksv_fifo(struct intel_digital_port *intel_dig_port, const struct intel_hdcp_shim *shim) @@ -721,20 +772,27 @@ int intel_hdcp_init(struct intel_connector *connector, int intel_hdcp_enable(struct intel_connector *connector) { struct intel_hdcp *hdcp = &connector->hdcp; - int ret; + int ret = -EINVAL;
if (!hdcp->hdcp_shim) return -ENOENT;
mutex_lock(&hdcp->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); + else if (intel_hdcp_capable(connector)) + ret = _intel_hdcp_enable(connector); + + if (!ret) { + hdcp->hdcp_value = DRM_MODE_CONTENT_PROTECTION_ENABLED; + schedule_work(&hdcp->hdcp_prop_work); + }
- hdcp->hdcp_value = DRM_MODE_CONTENT_PROTECTION_ENABLED; - schedule_work(&hdcp->hdcp_prop_work); -out: mutex_unlock(&hdcp->hdcp_mutex); return ret; } @@ -751,10 +809,14 @@ int intel_hdcp_disable(struct intel_connector *connector)
if (hdcp->hdcp_value != DRM_MODE_CONTENT_PROTECTION_UNDESIRED) { hdcp->hdcp_value = DRM_MODE_CONTENT_PROTECTION_UNDESIRED; + if (hdcp->hdcp2_supported) + _intel_hdcp2_disable(connector); + ret = _intel_hdcp_disable(connector); }
mutex_unlock(&hdcp->hdcp_mutex); + cancel_delayed_work_sync(&hdcp->hdcp2_check_work); cancel_delayed_work_sync(&hdcp->hdcp_check_work); return ret; }
When HDCP2.2 enabling fails and HDCP1.4 is supported, HDCP1.4 is enabled.
v2: Rebased.
Signed-off-by: Ramalingam C ramalingam.c@intel.com --- drivers/gpu/drm/i915/intel_hdcp.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/drivers/gpu/drm/i915/intel_hdcp.c b/drivers/gpu/drm/i915/intel_hdcp.c index 1f3b0db0b70d..152cb1eebd90 100644 --- a/drivers/gpu/drm/i915/intel_hdcp.c +++ b/drivers/gpu/drm/i915/intel_hdcp.c @@ -785,7 +785,9 @@ int intel_hdcp_enable(struct intel_connector *connector) */ if (intel_hdcp2_capable(connector)) ret = _intel_hdcp2_enable(connector); - else if (intel_hdcp_capable(connector)) + + /* When HDCP2.2 fails, HDCP1.4 will be attempted */ + if (ret && intel_hdcp_capable(connector)) ret = _intel_hdcp_enable(connector);
if (!ret) {
HDCP check link is invoked only on CP_IRQ detection, instead of all short pulses.
Signed-off-by: Ramalingam C ramalingam.c@intel.com --- drivers/gpu/drm/i915/intel_dp.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-)
diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index 42f3b3331e90..2fce0d6329a5 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -4470,8 +4470,10 @@ intel_dp_short_pulse(struct intel_dp *intel_dp)
if (sink_irq_vector & DP_AUTOMATED_TEST_REQUEST) intel_dp_handle_test_request(intel_dp); - if (sink_irq_vector & (DP_CP_IRQ | DP_SINK_SPECIFIC_IRQ)) - DRM_DEBUG_DRIVER("CP or sink specific irq unhandled\n"); + if (sink_irq_vector & DP_CP_IRQ) + intel_hdcp_check_link(intel_dp->attached_connector); + if (sink_irq_vector & DP_SINK_SPECIFIC_IRQ) + DRM_DEBUG_DRIVER("Sink specific irq unhandled\n"); }
intel_dp_check_link_status(intel_dp); @@ -5478,9 +5480,6 @@ intel_dp_hpd_pulse(struct intel_digital_port *intel_dig_port, bool long_hpd) drm_modeset_acquire_fini(&ctx); WARN(iret, "Acquiring modeset locks failed with %i\n", iret);
- /* Short pulse can signify loss of hdcp authentication */ - intel_hdcp_check_link(intel_dp->attached_connector); - if (!handled) { intel_dp->detect_done = false; goto put_power;
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.
Signed-off-by: Ramalingam C ramalingam.c@intel.com --- drivers/gpu/drm/i915/intel_dp.c | 2 +- drivers/gpu/drm/i915/intel_drv.h | 2 +- drivers/gpu/drm/i915/intel_hdcp.c | 31 ++++++++++++++++++++++++++++++- 3 files changed, 32 insertions(+), 3 deletions(-)
diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index 2fce0d6329a5..5d0699538e7a 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -4471,7 +4471,7 @@ intel_dp_short_pulse(struct intel_dp *intel_dp) if (sink_irq_vector & DP_AUTOMATED_TEST_REQUEST) intel_dp_handle_test_request(intel_dp); if (sink_irq_vector & DP_CP_IRQ) - intel_hdcp_check_link(intel_dp->attached_connector); + intel_hdcp_handle_cp_irq(intel_dp->attached_connector); if (sink_irq_vector & 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 b431e7027bc9..17e6c054e171 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -1926,8 +1926,8 @@ 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); +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 152cb1eebd90..336116032ff7 100644 --- a/drivers/gpu/drm/i915/intel_hdcp.c +++ b/drivers/gpu/drm/i915/intel_hdcp.c @@ -31,6 +31,7 @@ int intel_hdcp_read_valid_bksv(struct intel_digital_port *intel_dig_port, const struct intel_hdcp_shim *shim, u8 *bksv); static struct intel_digital_port *conn_to_dig_port(struct intel_connector *connector); +static int intel_hdcp_check_link(struct intel_connector *connector);
static inline bool panel_supports_hdcp(struct intel_connector *connector) @@ -78,6 +79,26 @@ static inline bool intel_hdcp2_capable(struct intel_connector *connector) panel_supports_hdcp2(connector)); }
+static inline bool intel_hdcp_in_force(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_force(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) { @@ -857,7 +878,7 @@ void intel_hdcp_atomic_check(struct drm_connector *connector, }
/* Implements Part 3 of the HDCP authorization procedure */ -int intel_hdcp_check_link(struct intel_connector *connector) +static 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; @@ -1753,3 +1774,11 @@ static int intel_hdcp2_init(struct intel_connector *connector) hdcp->hdcp2_supported = true; return 0; } + +void intel_hdcp_handle_cp_irq(struct intel_connector *connector) +{ + if (intel_hdcp_in_force(connector)) + intel_hdcp_check_link(connector); + else if (intel_hdcp2_in_force(connector)) + intel_hdcp2_check_link(connector); +}
Implements a interface for single burst read of data that is larger than 512 Bytes through gmbus.
HDCP2.2 spec expects HDCP2.2 transmitter to read 522Bytes of HDCP receiver certificates in single burst read. On gmbus, to read more than 511Bytes, HW provides a workaround for burst read.
This patch passes the burst read request through gmbus read functions. And implements the sequence of enabling and disabling the burst read.
v2: No Change.
Signed-off-by: Ramalingam C ramalingam.c@intel.com --- drivers/gpu/drm/i915/i915_drv.h | 2 + drivers/gpu/drm/i915/i915_reg.h | 3 + drivers/gpu/drm/i915/intel_i2c.c | 124 +++++++++++++++++++++++++++++++++------ 3 files changed, 112 insertions(+), 17 deletions(-)
diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 0e35e24948a3..6991ba72b18b 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -3688,6 +3688,8 @@ extern void intel_teardown_gmbus(struct drm_i915_private *dev_priv); extern bool intel_gmbus_is_valid_pin(struct drm_i915_private *dev_priv, unsigned int pin); extern int intel_gmbus_output_aksv(struct i2c_adapter *adapter); +extern int intel_gmbus_burst_read(struct i2c_adapter *adapter, + unsigned int offset, void *buf, size_t size);
extern struct i2c_adapter * intel_gmbus_get_adapter(struct drm_i915_private *dev_priv, unsigned int pin); diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h index 08dafd7e9278..bc5aecd1cbcc 100644 --- a/drivers/gpu/drm/i915/i915_reg.h +++ b/drivers/gpu/drm/i915/i915_reg.h @@ -3114,6 +3114,7 @@ enum i915_power_well_id { #define GMBUS_RATE_400KHZ (2<<8) /* reserved on Pineview */ #define GMBUS_RATE_1MHZ (3<<8) /* reserved on Pineview */ #define GMBUS_HOLD_EXT (1<<7) /* 300ns hold time, rsvd on Pineview */ +#define GMBUS_BYTE_CNT_OVERRIDE (1<<6) #define GMBUS_PIN_DISABLED 0 #define GMBUS_PIN_SSC 1 #define GMBUS_PIN_VGADDC 2 @@ -3141,8 +3142,10 @@ enum i915_power_well_id { #define GMBUS_CYCLE_WAIT (1<<25) #define GMBUS_CYCLE_INDEX (2<<25) #define GMBUS_CYCLE_STOP (4<<25) +#define GMBUS_CYCLE_MASK (7<<25) #define GMBUS_BYTE_COUNT_SHIFT 16 #define GMBUS_BYTE_COUNT_MAX 256U +#define GMBUS_BYTE_COUNT_HW_MAX 511U #define GMBUS_SLAVE_INDEX_SHIFT 8 #define GMBUS_SLAVE_ADDR_SHIFT 1 #define GMBUS_SLAVE_READ (1<<0) diff --git a/drivers/gpu/drm/i915/intel_i2c.c b/drivers/gpu/drm/i915/intel_i2c.c index e6875509bcd9..dcb2be0d54ee 100644 --- a/drivers/gpu/drm/i915/intel_i2c.c +++ b/drivers/gpu/drm/i915/intel_i2c.c @@ -364,21 +364,30 @@ gmbus_wait_idle(struct drm_i915_private *dev_priv) static int gmbus_xfer_read_chunk(struct drm_i915_private *dev_priv, unsigned short addr, u8 *buf, unsigned int len, - u32 gmbus1_index) + u32 gmbus1_index, bool burst_read) { + unsigned int size = len; + int ret; + + if (burst_read) { + /* Seq to enable Burst Read */ + I915_WRITE_FW(GMBUS0, (I915_READ_FW(GMBUS0) | + GMBUS_BYTE_CNT_OVERRIDE)); + size = GMBUS_BYTE_COUNT_HW_MAX; + } + I915_WRITE_FW(GMBUS1, gmbus1_index | GMBUS_CYCLE_WAIT | - (len << GMBUS_BYTE_COUNT_SHIFT) | + (size << GMBUS_BYTE_COUNT_SHIFT) | (addr << GMBUS_SLAVE_ADDR_SHIFT) | GMBUS_SLAVE_READ | GMBUS_SW_RDY); while (len) { - int ret; u32 val, loop = 0;
ret = gmbus_wait(dev_priv, GMBUS_HW_RDY, GMBUS_HW_RDY_EN); if (ret) - return ret; + goto exit;
val = I915_READ_FW(GMBUS3); do { @@ -387,12 +396,29 @@ gmbus_xfer_read_chunk(struct drm_i915_private *dev_priv, } while (--len && ++loop < 4); }
- return 0; +exit: + if (burst_read) { + + /* Seq to disable the Burst Read */ + I915_WRITE_FW(GMBUS0, (I915_READ_FW(GMBUS0) & + ~GMBUS_BYTE_CNT_OVERRIDE)); + I915_WRITE_FW(GMBUS1, (I915_READ_FW(GMBUS1) & + ~GMBUS_CYCLE_MASK) | GMBUS_CYCLE_STOP); + + /* + * On Burst read disable, GMBUS need more time to settle + * down to Idle State. + */ + ret = intel_wait_for_register_fw(dev_priv, GMBUS2, + GMBUS_ACTIVE, 0, 50); + } + + return ret; }
static int gmbus_xfer_read(struct drm_i915_private *dev_priv, struct i2c_msg *msg, - u32 gmbus1_index) + u32 gmbus1_index, bool burst_read) { u8 *buf = msg->buf; unsigned int rx_size = msg->len; @@ -400,10 +426,13 @@ gmbus_xfer_read(struct drm_i915_private *dev_priv, struct i2c_msg *msg, int ret;
do { - len = min(rx_size, GMBUS_BYTE_COUNT_MAX); + if (burst_read) + len = rx_size; + else + len = min(rx_size, GMBUS_BYTE_COUNT_MAX);
- ret = gmbus_xfer_read_chunk(dev_priv, msg->addr, - buf, len, gmbus1_index); + ret = gmbus_xfer_read_chunk(dev_priv, msg->addr, buf, len, + gmbus1_index, burst_read); if (ret) return ret;
@@ -491,7 +520,8 @@ gmbus_is_index_xfer(struct i2c_msg *msgs, int i, int num) }
static int -gmbus_index_xfer(struct drm_i915_private *dev_priv, struct i2c_msg *msgs) +gmbus_index_xfer(struct drm_i915_private *dev_priv, struct i2c_msg *msgs, + bool burst_read) { u32 gmbus1_index = 0; u32 gmbus5 = 0; @@ -509,7 +539,8 @@ gmbus_index_xfer(struct drm_i915_private *dev_priv, struct i2c_msg *msgs) I915_WRITE_FW(GMBUS5, gmbus5);
if (msgs[1].flags & I2C_M_RD) - ret = gmbus_xfer_read(dev_priv, &msgs[1], gmbus1_index); + ret = gmbus_xfer_read(dev_priv, &msgs[1], + gmbus1_index, burst_read); else ret = gmbus_xfer_write(dev_priv, &msgs[1], gmbus1_index);
@@ -522,7 +553,7 @@ gmbus_index_xfer(struct drm_i915_private *dev_priv, struct i2c_msg *msgs)
static int do_gmbus_xfer(struct i2c_adapter *adapter, struct i2c_msg *msgs, int num, - u32 gmbus0_source) + u32 gmbus0_source, bool burst_read) { struct intel_gmbus *bus = container_of(adapter, struct intel_gmbus, @@ -544,15 +575,20 @@ do_gmbus_xfer(struct i2c_adapter *adapter, struct i2c_msg *msgs, int num, for (; i < num; i += inc) { inc = 1; if (gmbus_is_index_xfer(msgs, i, num)) { - ret = gmbus_index_xfer(dev_priv, &msgs[i]); + ret = gmbus_index_xfer(dev_priv, &msgs[i], burst_read); inc = 2; /* an index transmission is two msgs */ } else if (msgs[i].flags & I2C_M_RD) { - ret = gmbus_xfer_read(dev_priv, &msgs[i], 0); + ret = gmbus_xfer_read(dev_priv, &msgs[i], + 0, burst_read); } else { ret = gmbus_xfer_write(dev_priv, &msgs[i], 0); }
- if (!ret) + /* + * Burst read Sequence ends with STOP. So Dont expect + * HW wait phase. + */ + if (!ret && !burst_read) ret = gmbus_wait(dev_priv, GMBUS_HW_WAIT_PHASE, GMBUS_HW_WAIT_EN); if (ret == -ETIMEDOUT) @@ -664,7 +700,7 @@ gmbus_xfer(struct i2c_adapter *adapter, struct i2c_msg *msgs, int num) if (ret < 0) bus->force_bit &= ~GMBUS_FORCE_BIT_RETRY; } else { - ret = do_gmbus_xfer(adapter, msgs, num, 0); + ret = do_gmbus_xfer(adapter, msgs, num, 0, false); if (ret == -EAGAIN) bus->force_bit |= GMBUS_FORCE_BIT_RETRY; } @@ -705,7 +741,8 @@ int intel_gmbus_output_aksv(struct i2c_adapter *adapter) * pass the i2c command, and tell GMBUS to use the HW-provided value * instead of sourcing GMBUS3 for the data. */ - ret = do_gmbus_xfer(adapter, msgs, ARRAY_SIZE(msgs), GMBUS_AKSV_SELECT); + ret = do_gmbus_xfer(adapter, msgs, ARRAY_SIZE(msgs), + GMBUS_AKSV_SELECT, false);
mutex_unlock(&dev_priv->gmbus_mutex); intel_display_power_put(dev_priv, POWER_DOMAIN_GMBUS); @@ -713,6 +750,59 @@ int intel_gmbus_output_aksv(struct i2c_adapter *adapter) return ret; }
+static inline +bool intel_gmbus_burst_read_supported(struct drm_i915_private *dev_priv) +{ + if (INTEL_GEN(dev_priv) > 10 || IS_GEMINILAKE(dev_priv) || + IS_KABYLAKE(dev_priv)) + return true; + return false; +} + +int intel_gmbus_burst_read(struct i2c_adapter *adapter, unsigned int offset, + void *buf, size_t size) +{ + struct intel_gmbus *bus = container_of(adapter, struct intel_gmbus, + adapter); + struct drm_i915_private *dev_priv = bus->dev_priv; + int ret; + u8 start = offset & 0xff; + struct i2c_msg msgs[] = { + { + .addr = DRM_HDCP_DDC_ADDR, + .flags = 0, + .len = 1, + .buf = &start, + }, + { + .addr = DRM_HDCP_DDC_ADDR, + .flags = I2C_M_RD, + .len = size, + .buf = buf, + } + }; + + if (!intel_gmbus_burst_read_supported(dev_priv)) + return -EINVAL; + + intel_display_power_get(dev_priv, POWER_DOMAIN_GMBUS); + mutex_lock(&dev_priv->gmbus_mutex); + + /* + * In order to read the complete length(More than GMBus Limit) of data, + * in burst mode, implement the Workaround supported in HW. + */ + ret = do_gmbus_xfer(adapter, msgs, ARRAY_SIZE(msgs), 0, true); + + mutex_unlock(&dev_priv->gmbus_mutex); + intel_display_power_put(dev_priv, POWER_DOMAIN_GMBUS); + + if (ret == ARRAY_SIZE(msgs)) + return 0; + + return ret >= 0 ? -EIO : ret; +} + static u32 gmbus_func(struct i2c_adapter *adapter) { return i2c_bit_algo.functionality(adapter) &
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.
Note: Chris Wilson suggested alternate method for waiting for CP_IRQ, than completions concept. WIP to understand and implement that, if needed. Just to unblock the review of other changes, v2 still continues with completions.
v2: wait for cp_irq is merged with this patch. Rebased.
Signed-off-by: Ramalingam C ramalingam.c@intel.com --- drivers/gpu/drm/i915/intel_dp.c | 350 ++++++++++++++++++++++++++++++++++++++ drivers/gpu/drm/i915/intel_drv.h | 1 + drivers/gpu/drm/i915/intel_hdcp.c | 3 + 3 files changed, 354 insertions(+)
diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index 5d0699538e7a..248fd570fc0f 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> @@ -5085,6 +5086,26 @@ void intel_dp_encoder_suspend(struct intel_encoder *intel_encoder) pps_unlock(intel_dp); }
+static int intel_dp_hdcp_wait_for_cp_irq(struct completion *cp_irq_recved, + int timeout) +{ + long ret; + + if (completion_done(cp_irq_recved)) + reinit_completion(cp_irq_recved); + + ret = wait_for_completion_interruptible_timeout(cp_irq_recved, + msecs_to_jiffies( + timeout)); + reinit_completion(cp_irq_recved); + if (ret < 0) + return (int)ret; + else if (!ret) + return -ETIMEDOUT; + return 0; +} + + static int intel_dp_hdcp_write_an_aksv(struct intel_digital_port *intel_dig_port, u8 *an) @@ -5301,6 +5322,329 @@ int intel_dp_hdcp_capable(struct intel_digital_port *intel_dig_port, return 0; }
+static inline +int intel_dpcd_offset_for_hdcp2_msgid(uint8_t byte, unsigned int *offset) +{ + switch (byte) { + case HDCP_2_2_AKE_INIT: + *offset = DP_HDCP_2_2_AKE_INIT_OFFSET; + break; + case HDCP_2_2_AKE_SEND_CERT: + *offset = DP_HDCP_2_2_AKE_SEND_CERT_OFFSET; + break; + case HDCP_2_2_AKE_NO_STORED_KM: + *offset = DP_HDCP_2_2_AKE_NO_STORED_KM_OFFSET; + break; + case HDCP_2_2_AKE_STORED_KM: + *offset = DP_HDCP_2_2_AKE_STORED_KM_OFFSET; + break; + case HDCP_2_2_AKE_SEND_HPRIME: + *offset = DP_HDCP_2_2_AKE_SEND_HPRIME_OFFSET; + break; + case HDCP_2_2_AKE_SEND_PARING_INFO: + *offset = DP_HDCP_2_2_AKE_SEND_PARING_INFO_OFFSET; + break; + case HDCP_2_2_LC_INIT: + *offset = DP_HDCP_2_2_LC_INIT_OFFSET; + break; + case HDCP_2_2_LC_SEND_LPRIME: + *offset = DP_HDCP_2_2_LC_SEND_LPRIME_OFFSET; + break; + case HDCP_2_2_SKE_SEND_EKS: + *offset = DP_HDCP_2_2_SKE_SEND_EKS_OFFSET; + break; + case HDCP_2_2_REP_SEND_RECVID_LIST: + *offset = DP_HDCP_2_2_REP_SEND_RECVID_LIST_OFFSET; + break; + case HDCP_2_2_REP_SEND_ACK: + *offset = DP_HDCP_2_2_REP_SEND_ACK_OFFSET; + break; + case HDCP_2_2_REP_STREAM_MANAGE: + *offset = DP_HDCP_2_2_REP_STREAM_MANAGE_OFFSET; + break; + case HDCP_2_2_REP_STREAM_READY: + *offset = DP_HDCP_2_2_REP_STREAM_READY_OFFSET; + break; + case HDCP_2_2_ERRATA_DP_STREAM_TYPE: + *offset = DP_HDCP_2_2_REG_STREAM_TYPE_OFFSET; + break; + default: + DRM_ERROR("Unrecognized Msg ID\n"); + return -EINVAL; + } + return 0; +} + +static inline +int intel_dp_hdcp2_read_rx_status(struct intel_digital_port *intel_dig_port, + uint8_t *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_ERROR("Read bstatus from DP/AUX failed (%ld)\n", ret); + return ret >= 0 ? -EIO : ret; + } + + return 0; +} + +static inline +int intel_dp_hdcp2_timeout_for_msg(uint8_t msg_id, bool paired) +{ + int timeout = -EINVAL; + + switch (msg_id) { + case HDCP_2_2_AKE_SEND_CERT: + timeout = HDCP_2_2_CERT_TIMEOUT; + break; + case HDCP_2_2_AKE_SEND_HPRIME: + if (paired) + timeout = HDCP_2_2_HPRIME_PAIRED_TIMEOUT; + else + timeout = HDCP_2_2_HPRIME_NO_PAIRED_TIMEOUT; + break; + case HDCP_2_2_AKE_SEND_PARING_INFO: + timeout = HDCP_2_2_PAIRING_TIMEOUT; + break; + case HDCP_2_2_LC_SEND_LPRIME: + timeout = HDCP_2_2_DP_LPRIME_TIMEOUT; + break; + case HDCP_2_2_REP_SEND_RECVID_LIST: + timeout = HDCP_2_2_RECVID_LIST_TIMEOUT; + break; + case HDCP_2_2_REP_STREAM_READY: + timeout = HDCP_2_2_STREAM_READY_TIMEOUT; + break; + default: + DRM_ERROR("Unsupported msg_id: %d\n", (int)msg_id); + } + return timeout; +} + +static inline +int hdcp2_detect_msg_availability(struct intel_digital_port *intel_dig_port, + uint8_t msg_id, bool *msg_ready) +{ + uint8_t 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_PARING_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_DEBUG_KMS("Unidentified msg_id: %d\n", (int)msg_id); + return -EINVAL; + } + return 0; +} + + +static inline ssize_t +intel_dp_hdcp2_wait_for_msg(struct intel_digital_port *intel_dig_port, + uint8_t msg_id) +{ + struct intel_dp *dp = &intel_dig_port->dp; + struct intel_hdcp *hdcp = &dp->attached_connector->hdcp; + int ret, timeout; + bool msg_ready = false; + + timeout = intel_dp_hdcp2_timeout_for_msg(msg_id, hdcp->is_paired); + switch (msg_id) { + + /* + * There is no way to detect the CERT, LPRIME and STREAM_READY + * availability. So Wait for timeout and read the msg. + */ + case HDCP_2_2_AKE_SEND_CERT: + case HDCP_2_2_LC_SEND_LPRIME: + case HDCP_2_2_REP_STREAM_READY: + mdelay(timeout); + ret = 0; + break; + case HDCP_2_2_AKE_SEND_HPRIME: + case HDCP_2_2_AKE_SEND_PARING_INFO: + case HDCP_2_2_REP_SEND_RECVID_LIST: + intel_dp_hdcp_wait_for_cp_irq(&hdcp->cp_irq_recved, timeout); + ret = hdcp2_detect_msg_availability(intel_dig_port, msg_id, + &msg_ready); + if (!msg_ready) + ret = -ETIMEDOUT; + break; + default: + DRM_DEBUG_KMS("Unidentified msg_id: %d\n", (int)msg_id); + return -EINVAL; + } + if (ret) + DRM_ERROR("msg_id %d, ret %d, timeout(mSec): %d\n", msg_id, ret, + timeout); + return ret; +} + +static +int intel_dp_hdcp2_write_msg(struct intel_digital_port *intel_dig_port, + void *buf, size_t size) +{ + unsigned int offset; + uint8_t *byte = buf; + ssize_t ret, bytes_to_write, len; + + if (intel_dpcd_offset_for_hdcp2_msgid(*byte, &offset) < 0) + return -EINVAL; + + /* 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 +int intel_dp_hdcp2_read_msg(struct intel_digital_port *intel_dig_port, + uint8_t msg_id, void *buf, size_t size) +{ + unsigned int offset, dev_cnt; + uint8_t *byte = buf; + uint8_t rx_info[HDCP_2_2_RXINFO_LEN]; + ssize_t ret, bytes_to_recv, len; + + if (intel_dpcd_offset_for_hdcp2_msgid(msg_id, &offset) < 0) + return -EINVAL; + + ret = intel_dp_hdcp2_wait_for_msg(intel_dig_port, msg_id); + if (ret < 0) + return ret; + + /* Finding the ReceiverID List size */ + if (msg_id == HDCP_2_2_REP_SEND_RECVID_LIST) { + 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; + + size = sizeof(struct hdcp2_rep_send_receiverid_list) - + HDCP_2_2_RECEIVER_IDS_MAX_LEN + + (dev_cnt * HDCP_2_2_RECEIVER_ID_LEN); + } + + bytes_to_recv = size - 1; + + /* To skip the msg_id, as msgs in DP adaptation 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 %d\n", msg_id, (int)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) +{ + uint8_t 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) +{ + uint8_t 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_RXCAPS_VERSION_VAL && + HDCP_2_2_DP_HDCP_CAPABLE(rx_caps[2])) + *capable = true; + + return 0; +} + +static +enum hdcp_protocol intel_dp_hdcp2_protocol(void) +{ + return 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, @@ -5313,6 +5657,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 17e6c054e171..e931877deb69 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -442,6 +442,7 @@ struct intel_hdcp { struct mei_hdcp_data mei_data;
struct delayed_work hdcp2_check_work; + struct completion 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 336116032ff7..16144ae1bbac 100644 --- a/drivers/gpu/drm/i915/intel_hdcp.c +++ b/drivers/gpu/drm/i915/intel_hdcp.c @@ -787,6 +787,7 @@ int intel_hdcp_init(struct intel_connector *connector, if (hdcp2_supported) intel_hdcp2_init(connector);
+ init_completion(&hdcp->cp_irq_recved); return 0; }
@@ -1781,4 +1782,6 @@ void intel_hdcp_handle_cp_irq(struct intel_connector *connector) intel_hdcp_check_link(connector); else if (intel_hdcp2_in_force(connector)) intel_hdcp2_check_link(connector); + + complete_all(&connector->hdcp.cp_irq_recved); }
Implements the HDMI adapatation specific HDCP2.2 operations.
Basically these are DDC read and write for authenticating through HDCP2.2 messages.
v2: Rebased.
Signed-off-by: Ramalingam C ramalingam.c@intel.com --- drivers/gpu/drm/i915/intel_hdmi.c | 203 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 203 insertions(+)
diff --git a/drivers/gpu/drm/i915/intel_hdmi.c b/drivers/gpu/drm/i915/intel_hdmi.c index 41d2505a964b..12f6aca947b6 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> @@ -1106,6 +1107,203 @@ bool intel_hdmi_hdcp_check_link(struct intel_digital_port *intel_dig_port) return true; }
+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 inline +int intel_hdmi_hdcp2_timeout_for_msg(uint8_t msg_id, bool is_paired) +{ + int timeout = -EINVAL; + + switch (msg_id) { + case HDCP_2_2_AKE_SEND_CERT: + timeout = HDCP_2_2_CERT_TIMEOUT; + break; + case HDCP_2_2_AKE_SEND_HPRIME: + if (is_paired) + timeout = HDCP_2_2_HPRIME_PAIRED_TIMEOUT; + else + timeout = HDCP_2_2_HPRIME_NO_PAIRED_TIMEOUT; + break; + case HDCP_2_2_AKE_SEND_PARING_INFO: + timeout = HDCP_2_2_PAIRING_TIMEOUT; + break; + case HDCP_2_2_LC_SEND_LPRIME: + timeout = HDCP_2_2_HDMI_LPRIME_TIMEOUT; + break; + case HDCP_2_2_REP_SEND_RECVID_LIST: + timeout = HDCP_2_2_RECVID_LIST_TIMEOUT; + break; + case HDCP_2_2_REP_STREAM_READY: + timeout = HDCP_2_2_STREAM_READY_TIMEOUT; + break; + default: + DRM_ERROR("Unsupported msg_id: %d\n", (int)msg_id); + } + return timeout; +} + +static inline +int hdcp2_detect_msg_availability(struct intel_digital_port *intel_digital_port, + uint8_t msg_id, bool *msg_ready, + ssize_t *msg_sz) +{ + uint8_t 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; +} + +/** + * intel_hdmi_hdcp2_wait_for_msg: Detects the hdmi hdcp2.2 msg availability + * @hdcp: hdcp structure + * @msg_id: Message ID for which we are waiting + * + * Detects the HDMI HDCP2.2 Message availability + * + * Returns -ETIMEOUT in case of timeout, Message Size on success + */ +static ssize_t +intel_hdmi_hdcp2_wait_for_msg(struct intel_digital_port *intel_dig_port, + uint8_t msg_id, bool paired) +{ + bool msg_ready = false; + int timeout, ret; + ssize_t msg_sz; + + timeout = intel_hdmi_hdcp2_timeout_for_msg(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_ERROR("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, + uint8_t msg_id, void *buf, size_t size) +{ + struct intel_hdmi *hdmi = &intel_dig_port->hdmi; + struct intel_hdcp *hdcp = &hdmi->attached_connector->hdcp; + struct drm_i915_private *dev_priv; + struct i2c_adapter *adapter; + 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(%d) is more than exp size(%d)\n", + (int)ret, (int)size); + return -1; + } + + offset = HDCP_2_2_HDMI_REG_RD_MSG_OFFSET; + + if (msg_id == HDCP_2_2_AKE_SEND_CERT) { + dev_priv = intel_dig_port->base.base.dev->dev_private; + adapter = intel_gmbus_get_adapter(dev_priv, hdmi->ddc_bus); + + ret = intel_gmbus_burst_read(adapter, offset, buf, ret); + } else { + ret = intel_hdmi_hdcp_read(intel_dig_port, offset, buf, ret); + } + + if (ret) + DRM_DEBUG_KMS("msg_id: %d, ret: %d\n", msg_id, (int)ret); + return ret; +} + +static +int intel_hdmi_hdcp2_check_link(struct intel_digital_port *intel_dig_port) +{ + uint8_t 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) +{ + uint8_t hdcp2version; + int ret; + + *capable = false; + ret = intel_hdmi_hdcp_read(intel_dig_port, HDCP_2_2_HDMI_REG_VER_OFFSET, + &hdcp2version, sizeof(hdcp2version)); + if (!ret) + if (hdcp2version & HDCP_2_2_HDMI_SUPPORT_MASK) + *capable = true; + + return ret; +} + +static +enum hdcp_protocol intel_hdmi_hdcp2_protocol(void) +{ + return 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, @@ -1117,6 +1315,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,
On DP connector init, intel_hdcp_init is passed with a flag for hdcp2.2 support based on the platform capability.
v2: Rebased.
Signed-off-by: Ramalingam C ramalingam.c@intel.com --- drivers/gpu/drm/i915/intel_dp.c | 2 +- drivers/gpu/drm/i915/intel_drv.h | 1 + drivers/gpu/drm/i915/intel_hdcp.c | 1 - 3 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index 248fd570fc0f..1b3e56783b93 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -6771,7 +6771,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"); } diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index e931877deb69..a76ad5b421bd 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -1929,6 +1929,7 @@ int intel_hdcp_enable(struct intel_connector *connector); int intel_hdcp_disable(struct intel_connector *connector); bool is_hdcp_supported(struct drm_i915_private *dev_priv, enum port port); void intel_hdcp_handle_cp_irq(struct intel_connector *connector); +bool is_hdcp2_supported(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 16144ae1bbac..84a0a3cf4f10 100644 --- a/drivers/gpu/drm/i915/intel_hdcp.c +++ b/drivers/gpu/drm/i915/intel_hdcp.c @@ -1735,7 +1735,6 @@ void intel_mei_cldev_reference_notify(void *client, drm_connector_list_iter_end(&conn_iter); }
-static inline bool is_hdcp2_supported(struct drm_i915_private *dev_priv) { return (INTEL_GEN(dev_priv) >= 10 || IS_GEMINILAKE(dev_priv) ||
On HDMI connector init, intel_hdcp_init is passed with a flag for hdcp2.2 support based on the platform capability.
v2: Rebased.
Signed-off-by: Ramalingam C ramalingam.c@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 12f6aca947b6..f6da323fe2fc 100644 --- a/drivers/gpu/drm/i915/intel_hdmi.c +++ b/drivers/gpu/drm/i915/intel_hdmi.c @@ -2545,7 +2545,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"); }
-----Original Message----- From: C, Ramalingam Sent: Thursday, March 08, 2018 13:58 To: intel-gfx@lists.freedesktop.org; dri-devel@lists.freedesktop.org; seanpaul@chromium.org; chris@chris-wilson.co.uk; Winkler, Tomas tomas.winkler@intel.com; jani.nikula@linux.intel.com Cc: Vivi, Rodrigo rodrigo.vivi@intel.com; Sharma, Shashank shashank.sharma@intel.com; Shankar, Uma uma.shankar@intel.com; C, Ramalingam ramalingam.c@intel.com Subject: [PATCH v2 00/42] drm/i915: Implement HDCP2.2
Based on HDCP1.4 framework introduced by Sean Paul, this series implements the HDCP2.2 in I915.
I didn't see HDCP 1.4 framework being merged upstream? What tree is this based on?
Thanks Tomas
2.7.4
On Thursday 08 March 2018 06:00 PM, Winkler, Tomas wrote:
-----Original Message----- From: C, Ramalingam Sent: Thursday, March 08, 2018 13:58 To: intel-gfx@lists.freedesktop.org; dri-devel@lists.freedesktop.org; seanpaul@chromium.org; chris@chris-wilson.co.uk; Winkler, Tomas tomas.winkler@intel.com; jani.nikula@linux.intel.com Cc: Vivi, Rodrigo rodrigo.vivi@intel.com; Sharma, Shashank shashank.sharma@intel.com; Shankar, Uma uma.shankar@intel.com; C, Ramalingam ramalingam.c@intel.com Subject: [PATCH v2 00/42] drm/i915: Implement HDCP2.2
Based on HDCP1.4 framework introduced by Sean Paul, this series implements the HDCP2.2 in I915.
I didn't see HDCP 1.4 framework being merged upstream? What tree is this based on?
This is based on drm-tip branch of https://cgit.freedesktop.org/drm-tip/
--Ram
Thanks Tomas
2.7.4
On Thu, Mar 08, 2018 at 06:03:32PM +0530, Ramalingam C wrote:
On Thursday 08 March 2018 06:00 PM, Winkler, Tomas wrote:
-----Original Message----- From: C, Ramalingam Sent: Thursday, March 08, 2018 13:58 To: intel-gfx@lists.freedesktop.org; dri-devel@lists.freedesktop.org; seanpaul@chromium.org; chris@chris-wilson.co.uk; Winkler, Tomas tomas.winkler@intel.com; jani.nikula@linux.intel.com Cc: Vivi, Rodrigo rodrigo.vivi@intel.com; Sharma, Shashank shashank.sharma@intel.com; Shankar, Uma uma.shankar@intel.com; C, Ramalingam ramalingam.c@intel.com Subject: [PATCH v2 00/42] drm/i915: Implement HDCP2.2
Based on HDCP1.4 framework introduced by Sean Paul, this series implements the HDCP2.2 in I915.
I didn't see HDCP 1.4 framework being merged upstream? What tree is this based on?
This is based on drm-tip branch of https://cgit.freedesktop.org/drm-tip/
All the code also should be in linux-next. If it's not, then something went wrong somewhere with the scripting. -Daniel
Thank you Tomas for the review on mei related changes. I will work on those changes. Awaiting for the reviews on the I915 and DRM changes before going for v3. Thanks and Regards --Ram
On Thursday 08 March 2018 05:28 PM, Ramalingam C wrote:
Based on HDCP1.4 framework introduced by Sean Paul, this series implements the HDCP2.2 in I915.
The sequence for HDCP2.2 authentication and encryption is implemented in I915. 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.
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 streams.
Yes its bit lengthy series. Please tolerate. Thanks
Major Changes in v2:
- Synchronous implementation of HDCP authentication [SeanPaul]
- Removal of bit-fields usage.[Tomas and Jani]
- Protecting the mei_interface handle with mutex [Chris]
- Droped, added and squashed few patches
- Extended hdcp_shim to support hdcp2.2 operations too. [SeanPaul]
- Used Intel_wait_for_registers(), Where ever it is applicable.[Chris]
- mei_hdcp driver is moved into drivers/misc/mei/hdcp/ [Tomas]
- Adapted the static declaration for struct intel_hdcp and mei_hdcp_data. [SeanPaul]
Sincere thanks for Sean Paul, Jani Nikula, Chris Wilson and Tomas Winkler for the review comments on v1 series.
Ramalingam C (41): drm: hdcp2.2 authentication msg definitions drm: HDMI and DP specific HDCP2.2 defines misc/mei/hdcp: Client driver for HDCP application misc/mei/hdcp: Add KBuild for mei hdcp driver misc/mei/hdcp: Verify mei client device status misc/mei/hdcp: Get & Put for mei cl_device misc/mei/hdcp: Define ME FW interface for HDCP2.2 linux/mei: Header for mei_hdcp driver interface 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 verifcation and ack misc/mei/hdcp: Verify M_prime misc/mei/hdcp: Enabling the HDCP authentication misc/mei/hdcp: Closing wired HDCP2.2 Tx Session drm/i915: wrapping all hdcp var into intel_hdcp drm/i915: Define HDCP2.2 related variables drm/i915: Define Intel HDCP2.2 registers drm/i915: Wrappers for mei HDCP2.2 services drm/i915: Implement HDCP2.2 receiver authentication drm/i915: Implement HDCP2.2 repeater authentication drm/i915: Enable and Disable HDCP2.2 port encryption drm/i915: Implement HDCP2.2 En/Dis-able drm/i915: Implement HDCP2.2 link integrity check drm/i915: Handle HDCP2.2 downstream topology change drm/i915: Pullout the bksv read and validation drm/i915: Initialize HDCP2.2 and its MEI interface drm/i915: Schedule hdcp_check_link in _intel_hdcp_enable drm/i915: Enable superior HDCP ver that is capable drm/i915: Enable HDCP1.4 incase of HDCP2.2 failure drm/i915: hdcp_check_link only on CP_IRQ drm/i915: Check HDCP 1.4 and 2.2 link on CP_IRQ drm/i915: Implement gmbus burst read 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
Tomas Winkler (1): mei: bus: whitelist hdcp client
drivers/gpu/drm/i915/i915_drv.c | 1 + drivers/gpu/drm/i915/i915_drv.h | 9 + drivers/gpu/drm/i915/i915_reg.h | 35 ++ drivers/gpu/drm/i915/intel_display.c | 7 +- drivers/gpu/drm/i915/intel_dp.c | 362 ++++++++++- drivers/gpu/drm/i915/intel_drv.h | 81 ++- drivers/gpu/drm/i915/intel_hdcp.c | 1107 ++++++++++++++++++++++++++++++++-- drivers/gpu/drm/i915/intel_hdmi.c | 206 ++++++- drivers/gpu/drm/i915/intel_i2c.c | 124 +++- drivers/misc/mei/Kconfig | 6 + drivers/misc/mei/Makefile | 2 + drivers/misc/mei/bus-fixup.c | 16 + drivers/misc/mei/hdcp/Makefile | 6 + drivers/misc/mei/hdcp/mei_hdcp.c | 927 ++++++++++++++++++++++++++++ drivers/misc/mei/hdcp/mei_hdcp.h | 566 +++++++++++++++++ include/drm/drm_dp_helper.h | 54 ++ include/drm/drm_hdcp.h | 220 +++++++ include/linux/mei_hdcp.h | 215 +++++++ 18 files changed, 3846 insertions(+), 98 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
dri-devel@lists.freedesktop.org