diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c index 6e999bd039359eb9fb661c017049c58f2a55f359..3924a7bf271af0a548cc6af5272c3b4c692f37ff 100644 --- a/drivers/gpu/drm/drm_edid.c +++ b/drivers/gpu/drm/drm_edid.c @@ -64,7 +64,8 @@ #define LEVEL_DMT 0 #define LEVEL_GTF 1 -#define LEVEL_CVT 2 +#define LEVEL_GTF2 2 +#define LEVEL_CVT 3 static struct edid_quirk { char *vendor; @@ -713,6 +714,71 @@ drm_monitor_supports_rb(struct edid *edid) return ((edid->input & DRM_EDID_INPUT_DIGITAL) != 0); } +static void +find_gtf2(struct detailed_timing *t, void *data) +{ + u8 *r = (u8 *)t; + if (r[3] == EDID_DETAIL_MONITOR_RANGE && r[10] == 0x02) + *(u8 **)data = r; +} + +/* Secondary GTF curve kicks in above some break frequency */ +static int +drm_gtf2_hbreak(struct edid *edid) +{ + u8 *r = NULL; + drm_for_each_detailed_block((u8 *)edid, find_gtf2, &r); + return r ? (r[12] * 2) : 0; +} + +static int +drm_gtf2_2c(struct edid *edid) +{ + u8 *r = NULL; + drm_for_each_detailed_block((u8 *)edid, find_gtf2, &r); + return r ? r[13] : 0; +} + +static int +drm_gtf2_m(struct edid *edid) +{ + u8 *r = NULL; + drm_for_each_detailed_block((u8 *)edid, find_gtf2, &r); + return r ? (r[15] << 8) + r[14] : 0; +} + +static int +drm_gtf2_k(struct edid *edid) +{ + u8 *r = NULL; + drm_for_each_detailed_block((u8 *)edid, find_gtf2, &r); + return r ? r[16] : 0; +} + +static int +drm_gtf2_2j(struct edid *edid) +{ + u8 *r = NULL; + drm_for_each_detailed_block((u8 *)edid, find_gtf2, &r); + return r ? r[17] : 0; +} + +/** + * standard_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; + if (drm_gtf2_hbreak(edid)) + return LEVEL_GTF2; + return LEVEL_GTF; + } + return LEVEL_DMT; +} + /* * 0 is reserved. The spec says 0x01 fill for unused timings. Some old * monitors fill with ascii space (0x20) instead. @@ -734,8 +800,8 @@ bad_std_timing(u8 a, u8 b) * and convert them into a real mode using CVT/GTF/DMT. */ static struct drm_display_mode * -drm_mode_std(struct drm_connector *connector, struct std_timing *t, - int revision, int timing_level) +drm_mode_std(struct drm_connector *connector, struct edid *edid, + struct std_timing *t, int revision) { struct drm_device *dev = connector->dev; struct drm_display_mode *m, *mode = NULL; @@ -745,6 +811,7 @@ drm_mode_std(struct drm_connector *connector, struct std_timing *t, >> EDID_TIMING_ASPECT_SHIFT; unsigned vfreq = (t->vfreq_aspect & EDID_TIMING_VFREQ_MASK) >> EDID_TIMING_VFREQ_SHIFT; + int timing_level = standard_timing_level(edid); if (bad_std_timing(t->hsize, t->vfreq_aspect)) return NULL; @@ -806,6 +873,23 @@ drm_mode_std(struct drm_connector *connector, struct std_timing *t, case LEVEL_GTF: mode = drm_gtf_mode(dev, hsize, vsize, vrefresh_rate, 0, 0); break; + case LEVEL_GTF2: + /* + * This is potentially wrong if there's ever a monitor with + * more than one ranges section, each claiming a different + * secondary GTF curve. Please don't do that. + */ + mode = drm_gtf_mode(dev, hsize, vsize, vrefresh_rate, 0, 0); + if (drm_mode_hsync(mode) > drm_gtf2_hbreak(edid)) { + kfree(mode); + mode = drm_gtf_mode_complex(dev, hsize, vsize, + vrefresh_rate, 0, 0, + drm_gtf2_m(edid), + drm_gtf2_2c(edid), + drm_gtf2_k(edid), + drm_gtf2_2j(edid)); + } + break; case LEVEL_CVT: mode = drm_cvt_mode(dev, hsize, vsize, vrefresh_rate, 0, 0, false); @@ -1042,19 +1126,6 @@ static int add_established_modes(struct drm_connector *connector, struct edid *e return modes; } -/** - * 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 @@ -1066,15 +1137,13 @@ static int standard_timing_level(struct edid *edid) static int add_standard_modes(struct drm_connector *connector, struct edid *edid) { int i, modes = 0; - int timing_level; - - timing_level = standard_timing_level(edid); for (i = 0; i < EDID_STD_TIMINGS; i++) { struct drm_display_mode *newmode; - newmode = drm_mode_std(connector, &edid->standard_timings[i], - edid->revision, timing_level); + newmode = drm_mode_std(connector, edid, + &edid->standard_timings[i], + edid->revision); if (newmode) { drm_mode_probed_add(connector, newmode); modes++; @@ -1140,9 +1209,6 @@ range_pixel_clock(struct edid *edid, u8 *t) return t[9] * 10000 + 5001; } -/* - * XXX fix this for GTF secondary curve formula - */ static bool mode_in_range(struct drm_display_mode *mode, struct edid *edid, struct detailed_timing *timing) @@ -1339,7 +1405,6 @@ 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; @@ -1370,8 +1435,8 @@ static int add_detailed_modes(struct drm_connector *connector, struct drm_display_mode *newmode; std = &data->data.timings[i]; - newmode = drm_mode_std(connector, std, edid->revision, - timing_level); + newmode = drm_mode_std(connector, edid, std, + edid->revision); if (newmode) { drm_mode_probed_add(connector, newmode); modes++; diff --git a/drivers/gpu/drm/drm_modes.c b/drivers/gpu/drm/drm_modes.c index 76d63394c77696065fabcb77940b015a8af1d327..d460b6c472d4f4ad2e33aa79afe58f9131d8978b 100644 --- a/drivers/gpu/drm/drm_modes.c +++ b/drivers/gpu/drm/drm_modes.c @@ -276,35 +276,29 @@ struct drm_display_mode *drm_cvt_mode(struct drm_device *dev, int hdisplay, EXPORT_SYMBOL(drm_cvt_mode); /** - * drm_gtf_mode - create the modeline based on GTF algorithm + * drm_gtf_mode_complex - create the modeline based on full GTF algorithm * * @dev :drm device * @hdisplay :hdisplay size * @vdisplay :vdisplay size * @vrefresh :vrefresh rate. * @interlaced :whether the interlace is supported - * @margins :whether the margin is supported + * @margins :desired margin size + * @GTF_[MCKJ] :extended GTF formula parameters * * LOCKING. * none. * - * return the modeline based on GTF algorithm - * - * This function is to create the modeline based on the GTF algorithm. - * Generalized Timing Formula is derived from: - * GTF Spreadsheet by Andy Morrish (1/5/97) - * available at http://www.vesa.org + * return the modeline based on full GTF algorithm. * - * And it is copied from the file of xserver/hw/xfree86/modes/xf86gtf.c. - * What I have done is to translate it by using integer calculation. - * I also refer to the function of fb_get_mode in the file of - * drivers/video/fbmon.c + * GTF feature blocks specify C and J in multiples of 0.5, so we pass them + * in here multiplied by two. For a C of 40, pass in 80. */ -struct drm_display_mode *drm_gtf_mode(struct drm_device *dev, int hdisplay, - int vdisplay, int vrefresh, - bool interlaced, int margins) -{ - /* 1) top/bottom margin size (% of height) - default: 1.8, */ +struct drm_display_mode * +drm_gtf_mode_complex(struct drm_device *dev, int hdisplay, int vdisplay, + int vrefresh, bool interlaced, int margins, + int GTF_M, int GTF_2C, int GTF_K, int GTF_2J) +{ /* 1) top/bottom margin size (% of height) - default: 1.8, */ #define GTF_MARGIN_PERCENTAGE 18 /* 2) character cell horizontal granularity (pixels) - default 8 */ #define GTF_CELL_GRAN 8 @@ -316,17 +310,9 @@ struct drm_display_mode *drm_gtf_mode(struct drm_device *dev, int hdisplay, #define H_SYNC_PERCENT 8 /* min time of vsync + back porch (microsec) */ #define MIN_VSYNC_PLUS_BP 550 - /* blanking formula gradient */ -#define GTF_M 600 - /* blanking formula offset */ -#define GTF_C 40 - /* blanking formula scaling factor */ -#define GTF_K 128 - /* blanking formula scaling factor */ -#define GTF_J 20 /* C' and M' are part of the Blanking Duty Cycle computation */ -#define GTF_C_PRIME (((GTF_C - GTF_J) * GTF_K / 256) + GTF_J) -#define GTF_M_PRIME (GTF_K * GTF_M / 256) +#define GTF_C_PRIME ((((GTF_2C - GTF_2J) * GTF_K / 256) + GTF_2J) / 2) +#define GTF_M_PRIME (GTF_K * GTF_M / 256) struct drm_display_mode *drm_mode; unsigned int hdisplay_rnd, vdisplay_rnd, vfieldrate_rqd; int top_margin, bottom_margin; @@ -470,7 +456,48 @@ struct drm_display_mode *drm_gtf_mode(struct drm_device *dev, int hdisplay, return drm_mode; } +EXPORT_SYMBOL(drm_gtf_mode_complex); + +/** + * drm_gtf_mode - create the modeline based on GTF algorithm + * + * @dev :drm device + * @hdisplay :hdisplay size + * @vdisplay :vdisplay size + * @vrefresh :vrefresh rate. + * @interlaced :whether the interlace is supported + * @margins :whether the margin is supported + * + * LOCKING. + * none. + * + * return the modeline based on GTF algorithm + * + * This function is to create the modeline based on the GTF algorithm. + * Generalized Timing Formula is derived from: + * GTF Spreadsheet by Andy Morrish (1/5/97) + * available at http://www.vesa.org + * + * And it is copied from the file of xserver/hw/xfree86/modes/xf86gtf.c. + * What I have done is to translate it by using integer calculation. + * I also refer to the function of fb_get_mode in the file of + * drivers/video/fbmon.c + * + * Standard GTF parameters: + * M = 600 + * C = 40 + * K = 128 + * J = 20 + */ +struct drm_display_mode * +drm_gtf_mode(struct drm_device *dev, int hdisplay, int vdisplay, int vrefresh, + bool lace, int margins) +{ + return drm_gtf_mode_complex(dev, hdisplay, vdisplay, vrefresh, lace, + margins, 600, 40 * 2, 128, 20 * 2); +} EXPORT_SYMBOL(drm_gtf_mode); + /** * drm_mode_set_name - set the name on a mode * @mode: name will be set in this mode diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h index f74523a299c99b8ce1cc256dd669fb772fa7f666..8eb3630ee67d78f7db5b3d69f0270569b7b64f78 100644 --- a/include/drm/drm_crtc.h +++ b/include/drm/drm_crtc.h @@ -797,6 +797,10 @@ extern struct drm_display_mode *drm_cvt_mode(struct drm_device *dev, extern struct drm_display_mode *drm_gtf_mode(struct drm_device *dev, int hdisplay, int vdisplay, int vrefresh, bool interlaced, int margins); +extern struct drm_display_mode *drm_gtf_mode_complex(struct drm_device *dev, + int hdisplay, int vdisplay, int vrefresh, + bool interlaced, int margins, int GTF_M, + int GTF_2C, int GTF_K, int GTF_2J); extern int drm_add_modes_noedid(struct drm_connector *connector, int hdisplay, int vdisplay);