Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

drm/amd/display: Expose HDR output metadata for supported connectors

[Why]
For userspace to send static HDR metadata to the display we need to
attach the property on the connector and send it to DC.

[How]
The property is attached to HDMI and DP connectors. Since the metadata
isn't actually available when creating the connector this isn't a
property we can dynamically support based on the extension block
being available or not.

When the HDR metadata is changed a modeset will be forced for now.
We need to switch from 8bpc to 10bpc in most cases anyway, and we want
to fully exit HDR mode when userspace gives us a NULL metadata, so this
isn't completely unnecessary.

The requirement can later be reduced to just entering and exiting HDR
or switching max bpc.

Cc: Harry Wentland <harry.wentland@amd.com>
Signed-off-by: Nicholas Kazlauskas <nicholas.kazlauskas@amd.com>
Signed-off-by: Harry Wentland <harry.wentland@amd.com>
Reviewed-by: Harry Wentland <harry.wentland@amd.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20190528190836.10738-2-nicholas.kazlauskas@amd.com

authored by

Nicholas Kazlauskas and committed by
Harry Wentland
88694af9 cfc1ce7e

+125
+125
drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
··· 3871 3871 return result; 3872 3872 } 3873 3873 3874 + static int fill_hdr_info_packet(const struct drm_connector_state *state, 3875 + struct dc_info_packet *out) 3876 + { 3877 + struct hdmi_drm_infoframe frame; 3878 + unsigned char buf[30]; /* 26 + 4 */ 3879 + ssize_t len; 3880 + int ret, i; 3881 + 3882 + memset(out, 0, sizeof(*out)); 3883 + 3884 + if (!state->hdr_output_metadata) 3885 + return 0; 3886 + 3887 + ret = drm_hdmi_infoframe_set_hdr_metadata(&frame, state); 3888 + if (ret) 3889 + return ret; 3890 + 3891 + len = hdmi_drm_infoframe_pack_only(&frame, buf, sizeof(buf)); 3892 + if (len < 0) 3893 + return (int)len; 3894 + 3895 + /* Static metadata is a fixed 26 bytes + 4 byte header. */ 3896 + if (len != 30) 3897 + return -EINVAL; 3898 + 3899 + /* Prepare the infopacket for DC. */ 3900 + switch (state->connector->connector_type) { 3901 + case DRM_MODE_CONNECTOR_HDMIA: 3902 + out->hb0 = 0x87; /* type */ 3903 + out->hb1 = 0x01; /* version */ 3904 + out->hb2 = 0x1A; /* length */ 3905 + out->sb[0] = buf[3]; /* checksum */ 3906 + i = 1; 3907 + break; 3908 + 3909 + case DRM_MODE_CONNECTOR_DisplayPort: 3910 + case DRM_MODE_CONNECTOR_eDP: 3911 + out->hb0 = 0x00; /* sdp id, zero */ 3912 + out->hb1 = 0x87; /* type */ 3913 + out->hb2 = 0x1D; /* payload len - 1 */ 3914 + out->hb3 = (0x13 << 2); /* sdp version */ 3915 + out->sb[0] = 0x01; /* version */ 3916 + out->sb[1] = 0x1A; /* length */ 3917 + i = 2; 3918 + break; 3919 + 3920 + default: 3921 + return -EINVAL; 3922 + } 3923 + 3924 + memcpy(&out->sb[i], &buf[4], 26); 3925 + out->valid = true; 3926 + 3927 + print_hex_dump(KERN_DEBUG, "HDR SB:", DUMP_PREFIX_NONE, 16, 1, out->sb, 3928 + sizeof(out->sb), false); 3929 + 3930 + return 0; 3931 + } 3932 + 3933 + static bool 3934 + is_hdr_metadata_different(const struct drm_connector_state *old_state, 3935 + const struct drm_connector_state *new_state) 3936 + { 3937 + struct drm_property_blob *old_blob = old_state->hdr_output_metadata; 3938 + struct drm_property_blob *new_blob = new_state->hdr_output_metadata; 3939 + 3940 + if (old_blob != new_blob) { 3941 + if (old_blob && new_blob && 3942 + old_blob->length == new_blob->length) 3943 + return memcmp(old_blob->data, new_blob->data, 3944 + old_blob->length); 3945 + 3946 + return true; 3947 + } 3948 + 3949 + return false; 3950 + } 3951 + 3952 + static int 3953 + amdgpu_dm_connector_atomic_check(struct drm_connector *conn, 3954 + struct drm_connector_state *new_con_state) 3955 + { 3956 + struct drm_atomic_state *state = new_con_state->state; 3957 + struct drm_connector_state *old_con_state = 3958 + drm_atomic_get_old_connector_state(state, conn); 3959 + struct drm_crtc *crtc = new_con_state->crtc; 3960 + struct drm_crtc_state *new_crtc_state; 3961 + int ret; 3962 + 3963 + if (!crtc) 3964 + return 0; 3965 + 3966 + if (is_hdr_metadata_different(old_con_state, new_con_state)) { 3967 + struct dc_info_packet hdr_infopacket; 3968 + 3969 + ret = fill_hdr_info_packet(new_con_state, &hdr_infopacket); 3970 + if (ret) 3971 + return ret; 3972 + 3973 + new_crtc_state = drm_atomic_get_crtc_state(state, crtc); 3974 + if (IS_ERR(new_crtc_state)) 3975 + return PTR_ERR(new_crtc_state); 3976 + 3977 + /* 3978 + * DC considers the stream backends changed if the 3979 + * static metadata changes. Forcing the modeset also 3980 + * gives a simple way for userspace to switch from 3981 + * 8bpc to 10bpc when setting the metadata. 3982 + */ 3983 + new_crtc_state->mode_changed = true; 3984 + } 3985 + 3986 + return 0; 3987 + } 3988 + 3874 3989 static const struct drm_connector_helper_funcs 3875 3990 amdgpu_dm_connector_helper_funcs = { 3876 3991 /* ··· 3996 3881 */ 3997 3882 .get_modes = get_modes, 3998 3883 .mode_valid = amdgpu_dm_connector_mode_valid, 3884 + .atomic_check = amdgpu_dm_connector_atomic_check, 3999 3885 }; 4000 3886 4001 3887 static void dm_crtc_helper_disable(struct drm_crtc *crtc) ··· 4793 4677 if (connector_type == DRM_MODE_CONNECTOR_HDMIA || 4794 4678 connector_type == DRM_MODE_CONNECTOR_DisplayPort || 4795 4679 connector_type == DRM_MODE_CONNECTOR_eDP) { 4680 + drm_object_attach_property( 4681 + &aconnector->base.base, 4682 + dm->ddev->mode_config.hdr_output_metadata_property, 0); 4683 + 4796 4684 drm_connector_attach_vrr_capable_property( 4797 4685 &aconnector->base); 4798 4686 } ··· 6260 6140 } 6261 6141 6262 6142 dm_new_crtc_state->abm_level = dm_new_conn_state->abm_level; 6143 + 6144 + ret = fill_hdr_info_packet(drm_new_conn_state, 6145 + &new_stream->hdr_static_metadata); 6146 + if (ret) 6147 + goto fail; 6263 6148 6264 6149 if (dc_is_stream_unchanged(new_stream, dm_old_crtc_state->stream) && 6265 6150 dc_is_stream_scaling_unchanged(new_stream, dm_old_crtc_state->stream)) {