aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/gpu/drm/drm_edid.c
diff options
context:
space:
mode:
authorZhao Yakui <yakui.zhao@intel.com>2009-08-26 06:20:49 -0400
committerDave Airlie <airlied@redhat.com>2009-08-30 19:22:43 -0400
commit882f0219518196a94cd2772004e87b178467139a (patch)
treeb3a48771b253a49981a72d72e20f636d9278833b /drivers/gpu/drm/drm_edid.c
parent785b93ef8c309730c2de84ce9c229e40e2d01480 (diff)
drm/kms: Parse the detailed time info in CEA-EDID
Sometimes we can obtain the EDID with multiple blocks from the display device. For example: HDMI monitor. When the CEA-EDID block is detected, we should also parse the detailed timing info from it. Otherwise we will lose some modes for the display device. The first step is check whether the CEA EDID block is found. If it exists, it will skip the CEA-data block and parse the detailed timing info. Signed-off-by: Zhao Yakui <yakui.zhao@intel.com> Signed-off-by: Dave Airlie <airlied@redhat.com>
Diffstat (limited to 'drivers/gpu/drm/drm_edid.c')
-rw-r--r--drivers/gpu/drm/drm_edid.c120
1 files changed, 119 insertions, 1 deletions
diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c
index a1cab5de2f4b..e4f1cb5fa60e 100644
--- a/drivers/gpu/drm/drm_edid.c
+++ b/drivers/gpu/drm/drm_edid.c
@@ -60,6 +60,8 @@
60#define EDID_QUIRK_FIRST_DETAILED_PREFERRED (1 << 5) 60#define EDID_QUIRK_FIRST_DETAILED_PREFERRED (1 << 5)
61/* use +hsync +vsync for detailed mode */ 61/* use +hsync +vsync for detailed mode */
62#define EDID_QUIRK_DETAILED_SYNC_PP (1 << 6) 62#define EDID_QUIRK_DETAILED_SYNC_PP (1 << 6)
63/* define the number of Extension EDID block */
64#define MAX_EDID_EXT_NUM 4
63 65
64#define LEVEL_DMT 0 66#define LEVEL_DMT 0
65#define LEVEL_GTF 1 67#define LEVEL_GTF 1
@@ -597,6 +599,122 @@ static int add_detailed_info(struct drm_connector *connector,
597 599
598 return modes; 600 return modes;
599} 601}
602/**
603 * add_detailed_mode_eedid - get detailed mode info from addtional timing
604 * EDID block
605 * @connector: attached connector
606 * @edid: EDID block to scan(It is only to get addtional timing EDID block)
607 * @quirks: quirks to apply
608 *
609 * Some of the detailed timing sections may contain mode information. Grab
610 * it and add it to the list.
611 */
612static int add_detailed_info_eedid(struct drm_connector *connector,
613 struct edid *edid, u32 quirks)
614{
615 struct drm_device *dev = connector->dev;
616 int i, j, modes = 0;
617 char *edid_ext = NULL;
618 struct detailed_timing *timing;
619 struct detailed_non_pixel *data;
620 struct drm_display_mode *newmode;
621 int edid_ext_num;
622 int start_offset, end_offset;
623 int timing_level;
624
625 if (edid->version == 1 && edid->revision < 3) {
626 /* If the EDID version is less than 1.3, there is no
627 * extension EDID.
628 */
629 return 0;
630 }
631 if (!edid->extensions) {
632 /* if there is no extension EDID, it is unnecessary to
633 * parse the E-EDID to get detailed info
634 */
635 return 0;
636 }
637
638 /* Chose real EDID extension number */
639 edid_ext_num = edid->extensions > MAX_EDID_EXT_NUM ?
640 MAX_EDID_EXT_NUM : edid->extensions;
641
642 /* Find CEA extension */
643 for (i = 0; i < edid_ext_num; i++) {
644 edid_ext = (char *)edid + EDID_LENGTH * (i + 1);
645 /* This block is CEA extension */
646 if (edid_ext[0] == 0x02)
647 break;
648 }
649
650 if (i == edid_ext_num) {
651 /* if there is no additional timing EDID block, return */
652 return 0;
653 }
654
655 /* Get the start offset of detailed timing block */
656 start_offset = edid_ext[2];
657 if (start_offset == 0) {
658 /* If the start_offset is zero, it means that neither detailed
659 * info nor data block exist. In such case it is also
660 * unnecessary to parse the detailed timing info.
661 */
662 return 0;
663 }
664
665 timing_level = standard_timing_level(edid);
666 end_offset = EDID_LENGTH;
667 end_offset -= sizeof(struct detailed_timing);
668 for (i = start_offset; i < end_offset;
669 i += sizeof(struct detailed_timing)) {
670 timing = (struct detailed_timing *)(edid_ext + i);
671 data = &timing->data.other_data;
672 /* Detailed mode timing */
673 if (timing->pixel_clock) {
674 newmode = drm_mode_detailed(dev, edid, timing, quirks);
675 if (!newmode)
676 continue;
677
678 drm_mode_probed_add(connector, newmode);
679
680 modes++;
681 continue;
682 }
683
684 /* Other timing or info */
685 switch (data->type) {
686 case EDID_DETAIL_MONITOR_SERIAL:
687 break;
688 case EDID_DETAIL_MONITOR_STRING:
689 break;
690 case EDID_DETAIL_MONITOR_RANGE:
691 /* Get monitor range data */
692 break;
693 case EDID_DETAIL_MONITOR_NAME:
694 break;
695 case EDID_DETAIL_MONITOR_CPDATA:
696 break;
697 case EDID_DETAIL_STD_MODES:
698 /* Five modes per detailed section */
699 for (j = 0; j < 5; i++) {
700 struct std_timing *std;
701 struct drm_display_mode *newmode;
702
703 std = &data->data.timings[j];
704 newmode = drm_mode_std(dev, std, timing_level);
705 if (newmode) {
706 drm_mode_probed_add(connector, newmode);
707 modes++;
708 }
709 }
710 break;
711 default:
712 break;
713 }
714 }
715
716 return modes;
717}
600 718
601#define DDC_ADDR 0x50 719#define DDC_ADDR 0x50
602/** 720/**
@@ -656,7 +774,6 @@ end:
656 return ret; 774 return ret;
657} 775}
658 776
659#define MAX_EDID_EXT_NUM 4
660/** 777/**
661 * drm_get_edid - get EDID data, if available 778 * drm_get_edid - get EDID data, if available
662 * @connector: connector we're probing 779 * @connector: connector we're probing
@@ -809,6 +926,7 @@ int drm_add_edid_modes(struct drm_connector *connector, struct edid *edid)
809 num_modes += add_established_modes(connector, edid); 926 num_modes += add_established_modes(connector, edid);
810 num_modes += add_standard_modes(connector, edid); 927 num_modes += add_standard_modes(connector, edid);
811 num_modes += add_detailed_info(connector, edid, quirks); 928 num_modes += add_detailed_info(connector, edid, quirks);
929 num_modes += add_detailed_info_eedid(connector, edid, quirks);
812 930
813 if (quirks & (EDID_QUIRK_PREFER_LARGE_60 | EDID_QUIRK_PREFER_LARGE_75)) 931 if (quirks & (EDID_QUIRK_PREFER_LARGE_60 | EDID_QUIRK_PREFER_LARGE_75))
814 edid_fixup_preferred(connector, quirks); 932 edid_fixup_preferred(connector, quirks);