| /* |
| * 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" |
| #include "../../hdmi_ti_4xxx_ip_ddc.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 (ddc_read(DDC_Ri_LEN, DDC_Ri_ADDR , (u8 *)&ro_rx)) |
| return -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 (ddc_read(DDC_BSTATUS_LEN, DDC_BSTATUS_ADDR, data)) |
| return -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 (ddc_read(DDC_BKSV_LEN, DDC_BKSV_ADDR , an_ksv_data)) |
| return -DDC_ERROR; |
| |
| if (ddc.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 (ddc_read(DDC_BCAPS_LEN, DDC_BCAPS_ADDR, &rx_type)) |
| return -DDC_ERROR; |
| |
| if (ddc.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 (ddc_read(DDC_BKSV_LEN, DDC_BKSV_ADDR , an_bksv_data)) |
| return -DDC_ERROR; |
| |
| /* Generate An */ |
| hdcp_lib_generate_an(an_ksv_data); |
| |
| /* Authentication 1st step initiated HERE */ |
| |
| /* DDC: Write An */ |
| if (ddc_write(DDC_AN_LEN, DDC_AN_ADDR , an_ksv_data)) |
| return -DDC_ERROR; |
| |
| if (ddc.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"); |
| printk(KERN_INFO "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]); |
| return -HDCP_AKSV_ERROR; |
| } |
| |
| if (ddc.pending_disable) |
| return -HDCP_CANCELLED_AUTH; |
| |
| /* DDC: Write AKSV */ |
| if (ddc_write(DDC_AKSV_LEN, DDC_AKSV_ADDR, an_ksv_data)) |
| return -DDC_ERROR; |
| |
| if (ddc.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); |
| |
| if (hdcp.print_messages) |
| 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 if needed */ |
| if (hdcp.av_mute_needed) |
| 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 (ddc.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 (ddc.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 (ddc_read(DDC_BSTATUS_LEN, DDC_BSTATUS_ADDR, bstatus)) |
| return -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 (ddc.pending_disable) |
| return -HDCP_CANCELLED_AUTH; |
| |
| /* DDC: read KSV list */ |
| if (sha_input.byte_counter) { |
| if (ddc_read(sha_input.byte_counter, DDC_KSV_FIFO_ADDR, |
| (u8 *)&sha_input.data)) |
| return -DDC_ERROR; |
| } |
| |
| /* Read and add Bstatus */ |
| if (hdcp_lib_sha_bstatus(&sha_input)) |
| return -DDC_ERROR; |
| |
| if (ddc.pending_disable) |
| return -HDCP_CANCELLED_AUTH; |
| |
| /* Read V' */ |
| if (ddc_read(DDC_V_LEN, DDC_V_ADDR, sha_input.vprime)) |
| return -DDC_ERROR; |
| |
| if (ddc.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; |
| } |