Adding dri-devel. On Tue, Mar 22, 2011 at 1:32 PM, Mythri P K <mythripk@xxxxxx> wrote: > 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 > -- 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