[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, &reg, &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, &reg, &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 = &reg_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