summaryrefslogtreecommitdiff
path: root/drivers/video/wmt/parse-edid.c
diff options
context:
space:
mode:
authorSrikant Patnaik2015-01-11 12:28:04 +0530
committerSrikant Patnaik2015-01-11 12:28:04 +0530
commit871480933a1c28f8a9fed4c4d34d06c439a7a422 (patch)
tree8718f573808810c2a1e8cb8fb6ac469093ca2784 /drivers/video/wmt/parse-edid.c
parent9d40ac5867b9aefe0722bc1f110b965ff294d30d (diff)
downloadFOSSEE-netbook-kernel-source-871480933a1c28f8a9fed4c4d34d06c439a7a422.tar.gz
FOSSEE-netbook-kernel-source-871480933a1c28f8a9fed4c4d34d06c439a7a422.tar.bz2
FOSSEE-netbook-kernel-source-871480933a1c28f8a9fed4c4d34d06c439a7a422.zip
Moved, renamed, and deleted files
The original directory structure was scattered and unorganized. Changes are basically to make it look like kernel structure.
Diffstat (limited to 'drivers/video/wmt/parse-edid.c')
-rwxr-xr-xdrivers/video/wmt/parse-edid.c1069
1 files changed, 1069 insertions, 0 deletions
diff --git a/drivers/video/wmt/parse-edid.c b/drivers/video/wmt/parse-edid.c
new file mode 100755
index 00000000..8bf8da74
--- /dev/null
+++ b/drivers/video/wmt/parse-edid.c
@@ -0,0 +1,1069 @@
+/*++
+ * linux/drivers/video/wmt/parse-edid.c
+ * WonderMedia video post processor (VPP) driver
+ *
+ * Copyright c 2014 WonderMedia Technologies, Inc.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ *
+ * WonderMedia Technologies, Inc.
+ * 4F, 533, Chung-Cheng Road, Hsin-Tien, Taipei 231, R.O.C
+--*/
+
+#define PARSE_EDID_C
+#undef DEBUG
+/* #define DEBUG */
+/* #define DEBUG_DETAIL */
+
+#include "vpp.h"
+#include "edid.h"
+#include "hdmi.h"
+
+const char edid_v1_header[] = {
+ 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00};
+
+const char edid_v1_descriptor_flag[] = { 0x00, 0x00 };
+
+#define COMBINE_HI_8LO(hi, lo) \
+ ((((unsigned)hi) << 8) | (unsigned)lo)
+
+#define COMBINE_HI_4LO(hi, lo) \
+ ((((unsigned)hi) << 4) | (unsigned)lo)
+
+#define UPPER_NIBBLE(x) \
+ (((128|64|32|16) & (x)) >> 4)
+
+#define LOWER_NIBBLE(x) \
+ ((1|2|4|8) & (x))
+
+#define MONITOR_NAME 0xfc
+#define MONITOR_LIMITS 0xfd
+#define UNKNOWN_DESCRIPTOR -1
+#define DETAILED_TIMING_BLOCK -2
+
+struct edid_timing_t edid_establish_timing[] = {
+ { 800, 600, 60 }, { 800, 600, 56 }, { 640, 480, 75 }, { 640, 480, 72 },
+ { 640, 480, 67 }, { 640, 480, 60 }, { 720, 400, 88 }, { 720, 400, 70 },
+ { 1280, 1024, 75 }, { 1024, 768, 75 }, { 1024, 768, 70 },
+ { 1024, 768, 60 }, { 1024, 768, 87 }, { 832, 624, 75 },
+ { 800, 600, 75 }, { 800, 600, 72 }, { 1152, 870, 75 }
+};
+int edid_msg_enable;
+int edid_disable;
+struct edid_parsed_t edid_parsed;
+
+#undef DBGMSG
+#define DBGMSG(fmt, args...) if (edid_msg_enable) \
+ DPRINT(fmt, ## args)
+
+static int block_type(char *block)
+{
+ if (!memcmp(edid_v1_descriptor_flag, block, 2)) {
+ /* descriptor */
+ if (block[2] != 0)
+ return UNKNOWN_DESCRIPTOR;
+ return block[3];
+ }
+ /* detailed timing block */
+ return DETAILED_TIMING_BLOCK;
+} /* End of block_type() */
+
+static char *get_vendor_sign(char *block, char *sign)
+{
+ unsigned short h;
+
+ /*
+ 08h WORD big-endian manufacturer ID (see #00136)
+ bits 14-10: first letter (01h='A', 02h='B', etc.)
+ bits 9-5: second letter
+ bits 4-0: third letter
+ */
+ h = COMBINE_HI_8LO(block[0], block[1]);
+ sign[0] = ((h >> 10) & 0x1f) + 'A' - 1;
+ sign[1] = ((h >> 5) & 0x1f) + 'A' - 1;
+ sign[2] = (h & 0x1f) + 'A' - 1;
+ sign[3] = 0;
+ return sign;
+} /* End of get_vendor_sign() */
+
+static char *get_monitor_name(char *block, char *name)
+{
+#define DESCRIPTOR_DATA 5
+
+ char *ptr = block + DESCRIPTOR_DATA;
+ unsigned i;
+
+ for (i = 0; i < 13; i++, ptr++) {
+ if (*ptr == 0xa) {
+ name[i] = 0;
+ return name;
+ }
+ name[i] = *ptr;
+ }
+ return name;
+} /* End of get_monitor_name() */
+
+static int parse_timing_description(char *dtd, struct edid_info_t *info)
+{
+#define PIXEL_CLOCK_LO ((unsigned)dtd[0])
+#define PIXEL_CLOCK_HI ((unsigned)dtd[1])
+#define PIXEL_CLOCK (COMBINE_HI_8LO(PIXEL_CLOCK_HI, PIXEL_CLOCK_LO) * 10000)
+#define H_ACTIVE_LO ((unsigned)dtd[2])
+#define H_BLANKING_LO ((unsigned)dtd[3])
+#define H_ACTIVE_HI UPPER_NIBBLE((unsigned)dtd[4])
+#define H_ACTIVE COMBINE_HI_8LO(H_ACTIVE_HI, H_ACTIVE_LO)
+#define H_BLANKING_HI LOWER_NIBBLE((unsigned)dtd[4])
+#define H_BLANKING COMBINE_HI_8LO(H_BLANKING_HI, H_BLANKING_LO)
+#define V_ACTIVE_LO ((unsigned)dtd[5])
+#define V_BLANKING_LO ((unsigned)dtd[6])
+#define V_ACTIVE_HI UPPER_NIBBLE((unsigned)dtd[7])
+#define V_ACTIVE COMBINE_HI_8LO(V_ACTIVE_HI, V_ACTIVE_LO)
+#define V_BLANKING_HI LOWER_NIBBLE((unsigned)dtd[7])
+#define V_BLANKING COMBINE_HI_8LO(V_BLANKING_HI, V_BLANKING_LO)
+#define H_SYNC_OFFSET_LO ((unsigned)dtd[8])
+#define H_SYNC_WIDTH_LO ((unsigned)dtd[9])
+#define V_SYNC_OFFSET_LO UPPER_NIBBLE((unsigned)dtd[10])
+#define V_SYNC_WIDTH_LO LOWER_NIBBLE((unsigned)dtd[10])
+#define V_SYNC_WIDTH_HI ((unsigned)dtd[11] & (1|2))
+#define V_SYNC_OFFSET_HI (((unsigned)dtd[11] & (4|8)) >> 2)
+#define H_SYNC_WIDTH_HI (((unsigned)dtd[11] & (16|32)) >> 4)
+#define H_SYNC_OFFSET_HI (((unsigned)dtd[11] & (64|128)) >> 6)
+#define V_SYNC_WIDTH COMBINE_HI_4LO(V_SYNC_WIDTH_HI, V_SYNC_WIDTH_LO)
+#define V_SYNC_OFFSET COMBINE_HI_4LO(V_SYNC_OFFSET_HI, V_SYNC_OFFSET_LO)
+#define H_SYNC_WIDTH COMBINE_HI_4LO(H_SYNC_WIDTH_HI, H_SYNC_WIDTH_LO)
+#define H_SYNC_OFFSET COMBINE_HI_4LO(H_SYNC_OFFSET_HI, H_SYNC_OFFSET_LO)
+#define H_SIZE_LO ((unsigned)dtd[12])
+#define V_SIZE_LO ((unsigned)dtd[13])
+#define H_SIZE_HI UPPER_NIBBLE((unsigned)dtd[14])
+#define V_SIZE_HI LOWER_NIBBLE((unsigned)dtd[14])
+#define H_SIZE COMBINE_HI_8LO(H_SIZE_HI, H_SIZE_LO)
+#define V_SIZE COMBINE_HI_8LO(V_SIZE_HI, V_SIZE_LO)
+#define H_BORDER ((unsigned)dtd[15])
+#define V_BORDER ((unsigned)dtd[16])
+#define FLAGS ((unsigned) dtd[17])
+#define INTERLACED (FLAGS & 128)
+#define SYNC_TYPE (FLAGS & 3 << 3) /* bits 4,3 */
+#define SYNC_SEPARATE (3 << 3)
+#define HSYNC_POSITIVE (FLAGS & 4)
+#define VSYNC_POSITIVE (FLAGS & 2)
+
+ int htotal, vtotal;
+ int i;
+ struct fb_videomode *t;
+ int fps;
+ int vmul;
+
+ htotal = H_ACTIVE + H_BLANKING;
+ vtotal = V_ACTIVE + V_BLANKING;
+
+ DBGMSG("Detail Timing: \"%dx%d\"\n", H_ACTIVE, V_ACTIVE);
+ DBGMSG("\tVfreq %dHz, Hfreq %dkHz\n",
+ PIXEL_CLOCK / (vtotal * htotal),
+ PIXEL_CLOCK / (htotal * 1000));
+ DBGMSG("\tDotClock\t%d\n", PIXEL_CLOCK / 1000000);
+ DBGMSG("\tHTimings\t%u %u %u %u\n", H_ACTIVE,
+ H_ACTIVE + H_SYNC_OFFSET,
+ H_ACTIVE + H_SYNC_OFFSET + H_SYNC_WIDTH,
+ htotal);
+
+ DBGMSG("\tVTimings\t%u %u %u %u\n", V_ACTIVE,
+ V_ACTIVE + V_SYNC_OFFSET,
+ V_ACTIVE + V_SYNC_OFFSET + V_SYNC_WIDTH,
+ vtotal);
+
+ if (INTERLACED || (SYNC_TYPE == SYNC_SEPARATE)) {
+ DBGMSG("\tFlags\t%s\"%sHSync\" \"%sVSync\"\n",
+ INTERLACED ? "\"Interlace\" " : "",
+ HSYNC_POSITIVE ? "+" : "-",
+ VSYNC_POSITIVE ? "+" : "-");
+ }
+
+ for (i = 0; i < 4; i++) {
+ t = &info->detail_timing[i];
+ if (t->pixclock == 0)
+ break;
+ }
+
+ if (i >= 4) {
+ DBGMSG("*W* slot full\n");
+ return 0;
+ }
+
+ t->pixclock = KHZ2PICOS(PIXEL_CLOCK / 1000);
+ t->right_margin = H_SYNC_OFFSET;
+ t->hsync_len = H_SYNC_WIDTH;
+ t->xres = H_ACTIVE;
+ t->left_margin = H_BLANKING - (H_SYNC_WIDTH + H_SYNC_OFFSET);
+ t->vmode = INTERLACED ? FB_VMODE_INTERLACED : FB_VMODE_NONINTERLACED;
+ vmul = (INTERLACED) ? 2 : 1;
+ t->lower_margin = V_SYNC_OFFSET * vmul;
+ t->vsync_len = V_SYNC_WIDTH * vmul;
+ t->yres = V_ACTIVE * vmul;
+ t->upper_margin = V_BLANKING - (V_SYNC_WIDTH + V_SYNC_OFFSET);
+
+ fps = vpp_calc_refresh(PIXEL_CLOCK, htotal, vtotal);
+ t->refresh = fps;
+ t->sync = HSYNC_POSITIVE ? (FB_SYNC_HOR_HIGH_ACT) : 0;
+ t->sync |= VSYNC_POSITIVE ? (FB_SYNC_VERT_HIGH_ACT) : 0;
+
+ if (vout_check_ratio_16_9(H_ACTIVE, V_ACTIVE))
+ info->option |= EDID_OPT_16_9;
+#if 0
+ DBGMSG("%dx%d,%d,%s\n", H_ACTIVE, V_ACTIVE, t->pixel_clock,
+ (info->option & EDID_OPT_16_9) ? "16:9" : "4:3");
+#endif
+ return 0;
+} /* End of parse_timing_description() */
+
+static int parse_dpms_capabilities(char flags)
+{
+#define DPMS_ACTIVE_OFF (1 << 5)
+#define DPMS_SUSPEND (1 << 6)
+#define DPMS_STANDBY (1 << 7)
+
+ DBGMSG("# DPMS capabilities: Active off:%s Suspend:%s Standby:%s\n\n",
+ (flags & DPMS_ACTIVE_OFF) ? "yes" : "no",
+ (flags & DPMS_SUSPEND) ? "yes" : "no",
+ (flags & DPMS_STANDBY) ? "yes" : "no");
+ return 0;
+} /* End of parse_dpms_capabilities() */
+
+static int parse_monitor_limits(char *block, struct edid_info_t *info)
+{
+#define V_MIN_RATE block[5]
+#define V_MAX_RATE block[6]
+#define H_MIN_RATE block[7]
+#define H_MAX_RATE block[8]
+#define MAX_PIXEL_CLOCK (((int)block[9]) * 10)
+#define GTF_SUPPORT block[10]
+
+ DBGMSG("Monitor limit\n");
+ DBGMSG("\tHorizontal Frequency: %u-%u Hz\n", H_MIN_RATE, H_MAX_RATE);
+ DBGMSG("\tVertical Frequency: %u-%u kHz\n", V_MIN_RATE, V_MAX_RATE);
+ if (MAX_PIXEL_CLOCK == 10 * 0xff) {
+ DBGMSG("\tMax dot clock not given\n");
+ } else {
+ DBGMSG("\tMax dot clock (video bandwidth) %u MHz\n",
+ (int)MAX_PIXEL_CLOCK);
+ info->pixel_clock_limit = MAX_PIXEL_CLOCK;
+ }
+
+ if (GTF_SUPPORT)
+ DBGMSG("\tEDID version 3 GTF given: contact author\n");
+ return 0;
+} /* End of parse_monitor_limits() */
+
+static int get_established_timing(char *edid, struct edid_info_t *info)
+{
+ char time_1, time_2;
+
+ time_1 = edid[ESTABLISHED_TIMING_I];
+ time_2 = edid[ESTABLISHED_TIMING_II];
+ info->establish_timing = time_1 + (time_2 << 8);
+
+ /*---------------------------------------------------------------------
+ 35: ESTABLISHED TIMING I
+ bit 7-0: 720x400@70 Hz, 720x400@88 Hz, 640x480@60 Hz, 640x480@67 Hz,
+ 640x480@72 Hz, 640x480@75 Hz, 800x600@56 Hz, 800x600@60 Hz
+ ---------------------------------------------------------------------*/
+ DBGMSG("Established Timimgs I: 0x%x\n", time_1);
+ if (time_1 & 0x80)
+ DBGMSG("\t%dx%d@%dHz\n", 720, 400, 70);
+ if (time_1 & 0x40)
+ DBGMSG("\t%dx%d@%dHz\n", 720, 400, 88);
+ if (time_1 & 0x20)
+ DBGMSG("\t%dx%d@%dHz\n", 640, 480, 60);
+ if (time_1 & 0x10)
+ DBGMSG("\t%dx%d@%dHz\n", 640, 480, 67);
+ if (time_1 & 0x08)
+ DBGMSG("\t%dx%d@%dHz\n", 640, 480, 72);
+ if (time_1 & 0x04)
+ DBGMSG("\t%dx%d@%dHz\n", 640, 480, 75);
+ if (time_1 & 0x02)
+ DBGMSG("\t%dx%d@%dHz\n", 800, 600, 56);
+ if (time_1 & 0x01)
+ DBGMSG("\t%dx%d@%dHz\n", 800, 600, 60);
+
+ /*---------------------------------------------------------------------
+ 36: ESTABLISHED TIMING II
+ bit 7-0: 800x600@72 Hz, 800x600@75 Hz, 832x624@75 Hz,
+ 1024x768@87 Hz (Interlaced), 1024x768@60 Hz, 1024x768@70 Hz,
+ 1024x768@75 Hz, 1280x1024@75 Hz
+ ---------------------------------------------------------------------*/
+ DBGMSG("Established Timimgs II: 0x%x\n", time_2);
+ if (time_2 & 0x80)
+ DBGMSG("\t%dx%d@%dHz\n", 800, 600, 72);
+ if (time_2 & 0x40)
+ DBGMSG("\t%dx%d@%dHz\n", 800, 600, 75);
+ if (time_2 & 0x20)
+ DBGMSG("\t%dx%d@%dHz\n", 832, 624, 75);
+ if (time_2 & 0x10)
+ DBGMSG("\t%dx%d@%dHz (Interlace)\n", 1024, 768, 87);
+ if (time_2 & 0x08)
+ DBGMSG("\t%dx%d@%dHz\n", 1024, 768, 60);
+ if (time_2 & 0x04)
+ DBGMSG("\t%dx%d@%dHz\n", 1024, 768, 70);
+ if (time_2 & 0x02)
+ DBGMSG("\t%dx%d@%dHz\n", 1024, 768, 75);
+ if (time_2 & 0x01)
+ DBGMSG("\t%dx%d@%dHz\n", 1280, 1024, 75);
+ return 0;
+} /* End of get_established_timing() */
+
+static int get_standard_timing(char *edid, struct edid_info_t *info)
+{
+ char *ptr = edid + STANDARD_TIMING_IDENTIFICATION_START;
+ int h_res, v_res, v_freq;
+ int byte_1, byte_2, aspect, i;
+
+ /*---------------------------------------------------------------------
+ First byte
+ Horizontal resolution. Multiply by 8, then add 248 for actual value.
+ Second byte
+ bit 7-6: Aspect ratio. Actual vertical resolution depends on horizontal
+ resolution.
+ 00=16:10, 01=4:3, 10=5:4, 11=16:9 (00=1:1 prior to v1.3)
+ bit 5-0: Vertical frequency. Add 60 to get actual value.
+ ---------------------------------------------------------------------*/
+ DBGMSG("Standard Timing Identification\n");
+ for (i = 0; i < STANDARD_TIMING_IDENTIFICATION_SIZE / 2; i++) {
+ byte_1 = *ptr++;
+ byte_2 = *ptr++;
+ if ((byte_1 == 0x01) && (byte_2 == 0x01))
+ break;
+ h_res = (byte_1 * 8) + 248;
+ aspect = byte_2 & 0xC0;
+ switch (aspect) {
+ default:
+ case 0x00:
+ v_res = h_res * 10/16;
+ break;
+ case 0x40:
+ v_res = h_res * 3/4;
+ break;
+ case 0x80:
+ v_res = h_res * 4/5;
+ break;
+ case 0xC0:
+ v_res = h_res * 9/16;
+ break;
+ }
+ v_freq = (byte_2 & 0x1F) + 60;
+ DBGMSG("\t%dx%d@%dHz\n", h_res, v_res, v_freq);
+ info->standard_timing[i].resx = h_res;
+ info->standard_timing[i].resy = v_res;
+ info->standard_timing[i].freq = v_freq;
+ }
+ return 0;
+} /* End of get_standard_timing() */
+
+static int edid_parse_v1(char *edid, struct edid_info_t *info)
+{
+ char *block;
+ char *monitor_name = 0;
+ char monitor_alt_name[100];
+ char vendor_sign[4];
+ int i, ret = 0;
+
+ if (edid_checksum(edid, EDID_LENGTH)) {
+ DBG_ERR("checksum failed\n");
+ ret = -1;
+ goto parse_end;
+ }
+
+ if (memcmp(edid+EDID_HEADER, edid_v1_header, EDID_HEADER_END+1)) {
+ DBGMSG("*E* first bytes don't match EDID version 1 header\n");
+ ret = -1;
+ goto parse_end;
+ }
+
+ if(edid_msg_enable)
+ edid_dump(edid);
+
+ DBGMSG("[EDID] EDID version: %d.%d\n",
+ (int)edid[EDID_STRUCT_VERSION],
+ (int)edid[EDID_STRUCT_REVISION]);
+
+ get_vendor_sign(edid + ID_MANUFACTURER_NAME, (char *) &vendor_sign);
+
+ info->width = edid[EDID_MAX_HOR_IMAGE_SIZE] * 10;
+ info->height = edid[EDID_MAX_VER_IMAGE_SIZE] * 10;
+ DBGMSG("[EDID] max hor %d cm ver %d cm\n", info->width, info->height);
+
+ /*---------------------------------------------------------------------
+ Parse Monitor name
+ ---------------------------------------------------------------------*/
+ block = edid + DETAILED_TIMING_DESCRIPTIONS_START;
+ for (i = 0; i < NO_DETAILED_TIMING_DESCRIPTIONS; i++,
+ block += DETAILED_TIMING_DESCRIPTION_SIZE) {
+ if (block_type(block) == MONITOR_NAME) {
+ monitor_name =
+ get_monitor_name(block, monitor_alt_name);
+ break;
+ }
+ }
+
+ if (!monitor_name) {
+ /* Stupid djgpp hasn't snDBGMSG so we have to
+ hack something together */
+ if (strlen(vendor_sign) + 10 > sizeof(monitor_alt_name))
+ vendor_sign[3] = 0;
+
+ sprintf(monitor_alt_name, "%s:%02x%02x",
+ vendor_sign, edid[ID_MODEL], edid[ID_MODEL+1]);
+ monitor_name = monitor_alt_name;
+ }
+
+ DBGMSG("Identifier \"%s\"\n", monitor_name);
+ DBGMSG("VendorName \"%s\"\n", vendor_sign);
+ DBGMSG("ModelName \"%s\"\n", monitor_name);
+
+ memset(edid_parsed.tv_name.vendor_name, 0, sizeof(edid_parsed.tv_name.vendor_name));
+ strcpy(edid_parsed.tv_name.vendor_name, vendor_sign);
+
+ memset(edid_parsed.tv_name.monitor_name, 0, sizeof(edid_parsed.tv_name.monitor_name));
+ if (strlen(monitor_name) < MONITOR_NAME_LEN)
+ strcpy(edid_parsed.tv_name.monitor_name, monitor_name);
+ else
+ strncpy(edid_parsed.tv_name.monitor_name, monitor_name, MONITOR_NAME_LEN - 1);
+
+ parse_dpms_capabilities(edid[DPMS_FLAGS]);
+
+ /*---------------------------------------------------------------------
+ Parse ESTABLISHED TIMING I and II
+ ---------------------------------------------------------------------*/
+ get_established_timing(edid, info);
+
+ /*---------------------------------------------------------------------
+ Parse STANDARD TIMING IDENTIFICATION
+ ---------------------------------------------------------------------*/
+ get_standard_timing(edid, info);
+
+ block = edid + DETAILED_TIMING_DESCRIPTIONS_START;
+ for (i = 0; i < NO_DETAILED_TIMING_DESCRIPTIONS; i++,
+ block += DETAILED_TIMING_DESCRIPTION_SIZE) {
+ if (block_type(block) == MONITOR_LIMITS)
+ parse_monitor_limits(block, info);
+ }
+
+ block = edid + DETAILED_TIMING_DESCRIPTIONS_START;
+ for (i = 0; i < NO_DETAILED_TIMING_DESCRIPTIONS; i++,
+ block += DETAILED_TIMING_DESCRIPTION_SIZE) {
+ if (block_type(block) == DETAILED_TIMING_BLOCK)
+ parse_timing_description(block, info);
+ }
+parse_end:
+ return ret;
+}
+
+void edid_parse_CEA_VendorSpecDataBlock(char *block, int len,
+ struct edid_info_t *info)
+{
+ int index;
+ char temp;
+
+ DBGMSG("Vendor Spec Data Block\n");
+ if (len < 5) /* min size */
+ return;
+ /* IEEE Registration Identifier 0x000C03 */
+ if ((block[1] == 0x03) && (block[2] == 0x0C) &&
+ (block[3] == 0x0)) {
+ info->option |= EDID_OPT_HDMI;
+ DBGMSG("\t support HDMI\n");
+ }
+ DBGMSG("\t Source Physical Addr %d.%d.%d.%d\n",
+ (block[4] & 0xF0) >> 4, block[4] & 0x0F,
+ (block[5] & 0xF0) >> 4, block[5] & 0x0F);
+ info->hdmi_phy_addr = (block[4] << 8) + block[5];
+
+ /* extersion fields */
+ if (len < 8)
+ return;
+ DBGMSG("\t%s support AI\n",
+ (block[6] & 0x80) ? "" : "no");
+ DBGMSG("\t%s support 30 bits/pixel(10 bits/color)\n",
+ (block[6] & 0x40) ? "" : "no");
+ DBGMSG("\t%s support 36 bits/pixel(12 bits/color)\n",
+ (block[6] & 0x20) ? "" : "no");
+ DBGMSG("\t%s support 48 bits/pixel(16 bits/color)\n",
+ (block[6] & 0x10) ? "" : "no");
+ DBGMSG("\t%s support YUV444 in Deep Color mode\n",
+ (block[6] & 0x08) ? "" : "no");
+ DBGMSG("\t%s support DVI dual-link\n",
+ (block[6] & 0x01) ? "" : "no");
+ DBGMSG("\tMax TMDS Clock %d MHz\n", block[7] * 5);
+ temp = block[8];
+ index = 9;
+
+ if (temp & BIT(7)) {
+ DBGMSG("\tVideo Latency %d,Audio Latency %d\n",
+ block[index], block[index+1]);
+ index += 2;
+ }
+
+ if (temp & BIT(6)) {
+ DBGMSG("\tInterlaced Video Latency %d,Audio Latency %d\n",
+ block[index], block[index+1]);
+ index += 2;
+ }
+
+ if (temp & BIT(5)) {
+ int hdmi_xx_len, hdmi_3d_len;
+
+ DBGMSG("\tHDMI Video present\n");
+ temp = block[index];
+ if (temp & 0x80)
+ DBGMSG("\t\t3D present support Mandatory formats\n");
+ if (temp & 0x60)
+ DBGMSG("\t\t3D Multi present %d\n", (temp & 0x60) >> 5);
+ DBGMSG("\t\tImage size %d\n", (temp & 0x18) >> 3);
+ hdmi_xx_len = (block[index + 1] & 0xE0) >> 5;
+ hdmi_3d_len = block[index + 1] & 0x1F;
+ DBGMSG("\t\thdmi_xx_len %d,hdmi_3d_len %d\n",
+ hdmi_xx_len, hdmi_3d_len);
+ index += 2;
+
+ if (hdmi_xx_len) {
+ /* Refer to HDMI Spec Ver 1.4a */
+ index += hdmi_xx_len;
+ }
+
+ if (hdmi_3d_len) {
+ int struct_all_3d = 0;
+ int mask_3d = 0xFF;
+ int vic_order_2d;
+ char struct_3d, detail_3d;
+ int i;
+
+ hdmi_3d_len += index;
+ switch (temp & 0x60) {
+ case 0x40:
+ struct_all_3d = (block[index] << 8) +
+ block[index + 1];
+ mask_3d = (block[index + 2] << 8) +
+ block[index + 3]; /* 3D support mask */
+ DBGMSG("\t\t3D struct 0x%x,mask 0x%x\n",
+ struct_all_3d, mask_3d);
+ index += 4;
+ break;
+ case 0x20:
+ struct_all_3d = (block[index] << 8) +
+ block[index + 1];
+ DBGMSG("\t\t3D struct 0x%x\n", struct_all_3d);
+ index += 2;
+ break;
+ default:
+ break;
+ }
+ /* 3D support type */
+ if (struct_all_3d & BIT0)
+ DBGMSG("\t\tSupport Frame packing\n");
+ if (struct_all_3d & BIT6)
+ DBGMSG("\t\tSupport Top and Bottom\n");
+ if (struct_all_3d & BIT8)
+ DBGMSG("\t\tSupport Side by Side\n");
+
+ for (i = 0; i < 16; i++) {
+ if (mask_3d & (0x1 << i))
+ info->vic_3d[i].mask = struct_all_3d;
+ }
+
+ DBGMSG("\t\t[3D Structure type 0-Frame packing");
+ DBGMSG(",6-Top and Bottom,8-Side by Side]\n");
+ while (index < hdmi_3d_len) {
+ vic_order_2d = (block[index] & 0xF0) >> 4;
+ struct_3d = block[index] & 0x0F;
+ index++;
+ if (struct_3d & 0x8) {
+ detail_3d = (block[index] & 0xF0) >> 4;
+ index++;
+ } else {
+ detail_3d = 0;
+ }
+ info->vic_3d[vic_order_2d].mask |=
+ (0x1 << struct_3d);
+ DBGMSG("\t\tVIC %d,3D struct %d,detail %d\n",
+ vic_order_2d, struct_3d, detail_3d);
+ }
+ }
+ }
+}
+
+static int edid_parse_CEA(char *edid, struct edid_info_t *info)
+{
+ char *block, *block_end;
+ char checksum = 0;
+ int i, len;
+ char audio_format;
+ int num = 0, sadcnt_in_block;
+
+ memset(edid_parsed.sad, 0, sizeof(edid_parsed.sad));
+
+ if (edid[0] != 0x2)
+ return -1;
+
+ for (i = 0; i < EDID_LENGTH; i++)
+ checksum += edid[i];
+
+ if (checksum != 0) {
+ DPRINT("*E* CEA EDID checksum (0x%02x) - data is corrupt\n",
+ checksum);
+ info->option |= (EDID_OPT_HDMI + EDID_OPT_AUDIO);
+ edid_dump(edid);
+ return -1;
+ }
+
+ if(edid_msg_enable)
+ edid_dump(edid);
+
+ DBGMSG("[EDID] CEA EDID Version %d.%d\n", edid[0], edid[1]);
+
+ if (edid[1] >= 2) {
+ info->option |= (edid[3] & 0xF0);
+ DBGMSG("\t%s support 422\n", (edid[3] & 0x10) ? "" : "no");
+ DBGMSG("\t%s support 444\n", (edid[3] & 0x20) ? "" : "no");
+ DBGMSG("\t%s support audio\n", (edid[3] & 0x40) ? "" : "no");
+ DBGMSG("\t%s support underscan\n",
+ (edid[3] & 0x80) ? "" : "no");
+ }
+
+ block_end = edid + edid[2];
+ block = edid + 4;
+ do {
+ len = block[0] & 0x1F;
+ switch (((block[0] & 0xE0) >> 5)) {
+ case 1: /* Audio Data Block */
+ DBGMSG("Audio Data Block (0x%02X)\n", block[0]);
+ info->option |= EDID_OPT_AUDIO;
+ sadcnt_in_block = len / 3;
+
+ for (i = 0; i < sadcnt_in_block; i++) {
+ if (num < AUD_SAD_NUM) {
+ edid_parsed.sad[num].flag = 1;
+ edid_parsed.sad[num].sad_byte[0] =
+ block[i * 3 + 1];
+ edid_parsed.sad[num].sad_byte[1] =
+ block[i * 3 + 2];
+ edid_parsed.sad[num].sad_byte[2] =
+ block[i * 3 + 3];
+ num++;
+ } else {
+ DPRINT("Lose SAD info:%02X %02X %02X\n",
+ block[i * 3 + 1],
+ block[i * 3 + 2],
+ block[i * 3 + 3]);
+ }
+
+ DBGMSG("\t ======== SDA %d ========\n", i);
+ DBGMSG("\t SDA Data: 0x%02X 0x%02X 0x%02X\n",
+ block[i * 3 + 1],
+ block[i * 3 + 2],
+ block[i * 3 + 3]);
+
+ audio_format = (block[i * 3 + 1] & 0x78) >> 3;
+ switch (audio_format) {
+ default:
+ case 0: /* reserved */
+ case 15:/* reserved */
+ DBGMSG("\t Reserved Audio Fmt\n");
+ break;
+ case 1: /* LPCM */
+ DBGMSG("\t Audio Fmt: LPCM\n");
+ break;
+ case 2: /* AC3 */
+ DBGMSG("\t Audio Fmt: AC3\n");
+ break;
+ case 3: /* MPEG1 */
+ DBGMSG("\t Audio Fmt: MPEG1\n");
+ break;
+ case 4: /* MP3 */
+ DBGMSG("\t Audio Fmt: MP3\n");
+ break;
+ case 5: /* MPEG2 */
+ DBGMSG("\t Audio Fmt: MPEG2\n");
+ break;
+ case 6: /* AAC */
+ DBGMSG("\t Audio Fmt: AAC\n");
+ break;
+ case 7: /* DTS */
+ DBGMSG("\t Audio Fmt: DTS\n");
+ break;
+ case 8: /* ATRAC */
+ DBGMSG("\t Audio Fmt: ATRAC\n");
+ break;
+ case 9: /* One bit audio */
+ DBGMSG("\t Audio Fmt: ONE BIT AUDIO\n");
+ break;
+ case 10:/* Dolby */
+ DBGMSG("\t Audio Fmt: DOLBY\n");
+ break;
+ case 11:/* DTS-HD */
+ DBGMSG("\t Audio Fmt: DTS-HD\n");
+ break;
+ case 12:/* MAT (MLP) */
+ DBGMSG("\t Audio Fmt: MAT\n");
+ break;
+ case 13:/* DST */
+ DBGMSG("\t Audio Fmt: DST\n");
+ break;
+ case 14:/* WMA Pro */
+ DBGMSG("\t Audio Fmt: WMA Pro\n");
+ break;
+ }
+
+ DBGMSG("\t Max channel: %d\n",
+ (block[i * 3 + 1] & 0x7) + 1);
+ DBGMSG("\t %s support 32 KHz\n",
+ (block[i * 3 + 2] & 0x1) ? "" : "no");
+ DBGMSG("\t %s support 44 KHz\n",
+ (block[i * 3 + 2] & 0x2) ? "" : "no");
+ DBGMSG("\t %s support 48 KHz\n",
+ (block[i * 3 + 2] & 0x4) ? "" : "no");
+ DBGMSG("\t %s support 88 KHz\n",
+ (block[i * 3 + 2] & 0x8) ? "" : "no");
+ DBGMSG("\t %s support 96 KHz\n",
+ (block[i * 3 + 2] & 0x10) ? "" : "no");
+ DBGMSG("\t %s support 176 KHz\n",
+ (block[i * 3 + 2] & 0x20) ? "" : "no");
+ DBGMSG("\t %s support 192 KHz\n",
+ (block[i * 3 + 2] & 0x40) ? "" : "no");
+ if (audio_format == 1) { /* For LPCM */
+ DBGMSG("\t %s support 16 bit\n",
+ (block[i * 3 + 3] & 0x1) ?
+ "" : "no");
+ DBGMSG("\t %s support 20 bit\n",
+ (block[i * 3 + 3] & 0x2) ?
+ "" : "no");
+ DBGMSG("\t %s support 24 bit\n",
+ (block[i * 3 + 3] & 0x4) ?
+ "" : "no");
+ } else if (audio_format >= 2 &&
+ audio_format <= 8) {
+ /* From AC3 to ATRAC */
+ DBGMSG("\t Max bitrate: %d kbit/s\n",
+ block[i * 3 + 3] * 8);
+ } else if (audio_format >= 9 &&
+ audio_format <= 14) {
+ /* From One-bit-audio to WMA Pro*/
+ DBGMSG("\t Audio Format Code:0x%02X\n",
+ block[i * 3 + 3]);
+ }
+ DBGMSG("\t ========================\n");
+ }
+ break;
+ case 2: /* Video Data Block */
+ DBGMSG("Video Data Block\n");
+ for (i = 0; i < len; i++) {
+ unsigned int vic;
+
+ vic = block[1 + i] & 0x7F;
+ info->cea_vic[vic / 8] |= (0x1 << (vic % 8));
+ if (i < 16)
+ info->vic_3d[i].vic = vic;
+ DBGMSG("\t %2d : VIC %2d %dx%d@%d%s %s\n", i,
+ vic, hdmi_vic_info[vic].resx,
+ hdmi_vic_info[vic].resy,
+ hdmi_vic_info[vic].freq,
+ (hdmi_vic_info[vic].option
+ & HDMI_VIC_INTERLACE) ? "I" : "P",
+ (hdmi_vic_info[vic].option
+ & HDMI_VIC_4x3) ? "4:3" : "16:9");
+ }
+ break;
+ case 3: /* Vendor Spec Data Block */
+ edid_parse_CEA_VendorSpecDataBlock(block, len, info);
+ break;
+ case 4: /* Speaker Allocation Data Block */
+ DBGMSG("Speaker Allocation Data Block\n");
+ break;
+ case 5: /* VESA DTC Data Block */
+ DBGMSG("VESA DTC Data Block\n");
+ break;
+ case 7: /* Use Extended Tag */
+ DBGMSG("Use Extended Tag\n");
+ break;
+ case 0: /* Reserved */
+ default:
+ len = 0;
+ break;
+ }
+ block += (1 + len);
+ } while (block < block_end);
+
+ {
+ struct fb_videomode *p;
+ unsigned int fps;
+
+ DBGMSG("Detail Timing\n");
+ block = edid + edid[2];
+ len = (128 - edid[2]) / 18;
+ for (i = 0; i < len; i++, block += 18) {
+ p = &info->cea_timing[i];
+ p->pixclock = ((block[1] << 8) + block[0]) * 10000;
+ if (p->pixclock == 0)
+ break;
+ p->xres = ((block[4] & 0xF0) << 4) + block[2];
+ p->left_margin = ((block[4] & 0x0F) << 8) + block[3];
+ p->yres = ((block[7] & 0xF0) << 4) + block[5];
+ p->upper_margin = ((block[7] & 0x0F) << 8) + block[6];
+ fps = vpp_calc_refresh(p->pixclock, p->xres + p->left_margin,
+ p->yres + p->upper_margin);
+ p->right_margin = ((block[11] & 0xC0) << 2) + block[8];
+ p->hsync_len = ((block[11] & 0x30) << 4) + block[9];
+ p->left_margin = p->left_margin -
+ p->right_margin - p->hsync_len;
+ p->lower_margin = ((block[11] & 0x0C) << 2) +
+ ((block[10] & 0xF0) >> 4);
+ p->vsync_len = ((block[11] & 0x03) << 4) + (block[10] & 0x0F);
+ p->upper_margin = p->upper_margin -
+ p->lower_margin - p->vsync_len;
+ p->refresh = fps;
+ p->vmode = (block[17] & 0x80) ?
+ FB_VMODE_INTERLACED : FB_VMODE_NONINTERLACED;
+ p->sync = (block[17] & 0x04) ? FB_SYNC_VERT_HIGH_ACT : 0;
+ p->sync |= (block[17] & 0x02) ? FB_SYNC_HOR_HIGH_ACT : 0;
+ if (p->vmode & FB_VMODE_INTERLACED) {
+ p->yres *= 2;
+ p->upper_margin *= 2;
+ p->lower_margin *= 2;
+ p->vsync_len *= 2;
+ }
+ DBGMSG("\t%dx%d%s@%d,clk %d\n", p->xres, p->yres,
+ (block[17] & 0x80) ? "I" : "P", fps, p->pixclock);
+ DBGMSG("\t\tH bp %d,sync %d,fp %d\n",
+ p->left_margin, p->hsync_len, p->right_margin);
+ DBGMSG("\t\tV bp %d,sync %d,fp %d\n",
+ p->upper_margin, p->vsync_len, p->lower_margin);
+ DBGMSG("\t\thsync %d,vsync %d\n",
+ (p->sync & FB_SYNC_HOR_HIGH_ACT) ? 1 : 0,
+ (p->sync & FB_SYNC_VERT_HIGH_ACT) ? 1 : 0);
+ p->pixclock = KHZ2PICOS(p->pixclock / 1000);
+ }
+ }
+ return 0;
+}
+
+void edid_dump(char *edid)
+{
+ int i;
+
+ DPRINT("===================== EDID BlOCK =====================");
+ for (i = 0; i < 128; i++) {
+ if ((i % 16) == 0)
+ DPRINT("\n");
+ DPRINT("%02x ", edid[i]);
+ }
+ DPRINT("\n");
+ DPRINT("======================================================\n");
+}
+
+int edid_checksum(char *edid, int len)
+{
+ char checksum = 0;
+ int i;
+
+ for (i = 0; i < len; i++)
+ checksum += edid[i];
+
+ if (checksum) {
+#ifdef DEBUG
+ edid_dump(edid);
+#endif
+ }
+ return checksum;
+}
+
+int edid_parse(char *edid, struct edid_info_t *info)
+{
+ int ext_cnt = 0;
+
+ if (edid == 0)
+ return 0;
+
+ if (info->option & EDID_OPT_VALID) {
+ DBG_MSG("[EDID] parse exist\n");
+ return info->option;
+ }
+
+ DBG_MSG("[EDID] Enter\n");
+
+ memset(info, 0, sizeof(struct edid_info_t));
+ info->option = EDID_OPT_VALID;
+ if (edid_parse_v1(edid, info) == 0) {
+ ext_cnt = edid[0x7E];
+ if (ext_cnt >= EDID_BLOCK_MAX) {
+ DPRINT("[EDID] *W* ext block cnt %d\n", ext_cnt);
+ ext_cnt = EDID_BLOCK_MAX - 1;
+ }
+ } else {
+ info->option = 0;
+ return 0;
+ }
+
+ while (ext_cnt) {
+ edid += 128;
+ ext_cnt--;
+ if (edid_parse_CEA(edid, info) == 0)
+ continue;
+
+ DPRINT("*W* not support EDID\n");
+ edid_dump(edid);
+ }
+ return info->option;
+}
+
+int edid_find_support(struct edid_info_t *info, unsigned int resx,
+ unsigned int resy, int freq, struct fb_videomode **vmode)
+{
+ struct fb_videomode *p;
+ int temp;
+ int ret;
+ int i;
+
+ ret = 0;
+ *vmode = 0;
+
+ if (!info)
+ return 0;
+
+ /* find detail timing */
+ for (i = 0; i < 4; i++) {
+ p = &info->detail_timing[i];
+ if (p->pixclock == 0)
+ continue;
+ if (resx != p->xres)
+ continue;
+ if (resy != p->yres)
+ continue;
+
+ temp = p->refresh;
+ temp |= (p->vmode & FB_VMODE_INTERLACED) ?
+ EDID_TMR_INTERLACE : 0;
+ if (freq != temp)
+ continue;
+ *vmode = p;
+ ret = 3;
+ goto find_end;
+ }
+
+ /* find cea timing */
+ for (i = 0; i < 6; i++) {
+ p = &info->cea_timing[i];
+
+ if (p->pixclock == 0)
+ continue;
+ if (resx != p->xres)
+ continue;
+ if (resy != p->yres)
+ continue;
+
+ temp = p->refresh;
+ temp |= (p->vmode & FB_VMODE_INTERLACED) ?
+ EDID_TMR_INTERLACE : 0;
+ if (freq != temp)
+ continue;
+
+ *vmode = p;
+ ret = 4;
+ goto find_end;
+ }
+
+ /* find vic timing */
+ for (i = 0; i < 64; i++) {
+ if ((info->cea_vic[i / 8] & (0x1 << (i % 8))) == 0)
+ continue;
+
+ if (i >= HDMI_VIDEO_CODE_MAX)
+ continue;
+
+ if (resx != hdmi_vic_info[i].resx)
+ continue;
+ if (resy != hdmi_vic_info[i].resy)
+ continue;
+ temp = hdmi_vic_info[i].freq;
+ temp |= (hdmi_vic_info[i].option & HDMI_VIC_INTERLACE) ?
+ EDID_TMR_INTERLACE : 0;
+ if (freq != temp)
+ continue;
+ ret = 5;
+ goto find_end;
+ }
+
+ /* find established timing */
+ if (info->establish_timing) {
+ for (i = 0; i < 17; i++) {
+ if (info->establish_timing & (0x1 << i)) {
+ if ((resx == edid_establish_timing[i].resx) &&
+ (resy == edid_establish_timing[i].resy)) {
+ if (freq ==
+ edid_establish_timing[i].freq) {
+ ret = 1;
+ goto find_end;
+ }
+ }
+ }
+ }
+ }
+
+ /* find standard timing */
+ for (i = 0; i < 8; i++) {
+ if (info->standard_timing[i].resx == 0)
+ continue;
+ if ((resx == info->standard_timing[i].resx) &&
+ (resy == info->standard_timing[i].resy)) {
+ if (freq == info->standard_timing[i].freq) {
+ ret = 2;
+ goto find_end;
+ }
+ }
+ }
+
+find_end:
+#if 0
+ if ((resx == 1920) && (resy == 1080) && !(freq & EDID_TMR_INTERLACE))
+ ret = 0;
+#endif
+#if 0
+ if (!(freq & EDID_TMR_INTERLACE))
+ ret = 0;
+#endif
+#if 0
+ DPRINT("[EDID] %s support %dx%d@%d%s(ret %d)\n",
+ (ret) ? "" : "No", resx, resy, freq & EDID_TMR_FREQ,
+ (freq & EDID_TMR_INTERLACE) ? "I" : "P", ret);
+#endif
+ return ret;
+}
+
+unsigned int edid_get_hdmi_phy_addr(void)
+{
+ struct vout_t *vo;
+
+ vo = vout_get_entry(VPP_VOUT_NUM_HDMI);
+ return vo->edid_info.hdmi_phy_addr;
+}
+
+unsigned int edid_get_hdmi_3d_mask(struct edid_info_t *info, int vic)
+{
+ int i;
+
+ if (!info)
+ return 0;
+ for (i = 0; i < 16; i++) {
+ if (info->vic_3d[i].vic == vic)
+ return info->vic_3d[i].mask;
+ }
+ return 0;
+}
+