gfx: Support for HDMI repeater operations.
authorAmit Bhanagay <amit.bhanagay@intel.com>
Fri, 30 Mar 2012 02:43:24 +0000 (19:43 -0700)
committerMarkus Lehtonen <markus.lehtonen@linux.intel.com>
Tue, 3 Jul 2012 09:30:48 +0000 (12:30 +0300)
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 <amit.bhanagay@intel.com>
Signed-off-by: Artem Bityutskiy <artem.bityutskiy@linux.intel.com>
drivers/staging/mrst/drv/otm_hdmi/ipil/common/ipil_hdcp.c
drivers/staging/mrst/drv/otm_hdmi/ipil/include/hdcp_rx_defs.h
drivers/staging/mrst/drv/otm_hdmi/ipil/include/ipil_hdcp_api.h
drivers/staging/mrst/drv/otm_hdmi/ipil/specific/include/ips_hdcp_api.h
drivers/staging/mrst/drv/otm_hdmi/ipil/specific/mfld/ips_hdcp.c
drivers/staging/mrst/drv/otm_hdmi/ipil/specific/mfld/mfld_hdcp_reg.h
drivers/staging/mrst/drv/otm_hdmi/pil/common/hdcp.c

index 0497b5a..cfbc157 100644 (file)
@@ -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();
index 2326ba0..89debff 100644 (file)
@@ -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
index d504786..c028efa 100644 (file)
@@ -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);
 
index 3b94b02..2d2fc98 100644 (file)
@@ -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);
 
index 665452b..9e6d7c9 100644 (file)
@@ -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();
index a2bd817..fd9bc88 100644 (file)
 #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,
index 557cce1..d973336 100644 (file)
@@ -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;