video: HDR display/output support (BT2020 / DCI P3) (#718)

* Merge branch 'hdr' of https://github.com/wickerwaka/Main_MiSTer into feature-hdr

Merged in wickerwaka's old HDR branch and integrated the BT2020 and DCI P3 color space conversion matrices. Changed "hdr" option to be a selection of matrix instead of saturation option.

* Add HDR example to MiSTer ini
This commit is contained in:
Sam Hardeman
2022-12-22 03:43:15 +09:00
committed by GitHub
parent 3214046c50
commit 2b279d1488
8 changed files with 100 additions and 11 deletions

View File

@@ -20,6 +20,9 @@ menu_pal=0 ; 1 - PAL mode for menu core
hdmi_limited=0 ; 1 - use limited (16..235) color range over HDMI
; 2 - use limited (16..255) color range over HDMI, for VGA converters.
direct_video=0 ; 1 - enable core video timing over HDMI, use only with VGA converters.
hdr=0 ; 1 - enable HDR using the BT2020 color space (faux-HDR, use color controls to tweak).
; 2 - enable HDR using the DCI P3 color space.
; 3 - enable HDR without color space conversion.
fb_size=0 ; 0 - automatic, 1 - full size, 2 - 1/2 of resolution, 4 - 1/4 of resolution.
fb_terminal=1 ; 1 - enabled (default), 0 - disabled
osd_timeout=30 ; 5-3600 timeout (in seconds) for OSD to disappear in Menu core. 0 - never timeout.

View File

@@ -113,6 +113,7 @@ static const ini_var_t ini_vars[] =
{ "VIDEO_SATURATION", (void *)(&(cfg.video_saturation)), UINT16, 0, 100},
{ "VIDEO_HUE", (void *)(&(cfg.video_hue)), UINT16, 0, 360},
{ "VIDEO_GAIN_OFFSET", (void *)(&(cfg.video_gain_offset)), STRING, 0, sizeof(cfg.video_gain_offset)},
{ "HDR", (void*)(&cfg.hdr), UINT8, 0, 3 },
};
static const int nvars = (int)(sizeof(ini_vars) / sizeof(ini_var_t));
@@ -445,6 +446,7 @@ void cfg_parse()
cfg.rumble = 1;
cfg.wheel_force = 50;
cfg.dvi_mode = 2;
cfg.hdr = 0;
cfg.video_brightness = 50;
cfg.video_contrast = 50;
cfg.video_saturation = 100;

1
cfg.h
View File

@@ -85,6 +85,7 @@ typedef struct {
uint16_t video_saturation;
uint16_t video_hue;
char video_gain_offset[256];
uint8_t hdr;
} cfg_t;
extern cfg_t cfg;

0
mat4x4.h Executable file → Normal file
View File

0
setup_default_toolchain.sh Executable file → Normal file
View File

0
str_util.cpp Executable file → Normal file
View File

0
str_util.h Executable file → Normal file
View File

105
video.cpp
View File

@@ -1044,16 +1044,17 @@ static void hdmi_config_set_spd(bool val)
}
}
static void hdmi_config_set_spare(bool val)
static void hdmi_config_set_spare(int packet, bool enabled)
{
int fd = i2c_open(0x39, 0);
uint8_t mask = packet == 0 ? 0x01 : 0x02;
if (fd >= 0)
{
uint8_t packet_val = i2c_smbus_read_byte_data(fd, 0x40);
if (val)
packet_val |= 0x01;
if (enabled)
packet_val |= mask;
else
packet_val &= ~0x01;
packet_val &= ~mask;
int res = i2c_smbus_write_byte_data(fd, 0x40, packet_val);
if (res < 0) printf("i2c: write error (%02X %02X): %d\n", 0x40, packet_val, res);
i2c_close(fd);
@@ -1094,16 +1095,35 @@ static void hdmi_config_set_csc()
0.0f, 0.0f, 0.0f, 1.0f
};
float hdr_bt2020_coeffs[] = {
0.6274f, 0.3293f, 0.0433f, 0.0f,
0.0691f, 0.9195f, 0.0114f, 0.0f,
0.0164f, 0.0880f, 0.8956f, 0.0f,
0.0f, 0.0f, 0.0f, 1.0f
};
float hdr_dcip3_coeffs[] = {
0.8225f, 0.1774f, 0.0000f, 0.0f,
0.0332f, 0.9669f, 0.0000f, 0.0f,
0.0171f, 0.0724f, 0.9108f, 0.0f,
0.0f, 0.0f, 0.0f, 1.0f
};
const float pi = float(M_PI);
// select the base CSC
int ypbpr = cfg.ypbpr && cfg.direct_video;
int hdmi_limited_1 = cfg.hdmi_limited & 1;
int hdmi_limited_2 = cfg.hdmi_limited & 2;
int hdr = cfg.hdr;
mat4x4 coeffs = hdmi_full_coeffs;
if (ypbpr)
if (hdr == 1)
coeffs = hdr_bt2020_coeffs;
else if (hdr == 2)
coeffs = hdr_dcip3_coeffs;
else if (ypbpr)
coeffs = ypbpr_coeffs;
else if (hdmi_limited_1)
coeffs = hdmi_limited_1_coeffs;
@@ -1358,12 +1378,12 @@ static void hdmi_config_init()
// Bar Info [3:2] b00 Bars invalid. b01 Bars vertical. b10 Bars horizontal. b11 Bars both.
// Scan Info [1:0] b00 (No data). b01 TV. b10 PC. b11 None.
0x56, 0b00001000, // [5:4] Picture Aspect Ratio
0x56, (uint8_t)( 0b00001000 | (cfg.hdr ? 0xb11000000 : 0)), // [5:4] Picture Aspect Ratio
// [3:0] Active Portion Aspect Ratio b1000 = Same as Picture Aspect Ratio
0x57, (uint8_t)((cfg.hdmi_game_mode ? 0x80 : 0x00) // [7] IT Content. 0 - No. 1 - Yes (type set in register 0x59).
// [6:4] Color space (ignored for RGB)
| ((ypbpr || cfg.hdmi_limited) ? 0b0100 : 0b1000)), // [3:2] RGB Quantization range
| ((ypbpr || cfg.hdmi_limited) ? 0b0100 : cfg.hdr ? 0b1101000 : 0b0001000)), // [3:2] RGB Quantization range
// [1:0] Non-Uniform Scaled: 00 - None. 01 - Horiz. 10 - Vert. 11 - Both.
0x59, (uint8_t)(cfg.hdmi_game_mode ? 0x30 : 0x00), // [7:6] [YQ1 YQ0] YCC Quantization Range: b00 = Limited Range, b01 = Full Range
@@ -1459,6 +1479,68 @@ static void hdmi_config_init()
hdmi_config_set_csc();
}
static void hdmi_config_set_hdr()
{
// 87:01:1a:74:02:00:c2:33:c4:86:4c:1d:b8:0b:d0:84:80 :3e:13:3d:42:40:e8:03:32:00:e8:03:90:01
uint8_t hdr_data[] = {
0x87,
0x01,
0x1a,
0x74,
0x02,
0x00,
0xc2,
0x33,
0xc4,
0x86,
0x4c,
0x1d,
0xb8,
0x0b,
0xd0,
0x84,
0x80,
0x3e,
0x13,
0x3d,
0x42,
0x40,
0xe8,
0x03,
0x32,
0x00,
0xe8,
0x03,
0x90,
0x01
};
if (cfg.hdr == 0)
{
hdmi_config_set_spare(1, false);
}
else
{
hdmi_config_set_spare(1, true);
int fd = i2c_open(0x38, 0);
int res = i2c_smbus_write_byte_data(fd, 0xFF, 0b10000000);
if (res < 0)
{
printf("i2c: hdr: Couldn't update Spare Packet change register (0xDF, 0x80) %d\n", res);
}
uint8_t addr = 0xe0;
for (uint i = 0; i < sizeof(hdr_data); i++)
{
res = i2c_smbus_write_byte_data(fd, addr, hdr_data[i]);
if (res < 0) printf("i2c: hdr register write error (%02X %02x): %d\n", addr, hdr_data[i], res);
addr += 1;
}
res = i2c_smbus_write_byte_data(fd, 0xfF, 0x00);
if (res < 0) printf("i2c: hdr: Couldn't update Spare Packet change register (0xDF, 0x00), %d\n", res);
}
}
static uint8_t last_sync_invert = 0xff;
static uint8_t last_pr_flags = 0xff;
static uint8_t last_vic_mode = 0xff;
@@ -1786,8 +1868,8 @@ static void set_vrr_mode()
{
if (last_vrr_mode != 0)
{
hdmi_config_set_spd(0);
hdmi_config_set_spare(0);
hdmi_config_set_spd(false);
hdmi_config_set_spare(0, false);
}
last_vrr_mode = 0;
return;
@@ -1924,7 +2006,7 @@ static void set_vrr_mode()
if (use_vrr == VRR_VESA)
{
hdmi_config_set_spare(1);
hdmi_config_set_spare(0, true);
res = i2c_smbus_write_byte_data(fd, 0xDF, 0b10000000);
if (res < 0)
{
@@ -1941,7 +2023,7 @@ static void set_vrr_mode()
}
else
{
hdmi_config_set_spare(0);
hdmi_config_set_spare(0, false);
}
i2c_close(fd);
}
@@ -2222,6 +2304,7 @@ void video_init()
{
fb_init();
hdmi_config_init();
hdmi_config_set_hdr();
video_mode_load();
has_gamma = spi_uio_cmd(UIO_SET_GAMMA);