[3/3] DRM: Get the modeline for standard timing in EDID by using CVT/GTF
diff mbox

Message ID 1248253005-10337-3-git-send-email-yakui.zhao@intel.com
State Not Applicable
Headers show

Commit Message

Zhao, Yakui July 22, 2009, 8:56 a.m. UTC
From: Zhao Yakui <yakui.zhao@intel.com>

   Create the standard timing modeline by using CVT/GFT algorithm while
interpreting the EDID.

   In course of interpreting the EDID, the timing level will be obtained,
which is used to determine whether the CVT/GTF algorithm is selected to
generate the modeline for the given hdisplay/vdisplay/vfresh_rate.
   In the UMS mode firstly it will check whether it can be found in
the DMT table. If it can be found, then the modeline is returned. Then the
timing_level is used to choose CVT/GTF.
   As there is no DMT table, no modeline is returned when timing level
is DMT. For the other two timing levels, the CVT/GTF will be called to
generate the required standard timing modeline.

Signed-off-by: Zhao Yakui <yakui.zhao@intel.com>
---
 drivers/gpu/drm/drm_edid.c |   71 ++++++++++++++++++++++++++++++++++++++-----
 1 files changed, 62 insertions(+), 9 deletions(-)

Patch
diff mbox

diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c
index 80cc6d0..917ed65 100644
--- a/drivers/gpu/drm/drm_edid.c
+++ b/drivers/gpu/drm/drm_edid.c
@@ -61,6 +61,10 @@ 
 /* use +hsync +vsync for detailed mode */
 #define EDID_QUIRK_DETAILED_SYNC_PP		(1 << 6)
 
+#define LEVEL_DMT	0
+#define LEVEL_GTF	1
+#define	LEVEL_CVT	2
+
 static struct edid_quirk {
 	char *vendor;
 	int product_id;
@@ -240,24 +244,29 @@  static void edid_fixup_preferred(struct drm_connector *connector,
 /**
  * drm_mode_std - convert standard mode info (width, height, refresh) into mode
  * @t: standard timing params
+ * @timing_level: standard timing level(CVT/GTF/DMT)
  *
  * Take the standard timing params (in this case width, aspect, and refresh)
- * and convert them into a real mode using CVT.
+ * and convert them into a real mode using CVT/GTF/DMT.
  *
  * Punts for now, but should eventually use the FB layer's CVT based mode
  * generation code.
  */
 struct drm_display_mode *drm_mode_std(struct drm_device *dev,
-				      struct std_timing *t)
+				      struct std_timing *t,
+				      int timing_level)
 {
 	struct drm_display_mode *mode;
-	int hsize = t->hsize * 8 + 248, vsize;
+	int hsize, vsize;
+	int vfresh_rate;
 	unsigned aspect_ratio = (t->vfreq_aspect & EDID_TIMING_ASPECT_MASK)
 		>> EDID_TIMING_ASPECT_SHIFT;
 
-	mode = drm_mode_create(dev);
-	if (!mode)
-		return NULL;
+	/* According to the EDID spec, the hdisplay = hsize * 8 + 248 */
+	hsize = t->hsize * 8 + 248;
+	/* vfresh_rate = t->vfreq_aspect + 60 */
+	vfresh_rate = t->vfreq_aspect & EDID_TIMING_VFREQ_MASK;
+	vfresh_rate += 60;
 
 	if (aspect_ratio == 0)
 		vsize = (hsize * 10) / 16;
@@ -268,7 +277,29 @@  struct drm_display_mode *drm_mode_std(struct drm_device *dev,
 	else
 		vsize = (hsize * 9) / 16;
 
-	drm_mode_set_name(mode);
+	/* Any of hsize/vsize/vfresh_rate is zero, return NULL */
+	if (!hsize || !vsize || !vfresh_rate)
+		return NULL;
+	/*
+	 * Should we check whether the given mode can be found in
+	 * the default standard mode. If so, please add the default
+	 * standard mode table and return the required modelines
+	 */
+	mode = NULL;
+	switch (timing_level) {
+	case LEVEL_DMT:
+		/* When the timing level is DMT, return NULL */
+		/* If it is incorrect, please fix me */
+		break;
+	case LEVEL_GTF:
+		mode = drm_gtf_mode(dev, hsize, vsize, vfresh_rate,
+					0, 0);
+		break;
+	case LEVEL_CVT:
+		mode = drm_cvt_mode(dev, hsize, vsize, vfresh_rate,
+					0, 0);
+		break;
+	}
 
 	return mode;
 }
@@ -453,6 +484,21 @@  static int add_established_modes(struct drm_connector *connector, struct edid *e
 }
 
 /**
+ * stanard_timing_level - get std. timing level(CVT/GTF/DMT)
+ * @edid: EDID block to scan
+ */
+static int standard_timing_level(struct edid *edid)
+{
+	if (edid->revision >= 2) {
+		if (edid->revision >= 4 &&
+			(edid->features & DRM_EDID_FEATURE_DEFAULT_GTF))
+			return LEVEL_CVT;
+		return LEVEL_GTF;
+	}
+	return LEVEL_DMT;
+}
+
+/**
  * add_standard_modes - get std. modes from EDID and add them
  * @edid: EDID block to scan
  *
@@ -463,6 +509,9 @@  static int add_standard_modes(struct drm_connector *connector, struct edid *edid
 {
 	struct drm_device *dev = connector->dev;
 	int i, modes = 0;
+	int timing_level;
+
+	timing_level = standard_timing_level(edid);
 
 	for (i = 0; i < EDID_STD_TIMINGS; i++) {
 		struct std_timing *t = &edid->standard_timings[i];
@@ -472,7 +521,8 @@  static int add_standard_modes(struct drm_connector *connector, struct edid *edid
 		if (t->hsize == 1 && t->vfreq_aspect == 1)
 			continue;
 
-		newmode = drm_mode_std(dev, &edid->standard_timings[i]);
+		newmode = drm_mode_std(dev, &edid->standard_timings[i],
+					timing_level);
 		if (newmode) {
 			drm_mode_probed_add(connector, newmode);
 			modes++;
@@ -496,6 +546,9 @@  static int add_detailed_info(struct drm_connector *connector,
 {
 	struct drm_device *dev = connector->dev;
 	int i, j, modes = 0;
+	int timing_level;
+
+	timing_level = standard_timing_level(edid);
 
 	for (i = 0; i < EDID_DETAILED_TIMINGS; i++) {
 		struct detailed_timing *timing = &edid->detailed_timings[i];
@@ -541,7 +594,7 @@  static int add_detailed_info(struct drm_connector *connector,
 				struct drm_display_mode *newmode;
 
 				std = &data->data.timings[j];
-				newmode = drm_mode_std(dev, std);
+				newmode = drm_mode_std(dev, std, timing_level);
 				if (newmode) {
 					drm_mode_probed_add(connector, newmode);
 					modes++;