blob: 4161d183a72f78e9d3cb836c2a4542df28c4f15a [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/kthread.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/of_gpio.h>
#include <linux/amlogic/media/frame_provider/tvin/tvin.h>
/* Local include */
#include "hdmi_rx_repeater.h"
#include "hdmi_rx_drv.h"
#include "hdmi_rx_hw.h"
#include "hdmi_rx_wrapper.h"
#include "hdmi_rx_edid.h"
/*edid original data from device*/
static unsigned char receive_edid[MAX_RECEIVE_EDID];
int receive_edid_len = MAX_RECEIVE_EDID;
MODULE_PARM_DESC(receive_edid, "\n receive_edid\n");
module_param_array(receive_edid, byte, &receive_edid_len, 0664);
int tx_hpd_event;
int edid_len;
MODULE_PARM_DESC(edid_len, "\n edid_len\n");
module_param(edid_len, int, 0664);
bool new_edid;
/*original bksv from device*/
unsigned char receive_hdcp[MAX_KSV_LIST_SIZE];
int hdcp_array_len = MAX_KSV_LIST_SIZE;
MODULE_PARM_DESC(receive_hdcp, "\n receive_hdcp\n");
module_param_array(receive_hdcp, byte, &hdcp_array_len, 0664);
int hdcp_len;
int hdcp_repeat_depth;
bool new_hdcp;
bool start_auth_14;
MODULE_PARM_DESC(start_auth_14, "\n start_auth_14\n");
module_param(start_auth_14, bool, 0664);
bool repeat_plug;
MODULE_PARM_DESC(repeat_plug, "\n repeat_plug\n");
module_param(repeat_plug, bool, 0664);
int up_phy_addr;/*d c b a 4bit*/
MODULE_PARM_DESC(up_phy_addr, "\n up_phy_addr\n");
module_param(up_phy_addr, int, 0664);
int hdcp22_firm_switch_timeout;
unsigned char *rx_get_dw_hdcp_addr(void)
{
return receive_hdcp;
}
void rx_start_repeater_auth(void)
{
rx.hdcp.state = REPEATER_STATE_START;
start_auth_14 = 1;
rx.hdcp.delay = 0;
hdcp_len = 0;
hdcp_repeat_depth = 0;
rx.hdcp.dev_exceed = 0;
rx.hdcp.cascade_exceed = 0;
rx.hdcp.depth = 0;
rx.hdcp.count = 0;
memset(&receive_hdcp, 0, sizeof(receive_hdcp));
}
void rx_check_repeat(void)
{
struct hdcp14_topo_s *topo_data = (struct hdcp14_topo_s *)receive_hdcp;
if (!hdmirx_repeat_support())
return;
if (rx.hdcp.repeat != repeat_plug) {
/*pull down hpd if downstream plug low*/
/* rx_set_cur_hpd(0, 3); */
rx_send_hpd_pulse();
rx_pr("firm_change:%d,repeat_plug:%d,repeat:%d\n",
rx.firm_change, repeat_plug, rx.hdcp.repeat);
rx_set_repeat_signal(repeat_plug);
if (!repeat_plug) {
edid_len = 0;
rx.hdcp.dev_exceed = 0;
rx.hdcp.cascade_exceed = 0;
memset(&receive_hdcp, 0, sizeof(receive_hdcp));
memset(&receive_edid, 0, sizeof(receive_edid));
up_phy_addr = 0;
/*new_edid = true;*/
/* rx_set_cur_hpd(1, 3); */
/*rx.firm_change = 0;*/
rx_pr("1firm_change:%d,repeat_plug:%d,repeat:%d\n",
rx.firm_change, repeat_plug, rx.hdcp.repeat);
}
}
/*this is addition for the downstream edid too late*/
if (new_edid && rx.hdcp.repeat && !rx.firm_change) {
/*check downstream plug when new plug occur*/
/*check receive change*/
/*it's contained in hwconfig*/
hdmi_rx_top_edid_update();
hdcp22_firm_switch_timeout = 0;
new_edid = false;
rx_send_hpd_pulse();
}
if (repeat_plug) {
switch (rx.hdcp.state) {
case REPEATER_STATE_START:
rx_pr("[RX] receive aksv\n");
hdmirx_wr_bits_dwc(DWC_HDCP_RPT_CTRL,
KSVLIST_TIMEOUT, 0);
hdmirx_wr_bits_dwc(DWC_HDCP_RPT_CTRL,
KSVLIST_LOSTAUTH, 0);
hdmirx_wr_bits_dwc(DWC_HDCP_RPT_CTRL,
KSVLIST_READY, 0);
hdmirx_wr_bits_dwc(DWC_HDCP_RPT_BSTATUS,
MAX_CASCADE_EXCEEDED, 0);
hdmirx_wr_bits_dwc(DWC_HDCP_RPT_BSTATUS,
MAX_DEVS_EXCEEDED, 0);
rx.hdcp.state = REPEATER_STATE_WAIT_KSV;
break;
case REPEATER_STATE_WAIT_KSV:
if (!rx.cur_5v_sts) {
rx.hdcp.state = REPEATER_STATE_IDLE;
break;
}
if (hdmirx_rd_bits_dwc(DWC_HDCP_RPT_CTRL, WAITING_KSV)) {
rx.hdcp.delay++;
if (rx.hdcp.delay == 1)
rx_pr("[RX] receive ksv wait signal\n");
if (rx.hdcp.delay < KSV_LIST_WR_MAX) {
break;
} else if (rx.hdcp.delay >= KSV_LIST_WAIT_DELAY) {
hdmirx_wr_bits_dwc(DWC_HDCP_RPT_CTRL,
KSVLIST_TIMEOUT, 1);
rx.hdcp.state = REPEATER_STATE_IDLE;
rx_pr("[RX] receive ksv wait timeout\n");
}
if (rx_set_repeat_aksv(topo_data->ksv_list,
topo_data->device_count,
topo_data->depth,
topo_data->max_devs_exceeded,
topo_data->max_cascade_exceeded)) {
rx.hdcp.state = REPEATER_STATE_IDLE;
}
}
/*if support hdcp2.2 jump to wait_ack else to idle*/
break;
case REPEATER_STATE_WAIT_ACK:
/*if receive ack jump to idle else send auth_req*/
break;
case REPEATER_STATE_IDLE:
break;
default:
break;
}
}
/*hdr change from app*/
/*if (new_hdr_lum) {*/
/*hdmi_rx_ctrl_edid_update();*/
/*new_hdr_lum = false;*/
/*rx_send_hpd_pulse();*/
/*}*/
}
void rx_reload_firm_reset(int reset)
{
if (reset)
hdmirx_load_firm_reset(reset);
else
rx_firm_reset_end();
}
void rx_firm_reset_end(void)
{
rx_pr("%s new_edid:%d\n", __func__, new_edid);
if (new_edid) {
new_edid = 0;
hdmi_rx_top_edid_update();
}
rx.firm_change = 0;
}
unsigned char *rx_get_dw_edid_addr(void)
{
return receive_edid;
}
int rx_set_receiver_edid(const char *data, int len)
{
if (!data || !len)
return false;
if (len > MAX_RECEIVE_EDID || len < 3) {
memset(receive_edid, 0, sizeof(receive_edid));
edid_len = 0;
} else {
memcpy(receive_edid, data, len);
edid_len = len;
}
new_edid = true;
return true;
}
void rx_hdcp14_resume(void)
{
hdcp22_kill_esm = 0;
/* extcon_set_state_sync(rx.rx_excton_rx22, EXTCON_DISP_HDMI, 0); */
rx_hdcp22_send_uevent(0);
hdmirx_wr_dwc(DWC_HDCP22_CONTROL, 0x1000);
/* extcon_set_state_sync(rx.rx_excton_rx22, EXTCON_DISP_HDMI, 1); */
rx_hdcp22_send_uevent(1);
hpd_to_esm = 1;
rx_pr("hdcp14 on\n");
}
void rx_set_repeater_support(bool enable)
{
downstream_repeat_support = enable;
repeat_plug = enable;
rx_pr("****************=%d\n", downstream_repeat_support);
}
EXPORT_SYMBOL(rx_set_repeater_support);
bool rx_poll_dwc(u16 addr, u32 exp_data,
u32 mask, u32 max_try)
{
u32 rd_data;
u32 cnt = 0;
u8 done = 0;
rd_data = hdmirx_rd_dwc(addr);
while (((cnt < max_try) || (max_try == 0)) && (done != 1)) {
if ((rd_data & mask) == (exp_data & mask)) {
done = 1;
} else {
cnt++;
rd_data = hdmirx_rd_dwc(addr);
}
}
if (log_level & VIDEO_LOG)
rx_pr("poll dwc cnt :%d\n", cnt);
if (done == 0) {
/* if(log_level & ERR_LOG) */
rx_pr("poll dwc%x time-out!\n", addr);
return false;
}
return true;
}
bool rx_set_repeat_aksv(unsigned char *data, int len, int depth,
bool dev_exceed, bool cascade_exceed)
{
int i;
bool ksvlist_ready = 0;
if (data == 0 || ((depth == 0 || len == 0) &&
!dev_exceed && !cascade_exceed))
return false;
rx_pr("set ksv list len:%d,depth:%d, exceed count:%d,cascade:%d\n",
len, depth, dev_exceed, cascade_exceed);
/*set repeat depth*/
if (depth <= MAX_REPEAT_DEPTH && !cascade_exceed) {
hdmirx_wr_bits_dwc(DWC_HDCP_RPT_BSTATUS,
MAX_CASCADE_EXCEEDED, 0);
hdmirx_wr_bits_dwc(DWC_HDCP_RPT_BSTATUS, DEPTH, depth);
rx.hdcp.depth = depth;
} else {
hdmirx_wr_bits_dwc(DWC_HDCP_RPT_BSTATUS,
MAX_CASCADE_EXCEEDED, 1);
rx.hdcp.depth = 0;
}
/*set repeat count*/
if (len <= HDCP14_KSV_MAX_COUNT && !dev_exceed) {
hdmirx_wr_bits_dwc(DWC_HDCP_RPT_BSTATUS,
MAX_DEVS_EXCEEDED, 0);
rx.hdcp.count = len;
hdmirx_wr_bits_dwc(DWC_HDCP_RPT_BSTATUS, DEVICE_COUNT,
rx.hdcp.count);
} else {
hdmirx_wr_bits_dwc(DWC_HDCP_RPT_BSTATUS,
MAX_DEVS_EXCEEDED, 1);
rx.hdcp.count = 0;
}
/*set repeat status*/
/* if (rx.hdcp.count > 0) {
* rx.hdcp.repeat = true;
* hdmirx_wr_bits_dwc(DWC_HDCP_RPT_CTRL, REPEATER, 1);
*} else {
* rx.hdcp.repeat = false;
* hdmirx_wr_bits_dwc(DWC_HDCP_RPT_CTRL, REPEATER, 0);
*}
*/
ksvlist_ready = ((rx.hdcp.count > 0) && (rx.hdcp.depth > 0));
rx_pr("[RX]write ksv list count:%d\n", rx.hdcp.count);
/*write ksv list to fifo*/
for (i = 0; i < rx.hdcp.count; i++) {
if (rx_poll_dwc(DWC_HDCP_RPT_CTRL, ~KSV_HOLD, KSV_HOLD,
KSV_LIST_WR_TH)) {
/*check fifo can be written*/
hdmirx_wr_dwc(DWC_HDCP_RPT_KSVFIFOCTRL, i);
hdmirx_wr_dwc(DWC_HDCP_RPT_KSVFIFO1,
*(data + i * MAX_KSV_SIZE + 4));
hdmirx_wr_dwc(DWC_HDCP_RPT_KSVFIFO0,
*((u32 *)(data + i * MAX_KSV_SIZE)));
if (log_level & VIDEO_LOG)
rx_pr("[RX]write ksv list index:%d, ksv hi:%#x, low:%#x\n",
i, *(data + i * MAX_KSV_SIZE + 4),
*((u32 *)(data + i * MAX_KSV_SIZE)));
} else {
return false;
}
}
/* Wait for ksv_hold=0*/
rx_poll_dwc(DWC_HDCP_RPT_CTRL, ~KSV_HOLD, KSV_HOLD, KSV_LIST_WR_TH);
/*set ksv list ready*/
hdmirx_wr_bits_dwc(DWC_HDCP_RPT_CTRL, KSVLIST_READY, ksvlist_ready);
/* Wait for HW completion of V value*/
if (ksvlist_ready)
rx_poll_dwc(DWC_HDCP_RPT_CTRL, FIFO_READY,
FIFO_READY, KSV_V_WR_TH);
rx_pr("[RX]write Ready signal! ready:%u\n",
(unsigned int)ksvlist_ready);
return true;
}
void rx_set_repeat_signal(bool repeat)
{
rx.hdcp.repeat = repeat;
hdmirx_wr_bits_dwc(DWC_HDCP_RPT_CTRL, REPEATER, repeat);
}
bool rx_set_receive_hdcp(unsigned char *data, int len, int depth,
bool cas_exceed, bool devs_exceed)
{
return true;
}
EXPORT_SYMBOL(rx_set_receive_hdcp);
void rx_repeat_hpd_state(bool plug)
{
}
EXPORT_SYMBOL(rx_repeat_hpd_state);