#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>
#include <mach/clk.h>
+#include <drm/drm_edid.h>
+
#include "hdmi.h"
#include "drm.h"
#include "dc.h"
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++);
}
}
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,
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);
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,
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);
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);
}
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;
#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