From: Amit Bhanagay Date: Fri, 30 Mar 2012 02:43:24 +0000 (-0700) Subject: gfx: Support for HDMI repeater operations. X-Git-Tag: 2.1b_release~179 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=b7982b26a4ab29475bd2acd355b3a48bb0534bd4;p=kernel%2Fkernel-mfld-blackbay.git gfx: Support for HDMI repeater operations. Phase 2 authentication protocol is required (in addition to phase 1 and 3) if the HDMI receiver is an HDMI repeater. The protocol assembles the list of all downstream KSVs attached to the receiver and verifies its integrity. Issue: ANDROID-238 Signed-off-by: Amit Bhanagay Signed-off-by: Artem Bityutskiy --- diff --git a/drivers/staging/mrst/drv/otm_hdmi/ipil/common/ipil_hdcp.c b/drivers/staging/mrst/drv/otm_hdmi/ipil/common/ipil_hdcp.c index 0497b5a..cfbc157 100644 --- a/drivers/staging/mrst/drv/otm_hdmi/ipil/common/ipil_hdcp.c +++ b/drivers/staging/mrst/drv/otm_hdmi/ipil/common/ipil_hdcp.c @@ -123,6 +123,20 @@ bool ipil_hdcp_enable_encryption(void) return ips_hdcp_enable_encryption(); } +bool ipil_hdcp_compute_tx_v(uint8_t *rep_ksv_list, + uint32_t rep_ksv_list_entries, + uint16_t topology_data) +{ + return ips_hdcp_compute_tx_v(rep_ksv_list, + rep_ksv_list_entries, + topology_data); +} + +bool ipil_hdcp_compare_v(uint32_t *rep_prime_v) +{ + return ips_hdcp_compare_v(rep_prime_v); +} + bool ipil_hdcp_disable(void) { return ips_hdcp_disable(); diff --git a/drivers/staging/mrst/drv/otm_hdmi/ipil/include/hdcp_rx_defs.h b/drivers/staging/mrst/drv/otm_hdmi/ipil/include/hdcp_rx_defs.h index 2326ba0..89debff 100644 --- a/drivers/staging/mrst/drv/otm_hdmi/ipil/include/hdcp_rx_defs.h +++ b/drivers/staging/mrst/drv/otm_hdmi/ipil/include/hdcp_rx_defs.h @@ -68,6 +68,9 @@ #define HDCP_PRIMARY_I2C_ADDR 0x3A +#define HDCP_MAX_RETRY_STATUS (1500) +#define HDCP_MAX_DEVICES (127) + #define HDCP_KSV_SIZE 0x05 #define HDCP_KSV_HAMMING_WT (20) #define HDCP_AN_SIZE 0x08 diff --git a/drivers/staging/mrst/drv/otm_hdmi/ipil/include/ipil_hdcp_api.h b/drivers/staging/mrst/drv/otm_hdmi/ipil/include/ipil_hdcp_api.h index d504786..c028efa 100644 --- a/drivers/staging/mrst/drv/otm_hdmi/ipil/include/ipil_hdcp_api.h +++ b/drivers/staging/mrst/drv/otm_hdmi/ipil/include/ipil_hdcp_api.h @@ -78,6 +78,10 @@ extern bool ipil_hdcp_start_authentication(void); extern bool ipil_hdcp_is_r0_ready(void); extern bool ipil_hdcp_does_ri_match(uint16_t rx_ri); extern bool ipil_hdcp_enable_encryption(void); +extern bool ipil_hdcp_compute_tx_v(uint8_t *rep_ksv_list, + uint32_t rep_ksv_list_entries, + uint16_t topology_data); +extern bool ipil_hdcp_compare_v(uint32_t *rep_prime_v); extern bool ipil_hdcp_disable(void); extern bool ipil_hdcp_device_can_authenticate(void); diff --git a/drivers/staging/mrst/drv/otm_hdmi/ipil/specific/include/ips_hdcp_api.h b/drivers/staging/mrst/drv/otm_hdmi/ipil/specific/include/ips_hdcp_api.h index 3b94b02..2d2fc98 100644 --- a/drivers/staging/mrst/drv/otm_hdmi/ipil/specific/include/ips_hdcp_api.h +++ b/drivers/staging/mrst/drv/otm_hdmi/ipil/specific/include/ips_hdcp_api.h @@ -80,6 +80,10 @@ extern bool ips_hdcp_set_repeater(bool present); extern bool ips_hdcp_start_authentication(void); extern bool ips_hdcp_enable_encryption(void); extern bool ips_hdcp_does_ri_match(uint16_t rx_ri); +extern bool ips_hdcp_compute_tx_v(uint8_t *rep_ksv_list, + uint32_t rep_ksv_list_entries, + uint16_t topology_data); +extern bool ips_hdcp_compare_v(uint32_t *rep_prime_v); extern bool ips_hdcp_disable(void); extern bool ips_hdcp_device_can_authenticate(void); diff --git a/drivers/staging/mrst/drv/otm_hdmi/ipil/specific/mfld/ips_hdcp.c b/drivers/staging/mrst/drv/otm_hdmi/ipil/specific/mfld/ips_hdcp.c index 665452b..9e6d7c9 100644 --- a/drivers/staging/mrst/drv/otm_hdmi/ipil/specific/mfld/ips_hdcp.c +++ b/drivers/staging/mrst/drv/otm_hdmi/ipil/specific/mfld/ips_hdcp.c @@ -81,9 +81,9 @@ static bool ips_hdcp_is_encrypting(void) __attribute__((unused)); static uint8_t ips_hdcp_get_repeater_control(void)__attribute__((unused)); static void ips_hdcp_set_repeater_control(int value) __attribute__((unused)); static uint8_t ips_hdcp_get_repeater_status(void); -static int ips_hdcp_repeater_v_match_check(void) __attribute__((unused)); +static int ips_hdcp_repeater_v_match_check(void); static bool ips_hdcp_repeater_is_busy(void) __attribute__((unused)); -static int ips_hdcp_repeater_rdy_for_nxt_data(void) __attribute__((unused)); +static bool ips_hdcp_repeater_rdy_for_nxt_data(void); static uint32_t ips_hdcp_get_status(void) { @@ -221,7 +221,7 @@ static bool ips_hdcp_repeater_is_busy(void) return false; } -static int ips_hdcp_repeater_rdy_for_nxt_data(void) +static bool ips_hdcp_repeater_rdy_for_nxt_data(void) { uint8_t status = ips_hdcp_get_repeater_status(); if (status == HDCP_REPEATER_STATUS_RDY_NEXT_DATA) @@ -229,6 +229,34 @@ static int ips_hdcp_repeater_rdy_for_nxt_data(void) return false; } +static bool ips_hdcp_repeater_rdy_for_idle(void) +{ + uint8_t status = ips_hdcp_get_repeater_status(); + if (status == HDCP_REPEATER_STATUS_IDLE) + return true; + return false; +} + +static bool ips_hdcp_repeater_wait_for_next_data(void) +{ + uint16_t i = 0; + for (; i < HDCP_MAX_RETRY_STATUS; i++) { + if (ips_hdcp_repeater_rdy_for_nxt_data()) + return true; + } + return false; +} + +static bool ips_hdcp_repeater_wait_for_idle(void) +{ + uint16_t i = 0; + for (; i < HDCP_MAX_RETRY_STATUS; i++) { + if (ips_hdcp_repeater_rdy_for_idle()) + return true; + } + return false; +} + bool ips_hdcp_is_ready(void) { struct ips_hdcp_status_reg_t status; @@ -347,6 +375,273 @@ bool ips_hdcp_does_ri_match(uint16_t rx_ri) return false; } +bool ips_hdcp_compute_tx_v(uint8_t *rep_ksv_list, + uint32_t rep_ksv_list_entries, + uint16_t topology_data) +{ + bool ret = false; + const uint8_t BSTAT_M0_LEN = 18; /* 2 (bstatus) + 8 (M0) + 8 (length) */ + const uint8_t BSTAT_M0 = 10; /* 2 (bstatus) + 8 (M0) */ + const uint8_t M0 = 8; /* 8 (M0) */ + uint32_t num_devices = rep_ksv_list_entries; + uint32_t lower_num_bytes_for_sha = 0, num_pad_bytes = 0, temp_data = 0; + uint32_t rem_text_data = 0, num_mo_bytes_left = M0, value = 0, i = 0; + uint8_t *buffer = NULL, *temp_buffer = NULL, *temp_data_ptr = NULL; + struct ips_hdcp_repeater_reg_t hdcp_rep_ctrl_reg; + struct sqword_t buffer_len; + + /* Clear SHA hash generator for new V calculation and + * set the repeater to idle state + */ + hdmi_write32(MDFLD_HDCP_SHA1_IN, 0); + + hdcp_rep_ctrl_reg.value = hdmi_read32(MDFLD_HDCP_REP_REG); + hdcp_rep_ctrl_reg.control = HDCP_REPEATER_CTRL_IDLE; + hdmi_write32(MDFLD_HDCP_REP_REG, hdcp_rep_ctrl_reg.value); + if (!ips_hdcp_repeater_wait_for_idle()) + return false; + + /* Start the SHA buffer creation to find the number of pad bytes */ + num_pad_bytes = (64 - ((rep_ksv_list_entries * HDCP_KSV_SIZE) + + BSTAT_M0_LEN) + % 64); + + /* Get the number of bytes for SHA */ + lower_num_bytes_for_sha = (HDCP_KSV_SIZE * num_devices) + + BSTAT_M0_LEN + + num_pad_bytes; /* multiple of 64 bytes */ + + buffer = kzalloc(lower_num_bytes_for_sha, GFP_KERNEL); + if (!buffer) + return false; + + /* 1. Copy the KSV buffer + * Note: data is in little endian format + */ + temp_buffer = buffer; + memcpy((void *)temp_buffer, (void *)rep_ksv_list, + num_devices * HDCP_KSV_SIZE); + temp_buffer += num_devices * HDCP_KSV_SIZE; + + /* 2. Copy the topology_data + */ + memcpy((void *)temp_buffer, (void *)&topology_data, 2); + /* bstatus is copied in little endian format */ + temp_buffer += 2; + + /* 3. Offset the pointer buffer by 8 bytes + * These 8 bytes are zeroed and are place holders for M0 + */ + temp_buffer += 8; + + /* 4. Pad the buffer with extra bytes + * No need to pad the begining of padding bytes by adding + * 0x80. HW automatically appends the same while creating + * the buffer. + */ + for (i = 0; i < num_pad_bytes; i++) { + *temp_buffer = (uint8_t)0x00; + temp_buffer++; + } + + /* 5. Construct the length byte */ + buffer_len.quad_part = (unsigned long long)(rep_ksv_list_entries * + HDCP_KSV_SIZE + BSTAT_M0) * 8; /* in bits */ + temp_data_ptr = (uint8_t *)&buffer_len.quad_part; + /* Store in big endian form, it is reversed to little endian + * when fed to SHA1 + */ + for (i = 1; i <= 8; i++) { + *temp_buffer = *(temp_data_ptr + 8 - i); + temp_buffer++; + } + + /* 6. Write KSV's and bstatus into SHA */ + temp_buffer = buffer; + for (i = 0; i < (HDCP_KSV_SIZE * num_devices + 2)/4; i++) { + hdcp_rep_ctrl_reg.value = hdmi_read32(MDFLD_HDCP_REP_REG); + hdcp_rep_ctrl_reg.control = HDCP_REPEATER_32BIT_TEXT_IP; + hdmi_write32(MDFLD_HDCP_REP_REG, hdcp_rep_ctrl_reg.value); + + /* As per HDCP spec sample SHA is in little endian format. + * But the data fed to the cipher needs to be in big endian + * format for it to compute it correctly + */ + memcpy(&value, temp_buffer, 4); + value = HDCP_CONVERT_ENDIANNESS(value); + hdmi_write32(MDFLD_HDCP_SHA1_IN, value); + temp_buffer += 4; + + if (false == ips_hdcp_repeater_wait_for_next_data()) + goto exit; + } + + /* 7. Write the remaining bstatus data and M0 + * Text input must be aligned to LSB of the SHA1 + * in register when inputting partial text and partial M0 + */ + rem_text_data = (HDCP_KSV_SIZE * num_devices + 2) % 4; + if (rem_text_data) { + /* Update the number of M0 bytes */ + num_mo_bytes_left = num_mo_bytes_left - (4-rem_text_data); + + if (false == ips_hdcp_repeater_wait_for_next_data()) + goto exit; + + hdcp_rep_ctrl_reg.value = hdmi_read32(MDFLD_HDCP_REP_REG); + switch (rem_text_data) { + case 1: + hdcp_rep_ctrl_reg.control = + HDCP_REPEATER_8BIT_TEXT_24BIT_MO_IP; + break; + case 2: + hdcp_rep_ctrl_reg.control = + HDCP_REPEATER_16BIT_TEXT_16BIT_MO_IP; + break; + case 3: + hdcp_rep_ctrl_reg.control = + HDCP_REPEATER_24BIT_TEXT_8BIT_MO_IP; + break; + default: + goto exit; + } + + hdmi_write32(MDFLD_HDCP_REP_REG, hdcp_rep_ctrl_reg.value); + memcpy(&value, temp_buffer, 4); + + /* Swap the text data in big endian format leaving the M0 data + * as it is. LSB should contain the data in big endian format. + * Since the M0 specfic data is all zeros while it's fed to the + * cipher, those bit don't need to be modified. + */ + temp_data = 0; + for (i = 0; i < rem_text_data; i++) { + temp_data |= ((value & 0xff << (i * 8)) >> + (i * 8)) << + ((rem_text_data - i - 1) * 8); + } + hdmi_write32(MDFLD_HDCP_SHA1_IN, temp_data); + temp_buffer += 4; + } + + /* write 4 bytes of M0 */ + if (false == ips_hdcp_repeater_wait_for_next_data()) + goto exit; + + hdcp_rep_ctrl_reg.value = hdmi_read32(MDFLD_HDCP_REP_REG); + hdcp_rep_ctrl_reg.control = HDCP_REPEATER_32BIT_MO_IP; + hdmi_write32(MDFLD_HDCP_REP_REG, hdcp_rep_ctrl_reg.value); + hdmi_write32(MDFLD_HDCP_SHA1_IN, (uint32_t)temp_buffer); + temp_buffer += 4; + num_mo_bytes_left -= 4; + + if (num_mo_bytes_left) { + /* The remaining M0 + padding bytes need to be added */ + num_pad_bytes = num_pad_bytes - (4 - num_mo_bytes_left); + + /* write 4 bytes of M0 */ + if (false == ips_hdcp_repeater_wait_for_next_data()) + goto exit; + hdcp_rep_ctrl_reg.value = hdmi_read32(MDFLD_HDCP_REP_REG); + switch (num_mo_bytes_left) { + case 1: + hdcp_rep_ctrl_reg.control = + HDCP_REPEATER_24BIT_TEXT_8BIT_MO_IP; + break; + case 2: + hdcp_rep_ctrl_reg.control = + HDCP_REPEATER_16BIT_TEXT_16BIT_MO_IP; + break; + case 3: + hdcp_rep_ctrl_reg.control = + HDCP_REPEATER_8BIT_TEXT_24BIT_MO_IP; + break; + default: + /* should never happen */ + goto exit; + } + + hdmi_write32(MDFLD_HDCP_REP_REG, hdcp_rep_ctrl_reg.value); + hdmi_write32(MDFLD_HDCP_SHA1_IN, (uint32_t)temp_buffer); + temp_buffer += 4; + num_mo_bytes_left = 0; + } + + /* 8. Write the remaining padding bytes and length */ + /* Remaining data = remaining padding data + 64 bits of length data */ + rem_text_data = num_pad_bytes + 8; + + if (rem_text_data % 4) { + /* Should not happen */ + pr_debug("hdcp: compute_tx_v - data not aligned\n"); + goto exit; + } + + for (i = 0; i < rem_text_data / 4; i++) { + if (false == ips_hdcp_repeater_wait_for_next_data()) + goto exit; + + hdcp_rep_ctrl_reg.value = hdmi_read32(MDFLD_HDCP_REP_REG); + hdcp_rep_ctrl_reg.control = HDCP_REPEATER_32BIT_TEXT_IP; + hdmi_write32(MDFLD_HDCP_REP_REG, hdcp_rep_ctrl_reg.value); + memcpy(&value, temp_buffer, 4); + /* Do the big endian conversion */ + value = HDCP_CONVERT_ENDIANNESS(value); + hdmi_write32(MDFLD_HDCP_SHA1_IN, value); + temp_buffer += 4; + } + + /* Done */ + ret = true; + +exit: + kfree(buffer); + return ret; +} + +bool ips_hdcp_compare_v(uint32_t *rep_prime_v) +{ + bool ret = false; + uint32_t i = 10, stat; + struct ips_hdcp_repeater_reg_t hdcp_rep_ctrl_reg; + + /* Load V' */ + hdmi_write32(MDFLD_HDCP_VPRIME_H0, *rep_prime_v); + + hdmi_write32(MDFLD_HDCP_VPRIME_H1, *(rep_prime_v + 1)); + + hdmi_write32(MDFLD_HDCP_VPRIME_H2, *(rep_prime_v + 2)); + + hdmi_write32(MDFLD_HDCP_VPRIME_H3, *(rep_prime_v + 3)); + + hdmi_write32(MDFLD_HDCP_VPRIME_H4, *(rep_prime_v + 4)); + + if (false == ips_hdcp_repeater_wait_for_next_data()) + goto exit; + + /* Set HDCP_REP to do the comparison, start + * transmitter's V calculation + */ + hdcp_rep_ctrl_reg.value = hdmi_read32(MDFLD_HDCP_REP_REG); + hdcp_rep_ctrl_reg.control = HDCP_REPEATER_COMPLETE_SHA1; + hdmi_write32(MDFLD_HDCP_REP_REG, hdcp_rep_ctrl_reg.value); + + msleep(5); + do { + stat = ips_hdcp_repeater_v_match_check(); + if (1 == stat) { + ret = true; /* match */ + break; + } else if (-1 == stat) + msleep(5); /* busy, retry */ + else + break; /* mismatch */ + } while (--i); + +exit: + return ret; +} + bool ips_hdcp_disable(void) { ips_hdcp_off(); diff --git a/drivers/staging/mrst/drv/otm_hdmi/ipil/specific/mfld/mfld_hdcp_reg.h b/drivers/staging/mrst/drv/otm_hdmi/ipil/specific/mfld/mfld_hdcp_reg.h index a2bd817..fd9bc88 100644 --- a/drivers/staging/mrst/drv/otm_hdmi/ipil/specific/mfld/mfld_hdcp_reg.h +++ b/drivers/staging/mrst/drv/otm_hdmi/ipil/specific/mfld/mfld_hdcp_reg.h @@ -97,6 +97,10 @@ #define MDFLD_HDCP_AKEY_MED_REG 0x61420 #define MDFLD_HDCP_AKEY_HI_REG 0x61424 +#define HDCP_CONVERT_ENDIANNESS(x) (((x&0x000000ff)<<24)|\ + ((x&0x0000ff00)<<8)|\ + ((x&0x00ff0000)>>8)|\ + ((x&0xff000000)>>24)) struct double_word_t { union { @@ -111,6 +115,18 @@ struct double_word_t { }; }; +struct sqword_t { + union { + unsigned long long quad_part; + struct { + unsigned long low_part; + unsigned long high_part; + } u; + struct { + uint8_t byte[8]; + }; + }; +}; enum ips_hdcp_config_enum { HDCP_Off = 0, diff --git a/drivers/staging/mrst/drv/otm_hdmi/pil/common/hdcp.c b/drivers/staging/mrst/drv/otm_hdmi/pil/common/hdcp.c index 557cce1..d973336 100644 --- a/drivers/staging/mrst/drv/otm_hdmi/pil/common/hdcp.c +++ b/drivers/staging/mrst/drv/otm_hdmi/pil/common/hdcp.c @@ -106,13 +106,16 @@ struct hdcp_context_t { /*!< indicates HDCP Authentication currently allowed */ bool is_required; - bool is_enabled; + bool is_phase1_enabled; + bool is_phase2_enabled; bool suspend; bool hpd; bool display_power_on; bool auto_retry; bool wdt_expired; + unsigned int wdt_id; + /*!< phase 2 auth watchdog timer id */ unsigned int current_srm_ver; /*!< currently used SRM version (if vrl is not null) */ @@ -293,12 +296,44 @@ static bool hdcp_read_rx_r0(uint16_t *rx_r0) return hdcp_read_rx_ri(rx_r0); } +static bool hdcp_read_rx_ksv_list(uint8_t *ksv_list, uint32_t size) +{ + bool ret = false; + if (ksv_list != NULL && size) { + if (hdcp_ddc_read(HDCP_RX_KSV_FIFO_ADDR, + ksv_list, size) == true) { + ret = true; + } + } + return ret; +} + +static bool hdcp_read_rx_v(uint8_t *v) +{ + bool ret = false; + uint8_t *buf = v; + uint8_t offset = HDCP_RX_V_H0_ADDR; + + if (v != NULL) { + for (; offset <= HDCP_RX_V_H4_ADDR; offset += 4) { + if (hdcp_ddc_read(offset, buf, 4) == false) { + pr_debug("hdcp: read rx v failure\n"); + break; + } + buf += 4; + } + if (offset > HDCP_RX_V_H4_ADDR) + ret = true; + } + return ret; +} + static bool hdcp_stage3_ri_check(void) { uint16_t rx_ri = 0; if (hdcp_enable_condition_ready() == false || - hdcp_context->is_enabled == false) + hdcp_context->is_phase1_enabled == false) return false; #ifdef OTM_HDCP_DEBUG_MODULE @@ -347,7 +382,9 @@ static void hdcp_reset(void) /* Stop HDCP */ ipil_hdcp_disable(); - hdcp_context->is_enabled = false; + hdcp_context->is_phase1_enabled = false; + hdcp_context->is_phase2_enabled = false; + hdcp_context->wdt_id++; } static bool hdcp_rep_check(void) @@ -359,7 +396,13 @@ static bool hdcp_rep_check(void) static bool hdcp_rep_watch_dog(void) { int msg = HDCP_REPEATER_WDT_EXPIRED; - return wq_send_message_delayed(msg, NULL, 5000); + unsigned int *wdt_id = kmalloc(sizeof(unsigned int), GFP_KERNEL); + if (wdt_id != NULL) { + *wdt_id = hdcp_context->wdt_id; + return wq_send_message_delayed(msg, (void *)wdt_id, 5000); + } else + pr_debug("hdcp: %s failed to alloc mem\n", __func__); + return false; } static bool hdcp_initiate_rep_auth(void) @@ -485,25 +528,108 @@ static int hdcp_stage1_authentication(bool *is_repeater) return false; pr_debug("hdcp: encryption enabled\n"); - hdcp_context->is_enabled = true; + hdcp_context->is_phase1_enabled = true; return true; } static int hdcp_stage2_repeater_authentication(void) { - /* TODO: Repeater Authentication */ + uint8_t *rep_ksv_list = NULL; + uint32_t rep_prime_v[HDCP_V_H_SIZE] = {0}; + struct hdcp_rx_bstatus_t bstatus; + struct hdcp_rx_bcaps_t bcaps; + bool ret = false; + + /* Repeater Authentication */ if (hdcp_enable_condition_ready() == false || - hdcp_context->is_enabled == false || - hdcp_context->wdt_expired == false) + hdcp_context->is_phase1_enabled == false || + hdcp_context->wdt_expired == true) { + pr_debug("hdcp: stage2 auth condition not ready\n"); return false; + } - /* check validity of repeater depth & device count */ - /* check if fifo ready */ - /* read ksv list */ - /* verify SHA1 */ - /* validate KSV's */ - return true; + /* Read BCAPS */ + if (hdcp_read_bcaps(&bcaps.value) == false) + return false; + + if (!bcaps.is_repeater) + return false; + + /* Check if fifo ready */ + if (!bcaps.ksv_fifo_ready) { + /* reschedule if not ready */ + pr_debug("hdcp: rescheduling repeater auth\n"); + msleep(100); + hdcp_rep_check(); + return true; + } + + /* Read BSTATUS */ + if (hdcp_read_bstatus(&bstatus.value) == false) + return false; + + /* Check validity of repeater depth & device count */ + if (bstatus.max_devs_exceeded) + return false; + + if (bstatus.max_cascade_exceeded) + return false; + + if (0 == bstatus.device_count) + return true; + + if (bstatus.device_count > HDCP_MAX_DEVICES) + return false; + + /* Set repeater bit */ + if (ipil_hdcp_set_repeater(bcaps.is_repeater) == false) + return false; + + /* Read ksv list from repeater */ + rep_ksv_list = kzalloc(bstatus.device_count * HDCP_KSV_SIZE, + GFP_KERNEL); + if (!rep_ksv_list) { + pr_debug("hdcp: rep ksv list alloc failure\n"); + return false; + } + + if (hdcp_read_rx_ksv_list(rep_ksv_list, + bstatus.device_count * HDCP_KSV_SIZE) + == false) { + pr_debug("hdcp: rep ksv list read failure\n"); + goto exit; + } + + /* TODO: SRM check */ + + /* Compute tx(v) */ + if (ipil_hdcp_compute_tx_v(rep_ksv_list, bstatus.device_count, + bstatus.value) == false) { + pr_debug("hdcp: rep compute tx v failure\n"); + goto exit; + } + + /* Read rx(v') */ + if (hdcp_read_rx_v((uint8_t *)rep_prime_v) == false) { + pr_debug("hdcp: rep read rx v failure\n"); + ipil_hdcp_set_repeater(0); + goto exit; + } + + /* Verify SHA1 tx(v) = rx(v') */ + if (ipil_hdcp_compare_v(rep_prime_v) == false) { + pr_debug("hdcp: rep compare v failure\n"); + goto exit; + } + + pr_debug("hdcp: repeater auth success\n"); + hdcp_context->is_phase2_enabled = true; + ret = true; + +exit: + kfree(rep_ksv_list); + return ret; } static bool hdcp_start(void) @@ -539,7 +665,7 @@ static void hdcp_retry_enable(void) { int msg = HDCP_ENABLE; if (hdcp_enable_condition_ready() == true && - hdcp_context->is_enabled == false && + hdcp_context->is_phase1_enabled == false && hdcp_context->auto_retry == true) { wq_send_message_delayed(msg, NULL, 30); pr_debug("hdcp: retry enable\n"); @@ -567,7 +693,7 @@ static void hdcp_task_event_handler(struct work_struct *work) switch (msg) { case HDCP_ENABLE: if (hdcp_enable_condition_ready() == true && - hdcp_context->is_enabled == false && + hdcp_context->is_phase1_enabled == false && hdcp_start() == false) { reset_hdcp = true; pr_debug("hdcp: failed to start hdcp\n"); @@ -588,9 +714,16 @@ static void hdcp_task_event_handler(struct work_struct *work) break; case HDCP_REPEATER_WDT_EXPIRED: - /* TODO */ - hdcp_context->wdt_expired = true; - pr_debug("hdcp: repeater wdt expired\n"); + if (msg_data != NULL) { + pr_debug("hdcp: reapter wdt expired, " + "wdt_id = %d, msg_data = %d\n", + hdcp_context->wdt_id, + *(unsigned int *)msg_data); + hdcp_context->wdt_expired = true; + if (hdcp_context->wdt_id == *((unsigned int *)msg_data) + && !hdcp_context->is_phase2_enabled) + reset_hdcp = true; + } break; case HDCP_RESET: @@ -603,7 +736,8 @@ static void hdcp_task_event_handler(struct work_struct *work) pr_debug("hdcp: suspend = %d\n", hdcp_context->suspend); if (hdcp_context->suspend == true) { - if (hdcp_context->is_enabled == true) + if (hdcp_context->is_phase1_enabled + == true) reset_hdcp = true; } } @@ -837,13 +971,15 @@ bool otm_hdmi_hdcp_init(hdmi_context_t *hdmi_context, } hdcp_context->is_required = false; - hdcp_context->is_enabled = false; + hdcp_context->is_phase1_enabled = false; + hdcp_context->is_phase2_enabled = false; hdcp_context->suspend = false; hdcp_context->hpd = false; hdcp_context->display_power_on = false; hdcp_context->auto_retry = true; hdcp_context->wdt_expired = false; hdcp_context->can_authenticate = true; + hdcp_context->wdt_id = 1u; hdcp_context->current_srm_ver = 0u; hdcp_context->vrl = NULL; hdcp_context->vrl_count = 0u;