blob: e1073f371d1332e8af3e6f54e72d70c14575b99e [file] [log] [blame]
// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
/*
* Copyright (c) 2019 Amlogic, Inc. All rights reserved.
*/
#include <linux/version.h>
#include <linux/module.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/mm.h>
#include <linux/major.h>
#include <linux/platform_device.h>
#include <linux/mutex.h>
#include <linux/cdev.h>
#include <linux/slab.h>
#include <linux/uaccess.h>
#include <linux/delay.h>
#include <linux/clk.h>
#include <linux/arm-smccc.h>
#include <linux/amlogic/media/vout/hdmi_tx21/hdmi_tx_ddc.h>
#include <linux/amlogic/media/vout/hdmi_tx21/hdmi_info_global.h>
#include <linux/amlogic/media/vout/hdmi_tx21/hdmi_tx_module.h>
#include "common.h"
static void hdcptx1_load_key(void)
{
struct arm_smccc_res res;
// hdcptx14_load_key
arm_smccc_smc(HDCPTX_IOOPR, HDCP14_LOADKEY, 0, 0, 0, 0, 0, 0, &res);
}
bool get_hdcp1_lstore(void)
{
struct arm_smccc_res res;
arm_smccc_smc(HDCPTX_IOOPR, HDCP14_KEY_READY, 0, 0, 0, 0, 0, 0, &res);
return (unsigned int)((res.a0) & 0xffffffff);
}
bool get_hdcp2_lstore(void)
{
struct arm_smccc_res res;
arm_smccc_smc(HDCPTX_IOOPR, HDCP22_KEY_READY, 0, 0, 0, 0, 0, 0, &res);
return (unsigned int)((res.a0) & 0xffffffff);
}
void hdcptx_init_reg(void)
{
hdmitx21_set_bit(HDCP_CTRL_IVCTX, BIT(2), false);
hdmitx21_set_bit(HDCP_CTRL_IVCTX, BIT(2), true);
hdmitx21_wr_reg(CP2TX_TP1_IVCTX, 0x92);
hdmitx21_set_bit(CP2TX_CTRL_2_IVCTX, BIT_CP2TX_CTRL_2_RI_SEQNUMM_AUTO, false);
}
u8 hdcptx1_ds_cap_status_get(void)
{
return hdmitx21_rd_reg(TPI_COPP_DATA1_IVCTX);
}
void hdcptx1_ds_bksv_read(u8 *p_bksv, u8 b)
{
hdmitx21_seq_rd_reg(TPI_WR_BKSV_1_IVCTX, p_bksv, b);
}
u8 hdcptx1_ksv_v_get(void)
{
return hdmitx21_rd_reg(TPI_KSV_V_IVCTX);
}
void hdcptx1_protection_enable(bool en)
{
if (en) {
hdmitx21_set_bit(TPI_COPP_DATA2_IVCTX, BIT_TPI_COPP_DATA2_CANCEL_PROT_EN, false);
hdmitx21_set_bit(TPI_COPP_DATA2_IVCTX, (BIT_TPI_COPP_DATA2_COPP_PROTLEVEL |
BIT_TPI_COPP_DATA2_DOUBLE_RI_CHECK |
BIT_TPI_COPP_DATA2_INTERM_RI_CHECK_EN), true);
} else {
hdmitx21_set_bit(TPI_COPP_DATA2_IVCTX, (BIT_TPI_COPP_DATA2_COPP_PROTLEVEL |
BIT_TPI_COPP_DATA2_DOUBLE_RI_CHECK |
BIT_TPI_COPP_DATA2_INTERM_RI_CHECK_EN |
BIT_TPI_COPP_DATA2_KSV_FORWARD), false);
hdmitx21_set_bit(TPI_COPP_DATA2_IVCTX,
BIT_TPI_COPP_DATA2_CANCEL_PROT_EN, true);
}
}
void hdcptx1_intermed_ri_check_enable(bool en)
{
hdmitx21_set_bit(TPI_COPP_DATA2_IVCTX, BIT_TPI_COPP_DATA2_INTERM_RI_CHECK_EN, en);
}
void hdcptx1_encryption_update(bool en)
{
hdmitx21_set_bit(TPI_COPP_DATA2_IVCTX, BIT_TPI_COPP_DATA2_INTR_ENCRYPTION, !en);
}
void hdcptx1_auth_start(void)
{
hdcptx1_load_key();
hdmitx21_wr_reg(RI_START_IVCTX, 1);
hdmitx21_wr_reg(RI_128_COMP_IVCTX, 0x2);
hdmitx21_wr_reg(TPI_HW_OPT1_IVCTX, 0);
hdmitx21_set_bit(LM_DDC_IVCTX, BIT_LM_DDC_SWTPIEN_B7, true);
hdmitx21_set_bit(TPI_COPP_DATA2_IVCTX, BIT_TPI_COPP_DATA2_CANCEL_PROT_EN, false);
hdmitx21_set_bit(TPI_COPP_DATA2_IVCTX,
(BIT_TPI_COPP_DATA2_TPI_HDCP_PREP_EN |
BIT_TPI_COPP_DATA2_DOUBLE_RI_CHECK |
BIT_TPI_COPP_DATA2_INTERM_RI_CHECK_EN |
BIT_TPI_COPP_DATA2_COPP_PROTLEVEL), true);
hdmitx21_set_bit(LM_DDC_IVCTX, BIT_LM_DDC_SWTPIEN_B7, false);
}
void hdcptx1_auth_stop(void)
{
hdmitx21_set_bit(TPI_COPP_DATA2_IVCTX,
(BIT_TPI_COPP_DATA2_TPI_HDCP_PREP_EN | BIT_TPI_COPP_DATA2_COPP_PROTLEVEL), false);
}
u8 hdcptx1_copp_status_get(void)
{
return hdmitx21_rd_reg(TPI_COPP_DATA1_IVCTX);
}
void hdcptx1_bcaps_get(u8 *p_bcaps_status)
{
*p_bcaps_status = hdmitx21_rd_reg(TPI_DS_BCAPS_IVCTX);
}
bool hdcptx1_ds_rptr_capability(void)
{
if (hdmitx21_rd_reg(TPI_COPP_DATA1_IVCTX) & BIT_TPI_COPP_DATA1_HDCP_REP)
return true;
else
return false;
}
void hdcptx1_bstatus_get(u8 *p_ds_bstatus)
{
hdmitx21_seq_rd_reg(TPI_BSTATUS1_IVCTX, p_ds_bstatus, 2);
}
void hdcptx1_get_ds_ksvlists(u8 **p_ksv, u8 count)
{
u16 bytes_to_read = 0;
u16 fifo_byte_counter = 0;
u8 time_out = 100; /* timeout for reading ds ksv list */
u8 fifo_status = 0;
int temp = 0;
/* Clear hash done interrupt */
hdmitx21_set_bit(INTR2_SW_TPI_IVCTX, BIT_INTR2_SW_HASH_DONE_B6, true);
fifo_byte_counter = count * KSV_SIZE;
while ((fifo_byte_counter != 0) && time_out--) {
if (bytes_to_read != 0) {
/* get ds bksv list from fifo */
hdmitx21_seq_rd_reg(TPI_KSV_FIFO_FORW_IVCTX, *p_ksv, bytes_to_read);
temp = temp + bytes_to_read;
if ((temp % 64) == 0) {
temp = 0;
/* wait for hash done interrupt */
while ((hdmitx21_rd_reg(INTR2_SW_TPI_IVCTX) & BIT(6)) == 0)
;
/* clear hash done interrupt */
hdmitx21_set_bit(INTR2_SW_TPI_IVCTX,
BIT_INTR2_SW_HASH_DONE_B6, true);
}
*p_ksv += bytes_to_read;
fifo_byte_counter -= bytes_to_read;
}
fifo_status = hdmitx21_rd_reg(TPI_KSV_FIFO_STAT_IVCTX);
bytes_to_read = fifo_status & BIT_TPI_KSV_FIFO_STAT_BYTES;
mdelay(1);
}
}
bool hdcptx2_ds_rptr_capability(void)
{
if (hdmitx21_rd_reg(CP2TX_GEN_STATUS_IVCTX) & BIT_CP2TX_GEN_STATUS_RO_REPEATER)
return true;
else
return false;
}
void hdcptx2_encryption_update(bool en)
{
hdmitx21_set_bit(HDCP2X_CTL_0_IVCTX, BIT_HDCP2X_CTL_0_ENCRYPT_EN, en);
}
void hdcptx2_csm_send(struct hdcp_csm_t *csm_msg)
{
hdmitx21_wr_reg(CP2TX_RPT_SMNG_K_IVCTX, 1);
hdmitx21_wr_reg(CP2TX_SEQ_NUM_M_0_IVCTX, (u8)(csm_msg->seq_num_m & 0xFF));
hdmitx21_wr_reg(CP2TX_SEQ_NUM_M_1_IVCTX, (u8)((csm_msg->seq_num_m >> 8) & 0xff));
hdmitx21_wr_reg(CP2TX_SEQ_NUM_M_2_IVCTX, (u8)((csm_msg->seq_num_m >> 16) & 0xff));
hdmitx21_wr_reg(CP2TX_TX_CTRL_0_IVCTX, BIT_CP2TX_TX_CTRL_0_RI_RPT_SMNG_WR_START);
hdmitx21_wr_reg(CP2TX_TX_CTRL_0_IVCTX, 0x00);
hdmitx21_wr_reg(CP2TX_TX_RPT_SMNG_IN_IVCTX, (u8)(csm_msg->streamid_type >> 8));
hdmitx21_wr_reg(CP2TX_TX_CTRL_0_IVCTX, BIT_CP2TX_TX_CTRL_0_RI_RPT_SMNG_WR);
hdmitx21_wr_reg(CP2TX_TX_CTRL_0_IVCTX, 0);
hdmitx21_wr_reg(CP2TX_TX_RPT_SMNG_IN_IVCTX, (u8)(csm_msg->streamid_type));
hdmitx21_wr_reg(CP2TX_TX_CTRL_0_IVCTX, BIT_CP2TX_TX_CTRL_0_RI_RPT_SMNG_WR);
hdmitx21_wr_reg(CP2TX_TX_CTRL_0_IVCTX, 0);
hdmitx21_wr_reg(CP2TX_TX_CTRL_0_IVCTX, BIT_CP2TX_TX_CTRL_0_RI_RPT_SMNG_XFER_START);
hdmitx21_wr_reg(CP2TX_TX_CTRL_0_IVCTX, 0);
}
bool hdcptx2_auth_status(void)
{
u8 hdcp2_auth_status = hdmitx21_rd_reg(CP2TX_AUTH_STAT_IVCTX);
u8 hdcp2_auth_state_status = hdmitx21_rd_reg(CP2TX_STATE_IVCTX);
if (hdcp2_auth_status == 0x81 && hdcp2_auth_state_status == 0x2B)
return true;
else
return false;
}
void hdcptx2_auth_stop(void)
{
u8 ddc_status;
u8 count = 0;
hdmitx21_wr_reg(HDCP2X_POLL_CS_IVCTX, 0x71);
hdmitx21_set_bit(HDCP2X_CTL_1_IVCTX, BIT_HDCP2X_CTL_1_HPD_SW, false);
hdmitx21_set_bit(HDCP2X_CTL_1_IVCTX, BIT_HDCP2X_CTL_1_HPD_OVR, true);
hdmitx21_set_bit(HDCP2X_CTL_1_IVCTX, BIT_HDCP2X_CTL_1_REAUTH_SW, true);
hdmitx21_set_bit(HDCP2X_CTL_1_IVCTX, BIT_HDCP2X_CTL_1_REAUTH_SW, false);
do {
ddc_status = hdmitx21_rd_reg(HDCP2X_DDC_STS_IVCTX);
if ((ddc_status & BIT_REGTX0_HDCP2X_DDC_STS_CTL_CS_B3_B0) == false)
break;
count++;
if ((ddc_status & BIT_REGTX0_HDCP2X_DDC_STS_CTL_CS_B3_B0) == 0x0E) {
hdmitx21_set_bit(HDCP2X_CTL_0_IVCTX, BIT_HDCP2X_CTL_0_EN, false);
hdmitx21_set_bit(TPI_DDC_MASTER_EN_IVCTX,
BIT_TPI_DDC_MASTER_EN_HW_EN, true);
hdmitx21_wr_reg(DDC_CMD_IVCTX, BIT_DDC_CMD_DDC_CMD);
usleep_range(2000, 3000);
hdmitx21_set_bit(TPI_DDC_MASTER_EN_IVCTX,
BIT_TPI_DDC_MASTER_EN_HW_EN, false);
hdmitx21_set_bit(HDCP2X_CTL_0_IVCTX, BIT_HDCP2X_CTL_0_EN, true);
}
usleep_range(1000, 2000);
} while (count < 5);
hdmitx21_set_bit(HDCP2X_CTL_0_IVCTX, BIT_HDCP2X_CTL_0_EN, false);
}
void hdcptx2_reauth_send(void)
{
hdmitx21_wr_reg(HDCP2X_POLL_CS_IVCTX, 0x70); /* Enable Polling */
hdmitx21_set_bit(HDCP2X_CTL_1_IVCTX, BIT_HDCP2X_CTL_1_HPD_SW, true);
hdmitx21_set_bit(HDCP2X_CTL_1_IVCTX, BIT_HDCP2X_CTL_1_REAUTH_SW, true);
hdmitx21_set_bit(HDCP2X_CTL_1_IVCTX, BIT_HDCP2X_CTL_1_REAUTH_SW, false);
hdmitx21_wr_reg(CP2TX_CTRL_1_IVCTX, 0x21);
hdmitx21_wr_reg(CP2TX_CTRL_1_IVCTX, 0x20);
}
u8 hdcptx2_topology_get(void)
{
return hdmitx21_rd_reg(CP2TX_RPT_DETAIL_IVCTX);
}
u8 hdcptx2_rpt_dev_cnt_get(void)
{
return hdmitx21_rd_reg(CP2TX_RPT_DEVCNT_IVCTX);
}
u8 hdcptx2_rpt_depth_get(void)
{
return hdmitx21_rd_reg(CP2TX_RPT_DEPTH_IVCTX);
}
void hdcptx2_ds_rpt_rcvid_list_read(u8 *p_rpt_rcv_id, u8 dev_count, u8 bytes_to_read)
{
u8 i, j;
hdmitx21_set_bit(CP2TX_TX_CTRL_0_IVCTX, BIT_CP2TX_TX_CTRL_0_RI_RPT_RCVID_RD_START, true);
hdmitx21_set_bit(CP2TX_TX_CTRL_0_IVCTX, BIT_CP2TX_TX_CTRL_0_RI_RPT_RCVID_RD_START, false);
for (j = 0; j < dev_count; j++) {
for (i = 0; i < bytes_to_read; i++) {
*p_rpt_rcv_id++ = hdmitx21_rd_reg(CP2TX_RPT_RCVID_OUT_IVCTX);
hdmitx21_set_bit(CP2TX_TX_CTRL_0_IVCTX,
BIT_CP2TX_TX_CTRL_0_RI_RPT_RCVID_RD, false);
hdmitx21_set_bit(CP2TX_TX_CTRL_0_IVCTX,
BIT_CP2TX_TX_CTRL_0_RI_RPT_RCVID_RD, true);
}
}
}
void hdcptx2_ds_rcv_id_read(u8 *p_rcv_id)
{
p_rcv_id[4] = hdmitx21_rd_reg(CP2TX_RX_ID_CORE_0_IVCTX);
p_rcv_id[3] = hdmitx21_rd_reg(CP2TX_RX_ID_CORE_1_IVCTX);
p_rcv_id[2] = hdmitx21_rd_reg(CP2TX_RX_ID_CORE_2_IVCTX);
p_rcv_id[1] = hdmitx21_rd_reg(CP2TX_RX_ID_CORE_3_IVCTX);
p_rcv_id[0] = hdmitx21_rd_reg(CP2TX_RX_ID_CORE_4_IVCTX);
}
void hdcptx2_src_auth_start(u8 content_type)
{
if (content_type != 0 && content_type != 1)
content_type = 0;
hdmitx21_set_bit(HDCP2X_CTL_1_IVCTX, BIT_HDCP2X_CTL_1_HPD_SW, true);
hdmitx21_set_bit(HDCP2X_CTL_1_IVCTX, BIT_HDCP2X_CTL_1_HPD_OVR, true);
hdmitx21_wr_reg(CP2TX_GP_IN2_IVCTX,
BIT_CP2TX_TIMEOUT_DISABLE_B2 | BIT_CP2TX_IGNORE_RXSTATUS_B0);
hdmitx21_set_bit(HDCP2X_CTL_0_IVCTX, BIT_HDCP2X_CTL_0_EN, true);
}
void hdcptx2_smng_auto(bool en)
{
hdmitx21_set_bit(CP2TX_CTRL_0_IVCTX, BIT_CP2TX_CTRL_0_RI_SMNG_AUTO, en);
}
void hdcptx1_query_aksv(struct hdcp_ksv_t *p_val)
{
hdmitx21_seq_rd_reg(AKSV_1_IVCTX, p_val->b, KSV_SIZE);
}