From patchwork Tue Mar 22 17:32:56 2011 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "K, Mythri P" X-Patchwork-Id: 653201 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by demeter1.kernel.org (8.14.4/8.14.3) with ESMTP id p2MHdTjb000415 for ; Tue, 22 Mar 2011 17:39:30 GMT Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1756514Ab1CVRj2 (ORCPT ); Tue, 22 Mar 2011 13:39:28 -0400 Received: from arroyo.ext.ti.com ([192.94.94.40]:36892 "EHLO arroyo.ext.ti.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1756504Ab1CVRj2 (ORCPT ); Tue, 22 Mar 2011 13:39:28 -0400 Received: from dbdp31.itg.ti.com ([172.24.170.98]) by arroyo.ext.ti.com (8.13.7/8.13.7) with ESMTP id p2MHdORJ028557 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-SHA bits=256 verify=NO); Tue, 22 Mar 2011 12:39:26 -0500 Received: from localhost.localdomain (localhost [127.0.0.1]) by dbdp31.itg.ti.com (8.13.8/8.13.8) with ESMTP id p2MHdMrw025637; Tue, 22 Mar 2011 23:09:23 +0530 (IST) From: Mythri P K To: linux-fbdev@vger.kernel.org, linux-omap@vger.kernel.org, linux-media@vger.kernel.org Cc: Mythri P K Subject: [RFC PATCH] HDMI:Support for EDID parsing in kernel. Date: Tue, 22 Mar 2011 23:02:56 +0530 Message-Id: <1300815176-21206-1-git-send-email-mythripk@ti.com> X-Mailer: git-send-email 1.5.6.3 Sender: linux-fbdev-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-fbdev@vger.kernel.org X-Greylist: IP, sender and recipient auto-whitelisted, not delayed by milter-greylist-4.2.6 (demeter1.kernel.org [140.211.167.41]); Tue, 22 Mar 2011 17:39:30 +0000 (UTC) diff --git a/arch/arm/include/asm/edid.h b/arch/arm/include/asm/edid.h new file mode 100644 index 0000000..843346a --- /dev/null +++ b/arch/arm/include/asm/edid.h @@ -0,0 +1,243 @@ +/* + * edid.h + * + * Copyright (C) 2011 Texas Instruments + * Author: Mythri P K + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * 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 . + * History: + */ + +#ifndef _EDID_H_ +#define _EDID_H_ + +/* HDMI EDID Length */ +#define HDMI_EDID_MAX_LENGTH 512 + +/* HDMI EDID Extension Data Block Tags */ +#define HDMI_EDID_EX_DATABLOCK_TAG_MASK 0xE0 +#define HDMI_EDID_EX_DATABLOCK_LEN_MASK 0x1F + +#define EDID_TIMING_DESCRIPTOR_SIZE 0x12 +#define EDID_DESCRIPTOR_BLOCK0_ADDRESS 0x36 +#define EDID_DESCRIPTOR_BLOCK1_ADDRESS 0x80 +#define EDID_SIZE_BLOCK0_TIMING_DESCRIPTOR 4 +#define EDID_SIZE_BLOCK1_TIMING_DESCRIPTOR 4 + +/* EDID Detailed Timing Info 0 begin offset */ +#define HDMI_EDID_DETAILED_TIMING_OFFSET 0x36 + +#define HDMI_EDID_PIX_CLK_OFFSET 0 +#define HDMI_EDID_H_ACTIVE_OFFSET 2 +#define HDMI_EDID_H_BLANKING_OFFSET 3 +#define HDMI_EDID_V_ACTIVE_OFFSET 5 +#define HDMI_EDID_V_BLANKING_OFFSET 6 +#define HDMI_EDID_H_SYNC_OFFSET 8 +#define HDMI_EDID_H_SYNC_PW_OFFSET 9 +#define HDMI_EDID_V_SYNC_OFFSET 10 +#define HDMI_EDID_V_SYNC_PW_OFFSET 11 +#define HDMI_EDID_H_IMAGE_SIZE_OFFSET 12 +#define HDMI_EDID_V_IMAGE_SIZE_OFFSET 13 +#define HDMI_EDID_H_BORDER_OFFSET 15 +#define HDMI_EDID_V_BORDER_OFFSET 16 +#define HDMI_EDID_FLAGS_OFFSET 17 + +/* HDMI EDID DTDs */ +#define HDMI_EDID_MAX_DTDS 4 + +/* HDMI EDID DTD Tags */ +#define HDMI_EDID_DTD_TAG_MONITOR_NAME 0xFC +#define HDMI_EDID_DTD_TAG_MONITOR_SERIALNUM 0xFF +#define HDMI_EDID_DTD_TAG_MONITOR_LIMITS 0xFD +#define HDMI_EDID_DTD_TAG_STANDARD_TIMING_DATA 0xFA +#define HDMI_EDID_DTD_TAG_COLOR_POINT_DATA 0xFB +#define HDMI_EDID_DTD_TAG_ASCII_STRING 0xFE + +#define HDMI_IMG_FORMAT_MAX_LENGTH 20 +#define HDMI_AUDIO_FORMAT_MAX_LENGTH 10 + +/* HDMI EDID Extenion Data Block Values: Video */ +#define HDMI_EDID_EX_VIDEO_NATIVE 0x80 +#define HDMI_EDID_EX_VIDEO_MASK 0x7F +#define HDMI_EDID_EX_VIDEO_MAX 35 + +#define STANDARD_HDMI_TIMINGS_NB 34 +#define STANDARD_HDMI_TIMINGS_VESA_START 15 + +#ifdef __cplusplus +extern "C" { +#endif + +enum extension_edid_db { + DATABLOCK_AUDIO = 1, + DATABLOCK_VIDEO = 2, + DATABLOCK_VENDOR = 3, + DATABLOCK_SPEAKERS = 4, +}; + +struct img_edid { + bool pref; + int code; +}; + +struct image_format { + int length; + struct img_edid fmt[HDMI_IMG_FORMAT_MAX_LENGTH]; +}; + +struct audio_edid { + int num_of_ch; + int format; +}; + +struct audio_format { + int length; + struct audio_edid fmt[HDMI_AUDIO_FORMAT_MAX_LENGTH]; +}; + +struct latency { + /* vid: if indicated, value=1+ms/2 with a max of 251 meaning 500ms */ + int vid_latency; + int aud_latency; + int int_vid_latency; + int int_aud_latency; +}; + +struct deep_color { + bool bit_30; + bool bit_36; + int max_tmds_freq; +}; + +/* Video Descriptor Block */ +struct HDMI_EDID_DTD_VIDEO { + u16 pixel_clock; /* 54-55 */ + u8 horiz_active; /* 56 */ + u8 horiz_blanking; /* 57 */ + u8 horiz_high; /* 58 */ + u8 vert_active; /* 59 */ + u8 vert_blanking; /* 60 */ + u8 vert_high; /* 61 */ + u8 horiz_sync_offset; /* 62 */ + u8 horiz_sync_pulse; /* 63 */ + u8 vert_sync_pulse; /* 64 */ + u8 sync_pulse_high; /* 65 */ + u8 horiz_image_size; /* 66 */ + u8 vert_image_size; /* 67 */ + u8 image_size_high; /* 68 */ + u8 horiz_border; /* 69 */ + u8 vert_border; /* 70 */ + u8 misc_settings; /* 71 */ +}; + +/* Monitor Limits Descriptor Block */ +struct HDMI_EDID_DTD_MONITOR { + u16 pixel_clock; /* 54-55*/ + u8 _reserved1; /* 56 */ + u8 block_type; /* 57 */ + u8 _reserved2; /* 58 */ + u8 min_vert_freq; /* 59 */ + u8 max_vert_freq; /* 60 */ + u8 min_horiz_freq; /* 61 */ + u8 max_horiz_freq; /* 62 */ + u8 pixel_clock_mhz; /* 63 */ + u8 GTF[2]; /* 64 -65 */ + u8 start_horiz_freq; /* 66 */ + u8 C; /* 67 */ + u8 M[2]; /* 68-69 */ + u8 K; /* 70 */ + u8 J; /* 71 */ + +} __packed; + +/* Text Descriptor Block */ +struct HDMI_EDID_DTD_TEXT { + u16 pixel_clock; /* 54-55 */ + u8 _reserved1; /* 56 */ + u8 block_type; /* 57 */ + u8 _reserved2; /* 58 */ + u8 text[13]; /* 59-71 */ +} __packed; + +/* DTD Union */ +union HDMI_EDID_DTD { + struct HDMI_EDID_DTD_VIDEO video; + struct HDMI_EDID_DTD_TEXT monitor_name; + struct HDMI_EDID_DTD_TEXT monitor_serial_number; + struct HDMI_EDID_DTD_TEXT ascii; + struct HDMI_EDID_DTD_MONITOR monitor_limits; +} __packed; + +/* EDID struct */ +struct HDMI_EDID { + u8 header[8]; /* 00-07 */ + u16 manufacturerID; /* 08-09 */ + u16 product_id; /* 10-11 */ + u32 serial_number; /* 12-15 */ + u8 week_manufactured; /* 16 */ + u8 year_manufactured; /* 17 */ + u8 edid_version; /* 18 */ + u8 edid_revision; /* 19 */ + u8 video_in_definition; /* 20 */ + u8 max_horiz_image_size; /* 21 */ + u8 max_vert_image_size; /* 22 */ + u8 display_gamma; /* 23 */ + u8 power_features; /* 24 */ + u8 chroma_info[10]; /* 25-34 */ + u8 timing_1; /* 35 */ + u8 timing_2; /* 36 */ + u8 timing_3; /* 37 */ + u8 std_timings[16]; /* 38-53 */ + union HDMI_EDID_DTD DTD[4]; /* 54-125 */ + u8 extension_edid; /* 126 */ + u8 checksum; /* 127 */ + u8 extension_tag; /* 00 (extensions follow EDID) */ + u8 extention_rev; /* 01 */ + u8 offset_dtd; /* 02 */ + u8 num_dtd; /* 03 */ + u8 data_block[123]; /* 04 - 126 */ + u8 extension_checksum; /* 127 */ + + u8 ext_datablock[256]; +} __packed; + +struct hdmi_timings { + + u16 x_res; + u16 y_res; + u32 pixel_clock; /* pixel clock in KHz */ + u16 hsw; /* Horizontal synchronization pulse width */ + u16 hfp; /* Horizontal front porch */ + u16 hbp; /* Horizontal back porch */ + u16 vsw; /* Vertical synchronization pulse width */ + u16 vfp; /* Vertical front porch */ + u16 vbp; /* Vertical back porch */ +}; + +int get_edid_timing_info(union HDMI_EDID_DTD *edid_dtd, + struct hdmi_timings *timings); +void get_eedid_timing_info(int current_descriptor_addrs, u8 *edid , + struct hdmi_timings *timings); +int hdmi_get_datablock_offset(u8 *edid, enum extension_edid_db datablock, + int *offset); +int hdmi_get_image_format(u8 *edid, struct image_format *format); +int hdmi_get_audio_format(u8 *edid, struct audio_format *format); +void hdmi_get_av_delay(u8 *edid, struct latency *lat); +void hdmi_deep_color_support_info(u8 *edid, struct deep_color *format); +bool hdmi_tv_yuv_supported(u8 *edid); + +#ifdef __cplusplus +}; +#endif + +#endif diff --git a/drivers/video/edid.c b/drivers/video/edid.c new file mode 100644 index 0000000..4eb2074 --- /dev/null +++ b/drivers/video/edid.c @@ -0,0 +1,340 @@ +/* + * edid.c + * + * Copyright (C) 2011 Texas Instruments + * Author: Mythri P K + * With EDID parsing for DVI Monitor from Rob Clark + * + * EDID.c to parse the EDID content. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * 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 . + * History: + * + */ + +#include +#include +#include +#include +#include + +/* Standard HDMI/VESA timings */ +const struct hdmi_timings standard_hdmi_timings[STANDARD_HDMI_TIMINGS_NB] = { + {640, 480, 25200, 96, 16, 48, 2, 10, 33}, + {1280, 720, 74250, 40, 440, 220, 5, 5, 20}, + {1280, 720, 74250, 40, 110, 220, 5, 5, 20}, + {720, 480, 27027, 62, 16, 60, 6, 9, 30}, + {2880, 576, 108000, 256, 48, 272, 5, 5, 39}, + {1440, 240, 27027, 124, 38, 114, 3, 4, 15}, + {1440, 288, 27000, 126, 24, 138, 3, 2, 19}, + {1920, 540, 74250, 44, 528, 148, 5, 2, 15}, + {1920, 540, 74250, 44, 88, 148, 5, 2, 15}, + {1920, 1080, 148500, 44, 88, 148, 5, 4, 36}, + {720, 576, 27000, 64, 12, 68, 5, 5, 39}, + {1440, 576, 54000, 128, 24, 136, 5, 5, 39}, + {1920, 1080, 148500, 44, 528, 148, 5, 4, 36}, + {2880, 480, 108108, 248, 64, 240, 6, 9, 30}, + {1920, 1080, 74250, 44, 638, 148, 5, 4, 36}, + /* Vesa frome here */ + {640, 480, 25175, 96, 16, 48, 2 , 11, 31}, + {800, 600, 40000, 128, 40, 88, 4 , 1, 23}, + {848, 480, 33750, 112, 16, 112, 8 , 6, 23}, + {1280, 768, 79500, 128, 64, 192, 7 , 3, 20}, + {1280, 800, 83500, 128, 72, 200, 6 , 3, 22}, + {1360, 768, 85500, 112, 64, 256, 6 , 3, 18}, + {1280, 960, 108000, 112, 96, 312, 3 , 1, 36}, + {1280, 1024, 108000, 112, 48, 248, 3 , 1, 38}, + {1024, 768, 65000, 136, 24, 160, 6, 3, 29}, + {1400, 1050, 121750, 144, 88, 232, 4, 3, 32}, + {1440, 900, 106500, 152, 80, 232, 6, 3, 25}, + {1680, 1050, 146250, 176 , 104, 280, 6, 3, 30}, + {1366, 768, 85500, 143, 70, 213, 3, 3, 24}, + {1920, 1080, 148500, 44, 88, 80, 5, 4, 36}, + {1280, 768, 68250, 32, 48, 80, 7, 3, 12}, + {1400, 1050, 101000, 32, 48, 80, 4, 3, 23}, + {1680, 1050, 119000, 32, 48, 80, 6, 3, 21}, + {1280, 800, 79500, 32, 48, 80, 6, 3, 14}, + {1280, 720, 74250, 40, 110, 220, 5, 5, 20} +}; + +int get_edid_timing_info(union HDMI_EDID_DTD *edid_dtd, + struct hdmi_timings *timings) +{ + if (edid_dtd->video.pixel_clock) { + struct HDMI_EDID_DTD_VIDEO *vid = &edid_dtd->video; + + timings->pixel_clock = 10 * vid->pixel_clock; + timings->x_res = vid->horiz_active | + (((u16)vid->horiz_high & 0xf0) << 4); + timings->y_res = vid->vert_active | + (((u16)vid->vert_high & 0xf0) << 4); + timings->hfp = vid->horiz_sync_offset | + (((u16)vid->sync_pulse_high & 0xc0) << 2); + timings->hsw = vid->horiz_sync_pulse | + (((u16)vid->sync_pulse_high & 0x30) << 4); + timings->hbp = (vid->horiz_blanking | + (((u16)vid->horiz_high & 0x0f) << 8)) - + (timings->hfp + timings->hsw); + timings->vfp = ((vid->vert_sync_pulse & 0xf0) >> 4) | + ((vid->sync_pulse_high & 0x0f) << 2); + timings->vsw = (vid->vert_sync_pulse & 0x0f) | + ((vid->sync_pulse_high & 0x03) << 4); + timings->vbp = (vid->vert_blanking | + (((u16)vid->vert_high & 0x0f) << 8)) - + (timings->vfp + timings->vsw); + return 0; + } + + switch (edid_dtd->monitor_name.block_type) { + case HDMI_EDID_DTD_TAG_STANDARD_TIMING_DATA: + printk(KERN_INFO "standard timing data\n"); + return -EINVAL; + case HDMI_EDID_DTD_TAG_COLOR_POINT_DATA: + printk(KERN_INFO "color point data\n"); + return -EINVAL; + case HDMI_EDID_DTD_TAG_MONITOR_NAME: + printk(KERN_INFO "monitor name: %s\n", + edid_dtd->monitor_name.text); + return -EINVAL; + case HDMI_EDID_DTD_TAG_MONITOR_LIMITS: + { + int i, max_area = 0, best_idx = -1; + struct HDMI_EDID_DTD_MONITOR *limits = + &edid_dtd->monitor_limits; + + printk(KERN_DEBUG " monitor limits\n"); + printk(KERN_DEBUG " min_vert_freq=%d\n", + limits->min_vert_freq); + printk(KERN_DEBUG " max_vert_freq=%d\n", + limits->max_vert_freq); + printk(KERN_DEBUG " min_horiz_freq=%d\n", + limits->min_horiz_freq); + printk(KERN_DEBUG " max_horiz_freq=%d\n", + limits->max_horiz_freq); + printk(KERN_DEBUG " pixel_clock_mhz=%d\n", + limits->pixel_clock_mhz * 10); + + /* find the highest matching resolution (w*h) */ + + /* + * XXX since this is mainly for DVI monitors, should we only + * support VESA timings? My monitor at home would pick + * 1920x1080 otherwise, but that seems to not work well (monitor + * blanks out and comes back, and picture doesn't fill full + * screen, but leaves a black bar on left (native res is + * 2048x1152). However if I only consider VESA timings, it picks + * 1680x1050 and the picture is stable and fills whole screen + */ + for (i = STANDARD_HDMI_TIMINGS_VESA_START; + i < STANDARD_HDMI_TIMINGS_NB; i++) { + const struct hdmi_timings *timings = + &standard_hdmi_timings[i]; + int hz, hscan, pixclock; + int vtotal, htotal; + htotal = timings->hbp + timings->hfp + + timings->hsw + timings->x_res; + vtotal = timings->vbp + timings->vfp + + timings->vsw + timings->y_res; + + /* NOTE: We don't support interlaced mode for VESA */ + pixclock = timings->pixel_clock * 1000; + hscan = (pixclock + htotal / 2) / htotal; + hscan = (hscan + 500) / 1000 * 1000; + hz = (hscan + vtotal / 2) / vtotal; + hscan /= 1000; + pixclock /= 1000000; + if ((pixclock < (limits->pixel_clock_mhz * 10)) && + (limits->min_horiz_freq <= hscan) && + (hscan <= limits->max_horiz_freq) && + (limits->min_vert_freq <= hz) && + (hz <= limits->max_vert_freq)) { + int area = timings->x_res * timings->y_res; + printk(KERN_INFO " -> %d: %dx%d\n", i, + timings->x_res, timings->y_res); + if (area > max_area) { + max_area = area; + best_idx = i; + } + } + } + if (best_idx > 0) { + *timings = standard_hdmi_timings[best_idx]; + printk(KERN_DEBUG "found best resolution: %dx%d (%d)\n", + timings->x_res, timings->y_res, best_idx); + } + return 0; + } + case HDMI_EDID_DTD_TAG_ASCII_STRING: + printk(KERN_INFO "ascii string: %s\n", edid_dtd->ascii.text); + return -EINVAL; + case HDMI_EDID_DTD_TAG_MONITOR_SERIALNUM: + printk(KERN_INFO "monitor serialnum: %s\n", + edid_dtd->monitor_serial_number.text); + return -EINVAL; + default: + printk(KERN_INFO "unsupported EDID descriptor block format\n"); + return -EINVAL; + } +} + +void get_eedid_timing_info(int current_descriptor_addrs, u8 *edid , + struct hdmi_timings *timings) +{ + timings->x_res = (((edid[current_descriptor_addrs + 4] & 0xF0) << 4) + | edid[current_descriptor_addrs + 2]); + timings->y_res = (((edid[current_descriptor_addrs + 7] & 0xF0) << 4) + | edid[current_descriptor_addrs + 5]); + timings->pixel_clock = ((edid[current_descriptor_addrs + 1] << 8) + | edid[current_descriptor_addrs]); + timings->pixel_clock = 10 * timings->pixel_clock; + timings->hfp = edid[current_descriptor_addrs + 8]; + timings->hsw = edid[current_descriptor_addrs + 9]; + timings->hbp = (((edid[current_descriptor_addrs + 4] & 0x0F) << 8) + | edid[current_descriptor_addrs + 3]) - + (timings->hfp + timings->hsw); + timings->vfp = ((edid[current_descriptor_addrs + 10] & 0xF0) >> 4); + timings->vsw = (edid[current_descriptor_addrs + 10] & 0x0F); + timings->vbp = (((edid[current_descriptor_addrs + 7] & 0x0F) << 8) + | edid[current_descriptor_addrs + 6]) - + (timings->vfp + timings->vsw); +} + +int hdmi_get_datablock_offset(u8 *edid, enum extension_edid_db datablock, + int *offset) +{ + int current_byte, disp, i = 0, length = 0; + + if (edid[0x7e] == 0x00) + return -EINVAL; + + disp = edid[(0x80) + 2]; + if (disp == 0x4) + return -EINVAL; + + i = 0x80 + 0x4; + printk(KERN_INFO "%x\n", i); + while (i < (0x80 + disp)) { + current_byte = edid[i]; + if ((current_byte >> 5) == datablock) { + *offset = i; + printk(KERN_INFO "datablock %d %d\n", + datablock, *offset); + return 0; + } else { + length = (current_byte & + HDMI_EDID_EX_DATABLOCK_LEN_MASK) + 1; + i += length; + } + } + return -EINVAL; +} + +int hdmi_get_image_format(u8 *edid, struct image_format *format) +{ + int offset, current_byte, j = 0, length = 0; + enum extension_edid_db vsdb = DATABLOCK_VIDEO; + format->length = 0; + + memset(format->fmt, 0, sizeof(format->fmt)); + if (!hdmi_get_datablock_offset(edid, vsdb, &offset)) { + current_byte = edid[offset]; + length = current_byte & HDMI_EDID_EX_DATABLOCK_LEN_MASK; + + if (length >= HDMI_IMG_FORMAT_MAX_LENGTH) + format->length = HDMI_IMG_FORMAT_MAX_LENGTH; + else + format->length = length; + + for (j = 1 ; j < length ; j++) { + current_byte = edid[offset+j]; + format->fmt[j-1].code = current_byte & 0x7F; + format->fmt[j-1].pref = current_byte & 0x80; + } + } + return 0; +} + +int hdmi_get_audio_format(u8 *edid, struct audio_format *format) +{ + int offset, current_byte, j = 0, length = 0; + enum extension_edid_db vsdb = DATABLOCK_AUDIO; + + format->length = 0; + memset(format->fmt, 0, sizeof(format->fmt)); + + if (!hdmi_get_datablock_offset(edid, vsdb, &offset)) { + current_byte = edid[offset]; + length = current_byte & HDMI_EDID_EX_DATABLOCK_LEN_MASK; + + if (length >= HDMI_AUDIO_FORMAT_MAX_LENGTH) + format->length = HDMI_AUDIO_FORMAT_MAX_LENGTH; + else + format->length = length; + + for (j = 1 ; j < length ; j++) { + if (j%3 == 1) { + current_byte = edid[offset + j]; + format->fmt[j-1].format = current_byte & 0x78; + format->fmt[j-1].num_of_ch = + (current_byte & 0x07) + 1; + } + } + } + return 0; +} + +void hdmi_get_av_delay(u8 *edid, struct latency *lat) +{ + int offset, current_byte, length = 0; + enum extension_edid_db vsdb = DATABLOCK_VENDOR; + + if (!hdmi_get_datablock_offset(edid, vsdb, &offset)) { + current_byte = edid[offset]; + length = current_byte & HDMI_EDID_EX_DATABLOCK_LEN_MASK; + if (length >= 8 && ((current_byte + 8) & 0x80)) { + lat->vid_latency = (edid[offset + 8] - 1) * 2; + lat->aud_latency = (edid[offset + 9] - 1) * 2; + } + if (length >= 8 && ((current_byte + 8) & 0xC0)) { + lat->int_vid_latency = (edid[offset + 10] - 1) * 2; + lat->int_aud_latency = (edid[offset + 11] - 1) * 2; + } + } +} + +void hdmi_deep_color_support_info(u8 *edid, struct deep_color *format) +{ + int offset, current_byte, length = 0; + enum extension_edid_db vsdb = DATABLOCK_VENDOR; + memset(format, 0, sizeof(*format)); + + if (!hdmi_get_datablock_offset(edid, vsdb, &offset)) { + current_byte = edid[offset]; + length = current_byte & HDMI_EDID_EX_DATABLOCK_LEN_MASK; + if (length >= 6) { + format->bit_30 = (edid[offset + 6] & 0x10); + format->bit_36 = (edid[offset + 6] & 0x20); + } + if (length >= 7) + format->max_tmds_freq = (edid[offset + 7]) * 5; + } +} + +bool hdmi_tv_yuv_supported(u8 *edid) +{ + if (edid[0x7e] != 0x00 && edid[0x83] & 0x30) { + printk(KERN_INFO "YUV supported"); + return true; + } + return false; +}