Adding support for common EDID parsing in kernel. EDID - Extended display identification data is a data structure provided by a digital display to describe its capabilities to a video source, This a standard supported by CEA and VESA. There are several custom implementations for parsing EDID in kernel, some of them are present in fbmon.c, drm_edid.c, sh_mobile_hdmi.c, Ideally parsing of EDID should be done in a library, which is agnostic of the framework (V4l2, DRM, FB) which is using the functionality, just based on the raw EDID pointer with size/segment information. With other RFC's such as the one below, which tries to standardize HDMI API's It would be better to have a common EDID code in one place.It also helps to provide better interoperability with variety of TV/Monitor may be even by listing out quirks which might get missed with several custom implementation of EDID. http://permalink.gmane.org/gmane.linux.drivers.video-input-infrastructure/30401 This patch tries to add functions to parse some portion EDID (detailed timing, monitor limits, AV delay information, deep color mode support, Audio and VSDB) If we can align on this library approach i can enhance this library to parse other blocks and probably we could also add quirks from other implementation as well. Signed-off-by: Mythri P K <mythripk@xxxxxx> --- arch/arm/include/asm/edid.h | 243 ++++++++++++++++++++++++++++++ drivers/video/edid.c | 340 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 583 insertions(+), 0 deletions(-) create mode 100644 arch/arm/include/asm/edid.h create mode 100644 drivers/video/edid.c 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 <mythripk@xxxxxx> + * + * 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 <http://www.gnu.org/licenses/>. + * 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 <mythripk@xxxxxx> + * With EDID parsing for DVI Monitor from Rob Clark <rob@xxxxxx> + * + * 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 <http://www.gnu.org/licenses/>. + * History: + * + */ + +#include <linux/kernel.h> +#include <linux/err.h> +#include <linux/string.h> +#include <linux/slab.h> +#include <asm/edid.h> + +/* 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; +} -- 1.5.6.3 -- To unsubscribe from this list: send the line "unsubscribe linux-media" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html