diff options
| author | Ajay Singh Parmar <aparmar@codeaurora.org> | 2013-10-14 16:32:34 -0700 |
|---|---|---|
| committer | David Keitel <dkeitel@codeaurora.org> | 2016-03-23 20:26:37 -0700 |
| commit | fdda2602e774c5cb93e776fa170ccdc91d25f263 (patch) | |
| tree | c4689d63d793220468aff7e0994f25e0419ee4e1 /drivers | |
| parent | f7655b4514c25c752737e167a4de38cf9b53cf39 (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')
| -rw-r--r-- | drivers/video/fbdev/msm/mdss_hdmi_hdcp.c | 239 | ||||
| -rw-r--r-- | drivers/video/fbdev/msm/mdss_hdmi_hdcp.h | 4 | ||||
| -rw-r--r-- | drivers/video/fbdev/msm/mdss_hdmi_tx.c | 9 |
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]; |
