blob: e3a1e3d765f9837cc139564276c705fd230290a8 [file] [log] [blame]
/*
* drivers/amlogic/media/vin/tvin/hdmirx/hdmirx_repeater.c
*
* Copyright (C) 2017 Amlogic, Inc. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* 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.
*
*/
#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 edid_len;
MODULE_PARM_DESC(edid_len, "\n edid_len\n");
module_param(edid_len, int, 0664);
bool new_edid;
/*original bksv from device*/
static 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);
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); */
/*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 == NULL) || (len == 0))
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);
hdmirx_wr_dwc(DWC_HDCP22_CONTROL,
0x1000);
extcon_set_state_sync(rx.rx_excton_rx22, EXTCON_DISP_HDMI, 1);
hpd_to_esm = 1;
rx_pr("hdcp14 on\n");
}
void rx_set_repeater_support(bool enable)
{
downstream_repeat_support = enable;
}
EXPORT_SYMBOL(rx_set_repeater_support);
bool rx_poll_dwc(uint16_t addr, uint32_t exp_data,
uint32_t mask, uint32_t max_try)
{
uint32_t rd_data;
uint32_t cnt = 0;
uint8_t 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,
*((uint32_t *)(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), *((uint32_t *)(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!\n", 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);
void rx_edid_physical_addr(int a, int b, int c, int d)
{
}
EXPORT_SYMBOL(rx_edid_physical_addr);