rockchip: implement hdcp key decryption feature for rk3399

Decrypt device private keys which transfer from kernel, then stuff it to
DP controller. So that DP driver could start HDCP authentication in
kernel.

Change-Id: If3c2cd99bca811fe5fc30acc88bf5dc1afd7416d
Signed-off-by: Ziyuan Xu <xzy.xu@rock-chips.com>
Signed-off-by: Caesar Wang <wxt@rock-chips.com>
diff --git a/plat/rockchip/rk3399/drivers/dp/cdn_dp.c b/plat/rockchip/rk3399/drivers/dp/cdn_dp.c
index 9fc623b..d0e5986 100644
--- a/plat/rockchip/rk3399/drivers/dp/cdn_dp.c
+++ b/plat/rockchip/rk3399/drivers/dp/cdn_dp.c
@@ -4,7 +4,10 @@
  * SPDX-License-Identifier: BSD-3-Clause
  */
 
+#include <cdn_dp.h>
+#include <smcc.h>
 #include <stdlib.h>
+#include <string.h>
 
 __asm__(
 	".pushsection .text.hdcp_handler, \"ax\", %progbits\n"
@@ -17,3 +20,46 @@
 	".popsection\n"
 );
 
+static uint64_t *hdcp_key_pdata;
+static struct cdn_dp_hdcp_key_1x key;
+
+int hdcp_handler(struct cdn_dp_hdcp_key_1x *key);
+
+uint64_t dp_hdcp_ctrl(uint64_t type)
+{
+	switch (type) {
+	case HDCP_KEY_DATA_START_TRANSFER:
+		memset(&key, 0x00, sizeof(key));
+		hdcp_key_pdata = (uint64_t *)&key;
+		return 0;
+	case HDCP_KEY_DATA_START_DECRYPT:
+		if (hdcp_key_pdata == (uint64_t *)(&key + 1))
+			return hdcp_handler(&key);
+		else
+			return PSCI_E_INVALID_PARAMS;
+	default:
+		return SMC_UNK;
+	}
+}
+
+uint64_t dp_hdcp_store_key(uint64_t x1,
+			   uint64_t x2,
+			   uint64_t x3,
+			   uint64_t x4,
+			   uint64_t x5,
+			   uint64_t x6)
+{
+	if (hdcp_key_pdata < (uint64_t *)&key ||
+		hdcp_key_pdata + 6 > (uint64_t *)(&key + 1))
+		return PSCI_E_INVALID_PARAMS;
+
+	hdcp_key_pdata[0] = x1;
+	hdcp_key_pdata[1] = x2;
+	hdcp_key_pdata[2] = x3;
+	hdcp_key_pdata[3] = x4;
+	hdcp_key_pdata[4] = x5;
+	hdcp_key_pdata[5] = x6;
+	hdcp_key_pdata += 6;
+
+	return 0;
+}
diff --git a/plat/rockchip/rk3399/drivers/dp/cdn_dp.h b/plat/rockchip/rk3399/drivers/dp/cdn_dp.h
new file mode 100644
index 0000000..3b9a108
--- /dev/null
+++ b/plat/rockchip/rk3399/drivers/dp/cdn_dp.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2017, ARM Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef __SOC_ROCKCHIP_RK3399_DP_H__
+#define __SOC_ROCKCHIP_RK3399_DP_H__
+#include <plat_private.h>
+
+enum {
+	CDN_DP_HDCP_1X_KSV_LEN = 5,
+	CDN_DP_HDCP_KSV_LEN = 8,
+	CDN_DP_HDCP_RESERVED_LEN = 10,
+	CDN_DP_HDCP_UID_LEN = 16,
+	CDN_DP_HDCP_SHA_LEN = 20,
+	CDN_DP_HDCP_DPK_LEN = 280,
+	CDN_DP_HDCP_1X_KEYS_LEN	= 285,
+	CDN_DP_HDCP_KEY_LEN = 326,
+};
+
+struct cdn_dp_hdcp_key_1x {
+	uint8_t ksv[CDN_DP_HDCP_KSV_LEN];
+	uint8_t device_key[CDN_DP_HDCP_DPK_LEN];
+	uint8_t sha1[CDN_DP_HDCP_SHA_LEN];
+	uint8_t uid[CDN_DP_HDCP_UID_LEN];
+	uint16_t seed;
+	uint8_t reserved[CDN_DP_HDCP_RESERVED_LEN];
+};
+
+#define HDCP_KEY_DATA_START_TRANSFER	0
+#define HDCP_KEY_DATA_START_DECRYPT	1
+#define HDCP_KEY_1X_STORE_DATA_ALIGN_SIZE	(6 * 64) / 8
+
+/* Checks the cdn_dp_hdcp_key_1x must be aligned on 6 x 64-bit word boundary */
+CASSERT(sizeof(struct cdn_dp_hdcp_key_1x) % HDCP_KEY_1X_STORE_DATA_ALIGN_SIZE, \
+	assert_hdcp_key_1x_store_data_align_size_mismatch);
+
+uint64_t dp_hdcp_ctrl(uint64_t type);
+
+uint64_t dp_hdcp_store_key(uint64_t x1,
+			   uint64_t x2,
+			   uint64_t x3,
+			   uint64_t x4,
+			   uint64_t x5,
+			   uint64_t x6);
+#endif
diff --git a/plat/rockchip/rk3399/plat_sip_calls.c b/plat/rockchip/rk3399/plat_sip_calls.c
index 77b7c3e..1a01604 100644
--- a/plat/rockchip/rk3399/plat_sip_calls.c
+++ b/plat/rockchip/rk3399/plat_sip_calls.c
@@ -4,6 +4,7 @@
  * SPDX-License-Identifier: BSD-3-Clause
  */
 
+#include <cdn_dp.h>
 #include <debug.h>
 #include <mmio.h>
 #include <plat_sip_calls.h>
@@ -22,6 +23,9 @@
 #define DRAM_SET_PARAM		0x07
 #define DRAM_SET_ODT_PD		0x08
 
+#define RK_SIP_HDCP_CONTROL	0x82000009
+#define RK_SIP_HDCP_KEY_DATA64	0xC200000A
+
 uint32_t ddr_smc_handler(uint64_t arg0, uint64_t arg1,
 			 uint64_t id, uint64_t arg2)
 {
@@ -51,9 +55,17 @@
 				   void *handle,
 				   uint64_t flags)
 {
+	uint64_t x5, x6;
+
 	switch (smc_fid) {
 	case RK_SIP_DDR_CFG:
 		SMC_RET1(handle, ddr_smc_handler(x1, x2, x3, x4));
+	case RK_SIP_HDCP_CONTROL:
+		SMC_RET1(handle, dp_hdcp_ctrl(x1));
+	case RK_SIP_HDCP_KEY_DATA64:
+		x5 = read_ctx_reg(get_gpregs_ctx(handle), CTX_GPREG_X5);
+		x6 = read_ctx_reg(get_gpregs_ctx(handle), CTX_GPREG_X6);
+		SMC_RET1(handle, dp_hdcp_store_key(x1, x2, x3, x4, x5, x6));
 	default:
 		ERROR("%s: unhandled SMC (0x%x)\n", __func__, smc_fid);
 		SMC_RET1(handle, SMC_UNK);