860 lines
24 KiB
C
860 lines
24 KiB
C
/*
|
|
* (C) Copyright 2012
|
|
* Amlogic. Inc. zongdong.jiao@amlogic.com
|
|
*
|
|
* This file is used to prefetch/verify/compare HDCP keys
|
|
*
|
|
* 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.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
|
|
* MA 02111-1307 USA
|
|
*/
|
|
|
|
#include <common.h>
|
|
#include <command.h>
|
|
#include <environment.h>
|
|
#include <malloc.h>
|
|
#include <asm/byteorder.h>
|
|
#include <amlogic/hdmi.h>
|
|
#include <amlogic/dolby_vision.h>
|
|
#ifdef CONFIG_AML_LCD
|
|
#include <amlogic/aml_lcd.h>
|
|
#endif
|
|
|
|
#ifdef CONFIG_AML_LCD
|
|
static unsigned int hdmitx_parse_vout_name(char *name)
|
|
{
|
|
char *p, *frac_str;
|
|
unsigned int frac = 0;
|
|
|
|
p = strchr(name, ',');
|
|
if (!p) {
|
|
frac = 0;
|
|
} else {
|
|
frac_str = p + 1;
|
|
*p = '\0';
|
|
if (strcmp(frac_str, "frac") == 0)
|
|
frac = 1;
|
|
}
|
|
|
|
return frac;
|
|
}
|
|
#endif
|
|
|
|
static int vout_hdmi_hpd(int hpd_st)
|
|
{
|
|
#ifdef CONFIG_AML_LCD
|
|
struct aml_lcd_drv_s *lcd_drv = NULL;
|
|
char *mode;
|
|
unsigned int frac;
|
|
#endif
|
|
char *hdmimode;
|
|
char *cvbsmode;
|
|
char *colorattribute;
|
|
|
|
#ifdef CONFIG_AML_LCD
|
|
lcd_drv = aml_lcd_get_driver();
|
|
if (lcd_drv) {
|
|
if (lcd_drv->lcd_outputmode_check) {
|
|
mode = (char *)malloc(64 * sizeof(char));
|
|
if (!mode) {
|
|
printf("cmd_hpd: mode malloc falied\n");
|
|
if (lcd_drv->lcd_outputmode_check(mode, 0) == 0)
|
|
return 0;
|
|
} else {
|
|
memset(mode, 0, sizeof(mode));
|
|
sprintf(mode, "%s", getenv("outputmode"));
|
|
frac = hdmitx_parse_vout_name(mode);
|
|
if (lcd_drv->lcd_outputmode_check(mode, frac) == 0) {
|
|
free(mode);
|
|
return 0;
|
|
}
|
|
free(mode);
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
/*get hdmi mode and colorattribute from env */
|
|
hdmimode = getenv("hdmimode");
|
|
if (hdmimode)
|
|
printf("%s: hdmimode=%s\n", __func__, hdmimode);
|
|
|
|
colorattribute = getenv("colorattribute");
|
|
if (colorattribute)
|
|
printf("%s: colorattribute=%s\n", __func__, colorattribute);
|
|
|
|
if (hdmimode) {
|
|
if (strstr(hdmimode, "null")) {
|
|
setenv("hdmimode", "1080p60hz");
|
|
run_command("saveenv", 0);
|
|
}
|
|
} else {
|
|
setenv("hdmimode", "1080p60hz");
|
|
run_command("saveenv", 0);
|
|
}
|
|
hdmimode = getenv("hdmimode");
|
|
if (hpd_st && hdmimode) {
|
|
setenv("outputmode", hdmimode);
|
|
} else {
|
|
cvbsmode = getenv("cvbsmode");
|
|
if (cvbsmode)
|
|
setenv("outputmode", cvbsmode);
|
|
setenv("hdmichecksum", "0x00000000");
|
|
run_command("saveenv", 0);
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
static int vout2_hdmi_hpd(int hpd_st)
|
|
{
|
|
#ifdef CONFIG_AML_LCD
|
|
struct aml_lcd_drv_s *lcd_drv = NULL;
|
|
char *mode;
|
|
unsigned int frac;
|
|
#endif
|
|
char *hdmimode;
|
|
char *cvbsmode;
|
|
char *colorattribute;
|
|
|
|
#ifdef CONFIG_AML_LCD
|
|
lcd_drv = aml_lcd_get_driver();
|
|
if (lcd_drv) {
|
|
if (lcd_drv->lcd_outputmode_check) {
|
|
mode = (char *)malloc(64 * sizeof(char));
|
|
if (!mode) {
|
|
printf("cmd_hpd: mode malloc failed\n");
|
|
if (lcd_drv->lcd_outputmode_check(mode, 0) == 0)
|
|
return 0;
|
|
} else {
|
|
memset(mode, 0, sizeof(mode));
|
|
sprintf(mode, "%s", getenv("outputmode2"));
|
|
frac = hdmitx_parse_vout_name(mode);
|
|
if (lcd_drv->lcd_outputmode_check(mode, frac) == 0) {
|
|
free(mode);
|
|
return 0;
|
|
}
|
|
free(mode);
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
/*get hdmi mode and colorattribute from env */
|
|
hdmimode = getenv("hdmimode");
|
|
if (hdmimode)
|
|
printf("%s: hdmimode=%s\n", __func__, hdmimode);
|
|
|
|
colorattribute = getenv("colorattribute");
|
|
if (colorattribute)
|
|
printf("%s: colorattribute=%s\n", __func__, colorattribute);
|
|
|
|
if (hdmimode) {
|
|
if (strstr(hdmimode, "null")) {
|
|
setenv("hdmimode", "1080p60hz");
|
|
run_command("saveenv", 0);
|
|
}
|
|
} else {
|
|
setenv("hdmimode", "1080p60hz");
|
|
run_command("saveenv", 0);
|
|
}
|
|
hdmimode = getenv("hdmimode");
|
|
if (hpd_st && hdmimode) {
|
|
setenv("outputmode2", hdmimode);
|
|
} else {
|
|
cvbsmode = getenv("cvbsmode");
|
|
if (cvbsmode)
|
|
setenv("outputmode2", cvbsmode);
|
|
setenv("hdmichecksum", "0x00000000");
|
|
run_command("saveenv", 0);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int do_hpd_detect(cmd_tbl_t *cmdtp, int flag, int argc,
|
|
char *const argv[])
|
|
{
|
|
int st;
|
|
int hpd_st = 0;
|
|
/* some TV sets pull hpd high 1.3S after detect pwr5v high */
|
|
int loop = 15;
|
|
int ret = 0;
|
|
|
|
st = getenv_ulong("hdmitx_hpd_bypass", 10, 0);
|
|
if (st) {
|
|
printf("hdmitx_hpd_bypass detect\n");
|
|
return 0;
|
|
}
|
|
|
|
hpd_st = hdmitx_device.HWOp.get_hpd_state();
|
|
if (!hpd_st) {
|
|
/* For some TV, they cost extra time to pullup HPD after 5V */
|
|
while (loop--) {
|
|
mdelay(100);
|
|
hpd_st = hdmitx_device.HWOp.get_hpd_state();
|
|
if (hpd_st) {
|
|
printf("hpd delay %d ms\n", (15 - loop) * 100);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
printf("hpd_state=%d\n", hpd_st);
|
|
|
|
ret = vout_hdmi_hpd(hpd_st);
|
|
if (!ret)
|
|
vout2_hdmi_hpd(hpd_st);
|
|
|
|
hdmitx_device.hpd_state = hpd_st;
|
|
return hpd_st;
|
|
}
|
|
|
|
bool hdmitx_get_hpd_state_ext(void)
|
|
{
|
|
return hdmitx_device.hpd_state;
|
|
}
|
|
|
|
static unsigned char edid_raw_buf[256] = {0};
|
|
static void dump_edid_raw_8bytes(unsigned char *buf)
|
|
{
|
|
int i = 0;
|
|
for (i = 0; i < 8; i++)
|
|
printf("%02x ", buf[i]);
|
|
printf("\n");
|
|
}
|
|
|
|
static void dump_full_edid(const unsigned char *buf)
|
|
{
|
|
int i;
|
|
int blk_no = buf[126] + 1;
|
|
|
|
if (blk_no > 4)
|
|
blk_no = 4;
|
|
|
|
if (blk_no == 2)
|
|
if (buf[128 + 4] == 0xe2 && buf[128 + 5] == 0x78)
|
|
blk_no = buf[128 + 6] + 1;
|
|
if (blk_no > EDID_BLK_NO)
|
|
blk_no = EDID_BLK_NO;
|
|
|
|
printf("Dump EDID Rawdata\n");
|
|
for (i = 0; i < blk_no * EDID_BLK_SIZE; i++) {
|
|
printk("%02x", buf[i]);
|
|
if (((i+1) & 0x1f) == 0) /* print 32bytes a line */
|
|
printk("\n");
|
|
}
|
|
}
|
|
|
|
static int do_edid(cmd_tbl_t *cmdtp, int flag, int argc, char *const argv[])
|
|
{
|
|
unsigned int tmp_addr = 0;
|
|
unsigned char edid_addr = 0;
|
|
unsigned char st = 0;
|
|
|
|
memset(edid_raw_buf, 0, ARRAY_SIZE(edid_raw_buf));
|
|
if (argc < 2)
|
|
return cmd_usage(cmdtp);
|
|
if (strcmp(argv[1], "read") == 0) {
|
|
tmp_addr = simple_strtoul(argv[2], NULL, 16);
|
|
if (tmp_addr > 0xff)
|
|
return cmd_usage(cmdtp);
|
|
edid_addr = tmp_addr;
|
|
/* read edid raw data */
|
|
/* current only support read 1 byte edid data */
|
|
st = hdmitx_device.HWOp.read_edid(
|
|
&edid_raw_buf[edid_addr & 0xf8], edid_addr & 0xf8, 8);
|
|
printf("edid[0x%02x]: 0x%02x\n", edid_addr,
|
|
edid_raw_buf[edid_addr]);
|
|
if (0) /* Debug only */
|
|
dump_edid_raw_8bytes(&edid_raw_buf[edid_addr & 0xf8]);
|
|
if (!st)
|
|
printf("edid read failed\n");
|
|
}
|
|
return st;
|
|
}
|
|
|
|
static int do_rx_det(cmd_tbl_t * cmdtp, int flag, int argc, char * const argv[])
|
|
{
|
|
unsigned char edid_addr = 0xf8; // Fixed Address
|
|
unsigned char st = 0;
|
|
|
|
memset(edid_raw_buf, 0, ARRAY_SIZE(edid_raw_buf));
|
|
|
|
// read edid raw data
|
|
// current only support read 1 byte edid data
|
|
st = hdmitx_device.HWOp.read_edid(&edid_raw_buf[edid_addr & 0xf8], edid_addr & 0xf8, 8);
|
|
if (1) // Debug only
|
|
dump_edid_raw_8bytes(&edid_raw_buf[edid_addr & 0xf8]);
|
|
if (st) {
|
|
#if 0
|
|
// set fake value for debug
|
|
edid_raw_buf[250] = 0xfb;
|
|
edid_raw_buf[251] = 0x0c;
|
|
edid_raw_buf[252] = 0x01;
|
|
#endif
|
|
if ((edid_raw_buf[250] == 0xfb) & (edid_raw_buf[251] == 0x0c)) {
|
|
printf("RX is FBC\n");
|
|
|
|
// set outputmode ENV
|
|
switch (edid_raw_buf[252] & 0x0f) {
|
|
case 0x0:
|
|
run_command("setenv outputmode 1080p50hz", 0);
|
|
break;
|
|
case 0x1:
|
|
run_command("setenv outputmode 2160p50hz420", 0);
|
|
break;
|
|
case 0x2:
|
|
run_command("setenv outputmode 1080p50hz44410bit", 0);
|
|
break;
|
|
case 0x3:
|
|
run_command("setenv outputmode 2160p50hz42010bit", 0);
|
|
break;
|
|
case 0x4:
|
|
run_command("setenv outputmode 2160p50hz42210bit", 0);
|
|
break;
|
|
case 0x5:
|
|
run_command("setenv outputmode 2160p50hz", 0);
|
|
break;
|
|
default:
|
|
run_command("setenv outputmode 1080p50hz", 0);
|
|
break;
|
|
}
|
|
|
|
// set RX 3D Info
|
|
switch ((edid_raw_buf[252] >> 4) & 0x0f) {
|
|
case 0x00:
|
|
run_command("setenv rx_3d_info 0", 0);
|
|
break;
|
|
case 0x01:
|
|
run_command("setenv rx_3d_info 1", 0);
|
|
break;
|
|
case 0x02:
|
|
run_command("setenv rx_3d_info 2", 0);
|
|
break;
|
|
case 0x03:
|
|
run_command("setenv rx_3d_info 3", 0);
|
|
break;
|
|
case 0x04:
|
|
run_command("setenv rx_3d_info 4", 0);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
switch (edid_raw_buf[253]) {
|
|
case 0x1:
|
|
// TODO
|
|
break;
|
|
case 0x2:
|
|
// TODO
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
} else
|
|
printf("edid read failed\n");
|
|
|
|
return st;
|
|
}
|
|
|
|
static void save_default_720p(void)
|
|
{
|
|
setenv("hdmimode", "720p60hz");
|
|
setenv("outputmode", "720p60hz");
|
|
setenv("colorattribute", "rgb,8bit");
|
|
}
|
|
|
|
static void hdmitx_mask_rx_info(struct hdmitx_dev *hdev)
|
|
{
|
|
if (!hdev || !hdev->para)
|
|
return;
|
|
|
|
if (getenv("colorattribute"))
|
|
hdmi_parse_attr(hdev->para, getenv("colorattribute"));
|
|
|
|
/* when current output color depth is 8bit, mask hdr capability */
|
|
/* refer to SWPL-44445 for more detail */
|
|
if (hdev->para->cd == HDMI_COLOR_DEPTH_24B)
|
|
memset(&hdev->RXCap.hdr_info, 0, sizeof(struct hdr_info));
|
|
}
|
|
|
|
static void hdmitx_config_csc_en(struct hdmitx_dev *hdev)
|
|
{
|
|
if (!hdev)
|
|
return;
|
|
|
|
if (getenv("config_csc_en")) {
|
|
if (strncmp("0", getenv("config_csc_en"), 1) == 0)
|
|
hdev->config_csc_en = 0;
|
|
else
|
|
hdev->config_csc_en = 1;
|
|
printf("config_csc_en:%d\n", hdev->config_csc_en);
|
|
} else {
|
|
hdev->config_csc_en = 0;
|
|
}
|
|
}
|
|
|
|
static int do_output(cmd_tbl_t *cmdtp, int flag, int argc, char *const argv[])
|
|
{
|
|
if (argc < 1)
|
|
return cmd_usage(cmdtp);
|
|
|
|
if (strcmp(argv[1], "list") == 0)
|
|
hdmitx_device.HWOp.list_support_modes();
|
|
else if (strcmp(argv[1], "bist") == 0) {
|
|
unsigned int mode = 0;
|
|
if (strcmp(argv[2], "off") == 0)
|
|
mode = 0;
|
|
else if (strcmp(argv[2], "line") == 0)
|
|
mode = 2;
|
|
else if (strcmp(argv[2], "dot") == 0)
|
|
mode = 3;
|
|
else
|
|
mode = simple_strtoul(argv[2], NULL, 10);
|
|
hdmitx_device.HWOp.test_bist(mode);
|
|
} else if (strcmp(argv[1], "prbs") == 0) {
|
|
hdmitx_device.para->cs = HDMI_COLOR_FORMAT_RGB;
|
|
hdmitx_device.para->cd = HDMI_COLOR_DEPTH_24B;
|
|
hdmitx_device.vic = HDMI_1920x1080p60_16x9;
|
|
hdmi_tx_set(&hdmitx_device);
|
|
hdmitx_device.HWOp.test_bist(10);
|
|
} else { /* "output" */
|
|
if (!edid_parsing_ok(&hdmitx_device)) {
|
|
/* in SWPL-34712: if EDID parsing error in kernel,
|
|
* only forcely output default mode(480p,RGB,8bit)
|
|
* in sysctl, not save the default mode to env.
|
|
* if uboot follow this rule, will cause issue OTT-19333:
|
|
* uboot read edid error and then output default mode,
|
|
* without save it mode env. if then kernel edid normal,
|
|
* sysctrl/kernel get mode from env, the actual output
|
|
* mode differs with outputmode env,it will
|
|
* cause display abnormal(such as stretch). so don't
|
|
* follow this rule in uboot, that's to say the actual
|
|
* output mode needs to stays with the outputmode env.
|
|
*/
|
|
printf("edid parsing ng, forcely output 720p, rgb,8bit\n");
|
|
save_default_720p();
|
|
hdmitx_device.vic = HDMI_1280x720p60_16x9;
|
|
hdmitx_device.para =
|
|
hdmi_get_fmt_paras(hdmitx_device.vic);
|
|
hdmitx_device.para->cs = HDMI_COLOR_FORMAT_RGB;
|
|
hdmitx_device.para->cd = HDMI_COLOR_DEPTH_24B;
|
|
hdmi_tx_set(&hdmitx_device);
|
|
return CMD_RET_SUCCESS;
|
|
}
|
|
hdmitx_device.vic = hdmi_get_fmt_vic(argv[1]);
|
|
hdmitx_device.para = hdmi_get_fmt_paras(hdmitx_device.vic);
|
|
if (hdmitx_device.vic == HDMI_unknown) {
|
|
/* Not find VIC */
|
|
printf("Not find '%s' mapped VIC\n", argv[1]);
|
|
return CMD_RET_FAILURE;
|
|
} else
|
|
printf("set hdmitx VIC = %d\n", hdmitx_device.vic);
|
|
if (strstr(argv[1], "hz420") != NULL)
|
|
hdmitx_device.para->cs = HDMI_COLOR_FORMAT_420;
|
|
if (getenv("colorattribute"))
|
|
hdmi_parse_attr(hdmitx_device.para, getenv("colorattribute"));
|
|
/* For RGB444 or YCbCr444 under 6Gbps mode, no deepcolor */
|
|
/* Only 4k50/60 has 420 modes */
|
|
switch (hdmitx_device.vic) {
|
|
case HDMI_3840x2160p50_16x9:
|
|
case HDMI_3840x2160p60_16x9:
|
|
case HDMI_4096x2160p50_256x135:
|
|
case HDMI_4096x2160p60_256x135:
|
|
case HDMI_3840x2160p50_64x27:
|
|
case HDMI_3840x2160p60_64x27:
|
|
case HDMI_3840x2160p50_16x9_Y420:
|
|
case HDMI_3840x2160p60_16x9_Y420:
|
|
case HDMI_4096x2160p50_256x135_Y420:
|
|
case HDMI_4096x2160p60_256x135_Y420:
|
|
case HDMI_3840x2160p50_64x27_Y420:
|
|
case HDMI_3840x2160p60_64x27_Y420:
|
|
if ((hdmitx_device.para->cs == HDMI_COLOR_FORMAT_RGB) ||
|
|
(hdmitx_device.para->cs == HDMI_COLOR_FORMAT_444)) {
|
|
if (hdmitx_device.para->cd != HDMI_COLOR_DEPTH_24B) {
|
|
printf("vic %d cs %d has no cd %d\n",
|
|
hdmitx_device.vic,
|
|
hdmitx_device.para->cs,
|
|
hdmitx_device.para->cd);
|
|
hdmitx_device.para->cd = HDMI_COLOR_DEPTH_24B;
|
|
printf("set cd as %d\n", HDMI_COLOR_DEPTH_24B);
|
|
}
|
|
}
|
|
if (hdmitx_device.para->cs == HDMI_COLOR_FORMAT_420)
|
|
hdmitx_device.vic |= HDMITX_VIC420_OFFSET;
|
|
break;
|
|
default:
|
|
if (hdmitx_device.para->cs == HDMI_COLOR_FORMAT_420) {
|
|
printf("vic %d has no cs %d\n", hdmitx_device.vic,
|
|
hdmitx_device.para->cs);
|
|
hdmitx_device.para->cs = HDMI_COLOR_FORMAT_444;
|
|
printf("set cs as %d\n", HDMI_COLOR_FORMAT_444);
|
|
}
|
|
/* For VESA modes, should be RGB format */
|
|
if (hdmitx_device.vic >= HDMITX_VESA_OFFSET) {
|
|
hdmitx_device.para->cs = HDMI_COLOR_FORMAT_RGB;
|
|
hdmitx_device.para->cd = HDMI_COLOR_DEPTH_24B;
|
|
}
|
|
break;
|
|
}
|
|
/* currently, hdmi mode is always set, if
|
|
* mode set abort/exit, need to add return
|
|
* result of mode setting, so that vout
|
|
* driver will pass it to kernel, and do
|
|
* mode setting again when vout init in kernel
|
|
*/
|
|
hdmitx_config_csc_en(&hdmitx_device);
|
|
hdmi_tx_set(&hdmitx_device);
|
|
}
|
|
return CMD_RET_SUCCESS;
|
|
}
|
|
|
|
static int do_blank(cmd_tbl_t *cmdtp, int flag, int argc, char *const argv[])
|
|
{
|
|
if (argc < 1)
|
|
return cmd_usage(cmdtp);
|
|
|
|
if (strcmp(argv[1], "1") == 0)
|
|
hdmitx_device.HWOp.output_blank(1);
|
|
if (strcmp(argv[1], "0") == 0)
|
|
hdmitx_device.HWOp.output_blank(0);
|
|
|
|
return CMD_RET_SUCCESS;
|
|
}
|
|
|
|
static int do_off(cmd_tbl_t * cmdtp, int flag, int argc, char * const argv[])
|
|
{
|
|
hdmitx_device.vic = HDMI_unknown;
|
|
hdmitx_device.HWOp.turn_off();
|
|
printf("turn off hdmitx\n");
|
|
return 1;
|
|
}
|
|
|
|
static int do_dump(cmd_tbl_t * cmdtp, int flag, int argc, char * const argv[])
|
|
{
|
|
hdmitx_device.HWOp.dump_regs();
|
|
return 1;
|
|
}
|
|
|
|
static int do_info(cmd_tbl_t * cmdtp, int flag, int argc, char * const argv[])
|
|
{
|
|
struct hdmitx_dev *hdev = &hdmitx_device;
|
|
struct hdmi_format_para *para = hdev->para;
|
|
|
|
printf("%s %d\n", para->ext_name, hdev->vic);
|
|
printf("cd%d cs%d cr%d\n", para->cd, para->cs, para->cr);
|
|
printf("frac_rate: %d\n", hdev->frac_rate_policy);
|
|
return 1;
|
|
}
|
|
|
|
static int xtochar(int num, char *checksum)
|
|
{
|
|
struct hdmitx_dev *hdev = &hdmitx_device;
|
|
|
|
if (((hdev->rawedid[num] >> 4 ) & 0xf) <= 9)
|
|
checksum[0] = ((hdev->rawedid[num] >> 4 ) & 0xf) + '0';
|
|
else
|
|
checksum[0] = ((hdev->rawedid[num] >> 4 ) & 0xf) -10 + 'a';
|
|
|
|
if ((hdev->rawedid[num] & 0xf) <= 9)
|
|
checksum[1] = (hdev->rawedid[num] & 0xf) + '0';
|
|
else
|
|
checksum[1] = (hdev->rawedid[num] & 0xf) -10 + 'a';
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void get_parse_edid_data(struct hdmitx_dev *hdev)
|
|
{
|
|
unsigned char *edid = hdev->rawedid;
|
|
unsigned int byte_num = 0;
|
|
unsigned char blk_no = 1;
|
|
char *hdr_priority = getenv("hdr_priority");
|
|
|
|
/* get edid data */
|
|
while (byte_num < 128 * blk_no) {
|
|
hdev->HWOp.read_edid(&edid[byte_num], byte_num & 0x7f, byte_num / 128);
|
|
if (byte_num == 120) {
|
|
blk_no = edid[126] + 1;
|
|
if (blk_no > 4)
|
|
blk_no = 4; /* MAX Read Blocks 4 */
|
|
}
|
|
if (byte_num == 128) {
|
|
if (edid[128 + 4] == 0xe2 && edid[128 + 5] == 0x78)
|
|
blk_no = edid[128 + 6] + 1;
|
|
if (blk_no > EDID_BLK_NO)
|
|
blk_no = EDID_BLK_NO; /* MAX Read Blocks 8 */
|
|
}
|
|
byte_num += 8;
|
|
}
|
|
|
|
/* dump edid raw data */
|
|
dump_full_edid(hdev->rawedid);
|
|
|
|
/* parse edid data */
|
|
hdmi_edid_parsing(hdev->rawedid, &hdev->RXCap);
|
|
|
|
if (!hdr_priority)
|
|
return;
|
|
/* if hdr_priority is 2, then mark both dv_info and hdr_info */
|
|
if (strcmp(hdr_priority, "2") == 0) {
|
|
memset(&hdev->RXCap.dv_info, 0, sizeof(struct dv_info));
|
|
memset(&hdev->RXCap.hdr_info, 0, sizeof(struct hdr_info));
|
|
memset(&hdev->RXCap.hdr10plus_info, 0, sizeof(struct hdr10_plus_info));
|
|
pr_info("hdr_priority: %s and clear dv/hdr_info\n", hdr_priority);
|
|
return;
|
|
}
|
|
/* if hdr_priority is 1, then only mark dv_info */
|
|
if (strcmp(hdr_priority, "1") == 0) {
|
|
memset(&hdev->RXCap.dv_info, 0, sizeof(struct dv_info));
|
|
pr_info("hdr_priority: %s and clear dv_info\n", hdr_priority);
|
|
}
|
|
}
|
|
|
|
/* policy process: to find the output mode/attr/dv_type */
|
|
void scene_process(struct hdmitx_dev *hdev,
|
|
scene_output_info_t *scene_output_info)
|
|
{
|
|
hdmi_data_t hdmidata;
|
|
|
|
if (!hdev || !scene_output_info)
|
|
return;
|
|
/* 1.read dolby vision mode from prop(maybe need to env) */
|
|
memset(&hdmidata, 0, sizeof(hdmi_data_t));
|
|
get_hdmi_data(hdev, &hdmidata);
|
|
|
|
/* 2. dolby vision scene process */
|
|
/* only for tv support dv and box enable dv */
|
|
if (is_dv_preference(hdev)) {
|
|
dolbyvision_scene_process(&hdmidata, scene_output_info);
|
|
} else if (is_dolby_enabled()) {
|
|
/* for enable dolby vision core when
|
|
* first boot connecting non dv tv
|
|
* NOTE: let systemcontrol to enable DV core
|
|
*/
|
|
/* scene_output_info->final_dv_type = DOLBY_VISION_ENABLE; */
|
|
} else {
|
|
/* for UI disable dolby vision core and boot keep the status
|
|
* NOTE: TBD if need to disable DV here
|
|
*/
|
|
/* scene_output_info->final_dv_type = DOLBY_VISION_DISABLE; */
|
|
}
|
|
/* 3.sdr scene process */
|
|
/* decide final display mode and deepcolor */
|
|
if (is_dv_preference(hdev)) {
|
|
/* do nothing
|
|
* already done above, just sync with sysctrl
|
|
*/
|
|
} else if (is_hdr_preference(hdev)) {
|
|
hdr_scene_process(&hdmidata, scene_output_info);
|
|
} else {
|
|
sdr_scene_process(&hdmidata, scene_output_info);
|
|
}
|
|
/* not find outputmode and use default mode */
|
|
if (strlen(scene_output_info->final_displaymode) == 0)
|
|
strcpy(scene_output_info->final_displaymode, DEFAULT_HDMI_MODE);
|
|
/* not find color space and use default mode */
|
|
if (!strstr(scene_output_info->final_deepcolor, "bit"))
|
|
strcpy(scene_output_info->final_deepcolor, DEFAULT_COLOR_FORMAT);
|
|
}
|
|
|
|
static int do_get_parse_edid(cmd_tbl_t * cmdtp, int flag, int argc,
|
|
char * const argv[])
|
|
{
|
|
struct hdmitx_dev *hdev = &hdmitx_device;
|
|
|
|
unsigned char *store_checkvalue;
|
|
unsigned int i;
|
|
unsigned int checkvalue[4];
|
|
unsigned int checkvalue1;
|
|
unsigned int checkvalue2;
|
|
char checksum[11];
|
|
unsigned char def_cksum[] = {'0', 'x', '0', '0', '0', '0', '0', '0', '0', '0', '\0'};
|
|
char* hdmimode;
|
|
char* colorattribute;
|
|
char dv_type[2] = {0};
|
|
scene_output_info_t scene_output_info;
|
|
struct hdmi_format_para *para = NULL;
|
|
bool mode_support = false;
|
|
|
|
if (!hdev->hpd_state) {
|
|
printf("HDMI HPD low, no need parse EDID\n");
|
|
return 1;
|
|
}
|
|
memset(&scene_output_info, 0, sizeof(scene_output_info_t));
|
|
memset(hdev->rawedid, 0, EDID_BLK_SIZE * EDID_BLK_NO);
|
|
|
|
get_parse_edid_data(hdev);
|
|
|
|
/* check if the tv has changed or anything wrong */
|
|
store_checkvalue = (unsigned char*)getenv("hdmichecksum");
|
|
colorattribute = getenv("colorattribute");
|
|
hdmimode = getenv("hdmimode");
|
|
if (!store_checkvalue)
|
|
store_checkvalue = def_cksum;
|
|
|
|
printf("read hdmichecksum: %s, hdmimode: %s, colorattribute: %s\n",
|
|
store_checkvalue, hdmimode, colorattribute);
|
|
|
|
for (i = 0; i < 4; i++) {
|
|
if (('0' <= store_checkvalue[i * 2 + 2]) && (store_checkvalue[i * 2 + 2] <= '9'))
|
|
checkvalue1 = store_checkvalue[i * 2 + 2] -'0';
|
|
else
|
|
checkvalue1 = store_checkvalue[i * 2 + 2] -'W';
|
|
if (('0' <= store_checkvalue[i * 2 + 3]) && (store_checkvalue[i * 2 + 3] <= '9'))
|
|
checkvalue2 = store_checkvalue[i * 2 + 3] -'0';
|
|
else
|
|
checkvalue2 = store_checkvalue[i * 2 + 3] -'W';
|
|
checkvalue[i] = checkvalue1 * 16 + checkvalue2;
|
|
}
|
|
|
|
if ((checkvalue[0] != hdev->rawedid[0x7f]) ||
|
|
(checkvalue[1] != hdev->rawedid[0xff]) ||
|
|
(checkvalue[2] != hdev->rawedid[0x17f]) ||
|
|
(checkvalue[3] != hdev->rawedid[0x1ff])) {
|
|
hdev->RXCap.edid_changed = 1;
|
|
|
|
checksum[0] = '0';
|
|
checksum[1] = 'x';
|
|
for (i = 0; i < 4; i++)
|
|
xtochar(0x80 * i + 0x7f, &checksum[2* i + 2]);
|
|
checksum[10] = '\0';
|
|
memcpy(hdev->RXCap.checksum, checksum, 10);
|
|
printf("TV has changed, now crc: %s\n", checksum);
|
|
} else {
|
|
memcpy(hdev->RXCap.checksum, store_checkvalue, 10);
|
|
printf("TV is the same, checksum: %s\n", hdev->RXCap.checksum);
|
|
}
|
|
|
|
/* check current mode+colorattr support or not */
|
|
para = hdmi_tst_fmt_name(hdmimode, colorattribute);
|
|
if (hdmitx_edid_check_valid_mode(hdev, para))
|
|
mode_support = true;
|
|
else
|
|
mode_support = false;
|
|
|
|
/* two cases need to go with uboot mode select policy:
|
|
* 1.TV changed
|
|
* 2.TV not changed, but current mode(set by sysctrl/hwc)
|
|
* not supportted by uboot (probably means mode select policy or
|
|
* edid parse between sysctrl and uboot have some gap)
|
|
* then need to find proper output mode with uboot policy.
|
|
*/
|
|
if (hdev->RXCap.edid_changed || !mode_support) {
|
|
/* find proper mode if EDID changed */
|
|
scene_process(hdev, &scene_output_info);
|
|
setenv("hdmichecksum", hdev->RXCap.checksum);
|
|
if (edid_parsing_ok(hdev)) {
|
|
/* SWPL-34712: if EDID parsing error case, not save env,
|
|
* only output default mode(480p,RGB,8bit). after
|
|
* EDID read OK, systemcontrol will recover the hdmi
|
|
* mode from env, to avoid keep the default hdmi output
|
|
*/
|
|
setenv("hdmimode", scene_output_info.final_displaymode);
|
|
setenv("outputmode",
|
|
scene_output_info.final_displaymode);
|
|
setenv("colorattribute",
|
|
scene_output_info.final_deepcolor);
|
|
/* if change from DV TV to HDR/SDR TV, don't change
|
|
* DV status to disabled, as DV core need to be enabled.
|
|
*/
|
|
if ((scene_output_info.final_dv_type !=
|
|
get_ubootenv_dv_type()) &&
|
|
(scene_output_info.final_dv_type !=
|
|
DOLBY_VISION_DISABLE)) {
|
|
sprintf(dv_type, "%d", scene_output_info.final_dv_type);
|
|
setenv("dolby_status", dv_type);
|
|
/* according to the policy of systemcontrol,
|
|
* if current DV mode is not supported by TV
|
|
* EDID, DV type maybe changed to one witch
|
|
* TV support, and need VPP/DV module to
|
|
* update new DV output mode.
|
|
*/
|
|
printf("update dv_type: %d\n",
|
|
scene_output_info.final_dv_type);
|
|
}
|
|
} else {
|
|
save_default_720p();
|
|
}
|
|
printf("update outputmode: %s\n", getenv("outputmode"));
|
|
printf("update colorattribute: %s\n", getenv("colorattribute"));
|
|
printf("update hdmichecksum: %s\n", getenv("hdmichecksum"));
|
|
}
|
|
hdev->vic = hdmi_get_fmt_vic(getenv("outputmode"));
|
|
hdev->para = hdmi_get_fmt_paras(hdev->vic);
|
|
hdmitx_mask_rx_info(hdev);
|
|
return 0;
|
|
}
|
|
|
|
static cmd_tbl_t cmd_hdmi_sub[] = {
|
|
U_BOOT_CMD_MKENT(hpd, 1, 1, do_hpd_detect, "", ""),
|
|
U_BOOT_CMD_MKENT(edid, 3, 1, do_edid, "", ""),
|
|
U_BOOT_CMD_MKENT(rx_det, 1, 1, do_rx_det, "", ""),
|
|
U_BOOT_CMD_MKENT(output, 3, 1, do_output, "", ""),
|
|
U_BOOT_CMD_MKENT(blank, 3, 1, do_blank, "", ""),
|
|
U_BOOT_CMD_MKENT(off, 1, 1, do_off, "", ""),
|
|
U_BOOT_CMD_MKENT(dump, 1, 1, do_dump, "", ""),
|
|
U_BOOT_CMD_MKENT(info, 1, 1, do_info, "", ""),
|
|
U_BOOT_CMD_MKENT(get_parse_edid, 1, 1, do_get_parse_edid, "", ""),
|
|
};
|
|
|
|
static int do_hdmitx(cmd_tbl_t *cmdtp, int flag, int argc, char *const argv[])
|
|
{
|
|
cmd_tbl_t *c;
|
|
|
|
if (argc < 2)
|
|
return cmd_usage(cmdtp);
|
|
|
|
argc--;
|
|
argv++;
|
|
|
|
c = find_cmd_tbl(argv[0], &cmd_hdmi_sub[0], ARRAY_SIZE(cmd_hdmi_sub));
|
|
|
|
if (c)
|
|
return c->cmd(cmdtp, flag, argc, argv);
|
|
else
|
|
return cmd_usage(cmdtp);
|
|
}
|
|
|
|
U_BOOT_CMD(hdmitx, CONFIG_SYS_MAXARGS, 0, do_hdmitx,
|
|
"HDMITX sub-system 20190123",
|
|
"hdmitx hpd\n"
|
|
" Detect hdmi rx plug-in\n"
|
|
#if 0
|
|
"hdmitx edid read ADDRESS\n"
|
|
" Read hdmi rx edid from ADDRESS(0x00~0xff)\n"
|
|
#endif
|
|
"hdmitx output [list | FORMAT | bist PATTERN]\n"
|
|
" list: list support formats\n"
|
|
" FORMAT can be 720p60/50hz, 1080i60/50hz, 1080p60hz, etc\n"
|
|
" extend with 8bits/10bits, y444/y422/y420/rgb\n"
|
|
" such as 2160p60hz,10bits,y420\n"
|
|
" PATTERN: can be as: line, dot, off, or 1920(width)\n"
|
|
"hdmitx blank [0|1]\n"
|
|
" 1: output blank 0: output normal\n"
|
|
"hdmitx off\n"
|
|
" Turn off hdmitx output\n"
|
|
"hdmitx info\n"
|
|
" current mode info\n"
|
|
"hdmitx rx_det\n"
|
|
" Auto detect if RX is FBC and set outputmode\n"
|
|
);
|
|
|
|
struct hdr_info *hdmitx_get_rx_hdr_info(void)
|
|
{
|
|
struct hdmitx_dev *hdev = &hdmitx_device;
|
|
|
|
return &hdev->RXCap.hdr_info;
|
|
}
|