/*-----------------------------------------------------------------------------
 * Function: hdcp_suspend_resume_auto_ri
 *-----------------------------------------------------------------------------
 */
static int hdcp_suspend_resume_auto_ri(enum ri_suspend_resume state)
{
    static u8 OldRiStat, OldRiCommand;
    u8 TimeOut = 10;

    /* Suspend Auto Ri in order to allow FW access MDDC bus.
     * Poll 0x72:0x26[0] for MDDC bus availability or timeout
     */

    DBG("hdcp_suspend_resume_auto_ri() state=%s",
        state == AUTO_RI_SUSPEND ? "SUSPEND" : "RESUME");

    if (state == AUTO_RI_SUSPEND) {
        /* Save original Auto Ri state */
        OldRiCommand = RD_FIELD_32(hdcp.hdmi_wp_base_addr +
                                   HDMI_IP_CORE_SYSTEM,
                                   HDMI_IP_CORE_SYSTEM__RI_CMD, 0, 0);

        /* Disable Auto Ri */
        hdcp_lib_auto_ri_check(false);

        /* Wait for HW to release MDDC bus */
        /* TODO: while loop / timeout to be enhanced */
        while (--TimeOut) {
            if (!RD_FIELD_32(hdcp.hdmi_wp_base_addr +
                             HDMI_IP_CORE_SYSTEM,
                             HDMI_IP_CORE_SYSTEM__RI_STAT, 0, 0))
                break;
        }

        /* MDDC bus not relinquished */
        if (!TimeOut) {
            printk(KERN_ERR "HDCP: Suspending Auto Ri failed !\n");
            return -HDCP_DDC_ERROR;
        }

        OldRiStat = RD_FIELD_32(hdcp.hdmi_wp_base_addr +
                                HDMI_IP_CORE_SYSTEM,
                                HDMI_IP_CORE_SYSTEM__RI_STAT, 0, 0);
    } else {
        /* If Auto Ri was enabled before it was suspended */
        if ((OldRiStat) && (OldRiCommand))
            /* Re-enable Auto Ri */
            hdcp_lib_auto_ri_check(false);
    }

    return HDCP_OK;
}
示例#2
0
/*-----------------------------------------------------------------------------
 * 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;
	}
}
示例#3
0
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;
}
示例#4
0
/*-----------------------------------------------------------------------------
 * 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);
}
示例#5
0
/*-----------------------------------------------------------------------------
 * Function: hdcp_lib_check_repeater_bit_in_tx
 *-----------------------------------------------------------------------------
 */
u8 hdcp_lib_check_repeater_bit_in_tx(void)
{
	return RD_FIELD_32(hdcp.hdmi_wp_base_addr + HDMI_IP_CORE_SYSTEM,
			   HDMI_IP_CORE_SYSTEM__HDCP_CTRL, 4, 4);
}
示例#6
0
/*-----------------------------------------------------------------------------
 * Function: hdcp_lib_initiate_step1
 *-----------------------------------------------------------------------------
 */
static int hdcp_lib_initiate_step1(void)
{
	/* HDCP authentication steps:
	 *   1) Read Bksv - check validity (is HDMI Rx supporting HDCP ?)
	 *   2) Initializes HDCP (CP reset release)
	 *   3) Read Bcaps - is HDMI Rx a repeater ?
	 *   *** First part authentication ***
	 *   4) Read Bksv - check validity (is HDMI Rx supporting HDCP ?)
	 *   5) Generates An
	 *   6) DDC: Writes An, Aksv
	 *   7) DDC: Write Bksv
	 */
	uint8_t an_ksv_data[8], an_bksv_data[8];
	uint8_t rx_type;

	DBG("hdcp_lib_initiate_step1()\n");

	/* DDC: Read BKSV from RX */
	if (hdcp_ddc_read(DDC_BKSV_LEN, DDC_BKSV_ADDR , an_ksv_data))
		return -HDCP_DDC_ERROR;

	if (hdcp.pending_disable)
		return -HDCP_CANCELLED_AUTH;

	DBG("BKSV: %02x %02x %02x %02x %02x", an_ksv_data[0], an_ksv_data[1],
					      an_ksv_data[2], an_ksv_data[3],
					      an_ksv_data[4]);

	if (hdcp_lib_check_ksv(an_ksv_data)) {
		DBG("BKSV error (number of 0 and 1)");
		return -HDCP_AUTH_FAILURE;
	}

	/* TODO: Need to confirm it is required */
#ifndef _9032_AN_STOP_FIX_
	hdcp_lib_toggle_repeater_bit_in_tx();
#endif

	/* Release CP reset bit */
	WR_FIELD_32(hdcp.hdmi_wp_base_addr + HDMI_IP_CORE_SYSTEM,
		    HDMI_IP_CORE_SYSTEM__HDCP_CTRL, 2, 2, 1);

	/* Read BCAPS to determine if HDCP RX is a repeater */
	if (hdcp_ddc_read(DDC_BCAPS_LEN, DDC_BCAPS_ADDR, &rx_type))
		return -HDCP_DDC_ERROR;

	if (hdcp.pending_disable)
		return -HDCP_CANCELLED_AUTH;

	rx_type = FLD_GET(rx_type, DDC_BIT_REPEATER, DDC_BIT_REPEATER);

	/* Set repeater bit in HDCP CTRL */
	if (rx_type == 1) {
		hdcp_lib_set_repeater_bit_in_tx(HDCP_REPEATER);
		DBG("HDCP RX is a repeater");
	} else {
		hdcp_lib_set_repeater_bit_in_tx(HDCP_RECEIVER);
		DBG("HDCP RX is a receiver");
	}

/* Power debug code */
#ifdef POWER_TRANSITION_DBG
	printk(KERN_INFO "\n**************************\n"
			 "AUTHENTICATION: WAIT FOR DSS TRANSITION\n"
			 "*************************\n");
	mdelay(10000);
	printk(KERN_INFO "\n**************************\n"
			 "DONE\n"
			 "*************************\n");
#endif
	/* DDC: Read BKSV from RX */
	if (hdcp_ddc_read(DDC_BKSV_LEN, DDC_BKSV_ADDR , an_bksv_data))
		return -HDCP_DDC_ERROR;

	/* Generate An */
	hdcp_lib_generate_an(an_ksv_data);

	/* Authentication 1st step initiated HERE */

	/* DDC: Write An */
	if (hdcp_ddc_write(DDC_AN_LEN, DDC_AN_ADDR , an_ksv_data))
		return -HDCP_DDC_ERROR;

	if (hdcp.pending_disable)
		return -HDCP_CANCELLED_AUTH;

	/* Read AKSV from IP: (HDCP AKSV register) */
	hdcp_lib_read_aksv(an_ksv_data);

	DBG("AKSV: %02x %02x %02x %02x %02x", an_ksv_data[0], an_ksv_data[1],
					      an_ksv_data[2], an_ksv_data[3],
					      an_ksv_data[4]);

	if (hdcp_lib_check_ksv(an_ksv_data)) {
		printk(KERN_INFO "HDCP: AKSV error (number of 0 and 1)\n");
		return -HDCP_AKSV_ERROR;
	}

	if (hdcp.pending_disable)
		return -HDCP_CANCELLED_AUTH;

	/* DDC: Write AKSV */
	if (hdcp_ddc_write(DDC_AKSV_LEN, DDC_AKSV_ADDR, an_ksv_data))
		return -HDCP_DDC_ERROR;

	if (hdcp.pending_disable)
		return -HDCP_CANCELLED_AUTH;

	/* Write Bksv to IP */
	hdcp_lib_write_bksv(an_bksv_data);

	/* Check IP BKSV error */
	if (RD_FIELD_32(hdcp.hdmi_wp_base_addr + HDMI_IP_CORE_SYSTEM,
			HDMI_IP_CORE_SYSTEM__HDCP_CTRL, 5, 5))
		return -HDCP_AUTH_FAILURE;

	/* Here BSKV should be checked against revokation list */

	return HDCP_OK;
}
/*-----------------------------------------------------------------------------
 * 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;
}