Example #1
0
/*-----------------------------------------------------------------------------
 * 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;
}