162306a36Sopenharmony_ci// SPDX-License-Identifier: MIT
262306a36Sopenharmony_ci
362306a36Sopenharmony_ci#include <linux/module.h>
462306a36Sopenharmony_ci
562306a36Sopenharmony_ci#include <drm/display/drm_hdmi_helper.h>
662306a36Sopenharmony_ci#include <drm/drm_connector.h>
762306a36Sopenharmony_ci#include <drm/drm_edid.h>
862306a36Sopenharmony_ci#include <drm/drm_modes.h>
962306a36Sopenharmony_ci#include <drm/drm_print.h>
1062306a36Sopenharmony_ci#include <drm/drm_property.h>
1162306a36Sopenharmony_ci
1262306a36Sopenharmony_cistatic inline bool is_eotf_supported(u8 output_eotf, u8 sink_eotf)
1362306a36Sopenharmony_ci{
1462306a36Sopenharmony_ci	return sink_eotf & BIT(output_eotf);
1562306a36Sopenharmony_ci}
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_ci/**
1862306a36Sopenharmony_ci * drm_hdmi_infoframe_set_hdr_metadata() - fill an HDMI DRM infoframe with
1962306a36Sopenharmony_ci *                                         HDR metadata from userspace
2062306a36Sopenharmony_ci * @frame: HDMI DRM infoframe
2162306a36Sopenharmony_ci * @conn_state: Connector state containing HDR metadata
2262306a36Sopenharmony_ci *
2362306a36Sopenharmony_ci * Return: 0 on success or a negative error code on failure.
2462306a36Sopenharmony_ci */
2562306a36Sopenharmony_ciint drm_hdmi_infoframe_set_hdr_metadata(struct hdmi_drm_infoframe *frame,
2662306a36Sopenharmony_ci					const struct drm_connector_state *conn_state)
2762306a36Sopenharmony_ci{
2862306a36Sopenharmony_ci	struct drm_connector *connector;
2962306a36Sopenharmony_ci	struct hdr_output_metadata *hdr_metadata;
3062306a36Sopenharmony_ci	int err;
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_ci	if (!frame || !conn_state)
3362306a36Sopenharmony_ci		return -EINVAL;
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_ci	connector = conn_state->connector;
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_ci	if (!conn_state->hdr_output_metadata)
3862306a36Sopenharmony_ci		return -EINVAL;
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_ci	hdr_metadata = conn_state->hdr_output_metadata->data;
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_ci	if (!hdr_metadata || !connector)
4362306a36Sopenharmony_ci		return -EINVAL;
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_ci	/* Sink EOTF is Bit map while infoframe is absolute values */
4662306a36Sopenharmony_ci	if (!is_eotf_supported(hdr_metadata->hdmi_metadata_type1.eotf,
4762306a36Sopenharmony_ci	    connector->hdr_sink_metadata.hdmi_type1.eotf))
4862306a36Sopenharmony_ci		DRM_DEBUG_KMS("Unknown EOTF %d\n", hdr_metadata->hdmi_metadata_type1.eotf);
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_ci	err = hdmi_drm_infoframe_init(frame);
5162306a36Sopenharmony_ci	if (err < 0)
5262306a36Sopenharmony_ci		return err;
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_ci	frame->eotf = hdr_metadata->hdmi_metadata_type1.eotf;
5562306a36Sopenharmony_ci	frame->metadata_type = hdr_metadata->hdmi_metadata_type1.metadata_type;
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_ci	BUILD_BUG_ON(sizeof(frame->display_primaries) !=
5862306a36Sopenharmony_ci		     sizeof(hdr_metadata->hdmi_metadata_type1.display_primaries));
5962306a36Sopenharmony_ci	BUILD_BUG_ON(sizeof(frame->white_point) !=
6062306a36Sopenharmony_ci		     sizeof(hdr_metadata->hdmi_metadata_type1.white_point));
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ci	memcpy(&frame->display_primaries,
6362306a36Sopenharmony_ci	       &hdr_metadata->hdmi_metadata_type1.display_primaries,
6462306a36Sopenharmony_ci	       sizeof(frame->display_primaries));
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_ci	memcpy(&frame->white_point,
6762306a36Sopenharmony_ci	       &hdr_metadata->hdmi_metadata_type1.white_point,
6862306a36Sopenharmony_ci	       sizeof(frame->white_point));
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_ci	frame->max_display_mastering_luminance =
7162306a36Sopenharmony_ci		hdr_metadata->hdmi_metadata_type1.max_display_mastering_luminance;
7262306a36Sopenharmony_ci	frame->min_display_mastering_luminance =
7362306a36Sopenharmony_ci		hdr_metadata->hdmi_metadata_type1.min_display_mastering_luminance;
7462306a36Sopenharmony_ci	frame->max_fall = hdr_metadata->hdmi_metadata_type1.max_fall;
7562306a36Sopenharmony_ci	frame->max_cll = hdr_metadata->hdmi_metadata_type1.max_cll;
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_ci	return 0;
7862306a36Sopenharmony_ci}
7962306a36Sopenharmony_ciEXPORT_SYMBOL(drm_hdmi_infoframe_set_hdr_metadata);
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_ci/* HDMI Colorspace Spec Definitions */
8262306a36Sopenharmony_ci#define FULL_COLORIMETRY_MASK		0x1FF
8362306a36Sopenharmony_ci#define NORMAL_COLORIMETRY_MASK		0x3
8462306a36Sopenharmony_ci#define EXTENDED_COLORIMETRY_MASK	0x7
8562306a36Sopenharmony_ci#define EXTENDED_ACE_COLORIMETRY_MASK	0xF
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_ci#define C(x) ((x) << 0)
8862306a36Sopenharmony_ci#define EC(x) ((x) << 2)
8962306a36Sopenharmony_ci#define ACE(x) ((x) << 5)
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_ci#define HDMI_COLORIMETRY_NO_DATA		0x0
9262306a36Sopenharmony_ci#define HDMI_COLORIMETRY_SMPTE_170M_YCC		(C(1) | EC(0) | ACE(0))
9362306a36Sopenharmony_ci#define HDMI_COLORIMETRY_BT709_YCC		(C(2) | EC(0) | ACE(0))
9462306a36Sopenharmony_ci#define HDMI_COLORIMETRY_XVYCC_601		(C(3) | EC(0) | ACE(0))
9562306a36Sopenharmony_ci#define HDMI_COLORIMETRY_XVYCC_709		(C(3) | EC(1) | ACE(0))
9662306a36Sopenharmony_ci#define HDMI_COLORIMETRY_SYCC_601		(C(3) | EC(2) | ACE(0))
9762306a36Sopenharmony_ci#define HDMI_COLORIMETRY_OPYCC_601		(C(3) | EC(3) | ACE(0))
9862306a36Sopenharmony_ci#define HDMI_COLORIMETRY_OPRGB			(C(3) | EC(4) | ACE(0))
9962306a36Sopenharmony_ci#define HDMI_COLORIMETRY_BT2020_CYCC		(C(3) | EC(5) | ACE(0))
10062306a36Sopenharmony_ci#define HDMI_COLORIMETRY_BT2020_RGB		(C(3) | EC(6) | ACE(0))
10162306a36Sopenharmony_ci#define HDMI_COLORIMETRY_BT2020_YCC		(C(3) | EC(6) | ACE(0))
10262306a36Sopenharmony_ci#define HDMI_COLORIMETRY_DCI_P3_RGB_D65		(C(3) | EC(7) | ACE(0))
10362306a36Sopenharmony_ci#define HDMI_COLORIMETRY_DCI_P3_RGB_THEATER	(C(3) | EC(7) | ACE(1))
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_cistatic const u32 hdmi_colorimetry_val[] = {
10662306a36Sopenharmony_ci	[DRM_MODE_COLORIMETRY_NO_DATA] = HDMI_COLORIMETRY_NO_DATA,
10762306a36Sopenharmony_ci	[DRM_MODE_COLORIMETRY_SMPTE_170M_YCC] = HDMI_COLORIMETRY_SMPTE_170M_YCC,
10862306a36Sopenharmony_ci	[DRM_MODE_COLORIMETRY_BT709_YCC] = HDMI_COLORIMETRY_BT709_YCC,
10962306a36Sopenharmony_ci	[DRM_MODE_COLORIMETRY_XVYCC_601] = HDMI_COLORIMETRY_XVYCC_601,
11062306a36Sopenharmony_ci	[DRM_MODE_COLORIMETRY_XVYCC_709] = HDMI_COLORIMETRY_XVYCC_709,
11162306a36Sopenharmony_ci	[DRM_MODE_COLORIMETRY_SYCC_601] = HDMI_COLORIMETRY_SYCC_601,
11262306a36Sopenharmony_ci	[DRM_MODE_COLORIMETRY_OPYCC_601] = HDMI_COLORIMETRY_OPYCC_601,
11362306a36Sopenharmony_ci	[DRM_MODE_COLORIMETRY_OPRGB] = HDMI_COLORIMETRY_OPRGB,
11462306a36Sopenharmony_ci	[DRM_MODE_COLORIMETRY_BT2020_CYCC] = HDMI_COLORIMETRY_BT2020_CYCC,
11562306a36Sopenharmony_ci	[DRM_MODE_COLORIMETRY_BT2020_RGB] = HDMI_COLORIMETRY_BT2020_RGB,
11662306a36Sopenharmony_ci	[DRM_MODE_COLORIMETRY_BT2020_YCC] = HDMI_COLORIMETRY_BT2020_YCC,
11762306a36Sopenharmony_ci};
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_ci#undef C
12062306a36Sopenharmony_ci#undef EC
12162306a36Sopenharmony_ci#undef ACE
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_ci/**
12462306a36Sopenharmony_ci * drm_hdmi_avi_infoframe_colorimetry() - fill the HDMI AVI infoframe
12562306a36Sopenharmony_ci *                                       colorimetry information
12662306a36Sopenharmony_ci * @frame: HDMI AVI infoframe
12762306a36Sopenharmony_ci * @conn_state: connector state
12862306a36Sopenharmony_ci */
12962306a36Sopenharmony_civoid drm_hdmi_avi_infoframe_colorimetry(struct hdmi_avi_infoframe *frame,
13062306a36Sopenharmony_ci					const struct drm_connector_state *conn_state)
13162306a36Sopenharmony_ci{
13262306a36Sopenharmony_ci	u32 colorimetry_val;
13362306a36Sopenharmony_ci	u32 colorimetry_index = conn_state->colorspace & FULL_COLORIMETRY_MASK;
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_ci	if (colorimetry_index >= ARRAY_SIZE(hdmi_colorimetry_val))
13662306a36Sopenharmony_ci		colorimetry_val = HDMI_COLORIMETRY_NO_DATA;
13762306a36Sopenharmony_ci	else
13862306a36Sopenharmony_ci		colorimetry_val = hdmi_colorimetry_val[colorimetry_index];
13962306a36Sopenharmony_ci
14062306a36Sopenharmony_ci	frame->colorimetry = colorimetry_val & NORMAL_COLORIMETRY_MASK;
14162306a36Sopenharmony_ci	/*
14262306a36Sopenharmony_ci	 * ToDo: Extend it for ACE formats as well. Modify the infoframe
14362306a36Sopenharmony_ci	 * structure and extend it in drivers/video/hdmi
14462306a36Sopenharmony_ci	 */
14562306a36Sopenharmony_ci	frame->extended_colorimetry = (colorimetry_val >> 2) &
14662306a36Sopenharmony_ci					EXTENDED_COLORIMETRY_MASK;
14762306a36Sopenharmony_ci}
14862306a36Sopenharmony_ciEXPORT_SYMBOL(drm_hdmi_avi_infoframe_colorimetry);
14962306a36Sopenharmony_ci
15062306a36Sopenharmony_ci/**
15162306a36Sopenharmony_ci * drm_hdmi_avi_infoframe_bars() - fill the HDMI AVI infoframe
15262306a36Sopenharmony_ci *                                 bar information
15362306a36Sopenharmony_ci * @frame: HDMI AVI infoframe
15462306a36Sopenharmony_ci * @conn_state: connector state
15562306a36Sopenharmony_ci */
15662306a36Sopenharmony_civoid drm_hdmi_avi_infoframe_bars(struct hdmi_avi_infoframe *frame,
15762306a36Sopenharmony_ci				 const struct drm_connector_state *conn_state)
15862306a36Sopenharmony_ci{
15962306a36Sopenharmony_ci	frame->right_bar = conn_state->tv.margins.right;
16062306a36Sopenharmony_ci	frame->left_bar = conn_state->tv.margins.left;
16162306a36Sopenharmony_ci	frame->top_bar = conn_state->tv.margins.top;
16262306a36Sopenharmony_ci	frame->bottom_bar = conn_state->tv.margins.bottom;
16362306a36Sopenharmony_ci}
16462306a36Sopenharmony_ciEXPORT_SYMBOL(drm_hdmi_avi_infoframe_bars);
16562306a36Sopenharmony_ci
16662306a36Sopenharmony_ci/**
16762306a36Sopenharmony_ci * drm_hdmi_avi_infoframe_content_type() - fill the HDMI AVI infoframe
16862306a36Sopenharmony_ci *                                         content type information, based
16962306a36Sopenharmony_ci *                                         on correspondent DRM property.
17062306a36Sopenharmony_ci * @frame: HDMI AVI infoframe
17162306a36Sopenharmony_ci * @conn_state: DRM display connector state
17262306a36Sopenharmony_ci *
17362306a36Sopenharmony_ci */
17462306a36Sopenharmony_civoid drm_hdmi_avi_infoframe_content_type(struct hdmi_avi_infoframe *frame,
17562306a36Sopenharmony_ci					 const struct drm_connector_state *conn_state)
17662306a36Sopenharmony_ci{
17762306a36Sopenharmony_ci	switch (conn_state->content_type) {
17862306a36Sopenharmony_ci	case DRM_MODE_CONTENT_TYPE_GRAPHICS:
17962306a36Sopenharmony_ci		frame->content_type = HDMI_CONTENT_TYPE_GRAPHICS;
18062306a36Sopenharmony_ci		break;
18162306a36Sopenharmony_ci	case DRM_MODE_CONTENT_TYPE_CINEMA:
18262306a36Sopenharmony_ci		frame->content_type = HDMI_CONTENT_TYPE_CINEMA;
18362306a36Sopenharmony_ci		break;
18462306a36Sopenharmony_ci	case DRM_MODE_CONTENT_TYPE_GAME:
18562306a36Sopenharmony_ci		frame->content_type = HDMI_CONTENT_TYPE_GAME;
18662306a36Sopenharmony_ci		break;
18762306a36Sopenharmony_ci	case DRM_MODE_CONTENT_TYPE_PHOTO:
18862306a36Sopenharmony_ci		frame->content_type = HDMI_CONTENT_TYPE_PHOTO;
18962306a36Sopenharmony_ci		break;
19062306a36Sopenharmony_ci	default:
19162306a36Sopenharmony_ci		/* Graphics is the default(0) */
19262306a36Sopenharmony_ci		frame->content_type = HDMI_CONTENT_TYPE_GRAPHICS;
19362306a36Sopenharmony_ci	}
19462306a36Sopenharmony_ci
19562306a36Sopenharmony_ci	frame->itc = conn_state->content_type != DRM_MODE_CONTENT_TYPE_NO_DATA;
19662306a36Sopenharmony_ci}
19762306a36Sopenharmony_ciEXPORT_SYMBOL(drm_hdmi_avi_infoframe_content_type);
198