drm/msm: program hdr information to HDMI registers

HDR metadata information is passed down from userspace
based on the HDR frame.

Program these properties to relevant HDMI registers
using the pre_kickoff callback registered for the
DRM HDMI connector.

This ensures that HDR information is updated on a
per-frame basis to the sink.

Change-Id: I20c4018316329b1b35a11b8ab4b96e923b3abb3a
Signed-off-by: Abhinav Kumar <abhinavk@codeaurora.org>
diff --git a/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi.c b/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi.c
index 8e60235..2ee75a2 100644
--- a/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi.c
+++ b/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi.c
@@ -1867,6 +1867,106 @@
 	return rc;
 }
 
+static void sde_hdmi_panel_set_hdr_infoframe(struct sde_hdmi *display,
+struct drm_msm_ext_panel_hdr_metadata *hdr_meta)
+{
+	u32 packet_payload = 0;
+	u32 packet_header = 0;
+	u32 packet_control = 0;
+	u32 const type_code = 0x87;
+	u32 const version = 0x01;
+	u32 const length = 0x1a;
+	u32 const descriptor_id = 0x00;
+	struct hdmi *hdmi;
+	struct drm_connector *connector;
+
+	if (!display || !hdr_meta) {
+		SDE_ERROR("invalid input\n");
+		return;
+	}
+
+	hdmi = display->ctrl.ctrl;
+	connector = display->ctrl.ctrl->connector;
+
+	if (!hdmi || !connector) {
+		SDE_ERROR("invalid input\n");
+		return;
+	}
+
+	if (!connector->hdr_supported) {
+		SDE_ERROR("Sink does not support HDR\n");
+		return;
+	}
+
+	/* Setup Packet header and payload */
+	packet_header = type_code | (version << 8) | (length << 16);
+	hdmi_write(hdmi, HDMI_GENERIC0_HDR, packet_header);
+
+	packet_payload = (hdr_meta->eotf << 8);
+	if (connector->hdr_metadata_type_one) {
+		packet_payload |= (descriptor_id << 16)
+			| (HDMI_GET_LSB(hdr_meta->display_primaries_x[0])
+			   << 24);
+		hdmi_write(hdmi, HDMI_GENERIC0_0, packet_payload);
+	} else {
+		pr_debug("Metadata Type 1 not supported\n");
+		hdmi_write(hdmi, HDMI_GENERIC0_0, packet_payload);
+		goto enable_packet_control;
+	}
+
+	packet_payload =
+	(HDMI_GET_MSB(hdr_meta->display_primaries_x[0]))
+	| (HDMI_GET_LSB(hdr_meta->display_primaries_y[0]) << 8)
+	| (HDMI_GET_MSB(hdr_meta->display_primaries_y[0]) << 16)
+	| (HDMI_GET_LSB(hdr_meta->display_primaries_x[1]) << 24);
+	hdmi_write(hdmi, HDMI_GENERIC0_1, packet_payload);
+
+	packet_payload =
+		(HDMI_GET_MSB(hdr_meta->display_primaries_x[1]))
+		| (HDMI_GET_LSB(hdr_meta->display_primaries_y[1]) << 8)
+		| (HDMI_GET_MSB(hdr_meta->display_primaries_y[1]) << 16)
+		| (HDMI_GET_LSB(hdr_meta->display_primaries_x[2]) << 24);
+	hdmi_write(hdmi, HDMI_GENERIC0_2, packet_payload);
+
+	packet_payload =
+		(HDMI_GET_MSB(hdr_meta->display_primaries_x[2]))
+		| (HDMI_GET_LSB(hdr_meta->display_primaries_y[2]) << 8)
+		| (HDMI_GET_MSB(hdr_meta->display_primaries_y[2]) << 16)
+		| (HDMI_GET_LSB(hdr_meta->white_point_x) << 24);
+	hdmi_write(hdmi, HDMI_GENERIC0_3, packet_payload);
+
+	packet_payload =
+		(HDMI_GET_MSB(hdr_meta->white_point_x))
+		| (HDMI_GET_LSB(hdr_meta->white_point_y) << 8)
+		| (HDMI_GET_MSB(hdr_meta->white_point_y) << 16)
+		| (HDMI_GET_LSB(hdr_meta->max_luminance) << 24);
+	hdmi_write(hdmi, HDMI_GENERIC0_4, packet_payload);
+
+	packet_payload =
+		(HDMI_GET_MSB(hdr_meta->max_luminance))
+		| (HDMI_GET_LSB(hdr_meta->min_luminance) << 8)
+		| (HDMI_GET_MSB(hdr_meta->min_luminance) << 16)
+		| (HDMI_GET_LSB(hdr_meta->max_content_light_level) << 24);
+	hdmi_write(hdmi, HDMI_GENERIC0_5, packet_payload);
+
+	packet_payload =
+		(HDMI_GET_MSB(hdr_meta->max_content_light_level))
+		| (HDMI_GET_LSB(hdr_meta->max_average_light_level) << 8)
+		| (HDMI_GET_MSB(hdr_meta->max_average_light_level) << 16);
+	hdmi_write(hdmi, HDMI_GENERIC0_6, packet_payload);
+
+enable_packet_control:
+	/*
+	 * GENERIC0_LINE | GENERIC0_CONT | GENERIC0_SEND
+	 * Setup HDMI TX generic packet control
+	 * Enable this packet to transmit every frame
+	 * Enable HDMI TX engine to transmit Generic packet 1
+	 */
+	packet_control = hdmi_read(hdmi, HDMI_GEN_PKT_CTRL);
+	packet_control |= BIT(0) | BIT(1) | BIT(2) | BIT(16);
+	hdmi_write(hdmi, HDMI_GEN_PKT_CTRL, packet_control);
+}
+
 int sde_hdmi_set_property(struct drm_connector *connector,
 			struct drm_connector_state *state,
 			int property_index,
@@ -2207,6 +2307,10 @@
 	void *display,
 	struct msm_display_kickoff_params *params)
 {
+
+	sde_hdmi_panel_set_hdr_infoframe(display,
+		params->hdr_metadata);
+
 	return 0;
 }
 
diff --git a/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi_util.h b/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi_util.h
index 6b310ac..8b69dd31 100644
--- a/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi_util.h
+++ b/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi_util.h
@@ -94,6 +94,9 @@
 /* default hsyncs for 4k@60 for 200ms */
 #define HDMI_DEFAULT_TIMEOUT_HSYNC 28571
 
+#define HDMI_GET_MSB(x)(x >> 8)
+#define HDMI_GET_LSB(x)(x & 0xff)
+
 /*
  * Bits 1:0 in HDMI_HW_DDC_CTRL that dictate how the HDCP 2.2 RxStatus will be
  * read by the hardware