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