diff options
author | Guennadi Liakhovetski <g.liakhovetski@gmx.de> | 2010-11-11 09:45:09 -0500 |
---|---|---|
committer | Paul Mundt <lethal@linux-sh.org> | 2010-11-15 01:01:27 -0500 |
commit | 4232f60791d12a5156c3e1c9b04ae08b873a2920 (patch) | |
tree | 11995d45e6582aa8c99bdf201354b4ef31e126ae /drivers/video/sh_mobile_hdmi.c | |
parent | d8d776f3fb1ff19c37d43b600fc8c128ff172deb (diff) |
fbdev: sh_mobile_hdmi: add support for E-EDID parsing
Many HDMI clients implement enhanced EDID blocks, which often contain
additional supported video modes. This patch implements parsing of such
E-EDID blocks.
Signed-off-by: Guennadi Liakhovetski <g.liakhovetski@gmx.de>
Signed-off-by: Paul Mundt <lethal@linux-sh.org>
Diffstat (limited to 'drivers/video/sh_mobile_hdmi.c')
-rw-r--r-- | drivers/video/sh_mobile_hdmi.c | 60 |
1 files changed, 49 insertions, 11 deletions
diff --git a/drivers/video/sh_mobile_hdmi.c b/drivers/video/sh_mobile_hdmi.c index 3b4cf987fb43..76f9fac9020f 100644 --- a/drivers/video/sh_mobile_hdmi.c +++ b/drivers/video/sh_mobile_hdmi.c | |||
@@ -211,6 +211,9 @@ struct sh_hdmi { | |||
211 | enum hotplug_state hp_state; /* hot-plug status */ | 211 | enum hotplug_state hp_state; /* hot-plug status */ |
212 | u8 preprogrammed_vic; /* use a pre-programmed VIC or | 212 | u8 preprogrammed_vic; /* use a pre-programmed VIC or |
213 | the external mode */ | 213 | the external mode */ |
214 | u8 edid_block_addr; | ||
215 | u8 edid_segment_nr; | ||
216 | u8 edid_blocks; | ||
214 | struct clk *hdmi_clk; | 217 | struct clk *hdmi_clk; |
215 | struct device *dev; | 218 | struct device *dev; |
216 | struct fb_info *info; | 219 | struct fb_info *info; |
@@ -756,7 +759,38 @@ static int sh_hdmi_read_edid(struct sh_hdmi *hdmi, unsigned long *hdmi_rate, | |||
756 | printk(KERN_CONT "\n"); | 759 | printk(KERN_CONT "\n"); |
757 | #endif | 760 | #endif |
758 | 761 | ||
759 | fb_edid_to_monspecs(edid, &hdmi->monspec); | 762 | if (!hdmi->edid_blocks) { |
763 | fb_edid_to_monspecs(edid, &hdmi->monspec); | ||
764 | hdmi->edid_blocks = edid[126] + 1; | ||
765 | |||
766 | dev_dbg(hdmi->dev, "%d main modes, %d extension blocks\n", | ||
767 | hdmi->monspec.modedb_len, hdmi->edid_blocks - 1); | ||
768 | } else { | ||
769 | dev_dbg(hdmi->dev, "Extension %u detected, DTD start %u\n", | ||
770 | edid[0], edid[2]); | ||
771 | fb_edid_add_monspecs(edid, &hdmi->monspec); | ||
772 | } | ||
773 | |||
774 | if (hdmi->edid_blocks > hdmi->edid_segment_nr * 2 + | ||
775 | (hdmi->edid_block_addr >> 7) + 1) { | ||
776 | /* More blocks to read */ | ||
777 | if (hdmi->edid_block_addr) { | ||
778 | hdmi->edid_block_addr = 0; | ||
779 | hdmi->edid_segment_nr++; | ||
780 | } else { | ||
781 | hdmi->edid_block_addr = 0x80; | ||
782 | } | ||
783 | /* Set EDID word address */ | ||
784 | hdmi_write(hdmi, hdmi->edid_block_addr, HDMI_EDID_WORD_ADDRESS); | ||
785 | /* Enable EDID interrupt */ | ||
786 | hdmi_write(hdmi, 0xC6, HDMI_INTERRUPT_MASK_1); | ||
787 | /* Set EDID segment pointer - starts reading EDID */ | ||
788 | hdmi_write(hdmi, hdmi->edid_segment_nr, HDMI_EDID_SEGMENT_POINTER); | ||
789 | return -EAGAIN; | ||
790 | } | ||
791 | |||
792 | /* All E-EDID blocks ready */ | ||
793 | dev_dbg(hdmi->dev, "%d main and extended modes\n", hdmi->monspec.modedb_len); | ||
760 | 794 | ||
761 | fb_get_options("sh_mobile_lcdc", &forced); | 795 | fb_get_options("sh_mobile_lcdc", &forced); |
762 | if (forced && *forced) { | 796 | if (forced && *forced) { |
@@ -903,32 +937,34 @@ static irqreturn_t sh_hdmi_hotplug(int irq, void *dev_id) | |||
903 | /* Check, if hot plug & MSENS pin status are both high */ | 937 | /* Check, if hot plug & MSENS pin status are both high */ |
904 | if ((msens & 0xC0) == 0xC0) { | 938 | if ((msens & 0xC0) == 0xC0) { |
905 | /* Display plug in */ | 939 | /* Display plug in */ |
940 | hdmi->edid_segment_nr = 0; | ||
941 | hdmi->edid_block_addr = 0; | ||
942 | hdmi->edid_blocks = 0; | ||
906 | hdmi->hp_state = HDMI_HOTPLUG_CONNECTED; | 943 | hdmi->hp_state = HDMI_HOTPLUG_CONNECTED; |
907 | 944 | ||
908 | /* Set EDID word address */ | 945 | /* Set EDID word address */ |
909 | hdmi_write(hdmi, 0x00, HDMI_EDID_WORD_ADDRESS); | 946 | hdmi_write(hdmi, 0x00, HDMI_EDID_WORD_ADDRESS); |
910 | /* Set EDID segment pointer */ | ||
911 | hdmi_write(hdmi, 0x00, HDMI_EDID_SEGMENT_POINTER); | ||
912 | /* Enable EDID interrupt */ | 947 | /* Enable EDID interrupt */ |
913 | hdmi_write(hdmi, 0xC6, HDMI_INTERRUPT_MASK_1); | 948 | hdmi_write(hdmi, 0xC6, HDMI_INTERRUPT_MASK_1); |
949 | /* Set EDID segment pointer - starts reading EDID */ | ||
950 | hdmi_write(hdmi, 0x00, HDMI_EDID_SEGMENT_POINTER); | ||
914 | } else if (!(status1 & 0x80)) { | 951 | } else if (!(status1 & 0x80)) { |
915 | /* Display unplug, beware multiple interrupts */ | 952 | /* Display unplug, beware multiple interrupts */ |
916 | if (hdmi->hp_state != HDMI_HOTPLUG_DISCONNECTED) | 953 | if (hdmi->hp_state != HDMI_HOTPLUG_DISCONNECTED) { |
954 | hdmi->hp_state = HDMI_HOTPLUG_DISCONNECTED; | ||
917 | schedule_delayed_work(&hdmi->edid_work, 0); | 955 | schedule_delayed_work(&hdmi->edid_work, 0); |
918 | 956 | } | |
919 | hdmi->hp_state = HDMI_HOTPLUG_DISCONNECTED; | ||
920 | /* display_off will switch back to mode_a */ | 957 | /* display_off will switch back to mode_a */ |
921 | } | 958 | } |
922 | } else if (status1 & 2) { | 959 | } else if (status1 & 2) { |
923 | /* EDID error interrupt: retry */ | 960 | /* EDID error interrupt: retry */ |
924 | /* Set EDID word address */ | 961 | /* Set EDID word address */ |
925 | hdmi_write(hdmi, 0x00, HDMI_EDID_WORD_ADDRESS); | 962 | hdmi_write(hdmi, hdmi->edid_block_addr, HDMI_EDID_WORD_ADDRESS); |
926 | /* Set EDID segment pointer */ | 963 | /* Set EDID segment pointer */ |
927 | hdmi_write(hdmi, 0x00, HDMI_EDID_SEGMENT_POINTER); | 964 | hdmi_write(hdmi, hdmi->edid_segment_nr, HDMI_EDID_SEGMENT_POINTER); |
928 | } else if (status1 & 4) { | 965 | } else if (status1 & 4) { |
929 | /* Disable EDID interrupt */ | 966 | /* Disable EDID interrupt */ |
930 | hdmi_write(hdmi, 0xC0, HDMI_INTERRUPT_MASK_1); | 967 | hdmi_write(hdmi, 0xC0, HDMI_INTERRUPT_MASK_1); |
931 | hdmi->hp_state = HDMI_HOTPLUG_EDID_DONE; | ||
932 | schedule_delayed_work(&hdmi->edid_work, msecs_to_jiffies(10)); | 968 | schedule_delayed_work(&hdmi->edid_work, msecs_to_jiffies(10)); |
933 | } | 969 | } |
934 | 970 | ||
@@ -1056,7 +1092,7 @@ static void sh_hdmi_edid_work_fn(struct work_struct *work) | |||
1056 | 1092 | ||
1057 | mutex_lock(&hdmi->mutex); | 1093 | mutex_lock(&hdmi->mutex); |
1058 | 1094 | ||
1059 | if (hdmi->hp_state == HDMI_HOTPLUG_EDID_DONE) { | 1095 | if (hdmi->hp_state == HDMI_HOTPLUG_CONNECTED) { |
1060 | unsigned long parent_rate = 0, hdmi_rate; | 1096 | unsigned long parent_rate = 0, hdmi_rate; |
1061 | 1097 | ||
1062 | /* A device has been plugged in */ | 1098 | /* A device has been plugged in */ |
@@ -1066,6 +1102,8 @@ static void sh_hdmi_edid_work_fn(struct work_struct *work) | |||
1066 | if (ret < 0) | 1102 | if (ret < 0) |
1067 | goto out; | 1103 | goto out; |
1068 | 1104 | ||
1105 | hdmi->hp_state = HDMI_HOTPLUG_EDID_DONE; | ||
1106 | |||
1069 | /* Reconfigure the clock */ | 1107 | /* Reconfigure the clock */ |
1070 | ret = sh_hdmi_clk_configure(hdmi, hdmi_rate, parent_rate); | 1108 | ret = sh_hdmi_clk_configure(hdmi, hdmi_rate, parent_rate); |
1071 | if (ret < 0) | 1109 | if (ret < 0) |
@@ -1119,7 +1157,7 @@ static void sh_hdmi_edid_work_fn(struct work_struct *work) | |||
1119 | } | 1157 | } |
1120 | 1158 | ||
1121 | out: | 1159 | out: |
1122 | if (ret < 0) | 1160 | if (ret < 0 && ret != -EAGAIN) |
1123 | hdmi->hp_state = HDMI_HOTPLUG_DISCONNECTED; | 1161 | hdmi->hp_state = HDMI_HOTPLUG_DISCONNECTED; |
1124 | mutex_unlock(&hdmi->mutex); | 1162 | mutex_unlock(&hdmi->mutex); |
1125 | 1163 | ||