From 837ba00f20aa47018a3317bc7c1f058be0a92e39 Mon Sep 17 00:00:00 2001 From: Paulo Zanoni Date: Fri, 4 May 2012 17:18:14 -0300 Subject: [PATCH 01/57] drm/i915: DSL_LINEMASK is 12 bits only on gen2 Gen3+ is 13 bits (12:0), and on gen2 only 12 (11:0). For both the high bits are marked reserved, read-only so continue to mask them. Bit 31 is not reserved and has a meaning. Signed-off-by: Paulo Zanoni Reviewed-by: Chris Wilson Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_reg.h | 3 ++- drivers/gpu/drm/i915/intel_display.c | 11 ++++++++--- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h index 10e71a9f8bd..833052ef7cf 100644 --- a/drivers/gpu/drm/i915/i915_reg.h +++ b/drivers/gpu/drm/i915/i915_reg.h @@ -2476,7 +2476,8 @@ /* Pipe A */ #define _PIPEADSL 0x70000 -#define DSL_LINEMASK 0x00000fff +#define DSL_LINEMASK_GEN2 0x00000fff +#define DSL_LINEMASK_GEN3 0x00001fff #define _PIPEACONF 0x70008 #define PIPECONF_ENABLE (1<<31) #define PIPECONF_DISABLE 0 diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 6b4139064f9..83b785f400f 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -849,15 +849,20 @@ void intel_wait_for_pipe_off(struct drm_device *dev, int pipe) 100)) DRM_DEBUG_KMS("pipe_off wait timed out\n"); } else { - u32 last_line; + u32 last_line, line_mask; int reg = PIPEDSL(pipe); unsigned long timeout = jiffies + msecs_to_jiffies(100); + if (IS_GEN2(dev)) + line_mask = DSL_LINEMASK_GEN2; + else + line_mask = DSL_LINEMASK_GEN3; + /* Wait for the display line to settle */ do { - last_line = I915_READ(reg) & DSL_LINEMASK; + last_line = I915_READ(reg) & line_mask; mdelay(5); - } while (((I915_READ(reg) & DSL_LINEMASK) != last_line) && + } while (((I915_READ(reg) & line_mask) != last_line) && time_after(timeout, jiffies)); if (time_after(jiffies, timeout)) DRM_DEBUG_KMS("pipe_off wait timed out\n"); From 22509ec8676fdbba8da525b9ec9cb3ddb4cb71b0 Mon Sep 17 00:00:00 2001 From: Paulo Zanoni Date: Fri, 4 May 2012 17:18:17 -0300 Subject: [PATCH 02/57] drm/i915: change coding style of the write_infoframe functions Don't use intermediate variables, change the value of 'val' as we go through the function. The new style looks more similar to the rest of our code. IMHO, it's also easier to read and change. Signed-off-by: Paulo Zanoni Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_hdmi.c | 43 +++++++++++++++++-------------- 1 file changed, 23 insertions(+), 20 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_hdmi.c b/drivers/gpu/drm/i915/intel_hdmi.c index bf218753cba..b84d19d0eae 100644 --- a/drivers/gpu/drm/i915/intel_hdmi.c +++ b/drivers/gpu/drm/i915/intel_hdmi.c @@ -120,32 +120,33 @@ static void i9xx_write_infoframe(struct drm_encoder *encoder, 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, flags, val = I915_READ(VIDEO_DIP_CTL); + u32 val = I915_READ(VIDEO_DIP_CTL); unsigned i, len = DIP_HEADER_SIZE + frame->len; /* XXX first guess at handling video port, is this corrent? */ if (intel_hdmi->sdvox_reg == SDVOB) - port = VIDEO_DIP_PORT_B; + val |= VIDEO_DIP_PORT_B; else if (intel_hdmi->sdvox_reg == SDVOC) - port = VIDEO_DIP_PORT_C; + val |= VIDEO_DIP_PORT_C; else return; - flags = intel_infoframe_index(frame); - val &= ~VIDEO_DIP_SELECT_MASK; + val |= intel_infoframe_index(frame); - I915_WRITE(VIDEO_DIP_CTL, VIDEO_DIP_ENABLE | val | port | flags); + val |= VIDEO_DIP_ENABLE; + + I915_WRITE(VIDEO_DIP_CTL, val); for (i = 0; i < len; i += 4) { I915_WRITE(VIDEO_DIP_DATA, *data); data++; } - flags |= intel_infoframe_flags(frame); + val |= intel_infoframe_flags(frame); - I915_WRITE(VIDEO_DIP_CTL, VIDEO_DIP_ENABLE | val | port | flags); + I915_WRITE(VIDEO_DIP_CTL, val); } static void ironlake_write_infoframe(struct drm_encoder *encoder, @@ -158,24 +159,25 @@ static void ironlake_write_infoframe(struct drm_encoder *encoder, struct intel_crtc *intel_crtc = to_intel_crtc(crtc); int reg = TVIDEO_DIP_CTL(intel_crtc->pipe); unsigned i, len = DIP_HEADER_SIZE + frame->len; - u32 flags, val = I915_READ(reg); + u32 val = I915_READ(reg); intel_wait_for_vblank(dev, intel_crtc->pipe); - flags = intel_infoframe_index(frame); - val &= ~(VIDEO_DIP_SELECT_MASK | 0xf); /* clear DIP data offset */ + val |= intel_infoframe_index(frame); - I915_WRITE(reg, VIDEO_DIP_ENABLE | val | flags); + val |= VIDEO_DIP_ENABLE; + + I915_WRITE(reg, val); for (i = 0; i < len; i += 4) { I915_WRITE(TVIDEO_DIP_DATA(intel_crtc->pipe), *data); data++; } - flags |= intel_infoframe_flags(frame); + val |= intel_infoframe_flags(frame); - I915_WRITE(reg, VIDEO_DIP_ENABLE | val | flags); + I915_WRITE(reg, val); } static void vlv_write_infoframe(struct drm_encoder *encoder, @@ -188,24 +190,25 @@ static void vlv_write_infoframe(struct drm_encoder *encoder, struct intel_crtc *intel_crtc = to_intel_crtc(crtc); int reg = VLV_TVIDEO_DIP_CTL(intel_crtc->pipe); unsigned i, len = DIP_HEADER_SIZE + frame->len; - u32 flags, val = I915_READ(reg); + u32 val = I915_READ(reg); intel_wait_for_vblank(dev, intel_crtc->pipe); - flags = intel_infoframe_index(frame); - val &= ~(VIDEO_DIP_SELECT_MASK | 0xf); /* clear DIP data offset */ + val |= intel_infoframe_index(frame); - I915_WRITE(reg, VIDEO_DIP_ENABLE | val | flags); + val |= VIDEO_DIP_ENABLE; + + I915_WRITE(reg, val); for (i = 0; i < len; i += 4) { I915_WRITE(VLV_TVIDEO_DIP_DATA(intel_crtc->pipe), *data); data++; } - flags |= intel_infoframe_flags(frame); + val |= intel_infoframe_flags(frame); - I915_WRITE(reg, VIDEO_DIP_ENABLE | val | flags); + I915_WRITE(reg, val); } static void intel_set_infoframe(struct drm_encoder *encoder, From 1d4f85ac2d5ef1892deba2a3df8a5695645418c8 Mon Sep 17 00:00:00 2001 From: Paulo Zanoni Date: Fri, 4 May 2012 17:18:18 -0300 Subject: [PATCH 03/57] drm/i915: start writing infoframes at address 0 on gen 4 Make sure we're doing the right thing, just like we do on gen5+. Signed-off-by: Paulo Zanoni Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_hdmi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/i915/intel_hdmi.c b/drivers/gpu/drm/i915/intel_hdmi.c index b84d19d0eae..af88313d72d 100644 --- a/drivers/gpu/drm/i915/intel_hdmi.c +++ b/drivers/gpu/drm/i915/intel_hdmi.c @@ -132,7 +132,7 @@ static void i9xx_write_infoframe(struct drm_encoder *encoder, else return; - val &= ~VIDEO_DIP_SELECT_MASK; + val &= ~(VIDEO_DIP_SELECT_MASK | 0xf); /* clear DIP data offset */ val |= intel_infoframe_index(frame); val |= VIDEO_DIP_ENABLE; From 3e6e63952f6f7a42c40751cd88295fd297b80a84 Mon Sep 17 00:00:00 2001 From: Paulo Zanoni Date: Fri, 4 May 2012 17:18:19 -0300 Subject: [PATCH 04/57] drm/i915: mask the video DIP port select Should prevent bugs when changing the port. Signed-off-by: Paulo Zanoni Reviewed-by: Eugeni Dodonov Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_reg.h | 1 + drivers/gpu/drm/i915/intel_hdmi.c | 1 + 2 files changed, 2 insertions(+) diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h index 833052ef7cf..71e7af91f1b 100644 --- a/drivers/gpu/drm/i915/i915_reg.h +++ b/drivers/gpu/drm/i915/i915_reg.h @@ -1700,6 +1700,7 @@ #define VIDEO_DIP_ENABLE (1 << 31) #define VIDEO_DIP_PORT_B (1 << 29) #define VIDEO_DIP_PORT_C (2 << 29) +#define VIDEO_DIP_PORT_MASK (3 << 29) #define VIDEO_DIP_ENABLE_AVI (1 << 21) #define VIDEO_DIP_ENABLE_VENDOR (2 << 21) #define VIDEO_DIP_ENABLE_SPD (8 << 21) diff --git a/drivers/gpu/drm/i915/intel_hdmi.c b/drivers/gpu/drm/i915/intel_hdmi.c index af88313d72d..952eaf777ac 100644 --- a/drivers/gpu/drm/i915/intel_hdmi.c +++ b/drivers/gpu/drm/i915/intel_hdmi.c @@ -125,6 +125,7 @@ static void i9xx_write_infoframe(struct drm_encoder *encoder, /* XXX first guess at handling video port, is this corrent? */ + val &= ~VIDEO_DIP_PORT_MASK; if (intel_hdmi->sdvox_reg == SDVOB) val |= VIDEO_DIP_PORT_B; else if (intel_hdmi->sdvox_reg == SDVOC) From fa193ff7999a7c1cdd1723f1cbc4a108540ca478 Mon Sep 17 00:00:00 2001 From: Paulo Zanoni Date: Fri, 4 May 2012 17:18:20 -0300 Subject: [PATCH 05/57] drm/i915: break intel_infoframe_flags into _enable and _frequency This will allow us to disable an infoframe without changing its frequency. Signed-off-by: Paulo Zanoni Reviewed-by: Eugeni Dodonov Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_hdmi.c | 32 +++++++++++++++++++++++++------ 1 file changed, 26 insertions(+), 6 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_hdmi.c b/drivers/gpu/drm/i915/intel_hdmi.c index 952eaf777ac..661fed48c5b 100644 --- a/drivers/gpu/drm/i915/intel_hdmi.c +++ b/drivers/gpu/drm/i915/intel_hdmi.c @@ -94,16 +94,33 @@ static u32 intel_infoframe_index(struct dip_infoframe *frame) return flags; } -static u32 intel_infoframe_flags(struct dip_infoframe *frame) +static u32 intel_infoframe_enable(struct dip_infoframe *frame) { u32 flags = 0; switch (frame->type) { case DIP_TYPE_AVI: - flags |= VIDEO_DIP_ENABLE_AVI | VIDEO_DIP_FREQ_VSYNC; + flags |= VIDEO_DIP_ENABLE_AVI; break; case DIP_TYPE_SPD: - flags |= VIDEO_DIP_ENABLE_SPD | VIDEO_DIP_FREQ_VSYNC; + flags |= VIDEO_DIP_ENABLE_SPD; + break; + default: + DRM_DEBUG_DRIVER("unknown info frame type %d\n", frame->type); + break; + } + + return flags; +} + +static u32 intel_infoframe_frequency(struct dip_infoframe *frame) +{ + u32 flags = 0; + + switch (frame->type) { + case DIP_TYPE_AVI: + case DIP_TYPE_SPD: + flags |= VIDEO_DIP_FREQ_VSYNC; break; default: DRM_DEBUG_DRIVER("unknown info frame type %d\n", frame->type); @@ -145,7 +162,8 @@ static void i9xx_write_infoframe(struct drm_encoder *encoder, data++; } - val |= intel_infoframe_flags(frame); + val |= intel_infoframe_enable(frame); + val |= intel_infoframe_frequency(frame); I915_WRITE(VIDEO_DIP_CTL, val); } @@ -176,7 +194,8 @@ static void ironlake_write_infoframe(struct drm_encoder *encoder, data++; } - val |= intel_infoframe_flags(frame); + val |= intel_infoframe_enable(frame); + val |= intel_infoframe_frequency(frame); I915_WRITE(reg, val); } @@ -207,7 +226,8 @@ static void vlv_write_infoframe(struct drm_encoder *encoder, data++; } - val |= intel_infoframe_flags(frame); + val |= intel_infoframe_enable(frame); + val |= intel_infoframe_frequency(frame); I915_WRITE(reg, val); } From ecb978515c88183b111b8994acd6b572b1361a72 Mon Sep 17 00:00:00 2001 From: Paulo Zanoni Date: Fri, 4 May 2012 17:18:21 -0300 Subject: [PATCH 06/57] drm/i915: disable the infoframe before changing it That's what the VIDEO_DIP_CTL documentation says we need to do. Except when it's the AVI InfoFrame and we're ironlake_write_infoframe. Signed-off-by: Paulo Zanoni Reviewed-by: Eugeni Dodonov Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_hdmi.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drivers/gpu/drm/i915/intel_hdmi.c b/drivers/gpu/drm/i915/intel_hdmi.c index 661fed48c5b..6c96bb54e96 100644 --- a/drivers/gpu/drm/i915/intel_hdmi.c +++ b/drivers/gpu/drm/i915/intel_hdmi.c @@ -153,6 +153,7 @@ static void i9xx_write_infoframe(struct drm_encoder *encoder, val &= ~(VIDEO_DIP_SELECT_MASK | 0xf); /* clear DIP data offset */ val |= intel_infoframe_index(frame); + val &= ~intel_infoframe_enable(frame); val |= VIDEO_DIP_ENABLE; I915_WRITE(VIDEO_DIP_CTL, val); @@ -185,6 +186,13 @@ static void ironlake_write_infoframe(struct drm_encoder *encoder, val &= ~(VIDEO_DIP_SELECT_MASK | 0xf); /* clear DIP data offset */ val |= intel_infoframe_index(frame); + /* The DIP control register spec says that we need to update the AVI + * infoframe without clearing its enable bit */ + if (frame->type == DIP_TYPE_AVI) + val |= VIDEO_DIP_ENABLE_AVI; + else + val &= ~intel_infoframe_enable(frame); + val |= VIDEO_DIP_ENABLE; I915_WRITE(reg, val); @@ -217,6 +225,7 @@ static void vlv_write_infoframe(struct drm_encoder *encoder, val &= ~(VIDEO_DIP_SELECT_MASK | 0xf); /* clear DIP data offset */ val |= intel_infoframe_index(frame); + val &= ~intel_infoframe_enable(frame); val |= VIDEO_DIP_ENABLE; I915_WRITE(reg, val); From 60c5ea2dd981d929d873823433294b991d3e3cd8 Mon Sep 17 00:00:00 2001 From: Paulo Zanoni Date: Fri, 4 May 2012 17:18:22 -0300 Subject: [PATCH 07/57] drm/i915: mask the video DIP frequency when changing it Better safe than sorry. Currently we never change the frequency and use the same for every infoframe type, so the only way to reproduce a bug would be with the BIOS doing something. Signed-off-by: Paulo Zanoni Reviewed-by: Eugeni Dodonov Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_reg.h | 1 + drivers/gpu/drm/i915/intel_hdmi.c | 3 +++ 2 files changed, 4 insertions(+) diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h index 71e7af91f1b..ce6e64dd00f 100644 --- a/drivers/gpu/drm/i915/i915_reg.h +++ b/drivers/gpu/drm/i915/i915_reg.h @@ -1711,6 +1711,7 @@ #define VIDEO_DIP_FREQ_ONCE (0 << 16) #define VIDEO_DIP_FREQ_VSYNC (1 << 16) #define VIDEO_DIP_FREQ_2VSYNC (2 << 16) +#define VIDEO_DIP_FREQ_MASK (3 << 16) /* Panel power sequencing */ #define PP_STATUS 0x61200 diff --git a/drivers/gpu/drm/i915/intel_hdmi.c b/drivers/gpu/drm/i915/intel_hdmi.c index 6c96bb54e96..4a4ee8b25f2 100644 --- a/drivers/gpu/drm/i915/intel_hdmi.c +++ b/drivers/gpu/drm/i915/intel_hdmi.c @@ -164,6 +164,7 @@ static void i9xx_write_infoframe(struct drm_encoder *encoder, } val |= intel_infoframe_enable(frame); + val &= ~VIDEO_DIP_FREQ_MASK; val |= intel_infoframe_frequency(frame); I915_WRITE(VIDEO_DIP_CTL, val); @@ -203,6 +204,7 @@ static void ironlake_write_infoframe(struct drm_encoder *encoder, } val |= intel_infoframe_enable(frame); + val &= ~VIDEO_DIP_FREQ_MASK; val |= intel_infoframe_frequency(frame); I915_WRITE(reg, val); @@ -236,6 +238,7 @@ static void vlv_write_infoframe(struct drm_encoder *encoder, } val |= intel_infoframe_enable(frame); + val &= ~VIDEO_DIP_FREQ_MASK; val |= intel_infoframe_frequency(frame); I915_WRITE(reg, val); From d47d7cb824ce54af33110a02164e9471a8a9c560 Mon Sep 17 00:00:00 2001 From: Paulo Zanoni Date: Fri, 4 May 2012 17:18:23 -0300 Subject: [PATCH 08/57] drm/i915: simplify intel_encoder_commit Signed-off-by: Paulo Zanoni Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_display.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 83b785f400f..ae3f4454928 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -3125,8 +3125,7 @@ void intel_encoder_commit(struct drm_encoder *encoder) { struct drm_encoder_helper_funcs *encoder_funcs = encoder->helper_private; struct drm_device *dev = encoder->dev; - struct intel_encoder *intel_encoder = to_intel_encoder(encoder); - struct intel_crtc *intel_crtc = to_intel_crtc(intel_encoder->base.crtc); + struct intel_crtc *intel_crtc = to_intel_crtc(encoder->crtc); /* lvds has its own version of commit see intel_lvds_commit */ encoder_funcs->dpms(encoder, DRM_MODE_DPMS_ON); From fdf1250aaae12625ee2abaf2ef22def0a4cdf71b Mon Sep 17 00:00:00 2001 From: Paulo Zanoni Date: Fri, 4 May 2012 17:18:24 -0300 Subject: [PATCH 09/57] drm/i915: split ironlake_write_infoframe into ibx_ and cpt_ The registers are on the PCH, so use the PCH name instead of the CPU name. Also, the way this function is implemented is really only for CPT and PPT. For now, both functions have the same implementations: the next patch will fix ibx_write_infoframe. Signed-off-by: Paulo Zanoni Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_hdmi.c | 52 ++++++++++++++++++++++++++++--- 1 file changed, 48 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_hdmi.c b/drivers/gpu/drm/i915/intel_hdmi.c index 4a4ee8b25f2..e65ebc2d3ad 100644 --- a/drivers/gpu/drm/i915/intel_hdmi.c +++ b/drivers/gpu/drm/i915/intel_hdmi.c @@ -170,8 +170,48 @@ static void i9xx_write_infoframe(struct drm_encoder *encoder, I915_WRITE(VIDEO_DIP_CTL, val); } -static void ironlake_write_infoframe(struct drm_encoder *encoder, - struct dip_infoframe *frame) +static void ibx_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 drm_crtc *crtc = encoder->crtc; + struct intel_crtc *intel_crtc = to_intel_crtc(crtc); + int reg = TVIDEO_DIP_CTL(intel_crtc->pipe); + unsigned i, len = DIP_HEADER_SIZE + frame->len; + u32 val = I915_READ(reg); + + intel_wait_for_vblank(dev, intel_crtc->pipe); + + val &= ~(VIDEO_DIP_SELECT_MASK | 0xf); /* clear DIP data offset */ + val |= intel_infoframe_index(frame); + + /* The DIP control register spec says that we need to update the AVI + * infoframe without clearing its enable bit */ + if (frame->type == DIP_TYPE_AVI) + val |= VIDEO_DIP_ENABLE_AVI; + else + val &= ~intel_infoframe_enable(frame); + + val |= VIDEO_DIP_ENABLE; + + I915_WRITE(reg, val); + + for (i = 0; i < len; i += 4) { + I915_WRITE(TVIDEO_DIP_DATA(intel_crtc->pipe), *data); + data++; + } + + val |= intel_infoframe_enable(frame); + val &= ~VIDEO_DIP_FREQ_MASK; + val |= intel_infoframe_frequency(frame); + + I915_WRITE(reg, val); +} + +static void cpt_write_infoframe(struct drm_encoder *encoder, + struct dip_infoframe *frame) { uint32_t *data = (uint32_t *)frame; struct drm_device *dev = encoder->dev; @@ -627,8 +667,12 @@ void intel_hdmi_init(struct drm_device *dev, int sdvox_reg) intel_hdmi->write_infoframe = vlv_write_infoframe; for_each_pipe(i) I915_WRITE(VLV_TVIDEO_DIP_CTL(i), 0); - } else { - intel_hdmi->write_infoframe = ironlake_write_infoframe; + } else if (HAS_PCH_IBX(dev)) { + intel_hdmi->write_infoframe = ibx_write_infoframe; + for_each_pipe(i) + I915_WRITE(TVIDEO_DIP_CTL(i), 0); + } else { + intel_hdmi->write_infoframe = cpt_write_infoframe; for_each_pipe(i) I915_WRITE(TVIDEO_DIP_CTL(i), 0); } From 4dc20c0d185132077522dd92a72db79274e13f65 Mon Sep 17 00:00:00 2001 From: Paulo Zanoni Date: Fri, 4 May 2012 17:18:25 -0300 Subject: [PATCH 10/57] drm/i915: ibx_write_infoframe can disable AVI IBX does not need the workaround used in cpt_write_infoframe that requires the AVI frame to be enabled while being updated. Signed-off-by: Paulo Zanoni Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_hdmi.c | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_hdmi.c b/drivers/gpu/drm/i915/intel_hdmi.c index e65ebc2d3ad..539073ec56b 100644 --- a/drivers/gpu/drm/i915/intel_hdmi.c +++ b/drivers/gpu/drm/i915/intel_hdmi.c @@ -187,13 +187,7 @@ static void ibx_write_infoframe(struct drm_encoder *encoder, val &= ~(VIDEO_DIP_SELECT_MASK | 0xf); /* clear DIP data offset */ val |= intel_infoframe_index(frame); - /* The DIP control register spec says that we need to update the AVI - * infoframe without clearing its enable bit */ - if (frame->type == DIP_TYPE_AVI) - val |= VIDEO_DIP_ENABLE_AVI; - else - val &= ~intel_infoframe_enable(frame); - + val &= ~intel_infoframe_enable(frame); val |= VIDEO_DIP_ENABLE; I915_WRITE(reg, val); From 4e89ee174bb2da341bf90a84321c7008a3c9210d Mon Sep 17 00:00:00 2001 From: Paulo Zanoni Date: Fri, 4 May 2012 17:18:26 -0300 Subject: [PATCH 11/57] drm/i915: set the DIP port on ibx_write_infoframe Just like Gen 4, IBX has a "Port Select" field on the DIP register, but the ports are different. Signed-off-by: Paulo Zanoni Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_reg.h | 1 + drivers/gpu/drm/i915/intel_hdmi.c | 16 ++++++++++++++++ 2 files changed, 17 insertions(+) diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h index ce6e64dd00f..4a97db25cd3 100644 --- a/drivers/gpu/drm/i915/i915_reg.h +++ b/drivers/gpu/drm/i915/i915_reg.h @@ -1700,6 +1700,7 @@ #define VIDEO_DIP_ENABLE (1 << 31) #define VIDEO_DIP_PORT_B (1 << 29) #define VIDEO_DIP_PORT_C (2 << 29) +#define VIDEO_DIP_PORT_D (3 << 29) #define VIDEO_DIP_PORT_MASK (3 << 29) #define VIDEO_DIP_ENABLE_AVI (1 << 21) #define VIDEO_DIP_ENABLE_VENDOR (2 << 21) diff --git a/drivers/gpu/drm/i915/intel_hdmi.c b/drivers/gpu/drm/i915/intel_hdmi.c index 539073ec56b..9902904de2c 100644 --- a/drivers/gpu/drm/i915/intel_hdmi.c +++ b/drivers/gpu/drm/i915/intel_hdmi.c @@ -178,10 +178,26 @@ static void ibx_write_infoframe(struct drm_encoder *encoder, struct drm_i915_private *dev_priv = dev->dev_private; struct drm_crtc *crtc = encoder->crtc; struct intel_crtc *intel_crtc = to_intel_crtc(crtc); + struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(encoder); int reg = TVIDEO_DIP_CTL(intel_crtc->pipe); unsigned i, len = DIP_HEADER_SIZE + frame->len; u32 val = I915_READ(reg); + val &= ~VIDEO_DIP_PORT_MASK; + switch (intel_hdmi->sdvox_reg) { + case HDMIB: + val |= VIDEO_DIP_PORT_B; + break; + case HDMIC: + val |= VIDEO_DIP_PORT_C; + break; + case HDMID: + val |= VIDEO_DIP_PORT_D; + break; + default: + return; + } + intel_wait_for_vblank(dev, intel_crtc->pipe); val &= ~(VIDEO_DIP_SELECT_MASK | 0xf); /* clear DIP data offset */ From a928d536c09eb071d114aa94bc60d002e7c8f92b Mon Sep 17 00:00:00 2001 From: Paulo Zanoni Date: Fri, 4 May 2012 17:18:15 -0300 Subject: [PATCH 12/57] drm/i915: implement ironlake_wait_for_vblank intel_wait_for_vblank uses PIPESTAT, which does not exist on Ironlake and newer, so now we use PIPEFRAME. Signed-off-by: Paulo Zanoni [danvet: Ditch the check for disable pipe from the new ilk wait for vblank function to keep it consisten with existing behaviour.] Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_display.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index ae3f4454928..b322bde9867 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -784,6 +784,17 @@ intel_find_pll_g4x_dp(const intel_limit_t *limit, struct drm_crtc *crtc, return true; } +static void ironlake_wait_for_vblank(struct drm_device *dev, int pipe) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + u32 frame, frame_reg = PIPEFRAME(pipe); + + frame = I915_READ(frame_reg); + + if (wait_for(I915_READ_NOTRACE(frame_reg) != frame, 50)) + DRM_DEBUG_KMS("vblank wait timed out\n"); +} + /** * intel_wait_for_vblank - wait for vblank on a given pipe * @dev: drm device @@ -797,6 +808,11 @@ void intel_wait_for_vblank(struct drm_device *dev, int pipe) struct drm_i915_private *dev_priv = dev->dev_private; int pipestat_reg = PIPESTAT(pipe); + if (INTEL_INFO(dev)->gen >= 5) { + ironlake_wait_for_vblank(dev, pipe); + return; + } + /* Clear existing vblank status. Note this will clear any other * sticky status fields as well. * From acb87dfb4b847de1de1134e3e767e9a773d6454e Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Thu, 3 May 2012 15:47:57 +0100 Subject: [PATCH 13/57] drm/i915: Limit calling mark-busy only for potential scanouts The principle of intel_mark_busy() is that we want to spot the transition of when the display engine is being used in order to bump powersaving modes and increase display clocks. As such it is only important when the display is changing, i.e. when rendering to the scanout or other sprite/plane, and these are characterised by being pinned. v2: Mark the whole device as busy on execbuffer and pageflips as well and rebase against dinq for the minor bug fix to be immediately applicable. Signed-off-by: Chris Wilson [danvet: fix compile fail.] Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_gem_execbuffer.c | 5 ++++- drivers/gpu/drm/i915/intel_display.c | 4 ++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/i915/i915_gem_execbuffer.c b/drivers/gpu/drm/i915/i915_gem_execbuffer.c index 206b9bbe697..21fc11d8471 100644 --- a/drivers/gpu/drm/i915/i915_gem_execbuffer.c +++ b/drivers/gpu/drm/i915/i915_gem_execbuffer.c @@ -967,11 +967,14 @@ i915_gem_execbuffer_move_to_active(struct list_head *objects, obj->pending_gpu_write = true; list_move_tail(&obj->gpu_write_list, &ring->gpu_write_list); - intel_mark_busy(ring->dev, obj); + if (obj->pin_count) /* check for potential scanout */ + intel_mark_busy(ring->dev, obj); } trace_i915_gem_object_change_domain(obj, old_read, old_write); } + + intel_mark_busy(ring->dev, NULL); } static void diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index b322bde9867..1cbe2680fde 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -5558,6 +5558,9 @@ void intel_mark_busy(struct drm_device *dev, struct drm_i915_gem_object *obj) mod_timer(&dev_priv->idle_timer, jiffies + msecs_to_jiffies(GPU_IDLE_TIMEOUT)); + if (obj == NULL) + return; + list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { if (!crtc->fb) continue; @@ -6007,6 +6010,7 @@ static int intel_crtc_page_flip(struct drm_crtc *crtc, goto cleanup_pending; intel_disable_fbc(dev); + intel_mark_busy(dev, obj); mutex_unlock(&dev->struct_mutex); trace_i915_flip_request(intel_crtc->plane, obj); From 4b24c9331a761d237b8e071941759b80cc580802 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Tue, 8 May 2012 14:41:00 +0200 Subject: [PATCH 14/57] drm/i915: replace intel_infoframe_freq with VIDEO_DIP_FREQ_VSYNC Simplifies things because for all the infoframes we care about, we always send them on each vblank. Also, this gets rid of one of the hw specific functions mislabelled with the intel_ prefix - hsw will completely change how this works! Acked-by: Paulo Zanoni Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_hdmi.c | 25 ++++--------------------- 1 file changed, 4 insertions(+), 21 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_hdmi.c b/drivers/gpu/drm/i915/intel_hdmi.c index 9902904de2c..4c822e19d96 100644 --- a/drivers/gpu/drm/i915/intel_hdmi.c +++ b/drivers/gpu/drm/i915/intel_hdmi.c @@ -113,23 +113,6 @@ static u32 intel_infoframe_enable(struct dip_infoframe *frame) return flags; } -static u32 intel_infoframe_frequency(struct dip_infoframe *frame) -{ - u32 flags = 0; - - switch (frame->type) { - case DIP_TYPE_AVI: - case DIP_TYPE_SPD: - flags |= VIDEO_DIP_FREQ_VSYNC; - 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) { @@ -165,7 +148,7 @@ static void i9xx_write_infoframe(struct drm_encoder *encoder, val |= intel_infoframe_enable(frame); val &= ~VIDEO_DIP_FREQ_MASK; - val |= intel_infoframe_frequency(frame); + val |= VIDEO_DIP_FREQ_VSYNC; I915_WRITE(VIDEO_DIP_CTL, val); } @@ -215,7 +198,7 @@ static void ibx_write_infoframe(struct drm_encoder *encoder, val |= intel_infoframe_enable(frame); val &= ~VIDEO_DIP_FREQ_MASK; - val |= intel_infoframe_frequency(frame); + val |= VIDEO_DIP_FREQ_VSYNC; I915_WRITE(reg, val); } @@ -255,7 +238,7 @@ static void cpt_write_infoframe(struct drm_encoder *encoder, val |= intel_infoframe_enable(frame); val &= ~VIDEO_DIP_FREQ_MASK; - val |= intel_infoframe_frequency(frame); + val |= VIDEO_DIP_FREQ_VSYNC; I915_WRITE(reg, val); } @@ -289,7 +272,7 @@ static void vlv_write_infoframe(struct drm_encoder *encoder, val |= intel_infoframe_enable(frame); val &= ~VIDEO_DIP_FREQ_MASK; - val |= intel_infoframe_frequency(frame); + val |= VIDEO_DIP_FREQ_VSYNC; I915_WRITE(reg, val); } From a3da1df7bd1697ff661f7fd310a893815fa52391 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Tue, 8 May 2012 15:19:06 +0200 Subject: [PATCH 15/57] drm/i915: s/i9xx_/gm45_ for the gm45 write_infoframe function Generally we call stuff with i9xx_ when it's valid for gen3+. But gen3 and early gen4 only support hdmi with sdvo cards, and writing infoframes works completely different there. v2: Use g4x instead of gm45 - it applies to the desktop variant, too. v3: Properly align the paramters of g4x_write_infoframe again, noticed by Paulo Zanoni. Acked-by: Paulo Zanoni Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_hdmi.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_hdmi.c b/drivers/gpu/drm/i915/intel_hdmi.c index 4c822e19d96..4db8d7463f4 100644 --- a/drivers/gpu/drm/i915/intel_hdmi.c +++ b/drivers/gpu/drm/i915/intel_hdmi.c @@ -113,8 +113,8 @@ static u32 intel_infoframe_enable(struct dip_infoframe *frame) return flags; } -static void i9xx_write_infoframe(struct drm_encoder *encoder, - struct dip_infoframe *frame) +static void g4x_write_infoframe(struct drm_encoder *encoder, + struct dip_infoframe *frame) { uint32_t *data = (uint32_t *)frame; struct drm_device *dev = encoder->dev; @@ -654,7 +654,7 @@ 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; + intel_hdmi->write_infoframe = g4x_write_infoframe; I915_WRITE(VIDEO_DIP_CTL, 0); } else if (IS_VALLEYVIEW(dev)) { intel_hdmi->write_infoframe = vlv_write_infoframe; From bc2481f313a05887f0b650555d289dcee5c46d8b Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Tue, 8 May 2012 15:18:32 +0200 Subject: [PATCH 16/57] drm/i915: s/intel_infoframe/gm45_infoframe These two functions are actually hw-specific and only valid for gm45 thru gen7. HSW completely changes how this works, so label them accordingly. v2: s/gm45/g4x/ like for the previous patch. Acked-by: Paulo Zanoni Signed-Off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_hdmi.c | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_hdmi.c b/drivers/gpu/drm/i915/intel_hdmi.c index 4db8d7463f4..e240d99dbf9 100644 --- a/drivers/gpu/drm/i915/intel_hdmi.c +++ b/drivers/gpu/drm/i915/intel_hdmi.c @@ -75,7 +75,7 @@ void intel_dip_infoframe_csum(struct dip_infoframe *frame) frame->checksum = 0x100 - sum; } -static u32 intel_infoframe_index(struct dip_infoframe *frame) +static u32 g4x_infoframe_index(struct dip_infoframe *frame) { u32 flags = 0; @@ -94,7 +94,7 @@ static u32 intel_infoframe_index(struct dip_infoframe *frame) return flags; } -static u32 intel_infoframe_enable(struct dip_infoframe *frame) +static u32 g4x_infoframe_enable(struct dip_infoframe *frame) { u32 flags = 0; @@ -134,9 +134,9 @@ static void g4x_write_infoframe(struct drm_encoder *encoder, return; val &= ~(VIDEO_DIP_SELECT_MASK | 0xf); /* clear DIP data offset */ - val |= intel_infoframe_index(frame); + val |= g4x_infoframe_index(frame); - val &= ~intel_infoframe_enable(frame); + val &= ~g4x_infoframe_enable(frame); val |= VIDEO_DIP_ENABLE; I915_WRITE(VIDEO_DIP_CTL, val); @@ -146,7 +146,7 @@ static void g4x_write_infoframe(struct drm_encoder *encoder, data++; } - val |= intel_infoframe_enable(frame); + val |= g4x_infoframe_enable(frame); val &= ~VIDEO_DIP_FREQ_MASK; val |= VIDEO_DIP_FREQ_VSYNC; @@ -184,9 +184,9 @@ static void ibx_write_infoframe(struct drm_encoder *encoder, intel_wait_for_vblank(dev, intel_crtc->pipe); val &= ~(VIDEO_DIP_SELECT_MASK | 0xf); /* clear DIP data offset */ - val |= intel_infoframe_index(frame); + val |= g4x_infoframe_index(frame); - val &= ~intel_infoframe_enable(frame); + val &= ~g4x_infoframe_enable(frame); val |= VIDEO_DIP_ENABLE; I915_WRITE(reg, val); @@ -196,7 +196,7 @@ static void ibx_write_infoframe(struct drm_encoder *encoder, data++; } - val |= intel_infoframe_enable(frame); + val |= g4x_infoframe_enable(frame); val &= ~VIDEO_DIP_FREQ_MASK; val |= VIDEO_DIP_FREQ_VSYNC; @@ -218,14 +218,14 @@ static void cpt_write_infoframe(struct drm_encoder *encoder, intel_wait_for_vblank(dev, intel_crtc->pipe); val &= ~(VIDEO_DIP_SELECT_MASK | 0xf); /* clear DIP data offset */ - val |= intel_infoframe_index(frame); + val |= g4x_infoframe_index(frame); /* The DIP control register spec says that we need to update the AVI * infoframe without clearing its enable bit */ if (frame->type == DIP_TYPE_AVI) val |= VIDEO_DIP_ENABLE_AVI; else - val &= ~intel_infoframe_enable(frame); + val &= ~g4x_infoframe_enable(frame); val |= VIDEO_DIP_ENABLE; @@ -236,7 +236,7 @@ static void cpt_write_infoframe(struct drm_encoder *encoder, data++; } - val |= intel_infoframe_enable(frame); + val |= g4x_infoframe_enable(frame); val &= ~VIDEO_DIP_FREQ_MASK; val |= VIDEO_DIP_FREQ_VSYNC; @@ -258,9 +258,9 @@ static void vlv_write_infoframe(struct drm_encoder *encoder, intel_wait_for_vblank(dev, intel_crtc->pipe); val &= ~(VIDEO_DIP_SELECT_MASK | 0xf); /* clear DIP data offset */ - val |= intel_infoframe_index(frame); + val |= g4x_infoframe_index(frame); - val &= ~intel_infoframe_enable(frame); + val &= ~g4x_infoframe_enable(frame); val |= VIDEO_DIP_ENABLE; I915_WRITE(reg, val); @@ -270,7 +270,7 @@ static void vlv_write_infoframe(struct drm_encoder *encoder, data++; } - val |= intel_infoframe_enable(frame); + val |= g4x_infoframe_enable(frame); val &= ~VIDEO_DIP_FREQ_MASK; val |= VIDEO_DIP_FREQ_VSYNC; From 38e490fea7d2885d79fcd1ca37edb64e489d470d Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Tue, 8 May 2012 13:19:12 +0200 Subject: [PATCH 17/57] MAINTAINERS: switch drm/i915 to Daniel Vetter ... given that I essentially run the show already, let's make this official. Acked-by: Dave Airlie Signed-Off-by: Daniel Vetter --- MAINTAINERS | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/MAINTAINERS b/MAINTAINERS index 707163365a9..90e7e226a90 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2373,10 +2373,10 @@ F: drivers/gpu/drm/ F: include/drm/ INTEL DRM DRIVERS (excluding Poulsbo, Moorestown and derivative chipsets) -M: Keith Packard +M: Daniel Vetter L: intel-gfx@lists.freedesktop.org (subscribers-only) L: dri-devel@lists.freedesktop.org -T: git git://git.kernel.org/pub/scm/linux/kernel/git/keithp/linux.git +T: git git://people.freedesktop.org/~danvet/drm-intel S: Supported F: drivers/gpu/drm/i915 F: include/drm/i915* From 59de3295adaf9503e91e7e6d323cf9becef600de Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Mon, 2 Apr 2012 20:48:43 +0200 Subject: [PATCH 18/57] drm/i915: enable semaphores on gen6 if dmar is not active Inspired by the recent ppgtt regression report, where switching of dmar only for the gpu seems to fix things completely, I've looked again at the semaphores+vt-d situation. Contrary to my earlier testing a few months back my system is now stable with dmar disabled for the igd, and not only when disabling dmar completely. So I'm rather hopeful that all our recent fixes for snb have changed things for code and it's time to try enabling semaphores again. We've also had issues with enabling semaphores which are not vt-d related, but I guess these are all fixed by the autoreport-disabling and lazy request fix. And there's only one way to find out whether there are still other issues ... When I've tried to apply this patch I've noticed that semaphores on gen6 have already silently been enabled in commit 2911a35b2e4eb87ec48d03aeb11f019e51ae3c0d Author: Ben Widawsky Date: Thu Apr 5 14:47:36 2012 -0700 drm/i915: use semaphores for the display plane Fix this up by only checking whether dmar is enabled on the gfx (not on the entire system). Reviewed-by: Ben Widawsky Cc: Chris Wilson Signed-Off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_drv.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c index 77b7a50e201..1ccfc23d1ab 100644 --- a/drivers/gpu/drm/i915/i915_drv.c +++ b/drivers/gpu/drm/i915/i915_drv.c @@ -407,9 +407,11 @@ bool i915_semaphore_is_enabled(struct drm_device *dev) if (i915_semaphores >= 0) return i915_semaphores; +#ifdef CONFIG_INTEL_IOMMU /* Enable semaphores on SNB when IO remapping is off */ - if (INTEL_INFO(dev)->gen == 6) - return !intel_iommu_enabled; + if (INTEL_INFO(dev)->gen == 6 && intel_iommu_gfx_mapped) + return false; +#endif return 1; } From 9adab8b5a7fde248504f484e197589f3e3c922e2 Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Wed, 9 May 2012 21:45:43 +0100 Subject: [PATCH 19/57] drm/i915: Avoid a double-read of PCH_IIR during interrupt handling Currently the code re-reads PCH_IIR during the hotplug interrupt processing. Not only is this a wasted read, but introduces a potential for handling a spurious interrupt as we then may not clear all the interrupts processed (since the re-read IIR may contains more interrupts asserted than we clear using the result of the original read). Signed-off-by: Chris Wilson Cc: Jesse Barnes Cc: stable@kernel.org Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_irq.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index b4999b5288e..14943ba4d73 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -533,14 +533,11 @@ out: return ret; } -static void pch_irq_handler(struct drm_device *dev) +static void pch_irq_handler(struct drm_device *dev, u32 pch_iir) { drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; - u32 pch_iir; int pipe; - pch_iir = I915_READ(SDEIIR); - if (pch_iir & SDE_AUDIO_POWER_MASK) DRM_DEBUG_DRIVER("PCH audio power change on port %d\n", (pch_iir & SDE_AUDIO_POWER_MASK) >> @@ -633,7 +630,7 @@ static irqreturn_t ivybridge_irq_handler(DRM_IRQ_ARGS) if (de_iir & DE_PCH_EVENT_IVB) { if (pch_iir & SDE_HOTPLUG_MASK_CPT) queue_work(dev_priv->wq, &dev_priv->hotplug_work); - pch_irq_handler(dev); + pch_irq_handler(dev, pch_iir); } if (pm_iir & GEN6_PM_DEFERRED_EVENTS) @@ -721,7 +718,7 @@ static irqreturn_t ironlake_irq_handler(DRM_IRQ_ARGS) if (de_iir & DE_PCH_EVENT) { if (pch_iir & hotplug_mask) queue_work(dev_priv->wq, &dev_priv->hotplug_work); - pch_irq_handler(dev); + pch_irq_handler(dev, pch_iir); } if (de_iir & DE_PCU_EVENT) { From 0e43406bcc1868a316eea6012a0a09d992c53521 Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Wed, 9 May 2012 21:45:44 +0100 Subject: [PATCH 20/57] drm/i915: Simplify interrupt processing for IvyBridge We can take advantage that the PCH_IIR is a subordinate register to reduce one of the required IIR reads, and that we only need to clear interrupts handled to reduce the writes. And by simply tidying the code we can reduce the line count and hopefully make it more readable. v2: Split out the bugfix from the refactoring. Signed-off-by: Chris Wilson Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_irq.c | 99 +++++++++++++++------------------ 1 file changed, 44 insertions(+), 55 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index 14943ba4d73..324431e42fd 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -577,72 +577,61 @@ static irqreturn_t ivybridge_irq_handler(DRM_IRQ_ARGS) { struct drm_device *dev = (struct drm_device *) arg; drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; - int ret = IRQ_NONE; - u32 de_iir, gt_iir, de_ier, pch_iir, pm_iir; + u32 de_iir, gt_iir, de_ier, pm_iir; + irqreturn_t ret = IRQ_NONE; + int i; atomic_inc(&dev_priv->irq_received); /* disable master interrupt before clearing iir */ de_ier = I915_READ(DEIER); I915_WRITE(DEIER, de_ier & ~DE_MASTER_IRQ_CONTROL); - POSTING_READ(DEIER); + + gt_iir = I915_READ(GTIIR); + if (gt_iir) { + snb_gt_irq_handler(dev, dev_priv, gt_iir); + I915_WRITE(GTIIR, gt_iir); + ret = IRQ_HANDLED; + } de_iir = I915_READ(DEIIR); - gt_iir = I915_READ(GTIIR); - pch_iir = I915_READ(SDEIIR); + if (de_iir) { + if (de_iir & DE_GSE_IVB) + intel_opregion_gse_intr(dev); + + for (i = 0; i < 3; i++) { + if (de_iir & (DE_PLANEA_FLIP_DONE_IVB << (5 * i))) { + intel_prepare_page_flip(dev, i); + intel_finish_page_flip_plane(dev, i); + } + if (de_iir & (DE_PIPEA_VBLANK_IVB << (5 * i))) + drm_handle_vblank(dev, i); + } + + /* check event from PCH */ + if (de_iir & DE_PCH_EVENT_IVB) { + u32 pch_iir = I915_READ(SDEIIR); + + if (pch_iir & SDE_HOTPLUG_MASK_CPT) + queue_work(dev_priv->wq, &dev_priv->hotplug_work); + pch_irq_handler(dev, pch_iir); + + /* clear PCH hotplug event before clear CPU irq */ + I915_WRITE(SDEIIR, pch_iir); + } + + I915_WRITE(DEIIR, de_iir); + ret = IRQ_HANDLED; + } + pm_iir = I915_READ(GEN6_PMIIR); - - if (de_iir == 0 && gt_iir == 0 && pch_iir == 0 && pm_iir == 0) - goto done; - - ret = IRQ_HANDLED; - - snb_gt_irq_handler(dev, dev_priv, gt_iir); - - if (de_iir & DE_GSE_IVB) - intel_opregion_gse_intr(dev); - - if (de_iir & DE_PLANEA_FLIP_DONE_IVB) { - intel_prepare_page_flip(dev, 0); - intel_finish_page_flip_plane(dev, 0); + if (pm_iir) { + if (pm_iir & GEN6_PM_DEFERRED_EVENTS) + gen6_queue_rps_work(dev_priv, pm_iir); + I915_WRITE(GEN6_PMIIR, pm_iir); + ret = IRQ_HANDLED; } - if (de_iir & DE_PLANEB_FLIP_DONE_IVB) { - intel_prepare_page_flip(dev, 1); - intel_finish_page_flip_plane(dev, 1); - } - - if (de_iir & DE_PLANEC_FLIP_DONE_IVB) { - intel_prepare_page_flip(dev, 2); - intel_finish_page_flip_plane(dev, 2); - } - - if (de_iir & DE_PIPEA_VBLANK_IVB) - drm_handle_vblank(dev, 0); - - if (de_iir & DE_PIPEB_VBLANK_IVB) - drm_handle_vblank(dev, 1); - - if (de_iir & DE_PIPEC_VBLANK_IVB) - drm_handle_vblank(dev, 2); - - /* check event from PCH */ - if (de_iir & DE_PCH_EVENT_IVB) { - if (pch_iir & SDE_HOTPLUG_MASK_CPT) - queue_work(dev_priv->wq, &dev_priv->hotplug_work); - pch_irq_handler(dev, pch_iir); - } - - if (pm_iir & GEN6_PM_DEFERRED_EVENTS) - gen6_queue_rps_work(dev_priv, pm_iir); - - /* should clear PCH hotplug event before clear CPU irq */ - I915_WRITE(SDEIIR, pch_iir); - I915_WRITE(GTIIR, gt_iir); - I915_WRITE(DEIIR, de_iir); - I915_WRITE(GEN6_PMIIR, pm_iir); - -done: I915_WRITE(DEIER, de_ier); POSTING_READ(DEIER); From 1833b134454d5300d8a9d07b78876a20395f01a9 Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Wed, 9 May 2012 11:56:28 +0100 Subject: [PATCH 21/57] drm/i915: gen6_enable_rps() wants to be called after ring initialisation Currently we call gen6_enable_rps() (which writes into the per-ring register mmio space) from intel_modeset_init_hw() which is called before we initialise the rings. If we defer intel_modeset_init_hw() until afterwards (in the intel_modeset_gem_init() phase) all is well. v2: Rectify ordering of gem vs display HW init upon resume. (Daniel) v3: Fix up locking. (Paulo) Signed-off-by: Chris Wilson [danvet: Smash Paulo's locking fix onto Chris' patch.] Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_drv.c | 10 ++++------ drivers/gpu/drm/i915/i915_suspend.c | 6 ------ drivers/gpu/drm/i915/intel_display.c | 6 ++---- 3 files changed, 6 insertions(+), 16 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c index 1ccfc23d1ab..88d32908892 100644 --- a/drivers/gpu/drm/i915/i915_drv.c +++ b/drivers/gpu/drm/i915/i915_drv.c @@ -624,15 +624,16 @@ static int i915_drm_thaw(struct drm_device *dev) /* KMS EnterVT equivalent */ if (drm_core_check_feature(dev, DRIVER_MODESET)) { + if (HAS_PCH_SPLIT(dev)) + ironlake_init_pch_refclk(dev); + mutex_lock(&dev->struct_mutex); dev_priv->mm.suspended = 0; error = i915_gem_init_hw(dev); mutex_unlock(&dev->struct_mutex); - if (HAS_PCH_SPLIT(dev)) - ironlake_init_pch_refclk(dev); - + intel_modeset_init_hw(dev); drm_mode_config_reset(dev); drm_irq_install(dev); @@ -640,9 +641,6 @@ static int i915_drm_thaw(struct drm_device *dev) mutex_lock(&dev->mode_config.mutex); drm_helper_resume_force_mode(dev); mutex_unlock(&dev->mode_config.mutex); - - if (IS_IRONLAKE_M(dev)) - ironlake_enable_rc6(dev); } intel_opregion_init(dev); diff --git a/drivers/gpu/drm/i915/i915_suspend.c b/drivers/gpu/drm/i915/i915_suspend.c index 73a5c3c12fe..0ede02a99d9 100644 --- a/drivers/gpu/drm/i915/i915_suspend.c +++ b/drivers/gpu/drm/i915/i915_suspend.c @@ -876,12 +876,6 @@ int i915_restore_state(struct drm_device *dev) I915_WRITE(IER, dev_priv->saveIER); I915_WRITE(IMR, dev_priv->saveIMR); } - mutex_unlock(&dev->struct_mutex); - - if (drm_core_check_feature(dev, DRIVER_MODESET)) - intel_modeset_init_hw(dev); - - mutex_lock(&dev->struct_mutex); /* Cache mode state */ I915_WRITE(CACHE_MODE_0, dev_priv->saveCACHE_MODE_0 | 0xffff0000); diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 1cbe2680fde..42b9e20782b 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -6658,6 +6658,7 @@ void intel_modeset_init_hw(struct drm_device *dev) if (IS_IRONLAKE_M(dev)) { ironlake_enable_drps(dev); + ironlake_enable_rc6(dev); intel_init_emon(dev); } @@ -6719,8 +6720,6 @@ void intel_modeset_init(struct drm_device *dev) i915_disable_vga(dev); intel_setup_outputs(dev); - intel_modeset_init_hw(dev); - INIT_WORK(&dev_priv->idle_work, intel_idle_update); setup_timer(&dev_priv->idle_timer, intel_gpu_idle_timer, (unsigned long)dev); @@ -6728,8 +6727,7 @@ void intel_modeset_init(struct drm_device *dev) void intel_modeset_gem_init(struct drm_device *dev) { - if (IS_IRONLAKE_M(dev)) - ironlake_enable_rc6(dev); + intel_modeset_init_hw(dev); intel_setup_overlay(dev); } From 8c5f5f7c42e009bbb6184563d3cb3861bb73b98c Mon Sep 17 00:00:00 2001 From: Eugeni Dodonov Date: Thu, 10 May 2012 10:18:02 -0300 Subject: [PATCH 22/57] drm/i915: add new Haswell DIP controls registers Haswell has different DIP control registers and offsets which we need to use for infoframes, which this patch adds. Note that this does not adds full DIP frames support, but only the basic functionality necessary for HDMI to work in early enablement. v2: replace infoframe handling with a debug message, proper support will be added via a patch from Paulo Zanoni later. Signed-off-by: Eugeni Dodonov Reviewed-by: Paulo Zanoni Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_reg.h | 36 +++++++++++++++++++++++++++++++ drivers/gpu/drm/i915/intel_hdmi.c | 19 ++++++++++++++++ 2 files changed, 55 insertions(+) diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h index 4a97db25cd3..fb76b19e32b 100644 --- a/drivers/gpu/drm/i915/i915_reg.h +++ b/drivers/gpu/drm/i915/i915_reg.h @@ -3524,6 +3524,42 @@ #define VLV_TVIDEO_DIP_GCP(pipe) \ _PIPE(pipe, VLV_VIDEO_DIP_GDCP_PAYLOAD_A, VLV_VIDEO_DIP_GDCP_PAYLOAD_B) +/* Haswell DIP controls */ +#define HSW_VIDEO_DIP_CTL_A 0x60200 +#define HSW_VIDEO_DIP_AVI_DATA_A 0x60220 +#define HSW_VIDEO_DIP_VS_DATA_A 0x60260 +#define HSW_VIDEO_DIP_SPD_DATA_A 0x602A0 +#define HSW_VIDEO_DIP_GMP_DATA_A 0x602E0 +#define HSW_VIDEO_DIP_VSC_DATA_A 0x60320 +#define HSW_VIDEO_DIP_AVI_ECC_A 0x60240 +#define HSW_VIDEO_DIP_VS_ECC_A 0x60280 +#define HSW_VIDEO_DIP_SPD_ECC_A 0x602C0 +#define HSW_VIDEO_DIP_GMP_ECC_A 0x60300 +#define HSW_VIDEO_DIP_VSC_ECC_A 0x60344 +#define HSW_VIDEO_DIP_GCP_A 0x60210 + +#define HSW_VIDEO_DIP_CTL_B 0x61200 +#define HSW_VIDEO_DIP_AVI_DATA_B 0x61220 +#define HSW_VIDEO_DIP_VS_DATA_B 0x61260 +#define HSW_VIDEO_DIP_SPD_DATA_B 0x612A0 +#define HSW_VIDEO_DIP_GMP_DATA_B 0x612E0 +#define HSW_VIDEO_DIP_VSC_DATA_B 0x61320 +#define HSW_VIDEO_DIP_BVI_ECC_B 0x61240 +#define HSW_VIDEO_DIP_VS_ECC_B 0x61280 +#define HSW_VIDEO_DIP_SPD_ECC_B 0x612C0 +#define HSW_VIDEO_DIP_GMP_ECC_B 0x61300 +#define HSW_VIDEO_DIP_VSC_ECC_B 0x61344 +#define HSW_VIDEO_DIP_GCP_B 0x61210 + +#define HSW_TVIDEO_DIP_CTL(pipe) \ + _PIPE(pipe, HSW_VIDEO_DIP_CTL_A, HSW_VIDEO_DIP_CTL_B) +#define HSW_TVIDEO_DIP_AVI_DATA(pipe) \ + _PIPE(pipe, HSW_VIDEO_DIP_AVI_DATA_A, HSW_VIDEO_DIP_AVI_DATA_B) +#define HSW_TVIDEO_DIP_SPD_DATA(pipe) \ + _PIPE(pipe, HSW_VIDEO_DIP_SPD_DATA_A, HSW_VIDEO_DIP_SPD_DATA_B) +#define HSW_TVIDEO_DIP_GCP(pipe) \ + _PIPE(pipe, HSW_VIDEO_DIP_GCP_A, HSW_VIDEO_DIP_GCP_B) + #define _TRANS_HTOTAL_B 0xe1000 #define _TRANS_HBLANK_B 0xe1004 #define _TRANS_HSYNC_B 0xe1008 diff --git a/drivers/gpu/drm/i915/intel_hdmi.c b/drivers/gpu/drm/i915/intel_hdmi.c index e240d99dbf9..4a9d553719f 100644 --- a/drivers/gpu/drm/i915/intel_hdmi.c +++ b/drivers/gpu/drm/i915/intel_hdmi.c @@ -277,6 +277,18 @@ static void vlv_write_infoframe(struct drm_encoder *encoder, I915_WRITE(reg, val); } +static void hsw_write_infoframe(struct drm_encoder *encoder, + struct dip_infoframe *frame) +{ + /* Not implemented yet, so avoid doing anything at all. + * This is the placeholder for Paulo Zanoni's infoframe writing patch + */ + DRM_DEBUG_DRIVER("Attempting to write infoframe on Haswell, this is not implemented yet.\n"); + + return; + +} + static void intel_set_infoframe(struct drm_encoder *encoder, struct dip_infoframe *frame) { @@ -660,6 +672,13 @@ void intel_hdmi_init(struct drm_device *dev, int sdvox_reg) intel_hdmi->write_infoframe = vlv_write_infoframe; for_each_pipe(i) I915_WRITE(VLV_TVIDEO_DIP_CTL(i), 0); + } else if (IS_HASWELL(dev)) { + /* FIXME: Haswell has a new set of DIP frame registers, but we are + * just doing the minimal required for HDMI to work at this stage. + */ + intel_hdmi->write_infoframe = hsw_write_infoframe; + for_each_pipe(i) + I915_WRITE(HSW_TVIDEO_DIP_CTL(i), 0); } else if (HAS_PCH_IBX(dev)) { intel_hdmi->write_infoframe = ibx_write_infoframe; for_each_pipe(i) From 7d4e146f7509b872fbcfbbbb39476eb9afaf2ba2 Mon Sep 17 00:00:00 2001 From: Eugeni Dodonov Date: Wed, 9 May 2012 15:37:09 -0300 Subject: [PATCH 23/57] drm/i915: reuse Ivy Bridge interrupts code for Haswell Haswell interrupts are mostly similar with Ivy Bridge, so we share same routines with it. This patch also simplifies the vblank counter handling for all the Gen5+ architectures. Signed-off-by: Eugeni Dodonov Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_irq.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index 324431e42fd..2a062d778ff 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -2583,8 +2583,7 @@ void intel_irq_init(struct drm_device *dev) dev->driver->get_vblank_counter = i915_get_vblank_counter; dev->max_vblank_count = 0xffffff; /* only 24 bits of frame count */ - if (IS_G4X(dev) || IS_GEN5(dev) || IS_GEN6(dev) || IS_IVYBRIDGE(dev) || - IS_VALLEYVIEW(dev)) { + if (IS_G4X(dev) || INTEL_INFO(dev)->gen >= 5) { dev->max_vblank_count = 0xffffffff; /* full 32 bit counter */ dev->driver->get_vblank_counter = gm45_get_vblank_counter; } @@ -2610,6 +2609,14 @@ void intel_irq_init(struct drm_device *dev) dev->driver->irq_uninstall = ironlake_irq_uninstall; dev->driver->enable_vblank = ivybridge_enable_vblank; dev->driver->disable_vblank = ivybridge_disable_vblank; + } else if (IS_HASWELL(dev)) { + /* Share interrupts handling with IVB */ + dev->driver->irq_handler = ivybridge_irq_handler; + dev->driver->irq_preinstall = ironlake_irq_preinstall; + dev->driver->irq_postinstall = ivybridge_irq_postinstall; + dev->driver->irq_uninstall = ironlake_irq_uninstall; + dev->driver->enable_vblank = ivybridge_enable_vblank; + dev->driver->disable_vblank = ivybridge_disable_vblank; } else if (HAS_PCH_SPLIT(dev)) { dev->driver->irq_handler = ironlake_irq_handler; dev->driver->irq_preinstall = ironlake_irq_preinstall; From a416edefbbaf942b588595ad6fa63ac700f5d73c Mon Sep 17 00:00:00 2001 From: Eugeni Dodonov Date: Wed, 9 May 2012 15:37:10 -0300 Subject: [PATCH 24/57] drm/i915: add support for SBI ops With Lynx Point, we need to use SBI to communicate with the display clock control. This commit adds helper functions to access the registers via SBI. v2: de-inline the function and address changes in bits names v3: protect operations with dpio_lock, increase timeout to 100 for paranoia sake. v4: decrease paranoia a bit, as noticed by Chris Wilson Reviewed-by: Rodrigo Vivi Reviewed-by: Jesse Barnes Signed-off-by: Eugeni Dodonov Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_display.c | 63 ++++++++++++++++++++++++++++ 1 file changed, 63 insertions(+) diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 42b9e20782b..45b93a09619 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -1299,6 +1299,69 @@ static void intel_disable_pll(struct drm_i915_private *dev_priv, enum pipe pipe) POSTING_READ(reg); } +/* SBI access */ +static void +intel_sbi_write(struct drm_i915_private *dev_priv, u16 reg, u32 value) +{ + unsigned long flags; + + spin_lock_irqsave(&dev_priv->dpio_lock, flags); + if (wait_for((I915_READ(SBI_CTL_STAT) & SBI_READY) == 0, + 100)) { + DRM_ERROR("timeout waiting for SBI to become ready\n"); + goto out_unlock; + } + + I915_WRITE(SBI_ADDR, + (reg << 16)); + I915_WRITE(SBI_DATA, + value); + I915_WRITE(SBI_CTL_STAT, + SBI_BUSY | + SBI_CTL_OP_CRWR); + + if (wait_for((I915_READ(SBI_CTL_STAT) & (SBI_READY | SBI_RESPONSE_SUCCESS)) == 0, + 100)) { + DRM_ERROR("timeout waiting for SBI to complete write transaction\n"); + goto out_unlock; + } + +out_unlock: + spin_unlock_irqrestore(&dev_priv->dpio_lock, flags); +} + +static u32 +intel_sbi_read(struct drm_i915_private *dev_priv, u16 reg) +{ + unsigned long flags; + u32 value; + + spin_lock_irqsave(&dev_priv->dpio_lock, flags); + if (wait_for((I915_READ(SBI_CTL_STAT) & SBI_READY) == 0, + 100)) { + DRM_ERROR("timeout waiting for SBI to become ready\n"); + goto out_unlock; + } + + I915_WRITE(SBI_ADDR, + (reg << 16)); + I915_WRITE(SBI_CTL_STAT, + SBI_BUSY | + SBI_CTL_OP_CRRD); + + if (wait_for((I915_READ(SBI_CTL_STAT) & (SBI_READY | SBI_RESPONSE_SUCCESS)) == 0, + 100)) { + DRM_ERROR("timeout waiting for SBI to complete read transaction\n"); + goto out_unlock; + } + + value = I915_READ(SBI_DATA); + +out_unlock: + spin_unlock_irqrestore(&dev_priv->dpio_lock, flags); + return value; +} + /** * intel_enable_pch_pll - enable PCH PLL * @dev_priv: i915 private structure From 461bc9b587389586edf9315b337fb136f33594ad Mon Sep 17 00:00:00 2001 From: Eugeni Dodonov Date: Wed, 9 May 2012 15:37:11 -0300 Subject: [PATCH 25/57] drm/i915: calculate watermarks for devices that have 3 pipes This adds proper support for calculating those watermarks, checking for number of available pipes instead of specific GPU variants when deciding if watermarks for 3rd pipe are necessary. Reviewed-by: Jesse Barnes Signed-off-by: Eugeni Dodonov Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_pm.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c index 43892341079..21587f89f44 100644 --- a/drivers/gpu/drm/i915/intel_pm.c +++ b/drivers/gpu/drm/i915/intel_pm.c @@ -1803,8 +1803,7 @@ static void sandybridge_update_wm(struct drm_device *dev) enabled |= 2; } - /* IVB has 3 pipes */ - if (IS_IVYBRIDGE(dev) && + if ((dev_priv->num_pipe == 3) && g4x_compute_wm0(dev, 2, &sandybridge_display_wm_info, latency, &sandybridge_cursor_wm_info, latency, From 5826eca5ac58469a704984bceaebfda5bcb383db Mon Sep 17 00:00:00 2001 From: Eugeni Dodonov Date: Wed, 9 May 2012 15:37:12 -0300 Subject: [PATCH 26/57] drm/i915: properly check for pipe count As suggested by Chris Wilson and Daniel Vetter, this chunk of code can be simplified with a more simple check. Also, as noticed by Jesse Barnes, it is worth mentioning that plane is an enum and num_pipe is an int, so we could be more paranoid here about those validation checks eventually. CC: Daniel Vetter CC: Chris Wilson Reviewed-by: Jesse Barnes Signed-off-by: Eugeni Dodonov Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_display.c | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 45b93a09619..6b93d4a697e 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -1980,16 +1980,10 @@ intel_pipe_set_base(struct drm_crtc *crtc, int x, int y, return 0; } - switch (intel_crtc->plane) { - case 0: - case 1: - break; - case 2: - if (IS_IVYBRIDGE(dev)) - break; - /* fall through otherwise */ - default: - DRM_ERROR("no plane for crtc\n"); + if(intel_crtc->plane > dev_priv->num_pipe) { + DRM_ERROR("no plane for crtc: plane %d, num_pipes %d\n", + intel_crtc->plane, + dev_priv->num_pipe); return -EINVAL; } From 6e4c1677fef47a5f538e8d031998a68aaf87275a Mon Sep 17 00:00:00 2001 From: Eugeni Dodonov Date: Wed, 9 May 2012 15:37:13 -0300 Subject: [PATCH 27/57] drm/i915: show unknown sdvox registers on hdmi init This will throw a BUG() message when an unknown sdvox register is given to intel_hdmi_init. When this happens, things could going to be pretty much broken afterwards, so we better detect this as soon as possible. Signed-off-by: Eugeni Dodonov Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_hdmi.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/gpu/drm/i915/intel_hdmi.c b/drivers/gpu/drm/i915/intel_hdmi.c index 4a9d553719f..8da8dfe1b4f 100644 --- a/drivers/gpu/drm/i915/intel_hdmi.c +++ b/drivers/gpu/drm/i915/intel_hdmi.c @@ -661,6 +661,10 @@ void intel_hdmi_init(struct drm_device *dev, int sdvox_reg) intel_encoder->clone_mask = (1 << INTEL_HDMIF_CLONE_BIT); intel_hdmi->ddc_bus = GMBUS_PORT_DPD; dev_priv->hotplug_supported_mask |= HDMID_HOTPLUG_INT_STATUS; + } else { + /* If we got an unknown sdvox_reg, things are pretty much broken + * in a way that we should let the kernel know about it */ + BUG(); } intel_hdmi->sdvox_reg = sdvox_reg; From f57e1e3a67503927824839996a6efce269281afa Mon Sep 17 00:00:00 2001 From: Eugeni Dodonov Date: Wed, 9 May 2012 15:37:14 -0300 Subject: [PATCH 28/57] drm/i915: do not use fdi_normal_train on Haswell This should be already configured when FDI auto-negotiation is done. Reviewed-by: Rodrigo Vivi Reviewed-by: Jesse Barnes Signed-off-by: Eugeni Dodonov Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_display.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 6b93d4a697e..cc910e69f70 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -2679,7 +2679,8 @@ static void ironlake_pch_enable(struct drm_crtc *crtc) I915_WRITE(TRANS_VSYNC(pipe), I915_READ(VSYNC(pipe))); I915_WRITE(TRANS_VSYNCSHIFT(pipe), I915_READ(VSYNCSHIFT(pipe))); - intel_fdi_normal_train(crtc); + if (!IS_HASWELL(dev)) + intel_fdi_normal_train(crtc); /* For PCH DP, enable TRANS_DP_CTL */ if (HAS_PCH_CPT(dev) && From 6ee8bab09d515503f9f1d01690840c2bd5f66267 Mon Sep 17 00:00:00 2001 From: Eugeni Dodonov Date: Wed, 9 May 2012 20:30:31 -0300 Subject: [PATCH 29/57] drm/i915: detect PCH encoders on Haswell On Haswell, the recommended PCH-connected output is the one driven by DDI E in FDI mode, used for VGA connection. All the others are handled by the CPU. Note that this does not accounts for Haswell/PPT combination yet, so if we encounter such combination an error message is thrown to indicate that things could go wrong. v2: improve non-LPT detection warning per Daniel Vetter's suggestion. Signed-off-by: Eugeni Dodonov Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_display.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index cc910e69f70..bb049a17863 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -2610,6 +2610,22 @@ static bool intel_crtc_driving_pch(struct drm_crtc *crtc) if (encoder->base.crtc != crtc) continue; + /* On Haswell, LPT PCH handles the VGA connection via FDI, and Haswell + * CPU handles all others */ + if (IS_HASWELL(dev)) { + /* It is still unclear how this will work on PPT, so throw up a warning */ + WARN_ON(!HAS_PCH_LPT(dev)); + + if (encoder->type == DRM_MODE_ENCODER_DAC) { + DRM_DEBUG_KMS("Haswell detected DAC encoder, assuming is PCH\n"); + return true; + } else { + DRM_DEBUG_KMS("Haswell detected encoder %d, assuming is CPU\n", + encoder->type); + return false; + } + } + switch (encoder->type) { case INTEL_OUTPUT_EDP: if (!intel_encoder_is_pch_edp(&encoder->base)) From d0d3e513609a19de52a42ee25ce40fd5b55b5a38 Mon Sep 17 00:00:00 2001 From: Eugeni Dodonov Date: Wed, 9 May 2012 15:37:16 -0300 Subject: [PATCH 30/57] drm/i915: enable power wells on Haswell init This attempts to enable all the available power wells during the initialization. Those power wells can be enabled in parallel or on-demand, and disabled when no longer needed, but this is out of scope of this initial enablement. Proper tracking of who uses which power well will require a considerable rework of our display handling, so we just leave them all enabled when the driver is loaded for now. v2: use more generic and future-proof code Signed-off-by: Eugeni Dodonov Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_pm.c | 36 +++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c index 21587f89f44..bd9549de4a3 100644 --- a/drivers/gpu/drm/i915/intel_pm.c +++ b/drivers/gpu/drm/i915/intel_pm.c @@ -3559,6 +3559,37 @@ void intel_sanitize_pm(struct drm_device *dev) dev_priv->display.sanitize_pm(dev); } +/* Starting with Haswell, we have different power wells for + * different parts of the GPU. This attempts to enable them all. + */ +void intel_init_power_wells(struct drm_device *dev) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + unsigned long power_wells[] = { + HSW_PWR_WELL_CTL1, + HSW_PWR_WELL_CTL2, + HSW_PWR_WELL_CTL4 + }; + int i; + + if (!IS_HASWELL(dev)) + return; + + mutex_lock(&dev->struct_mutex); + + for (i = 0; i < ARRAY_SIZE(power_wells); i++) { + int well = I915_READ(power_wells[i]); + + if ((well & HSW_PWR_WELL_STATE) == 0) { + I915_WRITE(power_wells[i], well & HSW_PWR_WELL_ENABLE); + if (wait_for(I915_READ(power_wells[i] & HSW_PWR_WELL_STATE), 20)) + DRM_ERROR("Error enabling power well %lx\n", power_wells[i]); + } + } + + mutex_unlock(&dev->struct_mutex); +} + /* Set up chip specific power management-related functions */ void intel_init_pm(struct drm_device *dev) { @@ -3707,5 +3738,10 @@ void intel_init_pm(struct drm_device *dev) else dev_priv->display.get_fifo_size = i830_get_fifo_size; } + + /* We attempt to init the necessary power wells early in the initialization + * time, so the subsystems that expect power to be enabled can work. + */ + intel_init_power_wells(dev); } From 9d82aa17407a9f3fcdd235799fa0eaed3ed1e2f1 Mon Sep 17 00:00:00 2001 From: Eugeni Dodonov Date: Wed, 9 May 2012 15:37:17 -0300 Subject: [PATCH 31/57] drm/i915: add LPT PCH checks Avoid bogus asserts and PCH PLL accesses on Lynx Point. Signed-off-by: Eugeni Dodonov Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_display.c | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index bb049a17863..c8f85de137e 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -916,6 +916,11 @@ static void assert_pch_pll(struct drm_i915_private *dev_priv, u32 val; bool cur_state; + if (HAS_PCH_LPT(dev_priv->dev)) { + DRM_DEBUG_DRIVER("LPT detected: skipping PCH PLL test\n"); + return; + } + if (!intel_crtc->pch_pll) { WARN(1, "asserting PCH PLL enabled with no PLL\n"); return; @@ -1101,6 +1106,11 @@ static void assert_pch_refclk_enabled(struct drm_i915_private *dev_priv) u32 val; bool enabled; + if (HAS_PCH_LPT(dev_priv->dev)) { + DRM_DEBUG_DRIVER("LPT does not has PCH refclk, skipping check\n"); + return; + } + val = I915_READ(PCH_DREF_CONTROL); enabled = !!(val & (DREF_SSC_SOURCE_MASK | DREF_NONSPREAD_SOURCE_MASK | DREF_SUPERSPREAD_SOURCE_MASK)); @@ -4406,8 +4416,12 @@ static int ironlake_crtc_mode_set(struct drm_crtc *crtc, DRM_DEBUG_KMS("Mode for pipe %d:\n", pipe); drm_mode_debug_printmodeline(mode); - /* CPU eDP is the only output that doesn't need a PCH PLL of its own */ - if (!is_cpu_edp) { + /* CPU eDP is the only output that doesn't need a PCH PLL of its own on + * pre-Haswell/LPT generation */ + if (HAS_PCH_LPT(dev)) { + DRM_DEBUG_KMS("LPT detected: no PLL for pipe %d necessary\n", + pipe); + } else if (!is_cpu_edp) { struct intel_pch_pll *pll; pll = intel_get_pch_pll(intel_crtc, dpll, fp); From bf507ef7aaeed60b20aca9cf4b5a89060158724b Mon Sep 17 00:00:00 2001 From: Eugeni Dodonov Date: Wed, 9 May 2012 15:37:18 -0300 Subject: [PATCH 32/57] drm/i915: handle DDI-related assertions Prevent bogus asserts on DDI-related paths. Longer explanation from Eugeni by mail: "For the asserts there are 3 paths where we hit them: - in assert_fdi_tx (we don't have the FDI_TX_CTL anymore, backup plan DDI_FUNC_CTL is used instead) - in assert_fdi_tx_pll_enabled (we have the combination of iCLKIP and DDI_FUNC_CTL, plus PORT_CLK_SEL and PIPE_CLK_SEL now to make things work). We could use an assert here indeed - if we configure port to use one clock, and pipe to use another, everything hangs. Right now, we configure all of them in one place only; but yes, when DP code lands it will get more funky. - and in ironlake_fdi_pll_enable. I reuse part of this function (to configure the TU sizes), but as in the 1st case, FDI_TX_CTL is gone so I just ignore it here." Signed-off-by: Eugeni Dodonov [danvet: Pasted Eugeni's explanation into the commit message.] Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_display.c | 35 ++++++++++++++++++++-------- 1 file changed, 25 insertions(+), 10 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index c8f85de137e..7d59d0e852f 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -953,9 +953,16 @@ static void assert_fdi_tx(struct drm_i915_private *dev_priv, u32 val; bool cur_state; - reg = FDI_TX_CTL(pipe); - val = I915_READ(reg); - cur_state = !!(val & FDI_TX_ENABLE); + if (IS_HASWELL(dev_priv->dev)) { + /* On Haswell, DDI is used instead of FDI_TX_CTL */ + reg = DDI_FUNC_CTL(pipe); + val = I915_READ(reg); + cur_state = !!(val & PIPE_DDI_FUNC_ENABLE); + } else { + reg = FDI_TX_CTL(pipe); + val = I915_READ(reg); + cur_state = !!(val & FDI_TX_ENABLE); + } WARN(cur_state != state, "FDI TX state assertion failure (expected %s, current %s)\n", state_string(state), state_string(cur_state)); @@ -990,6 +997,10 @@ static void assert_fdi_tx_pll_enabled(struct drm_i915_private *dev_priv, if (dev_priv->info->gen == 5) return; + /* On Haswell, DDI ports are responsible for the FDI PLL setup */ + if (IS_HASWELL(dev_priv->dev)) + return; + reg = FDI_TX_CTL(pipe); val = I915_READ(reg); WARN(!(val & FDI_TX_PLL_ENABLE), "FDI TX PLL assertion failure, should be active but is disabled\n"); @@ -2514,14 +2525,18 @@ static void ironlake_fdi_pll_enable(struct drm_crtc *crtc) POSTING_READ(reg); udelay(200); - /* Enable CPU FDI TX PLL, always on for Ironlake */ - reg = FDI_TX_CTL(pipe); - temp = I915_READ(reg); - if ((temp & FDI_TX_PLL_ENABLE) == 0) { - I915_WRITE(reg, temp | FDI_TX_PLL_ENABLE); + /* On Haswell, the PLL configuration for ports and pipes is handled + * separately, as part of DDI setup */ + if (!IS_HASWELL(dev)) { + /* Enable CPU FDI TX PLL, always on for Ironlake */ + reg = FDI_TX_CTL(pipe); + temp = I915_READ(reg); + if ((temp & FDI_TX_PLL_ENABLE) == 0) { + I915_WRITE(reg, temp | FDI_TX_PLL_ENABLE); - POSTING_READ(reg); - udelay(100); + POSTING_READ(reg); + udelay(100); + } } } From 59c859d6f2e78344945e8a8406a194156176bc4e Mon Sep 17 00:00:00 2001 From: Eugeni Dodonov Date: Wed, 9 May 2012 15:37:19 -0300 Subject: [PATCH 33/57] drm/i915: account for only one PCH receiver on Haswell On Haswell, only one pipe can work in FDI mode, so this patch prevents messing with wrong registers when FDI is being used by non-first pipe. And to prevent this, we also specify that the VGA can only be used on pipe 0 for now in the crtc_mask value. Reviewed-by: Jesse Barnes Signed-off-by: Eugeni Dodonov Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_crt.c | 6 +++++- drivers/gpu/drm/i915/intel_display.c | 19 ++++++++++++++++--- 2 files changed, 21 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_crt.c b/drivers/gpu/drm/i915/intel_crt.c index 417ca99e697..75a70c46ef1 100644 --- a/drivers/gpu/drm/i915/intel_crt.c +++ b/drivers/gpu/drm/i915/intel_crt.c @@ -615,7 +615,11 @@ void intel_crt_init(struct drm_device *dev) crt->base.clone_mask = (1 << INTEL_SDVO_NON_TV_CLONE_BIT | 1 << INTEL_ANALOG_CLONE_BIT | 1 << INTEL_SDVO_LVDS_CLONE_BIT); - crt->base.crtc_mask = (1 << 0) | (1 << 1); + if (IS_HASWELL(dev)) + crt->base.crtc_mask = (1 << 0); + else + crt->base.crtc_mask = (1 << 0) | (1 << 1); + if (IS_GEN2(dev)) connector->interlace_allowed = 0; else diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 7d59d0e852f..101c4d458c3 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -977,9 +977,14 @@ static void assert_fdi_rx(struct drm_i915_private *dev_priv, u32 val; bool cur_state; - reg = FDI_RX_CTL(pipe); - val = I915_READ(reg); - cur_state = !!(val & FDI_RX_ENABLE); + if (IS_HASWELL(dev_priv->dev) && pipe > 0) { + DRM_ERROR("Attempting to enable FDI_RX on Haswell pipe > 0\n"); + return; + } else { + reg = FDI_RX_CTL(pipe); + val = I915_READ(reg); + cur_state = !!(val & FDI_RX_ENABLE); + } WARN(cur_state != state, "FDI RX state assertion failure (expected %s, current %s)\n", state_string(state), state_string(cur_state)); @@ -1012,6 +1017,10 @@ static void assert_fdi_rx_pll_enabled(struct drm_i915_private *dev_priv, int reg; u32 val; + if (IS_HASWELL(dev_priv->dev) && pipe > 0) { + DRM_ERROR("Attempting to enable FDI on Haswell with pipe > 0\n"); + return; + } reg = FDI_RX_CTL(pipe); val = I915_READ(reg); WARN(!(val & FDI_RX_PLL_ENABLE), "FDI RX PLL assertion failure, should be active but is disabled\n"); @@ -1483,6 +1492,10 @@ static void intel_enable_transcoder(struct drm_i915_private *dev_priv, assert_fdi_tx_enabled(dev_priv, pipe); assert_fdi_rx_enabled(dev_priv, pipe); + if (IS_HASWELL(dev_priv->dev) && pipe > 0) { + DRM_ERROR("Attempting to enable transcoder on Haswell with pipe > 0\n"); + return; + } reg = TRANSCONF(pipe); val = I915_READ(reg); pipeconf_val = I915_READ(PIPECONF(pipe)); From 45244b87943b1173353b48854352bb9f4b065867 Mon Sep 17 00:00:00 2001 From: Eugeni Dodonov Date: Wed, 9 May 2012 15:37:20 -0300 Subject: [PATCH 34/57] drm/i915: initialize DDI buffer translations DDI is introduced starting with Haswell GPU generation. So to simplify its management in the future, we also add intel_ddi.c to hold all the DDI-related items. Buffer translations for DDI links must be initialized prior to enablement. For FDI and DP, first 9 pairs of values are used to select the connection parameters. HDMI uses the last pair of values and ignores the first 9 pairs. So we program HDMI values in both cases, which allows HDMI to work over both FDI and DP-friendly buffers. Reviewed-by: Jesse Barnes Signed-off-by: Eugeni Dodonov Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/Makefile | 1 + drivers/gpu/drm/i915/intel_ddi.c | 107 +++++++++++++++++++++++++++ drivers/gpu/drm/i915/intel_display.c | 2 + drivers/gpu/drm/i915/intel_drv.h | 1 + 4 files changed, 111 insertions(+) create mode 100644 drivers/gpu/drm/i915/intel_ddi.c diff --git a/drivers/gpu/drm/i915/Makefile b/drivers/gpu/drm/i915/Makefile index 8b8bbc70f86..0ca7f7646ab 100644 --- a/drivers/gpu/drm/i915/Makefile +++ b/drivers/gpu/drm/i915/Makefile @@ -19,6 +19,7 @@ i915-y := i915_drv.o i915_dma.o i915_irq.o \ intel_crt.o \ intel_lvds.o \ intel_bios.o \ + intel_ddi.o \ intel_dp.o \ intel_hdmi.o \ intel_sdvo.o \ diff --git a/drivers/gpu/drm/i915/intel_ddi.c b/drivers/gpu/drm/i915/intel_ddi.c new file mode 100644 index 00000000000..08f210b8daf --- /dev/null +++ b/drivers/gpu/drm/i915/intel_ddi.c @@ -0,0 +1,107 @@ +/* + * Copyright © 2012 Intel Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + * + * Authors: + * Eugeni Dodonov + * + */ + +#include "i915_drv.h" +#include "intel_drv.h" + +/* HDMI/DVI modes ignore everything but the last 2 items. So we share + * them for both DP and FDI transports, allowing those ports to + * automatically adapt to HDMI connections as well + */ +static const u32 hsw_ddi_translations_dp[] = { + 0x00FFFFFF, 0x0006000E, /* DP parameters */ + 0x00D75FFF, 0x0005000A, + 0x00C30FFF, 0x00040006, + 0x80AAAFFF, 0x000B0000, + 0x00FFFFFF, 0x0005000A, + 0x00D75FFF, 0x000C0004, + 0x80C30FFF, 0x000B0000, + 0x00FFFFFF, 0x00040006, + 0x80D75FFF, 0x000B0000, + 0x00FFFFFF, 0x00040006 /* HDMI parameters */ +}; + +static const u32 hsw_ddi_translations_fdi[] = { + 0x00FFFFFF, 0x0007000E, /* FDI parameters */ + 0x00D75FFF, 0x000F000A, + 0x00C30FFF, 0x00060006, + 0x00AAAFFF, 0x001E0000, + 0x00FFFFFF, 0x000F000A, + 0x00D75FFF, 0x00160004, + 0x00C30FFF, 0x001E0000, + 0x00FFFFFF, 0x00060006, + 0x00D75FFF, 0x001E0000, + 0x00FFFFFF, 0x00040006 /* HDMI parameters */ +}; + +/* On Haswell, DDI port buffers must be programmed with correct values + * in advance. The buffer values are different for FDI and DP modes, + * but the HDMI/DVI fields are shared among those. So we program the DDI + * in either FDI or DP modes only, as HDMI connections will work with both + * of those + */ +void intel_prepare_ddi_buffers(struct drm_device *dev, enum port port, bool use_fdi_mode) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + u32 reg; + int i; + const u32 *ddi_translations = ((use_fdi_mode) ? + hsw_ddi_translations_fdi : + hsw_ddi_translations_dp); + + DRM_DEBUG_DRIVER("Initializing DDI buffers for port %c in %s mode\n", + port_name(port), + use_fdi_mode ? "FDI" : "DP"); + + WARN((use_fdi_mode && (port != PORT_E)), + "Programming port %c in FDI mode, this probably will not work.\n", + port_name(port)); + + for (i=0, reg=DDI_BUF_TRANS(port); i < ARRAY_SIZE(hsw_ddi_translations_fdi); i++) { + I915_WRITE(reg, ddi_translations[i]); + reg += 4; + } +} + +/* Program DDI buffers translations for DP. By default, program ports A-D in DP + * mode and port E for FDI. + */ +void intel_prepare_ddi(struct drm_device *dev) +{ + int port; + + if (IS_HASWELL(dev)) { + for (port = PORT_A; port < PORT_E; port++) + intel_prepare_ddi_buffers(dev, port, false); + + /* DDI E is the suggested one to work in FDI mode, so program is as such by + * default. It will have to be re-programmed in case a digital DP output + * will be detected on it + */ + intel_prepare_ddi_buffers(dev, PORT_E, true); + } +} diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 101c4d458c3..096a1b1eaf4 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -6806,6 +6806,8 @@ void intel_modeset_init(struct drm_device *dev) intel_init_pm(dev); + intel_prepare_ddi(dev); + intel_init_display(dev); if (IS_GEN2(dev)) { diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index e5ee166e2fa..e6ce02b5730 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -446,6 +446,7 @@ extern void intel_init_clock_gating(struct drm_device *dev); extern void intel_write_eld(struct drm_encoder *encoder, struct drm_display_mode *mode); extern void intel_cpt_verify_modeset(struct drm_device *dev, int pipe); +extern void intel_prepare_ddi(struct drm_device *dev); /* For use by IVB LP watermark workaround in intel_sprite.c */ extern void intel_update_watermarks(struct drm_device *dev); From c82e4d265d98aed94547435605b5e05f8aae9676 Mon Sep 17 00:00:00 2001 From: Eugeni Dodonov Date: Wed, 9 May 2012 15:37:21 -0300 Subject: [PATCH 35/57] drm/i915: support DDI training in FDI mode Starting with Haswell, DDI ports can work in FDI mode to support connectivity with the outputs located on the PCH. This commit adds support for such connections in the intel_ddi module, and provides Haswell-specific functionality to make it work. v2: simplify the commit as per Daniel Vetter suggestion. Signed-off-by: Eugeni Dodonov Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_ddi.c | 115 +++++++++++++++++++++++++++ drivers/gpu/drm/i915/intel_display.c | 2 + drivers/gpu/drm/i915/intel_drv.h | 1 + 3 files changed, 118 insertions(+) diff --git a/drivers/gpu/drm/i915/intel_ddi.c b/drivers/gpu/drm/i915/intel_ddi.c index 08f210b8daf..f44aae12477 100644 --- a/drivers/gpu/drm/i915/intel_ddi.c +++ b/drivers/gpu/drm/i915/intel_ddi.c @@ -105,3 +105,118 @@ void intel_prepare_ddi(struct drm_device *dev) intel_prepare_ddi_buffers(dev, PORT_E, true); } } + +static const long hsw_ddi_buf_ctl_values[] = { + DDI_BUF_EMP_400MV_0DB_HSW, + DDI_BUF_EMP_400MV_3_5DB_HSW, + DDI_BUF_EMP_400MV_6DB_HSW, + DDI_BUF_EMP_400MV_9_5DB_HSW, + DDI_BUF_EMP_600MV_0DB_HSW, + DDI_BUF_EMP_600MV_3_5DB_HSW, + DDI_BUF_EMP_600MV_6DB_HSW, + DDI_BUF_EMP_800MV_0DB_HSW, + DDI_BUF_EMP_800MV_3_5DB_HSW +}; + + +/* Starting with Haswell, different DDI ports can work in FDI mode for + * connection to the PCH-located connectors. For this, it is necessary to train + * both the DDI port and PCH receiver for the desired DDI buffer settings. + * + * The recommended port to work in FDI mode is DDI E, which we use here. Also, + * please note that when FDI mode is active on DDI E, it shares 2 lines with + * DDI A (which is used for eDP) + */ + +void hsw_fdi_link_train(struct drm_crtc *crtc) +{ + struct drm_device *dev = crtc->dev; + struct drm_i915_private *dev_priv = dev->dev_private; + struct intel_crtc *intel_crtc = to_intel_crtc(crtc); + int pipe = intel_crtc->pipe; + u32 reg, temp, i; + + /* Configure CPU PLL, wait for warmup */ + I915_WRITE(SPLL_CTL, + SPLL_PLL_ENABLE | + SPLL_PLL_FREQ_1350MHz | + SPLL_PLL_SCC); + + /* Use SPLL to drive the output when in FDI mode */ + I915_WRITE(PORT_CLK_SEL(PORT_E), + PORT_CLK_SEL_SPLL); + I915_WRITE(PIPE_CLK_SEL(pipe), + PIPE_CLK_SEL_PORT(PORT_E)); + + udelay(20); + + /* Start the training iterating through available voltages and emphasis */ + for (i=0; i < ARRAY_SIZE(hsw_ddi_buf_ctl_values); i++) { + /* Configure DP_TP_CTL with auto-training */ + I915_WRITE(DP_TP_CTL(PORT_E), + DP_TP_CTL_FDI_AUTOTRAIN | + DP_TP_CTL_ENHANCED_FRAME_ENABLE | + DP_TP_CTL_LINK_TRAIN_PAT1 | + DP_TP_CTL_ENABLE); + + /* Configure and enable DDI_BUF_CTL for DDI E with next voltage */ + temp = I915_READ(DDI_BUF_CTL(PORT_E)); + temp = (temp & ~DDI_BUF_EMP_MASK); + I915_WRITE(DDI_BUF_CTL(PORT_E), + temp | + DDI_BUF_CTL_ENABLE | + DDI_PORT_WIDTH_X2 | + hsw_ddi_buf_ctl_values[i]); + + udelay(600); + + /* Enable CPU FDI Receiver with auto-training */ + reg = FDI_RX_CTL(pipe); + I915_WRITE(reg, + I915_READ(reg) | + FDI_LINK_TRAIN_AUTO | + FDI_RX_ENABLE | + FDI_LINK_TRAIN_PATTERN_1_CPT | + FDI_RX_ENHANCE_FRAME_ENABLE | + FDI_PORT_WIDTH_2X_LPT | + FDI_RX_PLL_ENABLE); + POSTING_READ(reg); + udelay(100); + + temp = I915_READ(DP_TP_STATUS(PORT_E)); + if (temp & DP_TP_STATUS_AUTOTRAIN_DONE) { + DRM_DEBUG_DRIVER("BUF_CTL training done on %d step\n", i); + + /* Enable normal pixel sending for FDI */ + I915_WRITE(DP_TP_CTL(PORT_E), + DP_TP_CTL_FDI_AUTOTRAIN | + DP_TP_CTL_LINK_TRAIN_NORMAL | + DP_TP_CTL_ENHANCED_FRAME_ENABLE | + DP_TP_CTL_ENABLE); + + /* Enable PIPE_DDI_FUNC_CTL for the pipe to work in FDI mode */ + temp = I915_READ(DDI_FUNC_CTL(pipe)); + temp &= ~PIPE_DDI_PORT_MASK; + temp |= PIPE_DDI_SELECT_PORT(PORT_E) | + PIPE_DDI_MODE_SELECT_FDI | + PIPE_DDI_FUNC_ENABLE | + PIPE_DDI_PORT_WIDTH_X2; + I915_WRITE(DDI_FUNC_CTL(pipe), + temp); + break; + } else { + DRM_ERROR("Error training BUF_CTL %d\n", i); + + /* Disable DP_TP_CTL and FDI_RX_CTL) and retry */ + I915_WRITE(DP_TP_CTL(PORT_E), + I915_READ(DP_TP_CTL(PORT_E)) & + ~DP_TP_CTL_ENABLE); + I915_WRITE(FDI_RX_CTL(pipe), + I915_READ(FDI_RX_CTL(pipe)) & + ~FDI_RX_PLL_ENABLE); + continue; + } + } + + DRM_DEBUG_KMS("FDI train done.\n"); +} diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 096a1b1eaf4..3edf8f6ce2f 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -6607,6 +6607,8 @@ static void intel_init_display(struct drm_device *dev) /* FIXME: detect B0+ stepping and use auto training */ dev_priv->display.fdi_link_train = ivb_manual_fdi_link_train; dev_priv->display.write_eld = ironlake_write_eld; + } else if (IS_HASWELL(dev)) { + dev_priv->display.fdi_link_train = hsw_fdi_link_train; } else dev_priv->display.update_wm = NULL; } else if (IS_VALLEYVIEW(dev)) { diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index e6ce02b5730..a5bb5b6ebb4 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -447,6 +447,7 @@ extern void intel_write_eld(struct drm_encoder *encoder, struct drm_display_mode *mode); extern void intel_cpt_verify_modeset(struct drm_device *dev, int pipe); extern void intel_prepare_ddi(struct drm_device *dev); +extern void hsw_fdi_link_train(struct drm_crtc *crtc); /* For use by IVB LP watermark workaround in intel_sprite.c */ extern void intel_update_watermarks(struct drm_device *dev); From 4abb3c8c713c34499ca2d640a319a8dead95f761 Mon Sep 17 00:00:00 2001 From: Eugeni Dodonov Date: Wed, 9 May 2012 15:37:22 -0300 Subject: [PATCH 36/57] drm/i915: use ironlake eld write routine for Haswell Signed-off-by: Eugeni Dodonov Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_display.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 3edf8f6ce2f..39f122b3ce7 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -6609,6 +6609,7 @@ static void intel_init_display(struct drm_device *dev) dev_priv->display.write_eld = ironlake_write_eld; } else if (IS_HASWELL(dev)) { dev_priv->display.fdi_link_train = hsw_fdi_link_train; + dev_priv->display.write_eld = ironlake_write_eld; } else dev_priv->display.update_wm = NULL; } else if (IS_VALLEYVIEW(dev)) { From 6b8a5eeb9f428becc88adef148518ed07bf06d02 Mon Sep 17 00:00:00 2001 From: Eugeni Dodonov Date: Wed, 9 May 2012 15:37:23 -0300 Subject: [PATCH 37/57] drm/i915: define Haswell watermarks and clock gating For now, we simple reuse the Ivy Bridge routines here. Signed-off-by: Eugeni Dodonov Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_pm.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c index bd9549de4a3..452a9bce816 100644 --- a/drivers/gpu/drm/i915/intel_pm.c +++ b/drivers/gpu/drm/i915/intel_pm.c @@ -3685,6 +3685,17 @@ void intel_init_pm(struct drm_device *dev) } dev_priv->display.init_clock_gating = ivybridge_init_clock_gating; dev_priv->display.sanitize_pm = gen6_sanitize_pm; + } else if (IS_HASWELL(dev)) { + if (SNB_READ_WM0_LATENCY()) { + dev_priv->display.update_wm = sandybridge_update_wm; + dev_priv->display.update_sprite_wm = sandybridge_update_sprite_wm; + } else { + DRM_DEBUG_KMS("Failed to read display plane latency. " + "Disable CxSR\n"); + dev_priv->display.update_wm = NULL; + } + dev_priv->display.init_clock_gating = ivybridge_init_clock_gating; + dev_priv->display.sanitize_pm = gen6_sanitize_pm; } else dev_priv->display.update_wm = NULL; } else if (IS_VALLEYVIEW(dev)) { From 1f8eeabf2e6f4a6cb5afe9e90d2a705e9709f1a1 Mon Sep 17 00:00:00 2001 From: Eugeni Dodonov Date: Wed, 9 May 2012 15:37:24 -0300 Subject: [PATCH 38/57] drm/i915: program WM_LINETIME on Haswell The line time can be programmed according to the number of horizontal pixels vs effective pixel rate ratio. v2: improve comment as per Chris Wilson suggestion v3: incorporate latest changes in specs. v4: move into wm update routine, also mention that the same routine can program IPS watermarks. We do not have their enablement code yet, nor handle the required clock settings at the moment, so this patch won't program those values for now. Signed-off-by: Eugeni Dodonov Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_drv.h | 2 ++ drivers/gpu/drm/i915/intel_display.c | 2 ++ drivers/gpu/drm/i915/intel_drv.h | 2 ++ drivers/gpu/drm/i915/intel_pm.c | 37 ++++++++++++++++++++++++++++ 4 files changed, 43 insertions(+) diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index e03a4f80c5c..83a557c7bcc 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -243,6 +243,8 @@ struct drm_i915_display_funcs { void (*update_sprite_wm)(struct drm_device *dev, int pipe, uint32_t sprite_width, int pixel_size); void (*sanitize_pm)(struct drm_device *dev); + void (*update_linetime_wm)(struct drm_device *dev, int pipe, + struct drm_display_mode *mode); int (*crtc_mode_set)(struct drm_crtc *crtc, struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode, diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 39f122b3ce7..fb3f4aaca2c 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -4609,6 +4609,8 @@ static int ironlake_crtc_mode_set(struct drm_crtc *crtc, intel_update_watermarks(dev); + intel_update_linetime_watermarks(dev, pipe, adjusted_mode); + return ret; } diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index a5bb5b6ebb4..0ad1bb38397 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -454,6 +454,8 @@ extern void intel_update_watermarks(struct drm_device *dev); extern void intel_update_sprite_watermarks(struct drm_device *dev, int pipe, uint32_t sprite_width, int pixel_size); +extern void intel_update_linetime_watermarks(struct drm_device *dev, int pipe, + struct drm_display_mode *mode); extern int intel_sprite_set_colorkey(struct drm_device *dev, void *data, struct drm_file *file_priv); diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c index 452a9bce816..8f8d1daf1ca 100644 --- a/drivers/gpu/drm/i915/intel_pm.c +++ b/drivers/gpu/drm/i915/intel_pm.c @@ -1883,6 +1883,33 @@ static void sandybridge_update_wm(struct drm_device *dev) cursor_wm); } +static void +haswell_update_linetime_wm(struct drm_device *dev, int pipe, + struct drm_display_mode *mode) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + u32 temp; + + temp = I915_READ(PIPE_WM_LINETIME(pipe)); + temp &= ~PIPE_WM_LINETIME_MASK; + + /* The WM are computed with base on how long it takes to fill a single + * row at the given clock rate, multiplied by 8. + * */ + temp |= PIPE_WM_LINETIME_TIME( + ((mode->crtc_hdisplay * 1000) / mode->clock) * 8); + + /* IPS watermarks are only used by pipe A, and are ignored by + * pipes B and C. They are calculated similarly to the common + * linetime values, except that we are using CD clock frequency + * in MHz instead of pixel rate for the division. + * + * This is a placeholder for the IPS watermark calculation code. + */ + + I915_WRITE(PIPE_WM_LINETIME(pipe), temp); +} + static bool sandybridge_compute_sprite_wm(struct drm_device *dev, int plane, uint32_t sprite_width, int pixel_size, @@ -2078,6 +2105,15 @@ void intel_update_watermarks(struct drm_device *dev) dev_priv->display.update_wm(dev); } +void intel_update_linetime_watermarks(struct drm_device *dev, + int pipe, struct drm_display_mode *mode) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + + if (dev_priv->display.update_linetime_wm) + dev_priv->display.update_linetime_wm(dev, pipe, mode); +} + void intel_update_sprite_watermarks(struct drm_device *dev, int pipe, uint32_t sprite_width, int pixel_size) { @@ -3689,6 +3725,7 @@ void intel_init_pm(struct drm_device *dev) if (SNB_READ_WM0_LATENCY()) { dev_priv->display.update_wm = sandybridge_update_wm; dev_priv->display.update_sprite_wm = sandybridge_update_sprite_wm; + dev_priv->display.update_linetime_wm = haswell_update_linetime_wm; } else { DRM_DEBUG_KMS("Failed to read display plane latency. " "Disable CxSR\n"); From e615efe4b879de87ef6a4192d7e5b0bd0d2e1518 Mon Sep 17 00:00:00 2001 From: Eugeni Dodonov Date: Wed, 9 May 2012 15:37:26 -0300 Subject: [PATCH 39/57] drm/i915: program iCLKIP on Lynx Point The iCLKIP clock is used to drive the VGA pixel clock on the PCH. In order to do so, it must be programmed to properly do the clock ticks according to the divisor, phase direction, phase increments and a special auxiliary divisor for 20MHz clock. v2: calculate divisor values directly instead of relying on a table. v3: merged a fix from Ben to properly check for invalid divider values. Reviewed-by: Ben Widawsky Signed-off-by: Eugeni Dodonov Signed-off-by: Ben Widawsky Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_display.c | 100 ++++++++++++++++++++++++++- 1 file changed, 97 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index fb3f4aaca2c..f1bb2e2ec17 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -2675,6 +2675,97 @@ static bool intel_crtc_driving_pch(struct drm_crtc *crtc) return true; } +/* Program iCLKIP clock to the desired frequency */ +static void lpt_program_iclkip(struct drm_crtc *crtc) +{ + struct drm_device *dev = crtc->dev; + struct drm_i915_private *dev_priv = dev->dev_private; + u32 divsel, phaseinc, auxdiv, phasedir = 0; + u32 temp; + + /* It is necessary to ungate the pixclk gate prior to programming + * the divisors, and gate it back when it is done. + */ + I915_WRITE(PIXCLK_GATE, PIXCLK_GATE_GATE); + + /* Disable SSCCTL */ + intel_sbi_write(dev_priv, SBI_SSCCTL6, + intel_sbi_read(dev_priv, SBI_SSCCTL6) | + SBI_SSCCTL_DISABLE); + + /* 20MHz is a corner case which is out of range for the 7-bit divisor */ + if (crtc->mode.clock == 20000) { + auxdiv = 1; + divsel = 0x41; + phaseinc = 0x20; + } else { + /* The iCLK virtual clock root frequency is in MHz, + * but the crtc->mode.clock in in KHz. To get the divisors, + * it is necessary to divide one by another, so we + * convert the virtual clock precision to KHz here for higher + * precision. + */ + u32 iclk_virtual_root_freq = 172800 * 1000; + u32 iclk_pi_range = 64; + u32 desired_divisor, msb_divisor_value, pi_value; + + desired_divisor = (iclk_virtual_root_freq / crtc->mode.clock); + msb_divisor_value = desired_divisor / iclk_pi_range; + pi_value = desired_divisor % iclk_pi_range; + + auxdiv = 0; + divsel = msb_divisor_value - 2; + phaseinc = pi_value; + } + + /* This should not happen with any sane values */ + WARN_ON(SBI_SSCDIVINTPHASE_DIVSEL(divsel) & + ~SBI_SSCDIVINTPHASE_DIVSEL_MASK); + WARN_ON(SBI_SSCDIVINTPHASE_DIR(phasedir) & + ~SBI_SSCDIVINTPHASE_INCVAL_MASK); + + DRM_DEBUG_KMS("iCLKIP clock: found settings for %dKHz refresh rate: auxdiv=%x, divsel=%x, phasedir=%x, phaseinc=%x\n", + crtc->mode.clock, + auxdiv, + divsel, + phasedir, + phaseinc); + + /* Program SSCDIVINTPHASE6 */ + temp = intel_sbi_read(dev_priv, SBI_SSCDIVINTPHASE6); + temp &= ~SBI_SSCDIVINTPHASE_DIVSEL_MASK; + temp |= SBI_SSCDIVINTPHASE_DIVSEL(divsel); + temp &= ~SBI_SSCDIVINTPHASE_INCVAL_MASK; + temp |= SBI_SSCDIVINTPHASE_INCVAL(phaseinc); + temp |= SBI_SSCDIVINTPHASE_DIR(phasedir); + temp |= SBI_SSCDIVINTPHASE_PROPAGATE; + + intel_sbi_write(dev_priv, + SBI_SSCDIVINTPHASE6, + temp); + + /* Program SSCAUXDIV */ + temp = intel_sbi_read(dev_priv, SBI_SSCAUXDIV6); + temp &= ~SBI_SSCAUXDIV_FINALDIV2SEL(1); + temp |= SBI_SSCAUXDIV_FINALDIV2SEL(auxdiv); + intel_sbi_write(dev_priv, + SBI_SSCAUXDIV6, + temp); + + + /* Enable modulator and associated divider */ + temp = intel_sbi_read(dev_priv, SBI_SSCCTL6); + temp &= ~SBI_SSCCTL_DISABLE; + intel_sbi_write(dev_priv, + SBI_SSCCTL6, + temp); + + /* Wait for initialization time */ + udelay(24); + + I915_WRITE(PIXCLK_GATE, PIXCLK_GATE_UNGATE); +} + /* * Enable PCH resources required for PCH ports: * - PCH PLLs @@ -2694,11 +2785,14 @@ static void ironlake_pch_enable(struct drm_crtc *crtc) /* For PCH output, training FDI link */ dev_priv->display.fdi_link_train(crtc); - intel_enable_pch_pll(intel_crtc); - - if (HAS_PCH_CPT(dev)) { + if (HAS_PCH_LPT(dev)) { + DRM_DEBUG_KMS("LPT detected: programming iCLKIP\n"); + lpt_program_iclkip(crtc); + } else if (HAS_PCH_CPT(dev)) { u32 sel; + intel_enable_pch_pll(intel_crtc); + temp = I915_READ(PCH_DPLL_SEL); switch (pipe) { default: From 0e72a5b55e706dafa3402611f6f9e082a810673d Mon Sep 17 00:00:00 2001 From: Eugeni Dodonov Date: Wed, 9 May 2012 15:37:27 -0300 Subject: [PATCH 40/57] drm/i915: detect digital outputs on Haswell Digital port detection on Haswell is indicated by the presence of a bit in DDI_BUF_CTL for port A, and by a different register for ports B, C and D. So we check for those bits during the initialization time and let the hdmi function know about those. Note that this bit does not indicates whether the output is DP or HDMI. However, the DDI buffers can be programmed in a way that is shared between DP/HDMI and FDI/HDMI except for PORT E. So for now, we detect those digital outputs as being HDMI, but proper DP support is still pending. Note that DDI A can only drive eDP, so we do not handle it here for hdmi initialization. v2: simplify Haswell handling logic v3: use generic function for handling digital outputs. Signed-off-by: Eugeni Dodonov Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_ddi.c | 29 ++++++++++++++++++++++++++++ drivers/gpu/drm/i915/intel_display.c | 21 +++++++++++++++++++- drivers/gpu/drm/i915/intel_drv.h | 1 + 3 files changed, 50 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/i915/intel_ddi.c b/drivers/gpu/drm/i915/intel_ddi.c index f44aae12477..22bb0dd9a91 100644 --- a/drivers/gpu/drm/i915/intel_ddi.c +++ b/drivers/gpu/drm/i915/intel_ddi.c @@ -220,3 +220,32 @@ void hsw_fdi_link_train(struct drm_crtc *crtc) DRM_DEBUG_KMS("FDI train done.\n"); } + +/* For DDI connections, it is possible to support different outputs over the + * same DDI port, such as HDMI or DP or even VGA via FDI. So we don't know by + * the time the output is detected what exactly is on the other end of it. This + * function aims at providing support for this detection and proper output + * configuration. + */ +void intel_ddi_init(struct drm_device *dev, enum port port) +{ + /* For now, we don't do any proper output detection and assume that we + * handle HDMI only */ + + switch(port){ + case PORT_A: + /* We don't handle eDP and DP yet */ + DRM_DEBUG_DRIVER("Found digital output on DDI port A\n"); + break; + /* Assume that the ports B, C and D are working in HDMI mode for now */ + case PORT_B: + case PORT_C: + case PORT_D: + intel_hdmi_init(dev, DDI_BUF_CTL(port)); + break; + default: + DRM_DEBUG_DRIVER("No handlers defined for port %d, skipping DDI initialization\n", + port); + break; + } +} diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index f1bb2e2ec17..1a06f97e73f 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -6474,7 +6474,26 @@ static void intel_setup_outputs(struct drm_device *dev) intel_crt_init(dev); - if (HAS_PCH_SPLIT(dev)) { + if (IS_HASWELL(dev)) { + int found; + + /* Haswell uses DDI functions to detect digital outputs */ + found = I915_READ(DDI_BUF_CTL_A) & DDI_INIT_DISPLAY_DETECTED; + /* DDI A only supports eDP */ + if (found) + intel_ddi_init(dev, PORT_A); + + /* DDI B, C and D detection is indicated by the SFUSE_STRAP + * register */ + found = I915_READ(SFUSE_STRAP); + + if (found & SFUSE_STRAP_DDIB_DETECTED) + intel_ddi_init(dev, PORT_B); + if (found & SFUSE_STRAP_DDIC_DETECTED) + intel_ddi_init(dev, PORT_C); + if (found & SFUSE_STRAP_DDID_DETECTED) + intel_ddi_init(dev, PORT_D); + } else if (HAS_PCH_SPLIT(dev)) { int found; if (I915_READ(HDMIB) & PORT_DETECTED) { diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index 0ad1bb38397..fd96ffbf501 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -448,6 +448,7 @@ extern void intel_write_eld(struct drm_encoder *encoder, extern void intel_cpt_verify_modeset(struct drm_device *dev, int pipe); extern void intel_prepare_ddi(struct drm_device *dev); extern void hsw_fdi_link_train(struct drm_crtc *crtc); +extern void intel_ddi_init(struct drm_device *dev, enum port port); /* For use by IVB LP watermark workaround in intel_sprite.c */ extern void intel_update_watermarks(struct drm_device *dev); From 7ceae0a55c52ca8a2a7cbdecc945e434fff77f94 Mon Sep 17 00:00:00 2001 From: Eugeni Dodonov Date: Wed, 9 May 2012 15:37:28 -0300 Subject: [PATCH 41/57] drm/i915: add support for DDI-controlled digital outputs Those are driven by DDIs on Haswell architecture, so we need to keep track of which DDI is being used on each output. Reviewed-by: Jesse Barnes Signed-off-by: Eugeni Dodonov Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_hdmi.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/drivers/gpu/drm/i915/intel_hdmi.c b/drivers/gpu/drm/i915/intel_hdmi.c index 8da8dfe1b4f..3b0cf719725 100644 --- a/drivers/gpu/drm/i915/intel_hdmi.c +++ b/drivers/gpu/drm/i915/intel_hdmi.c @@ -41,6 +41,7 @@ struct intel_hdmi { struct intel_encoder base; u32 sdvox_reg; int ddc_bus; + int ddi_port; uint32_t color_range; bool has_hdmi_sink; bool has_audio; @@ -661,6 +662,24 @@ void intel_hdmi_init(struct drm_device *dev, int sdvox_reg) intel_encoder->clone_mask = (1 << INTEL_HDMIF_CLONE_BIT); intel_hdmi->ddc_bus = GMBUS_PORT_DPD; dev_priv->hotplug_supported_mask |= HDMID_HOTPLUG_INT_STATUS; + } else if (sdvox_reg == DDI_BUF_CTL(PORT_B)) { + DRM_DEBUG_DRIVER("LPT: detected output on DDI B\n"); + intel_encoder->clone_mask = (1 << INTEL_HDMIB_CLONE_BIT); + intel_hdmi->ddc_bus = GMBUS_PORT_DPB; + intel_hdmi->ddi_port = PORT_B; + dev_priv->hotplug_supported_mask |= HDMIB_HOTPLUG_INT_STATUS; + } else if (sdvox_reg == DDI_BUF_CTL(PORT_C)) { + DRM_DEBUG_DRIVER("LPT: detected output on DDI C\n"); + intel_encoder->clone_mask = (1 << INTEL_HDMIC_CLONE_BIT); + intel_hdmi->ddc_bus = GMBUS_PORT_DPC; + intel_hdmi->ddi_port = PORT_C; + dev_priv->hotplug_supported_mask |= HDMIC_HOTPLUG_INT_STATUS; + } else if (sdvox_reg == DDI_BUF_CTL(PORT_D)) { + DRM_DEBUG_DRIVER("LPT: detected output on DDI D\n"); + intel_encoder->clone_mask = (1 << INTEL_HDMID_CLONE_BIT); + intel_hdmi->ddc_bus = GMBUS_PORT_DPD; + intel_hdmi->ddi_port = PORT_D; + dev_priv->hotplug_supported_mask |= HDMID_HOTPLUG_INT_STATUS; } else { /* If we got an unknown sdvox_reg, things are pretty much broken * in a way that we should let the kernel know about it */ From 12a13a3389716009db8deb64094656f946e8950d Mon Sep 17 00:00:00 2001 From: Eugeni Dodonov Date: Wed, 9 May 2012 15:37:29 -0300 Subject: [PATCH 42/57] drm/i915: add WR PLL programming table This table is used for programming WR PLL clocks, used by HDMI and DVI outputs. I split it into a separate patch to simplify the HDMI enabling patch which was getting huge. Note that this table is a temporary solution for WR PLL programming. It will be reworked into a more exact algorithm at a later stage. But for now, it provides the most accurate clock setting solution, so we use it here. Signed-off-by: Eugeni Dodonov Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_ddi.c | 388 +++++++++++++++++++++++++++++++ 1 file changed, 388 insertions(+) diff --git a/drivers/gpu/drm/i915/intel_ddi.c b/drivers/gpu/drm/i915/intel_ddi.c index 22bb0dd9a91..d4b268e5056 100644 --- a/drivers/gpu/drm/i915/intel_ddi.c +++ b/drivers/gpu/drm/i915/intel_ddi.c @@ -249,3 +249,391 @@ void intel_ddi_init(struct drm_device *dev, enum port port) break; } } + +/* WRPLL clock dividers */ +struct wrpll_tmds_clock { + u32 clock; + u16 p; /* Post divider */ + u16 n2; /* Feedback divider */ + u16 r2; /* Reference divider */ +}; + +/* Table of matching values for WRPLL clocks programming for each frequency */ +static const struct wrpll_tmds_clock wrpll_tmds_clock_table[] = { + {19750, 38, 25, 18}, + {20000, 48, 32, 18}, + {21000, 36, 21, 15}, + {21912, 42, 29, 17}, + {22000, 36, 22, 15}, + {23000, 36, 23, 15}, + {23500, 40, 40, 23}, + {23750, 26, 16, 14}, + {23750, 26, 16, 14}, + {24000, 36, 24, 15}, + {25000, 36, 25, 15}, + {25175, 26, 40, 33}, + {25200, 30, 21, 15}, + {26000, 36, 26, 15}, + {27000, 30, 21, 14}, + {27027, 18, 100, 111}, + {27500, 30, 29, 19}, + {28000, 34, 30, 17}, + {28320, 26, 30, 22}, + {28322, 32, 42, 25}, + {28750, 24, 23, 18}, + {29000, 30, 29, 18}, + {29750, 32, 30, 17}, + {30000, 30, 25, 15}, + {30750, 30, 41, 24}, + {31000, 30, 31, 18}, + {31500, 30, 28, 16}, + {32000, 30, 32, 18}, + {32500, 28, 32, 19}, + {33000, 24, 22, 15}, + {34000, 28, 30, 17}, + {35000, 26, 32, 19}, + {35500, 24, 30, 19}, + {36000, 26, 26, 15}, + {36750, 26, 46, 26}, + {37000, 24, 23, 14}, + {37762, 22, 40, 26}, + {37800, 20, 21, 15}, + {38000, 24, 27, 16}, + {38250, 24, 34, 20}, + {39000, 24, 26, 15}, + {40000, 24, 32, 18}, + {40500, 20, 21, 14}, + {40541, 22, 147, 89}, + {40750, 18, 19, 14}, + {41000, 16, 17, 14}, + {41500, 22, 44, 26}, + {41540, 22, 44, 26}, + {42000, 18, 21, 15}, + {42500, 22, 45, 26}, + {43000, 20, 43, 27}, + {43163, 20, 24, 15}, + {44000, 18, 22, 15}, + {44900, 20, 108, 65}, + {45000, 20, 25, 15}, + {45250, 20, 52, 31}, + {46000, 18, 23, 15}, + {46750, 20, 45, 26}, + {47000, 20, 40, 23}, + {48000, 18, 24, 15}, + {49000, 18, 49, 30}, + {49500, 16, 22, 15}, + {50000, 18, 25, 15}, + {50500, 18, 32, 19}, + {51000, 18, 34, 20}, + {52000, 18, 26, 15}, + {52406, 14, 34, 25}, + {53000, 16, 22, 14}, + {54000, 16, 24, 15}, + {54054, 16, 173, 108}, + {54500, 14, 24, 17}, + {55000, 12, 22, 18}, + {56000, 14, 45, 31}, + {56250, 16, 25, 15}, + {56750, 14, 25, 17}, + {57000, 16, 27, 16}, + {58000, 16, 43, 25}, + {58250, 16, 38, 22}, + {58750, 16, 40, 23}, + {59000, 14, 26, 17}, + {59341, 14, 40, 26}, + {59400, 16, 44, 25}, + {60000, 16, 32, 18}, + {60500, 12, 39, 29}, + {61000, 14, 49, 31}, + {62000, 14, 37, 23}, + {62250, 14, 42, 26}, + {63000, 12, 21, 15}, + {63500, 14, 28, 17}, + {64000, 12, 27, 19}, + {65000, 14, 32, 19}, + {65250, 12, 29, 20}, + {65500, 12, 32, 22}, + {66000, 12, 22, 15}, + {66667, 14, 38, 22}, + {66750, 10, 21, 17}, + {67000, 14, 33, 19}, + {67750, 14, 58, 33}, + {68000, 14, 30, 17}, + {68179, 14, 46, 26}, + {68250, 14, 46, 26}, + {69000, 12, 23, 15}, + {70000, 12, 28, 18}, + {71000, 12, 30, 19}, + {72000, 12, 24, 15}, + {73000, 10, 23, 17}, + {74000, 12, 23, 14}, + {74176, 8, 100, 91}, + {74250, 10, 22, 16}, + {74481, 12, 43, 26}, + {74500, 10, 29, 21}, + {75000, 12, 25, 15}, + {75250, 10, 39, 28}, + {76000, 12, 27, 16}, + {77000, 12, 53, 31}, + {78000, 12, 26, 15}, + {78750, 12, 28, 16}, + {79000, 10, 38, 26}, + {79500, 10, 28, 19}, + {80000, 12, 32, 18}, + {81000, 10, 21, 14}, + {81081, 6, 100, 111}, + {81624, 8, 29, 24}, + {82000, 8, 17, 14}, + {83000, 10, 40, 26}, + {83950, 10, 28, 18}, + {84000, 10, 28, 18}, + {84750, 6, 16, 17}, + {85000, 6, 17, 18}, + {85250, 10, 30, 19}, + {85750, 10, 27, 17}, + {86000, 10, 43, 27}, + {87000, 10, 29, 18}, + {88000, 10, 44, 27}, + {88500, 10, 41, 25}, + {89000, 10, 28, 17}, + {89012, 6, 90, 91}, + {89100, 10, 33, 20}, + {90000, 10, 25, 15}, + {91000, 10, 32, 19}, + {92000, 10, 46, 27}, + {93000, 10, 31, 18}, + {94000, 10, 40, 23}, + {94500, 10, 28, 16}, + {95000, 10, 44, 25}, + {95654, 10, 39, 22}, + {95750, 10, 39, 22}, + {96000, 10, 32, 18}, + {97000, 8, 23, 16}, + {97750, 8, 42, 29}, + {98000, 8, 45, 31}, + {99000, 8, 22, 15}, + {99750, 8, 34, 23}, + {100000, 6, 20, 18}, + {100500, 6, 19, 17}, + {101000, 6, 37, 33}, + {101250, 8, 21, 14}, + {102000, 6, 17, 15}, + {102250, 6, 25, 22}, + {103000, 8, 29, 19}, + {104000, 8, 37, 24}, + {105000, 8, 28, 18}, + {106000, 8, 22, 14}, + {107000, 8, 46, 29}, + {107214, 8, 27, 17}, + {108000, 8, 24, 15}, + {108108, 8, 173, 108}, + {109000, 6, 23, 19}, + {109000, 6, 23, 19}, + {110000, 6, 22, 18}, + {110013, 6, 22, 18}, + {110250, 8, 49, 30}, + {110500, 8, 36, 22}, + {111000, 8, 23, 14}, + {111264, 8, 150, 91}, + {111375, 8, 33, 20}, + {112000, 8, 63, 38}, + {112500, 8, 25, 15}, + {113100, 8, 57, 34}, + {113309, 8, 42, 25}, + {114000, 8, 27, 16}, + {115000, 6, 23, 18}, + {116000, 8, 43, 25}, + {117000, 8, 26, 15}, + {117500, 8, 40, 23}, + {118000, 6, 38, 29}, + {119000, 8, 30, 17}, + {119500, 8, 46, 26}, + {119651, 8, 39, 22}, + {120000, 8, 32, 18}, + {121000, 6, 39, 29}, + {121250, 6, 31, 23}, + {121750, 6, 23, 17}, + {122000, 6, 42, 31}, + {122614, 6, 30, 22}, + {123000, 6, 41, 30}, + {123379, 6, 37, 27}, + {124000, 6, 51, 37}, + {125000, 6, 25, 18}, + {125250, 4, 13, 14}, + {125750, 4, 27, 29}, + {126000, 6, 21, 15}, + {127000, 6, 24, 17}, + {127250, 6, 41, 29}, + {128000, 6, 27, 19}, + {129000, 6, 43, 30}, + {129859, 4, 25, 26}, + {130000, 6, 26, 18}, + {130250, 6, 42, 29}, + {131000, 6, 32, 22}, + {131500, 6, 38, 26}, + {131850, 6, 41, 28}, + {132000, 6, 22, 15}, + {132750, 6, 28, 19}, + {133000, 6, 34, 23}, + {133330, 6, 37, 25}, + {134000, 6, 61, 41}, + {135000, 6, 21, 14}, + {135250, 6, 167, 111}, + {136000, 6, 62, 41}, + {137000, 6, 35, 23}, + {138000, 6, 23, 15}, + {138500, 6, 40, 26}, + {138750, 6, 37, 24}, + {139000, 6, 34, 22}, + {139050, 6, 34, 22}, + {139054, 6, 34, 22}, + {140000, 6, 28, 18}, + {141000, 6, 36, 23}, + {141500, 6, 22, 14}, + {142000, 6, 30, 19}, + {143000, 6, 27, 17}, + {143472, 4, 17, 16}, + {144000, 6, 24, 15}, + {145000, 6, 29, 18}, + {146000, 6, 47, 29}, + {146250, 6, 26, 16}, + {147000, 6, 49, 30}, + {147891, 6, 23, 14}, + {148000, 6, 23, 14}, + {148250, 6, 28, 17}, + {148352, 4, 100, 91}, + {148500, 6, 33, 20}, + {149000, 6, 48, 29}, + {150000, 6, 25, 15}, + {151000, 4, 19, 17}, + {152000, 6, 27, 16}, + {152280, 6, 44, 26}, + {153000, 6, 34, 20}, + {154000, 6, 53, 31}, + {155000, 6, 31, 18}, + {155250, 6, 50, 29}, + {155750, 6, 45, 26}, + {156000, 6, 26, 15}, + {157000, 6, 61, 35}, + {157500, 6, 28, 16}, + {158000, 6, 65, 37}, + {158250, 6, 44, 25}, + {159000, 6, 53, 30}, + {159500, 6, 39, 22}, + {160000, 6, 32, 18}, + {161000, 4, 31, 26}, + {162000, 4, 18, 15}, + {162162, 4, 131, 109}, + {162500, 4, 53, 44}, + {163000, 4, 29, 24}, + {164000, 4, 17, 14}, + {165000, 4, 22, 18}, + {166000, 4, 32, 26}, + {167000, 4, 26, 21}, + {168000, 4, 46, 37}, + {169000, 4, 104, 83}, + {169128, 4, 64, 51}, + {169500, 4, 39, 31}, + {170000, 4, 34, 27}, + {171000, 4, 19, 15}, + {172000, 4, 51, 40}, + {172750, 4, 32, 25}, + {172800, 4, 32, 25}, + {173000, 4, 41, 32}, + {174000, 4, 49, 38}, + {174787, 4, 22, 17}, + {175000, 4, 35, 27}, + {176000, 4, 30, 23}, + {177000, 4, 38, 29}, + {178000, 4, 29, 22}, + {178500, 4, 37, 28}, + {179000, 4, 53, 40}, + {179500, 4, 73, 55}, + {180000, 4, 20, 15}, + {181000, 4, 55, 41}, + {182000, 4, 31, 23}, + {183000, 4, 42, 31}, + {184000, 4, 30, 22}, + {184750, 4, 26, 19}, + {185000, 4, 37, 27}, + {186000, 4, 51, 37}, + {187000, 4, 36, 26}, + {188000, 4, 32, 23}, + {189000, 4, 21, 15}, + {190000, 4, 38, 27}, + {190960, 4, 41, 29}, + {191000, 4, 41, 29}, + {192000, 4, 27, 19}, + {192250, 4, 37, 26}, + {193000, 4, 20, 14}, + {193250, 4, 53, 37}, + {194000, 4, 23, 16}, + {194208, 4, 23, 16}, + {195000, 4, 26, 18}, + {196000, 4, 45, 31}, + {197000, 4, 35, 24}, + {197750, 4, 41, 28}, + {198000, 4, 22, 15}, + {198500, 4, 25, 17}, + {199000, 4, 28, 19}, + {200000, 4, 37, 25}, + {201000, 4, 61, 41}, + {202000, 4, 112, 75}, + {202500, 4, 21, 14}, + {203000, 4, 146, 97}, + {204000, 4, 62, 41}, + {204750, 4, 44, 29}, + {205000, 4, 38, 25}, + {206000, 4, 29, 19}, + {207000, 4, 23, 15}, + {207500, 4, 40, 26}, + {208000, 4, 37, 24}, + {208900, 4, 48, 31}, + {209000, 4, 48, 31}, + {209250, 4, 31, 20}, + {210000, 4, 28, 18}, + {211000, 4, 25, 16}, + {212000, 4, 22, 14}, + {213000, 4, 30, 19}, + {213750, 4, 38, 24}, + {214000, 4, 46, 29}, + {214750, 4, 35, 22}, + {215000, 4, 43, 27}, + {216000, 4, 24, 15}, + {217000, 4, 37, 23}, + {218000, 4, 42, 26}, + {218250, 4, 42, 26}, + {218750, 4, 34, 21}, + {219000, 4, 47, 29}, + {219000, 4, 47, 29}, + {220000, 4, 44, 27}, + {220640, 4, 49, 30}, + {220750, 4, 36, 22}, + {221000, 4, 36, 22}, + {222000, 4, 23, 14}, + {222525, 4, 28, 17}, + {222750, 4, 33, 20}, + {227000, 4, 37, 22}, + {230250, 4, 29, 17}, + {233500, 4, 38, 22}, + {235000, 4, 40, 23}, + {238000, 4, 30, 17}, + {241500, 2, 17, 19}, + {245250, 2, 20, 22}, + {247750, 2, 22, 24}, + {253250, 2, 15, 16}, + {256250, 2, 18, 19}, + {262500, 2, 31, 32}, + {267250, 2, 66, 67}, + {268500, 2, 94, 95}, + {270000, 2, 14, 14}, + {272500, 2, 77, 76}, + {273750, 2, 57, 56}, + {280750, 2, 24, 23}, + {281250, 2, 23, 22}, + {286000, 2, 17, 16}, + {291750, 2, 26, 24}, + {296703, 2, 56, 51}, + {297000, 2, 22, 20}, + {298000, 2, 21, 19}, +}; From f5bbfca3e5aedc7e7b299b48b8ec2509b1052acf Mon Sep 17 00:00:00 2001 From: Eugeni Dodonov Date: Wed, 9 May 2012 15:37:30 -0300 Subject: [PATCH 43/57] drm/i915: move HDMI structs to shared location Move intel_hdmi data structure and support functions to a shared location, to allow their usage from intel_ddi module. Reviewed-by: Jesse Barnes Signed-off-by: Eugeni Dodonov Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_drv.h | 19 ++++++++++++++++++- drivers/gpu/drm/i915/intel_hdmi.c | 19 +++---------------- 2 files changed, 21 insertions(+), 17 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index fd96ffbf501..afdd74f57d9 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -290,6 +290,19 @@ struct dip_infoframe { } __attribute__ ((packed)) body; } __attribute__((packed)); +struct intel_hdmi { + struct intel_encoder base; + u32 sdvox_reg; + int ddc_bus; + int ddi_port; + uint32_t color_range; + bool has_hdmi_sink; + bool has_audio; + enum hdmi_force_audio force_audio; + void (*write_infoframe)(struct drm_encoder *encoder, + struct dip_infoframe *frame); +}; + static inline struct drm_crtc * intel_get_crtc_for_pipe(struct drm_device *dev, int pipe) { @@ -329,7 +342,11 @@ extern void intel_attach_broadcast_rgb_property(struct drm_connector *connector) extern void intel_crt_init(struct drm_device *dev); extern void intel_hdmi_init(struct drm_device *dev, int sdvox_reg); -void intel_dip_infoframe_csum(struct dip_infoframe *avi_if); +extern struct intel_hdmi *enc_to_intel_hdmi(struct drm_encoder *encoder); +extern void intel_hdmi_set_avi_infoframe(struct drm_encoder *encoder, + struct drm_display_mode *adjusted_mode); +extern void intel_hdmi_set_spd_infoframe(struct drm_encoder *encoder); +extern void intel_dip_infoframe_csum(struct dip_infoframe *avi_if); extern bool intel_sdvo_init(struct drm_device *dev, uint32_t sdvo_reg, bool is_sdvob); extern void intel_dvo_init(struct drm_device *dev); diff --git a/drivers/gpu/drm/i915/intel_hdmi.c b/drivers/gpu/drm/i915/intel_hdmi.c index 3b0cf719725..a7871b18c20 100644 --- a/drivers/gpu/drm/i915/intel_hdmi.c +++ b/drivers/gpu/drm/i915/intel_hdmi.c @@ -37,20 +37,7 @@ #include "i915_drm.h" #include "i915_drv.h" -struct intel_hdmi { - struct intel_encoder base; - u32 sdvox_reg; - int ddc_bus; - int ddi_port; - uint32_t color_range; - bool has_hdmi_sink; - bool has_audio; - enum hdmi_force_audio 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) +struct intel_hdmi *enc_to_intel_hdmi(struct drm_encoder *encoder) { return container_of(encoder, struct intel_hdmi, base.base); } @@ -302,7 +289,7 @@ static void intel_set_infoframe(struct drm_encoder *encoder, intel_hdmi->write_infoframe(encoder, frame); } -static void intel_hdmi_set_avi_infoframe(struct drm_encoder *encoder, +void intel_hdmi_set_avi_infoframe(struct drm_encoder *encoder, struct drm_display_mode *adjusted_mode) { struct dip_infoframe avi_if = { @@ -317,7 +304,7 @@ static void intel_hdmi_set_avi_infoframe(struct drm_encoder *encoder, intel_set_infoframe(encoder, &avi_if); } -static void intel_hdmi_set_spd_infoframe(struct drm_encoder *encoder) +void intel_hdmi_set_spd_infoframe(struct drm_encoder *encoder) { struct dip_infoframe spd_if; From 72662e103c7dd305725dcf4aabcbd8f69483dfbc Mon Sep 17 00:00:00 2001 From: Eugeni Dodonov Date: Wed, 9 May 2012 15:37:31 -0300 Subject: [PATCH 44/57] drm/i915: prepare HDMI link for Haswell On Haswell, we need to properly train the DDI buffers prior to enabling HDMI, and enable the required clocks with correct dividers for the desired frequency. Also, we cannot simple reuse HDMI routines from previous generations of GPU, as most of HDMI-specific stuff is being done via the DDI port programming instead of HDMI-specific registers. This commit take advantage of the WR PLL clock table which is in a separate (previous) commit to select the right divisors for each mode. Signed-off-by: Eugeni Dodonov Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_ddi.c | 116 ++++++++++++++++++++++++++++++ drivers/gpu/drm/i915/intel_drv.h | 5 ++ drivers/gpu/drm/i915/intel_hdmi.c | 13 +++- 3 files changed, 133 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/i915/intel_ddi.c b/drivers/gpu/drm/i915/intel_ddi.c index d4b268e5056..46d1e886c69 100644 --- a/drivers/gpu/drm/i915/intel_ddi.c +++ b/drivers/gpu/drm/i915/intel_ddi.c @@ -637,3 +637,119 @@ static const struct wrpll_tmds_clock wrpll_tmds_clock_table[] = { {297000, 2, 22, 20}, {298000, 2, 21, 19}, }; + +void intel_ddi_mode_set(struct drm_encoder *encoder, + struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + struct drm_device *dev = encoder->dev; + struct drm_i915_private *dev_priv = dev->dev_private; + struct drm_crtc *crtc = encoder->crtc; + struct intel_crtc *intel_crtc = to_intel_crtc(crtc); + struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(encoder); + int port = intel_hdmi->ddi_port; + int pipe = intel_crtc->pipe; + int p, n2, r2, valid=0; + u32 temp, i; + + /* On Haswell, we need to enable the clocks and prepare DDI function to + * work in HDMI mode for this pipe. + */ + DRM_DEBUG_KMS("Preparing HDMI DDI mode for Haswell on port %c, pipe %c\n", port_name(port), pipe_name(pipe)); + + for (i=0; i < ARRAY_SIZE(wrpll_tmds_clock_table); i++) { + if (crtc->mode.clock == wrpll_tmds_clock_table[i].clock) { + p = wrpll_tmds_clock_table[i].p; + n2 = wrpll_tmds_clock_table[i].n2; + r2 = wrpll_tmds_clock_table[i].r2; + + DRM_DEBUG_KMS("WR PLL clock: found settings for %dKHz refresh rate: p=%d, n2=%d, r2=%d\n", + crtc->mode.clock, + p, n2, r2); + + valid = 1; + break; + } + } + + if (!valid) { + DRM_ERROR("Unable to find WR PLL clock settings for %dKHz refresh rate\n", + crtc->mode.clock); + return; + } + + /* Enable LCPLL if disabled */ + temp = I915_READ(LCPLL_CTL); + if (temp & LCPLL_PLL_DISABLE) + I915_WRITE(LCPLL_CTL, + temp & ~LCPLL_PLL_DISABLE); + + /* Configure WR PLL 1, program the correct divider values for + * the desired frequency and wait for warmup */ + I915_WRITE(WRPLL_CTL1, + WRPLL_PLL_ENABLE | + WRPLL_PLL_SELECT_LCPLL_2700 | + WRPLL_DIVIDER_REFERENCE(r2) | + WRPLL_DIVIDER_FEEDBACK(n2) | + WRPLL_DIVIDER_POST(p)); + + udelay(20); + + /* Use WRPLL1 clock to drive the output to the port, and tell the pipe to use + * this port for connection. + */ + I915_WRITE(PORT_CLK_SEL(port), + PORT_CLK_SEL_WRPLL1); + I915_WRITE(PIPE_CLK_SEL(pipe), + PIPE_CLK_SEL_PORT(port)); + + udelay(20); + + if (intel_hdmi->has_audio) { + /* Proper support for digital audio needs a new logic and a new set + * of registers, so we leave it for future patch bombing. + */ + DRM_DEBUG_DRIVER("HDMI audio on pipe %c not yet supported on DDI\n", + pipe_name(intel_crtc->pipe)); + } + + /* Enable PIPE_DDI_FUNC_CTL for the pipe to work in HDMI mode */ + temp = I915_READ(DDI_FUNC_CTL(pipe)); + temp &= ~PIPE_DDI_PORT_MASK; + temp &= ~PIPE_DDI_BPC_12; + temp |= PIPE_DDI_SELECT_PORT(port) | + PIPE_DDI_MODE_SELECT_HDMI | + ((intel_crtc->bpp > 24) ? + PIPE_DDI_BPC_12 : + PIPE_DDI_BPC_8) | + PIPE_DDI_FUNC_ENABLE; + + I915_WRITE(DDI_FUNC_CTL(pipe), temp); + + intel_hdmi_set_avi_infoframe(encoder, adjusted_mode); + intel_hdmi_set_spd_infoframe(encoder); +} + +void intel_ddi_dpms(struct drm_encoder *encoder, int mode) +{ + 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); + int port = intel_hdmi->ddi_port; + u32 temp; + + temp = I915_READ(DDI_BUF_CTL(port)); + + if (mode != DRM_MODE_DPMS_ON) { + temp &= ~DDI_BUF_CTL_ENABLE; + } else { + temp |= DDI_BUF_CTL_ENABLE; + } + + /* Enable DDI_BUF_CTL. In HDMI/DVI mode, the port width, + * and swing/emphasis values are ignored so nothing special needs + * to be done besides enabling the port. + */ + I915_WRITE(DDI_BUF_CTL(port), + temp); +} diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index afdd74f57d9..d51f27faa20 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -497,4 +497,9 @@ extern void gen6_update_ring_freq(struct drm_i915_private *dev_priv); extern void gen6_disable_rps(struct drm_device *dev); extern void intel_init_emon(struct drm_device *dev); +extern void intel_ddi_dpms(struct drm_encoder *encoder, int mode); +extern void intel_ddi_mode_set(struct drm_encoder *encoder, + struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode); + #endif /* __INTEL_DRV_H__ */ diff --git a/drivers/gpu/drm/i915/intel_hdmi.c b/drivers/gpu/drm/i915/intel_hdmi.c index a7871b18c20..03b3524a6ba 100644 --- a/drivers/gpu/drm/i915/intel_hdmi.c +++ b/drivers/gpu/drm/i915/intel_hdmi.c @@ -560,6 +560,14 @@ static void intel_hdmi_destroy(struct drm_connector *connector) kfree(connector); } +static const struct drm_encoder_helper_funcs intel_hdmi_helper_funcs_hsw = { + .dpms = intel_ddi_dpms, + .mode_fixup = intel_hdmi_mode_fixup, + .prepare = intel_encoder_prepare, + .mode_set = intel_ddi_mode_set, + .commit = intel_encoder_commit, +}; + static const struct drm_encoder_helper_funcs intel_hdmi_helper_funcs = { .dpms = intel_hdmi_dpms, .mode_fixup = intel_hdmi_mode_fixup, @@ -699,7 +707,10 @@ void intel_hdmi_init(struct drm_device *dev, int sdvox_reg) I915_WRITE(TVIDEO_DIP_CTL(i), 0); } - drm_encoder_helper_add(&intel_encoder->base, &intel_hdmi_helper_funcs); + if (IS_HASWELL(dev)) + drm_encoder_helper_add(&intel_encoder->base, &intel_hdmi_helper_funcs_hsw); + else + drm_encoder_helper_add(&intel_encoder->base, &intel_hdmi_helper_funcs); intel_hdmi_add_properties(intel_hdmi, connector); From c14f52860e9e8a6e6db65b7d10dfea7c8f82aa1e Mon Sep 17 00:00:00 2001 From: Eugeni Dodonov Date: Wed, 9 May 2012 15:37:32 -0300 Subject: [PATCH 45/57] drm/i915: hook Haswell devices in place This patch enables i915 driver to handle Haswell devices. It should go in last, when things are working stable enough. Signed-off-by: Eugeni Dodonov Signed-off-by: Daniel Vetter --- drivers/char/agp/intel-agp.c | 4 ++++ drivers/gpu/drm/i915/i915_drv.c | 7 +++++++ 2 files changed, 11 insertions(+) diff --git a/drivers/char/agp/intel-agp.c b/drivers/char/agp/intel-agp.c index 74c2d9274c5..764f70c5e69 100644 --- a/drivers/char/agp/intel-agp.c +++ b/drivers/char/agp/intel-agp.c @@ -908,6 +908,10 @@ static struct pci_device_id agp_intel_pci_table[] = { ID(PCI_DEVICE_ID_INTEL_IVYBRIDGE_M_HB), ID(PCI_DEVICE_ID_INTEL_IVYBRIDGE_S_HB), ID(PCI_DEVICE_ID_INTEL_VALLEYVIEW_HB), + ID(PCI_DEVICE_ID_INTEL_HASWELL_HB), + ID(PCI_DEVICE_ID_INTEL_HASWELL_M_HB), + ID(PCI_DEVICE_ID_INTEL_HASWELL_S_HB), + ID(PCI_DEVICE_ID_INTEL_HASWELL_E_HB), { } }; diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c index 88d32908892..f0763f29aef 100644 --- a/drivers/gpu/drm/i915/i915_drv.c +++ b/drivers/gpu/drm/i915/i915_drv.c @@ -345,6 +345,13 @@ static const struct pci_device_id pciidlist[] = { /* aka */ INTEL_VGA_DEVICE(0x0162, &intel_ivybridge_d_info), /* GT2 desktop */ INTEL_VGA_DEVICE(0x015a, &intel_ivybridge_d_info), /* GT1 server */ INTEL_VGA_DEVICE(0x016a, &intel_ivybridge_d_info), /* GT2 server */ + INTEL_VGA_DEVICE(0x0402, &intel_haswell_d_info), /* GT1 desktop */ + INTEL_VGA_DEVICE(0x0412, &intel_haswell_d_info), /* GT2 desktop */ + INTEL_VGA_DEVICE(0x040a, &intel_haswell_d_info), /* GT1 server */ + INTEL_VGA_DEVICE(0x041a, &intel_haswell_d_info), /* GT2 server */ + INTEL_VGA_DEVICE(0x0406, &intel_haswell_m_info), /* GT1 mobile */ + INTEL_VGA_DEVICE(0x0416, &intel_haswell_m_info), /* GT2 mobile */ + INTEL_VGA_DEVICE(0x0c16, &intel_haswell_d_info), /* SDV */ {0, 0, 0} }; From e7e164db6db292d6f804ec6cd25f29ff62dd0618 Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Fri, 11 May 2012 09:21:25 +0100 Subject: [PATCH 46/57] drm/i915: Assert that the transcoder is indeed off before modifying it Inspired by a recent regression that seems to confuse pch transcoder state, let's be a bit more paranoid. References: https://bugs.freedesktop.org/show_bug.cgi?id=49712 Cc: Jesse Barnes Signed-off-by: Chris Wilson [danvet: Pimped commit message.] Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_display.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 1a06f97e73f..391439fa17b 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -2782,6 +2782,8 @@ static void ironlake_pch_enable(struct drm_crtc *crtc) int pipe = intel_crtc->pipe; u32 reg, temp; + assert_transcoder_disabled(dev_priv, pipe); + /* For PCH output, training FDI link */ dev_priv->display.fdi_link_train(crtc); From b4519513e8ca3bd82eabff9874d69166b58b6db9 Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Fri, 11 May 2012 14:29:30 +0100 Subject: [PATCH 47/57] drm/i915: Introduce for_each_ring() macro In many places we wish to iterate over the rings associated with the GPU, so refactor them to use a common macro. Along the way, there are a few code removals that should be side-effect free and some rearrangement which should only have a cosmetic impact, such as error-state. Note that this slightly changes the semantics in the hangcheck code: We now always cycle through all enabled rings instead of short-circuiting the logic. v2: Pull in a couple of suggestions from Ben and Daniel for intel_ring_initialized() and not removing the warning (just moving them to a new home, closer to the error). Signed-off-by: Chris Wilson Reviewed-by: Ben Widawsky [danvet: Added note to commit message about the small behaviour change, suggested by Ben Widawsky.] Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_debugfs.c | 9 ++-- drivers/gpu/drm/i915/i915_drv.c | 10 ++-- drivers/gpu/drm/i915/i915_drv.h | 9 ++-- drivers/gpu/drm/i915/i915_gem.c | 39 +++++++------- drivers/gpu/drm/i915/i915_gem_evict.c | 14 ++--- drivers/gpu/drm/i915/i915_irq.c | 69 ++++++++++--------------- drivers/gpu/drm/i915/intel_pm.c | 5 +- drivers/gpu/drm/i915/intel_ringbuffer.h | 6 +++ 8 files changed, 76 insertions(+), 85 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_debugfs.c b/drivers/gpu/drm/i915/i915_debugfs.c index 950f72a0d72..eb2b3c25b9e 100644 --- a/drivers/gpu/drm/i915/i915_debugfs.c +++ b/drivers/gpu/drm/i915/i915_debugfs.c @@ -699,6 +699,7 @@ static int i915_error_state(struct seq_file *m, void *unused) struct drm_device *dev = error_priv->dev; drm_i915_private_t *dev_priv = dev->dev_private; struct drm_i915_error_state *error = error_priv->error; + struct intel_ring_buffer *ring; int i, j, page, offset, elt; if (!error) { @@ -706,7 +707,6 @@ static int i915_error_state(struct seq_file *m, void *unused) return 0; } - seq_printf(m, "Time: %ld s %ld us\n", error->time.tv_sec, error->time.tv_usec); seq_printf(m, "PCI ID: 0x%04x\n", dev->pci_device); @@ -722,11 +722,8 @@ static int i915_error_state(struct seq_file *m, void *unused) seq_printf(m, "DONE_REG: 0x%08x\n", error->done_reg); } - i915_ring_error_state(m, dev, error, RCS); - if (HAS_BLT(dev)) - i915_ring_error_state(m, dev, error, BCS); - if (HAS_BSD(dev)) - i915_ring_error_state(m, dev, error, VCS); + for_each_ring(ring, dev_priv, i) + i915_ring_error_state(m, dev, error, i); if (error->active_bo) print_error_buffers(m, "Active", diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c index f0763f29aef..d3e19485306 100644 --- a/drivers/gpu/drm/i915/i915_drv.c +++ b/drivers/gpu/drm/i915/i915_drv.c @@ -893,15 +893,15 @@ int i915_reset(struct drm_device *dev) */ if (drm_core_check_feature(dev, DRIVER_MODESET) || !dev_priv->mm.suspended) { + struct intel_ring_buffer *ring; + int i; + dev_priv->mm.suspended = 0; i915_gem_init_swizzling(dev); - dev_priv->ring[RCS].init(&dev_priv->ring[RCS]); - if (HAS_BSD(dev)) - dev_priv->ring[VCS].init(&dev_priv->ring[VCS]); - if (HAS_BLT(dev)) - dev_priv->ring[BCS].init(&dev_priv->ring[BCS]); + for_each_ring(ring, dev_priv, i) + ring->init(ring); i915_gem_init_ppgtt(dev); diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 83a557c7bcc..11c7a6a330c 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -410,9 +410,7 @@ typedef struct drm_i915_private { #define DRM_I915_HANGCHECK_PERIOD 1500 /* in ms */ struct timer_list hangcheck_timer; int hangcheck_count; - uint32_t last_acthd; - uint32_t last_acthd_bsd; - uint32_t last_acthd_blt; + uint32_t last_acthd[I915_NUM_RINGS]; uint32_t last_instdone; uint32_t last_instdone1; @@ -820,6 +818,11 @@ typedef struct drm_i915_private { struct drm_property *force_audio_property; } drm_i915_private_t; +/* Iterate over initialised rings */ +#define for_each_ring(ring__, dev_priv__, i__) \ + for ((i__) = 0; (i__) < I915_NUM_RINGS; (i__)++) \ + if (((ring__) = &(dev_priv__)->ring[(i__)]), intel_ring_initialized((ring__))) + enum hdmi_force_audio { HDMI_AUDIO_OFF_DVI = -2, /* no aux data for HDMI-DVI converter */ HDMI_AUDIO_OFF, /* force turn off HDMI audio */ diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index 44a5f241b1a..6d2180cf3da 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -1655,10 +1655,11 @@ void i915_gem_reset(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; struct drm_i915_gem_object *obj; + struct intel_ring_buffer *ring; int i; - for (i = 0; i < I915_NUM_RINGS; i++) - i915_gem_reset_ring_lists(dev_priv, &dev_priv->ring[i]); + for_each_ring(ring, dev_priv, i) + i915_gem_reset_ring_lists(dev_priv, ring); /* Remove anything from the flushing lists. The GPU cache is likely * to be lost on reset along with the data, so simply move the @@ -1763,10 +1764,11 @@ void i915_gem_retire_requests(struct drm_device *dev) { drm_i915_private_t *dev_priv = dev->dev_private; + struct intel_ring_buffer *ring; int i; - for (i = 0; i < I915_NUM_RINGS; i++) - i915_gem_retire_requests_ring(&dev_priv->ring[i]); + for_each_ring(ring, dev_priv, i) + i915_gem_retire_requests_ring(ring); } static void @@ -1774,6 +1776,7 @@ i915_gem_retire_work_handler(struct work_struct *work) { drm_i915_private_t *dev_priv; struct drm_device *dev; + struct intel_ring_buffer *ring; bool idle; int i; @@ -1793,9 +1796,7 @@ i915_gem_retire_work_handler(struct work_struct *work) * objects indefinitely. */ idle = true; - for (i = 0; i < I915_NUM_RINGS; i++) { - struct intel_ring_buffer *ring = &dev_priv->ring[i]; - + for_each_ring(ring, dev_priv, i) { if (!list_empty(&ring->gpu_write_list)) { struct drm_i915_gem_request *request; int ret; @@ -2137,13 +2138,18 @@ static int i915_ring_idle(struct intel_ring_buffer *ring) int i915_gpu_idle(struct drm_device *dev) { drm_i915_private_t *dev_priv = dev->dev_private; + struct intel_ring_buffer *ring; int ret, i; /* Flush everything onto the inactive list. */ - for (i = 0; i < I915_NUM_RINGS; i++) { - ret = i915_ring_idle(&dev_priv->ring[i]); + for_each_ring(ring, dev_priv, i) { + ret = i915_ring_idle(ring); if (ret) return ret; + + /* Is the device fubar? */ + if (WARN_ON(!list_empty(&ring->gpu_write_list))) + return -EBUSY; } return 0; @@ -3463,9 +3469,7 @@ void i915_gem_init_ppgtt(struct drm_device *dev) /* GFX_MODE is per-ring on gen7+ */ } - for (i = 0; i < I915_NUM_RINGS; i++) { - ring = &dev_priv->ring[i]; - + for_each_ring(ring, dev_priv, i) { if (INTEL_INFO(dev)->gen >= 7) I915_WRITE(RING_MODE_GEN7(ring), _MASKED_BIT_ENABLE(GFX_PPGTT_ENABLE)); @@ -3581,10 +3585,11 @@ void i915_gem_cleanup_ringbuffer(struct drm_device *dev) { drm_i915_private_t *dev_priv = dev->dev_private; + struct intel_ring_buffer *ring; int i; - for (i = 0; i < I915_NUM_RINGS; i++) - intel_cleanup_ring_buffer(&dev_priv->ring[i]); + for_each_ring(ring, dev_priv, i) + intel_cleanup_ring_buffer(ring); } int @@ -3592,7 +3597,7 @@ i915_gem_entervt_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv) { drm_i915_private_t *dev_priv = dev->dev_private; - int ret, i; + int ret; if (drm_core_check_feature(dev, DRIVER_MODESET)) return 0; @@ -3614,10 +3619,6 @@ i915_gem_entervt_ioctl(struct drm_device *dev, void *data, BUG_ON(!list_empty(&dev_priv->mm.active_list)); BUG_ON(!list_empty(&dev_priv->mm.flushing_list)); BUG_ON(!list_empty(&dev_priv->mm.inactive_list)); - for (i = 0; i < I915_NUM_RINGS; i++) { - BUG_ON(!list_empty(&dev_priv->ring[i].active_list)); - BUG_ON(!list_empty(&dev_priv->ring[i].request_list)); - } mutex_unlock(&dev->struct_mutex); ret = drm_irq_install(dev); diff --git a/drivers/gpu/drm/i915/i915_gem_evict.c b/drivers/gpu/drm/i915/i915_gem_evict.c index 3bcf0451d07..ae7c24e12e5 100644 --- a/drivers/gpu/drm/i915/i915_gem_evict.c +++ b/drivers/gpu/drm/i915/i915_gem_evict.c @@ -168,7 +168,7 @@ i915_gem_evict_everything(struct drm_device *dev, bool purgeable_only) drm_i915_private_t *dev_priv = dev->dev_private; struct drm_i915_gem_object *obj, *next; bool lists_empty; - int ret,i; + int ret; lists_empty = (list_empty(&dev_priv->mm.inactive_list) && list_empty(&dev_priv->mm.flushing_list) && @@ -178,17 +178,13 @@ i915_gem_evict_everything(struct drm_device *dev, bool purgeable_only) trace_i915_gem_evict_everything(dev, purgeable_only); - ret = i915_gpu_idle(dev); - if (ret) - return ret; - /* The gpu_idle will flush everything in the write domain to the * active list. Then we must move everything off the active list * with retire requests. */ - for (i = 0; i < I915_NUM_RINGS; i++) - if (WARN_ON(!list_empty(&dev_priv->ring[i].gpu_write_list))) - return -EBUSY; + ret = i915_gpu_idle(dev); + if (ret) + return ret; i915_gem_retire_requests(dev); @@ -203,5 +199,5 @@ i915_gem_evict_everything(struct drm_device *dev, bool purgeable_only) } } - return ret; + return 0; } diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index 2a062d778ff..cc4a6330761 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -1022,15 +1022,11 @@ static void i915_gem_record_rings(struct drm_device *dev, struct drm_i915_error_state *error) { struct drm_i915_private *dev_priv = dev->dev_private; + struct intel_ring_buffer *ring; struct drm_i915_gem_request *request; int i, count; - for (i = 0; i < I915_NUM_RINGS; i++) { - struct intel_ring_buffer *ring = &dev_priv->ring[i]; - - if (ring->obj == NULL) - continue; - + for_each_ring(ring, dev_priv, i) { i915_record_ring_state(dev, error, ring); error->ring[i].batchbuffer = @@ -1295,6 +1291,8 @@ static void i915_report_and_clear_eir(struct drm_device *dev) void i915_handle_error(struct drm_device *dev, bool wedged) { struct drm_i915_private *dev_priv = dev->dev_private; + struct intel_ring_buffer *ring; + int i; i915_capture_error_state(dev); i915_report_and_clear_eir(dev); @@ -1306,11 +1304,8 @@ void i915_handle_error(struct drm_device *dev, bool wedged) /* * Wakeup waiting processes so they don't hang */ - wake_up_all(&dev_priv->ring[RCS].irq_queue); - if (HAS_BSD(dev)) - wake_up_all(&dev_priv->ring[VCS].irq_queue); - if (HAS_BLT(dev)) - wake_up_all(&dev_priv->ring[BCS].irq_queue); + for_each_ring(ring, dev_priv, i) + wake_up_all(&ring->irq_queue); } queue_work(dev_priv->wq, &dev_priv->error_work); @@ -1515,11 +1510,6 @@ ring_last_seqno(struct intel_ring_buffer *ring) static bool i915_hangcheck_ring_idle(struct intel_ring_buffer *ring, bool *err) { - /* We don't check whether the ring even exists before calling this - * function. Hence check whether it's initialized. */ - if (ring->obj == NULL) - return true; - if (list_empty(&ring->request_list) || i915_seqno_passed(ring->get_seqno(ring), ring_last_seqno(ring))) { /* Issue a wake-up to catch stuck h/w. */ @@ -1553,26 +1543,25 @@ static bool i915_hangcheck_hung(struct drm_device *dev) drm_i915_private_t *dev_priv = dev->dev_private; if (dev_priv->hangcheck_count++ > 1) { + bool hung = true; + DRM_ERROR("Hangcheck timer elapsed... GPU hung\n"); i915_handle_error(dev, true); if (!IS_GEN2(dev)) { + struct intel_ring_buffer *ring; + int i; + /* Is the chip hanging on a WAIT_FOR_EVENT? * If so we can simply poke the RB_WAIT bit * and break the hang. This should work on * all but the second generation chipsets. */ - if (kick_ring(&dev_priv->ring[RCS])) - return false; - - if (HAS_BSD(dev) && kick_ring(&dev_priv->ring[VCS])) - return false; - - if (HAS_BLT(dev) && kick_ring(&dev_priv->ring[BCS])) - return false; + for_each_ring(ring, dev_priv, i) + hung &= !kick_ring(ring); } - return true; + return hung; } return false; @@ -1588,16 +1577,23 @@ void i915_hangcheck_elapsed(unsigned long data) { struct drm_device *dev = (struct drm_device *)data; drm_i915_private_t *dev_priv = dev->dev_private; - uint32_t acthd, instdone, instdone1, acthd_bsd, acthd_blt; - bool err = false; + uint32_t acthd[I915_NUM_RINGS], instdone, instdone1; + struct intel_ring_buffer *ring; + bool err = false, idle; + int i; if (!i915_enable_hangcheck) return; + memset(acthd, 0, sizeof(acthd)); + idle = true; + for_each_ring(ring, dev_priv, i) { + idle &= i915_hangcheck_ring_idle(ring, &err); + acthd[i] = intel_ring_get_active_head(ring); + } + /* If all work is done then ACTHD clearly hasn't advanced. */ - if (i915_hangcheck_ring_idle(&dev_priv->ring[RCS], &err) && - i915_hangcheck_ring_idle(&dev_priv->ring[VCS], &err) && - i915_hangcheck_ring_idle(&dev_priv->ring[BCS], &err)) { + if (idle) { if (err) { if (i915_hangcheck_hung(dev)) return; @@ -1616,15 +1612,8 @@ void i915_hangcheck_elapsed(unsigned long data) instdone = I915_READ(INSTDONE_I965); instdone1 = I915_READ(INSTDONE1); } - acthd = intel_ring_get_active_head(&dev_priv->ring[RCS]); - acthd_bsd = HAS_BSD(dev) ? - intel_ring_get_active_head(&dev_priv->ring[VCS]) : 0; - acthd_blt = HAS_BLT(dev) ? - intel_ring_get_active_head(&dev_priv->ring[BCS]) : 0; - if (dev_priv->last_acthd == acthd && - dev_priv->last_acthd_bsd == acthd_bsd && - dev_priv->last_acthd_blt == acthd_blt && + if (memcmp(dev_priv->last_acthd, acthd, sizeof(acthd)) == 0 && dev_priv->last_instdone == instdone && dev_priv->last_instdone1 == instdone1) { if (i915_hangcheck_hung(dev)) @@ -1632,9 +1621,7 @@ void i915_hangcheck_elapsed(unsigned long data) } else { dev_priv->hangcheck_count = 0; - dev_priv->last_acthd = acthd; - dev_priv->last_acthd_bsd = acthd_bsd; - dev_priv->last_acthd_blt = acthd_blt; + memcpy(dev_priv->last_acthd, acthd, sizeof(acthd)); dev_priv->last_instdone = instdone; dev_priv->last_instdone1 = instdone1; } diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c index 8f8d1daf1ca..8e79ff67ec9 100644 --- a/drivers/gpu/drm/i915/intel_pm.c +++ b/drivers/gpu/drm/i915/intel_pm.c @@ -2326,6 +2326,7 @@ int intel_enable_rc6(const struct drm_device *dev) void gen6_enable_rps(struct drm_i915_private *dev_priv) { + struct intel_ring_buffer *ring; u32 rp_state_cap = I915_READ(GEN6_RP_STATE_CAP); u32 gt_perf_status = I915_READ(GEN6_GT_PERF_STATUS); u32 pcu_mbox, rc6_mask = 0; @@ -2360,8 +2361,8 @@ void gen6_enable_rps(struct drm_i915_private *dev_priv) I915_WRITE(GEN6_RC_EVALUATION_INTERVAL, 125000); I915_WRITE(GEN6_RC_IDLE_HYSTERSIS, 25); - for (i = 0; i < I915_NUM_RINGS; i++) - I915_WRITE(RING_MAX_IDLE(dev_priv->ring[i].mmio_base), 10); + for_each_ring(ring, dev_priv, i) + I915_WRITE(RING_MAX_IDLE(ring->mmio_base), 10); I915_WRITE(GEN6_RC_SLEEP, 0); I915_WRITE(GEN6_RC1e_THRESHOLD, 1000); diff --git a/drivers/gpu/drm/i915/intel_ringbuffer.h b/drivers/gpu/drm/i915/intel_ringbuffer.h index baba7571457..55d3da26bae 100644 --- a/drivers/gpu/drm/i915/intel_ringbuffer.h +++ b/drivers/gpu/drm/i915/intel_ringbuffer.h @@ -119,6 +119,12 @@ struct intel_ring_buffer { void *private; }; +static inline bool +intel_ring_initialized(struct intel_ring_buffer *ring) +{ + return ring->obj != NULL; +} + static inline unsigned intel_ring_flag(struct intel_ring_buffer *ring) { From a15817cf16081b140e105736c9a6a50af38540b3 Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Fri, 11 May 2012 14:29:31 +0100 Subject: [PATCH 48/57] drm/i915: Check whether the ring is initialised prior to dispatch Rather than use the magic feature tests HAS_BLT/HAS_BSD just check whether the ring we are about to dispatch the execbuffer on is initialised. v2: Use intel_ring_initialized() Signed-off-by: Chris Wilson Reviewed-by: Ben Widawsky Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_gem_execbuffer.c | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_gem_execbuffer.c b/drivers/gpu/drm/i915/i915_gem_execbuffer.c index 21fc11d8471..974a9f1068a 100644 --- a/drivers/gpu/drm/i915/i915_gem_execbuffer.c +++ b/drivers/gpu/drm/i915/i915_gem_execbuffer.c @@ -1064,17 +1064,9 @@ i915_gem_do_execbuffer(struct drm_device *dev, void *data, ring = &dev_priv->ring[RCS]; break; case I915_EXEC_BSD: - if (!HAS_BSD(dev)) { - DRM_DEBUG("execbuf with invalid ring (BSD)\n"); - return -EINVAL; - } ring = &dev_priv->ring[VCS]; break; case I915_EXEC_BLT: - if (!HAS_BLT(dev)) { - DRM_DEBUG("execbuf with invalid ring (BLT)\n"); - return -EINVAL; - } ring = &dev_priv->ring[BCS]; break; default: @@ -1082,6 +1074,11 @@ i915_gem_do_execbuffer(struct drm_device *dev, void *data, (int)(args->flags & I915_EXEC_RING_MASK)); return -EINVAL; } + if (!intel_ring_initialized(ring)) { + DRM_DEBUG("execbuf with invalid ring: %d\n", + (int)(args->flags & I915_EXEC_RING_MASK)); + return -EINVAL; + } mode = args->flags & I915_EXEC_CONSTANTS_MASK; mask = I915_EXEC_CONSTANTS_MASK; From edc912f58eaec20fd3372d9b1dab33fc28b3b3fd Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Fri, 11 May 2012 14:29:32 +0100 Subject: [PATCH 49/57] drm/i915: Replace the feature tests for BLT/BSD with ring init checks When userspace asks whether the driver supports the BLT or BSD rings for this chip, simply report whether those particular rings are initialised v2: Use intel_ring_initialized() Signed-off-by: Chris Wilson Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_dma.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_dma.c b/drivers/gpu/drm/i915/i915_dma.c index 006ea473b57..b0df2949c1b 100644 --- a/drivers/gpu/drm/i915/i915_dma.c +++ b/drivers/gpu/drm/i915/i915_dma.c @@ -980,10 +980,10 @@ static int i915_getparam(struct drm_device *dev, void *data, value = 1; break; case I915_PARAM_HAS_BSD: - value = HAS_BSD(dev); + value = intel_ring_initialized(&dev_priv->ring[VCS]); break; case I915_PARAM_HAS_BLT: - value = HAS_BLT(dev); + value = intel_ring_initialized(&dev_priv->ring[BCS]); break; case I915_PARAM_HAS_RELAXED_FENCING: value = 1; From 83ee9e645846f0c56bd9b33ee28ead03b416bb25 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Sun, 13 May 2012 14:44:20 +0200 Subject: [PATCH 50/57] drm/i915: disable gmbus on i830 The hw just returns garbage. Bugzilla: https://bugs.freedesktop.org/show_bug.cgi?id=49838 Reported-and-tested-by: Vladyslav Acked-by: Chris Wilson Signed-Off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_i2c.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/gpu/drm/i915/intel_i2c.c b/drivers/gpu/drm/i915/intel_i2c.c index e04255edc80..4a9707dd0f9 100644 --- a/drivers/gpu/drm/i915/intel_i2c.c +++ b/drivers/gpu/drm/i915/intel_i2c.c @@ -490,6 +490,10 @@ int intel_setup_gmbus(struct drm_device *dev) /* By default use a conservative clock rate */ bus->reg0 = port | GMBUS_RATE_100KHZ; + /* gmbus seems to be broken on i830 */ + if (IS_I830(dev)) + bus->force_bit = true; + intel_gpio_setup(bus, port); } From a9dcf84b14ef4e9a609910367576995e6f32f3dc Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Sun, 13 May 2012 22:29:25 +0200 Subject: [PATCH 51/57] drm/i915: don't clobber the pipe param in sanitize_modesetting ... we need it later on in the function to clean up pipe <-> plane associations. This regression has been introduced in commit f47166d2b0001fcb752b40c5a2d4db986dfbea68 Author: Chris Wilson Date: Thu Mar 22 15:00:50 2012 +0000 drm/i915: Sanitize BIOS debugging bits from PIPECONF Spotted by staring at debug output of an (as it turns out) totally unrelated bug. v2: I've totally failed to do the s/pipe/i/ correctly, spotted by Chris Wilson. Reviewed-by: Chris Wilson Reviewed-by: Eugeni Dodonov Cc: stable@kernel.org (the regression was Cc: stable, too) Signed-Off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_display.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 391439fa17b..956b22899b7 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -6253,10 +6253,11 @@ static void intel_sanitize_modesetting(struct drm_device *dev, { struct drm_i915_private *dev_priv = dev->dev_private; u32 reg, val; + int i; /* Clear any frame start delays used for debugging left by the BIOS */ - for_each_pipe(pipe) { - reg = PIPECONF(pipe); + for_each_pipe(i) { + reg = PIPECONF(i); I915_WRITE(reg, I915_READ(reg) & ~PIPECONF_FRAME_START_DELAY_MASK); } From 48da64a8bf2e00952fcd3ad108babae5e003a03d Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Sun, 13 May 2012 20:16:12 +0100 Subject: [PATCH 52/57] drm/i915: Convert BUG_ON(!pll->active) and friends to a WARN Turn a fatal lockup into a merely blank display with lots of shouty messages. v2: Whilst in the area, convert the other BUG_ON into less fatal errors. In particular, note that we may be called on a PCH platform not using PLLs, such as Haswell, and so we do not always want to BUG_ON(!pll) Signed-off-by: Chris Wilson Signed-Off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_display.c | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 956b22899b7..9dc42bf557b 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -1403,14 +1403,18 @@ out_unlock: static void intel_enable_pch_pll(struct intel_crtc *intel_crtc) { struct drm_i915_private *dev_priv = intel_crtc->base.dev->dev_private; - struct intel_pch_pll *pll = intel_crtc->pch_pll; + struct intel_pch_pll *pll; int reg; u32 val; - /* PCH only available on ILK+ */ + /* PCH PLLs only available on ILK, SNB and IVB */ BUG_ON(dev_priv->info->gen < 5); - BUG_ON(pll == NULL); - BUG_ON(pll->refcount == 0); + pll = intel_crtc->pch_pll; + if (pll == NULL) + return; + + if (WARN_ON(pll->refcount == 0)) + return; DRM_DEBUG_KMS("enable PCH PLL %x (active %d, on? %d)for crtc %d\n", pll->pll_reg, pll->active, pll->on, @@ -1448,13 +1452,18 @@ static void intel_disable_pch_pll(struct intel_crtc *intel_crtc) if (pll == NULL) return; - BUG_ON(pll->refcount == 0); + if (WARN_ON(pll->refcount == 0)) + return; DRM_DEBUG_KMS("disable PCH PLL %x (active %d, on? %d) for crtc %d\n", pll->pll_reg, pll->active, pll->on, intel_crtc->base.base.id); - BUG_ON(pll->active == 0); + if (WARN_ON(pll->active == 0)) { + assert_pch_pll_disabled(dev_priv, intel_crtc); + return; + } + if (--pll->active) { assert_pch_pll_enabled(dev_priv, intel_crtc); return; From 6f13b7b5be500178d5541b69ab911af2a77ec488 Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Sun, 13 May 2012 09:54:09 +0100 Subject: [PATCH 53/57] drm/i915: Enable the PCH PLL for all generations after link training Hidden away within one chipset specific path was the necessary logic to turn on the PLL. This needs to be done everywhere in order for us to drive any display! As such as soon as we tested on a non-CougarPoint chipset, we failed to bring up any DisplayPorts and generated a nice set of assertion failures in the process. At least one part of our logic is working, the part that assumes that we have no idea what we are doing. Reported-by: guang.a.yang@intel.com References: https://bugs.freedesktop.org/show_bug.cgi?id=49712 Signed-off-by: Chris Wilson Reviewed-by: Jesse Barnes Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_display.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 9dc42bf557b..f3d39f4f690 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -2796,14 +2796,14 @@ static void ironlake_pch_enable(struct drm_crtc *crtc) /* For PCH output, training FDI link */ dev_priv->display.fdi_link_train(crtc); + intel_enable_pch_pll(intel_crtc); + if (HAS_PCH_LPT(dev)) { DRM_DEBUG_KMS("LPT detected: programming iCLKIP\n"); lpt_program_iclkip(crtc); } else if (HAS_PCH_CPT(dev)) { u32 sel; - intel_enable_pch_pll(intel_crtc); - temp = I915_READ(PCH_DPLL_SEL); switch (pipe) { default: From 81014b9d0b55fb0b48f26cd2a943359750d532db Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Sat, 12 May 2012 20:22:00 +0200 Subject: [PATCH 54/57] drm/i915: fixup infoframe support for sdvo MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit At least the worst offenders: - SDVO specifies that the encoder should compute the ecc. Testing also shows that we must not send the ecc field, so copy the dip_infoframe struct to a temporay place and avoid the ecc field. This way the avi infoframe is exactly 17 bytes long, which agrees with what the spec mandates as a minimal storage capacity (with the ecc field it would be 18 bytes). - Only 17 when sending the avi infoframe. The SDVO spec explicitly says that sending more data than what the device announces results in undefined behaviour. - Add __attribute__((packed)) to the avi and spd infoframes, for otherwise they're wrongly aligned. Noticed because the avi infoframe ended up being 18 bytes large instead of 17. We haven't noticed this yet because we don't use the uint16_t fields yet (which are the only ones that would be wrongly aligned). This regression has been introduce by 3c17fe4b8f40a112a85758a9ab2aebf772bdd647 is the first bad commit commit 3c17fe4b8f40a112a85758a9ab2aebf772bdd647 Author: David Härdeman Date: Fri Sep 24 21:44:32 2010 +0200 i915: enable AVI infoframe for intel_hdmi.c [v4] Patch tested on my g33 with a sdvo hdmi adaptor. Bugzilla: https://bugzilla.kernel.org/show_bug.cgi?id=25732 Tested-by: Peter Ross (G35 SDVO-HDMI) Reviewed-by: Eugeni Dodonov Signed-Off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_drv.h | 4 ++-- drivers/gpu/drm/i915/intel_sdvo.c | 11 +++++++++-- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index d51f27faa20..3e0918834e7 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -280,12 +280,12 @@ struct dip_infoframe { uint16_t bottom_bar_start; uint16_t left_bar_end; uint16_t right_bar_start; - } avi; + } __attribute__ ((packed)) avi; struct { uint8_t vn[8]; uint8_t pd[16]; uint8_t sdi; - } spd; + } __attribute__ ((packed)) spd; uint8_t payload[27]; } __attribute__ ((packed)) body; } __attribute__((packed)); diff --git a/drivers/gpu/drm/i915/intel_sdvo.c b/drivers/gpu/drm/i915/intel_sdvo.c index 7d3f238e826..125228e77c5 100644 --- a/drivers/gpu/drm/i915/intel_sdvo.c +++ b/drivers/gpu/drm/i915/intel_sdvo.c @@ -887,17 +887,24 @@ static bool intel_sdvo_set_avi_infoframe(struct intel_sdvo *intel_sdvo) }; uint8_t tx_rate = SDVO_HBUF_TX_VSYNC; uint8_t set_buf_index[2] = { 1, 0 }; - uint64_t *data = (uint64_t *)&avi_if; + uint8_t sdvo_data[4 + sizeof(avi_if.body.avi)]; + uint64_t *data = (uint64_t *)sdvo_data; unsigned i; intel_dip_infoframe_csum(&avi_if); + /* sdvo spec says that the ecc is handled by the hw, and it looks like + * we must not send the ecc field, either. */ + memcpy(sdvo_data, &avi_if, 3); + sdvo_data[3] = avi_if.checksum; + memcpy(&sdvo_data[4], &avi_if.body, sizeof(avi_if.body.avi)); + if (!intel_sdvo_set_value(intel_sdvo, SDVO_CMD_SET_HBUF_INDEX, set_buf_index, 2)) return false; - for (i = 0; i < sizeof(avi_if); i += 8) { + for (i = 0; i < sizeof(sdvo_data); i += 8) { if (!intel_sdvo_set_value(intel_sdvo, SDVO_CMD_SET_HBUF_DATA, data, 8)) From ed517fbbbd3a01692a667ac18b4a413695513428 Mon Sep 17 00:00:00 2001 From: Paulo Zanoni Date: Mon, 14 May 2012 17:12:50 -0300 Subject: [PATCH 55/57] drm/i915: small hdmi coding style cleanups - Changed the coding style of auxiliary infoframe functions to make them smaller - Fixed the column alignment of some function definitions - Remove definition of "struct drm_crtc" in some places as they're used only to retrieve "struct intel_crtc" Signed-off-by: Paulo Zanoni Reviewed-by: Eugeni Dodonov Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_hdmi.c | 44 +++++++++---------------------- 1 file changed, 13 insertions(+), 31 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_hdmi.c b/drivers/gpu/drm/i915/intel_hdmi.c index 03b3524a6ba..cd5d9a9bded 100644 --- a/drivers/gpu/drm/i915/intel_hdmi.c +++ b/drivers/gpu/drm/i915/intel_hdmi.c @@ -65,40 +65,28 @@ void intel_dip_infoframe_csum(struct dip_infoframe *frame) static u32 g4x_infoframe_index(struct dip_infoframe *frame) { - u32 flags = 0; - switch (frame->type) { case DIP_TYPE_AVI: - flags |= VIDEO_DIP_SELECT_AVI; - break; + return VIDEO_DIP_SELECT_AVI; case DIP_TYPE_SPD: - flags |= VIDEO_DIP_SELECT_SPD; - break; + return VIDEO_DIP_SELECT_SPD; default: DRM_DEBUG_DRIVER("unknown info frame type %d\n", frame->type); - break; + return 0; } - - return flags; } static u32 g4x_infoframe_enable(struct dip_infoframe *frame) { - u32 flags = 0; - switch (frame->type) { case DIP_TYPE_AVI: - flags |= VIDEO_DIP_ENABLE_AVI; - break; + return VIDEO_DIP_ENABLE_AVI; case DIP_TYPE_SPD: - flags |= VIDEO_DIP_ENABLE_SPD; - break; + return VIDEO_DIP_ENABLE_SPD; default: DRM_DEBUG_DRIVER("unknown info frame type %d\n", frame->type); - break; + return 0; } - - return flags; } static void g4x_write_infoframe(struct drm_encoder *encoder, @@ -111,8 +99,6 @@ static void g4x_write_infoframe(struct drm_encoder *encoder, u32 val = I915_READ(VIDEO_DIP_CTL); unsigned i, len = DIP_HEADER_SIZE + frame->len; - - /* XXX first guess at handling video port, is this corrent? */ val &= ~VIDEO_DIP_PORT_MASK; if (intel_hdmi->sdvox_reg == SDVOB) val |= VIDEO_DIP_PORT_B; @@ -147,8 +133,7 @@ static void ibx_write_infoframe(struct drm_encoder *encoder, uint32_t *data = (uint32_t *)frame; struct drm_device *dev = encoder->dev; struct drm_i915_private *dev_priv = dev->dev_private; - struct drm_crtc *crtc = encoder->crtc; - struct intel_crtc *intel_crtc = to_intel_crtc(crtc); + struct intel_crtc *intel_crtc = to_intel_crtc(encoder->crtc); struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(encoder); int reg = TVIDEO_DIP_CTL(intel_crtc->pipe); unsigned i, len = DIP_HEADER_SIZE + frame->len; @@ -197,8 +182,7 @@ static void cpt_write_infoframe(struct drm_encoder *encoder, uint32_t *data = (uint32_t *)frame; struct drm_device *dev = encoder->dev; struct drm_i915_private *dev_priv = dev->dev_private; - struct drm_crtc *crtc = encoder->crtc; - struct intel_crtc *intel_crtc = to_intel_crtc(crtc); + struct intel_crtc *intel_crtc = to_intel_crtc(encoder->crtc); int reg = TVIDEO_DIP_CTL(intel_crtc->pipe); unsigned i, len = DIP_HEADER_SIZE + frame->len; u32 val = I915_READ(reg); @@ -237,8 +221,7 @@ static void vlv_write_infoframe(struct drm_encoder *encoder, uint32_t *data = (uint32_t *)frame; struct drm_device *dev = encoder->dev; struct drm_i915_private *dev_priv = dev->dev_private; - struct drm_crtc *crtc = encoder->crtc; - struct intel_crtc *intel_crtc = to_intel_crtc(crtc); + struct intel_crtc *intel_crtc = to_intel_crtc(encoder->crtc); int reg = VLV_TVIDEO_DIP_CTL(intel_crtc->pipe); unsigned i, len = DIP_HEADER_SIZE + frame->len; u32 val = I915_READ(reg); @@ -266,7 +249,7 @@ static void vlv_write_infoframe(struct drm_encoder *encoder, } static void hsw_write_infoframe(struct drm_encoder *encoder, - struct dip_infoframe *frame) + struct dip_infoframe *frame) { /* Not implemented yet, so avoid doing anything at all. * This is the placeholder for Paulo Zanoni's infoframe writing patch @@ -325,8 +308,7 @@ static void intel_hdmi_mode_set(struct drm_encoder *encoder, { struct drm_device *dev = encoder->dev; struct drm_i915_private *dev_priv = dev->dev_private; - struct drm_crtc *crtc = encoder->crtc; - struct intel_crtc *intel_crtc = to_intel_crtc(crtc); + struct intel_crtc *intel_crtc = to_intel_crtc(encoder->crtc); struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(encoder); u32 sdvox; @@ -500,8 +482,8 @@ intel_hdmi_detect_audio(struct drm_connector *connector) static int intel_hdmi_set_property(struct drm_connector *connector, - struct drm_property *property, - uint64_t val) + struct drm_property *property, + uint64_t val) { struct intel_hdmi *intel_hdmi = intel_attached_hdmi(connector); struct drm_i915_private *dev_priv = connector->dev->dev_private; From 2da8af54059bd8c0d7bdf1f56b39cbec7f9c6f05 Mon Sep 17 00:00:00 2001 From: Paulo Zanoni Date: Mon, 14 May 2012 17:12:51 -0300 Subject: [PATCH 56/57] drm/i915: implement hsw_write_infoframe Both the control and data registers are completely different now. Signed-off-by: Paulo Zanoni Reviewed-by: Eugeni Dodonov Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_reg.h | 4 +++ drivers/gpu/drm/i915/intel_hdmi.c | 53 ++++++++++++++++++++++++++++--- 2 files changed, 52 insertions(+), 5 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h index fb76b19e32b..2d49b9507ed 100644 --- a/drivers/gpu/drm/i915/i915_reg.h +++ b/drivers/gpu/drm/i915/i915_reg.h @@ -1697,6 +1697,7 @@ /* Video Data Island Packet control */ #define VIDEO_DIP_DATA 0x61178 #define VIDEO_DIP_CTL 0x61170 +/* Pre HSW: */ #define VIDEO_DIP_ENABLE (1 << 31) #define VIDEO_DIP_PORT_B (1 << 29) #define VIDEO_DIP_PORT_C (2 << 29) @@ -1713,6 +1714,9 @@ #define VIDEO_DIP_FREQ_VSYNC (1 << 16) #define VIDEO_DIP_FREQ_2VSYNC (2 << 16) #define VIDEO_DIP_FREQ_MASK (3 << 16) +/* HSW and later: */ +#define VIDEO_DIP_ENABLE_AVI_HSW (1 << 12) +#define VIDEO_DIP_ENABLE_SPD_HSW (1 << 0) /* Panel power sequencing */ #define PP_STATUS 0x61200 diff --git a/drivers/gpu/drm/i915/intel_hdmi.c b/drivers/gpu/drm/i915/intel_hdmi.c index cd5d9a9bded..2ead3bf7c21 100644 --- a/drivers/gpu/drm/i915/intel_hdmi.c +++ b/drivers/gpu/drm/i915/intel_hdmi.c @@ -89,6 +89,32 @@ static u32 g4x_infoframe_enable(struct dip_infoframe *frame) } } +static u32 hsw_infoframe_enable(struct dip_infoframe *frame) +{ + switch (frame->type) { + case DIP_TYPE_AVI: + return VIDEO_DIP_ENABLE_AVI_HSW; + case DIP_TYPE_SPD: + return VIDEO_DIP_ENABLE_SPD_HSW; + default: + DRM_DEBUG_DRIVER("unknown info frame type %d\n", frame->type); + return 0; + } +} + +static u32 hsw_infoframe_data_reg(struct dip_infoframe *frame, enum pipe pipe) +{ + switch (frame->type) { + case DIP_TYPE_AVI: + return HSW_TVIDEO_DIP_AVI_DATA(pipe); + case DIP_TYPE_SPD: + return HSW_TVIDEO_DIP_SPD_DATA(pipe); + default: + DRM_DEBUG_DRIVER("unknown info frame type %d\n", frame->type); + return 0; + } +} + static void g4x_write_infoframe(struct drm_encoder *encoder, struct dip_infoframe *frame) { @@ -251,13 +277,30 @@ static void vlv_write_infoframe(struct drm_encoder *encoder, static void hsw_write_infoframe(struct drm_encoder *encoder, struct dip_infoframe *frame) { - /* Not implemented yet, so avoid doing anything at all. - * This is the placeholder for Paulo Zanoni's infoframe writing patch - */ - DRM_DEBUG_DRIVER("Attempting to write infoframe on Haswell, this is not implemented yet.\n"); + uint32_t *data = (uint32_t *)frame; + struct drm_device *dev = encoder->dev; + struct drm_i915_private *dev_priv = dev->dev_private; + struct intel_crtc *intel_crtc = to_intel_crtc(encoder->crtc); + u32 ctl_reg = HSW_TVIDEO_DIP_CTL(intel_crtc->pipe); + u32 data_reg = hsw_infoframe_data_reg(frame, intel_crtc->pipe); + unsigned int i, len = DIP_HEADER_SIZE + frame->len; + u32 val = I915_READ(ctl_reg); - return; + if (data_reg == 0) + return; + intel_wait_for_vblank(dev, intel_crtc->pipe); + + val &= ~hsw_infoframe_enable(frame); + I915_WRITE(ctl_reg, val); + + for (i = 0; i < len; i += 4) { + I915_WRITE(data_reg + i, *data); + data++; + } + + val |= hsw_infoframe_enable(frame); + I915_WRITE(ctl_reg, val); } static void intel_set_infoframe(struct drm_encoder *encoder, From 98b6bd998ae057611d2bc040ace1fc847f575b84 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Sun, 20 May 2012 20:00:25 +0200 Subject: [PATCH 57/57] drm/i915: IBX has a fixed pch pll to pch pipe mapping This should fix breakage introduced in commit ee7b9f93fd96a72e5d09e2b44024c11880873c6b Author: Jesse Barnes Date: Fri Apr 20 17:11:53 2012 +0100 drm/i915: manage PCH PLLs separately from pipes v2: Add a DRM_DEBUG_KMS message to explain why a given pll was selected, suggested by Chris Wilson. v3: Actually run git add. Cc: Jesse Barnes Cc: Chris Wilson Reviewed-by: Chris Wilson Bugzilla: https://bugs.freedesktop.org/show_bug.cgi?id=49712 Signed-Off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_display.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index f3d39f4f690..3c71850ddf2 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -2911,6 +2911,17 @@ static struct intel_pch_pll *intel_get_pch_pll(struct intel_crtc *intel_crtc, u3 goto prepare; } + if (HAS_PCH_IBX(dev_priv->dev)) { + /* Ironlake PCH has a fixed PLL->PCH pipe mapping. */ + i = intel_crtc->pipe; + pll = &dev_priv->pch_plls[i]; + + DRM_DEBUG_KMS("CRTC:%d using pre-allocated PCH PLL %x\n", + intel_crtc->base.base.id, pll->pll_reg); + + goto found; + } + for (i = 0; i < dev_priv->num_pch_pll; i++) { pll = &dev_priv->pch_plls[i];