aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/video/omap2/hdcp/hdcp_lib.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/video/omap2/hdcp/hdcp_lib.c')
-rw-r--r--drivers/video/omap2/hdcp/hdcp_lib.c838
1 files changed, 838 insertions, 0 deletions
diff --git a/drivers/video/omap2/hdcp/hdcp_lib.c b/drivers/video/omap2/hdcp/hdcp_lib.c
new file mode 100644
index 0000000..6e7850c
--- /dev/null
+++ b/drivers/video/omap2/hdcp/hdcp_lib.c
@@ -0,0 +1,838 @@
+/*
+ * hdcp_lib.c
+ *
+ * HDCP interface DSS driver setting for TI's OMAP4 family of processor.
+ * Copyright (C) 2010-2011 Texas Instruments Incorporated - http://www.ti.com/
+ * Authors: Fabrice Olivero
+ * Fabrice Olivero <f-olivero@ti.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/delay.h>
+#include <mach/omap4-common.h>
+#include <linux/dma-mapping.h>
+#include "hdcp.h"
+
+static void hdcp_lib_read_an(u8 *an);
+static void hdcp_lib_read_aksv(u8 *ksv_data);
+static void hdcp_lib_write_bksv(u8 *ksv_data);
+static void hdcp_lib_generate_an(u8 *an);
+static int hdcp_lib_r0_check(void);
+static int hdcp_lib_sha_bstatus(struct hdcp_sha_in *sha);
+static void hdcp_lib_set_repeater_bit_in_tx(enum hdcp_repeater rx_mode);
+static void hdcp_lib_toggle_repeater_bit_in_tx(void);
+static int hdcp_lib_initiate_step1(void);
+static int hdcp_lib_check_ksv(uint8_t ksv[5]);
+
+#define PPA_SERVICE_HDCP_READ_M0 0x30
+#define PPA_SERVICE_HDCP_CHECK_V 0x31
+/*-----------------------------------------------------------------------------
+ * Function: hdcp_lib_read_an
+ *-----------------------------------------------------------------------------
+ */
+static void hdcp_lib_read_an(u8 *an)
+{
+ u8 i;
+
+ for (i = 0; i < 8; i++) {
+ an[i] = (RD_REG_32(hdcp.hdmi_wp_base_addr +
+ HDMI_IP_CORE_SYSTEM,
+ HDMI_IP_CORE_SYSTEM__AN0 +
+ i * sizeof(uint32_t))) & 0xFF;
+ }
+}
+
+/*-----------------------------------------------------------------------------
+ * Function: hdcp_lib_read_aksv
+ *-----------------------------------------------------------------------------
+ */
+static void hdcp_lib_read_aksv(u8 *ksv_data)
+{
+ u8 i;
+ for (i = 0; i < 5; i++) {
+ ksv_data[i] = RD_REG_32(hdcp.hdmi_wp_base_addr +
+ HDMI_IP_CORE_SYSTEM,
+ HDMI_IP_CORE_SYSTEM__AKSV0 +
+ i * sizeof(uint32_t));
+
+ }
+}
+
+/*-----------------------------------------------------------------------------
+ * 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_lib_generate_an
+ *-----------------------------------------------------------------------------
+ */
+static void hdcp_lib_generate_an(u8 *an)
+{
+ /* Generate An using HDCP HW */
+ DBG("hdcp_lib_generate_an()");
+
+ /* Start AN Gen */
+ WR_FIELD_32(hdcp.hdmi_wp_base_addr + HDMI_IP_CORE_SYSTEM,
+ HDMI_IP_CORE_SYSTEM__HDCP_CTRL, 3, 3, 0);
+
+ /* Delay of 10 ms */
+ mdelay(10);
+
+ /* Stop AN Gen */
+ WR_FIELD_32(hdcp.hdmi_wp_base_addr + HDMI_IP_CORE_SYSTEM,
+ HDMI_IP_CORE_SYSTEM__HDCP_CTRL, 3, 3, 1);
+
+ /* Must set 0x72:0x0F[3] twice to guarantee that takes effect */
+ WR_FIELD_32(hdcp.hdmi_wp_base_addr + HDMI_IP_CORE_SYSTEM,
+ HDMI_IP_CORE_SYSTEM__HDCP_CTRL, 3, 3, 1);
+
+ hdcp_lib_read_an(an);
+
+ DBG("AN: %x %x %x %x %x %x %x %x", an[0], an[1], an[2], an[3],
+ an[4], an[5], an[6], an[7]);
+}
+
+/*-----------------------------------------------------------------------------
+ * Function: hdcp_lib_r0_check
+ *-----------------------------------------------------------------------------
+ */
+static int hdcp_lib_r0_check(void)
+{
+ u8 ro_rx[2], ro_tx[2];
+
+ DBG("hdcp_lib_r0_check()");
+
+ /* DDC: Read Ri' from RX */
+ if (hdcp_ddc_read(DDC_Ri_LEN, DDC_Ri_ADDR , (u8 *)&ro_rx))
+ return -HDCP_DDC_ERROR;
+
+ /* Read Ri in HDCP IP */
+ ro_tx[0] = RD_REG_32(hdcp.hdmi_wp_base_addr + HDMI_IP_CORE_SYSTEM,
+ HDMI_IP_CORE_SYSTEM__R1) & 0xFF;
+
+ ro_tx[1] = RD_REG_32(hdcp.hdmi_wp_base_addr + HDMI_IP_CORE_SYSTEM,
+ HDMI_IP_CORE_SYSTEM__R2) & 0xFF;
+
+ /* Compare values */
+ DBG("ROTX: %x%x RORX:%x%x", ro_tx[0], ro_tx[1], ro_rx[0], ro_rx[1]);
+
+ if ((ro_rx[0] == ro_tx[0]) && (ro_rx[1] == ro_tx[1]))
+ return HDCP_OK;
+ else
+ return -HDCP_AUTH_FAILURE;
+}
+
+/*-----------------------------------------------------------------------------
+ * Function: hdcp_lib_sha_bstatus
+ *-----------------------------------------------------------------------------
+ */
+static int hdcp_lib_sha_bstatus(struct hdcp_sha_in *sha)
+{
+ u8 data[2];
+
+ if (hdcp_ddc_read(DDC_BSTATUS_LEN, DDC_BSTATUS_ADDR, data))
+ return -HDCP_DDC_ERROR;
+
+ sha->data[sha->byte_counter++] = data[0];
+ sha->data[sha->byte_counter++] = data[1];
+
+ return HDCP_OK;
+}
+
+/*-----------------------------------------------------------------------------
+ * Function: hdcp_lib_set_repeater_bit_in_tx
+ *-----------------------------------------------------------------------------
+ */
+static void hdcp_lib_set_repeater_bit_in_tx(enum hdcp_repeater rx_mode)
+{
+ DBG("hdcp_lib_set_repeater_bit_in_tx() value=%d", rx_mode);
+
+ WR_FIELD_32(hdcp.hdmi_wp_base_addr + HDMI_IP_CORE_SYSTEM,
+ HDMI_IP_CORE_SYSTEM__HDCP_CTRL, 4, 4, rx_mode);
+}
+
+/*-----------------------------------------------------------------------------
+ * Function: hdcp_lib_toggle_repeater_bit_in_tx
+ *-----------------------------------------------------------------------------
+ */
+static void hdcp_lib_toggle_repeater_bit_in_tx(void)
+{
+ if (hdcp_lib_check_repeater_bit_in_tx())
+ hdcp_lib_set_repeater_bit_in_tx(HDCP_RECEIVER);
+ else
+ hdcp_lib_set_repeater_bit_in_tx(HDCP_REPEATER);
+}
+
+/*-----------------------------------------------------------------------------
+ * 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_lib_check_ksv
+ *-----------------------------------------------------------------------------
+ */
+static int hdcp_lib_check_ksv(uint8_t ksv[5])
+{
+ int i, j;
+ int zero = 0, one = 0;
+
+ for (i = 0; i < 5; i++) {
+ /* Count number of zero / one */
+ for (j = 0; j < 8; j++) {
+ if (ksv[i] & (0x01 << j))
+ one++;
+ else
+ zero++;
+ }
+ }
+
+ if (one == zero)
+ return 0;
+ else
+ return -1;
+}
+
+/*-----------------------------------------------------------------------------
+ * Function: hdcp_3des_load_key
+ *-----------------------------------------------------------------------------
+ */
+int hdcp_3des_load_key(uint32_t *deshdcp_encrypted_key)
+{
+ int counter = 0, status = HDCP_OK;
+
+ DBG("Loading HDCP keys...");
+
+ /* 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;
+ printk(KERN_ERR "HDCP: DESHDCP dummy read error\n");
+ }
+ if (RD_REG_32(hdcp.deshdcp_base_addr, DESHDCP__DHDCP_DATA_H) !=
+ 0x0) {
+ status = -HDCP_3DES_ERROR;
+ printk(KERN_ERR "HDCP: DESHDCP dummy read error\n");
+ }
+
+ counter += 2;
+ }
+
+ return status;
+}
+
+/*-----------------------------------------------------------------------------
+ * 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;
+ }
+}
+
+/*-----------------------------------------------------------------------------
+ * Function: hdcp_lib_disable
+ *-----------------------------------------------------------------------------
+ */
+int hdcp_lib_disable()
+{
+ DBG("hdcp_lib_disable() %u", jiffies_to_msecs(jiffies));
+
+ /* CP reset */
+ WR_FIELD_32(hdcp.hdmi_wp_base_addr + HDMI_IP_CORE_SYSTEM,
+ HDMI_IP_CORE_SYSTEM__HDCP_CTRL, 2, 2, 0);
+
+ /* Clear AV mute in case it was set */
+ hdcp_lib_set_av_mute(AV_MUTE_CLEAR);
+
+ return HDCP_OK;
+}
+
+/*-----------------------------------------------------------------------------
+ * Function: hdcp_lib_set_encryption
+ *-----------------------------------------------------------------------------
+ */
+void hdcp_lib_set_encryption(enum encryption_state enc_state)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&hdcp.spinlock, flags);
+
+ /* HDCP_CTRL::ENC_EN set/clear */
+ WR_FIELD_32(hdcp.hdmi_wp_base_addr + HDMI_IP_CORE_SYSTEM,
+ HDMI_IP_CORE_SYSTEM__HDCP_CTRL, 0, 0, enc_state);
+
+ /* Read to flush */
+ RD_REG_32(hdcp.hdmi_wp_base_addr + HDMI_IP_CORE_SYSTEM,
+ HDMI_IP_CORE_SYSTEM__HDCP_CTRL);
+
+ spin_unlock_irqrestore(&hdcp.spinlock, flags);
+
+ pr_info("HDCP: Encryption state changed: %s hdcp_ctrl: %02x",
+ enc_state == HDCP_ENC_OFF ? "OFF" : "ON",
+ RD_REG_32(hdcp.hdmi_wp_base_addr +
+ HDMI_IP_CORE_SYSTEM,
+ HDMI_IP_CORE_SYSTEM__HDCP_CTRL));
+
+}
+
+/*-----------------------------------------------------------------------------
+ * 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_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);
+}
+
+/*-----------------------------------------------------------------------------
+ * 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_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_step1_start
+ *-----------------------------------------------------------------------------
+ */
+int hdcp_lib_step1_start(void)
+{
+ u8 hdmi_mode;
+ int status;
+
+ DBG("hdcp_lib_step1_start() %u", jiffies_to_msecs(jiffies));
+
+ /* Check if mode is HDMI or DVI */
+ hdmi_mode = RD_REG_32(hdcp.hdmi_wp_base_addr + HDMI_CORE_AV_BASE,
+ HDMI_CORE_AV_HDMI_CTRL) &
+ HDMI_CORE_AV_HDMI_CTRL__HDMI_MODE;
+
+ DBG("RX mode: %s", hdmi_mode ? "HDMI" : "DVI");
+
+ /* Set AV Mute */
+ hdcp_lib_set_av_mute(AV_MUTE_SET);
+
+ /* Must turn encryption off when AVMUTE */
+ hdcp_lib_set_encryption(HDCP_ENC_OFF);
+
+ status = hdcp_lib_initiate_step1();
+
+ if (hdcp.pending_disable)
+ return -HDCP_CANCELLED_AUTH;
+ else
+ return status;
+}
+
+/*-----------------------------------------------------------------------------
+ * Function: hdcp_lib_step1_r0_check
+ *-----------------------------------------------------------------------------
+ */
+int hdcp_lib_step1_r0_check(void)
+{
+ int status = HDCP_OK;
+
+ /* HDCP authentication steps:
+ * 1) DDC: Read M0'
+ * 2) Compare M0 and M0'
+ * if Rx is a receiver: switch to authentication step 3
+ * 3) Enable encryption / auto Ri check / disable AV mute
+ * if Rx is a repeater: switch to authentication step 2
+ * 3) Get M0 from HDMI IP and store it for further processing (V)
+ * 4) Enable encryption / auto Ri check / auto BCAPS RDY polling
+ * Disable AV mute
+ */
+
+ DBG("hdcp_lib_step1_r0_check() %u", jiffies_to_msecs(jiffies));
+
+ status = hdcp_lib_r0_check();
+ if (status < 0)
+ return status;
+
+ /* Authentication 1st step done */
+
+ /* Now prepare 2nd step authentication in case of RX repeater and
+ * enable encryption / Ri check
+ */
+
+ if (hdcp.pending_disable)
+ return -HDCP_CANCELLED_AUTH;
+
+ if (hdcp_lib_check_repeater_bit_in_tx()) {
+ status = omap4_secure_dispatcher(PPA_SERVICE_HDCP_READ_M0,
+ FLAG_START_CRITICAL,
+ 0, 0, 0, 0, 0);
+ /* Wait for user space */
+ if (status) {
+ printk(KERN_ERR "HDCP: omap4_secure_dispatcher M0 error "
+ "%d\n", status);
+ return -HDCP_AUTH_FAILURE;
+ }
+
+ DBG("hdcp_lib_set_encryption() %u", jiffies_to_msecs(jiffies));
+
+ /* Enable encryption */
+ hdcp_lib_set_encryption(HDCP_ENC_ON);
+
+#ifdef _9032_AUTO_RI_
+ /* Enable Auto Ri */
+ hdcp_lib_auto_ri_check(true);
+#endif
+
+#ifdef _9032_BCAP_
+ /* Enable automatic BCAPS polling */
+ hdcp_lib_auto_bcaps_rdy_check(true);
+#endif
+
+ /* Now, IP waiting for BCAPS ready bit */
+ } else {
+ /* Receiver: enable encryption and auto Ri check */
+ hdcp_lib_set_encryption(HDCP_ENC_ON);
+
+#ifdef _9032_AUTO_RI_
+ /* Enable Auto Ri */
+ hdcp_lib_auto_ri_check(true);
+#endif
+
+ }
+
+ /* Clear AV mute */
+ hdcp_lib_set_av_mute(AV_MUTE_CLEAR);
+
+ return HDCP_OK;
+}
+
+/*-----------------------------------------------------------------------------
+ * Function: hdcp_lib_step2
+ *-----------------------------------------------------------------------------
+ */
+int hdcp_lib_step2(void)
+{
+ /* HDCP authentication steps:
+ * 1) Disable auto Ri check
+ * 2) DDC: read BStatus (nb of devices, MAX_DEV
+ */
+
+ u8 bstatus[2];
+ int status = HDCP_OK;
+
+ DBG("hdcp_lib_step2() %u", jiffies_to_msecs(jiffies));
+
+#ifdef _9032_AUTO_RI_
+ /* Disable Auto Ri */
+ hdcp_lib_auto_ri_check(false);
+#endif
+
+ /* DDC: Read Bstatus (1st byte) from Rx */
+ if (hdcp_ddc_read(DDC_BSTATUS_LEN, DDC_BSTATUS_ADDR, bstatus))
+ return -HDCP_DDC_ERROR;
+
+ /* Get KSV list size */
+ DBG("KSV list size: %d", bstatus[0] & DDC_BSTATUS0_DEV_COUNT);
+ sha_input.byte_counter = (bstatus[0] & DDC_BSTATUS0_DEV_COUNT) * 5;
+
+ /* Check BStatus topology errors */
+ if (bstatus[0] & DDC_BSTATUS0_MAX_DEVS) {
+ DBG("MAX_DEV_EXCEEDED set");
+ return -HDCP_AUTH_FAILURE;
+ }
+
+ if (bstatus[1] & DDC_BSTATUS1_MAX_CASC) {
+ DBG("MAX_CASCADE_EXCEEDED set");
+ return -HDCP_AUTH_FAILURE;
+ }
+
+ DBG("Retrieving KSV list...");
+
+ /* Clear all SHA input data */
+ /* TODO: should be done earlier at HDCP init */
+ memset(sha_input.data, 0, MAX_SHA_DATA_SIZE);
+
+ if (hdcp.pending_disable)
+ return -HDCP_CANCELLED_AUTH;
+
+ /* DDC: read KSV list */
+ if (sha_input.byte_counter) {
+ if (hdcp_ddc_read(sha_input.byte_counter, DDC_KSV_FIFO_ADDR,
+ (u8 *)&sha_input.data))
+ return -HDCP_DDC_ERROR;
+ }
+
+ /* Read and add Bstatus */
+ if (hdcp_lib_sha_bstatus(&sha_input))
+ return -HDCP_DDC_ERROR;
+
+ if (hdcp.pending_disable)
+ return -HDCP_CANCELLED_AUTH;
+
+ /* Read V' */
+ if (hdcp_ddc_read(DDC_V_LEN, DDC_V_ADDR, sha_input.vprime))
+ return -HDCP_DDC_ERROR;
+
+ if (hdcp.pending_disable)
+ return -HDCP_CANCELLED_AUTH;
+
+ /* clear sha_input values in cache*/
+ dma_sync_single_for_device(NULL,
+ __pa((u32)(&sha_input)),
+ sizeof(struct hdcp_sha_in),
+ DMA_TO_DEVICE);
+
+ status = omap4_secure_dispatcher(PPA_SERVICE_HDCP_CHECK_V,
+ FLAG_START_CRITICAL,
+ 1, __pa((u32)&sha_input), 0, 0, 0);
+ /* Wait for user space */
+ if (status) {
+ printk(KERN_ERR "HDCP: omap4_secure_dispatcher CHECH_V error "
+ "%d\n", status);
+ return -HDCP_AUTH_FAILURE;
+ }
+
+ if (status == HDCP_OK) {
+ /* Re-enable Ri check */
+#ifdef _9032_AUTO_RI_
+ hdcp_lib_auto_ri_check(true);
+#endif
+ }
+
+ return status;
+}