diff --git a/video/out/drm_atomic.c b/video/out/drm_atomic.c index c1a15fa75f..ffdaa391f0 100644 --- a/video/out/drm_atomic.c +++ b/video/out/drm_atomic.c @@ -299,6 +299,7 @@ void drm_atomic_destroy_context(struct drm_atomic_context *ctx) drm_object_free(ctx->connector); drm_object_free(ctx->draw_plane); drm_object_free(ctx->drmprime_video_plane); + drmModeAtomicFree(ctx->request); talloc_free(ctx); } diff --git a/video/out/drm_common.c b/video/out/drm_common.c index e53f0d5d41..be4af96f0a 100644 --- a/video/out/drm_common.c +++ b/video/out/drm_common.c @@ -59,6 +59,9 @@ #define ACQUIRE_SIGNAL SIGUSR2 #define MAX_CONNECTOR_NAME_LEN 20 +#define DRM_PRIM_FACTOR 50000 +#define DRM_MIN_LUMA_FACTOR 10000 + static int vt_switcher_pipe[2]; static int drm_connector_opt_help(struct mp_log *log, const struct m_option *opt, @@ -135,6 +138,12 @@ static const char *connector_names[] = { "USB", // DRM_MODE_CONNECTOR_USB }; +static int eotf_map[PL_COLOR_TRC_COUNT] = { + [PL_COLOR_TRC_BT_1886] = HDMI_EOTF_TRADITIONAL_GAMMA_SDR, + [PL_COLOR_TRC_PQ] = HDMI_EOTF_SMPTE_ST2084, + [PL_COLOR_TRC_HLG] = HDMI_EOTF_BT_2100_HLG, +}; + struct drm_mode_spec { enum { DRM_MODE_SPEC_BY_IDX, // Specified by idx @@ -443,6 +452,14 @@ void vo_drm_release_crtc(struct vo_drm_state *drm) } /* libdrm */ +static void destroy_hdr_blob(struct vo_drm_state *drm) +{ + if (drm->hdr.blob_id) { + drmModeDestroyPropertyBlob(drm->fd, drm->hdr.blob_id); + drm->hdr.blob_id = 0; + } +} + static void get_connector_name(const drmModeConnector *connector, char ret[MAX_CONNECTOR_NAME_LEN]) { @@ -496,6 +513,18 @@ static drmModeConnector *get_first_connected_connector(const drmModeRes *res, return NULL; } +static void restore_sdr(struct vo_drm_state *drm) +{ + struct drm_atomic_context *atomic_ctx = drm->atomic_context; + if (!atomic_ctx) + return; + + vo_drm_set_hdr_metadata(drm->vo, true); + int ret = drmModeAtomicCommit(drm->fd, atomic_ctx->request, DRM_MODE_ATOMIC_ALLOW_MODESET, drm); + if (ret) + MP_VERBOSE(drm, "Failed to commit atomic request: %s\n", mp_strerror(ret)); +} + static bool setup_connector(struct vo_drm_state *drm, const drmModeRes *res, const char *connector_name) { @@ -1047,6 +1076,9 @@ void vo_drm_uninit(struct vo *vo) if (!drm) return; + restore_sdr(drm); + destroy_hdr_blob(drm); + vo_drm_release_crtc(drm); if (drm->vt_switcher_active) vt_switcher_destroy(&drm->vt_switcher); @@ -1061,9 +1093,8 @@ void vo_drm_uninit(struct vo *vo) drmModeFreeEncoder(drm->encoder); drm->encoder = NULL; } - if (drm->atomic_context) { + if (drm->atomic_context) drm_atomic_destroy_context(drm->atomic_context); - } close(drm->fd); talloc_free(drm); @@ -1242,6 +1273,51 @@ double vo_drm_get_display_fps(struct vo_drm_state *drm) return mode_get_Hz(&drm->mode.mode); } +bool vo_drm_set_hdr_metadata(struct vo *vo, bool force_sdr) +{ + struct vo_drm_state *drm = vo->drm; + struct mp_image_params target_params = vo_get_target_params(vo); + if (!force_sdr && (pl_color_space_equal(&target_params.color, &drm->target_params.color) || + !target_params.w || !target_params.h)) + return false; + + destroy_hdr_blob(drm); + drm->target_params = target_params; + + // For any HDR, the BT2020 drm colorspace is the only one that works in practice. + struct drm_atomic_context *atomic_ctx = drm->atomic_context; + int colorspace = !force_sdr && pl_color_space_is_hdr(&drm->target_params.color) ? + DRM_MODE_COLORIMETRY_BT2020_RGB : DRM_MODE_COLORIMETRY_DEFAULT; + drm_object_set_property(atomic_ctx->request, atomic_ctx->connector, "Colorspace", colorspace); + + const struct pl_hdr_metadata *hdr = &target_params.color.hdr; + struct hdr_output_metadata metadata = { + .metadata_type = HDMI_STATIC_METADATA_TYPE1, + .hdmi_metadata_type1.metadata_type = HDMI_STATIC_METADATA_TYPE1, + + .hdmi_metadata_type1.eotf = force_sdr ? HDMI_EOTF_TRADITIONAL_GAMMA_SDR : eotf_map[target_params.color.transfer], + + .hdmi_metadata_type1.display_primaries[0].x = lrintf(hdr->prim.red.x * DRM_PRIM_FACTOR), + .hdmi_metadata_type1.display_primaries[0].y = lrintf(hdr->prim.red.y * DRM_PRIM_FACTOR), + .hdmi_metadata_type1.display_primaries[1].x = lrintf(hdr->prim.green.x * DRM_PRIM_FACTOR), + .hdmi_metadata_type1.display_primaries[1].y = lrintf(hdr->prim.green.y * DRM_PRIM_FACTOR), + .hdmi_metadata_type1.display_primaries[2].x = lrintf(hdr->prim.blue.x * DRM_PRIM_FACTOR), + .hdmi_metadata_type1.display_primaries[2].y = lrintf(hdr->prim.blue.y * DRM_PRIM_FACTOR), + + .hdmi_metadata_type1.white_point.x = lrintf(hdr->prim.white.x * DRM_PRIM_FACTOR), + .hdmi_metadata_type1.white_point.y = lrintf(hdr->prim.white.y * DRM_PRIM_FACTOR), + + .hdmi_metadata_type1.min_display_mastering_luminance = lrintf(hdr->min_luma * DRM_MIN_LUMA_FACTOR), + .hdmi_metadata_type1.max_display_mastering_luminance = hdr->max_luma, + + .hdmi_metadata_type1.max_cll = hdr->max_cll, + .hdmi_metadata_type1.max_fall = hdr->max_fall, + }; + drmModeCreatePropertyBlob(drm->fd, &metadata, sizeof(metadata), &drm->hdr.blob_id); + drm_object_set_property(atomic_ctx->request, atomic_ctx->connector, "HDR_OUTPUT_METADATA", drm->hdr.blob_id); + return true; +} + void vo_drm_set_monitor_par(struct vo *vo) { struct vo_drm_state *drm = vo->drm; diff --git a/video/out/drm_common.h b/video/out/drm_common.h index 9418b3f31e..a08177b770 100644 --- a/video/out/drm_common.h +++ b/video/out/drm_common.h @@ -21,6 +21,8 @@ #include #include #include + +#include "video/mp_image.h" #include "vo.h" enum { @@ -31,6 +33,46 @@ enum { DRM_OPTS_FORMAT_YUYV, }; +// Enum values based on include/linux/hdmi.h +// and https://docs.kernel.org/gpu/drm-uapi.html +// for interoperability with drm API. +enum drm_metadata_type { + HDMI_STATIC_METADATA_TYPE1 = 0, +}; + +// Enum values based on https://docs.kernel.org/gpu/drm-kms.html +// for interoperability with drm API. +enum drm_colorspace { + DRM_MODE_COLORIMETRY_DEFAULT = 0, + DRM_MODE_COLORIMETRY_NO_DATA = 0, + DRM_MODE_COLORIMETRY_SMPTE_170M_YCC = 1, + DRM_MODE_COLORIMETRY_BT709_YCC = 2, + DRM_MODE_COLORIMETRY_XVYCC_601 = 3, + DRM_MODE_COLORIMETRY_XVYCC_709 = 4, + DRM_MODE_COLORIMETRY_SYCC_601 = 5, + DRM_MODE_COLORIMETRY_OPYCC_601 = 6, + DRM_MODE_COLORIMETRY_OPRGB = 7, + DRM_MODE_COLORIMETRY_BT2020_CYCC = 8, + DRM_MODE_COLORIMETRY_BT2020_RGB = 9, + DRM_MODE_COLORIMETRY_BT2020_YCC = 10, + DRM_MODE_COLORIMETRY_DCI_P3_RGB_D65 = 11, + DRM_MODE_COLORIMETRY_DCI_P3_RGB_THEATER = 12, + DRM_MODE_COLORIMETRY_RGB_WIDE_FIXED = 13, + DRM_MODE_COLORIMETRY_RGB_WIDE_FLOAT = 14, + DRM_MODE_COLORIMETRY_BT601_YCC = 15, + DRM_MODE_COLORIMETRY_COUNT, +}; + +// Enum values based on include/linux/hdmi.h +// and https://docs.kernel.org/gpu/drm-uapi.html +// for interoperability with drm API. +enum drm_eotf { + HDMI_EOTF_TRADITIONAL_GAMMA_SDR, + HDMI_EOTF_TRADITIONAL_GAMMA_HDR, + HDMI_EOTF_SMPTE_ST2084, + HDMI_EOTF_BT_2100_HLG, +}; + struct framebuffer { int fd; uint32_t width; @@ -42,6 +84,11 @@ struct framebuffer { uint32_t id; }; +struct drm_hdr { + struct hdr_output_metadata metadata; + uint32_t blob_id; +}; + struct drm_mode { drmModeModeInfo mode; uint32_t blob_id; @@ -72,9 +119,11 @@ struct vo_drm_state { drmEventContext ev; struct drm_atomic_context *atomic_context; + struct drm_hdr hdr; struct drm_mode mode; struct drm_opts *opts; struct framebuffer *fb; + struct mp_image_params target_params; struct mp_log *log; struct mp_present *present; struct vo *vo; @@ -99,6 +148,7 @@ bool vo_drm_init(struct vo *vo); int vo_drm_control(struct vo *vo, int *events, int request, void *arg); double vo_drm_get_display_fps(struct vo_drm_state *drm); +bool vo_drm_set_hdr_metadata(struct vo *vo, bool force_sdr); void vo_drm_set_monitor_par(struct vo *vo); void vo_drm_uninit(struct vo *vo); void vo_drm_wait_events(struct vo *vo, int64_t until_time_ns); diff --git a/video/out/opengl/context_drm_egl.c b/video/out/opengl/context_drm_egl.c index 2a0d1883a1..1c82f39dc5 100644 --- a/video/out/opengl/context_drm_egl.c +++ b/video/out/opengl/context_drm_egl.c @@ -319,12 +319,15 @@ static void queue_flip(struct ra_ctx *ctx, struct gbm_frame *frame) update_framebuffer_from_bo(ctx, frame->bo); struct drm_atomic_context *atomic_ctx = drm->atomic_context; + int flags = DRM_MODE_ATOMIC_NONBLOCK | DRM_MODE_PAGE_FLIP_EVENT; drm_object_set_property(atomic_ctx->request, atomic_ctx->draw_plane, "FB_ID", drm->fb->id); drm_object_set_property(atomic_ctx->request, atomic_ctx->draw_plane, "CRTC_ID", atomic_ctx->crtc->id); drm_object_set_property(atomic_ctx->request, atomic_ctx->draw_plane, "ZPOS", 1); - int ret = drmModeAtomicCommit(drm->fd, atomic_ctx->request, - DRM_MODE_ATOMIC_NONBLOCK | DRM_MODE_PAGE_FLIP_EVENT, drm); + if (vo_drm_set_hdr_metadata(ctx->vo, false)) + flags |= DRM_MODE_ATOMIC_ALLOW_MODESET; + + int ret = drmModeAtomicCommit(drm->fd, atomic_ctx->request, flags, drm); if (ret) MP_WARN(ctx->vo, "Failed to commit atomic request: %s\n", mp_strerror(ret)); @@ -457,17 +460,6 @@ static const struct ra_swapchain_fns drm_egl_swapchain = { static void drm_egl_uninit(struct ra_ctx *ctx) { struct priv *p = ctx->priv; - struct vo_drm_state *drm = ctx->vo->drm; - if (drm) { - struct drm_atomic_context *atomic_ctx = drm->atomic_context; - - if (drmModeAtomicCommit(drm->fd, atomic_ctx->request, 0, NULL)) - MP_ERR(ctx->vo, "Failed to commit atomic request: %s\n", - mp_strerror(errno)); - - drmModeAtomicFree(atomic_ctx->request); - } - ra_gl_ctx_uninit(ctx); vo_drm_uninit(ctx->vo);