/*----------------------------------------------------------------------------- * Function: hdcp_irq_cb *----------------------------------------------------------------------------- */ static void hdcp_irq_cb(int status) { DBG("hdcp_irq_cb() status=%x", status); if (!hdcp.hdcp_keys_loaded) { DBG("%s: hdcp_keys not loaded = %d", __func__, hdcp.hdcp_keys_loaded); return; } /* Disable auto Ri/BCAPS immediately */ if (((status & HDMI_RI_ERR) || (status & HDMI_BCAP) || (status & HDMI_HPD_LOW)) && (hdcp.hdcp_state != HDCP_ENABLE_PENDING)) { hdcp_lib_auto_ri_check(false); hdcp_lib_auto_bcaps_rdy_check(false); } /* Work queue execution not required if HDCP is disabled */ /* TODO: ignore interrupts if they are masked (cannnot access UMASK * here so should use global variable */ if ((hdcp.hdcp_state != HDCP_DISABLED) && (hdcp.hdcp_state != HDCP_ENABLE_PENDING)) { if (status & HDMI_HPD_LOW) { hdcp_lib_set_encryption(HDCP_ENC_OFF); hdcp_ddc_abort(); } if (status & HDMI_RI_ERR) { hdcp_lib_set_av_mute(AV_MUTE_SET); hdcp_lib_set_encryption(HDCP_ENC_OFF); hdcp_submit_work(HDCP_RI_FAIL_EVENT, 0); } /* RI error takes precedence over BCAP */ else if (status & HDMI_BCAP) hdcp_submit_work(HDCP_KSV_LIST_RDY_EVENT, 0); } if (status & HDMI_HPD_LOW) { hdcp.pending_disable = 1; /* Used to exit on-going HDCP * work */ hdcp.hpd_low = 0; /* Used to cancel HDCP works */ hdcp_lib_disable(); /* In case of HDCP_STOP_FRAME_EVENT, HDCP stop * frame callback is blocked and waiting for * HDCP driver to finish accessing the HW * before returning * Reason is to avoid HDMI driver to shutdown * DSS/HDMI power before HDCP work is finished */ hdcp.hdmi_state = HDMI_STOPPED; hdcp.hdcp_state = HDCP_ENABLE_PENDING; hdcp.auth_state = HDCP_STATE_DISABLED; } }
/*----------------------------------------------------------------------------- * 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; }