drm, steamcompmgr: Initial EDID patching stuff
Initially, patch out rotations atm. We want to do more here and fix up other blocks eventually though.
This commit is contained in:
parent
11bb9f03b9
commit
a90e359fc4
3 changed files with 154 additions and 35 deletions
123
src/drm.cpp
123
src/drm.cpp
|
@ -1,6 +1,7 @@
|
||||||
// DRM output stuff
|
// DRM output stuff
|
||||||
|
|
||||||
#include <limits.h>
|
#include <limits.h>
|
||||||
|
#include <linux/limits.h>
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
@ -472,6 +473,117 @@ drm_hdr_parse_edid(drm_t *drm, struct connector *connector, const struct di_edid
|
||||||
drm_log.infof("[colorimetry]: w %f %f", metadata->colorimetry.white.x, metadata->colorimetry.white.y);
|
drm_log.infof("[colorimetry]: w %f %f", metadata->colorimetry.white.x, metadata->colorimetry.white.y);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static constexpr uint32_t EDID_MAX_BLOCK_COUNT = 256;
|
||||||
|
static constexpr uint32_t EDID_BLOCK_SIZE = 128;
|
||||||
|
static constexpr uint32_t EDID_MAX_STANDARD_TIMING_COUNT = 8;
|
||||||
|
static constexpr uint32_t EDID_BYTE_DESCRIPTOR_COUNT = 4;
|
||||||
|
static constexpr uint32_t EDID_BYTE_DESCRIPTOR_SIZE = 18;
|
||||||
|
static constexpr uint32_t EDID_MAX_DESCRIPTOR_STANDARD_TIMING_COUNT = 6;
|
||||||
|
static constexpr uint32_t EDID_MAX_DESCRIPTOR_COLOR_POINT_COUNT = 2;
|
||||||
|
static constexpr uint32_t EDID_MAX_DESCRIPTOR_ESTABLISHED_TIMING_III_COUNT = 44;
|
||||||
|
static constexpr uint32_t EDID_MAX_DESCRIPTOR_CVT_TIMING_CODES_COUNT = 4;
|
||||||
|
|
||||||
|
static inline uint8_t get_bit_range(uint8_t val, size_t high, size_t low)
|
||||||
|
{
|
||||||
|
size_t n;
|
||||||
|
uint8_t bitmask;
|
||||||
|
|
||||||
|
assert(high <= 7 && high >= low);
|
||||||
|
|
||||||
|
n = high - low + 1;
|
||||||
|
bitmask = (uint8_t) ((1 << n) - 1);
|
||||||
|
return (uint8_t) (val >> low) & bitmask;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void patch_edid_checksum(uint8_t* block)
|
||||||
|
{
|
||||||
|
uint8_t sum = 0;
|
||||||
|
for (uint32_t i = 0; i < EDID_BLOCK_SIZE - 1; i++)
|
||||||
|
sum += block[i];
|
||||||
|
|
||||||
|
uint8_t checksum = uint32_t(256) - uint32_t(sum);
|
||||||
|
|
||||||
|
block[127] = checksum;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool validate_block_checksum(const uint8_t* data)
|
||||||
|
{
|
||||||
|
uint8_t sum = 0;
|
||||||
|
size_t i;
|
||||||
|
|
||||||
|
for (i = 0; i < EDID_BLOCK_SIZE; i++) {
|
||||||
|
sum += data[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
return sum == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *drm_get_patched_edid_path()
|
||||||
|
{
|
||||||
|
return getenv("GAMESCOPE_PATCHED_EDID_FILE");
|
||||||
|
}
|
||||||
|
|
||||||
|
static void create_patched_edid( const uint8_t *orig_data, size_t orig_size, drm_t *drm, struct connector *conn )
|
||||||
|
{
|
||||||
|
std::vector<uint8_t> edid(orig_data, orig_data + orig_size);
|
||||||
|
|
||||||
|
if ( g_bRotated )
|
||||||
|
{
|
||||||
|
for (uint32_t i = 0; i < EDID_BYTE_DESCRIPTOR_COUNT; i++)
|
||||||
|
{
|
||||||
|
uint8_t *byte_desc_data = &edid[0x36 + i * EDID_BYTE_DESCRIPTOR_SIZE];
|
||||||
|
if (byte_desc_data[0] || byte_desc_data[1])
|
||||||
|
{
|
||||||
|
uint32_t horiz = (get_bit_range(byte_desc_data[4], 7, 4) << 8) | byte_desc_data[2];
|
||||||
|
uint32_t vert = (get_bit_range(byte_desc_data[7], 7, 4) << 8) | byte_desc_data[5];
|
||||||
|
drm_log.infof("[josh edid] Patching %ux%u -> %ux%u", horiz, vert, vert, horiz);
|
||||||
|
std::swap(byte_desc_data[4], byte_desc_data[7]);
|
||||||
|
std::swap(byte_desc_data[2], byte_desc_data[5]);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
patch_edid_checksum(&edid[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool sum_valid = validate_block_checksum(&edid[0]);
|
||||||
|
drm_log.infof("[josh edid] Checksum valid? %s", sum_valid ? "Y" : "N");
|
||||||
|
|
||||||
|
// Write it out then flip it over atomically.
|
||||||
|
|
||||||
|
const char *filename = drm_get_patched_edid_path();
|
||||||
|
if (!filename)
|
||||||
|
{
|
||||||
|
drm_log.errorf("[josh edid] Couldn't write patched edid. No Path.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
char filename_tmp[PATH_MAX];
|
||||||
|
snprintf(filename_tmp, sizeof(filename_tmp), "%s.tmp", filename);
|
||||||
|
|
||||||
|
FILE *file = fopen(filename_tmp, "wb");
|
||||||
|
if (!file)
|
||||||
|
{
|
||||||
|
drm_log.errorf("[josh edid] Couldn't open file: %s", filename_tmp);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
fwrite(edid.data(), 1, edid.size(), file);
|
||||||
|
fflush(file);
|
||||||
|
fclose(file);
|
||||||
|
|
||||||
|
rename(filename_tmp, filename);
|
||||||
|
drm_log.infof("[josh edid] Wrote new edid to: %s", filename);
|
||||||
|
}
|
||||||
|
|
||||||
|
void drm_update_patched_edid( drm_t *drm )
|
||||||
|
{
|
||||||
|
if (!drm || !drm->connector)
|
||||||
|
return;
|
||||||
|
|
||||||
|
create_patched_edid(drm->connector->edid_data.data(), drm->connector->edid_data.size(), drm, drm->connector);
|
||||||
|
}
|
||||||
|
|
||||||
static void parse_edid( drm_t *drm, struct connector *conn)
|
static void parse_edid( drm_t *drm, struct connector *conn)
|
||||||
{
|
{
|
||||||
memset(conn->make_pnp, 0, sizeof(conn->make_pnp));
|
memset(conn->make_pnp, 0, sizeof(conn->make_pnp));
|
||||||
|
@ -501,6 +613,8 @@ static void parse_edid( drm_t *drm, struct connector *conn)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
conn->edid_data = std::vector<uint8_t>((const uint8_t*)blob->data, ((const uint8_t*)(blob->data)) + blob->length);
|
||||||
|
|
||||||
drmModeFreePropertyBlob(blob);
|
drmModeFreePropertyBlob(blob);
|
||||||
|
|
||||||
const struct di_edid *edid = di_info_get_edid(info);
|
const struct di_edid *edid = di_info_get_edid(info);
|
||||||
|
@ -844,7 +958,7 @@ static bool get_saved_mode(const char *description, saved_mode &mode_info)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool setup_best_connector(struct drm_t *drm, bool force)
|
static bool setup_best_connector(struct drm_t *drm, bool force, bool initial)
|
||||||
{
|
{
|
||||||
if (drm->connector && drm->connector->connector->connection != DRM_MODE_CONNECTED) {
|
if (drm->connector && drm->connector->connector->connection != DRM_MODE_CONNECTED) {
|
||||||
drm_log.infof("current connector '%s' disconnected", drm->connector->name);
|
drm_log.infof("current connector '%s' disconnected", drm->connector->name);
|
||||||
|
@ -936,6 +1050,9 @@ static bool setup_best_connector(struct drm_t *drm, bool force)
|
||||||
};
|
};
|
||||||
wlserver_set_output_info(&wlserver_output_info);
|
wlserver_set_output_info(&wlserver_output_info);
|
||||||
|
|
||||||
|
if (!initial)
|
||||||
|
create_patched_edid(best->edid_data.data(), best->edid_data.size(), drm, best);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1048,7 +1165,7 @@ bool init_drm(struct drm_t *drm, int width, int height, int refresh, bool wants_
|
||||||
|
|
||||||
drm->connector_priorities = parse_connector_priorities( g_sOutputName );
|
drm->connector_priorities = parse_connector_priorities( g_sOutputName );
|
||||||
|
|
||||||
if (!setup_best_connector(drm, true)) {
|
if (!setup_best_connector(drm, true, true)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2281,7 +2398,7 @@ bool drm_poll_state( struct drm_t *drm )
|
||||||
|
|
||||||
refresh_state( drm );
|
refresh_state( drm );
|
||||||
|
|
||||||
setup_best_connector(drm, out_of_date >= 2);
|
setup_best_connector(drm, out_of_date >= 2, false);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -157,6 +157,8 @@ struct connector {
|
||||||
std::shared_ptr<wlserver_hdr_metadata> hdr_output_metadata;
|
std::shared_ptr<wlserver_hdr_metadata> hdr_output_metadata;
|
||||||
} current, pending;
|
} current, pending;
|
||||||
|
|
||||||
|
std::vector<uint8_t> edid_data;
|
||||||
|
|
||||||
bool has_colorspace;
|
bool has_colorspace;
|
||||||
bool has_content_type;
|
bool has_content_type;
|
||||||
bool has_hdr_output_metadata;
|
bool has_hdr_output_metadata;
|
||||||
|
@ -367,3 +369,6 @@ extern bool g_bSupportsAsyncFlips;
|
||||||
#define DRM_MODE_CONTENT_TYPE_PHOTO 2
|
#define DRM_MODE_CONTENT_TYPE_PHOTO 2
|
||||||
#define DRM_MODE_CONTENT_TYPE_CINEMA 3
|
#define DRM_MODE_CONTENT_TYPE_CINEMA 3
|
||||||
#define DRM_MODE_CONTENT_TYPE_GAME 4
|
#define DRM_MODE_CONTENT_TYPE_GAME 4
|
||||||
|
|
||||||
|
const char* drm_get_patched_edid_path();
|
||||||
|
void drm_update_patched_edid(drm_t *drm);
|
||||||
|
|
|
@ -495,7 +495,6 @@ bool g_bForceHDR10OutputDebug = false;
|
||||||
bool g_bForceHDRSupportDebug = false;
|
bool g_bForceHDRSupportDebug = false;
|
||||||
bool g_bHDREnabled = false;
|
bool g_bHDREnabled = false;
|
||||||
bool g_bHDRItmEnable = false;
|
bool g_bHDRItmEnable = false;
|
||||||
std::pair<uint32_t, uint32_t> g_LastConnectorIdentifier = { 0u, 0u };
|
|
||||||
|
|
||||||
Window x11_win(steamcompmgr_win_t *w) {
|
Window x11_win(steamcompmgr_win_t *w) {
|
||||||
if (w == nullptr)
|
if (w == nullptr)
|
||||||
|
@ -6294,6 +6293,30 @@ void steamcompmgr_check_xdg()
|
||||||
check_new_xdg_res();
|
check_new_xdg_res();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void update_edid_prop()
|
||||||
|
{
|
||||||
|
if ( !BIsNested() )
|
||||||
|
{
|
||||||
|
const char *filename = drm_get_patched_edid_path();
|
||||||
|
if (!filename)
|
||||||
|
return;
|
||||||
|
|
||||||
|
gamescope_xwayland_server_t *server = NULL;
|
||||||
|
for (size_t i = 0; (server = wlserver_get_xwayland_server(i)); i++)
|
||||||
|
{
|
||||||
|
XTextProperty text_property =
|
||||||
|
{
|
||||||
|
.value = (unsigned char *)filename,
|
||||||
|
.encoding = server->ctx->atoms.utf8StringAtom,
|
||||||
|
.format = 8,
|
||||||
|
.nitems = strlen(filename),
|
||||||
|
};
|
||||||
|
|
||||||
|
XSetTextProperty( server->ctx->dpy, server->ctx->root, &text_property, server->ctx->atoms.gamescopeDisplayEdidPath );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
steamcompmgr_main(int argc, char **argv)
|
steamcompmgr_main(int argc, char **argv)
|
||||||
{
|
{
|
||||||
|
@ -6470,6 +6493,11 @@ steamcompmgr_main(int argc, char **argv)
|
||||||
|
|
||||||
update_vrr_atoms(root_ctx, true);
|
update_vrr_atoms(root_ctx, true);
|
||||||
update_mode_atoms(root_ctx);
|
update_mode_atoms(root_ctx);
|
||||||
|
if ( !BIsNested() )
|
||||||
|
{
|
||||||
|
drm_update_patched_edid(&g_DRM);
|
||||||
|
update_edid_prop();
|
||||||
|
}
|
||||||
|
|
||||||
for (;;)
|
for (;;)
|
||||||
{
|
{
|
||||||
|
@ -6557,37 +6585,6 @@ steamcompmgr_main(int argc, char **argv)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( !BIsNested() )
|
|
||||||
{
|
|
||||||
auto connector_id = drm_get_connector_identifier( &g_DRM );
|
|
||||||
if ( g_LastConnectorIdentifier != connector_id )
|
|
||||||
{
|
|
||||||
const char *currentConnectorName = drm_get_connector_name( &g_DRM );
|
|
||||||
const char *device_name = drm_get_device_name( &g_DRM );
|
|
||||||
int id = 0;
|
|
||||||
if (sscanf(device_name, "/dev/dri/card%d", &id) != -1)
|
|
||||||
{
|
|
||||||
char connectorEdidPath[ 128 ];
|
|
||||||
snprintf( connectorEdidPath, sizeof( connectorEdidPath ), "/sys/class/drm/card%d/card%d-%s/edid", id, id, currentConnectorName );
|
|
||||||
|
|
||||||
XTextProperty text_property =
|
|
||||||
{
|
|
||||||
.value = (unsigned char *)connectorEdidPath,
|
|
||||||
.encoding = root_ctx->atoms.utf8StringAtom,
|
|
||||||
.format = 8,
|
|
||||||
.nitems = strlen(connectorEdidPath),
|
|
||||||
};
|
|
||||||
gamescope_xwayland_server_t *server = NULL;
|
|
||||||
for (size_t i = 0; (server = wlserver_get_xwayland_server(i)); i++)
|
|
||||||
{
|
|
||||||
XSetTextProperty( server->ctx->dpy, server->ctx->root, &text_property, server->ctx->atoms.gamescopeDisplayEdidPath );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
g_LastConnectorIdentifier = connector_id;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
g_bOutputHDREnabled = (g_bSupportsST2084_CachedValue || g_bForceHDR10OutputDebug) && g_bHDREnabled;
|
g_bOutputHDREnabled = (g_bSupportsST2084_CachedValue || g_bForceHDR10OutputDebug) && g_bHDREnabled;
|
||||||
|
|
||||||
// Pick our width/height for this potential frame, regardless of how it might change later
|
// Pick our width/height for this potential frame, regardless of how it might change later
|
||||||
|
|
Loading…
Reference in a new issue