summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorZach Pfeffer <zach.pfeffer@linaro.org>2011-06-26 09:39:27 +0100
committerAndy Green <andy.green@linaro.org>2011-06-26 09:39:27 +0100
commit20b7045046128e5f4e3136008fd1bcac5f732899 (patch)
tree08507e90285df966b2ab13764888af49b41d84af
parentca0fd314c48699d2896fee9cef18043f58e9e831 (diff)
Subject: [PATCH] hdmi: edid: read the resolution at runtime
Undo the force workaround and parse the returned edid for the correct timing and resolution.
-rw-r--r--arch/arm/plat-omap/include/plat/edid.h291
-rw-r--r--drivers/video/omap2/dss/Makefile2
-rw-r--r--drivers/video/omap2/dss/edid.c393
-rw-r--r--drivers/video/omap2/dss/hdmi.c200
4 files changed, 883 insertions, 3 deletions
diff --git a/arch/arm/plat-omap/include/plat/edid.h b/arch/arm/plat-omap/include/plat/edid.h
new file mode 100644
index 00000000000..4e76563517e
--- /dev/null
+++ b/arch/arm/plat-omap/include/plat/edid.h
@@ -0,0 +1,291 @@
+/*
+ * linux/drivers/video/omap2/dss/edid.c
+ *
+ * Copyright (C) 2009 Texas Instruments
+ * Author: Mythri P K <mythripk@ti.com>
+ * with some of the ENUM's and structure derived from Yong Zhi's
+ * hdmi.h(Now obsolete)
+ *
+ * EDID.c to read the EDID content , given the 256 Bytes EDID.
+ *
+ * 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_
+
+#include <plat/display.h>
+
+/* HDMI EDID Extension Data Block Tags */
+#define HDMI_EDID_EX_DATABLOCK_TAG_MASK 0xE0
+#define HDMI_EDID_EX_DATABLOCK_LEN_MASK 0x1F
+#define HDMI_EDID_EX_SUPPORTS_AI_MASK 0x80
+
+#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
+
+#define HDMI_IEEE_REGISTRATION_ID 0x000c03
+
+/* HDMI Connected States */
+#define HDMI_STATE_NOMONITOR 0 /* No HDMI monitor connected*/
+#define HDMI_STATE_CONNECTED 1 /* HDMI monitor connected but powered off */
+#define HDMI_STATE_ON 2 /* HDMI monitor connected and powered on*/
+
+/* HDMI EDID Length */
+#define HDMI_EDID_MAX_LENGTH 512
+
+/* 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_VIDEO_NATIVE_DTDS_MASK 0x0f
+#define HDMI_AUDIO_FORMAT_MAX_LENGTH 10
+#define HDMI_AUDIO_BASIC_MASK 0x40
+
+/* 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 HDMI_EDID_EX_VIDEO_640x480p_60Hz_4_3 1
+#define HDMI_EDID_EX_VIDEO_720x480p_60Hz_4_3 2
+#define HDMI_EDID_EX_VIDEO_720x480p_60Hz_16_9 3
+#define HDMI_EDID_EX_VIDEO_1280x720p_60Hz_16_9 4
+#define HDMI_EDID_EX_VIDEO_1920x1080i_60Hz_16_9 5
+#define HDMI_EDID_EX_VIDEO_720x480i_60Hz_4_3 6
+#define HDMI_EDID_EX_VIDEO_720x480i_60Hz_16_9 7
+#define HDMI_EDID_EX_VIDEO_720x240p_60Hz_4_3 8
+#define HDMI_EDID_EX_VIDEO_720x240p_60Hz_16_9 9
+#define HDMI_EDID_EX_VIDEO_2880x480i_60Hz_4_3 10
+#define HDMI_EDID_EX_VIDEO_2880x480i_60Hz_16_9 11
+#define HDMI_EDID_EX_VIDEO_2880x480p_60Hz_4_3 12
+#define HDMI_EDID_EX_VIDEO_2880x480p_60Hz_16_9 13
+#define HDMI_EDID_EX_VIDEO_1440x480p_60Hz_4_3 14
+#define HDMI_EDID_EX_VIDEO_1440x480p_60Hz_16_9 15
+#define HDMI_EDID_EX_VIDEO_1920x1080p_60Hz_16_9 16
+#define HDMI_EDID_EX_VIDEO_720x576p_50Hz_4_3 17
+#define HDMI_EDID_EX_VIDEO_720x576p_50Hz_16_9 18
+#define HDMI_EDID_EX_VIDEO_1280x720p_50Hz_16_9 19
+#define HDMI_EDID_EX_VIDEO_1920x1080i_50Hz_16_9 20
+#define HDMI_EDID_EX_VIDEO_720x576i_50Hz_4_3 21
+#define HDMI_EDID_EX_VIDEO_720x576i_50Hz_16_9 22
+#define HDMI_EDID_EX_VIDEO_720x288p_50Hz_4_3 23
+#define HDMI_EDID_EX_VIDEO_720x288p_50Hz_16_9 24
+#define HDMI_EDID_EX_VIDEO_2880x576i_50Hz_4_3 25
+#define HDMI_EDID_EX_VIDEO_2880x576i_50Hz_16_9 26
+#define HDMI_EDID_EX_VIDEO_2880x288p_50Hz_4_3 27
+#define HDMI_EDID_EX_VIDEO_2880x288p_50Hz_16_9 28
+#define HDMI_EDID_EX_VIDEO_1440x576p_50Hz_4_3 29
+#define HDMI_EDID_EX_VIDEO_1440x576p_50Hz_16_9 30
+#define HDMI_EDID_EX_VIDEO_1920x1080p_50Hz_16_9 31
+#define HDMI_EDID_EX_VIDEO_1920x1080p_24Hz_16_9 32
+#define HDMI_EDID_EX_VIDEO_1920x1080p_25Hz_16_9 33
+#define HDMI_EDID_EX_VIDEO_1920x1080p_30Hz_16_9 34
+
+#define OMAP_HDMI_TIMINGS_NB 37
+#define OMAP_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 */
+
+} __attribute__ ((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 */
+} __attribute__ ((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;
+} __attribute__ ((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];
+} __attribute__ ((packed));
+
+int get_edid_timing_info(union HDMI_EDID_DTD *edid_dtd,
+ struct omap_video_timings *timings);
+void get_eedid_timing_info(int current_descriptor_addrs, u8 *edid ,
+ struct omap_video_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);
+bool hdmi_has_ieee_id(u8 *edid);
+int hdmi_get_video_svds(u8 *edid, int *offset, int *length);
+void hdmi_get_av_delay(u8 *edid, struct latency *lat);
+void hdmi_deep_color_support_info(u8 *edid, struct deep_color *format);
+int hdmi_tv_yuv_supported(u8 *edid);
+bool hdmi_s3d_supported(u8 *edid);
+bool hdmi_ai_supported(u8 *edid);
+const struct omap_video_timings *hdmi_get_omap_timing(int ix);
+
+#ifdef __cplusplus
+};
+#endif
+
+#endif
+
diff --git a/drivers/video/omap2/dss/Makefile b/drivers/video/omap2/dss/Makefile
index 10d9d3bb3e2..a3c738ea354 100644
--- a/drivers/video/omap2/dss/Makefile
+++ b/drivers/video/omap2/dss/Makefile
@@ -6,4 +6,4 @@ omapdss-$(CONFIG_OMAP2_DSS_VENC) += venc.o
omapdss-$(CONFIG_OMAP2_DSS_SDI) += sdi.o
omapdss-$(CONFIG_OMAP2_DSS_DSI) += dsi.o
omapdss-$(CONFIG_OMAP4_DSS_HDMI) += hdmi.o \
- hdmi_omap4_panel.o
+ hdmi_omap4_panel.o edid.o \ No newline at end of file
diff --git a/drivers/video/omap2/dss/edid.c b/drivers/video/omap2/dss/edid.c
new file mode 100644
index 00000000000..48aaea35342
--- /dev/null
+++ b/drivers/video/omap2/dss/edid.c
@@ -0,0 +1,393 @@
+/*
+ * linux/drivers/video/omap2/dss/edid.c
+ *
+ * Copyright (C) 2009 Texas Instruments
+ * Author: Mythri P K <mythripk@ti.com>
+ * With EDID parsing for DVI Monitor from Rob Clark <rob@ti.com>
+ *
+ * EDID.c to read the EDID content , given the 256 Bytes EDID.
+ *
+ * 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/module.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+
+#include <linux/completion.h>
+#include <linux/delay.h>
+#include <linux/string.h>
+#include <linux/platform_device.h>
+
+#include <plat/cpu.h>
+
+#include <linux/slab.h>
+#include <linux/cdev.h>
+#include <linux/fs.h>
+#include <plat/edid.h>
+
+int get_edid_timing_info(union HDMI_EDID_DTD *edid_dtd,
+ struct omap_video_timings *timings)
+{
+ printk("zp: start of %s\n", __FUNCTION__);
+
+ 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 1;
+ case HDMI_EDID_DTD_TAG_COLOR_POINT_DATA:
+ printk(KERN_INFO "color point data\n");
+ return 1;
+ case HDMI_EDID_DTD_TAG_MONITOR_NAME:
+ printk(KERN_INFO "monitor name: %s\n",
+ edid_dtd->monitor_name.text);
+ return 1;
+ case HDMI_EDID_DTD_TAG_MONITOR_LIMITS:
+ {
+ int i, max_area = 0;
+ struct HDMI_EDID_DTD_MONITOR *limits =
+ &edid_dtd->monitor_limits;
+
+ printk(KERN_INFO "monitor limits\n");
+ printk(KERN_INFO " min_vert_freq=%d\n", limits->min_vert_freq);
+ printk(KERN_INFO " max_vert_freq=%d\n", limits->max_vert_freq);
+ printk(KERN_INFO " min_horiz_freq=%d\n",
+ limits->min_horiz_freq);
+ printk(KERN_INFO " max_horiz_freq=%d\n",
+ limits->max_horiz_freq);
+ printk(KERN_INFO " 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 = OMAP_HDMI_TIMINGS_VESA_START;
+ i < OMAP_HDMI_TIMINGS_NB; i++) {
+ const struct omap_video_timings *t =
+ hdmi_get_omap_timing(i);
+ int hz, hscan, pixclock;
+ int vtotal, htotal;
+ htotal = t->hbp + t->hfp + t->hsw + t->x_res;
+ vtotal = t->vbp + t->vfp + t->vsw + t->y_res;
+
+ /* NOTE: We don't support interlaced mode for VESA */
+ pixclock = t->pixel_clock * 1000;
+ hscan = (pixclock + htotal / 2) / htotal;
+ hscan = (hscan + 500) / 1000 * 1000;
+ hz = (hscan + vtotal / 2) / vtotal;
+ hscan /= 1000;
+ pixclock /= 1000000;
+ printk(KERN_DEBUG "debug only pixclock=%d, hscan=%d, hz=%d\n",
+ pixclock, hscan, hz);
+ 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 = t->x_res * t->y_res;
+ printk(KERN_INFO " -> %d: %dx%d\n", i,
+ t->x_res, t->y_res);
+ if (area > max_area) {
+ max_area = area;
+ *timings = *t;
+ }
+ }
+ }
+ if (max_area)
+ printk(KERN_INFO "found best resolution: %dx%d\n",
+ timings->x_res, timings->y_res);
+ return 0;
+ }
+ case HDMI_EDID_DTD_TAG_ASCII_STRING:
+ printk(KERN_INFO "ascii string: %s\n", edid_dtd->ascii.text);
+ return 1;
+ case HDMI_EDID_DTD_TAG_MONITOR_SERIALNUM:
+ printk(KERN_INFO "monitor serialnum: %s\n",
+ edid_dtd->monitor_serial_number.text);
+ return 1;
+ default:
+ printk(KERN_INFO "unsupported EDID descriptor block format\n");
+ return 1;
+ }
+}
+
+void get_eedid_timing_info(int current_descriptor_addrs, u8 *edid ,
+ struct omap_video_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 1;
+
+ disp = edid[(0x80) + 2];
+ printk(KERN_DEBUG "Extension block present db %d %x\n", datablock, disp);
+ if (disp == 0x4)
+ return 1;
+
+ i = 0x80 + 0x4;
+ printk(KERN_DEBUG "%x\n", i);
+ while (i < (0x80 + disp)) {
+ current_byte = edid[i];
+ printk(KERN_DEBUG "i = %x cur_byte = %x (cur_byte & EX_DATABLOCK_TAG_MASK) = %d\n",
+ i, current_byte,
+ (current_byte & HDMI_EDID_EX_DATABLOCK_TAG_MASK));
+ if ((current_byte >> 5) == datablock) {
+ *offset = i;
+ printk(KERN_DEBUG "datablock %d %d\n",
+ datablock, *offset);
+ return 0;
+ } else {
+ length = (current_byte &
+ HDMI_EDID_EX_DATABLOCK_LEN_MASK) + 1;
+ i += length;
+ }
+ }
+ return 1;
+}
+
+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;
+}
+
+bool hdmi_has_ieee_id(u8 *edid)
+{
+ int offset, current_byte, length = 0;
+ enum extension_edid_db vsdb = DATABLOCK_VENDOR;
+ u32 hdmi_identifier = 0;
+
+ if (!hdmi_get_datablock_offset(edid, vsdb, &offset)) {
+ current_byte = edid[offset];
+ length = current_byte & HDMI_EDID_EX_DATABLOCK_LEN_MASK;
+
+ if (length < 3)
+ return 0;
+ offset++;
+ hdmi_identifier = edid[offset] | edid[offset+1]<<8
+ | edid[offset+2]<<16;
+ if (hdmi_identifier == HDMI_IEEE_REGISTRATION_ID)
+ return 1;
+
+ }
+ return 0;
+}
+
+int hdmi_get_video_svds(u8 *edid, int *offset, int *length)
+{
+ enum extension_edid_db vdb = DATABLOCK_VIDEO;
+ if ((offset == NULL) || (length == NULL))
+ return 0;
+ if (!hdmi_get_datablock_offset(edid, vdb, offset)) {
+ *length = edid[*offset] & HDMI_EDID_EX_DATABLOCK_LEN_MASK;
+ (*offset)++;
+ return 1;
+ }
+ *length = 0;
+ *offset = 0;
+ 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 = ((current_byte + 8) - 1) * 2;
+ lat->aud_latency = ((current_byte + 9) - 1) * 2;
+ }
+ if (length >= 8 && ((current_byte + 8) & 0xC0)) {
+ lat->int_vid_latency = ((current_byte + 10) - 1) * 2;
+ lat->int_aud_latency = ((current_byte + 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 = ((current_byte + 6) & 0x10);
+ format->bit_36 = ((current_byte + 6) & 0x20);
+ }
+ if (length >= 7)
+ format->max_tmds_freq = ((current_byte + 7)) * 5;
+ }
+}
+
+int hdmi_tv_yuv_supported(u8 *edid)
+{
+ if (edid[0x7e] != 0x00 && edid[0x83] & 0x30) {
+ printk(KERN_INFO "YUV supported");
+ return 1;
+ }
+ return 0;
+}
+
+bool hdmi_s3d_supported(u8 *edid)
+{
+ bool s3d_support = false;
+ int offset, current_byte;
+ if (!hdmi_get_datablock_offset(edid, DATABLOCK_VENDOR, &offset)) {
+ offset += 8;
+ current_byte = edid[offset++];
+ /*Latency_Fields_Present?*/
+ if (current_byte & 0x80)
+ offset += 2;
+ /*I_Latency_Fields_Present?*/
+ if (current_byte & 0x40)
+ offset += 2;
+ /*HDMI_Video_present?*/
+ if (current_byte & 0x20) {
+ current_byte = edid[offset];
+ /*3D_Present?*/
+ if (current_byte & 0x80) {
+ printk(KERN_INFO "S3D supported\n");
+ s3d_support = true;
+ }
+ }
+ }
+ return s3d_support;
+}
+
+bool hdmi_ai_supported(u8 *edid)
+{
+ int offset, current_byte, length = 0;
+
+ if (!hdmi_get_datablock_offset(edid, DATABLOCK_VENDOR, &offset)) {
+ current_byte = edid[offset];
+ length = current_byte & HDMI_EDID_EX_DATABLOCK_LEN_MASK;
+ if (length < 6)
+ return false;
+ offset += 6;
+ if (edid[offset] & HDMI_EDID_EX_SUPPORTS_AI_MASK)
+ return true;
+ }
+ return false;
+}
diff --git a/drivers/video/omap2/dss/hdmi.c b/drivers/video/omap2/dss/hdmi.c
index 73490cf779a..f4e4b763733 100644
--- a/drivers/video/omap2/dss/hdmi.c
+++ b/drivers/video/omap2/dss/hdmi.c
@@ -37,6 +37,7 @@
#endif
#include "dss.h"
+#include <plat/edid.h>
#include "hdmi.h"
static int def_force_hdmi_code = 4;
@@ -110,6 +111,50 @@ static const struct hdmi_timings cea_vesa_timings[OMAP_HDMI_TIMINGS_NB] = {
{ {1280, 720, 74250, 40, 110, 220, 5, 5, 20}, 1, 1}
};
+/* All supported timing values that OMAP4 supports */
+static const struct omap_video_timings all_timings_direct[] = {
+ {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, 27000, 62, 16, 60, 6, 9, 30},
+ {2880, 576, 108000, 256, 48, 272, 5, 5, 39},
+ {1440, 240, 27000, 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, 108000, 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, 10, 33},
+ {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, 148, 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},
+ {1920, 1200, 154000, 32, 48, 80, 6, 3, 26},
+ /* supported 3d timings UNDEROVER full frame */
+ {1280, 1470, 148350, 40, 110, 220, 5, 5, 20},
+ {1280, 1470, 148500, 40, 110, 220, 5, 5, 20},
+ {1280, 1470, 148500, 40, 440, 220, 5, 5, 20}
+};
+
/*
* This is a static mapping array which maps the timing values
* with corresponding CEA / VESA code
@@ -565,6 +610,148 @@ static struct hdmi_cm hdmi_get_code(struct omap_video_timings *timing)
return cm;
}
+
+struct omap_video_timings edid_timings;
+
+static inline void print_omap_video_timings(struct omap_video_timings *timings)
+{
+ extern unsigned int dss_debug;
+ if (dss_debug) {
+ printk(KERN_INFO "Timing Info:\n");
+ printk(KERN_INFO " pixel_clk = %d\n", timings->pixel_clock);
+ printk(KERN_INFO " x_res = %d\n", timings->x_res);
+ printk(KERN_INFO " y_res = %d\n", timings->y_res);
+ printk(KERN_INFO " hfp = %d\n", timings->hfp);
+ printk(KERN_INFO " hsw = %d\n", timings->hsw);
+ printk(KERN_INFO " hbp = %d\n", timings->hbp);
+ printk(KERN_INFO " vfp = %d\n", timings->vfp);
+ printk(KERN_INFO " vsw = %d\n", timings->vsw);
+ printk(KERN_INFO " vbp = %d\n", timings->vbp);
+ }
+}
+
+/*
+ * Written mainly by 223a4fdb Ricard Neri
+ * git://git.linaro.org/people/asac/android/kernel/pandroid
+ */
+static int get_edid_timing_data(struct HDMI_EDID *edid)
+{
+ u8 i, j, code, offset = 0, addr = 0;
+ struct hdmi_cm cm;
+ bool audio_support = false;
+ int svd_base, svd_length, svd_code, svd_native;
+
+ /*
+ * Verify if the sink supports audio
+ */
+ /* check if EDID has CEA extension block */
+ if ((edid->extension_edid != 0x00))
+ /* check if CEA extension block is version 3 */
+ if (edid->extention_rev == 3)
+ /* check if extension block has the IEEE HDMI ID*/
+ if (hdmi_has_ieee_id((u8 *)edid))
+ /* check if sink supports basic audio */
+ if (edid->num_dtd & HDMI_AUDIO_BASIC_MASK)
+ audio_support = true;
+
+ /* Seach block 0, there are 4 DTDs arranged in priority order */
+ for (i = 0; i < EDID_SIZE_BLOCK0_TIMING_DESCRIPTOR; i++) {
+ get_edid_timing_info(&edid->DTD[i], &edid_timings);
+ DSSDBG("Block0 [%d] timings:", i);
+ print_omap_video_timings(&edid_timings);
+ cm = hdmi_get_code(&edid_timings);
+ DSSDBG("Block0[%d] value matches code = %d , mode = %d",
+ i, cm.code, cm.mode);
+ if (cm.code == -1)
+ continue;
+/*
+ if (hdmi.s3d_enabled && s3d_code_cea[cm.code] == -1)
+ continue;
+*/
+ /* if sink supports audio, use CEA video timing */
+ if (audio_support && !cm.mode)
+ continue;
+ hdmi.code = cm.code;
+ hdmi.mode = cm.mode;
+ DSSDBG("code = %d , mode = %d", hdmi.code, hdmi.mode);
+ return 1;
+ }
+ /* Search SVDs in block 1 twice: first natives and then all */
+ if (edid->extension_edid != 0x00) {
+ hdmi_get_video_svds((u8 *)edid, &svd_base, &svd_length);
+ for (j = 1; j >= 0; j--) {
+ for (i = 0; i < svd_length; i++) {
+ svd_native = ((u8 *)edid)[svd_base+i]
+ & HDMI_EDID_EX_VIDEO_NATIVE;
+ svd_code = ((u8 *)edid)[svd_base+i]
+ & HDMI_EDID_EX_VIDEO_MASK;
+ if (svd_code >= ARRAY_SIZE(code_cea))
+ continue;
+ /* Check if this SVD is native*/
+ if (!svd_native && j)
+ continue;
+ /* Check if this 3D CEA timing is supported*/
+/*
+ if (hdmi.s3d_enabled &&
+ s3d_code_cea[svd_code] == -1)
+ continue;
+*/
+ /* Check if this CEA timing is supported*/
+ if (code_cea[svd_code] == -1)
+ continue;
+ hdmi.code = svd_code;
+ hdmi.mode = 1;
+ return 1;
+ }
+ }
+ }
+ /* Search DTDs in block1 */
+ if (edid->extension_edid != 0x00) {
+ offset = edid->offset_dtd;
+ if (offset != 0)
+ addr = EDID_DESCRIPTOR_BLOCK1_ADDRESS + offset;
+ for (i = 0; i < EDID_SIZE_BLOCK1_TIMING_DESCRIPTOR; i++) {
+ get_eedid_timing_info(addr, (u8 *)edid, &edid_timings);
+ addr += EDID_TIMING_DESCRIPTOR_SIZE;
+ cm = hdmi_get_code(&edid_timings);
+ DSSDBG("Block1[%d] value matches code = %d , mode = %d",
+ i, cm.code, cm.mode);
+ if (cm.code == -1)
+ continue;
+/*
+ if (hdmi.s3d_enabled && s3d_code_cea[cm.code] == -1)
+ continue;
+*/
+ /* if sink supports audio, use CEA video timing */
+ if (audio_support && !cm.mode)
+ continue;
+ hdmi.code = cm.code;
+ hdmi.mode = cm.mode;
+ DSSDBG("code = %d , mode = %d", hdmi.code, hdmi.mode);
+ return 1;
+ }
+ }
+ /*As last resort, check for best standard timing supported:*/
+ if (edid->timing_1 & 0x01) {
+ DSSDBG("800x600@60Hz\n");
+ hdmi.mode = 0;
+ hdmi.code = 9;
+ return 1;
+ }
+ if (edid->timing_2 & 0x08) {
+ DSSDBG("1024x768@60Hz\n");
+ hdmi.mode = 0;
+ hdmi.code = 16;
+ return 1;
+ }
+
+ hdmi.code = 4; /*setting default value of 640 480 VGA*/
+ hdmi.mode = 0;
+ code = code_vesa[hdmi.code];
+ edid_timings = all_timings_direct[code];
+ return 1;
+}
+
static void hdmi_read_edid()
{
int ret = 0;
@@ -576,7 +763,8 @@ static void hdmi_read_edid()
if (!ret) {
if (!memcmp(hdmi.edid, edid_header, sizeof(edid_header))) {
- hdmi.edid_set = true;
+ if (get_edid_timing_data((struct HDMI_EDID *) hdmi.edid))
+ hdmi.edid_set = true;
}
} else {
pr_err("failed to read E-EDID\n");
@@ -1632,7 +1820,8 @@ static int omapdss_hdmihw_probe(struct platform_device *pdev)
}
#endif
- hdmi.code = def_force_hdmi_code;
+ /* hdmi.code = def_force_hdmi_code; */
+ printk("zp: hdmi.code = def_force_hdmi_code");
hdmi.pdata = pdev->dev.platform_data;
hdmi.pdev = pdev;
@@ -1688,3 +1877,10 @@ void hdmi_uninit_platform_driver(void)
{
return platform_driver_unregister(&omapdss_hdmihw_driver);
}
+
+const struct omap_video_timings *hdmi_get_omap_timing(int ix)
+{
+ if (ix < 0 || ix >= ARRAY_SIZE(all_timings_direct))
+ return NULL;
+ return all_timings_direct + ix;
+}