-
Notifications
You must be signed in to change notification settings - Fork 0
/
calc-ovt.cpp
180 lines (157 loc) · 4.21 KB
/
calc-ovt.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
// SPDX-License-Identifier: MIT
/*
* Copyright 2021 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
*
* Author: Hans Verkuil <[email protected]>
*/
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <time.h>
#include <numeric>
#include "edid-decode.h"
#define MinVblankDuration 460
#define MinVblankLines 20
#define MinVsyncLeadingEdge 400
#define MinVsyncLELines 14
#define MinClockRate420 590000000
#define PixelFactor420 2
#define MinHblank444 80
#define MinHblank420 128
#define PixelClockGranularity 1000
#define MinHtotalGranularity 8
#define MaxChunkRate 650000000
#define AudioPacketRate 195000
#define AudioPacketSize 32
#define LineOverhead 32
static unsigned roundup_to_power_of_two(unsigned v)
{
unsigned shift = 1;
unsigned mask = 0;
if (!v || v > 0x80000000) {
fprintf(stderr, "roundup_to_power_of_two: invalid input %u.\n", v);
exit(1);
}
v--;
do {
mask = v >> shift;
v |= mask;
shift <<= 1;
} while (mask);
return v + 1;
}
static unsigned greatest_power_of_two_divider(unsigned x)
{
return x & ~(x - 1);
}
timings edid_state::calc_ovt_mode(unsigned Hactive, unsigned Vactive,
unsigned Hratio, unsigned Vratio,
unsigned Vrate)
{
timings t = {};
t.hact = Hactive;
t.vact = Vactive;
t.hratio = Hratio;
t.vratio = Vratio;
unsigned MaxVrate = Vrate;
unsigned VtotalGranularity = 1;
// Step 1
switch (Vrate) {
case 24: case 25: case 30:
MaxVrate = 30;
VtotalGranularity = 20;
break;
case 48: case 50: case 60:
MaxVrate = 60;
VtotalGranularity = 20;
break;
case 100: case 120:
MaxVrate = 120;
VtotalGranularity = 5;
break;
case 200: case 240:
MaxVrate = 240;
VtotalGranularity = 5;
break;
case 300: case 360:
MaxVrate = 360;
VtotalGranularity = 5;
break;
case 400: case 480:
MaxVrate = 480;
VtotalGranularity = 5;
break;
}
// Step 2
double MaxActiveTime = (1000000.0 / MaxVrate) - MinVblankDuration;
double MinLineTime = MaxActiveTime / Vactive;
unsigned MinVblank = max(MinVblankLines, ceil(MinVblankDuration / MinLineTime));
unsigned MinVtotal = Vactive + MinVblank;
if (MinVtotal % VtotalGranularity)
MinVtotal += VtotalGranularity - (MinVtotal % VtotalGranularity);
// Step 3
unsigned MinLineRate = MaxVrate * MinVtotal;
unsigned MaxAudioPacketsPerLine = ceil((double)AudioPacketRate / MinLineRate);
// Step 4
unsigned MinHtotal = Hactive +
max(MinHblank444, LineOverhead + AudioPacketSize * MaxAudioPacketsPerLine);
double MinPixelClockRate = (double)MaxVrate * MinHtotal * MinVtotal;
double HCalcGranularity = roundup_to_power_of_two(ceil(MinPixelClockRate / MaxChunkRate));
unsigned HtotalGranularity = max(MinHtotalGranularity, HCalcGranularity);
if (MinHtotal % HtotalGranularity)
MinHtotal += HtotalGranularity - (MinHtotal % HtotalGranularity);
unsigned ResolutionGranularity = PixelClockGranularity /
gcd(PixelClockGranularity, MaxVrate);
unsigned Htotal = 0;
unsigned Vtotal = 0;
unsigned long long PixelClockRate = 0;
for (;;) {
// Step 5
unsigned long long RMin = 0;
unsigned V = MinVtotal;
for (;;) {
unsigned H = MinHtotal;
unsigned long long R = H * V;
if (RMin && R > RMin)
break;
while (R % ResolutionGranularity ||
MaxVrate * R / greatest_power_of_two_divider(H) > MaxChunkRate) {
H += HtotalGranularity;
R = H * V;
}
if (!RMin || R < RMin) {
Htotal = H;
Vtotal = V;
RMin = R;
}
V += VtotalGranularity;
}
PixelClockRate = MaxVrate * RMin;
// Step 6
MinHtotal = Hactive + max(MinHblank420, PixelFactor420 *
(LineOverhead + AudioPacketSize * MaxAudioPacketsPerLine));
if (PixelClockRate >= MinClockRate420 &&
Htotal < MinHtotal)
continue;
break;
}
// Step 7
Vtotal = Vtotal * MaxVrate / Vrate;
// Step 8
unsigned Vblank = Vtotal - Vactive;
unsigned VsyncPosition = max(MinVsyncLELines,
ceil((double)MinVsyncLeadingEdge * PixelClockRate / (1000000.0 * Htotal)));
t.vfp = Vblank - VsyncPosition;
t.vsync = 8;
t.vbp = Vblank - t.vfp - t.vsync;
t.pos_pol_vsync = true;
unsigned Hblank = Htotal - Hactive;
t.hsync = 32;
t.hbp = 32;
t.hfp = Hblank - t.hsync - t.hbp;
t.pos_pol_hsync = true;
t.pixclk_khz = PixelClockRate / 1000;
if (!t.hratio || !t.vratio)
calc_ratio(&t);
return t;
}