1/* 2 * Copyright 2005-2006 Luc Verhaegen. 3 * Copyright © 2021 Red Hat, Inc. 4 * 5 * Permission is hereby granted, free of charge, to any person obtaining a 6 * copy of this software and associated documentation files (the "Software"), 7 * to deal in the Software without restriction, including without limitation 8 * the rights to use, copy, modify, merge, publish, distribute, sublicense, 9 * and/or sell copies of the Software, and to permit persons to whom the 10 * Software is furnished to do so, subject to the following conditions: 11 * 12 * The above copyright notice and this permission notice shall be included in 13 * all copies or substantial portions of the Software. 14 * 15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 18 * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR 19 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 20 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 21 * OTHER DEALINGS IN THE SOFTWARE. 22 * 23 */ 24 25/* Standalone VESA CVT standard timing modelines generator. */ 26 27 28#include <string.h> 29#include <stdio.h> 30#include <stdlib.h> 31 32#include <libxcvt/libxcvt.h> 33 34/* 35 * Generate a CVT standard mode from hdisplay, vdisplay and vrefresh. 36 * 37 * These calculations are stolen from the CVT calculation spreadsheet written 38 * by Graham Loveridge. He seems to be claiming no copyright and there seems to 39 * be no license attached to this. He apparently just wants to see his name 40 * mentioned. 41 * 42 * This file can be found at http://www.vesa.org/Public/CVT/CVTd6r1.xls 43 * 44 * Comments and structure corresponds to the comments and structure of the xls. 45 * This should ease importing of future changes to the standard (not very 46 * likely though). 47 * 48 * About margins; i'm sure that they are to be the bit between HDisplay and 49 * HBlankStart, HBlankEnd and HTotal, VDisplay and VBlankStart, VBlankEnd and 50 * VTotal, where the overscan colour is shown. FB seems to call _all_ blanking 51 * outside sync "margin" for some reason. Since we prefer seeing proper 52 * blanking instead of the overscan colour, and since the Crtc* values will 53 * probably get altered after us, we will disable margins altogether. With 54 * these calculations, Margins will plainly expand H/VDisplay, and we don't 55 * want that. -- libv 56 * 57 */ 58struct libxcvt_mode_info * 59libxcvt_gen_mode_info(int hdisplay, int vdisplay, float vrefresh, bool reduced, bool interlaced) 60{ 61 bool margins = false; 62 float vfield_rate, hperiod; 63 int hdisplay_rnd, hmargin; 64 int vdisplay_rnd, vmargin, vsync; 65 float interlace; /* Please rename this */ 66 struct libxcvt_mode_info *mode_info; 67 68 mode_info = calloc(1, sizeof *mode_info); 69 if (!mode_info) 70 return NULL; 71 72 mode_info->hdisplay = hdisplay; 73 mode_info->vdisplay = vdisplay; 74 mode_info->vrefresh = vrefresh; 75 76 /* 1) top/bottom margin size (% of height) - default: 1.8 */ 77#define CVT_MARGIN_PERCENTAGE 1.8 78 79 /* 2) character cell horizontal granularity (pixels) - default 8 */ 80#define CVT_H_GRANULARITY 8 81 82 /* 4) Minimum vertical front porch (lines) - default 3 */ 83#define CVT_MIN_V_PORCH_RND 3 84 85 /* 4) Minimum number of vertical back porch lines - default 6 */ 86#define CVT_MIN_V_BPORCH 6 87 88 /* Pixel Clock step (kHz) */ 89#define CVT_CLOCK_STEP 250 90 91 /* CVT default is 60.0Hz */ 92 if (!mode_info->vrefresh) 93 mode_info->vrefresh = 60.0; 94 95 /* 1. Required field rate */ 96 if (interlaced) 97 vfield_rate = mode_info->vrefresh * 2; 98 else 99 vfield_rate = mode_info->vrefresh; 100 101 /* 2. Horizontal pixels */ 102 hdisplay_rnd = mode_info->hdisplay - (mode_info->hdisplay % CVT_H_GRANULARITY); 103 104 /* 3. Determine left and right borders */ 105 if (margins) { 106 /* right margin is actually exactly the same as left */ 107 hmargin = (((float) hdisplay_rnd) * CVT_MARGIN_PERCENTAGE / 100.0); 108 hmargin -= hmargin % CVT_H_GRANULARITY; 109 } 110 else { 111 hmargin = 0; 112 } 113 114 /* 4. Find total active pixels */ 115 mode_info->hdisplay = hdisplay_rnd + 2 * hmargin; 116 117 /* 5. Find number of lines per field */ 118 if (interlaced) 119 vdisplay_rnd = mode_info->vdisplay / 2; 120 else 121 vdisplay_rnd = mode_info->vdisplay; 122 123 /* 6. Find top and bottom margins */ 124 /* nope. */ 125 if (margins) 126 /* top and bottom margins are equal again. */ 127 vmargin = (((float) vdisplay_rnd) * CVT_MARGIN_PERCENTAGE / 100.0); 128 else 129 vmargin = 0; 130 131 mode_info->vdisplay = mode_info->vdisplay + 2 * vmargin; 132 133 /* 7. interlace */ 134 if (interlaced) 135 interlace = 0.5; 136 else 137 interlace = 0.0; 138 139 /* Determine vsync Width from aspect ratio */ 140 if (!(mode_info->vdisplay % 3) && ((mode_info->vdisplay * 4 / 3) == mode_info->hdisplay)) 141 vsync = 4; 142 else if (!(mode_info->vdisplay % 9) && ((mode_info->vdisplay * 16 / 9) == mode_info->hdisplay)) 143 vsync = 5; 144 else if (!(mode_info->vdisplay % 10) && ((mode_info->vdisplay * 16 / 10) == mode_info->hdisplay)) 145 vsync = 6; 146 else if (!(mode_info->vdisplay % 4) && ((mode_info->vdisplay * 5 / 4) == mode_info->hdisplay)) 147 vsync = 7; 148 else if (!(mode_info->vdisplay % 9) && ((mode_info->vdisplay * 15 / 9) == mode_info->hdisplay)) 149 vsync = 7; 150 else /* Custom */ 151 vsync = 10; 152 153 if (!reduced) { /* simplified GTF calculation */ 154 155 /* 4) Minimum time of vertical sync + back porch interval (µs) 156 * default 550.0 */ 157#define CVT_MIN_VSYNC_BP 550.0 158 159 /* 3) Nominal HSync width (% of line period) - default 8 */ 160#define CVT_HSYNC_PERCENTAGE 8 161 162 float hblank_percentage; 163 int vsync_and_back_porch, vback_porch; 164 int hblank, hsync_w; 165 166 /* 8. Estimated Horizontal period */ 167 hperiod = ((float) (1000000.0 / vfield_rate - CVT_MIN_VSYNC_BP)) / 168 (vdisplay_rnd + 2 * vmargin + CVT_MIN_V_PORCH_RND + interlace); 169 170 /* 9. Find number of lines in sync + backporch */ 171 if (((int) (CVT_MIN_VSYNC_BP / hperiod) + 1) < 172 (vsync + CVT_MIN_V_BPORCH)) 173 vsync_and_back_porch = vsync + CVT_MIN_V_BPORCH; 174 else 175 vsync_and_back_porch = (int) (CVT_MIN_VSYNC_BP / hperiod) + 1; 176 177 /* 10. Find number of lines in back porch */ 178 vback_porch = vsync_and_back_porch - vsync; 179 (void) vback_porch; 180 181 /* 11. Find total number of lines in vertical field */ 182 mode_info->vtotal = 183 vdisplay_rnd + 2 * vmargin + vsync_and_back_porch + interlace + 184 CVT_MIN_V_PORCH_RND; 185 186 /* 5) Definition of Horizontal blanking time limitation */ 187 /* Gradient (%/kHz) - default 600 */ 188#define CVT_M_FACTOR 600 189 190 /* Offset (%) - default 40 */ 191#define CVT_C_FACTOR 40 192 193 /* Blanking time scaling factor - default 128 */ 194#define CVT_K_FACTOR 128 195 196 /* Scaling factor weighting - default 20 */ 197#define CVT_J_FACTOR 20 198 199#define CVT_M_PRIME CVT_M_FACTOR * CVT_K_FACTOR / 256 200#define CVT_C_PRIME (CVT_C_FACTOR - CVT_J_FACTOR) * CVT_K_FACTOR / 256 + \ 201 CVT_J_FACTOR 202 203 /* 12. Find ideal blanking duty cycle from formula */ 204 hblank_percentage = CVT_C_PRIME - CVT_M_PRIME * hperiod / 1000.0; 205 206 /* 13. Blanking time */ 207 if (hblank_percentage < 20) 208 hblank_percentage = 20; 209 210 hblank = mode_info->hdisplay * hblank_percentage / (100.0 - hblank_percentage); 211 hblank -= hblank % (2 * CVT_H_GRANULARITY); 212 213 /* 14. Find total number of pixels in a line. */ 214 mode_info->htotal = mode_info->hdisplay + hblank; 215 216 /* Fill in HSync values */ 217 mode_info->hsync_end = mode_info->hdisplay + hblank / 2; 218 219 hsync_w = (mode_info->htotal * CVT_HSYNC_PERCENTAGE) / 100; 220 hsync_w -= hsync_w % CVT_H_GRANULARITY; 221 mode_info->hsync_start = mode_info->hsync_end - hsync_w; 222 223 /* Fill in vsync values */ 224 mode_info->vsync_start = mode_info->vdisplay + CVT_MIN_V_PORCH_RND; 225 mode_info->vsync_end = mode_info->vsync_start + vsync; 226 227 } 228 else { /* reduced blanking */ 229 /* Minimum vertical blanking interval time (µs) - default 460 */ 230#define CVT_RB_MIN_VBLANK 460.0 231 232 /* Fixed number of clocks for horizontal sync */ 233#define CVT_RB_H_SYNC 32.0 234 235 /* Fixed number of clocks for horizontal blanking */ 236#define CVT_RB_H_BLANK 160.0 237 238 /* Fixed number of lines for vertical front porch - default 3 */ 239#define CVT_RB_VFPORCH 3 240 241 int vblank_interval_lines; 242 243 /* 8. Estimate Horizontal period. */ 244 hperiod = ((float) (1000000.0 / vfield_rate - CVT_RB_MIN_VBLANK)) / 245 (vdisplay_rnd + 2 * vmargin); 246 247 /* 9. Find number of lines in vertical blanking */ 248 vblank_interval_lines = ((float) CVT_RB_MIN_VBLANK) / hperiod + 1; 249 250 /* 10. Check if vertical blanking is sufficient */ 251 if (vblank_interval_lines < (CVT_RB_VFPORCH + vsync + CVT_MIN_V_BPORCH)) 252 vblank_interval_lines = CVT_RB_VFPORCH + vsync + CVT_MIN_V_BPORCH; 253 254 /* 11. Find total number of lines in vertical field */ 255 mode_info->vtotal = vdisplay_rnd + 2 * vmargin + interlace + vblank_interval_lines; 256 257 /* 12. Find total number of pixels in a line */ 258 mode_info->htotal = mode_info->hdisplay + CVT_RB_H_BLANK; 259 260 /* Fill in HSync values */ 261 mode_info->hsync_end = mode_info->hdisplay + CVT_RB_H_BLANK / 2; 262 mode_info->hsync_start = mode_info->hsync_end - CVT_RB_H_SYNC; 263 264 /* Fill in vsync values */ 265 mode_info->vsync_start = mode_info->vdisplay + CVT_RB_VFPORCH; 266 mode_info->vsync_end = mode_info->vsync_start + vsync; 267 } 268 269 /* 15/13. Find pixel clock frequency (kHz for xf86) */ 270 mode_info->dot_clock = mode_info->htotal * 1000.0 / hperiod; 271 mode_info->dot_clock -= mode_info->dot_clock % CVT_CLOCK_STEP; 272 273 /* 16/14. Find actual Horizontal Frequency (kHz) */ 274 mode_info->hsync = ((float) mode_info->dot_clock) / ((float) mode_info->htotal); 275 276 /* 17/15. Find actual Field rate */ 277 mode_info->vrefresh = (1000.0 * ((float) mode_info->dot_clock)) / 278 ((float) (mode_info->htotal * mode_info->vtotal)); 279 280 /* 18/16. Find actual vertical frame frequency */ 281 /* ignore - just set the mode flag for interlaced */ 282 if (interlaced) 283 mode_info->vtotal *= 2; 284 285 if (reduced) 286 mode_info->mode_flags |= LIBXCVT_MODE_FLAG_HSYNC_POSITIVE | LIBXCVT_MODE_FLAG_VSYNC_NEGATIVE; 287 else 288 mode_info->mode_flags |= LIBXCVT_MODE_FLAG_HSYNC_NEGATIVE | LIBXCVT_MODE_FLAG_VSYNC_POSITIVE; 289 290 if (interlaced) 291 mode_info->mode_flags |= LIBXCVT_MODE_FLAG_INTERLACE; 292 293 /* FWXGA hack adapted from hw/xfree86/modes/xf86EdidModes.c, because you can't say 1366 */ 294 if (mode_info->hdisplay == 1360 && mode_info->vdisplay == 768) { 295 mode_info->hdisplay = 1366; 296 mode_info->hsync_start--; 297 mode_info->hsync_end--; 298 } 299 300 return mode_info; 301} 302