/*----------------------------------------------------------------------------- * Function: hdcp_ddc_abort *----------------------------------------------------------------------------- */ void hdcp_ddc_abort(void) { unsigned long flags; /* In case of I2C_NO_ACK error, do not abort DDC to avoid * DDC lockup */ if (RD_REG_32(hdcp.hdmi_wp_base_addr + HDMI_IP_CORE_SYSTEM, HDMI_IP_CORE_SYSTEM__DDC_STATUS) & 0x20) return; spin_lock_irqsave(&hdcp.spinlock, flags); /* Abort Master DDC operation and Clear FIFO pointer */ WR_REG_32(hdcp.hdmi_wp_base_addr + HDMI_IP_CORE_SYSTEM, HDMI_IP_CORE_SYSTEM__DDC_CMD, MASTER_CMD_ABORT); /* Read to flush */ RD_REG_32(hdcp.hdmi_wp_base_addr + HDMI_IP_CORE_SYSTEM, HDMI_IP_CORE_SYSTEM__DDC_CMD); WR_REG_32(hdcp.hdmi_wp_base_addr + HDMI_IP_CORE_SYSTEM, HDMI_IP_CORE_SYSTEM__DDC_CMD, MASTER_CMD_CLEAR_FIFO); /* Read to flush */ RD_REG_32(hdcp.hdmi_wp_base_addr + HDMI_IP_CORE_SYSTEM, HDMI_IP_CORE_SYSTEM__DDC_CMD); spin_unlock_irqrestore(&hdcp.spinlock, flags); }
/*----------------------------------------------------------------------------- * Function: hdcp_3des_encrypt_key *----------------------------------------------------------------------------- */ void hdcp_3des_encrypt_key(struct hdcp_encrypt_control *enc_ctrl, uint32_t out_key[DESHDCP_KEY_SIZE]) { int counter = 0; DBG("Encrypting HDCP keys..."); /* Reset encrypted key array */ for (counter = 0; counter < DESHDCP_KEY_SIZE; counter++) out_key[counter] = 0; /* Set encryption mode in DES control register */ WR_FIELD_32(hdcp.deshdcp_base_addr, DESHDCP__DHDCP_CTRL, DESHDCP__DHDCP_CTRL__DIRECTION_POS_F, DESHDCP__DHDCP_CTRL__DIRECTION_POS_L, 0x1); /* Write raw data and read encrypted data */ counter = 0; #ifdef POWER_TRANSITION_DBG printk(KERN_ERR "\n**************************\n" "ENCRYPTION: WAIT FOR DSS TRANSITION\n" "*************************\n"); mdelay(10000); printk(KER_INFO "\n**************************\n" "DONE\n" "*************************\n"); #endif while (counter < DESHDCP_KEY_SIZE) { /* Fill Data registers */ WR_REG_32(hdcp.deshdcp_base_addr, DESHDCP__DHDCP_DATA_L, enc_ctrl->in_key[counter]); WR_REG_32(hdcp.deshdcp_base_addr, DESHDCP__DHDCP_DATA_H, enc_ctrl->in_key[counter + 1]); /* Wait for output bit at '1' */ while (RD_FIELD_32(hdcp.deshdcp_base_addr, DESHDCP__DHDCP_CTRL, DESHDCP__DHDCP_CTRL__OUTPUT_READY_POS_F, DESHDCP__DHDCP_CTRL__OUTPUT_READY_POS_L ) != 0x1) ; /* Read enrypted data */ out_key[counter] = RD_REG_32(hdcp.deshdcp_base_addr, DESHDCP__DHDCP_DATA_L); out_key[counter + 1] = RD_REG_32(hdcp.deshdcp_base_addr, DESHDCP__DHDCP_DATA_H); counter += 2; } }
int hdcp_3des_load_key(uint32_t *deshdcp_encrypted_key) { int counter = 0, status = HDCP_OK; if (!deshdcp_encrypted_key) { HDCP_ERR("HDCP keys NULL, failed to load keys\n"); return HDCP_3DES_ERROR; } HDCP_DBG("Loading HDCP keys...\n"); /* Set decryption mode in DES control register */ WR_FIELD_32(hdcp.deshdcp_base_addr, DESHDCP__DHDCP_CTRL, DESHDCP__DHDCP_CTRL__DIRECTION_POS_F, DESHDCP__DHDCP_CTRL__DIRECTION_POS_L, 0x0); /* Write encrypted data */ while (counter < DESHDCP_KEY_SIZE) { /* Fill Data registers */ WR_REG_32(hdcp.deshdcp_base_addr, DESHDCP__DHDCP_DATA_L, deshdcp_encrypted_key[counter]); WR_REG_32(hdcp.deshdcp_base_addr, DESHDCP__DHDCP_DATA_H, deshdcp_encrypted_key[counter + 1]); /* Wait for output bit at '1' */ while (RD_FIELD_32(hdcp.deshdcp_base_addr, DESHDCP__DHDCP_CTRL, DESHDCP__DHDCP_CTRL__OUTPUT_READY_POS_F, DESHDCP__DHDCP_CTRL__OUTPUT_READY_POS_L) != 0x1) ; /* Dummy read (indeed data are transfered directly into * key memory) */ if (RD_REG_32(hdcp.deshdcp_base_addr, DESHDCP__DHDCP_DATA_L) != 0x0) { status = -HDCP_3DES_ERROR; HDCP_ERR("DESHDCP dummy read error\n"); } if (RD_REG_32(hdcp.deshdcp_base_addr, DESHDCP__DHDCP_DATA_H) != 0x0) { status = -HDCP_3DES_ERROR; HDCP_ERR("DESHDCP dummy read error\n"); } counter += 2; } if (status == HDCP_OK) hdcp.hdcp_keys_loaded = true; return status; }
/*----------------------------------------------------------------------------- * Function: hdcp_lib_set_av_mute *----------------------------------------------------------------------------- */ void hdcp_lib_set_av_mute(enum av_mute av_mute_state) { unsigned long flags; DBG("hdcp_lib_set_av_mute() av_mute=%d", av_mute_state); spin_lock_irqsave(&hdcp.spinlock, flags); { u8 RegVal, TimeOutCount = 64; RegVal = RD_REG_32(hdcp.hdmi_wp_base_addr + HDMI_CORE_AV_BASE, HDMI_CORE_AV_PB_CTRL2); /* PRguide-GPC: To change the content of the CP_BYTE1 register, * CP_EN must be zero * set PB_CTRL2 :: CP_RPT = 0 */ WR_FIELD_32(hdcp.hdmi_wp_base_addr + HDMI_CORE_AV_BASE, HDMI_CORE_AV_PB_CTRL2, 2, 2, 0); /* Set/clear AV mute state */ WR_REG_32(hdcp.hdmi_wp_base_addr + HDMI_CORE_AV_BASE, HDMI_CORE_AV_CP_BYTE1, av_mute_state); /* FIXME: This loop should be removed */ while (TimeOutCount--) { /* Continue in this loop till CP_EN becomes 0, * prior to TimeOutCount becoming 0 */ if (!RD_FIELD_32(hdcp.hdmi_wp_base_addr + HDMI_CORE_AV_BASE, HDMI_CORE_AV_PB_CTRL2, 3, 3)) break; } DBG(" timeoutcount=%d", TimeOutCount); /* FIXME: why is this if condition required?, according to prg, * this shall be unconditioanlly */ if (TimeOutCount) { /* set PB_CTRL2 :: CP_EN = 1 & CP_RPT = 1 */ RegVal = FLD_MOD(RegVal, 0x3, 3, 2); WR_REG_32(hdcp.hdmi_wp_base_addr + HDMI_CORE_AV_BASE, HDMI_CORE_AV_PB_CTRL2, RegVal); } } spin_unlock_irqrestore(&hdcp.spinlock, flags); }
/*----------------------------------------------------------------------------- * Function: hdcp_lib_auto_bcaps_rdy_check *----------------------------------------------------------------------------- */ void hdcp_lib_auto_bcaps_rdy_check(bool state) { u8 reg_val; unsigned long flags; DBG("hdcp_lib_auto_bcaps_rdy_check() state=%s", state == true ? "ON" : "OFF"); spin_lock_irqsave(&hdcp.spinlock, flags); /* Enable KSV_READY / BACP_DONE interrupt */ WR_FIELD_32(hdcp.hdmi_wp_base_addr + HDMI_IP_CORE_SYSTEM, HDMI_IP_CORE_SYSTEM__INT_UNMASK2, 7, 7, ((state == true) ? 1 : 0)); /* Enable/Disable Ri & Bcap */ reg_val = RD_REG_32(hdcp.hdmi_wp_base_addr + HDMI_IP_CORE_SYSTEM, HDMI_IP_CORE_SYSTEM__RI_CMD); /* Enable RI_EN & BCAP_EN OR disable BCAP_EN */ reg_val = (state == true) ? (reg_val | 0x3) : (reg_val & ~0x2); WR_REG_32(hdcp.hdmi_wp_base_addr + HDMI_IP_CORE_SYSTEM, HDMI_IP_CORE_SYSTEM__RI_CMD, reg_val); /* Read to flush */ RD_REG_32(hdcp.hdmi_wp_base_addr + HDMI_IP_CORE_SYSTEM, HDMI_IP_CORE_SYSTEM__RI_CMD); spin_unlock_irqrestore(&hdcp.spinlock, flags); DBG("hdcp_lib_auto_bcaps_rdy_check() Done\n"); }
/*----------------------------------------------------------------------------- * Function: hdcp_lib_auto_ri_check *----------------------------------------------------------------------------- */ void hdcp_lib_auto_ri_check(bool state) { u8 reg_val; unsigned long flags; DBG("hdcp_lib_auto_ri_check() state=%s", state == true ? "ON" : "OFF"); spin_lock_irqsave(&hdcp.spinlock, flags); reg_val = RD_REG_32(hdcp.hdmi_wp_base_addr + HDMI_IP_CORE_SYSTEM, HDMI_IP_CORE_SYSTEM__INT_UNMASK3); reg_val = (state == true) ? (reg_val | 0xB0) : (reg_val & ~0xB0); /* Turn on/off the following Auto Ri interrupts */ WR_REG_32(hdcp.hdmi_wp_base_addr + HDMI_IP_CORE_SYSTEM, HDMI_IP_CORE_SYSTEM__INT_UNMASK3, reg_val); /* Enable/Disable Ri */ WR_FIELD_32(hdcp.hdmi_wp_base_addr + HDMI_IP_CORE_SYSTEM, HDMI_IP_CORE_SYSTEM__RI_CMD, 0, 0, ((state == true) ? 1 : 0)); /* Read to flush */ RD_REG_32(hdcp.hdmi_wp_base_addr + HDMI_IP_CORE_SYSTEM, HDMI_IP_CORE_SYSTEM__RI_CMD); spin_unlock_irqrestore(&hdcp.spinlock, flags); }
/*----------------------------------------------------------------------------- * Function: hdcp_lib_write_bksv *----------------------------------------------------------------------------- */ static void hdcp_lib_write_bksv(u8 *ksv_data) { u8 i; for (i = 0; i < 5; i++) { WR_REG_32(hdcp.hdmi_wp_base_addr + HDMI_IP_CORE_SYSTEM, HDMI_IP_CORE_SYSTEM__BKSV0 + i * sizeof(uint32_t), ksv_data[i]); } }
/*----------------------------------------------------------------------------- * Function: hdcp_start_ddc_transfer *----------------------------------------------------------------------------- */ static int hdcp_start_ddc_transfer(mddc_type *mddc_cmd, u8 operation) { u8 *cmd = (u8 *)mddc_cmd; struct timeval t0, t1, t2; u32 time_elapsed_ms = 0; u32 i, size; unsigned long flags; #ifdef _9032_AUTO_RI_ if (hdcp_suspend_resume_auto_ri(AUTO_RI_SUSPEND)) return -HDCP_DDC_ERROR; #endif spin_lock_irqsave(&hdcp.spinlock, flags); /* Abort Master DDC operation and Clear FIFO pointer */ WR_REG_32(hdcp.hdmi_wp_base_addr + HDMI_IP_CORE_SYSTEM, HDMI_IP_CORE_SYSTEM__DDC_CMD, MASTER_CMD_CLEAR_FIFO); /* Read to flush */ RD_REG_32(hdcp.hdmi_wp_base_addr + HDMI_IP_CORE_SYSTEM, HDMI_IP_CORE_SYSTEM__DDC_CMD); /* Sending DDC header, it'll clear DDC Status register too */ for (i = 0; i < 7; i++) { WR_REG_32(hdcp.hdmi_wp_base_addr + HDMI_IP_CORE_SYSTEM, HDMI_IP_CORE_SYSTEM__DDC_ADDR + i * sizeof(uint32_t), cmd[i]); /* Read to flush */ RD_REG_32(hdcp.hdmi_wp_base_addr + HDMI_IP_CORE_SYSTEM, HDMI_IP_CORE_SYSTEM__DDC_ADDR + i * sizeof(uint32_t)); } spin_unlock_irqrestore(&hdcp.spinlock, flags); do_gettimeofday(&t1); memcpy(&t0, &t1, sizeof(t0)); i = 0; size = mddc_cmd->nbytes_lsb + (mddc_cmd->nbytes_msb << 8); while ((i < size) && (hdcp.pending_disable == 0)) { if (operation == DDC_WRITE) { /* Write data to DDC FIFO as long as it is NOT full */ if (RD_FIELD_32(hdcp.hdmi_wp_base_addr + HDMI_IP_CORE_SYSTEM, HDMI_IP_CORE_SYSTEM__DDC_STATUS, 3, 3) == 0) { WR_REG_32(hdcp.hdmi_wp_base_addr + HDMI_IP_CORE_SYSTEM, HDMI_IP_CORE_SYSTEM__DDC_DATA, mddc_cmd->pdata[i++]); do_gettimeofday(&t1); } } else if (operation == DDC_READ) { /* Read from DDC FIFO as long as it is NOT empty */ if (RD_FIELD_32(hdcp.hdmi_wp_base_addr + HDMI_IP_CORE_SYSTEM, HDMI_IP_CORE_SYSTEM__DDC_STATUS, 2, 2) == 0) { mddc_cmd->pdata[i++] = RD_REG_32(hdcp.hdmi_wp_base_addr + HDMI_IP_CORE_SYSTEM, HDMI_IP_CORE_SYSTEM__DDC_DATA); do_gettimeofday(&t1); } } do_gettimeofday(&t2); time_elapsed_ms = (t2.tv_sec - t1.tv_sec) * 1000 + (t2.tv_usec - t1.tv_usec) / 1000; if (time_elapsed_ms > HDCP_DDC_TIMEOUT) { DBG("DDC timeout - no data during %d ms - " "status=%02x %u", HDCP_DDC_TIMEOUT, RD_REG_32(hdcp.hdmi_wp_base_addr + HDMI_IP_CORE_SYSTEM, HDMI_IP_CORE_SYSTEM__DDC_STATUS), jiffies_to_msecs(jiffies)); goto ddc_error; } } if (hdcp.pending_disable) goto ddc_abort; /* Wait for the FIFO to be empty (end of transfer) */ while ((RD_REG_32(hdcp.hdmi_wp_base_addr + HDMI_IP_CORE_SYSTEM, HDMI_IP_CORE_SYSTEM__DDC_STATUS) != 0x4) && (hdcp.pending_disable == 0)) { do_gettimeofday(&t2); time_elapsed_ms = (t2.tv_sec - t1.tv_sec) * 1000 + (t2.tv_usec - t1.tv_usec) / 1000; if (time_elapsed_ms > HDCP_DDC_TIMEOUT) { DBG("DDC timeout - FIFO not getting empty - " "status=%02x", RD_REG_32(hdcp.hdmi_wp_base_addr + HDMI_IP_CORE_SYSTEM, HDMI_IP_CORE_SYSTEM__DDC_STATUS)); goto ddc_error; } } if (hdcp.pending_disable) goto ddc_abort; DBG("DDC transfer: bytes: %d time_us: %lu status: %x", i, (t2.tv_sec - t0.tv_sec) * 1000000 + (t2.tv_usec - t0.tv_usec), RD_REG_32(hdcp.hdmi_wp_base_addr + HDMI_IP_CORE_SYSTEM, HDMI_IP_CORE_SYSTEM__DDC_STATUS)); #ifdef DDC_DBG { int k; for (k = 0; k < i; k++) printk(KERN_DEBUG "%02x ", mddc_cmd->pdata[k]); printk(KERN_DEBUG "\n"); } #endif #ifdef _9032_AUTO_RI_ /* Re-enable Auto Ri */ if (hdcp_suspend_resume_auto_ri(AUTO_RI_RESUME)) return -HDCP_DDC_ERROR; #endif return HDCP_OK; ddc_error: hdcp_ddc_abort(); return -HDCP_DDC_ERROR; ddc_abort: DBG("DDC transfer aborted - status=%02x", RD_REG_32(hdcp.hdmi_wp_base_addr + HDMI_IP_CORE_SYSTEM, HDMI_IP_CORE_SYSTEM__DDC_STATUS)); return HDCP_OK; }