// SPDX-License-Identifier: MIT
/*
* Copyright 2021 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
*
* Author: Hans Verkuil <hverkuil-cisco@xs4all.nl>
*/
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <time.h>
#include <numeric>
#include "edid-decode.h"
#define MinVblankDuration 460
#define MinVsyncLeadingEdge 400
#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 = 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 = 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;
}