/* * (C) Copyright 2012 * Amlogic. Inc. zongdong.jiao@amlogic.com * * This file is used to prefetch/varify/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 #include #include #include #include #include #ifdef CONFIG_AML_LCD #include #endif #include static int do_hpd_detect(cmd_tbl_t *cmdtp, int flag, int argc, char *const argv[]) { #ifdef CONFIG_AML_LCD struct aml_lcd_drv_s *lcd_drv = NULL; char *mode; #endif int st; #ifdef CONFIG_AML_LCD lcd_drv = aml_lcd_get_driver(); if (lcd_drv) { if (lcd_drv->lcd_outputmode_check) { mode = getenv("outputmode"); if (lcd_drv->lcd_outputmode_check(mode) == 0) return 0; } } #endif st = hdmitx_device.HWOp.get_hpd_state(); printf("hpd_state=%c\n", st ? '1' : '0'); if (st) { setenv("outputmode", getenv("hdmimode")); } else { setenv("outputmode", getenv("cvbsmode")); } return st; } static unsigned char edid_raw_buf[512] = {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; 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"); } } /* 500ms for each retry, total 7 retry */ #define EDID_RETRY_WAITTIME 500 static int do_edid(cmd_tbl_t *cmdtp, int flag, int argc, char *const argv[]) { unsigned int blk_len = 0; unsigned char count = 0; memset(edid_raw_buf, 0, ARRAY_SIZE(edid_raw_buf)); if (argc < 1) return cmd_usage(cmdtp); if (!hdmitx_device.HWOp.get_hpd_state()) { printf("HDMI cable is NOT connected\n"); return CMD_RET_FAILURE; } READ_EDID: /* read edid raw data */ blk_len = hdmitx_device.HWOp.read_edid_raw(edid_raw_buf); if (!blk_len) printf("edid read failed\n"); else { #ifdef DEBUG_DUMPEDID /* dump all raw data */ dump_full_edid(edid_raw_buf); #endif /* parsing edid data */ if (-EDID_ERR_RETRY == parse_edid(edid_raw_buf, blk_len, count)) { printf("hdmitx: read edid fails.. retry..\n"); mdelay(EDID_RETRY_WAITTIME); count++; goto READ_EDID; } /* select best resolution */ setenv("hdmimode", select_best_resolution()); if (strncmp((const char *)select_best_resolution(), "2160p", 5) == 0) setenv("4k", "yes"); else setenv("4k", "no"); setenv("vout", getenv("hdmimode")); } return CMD_RET_SUCCESS; } 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 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" */ hdmitx_device.vic = hdmi_get_fmt_vic(argv[1]); hdmitx_device.para = hdmi_get_fmt_paras(hdmitx_device.vic); if (hdmitx_device.vic == HDMI_unkown) { /* 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); } break; /* For VESA modes, should be RGB format */ if (hdmitx_device.vic >= HDMITX_VESA_OFFSET) hdmitx_device.para->cs = HDMI_COLOR_FORMAT_RGB; } 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_unkown; 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_mode(cmd_tbl_t *cmdtp, int flag, int argc, char *const argv[]) { if (strstr(argv[1], "dvi") != NULL) hdmitx_device.dvimode = 1; else hdmitx_device.dvimode = 0; 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 do_get_preferred_mode(cmd_tbl_t * cmdtp, int flag, int argc, char * const argv[]) { struct hdmitx_dev *hdev = &hdmitx_device; unsigned int byte_num = 0; unsigned char *edid = hdev->rawedid; unsigned char blk_no = 1; struct hdmi_format_para *para; char pref_mode[64]; char color_attr[64]; memset(edid, 0, EDID_BLK_SIZE * EDID_BLK_NO); memset(pref_mode, 0, sizeof(pref_mode)); memset(color_attr, 0, sizeof(color_attr)); /* Read complete EDID data sequentially */ while (byte_num < 128 * blk_no) { hdmitx_device.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 */ } byte_num += 8; } if (hdmi_edid_parsing(hdev->rawedid, &hdev->RXCap) == 0) { dump_full_edid(hdev->rawedid); } para = hdmi_get_fmt_paras(hdev->RXCap.preferred_mode); if (para) { sprintf(pref_mode, "setenv hdmimode %s", para->sname); if (hdev->RXCap.pref_colorspace & (1 << 5)) sprintf(color_attr, "setenv colorattribute %s", "444,8bit"); else if (hdev->RXCap.pref_colorspace & (1 << 4)) sprintf(color_attr, "setenv colorattribute %s", "422,8bit"); else sprintf(color_attr, "setenv colorattribute %s", "rgb,8bit"); } else { /* set default mode */ hdev->RXCap.preferred_mode = HDMI_720x480p60_16x9; para = hdmi_get_fmt_paras(HDMI_720x480p60_16x9); sprintf(pref_mode, "setenv hdmimode %s", para->sname); sprintf(color_attr, "setenv colorattribute %s", "444,8bit"); } printk("edid preferred_mode is %s[%d]\n", para->sname, hdev->RXCap.preferred_mode); /* save to ENV */ run_command(pref_mode, 0); run_command(color_attr, 0); run_command("saveenv", 0); return 0; } static cmd_tbl_t cmd_hdmi_sub[] = { U_BOOT_CMD_MKENT(hpd, 1, 1, do_hpd_detect, "", ""), U_BOOT_CMD_MKENT(edid, 1, 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_preferred_mode, 1, 1, do_get_preferred_mode, "", ""), U_BOOT_CMD_MKENT(mode, 1, 1, do_mode, "", ""), }; 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", "hdmitx hpd\n" " Detect hdmi rx plug-in\n" "hdmitx get_preferred_mode\n" " Read full edid data, parse edid, and get preferred mode\n" "hdmitx edid\n" " Read hdmi edid full raw data\n" "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" "hdmitx mode [hdmi/dvi]\n" " set vout mode : hdmi or dvi\n" );