From 08d644add0e5f799a47dbe7849401606c522e59e Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Thu, 12 Jul 2012 20:19:59 +0200 Subject: drm/i915: add port parameter to intel_hdmi_init Instead of having a giant if cascade to figure this out according to the passed-in register. We could do quite a bit more cleaning up and all by using the port at more places, but I think this should be part of a bigger rework to introduce a struct intel_digital_port which would keep track of all these things. I guess this will be part of some haswell-DP-induced refactoring. For now this rips out the big cascade, which is what annoyed me so much. v2: Add port variable name back for the func decl (I've tried to trick myself below the 80 char limit). Reviewed-by: Paulo Zanoni Signed-Off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_ddi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/gpu/drm/i915/intel_ddi.c') diff --git a/drivers/gpu/drm/i915/intel_ddi.c b/drivers/gpu/drm/i915/intel_ddi.c index 933c74859172..32604ac80204 100644 --- a/drivers/gpu/drm/i915/intel_ddi.c +++ b/drivers/gpu/drm/i915/intel_ddi.c @@ -250,7 +250,7 @@ void intel_ddi_init(struct drm_device *dev, enum port port) case PORT_B: case PORT_C: case PORT_D: - intel_hdmi_init(dev, DDI_BUF_CTL(port)); + intel_hdmi_init(dev, DDI_BUF_CTL(port), port); break; default: DRM_DEBUG_DRIVER("No handlers defined for port %d, skipping DDI initialization\n", -- cgit v1.2.3 From 3f7c447faab3e750b0b2f94edad605ba8eb28a7f Mon Sep 17 00:00:00 2001 From: Paulo Zanoni Date: Wed, 8 Aug 2012 14:15:27 -0300 Subject: drm/i915: fix pipe DDI mode select Mask the value before changing it and also select DVI when needed. DVI was working in cases where the BIOS was setting the correct value because we were not masking the value before changing it. Signed-off-by: Paulo Zanoni Reviewed-by: Jani Nikula Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_reg.h | 1 + drivers/gpu/drm/i915/intel_ddi.c | 7 ++++++- 2 files changed, 7 insertions(+), 1 deletion(-) (limited to 'drivers/gpu/drm/i915/intel_ddi.c') diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h index 1310caaaafd6..97f00fb5307f 100644 --- a/drivers/gpu/drm/i915/i915_reg.h +++ b/drivers/gpu/drm/i915/i915_reg.h @@ -4302,6 +4302,7 @@ /* Those bits are ignored by pipe EDP since it can only connect to DDI A */ #define PIPE_DDI_PORT_MASK (7<<28) #define PIPE_DDI_SELECT_PORT(x) ((x)<<28) +#define PIPE_DDI_MODE_SELECT_MASK (7<<24) #define PIPE_DDI_MODE_SELECT_HDMI (0<<24) #define PIPE_DDI_MODE_SELECT_DVI (1<<24) #define PIPE_DDI_MODE_SELECT_DP_SST (2<<24) diff --git a/drivers/gpu/drm/i915/intel_ddi.c b/drivers/gpu/drm/i915/intel_ddi.c index 32604ac80204..0d7acd76c1b4 100644 --- a/drivers/gpu/drm/i915/intel_ddi.c +++ b/drivers/gpu/drm/i915/intel_ddi.c @@ -726,13 +726,18 @@ void intel_ddi_mode_set(struct drm_encoder *encoder, temp = I915_READ(DDI_FUNC_CTL(pipe)); temp &= ~PIPE_DDI_PORT_MASK; temp &= ~PIPE_DDI_BPC_12; + temp &= ~PIPE_DDI_MODE_SELECT_MASK; 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; + if (intel_hdmi->has_hdmi_sink) + temp |= PIPE_DDI_MODE_SELECT_HDMI; + else + temp |= PIPE_DDI_MODE_SELECT_DVI; + I915_WRITE(DDI_FUNC_CTL(pipe), temp); intel_hdmi->set_infoframes(encoder, adjusted_mode); -- cgit v1.2.3 From f63eb7c4528060e54688baafb7a499e9391eefe6 Mon Sep 17 00:00:00 2001 From: Paulo Zanoni Date: Wed, 8 Aug 2012 14:15:28 -0300 Subject: drm/i915: set the DDI sync polarity bits During my tests, everything worked even if the wrong polarity was set. Still, we should try to set the correct values. Signed-off-by: Paulo Zanoni Reviewed-by: Jani Nikula Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_reg.h | 2 ++ drivers/gpu/drm/i915/intel_ddi.c | 6 ++++++ 2 files changed, 8 insertions(+) (limited to 'drivers/gpu/drm/i915/intel_ddi.c') diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h index 97f00fb5307f..896b27966bc4 100644 --- a/drivers/gpu/drm/i915/i915_reg.h +++ b/drivers/gpu/drm/i915/i915_reg.h @@ -4312,6 +4312,8 @@ #define PIPE_DDI_BPC_10 (1<<20) #define PIPE_DDI_BPC_6 (2<<20) #define PIPE_DDI_BPC_12 (3<<20) +#define PIPE_DDI_PVSYNC (1<<17) +#define PIPE_DDI_PHSYNC (1<<16) #define PIPE_DDI_BFI_ENABLE (1<<4) #define PIPE_DDI_PORT_WIDTH_X1 (0<<1) #define PIPE_DDI_PORT_WIDTH_X2 (1<<1) diff --git a/drivers/gpu/drm/i915/intel_ddi.c b/drivers/gpu/drm/i915/intel_ddi.c index 0d7acd76c1b4..1fbd67cd5586 100644 --- a/drivers/gpu/drm/i915/intel_ddi.c +++ b/drivers/gpu/drm/i915/intel_ddi.c @@ -727,6 +727,7 @@ void intel_ddi_mode_set(struct drm_encoder *encoder, temp &= ~PIPE_DDI_PORT_MASK; temp &= ~PIPE_DDI_BPC_12; temp &= ~PIPE_DDI_MODE_SELECT_MASK; + temp &= ~(PIPE_DDI_PVSYNC | PIPE_DDI_PHSYNC); temp |= PIPE_DDI_SELECT_PORT(port) | ((intel_crtc->bpp > 24) ? PIPE_DDI_BPC_12 : @@ -738,6 +739,11 @@ void intel_ddi_mode_set(struct drm_encoder *encoder, else temp |= PIPE_DDI_MODE_SELECT_DVI; + if (adjusted_mode->flags & DRM_MODE_FLAG_PVSYNC) + temp |= PIPE_DDI_PVSYNC; + if (adjusted_mode->flags & DRM_MODE_FLAG_PHSYNC) + temp |= PIPE_DDI_PHSYNC; + I915_WRITE(DDI_FUNC_CTL(pipe), temp); intel_hdmi->set_infoframes(encoder, adjusted_mode); -- cgit v1.2.3 From dfcef252e024b4ca6fe5be9e1d656ac1929ce894 Mon Sep 17 00:00:00 2001 From: Paulo Zanoni Date: Wed, 8 Aug 2012 14:15:29 -0300 Subject: drm/i915: correctly set the DDI_FUNC_CTL bpc field Correctly erase the values previously set and also check for 6bpc and 10bpc. Signed-off-by: Paulo Zanoni Reviewed-by: Jani Nikula Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/i915_reg.h | 1 + drivers/gpu/drm/i915/intel_ddi.c | 26 ++++++++++++++++++++------ 2 files changed, 21 insertions(+), 6 deletions(-) (limited to 'drivers/gpu/drm/i915/intel_ddi.c') diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h index 896b27966bc4..f3fafb865365 100644 --- a/drivers/gpu/drm/i915/i915_reg.h +++ b/drivers/gpu/drm/i915/i915_reg.h @@ -4308,6 +4308,7 @@ #define PIPE_DDI_MODE_SELECT_DP_SST (2<<24) #define PIPE_DDI_MODE_SELECT_DP_MST (3<<24) #define PIPE_DDI_MODE_SELECT_FDI (4<<24) +#define PIPE_DDI_BPC_MASK (7<<20) #define PIPE_DDI_BPC_8 (0<<20) #define PIPE_DDI_BPC_10 (1<<20) #define PIPE_DDI_BPC_6 (2<<20) diff --git a/drivers/gpu/drm/i915/intel_ddi.c b/drivers/gpu/drm/i915/intel_ddi.c index 1fbd67cd5586..8b383593b0d3 100644 --- a/drivers/gpu/drm/i915/intel_ddi.c +++ b/drivers/gpu/drm/i915/intel_ddi.c @@ -725,14 +725,28 @@ void intel_ddi_mode_set(struct drm_encoder *encoder, /* 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_BPC_MASK; temp &= ~PIPE_DDI_MODE_SELECT_MASK; temp &= ~(PIPE_DDI_PVSYNC | PIPE_DDI_PHSYNC); - temp |= PIPE_DDI_SELECT_PORT(port) | - ((intel_crtc->bpp > 24) ? - PIPE_DDI_BPC_12 : - PIPE_DDI_BPC_8) | - PIPE_DDI_FUNC_ENABLE; + temp |= PIPE_DDI_FUNC_ENABLE | PIPE_DDI_SELECT_PORT(port); + + switch (intel_crtc->bpp) { + case 18: + temp |= PIPE_DDI_BPC_6; + break; + case 24: + temp |= PIPE_DDI_BPC_8; + break; + case 30: + temp |= PIPE_DDI_BPC_10; + break; + case 36: + temp |= PIPE_DDI_BPC_12; + break; + default: + WARN(1, "%d bpp unsupported by pipe DDI function\n", + intel_crtc->bpp); + } if (intel_hdmi->has_hdmi_sink) temp |= PIPE_DDI_MODE_SELECT_HDMI; -- cgit v1.2.3 From 602c43d34dcd46b0349b31c9b53b19cd5063ae3f Mon Sep 17 00:00:00 2001 From: Paulo Zanoni Date: Wed, 8 Aug 2012 14:15:30 -0300 Subject: drm/i915: completely reset the value of DDI_FUNC_CTL Don't rely on previous values already set on the register. Everything we're not explicitly setting should be zero for now. Signed-off-by: Paulo Zanoni Reviewed-by: Jani Nikula Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_ddi.c | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) (limited to 'drivers/gpu/drm/i915/intel_ddi.c') diff --git a/drivers/gpu/drm/i915/intel_ddi.c b/drivers/gpu/drm/i915/intel_ddi.c index 8b383593b0d3..ff03a3a57193 100644 --- a/drivers/gpu/drm/i915/intel_ddi.c +++ b/drivers/gpu/drm/i915/intel_ddi.c @@ -723,12 +723,7 @@ void intel_ddi_mode_set(struct drm_encoder *encoder, } /* 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_MASK; - temp &= ~PIPE_DDI_MODE_SELECT_MASK; - temp &= ~(PIPE_DDI_PVSYNC | PIPE_DDI_PHSYNC); - temp |= PIPE_DDI_FUNC_ENABLE | PIPE_DDI_SELECT_PORT(port); + temp = PIPE_DDI_FUNC_ENABLE | PIPE_DDI_SELECT_PORT(port); switch (intel_crtc->bpp) { case 18: -- cgit v1.2.3 From 126e9be816e7c0322f5dac994f94d65a8fabcc56 Mon Sep 17 00:00:00 2001 From: Paulo Zanoni Date: Fri, 10 Aug 2012 10:03:03 -0300 Subject: drm/i915: try harder to find WR PLL clock settings If we don't find the exact refresh rate, go with the next one. This makes some modes work for me. They won't have the best settings, but will at least have something. Just returning from this function when we don't find the perfect settings does not help us at all. Version 2: - Remove duplicate lines on the clock table. - Add back debug message with refresh, p, n2 and r2. Signed-off-by: Paulo Zanoni Reviewed-by: Jani Nikula Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_ddi.c | 39 +++++++++++++++++---------------------- 1 file changed, 17 insertions(+), 22 deletions(-) (limited to 'drivers/gpu/drm/i915/intel_ddi.c') diff --git a/drivers/gpu/drm/i915/intel_ddi.c b/drivers/gpu/drm/i915/intel_ddi.c index ff03a3a57193..958422606bc7 100644 --- a/drivers/gpu/drm/i915/intel_ddi.c +++ b/drivers/gpu/drm/i915/intel_ddi.c @@ -267,7 +267,8 @@ struct wrpll_tmds_clock { u16 r2; /* Reference divider */ }; -/* Table of matching values for WRPLL clocks programming for each frequency */ +/* Table of matching values for WRPLL clocks programming for each frequency. + * The code assumes this table is sorted. */ static const struct wrpll_tmds_clock wrpll_tmds_clock_table[] = { {19750, 38, 25, 18}, {20000, 48, 32, 18}, @@ -277,7 +278,6 @@ static const struct wrpll_tmds_clock wrpll_tmds_clock_table[] = { {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}, @@ -437,7 +437,6 @@ static const struct wrpll_tmds_clock wrpll_tmds_clock_table[] = { {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}, @@ -614,7 +613,6 @@ static const struct wrpll_tmds_clock wrpll_tmds_clock_table[] = { {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}, @@ -658,7 +656,7 @@ void intel_ddi_mode_set(struct drm_encoder *encoder, 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; + int p, n2, r2; u32 temp, i; /* On Haswell, we need to enable the clocks and prepare DDI function to @@ -666,26 +664,23 @@ void intel_ddi_mode_set(struct drm_encoder *encoder, */ 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; + for (i = 0; i < ARRAY_SIZE(wrpll_tmds_clock_table); i++) + if (crtc->mode.clock <= wrpll_tmds_clock_table[i].clock) + break; - 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); + if (i == ARRAY_SIZE(wrpll_tmds_clock_table)) + i--; - valid = 1; - break; - } - } + p = wrpll_tmds_clock_table[i].p; + n2 = wrpll_tmds_clock_table[i].n2; + r2 = wrpll_tmds_clock_table[i].r2; - if (!valid) { - DRM_ERROR("Unable to find WR PLL clock settings for %dKHz refresh rate\n", - crtc->mode.clock); - return; - } + if (wrpll_tmds_clock_table[i].clock != crtc->mode.clock) + DRM_INFO("WR PLL: using settings for %dKHz on %dKHz mode\n", + wrpll_tmds_clock_table[i].clock, crtc->mode.clock); + + DRM_DEBUG_KMS("WR PLL: %dKHz refresh rate with p=%d, n2=%d r2=%d\n", + crtc->mode.clock, p, n2, r2); /* Enable LCPLL if disabled */ temp = I915_READ(LCPLL_CTL); -- cgit v1.2.3 From 4f07854dd7fc197d9ea2bd83c26736baabb0b981 Mon Sep 17 00:00:00 2001 From: Wang Xingchao Date: Thu, 9 Aug 2012 16:52:16 +0800 Subject: drm/i915: write eld info for HDMI audio HDMI audio related registers will be configured in write_eld callback. Signed-off-by: Wang Xingchao Reviewed-by: Imre Deak Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_ddi.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'drivers/gpu/drm/i915/intel_ddi.c') diff --git a/drivers/gpu/drm/i915/intel_ddi.c b/drivers/gpu/drm/i915/intel_ddi.c index 958422606bc7..170e3861aa4e 100644 --- a/drivers/gpu/drm/i915/intel_ddi.c +++ b/drivers/gpu/drm/i915/intel_ddi.c @@ -713,8 +713,12 @@ void intel_ddi_mode_set(struct drm_encoder *encoder, /* 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", + DRM_DEBUG_DRIVER("HDMI audio on pipe %c on DDI\n", pipe_name(intel_crtc->pipe)); + + /* write eld */ + DRM_DEBUG_DRIVER("HDMI audio: write eld information\n"); + intel_write_eld(encoder, adjusted_mode); } /* Enable PIPE_DDI_FUNC_CTL for the pipe to work in HDMI mode */ -- cgit v1.2.3 From 5ab432ef4997ce32c9406721b37ef6e97e57dae1 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Sat, 30 Jun 2012 08:59:56 +0200 Subject: drm/i915/hdmi: convert to encoder->disable/enable I've picked hdmi as the first encoder to convert because it's rather simple: - no cloning possible - no differences between prepare/commit and dpms off/on switching. A few changes are required to do so: - Split up the dpms code into an enable/disable function and wire it up with the intel encoder. - Noop out the existing encoder prepare/commit functions used by the crtc helper - our crtc enable/disable code now calls back into the encoder enable/disable code at the right spot. - Create new helper functions to handle dpms changes. - Add intel_encoder->connectors_active to better track dpms state. Atm this is unused, but it will be useful to correctly disable the entire display pipe for cloned configurations. Also note that for now this is only useful in the dpms code - thanks to the crtc helper's dpms confusion across a modeset operation we can't (yet) rely on this having a sensible value in all circumstances. - Rip out the encoder helper dpms callback, if this is still getting called somewhere we have a bug. The slight issue with that is that the crtc helper abuses dpms off to disable unused functions. Hence we also need to implement a default encoder disable function to do just that with the new encoder->disable callback. - Note that we drop the cpt modeset verification in the commit callback, too. The right place to do this would be in the crtc's enable function, _after_ all the encoders are set up. But because not all encoders are converted yet, we can't do that. Hence disable this check temporarily as a minor concession to bisectability. v2: Squash the dpms mode to only the supported values - connector->dpms is for internal tracking only, we can hence avoid needless state-changes a bit whithout causing harm. v3: Apply bikeshed to disable|enable_ddi, suggested by Paulo Zanoni. Reviewed-by: Jesse Barnes Signed-Off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_ddi.c | 30 ++++++--- drivers/gpu/drm/i915/intel_display.c | 49 ++++++++++++++ drivers/gpu/drm/i915/intel_drv.h | 8 ++- drivers/gpu/drm/i915/intel_hdmi.c | 126 +++++++++++++++++++++++------------ 4 files changed, 160 insertions(+), 53 deletions(-) (limited to 'drivers/gpu/drm/i915/intel_ddi.c') diff --git a/drivers/gpu/drm/i915/intel_ddi.c b/drivers/gpu/drm/i915/intel_ddi.c index 170e3861aa4e..38a7006a84f4 100644 --- a/drivers/gpu/drm/i915/intel_ddi.c +++ b/drivers/gpu/drm/i915/intel_ddi.c @@ -757,26 +757,34 @@ void intel_ddi_mode_set(struct drm_encoder *encoder, intel_hdmi->set_infoframes(encoder, adjusted_mode); } -void intel_ddi_dpms(struct drm_encoder *encoder, int mode) +void intel_enable_ddi(struct intel_encoder *encoder) { - struct drm_device *dev = encoder->dev; + struct drm_device *dev = encoder->base.dev; struct drm_i915_private *dev_priv = dev->dev_private; - struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(encoder); + struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(&encoder->base); 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; - } + 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); + I915_WRITE(DDI_BUF_CTL(port), temp); +} + +void intel_disable_ddi(struct intel_encoder *encoder) +{ + struct drm_device *dev = encoder->base.dev; + struct drm_i915_private *dev_priv = dev->dev_private; + struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(&encoder->base); + int port = intel_hdmi->ddi_port; + u32 temp; + + temp = I915_READ(DDI_BUF_CTL(port)); + temp &= ~DDI_BUF_CTL_ENABLE; + + I915_WRITE(DDI_BUF_CTL(port), temp); } diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 82aaded6713b..cbd356f43d35 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -3554,6 +3554,17 @@ void intel_encoder_commit(struct drm_encoder *encoder) intel_cpt_verify_modeset(dev, intel_crtc->pipe); } +void intel_encoder_noop(struct drm_encoder *encoder) +{ +} + +void intel_encoder_disable(struct drm_encoder *encoder) +{ + struct intel_encoder *intel_encoder = to_intel_encoder(encoder); + + intel_encoder->disable(intel_encoder); +} + void intel_encoder_destroy(struct drm_encoder *encoder) { struct intel_encoder *intel_encoder = to_intel_encoder(encoder); @@ -3562,6 +3573,44 @@ void intel_encoder_destroy(struct drm_encoder *encoder) kfree(intel_encoder); } +/* Simple dpms helper for encodres with just one connector, no cloning and only + * one kind of off state. It clamps all !ON modes to fully OFF and changes the + * state of the entire output pipe. */ +void intel_encoder_dpms(struct intel_encoder *encoder, int mode) +{ + if (mode == DRM_MODE_DPMS_ON) { + encoder->connectors_active = true; + + intel_crtc_dpms(encoder->base.crtc, DRM_MODE_DPMS_ON); + } else { + encoder->connectors_active = false; + + intel_crtc_dpms(encoder->base.crtc, DRM_MODE_DPMS_OFF); + } +} + +/* Even simpler default implementation, if there's really no special case to + * consider. */ +void intel_connector_dpms(struct drm_connector *connector, int mode) +{ + struct intel_encoder *encoder = intel_attached_encoder(connector); + + /* All the simple cases only support two dpms states. */ + if (mode != DRM_MODE_DPMS_ON) + mode = DRM_MODE_DPMS_OFF; + + if (mode == connector->dpms) + return; + + connector->dpms = mode; + + /* Only need to change hw state when actually enabled */ + if (encoder->base.crtc) + intel_encoder_dpms(encoder, mode); + else + encoder->connectors_active = false; +} + static bool intel_crtc_mode_fixup(struct drm_crtc *crtc, const struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode) diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index 9a5adcc35bde..759dcbab0e5d 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -140,6 +140,7 @@ struct intel_encoder { * simple flag is enough to compute the possible_clones mask. */ bool cloneable; + bool connectors_active; void (*hot_plug)(struct intel_encoder *); void (*enable)(struct intel_encoder *); void (*disable)(struct intel_encoder *); @@ -412,7 +413,11 @@ extern enum drm_connector_status intel_panel_detect(struct drm_device *dev); extern void intel_crtc_load_lut(struct drm_crtc *crtc); extern void intel_encoder_prepare(struct drm_encoder *encoder); extern void intel_encoder_commit(struct drm_encoder *encoder); +extern void intel_encoder_noop(struct drm_encoder *encoder); +extern void intel_encoder_disable(struct drm_encoder *encoder); extern void intel_encoder_destroy(struct drm_encoder *encoder); +extern void intel_encoder_dpms(struct intel_encoder *encoder, int mode); +extern void intel_connector_dpms(struct drm_connector *, int mode); static inline struct intel_encoder *intel_attached_encoder(struct drm_connector *connector) { @@ -519,7 +524,8 @@ extern void intel_disable_gt_powersave(struct drm_device *dev); extern void gen6_gt_check_fifodbg(struct drm_i915_private *dev_priv); extern void ironlake_teardown_rc6(struct drm_device *dev); -extern void intel_ddi_dpms(struct drm_encoder *encoder, int mode); +extern void intel_enable_ddi(struct intel_encoder *encoder); +extern void intel_disable_ddi(struct intel_encoder *encoder); extern void intel_ddi_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode); diff --git a/drivers/gpu/drm/i915/intel_hdmi.c b/drivers/gpu/drm/i915/intel_hdmi.c index e4c37bb572e8..acddaaa5f04c 100644 --- a/drivers/gpu/drm/i915/intel_hdmi.c +++ b/drivers/gpu/drm/i915/intel_hdmi.c @@ -601,11 +601,11 @@ static void intel_hdmi_mode_set(struct drm_encoder *encoder, intel_hdmi->set_infoframes(encoder, adjusted_mode); } -static void intel_hdmi_dpms(struct drm_encoder *encoder, int mode) +static void intel_enable_hdmi(struct intel_encoder *encoder) { - struct drm_device *dev = encoder->dev; + struct drm_device *dev = encoder->base.dev; struct drm_i915_private *dev_priv = dev->dev_private; - struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(encoder); + struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(&encoder->base); u32 temp; u32 enable_bits = SDVO_ENABLE; @@ -617,31 +617,12 @@ static void intel_hdmi_dpms(struct drm_encoder *encoder, int mode) /* HW workaround for IBX, we need to move the port to transcoder A * before disabling it. */ if (HAS_PCH_IBX(dev)) { - struct drm_crtc *crtc = encoder->crtc; + struct drm_crtc *crtc = encoder->base.crtc; int pipe = crtc ? to_intel_crtc(crtc)->pipe : -1; - if (mode != DRM_MODE_DPMS_ON) { - if (temp & SDVO_PIPE_B_SELECT) { - temp &= ~SDVO_PIPE_B_SELECT; - I915_WRITE(intel_hdmi->sdvox_reg, temp); - POSTING_READ(intel_hdmi->sdvox_reg); - - /* Again we need to write this twice. */ - I915_WRITE(intel_hdmi->sdvox_reg, temp); - POSTING_READ(intel_hdmi->sdvox_reg); - - /* Transcoder selection bits only update - * effectively on vblank. */ - if (crtc) - intel_wait_for_vblank(dev, pipe); - else - msleep(50); - } - } else { - /* Restore the transcoder select bit. */ - if (pipe == PIPE_B) - enable_bits |= SDVO_PIPE_B_SELECT; - } + /* Restore the transcoder select bit. */ + if (pipe == PIPE_B) + enable_bits |= SDVO_PIPE_B_SELECT; } /* HW workaround, need to toggle enable bit off and on for 12bpc, but @@ -652,12 +633,67 @@ static void intel_hdmi_dpms(struct drm_encoder *encoder, int mode) POSTING_READ(intel_hdmi->sdvox_reg); } - if (mode != DRM_MODE_DPMS_ON) { - temp &= ~enable_bits; - } else { - temp |= enable_bits; + temp |= enable_bits; + + I915_WRITE(intel_hdmi->sdvox_reg, temp); + POSTING_READ(intel_hdmi->sdvox_reg); + + /* HW workaround, need to write this twice for issue that may result + * in first write getting masked. + */ + if (HAS_PCH_SPLIT(dev)) { + I915_WRITE(intel_hdmi->sdvox_reg, temp); + POSTING_READ(intel_hdmi->sdvox_reg); + } +} + +static void intel_disable_hdmi(struct intel_encoder *encoder) +{ + struct drm_device *dev = encoder->base.dev; + struct drm_i915_private *dev_priv = dev->dev_private; + struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(&encoder->base); + u32 temp; + u32 enable_bits = SDVO_ENABLE; + + if (intel_hdmi->has_audio) + enable_bits |= SDVO_AUDIO_ENABLE; + + temp = I915_READ(intel_hdmi->sdvox_reg); + + /* HW workaround for IBX, we need to move the port to transcoder A + * before disabling it. */ + if (HAS_PCH_IBX(dev)) { + struct drm_crtc *crtc = encoder->base.crtc; + int pipe = crtc ? to_intel_crtc(crtc)->pipe : -1; + + if (temp & SDVO_PIPE_B_SELECT) { + temp &= ~SDVO_PIPE_B_SELECT; + I915_WRITE(intel_hdmi->sdvox_reg, temp); + POSTING_READ(intel_hdmi->sdvox_reg); + + /* Again we need to write this twice. */ + I915_WRITE(intel_hdmi->sdvox_reg, temp); + POSTING_READ(intel_hdmi->sdvox_reg); + + /* Transcoder selection bits only update + * effectively on vblank. */ + if (crtc) + intel_wait_for_vblank(dev, pipe); + else + msleep(50); + } } + /* HW workaround, need to toggle enable bit off and on for 12bpc, but + * we do this anyway which shows more stable in testing. + */ + if (HAS_PCH_SPLIT(dev)) { + I915_WRITE(intel_hdmi->sdvox_reg, temp & ~SDVO_ENABLE); + POSTING_READ(intel_hdmi->sdvox_reg); + } + + temp &= ~enable_bits; + I915_WRITE(intel_hdmi->sdvox_reg, temp); POSTING_READ(intel_hdmi->sdvox_reg); @@ -849,23 +885,23 @@ static void intel_hdmi_destroy(struct drm_connector *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, + .prepare = intel_encoder_noop, .mode_set = intel_ddi_mode_set, - .commit = intel_encoder_commit, + .commit = intel_encoder_noop, + .disable = intel_encoder_disable, }; static const struct drm_encoder_helper_funcs intel_hdmi_helper_funcs = { - .dpms = intel_hdmi_dpms, .mode_fixup = intel_hdmi_mode_fixup, - .prepare = intel_encoder_prepare, + .prepare = intel_encoder_noop, .mode_set = intel_hdmi_mode_set, - .commit = intel_encoder_commit, + .commit = intel_encoder_noop, + .disable = intel_encoder_disable, }; static const struct drm_connector_funcs intel_hdmi_connector_funcs = { - .dpms = drm_helper_connector_dpms, + .dpms = intel_connector_dpms, .detect = intel_hdmi_detect, .fill_modes = drm_helper_probe_single_connector_modes, .set_property = intel_hdmi_set_property, @@ -964,10 +1000,18 @@ void intel_hdmi_init(struct drm_device *dev, int sdvox_reg, enum port port) intel_hdmi->set_infoframes = cpt_set_infoframes; } - 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); + if (IS_HASWELL(dev)) { + intel_encoder->enable = intel_enable_ddi; + intel_encoder->disable = intel_disable_ddi; + drm_encoder_helper_add(&intel_encoder->base, + &intel_hdmi_helper_funcs_hsw); + } else { + intel_encoder->enable = intel_enable_hdmi; + intel_encoder->disable = intel_disable_hdmi; + drm_encoder_helper_add(&intel_encoder->base, + &intel_hdmi_helper_funcs); + } + intel_hdmi_add_properties(intel_hdmi, connector); -- cgit v1.2.3 From 85234cdc28f622dc94d8bb0089635113e2aa5609 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Mon, 2 Jul 2012 13:27:29 +0200 Subject: drm/i915/hdmi: implement get_hw_state Reviewed-by: Jesse Barnes Signed-off-by: Daniel Vetter --- drivers/gpu/drm/i915/intel_ddi.c | 29 +++++++++++++++++++++++++++++ drivers/gpu/drm/i915/intel_drv.h | 2 ++ drivers/gpu/drm/i915/intel_hdmi.c | 24 ++++++++++++++++++++++++ 3 files changed, 55 insertions(+) (limited to 'drivers/gpu/drm/i915/intel_ddi.c') diff --git a/drivers/gpu/drm/i915/intel_ddi.c b/drivers/gpu/drm/i915/intel_ddi.c index 38a7006a84f4..bfe375466a0e 100644 --- a/drivers/gpu/drm/i915/intel_ddi.c +++ b/drivers/gpu/drm/i915/intel_ddi.c @@ -757,6 +757,35 @@ void intel_ddi_mode_set(struct drm_encoder *encoder, intel_hdmi->set_infoframes(encoder, adjusted_mode); } +bool intel_ddi_get_hw_state(struct intel_encoder *encoder, + enum pipe *pipe) +{ + struct drm_device *dev = encoder->base.dev; + struct drm_i915_private *dev_priv = dev->dev_private; + struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(&encoder->base); + u32 tmp; + int i; + + tmp = I915_READ(DDI_BUF_CTL(intel_hdmi->ddi_port)); + + if (!(tmp & DDI_BUF_CTL_ENABLE)) + return false; + + for_each_pipe(i) { + tmp = I915_READ(DDI_FUNC_CTL(i)); + + if ((tmp & PIPE_DDI_PORT_MASK) + == PIPE_DDI_SELECT_PORT(intel_hdmi->ddi_port)) { + *pipe = i; + return true; + } + } + + DRM_DEBUG_KMS("No pipe for ddi port %i found\n", intel_hdmi->ddi_port); + + return true; +} + void intel_enable_ddi(struct intel_encoder *encoder) { struct drm_device *dev = encoder->base.dev; diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index c39c70554891..4daa7e65b04a 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -542,6 +542,8 @@ extern void ironlake_teardown_rc6(struct drm_device *dev); extern void intel_enable_ddi(struct intel_encoder *encoder); extern void intel_disable_ddi(struct intel_encoder *encoder); +extern bool intel_ddi_get_hw_state(struct intel_encoder *encoder, + enum pipe *pipe); extern void intel_ddi_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode); diff --git a/drivers/gpu/drm/i915/intel_hdmi.c b/drivers/gpu/drm/i915/intel_hdmi.c index c9535cee1a73..e7d5078e6da7 100644 --- a/drivers/gpu/drm/i915/intel_hdmi.c +++ b/drivers/gpu/drm/i915/intel_hdmi.c @@ -601,6 +601,27 @@ static void intel_hdmi_mode_set(struct drm_encoder *encoder, intel_hdmi->set_infoframes(encoder, adjusted_mode); } +static bool intel_hdmi_get_hw_state(struct intel_encoder *encoder, + enum pipe *pipe) +{ + struct drm_device *dev = encoder->base.dev; + struct drm_i915_private *dev_priv = dev->dev_private; + struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(&encoder->base); + u32 tmp; + + tmp = I915_READ(intel_hdmi->sdvox_reg); + + if (!(tmp & SDVO_ENABLE)) + return false; + + if (HAS_PCH_CPT(dev)) + *pipe = PORT_TO_PIPE_CPT(tmp); + else + *pipe = PORT_TO_PIPE(tmp); + + return true; +} + static void intel_enable_hdmi(struct intel_encoder *encoder) { struct drm_device *dev = encoder->base.dev; @@ -998,14 +1019,17 @@ void intel_hdmi_init(struct drm_device *dev, int sdvox_reg, enum port port) if (IS_HASWELL(dev)) { intel_encoder->enable = intel_enable_ddi; intel_encoder->disable = intel_disable_ddi; + intel_encoder->get_hw_state = intel_ddi_get_hw_state; drm_encoder_helper_add(&intel_encoder->base, &intel_hdmi_helper_funcs_hsw); } else { intel_encoder->enable = intel_enable_hdmi; intel_encoder->disable = intel_disable_hdmi; + intel_encoder->get_hw_state = intel_hdmi_get_hw_state; drm_encoder_helper_add(&intel_encoder->base, &intel_hdmi_helper_funcs); } + intel_connector->get_hw_state = intel_connector_get_hw_state; intel_hdmi_add_properties(intel_hdmi, connector); -- cgit v1.2.3