diff mbox

[PATCHv6,2/5] hdmi: added functions for MPEG InfoFrames

Message ID 1449218107-6590-3-git-send-email-enric.balletbo@collabora.com (mailing list archive)
State New, archived
Headers show

Commit Message

Enric Balletbo Serra Dec. 4, 2015, 8:35 a.m. UTC
The MPEG Source (MS) InfoFrame is in EIA/CEA-861B. It describes aspects of
the compressed video stream that were used to produce the uncompressed
video.

The patch adds functions to work with MPEG InfoFrames.

Signed-off-by: Enric Balletbo i Serra <enric.balletbo@collabora.com>
---

Changes since last version (requested by Thierry Redding)
 - hdmi_infoframe_pack: Fix missing break
 - hdmi_mpeg_picture_get_name: return NULL instead of "Reserved"
 - hdmi_mpeg_picture_get_name: use more canonical names "I-Frame", "P-Frame", etc
 - hdmi_mpeg_infoframe_unpack: remove braces that aren't needed
 - hdmi_vendor_any_infoframe: s/mpeg/MPEG/

 drivers/video/hdmi.c | 156 +++++++++++++++++++++++++++++++++++++++++++++++++++
 include/linux/hdmi.h |  24 ++++++++
 2 files changed, 180 insertions(+)
diff mbox

Patch

diff --git a/drivers/video/hdmi.c b/drivers/video/hdmi.c
index 1626892..47121a6 100644
--- a/drivers/video/hdmi.c
+++ b/drivers/video/hdmi.c
@@ -388,6 +388,81 @@  ssize_t hdmi_vendor_infoframe_pack(struct hdmi_vendor_infoframe *frame,
 }
 EXPORT_SYMBOL(hdmi_vendor_infoframe_pack);
 
+/**
+ * hdmi_mpeg_infoframe_init() - initialize an HDMI MPEG infoframe
+ * @frame: HDMI MPEG infoframe
+ *
+ * Returns 0 on success or a negative error code on failure.
+ */
+int hdmi_mpeg_infoframe_init(struct hdmi_mpeg_infoframe *frame)
+{
+	memset(frame, 0, sizeof(*frame));
+
+	frame->type = HDMI_INFOFRAME_TYPE_MPEG;
+	frame->version = 1;
+	frame->length = HDMI_MPEG_INFOFRAME_SIZE;
+
+	return 0;
+}
+EXPORT_SYMBOL(hdmi_mpeg_infoframe_init);
+
+/**
+ * hdmi_mpeg_infoframe_pack() - write HDMI MPEG infoframe to binary buffer
+ * @frame: HDMI MPEG 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_mpeg_infoframe_pack(struct hdmi_mpeg_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;
+
+	/*
+	 * The MPEG Bit Rate is stored as a 32-bit number and is expressed in
+	 * Hertz. MB#0 contains the least significant byte while MB#3 contains
+	 * the most significant byte. If the MPEG Bit Rate is unknown or this
+	 * field doesn’t apply, then all of the bits in Data Bytes 1-4 shall
+	 * be set to 0.
+	 */
+	ptr[0] = frame->bitrate & 0x000000ff;
+	ptr[1] = (frame->bitrate & 0x0000ff00) >> 8;
+	ptr[2] = (frame->bitrate & 0x00ff0000) >> 16;
+	ptr[3] = (frame->bitrate & 0xff000000) >> 24;
+
+	ptr[4] = frame->frame_type;
+	if (frame->repeated)
+		ptr[4] |= BIT(4);
+
+	hdmi_infoframe_set_checksum(buffer, length);
+
+	return length;
+}
+EXPORT_SYMBOL(hdmi_mpeg_infoframe_pack);
+
 /*
  * hdmi_vendor_any_infoframe_pack() - write a vendor infoframe to binary buffer
  */
@@ -435,6 +510,9 @@  hdmi_infoframe_pack(union hdmi_infoframe *frame, void *buffer, size_t size)
 		length = hdmi_vendor_any_infoframe_pack(&frame->vendor,
 							buffer, size);
 		break;
+	case HDMI_INFOFRAME_TYPE_MPEG:
+		length = hdmi_mpeg_infoframe_pack(&frame->mpeg, buffer, size);
+		break;
 	default:
 		WARN(1, "Bad infoframe type %d\n", frame->any.type);
 		length = -EINVAL;
@@ -457,6 +535,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_MPEG:
+		return "MPEG";
 	}
 	return "Reserved";
 }
@@ -899,6 +979,41 @@  static void hdmi_audio_infoframe_log(const char *level,
 			frame->downmix_inhibit ? "Yes" : "No");
 }
 
+static const char *hdmi_mpeg_picture_get_name(enum hdmi_mpeg_frame_type type)
+{
+	switch (type) {
+	case HDMI_MPEG_UNKNOWN_FRAME:
+		return "Unknown";
+	case HDMI_MPEG_I_FRAME:
+		return "Intra-coded picture";
+	case HDMI_MPEG_B_FRAME:
+		return "Bi-predictive picture";
+	case HDMI_MPEG_P_FRAME:
+		return "Predicted picture";
+	}
+	return NULL;
+}
+
+/**
+ * hdmi_mpeg_infoframe_log() - log info of HDMI MPEG infoframe
+ * @level: logging level
+ * @dev: device
+ * @frame: HDMI MPEG infoframe
+ */
+static void hdmi_mpeg_infoframe_log(const char *level,
+				     struct device *dev,
+				     struct hdmi_mpeg_infoframe *frame)
+{
+	hdmi_infoframe_log_header(level, dev,
+				  (struct hdmi_any_infoframe *)frame);
+
+	hdmi_log("    bit rate: %d Hz\n", frame->bitrate);
+	hdmi_log("    frame type: %s\n",
+			hdmi_mpeg_picture_get_name(frame->frame_type));
+	hdmi_log("    repeated frame: %s\n",
+			frame->repeated ? "Yes" : "No");
+}
+
 static const char *
 hdmi_3d_structure_get_name(enum hdmi_3d_structure s3d_struct)
 {
@@ -987,6 +1102,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_MPEG:
+		hdmi_mpeg_infoframe_log(level, dev, &frame->mpeg);
+		break;
 	}
 }
 EXPORT_SYMBOL(hdmi_infoframe_log);
@@ -1138,6 +1256,41 @@  static int hdmi_audio_infoframe_unpack(struct hdmi_audio_infoframe *frame,
 }
 
 /**
+ * hdmi_mpeg_infoframe_unpack() - unpack binary buffer to a HDMI MPEG infoframe
+ * @buffer: source buffer
+ * @frame: HDMI MPEG infoframe
+ *
+ * Unpacks the information contained in binary @buffer into a structured
+ * @frame of the HDMI MPEG information frame. Also verifies the checksum as
+ * required by section 5.3.5 of the HDMI 1.4 specification.
+ *
+ * Returns 0 on success or a negative error code on failure.
+ */
+static int hdmi_mpeg_infoframe_unpack(struct hdmi_mpeg_infoframe *frame,
+				     void *buffer)
+{
+	u8 *ptr = buffer;
+
+	if (ptr[0] != HDMI_INFOFRAME_TYPE_MPEG ||
+	    ptr[1] != 1 ||
+	    ptr[2] != HDMI_MPEG_INFOFRAME_SIZE)
+		return -EINVAL;
+
+	if (hdmi_infoframe_checksum(buffer, HDMI_INFOFRAME_SIZE(MPEG)) != 0)
+		return -EINVAL;
+
+	ptr += HDMI_INFOFRAME_HEADER_SIZE;
+
+	frame->bitrate = (ptr[3] << 24) | (ptr[2] << 16) |
+			 (ptr[1] << 8) | ptr[0];
+
+	frame->frame_type = ptr[4] & 0x03;
+	frame->repeated = ptr[4] & BIT(4) ? true : false;
+
+	return 0;
+}
+
+/**
  * hdmi_vendor_infoframe_unpack() - unpack binary buffer to a HDMI vendor infoframe
  * @buffer: source buffer
  * @frame: HDMI Vendor infoframe
@@ -1234,6 +1387,9 @@  int hdmi_infoframe_unpack(union hdmi_infoframe *frame, void *buffer)
 	case HDMI_INFOFRAME_TYPE_VENDOR:
 		ret = hdmi_vendor_any_infoframe_unpack(&frame->vendor, buffer);
 		break;
+	case HDMI_INFOFRAME_TYPE_MPEG:
+		ret = hdmi_mpeg_infoframe_unpack(&frame->mpeg, buffer);
+		break;
 	default:
 		ret = -EINVAL;
 		break;
diff --git a/include/linux/hdmi.h b/include/linux/hdmi.h
index e974420..c033554 100644
--- a/include/linux/hdmi.h
+++ b/include/linux/hdmi.h
@@ -32,11 +32,13 @@  enum hdmi_infoframe_type {
 	HDMI_INFOFRAME_TYPE_AVI = 0x82,
 	HDMI_INFOFRAME_TYPE_SPD = 0x83,
 	HDMI_INFOFRAME_TYPE_AUDIO = 0x84,
+	HDMI_INFOFRAME_TYPE_MPEG = 0x85,
 };
 
 #define HDMI_IEEE_OUI 0x000c03
 #define HDMI_INFOFRAME_HEADER_SIZE  4
 #define HDMI_AVI_INFOFRAME_SIZE    13
+#define HDMI_MPEG_INFOFRAME_SIZE   10
 #define HDMI_SPD_INFOFRAME_SIZE    25
 #define HDMI_AUDIO_INFOFRAME_SIZE  10
 
@@ -297,6 +299,26 @@  int hdmi_vendor_infoframe_init(struct hdmi_vendor_infoframe *frame);
 ssize_t hdmi_vendor_infoframe_pack(struct hdmi_vendor_infoframe *frame,
 				   void *buffer, size_t size);
 
+enum hdmi_mpeg_frame_type {
+	HDMI_MPEG_UNKNOWN_FRAME = 0x00,
+	HDMI_MPEG_I_FRAME = 0x01,
+	HDMI_MPEG_B_FRAME = 0x02,
+	HDMI_MPEG_P_FRAME = 0x03,
+};
+
+struct hdmi_mpeg_infoframe {
+	enum hdmi_infoframe_type type;
+	unsigned char version;
+	unsigned char length;
+	u32 bitrate;
+	enum hdmi_mpeg_frame_type frame_type;
+	bool repeated;
+};
+
+int hdmi_mpeg_infoframe_init(struct hdmi_mpeg_infoframe *frame);
+ssize_t hdmi_mpeg_infoframe_pack(struct hdmi_mpeg_infoframe *frame,
+				   void *buffer, size_t size);
+
 union hdmi_vendor_any_infoframe {
 	struct {
 		enum hdmi_infoframe_type type;
@@ -314,6 +336,7 @@  union hdmi_vendor_any_infoframe {
  * @spd: spd infoframe
  * @vendor: union of all vendor infoframes
  * @audio: audio infoframe
+ * @mpeg: MPEG infoframe
  *
  * This is used by the generic pack function. This works since all infoframes
  * have the same header which also indicates which type of infoframe should be
@@ -325,6 +348,7 @@  union hdmi_infoframe {
 	struct hdmi_spd_infoframe spd;
 	union hdmi_vendor_any_infoframe vendor;
 	struct hdmi_audio_infoframe audio;
+	struct hdmi_mpeg_infoframe mpeg;
 };
 
 ssize_t