[RFC,8/9] drm: Enable HDR infoframe support

Submitted by Shankar, Uma on May 31, 2017, 10:10 a.m.

Details

Message ID 1496225457-30514-9-git-send-email-uma.shankar@intel.com
State New
Headers show
Series "Add HDR Metadata Parsing and handling in DRM layer" ( rev: 1 ) in DRI devel

Commit Message

Shankar, Uma May 31, 2017, 10:10 a.m.
Enable Dynamic Range and Mastering Infoframe for HDR
content, which is defined in CEA 861.3 spec.

 The metadata will be computed based on blending
policy in userspace compositors and passed as a connector
property blob to driver. The same will be sent as infoframe
to panel which support HDR.

Signed-off-by: Uma Shankar <uma.shankar@intel.com>
---
 drivers/gpu/drm/drm_edid.c |   53 +++++++++++++++++
 drivers/video/hdmi.c       |  138 ++++++++++++++++++++++++++++++++++++++++++++
 include/drm/drm_edid.h     |    4 ++
 include/linux/hdmi.h       |   21 +++++++
 4 files changed, 216 insertions(+)

Patch hide | download patch | download mbox

diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c
index c9b9c3c..3b99284 100644
--- a/drivers/gpu/drm/drm_edid.c
+++ b/drivers/gpu/drm/drm_edid.c
@@ -4428,6 +4428,59 @@  void drm_set_preferred_mode(struct drm_connector *connector,
 EXPORT_SYMBOL(drm_set_preferred_mode);
 
 /**
+ * drm_hdmi_infoframe_set_hdr_metadata() - fill an HDMI AVI infoframe with
+ *                                              HDR metadata from userspace
+ * @frame: HDMI AVI infoframe
+ * @hdr_source_metadata: hdr_source_metadata info from userspace
+ *
+ * Return: 0 on success or a negative error code on failure.
+ */
+int
+drm_hdmi_infoframe_set_hdr_metadata(struct hdmi_drm_infoframe *frame,
+				   void *hdr_metadata)
+{
+	struct hdr_static_metadata *hdr_source_metadata;
+	int err, i;
+
+	if (!frame || !hdr_metadata)
+		return -EINVAL;
+
+	err = hdmi_drm_infoframe_init(frame);
+	if (err < 0)
+		return err;
+
+	hdr_source_metadata = (struct hdr_static_metadata *)hdr_metadata;
+
+	frame->length = sizeof(struct hdr_static_metadata);
+
+
+	frame->eotf = hdr_source_metadata->eotf;
+	frame->type = hdr_source_metadata->type;
+
+	for (i = 0; i < 3; i++) {
+		frame->display_primaries_x[i] =
+			hdr_source_metadata->display_primaries_x[i];
+		frame->display_primaries_y[i] =
+			hdr_source_metadata->display_primaries_y[i];
+	}
+
+	frame->white_point_x = hdr_source_metadata->white_point_x;
+	frame->white_point_y = hdr_source_metadata->white_point_y;
+
+	frame->max_mastering_display_luminance =
+		hdr_source_metadata->max_mastering_display_luminance;
+	frame->min_mastering_display_luminance =
+		hdr_source_metadata->min_mastering_display_luminance;
+
+	frame->max_cll = hdr_source_metadata->max_cll;
+	frame->max_fall = hdr_source_metadata->max_fall;
+
+	return 0;
+}
+EXPORT_SYMBOL(drm_hdmi_infoframe_set_hdr_metadata);
+
+
+/**
  * drm_hdmi_avi_infoframe_from_display_mode() - fill an HDMI AVI infoframe with
  *                                              data from a DRM display mode
  * @frame: HDMI AVI infoframe
diff --git a/drivers/video/hdmi.c b/drivers/video/hdmi.c
index 1cf907e..2740f40 100644
--- a/drivers/video/hdmi.c
+++ b/drivers/video/hdmi.c
@@ -388,6 +388,103 @@  ssize_t hdmi_vendor_infoframe_pack(struct hdmi_vendor_infoframe *frame,
 }
 EXPORT_SYMBOL(hdmi_vendor_infoframe_pack);
 
+/**
+ * hdmi_drm_infoframe_init() - initialize an HDMI Dynaminc Range and
+ * mastering infoframe
+ * @frame: HDMI DRM infoframe
+ *
+ * Returns 0 on success or a negative error code on failure.
+ */
+int hdmi_drm_infoframe_init(struct hdmi_drm_infoframe *frame)
+{
+	memset(frame, 0, sizeof(*frame));
+
+	frame->type = HDMI_INFOFRAME_TYPE_DRM;
+	frame->version = 1;
+
+	return 0;
+}
+EXPORT_SYMBOL(hdmi_drm_infoframe_init);
+
+/**
+ * hdmi_drm_infoframe_pack() - write HDMI DRM infoframe to binary buffer
+ * @frame: HDMI DRM infoframe
+ * @buffer: destination buffer
+ * @size: size of buffer
+ *
+ * Packs the information contained in the @frame structure into a binary
+ * representation that can be written into the corresponding controller
+ * registers. Also computes the checksum as required by section 5.3.5 of
+ * the HDMI 1.4 specification.
+ *
+ * Returns the number of bytes packed into the binary buffer or a negative
+ * error code on failure.
+ */
+ssize_t hdmi_drm_infoframe_pack(struct hdmi_drm_infoframe *frame, void *buffer,
+                                size_t size)
+{
+	u8 *ptr = buffer;
+	size_t length;
+
+	length = HDMI_INFOFRAME_HEADER_SIZE + frame->length;
+
+	if (size < length)
+		return -ENOSPC;
+
+	memset(buffer, 0, size);
+
+	ptr[0] = frame->type;
+	ptr[1] = frame->version;
+	ptr[2] = frame->length;
+	ptr[3] = 0; /* checksum */
+
+	/* start infoframe payload */
+	ptr += HDMI_INFOFRAME_HEADER_SIZE;
+
+	ptr[0] = frame->eotf;
+	ptr[1] = frame->metadata_type;
+
+	ptr[2] = frame->display_primaries_x[0] & 0xff;
+	ptr[3] = frame->display_primaries_x[0] >> 8;
+
+	ptr[4] = frame->display_primaries_x[1] & 0xff;
+	ptr[5] = frame->display_primaries_x[1] >> 8;
+
+	ptr[6] = frame->display_primaries_x[2] & 0xff;
+	ptr[7] = frame->display_primaries_x[2] >> 8;
+
+	ptr[9] = frame->display_primaries_y[0] & 0xff;
+	ptr[10] = frame->display_primaries_y[0] >> 8;
+
+	ptr[11] = frame->display_primaries_y[1] & 0xff;
+	ptr[12] = frame->display_primaries_y[1] >> 8;
+
+	ptr[13] = frame->display_primaries_y[2] & 0xff;
+	ptr[14] = frame->display_primaries_y[2] >> 8;
+
+	ptr[15] = frame->white_point_x & 0xff;
+	ptr[16] = frame->white_point_x >> 8;
+
+	ptr[17] = frame->white_point_y & 0xff;
+	ptr[18] = frame->white_point_y >> 8;
+
+	ptr[19] = frame->max_mastering_display_luminance & 0xff;
+	ptr[20] = frame->max_mastering_display_luminance >> 8;
+
+	ptr[21] = frame->min_mastering_display_luminance & 0xff;
+	ptr[22] = frame->min_mastering_display_luminance >> 8;
+
+	ptr[23] = frame->max_cll & 0xff;
+	ptr[24] = frame->max_cll >> 8;
+
+	ptr[25] = frame->max_fall & 0xff;
+	ptr[26] = frame->max_fall >> 8;
+
+	hdmi_infoframe_set_checksum(buffer, length);
+
+	return length;
+}
+
 /*
  * hdmi_vendor_any_infoframe_pack() - write a vendor infoframe to binary buffer
  */
@@ -425,6 +522,9 @@  ssize_t hdmi_vendor_infoframe_pack(struct hdmi_vendor_infoframe *frame,
 	case HDMI_INFOFRAME_TYPE_AVI:
 		length = hdmi_avi_infoframe_pack(&frame->avi, buffer, size);
 		break;
+	case HDMI_INFOFRAME_TYPE_DRM:
+		length = hdmi_drm_infoframe_pack(&frame->drm, buffer, size);
+		break;
 	case HDMI_INFOFRAME_TYPE_SPD:
 		length = hdmi_spd_infoframe_pack(&frame->spd, buffer, size);
 		break;
@@ -457,6 +557,8 @@  static const char *hdmi_infoframe_type_get_name(enum hdmi_infoframe_type type)
 		return "Source Product Description (SPD)";
 	case HDMI_INFOFRAME_TYPE_AUDIO:
 		return "Audio";
+	case HDMI_INFOFRAME_TYPE_DRM:
+		return "Dynamic Range and Mastering";
 	}
 	return "Reserved";
 }
@@ -903,6 +1005,39 @@  static void hdmi_audio_infoframe_log(const char *level,
 			frame->downmix_inhibit ? "Yes" : "No");
 }
 
+/**
+ * hdmi_drm_infoframe_log() - log info of HDMI DRM infoframe
+ * @level: logging level
+ * @dev: device
+ * @frame: HDMI DRM infoframe
+ */
+static void hdmi_drm_infoframe_log(const char *level,
+                                  struct device *dev,
+                                  struct hdmi_drm_infoframe *frame)
+{
+	int i;
+
+	hdmi_infoframe_log_header(level, dev,
+			(struct hdmi_any_infoframe *)frame);
+	hdmi_log("length: %d\n", frame->length);
+	hdmi_log("eotf: %d\n", frame->eotf);
+	for (i = 0; i <= 2; i++) {
+		hdmi_log("x[%d]: %d\n", i, frame->display_primaries_x[i]);
+		hdmi_log("y[%d]: %d\n", i, frame->display_primaries_y[i]);
+	}
+
+	hdmi_log("white point x: %d\n", frame->white_point_x);
+	hdmi_log("white point y: %d\n", frame->white_point_y);
+
+	hdmi_log("max_mastering_display_luminance: %d\n",
+			frame->max_mastering_display_luminance);
+	hdmi_log("min_mastering_display_luminance: %d\n",
+			frame->min_mastering_display_luminance);
+
+	hdmi_log("max_cll: %d\n", frame->max_cll);
+	hdmi_log("max_fall: %d\n", frame->max_fall);
+}
+
 static const char *
 hdmi_3d_structure_get_name(enum hdmi_3d_structure s3d_struct)
 {
@@ -991,6 +1126,9 @@  void hdmi_infoframe_log(const char *level,
 	case HDMI_INFOFRAME_TYPE_VENDOR:
 		hdmi_vendor_any_infoframe_log(level, dev, &frame->vendor);
 		break;
+	case HDMI_INFOFRAME_TYPE_DRM:
+		hdmi_drm_infoframe_log(level, dev, &frame->drm);
+		break;
 	}
 }
 EXPORT_SYMBOL(hdmi_infoframe_log);
diff --git a/include/drm/drm_edid.h b/include/drm/drm_edid.h
index 7b9f48b..f80c235 100644
--- a/include/drm/drm_edid.h
+++ b/include/drm/drm_edid.h
@@ -353,6 +353,10 @@  int drm_av_sync_delay(struct drm_connector *connector,
 				   enum hdmi_quantization_range rgb_quant_range,
 				   bool rgb_quant_range_selectable);
 
+int
+drm_hdmi_infoframe_set_hdr_metadata(struct hdmi_drm_infoframe *frame,
+				    void *hdr_source_metadata);
+
 /**
  * drm_eld_mnl - Get ELD monitor name length in bytes.
  * @eld: pointer to an eld memory structure with mnl set
diff --git a/include/linux/hdmi.h b/include/linux/hdmi.h
index d271ff2..a8095e1 100644
--- a/include/linux/hdmi.h
+++ b/include/linux/hdmi.h
@@ -32,6 +32,7 @@  enum hdmi_infoframe_type {
 	HDMI_INFOFRAME_TYPE_AVI = 0x82,
 	HDMI_INFOFRAME_TYPE_SPD = 0x83,
 	HDMI_INFOFRAME_TYPE_AUDIO = 0x84,
+	HDMI_INFOFRAME_TYPE_DRM = 0x87,
 };
 
 #define HDMI_IEEE_OUI 0x000c03
@@ -160,10 +161,29 @@  struct hdmi_avi_infoframe {
 	unsigned short right_bar;
 };
 
+struct hdmi_drm_infoframe {
+	enum hdmi_infoframe_type type;
+	unsigned char version;
+	unsigned char length;
+	uint16_t eotf;
+	uint16_t metadata_type;
+	uint16_t display_primaries_x[3];
+	uint16_t display_primaries_y[3];
+	uint16_t white_point_x;
+	uint16_t white_point_y;
+	uint16_t max_mastering_display_luminance;
+	uint16_t min_mastering_display_luminance;
+	uint16_t max_fall;
+	uint16_t max_cll;
+	uint16_t min_cll;
+};
+
 int hdmi_avi_infoframe_init(struct hdmi_avi_infoframe *frame);
 ssize_t hdmi_avi_infoframe_pack(struct hdmi_avi_infoframe *frame, void *buffer,
 				size_t size);
 
+int hdmi_drm_infoframe_init(struct hdmi_drm_infoframe *frame);
+
 enum hdmi_spd_sdi {
 	HDMI_SPD_SDI_UNKNOWN,
 	HDMI_SPD_SDI_DSTB,
@@ -328,6 +348,7 @@  ssize_t hdmi_vendor_infoframe_pack(struct hdmi_vendor_infoframe *frame,
 	struct hdmi_spd_infoframe spd;
 	union hdmi_vendor_any_infoframe vendor;
 	struct hdmi_audio_infoframe audio;
+	struct hdmi_drm_infoframe drm;
 };
 
 ssize_t