Skip to content

Commit

Permalink
---
Browse files Browse the repository at this point in the history
yaml
---
r: 358373
b: refs/heads/master
c: ac24c22
h: refs/heads/master
i:
  358371: cfbcb22
v: v3
  • Loading branch information
Thierry Reding committed Feb 22, 2013
1 parent e2a63fc commit 8c0b9f0
Show file tree
Hide file tree
Showing 4 changed files with 111 additions and 307 deletions.
2 changes: 1 addition & 1 deletion [refs]
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
---
refs/heads/master: 5e308591a887604ed4fca7e7fcd8fb18d8bdc459
refs/heads/master: ac24c2204a76e5b42aa103bf963ae0eda1b827f3
1 change: 1 addition & 0 deletions trunk/drivers/gpu/drm/tegra/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
226 changes: 109 additions & 117 deletions trunk/drivers/gpu/drm/tegra/hdmi.c
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,16 @@
#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 <linux/regulator/consumer.h>

#include <mach/clk.h>

#include <drm/drm_edid.h>

#include "hdmi.h"
#include "drm.h"
#include "dc.h"
Expand Down Expand Up @@ -401,134 +404,94 @@ 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++);
}
}

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,
HDMI_NV_PDISP_HDMI_AVI_INFOFRAME_CTRL);
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);
Expand All @@ -537,30 +500,49 @@ 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,
HDMI_NV_PDISP_HDMI_AUDIO_INFOFRAME_CTRL);
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);
Expand All @@ -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;
Expand Down
Loading

0 comments on commit 8c0b9f0

Please sign in to comment.