On Tue, Jan 13, 2015 at 12:43 PM, Jilai Wang jilaiw@codeaurora.org wrote:
Add HDMI HDCP support including HDCP PartI/II/III authentication. V1: Initial Change V2: Address Bjorn&Rob's comments Refactor the authentication process to use single work instead of multiple work for different authentication stages.
Looks cleaner and the SCM parts look good now.
But the ddc communication still makes me wonder, see below.
[..]
diff --git a/drivers/gpu/drm/msm/hdmi/hdmi_hdcp.c b/drivers/gpu/drm/msm/hdmi/hdmi_hdcp.c
[..]
+static int hdmi_ddc_read(struct hdmi *hdmi, u16 addr, u8 offset,
u8 *data, u16 data_len)
+{
int rc;
int retry = 5;
struct i2c_msg msgs[] = {
{
.addr = addr >> 1,
.flags = 0,
.len = 1,
.buf = &offset,
}, {
.addr = addr >> 1,
.flags = I2C_M_RD,
.len = data_len,
.buf = data,
}
};
DBG("Start DDC read");
+retry:
rc = i2c_transfer(hdmi->i2c, msgs, 2);
retry--;
if (rc == 2)
rc = 0;
else if (retry > 0)
goto retry;
else
rc = -EIO;
DBG("End DDC read %d", rc);
return rc;
+}
Looking back and forth at this, to me this really looks like i2c_smbus_read_i2c_block_data().
+#define HDCP_DDC_WRITE_MAX_BYTE_NUM 32
This matches I2C_SMBUS_BLOCK_MAX, indicating further that there's something in common here.
+static int hdmi_ddc_write(struct hdmi *hdmi, u16 addr, u8 offset,
u8 *data, u16 data_len)
+{
int rc;
int retry = 10;
u8 buf[HDCP_DDC_WRITE_MAX_BYTE_NUM];
struct i2c_msg msgs[] = {
{
.addr = addr >> 1,
.flags = 0,
.len = 1,
}
};
DBG("Start DDC write");
if (data_len > (HDCP_DDC_WRITE_MAX_BYTE_NUM - 1)) {
pr_err("%s: write size too big\n", __func__);
return -ERANGE;
}
buf[0] = offset;
memcpy(&buf[1], data, data_len);
msgs[0].buf = buf;
msgs[0].len = data_len + 1;
+retry:
rc = i2c_transfer(hdmi->i2c, msgs, 1);
retry--;
if (rc == 1)
rc = 0;
else if (retry > 0)
goto retry;
else
rc = -EIO;
DBG("End DDC write %d", rc);
return rc;
+}
And this looks like i2c_smbus_write_i2c_block_data()
[..]
+static int hdmi_hdcp_send_aksv_an(struct hdmi_hdcp_ctrl *hdcp_ctrl) +{
int rc = 0;
struct hdmi *hdmi = hdcp_ctrl->hdmi;
u32 link0_aksv_0, link0_aksv_1;
u32 link0_an[2];
u8 aksv[5];
/* Read An0 and An1 */
link0_an[0] = hdmi_read(hdmi, REG_HDMI_HDCP_RCVPORT_DATA5);
link0_an[1] = hdmi_read(hdmi, REG_HDMI_HDCP_RCVPORT_DATA6);
/* Read AKSV */
link0_aksv_0 = hdmi_read(hdmi, REG_HDMI_HDCP_RCVPORT_DATA3);
link0_aksv_1 = hdmi_read(hdmi, REG_HDMI_HDCP_RCVPORT_DATA4);
DBG("Link ASKV=%08x%08x", link0_aksv_0, link0_aksv_1);
/* Copy An and AKSV to byte arrays for transmission */
aksv[0] = link0_aksv_0 & 0xFF;
aksv[1] = (link0_aksv_0 >> 8) & 0xFF;
aksv[2] = (link0_aksv_0 >> 16) & 0xFF;
aksv[3] = (link0_aksv_0 >> 24) & 0xFF;
aksv[4] = link0_aksv_1 & 0xFF;
/* Write An to offset 0x18 */
rc = hdmi_ddc_write(hdmi, HDCP_PORT_ADDR, 0x18, (u8 *)link0_an,
(u16)sizeof(link0_an));
rc = i2c_smbus_write_i2c_block_data(hdmi->i2c, 0x18, sizeof(link0_an), link0_an); if (rc < 0) {
if (rc) {
pr_err("%s:An write failed\n", __func__);
return rc;
}
DBG("Link0-An=%08x%08x", link0_an[0], link0_an[1]);
/* Write AKSV to offset 0x10 */
rc = hdmi_ddc_write(hdmi, HDCP_PORT_ADDR, 0x10, aksv, 5);
rc = i2c_smbus_write_i2c_block_data(hdmi->i2c, 0x10, sizeof(aksv), aksv); if (rc < 0) {
if (rc) {
pr_err("%s:AKSV write failed\n", __func__);
return rc;
}
DBG("Link0-AKSV=%02x%08x", link0_aksv_1 & 0xFF, link0_aksv_0);
return 0;
+}
+static int hdmi_hdcp_recv_bksv(struct hdmi_hdcp_ctrl *hdcp_ctrl) +{
int rc = 0;
struct hdmi *hdmi = hdcp_ctrl->hdmi;
u8 bksv[5];
u32 reg[2], data[2];
/* Read BKSV at offset 0x00 */
rc = hdmi_ddc_read(hdmi, HDCP_PORT_ADDR, 0x00, bksv, 5);
if (rc) {
You should be able to replace this with:
rc = i2c_smbus_read_i2c_block_data(hdmi->i2c, 0x0, 5, bksv); if (rc < 0) {
pr_err("%s:BKSV read failed\n", __func__);
return rc;
}
hdcp_ctrl->bksv_lsb = bksv[0] | (bksv[1] << 8) |
(bksv[2] << 16) | (bksv[3] << 24);
hdcp_ctrl->bksv_msb = bksv[4];
DBG(":BKSV=%02x%08x", hdcp_ctrl->bksv_msb, hdcp_ctrl->bksv_lsb);
/* check there are 20 ones in BKSV */
if ((hweight32(hdcp_ctrl->bksv_lsb) + hweight32(hdcp_ctrl->bksv_msb))
!= 20) {
pr_err(": BKSV doesn't have 20 1's and 20 0's\n");
pr_err(": BKSV chk fail. BKSV=%02x%02x%02x%02x%02x\n",
bksv[4], bksv[3], bksv[2], bksv[1], bksv[0]);
return -EINVAL;
}
/* Write BKSV read from sink to HDCP registers */
reg[0] = REG_HDMI_HDCP_RCVPORT_DATA0;
data[0] = hdcp_ctrl->bksv_lsb;
reg[1] = REG_HDMI_HDCP_RCVPORT_DATA1;
data[1] = hdcp_ctrl->bksv_msb;
rc = hdmi_hdcp_scm_wr(hdcp_ctrl, reg, data, 2);
return rc;
+}
+static int hdmi_hdcp_recv_bcaps(struct hdmi_hdcp_ctrl *hdcp_ctrl) +{
int rc = 0;
struct hdmi *hdmi = hdcp_ctrl->hdmi;
u32 reg, data;
u8 bcaps;
rc = hdmi_ddc_read(hdmi, HDCP_PORT_ADDR, 0x40, &bcaps, 1);
int bcaps; bcaps = i2c_smbus_read_byte_data(hdmi->i2c, 0x40); if (bcaps < 0) {
if (rc) {
pr_err("%s:BCAPS read failed\n", __func__);
return rc;
}
DBG("BCAPS=%02x", bcaps);
/* receiver (0), repeater (1) */
hdcp_ctrl->ds_type = (bcaps & BIT(6)) ? DS_REPEATER : DS_RECEIVER;
/* Write BCAPS to the hardware */
reg = REG_HDMI_HDCP_RCVPORT_DATA12;
data = (u32)bcaps;
rc = hdmi_hdcp_scm_wr(hdcp_ctrl, ®, &data, 1);
return rc;
+}
[..]
+/* read R0' from sink and pass it to HDCP engine */ +static int hdmi_hdcp_auth_part1_recv_r0(struct hdmi_hdcp_ctrl *hdcp_ctrl) +{
struct hdmi *hdmi = hdcp_ctrl->hdmi;
int rc = 0;
u8 buf[2];
/*
* HDCP Compliance Test case 1A-01:
* Wait here at least 100ms before reading R0'
*/
rc = hdmi_hdcp_msleep(hdcp_ctrl, 125, AUTH_ABORT_EV);
if (rc)
return rc;
/* Read R0' at offset 0x08 */
rc = hdmi_ddc_read(hdmi, HDCP_PORT_ADDR, 0x08, buf, 2);
rc = i2c_smbus_read_word_data(hdmi->i2c, 0x8); if (rc < 0) {
}
if (rc) {
pr_err("%s:R0' read failed\n", __func__);
return rc;
}
DBG("R0'=%02x%02x", buf[1], buf[0]);
/* Write R0' to HDCP registers and check to see if it is a match */
hdmi_write(hdmi, REG_HDMI_HDCP_RCVPORT_DATA2_0,
(((u32)buf[1]) << 8) | buf[0]);
hdmi_write(hdmi, REG_HDMI_HDCP_RCVPORT_DATA2_0, rc);
return 0;
+}
[..]
+static int hdmi_hdcp_recv_check_bstatus(struct hdmi_hdcp_ctrl *hdcp_ctrl,
u16 *pbstatus)
+{
int rc;
struct hdmi *hdmi = hdcp_ctrl->hdmi;
bool max_devs_exceeded = false, max_cascade_exceeded = false;
u32 repeater_cascade_depth = 0, down_stream_devices = 0;
u16 bstatus;
u8 buf[2];
/* Read BSTATUS at offset 0x41 */
rc = hdmi_ddc_read(hdmi, HDCP_PORT_ADDR, 0x41, buf, 2);
rc = i2c_smbus_read_word_data(hdmi->i2c, 0x41); if (rc < 0) {
}
*pbstatus = bstatus = rc;
if (rc) {
pr_err("%s: BSTATUS read failed\n", __func__);
goto error;
}
*pbstatus = bstatus = (buf[1] << 8) | buf[0];
down_stream_devices = bstatus & 0x7F;
repeater_cascade_depth = (bstatus >> 8) & 0x7;
max_devs_exceeded = (bstatus & BIT(7)) ? true : false;
max_cascade_exceeded = (bstatus & BIT(11)) ? true : false;
if (down_stream_devices == 0) {
/*
* If no downstream devices are attached to the repeater
* then part II fails.
* todo: The other approach would be to continue PART II.
*/
pr_err("%s: No downstream devices\n", __func__);
rc = -EINVAL;
goto error;
}
/*
* HDCP Compliance 1B-05:
* Check if no. of devices connected to repeater
* exceed max_devices_connected from bit 7 of Bstatus.
*/
if (max_devs_exceeded) {
pr_err("%s: no. of devs connected exceeds max allowed",
__func__);
rc = -EINVAL;
goto error;
}
/*
* HDCP Compliance 1B-06:
* Check if no. of cascade connected to repeater
* exceed max_cascade_connected from bit 11 of Bstatus.
*/
if (max_cascade_exceeded) {
pr_err("%s: no. of cascade conn exceeds max allowed",
__func__);
rc = -EINVAL;
goto error;
}
+error:
hdcp_ctrl->dev_count = down_stream_devices;
hdcp_ctrl->max_cascade_exceeded = max_cascade_exceeded;
hdcp_ctrl->max_dev_exceeded = max_devs_exceeded;
hdcp_ctrl->depth = repeater_cascade_depth;
return rc;
+}
+static int hdmi_hdcp_auth_part2_wait_ksv_fifo_ready(
struct hdmi_hdcp_ctrl *hdcp_ctrl)
+{
int rc;
struct hdmi *hdmi = hdcp_ctrl->hdmi;
u32 reg, data;
u32 timeout_count;
u16 bstatus;
u8 bcaps;
/*
* Wait until READY bit is set in BCAPS, as per HDCP specifications
* maximum permitted time to check for READY bit is five seconds.
*/
timeout_count = 100;
do {
/* Read BCAPS at offset 0x40 */
rc = hdmi_ddc_read(hdmi, HDCP_PORT_ADDR, 0x40, &bcaps, 1);
rc = i2c_smbus_read_byte_data(hdmi->i2c, 0x40); if (rc < 0) {
} bcaps = rc;
if (rc) {
pr_err("%s: BCAPS read failed\n", __func__);
return rc;
}
if (bcaps & BIT(5))
break;
timeout_count--;
if (!timeout_count) {
pr_err("%s: Wait KSV fifo ready timedout", __func__);
return -ETIMEDOUT;
}
rc = hdmi_hdcp_msleep(hdcp_ctrl, 20, AUTH_ABORT_EV);
if (rc)
return rc;
} while (1);
rc = hdmi_hdcp_recv_check_bstatus(hdcp_ctrl, &bstatus);
if (rc) {
pr_err("%s: bstatus error\n", __func__);
return rc;
}
/* Write BSTATUS and BCAPS to HDCP registers */
reg = REG_HDMI_HDCP_RCVPORT_DATA12;
data = bcaps | (bstatus << 8);
rc = hdmi_hdcp_scm_wr(hdcp_ctrl, ®, &data, 1);
if (rc) {
pr_err("%s: BSTATUS write failed\n", __func__);
return rc;
}
return 0;
+}
+/*
- hdcp authenticating part 2: 2nd
- read ksv fifo from sink
- transfer V' from sink to HDCP engine
- reset SHA engine
- */
+static int hdmi_hdcp_transfer_v_h(struct hdmi_hdcp_ctrl *hdcp_ctrl) +{
struct hdmi *hdmi = hdcp_ctrl->hdmi;
int rc = 0;
struct hdmi_hdcp_reg_data reg_data[] = {
{REG_HDMI_HDCP_RCVPORT_DATA7, 0x20, "V' H0"},
{REG_HDMI_HDCP_RCVPORT_DATA8, 0x24, "V' H1"},
{REG_HDMI_HDCP_RCVPORT_DATA9, 0x28, "V' H2"},
{REG_HDMI_HDCP_RCVPORT_DATA10, 0x2C, "V' H3"},
{REG_HDMI_HDCP_RCVPORT_DATA11, 0x30, "V' H4"},
};
struct hdmi_hdcp_reg_data *rd;
u32 size = ARRAY_SIZE(reg_data);
u32 reg[ARRAY_SIZE(reg_data)];
u32 data[ARRAY_SIZE(reg_data)];
int i;
for (i = 0; i < size; i++) {
rd = ®_data[i];
rc = hdmi_ddc_read(hdmi, HDCP_PORT_ADDR,
rd->off, (u8 *)&data[i], (u16)sizeof(data[i]));
rc = i2c_smbus_read_i2c_block_data(hdmi->i2c, rd->off, sizeof(u32), &data[i]); if (rc < 0) {
if (rc) {
pr_err("%s: Read %s failed\n", __func__, rd->name);
goto error;
}
DBG("%s =%x", rd->name, data[i]);
reg[i] = reg_data[i].reg_id;
}
rc = hdmi_hdcp_scm_wr(hdcp_ctrl, reg, data, size);
+error:
return rc;
+}
+static int hdmi_hdcp_recv_ksv_fifo(struct hdmi_hdcp_ctrl *hdcp_ctrl) +{
int rc;
struct hdmi *hdmi = hdcp_ctrl->hdmi;
u32 ksv_bytes;
ksv_bytes = 5 * hdcp_ctrl->dev_count;
rc = hdmi_ddc_read(hdmi, HDCP_PORT_ADDR, 0x43,
hdcp_ctrl->ksv_list, ksv_bytes);
rc = i2c_smbus_read_i2c_block_data(hdmi->i2c, 0x43, ksv_bytes, hdcp_ctrl->ksv_list); if (rc < 0) {
if (rc)
pr_err("%s: KSV FIFO read failed\n", __func__);
return rc;
+}
Maybe I'm missundersanding how smbus or ddc works, please let me know what you think.
Regards, Bjorn