[PATCH v4 7/9] drm/tegra: Use generic HDMI infoframe helpers

Thierry Reding thierry.reding at avionic-design.de
Thu Feb 21 06:50:16 PST 2013


Use the generic HDMI infoframe helpers to get rid of the NVIDIA Tegra
reimplementation.

Signed-off-by: Thierry Reding <thierry.reding at avionic-design.de>
---
 drivers/gpu/drm/tegra/Kconfig |   1 +
 drivers/gpu/drm/tegra/hdmi.c  | 226 ++++++++++++++++++++----------------------
 drivers/gpu/drm/tegra/hdmi.h  | 189 -----------------------------------
 3 files changed, 110 insertions(+), 306 deletions(-)

diff --git a/drivers/gpu/drm/tegra/Kconfig b/drivers/gpu/drm/tegra/Kconfig
index be1daf7..c92955d 100644
--- a/drivers/gpu/drm/tegra/Kconfig
+++ b/drivers/gpu/drm/tegra/Kconfig
@@ -4,6 +4,7 @@ config DRM_TEGRA
 	select DRM_KMS_HELPER
 	select DRM_GEM_CMA_HELPER
 	select DRM_KMS_CMA_HELPER
+	select DRM_HDMI
 	select FB_CFB_FILLRECT
 	select FB_CFB_COPYAREA
 	select FB_CFB_IMAGEBLIT
diff --git a/drivers/gpu/drm/tegra/hdmi.c b/drivers/gpu/drm/tegra/hdmi.c
index e060c7e..0daee8e 100644
--- a/drivers/gpu/drm/tegra/hdmi.c
+++ b/drivers/gpu/drm/tegra/hdmi.c
@@ -10,6 +10,7 @@
 #include <linux/clk.h>
 #include <linux/debugfs.h>
 #include <linux/gpio.h>
+#include <linux/hdmi.h>
 #include <linux/module.h>
 #include <linux/of.h>
 #include <linux/platform_device.h>
@@ -17,6 +18,8 @@
 
 #include <mach/clk.h>
 
+#include <drm/drm_edid.h>
+
 #include "hdmi.h"
 #include "drm.h"
 #include "dc.h"
@@ -401,54 +404,65 @@ static int tegra_hdmi_setup_audio(struct tegra_hdmi *hdmi, unsigned int pclk)
 	return 0;
 }
 
-static void tegra_hdmi_write_infopack(struct tegra_hdmi *hdmi,
-				      unsigned int offset, u8 type,
-				      u8 version, void *data, size_t size)
+static inline unsigned long tegra_hdmi_subpack(const u8 *ptr, size_t size)
 {
-	unsigned long value;
-	u8 *ptr = data;
-	u32 subpack[2];
+	unsigned long value = 0;
 	size_t i;
-	u8 csum;
 
-	/* first byte of data is the checksum */
-	csum = type + version + size - 1;
+	for (i = size; i > 0; i--)
+		value = (value << 8) | ptr[i - 1];
 
-	for (i = 1; i < size; i++)
-		csum += ptr[i];
+	return value;
+}
 
-	ptr[0] = 0x100 - csum;
+static void tegra_hdmi_write_infopack(struct tegra_hdmi *hdmi, const void *data,
+				      size_t size)
+{
+	const u8 *ptr = data;
+	unsigned long offset;
+	unsigned long value;
+	size_t i, j;
 
-	value = INFOFRAME_HEADER_TYPE(type) |
-		INFOFRAME_HEADER_VERSION(version) |
-		INFOFRAME_HEADER_LEN(size - 1);
-	tegra_hdmi_writel(hdmi, value, offset);
+	switch (ptr[0]) {
+	case HDMI_INFOFRAME_TYPE_AVI:
+		offset = HDMI_NV_PDISP_HDMI_AVI_INFOFRAME_HEADER;
+		break;
 
-	/* The audio inforame only has one set of subpack registers.  The hdmi
-	 * block pads the rest of the data as per the spec so we have to fixup
-	 * the length before filling in the subpacks.
-	 */
-	if (offset == HDMI_NV_PDISP_HDMI_AUDIO_INFOFRAME_HEADER)
-		size = 6;
+	case HDMI_INFOFRAME_TYPE_AUDIO:
+		offset = HDMI_NV_PDISP_HDMI_AUDIO_INFOFRAME_HEADER;
+		break;
 
-	/* each subpack 7 bytes devided into:
-	 *   subpack_low - bytes 0 - 3
-	 *   subpack_high - bytes 4 - 6 (with byte 7 padded to 0x00)
-	 */
-	for (i = 0; i < size; i++) {
-		size_t index = i % 7;
+	case HDMI_INFOFRAME_TYPE_VENDOR:
+		offset = HDMI_NV_PDISP_HDMI_GENERIC_HEADER;
+		break;
+
+	default:
+		dev_err(hdmi->dev, "unsupported infoframe type: %02x\n",
+			ptr[0]);
+		return;
+	}
+
+	value = INFOFRAME_HEADER_TYPE(ptr[0]) |
+		INFOFRAME_HEADER_VERSION(ptr[1]) |
+		INFOFRAME_HEADER_LEN(ptr[2]);
+	tegra_hdmi_writel(hdmi, value, offset);
+	offset++;
 
-		if (index == 0)
-			memset(subpack, 0x0, sizeof(subpack));
+	/*
+	 * Each subpack contains 7 bytes, divided into:
+	 * - subpack_low: bytes 0 - 3
+	 * - subpack_high: bytes 4 - 6 (with byte 7 padded to 0x00)
+	 */
+	for (i = 3, j = 0; i < size; i += 7, j += 8) {
+		size_t rem = size - i, num = min_t(size_t, rem, 4);
 
-		((u8 *)subpack)[index] = ptr[i];
+		value = tegra_hdmi_subpack(&ptr[i], num);
+		tegra_hdmi_writel(hdmi, value, offset++);
 
-		if (index == 6 || (i + 1 == size)) {
-			unsigned int reg = offset + 1 + (i / 7) * 2;
+		num = min_t(size_t, rem - num, 3);
 
-			tegra_hdmi_writel(hdmi, subpack[0], reg);
-			tegra_hdmi_writel(hdmi, subpack[1], reg + 1);
-		}
+		value = tegra_hdmi_subpack(&ptr[i + 4], num);
+		tegra_hdmi_writel(hdmi, value, offset++);
 	}
 }
 
@@ -456,9 +470,8 @@ static void tegra_hdmi_setup_avi_infoframe(struct tegra_hdmi *hdmi,
 					   struct drm_display_mode *mode)
 {
 	struct hdmi_avi_infoframe frame;
-	unsigned int h_front_porch;
-	unsigned int hsize = 16;
-	unsigned int vsize = 9;
+	u8 buffer[17];
+	ssize_t err;
 
 	if (hdmi->dvi) {
 		tegra_hdmi_writel(hdmi, 0,
@@ -466,69 +479,19 @@ static void tegra_hdmi_setup_avi_infoframe(struct tegra_hdmi *hdmi,
 		return;
 	}
 
-	h_front_porch = mode->hsync_start - mode->hdisplay;
-	memset(&frame, 0, sizeof(frame));
-	frame.r = HDMI_AVI_R_SAME;
-
-	switch (mode->vdisplay) {
-	case 480:
-		if (mode->hdisplay == 640) {
-			frame.m = HDMI_AVI_M_4_3;
-			frame.vic = 1;
-		} else {
-			frame.m = HDMI_AVI_M_16_9;
-			frame.vic = 3;
-		}
-		break;
-
-	case 576:
-		if (((hsize * 10) / vsize) > 14) {
-			frame.m = HDMI_AVI_M_16_9;
-			frame.vic = 18;
-		} else {
-			frame.m = HDMI_AVI_M_4_3;
-			frame.vic = 17;
-		}
-		break;
-
-	case 720:
-	case 1470: /* stereo mode */
-		frame.m = HDMI_AVI_M_16_9;
-
-		if (h_front_porch == 110)
-			frame.vic = 4;
-		else
-			frame.vic = 19;
-		break;
-
-	case 1080:
-	case 2205: /* stereo mode */
-		frame.m = HDMI_AVI_M_16_9;
-
-		switch (h_front_porch) {
-		case 88:
-			frame.vic = 16;
-			break;
-
-		case 528:
-			frame.vic = 31;
-			break;
-
-		default:
-			frame.vic = 32;
-			break;
-		}
-		break;
+	err = drm_hdmi_avi_infoframe_from_display_mode(&frame, mode);
+	if (err < 0) {
+		dev_err(hdmi->dev, "failed to setup AVI infoframe: %zd\n", err);
+		return;
+	}
 
-	default:
-		frame.m = HDMI_AVI_M_16_9;
-		frame.vic = 0;
-		break;
+	err = hdmi_avi_infoframe_pack(&frame, buffer, sizeof(buffer));
+	if (err < 0) {
+		dev_err(hdmi->dev, "failed to pack AVI infoframe: %zd\n", err);
+		return;
 	}
 
-	tegra_hdmi_write_infopack(hdmi, HDMI_NV_PDISP_HDMI_AVI_INFOFRAME_HEADER,
-				  HDMI_INFOFRAME_TYPE_AVI, HDMI_AVI_VERSION,
-				  &frame, sizeof(frame));
+	tegra_hdmi_write_infopack(hdmi, buffer, err);
 
 	tegra_hdmi_writel(hdmi, INFOFRAME_CTRL_ENABLE,
 			  HDMI_NV_PDISP_HDMI_AVI_INFOFRAME_CTRL);
@@ -537,6 +500,8 @@ static void tegra_hdmi_setup_avi_infoframe(struct tegra_hdmi *hdmi,
 static void tegra_hdmi_setup_audio_infoframe(struct tegra_hdmi *hdmi)
 {
 	struct hdmi_audio_infoframe frame;
+	u8 buffer[14];
+	ssize_t err;
 
 	if (hdmi->dvi) {
 		tegra_hdmi_writel(hdmi, 0,
@@ -544,14 +509,29 @@ static void tegra_hdmi_setup_audio_infoframe(struct tegra_hdmi *hdmi)
 		return;
 	}
 
-	memset(&frame, 0, sizeof(frame));
-	frame.cc = HDMI_AUDIO_CC_2;
+	err = hdmi_audio_infoframe_init(&frame);
+	if (err < 0) {
+		dev_err(hdmi->dev, "failed to initialize audio infoframe: %d\n",
+			err);
+		return;
+	}
+
+	frame.channels = 2;
+
+	err = hdmi_audio_infoframe_pack(&frame, buffer, sizeof(buffer));
+	if (err < 0) {
+		dev_err(hdmi->dev, "failed to pack audio infoframe: %zd\n",
+			err);
+		return;
+	}
 
-	tegra_hdmi_write_infopack(hdmi,
-				  HDMI_NV_PDISP_HDMI_AUDIO_INFOFRAME_HEADER,
-				  HDMI_INFOFRAME_TYPE_AUDIO,
-				  HDMI_AUDIO_VERSION,
-				  &frame, sizeof(frame));
+	/*
+	 * The audio infoframe has only one set of subpack registers, so the
+	 * infoframe needs to be truncated. One set of subpack registers can
+	 * contain 7 bytes. Including the 3 byte header only the first 10
+	 * bytes can be programmed.
+	 */
+	tegra_hdmi_write_infopack(hdmi, buffer, min(10, err));
 
 	tegra_hdmi_writel(hdmi, INFOFRAME_CTRL_ENABLE,
 			  HDMI_NV_PDISP_HDMI_AUDIO_INFOFRAME_CTRL);
@@ -559,8 +539,10 @@ static void tegra_hdmi_setup_audio_infoframe(struct tegra_hdmi *hdmi)
 
 static void tegra_hdmi_setup_stereo_infoframe(struct tegra_hdmi *hdmi)
 {
-	struct hdmi_stereo_infoframe frame;
+	struct hdmi_vendor_infoframe frame;
 	unsigned long value;
+	u8 buffer[10];
+	ssize_t err;
 
 	if (!hdmi->stereo) {
 		value = tegra_hdmi_readl(hdmi, HDMI_NV_PDISP_HDMI_GENERIC_CTRL);
@@ -570,22 +552,32 @@ static void tegra_hdmi_setup_stereo_infoframe(struct tegra_hdmi *hdmi)
 	}
 
 	memset(&frame, 0, sizeof(frame));
-	frame.regid0 = 0x03;
-	frame.regid1 = 0x0c;
-	frame.regid2 = 0x00;
-	frame.hdmi_video_format = 2;
+
+	frame.type = HDMI_INFOFRAME_TYPE_VENDOR;
+	frame.version = 0x01;
+	frame.length = 6;
+
+	frame.data[0] = 0x03; /* regid0 */
+	frame.data[1] = 0x0c; /* regid1 */
+	frame.data[2] = 0x00; /* regid2 */
+	frame.data[3] = 0x02 << 5; /* video format */
 
 	/* TODO: 74 MHz limit? */
 	if (1) {
-		frame._3d_structure = 0;
+		frame.data[4] = 0x00 << 4; /* 3D structure */
 	} else {
-		frame._3d_structure = 8;
-		frame._3d_ext_data = 0;
+		frame.data[4] = 0x08 << 4; /* 3D structure */
+		frame.data[5] = 0x00 << 4; /* 3D ext. data */
+	}
+
+	err = hdmi_vendor_infoframe_pack(&frame, buffer, sizeof(buffer));
+	if (err < 0) {
+		dev_err(hdmi->dev, "failed to pack vendor infoframe: %zd\n",
+			err);
+		return;
 	}
 
-	tegra_hdmi_write_infopack(hdmi, HDMI_NV_PDISP_HDMI_GENERIC_HEADER,
-				  HDMI_INFOFRAME_TYPE_VENDOR,
-				  HDMI_VENDOR_VERSION, &frame, 6);
+	tegra_hdmi_write_infopack(hdmi, buffer, err);
 
 	value = tegra_hdmi_readl(hdmi, HDMI_NV_PDISP_HDMI_GENERIC_CTRL);
 	value |= GENERIC_CTRL_ENABLE;
diff --git a/drivers/gpu/drm/tegra/hdmi.h b/drivers/gpu/drm/tegra/hdmi.h
index 1477f36..52ac36e 100644
--- a/drivers/gpu/drm/tegra/hdmi.h
+++ b/drivers/gpu/drm/tegra/hdmi.h
@@ -10,195 +10,6 @@
 #ifndef TEGRA_HDMI_H
 #define TEGRA_HDMI_H 1
 
-#define HDMI_INFOFRAME_TYPE_VENDOR   0x81
-#define HDMI_INFOFRAME_TYPE_AVI      0x82
-#define HDMI_INFOFRAME_TYPE_SPD      0x83
-#define HDMI_INFOFRAME_TYPE_AUDIO    0x84
-#define HDMI_INFOFRAME_TYPE_MPEG_SRC 0x85
-#define HDMI_INFOFRAME_TYPE_NTSC_VBI 0x86
-
-/* all fields little endian */
-struct hdmi_avi_infoframe {
-	/* PB0 */
-	u8 csum;
-
-	/* PB1 */
-	unsigned s:2; /* scan information */
-	unsigned b:2; /* bar info data valid */
-	unsigned a:1; /* active info present */
-	unsigned y:2; /* RGB or YCbCr */
-	unsigned res1:1;
-
-	/* PB2 */
-	unsigned r:4; /* active format aspect ratio */
-	unsigned m:2; /* picture aspect ratio */
-	unsigned c:2; /* colorimetry */
-
-	/* PB3 */
-	unsigned sc:2;  /* scan information */
-	unsigned q:2;   /* quantization range */
-	unsigned ec:3;  /* extended colorimetry */
-	unsigned itc:1; /* it content */
-
-	/* PB4 */
-	unsigned vic:7; /* video format id code */
-	unsigned res4:1;
-
-	/* PB5 */
-	unsigned pr:4; /* pixel repetition factor */
-	unsigned cn:2; /* it content type*/
-	unsigned yq:2; /* ycc quantization range */
-
-	/* PB6-7 */
-	u16 top_bar_end_line;
-
-	/* PB8-9 */
-	u16 bot_bar_start_line;
-
-	/* PB10-11 */
-	u16 left_bar_end_pixel;
-
-	/* PB12-13 */
-	u16 right_bar_start_pixel;
-} __packed;
-
-#define HDMI_AVI_VERSION 0x02
-
-#define HDMI_AVI_Y_RGB       0x0
-#define HDMI_AVI_Y_YCBCR_422 0x1
-#define HDMI_AVI_Y_YCBCR_444 0x2
-
-#define HDMI_AVI_B_VERT  0x1
-#define HDMI_AVI_B_HORIZ 0x2
-
-#define HDMI_AVI_S_NONE      0x0
-#define HDMI_AVI_S_OVERSCAN  0x1
-#define HDMI_AVI_S_UNDERSCAN 0x2
-
-#define HDMI_AVI_C_NONE     0x0
-#define HDMI_AVI_C_SMPTE    0x1
-#define HDMI_AVI_C_ITU_R    0x2
-#define HDMI_AVI_C_EXTENDED 0x4
-
-#define HDMI_AVI_M_4_3  0x1
-#define HDMI_AVI_M_16_9 0x2
-
-#define HDMI_AVI_R_SAME        0x8
-#define HDMI_AVI_R_4_3_CENTER  0x9
-#define HDMI_AVI_R_16_9_CENTER 0xa
-#define HDMI_AVI_R_14_9_CENTER 0xb
-
-/* all fields little endian */
-struct hdmi_audio_infoframe {
-	/* PB0 */
-	u8 csum;
-
-	/* PB1 */
-	unsigned cc:3; /* channel count */
-	unsigned res1:1;
-	unsigned ct:4; /* coding type */
-
-	/* PB2 */
-	unsigned ss:2; /* sample size */
-	unsigned sf:3; /* sample frequency */
-	unsigned res2:3;
-
-	/* PB3 */
-	unsigned cxt:5; /* coding extention type */
-	unsigned res3:3;
-
-	/* PB4 */
-	u8 ca; /* channel/speaker allocation */
-
-	/* PB5 */
-	unsigned res5:3;
-	unsigned lsv:4; /* level shift value */
-	unsigned dm_inh:1; /* downmix inhibit */
-
-	/* PB6-10 reserved */
-	u8 res6;
-	u8 res7;
-	u8 res8;
-	u8 res9;
-	u8 res10;
-} __packed;
-
-#define HDMI_AUDIO_VERSION 0x01
-
-#define HDMI_AUDIO_CC_STREAM 0x0 /* specified by audio stream */
-#define HDMI_AUDIO_CC_2      0x1
-#define HDMI_AUDIO_CC_3      0x2
-#define HDMI_AUDIO_CC_4      0x3
-#define HDMI_AUDIO_CC_5      0x4
-#define HDMI_AUDIO_CC_6      0x5
-#define HDMI_AUDIO_CC_7      0x6
-#define HDMI_AUDIO_CC_8      0x7
-
-#define HDMI_AUDIO_CT_STREAM  0x0 /* specified by audio stream */
-#define HDMI_AUDIO_CT_PCM     0x1
-#define HDMI_AUDIO_CT_AC3     0x2
-#define HDMI_AUDIO_CT_MPEG1   0x3
-#define HDMI_AUDIO_CT_MP3     0x4
-#define HDMI_AUDIO_CT_MPEG2   0x5
-#define HDMI_AUDIO_CT_AAC_LC  0x6
-#define HDMI_AUDIO_CT_DTS     0x7
-#define HDMI_AUDIO_CT_ATRAC   0x8
-#define HDMI_AUDIO_CT_DSD     0x9
-#define HDMI_AUDIO_CT_E_AC3   0xa
-#define HDMI_AUDIO_CT_DTS_HD  0xb
-#define HDMI_AUDIO_CT_MLP     0xc
-#define HDMI_AUDIO_CT_DST     0xd
-#define HDMI_AUDIO_CT_WMA_PRO 0xe
-#define HDMI_AUDIO_CT_CXT     0xf
-
-#define HDMI_AUDIO_SF_STREAM 0x0 /* specified by audio stream */
-#define HDMI_AUIDO_SF_32K    0x1
-#define HDMI_AUDIO_SF_44_1K  0x2
-#define HDMI_AUDIO_SF_48K    0x3
-#define HDMI_AUDIO_SF_88_2K  0x4
-#define HDMI_AUDIO_SF_96K    0x5
-#define HDMI_AUDIO_SF_176_4K 0x6
-#define HDMI_AUDIO_SF_192K   0x7
-
-#define HDMI_AUDIO_SS_STREAM 0x0 /* specified by audio stream */
-#define HDMI_AUDIO_SS_16BIT  0x1
-#define HDMI_AUDIO_SS_20BIT  0x2
-#define HDMI_AUDIO_SS_24BIT  0x3
-
-#define HDMI_AUDIO_CXT_CT            0x0 /* refer to coding in CT */
-#define HDMI_AUDIO_CXT_HE_AAC        0x1
-#define HDMI_AUDIO_CXT_HE_AAC_V2     0x2
-#define HDMI_AUDIO_CXT_MPEG_SURROUND 0x3
-
-/* all fields little endian */
-struct hdmi_stereo_infoframe {
-	/* PB0 */
-	u8 csum;
-
-	/* PB1 */
-	u8 regid0;
-
-	/* PB2 */
-	u8 regid1;
-
-	/* PB3 */
-	u8 regid2;
-
-	/* PB4 */
-	unsigned res1:5;
-	unsigned hdmi_video_format:3;
-
-	/* PB5 */
-	unsigned res2:4;
-	unsigned _3d_structure:4;
-
-	/* PB6*/
-	unsigned res3:4;
-	unsigned _3d_ext_data:4;
-} __packed;
-
-#define HDMI_VENDOR_VERSION 0x01
-
 /* register definitions */
 #define HDMI_CTXSW						0x00
 
-- 
1.8.1.2



More information about the dri-devel mailing list