summaryrefslogtreecommitdiff
path: root/drivers/video/fbdev
diff options
context:
space:
mode:
authorAjay Singh Parmar <aparmar@codeaurora.org>2013-10-14 16:32:34 -0700
committerDavid Keitel <dkeitel@codeaurora.org>2016-03-23 20:26:37 -0700
commitfdda2602e774c5cb93e776fa170ccdc91d25f263 (patch)
treec4689d63d793220468aff7e0994f25e0419ee4e1 /drivers/video/fbdev
parentf7655b4514c25c752737e167a4de38cf9b53cf39 (diff)
msm: mdss: hdmi: Use SCM calls for secure HDCP registers
Use TZ (Trust Zone) secure API to program HDCP (High-bandwidth Digital Content Protection) registers to aviod unauthorized access. Change-Id: I7533babd1e626cd032dd4e3e8231f5ac3061ecec Signed-off-by: Ajay Singh Parmar <aparmar@codeaurora.org>
Diffstat (limited to 'drivers/video/fbdev')
-rw-r--r--drivers/video/fbdev/msm/mdss_hdmi_hdcp.c239
-rw-r--r--drivers/video/fbdev/msm/mdss_hdmi_hdcp.h4
-rw-r--r--drivers/video/fbdev/msm/mdss_hdmi_tx.c9
3 files changed, 230 insertions, 22 deletions
diff --git a/drivers/video/fbdev/msm/mdss_hdmi_hdcp.c b/drivers/video/fbdev/msm/mdss_hdmi_hdcp.c
index 0f2d407c9d4e..fd97a522fdaa 100644
--- a/drivers/video/fbdev/msm/mdss_hdmi_hdcp.c
+++ b/drivers/video/fbdev/msm/mdss_hdmi_hdcp.c
@@ -15,6 +15,7 @@
#include <linux/delay.h>
#include <linux/slab.h>
#include <linux/stat.h>
+#include <soc/qcom/scm.h>
#include "mdss_hdmi_hdcp.h"
#include "video/msm_hdmi_hdcp_mgr.h"
@@ -31,11 +32,23 @@
#define HDCP_KEYS_STATE_PROD_AKSV 6
#define HDCP_KEYS_STATE_RESERVED 7
+#define TZ_HDCP_CMD_ID 0x00004401
+#define HDCP_REG_ENABLE 0x01
+#define HDCP_REG_DISABLE 0x00
+
#define HDCP_INT_CLR (BIT(1) | BIT(5) | BIT(7) | BIT(9) | BIT(13))
+struct hdmi_hdcp_reg_data {
+ u32 reg_id;
+ u32 off;
+ char *name;
+ u32 reg_val;
+};
+
struct hdmi_hdcp_ctrl {
u32 auth_retries;
u32 tp_msgid;
+ u32 tz_hdcp;
enum hdmi_hdcp_state hdcp_state;
struct HDCP_V2V1_MSG_TOPOLOGY cached_tp;
struct HDCP_V2V1_MSG_TOPOLOGY current_tp;
@@ -208,6 +221,11 @@ static int hdmi_hdcp_authentication_part1(struct hdmi_hdcp_ctrl *hdcp_ctrl)
u32 link0_status, an_ready, keys_state;
u8 buf[0xFF];
+ struct scm_hdcp_req scm_buf[SCM_HDCP_MAX_REG];
+ u32 phy_addr;
+ u32 ret = 0;
+ u32 resp = 0;
+
if (!hdcp_ctrl || !hdcp_ctrl->init_data.core_io ||
!hdcp_ctrl->init_data.qfprom_io) {
DEV_ERR("%s: invalid input\n", __func__);
@@ -215,6 +233,8 @@ static int hdmi_hdcp_authentication_part1(struct hdmi_hdcp_ctrl *hdcp_ctrl)
goto error;
}
+ phy_addr = hdcp_ctrl->init_data.phy_addr;
+
if (HDCP_STATE_AUTHENTICATING != hdcp_ctrl->hdcp_state) {
DEV_DBG("%s: %s: invalid state. returning\n", __func__,
HDCP_STATE_NAME);
@@ -324,7 +344,26 @@ static int hdmi_hdcp_authentication_part1(struct hdmi_hdcp_ctrl *hdcp_ctrl)
reset_hdcp_ddc_failures(hdcp_ctrl);
/* Write BCAPS to the hardware */
- DSS_REG_W(io, HDMI_HDCP_RCVPORT_DATA12, bcaps);
+
+ if (hdcp_ctrl->tz_hdcp) {
+ memset(scm_buf, 0x00, sizeof(scm_buf));
+
+ /* Write BCAPS to hardware */
+ scm_buf[0].addr = phy_addr + HDMI_HDCP_RCVPORT_DATA12;
+ scm_buf[0].val = bcaps;
+
+ ret = scm_call(SCM_SVC_HDCP, SCM_CMD_HDCP, (void *) scm_buf,
+ sizeof(scm_buf), (void *) &resp, sizeof(resp));
+
+ if (ret || resp) {
+ DEV_ERR("%s: error: scm_call ret = %d, resp = %d\n",
+ __func__, ret, resp);
+ rc = -EINVAL;
+ goto error;
+ }
+ } else {
+ DSS_REG_W(io, HDMI_HDCP_RCVPORT_DATA12, bcaps);
+ }
/*
* If we had stale values for the An ready bit, it should most
@@ -486,8 +525,27 @@ static int hdmi_hdcp_authentication_part1(struct hdmi_hdcp_ctrl *hdcp_ctrl)
link0_bksv_1, link0_bksv_0);
/* Write BKSV read from sink to HDCP registers */
- DSS_REG_W(io, HDMI_HDCP_RCVPORT_DATA0, link0_bksv_0);
- DSS_REG_W(io, HDMI_HDCP_RCVPORT_DATA1, link0_bksv_1);
+ if (hdcp_ctrl->tz_hdcp) {
+ memset(scm_buf, 0x00, sizeof(scm_buf));
+
+ scm_buf[0].addr = phy_addr + HDMI_HDCP_RCVPORT_DATA0;
+ scm_buf[0].val = link0_bksv_0;
+ scm_buf[1].addr = phy_addr + HDMI_HDCP_RCVPORT_DATA1;
+ scm_buf[1].val = link0_bksv_1;
+
+ ret = scm_call(SCM_SVC_HDCP, SCM_CMD_HDCP, (void *) scm_buf,
+ sizeof(scm_buf), (void *) &resp, sizeof(resp));
+
+ if (ret || resp) {
+ DEV_ERR("%s: error: scm_call ret = %d, resp = %d\n",
+ __func__, ret, resp);
+ rc = -EINVAL;
+ goto error;
+ }
+ } else {
+ DSS_REG_W(io, HDMI_HDCP_RCVPORT_DATA0, link0_bksv_0);
+ DSS_REG_W(io, HDMI_HDCP_RCVPORT_DATA1, link0_bksv_1);
+ }
/* Enable HDCP interrupts and ack/clear any stale interrupts */
DSS_REG_W(io, HDMI_HDCP_INT_CTRL, 0xE6);
@@ -555,7 +613,7 @@ error:
return rc;
} /* hdmi_hdcp_authentication_part1 */
-#define READ_WRITE_V_H(off, name, reg) \
+#define READ_WRITE_V_H(off, name, reg, wr) \
do { \
ddc_data.offset = (off); \
memset(what, 0, sizeof(what)); \
@@ -569,8 +627,10 @@ do { \
DEV_DBG("%s: %s: %s: buf[0]=%x, buf[1]=%x, buf[2]=%x, buf[3]=%x\n", \
__func__, HDCP_STATE_NAME, what, buf[0], buf[1], \
buf[2], buf[3]); \
- DSS_REG_W(io, (reg), \
+ if (wr) { \
+ DSS_REG_W(io, (reg), \
(buf[3] << 24 | buf[2] << 16 | buf[1] << 8 | buf[0])); \
+ } \
} while (0);
static int hdmi_hdcp_transfer_v_h(struct hdmi_hdcp_ctrl *hdcp_ctrl)
@@ -581,11 +641,28 @@ static int hdmi_hdcp_transfer_v_h(struct hdmi_hdcp_ctrl *hdcp_ctrl)
struct hdmi_tx_ddc_data ddc_data;
struct dss_io_data *io;
+ struct scm_hdcp_req scm_buf[SCM_HDCP_MAX_REG];
+ u32 phy_addr;
+
+ struct hdmi_hdcp_reg_data reg_data[] = {
+ {HDMI_HDCP_RCVPORT_DATA7, 0x20, "V' H0"},
+ {HDMI_HDCP_RCVPORT_DATA8, 0x24, "V' H1"},
+ {HDMI_HDCP_RCVPORT_DATA9, 0x28, "V' H2"},
+ {HDMI_HDCP_RCVPORT_DATA10, 0x2C, "V' H3"},
+ {HDMI_HDCP_RCVPORT_DATA11, 0x30, "V' H4"},
+ };
+ u32 size = sizeof(reg_data)/sizeof(reg_data[0]);
+ u32 iter = 0;
+ u32 ret = 0;
+ u32 resp = 0;
+
if (!hdcp_ctrl || !hdcp_ctrl->init_data.core_io) {
DEV_ERR("%s: invalid input\n", __func__);
return -EINVAL;
}
+ phy_addr = hdcp_ctrl->init_data.phy_addr;
+
io = hdcp_ctrl->init_data.core_io;
memset(&ddc_data, 0, sizeof(ddc_data));
ddc_data.dev_addr = 0x74;
@@ -596,20 +673,46 @@ static int hdmi_hdcp_transfer_v_h(struct hdmi_hdcp_ctrl *hdcp_ctrl)
ddc_data.what = what;
ddc_data.no_align = true;
- /* Read V'.HO 4 Byte at offset 0x20 */
- READ_WRITE_V_H(0x20, "V' H0", HDMI_HDCP_RCVPORT_DATA7);
+ if (hdcp_ctrl->tz_hdcp) {
+ memset(scm_buf, 0x00, sizeof(scm_buf));
+
+ for (iter = 0; iter < size && iter < SCM_HDCP_MAX_REG; iter++) {
+ struct hdmi_hdcp_reg_data *rd = reg_data + iter;
- /* Read V'.H1 4 Byte at offset 0x24 */
- READ_WRITE_V_H(0x24, "V' H1", HDMI_HDCP_RCVPORT_DATA8);
+ READ_WRITE_V_H(rd->off, rd->name, 0, false);
- /* Read V'.H2 4 Byte at offset 0x28 */
- READ_WRITE_V_H(0x28, "V' H2", HDMI_HDCP_RCVPORT_DATA9);
+ rd->reg_val = buf[3] << 24 | buf[2] << 16 |
+ buf[1] << 8 | buf[0];
+
+ scm_buf[iter].addr = phy_addr + reg_data[iter].reg_id;
+ scm_buf[iter].val = reg_data[iter].reg_val;
+ }
- /* Read V'.H3 4 Byte at offset 0x2C */
- READ_WRITE_V_H(0x2C, "V' H3", HDMI_HDCP_RCVPORT_DATA10);
+ ret = scm_call(SCM_SVC_HDCP, SCM_CMD_HDCP, (void *) scm_buf,
+ sizeof(scm_buf), (void *) &resp, sizeof(resp));
- /* Read V'.H4 4 Byte at offset 0x30 */
- READ_WRITE_V_H(0x30, "V' H4", HDMI_HDCP_RCVPORT_DATA11);
+ if (ret || resp) {
+ DEV_ERR("%s: error: scm_call ret = %d, resp = %d\n",
+ __func__, ret, resp);
+ rc = -EINVAL;
+ goto error;
+ }
+ } else {
+ /* Read V'.HO 4 Byte at offset 0x20 */
+ READ_WRITE_V_H(0x20, "V' H0", HDMI_HDCP_RCVPORT_DATA7, true);
+
+ /* Read V'.H1 4 Byte at offset 0x24 */
+ READ_WRITE_V_H(0x24, "V' H1", HDMI_HDCP_RCVPORT_DATA8, true);
+
+ /* Read V'.H2 4 Byte at offset 0x28 */
+ READ_WRITE_V_H(0x28, "V' H2", HDMI_HDCP_RCVPORT_DATA9, true);
+
+ /* Read V'.H3 4 Byte at offset 0x2C */
+ READ_WRITE_V_H(0x2C, "V' H3", HDMI_HDCP_RCVPORT_DATA10, true);
+
+ /* Read V'.H4 4 Byte at offset 0x30 */
+ READ_WRITE_V_H(0x30, "V' H4", HDMI_HDCP_RCVPORT_DATA11, true);
+ }
error:
return rc;
@@ -629,12 +732,19 @@ static int hdmi_hdcp_authentication_part2(struct hdmi_hdcp_ctrl *hdcp_ctrl)
u32 ksv_bytes;
struct dss_io_data *io;
+ struct scm_hdcp_req scm_buf[SCM_HDCP_MAX_REG];
+ u32 phy_addr;
+ u32 ret = 0;
+ u32 resp = 0;
+
if (!hdcp_ctrl || !hdcp_ctrl->init_data.core_io) {
DEV_ERR("%s: invalid input\n", __func__);
rc = -EINVAL;
goto error;
}
+ phy_addr = hdcp_ctrl->init_data.phy_addr;
+
if (HDCP_STATE_AUTHENTICATING != hdcp_ctrl->hdcp_state) {
DEV_DBG("%s: %s: invalid state. returning\n", __func__,
HDCP_STATE_NAME);
@@ -695,8 +805,25 @@ static int hdmi_hdcp_authentication_part2(struct hdmi_hdcp_ctrl *hdcp_ctrl)
bstatus = buf[1];
bstatus = (bstatus << 8) | buf[0];
- /* Write BSTATUS and BCAPS to HDCP registers */
- DSS_REG_W(io, HDMI_HDCP_RCVPORT_DATA12, bcaps | (bstatus << 8));
+ if (hdcp_ctrl->tz_hdcp) {
+ memset(scm_buf, 0x00, sizeof(scm_buf));
+
+ /* Write BSTATUS and BCAPS to HDCP registers */
+ scm_buf[0].addr = phy_addr + HDMI_HDCP_RCVPORT_DATA12;
+ scm_buf[0].val = bcaps | (bstatus << 8);
+
+ ret = scm_call(SCM_SVC_HDCP, SCM_CMD_HDCP, (void *) scm_buf,
+ sizeof(scm_buf), (void *) &resp, sizeof(resp));
+
+ if (ret || resp) {
+ DEV_ERR("%s: error: scm_call ret = %d, resp = %d\n",
+ __func__, ret, resp);
+ rc = -EINVAL;
+ goto error;
+ }
+ } else {
+ DSS_REG_W(io, HDMI_HDCP_RCVPORT_DATA12, bcaps | (bstatus << 8));
+ }
down_stream_devices = bstatus & 0x7F;
if (down_stream_devices == 0) {
@@ -791,13 +918,51 @@ static int hdmi_hdcp_authentication_part2(struct hdmi_hdcp_ctrl *hdcp_ctrl)
*/
/* First, reset SHA engine */
- DSS_REG_W(io, HDMI_HDCP_SHA_CTRL, 1);
/* Next, enable SHA engine, SEL=DIGA_HDCP */
- DSS_REG_W(io, HDMI_HDCP_SHA_CTRL, 0);
+ if (hdcp_ctrl->tz_hdcp) {
+ memset(scm_buf, 0x00, sizeof(scm_buf));
+
+ scm_buf[0].addr = phy_addr + HDMI_HDCP_SHA_CTRL;
+ scm_buf[0].val = HDCP_REG_ENABLE;
+ scm_buf[1].addr = phy_addr + HDMI_HDCP_SHA_CTRL;
+ scm_buf[1].val = HDCP_REG_DISABLE;
+
+ ret = scm_call(SCM_SVC_HDCP, SCM_CMD_HDCP, (void *) scm_buf,
+ sizeof(scm_buf), (void *) &resp,
+ sizeof(resp));
+
+ if (ret || resp) {
+ DEV_ERR("%s: error: scm_call ret = %d, resp = %d\n",
+ __func__, ret, resp);
+ rc = -EINVAL;
+ goto error;
+ }
+ } else {
+ DSS_REG_W(io, HDMI_HDCP_SHA_CTRL, HDCP_REG_ENABLE);
+ DSS_REG_W(io, HDMI_HDCP_SHA_CTRL, HDCP_REG_DISABLE);
+ }
for (i = 0; i < ksv_bytes - 1; i++) {
/* Write KSV byte and do not set DONE bit[0] */
- DSS_REG_W_ND(io, HDMI_HDCP_SHA_DATA, ksv_fifo[i] << 16);
+ if (hdcp_ctrl->tz_hdcp) {
+ memset(scm_buf, 0x00, sizeof(scm_buf));
+
+ scm_buf[0].addr = phy_addr + HDMI_HDCP_SHA_DATA;
+ scm_buf[0].val = ksv_fifo[i] << 16;
+
+ ret = scm_call(SCM_SVC_HDCP, SCM_CMD_HDCP,
+ (void *)scm_buf, sizeof(scm_buf),
+ (void *) &resp, sizeof(resp));
+
+ if (ret || resp) {
+ DEV_ERR("%s: scm_call ret = %d, resp = %d\n",
+ __func__, ret, resp);
+ rc = -EINVAL;
+ goto error;
+ }
+ } else {
+ DSS_REG_W_ND(io, HDMI_HDCP_SHA_DATA, ksv_fifo[i] << 16);
+ }
/*
* Once 64 bytes have been written, we need to poll for
@@ -825,8 +990,25 @@ static int hdmi_hdcp_authentication_part2(struct hdmi_hdcp_ctrl *hdcp_ctrl)
}
/* Write l to DONE bit[0] */
- DSS_REG_W_ND(io, HDMI_HDCP_SHA_DATA,
+ if (hdcp_ctrl->tz_hdcp) {
+ memset(scm_buf, 0x00, sizeof(scm_buf));
+
+ scm_buf[0].addr = phy_addr + HDMI_HDCP_SHA_DATA;
+ scm_buf[0].val = (ksv_fifo[ksv_bytes - 1] << 16) | 0x1;
+
+ ret = scm_call(SCM_SVC_HDCP, SCM_CMD_HDCP, (void *) scm_buf,
+ sizeof(scm_buf), (void *) &resp, sizeof(resp));
+
+ if (ret || resp) {
+ DEV_ERR("%s: error: scm_call ret = %d, resp = %d\n",
+ __func__, ret, resp);
+ rc = -EINVAL;
+ goto error;
+ }
+ } else {
+ DSS_REG_W_ND(io, HDMI_HDCP_SHA_DATA,
(ksv_fifo[ksv_bytes - 1] << 16) | 0x1);
+ }
/* Now wait for HDCP_SHA_COMP_DONE */
timeout_count = 100;
@@ -1360,6 +1542,9 @@ void hdmi_hdcp_deinit(void *input)
void *hdmi_hdcp_init(struct hdmi_hdcp_init_data *init_data)
{
struct hdmi_hdcp_ctrl *hdcp_ctrl = NULL;
+ u32 scm_buf = TZ_HDCP_CMD_ID;
+ u32 ret = 0;
+ u32 resp = 0;
if (!init_data || !init_data->core_io || !init_data->qfprom_io ||
!init_data->mutex || !init_data->ddc_ctrl ||
@@ -1388,6 +1573,18 @@ void *hdmi_hdcp_init(struct hdmi_hdcp_init_data *init_data)
hdcp_ctrl->hdcp_state = HDCP_STATE_INACTIVE;
init_completion(&hdcp_ctrl->r0_checked);
+
+ ret = scm_call(SCM_SVC_INFO, SCM_CMD_HDCP, (void *) &scm_buf,
+ sizeof(scm_buf), (void *) &resp, sizeof(resp));
+
+ if (ret) {
+ DEV_ERR("%s: error: scm_call ret = %d, resp = %d\n",
+ __func__, ret, resp);
+ } else {
+ DEV_DBG("%s: tz_hdcp = %d\n", __func__, resp);
+ hdcp_ctrl->tz_hdcp = resp;
+ }
+
DEV_DBG("%s: HDCP module initialized. HDCP_STATE=%s", __func__,
HDCP_STATE_NAME);
diff --git a/drivers/video/fbdev/msm/mdss_hdmi_hdcp.h b/drivers/video/fbdev/msm/mdss_hdmi_hdcp.h
index 2f0c3b9f2bab..b1326512d1b8 100644
--- a/drivers/video/fbdev/msm/mdss_hdmi_hdcp.h
+++ b/drivers/video/fbdev/msm/mdss_hdmi_hdcp.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012 The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012, 2014 The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -32,6 +32,8 @@ struct hdmi_hdcp_init_data {
void (*notify_status)(void *cb_data, enum hdmi_hdcp_state status);
struct hdmi_tx_ddc_ctrl *ddc_ctrl;
+
+ u32 phy_addr;
};
const char *hdcp_state_name(enum hdmi_hdcp_state hdcp_state);
diff --git a/drivers/video/fbdev/msm/mdss_hdmi_tx.c b/drivers/video/fbdev/msm/mdss_hdmi_tx.c
index ae6fd583a599..e512edce557d 100644
--- a/drivers/video/fbdev/msm/mdss_hdmi_tx.c
+++ b/drivers/video/fbdev/msm/mdss_hdmi_tx.c
@@ -1070,6 +1070,15 @@ static int hdmi_tx_init_features(struct hdmi_tx_ctrl *hdmi_ctrl)
/* Initialize HDCP feature */
if (hdmi_ctrl->present_hdcp) {
+ struct resource *res = NULL;
+ res = platform_get_resource_byname(hdmi_ctrl->pdev,
+ IORESOURCE_MEM, hdmi_tx_io_name(HDMI_TX_CORE_IO));
+ if (!res) {
+ DEV_ERR("%s: Error getting HDMI tx core resource\n",
+ __func__);
+ return -ENODEV;
+ }
+ hdcp_init_data.phy_addr = res->start;
hdcp_init_data.core_io = &hdmi_ctrl->pdata.io[HDMI_TX_CORE_IO];
hdcp_init_data.qfprom_io =
&hdmi_ctrl->pdata.io[HDMI_TX_QFPROM_IO];