On Tue, Jan 13, 2015 at 12:43 PM, Jilai Wang <jilaiw@xxxxxxxxxxxxxx> 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 -- To unsubscribe from this list: send the line "unsubscribe linux-arm-msm" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html