Skip to content

Commit

Permalink
drm/i915/hdmi: split infoframe setting from infoframe type code
Browse files Browse the repository at this point in the history
This makes it easier to add support for other infoframes (e.g. SPD,
vendor specific).

Signed-off-by: Jesse Barnes <[email protected]>
Signed-off-by: Keith Packard <[email protected]>
  • Loading branch information
jbarnes993 authored and keith-packard committed Aug 4, 2011
1 parent ebec9a7 commit 45187ac
Show file tree
Hide file tree
Showing 3 changed files with 106 additions and 46 deletions.
1 change: 1 addition & 0 deletions drivers/gpu/drm/i915/i915_reg.h
Original file line number Diff line number Diff line change
Expand Up @@ -1506,6 +1506,7 @@
#define VIDEO_DIP_SELECT_AVI (0 << 19)
#define VIDEO_DIP_SELECT_VENDOR (1 << 19)
#define VIDEO_DIP_SELECT_SPD (3 << 19)
#define VIDEO_DIP_SELECT_MASK (3 << 19)
#define VIDEO_DIP_FREQ_ONCE (0 << 16)
#define VIDEO_DIP_FREQ_VSYNC (1 << 16)
#define VIDEO_DIP_FREQ_2VSYNC (2 << 16)
Expand Down
2 changes: 2 additions & 0 deletions drivers/gpu/drm/i915/intel_drv.h
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,8 @@ struct intel_crtc {
#define to_intel_encoder(x) container_of(x, struct intel_encoder, base)
#define to_intel_framebuffer(x) container_of(x, struct intel_framebuffer, base)

#define DIP_HEADER_SIZE 5

#define DIP_TYPE_AVI 0x82
#define DIP_VERSION_AVI 0x2
#define DIP_LEN_AVI 13
Expand Down
149 changes: 103 additions & 46 deletions drivers/gpu/drm/i915/intel_hdmi.c
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ struct intel_hdmi {
bool has_hdmi_sink;
bool has_audio;
int force_audio;
void (*write_infoframe)(struct drm_encoder *encoder,
struct dip_infoframe *frame);
};

static struct intel_hdmi *enc_to_intel_hdmi(struct drm_encoder *encoder)
Expand All @@ -58,37 +60,70 @@ static struct intel_hdmi *intel_attached_hdmi(struct drm_connector *connector)
struct intel_hdmi, base);
}

void intel_dip_infoframe_csum(struct dip_infoframe *avi_if)
void intel_dip_infoframe_csum(struct dip_infoframe *frame)
{
uint8_t *data = (uint8_t *)avi_if;
uint8_t *data = (uint8_t *)frame;
uint8_t sum = 0;
unsigned i;

avi_if->checksum = 0;
avi_if->ecc = 0;
frame->checksum = 0;
frame->ecc = 0;

for (i = 0; i < sizeof(*avi_if); i++)
/* Header isn't part of the checksum */
for (i = 5; i < frame->len; i++)
sum += data[i];

avi_if->checksum = 0x100 - sum;
frame->checksum = 0x100 - sum;
}

static void intel_hdmi_set_avi_infoframe(struct drm_encoder *encoder)
static u32 intel_infoframe_index(struct dip_infoframe *frame)
{
struct dip_infoframe avi_if = {
.type = DIP_TYPE_AVI,
.ver = DIP_VERSION_AVI,
.len = DIP_LEN_AVI,
};
uint32_t *data = (uint32_t *)&avi_if;
u32 flags = 0;

switch (frame->type) {
case DIP_TYPE_AVI:
flags |= VIDEO_DIP_SELECT_AVI;
break;
case DIP_TYPE_SPD:
flags |= VIDEO_DIP_SELECT_SPD;
break;
default:
DRM_DEBUG_DRIVER("unknown info frame type %d\n", frame->type);
break;
}

return flags;
}

static u32 intel_infoframe_flags(struct dip_infoframe *frame)
{
u32 flags = 0;

switch (frame->type) {
case DIP_TYPE_AVI:
flags |= VIDEO_DIP_ENABLE_AVI | VIDEO_DIP_FREQ_VSYNC;
break;
case DIP_TYPE_SPD:
flags |= VIDEO_DIP_ENABLE_SPD | VIDEO_DIP_FREQ_2VSYNC;
break;
default:
DRM_DEBUG_DRIVER("unknown info frame type %d\n", frame->type);
break;
}

return flags;
}

static void i9xx_write_infoframe(struct drm_encoder *encoder,
struct dip_infoframe *frame)
{
uint32_t *data = (uint32_t *)frame;
struct drm_device *dev = encoder->dev;
struct drm_i915_private *dev_priv = dev->dev_private;
struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(encoder);
u32 port;
unsigned i;
u32 port, flags, val = I915_READ(VIDEO_DIP_CTL);
unsigned i, len = DIP_HEADER_SIZE + frame->len;

if (!intel_hdmi->has_hdmi_sink)
return;

/* XXX first guess at handling video port, is this corrent? */
if (intel_hdmi->sdvox_reg == SDVOB)
Expand All @@ -98,52 +133,72 @@ static void intel_hdmi_set_avi_infoframe(struct drm_encoder *encoder)
else
return;

I915_WRITE(VIDEO_DIP_CTL, VIDEO_DIP_ENABLE | port |
VIDEO_DIP_SELECT_AVI | VIDEO_DIP_FREQ_VSYNC);
flags = intel_infoframe_index(frame);

val &= ~VIDEO_DIP_SELECT_MASK;

I915_WRITE(VIDEO_DIP_CTL, val | port | flags);

intel_dip_infoframe_csum(&avi_if);
for (i = 0; i < sizeof(avi_if); i += 4) {
for (i = 0; i < len; i += 4) {
I915_WRITE(VIDEO_DIP_DATA, *data);
data++;
}

I915_WRITE(VIDEO_DIP_CTL, VIDEO_DIP_ENABLE | port |
VIDEO_DIP_SELECT_AVI | VIDEO_DIP_FREQ_VSYNC |
VIDEO_DIP_ENABLE_AVI);
flags |= intel_infoframe_flags(frame);

I915_WRITE(VIDEO_DIP_CTL, VIDEO_DIP_ENABLE | val | port | flags);
}

static void intel_ironlake_hdmi_set_avi_infoframe(struct drm_encoder *encoder)
static void ironlake_write_infoframe(struct drm_encoder *encoder,
struct dip_infoframe *frame)
{
struct dip_infoframe avi_if = {
.type = DIP_TYPE_AVI,
.ver = DIP_VERSION_AVI,
.len = DIP_LEN_AVI,
};
uint32_t *data = (uint32_t *)&avi_if;
uint32_t *data = (uint32_t *)frame;
struct drm_device *dev = encoder->dev;
struct drm_i915_private *dev_priv = dev->dev_private;
struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(encoder);
struct drm_crtc *crtc = encoder->crtc;
struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
int reg = TVIDEO_DIP_CTL(intel_crtc->pipe);
unsigned i;

if (!intel_hdmi->has_hdmi_sink)
return;
unsigned i, len = DIP_HEADER_SIZE + frame->len;
u32 flags, val = I915_READ(reg);

intel_wait_for_vblank(dev, intel_crtc->pipe);

I915_WRITE(reg, VIDEO_DIP_SELECT_AVI);
flags = intel_infoframe_index(frame);

intel_dip_infoframe_csum(&avi_if);
for (i = 0; i < sizeof(avi_if); i += 4) {
val &= ~VIDEO_DIP_SELECT_MASK;

I915_WRITE(reg, val | flags);

for (i = 0; i < len; i += 4) {
I915_WRITE(TVIDEO_DIP_DATA(intel_crtc->pipe), *data);
data++;
}

I915_WRITE(reg, VIDEO_DIP_ENABLE | VIDEO_DIP_SELECT_AVI |
VIDEO_DIP_FREQ_VSYNC | (DIP_LEN_AVI << 8) |
VIDEO_DIP_ENABLE_AVI);
flags |= intel_infoframe_flags(frame);

I915_WRITE(reg, VIDEO_DIP_ENABLE | val | flags);
}
static void intel_set_infoframe(struct drm_encoder *encoder,
struct dip_infoframe *frame)
{
struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(encoder);

if (!intel_hdmi->has_hdmi_sink)
return;

intel_dip_infoframe_csum(frame);
intel_hdmi->write_infoframe(encoder, frame);
}

static void intel_hdmi_set_avi_infoframe(struct drm_encoder *encoder)
{
struct dip_infoframe avi_if = {
.type = DIP_TYPE_AVI,
.ver = DIP_VERSION_AVI,
.len = DIP_LEN_AVI,
};

intel_set_infoframe(encoder, &avi_if);
}

static void intel_hdmi_mode_set(struct drm_encoder *encoder,
Expand Down Expand Up @@ -189,10 +244,7 @@ static void intel_hdmi_mode_set(struct drm_encoder *encoder,
I915_WRITE(intel_hdmi->sdvox_reg, sdvox);
POSTING_READ(intel_hdmi->sdvox_reg);

if (HAS_PCH_SPLIT(dev))
intel_ironlake_hdmi_set_avi_infoframe(encoder);
else
intel_hdmi_set_avi_infoframe(encoder);
intel_hdmi_set_avi_infoframe(encoder);
}

static void intel_hdmi_dpms(struct drm_encoder *encoder, int mode)
Expand Down Expand Up @@ -470,6 +522,11 @@ void intel_hdmi_init(struct drm_device *dev, int sdvox_reg)

intel_hdmi->sdvox_reg = sdvox_reg;

if (!HAS_PCH_SPLIT(dev))
intel_hdmi->write_infoframe = i9xx_write_infoframe;
else
intel_hdmi->write_infoframe = ironlake_write_infoframe;

drm_encoder_helper_add(&intel_encoder->base, &intel_hdmi_helper_funcs);

intel_hdmi_add_properties(intel_hdmi, connector);
Expand Down

0 comments on commit 45187ac

Please sign in to comment.