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
|
||||
|
||||
#include <limits.h>
|
||||
#include <linux/limits.h>
|
||||
#include <stddef.h>
|
||||
#include <stdio.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);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
memset(conn->make_pnp, 0, sizeof(conn->make_pnp));
|
||||
|
@ -501,6 +613,8 @@ static void parse_edid( drm_t *drm, struct connector *conn)
|
|||
return;
|
||||
}
|
||||
|
||||
conn->edid_data = std::vector<uint8_t>((const uint8_t*)blob->data, ((const uint8_t*)(blob->data)) + blob->length);
|
||||
|
||||
drmModeFreePropertyBlob(blob);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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) {
|
||||
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);
|
||||
|
||||
if (!initial)
|
||||
create_patched_edid(best->edid_data.data(), best->edid_data.size(), drm, best);
|
||||
|
||||
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 );
|
||||
|
||||
if (!setup_best_connector(drm, true)) {
|
||||
if (!setup_best_connector(drm, true, true)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -2281,7 +2398,7 @@ bool drm_poll_state( struct drm_t *drm )
|
|||
|
||||
refresh_state( drm );
|
||||
|
||||
setup_best_connector(drm, out_of_date >= 2);
|
||||
setup_best_connector(drm, out_of_date >= 2, false);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -157,6 +157,8 @@ struct connector {
|
|||
std::shared_ptr<wlserver_hdr_metadata> hdr_output_metadata;
|
||||
} current, pending;
|
||||
|
||||
std::vector<uint8_t> edid_data;
|
||||
|
||||
bool has_colorspace;
|
||||
bool has_content_type;
|
||||
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_CINEMA 3
|
||||
#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_bHDREnabled = false;
|
||||
bool g_bHDRItmEnable = false;
|
||||
std::pair<uint32_t, uint32_t> g_LastConnectorIdentifier = { 0u, 0u };
|
||||
|
||||
Window x11_win(steamcompmgr_win_t *w) {
|
||||
if (w == nullptr)
|
||||
|
@ -6294,6 +6293,30 @@ void steamcompmgr_check_xdg()
|
|||
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
|
||||
steamcompmgr_main(int argc, char **argv)
|
||||
{
|
||||
|
@ -6470,6 +6493,11 @@ steamcompmgr_main(int argc, char **argv)
|
|||
|
||||
update_vrr_atoms(root_ctx, true);
|
||||
update_mode_atoms(root_ctx);
|
||||
if ( !BIsNested() )
|
||||
{
|
||||
drm_update_patched_edid(&g_DRM);
|
||||
update_edid_prop();
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
// Pick our width/height for this potential frame, regardless of how it might change later
|
||||
|
|
Loading…
Reference in a new issue