/* Copyright 2016 Castle Technology Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include <stdio.h> #include <stdint.h> #include <stdbool.h> #include <stdlib.h> #include <string.h> #include <math.h> #include "kernel.h" #include "Global/VIDCList.h" #include "Global/VduExt.h" #include "modex.h" /* Put this file in the 'c' directory of ScrModes and do * cc -c -o testfp.o testfp.c * cc -c -o tables.o tables.c * link -o test testfp.o tables.o C:o.stubs * run the test and look at the results. */ static int timing_support = EDID_USE_CVT; static int count, countall; static void comparetimings(ModeDescriptionRef a, ModeDescriptionRef b, int rate) { size_t i; bool same = true; int error; countall++; for (i = 0; i < FR__COUNT; i++) { if ((a->definition.timings.vpar[i] != b->definition.timings.vpar[i]) || (a->definition.timings.vpar[i] != b->definition.timings.vpar[i])) { same = false; break; } } /* Allow 0.5% tolerance on pixel clock (CVT 3.3.1) so we can compare * rational integer fractions with irrational float fractions. */ error = (1000 * a->definition.timings.pixel_khz) / b->definition.timings.pixel_khz; error = 1000 - error; if (abs(error) > 5) same = false; if (same) { count++; return; } printf("Mode differs for %dx%d @ %d\n", a->definition.timings.xres, a->definition.timings.yres, rate); printf("pclk %d %d\n", a->definition.timings.pixel_khz, b->definition.timings.pixel_khz); for (i = 0; i < FR__COUNT; i++) { printf("hpar %d %d, vpar %d %d\n", a->definition.timings.hpar[i], b->definition.timings.hpar[i], a->definition.timings.vpar[i], b->definition.timings.vpar[i]); } } #define ROUND_DIV(n,d) (((n) + ((d) >> 1)) / (d)) /* Excel's ROUND(n/d,0) */ #define ROUNDDOWN_DIV(n,d) ((n) / (d)) /* Excel's ROUNDDOWN(n/d,0) */ static bool generate_mode_using_gtfint(uint32_t h_pixels, uint32_t v_lines, uint32_t ip_freq_rqd, ModeDescriptionRef mode_desc, MonitorDescriptionRef monitor) { /* Based on VESA GTF spec 1.1, the variable names match those in the spec and equation * numbers labelled for cross referencing. * Stage 1 uses basic timing parameters and vertical frequency, then follows Stage 2. * Most of the results are integer values (lines, pixels), only a few timings (�s, Hz, %) * need to keep fractional bits which are suffixed "[un|signed]whole<p>fractional". * The v_lines & ip_freq_rqd are as though progressive scan, eg. PAL would be 720x576 @ 25Hz. */ #define GTF_MARGIN_PTHOU 18 /* 1.8% */ #define GTF_CELL_GRAN_RND 8 /* Char cell granularity, must be a multiple of 2 */ #define GTF_2CELL_GRAN_RND (2*GTF_CELL_GRAN_RND) #define GTF_MIN_PORCH 1 /* Lines (or char cells) porch */ #define GTF_V_SYNC_RQD 3 /* Lines */ #define GTF_H_SYNC_PTHOU 80 /* 8% */ #define GTF_MIN_VSYNC_BP 550uLL /* �s */ #define GTF_C_PRIME 30 /* ((C - J) x (K / 256)) + J where C=40% M=600%/kHz K=128 J=20% */ #define GTF_M_PRIME 300 /* (K / 256) x M */ uint32_t h_pixels_rnd, h_blank, total_active_pixels; uint32_t h_period_u8p24, h_period_est_u8p24; uint32_t h_back_porch, h_sync_pixels, h_front_porch, total_pixels; uint32_t v_field_rate_est_u8p24, v_frame_rate_u8p24, v_field_rate_u8p24; uint32_t v_lines_rnd, v_sync_bp, v_field_rate_rqd; uint32_t v_back_porch, v_sync_rqd, v_front_porch, total_v_lines; uint32_t top_margin, bottom_margin, left_margin, right_margin; uint32_t pixel_freq; uint64_t num, den; int32_t ideal_duty_cycle_s7p24; ModeDef *mp; const bool int_rqd = false; /* Interlacing not propagated to mode description */ const bool margins_rqd = false; /* Margins currently not offered */ /* 7.3.1 horizontal pixels rounded to whole character cells */ h_pixels_rnd = GTF_CELL_GRAN_RND * ROUND_DIV(h_pixels, GTF_CELL_GRAN_RND); /* 7.3.2 vertical lines per field & 7.3.3 field rate */ if (int_rqd) { v_lines_rnd = ROUND_DIV(v_lines, 2); v_field_rate_rqd = ip_freq_rqd * 2; } else { v_lines_rnd = v_lines; v_field_rate_rqd = ip_freq_rqd; } /* 7.3.4 top margin & 7.3.5 bottom margin */ top_margin = margins_rqd ? ROUND_DIV(v_lines_rnd * GTF_MARGIN_PTHOU, 1000) : 0; bottom_margin = top_margin; /* 7.3.7 estimate horizontal period in �s */ num = ((1000000uLL << 24) / v_field_rate_rqd) - (GTF_MIN_VSYNC_BP << 24); den = v_lines_rnd + (2uLL * top_margin) + GTF_MIN_PORCH; if (int_rqd) { h_period_est_u8p24 = (uint32_t)((2 * num) / ((2 * den) + 1)); } else { h_period_est_u8p24 = (uint32_t)(num / den); } /* 7.3.8 lines in vsync and vertical back porch & 7.3.9 vertical back porch alone */ v_sync_bp = (uint32_t)ROUND_DIV(GTF_MIN_VSYNC_BP << 24, h_period_est_u8p24); v_back_porch = v_sync_bp - GTF_V_SYNC_RQD; /* 7.3.10 lines in the field period (actually, half lines) */ total_v_lines = (2 * (v_lines_rnd + top_margin + bottom_margin + v_sync_bp + GTF_MIN_PORCH)) + (int_rqd ? 1 : 0); /* 7.3.11 estimate vertical field rate */ num = (1uLL << (24 + 24 + 1)); den = (uint64_t)h_period_est_u8p24; num = 1000000 * (num / den); v_field_rate_est_u8p24 = (uint32_t)(num / total_v_lines); /* 7.3.12 find actual horizontal period */ num = (uint64_t)h_period_est_u8p24 * v_field_rate_est_u8p24; den = v_field_rate_rqd; h_period_u8p24 = (uint32_t)((num / den) >> 24); /* 7.3.13 find actual vertical field rate & 7.3.14 frame rate */ num = (1uLL << (24 + 24 + 1)); den = (uint64_t)h_period_u8p24; num = 1000000 * (num / den); v_field_rate_u8p24 = (uint32_t)(num / total_v_lines); v_frame_rate_u8p24 = int_rqd ? (v_field_rate_u8p24 / 2) : v_field_rate_u8p24; /* 7.3.15 left margin & 7.3.16 right margin */ left_margin = margins_rqd ? ROUND_DIV(h_pixels_rnd * GTF_MARGIN_PTHOU, 1000 * GTF_CELL_GRAN_RND) : 0; left_margin = GTF_CELL_GRAN_RND * left_margin; right_margin = left_margin; /* 7.3.17 total active pixels & 7.3.18 ideal duty cycle */ total_active_pixels = h_pixels_rnd + left_margin + right_margin; num = (uint64_t)GTF_M_PRIME * h_period_u8p24; den = 1000; ideal_duty_cycle_s7p24 = (int32_t)((GTF_C_PRIME << 24) - (uint32_t)(num / den)); if (ideal_duty_cycle_s7p24 < 0) { return false; /* Not a valid duty cycle */ } /* 7.3.19 horizontal blanking period in pixels & 7.3.20 total pixels */ num = (uint64_t)total_active_pixels * ideal_duty_cycle_s7p24; den = (100uLL << 24) - ideal_duty_cycle_s7p24; h_blank = GTF_2CELL_GRAN_RND * ROUND_DIV((uint32_t)(num / den), GTF_2CELL_GRAN_RND); total_pixels = total_active_pixels + h_blank; /* 7.3.21 pixel clock (actually, kHz) */ num = ((1000uLL << 24) * total_pixels); den = h_period_u8p24; pixel_freq = (uint32_t)(num / den); /* 7.6.17 pixels in horizontal sync */ h_sync_pixels = (total_pixels * GTF_H_SYNC_PTHOU) / 1000; h_sync_pixels = GTF_CELL_GRAN_RND * ROUND_DIV(h_sync_pixels, GTF_CELL_GRAN_RND); /* 7.6.18 horizontal front porch & 7.6.19 horizontal back porch */ h_front_porch = (h_blank / 2) - h_sync_pixels; h_back_porch = h_front_porch + h_sync_pixels; /* 7.6.36 vertical front porch */ v_front_porch = GTF_MIN_PORCH; v_sync_rqd = GTF_V_SYNC_RQD; mp = &mode_desc->definition.timings; mp->xres = h_pixels; mp->yres = v_lines; mp->hpar[FR_SYNC] = h_sync_pixels; mp->hpar[FR_BPCH] = h_back_porch; mp->hpar[FR_BDR1] = left_margin; mp->hpar[FR_DISP] = h_pixels; mp->hpar[FR_BDR2] = right_margin; mp->hpar[FR_FPCH] = h_front_porch; mp->vpar[FR_SYNC] = v_sync_rqd; mp->vpar[FR_BPCH] = v_back_porch; /* Note, loss of interlacing */ mp->vpar[FR_BDR1] = top_margin; mp->vpar[FR_DISP] = v_lines; mp->vpar[FR_BDR2] = bottom_margin; mp->vpar[FR_FPCH] = v_front_porch; /* Note, loss of interlacing */ mp->pixel_khz = pixel_freq; mp->external_clock = -1; return true; } static bool generate_mode_using_gtf(int h_pixelsi, int v_linesi, int ip_freq_rqdi, ModeDescriptionRef mode_desc, MonitorDescriptionRef monitor) { double h_pixels = (double)h_pixelsi, v_lines = (double)v_linesi, ip_freq_rqd = (double)ip_freq_rqdi; ModeDescription mymode; ModeDescriptionRef mode_desci = &mymode; /* vvv Comparison goes here vvv */ const int margins_rqd = 0; /* Set to 1 if margins are wanted. */ const double margin_per = 1.8; /* Percentage size of margin (0 to 100) if reqd */ double v_lines_rnd = 0; /* Number of desired visible lines rounded down to */ /* The nearest character cell */ double v_sync_rqd = 3; /* The width of the v sync in lines */ double h_sync_percent = 8; /* The width of the H sync as percentage of the total line period */ double cell_gran_rnd = 8; /* Character cell width in pixels. May be able to confirm this value in hardware but at present hardcode this to 8 (usual value), */ const int int_rqd = 0; /* int_rqd specifies whether the mode should be interlaced. Most modes used are not. */ double v_field_rate_reqd = 0; /* The actual vertical field rate after interlacing is taking into consideration */ double interlace = 0; /* If interlacing is used, this will be set to 0.5 */ const double min_vsync_and_bp = 550; /* The minimum time of vertical sync and back porch interval(us) */ const double min_porch = 1; /* Minimum front porch in lines (vertical) and character cells (horizontal) */ const double c = 40; /* The blanking formula offset */ const double m = 600; /* The blanking formula gradient */ const double k = 128; /* The blanking formula scaling factor */ const double j = 20; /* The blanking formula scaling factor weighting */ /* coefficient calculations. These should be constants in standard GTF */ double C = ((c-j) * k / 256) + j; double M = k / 256 * m; /* Find the refresh rate required (Hz) */ /* If interlaced, the number of vertical lines assumed by the * calculation must be halved, as the computation calculates the * number of vertical lines per field. In either case, the number * of lines is rounded down to the nearest integer. */ if (int_rqd == 1) { v_lines_rnd = round(v_lines / 2); v_field_rate_reqd = ip_freq_rqd * 2; interlace = 0.5; } else { v_lines_rnd = round(v_lines); v_field_rate_reqd = ip_freq_rqd; /* Interlace is automatically set to 0 */ } /* Round the number of pixels to the nearest character */ /* cell boundary */ double h_pixels_rnd = (round(h_pixels/cell_gran_rnd)) * cell_gran_rnd; /* Determine the width of the left and right borders */ double left_margin = 0; /* Number of pixels in the left hand margin, rounded */ /* down to the nearest character cell. If no margins */ /* required this is set to 0. */ double right_margin = 0; /* see left_margin */ double top_margin = 0; /* No of lines rounded down to nearest line. */ double bot_margin = 0; /* See top, left, right */ if (margins_rqd == 1) { left_margin = round(h_pixels_rnd * margin_per / 100 / cell_gran_rnd) *cell_gran_rnd; right_margin = left_margin; top_margin = round(margin_per / 100 * v_lines_rnd); bot_margin = top_margin; } /* For the calculations below, refer to the VESA GTF v1.1 spec */ /* Estimate the horizontal period */ double h_period_est = ((1 / v_field_rate_reqd) - min_vsync_and_bp/1000000) / (v_lines_rnd + (2 * top_margin) + min_porch + interlace) * 1000000; /* Find the number of lines in V sync + B Porch */ double vsync_and_bp = round(min_vsync_and_bp / h_period_est); /* Find number of lines in back porch alone */ double v_back_porch = vsync_and_bp - v_sync_rqd; /* Find the total number of lines in the vertical field period */ double total_v_lines = v_lines_rnd + top_margin + bot_margin + vsync_and_bp + interlace + min_porch; /* Estimate the vertical field frequency */ double v_field_rate_est = 1/h_period_est/total_v_lines * 1000000; /* Find the actual horizontal period */ double h_period = h_period_est / (v_field_rate_reqd / v_field_rate_est); /* Find the actual vertical field frequency */ double v_field_rate = 1/h_period/total_v_lines * 1000000; /* Find the vertical frame frequency */ if (int_rqd == 1) { v_field_rate = v_field_rate / 2; } /* GTF calculates margins using a percentage here. We use actual pixels */ /* (and 0 always at present) */ double total_active_pixels = h_pixels_rnd + left_margin + right_margin; /* Find the ideal blanking duty cycle from the blanking duty cycle equation */ double ideal_duty_cycle = C - (M * h_period / 1000); if (ideal_duty_cycle < 0) { /* If this happens we should just ignore the mode */ return false; } /* Find the number of pixels in the blanking time to the nearest double */ /* character cell */ double h_blank_pixels = (round(total_active_pixels * ideal_duty_cycle / (100 - ideal_duty_cycle) / (2 * cell_gran_rnd))) * (2 * cell_gran_rnd); /* Find the total number of pixels */ double total_pixels = total_active_pixels + h_blank_pixels; /* Find pixel clock frequency */ double pixel_freq = total_pixels / h_period; /* Find horizontal frequency */ /* double h_freq = 1000 / h_period; */ /* From GTF spec - Using Stage 1 parameters to derive stage 2 parameters */ /* Find addressable lines per frame */ double addr_lines_per_frame; if (int_rqd == 1) { addr_lines_per_frame = v_lines_rnd * 2; } else { addr_lines_per_frame = v_lines_rnd; } /* Find the total number of lines in a frame: */ double total_lines_per_frame = v_lines_rnd + top_margin + bot_margin + vsync_and_bp + interlace + min_porch; if (int_rqd == 1) { total_lines_per_frame = total_lines_per_frame * 2; } /* Find the number of pixels in the horizontal sync period */ double h_sync_pixels = (round(h_sync_percent/100 * total_pixels / cell_gran_rnd)) * cell_gran_rnd; /* Find the number of pixels in the horizontal front porch period */ double h_front_porch = (h_blank_pixels / 2) - h_sync_pixels; double h_back_porch = h_front_porch + h_sync_pixels; /* Find the odd front porch period (lines) (36) */ double v_fporch = min_porch + interlace; mode_desc->definition.timings.xres = (int)h_pixels; mode_desc->definition.timings.yres = (int)v_lines; mode_desc->definition.timings.hpar[FR_SYNC] = (int) h_sync_pixels; mode_desc->definition.timings.hpar[FR_BPCH] = (int) h_back_porch; mode_desc->definition.timings.hpar[FR_BDR1] = (int) left_margin; mode_desc->definition.timings.hpar[FR_DISP] = (int) h_pixels; mode_desc->definition.timings.hpar[FR_BDR2] = (int) right_margin; mode_desc->definition.timings.hpar[FR_FPCH] = (int) h_front_porch; mode_desc->definition.timings.vpar[FR_SYNC] = (int) v_sync_rqd; mode_desc->definition.timings.vpar[FR_BPCH] = (int) v_back_porch; mode_desc->definition.timings.vpar[FR_BDR1] = (int) top_margin; if (int_rqd == 1) { mode_desc->definition.timings.vpar[FR_DISP] = (int) v_lines / 2; } else { mode_desc->definition.timings.vpar[FR_DISP] = (int) v_lines; } mode_desc->definition.timings.vpar[FR_BDR2] = (int) bot_margin; mode_desc->definition.timings.vpar[FR_FPCH] = (int) v_fporch; mode_desc->definition.timings.pixel_khz = (int) round(pixel_freq * 1000); mode_desc->definition.timings.external_clock = -1; if (timing_support == EDID_USE_GTF) { mode_desc->definition.timings.syncpol = HSync_Negative+VSync_Positive; /* Default GTF */ } else { mode_desc->definition.timings.syncpol = HSync_Positive+VSync_Negative; /* Secondary GTF */ } mode_desc->definition.timings.interlaced = int_rqd; /* ^^^ Comparison ends here ^^^ */ if (generate_mode_using_gtfint(h_pixelsi, v_linesi, ip_freq_rqdi, mode_desci, NULL)) { comparetimings(mode_desc, mode_desci, ip_freq_rqdi); } return true; } static bool generate_mode_using_cvt_rbint(uint32_t h_pixels, uint32_t v_lines, uint32_t ip_freq_rqd, ModeDescriptionRef mode_desc, MonitorDescriptionRef monitor) { /* Based on VESA CVT spec 1.2, the variable names match those in the spec and equation * numbers labelled for cross referencing. * Most of the results are integer values (lines, pixels), only a few timings (�s, Hz, %) * need to keep fractional bits which are suffixed "[un|signed]whole<p>fractional". * The v_lines & ip_freq_rqd are as though progressive scan, eg. PAL would be 720x576 @ 25Hz. */ #define CVT_MARGIN_PTHOU 18 /* 1.8% */ #define CVT_C_PRIME 30 /* ((C - J) x (K / 256)) + J where C=40% M=600%/kHz K=128 J=20% */ #define CVT_CLOCK_STEP 250 /* 0.25MHz */ #define CVT_H_SYNC_PTHOU 80 /* 8% */ #define CVT_M_PRIME 300 /* (K / 256) x M */ #define CVT_MIN_V_PORCH_RND 3 /* Lines */ #define CVT_MIN_V_BPORCH 6 /* Lines */ #define CVT_MIN_VSYNC_BP 550uLL /* �s */ #define CVT_RB_H_BLANK 160 /* �s */ #define CVT_RB_H_SYNC 32 /* �s */ #define CVT_RB_MIN_V_BLANK 460uLL /* �s */ #define CVT_RB_V_FPORCH 3 /* Lines */ #define CVT_CELL_GRAN_RND 8 /* Char cell granularity, must be a multiple of 2 */ #define CVT_2CELL_GRAN_RND (2*CVT_CELL_GRAN_RND) uint32_t h_pixels_rnd, h_blank, total_active_pixels; uint32_t h_period_est_u8p24; uint32_t h_back_porch, h_sync_pixels, h_front_porch, total_pixels; uint32_t v_lines_rnd, v_sync_bp, vbi_lines, v_field_rate_rqd; uint32_t v_back_porch, v_sync_rnd = 0, v_front_porch, total_v_lines; uint32_t top_margin, bottom_margin, left_margin, right_margin; uint32_t pixel_freq; uint64_t num, den; int32_t ideal_duty_cycle_s7p24; ModeDef *mp; const bool int_rqd = false; /* Interlacing not propagated to mode description */ const bool margins_rqd = false; /* Margins currently not offered */ /* Table 3-2 vertical sync based on the aspect ratio */ if (((v_lines % 3) == 0) && ((v_lines * 4 / 3) == h_pixels)) v_sync_rnd = 4; /* 4:3 */ if (((v_lines % 9) == 0) && ((v_lines * 16 / 9) == h_pixels)) v_sync_rnd = 5; /* 16:9 */ if (((v_lines % 10) == 0) && ((v_lines * 16 / 10) == h_pixels)) v_sync_rnd = 6; /* 16:10 */ if (((h_pixels == 1280) && (v_lines == 1024)) || ((h_pixels == 1280) && (v_lines == 768))) v_sync_rnd = 7; /* Specials */ if (v_sync_rnd == 0) { return false; /* Not a valid aspect */ } /* 5.2.1 field rate & 5.2.5 vertical lines per field */ if (int_rqd) { v_lines_rnd = ROUNDDOWN_DIV(v_lines, 2); v_field_rate_rqd = ip_freq_rqd * 2; } else { v_lines_rnd = v_lines; v_field_rate_rqd = ip_freq_rqd; } /* 5.2.2 horizontal pixels rounded to whole character cells */ h_pixels_rnd = CVT_CELL_GRAN_RND * ROUNDDOWN_DIV(h_pixels, CVT_CELL_GRAN_RND); /* 5.2.3 left margin and right margins */ left_margin = margins_rqd ? ROUNDDOWN_DIV(h_pixels_rnd * CVT_MARGIN_PTHOU, 1000) : 0; left_margin = CVT_CELL_GRAN_RND * ROUNDDOWN_DIV(left_margin, CVT_CELL_GRAN_RND); right_margin = left_margin; /* 5.2.4 total active pixels */ total_active_pixels = h_pixels_rnd + left_margin + right_margin; /* 5.2.6 top and bottom margin */ top_margin = margins_rqd ? ROUNDDOWN_DIV(v_lines_rnd * CVT_MARGIN_PTHOU, 1000) : 0; bottom_margin = top_margin; /* Select CRT or CRT reduced blanking */ if (timing_support == EDID_USE_CVT) { /* 5.3.8 estimate horizontal period in �s */ num = ((1000000uLL << 24) / v_field_rate_rqd) - (CVT_MIN_VSYNC_BP << 24); den = v_lines_rnd + (2uLL * top_margin) + CVT_MIN_V_PORCH_RND; if (int_rqd) { h_period_est_u8p24 = (uint32_t)((2 * num) / ((2 * den) + 1)); } else { h_period_est_u8p24 = (uint32_t)(num / den); } /* 5.3.9 lines in vsync and vertical back porch & 5.3.10 vertical back porch alone */ v_sync_bp = (uint32_t)ROUNDDOWN_DIV(CVT_MIN_VSYNC_BP << 24, h_period_est_u8p24) + 1; v_sync_bp = MAX(v_sync_bp, v_sync_rnd + CVT_MIN_V_BPORCH); v_back_porch = v_sync_bp - v_sync_rnd; /* 5.3.12 ideal duty cycle */ num = (uint64_t)CVT_M_PRIME * h_period_est_u8p24; den = 1000; ideal_duty_cycle_s7p24 = (int32_t)((CVT_C_PRIME << 24) - (uint32_t)(num / den)); ideal_duty_cycle_s7p24 = MAX(ideal_duty_cycle_s7p24, (20 << 24)); /* 5.3.13 horizontal blanking period in pixels & 5.3.14 total pixels */ num = (uint64_t)total_active_pixels * ideal_duty_cycle_s7p24; den = (100uLL << 24) - ideal_duty_cycle_s7p24; h_blank = CVT_2CELL_GRAN_RND * ROUNDDOWN_DIV((uint32_t)(num / den), CVT_2CELL_GRAN_RND); total_pixels = total_active_pixels + h_blank; /* 5.3.15 pixel clock (actually, kHz) */ num = ((1000uLL << 24) * total_pixels); den = h_period_est_u8p24; pixel_freq = (uint32_t)(num / den); pixel_freq = CVT_CLOCK_STEP * ROUNDDOWN_DIV(pixel_freq, CVT_CLOCK_STEP); /* 5.3.?? vertical front porch */ v_front_porch = CVT_MIN_V_PORCH_RND; /* 5.3.?? horizontal sync */ h_sync_pixels = (total_pixels * CVT_H_SYNC_PTHOU) / 1000; h_sync_pixels = CVT_CELL_GRAN_RND * ROUNDDOWN_DIV(h_sync_pixels, CVT_CELL_GRAN_RND); } else { /* 5.4.8 estimate horizontal period in �s */ num = ((1000000uLL << 24) / v_field_rate_rqd) - (CVT_RB_MIN_V_BLANK << 24); den = (uint64_t)v_lines_rnd + top_margin + bottom_margin; h_period_est_u8p24 = (uint32_t)(num / den); /* 5.4.9 lines in vsync and vertical back porch & 5.4.10 vertical back porch alone */ vbi_lines = (uint32_t)ROUNDDOWN_DIV(CVT_RB_MIN_V_BLANK << 24, h_period_est_u8p24) + 1; vbi_lines = MAX(vbi_lines, CVT_RB_V_FPORCH + v_sync_rnd + CVT_MIN_V_BPORCH); v_back_porch = vbi_lines - v_sync_rnd - CVT_RB_V_FPORCH; /* 5.4.11 lines in the field period (actually, half lines) */ total_v_lines = (2 * (vbi_lines + v_lines_rnd + top_margin + bottom_margin)) + (int_rqd ? 1 : 0); /* 5.4.?? horizontal blanking period in pixels && 5.4.12 total pixels */ h_blank = CVT_RB_H_BLANK; total_pixels = CVT_RB_H_BLANK + total_active_pixels; /* 5.4.13 pixel clock (actually, kHz) */ num = (uint64_t)v_field_rate_rqd * total_v_lines * total_pixels; den = 2 * 1000; pixel_freq = (uint32_t)(num / den); pixel_freq = CVT_CLOCK_STEP * ROUNDDOWN_DIV(pixel_freq, CVT_CLOCK_STEP); /* 5.4.?? vertical front porch */ v_front_porch = CVT_RB_V_FPORCH; /* 5.4.?? horizontal sync */ h_sync_pixels = CVT_RB_H_SYNC; } /* 5.2.?? horizontal front porch & 5.2.?? horizontal back porch */ h_front_porch = (h_blank / 2) - h_sync_pixels; h_back_porch = h_front_porch + h_sync_pixels; mp = &mode_desc->definition.timings; mp->xres = h_pixels; mp->yres = v_lines; mp->hpar[FR_SYNC] = h_sync_pixels; mp->hpar[FR_BPCH] = h_back_porch; mp->hpar[FR_BDR1] = left_margin; mp->hpar[FR_DISP] = h_pixels; mp->hpar[FR_BDR2] = right_margin; mp->hpar[FR_FPCH] = h_front_porch; mp->vpar[FR_SYNC] = v_sync_rnd; mp->vpar[FR_BPCH] = v_back_porch; mp->vpar[FR_BDR1] = top_margin; mp->vpar[FR_DISP] = v_lines; mp->vpar[FR_BDR2] = bottom_margin; mp->vpar[FR_FPCH] = v_front_porch; mp->pixel_khz = pixel_freq; mp->external_clock = -1; return true; } static bool generate_mode_using_cvt_rb(int h_pixelsi, int v_linesi, int ip_freq_rqdi, ModeDescriptionRef mode_desc, MonitorDescriptionRef monitor) { double h_pixels = (double)h_pixelsi, v_lines = (double)v_linesi, ip_freq_rqd = (double)ip_freq_rqdi; ModeDescription mymode; ModeDescriptionRef mode_desci = &mymode; /* vvv Comparison goes here vvv */ const int int_rqd = 0; /* int_rqd specifies whether the mode should be interlaced. Most modes used are not. */ const int margins_rqd = 0; /* Set to 1 if margins are wanted. */ const double margin_per = 1.8; /* Percentage size of margin(0 to 100) if reqd */ double cell_gran_rnd = 8; /* Character cell width in pixels. May be able to confirm this value in hardware but at present hardcode this to 8 (usual value), */ double v_field_rate_reqd = 0; /* The actual vertical field rate after interlacing is taking into consideration */ double v_lines_rnd = 0; /* Number of desired visible lines rounded down to the nearest character cell */ double interlace = 0; /* If interlacing is used, this will be set to 0.5 */ int v_sync_rnd = 0; #if 0 /* Determine the aspect ratio and set the v_sync_rnd variable * This is a slightly messy lookup table - VESA recommends the lookup * approach. We match on X, but in some cases we need to also check Y if * there are other modes with different heights. */ if ((h_pixels == 640) || (h_pixels == 800) || (h_pixels == 1024) || (h_pixels == 1400) || (h_pixels == 1600) || (h_pixels == 1920) || (h_pixels == 2048) || (h_pixels == 2560) || (h_pixels == 3200) || (h_pixels == 3840)) { v_sync_rnd = 4; } if ((h_pixels == 1280) && ((v_lines == 1024) || (v_lines == 768))) { v_sync_rnd = 7; } if ((h_pixels == 848) || (h_pixels == 1064) || ((h_pixels == 1280) && (v_lines == 720)) || (h_pixels == 1360) || (h_pixels == 1704) || (h_pixels == 1864) || ((h_pixels == 1920) && (v_lines == 1080)) || (h_pixels == 2128) || (h_pixels == 2560) || (h_pixels == 2728) || (h_pixels == 3408) || (h_pixels == 4264)) { v_sync_rnd = 5; } if ((int) (h_pixels / v_lines * 10) == 16) { v_sync_rnd = 6; } #else if (((v_linesi % 3) == 0) && ((v_linesi * 4 / 3) == h_pixelsi)) v_sync_rnd = 4; /* 4:3 */ if (((v_linesi % 9) == 0) && ((v_linesi * 16 / 9) == h_pixelsi)) v_sync_rnd = 5; /* 16:9 */ if (((v_linesi % 10) == 0) && ((v_linesi * 16 / 10) == h_pixelsi)) v_sync_rnd = 6; /* 16:10 */ if (((h_pixelsi == 1280) && (v_linesi == 1024)) || ((h_pixelsi == 1280) && (v_linesi == 768))) v_sync_rnd = 7; /* Specials */ #endif if (v_sync_rnd == 0) { /* If this happens we should just ignore the mode */ return false; } /* The variable names used below are those in the VESA Coordinated Timings * Standard. I've kept this code as close to the original as possible for * clarity at the cost of some speed. When this works 110%, this can be * optimised out. */ /* Find the refresh rate required (Hz) */ /* If interlaced, the number of vertical lines assumed by the * calculation must be halved, as the computation calculates the * number of vertical lines per field. In either case, the number * of lines is rounded down to the nearest integer. */ if (int_rqd == 1) { v_lines_rnd = floor(v_lines / 2); v_field_rate_reqd = ip_freq_rqd * 2; interlace = 0.5; } else { v_lines_rnd = floor(v_lines); v_field_rate_reqd = ip_freq_rqd; /* Interlace is automatically set to 0 */ } /* Round the number of pixels to the nearest character cell boundary */ double h_pixels_rnd = (floor(h_pixels/cell_gran_rnd)) * cell_gran_rnd; /* Determine the width of the left and right borders */ double left_margin = 0; /* Number of pixels in the left hand margin, rounded down to the nearest character cell. If no margins required this is set to 0. */ double right_margin = 0; /* see left_margin */ double top_margin = 0; /* No of lines rounded down to nearest line. */ double bot_margin = 0; /* See top, left, right */ if (margins_rqd == 1) { left_margin = floor(h_pixels_rnd * margin_per / 100 / cell_gran_rnd) *cell_gran_rnd; right_margin = left_margin; top_margin = floor(margin_per / 100 * v_lines_rnd); bot_margin = top_margin; } /* Minimum vertical back porch (used in both sets of calcs). */ const double min_v_bporch = 6; /* The total number of active pixels is equal to the rounded * horizontal pixels and the margins: */ double total_active_pixels = h_pixels_rnd + left_margin + right_margin; /* Pixel clock resolution - see CVT spec S3.2 */ const double clock_step = 0.25; /* Define back porch (used in our final block) */ int v_back_porch; /* Define actual pixel frequency (for final block) */ double act_pixel_freq; /* H blanking period (for final block) */ int h_blank; /* H sync (for final block) */ int h_sync; /* V front porch (NB in CVT this is min_v_porch_rnd and in CVT-RB this is * rb_v_fporch but as they are both constants declare them. */ double v_front_porch = 3; if (timing_support == EDID_USE_CVT) { /* Computation of "CRT" (ie non-RB) CVT timings */ /* Minimum timing for vertical blanking for 'CRT' timings */ const double min_vsync_bp = 550; /* Standard 'CRT' timing vertical front porch */ /* Estimate the horizontal period (kHz) */ double h_period_est = ((1 / v_field_rate_reqd) - min_vsync_bp / 1000000) / (v_lines_rnd + (2 * top_margin) + v_front_porch + interlace) * 1000000; /* Find the number of lines in V sync + back porch */ double v_sync_bp = floor(min_vsync_bp / h_period_est) + 1; if (v_sync_bp < v_sync_rnd + min_v_bporch) { v_sync_bp = v_sync_rnd + min_v_bporch; } /* Find the number of lines in V back porch */ v_back_porch = (int) (v_sync_bp - v_sync_rnd); /* Find total number of lines in vertical field period */ double total_v_lines = v_lines_rnd + top_margin + bot_margin + v_sync_bp + interlace + v_front_porch; /* C_PRIME = ((C-J) * K / 256 + J (from CVT spec) */ const double c_prime = 30; /* M_PRIME = K / 256 * M (from CVT spec) */ const double m_prime = 300; /* Find the ideal blanking duty cycle from the blanking duty cycle equation (%): */ double ideal_duty_cycle = c_prime - (m_prime * h_period_est / 1000); /* Find the number of pixels in the horizontal blanking time to the nearest double character cell * (limit horizontal blanking so that it is >= 20% of the horizontal total). */ if (ideal_duty_cycle < 20) { h_blank = (int) (floor(total_active_pixels * 20 / (100-20) / (2 * cell_gran_rnd)) * (2 * cell_gran_rnd)); } else { h_blank = (int) (floor(total_active_pixels * ideal_duty_cycle / (100 - ideal_duty_cycle) / (2 * cell_gran_rnd)) * (2 * cell_gran_rnd)); } /* Find the total number of pixels in a line */ double total_pixels = total_active_pixels + h_blank; /* Find pixel clock frequency (MHz): */ act_pixel_freq = clock_step * floor((total_pixels / h_period_est) / clock_step); /* Find actual horizontal frequency (kHz) */ double act_h_freq = 1000 * act_pixel_freq / total_pixels; /* Find actual field rate (Hz) */ double act_field_rate = 1000 * act_h_freq / total_v_lines; /* Find actual refresh rate (Hz) */ double act_frame_rate = act_field_rate; if (int_rqd == 1) { act_frame_rate = act_field_rate / 2; } /* H sync per is the percentage of horizontal total period that * defines horizontal sync width */ const double h_sync_per = 8; /* Calculate H sync */ h_sync = (int) (floor(h_sync_per / 100 * total_pixels / cell_gran_rnd) * cell_gran_rnd); } else { /* Computation of Reduced Blanking timing parameters. */ /* Estimate the horizontal period (kHz) */ const double rb_min_v_blank = 460; /* Min V blank for reduced timings */ double h_period_est = ((1000000/v_field_rate_reqd) - rb_min_v_blank) / (v_lines_rnd + top_margin + bot_margin); double vbi_lines = floor(rb_min_v_blank / h_period_est) + 1; /* Vertical front porch is fixed in reduced blanking */ v_front_porch = 3; double rb_min_vbi = v_front_porch + v_sync_rnd + min_v_bporch; double act_vbi_lines; /* Actual number of vertical blanking lines */ if (vbi_lines < rb_min_vbi) { act_vbi_lines = rb_min_vbi; } else { act_vbi_lines = vbi_lines; } double total_v_lines = act_vbi_lines + v_lines_rnd + top_margin + bot_margin + interlace; h_sync = 32; /* H sync is fixed in reduced blanking */ h_blank = 160; /* H Blank is fixed in reduced blanking */ /* Find total number of pixel clocks per line */ double total_pixels = h_blank + total_active_pixels; const double clock_step = 0.25; /* Calculate the pixel clock frequency to nearest 0.25MHz */ act_pixel_freq = clock_step * floor((v_field_rate_reqd * total_v_lines * total_pixels / 1000000) / clock_step); /* Find the number of lines in V_sync + back porch: */ v_back_porch = (int) (act_vbi_lines - v_front_porch - v_sync_rnd); } /* Calculate front and back porch */ int h_back_porch = h_blank / 2; int h_front_porch = h_blank - h_back_porch - h_sync; /* Now populate the mode definition block */ mode_desc->definition.timings.xres = (int)h_pixels; mode_desc->definition.timings.yres = (int)v_lines; mode_desc->definition.timings.hpar[FR_SYNC] = h_sync; mode_desc->definition.timings.hpar[FR_BPCH] = h_back_porch; mode_desc->definition.timings.hpar[FR_BDR1] = (int) left_margin; mode_desc->definition.timings.hpar[FR_DISP] = (int) h_pixels; mode_desc->definition.timings.hpar[FR_BDR2] = (int) right_margin; mode_desc->definition.timings.hpar[FR_FPCH] = h_front_porch; mode_desc->definition.timings.vpar[FR_SYNC] = (int) v_sync_rnd; mode_desc->definition.timings.vpar[FR_BPCH] = v_back_porch; mode_desc->definition.timings.vpar[FR_BDR1] = (int) top_margin; if (int_rqd == 1) { mode_desc->definition.timings.vpar[FR_DISP] = (int) v_lines / 2; } else { mode_desc->definition.timings.vpar[FR_DISP] = (int) v_lines; } mode_desc->definition.timings.vpar[FR_BDR2] = (int) bot_margin; mode_desc->definition.timings.vpar[FR_FPCH] = (int) v_front_porch; mode_desc->definition.timings.pixel_khz = (int) (act_pixel_freq * 1000); mode_desc->definition.timings.external_clock = -1; if (timing_support == EDID_USE_CVT) { mode_desc->definition.timings.syncpol = HSync_Negative+VSync_Positive; } else { /* EDID_USE_CVTRB */ mode_desc->definition.timings.syncpol = HSync_Positive+VSync_Negative; } mode_desc->definition.timings.interlaced = int_rqd; /* ^^^ Comparison ends here ^^^ */ if (generate_mode_using_cvt_rbint(h_pixelsi, v_linesi, ip_freq_rqdi, mode_desci, NULL)) { comparetimings(mode_desc, mode_desci, ip_freq_rqdi); } return true; } /* FP bit from generate_cvt3_timing() */ static void generate_cvt3_timing(int yres, int pixel_ratio_flags) { int xresi, xres; /* vvv Comparison goes here vvv */ switch (pixel_ratio_flags) { case 0: xres = (int) (8 * floor((((double) yres * 4) / 3) / 8)); xresi = 8 * ((yres * 4) / (3 * 8)); break; case 1: xres = (int) (8 * floor((((double) yres * 16) / 9) / 8)); xresi = 8 * ((yres * 16) / (9 * 8)); break; case 2: xres = (int) (8 * floor((((double) yres * 16) / 10) / 8)); xresi = 8 * ((yres * 16) / (10 * 8)); break; case 3: xres = (int) (8 * floor((((double) yres * 15) / 9) / 8)); xresi = 8 * ((yres * 15) / (9 * 8)); break; } /* ^^^ Comparison ends here ^^^ */ if (xres != xresi) { printf("%dx%d != %dx%d ratio %d\n", xres, yres, xresi, yres, pixel_ratio_flags); exit(1); } } int main(void) { int pixel_ratio_flags, xres, yres, framerate, pass; printf("Comparing generate_cvt3_timing\n"); for (yres = 256; yres < 4096; yres++) { for (pixel_ratio_flags = 0; pixel_ratio_flags < 4; pixel_ratio_flags++) { generate_cvt3_timing(yres, pixel_ratio_flags); } } printf("Comparing generate_mode_using_gtf\n"); countall = count = 0; for (pass = 1; pass <= 2; pass++) { timing_support = (pass == 1) ? EDID_USE_GTF : !EDID_USE_GTF2; for (framerate = 25; framerate < 100; framerate++) { printf(" framerate %d timing support %d\n", framerate, timing_support); for (yres = 256; yres < 4096; yres = yres + 8) { for (xres = yres; xres < 4096; xres = xres + 8) { ModeDescription mode; MonitorDescription monitor; generate_mode_using_gtf(xres, yres, framerate, &mode, &monitor); } } } } printf("%d of %d compared modes match\n", count, countall); printf("Comparing generate_mode_using_cvt_rb\n"); countall = count = 0; for (pass = 1; pass <= 2; pass++) { timing_support = (pass == 1) ? EDID_USE_CVT : !EDID_USE_CVT; for (framerate = 25; framerate < 100; framerate++) { printf(" framerate %d timing support %d\n", framerate, timing_support); for (yres = 256; yres < 4096; yres = yres + 8) { for (xres = yres; xres < 4096; xres = xres + 8) { ModeDescription mode; MonitorDescription monitor; generate_mode_using_cvt_rb(xres, yres, framerate, &mode, &monitor); } } } } printf("%d of %d compared modes match\n", count, countall); return 0; }