diff options
author | Adam Jackson <ajax@redhat.com> | 2010-03-29 17:43:27 -0400 |
---|---|---|
committer | Dave Airlie <airlied@redhat.com> | 2010-04-05 20:40:23 -0400 |
commit | b17e52ef7e7961f02baec98872781386218558bc (patch) | |
tree | 702ea1af46001c8bbf34aca156f2d18e40422499 /drivers/gpu/drm/drm_edid.c | |
parent | d1ff6409b1cd2cd2a65df415648fa38b9fdf4cd8 (diff) |
drm/edid: Extend range-based mode addition for EDID 1.4
1.4 adds better pixel clock precision, explicit reduced blanking
awareness, and extended sync ranges. It's almost like a real spec.
Signed-off-by: Adam Jackson <ajax@redhat.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.c | 101 |
1 files changed, 78 insertions, 23 deletions
diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c index a1b6b433e5bc..e746dd56658a 100644 --- a/drivers/gpu/drm/drm_edid.c +++ b/drivers/gpu/drm/drm_edid.c | |||
@@ -1066,36 +1066,89 @@ static int add_standard_modes(struct drm_connector *connector, struct edid *edid | |||
1066 | return modes; | 1066 | return modes; |
1067 | } | 1067 | } |
1068 | 1068 | ||
1069 | /* | ||
1070 | * XXX fix this for: | ||
1071 | * - GTF secondary curve formula | ||
1072 | * - EDID 1.4 range offsets | ||
1073 | * - CVT extended bits | ||
1074 | */ | ||
1075 | static bool | 1069 | static bool |
1076 | mode_in_range(struct drm_display_mode *mode, struct detailed_timing *timing) | 1070 | mode_is_rb(struct drm_display_mode *mode) |
1077 | { | 1071 | { |
1078 | struct detailed_data_monitor_range *range; | 1072 | return (mode->htotal - mode->hdisplay == 160) && |
1079 | int hsync, vrefresh; | 1073 | (mode->hsync_end - mode->hdisplay == 80) && |
1080 | 1074 | (mode->hsync_end - mode->hsync_start == 32) && | |
1081 | range = &timing->data.other_data.data.range; | 1075 | (mode->vsync_start - mode->vdisplay == 3); |
1076 | } | ||
1082 | 1077 | ||
1078 | static bool | ||
1079 | mode_in_hsync_range(struct drm_display_mode *mode, struct edid *edid, u8 *t) | ||
1080 | { | ||
1081 | int hsync, hmin, hmax; | ||
1082 | |||
1083 | hmin = t[7]; | ||
1084 | if (edid->revision >= 4) | ||
1085 | hmin += ((t[4] & 0x04) ? 255 : 0); | ||
1086 | hmax = t[8]; | ||
1087 | if (edid->revision >= 4) | ||
1088 | hmax += ((t[4] & 0x08) ? 255 : 0); | ||
1083 | hsync = drm_mode_hsync(mode); | 1089 | hsync = drm_mode_hsync(mode); |
1084 | vrefresh = drm_mode_vrefresh(mode); | ||
1085 | 1090 | ||
1086 | if (hsync < range->min_hfreq_khz || hsync > range->max_hfreq_khz) | 1091 | return (hsync <= hmax && hsync >= hmin); |
1092 | } | ||
1093 | |||
1094 | static bool | ||
1095 | mode_in_vsync_range(struct drm_display_mode *mode, struct edid *edid, u8 *t) | ||
1096 | { | ||
1097 | int vsync, vmin, vmax; | ||
1098 | |||
1099 | vmin = t[5]; | ||
1100 | if (edid->revision >= 4) | ||
1101 | vmin += ((t[4] & 0x01) ? 255 : 0); | ||
1102 | vmax = t[6]; | ||
1103 | if (edid->revision >= 4) | ||
1104 | vmax += ((t[4] & 0x02) ? 255 : 0); | ||
1105 | vsync = drm_mode_vrefresh(mode); | ||
1106 | |||
1107 | return (vsync <= vmax && vsync >= vmin); | ||
1108 | } | ||
1109 | |||
1110 | static u32 | ||
1111 | range_pixel_clock(struct edid *edid, u8 *t) | ||
1112 | { | ||
1113 | /* unspecified */ | ||
1114 | if (t[9] == 0 || t[9] == 255) | ||
1115 | return 0; | ||
1116 | |||
1117 | /* 1.4 with CVT support gives us real precision, yay */ | ||
1118 | if (edid->revision >= 4 && t[10] == 0x04) | ||
1119 | return (t[9] * 10000) - ((t[12] >> 2) * 250); | ||
1120 | |||
1121 | /* 1.3 is pathetic, so fuzz up a bit */ | ||
1122 | return t[9] * 10000 + 5001; | ||
1123 | } | ||
1124 | |||
1125 | /* | ||
1126 | * XXX fix this for GTF secondary curve formula | ||
1127 | */ | ||
1128 | static bool | ||
1129 | mode_in_range(struct drm_display_mode *mode, struct edid *edid, | ||
1130 | struct detailed_timing *timing) | ||
1131 | { | ||
1132 | u32 max_clock; | ||
1133 | u8 *t = (u8 *)timing; | ||
1134 | |||
1135 | if (!mode_in_hsync_range(mode, edid, t)) | ||
1087 | return false; | 1136 | return false; |
1088 | 1137 | ||
1089 | if (vrefresh < range->min_vfreq || vrefresh > range->max_vfreq) | 1138 | if (!mode_in_vsync_range(mode, edid, t)) |
1090 | return false; | 1139 | return false; |
1091 | 1140 | ||
1092 | if (range->pixel_clock_mhz && range->pixel_clock_mhz != 0xff) { | 1141 | if ((max_clock = range_pixel_clock(edid, t))) |
1093 | /* be forgiving since it's in units of 10MHz */ | ||
1094 | int max_clock = range->pixel_clock_mhz * 10 + 9; | ||
1095 | max_clock *= 1000; | ||
1096 | if (mode->clock > max_clock) | 1142 | if (mode->clock > max_clock) |
1097 | return false; | 1143 | return false; |
1098 | } | 1144 | |
1145 | /* 1.4 max horizontal check */ | ||
1146 | if (edid->revision >= 4 && t[10] == 0x04) | ||
1147 | if (t[13] && mode->hdisplay > 8 * (t[13] + (256 * (t[12]&0x3)))) | ||
1148 | return false; | ||
1149 | |||
1150 | if (mode_is_rb(mode) && !drm_monitor_supports_rb(edid)) | ||
1151 | return false; | ||
1099 | 1152 | ||
1100 | return true; | 1153 | return true; |
1101 | } | 1154 | } |
@@ -1104,15 +1157,16 @@ mode_in_range(struct drm_display_mode *mode, struct detailed_timing *timing) | |||
1104 | * XXX If drm_dmt_modes ever regrows the CVT-R modes (and it will) this will | 1157 | * XXX If drm_dmt_modes ever regrows the CVT-R modes (and it will) this will |
1105 | * need to account for them. | 1158 | * need to account for them. |
1106 | */ | 1159 | */ |
1107 | static int drm_gtf_modes_for_range(struct drm_connector *connector, | 1160 | static int |
1108 | struct detailed_timing *timing) | 1161 | drm_gtf_modes_for_range(struct drm_connector *connector, struct edid *edid, |
1162 | struct detailed_timing *timing) | ||
1109 | { | 1163 | { |
1110 | int i, modes = 0; | 1164 | int i, modes = 0; |
1111 | struct drm_display_mode *newmode; | 1165 | struct drm_display_mode *newmode; |
1112 | struct drm_device *dev = connector->dev; | 1166 | struct drm_device *dev = connector->dev; |
1113 | 1167 | ||
1114 | for (i = 0; i < drm_num_dmt_modes; i++) { | 1168 | for (i = 0; i < drm_num_dmt_modes; i++) { |
1115 | if (mode_in_range(drm_dmt_modes + i, timing)) { | 1169 | if (mode_in_range(drm_dmt_modes + i, edid, timing)) { |
1116 | newmode = drm_mode_duplicate(dev, &drm_dmt_modes[i]); | 1170 | newmode = drm_mode_duplicate(dev, &drm_dmt_modes[i]); |
1117 | if (newmode) { | 1171 | if (newmode) { |
1118 | drm_mode_probed_add(connector, newmode); | 1172 | drm_mode_probed_add(connector, newmode); |
@@ -1288,7 +1342,8 @@ static int add_detailed_modes(struct drm_connector *connector, | |||
1288 | switch (data->type) { | 1342 | switch (data->type) { |
1289 | case EDID_DETAIL_MONITOR_RANGE: | 1343 | case EDID_DETAIL_MONITOR_RANGE: |
1290 | if (gtf) | 1344 | if (gtf) |
1291 | modes += drm_gtf_modes_for_range(connector, timing); | 1345 | modes += drm_gtf_modes_for_range(connector, edid, |
1346 | timing); | ||
1292 | break; | 1347 | break; |
1293 | case EDID_DETAIL_STD_MODES: | 1348 | case EDID_DETAIL_STD_MODES: |
1294 | /* Six modes per detailed section */ | 1349 | /* Six modes per detailed section */ |