summaryrefslogtreecommitdiffstats
path: root/drivers/gpu/drm/drm_edid.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/gpu/drm/drm_edid.c')
-rw-r--r--drivers/gpu/drm/drm_edid.c69
1 files changed, 65 insertions, 4 deletions
diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c
index 999571ab0b6..cc8e6968271 100644
--- a/drivers/gpu/drm/drm_edid.c
+++ b/drivers/gpu/drm/drm_edid.c
@@ -491,16 +491,17 @@ static struct drm_display_mode drm_dmt_modes[] = {
3048, 3536, 0, 1600, 1603, 1609, 1682, 0,
DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
};
+static const int drm_num_dmt_modes =
+ sizeof(drm_dmt_modes) / sizeof(struct drm_display_mode);
static struct drm_display_mode *drm_find_dmt(struct drm_device *dev,
int hsize, int vsize, int fresh)
{
- int i, count;
+ int i;
struct drm_display_mode *ptr, *mode;
- count = sizeof(drm_dmt_modes) / sizeof(struct drm_display_mode);
mode = NULL;
- for (i = 0; i < count; i++) {
+ for (i = 0; i < drm_num_dmt_modes; i++) {
ptr = &drm_dmt_modes[i];
if (hsize == ptr->hdisplay &&
vsize == ptr->vdisplay &&
@@ -838,6 +839,64 @@ static int add_standard_modes(struct drm_connector *connector, struct edid *edid
return modes;
}
+/*
+ * XXX fix this for:
+ * - GTF secondary curve formula
+ * - EDID 1.4 range offsets
+ * - CVT extended bits
+ */
+static bool
+mode_in_range(struct drm_display_mode *mode, struct detailed_timing *timing)
+{
+ struct detailed_data_monitor_range *range;
+ int hsync, vrefresh;
+
+ range = &timing->data.other_data.data.range;
+
+ hsync = drm_mode_hsync(mode);
+ vrefresh = drm_mode_vrefresh(mode);
+
+ if (hsync < range->min_hfreq_khz || hsync > range->max_hfreq_khz)
+ return false;
+
+ if (vrefresh < range->min_vfreq || vrefresh > range->max_vfreq)
+ return false;
+
+ if (range->pixel_clock_mhz && range->pixel_clock_mhz != 0xff) {
+ /* be forgiving since it's in units of 10MHz */
+ int max_clock = range->pixel_clock_mhz * 10 + 9;
+ max_clock *= 1000;
+ if (mode->clock > max_clock)
+ return false;
+ }
+
+ return true;
+}
+
+/*
+ * XXX If drm_dmt_modes ever regrows the CVT-R modes (and it will) this will
+ * need to account for them.
+ */
+static int drm_gtf_modes_for_range(struct drm_connector *connector,
+ struct detailed_timing *timing)
+{
+ int i, modes = 0;
+ struct drm_display_mode *newmode;
+ struct drm_device *dev = connector->dev;
+
+ for (i = 0; i < drm_num_dmt_modes; i++) {
+ if (mode_in_range(drm_dmt_modes + i, timing)) {
+ newmode = drm_mode_duplicate(dev, &drm_dmt_modes[i]);
+ if (newmode) {
+ drm_mode_probed_add(connector, newmode);
+ modes++;
+ }
+ }
+ }
+
+ return modes;
+}
+
static int add_detailed_modes(struct drm_connector *connector,
struct detailed_timing *timing,
struct edid *edid, u32 quirks, int preferred)
@@ -845,6 +904,7 @@ static int add_detailed_modes(struct drm_connector *connector,
int i, modes = 0;
struct detailed_non_pixel *data = &timing->data.other_data;
int timing_level = standard_timing_level(edid);
+ int gtf = (edid->features & DRM_EDID_FEATURE_DEFAULT_GTF);
struct drm_display_mode *newmode;
struct drm_device *dev = connector->dev;
@@ -863,7 +923,8 @@ static int add_detailed_modes(struct drm_connector *connector,
/* other timing types */
switch (data->type) {
case EDID_DETAIL_MONITOR_RANGE:
- /* Get monitor range data */
+ if (gtf)
+ modes += drm_gtf_modes_for_range(connector, timing);
break;
case EDID_DETAIL_STD_MODES:
/* Six modes per detailed section */