diff options
-rw-r--r-- | src/cvt.c | 552 | ||||
-rw-r--r-- | src/cvt.h | 26 |
2 files changed, 578 insertions, 0 deletions
diff --git a/src/cvt.c b/src/cvt.c new file mode 100644 index 0000000..9563f0f --- /dev/null +++ b/src/cvt.c @@ -0,0 +1,552 @@ +#include "cvt.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Based on the cvt util: + * http://www.uruk.org/projects/cvt/cvt.c + */ + +#include <stdlib.h> +#include <string.h> +#include <math.h> + +#define CLOCK_STEP 0.25 /* Clock steps in MHz */ +#define MARGIN_PERCENT 1.8 /* % of active vertical image */ +#define H_SYNC_PER 8.0 /* sync % of horizontal image */ +#define CELL_GRAN 8.4999 /* assumed character cell granularity */ +#define CELL_GRAN_RND 8.0 /* assumed character cell granularity (round)*/ +#define MIN_V_BPORCH 3.0 /* width of vsync in lines */ +#define MIN_V_PORCH_RND 3.0 /* width of vsync in lines */ +#define M 600.0 /* blanking formula gradient */ +#define C 40.0 /* blanking formula offset */ +#define K 128.0 /* blanking formula scaling factor */ +#define J 20.0 /* blanking formula scaling factor */ + +/* Standard Timing Parameters */ +#define MIN_VSYNC_BP 550.0 /* min time of vsync + back porch (us) */ +#define H_SYNC_PERCENT 8.0 /* width of hsync as % of total line */ + +/* Reduced Blanking defines */ +#define RB_MIN_V_BPORCH 6.0 /* lines */ +#define RB_V_FPORCH 3.0 /* lines */ +#define RB_MIN_V_BLANK 460.0 /* us */ +#define RB_H_SYNC 32.0 /* pixels */ +#define RB_H_BLANK 160.0 /* pixels */ + +/* C' and M' are part of the Blanking Duty Cycle computation */ + +#define C_PRIME (((C - J) * K/256.0) + J) +#define M_PRIME (K/256.0 * M) + +/* NOP out prints */ +#define print_value(...) (void)0 + +typedef struct __options +{ + int x, y; + int reduced_blank, interlaced; + int xf86mode, fbmode; + float v_freq; +} options; + + +/* + * vert_refresh() - as defined by the CVT Timing Standard, compute the + * Stage 1 Parameters using the vertical refresh frequency. In other + * words: input a desired resolution and desired refresh rate, and + * output the CVT mode timings. + * + * XXX margin computations are implemented but not tested (nor used by + * XFree86 of fbset mode descriptions, from what I can tell). + */ + +mode *vert_refresh (int h_pixels, int v_lines, float freq, + int interlaced, int reduced_blank, int margins) +{ + float h_pixels_rnd; + float v_lines_rnd; + float v_field_rate_rqd; + float top_margin; + float bot_margin; + float interlace; + float h_period_est; + float v_sync_bp; + float total_v_lines; + float left_margin; + float right_margin; + float total_active_pixels; + float ideal_duty_cycle; + float h_blank; + float total_pixels; + + float cur_duty_cycle; + float v_sync; + float v_sync_rnd, h_sync_rnd; + float h_back_porch, v_front_porch, h_front_porch; + + float vbi_lines, act_vbi_lines, rb_min_vbi; + float act_pixel_freq, act_h_freq; + float act_field_rate, act_frame_rate; + char *aspect_ratio; + int stage; + + mode *m = (mode*) malloc (sizeof (mode)); + + + /* 1. Required Field Rate + * + * This is slightly different from the spreadsheet because we use + * a different result for interlaced video modes. Simplifies this + * to the input field rate. + * + * [V FIELD RATE RQD] = [I/P FREQ RQD] + */ + + v_field_rate_rqd = freq; + + print_value(1, "[V FIELD RATE RQD]", v_field_rate_rqd); + + + /* 2. Horizontal Pixels + * + * In order to give correct results, the number of horizontal + * pixels requested is first processed to ensure that it is divisible + * by the character size, by rounding it to the nearest character + * cell boundary. + * + * [H PIXELS RND] = ((ROUNDDOWN([H PIXELS]/[CELL GRAN RND],0)) + * *[CELLGRAN RND]) + */ + + h_pixels_rnd = floor((float) h_pixels / CELL_GRAN_RND) * CELL_GRAN_RND; + + print_value(2, "[H PIXELS RND]", h_pixels_rnd); + + + /* 2.5th Calculation, aspect_ratio & v_sync_rnd + * + * [ASPECT_RATIO] = IF(H_PIXELS_RND = CELL_GRAN_RND*ROUND((V_LINES* + * 4.0/3.0)/CELL_GRAN_RND),"4:3") + * etc... + * [V_SYNC] = [value from table based on aspect ratio] + * [V_SYNC_RND] = ROUND(V_SYNC,0) // Not needed in principle + */ + + if (h_pixels_rnd == CELL_GRAN_RND * floor(((float)v_lines * 4.0 / 3.0) + / CELL_GRAN_RND)) { + aspect_ratio = "4:3"; + v_sync = 4; + } else if (h_pixels_rnd == CELL_GRAN_RND * floor(((float)v_lines * 16.0 + / 9.0) / CELL_GRAN_RND)) { + aspect_ratio = "16:9"; + v_sync = 5; + } else if (h_pixels_rnd == CELL_GRAN_RND * floor(((float)v_lines * 16.0 + / 10.0) / CELL_GRAN_RND)) { + aspect_ratio = "16:10"; + v_sync = 6; + } else if (h_pixels_rnd == CELL_GRAN_RND * floor(((float)v_lines * 5.0 + / 4.0) / CELL_GRAN_RND)) { + aspect_ratio = "5:4"; + v_sync = 7; + } else if (h_pixels_rnd == CELL_GRAN_RND * floor(((float)v_lines * 15.0 + / 9.0) / CELL_GRAN_RND)) { + aspect_ratio = "15:9"; + v_sync = 7; + } else { + /* Default case of unknown aspect ratio */ + aspect_ratio = "Custom"; + v_sync = 10; + } + v_sync_rnd = v_sync; + + /* + * 3. Determine Left & Right Borders + * + * Calculate the margins on the left and right side. + * + * [LEFT MARGIN (PIXELS)] = (IF( [MARGINS RQD?]="Y", + * (ROUNDDOWN( ([H PIXELS RND] * [MARGIN%] / 100 / + * [CELL GRAN RND]),0)) * [CELL GRAN RND], + * 0)) + * [RIGHT MARGIN (PIXELS)] = (IF( [MARGINS RQD?]="Y", + * (ROUNDDOWN( ([H PIXELS RND] * [MARGIN%] / 100 / + * [CELL GRAN RND]),0)) * [CELL GRAN RND], + * 0)) + */ + + left_margin = margins ? + floor(h_pixels_rnd * MARGIN_PERCENT / 100.0 / CELL_GRAN_RND) + * CELL_GRAN_RND : 0.0; + right_margin = left_margin; + + print_value(3, "[LEFT MARGIN (PIXELS)]", left_margin); + print_value(3, "[RIGHT MARGIN (PIXELS)]", right_margin); + + + /* 4. Find total active pixels. + * + * Find total number of active pixels in image and left and right + * margins. + * + * [TOTAL ACTIVE PIXELS] = [H PIXELS RND] + [LEFT MARGIN (PIXELS)] + + * [RIGHT MARGIN (PIXELS)] + */ + + total_active_pixels = h_pixels_rnd + left_margin + right_margin; + + print_value(4, "[TOTAL ACTIVE PIXELS]", total_active_pixels); + + + /* 5. Find number of lines per field. + * + * If interlace is requested, 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 to the nearest integer. + * + * [V LINES RND] = IF([INT RQD?]="y", ROUNDDOWN([V LINES]/2,0), + * ROUNDDOWN([V LINES],0)) + */ + + v_lines_rnd = interlaced ? + floor((float) v_lines / 2.0) : + floor((float) v_lines); + + print_value(5, "[V LINES RND]", v_lines_rnd); + + + /* 6. Find Top and Bottom margins. + * + * [TOP MARGIN (LINES)] = IF([MARGINS RQD?]="Y", + * ROUNDDOWN(([MARGIN%]/100*[V LINES RND]),0), + * 0) + * [BOT MARGIN (LINES)] = IF([MARGINS RQD?]="Y", + * ROUNDDOWN(([MARGIN%]/100*[V LINES RND]),0), + * 0) + */ + + top_margin = margins ? floor(MARGIN_PERCENT / 100.0 * v_lines_rnd) : (0.0); + bot_margin = top_margin; + + print_value(6, "[TOP MARGIN (LINES)]", top_margin); + print_value(6, "[BOT MARGIN (LINES)]", bot_margin); + + + /* 7. If interlace is required, then set variable [INTERLACE]=0.5: + * + * [INTERLACE]=(IF([INT RQD?]="y",0.5,0)) + */ + + interlace = interlaced ? 0.5 : 0.0; + + print_value(7, "[INTERLACE]", interlace); + + + /* + * Here it diverges for "reduced blanking" or normal blanking modes. + */ + + if (reduced_blank) { + h_blank = RB_H_BLANK; + + + /* 8. Estimate Horiz. Period (us). + * + * [H PERIOD EST] = ((1000000/V_FIELD_RATE_RQD)-RB_MIN_V_BLANK)/(V_LINES_RND+TOP_MARGIN+BOT_MARGIN) + */ + + h_period_est = (1000000.0/v_field_rate_rqd - RB_MIN_V_BLANK) + / (v_lines_rnd + top_margin + bot_margin); + + print_value(8, "[H PERIOD EST]", h_period_est); + + + /* 9. Find number of lines in vertical blanking. + * + * [Actual VBI_LINES] = RB_MIN_V_BLANK/H_PERIOD_EST + * [VBI_LINES] = ROUNDDOWN(RB_MIN_V_BLANK/H_PERIOD_EST,0) + 1 + */ + + vbi_lines = RB_MIN_V_BLANK/h_period_est; + print_value(9, "[Actual VBI LINES]", vbi_lines); + + vbi_lines = floor(vbi_lines) + 1.0; + print_value(9, "[VBI LINES]", vbi_lines); + + + /* 10. Check Vertical Blanking is sufficient. + * + * [RB MIN VBI] = RB_V_FPORCH+V_SYNC_RND+RB_MIN_V_BPORCH + * [ACT VBI LINES] = IF(VBI_LINES<RB_MIN_VBI,RB_MIN_VBI,VBI_LINES) + */ + + rb_min_vbi = RB_V_FPORCH + v_sync_rnd + RB_MIN_V_BPORCH; + act_vbi_lines = (vbi_lines < rb_min_vbi) ? rb_min_vbi : vbi_lines; + + print_value(10, "[Minimum VBI Lines]", rb_min_vbi); + print_value(10, "[ACT VBI LINES]", act_vbi_lines); + + + /* 11. Find total number of lines in vertical field. + * + * [TOTAL V LINES] = ACT_VBI_LINES+V_LINES_RND+TOP_MARGIN+BOT_MARGIN+INTERLACE + */ + + total_v_lines = act_vbi_lines + v_lines_rnd + top_margin + + bot_margin + interlace; + + print_value(11, "[TOTAL V LINES]", total_v_lines); + + + /* 12. Find total number of pixels in a line (pixels). + * + * [TOTAL PIXELS] = RB_H_BLANK+TOTAL_ACTIVE_PIXELS + */ + + total_pixels = total_active_pixels + RB_H_BLANK; + + print_value(12, "[TOTAL PIXELS]", total_pixels); + + + /* 13. Find Pixel Clock Frequency (MHz). + * + * [Non-rounded PIXEL_FREQ] = V_FIELD_RATE_RQD*TOTAL_V_LINES*TOTAL_PIXELS/1000000 + * [ACT PIXEL FREQ] = CLOCK_STEP * ROUND((V_FIELD_RATE_RQD*TOTAL_V_LINES*TOTAL_PIXELS/1000000)/CLOCK_STEP,0) + */ + + act_pixel_freq = v_field_rate_rqd * total_v_lines + * total_pixels / 1000000.0; + print_value(13, "[Non-rounded PIXEL FREQ]", act_pixel_freq); + + act_pixel_freq = CLOCK_STEP * floor(act_pixel_freq / CLOCK_STEP); + print_value(13, "[ACT PIXEL FREQ]", act_pixel_freq); + + + stage = 14; + + } else { /* Normal Blanking */ + + /* 8. Estimate Horiz. Period (us). + * + * [H PERIOD EST] = ((1/V_FIELD_RATE_RQD)-MIN_VSYNC_BP/1000000)/(V_LINES_RND+(2*TOP_MARGIN)+MIN_V_PORCH_RND+INTERLACE)*1000000 + */ + + h_period_est = ((1/v_field_rate_rqd) - MIN_VSYNC_BP/1000000.0) + / (v_lines_rnd + (2*top_margin) + MIN_V_PORCH_RND + interlace) + * 1000000.0; + + print_value(8, "[H PERIOD EST]", h_period_est); + + + /* 9. Find number of lines in (SYNC + BACK PORCH). + * + * [Estimated V_SYNC_BP] = ROUNDDOWN((MIN_VSYNC_BP/H_PERIOD_EST),0)+1 + * [Actual V_SYNC_BP] = MIN_VSYNC_BP/H_PERIOD_EST + * [V_SYNC_BP] = IF(Estimated V_SYNC_BP<(V_SYNC+MIN_V_BPORCH), + * V_SYNC+MIN_V_BPORCH,Estimated V_SYNC_BP) + */ + + v_sync_bp = MIN_VSYNC_BP/h_period_est; + print_value(9, "[Actual V_SYNC_BP]", v_sync_bp); + + v_sync_bp = floor(v_sync_bp) + 1; + print_value(9, "[Estimated V_SYNC_BP]", v_sync_bp); + + v_sync_bp = (v_sync_bp < v_sync + MIN_V_BPORCH) ? + v_sync + MIN_V_BPORCH : v_sync_bp; + print_value(9, "[V_SYNC_BP]", v_sync_bp); + + + /* 10. Find number of lines in back porch (Lines). + * + * [Back porch] = V_SYNC_BP - V_SYNC_RND; + */ + + print_value(10, "[Back porch]", v_sync_bp - v_sync_rnd); + + + /* 11. Find total number of lines in vertical field. + * + * [TOTAL V LINES] = V_LINES_RND+TOP_MARGIN+BOT_MARGIN + * +V_SYNC_BP+INTERLACE+MIN_V_PORCH_RND + */ + + total_v_lines = v_lines_rnd + top_margin + bot_margin + + v_sync_bp + interlace + MIN_V_PORCH_RND; + + print_value(11, "[TOTAL V LINES]", total_v_lines); + + + /* 12. Find ideal blanking duty cycle from formula (%): + * + * [IDEAL DUTY CYCLE] = C_PRIME-(M_PRIME*H_PERIOD_EST/1000) + */ + + ideal_duty_cycle = C_PRIME - (M_PRIME * h_period_est / 1000.0); + + print_value(12, "[IDEAL DUTY CYCLE]", ideal_duty_cycle); + + + /* 13. Find blanking time to nearest cell (Pixels). + * + * [H BLANK] = IF(IDEAL_DUTY_CYCLE<20,(ROUNDDOWN((TOTAL_ACTIVE_PIXELS*20/(100-20)/(2*CELL_GRAN_RND)),0))*(2*CELL_GRAN_RND),(ROUNDDOWN((TOTAL_ACTIVE_PIXELS*IDEAL_DUTY_CYCLE/(100-IDEAL_DUTY_CYCLE)/(2*CELL_GRAN_RND)),0))*(2*CELL_GRAN_RND)) + */ + + cur_duty_cycle = (ideal_duty_cycle < 20.0) ? 20.0 : ideal_duty_cycle; + h_blank = floor((total_active_pixels * cur_duty_cycle/(100.0 - cur_duty_cycle)/(2.0*CELL_GRAN_RND))) * (2.0*CELL_GRAN_RND); + + print_value(13, "[H BLANK]", h_blank); + + + /* 14. Find total number of pixels in a line (Pixels). + * + * [TOTAL PIXELS] = TOTAL_ACTIVE_PIXELS + H_BLANK + */ + + total_pixels = total_active_pixels + h_blank; + + print_value(14, "[TOTAL PIXELS]", total_pixels); + + + /* 15. Find pixel clock frequency (MHz). + * + * [Non-rounded PIXEL FREQ] = TOTAL_PIXELS / H_PERIOD_EST + * [ACT PIXEL FREQ] = CLOCK_STEP * ROUNDDOWN( + */ + + act_pixel_freq = total_pixels / h_period_est; + print_value(15, "[Non-rounded PIXEL FREQ]", act_pixel_freq); + + act_pixel_freq = CLOCK_STEP * floor(act_pixel_freq / CLOCK_STEP); + print_value(15, "[ACT PIXEL FREQ]", act_pixel_freq); + + + stage = 16; + } + + + /* 14/16. Find actual horizontal frequency (kHz) + * + * [ACT H FREQ] = 1000*ACT_PIXEL_FREQ/TOTAL_PIXELS + */ + + act_h_freq = 1000 * act_pixel_freq / total_pixels; + + print_value(stage, "[ACT H FREQ]", act_h_freq); + stage += 1; + + + /* 15/17. Find actual field rate (Hz) + * + * [ACT FIELD RATE] = 1000*ACT_H_FREQ/TOTAL_V_LINES + */ + + act_field_rate = 1000 * act_h_freq / total_v_lines; + + print_value(stage, "[ACT FIELD RATE]", act_field_rate); + stage += 1; + + + /* 16/18. Find actual vertical frame frequency (Hz) + * + * [ACT FRAME RATE] = IF(INT_RQD?=Y,ACT_FIELD_RATE/2,ACT_FIELD_RATE) + */ + + act_frame_rate = interlace ? + (act_field_rate / 2) : act_field_rate; + + print_value(stage, "[ACT FRAME RATE]", act_frame_rate); + + + + /* + * Extra computations not numbered in the CVT spreadsheet. + */ + + + /* 20. Find Horizontal Back Porch. + * + * [H BACK PORCH] = H_BLANK/2 + */ + + h_back_porch = h_blank/2; + + print_value(20, "[H BACK PORCH]", h_back_porch); + + + /* 21. Find Horizontal Front Porch. + * + * [H SYNC RND] = IF(RED_BLANK_RQD?="Y",RB_H_SYNC,(ROUNDDOWN((H_SYNC_PER/100*TOTAL_PIXELS/CELL_GRAN_RND),0))*CELL_GRAN_RND) + */ + + if (reduced_blank) { + h_sync_rnd = RB_H_SYNC; + } else { + h_sync_rnd = floor(H_SYNC_PER/100.0*total_pixels/CELL_GRAN_RND) + * CELL_GRAN_RND; + } + + print_value(21, "[H SYNC RND]", h_sync_rnd); + + + /* 22. Find Horizontal Front Porch. + * + * [H FRONT PORCH] = H_BLANK - H_BACK_PORCH - H_SYNC_RND + */ + + h_front_porch = h_blank - h_back_porch - h_sync_rnd; + + print_value(22, "[H FRONT PORCH]", h_front_porch); + + + /* 23. Find Vertical Front Porch. + * + * [V FRONT PORCH] = IF(RED_BLANK_RQD?="y",RB_V_FPORCH,MIN_V_PORCH_RND) + */ + + v_front_porch = reduced_blank ? RB_V_FPORCH : MIN_V_PORCH_RND; + + print_value(23, "[V FRONT PORCH]", v_front_porch); + + + + /* finally, pack the results in the mode struct */ + + m->hr = (int) (h_pixels_rnd); + m->hss = (int) (h_pixels_rnd + h_front_porch); + m->hse = (int) (h_pixels_rnd + h_front_porch + h_sync_rnd); + m->hfl = (int) (total_pixels); + +#if 0 + m->vr = (int) (v_lines_rnd); + m->vss = (int) (v_lines_rnd + v_front_porch); + m->vse = (int) (v_lines_rnd + v_front_porch + v_sync_rnd); + m->vfl = (int) (total_v_lines); +#else + { + int real_v_lines = v_lines; + m->vr = (int) (real_v_lines); + m->vss = (int) (real_v_lines + v_front_porch); + m->vse = (int) (real_v_lines + v_front_porch + v_sync_rnd); + m->vfl = (int) (total_v_lines - v_lines_rnd + real_v_lines); + } +#endif + + m->pclk = act_pixel_freq; + m->h_freq = act_h_freq; + m->v_freq = freq; + m->real_v_rate = act_field_rate; + + m->in = interlaced; + m->rb = reduced_blank; + + return (m); + +} // vert_refresh() + +#ifdef __cplusplus +} +#endif diff --git a/src/cvt.h b/src/cvt.h new file mode 100644 index 0000000..7f2ce74 --- /dev/null +++ b/src/cvt.h @@ -0,0 +1,26 @@ +#ifndef __CVT_H_ +#define __CVT_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +/* struct definitions */ + +typedef struct __mode +{ + int hr, hss, hse, hfl; + int vr, vss, vse, vfl; + float pclk, h_freq, v_freq; + float real_v_rate; + int rb, in; +} mode; + +mode *vert_refresh (int h_pixels, int v_lines, float freq, + int interlaced, int reduced_blank, int margins); + +#ifdef __cplusplus +} +#endif + +#endif |