[PATCH 2/2] drm/msm/hdmi: add hdmi hdcp support (V2)
Bjorn Andersson
bjorn at kryo.se
Fri Jan 30 13:51:19 PST 2015
On Tue, Jan 13, 2015 at 12:43 PM, Jilai Wang <jilaiw at 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
More information about the dri-devel
mailing list