From 54d63ca6605d5eb5d2ed52673b523f5781ead71b Mon Sep 17 00:00:00 2001 From: Shobhit Kumar Date: Fri, 29 Jun 2012 16:03:35 -0300 Subject: drm/i915: Move DP structs to shared location Move the DP structure to shared location so that it can be used from within the ddi module. Changes from Paulo: - Move less code to intel_drv.h - Remove #include statement - Replace a tab with a space in train_set Signed-off-by: Shobhit Kumar Signed-off-by: Paulo Zanoni Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index 0a56b9a..9f41564 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -36,42 +36,10 @@ #include "intel_drv.h" #include "i915_drm.h" #include "i915_drv.h" -#include "drm_dp_helper.h" -#define DP_RECEIVER_CAP_SIZE 0xf #define DP_LINK_STATUS_SIZE 6 #define DP_LINK_CHECK_TIMEOUT (10 * 1000) -#define DP_LINK_CONFIGURATION_SIZE 9 - -struct intel_dp { - struct intel_encoder base; - uint32_t output_reg; - uint32_t DP; - uint8_t link_configuration[DP_LINK_CONFIGURATION_SIZE]; - bool has_audio; - enum hdmi_force_audio force_audio; - uint32_t color_range; - int dpms_mode; - uint8_t link_bw; - uint8_t lane_count; - uint8_t dpcd[DP_RECEIVER_CAP_SIZE]; - struct i2c_adapter adapter; - struct i2c_algo_dp_aux_data algo; - bool is_pch_edp; - uint8_t train_set[4]; - int panel_power_up_delay; - int panel_power_down_delay; - int panel_power_cycle_delay; - int backlight_on_delay; - int backlight_off_delay; - struct drm_display_mode *panel_fixed_mode; /* for eDP */ - struct delayed_work panel_vdd_work; - bool want_panel_vdd; - struct edid *edid; /* cached EDID for eDP */ - int edid_mode_count; -}; - /** * is_edp - is the given port attached to an eDP panel (either CPU or PCH) * @intel_dp: DP struct diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index 8435355..35488aa 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -31,6 +31,7 @@ #include "drm_crtc.h" #include "drm_crtc_helper.h" #include "drm_fb_helper.h" +#include "drm_dp_helper.h" #define _wait_for(COND, MS, W) ({ \ unsigned long timeout__ = jiffies + msecs_to_jiffies(MS); \ @@ -310,6 +311,37 @@ struct intel_hdmi { struct drm_display_mode *adjusted_mode); }; +#define DP_RECEIVER_CAP_SIZE 0xf +#define DP_LINK_CONFIGURATION_SIZE 9 + +struct intel_dp { + struct intel_encoder base; + uint32_t output_reg; + uint32_t DP; + uint8_t link_configuration[DP_LINK_CONFIGURATION_SIZE]; + bool has_audio; + enum hdmi_force_audio force_audio; + uint32_t color_range; + int dpms_mode; + uint8_t link_bw; + uint8_t lane_count; + uint8_t dpcd[DP_RECEIVER_CAP_SIZE]; + struct i2c_adapter adapter; + struct i2c_algo_dp_aux_data algo; + bool is_pch_edp; + uint8_t train_set[4]; + int panel_power_up_delay; + int panel_power_down_delay; + int panel_power_cycle_delay; + int backlight_on_delay; + int backlight_off_delay; + struct drm_display_mode *panel_fixed_mode; /* for eDP */ + struct delayed_work panel_vdd_work; + bool want_panel_vdd; + struct edid *edid; /* cached EDID for eDP */ + int edid_mode_count; +}; + static inline struct drm_crtc * intel_get_crtc_for_pipe(struct drm_device *dev, int pipe) { -- cgit v0.10.2 From 9a3b530455380eed28e7a93121c46d7c334153d9 Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Sun, 15 Jul 2012 12:34:24 +0100 Subject: drm/i915: Cleanup context switching through do_switch() When bug hunting, I found the interface to do_switch() overly complicated and I believe festered the earlier bug. This aims to make the code a little clearer. Signed-off-by: Chris Wilson Reviewed-by: Ben Widawsky Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/i915_gem_context.c b/drivers/gpu/drm/i915/i915_gem_context.c index da8b01f..5d0d6ad 100644 --- a/drivers/gpu/drm/i915/i915_gem_context.c +++ b/drivers/gpu/drm/i915/i915_gem_context.c @@ -97,8 +97,7 @@ static struct i915_hw_context * i915_gem_context_get(struct drm_i915_file_private *file_priv, u32 id); -static int do_switch(struct drm_i915_gem_object *from_obj, - struct i915_hw_context *to, u32 seqno); +static int do_switch(struct i915_hw_context *to); static int get_context_size(struct drm_device *dev) { @@ -220,19 +219,20 @@ static int create_default_context(struct drm_i915_private *dev_priv) */ dev_priv->ring[RCS].default_context = ctx; ret = i915_gem_object_pin(ctx->obj, CONTEXT_ALIGN, false); - if (ret) { - do_destroy(ctx); - return ret; - } + if (ret) + goto err_destroy; - ret = do_switch(NULL, ctx, 0); - if (ret) { - i915_gem_object_unpin(ctx->obj); - do_destroy(ctx); - } else { - DRM_DEBUG_DRIVER("Default HW context loaded\n"); - } + ret = do_switch(ctx); + if (ret) + goto err_unpin; + DRM_DEBUG_DRIVER("Default HW context loaded\n"); + return 0; + +err_unpin: + i915_gem_object_unpin(ctx->obj); +err_destroy: + do_destroy(ctx); return ret; } @@ -359,17 +359,18 @@ mi_set_context(struct intel_ring_buffer *ring, return ret; } -static int do_switch(struct drm_i915_gem_object *from_obj, - struct i915_hw_context *to, - u32 seqno) +static int do_switch(struct i915_hw_context *to) { - struct intel_ring_buffer *ring = NULL; + struct intel_ring_buffer *ring = to->ring; + struct drm_i915_gem_object *from_obj = ring->last_context_obj; u32 hw_flags = 0; int ret; - BUG_ON(to == NULL); BUG_ON(from_obj != NULL && from_obj->pin_count == 0); + if (from_obj == to->obj) + return 0; + ret = i915_gem_object_pin(to->obj, CONTEXT_ALIGN, false); if (ret) return ret; @@ -393,7 +394,6 @@ static int do_switch(struct drm_i915_gem_object *from_obj, else if (WARN_ON_ONCE(from_obj == to->obj)) /* not yet expected */ hw_flags |= MI_FORCE_RESTORE; - ring = to->ring; ret = mi_set_context(ring, to, hw_flags); if (ret) { i915_gem_object_unpin(to->obj); @@ -407,6 +407,7 @@ static int do_switch(struct drm_i915_gem_object *from_obj, * MI_SET_CONTEXT instead of when the next seqno has completed. */ if (from_obj != NULL) { + u32 seqno = i915_gem_next_request_seqno(ring); from_obj->base.read_domains = I915_GEM_DOMAIN_INSTRUCTION; i915_gem_object_move_to_active(from_obj, ring, seqno); /* As long as MI_SET_CONTEXT is serializing, ie. it flushes the @@ -417,7 +418,7 @@ static int do_switch(struct drm_i915_gem_object *from_obj, * swapped, but there is no way to do that yet. */ from_obj->dirty = 1; - BUG_ON(from_obj->ring != to->ring); + BUG_ON(from_obj->ring != ring); i915_gem_object_unpin(from_obj); drm_gem_object_unreference(&from_obj->base); @@ -448,10 +449,7 @@ int i915_switch_context(struct intel_ring_buffer *ring, int to_id) { struct drm_i915_private *dev_priv = ring->dev->dev_private; - struct drm_i915_file_private *file_priv = NULL; struct i915_hw_context *to; - struct drm_i915_gem_object *from_obj = ring->last_context_obj; - int ret; if (dev_priv->hw_contexts_disabled) return 0; @@ -459,21 +457,18 @@ int i915_switch_context(struct intel_ring_buffer *ring, if (ring != &dev_priv->ring[RCS]) return 0; - if (file) - file_priv = file->driver_priv; - if (to_id == DEFAULT_CONTEXT_ID) { to = ring->default_context; } else { - to = i915_gem_context_get(file_priv, to_id); + if (file == NULL) + return -EINVAL; + + to = i915_gem_context_get(file->driver_priv, to_id); if (to == NULL) return -ENOENT; } - if (from_obj == to->obj) - return 0; - - return do_switch(from_obj, to, i915_gem_next_request_seqno(to->ring)); + return do_switch(to); } int i915_gem_context_create_ioctl(struct drm_device *dev, void *data, -- cgit v0.10.2 From ebc0fd882b000b119e0684815db8c2245e61162f Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Wed, 11 Jul 2012 16:27:56 +0200 Subject: drm/i915: group ADPA #defines together Splitting them up between pch and gmch variants just makes it harder to find things. Especially since the hotplug bits are actually valid on earlier chips, too. v2: Fixed the comment as pointed out by Paulo Zanoni. Reviewed-by: Paulo Zanoni Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h index acc99b2..d8af397 100644 --- a/drivers/gpu/drm/i915/i915_reg.h +++ b/drivers/gpu/drm/i915/i915_reg.h @@ -1548,12 +1548,34 @@ /* VGA port control */ #define ADPA 0x61100 +#define PCH_ADPA 0xe1100 + #define ADPA_DAC_ENABLE (1<<31) #define ADPA_DAC_DISABLE 0 #define ADPA_PIPE_SELECT_MASK (1<<30) #define ADPA_PIPE_A_SELECT 0 #define ADPA_PIPE_B_SELECT (1<<30) #define ADPA_PIPE_SELECT(pipe) ((pipe) << 30) +/* CPT uses bits 29:30 for pch transcoder select */ +#define ADPA_CRT_HOTPLUG_MASK 0x03ff0000 /* bit 25-16 */ +#define ADPA_CRT_HOTPLUG_MONITOR_NONE (0<<24) +#define ADPA_CRT_HOTPLUG_MONITOR_MASK (3<<24) +#define ADPA_CRT_HOTPLUG_MONITOR_COLOR (3<<24) +#define ADPA_CRT_HOTPLUG_MONITOR_MONO (2<<24) +#define ADPA_CRT_HOTPLUG_ENABLE (1<<23) +#define ADPA_CRT_HOTPLUG_PERIOD_64 (0<<22) +#define ADPA_CRT_HOTPLUG_PERIOD_128 (1<<22) +#define ADPA_CRT_HOTPLUG_WARMUP_5MS (0<<21) +#define ADPA_CRT_HOTPLUG_WARMUP_10MS (1<<21) +#define ADPA_CRT_HOTPLUG_SAMPLE_2S (0<<20) +#define ADPA_CRT_HOTPLUG_SAMPLE_4S (1<<20) +#define ADPA_CRT_HOTPLUG_VOLTAGE_40 (0<<18) +#define ADPA_CRT_HOTPLUG_VOLTAGE_50 (1<<18) +#define ADPA_CRT_HOTPLUG_VOLTAGE_60 (2<<18) +#define ADPA_CRT_HOTPLUG_VOLTAGE_70 (3<<18) +#define ADPA_CRT_HOTPLUG_VOLREF_325MV (0<<17) +#define ADPA_CRT_HOTPLUG_VOLREF_475MV (1<<17) +#define ADPA_CRT_HOTPLUG_FORCE_TRIGGER (1<<16) #define ADPA_USE_VGA_HVPOLARITY (1<<15) #define ADPA_SETS_HVPOLARITY 0 #define ADPA_VSYNC_CNTL_DISABLE (1<<11) @@ -3888,31 +3910,6 @@ #define FDI_PLL_CTL_1 0xfe000 #define FDI_PLL_CTL_2 0xfe004 -/* CRT */ -#define PCH_ADPA 0xe1100 -#define ADPA_TRANS_SELECT_MASK (1<<30) -#define ADPA_TRANS_A_SELECT 0 -#define ADPA_TRANS_B_SELECT (1<<30) -#define ADPA_CRT_HOTPLUG_MASK 0x03ff0000 /* bit 25-16 */ -#define ADPA_CRT_HOTPLUG_MONITOR_NONE (0<<24) -#define ADPA_CRT_HOTPLUG_MONITOR_MASK (3<<24) -#define ADPA_CRT_HOTPLUG_MONITOR_COLOR (3<<24) -#define ADPA_CRT_HOTPLUG_MONITOR_MONO (2<<24) -#define ADPA_CRT_HOTPLUG_ENABLE (1<<23) -#define ADPA_CRT_HOTPLUG_PERIOD_64 (0<<22) -#define ADPA_CRT_HOTPLUG_PERIOD_128 (1<<22) -#define ADPA_CRT_HOTPLUG_WARMUP_5MS (0<<21) -#define ADPA_CRT_HOTPLUG_WARMUP_10MS (1<<21) -#define ADPA_CRT_HOTPLUG_SAMPLE_2S (0<<20) -#define ADPA_CRT_HOTPLUG_SAMPLE_4S (1<<20) -#define ADPA_CRT_HOTPLUG_VOLTAGE_40 (0<<18) -#define ADPA_CRT_HOTPLUG_VOLTAGE_50 (1<<18) -#define ADPA_CRT_HOTPLUG_VOLTAGE_60 (2<<18) -#define ADPA_CRT_HOTPLUG_VOLTAGE_70 (3<<18) -#define ADPA_CRT_HOTPLUG_VOLREF_325MV (0<<17) -#define ADPA_CRT_HOTPLUG_VOLREF_475MV (1<<17) -#define ADPA_CRT_HOTPLUG_FORCE_TRIGGER (1<<16) - /* or SDVOB */ #define HDMIB 0xe1140 #define PORT_ENABLE (1 << 31) -- cgit v0.10.2 From 66a9278eecbef1c746e7fac8f4bcb0485d7aa4d0 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Thu, 12 Jul 2012 20:08:18 +0200 Subject: drm/i915: simplify possible_clones computation Intel hw only has one MUX for encoders, so outputs are either not cloneable or all in the same group of cloneable outputs. This neatly simplifies the code and allows us to ditch some ugly if cascades in the dp and hdmi init code (well, we need these if cascades for other stuff still, but that can be taken care of in follow-up patches). Note that this changes two things: - dvo can now be cloned with sdvo, but dvo is gen2 whereas sdvo is gen3+, so no problem. Note that the old code had a bug and didn't allow cloning crt with dvo (but only the other way round). - sdvo-lvds can now be cloned with sdvo-non-tv. Spec says this won't work, but the only reason I've found is that you can't use the panel-fitter (used for lvds upscaling) with anything else. But we don't use the panel fitter for sdvo-lvds. Imo this part of Bspec is a) rather confusing b) mostly as a guideline to implementors (i.e. explicitly stating what is already implicit from the spec, without always going into the details of why). So I think we can ignore this - worst case we'll get a bug report from a user with with sdvo-lvds and sdvo-tmds and have to add that special case back in. Because sdvo lvds is a bit special explain in comments why sdvo LVDS outputs can be cloned, but native LVDS and eDP can't be cloned - we use the panel fitter for the later, but not for sdvo. Note that this also uncoditionally initializes the panel_vdd work used by eDP. Trying to be clever doesn't buy us anything (but strange bugs) and this way we can kill the is_edp check. v2: Incorporate review from Paulo - Add in a missing space. - Pimp comment message to address his concerns. Reviewed-by: Paulo Zanoni Signed-Off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/intel_crt.c b/drivers/gpu/drm/i915/intel_crt.c index 7ed4a41..e1d02be 100644 --- a/drivers/gpu/drm/i915/intel_crt.c +++ b/drivers/gpu/drm/i915/intel_crt.c @@ -658,9 +658,7 @@ void intel_crt_init(struct drm_device *dev) intel_connector_attach_encoder(intel_connector, &crt->base); crt->base.type = INTEL_OUTPUT_ANALOG; - crt->base.clone_mask = (1 << INTEL_SDVO_NON_TV_CLONE_BIT | - 1 << INTEL_ANALOG_CLONE_BIT | - 1 << INTEL_SDVO_LVDS_CLONE_BIT); + crt->base.cloneable = true; if (IS_HASWELL(dev)) crt->base.crtc_mask = (1 << 0); else diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index e9e476e..5c0a726 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -6707,15 +6707,23 @@ int intel_get_pipe_from_crtc_id(struct drm_device *dev, void *data, return 0; } -static int intel_encoder_clones(struct drm_device *dev, int type_mask) +static int intel_encoder_clones(struct intel_encoder *encoder) { - struct intel_encoder *encoder; + struct drm_device *dev = encoder->base.dev; + struct intel_encoder *source_encoder; int index_mask = 0; int entry = 0; - list_for_each_entry(encoder, &dev->mode_config.encoder_list, base.head) { - if (type_mask & encoder->clone_mask) + list_for_each_entry(source_encoder, + &dev->mode_config.encoder_list, base.head) { + + if (encoder == source_encoder) index_mask |= (1 << entry); + + /* Intel hw has only one MUX where enocoders could be cloned. */ + if (encoder->cloneable && source_encoder->cloneable) + index_mask |= (1 << entry); + entry++; } @@ -6874,7 +6882,7 @@ static void intel_setup_outputs(struct drm_device *dev) list_for_each_entry(encoder, &dev->mode_config.encoder_list, base.head) { encoder->base.possible_crtcs = encoder->crtc_mask; encoder->base.possible_clones = - intel_encoder_clones(dev, encoder->clone_mask); + intel_encoder_clones(encoder); } /* disable all the possible outputs/crtcs before entering KMS mode */ diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index 9f41564..d1489ab 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -2451,18 +2451,10 @@ intel_dp_init(struct drm_device *dev, int output_reg) connector->polled = DRM_CONNECTOR_POLL_HPD; - if (output_reg == DP_B || output_reg == PCH_DP_B) - intel_encoder->clone_mask = (1 << INTEL_DP_B_CLONE_BIT); - else if (output_reg == DP_C || output_reg == PCH_DP_C) - intel_encoder->clone_mask = (1 << INTEL_DP_C_CLONE_BIT); - else if (output_reg == DP_D || output_reg == PCH_DP_D) - intel_encoder->clone_mask = (1 << INTEL_DP_D_CLONE_BIT); + intel_encoder->cloneable = false; - if (is_edp(intel_dp)) { - intel_encoder->clone_mask = (1 << INTEL_EDP_CLONE_BIT); - INIT_DELAYED_WORK(&intel_dp->panel_vdd_work, - ironlake_panel_vdd_work); - } + INIT_DELAYED_WORK(&intel_dp->panel_vdd_work, + ironlake_panel_vdd_work); intel_encoder->crtc_mask = (1 << 0) | (1 << 1) | (1 << 2); diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index 35488aa..76ba554 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -90,25 +90,6 @@ #define INTEL_OUTPUT_DISPLAYPORT 7 #define INTEL_OUTPUT_EDP 8 -/* Intel Pipe Clone Bit */ -#define INTEL_HDMIB_CLONE_BIT 1 -#define INTEL_HDMIC_CLONE_BIT 2 -#define INTEL_HDMID_CLONE_BIT 3 -#define INTEL_HDMIE_CLONE_BIT 4 -#define INTEL_HDMIF_CLONE_BIT 5 -#define INTEL_SDVO_NON_TV_CLONE_BIT 6 -#define INTEL_SDVO_TV_CLONE_BIT 7 -#define INTEL_SDVO_LVDS_CLONE_BIT 8 -#define INTEL_ANALOG_CLONE_BIT 9 -#define INTEL_TV_CLONE_BIT 10 -#define INTEL_DP_B_CLONE_BIT 11 -#define INTEL_DP_C_CLONE_BIT 12 -#define INTEL_DP_D_CLONE_BIT 13 -#define INTEL_LVDS_CLONE_BIT 14 -#define INTEL_DVO_TMDS_CLONE_BIT 15 -#define INTEL_DVO_LVDS_CLONE_BIT 16 -#define INTEL_EDP_CLONE_BIT 17 - #define INTEL_DVO_CHIP_NONE 0 #define INTEL_DVO_CHIP_LVDS 1 #define INTEL_DVO_CHIP_TMDS 2 @@ -153,9 +134,13 @@ struct intel_encoder { struct drm_encoder base; int type; bool needs_tv_clock; + /* + * Intel hw has only one MUX where encoders could be clone, hence a + * simple flag is enough to compute the possible_clones mask. + */ + bool cloneable; void (*hot_plug)(struct intel_encoder *); int crtc_mask; - int clone_mask; }; struct intel_connector { diff --git a/drivers/gpu/drm/i915/intel_dvo.c b/drivers/gpu/drm/i915/intel_dvo.c index 36c542e..556cf6b 100644 --- a/drivers/gpu/drm/i915/intel_dvo.c +++ b/drivers/gpu/drm/i915/intel_dvo.c @@ -396,17 +396,14 @@ void intel_dvo_init(struct drm_device *dev) intel_encoder->crtc_mask = (1 << 0) | (1 << 1); switch (dvo->type) { case INTEL_DVO_CHIP_TMDS: - intel_encoder->clone_mask = - (1 << INTEL_DVO_TMDS_CLONE_BIT) | - (1 << INTEL_ANALOG_CLONE_BIT); + intel_encoder->cloneable = true; drm_connector_init(dev, connector, &intel_dvo_connector_funcs, DRM_MODE_CONNECTOR_DVII); encoder_type = DRM_MODE_ENCODER_TMDS; break; case INTEL_DVO_CHIP_LVDS: - intel_encoder->clone_mask = - (1 << INTEL_DVO_LVDS_CLONE_BIT); + intel_encoder->cloneable = false; drm_connector_init(dev, connector, &intel_dvo_connector_funcs, DRM_MODE_CONNECTOR_LVDS); diff --git a/drivers/gpu/drm/i915/intel_hdmi.c b/drivers/gpu/drm/i915/intel_hdmi.c index 98f6024..593b8fe 100644 --- a/drivers/gpu/drm/i915/intel_hdmi.c +++ b/drivers/gpu/drm/i915/intel_hdmi.c @@ -923,42 +923,36 @@ void intel_hdmi_init(struct drm_device *dev, int sdvox_reg) connector->doublescan_allowed = 0; intel_encoder->crtc_mask = (1 << 0) | (1 << 1) | (1 << 2); + intel_encoder->cloneable = false; + /* Set up the DDC bus. */ if (sdvox_reg == SDVOB) { - intel_encoder->clone_mask = (1 << INTEL_HDMIB_CLONE_BIT); intel_hdmi->ddc_bus = GMBUS_PORT_DPB; dev_priv->hotplug_supported_mask |= HDMIB_HOTPLUG_INT_STATUS; } else if (sdvox_reg == SDVOC) { - intel_encoder->clone_mask = (1 << INTEL_HDMIC_CLONE_BIT); intel_hdmi->ddc_bus = GMBUS_PORT_DPC; dev_priv->hotplug_supported_mask |= HDMIC_HOTPLUG_INT_STATUS; } else if (sdvox_reg == HDMIB) { - intel_encoder->clone_mask = (1 << INTEL_HDMID_CLONE_BIT); intel_hdmi->ddc_bus = GMBUS_PORT_DPB; dev_priv->hotplug_supported_mask |= HDMIB_HOTPLUG_INT_STATUS; } else if (sdvox_reg == HDMIC) { - intel_encoder->clone_mask = (1 << INTEL_HDMIE_CLONE_BIT); intel_hdmi->ddc_bus = GMBUS_PORT_DPC; dev_priv->hotplug_supported_mask |= HDMIC_HOTPLUG_INT_STATUS; } else if (sdvox_reg == HDMID) { - 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; diff --git a/drivers/gpu/drm/i915/intel_lvds.c b/drivers/gpu/drm/i915/intel_lvds.c index e05c0d3..d789fda 100644 --- a/drivers/gpu/drm/i915/intel_lvds.c +++ b/drivers/gpu/drm/i915/intel_lvds.c @@ -967,7 +967,7 @@ bool intel_lvds_init(struct drm_device *dev) intel_connector_attach_encoder(intel_connector, intel_encoder); intel_encoder->type = INTEL_OUTPUT_LVDS; - intel_encoder->clone_mask = (1 << INTEL_LVDS_CLONE_BIT); + intel_encoder->cloneable = false; if (HAS_PCH_SPLIT(dev)) intel_encoder->crtc_mask = (1 << 0) | (1 << 1) | (1 << 2); else if (IS_GEN4(dev)) diff --git a/drivers/gpu/drm/i915/intel_sdvo.c b/drivers/gpu/drm/i915/intel_sdvo.c index 26a6a4d..d881602 100644 --- a/drivers/gpu/drm/i915/intel_sdvo.c +++ b/drivers/gpu/drm/i915/intel_sdvo.c @@ -2078,8 +2078,7 @@ intel_sdvo_dvi_init(struct intel_sdvo *intel_sdvo, int device) connector->connector_type = DRM_MODE_CONNECTOR_HDMIA; intel_sdvo->is_hdmi = true; } - intel_sdvo->base.clone_mask = ((1 << INTEL_SDVO_NON_TV_CLONE_BIT) | - (1 << INTEL_ANALOG_CLONE_BIT)); + intel_sdvo->base.cloneable = true; intel_sdvo_connector_init(intel_sdvo_connector, intel_sdvo); if (intel_sdvo->is_hdmi) @@ -2110,7 +2109,7 @@ intel_sdvo_tv_init(struct intel_sdvo *intel_sdvo, int type) intel_sdvo->is_tv = true; intel_sdvo->base.needs_tv_clock = true; - intel_sdvo->base.clone_mask = 1 << INTEL_SDVO_TV_CLONE_BIT; + intel_sdvo->base.cloneable = false; intel_sdvo_connector_init(intel_sdvo_connector, intel_sdvo); @@ -2153,8 +2152,7 @@ intel_sdvo_analog_init(struct intel_sdvo *intel_sdvo, int device) intel_sdvo_connector->output_flag = SDVO_OUTPUT_RGB1; } - intel_sdvo->base.clone_mask = ((1 << INTEL_SDVO_NON_TV_CLONE_BIT) | - (1 << INTEL_ANALOG_CLONE_BIT)); + intel_sdvo->base.cloneable = true; intel_sdvo_connector_init(intel_sdvo_connector, intel_sdvo); @@ -2186,8 +2184,10 @@ intel_sdvo_lvds_init(struct intel_sdvo *intel_sdvo, int device) intel_sdvo_connector->output_flag = SDVO_OUTPUT_LVDS1; } - intel_sdvo->base.clone_mask = ((1 << INTEL_ANALOG_CLONE_BIT) | - (1 << INTEL_SDVO_LVDS_CLONE_BIT)); + /* SDVO LVDS is cloneable because the SDVO encoder does the upscaling, + * as opposed to native LVDS, where we upscale with the panel-fitter + * (and hence only the native LVDS resolution could be cloned). */ + intel_sdvo->base.cloneable = true; intel_sdvo_connector_init(intel_sdvo_connector, intel_sdvo); if (!intel_sdvo_create_enhance_property(intel_sdvo, intel_sdvo_connector)) diff --git a/drivers/gpu/drm/i915/intel_tv.c b/drivers/gpu/drm/i915/intel_tv.c index befce6c..1a0bab0 100644 --- a/drivers/gpu/drm/i915/intel_tv.c +++ b/drivers/gpu/drm/i915/intel_tv.c @@ -1622,7 +1622,7 @@ intel_tv_init(struct drm_device *dev) intel_connector_attach_encoder(intel_connector, intel_encoder); intel_encoder->type = INTEL_OUTPUT_TVOUT; intel_encoder->crtc_mask = (1 << 0) | (1 << 1); - intel_encoder->clone_mask = (1 << INTEL_TV_CLONE_BIT); + intel_encoder->cloneable = false; intel_encoder->base.possible_crtcs = ((1 << 0) | (1 << 1)); intel_encoder->base.possible_clones = (1 << INTEL_OUTPUT_TVOUT); intel_tv->type = DRM_MODE_CONNECTOR_Unknown; -- cgit v0.10.2 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 diff --git a/drivers/gpu/drm/i915/intel_ddi.c b/drivers/gpu/drm/i915/intel_ddi.c index 933c748..32604ac 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", diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 5c0a726..e86fd39 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -6798,16 +6798,16 @@ static void intel_setup_outputs(struct drm_device *dev) /* PCH SDVOB multiplex with HDMIB */ found = intel_sdvo_init(dev, PCH_SDVOB, true); if (!found) - intel_hdmi_init(dev, HDMIB); + intel_hdmi_init(dev, HDMIB, PORT_B); if (!found && (I915_READ(PCH_DP_B) & DP_DETECTED)) intel_dp_init(dev, PCH_DP_B); } if (I915_READ(HDMIC) & PORT_DETECTED) - intel_hdmi_init(dev, HDMIC); + intel_hdmi_init(dev, HDMIC, PORT_C); if (!dpd_is_edp && I915_READ(HDMID) & PORT_DETECTED) - intel_hdmi_init(dev, HDMID); + intel_hdmi_init(dev, HDMID, PORT_D); if (I915_READ(PCH_DP_C) & DP_DETECTED) intel_dp_init(dev, PCH_DP_C); @@ -6821,13 +6821,13 @@ static void intel_setup_outputs(struct drm_device *dev) /* SDVOB multiplex with HDMIB */ found = intel_sdvo_init(dev, SDVOB, true); if (!found) - intel_hdmi_init(dev, SDVOB); + intel_hdmi_init(dev, SDVOB, PORT_B); if (!found && (I915_READ(DP_B) & DP_DETECTED)) intel_dp_init(dev, DP_B); } if (I915_READ(SDVOC) & PORT_DETECTED) - intel_hdmi_init(dev, SDVOC); + intel_hdmi_init(dev, SDVOC, PORT_C); /* Shares lanes with HDMI on SDVOC */ if (I915_READ(DP_C) & DP_DETECTED) @@ -6840,7 +6840,7 @@ static void intel_setup_outputs(struct drm_device *dev) found = intel_sdvo_init(dev, SDVOB, true); if (!found && SUPPORTS_INTEGRATED_HDMI(dev)) { DRM_DEBUG_KMS("probing HDMI on SDVOB\n"); - intel_hdmi_init(dev, SDVOB); + intel_hdmi_init(dev, SDVOB, PORT_B); } if (!found && SUPPORTS_INTEGRATED_DP(dev)) { @@ -6860,7 +6860,7 @@ static void intel_setup_outputs(struct drm_device *dev) if (SUPPORTS_INTEGRATED_HDMI(dev)) { DRM_DEBUG_KMS("probing HDMI on SDVOC\n"); - intel_hdmi_init(dev, SDVOC); + intel_hdmi_init(dev, SDVOC, PORT_C); } if (SUPPORTS_INTEGRATED_DP(dev)) { DRM_DEBUG_KMS("probing DP_C\n"); diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index 76ba554..d234f1a 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -364,7 +364,8 @@ extern void intel_attach_force_audio_property(struct drm_connector *connector); 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); +extern void intel_hdmi_init(struct drm_device *dev, + int sdvox_reg, enum port port); extern struct intel_hdmi *enc_to_intel_hdmi(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, diff --git a/drivers/gpu/drm/i915/intel_hdmi.c b/drivers/gpu/drm/i915/intel_hdmi.c index 593b8fe..e4c37bb 100644 --- a/drivers/gpu/drm/i915/intel_hdmi.c +++ b/drivers/gpu/drm/i915/intel_hdmi.c @@ -889,7 +889,7 @@ intel_hdmi_add_properties(struct intel_hdmi *intel_hdmi, struct drm_connector *c intel_attach_broadcast_rgb_property(connector); } -void intel_hdmi_init(struct drm_device *dev, int sdvox_reg) +void intel_hdmi_init(struct drm_device *dev, int sdvox_reg, enum port port) { struct drm_i915_private *dev_priv = dev->dev_private; struct drm_connector *connector; @@ -925,40 +925,23 @@ void intel_hdmi_init(struct drm_device *dev, int sdvox_reg) intel_encoder->cloneable = false; - /* Set up the DDC bus. */ - if (sdvox_reg == SDVOB) { + intel_hdmi->ddi_port = port; + switch (port) { + case PORT_B: intel_hdmi->ddc_bus = GMBUS_PORT_DPB; dev_priv->hotplug_supported_mask |= HDMIB_HOTPLUG_INT_STATUS; - } else if (sdvox_reg == SDVOC) { - intel_hdmi->ddc_bus = GMBUS_PORT_DPC; - dev_priv->hotplug_supported_mask |= HDMIC_HOTPLUG_INT_STATUS; - } else if (sdvox_reg == HDMIB) { - intel_hdmi->ddc_bus = GMBUS_PORT_DPB; - dev_priv->hotplug_supported_mask |= HDMIB_HOTPLUG_INT_STATUS; - } else if (sdvox_reg == HDMIC) { - intel_hdmi->ddc_bus = GMBUS_PORT_DPC; - dev_priv->hotplug_supported_mask |= HDMIC_HOTPLUG_INT_STATUS; - } else if (sdvox_reg == HDMID) { - 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_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"); + break; + case PORT_C: 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"); + break; + case PORT_D: 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 */ + break; + case PORT_A: + /* Internal port only for eDP. */ + default: BUG(); } -- cgit v0.10.2 From 47ea7542a1ac33ba9f15608d2fca00abcc1c11e5 Mon Sep 17 00:00:00 2001 From: Paulo Zanoni Date: Tue, 17 Jul 2012 16:55:16 -0300 Subject: drm/i915: move common code to intel_dp_set_link_train We have some common code that we always run before calling intel_dp_set_link_train. This common code sets the correct training patterns to the DP variable. If we add more calls to intel_dp_set_link_train, we'll also have to duplicate this common code. So instead of repeating this code whenever we call intel_dp_set_link_train, we move the code to inside the function: now we check which training pattern we're going to set and then we set the DP register according to it. One of the side-effects of this change is that now we never forget to mask the training pattern bits before changing them. It looks like this was working before because we were first masking the bits, then writing 00, 01 and then 11. This patch also enables us to use the intel_dp_set_link_train function when disabling link training: in this case we need to avoid writing the DP_TRAINING_LANE*_SET AUX commands. As a bonus, the big intel_dp_{start,complete}_link_train functions will get smaller and a little bit easier to read. Version 2 changes: - Rewrite commit message. - Also clear the training pattern bits before changing them. Signed-off-by: Paulo Zanoni Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index d1489ab..61400c1 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -1636,6 +1636,45 @@ intel_dp_set_link_train(struct intel_dp *intel_dp, struct drm_i915_private *dev_priv = dev->dev_private; int ret; + if (HAS_PCH_CPT(dev) && (IS_GEN7(dev) || !is_cpu_edp(intel_dp))) { + dp_reg_value &= ~DP_LINK_TRAIN_MASK_CPT; + + switch (dp_train_pat & DP_TRAINING_PATTERN_MASK) { + case DP_TRAINING_PATTERN_DISABLE: + dp_reg_value |= DP_LINK_TRAIN_OFF_CPT; + break; + case DP_TRAINING_PATTERN_1: + dp_reg_value |= DP_LINK_TRAIN_PAT_1_CPT; + break; + case DP_TRAINING_PATTERN_2: + dp_reg_value |= DP_LINK_TRAIN_PAT_2_CPT; + break; + case DP_TRAINING_PATTERN_3: + DRM_ERROR("DP training pattern 3 not supported\n"); + dp_reg_value |= DP_LINK_TRAIN_PAT_2_CPT; + break; + } + + } else { + dp_reg_value &= ~DP_LINK_TRAIN_MASK; + + switch (dp_train_pat & DP_TRAINING_PATTERN_MASK) { + case DP_TRAINING_PATTERN_DISABLE: + dp_reg_value |= DP_LINK_TRAIN_OFF; + break; + case DP_TRAINING_PATTERN_1: + dp_reg_value |= DP_LINK_TRAIN_PAT_1; + break; + case DP_TRAINING_PATTERN_2: + dp_reg_value |= DP_LINK_TRAIN_PAT_2; + break; + case DP_TRAINING_PATTERN_3: + DRM_ERROR("DP training pattern 3 not supported\n"); + dp_reg_value |= DP_LINK_TRAIN_PAT_2; + break; + } + } + I915_WRITE(intel_dp->output_reg, dp_reg_value); POSTING_READ(intel_dp->output_reg); @@ -1643,12 +1682,15 @@ intel_dp_set_link_train(struct intel_dp *intel_dp, DP_TRAINING_PATTERN_SET, dp_train_pat); - ret = intel_dp_aux_native_write(intel_dp, - DP_TRAINING_LANE0_SET, - intel_dp->train_set, - intel_dp->lane_count); - if (ret != intel_dp->lane_count) - return false; + if ((dp_train_pat & DP_TRAINING_PATTERN_MASK) != + DP_TRAINING_PATTERN_DISABLE) { + ret = intel_dp_aux_native_write(intel_dp, + DP_TRAINING_LANE0_SET, + intel_dp->train_set, + intel_dp->lane_count); + if (ret != intel_dp->lane_count) + return false; + } return true; } @@ -1664,7 +1706,6 @@ intel_dp_start_link_train(struct intel_dp *intel_dp) uint8_t voltage; bool clock_recovery = false; int voltage_tries, loop_tries; - u32 reg; uint32_t DP = intel_dp->DP; /* @@ -1685,10 +1726,6 @@ intel_dp_start_link_train(struct intel_dp *intel_dp) DP |= DP_PORT_EN; - if (HAS_PCH_CPT(dev) && (IS_GEN7(dev) || !is_cpu_edp(intel_dp))) - DP &= ~DP_LINK_TRAIN_MASK_CPT; - else - DP &= ~DP_LINK_TRAIN_MASK; memset(intel_dp->train_set, 0, 4); voltage = 0xff; voltage_tries = 0; @@ -1712,12 +1749,7 @@ intel_dp_start_link_train(struct intel_dp *intel_dp) DP = (DP & ~(DP_VOLTAGE_MASK|DP_PRE_EMPHASIS_MASK)) | signal_levels; } - if (HAS_PCH_CPT(dev) && (IS_GEN7(dev) || !is_cpu_edp(intel_dp))) - reg = DP | DP_LINK_TRAIN_PAT_1_CPT; - else - reg = DP | DP_LINK_TRAIN_PAT_1; - - if (!intel_dp_set_link_train(intel_dp, reg, + if (!intel_dp_set_link_train(intel_dp, DP, DP_TRAINING_PATTERN_1 | DP_LINK_SCRAMBLING_DISABLE)) break; @@ -1772,10 +1804,8 @@ static void intel_dp_complete_link_train(struct intel_dp *intel_dp) { struct drm_device *dev = intel_dp->base.base.dev; - struct drm_i915_private *dev_priv = dev->dev_private; bool channel_eq = false; int tries, cr_tries; - u32 reg; uint32_t DP = intel_dp->DP; /* channel equalization */ @@ -1804,13 +1834,8 @@ intel_dp_complete_link_train(struct intel_dp *intel_dp) DP = (DP & ~(DP_VOLTAGE_MASK|DP_PRE_EMPHASIS_MASK)) | signal_levels; } - if (HAS_PCH_CPT(dev) && (IS_GEN7(dev) || !is_cpu_edp(intel_dp))) - reg = DP | DP_LINK_TRAIN_PAT_2_CPT; - else - reg = DP | DP_LINK_TRAIN_PAT_2; - /* channel eq pattern */ - if (!intel_dp_set_link_train(intel_dp, reg, + if (!intel_dp_set_link_train(intel_dp, DP, DP_TRAINING_PATTERN_2 | DP_LINK_SCRAMBLING_DISABLE)) break; @@ -1845,15 +1870,7 @@ intel_dp_complete_link_train(struct intel_dp *intel_dp) ++tries; } - if (HAS_PCH_CPT(dev) && (IS_GEN7(dev) || !is_cpu_edp(intel_dp))) - reg = DP | DP_LINK_TRAIN_OFF_CPT; - else - reg = DP | DP_LINK_TRAIN_OFF; - - I915_WRITE(intel_dp->output_reg, reg); - POSTING_READ(intel_dp->output_reg); - intel_dp_aux_native_write_1(intel_dp, - DP_TRAINING_PATTERN_SET, DP_TRAINING_PATTERN_DISABLE); + intel_dp_set_link_train(intel_dp, DP, DP_TRAINING_PATTERN_DISABLE); } static void -- cgit v0.10.2 From ab9d7c302af858e1bc8f613c3a6f1eea3c4c0364 Mon Sep 17 00:00:00 2001 From: Paulo Zanoni Date: Tue, 17 Jul 2012 17:53:45 -0300 Subject: drm/i915: add port field to struct intel_dp and use it This will be needed for Haswell, but already has its uses here. This patch started as a small patch written patch by Shobhit Kumar, but it has changed so much that none of its original lines remain. Credits-to: Shobhit Kumar Signed-off-by: Paulo Zanoni Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index e86fd39..f8c2aa1 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -6764,10 +6764,10 @@ static void intel_setup_outputs(struct drm_device *dev) dpd_is_edp = intel_dpd_is_edp(dev); if (has_edp_a(dev)) - intel_dp_init(dev, DP_A); + intel_dp_init(dev, DP_A, PORT_A); if (dpd_is_edp && (I915_READ(PCH_DP_D) & DP_DETECTED)) - intel_dp_init(dev, PCH_DP_D); + intel_dp_init(dev, PCH_DP_D, PORT_D); } intel_crt_init(dev); @@ -6800,7 +6800,7 @@ static void intel_setup_outputs(struct drm_device *dev) if (!found) intel_hdmi_init(dev, HDMIB, PORT_B); if (!found && (I915_READ(PCH_DP_B) & DP_DETECTED)) - intel_dp_init(dev, PCH_DP_B); + intel_dp_init(dev, PCH_DP_B, PORT_B); } if (I915_READ(HDMIC) & PORT_DETECTED) @@ -6810,10 +6810,10 @@ static void intel_setup_outputs(struct drm_device *dev) intel_hdmi_init(dev, HDMID, PORT_D); if (I915_READ(PCH_DP_C) & DP_DETECTED) - intel_dp_init(dev, PCH_DP_C); + intel_dp_init(dev, PCH_DP_C, PORT_C); if (!dpd_is_edp && (I915_READ(PCH_DP_D) & DP_DETECTED)) - intel_dp_init(dev, PCH_DP_D); + intel_dp_init(dev, PCH_DP_D, PORT_D); } else if (IS_VALLEYVIEW(dev)) { int found; @@ -6823,7 +6823,7 @@ static void intel_setup_outputs(struct drm_device *dev) if (!found) intel_hdmi_init(dev, SDVOB, PORT_B); if (!found && (I915_READ(DP_B) & DP_DETECTED)) - intel_dp_init(dev, DP_B); + intel_dp_init(dev, DP_B, PORT_B); } if (I915_READ(SDVOC) & PORT_DETECTED) @@ -6831,7 +6831,7 @@ static void intel_setup_outputs(struct drm_device *dev) /* Shares lanes with HDMI on SDVOC */ if (I915_READ(DP_C) & DP_DETECTED) - intel_dp_init(dev, DP_C); + intel_dp_init(dev, DP_C, PORT_C); } else if (SUPPORTS_DIGITAL_OUTPUTS(dev)) { bool found = false; @@ -6845,7 +6845,7 @@ static void intel_setup_outputs(struct drm_device *dev) if (!found && SUPPORTS_INTEGRATED_DP(dev)) { DRM_DEBUG_KMS("probing DP_B\n"); - intel_dp_init(dev, DP_B); + intel_dp_init(dev, DP_B, PORT_B); } } @@ -6864,14 +6864,14 @@ static void intel_setup_outputs(struct drm_device *dev) } if (SUPPORTS_INTEGRATED_DP(dev)) { DRM_DEBUG_KMS("probing DP_C\n"); - intel_dp_init(dev, DP_C); + intel_dp_init(dev, DP_C, PORT_C); } } if (SUPPORTS_INTEGRATED_DP(dev) && (I915_READ(DP_D) & DP_DETECTED)) { DRM_DEBUG_KMS("probing DP_D\n"); - intel_dp_init(dev, DP_D); + intel_dp_init(dev, DP_D, PORT_D); } } else if (IS_GEN2(dev)) intel_dvo_init(dev); diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index 61400c1..ad90a49 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -2426,7 +2426,7 @@ intel_dp_add_properties(struct intel_dp *intel_dp, struct drm_connector *connect } void -intel_dp_init(struct drm_device *dev, int output_reg) +intel_dp_init(struct drm_device *dev, int output_reg, enum port port) { struct drm_i915_private *dev_priv = dev->dev_private; struct drm_connector *connector; @@ -2441,6 +2441,7 @@ intel_dp_init(struct drm_device *dev, int output_reg) return; intel_dp->output_reg = output_reg; + intel_dp->port = port; intel_dp->dpms_mode = -1; intel_connector = kzalloc(sizeof(struct intel_connector), GFP_KERNEL); @@ -2486,28 +2487,25 @@ intel_dp_init(struct drm_device *dev, int output_reg) drm_sysfs_connector_add(connector); /* Set up the DDC bus. */ - switch (output_reg) { - case DP_A: - name = "DPDDC-A"; - break; - case DP_B: - case PCH_DP_B: - dev_priv->hotplug_supported_mask |= - DPB_HOTPLUG_INT_STATUS; - name = "DPDDC-B"; - break; - case DP_C: - case PCH_DP_C: - dev_priv->hotplug_supported_mask |= - DPC_HOTPLUG_INT_STATUS; - name = "DPDDC-C"; - break; - case DP_D: - case PCH_DP_D: - dev_priv->hotplug_supported_mask |= - DPD_HOTPLUG_INT_STATUS; - name = "DPDDC-D"; - break; + switch (port) { + case PORT_A: + name = "DPDDC-A"; + break; + case PORT_B: + dev_priv->hotplug_supported_mask |= DPB_HOTPLUG_INT_STATUS; + name = "DPDDC-B"; + break; + case PORT_C: + dev_priv->hotplug_supported_mask |= DPC_HOTPLUG_INT_STATUS; + name = "DPDDC-C"; + break; + case PORT_D: + dev_priv->hotplug_supported_mask |= DPD_HOTPLUG_INT_STATUS; + name = "DPDDC-D"; + break; + default: + WARN(1, "Invalid port %c\n", port_name(port)); + break; } intel_dp_i2c_init(intel_dp, intel_connector, name); diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index d234f1a..2846f5e 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -306,6 +306,7 @@ struct intel_dp { uint8_t link_configuration[DP_LINK_CONFIGURATION_SIZE]; bool has_audio; enum hdmi_force_audio force_audio; + enum port port; uint32_t color_range; int dpms_mode; uint8_t link_bw; @@ -375,7 +376,8 @@ extern void intel_tv_init(struct drm_device *dev); extern void intel_mark_busy(struct drm_device *dev, struct drm_i915_gem_object *obj); extern bool intel_lvds_init(struct drm_device *dev); -extern void intel_dp_init(struct drm_device *dev, int dp_reg); +extern void intel_dp_init(struct drm_device *dev, int output_reg, + enum port port); void intel_dp_set_m_n(struct drm_crtc *crtc, struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode); -- cgit v0.10.2 From 7434a255a5cf42819b7e42377f18aaa02f6be52b Mon Sep 17 00:00:00 2001 From: Thomas Richter Date: Wed, 18 Jul 2012 19:22:30 +0200 Subject: drm/i915: Support for ns2501-DVO This patch adds support for the ns2501 DVO, found in some older Fujitsu/Siemens Labtops. It is in the state of "works for me". Includes now proper DPMS support. Includes switching between resolutions - from 640x480 to 1024x768. Currently assumes that the native display resolution is 1024x768. The ns2501 seems to be rather critical - if the output PLL is not running, the chip doesn't seem to be clocked and then doesn't react on i2c messages. Thus, a quick'n-dirty trick ensures that the DVO is active before submitting any i2c messages to it. This is probably to be reviewed. Bugzilla: https://bugs.freedesktop.org/show_bug.cgi?id=17902 Signed-off-by: Thomas Richter [danvet: fixup whitespace fail.] Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/Makefile b/drivers/gpu/drm/i915/Makefile index b0bacdb..0f2c549 100644 --- a/drivers/gpu/drm/i915/Makefile +++ b/drivers/gpu/drm/i915/Makefile @@ -40,6 +40,7 @@ i915-y := i915_drv.o i915_dma.o i915_irq.o \ dvo_ivch.o \ dvo_tfp410.o \ dvo_sil164.o \ + dvo_ns2501.o \ i915_gem_dmabuf.o i915-$(CONFIG_COMPAT) += i915_ioc32.o diff --git a/drivers/gpu/drm/i915/dvo.h b/drivers/gpu/drm/i915/dvo.h index 5891469..0c8ac4d 100644 --- a/drivers/gpu/drm/i915/dvo.h +++ b/drivers/gpu/drm/i915/dvo.h @@ -140,5 +140,6 @@ extern struct intel_dvo_dev_ops ch7xxx_ops; extern struct intel_dvo_dev_ops ivch_ops; extern struct intel_dvo_dev_ops tfp410_ops; extern struct intel_dvo_dev_ops ch7017_ops; +extern struct intel_dvo_dev_ops ns2501_ops; #endif /* _INTEL_DVO_H */ diff --git a/drivers/gpu/drm/i915/dvo_ns2501.c b/drivers/gpu/drm/i915/dvo_ns2501.c new file mode 100644 index 0000000..1a0bad9 --- /dev/null +++ b/drivers/gpu/drm/i915/dvo_ns2501.c @@ -0,0 +1,582 @@ +/* + * + * Copyright (c) 2012 Gilles Dartiguelongue, Thomas Richter + * + * All Rights Reserved. + * + * 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, sub license, 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 NON-INFRINGEMENT. + * IN NO EVENT SHALL THE AUTHOR 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. + * + */ + +#include "dvo.h" +#include "i915_reg.h" +#include "i915_drv.h" + +#define NS2501_VID 0x1305 +#define NS2501_DID 0x6726 + +#define NS2501_VID_LO 0x00 +#define NS2501_VID_HI 0x01 +#define NS2501_DID_LO 0x02 +#define NS2501_DID_HI 0x03 +#define NS2501_REV 0x04 +#define NS2501_RSVD 0x05 +#define NS2501_FREQ_LO 0x06 +#define NS2501_FREQ_HI 0x07 + +#define NS2501_REG8 0x08 +#define NS2501_8_VEN (1<<5) +#define NS2501_8_HEN (1<<4) +#define NS2501_8_DSEL (1<<3) +#define NS2501_8_BPAS (1<<2) +#define NS2501_8_RSVD (1<<1) +#define NS2501_8_PD (1<<0) + +#define NS2501_REG9 0x09 +#define NS2501_9_VLOW (1<<7) +#define NS2501_9_MSEL_MASK (0x7<<4) +#define NS2501_9_TSEL (1<<3) +#define NS2501_9_RSEN (1<<2) +#define NS2501_9_RSVD (1<<1) +#define NS2501_9_MDI (1<<0) + +#define NS2501_REGC 0x0c + +struct ns2501_priv { + //I2CDevRec d; + bool quiet; + int reg_8_shadow; + int reg_8_set; + // Shadow registers for i915 + int dvoc; + int pll_a; + int srcdim; + int fw_blc; +}; + +#define NSPTR(d) ((NS2501Ptr)(d->DriverPrivate.ptr)) + +/* + * Include the PLL launcher prototype + */ +extern void intel_enable_pll(struct drm_i915_private *dev_priv, enum pipe pipe); + +/* + * For reasons unclear to me, the ns2501 at least on the Fujitsu/Siemens + * laptops does not react on the i2c bus unless + * both the PLL is running and the display is configured in its native + * resolution. + * This function forces the DVO on, and stores the registers it touches. + * Afterwards, registers are restored to regular values. + * + * This is pretty much a hack, though it works. + * Without that, ns2501_readb and ns2501_writeb fail + * when switching the resolution. + */ + +static void enable_dvo(struct intel_dvo_device *dvo) +{ + struct ns2501_priv *ns = (struct ns2501_priv *)(dvo->dev_priv); + struct i2c_adapter *adapter = dvo->i2c_bus; + struct intel_gmbus *bus = container_of(adapter, + struct intel_gmbus, + adapter); + struct drm_i915_private *dev_priv = bus->dev_priv; + + DRM_DEBUG_KMS("%s: Trying to re-enable the DVO\n", __FUNCTION__); + + ns->dvoc = I915_READ(DVO_C); + ns->pll_a = I915_READ(_DPLL_A); + ns->srcdim = I915_READ(DVOC_SRCDIM); + ns->fw_blc = I915_READ(FW_BLC); + + I915_WRITE(DVOC, 0x10004084); + I915_WRITE(_DPLL_A, 0xd0820000); + I915_WRITE(DVOC_SRCDIM, 0x400300); // 1024x768 + I915_WRITE(FW_BLC, 0x1080304); + + intel_enable_pll(dev_priv, 0); + + I915_WRITE(DVOC, 0x90004084); +} + +/* + * Restore the I915 registers modified by the above + * trigger function. + */ +static void restore_dvo(struct intel_dvo_device *dvo) +{ + struct i2c_adapter *adapter = dvo->i2c_bus; + struct intel_gmbus *bus = container_of(adapter, + struct intel_gmbus, + adapter); + struct drm_i915_private *dev_priv = bus->dev_priv; + struct ns2501_priv *ns = (struct ns2501_priv *)(dvo->dev_priv); + + I915_WRITE(DVOC, ns->dvoc); + I915_WRITE(_DPLL_A, ns->pll_a); + I915_WRITE(DVOC_SRCDIM, ns->srcdim); + I915_WRITE(FW_BLC, ns->fw_blc); +} + +/* +** Read a register from the ns2501. +** Returns true if successful, false otherwise. +** If it returns false, it might be wise to enable the +** DVO with the above function. +*/ +static bool ns2501_readb(struct intel_dvo_device *dvo, int addr, uint8_t * ch) +{ + struct ns2501_priv *ns = dvo->dev_priv; + struct i2c_adapter *adapter = dvo->i2c_bus; + u8 out_buf[2]; + u8 in_buf[2]; + + struct i2c_msg msgs[] = { + { + .addr = dvo->slave_addr, + .flags = 0, + .len = 1, + .buf = out_buf, + }, + { + .addr = dvo->slave_addr, + .flags = I2C_M_RD, + .len = 1, + .buf = in_buf, + } + }; + + out_buf[0] = addr; + out_buf[1] = 0; + + if (i2c_transfer(adapter, msgs, 2) == 2) { + *ch = in_buf[0]; + return true; + }; + + if (!ns->quiet) { + DRM_DEBUG_KMS + ("Unable to read register 0x%02x from %s:0x%02x.\n", addr, + adapter->name, dvo->slave_addr); + } + + return false; +} + +/* +** Write a register to the ns2501. +** Returns true if successful, false otherwise. +** If it returns false, it might be wise to enable the +** DVO with the above function. +*/ +static bool ns2501_writeb(struct intel_dvo_device *dvo, int addr, uint8_t ch) +{ + struct ns2501_priv *ns = dvo->dev_priv; + struct i2c_adapter *adapter = dvo->i2c_bus; + uint8_t out_buf[2]; + + struct i2c_msg msg = { + .addr = dvo->slave_addr, + .flags = 0, + .len = 2, + .buf = out_buf, + }; + + out_buf[0] = addr; + out_buf[1] = ch; + + if (i2c_transfer(adapter, &msg, 1) == 1) { + return true; + } + + if (!ns->quiet) { + DRM_DEBUG_KMS("Unable to write register 0x%02x to %s:%d\n", + addr, adapter->name, dvo->slave_addr); + } + + return false; +} + +/* National Semiconductor 2501 driver for chip on i2c bus + * scan for the chip on the bus. + * Hope the VBIOS initialized the PLL correctly so we can + * talk to it. If not, it will not be seen and not detected. + * Bummer! + */ +static bool ns2501_init(struct intel_dvo_device *dvo, + struct i2c_adapter *adapter) +{ + /* this will detect the NS2501 chip on the specified i2c bus */ + struct ns2501_priv *ns; + unsigned char ch; + + ns = kzalloc(sizeof(struct ns2501_priv), GFP_KERNEL); + if (ns == NULL) + return false; + + dvo->i2c_bus = adapter; + dvo->dev_priv = ns; + ns->quiet = true; + + if (!ns2501_readb(dvo, NS2501_VID_LO, &ch)) + goto out; + + if (ch != (NS2501_VID & 0xff)) { + DRM_DEBUG_KMS("ns2501 not detected got %d: from %s Slave %d.\n", + ch, adapter->name, dvo->slave_addr); + goto out; + } + + if (!ns2501_readb(dvo, NS2501_DID_LO, &ch)) + goto out; + + if (ch != (NS2501_DID & 0xff)) { + DRM_DEBUG_KMS("ns2501 not detected got %d: from %s Slave %d.\n", + ch, adapter->name, dvo->slave_addr); + goto out; + } + ns->quiet = false; + ns->reg_8_set = 0; + ns->reg_8_shadow = + NS2501_8_PD | NS2501_8_BPAS | NS2501_8_VEN | NS2501_8_HEN; + + DRM_DEBUG_KMS("init ns2501 dvo controller successfully!\n"); + return true; + +out: + kfree(ns); + return false; +} + +static enum drm_connector_status ns2501_detect(struct intel_dvo_device *dvo) +{ + /* + * This is a Laptop display, it doesn't have hotplugging. + * Even if not, the detection bit of the 2501 is unreliable as + * it only works for some display types. + * It is even more unreliable as the PLL must be active for + * allowing reading from the chiop. + */ + return connector_status_connected; +} + +static enum drm_mode_status ns2501_mode_valid(struct intel_dvo_device *dvo, + struct drm_display_mode *mode) +{ + DRM_DEBUG_KMS + ("%s: is mode valid (hdisplay=%d,htotal=%d,vdisplay=%d,vtotal=%d)\n", + __FUNCTION__, mode->hdisplay, mode->htotal, mode->vdisplay, + mode->vtotal); + + /* + * Currently, these are all the modes I have data from. + * More might exist. Unclear how to find the native resolution + * of the panel in here so we could always accept it + * by disabling the scaler. + */ + if ((mode->hdisplay == 800 && mode->vdisplay == 600) || + (mode->hdisplay == 640 && mode->vdisplay == 480) || + (mode->hdisplay == 1024 && mode->vdisplay == 768)) { + return MODE_OK; + } else { + return MODE_ONE_SIZE; /* Is this a reasonable error? */ + } +} + +static void ns2501_mode_set(struct intel_dvo_device *dvo, + struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + bool ok; + bool restore = false; + struct ns2501_priv *ns = (struct ns2501_priv *)(dvo->dev_priv); + + DRM_DEBUG_KMS + ("%s: set mode (hdisplay=%d,htotal=%d,vdisplay=%d,vtotal=%d).\n", + __FUNCTION__, mode->hdisplay, mode->htotal, mode->vdisplay, + mode->vtotal); + + /* + * Where do I find the native resolution for which scaling is not required??? + * + * First trigger the DVO on as otherwise the chip does not appear on the i2c + * bus. + */ + do { + ok = true; + + if (mode->hdisplay == 800 && mode->vdisplay == 600) { + /* mode 277 */ + ns->reg_8_shadow &= ~NS2501_8_BPAS; + DRM_DEBUG_KMS("%s: switching to 800x600\n", + __FUNCTION__); + + /* + * No, I do not know where this data comes from. + * It is just what the video bios left in the DVO, so + * I'm just copying it here over. + * This also means that I cannot support any other modes + * except the ones supported by the bios. + */ + ok &= ns2501_writeb(dvo, 0x11, 0xc8); // 0xc7 also works. + ok &= ns2501_writeb(dvo, 0x1b, 0x19); + ok &= ns2501_writeb(dvo, 0x1c, 0x62); // VBIOS left 0x64 here, but 0x62 works nicer + ok &= ns2501_writeb(dvo, 0x1d, 0x02); + + ok &= ns2501_writeb(dvo, 0x34, 0x03); + ok &= ns2501_writeb(dvo, 0x35, 0xff); + + ok &= ns2501_writeb(dvo, 0x80, 0x27); + ok &= ns2501_writeb(dvo, 0x81, 0x03); + ok &= ns2501_writeb(dvo, 0x82, 0x41); + ok &= ns2501_writeb(dvo, 0x83, 0x05); + + ok &= ns2501_writeb(dvo, 0x8d, 0x02); + ok &= ns2501_writeb(dvo, 0x8e, 0x04); + ok &= ns2501_writeb(dvo, 0x8f, 0x00); + + ok &= ns2501_writeb(dvo, 0x90, 0xfe); /* vertical. VBIOS left 0xff here, but 0xfe works better */ + ok &= ns2501_writeb(dvo, 0x91, 0x07); + ok &= ns2501_writeb(dvo, 0x94, 0x00); + ok &= ns2501_writeb(dvo, 0x95, 0x00); + + ok &= ns2501_writeb(dvo, 0x96, 0x00); + + ok &= ns2501_writeb(dvo, 0x99, 0x00); + ok &= ns2501_writeb(dvo, 0x9a, 0x88); + + ok &= ns2501_writeb(dvo, 0x9c, 0x23); /* Looks like first and last line of the image. */ + ok &= ns2501_writeb(dvo, 0x9d, 0x00); + ok &= ns2501_writeb(dvo, 0x9e, 0x25); + ok &= ns2501_writeb(dvo, 0x9f, 0x03); + + ok &= ns2501_writeb(dvo, 0xa4, 0x80); + + ok &= ns2501_writeb(dvo, 0xb6, 0x00); + + ok &= ns2501_writeb(dvo, 0xb9, 0xc8); /* horizontal? */ + ok &= ns2501_writeb(dvo, 0xba, 0x00); /* horizontal? */ + + ok &= ns2501_writeb(dvo, 0xc0, 0x05); /* horizontal? */ + ok &= ns2501_writeb(dvo, 0xc1, 0xd7); + + ok &= ns2501_writeb(dvo, 0xc2, 0x00); + ok &= ns2501_writeb(dvo, 0xc3, 0xf8); + + ok &= ns2501_writeb(dvo, 0xc4, 0x03); + ok &= ns2501_writeb(dvo, 0xc5, 0x1a); + + ok &= ns2501_writeb(dvo, 0xc6, 0x00); + ok &= ns2501_writeb(dvo, 0xc7, 0x73); + ok &= ns2501_writeb(dvo, 0xc8, 0x02); + + } else if (mode->hdisplay == 640 && mode->vdisplay == 480) { + /* mode 274 */ + DRM_DEBUG_KMS("%s: switching to 640x480\n", + __FUNCTION__); + /* + * No, I do not know where this data comes from. + * It is just what the video bios left in the DVO, so + * I'm just copying it here over. + * This also means that I cannot support any other modes + * except the ones supported by the bios. + */ + ns->reg_8_shadow &= ~NS2501_8_BPAS; + + ok &= ns2501_writeb(dvo, 0x11, 0xa0); + ok &= ns2501_writeb(dvo, 0x1b, 0x11); + ok &= ns2501_writeb(dvo, 0x1c, 0x54); + ok &= ns2501_writeb(dvo, 0x1d, 0x03); + + ok &= ns2501_writeb(dvo, 0x34, 0x03); + ok &= ns2501_writeb(dvo, 0x35, 0xff); + + ok &= ns2501_writeb(dvo, 0x80, 0xff); + ok &= ns2501_writeb(dvo, 0x81, 0x07); + ok &= ns2501_writeb(dvo, 0x82, 0x3d); + ok &= ns2501_writeb(dvo, 0x83, 0x05); + + ok &= ns2501_writeb(dvo, 0x8d, 0x02); + ok &= ns2501_writeb(dvo, 0x8e, 0x10); + ok &= ns2501_writeb(dvo, 0x8f, 0x00); + + ok &= ns2501_writeb(dvo, 0x90, 0xff); /* vertical */ + ok &= ns2501_writeb(dvo, 0x91, 0x07); + ok &= ns2501_writeb(dvo, 0x94, 0x00); + ok &= ns2501_writeb(dvo, 0x95, 0x00); + + ok &= ns2501_writeb(dvo, 0x96, 0x05); + + ok &= ns2501_writeb(dvo, 0x99, 0x00); + ok &= ns2501_writeb(dvo, 0x9a, 0x88); + + ok &= ns2501_writeb(dvo, 0x9c, 0x24); + ok &= ns2501_writeb(dvo, 0x9d, 0x00); + ok &= ns2501_writeb(dvo, 0x9e, 0x25); + ok &= ns2501_writeb(dvo, 0x9f, 0x03); + + ok &= ns2501_writeb(dvo, 0xa4, 0x84); + + ok &= ns2501_writeb(dvo, 0xb6, 0x09); + + ok &= ns2501_writeb(dvo, 0xb9, 0xa0); /* horizontal? */ + ok &= ns2501_writeb(dvo, 0xba, 0x00); /* horizontal? */ + + ok &= ns2501_writeb(dvo, 0xc0, 0x05); /* horizontal? */ + ok &= ns2501_writeb(dvo, 0xc1, 0x90); + + ok &= ns2501_writeb(dvo, 0xc2, 0x00); + ok &= ns2501_writeb(dvo, 0xc3, 0x0f); + + ok &= ns2501_writeb(dvo, 0xc4, 0x03); + ok &= ns2501_writeb(dvo, 0xc5, 0x16); + + ok &= ns2501_writeb(dvo, 0xc6, 0x00); + ok &= ns2501_writeb(dvo, 0xc7, 0x02); + ok &= ns2501_writeb(dvo, 0xc8, 0x02); + + } else if (mode->hdisplay == 1024 && mode->vdisplay == 768) { + /* mode 280 */ + DRM_DEBUG_KMS("%s: switching to 1024x768\n", + __FUNCTION__); + /* + * This might or might not work, actually. I'm silently + * assuming here that the native panel resolution is + * 1024x768. If not, then this leaves the scaler disabled + * generating a picture that is likely not the expected. + * + * Problem is that I do not know where to take the panel + * dimensions from. + * + * Enable the bypass, scaling not required. + * + * The scaler registers are irrelevant here.... + * + */ + ns->reg_8_shadow |= NS2501_8_BPAS; + ok &= ns2501_writeb(dvo, 0x37, 0x44); + } else { + /* + * Data not known. Bummer! + * Hopefully, the code should not go here + * as mode_OK delivered no other modes. + */ + ns->reg_8_shadow |= NS2501_8_BPAS; + } + ok &= ns2501_writeb(dvo, NS2501_REG8, ns->reg_8_shadow); + + if (!ok) { + if (restore) + restore_dvo(dvo); + enable_dvo(dvo); + restore = true; + } + } while (!ok); + /* + * Restore the old i915 registers before + * forcing the ns2501 on. + */ + if (restore) + restore_dvo(dvo); +} + +/* set the NS2501 power state */ +static void ns2501_dpms(struct intel_dvo_device *dvo, int mode) +{ + bool ok; + bool restore = false; + struct ns2501_priv *ns = (struct ns2501_priv *)(dvo->dev_priv); + unsigned char ch; + + DRM_DEBUG_KMS("%s: Trying set the dpms of the DVO to %d\n", + __FUNCTION__, mode); + + ch = ns->reg_8_shadow; + + if (mode == DRM_MODE_DPMS_ON) + ch |= NS2501_8_PD; + else + ch &= ~NS2501_8_PD; + + if (ns->reg_8_set == 0 || ns->reg_8_shadow != ch) { + ns->reg_8_set = 1; + ns->reg_8_shadow = ch; + + do { + ok = true; + ok &= ns2501_writeb(dvo, NS2501_REG8, ch); + ok &= + ns2501_writeb(dvo, 0x34, + (mode == + DRM_MODE_DPMS_ON) ? (0x03) : (0x00)); + ok &= + ns2501_writeb(dvo, 0x35, + (mode == + DRM_MODE_DPMS_ON) ? (0xff) : (0x00)); + if (!ok) { + if (restore) + restore_dvo(dvo); + enable_dvo(dvo); + restore = true; + } + } while (!ok); + + if (restore) + restore_dvo(dvo); + } +} + +static void ns2501_dump_regs(struct intel_dvo_device *dvo) +{ + uint8_t val; + + ns2501_readb(dvo, NS2501_FREQ_LO, &val); + DRM_LOG_KMS("NS2501_FREQ_LO: 0x%02x\n", val); + ns2501_readb(dvo, NS2501_FREQ_HI, &val); + DRM_LOG_KMS("NS2501_FREQ_HI: 0x%02x\n", val); + ns2501_readb(dvo, NS2501_REG8, &val); + DRM_LOG_KMS("NS2501_REG8: 0x%02x\n", val); + ns2501_readb(dvo, NS2501_REG9, &val); + DRM_LOG_KMS("NS2501_REG9: 0x%02x\n", val); + ns2501_readb(dvo, NS2501_REGC, &val); + DRM_LOG_KMS("NS2501_REGC: 0x%02x\n", val); +} + +static void ns2501_destroy(struct intel_dvo_device *dvo) +{ + struct ns2501_priv *ns = dvo->dev_priv; + + if (ns) { + kfree(ns); + dvo->dev_priv = NULL; + } +} + +struct intel_dvo_dev_ops ns2501_ops = { + .init = ns2501_init, + .detect = ns2501_detect, + .mode_valid = ns2501_mode_valid, + .mode_set = ns2501_mode_set, + .dpms = ns2501_dpms, + .dump_regs = ns2501_dump_regs, + .destroy = ns2501_destroy, +}; diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index f8c2aa1..93d9934 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -1428,8 +1428,10 @@ static void assert_pch_ports_disabled(struct drm_i915_private *dev_priv, * protect mechanism may be enabled. * * Note! This is for pre-ILK only. + * + * Unfortunately needed by dvo_ns2501 since the dvo depends on it running. */ -static void intel_enable_pll(struct drm_i915_private *dev_priv, enum pipe pipe) +void intel_enable_pll(struct drm_i915_private *dev_priv, enum pipe pipe) { int reg; u32 val; diff --git a/drivers/gpu/drm/i915/intel_dvo.c b/drivers/gpu/drm/i915/intel_dvo.c index 556cf6b..03dfdff 100644 --- a/drivers/gpu/drm/i915/intel_dvo.c +++ b/drivers/gpu/drm/i915/intel_dvo.c @@ -37,6 +37,7 @@ #define SIL164_ADDR 0x38 #define CH7xxx_ADDR 0x76 #define TFP410_ADDR 0x38 +#define NS2501_ADDR 0x38 static const struct intel_dvo_device intel_dvo_devices[] = { { @@ -74,7 +75,14 @@ static const struct intel_dvo_device intel_dvo_devices[] = { .slave_addr = 0x75, .gpio = GMBUS_PORT_DPB, .dev_ops = &ch7017_ops, - } + }, + { + .type = INTEL_DVO_CHIP_TMDS, + .name = "ns2501", + .dvo_reg = DVOC, + .slave_addr = NS2501_ADDR, + .dev_ops = &ns2501_ops, + } }; struct intel_dvo { -- cgit v0.10.2 From 2b860db67ff3ce886fcb35289a9283a041cce6a4 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Wed, 18 Jul 2012 20:03:05 +0200 Subject: drm/i915: Reserve ioctl numbers for set/get_caching I'm planing to merge this next week for 3.7, but I'd like to avoid stupid conflicts with the exsting userspace when merging the new reg_read ioctl (which doesn't have userspace yet, but this caching interface has). Header extracted from Chris Wilson's patch, but fix up the copy&pasted comment in the interface struct. Signed-off-by: Chris Wilson Signed-off-by: Daniel Vetter diff --git a/include/drm/i915_drm.h b/include/drm/i915_drm.h index 8cc7083..95648ab 100644 --- a/include/drm/i915_drm.h +++ b/include/drm/i915_drm.h @@ -203,6 +203,8 @@ typedef struct _drm_i915_sarea { #define DRM_I915_GEM_WAIT 0x2c #define DRM_I915_GEM_CONTEXT_CREATE 0x2d #define DRM_I915_GEM_CONTEXT_DESTROY 0x2e +#define DRM_I915_GEM_SET_CACHEING 0x2f +#define DRM_I915_GEM_GET_CACHEING 0x30 #define DRM_IOCTL_I915_INIT DRM_IOW( DRM_COMMAND_BASE + DRM_I915_INIT, drm_i915_init_t) #define DRM_IOCTL_I915_FLUSH DRM_IO ( DRM_COMMAND_BASE + DRM_I915_FLUSH) @@ -227,6 +229,8 @@ typedef struct _drm_i915_sarea { #define DRM_IOCTL_I915_GEM_PIN DRM_IOWR(DRM_COMMAND_BASE + DRM_I915_GEM_PIN, struct drm_i915_gem_pin) #define DRM_IOCTL_I915_GEM_UNPIN DRM_IOW(DRM_COMMAND_BASE + DRM_I915_GEM_UNPIN, struct drm_i915_gem_unpin) #define DRM_IOCTL_I915_GEM_BUSY DRM_IOWR(DRM_COMMAND_BASE + DRM_I915_GEM_BUSY, struct drm_i915_gem_busy) +#define DRM_IOCTL_I915_GEM_SET_CACHEING DRM_IOW(DRM_COMMAND_BASE + DRM_I915_GEM_SET_CACHEING, struct drm_i915_gem_cacheing) +#define DRM_IOCTL_I915_GEM_GET_CACHEING DRM_IOWR(DRM_COMMAND_BASE + DRM_I915_GEM_GET_CACHEING, struct drm_i915_gem_cacheing) #define DRM_IOCTL_I915_GEM_THROTTLE DRM_IO ( DRM_COMMAND_BASE + DRM_I915_GEM_THROTTLE) #define DRM_IOCTL_I915_GEM_ENTERVT DRM_IO(DRM_COMMAND_BASE + DRM_I915_GEM_ENTERVT) #define DRM_IOCTL_I915_GEM_LEAVEVT DRM_IO(DRM_COMMAND_BASE + DRM_I915_GEM_LEAVEVT) @@ -702,6 +706,17 @@ struct drm_i915_gem_busy { __u32 busy; }; +#define I915_CACHEING_NONE 0 +#define I915_CACHEING_CACHED 1 + +struct drm_i915_gem_cacheing { + /** Handle of the buffer to check for busy */ + __u32 handle; + + /** Cacheing level to apply or return value */ + __u32 cacheing; +}; + #define I915_TILING_NONE 0 #define I915_TILING_X 1 #define I915_TILING_Y 2 -- cgit v0.10.2 From c0c7babc48c4f6943ed3070d04630ea3ac9272ee Mon Sep 17 00:00:00 2001 From: Ben Widawsky Date: Thu, 12 Jul 2012 11:01:05 -0700 Subject: drm/i915: add register read IOCTL The interface's immediate purpose is to do synchronous timestamp queries as required by GL_TIMESTAMP. The GPU has a register for reading the timestamp but because that would normally require root access through libpciaccess, the IOCTL can provide this service instead. Currently the implementation whitelists only the render ring timestamp register, because that is the only thing we need to expose at this time. v2: make size implicit based on the register offset Add a generation check Reviewed-by: Eric Anholt Cc: Jacek Lawrynowicz Signed-off-by: Ben Widawsky [danvet: fixup the ioctl numerb:] Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/i915_dma.c b/drivers/gpu/drm/i915/i915_dma.c index 9cf7dfe..733744f 100644 --- a/drivers/gpu/drm/i915/i915_dma.c +++ b/drivers/gpu/drm/i915/i915_dma.c @@ -1857,6 +1857,7 @@ struct drm_ioctl_desc i915_ioctls[] = { DRM_IOCTL_DEF_DRV(I915_GEM_WAIT, i915_gem_wait_ioctl, DRM_AUTH|DRM_UNLOCKED), DRM_IOCTL_DEF_DRV(I915_GEM_CONTEXT_CREATE, i915_gem_context_create_ioctl, DRM_UNLOCKED), DRM_IOCTL_DEF_DRV(I915_GEM_CONTEXT_DESTROY, i915_gem_context_destroy_ioctl, DRM_UNLOCKED), + DRM_IOCTL_DEF_DRV(I915_REG_READ, i915_reg_read_ioctl, DRM_UNLOCKED), }; int i915_max_ioctl = DRM_ARRAY_SIZE(i915_ioctls); diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c index ed22612..ab3b9d3 100644 --- a/drivers/gpu/drm/i915/i915_drv.c +++ b/drivers/gpu/drm/i915/i915_drv.c @@ -1151,3 +1151,49 @@ __i915_write(16, w) __i915_write(32, l) __i915_write(64, q) #undef __i915_write + +static const struct register_whitelist { + uint64_t offset; + uint32_t size; + uint32_t gen_bitmask; /* support gens, 0x10 for 4, 0x30 for 4 and 5, etc. */ +} whitelist[] = { + { RING_TIMESTAMP(RENDER_RING_BASE), 8, 0xF0 }, +}; + +int i915_reg_read_ioctl(struct drm_device *dev, + void *data, struct drm_file *file) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + struct drm_i915_reg_read *reg = data; + struct register_whitelist const *entry = whitelist; + int i; + + for (i = 0; i < ARRAY_SIZE(whitelist); i++, entry++) { + if (entry->offset == reg->offset && + (1 << INTEL_INFO(dev)->gen & entry->gen_bitmask)) + break; + } + + if (i == ARRAY_SIZE(whitelist)) + return -EINVAL; + + switch (entry->size) { + case 8: + reg->val = I915_READ64(reg->offset); + break; + case 4: + reg->val = I915_READ(reg->offset); + break; + case 2: + reg->val = I915_READ16(reg->offset); + break; + case 1: + reg->val = I915_READ8(reg->offset); + break; + default: + WARN_ON(1); + return -EINVAL; + } + + return 0; +} diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 627fe35..2c0d840 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -1529,6 +1529,8 @@ extern int intel_trans_dp_port_sel(struct drm_crtc *crtc); extern int intel_enable_rc6(const struct drm_device *dev); extern bool i915_semaphore_is_enabled(struct drm_device *dev); +int i915_reg_read_ioctl(struct drm_device *dev, void *data, + struct drm_file *file); /* overlay */ #ifdef CONFIG_DEBUG_FS diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h index d8af397..0d1fa64 100644 --- a/drivers/gpu/drm/i915/i915_reg.h +++ b/drivers/gpu/drm/i915/i915_reg.h @@ -449,6 +449,7 @@ #define RING_ACTHD(base) ((base)+0x74) #define RING_NOPID(base) ((base)+0x94) #define RING_IMR(base) ((base)+0xa8) +#define RING_TIMESTAMP(base) ((base)+0x358) #define TAIL_ADDR 0x001FFFF8 #define HEAD_WRAP_COUNT 0xFFE00000 #define HEAD_WRAP_ONE 0x00200000 diff --git a/include/drm/i915_drm.h b/include/drm/i915_drm.h index 95648ab..b923b03 100644 --- a/include/drm/i915_drm.h +++ b/include/drm/i915_drm.h @@ -205,6 +205,7 @@ typedef struct _drm_i915_sarea { #define DRM_I915_GEM_CONTEXT_DESTROY 0x2e #define DRM_I915_GEM_SET_CACHEING 0x2f #define DRM_I915_GEM_GET_CACHEING 0x30 +#define DRM_I915_REG_READ 0x31 #define DRM_IOCTL_I915_INIT DRM_IOW( DRM_COMMAND_BASE + DRM_I915_INIT, drm_i915_init_t) #define DRM_IOCTL_I915_FLUSH DRM_IO ( DRM_COMMAND_BASE + DRM_I915_FLUSH) @@ -253,6 +254,7 @@ typedef struct _drm_i915_sarea { #define DRM_IOCTL_I915_GEM_WAIT DRM_IOWR(DRM_COMMAND_BASE + DRM_I915_GEM_WAIT, struct drm_i915_gem_wait) #define DRM_IOCTL_I915_GEM_CONTEXT_CREATE DRM_IOWR (DRM_COMMAND_BASE + DRM_I915_GEM_CONTEXT_CREATE, struct drm_i915_gem_context_create) #define DRM_IOCTL_I915_GEM_CONTEXT_DESTROY DRM_IOW (DRM_COMMAND_BASE + DRM_I915_GEM_CONTEXT_DESTROY, struct drm_i915_gem_context_destroy) +#define DRM_IOCTL_I915_REG_READ DRM_IOWR (DRM_COMMAND_BASE + DRM_I915_REG_READ, struct drm_i915_reg_read) /* Allow drivers to submit batchbuffers directly to hardware, relying * on the security mechanisms provided by hardware. @@ -710,7 +712,7 @@ struct drm_i915_gem_busy { #define I915_CACHEING_CACHED 1 struct drm_i915_gem_cacheing { - /** Handle of the buffer to check for busy */ + /** Handle of the buffer to set/get the cacheing level of */ __u32 handle; /** Cacheing level to apply or return value */ @@ -933,4 +935,8 @@ struct drm_i915_gem_context_destroy { __u32 pad; }; +struct drm_i915_reg_read { + __u64 offset; + __u64 val; /* Return value */ +}; #endif /* _I915_DRM_H_ */ -- cgit v0.10.2 From e9808edd98679680804dfbc42c5ee8f1aa91f617 Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Wed, 4 Jul 2012 12:25:08 +0100 Subject: drm/i915: Return a mask of the active rings in the high word of busy_ioctl The intention is to help select which engine to use for copies with interoperating clients - such as a GL client making a request to the X server to perform a SwapBuffers, which may require copying from the active GL back buffer to the X front buffer. We choose to report a mask of the active rings to future proof the interface against any changes which may allow for the object to reside upon multiple rings. Signed-off-by: Chris Wilson [danvet: bikeshed away the write ring mask and add the explanation Chris sent in a follow-up mail why we decided to use masks.] Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index 5c4657a..4be0960 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -3400,6 +3400,10 @@ i915_gem_busy_ioctl(struct drm_device *dev, void *data, ret = i915_gem_object_flush_active(obj); args->busy = obj->active; + if (obj->ring) { + BUILD_BUG_ON(I915_NUM_RINGS > 16); + args->busy |= intel_ring_flag(obj->ring) << 16; + } drm_gem_object_unreference(&obj->base); unlock: diff --git a/include/drm/i915_drm.h b/include/drm/i915_drm.h index b923b03..0f149fe 100644 --- a/include/drm/i915_drm.h +++ b/include/drm/i915_drm.h @@ -704,7 +704,11 @@ struct drm_i915_gem_busy { /** Handle of the buffer to check for busy */ __u32 handle; - /** Return busy status (1 if busy, 0 if idle) */ + /** Return busy status (1 if busy, 0 if idle). + * The high word is used to indicate on which rings the object + * currently resides: + * 16:31 - busy (r or r/w) rings (16 render, 17 bsd, 18 blt, etc) + */ __u32 busy; }; -- cgit v0.10.2 From a7e806de4e53f7496a6701194d736a92a80db5b3 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Wed, 11 Jul 2012 16:27:55 +0200 Subject: drm/i915: create VLV_DSIPLAY_BASE #define Will be used more in the next patch. Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c index ab3b9d3..ff569cc 100644 --- a/drivers/gpu/drm/i915/i915_drv.c +++ b/drivers/gpu/drm/i915/i915_drv.c @@ -1031,7 +1031,7 @@ static bool IS_DISPLAYREG(u32 reg) * This should make it easier to transition modules over to the * new register block scheme, since we can do it incrementally. */ - if (reg >= 0x180000) + if (reg >= VLV_DISPLAY_BASE) return false; if (reg >= RENDER_RING_BASE && diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h index 0d1fa64..d122c93 100644 --- a/drivers/gpu/drm/i915/i915_reg.h +++ b/drivers/gpu/drm/i915/i915_reg.h @@ -529,6 +529,8 @@ #define GFX_PSMI_GRANULARITY (1<<10) #define GFX_PPGTT_ENABLE (1<<9) +#define VLV_DISPLAY_BASE 0x180000 + #define SCPD0 0x0209c /* 915+ only */ #define IER 0x020a0 #define IIR 0x020a4 -- cgit v0.10.2 From 540a8950047579a368a9b8fdcc15ab7fdb9921d3 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Wed, 11 Jul 2012 16:27:57 +0200 Subject: drm/i915: add inte_crt->adpa_reg With the base addresses shifting around, this is easier to handle. Also move to the real reg offset on vlv. Acked-by: Chris Wilson Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h index d122c93..81a3de6 100644 --- a/drivers/gpu/drm/i915/i915_reg.h +++ b/drivers/gpu/drm/i915/i915_reg.h @@ -1552,6 +1552,7 @@ /* VGA port control */ #define ADPA 0x61100 #define PCH_ADPA 0xe1100 +#define VLV_ADPA (VLV_DISPLAY_BASE + ADPA) #define ADPA_DAC_ENABLE (1<<31) #define ADPA_DAC_DISABLE 0 diff --git a/drivers/gpu/drm/i915/intel_crt.c b/drivers/gpu/drm/i915/intel_crt.c index e1d02be..bc5e2c9 100644 --- a/drivers/gpu/drm/i915/intel_crt.c +++ b/drivers/gpu/drm/i915/intel_crt.c @@ -47,6 +47,7 @@ struct intel_crt { struct intel_encoder base; bool force_hotplug_required; + u32 adpa_reg; }; static struct intel_crt *intel_attached_crt(struct drm_connector *connector) @@ -55,6 +56,11 @@ static struct intel_crt *intel_attached_crt(struct drm_connector *connector) struct intel_crt, base); } +static struct intel_crt *intel_encoder_to_crt(struct intel_encoder *encoder) +{ + return container_of(encoder, struct intel_crt, base); +} + static void pch_crt_dpms(struct drm_encoder *encoder, int mode) { struct drm_device *dev = encoder->dev; @@ -145,19 +151,15 @@ static void intel_crt_mode_set(struct drm_encoder *encoder, struct drm_device *dev = encoder->dev; struct drm_crtc *crtc = encoder->crtc; + struct intel_crt *crt = + intel_encoder_to_crt(to_intel_encoder(encoder)); struct intel_crtc *intel_crtc = to_intel_crtc(crtc); struct drm_i915_private *dev_priv = dev->dev_private; int dpll_md_reg; u32 adpa, dpll_md; - u32 adpa_reg; dpll_md_reg = DPLL_MD(intel_crtc->pipe); - if (HAS_PCH_SPLIT(dev)) - adpa_reg = PCH_ADPA; - else - adpa_reg = ADPA; - /* * Disable separate mode multiplier used when cloning SDVO to CRT * XXX this needs to be adjusted when we really are cloning @@ -185,7 +187,7 @@ static void intel_crt_mode_set(struct drm_encoder *encoder, if (!HAS_PCH_SPLIT(dev)) I915_WRITE(BCLRPAT(intel_crtc->pipe), 0); - I915_WRITE(adpa_reg, adpa); + I915_WRITE(crt->adpa_reg, adpa); } static bool intel_ironlake_crt_detect_hotplug(struct drm_connector *connector) @@ -675,6 +677,13 @@ void intel_crt_init(struct drm_device *dev) else encoder_helper_funcs = &gmch_encoder_funcs; + if (HAS_PCH_SPLIT(dev)) + crt->adpa_reg = PCH_ADPA; + else if (IS_VALLEYVIEW(dev)) + crt->adpa_reg = VLV_ADPA; + else + crt->adpa_reg = ADPA; + drm_encoder_helper_add(&crt->base.base, encoder_helper_funcs); drm_connector_helper_add(connector, &intel_crt_connector_helper_funcs); -- cgit v0.10.2 From 3bb73aba1ed5198a2c1dfaac4f3c95459930d84a Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Fri, 20 Jul 2012 12:40:59 +0100 Subject: drm/i915: Allow late allocation of request for i915_add_request() Request preallocation was added to i915_add_request() in order to support the overlay. However, not all users care and can quite happily ignore the failure to allocate the request as they will simply repeat the request in the future. By pushing the allocation down into i915_add_request(), we can then remove some rather ugly error handling in the callers. v2: Nullify request->file_priv otherwise we chase a garbage pointer when retiring requests. Signed-off-by: Chris Wilson Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 2c0d840..1f5f5ff 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -1358,9 +1358,9 @@ void i915_gem_init_ppgtt(struct drm_device *dev); void i915_gem_cleanup_ringbuffer(struct drm_device *dev); int __must_check i915_gpu_idle(struct drm_device *dev); int __must_check i915_gem_idle(struct drm_device *dev); -int __must_check i915_add_request(struct intel_ring_buffer *ring, - struct drm_file *file, - struct drm_i915_gem_request *request); +int i915_add_request(struct intel_ring_buffer *ring, + struct drm_file *file, + struct drm_i915_gem_request *request); int __must_check i915_wait_seqno(struct intel_ring_buffer *ring, uint32_t seqno); int i915_gem_fault(struct vm_area_struct *vma, struct vm_fault *vmf); diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index 4be0960..771b8ba 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -1597,7 +1597,12 @@ i915_add_request(struct intel_ring_buffer *ring, ring->gpu_caches_dirty = false; } - BUG_ON(request == NULL); + if (request == NULL) { + request = kmalloc(sizeof(*request), GFP_KERNEL); + if (request == NULL) + return -ENOMEM; + } + seqno = i915_gem_next_request_seqno(ring); /* Record the position of the start of the request so that @@ -1608,8 +1613,10 @@ i915_add_request(struct intel_ring_buffer *ring, request_ring_position = intel_ring_get_tail(ring); ret = ring->add_request(ring, &seqno); - if (ret) - return ret; + if (ret) { + kfree(request); + return ret; + } trace_i915_gem_request_add(ring, seqno); @@ -1619,6 +1626,7 @@ i915_add_request(struct intel_ring_buffer *ring, request->emitted_jiffies = jiffies; was_empty = list_empty(&ring->request_list); list_add_tail(&request->list, &ring->request_list); + request->file_priv = NULL; if (file) { struct drm_i915_file_private *file_priv = file->driver_priv; @@ -1859,14 +1867,8 @@ i915_gem_retire_work_handler(struct work_struct *work) */ idle = true; for_each_ring(ring, dev_priv, i) { - if (ring->gpu_caches_dirty) { - struct drm_i915_gem_request *request; - - request = kzalloc(sizeof(*request), GFP_KERNEL); - if (request == NULL || - i915_add_request(ring, NULL, request)) - kfree(request); - } + if (ring->gpu_caches_dirty) + i915_add_request(ring, NULL, NULL); idle &= list_empty(&ring->request_list); } @@ -1913,25 +1915,13 @@ i915_gem_check_wedge(struct drm_i915_private *dev_priv, static int i915_gem_check_olr(struct intel_ring_buffer *ring, u32 seqno) { - int ret = 0; + int ret; BUG_ON(!mutex_is_locked(&ring->dev->struct_mutex)); - if (seqno == ring->outstanding_lazy_request) { - struct drm_i915_gem_request *request; - - request = kzalloc(sizeof(*request), GFP_KERNEL); - if (request == NULL) - return -ENOMEM; - - ret = i915_add_request(ring, NULL, request); - if (ret) { - kfree(request); - return ret; - } - - BUG_ON(seqno != request->seqno); - } + ret = 0; + if (seqno == ring->outstanding_lazy_request) + ret = i915_add_request(ring, NULL, NULL); return ret; } diff --git a/drivers/gpu/drm/i915/i915_gem_execbuffer.c b/drivers/gpu/drm/i915/i915_gem_execbuffer.c index 5af631e..f94ec57 100644 --- a/drivers/gpu/drm/i915/i915_gem_execbuffer.c +++ b/drivers/gpu/drm/i915/i915_gem_execbuffer.c @@ -972,16 +972,11 @@ i915_gem_execbuffer_retire_commands(struct drm_device *dev, struct drm_file *file, struct intel_ring_buffer *ring) { - struct drm_i915_gem_request *request; - /* Unconditionally force add_request to emit a full flush. */ ring->gpu_caches_dirty = true; /* Add a breadcrumb for the completion of the batch buffer */ - request = kzalloc(sizeof(*request), GFP_KERNEL); - if (request == NULL || i915_add_request(ring, file, request)) { - kfree(request); - } + (void)i915_add_request(ring, file, NULL); } static int -- cgit v0.10.2 From e5f1d962a8e4c5fd6b3a8155c0f7a40b0bff4a96 Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Fri, 20 Jul 2012 12:41:00 +0100 Subject: drm/i915: Remove assertion over write domain after i915_gem_object_sync() As we move to lazily clearing the GPU write domain only when the buffer becomes inactive, this leaves a window of opportunity for i915_gem_object_pin_to_display_plane() to detect a seemingly inconsistent value. This function is special as it tries to pipeline the operation to avoid the stall and so may not retires the buffer and we may not get the opportunity to clear the write domain. However, we know all is good, so drop the assertion. Signed-off-by: Chris Wilson Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index 771b8ba..3bef7e6 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -3091,7 +3091,7 @@ i915_gem_object_pin_to_display_plane(struct drm_i915_gem_object *obj, /* It should now be out of any other write domains, and we can update * the domain values for our changes. */ - BUG_ON((obj->base.write_domain & ~I915_GEM_DOMAIN_GTT) != 0); + obj->base.write_domain = 0; obj->base.read_domains |= I915_GEM_DOMAIN_GTT; trace_i915_gem_object_change_domain(obj, -- cgit v0.10.2 From 0201f1ecf4b81f08799b1fb9c8cdf1125b9b78a6 Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Fri, 20 Jul 2012 12:41:01 +0100 Subject: drm/i915: Replace the pending_gpu_write flag with an explicit seqno As we always flush the GPU cache prior to emitting the breadcrumb, we no longer have to worry about the deferred flush causing the pending_gpu_write to be delayed. So we can instead utilize the known last_write_seqno to hopefully minimise the wait times. Signed-off-by: Chris Wilson Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/i915_debugfs.c b/drivers/gpu/drm/i915/i915_debugfs.c index 359f6e8..a8b7db6 100644 --- a/drivers/gpu/drm/i915/i915_debugfs.c +++ b/drivers/gpu/drm/i915/i915_debugfs.c @@ -121,14 +121,15 @@ static const char *cache_level_str(int type) static void describe_obj(struct seq_file *m, struct drm_i915_gem_object *obj) { - seq_printf(m, "%p: %s%s %8zdKiB %04x %04x %d %d%s%s%s", + seq_printf(m, "%p: %s%s %8zdKiB %04x %04x %d %d %d%s%s%s", &obj->base, get_pin_flag(obj), get_tiling_flag(obj), obj->base.size / 1024, obj->base.read_domains, obj->base.write_domain, - obj->last_rendering_seqno, + obj->last_read_seqno, + obj->last_write_seqno, obj->last_fenced_seqno, cache_level_str(obj->cache_level), obj->dirty ? " dirty" : "", @@ -630,12 +631,12 @@ static void print_error_buffers(struct seq_file *m, seq_printf(m, "%s [%d]:\n", name, count); while (count--) { - seq_printf(m, " %08x %8u %04x %04x %08x%s%s%s%s%s%s%s", + seq_printf(m, " %08x %8u %04x %04x %x %x%s%s%s%s%s%s%s", err->gtt_offset, err->size, err->read_domains, err->write_domain, - err->seqno, + err->rseqno, err->wseqno, pin_flag(err->pinned), tiling_flag(err->tiling), dirty_flag(err->dirty), diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 1f5f5ff..49a532e 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -221,7 +221,7 @@ struct drm_i915_error_state { struct drm_i915_error_buffer { u32 size; u32 name; - u32 seqno; + u32 rseqno, wseqno; u32 gtt_offset; u32 read_domains; u32 write_domain; @@ -895,12 +895,6 @@ struct drm_i915_gem_object { unsigned int dirty:1; /** - * This is set if the object has been written to since the last - * GPU flush. - */ - unsigned int pending_gpu_write:1; - - /** * Fence register bits (if any) for this object. Will be set * as needed when mapped into the GTT. * Protected by dev->struct_mutex. @@ -992,7 +986,8 @@ struct drm_i915_gem_object { struct intel_ring_buffer *ring; /** Breadcrumb of last rendering to the buffer. */ - uint32_t last_rendering_seqno; + uint32_t last_read_seqno; + uint32_t last_write_seqno; /** Breadcrumb of last fenced GPU access to the buffer. */ uint32_t last_fenced_seqno; @@ -1291,7 +1286,6 @@ void i915_gem_lastclose(struct drm_device *dev); int i915_gem_object_get_pages_gtt(struct drm_i915_gem_object *obj, gfp_t gfpmask); int __must_check i915_mutex_lock_interruptible(struct drm_device *dev); -int __must_check i915_gem_object_wait_rendering(struct drm_i915_gem_object *obj); int i915_gem_object_sync(struct drm_i915_gem_object *obj, struct intel_ring_buffer *to); void i915_gem_object_move_to_active(struct drm_i915_gem_object *obj, diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index 3bef7e6..6a80d65 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -1441,7 +1441,7 @@ i915_gem_object_move_to_active(struct drm_i915_gem_object *obj, list_move_tail(&obj->mm_list, &dev_priv->mm.active_list); list_move_tail(&obj->ring_list, &ring->active_list); - obj->last_rendering_seqno = seqno; + obj->last_read_seqno = seqno; if (obj->fenced_gpu_access) { obj->last_fenced_seqno = seqno; @@ -1461,7 +1461,8 @@ static void i915_gem_object_move_off_active(struct drm_i915_gem_object *obj) { list_del_init(&obj->ring_list); - obj->last_rendering_seqno = 0; + obj->last_read_seqno = 0; + obj->last_write_seqno = 0; obj->last_fenced_seqno = 0; } @@ -1493,7 +1494,6 @@ i915_gem_object_move_to_inactive(struct drm_i915_gem_object *obj) obj->fenced_gpu_access = false; obj->active = 0; - obj->pending_gpu_write = false; drm_gem_object_unreference(&obj->base); WARN_ON(i915_verify_lists(dev)); @@ -1812,7 +1812,7 @@ i915_gem_retire_requests_ring(struct intel_ring_buffer *ring) struct drm_i915_gem_object, ring_list); - if (!i915_seqno_passed(seqno, obj->last_rendering_seqno)) + if (!i915_seqno_passed(seqno, obj->last_read_seqno)) break; if (obj->base.write_domain != 0) @@ -2036,9 +2036,11 @@ i915_wait_seqno(struct intel_ring_buffer *ring, uint32_t seqno) * Ensures that all rendering to the object has completed and the object is * safe to unbind from the GTT or access from the CPU. */ -int -i915_gem_object_wait_rendering(struct drm_i915_gem_object *obj) +static __must_check int +i915_gem_object_wait_rendering(struct drm_i915_gem_object *obj, + bool readonly) { + u32 seqno; int ret; /* This function only exists to support waiting for existing rendering, @@ -2049,13 +2051,27 @@ i915_gem_object_wait_rendering(struct drm_i915_gem_object *obj) /* If there is rendering queued on the buffer being evicted, wait for * it. */ - if (obj->active) { - ret = i915_wait_seqno(obj->ring, obj->last_rendering_seqno); - if (ret) - return ret; - i915_gem_retire_requests_ring(obj->ring); + if (readonly) + seqno = obj->last_write_seqno; + else + seqno = obj->last_read_seqno; + if (seqno == 0) + return 0; + + ret = i915_wait_seqno(obj->ring, seqno); + if (ret) + return ret; + + /* Manually manage the write flush as we may have not yet retired + * the buffer. + */ + if (obj->last_write_seqno && + i915_seqno_passed(seqno, obj->last_write_seqno)) { + obj->last_write_seqno = 0; + obj->base.write_domain &= ~I915_GEM_GPU_DOMAINS; } + i915_gem_retire_requests_ring(obj->ring); return 0; } @@ -2074,10 +2090,10 @@ i915_gem_object_flush_active(struct drm_i915_gem_object *obj) if (ret) return ret; - ret = i915_gem_check_olr(obj->ring, - obj->last_rendering_seqno); + ret = i915_gem_check_olr(obj->ring, obj->last_read_seqno); if (ret) return ret; + i915_gem_retire_requests_ring(obj->ring); } @@ -2137,7 +2153,7 @@ i915_gem_wait_ioctl(struct drm_device *dev, void *data, struct drm_file *file) goto out; if (obj->active) { - seqno = obj->last_rendering_seqno; + seqno = obj->last_read_seqno; ring = obj->ring; } @@ -2192,11 +2208,11 @@ i915_gem_object_sync(struct drm_i915_gem_object *obj, return 0; if (to == NULL || !i915_semaphore_is_enabled(obj->base.dev)) - return i915_gem_object_wait_rendering(obj); + return i915_gem_object_wait_rendering(obj, false); idx = intel_ring_sync_index(from, to); - seqno = obj->last_rendering_seqno; + seqno = obj->last_read_seqno; if (seqno <= from->sync_seqno[idx]) return 0; @@ -2940,11 +2956,9 @@ i915_gem_object_set_to_gtt_domain(struct drm_i915_gem_object *obj, bool write) if (ret) return ret; - if (obj->pending_gpu_write || write) { - ret = i915_gem_object_wait_rendering(obj); - if (ret) - return ret; - } + ret = i915_gem_object_wait_rendering(obj, !write); + if (ret) + return ret; i915_gem_object_flush_cpu_write_domain(obj); @@ -3115,7 +3129,7 @@ i915_gem_object_finish_gpu(struct drm_i915_gem_object *obj) return ret; } - ret = i915_gem_object_wait_rendering(obj); + ret = i915_gem_object_wait_rendering(obj, false); if (ret) return ret; @@ -3143,11 +3157,9 @@ i915_gem_object_set_to_cpu_domain(struct drm_i915_gem_object *obj, bool write) if (ret) return ret; - if (write || obj->pending_gpu_write) { - ret = i915_gem_object_wait_rendering(obj); - if (ret) - return ret; - } + ret = i915_gem_object_wait_rendering(obj, !write); + if (ret) + return ret; i915_gem_object_flush_gtt_write_domain(obj); diff --git a/drivers/gpu/drm/i915/i915_gem_execbuffer.c b/drivers/gpu/drm/i915/i915_gem_execbuffer.c index f94ec57..2353e6e 100644 --- a/drivers/gpu/drm/i915/i915_gem_execbuffer.c +++ b/drivers/gpu/drm/i915/i915_gem_execbuffer.c @@ -954,7 +954,7 @@ i915_gem_execbuffer_move_to_active(struct list_head *objects, i915_gem_object_move_to_active(obj, ring, seqno); if (obj->base.write_domain) { obj->dirty = 1; - obj->pending_gpu_write = true; + obj->last_write_seqno = seqno; list_move_tail(&obj->gpu_write_list, &ring->gpu_write_list); if (obj->pin_count) /* check for potential scanout */ diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index 566f61b..41ed41d 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -950,7 +950,8 @@ static void capture_bo(struct drm_i915_error_buffer *err, { err->size = obj->base.size; err->name = obj->base.name; - err->seqno = obj->last_rendering_seqno; + err->rseqno = obj->last_read_seqno; + err->wseqno = obj->last_write_seqno; err->gtt_offset = obj->gtt_offset; err->read_domains = obj->base.read_domains; err->write_domain = obj->base.write_domain; @@ -1045,7 +1046,7 @@ i915_error_first_batchbuffer(struct drm_i915_private *dev_priv, if (obj->ring != ring) continue; - if (i915_seqno_passed(seqno, obj->last_rendering_seqno)) + if (i915_seqno_passed(seqno, obj->last_read_seqno)) continue; if ((obj->base.read_domains & I915_GEM_DOMAIN_COMMAND) == 0) -- cgit v0.10.2 From 65ce3027415d4dc9ee18ef0a135214b4fb76730b Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Fri, 20 Jul 2012 12:41:02 +0100 Subject: drm/i915: Remove the defunct flushing list As we guarantee to emit a flush before emitting the breadcrumb or the next batchbuffer, there is no further need for the flushing list. Signed-off-by: Chris Wilson Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/i915_debugfs.c b/drivers/gpu/drm/i915/i915_debugfs.c index a8b7db6..1312b79 100644 --- a/drivers/gpu/drm/i915/i915_debugfs.c +++ b/drivers/gpu/drm/i915/i915_debugfs.c @@ -44,7 +44,6 @@ enum { ACTIVE_LIST, - FLUSHING_LIST, INACTIVE_LIST, PINNED_LIST, }; @@ -178,10 +177,6 @@ static int i915_gem_object_list_info(struct seq_file *m, void *data) seq_printf(m, "Inactive:\n"); head = &dev_priv->mm.inactive_list; break; - case FLUSHING_LIST: - seq_printf(m, "Flushing:\n"); - head = &dev_priv->mm.flushing_list; - break; default: mutex_unlock(&dev->struct_mutex); return -EINVAL; @@ -239,7 +234,6 @@ static int i915_gem_object_info(struct seq_file *m, void* data) size = count = mappable_size = mappable_count = 0; count_objects(&dev_priv->mm.active_list, mm_list); - count_objects(&dev_priv->mm.flushing_list, mm_list); seq_printf(m, " %u [%u] active objects, %zu [%zu] bytes\n", count, mappable_count, size, mappable_size); @@ -2007,7 +2001,6 @@ static struct drm_info_list i915_debugfs_list[] = { {"i915_gem_gtt", i915_gem_gtt_info, 0}, {"i915_gem_pinned", i915_gem_gtt_info, 0, (void *) PINNED_LIST}, {"i915_gem_active", i915_gem_object_list_info, 0, (void *) ACTIVE_LIST}, - {"i915_gem_flushing", i915_gem_object_list_info, 0, (void *) FLUSHING_LIST}, {"i915_gem_inactive", i915_gem_object_list_info, 0, (void *) INACTIVE_LIST}, {"i915_gem_pageflip", i915_gem_pageflip_info, 0}, {"i915_gem_request", i915_gem_request_info, 0}, diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 49a532e..6b91755 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -696,17 +696,6 @@ typedef struct drm_i915_private { struct list_head active_list; /** - * List of objects which are not in the ringbuffer but which - * still have a write_domain which needs to be flushed before - * unbinding. - * - * last_rendering_seqno is 0 while an object is in this list. - * - * A reference is held on the buffer while on this list. - */ - struct list_head flushing_list; - - /** * LRU list of objects which are not in the ringbuffer and * are ready to unbind, but are still in the GTT. * @@ -873,7 +862,7 @@ struct drm_i915_gem_object { struct drm_mm_node *gtt_space; struct list_head gtt_list; - /** This object's place on the active/flushing/inactive lists */ + /** This object's place on the active/inactive lists */ struct list_head ring_list; struct list_head mm_list; /** This object's place on GPU write list */ @@ -882,9 +871,9 @@ struct drm_i915_gem_object { struct list_head exec_list; /** - * This is set if the object is on the active or flushing lists - * (has pending rendering), and is not set if it's on inactive (ready - * to be unbound). + * This is set if the object is on the active lists (has pending + * rendering and so a non-zero seqno), and is not set if it i s on + * inactive (ready to be unbound) list. */ unsigned int active:1; diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index 6a80d65..f62dd29 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -1458,27 +1458,6 @@ i915_gem_object_move_to_active(struct drm_i915_gem_object *obj, } static void -i915_gem_object_move_off_active(struct drm_i915_gem_object *obj) -{ - list_del_init(&obj->ring_list); - obj->last_read_seqno = 0; - obj->last_write_seqno = 0; - obj->last_fenced_seqno = 0; -} - -static void -i915_gem_object_move_to_flushing(struct drm_i915_gem_object *obj) -{ - struct drm_device *dev = obj->base.dev; - drm_i915_private_t *dev_priv = dev->dev_private; - - BUG_ON(!obj->active); - list_move_tail(&obj->mm_list, &dev_priv->mm.flushing_list); - - i915_gem_object_move_off_active(obj); -} - -static void i915_gem_object_move_to_inactive(struct drm_i915_gem_object *obj) { struct drm_device *dev = obj->base.dev; @@ -1487,10 +1466,17 @@ i915_gem_object_move_to_inactive(struct drm_i915_gem_object *obj) list_move_tail(&obj->mm_list, &dev_priv->mm.inactive_list); BUG_ON(!list_empty(&obj->gpu_write_list)); + BUG_ON(obj->base.write_domain & ~I915_GEM_GPU_DOMAINS); BUG_ON(!obj->active); + + list_del_init(&obj->ring_list); obj->ring = NULL; - i915_gem_object_move_off_active(obj); + obj->last_read_seqno = 0; + obj->last_write_seqno = 0; + obj->base.write_domain = 0; + + obj->last_fenced_seqno = 0; obj->fenced_gpu_access = false; obj->active = 0; @@ -1694,7 +1680,6 @@ static void i915_gem_reset_ring_lists(struct drm_i915_private *dev_priv, struct drm_i915_gem_object, ring_list); - obj->base.write_domain = 0; list_del_init(&obj->gpu_write_list); i915_gem_object_move_to_inactive(obj); } @@ -1731,20 +1716,6 @@ void i915_gem_reset(struct drm_device *dev) 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 - * lost bo to the inactive list. - */ - while (!list_empty(&dev_priv->mm.flushing_list)) { - obj = list_first_entry(&dev_priv->mm.flushing_list, - struct drm_i915_gem_object, - mm_list); - - obj->base.write_domain = 0; - list_del_init(&obj->gpu_write_list); - i915_gem_object_move_to_inactive(obj); - } - /* Move everything out of the GPU domains to ensure we do any * necessary invalidation upon reuse. */ @@ -1815,10 +1786,7 @@ i915_gem_retire_requests_ring(struct intel_ring_buffer *ring) if (!i915_seqno_passed(seqno, obj->last_read_seqno)) break; - if (obj->base.write_domain != 0) - i915_gem_object_move_to_flushing(obj); - else - i915_gem_object_move_to_inactive(obj); + i915_gem_object_move_to_inactive(obj); } if (unlikely(ring->trace_irq_seqno && @@ -3897,7 +3865,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)); mutex_unlock(&dev->struct_mutex); @@ -3955,7 +3922,6 @@ i915_gem_load(struct drm_device *dev) drm_i915_private_t *dev_priv = dev->dev_private; INIT_LIST_HEAD(&dev_priv->mm.active_list); - INIT_LIST_HEAD(&dev_priv->mm.flushing_list); INIT_LIST_HEAD(&dev_priv->mm.inactive_list); INIT_LIST_HEAD(&dev_priv->mm.fence_list); INIT_LIST_HEAD(&dev_priv->mm.gtt_list); @@ -4206,12 +4172,7 @@ static int i915_gpu_is_active(struct drm_device *dev) { drm_i915_private_t *dev_priv = dev->dev_private; - int lists_empty; - - lists_empty = list_empty(&dev_priv->mm.flushing_list) && - list_empty(&dev_priv->mm.active_list); - - return !lists_empty; + return !list_empty(&dev_priv->mm.active_list); } static int diff --git a/drivers/gpu/drm/i915/i915_gem_evict.c b/drivers/gpu/drm/i915/i915_gem_evict.c index eba0308..51e547c 100644 --- a/drivers/gpu/drm/i915/i915_gem_evict.c +++ b/drivers/gpu/drm/i915/i915_gem_evict.c @@ -93,23 +93,6 @@ i915_gem_evict_something(struct drm_device *dev, int min_size, /* Now merge in the soon-to-be-expired objects... */ list_for_each_entry(obj, &dev_priv->mm.active_list, mm_list) { - /* Does the object require an outstanding flush? */ - if (obj->base.write_domain) - continue; - - if (mark_free(obj, &unwind_list)) - goto found; - } - - /* Finally add anything with a pending flush (in order of retirement) */ - list_for_each_entry(obj, &dev_priv->mm.flushing_list, mm_list) { - if (mark_free(obj, &unwind_list)) - goto found; - } - list_for_each_entry(obj, &dev_priv->mm.active_list, mm_list) { - if (!obj->base.write_domain) - continue; - if (mark_free(obj, &unwind_list)) goto found; } @@ -172,7 +155,6 @@ i915_gem_evict_everything(struct drm_device *dev, bool purgeable_only) int ret; lists_empty = (list_empty(&dev_priv->mm.inactive_list) && - list_empty(&dev_priv->mm.flushing_list) && list_empty(&dev_priv->mm.active_list)); if (lists_empty) return -ENOSPC; @@ -189,8 +171,6 @@ i915_gem_evict_everything(struct drm_device *dev, bool purgeable_only) i915_gem_retire_requests(dev); - BUG_ON(!list_empty(&dev_priv->mm.flushing_list)); - /* Having flushed everything, unbind() should never raise an error */ list_for_each_entry_safe(obj, next, &dev_priv->mm.inactive_list, mm_list) { -- cgit v0.10.2 From 69c2fc891343cb5217c866d10709343cff190bdc Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Fri, 20 Jul 2012 12:41:03 +0100 Subject: drm/i915: Remove the per-ring write list This is now handled by a global flag to ensure we emit a flush before the next serialisation point (if we failed to queue one previously). Signed-off-by: Chris Wilson Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 6b91755..59e3199 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -865,8 +865,6 @@ struct drm_i915_gem_object { /** This object's place on the active/inactive lists */ struct list_head ring_list; struct list_head mm_list; - /** This object's place on GPU write list */ - struct list_head gpu_write_list; /** This object's place in the batchbuffer or on the eviction list */ struct list_head exec_list; diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index f62dd29..78fa950 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -1465,7 +1465,6 @@ i915_gem_object_move_to_inactive(struct drm_i915_gem_object *obj) list_move_tail(&obj->mm_list, &dev_priv->mm.inactive_list); - BUG_ON(!list_empty(&obj->gpu_write_list)); BUG_ON(obj->base.write_domain & ~I915_GEM_GPU_DOMAINS); BUG_ON(!obj->active); @@ -1511,30 +1510,6 @@ i915_gem_object_is_purgeable(struct drm_i915_gem_object *obj) return obj->madv == I915_MADV_DONTNEED; } -static void -i915_gem_process_flushing_list(struct intel_ring_buffer *ring, - uint32_t flush_domains) -{ - struct drm_i915_gem_object *obj, *next; - - list_for_each_entry_safe(obj, next, - &ring->gpu_write_list, - gpu_write_list) { - if (obj->base.write_domain & flush_domains) { - uint32_t old_write_domain = obj->base.write_domain; - - obj->base.write_domain = 0; - list_del_init(&obj->gpu_write_list); - i915_gem_object_move_to_active(obj, ring, - i915_gem_next_request_seqno(ring)); - - trace_i915_gem_object_change_domain(obj, - obj->base.read_domains, - old_write_domain); - } - } -} - static u32 i915_gem_get_seqno(struct drm_device *dev) { @@ -1637,8 +1612,6 @@ i915_add_request(struct intel_ring_buffer *ring, &dev_priv->mm.retire_work, HZ); } - WARN_ON(!list_empty(&ring->gpu_write_list)); - return 0; } @@ -1680,7 +1653,6 @@ static void i915_gem_reset_ring_lists(struct drm_i915_private *dev_priv, struct drm_i915_gem_object, ring_list); - list_del_init(&obj->gpu_write_list); i915_gem_object_move_to_inactive(obj); } } @@ -2011,11 +1983,6 @@ i915_gem_object_wait_rendering(struct drm_i915_gem_object *obj, u32 seqno; int ret; - /* This function only exists to support waiting for existing rendering, - * not for emitting required flushes. - */ - BUG_ON((obj->base.write_domain & I915_GEM_GPU_DOMAINS) != 0); - /* If there is rendering queued on the buffer being evicted, wait for * it. */ @@ -2308,26 +2275,14 @@ i915_gem_flush_ring(struct intel_ring_buffer *ring, if (ret) return ret; - if (flush_domains & I915_GEM_GPU_DOMAINS) - i915_gem_process_flushing_list(ring, flush_domains); - return 0; } static int i915_ring_idle(struct intel_ring_buffer *ring) { - int ret; - - if (list_empty(&ring->gpu_write_list) && list_empty(&ring->active_list)) + if (list_empty(&ring->active_list)) return 0; - if (!list_empty(&ring->gpu_write_list)) { - ret = i915_gem_flush_ring(ring, - I915_GEM_GPU_DOMAINS, I915_GEM_GPU_DOMAINS); - if (ret) - return ret; - } - return i915_wait_seqno(ring, i915_gem_next_request_seqno(ring)); } @@ -2343,10 +2298,6 @@ int i915_gpu_idle(struct drm_device *dev) if (ret) return ret; - /* Is the device fubar? */ - if (WARN_ON(!list_empty(&ring->gpu_write_list))) - return -EBUSY; - ret = i915_switch_context(ring, NULL, DEFAULT_CONTEXT_ID); if (ret) return ret; @@ -3491,7 +3442,6 @@ struct drm_i915_gem_object *i915_gem_alloc_object(struct drm_device *dev, INIT_LIST_HEAD(&obj->gtt_list); INIT_LIST_HEAD(&obj->ring_list); INIT_LIST_HEAD(&obj->exec_list); - INIT_LIST_HEAD(&obj->gpu_write_list); obj->madv = I915_MADV_WILLNEED; /* Avoid an unnecessary call to unbind on the first bind. */ obj->map_and_fenceable = true; @@ -3912,7 +3862,6 @@ init_ring_lists(struct intel_ring_buffer *ring) { INIT_LIST_HEAD(&ring->active_list); INIT_LIST_HEAD(&ring->request_list); - INIT_LIST_HEAD(&ring->gpu_write_list); } void diff --git a/drivers/gpu/drm/i915/i915_gem_execbuffer.c b/drivers/gpu/drm/i915/i915_gem_execbuffer.c index 2353e6e..36c940c 100644 --- a/drivers/gpu/drm/i915/i915_gem_execbuffer.c +++ b/drivers/gpu/drm/i915/i915_gem_execbuffer.c @@ -943,9 +943,8 @@ i915_gem_execbuffer_move_to_active(struct list_head *objects, struct drm_i915_gem_object *obj; list_for_each_entry(obj, objects, exec_list) { - u32 old_read = obj->base.read_domains; - u32 old_write = obj->base.write_domain; - + u32 old_read = obj->base.read_domains; + u32 old_write = obj->base.write_domain; obj->base.read_domains = obj->base.pending_read_domains; obj->base.write_domain = obj->base.pending_write_domain; @@ -955,8 +954,6 @@ i915_gem_execbuffer_move_to_active(struct list_head *objects, if (obj->base.write_domain) { obj->dirty = 1; obj->last_write_seqno = seqno; - list_move_tail(&obj->gpu_write_list, - &ring->gpu_write_list); if (obj->pin_count) /* check for potential scanout */ intel_mark_busy(ring->dev, obj); } diff --git a/drivers/gpu/drm/i915/intel_ringbuffer.c b/drivers/gpu/drm/i915/intel_ringbuffer.c index bf0195a..8f221d9 100644 --- a/drivers/gpu/drm/i915/intel_ringbuffer.c +++ b/drivers/gpu/drm/i915/intel_ringbuffer.c @@ -1002,7 +1002,6 @@ static int intel_init_ring_buffer(struct drm_device *dev, ring->dev = dev; INIT_LIST_HEAD(&ring->active_list); INIT_LIST_HEAD(&ring->request_list); - INIT_LIST_HEAD(&ring->gpu_write_list); ring->size = 32 * PAGE_SIZE; init_waitqueue_head(&ring->irq_queue); @@ -1473,7 +1472,6 @@ int intel_render_ring_init_dri(struct drm_device *dev, u64 start, u32 size) ring->dev = dev; INIT_LIST_HEAD(&ring->active_list); INIT_LIST_HEAD(&ring->request_list); - INIT_LIST_HEAD(&ring->gpu_write_list); ring->size = size; ring->effective_size = ring->size; diff --git a/drivers/gpu/drm/i915/intel_ringbuffer.h b/drivers/gpu/drm/i915/intel_ringbuffer.h index 1d3c81f..7986f30 100644 --- a/drivers/gpu/drm/i915/intel_ringbuffer.h +++ b/drivers/gpu/drm/i915/intel_ringbuffer.h @@ -101,15 +101,6 @@ struct intel_ring_buffer { struct list_head request_list; /** - * List of objects currently pending a GPU write flush. - * - * All elements on this list will belong to either the - * active_list or flushing_list, last_rendering_seqno can - * be used to differentiate between the two elements. - */ - struct list_head gpu_write_list; - - /** * Do we have some not yet emitted requests outstanding? */ u32 outstanding_lazy_request; -- cgit v0.10.2 From 86d5bc37824f169a03d03bcc47259e82136d6a34 Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Fri, 20 Jul 2012 12:41:04 +0100 Subject: drm/i915: Remove explicit flush from i915_gem_object_flush_fence() As the flush is either performed explictly immediately after the execbuffer dispatch, or before the serialisation of last_fenced_seqno we can forgo the explict i915_gem_flush_ring(). Signed-off-by: Chris Wilson Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index 78fa950..feb3498 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -2473,21 +2473,8 @@ static void i915_gem_object_update_fence(struct drm_i915_gem_object *obj, static int i915_gem_object_flush_fence(struct drm_i915_gem_object *obj) { - int ret; - - if (obj->fenced_gpu_access) { - if (obj->base.write_domain & I915_GEM_GPU_DOMAINS) { - ret = i915_gem_flush_ring(obj->ring, - 0, obj->base.write_domain); - if (ret) - return ret; - } - - obj->fenced_gpu_access = false; - } - if (obj->last_fenced_seqno) { - ret = i915_wait_seqno(obj->ring, obj->last_fenced_seqno); + int ret = i915_wait_seqno(obj->ring, obj->last_fenced_seqno); if (ret) return ret; @@ -2500,6 +2487,7 @@ i915_gem_object_flush_fence(struct drm_i915_gem_object *obj) if (obj->base.read_domains & I915_GEM_DOMAIN_GTT) mb(); + obj->fenced_gpu_access = false; return 0; } -- cgit v0.10.2 From 26b9c4a57fc3ff0ae6032548870bebfa5cd0de3d Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Fri, 20 Jul 2012 12:41:05 +0100 Subject: drm/i915: Remove the explicit flush of the GPU write domain Rely instead on the insertion of the implicit flush before the seqno breadcrumb. Signed-off-by: Chris Wilson Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index feb3498..3659d47 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -37,7 +37,6 @@ #include #include -static __must_check int i915_gem_object_flush_gpu_write_domain(struct drm_i915_gem_object *obj); static void i915_gem_object_flush_gtt_write_domain(struct drm_i915_gem_object *obj); static void i915_gem_object_flush_cpu_write_domain(struct drm_i915_gem_object *obj); static __must_check int i915_gem_object_bind_to_gtt(struct drm_i915_gem_object *obj, @@ -2021,10 +2020,6 @@ i915_gem_object_flush_active(struct drm_i915_gem_object *obj) int ret; if (obj->active) { - ret = i915_gem_object_flush_gpu_write_domain(obj); - if (ret) - return ret; - ret = i915_gem_check_olr(obj->ring, obj->last_read_seqno); if (ret) return ret; @@ -2782,17 +2777,6 @@ i915_gem_clflush_object(struct drm_i915_gem_object *obj) drm_clflush_pages(obj->pages, obj->base.size / PAGE_SIZE); } -/** Flushes any GPU write domain for the object if it's dirty. */ -static int -i915_gem_object_flush_gpu_write_domain(struct drm_i915_gem_object *obj) -{ - if ((obj->base.write_domain & I915_GEM_GPU_DOMAINS) == 0) - return 0; - - /* Queue the GPU write cache flushing we need. */ - return i915_gem_flush_ring(obj->ring, 0, obj->base.write_domain); -} - /** Flushes the GTT write domain for the object if it's dirty. */ static void i915_gem_object_flush_gtt_write_domain(struct drm_i915_gem_object *obj) @@ -2859,10 +2843,6 @@ i915_gem_object_set_to_gtt_domain(struct drm_i915_gem_object *obj, bool write) if (obj->base.write_domain == I915_GEM_DOMAIN_GTT) return 0; - ret = i915_gem_object_flush_gpu_write_domain(obj); - if (ret) - return ret; - ret = i915_gem_object_wait_rendering(obj, !write); if (ret) return ret; @@ -2973,10 +2953,6 @@ i915_gem_object_pin_to_display_plane(struct drm_i915_gem_object *obj, u32 old_read_domains, old_write_domain; int ret; - ret = i915_gem_object_flush_gpu_write_domain(obj); - if (ret) - return ret; - if (pipelined != obj->ring) { ret = i915_gem_object_sync(obj, pipelined); if (ret) @@ -3030,12 +3006,6 @@ i915_gem_object_finish_gpu(struct drm_i915_gem_object *obj) if ((obj->base.read_domains & I915_GEM_GPU_DOMAINS) == 0) return 0; - if (obj->base.write_domain & I915_GEM_GPU_DOMAINS) { - ret = i915_gem_flush_ring(obj->ring, 0, obj->base.write_domain); - if (ret) - return ret; - } - ret = i915_gem_object_wait_rendering(obj, false); if (ret) return ret; @@ -3060,10 +3030,6 @@ i915_gem_object_set_to_cpu_domain(struct drm_i915_gem_object *obj, bool write) if (obj->base.write_domain == I915_GEM_DOMAIN_CPU) return 0; - ret = i915_gem_object_flush_gpu_write_domain(obj); - if (ret) - return ret; - ret = i915_gem_object_wait_rendering(obj, !write); if (ret) return ret; -- cgit v0.10.2 From 6ac42f4148bc27e5ffd18a9ab0eac57f58822af4 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Sat, 21 Jul 2012 12:25:01 +0200 Subject: drm/i915: Replace the complex flushing logic with simple invalidate/flush all Now that we unconditionally flush and invalidate between every batch buffer, we no longer need the complex logic to decide which domains require flushing. Remove it and rejoice. v2 (danvet): Keep around the flip waiting logic. It's gross and broken, I know, but we can't just kill that thing ... even if we just keep it around as a reminder that things are broken. Signed-off-by: Chris Wilson Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/i915_gem_execbuffer.c b/drivers/gpu/drm/i915/i915_gem_execbuffer.c index 36c940c..6c81079 100644 --- a/drivers/gpu/drm/i915/i915_gem_execbuffer.c +++ b/drivers/gpu/drm/i915/i915_gem_execbuffer.c @@ -34,180 +34,6 @@ #include "intel_drv.h" #include -struct change_domains { - uint32_t invalidate_domains; - uint32_t flush_domains; - uint32_t flush_rings; - uint32_t flips; -}; - -/* - * Set the next domain for the specified object. This - * may not actually perform the necessary flushing/invaliding though, - * as that may want to be batched with other set_domain operations - * - * This is (we hope) the only really tricky part of gem. The goal - * is fairly simple -- track which caches hold bits of the object - * and make sure they remain coherent. A few concrete examples may - * help to explain how it works. For shorthand, we use the notation - * (read_domains, write_domain), e.g. (CPU, CPU) to indicate the - * a pair of read and write domain masks. - * - * Case 1: the batch buffer - * - * 1. Allocated - * 2. Written by CPU - * 3. Mapped to GTT - * 4. Read by GPU - * 5. Unmapped from GTT - * 6. Freed - * - * Let's take these a step at a time - * - * 1. Allocated - * Pages allocated from the kernel may still have - * cache contents, so we set them to (CPU, CPU) always. - * 2. Written by CPU (using pwrite) - * The pwrite function calls set_domain (CPU, CPU) and - * this function does nothing (as nothing changes) - * 3. Mapped by GTT - * This function asserts that the object is not - * currently in any GPU-based read or write domains - * 4. Read by GPU - * i915_gem_execbuffer calls set_domain (COMMAND, 0). - * As write_domain is zero, this function adds in the - * current read domains (CPU+COMMAND, 0). - * flush_domains is set to CPU. - * invalidate_domains is set to COMMAND - * clflush is run to get data out of the CPU caches - * then i915_dev_set_domain calls i915_gem_flush to - * emit an MI_FLUSH and drm_agp_chipset_flush - * 5. Unmapped from GTT - * i915_gem_object_unbind calls set_domain (CPU, CPU) - * flush_domains and invalidate_domains end up both zero - * so no flushing/invalidating happens - * 6. Freed - * yay, done - * - * Case 2: The shared render buffer - * - * 1. Allocated - * 2. Mapped to GTT - * 3. Read/written by GPU - * 4. set_domain to (CPU,CPU) - * 5. Read/written by CPU - * 6. Read/written by GPU - * - * 1. Allocated - * Same as last example, (CPU, CPU) - * 2. Mapped to GTT - * Nothing changes (assertions find that it is not in the GPU) - * 3. Read/written by GPU - * execbuffer calls set_domain (RENDER, RENDER) - * flush_domains gets CPU - * invalidate_domains gets GPU - * clflush (obj) - * MI_FLUSH and drm_agp_chipset_flush - * 4. set_domain (CPU, CPU) - * flush_domains gets GPU - * invalidate_domains gets CPU - * wait_rendering (obj) to make sure all drawing is complete. - * This will include an MI_FLUSH to get the data from GPU - * to memory - * clflush (obj) to invalidate the CPU cache - * Another MI_FLUSH in i915_gem_flush (eliminate this somehow?) - * 5. Read/written by CPU - * cache lines are loaded and dirtied - * 6. Read written by GPU - * Same as last GPU access - * - * Case 3: The constant buffer - * - * 1. Allocated - * 2. Written by CPU - * 3. Read by GPU - * 4. Updated (written) by CPU again - * 5. Read by GPU - * - * 1. Allocated - * (CPU, CPU) - * 2. Written by CPU - * (CPU, CPU) - * 3. Read by GPU - * (CPU+RENDER, 0) - * flush_domains = CPU - * invalidate_domains = RENDER - * clflush (obj) - * MI_FLUSH - * drm_agp_chipset_flush - * 4. Updated (written) by CPU again - * (CPU, CPU) - * flush_domains = 0 (no previous write domain) - * invalidate_domains = 0 (no new read domains) - * 5. Read by GPU - * (CPU+RENDER, 0) - * flush_domains = CPU - * invalidate_domains = RENDER - * clflush (obj) - * MI_FLUSH - * drm_agp_chipset_flush - */ -static void -i915_gem_object_set_to_gpu_domain(struct drm_i915_gem_object *obj, - struct intel_ring_buffer *ring, - struct change_domains *cd) -{ - uint32_t invalidate_domains = 0, flush_domains = 0; - - /* - * If the object isn't moving to a new write domain, - * let the object stay in multiple read domains - */ - if (obj->base.pending_write_domain == 0) - obj->base.pending_read_domains |= obj->base.read_domains; - - /* - * Flush the current write domain if - * the new read domains don't match. Invalidate - * any read domains which differ from the old - * write domain - */ - if (obj->base.write_domain && - (((obj->base.write_domain != obj->base.pending_read_domains || - obj->ring != ring)) || - (obj->fenced_gpu_access && !obj->pending_fenced_gpu_access))) { - flush_domains |= obj->base.write_domain; - invalidate_domains |= - obj->base.pending_read_domains & ~obj->base.write_domain; - } - /* - * Invalidate any read caches which may have - * stale data. That is, any new read domains. - */ - invalidate_domains |= obj->base.pending_read_domains & ~obj->base.read_domains; - if ((flush_domains | invalidate_domains) & I915_GEM_DOMAIN_CPU) - i915_gem_clflush_object(obj); - - if (obj->base.pending_write_domain) - cd->flips |= atomic_read(&obj->pending_flip); - - /* The actual obj->write_domain will be updated with - * pending_write_domain after we emit the accumulated flush for all - * of our domain changes in execbuffers (which clears objects' - * write_domains). So if we have a current write domain that we - * aren't changing, set pending_write_domain to that. - */ - if (flush_domains == 0 && obj->base.pending_write_domain == 0) - obj->base.pending_write_domain = obj->base.write_domain; - - cd->invalidate_domains |= invalidate_domains; - cd->flush_domains |= flush_domains; - if (flush_domains & I915_GEM_GPU_DOMAINS) - cd->flush_rings |= intel_ring_flag(obj->ring); - if (invalidate_domains & I915_GEM_GPU_DOMAINS) - cd->flush_rings |= intel_ring_flag(ring); -} - struct eb_objects { int and; struct hlist_head buckets[0]; @@ -810,18 +636,6 @@ err: return ret; } -static void -i915_gem_execbuffer_flush(struct drm_device *dev, - uint32_t invalidate_domains, - uint32_t flush_domains) -{ - if (flush_domains & I915_GEM_DOMAIN_CPU) - intel_gtt_chipset_flush(); - - if (flush_domains & I915_GEM_DOMAIN_GTT) - wmb(); -} - static int i915_gem_execbuffer_wait_for_flips(struct intel_ring_buffer *ring, u32 flips) { @@ -854,37 +668,41 @@ i915_gem_execbuffer_wait_for_flips(struct intel_ring_buffer *ring, u32 flips) return 0; } - static int i915_gem_execbuffer_move_to_gpu(struct intel_ring_buffer *ring, struct list_head *objects) { struct drm_i915_gem_object *obj; - struct change_domains cd; + uint32_t flush_domains = 0; + uint32_t flips = 0; int ret; - memset(&cd, 0, sizeof(cd)); - list_for_each_entry(obj, objects, exec_list) - i915_gem_object_set_to_gpu_domain(obj, ring, &cd); - - if (cd.invalidate_domains | cd.flush_domains) { - i915_gem_execbuffer_flush(ring->dev, - cd.invalidate_domains, - cd.flush_domains); - } - - if (cd.flips) { - ret = i915_gem_execbuffer_wait_for_flips(ring, cd.flips); + list_for_each_entry(obj, objects, exec_list) { + ret = i915_gem_object_sync(obj, ring); if (ret) return ret; + + if (obj->base.write_domain & I915_GEM_DOMAIN_CPU) + i915_gem_clflush_object(obj); + + if (obj->base.pending_write_domain) + flips |= atomic_read(&obj->pending_flip); + + flush_domains |= obj->base.write_domain; } - list_for_each_entry(obj, objects, exec_list) { - ret = i915_gem_object_sync(obj, ring); + if (flips) { + ret = i915_gem_execbuffer_wait_for_flips(ring, flips); if (ret) return ret; } + if (flush_domains & I915_GEM_DOMAIN_CPU) + intel_gtt_chipset_flush(); + + if (flush_domains & I915_GEM_DOMAIN_GTT) + wmb(); + /* Unconditionally invalidate gpu caches and ensure that we do flush * any residual writes from the previous batch. */ -- cgit v0.10.2 From 016fd0c1aee31902d82c1ac32312f1cc32298b66 Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Fri, 20 Jul 2012 12:41:07 +0100 Subject: drm/i915: Clear the pending_gpu_fenced_access flag at the start of execbuffer Otherwise once we use the buffer with a BLT command on gen2/3, we will always regard future command submissions as continuing the fenced access. However, now that we flush/invalidate between every batch we can drop this pessimism. Signed-off-by: Chris Wilson Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/i915_gem_execbuffer.c b/drivers/gpu/drm/i915/i915_gem_execbuffer.c index 6c81079..55a94c1 100644 --- a/drivers/gpu/drm/i915/i915_gem_execbuffer.c +++ b/drivers/gpu/drm/i915/i915_gem_execbuffer.c @@ -413,6 +413,7 @@ i915_gem_execbuffer_reserve(struct intel_ring_buffer *ring, obj->base.pending_read_domains = 0; obj->base.pending_write_domain = 0; + obj->pending_fenced_gpu_access = false; } list_splice(&ordered_objects, objects); -- cgit v0.10.2 From a7b9761d0a2ded58170ffb4d423ff3d7228103f4 Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Fri, 20 Jul 2012 12:41:08 +0100 Subject: drm/i915: Split i915_gem_flush_ring() into seperate invalidate/flush funcs By moving the function to intel_ringbuffer and currying the appropriate parameter, hopefully we make the callsites easier to read and understand. Signed-off-by: Chris Wilson Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 59e3199..700dc83 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -1256,9 +1256,6 @@ int i915_gem_wait_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv); void i915_gem_load(struct drm_device *dev); int i915_gem_init_object(struct drm_gem_object *obj); -int __must_check i915_gem_flush_ring(struct intel_ring_buffer *ring, - uint32_t invalidate_domains, - uint32_t flush_domains); struct drm_i915_gem_object *i915_gem_alloc_object(struct drm_device *dev, size_t size); void i915_gem_free_object(struct drm_gem_object *obj); diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index 3659d47..f26e2b2 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -1549,13 +1549,9 @@ i915_add_request(struct intel_ring_buffer *ring, * is that the flush _must_ happen before the next request, no matter * what. */ - if (ring->gpu_caches_dirty) { - ret = i915_gem_flush_ring(ring, 0, I915_GEM_GPU_DOMAINS); - if (ret) - return ret; - - ring->gpu_caches_dirty = false; - } + ret = intel_ring_flush_all_caches(ring); + if (ret) + return ret; if (request == NULL) { request = kmalloc(sizeof(*request), GFP_KERNEL); @@ -2254,25 +2250,6 @@ i915_gem_object_unbind(struct drm_i915_gem_object *obj) return ret; } -int -i915_gem_flush_ring(struct intel_ring_buffer *ring, - uint32_t invalidate_domains, - uint32_t flush_domains) -{ - int ret; - - if (((invalidate_domains | flush_domains) & I915_GEM_GPU_DOMAINS) == 0) - return 0; - - trace_i915_gem_ring_flush(ring, invalidate_domains, flush_domains); - - ret = ring->flush(ring, invalidate_domains, flush_domains); - if (ret) - return ret; - - return 0; -} - static int i915_ring_idle(struct intel_ring_buffer *ring) { if (list_empty(&ring->active_list)) diff --git a/drivers/gpu/drm/i915/i915_gem_execbuffer.c b/drivers/gpu/drm/i915/i915_gem_execbuffer.c index 55a94c1..6be1a89 100644 --- a/drivers/gpu/drm/i915/i915_gem_execbuffer.c +++ b/drivers/gpu/drm/i915/i915_gem_execbuffer.c @@ -707,14 +707,7 @@ i915_gem_execbuffer_move_to_gpu(struct intel_ring_buffer *ring, /* Unconditionally invalidate gpu caches and ensure that we do flush * any residual writes from the previous batch. */ - ret = i915_gem_flush_ring(ring, - I915_GEM_GPU_DOMAINS, - ring->gpu_caches_dirty ? I915_GEM_GPU_DOMAINS : 0); - if (ret) - return ret; - - ring->gpu_caches_dirty = false; - return 0; + return intel_ring_invalidate_all_caches(ring); } static bool diff --git a/drivers/gpu/drm/i915/intel_ringbuffer.c b/drivers/gpu/drm/i915/intel_ringbuffer.c index 8f221d9..8b7085e 100644 --- a/drivers/gpu/drm/i915/intel_ringbuffer.c +++ b/drivers/gpu/drm/i915/intel_ringbuffer.c @@ -1564,3 +1564,41 @@ int intel_init_blt_ring_buffer(struct drm_device *dev) return intel_init_ring_buffer(dev, ring); } + +int +intel_ring_flush_all_caches(struct intel_ring_buffer *ring) +{ + int ret; + + if (!ring->gpu_caches_dirty) + return 0; + + ret = ring->flush(ring, 0, I915_GEM_GPU_DOMAINS); + if (ret) + return ret; + + trace_i915_gem_ring_flush(ring, 0, I915_GEM_GPU_DOMAINS); + + ring->gpu_caches_dirty = false; + return 0; +} + +int +intel_ring_invalidate_all_caches(struct intel_ring_buffer *ring) +{ + uint32_t flush_domains; + int ret; + + flush_domains = 0; + if (ring->gpu_caches_dirty) + flush_domains = I915_GEM_GPU_DOMAINS; + + ret = ring->flush(ring, I915_GEM_GPU_DOMAINS, flush_domains); + if (ret) + return ret; + + trace_i915_gem_ring_flush(ring, I915_GEM_GPU_DOMAINS, flush_domains); + + ring->gpu_caches_dirty = false; + return 0; +} diff --git a/drivers/gpu/drm/i915/intel_ringbuffer.h b/drivers/gpu/drm/i915/intel_ringbuffer.h index 7986f30..8b2b92e 100644 --- a/drivers/gpu/drm/i915/intel_ringbuffer.h +++ b/drivers/gpu/drm/i915/intel_ringbuffer.h @@ -195,6 +195,8 @@ static inline void intel_ring_emit(struct intel_ring_buffer *ring, void intel_ring_advance(struct intel_ring_buffer *ring); u32 intel_ring_get_seqno(struct intel_ring_buffer *ring); +int intel_ring_flush_all_caches(struct intel_ring_buffer *ring); +int intel_ring_invalidate_all_caches(struct intel_ring_buffer *ring); int intel_init_render_ring_buffer(struct drm_device *dev); int intel_init_bsd_ring_buffer(struct drm_device *dev); -- cgit v0.10.2 From f047e395ddc9da6c307a10629a237502e627ed85 Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Sat, 21 Jul 2012 12:31:41 +0100 Subject: drm/i915: Avoid concurrent access when marking the device as idle/busy As suggested by Daniel, rip out the independent timers for device and crtc busyness and integrate the manual powermanagement of the display engine into the GEM core and its request tracking. The benefits are that the code is a lot smaller, fewer moving parts and should fit more neatly into the overall activity tracking of the driver. v2: Complete overhaul and removal of the racy timers and workers. Signed-off-by: Chris Wilson Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 700dc83..f176589 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -785,9 +785,6 @@ typedef struct drm_i915_private { bool lvds_downclock_avail; /* indicates the reduced downclock for LVDS*/ int lvds_downclock; - struct work_struct idle_work; - struct timer_list idle_timer; - bool busy; u16 orig_clock; int child_dev_num; struct child_device_config *child_dev; diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index f26e2b2..b274810 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -1462,11 +1462,14 @@ i915_gem_object_move_to_inactive(struct drm_i915_gem_object *obj) struct drm_device *dev = obj->base.dev; struct drm_i915_private *dev_priv = dev->dev_private; - list_move_tail(&obj->mm_list, &dev_priv->mm.inactive_list); - BUG_ON(obj->base.write_domain & ~I915_GEM_GPU_DOMAINS); BUG_ON(!obj->active); + if (obj->pin_count) /* are we a framebuffer? */ + intel_mark_fb_idle(obj); + + list_move_tail(&obj->mm_list, &dev_priv->mm.inactive_list); + list_del_init(&obj->ring_list); obj->ring = NULL; @@ -1602,9 +1605,11 @@ i915_add_request(struct intel_ring_buffer *ring, jiffies + msecs_to_jiffies(DRM_I915_HANGCHECK_PERIOD)); } - if (was_empty) + if (was_empty) { queue_delayed_work(dev_priv->wq, &dev_priv->mm.retire_work, HZ); + intel_mark_busy(dev_priv->dev); + } } return 0; @@ -1810,6 +1815,8 @@ i915_gem_retire_work_handler(struct work_struct *work) if (!dev_priv->mm.suspended && !idle) queue_delayed_work(dev_priv->wq, &dev_priv->mm.retire_work, HZ); + if (idle) + intel_mark_idle(dev); mutex_unlock(&dev->struct_mutex); } diff --git a/drivers/gpu/drm/i915/i915_gem_execbuffer.c b/drivers/gpu/drm/i915/i915_gem_execbuffer.c index 6be1a89..25b2c54 100644 --- a/drivers/gpu/drm/i915/i915_gem_execbuffer.c +++ b/drivers/gpu/drm/i915/i915_gem_execbuffer.c @@ -767,13 +767,11 @@ i915_gem_execbuffer_move_to_active(struct list_head *objects, obj->dirty = 1; obj->last_write_seqno = seqno; if (obj->pin_count) /* check for potential scanout */ - intel_mark_busy(ring->dev, obj); + intel_mark_fb_busy(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 93d9934..b463829 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -5860,46 +5860,6 @@ struct drm_display_mode *intel_crtc_mode_get(struct drm_device *dev, return mode; } -#define GPU_IDLE_TIMEOUT 500 /* ms */ - -/* When this timer fires, we've been idle for awhile */ -static void intel_gpu_idle_timer(unsigned long arg) -{ - struct drm_device *dev = (struct drm_device *)arg; - drm_i915_private_t *dev_priv = dev->dev_private; - - if (!list_empty(&dev_priv->mm.active_list)) { - /* Still processing requests, so just re-arm the timer. */ - mod_timer(&dev_priv->idle_timer, jiffies + - msecs_to_jiffies(GPU_IDLE_TIMEOUT)); - return; - } - - dev_priv->busy = false; - queue_work(dev_priv->wq, &dev_priv->idle_work); -} - -#define CRTC_IDLE_TIMEOUT 1000 /* ms */ - -static void intel_crtc_idle_timer(unsigned long arg) -{ - struct intel_crtc *intel_crtc = (struct intel_crtc *)arg; - struct drm_crtc *crtc = &intel_crtc->base; - drm_i915_private_t *dev_priv = crtc->dev->dev_private; - struct intel_framebuffer *intel_fb; - - intel_fb = to_intel_framebuffer(crtc->fb); - if (intel_fb && intel_fb->obj->active) { - /* The framebuffer is still being accessed by the GPU. */ - mod_timer(&intel_crtc->idle_timer, jiffies + - msecs_to_jiffies(CRTC_IDLE_TIMEOUT)); - return; - } - - intel_crtc->busy = false; - queue_work(dev_priv->wq, &dev_priv->idle_work); -} - static void intel_increase_pllclock(struct drm_crtc *crtc) { struct drm_device *dev = crtc->dev; @@ -5929,10 +5889,6 @@ static void intel_increase_pllclock(struct drm_crtc *crtc) if (dpll & DISPLAY_RATE_SELECT_FPA1) DRM_DEBUG_DRIVER("failed to upclock LVDS!\n"); } - - /* Schedule downclock */ - mod_timer(&intel_crtc->idle_timer, jiffies + - msecs_to_jiffies(CRTC_IDLE_TIMEOUT)); } static void intel_decrease_pllclock(struct drm_crtc *crtc) @@ -5971,89 +5927,48 @@ static void intel_decrease_pllclock(struct drm_crtc *crtc) } -/** - * intel_idle_update - adjust clocks for idleness - * @work: work struct - * - * Either the GPU or display (or both) went idle. Check the busy status - * here and adjust the CRTC and GPU clocks as necessary. - */ -static void intel_idle_update(struct work_struct *work) +void intel_mark_busy(struct drm_device *dev) +{ + intel_sanitize_pm(dev); + i915_update_gfx_val(dev->dev_private); +} + +void intel_mark_idle(struct drm_device *dev) { - drm_i915_private_t *dev_priv = container_of(work, drm_i915_private_t, - idle_work); - struct drm_device *dev = dev_priv->dev; + intel_sanitize_pm(dev); +} + +void intel_mark_fb_busy(struct drm_i915_gem_object *obj) +{ + struct drm_device *dev = obj->base.dev; struct drm_crtc *crtc; - struct intel_crtc *intel_crtc; if (!i915_powersave) return; - mutex_lock(&dev->struct_mutex); - - i915_update_gfx_val(dev_priv); - list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { - /* Skip inactive CRTCs */ if (!crtc->fb) continue; - intel_crtc = to_intel_crtc(crtc); - if (!intel_crtc->busy) - intel_decrease_pllclock(crtc); + if (to_intel_framebuffer(crtc->fb)->obj == obj) + intel_increase_pllclock(crtc); } - - - mutex_unlock(&dev->struct_mutex); } -/** - * intel_mark_busy - mark the GPU and possibly the display busy - * @dev: drm device - * @obj: object we're operating on - * - * Callers can use this function to indicate that the GPU is busy processing - * commands. If @obj matches one of the CRTC objects (i.e. it's a scanout - * buffer), we'll also mark the display as busy, so we know to increase its - * clock frequency. - */ -void intel_mark_busy(struct drm_device *dev, struct drm_i915_gem_object *obj) +void intel_mark_fb_idle(struct drm_i915_gem_object *obj) { - drm_i915_private_t *dev_priv = dev->dev_private; - struct drm_crtc *crtc = NULL; - struct intel_framebuffer *intel_fb; - struct intel_crtc *intel_crtc; - - if (!drm_core_check_feature(dev, DRIVER_MODESET)) - return; - - if (!dev_priv->busy) { - intel_sanitize_pm(dev); - dev_priv->busy = true; - } else - mod_timer(&dev_priv->idle_timer, jiffies + - msecs_to_jiffies(GPU_IDLE_TIMEOUT)); + struct drm_device *dev = obj->base.dev; + struct drm_crtc *crtc; - if (obj == NULL) + if (!i915_powersave) return; list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { if (!crtc->fb) continue; - intel_crtc = to_intel_crtc(crtc); - intel_fb = to_intel_framebuffer(crtc->fb); - if (intel_fb->obj == obj) { - if (!intel_crtc->busy) { - /* Non-busy -> busy, upclock */ - intel_increase_pllclock(crtc); - intel_crtc->busy = true; - } else { - /* Busy -> busy, put off timer */ - mod_timer(&intel_crtc->idle_timer, jiffies + - msecs_to_jiffies(CRTC_IDLE_TIMEOUT)); - } - } + if (to_intel_framebuffer(crtc->fb)->obj == obj) + intel_decrease_pllclock(crtc); } } @@ -6512,7 +6427,7 @@ static int intel_crtc_page_flip(struct drm_crtc *crtc, goto cleanup_pending; intel_disable_fbc(dev); - intel_mark_busy(dev, obj); + intel_mark_fb_busy(obj); mutex_unlock(&dev->struct_mutex); trace_i915_flip_request(intel_crtc->plane, obj); @@ -6678,11 +6593,6 @@ static void intel_crtc_init(struct drm_device *dev, int pipe) } drm_crtc_helper_add(&intel_crtc->base, &intel_helper_funcs); - - intel_crtc->busy = false; - - setup_timer(&intel_crtc->idle_timer, intel_crtc_idle_timer, - (unsigned long)intel_crtc); } int intel_get_pipe_from_crtc_id(struct drm_device *dev, void *data, @@ -7265,10 +7175,6 @@ void intel_modeset_init(struct drm_device *dev) /* Just disable it once at startup */ i915_disable_vga(dev); intel_setup_outputs(dev); - - INIT_WORK(&dev_priv->idle_work, intel_idle_update); - setup_timer(&dev_priv->idle_timer, intel_gpu_idle_timer, - (unsigned long)dev); } void intel_modeset_gem_init(struct drm_device *dev) @@ -7319,14 +7225,6 @@ void intel_modeset_cleanup(struct drm_device *dev) /* flush any delayed tasks or pending work */ flush_scheduled_work(); - /* Shut off idle work before the crtcs get freed. */ - list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { - intel_crtc = to_intel_crtc(crtc); - del_timer_sync(&intel_crtc->idle_timer); - } - del_timer_sync(&dev_priv->idle_timer); - cancel_work_sync(&dev_priv->idle_work); - drm_mode_config_cleanup(dev); } diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index 2846f5e..8c7f483 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -156,8 +156,6 @@ struct intel_crtc { int dpms_mode; bool active; /* is the crtc on? independent of the dpms mode */ bool primary_disabled; /* is the crtc obscured by a plane? */ - bool busy; /* is scanout buffer being updated frequently? */ - struct timer_list idle_timer; bool lowfreq_avail; struct intel_overlay *overlay; struct intel_unpin_work *unpin_work; @@ -373,8 +371,10 @@ 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); extern void intel_tv_init(struct drm_device *dev); -extern void intel_mark_busy(struct drm_device *dev, - struct drm_i915_gem_object *obj); +extern void intel_mark_busy(struct drm_device *dev); +extern void intel_mark_idle(struct drm_device *dev); +extern void intel_mark_fb_busy(struct drm_i915_gem_object *obj); +extern void intel_mark_fb_idle(struct drm_i915_gem_object *obj); extern bool intel_lvds_init(struct drm_device *dev); extern void intel_dp_init(struct drm_device *dev, int output_reg, enum port port); diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c index 94aabca..85d1b1c 100644 --- a/drivers/gpu/drm/i915/intel_pm.c +++ b/drivers/gpu/drm/i915/intel_pm.c @@ -3075,14 +3075,17 @@ EXPORT_SYMBOL_GPL(i915_gpu_lower); bool i915_gpu_busy(void) { struct drm_i915_private *dev_priv; + struct intel_ring_buffer *ring; bool ret = false; + int i; spin_lock(&mchdev_lock); if (!i915_mch_dev) goto out_unlock; dev_priv = i915_mch_dev; - ret = dev_priv->busy; + for_each_ring(ring, dev_priv, i) + ret |= !list_empty(&ring->request_list); out_unlock: spin_unlock(&mchdev_lock); -- cgit v0.10.2 From 2e4291e0bc6cff9514515a899a8158ea62b3ff90 Mon Sep 17 00:00:00 2001 From: Ben Widawsky Date: Tue, 24 Jul 2012 20:47:30 -0700 Subject: drm/i915: Add contexts for HSW Basic context support on HSW is no different than previous generations. The size of the context object changes, but that's about it. Signed-off-by: Ben Widawsky Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/i915_gem_context.c b/drivers/gpu/drm/i915/i915_gem_context.c index 5d0d6ad..5c2d354 100644 --- a/drivers/gpu/drm/i915/i915_gem_context.c +++ b/drivers/gpu/drm/i915/i915_gem_context.c @@ -112,7 +112,10 @@ static int get_context_size(struct drm_device *dev) break; case 7: reg = I915_READ(GEN7_CXT_SIZE); - ret = GEN7_CXT_TOTAL_SIZE(reg) * 64; + if (IS_HASWELL(dev)) + ret = HSW_CXT_TOTAL_SIZE(reg) * 64; + else + ret = GEN7_CXT_TOTAL_SIZE(reg) * 64; break; default: BUG(); diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h index 81a3de6..1310caa 100644 --- a/drivers/gpu/drm/i915/i915_reg.h +++ b/drivers/gpu/drm/i915/i915_reg.h @@ -1498,6 +1498,14 @@ GEN7_CXT_EXTENDED_SIZE(ctx_reg) + \ GEN7_CXT_GT1_SIZE(ctx_reg) + \ GEN7_CXT_VFSTATE_SIZE(ctx_reg)) +#define HSW_CXT_POWER_SIZE(ctx_reg) ((ctx_reg >> 26) & 0x3f) +#define HSW_CXT_RING_SIZE(ctx_reg) ((ctx_reg >> 23) & 0x7) +#define HSW_CXT_RENDER_SIZE(ctx_reg) ((ctx_reg >> 15) & 0xff) +#define HSW_CXT_TOTAL_SIZE(ctx_reg) (HSW_CXT_POWER_SIZE(ctx_reg) + \ + HSW_CXT_RING_SIZE(ctx_reg) + \ + HSW_CXT_RENDER_SIZE(ctx_reg) + \ + GEN7_CXT_VFSTATE_SIZE(ctx_reg)) + /* * Overlay regs -- cgit v0.10.2 From e1ef7cc299839e68dae3f1843f62e52acda04538 Mon Sep 17 00:00:00 2001 From: Ben Widawsky Date: Tue, 24 Jul 2012 20:47:31 -0700 Subject: drm/i915: Macro to determine DPF support Originally I had a macro specifically for DPF support, and Daniel, with good reason asked me to change it to this. It's not the way I would have gone (and indeed I didn't), but for now there is no distinction as all platforms with L3 also have DPF. Note: The good reasons are that dpf is a l3$ feature (at least on currrent hw), hence I don't expect one to go without the other. Signed-off-by: Ben Widawsky [danvet: added note] Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index f176589..35a90da 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -1114,6 +1114,8 @@ struct drm_i915_file_private { #define HAS_FORCE_WAKE(dev) (INTEL_INFO(dev)->has_force_wake) +#define HAS_L3_GPU_CACHE(dev) (IS_IVYBRIDGE(dev)) + #include "i915_trace.h" /** diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index 41ed41d..440c905 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -444,7 +444,7 @@ static void ivybridge_handle_parity_error(struct drm_device *dev) drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; unsigned long flags; - if (!IS_IVYBRIDGE(dev)) + if (!HAS_L3_GPU_CACHE(dev)) return; spin_lock_irqsave(&dev_priv->irq_lock, flags); diff --git a/drivers/gpu/drm/i915/i915_sysfs.c b/drivers/gpu/drm/i915/i915_sysfs.c index 2f5388a..77a97bf 100644 --- a/drivers/gpu/drm/i915/i915_sysfs.c +++ b/drivers/gpu/drm/i915/i915_sysfs.c @@ -212,7 +212,7 @@ void i915_setup_sysfs(struct drm_device *dev) DRM_ERROR("RC6 residency sysfs setup failed\n"); } - if (IS_IVYBRIDGE(dev)) { + if (HAS_L3_GPU_CACHE(dev)) { ret = device_create_bin_file(&dev->primary->kdev, &dpf_attrs); if (ret) DRM_ERROR("l3 parity sysfs setup failed\n"); diff --git a/drivers/gpu/drm/i915/intel_ringbuffer.c b/drivers/gpu/drm/i915/intel_ringbuffer.c index 8b7085e..c58f1b9 100644 --- a/drivers/gpu/drm/i915/intel_ringbuffer.c +++ b/drivers/gpu/drm/i915/intel_ringbuffer.c @@ -454,7 +454,7 @@ static int init_render_ring(struct intel_ring_buffer *ring) if (INTEL_INFO(dev)->gen >= 6) I915_WRITE(INSTPM, _MASKED_BIT_ENABLE(INSTPM_FORCE_ORDERING)); - if (IS_IVYBRIDGE(dev)) + if (HAS_L3_GPU_CACHE(dev)) I915_WRITE_IMR(ring, ~GEN6_RENDER_L3_PARITY_ERROR); return ret; @@ -844,7 +844,7 @@ gen6_ring_get_irq(struct intel_ring_buffer *ring) spin_lock_irqsave(&dev_priv->irq_lock, flags); if (ring->irq_refcount++ == 0) { - if (IS_IVYBRIDGE(dev) && ring->id == RCS) + if (HAS_L3_GPU_CACHE(dev) && ring->id == RCS) I915_WRITE_IMR(ring, ~(ring->irq_enable_mask | GEN6_RENDER_L3_PARITY_ERROR)); else @@ -867,7 +867,7 @@ gen6_ring_put_irq(struct intel_ring_buffer *ring) spin_lock_irqsave(&dev_priv->irq_lock, flags); if (--ring->irq_refcount == 0) { - if (IS_IVYBRIDGE(dev) && ring->id == RCS) + if (HAS_L3_GPU_CACHE(dev) && ring->id == RCS) I915_WRITE_IMR(ring, ~GEN6_RENDER_L3_PARITY_ERROR); else I915_WRITE_IMR(ring, ~0); -- cgit v0.10.2 From f27b92651d72e863c308ea5dca5615fc98e38ca6 Mon Sep 17 00:00:00 2001 From: Ben Widawsky Date: Tue, 24 Jul 2012 20:47:32 -0700 Subject: drm/i915: Expand DPF support to Haswell Signed-off-by: Ben Widawsky Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 35a90da..e6e63c1 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -1114,7 +1114,7 @@ struct drm_i915_file_private { #define HAS_FORCE_WAKE(dev) (INTEL_INFO(dev)->has_force_wake) -#define HAS_L3_GPU_CACHE(dev) (IS_IVYBRIDGE(dev)) +#define HAS_L3_GPU_CACHE(dev) (IS_IVYBRIDGE(dev) || IS_HASWELL(dev)) #include "i915_trace.h" -- cgit v0.10.2 From 42d6ab4839799b2f246748ce663d6b023f02bb73 Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Thu, 26 Jul 2012 11:49:32 +0100 Subject: drm/i915: Segregate memory domains in the GTT using coloring Several functions of the GPU have the restriction that differing memory domains cannot be placed next to each other (as the GPU may prefetch beyond the end of one domain and hang as it crosses into the other domain). We use the facility of the drm_mm to mark ranges with a particular color that corresponds to the cache attributes of those pages in order to prevent allocating adjacent blocks of differing memory types. v2: Rebase ontop of drm_mm coloring v2. v3: Fix rebinding existing gtt_space and add a verification routine. Signed-off-by: Chris Wilson Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index e6e63c1..270b31c 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -109,6 +109,7 @@ struct intel_pch_pll { #define WATCH_COHERENCY 0 #define WATCH_LISTS 0 +#define WATCH_GTT 0 #define I915_GEM_PHYS_CURSOR_0 1 #define I915_GEM_PHYS_CURSOR_1 2 @@ -1406,7 +1407,9 @@ void i915_gem_init_global_gtt(struct drm_device *dev, /* i915_gem_evict.c */ int __must_check i915_gem_evict_something(struct drm_device *dev, int min_size, - unsigned alignment, bool mappable); + unsigned alignment, + unsigned cache_level, + bool mappable); int i915_gem_evict_everything(struct drm_device *dev, bool purgeable_only); /* i915_gem_stolen.c */ diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index b274810..19bdc24 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -2586,6 +2586,76 @@ i915_gem_object_get_fence(struct drm_i915_gem_object *obj) return 0; } +static bool i915_gem_valid_gtt_space(struct drm_device *dev, + struct drm_mm_node *gtt_space, + unsigned long cache_level) +{ + struct drm_mm_node *other; + + /* On non-LLC machines we have to be careful when putting differing + * types of snoopable memory together to avoid the prefetcher + * crossing memory domains and dieing. + */ + if (HAS_LLC(dev)) + return true; + + if (gtt_space == NULL) + return true; + + if (list_empty(>t_space->node_list)) + return true; + + other = list_entry(gtt_space->node_list.prev, struct drm_mm_node, node_list); + if (other->allocated && !other->hole_follows && other->color != cache_level) + return false; + + other = list_entry(gtt_space->node_list.next, struct drm_mm_node, node_list); + if (other->allocated && !gtt_space->hole_follows && other->color != cache_level) + return false; + + return true; +} + +static void i915_gem_verify_gtt(struct drm_device *dev) +{ +#if WATCH_GTT + struct drm_i915_private *dev_priv = dev->dev_private; + struct drm_i915_gem_object *obj; + int err = 0; + + list_for_each_entry(obj, &dev_priv->mm.gtt_list, gtt_list) { + if (obj->gtt_space == NULL) { + printk(KERN_ERR "object found on GTT list with no space reserved\n"); + err++; + continue; + } + + if (obj->cache_level != obj->gtt_space->color) { + printk(KERN_ERR "object reserved space [%08lx, %08lx] with wrong color, cache_level=%x, color=%lx\n", + obj->gtt_space->start, + obj->gtt_space->start + obj->gtt_space->size, + obj->cache_level, + obj->gtt_space->color); + err++; + continue; + } + + if (!i915_gem_valid_gtt_space(dev, + obj->gtt_space, + obj->cache_level)) { + printk(KERN_ERR "invalid GTT space found at [%08lx, %08lx] - color=%x\n", + obj->gtt_space->start, + obj->gtt_space->start + obj->gtt_space->size, + obj->cache_level); + err++; + continue; + } + } + + WARN_ON(err); +#endif +} + /** * Finds free space in the GTT aperture and binds the object there. */ @@ -2640,36 +2710,47 @@ i915_gem_object_bind_to_gtt(struct drm_i915_gem_object *obj, search_free: if (map_and_fenceable) free_space = - drm_mm_search_free_in_range(&dev_priv->mm.gtt_space, - size, alignment, - 0, dev_priv->mm.gtt_mappable_end, - 0); + drm_mm_search_free_in_range_color(&dev_priv->mm.gtt_space, + size, alignment, obj->cache_level, + 0, dev_priv->mm.gtt_mappable_end, + false); else - free_space = drm_mm_search_free(&dev_priv->mm.gtt_space, - size, alignment, 0); + free_space = drm_mm_search_free_color(&dev_priv->mm.gtt_space, + size, alignment, obj->cache_level, + false); if (free_space != NULL) { if (map_and_fenceable) obj->gtt_space = drm_mm_get_block_range_generic(free_space, - size, alignment, 0, + size, alignment, obj->cache_level, 0, dev_priv->mm.gtt_mappable_end, - 0); + false); else obj->gtt_space = - drm_mm_get_block(free_space, size, alignment); + drm_mm_get_block_generic(free_space, + size, alignment, obj->cache_level, + false); } if (obj->gtt_space == NULL) { /* If the gtt is empty and we're still having trouble * fitting our object in, we're out of memory. */ ret = i915_gem_evict_something(dev, size, alignment, + obj->cache_level, map_and_fenceable); if (ret) return ret; goto search_free; } + if (WARN_ON(!i915_gem_valid_gtt_space(dev, + obj->gtt_space, + obj->cache_level))) { + drm_mm_put_block(obj->gtt_space); + obj->gtt_space = NULL; + return -EINVAL; + } ret = i915_gem_object_get_pages_gtt(obj, gfpmask); if (ret) { @@ -2732,6 +2813,7 @@ i915_gem_object_bind_to_gtt(struct drm_i915_gem_object *obj, obj->map_and_fenceable = mappable && fenceable; trace_i915_gem_object_bind(obj, map_and_fenceable); + i915_gem_verify_gtt(dev); return 0; } @@ -2873,6 +2955,12 @@ int i915_gem_object_set_cache_level(struct drm_i915_gem_object *obj, return -EBUSY; } + if (!i915_gem_valid_gtt_space(dev, obj->gtt_space, cache_level)) { + ret = i915_gem_object_unbind(obj); + if (ret) + return ret; + } + if (obj->gtt_space) { ret = i915_gem_object_finish_gpu(obj); if (ret) @@ -2884,7 +2972,7 @@ int i915_gem_object_set_cache_level(struct drm_i915_gem_object *obj, * registers with snooped memory, so relinquish any fences * currently pointing to our region in the aperture. */ - if (INTEL_INFO(obj->base.dev)->gen < 6) { + if (INTEL_INFO(dev)->gen < 6) { ret = i915_gem_object_put_fence(obj); if (ret) return ret; @@ -2895,6 +2983,8 @@ int i915_gem_object_set_cache_level(struct drm_i915_gem_object *obj, if (obj->has_aliasing_ppgtt_mapping) i915_ppgtt_bind_object(dev_priv->mm.aliasing_ppgtt, obj, cache_level); + + obj->gtt_space->color = cache_level; } if (cache_level == I915_CACHE_NONE) { @@ -2921,6 +3011,7 @@ int i915_gem_object_set_cache_level(struct drm_i915_gem_object *obj, } obj->cache_level = cache_level; + i915_gem_verify_gtt(dev); return 0; } diff --git a/drivers/gpu/drm/i915/i915_gem_evict.c b/drivers/gpu/drm/i915/i915_gem_evict.c index 51e547c..7279c31 100644 --- a/drivers/gpu/drm/i915/i915_gem_evict.c +++ b/drivers/gpu/drm/i915/i915_gem_evict.c @@ -44,7 +44,8 @@ mark_free(struct drm_i915_gem_object *obj, struct list_head *unwind) int i915_gem_evict_something(struct drm_device *dev, int min_size, - unsigned alignment, bool mappable) + unsigned alignment, unsigned cache_level, + bool mappable) { drm_i915_private_t *dev_priv = dev->dev_private; struct list_head eviction_list, unwind_list; @@ -79,11 +80,11 @@ i915_gem_evict_something(struct drm_device *dev, int min_size, INIT_LIST_HEAD(&unwind_list); if (mappable) drm_mm_init_scan_with_range(&dev_priv->mm.gtt_space, - min_size, alignment, 0, + min_size, alignment, cache_level, 0, dev_priv->mm.gtt_mappable_end); else drm_mm_init_scan(&dev_priv->mm.gtt_space, - min_size, alignment, 0); + min_size, alignment, cache_level); /* First see if there is a large enough contiguous idle region... */ list_for_each_entry(obj, &dev_priv->mm.inactive_list, mm_list) { diff --git a/drivers/gpu/drm/i915/i915_gem_gtt.c b/drivers/gpu/drm/i915/i915_gem_gtt.c index 9fd25a4..4584f7f 100644 --- a/drivers/gpu/drm/i915/i915_gem_gtt.c +++ b/drivers/gpu/drm/i915/i915_gem_gtt.c @@ -422,6 +422,23 @@ void i915_gem_gtt_finish_object(struct drm_i915_gem_object *obj) undo_idling(dev_priv, interruptible); } +static void i915_gtt_color_adjust(struct drm_mm_node *node, + unsigned long color, + unsigned long *start, + unsigned long *end) +{ + if (node->color != color) + *start += 4096; + + if (!list_empty(&node->node_list)) { + node = list_entry(node->node_list.next, + struct drm_mm_node, + node_list); + if (node->allocated && node->color != color) + *end -= 4096; + } +} + void i915_gem_init_global_gtt(struct drm_device *dev, unsigned long start, unsigned long mappable_end, @@ -431,6 +448,8 @@ void i915_gem_init_global_gtt(struct drm_device *dev, /* Substract the guard page ... */ drm_mm_init(&dev_priv->mm.gtt_space, start, end - start - PAGE_SIZE); + if (!HAS_LLC(dev)) + dev_priv->mm.gtt_space.color_adjust = i915_gtt_color_adjust; dev_priv->mm.gtt_start = start; dev_priv->mm.gtt_mappable_end = mappable_end; -- cgit v0.10.2 From e6994aeedcee4f71998d89d2c10c5baa419ebeac Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Tue, 10 Jul 2012 10:27:08 +0100 Subject: drm/i915: Export ability of changing cache levels to userspace By selecting the cache level (essentially whether or not the CPU snoops any updates to the bo, and on more recent machines whether it resides inside the CPU's last-level-cache) a userspace driver is able to then manage all of its memory within buffer objects, if it so desires. This enables the userspace driver to accelerate uploads and more importantly downloads from the GPU and to able to mix CPU and GPU rendering/activity efficiently. Signed-off-by: Chris Wilson [danvet: Added code comment about where we plan to stuff platform specific cacheing control bits in the ioctl struct.] Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/i915_dma.c b/drivers/gpu/drm/i915/i915_dma.c index 733744f..71672ce 100644 --- a/drivers/gpu/drm/i915/i915_dma.c +++ b/drivers/gpu/drm/i915/i915_dma.c @@ -1835,6 +1835,8 @@ struct drm_ioctl_desc i915_ioctls[] = { DRM_IOCTL_DEF_DRV(I915_GEM_PIN, i915_gem_pin_ioctl, DRM_AUTH|DRM_ROOT_ONLY|DRM_UNLOCKED), DRM_IOCTL_DEF_DRV(I915_GEM_UNPIN, i915_gem_unpin_ioctl, DRM_AUTH|DRM_ROOT_ONLY|DRM_UNLOCKED), DRM_IOCTL_DEF_DRV(I915_GEM_BUSY, i915_gem_busy_ioctl, DRM_AUTH|DRM_UNLOCKED), + DRM_IOCTL_DEF_DRV(I915_GEM_SET_CACHEING, i915_gem_set_cacheing_ioctl, DRM_UNLOCKED), + DRM_IOCTL_DEF_DRV(I915_GEM_GET_CACHEING, i915_gem_get_cacheing_ioctl, DRM_UNLOCKED), DRM_IOCTL_DEF_DRV(I915_GEM_THROTTLE, i915_gem_throttle_ioctl, DRM_AUTH|DRM_UNLOCKED), DRM_IOCTL_DEF_DRV(I915_GEM_ENTERVT, i915_gem_entervt_ioctl, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY|DRM_UNLOCKED), DRM_IOCTL_DEF_DRV(I915_GEM_LEAVEVT, i915_gem_leavevt_ioctl, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY|DRM_UNLOCKED), diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 270b31c..0dc89a4 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -848,9 +848,9 @@ enum hdmi_force_audio { }; enum i915_cache_level { - I915_CACHE_NONE, + I915_CACHE_NONE = 0, I915_CACHE_LLC, - I915_CACHE_LLC_MLC, /* gen6+ */ + I915_CACHE_LLC_MLC, /* gen6+, in docs at least! */ }; struct drm_i915_gem_object { @@ -1238,6 +1238,10 @@ int i915_gem_unpin_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv); int i915_gem_busy_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv); +int i915_gem_get_cacheing_ioctl(struct drm_device *dev, void *data, + struct drm_file *file); +int i915_gem_set_cacheing_ioctl(struct drm_device *dev, void *data, + struct drm_file *file); int i915_gem_throttle_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv); int i915_gem_madvise_ioctl(struct drm_device *dev, void *data, diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index 19bdc24..c540321 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -3015,6 +3015,68 @@ int i915_gem_object_set_cache_level(struct drm_i915_gem_object *obj, return 0; } +int i915_gem_get_cacheing_ioctl(struct drm_device *dev, void *data, + struct drm_file *file) +{ + struct drm_i915_gem_cacheing *args = data; + struct drm_i915_gem_object *obj; + int ret; + + ret = i915_mutex_lock_interruptible(dev); + if (ret) + return ret; + + obj = to_intel_bo(drm_gem_object_lookup(dev, file, args->handle)); + if (&obj->base == NULL) { + ret = -ENOENT; + goto unlock; + } + + args->cacheing = obj->cache_level != I915_CACHE_NONE; + + drm_gem_object_unreference(&obj->base); +unlock: + mutex_unlock(&dev->struct_mutex); + return ret; +} + +int i915_gem_set_cacheing_ioctl(struct drm_device *dev, void *data, + struct drm_file *file) +{ + struct drm_i915_gem_cacheing *args = data; + struct drm_i915_gem_object *obj; + enum i915_cache_level level; + int ret; + + ret = i915_mutex_lock_interruptible(dev); + if (ret) + return ret; + + switch (args->cacheing) { + case I915_CACHEING_NONE: + level = I915_CACHE_NONE; + break; + case I915_CACHEING_CACHED: + level = I915_CACHE_LLC; + break; + default: + return -EINVAL; + } + + obj = to_intel_bo(drm_gem_object_lookup(dev, file, args->handle)); + if (&obj->base == NULL) { + ret = -ENOENT; + goto unlock; + } + + ret = i915_gem_object_set_cache_level(obj, level); + + drm_gem_object_unreference(&obj->base); +unlock: + mutex_unlock(&dev->struct_mutex); + return ret; +} + /* * Prepare buffer for display plane (scanout, cursors, etc). * Can be called from an uninterruptible phase (modesetting) and allows diff --git a/include/drm/i915_drm.h b/include/drm/i915_drm.h index 0f149fe..772b0d6 100644 --- a/include/drm/i915_drm.h +++ b/include/drm/i915_drm.h @@ -716,10 +716,16 @@ struct drm_i915_gem_busy { #define I915_CACHEING_CACHED 1 struct drm_i915_gem_cacheing { - /** Handle of the buffer to set/get the cacheing level of */ + /** + * Handle of the buffer to set/get the cacheing level of. */ __u32 handle; - /** Cacheing level to apply or return value */ + /** + * Cacheing level to apply or return value + * + * bits0-15 are for generic cacheing control (i.e. the above defined + * values). bits16-31 are reserved for platform-specific variations + * (e.g. l3$ caching on gen7). */ __u32 cacheing; }; -- cgit v0.10.2 From 20b46e59dd102665ce7168baa215e5b1ee66b69b Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Thu, 26 Jul 2012 11:16:14 +0200 Subject: drm/i915: Only set the down rps limit when at the loweset frequency The power docs say that when the gt leaves rc6, it is in the lowest frequency and only about 25 usec later will switch to the frequency selected in GEN6_RPNSWREQ. If the downclock limit expires in that window and the down limit is set to the lowest possible frequency, the hw will not send the down interrupt. Which leads to a too high gpu clock and wasted power. Chris Wilson already worked on this with commit 7b9e0ae6da0a7eaf2680a1a788f08df123724f3b Author: Chris Wilson Date: Sat Apr 28 08:56:39 2012 +0100 drm/i915: Always update RPS interrupts thresholds along with frequency but got the logic inverted: The current code set the down limit as long as we haven't reached it. Instead of only once with reached the lowest frequency. Note that we can't always set the downclock limit to 0, because otherwise the hw will keep on bugging us with downclock request irqs once the lowest level is reached. For similar reasons also always set the upclock limit, otherwise the hw might poke us again with interrupts. v2: Chris Wilson noticed that the limit reg is also computed in sanitize_pm. To avoid duplication, extract the code into a common function. Reviewed-by: Chris Wilson Signed-Off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c index 85d1b1c..e8727da 100644 --- a/drivers/gpu/drm/i915/intel_pm.c +++ b/drivers/gpu/drm/i915/intel_pm.c @@ -2267,21 +2267,33 @@ static void ironlake_disable_drps(struct drm_device *dev) } -void gen6_set_rps(struct drm_device *dev, u8 val) +static u32 gen6_rps_limits(struct drm_i915_private *dev_priv, u8 val) { - struct drm_i915_private *dev_priv = dev->dev_private; u32 limits; limits = 0; if (val >= dev_priv->max_delay) val = dev_priv->max_delay; - else - limits |= dev_priv->max_delay << 24; - - if (val <= dev_priv->min_delay) + limits |= dev_priv->max_delay << 24; + + /* Only set the down limit when we've reached the lowest level to avoid + * getting more interrupts, otherwise leave this clear. This prevents a + * race in the hw when coming out of rc6: There's a tiny window where + * the hw runs at the minimal clock before selecting the desired + * frequency, if the down threshold expires in that window we will not + * receive a down interrupt. */ + if (val <= dev_priv->min_delay) { val = dev_priv->min_delay; - else limits |= dev_priv->min_delay << 16; + } + + return limits; +} + +void gen6_set_rps(struct drm_device *dev, u8 val) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + u32 limits = gen6_rps_limits(dev_priv, val); if (val == dev_priv->cur_delay) return; @@ -3741,25 +3753,20 @@ void intel_init_clock_gating(struct drm_device *dev) static void gen6_sanitize_pm(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; - u32 limits, delay, old; + u32 limits, current_limits; gen6_gt_force_wake_get(dev_priv); - old = limits = I915_READ(GEN6_RP_INTERRUPT_LIMITS); + current_limits = I915_READ(GEN6_RP_INTERRUPT_LIMITS); /* Make sure we continue to get interrupts * until we hit the minimum or maximum frequencies. */ - limits &= ~(0x3f << 16 | 0x3f << 24); - delay = dev_priv->cur_delay; - if (delay < dev_priv->max_delay) - limits |= (dev_priv->max_delay & 0x3f) << 24; - if (delay > dev_priv->min_delay) - limits |= (dev_priv->min_delay & 0x3f) << 16; - - if (old != limits) { + limits = gen6_rps_limits(dev_priv, dev_priv->cur_delay); + + if (current_limits != limits) { /* Note that the known failure case is to read back 0. */ DRM_DEBUG_DRIVER("Power management discrepancy: GEN6_RP_INTERRUPT_LIMITS " - "expected %08x, was %08x\n", limits, old); + "expected %08x, was %08x\n", limits, current_limits); I915_WRITE(GEN6_RP_INTERRUPT_LIMITS, limits); } -- cgit v0.10.2 From acbe9475505de68540fab8653131d41d424c4fa3 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Thu, 26 Jul 2012 11:50:05 +0200 Subject: drm/i915: rip out sanitize_pm again We believe to have squashed all issues around the gen6+ rps interrupt generation and why the gpu sometimes got stuck. With that cleared up, there's no user left for the sanitize_pm infrastructure, so let's just rip it out. Note that 'intel_reg_write 0xa014 0x13070000' is the w/a if we find ourselves stuck again. Acked-by: Chris Wilson Signed-Off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 0dc89a4..0b2eb17 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -249,7 +249,6 @@ struct drm_i915_display_funcs { void (*update_wm)(struct drm_device *dev); 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, diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index b463829..17020cd 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -5929,13 +5929,11 @@ static void intel_decrease_pllclock(struct drm_crtc *crtc) void intel_mark_busy(struct drm_device *dev) { - intel_sanitize_pm(dev); i915_update_gfx_val(dev->dev_private); } void intel_mark_idle(struct drm_device *dev) { - intel_sanitize_pm(dev); } void intel_mark_fb_busy(struct drm_i915_gem_object *obj) diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index 8c7f483..13f0467 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -390,8 +390,6 @@ extern int intel_plane_init(struct drm_device *dev, enum pipe pipe); extern void intel_flush_display_plane(struct drm_i915_private *dev_priv, enum plane plane); -void intel_sanitize_pm(struct drm_device *dev); - /* intel_panel.c */ extern void intel_fixed_panel_mode(struct drm_display_mode *fixed_mode, struct drm_display_mode *adjusted_mode); diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c index e8727da..d0ce894 100644 --- a/drivers/gpu/drm/i915/intel_pm.c +++ b/drivers/gpu/drm/i915/intel_pm.c @@ -2267,6 +2267,11 @@ static void ironlake_disable_drps(struct drm_device *dev) } +/* There's a funny hw issue where the hw returns all 0 when reading from + * GEN6_RP_INTERRUPT_LIMITS. Hence we always need to compute the desired value + * ourselves, instead of doing a rmw cycle (which might result in us clearing + * all limits and the gpu stuck at whatever frequency it is at atm). + */ static u32 gen6_rps_limits(struct drm_i915_private *dev_priv, u8 val) { u32 limits; @@ -3750,37 +3755,6 @@ void intel_init_clock_gating(struct drm_device *dev) dev_priv->display.init_pch_clock_gating(dev); } -static void gen6_sanitize_pm(struct drm_device *dev) -{ - struct drm_i915_private *dev_priv = dev->dev_private; - u32 limits, current_limits; - - gen6_gt_force_wake_get(dev_priv); - - current_limits = I915_READ(GEN6_RP_INTERRUPT_LIMITS); - /* Make sure we continue to get interrupts - * until we hit the minimum or maximum frequencies. - */ - limits = gen6_rps_limits(dev_priv, dev_priv->cur_delay); - - if (current_limits != limits) { - /* Note that the known failure case is to read back 0. */ - DRM_DEBUG_DRIVER("Power management discrepancy: GEN6_RP_INTERRUPT_LIMITS " - "expected %08x, was %08x\n", limits, current_limits); - I915_WRITE(GEN6_RP_INTERRUPT_LIMITS, limits); - } - - gen6_gt_force_wake_put(dev_priv); -} - -void intel_sanitize_pm(struct drm_device *dev) -{ - struct drm_i915_private *dev_priv = dev->dev_private; - - if (dev_priv->display.sanitize_pm) - 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. */ @@ -3866,7 +3840,6 @@ void intel_init_pm(struct drm_device *dev) dev_priv->display.update_wm = NULL; } dev_priv->display.init_clock_gating = gen6_init_clock_gating; - dev_priv->display.sanitize_pm = gen6_sanitize_pm; } else if (IS_IVYBRIDGE(dev)) { /* FIXME: detect B0+ stepping and use auto training */ if (SNB_READ_WM0_LATENCY()) { @@ -3878,7 +3851,6 @@ void intel_init_pm(struct drm_device *dev) 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 if (IS_HASWELL(dev)) { if (SNB_READ_WM0_LATENCY()) { dev_priv->display.update_wm = sandybridge_update_wm; @@ -3890,7 +3862,6 @@ void intel_init_pm(struct drm_device *dev) dev_priv->display.update_wm = NULL; } dev_priv->display.init_clock_gating = haswell_init_clock_gating; - dev_priv->display.sanitize_pm = gen6_sanitize_pm; } else dev_priv->display.update_wm = NULL; } else if (IS_VALLEYVIEW(dev)) { -- cgit v0.10.2 From ab3951eb74e7c33a2f5b7b64d72e82f1eea61571 Mon Sep 17 00:00:00 2001 From: Eugeni Dodonov Date: Mon, 18 Jun 2012 19:03:38 -0300 Subject: drm/i915: prevent possible pin leak on error path We should not hit this under any sane conditions, but still, this does not looks right. CC: Chris Wilson CC: Daniel Vetter CC: stable@vger.kernel.org Reported-by: Herton Ronaldo Krzesinski Reviewed-by: Chris Wlison Signed-off-by: Eugeni Dodonov Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 17020cd..2e1f28f 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -6317,7 +6317,7 @@ static int intel_gen7_queue_flip(struct drm_device *dev, default: WARN_ONCE(1, "unknown plane in flip command\n"); ret = -ENODEV; - goto err; + goto err_unpin; } ret = intel_ring_begin(ring, 4); -- cgit v0.10.2 From 6c6cf5aa9c583478b19e23149feaa92d01fb8c2d Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Fri, 20 Jul 2012 18:02:28 +0100 Subject: drm/i915: Only apply the SNB pipe control w/a to gen6 The requirements for the sync flush to be emitted prior to the render cache flush is only true for SandyBridge. On IvyBridge and friends we can just emit the flushes with an inline CS stall. Signed-off-by: Chris Wilson Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/intel_ringbuffer.c b/drivers/gpu/drm/i915/intel_ringbuffer.c index c58f1b9..8733da5 100644 --- a/drivers/gpu/drm/i915/intel_ringbuffer.c +++ b/drivers/gpu/drm/i915/intel_ringbuffer.c @@ -214,15 +214,8 @@ gen6_render_ring_flush(struct intel_ring_buffer *ring, u32 invalidate_domains, u32 flush_domains) { u32 flags = 0; - struct pipe_control *pc = ring->private; - u32 scratch_addr = pc->gtt_offset + 128; int ret; - /* Force SNB workarounds for PIPE_CONTROL flushes */ - ret = intel_emit_post_sync_nonzero_flush(ring); - if (ret) - return ret; - /* Just flush everything. Experiments have shown that reducing the * number of bits based on the write domains has little performance * impact. @@ -242,21 +235,33 @@ gen6_render_ring_flush(struct intel_ring_buffer *ring, if (flush_domains) flags |= PIPE_CONTROL_CS_STALL; - ret = intel_ring_begin(ring, 6); + ret = intel_ring_begin(ring, 4); if (ret) return ret; - intel_ring_emit(ring, GFX_OP_PIPE_CONTROL(5)); + intel_ring_emit(ring, GFX_OP_PIPE_CONTROL(4)); intel_ring_emit(ring, flags); - intel_ring_emit(ring, scratch_addr | PIPE_CONTROL_GLOBAL_GTT); - intel_ring_emit(ring, 0); /* lower dword */ - intel_ring_emit(ring, 0); /* uppwer dword */ - intel_ring_emit(ring, MI_NOOP); + intel_ring_emit(ring, 0); + intel_ring_emit(ring, 0); intel_ring_advance(ring); return 0; } +static int +gen6_render_ring_flush__wa(struct intel_ring_buffer *ring, + u32 invalidate_domains, u32 flush_domains) +{ + int ret; + + /* Force SNB workarounds for PIPE_CONTROL flushes */ + ret = intel_emit_post_sync_nonzero_flush(ring); + if (ret) + return ret; + + return gen6_render_ring_flush(ring, invalidate_domains, flush_domains); +} + static void ring_write_tail(struct intel_ring_buffer *ring, u32 value) { @@ -1371,6 +1376,8 @@ int intel_init_render_ring_buffer(struct drm_device *dev) if (INTEL_INFO(dev)->gen >= 6) { ring->add_request = gen6_add_request; ring->flush = gen6_render_ring_flush; + if (INTEL_INFO(dev)->gen == 6) + ring->flush = gen6_render_ring_flush__wa; ring->irq_get = gen6_ring_get_irq; ring->irq_put = gen6_ring_put_irq; ring->irq_enable_mask = GT_USER_INTERRUPT; -- cgit v0.10.2 From 2fedbff948cf5bc1e9977b7bc885aae45bd32f74 Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Wed, 8 Aug 2012 10:23:22 +0100 Subject: drm/i915: Add I915_GEM_PARAM_HAS_SEMAPHORES Userspace tries to estimate the cost of ring switching based on whether the GPU and GEM supports semaphores. (If we have multiple rings and no semaphores, userspace assumes that the cost of switching rings between batches is exorbitant and will endeavour to keep the next batch on the active ring - as a coarse approximation to tracking both destination and source surfaces.) Currently userspace has to guess whether semaphores exist based on the chipset generation and the module parameter, i915.semaphores. This is a crude and inaccurate guess as the defaults internally depend upon other chipset features being enabled or disabled, nor does it extend well into the future. By exporting a HAS_SEMAPHORES parameter, we can easily query the driver and obtain an accurate answer. Signed-off-by: Chris Wilson Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/i915_dma.c b/drivers/gpu/drm/i915/i915_dma.c index 71672ce..d57ea16 100644 --- a/drivers/gpu/drm/i915/i915_dma.c +++ b/drivers/gpu/drm/i915/i915_dma.c @@ -1009,6 +1009,9 @@ static int i915_getparam(struct drm_device *dev, void *data, case I915_PARAM_HAS_WAIT_TIMEOUT: value = 1; break; + case I915_PARAM_HAS_SEMAPHORES: + value = i915_semaphore_is_enabled(dev); + break; default: DRM_DEBUG_DRIVER("Unknown parameter %d\n", param->param); diff --git a/include/drm/i915_drm.h b/include/drm/i915_drm.h index 772b0d6..d8a79bf 100644 --- a/include/drm/i915_drm.h +++ b/include/drm/i915_drm.h @@ -311,6 +311,7 @@ typedef struct drm_i915_irq_wait { #define I915_PARAM_HAS_LLC 17 #define I915_PARAM_HAS_ALIASING_PPGTT 18 #define I915_PARAM_HAS_WAIT_TIMEOUT 19 +#define I915_PARAM_HAS_SEMAPHORES 20 typedef struct drm_i915_getparam { int param; -- cgit v0.10.2 From 65bccb5c708bd9f00d24f041f4f7c45130359448 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Wed, 8 Aug 2012 17:42:52 +0200 Subject: drm/i915: fixup desired rps frequency computation In commit commit 20b46e59dd102665ce7168baa215e5b1ee66b69b Author: Daniel Vetter Date: Thu Jul 26 11:16:14 2012 +0200 drm/i915: Only set the down rps limit when at the loweset frequency The computation for the new desired frequency was extracted, but since the desired frequency was passed-by value, the adjustments didn't propgate back. Fix this. Reviewed-by: Chris Wilson Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c index d0ce894..5050bb8 100644 --- a/drivers/gpu/drm/i915/intel_pm.c +++ b/drivers/gpu/drm/i915/intel_pm.c @@ -2272,13 +2272,13 @@ static void ironlake_disable_drps(struct drm_device *dev) * ourselves, instead of doing a rmw cycle (which might result in us clearing * all limits and the gpu stuck at whatever frequency it is at atm). */ -static u32 gen6_rps_limits(struct drm_i915_private *dev_priv, u8 val) +static u32 gen6_rps_limits(struct drm_i915_private *dev_priv, u8 *val) { u32 limits; limits = 0; - if (val >= dev_priv->max_delay) - val = dev_priv->max_delay; + if (*val >= dev_priv->max_delay) + *val = dev_priv->max_delay; limits |= dev_priv->max_delay << 24; /* Only set the down limit when we've reached the lowest level to avoid @@ -2287,8 +2287,8 @@ static u32 gen6_rps_limits(struct drm_i915_private *dev_priv, u8 val) * the hw runs at the minimal clock before selecting the desired * frequency, if the down threshold expires in that window we will not * receive a down interrupt. */ - if (val <= dev_priv->min_delay) { - val = dev_priv->min_delay; + if (*val <= dev_priv->min_delay) { + *val = dev_priv->min_delay; limits |= dev_priv->min_delay << 16; } @@ -2298,7 +2298,7 @@ static u32 gen6_rps_limits(struct drm_i915_private *dev_priv, u8 val) void gen6_set_rps(struct drm_device *dev, u8 val) { struct drm_i915_private *dev_priv = dev->dev_private; - u32 limits = gen6_rps_limits(dev_priv, val); + u32 limits = gen6_rps_limits(dev_priv, &val); if (val == dev_priv->cur_delay) return; -- cgit v0.10.2 From c96ea64ebbbe911becccb20dea1609017caad3c9 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Wed, 8 Aug 2012 22:01:51 +0200 Subject: drm/i915: dump the device info Handy for lazy people like me, or when people forget to add the output of lspci -nn. v2: Chris Wilson noticed that we have this duplicated already in the i915_capabilites debugfs file. But there \n as separator looks better, which would be a bit verbose in dmesg. Abuse the preprocessor to extract this all. Reviewed-by: Chris Wilson Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/i915_debugfs.c b/drivers/gpu/drm/i915/i915_debugfs.c index 1312b79..2e07cdd 100644 --- a/drivers/gpu/drm/i915/i915_debugfs.c +++ b/drivers/gpu/drm/i915/i915_debugfs.c @@ -61,28 +61,11 @@ static int i915_capabilities(struct seq_file *m, void *data) seq_printf(m, "gen: %d\n", info->gen); seq_printf(m, "pch: %d\n", INTEL_PCH_TYPE(dev)); -#define B(x) seq_printf(m, #x ": %s\n", yesno(info->x)) - B(is_mobile); - B(is_i85x); - B(is_i915g); - B(is_i945gm); - B(is_g33); - B(need_gfx_hws); - B(is_g4x); - B(is_pineview); - B(is_broadwater); - B(is_crestline); - B(has_fbc); - B(has_pipe_cxsr); - B(has_hotplug); - B(cursor_needs_physical); - B(has_overlay); - B(overlay_needs_physical); - B(supports_tv); - B(has_bsd_ring); - B(has_blt_ring); - B(has_llc); -#undef B +#define DEV_INFO_FLAG(x) seq_printf(m, #x ": %s\n", yesno(info->x)) +#define DEV_INFO_SEP ; + DEV_INFO_FLAGS; +#undef DEV_INFO_FLAG +#undef DEV_INFO_SEP return 0; } diff --git a/drivers/gpu/drm/i915/i915_dma.c b/drivers/gpu/drm/i915/i915_dma.c index d57ea16..a7a213c 100644 --- a/drivers/gpu/drm/i915/i915_dma.c +++ b/drivers/gpu/drm/i915/i915_dma.c @@ -1428,6 +1428,21 @@ static void i915_kick_out_firmware_fb(struct drm_i915_private *dev_priv) kfree(ap); } +static void i915_dump_device_info(struct drm_i915_private *dev_priv) +{ + const struct intel_device_info *info = dev_priv->info; + +#define DEV_INFO_FLAG(name) info->name ? #name "," : "" +#define DEV_INFO_SEP , + DRM_DEBUG_DRIVER("i915 device info: gen=%i, pciid=0x%04x flags=" + "%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s", + info->gen, + dev_priv->dev->pdev->device, + DEV_INFO_FLAGS); +#undef DEV_INFO_FLAG +#undef DEV_INFO_SEP +} + /** * i915_driver_load - setup chip and create an initial config * @dev: DRM device @@ -1452,7 +1467,6 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags) if (info->gen >= 6 && !drm_core_check_feature(dev, DRIVER_MODESET)) return -ENODEV; - /* i915 has 4 more counters */ dev->counters += 4; dev->types[6] = _DRM_STAT_IRQ; @@ -1468,6 +1482,8 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags) dev_priv->dev = dev; dev_priv->info = info; + i915_dump_device_info(dev_priv); + if (i915_get_bridge_dev(dev)) { ret = -EIO; goto free_priv; diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 0b2eb17..26a2cf6 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -279,6 +279,32 @@ struct drm_i915_gt_funcs { void (*force_wake_put)(struct drm_i915_private *dev_priv); }; +#define DEV_INFO_FLAGS \ + DEV_INFO_FLAG(is_mobile) DEV_INFO_SEP \ + DEV_INFO_FLAG(is_i85x) DEV_INFO_SEP \ + DEV_INFO_FLAG(is_i915g) DEV_INFO_SEP \ + DEV_INFO_FLAG(is_i945gm) DEV_INFO_SEP \ + DEV_INFO_FLAG(is_g33) DEV_INFO_SEP \ + DEV_INFO_FLAG(need_gfx_hws) DEV_INFO_SEP \ + DEV_INFO_FLAG(is_g4x) DEV_INFO_SEP \ + DEV_INFO_FLAG(is_pineview) DEV_INFO_SEP \ + DEV_INFO_FLAG(is_broadwater) DEV_INFO_SEP \ + DEV_INFO_FLAG(is_crestline) DEV_INFO_SEP \ + DEV_INFO_FLAG(is_ivybridge) DEV_INFO_SEP \ + DEV_INFO_FLAG(is_valleyview) DEV_INFO_SEP \ + DEV_INFO_FLAG(is_haswell) DEV_INFO_SEP \ + DEV_INFO_FLAG(has_force_wake) DEV_INFO_SEP \ + DEV_INFO_FLAG(has_fbc) DEV_INFO_SEP \ + DEV_INFO_FLAG(has_pipe_cxsr) DEV_INFO_SEP \ + DEV_INFO_FLAG(has_hotplug) DEV_INFO_SEP \ + DEV_INFO_FLAG(cursor_needs_physical) DEV_INFO_SEP \ + DEV_INFO_FLAG(has_overlay) DEV_INFO_SEP \ + DEV_INFO_FLAG(overlay_needs_physical) DEV_INFO_SEP \ + DEV_INFO_FLAG(supports_tv) DEV_INFO_SEP \ + DEV_INFO_FLAG(has_bsd_ring) DEV_INFO_SEP \ + DEV_INFO_FLAG(has_blt_ring) DEV_INFO_SEP \ + DEV_INFO_FLAG(has_llc) + struct intel_device_info { u8 gen; u8 is_mobile:1; -- cgit v0.10.2 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 diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h index 1310caa..97f00fb 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 32604ac..0d7acd7 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 v0.10.2 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 diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h index 97f00fb..896b279 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 0d7acd7..1fbd67c 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 v0.10.2 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 diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h index 896b279..f3fafb8 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 1fbd67c..8b38359 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 v0.10.2 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 diff --git a/drivers/gpu/drm/i915/intel_ddi.c b/drivers/gpu/drm/i915/intel_ddi.c index 8b38359..ff03a3a 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 v0.10.2 From 5e49cea6437566700294fb559ac0c34e053ad8de Mon Sep 17 00:00:00 2001 From: Paulo Zanoni Date: Wed, 8 Aug 2012 14:15:31 -0300 Subject: drm/i915: reindent Haswell register definitions It's the only part of the i915_reg.h file that looks totally wrongly indented, so I assume my editor config is the correct one. Signed-off-by: Paulo Zanoni Reviewed-by: Jani Nikula Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h index f3fafb8..af41d03 100644 --- a/drivers/gpu/drm/i915/i915_reg.h +++ b/drivers/gpu/drm/i915/i915_reg.h @@ -4278,198 +4278,184 @@ #define AUD_CONFIG_DISABLE_NCTS (1 << 3) /* HSW Power Wells */ -#define HSW_PWR_WELL_CTL1 0x45400 /* BIOS */ -#define HSW_PWR_WELL_CTL2 0x45404 /* Driver */ -#define HSW_PWR_WELL_CTL3 0x45408 /* KVMR */ -#define HSW_PWR_WELL_CTL4 0x4540C /* Debug */ -#define HSW_PWR_WELL_ENABLE (1<<31) -#define HSW_PWR_WELL_STATE (1<<30) -#define HSW_PWR_WELL_CTL5 0x45410 +#define HSW_PWR_WELL_CTL1 0x45400 /* BIOS */ +#define HSW_PWR_WELL_CTL2 0x45404 /* Driver */ +#define HSW_PWR_WELL_CTL3 0x45408 /* KVMR */ +#define HSW_PWR_WELL_CTL4 0x4540C /* Debug */ +#define HSW_PWR_WELL_ENABLE (1<<31) +#define HSW_PWR_WELL_STATE (1<<30) +#define HSW_PWR_WELL_CTL5 0x45410 #define HSW_PWR_WELL_ENABLE_SINGLE_STEP (1<<31) #define HSW_PWR_WELL_PWR_GATE_OVERRIDE (1<<20) -#define HSW_PWR_WELL_FORCE_ON (1<<19) -#define HSW_PWR_WELL_CTL6 0x45414 +#define HSW_PWR_WELL_FORCE_ON (1<<19) +#define HSW_PWR_WELL_CTL6 0x45414 /* Per-pipe DDI Function Control */ -#define PIPE_DDI_FUNC_CTL_A 0x60400 -#define PIPE_DDI_FUNC_CTL_B 0x61400 -#define PIPE_DDI_FUNC_CTL_C 0x62400 +#define PIPE_DDI_FUNC_CTL_A 0x60400 +#define PIPE_DDI_FUNC_CTL_B 0x61400 +#define PIPE_DDI_FUNC_CTL_C 0x62400 #define PIPE_DDI_FUNC_CTL_EDP 0x6F400 -#define DDI_FUNC_CTL(pipe) _PIPE(pipe, \ - PIPE_DDI_FUNC_CTL_A, \ - PIPE_DDI_FUNC_CTL_B) +#define DDI_FUNC_CTL(pipe) _PIPE(pipe, PIPE_DDI_FUNC_CTL_A, \ + PIPE_DDI_FUNC_CTL_B) #define PIPE_DDI_FUNC_ENABLE (1<<31) /* 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_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) #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) -#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) -#define PIPE_DDI_PORT_WIDTH_X4 (3<<1) +#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) +#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) +#define PIPE_DDI_PORT_WIDTH_X4 (3<<1) /* DisplayPort Transport Control */ #define DP_TP_CTL_A 0x64040 #define DP_TP_CTL_B 0x64140 -#define DP_TP_CTL(port) _PORT(port, \ - DP_TP_CTL_A, \ - DP_TP_CTL_B) -#define DP_TP_CTL_ENABLE (1<<31) -#define DP_TP_CTL_MODE_SST (0<<27) -#define DP_TP_CTL_MODE_MST (1<<27) +#define DP_TP_CTL(port) _PORT(port, DP_TP_CTL_A, DP_TP_CTL_B) +#define DP_TP_CTL_ENABLE (1<<31) +#define DP_TP_CTL_MODE_SST (0<<27) +#define DP_TP_CTL_MODE_MST (1<<27) #define DP_TP_CTL_ENHANCED_FRAME_ENABLE (1<<18) -#define DP_TP_CTL_FDI_AUTOTRAIN (1<<15) +#define DP_TP_CTL_FDI_AUTOTRAIN (1<<15) #define DP_TP_CTL_LINK_TRAIN_MASK (7<<8) #define DP_TP_CTL_LINK_TRAIN_PAT1 (0<<8) #define DP_TP_CTL_LINK_TRAIN_PAT2 (1<<8) -#define DP_TP_CTL_LINK_TRAIN_NORMAL (3<<8) +#define DP_TP_CTL_LINK_TRAIN_NORMAL (3<<8) /* DisplayPort Transport Status */ #define DP_TP_STATUS_A 0x64044 #define DP_TP_STATUS_B 0x64144 -#define DP_TP_STATUS(port) _PORT(port, \ - DP_TP_STATUS_A, \ - DP_TP_STATUS_B) +#define DP_TP_STATUS(port) _PORT(port, DP_TP_STATUS_A, DP_TP_STATUS_B) #define DP_TP_STATUS_AUTOTRAIN_DONE (1<<12) /* DDI Buffer Control */ #define DDI_BUF_CTL_A 0x64000 #define DDI_BUF_CTL_B 0x64100 -#define DDI_BUF_CTL(port) _PORT(port, \ - DDI_BUF_CTL_A, \ - DDI_BUF_CTL_B) -#define DDI_BUF_CTL_ENABLE (1<<31) +#define DDI_BUF_CTL(port) _PORT(port, DDI_BUF_CTL_A, DDI_BUF_CTL_B) +#define DDI_BUF_CTL_ENABLE (1<<31) #define DDI_BUF_EMP_400MV_0DB_HSW (0<<24) /* Sel0 */ -#define DDI_BUF_EMP_400MV_3_5DB_HSW (1<<24) /* Sel1 */ +#define DDI_BUF_EMP_400MV_3_5DB_HSW (1<<24) /* Sel1 */ #define DDI_BUF_EMP_400MV_6DB_HSW (2<<24) /* Sel2 */ -#define DDI_BUF_EMP_400MV_9_5DB_HSW (3<<24) /* Sel3 */ +#define DDI_BUF_EMP_400MV_9_5DB_HSW (3<<24) /* Sel3 */ #define DDI_BUF_EMP_600MV_0DB_HSW (4<<24) /* Sel4 */ -#define DDI_BUF_EMP_600MV_3_5DB_HSW (5<<24) /* Sel5 */ +#define DDI_BUF_EMP_600MV_3_5DB_HSW (5<<24) /* Sel5 */ #define DDI_BUF_EMP_600MV_6DB_HSW (6<<24) /* Sel6 */ #define DDI_BUF_EMP_800MV_0DB_HSW (7<<24) /* Sel7 */ -#define DDI_BUF_EMP_800MV_3_5DB_HSW (8<<24) /* Sel8 */ -#define DDI_BUF_EMP_MASK (0xf<<24) -#define DDI_BUF_IS_IDLE (1<<7) -#define DDI_PORT_WIDTH_X1 (0<<1) -#define DDI_PORT_WIDTH_X2 (1<<1) -#define DDI_PORT_WIDTH_X4 (3<<1) +#define DDI_BUF_EMP_800MV_3_5DB_HSW (8<<24) /* Sel8 */ +#define DDI_BUF_EMP_MASK (0xf<<24) +#define DDI_BUF_IS_IDLE (1<<7) +#define DDI_PORT_WIDTH_X1 (0<<1) +#define DDI_PORT_WIDTH_X2 (1<<1) +#define DDI_PORT_WIDTH_X4 (3<<1) #define DDI_INIT_DISPLAY_DETECTED (1<<0) /* DDI Buffer Translations */ #define DDI_BUF_TRANS_A 0x64E00 #define DDI_BUF_TRANS_B 0x64E60 -#define DDI_BUF_TRANS(port) _PORT(port, \ - DDI_BUF_TRANS_A, \ - DDI_BUF_TRANS_B) +#define DDI_BUF_TRANS(port) _PORT(port, DDI_BUF_TRANS_A, DDI_BUF_TRANS_B) /* Sideband Interface (SBI) is programmed indirectly, via * SBI_ADDR, which contains the register offset; and SBI_DATA, * which contains the payload */ -#define SBI_ADDR 0xC6000 -#define SBI_DATA 0xC6004 +#define SBI_ADDR 0xC6000 +#define SBI_DATA 0xC6004 #define SBI_CTL_STAT 0xC6008 #define SBI_CTL_OP_CRRD (0x6<<8) #define SBI_CTL_OP_CRWR (0x7<<8) #define SBI_RESPONSE_FAIL (0x1<<1) -#define SBI_RESPONSE_SUCCESS (0x0<<1) -#define SBI_BUSY (0x1<<0) -#define SBI_READY (0x0<<0) +#define SBI_RESPONSE_SUCCESS (0x0<<1) +#define SBI_BUSY (0x1<<0) +#define SBI_READY (0x0<<0) /* SBI offsets */ -#define SBI_SSCDIVINTPHASE6 0x0600 +#define SBI_SSCDIVINTPHASE6 0x0600 #define SBI_SSCDIVINTPHASE_DIVSEL_MASK ((0x7f)<<1) #define SBI_SSCDIVINTPHASE_DIVSEL(x) ((x)<<1) #define SBI_SSCDIVINTPHASE_INCVAL_MASK ((0x7f)<<8) #define SBI_SSCDIVINTPHASE_INCVAL(x) ((x)<<8) -#define SBI_SSCDIVINTPHASE_DIR(x) ((x)<<15) +#define SBI_SSCDIVINTPHASE_DIR(x) ((x)<<15) #define SBI_SSCDIVINTPHASE_PROPAGATE (1<<0) -#define SBI_SSCCTL 0x020c +#define SBI_SSCCTL 0x020c #define SBI_SSCCTL6 0x060C -#define SBI_SSCCTL_DISABLE (1<<0) +#define SBI_SSCCTL_DISABLE (1<<0) #define SBI_SSCAUXDIV6 0x0610 #define SBI_SSCAUXDIV_FINALDIV2SEL(x) ((x)<<4) -#define SBI_DBUFF0 0x2a00 +#define SBI_DBUFF0 0x2a00 /* LPT PIXCLK_GATE */ -#define PIXCLK_GATE 0xC6020 +#define PIXCLK_GATE 0xC6020 #define PIXCLK_GATE_UNGATE 1<<0 #define PIXCLK_GATE_GATE 0<<0 /* SPLL */ -#define SPLL_CTL 0x46020 +#define SPLL_CTL 0x46020 #define SPLL_PLL_ENABLE (1<<31) #define SPLL_PLL_SCC (1<<28) #define SPLL_PLL_NON_SCC (2<<28) -#define SPLL_PLL_FREQ_810MHz (0<<26) -#define SPLL_PLL_FREQ_1350MHz (1<<26) +#define SPLL_PLL_FREQ_810MHz (0<<26) +#define SPLL_PLL_FREQ_1350MHz (1<<26) /* WRPLL */ -#define WRPLL_CTL1 0x46040 -#define WRPLL_CTL2 0x46060 -#define WRPLL_PLL_ENABLE (1<<31) -#define WRPLL_PLL_SELECT_SSC (0x01<<28) -#define WRPLL_PLL_SELECT_NON_SCC (0x02<<28) +#define WRPLL_CTL1 0x46040 +#define WRPLL_CTL2 0x46060 +#define WRPLL_PLL_ENABLE (1<<31) +#define WRPLL_PLL_SELECT_SSC (0x01<<28) +#define WRPLL_PLL_SELECT_NON_SCC (0x02<<28) #define WRPLL_PLL_SELECT_LCPLL_2700 (0x03<<28) /* WRPLL divider programming */ -#define WRPLL_DIVIDER_REFERENCE(x) ((x)<<0) -#define WRPLL_DIVIDER_POST(x) ((x)<<8) -#define WRPLL_DIVIDER_FEEDBACK(x) ((x)<<16) +#define WRPLL_DIVIDER_REFERENCE(x) ((x)<<0) +#define WRPLL_DIVIDER_POST(x) ((x)<<8) +#define WRPLL_DIVIDER_FEEDBACK(x) ((x)<<16) /* Port clock selection */ #define PORT_CLK_SEL_A 0x46100 #define PORT_CLK_SEL_B 0x46104 -#define PORT_CLK_SEL(port) _PORT(port, \ - PORT_CLK_SEL_A, \ - PORT_CLK_SEL_B) +#define PORT_CLK_SEL(port) _PORT(port, PORT_CLK_SEL_A, PORT_CLK_SEL_B) #define PORT_CLK_SEL_LCPLL_2700 (0<<29) #define PORT_CLK_SEL_LCPLL_1350 (1<<29) #define PORT_CLK_SEL_LCPLL_810 (2<<29) -#define PORT_CLK_SEL_SPLL (3<<29) +#define PORT_CLK_SEL_SPLL (3<<29) #define PORT_CLK_SEL_WRPLL1 (4<<29) #define PORT_CLK_SEL_WRPLL2 (5<<29) /* Pipe clock selection */ #define PIPE_CLK_SEL_A 0x46140 #define PIPE_CLK_SEL_B 0x46144 -#define PIPE_CLK_SEL(pipe) _PIPE(pipe, \ - PIPE_CLK_SEL_A, \ - PIPE_CLK_SEL_B) +#define PIPE_CLK_SEL(pipe) _PIPE(pipe, PIPE_CLK_SEL_A, PIPE_CLK_SEL_B) /* For each pipe, we need to select the corresponding port clock */ -#define PIPE_CLK_SEL_DISABLED (0x0<<29) -#define PIPE_CLK_SEL_PORT(x) ((x+1)<<29) +#define PIPE_CLK_SEL_DISABLED (0x0<<29) +#define PIPE_CLK_SEL_PORT(x) ((x+1)<<29) /* LCPLL Control */ -#define LCPLL_CTL 0x130040 +#define LCPLL_CTL 0x130040 #define LCPLL_PLL_DISABLE (1<<31) #define LCPLL_PLL_LOCK (1<<30) -#define LCPLL_CD_CLOCK_DISABLE (1<<25) +#define LCPLL_CD_CLOCK_DISABLE (1<<25) #define LCPLL_CD2X_CLOCK_DISABLE (1<<23) /* Pipe WM_LINETIME - watermark line time */ #define PIPE_WM_LINETIME_A 0x45270 #define PIPE_WM_LINETIME_B 0x45274 -#define PIPE_WM_LINETIME(pipe) _PIPE(pipe, \ - PIPE_WM_LINETIME_A, \ - PIPE_WM_LINETIME_B) -#define PIPE_WM_LINETIME_MASK (0x1ff) -#define PIPE_WM_LINETIME_TIME(x) ((x)) +#define PIPE_WM_LINETIME(pipe) _PIPE(pipe, PIPE_WM_LINETIME_A, \ + PIPE_WM_LINETIME_B) +#define PIPE_WM_LINETIME_MASK (0x1ff) +#define PIPE_WM_LINETIME_TIME(x) ((x)) #define PIPE_WM_LINETIME_IPS_LINETIME_MASK (0x1ff<<16) -#define PIPE_WM_LINETIME_IPS_LINETIME(x) ((x)<<16) +#define PIPE_WM_LINETIME_IPS_LINETIME(x) ((x)<<16) /* SFUSE_STRAP */ -#define SFUSE_STRAP 0xc2014 +#define SFUSE_STRAP 0xc2014 #define SFUSE_STRAP_DDIB_DETECTED (1<<2) #define SFUSE_STRAP_DDIC_DETECTED (1<<1) #define SFUSE_STRAP_DDID_DETECTED (1<<0) -- cgit v0.10.2 From 745ca3be810b37fba8dd113a028ea71d42e1d892 Mon Sep 17 00:00:00 2001 From: Paulo Zanoni Date: Wed, 8 Aug 2012 14:15:32 -0300 Subject: drm/i915: add parentheses around PIXCLK_GATE definitions By looking at the current way we're using these definitions I don't think this commit will fix any bug, but programmers from the future are evil and will certainly find ways to combine macro expansion with operator precedence to introduce bugs that are hard to find. Signed-off-by: Paulo Zanoni Reviewed-by: Jani Nikula Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h index af41d03..321ae72 100644 --- a/drivers/gpu/drm/i915/i915_reg.h +++ b/drivers/gpu/drm/i915/i915_reg.h @@ -4395,8 +4395,8 @@ /* LPT PIXCLK_GATE */ #define PIXCLK_GATE 0xC6020 -#define PIXCLK_GATE_UNGATE 1<<0 -#define PIXCLK_GATE_GATE 0<<0 +#define PIXCLK_GATE_UNGATE (1<<0) +#define PIXCLK_GATE_GATE (0<<0) /* SPLL */ #define SPLL_CTL 0x46020 -- cgit v0.10.2 From 02d719562ef40483648b2cc46899d4a2ff5953bb Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Thu, 9 Aug 2012 16:44:54 +0200 Subject: drm/i915: properly guard ilk ips state The update_gfx_val function called from mark_busy wasn't taking the mchdev_lock, as it should have. Also sprinkle a few spinlock asserts over the code to document things better. Things are still rather confusing, especially since a few variables in dev_priv are used by both the gen6+ rps code and the ilk ips code. But protected by totally different locks. Follow-on patches will clean that up. v2: Don't add a deadlock ... hence split up update_gfx_val into a wrapper that grabs the lock and an internal __ variant for callsites within intel_pm.c that already have taken the lock. v3: Mark the internal helper as static, noticed by Ben Widawsky. v4: Damien Lespiau had questions about the safety of the ips setup sequence, explain in a comment why it works. Reviewed-by: Damien Lespiau Reviewed-by: Chris Wilson Signed-Off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c index 5050bb8..ec3fe92 100644 --- a/drivers/gpu/drm/i915/intel_pm.c +++ b/drivers/gpu/drm/i915/intel_pm.c @@ -2710,6 +2710,21 @@ static const struct cparams { { 0, 800, 231, 23784 }, }; +/** + * Lock protecting IPS related data structures + * - i915_mch_dev + * - dev_priv->max_delay + * - dev_priv->min_delay + * - dev_priv->fmax + * - dev_priv->gpu_busy + * - dev_priv->gfx_power + */ +static DEFINE_SPINLOCK(mchdev_lock); + +/* Global for IPS driver to get at the current i915 device. Protected by + * mchdev_lock. */ +static struct drm_i915_private *i915_mch_dev; + unsigned long i915_chipset_val(struct drm_i915_private *dev_priv) { u64 total_count, diff, ret; @@ -2717,6 +2732,8 @@ unsigned long i915_chipset_val(struct drm_i915_private *dev_priv) unsigned long now = jiffies_to_msecs(jiffies), diff1; int i; + assert_spin_locked(&mchdev_lock); + diff1 = now - dev_priv->last_time1; /* Prevent division-by-zero if we are asking too fast. @@ -2918,15 +2935,14 @@ static u16 pvid_to_extvid(struct drm_i915_private *dev_priv, u8 pxvid) return v_table[pxvid].vd; } -void i915_update_gfx_val(struct drm_i915_private *dev_priv) +static void __i915_update_gfx_val(struct drm_i915_private *dev_priv) { struct timespec now, diff1; u64 diff; unsigned long diffms; u32 count; - if (dev_priv->info->gen != 5) - return; + assert_spin_locked(&mchdev_lock); getrawmonotonic(&now); diff1 = timespec_sub(now, dev_priv->last_time2); @@ -2954,11 +2970,25 @@ void i915_update_gfx_val(struct drm_i915_private *dev_priv) dev_priv->gfx_power = diff; } +void i915_update_gfx_val(struct drm_i915_private *dev_priv) +{ + if (dev_priv->info->gen != 5) + return; + + spin_lock(&mchdev_lock); + + __i915_update_gfx_val(dev_priv); + + spin_unlock(&mchdev_lock); +} + unsigned long i915_gfx_val(struct drm_i915_private *dev_priv) { unsigned long t, corr, state1, corr2, state2; u32 pxvid, ext_v; + assert_spin_locked(&mchdev_lock); + pxvid = I915_READ(PXVFREQ_BASE + (dev_priv->cur_delay * 4)); pxvid = (pxvid >> 24) & 0x7f; ext_v = pvid_to_extvid(dev_priv, pxvid); @@ -2984,23 +3014,11 @@ unsigned long i915_gfx_val(struct drm_i915_private *dev_priv) state2 = (corr2 * state1) / 10000; state2 /= 100; /* convert to mW */ - i915_update_gfx_val(dev_priv); + __i915_update_gfx_val(dev_priv); return dev_priv->gfx_power + state2; } -/* Global for IPS driver to get at the current i915 device */ -static struct drm_i915_private *i915_mch_dev; -/* - * Lock protecting IPS related data structures - * - i915_mch_dev - * - dev_priv->max_delay - * - dev_priv->min_delay - * - dev_priv->fmax - * - dev_priv->gpu_busy - */ -static DEFINE_SPINLOCK(mchdev_lock); - /** * i915_read_mch_val - return value for IPS use * @@ -3163,6 +3181,8 @@ ips_ping_for_i915_load(void) void intel_gpu_ips_init(struct drm_i915_private *dev_priv) { + /* We only register the i915 ips part with intel-ips once everything is + * set up, to avoid intel-ips sneaking in and reading bogus values. */ spin_lock(&mchdev_lock); i915_mch_dev = dev_priv; dev_priv->mchdev_lock = &mchdev_lock; -- cgit v0.10.2 From 004777cb22a29e0a95dd1bfce6816a5a06db1e1d Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Thu, 9 Aug 2012 15:07:01 +0200 Subject: drm/i915: fixup up debugfs rps state handling - Take the dev->struct_mutex around access the corresponding state (and adjusting the rps hw state). - Add an assert to gen6_set_rps to ensure we don't forget about this in the future. - Don't set up the min/max_freq files if it doesn't apply to the hw. And do the same for the gen6+ cache sharing file while at it. v2: Move the gen6+ checks into the read/write callbacks. Thanks to the awesome drm midlayer we can't check that when registering the debugfs files, because the driver is not yet fully set up, specifically the ->load callback hasn't run yet. Oh how I despise this disaster ... v3: Also add a WARN_ON(mutex_is_locked) in set_rps to check the locking. v4: Use mutex_lock_interruptible, suggested by Chris Wilson. Reviewed-by: Ben Widawsky (for v2) Reviewed-by: Damien Lespiau Reviewed-by: Chris Wilson Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/i915_debugfs.c b/drivers/gpu/drm/i915/i915_debugfs.c index 2e07cdd..d836bf8 100644 --- a/drivers/gpu/drm/i915/i915_debugfs.c +++ b/drivers/gpu/drm/i915/i915_debugfs.c @@ -1691,10 +1691,18 @@ i915_max_freq_read(struct file *filp, struct drm_device *dev = filp->private_data; drm_i915_private_t *dev_priv = dev->dev_private; char buf[80]; - int len; + int len, ret; + + if (!(IS_GEN6(dev) || IS_GEN7(dev))) + return -ENODEV; + + ret = mutex_lock_interruptible(&dev->struct_mutex); + if (ret) + return ret; len = snprintf(buf, sizeof(buf), "max freq: %d\n", dev_priv->max_delay * 50); + mutex_unlock(&dev->struct_mutex); if (len > sizeof(buf)) len = sizeof(buf); @@ -1711,7 +1719,10 @@ i915_max_freq_write(struct file *filp, struct drm_device *dev = filp->private_data; struct drm_i915_private *dev_priv = dev->dev_private; char buf[20]; - int val = 1; + int val = 1, ret; + + if (!(IS_GEN6(dev) || IS_GEN7(dev))) + return -ENODEV; if (cnt > 0) { if (cnt > sizeof(buf) - 1) @@ -1726,12 +1737,17 @@ i915_max_freq_write(struct file *filp, DRM_DEBUG_DRIVER("Manually setting max freq to %d\n", val); + ret = mutex_lock_interruptible(&dev->struct_mutex); + if (ret) + return ret; + /* * Turbo will still be enabled, but won't go above the set value. */ dev_priv->max_delay = val / 50; gen6_set_rps(dev, val / 50); + mutex_unlock(&dev->struct_mutex); return cnt; } @@ -1751,10 +1767,18 @@ i915_min_freq_read(struct file *filp, char __user *ubuf, size_t max, struct drm_device *dev = filp->private_data; drm_i915_private_t *dev_priv = dev->dev_private; char buf[80]; - int len; + int len, ret; + + if (!(IS_GEN6(dev) || IS_GEN7(dev))) + return -ENODEV; + + ret = mutex_lock_interruptible(&dev->struct_mutex); + if (ret) + return ret; len = snprintf(buf, sizeof(buf), "min freq: %d\n", dev_priv->min_delay * 50); + mutex_unlock(&dev->struct_mutex); if (len > sizeof(buf)) len = sizeof(buf); @@ -1769,7 +1793,10 @@ i915_min_freq_write(struct file *filp, const char __user *ubuf, size_t cnt, struct drm_device *dev = filp->private_data; struct drm_i915_private *dev_priv = dev->dev_private; char buf[20]; - int val = 1; + int val = 1, ret; + + if (!(IS_GEN6(dev) || IS_GEN7(dev))) + return -ENODEV; if (cnt > 0) { if (cnt > sizeof(buf) - 1) @@ -1784,12 +1811,17 @@ i915_min_freq_write(struct file *filp, const char __user *ubuf, size_t cnt, DRM_DEBUG_DRIVER("Manually setting min freq to %d\n", val); + ret = mutex_lock_interruptible(&dev->struct_mutex); + if (ret) + return ret; + /* * Turbo will still be enabled, but won't go below the set value. */ dev_priv->min_delay = val / 50; gen6_set_rps(dev, val / 50); + mutex_unlock(&dev->struct_mutex); return cnt; } @@ -1814,6 +1846,9 @@ i915_cache_sharing_read(struct file *filp, u32 snpcr; int len; + if (!(IS_GEN6(dev) || IS_GEN7(dev))) + return -ENODEV; + mutex_lock(&dev_priv->dev->struct_mutex); snpcr = I915_READ(GEN6_MBCUNIT_SNPCR); mutex_unlock(&dev_priv->dev->struct_mutex); @@ -1840,6 +1875,9 @@ i915_cache_sharing_write(struct file *filp, u32 snpcr; int val = 1; + if (!(IS_GEN6(dev) || IS_GEN7(dev))) + return -ENODEV; + if (cnt > 0) { if (cnt > sizeof(buf) - 1) return -EINVAL; @@ -2044,6 +2082,7 @@ int i915_debugfs_init(struct drm_minor *minor) &i915_cache_sharing_fops); if (ret) return ret; + ret = i915_debugfs_create(minor->debugfs_root, minor, "i915_ring_stop", &i915_ring_stop_fops); diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c index ec3fe92..4fe8d0a 100644 --- a/drivers/gpu/drm/i915/intel_pm.c +++ b/drivers/gpu/drm/i915/intel_pm.c @@ -2300,6 +2300,8 @@ void gen6_set_rps(struct drm_device *dev, u8 val) struct drm_i915_private *dev_priv = dev->dev_private; u32 limits = gen6_rps_limits(dev_priv, &val); + WARN_ON(!mutex_is_locked(&dev->struct_mutex)); + if (val == dev_priv->cur_delay) return; -- cgit v0.10.2 From 22bcfc6a4b2288675a54b761ebcd85b9613ab9a6 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Thu, 9 Aug 2012 15:07:02 +0200 Subject: drm/i915: use mutex_lock_interruptible for debugfs files It's no fun if your shell hangs when the driver has gone on vacation and you want to know why ... Reviewed-by: Damien Lespiau Signed-Off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/i915_debugfs.c b/drivers/gpu/drm/i915/i915_debugfs.c index d836bf8..9c9e199 100644 --- a/drivers/gpu/drm/i915/i915_debugfs.c +++ b/drivers/gpu/drm/i915/i915_debugfs.c @@ -777,10 +777,14 @@ i915_error_state_write(struct file *filp, struct seq_file *m = filp->private_data; struct i915_error_state_file_priv *error_priv = m->private; struct drm_device *dev = error_priv->dev; + int ret; DRM_DEBUG_DRIVER("Resetting error state\n"); - mutex_lock(&dev->struct_mutex); + ret = mutex_lock_interruptible(&dev->struct_mutex); + if (ret) + return ret; + i915_destroy_error_state(dev); mutex_unlock(&dev->struct_mutex); @@ -1450,8 +1454,12 @@ static int i915_swizzle_info(struct seq_file *m, void *data) struct drm_info_node *node = (struct drm_info_node *) m->private; struct drm_device *dev = node->minor->dev; struct drm_i915_private *dev_priv = dev->dev_private; + int ret; + + ret = mutex_lock_interruptible(&dev->struct_mutex); + if (ret) + return ret; - mutex_lock(&dev->struct_mutex); seq_printf(m, "bit6 swizzle for X-tiling = %s\n", swizzle_string(dev_priv->mm.bit_6_swizzle_x)); seq_printf(m, "bit6 swizzle for Y-tiling = %s\n", @@ -1652,7 +1660,7 @@ i915_ring_stop_write(struct file *filp, struct drm_device *dev = filp->private_data; struct drm_i915_private *dev_priv = dev->dev_private; char buf[20]; - int val = 0; + int val = 0, ret; if (cnt > 0) { if (cnt > sizeof(buf) - 1) @@ -1667,7 +1675,10 @@ i915_ring_stop_write(struct file *filp, DRM_DEBUG_DRIVER("Stopping rings 0x%08x\n", val); - mutex_lock(&dev->struct_mutex); + ret = mutex_lock_interruptible(&dev->struct_mutex); + if (ret) + return ret; + dev_priv->stop_rings = val; mutex_unlock(&dev->struct_mutex); @@ -1844,12 +1855,15 @@ i915_cache_sharing_read(struct file *filp, drm_i915_private_t *dev_priv = dev->dev_private; char buf[80]; u32 snpcr; - int len; + int len, ret; if (!(IS_GEN6(dev) || IS_GEN7(dev))) return -ENODEV; - mutex_lock(&dev_priv->dev->struct_mutex); + ret = mutex_lock_interruptible(&dev->struct_mutex); + if (ret) + return ret; + snpcr = I915_READ(GEN6_MBCUNIT_SNPCR); mutex_unlock(&dev_priv->dev->struct_mutex); @@ -1959,6 +1973,7 @@ static int i915_forcewake_release(struct inode *inode, struct file *file) { struct drm_device *dev = inode->i_private; struct drm_i915_private *dev_priv = dev->dev_private; + int ret; if (INTEL_INFO(dev)->gen < 6) return 0; @@ -1970,7 +1985,10 @@ static int i915_forcewake_release(struct inode *inode, struct file *file) * hanging here is probably a minor inconvenience not to be seen my * almost every user. */ - mutex_lock(&dev->struct_mutex); + ret = mutex_lock_interruptible(&dev->struct_mutex); + if (ret) + return ret; + gen6_gt_force_wake_put(dev_priv); mutex_unlock(&dev->struct_mutex); -- cgit v0.10.2 From c6a828d3269a5d0b9adda1438cf8d7cb2007ca71 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Wed, 8 Aug 2012 23:35:35 +0200 Subject: drm/i915: move all rps state into dev_priv->rps This way it's easier so see what belongs together, and what is used by the ilk ips code. Also add some comments that explain the locking. Note that (cur|min|max)_delay need to be duplicated, because they're also used by the ips code. v2: Missed one place that the dev_priv->ips change caught ... Reviewed-by: Ben Widawsky Reviewed-by: Damien Lespiau Reviewed-by: Chris Wilson Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/i915_debugfs.c b/drivers/gpu/drm/i915/i915_debugfs.c index 9c9e199..ed4bc98 100644 --- a/drivers/gpu/drm/i915/i915_debugfs.c +++ b/drivers/gpu/drm/i915/i915_debugfs.c @@ -1274,7 +1274,8 @@ static int i915_ring_freq_table(struct seq_file *m, void *unused) seq_printf(m, "GPU freq (MHz)\tEffective CPU freq (MHz)\n"); - for (gpu_freq = dev_priv->min_delay; gpu_freq <= dev_priv->max_delay; + for (gpu_freq = dev_priv->rps.min_delay; + gpu_freq <= dev_priv->rps.max_delay; gpu_freq++) { I915_WRITE(GEN6_PCODE_DATA, gpu_freq); I915_WRITE(GEN6_PCODE_MAILBOX, GEN6_PCODE_READY | @@ -1712,7 +1713,7 @@ i915_max_freq_read(struct file *filp, return ret; len = snprintf(buf, sizeof(buf), - "max freq: %d\n", dev_priv->max_delay * 50); + "max freq: %d\n", dev_priv->rps.max_delay * 50); mutex_unlock(&dev->struct_mutex); if (len > sizeof(buf)) @@ -1755,7 +1756,7 @@ i915_max_freq_write(struct file *filp, /* * Turbo will still be enabled, but won't go above the set value. */ - dev_priv->max_delay = val / 50; + dev_priv->rps.max_delay = val / 50; gen6_set_rps(dev, val / 50); mutex_unlock(&dev->struct_mutex); @@ -1788,7 +1789,7 @@ i915_min_freq_read(struct file *filp, char __user *ubuf, size_t max, return ret; len = snprintf(buf, sizeof(buf), - "min freq: %d\n", dev_priv->min_delay * 50); + "min freq: %d\n", dev_priv->rps.min_delay * 50); mutex_unlock(&dev->struct_mutex); if (len > sizeof(buf)) @@ -1829,7 +1830,7 @@ i915_min_freq_write(struct file *filp, const char __user *ubuf, size_t cnt, /* * Turbo will still be enabled, but won't go below the set value. */ - dev_priv->min_delay = val / 50; + dev_priv->rps.min_delay = val / 50; gen6_set_rps(dev, val / 50); mutex_unlock(&dev->struct_mutex); diff --git a/drivers/gpu/drm/i915/i915_dma.c b/drivers/gpu/drm/i915/i915_dma.c index a7a213c..0a1b64f 100644 --- a/drivers/gpu/drm/i915/i915_dma.c +++ b/drivers/gpu/drm/i915/i915_dma.c @@ -1605,7 +1605,7 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags) spin_lock_init(&dev_priv->irq_lock); spin_lock_init(&dev_priv->error_lock); - spin_lock_init(&dev_priv->rps_lock); + spin_lock_init(&dev_priv->rps.lock); if (IS_IVYBRIDGE(dev) || IS_HASWELL(dev)) dev_priv->num_pipe = 3; diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 26a2cf6..1614097 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -819,9 +819,21 @@ typedef struct drm_i915_private { bool mchbar_need_disable; - struct work_struct rps_work; - spinlock_t rps_lock; - u32 pm_iir; + /* gen6+ rps state */ + struct { + struct work_struct work; + u32 pm_iir; + /* lock - irqsave spinlock that protectects the work_struct and + * pm_iir. */ + spinlock_t lock; + + /* The below variables an all the rps hw state are protected by + * dev->struct mutext. */ + u8 cur_delay; + u8 min_delay; + u8 max_delay; + } rps; + u8 cur_delay; u8 min_delay; diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index 440c905..74c9a0e 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -349,16 +349,16 @@ static void notify_ring(struct drm_device *dev, static void gen6_pm_rps_work(struct work_struct *work) { drm_i915_private_t *dev_priv = container_of(work, drm_i915_private_t, - rps_work); + rps.work); u32 pm_iir, pm_imr; u8 new_delay; - spin_lock_irq(&dev_priv->rps_lock); - pm_iir = dev_priv->pm_iir; - dev_priv->pm_iir = 0; + spin_lock_irq(&dev_priv->rps.lock); + pm_iir = dev_priv->rps.pm_iir; + dev_priv->rps.pm_iir = 0; pm_imr = I915_READ(GEN6_PMIMR); I915_WRITE(GEN6_PMIMR, 0); - spin_unlock_irq(&dev_priv->rps_lock); + spin_unlock_irq(&dev_priv->rps.lock); if ((pm_iir & GEN6_PM_DEFERRED_EVENTS) == 0) return; @@ -366,9 +366,9 @@ static void gen6_pm_rps_work(struct work_struct *work) mutex_lock(&dev_priv->dev->struct_mutex); if (pm_iir & GEN6_PM_RP_UP_THRESHOLD) - new_delay = dev_priv->cur_delay + 1; + new_delay = dev_priv->rps.cur_delay + 1; else - new_delay = dev_priv->cur_delay - 1; + new_delay = dev_priv->rps.cur_delay - 1; gen6_set_rps(dev_priv->dev, new_delay); @@ -488,20 +488,20 @@ static void gen6_queue_rps_work(struct drm_i915_private *dev_priv, * IIR bits should never already be set because IMR should * prevent an interrupt from being shown in IIR. The warning * displays a case where we've unsafely cleared - * dev_priv->pm_iir. Although missing an interrupt of the same + * dev_priv->rps.pm_iir. Although missing an interrupt of the same * type is not a problem, it displays a problem in the logic. * - * The mask bit in IMR is cleared by rps_work. + * The mask bit in IMR is cleared by dev_priv->rps.work. */ - spin_lock_irqsave(&dev_priv->rps_lock, flags); - WARN(dev_priv->pm_iir & pm_iir, "Missed a PM interrupt\n"); - dev_priv->pm_iir |= pm_iir; - I915_WRITE(GEN6_PMIMR, dev_priv->pm_iir); + spin_lock_irqsave(&dev_priv->rps.lock, flags); + WARN(dev_priv->rps.pm_iir & pm_iir, "Missed a PM interrupt\n"); + dev_priv->rps.pm_iir |= pm_iir; + I915_WRITE(GEN6_PMIMR, dev_priv->rps.pm_iir); POSTING_READ(GEN6_PMIMR); - spin_unlock_irqrestore(&dev_priv->rps_lock, flags); + spin_unlock_irqrestore(&dev_priv->rps.lock, flags); - queue_work(dev_priv->wq, &dev_priv->rps_work); + queue_work(dev_priv->wq, &dev_priv->rps.work); } static irqreturn_t valleyview_irq_handler(DRM_IRQ_ARGS) @@ -2649,7 +2649,7 @@ void intel_irq_init(struct drm_device *dev) INIT_WORK(&dev_priv->hotplug_work, i915_hotplug_work_func); INIT_WORK(&dev_priv->error_work, i915_error_work_func); - INIT_WORK(&dev_priv->rps_work, gen6_pm_rps_work); + INIT_WORK(&dev_priv->rps.work, gen6_pm_rps_work); INIT_WORK(&dev_priv->parity_error_work, ivybridge_parity_work); dev->driver->get_vblank_counter = i915_get_vblank_counter; diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 2e1f28f..bddb290 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -7218,7 +7218,7 @@ void intel_modeset_cleanup(struct drm_device *dev) * enqueue unpin/hotplug work. */ drm_irq_uninstall(dev); cancel_work_sync(&dev_priv->hotplug_work); - cancel_work_sync(&dev_priv->rps_work); + cancel_work_sync(&dev_priv->rps.work); /* flush any delayed tasks or pending work */ flush_scheduled_work(); diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c index 4fe8d0a..e1439ae 100644 --- a/drivers/gpu/drm/i915/intel_pm.c +++ b/drivers/gpu/drm/i915/intel_pm.c @@ -2277,9 +2277,10 @@ static u32 gen6_rps_limits(struct drm_i915_private *dev_priv, u8 *val) u32 limits; limits = 0; - if (*val >= dev_priv->max_delay) - *val = dev_priv->max_delay; - limits |= dev_priv->max_delay << 24; + + if (*val >= dev_priv->rps.max_delay) + *val = dev_priv->rps.max_delay; + limits |= dev_priv->rps.max_delay << 24; /* Only set the down limit when we've reached the lowest level to avoid * getting more interrupts, otherwise leave this clear. This prevents a @@ -2287,9 +2288,9 @@ static u32 gen6_rps_limits(struct drm_i915_private *dev_priv, u8 *val) * the hw runs at the minimal clock before selecting the desired * frequency, if the down threshold expires in that window we will not * receive a down interrupt. */ - if (*val <= dev_priv->min_delay) { - *val = dev_priv->min_delay; - limits |= dev_priv->min_delay << 16; + if (*val <= dev_priv->rps.min_delay) { + *val = dev_priv->rps.min_delay; + limits |= dev_priv->rps.min_delay << 16; } return limits; @@ -2302,7 +2303,7 @@ void gen6_set_rps(struct drm_device *dev, u8 val) WARN_ON(!mutex_is_locked(&dev->struct_mutex)); - if (val == dev_priv->cur_delay) + if (val == dev_priv->rps.cur_delay) return; I915_WRITE(GEN6_RPNSWREQ, @@ -2315,7 +2316,7 @@ void gen6_set_rps(struct drm_device *dev, u8 val) */ I915_WRITE(GEN6_RP_INTERRUPT_LIMITS, limits); - dev_priv->cur_delay = val; + dev_priv->rps.cur_delay = val; } static void gen6_disable_rps(struct drm_device *dev) @@ -2331,9 +2332,9 @@ static void gen6_disable_rps(struct drm_device *dev) * register (PMIMR) to mask PM interrupts. The only risk is in leaving * stale bits in PMIIR and PMIMR which gen6_enable_rps will clean up. */ - spin_lock_irq(&dev_priv->rps_lock); - dev_priv->pm_iir = 0; - spin_unlock_irq(&dev_priv->rps_lock); + spin_lock_irq(&dev_priv->rps.lock); + dev_priv->rps.pm_iir = 0; + spin_unlock_irq(&dev_priv->rps.lock); I915_WRITE(GEN6_PMIIR, I915_READ(GEN6_PMIIR)); } @@ -2402,9 +2403,9 @@ static void gen6_enable_rps(struct drm_device *dev) gt_perf_status = I915_READ(GEN6_GT_PERF_STATUS); /* In units of 100MHz */ - dev_priv->max_delay = rp_state_cap & 0xff; - dev_priv->min_delay = (rp_state_cap & 0xff0000) >> 16; - dev_priv->cur_delay = 0; + dev_priv->rps.max_delay = rp_state_cap & 0xff; + dev_priv->rps.min_delay = (rp_state_cap & 0xff0000) >> 16; + dev_priv->rps.cur_delay = 0; /* disable the counters and set deterministic thresholds */ I915_WRITE(GEN6_RC_CONTROL, 0); @@ -2457,8 +2458,8 @@ static void gen6_enable_rps(struct drm_device *dev) I915_WRITE(GEN6_RP_DOWN_TIMEOUT, 1000000); I915_WRITE(GEN6_RP_INTERRUPT_LIMITS, - dev_priv->max_delay << 24 | - dev_priv->min_delay << 16); + dev_priv->rps.max_delay << 24 | + dev_priv->rps.min_delay << 16); if (IS_HASWELL(dev)) { I915_WRITE(GEN6_RP_UP_THRESHOLD, 59400); @@ -2503,7 +2504,7 @@ static void gen6_enable_rps(struct drm_device *dev) 500)) DRM_ERROR("timeout waiting for pcode mailbox to finish\n"); if (pcu_mbox & (1<<31)) { /* OC supported */ - dev_priv->max_delay = pcu_mbox & 0xff; + dev_priv->rps.max_delay = pcu_mbox & 0xff; DRM_DEBUG_DRIVER("overclocking supported, adjusting frequency max to %dMHz\n", pcu_mbox * 50); } @@ -2511,10 +2512,10 @@ static void gen6_enable_rps(struct drm_device *dev) /* requires MSI enabled */ I915_WRITE(GEN6_PMIER, GEN6_PM_DEFERRED_EVENTS); - spin_lock_irq(&dev_priv->rps_lock); - WARN_ON(dev_priv->pm_iir != 0); + spin_lock_irq(&dev_priv->rps.lock); + WARN_ON(dev_priv->rps.pm_iir != 0); I915_WRITE(GEN6_PMIMR, 0); - spin_unlock_irq(&dev_priv->rps_lock); + spin_unlock_irq(&dev_priv->rps.lock); /* enable all PM interrupts */ I915_WRITE(GEN6_PMINTRMSK, 0); @@ -2546,9 +2547,9 @@ static void gen6_update_ring_freq(struct drm_device *dev) * to use for memory access. We do this by specifying the IA frequency * the PCU should use as a reference to determine the ring frequency. */ - for (gpu_freq = dev_priv->max_delay; gpu_freq >= dev_priv->min_delay; + for (gpu_freq = dev_priv->rps.max_delay; gpu_freq >= dev_priv->rps.min_delay; gpu_freq--) { - int diff = dev_priv->max_delay - gpu_freq; + int diff = dev_priv->rps.max_delay - gpu_freq; /* * For GPU frequencies less than 750MHz, just use the lowest @@ -2991,7 +2992,7 @@ unsigned long i915_gfx_val(struct drm_i915_private *dev_priv) assert_spin_locked(&mchdev_lock); - pxvid = I915_READ(PXVFREQ_BASE + (dev_priv->cur_delay * 4)); + pxvid = I915_READ(PXVFREQ_BASE + (dev_priv->rps.cur_delay * 4)); pxvid = (pxvid >> 24) & 0x7f; ext_v = pvid_to_extvid(dev_priv, pxvid); -- cgit v0.10.2 From 35eb73234be6e7fa515c5f1b233ba33d030938cf Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Wed, 8 Aug 2012 23:35:36 +0200 Subject: drm/i915: kill dev_priv->mchdev_lock It's only ever a pointer to the global mchdev_lock, and we don't use it at all. Reviewed-by: Damien Lespiau Reviewed-by: Chris Wilson Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 1614097..261fe21 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -850,7 +850,6 @@ typedef struct drm_i915_private { int c_m; int r_t; u8 corr; - spinlock_t *mchdev_lock; enum no_fbc_reason no_fbc_reason; diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c index e1439ae..212b28e 100644 --- a/drivers/gpu/drm/i915/intel_pm.c +++ b/drivers/gpu/drm/i915/intel_pm.c @@ -3188,7 +3188,6 @@ void intel_gpu_ips_init(struct drm_i915_private *dev_priv) * set up, to avoid intel-ips sneaking in and reading bogus values. */ spin_lock(&mchdev_lock); i915_mch_dev = dev_priv; - dev_priv->mchdev_lock = &mchdev_lock; spin_unlock(&mchdev_lock); ips_ping_for_i915_load(); -- cgit v0.10.2 From 73edd18f610b6dd900cc3a180919dc643fff8513 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Wed, 8 Aug 2012 23:35:37 +0200 Subject: drm/i915: DE_PCU_EVENT irq is ilk-only Like all the other drps/ips stuff. Hence add the corresponding check, give the function a preciser prefix and move the single reg clearing into the rps handling function, too. Reviewed-by: Ben Widawsky Reviewed-by: Damien Lespiau Reviewed-by: Chris Wilson Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index 74c9a0e..3e203da 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -296,12 +296,14 @@ static void i915_hotplug_work_func(struct work_struct *work) drm_helper_hpd_irq_event(dev); } -static void i915_handle_rps_change(struct drm_device *dev) +static void ironlake_handle_rps_change(struct drm_device *dev) { drm_i915_private_t *dev_priv = dev->dev_private; u32 busy_up, busy_down, max_avg, min_avg; u8 new_delay = dev_priv->cur_delay; + I915_WRITE16(MEMINTRSTS, I915_READ(MEMINTRSTS)); + I915_WRITE16(MEMINTRSTS, MEMINT_EVAL_CHG); busy_up = I915_READ(RCPREVBSYTUPAVG); busy_down = I915_READ(RCPREVBSYTDNAVG); @@ -794,10 +796,8 @@ static irqreturn_t ironlake_irq_handler(DRM_IRQ_ARGS) ibx_irq_handler(dev, pch_iir); } - if (de_iir & DE_PCU_EVENT) { - I915_WRITE16(MEMINTRSTS, I915_READ(MEMINTRSTS)); - i915_handle_rps_change(dev); - } + if (IS_GEN5(dev) && de_iir & DE_PCU_EVENT) + ironlake_handle_rps_change(dev); if (IS_GEN6(dev) && pm_iir & GEN6_PM_DEFERRED_EVENTS) gen6_queue_rps_work(dev_priv, pm_iir); -- cgit v0.10.2 From 9270388e184fddb83e3b69c6b7f5b523c070e53d Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Thu, 9 Aug 2012 16:46:01 +0200 Subject: drm/i915: fix up ilk drps/ips locking We change the drps/ips sw/hw state from different callers: Our own irq handler, the external intel-ips module and from process context. Most of these callers don't take any lock at all. Protect everything by making the mchdev_lock irqsave and grabbing it in all relevant callsites. Note that we have to convert a few sleeps in the drps enable/disable code to delays, but alas, I'm not volunteering to restructure the code around a few work items. For paranoia add a spin_locked assert to ironlake_set_drps, too. v2: Move one access inside the lock protection. Caught by the dev_priv->ips mass-rename ... v3: Resolve rebase conflict. Reviewed-by: Damien Lespiau Reviewed-by: Chris Wilson Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index 3e203da..d15ea50 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -296,14 +296,22 @@ static void i915_hotplug_work_func(struct work_struct *work) drm_helper_hpd_irq_event(dev); } +/* defined intel_pm.c */ +extern spinlock_t mchdev_lock; + static void ironlake_handle_rps_change(struct drm_device *dev) { drm_i915_private_t *dev_priv = dev->dev_private; u32 busy_up, busy_down, max_avg, min_avg; - u8 new_delay = dev_priv->cur_delay; + u8 new_delay; + unsigned long flags; + + spin_lock_irqsave(&mchdev_lock, flags); I915_WRITE16(MEMINTRSTS, I915_READ(MEMINTRSTS)); + new_delay = dev_priv->cur_delay; + I915_WRITE16(MEMINTRSTS, MEMINT_EVAL_CHG); busy_up = I915_READ(RCPREVBSYTUPAVG); busy_down = I915_READ(RCPREVBSYTDNAVG); @@ -326,6 +334,8 @@ static void ironlake_handle_rps_change(struct drm_device *dev) if (ironlake_set_drps(dev, new_delay)) dev_priv->cur_delay = new_delay; + spin_unlock_irqrestore(&mchdev_lock, flags); + return; } diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c index 212b28e..6d68a05 100644 --- a/drivers/gpu/drm/i915/intel_pm.c +++ b/drivers/gpu/drm/i915/intel_pm.c @@ -2160,11 +2160,28 @@ err_unref: return NULL; } +/** + * Lock protecting IPS related data structures + * - i915_mch_dev + * - dev_priv->max_delay + * - dev_priv->min_delay + * - dev_priv->fmax + * - dev_priv->gpu_busy + * - dev_priv->gfx_power + */ +DEFINE_SPINLOCK(mchdev_lock); + +/* Global for IPS driver to get at the current i915 device. Protected by + * mchdev_lock. */ +static struct drm_i915_private *i915_mch_dev; + bool ironlake_set_drps(struct drm_device *dev, u8 val) { struct drm_i915_private *dev_priv = dev->dev_private; u16 rgvswctl; + assert_spin_locked(&mchdev_lock); + rgvswctl = I915_READ16(MEMSWCTL); if (rgvswctl & MEMCTL_CMD_STS) { DRM_DEBUG("gpu busy, RCS change rejected\n"); @@ -2188,6 +2205,8 @@ static void ironlake_enable_drps(struct drm_device *dev) u32 rgvmodectl = I915_READ(MEMMODECTL); u8 fmax, fmin, fstart, vstart; + spin_lock_irq(&mchdev_lock); + /* Enable temp reporting */ I915_WRITE16(PMMISC, I915_READ(PMMISC) | MCPPCE_EN); I915_WRITE16(TSC1, I915_READ(TSC1) | TSE); @@ -2233,9 +2252,9 @@ static void ironlake_enable_drps(struct drm_device *dev) rgvmodectl |= MEMMODE_SWMODE_EN; I915_WRITE(MEMMODECTL, rgvmodectl); - if (wait_for((I915_READ(MEMSWCTL) & MEMCTL_CMD_STS) == 0, 10)) + if (wait_for_atomic((I915_READ(MEMSWCTL) & MEMCTL_CMD_STS) == 0, 10)) DRM_ERROR("stuck trying to change perf mode\n"); - msleep(1); + mdelay(1); ironlake_set_drps(dev, fstart); @@ -2244,12 +2263,18 @@ static void ironlake_enable_drps(struct drm_device *dev) dev_priv->last_time1 = jiffies_to_msecs(jiffies); dev_priv->last_count2 = I915_READ(0x112f4); getrawmonotonic(&dev_priv->last_time2); + + spin_unlock_irq(&mchdev_lock); } static void ironlake_disable_drps(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; - u16 rgvswctl = I915_READ16(MEMSWCTL); + u16 rgvswctl; + + spin_lock_irq(&mchdev_lock); + + rgvswctl = I915_READ16(MEMSWCTL); /* Ack interrupts, disable EFC interrupt */ I915_WRITE(MEMINTREN, I915_READ(MEMINTREN) & ~MEMINT_EVAL_CHG_EN); @@ -2260,11 +2285,12 @@ static void ironlake_disable_drps(struct drm_device *dev) /* Go back to the starting frequency */ ironlake_set_drps(dev, dev_priv->fstart); - msleep(1); + mdelay(1); rgvswctl |= MEMCTL_CMD_STS; I915_WRITE(MEMSWCTL, rgvswctl); - msleep(1); + mdelay(1); + spin_unlock_irq(&mchdev_lock); } /* There's a funny hw issue where the hw returns all 0 when reading from @@ -2713,21 +2739,6 @@ static const struct cparams { { 0, 800, 231, 23784 }, }; -/** - * Lock protecting IPS related data structures - * - i915_mch_dev - * - dev_priv->max_delay - * - dev_priv->min_delay - * - dev_priv->fmax - * - dev_priv->gpu_busy - * - dev_priv->gfx_power - */ -static DEFINE_SPINLOCK(mchdev_lock); - -/* Global for IPS driver to get at the current i915 device. Protected by - * mchdev_lock. */ -static struct drm_i915_private *i915_mch_dev; - unsigned long i915_chipset_val(struct drm_i915_private *dev_priv) { u64 total_count, diff, ret; @@ -2978,11 +2989,11 @@ void i915_update_gfx_val(struct drm_i915_private *dev_priv) if (dev_priv->info->gen != 5) return; - spin_lock(&mchdev_lock); + spin_lock_irq(&mchdev_lock); __i915_update_gfx_val(dev_priv); - spin_unlock(&mchdev_lock); + spin_unlock_irq(&mchdev_lock); } unsigned long i915_gfx_val(struct drm_i915_private *dev_priv) @@ -3033,7 +3044,7 @@ unsigned long i915_read_mch_val(void) struct drm_i915_private *dev_priv; unsigned long chipset_val, graphics_val, ret = 0; - spin_lock(&mchdev_lock); + spin_lock_irq(&mchdev_lock); if (!i915_mch_dev) goto out_unlock; dev_priv = i915_mch_dev; @@ -3044,7 +3055,7 @@ unsigned long i915_read_mch_val(void) ret = chipset_val + graphics_val; out_unlock: - spin_unlock(&mchdev_lock); + spin_unlock_irq(&mchdev_lock); return ret; } @@ -3060,7 +3071,7 @@ bool i915_gpu_raise(void) struct drm_i915_private *dev_priv; bool ret = true; - spin_lock(&mchdev_lock); + spin_lock_irq(&mchdev_lock); if (!i915_mch_dev) { ret = false; goto out_unlock; @@ -3071,7 +3082,7 @@ bool i915_gpu_raise(void) dev_priv->max_delay--; out_unlock: - spin_unlock(&mchdev_lock); + spin_unlock_irq(&mchdev_lock); return ret; } @@ -3088,7 +3099,7 @@ bool i915_gpu_lower(void) struct drm_i915_private *dev_priv; bool ret = true; - spin_lock(&mchdev_lock); + spin_lock_irq(&mchdev_lock); if (!i915_mch_dev) { ret = false; goto out_unlock; @@ -3099,7 +3110,7 @@ bool i915_gpu_lower(void) dev_priv->max_delay++; out_unlock: - spin_unlock(&mchdev_lock); + spin_unlock_irq(&mchdev_lock); return ret; } @@ -3117,7 +3128,7 @@ bool i915_gpu_busy(void) bool ret = false; int i; - spin_lock(&mchdev_lock); + spin_lock_irq(&mchdev_lock); if (!i915_mch_dev) goto out_unlock; dev_priv = i915_mch_dev; @@ -3126,7 +3137,7 @@ bool i915_gpu_busy(void) ret |= !list_empty(&ring->request_list); out_unlock: - spin_unlock(&mchdev_lock); + spin_unlock_irq(&mchdev_lock); return ret; } @@ -3143,7 +3154,7 @@ bool i915_gpu_turbo_disable(void) struct drm_i915_private *dev_priv; bool ret = true; - spin_lock(&mchdev_lock); + spin_lock_irq(&mchdev_lock); if (!i915_mch_dev) { ret = false; goto out_unlock; @@ -3156,7 +3167,7 @@ bool i915_gpu_turbo_disable(void) ret = false; out_unlock: - spin_unlock(&mchdev_lock); + spin_unlock_irq(&mchdev_lock); return ret; } @@ -3186,18 +3197,18 @@ void intel_gpu_ips_init(struct drm_i915_private *dev_priv) { /* We only register the i915 ips part with intel-ips once everything is * set up, to avoid intel-ips sneaking in and reading bogus values. */ - spin_lock(&mchdev_lock); + spin_lock_irq(&mchdev_lock); i915_mch_dev = dev_priv; - spin_unlock(&mchdev_lock); + spin_unlock_irq(&mchdev_lock); ips_ping_for_i915_load(); } void intel_gpu_ips_teardown(void) { - spin_lock(&mchdev_lock); + spin_lock_irq(&mchdev_lock); i915_mch_dev = NULL; - spin_unlock(&mchdev_lock); + spin_unlock_irq(&mchdev_lock); } static void intel_init_emon(struct drm_device *dev) { -- cgit v0.10.2 From 456470eb583f063ee84c6818251e638598be0fb8 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Wed, 8 Aug 2012 23:35:40 +0200 Subject: drm/i915: enable rc6 on ilk again I have the faint hope that the total absence of any locking for the rps code wasn't too good an idea and could very well have caused some rc6 related regressions. Unfortunately we've never managed to reproduce these issues on any of our own machines, so the only way to go about this is to enable it and see what happens. While at it, kill some stale comments and improve the logging. Acked-by: Ben Widawsky Reviewed-by: Damien Lespiau Signed-Off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c index 6d68a05..d64dffb 100644 --- a/drivers/gpu/drm/i915/intel_pm.c +++ b/drivers/gpu/drm/i915/intel_pm.c @@ -2367,31 +2367,26 @@ static void gen6_disable_rps(struct drm_device *dev) int intel_enable_rc6(const struct drm_device *dev) { - /* - * Respect the kernel parameter if it is set - */ + /* Respect the kernel parameter if it is set */ if (i915_enable_rc6 >= 0) return i915_enable_rc6; - /* - * Disable RC6 on Ironlake - */ - if (INTEL_INFO(dev)->gen == 5) - return 0; + if (INTEL_INFO(dev)->gen == 5) { + DRM_DEBUG_DRIVER("Ironlake: only RC6 available\n"); + return INTEL_RC6_ENABLE; + } - /* On Haswell, only RC6 is available. So let's enable it by default to - * provide better testing and coverage since the beginning. - */ - if (IS_HASWELL(dev)) + if (IS_HASWELL(dev)) { + DRM_DEBUG_DRIVER("Haswell: only RC6 available\n"); return INTEL_RC6_ENABLE; + } - /* - * Disable rc6 on Sandybridge - */ + /* snb/ivb have more than one rc6 state. */ if (INTEL_INFO(dev)->gen == 6) { DRM_DEBUG_DRIVER("Sandybridge: deep RC6 disabled\n"); return INTEL_RC6_ENABLE; } + DRM_DEBUG_DRIVER("RC6 and deep RC6 enabled\n"); return (INTEL_RC6_ENABLE | INTEL_RC6p_ENABLE); } -- cgit v0.10.2 From b2eadbc85b2c26df3fd2fe5c53c2a47cfd307249 Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Thu, 9 Aug 2012 10:58:30 +0100 Subject: drm/i915: Lazily apply the SNB+ seqno w/a Avoid the forcewake overhead when simply retiring requests, as often the last seen seqno is good enough to satisfy the retirment process and will be promptly re-run in any case. Only ensure that we force the coherent seqno read when we are explicitly waiting upon a completion event to be sure that none go missing, and also for when we are reporting seqno values in case of error or debugging. This greatly reduces the load for userspace using the busy-ioctl to track active buffers, for instance halving the CPU used by X in pushing the pixels from a software render (flash). The effect will be even more magnified with userptr and so providing a zero-copy upload path in that instance, or in similar instances where X is simply compositing DRI buffers. v2: Reverse the polarity of the tachyon stream. Daniel suggested that 'force' was too generic for the parameter name and that 'lazy_coherency' better encapsulated the semantics of it being an optimization and its purpose. Also notice that gen6_get_seqno() is only used by gen6/7 chipsets and so the test for IS_GEN6 || IS_GEN7 is redundant in that function. Signed-off-by: Chris Wilson Reviewed-by: Daniel Vetter Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/i915_debugfs.c b/drivers/gpu/drm/i915/i915_debugfs.c index ed4bc98..0e8f14d 100644 --- a/drivers/gpu/drm/i915/i915_debugfs.c +++ b/drivers/gpu/drm/i915/i915_debugfs.c @@ -391,7 +391,7 @@ static void i915_ring_seqno_info(struct seq_file *m, { if (ring->get_seqno) { seq_printf(m, "Current sequence (%s): %d\n", - ring->name, ring->get_seqno(ring)); + ring->name, ring->get_seqno(ring, false)); } } diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index c540321..0514593 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -1716,7 +1716,7 @@ i915_gem_retire_requests_ring(struct intel_ring_buffer *ring) WARN_ON(i915_verify_lists(ring->dev)); - seqno = ring->get_seqno(ring); + seqno = ring->get_seqno(ring, true); for (i = 0; i < ARRAY_SIZE(ring->sync_seqno); i++) if (seqno >= ring->sync_seqno[i]) @@ -1888,7 +1888,7 @@ static int __wait_seqno(struct intel_ring_buffer *ring, u32 seqno, bool wait_forever = true; int ret; - if (i915_seqno_passed(ring->get_seqno(ring), seqno)) + if (i915_seqno_passed(ring->get_seqno(ring, true), seqno)) return 0; trace_i915_gem_request_wait_begin(ring, seqno); @@ -1907,7 +1907,7 @@ static int __wait_seqno(struct intel_ring_buffer *ring, u32 seqno, getrawmonotonic(&before); #define EXIT_COND \ - (i915_seqno_passed(ring->get_seqno(ring), seqno) || \ + (i915_seqno_passed(ring->get_seqno(ring, false), seqno) || \ atomic_read(&dev_priv->mm.wedged)) do { if (interruptible) diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index d15ea50..0c37101 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -347,7 +347,7 @@ static void notify_ring(struct drm_device *dev, if (ring->obj == NULL) return; - trace_i915_gem_request_complete(ring, ring->get_seqno(ring)); + trace_i915_gem_request_complete(ring, ring->get_seqno(ring, false)); wake_up_all(&ring->irq_queue); if (i915_enable_hangcheck) { @@ -1051,7 +1051,7 @@ i915_error_first_batchbuffer(struct drm_i915_private *dev_priv, if (!ring->get_seqno) return NULL; - seqno = ring->get_seqno(ring); + seqno = ring->get_seqno(ring, false); list_for_each_entry(obj, &dev_priv->mm.active_list, mm_list) { if (obj->ring != ring) continue; @@ -1105,7 +1105,7 @@ static void i915_record_ring_state(struct drm_device *dev, error->waiting[ring->id] = waitqueue_active(&ring->irq_queue); error->instpm[ring->id] = I915_READ(RING_INSTPM(ring->mmio_base)); - error->seqno[ring->id] = ring->get_seqno(ring); + error->seqno[ring->id] = ring->get_seqno(ring, false); error->acthd[ring->id] = intel_ring_get_active_head(ring); error->head[ring->id] = I915_READ_HEAD(ring); error->tail[ring->id] = I915_READ_TAIL(ring); @@ -1602,7 +1602,8 @@ ring_last_seqno(struct intel_ring_buffer *ring) static bool i915_hangcheck_ring_idle(struct intel_ring_buffer *ring, bool *err) { if (list_empty(&ring->request_list) || - i915_seqno_passed(ring->get_seqno(ring), ring_last_seqno(ring))) { + i915_seqno_passed(ring->get_seqno(ring, false), + ring_last_seqno(ring))) { /* Issue a wake-up to catch stuck h/w. */ if (waitqueue_active(&ring->irq_queue)) { DRM_ERROR("Hangcheck timer elapsed... %s idle\n", diff --git a/drivers/gpu/drm/i915/intel_ringbuffer.c b/drivers/gpu/drm/i915/intel_ringbuffer.c index 8733da5..e278675 100644 --- a/drivers/gpu/drm/i915/intel_ringbuffer.c +++ b/drivers/gpu/drm/i915/intel_ringbuffer.c @@ -625,26 +625,24 @@ pc_render_add_request(struct intel_ring_buffer *ring, } static u32 -gen6_ring_get_seqno(struct intel_ring_buffer *ring) +gen6_ring_get_seqno(struct intel_ring_buffer *ring, bool lazy_coherency) { - struct drm_device *dev = ring->dev; - /* Workaround to force correct ordering between irq and seqno writes on * ivb (and maybe also on snb) by reading from a CS register (like * ACTHD) before reading the status page. */ - if (IS_GEN6(dev) || IS_GEN7(dev)) + if (!lazy_coherency) intel_ring_get_active_head(ring); return intel_read_status_page(ring, I915_GEM_HWS_INDEX); } static u32 -ring_get_seqno(struct intel_ring_buffer *ring) +ring_get_seqno(struct intel_ring_buffer *ring, bool lazy_coherency) { return intel_read_status_page(ring, I915_GEM_HWS_INDEX); } static u32 -pc_render_get_seqno(struct intel_ring_buffer *ring) +pc_render_get_seqno(struct intel_ring_buffer *ring, bool lazy_coherency) { struct pipe_control *pc = ring->private; return pc->cpu_page[0]; diff --git a/drivers/gpu/drm/i915/intel_ringbuffer.h b/drivers/gpu/drm/i915/intel_ringbuffer.h index 8b2b92e..2ea7a31 100644 --- a/drivers/gpu/drm/i915/intel_ringbuffer.h +++ b/drivers/gpu/drm/i915/intel_ringbuffer.h @@ -72,7 +72,14 @@ struct intel_ring_buffer { u32 flush_domains); int (*add_request)(struct intel_ring_buffer *ring, u32 *seqno); - u32 (*get_seqno)(struct intel_ring_buffer *ring); + /* Some chipsets are not quite as coherent as advertised and need + * an expensive kick to force a true read of the up-to-date seqno. + * However, the up-to-date seqno is not always required and the last + * seen value is good enough. Note that the seqno will always be + * monotonic, even if not coherent. + */ + u32 (*get_seqno)(struct intel_ring_buffer *ring, + bool lazy_coherency); int (*dispatch_execbuffer)(struct intel_ring_buffer *ring, u32 offset, u32 length); void (*cleanup)(struct intel_ring_buffer *ring); -- cgit v0.10.2 From 228d3e367c91af16d672fb8bff78b030acb5efc3 Mon Sep 17 00:00:00 2001 From: Paulo Zanoni Date: Fri, 10 Aug 2012 10:05:10 -0300 Subject: drm/i915: use the correct encoder type when comparing - intel_encoder->type is INTEL_OUTPUT_SOMETHING - drm_encoder->encoder_type is DRM_MODE_ENCODER_SOMETHING Here we're using intel_encoder, so compare the oranges against oranges. While at it, rename the variable to "intel_encoder" so we keep our naming standards used everywhere. Luckily this was not a bug because both DRM_MODE_ENCODER_DAC and INTEL_OUTPUT_ANALOG are defined as 1. This is the only case where the drm definition matches the intel definition. Signed-off-by: Paulo Zanoni Reviewed-by: Jani Nikula Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index bddb290..e3afe96 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -2837,13 +2837,13 @@ static void intel_crtc_wait_for_pending_flips(struct drm_crtc *crtc) static bool intel_crtc_driving_pch(struct drm_crtc *crtc) { struct drm_device *dev = crtc->dev; - struct intel_encoder *encoder; + struct intel_encoder *intel_encoder; /* * If there's a non-PCH eDP on this crtc, it must be DP_A, and that * must be driven by its own crtc; no sharing is possible. */ - for_each_encoder_on_crtc(dev, crtc, encoder) { + for_each_encoder_on_crtc(dev, crtc, intel_encoder) { /* On Haswell, LPT PCH handles the VGA connection via FDI, and Haswell * CPU handles all others */ @@ -2851,19 +2851,19 @@ static bool intel_crtc_driving_pch(struct drm_crtc *crtc) /* 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) { + if (intel_encoder->type == INTEL_OUTPUT_ANALOG) { 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); + intel_encoder->type); return false; } } - switch (encoder->type) { + switch (intel_encoder->type) { case INTEL_OUTPUT_EDP: - if (!intel_encoder_is_pch_edp(&encoder->base)) + if (!intel_encoder_is_pch_edp(&intel_encoder->base)) return false; continue; } -- cgit v0.10.2 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 diff --git a/drivers/gpu/drm/i915/intel_ddi.c b/drivers/gpu/drm/i915/intel_ddi.c index ff03a3a..9584226 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 v0.10.2 From 20d5a540e55a29daeef12706f9ee73baf5641c16 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Thu, 9 Aug 2012 22:33:58 +0200 Subject: drm/i915: don't grab dev->struct_mutex for userspace forcewak Since forcewake is now protected by a spinlock, we don't need to grab dev->struct_mutex any more. This way we can also get rid of a stale comment, noticed by Ben Widawsky while reviewing some locking changes. v2: Kill the unused variable ret, noticed by Fengguang Wu. Reviewed-by: Chris Wilson Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/i915_debugfs.c b/drivers/gpu/drm/i915/i915_debugfs.c index 0e8f14d..a18e936 100644 --- a/drivers/gpu/drm/i915/i915_debugfs.c +++ b/drivers/gpu/drm/i915/i915_debugfs.c @@ -1956,16 +1956,11 @@ static int i915_forcewake_open(struct inode *inode, struct file *file) { struct drm_device *dev = inode->i_private; struct drm_i915_private *dev_priv = dev->dev_private; - int ret; if (INTEL_INFO(dev)->gen < 6) return 0; - ret = mutex_lock_interruptible(&dev->struct_mutex); - if (ret) - return ret; gen6_gt_force_wake_get(dev_priv); - mutex_unlock(&dev->struct_mutex); return 0; } @@ -1974,24 +1969,11 @@ static int i915_forcewake_release(struct inode *inode, struct file *file) { struct drm_device *dev = inode->i_private; struct drm_i915_private *dev_priv = dev->dev_private; - int ret; if (INTEL_INFO(dev)->gen < 6) return 0; - /* - * It's bad that we can potentially hang userspace if struct_mutex gets - * forever stuck. However, if we cannot acquire this lock it means that - * almost certainly the driver has hung, is not unload-able. Therefore - * hanging here is probably a minor inconvenience not to be seen my - * almost every user. - */ - ret = mutex_lock_interruptible(&dev->struct_mutex); - if (ret) - return ret; - gen6_gt_force_wake_put(dev_priv); - mutex_unlock(&dev->struct_mutex); return 0; } -- cgit v0.10.2 From e9a851ed634628489ca4a392740694d0ded78cb9 Mon Sep 17 00:00:00 2001 From: "Xu, Anhua" Date: Mon, 13 Aug 2012 03:08:33 +0000 Subject: drm/i915: fix wrong order of parameters in port checking functions Wrong order of parameters passed-in when calling hdmi/adpa /lvds_pipe_enabled(), 2nd and 3rd parameters are reversed. This bug was indroduced by commit 1519b9956eb4b4180fa3f47c73341463cdcfaa37 Author: Keith Packard Date: Sat Aug 6 10:35:34 2011 -0700 drm/i915: Fix PCH port pipe select in CPT disable paths The reachable tag for this commit is v3.1-rc1-3-g1519b99 Signed-off-by: Anhua Xu Reviewed-by: Chris Wilson Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index a9ab1af..46a4c92 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -1384,7 +1384,7 @@ static void assert_pch_hdmi_disabled(struct drm_i915_private *dev_priv, enum pipe pipe, int reg) { u32 val = I915_READ(reg); - WARN(hdmi_pipe_enabled(dev_priv, val, pipe), + WARN(hdmi_pipe_enabled(dev_priv, pipe, val), "PCH HDMI (0x%08x) enabled on transcoder %c, should be disabled\n", reg, pipe_name(pipe)); @@ -1404,13 +1404,13 @@ static void assert_pch_ports_disabled(struct drm_i915_private *dev_priv, reg = PCH_ADPA; val = I915_READ(reg); - WARN(adpa_pipe_enabled(dev_priv, val, pipe), + WARN(adpa_pipe_enabled(dev_priv, pipe, val), "PCH VGA enabled on transcoder %c, should be disabled\n", pipe_name(pipe)); reg = PCH_LVDS; val = I915_READ(reg); - WARN(lvds_pipe_enabled(dev_priv, val, pipe), + WARN(lvds_pipe_enabled(dev_priv, pipe, val), "PCH LVDS enabled on transcoder %c, should be disabled\n", pipe_name(pipe)); @@ -1874,7 +1874,7 @@ static void disable_pch_hdmi(struct drm_i915_private *dev_priv, enum pipe pipe, int reg) { u32 val = I915_READ(reg); - if (hdmi_pipe_enabled(dev_priv, val, pipe)) { + if (hdmi_pipe_enabled(dev_priv, pipe, val)) { DRM_DEBUG_KMS("Disabling pch HDMI %x on pipe %d\n", reg, pipe); I915_WRITE(reg, val & ~PORT_ENABLE); @@ -1896,12 +1896,12 @@ static void intel_disable_pch_ports(struct drm_i915_private *dev_priv, reg = PCH_ADPA; val = I915_READ(reg); - if (adpa_pipe_enabled(dev_priv, val, pipe)) + if (adpa_pipe_enabled(dev_priv, pipe, val)) I915_WRITE(reg, val & ~ADPA_DAC_ENABLE); reg = PCH_LVDS; val = I915_READ(reg); - if (lvds_pipe_enabled(dev_priv, val, pipe)) { + if (lvds_pipe_enabled(dev_priv, pipe, val)) { DRM_DEBUG_KMS("disable lvds on pipe %d val 0x%08x\n", pipe, val); I915_WRITE(reg, val & ~LVDS_PORT_EN); POSTING_READ(reg); -- cgit v0.10.2 From dcdaed6eae0d7ed0c68aaa1ebfceb242625b3bf0 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Sun, 12 Aug 2012 21:19:34 +0200 Subject: drm/i915: add missing gen2 pipe A quirk entries For some odd reason we've missed i830 and a i855 variant. Also kill the two now redundant i830 entries. v2: Don't add the missing 855 id to the pipe A quirk list, we seem to lack justification for it. Reviewed-by: Chris Wilson Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 46a4c92..dd760f4 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -7017,21 +7017,16 @@ static struct intel_quirk intel_quirks[] = { /* HP Mini needs pipe A force quirk (LP: #322104) */ { 0x27ae, 0x103c, 0x361a, quirk_pipea_force }, - /* Thinkpad R31 needs pipe A force quirk */ - { 0x3577, 0x1014, 0x0505, quirk_pipea_force }, /* Toshiba Protege R-205, S-209 needs pipe A force quirk */ { 0x2592, 0x1179, 0x0001, quirk_pipea_force }, - /* ThinkPad X30 needs pipe A force quirk (LP: #304614) */ - { 0x3577, 0x1014, 0x0513, quirk_pipea_force }, - /* ThinkPad X40 needs pipe A force quirk */ - /* ThinkPad T60 needs pipe A force quirk (bug #16494) */ { 0x2782, 0x17aa, 0x201a, quirk_pipea_force }, /* 855 & before need to leave pipe A & dpll A up */ { 0x3582, PCI_ANY_ID, PCI_ANY_ID, quirk_pipea_force }, { 0x2562, PCI_ANY_ID, PCI_ANY_ID, quirk_pipea_force }, + { 0x3577, PCI_ANY_ID, PCI_ANY_ID, quirk_pipea_force }, /* Lenovo U160 cannot use SSC on LVDS */ { 0x0046, 0x17aa, 0x3920, quirk_ssc_force_disable }, -- cgit v0.10.2 From a37b9b349ef26cb44f0e8d59c3efbcd9485018b4 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Sun, 12 Aug 2012 19:27:09 +0200 Subject: drm/i915/ns2501: kill pll A enabling hack With the pipe A quirk properly fixed up for i830M, this shouldn't be required any longer. Reviewed-by: Chris Wilson Signed-Off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/dvo_ns2501.c b/drivers/gpu/drm/i915/dvo_ns2501.c index 1a0bad9..6bd383d 100644 --- a/drivers/gpu/drm/i915/dvo_ns2501.c +++ b/drivers/gpu/drm/i915/dvo_ns2501.c @@ -75,11 +75,6 @@ struct ns2501_priv { #define NSPTR(d) ((NS2501Ptr)(d->DriverPrivate.ptr)) /* - * Include the PLL launcher prototype - */ -extern void intel_enable_pll(struct drm_i915_private *dev_priv, enum pipe pipe); - -/* * For reasons unclear to me, the ns2501 at least on the Fujitsu/Siemens * laptops does not react on the i2c bus unless * both the PLL is running and the display is configured in its native @@ -113,8 +108,6 @@ static void enable_dvo(struct intel_dvo_device *dvo) I915_WRITE(DVOC_SRCDIM, 0x400300); // 1024x768 I915_WRITE(FW_BLC, 0x1080304); - intel_enable_pll(dev_priv, 0); - I915_WRITE(DVOC, 0x90004084); } diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index dd760f4..a55f664 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -1432,7 +1432,7 @@ static void assert_pch_ports_disabled(struct drm_i915_private *dev_priv, * * Unfortunately needed by dvo_ns2501 since the dvo depends on it running. */ -void intel_enable_pll(struct drm_i915_private *dev_priv, enum pipe pipe) +static void intel_enable_pll(struct drm_i915_private *dev_priv, enum pipe pipe) { int reg; u32 val; -- cgit v0.10.2 From 6306cb4f809ccf92c1b7bf446635c571e33f5151 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Sun, 12 Aug 2012 19:27:10 +0200 Subject: drm/i915: rip out the overlay pipe A workaround Now that all affected i830M systems have the pipe A quirk set, we don't need to do any special dances in the overlay code any longer. And reading through the code I'm rather dubios that it actually does what it claims to do ... As a nice benefit this rips out a users of the crtc helper dpms callback. v2: As suggested by Chris Wilson, replace the code by an appropriate WARN to ensure that the pipe A is indeed running. Reviewed-by: Chris Wilson Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/intel_overlay.c b/drivers/gpu/drm/i915/intel_overlay.c index 830d0dd..c0f4858 100644 --- a/drivers/gpu/drm/i915/intel_overlay.c +++ b/drivers/gpu/drm/i915/intel_overlay.c @@ -235,54 +235,6 @@ static int intel_overlay_do_wait_request(struct intel_overlay *overlay, return 0; } -/* Workaround for i830 bug where pipe a must be enable to change control regs */ -static int -i830_activate_pipe_a(struct drm_device *dev) -{ - drm_i915_private_t *dev_priv = dev->dev_private; - struct intel_crtc *crtc; - struct drm_crtc_helper_funcs *crtc_funcs; - struct drm_display_mode vesa_640x480 = { - DRM_MODE("640x480", DRM_MODE_TYPE_DRIVER, 25175, 640, 656, - 752, 800, 0, 480, 489, 492, 525, 0, - DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) - }, *mode; - - crtc = to_intel_crtc(dev_priv->pipe_to_crtc_mapping[0]); - if (crtc->dpms_mode == DRM_MODE_DPMS_ON) - return 0; - - /* most i8xx have pipe a forced on, so don't trust dpms mode */ - if (I915_READ(_PIPEACONF) & PIPECONF_ENABLE) - return 0; - - crtc_funcs = crtc->base.helper_private; - if (crtc_funcs->dpms == NULL) - return 0; - - DRM_DEBUG_DRIVER("Enabling pipe A in order to enable overlay\n"); - - mode = drm_mode_duplicate(dev, &vesa_640x480); - - if (!drm_crtc_helper_set_mode(&crtc->base, mode, - crtc->base.x, crtc->base.y, - crtc->base.fb)) - return 0; - - crtc_funcs->dpms(&crtc->base, DRM_MODE_DPMS_ON); - return 1; -} - -static void -i830_deactivate_pipe_a(struct drm_device *dev) -{ - drm_i915_private_t *dev_priv = dev->dev_private; - struct drm_crtc *crtc = dev_priv->pipe_to_crtc_mapping[0]; - struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private; - - crtc_funcs->dpms(crtc, DRM_MODE_DPMS_OFF); -} - /* overlay needs to be disable in OCMD reg */ static int intel_overlay_on(struct intel_overlay *overlay) { @@ -290,17 +242,12 @@ static int intel_overlay_on(struct intel_overlay *overlay) struct drm_i915_private *dev_priv = dev->dev_private; struct intel_ring_buffer *ring = &dev_priv->ring[RCS]; struct drm_i915_gem_request *request; - int pipe_a_quirk = 0; int ret; BUG_ON(overlay->active); overlay->active = 1; - if (IS_I830(dev)) { - pipe_a_quirk = i830_activate_pipe_a(dev); - if (pipe_a_quirk < 0) - return pipe_a_quirk; - } + WARN_ON(IS_I830(dev) && !(dev_priv->quirks & QUIRK_PIPEA_FORCE)); request = kzalloc(sizeof(*request), GFP_KERNEL); if (request == NULL) { @@ -322,9 +269,6 @@ static int intel_overlay_on(struct intel_overlay *overlay) ret = intel_overlay_do_wait_request(overlay, request, NULL); out: - if (pipe_a_quirk) - i830_deactivate_pipe_a(dev); - return ret; } -- cgit v0.10.2 From 24218aacac65f5b731bbff8eeb09ea7847dda2ef Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Sun, 12 Aug 2012 19:27:11 +0200 Subject: drm/i915: prepare load-detect pipe code for dpms changes A few things need adjustement: - Change the dpms state by calling the dpms connector function and not some crtc helper internal callbacks. Otherwise this will break once we switch to our own dpms handling. - Instead of tracking and restoring intel_crtc->dpms_mode use the connector's dpms variable - the former relies on the dpms compuation rules used by the crtc helper. And it would break when the encoder is cloned and the other output has a different dpms state. But luckily no one is crazy enough for that. - Properly clear the connector -> encoder -> crtc linking, even when failing (note that the crtc helper removes the encoder -> crtc link in disabled_unused_functions for us). Reviewed-by: Chris Wilson Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index a55f664..f851474 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -5615,21 +5615,12 @@ bool intel_get_load_detect_pipe(struct intel_encoder *intel_encoder, if (encoder->crtc) { crtc = encoder->crtc; - intel_crtc = to_intel_crtc(crtc); - old->dpms_mode = intel_crtc->dpms_mode; + old->dpms_mode = connector->dpms; old->load_detect_temp = false; /* Make sure the crtc and connector are running */ - if (intel_crtc->dpms_mode != DRM_MODE_DPMS_ON) { - struct drm_encoder_helper_funcs *encoder_funcs; - struct drm_crtc_helper_funcs *crtc_funcs; - - crtc_funcs = crtc->helper_private; - crtc_funcs->dpms(crtc, DRM_MODE_DPMS_ON); - - encoder_funcs = encoder->helper_private; - encoder_funcs->dpms(encoder, DRM_MODE_DPMS_ON); - } + if (connector->dpms != DRM_MODE_DPMS_ON) + connector->funcs->dpms(connector, DRM_MODE_DPMS_ON); return true; } @@ -5657,7 +5648,7 @@ bool intel_get_load_detect_pipe(struct intel_encoder *intel_encoder, connector->encoder = encoder; intel_crtc = to_intel_crtc(crtc); - old->dpms_mode = intel_crtc->dpms_mode; + old->dpms_mode = connector->dpms; old->load_detect_temp = true; old->release_fb = NULL; @@ -5682,22 +5673,25 @@ bool intel_get_load_detect_pipe(struct intel_encoder *intel_encoder, DRM_DEBUG_KMS("reusing fbdev for load-detection framebuffer\n"); if (IS_ERR(crtc->fb)) { DRM_DEBUG_KMS("failed to allocate framebuffer for load-detection\n"); - crtc->fb = old_fb; - return false; + goto fail; } if (!drm_crtc_helper_set_mode(crtc, mode, 0, 0, old_fb)) { DRM_DEBUG_KMS("failed to set mode on load-detect pipe\n"); if (old->release_fb) old->release_fb->funcs->destroy(old->release_fb); - crtc->fb = old_fb; - return false; + goto fail; } /* let the connector get through one full cycle before testing */ intel_wait_for_vblank(dev, intel_crtc->pipe); return true; +fail: + connector->encoder = NULL; + encoder->crtc = NULL; + crtc->fb = old_fb; + return false; } void intel_release_load_detect_pipe(struct intel_encoder *intel_encoder, @@ -5706,9 +5700,6 @@ void intel_release_load_detect_pipe(struct intel_encoder *intel_encoder, { struct drm_encoder *encoder = &intel_encoder->base; struct drm_device *dev = encoder->dev; - struct drm_crtc *crtc = encoder->crtc; - struct drm_encoder_helper_funcs *encoder_funcs = encoder->helper_private; - struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private; DRM_DEBUG_KMS("[CONNECTOR:%d:%s], [ENCODER:%d:%s]\n", connector->base.id, drm_get_connector_name(connector), @@ -5716,6 +5707,7 @@ void intel_release_load_detect_pipe(struct intel_encoder *intel_encoder, if (old->load_detect_temp) { connector->encoder = NULL; + encoder->crtc = NULL; drm_helper_disable_unused_functions(dev); if (old->release_fb) @@ -5725,10 +5717,8 @@ void intel_release_load_detect_pipe(struct intel_encoder *intel_encoder, } /* Switch crtc and encoder back off if necessary */ - if (old->dpms_mode != DRM_MODE_DPMS_ON) { - encoder_funcs->dpms(encoder, old->dpms_mode); - crtc_funcs->dpms(crtc, old->dpms_mode); - } + if (old->dpms_mode != DRM_MODE_DPMS_ON) + connector->funcs->dpms(connector, old->dpms_mode); } /* Returns the clock of the currently programmed mode of the given pipe. */ -- cgit v0.10.2 From d2434ab7fb085ac6848acd9d82366f96a642e471 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Sun, 12 Aug 2012 21:20:10 +0200 Subject: drm/i915: drop intel_encoder argument to load_detect_pipe functions Since it's redundant - we can get the attached encoder in the functions themselves. Reviewed-by: Chris Wilson Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/intel_crt.c b/drivers/gpu/drm/i915/intel_crt.c index bc5e2c9..62b6555 100644 --- a/drivers/gpu/drm/i915/intel_crt.c +++ b/drivers/gpu/drm/i915/intel_crt.c @@ -517,14 +517,12 @@ intel_crt_detect(struct drm_connector *connector, bool force) return connector->status; /* for pre-945g platforms use load detect */ - if (intel_get_load_detect_pipe(&crt->base, connector, NULL, - &tmp)) { + if (intel_get_load_detect_pipe(connector, NULL, &tmp)) { if (intel_crt_detect_ddc(connector)) status = connector_status_connected; else status = intel_crt_load_detect(crt); - intel_release_load_detect_pipe(&crt->base, connector, - &tmp); + intel_release_load_detect_pipe(connector, &tmp); } else status = connector_status_unknown; diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index f851474..62524b0 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -5584,12 +5584,13 @@ mode_fits_in_fbdev(struct drm_device *dev, return fb; } -bool intel_get_load_detect_pipe(struct intel_encoder *intel_encoder, - struct drm_connector *connector, +bool intel_get_load_detect_pipe(struct drm_connector *connector, struct drm_display_mode *mode, struct intel_load_detect_pipe *old) { struct intel_crtc *intel_crtc; + struct intel_encoder *intel_encoder = + intel_attached_encoder(connector); struct drm_crtc *possible_crtc; struct drm_encoder *encoder = &intel_encoder->base; struct drm_crtc *crtc = NULL; @@ -5694,10 +5695,11 @@ fail: return false; } -void intel_release_load_detect_pipe(struct intel_encoder *intel_encoder, - struct drm_connector *connector, +void intel_release_load_detect_pipe(struct drm_connector *connector, struct intel_load_detect_pipe *old) { + struct intel_encoder *intel_encoder = + intel_attached_encoder(connector); struct drm_encoder *encoder = &intel_encoder->base; struct drm_device *dev = encoder->dev; diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index 6920bc2..95f635b 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -433,12 +433,10 @@ struct intel_load_detect_pipe { bool load_detect_temp; int dpms_mode; }; -extern bool intel_get_load_detect_pipe(struct intel_encoder *intel_encoder, - struct drm_connector *connector, +extern bool intel_get_load_detect_pipe(struct drm_connector *connector, struct drm_display_mode *mode, struct intel_load_detect_pipe *old); -extern void intel_release_load_detect_pipe(struct intel_encoder *intel_encoder, - struct drm_connector *connector, +extern void intel_release_load_detect_pipe(struct drm_connector *connector, struct intel_load_detect_pipe *old); extern void intelfb_restore(void); diff --git a/drivers/gpu/drm/i915/intel_tv.c b/drivers/gpu/drm/i915/intel_tv.c index 1a0bab0..95653a5 100644 --- a/drivers/gpu/drm/i915/intel_tv.c +++ b/drivers/gpu/drm/i915/intel_tv.c @@ -1303,12 +1303,9 @@ intel_tv_detect(struct drm_connector *connector, bool force) if (force) { struct intel_load_detect_pipe tmp; - if (intel_get_load_detect_pipe(&intel_tv->base, connector, - &mode, &tmp)) { + if (intel_get_load_detect_pipe(connector, &mode, &tmp)) { type = intel_tv_detect_type(intel_tv, connector); - intel_release_load_detect_pipe(&intel_tv->base, - connector, - &tmp); + intel_release_load_detect_pipe(connector, &tmp); } else return connector_status_unknown; } else -- cgit v0.10.2 From fac3274c4ee090140410b2f29d3fbd8f10eae186 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Sun, 12 Aug 2012 19:27:12 +0200 Subject: drm/i915: simplify dvo dpms interface All dvo drivers only support 2 dpms states, and our dvo driver even switches of the dvo port for anything else than DPMS_ON. Hence ditch this complexity and simply use bool enable. While reading through this code I've noticed that the mode_set function of ch7017 is a bit peculiar - it disable the lvds again, even though the crtc helper code should have done that ... This might be to work around an issue at driver load, we pretty much ignore the hw state when taking over. v2: Also do the conversion for the new ns2501 driver. Reviewed-by: Chris Wilson Signed-Off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/dvo.h b/drivers/gpu/drm/i915/dvo.h index 0c8ac4d..0fa839e 100644 --- a/drivers/gpu/drm/i915/dvo.h +++ b/drivers/gpu/drm/i915/dvo.h @@ -58,13 +58,12 @@ struct intel_dvo_dev_ops { void (*create_resources)(struct intel_dvo_device *dvo); /* - * Turn on/off output or set intermediate power levels if available. + * Turn on/off output. * - * Unsupported intermediate modes drop to the lower power setting. - * If the mode is DPMSModeOff, the output must be disabled, - * as the DPLL may be disabled afterwards. + * Because none of our dvo drivers support an intermediate power levels, + * we don't expose this in the interfac. */ - void (*dpms)(struct intel_dvo_device *dvo, int mode); + void (*dpms)(struct intel_dvo_device *dvo, bool enable); /* * Callback for testing a video mode for a given output. diff --git a/drivers/gpu/drm/i915/dvo_ch7017.c b/drivers/gpu/drm/i915/dvo_ch7017.c index 1ca799a..71e7650 100644 --- a/drivers/gpu/drm/i915/dvo_ch7017.c +++ b/drivers/gpu/drm/i915/dvo_ch7017.c @@ -163,7 +163,7 @@ struct ch7017_priv { }; static void ch7017_dump_regs(struct intel_dvo_device *dvo); -static void ch7017_dpms(struct intel_dvo_device *dvo, int mode); +static void ch7017_dpms(struct intel_dvo_device *dvo, bool enable); static bool ch7017_read(struct intel_dvo_device *dvo, u8 addr, u8 *val) { @@ -309,7 +309,7 @@ static void ch7017_mode_set(struct intel_dvo_device *dvo, lvds_power_down = CH7017_LVDS_POWER_DOWN_DEFAULT_RESERVED | (mode->hdisplay & 0x0700) >> 8; - ch7017_dpms(dvo, DRM_MODE_DPMS_OFF); + ch7017_dpms(dvo, false); ch7017_write(dvo, CH7017_HORIZONTAL_ACTIVE_PIXEL_INPUT, horizontal_active_pixel_input); ch7017_write(dvo, CH7017_HORIZONTAL_ACTIVE_PIXEL_OUTPUT, @@ -331,7 +331,7 @@ static void ch7017_mode_set(struct intel_dvo_device *dvo, } /* set the CH7017 power state */ -static void ch7017_dpms(struct intel_dvo_device *dvo, int mode) +static void ch7017_dpms(struct intel_dvo_device *dvo, bool enable) { uint8_t val; @@ -345,7 +345,7 @@ static void ch7017_dpms(struct intel_dvo_device *dvo, int mode) CH7017_DAC3_POWER_DOWN | CH7017_TV_POWER_DOWN_EN); - if (mode == DRM_MODE_DPMS_ON) { + if (enable) { /* Turn on the LVDS */ ch7017_write(dvo, CH7017_LVDS_POWER_DOWN, val & ~CH7017_LVDS_POWER_DOWN_EN); diff --git a/drivers/gpu/drm/i915/dvo_ch7xxx.c b/drivers/gpu/drm/i915/dvo_ch7xxx.c index 4a03660..c1dea5b 100644 --- a/drivers/gpu/drm/i915/dvo_ch7xxx.c +++ b/drivers/gpu/drm/i915/dvo_ch7xxx.c @@ -289,9 +289,9 @@ static void ch7xxx_mode_set(struct intel_dvo_device *dvo, } /* set the CH7xxx power state */ -static void ch7xxx_dpms(struct intel_dvo_device *dvo, int mode) +static void ch7xxx_dpms(struct intel_dvo_device *dvo, bool enable) { - if (mode == DRM_MODE_DPMS_ON) + if (enable) ch7xxx_writeb(dvo, CH7xxx_PM, CH7xxx_PM_DVIL | CH7xxx_PM_DVIP); else ch7xxx_writeb(dvo, CH7xxx_PM, CH7xxx_PM_FPD); diff --git a/drivers/gpu/drm/i915/dvo_ivch.c b/drivers/gpu/drm/i915/dvo_ivch.c index 04f2893..fa8ff6b 100644 --- a/drivers/gpu/drm/i915/dvo_ivch.c +++ b/drivers/gpu/drm/i915/dvo_ivch.c @@ -288,7 +288,7 @@ static enum drm_mode_status ivch_mode_valid(struct intel_dvo_device *dvo, } /** Sets the power state of the panel connected to the ivch */ -static void ivch_dpms(struct intel_dvo_device *dvo, int mode) +static void ivch_dpms(struct intel_dvo_device *dvo, bool enable) { int i; uint16_t vr01, vr30, backlight; @@ -297,13 +297,13 @@ static void ivch_dpms(struct intel_dvo_device *dvo, int mode) if (!ivch_read(dvo, VR01, &vr01)) return; - if (mode == DRM_MODE_DPMS_ON) + if (enable) backlight = 1; else backlight = 0; ivch_write(dvo, VR80, backlight); - if (mode == DRM_MODE_DPMS_ON) + if (enable) vr01 |= VR01_LCD_ENABLE | VR01_DVO_ENABLE; else vr01 &= ~(VR01_LCD_ENABLE | VR01_DVO_ENABLE); @@ -315,7 +315,7 @@ static void ivch_dpms(struct intel_dvo_device *dvo, int mode) if (!ivch_read(dvo, VR30, &vr30)) break; - if (((vr30 & VR30_PANEL_ON) != 0) == (mode == DRM_MODE_DPMS_ON)) + if (((vr30 & VR30_PANEL_ON) != 0) == enable) break; udelay(1000); } diff --git a/drivers/gpu/drm/i915/dvo_ns2501.c b/drivers/gpu/drm/i915/dvo_ns2501.c index 6bd383d..c4d9f2f 100644 --- a/drivers/gpu/drm/i915/dvo_ns2501.c +++ b/drivers/gpu/drm/i915/dvo_ns2501.c @@ -493,19 +493,19 @@ static void ns2501_mode_set(struct intel_dvo_device *dvo, } /* set the NS2501 power state */ -static void ns2501_dpms(struct intel_dvo_device *dvo, int mode) +static void ns2501_dpms(struct intel_dvo_device *dvo, bool enable) { bool ok; bool restore = false; struct ns2501_priv *ns = (struct ns2501_priv *)(dvo->dev_priv); unsigned char ch; - DRM_DEBUG_KMS("%s: Trying set the dpms of the DVO to %d\n", - __FUNCTION__, mode); + DRM_DEBUG_KMS("%s: Trying set the dpms of the DVO to %i\n", + __FUNCTION__, enable); ch = ns->reg_8_shadow; - if (mode == DRM_MODE_DPMS_ON) + if (enable) ch |= NS2501_8_PD; else ch &= ~NS2501_8_PD; @@ -519,12 +519,10 @@ static void ns2501_dpms(struct intel_dvo_device *dvo, int mode) ok &= ns2501_writeb(dvo, NS2501_REG8, ch); ok &= ns2501_writeb(dvo, 0x34, - (mode == - DRM_MODE_DPMS_ON) ? (0x03) : (0x00)); + enable ? 0x03 : 0x00); ok &= ns2501_writeb(dvo, 0x35, - (mode == - DRM_MODE_DPMS_ON) ? (0xff) : (0x00)); + enable ? 0xff : 0x00); if (!ok) { if (restore) restore_dvo(dvo); diff --git a/drivers/gpu/drm/i915/dvo_sil164.c b/drivers/gpu/drm/i915/dvo_sil164.c index a0b13a6..cc24c1c 100644 --- a/drivers/gpu/drm/i915/dvo_sil164.c +++ b/drivers/gpu/drm/i915/dvo_sil164.c @@ -208,7 +208,7 @@ static void sil164_mode_set(struct intel_dvo_device *dvo, } /* set the SIL164 power state */ -static void sil164_dpms(struct intel_dvo_device *dvo, int mode) +static void sil164_dpms(struct intel_dvo_device *dvo, bool enable) { int ret; unsigned char ch; @@ -217,7 +217,7 @@ static void sil164_dpms(struct intel_dvo_device *dvo, int mode) if (ret == false) return; - if (mode == DRM_MODE_DPMS_ON) + if (enable) ch |= SIL164_8_PD; else ch &= ~SIL164_8_PD; diff --git a/drivers/gpu/drm/i915/dvo_tfp410.c b/drivers/gpu/drm/i915/dvo_tfp410.c index aa2cd3e..097b3e8 100644 --- a/drivers/gpu/drm/i915/dvo_tfp410.c +++ b/drivers/gpu/drm/i915/dvo_tfp410.c @@ -234,14 +234,14 @@ static void tfp410_mode_set(struct intel_dvo_device *dvo, } /* set the tfp410 power state */ -static void tfp410_dpms(struct intel_dvo_device *dvo, int mode) +static void tfp410_dpms(struct intel_dvo_device *dvo, bool enable) { uint8_t ctl1; if (!tfp410_readb(dvo, TFP410_CTL_1, &ctl1)) return; - if (mode == DRM_MODE_DPMS_ON) + if (enable) ctl1 |= TFP410_CTL_1_PD; else ctl1 &= ~TFP410_CTL_1_PD; diff --git a/drivers/gpu/drm/i915/intel_dvo.c b/drivers/gpu/drm/i915/intel_dvo.c index 03dfdff..227551f 100644 --- a/drivers/gpu/drm/i915/intel_dvo.c +++ b/drivers/gpu/drm/i915/intel_dvo.c @@ -115,9 +115,9 @@ static void intel_dvo_dpms(struct drm_encoder *encoder, int mode) if (mode == DRM_MODE_DPMS_ON) { I915_WRITE(dvo_reg, temp | DVO_ENABLE); I915_READ(dvo_reg); - intel_dvo->dev.dev_ops->dpms(&intel_dvo->dev, mode); + intel_dvo->dev.dev_ops->dpms(&intel_dvo->dev, true); } else { - intel_dvo->dev.dev_ops->dpms(&intel_dvo->dev, mode); + intel_dvo->dev.dev_ops->dpms(&intel_dvo->dev, false); I915_WRITE(dvo_reg, temp & ~DVO_ENABLE); I915_READ(dvo_reg); } -- cgit v0.10.2 From 5d985ac81ab15631570cad83d187565c7fcfd00a Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Sun, 12 Aug 2012 19:27:13 +0200 Subject: drm/i915: kill a few unused things in dev_priv ... and move a few others only used by i915_dma.c into the dri1 dungeon. Reviewed-by: Chris Wilson Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/i915_dma.c b/drivers/gpu/drm/i915/i915_dma.c index 0a1b64f..2285ae3 100644 --- a/drivers/gpu/drm/i915/i915_dma.c +++ b/drivers/gpu/drm/i915/i915_dma.c @@ -235,10 +235,10 @@ static int i915_initialize(struct drm_device * dev, drm_i915_init_t * init) } } - dev_priv->cpp = init->cpp; - dev_priv->back_offset = init->back_offset; - dev_priv->front_offset = init->front_offset; - dev_priv->current_page = 0; + dev_priv->dri1.cpp = init->cpp; + dev_priv->dri1.back_offset = init->back_offset; + dev_priv->dri1.front_offset = init->front_offset; + dev_priv->dri1.current_page = 0; if (master_priv->sarea_priv) master_priv->sarea_priv->pf_current_page = 0; @@ -575,7 +575,7 @@ static int i915_dispatch_flip(struct drm_device * dev) DRM_DEBUG_DRIVER("%s: page=%d pfCurrentPage=%d\n", __func__, - dev_priv->current_page, + dev_priv->dri1.current_page, master_priv->sarea_priv->pf_current_page); i915_kernel_lost_context(dev); @@ -589,12 +589,12 @@ static int i915_dispatch_flip(struct drm_device * dev) OUT_RING(CMD_OP_DISPLAYBUFFER_INFO | ASYNC_FLIP); OUT_RING(0); - if (dev_priv->current_page == 0) { - OUT_RING(dev_priv->back_offset); - dev_priv->current_page = 1; + if (dev_priv->dri1.current_page == 0) { + OUT_RING(dev_priv->dri1.back_offset); + dev_priv->dri1.current_page = 1; } else { - OUT_RING(dev_priv->front_offset); - dev_priv->current_page = 0; + OUT_RING(dev_priv->dri1.front_offset); + dev_priv->dri1.current_page = 0; } OUT_RING(0); @@ -613,7 +613,7 @@ static int i915_dispatch_flip(struct drm_device * dev) ADVANCE_LP_RING(); } - master_priv->sarea_priv->pf_current_page = dev_priv->current_page; + master_priv->sarea_priv->pf_current_page = dev_priv->dri1.current_page; return 0; } diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 261fe21..ed3ba70 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -428,12 +428,6 @@ typedef struct drm_i915_private { struct resource mch_res; - unsigned int cpp; - int back_offset; - int front_offset; - int current_page; - int page_flipping; - atomic_t irq_received; /* protects the irq masks */ @@ -451,7 +445,6 @@ typedef struct drm_i915_private { u32 hotplug_supported_mask; struct work_struct hotplug_work; - unsigned int sr01, adpa, ppcr, dvob, dvoc, lvds; int num_pipe; int num_pch_pll; @@ -790,6 +783,12 @@ typedef struct drm_i915_private { struct { unsigned allow_batchbuffer : 1; u32 __iomem *gfx_hws_cpu_addr; + + unsigned int cpp; + int back_offset; + int front_offset; + int current_page; + int page_flipping; } dri1; /* Kernel Modesetting */ -- cgit v0.10.2 From 88cefb6c60712982f739f6bbd9f82968aca46033 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Sun, 12 Aug 2012 19:27:14 +0200 Subject: drm/i915: extract ironlake_fdi_pll_disable Simply to make the ilk+ crtc disable path clearer and more symmetric with the enable function. Also switch to intel_crtc for the enable function. Reviewed-by: Chris Wilson Signed-Off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 62524b0..b580d94 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -2709,11 +2709,10 @@ static void ivb_manual_fdi_link_train(struct drm_crtc *crtc) DRM_DEBUG_KMS("FDI train done.\n"); } -static void ironlake_fdi_pll_enable(struct drm_crtc *crtc) +static void ironlake_fdi_pll_enable(struct intel_crtc *intel_crtc) { - struct drm_device *dev = crtc->dev; + struct drm_device *dev = intel_crtc->base.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; @@ -2754,6 +2753,35 @@ static void ironlake_fdi_pll_enable(struct drm_crtc *crtc) } } +static void ironlake_fdi_pll_disable(struct intel_crtc *intel_crtc) +{ + struct drm_device *dev = intel_crtc->base.dev; + struct drm_i915_private *dev_priv = dev->dev_private; + int pipe = intel_crtc->pipe; + u32 reg, temp; + + /* Switch from PCDclk to Rawclk */ + reg = FDI_RX_CTL(pipe); + temp = I915_READ(reg); + I915_WRITE(reg, temp & ~FDI_PCDCLK); + + /* Disable CPU FDI TX PLL */ + reg = FDI_TX_CTL(pipe); + temp = I915_READ(reg); + I915_WRITE(reg, temp & ~FDI_TX_PLL_ENABLE); + + POSTING_READ(reg); + udelay(100); + + reg = FDI_RX_CTL(pipe); + temp = I915_READ(reg); + I915_WRITE(reg, temp & ~FDI_RX_PLL_ENABLE); + + /* Wait for the clocks to turn off. */ + POSTING_READ(reg); + udelay(100); +} + static void cpt_phase_pointer_disable(struct drm_device *dev, int pipe) { struct drm_i915_private *dev_priv = dev->dev_private; @@ -3201,7 +3229,7 @@ static void ironlake_crtc_enable(struct drm_crtc *crtc) is_pch_port = intel_crtc_driving_pch(crtc); if (is_pch_port) - ironlake_fdi_pll_enable(crtc); + ironlake_fdi_pll_enable(intel_crtc); else ironlake_fdi_disable(crtc); @@ -3304,26 +3332,7 @@ static void ironlake_crtc_disable(struct drm_crtc *crtc) /* disable PCH DPLL */ intel_disable_pch_pll(intel_crtc); - /* Switch from PCDclk to Rawclk */ - reg = FDI_RX_CTL(pipe); - temp = I915_READ(reg); - I915_WRITE(reg, temp & ~FDI_PCDCLK); - - /* Disable CPU FDI TX PLL */ - reg = FDI_TX_CTL(pipe); - temp = I915_READ(reg); - I915_WRITE(reg, temp & ~FDI_TX_PLL_ENABLE); - - POSTING_READ(reg); - udelay(100); - - reg = FDI_RX_CTL(pipe); - temp = I915_READ(reg); - I915_WRITE(reg, temp & ~FDI_RX_PLL_ENABLE); - - /* Wait for the clocks to turn off. */ - POSTING_READ(reg); - udelay(100); + ironlake_fdi_pll_disable(intel_crtc); intel_crtc->active = false; intel_update_watermarks(dev); -- cgit v0.10.2 From 9a78b6cce5ebb04ae1b2f99b6c9f5fa54a3428b0 Mon Sep 17 00:00:00 2001 From: Wang Xingchao Date: Thu, 9 Aug 2012 16:52:15 +0800 Subject: drm/i915: HSW audio registers definition Add hsw audio registers definition Signed-off-by: Wang Xingchao Reviewed-by: Imre Deak Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h index 321ae72..cbddc7d 100644 --- a/drivers/gpu/drm/i915/i915_reg.h +++ b/drivers/gpu/drm/i915/i915_reg.h @@ -4277,6 +4277,53 @@ #define AUD_CONFIG_PIXEL_CLOCK_HDMI (0xf << 16) #define AUD_CONFIG_DISABLE_NCTS (1 << 3) +/* HSW Audio */ +#define HSW_AUD_CONFIG_A 0x65000 /* Audio Configuration Transcoder A */ +#define HSW_AUD_CONFIG_B 0x65100 /* Audio Configuration Transcoder B */ +#define HSW_AUD_CFG(pipe) _PIPE(pipe, \ + HSW_AUD_CONFIG_A, \ + HSW_AUD_CONFIG_B) + +#define HSW_AUD_MISC_CTRL_A 0x65010 /* Audio Misc Control Convert 1 */ +#define HSW_AUD_MISC_CTRL_B 0x65110 /* Audio Misc Control Convert 2 */ +#define HSW_AUD_MISC_CTRL(pipe) _PIPE(pipe, \ + HSW_AUD_MISC_CTRL_A, \ + HSW_AUD_MISC_CTRL_B) + +#define HSW_AUD_DIP_ELD_CTRL_ST_A 0x650b4 /* Audio DIP and ELD Control State Transcoder A */ +#define HSW_AUD_DIP_ELD_CTRL_ST_B 0x651b4 /* Audio DIP and ELD Control State Transcoder B */ +#define HSW_AUD_DIP_ELD_CTRL(pipe) _PIPE(pipe, \ + HSW_AUD_DIP_ELD_CTRL_ST_A, \ + HSW_AUD_DIP_ELD_CTRL_ST_B) + +/* Audio Digital Converter */ +#define HSW_AUD_DIG_CNVT_1 0x65080 /* Audio Converter 1 */ +#define HSW_AUD_DIG_CNVT_2 0x65180 /* Audio Converter 1 */ +#define AUD_DIG_CNVT(pipe) _PIPE(pipe, \ + HSW_AUD_DIG_CNVT_1, \ + HSW_AUD_DIG_CNVT_2) + +#define HSW_AUD_EDID_DATA_A 0x65050 +#define HSW_AUD_EDID_DATA_B 0x65150 +#define HSW_AUD_EDID_DATA(pipe) _PIPE(pipe, \ + HSW_AUD_EDID_DATA_A, \ + HSW_AUD_EDID_DATA_B) + +#define HSW_AUD_PIPE_CONV_CFG 0x6507c /* Audio pipe and converter configs */ +#define HSW_AUD_PIN_ELD_CP_VLD 0x650c0 /* Audio ELD and CP Ready Status */ +#define AUDIO_INACTIVE_C (1<<11) +#define AUDIO_INACTIVE_B (1<<7) +#define AUDIO_INACTIVE_A (1<<3) +#define AUDIO_OUTPUT_ENABLE_A (1<<2) +#define AUDIO_OUTPUT_ENABLE_B (1<<6) +#define AUDIO_OUTPUT_ENABLE_C (1<<10) +#define AUDIO_ELD_VALID_A (1<<0) +#define AUDIO_ELD_VALID_B (1<<4) +#define AUDIO_ELD_VALID_C (1<<8) +#define AUDIO_CP_READY_A (1<<1) +#define AUDIO_CP_READY_B (1<<5) +#define AUDIO_CP_READY_C (1<<9) + /* HSW Power Wells */ #define HSW_PWR_WELL_CTL1 0x45400 /* BIOS */ #define HSW_PWR_WELL_CTL2 0x45404 /* Driver */ -- cgit v0.10.2 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 diff --git a/drivers/gpu/drm/i915/intel_ddi.c b/drivers/gpu/drm/i915/intel_ddi.c index 9584226..170e386 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 v0.10.2 From 9b138a83675364366388af8b6986b2e52be36bcc Mon Sep 17 00:00:00 2001 From: Wang Xingchao Date: Thu, 9 Aug 2012 16:52:18 +0800 Subject: drm/i915: ironlake_write_eld code cleanup Use _PIPE macro to get correct register definition for IBX/CPT, discard old variable "i" way. Signed-off-by: Wang Xingchao Reviewed-by: Imre Deak [danvet: Added the DIP_PORT_SEL #define from a preceeding patch in the series that needs more work.] Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h index cbddc7d..fd6a26a 100644 --- a/drivers/gpu/drm/i915/i915_reg.h +++ b/drivers/gpu/drm/i915/i915_reg.h @@ -4247,7 +4247,15 @@ #define G4X_HDMIW_HDMIEDID 0x6210C #define IBX_HDMIW_HDMIEDID_A 0xE2050 +#define IBX_HDMIW_HDMIEDID_B 0xE2150 +#define IBX_HDMIW_HDMIEDID(pipe) _PIPE(pipe, \ + IBX_HDMIW_HDMIEDID_A, \ + IBX_HDMIW_HDMIEDID_B) #define IBX_AUD_CNTL_ST_A 0xE20B4 +#define IBX_AUD_CNTL_ST_B 0xE21B4 +#define IBX_AUD_CNTL_ST(pipe) _PIPE(pipe, \ + IBX_AUD_CNTL_ST_A, \ + IBX_AUD_CNTL_ST_B) #define IBX_ELD_BUFFER_SIZE (0x1f << 10) #define IBX_ELD_ADDRESS (0x1f << 5) #define IBX_ELD_ACK (1 << 4) @@ -4256,7 +4264,15 @@ #define IBX_CP_READYB (1 << 1) #define CPT_HDMIW_HDMIEDID_A 0xE5050 +#define CPT_HDMIW_HDMIEDID_B 0xE5150 +#define CPT_HDMIW_HDMIEDID(pipe) _PIPE(pipe, \ + CPT_HDMIW_HDMIEDID_A, \ + CPT_HDMIW_HDMIEDID_B) #define CPT_AUD_CNTL_ST_A 0xE50B4 +#define CPT_AUD_CNTL_ST_B 0xE51B4 +#define CPT_AUD_CNTL_ST(pipe) _PIPE(pipe, \ + CPT_AUD_CNTL_ST_A, \ + CPT_AUD_CNTL_ST_B) #define CPT_AUD_CNTRL_ST2 0xE50C0 /* These are the 4 32-bit write offset registers for each stream @@ -4266,7 +4282,15 @@ #define GEN7_SO_WRITE_OFFSET(n) (0x5280 + (n) * 4) #define IBX_AUD_CONFIG_A 0xe2000 +#define IBX_AUD_CONFIG_B 0xe2100 +#define IBX_AUD_CFG(pipe) _PIPE(pipe, \ + IBX_AUD_CONFIG_A, \ + IBX_AUD_CONFIG_B) #define CPT_AUD_CONFIG_A 0xe5000 +#define CPT_AUD_CONFIG_B 0xe5100 +#define CPT_AUD_CFG(pipe) _PIPE(pipe, \ + CPT_AUD_CONFIG_A, \ + CPT_AUD_CONFIG_B) #define AUD_CONFIG_N_VALUE_INDEX (1 << 29) #define AUD_CONFIG_N_PROG_ENABLE (1 << 28) #define AUD_CONFIG_UPPER_N_SHIFT 20 @@ -4302,6 +4326,7 @@ #define AUD_DIG_CNVT(pipe) _PIPE(pipe, \ HSW_AUD_DIG_CNVT_1, \ HSW_AUD_DIG_CNVT_2) +#define DIP_PORT_SEL_MASK 0x3 #define HSW_AUD_EDID_DATA_A 0x65050 #define HSW_AUD_EDID_DATA_B 0x65150 diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index b580d94..f55e024 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -5078,28 +5078,24 @@ static void ironlake_write_eld(struct drm_connector *connector, int aud_config; int aud_cntl_st; int aud_cntrl_st2; + int pipe = to_intel_crtc(crtc)->pipe; if (HAS_PCH_IBX(connector->dev)) { - hdmiw_hdmiedid = IBX_HDMIW_HDMIEDID_A; - aud_config = IBX_AUD_CONFIG_A; - aud_cntl_st = IBX_AUD_CNTL_ST_A; + hdmiw_hdmiedid = IBX_HDMIW_HDMIEDID(pipe); + aud_config = IBX_AUD_CFG(pipe); + aud_cntl_st = IBX_AUD_CNTL_ST(pipe); aud_cntrl_st2 = IBX_AUD_CNTL_ST2; } else { - hdmiw_hdmiedid = CPT_HDMIW_HDMIEDID_A; - aud_config = CPT_AUD_CONFIG_A; - aud_cntl_st = CPT_AUD_CNTL_ST_A; + hdmiw_hdmiedid = CPT_HDMIW_HDMIEDID(pipe); + aud_config = CPT_AUD_CFG(pipe); + aud_cntl_st = CPT_AUD_CNTL_ST(pipe); aud_cntrl_st2 = CPT_AUD_CNTRL_ST2; } - i = to_intel_crtc(crtc)->pipe; - hdmiw_hdmiedid += i * 0x100; - aud_cntl_st += i * 0x100; - aud_config += i * 0x100; - - DRM_DEBUG_DRIVER("ELD on pipe %c\n", pipe_name(i)); + DRM_DEBUG_DRIVER("ELD on pipe %c\n", pipe_name(pipe)); i = I915_READ(aud_cntl_st); - i = (i >> 29) & 0x3; /* DIP_Port_Select, 0x1 = PortB */ + i = (i >> 29) & DIP_PORT_SEL_MASK; /* DIP_Port_Select, 0x1 = PortB */ if (!i) { DRM_DEBUG_DRIVER("Audio directed to unknown port\n"); /* operate blindly on all ports */ -- cgit v0.10.2 From 33faad195eec0c72fec84e638623ea25652eed72 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Sun, 12 Aug 2012 18:15:48 +0200 Subject: drm/fb-helper: don't clobber output routing in setup_crtcs Yet again the too close relationship between the fb helper and the crtc helper code strikes. This time around the fb helper resets all encoder->crtc pointers to NULL before starting to set up it's own mode. Which is total bullocks, because this will clobber the existing output routing, which the new drm/i915 code depends upon to be absolutely correct. The crtc helper itself doesn't really care about that, since it disables unused encoders in a rather roundabout way anyway. Two places call drm_setup_crts: - For the initial fb config. I've auditted all current drivers, and they all allocate their encoders with kzalloc. So there's no need to clear encoder->crtc once more. - When processing hotplug events while showing the fb console. This one is a bit more tricky, but both the crtc helper code and the new drm/i915 modeset code disable encoders if their crtc is changed and that encoder isn't part of the new config. Also, both disable any disconnected outputs, too. Which only leaves encoders that are on, connected, but for some odd reason the fb helper code doesn't want to use. That would be a bug in the fb configuration selector, since it tries to light up as many outputs as possible. v2: Kill the now unused encoders variable. Acked-by: Dave Airlie Signed-Off-by: Daniel Vetter diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c index f546d1e..4ecc869 100644 --- a/drivers/gpu/drm/drm_fb_helper.c +++ b/drivers/gpu/drm/drm_fb_helper.c @@ -1230,7 +1230,6 @@ static void drm_setup_crtcs(struct drm_fb_helper *fb_helper) struct drm_device *dev = fb_helper->dev; struct drm_fb_helper_crtc **crtcs; struct drm_display_mode **modes; - struct drm_encoder *encoder; struct drm_mode_set *modeset; bool *enabled; int width, height; @@ -1241,11 +1240,6 @@ static void drm_setup_crtcs(struct drm_fb_helper *fb_helper) width = dev->mode_config.max_width; height = dev->mode_config.max_height; - /* clean out all the encoder/crtc combos */ - list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { - encoder->crtc = NULL; - } - crtcs = kcalloc(dev->mode_config.num_connector, sizeof(struct drm_fb_helper_crtc *), GFP_KERNEL); modes = kcalloc(dev->mode_config.num_connector, -- cgit v0.10.2 From 523313092abffb21bfe7cd8d37615402b2228850 Mon Sep 17 00:00:00 2001 From: Damien Lespiau Date: Wed, 15 Aug 2012 19:23:25 +0100 Subject: drm/i915: Don't hardcode the number of pipes in the error state dump New-ish devices have 3 pipes, so let's not just hardcode 2 but use the for_each_pipe() macro and make struct intel_display_error_state is big enough. V2: Also add the number of pipes emitted (Chris Wilson) Signed-off-by: Damien Lespiau Reviewed-by: Chris Wilson Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index f55e024..7a1c197 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -7234,7 +7234,7 @@ struct intel_display_error_state { u32 position; u32 base; u32 size; - } cursor[2]; + } cursor[I915_MAX_PIPES]; struct intel_pipe_error_state { u32 conf; @@ -7246,7 +7246,7 @@ struct intel_display_error_state { u32 vtotal; u32 vblank; u32 vsync; - } pipe[2]; + } pipe[I915_MAX_PIPES]; struct intel_plane_error_state { u32 control; @@ -7256,7 +7256,7 @@ struct intel_display_error_state { u32 addr; u32 surface; u32 tile_offset; - } plane[2]; + } plane[I915_MAX_PIPES]; }; struct intel_display_error_state * @@ -7270,7 +7270,7 @@ intel_display_capture_error_state(struct drm_device *dev) if (error == NULL) return NULL; - for (i = 0; i < 2; i++) { + for_each_pipe(i) { error->cursor[i].control = I915_READ(CURCNTR(i)); error->cursor[i].position = I915_READ(CURPOS(i)); error->cursor[i].base = I915_READ(CURBASE(i)); @@ -7303,9 +7303,11 @@ intel_display_print_error_state(struct seq_file *m, struct drm_device *dev, struct intel_display_error_state *error) { + drm_i915_private_t *dev_priv = dev->dev_private; int i; - for (i = 0; i < 2; i++) { + seq_printf(m, "Num Pipes: %d\n", dev_priv->num_pipe); + for_each_pipe(i) { seq_printf(m, "Pipe [%d]:\n", i); seq_printf(m, " CONF: %08x\n", error->pipe[i].conf); seq_printf(m, " SRC: %08x\n", error->pipe[i].source); -- cgit v0.10.2 From 0826874a664f9e9479d50fda23923b87a50cda0d Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Mon, 13 Aug 2012 21:34:45 -0700 Subject: drm/i915: Allow VGA on CRTC 2 This is left over from the old PLL sharing code and isn't useful now that PLLs are shared when possible. Signed-off-by: Keith Packard Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/intel_crt.c b/drivers/gpu/drm/i915/intel_crt.c index 62b6555..1d05b2d 100644 --- a/drivers/gpu/drm/i915/intel_crt.c +++ b/drivers/gpu/drm/i915/intel_crt.c @@ -662,7 +662,7 @@ void intel_crt_init(struct drm_device *dev) if (IS_HASWELL(dev)) crt->base.crtc_mask = (1 << 0); else - crt->base.crtc_mask = (1 << 0) | (1 << 1); + crt->base.crtc_mask = (1 << 0) | (1 << 1) | (1 << 2); if (IS_GEN2(dev)) connector->interlace_allowed = 0; -- cgit v0.10.2 From ec6f1bb90c9865d4a5af01b1c643bcf020d88706 Mon Sep 17 00:00:00 2001 From: Dave Airlie Date: Thu, 16 Aug 2012 10:15:34 +1000 Subject: drm/i915: implement dma buf begin_cpu_access (v2) In order for udl vmap to work properly, we need to push the object into the CPU domain before we start copying the data to the USB device. This along with the udl change avoids userspace explicit mapping to be used. v2: add a flag for userspace to query to know if Intel kernel driver can deal with the vmap flushing properly. In theory udl would need a flag also, but I intend to push the patches very close to each other and other drivers should do the right thing from the start. I've added a test to my intel-gpu-tools prime branch, however testing this is a bit messy since the only way to get udl to vmap is to rendering something. I've tested this with real code as well to make sure it works. Signed-off-by: Dave Airlie [danvet: resolved conflict, which required reallocating the PARAM number to 21.] Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/i915_dma.c b/drivers/gpu/drm/i915/i915_dma.c index 2285ae3..2cba7b4 100644 --- a/drivers/gpu/drm/i915/i915_dma.c +++ b/drivers/gpu/drm/i915/i915_dma.c @@ -1012,6 +1012,9 @@ static int i915_getparam(struct drm_device *dev, void *data, case I915_PARAM_HAS_SEMAPHORES: value = i915_semaphore_is_enabled(dev); break; + case I915_PARAM_HAS_PRIME_VMAP_FLUSH: + value = 1; + break; default: DRM_DEBUG_DRIVER("Unknown parameter %d\n", param->param); diff --git a/drivers/gpu/drm/i915/i915_gem_dmabuf.c b/drivers/gpu/drm/i915/i915_gem_dmabuf.c index aa308e1..ceaad5a 100644 --- a/drivers/gpu/drm/i915/i915_gem_dmabuf.c +++ b/drivers/gpu/drm/i915/i915_gem_dmabuf.c @@ -151,6 +151,22 @@ static int i915_gem_dmabuf_mmap(struct dma_buf *dma_buf, struct vm_area_struct * return -EINVAL; } +static int i915_gem_begin_cpu_access(struct dma_buf *dma_buf, size_t start, size_t length, enum dma_data_direction direction) +{ + struct drm_i915_gem_object *obj = dma_buf->priv; + struct drm_device *dev = obj->base.dev; + int ret; + bool write = (direction == DMA_BIDIRECTIONAL || direction == DMA_TO_DEVICE); + + ret = i915_mutex_lock_interruptible(dev); + if (ret) + return ret; + + ret = i915_gem_object_set_to_cpu_domain(obj, write); + mutex_unlock(&dev->struct_mutex); + return ret; +} + static const struct dma_buf_ops i915_dmabuf_ops = { .map_dma_buf = i915_gem_map_dma_buf, .unmap_dma_buf = i915_gem_unmap_dma_buf, @@ -162,6 +178,7 @@ static const struct dma_buf_ops i915_dmabuf_ops = { .mmap = i915_gem_dmabuf_mmap, .vmap = i915_gem_dmabuf_vmap, .vunmap = i915_gem_dmabuf_vunmap, + .begin_cpu_access = i915_gem_begin_cpu_access, }; struct dma_buf *i915_gem_prime_export(struct drm_device *dev, diff --git a/include/drm/i915_drm.h b/include/drm/i915_drm.h index d8a79bf..05e24d3 100644 --- a/include/drm/i915_drm.h +++ b/include/drm/i915_drm.h @@ -312,6 +312,7 @@ typedef struct drm_i915_irq_wait { #define I915_PARAM_HAS_ALIASING_PPGTT 18 #define I915_PARAM_HAS_WAIT_TIMEOUT 19 #define I915_PARAM_HAS_SEMAPHORES 20 +#define I915_PARAM_HAS_PRIME_VMAP_FLUSH 21 typedef struct drm_i915_getparam { int param; -- cgit v0.10.2 From 32ecd24264542328893e8649312c301150dbadc5 Mon Sep 17 00:00:00 2001 From: Dave Airlie Date: Mon, 30 Jul 2012 14:06:55 +1000 Subject: drm/udl: call begin/end cpu access at more appropriate time We need to call these before we transfer the damaged areas to the device not before/after we setup the long lived vmaps. Signed-off-by: Dave Airlie Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/udl/udl_fb.c b/drivers/gpu/drm/udl/udl_fb.c index ce9a611..b8c00ed 100644 --- a/drivers/gpu/drm/udl/udl_fb.c +++ b/drivers/gpu/drm/udl/udl_fb.c @@ -13,6 +13,7 @@ #include #include #include +#include #include "drmP.h" #include "drm.h" @@ -377,16 +378,33 @@ static int udl_user_framebuffer_dirty(struct drm_framebuffer *fb, { struct udl_framebuffer *ufb = to_udl_fb(fb); int i; + int ret = 0; if (!ufb->active_16) return 0; + if (ufb->obj->base.import_attach) { + ret = dma_buf_begin_cpu_access(ufb->obj->base.import_attach->dmabuf, + 0, ufb->obj->base.size, + DMA_FROM_DEVICE); + if (ret) + return ret; + } + for (i = 0; i < num_clips; i++) { - udl_handle_damage(ufb, clips[i].x1, clips[i].y1, + ret = udl_handle_damage(ufb, clips[i].x1, clips[i].y1, clips[i].x2 - clips[i].x1, clips[i].y2 - clips[i].y1); + if (ret) + break; } - return 0; + + if (ufb->obj->base.import_attach) { + dma_buf_end_cpu_access(ufb->obj->base.import_attach->dmabuf, + 0, ufb->obj->base.size, + DMA_FROM_DEVICE); + } + return ret; } static void udl_user_framebuffer_destroy(struct drm_framebuffer *fb) diff --git a/drivers/gpu/drm/udl/udl_gem.c b/drivers/gpu/drm/udl/udl_gem.c index 291ecc1..47b2563 100644 --- a/drivers/gpu/drm/udl/udl_gem.c +++ b/drivers/gpu/drm/udl/udl_gem.c @@ -181,11 +181,6 @@ int udl_gem_vmap(struct udl_gem_object *obj) int ret; if (obj->base.import_attach) { - ret = dma_buf_begin_cpu_access(obj->base.import_attach->dmabuf, - 0, obj->base.size, DMA_BIDIRECTIONAL); - if (ret) - return -EINVAL; - obj->vmapping = dma_buf_vmap(obj->base.import_attach->dmabuf); if (!obj->vmapping) return -ENOMEM; @@ -206,8 +201,6 @@ void udl_gem_vunmap(struct udl_gem_object *obj) { if (obj->base.import_attach) { dma_buf_vunmap(obj->base.import_attach->dmabuf, obj->vmapping); - dma_buf_end_cpu_access(obj->base.import_attach->dmabuf, 0, - obj->base.size, DMA_BIDIRECTIONAL); return; } -- cgit v0.10.2 From 83358c85866ebd2af1229fc9870b93e126690671 Mon Sep 17 00:00:00 2001 From: Wang Xingchao Date: Thu, 16 Aug 2012 22:43:37 +0800 Subject: drm/i915: Haswell HDMI audio initialization Added new haswell_write_eld() to initialize Haswell HDMI audio registers to generate an unsolicited response to the audio controller driver to indicate that the controller sequence should start. Reviewed-by: Imre Deak Signed-off-by: Wang Xingchao Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 7a1c197..42c5758 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -5066,6 +5066,91 @@ static void g4x_write_eld(struct drm_connector *connector, I915_WRITE(G4X_AUD_CNTL_ST, i); } +static void haswell_write_eld(struct drm_connector *connector, + struct drm_crtc *crtc) +{ + struct drm_i915_private *dev_priv = connector->dev->dev_private; + uint8_t *eld = connector->eld; + struct drm_device *dev = crtc->dev; + uint32_t eldv; + uint32_t i; + int len; + int pipe = to_intel_crtc(crtc)->pipe; + int tmp; + + int hdmiw_hdmiedid = HSW_AUD_EDID_DATA(pipe); + int aud_cntl_st = HSW_AUD_DIP_ELD_CTRL(pipe); + int aud_config = HSW_AUD_CFG(pipe); + int aud_cntrl_st2 = HSW_AUD_PIN_ELD_CP_VLD; + + + DRM_DEBUG_DRIVER("HDMI: Haswell Audio initialize....\n"); + + /* Audio output enable */ + DRM_DEBUG_DRIVER("HDMI audio: enable codec\n"); + tmp = I915_READ(aud_cntrl_st2); + tmp |= (AUDIO_OUTPUT_ENABLE_A << (pipe * 4)); + I915_WRITE(aud_cntrl_st2, tmp); + + /* Wait for 1 vertical blank */ + intel_wait_for_vblank(dev, pipe); + + /* Set ELD valid state */ + tmp = I915_READ(aud_cntrl_st2); + DRM_DEBUG_DRIVER("HDMI audio: pin eld vld status=0x%8x\n", tmp); + tmp |= (AUDIO_ELD_VALID_A << (pipe * 4)); + I915_WRITE(aud_cntrl_st2, tmp); + tmp = I915_READ(aud_cntrl_st2); + DRM_DEBUG_DRIVER("HDMI audio: eld vld status=0x%8x\n", tmp); + + /* Enable HDMI mode */ + tmp = I915_READ(aud_config); + DRM_DEBUG_DRIVER("HDMI audio: audio conf: 0x%8x\n", tmp); + /* clear N_programing_enable and N_value_index */ + tmp &= ~(AUD_CONFIG_N_VALUE_INDEX | AUD_CONFIG_N_PROG_ENABLE); + I915_WRITE(aud_config, tmp); + + DRM_DEBUG_DRIVER("ELD on pipe %c\n", pipe_name(pipe)); + + eldv = AUDIO_ELD_VALID_A << (pipe * 4); + + if (intel_pipe_has_type(crtc, INTEL_OUTPUT_DISPLAYPORT)) { + DRM_DEBUG_DRIVER("ELD: DisplayPort detected\n"); + eld[5] |= (1 << 2); /* Conn_Type, 0x1 = DisplayPort */ + I915_WRITE(aud_config, AUD_CONFIG_N_VALUE_INDEX); /* 0x1 = DP */ + } else + I915_WRITE(aud_config, 0); + + if (intel_eld_uptodate(connector, + aud_cntrl_st2, eldv, + aud_cntl_st, IBX_ELD_ADDRESS, + hdmiw_hdmiedid)) + return; + + i = I915_READ(aud_cntrl_st2); + i &= ~eldv; + I915_WRITE(aud_cntrl_st2, i); + + if (!eld[0]) + return; + + i = I915_READ(aud_cntl_st); + i &= ~IBX_ELD_ADDRESS; + I915_WRITE(aud_cntl_st, i); + i = (i >> 29) & DIP_PORT_SEL_MASK; /* DIP_Port_Select, 0x1 = PortB */ + DRM_DEBUG_DRIVER("port num:%d\n", i); + + len = min_t(uint8_t, eld[2], 21); /* 84 bytes of hw ELD buffer */ + DRM_DEBUG_DRIVER("ELD size %d\n", len); + for (i = 0; i < len; i++) + I915_WRITE(hdmiw_hdmiedid, *((uint32_t *)eld + i)); + + i = I915_READ(aud_cntrl_st2); + i |= eldv; + I915_WRITE(aud_cntrl_st2, i); + +} + static void ironlake_write_eld(struct drm_connector *connector, struct drm_crtc *crtc) { @@ -6936,7 +7021,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; + dev_priv->display.write_eld = haswell_write_eld; } else dev_priv->display.update_wm = NULL; } else if (IS_G4X(dev)) { -- cgit v0.10.2 From 225067eedf1f4d4065940232c9069fcb255206ee Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Mon, 20 Aug 2012 10:23:20 +0200 Subject: drm/i915: move functions around Prep work to make Chris Wilson's unbound tracking patch a bit easier to read. Alas, I'd have preferred that moving the page allocation retry loop from bind to get_pages would have been a separate patch, too. But that looks like real work ;-) Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index 0514593..0f70c2a 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -1340,6 +1340,64 @@ i915_gem_mmap_gtt_ioctl(struct drm_device *dev, void *data, return i915_gem_mmap_gtt(file, dev, args->handle, &args->offset); } +/* Immediately discard the backing storage */ +static void +i915_gem_object_truncate(struct drm_i915_gem_object *obj) +{ + struct inode *inode; + + /* Our goal here is to return as much of the memory as + * is possible back to the system as we are called from OOM. + * To do this we must instruct the shmfs to drop all of its + * backing pages, *now*. + */ + inode = obj->base.filp->f_path.dentry->d_inode; + shmem_truncate_range(inode, 0, (loff_t)-1); + + if (obj->base.map_list.map) + drm_gem_free_mmap_offset(&obj->base); + + obj->madv = __I915_MADV_PURGED; +} + +static inline int +i915_gem_object_is_purgeable(struct drm_i915_gem_object *obj) +{ + return obj->madv == I915_MADV_DONTNEED; +} + +static void +i915_gem_object_put_pages_gtt(struct drm_i915_gem_object *obj) +{ + int page_count = obj->base.size / PAGE_SIZE; + int i; + + if (!obj->pages) + return; + + BUG_ON(obj->madv == __I915_MADV_PURGED); + + if (i915_gem_object_needs_bit17_swizzle(obj)) + i915_gem_object_save_bit_17_swizzle(obj); + + if (obj->madv == I915_MADV_DONTNEED) + obj->dirty = 0; + + for (i = 0; i < page_count; i++) { + if (obj->dirty) + set_page_dirty(obj->pages[i]); + + if (obj->madv == I915_MADV_WILLNEED) + mark_page_accessed(obj->pages[i]); + + page_cache_release(obj->pages[i]); + } + obj->dirty = 0; + + drm_free_large(obj->pages); + obj->pages = NULL; +} + int i915_gem_object_get_pages_gtt(struct drm_i915_gem_object *obj, gfp_t gfpmask) @@ -1387,38 +1445,6 @@ err_pages: return PTR_ERR(page); } -static void -i915_gem_object_put_pages_gtt(struct drm_i915_gem_object *obj) -{ - int page_count = obj->base.size / PAGE_SIZE; - int i; - - if (!obj->pages) - return; - - BUG_ON(obj->madv == __I915_MADV_PURGED); - - if (i915_gem_object_needs_bit17_swizzle(obj)) - i915_gem_object_save_bit_17_swizzle(obj); - - if (obj->madv == I915_MADV_DONTNEED) - obj->dirty = 0; - - for (i = 0; i < page_count; i++) { - if (obj->dirty) - set_page_dirty(obj->pages[i]); - - if (obj->madv == I915_MADV_WILLNEED) - mark_page_accessed(obj->pages[i]); - - page_cache_release(obj->pages[i]); - } - obj->dirty = 0; - - drm_free_large(obj->pages); - obj->pages = NULL; -} - void i915_gem_object_move_to_active(struct drm_i915_gem_object *obj, struct intel_ring_buffer *ring, @@ -1486,32 +1512,6 @@ i915_gem_object_move_to_inactive(struct drm_i915_gem_object *obj) WARN_ON(i915_verify_lists(dev)); } -/* Immediately discard the backing storage */ -static void -i915_gem_object_truncate(struct drm_i915_gem_object *obj) -{ - struct inode *inode; - - /* Our goal here is to return as much of the memory as - * is possible back to the system as we are called from OOM. - * To do this we must instruct the shmfs to drop all of its - * backing pages, *now*. - */ - inode = obj->base.filp->f_path.dentry->d_inode; - shmem_truncate_range(inode, 0, (loff_t)-1); - - if (obj->base.map_list.map) - drm_gem_free_mmap_offset(&obj->base); - - obj->madv = __I915_MADV_PURGED; -} - -static inline int -i915_gem_object_is_purgeable(struct drm_i915_gem_object *obj) -{ - return obj->madv == I915_MADV_DONTNEED; -} - static u32 i915_gem_get_seqno(struct drm_device *dev) { -- cgit v0.10.2 From 6c085a728cf000ac1865d66f8c9b52935558b328 Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Mon, 20 Aug 2012 11:40:46 +0200 Subject: drm/i915: Track unbound pages When dealing with a working set larger than the GATT, or even the mappable aperture when touching through the GTT, we end up with evicting objects only to rebind them at a new offset again later. Moving an object into and out of the GTT requires clflushing the pages, thus causing a double-clflush penalty for rebinding. To avoid having to clflush on rebinding, we can track the pages as they are evicted from the GTT and only relinquish those pages on memory pressure. As usual, if it were not for the handling of out-of-memory condition and having to manually shrink our own bo caches, it would be a net reduction of code. Alas. Note: The patch also contains a few changes to the last-hope evict_everything logic in i916_gem_execbuffer.c - we no longer try to only evict the purgeable stuff in a first try (since that's superflous and only helps in OOM corner-cases, not fragmented-gtt trashing situations). Also, the extraction of the get_pages retry loop from bind_to_gtt (and other callsites) to get_pages should imo have been a separate patch. v2: Ditch the newly added put_pages (for unbound objects only) in i915_gem_reset. A quick irc discussion hasn't revealed any important reason for this, so if we need this, I'd like to have a git blame'able explanation for it. v3: Undo the s/drm_malloc_ab/kmalloc/ in get_pages that Chris noticed. Signed-off-by: Chris Wilson [danvet: Split out code movements and rant a bit in the commit message with a few Notes. Done v2] Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/i915_debugfs.c b/drivers/gpu/drm/i915/i915_debugfs.c index a18e936..608d3ae 100644 --- a/drivers/gpu/drm/i915/i915_debugfs.c +++ b/drivers/gpu/drm/i915/i915_debugfs.c @@ -211,7 +211,7 @@ static int i915_gem_object_info(struct seq_file *m, void* data) dev_priv->mm.object_memory); size = count = mappable_size = mappable_count = 0; - count_objects(&dev_priv->mm.gtt_list, gtt_list); + count_objects(&dev_priv->mm.bound_list, gtt_list); seq_printf(m, "%u [%u] objects, %zu [%zu] bytes in gtt\n", count, mappable_count, size, mappable_size); @@ -225,8 +225,13 @@ static int i915_gem_object_info(struct seq_file *m, void* data) seq_printf(m, " %u [%u] inactive objects, %zu [%zu] bytes\n", count, mappable_count, size, mappable_size); + size = count = 0; + list_for_each_entry(obj, &dev_priv->mm.unbound_list, gtt_list) + size += obj->base.size, ++count; + seq_printf(m, "%u unbound objects, %zu bytes\n", count, size); + size = count = mappable_size = mappable_count = 0; - list_for_each_entry(obj, &dev_priv->mm.gtt_list, gtt_list) { + list_for_each_entry(obj, &dev_priv->mm.bound_list, gtt_list) { if (obj->fault_mappable) { size += obj->gtt_space->size; ++count; @@ -264,7 +269,7 @@ static int i915_gem_gtt_info(struct seq_file *m, void* data) return ret; total_obj_size = total_gtt_size = count = 0; - list_for_each_entry(obj, &dev_priv->mm.gtt_list, gtt_list) { + list_for_each_entry(obj, &dev_priv->mm.bound_list, gtt_list) { if (list == PINNED_LIST && obj->pin_count == 0) continue; @@ -526,7 +531,8 @@ static int i915_gem_fence_regs_info(struct seq_file *m, void *data) for (i = 0; i < dev_priv->num_fence_regs; i++) { struct drm_i915_gem_object *obj = dev_priv->fence_regs[i].obj; - seq_printf(m, "Fenced object[%2d] = ", i); + seq_printf(m, "Fence %d, pin count = %d, object = ", + i, dev_priv->fence_regs[i].pin_count); if (obj == NULL) seq_printf(m, "unused"); else diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index ed3ba70..a2382a1 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -685,7 +685,13 @@ typedef struct drm_i915_private { struct drm_mm gtt_space; /** List of all objects in gtt_space. Used to restore gtt * mappings on resume */ - struct list_head gtt_list; + struct list_head bound_list; + /** + * List of objects which are not bound to the GTT (thus + * are idle and not used by the GPU) but still have + * (presumably uncached) pages still attached. + */ + struct list_head unbound_list; /** Usable portion of the GTT for GEM */ unsigned long gtt_start; @@ -1306,8 +1312,7 @@ int __must_check i915_gem_object_unbind(struct drm_i915_gem_object *obj); void i915_gem_release_mmap(struct drm_i915_gem_object *obj); void i915_gem_lastclose(struct drm_device *dev); -int i915_gem_object_get_pages_gtt(struct drm_i915_gem_object *obj, - gfp_t gfpmask); +int __must_check i915_gem_object_get_pages_gtt(struct drm_i915_gem_object *obj); int __must_check i915_mutex_lock_interruptible(struct drm_device *dev); int i915_gem_object_sync(struct drm_i915_gem_object *obj, struct intel_ring_buffer *to); @@ -1449,7 +1454,7 @@ int __must_check i915_gem_evict_something(struct drm_device *dev, int min_size, unsigned alignment, unsigned cache_level, bool mappable); -int i915_gem_evict_everything(struct drm_device *dev, bool purgeable_only); +int i915_gem_evict_everything(struct drm_device *dev); /* i915_gem_stolen.c */ int i915_gem_init_stolen(struct drm_device *dev); diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index 0f70c2a..462a8f2 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -55,6 +55,8 @@ static void i915_gem_object_update_fence(struct drm_i915_gem_object *obj, static int i915_gem_inactive_shrink(struct shrinker *shrinker, struct shrink_control *sc); +static long i915_gem_purge(struct drm_i915_private *dev_priv, long target); +static void i915_gem_shrink_all(struct drm_i915_private *dev_priv); static void i915_gem_object_truncate(struct drm_i915_gem_object *obj); static inline void i915_gem_object_fence_lost(struct drm_i915_gem_object *obj) @@ -140,7 +142,7 @@ int i915_mutex_lock_interruptible(struct drm_device *dev) static inline bool i915_gem_object_is_inactive(struct drm_i915_gem_object *obj) { - return !obj->active; + return obj->gtt_space && !obj->active; } int @@ -179,7 +181,7 @@ i915_gem_get_aperture_ioctl(struct drm_device *dev, void *data, pinned = 0; mutex_lock(&dev->struct_mutex); - list_for_each_entry(obj, &dev_priv->mm.gtt_list, gtt_list) + list_for_each_entry(obj, &dev_priv->mm.bound_list, gtt_list) if (obj->pin_count) pinned += obj->gtt_space->size; mutex_unlock(&dev->struct_mutex); @@ -423,9 +425,11 @@ i915_gem_shmem_pread(struct drm_device *dev, * anyway again before the next pread happens. */ if (obj->cache_level == I915_CACHE_NONE) needs_clflush = 1; - ret = i915_gem_object_set_to_gtt_domain(obj, false); - if (ret) - return ret; + if (obj->gtt_space) { + ret = i915_gem_object_set_to_gtt_domain(obj, false); + if (ret) + return ret; + } } offset = args->offset; @@ -751,9 +755,11 @@ i915_gem_shmem_pwrite(struct drm_device *dev, * right away and we therefore have to clflush anyway. */ if (obj->cache_level == I915_CACHE_NONE) needs_clflush_after = 1; - ret = i915_gem_object_set_to_gtt_domain(obj, true); - if (ret) - return ret; + if (obj->gtt_space) { + ret = i915_gem_object_set_to_gtt_domain(obj, true); + if (ret) + return ret; + } } /* Same trick applies for invalidate partially written cachelines before * writing. */ @@ -1366,17 +1372,28 @@ i915_gem_object_is_purgeable(struct drm_i915_gem_object *obj) return obj->madv == I915_MADV_DONTNEED; } -static void +static int i915_gem_object_put_pages_gtt(struct drm_i915_gem_object *obj) { int page_count = obj->base.size / PAGE_SIZE; - int i; + int ret, i; - if (!obj->pages) - return; + if (obj->pages == NULL) + return 0; + BUG_ON(obj->gtt_space); BUG_ON(obj->madv == __I915_MADV_PURGED); + ret = i915_gem_object_set_to_cpu_domain(obj, true); + if (ret) { + /* In the event of a disaster, abandon all caches and + * hope for the best. + */ + WARN_ON(ret != -EIO); + i915_gem_clflush_object(obj); + obj->base.read_domains = obj->base.write_domain = I915_GEM_DOMAIN_CPU; + } + if (i915_gem_object_needs_bit17_swizzle(obj)) i915_gem_object_save_bit_17_swizzle(obj); @@ -1396,37 +1413,112 @@ i915_gem_object_put_pages_gtt(struct drm_i915_gem_object *obj) drm_free_large(obj->pages); obj->pages = NULL; + + list_del(&obj->gtt_list); + + if (i915_gem_object_is_purgeable(obj)) + i915_gem_object_truncate(obj); + + return 0; +} + +static long +i915_gem_purge(struct drm_i915_private *dev_priv, long target) +{ + struct drm_i915_gem_object *obj, *next; + long count = 0; + + list_for_each_entry_safe(obj, next, + &dev_priv->mm.unbound_list, + gtt_list) { + if (i915_gem_object_is_purgeable(obj) && + i915_gem_object_put_pages_gtt(obj) == 0) { + count += obj->base.size >> PAGE_SHIFT; + if (count >= target) + return count; + } + } + + list_for_each_entry_safe(obj, next, + &dev_priv->mm.inactive_list, + mm_list) { + if (i915_gem_object_is_purgeable(obj) && + i915_gem_object_unbind(obj) == 0 && + i915_gem_object_put_pages_gtt(obj) == 0) { + count += obj->base.size >> PAGE_SHIFT; + if (count >= target) + return count; + } + } + + return count; +} + +static void +i915_gem_shrink_all(struct drm_i915_private *dev_priv) +{ + struct drm_i915_gem_object *obj, *next; + + i915_gem_evict_everything(dev_priv->dev); + + list_for_each_entry_safe(obj, next, &dev_priv->mm.unbound_list, gtt_list) + i915_gem_object_put_pages_gtt(obj); } int -i915_gem_object_get_pages_gtt(struct drm_i915_gem_object *obj, - gfp_t gfpmask) +i915_gem_object_get_pages_gtt(struct drm_i915_gem_object *obj) { + struct drm_i915_private *dev_priv = obj->base.dev->dev_private; int page_count, i; struct address_space *mapping; - struct inode *inode; struct page *page; + gfp_t gfp; if (obj->pages || obj->sg_table) return 0; + /* Assert that the object is not currently in any GPU domain. As it + * wasn't in the GTT, there shouldn't be any way it could have been in + * a GPU cache + */ + BUG_ON(obj->base.read_domains & I915_GEM_GPU_DOMAINS); + BUG_ON(obj->base.write_domain & I915_GEM_GPU_DOMAINS); + /* Get the list of pages out of our struct file. They'll be pinned * at this point until we release them. */ page_count = obj->base.size / PAGE_SIZE; - BUG_ON(obj->pages != NULL); obj->pages = drm_malloc_ab(page_count, sizeof(struct page *)); if (obj->pages == NULL) return -ENOMEM; - inode = obj->base.filp->f_path.dentry->d_inode; - mapping = inode->i_mapping; - gfpmask |= mapping_gfp_mask(mapping); - + /* Fail silently without starting the shrinker */ + mapping = obj->base.filp->f_path.dentry->d_inode->i_mapping; + gfp = mapping_gfp_mask(mapping); + gfp |= __GFP_NORETRY | __GFP_NOWARN | __GFP_NO_KSWAPD; + gfp &= ~(__GFP_IO | __GFP_WAIT); for (i = 0; i < page_count; i++) { - page = shmem_read_mapping_page_gfp(mapping, i, gfpmask); - if (IS_ERR(page)) - goto err_pages; + page = shmem_read_mapping_page_gfp(mapping, i, gfp); + if (IS_ERR(page)) { + i915_gem_purge(dev_priv, page_count); + page = shmem_read_mapping_page_gfp(mapping, i, gfp); + } + if (IS_ERR(page)) { + /* We've tried hard to allocate the memory by reaping + * our own buffer, now let the real VM do its job and + * go down in flames if truly OOM. + */ + gfp &= ~(__GFP_NORETRY | __GFP_NOWARN | __GFP_NO_KSWAPD); + gfp |= __GFP_IO | __GFP_WAIT; + + i915_gem_shrink_all(dev_priv); + page = shmem_read_mapping_page_gfp(mapping, i, gfp); + if (IS_ERR(page)) + goto err_pages; + + gfp |= __GFP_NORETRY | __GFP_NOWARN | __GFP_NO_KSWAPD; + gfp &= ~(__GFP_IO | __GFP_WAIT); + } obj->pages[i] = page; } @@ -1434,6 +1526,7 @@ i915_gem_object_get_pages_gtt(struct drm_i915_gem_object *obj, if (i915_gem_object_needs_bit17_swizzle(obj)) i915_gem_object_do_bit_17_swizzle(obj); + list_add_tail(&obj->gtt_list, &dev_priv->mm.unbound_list); return 0; err_pages: @@ -1698,6 +1791,7 @@ void i915_gem_reset(struct drm_device *dev) obj->base.read_domains &= ~I915_GEM_GPU_DOMAINS; } + /* The fence registers are invalidated so clear them out */ i915_gem_reset_fences(dev); } @@ -2209,22 +2303,6 @@ i915_gem_object_unbind(struct drm_i915_gem_object *obj) i915_gem_object_finish_gtt(obj); - /* Move the object to the CPU domain to ensure that - * any possible CPU writes while it's not in the GTT - * are flushed when we go to remap it. - */ - if (ret == 0) - ret = i915_gem_object_set_to_cpu_domain(obj, 1); - if (ret == -ERESTARTSYS) - return ret; - if (ret) { - /* In the event of a disaster, abandon all caches and - * hope for the best. - */ - i915_gem_clflush_object(obj); - obj->base.read_domains = obj->base.write_domain = I915_GEM_DOMAIN_CPU; - } - /* release the fence reg _after_ flushing */ ret = i915_gem_object_put_fence(obj); if (ret) @@ -2240,10 +2318,8 @@ i915_gem_object_unbind(struct drm_i915_gem_object *obj) } i915_gem_gtt_finish_object(obj); - i915_gem_object_put_pages_gtt(obj); - - list_del_init(&obj->gtt_list); - list_del_init(&obj->mm_list); + list_del(&obj->mm_list); + list_move_tail(&obj->gtt_list, &dev_priv->mm.unbound_list); /* Avoid an unnecessary call to unbind on rebind. */ obj->map_and_fenceable = true; @@ -2251,10 +2327,7 @@ i915_gem_object_unbind(struct drm_i915_gem_object *obj) obj->gtt_space = NULL; obj->gtt_offset = 0; - if (i915_gem_object_is_purgeable(obj)) - i915_gem_object_truncate(obj); - - return ret; + return 0; } static int i915_ring_idle(struct intel_ring_buffer *ring) @@ -2667,7 +2740,6 @@ i915_gem_object_bind_to_gtt(struct drm_i915_gem_object *obj, struct drm_device *dev = obj->base.dev; drm_i915_private_t *dev_priv = dev->dev_private; struct drm_mm_node *free_space; - gfp_t gfpmask = __GFP_NORETRY | __GFP_NOWARN; u32 size, fence_size, fence_alignment, unfenced_alignment; bool mappable, fenceable; int ret; @@ -2707,6 +2779,10 @@ i915_gem_object_bind_to_gtt(struct drm_i915_gem_object *obj, return -E2BIG; } + ret = i915_gem_object_get_pages_gtt(obj); + if (ret) + return ret; + search_free: if (map_and_fenceable) free_space = @@ -2733,9 +2809,6 @@ i915_gem_object_bind_to_gtt(struct drm_i915_gem_object *obj, false); } if (obj->gtt_space == NULL) { - /* If the gtt is empty and we're still having trouble - * fitting our object in, we're out of memory. - */ ret = i915_gem_evict_something(dev, size, alignment, obj->cache_level, map_and_fenceable); @@ -2752,55 +2825,20 @@ i915_gem_object_bind_to_gtt(struct drm_i915_gem_object *obj, return -EINVAL; } - ret = i915_gem_object_get_pages_gtt(obj, gfpmask); - if (ret) { - drm_mm_put_block(obj->gtt_space); - obj->gtt_space = NULL; - - if (ret == -ENOMEM) { - /* first try to reclaim some memory by clearing the GTT */ - ret = i915_gem_evict_everything(dev, false); - if (ret) { - /* now try to shrink everyone else */ - if (gfpmask) { - gfpmask = 0; - goto search_free; - } - - return -ENOMEM; - } - - goto search_free; - } - - return ret; - } ret = i915_gem_gtt_prepare_object(obj); if (ret) { - i915_gem_object_put_pages_gtt(obj); drm_mm_put_block(obj->gtt_space); obj->gtt_space = NULL; - - if (i915_gem_evict_everything(dev, false)) - return ret; - - goto search_free; + return ret; } if (!dev_priv->mm.aliasing_ppgtt) i915_gem_gtt_bind_object(obj, obj->cache_level); - list_add_tail(&obj->gtt_list, &dev_priv->mm.gtt_list); + list_move_tail(&obj->gtt_list, &dev_priv->mm.bound_list); list_add_tail(&obj->mm_list, &dev_priv->mm.inactive_list); - /* Assert that the object is not currently in any GPU domain. As it - * wasn't in the GTT, there shouldn't be any way it could have been in - * a GPU cache - */ - BUG_ON(obj->base.read_domains & I915_GEM_GPU_DOMAINS); - BUG_ON(obj->base.write_domain & I915_GEM_GPU_DOMAINS); - obj->gtt_offset = obj->gtt_space->start; fenceable = @@ -3464,9 +3502,8 @@ i915_gem_madvise_ioctl(struct drm_device *dev, void *data, if (obj->madv != __I915_MADV_PURGED) obj->madv = args->madv; - /* if the object is no longer bound, discard its backing storage */ - if (i915_gem_object_is_purgeable(obj) && - obj->gtt_space == NULL) + /* if the object is no longer attached, discard its backing storage */ + if (i915_gem_object_is_purgeable(obj) && obj->pages == NULL) i915_gem_object_truncate(obj); args->retained = obj->madv != __I915_MADV_PURGED; @@ -3573,6 +3610,7 @@ void i915_gem_free_object(struct drm_gem_object *gem_obj) dev_priv->mm.interruptible = was_interruptible; } + i915_gem_object_put_pages_gtt(obj); if (obj->base.map_list.map) drm_gem_free_mmap_offset(&obj->base); @@ -3605,7 +3643,7 @@ i915_gem_idle(struct drm_device *dev) /* Under UMS, be paranoid and evict. */ if (!drm_core_check_feature(dev, DRIVER_MODESET)) - i915_gem_evict_everything(dev, false); + i915_gem_evict_everything(dev); i915_gem_reset_fences(dev); @@ -3963,8 +4001,9 @@ i915_gem_load(struct drm_device *dev) INIT_LIST_HEAD(&dev_priv->mm.active_list); INIT_LIST_HEAD(&dev_priv->mm.inactive_list); + INIT_LIST_HEAD(&dev_priv->mm.unbound_list); + INIT_LIST_HEAD(&dev_priv->mm.bound_list); INIT_LIST_HEAD(&dev_priv->mm.fence_list); - INIT_LIST_HEAD(&dev_priv->mm.gtt_list); for (i = 0; i < I915_NUM_RINGS; i++) init_ring_lists(&dev_priv->ring[i]); for (i = 0; i < I915_MAX_NUM_FENCES; i++) @@ -4209,13 +4248,6 @@ void i915_gem_release(struct drm_device *dev, struct drm_file *file) } static int -i915_gpu_is_active(struct drm_device *dev) -{ - drm_i915_private_t *dev_priv = dev->dev_private; - return !list_empty(&dev_priv->mm.active_list); -} - -static int i915_gem_inactive_shrink(struct shrinker *shrinker, struct shrink_control *sc) { struct drm_i915_private *dev_priv = @@ -4223,60 +4255,26 @@ i915_gem_inactive_shrink(struct shrinker *shrinker, struct shrink_control *sc) struct drm_i915_private, mm.inactive_shrinker); struct drm_device *dev = dev_priv->dev; - struct drm_i915_gem_object *obj, *next; + struct drm_i915_gem_object *obj; int nr_to_scan = sc->nr_to_scan; int cnt; if (!mutex_trylock(&dev->struct_mutex)) return 0; - /* "fast-path" to count number of available objects */ - if (nr_to_scan == 0) { - cnt = 0; - list_for_each_entry(obj, - &dev_priv->mm.inactive_list, - mm_list) - cnt++; - mutex_unlock(&dev->struct_mutex); - return cnt / 100 * sysctl_vfs_cache_pressure; + if (nr_to_scan) { + nr_to_scan -= i915_gem_purge(dev_priv, nr_to_scan); + if (nr_to_scan > 0) + i915_gem_shrink_all(dev_priv); } -rescan: - /* first scan for clean buffers */ - i915_gem_retire_requests(dev); - - list_for_each_entry_safe(obj, next, - &dev_priv->mm.inactive_list, - mm_list) { - if (i915_gem_object_is_purgeable(obj)) { - if (i915_gem_object_unbind(obj) == 0 && - --nr_to_scan == 0) - break; - } - } - - /* second pass, evict/count anything still on the inactive list */ cnt = 0; - list_for_each_entry_safe(obj, next, - &dev_priv->mm.inactive_list, - mm_list) { - if (nr_to_scan && - i915_gem_object_unbind(obj) == 0) - nr_to_scan--; - else - cnt++; - } + list_for_each_entry(obj, &dev_priv->mm.unbound_list, gtt_list) + cnt += obj->base.size >> PAGE_SHIFT; + list_for_each_entry(obj, &dev_priv->mm.bound_list, gtt_list) + if (obj->pin_count == 0) + cnt += obj->base.size >> PAGE_SHIFT; - if (nr_to_scan && i915_gpu_is_active(dev)) { - /* - * We are desperate for pages, so as a last resort, wait - * for the GPU to finish and discard whatever we can. - * This has a dramatic impact to reduce the number of - * OOM-killer events whilst running the GPU aggressively. - */ - if (i915_gpu_idle(dev) == 0) - goto rescan; - } mutex_unlock(&dev->struct_mutex); - return cnt / 100 * sysctl_vfs_cache_pressure; + return cnt; } diff --git a/drivers/gpu/drm/i915/i915_gem_dmabuf.c b/drivers/gpu/drm/i915/i915_gem_dmabuf.c index ceaad5a..43c9530 100644 --- a/drivers/gpu/drm/i915/i915_gem_dmabuf.c +++ b/drivers/gpu/drm/i915/i915_gem_dmabuf.c @@ -33,7 +33,7 @@ static struct sg_table *i915_gem_map_dma_buf(struct dma_buf_attachment *attachme struct drm_i915_gem_object *obj = attachment->dmabuf->priv; struct drm_device *dev = obj->base.dev; int npages = obj->base.size / PAGE_SIZE; - struct sg_table *sg = NULL; + struct sg_table *sg; int ret; int nents; @@ -41,10 +41,10 @@ static struct sg_table *i915_gem_map_dma_buf(struct dma_buf_attachment *attachme if (ret) return ERR_PTR(ret); - if (!obj->pages) { - ret = i915_gem_object_get_pages_gtt(obj, __GFP_NORETRY | __GFP_NOWARN); - if (ret) - goto out; + ret = i915_gem_object_get_pages_gtt(obj); + if (ret) { + sg = ERR_PTR(ret); + goto out; } /* link the pages into an SG then map the sg */ @@ -89,12 +89,10 @@ static void *i915_gem_dmabuf_vmap(struct dma_buf *dma_buf) goto out_unlock; } - if (!obj->pages) { - ret = i915_gem_object_get_pages_gtt(obj, __GFP_NORETRY | __GFP_NOWARN); - if (ret) { - mutex_unlock(&dev->struct_mutex); - return ERR_PTR(ret); - } + ret = i915_gem_object_get_pages_gtt(obj); + if (ret) { + mutex_unlock(&dev->struct_mutex); + return ERR_PTR(ret); } obj->dma_buf_vmapping = vmap(obj->pages, obj->base.size / PAGE_SIZE, 0, PAGE_KERNEL); diff --git a/drivers/gpu/drm/i915/i915_gem_evict.c b/drivers/gpu/drm/i915/i915_gem_evict.c index 7279c31..74635da 100644 --- a/drivers/gpu/drm/i915/i915_gem_evict.c +++ b/drivers/gpu/drm/i915/i915_gem_evict.c @@ -148,7 +148,7 @@ found: } int -i915_gem_evict_everything(struct drm_device *dev, bool purgeable_only) +i915_gem_evict_everything(struct drm_device *dev) { drm_i915_private_t *dev_priv = dev->dev_private; struct drm_i915_gem_object *obj, *next; @@ -160,7 +160,7 @@ i915_gem_evict_everything(struct drm_device *dev, bool purgeable_only) if (lists_empty) return -ENOSPC; - trace_i915_gem_evict_everything(dev, purgeable_only); + trace_i915_gem_evict_everything(dev); /* The gpu_idle will flush everything in the write domain to the * active list. Then we must move everything off the active list @@ -174,12 +174,9 @@ i915_gem_evict_everything(struct drm_device *dev, bool purgeable_only) /* Having flushed everything, unbind() should never raise an error */ list_for_each_entry_safe(obj, next, - &dev_priv->mm.inactive_list, mm_list) { - if (!purgeable_only || obj->madv != I915_MADV_WILLNEED) { - if (obj->pin_count == 0) - WARN_ON(i915_gem_object_unbind(obj)); - } - } + &dev_priv->mm.inactive_list, mm_list) + if (obj->pin_count == 0) + WARN_ON(i915_gem_object_unbind(obj)); return 0; } diff --git a/drivers/gpu/drm/i915/i915_gem_execbuffer.c b/drivers/gpu/drm/i915/i915_gem_execbuffer.c index afb312e..834a636 100644 --- a/drivers/gpu/drm/i915/i915_gem_execbuffer.c +++ b/drivers/gpu/drm/i915/i915_gem_execbuffer.c @@ -502,17 +502,12 @@ i915_gem_execbuffer_reserve(struct intel_ring_buffer *ring, } } - if (ret != -ENOSPC || retry > 1) + if (ret != -ENOSPC || retry++) return ret; - /* First attempt, just clear anything that is purgeable. - * Second attempt, clear the entire GTT. - */ - ret = i915_gem_evict_everything(ring->dev, retry == 0); + ret = i915_gem_evict_everything(ring->dev); if (ret) return ret; - - retry++; } while (1); err: diff --git a/drivers/gpu/drm/i915/i915_gem_gtt.c b/drivers/gpu/drm/i915/i915_gem_gtt.c index 3b3b731..8329a14 100644 --- a/drivers/gpu/drm/i915/i915_gem_gtt.c +++ b/drivers/gpu/drm/i915/i915_gem_gtt.c @@ -348,7 +348,7 @@ void i915_gem_restore_gtt_mappings(struct drm_device *dev) intel_gtt_clear_range(dev_priv->mm.gtt_start / PAGE_SIZE, (dev_priv->mm.gtt_end - dev_priv->mm.gtt_start) / PAGE_SIZE); - list_for_each_entry(obj, &dev_priv->mm.gtt_list, gtt_list) { + list_for_each_entry(obj, &dev_priv->mm.bound_list, gtt_list) { i915_gem_clflush_object(obj); i915_gem_gtt_bind_object(obj, obj->cache_level); } diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index a61b41a..002dcee 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -1221,7 +1221,7 @@ static void i915_capture_error_state(struct drm_device *dev) list_for_each_entry(obj, &dev_priv->mm.active_list, mm_list) i++; error->active_bo_count = i; - list_for_each_entry(obj, &dev_priv->mm.gtt_list, gtt_list) + list_for_each_entry(obj, &dev_priv->mm.bound_list, gtt_list) if (obj->pin_count) i++; error->pinned_bo_count = i - error->active_bo_count; @@ -1246,7 +1246,7 @@ static void i915_capture_error_state(struct drm_device *dev) error->pinned_bo_count = capture_pinned_bo(error->pinned_bo, error->pinned_bo_count, - &dev_priv->mm.gtt_list); + &dev_priv->mm.bound_list); do_gettimeofday(&error->time); diff --git a/drivers/gpu/drm/i915/i915_trace.h b/drivers/gpu/drm/i915/i915_trace.h index fe90b3a..3c4093d 100644 --- a/drivers/gpu/drm/i915/i915_trace.h +++ b/drivers/gpu/drm/i915/i915_trace.h @@ -214,22 +214,18 @@ TRACE_EVENT(i915_gem_evict, ); TRACE_EVENT(i915_gem_evict_everything, - TP_PROTO(struct drm_device *dev, bool purgeable), - TP_ARGS(dev, purgeable), + TP_PROTO(struct drm_device *dev), + TP_ARGS(dev), TP_STRUCT__entry( __field(u32, dev) - __field(bool, purgeable) ), TP_fast_assign( __entry->dev = dev->primary->index; - __entry->purgeable = purgeable; ), - TP_printk("dev=%d%s", - __entry->dev, - __entry->purgeable ? ", purgeable only" : "") + TP_printk("dev=%d", __entry->dev) ); TRACE_EVENT(i915_gem_ring_dispatch, -- cgit v0.10.2 From c4670ad080a1fe60d49f850e11f64a41db525496 Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Mon, 20 Aug 2012 10:23:27 +0100 Subject: drm/i915: Add some sanity checks to unbound tracking A pair of universally true checks that just need to be put in the right place depending on where in the patch sequence you go. Note that i915_gem_object_put_pages_gtt() already gains the BUG_ON(obj->gtt_space), but on reflection that needed to migrate to put_pages(). Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index 462a8f2..a549be5 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -1378,6 +1378,8 @@ i915_gem_object_put_pages_gtt(struct drm_i915_gem_object *obj) int page_count = obj->base.size / PAGE_SIZE; int ret, i; + BUG_ON(obj->gtt_space); + if (obj->pages == NULL) return 0; @@ -2293,6 +2295,8 @@ i915_gem_object_unbind(struct drm_i915_gem_object *obj) if (obj->pin_count) return -EBUSY; + BUG_ON(obj->pages == NULL); + ret = i915_gem_object_finish_gpu(obj); if (ret) return ret; -- cgit v0.10.2 From b7abb7144356536c41a81eda2bbfd46027e529f6 Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Mon, 20 Aug 2012 11:33:30 +0200 Subject: drm/i915: Show (count, size) of purgeable objects in i915_gem_objects Signed-off-by: Chris Wilson Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/i915_debugfs.c b/drivers/gpu/drm/i915/i915_debugfs.c index 608d3ae..5548b02 100644 --- a/drivers/gpu/drm/i915/i915_debugfs.c +++ b/drivers/gpu/drm/i915/i915_debugfs.c @@ -197,8 +197,8 @@ static int i915_gem_object_info(struct seq_file *m, void* data) struct drm_info_node *node = (struct drm_info_node *) m->private; struct drm_device *dev = node->minor->dev; struct drm_i915_private *dev_priv = dev->dev_private; - u32 count, mappable_count; - size_t size, mappable_size; + u32 count, mappable_count, purgeable_count; + size_t size, mappable_size, purgeable_size; struct drm_i915_gem_object *obj; int ret; @@ -225,9 +225,12 @@ static int i915_gem_object_info(struct seq_file *m, void* data) seq_printf(m, " %u [%u] inactive objects, %zu [%zu] bytes\n", count, mappable_count, size, mappable_size); - size = count = 0; - list_for_each_entry(obj, &dev_priv->mm.unbound_list, gtt_list) + size = count = purgeable_size = purgeable_count = 0; + list_for_each_entry(obj, &dev_priv->mm.unbound_list, gtt_list) { size += obj->base.size, ++count; + if (obj->madv == I915_MADV_DONTNEED) + purgeable_size += obj->base.size, ++purgeable_count; + } seq_printf(m, "%u unbound objects, %zu bytes\n", count, size); size = count = mappable_size = mappable_count = 0; @@ -240,7 +243,13 @@ static int i915_gem_object_info(struct seq_file *m, void* data) mappable_size += obj->gtt_space->size; ++mappable_count; } + if (obj->madv == I915_MADV_DONTNEED) { + purgeable_size += obj->base.size; + ++purgeable_count; + } } + seq_printf(m, "%u purgeable objects, %zu bytes\n", + purgeable_count, purgeable_size); seq_printf(m, "%u pinned mappable objects, %zu bytes\n", mappable_count, mappable_size); seq_printf(m, "%u fault mappable objects, %zu bytes\n", -- cgit v0.10.2 From c110a6d728d0243a755973d34e363952b74af984 Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Sat, 11 Aug 2012 15:41:02 +0100 Subject: drm/i915: Show pin count in debugfs Signed-off-by: Chris Wilson Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/i915_debugfs.c b/drivers/gpu/drm/i915/i915_debugfs.c index 5548b02..204c19a 100644 --- a/drivers/gpu/drm/i915/i915_debugfs.c +++ b/drivers/gpu/drm/i915/i915_debugfs.c @@ -118,6 +118,8 @@ describe_obj(struct seq_file *m, struct drm_i915_gem_object *obj) obj->madv == I915_MADV_DONTNEED ? " purgeable" : ""); if (obj->base.name) seq_printf(m, " (name: %d)", obj->base.name); + if (obj->pin_count) + seq_printf(m, " (pinned x %d)", obj->pin_count); if (obj->fence_reg != I915_FENCE_REG_NONE) seq_printf(m, " (fence: %d)", obj->fence_reg); if (obj->gtt_space != NULL) -- cgit v0.10.2 From d8cb5086695dcdd076e911fc298a5a6701497371 Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Sat, 11 Aug 2012 15:41:03 +0100 Subject: drm/i915: Try harder to allocate an mmap_offset Given the persistence of an offset for the lifetime of an object, itis easy to contemplate how the mmap space becomes badly fragmented to the point that further allocations fail with ENOSPC. Our only recourse at this point is to try to purge the objects to release some space and reattempt the allocation. References: https://bugs.freedesktop.org/show_bug.cgi?id=39552 Signed-off-by: Chris Wilson Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index a549be5..e1ec587 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -1276,6 +1276,42 @@ i915_gem_get_unfenced_gtt_alignment(struct drm_device *dev, return i915_gem_get_gtt_size(dev, size, tiling_mode); } +static int i915_gem_object_create_mmap_offset(struct drm_i915_gem_object *obj) +{ + struct drm_i915_private *dev_priv = obj->base.dev->dev_private; + int ret; + + if (obj->base.map_list.map) + return 0; + + ret = drm_gem_create_mmap_offset(&obj->base); + if (ret != -ENOSPC) + return ret; + + /* Badly fragmented mmap space? The only way we can recover + * space is by destroying unwanted objects. We can't randomly release + * mmap_offsets as userspace expects them to be persistent for the + * lifetime of the objects. The closest we can is to release the + * offsets on purgeable objects by truncating it and marking it purged, + * which prevents userspace from ever using that object again. + */ + i915_gem_purge(dev_priv, obj->base.size >> PAGE_SHIFT); + ret = drm_gem_create_mmap_offset(&obj->base); + if (ret != -ENOSPC) + return ret; + + i915_gem_shrink_all(dev_priv); + return drm_gem_create_mmap_offset(&obj->base); +} + +static void i915_gem_object_free_mmap_offset(struct drm_i915_gem_object *obj) +{ + if (!obj->base.map_list.map) + return; + + drm_gem_free_mmap_offset(&obj->base); +} + int i915_gem_mmap_gtt(struct drm_file *file, struct drm_device *dev, @@ -1307,11 +1343,9 @@ i915_gem_mmap_gtt(struct drm_file *file, goto out; } - if (!obj->base.map_list.map) { - ret = drm_gem_create_mmap_offset(&obj->base); - if (ret) - goto out; - } + ret = i915_gem_object_create_mmap_offset(obj); + if (ret) + goto out; *offset = (u64)obj->base.map_list.hash.key << PAGE_SHIFT; @@ -1360,8 +1394,7 @@ i915_gem_object_truncate(struct drm_i915_gem_object *obj) inode = obj->base.filp->f_path.dentry->d_inode; shmem_truncate_range(inode, 0, (loff_t)-1); - if (obj->base.map_list.map) - drm_gem_free_mmap_offset(&obj->base); + i915_gem_object_free_mmap_offset(obj); obj->madv = __I915_MADV_PURGED; } @@ -3615,8 +3648,7 @@ void i915_gem_free_object(struct drm_gem_object *gem_obj) } i915_gem_object_put_pages_gtt(obj); - if (obj->base.map_list.map) - drm_gem_free_mmap_offset(&obj->base); + i915_gem_object_free_mmap_offset(obj); drm_gem_object_release(&obj->base); i915_gem_info_remove_obj(dev_priv, obj->base.size); -- cgit v0.10.2 From de9932d13c95c4125d2ed8703f5574fcc108bbcf Mon Sep 17 00:00:00 2001 From: "Xu, Anhua" Date: Tue, 21 Aug 2012 03:06:02 +0000 Subject: drm/i915: fix reassignment of variable "intel_dp->DP" In intel_dp_mode_set we OR in the exact same bits twice at the same spot. Kill one of the redundant assignments. This little regression was introduced by: commit 417e822deee1d2bcd8a8a60660c40a0903713f2b Author: Keith Packard Date: Tue Nov 1 19:54:11 2011 -0700 drm/i915: Treat PCH eDP like DP in most places PCH eDP has many of the same needs as regular PCH DP connections, including the DP_CTl bit settings, the TRANS_DP_CTL register. The reachable tag for this commit is: v3.1-5461-g417e822 Signed-off-by: Anhua Xu [danvet: Improved the commit message somewhat and ensured the diff is clearer.] Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index 977d9d2..1ab371b 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -850,10 +850,8 @@ intel_dp_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode, * supposed to be read-only. */ intel_dp->DP = I915_READ(intel_dp->output_reg) & DP_DETECTED; - intel_dp->DP |= DP_VOLTAGE_0_4 | DP_PRE_EMPHASIS_0; /* Handle DP bits in common between all three register formats */ - intel_dp->DP |= DP_VOLTAGE_0_4 | DP_PRE_EMPHASIS_0; switch (intel_dp->lane_count) { -- cgit v0.10.2 From 44f46b4222a9ba5b21dcdf13f93dc53f48768d2b Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Thu, 21 Jun 2012 13:19:59 +0300 Subject: drm/i915: Cantiga+ cannot handle a hsync front porch of 0 This addresses WaPruneModeWithIncorrectHsyncOffset. Bugzilla: http://bugs.freedesktop.org/show_bug.cgi?id=50236 Signed-off-by: Chris Wilson Signed-off-by: Jani Nikula Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 42c5758..778cbb8 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -3602,6 +3602,13 @@ static bool intel_crtc_mode_fixup(struct drm_crtc *crtc, if (!(adjusted_mode->private_flags & INTEL_MODE_CRTC_TIMINGS_SET)) drm_mode_set_crtcinfo(adjusted_mode, 0); + /* WaPruneModeWithIncorrectHsyncOffset: Cantiga+ cannot handle modes + * with a hsync front porch of 0. + */ + if ((INTEL_INFO(dev)->gen > 4 || IS_G4X(dev)) && + adjusted_mode->hsync_start == adjusted_mode->hdisplay) + return false; + return true; } -- cgit v0.10.2 From 71e172e8d141fc167b1cca28271fd39c8cf63ed3 Mon Sep 17 00:00:00 2001 From: Ben Widawsky Date: Mon, 20 Aug 2012 16:15:13 -0700 Subject: drm/i915: Add ERR_INT to gen7 error state ERR_INT can generate interrupts. However since most of the conditions seem quite fatal the patch opts to simply report it in error state instead of adding more complexity to the interrupt handler for little gain (the bits are sticky anyway). Signed-off-by: Ben Widawsky Reviewed-by: Paulo Zanoni Tested-by: Paulo Zanoni Reviewed-by: Antti Koskipaa Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/i915_debugfs.c b/drivers/gpu/drm/i915/i915_debugfs.c index 204c19a..d300393 100644 --- a/drivers/gpu/drm/i915/i915_debugfs.c +++ b/drivers/gpu/drm/i915/i915_debugfs.c @@ -719,6 +719,9 @@ static int i915_error_state(struct seq_file *m, void *unused) seq_printf(m, "DONE_REG: 0x%08x\n", error->done_reg); } + if (INTEL_INFO(dev)->gen == 7) + seq_printf(m, "ERR_INT: 0x%08x\n", error->err_int); + for_each_ring(ring, dev_priv, i) i915_ring_error_state(m, dev, error, i); diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index a2382a1..cbd3cd0 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -196,6 +196,7 @@ struct drm_i915_error_state { u32 cpu_ring_head[I915_NUM_RINGS]; u32 cpu_ring_tail[I915_NUM_RINGS]; u32 error; /* gen6+ */ + u32 err_int; /* gen7 */ u32 instpm[I915_NUM_RINGS]; u32 instps[I915_NUM_RINGS]; u32 instdone1; diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index 002dcee..3633029 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -1210,6 +1210,9 @@ static void i915_capture_error_state(struct drm_device *dev) error->done_reg = I915_READ(DONE_REG); } + if (INTEL_INFO(dev)->gen == 7) + error->err_int = I915_READ(GEN7_ERR_INT); + i915_gem_record_fences(dev, error); i915_gem_record_rings(dev, error); diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h index fd6a26a..a20885c 100644 --- a/drivers/gpu/drm/i915/i915_reg.h +++ b/drivers/gpu/drm/i915/i915_reg.h @@ -500,6 +500,7 @@ #define DMA_FADD_I8XX 0x020d0 #define ERROR_GEN6 0x040a0 +#define GEN7_ERR_INT 0x44040 /* GM45+ chicken bits -- debug workaround bits that may be required * for various sorts of correct behavior. The top 16 bits of each are -- cgit v0.10.2 From b4c145c1d245c2cc19754dbe4b718f5a48755993 Mon Sep 17 00:00:00 2001 From: Ben Widawsky Date: Mon, 20 Aug 2012 16:15:14 -0700 Subject: drm/i915: Find unclaimed MMIO writes. ERR_INT on HSW will display unclaimed MMIO accesses. This can be either the result of a driver bug writing to an invalid addresses, or the result of RC6. Signed-off-by: Ben Widawsky Reviewed-by: Paulo Zanoni Tested-by: Paulo Zanoni Reviewed-by: Antti Koskipaa Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c index 7ebb13b..7f3863f 100644 --- a/drivers/gpu/drm/i915/i915_drv.c +++ b/drivers/gpu/drm/i915/i915_drv.c @@ -1174,6 +1174,10 @@ void i915_write##x(struct drm_i915_private *dev_priv, u32 reg, u##x val) { \ if (unlikely(__fifo_ret)) { \ gen6_gt_check_fifodbg(dev_priv); \ } \ + if (IS_HASWELL(dev_priv->dev) && (I915_READ_NOTRACE(GEN7_ERR_INT) & ERR_INT_MMIO_UNCLAIMED)) { \ + DRM_ERROR("Unclaimed write to %x\n", reg); \ + writel(ERR_INT_MMIO_UNCLAIMED, dev_priv->regs + GEN7_ERR_INT); \ + } \ } __i915_write(8, b) __i915_write(16, w) diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h index a20885c..f27e998 100644 --- a/drivers/gpu/drm/i915/i915_reg.h +++ b/drivers/gpu/drm/i915/i915_reg.h @@ -501,6 +501,7 @@ #define ERROR_GEN6 0x040a0 #define GEN7_ERR_INT 0x44040 +#define ERR_INT_MMIO_UNCLAIMED (1<<13) /* GM45+ chicken bits -- debug workaround bits that may be required * for various sorts of correct behavior. The top 16 bits of each are -- cgit v0.10.2 From 9cad9c95d7e8d6d61d8c9729e0b6bbd18f47d86d Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Fri, 13 Jul 2012 00:57:26 +0200 Subject: Documentation: DocBook DRM framework documentation Signed-off-by: Laurent Pinchart Reviewed-by: Rob Clark diff --git a/Documentation/DocBook/drm.tmpl b/Documentation/DocBook/drm.tmpl index 196b8b9..b030052 100644 --- a/Documentation/DocBook/drm.tmpl +++ b/Documentation/DocBook/drm.tmpl @@ -6,11 +6,36 @@ Linux DRM Developer's Guide + + + Jesse + Barnes + Initial version + + Intel Corporation +
+ jesse.barnes@intel.com +
+
+
+ + Laurent + Pinchart + Driver internals + + Ideas on board SPRL +
+ laurent.pinchart@ideasonboard.com +
+
+
+
+ 2008-2009 - - Intel Corporation (Jesse Barnes <jesse.barnes@intel.com>) - + 2012 + Intel Corporation + Laurent Pinchart @@ -20,6 +45,17 @@ the kernel source COPYING file. + + + + + 1.0 + 2012-07-13 + LP + Added extensive documentation about driver internals. + + +
@@ -72,342 +108,361 @@ submission & fencing, suspend/resume support, and DMA services. - - The core of every DRM driver is struct drm_driver. Drivers - typically statically initialize a drm_driver structure, - then pass it to drm_init() at load time. - - Driver initialization - - Before calling the DRM initialization routines, the driver must - first create and fill out a struct drm_driver structure. - - - static struct drm_driver driver = { - /* Don't use MTRRs here; the Xserver or userspace app should - * deal with them for Intel hardware. - */ - .driver_features = - DRIVER_USE_AGP | DRIVER_REQUIRE_AGP | - DRIVER_HAVE_IRQ | DRIVER_IRQ_SHARED | DRIVER_MODESET, - .load = i915_driver_load, - .unload = i915_driver_unload, - .firstopen = i915_driver_firstopen, - .lastclose = i915_driver_lastclose, - .preclose = i915_driver_preclose, - .save = i915_save, - .restore = i915_restore, - .device_is_agp = i915_driver_device_is_agp, - .get_vblank_counter = i915_get_vblank_counter, - .enable_vblank = i915_enable_vblank, - .disable_vblank = i915_disable_vblank, - .irq_preinstall = i915_driver_irq_preinstall, - .irq_postinstall = i915_driver_irq_postinstall, - .irq_uninstall = i915_driver_irq_uninstall, - .irq_handler = i915_driver_irq_handler, - .reclaim_buffers = drm_core_reclaim_buffers, - .get_map_ofs = drm_core_get_map_ofs, - .get_reg_ofs = drm_core_get_reg_ofs, - .fb_probe = intelfb_probe, - .fb_remove = intelfb_remove, - .fb_resize = intelfb_resize, - .master_create = i915_master_create, - .master_destroy = i915_master_destroy, -#if defined(CONFIG_DEBUG_FS) - .debugfs_init = i915_debugfs_init, - .debugfs_cleanup = i915_debugfs_cleanup, -#endif - .gem_init_object = i915_gem_init_object, - .gem_free_object = i915_gem_free_object, - .gem_vm_ops = &i915_gem_vm_ops, - .ioctls = i915_ioctls, - .fops = { - .owner = THIS_MODULE, - .open = drm_open, - .release = drm_release, - .ioctl = drm_ioctl, - .mmap = drm_mmap, - .poll = drm_poll, - .fasync = drm_fasync, -#ifdef CONFIG_COMPAT - .compat_ioctl = i915_compat_ioctl, -#endif - .llseek = noop_llseek, - }, - .pci_driver = { - .name = DRIVER_NAME, - .id_table = pciidlist, - .probe = probe, - .remove = __devexit_p(drm_cleanup_pci), - }, - .name = DRIVER_NAME, - .desc = DRIVER_DESC, - .date = DRIVER_DATE, - .major = DRIVER_MAJOR, - .minor = DRIVER_MINOR, - .patchlevel = DRIVER_PATCHLEVEL, - }; - - - In the example above, taken from the i915 DRM driver, the driver - sets several flags indicating what core features it supports; - we go over the individual callbacks in later sections. Since - flags indicate which features your driver supports to the DRM - core, you need to set most of them prior to calling drm_init(). Some, - like DRIVER_MODESET can be set later based on user supplied parameters, - but that's the exception rather than the rule. - - - Driver flags - - DRIVER_USE_AGP - - Driver uses AGP interface - - - - DRIVER_REQUIRE_AGP - - Driver needs AGP interface to function. - - - - DRIVER_USE_MTRR - - - Driver uses MTRR interface for mapping memory. Deprecated. - - - - - DRIVER_PCI_DMA - - Driver is capable of PCI DMA. Deprecated. - - - - DRIVER_SG - - Driver can perform scatter/gather DMA. Deprecated. - - - - DRIVER_HAVE_DMA - Driver supports DMA. Deprecated. - - - DRIVER_HAVE_IRQDRIVER_IRQ_SHARED - - - DRIVER_HAVE_IRQ indicates whether the driver has an IRQ - handler. DRIVER_IRQ_SHARED indicates whether the device & - handler support shared IRQs (note that this is required of - PCI drivers). - - - - - DRIVER_DMA_QUEUE - - - Should be set if the driver queues DMA requests and completes them - asynchronously. Deprecated. - - - - - DRIVER_FB_DMA - - - Driver supports DMA to/from the framebuffer. Deprecated. - - - - - DRIVER_MODESET - - - Driver supports mode setting interfaces. - - - - - - In this specific case, the driver requires AGP and supports - IRQs. DMA, as discussed later, is handled by device-specific ioctls - in this case. It also supports the kernel mode setting APIs, though - unlike in the actual i915 driver source, this example unconditionally - exports KMS capability. + Driver Initialization + + At the core of every DRM driver is a drm_driver + structure. Drivers typically statically initialize a drm_driver structure, + and then pass it to one of the drm_*_init() functions + to register it with the DRM subsystem. - - - - - - Driver load - - In the previous section, we saw what a typical drm_driver - structure might look like. One of the more important fields in - the structure is the hook for the load function. - - - static struct drm_driver driver = { - ... - .load = i915_driver_load, - ... - }; - - - The load function has many responsibilities: allocating a driver - private structure, specifying supported performance counters, - configuring the device (e.g. mapping registers & command - buffers), initializing the memory manager, and setting up the - initial output configuration. - - - If compatibility is a concern (e.g. with drivers converted over - to the new interfaces from the old ones), care must be taken to - prevent device initialization and control that is incompatible with - currently active userspace drivers. For instance, if user - level mode setting drivers are in use, it would be problematic - to perform output discovery & configuration at load time. - Likewise, if user-level drivers unaware of memory management are - in use, memory management and command buffer setup may need to - be omitted. These requirements are driver-specific, and care - needs to be taken to keep both old and new applications and - libraries working. The i915 driver supports the "modeset" - module parameter to control whether advanced features are - enabled at load time or in legacy fashion. + + The drm_driver structure contains static + information that describes the driver and features it supports, and + pointers to methods that the DRM core will call to implement the DRM API. + We will first go through the drm_driver static + information fields, and will then describe individual operations in + details as they get used in later sections. - - Driver private & performance counters - - The driver private hangs off the main drm_device structure and - can be used for tracking various device-specific bits of - information, like register offsets, command buffer status, - register state for suspend/resume, etc. At load time, a - driver may simply allocate one and set drm_device.dev_priv - appropriately; it should be freed and drm_device.dev_priv set - to NULL when the driver is unloaded. - + Driver Information + + Driver Features + + Drivers inform the DRM core about their requirements and supported + features by setting appropriate flags in the + driver_features field. Since those flags + influence the DRM core behaviour since registration time, most of them + must be set to registering the drm_driver + instance. + + u32 driver_features; + + Driver Feature Flags + + DRIVER_USE_AGP + + Driver uses AGP interface, the DRM core will manage AGP resources. + + + + DRIVER_REQUIRE_AGP + + Driver needs AGP interface to function. AGP initialization failure + will become a fatal error. + + + + DRIVER_USE_MTRR + + Driver uses MTRR interface for mapping memory, the DRM core will + manage MTRR resources. Deprecated. + + + + DRIVER_PCI_DMA + + Driver is capable of PCI DMA, mapping of PCI DMA buffers to + userspace will be enabled. Deprecated. + + + + DRIVER_SG + + Driver can perform scatter/gather DMA, allocation and mapping of + scatter/gather buffers will be enabled. Deprecated. + + + + DRIVER_HAVE_DMA + + Driver supports DMA, the userspace DMA API will be supported. + Deprecated. + + + + DRIVER_HAVE_IRQDRIVER_IRQ_SHARED + + DRIVER_HAVE_IRQ indicates whether the driver has an IRQ handler. The + DRM core will automatically register an interrupt handler when the + flag is set. DRIVER_IRQ_SHARED indicates whether the device & + handler support shared IRQs (note that this is required of PCI + drivers). + + + + DRIVER_IRQ_VBL + Unused. Deprecated. + + + DRIVER_DMA_QUEUE + + Should be set if the driver queues DMA requests and completes them + asynchronously. Deprecated. + + + + DRIVER_FB_DMA + + Driver supports DMA to/from the framebuffer, mapping of frambuffer + DMA buffers to userspace will be supported. Deprecated. + + + + DRIVER_IRQ_VBL2 + Unused. Deprecated. + + + DRIVER_GEM + + Driver use the GEM memory manager. + + + + DRIVER_MODESET + + Driver supports mode setting interfaces (KMS). + + + + DRIVER_PRIME + + Driver implements DRM PRIME buffer sharing. + + + + + + Major, Minor and Patchlevel + int major; +int minor; +int patchlevel; + + The DRM core identifies driver versions by a major, minor and patch + level triplet. The information is printed to the kernel log at + initialization time and passed to userspace through the + DRM_IOCTL_VERSION ioctl. + + + The major and minor numbers are also used to verify the requested driver + API version passed to DRM_IOCTL_SET_VERSION. When the driver API changes + between minor versions, applications can call DRM_IOCTL_SET_VERSION to + select a specific version of the API. If the requested major isn't equal + to the driver major, or the requested minor is larger than the driver + minor, the DRM_IOCTL_SET_VERSION call will return an error. Otherwise + the driver's set_version() method will be called with the requested + version. + + + + Name, Description and Date + char *name; +char *desc; +char *date; + + The driver name is printed to the kernel log at initialization time, + used for IRQ registration and passed to userspace through + DRM_IOCTL_VERSION. + + + The driver description is a purely informative string passed to + userspace through the DRM_IOCTL_VERSION ioctl and otherwise unused by + the kernel. + + + The driver date, formatted as YYYYMMDD, is meant to identify the date of + the latest modification to the driver. However, as most drivers fail to + update it, its value is mostly useless. The DRM core prints it to the + kernel log at initialization time and passes it to userspace through the + DRM_IOCTL_VERSION ioctl. + + + + + Driver Load - The DRM supports several counters which may be used for rough - performance characterization. Note that the DRM stat counter - system is not often used by applications, and supporting - additional counters is completely optional. + The load method is the driver and device + initialization entry point. The method is responsible for allocating and + initializing driver private data, specifying supported performance + counters, performing resource allocation and mapping (e.g. acquiring + clocks, mapping registers or allocating command buffers), initializing + the memory manager (), installing + the IRQ handler (), setting up + vertical blanking handling (), mode + setting () and initial output + configuration (). + + If compatibility is a concern (e.g. with drivers converted over from + User Mode Setting to Kernel Mode Setting), care must be taken to prevent + device initialization and control that is incompatible with currently + active userspace drivers. For instance, if user level mode setting + drivers are in use, it would be problematic to perform output discovery + & configuration at load time. Likewise, if user-level drivers + unaware of memory management are in use, memory management and command + buffer setup may need to be omitted. These requirements are + driver-specific, and care needs to be taken to keep both old and new + applications and libraries working. + + int (*load) (struct drm_device *, unsigned long flags); - These interfaces are deprecated and should not be used. If performance - monitoring is desired, the developer should investigate and - potentially enhance the kernel perf and tracing infrastructure to export - GPU related performance information for consumption by performance - monitoring tools and applications. + The method takes two arguments, a pointer to the newly created + drm_device and flags. The flags are used to + pass the driver_data field of the device id + corresponding to the device passed to drm_*_init(). + Only PCI devices currently use this, USB and platform DRM drivers have + their load method called with flags to 0. + + Driver Private & Performance Counters + + The driver private hangs off the main + drm_device structure and can be used for + tracking various device-specific bits of information, like register + offsets, command buffer status, register state for suspend/resume, etc. + At load time, a driver may simply allocate one and set + drm_device.dev_priv + appropriately; it should be freed and + drm_device.dev_priv + set to NULL when the driver is unloaded. + + + DRM supports several counters which were used for rough performance + characterization. This stat counter system is deprecated and should not + be used. If performance monitoring is desired, the developer should + investigate and potentially enhance the kernel perf and tracing + infrastructure to export GPU related performance information for + consumption by performance monitoring tools and applications. + + + + IRQ Registration + + The DRM core tries to facilitate IRQ handler registration and + unregistration by providing drm_irq_install and + drm_irq_uninstall functions. Those functions only + support a single interrupt per device. + + + + Both functions get the device IRQ by calling + drm_dev_to_irq. This inline function will call a + bus-specific operation to retrieve the IRQ number. For platform devices, + platform_get_irq(..., 0) is used to retrieve the + IRQ number. + + + drm_irq_install starts by calling the + irq_preinstall driver operation. The operation + is optional and must make sure that the interrupt will not get fired by + clearing all pending interrupt flags or disabling the interrupt. + + + The IRQ will then be requested by a call to + request_irq. If the DRIVER_IRQ_SHARED driver + feature flag is set, a shared (IRQF_SHARED) IRQ handler will be + requested. + + + The IRQ handler function must be provided as the mandatory irq_handler + driver operation. It will get passed directly to + request_irq and thus has the same prototype as all + IRQ handlers. It will get called with a pointer to the DRM device as the + second argument. + + + Finally the function calls the optional + irq_postinstall driver operation. The operation + usually enables interrupts (excluding the vblank interrupt, which is + enabled separately), but drivers may choose to enable/disable interrupts + at a different time. + + + drm_irq_uninstall is similarly used to uninstall an + IRQ handler. It starts by waking up all processes waiting on a vblank + interrupt to make sure they don't hang, and then calls the optional + irq_uninstall driver operation. The operation + must disable all hardware interrupts. Finally the function frees the IRQ + by calling free_irq. + + + + Memory Manager Initialization + + Every DRM driver requires a memory manager which must be initialized at + load time. DRM currently contains two memory managers, the Translation + Table Manager (TTM) and the Graphics Execution Manager (GEM). + This document describes the use of the GEM memory manager only. See + for details. + + + + Miscellaneous Device Configuration + + Another task that may be necessary for PCI devices during configuration + is mapping the video BIOS. On many devices, the VBIOS describes device + configuration, LCD panel timings (if any), and contains flags indicating + device state. Mapping the BIOS can be done using the pci_map_rom() call, + a convenience function that takes care of mapping the actual ROM, + whether it has been shadowed into memory (typically at address 0xc0000) + or exists on the PCI device in the ROM BAR. Note that after the ROM has + been mapped and any necessary information has been extracted, it should + be unmapped; on many devices, the ROM address decoder is shared with + other BARs, so leaving it mapped could cause undesired behaviour like + hangs or memory corruption. + + + + - - Configuring the device - - Obviously, device configuration is device-specific. - However, there are several common operations: finding a - device's PCI resources, mapping them, and potentially setting - up an IRQ handler. - - - Finding & mapping resources is fairly straightforward. The - DRM wrapper functions, drm_get_resource_start() and - drm_get_resource_len(), may be used to find BARs on the given - drm_device struct. Once those values have been retrieved, the - driver load function can call drm_addmap() to create a new - mapping for the BAR in question. Note that you probably want a - drm_local_map_t in your driver private structure to track any - mappings you create. - - - - - if compatibility with other operating systems isn't a concern - (DRM drivers can run under various BSD variants and OpenSolaris), - native Linux calls may be used for the above, e.g. pci_resource_* - and iomap*/iounmap. See the Linux device driver book for more - info. - - - Once you have a register map, you may use the DRM_READn() and - DRM_WRITEn() macros to access the registers on your device, or - use driver-specific versions to offset into your MMIO space - relative to a driver-specific base pointer (see I915_READ for - an example). - - - If your device supports interrupt generation, you may want to - set up an interrupt handler when the driver is loaded. This - is done using the drm_irq_install() function. If your device - supports vertical blank interrupts, it should call - drm_vblank_init() to initialize the core vblank handling code before - enabling interrupts on your device. This ensures the vblank related - structures are allocated and allows the core to handle vblank events. - - - - Once your interrupt handler is registered (it uses your - drm_driver.irq_handler as the actual interrupt handling - function), you can safely enable interrupts on your device, - assuming any other state your interrupt handler uses is also - initialized. - - - Another task that may be necessary during configuration is - mapping the video BIOS. On many devices, the VBIOS describes - device configuration, LCD panel timings (if any), and contains - flags indicating device state. Mapping the BIOS can be done - using the pci_map_rom() call, a convenience function that - takes care of mapping the actual ROM, whether it has been - shadowed into memory (typically at address 0xc0000) or exists - on the PCI device in the ROM BAR. Note that after the ROM - has been mapped and any necessary information has been extracted, - it should be unmapped; on many devices, the ROM address decoder is - shared with other BARs, so leaving it mapped could cause - undesired behavior like hangs or memory corruption. - - - + + + Memory management + + Modern Linux systems require large amount of graphics memory to store + frame buffers, textures, vertices and other graphics-related data. Given + the very dynamic nature of many of that data, managing graphics memory + efficiently is thus crucial for the graphics stack and plays a central + role in the DRM infrastructure. + + + The DRM core includes two memory managers, namely Translation Table Maps + (TTM) and Graphics Execution Manager (GEM). TTM was the first DRM memory + manager to be developed and tried to be a one-size-fits-them all + solution. It provides a single userspace API to accomodate the need of + all hardware, supporting both Unified Memory Architecture (UMA) devices + and devices with dedicated video RAM (i.e. most discrete video cards). + This resulted in a large, complex piece of code that turned out to be + hard to use for driver development. + + + GEM started as an Intel-sponsored project in reaction to TTM's + complexity. Its design philosophy is completely different: instead of + providing a solution to every graphics memory-related problems, GEM + identified common code between drivers and created a support library to + share it. GEM has simpler initialization and execution requirements than + TTM, but has no video RAM management capabitilies and is thus limited to + UMA devices. + - Memory manager initialization - - In order to allocate command buffers, cursor memory, scanout - buffers, etc., as well as support the latest features provided - by packages like Mesa and the X.Org X server, your driver - should support a memory manager. - + The Translation Table Manager (TTM) - If your driver supports memory management (it should!), you - need to set that up at load time as well. How you initialize - it depends on which memory manager you're using: TTM or GEM. + TTM design background and information belongs here. TTM initialization - - TTM (for Translation Table Manager) manages video memory and - aperture space for graphics devices. TTM supports both UMA devices - and devices with dedicated video RAM (VRAM), i.e. most discrete - graphics devices. If your device has dedicated RAM, supporting - TTM is desirable. TTM also integrates tightly with your - driver-specific buffer execution function. See the radeon - driver for examples. - - - The core TTM structure is the ttm_bo_driver struct. It contains - several fields with function pointers for initializing the TTM, - allocating and freeing memory, waiting for command completion - and fence synchronization, and memory migration. See the - radeon_ttm.c file for an example of usage. + This section is outdated. + + Drivers wishing to support TTM must fill out a drm_bo_driver + structure. The structure contains several fields with function + pointers for initializing the TTM, allocating and freeing memory, + waiting for command completion and fence synchronization, and memory + migration. See the radeon_ttm.c file for an example of usage. The ttm_global_reference structure is made up of several fields: @@ -445,82 +500,1081 @@ count for the TTM, which will call your initialization function. + + + The Graphics Execution Manager (GEM) + + The GEM design approach has resulted in a memory manager that doesn't + provide full coverage of all (or even all common) use cases in its + userspace or kernel API. GEM exposes a set of standard memory-related + operations to userspace and a set of helper functions to drivers, and let + drivers implement hardware-specific operations with their own private API. + + + The GEM userspace API is described in the + GEM - the Graphics + Execution Manager article on LWN. While slightly + outdated, the document provides a good overview of the GEM API principles. + Buffer allocation and read and write operations, described as part of the + common GEM API, are currently implemented using driver-specific ioctls. + + + GEM is data-agnostic. It manages abstract buffer objects without knowing + what individual buffers contain. APIs that require knowledge of buffer + contents or purpose, such as buffer allocation or synchronization + primitives, are thus outside of the scope of GEM and must be implemented + using driver-specific ioctls. + + + On a fundamental level, GEM involves several operations: + + Memory allocation and freeing + Command execution + Aperture management at command execution time + + Buffer object allocation is relatively straightforward and largely + provided by Linux's shmem layer, which provides memory to back each + object. + + + Device-specific operations, such as command execution, pinning, buffer + read & write, mapping, and domain ownership transfers are left to + driver-specific ioctls. + + + GEM Initialization + + Drivers that use GEM must set the DRIVER_GEM bit in the struct + drm_driver + driver_features field. The DRM core will + then automatically initialize the GEM core before calling the + load operation. Behind the scene, this will + create a DRM Memory Manager object which provides an address space + pool for object allocation. + + + In a KMS configuration, drivers need to allocate and initialize a + command ring buffer following core GEM initialization if required by + the hardware. UMA devices usually have what is called a "stolen" + memory region, which provides space for the initial framebuffer and + large, contiguous memory regions required by the device. This space is + typically not managed by GEM, and must be initialized separately into + its own DRM MM object. + + - GEM initialization - - GEM is an alternative to TTM, designed specifically for UMA - devices. It has simpler initialization and execution requirements - than TTM, but has no VRAM management capability. Core GEM - is initialized by calling drm_mm_init() to create - a GTT DRM MM object, which provides an address space pool for - object allocation. In a KMS configuration, the driver - needs to allocate and initialize a command ring buffer following - core GEM initialization. A UMA device usually has what is called a - "stolen" memory region, which provides space for the initial - framebuffer and large, contiguous memory regions required by the - device. This space is not typically managed by GEM, and it must - be initialized separately into its own DRM MM object. - - - Initialization is driver-specific. In the case of Intel - integrated graphics chips like 965GM, GEM initialization can - be done by calling the internal GEM init function, - i915_gem_do_init(). Since the 965GM is a UMA device - (i.e. it doesn't have dedicated VRAM), GEM manages - making regular RAM available for GPU operations. Memory set - aside by the BIOS (called "stolen" memory by the i915 - driver) is managed by the DRM memrange allocator; the - rest of the aperture is managed by GEM. - - /* Basic memrange allocator for stolen space (aka vram) */ - drm_memrange_init(&dev_priv->vram, 0, prealloc_size); - /* Let GEM Manage from end of prealloc space to end of aperture */ - i915_gem_do_init(dev, prealloc_size, agp_size); - - - - - Once the memory manager has been set up, we may allocate the - command buffer. In the i915 case, this is also done with a - GEM function, i915_gem_init_ringbuffer(). - + GEM Objects Creation + + GEM splits creation of GEM objects and allocation of the memory that + backs them in two distinct operations. + + + GEM objects are represented by an instance of struct + drm_gem_object. Drivers usually need to extend + GEM objects with private information and thus create a driver-specific + GEM object structure type that embeds an instance of struct + drm_gem_object. + + + To create a GEM object, a driver allocates memory for an instance of its + specific GEM object type and initializes the embedded struct + drm_gem_object with a call to + drm_gem_object_init. The function takes a pointer to + the DRM device, a pointer to the GEM object and the buffer object size + in bytes. + + + GEM uses shmem to allocate anonymous pageable memory. + drm_gem_object_init will create an shmfs file of + the requested size and store it into the struct + drm_gem_object filp + field. The memory is used as either main storage for the object when the + graphics hardware uses system memory directly or as a backing store + otherwise. + + + Drivers are responsible for the actual physical pages allocation by + calling shmem_read_mapping_page_gfp for each page. + Note that they can decide to allocate pages when initializing the GEM + object, or to delay allocation until the memory is needed (for instance + when a page fault occurs as a result of a userspace memory access or + when the driver needs to start a DMA transfer involving the memory). + + + Anonymous pageable memory allocation is not always desired, for instance + when the hardware requires physically contiguous system memory as is + often the case in embedded devices. Drivers can create GEM objects with + no shmfs backing (called private GEM objects) by initializing them with + a call to drm_gem_private_object_init instead of + drm_gem_object_init. Storage for private GEM + objects must be managed by drivers. + + + Drivers that do not need to extend GEM objects with private information + can call the drm_gem_object_alloc function to + allocate and initialize a struct drm_gem_object + instance. The GEM core will call the optional driver + gem_init_object operation after initializing + the GEM object with drm_gem_object_init. + int (*gem_init_object) (struct drm_gem_object *obj); + + + No alloc-and-init function exists for private GEM objects. + + + + GEM Objects Lifetime + + All GEM objects are reference-counted by the GEM core. References can be + acquired and release by calling drm_gem_object_reference + and drm_gem_object_unreference respectively. The + caller must hold the drm_device + struct_mutex lock. As a convenience, GEM + provides the drm_gem_object_reference_unlocked and + drm_gem_object_unreference_unlocked functions that + can be called without holding the lock. + + + When the last reference to a GEM object is released the GEM core calls + the drm_driver + gem_free_object operation. That operation is + mandatory for GEM-enabled drivers and must free the GEM object and all + associated resources. + + + void (*gem_free_object) (struct drm_gem_object *obj); + Drivers are responsible for freeing all GEM object resources, including + the resources created by the GEM core. If an mmap offset has been + created for the object (in which case + drm_gem_object::map_list::map + is not NULL) it must be freed by a call to + drm_gem_free_mmap_offset. The shmfs backing store + must be released by calling drm_gem_object_release + (that function can safely be called if no shmfs backing store has been + created). + + + + GEM Objects Naming + + Communication between userspace and the kernel refers to GEM objects + using local handles, global names or, more recently, file descriptors. + All of those are 32-bit integer values; the usual Linux kernel limits + apply to the file descriptors. + + + GEM handles are local to a DRM file. Applications get a handle to a GEM + object through a driver-specific ioctl, and can use that handle to refer + to the GEM object in other standard or driver-specific ioctls. Closing a + DRM file handle frees all its GEM handles and dereferences the + associated GEM objects. + + + To create a handle for a GEM object drivers call + drm_gem_handle_create. The function takes a pointer + to the DRM file and the GEM object and returns a locally unique handle. + When the handle is no longer needed drivers delete it with a call to + drm_gem_handle_delete. Finally the GEM object + associated with a handle can be retrieved by a call to + drm_gem_object_lookup. + + + Handles don't take ownership of GEM objects, they only take a reference + to the object that will be dropped when the handle is destroyed. To + avoid leaking GEM objects, drivers must make sure they drop the + reference(s) they own (such as the initial reference taken at object + creation time) as appropriate, without any special consideration for the + handle. For example, in the particular case of combined GEM object and + handle creation in the implementation of the + dumb_create operation, drivers must drop the + initial reference to the GEM object before returning the handle. + + + GEM names are similar in purpose to handles but are not local to DRM + files. They can be passed between processes to reference a GEM object + globally. Names can't be used directly to refer to objects in the DRM + API, applications must convert handles to names and names to handles + using the DRM_IOCTL_GEM_FLINK and DRM_IOCTL_GEM_OPEN ioctls + respectively. The conversion is handled by the DRM core without any + driver-specific support. + + + Similar to global names, GEM file descriptors are also used to share GEM + objects across processes. They offer additional security: as file + descriptors must be explictly sent over UNIX domain sockets to be shared + between applications, they can't be guessed like the globally unique GEM + names. + + + Drivers that support GEM file descriptors, also known as the DRM PRIME + API, must set the DRIVER_PRIME bit in the struct + drm_driver + driver_features field, and implement the + prime_handle_to_fd and + prime_fd_to_handle operations. + + + int (*prime_handle_to_fd)(struct drm_device *dev, + struct drm_file *file_priv, uint32_t handle, + uint32_t flags, int *prime_fd); + int (*prime_fd_to_handle)(struct drm_device *dev, + struct drm_file *file_priv, int prime_fd, + uint32_t *handle); + Those two operations convert a handle to a PRIME file descriptor and + vice versa. Drivers must use the kernel dma-buf buffer sharing framework + to manage the PRIME file descriptors. + + + While non-GEM drivers must implement the operations themselves, GEM + drivers must use the drm_gem_prime_handle_to_fd + and drm_gem_prime_fd_to_handle helper functions. + Those helpers rely on the driver + gem_prime_export and + gem_prime_import operations to create a dma-buf + instance from a GEM object (dma-buf exporter role) and to create a GEM + object from a dma-buf instance (dma-buf importer role). + + + struct dma_buf * (*gem_prime_export)(struct drm_device *dev, + struct drm_gem_object *obj, + int flags); + struct drm_gem_object * (*gem_prime_import)(struct drm_device *dev, + struct dma_buf *dma_buf); + These two operations are mandatory for GEM drivers that support DRM + PRIME. + + + + GEM Objects Mapping + + Because mapping operations are fairly heavyweight GEM favours + read/write-like access to buffers, implemented through driver-specific + ioctls, over mapping buffers to userspace. However, when random access + to the buffer is needed (to perform software rendering for instance), + direct access to the object can be more efficient. + + + The mmap system call can't be used directly to map GEM objects, as they + don't have their own file handle. Two alternative methods currently + co-exist to map GEM objects to userspace. The first method uses a + driver-specific ioctl to perform the mapping operation, calling + do_mmap under the hood. This is often considered + dubious, seems to be discouraged for new GEM-enabled drivers, and will + thus not be described here. + + + The second method uses the mmap system call on the DRM file handle. + void *mmap(void *addr, size_t length, int prot, int flags, int fd, + off_t offset); + DRM identifies the GEM object to be mapped by a fake offset passed + through the mmap offset argument. Prior to being mapped, a GEM object + must thus be associated with a fake offset. To do so, drivers must call + drm_gem_create_mmap_offset on the object. The + function allocates a fake offset range from a pool and stores the + offset divided by PAGE_SIZE in + obj->map_list.hash.key. Care must be taken not to + call drm_gem_create_mmap_offset if a fake offset + has already been allocated for the object. This can be tested by + obj->map_list.map being non-NULL. + + + Once allocated, the fake offset value + (obj->map_list.hash.key << PAGE_SHIFT) + must be passed to the application in a driver-specific way and can then + be used as the mmap offset argument. + + + The GEM core provides a helper method drm_gem_mmap + to handle object mapping. The method can be set directly as the mmap + file operation handler. It will look up the GEM object based on the + offset value and set the VMA operations to the + drm_driver gem_vm_ops + field. Note that drm_gem_mmap doesn't map memory to + userspace, but relies on the driver-provided fault handler to map pages + individually. + + + To use drm_gem_mmap, drivers must fill the struct + drm_driver gem_vm_ops + field with a pointer to VM operations. + + + struct vm_operations_struct *gem_vm_ops + + struct vm_operations_struct { + void (*open)(struct vm_area_struct * area); + void (*close)(struct vm_area_struct * area); + int (*fault)(struct vm_area_struct *vma, struct vm_fault *vmf); + }; + + + The open and close + operations must update the GEM object reference count. Drivers can use + the drm_gem_vm_open and + drm_gem_vm_close helper functions directly as open + and close handlers. + + + The fault operation handler is responsible for mapping individual pages + to userspace when a page fault occurs. Depending on the memory + allocation scheme, drivers can allocate pages at fault time, or can + decide to allocate memory for the GEM object at the time the object is + created. + + + Drivers that want to map the GEM object upfront instead of handling page + faults can implement their own mmap file operation handler. + + + + Dumb GEM Objects + + The GEM API doesn't standardize GEM objects creation and leaves it to + driver-specific ioctls. While not an issue for full-fledged graphics + stacks that include device-specific userspace components (in libdrm for + instance), this limit makes DRM-based early boot graphics unnecessarily + complex. + + + Dumb GEM objects partly alleviate the problem by providing a standard + API to create dumb buffers suitable for scanout, which can then be used + to create KMS frame buffers. + + + To support dumb GEM objects drivers must implement the + dumb_create, + dumb_destroy and + dumb_map_offset operations. + + + + int (*dumb_create)(struct drm_file *file_priv, struct drm_device *dev, + struct drm_mode_create_dumb *args); + + The dumb_create operation creates a GEM + object suitable for scanout based on the width, height and depth + from the struct drm_mode_create_dumb + argument. It fills the argument's handle, + pitch and size + fields with a handle for the newly created GEM object and its line + pitch and size in bytes. + + + + int (*dumb_destroy)(struct drm_file *file_priv, struct drm_device *dev, + uint32_t handle); + + The dumb_destroy operation destroys a dumb + GEM object created by dumb_create. + + + + int (*dumb_map_offset)(struct drm_file *file_priv, struct drm_device *dev, + uint32_t handle, uint64_t *offset); + + The dumb_map_offset operation associates an + mmap fake offset with the GEM object given by the handle and returns + it. Drivers must use the + drm_gem_create_mmap_offset function to + associate the fake offset as described in + . + + + + + + Memory Coherency + + When mapped to the device or used in a command buffer, backing pages + for an object are flushed to memory and marked write combined so as to + be coherent with the GPU. Likewise, if the CPU accesses an object + after the GPU has finished rendering to the object, then the object + must be made coherent with the CPU's view of memory, usually involving + GPU cache flushing of various kinds. This core CPU<->GPU + coherency management is provided by a device-specific ioctl, which + evaluates an object's current domain and performs any necessary + flushing or synchronization to put the object into the desired + coherency domain (note that the object may be busy, i.e. an active + render target; in that case, setting the domain blocks the client and + waits for rendering to complete before performing any necessary + flushing operations). + + + + Command Execution + + Perhaps the most important GEM function for GPU devices is providing a + command execution interface to clients. Client programs construct + command buffers containing references to previously allocated memory + objects, and then submit them to GEM. At that point, GEM takes care to + bind all the objects into the GTT, execute the buffer, and provide + necessary synchronization between clients accessing the same buffers. + This often involves evicting some objects from the GTT and re-binding + others (a fairly expensive operation), and providing relocation + support which hides fixed GTT offsets from clients. Clients must take + care not to submit command buffers that reference more objects than + can fit in the GTT; otherwise, GEM will reject them and no rendering + will occur. Similarly, if several objects in the buffer require fence + registers to be allocated for correct rendering (e.g. 2D blits on + pre-965 chips), care must be taken not to require more fence registers + than are available to the client. Such resource management should be + abstracted from the client in libdrm. + + + + + + Mode Setting + + Drivers must initialize the mode setting core by calling + drm_mode_config_init on the DRM device. The function + initializes the drm_device + mode_config field and never fails. Once done, + mode configuration must be setup by initializing the following fields. + + + + int min_width, min_height; +int max_width, max_height; + + Minimum and maximum width and height of the frame buffers in pixel + units. + + + + struct drm_mode_config_funcs *funcs; + Mode setting functions. + + - Output configuration + Frame Buffer Creation + struct drm_framebuffer *(*fb_create)(struct drm_device *dev, + struct drm_file *file_priv, + struct drm_mode_fb_cmd2 *mode_cmd); - The final initialization task is output configuration. This involves: - - - Finding and initializing the CRTCs, encoders, and connectors - for the device. - - - Creating an initial configuration. - - - Registering a framebuffer console driver. - - + Frame buffers are abstract memory objects that provide a source of + pixels to scanout to a CRTC. Applications explicitly request the + creation of frame buffers through the DRM_IOCTL_MODE_ADDFB(2) ioctls and + receive an opaque handle that can be passed to the KMS CRTC control, + plane configuration and page flip functions. + + + Frame buffers rely on the underneath memory manager for low-level memory + operations. When creating a frame buffer applications pass a memory + handle (or a list of memory handles for multi-planar formats) through + the drm_mode_fb_cmd2 argument. This document + assumes that the driver uses GEM, those handles thus reference GEM + objects. + + + Drivers must first validate the requested frame buffer parameters passed + through the mode_cmd argument. In particular this is where invalid + sizes, pixel formats or pitches can be caught. + + + If the parameters are deemed valid, drivers then create, initialize and + return an instance of struct drm_framebuffer. + If desired the instance can be embedded in a larger driver-specific + structure. The new instance is initialized with a call to + drm_framebuffer_init which takes a pointer to DRM + frame buffer operations (struct + drm_framebuffer_funcs). Frame buffer operations are + + + int (*create_handle)(struct drm_framebuffer *fb, + struct drm_file *file_priv, unsigned int *handle); + + Create a handle to the frame buffer underlying memory object. If + the frame buffer uses a multi-plane format, the handle will + reference the memory object associated with the first plane. + + + Drivers call drm_gem_handle_create to create + the handle. + + + + void (*destroy)(struct drm_framebuffer *framebuffer); + + Destroy the frame buffer object and frees all associated + resources. Drivers must call + drm_framebuffer_cleanup to free resources + allocated by the DRM core for the frame buffer object, and must + make sure to unreference all memory objects associated with the + frame buffer. Handles created by the + create_handle operation are released by + the DRM core. + + + + int (*dirty)(struct drm_framebuffer *framebuffer, + struct drm_file *file_priv, unsigned flags, unsigned color, + struct drm_clip_rect *clips, unsigned num_clips); + + This optional operation notifies the driver that a region of the + frame buffer has changed in response to a DRM_IOCTL_MODE_DIRTYFB + ioctl call. + + + + + + After initializing the drm_framebuffer + instance drivers must fill its width, + height, pitches, + offsets, depth, + bits_per_pixel and + pixel_format fields from the values passed + through the drm_mode_fb_cmd2 argument. They + should call the drm_helper_mode_fill_fb_struct + helper function to do so. + + + + Output Polling + void (*output_poll_changed)(struct drm_device *dev); + + This operation notifies the driver that the status of one or more + connectors has changed. Drivers that use the fb helper can just call the + drm_fb_helper_hotplug_event function to handle this + operation. + + + + + + + + KMS Initialization and Cleanup + + A KMS device is abstracted and exposed as a set of planes, CRTCs, encoders + and connectors. KMS drivers must thus create and initialize all those + objects at load time after initializing mode setting. + + + CRTCs (struct <structname>drm_crtc</structname>) + + A CRTC is an abstraction representing a part of the chip that contains a + pointer to a scanout buffer. Therefore, the number of CRTCs available + determines how many independent scanout buffers can be active at any + given time. The CRTC structure contains several fields to support this: + a pointer to some video memory (abstracted as a frame buffer object), a + display mode, and an (x, y) offset into the video memory to support + panning or configurations where one piece of video memory spans multiple + CRTCs. - Output discovery and initialization - - Several core functions exist to create CRTCs, encoders, and - connectors, namely: drm_crtc_init(), drm_connector_init(), and - drm_encoder_init(), along with several "helper" functions to - perform common tasks. - - - Connectors should be registered with sysfs once they've been - detected and initialized, using the - drm_sysfs_connector_add() function. Likewise, when they're - removed from the system, they should be destroyed with - drm_sysfs_connector_remove(). - - -CRTC Initialization + + A KMS device must create and register at least one struct + drm_crtc instance. The instance is allocated + and zeroed by the driver, possibly as part of a larger structure, and + registered with a call to drm_crtc_init with a + pointer to CRTC functions. + + + + CRTC Operations + + Set Configuration + int (*set_config)(struct drm_mode_set *set); + + Apply a new CRTC configuration to the device. The configuration + specifies a CRTC, a frame buffer to scan out from, a (x,y) position in + the frame buffer, a display mode and an array of connectors to drive + with the CRTC if possible. + + + If the frame buffer specified in the configuration is NULL, the driver + must detach all encoders connected to the CRTC and all connectors + attached to those encoders and disable them. + + + This operation is called with the mode config lock held. + + + FIXME: How should set_config interact with DPMS? If the CRTC is + suspended, should it be resumed? + + + + Page Flipping + int (*page_flip)(struct drm_crtc *crtc, struct drm_framebuffer *fb, + struct drm_pending_vblank_event *event); + + Schedule a page flip to the given frame buffer for the CRTC. This + operation is called with the mode config mutex held. + + + Page flipping is a synchronization mechanism that replaces the frame + buffer being scanned out by the CRTC with a new frame buffer during + vertical blanking, avoiding tearing. When an application requests a page + flip the DRM core verifies that the new frame buffer is large enough to + be scanned out by the CRTC in the currently configured mode and then + calls the CRTC page_flip operation with a + pointer to the new frame buffer. + + + The page_flip operation schedules a page flip. + Once any pending rendering targetting the new frame buffer has + completed, the CRTC will be reprogrammed to display that frame buffer + after the next vertical refresh. The operation must return immediately + without waiting for rendering or page flip to complete and must block + any new rendering to the frame buffer until the page flip completes. + + + If a page flip is already pending, the + page_flip operation must return + -EBUSY. + + + To synchronize page flip to vertical blanking the driver will likely + need to enable vertical blanking interrupts. It should call + drm_vblank_get for that purpose, and call + drm_vblank_put after the page flip completes. + + + If the application has requested to be notified when page flip completes + the page_flip operation will be called with a + non-NULL event argument pointing to a + drm_pending_vblank_event instance. Upon page + flip completion the driver must fill the + event::event + sequence, tv_sec + and tv_usec fields with the associated + vertical blanking count and timestamp, add the event to the + drm_file list of events to be signaled, and wake + up any waiting process. This can be performed with + event.sequence = drm_vblank_count_and_time(..., &now); + event->event.tv_sec = now.tv_sec; + event->event.tv_usec = now.tv_usec; + + spin_lock_irqsave(&dev->event_lock, flags); + list_add_tail(&event->base.link, &event->base.file_priv->event_list); + wake_up_interruptible(&event->base.file_priv->event_wait); + spin_unlock_irqrestore(&dev->event_lock, flags); + ]]> + + + FIXME: Could drivers that don't need to wait for rendering to complete + just add the event to dev->vblank_event_list and + let the DRM core handle everything, as for "normal" vertical blanking + events? + + + While waiting for the page flip to complete, the + event->base.link list head can be used freely by + the driver to store the pending event in a driver-specific list. + + + If the file handle is closed before the event is signaled, drivers must + take care to destroy the event in their + preclose operation (and, if needed, call + drm_vblank_put). + + + + Miscellaneous + + + void (*gamma_set)(struct drm_crtc *crtc, u16 *r, u16 *g, u16 *b, + uint32_t start, uint32_t size); + + Apply a gamma table to the device. The operation is optional. + + + + void (*destroy)(struct drm_crtc *crtc); + + Destroy the CRTC when not needed anymore. See + . + + + + + + + + Planes (struct <structname>drm_plane</structname>) + + A plane represents an image source that can be blended with or overlayed + on top of a CRTC during the scanout process. Planes are associated with + a frame buffer to crop a portion of the image memory (source) and + optionally scale it to a destination size. The result is then blended + with or overlayed on top of a CRTC. + + + Plane Initialization + + Planes are optional. To create a plane, a KMS drivers allocates and + zeroes an instances of struct drm_plane + (possibly as part of a larger structure) and registers it with a call + to drm_plane_init. The function takes a bitmask + of the CRTCs that can be associated with the plane, a pointer to the + plane functions and a list of format supported formats. + + + + Plane Operations + + + int (*update_plane)(struct drm_plane *plane, struct drm_crtc *crtc, + struct drm_framebuffer *fb, int crtc_x, int crtc_y, + unsigned int crtc_w, unsigned int crtc_h, + uint32_t src_x, uint32_t src_y, + uint32_t src_w, uint32_t src_h); + + Enable and configure the plane to use the given CRTC and frame buffer. + + + The source rectangle in frame buffer memory coordinates is given by + the src_x, src_y, + src_w and src_h + parameters (as 16.16 fixed point values). Devices that don't support + subpixel plane coordinates can ignore the fractional part. + + + The destination rectangle in CRTC coordinates is given by the + crtc_x, crtc_y, + crtc_w and crtc_h + parameters (as integer values). Devices scale the source rectangle to + the destination rectangle. If scaling is not supported, and the source + rectangle size doesn't match the destination rectangle size, the + driver must return a -EINVAL error. + + + + int (*disable_plane)(struct drm_plane *plane); + + Disable the plane. The DRM core calls this method in response to a + DRM_IOCTL_MODE_SETPLANE ioctl call with the frame buffer ID set to 0. + Disabled planes must not be processed by the CRTC. + + + + void (*destroy)(struct drm_plane *plane); + + Destroy the plane when not needed anymore. See + . + + + + + + + Encoders (struct <structname>drm_encoder</structname>) + + An encoder takes pixel data from a CRTC and converts it to a format + suitable for any attached connectors. On some devices, it may be + possible to have a CRTC send data to more than one encoder. In that + case, both encoders would receive data from the same scanout buffer, + resulting in a "cloned" display configuration across the connectors + attached to each encoder. + + + Encoder Initialization + + As for CRTCs, a KMS driver must create, initialize and register at + least one struct drm_encoder instance. The + instance is allocated and zeroed by the driver, possibly as part of a + larger structure. + + + Drivers must initialize the struct drm_encoder + possible_crtcs and + possible_clones fields before registering the + encoder. Both fields are bitmasks of respectively the CRTCs that the + encoder can be connected to, and sibling encoders candidate for cloning. + + + After being initialized, the encoder must be registered with a call to + drm_encoder_init. The function takes a pointer to + the encoder functions and an encoder type. Supported types are + + + DRM_MODE_ENCODER_DAC for VGA and analog on DVI-I/DVI-A + + + DRM_MODE_ENCODER_TMDS for DVI, HDMI and (embedded) DisplayPort + + + DRM_MODE_ENCODER_LVDS for display panels + + + DRM_MODE_ENCODER_TVDAC for TV output (Composite, S-Video, Component, + SCART) + + + DRM_MODE_ENCODER_VIRTUAL for virtual machine displays + + + + + Encoders must be attached to a CRTC to be used. DRM drivers leave + encoders unattached at initialization time. Applications (or the fbdev + compatibility layer when implemented) are responsible for attaching the + encoders they want to use to a CRTC. + + + + Encoder Operations + + + void (*destroy)(struct drm_encoder *encoder); + + Called to destroy the encoder when not needed anymore. See + . + + + + + + + Connectors (struct <structname>drm_connector</structname>) + + A connector is the final destination for pixel data on a device, and + usually connects directly to an external display device like a monitor + or laptop panel. A connector can only be attached to one encoder at a + time. The connector is also the structure where information about the + attached display is kept, so it contains fields for display data, EDID + data, DPMS & connection status, and information about modes + supported on the attached displays. + + + Connector Initialization + + Finally a KMS driver must create, initialize, register and attach at + least one struct drm_connector instance. The + instance is created as other KMS objects and initialized by setting the + following fields. + + + + interlace_allowed + + Whether the connector can handle interlaced modes. + + + + doublescan_allowed + + Whether the connector can handle doublescan. + + + + display_info + + + Display information is filled from EDID information when a display + is detected. For non hot-pluggable displays such as flat panels in + embedded systems, the driver should initialize the + display_info.width_mm + and + display_info.height_mm + fields with the physical size of the display. + + + + polled + + Connector polling mode, a combination of + + + DRM_CONNECTOR_POLL_HPD + + The connector generates hotplug events and doesn't need to be + periodically polled. The CONNECT and DISCONNECT flags must not + be set together with the HPD flag. + + + + DRM_CONNECTOR_POLL_CONNECT + + Periodically poll the connector for connection. + + + + DRM_CONNECTOR_POLL_DISCONNECT + + Periodically poll the connector for disconnection. + + + + Set to 0 for connectors that don't support connection status + discovery. + + + + + The connector is then registered with a call to + drm_connector_init with a pointer to the connector + functions and a connector type, and exposed through sysfs with a call to + drm_sysfs_connector_add. + + + Supported connector types are + + DRM_MODE_CONNECTOR_VGA + DRM_MODE_CONNECTOR_DVII + DRM_MODE_CONNECTOR_DVID + DRM_MODE_CONNECTOR_DVIA + DRM_MODE_CONNECTOR_Composite + DRM_MODE_CONNECTOR_SVIDEO + DRM_MODE_CONNECTOR_LVDS + DRM_MODE_CONNECTOR_Component + DRM_MODE_CONNECTOR_9PinDIN + DRM_MODE_CONNECTOR_DisplayPort + DRM_MODE_CONNECTOR_HDMIA + DRM_MODE_CONNECTOR_HDMIB + DRM_MODE_CONNECTOR_TV + DRM_MODE_CONNECTOR_eDP + DRM_MODE_CONNECTOR_VIRTUAL + + + + Connectors must be attached to an encoder to be used. For devices that + map connectors to encoders 1:1, the connector should be attached at + initialization time with a call to + drm_mode_connector_attach_encoder. The driver must + also set the drm_connector + encoder field to point to the attached + encoder. + + + Finally, drivers must initialize the connectors state change detection + with a call to drm_kms_helper_poll_init. If at + least one connector is pollable but can't generate hotplug interrupts + (indicated by the DRM_CONNECTOR_POLL_CONNECT and + DRM_CONNECTOR_POLL_DISCONNECT connector flags), a delayed work will + automatically be queued to periodically poll for changes. Connectors + that can generate hotplug interrupts must be marked with the + DRM_CONNECTOR_POLL_HPD flag instead, and their interrupt handler must + call drm_helper_hpd_irq_event. The function will + queue a delayed work to check the state of all connectors, but no + periodic polling will be done. + + + + Connector Operations + + Unless otherwise state, all operations are mandatory. + + + DPMS + void (*dpms)(struct drm_connector *connector, int mode); + + The DPMS operation sets the power state of a connector. The mode + argument is one of + + DRM_MODE_DPMS_ON + DRM_MODE_DPMS_STANDBY + DRM_MODE_DPMS_SUSPEND + DRM_MODE_DPMS_OFF + + + + In all but DPMS_ON mode the encoder to which the connector is attached + should put the display in low-power mode by driving its signals + appropriately. If more than one connector is attached to the encoder + care should be taken not to change the power state of other displays as + a side effect. Low-power mode should be propagated to the encoders and + CRTCs when all related connectors are put in low-power mode. + + + + Modes + int (*fill_modes)(struct drm_connector *connector, uint32_t max_width, + uint32_t max_height); + + Fill the mode list with all supported modes for the connector. If the + max_width and max_height + arguments are non-zero, the implementation must ignore all modes wider + than max_width or higher than + max_height. + + + The connector must also fill in this operation its + display_info + width_mm and + height_mm fields with the connected display + physical size in millimeters. The fields should be set to 0 if the value + isn't known or is not applicable (for instance for projector devices). + + + + Connection Status + + The connection status is updated through polling or hotplug events when + supported (see ). The status + value is reported to userspace through ioctls and must not be used + inside the driver, as it only gets initialized by a call to + drm_mode_getconnector from userspace. + + enum drm_connector_status (*detect)(struct drm_connector *connector, + bool force); + + Check to see if anything is attached to the connector. The + force parameter is set to false whilst polling or + to true when checking the connector due to user request. + force can be used by the driver to avoid + expensive, destructive operations during automated probing. + + + Return connector_status_connected if something is connected to the + connector, connector_status_disconnected if nothing is connected and + connector_status_unknown if the connection state isn't known. + + + Drivers should only return connector_status_connected if the connection + status has really been probed as connected. Connectors that can't detect + the connection status, or failed connection status probes, should return + connector_status_unknown. + + + + Miscellaneous + + + void (*destroy)(struct drm_connector *connector); + + Destroy the connector when not needed anymore. See + . + + + + + + + + Cleanup + + The DRM core manages its objects' lifetime. When an object is not needed + anymore the core calls its destroy function, which must clean up and + free every resource allocated for the object. Every + drm_*_init call must be matched with a + corresponding drm_*_cleanup call to cleanup CRTCs + (drm_crtc_cleanup), planes + (drm_plane_cleanup), encoders + (drm_encoder_cleanup) and connectors + (drm_connector_cleanup). Furthermore, connectors + that have been added to sysfs must be removed by a call to + drm_sysfs_connector_remove before calling + drm_connector_cleanup. + + + Connectors state change detection must be cleanup up with a call to + drm_kms_helper_poll_fini. + + + + Output discovery and initialization example + - - - In the example above (again, taken from the i915 driver), a - CRT connector and encoder combination is created. A device-specific - i2c bus is also created for fetching EDID data and - performing monitor detection. Once the process is complete, - the new connector is registered with sysfs to make its - properties available to applications. - - - Helper functions and core functions - - Since many PC-class graphics devices have similar display output - designs, the DRM provides a set of helper functions to make - output management easier. The core helper routines handle - encoder re-routing and the disabling of unused functions following - mode setting. Using the helpers is optional, but recommended for - devices with PC-style architectures (i.e. a set of display planes - for feeding pixels to encoders which are in turn routed to - connectors). Devices with more complex requirements needing - finer grained management may opt to use the core callbacks - directly. - - - [Insert typical diagram here.] [Insert OMAP style config here.] - - - - Each encoder object needs to provide: - - - A DPMS (basically on/off) function. - - - A mode-fixup function (for converting requested modes into - native hardware timings). - - - Functions (prepare, set, and commit) for use by the core DRM - helper functions. - - - Connector helpers need to provide functions (mode-fetch, validity, - and encoder-matching) for returning an ideal encoder for a given - connector. The core connector functions include a DPMS callback, - save/restore routines (deprecated), detection, mode probing, - property handling, and cleanup functions. - - - - - +}]]> + + In the example above (taken from the i915 driver), a CRTC, connector and + encoder combination is created. A device-specific i2c bus is also + created for fetching EDID data and performing monitor detection. Once + the process is complete, the new connector is registered with sysfs to + make its properties available to applications. + - + - VBlank event handling + Mid-layer Helper Functions - The DRM core exposes two vertical blank related ioctls: - - - DRM_IOCTL_WAIT_VBLANK - - - This takes a struct drm_wait_vblank structure as its argument, - and it is used to block or request a signal when a specified - vblank event occurs. - - - - - DRM_IOCTL_MODESET_CTL - - - This should be called by application level drivers before and - after mode setting, since on many devices the vertical blank - counter is reset at that time. Internally, the DRM snapshots - the last vblank count when the ioctl is called with the - _DRM_PRE_MODESET command, so that the counter won't go backwards - (which is dealt with when _DRM_POST_MODESET is used). - - - - - + The CRTC, encoder and connector functions provided by the drivers + implement the DRM API. They're called by the DRM core and ioctl handlers + to handle device state changes and configuration request. As implementing + those functions often requires logic not specific to drivers, mid-layer + helper functions are available to avoid duplicating boilerplate code. + + + The DRM core contains one mid-layer implementation. The mid-layer provides + implementations of several CRTC, encoder and connector functions (called + from the top of the mid-layer) that pre-process requests and call + lower-level functions provided by the driver (at the bottom of the + mid-layer). For instance, the + drm_crtc_helper_set_config function can be used to + fill the struct drm_crtc_funcs + set_config field. When called, it will split + the set_config operation in smaller, simpler + operations and call the driver to handle them. - To support the functions above, the DRM core provides several - helper functions for tracking vertical blank counters, and - requires drivers to provide several callbacks: - get_vblank_counter(), enable_vblank() and disable_vblank(). The - core uses get_vblank_counter() to keep the counter accurate - across interrupt disable periods. It should return the current - vertical blank event count, which is often tracked in a device - register. The enable and disable vblank callbacks should enable - and disable vertical blank interrupts, respectively. In the - absence of DRM clients waiting on vblank events, the core DRM - code uses the disable_vblank() function to disable - interrupts, which saves power. They are re-enabled again when - a client calls the vblank wait ioctl above. + To use the mid-layer, drivers call drm_crtc_helper_add, + drm_encoder_helper_add and + drm_connector_helper_add functions to install their + mid-layer bottom operations handlers, and fill the + drm_crtc_funcs, + drm_encoder_funcs and + drm_connector_funcs structures with pointers to + the mid-layer top API functions. Installing the mid-layer bottom operation + handlers is best done right after registering the corresponding KMS object. - A device that doesn't provide a count register may simply use an - internal atomic counter incremented on every vertical blank - interrupt (and then treat the enable_vblank() and disable_vblank() - callbacks as no-ops). + The mid-layer is not split between CRTC, encoder and connector operations. + To use it, a driver must provide bottom functions for all of the three KMS + entities. + + Helper Functions + + + int drm_crtc_helper_set_config(struct drm_mode_set *set); + + The drm_crtc_helper_set_config helper function + is a CRTC set_config implementation. It + first tries to locate the best encoder for each connector by calling + the connector best_encoder helper + operation. + + + After locating the appropriate encoders, the helper function will + call the mode_fixup encoder and CRTC helper + operations to adjust the requested mode, or reject it completely in + which case an error will be returned to the application. If the new + configuration after mode adjustment is identical to the current + configuration the helper function will return without performing any + other operation. + + + If the adjusted mode is identical to the current mode but changes to + the frame buffer need to be applied, the + drm_crtc_helper_set_config function will call + the CRTC mode_set_base helper operation. If + the adjusted mode differs from the current mode, or if the + mode_set_base helper operation is not + provided, the helper function performs a full mode set sequence by + calling the prepare, + mode_set and + commit CRTC and encoder helper operations, + in that order. + + + + void drm_helper_connector_dpms(struct drm_connector *connector, int mode); + + The drm_helper_connector_dpms helper function + is a connector dpms implementation that + tracks power state of connectors. To use the function, drivers must + provide dpms helper operations for CRTCs + and encoders to apply the DPMS state to the device. + + + The mid-layer doesn't track the power state of CRTCs and encoders. + The dpms helper operations can thus be + called with a mode identical to the currently active mode. + + + + int drm_helper_probe_single_connector_modes(struct drm_connector *connector, + uint32_t maxX, uint32_t maxY); + + The drm_helper_probe_single_connector_modes helper + function is a connector fill_modes + implementation that updates the connection status for the connector + and then retrieves a list of modes by calling the connector + get_modes helper operation. + + + The function filters out modes larger than + max_width and max_height + if specified. It then calls the connector + mode_valid helper operation for each mode in + the probed list to check whether the mode is valid for the connector. + + + + + + CRTC Helper Operations + + + bool (*mode_fixup)(struct drm_crtc *crtc, + const struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode); + + Let CRTCs adjust the requested mode or reject it completely. This + operation returns true if the mode is accepted (possibly after being + adjusted) or false if it is rejected. + + + The mode_fixup operation should reject the + mode if it can't reasonably use it. The definition of "reasonable" + is currently fuzzy in this context. One possible behaviour would be + to set the adjusted mode to the panel timings when a fixed-mode + panel is used with hardware capable of scaling. Another behaviour + would be to accept any input mode and adjust it to the closest mode + supported by the hardware (FIXME: This needs to be clarified). + + + + int (*mode_set_base)(struct drm_crtc *crtc, int x, int y, + struct drm_framebuffer *old_fb) + + Move the CRTC on the current frame buffer (stored in + crtc->fb) to position (x,y). Any of the frame + buffer, x position or y position may have been modified. + + + This helper operation is optional. If not provided, the + drm_crtc_helper_set_config function will fall + back to the mode_set helper operation. + + + FIXME: Why are x and y passed as arguments, as they can be accessed + through crtc->x and + crtc->y? + + + + void (*prepare)(struct drm_crtc *crtc); + + Prepare the CRTC for mode setting. This operation is called after + validating the requested mode. Drivers use it to perform + device-specific operations required before setting the new mode. + + + + int (*mode_set)(struct drm_crtc *crtc, struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode, int x, int y, + struct drm_framebuffer *old_fb); + + Set a new mode, position and frame buffer. Depending on the device + requirements, the mode can be stored internally by the driver and + applied in the commit operation, or + programmed to the hardware immediately. + + + The mode_set operation returns 0 on success + or a negative error code if an error occurs. + + + + void (*commit)(struct drm_crtc *crtc); + + Commit a mode. This operation is called after setting the new mode. + Upon return the device must use the new mode and be fully + operational. + + + + + + Encoder Helper Operations + + + bool (*mode_fixup)(struct drm_encoder *encoder, + const struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode); + + FIXME: The mode argument be const, but the i915 driver modifies + mode->clock in intel_dp_mode_fixup. + + + Let encoders adjust the requested mode or reject it completely. This + operation returns true if the mode is accepted (possibly after being + adjusted) or false if it is rejected. See the + mode_fixup CRTC helper + operation for an explanation of the allowed adjustments. + + + + void (*prepare)(struct drm_encoder *encoder); + + Prepare the encoder for mode setting. This operation is called after + validating the requested mode. Drivers use it to perform + device-specific operations required before setting the new mode. + + + + void (*mode_set)(struct drm_encoder *encoder, + struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode); + + Set a new mode. Depending on the device requirements, the mode can + be stored internally by the driver and applied in the + commit operation, or programmed to the + hardware immediately. + + + + void (*commit)(struct drm_encoder *encoder); + + Commit a mode. This operation is called after setting the new mode. + Upon return the device must use the new mode and be fully + operational. + + + + + + Connector Helper Operations + + + struct drm_encoder *(*best_encoder)(struct drm_connector *connector); + + Return a pointer to the best encoder for the connecter. Device that + map connectors to encoders 1:1 simply return the pointer to the + associated encoder. This operation is mandatory. + + + + int (*get_modes)(struct drm_connector *connector); + + Fill the connector's probed_modes list + by parsing EDID data with drm_add_edid_modes or + calling drm_mode_probed_add directly for every + supported mode and return the number of modes it has detected. This + operation is mandatory. + + + When adding modes manually the driver creates each mode with a call to + drm_mode_create and must fill the following fields. + + + __u32 type; + + Mode type bitmask, a combination of + + + DRM_MODE_TYPE_BUILTIN + not used? + + + DRM_MODE_TYPE_CLOCK_C + not used? + + + DRM_MODE_TYPE_CRTC_C + not used? + + + + DRM_MODE_TYPE_PREFERRED - The preferred mode for the connector + + + not used? + + + + DRM_MODE_TYPE_DEFAULT + not used? + + + DRM_MODE_TYPE_USERDEF + not used? + + + DRM_MODE_TYPE_DRIVER + + + The mode has been created by the driver (as opposed to + to user-created modes). + + + + + Drivers must set the DRM_MODE_TYPE_DRIVER bit for all modes they + create, and set the DRM_MODE_TYPE_PREFERRED bit for the preferred + mode. + + + + __u32 clock; + Pixel clock frequency in kHz unit + + + __u16 hdisplay, hsync_start, hsync_end, htotal; + __u16 vdisplay, vsync_start, vsync_end, vtotal; + Horizontal and vertical timing information + <----------------><-------------><--------------> + + //////////////////////| + ////////////////////// | + ////////////////////// |.................. ................ + _______________ + + <----- [hv]display -----> + <------------- [hv]sync_start ------------> + <--------------------- [hv]sync_end ---------------------> + <-------------------------------- [hv]total -----------------------------> +]]> + + + __u16 hskew; + __u16 vscan; + Unknown + + + __u32 flags; + + Mode flags, a combination of + + + DRM_MODE_FLAG_PHSYNC + + Horizontal sync is active high + + + + DRM_MODE_FLAG_NHSYNC + + Horizontal sync is active low + + + + DRM_MODE_FLAG_PVSYNC + + Vertical sync is active high + + + + DRM_MODE_FLAG_NVSYNC + + Vertical sync is active low + + + + DRM_MODE_FLAG_INTERLACE + + Mode is interlaced + + + + DRM_MODE_FLAG_DBLSCAN + + Mode uses doublescan + + + + DRM_MODE_FLAG_CSYNC + + Mode uses composite sync + + + + DRM_MODE_FLAG_PCSYNC + + Composite sync is active high + + + + DRM_MODE_FLAG_NCSYNC + + Composite sync is active low + + + + DRM_MODE_FLAG_HSKEW + + hskew provided (not used?) + + + + DRM_MODE_FLAG_BCAST + + not used? + + + + DRM_MODE_FLAG_PIXMUX + + not used? + + + + DRM_MODE_FLAG_DBLCLK + + not used? + + + + DRM_MODE_FLAG_CLKDIV2 + + ? + + + + + + Note that modes marked with the INTERLACE or DBLSCAN flags will be + filtered out by + drm_helper_probe_single_connector_modes if + the connector's interlace_allowed or + doublescan_allowed field is set to 0. + + + + char name[DRM_DISPLAY_MODE_LEN]; + + Mode name. The driver must call + drm_mode_set_name to fill the mode name from + hdisplay, + vdisplay and interlace flag after + filling the corresponding fields. + + + + + + The vrefresh value is computed by + drm_helper_probe_single_connector_modes. + + + When parsing EDID data, drm_add_edid_modes fill the + connector display_info + width_mm and + height_mm fields. When creating modes + manually the get_modes helper operation must + set the display_info + width_mm and + height_mm fields if they haven't been set + already (for instance at initilization time when a fixed-size panel is + attached to the connector). The mode width_mm + and height_mm fields are only used internally + during EDID parsing and should not be set when creating modes manually. + + + + int (*mode_valid)(struct drm_connector *connector, + struct drm_display_mode *mode); + + Verify whether a mode is valid for the connector. Return MODE_OK for + supported modes and one of the enum drm_mode_status values (MODE_*) + for unsupported modes. This operation is mandatory. + + + As the mode rejection reason is currently not used beside for + immediately removing the unsupported mode, an implementation can + return MODE_BAD regardless of the exact reason why the mode is not + valid. + + + Note that the mode_valid helper operation is + only called for modes detected by the device, and + not for modes set by the user through the CRTC + set_config operation. + + + + - - Memory management + + + + Vertical Blanking + + Vertical blanking plays a major role in graphics rendering. To achieve + tear-free display, users must synchronize page flips and/or rendering to + vertical blanking. The DRM API offers ioctls to perform page flips + synchronized to vertical blanking and wait for vertical blanking. + + + The DRM core handles most of the vertical blanking management logic, which + involves filtering out spurious interrupts, keeping race-free blanking + counters, coping with counter wrap-around and resets and keeping use + counts. It relies on the driver to generate vertical blanking interrupts + and optionally provide a hardware vertical blanking counter. Drivers must + implement the following operations. + + + + int (*enable_vblank) (struct drm_device *dev, int crtc); +void (*disable_vblank) (struct drm_device *dev, int crtc); + + Enable or disable vertical blanking interrupts for the given CRTC. + + + + u32 (*get_vblank_counter) (struct drm_device *dev, int crtc); + + Retrieve the value of the vertical blanking counter for the given + CRTC. If the hardware maintains a vertical blanking counter its value + should be returned. Otherwise drivers can use the + drm_vblank_count helper function to handle this + operation. + + + - The memory manager lies at the heart of many DRM operations; it - is required to support advanced client features like OpenGL - pbuffers. The DRM currently contains two memory managers: TTM - and GEM. + Drivers must initialize the vertical blanking handling core with a call to + drm_vblank_init in their + load operation. The function will set the struct + drm_device + vblank_disable_allowed field to 0. This will + keep vertical blanking interrupts enabled permanently until the first mode + set operation, where vblank_disable_allowed is + set to 1. The reason behind this is not clear. Drivers can set the field + to 1 after calling drm_vblank_init to make vertical + blanking interrupts dynamically managed from the beginning. + + Vertical blanking interrupts can be enabled by the DRM core or by drivers + themselves (for instance to handle page flipping operations). The DRM core + maintains a vertical blanking use count to ensure that the interrupts are + not disabled while a user still needs them. To increment the use count, + drivers call drm_vblank_get. Upon return vertical + blanking interrupts are guaranteed to be enabled. + + + To decrement the use count drivers call + drm_vblank_put. Only when the use count drops to zero + will the DRM core disable the vertical blanking interrupts after a delay + by scheduling a timer. The delay is accessible through the vblankoffdelay + module parameter or the drm_vblank_offdelay global + variable and expressed in milliseconds. Its default value is 5000 ms. + + + When a vertical blanking interrupt occurs drivers only need to call the + drm_handle_vblank function to account for the + interrupt. + + + Resources allocated by drm_vblank_init must be freed + with a call to drm_vblank_cleanup in the driver + unload operation handler. + + + + + + Open/Close, File Operations and IOCTLs - The Translation Table Manager (TTM) + Open and Close + int (*firstopen) (struct drm_device *); +void (*lastclose) (struct drm_device *); +int (*open) (struct drm_device *, struct drm_file *); +void (*preclose) (struct drm_device *, struct drm_file *); +void (*postclose) (struct drm_device *, struct drm_file *); + Open and close handlers. None of those methods are mandatory. + - TTM was developed by Tungsten Graphics, primarily by Thomas - Hellström, and is intended to be a flexible, high performance - graphics memory manager. + The firstopen method is called by the DRM core + when an application opens a device that has no other opened file handle. + Similarly the lastclose method is called when + the last application holding a file handle opened on the device closes + it. Both methods are mostly used for UMS (User Mode Setting) drivers to + acquire and release device resources which should be done in the + load and unload + methods for KMS drivers. - Drivers wishing to support TTM must fill out a drm_bo_driver - structure. + Note that the lastclose method is also called + at module unload time or, for hot-pluggable devices, when the device is + unplugged. The firstopen and + lastclose calls can thus be unbalanced. - TTM design background and information belongs here. + The open method is called every time the device + is opened by an application. Drivers can allocate per-file private data + in this method and store them in the struct + drm_file driver_priv + field. Note that the open method is called + before firstopen. + + + The close operation is split into preclose and + postclose methods. Drivers must stop and + cleanup all per-file operations in the preclose + method. For instance pending vertical blanking and page flip events must + be cancelled. No per-file operation is allowed on the file handle after + returning from the preclose method. + + + Finally the postclose method is called as the + last step of the close operation, right before calling the + lastclose method if no other open file handle + exists for the device. Drivers that have allocated per-file private data + in the open method should free it here. + + + The lastclose method should restore CRTC and + plane properties to default value, so that a subsequent open of the + device will not inherit state from the previous user. - - The Graphics Execution Manager (GEM) + File Operations + const struct file_operations *fops + File operations for the DRM device node. - GEM is an Intel project, authored by Eric Anholt and Keith - Packard. It provides simpler interfaces than TTM, and is well - suited for UMA devices. + Drivers must define the file operations structure that forms the DRM + userspace API entry point, even though most of those operations are + implemented in the DRM core. The open, + release and ioctl + operations are handled by + + .owner = THIS_MODULE, + .open = drm_open, + .release = drm_release, + .unlocked_ioctl = drm_ioctl, + #ifdef CONFIG_COMPAT + .compat_ioctl = drm_compat_ioctl, + #endif + - GEM-enabled drivers must provide gem_init_object() and - gem_free_object() callbacks to support the core memory - allocation routines. They should also provide several driver-specific - ioctls to support command execution, pinning, buffer - read & write, mapping, and domain ownership transfers. + Drivers that implement private ioctls that requires 32/64bit + compatibility support must provide their own + compat_ioctl handler that processes private + ioctls and calls drm_compat_ioctl for core ioctls. - On a fundamental level, GEM involves several operations: - - Memory allocation and freeing - Command execution - Aperture management at command execution time - - Buffer object allocation is relatively - straightforward and largely provided by Linux's shmem layer, which - provides memory to back each object. When mapped into the GTT - or used in a command buffer, the backing pages for an object are - flushed to memory and marked write combined so as to be coherent - with the GPU. Likewise, if the CPU accesses an object after the GPU - has finished rendering to the object, then the object must be made - coherent with the CPU's view - of memory, usually involving GPU cache flushing of various kinds. - This core CPU<->GPU coherency management is provided by a - device-specific ioctl, which evaluates an object's current domain and - performs any necessary flushing or synchronization to put the object - into the desired coherency domain (note that the object may be busy, - i.e. an active render target; in that case, setting the domain - blocks the client and waits for rendering to complete before - performing any necessary flushing operations). - - - Perhaps the most important GEM function is providing a command - execution interface to clients. Client programs construct command - buffers containing references to previously allocated memory objects, - and then submit them to GEM. At that point, GEM takes care to bind - all the objects into the GTT, execute the buffer, and provide - necessary synchronization between clients accessing the same buffers. - This often involves evicting some objects from the GTT and re-binding - others (a fairly expensive operation), and providing relocation - support which hides fixed GTT offsets from clients. Clients must - take care not to submit command buffers that reference more objects - than can fit in the GTT; otherwise, GEM will reject them and no rendering - will occur. Similarly, if several objects in the buffer require - fence registers to be allocated for correct rendering (e.g. 2D blits - on pre-965 chips), care must be taken not to require more fence - registers than are available to the client. Such resource management - should be abstracted from the client in libdrm. + The read and poll + operations provide support for reading DRM events and polling them. They + are implemented by + + .poll = drm_poll, + .read = drm_read, + .fasync = drm_fasync, + .llseek = no_llseek, + + + + The memory mapping implementation varies depending on how the driver + manages memory. Pre-GEM drivers will use drm_mmap, + while GEM-aware drivers will use drm_gem_mmap. See + . + + .mmap = drm_gem_mmap, + + + + No other file operation is supported by the DRM API. + + + + IOCTLs + struct drm_ioctl_desc *ioctls; +int num_ioctls; + Driver-specific ioctls descriptors table. + + Driver-specific ioctls numbers start at DRM_COMMAND_BASE. The ioctls + descriptors table is indexed by the ioctl number offset from the base + value. Drivers can use the DRM_IOCTL_DEF_DRV() macro to initialize the + table entries. + + + DRM_IOCTL_DEF_DRV(ioctl, func, flags) + + ioctl is the ioctl name. Drivers must define + the DRM_##ioctl and DRM_IOCTL_##ioctl macros to the ioctl number + offset from DRM_COMMAND_BASE and the ioctl number respectively. The + first macro is private to the device while the second must be exposed + to userspace in a public header. + + + func is a pointer to the ioctl handler function + compatible with the drm_ioctl_t type. + typedef int drm_ioctl_t(struct drm_device *dev, void *data, + struct drm_file *file_priv); + + + flags is a bitmask combination of the following + values. It restricts how the ioctl is allowed to be called. + + + DRM_AUTH - Only authenticated callers allowed + + + DRM_MASTER - The ioctl can only be called on the master file + handle + + + DRM_ROOT_ONLY - Only callers with the SYSADMIN capability allowed + + + DRM_CONTROL_ALLOW - The ioctl can only be called on a control + device + + + DRM_UNLOCKED - The ioctl handler will be called without locking + the DRM global mutex + + + - - - - - - Output management - - At the core of the DRM output management code is a set of - structures representing CRTCs, encoders, and connectors. - - - A CRTC is an abstraction representing a part of the chip that - contains a pointer to a scanout buffer. Therefore, the number - of CRTCs available determines how many independent scanout - buffers can be active at any given time. The CRTC structure - contains several fields to support this: a pointer to some video - memory, a display mode, and an (x, y) offset into the video - memory to support panning or configurations where one piece of - video memory spans multiple CRTCs. - - - An encoder takes pixel data from a CRTC and converts it to a - format suitable for any attached connectors. On some devices, - it may be possible to have a CRTC send data to more than one - encoder. In that case, both encoders would receive data from - the same scanout buffer, resulting in a "cloned" display - configuration across the connectors attached to each encoder. - - - A connector is the final destination for pixel data on a device, - and usually connects directly to an external display device like - a monitor or laptop panel. A connector can only be attached to - one encoder at a time. The connector is also the structure - where information about the attached display is kept, so it - contains fields for display data, EDID data, DPMS & - connection status, and information about modes supported on the - attached displays. - - - - - - Framebuffer management - - Clients need to provide a framebuffer object which provides a source - of pixels for a CRTC to deliver to the encoder(s) and ultimately the - connector(s). A framebuffer is fundamentally a driver-specific memory - object, made into an opaque handle by the DRM's addfb() function. - Once a framebuffer has been created this way, it may be passed to the - KMS mode setting routines for use in a completed configuration. - @@ -812,15 +2355,24 @@ void intel_crt_init(struct drm_device *dev) + + - Suspend/resume + Suspend/Resume + + The DRM core provides some suspend/resume code, but drivers wanting full + suspend/resume support should provide save() and restore() functions. + These are called at suspend, hibernate, or resume time, and should perform + any state save or restore required by your device across suspend or + hibernate states. + + int (*suspend) (struct drm_device *, pm_message_t state); +int (*resume) (struct drm_device *); - The DRM core provides some suspend/resume code, but drivers - wanting full suspend/resume support should provide save() and - restore() functions. These are called at suspend, - hibernate, or resume time, and should perform any state save or - restore required by your device across suspend or hibernate - states. + Those are legacy suspend and resume methods. New driver should use the + power management interface provided by their bus type (usually through + the struct device_driver dev_pm_ops) and set + these methods to NULL. @@ -833,6 +2385,35 @@ void intel_crt_init(struct drm_device *dev) + + @@ -853,6 +2434,42 @@ void intel_crt_init(struct drm_device *dev) Cover generic ioctls and sysfs layout here. We only need high-level info, since man pages should cover the rest. + + + + + VBlank event handling + + The DRM core exposes two vertical blank related ioctls: + + + DRM_IOCTL_WAIT_VBLANK + + + This takes a struct drm_wait_vblank structure as its argument, + and it is used to block or request a signal when a specified + vblank event occurs. + + + + + DRM_IOCTL_MODESET_CTL + + + This should be called by application level drivers before and + after mode setting, since on many devices the vertical blank + counter is reset at that time. Internally, the DRM snapshots + the last vblank count when the ioctl is called with the + _DRM_PRE_MODESET command, so that the counter won't go backwards + (which is dealt with when _DRM_POST_MODESET is used). + + + + + + + + -- cgit v0.10.2 From 53621860c38caff37fff99ba5f0b817511907bc4 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Wed, 22 Aug 2012 16:40:57 -0700 Subject: i915: use alloc_ordered_workqueue() instead of explicit UNBOUND w/ max_active = 1 This is an equivalent conversion and will ease scheduled removal of WQ_NON_REENTRANT. Signed-off-by: Tejun Heo Reviewed-by: Chris Wilson Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/i915_dma.c b/drivers/gpu/drm/i915/i915_dma.c index 2cba7b4..2c09900 100644 --- a/drivers/gpu/drm/i915/i915_dma.c +++ b/drivers/gpu/drm/i915/i915_dma.c @@ -1558,11 +1558,9 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags) * * All tasks on the workqueue are expected to acquire the dev mutex * so there is no point in running more than one instance of the - * workqueue at any time: max_active = 1 and NON_REENTRANT. + * workqueue at any time. Use an ordered one. */ - dev_priv->wq = alloc_workqueue("i915", - WQ_UNBOUND | WQ_NON_REENTRANT, - 1); + dev_priv->wq = alloc_ordered_workqueue("i915", 0); if (dev_priv->wq == NULL) { DRM_ERROR("Failed to create our workqueue.\n"); ret = -ENOMEM; -- cgit v0.10.2 From 25e9dc69709afad2be8de4ac2ecd6015356936ca Mon Sep 17 00:00:00 2001 From: Zhao Yakui Date: Wed, 8 Aug 2012 13:53:15 +0000 Subject: gma500: Fix incorrect SR issue when disabling CRTC already in disabled state Currently when trying to call the DPMS off again for one CRTC with DPMS off, it will firstly disable the SR and can't enable it again because of the incorrect check/logic. In such case the self refresh is still disabled although one CRTC pipe is active. This is wrong. Signed-off-by: Zhao Yakui [Ported to in kernel driver] Signed-off-by: Alan Cox Signed-off-by: Dave Airlie diff --git a/drivers/gpu/drm/gma500/cdv_intel_display.c b/drivers/gpu/drm/gma500/cdv_intel_display.c index a68509b..5c3a312 100644 --- a/drivers/gpu/drm/gma500/cdv_intel_display.c +++ b/drivers/gpu/drm/gma500/cdv_intel_display.c @@ -791,7 +791,7 @@ static void cdv_intel_crtc_dpms(struct drm_crtc *crtc, int mode) case DRM_MODE_DPMS_STANDBY: case DRM_MODE_DPMS_SUSPEND: if (psb_intel_crtc->active) - return; + break; psb_intel_crtc->active = true; @@ -835,7 +835,6 @@ static void cdv_intel_crtc_dpms(struct drm_crtc *crtc, int mode) REG_WRITE(map->status, temp); REG_READ(map->status); - cdv_intel_update_watermark(dev, crtc); cdv_intel_crtc_load_lut(crtc); /* Give the overlay scaler a chance to enable @@ -845,7 +844,7 @@ static void cdv_intel_crtc_dpms(struct drm_crtc *crtc, int mode) break; case DRM_MODE_DPMS_OFF: if (!psb_intel_crtc->active) - return; + break; psb_intel_crtc->active = false; @@ -892,10 +891,10 @@ static void cdv_intel_crtc_dpms(struct drm_crtc *crtc, int mode) /* Wait for the clocks to turn off. */ udelay(150); - cdv_intel_update_watermark(dev, crtc); psb_intel_crtc->crtc_enable = false; break; } + cdv_intel_update_watermark(dev, crtc); /*Set FIFO Watermarks*/ REG_WRITE(DSPARB, 0x3F3E); } -- cgit v0.10.2 From d66760962d75af12697d5197b3e97d51fe64169c Mon Sep 17 00:00:00 2001 From: Zhao Yakui Date: Wed, 8 Aug 2012 13:53:48 +0000 Subject: gma500: Program the DPLL lane based on the selected digitial port Based on the spec, the CRT output doesn't use the lane. And the HDMI B output uses the Lane0/1 while the HDMI C output uses the Lane 2/3. But currently it will program all the four lanes for the CRT/HDMI. Signed-off-by: Zhao Yakui [Ported to the in-kernel driver] Signed-off-by: Alan Cox Signed-off-by: Dave Airlie diff --git a/drivers/gpu/drm/gma500/cdv_intel_display.c b/drivers/gpu/drm/gma500/cdv_intel_display.c index 5c3a312..f16169c 100644 --- a/drivers/gpu/drm/gma500/cdv_intel_display.c +++ b/drivers/gpu/drm/gma500/cdv_intel_display.c @@ -57,8 +57,14 @@ struct cdv_intel_clock_t { struct cdv_intel_limit_t { struct cdv_intel_range_t dot, vco, n, m, m1, m2, p, p1; struct cdv_intel_p2_t p2; + bool (*find_pll)(const struct cdv_intel_limit_t *, struct drm_crtc *, + int, int, struct cdv_intel_clock_t *); }; +static bool cdv_intel_find_best_PLL(const struct cdv_intel_limit_t *limit, + struct drm_crtc *crtc, int target, int refclk, + struct cdv_intel_clock_t *best_clock); + #define CDV_LIMIT_SINGLE_LVDS_96 0 #define CDV_LIMIT_SINGLE_LVDS_100 1 #define CDV_LIMIT_DAC_HDMI_27 2 @@ -76,6 +82,7 @@ static const struct cdv_intel_limit_t cdv_intel_limits[] = { .p1 = {.min = 2, .max = 10}, .p2 = {.dot_limit = 200000, .p2_slow = 14, .p2_fast = 14}, + .find_pll = cdv_intel_find_best_PLL, }, { /* CDV_SINGLE_LVDS_100MHz */ .dot = {.min = 20000, .max = 115500}, @@ -90,6 +97,7 @@ static const struct cdv_intel_limit_t cdv_intel_limits[] = { * is 80-224Mhz. Prefer single channel as much as possible. */ .p2 = {.dot_limit = 200000, .p2_slow = 14, .p2_fast = 14}, + .find_pll = cdv_intel_find_best_PLL, }, { /* CDV_DAC_HDMI_27MHz */ .dot = {.min = 20000, .max = 400000}, @@ -101,6 +109,7 @@ static const struct cdv_intel_limit_t cdv_intel_limits[] = { .p = {.min = 5, .max = 90}, .p1 = {.min = 1, .max = 9}, .p2 = {.dot_limit = 225000, .p2_slow = 10, .p2_fast = 5}, + .find_pll = cdv_intel_find_best_PLL, }, { /* CDV_DAC_HDMI_96MHz */ .dot = {.min = 20000, .max = 400000}, @@ -112,6 +121,7 @@ static const struct cdv_intel_limit_t cdv_intel_limits[] = { .p = {.min = 5, .max = 100}, .p1 = {.min = 1, .max = 10}, .p2 = {.dot_limit = 225000, .p2_slow = 10, .p2_fast = 5}, + .find_pll = cdv_intel_find_best_PLL, }, }; @@ -216,7 +226,7 @@ static void cdv_sb_reset(struct drm_device *dev) */ static int cdv_dpll_set_clock_cdv(struct drm_device *dev, struct drm_crtc *crtc, - struct cdv_intel_clock_t *clock, bool is_lvds) + struct cdv_intel_clock_t *clock, bool is_lvds, u32 ddi_select) { struct psb_intel_crtc *psb_crtc = to_psb_intel_crtc(crtc); int pipe = psb_crtc->pipe; @@ -336,30 +346,33 @@ cdv_dpll_set_clock_cdv(struct drm_device *dev, struct drm_crtc *crtc, if (ret) return ret; - lane_reg = PSB_LANE0; - cdv_sb_read(dev, lane_reg, &lane_value); - lane_value &= ~(LANE_PLL_MASK); - lane_value |= LANE_PLL_ENABLE | LANE_PLL_PIPE(pipe); - cdv_sb_write(dev, lane_reg, lane_value); - - lane_reg = PSB_LANE1; - cdv_sb_read(dev, lane_reg, &lane_value); - lane_value &= ~(LANE_PLL_MASK); - lane_value |= LANE_PLL_ENABLE | LANE_PLL_PIPE(pipe); - cdv_sb_write(dev, lane_reg, lane_value); - - lane_reg = PSB_LANE2; - cdv_sb_read(dev, lane_reg, &lane_value); - lane_value &= ~(LANE_PLL_MASK); - lane_value |= LANE_PLL_ENABLE | LANE_PLL_PIPE(pipe); - cdv_sb_write(dev, lane_reg, lane_value); - - lane_reg = PSB_LANE3; - cdv_sb_read(dev, lane_reg, &lane_value); - lane_value &= ~(LANE_PLL_MASK); - lane_value |= LANE_PLL_ENABLE | LANE_PLL_PIPE(pipe); - cdv_sb_write(dev, lane_reg, lane_value); - + if (ddi_select) { + if ((ddi_select & DDI_MASK) == DDI0_SELECT) { + lane_reg = PSB_LANE0; + cdv_sb_read(dev, lane_reg, &lane_value); + lane_value &= ~(LANE_PLL_MASK); + lane_value |= LANE_PLL_ENABLE | LANE_PLL_PIPE(pipe); + cdv_sb_write(dev, lane_reg, lane_value); + + lane_reg = PSB_LANE1; + cdv_sb_read(dev, lane_reg, &lane_value); + lane_value &= ~(LANE_PLL_MASK); + lane_value |= LANE_PLL_ENABLE | LANE_PLL_PIPE(pipe); + cdv_sb_write(dev, lane_reg, lane_value); + } else { + lane_reg = PSB_LANE2; + cdv_sb_read(dev, lane_reg, &lane_value); + lane_value &= ~(LANE_PLL_MASK); + lane_value |= LANE_PLL_ENABLE | LANE_PLL_PIPE(pipe); + cdv_sb_write(dev, lane_reg, lane_value); + + lane_reg = PSB_LANE3; + cdv_sb_read(dev, lane_reg, &lane_value); + lane_value &= ~(LANE_PLL_MASK); + lane_value |= LANE_PLL_ENABLE | LANE_PLL_PIPE(pipe); + cdv_sb_write(dev, lane_reg, lane_value); + } + } return 0; } @@ -438,13 +451,12 @@ static bool cdv_intel_PLL_is_valid(struct drm_crtc *crtc, return true; } -static bool cdv_intel_find_best_PLL(struct drm_crtc *crtc, int target, - int refclk, - struct cdv_intel_clock_t *best_clock) +static bool cdv_intel_find_best_PLL(const struct cdv_intel_limit_t *limit, + struct drm_crtc *crtc, int target, int refclk, + struct cdv_intel_clock_t *best_clock) { struct drm_device *dev = crtc->dev; struct cdv_intel_clock_t clock; - const struct cdv_intel_limit_t *limit = cdv_intel_limit(crtc, refclk); int err = target; @@ -954,6 +966,8 @@ static int cdv_intel_crtc_mode_set(struct drm_crtc *crtc, bool is_hdmi = false; struct drm_mode_config *mode_config = &dev->mode_config; struct drm_connector *connector; + const struct cdv_intel_limit_t *limit; + u32 ddi_select = 0; list_for_each_entry(connector, &mode_config->connector_list, head) { struct psb_intel_encoder *psb_intel_encoder = @@ -963,6 +977,7 @@ static int cdv_intel_crtc_mode_set(struct drm_crtc *crtc, || connector->encoder->crtc != crtc) continue; + ddi_select = psb_intel_encoder->ddi_select; switch (psb_intel_encoder->type) { case INTEL_OUTPUT_LVDS: is_lvds = true; @@ -976,6 +991,9 @@ static int cdv_intel_crtc_mode_set(struct drm_crtc *crtc, case INTEL_OUTPUT_HDMI: is_hdmi = true; break; + default: + DRM_ERROR("invalid output type.\n"); + return 0; } } @@ -992,8 +1010,10 @@ static int cdv_intel_crtc_mode_set(struct drm_crtc *crtc, } drm_mode_debug_printmodeline(adjusted_mode); + + limit = cdv_intel_limit(crtc, refclk); - ok = cdv_intel_find_best_PLL(crtc, adjusted_mode->clock, refclk, + ok = limit->find_pll(limit, crtc, adjusted_mode->clock, refclk, &clock); if (!ok) { dev_err(dev->dev, "Couldn't find PLL settings for mode!\n"); @@ -1032,7 +1052,7 @@ static int cdv_intel_crtc_mode_set(struct drm_crtc *crtc, REG_WRITE(map->dpll, dpll | DPLL_VGA_MODE_DIS | DPLL_SYNCLOCK_ENABLE); REG_READ(map->dpll); - cdv_dpll_set_clock_cdv(dev, crtc, &clock, is_lvds); + cdv_dpll_set_clock_cdv(dev, crtc, &clock, is_lvds, ddi_select); udelay(150); diff --git a/drivers/gpu/drm/gma500/cdv_intel_hdmi.c b/drivers/gpu/drm/gma500/cdv_intel_hdmi.c index a86f87b..0c90f03 100644 --- a/drivers/gpu/drm/gma500/cdv_intel_hdmi.c +++ b/drivers/gpu/drm/gma500/cdv_intel_hdmi.c @@ -352,9 +352,11 @@ void cdv_hdmi_init(struct drm_device *dev, switch (reg) { case SDVOB: ddc_bus = GPIOE; + psb_intel_encoder->ddi_select = DDI0_SELECT; break; case SDVOC: ddc_bus = GPIOD; + psb_intel_encoder->ddi_select = DDI1_SELECT; break; default: DRM_ERROR("unknown reg 0x%x for HDMI\n", reg); diff --git a/drivers/gpu/drm/gma500/psb_intel_drv.h b/drivers/gpu/drm/gma500/psb_intel_drv.h index ebe1a28..7b357b0 100644 --- a/drivers/gpu/drm/gma500/psb_intel_drv.h +++ b/drivers/gpu/drm/gma500/psb_intel_drv.h @@ -133,6 +133,11 @@ struct psb_intel_encoder { void (*hot_plug)(struct psb_intel_encoder *); int crtc_mask; int clone_mask; + u32 ddi_select; /* Channel info */ +#define DDI0_SELECT 0x01 +#define DDI1_SELECT 0x02 +#define DP_MASK 0x8000; +#define DDI_MASK 0x03 void *dev_priv; /* For sdvo_priv, lvds_priv, etc... */ /* FIXME: Either make SDVO and LVDS store it's i2c here or give CDV it's -- cgit v0.10.2 From 8695b612943561478fd22f28f45e5692e5d078db Mon Sep 17 00:00:00 2001 From: Alan Cox Date: Wed, 8 Aug 2012 13:54:15 +0000 Subject: gma500: Add the support of display port on CDV Import the pieces we need in order to do DisplayPort. Don't wire them up yet as there is work to do to integrate them. Signed-off-by: Zhao Yakui Signed-off-by: Alan Cox Signed-off-by: Dave Airlie diff --git a/drivers/gpu/drm/gma500/cdv_intel_dp.c b/drivers/gpu/drm/gma500/cdv_intel_dp.c new file mode 100644 index 0000000..c6d545d --- /dev/null +++ b/drivers/gpu/drm/gma500/cdv_intel_dp.c @@ -0,0 +1,1504 @@ +/* + * 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: + * Keith Packard + * + */ + +#include +#include +#include "drmP.h" +#include "drm.h" +#include "drm_crtc.h" +#include "drm_crtc_helper.h" +#include "psb_drv.h" +#include "psb_intel_drv.h" +#include "psb_drm.h" +#include "psb_intel_reg.h" +#include "drm_dp_helper.h" + + +#define DP_LINK_STATUS_SIZE 6 +#define DP_LINK_CHECK_TIMEOUT (10 * 1000) + +#define DP_LINK_CONFIGURATION_SIZE 9 + +#define CDV_FAST_LINK_TRAIN 1 + +struct psb_intel_dp { + uint32_t output_reg; + uint32_t DP; + uint8_t link_configuration[DP_LINK_CONFIGURATION_SIZE]; + bool has_audio; + int force_audio; + uint32_t color_range; + uint8_t link_bw; + uint8_t lane_count; + uint8_t dpcd[4]; + struct psb_intel_output *output; + struct i2c_adapter adapter; + struct i2c_algo_dp_aux_data algo; + uint8_t train_set[4]; + uint8_t link_status[DP_LINK_STATUS_SIZE]; +}; + +struct ddi_regoff { + uint32_t PreEmph1; + uint32_t PreEmph2; + uint32_t VSwing1; + uint32_t VSwing2; + uint32_t VSwing3; + uint32_t VSwing4; + uint32_t VSwing5; +}; + +static struct ddi_regoff ddi_DP_train_table[] = { + {.PreEmph1 = 0x812c, .PreEmph2 = 0x8124, .VSwing1 = 0x8154, + .VSwing2 = 0x8148, .VSwing3 = 0x814C, .VSwing4 = 0x8150, + .VSwing5 = 0x8158,}, + {.PreEmph1 = 0x822c, .PreEmph2 = 0x8224, .VSwing1 = 0x8254, + .VSwing2 = 0x8248, .VSwing3 = 0x824C, .VSwing4 = 0x8250, + .VSwing5 = 0x8258,}, +}; + +static uint32_t dp_vswing_premph_table[] = { + 0x55338954, 0x4000, + 0x554d8954, 0x2000, + 0x55668954, 0, + 0x559ac0d4, 0x6000, +}; +/** + * is_edp - is the given port attached to an eDP panel (either CPU or PCH) + * @intel_dp: DP struct + * + * If a CPU or PCH DP output is attached to an eDP panel, this function + * will return true, and false otherwise. + */ +static bool is_edp(struct psb_intel_output *output) +{ + return output->type == INTEL_OUTPUT_EDP; +} + + +static void psb_intel_dp_start_link_train(struct psb_intel_output *output); +static void psb_intel_dp_complete_link_train(struct psb_intel_output *output); +static void psb_intel_dp_link_down(struct psb_intel_output *output); + +static int +psb_intel_dp_max_lane_count(struct psb_intel_output *output) +{ + struct psb_intel_dp *intel_dp = output->dev_priv; + int max_lane_count = 4; + + if (intel_dp->dpcd[DP_DPCD_REV] >= 0x11) { + max_lane_count = intel_dp->dpcd[DP_MAX_LANE_COUNT] & 0x1f; + switch (max_lane_count) { + case 1: case 2: case 4: + break; + default: + max_lane_count = 4; + } + } + return max_lane_count; +} + +static int +psb_intel_dp_max_link_bw(struct psb_intel_output *output) +{ + struct psb_intel_dp *intel_dp = output->dev_priv; + int max_link_bw = intel_dp->dpcd[DP_MAX_LINK_RATE]; + + switch (max_link_bw) { + case DP_LINK_BW_1_62: + case DP_LINK_BW_2_7: + break; + default: + max_link_bw = DP_LINK_BW_1_62; + break; + } + return max_link_bw; +} + +static int +psb_intel_dp_link_clock(uint8_t link_bw) +{ + if (link_bw == DP_LINK_BW_2_7) + return 270000; + else + return 162000; +} + +static int +psb_intel_dp_link_required(int pixel_clock, int bpp) +{ + return (pixel_clock * bpp + 7) / 8; +} + +static int +psb_intel_dp_max_data_rate(int max_link_clock, int max_lanes) +{ + return (max_link_clock * max_lanes * 19) / 20; +} + +static int +psb_intel_dp_mode_valid(struct drm_connector *connector, + struct drm_display_mode *mode) +{ + struct psb_intel_output *output = to_psb_intel_output(connector); + struct drm_device *dev = connector->dev; + struct drm_psb_private *dev_priv = dev->dev_private; + int max_link_clock = psb_intel_dp_link_clock(psb_intel_dp_max_link_bw(output)); + int max_lanes = psb_intel_dp_max_lane_count(output); + + if (is_edp(output) && dev_priv->panel_fixed_mode) { + if (mode->hdisplay > dev_priv->panel_fixed_mode->hdisplay) + return MODE_PANEL; + + if (mode->vdisplay > dev_priv->panel_fixed_mode->vdisplay) + return MODE_PANEL; + } + + /* only refuse the mode on non eDP since we have seen some weird eDP panels + which are outside spec tolerances but somehow work by magic */ + if (!is_edp(output) && + (psb_intel_dp_link_required(mode->clock, 24) + > psb_intel_dp_max_data_rate(max_link_clock, max_lanes))) + return MODE_CLOCK_HIGH; + + if (mode->clock < 10000) + return MODE_CLOCK_LOW; + + return MODE_OK; +} + +static uint32_t +pack_aux(uint8_t *src, int src_bytes) +{ + int i; + uint32_t v = 0; + + if (src_bytes > 4) + src_bytes = 4; + for (i = 0; i < src_bytes; i++) + v |= ((uint32_t) src[i]) << ((3-i) * 8); + return v; +} + +static void +unpack_aux(uint32_t src, uint8_t *dst, int dst_bytes) +{ + int i; + if (dst_bytes > 4) + dst_bytes = 4; + for (i = 0; i < dst_bytes; i++) + dst[i] = src >> ((3-i) * 8); +} + +static int +psb_intel_dp_aux_ch(struct psb_intel_output *output, + uint8_t *send, int send_bytes, + uint8_t *recv, int recv_size) +{ + struct psb_intel_dp *intel_dp = output->dev_priv; + uint32_t output_reg = intel_dp->output_reg; + struct drm_device *dev = output->base.dev; + uint32_t ch_ctl = output_reg + 0x10; + uint32_t ch_data = ch_ctl + 4; + int i; + int recv_bytes; + uint32_t status; + uint32_t aux_clock_divider; + int try, precharge; + + /* The clock divider is based off the hrawclk, + * and would like to run at 2MHz. So, take the + * hrawclk value and divide by 2 and use that + * On CDV platform it uses 200MHz as hrawclk. + * + */ + aux_clock_divider = 200 / 2; + + precharge = 4; + + if (REG_READ(ch_ctl) & DP_AUX_CH_CTL_SEND_BUSY) { + DRM_ERROR("dp_aux_ch not started status 0x%08x\n", + REG_READ(ch_ctl)); + return -EBUSY; + } + + /* Must try at least 3 times according to DP spec */ + for (try = 0; try < 5; try++) { + /* Load the send data into the aux channel data registers */ + for (i = 0; i < send_bytes; i += 4) + REG_WRITE(ch_data + i, + pack_aux(send + i, send_bytes - i)); + + /* Send the command and wait for it to complete */ + REG_WRITE(ch_ctl, + DP_AUX_CH_CTL_SEND_BUSY | + DP_AUX_CH_CTL_TIME_OUT_400us | + (send_bytes << DP_AUX_CH_CTL_MESSAGE_SIZE_SHIFT) | + (precharge << DP_AUX_CH_CTL_PRECHARGE_2US_SHIFT) | + (aux_clock_divider << DP_AUX_CH_CTL_BIT_CLOCK_2X_SHIFT) | + DP_AUX_CH_CTL_DONE | + DP_AUX_CH_CTL_TIME_OUT_ERROR | + DP_AUX_CH_CTL_RECEIVE_ERROR); + for (;;) { + status = REG_READ(ch_ctl); + if ((status & DP_AUX_CH_CTL_SEND_BUSY) == 0) + break; + udelay(100); + } + + /* Clear done status and any errors */ + REG_WRITE(ch_ctl, + status | + DP_AUX_CH_CTL_DONE | + DP_AUX_CH_CTL_TIME_OUT_ERROR | + DP_AUX_CH_CTL_RECEIVE_ERROR); + if (status & DP_AUX_CH_CTL_DONE) + break; + } + + if ((status & DP_AUX_CH_CTL_DONE) == 0) { + DRM_ERROR("dp_aux_ch not done status 0x%08x\n", status); + return -EBUSY; + } + + /* Check for timeout or receive error. + * Timeouts occur when the sink is not connected + */ + if (status & DP_AUX_CH_CTL_RECEIVE_ERROR) { + DRM_ERROR("dp_aux_ch receive error status 0x%08x\n", status); + return -EIO; + } + + /* Timeouts occur when the device isn't connected, so they're + * "normal" -- don't fill the kernel log with these */ + if (status & DP_AUX_CH_CTL_TIME_OUT_ERROR) { + DRM_DEBUG_KMS("dp_aux_ch timeout status 0x%08x\n", status); + return -ETIMEDOUT; + } + + /* Unload any bytes sent back from the other side */ + recv_bytes = ((status & DP_AUX_CH_CTL_MESSAGE_SIZE_MASK) >> + DP_AUX_CH_CTL_MESSAGE_SIZE_SHIFT); + if (recv_bytes > recv_size) + recv_bytes = recv_size; + + for (i = 0; i < recv_bytes; i += 4) + unpack_aux(REG_READ(ch_data + i), + recv + i, recv_bytes - i); + + return recv_bytes; +} + +/* Write data to the aux channel in native mode */ +static int +psb_intel_dp_aux_native_write(struct psb_intel_output *output, + uint16_t address, uint8_t *send, int send_bytes) +{ + int ret; + uint8_t msg[20]; + int msg_bytes; + uint8_t ack; + + if (send_bytes > 16) + return -1; + msg[0] = AUX_NATIVE_WRITE << 4; + msg[1] = address >> 8; + msg[2] = address & 0xff; + msg[3] = send_bytes - 1; + memcpy(&msg[4], send, send_bytes); + msg_bytes = send_bytes + 4; + for (;;) { + ret = psb_intel_dp_aux_ch(output, msg, msg_bytes, &ack, 1); + if (ret < 0) + return ret; + if ((ack & AUX_NATIVE_REPLY_MASK) == AUX_NATIVE_REPLY_ACK) + break; + else if ((ack & AUX_NATIVE_REPLY_MASK) == AUX_NATIVE_REPLY_DEFER) + udelay(100); + else + return -EIO; + } + return send_bytes; +} + +/* Write a single byte to the aux channel in native mode */ +static int +psb_intel_dp_aux_native_write_1(struct psb_intel_output *output, + uint16_t address, uint8_t byte) +{ + return psb_intel_dp_aux_native_write(output, address, &byte, 1); +} + +/* read bytes from a native aux channel */ +static int +psb_intel_dp_aux_native_read(struct psb_intel_output *output, + uint16_t address, uint8_t *recv, int recv_bytes) +{ + uint8_t msg[4]; + int msg_bytes; + uint8_t reply[20]; + int reply_bytes; + uint8_t ack; + int ret; + + msg[0] = AUX_NATIVE_READ << 4; + msg[1] = address >> 8; + msg[2] = address & 0xff; + msg[3] = recv_bytes - 1; + + msg_bytes = 4; + reply_bytes = recv_bytes + 1; + + for (;;) { + ret = psb_intel_dp_aux_ch(output, msg, msg_bytes, + reply, reply_bytes); + if (ret == 0) + return -EPROTO; + if (ret < 0) + return ret; + ack = reply[0]; + if ((ack & AUX_NATIVE_REPLY_MASK) == AUX_NATIVE_REPLY_ACK) { + memcpy(recv, reply + 1, ret - 1); + return ret - 1; + } + else if ((ack & AUX_NATIVE_REPLY_MASK) == AUX_NATIVE_REPLY_DEFER) + udelay(100); + else + return -EIO; + } +} + +static int +psb_intel_dp_i2c_aux_ch(struct i2c_adapter *adapter, int mode, + uint8_t write_byte, uint8_t *read_byte) +{ + struct i2c_algo_dp_aux_data *algo_data = adapter->algo_data; + struct psb_intel_dp *intel_dp = container_of(adapter, + struct psb_intel_dp, + adapter); + struct psb_intel_output *output = intel_dp->output; + uint16_t address = algo_data->address; + uint8_t msg[5]; + uint8_t reply[2]; + unsigned retry; + int msg_bytes; + int reply_bytes; + int ret; + + /* Set up the command byte */ + if (mode & MODE_I2C_READ) + msg[0] = AUX_I2C_READ << 4; + else + msg[0] = AUX_I2C_WRITE << 4; + + if (!(mode & MODE_I2C_STOP)) + msg[0] |= AUX_I2C_MOT << 4; + + msg[1] = address >> 8; + msg[2] = address; + + switch (mode) { + case MODE_I2C_WRITE: + msg[3] = 0; + msg[4] = write_byte; + msg_bytes = 5; + reply_bytes = 1; + break; + case MODE_I2C_READ: + msg[3] = 0; + msg_bytes = 4; + reply_bytes = 2; + break; + default: + msg_bytes = 3; + reply_bytes = 1; + break; + } + + for (retry = 0; retry < 5; retry++) { + ret = psb_intel_dp_aux_ch(output, + msg, msg_bytes, + reply, reply_bytes); + if (ret < 0) { + DRM_DEBUG_KMS("aux_ch failed %d\n", ret); + return ret; + } + + switch (reply[0] & AUX_NATIVE_REPLY_MASK) { + case AUX_NATIVE_REPLY_ACK: + /* I2C-over-AUX Reply field is only valid + * when paired with AUX ACK. + */ + break; + case AUX_NATIVE_REPLY_NACK: + DRM_DEBUG_KMS("aux_ch native nack\n"); + return -EREMOTEIO; + case AUX_NATIVE_REPLY_DEFER: + udelay(100); + continue; + default: + DRM_ERROR("aux_ch invalid native reply 0x%02x\n", + reply[0]); + return -EREMOTEIO; + } + + switch (reply[0] & AUX_I2C_REPLY_MASK) { + case AUX_I2C_REPLY_ACK: + if (mode == MODE_I2C_READ) { + *read_byte = reply[1]; + } + return reply_bytes - 1; + case AUX_I2C_REPLY_NACK: + DRM_DEBUG_KMS("aux_i2c nack\n"); + return -EREMOTEIO; + case AUX_I2C_REPLY_DEFER: + DRM_DEBUG_KMS("aux_i2c defer\n"); + udelay(100); + break; + default: + DRM_ERROR("aux_i2c invalid reply 0x%02x\n", reply[0]); + return -EREMOTEIO; + } + } + + DRM_ERROR("too many retries, giving up\n"); + return -EREMOTEIO; +} + +static int +psb_intel_dp_i2c_init(struct psb_intel_output *output, const char *name) +{ + struct psb_intel_dp *intel_dp = output->dev_priv; + DRM_DEBUG_KMS("i2c_init %s\n", name); + intel_dp->algo.running = false; + intel_dp->algo.address = 0; + intel_dp->algo.aux_ch = psb_intel_dp_i2c_aux_ch; + + memset(&intel_dp->adapter, '\0', sizeof (intel_dp->adapter)); + intel_dp->adapter.owner = THIS_MODULE; + intel_dp->adapter.class = I2C_CLASS_DDC; + strncpy (intel_dp->adapter.name, name, sizeof(intel_dp->adapter.name) - 1); + intel_dp->adapter.name[sizeof(intel_dp->adapter.name) - 1] = '\0'; + intel_dp->adapter.algo_data = &intel_dp->algo; + intel_dp->adapter.dev.parent = &output->base.kdev; + + return i2c_dp_aux_add_bus(&intel_dp->adapter); +} + +static bool +psb_intel_dp_mode_fixup(struct drm_encoder *encoder, struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + struct psb_intel_output *output = enc_to_psb_intel_output(encoder); + struct psb_intel_dp *intel_dp = output->dev_priv; + int lane_count, clock; + int max_lane_count = psb_intel_dp_max_lane_count(output); + int max_clock = psb_intel_dp_max_link_bw(output) == DP_LINK_BW_2_7 ? 1 : 0; + static int bws[2] = { DP_LINK_BW_1_62, DP_LINK_BW_2_7 }; + + + for (lane_count = 1; lane_count <= max_lane_count; lane_count <<= 1) { + for (clock = max_clock; clock >= 0; clock--) { + int link_avail = psb_intel_dp_max_data_rate(psb_intel_dp_link_clock(bws[clock]), lane_count); + + if (psb_intel_dp_link_required(mode->clock, 24) + <= link_avail) { + intel_dp->link_bw = bws[clock]; + intel_dp->lane_count = lane_count; + adjusted_mode->clock = psb_intel_dp_link_clock(intel_dp->link_bw); + DRM_DEBUG_KMS("Display port link bw %02x lane " + "count %d clock %d\n", + intel_dp->link_bw, intel_dp->lane_count, + adjusted_mode->clock); + return true; + } + } + } + + return false; +} + +struct psb_intel_dp_m_n { + uint32_t tu; + uint32_t gmch_m; + uint32_t gmch_n; + uint32_t link_m; + uint32_t link_n; +}; + +static void +psb_intel_reduce_ratio(uint32_t *num, uint32_t *den) +{ + /* + while (*num > 0xffffff || *den > 0xffffff) { + *num >>= 1; + *den >>= 1; + }*/ + uint64_t value, m; + m = *num; + value = m * (0x800000); + m = do_div(value, *den); + *num = value; + *den = 0x800000; +} + +static void +psb_intel_dp_compute_m_n(int bpp, + int nlanes, + int pixel_clock, + int link_clock, + struct psb_intel_dp_m_n *m_n) +{ + m_n->tu = 64; + m_n->gmch_m = (pixel_clock * bpp + 7) >> 3; + m_n->gmch_n = link_clock * nlanes; + psb_intel_reduce_ratio(&m_n->gmch_m, &m_n->gmch_n); + m_n->link_m = pixel_clock; + m_n->link_n = link_clock; + psb_intel_reduce_ratio(&m_n->link_m, &m_n->link_n); +} + +void +psb_intel_dp_set_m_n(struct drm_crtc *crtc, struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + struct drm_device *dev = crtc->dev; + struct drm_mode_config *mode_config = &dev->mode_config; + struct drm_encoder *encoder; + struct psb_intel_crtc *intel_crtc = to_psb_intel_crtc(crtc); + int lane_count = 4, bpp = 24; + struct psb_intel_dp_m_n m_n; + int pipe = intel_crtc->pipe; + + /* + * Find the lane count in the intel_encoder private + */ + list_for_each_entry(encoder, &mode_config->encoder_list, head) { + struct psb_intel_output *intel_output; + struct psb_intel_dp *intel_dp; + + if (encoder->crtc != crtc) + continue; + + intel_output = enc_to_psb_intel_output(encoder); + intel_dp = intel_output->dev_priv; + if (intel_output->type == INTEL_OUTPUT_DISPLAYPORT) { + lane_count = intel_dp->lane_count; + break; + } else if (is_edp(intel_output)) { + lane_count = intel_dp->lane_count; + break; + } + } + + /* + * Compute the GMCH and Link ratios. The '3' here is + * the number of bytes_per_pixel post-LUT, which we always + * set up for 8-bits of R/G/B, or 3 bytes total. + */ + psb_intel_dp_compute_m_n(bpp, lane_count, + mode->clock, adjusted_mode->clock, &m_n); + + { + REG_WRITE(PIPE_GMCH_DATA_M(pipe), + ((m_n.tu - 1) << PIPE_GMCH_DATA_M_TU_SIZE_SHIFT) | + m_n.gmch_m); + REG_WRITE(PIPE_GMCH_DATA_N(pipe), m_n.gmch_n); + REG_WRITE(PIPE_DP_LINK_M(pipe), m_n.link_m); + REG_WRITE(PIPE_DP_LINK_N(pipe), m_n.link_n); + } +} + +static void +psb_intel_dp_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + struct psb_intel_output *intel_output = enc_to_psb_intel_output(encoder); + struct drm_crtc *crtc = encoder->crtc; + struct psb_intel_crtc *intel_crtc = to_psb_intel_crtc(crtc); + struct psb_intel_dp *intel_dp = intel_output->dev_priv; + + + intel_dp->DP = DP_VOLTAGE_0_4 | DP_PRE_EMPHASIS_0; + intel_dp->DP |= intel_dp->color_range; + + if (adjusted_mode->flags & DRM_MODE_FLAG_PHSYNC) + intel_dp->DP |= DP_SYNC_HS_HIGH; + if (adjusted_mode->flags & DRM_MODE_FLAG_PVSYNC) + intel_dp->DP |= DP_SYNC_VS_HIGH; + + intel_dp->DP |= DP_LINK_TRAIN_OFF; + + switch (intel_dp->lane_count) { + case 1: + intel_dp->DP |= DP_PORT_WIDTH_1; + break; + case 2: + intel_dp->DP |= DP_PORT_WIDTH_2; + break; + case 4: + intel_dp->DP |= DP_PORT_WIDTH_4; + break; + } + if (intel_dp->has_audio) + intel_dp->DP |= DP_AUDIO_OUTPUT_ENABLE; + + memset(intel_dp->link_configuration, 0, DP_LINK_CONFIGURATION_SIZE); + intel_dp->link_configuration[0] = intel_dp->link_bw; + intel_dp->link_configuration[1] = intel_dp->lane_count; + + /* + * Check for DPCD version > 1.1 and enhanced framing support + */ + if (intel_dp->dpcd[DP_DPCD_REV] >= 0x11 && + (intel_dp->dpcd[DP_MAX_LANE_COUNT] & DP_ENHANCED_FRAME_CAP)) { + intel_dp->link_configuration[1] |= DP_LANE_COUNT_ENHANCED_FRAME_EN; + intel_dp->DP |= DP_ENHANCED_FRAMING; + } + + /* CPT DP's pipe select is decided in TRANS_DP_CTL */ + if (intel_crtc->pipe == 1) + intel_dp->DP |= DP_PIPEB_SELECT; + + DRM_DEBUG_KMS("DP expected reg is %x\n", intel_dp->DP); +} + + +/* If the sink supports it, try to set the power state appropriately */ +static void psb_intel_dp_sink_dpms(struct psb_intel_output *output, int mode) +{ + struct psb_intel_dp *intel_dp = output->dev_priv; + int ret, i; + + /* Should have a valid DPCD by this point */ + if (intel_dp->dpcd[DP_DPCD_REV] < 0x11) + return; + + if (mode != DRM_MODE_DPMS_ON) { + ret = psb_intel_dp_aux_native_write_1(output, DP_SET_POWER, + DP_SET_POWER_D3); + if (ret != 1) + DRM_DEBUG_DRIVER("failed to write sink power state\n"); + } else { + /* + * When turning on, we need to retry for 1ms to give the sink + * time to wake up. + */ + for (i = 0; i < 3; i++) { + ret = psb_intel_dp_aux_native_write_1(output, + DP_SET_POWER, + DP_SET_POWER_D0); + if (ret == 1) + break; + udelay(1000); + } + } +} + +static void psb_intel_dp_prepare(struct drm_encoder *encoder) +{ + struct psb_intel_output *output = enc_to_psb_intel_output(encoder); + + /* Wake up the sink first */ + psb_intel_dp_sink_dpms(output, DRM_MODE_DPMS_ON); + + psb_intel_dp_link_down(output); +} + +static void psb_intel_dp_commit(struct drm_encoder *encoder) +{ + struct psb_intel_output *output = enc_to_psb_intel_output(encoder); + + psb_intel_dp_start_link_train(output); + + psb_intel_dp_complete_link_train(output); + +} + +static void +psb_intel_dp_dpms(struct drm_encoder *encoder, int mode) +{ + struct psb_intel_output *intel_output = enc_to_psb_intel_output(encoder); + struct psb_intel_dp *intel_dp = intel_output->dev_priv; + struct drm_device *dev = encoder->dev; + uint32_t dp_reg = REG_READ(intel_dp->output_reg); + + if (mode != DRM_MODE_DPMS_ON) { + psb_intel_dp_sink_dpms(intel_output, mode); + psb_intel_dp_link_down(intel_output); + } else { + psb_intel_dp_sink_dpms(intel_output, mode); + if (!(dp_reg & DP_PORT_EN)) { + psb_intel_dp_start_link_train(intel_output); + psb_intel_dp_complete_link_train(intel_output); + } + } +} + +/* + * Native read with retry for link status and receiver capability reads for + * cases where the sink may still be asleep. + */ +static bool +psb_intel_dp_aux_native_read_retry(struct psb_intel_output *output, uint16_t address, + uint8_t *recv, int recv_bytes) +{ + int ret, i; + + /* + * Sinks are *supposed* to come up within 1ms from an off state, + * but we're also supposed to retry 3 times per the spec. + */ + for (i = 0; i < 3; i++) { + ret = psb_intel_dp_aux_native_read(output, address, recv, + recv_bytes); + if (ret == recv_bytes) + return true; + udelay(1000); + } + + return false; +} + +/* + * Fetch AUX CH registers 0x202 - 0x207 which contain + * link status information + */ +static bool +psb_intel_dp_get_link_status(struct psb_intel_output *output) +{ + struct psb_intel_dp *intel_dp = output->dev_priv; + return psb_intel_dp_aux_native_read_retry(output, + DP_LANE0_1_STATUS, + intel_dp->link_status, + DP_LINK_STATUS_SIZE); +} + +static uint8_t +psb_intel_dp_link_status(uint8_t link_status[DP_LINK_STATUS_SIZE], + int r) +{ + return link_status[r - DP_LANE0_1_STATUS]; +} + +static uint8_t +psb_intel_get_adjust_request_voltage(uint8_t link_status[DP_LINK_STATUS_SIZE], + int lane) +{ + int i = DP_ADJUST_REQUEST_LANE0_1 + (lane >> 1); + int s = ((lane & 1) ? + DP_ADJUST_VOLTAGE_SWING_LANE1_SHIFT : + DP_ADJUST_VOLTAGE_SWING_LANE0_SHIFT); + uint8_t l = psb_intel_dp_link_status(link_status, i); + + return ((l >> s) & 3) << DP_TRAIN_VOLTAGE_SWING_SHIFT; +} + +static uint8_t +psb_intel_get_adjust_request_pre_emphasis(uint8_t link_status[DP_LINK_STATUS_SIZE], + int lane) +{ + int i = DP_ADJUST_REQUEST_LANE0_1 + (lane >> 1); + int s = ((lane & 1) ? + DP_ADJUST_PRE_EMPHASIS_LANE1_SHIFT : + DP_ADJUST_PRE_EMPHASIS_LANE0_SHIFT); + uint8_t l = psb_intel_dp_link_status(link_status, i); + + return ((l >> s) & 3) << DP_TRAIN_PRE_EMPHASIS_SHIFT; +} + + +#if 0 +static char *voltage_names[] = { + "0.4V", "0.6V", "0.8V", "1.2V" +}; +static char *pre_emph_names[] = { + "0dB", "3.5dB", "6dB", "9.5dB" +}; +static char *link_train_names[] = { + "pattern 1", "pattern 2", "idle", "off" +}; +#endif + +#define CDV_DP_VOLTAGE_MAX DP_TRAIN_VOLTAGE_SWING_1200 +/* +static uint8_t +psb_intel_dp_pre_emphasis_max(uint8_t voltage_swing) +{ + switch (voltage_swing & DP_TRAIN_VOLTAGE_SWING_MASK) { + case DP_TRAIN_VOLTAGE_SWING_400: + return DP_TRAIN_PRE_EMPHASIS_6; + case DP_TRAIN_VOLTAGE_SWING_600: + return DP_TRAIN_PRE_EMPHASIS_6; + case DP_TRAIN_VOLTAGE_SWING_800: + return DP_TRAIN_PRE_EMPHASIS_3_5; + case DP_TRAIN_VOLTAGE_SWING_1200: + default: + return DP_TRAIN_PRE_EMPHASIS_0; + } +} +*/ +static void +psb_intel_get_adjust_train(struct psb_intel_output *output) +{ + struct psb_intel_dp *intel_dp = output->dev_priv; + uint8_t v = 0; + uint8_t p = 0; + int lane; + + for (lane = 0; lane < intel_dp->lane_count; lane++) { + uint8_t this_v = psb_intel_get_adjust_request_voltage(intel_dp->link_status, lane); + uint8_t this_p = psb_intel_get_adjust_request_pre_emphasis(intel_dp->link_status, lane); + + if (this_v > v) + v = this_v; + if (this_p > p) + p = this_p; + } + + if (v >= CDV_DP_VOLTAGE_MAX) + v = CDV_DP_VOLTAGE_MAX | DP_TRAIN_MAX_SWING_REACHED; + + if (p == DP_TRAIN_PRE_EMPHASIS_MASK) + p |= DP_TRAIN_MAX_PRE_EMPHASIS_REACHED; + + for (lane = 0; lane < 4; lane++) + intel_dp->train_set[lane] = v | p; +} + + +static uint8_t +psb_intel_get_lane_status(uint8_t link_status[DP_LINK_STATUS_SIZE], + int lane) +{ + int i = DP_LANE0_1_STATUS + (lane >> 1); + int s = (lane & 1) * 4; + uint8_t l = psb_intel_dp_link_status(link_status, i); + + return (l >> s) & 0xf; +} + +/* Check for clock recovery is done on all channels */ +static bool +psb_intel_clock_recovery_ok(uint8_t link_status[DP_LINK_STATUS_SIZE], int lane_count) +{ + int lane; + uint8_t lane_status; + + for (lane = 0; lane < lane_count; lane++) { + lane_status = psb_intel_get_lane_status(link_status, lane); + if ((lane_status & DP_LANE_CR_DONE) == 0) + return false; + } + return true; +} + +/* Check to see if channel eq is done on all channels */ +#define CHANNEL_EQ_BITS (DP_LANE_CR_DONE|\ + DP_LANE_CHANNEL_EQ_DONE|\ + DP_LANE_SYMBOL_LOCKED) +static bool +psb_intel_channel_eq_ok(struct psb_intel_output *output) +{ + struct psb_intel_dp *intel_dp = output->dev_priv; + uint8_t lane_align; + uint8_t lane_status; + int lane; + + lane_align = psb_intel_dp_link_status(intel_dp->link_status, + DP_LANE_ALIGN_STATUS_UPDATED); + if ((lane_align & DP_INTERLANE_ALIGN_DONE) == 0) + return false; + for (lane = 0; lane < intel_dp->lane_count; lane++) { + lane_status = psb_intel_get_lane_status(intel_dp->link_status, lane); + if ((lane_status & CHANNEL_EQ_BITS) != CHANNEL_EQ_BITS) + return false; + } + return true; +} + +static bool +psb_intel_dp_set_link_train(struct psb_intel_output *output, + uint32_t dp_reg_value, + uint8_t dp_train_pat) +{ + + struct drm_device *dev = output->base.dev; + int ret; + struct psb_intel_dp *intel_dp = output->dev_priv; + + REG_WRITE(intel_dp->output_reg, dp_reg_value); + REG_READ(intel_dp->output_reg); + + ret = psb_intel_dp_aux_native_write_1(output, + DP_TRAINING_PATTERN_SET, + dp_train_pat); + + if (ret != 1) { + DRM_DEBUG_KMS("Failure in setting link pattern %x\n", + dp_train_pat); + return false; + } + + return true; +} + + +static bool +psb_intel_dplink_set_level(struct psb_intel_output *output, + uint8_t dp_train_pat) +{ + + int ret; + struct psb_intel_dp *intel_dp = output->dev_priv; + + ret = psb_intel_dp_aux_native_write(output, + DP_TRAINING_LANE0_SET, + intel_dp->train_set, + intel_dp->lane_count); + + if (ret != intel_dp->lane_count) { + DRM_DEBUG_KMS("Failure in setting level %d, lane_cnt= %d\n", + intel_dp->train_set[0], intel_dp->lane_count); + return false; + } + return true; +} + +static void +psb_intel_dp_set_vswing_premph(struct psb_intel_output *output, uint8_t signal_level) +{ + struct drm_device *dev = output->base.dev; + struct psb_intel_dp *intel_dp = output->dev_priv; + struct ddi_regoff *ddi_reg; + int vswing, premph, index; + + if (intel_dp->output_reg == DP_B) + ddi_reg = &ddi_DP_train_table[0]; + else + ddi_reg = &ddi_DP_train_table[1]; + + vswing = (signal_level & DP_TRAIN_VOLTAGE_SWING_MASK); + premph = ((signal_level & DP_TRAIN_PRE_EMPHASIS_MASK)) >> + DP_TRAIN_PRE_EMPHASIS_SHIFT; + + if (vswing + premph > 3) + return; +#ifdef CDV_FAST_LINK_TRAIN + return; +#endif + DRM_DEBUG_KMS("Test2\n"); + //return ; + psb_sb_reset(dev); + /* ;Swing voltage programming + ;gfx_dpio_set_reg(0xc058, 0x0505313A) */ + psb_sb_write(dev, ddi_reg->VSwing5, 0x0505313A); + + /* ;gfx_dpio_set_reg(0x8154, 0x43406055) */ + psb_sb_write(dev, ddi_reg->VSwing1, 0x43406055); + + /* ;gfx_dpio_set_reg(0x8148, 0x55338954) + * The VSwing_PreEmph table is also considered based on the vswing/premp + */ + index = (vswing + premph) * 2; + if (premph == 1 && vswing == 1) { + psb_sb_write(dev, ddi_reg->VSwing2, 0x055738954); + } else + psb_sb_write(dev, ddi_reg->VSwing2, dp_vswing_premph_table[index]); + + /* ;gfx_dpio_set_reg(0x814c, 0x40802040) */ + if ((vswing + premph) == DP_TRAIN_VOLTAGE_SWING_1200) + psb_sb_write(dev, ddi_reg->VSwing3, 0x70802040); + else + psb_sb_write(dev, ddi_reg->VSwing3, 0x40802040); + + /* ;gfx_dpio_set_reg(0x8150, 0x2b405555) */ + //psb_sb_write(dev, ddi_reg->VSwing4, 0x2b405555); + + /* ;gfx_dpio_set_reg(0x8154, 0xc3406055) */ + psb_sb_write(dev, ddi_reg->VSwing1, 0xc3406055); + + /* ;Pre emphasis programming + * ;gfx_dpio_set_reg(0xc02c, 0x1f030040) + */ + psb_sb_write(dev, ddi_reg->PreEmph1, 0x1f030040); + + /* ;gfx_dpio_set_reg(0x8124, 0x00004000) */ + index = 2 * premph + 1; + psb_sb_write(dev, ddi_reg->PreEmph2, dp_vswing_premph_table[index]); + return; +} + + +/* Enable corresponding port and start training pattern 1 */ +static void +psb_intel_dp_start_link_train(struct psb_intel_output *output) +{ + struct drm_device *dev = output->base.dev; + struct psb_intel_dp *intel_dp = output->dev_priv; + int i; + uint8_t voltage; + bool clock_recovery = false; + int tries; + u32 reg; + uint32_t DP = intel_dp->DP; + + DP |= DP_PORT_EN; + DP &= ~DP_LINK_TRAIN_MASK; + + reg = DP; + reg |= DP_LINK_TRAIN_PAT_1; + /* Enable output, wait for it to become active */ + REG_WRITE(intel_dp->output_reg, reg); + REG_READ(intel_dp->output_reg); + psb_intel_wait_for_vblank(dev); + + DRM_DEBUG_KMS("Link config\n"); + /* Write the link configuration data */ + psb_intel_dp_aux_native_write(output, DP_LINK_BW_SET, + intel_dp->link_configuration, + 2); + + memset(intel_dp->train_set, 0, 4); + voltage = 0; + tries = 0; + clock_recovery = false; + + DRM_DEBUG_KMS("Start train\n"); + reg = DP | DP_LINK_TRAIN_PAT_1; + + + for (;;) { + /* Use intel_dp->train_set[0] to set the voltage and pre emphasis values */ + + if (!psb_intel_dp_set_link_train(output, reg, DP_TRAINING_PATTERN_1)) { + DRM_DEBUG_KMS("Failure in aux-transfer setting pattern 1\n"); + } + psb_intel_dp_set_vswing_premph(output, intel_dp->train_set[0]); + /* Set training pattern 1 */ + + psb_intel_dplink_set_level(output, DP_TRAINING_PATTERN_1); + + udelay(200); + if (!psb_intel_dp_get_link_status(output)) + break; + + if (psb_intel_clock_recovery_ok(intel_dp->link_status, intel_dp->lane_count)) { + DRM_DEBUG_KMS("PT1 train is done\n"); + clock_recovery = true; + break; + } + + /* Check to see if we've tried the max voltage */ + for (i = 0; i < intel_dp->lane_count; i++) + if ((intel_dp->train_set[i] & DP_TRAIN_MAX_SWING_REACHED) == 0) + break; + if (i == intel_dp->lane_count) + break; + + /* Check to see if we've tried the same voltage 5 times */ + if ((intel_dp->train_set[0] & DP_TRAIN_VOLTAGE_SWING_MASK) == voltage) { + ++tries; + if (tries == 5) + break; + } else + tries = 0; + voltage = intel_dp->train_set[0] & DP_TRAIN_VOLTAGE_SWING_MASK; + + /* Compute new intel_dp->train_set as requested by target */ + psb_intel_get_adjust_train(output); + + } + + if (!clock_recovery) { + DRM_DEBUG_KMS("failure in DP patter 1 training, train set %x\n", intel_dp->train_set[0]); + } + + intel_dp->DP = DP; +} + +static void +psb_intel_dp_complete_link_train(struct psb_intel_output *output) +{ + struct drm_device *dev = output->base.dev; + struct psb_intel_dp *intel_dp = output->dev_priv; + bool channel_eq = false; + int tries, cr_tries; + u32 reg; + uint32_t DP = intel_dp->DP; + + /* channel equalization */ + tries = 0; + cr_tries = 0; + channel_eq = false; + + DRM_DEBUG_KMS("\n"); + reg = DP | DP_LINK_TRAIN_PAT_2; + + for (;;) { + /* channel eq pattern */ + if (!psb_intel_dp_set_link_train(output, reg, + DP_TRAINING_PATTERN_2)) { + DRM_DEBUG_KMS("Failure in aux-transfer setting pattern 2\n"); + } + /* Use intel_dp->train_set[0] to set the voltage and pre emphasis values */ + + if (cr_tries > 5) { + DRM_ERROR("failed to train DP, aborting\n"); + psb_intel_dp_link_down(output); + break; + } + + psb_intel_dp_set_vswing_premph(output, intel_dp->train_set[0]); + + psb_intel_dplink_set_level(output, DP_TRAINING_PATTERN_2); + + udelay(1000); + if (!psb_intel_dp_get_link_status(output)) + break; + + /* Make sure clock is still ok */ + if (!psb_intel_clock_recovery_ok(intel_dp->link_status, intel_dp->lane_count)) { + psb_intel_dp_start_link_train(output); + cr_tries++; + continue; + } + + if (psb_intel_channel_eq_ok(output)) { + DRM_DEBUG_KMS("PT2 train is done\n"); + channel_eq = true; + break; + } + + /* Try 5 times, then try clock recovery if that fails */ + if (tries > 5) { + psb_intel_dp_link_down(output); + psb_intel_dp_start_link_train(output); + tries = 0; + cr_tries++; + continue; + } + + /* Compute new intel_dp->train_set as requested by target */ + psb_intel_get_adjust_train(output); + ++tries; + + } + + reg = DP | DP_LINK_TRAIN_OFF; + + REG_WRITE(intel_dp->output_reg, reg); + REG_READ(intel_dp->output_reg); + psb_intel_dp_aux_native_write_1(output, + DP_TRAINING_PATTERN_SET, DP_TRAINING_PATTERN_DISABLE); +} + +static void +psb_intel_dp_link_down(struct psb_intel_output *output) +{ + struct drm_device *dev = output->base.dev; + struct psb_intel_dp *intel_dp = output->dev_priv; + uint32_t DP = intel_dp->DP; + + if ((REG_READ(intel_dp->output_reg) & DP_PORT_EN) == 0) + return; + + DRM_DEBUG_KMS("\n"); + + + { + DP &= ~DP_LINK_TRAIN_MASK; + REG_WRITE(intel_dp->output_reg, DP | DP_LINK_TRAIN_PAT_IDLE); + } + REG_READ(intel_dp->output_reg); + + msleep(17); + + REG_WRITE(intel_dp->output_reg, DP & ~DP_PORT_EN); + REG_READ(intel_dp->output_reg); +} + +static enum drm_connector_status +cdv_dp_detect(struct psb_intel_output *output) +{ + struct psb_intel_dp *intel_dp = output->dev_priv; + enum drm_connector_status status; + + status = connector_status_disconnected; + if (psb_intel_dp_aux_native_read(output, 0x000, intel_dp->dpcd, + sizeof (intel_dp->dpcd)) == sizeof (intel_dp->dpcd)) + { + if (intel_dp->dpcd[DP_DPCD_REV] != 0) + status = connector_status_connected; + } + if (status == connector_status_connected) + DRM_DEBUG_KMS("DPCD: Rev=%x LN_Rate=%x LN_CNT=%x LN_DOWNSP=%x\n", + intel_dp->dpcd[0], intel_dp->dpcd[1], + intel_dp->dpcd[2], intel_dp->dpcd[3]); + return status; +} + +/** + * Uses CRT_HOTPLUG_EN and CRT_HOTPLUG_STAT to detect DP connection. + * + * \return true if DP port is connected. + * \return false if DP port is disconnected. + */ +static enum drm_connector_status +psb_intel_dp_detect(struct drm_connector *connector, bool force) +{ + struct psb_intel_output *output = to_psb_intel_output(connector); + struct psb_intel_dp *intel_dp = output->dev_priv; + enum drm_connector_status status; + struct edid *edid = NULL; + + intel_dp->has_audio = false; + + status = cdv_dp_detect(output); + if (status != connector_status_connected) + return status; + + if (intel_dp->force_audio) { + intel_dp->has_audio = intel_dp->force_audio > 0; + } else { + edid = drm_get_edid(connector, &intel_dp->adapter); + if (edid) { + intel_dp->has_audio = drm_detect_monitor_audio(edid); + connector->display_info.raw_edid = NULL; + kfree(edid); + } + } + + return connector_status_connected; +} + +static int psb_intel_dp_get_modes(struct drm_connector *connector) +{ + struct psb_intel_output *intel_output = to_psb_intel_output(connector); + struct psb_intel_dp *intel_dp = intel_output->dev_priv; + struct edid *edid = NULL; + int ret = 0; + + + edid = drm_get_edid(&intel_output->base, + &intel_dp->adapter); + if (edid) { + drm_mode_connector_update_edid_property(&intel_output-> + base, edid); + ret = drm_add_edid_modes(&intel_output->base, edid); + kfree(edid); + } + + return ret; +} + +static bool +psb_intel_dp_detect_audio(struct drm_connector *connector) +{ + struct psb_intel_output *output = to_psb_intel_output(connector); + struct psb_intel_dp *intel_dp = output->dev_priv; + struct edid *edid; + bool has_audio = false; + + edid = drm_get_edid(connector, &intel_dp->adapter); + if (edid) { + has_audio = drm_detect_monitor_audio(edid); + + connector->display_info.raw_edid = NULL; + kfree(edid); + } + + return has_audio; +} + +static int +psb_intel_dp_set_property(struct drm_connector *connector, + struct drm_property *property, + uint64_t val) +{ + struct drm_psb_private *dev_priv = connector->dev->dev_private; + struct psb_intel_output *output = to_psb_intel_output(connector); + struct psb_intel_dp *intel_dp = output->dev_priv; + int ret; + + ret = drm_connector_property_set_value(connector, property, val); + if (ret) + return ret; + + if (property == dev_priv->force_audio_property) { + int i = val; + bool has_audio; + + if (i == intel_dp->force_audio) + return 0; + + intel_dp->force_audio = i; + + if (i == 0) + has_audio = psb_intel_dp_detect_audio(connector); + else + has_audio = i > 0; + + if (has_audio == intel_dp->has_audio) + return 0; + + intel_dp->has_audio = has_audio; + goto done; + } + + if (property == dev_priv->broadcast_rgb_property) { + if (val == !!intel_dp->color_range) + return 0; + + intel_dp->color_range = val ? DP_COLOR_RANGE_16_235 : 0; + goto done; + } + + return -EINVAL; + +done: + if (output->enc.crtc) { + struct drm_crtc *crtc = output->enc.crtc; + drm_crtc_helper_set_mode(crtc, &crtc->mode, + crtc->x, crtc->y, + crtc->fb); + } + + return 0; +} + +static void +psb_intel_dp_destroy (struct drm_connector *connector) +{ + struct psb_intel_output *output = to_psb_intel_output(connector); + struct psb_intel_dp *intel_dp = output->dev_priv; + + i2c_del_adapter(&intel_dp->adapter); + drm_sysfs_connector_remove(connector); + drm_connector_cleanup(connector); + kfree(connector); +} + +static void psb_intel_dp_encoder_destroy(struct drm_encoder *encoder) +{ + drm_encoder_cleanup(encoder); +} + +static const struct drm_encoder_helper_funcs psb_intel_dp_helper_funcs = { + .dpms = psb_intel_dp_dpms, + .mode_fixup = psb_intel_dp_mode_fixup, + .prepare = psb_intel_dp_prepare, + .mode_set = psb_intel_dp_mode_set, + .commit = psb_intel_dp_commit, +}; + +static const struct drm_connector_funcs psb_intel_dp_connector_funcs = { + .dpms = drm_helper_connector_dpms, + .detect = psb_intel_dp_detect, + .fill_modes = drm_helper_probe_single_connector_modes, + .set_property = psb_intel_dp_set_property, + .destroy = psb_intel_dp_destroy, +}; + +static const struct drm_connector_helper_funcs psb_intel_dp_connector_helper_funcs = { + .get_modes = psb_intel_dp_get_modes, + .mode_valid = psb_intel_dp_mode_valid, + .best_encoder = psb_intel_best_encoder, +}; + +static const struct drm_encoder_funcs psb_intel_dp_enc_funcs = { + .destroy = psb_intel_dp_encoder_destroy, +}; + + +static void +psb_intel_dp_add_properties(struct psb_intel_output *output, struct drm_connector *connector) +{ + psb_intel_attach_force_audio_property(connector); + psb_intel_attach_broadcast_rgb_property(connector); +} + +void +psb_intel_dp_init(struct drm_device *dev, struct psb_intel_mode_device *mode_dev, int output_reg) +{ + struct drm_connector *connector; + struct drm_encoder *encoder; + struct psb_intel_output *psb_intel_output; + struct psb_intel_dp *intel_dp; + const char *name = NULL; + int type; + + psb_intel_output = kzalloc(sizeof(struct psb_intel_output) + + sizeof(struct psb_intel_dp), GFP_KERNEL); + if (!psb_intel_output) + return; + + intel_dp = (struct psb_intel_dp *)(psb_intel_output + 1); + psb_intel_output->mode_dev = mode_dev; + connector = &psb_intel_output->base; + encoder = &psb_intel_output->enc; + psb_intel_output->dev_priv=intel_dp; + intel_dp->output = psb_intel_output; + + intel_dp->output_reg = output_reg; + + type = DRM_MODE_CONNECTOR_DisplayPort; + psb_intel_output->type = INTEL_OUTPUT_DISPLAYPORT; + + drm_connector_init(dev, connector, &psb_intel_dp_connector_funcs, type); + drm_connector_helper_add(connector, &psb_intel_dp_connector_helper_funcs); + + connector->polled = DRM_CONNECTOR_POLL_HPD; + + connector->interlace_allowed = 0; + connector->doublescan_allowed = 0; + + drm_encoder_init(dev, encoder, &psb_intel_dp_enc_funcs, + DRM_MODE_ENCODER_TMDS); + drm_encoder_helper_add(encoder, &psb_intel_dp_helper_funcs); + + drm_mode_connector_attach_encoder(&psb_intel_output->base, + &psb_intel_output->enc); + + drm_sysfs_connector_add(connector); + + /* Set up the DDC bus. */ + switch (output_reg) { + case DP_B: + name = "DPDDC-B"; + psb_intel_output->ddi_select = (DP_MASK | DDI0_SELECT); + break; + case DP_C: + name = "DPDDC-C"; + psb_intel_output->ddi_select = (DP_MASK | DDI1_SELECT); + break; + } + + psb_intel_dp_i2c_init(psb_intel_output, name); + psb_intel_dp_add_properties(psb_intel_output, connector); + +} diff --git a/drivers/gpu/drm/gma500/psb_intel_drv.h b/drivers/gpu/drm/gma500/psb_intel_drv.h index 7b357b0..73214e2 100644 --- a/drivers/gpu/drm/gma500/psb_intel_drv.h +++ b/drivers/gpu/drm/gma500/psb_intel_drv.h @@ -29,10 +29,6 @@ * Display related stuff */ -/* store information about an Ixxx DVO */ -/* The i830->i865 use multiple DVOs with multiple i2cs */ -/* the i915, i945 have a single sDVO i2c bus - which is different */ -#define MAX_OUTPUTS 6 /* maximum connectors per crtcs in the mode set */ #define INTELFB_CONN_LIMIT 4 @@ -69,6 +65,8 @@ #define INTEL_OUTPUT_HDMI 6 #define INTEL_OUTPUT_MIPI 7 #define INTEL_OUTPUT_MIPI2 8 +#define INTEL_OUTPUT_DISPLAYPORT 9 +#define INTEL_OUTPUT_EDP 10 #define INTEL_DVO_CHIP_NONE 0 #define INTEL_DVO_CHIP_LVDS 1 diff --git a/drivers/gpu/drm/gma500/psb_intel_reg.h b/drivers/gpu/drm/gma500/psb_intel_reg.h index 8e8c8ef..d3091fc 100644 --- a/drivers/gpu/drm/gma500/psb_intel_reg.h +++ b/drivers/gpu/drm/gma500/psb_intel_reg.h @@ -1347,5 +1347,160 @@ No status bits are changed. #define LANE_PLL_ENABLE (0x3 << 20) #define LANE_PLL_PIPE(p) (((p) == 0) ? (1 << 21) : (0 << 21)) +#define DP_B 0x64100 +#define DP_C 0x64200 + +#define DP_PORT_EN (1 << 31) +#define DP_PIPEB_SELECT (1 << 30) +#define DP_PIPE_MASK (1 << 30) + +/* Link training mode - select a suitable mode for each stage */ +#define DP_LINK_TRAIN_PAT_1 (0 << 28) +#define DP_LINK_TRAIN_PAT_2 (1 << 28) +#define DP_LINK_TRAIN_PAT_IDLE (2 << 28) +#define DP_LINK_TRAIN_OFF (3 << 28) +#define DP_LINK_TRAIN_MASK (3 << 28) +#define DP_LINK_TRAIN_SHIFT 28 + +/* Signal voltages. These are mostly controlled by the other end */ +#define DP_VOLTAGE_0_4 (0 << 25) +#define DP_VOLTAGE_0_6 (1 << 25) +#define DP_VOLTAGE_0_8 (2 << 25) +#define DP_VOLTAGE_1_2 (3 << 25) +#define DP_VOLTAGE_MASK (7 << 25) +#define DP_VOLTAGE_SHIFT 25 + +/* Signal pre-emphasis levels, like voltages, the other end tells us what + * they want + */ +#define DP_PRE_EMPHASIS_0 (0 << 22) +#define DP_PRE_EMPHASIS_3_5 (1 << 22) +#define DP_PRE_EMPHASIS_6 (2 << 22) +#define DP_PRE_EMPHASIS_9_5 (3 << 22) +#define DP_PRE_EMPHASIS_MASK (7 << 22) +#define DP_PRE_EMPHASIS_SHIFT 22 + +/* How many wires to use. I guess 3 was too hard */ +#define DP_PORT_WIDTH_1 (0 << 19) +#define DP_PORT_WIDTH_2 (1 << 19) +#define DP_PORT_WIDTH_4 (3 << 19) +#define DP_PORT_WIDTH_MASK (7 << 19) + +/* Mystic DPCD version 1.1 special mode */ +#define DP_ENHANCED_FRAMING (1 << 18) + +/** locked once port is enabled */ +#define DP_PORT_REVERSAL (1 << 15) + +/** sends the clock on lane 15 of the PEG for debug */ +#define DP_CLOCK_OUTPUT_ENABLE (1 << 13) + +#define DP_SCRAMBLING_DISABLE (1 << 12) +#define DP_SCRAMBLING_DISABLE_IRONLAKE (1 << 7) + +/** limit RGB values to avoid confusing TVs */ +#define DP_COLOR_RANGE_16_235 (1 << 8) + +/** Turn on the audio link */ +#define DP_AUDIO_OUTPUT_ENABLE (1 << 6) + +/** vs and hs sync polarity */ +#define DP_SYNC_VS_HIGH (1 << 4) +#define DP_SYNC_HS_HIGH (1 << 3) + +/** A fantasy */ +#define DP_DETECTED (1 << 2) + +/** The aux channel provides a way to talk to the + * signal sink for DDC etc. Max packet size supported + * is 20 bytes in each direction, hence the 5 fixed + * data registers + */ +#define DPB_AUX_CH_CTL 0x64110 +#define DPB_AUX_CH_DATA1 0x64114 +#define DPB_AUX_CH_DATA2 0x64118 +#define DPB_AUX_CH_DATA3 0x6411c +#define DPB_AUX_CH_DATA4 0x64120 +#define DPB_AUX_CH_DATA5 0x64124 + +#define DPC_AUX_CH_CTL 0x64210 +#define DPC_AUX_CH_DATA1 0x64214 +#define DPC_AUX_CH_DATA2 0x64218 +#define DPC_AUX_CH_DATA3 0x6421c +#define DPC_AUX_CH_DATA4 0x64220 +#define DPC_AUX_CH_DATA5 0x64224 + +#define DP_AUX_CH_CTL_SEND_BUSY (1 << 31) +#define DP_AUX_CH_CTL_DONE (1 << 30) +#define DP_AUX_CH_CTL_INTERRUPT (1 << 29) +#define DP_AUX_CH_CTL_TIME_OUT_ERROR (1 << 28) +#define DP_AUX_CH_CTL_TIME_OUT_400us (0 << 26) +#define DP_AUX_CH_CTL_TIME_OUT_600us (1 << 26) +#define DP_AUX_CH_CTL_TIME_OUT_800us (2 << 26) +#define DP_AUX_CH_CTL_TIME_OUT_1600us (3 << 26) +#define DP_AUX_CH_CTL_TIME_OUT_MASK (3 << 26) +#define DP_AUX_CH_CTL_RECEIVE_ERROR (1 << 25) +#define DP_AUX_CH_CTL_MESSAGE_SIZE_MASK (0x1f << 20) +#define DP_AUX_CH_CTL_MESSAGE_SIZE_SHIFT 20 +#define DP_AUX_CH_CTL_PRECHARGE_2US_MASK (0xf << 16) +#define DP_AUX_CH_CTL_PRECHARGE_2US_SHIFT 16 +#define DP_AUX_CH_CTL_AUX_AKSV_SELECT (1 << 15) +#define DP_AUX_CH_CTL_MANCHESTER_TEST (1 << 14) +#define DP_AUX_CH_CTL_SYNC_TEST (1 << 13) +#define DP_AUX_CH_CTL_DEGLITCH_TEST (1 << 12) +#define DP_AUX_CH_CTL_PRECHARGE_TEST (1 << 11) +#define DP_AUX_CH_CTL_BIT_CLOCK_2X_MASK (0x7ff) +#define DP_AUX_CH_CTL_BIT_CLOCK_2X_SHIFT 0 + +/* + * Computing GMCH M and N values for the Display Port link + * + * GMCH M/N = dot clock * bytes per pixel / ls_clk * # of lanes + * + * ls_clk (we assume) is the DP link clock (1.62 or 2.7 GHz) + * + * The GMCH value is used internally + * + * bytes_per_pixel is the number of bytes coming out of the plane, + * which is after the LUTs, so we want the bytes for our color format. + * For our current usage, this is always 3, one byte for R, G and B. + */ + +#define _PIPEA_GMCH_DATA_M 0x70050 +#define _PIPEB_GMCH_DATA_M 0x71050 + +/* Transfer unit size for display port - 1, default is 0x3f (for TU size 64) */ +#define PIPE_GMCH_DATA_M_TU_SIZE_MASK (0x3f << 25) +#define PIPE_GMCH_DATA_M_TU_SIZE_SHIFT 25 + +#define PIPE_GMCH_DATA_M_MASK (0xffffff) + +#define _PIPEA_GMCH_DATA_N 0x70054 +#define _PIPEB_GMCH_DATA_N 0x71054 +#define PIPE_GMCH_DATA_N_MASK (0xffffff) + +/* + * Computing Link M and N values for the Display Port link + * + * Link M / N = pixel_clock / ls_clk + * + * (the DP spec calls pixel_clock the 'strm_clk') + * + * The Link value is transmitted in the Main Stream + * Attributes and VB-ID. + */ + +#define _PIPEA_DP_LINK_M 0x70060 +#define _PIPEB_DP_LINK_M 0x71060 +#define PIPEA_DP_LINK_M_MASK (0xffffff) + +#define _PIPEA_DP_LINK_N 0x70064 +#define _PIPEB_DP_LINK_N 0x71064 +#define PIPEA_DP_LINK_N_MASK (0xffffff) + +#define PIPE_GMCH_DATA_M(pipe) _PIPE(pipe, _PIPEA_GMCH_DATA_M, _PIPEB_GMCH_DATA_M) +#define PIPE_GMCH_DATA_N(pipe) _PIPE(pipe, _PIPEA_GMCH_DATA_N, _PIPEB_GMCH_DATA_N) +#define PIPE_DP_LINK_M(pipe) _PIPE(pipe, _PIPEA_DP_LINK_M, _PIPEB_DP_LINK_M) +#define PIPE_DP_LINK_N(pipe) _PIPE(pipe, _PIPEA_DP_LINK_N, _PIPEB_DP_LINK_N) #endif -- cgit v0.10.2 From 220801bdb53ceeac01d021ac459d112acc7deb0b Mon Sep 17 00:00:00 2001 From: Alan Cox Date: Wed, 8 Aug 2012 13:54:41 +0000 Subject: gma500/cdv: add the bits that don't need the new code Based on bits from Yakui We can import various little bits of code before we plumb it all in and hopefully this way catch any regressions more easily. Signed-off-by: Alan Cox Signed-off-by: Dave Airlie diff --git a/drivers/gpu/drm/gma500/cdv_device.c b/drivers/gpu/drm/gma500/cdv_device.c index b7e7b49..e2fff24 100644 --- a/drivers/gpu/drm/gma500/cdv_device.c +++ b/drivers/gpu/drm/gma500/cdv_device.c @@ -488,6 +488,65 @@ static void cdv_hotplug_enable(struct drm_device *dev, bool on) } } +static const char *force_audio_names[] = { + "off", + "auto", + "on", +}; + +void cdv_intel_attach_force_audio_property(struct drm_connector *connector) +{ + struct drm_device *dev = connector->dev; + struct drm_psb_private *dev_priv = dev->dev_private; + struct drm_property *prop; + int i; + + prop = dev_priv->force_audio_property; + if (prop == NULL) { + prop = drm_property_create(dev, DRM_MODE_PROP_ENUM, + "audio", + ARRAY_SIZE(force_audio_names)); + if (prop == NULL) + return; + + for (i = 0; i < ARRAY_SIZE(force_audio_names); i++) + drm_property_add_enum(prop, i, i-1, force_audio_names[i]); + + dev_priv->force_audio_property = prop; + } + drm_connector_attach_property(connector, prop, 0); +} + + +static const char *broadcast_rgb_names[] = { + "Full", + "Limited 16:235", +}; + +void cdv_intel_attach_broadcast_rgb_property(struct drm_connector *connector) +{ + struct drm_device *dev = connector->dev; + struct drm_psb_private *dev_priv = dev->dev_private; + struct drm_property *prop; + int i; + + prop = dev_priv->broadcast_rgb_property; + if (prop == NULL) { + prop = drm_property_create(dev, DRM_MODE_PROP_ENUM, + "Broadcast RGB", + ARRAY_SIZE(broadcast_rgb_names)); + if (prop == NULL) + return; + + for (i = 0; i < ARRAY_SIZE(broadcast_rgb_names); i++) + drm_property_add_enum(prop, i, i, broadcast_rgb_names[i]); + + dev_priv->broadcast_rgb_property = prop; + } + + drm_connector_attach_property(connector, prop, 0); +} + /* Cedarview */ static const struct psb_offset cdv_regmap[2] = { { diff --git a/drivers/gpu/drm/gma500/cdv_intel_display.c b/drivers/gpu/drm/gma500/cdv_intel_display.c index f16169c..2e0231e 100644 --- a/drivers/gpu/drm/gma500/cdv_intel_display.c +++ b/drivers/gpu/drm/gma500/cdv_intel_display.c @@ -64,11 +64,16 @@ struct cdv_intel_limit_t { static bool cdv_intel_find_best_PLL(const struct cdv_intel_limit_t *limit, struct drm_crtc *crtc, int target, int refclk, struct cdv_intel_clock_t *best_clock); +static bool cdv_intel_find_dp_pll(const struct cdv_intel_limit_t *limit, struct drm_crtc *crtc, int target, + int refclk, + struct cdv_intel_clock_t *best_clock); #define CDV_LIMIT_SINGLE_LVDS_96 0 #define CDV_LIMIT_SINGLE_LVDS_100 1 #define CDV_LIMIT_DAC_HDMI_27 2 #define CDV_LIMIT_DAC_HDMI_96 3 +#define CDV_LIMIT_DP_27 4 +#define CDV_LIMIT_DP_100 5 static const struct cdv_intel_limit_t cdv_intel_limits[] = { { /* CDV_SIGNLE_LVDS_96MHz */ @@ -123,6 +128,30 @@ static const struct cdv_intel_limit_t cdv_intel_limits[] = { .p2 = {.dot_limit = 225000, .p2_slow = 10, .p2_fast = 5}, .find_pll = cdv_intel_find_best_PLL, }, + { /* CDV_DP_27MHz */ + .dot = {.min = 160000, .max = 272000}, + .vco = {.min = 1809000, .max = 3564000}, + .n = {.min = 1, .max = 1}, + .m = {.min = 67, .max = 132}, + .m1 = {.min = 0, .max = 0}, + .m2 = {.min = 65, .max = 130}, + .p = {.min = 5, .max = 90}, + .p1 = {.min = 1, .max = 9}, + .p2 = {.dot_limit = 225000, .p2_slow = 10, .p2_fast = 10}, + .find_pll = cdv_intel_find_dp_pll, + }, + { /* CDV_DP_100MHz */ + .dot = {.min = 160000, .max = 272000}, + .vco = {.min = 1800000, .max = 3600000}, + .n = {.min = 2, .max = 6}, + .m = {.min = 60, .max = 164}, + .m1 = {.min = 0, .max = 0}, + .m2 = {.min = 58, .max = 162}, + .p = {.min = 5, .max = 100}, + .p1 = {.min = 1, .max = 10}, + .p2 = {.dot_limit = 225000, .p2_slow = 10, .p2_fast = 10}, + .find_pll = cdv_intel_find_dp_pll, + } }; #define _wait_for(COND, MS, W) ({ \ @@ -269,7 +298,7 @@ cdv_dpll_set_clock_cdv(struct drm_device *dev, struct drm_crtc *crtc, ref_value &= ~(REF_CLK_MASK); /* use DPLL_A for pipeB on CRT/HDMI */ - if (pipe == 1 && !is_lvds) { + if (pipe == 1 && !is_lvds && !(ddi_select & DP_MASK)) { DRM_DEBUG_KMS("use DPLLA for pipe B\n"); ref_value |= REF_CLK_DPLLA; } else { @@ -409,6 +438,11 @@ static const struct cdv_intel_limit_t *cdv_intel_limit(struct drm_crtc *crtc, limit = &cdv_intel_limits[CDV_LIMIT_SINGLE_LVDS_96]; else limit = &cdv_intel_limits[CDV_LIMIT_SINGLE_LVDS_100]; + } else if (psb_intel_pipe_has_type(crtc, INTEL_OUTPUT_DISPLAYPORT)) { + if (refclk == 27000) + limit = &cdv_intel_limits[CDV_LIMIT_DP_27]; + else + limit = &cdv_intel_limits[CDV_LIMIT_DP_100]; } else { if (refclk == 27000) limit = &cdv_intel_limits[CDV_LIMIT_DAC_HDMI_27]; @@ -510,6 +544,49 @@ static bool cdv_intel_find_best_PLL(const struct cdv_intel_limit_t *limit, return err != target; } +static bool cdv_intel_find_dp_pll(const struct cdv_intel_limit_t *limit, struct drm_crtc *crtc, int target, + int refclk, + struct cdv_intel_clock_t *best_clock) +{ + struct cdv_intel_clock_t clock; + if (refclk == 27000) { + if (target < 200000) { + clock.p1 = 2; + clock.p2 = 10; + clock.n = 1; + clock.m1 = 0; + clock.m2 = 118; + } else { + clock.p1 = 1; + clock.p2 = 10; + clock.n = 1; + clock.m1 = 0; + clock.m2 = 98; + } + } else if (refclk == 100000) { + if (target < 200000) { + clock.p1 = 2; + clock.p2 = 10; + clock.n = 5; + clock.m1 = 0; + clock.m2 = 160; + } else { + clock.p1 = 1; + clock.p2 = 10; + clock.n = 5; + clock.m1 = 0; + clock.m2 = 133; + } + } else + return false; + clock.m = clock.m2 + 2; + clock.p = clock.p1 * clock.p2; + clock.vco = (refclk * clock.m) / clock.n; + clock.dot = clock.vco / clock.p; + memcpy(best_clock, &clock, sizeof(struct cdv_intel_clock_t)); + return true; +} + static int cdv_intel_pipe_set_base(struct drm_crtc *crtc, int x, int y, struct drm_framebuffer *old_fb) { @@ -963,7 +1040,7 @@ static int cdv_intel_crtc_mode_set(struct drm_crtc *crtc, u32 dpll = 0, dspcntr, pipeconf; bool ok; bool is_crt = false, is_lvds = false, is_tv = false; - bool is_hdmi = false; + bool is_hdmi = false, is_dp = false; struct drm_mode_config *mode_config = &dev->mode_config; struct drm_connector *connector; const struct cdv_intel_limit_t *limit; @@ -991,6 +1068,9 @@ static int cdv_intel_crtc_mode_set(struct drm_crtc *crtc, case INTEL_OUTPUT_HDMI: is_hdmi = true; break; + case INTEL_OUTPUT_DISPLAYPORT: + is_dp = true; + break; default: DRM_ERROR("invalid output type.\n"); return 0; @@ -1003,6 +1083,12 @@ static int cdv_intel_crtc_mode_set(struct drm_crtc *crtc, else /* high-end sku, 27/100 mhz */ refclk = 27000; + if (is_dp) { + if (pipe == 0) + refclk = 27000; + else + refclk = 100000; + } if (is_lvds && dev_priv->lvds_use_ssc) { refclk = dev_priv->lvds_ssc_freq * 1000; @@ -1028,6 +1114,15 @@ static int cdv_intel_crtc_mode_set(struct drm_crtc *crtc, } /* dpll |= PLL_REF_INPUT_DREFCLK; */ + if (is_dp) { +/*FIXME cdv_intel_dp_set_m_n(crtc, mode, adjusted_mode); */ + } else { + REG_WRITE(PIPE_GMCH_DATA_M(pipe), 0); + REG_WRITE(PIPE_GMCH_DATA_N(pipe), 0); + REG_WRITE(PIPE_DP_LINK_M(pipe), 0); + REG_WRITE(PIPE_DP_LINK_N(pipe), 0); + } + dpll |= DPLL_SYNCLOCK_ENABLE; /* if (is_lvds) dpll |= DPLLB_MODE_LVDS; diff --git a/drivers/gpu/drm/gma500/framebuffer.c b/drivers/gpu/drm/gma500/framebuffer.c index 5732b57..2de6b1f 100644 --- a/drivers/gpu/drm/gma500/framebuffer.c +++ b/drivers/gpu/drm/gma500/framebuffer.c @@ -764,6 +764,10 @@ static void psb_setup_outputs(struct drm_device *dev) crtc_mask = dev_priv->ops->hdmi_mask; clone_mask = (1 << INTEL_OUTPUT_HDMI); break; + case INTEL_OUTPUT_DISPLAYPORT: + crtc_mask = (1 << 0) | (1 << 1); + clone_mask = (1 << INTEL_OUTPUT_DISPLAYPORT); + break; } encoder->possible_crtcs = crtc_mask; encoder->possible_clones = diff --git a/drivers/gpu/drm/gma500/psb_intel_drv.h b/drivers/gpu/drm/gma500/psb_intel_drv.h index 73214e2..c574c01 100644 --- a/drivers/gpu/drm/gma500/psb_intel_drv.h +++ b/drivers/gpu/drm/gma500/psb_intel_drv.h @@ -134,7 +134,7 @@ struct psb_intel_encoder { u32 ddi_select; /* Channel info */ #define DDI0_SELECT 0x01 #define DDI1_SELECT 0x02 -#define DP_MASK 0x8000; +#define DP_MASK 0x8000 #define DDI_MASK 0x03 void *dev_priv; /* For sdvo_priv, lvds_priv, etc... */ -- cgit v0.10.2 From 37e7b184da18a9cf42e19f3c3cb42cb30874ac22 Mon Sep 17 00:00:00 2001 From: Alan Cox Date: Wed, 8 Aug 2012 13:55:03 +0000 Subject: gma500/cdv: sync up and add the displayport code to the build This is mostly just aligning bits of behaviour Signed-off-by: Alan Cox Signed-off-by: Dave Airlie diff --git a/drivers/gpu/drm/gma500/Makefile b/drivers/gpu/drm/gma500/Makefile index abfa2a93..56a0f2a 100644 --- a/drivers/gpu/drm/gma500/Makefile +++ b/drivers/gpu/drm/gma500/Makefile @@ -30,7 +30,8 @@ gma500_gfx-$(CONFIG_DRM_GMA3600) += cdv_device.o \ cdv_intel_crt.o \ cdv_intel_display.o \ cdv_intel_hdmi.o \ - cdv_intel_lvds.o + cdv_intel_lvds.o \ + cdv_intel_dp.o gma500_gfx-$(CONFIG_DRM_GMA600) += oaktrail_device.o \ oaktrail_crtc.o \ diff --git a/drivers/gpu/drm/gma500/cdv_intel_display.c b/drivers/gpu/drm/gma500/cdv_intel_display.c index 2e0231e..3f11060 100644 --- a/drivers/gpu/drm/gma500/cdv_intel_display.c +++ b/drivers/gpu/drm/gma500/cdv_intel_display.c @@ -171,7 +171,7 @@ static const struct cdv_intel_limit_t cdv_intel_limits[] = { #define wait_for(COND, MS) _wait_for(COND, MS, 1) -static int cdv_sb_read(struct drm_device *dev, u32 reg, u32 *val) +int cdv_sb_read(struct drm_device *dev, u32 reg, u32 *val) { int ret; @@ -198,7 +198,7 @@ static int cdv_sb_read(struct drm_device *dev, u32 reg, u32 *val) return 0; } -static int cdv_sb_write(struct drm_device *dev, u32 reg, u32 val) +int cdv_sb_write(struct drm_device *dev, u32 reg, u32 val) { int ret; static bool dpio_debug = true; @@ -240,7 +240,7 @@ static int cdv_sb_write(struct drm_device *dev, u32 reg, u32 val) /* Reset the DPIO configuration register. The BIOS does this at every * mode set. */ -static void cdv_sb_reset(struct drm_device *dev) +void cdv_sb_reset(struct drm_device *dev) { REG_WRITE(DPIO_CFG, 0); diff --git a/drivers/gpu/drm/gma500/cdv_intel_dp.c b/drivers/gpu/drm/gma500/cdv_intel_dp.c index c6d545d..b29b602 100644 --- a/drivers/gpu/drm/gma500/cdv_intel_dp.c +++ b/drivers/gpu/drm/gma500/cdv_intel_dp.c @@ -33,7 +33,6 @@ #include "drm_crtc_helper.h" #include "psb_drv.h" #include "psb_intel_drv.h" -#include "psb_drm.h" #include "psb_intel_reg.h" #include "drm_dp_helper.h" @@ -45,7 +44,7 @@ #define CDV_FAST_LINK_TRAIN 1 -struct psb_intel_dp { +struct cdv_intel_dp { uint32_t output_reg; uint32_t DP; uint8_t link_configuration[DP_LINK_CONFIGURATION_SIZE]; @@ -55,7 +54,7 @@ struct psb_intel_dp { uint8_t link_bw; uint8_t lane_count; uint8_t dpcd[4]; - struct psb_intel_output *output; + struct psb_intel_encoder *encoder; struct i2c_adapter adapter; struct i2c_algo_dp_aux_data algo; uint8_t train_set[4]; @@ -94,20 +93,20 @@ static uint32_t dp_vswing_premph_table[] = { * If a CPU or PCH DP output is attached to an eDP panel, this function * will return true, and false otherwise. */ -static bool is_edp(struct psb_intel_output *output) +static bool is_edp(struct psb_intel_encoder *encoder) { - return output->type == INTEL_OUTPUT_EDP; + return encoder->type == INTEL_OUTPUT_EDP; } -static void psb_intel_dp_start_link_train(struct psb_intel_output *output); -static void psb_intel_dp_complete_link_train(struct psb_intel_output *output); -static void psb_intel_dp_link_down(struct psb_intel_output *output); +static void cdv_intel_dp_start_link_train(struct psb_intel_encoder *encoder); +static void cdv_intel_dp_complete_link_train(struct psb_intel_encoder *encoder); +static void cdv_intel_dp_link_down(struct psb_intel_encoder *encoder); static int -psb_intel_dp_max_lane_count(struct psb_intel_output *output) +cdv_intel_dp_max_lane_count(struct psb_intel_encoder *encoder) { - struct psb_intel_dp *intel_dp = output->dev_priv; + struct cdv_intel_dp *intel_dp = encoder->dev_priv; int max_lane_count = 4; if (intel_dp->dpcd[DP_DPCD_REV] >= 0x11) { @@ -123,9 +122,9 @@ psb_intel_dp_max_lane_count(struct psb_intel_output *output) } static int -psb_intel_dp_max_link_bw(struct psb_intel_output *output) +cdv_intel_dp_max_link_bw(struct psb_intel_encoder *encoder) { - struct psb_intel_dp *intel_dp = output->dev_priv; + struct cdv_intel_dp *intel_dp = encoder->dev_priv; int max_link_bw = intel_dp->dpcd[DP_MAX_LINK_RATE]; switch (max_link_bw) { @@ -140,7 +139,7 @@ psb_intel_dp_max_link_bw(struct psb_intel_output *output) } static int -psb_intel_dp_link_clock(uint8_t link_bw) +cdv_intel_dp_link_clock(uint8_t link_bw) { if (link_bw == DP_LINK_BW_2_7) return 270000; @@ -149,28 +148,28 @@ psb_intel_dp_link_clock(uint8_t link_bw) } static int -psb_intel_dp_link_required(int pixel_clock, int bpp) +cdv_intel_dp_link_required(int pixel_clock, int bpp) { return (pixel_clock * bpp + 7) / 8; } static int -psb_intel_dp_max_data_rate(int max_link_clock, int max_lanes) +cdv_intel_dp_max_data_rate(int max_link_clock, int max_lanes) { return (max_link_clock * max_lanes * 19) / 20; } static int -psb_intel_dp_mode_valid(struct drm_connector *connector, +cdv_intel_dp_mode_valid(struct drm_connector *connector, struct drm_display_mode *mode) { - struct psb_intel_output *output = to_psb_intel_output(connector); + struct psb_intel_encoder *encoder = psb_intel_attached_encoder(connector); struct drm_device *dev = connector->dev; struct drm_psb_private *dev_priv = dev->dev_private; - int max_link_clock = psb_intel_dp_link_clock(psb_intel_dp_max_link_bw(output)); - int max_lanes = psb_intel_dp_max_lane_count(output); + int max_link_clock = cdv_intel_dp_link_clock(cdv_intel_dp_max_link_bw(encoder)); + int max_lanes = cdv_intel_dp_max_lane_count(encoder); - if (is_edp(output) && dev_priv->panel_fixed_mode) { + if (is_edp(encoder) && dev_priv->panel_fixed_mode) { if (mode->hdisplay > dev_priv->panel_fixed_mode->hdisplay) return MODE_PANEL; @@ -180,9 +179,9 @@ psb_intel_dp_mode_valid(struct drm_connector *connector, /* only refuse the mode on non eDP since we have seen some weird eDP panels which are outside spec tolerances but somehow work by magic */ - if (!is_edp(output) && - (psb_intel_dp_link_required(mode->clock, 24) - > psb_intel_dp_max_data_rate(max_link_clock, max_lanes))) + if (!is_edp(encoder) && + (cdv_intel_dp_link_required(mode->clock, 24) + > cdv_intel_dp_max_data_rate(max_link_clock, max_lanes))) return MODE_CLOCK_HIGH; if (mode->clock < 10000) @@ -215,13 +214,13 @@ unpack_aux(uint32_t src, uint8_t *dst, int dst_bytes) } static int -psb_intel_dp_aux_ch(struct psb_intel_output *output, +cdv_intel_dp_aux_ch(struct psb_intel_encoder *encoder, uint8_t *send, int send_bytes, uint8_t *recv, int recv_size) { - struct psb_intel_dp *intel_dp = output->dev_priv; + struct cdv_intel_dp *intel_dp = encoder->dev_priv; uint32_t output_reg = intel_dp->output_reg; - struct drm_device *dev = output->base.dev; + struct drm_device *dev = encoder->base.dev; uint32_t ch_ctl = output_reg + 0x10; uint32_t ch_data = ch_ctl + 4; int i; @@ -315,7 +314,7 @@ psb_intel_dp_aux_ch(struct psb_intel_output *output, /* Write data to the aux channel in native mode */ static int -psb_intel_dp_aux_native_write(struct psb_intel_output *output, +cdv_intel_dp_aux_native_write(struct psb_intel_encoder *encoder, uint16_t address, uint8_t *send, int send_bytes) { int ret; @@ -332,7 +331,7 @@ psb_intel_dp_aux_native_write(struct psb_intel_output *output, memcpy(&msg[4], send, send_bytes); msg_bytes = send_bytes + 4; for (;;) { - ret = psb_intel_dp_aux_ch(output, msg, msg_bytes, &ack, 1); + ret = cdv_intel_dp_aux_ch(encoder, msg, msg_bytes, &ack, 1); if (ret < 0) return ret; if ((ack & AUX_NATIVE_REPLY_MASK) == AUX_NATIVE_REPLY_ACK) @@ -347,15 +346,15 @@ psb_intel_dp_aux_native_write(struct psb_intel_output *output, /* Write a single byte to the aux channel in native mode */ static int -psb_intel_dp_aux_native_write_1(struct psb_intel_output *output, +cdv_intel_dp_aux_native_write_1(struct psb_intel_encoder *encoder, uint16_t address, uint8_t byte) { - return psb_intel_dp_aux_native_write(output, address, &byte, 1); + return cdv_intel_dp_aux_native_write(encoder, address, &byte, 1); } /* read bytes from a native aux channel */ static int -psb_intel_dp_aux_native_read(struct psb_intel_output *output, +cdv_intel_dp_aux_native_read(struct psb_intel_encoder *encoder, uint16_t address, uint8_t *recv, int recv_bytes) { uint8_t msg[4]; @@ -374,7 +373,7 @@ psb_intel_dp_aux_native_read(struct psb_intel_output *output, reply_bytes = recv_bytes + 1; for (;;) { - ret = psb_intel_dp_aux_ch(output, msg, msg_bytes, + ret = cdv_intel_dp_aux_ch(encoder, msg, msg_bytes, reply, reply_bytes); if (ret == 0) return -EPROTO; @@ -393,14 +392,14 @@ psb_intel_dp_aux_native_read(struct psb_intel_output *output, } static int -psb_intel_dp_i2c_aux_ch(struct i2c_adapter *adapter, int mode, +cdv_intel_dp_i2c_aux_ch(struct i2c_adapter *adapter, int mode, uint8_t write_byte, uint8_t *read_byte) { struct i2c_algo_dp_aux_data *algo_data = adapter->algo_data; - struct psb_intel_dp *intel_dp = container_of(adapter, - struct psb_intel_dp, + struct cdv_intel_dp *intel_dp = container_of(adapter, + struct cdv_intel_dp, adapter); - struct psb_intel_output *output = intel_dp->output; + struct psb_intel_encoder *encoder = intel_dp->encoder; uint16_t address = algo_data->address; uint8_t msg[5]; uint8_t reply[2]; @@ -440,7 +439,7 @@ psb_intel_dp_i2c_aux_ch(struct i2c_adapter *adapter, int mode, } for (retry = 0; retry < 5; retry++) { - ret = psb_intel_dp_aux_ch(output, + ret = cdv_intel_dp_aux_ch(encoder, msg, msg_bytes, reply, reply_bytes); if (ret < 0) { @@ -490,13 +489,13 @@ psb_intel_dp_i2c_aux_ch(struct i2c_adapter *adapter, int mode, } static int -psb_intel_dp_i2c_init(struct psb_intel_output *output, const char *name) +cdv_intel_dp_i2c_init(struct psb_intel_connector *connector, struct psb_intel_encoder *encoder, const char *name) { - struct psb_intel_dp *intel_dp = output->dev_priv; + struct cdv_intel_dp *intel_dp = encoder->dev_priv; DRM_DEBUG_KMS("i2c_init %s\n", name); intel_dp->algo.running = false; intel_dp->algo.address = 0; - intel_dp->algo.aux_ch = psb_intel_dp_i2c_aux_ch; + intel_dp->algo.aux_ch = cdv_intel_dp_i2c_aux_ch; memset(&intel_dp->adapter, '\0', sizeof (intel_dp->adapter)); intel_dp->adapter.owner = THIS_MODULE; @@ -504,32 +503,32 @@ psb_intel_dp_i2c_init(struct psb_intel_output *output, const char *name) strncpy (intel_dp->adapter.name, name, sizeof(intel_dp->adapter.name) - 1); intel_dp->adapter.name[sizeof(intel_dp->adapter.name) - 1] = '\0'; intel_dp->adapter.algo_data = &intel_dp->algo; - intel_dp->adapter.dev.parent = &output->base.kdev; + intel_dp->adapter.dev.parent = &connector->base.kdev; return i2c_dp_aux_add_bus(&intel_dp->adapter); } static bool -psb_intel_dp_mode_fixup(struct drm_encoder *encoder, struct drm_display_mode *mode, +cdv_intel_dp_mode_fixup(struct drm_encoder *encoder, const struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode) { - struct psb_intel_output *output = enc_to_psb_intel_output(encoder); - struct psb_intel_dp *intel_dp = output->dev_priv; + struct psb_intel_encoder *intel_encoder = to_psb_intel_encoder(encoder); + struct cdv_intel_dp *intel_dp = intel_encoder->dev_priv; int lane_count, clock; - int max_lane_count = psb_intel_dp_max_lane_count(output); - int max_clock = psb_intel_dp_max_link_bw(output) == DP_LINK_BW_2_7 ? 1 : 0; + int max_lane_count = cdv_intel_dp_max_lane_count(intel_encoder); + int max_clock = cdv_intel_dp_max_link_bw(intel_encoder) == DP_LINK_BW_2_7 ? 1 : 0; static int bws[2] = { DP_LINK_BW_1_62, DP_LINK_BW_2_7 }; for (lane_count = 1; lane_count <= max_lane_count; lane_count <<= 1) { for (clock = max_clock; clock >= 0; clock--) { - int link_avail = psb_intel_dp_max_data_rate(psb_intel_dp_link_clock(bws[clock]), lane_count); + int link_avail = cdv_intel_dp_max_data_rate(cdv_intel_dp_link_clock(bws[clock]), lane_count); - if (psb_intel_dp_link_required(mode->clock, 24) + if (cdv_intel_dp_link_required(mode->clock, 24) <= link_avail) { intel_dp->link_bw = bws[clock]; intel_dp->lane_count = lane_count; - adjusted_mode->clock = psb_intel_dp_link_clock(intel_dp->link_bw); + adjusted_mode->clock = cdv_intel_dp_link_clock(intel_dp->link_bw); DRM_DEBUG_KMS("Display port link bw %02x lane " "count %d clock %d\n", intel_dp->link_bw, intel_dp->lane_count, @@ -542,7 +541,7 @@ psb_intel_dp_mode_fixup(struct drm_encoder *encoder, struct drm_display_mode *mo return false; } -struct psb_intel_dp_m_n { +struct cdv_intel_dp_m_n { uint32_t tu; uint32_t gmch_m; uint32_t gmch_n; @@ -567,11 +566,11 @@ psb_intel_reduce_ratio(uint32_t *num, uint32_t *den) } static void -psb_intel_dp_compute_m_n(int bpp, +cdv_intel_dp_compute_m_n(int bpp, int nlanes, int pixel_clock, int link_clock, - struct psb_intel_dp_m_n *m_n) + struct cdv_intel_dp_m_n *m_n) { m_n->tu = 64; m_n->gmch_m = (pixel_clock * bpp + 7) >> 3; @@ -583,7 +582,7 @@ psb_intel_dp_compute_m_n(int bpp, } void -psb_intel_dp_set_m_n(struct drm_crtc *crtc, struct drm_display_mode *mode, +cdv_intel_dp_set_m_n(struct drm_crtc *crtc, struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode) { struct drm_device *dev = crtc->dev; @@ -591,25 +590,25 @@ psb_intel_dp_set_m_n(struct drm_crtc *crtc, struct drm_display_mode *mode, struct drm_encoder *encoder; struct psb_intel_crtc *intel_crtc = to_psb_intel_crtc(crtc); int lane_count = 4, bpp = 24; - struct psb_intel_dp_m_n m_n; + struct cdv_intel_dp_m_n m_n; int pipe = intel_crtc->pipe; /* * Find the lane count in the intel_encoder private */ list_for_each_entry(encoder, &mode_config->encoder_list, head) { - struct psb_intel_output *intel_output; - struct psb_intel_dp *intel_dp; + struct psb_intel_encoder *intel_encoder; + struct cdv_intel_dp *intel_dp; if (encoder->crtc != crtc) continue; - intel_output = enc_to_psb_intel_output(encoder); - intel_dp = intel_output->dev_priv; - if (intel_output->type == INTEL_OUTPUT_DISPLAYPORT) { + intel_encoder = to_psb_intel_encoder(encoder); + intel_dp = intel_encoder->dev_priv; + if (intel_encoder->type == INTEL_OUTPUT_DISPLAYPORT) { lane_count = intel_dp->lane_count; break; - } else if (is_edp(intel_output)) { + } else if (is_edp(intel_encoder)) { lane_count = intel_dp->lane_count; break; } @@ -620,7 +619,7 @@ psb_intel_dp_set_m_n(struct drm_crtc *crtc, struct drm_display_mode *mode, * the number of bytes_per_pixel post-LUT, which we always * set up for 8-bits of R/G/B, or 3 bytes total. */ - psb_intel_dp_compute_m_n(bpp, lane_count, + cdv_intel_dp_compute_m_n(bpp, lane_count, mode->clock, adjusted_mode->clock, &m_n); { @@ -634,13 +633,13 @@ psb_intel_dp_set_m_n(struct drm_crtc *crtc, struct drm_display_mode *mode, } static void -psb_intel_dp_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode, +cdv_intel_dp_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode) { - struct psb_intel_output *intel_output = enc_to_psb_intel_output(encoder); + struct psb_intel_encoder *intel_encoder = to_psb_intel_encoder(encoder); struct drm_crtc *crtc = encoder->crtc; struct psb_intel_crtc *intel_crtc = to_psb_intel_crtc(crtc); - struct psb_intel_dp *intel_dp = intel_output->dev_priv; + struct cdv_intel_dp *intel_dp = intel_encoder->dev_priv; intel_dp->DP = DP_VOLTAGE_0_4 | DP_PRE_EMPHASIS_0; @@ -689,9 +688,9 @@ psb_intel_dp_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode /* If the sink supports it, try to set the power state appropriately */ -static void psb_intel_dp_sink_dpms(struct psb_intel_output *output, int mode) +static void cdv_intel_dp_sink_dpms(struct psb_intel_encoder *encoder, int mode) { - struct psb_intel_dp *intel_dp = output->dev_priv; + struct cdv_intel_dp *intel_dp = encoder->dev_priv; int ret, i; /* Should have a valid DPCD by this point */ @@ -699,7 +698,7 @@ static void psb_intel_dp_sink_dpms(struct psb_intel_output *output, int mode) return; if (mode != DRM_MODE_DPMS_ON) { - ret = psb_intel_dp_aux_native_write_1(output, DP_SET_POWER, + ret = cdv_intel_dp_aux_native_write_1(encoder, DP_SET_POWER, DP_SET_POWER_D3); if (ret != 1) DRM_DEBUG_DRIVER("failed to write sink power state\n"); @@ -709,7 +708,7 @@ static void psb_intel_dp_sink_dpms(struct psb_intel_output *output, int mode) * time to wake up. */ for (i = 0; i < 3; i++) { - ret = psb_intel_dp_aux_native_write_1(output, + ret = cdv_intel_dp_aux_native_write_1(encoder, DP_SET_POWER, DP_SET_POWER_D0); if (ret == 1) @@ -719,42 +718,39 @@ static void psb_intel_dp_sink_dpms(struct psb_intel_output *output, int mode) } } -static void psb_intel_dp_prepare(struct drm_encoder *encoder) +static void cdv_intel_dp_prepare(struct drm_encoder *encoder) { - struct psb_intel_output *output = enc_to_psb_intel_output(encoder); + struct psb_intel_encoder *intel_encoder = to_psb_intel_encoder(encoder); /* Wake up the sink first */ - psb_intel_dp_sink_dpms(output, DRM_MODE_DPMS_ON); - - psb_intel_dp_link_down(output); + cdv_intel_dp_sink_dpms(intel_encoder, DRM_MODE_DPMS_ON); + cdv_intel_dp_link_down(intel_encoder); } -static void psb_intel_dp_commit(struct drm_encoder *encoder) +static void cdv_intel_dp_commit(struct drm_encoder *encoder) { - struct psb_intel_output *output = enc_to_psb_intel_output(encoder); - - psb_intel_dp_start_link_train(output); - - psb_intel_dp_complete_link_train(output); + struct psb_intel_encoder *intel_encoder = to_psb_intel_encoder(encoder); + cdv_intel_dp_start_link_train(intel_encoder); + cdv_intel_dp_complete_link_train(intel_encoder); } static void -psb_intel_dp_dpms(struct drm_encoder *encoder, int mode) +cdv_intel_dp_dpms(struct drm_encoder *encoder, int mode) { - struct psb_intel_output *intel_output = enc_to_psb_intel_output(encoder); - struct psb_intel_dp *intel_dp = intel_output->dev_priv; + struct psb_intel_encoder *intel_encoder = to_psb_intel_encoder(encoder); + struct cdv_intel_dp *intel_dp = intel_encoder->dev_priv; struct drm_device *dev = encoder->dev; uint32_t dp_reg = REG_READ(intel_dp->output_reg); if (mode != DRM_MODE_DPMS_ON) { - psb_intel_dp_sink_dpms(intel_output, mode); - psb_intel_dp_link_down(intel_output); + cdv_intel_dp_sink_dpms(intel_encoder, mode); + cdv_intel_dp_link_down(intel_encoder); } else { - psb_intel_dp_sink_dpms(intel_output, mode); + cdv_intel_dp_sink_dpms(intel_encoder, mode); if (!(dp_reg & DP_PORT_EN)) { - psb_intel_dp_start_link_train(intel_output); - psb_intel_dp_complete_link_train(intel_output); + cdv_intel_dp_start_link_train(intel_encoder); + cdv_intel_dp_complete_link_train(intel_encoder); } } } @@ -764,7 +760,7 @@ psb_intel_dp_dpms(struct drm_encoder *encoder, int mode) * cases where the sink may still be asleep. */ static bool -psb_intel_dp_aux_native_read_retry(struct psb_intel_output *output, uint16_t address, +cdv_intel_dp_aux_native_read_retry(struct psb_intel_encoder *encoder, uint16_t address, uint8_t *recv, int recv_bytes) { int ret, i; @@ -774,7 +770,7 @@ psb_intel_dp_aux_native_read_retry(struct psb_intel_output *output, uint16_t add * but we're also supposed to retry 3 times per the spec. */ for (i = 0; i < 3; i++) { - ret = psb_intel_dp_aux_native_read(output, address, recv, + ret = cdv_intel_dp_aux_native_read(encoder, address, recv, recv_bytes); if (ret == recv_bytes) return true; @@ -789,44 +785,44 @@ psb_intel_dp_aux_native_read_retry(struct psb_intel_output *output, uint16_t add * link status information */ static bool -psb_intel_dp_get_link_status(struct psb_intel_output *output) +cdv_intel_dp_get_link_status(struct psb_intel_encoder *encoder) { - struct psb_intel_dp *intel_dp = output->dev_priv; - return psb_intel_dp_aux_native_read_retry(output, + struct cdv_intel_dp *intel_dp = encoder->dev_priv; + return cdv_intel_dp_aux_native_read_retry(encoder, DP_LANE0_1_STATUS, intel_dp->link_status, DP_LINK_STATUS_SIZE); } static uint8_t -psb_intel_dp_link_status(uint8_t link_status[DP_LINK_STATUS_SIZE], +cdv_intel_dp_link_status(uint8_t link_status[DP_LINK_STATUS_SIZE], int r) { return link_status[r - DP_LANE0_1_STATUS]; } static uint8_t -psb_intel_get_adjust_request_voltage(uint8_t link_status[DP_LINK_STATUS_SIZE], +cdv_intel_get_adjust_request_voltage(uint8_t link_status[DP_LINK_STATUS_SIZE], int lane) { int i = DP_ADJUST_REQUEST_LANE0_1 + (lane >> 1); int s = ((lane & 1) ? DP_ADJUST_VOLTAGE_SWING_LANE1_SHIFT : DP_ADJUST_VOLTAGE_SWING_LANE0_SHIFT); - uint8_t l = psb_intel_dp_link_status(link_status, i); + uint8_t l = cdv_intel_dp_link_status(link_status, i); return ((l >> s) & 3) << DP_TRAIN_VOLTAGE_SWING_SHIFT; } static uint8_t -psb_intel_get_adjust_request_pre_emphasis(uint8_t link_status[DP_LINK_STATUS_SIZE], +cdv_intel_get_adjust_request_pre_emphasis(uint8_t link_status[DP_LINK_STATUS_SIZE], int lane) { int i = DP_ADJUST_REQUEST_LANE0_1 + (lane >> 1); int s = ((lane & 1) ? DP_ADJUST_PRE_EMPHASIS_LANE1_SHIFT : DP_ADJUST_PRE_EMPHASIS_LANE0_SHIFT); - uint8_t l = psb_intel_dp_link_status(link_status, i); + uint8_t l = cdv_intel_dp_link_status(link_status, i); return ((l >> s) & 3) << DP_TRAIN_PRE_EMPHASIS_SHIFT; } @@ -847,7 +843,7 @@ static char *link_train_names[] = { #define CDV_DP_VOLTAGE_MAX DP_TRAIN_VOLTAGE_SWING_1200 /* static uint8_t -psb_intel_dp_pre_emphasis_max(uint8_t voltage_swing) +cdv_intel_dp_pre_emphasis_max(uint8_t voltage_swing) { switch (voltage_swing & DP_TRAIN_VOLTAGE_SWING_MASK) { case DP_TRAIN_VOLTAGE_SWING_400: @@ -863,16 +859,16 @@ psb_intel_dp_pre_emphasis_max(uint8_t voltage_swing) } */ static void -psb_intel_get_adjust_train(struct psb_intel_output *output) +cdv_intel_get_adjust_train(struct psb_intel_encoder *encoder) { - struct psb_intel_dp *intel_dp = output->dev_priv; + struct cdv_intel_dp *intel_dp = encoder->dev_priv; uint8_t v = 0; uint8_t p = 0; int lane; for (lane = 0; lane < intel_dp->lane_count; lane++) { - uint8_t this_v = psb_intel_get_adjust_request_voltage(intel_dp->link_status, lane); - uint8_t this_p = psb_intel_get_adjust_request_pre_emphasis(intel_dp->link_status, lane); + uint8_t this_v = cdv_intel_get_adjust_request_voltage(intel_dp->link_status, lane); + uint8_t this_p = cdv_intel_get_adjust_request_pre_emphasis(intel_dp->link_status, lane); if (this_v > v) v = this_v; @@ -892,25 +888,25 @@ psb_intel_get_adjust_train(struct psb_intel_output *output) static uint8_t -psb_intel_get_lane_status(uint8_t link_status[DP_LINK_STATUS_SIZE], +cdv_intel_get_lane_status(uint8_t link_status[DP_LINK_STATUS_SIZE], int lane) { int i = DP_LANE0_1_STATUS + (lane >> 1); int s = (lane & 1) * 4; - uint8_t l = psb_intel_dp_link_status(link_status, i); + uint8_t l = cdv_intel_dp_link_status(link_status, i); return (l >> s) & 0xf; } /* Check for clock recovery is done on all channels */ static bool -psb_intel_clock_recovery_ok(uint8_t link_status[DP_LINK_STATUS_SIZE], int lane_count) +cdv_intel_clock_recovery_ok(uint8_t link_status[DP_LINK_STATUS_SIZE], int lane_count) { int lane; uint8_t lane_status; for (lane = 0; lane < lane_count; lane++) { - lane_status = psb_intel_get_lane_status(link_status, lane); + lane_status = cdv_intel_get_lane_status(link_status, lane); if ((lane_status & DP_LANE_CR_DONE) == 0) return false; } @@ -922,19 +918,19 @@ psb_intel_clock_recovery_ok(uint8_t link_status[DP_LINK_STATUS_SIZE], int lane_c DP_LANE_CHANNEL_EQ_DONE|\ DP_LANE_SYMBOL_LOCKED) static bool -psb_intel_channel_eq_ok(struct psb_intel_output *output) +cdv_intel_channel_eq_ok(struct psb_intel_encoder *encoder) { - struct psb_intel_dp *intel_dp = output->dev_priv; + struct cdv_intel_dp *intel_dp = encoder->dev_priv; uint8_t lane_align; uint8_t lane_status; int lane; - lane_align = psb_intel_dp_link_status(intel_dp->link_status, + lane_align = cdv_intel_dp_link_status(intel_dp->link_status, DP_LANE_ALIGN_STATUS_UPDATED); if ((lane_align & DP_INTERLANE_ALIGN_DONE) == 0) return false; for (lane = 0; lane < intel_dp->lane_count; lane++) { - lane_status = psb_intel_get_lane_status(intel_dp->link_status, lane); + lane_status = cdv_intel_get_lane_status(intel_dp->link_status, lane); if ((lane_status & CHANNEL_EQ_BITS) != CHANNEL_EQ_BITS) return false; } @@ -942,19 +938,19 @@ psb_intel_channel_eq_ok(struct psb_intel_output *output) } static bool -psb_intel_dp_set_link_train(struct psb_intel_output *output, +cdv_intel_dp_set_link_train(struct psb_intel_encoder *encoder, uint32_t dp_reg_value, uint8_t dp_train_pat) { - struct drm_device *dev = output->base.dev; + struct drm_device *dev = encoder->base.dev; int ret; - struct psb_intel_dp *intel_dp = output->dev_priv; + struct cdv_intel_dp *intel_dp = encoder->dev_priv; REG_WRITE(intel_dp->output_reg, dp_reg_value); REG_READ(intel_dp->output_reg); - ret = psb_intel_dp_aux_native_write_1(output, + ret = cdv_intel_dp_aux_native_write_1(encoder, DP_TRAINING_PATTERN_SET, dp_train_pat); @@ -969,14 +965,14 @@ psb_intel_dp_set_link_train(struct psb_intel_output *output, static bool -psb_intel_dplink_set_level(struct psb_intel_output *output, +cdv_intel_dplink_set_level(struct psb_intel_encoder *encoder, uint8_t dp_train_pat) { int ret; - struct psb_intel_dp *intel_dp = output->dev_priv; + struct cdv_intel_dp *intel_dp = encoder->dev_priv; - ret = psb_intel_dp_aux_native_write(output, + ret = cdv_intel_dp_aux_native_write(encoder, DP_TRAINING_LANE0_SET, intel_dp->train_set, intel_dp->lane_count); @@ -990,10 +986,10 @@ psb_intel_dplink_set_level(struct psb_intel_output *output, } static void -psb_intel_dp_set_vswing_premph(struct psb_intel_output *output, uint8_t signal_level) +cdv_intel_dp_set_vswing_premph(struct psb_intel_encoder *encoder, uint8_t signal_level) { - struct drm_device *dev = output->base.dev; - struct psb_intel_dp *intel_dp = output->dev_priv; + struct drm_device *dev = encoder->base.dev; + struct cdv_intel_dp *intel_dp = encoder->dev_priv; struct ddi_regoff *ddi_reg; int vswing, premph, index; @@ -1013,53 +1009,53 @@ psb_intel_dp_set_vswing_premph(struct psb_intel_output *output, uint8_t signal_l #endif DRM_DEBUG_KMS("Test2\n"); //return ; - psb_sb_reset(dev); + cdv_sb_reset(dev); /* ;Swing voltage programming ;gfx_dpio_set_reg(0xc058, 0x0505313A) */ - psb_sb_write(dev, ddi_reg->VSwing5, 0x0505313A); + cdv_sb_write(dev, ddi_reg->VSwing5, 0x0505313A); /* ;gfx_dpio_set_reg(0x8154, 0x43406055) */ - psb_sb_write(dev, ddi_reg->VSwing1, 0x43406055); + cdv_sb_write(dev, ddi_reg->VSwing1, 0x43406055); /* ;gfx_dpio_set_reg(0x8148, 0x55338954) * The VSwing_PreEmph table is also considered based on the vswing/premp */ index = (vswing + premph) * 2; if (premph == 1 && vswing == 1) { - psb_sb_write(dev, ddi_reg->VSwing2, 0x055738954); + cdv_sb_write(dev, ddi_reg->VSwing2, 0x055738954); } else - psb_sb_write(dev, ddi_reg->VSwing2, dp_vswing_premph_table[index]); + cdv_sb_write(dev, ddi_reg->VSwing2, dp_vswing_premph_table[index]); /* ;gfx_dpio_set_reg(0x814c, 0x40802040) */ if ((vswing + premph) == DP_TRAIN_VOLTAGE_SWING_1200) - psb_sb_write(dev, ddi_reg->VSwing3, 0x70802040); + cdv_sb_write(dev, ddi_reg->VSwing3, 0x70802040); else - psb_sb_write(dev, ddi_reg->VSwing3, 0x40802040); + cdv_sb_write(dev, ddi_reg->VSwing3, 0x40802040); /* ;gfx_dpio_set_reg(0x8150, 0x2b405555) */ - //psb_sb_write(dev, ddi_reg->VSwing4, 0x2b405555); + /* cdv_sb_write(dev, ddi_reg->VSwing4, 0x2b405555); */ /* ;gfx_dpio_set_reg(0x8154, 0xc3406055) */ - psb_sb_write(dev, ddi_reg->VSwing1, 0xc3406055); + cdv_sb_write(dev, ddi_reg->VSwing1, 0xc3406055); /* ;Pre emphasis programming * ;gfx_dpio_set_reg(0xc02c, 0x1f030040) */ - psb_sb_write(dev, ddi_reg->PreEmph1, 0x1f030040); + cdv_sb_write(dev, ddi_reg->PreEmph1, 0x1f030040); /* ;gfx_dpio_set_reg(0x8124, 0x00004000) */ index = 2 * premph + 1; - psb_sb_write(dev, ddi_reg->PreEmph2, dp_vswing_premph_table[index]); + cdv_sb_write(dev, ddi_reg->PreEmph2, dp_vswing_premph_table[index]); return; } /* Enable corresponding port and start training pattern 1 */ static void -psb_intel_dp_start_link_train(struct psb_intel_output *output) +cdv_intel_dp_start_link_train(struct psb_intel_encoder *encoder) { - struct drm_device *dev = output->base.dev; - struct psb_intel_dp *intel_dp = output->dev_priv; + struct drm_device *dev = encoder->base.dev; + struct cdv_intel_dp *intel_dp = encoder->dev_priv; int i; uint8_t voltage; bool clock_recovery = false; @@ -1068,10 +1064,10 @@ psb_intel_dp_start_link_train(struct psb_intel_output *output) uint32_t DP = intel_dp->DP; DP |= DP_PORT_EN; - DP &= ~DP_LINK_TRAIN_MASK; + DP &= ~DP_LINK_TRAIN_MASK; - reg = DP; - reg |= DP_LINK_TRAIN_PAT_1; + reg = DP; + reg |= DP_LINK_TRAIN_PAT_1; /* Enable output, wait for it to become active */ REG_WRITE(intel_dp->output_reg, reg); REG_READ(intel_dp->output_reg); @@ -1079,7 +1075,7 @@ psb_intel_dp_start_link_train(struct psb_intel_output *output) DRM_DEBUG_KMS("Link config\n"); /* Write the link configuration data */ - psb_intel_dp_aux_native_write(output, DP_LINK_BW_SET, + cdv_intel_dp_aux_native_write(encoder, DP_LINK_BW_SET, intel_dp->link_configuration, 2); @@ -1095,19 +1091,19 @@ psb_intel_dp_start_link_train(struct psb_intel_output *output) for (;;) { /* Use intel_dp->train_set[0] to set the voltage and pre emphasis values */ - if (!psb_intel_dp_set_link_train(output, reg, DP_TRAINING_PATTERN_1)) { + if (!cdv_intel_dp_set_link_train(encoder, reg, DP_TRAINING_PATTERN_1)) { DRM_DEBUG_KMS("Failure in aux-transfer setting pattern 1\n"); } - psb_intel_dp_set_vswing_premph(output, intel_dp->train_set[0]); + cdv_intel_dp_set_vswing_premph(encoder, intel_dp->train_set[0]); /* Set training pattern 1 */ - psb_intel_dplink_set_level(output, DP_TRAINING_PATTERN_1); + cdv_intel_dplink_set_level(encoder, DP_TRAINING_PATTERN_1); udelay(200); - if (!psb_intel_dp_get_link_status(output)) + if (!cdv_intel_dp_get_link_status(encoder)) break; - if (psb_intel_clock_recovery_ok(intel_dp->link_status, intel_dp->lane_count)) { + if (cdv_intel_clock_recovery_ok(intel_dp->link_status, intel_dp->lane_count)) { DRM_DEBUG_KMS("PT1 train is done\n"); clock_recovery = true; break; @@ -1130,7 +1126,7 @@ psb_intel_dp_start_link_train(struct psb_intel_output *output) voltage = intel_dp->train_set[0] & DP_TRAIN_VOLTAGE_SWING_MASK; /* Compute new intel_dp->train_set as requested by target */ - psb_intel_get_adjust_train(output); + cdv_intel_get_adjust_train(encoder); } @@ -1142,10 +1138,10 @@ psb_intel_dp_start_link_train(struct psb_intel_output *output) } static void -psb_intel_dp_complete_link_train(struct psb_intel_output *output) +cdv_intel_dp_complete_link_train(struct psb_intel_encoder *encoder) { - struct drm_device *dev = output->base.dev; - struct psb_intel_dp *intel_dp = output->dev_priv; + struct drm_device *dev = encoder->base.dev; + struct cdv_intel_dp *intel_dp = encoder->dev_priv; bool channel_eq = false; int tries, cr_tries; u32 reg; @@ -1161,7 +1157,7 @@ psb_intel_dp_complete_link_train(struct psb_intel_output *output) for (;;) { /* channel eq pattern */ - if (!psb_intel_dp_set_link_train(output, reg, + if (!cdv_intel_dp_set_link_train(encoder, reg, DP_TRAINING_PATTERN_2)) { DRM_DEBUG_KMS("Failure in aux-transfer setting pattern 2\n"); } @@ -1169,26 +1165,26 @@ psb_intel_dp_complete_link_train(struct psb_intel_output *output) if (cr_tries > 5) { DRM_ERROR("failed to train DP, aborting\n"); - psb_intel_dp_link_down(output); + cdv_intel_dp_link_down(encoder); break; } - psb_intel_dp_set_vswing_premph(output, intel_dp->train_set[0]); + cdv_intel_dp_set_vswing_premph(encoder, intel_dp->train_set[0]); - psb_intel_dplink_set_level(output, DP_TRAINING_PATTERN_2); + cdv_intel_dplink_set_level(encoder, DP_TRAINING_PATTERN_2); udelay(1000); - if (!psb_intel_dp_get_link_status(output)) + if (!cdv_intel_dp_get_link_status(encoder)) break; /* Make sure clock is still ok */ - if (!psb_intel_clock_recovery_ok(intel_dp->link_status, intel_dp->lane_count)) { - psb_intel_dp_start_link_train(output); + if (!cdv_intel_clock_recovery_ok(intel_dp->link_status, intel_dp->lane_count)) { + cdv_intel_dp_start_link_train(encoder); cr_tries++; continue; } - if (psb_intel_channel_eq_ok(output)) { + if (cdv_intel_channel_eq_ok(encoder)) { DRM_DEBUG_KMS("PT2 train is done\n"); channel_eq = true; break; @@ -1196,15 +1192,15 @@ psb_intel_dp_complete_link_train(struct psb_intel_output *output) /* Try 5 times, then try clock recovery if that fails */ if (tries > 5) { - psb_intel_dp_link_down(output); - psb_intel_dp_start_link_train(output); + cdv_intel_dp_link_down(encoder); + cdv_intel_dp_start_link_train(encoder); tries = 0; cr_tries++; continue; } /* Compute new intel_dp->train_set as requested by target */ - psb_intel_get_adjust_train(output); + cdv_intel_get_adjust_train(encoder); ++tries; } @@ -1213,15 +1209,15 @@ psb_intel_dp_complete_link_train(struct psb_intel_output *output) REG_WRITE(intel_dp->output_reg, reg); REG_READ(intel_dp->output_reg); - psb_intel_dp_aux_native_write_1(output, + cdv_intel_dp_aux_native_write_1(encoder, DP_TRAINING_PATTERN_SET, DP_TRAINING_PATTERN_DISABLE); } static void -psb_intel_dp_link_down(struct psb_intel_output *output) +cdv_intel_dp_link_down(struct psb_intel_encoder *encoder) { - struct drm_device *dev = output->base.dev; - struct psb_intel_dp *intel_dp = output->dev_priv; + struct drm_device *dev = encoder->base.dev; + struct cdv_intel_dp *intel_dp = encoder->dev_priv; uint32_t DP = intel_dp->DP; if ((REG_READ(intel_dp->output_reg) & DP_PORT_EN) == 0) @@ -1243,13 +1239,13 @@ psb_intel_dp_link_down(struct psb_intel_output *output) } static enum drm_connector_status -cdv_dp_detect(struct psb_intel_output *output) +cdv_dp_detect(struct psb_intel_encoder *encoder) { - struct psb_intel_dp *intel_dp = output->dev_priv; + struct cdv_intel_dp *intel_dp = encoder->dev_priv; enum drm_connector_status status; status = connector_status_disconnected; - if (psb_intel_dp_aux_native_read(output, 0x000, intel_dp->dpcd, + if (cdv_intel_dp_aux_native_read(encoder, 0x000, intel_dp->dpcd, sizeof (intel_dp->dpcd)) == sizeof (intel_dp->dpcd)) { if (intel_dp->dpcd[DP_DPCD_REV] != 0) @@ -1269,16 +1265,16 @@ cdv_dp_detect(struct psb_intel_output *output) * \return false if DP port is disconnected. */ static enum drm_connector_status -psb_intel_dp_detect(struct drm_connector *connector, bool force) +cdv_intel_dp_detect(struct drm_connector *connector, bool force) { - struct psb_intel_output *output = to_psb_intel_output(connector); - struct psb_intel_dp *intel_dp = output->dev_priv; + struct psb_intel_encoder *encoder = psb_intel_attached_encoder(connector); + struct cdv_intel_dp *intel_dp = encoder->dev_priv; enum drm_connector_status status; struct edid *edid = NULL; intel_dp->has_audio = false; - status = cdv_dp_detect(output); + status = cdv_dp_detect(encoder); if (status != connector_status_connected) return status; @@ -1296,20 +1292,18 @@ psb_intel_dp_detect(struct drm_connector *connector, bool force) return connector_status_connected; } -static int psb_intel_dp_get_modes(struct drm_connector *connector) +static int cdv_intel_dp_get_modes(struct drm_connector *connector) { - struct psb_intel_output *intel_output = to_psb_intel_output(connector); - struct psb_intel_dp *intel_dp = intel_output->dev_priv; + struct psb_intel_encoder *intel_encoder = psb_intel_attached_encoder(connector); + struct cdv_intel_dp *intel_dp = intel_encoder->dev_priv; struct edid *edid = NULL; int ret = 0; - edid = drm_get_edid(&intel_output->base, - &intel_dp->adapter); + edid = drm_get_edid(connector, &intel_dp->adapter); if (edid) { - drm_mode_connector_update_edid_property(&intel_output-> - base, edid); - ret = drm_add_edid_modes(&intel_output->base, edid); + drm_mode_connector_update_edid_property(connector, edid); + ret = drm_add_edid_modes(connector, edid); kfree(edid); } @@ -1317,10 +1311,10 @@ static int psb_intel_dp_get_modes(struct drm_connector *connector) } static bool -psb_intel_dp_detect_audio(struct drm_connector *connector) +cdv_intel_dp_detect_audio(struct drm_connector *connector) { - struct psb_intel_output *output = to_psb_intel_output(connector); - struct psb_intel_dp *intel_dp = output->dev_priv; + struct psb_intel_encoder *encoder = psb_intel_attached_encoder(connector); + struct cdv_intel_dp *intel_dp = encoder->dev_priv; struct edid *edid; bool has_audio = false; @@ -1336,13 +1330,13 @@ psb_intel_dp_detect_audio(struct drm_connector *connector) } static int -psb_intel_dp_set_property(struct drm_connector *connector, +cdv_intel_dp_set_property(struct drm_connector *connector, struct drm_property *property, uint64_t val) { struct drm_psb_private *dev_priv = connector->dev->dev_private; - struct psb_intel_output *output = to_psb_intel_output(connector); - struct psb_intel_dp *intel_dp = output->dev_priv; + struct psb_intel_encoder *encoder = psb_intel_attached_encoder(connector); + struct cdv_intel_dp *intel_dp = encoder->dev_priv; int ret; ret = drm_connector_property_set_value(connector, property, val); @@ -1359,7 +1353,7 @@ psb_intel_dp_set_property(struct drm_connector *connector, intel_dp->force_audio = i; if (i == 0) - has_audio = psb_intel_dp_detect_audio(connector); + has_audio = cdv_intel_dp_detect_audio(connector); else has_audio = i > 0; @@ -1381,8 +1375,8 @@ psb_intel_dp_set_property(struct drm_connector *connector, return -EINVAL; done: - if (output->enc.crtc) { - struct drm_crtc *crtc = output->enc.crtc; + if (encoder->base.crtc) { + struct drm_crtc *crtc = encoder->base.crtc; drm_crtc_helper_set_mode(crtc, &crtc->mode, crtc->x, crtc->y, crtc->fb); @@ -1392,10 +1386,11 @@ done: } static void -psb_intel_dp_destroy (struct drm_connector *connector) +cdv_intel_dp_destroy (struct drm_connector *connector) { - struct psb_intel_output *output = to_psb_intel_output(connector); - struct psb_intel_dp *intel_dp = output->dev_priv; + struct psb_intel_encoder *psb_intel_encoder = + psb_intel_attached_encoder(connector); + struct cdv_intel_dp *intel_dp = psb_intel_encoder->dev_priv; i2c_del_adapter(&intel_dp->adapter); drm_sysfs_connector_remove(connector); @@ -1403,86 +1398,83 @@ psb_intel_dp_destroy (struct drm_connector *connector) kfree(connector); } -static void psb_intel_dp_encoder_destroy(struct drm_encoder *encoder) +static void cdv_intel_dp_encoder_destroy(struct drm_encoder *encoder) { drm_encoder_cleanup(encoder); } -static const struct drm_encoder_helper_funcs psb_intel_dp_helper_funcs = { - .dpms = psb_intel_dp_dpms, - .mode_fixup = psb_intel_dp_mode_fixup, - .prepare = psb_intel_dp_prepare, - .mode_set = psb_intel_dp_mode_set, - .commit = psb_intel_dp_commit, +static const struct drm_encoder_helper_funcs cdv_intel_dp_helper_funcs = { + .dpms = cdv_intel_dp_dpms, + .mode_fixup = cdv_intel_dp_mode_fixup, + .prepare = cdv_intel_dp_prepare, + .mode_set = cdv_intel_dp_mode_set, + .commit = cdv_intel_dp_commit, }; -static const struct drm_connector_funcs psb_intel_dp_connector_funcs = { +static const struct drm_connector_funcs cdv_intel_dp_connector_funcs = { .dpms = drm_helper_connector_dpms, - .detect = psb_intel_dp_detect, + .detect = cdv_intel_dp_detect, .fill_modes = drm_helper_probe_single_connector_modes, - .set_property = psb_intel_dp_set_property, - .destroy = psb_intel_dp_destroy, + .set_property = cdv_intel_dp_set_property, + .destroy = cdv_intel_dp_destroy, }; -static const struct drm_connector_helper_funcs psb_intel_dp_connector_helper_funcs = { - .get_modes = psb_intel_dp_get_modes, - .mode_valid = psb_intel_dp_mode_valid, +static const struct drm_connector_helper_funcs cdv_intel_dp_connector_helper_funcs = { + .get_modes = cdv_intel_dp_get_modes, + .mode_valid = cdv_intel_dp_mode_valid, .best_encoder = psb_intel_best_encoder, }; -static const struct drm_encoder_funcs psb_intel_dp_enc_funcs = { - .destroy = psb_intel_dp_encoder_destroy, +static const struct drm_encoder_funcs cdv_intel_dp_enc_funcs = { + .destroy = cdv_intel_dp_encoder_destroy, }; -static void -psb_intel_dp_add_properties(struct psb_intel_output *output, struct drm_connector *connector) +static void cdv_intel_dp_add_properties(struct drm_connector *connector) { - psb_intel_attach_force_audio_property(connector); - psb_intel_attach_broadcast_rgb_property(connector); + cdv_intel_attach_force_audio_property(connector); + cdv_intel_attach_broadcast_rgb_property(connector); } void -psb_intel_dp_init(struct drm_device *dev, struct psb_intel_mode_device *mode_dev, int output_reg) +cdv_intel_dp_init(struct drm_device *dev, struct psb_intel_mode_device *mode_dev, int output_reg) { + struct psb_intel_encoder *psb_intel_encoder; + struct psb_intel_connector *psb_intel_connector; struct drm_connector *connector; struct drm_encoder *encoder; - struct psb_intel_output *psb_intel_output; - struct psb_intel_dp *intel_dp; + struct cdv_intel_dp *intel_dp; const char *name = NULL; - int type; - psb_intel_output = kzalloc(sizeof(struct psb_intel_output) + - sizeof(struct psb_intel_dp), GFP_KERNEL); - if (!psb_intel_output) + psb_intel_encoder = kzalloc(sizeof(struct psb_intel_encoder), GFP_KERNEL); + if (!psb_intel_encoder) return; + psb_intel_connector = kzalloc(sizeof(struct psb_intel_connector), GFP_KERNEL); + if (!psb_intel_connector) + goto err_connector; + intel_dp = kzalloc(sizeof(struct cdv_intel_dp), GFP_KERNEL); + if (!intel_dp) + goto err_priv; + + connector = &psb_intel_connector->base; + encoder = &psb_intel_encoder->base; - intel_dp = (struct psb_intel_dp *)(psb_intel_output + 1); - psb_intel_output->mode_dev = mode_dev; - connector = &psb_intel_output->base; - encoder = &psb_intel_output->enc; - psb_intel_output->dev_priv=intel_dp; - intel_dp->output = psb_intel_output; + drm_connector_init(dev, connector, &cdv_intel_dp_connector_funcs, DRM_MODE_CONNECTOR_DisplayPort); + drm_encoder_init(dev, encoder, &cdv_intel_dp_enc_funcs, DRM_MODE_ENCODER_TMDS); + psb_intel_connector_attach_encoder(psb_intel_connector, psb_intel_encoder); + psb_intel_encoder->type = INTEL_OUTPUT_DISPLAYPORT; + + psb_intel_encoder->dev_priv=intel_dp; + intel_dp->encoder = psb_intel_encoder; intel_dp->output_reg = output_reg; - type = DRM_MODE_CONNECTOR_DisplayPort; - psb_intel_output->type = INTEL_OUTPUT_DISPLAYPORT; - - drm_connector_init(dev, connector, &psb_intel_dp_connector_funcs, type); - drm_connector_helper_add(connector, &psb_intel_dp_connector_helper_funcs); + drm_encoder_helper_add(encoder, &cdv_intel_dp_helper_funcs); + drm_connector_helper_add(connector, &cdv_intel_dp_connector_helper_funcs); connector->polled = DRM_CONNECTOR_POLL_HPD; - - connector->interlace_allowed = 0; - connector->doublescan_allowed = 0; - - drm_encoder_init(dev, encoder, &psb_intel_dp_enc_funcs, - DRM_MODE_ENCODER_TMDS); - drm_encoder_helper_add(encoder, &psb_intel_dp_helper_funcs); - - drm_mode_connector_attach_encoder(&psb_intel_output->base, - &psb_intel_output->enc); + connector->interlace_allowed = false; + connector->doublescan_allowed = false; drm_sysfs_connector_add(connector); @@ -1490,15 +1482,21 @@ psb_intel_dp_init(struct drm_device *dev, struct psb_intel_mode_device *mode_dev switch (output_reg) { case DP_B: name = "DPDDC-B"; - psb_intel_output->ddi_select = (DP_MASK | DDI0_SELECT); + psb_intel_encoder->ddi_select = (DP_MASK | DDI0_SELECT); break; case DP_C: name = "DPDDC-C"; - psb_intel_output->ddi_select = (DP_MASK | DDI1_SELECT); + psb_intel_encoder->ddi_select = (DP_MASK | DDI1_SELECT); break; } - psb_intel_dp_i2c_init(psb_intel_output, name); - psb_intel_dp_add_properties(psb_intel_output, connector); + cdv_intel_dp_i2c_init(psb_intel_connector, psb_intel_encoder, name); + /* FIXME:fail check */ + cdv_intel_dp_add_properties(connector); + return; +err_priv: + kfree(psb_intel_connector); +err_connector: + kfree(psb_intel_encoder); } diff --git a/drivers/gpu/drm/gma500/psb_intel_drv.h b/drivers/gpu/drm/gma500/psb_intel_drv.h index c574c01..c53e592 100644 --- a/drivers/gpu/drm/gma500/psb_intel_drv.h +++ b/drivers/gpu/drm/gma500/psb_intel_drv.h @@ -288,4 +288,20 @@ extern void gma_intel_gmbus_set_speed(struct i2c_adapter *adapter, int speed); extern void gma_intel_gmbus_force_bit(struct i2c_adapter *adapter, bool force_bit); extern void gma_intel_teardown_gmbus(struct drm_device *dev); +/* DP support */ +extern void cdv_intel_dp_init(struct drm_device *dev, struct psb_intel_mode_device *mode_dev, int output_reg); +extern void psb_intel_dp_set_m_n(struct drm_crtc *crtc, + struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode); + +extern void psb_intel_attach_force_audio_property(struct drm_connector *connector); +extern void psb_intel_attach_broadcast_rgb_property(struct drm_connector *connector); + +extern int cdv_sb_read(struct drm_device *dev, u32 reg, u32 *val); +extern int cdv_sb_write(struct drm_device *dev, u32 reg, u32 val); +extern void cdv_sb_reset(struct drm_device *dev); + +extern void cdv_intel_attach_force_audio_property(struct drm_connector *connector); +extern void cdv_intel_attach_broadcast_rgb_property(struct drm_connector *connector); + #endif /* __INTEL_DRV_H__ */ -- cgit v0.10.2 From 35659715c42b5cd148935e8ebd4e5e8e4e256b96 Mon Sep 17 00:00:00 2001 From: Alan Cox Date: Wed, 8 Aug 2012 13:55:26 +0000 Subject: gma500/cdv: enable the DisplayPort support This will give the basic support only Signed-off-by: Alan Cox Signed-off-by: Dave Airlie diff --git a/drivers/gpu/drm/gma500/cdv_device.c b/drivers/gpu/drm/gma500/cdv_device.c index e2fff24..68f100e 100644 --- a/drivers/gpu/drm/gma500/cdv_device.c +++ b/drivers/gpu/drm/gma500/cdv_device.c @@ -58,10 +58,17 @@ static int cdv_output_init(struct drm_device *dev) cdv_intel_lvds_init(dev, &dev_priv->mode_dev); /* These bits indicate HDMI not SDVO on CDV */ - if (REG_READ(SDVOB) & SDVO_DETECTED) + if (REG_READ(SDVOB) & SDVO_DETECTED) { cdv_hdmi_init(dev, &dev_priv->mode_dev, SDVOB); - if (REG_READ(SDVOC) & SDVO_DETECTED) + if (REG_READ(DP_B) & DP_DETECTED) + cdv_intel_dp_init(dev, &dev_priv->mode_dev, DP_B); + } + + if (REG_READ(SDVOC) & SDVO_DETECTED) { cdv_hdmi_init(dev, &dev_priv->mode_dev, SDVOC); + if (REG_READ(DP_C) & DP_DETECTED) + cdv_intel_dp_init(dev, &dev_priv->mode_dev, DP_C); + } return 0; } -- cgit v0.10.2 From d112a8163f83752361dd639a9a579ae5cc05c6cf Mon Sep 17 00:00:00 2001 From: Zhao Yakui Date: Wed, 8 Aug 2012 13:55:55 +0000 Subject: gma500/cdv: Add eDP support Introduce the eDP support into the driver. This has been reworked a bit because kernel driver proper uses encoder/connectors while the legacy Intel driver uses the old output stuff. It also diverges on the backlight handling. The legacy Intel driver adds a panel abstraction based upon the i915 one. It's only really used for backlight bits and we have a perfectly good backlight abstraction which can extend instead. Signed-off-by: Zhao Yakui [ported to upstream driver, redid backlight abstraction] Signed-off-by: Alan Cox Signed-off-by: Dave Airlie diff --git a/drivers/gpu/drm/gma500/backlight.c b/drivers/gpu/drm/gma500/backlight.c index 2079395..143eba3 100644 --- a/drivers/gpu/drm/gma500/backlight.c +++ b/drivers/gpu/drm/gma500/backlight.c @@ -26,10 +26,55 @@ #include "intel_bios.h" #include "power.h" +static void do_gma_backlight_set(struct drm_device *dev) +{ +#ifdef CONFIG_BACKLIGHT_CLASS_DEVICE + struct drm_psb_private *dev_priv = dev->dev_private; + backlight_update_status(dev_priv->backlight_device); +#endif +} + +void gma_backlight_enable(struct drm_device *dev) +{ +#ifdef CONFIG_BACKLIGHT_CLASS_DEVICE + struct drm_psb_private *dev_priv = dev->dev_private; + dev_priv->backlight_enabled = true; + if (dev_priv->backlight_device) { + dev_priv->backlight_device->props.brightness = dev_priv->backlight_level; + do_gma_backlight_set(dev); + } +#endif +} + +void gma_backlight_disable(struct drm_device *dev) +{ +#ifdef CONFIG_BACKLIGHT_CLASS_DEVICE + struct drm_psb_private *dev_priv = dev->dev_private; + dev_priv->backlight_enabled = false; + if (dev_priv->backlight_device) { + dev_priv->backlight_device->props.brightness = 0; + do_gma_backlight_set(dev); + } +#endif +} + +void gma_backlight_set(struct drm_device *dev, int v) +{ +#ifdef CONFIG_BACKLIGHT_CLASS_DEVICE + struct drm_psb_private *dev_priv = dev->dev_private; + dev_priv->backlight_level = v; + if (dev_priv->backlight_device && dev_priv->backlight_enabled) { + dev_priv->backlight_device->props.brightness = v; + do_gma_backlight_set(dev); + } +#endif +} + int gma_backlight_init(struct drm_device *dev) { #ifdef CONFIG_BACKLIGHT_CLASS_DEVICE struct drm_psb_private *dev_priv = dev->dev_private; + dev_priv->backlight_enabled = true; return dev_priv->ops->backlight_init(dev); #else return 0; diff --git a/drivers/gpu/drm/gma500/cdv_device.c b/drivers/gpu/drm/gma500/cdv_device.c index 68f100e..4882e64 100644 --- a/drivers/gpu/drm/gma500/cdv_device.c +++ b/drivers/gpu/drm/gma500/cdv_device.c @@ -170,6 +170,7 @@ static int cdv_backlight_init(struct drm_device *dev) cdv_get_brightness(cdv_backlight_device); backlight_update_status(cdv_backlight_device); dev_priv->backlight_device = cdv_backlight_device; + dev_priv->backlight_enabled = true; return 0; } diff --git a/drivers/gpu/drm/gma500/cdv_intel_display.c b/drivers/gpu/drm/gma500/cdv_intel_display.c index 3f11060..bfb0565 100644 --- a/drivers/gpu/drm/gma500/cdv_intel_display.c +++ b/drivers/gpu/drm/gma500/cdv_intel_display.c @@ -438,7 +438,8 @@ static const struct cdv_intel_limit_t *cdv_intel_limit(struct drm_crtc *crtc, limit = &cdv_intel_limits[CDV_LIMIT_SINGLE_LVDS_96]; else limit = &cdv_intel_limits[CDV_LIMIT_SINGLE_LVDS_100]; - } else if (psb_intel_pipe_has_type(crtc, INTEL_OUTPUT_DISPLAYPORT)) { + } else if (psb_intel_pipe_has_type(crtc, INTEL_OUTPUT_DISPLAYPORT) || + psb_intel_pipe_has_type(crtc, INTEL_OUTPUT_EDP)) { if (refclk == 27000) limit = &cdv_intel_limits[CDV_LIMIT_DP_27]; else @@ -1045,6 +1046,7 @@ static int cdv_intel_crtc_mode_set(struct drm_crtc *crtc, struct drm_connector *connector; const struct cdv_intel_limit_t *limit; u32 ddi_select = 0; + bool is_edp = false; list_for_each_entry(connector, &mode_config->connector_list, head) { struct psb_intel_encoder *psb_intel_encoder = @@ -1071,6 +1073,9 @@ static int cdv_intel_crtc_mode_set(struct drm_crtc *crtc, case INTEL_OUTPUT_DISPLAYPORT: is_dp = true; break; + case INTEL_OUTPUT_EDP: + is_edp = true; + break; default: DRM_ERROR("invalid output type.\n"); return 0; @@ -1083,7 +1088,15 @@ static int cdv_intel_crtc_mode_set(struct drm_crtc *crtc, else /* high-end sku, 27/100 mhz */ refclk = 27000; - if (is_dp) { + if (is_dp || is_edp) { + /* + * Based on the spec the low-end SKU has only CRT/LVDS. So it is + * unnecessary to consider it for DP/eDP. + * On the high-end SKU, it will use the 27/100M reference clk + * for DP/eDP. When using SSC clock, the ref clk is 100MHz.Otherwise + * it will be 27MHz. From the VBIOS code it seems that the pipe A choose + * 27MHz for DP/eDP while the Pipe B chooses the 100MHz. + */ if (pipe == 0) refclk = 27000; else @@ -1133,6 +1146,31 @@ static int cdv_intel_crtc_mode_set(struct drm_crtc *crtc, /* setup pipeconf */ pipeconf = REG_READ(map->conf); + pipeconf &= ~(PIPE_BPC_MASK); + if (is_edp) { + switch (dev_priv->edp.bpp) { + case 24: + pipeconf |= PIPE_8BPC; + break; + case 18: + pipeconf |= PIPE_6BPC; + break; + case 30: + pipeconf |= PIPE_10BPC; + break; + default: + pipeconf |= PIPE_8BPC; + break; + } + } else if (is_lvds) { + /* the BPC will be 6 if it is 18-bit LVDS panel */ + if ((REG_READ(LVDS) & LVDS_A3_POWER_MASK) == LVDS_A3_POWER_UP) + pipeconf |= PIPE_8BPC; + else + pipeconf |= PIPE_6BPC; + } else + pipeconf |= PIPE_8BPC; + /* Set up the display plane register */ dspcntr = DISPPLANE_GAMMA_ENABLE; diff --git a/drivers/gpu/drm/gma500/cdv_intel_dp.c b/drivers/gpu/drm/gma500/cdv_intel_dp.c index b29b602..0571ef9b 100644 --- a/drivers/gpu/drm/gma500/cdv_intel_dp.c +++ b/drivers/gpu/drm/gma500/cdv_intel_dp.c @@ -36,6 +36,20 @@ #include "psb_intel_reg.h" #include "drm_dp_helper.h" +#define _wait_for(COND, MS, W) ({ \ + unsigned long timeout__ = jiffies + msecs_to_jiffies(MS); \ + int ret__ = 0; \ + while (! (COND)) { \ + if (time_after(jiffies, timeout__)) { \ + ret__ = -ETIMEDOUT; \ + break; \ + } \ + if (W && !in_dbg_master()) msleep(W); \ + } \ + ret__; \ +}) + +#define wait_for(COND, MS) _wait_for(COND, MS, 1) #define DP_LINK_STATUS_SIZE 6 #define DP_LINK_CHECK_TIMEOUT (10 * 1000) @@ -59,6 +73,13 @@ struct cdv_intel_dp { struct i2c_algo_dp_aux_data algo; uint8_t train_set[4]; uint8_t link_status[DP_LINK_STATUS_SIZE]; + int panel_power_up_delay; + int panel_power_down_delay; + int panel_power_cycle_delay; + int backlight_on_delay; + int backlight_off_delay; + struct drm_display_mode *panel_fixed_mode; /* for eDP */ + bool panel_on; }; struct ddi_regoff { @@ -159,31 +180,166 @@ cdv_intel_dp_max_data_rate(int max_link_clock, int max_lanes) return (max_link_clock * max_lanes * 19) / 20; } +static void cdv_intel_edp_panel_vdd_on(struct psb_intel_encoder *intel_encoder) +{ + struct drm_device *dev = intel_encoder->base.dev; + struct cdv_intel_dp *intel_dp = intel_encoder->dev_priv; + u32 pp; + + if (intel_dp->panel_on) { + DRM_DEBUG_KMS("Skip VDD on because of panel on\n"); + return; + } + DRM_DEBUG_KMS("\n"); + + pp = REG_READ(PP_CONTROL); + + pp |= EDP_FORCE_VDD; + REG_WRITE(PP_CONTROL, pp); + REG_READ(PP_CONTROL); + msleep(intel_dp->panel_power_up_delay); +} + +static void cdv_intel_edp_panel_vdd_off(struct psb_intel_encoder *intel_encoder) +{ + struct drm_device *dev = intel_encoder->base.dev; + u32 pp; + + DRM_DEBUG_KMS("\n"); + pp = REG_READ(PP_CONTROL); + + pp &= ~EDP_FORCE_VDD; + REG_WRITE(PP_CONTROL, pp); + REG_READ(PP_CONTROL); + +} + +/* Returns true if the panel was already on when called */ +static bool cdv_intel_edp_panel_on(struct psb_intel_encoder *intel_encoder) +{ + struct drm_device *dev = intel_encoder->base.dev; + struct cdv_intel_dp *intel_dp = intel_encoder->dev_priv; + u32 pp, idle_on_mask = PP_ON | PP_SEQUENCE_NONE; + + if (intel_dp->panel_on) + return true; + + DRM_DEBUG_KMS("\n"); + pp = REG_READ(PP_CONTROL); + pp &= ~PANEL_UNLOCK_MASK; + + pp |= (PANEL_UNLOCK_REGS | POWER_TARGET_ON); + REG_WRITE(PP_CONTROL, pp); + REG_READ(PP_CONTROL); + + if (wait_for(((REG_READ(PP_STATUS) & idle_on_mask) == idle_on_mask), 1000)) { + DRM_DEBUG_KMS("Error in Powering up eDP panel, status %x\n", REG_READ(PP_STATUS)); + intel_dp->panel_on = false; + } else + intel_dp->panel_on = true; + msleep(intel_dp->panel_power_up_delay); + + return false; +} + +static void cdv_intel_edp_panel_off (struct psb_intel_encoder *intel_encoder) +{ + struct drm_device *dev = intel_encoder->base.dev; + u32 pp, idle_off_mask = PP_ON ; + struct cdv_intel_dp *intel_dp = intel_encoder->dev_priv; + + DRM_DEBUG_KMS("\n"); + + pp = REG_READ(PP_CONTROL); + + if ((pp & POWER_TARGET_ON) == 0) + return; + + intel_dp->panel_on = false; + pp &= ~PANEL_UNLOCK_MASK; + /* ILK workaround: disable reset around power sequence */ + + pp &= ~POWER_TARGET_ON; + pp &= ~EDP_FORCE_VDD; + pp &= ~EDP_BLC_ENABLE; + REG_WRITE(PP_CONTROL, pp); + REG_READ(PP_CONTROL); + DRM_DEBUG_KMS("PP_STATUS %x\n", REG_READ(PP_STATUS)); + + if (wait_for((REG_READ(PP_STATUS) & idle_off_mask) == 0, 1000)) { + DRM_DEBUG_KMS("Error in turning off Panel\n"); + } + + msleep(intel_dp->panel_power_cycle_delay); + DRM_DEBUG_KMS("Over\n"); +} + +static void cdv_intel_edp_backlight_on (struct psb_intel_encoder *intel_encoder) +{ + struct drm_device *dev = intel_encoder->base.dev; + u32 pp; + + DRM_DEBUG_KMS("\n"); + /* + * If we enable the backlight right away following a panel power + * on, we may see slight flicker as the panel syncs with the eDP + * link. So delay a bit to make sure the image is solid before + * allowing it to appear. + */ + msleep(300); + pp = REG_READ(PP_CONTROL); + + pp |= EDP_BLC_ENABLE; + REG_WRITE(PP_CONTROL, pp); + gma_backlight_enable(dev); +} + +static void cdv_intel_edp_backlight_off (struct psb_intel_encoder *intel_encoder) +{ + struct drm_device *dev = intel_encoder->base.dev; + struct cdv_intel_dp *intel_dp = intel_encoder->dev_priv; + u32 pp; + + DRM_DEBUG_KMS("\n"); + gma_backlight_disable(dev); + msleep(10); + pp = REG_READ(PP_CONTROL); + + pp &= ~EDP_BLC_ENABLE; + REG_WRITE(PP_CONTROL, pp); + msleep(intel_dp->backlight_off_delay); +} + static int cdv_intel_dp_mode_valid(struct drm_connector *connector, struct drm_display_mode *mode) { struct psb_intel_encoder *encoder = psb_intel_attached_encoder(connector); - struct drm_device *dev = connector->dev; - struct drm_psb_private *dev_priv = dev->dev_private; + struct cdv_intel_dp *intel_dp = encoder->dev_priv; int max_link_clock = cdv_intel_dp_link_clock(cdv_intel_dp_max_link_bw(encoder)); int max_lanes = cdv_intel_dp_max_lane_count(encoder); + struct drm_psb_private *dev_priv = connector->dev->dev_private; - if (is_edp(encoder) && dev_priv->panel_fixed_mode) { - if (mode->hdisplay > dev_priv->panel_fixed_mode->hdisplay) + if (is_edp(encoder) && intel_dp->panel_fixed_mode) { + if (mode->hdisplay > intel_dp->panel_fixed_mode->hdisplay) return MODE_PANEL; - - if (mode->vdisplay > dev_priv->panel_fixed_mode->vdisplay) + if (mode->vdisplay > intel_dp->panel_fixed_mode->vdisplay) return MODE_PANEL; } /* only refuse the mode on non eDP since we have seen some weird eDP panels which are outside spec tolerances but somehow work by magic */ if (!is_edp(encoder) && - (cdv_intel_dp_link_required(mode->clock, 24) + (cdv_intel_dp_link_required(mode->clock, dev_priv->edp.bpp) > cdv_intel_dp_max_data_rate(max_link_clock, max_lanes))) return MODE_CLOCK_HIGH; + if (is_edp(encoder)) { + if (cdv_intel_dp_link_required(mode->clock, 24) + > cdv_intel_dp_max_data_rate(max_link_clock, max_lanes)) + return MODE_CLOCK_HIGH; + + } if (mode->clock < 10000) return MODE_CLOCK_LOW; @@ -238,6 +394,8 @@ cdv_intel_dp_aux_ch(struct psb_intel_encoder *encoder, aux_clock_divider = 200 / 2; precharge = 4; + if (is_edp(encoder)) + precharge = 10; if (REG_READ(ch_ctl) & DP_AUX_CH_CTL_SEND_BUSY) { DRM_ERROR("dp_aux_ch not started status 0x%08x\n", @@ -492,7 +650,10 @@ static int cdv_intel_dp_i2c_init(struct psb_intel_connector *connector, struct psb_intel_encoder *encoder, const char *name) { struct cdv_intel_dp *intel_dp = encoder->dev_priv; + int ret; + DRM_DEBUG_KMS("i2c_init %s\n", name); + intel_dp->algo.running = false; intel_dp->algo.address = 0; intel_dp->algo.aux_ch = cdv_intel_dp_i2c_aux_ch; @@ -505,27 +666,58 @@ cdv_intel_dp_i2c_init(struct psb_intel_connector *connector, struct psb_intel_en intel_dp->adapter.algo_data = &intel_dp->algo; intel_dp->adapter.dev.parent = &connector->base.kdev; - return i2c_dp_aux_add_bus(&intel_dp->adapter); + if (is_edp(encoder)) + cdv_intel_edp_panel_vdd_on(encoder); + ret = i2c_dp_aux_add_bus(&intel_dp->adapter); + if (is_edp(encoder)) + cdv_intel_edp_panel_vdd_off(encoder); + + return ret; +} + +void cdv_intel_fixed_panel_mode(struct drm_display_mode *fixed_mode, + struct drm_display_mode *adjusted_mode) +{ + adjusted_mode->hdisplay = fixed_mode->hdisplay; + adjusted_mode->hsync_start = fixed_mode->hsync_start; + adjusted_mode->hsync_end = fixed_mode->hsync_end; + adjusted_mode->htotal = fixed_mode->htotal; + + adjusted_mode->vdisplay = fixed_mode->vdisplay; + adjusted_mode->vsync_start = fixed_mode->vsync_start; + adjusted_mode->vsync_end = fixed_mode->vsync_end; + adjusted_mode->vtotal = fixed_mode->vtotal; + + adjusted_mode->clock = fixed_mode->clock; + + drm_mode_set_crtcinfo(adjusted_mode, CRTC_INTERLACE_HALVE_V); } static bool cdv_intel_dp_mode_fixup(struct drm_encoder *encoder, const struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode) { + struct drm_psb_private *dev_priv = encoder->dev->dev_private; struct psb_intel_encoder *intel_encoder = to_psb_intel_encoder(encoder); struct cdv_intel_dp *intel_dp = intel_encoder->dev_priv; int lane_count, clock; int max_lane_count = cdv_intel_dp_max_lane_count(intel_encoder); int max_clock = cdv_intel_dp_max_link_bw(intel_encoder) == DP_LINK_BW_2_7 ? 1 : 0; static int bws[2] = { DP_LINK_BW_1_62, DP_LINK_BW_2_7 }; + int refclock = mode->clock; + int bpp = 24; + if (is_edp(intel_encoder) && intel_dp->panel_fixed_mode) { + cdv_intel_fixed_panel_mode(intel_dp->panel_fixed_mode, adjusted_mode); + refclock = intel_dp->panel_fixed_mode->clock; + bpp = dev_priv->edp.bpp; + } for (lane_count = 1; lane_count <= max_lane_count; lane_count <<= 1) { for (clock = max_clock; clock >= 0; clock--) { int link_avail = cdv_intel_dp_max_data_rate(cdv_intel_dp_link_clock(bws[clock]), lane_count); - if (cdv_intel_dp_link_required(mode->clock, 24) - <= link_avail) { + if (cdv_intel_dp_link_required(refclock, bpp) <= link_avail) { intel_dp->link_bw = bws[clock]; intel_dp->lane_count = lane_count; adjusted_mode->clock = cdv_intel_dp_link_clock(intel_dp->link_bw); @@ -537,7 +729,18 @@ cdv_intel_dp_mode_fixup(struct drm_encoder *encoder, const struct drm_display_mo } } } - + if (is_edp(intel_encoder)) { + /* okay we failed just pick the highest */ + intel_dp->lane_count = max_lane_count; + intel_dp->link_bw = bws[max_clock]; + adjusted_mode->clock = cdv_intel_dp_link_clock(intel_dp->link_bw); + DRM_DEBUG_KMS("Force picking display port link bw %02x lane " + "count %d clock %d\n", + intel_dp->link_bw, intel_dp->lane_count, + adjusted_mode->clock); + + return true; + } return false; } @@ -550,7 +753,7 @@ struct cdv_intel_dp_m_n { }; static void -psb_intel_reduce_ratio(uint32_t *num, uint32_t *den) +cdv_intel_reduce_ratio(uint32_t *num, uint32_t *den) { /* while (*num > 0xffffff || *den > 0xffffff) { @@ -575,10 +778,10 @@ cdv_intel_dp_compute_m_n(int bpp, m_n->tu = 64; m_n->gmch_m = (pixel_clock * bpp + 7) >> 3; m_n->gmch_n = link_clock * nlanes; - psb_intel_reduce_ratio(&m_n->gmch_m, &m_n->gmch_n); + cdv_intel_reduce_ratio(&m_n->gmch_m, &m_n->gmch_n); m_n->link_m = pixel_clock; m_n->link_n = link_clock; - psb_intel_reduce_ratio(&m_n->link_m, &m_n->link_n); + cdv_intel_reduce_ratio(&m_n->link_m, &m_n->link_n); } void @@ -586,6 +789,7 @@ cdv_intel_dp_set_m_n(struct drm_crtc *crtc, struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode) { struct drm_device *dev = crtc->dev; + struct drm_psb_private *dev_priv = dev->dev_private; struct drm_mode_config *mode_config = &dev->mode_config; struct drm_encoder *encoder; struct psb_intel_crtc *intel_crtc = to_psb_intel_crtc(crtc); @@ -610,6 +814,7 @@ cdv_intel_dp_set_m_n(struct drm_crtc *crtc, struct drm_display_mode *mode, break; } else if (is_edp(intel_encoder)) { lane_count = intel_dp->lane_count; + bpp = dev_priv->edp.bpp; break; } } @@ -640,7 +845,7 @@ cdv_intel_dp_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode struct drm_crtc *crtc = encoder->crtc; struct psb_intel_crtc *intel_crtc = to_psb_intel_crtc(crtc); struct cdv_intel_dp *intel_dp = intel_encoder->dev_priv; - + struct drm_device *dev = encoder->dev; intel_dp->DP = DP_VOLTAGE_0_4 | DP_PRE_EMPHASIS_0; intel_dp->DP |= intel_dp->color_range; @@ -683,7 +888,22 @@ cdv_intel_dp_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode if (intel_crtc->pipe == 1) intel_dp->DP |= DP_PIPEB_SELECT; + REG_WRITE(intel_dp->output_reg, (intel_dp->DP | DP_PORT_EN)); DRM_DEBUG_KMS("DP expected reg is %x\n", intel_dp->DP); + if (is_edp(intel_encoder)) { + uint32_t pfit_control; + cdv_intel_edp_panel_on(intel_encoder); + + if (mode->hdisplay != adjusted_mode->hdisplay || + mode->vdisplay != adjusted_mode->vdisplay) + pfit_control = PFIT_ENABLE; + else + pfit_control = 0; + + pfit_control |= intel_crtc->pipe << PFIT_PIPE_SHIFT; + + REG_WRITE(PFIT_CONTROL, pfit_control); + } } @@ -721,18 +941,31 @@ static void cdv_intel_dp_sink_dpms(struct psb_intel_encoder *encoder, int mode) static void cdv_intel_dp_prepare(struct drm_encoder *encoder) { struct psb_intel_encoder *intel_encoder = to_psb_intel_encoder(encoder); + int edp = is_edp(intel_encoder); + if (edp) { + cdv_intel_edp_backlight_off(intel_encoder); + cdv_intel_edp_panel_off(intel_encoder); + cdv_intel_edp_panel_vdd_on(intel_encoder); + } /* Wake up the sink first */ cdv_intel_dp_sink_dpms(intel_encoder, DRM_MODE_DPMS_ON); cdv_intel_dp_link_down(intel_encoder); + if (edp) + cdv_intel_edp_panel_vdd_off(intel_encoder); } static void cdv_intel_dp_commit(struct drm_encoder *encoder) { struct psb_intel_encoder *intel_encoder = to_psb_intel_encoder(encoder); + int edp = is_edp(intel_encoder); + if (edp) + cdv_intel_edp_panel_on(intel_encoder); cdv_intel_dp_start_link_train(intel_encoder); cdv_intel_dp_complete_link_train(intel_encoder); + if (edp) + cdv_intel_edp_backlight_on(intel_encoder); } static void @@ -742,16 +975,29 @@ cdv_intel_dp_dpms(struct drm_encoder *encoder, int mode) struct cdv_intel_dp *intel_dp = intel_encoder->dev_priv; struct drm_device *dev = encoder->dev; uint32_t dp_reg = REG_READ(intel_dp->output_reg); + int edp = is_edp(intel_encoder); if (mode != DRM_MODE_DPMS_ON) { + if (edp) { + cdv_intel_edp_backlight_off(intel_encoder); + cdv_intel_edp_panel_vdd_on(intel_encoder); + } cdv_intel_dp_sink_dpms(intel_encoder, mode); cdv_intel_dp_link_down(intel_encoder); + if (edp) { + cdv_intel_edp_panel_vdd_off(intel_encoder); + cdv_intel_edp_panel_off(intel_encoder); + } } else { + if (edp) + cdv_intel_edp_panel_on(intel_encoder); cdv_intel_dp_sink_dpms(intel_encoder, mode); if (!(dp_reg & DP_PORT_EN)) { cdv_intel_dp_start_link_train(intel_encoder); cdv_intel_dp_complete_link_train(intel_encoder); } + if (edp) + cdv_intel_edp_backlight_on(intel_encoder); } } @@ -1090,6 +1336,10 @@ cdv_intel_dp_start_link_train(struct psb_intel_encoder *encoder) for (;;) { /* Use intel_dp->train_set[0] to set the voltage and pre emphasis values */ + DRM_DEBUG_KMS("DP Link Train Set %x, Link_config %x, %x\n", + intel_dp->train_set[0], + intel_dp->link_configuration[0], + intel_dp->link_configuration[1]); if (!cdv_intel_dp_set_link_train(encoder, reg, DP_TRAINING_PATTERN_1)) { DRM_DEBUG_KMS("Failure in aux-transfer setting pattern 1\n"); @@ -1103,6 +1353,10 @@ cdv_intel_dp_start_link_train(struct psb_intel_encoder *encoder) if (!cdv_intel_dp_get_link_status(encoder)) break; + DRM_DEBUG_KMS("DP Link status %x, %x, %x, %x, %x, %x\n", + intel_dp->link_status[0], intel_dp->link_status[1], intel_dp->link_status[2], + intel_dp->link_status[3], intel_dp->link_status[4], intel_dp->link_status[5]); + if (cdv_intel_clock_recovery_ok(intel_dp->link_status, intel_dp->lane_count)) { DRM_DEBUG_KMS("PT1 train is done\n"); clock_recovery = true; @@ -1156,7 +1410,13 @@ cdv_intel_dp_complete_link_train(struct psb_intel_encoder *encoder) reg = DP | DP_LINK_TRAIN_PAT_2; for (;;) { - /* channel eq pattern */ + + DRM_DEBUG_KMS("DP Link Train Set %x, Link_config %x, %x\n", + intel_dp->train_set[0], + intel_dp->link_configuration[0], + intel_dp->link_configuration[1]); + /* channel eq pattern */ + if (!cdv_intel_dp_set_link_train(encoder, reg, DP_TRAINING_PATTERN_2)) { DRM_DEBUG_KMS("Failure in aux-transfer setting pattern 2\n"); @@ -1177,6 +1437,10 @@ cdv_intel_dp_complete_link_train(struct psb_intel_encoder *encoder) if (!cdv_intel_dp_get_link_status(encoder)) break; + DRM_DEBUG_KMS("DP Link status %x, %x, %x, %x, %x, %x\n", + intel_dp->link_status[0], intel_dp->link_status[1], intel_dp->link_status[2], + intel_dp->link_status[3], intel_dp->link_status[4], intel_dp->link_status[5]); + /* Make sure clock is still ok */ if (!cdv_intel_clock_recovery_ok(intel_dp->link_status, intel_dp->lane_count)) { cdv_intel_dp_start_link_train(encoder); @@ -1271,12 +1535,18 @@ cdv_intel_dp_detect(struct drm_connector *connector, bool force) struct cdv_intel_dp *intel_dp = encoder->dev_priv; enum drm_connector_status status; struct edid *edid = NULL; + int edp = is_edp(encoder); intel_dp->has_audio = false; + if (edp) + cdv_intel_edp_panel_vdd_on(encoder); status = cdv_dp_detect(encoder); - if (status != connector_status_connected) + if (status != connector_status_connected) { + if (edp) + cdv_intel_edp_panel_vdd_off(encoder); return status; + } if (intel_dp->force_audio) { intel_dp->has_audio = intel_dp->force_audio > 0; @@ -1288,6 +1558,8 @@ cdv_intel_dp_detect(struct drm_connector *connector, bool force) kfree(edid); } } + if (edp) + cdv_intel_edp_panel_vdd_off(encoder); return connector_status_connected; } @@ -1298,6 +1570,7 @@ static int cdv_intel_dp_get_modes(struct drm_connector *connector) struct cdv_intel_dp *intel_dp = intel_encoder->dev_priv; struct edid *edid = NULL; int ret = 0; + int edp = is_edp(intel_encoder); edid = drm_get_edid(connector, &intel_dp->adapter); @@ -1307,6 +1580,42 @@ static int cdv_intel_dp_get_modes(struct drm_connector *connector) kfree(edid); } + if (is_edp(intel_encoder)) { + struct drm_device *dev = connector->dev; + struct drm_psb_private *dev_priv = dev->dev_private; + + cdv_intel_edp_panel_vdd_off(intel_encoder); + if (ret) { + if (edp && !intel_dp->panel_fixed_mode) { + struct drm_display_mode *newmode; + list_for_each_entry(newmode, &connector->probed_modes, + head) { + if (newmode->type & DRM_MODE_TYPE_PREFERRED) { + intel_dp->panel_fixed_mode = + drm_mode_duplicate(dev, newmode); + break; + } + } + } + + return ret; + } + if (!intel_dp->panel_fixed_mode && dev_priv->lfp_lvds_vbt_mode) { + intel_dp->panel_fixed_mode = + drm_mode_duplicate(dev, dev_priv->lfp_lvds_vbt_mode); + if (intel_dp->panel_fixed_mode) { + intel_dp->panel_fixed_mode->type |= + DRM_MODE_TYPE_PREFERRED; + } + } + if (intel_dp->panel_fixed_mode != NULL) { + struct drm_display_mode *mode; + mode = drm_mode_duplicate(dev, intel_dp->panel_fixed_mode); + drm_mode_probed_add(connector, mode); + return 1; + } + } + return ret; } @@ -1317,6 +1626,10 @@ cdv_intel_dp_detect_audio(struct drm_connector *connector) struct cdv_intel_dp *intel_dp = encoder->dev_priv; struct edid *edid; bool has_audio = false; + int edp = is_edp(encoder); + + if (edp) + cdv_intel_edp_panel_vdd_on(encoder); edid = drm_get_edid(connector, &intel_dp->adapter); if (edid) { @@ -1325,6 +1638,8 @@ cdv_intel_dp_detect_audio(struct drm_connector *connector) connector->display_info.raw_edid = NULL; kfree(edid); } + if (edp) + cdv_intel_edp_panel_vdd_off(encoder); return has_audio; } @@ -1386,12 +1701,19 @@ done: } static void -cdv_intel_dp_destroy (struct drm_connector *connector) +cdv_intel_dp_destroy(struct drm_connector *connector) { struct psb_intel_encoder *psb_intel_encoder = psb_intel_attached_encoder(connector); struct cdv_intel_dp *intel_dp = psb_intel_encoder->dev_priv; + if (is_edp(psb_intel_encoder)) { + /* cdv_intel_panel_destroy_backlight(connector->dev); */ + if (intel_dp->panel_fixed_mode) { + kfree(intel_dp->panel_fixed_mode); + intel_dp->panel_fixed_mode = NULL; + } + } i2c_del_adapter(&intel_dp->adapter); drm_sysfs_connector_remove(connector); drm_connector_cleanup(connector); @@ -1436,6 +1758,26 @@ static void cdv_intel_dp_add_properties(struct drm_connector *connector) cdv_intel_attach_broadcast_rgb_property(connector); } +/* check the VBT to see whether the eDP is on DP-D port */ +static bool cdv_intel_dpc_is_edp(struct drm_device *dev) +{ + struct drm_psb_private *dev_priv = dev->dev_private; + struct child_device_config *p_child; + int i; + + if (!dev_priv->child_dev_num) + return false; + + for (i = 0; i < dev_priv->child_dev_num; i++) { + p_child = dev_priv->child_dev + i; + + if (p_child->dvo_port == PORT_IDPC && + p_child->device_type == DEVICE_TYPE_eDP) + return true; + } + return false; +} + void cdv_intel_dp_init(struct drm_device *dev, struct psb_intel_mode_device *mode_dev, int output_reg) { @@ -1445,6 +1787,7 @@ cdv_intel_dp_init(struct drm_device *dev, struct psb_intel_mode_device *mode_dev struct drm_encoder *encoder; struct cdv_intel_dp *intel_dp; const char *name = NULL; + int type = DRM_MODE_CONNECTOR_DisplayPort; psb_intel_encoder = kzalloc(sizeof(struct psb_intel_encoder), GFP_KERNEL); if (!psb_intel_encoder) @@ -1456,14 +1799,22 @@ cdv_intel_dp_init(struct drm_device *dev, struct psb_intel_mode_device *mode_dev if (!intel_dp) goto err_priv; + if ((output_reg == DP_C) && cdv_intel_dpc_is_edp(dev)) + type = DRM_MODE_CONNECTOR_eDP; + connector = &psb_intel_connector->base; encoder = &psb_intel_encoder->base; - drm_connector_init(dev, connector, &cdv_intel_dp_connector_funcs, DRM_MODE_CONNECTOR_DisplayPort); + drm_connector_init(dev, connector, &cdv_intel_dp_connector_funcs, type); drm_encoder_init(dev, encoder, &cdv_intel_dp_enc_funcs, DRM_MODE_ENCODER_TMDS); psb_intel_connector_attach_encoder(psb_intel_connector, psb_intel_encoder); - psb_intel_encoder->type = INTEL_OUTPUT_DISPLAYPORT; + + if (type == DRM_MODE_CONNECTOR_DisplayPort) + psb_intel_encoder->type = INTEL_OUTPUT_DISPLAYPORT; + else + psb_intel_encoder->type = INTEL_OUTPUT_EDP; + psb_intel_encoder->dev_priv=intel_dp; intel_dp->encoder = psb_intel_encoder; @@ -1493,6 +1844,83 @@ cdv_intel_dp_init(struct drm_device *dev, struct psb_intel_mode_device *mode_dev cdv_intel_dp_i2c_init(psb_intel_connector, psb_intel_encoder, name); /* FIXME:fail check */ cdv_intel_dp_add_properties(connector); + + if (is_edp(psb_intel_encoder)) { + int ret; + struct edp_power_seq cur; + u32 pp_on, pp_off, pp_div; + u32 pwm_ctrl; + + pp_on = REG_READ(PP_CONTROL); + pp_on &= ~PANEL_UNLOCK_MASK; + pp_on |= PANEL_UNLOCK_REGS; + + REG_WRITE(PP_CONTROL, pp_on); + + pwm_ctrl = REG_READ(BLC_PWM_CTL2); + pwm_ctrl |= PWM_PIPE_B; + REG_WRITE(BLC_PWM_CTL2, pwm_ctrl); + + pp_on = REG_READ(PP_ON_DELAYS); + pp_off = REG_READ(PP_OFF_DELAYS); + pp_div = REG_READ(PP_DIVISOR); + + /* Pull timing values out of registers */ + cur.t1_t3 = (pp_on & PANEL_POWER_UP_DELAY_MASK) >> + PANEL_POWER_UP_DELAY_SHIFT; + + cur.t8 = (pp_on & PANEL_LIGHT_ON_DELAY_MASK) >> + PANEL_LIGHT_ON_DELAY_SHIFT; + + cur.t9 = (pp_off & PANEL_LIGHT_OFF_DELAY_MASK) >> + PANEL_LIGHT_OFF_DELAY_SHIFT; + + cur.t10 = (pp_off & PANEL_POWER_DOWN_DELAY_MASK) >> + PANEL_POWER_DOWN_DELAY_SHIFT; + + cur.t11_t12 = ((pp_div & PANEL_POWER_CYCLE_DELAY_MASK) >> + PANEL_POWER_CYCLE_DELAY_SHIFT); + + DRM_DEBUG_KMS("cur t1_t3 %d t8 %d t9 %d t10 %d t11_t12 %d\n", + cur.t1_t3, cur.t8, cur.t9, cur.t10, cur.t11_t12); + + + intel_dp->panel_power_up_delay = cur.t1_t3 / 10; + intel_dp->backlight_on_delay = cur.t8 / 10; + intel_dp->backlight_off_delay = cur.t9 / 10; + intel_dp->panel_power_down_delay = cur.t10 / 10; + intel_dp->panel_power_cycle_delay = (cur.t11_t12 - 1) * 100; + + DRM_DEBUG_KMS("panel power up delay %d, power down delay %d, power cycle delay %d\n", + intel_dp->panel_power_up_delay, intel_dp->panel_power_down_delay, + intel_dp->panel_power_cycle_delay); + + DRM_DEBUG_KMS("backlight on delay %d, off delay %d\n", + intel_dp->backlight_on_delay, intel_dp->backlight_off_delay); + + + cdv_intel_edp_panel_vdd_on(psb_intel_encoder); + ret = cdv_intel_dp_aux_native_read(psb_intel_encoder, DP_DPCD_REV, + intel_dp->dpcd, + sizeof(intel_dp->dpcd)); + cdv_intel_edp_panel_vdd_off(psb_intel_encoder); + if (ret == 0) { + /* if this fails, presume the device is a ghost */ + DRM_INFO("failed to retrieve link info, disabling eDP\n"); + cdv_intel_dp_encoder_destroy(encoder); + cdv_intel_dp_destroy(connector); + goto err_priv; + } else { + DRM_DEBUG_KMS("DPCD: Rev=%x LN_Rate=%x LN_CNT=%x LN_DOWNSP=%x\n", + intel_dp->dpcd[0], intel_dp->dpcd[1], + intel_dp->dpcd[2], intel_dp->dpcd[3]); + + } + /* The CDV reference driver moves pnale backlight setup into the displays that + have a backlight: this is a good idea and one we should probably adopt, however + we need to migrate all the drivers before we can do that */ + /*cdv_intel_panel_setup_backlight(dev); */ + } return; err_priv: diff --git a/drivers/gpu/drm/gma500/cdv_intel_lvds.c b/drivers/gpu/drm/gma500/cdv_intel_lvds.c index c7f9468..b362dd3 100644 --- a/drivers/gpu/drm/gma500/cdv_intel_lvds.c +++ b/drivers/gpu/drm/gma500/cdv_intel_lvds.c @@ -506,16 +506,8 @@ static int cdv_intel_lvds_set_property(struct drm_connector *connector, property, value)) return -1; - else { -#ifdef CONFIG_BACKLIGHT_CLASS_DEVICE - struct drm_psb_private *dev_priv = - encoder->dev->dev_private; - struct backlight_device *bd = - dev_priv->backlight_device; - bd->props.brightness = value; - backlight_update_status(bd); -#endif - } + else + gma_backlight_set(encoder->dev, value); } else if (!strcmp(property->name, "DPMS") && encoder) { struct drm_encoder_helper_funcs *helpers = encoder->helper_private; diff --git a/drivers/gpu/drm/gma500/framebuffer.c b/drivers/gpu/drm/gma500/framebuffer.c index 2de6b1f..884ba73 100644 --- a/drivers/gpu/drm/gma500/framebuffer.c +++ b/drivers/gpu/drm/gma500/framebuffer.c @@ -768,6 +768,9 @@ static void psb_setup_outputs(struct drm_device *dev) crtc_mask = (1 << 0) | (1 << 1); clone_mask = (1 << INTEL_OUTPUT_DISPLAYPORT); break; + case INTEL_OUTPUT_EDP: + crtc_mask = (1 << 1); + clone_mask = (1 << INTEL_OUTPUT_EDP); } encoder->possible_crtcs = crtc_mask; encoder->possible_clones = diff --git a/drivers/gpu/drm/gma500/intel_bios.c b/drivers/gpu/drm/gma500/intel_bios.c index 8d7caf0..4fb79cf 100644 --- a/drivers/gpu/drm/gma500/intel_bios.c +++ b/drivers/gpu/drm/gma500/intel_bios.c @@ -54,6 +54,98 @@ static void *find_section(struct bdb_header *bdb, int section_id) return NULL; } +static void +parse_edp(struct drm_psb_private *dev_priv, struct bdb_header *bdb) +{ + struct bdb_edp *edp; + struct edp_power_seq *edp_pps; + struct edp_link_params *edp_link_params; + uint8_t panel_type; + + edp = find_section(bdb, BDB_EDP); + + dev_priv->edp.bpp = 18; + if (!edp) { + if (dev_priv->edp.support) { + DRM_DEBUG_KMS("No eDP BDB found but eDP panel supported, assume %dbpp panel color depth.\n", + dev_priv->edp.bpp); + } + return; + } + + panel_type = dev_priv->panel_type; + switch ((edp->color_depth >> (panel_type * 2)) & 3) { + case EDP_18BPP: + dev_priv->edp.bpp = 18; + break; + case EDP_24BPP: + dev_priv->edp.bpp = 24; + break; + case EDP_30BPP: + dev_priv->edp.bpp = 30; + break; + } + + /* Get the eDP sequencing and link info */ + edp_pps = &edp->power_seqs[panel_type]; + edp_link_params = &edp->link_params[panel_type]; + + dev_priv->edp.pps = *edp_pps; + + DRM_DEBUG_KMS("EDP timing in vbt t1_t3 %d t8 %d t9 %d t10 %d t11_t12 %d\n", + dev_priv->edp.pps.t1_t3, dev_priv->edp.pps.t8, + dev_priv->edp.pps.t9, dev_priv->edp.pps.t10, + dev_priv->edp.pps.t11_t12); + + dev_priv->edp.rate = edp_link_params->rate ? DP_LINK_BW_2_7 : + DP_LINK_BW_1_62; + switch (edp_link_params->lanes) { + case 0: + dev_priv->edp.lanes = 1; + break; + case 1: + dev_priv->edp.lanes = 2; + break; + case 3: + default: + dev_priv->edp.lanes = 4; + break; + } + DRM_DEBUG_KMS("VBT reports EDP: Lane_count %d, Lane_rate %d, Bpp %d\n", + dev_priv->edp.lanes, dev_priv->edp.rate, dev_priv->edp.bpp); + + switch (edp_link_params->preemphasis) { + case 0: + dev_priv->edp.preemphasis = DP_TRAIN_PRE_EMPHASIS_0; + break; + case 1: + dev_priv->edp.preemphasis = DP_TRAIN_PRE_EMPHASIS_3_5; + break; + case 2: + dev_priv->edp.preemphasis = DP_TRAIN_PRE_EMPHASIS_6; + break; + case 3: + dev_priv->edp.preemphasis = DP_TRAIN_PRE_EMPHASIS_9_5; + break; + } + switch (edp_link_params->vswing) { + case 0: + dev_priv->edp.vswing = DP_TRAIN_VOLTAGE_SWING_400; + break; + case 1: + dev_priv->edp.vswing = DP_TRAIN_VOLTAGE_SWING_600; + break; + case 2: + dev_priv->edp.vswing = DP_TRAIN_VOLTAGE_SWING_800; + break; + case 3: + dev_priv->edp.vswing = DP_TRAIN_VOLTAGE_SWING_1200; + break; + } + DRM_DEBUG_KMS("VBT reports EDP: VSwing %d, Preemph %d\n", + dev_priv->edp.vswing, dev_priv->edp.preemphasis); +} + static u16 get_blocksize(void *p) { @@ -154,6 +246,8 @@ static void parse_lfp_panel_data(struct drm_psb_private *dev_priv, return; dev_priv->lvds_dither = lvds_options->pixel_dither; + dev_priv->panel_type = lvds_options->panel_type; + if (lvds_options->panel_type == 0xff) return; @@ -340,6 +434,9 @@ parse_driver_features(struct drm_psb_private *dev_priv, if (!driver) return; + if (driver->lvds_config == BDB_DRIVER_FEATURE_EDP) + dev_priv->edp.support = 1; + /* This bit means to use 96Mhz for DPLL_A or not */ if (driver->primary_lfp_id) dev_priv->dplla_96mhz = true; @@ -437,6 +534,9 @@ int psb_intel_init_bios(struct drm_device *dev) size_t size; int i; + + dev_priv->panel_type = 0xff; + /* XXX Should this validation be moved to intel_opregion.c? */ if (dev_priv->opregion.vbt) { struct vbt_header *vbt = dev_priv->opregion.vbt; @@ -477,6 +577,7 @@ int psb_intel_init_bios(struct drm_device *dev) parse_sdvo_device_mapping(dev_priv, bdb); parse_device_mapping(dev_priv, bdb); parse_backlight_data(dev_priv, bdb); + parse_edp(dev_priv, bdb); if (bios) pci_unmap_rom(pdev, bios); diff --git a/drivers/gpu/drm/gma500/intel_bios.h b/drivers/gpu/drm/gma500/intel_bios.h index 2e95523..c6267c9 100644 --- a/drivers/gpu/drm/gma500/intel_bios.h +++ b/drivers/gpu/drm/gma500/intel_bios.h @@ -23,6 +23,7 @@ #define _I830_BIOS_H_ #include +#include struct vbt_header { u8 signature[20]; /**< Always starts with 'VBT$' */ @@ -93,6 +94,7 @@ struct vbios_data { #define BDB_SDVO_LVDS_PNP_IDS 24 #define BDB_SDVO_LVDS_POWER_SEQ 25 #define BDB_TV_OPTIONS 26 +#define BDB_EDP 27 #define BDB_LVDS_OPTIONS 40 #define BDB_LVDS_LFP_DATA_PTRS 41 #define BDB_LVDS_LFP_DATA 42 @@ -391,6 +393,11 @@ struct bdb_sdvo_lvds_options { u8 panel_misc_bits_4; } __attribute__((packed)); +#define BDB_DRIVER_FEATURE_NO_LVDS 0 +#define BDB_DRIVER_FEATURE_INT_LVDS 1 +#define BDB_DRIVER_FEATURE_SDVO_LVDS 2 +#define BDB_DRIVER_FEATURE_EDP 3 + struct bdb_driver_features { u8 boot_dev_algorithm:1; u8 block_display_switch:1; @@ -431,6 +438,45 @@ struct bdb_driver_features { u8 custom_vbt_version; } __attribute__((packed)); +#define EDP_18BPP 0 +#define EDP_24BPP 1 +#define EDP_30BPP 2 +#define EDP_RATE_1_62 0 +#define EDP_RATE_2_7 1 +#define EDP_LANE_1 0 +#define EDP_LANE_2 1 +#define EDP_LANE_4 3 +#define EDP_PREEMPHASIS_NONE 0 +#define EDP_PREEMPHASIS_3_5dB 1 +#define EDP_PREEMPHASIS_6dB 2 +#define EDP_PREEMPHASIS_9_5dB 3 +#define EDP_VSWING_0_4V 0 +#define EDP_VSWING_0_6V 1 +#define EDP_VSWING_0_8V 2 +#define EDP_VSWING_1_2V 3 + +struct edp_power_seq { + u16 t1_t3; + u16 t8; + u16 t9; + u16 t10; + u16 t11_t12; +} __attribute__ ((packed)); + +struct edp_link_params { + u8 rate:4; + u8 lanes:4; + u8 preemphasis:4; + u8 vswing:4; +} __attribute__ ((packed)); + +struct bdb_edp { + struct edp_power_seq power_seqs[16]; + u32 color_depth; + u32 sdrrs_msa_timing_delay; + struct edp_link_params link_params[16]; +} __attribute__ ((packed)); + extern int psb_intel_init_bios(struct drm_device *dev); extern void psb_intel_destroy_bios(struct drm_device *dev); diff --git a/drivers/gpu/drm/gma500/mdfld_dsi_output.c b/drivers/gpu/drm/gma500/mdfld_dsi_output.c index 5675d93..32dba2a 100644 --- a/drivers/gpu/drm/gma500/mdfld_dsi_output.c +++ b/drivers/gpu/drm/gma500/mdfld_dsi_output.c @@ -299,17 +299,8 @@ static int mdfld_dsi_connector_set_property(struct drm_connector *connector, if (drm_connector_property_set_value(connector, property, value)) goto set_prop_error; - else { -#ifdef CONFIG_BACKLIGHT_CLASS_DEVICE - struct backlight_device *psb_bd; - - psb_bd = mdfld_get_backlight_device(); - if (psb_bd) { - psb_bd->props.brightness = value; - mdfld_set_brightness(psb_bd); - } -#endif - } + else + gma_backlight_set(encoder->dev, value); } set_prop_done: return 0; diff --git a/drivers/gpu/drm/gma500/mid_bios.c b/drivers/gpu/drm/gma500/mid_bios.c index b2a790b..850cd3f 100644 --- a/drivers/gpu/drm/gma500/mid_bios.c +++ b/drivers/gpu/drm/gma500/mid_bios.c @@ -118,20 +118,20 @@ static void mid_get_pci_revID(struct drm_psb_private *dev_priv) dev_priv->platform_rev_id); } -struct vbt_header { +struct mid_vbt_header { u32 signature; u8 revision; } __packed; /* The same for r0 and r1 */ struct vbt_r0 { - struct vbt_header vbt_header; + struct mid_vbt_header vbt_header; u8 size; u8 checksum; } __packed; struct vbt_r10 { - struct vbt_header vbt_header; + struct mid_vbt_header vbt_header; u8 checksum; u16 size; u8 panel_count; @@ -281,7 +281,7 @@ static void mid_get_vbt_data(struct drm_psb_private *dev_priv) struct drm_device *dev = dev_priv->dev; u32 addr; u8 __iomem *vbt_virtual; - struct vbt_header vbt_header; + struct mid_vbt_header vbt_header; struct pci_dev *pci_gfx_root = pci_get_bus_and_slot(0, PCI_DEVFN(2, 0)); int ret = -1; diff --git a/drivers/gpu/drm/gma500/opregion.c b/drivers/gpu/drm/gma500/opregion.c index c430bd4..ad0d6de 100644 --- a/drivers/gpu/drm/gma500/opregion.c +++ b/drivers/gpu/drm/gma500/opregion.c @@ -166,8 +166,7 @@ static u32 asle_set_backlight(struct drm_device *dev, u32 bclp) if (config_enabled(CONFIG_BACKLIGHT_CLASS_DEVICE)) { int max = bd->props.max_brightness; - bd->props.brightness = bclp * max / 255; - backlight_update_status(bd); + gma_backlight_set(dev, bclp * max / 255); } asle->cblv = (bclp * 0x64) / 0xff | ASLE_CBLV_VALID; diff --git a/drivers/gpu/drm/gma500/psb_drv.h b/drivers/gpu/drm/gma500/psb_drv.h index 1bd115e..4ab6ae7 100644 --- a/drivers/gpu/drm/gma500/psb_drv.h +++ b/drivers/gpu/drm/gma500/psb_drv.h @@ -28,6 +28,7 @@ #include "gma_drm.h" #include "psb_reg.h" #include "psb_intel_drv.h" +#include "intel_bios.h" #include "gtt.h" #include "power.h" #include "opregion.h" @@ -613,6 +614,8 @@ struct drm_psb_private { */ struct backlight_device *backlight_device; struct drm_property *backlight_property; + bool backlight_enabled; + int backlight_level; uint32_t blc_adj1; uint32_t blc_adj2; @@ -640,6 +643,19 @@ struct drm_psb_private { int mdfld_panel_id; bool dplla_96mhz; /* DPLL data from the VBT */ + + struct { + int rate; + int lanes; + int preemphasis; + int vswing; + + bool initialized; + bool support; + int bpp; + struct edp_power_seq pps; + } edp; + uint8_t panel_type; }; @@ -796,6 +812,9 @@ extern int psb_fbdev_init(struct drm_device *dev); /* backlight.c */ int gma_backlight_init(struct drm_device *dev); void gma_backlight_exit(struct drm_device *dev); +void gma_backlight_disable(struct drm_device *dev); +void gma_backlight_enable(struct drm_device *dev); +void gma_backlight_set(struct drm_device *dev, int v); /* oaktrail_crtc.c */ extern const struct drm_crtc_helper_funcs oaktrail_helper_funcs; diff --git a/drivers/gpu/drm/gma500/psb_intel_lvds.c b/drivers/gpu/drm/gma500/psb_intel_lvds.c index 37adc9e..2a4c3a9 100644 --- a/drivers/gpu/drm/gma500/psb_intel_lvds.c +++ b/drivers/gpu/drm/gma500/psb_intel_lvds.c @@ -630,17 +630,8 @@ int psb_intel_lvds_set_property(struct drm_connector *connector, property, value)) goto set_prop_error; - else { -#ifdef CONFIG_BACKLIGHT_CLASS_DEVICE - struct drm_psb_private *devp = - encoder->dev->dev_private; - struct backlight_device *bd = devp->backlight_device; - if (bd) { - bd->props.brightness = value; - backlight_update_status(bd); - } -#endif - } + else + gma_backlight_set(encoder->dev, value); } else if (!strcmp(property->name, "DPMS")) { struct drm_encoder_helper_funcs *hfuncs = encoder->helper_private; diff --git a/drivers/gpu/drm/gma500/psb_intel_reg.h b/drivers/gpu/drm/gma500/psb_intel_reg.h index d3091fc..389e969 100644 --- a/drivers/gpu/drm/gma500/psb_intel_reg.h +++ b/drivers/gpu/drm/gma500/psb_intel_reg.h @@ -173,15 +173,46 @@ #define PP_SEQUENCE_ON (1 << 28) #define PP_SEQUENCE_OFF (2 << 28) #define PP_SEQUENCE_MASK 0x30000000 +#define PP_CYCLE_DELAY_ACTIVE (1 << 27) +#define PP_SEQUENCE_STATE_ON_IDLE (1 << 3) +#define PP_SEQUENCE_STATE_MASK 0x0000000f + #define PP_CONTROL 0x61204 #define POWER_TARGET_ON (1 << 0) - +#define PANEL_UNLOCK_REGS (0xabcd << 16) +#define PANEL_UNLOCK_MASK (0xffff << 16) +#define EDP_FORCE_VDD (1 << 3) +#define EDP_BLC_ENABLE (1 << 2) +#define PANEL_POWER_RESET (1 << 1) +#define PANEL_POWER_OFF (0 << 0) +#define PANEL_POWER_ON (1 << 0) + +/* Poulsbo/Oaktrail */ #define LVDSPP_ON 0x61208 #define LVDSPP_OFF 0x6120c #define PP_CYCLE 0x61210 +/* Cedartrail */ #define PP_ON_DELAYS 0x61208 /* Cedartrail */ +#define PANEL_PORT_SELECT_MASK (3 << 30) +#define PANEL_PORT_SELECT_LVDS (0 << 30) +#define PANEL_PORT_SELECT_EDP (1 << 30) +#define PANEL_POWER_UP_DELAY_MASK (0x1fff0000) +#define PANEL_POWER_UP_DELAY_SHIFT 16 +#define PANEL_LIGHT_ON_DELAY_MASK (0x1fff) +#define PANEL_LIGHT_ON_DELAY_SHIFT 0 + #define PP_OFF_DELAYS 0x6120c /* Cedartrail */ +#define PANEL_POWER_DOWN_DELAY_MASK (0x1fff0000) +#define PANEL_POWER_DOWN_DELAY_SHIFT 16 +#define PANEL_LIGHT_OFF_DELAY_MASK (0x1fff) +#define PANEL_LIGHT_OFF_DELAY_SHIFT 0 + +#define PP_DIVISOR 0x61210 /* Cedartrail */ +#define PP_REFERENCE_DIVIDER_MASK (0xffffff00) +#define PP_REFERENCE_DIVIDER_SHIFT 8 +#define PANEL_POWER_CYCLE_DELAY_MASK (0x1f) +#define PANEL_POWER_CYCLE_DELAY_SHIFT 0 #define PFIT_CONTROL 0x61230 #define PFIT_ENABLE (1 << 31) @@ -1503,4 +1534,9 @@ No status bits are changed. #define PIPE_DP_LINK_M(pipe) _PIPE(pipe, _PIPEA_DP_LINK_M, _PIPEB_DP_LINK_M) #define PIPE_DP_LINK_N(pipe) _PIPE(pipe, _PIPEA_DP_LINK_N, _PIPEB_DP_LINK_N) +#define PIPE_BPC_MASK (7 << 5) +#define PIPE_8BPC (0 << 5) +#define PIPE_10BPC (1 << 5) +#define PIPE_6BPC (2 << 5) + #endif -- cgit v0.10.2 From 9a9f5786fcccda3cc61eaa8f537690327eff6853 Mon Sep 17 00:00:00 2001 From: Zhao Yakui Date: Wed, 8 Aug 2012 13:57:01 +0000 Subject: gma500: Disable the clock gating of display controller to make DP/eDP work well I don't know why the DP/eDP is affected by the clock gating. But the test shows that it really fixes the DP/eDP clock issue during enabling DP/eDP. Signed-off-by: Zhao Yakui [Updated to only apply the workaround if the device has DP. We don't want to do this on netbooks] Signed-off-by: Alan Cox Signed-off-by: Dave Airlie diff --git a/drivers/gpu/drm/gma500/cdv_intel_dp.c b/drivers/gpu/drm/gma500/cdv_intel_dp.c index 0571ef9b..9bacce3 100644 --- a/drivers/gpu/drm/gma500/cdv_intel_dp.c +++ b/drivers/gpu/drm/gma500/cdv_intel_dp.c @@ -1778,6 +1778,28 @@ static bool cdv_intel_dpc_is_edp(struct drm_device *dev) return false; } +/* Cedarview display clock gating + + We need this disable dot get correct behaviour while enabling + DP/eDP. TODO - investigate if we can turn it back to normality + after enabling */ +static void cdv_disable_intel_clock_gating(struct drm_device *dev) +{ + u32 reg_value; + reg_value = REG_READ(DSPCLK_GATE_D); + + reg_value |= (DPUNIT_PIPEB_GATE_DISABLE | + DPUNIT_PIPEA_GATE_DISABLE | + DPCUNIT_CLOCK_GATE_DISABLE | + DPLSUNIT_CLOCK_GATE_DISABLE | + DPOUNIT_CLOCK_GATE_DISABLE | + DPIOUNIT_CLOCK_GATE_DISABLE); + + REG_WRITE(DSPCLK_GATE_D, reg_value); + + udelay(500); +} + void cdv_intel_dp_init(struct drm_device *dev, struct psb_intel_mode_device *mode_dev, int output_reg) { @@ -1841,6 +1863,8 @@ cdv_intel_dp_init(struct drm_device *dev, struct psb_intel_mode_device *mode_dev break; } + cdv_disable_intel_clock_gating(dev); + cdv_intel_dp_i2c_init(psb_intel_connector, psb_intel_encoder, name); /* FIXME:fail check */ cdv_intel_dp_add_properties(connector); diff --git a/drivers/gpu/drm/gma500/psb_intel_reg.h b/drivers/gpu/drm/gma500/psb_intel_reg.h index 389e969..d914719 100644 --- a/drivers/gpu/drm/gma500/psb_intel_reg.h +++ b/drivers/gpu/drm/gma500/psb_intel_reg.h @@ -1313,6 +1313,10 @@ No status bits are changed. # define VRHUNIT_CLOCK_GATE_DISABLE (1 << 28) /* Fixed value on CDV */ # define DPOUNIT_CLOCK_GATE_DISABLE (1 << 11) # define DPIOUNIT_CLOCK_GATE_DISABLE (1 << 6) +# define DPUNIT_PIPEB_GATE_DISABLE (1 << 30) +# define DPUNIT_PIPEA_GATE_DISABLE (1 << 25) +# define DPCUNIT_CLOCK_GATE_DISABLE (1 << 24) +# define DPLSUNIT_CLOCK_GATE_DISABLE (1 << 13) #define RAMCLK_GATE_D 0x6210 -- cgit v0.10.2 From 4d46259f00c6dd04d53e74cdfa8f8b6ec35c0140 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Tue, 24 Jul 2012 16:47:34 +0100 Subject: drm: gma500: Kill the GEM glue layer The private gem_create_mmap_offset() function is now implemented in the DRM core as drm_gem_create_mmap_offset(). Use it and kill the private copy. Signed-off-by: Laurent Pinchart Signed-off-by: Alan Cox Signed-off-by: Dave Airlie diff --git a/drivers/gpu/drm/gma500/Makefile b/drivers/gpu/drm/gma500/Makefile index 56a0f2a..7a2d40a 100644 --- a/drivers/gpu/drm/gma500/Makefile +++ b/drivers/gpu/drm/gma500/Makefile @@ -3,7 +3,7 @@ # ccflags-y += -I$(srctree)/include/drm -gma500_gfx-y += gem_glue.o \ +gma500_gfx-y += \ accel_2d.o \ backlight.o \ framebuffer.o \ diff --git a/drivers/gpu/drm/gma500/gem.c b/drivers/gpu/drm/gma500/gem.c index fc7d144..df20546 100644 --- a/drivers/gpu/drm/gma500/gem.c +++ b/drivers/gpu/drm/gma500/gem.c @@ -36,7 +36,12 @@ int psb_gem_init_object(struct drm_gem_object *obj) void psb_gem_free_object(struct drm_gem_object *obj) { struct gtt_range *gtt = container_of(obj, struct gtt_range, gem); - drm_gem_object_release_wrap(obj); + + /* Remove the list map if one is present */ + if (obj->map_list.map) + drm_gem_free_mmap_offset(obj); + drm_gem_object_release(obj); + /* This must occur last as it frees up the memory of the GEM object */ psb_gtt_free_range(obj->dev, gtt); } @@ -77,7 +82,7 @@ int psb_gem_dumb_map_gtt(struct drm_file *file, struct drm_device *dev, /* Make it mmapable */ if (!obj->map_list.map) { - ret = gem_create_mmap_offset(obj); + ret = drm_gem_create_mmap_offset(obj); if (ret) goto out; } diff --git a/drivers/gpu/drm/gma500/gem_glue.c b/drivers/gpu/drm/gma500/gem_glue.c deleted file mode 100644 index 3c17634..0000000 --- a/drivers/gpu/drm/gma500/gem_glue.c +++ /dev/null @@ -1,90 +0,0 @@ -/************************************************************************** - * Copyright (c) 2011, Intel Corporation. - * All Rights Reserved. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. - * - **************************************************************************/ - -#include -#include -#include "gem_glue.h" - -void drm_gem_object_release_wrap(struct drm_gem_object *obj) -{ - /* Remove the list map if one is present */ - if (obj->map_list.map) { - struct drm_gem_mm *mm = obj->dev->mm_private; - struct drm_map_list *list = &obj->map_list; - drm_ht_remove_item(&mm->offset_hash, &list->hash); - drm_mm_put_block(list->file_offset_node); - kfree(list->map); - list->map = NULL; - } - drm_gem_object_release(obj); -} - -/** - * gem_create_mmap_offset - invent an mmap offset - * @obj: our object - * - * Standard implementation of offset generation for mmap as is - * duplicated in several drivers. This belongs in GEM. - */ -int gem_create_mmap_offset(struct drm_gem_object *obj) -{ - struct drm_device *dev = obj->dev; - struct drm_gem_mm *mm = dev->mm_private; - struct drm_map_list *list; - struct drm_local_map *map; - int ret; - - list = &obj->map_list; - list->map = kzalloc(sizeof(struct drm_map_list), GFP_KERNEL); - if (list->map == NULL) - return -ENOMEM; - map = list->map; - map->type = _DRM_GEM; - map->size = obj->size; - map->handle = obj; - - list->file_offset_node = drm_mm_search_free(&mm->offset_manager, - obj->size / PAGE_SIZE, 0, 0); - if (!list->file_offset_node) { - dev_err(dev->dev, "failed to allocate offset for bo %d\n", - obj->name); - ret = -ENOSPC; - goto free_it; - } - list->file_offset_node = drm_mm_get_block(list->file_offset_node, - obj->size / PAGE_SIZE, 0); - if (!list->file_offset_node) { - ret = -ENOMEM; - goto free_it; - } - list->hash.key = list->file_offset_node->start; - ret = drm_ht_insert_item(&mm->offset_hash, &list->hash); - if (ret) { - dev_err(dev->dev, "failed to add to map hash\n"); - goto free_mm; - } - return 0; - -free_mm: - drm_mm_put_block(list->file_offset_node); -free_it: - kfree(list->map); - list->map = NULL; - return ret; -} diff --git a/drivers/gpu/drm/gma500/gem_glue.h b/drivers/gpu/drm/gma500/gem_glue.h deleted file mode 100644 index ce5ce30..0000000 --- a/drivers/gpu/drm/gma500/gem_glue.h +++ /dev/null @@ -1,2 +0,0 @@ -extern void drm_gem_object_release_wrap(struct drm_gem_object *obj); -extern int gem_create_mmap_offset(struct drm_gem_object *obj); diff --git a/drivers/gpu/drm/gma500/psb_drv.h b/drivers/gpu/drm/gma500/psb_drv.h index 4ab6ae7..223ff5b 100644 --- a/drivers/gpu/drm/gma500/psb_drv.h +++ b/drivers/gpu/drm/gma500/psb_drv.h @@ -24,7 +24,6 @@ #include #include "drm_global.h" -#include "gem_glue.h" #include "gma_drm.h" #include "psb_reg.h" #include "psb_intel_drv.h" -- cgit v0.10.2 From f76c0dde78fbe021801fe904fb5999f3154d4e5d Mon Sep 17 00:00:00 2001 From: Alan Cox Date: Wed, 22 Aug 2012 12:00:28 +0000 Subject: gma500/cdv: Fix call to cdv_intel_dp_set_m_n We should be making this call not praying that the values are right. In addition as noted by Josiah Standing we should be calling this for eDP as well. Signed-off-by: Alan Cox Signed-off-by: Dave Airlie diff --git a/drivers/gpu/drm/gma500/cdv_intel_display.c b/drivers/gpu/drm/gma500/cdv_intel_display.c index bfb0565..55db356 100644 --- a/drivers/gpu/drm/gma500/cdv_intel_display.c +++ b/drivers/gpu/drm/gma500/cdv_intel_display.c @@ -1127,8 +1127,8 @@ static int cdv_intel_crtc_mode_set(struct drm_crtc *crtc, } /* dpll |= PLL_REF_INPUT_DREFCLK; */ - if (is_dp) { -/*FIXME cdv_intel_dp_set_m_n(crtc, mode, adjusted_mode); */ + if (is_dp || is_edp) { + cdv_intel_dp_set_m_n(crtc, mode, adjusted_mode); } else { REG_WRITE(PIPE_GMCH_DATA_M(pipe), 0); REG_WRITE(PIPE_GMCH_DATA_N(pipe), 0); -- cgit v0.10.2 From a8e2fad3e96a142a5a395cc64d8bf193d88a7cf0 Mon Sep 17 00:00:00 2001 From: Maarten Lankhorst Date: Mon, 20 Aug 2012 13:54:21 +0000 Subject: drm/ast: remove unused validate_sequence Signed-off-by: Maarten Lankhorst Signed-off-by: Dave Airlie diff --git a/drivers/gpu/drm/ast/ast_drv.h b/drivers/gpu/drm/ast/ast_drv.h index d4af9ed..20a437f 100644 --- a/drivers/gpu/drm/ast/ast_drv.h +++ b/drivers/gpu/drm/ast/ast_drv.h @@ -94,7 +94,6 @@ struct ast_private { struct drm_global_reference mem_global_ref; struct ttm_bo_global_ref bo_global_ref; struct ttm_bo_device bdev; - atomic_t validate_sequence; } ttm; struct drm_gem_object *cursor_cache; -- cgit v0.10.2 From 5d77f13c1e70c252591b06a43e553371781010b4 Mon Sep 17 00:00:00 2001 From: Maarten Lankhorst Date: Mon, 20 Aug 2012 13:54:28 +0000 Subject: drm/cirrus: Remove unused validate_sequence Signed-off-by: Maarten Lankhorst Signed-off-by: Dave Airlie diff --git a/drivers/gpu/drm/cirrus/cirrus_drv.h b/drivers/gpu/drm/cirrus/cirrus_drv.h index 64ea597..5d04564 100644 --- a/drivers/gpu/drm/cirrus/cirrus_drv.h +++ b/drivers/gpu/drm/cirrus/cirrus_drv.h @@ -143,7 +143,6 @@ struct cirrus_device { struct drm_global_reference mem_global_ref; struct ttm_bo_global_ref bo_global_ref; struct ttm_bo_device bdev; - atomic_t validate_sequence; } ttm; bool mm_inited; }; -- cgit v0.10.2 From 7d406b1cc0419dcfb00ffb46957f27fe809bbb74 Mon Sep 17 00:00:00 2001 From: Maarten Lankhorst Date: Mon, 20 Aug 2012 13:54:34 +0000 Subject: drm/mgag200: remove unused validate_sequence Signed-off-by: Maarten Lankhorst Signed-off-by: Dave Airlie diff --git a/drivers/gpu/drm/mgag200/mgag200_drv.h b/drivers/gpu/drm/mgag200/mgag200_drv.h index 6f13b35..d22cbbf 100644 --- a/drivers/gpu/drm/mgag200/mgag200_drv.h +++ b/drivers/gpu/drm/mgag200/mgag200_drv.h @@ -195,7 +195,6 @@ struct mga_device { struct drm_global_reference mem_global_ref; struct ttm_bo_global_ref bo_global_ref; struct ttm_bo_device bdev; - atomic_t validate_sequence; } ttm; u32 reg_1e24; /* SE model number */ -- cgit v0.10.2 From bcd7235cead59ae4b98324c668c90b291beefcb1 Mon Sep 17 00:00:00 2001 From: Jani Nikula Date: Wed, 15 Aug 2012 09:32:36 +0000 Subject: drm/i915: fix EDID memory leak in SDVO The EDID returned by drm_get_edid() was never freed. Signed-off-by: Jani Nikula Signed-off-by: Dave Airlie diff --git a/drivers/gpu/drm/i915/intel_sdvo.c b/drivers/gpu/drm/i915/intel_sdvo.c index f3762f8..5fb425b 100644 --- a/drivers/gpu/drm/i915/intel_sdvo.c +++ b/drivers/gpu/drm/i915/intel_sdvo.c @@ -1692,6 +1692,7 @@ static bool intel_sdvo_detect_hdmi_audio(struct drm_connector *connector) edid = intel_sdvo_get_edid(connector); if (edid != NULL && edid->input & DRM_EDID_INPUT_DIGITAL) has_audio = drm_detect_monitor_audio(edid); + kfree(edid); return has_audio; } -- cgit v0.10.2 From 993dcb05e47e357ade19c41f60b39085eeba8787 Mon Sep 17 00:00:00 2001 From: Jani Nikula Date: Wed, 15 Aug 2012 09:32:37 +0000 Subject: drm/ast: fix EDID memory leak The EDID returned by drm_get_edid() was never freed. Signed-off-by: Jani Nikula Signed-off-by: Dave Airlie diff --git a/drivers/gpu/drm/ast/ast_mode.c b/drivers/gpu/drm/ast/ast_mode.c index 7282c08..866e9d4 100644 --- a/drivers/gpu/drm/ast/ast_mode.c +++ b/drivers/gpu/drm/ast/ast_mode.c @@ -737,6 +737,7 @@ static int ast_get_modes(struct drm_connector *connector) if (edid) { drm_mode_connector_update_edid_property(&ast_connector->base, edid); ret = drm_add_edid_modes(connector, edid); + kfree(edid); return ret; } else drm_mode_connector_update_edid_property(&ast_connector->base, NULL); -- cgit v0.10.2 From 451023dc32d4542c21b52ad1692e6e01cb75b099 Mon Sep 17 00:00:00 2001 From: Jani Nikula Date: Wed, 15 Aug 2012 09:32:39 +0000 Subject: drm: remove the raw_edid field from struct drm_display_info Neither the drm core nor any of the drivers really need the raw_edid field of struct drm_display_info for anything. Instead of being useful, it creates confusion about who is responsible for freeing the memory it points to and setting the field to NULL afterwards, leading to memory leaks and dangling pointers. Remove the raw_edid field, and fix drivers as necessary. Reported-by: Russell King Signed-off-by: Jani Nikula Acked-by: Inki Dae Signed-off-by: Dave Airlie diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c index a8743c3..bcc4725 100644 --- a/drivers/gpu/drm/drm_edid.c +++ b/drivers/gpu/drm/drm_edid.c @@ -399,10 +399,7 @@ struct edid *drm_get_edid(struct drm_connector *connector, if (drm_probe_ddc(adapter)) edid = (struct edid *)drm_do_get_edid(connector, adapter); - connector->display_info.raw_edid = (char *)edid; - return edid; - } EXPORT_SYMBOL(drm_get_edid); diff --git a/drivers/gpu/drm/drm_edid_load.c b/drivers/gpu/drm/drm_edid_load.c index 0303935..186832e 100644 --- a/drivers/gpu/drm/drm_edid_load.c +++ b/drivers/gpu/drm/drm_edid_load.c @@ -114,8 +114,8 @@ static u8 generic_edid[GENERIC_EDIDS][128] = { }, }; -static int edid_load(struct drm_connector *connector, char *name, - char *connector_name) +static u8 *edid_load(struct drm_connector *connector, char *name, + char *connector_name) { const struct firmware *fw; struct platform_device *pdev; @@ -205,7 +205,6 @@ static int edid_load(struct drm_connector *connector, char *name, edid = new_edid; } - connector->display_info.raw_edid = edid; DRM_INFO("Got %s EDID base block and %d extension%s from " "\"%s\" for connector \"%s\"\n", builtin ? "built-in" : "external", valid_extensions, valid_extensions == 1 ? "" : "s", @@ -215,7 +214,10 @@ relfw_out: release_firmware(fw); out: - return err; + if (err) + return ERR_PTR(err); + + return edid; } int drm_load_edid_firmware(struct drm_connector *connector) @@ -223,6 +225,7 @@ int drm_load_edid_firmware(struct drm_connector *connector) char *connector_name = drm_get_connector_name(connector); char *edidname = edid_firmware, *last, *colon; int ret; + struct edid *edid; if (*edidname == '\0') return 0; @@ -240,13 +243,13 @@ int drm_load_edid_firmware(struct drm_connector *connector) if (*last == '\n') *last = '\0'; - ret = edid_load(connector, edidname, connector_name); - if (ret) + edid = (struct edid *) edid_load(connector, edidname, connector_name); + if (IS_ERR_OR_NULL(edid)) return 0; - drm_mode_connector_update_edid_property(connector, - (struct edid *) connector->display_info.raw_edid); + drm_mode_connector_update_edid_property(connector, edid); + ret = drm_add_edid_modes(connector, edid); + kfree(edid); - return drm_add_edid_modes(connector, (struct edid *) - connector->display_info.raw_edid); + return ret; } diff --git a/drivers/gpu/drm/exynos/exynos_drm_connector.c b/drivers/gpu/drm/exynos/exynos_drm_connector.c index d956819..9dce3b9 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_connector.c +++ b/drivers/gpu/drm/exynos/exynos_drm_connector.c @@ -147,9 +147,7 @@ static int exynos_drm_connector_get_modes(struct drm_connector *connector) drm_mode_connector_update_edid_property(connector, edid); count = drm_add_edid_modes(connector, edid); - - kfree(connector->display_info.raw_edid); - connector->display_info.raw_edid = edid; + kfree(edid); } else { struct drm_display_mode *mode = drm_mode_create(connector->dev); struct exynos_drm_panel_info *panel; diff --git a/drivers/gpu/drm/exynos/exynos_drm_vidi.c b/drivers/gpu/drm/exynos/exynos_drm_vidi.c index bb1550c..9239525 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_vidi.c +++ b/drivers/gpu/drm/exynos/exynos_drm_vidi.c @@ -102,7 +102,6 @@ static int vidi_get_edid(struct device *dev, struct drm_connector *connector, u8 *edid, int len) { struct vidi_context *ctx = get_vidi_context(dev); - struct edid *raw_edid; DRM_DEBUG_KMS("%s\n", __FILE__); @@ -115,18 +114,6 @@ static int vidi_get_edid(struct device *dev, struct drm_connector *connector, return -EFAULT; } - raw_edid = kzalloc(len, GFP_KERNEL); - if (!raw_edid) { - DRM_DEBUG_KMS("failed to allocate raw_edid.\n"); - return -ENOMEM; - } - - memcpy(raw_edid, ctx->raw_edid, min((1 + ctx->raw_edid->extensions) - * EDID_LENGTH, len)); - - /* attach the edid data to connector. */ - connector->display_info.raw_edid = (char *)raw_edid; - memcpy(edid, ctx->raw_edid, min((1 + ctx->raw_edid->extensions) * EDID_LENGTH, len)); diff --git a/drivers/gpu/drm/gma500/cdv_intel_hdmi.c b/drivers/gpu/drm/gma500/cdv_intel_hdmi.c index 0c90f03..b1b77bb 100644 --- a/drivers/gpu/drm/gma500/cdv_intel_hdmi.c +++ b/drivers/gpu/drm/gma500/cdv_intel_hdmi.c @@ -157,8 +157,6 @@ static enum drm_connector_status cdv_hdmi_detect( hdmi_priv->has_hdmi_audio = drm_detect_monitor_audio(edid); } - - psb_intel_connector->base.display_info.raw_edid = NULL; kfree(edid); } return status; diff --git a/drivers/gpu/drm/gma500/oaktrail_hdmi.c b/drivers/gpu/drm/gma500/oaktrail_hdmi.c index 2eb3dc4..69e51e9 100644 --- a/drivers/gpu/drm/gma500/oaktrail_hdmi.c +++ b/drivers/gpu/drm/gma500/oaktrail_hdmi.c @@ -252,7 +252,6 @@ static int oaktrail_hdmi_get_modes(struct drm_connector *connector) if (edid) { drm_mode_connector_update_edid_property(connector, edid); ret = drm_add_edid_modes(connector, edid); - connector->display_info.raw_edid = NULL; } /* diff --git a/drivers/gpu/drm/gma500/psb_intel_sdvo.c b/drivers/gpu/drm/gma500/psb_intel_sdvo.c index 0466c7b..a453d94 100644 --- a/drivers/gpu/drm/gma500/psb_intel_sdvo.c +++ b/drivers/gpu/drm/gma500/psb_intel_sdvo.c @@ -1343,7 +1343,6 @@ psb_intel_sdvo_hdmi_sink_detect(struct drm_connector *connector) } } else status = connector_status_disconnected; - connector->display_info.raw_edid = NULL; kfree(edid); } @@ -1404,7 +1403,6 @@ psb_intel_sdvo_detect(struct drm_connector *connector, bool force) ret = connector_status_disconnected; else ret = connector_status_connected; - connector->display_info.raw_edid = NULL; kfree(edid); } else ret = connector_status_connected; @@ -1453,7 +1451,6 @@ static void psb_intel_sdvo_get_ddc_modes(struct drm_connector *connector) drm_add_edid_modes(connector, edid); } - connector->display_info.raw_edid = NULL; kfree(edid); } } diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index 977d9d2..d14b1e3 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -2145,7 +2145,6 @@ intel_dp_get_edid_modes(struct drm_connector *connector, struct i2c_adapter *ada ret = drm_add_edid_modes(connector, intel_dp->edid); drm_edid_to_eld(connector, intel_dp->edid); - connector->display_info.raw_edid = NULL; return intel_dp->edid_mode_count; } @@ -2191,7 +2190,6 @@ intel_dp_detect(struct drm_connector *connector, bool force) edid = intel_dp_get_edid(connector, &intel_dp->adapter); if (edid) { intel_dp->has_audio = drm_detect_monitor_audio(edid); - connector->display_info.raw_edid = NULL; kfree(edid); } } @@ -2256,8 +2254,6 @@ intel_dp_detect_audio(struct drm_connector *connector) edid = intel_dp_get_edid(connector, &intel_dp->adapter); if (edid) { has_audio = drm_detect_monitor_audio(edid); - - connector->display_info.raw_edid = NULL; kfree(edid); } diff --git a/drivers/gpu/drm/i915/intel_hdmi.c b/drivers/gpu/drm/i915/intel_hdmi.c index e4c37bb..35a6ee7 100644 --- a/drivers/gpu/drm/i915/intel_hdmi.c +++ b/drivers/gpu/drm/i915/intel_hdmi.c @@ -737,7 +737,6 @@ intel_hdmi_detect(struct drm_connector *connector, bool force) drm_detect_hdmi_monitor(edid); intel_hdmi->has_audio = drm_detect_monitor_audio(edid); } - connector->display_info.raw_edid = NULL; kfree(edid); } @@ -778,8 +777,6 @@ intel_hdmi_detect_audio(struct drm_connector *connector) if (edid) { if (edid->input & DRM_EDID_INPUT_DIGITAL) has_audio = drm_detect_monitor_audio(edid); - - connector->display_info.raw_edid = NULL; kfree(edid); } diff --git a/drivers/gpu/drm/i915/intel_modes.c b/drivers/gpu/drm/i915/intel_modes.c index 45848b9..7a5238f 100644 --- a/drivers/gpu/drm/i915/intel_modes.c +++ b/drivers/gpu/drm/i915/intel_modes.c @@ -50,7 +50,6 @@ int intel_ddc_get_modes(struct drm_connector *connector, drm_mode_connector_update_edid_property(connector, edid); ret = drm_add_edid_modes(connector, edid); drm_edid_to_eld(connector, edid); - connector->display_info.raw_edid = NULL; kfree(edid); } diff --git a/drivers/gpu/drm/i915/intel_sdvo.c b/drivers/gpu/drm/i915/intel_sdvo.c index 5fb425b..434b1d1 100644 --- a/drivers/gpu/drm/i915/intel_sdvo.c +++ b/drivers/gpu/drm/i915/intel_sdvo.c @@ -1345,7 +1345,6 @@ intel_sdvo_tmds_sink_detect(struct drm_connector *connector) } } else status = connector_status_disconnected; - connector->display_info.raw_edid = NULL; kfree(edid); } @@ -1419,7 +1418,6 @@ intel_sdvo_detect(struct drm_connector *connector, bool force) else ret = connector_status_disconnected; - connector->display_info.raw_edid = NULL; kfree(edid); } else ret = connector_status_connected; @@ -1465,7 +1463,6 @@ static void intel_sdvo_get_ddc_modes(struct drm_connector *connector) drm_add_edid_modes(connector, edid); } - connector->display_info.raw_edid = NULL; kfree(edid); } } diff --git a/drivers/gpu/drm/mgag200/mgag200_mode.c b/drivers/gpu/drm/mgag200/mgag200_mode.c index b69642d..c7420e8 100644 --- a/drivers/gpu/drm/mgag200/mgag200_mode.c +++ b/drivers/gpu/drm/mgag200/mgag200_mode.c @@ -1399,7 +1399,6 @@ static int mga_vga_get_modes(struct drm_connector *connector) if (edid) { drm_mode_connector_update_edid_property(connector, edid); ret = drm_add_edid_modes(connector, edid); - connector->display_info.raw_edid = NULL; kfree(edid); } return ret; diff --git a/drivers/gpu/drm/udl/udl_connector.c b/drivers/gpu/drm/udl/udl_connector.c index ba055e9..2d98ff9 100644 --- a/drivers/gpu/drm/udl/udl_connector.c +++ b/drivers/gpu/drm/udl/udl_connector.c @@ -57,11 +57,8 @@ static int udl_get_modes(struct drm_connector *connector) edid = (struct edid *)udl_get_edid(udl); - connector->display_info.raw_edid = (char *)edid; - drm_mode_connector_update_edid_property(connector, edid); ret = drm_add_edid_modes(connector, edid); - connector->display_info.raw_edid = NULL; kfree(edid); return ret; } diff --git a/drivers/staging/omapdrm/omap_connector.c b/drivers/staging/omapdrm/omap_connector.c index 5e2856c..9c2287b 100644 --- a/drivers/staging/omapdrm/omap_connector.c +++ b/drivers/staging/omapdrm/omap_connector.c @@ -177,14 +177,11 @@ static int omap_connector_get_modes(struct drm_connector *connector) drm_mode_connector_update_edid_property( connector, edid); n = drm_add_edid_modes(connector, edid); - kfree(connector->display_info.raw_edid); - connector->display_info.raw_edid = edid; } else { drm_mode_connector_update_edid_property( connector, NULL); - connector->display_info.raw_edid = NULL; - kfree(edid); } + kfree(edid); } else { struct drm_display_mode *mode = drm_mode_create(dev); struct omap_video_timings timings; diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h index a1a0386..0522044 100644 --- a/include/drm/drm_crtc.h +++ b/include/drm/drm_crtc.h @@ -216,8 +216,6 @@ struct drm_display_info { u32 color_formats; u8 cea_rev; - - char *raw_edid; /* if any */ }; struct drm_framebuffer_funcs { -- cgit v0.10.2 From 04cf55e1fa5ff0c83756b803a47cb6bd0b39f38b Mon Sep 17 00:00:00 2001 From: Huacai Chen Date: Sat, 11 Aug 2012 09:32:17 +0000 Subject: drm: Handle io prot correctly for MIPS. Signed-off-by: Huacai Chen Signed-off-by: Hongliang Tao Signed-off-by: Hua Yan Cc: dri-devel@lists.freedesktop.org Signed-off-by: Dave Airlie diff --git a/drivers/gpu/drm/drm_vm.c b/drivers/gpu/drm/drm_vm.c index 961ee08..3f06166 100644 --- a/drivers/gpu/drm/drm_vm.c +++ b/drivers/gpu/drm/drm_vm.c @@ -62,7 +62,7 @@ static pgprot_t drm_io_prot(uint32_t map_type, struct vm_area_struct *vma) tmp = pgprot_writecombine(tmp); else tmp = pgprot_noncached(tmp); -#elif defined(__sparc__) || defined(__arm__) +#elif defined(__sparc__) || defined(__arm__) || defined(__mips__) tmp = pgprot_noncached(tmp); #endif return tmp; diff --git a/drivers/gpu/drm/ttm/ttm_bo_util.c b/drivers/gpu/drm/ttm/ttm_bo_util.c index f8187ea..0df71ea 100644 --- a/drivers/gpu/drm/ttm/ttm_bo_util.c +++ b/drivers/gpu/drm/ttm/ttm_bo_util.c @@ -472,7 +472,7 @@ pgprot_t ttm_io_prot(uint32_t caching_flags, pgprot_t tmp) else tmp = pgprot_noncached(tmp); #endif -#if defined(__sparc__) +#if defined(__sparc__) || defined(__mips__) if (!(caching_flags & TTM_PL_FLAG_CACHED)) tmp = pgprot_noncached(tmp); #endif -- cgit v0.10.2 From fc7b8127dcfd9c4c373eb5295b2da492cec40a8e Mon Sep 17 00:00:00 2001 From: Huacai Chen Date: Sat, 11 Aug 2012 09:32:18 +0000 Subject: drm: Define SAREA_MAX for Loongson (PageSize = 16KB). Signed-off-by: Huacai Chen Signed-off-by: Hongliang Tao Signed-off-by: Hua Yan Cc: dri-devel@lists.freedesktop.org Signed-off-by: Dave Airlie diff --git a/include/drm/drm_sarea.h b/include/drm/drm_sarea.h index ee5389d..1d1a858 100644 --- a/include/drm/drm_sarea.h +++ b/include/drm/drm_sarea.h @@ -37,6 +37,8 @@ /* SAREA area needs to be at least a page */ #if defined(__alpha__) #define SAREA_MAX 0x2000U +#elif defined(__mips__) +#define SAREA_MAX 0x4000U #elif defined(__ia64__) #define SAREA_MAX 0x10000U /* 64kB */ #else -- cgit v0.10.2 From 28bbda39b69af06e77801333c0c28ef9df2f256f Mon Sep 17 00:00:00 2001 From: Forest Bond Date: Mon, 13 Aug 2012 16:27:09 +0000 Subject: gma500: Fix comment mispelling in cdv_intel_limits definition. Signed-off-by: Forest Bond Signed-off-by: Alan Cox Signed-off-by: Dave Airlie diff --git a/drivers/gpu/drm/gma500/cdv_intel_display.c b/drivers/gpu/drm/gma500/cdv_intel_display.c index 55db356..65cb248 100644 --- a/drivers/gpu/drm/gma500/cdv_intel_display.c +++ b/drivers/gpu/drm/gma500/cdv_intel_display.c @@ -76,7 +76,7 @@ static bool cdv_intel_find_dp_pll(const struct cdv_intel_limit_t *limit, struct #define CDV_LIMIT_DP_100 5 static const struct cdv_intel_limit_t cdv_intel_limits[] = { - { /* CDV_SIGNLE_LVDS_96MHz */ + { /* CDV_SINGLE_LVDS_96MHz */ .dot = {.min = 20000, .max = 115500}, .vco = {.min = 1800000, .max = 3600000}, .n = {.min = 2, .max = 6}, -- cgit v0.10.2 From f835bc0fcacc7f4e29890938b1c2faecd07eda9e Mon Sep 17 00:00:00 2001 From: Forest Bond Date: Mon, 13 Aug 2012 16:29:04 +0000 Subject: gma500: psb_intel_crtc: Drop crtc_enable flag. This is set when setting DPMS on and off, but it isn't checked anywhere, so just remove it. Signed-off-by: Forest Bond Signed-off-by: Alan Cox Signed-off-by: Dave Airlie diff --git a/drivers/gpu/drm/gma500/cdv_intel_display.c b/drivers/gpu/drm/gma500/cdv_intel_display.c index 65cb248..3cfd093 100644 --- a/drivers/gpu/drm/gma500/cdv_intel_display.c +++ b/drivers/gpu/drm/gma500/cdv_intel_display.c @@ -930,7 +930,6 @@ static void cdv_intel_crtc_dpms(struct drm_crtc *crtc, int mode) /* Give the overlay scaler a chance to enable * if it's on this pipe */ /* psb_intel_crtc_dpms_video(crtc, true); TODO */ - psb_intel_crtc->crtc_enable = true; break; case DRM_MODE_DPMS_OFF: if (!psb_intel_crtc->active) @@ -981,7 +980,6 @@ static void cdv_intel_crtc_dpms(struct drm_crtc *crtc, int mode) /* Wait for the clocks to turn off. */ udelay(150); - psb_intel_crtc->crtc_enable = false; break; } cdv_intel_update_watermark(dev, crtc); diff --git a/drivers/gpu/drm/gma500/psb_intel_drv.h b/drivers/gpu/drm/gma500/psb_intel_drv.h index c53e592..c9710ff 100644 --- a/drivers/gpu/drm/gma500/psb_intel_drv.h +++ b/drivers/gpu/drm/gma500/psb_intel_drv.h @@ -193,7 +193,6 @@ struct psb_intel_crtc { u32 mode_flags; bool active; - bool crtc_enable; /* Saved Crtc HW states */ struct psb_intel_crtc_state *crtc_state; -- cgit v0.10.2 From 32285038f533f5bb4ba4c75de7ade710a836b639 Mon Sep 17 00:00:00 2001 From: Jani Nikula Date: Wed, 15 Aug 2012 09:34:43 +0000 Subject: gma500: remove an unreachable return statement Signed-off-by: Jani Nikula Signed-off-by: Dave Airlie diff --git a/drivers/gpu/drm/gma500/psb_intel_sdvo.c b/drivers/gpu/drm/gma500/psb_intel_sdvo.c index a453d94..d35f93b 100644 --- a/drivers/gpu/drm/gma500/psb_intel_sdvo.c +++ b/drivers/gpu/drm/gma500/psb_intel_sdvo.c @@ -1292,7 +1292,6 @@ psb_intel_sdvo_get_analog_edid(struct drm_connector *connector) return drm_get_edid(connector, &dev_priv->gmbus[dev_priv->crt_ddc_pin].adapter); - return NULL; } static enum drm_connector_status -- cgit v0.10.2 From df0b344300724e00db9fff7eb6406eb91f450b91 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Fri, 10 Aug 2012 05:30:25 +0000 Subject: drm/usb: select USB_SUPPORT in Kconfig DRM_USB selects USB. However, USB depends on USB_SUPPORT and USB_ARCH_HAS_HCD. Thus, selecting USB_SUPPORT in Kconfig avoids the following warning (detected when DisplayLink was selected using exynos4_defconfig): warning: (MOUSE_APPLETOUCH && MOUSE_BCM5974 && MOUSE_SYNAPTICS_USB && JOYSTICK_XPAD && TABLET_USB_ACECAD && TABLET_USB_AIPTEK && TABLET_USB_HANWANG && TABLET_USB_KBTAB && TABLET_USB_WACOM && TOUCHSCREEN_USB_COMPOSITE && INPUT_ATI_REMOTE2 && INPUT_KEYSPAN_REMOTE && INPUT_POWERMATE && INPUT_YEALINK && INPUT_CM109 && RC_ATI_REMOTE && IR_IMON && IR_MCEUSB && IR_REDRAT3 && IR_STREAMZAP && IR_IGUANA && DRM_USB) selects USB which has unmet direct dependencies (USB_SUPPORT && USB_ARCH_HAS_HCD) Signed-off-by: Sachin Kamat Signed-off-by: Dave Airlie diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig index 23120c0..45536db 100644 --- a/drivers/gpu/drm/Kconfig +++ b/drivers/gpu/drm/Kconfig @@ -23,6 +23,7 @@ config DRM_USB tristate depends on DRM select USB + select USB_SUPPORT config DRM_KMS_HELPER tristate -- cgit v0.10.2 From 0227a7fdc2a4756aa0e0d1152ce5eada016cf18d Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Tue, 7 Aug 2012 11:14:14 +0000 Subject: drm/i2c/ch7006: Convert to dev_pm_ops The I2C specific suspend and resume functions have been deprecated and printing a warning on boot for over a year, dev_pm_ops should be used instead so convert to that. Also remove the suspend function since all it does is log. Signed-off-by: Mark Brown Acked-by: Francisco Jerez Signed-off-by: Dave Airlie diff --git a/drivers/gpu/drm/i2c/ch7006_drv.c b/drivers/gpu/drm/i2c/ch7006_drv.c index 36d9522..599099f 100644 --- a/drivers/gpu/drm/i2c/ch7006_drv.c +++ b/drivers/gpu/drm/i2c/ch7006_drv.c @@ -427,15 +427,10 @@ static int ch7006_remove(struct i2c_client *client) return 0; } -static int ch7006_suspend(struct i2c_client *client, pm_message_t mesg) +static int ch7006_resume(struct device *dev) { - ch7006_dbg(client, "\n"); - - return 0; -} + struct i2c_client *client = to_i2c_client(dev); -static int ch7006_resume(struct i2c_client *client) -{ ch7006_dbg(client, "\n"); ch7006_write(client, 0x3d, 0x0); @@ -499,15 +494,18 @@ static struct i2c_device_id ch7006_ids[] = { }; MODULE_DEVICE_TABLE(i2c, ch7006_ids); +static const struct dev_pm_ops ch7006_pm_ops = { + .resume = ch7006_resume, +}; + static struct drm_i2c_encoder_driver ch7006_driver = { .i2c_driver = { .probe = ch7006_probe, .remove = ch7006_remove, - .suspend = ch7006_suspend, - .resume = ch7006_resume, .driver = { .name = "ch7006", + .pm = &ch7006_pm_ops, }, .id_table = ch7006_ids, -- cgit v0.10.2 From ea7f7abcbb3b72bdc7927d043b3e9dc6058bd75a Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Wed, 1 Aug 2012 17:15:31 +0530 Subject: drm: Add missing static storage class specifier in drm_irq.c file Fixes the following sparse warning: drivers/gpu/drm/drm_irq.c:1239:6: warning: symbol 'drm_handle_vblank_events' was not declared. Should it be static? Signed-off-by: Sachin Kamat Signed-off-by: Dave Airlie diff --git a/drivers/gpu/drm/drm_irq.c b/drivers/gpu/drm/drm_irq.c index 03f16f3..076c4a8 100644 --- a/drivers/gpu/drm/drm_irq.c +++ b/drivers/gpu/drm/drm_irq.c @@ -1236,7 +1236,7 @@ done: return ret; } -void drm_handle_vblank_events(struct drm_device *dev, int crtc) +static void drm_handle_vblank_events(struct drm_device *dev, int crtc) { struct drm_pending_vblank_event *e, *t; struct timeval now; -- cgit v0.10.2 From 78b9c3537e9c88ea3be2224343d2d08d4d2d4e4d Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Wed, 1 Aug 2012 17:15:32 +0530 Subject: drm: Add misssing static storage class specifier in drm_fb_helper.c file Fixes the following sparse warning: drivers/gpu/drm/drm_fb_helper.c:239:6: warning: symbol 'drm_fb_helper_force_kernel_mode' was not declared. Should it be static? Signed-off-by: Sachin Kamat Signed-off-by: Dave Airlie diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c index f546d1e..061d26d 100644 --- a/drivers/gpu/drm/drm_fb_helper.c +++ b/drivers/gpu/drm/drm_fb_helper.c @@ -236,7 +236,7 @@ bool drm_fb_helper_restore_fbdev_mode(struct drm_fb_helper *fb_helper) } EXPORT_SYMBOL(drm_fb_helper_restore_fbdev_mode); -bool drm_fb_helper_force_kernel_mode(void) +static bool drm_fb_helper_force_kernel_mode(void) { bool ret, error = false; struct drm_fb_helper *helper; -- cgit v0.10.2 From 86a1ee26bb60e1ab8984e92f0e9186c354670aed Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Sat, 11 Aug 2012 15:41:04 +0100 Subject: drm/i915: Only pwrite through the GTT if there is space in the aperture Avoid stalling and waiting for the GPU by checking to see if there is sufficient inactive space in the aperture for us to bind the buffer prior to writing through the GTT. If there is inadequate space we will have to stall waiting for the GPU, and incur overheads moving objects about. Instead, only incur the clflush overhead on the target object by writing through shmem. Signed-off-by: Chris Wilson Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index cbd3cd0..06a88be 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -1307,7 +1307,8 @@ struct drm_i915_gem_object *i915_gem_alloc_object(struct drm_device *dev, void i915_gem_free_object(struct drm_gem_object *obj); int __must_check i915_gem_object_pin(struct drm_i915_gem_object *obj, uint32_t alignment, - bool map_and_fenceable); + bool map_and_fenceable, + bool nonblocking); void i915_gem_object_unpin(struct drm_i915_gem_object *obj); int __must_check i915_gem_object_unbind(struct drm_i915_gem_object *obj); void i915_gem_release_mmap(struct drm_i915_gem_object *obj); @@ -1454,7 +1455,8 @@ void i915_gem_init_global_gtt(struct drm_device *dev, int __must_check i915_gem_evict_something(struct drm_device *dev, int min_size, unsigned alignment, unsigned cache_level, - bool mappable); + bool mappable, + bool nonblock); int i915_gem_evict_everything(struct drm_device *dev); /* i915_gem_stolen.c */ diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index e1ec587..1ec5f7e 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -41,7 +41,8 @@ static void i915_gem_object_flush_gtt_write_domain(struct drm_i915_gem_object *o static void i915_gem_object_flush_cpu_write_domain(struct drm_i915_gem_object *obj); static __must_check int i915_gem_object_bind_to_gtt(struct drm_i915_gem_object *obj, unsigned alignment, - bool map_and_fenceable); + bool map_and_fenceable, + bool nonblocking); static int i915_gem_phys_pwrite(struct drm_device *dev, struct drm_i915_gem_object *obj, struct drm_i915_gem_pwrite *args, @@ -609,7 +610,7 @@ i915_gem_gtt_pwrite_fast(struct drm_device *dev, char __user *user_data; int page_offset, page_length, ret; - ret = i915_gem_object_pin(obj, 0, true); + ret = i915_gem_object_pin(obj, 0, true, true); if (ret) goto out; @@ -925,10 +926,8 @@ i915_gem_pwrite_ioctl(struct drm_device *dev, void *data, goto out; } - if (obj->gtt_space && - obj->cache_level == I915_CACHE_NONE && + if (obj->cache_level == I915_CACHE_NONE && obj->tiling_mode == I915_TILING_NONE && - obj->map_and_fenceable && obj->base.write_domain != I915_GEM_DOMAIN_CPU) { ret = i915_gem_gtt_pwrite_fast(dev, obj, args, file); /* Note that the gtt paths might fail with non-page-backed user @@ -936,7 +935,7 @@ i915_gem_pwrite_ioctl(struct drm_device *dev, void *data, * textures). Fallback to the shmem path in that case. */ } - if (ret == -EFAULT) + if (ret == -EFAULT || ret == -ENOSPC) ret = i915_gem_shmem_pwrite(dev, obj, args, file); out: @@ -1115,7 +1114,7 @@ int i915_gem_fault(struct vm_area_struct *vma, struct vm_fault *vmf) goto unlock; } if (!obj->gtt_space) { - ret = i915_gem_object_bind_to_gtt(obj, 0, true); + ret = i915_gem_object_bind_to_gtt(obj, 0, true, false); if (ret) goto unlock; @@ -2772,7 +2771,8 @@ static void i915_gem_verify_gtt(struct drm_device *dev) static int i915_gem_object_bind_to_gtt(struct drm_i915_gem_object *obj, unsigned alignment, - bool map_and_fenceable) + bool map_and_fenceable, + bool nonblocking) { struct drm_device *dev = obj->base.dev; drm_i915_private_t *dev_priv = dev->dev_private; @@ -2848,7 +2848,8 @@ i915_gem_object_bind_to_gtt(struct drm_i915_gem_object *obj, if (obj->gtt_space == NULL) { ret = i915_gem_evict_something(dev, size, alignment, obj->cache_level, - map_and_fenceable); + map_and_fenceable, + nonblocking); if (ret) return ret; @@ -3188,7 +3189,7 @@ i915_gem_object_pin_to_display_plane(struct drm_i915_gem_object *obj, * (e.g. libkms for the bootup splash), we have to ensure that we * always use map_and_fenceable for all scanout buffers. */ - ret = i915_gem_object_pin(obj, alignment, true); + ret = i915_gem_object_pin(obj, alignment, true, false); if (ret) return ret; @@ -3325,7 +3326,8 @@ i915_gem_ring_throttle(struct drm_device *dev, struct drm_file *file) int i915_gem_object_pin(struct drm_i915_gem_object *obj, uint32_t alignment, - bool map_and_fenceable) + bool map_and_fenceable, + bool nonblocking) { int ret; @@ -3349,7 +3351,8 @@ i915_gem_object_pin(struct drm_i915_gem_object *obj, if (obj->gtt_space == NULL) { ret = i915_gem_object_bind_to_gtt(obj, alignment, - map_and_fenceable); + map_and_fenceable, + nonblocking); if (ret) return ret; } @@ -3407,7 +3410,7 @@ i915_gem_pin_ioctl(struct drm_device *dev, void *data, obj->user_pin_count++; obj->pin_filp = file; if (obj->user_pin_count == 1) { - ret = i915_gem_object_pin(obj, args->alignment, true); + ret = i915_gem_object_pin(obj, args->alignment, true, false); if (ret) goto out; } diff --git a/drivers/gpu/drm/i915/i915_gem_context.c b/drivers/gpu/drm/i915/i915_gem_context.c index 5c2d354..4aa7ecf 100644 --- a/drivers/gpu/drm/i915/i915_gem_context.c +++ b/drivers/gpu/drm/i915/i915_gem_context.c @@ -221,7 +221,7 @@ static int create_default_context(struct drm_i915_private *dev_priv) * default context. */ dev_priv->ring[RCS].default_context = ctx; - ret = i915_gem_object_pin(ctx->obj, CONTEXT_ALIGN, false); + ret = i915_gem_object_pin(ctx->obj, CONTEXT_ALIGN, false, false); if (ret) goto err_destroy; @@ -374,7 +374,7 @@ static int do_switch(struct i915_hw_context *to) if (from_obj == to->obj) return 0; - ret = i915_gem_object_pin(to->obj, CONTEXT_ALIGN, false); + ret = i915_gem_object_pin(to->obj, CONTEXT_ALIGN, false, false); if (ret) return ret; diff --git a/drivers/gpu/drm/i915/i915_gem_evict.c b/drivers/gpu/drm/i915/i915_gem_evict.c index 74635da..a2d8acd 100644 --- a/drivers/gpu/drm/i915/i915_gem_evict.c +++ b/drivers/gpu/drm/i915/i915_gem_evict.c @@ -45,7 +45,7 @@ mark_free(struct drm_i915_gem_object *obj, struct list_head *unwind) int i915_gem_evict_something(struct drm_device *dev, int min_size, unsigned alignment, unsigned cache_level, - bool mappable) + bool mappable, bool nonblocking) { drm_i915_private_t *dev_priv = dev->dev_private; struct list_head eviction_list, unwind_list; @@ -92,12 +92,16 @@ i915_gem_evict_something(struct drm_device *dev, int min_size, goto found; } + if (nonblocking) + goto none; + /* Now merge in the soon-to-be-expired objects... */ list_for_each_entry(obj, &dev_priv->mm.active_list, mm_list) { if (mark_free(obj, &unwind_list)) goto found; } +none: /* Nothing found, clean up and bail out! */ while (!list_empty(&unwind_list)) { obj = list_first_entry(&unwind_list, diff --git a/drivers/gpu/drm/i915/i915_gem_execbuffer.c b/drivers/gpu/drm/i915/i915_gem_execbuffer.c index 834a636..f7346d8 100644 --- a/drivers/gpu/drm/i915/i915_gem_execbuffer.c +++ b/drivers/gpu/drm/i915/i915_gem_execbuffer.c @@ -354,7 +354,7 @@ pin_and_fence_object(struct drm_i915_gem_object *obj, obj->tiling_mode != I915_TILING_NONE; need_mappable = need_fence || need_reloc_mappable(obj); - ret = i915_gem_object_pin(obj, entry->alignment, need_mappable); + ret = i915_gem_object_pin(obj, entry->alignment, need_mappable, false); if (ret) return ret; diff --git a/drivers/gpu/drm/i915/intel_overlay.c b/drivers/gpu/drm/i915/intel_overlay.c index c0f4858..afd0f30 100644 --- a/drivers/gpu/drm/i915/intel_overlay.c +++ b/drivers/gpu/drm/i915/intel_overlay.c @@ -1383,7 +1383,7 @@ void intel_setup_overlay(struct drm_device *dev) } overlay->flip_addr = reg_bo->phys_obj->handle->busaddr; } else { - ret = i915_gem_object_pin(reg_bo, PAGE_SIZE, true); + ret = i915_gem_object_pin(reg_bo, PAGE_SIZE, true, false); if (ret) { DRM_ERROR("failed to pin overlay register bo\n"); goto out_free_bo; diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c index c0407aa..c0721ff 100644 --- a/drivers/gpu/drm/i915/intel_pm.c +++ b/drivers/gpu/drm/i915/intel_pm.c @@ -2138,7 +2138,7 @@ intel_alloc_context_page(struct drm_device *dev) return NULL; } - ret = i915_gem_object_pin(ctx, 4096, true); + ret = i915_gem_object_pin(ctx, 4096, true, false); if (ret) { DRM_ERROR("failed to pin power context: %d\n", ret); goto err_unref; diff --git a/drivers/gpu/drm/i915/intel_ringbuffer.c b/drivers/gpu/drm/i915/intel_ringbuffer.c index c828169..ac93643 100644 --- a/drivers/gpu/drm/i915/intel_ringbuffer.c +++ b/drivers/gpu/drm/i915/intel_ringbuffer.c @@ -391,7 +391,7 @@ init_pipe_control(struct intel_ring_buffer *ring) i915_gem_object_set_cache_level(obj, I915_CACHE_LLC); - ret = i915_gem_object_pin(obj, 4096, true); + ret = i915_gem_object_pin(obj, 4096, true, false); if (ret) goto err_unref; @@ -979,7 +979,7 @@ static int init_status_page(struct intel_ring_buffer *ring) i915_gem_object_set_cache_level(obj, I915_CACHE_LLC); - ret = i915_gem_object_pin(obj, 4096, true); + ret = i915_gem_object_pin(obj, 4096, true, false); if (ret != 0) { goto err_unref; } @@ -1036,7 +1036,7 @@ static int intel_init_ring_buffer(struct drm_device *dev, ring->obj = obj; - ret = i915_gem_object_pin(obj, PAGE_SIZE, true); + ret = i915_gem_object_pin(obj, PAGE_SIZE, true, false); if (ret) goto err_unref; -- cgit v0.10.2 From 4d6294bf77834276d41b51154a18663b0a8f2063 Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Sat, 11 Aug 2012 15:41:05 +0100 Subject: drm/i915: Protect private gem objects from truncate (such as imported dmabuf) If the object has no backing shmemfs filp, then we obviously cannot perform a truncation operation upon it. Signed-off-by: Chris Wilson Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index 1ec5f7e..eb54a6d 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -1385,6 +1385,11 @@ i915_gem_object_truncate(struct drm_i915_gem_object *obj) { struct inode *inode; + i915_gem_object_free_mmap_offset(obj); + + if (obj->base.filp == NULL) + return; + /* Our goal here is to return as much of the memory as * is possible back to the system as we are called from OOM. * To do this we must instruct the shmfs to drop all of its @@ -1393,8 +1398,6 @@ i915_gem_object_truncate(struct drm_i915_gem_object *obj) inode = obj->base.filp->f_path.dentry->d_inode; shmem_truncate_range(inode, 0, (loff_t)-1); - i915_gem_object_free_mmap_offset(obj); - obj->madv = __I915_MADV_PURGED; } -- cgit v0.10.2 From 0327d6ba998ca181013a5a1709701a6532a41972 Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Sat, 11 Aug 2012 15:41:06 +0100 Subject: drm/i915: Extract general object init routine As we wish to create specialised object constructions in the near future that share the same basic GEM object struct, export the default initializer. Signed-off-by: Chris Wilson Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 06a88be..9f4c27e 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -1302,6 +1302,7 @@ int i915_gem_wait_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv); void i915_gem_load(struct drm_device *dev); int i915_gem_init_object(struct drm_gem_object *obj); +void i915_gem_object_init(struct drm_i915_gem_object *obj); struct drm_i915_gem_object *i915_gem_alloc_object(struct drm_device *dev, size_t size); void i915_gem_free_object(struct drm_gem_object *obj); diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index eb54a6d..5531758 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -3558,10 +3558,26 @@ unlock: return ret; } +void i915_gem_object_init(struct drm_i915_gem_object *obj) +{ + obj->base.driver_private = NULL; + + INIT_LIST_HEAD(&obj->mm_list); + INIT_LIST_HEAD(&obj->gtt_list); + INIT_LIST_HEAD(&obj->ring_list); + INIT_LIST_HEAD(&obj->exec_list); + + obj->fence_reg = I915_FENCE_REG_NONE; + obj->madv = I915_MADV_WILLNEED; + /* Avoid an unnecessary call to unbind on the first bind. */ + obj->map_and_fenceable = true; + + i915_gem_info_add_obj(obj->base.dev->dev_private, obj->base.size); +} + struct drm_i915_gem_object *i915_gem_alloc_object(struct drm_device *dev, size_t size) { - struct drm_i915_private *dev_priv = dev->dev_private; struct drm_i915_gem_object *obj; struct address_space *mapping; u32 mask; @@ -3585,7 +3601,7 @@ struct drm_i915_gem_object *i915_gem_alloc_object(struct drm_device *dev, mapping = obj->base.filp->f_path.dentry->d_inode->i_mapping; mapping_set_gfp_mask(mapping, mask); - i915_gem_info_add_obj(dev_priv, size); + i915_gem_object_init(obj); obj->base.write_domain = I915_GEM_DOMAIN_CPU; obj->base.read_domains = I915_GEM_DOMAIN_CPU; @@ -3607,16 +3623,6 @@ struct drm_i915_gem_object *i915_gem_alloc_object(struct drm_device *dev, } else obj->cache_level = I915_CACHE_NONE; - obj->base.driver_private = NULL; - obj->fence_reg = I915_FENCE_REG_NONE; - INIT_LIST_HEAD(&obj->mm_list); - INIT_LIST_HEAD(&obj->gtt_list); - INIT_LIST_HEAD(&obj->ring_list); - INIT_LIST_HEAD(&obj->exec_list); - obj->madv = I915_MADV_WILLNEED; - /* Avoid an unnecessary call to unbind on the first bind. */ - obj->map_and_fenceable = true; - return obj; } -- cgit v0.10.2 From 504c7267a1e84b157cbd7e9c1b805e1bc0c2c846 Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Thu, 23 Aug 2012 13:12:52 +0100 Subject: drm/i915: Use cpu relocations if the object is in the GTT but not mappable This prevents the case of unbinding the object in order to process the relocations through the GTT and then rebinding it only to then proceed to use cpu relocations as the object is now in the CPU write domain. By choosing to use cpu relocations up front, we can therefore avoid the rebind penalty. Signed-off-by: Chris Wilson Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/i915_gem_execbuffer.c b/drivers/gpu/drm/i915/i915_gem_execbuffer.c index f7346d8..dc87563 100644 --- a/drivers/gpu/drm/i915/i915_gem_execbuffer.c +++ b/drivers/gpu/drm/i915/i915_gem_execbuffer.c @@ -95,6 +95,7 @@ eb_destroy(struct eb_objects *eb) static inline int use_cpu_reloc(struct drm_i915_gem_object *obj) { return (obj->base.write_domain == I915_GEM_DOMAIN_CPU || + !obj->map_and_fenceable || obj->cache_level != I915_CACHE_NONE); } -- cgit v0.10.2 From ce5fdd63752390b89804b1e246de267b398d1515 Mon Sep 17 00:00:00 2001 From: Dave Airlie Date: Fri, 24 Aug 2012 13:53:49 +1000 Subject: gma500: fix incorrect declaration. this header was incorrect, caused a build failure. Signed-off-by: Dave Airlie diff --git a/drivers/gpu/drm/gma500/psb_intel_drv.h b/drivers/gpu/drm/gma500/psb_intel_drv.h index c9710ff..90f2d11 100644 --- a/drivers/gpu/drm/gma500/psb_intel_drv.h +++ b/drivers/gpu/drm/gma500/psb_intel_drv.h @@ -289,7 +289,7 @@ extern void gma_intel_teardown_gmbus(struct drm_device *dev); /* DP support */ extern void cdv_intel_dp_init(struct drm_device *dev, struct psb_intel_mode_device *mode_dev, int output_reg); -extern void psb_intel_dp_set_m_n(struct drm_crtc *crtc, +extern void cdv_intel_dp_set_m_n(struct drm_crtc *crtc, struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode); -- cgit v0.10.2 From f64c48f1d39cbe9e14dd25104633b486b1226606 Mon Sep 17 00:00:00 2001 From: Jani Nikula Date: Fri, 24 Aug 2012 09:42:44 +0300 Subject: gma500: remove references to drm_display_info raw_edid field Another reference to raw_edid field of struct drm_display_info was added in gma500 while the whole field was being removed, causing build failure. Remove the hopefully last references to raw_edid. Reported-by: Fengguang Wu Signed-off-by: Jani Nikula Signed-off-by: Dave Airlie diff --git a/drivers/gpu/drm/gma500/cdv_intel_dp.c b/drivers/gpu/drm/gma500/cdv_intel_dp.c index 9bacce3..c9abc06 100644 --- a/drivers/gpu/drm/gma500/cdv_intel_dp.c +++ b/drivers/gpu/drm/gma500/cdv_intel_dp.c @@ -1554,7 +1554,6 @@ cdv_intel_dp_detect(struct drm_connector *connector, bool force) edid = drm_get_edid(connector, &intel_dp->adapter); if (edid) { intel_dp->has_audio = drm_detect_monitor_audio(edid); - connector->display_info.raw_edid = NULL; kfree(edid); } } @@ -1634,8 +1633,6 @@ cdv_intel_dp_detect_audio(struct drm_connector *connector) edid = drm_get_edid(connector, &intel_dp->adapter); if (edid) { has_audio = drm_detect_monitor_audio(edid); - - connector->display_info.raw_edid = NULL; kfree(edid); } if (edp) -- cgit v0.10.2 From 6f314ebbaa2667d67a7206ba78f28e46cf47eda5 Mon Sep 17 00:00:00 2001 From: Alan Cox Date: Fri, 13 Jul 2012 15:27:29 +0100 Subject: gma500: Fix frequency detection If we have a 266MHz part we set core_freq to 0 in several spots Signed-off-by: Alan Cox Signed-off-by: Dave Airlie diff --git a/drivers/gpu/drm/gma500/cdv_device.c b/drivers/gpu/drm/gma500/cdv_device.c index 4882e64..bfc2f39 100644 --- a/drivers/gpu/drm/gma500/cdv_device.c +++ b/drivers/gpu/drm/gma500/cdv_device.c @@ -457,6 +457,7 @@ static void cdv_get_core_freq(struct drm_device *dev) case 6: case 7: dev_priv->core_freq = 266; + break; default: dev_priv->core_freq = 0; } diff --git a/drivers/gpu/drm/gma500/psb_device.c b/drivers/gpu/drm/gma500/psb_device.c index 5971bc8..f1432f0 100644 --- a/drivers/gpu/drm/gma500/psb_device.c +++ b/drivers/gpu/drm/gma500/psb_device.c @@ -290,6 +290,7 @@ static void psb_get_core_freq(struct drm_device *dev) case 6: case 7: dev_priv->core_freq = 266; + break; default: dev_priv->core_freq = 0; } -- cgit v0.10.2 From b361237bcc7cea1d99f770490120d8bc2aed7777 Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Fri, 24 Aug 2012 09:35:08 +0100 Subject: drm/i915: Juggle code order to ease flow of the next patch Move the wait-for-rendering logic around in the file so that we can group it together with the subsequent variations. The general goal is to have the lower level routines clustered together and then the higher level logic building upon those low level routines that came before. Signed-off-by: Chris Wilson Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index 5531758..0b236ea 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -945,6 +945,194 @@ unlock: return ret; } +int +i915_gem_check_wedge(struct drm_i915_private *dev_priv, + bool interruptible) +{ + if (atomic_read(&dev_priv->mm.wedged)) { + struct completion *x = &dev_priv->error_completion; + bool recovery_complete; + unsigned long flags; + + /* Give the error handler a chance to run. */ + spin_lock_irqsave(&x->wait.lock, flags); + recovery_complete = x->done > 0; + spin_unlock_irqrestore(&x->wait.lock, flags); + + /* Non-interruptible callers can't handle -EAGAIN, hence return + * -EIO unconditionally for these. */ + if (!interruptible) + return -EIO; + + /* Recovery complete, but still wedged means reset failure. */ + if (recovery_complete) + return -EIO; + + return -EAGAIN; + } + + return 0; +} + +/* + * Compare seqno against outstanding lazy request. Emit a request if they are + * equal. + */ +static int +i915_gem_check_olr(struct intel_ring_buffer *ring, u32 seqno) +{ + int ret; + + BUG_ON(!mutex_is_locked(&ring->dev->struct_mutex)); + + ret = 0; + if (seqno == ring->outstanding_lazy_request) + ret = i915_add_request(ring, NULL, NULL); + + return ret; +} + +/** + * __wait_seqno - wait until execution of seqno has finished + * @ring: the ring expected to report seqno + * @seqno: duh! + * @interruptible: do an interruptible wait (normally yes) + * @timeout: in - how long to wait (NULL forever); out - how much time remaining + * + * Returns 0 if the seqno was found within the alloted time. Else returns the + * errno with remaining time filled in timeout argument. + */ +static int __wait_seqno(struct intel_ring_buffer *ring, u32 seqno, + bool interruptible, struct timespec *timeout) +{ + drm_i915_private_t *dev_priv = ring->dev->dev_private; + struct timespec before, now, wait_time={1,0}; + unsigned long timeout_jiffies; + long end; + bool wait_forever = true; + int ret; + + if (i915_seqno_passed(ring->get_seqno(ring, true), seqno)) + return 0; + + trace_i915_gem_request_wait_begin(ring, seqno); + + if (timeout != NULL) { + wait_time = *timeout; + wait_forever = false; + } + + timeout_jiffies = timespec_to_jiffies(&wait_time); + + if (WARN_ON(!ring->irq_get(ring))) + return -ENODEV; + + /* Record current time in case interrupted by signal, or wedged * */ + getrawmonotonic(&before); + +#define EXIT_COND \ + (i915_seqno_passed(ring->get_seqno(ring, false), seqno) || \ + atomic_read(&dev_priv->mm.wedged)) + do { + if (interruptible) + end = wait_event_interruptible_timeout(ring->irq_queue, + EXIT_COND, + timeout_jiffies); + else + end = wait_event_timeout(ring->irq_queue, EXIT_COND, + timeout_jiffies); + + ret = i915_gem_check_wedge(dev_priv, interruptible); + if (ret) + end = ret; + } while (end == 0 && wait_forever); + + getrawmonotonic(&now); + + ring->irq_put(ring); + trace_i915_gem_request_wait_end(ring, seqno); +#undef EXIT_COND + + if (timeout) { + struct timespec sleep_time = timespec_sub(now, before); + *timeout = timespec_sub(*timeout, sleep_time); + } + + switch (end) { + case -EIO: + case -EAGAIN: /* Wedged */ + case -ERESTARTSYS: /* Signal */ + return (int)end; + case 0: /* Timeout */ + if (timeout) + set_normalized_timespec(timeout, 0, 0); + return -ETIME; + default: /* Completed */ + WARN_ON(end < 0); /* We're not aware of other errors */ + return 0; + } +} + +/** + * Waits for a sequence number to be signaled, and cleans up the + * request and object lists appropriately for that event. + */ +int +i915_wait_seqno(struct intel_ring_buffer *ring, uint32_t seqno) +{ + struct drm_device *dev = ring->dev; + struct drm_i915_private *dev_priv = dev->dev_private; + bool interruptible = dev_priv->mm.interruptible; + int ret; + + BUG_ON(!mutex_is_locked(&dev->struct_mutex)); + BUG_ON(seqno == 0); + + ret = i915_gem_check_wedge(dev_priv, interruptible); + if (ret) + return ret; + + ret = i915_gem_check_olr(ring, seqno); + if (ret) + return ret; + + return __wait_seqno(ring, seqno, interruptible, NULL); +} + +/** + * Ensures that all rendering to the object has completed and the object is + * safe to unbind from the GTT or access from the CPU. + */ +static __must_check int +i915_gem_object_wait_rendering(struct drm_i915_gem_object *obj, + bool readonly) +{ + struct intel_ring_buffer *ring = obj->ring; + u32 seqno; + int ret; + + seqno = readonly ? obj->last_write_seqno : obj->last_read_seqno; + if (seqno == 0) + return 0; + + ret = i915_wait_seqno(ring, seqno); + if (ret) + return ret; + + i915_gem_retire_requests_ring(ring); + + /* Manually manage the write flush as we may have not yet + * retired the buffer. + */ + if (obj->last_write_seqno && + i915_seqno_passed(seqno, obj->last_write_seqno)) { + obj->last_write_seqno = 0; + obj->base.write_domain &= ~I915_GEM_GPU_DOMAINS; + } + + return 0; +} + /** * Called when user space prepares to use an object with the CPU, either * through the mmap ioctl's mapping or a GTT mapping. @@ -1952,197 +2140,6 @@ i915_gem_retire_work_handler(struct work_struct *work) mutex_unlock(&dev->struct_mutex); } -int -i915_gem_check_wedge(struct drm_i915_private *dev_priv, - bool interruptible) -{ - if (atomic_read(&dev_priv->mm.wedged)) { - struct completion *x = &dev_priv->error_completion; - bool recovery_complete; - unsigned long flags; - - /* Give the error handler a chance to run. */ - spin_lock_irqsave(&x->wait.lock, flags); - recovery_complete = x->done > 0; - spin_unlock_irqrestore(&x->wait.lock, flags); - - /* Non-interruptible callers can't handle -EAGAIN, hence return - * -EIO unconditionally for these. */ - if (!interruptible) - return -EIO; - - /* Recovery complete, but still wedged means reset failure. */ - if (recovery_complete) - return -EIO; - - return -EAGAIN; - } - - return 0; -} - -/* - * Compare seqno against outstanding lazy request. Emit a request if they are - * equal. - */ -static int -i915_gem_check_olr(struct intel_ring_buffer *ring, u32 seqno) -{ - int ret; - - BUG_ON(!mutex_is_locked(&ring->dev->struct_mutex)); - - ret = 0; - if (seqno == ring->outstanding_lazy_request) - ret = i915_add_request(ring, NULL, NULL); - - return ret; -} - -/** - * __wait_seqno - wait until execution of seqno has finished - * @ring: the ring expected to report seqno - * @seqno: duh! - * @interruptible: do an interruptible wait (normally yes) - * @timeout: in - how long to wait (NULL forever); out - how much time remaining - * - * Returns 0 if the seqno was found within the alloted time. Else returns the - * errno with remaining time filled in timeout argument. - */ -static int __wait_seqno(struct intel_ring_buffer *ring, u32 seqno, - bool interruptible, struct timespec *timeout) -{ - drm_i915_private_t *dev_priv = ring->dev->dev_private; - struct timespec before, now, wait_time={1,0}; - unsigned long timeout_jiffies; - long end; - bool wait_forever = true; - int ret; - - if (i915_seqno_passed(ring->get_seqno(ring, true), seqno)) - return 0; - - trace_i915_gem_request_wait_begin(ring, seqno); - - if (timeout != NULL) { - wait_time = *timeout; - wait_forever = false; - } - - timeout_jiffies = timespec_to_jiffies(&wait_time); - - if (WARN_ON(!ring->irq_get(ring))) - return -ENODEV; - - /* Record current time in case interrupted by signal, or wedged * */ - getrawmonotonic(&before); - -#define EXIT_COND \ - (i915_seqno_passed(ring->get_seqno(ring, false), seqno) || \ - atomic_read(&dev_priv->mm.wedged)) - do { - if (interruptible) - end = wait_event_interruptible_timeout(ring->irq_queue, - EXIT_COND, - timeout_jiffies); - else - end = wait_event_timeout(ring->irq_queue, EXIT_COND, - timeout_jiffies); - - ret = i915_gem_check_wedge(dev_priv, interruptible); - if (ret) - end = ret; - } while (end == 0 && wait_forever); - - getrawmonotonic(&now); - - ring->irq_put(ring); - trace_i915_gem_request_wait_end(ring, seqno); -#undef EXIT_COND - - if (timeout) { - struct timespec sleep_time = timespec_sub(now, before); - *timeout = timespec_sub(*timeout, sleep_time); - } - - switch (end) { - case -EIO: - case -EAGAIN: /* Wedged */ - case -ERESTARTSYS: /* Signal */ - return (int)end; - case 0: /* Timeout */ - if (timeout) - set_normalized_timespec(timeout, 0, 0); - return -ETIME; - default: /* Completed */ - WARN_ON(end < 0); /* We're not aware of other errors */ - return 0; - } -} - -/** - * Waits for a sequence number to be signaled, and cleans up the - * request and object lists appropriately for that event. - */ -int -i915_wait_seqno(struct intel_ring_buffer *ring, uint32_t seqno) -{ - drm_i915_private_t *dev_priv = ring->dev->dev_private; - int ret = 0; - - BUG_ON(seqno == 0); - - ret = i915_gem_check_wedge(dev_priv, dev_priv->mm.interruptible); - if (ret) - return ret; - - ret = i915_gem_check_olr(ring, seqno); - if (ret) - return ret; - - ret = __wait_seqno(ring, seqno, dev_priv->mm.interruptible, NULL); - - return ret; -} - -/** - * Ensures that all rendering to the object has completed and the object is - * safe to unbind from the GTT or access from the CPU. - */ -static __must_check int -i915_gem_object_wait_rendering(struct drm_i915_gem_object *obj, - bool readonly) -{ - u32 seqno; - int ret; - - /* If there is rendering queued on the buffer being evicted, wait for - * it. - */ - if (readonly) - seqno = obj->last_write_seqno; - else - seqno = obj->last_read_seqno; - if (seqno == 0) - return 0; - - ret = i915_wait_seqno(obj->ring, seqno); - if (ret) - return ret; - - /* Manually manage the write flush as we may have not yet retired - * the buffer. - */ - if (obj->last_write_seqno && - i915_seqno_passed(seqno, obj->last_write_seqno)) { - obj->last_write_seqno = 0; - obj->base.write_domain &= ~I915_GEM_GPU_DOMAINS; - } - - i915_gem_retire_requests_ring(obj->ring); - return 0; -} - /** * Ensures that an object will eventually get non-busy by flushing any required * write domains, emitting any outstanding lazy request and retiring and -- cgit v0.10.2 From 3236f57a0162391f84b93f39fc1882c49a8998c7 Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Fri, 24 Aug 2012 09:35:09 +0100 Subject: drm/i915: Use a non-blocking wait for set-to-domain ioctl The principal use for set-to-domain is for userspace to serialise operations with a particular buffer, for example to maintain coherency with a CPU map or to ratelimit its rendering by waiting on all previous operations before continuing. As such we tend to hold the struct_mutex for long periods during the synchronisation and so cause contention issues with other users of the graphics device, even for independent operations as memory management. An example is the contention between compiz and X which causes jitter in the display and a drop in peak throughput. The ultimate solution would be a set of fine grained locks and lockless operations, but an intermediate step is to first attempt the synchronisation for set-to-domain without holding the mutex. This introduces a number of race conditions, so we limit it use to the ioctl periphery where we have no dependent state and can safely complete with a locked synchronisation afterwards. Signed-off-by: Chris Wilson Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index 0b236ea..719a933 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -1133,6 +1133,52 @@ i915_gem_object_wait_rendering(struct drm_i915_gem_object *obj, return 0; } +/* A nonblocking variant of the above wait. This is a highly dangerous routine + * as the object state may change during this call. + */ +static __must_check int +i915_gem_object_wait_rendering__nonblocking(struct drm_i915_gem_object *obj, + bool readonly) +{ + struct drm_device *dev = obj->base.dev; + struct drm_i915_private *dev_priv = dev->dev_private; + struct intel_ring_buffer *ring = obj->ring; + u32 seqno; + int ret; + + BUG_ON(!mutex_is_locked(&dev->struct_mutex)); + BUG_ON(!dev_priv->mm.interruptible); + + seqno = readonly ? obj->last_write_seqno : obj->last_read_seqno; + if (seqno == 0) + return 0; + + ret = i915_gem_check_wedge(dev_priv, true); + if (ret) + return ret; + + ret = i915_gem_check_olr(ring, seqno); + if (ret) + return ret; + + mutex_unlock(&dev->struct_mutex); + ret = __wait_seqno(ring, seqno, true, NULL); + mutex_lock(&dev->struct_mutex); + + i915_gem_retire_requests_ring(ring); + + /* Manually manage the write flush as we may have not yet + * retired the buffer. + */ + if (obj->last_write_seqno && + i915_seqno_passed(seqno, obj->last_write_seqno)) { + obj->last_write_seqno = 0; + obj->base.write_domain &= ~I915_GEM_GPU_DOMAINS; + } + + return ret; +} + /** * Called when user space prepares to use an object with the CPU, either * through the mmap ioctl's mapping or a GTT mapping. @@ -1170,6 +1216,14 @@ i915_gem_set_domain_ioctl(struct drm_device *dev, void *data, goto unlock; } + /* Try to flush the object off the GPU without holding the lock. + * We will repeat the flush holding the lock in the normal manner + * to catch cases where we are gazumped. + */ + ret = i915_gem_object_wait_rendering__nonblocking(obj, !write_domain); + if (ret) + goto unref; + if (read_domains & I915_GEM_DOMAIN_GTT) { ret = i915_gem_object_set_to_gtt_domain(obj, write_domain != 0); @@ -1183,6 +1237,7 @@ i915_gem_set_domain_ioctl(struct drm_device *dev, void *data, ret = i915_gem_object_set_to_cpu_domain(obj, write_domain != 0); } +unref: drm_gem_object_unreference(&obj->base); unlock: mutex_unlock(&dev->struct_mutex); -- cgit v0.10.2 From bd9854f995f56f4bc3f0dc32dc92be02303c6f19 Mon Sep 17 00:00:00 2001 From: Ben Widawsky Date: Thu, 23 Aug 2012 15:18:09 -0700 Subject: drm/i915: Extract reading INSTDONE INSTDONE is used in many places, and it varies from generation to generation. This provides a good reason for us to extract the logic to read the relevant information. The patch has no functional change. It's prep for some new stuff. v2: move the memset inside of i915_get_extra_instdone (Jani) v3,4: bugs caught by (Jani) Reviewed-by: Jani Nikula Signed-off-by: Ben Widawsky Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index 3633029..b7ec34e 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -1070,6 +1070,23 @@ i915_error_first_batchbuffer(struct drm_i915_private *dev_priv, return NULL; } +/* NB: please notice the memset */ +static void i915_get_extra_instdone(struct drm_device *dev, + uint32_t *instdone) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + memset(instdone, 0, sizeof(*instdone) * I915_NUM_INSTDONE_REG); + + if (INTEL_INFO(dev)->gen < 4) { + instdone[0] = I915_READ(INSTDONE); + instdone[1] = 0; + } else { + instdone[0] = I915_READ(INSTDONE_I965); + instdone[1] = I915_READ(INSTDONE1); + } +} + + static void i915_record_ring_state(struct drm_device *dev, struct drm_i915_error_state *error, struct intel_ring_buffer *ring) @@ -1288,6 +1305,7 @@ void i915_destroy_error_state(struct drm_device *dev) static void i915_report_and_clear_eir(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; + uint32_t instdone[I915_NUM_INSTDONE_REG]; u32 eir = I915_READ(EIR); int pipe; @@ -1296,16 +1314,17 @@ static void i915_report_and_clear_eir(struct drm_device *dev) pr_err("render error detected, EIR: 0x%08x\n", eir); + i915_get_extra_instdone(dev, instdone); + if (IS_G4X(dev)) { if (eir & (GM45_ERROR_MEM_PRIV | GM45_ERROR_CP_PRIV)) { u32 ipeir = I915_READ(IPEIR_I965); pr_err(" IPEIR: 0x%08x\n", I915_READ(IPEIR_I965)); pr_err(" IPEHR: 0x%08x\n", I915_READ(IPEHR_I965)); - pr_err(" INSTDONE: 0x%08x\n", - I915_READ(INSTDONE_I965)); + pr_err(" INSTDONE: 0x%08x\n", instdone[0]); pr_err(" INSTPS: 0x%08x\n", I915_READ(INSTPS)); - pr_err(" INSTDONE1: 0x%08x\n", I915_READ(INSTDONE1)); + pr_err(" INSTDONE1: 0x%08x\n", instdone[1]); pr_err(" ACTHD: 0x%08x\n", I915_READ(ACTHD_I965)); I915_WRITE(IPEIR_I965, ipeir); POSTING_READ(IPEIR_I965); @@ -1344,7 +1363,7 @@ static void i915_report_and_clear_eir(struct drm_device *dev) pr_err(" IPEIR: 0x%08x\n", I915_READ(IPEIR)); pr_err(" IPEHR: 0x%08x\n", I915_READ(IPEHR)); - pr_err(" INSTDONE: 0x%08x\n", I915_READ(INSTDONE)); + pr_err(" INSTDONE: 0x%08x\n", instdone[0]); pr_err(" ACTHD: 0x%08x\n", I915_READ(ACTHD)); I915_WRITE(IPEIR, ipeir); POSTING_READ(IPEIR); @@ -1353,10 +1372,9 @@ static void i915_report_and_clear_eir(struct drm_device *dev) pr_err(" IPEIR: 0x%08x\n", I915_READ(IPEIR_I965)); pr_err(" IPEHR: 0x%08x\n", I915_READ(IPEHR_I965)); - pr_err(" INSTDONE: 0x%08x\n", - I915_READ(INSTDONE_I965)); + pr_err(" INSTDONE: 0x%08x\n", instdone[0]); pr_err(" INSTPS: 0x%08x\n", I915_READ(INSTPS)); - pr_err(" INSTDONE1: 0x%08x\n", I915_READ(INSTDONE1)); + pr_err(" INSTDONE1: 0x%08x\n", instdone[1]); pr_err(" ACTHD: 0x%08x\n", I915_READ(ACTHD_I965)); I915_WRITE(IPEIR_I965, ipeir); POSTING_READ(IPEIR_I965); @@ -1671,7 +1689,7 @@ 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[I915_NUM_RINGS], instdone, instdone1; + uint32_t acthd[I915_NUM_RINGS], instdone[I915_NUM_INSTDONE_REG]; struct intel_ring_buffer *ring; bool err = false, idle; int i; @@ -1699,25 +1717,19 @@ void i915_hangcheck_elapsed(unsigned long data) return; } - if (INTEL_INFO(dev)->gen < 4) { - instdone = I915_READ(INSTDONE); - instdone1 = 0; - } else { - instdone = I915_READ(INSTDONE_I965); - instdone1 = I915_READ(INSTDONE1); - } + i915_get_extra_instdone(dev, instdone); if (memcmp(dev_priv->last_acthd, acthd, sizeof(acthd)) == 0 && - dev_priv->last_instdone == instdone && - dev_priv->last_instdone1 == instdone1) { + dev_priv->last_instdone == instdone[0] && + dev_priv->last_instdone1 == instdone[1]) { if (i915_hangcheck_hung(dev)) return; } else { dev_priv->hangcheck_count = 0; memcpy(dev_priv->last_acthd, acthd, sizeof(acthd)); - dev_priv->last_instdone = instdone; - dev_priv->last_instdone1 = instdone1; + dev_priv->last_instdone = instdone[0]; + dev_priv->last_instdone1 = instdone[1]; } repeat: diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h index f27e998..1f97b3f 100644 --- a/drivers/gpu/drm/i915/i915_reg.h +++ b/drivers/gpu/drm/i915/i915_reg.h @@ -478,6 +478,7 @@ #define IPEIR_I965 0x02064 #define IPEHR_I965 0x02068 #define INSTDONE_I965 0x0206c +#define I915_NUM_INSTDONE_REG 2 #define RING_IPEIR(base) ((base)+0x64) #define RING_IPEHR(base) ((base)+0x68) #define RING_INSTDONE(base) ((base)+0x6c) -- cgit v0.10.2 From d53bd48459a554503e07bc3a4b8bba2b57c2ff48 Mon Sep 17 00:00:00 2001 From: Ben Widawsky Date: Wed, 22 Aug 2012 11:32:14 -0700 Subject: drm/i915: Add new INSTDONE registers Signed-off-by: Ben Widawsky Reviewed-by: Jani Nikula Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h index 1f97b3f..d0b60f2f 100644 --- a/drivers/gpu/drm/i915/i915_reg.h +++ b/drivers/gpu/drm/i915/i915_reg.h @@ -478,7 +478,11 @@ #define IPEIR_I965 0x02064 #define IPEHR_I965 0x02068 #define INSTDONE_I965 0x0206c -#define I915_NUM_INSTDONE_REG 2 +#define GEN7_INSTDONE_1 0x0206c +#define GEN7_SC_INSTDONE 0x07100 +#define GEN7_SAMPLER_INSTDONE 0x0e160 +#define GEN7_ROW_INSTDONE 0x0e164 +#define I915_NUM_INSTDONE_REG 4 #define RING_IPEIR(base) ((base)+0x64) #define RING_IPEHR(base) ((base)+0x68) #define RING_INSTDONE(base) ((base)+0x6c) -- cgit v0.10.2 From 050ee91f128bd767b1413383fea6c973aa464c54 Mon Sep 17 00:00:00 2001 From: Ben Widawsky Date: Wed, 22 Aug 2012 11:32:15 -0700 Subject: drm/i915: Use new INSTDONE registers (Gen7+) Using the extracted INSTDONE reading, and our new register definitions, update our hangcheck detection and error collection to use it. This primarily means changing == to memcmp, and changing = to memcpy. Hopefully this will give more info on error dump, and provide more accurate hangcheck detection (both are actually TBD). Also, remove the reading of instdone1 from the ring error collection function, and just crap everything in capture_error_state (that could be split into a separate patch if it wasn't so trivial). v2: Now assuming i915_get_extra_instdone does the memset we can clean up the code a bit (Jani) v3: use ARRAY_SIZE as requested earlier by Jani (didn't change sizeof) Updated commit msg Cc: Jani Nikula Signed-off-by: Ben Widawsky Reviewed-by: Jani Nikula Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/i915_debugfs.c b/drivers/gpu/drm/i915/i915_debugfs.c index d300393..3d886af 100644 --- a/drivers/gpu/drm/i915/i915_debugfs.c +++ b/drivers/gpu/drm/i915/i915_debugfs.c @@ -662,10 +662,9 @@ static void i915_ring_error_state(struct seq_file *m, seq_printf(m, " IPEIR: 0x%08x\n", error->ipeir[ring]); seq_printf(m, " IPEHR: 0x%08x\n", error->ipehr[ring]); seq_printf(m, " INSTDONE: 0x%08x\n", error->instdone[ring]); - if (ring == RCS && INTEL_INFO(dev)->gen >= 4) { - seq_printf(m, " INSTDONE1: 0x%08x\n", error->instdone1); + if (ring == RCS && INTEL_INFO(dev)->gen >= 4) seq_printf(m, " BBADDR: 0x%08llx\n", error->bbaddr); - } + if (INTEL_INFO(dev)->gen >= 4) seq_printf(m, " INSTPS: 0x%08x\n", error->instps[ring]); seq_printf(m, " INSTPM: 0x%08x\n", error->instpm[ring]); @@ -714,6 +713,9 @@ static int i915_error_state(struct seq_file *m, void *unused) for (i = 0; i < dev_priv->num_fence_regs; i++) seq_printf(m, " fence[%d] = %08llx\n", i, error->fence[i]); + for (i = 0; i < ARRAY_SIZE(error->extra_instdone); i++) + seq_printf(m, " INSTDONE_%d: 0x%08x\n", i, error->extra_instdone[i]); + if (INTEL_INFO(dev)->gen >= 6) { seq_printf(m, "ERROR: 0x%08x\n", error->error); seq_printf(m, "DONE_REG: 0x%08x\n", error->done_reg); diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 9f4c27e..58b43db 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -199,7 +199,7 @@ struct drm_i915_error_state { u32 err_int; /* gen7 */ u32 instpm[I915_NUM_RINGS]; u32 instps[I915_NUM_RINGS]; - u32 instdone1; + u32 extra_instdone[I915_NUM_INSTDONE_REG]; u32 seqno[I915_NUM_RINGS]; u64 bbaddr; u32 fault_reg[I915_NUM_RINGS]; @@ -454,8 +454,7 @@ typedef struct drm_i915_private { struct timer_list hangcheck_timer; int hangcheck_count; uint32_t last_acthd[I915_NUM_RINGS]; - uint32_t last_instdone; - uint32_t last_instdone1; + uint32_t prev_instdone[I915_NUM_INSTDONE_REG]; unsigned int stop_rings; diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index b7ec34e..34dc715 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -1077,12 +1077,25 @@ static void i915_get_extra_instdone(struct drm_device *dev, struct drm_i915_private *dev_priv = dev->dev_private; memset(instdone, 0, sizeof(*instdone) * I915_NUM_INSTDONE_REG); - if (INTEL_INFO(dev)->gen < 4) { + switch(INTEL_INFO(dev)->gen) { + case 2: + case 3: instdone[0] = I915_READ(INSTDONE); - instdone[1] = 0; - } else { + break; + case 4: + case 5: + case 6: instdone[0] = I915_READ(INSTDONE_I965); instdone[1] = I915_READ(INSTDONE1); + break; + default: + WARN_ONCE(1, "Unsupported platform\n"); + case 7: + instdone[0] = I915_READ(GEN7_INSTDONE_1); + instdone[1] = I915_READ(GEN7_SC_INSTDONE); + instdone[2] = I915_READ(GEN7_SAMPLER_INSTDONE); + instdone[3] = I915_READ(GEN7_ROW_INSTDONE); + break; } } @@ -1108,10 +1121,8 @@ static void i915_record_ring_state(struct drm_device *dev, error->ipehr[ring->id] = I915_READ(RING_IPEHR(ring->mmio_base)); error->instdone[ring->id] = I915_READ(RING_INSTDONE(ring->mmio_base)); error->instps[ring->id] = I915_READ(RING_INSTPS(ring->mmio_base)); - if (ring->id == RCS) { - error->instdone1 = I915_READ(INSTDONE1); + if (ring->id == RCS) error->bbaddr = I915_READ64(BB_ADDR); - } } else { error->faddr[ring->id] = I915_READ(DMA_FADD_I8XX); error->ipeir[ring->id] = I915_READ(IPEIR); @@ -1230,6 +1241,8 @@ static void i915_capture_error_state(struct drm_device *dev) if (INTEL_INFO(dev)->gen == 7) error->err_int = I915_READ(GEN7_ERR_INT); + i915_get_extra_instdone(dev, error->extra_instdone); + i915_gem_record_fences(dev, error); i915_gem_record_rings(dev, error); @@ -1307,7 +1320,7 @@ static void i915_report_and_clear_eir(struct drm_device *dev) struct drm_i915_private *dev_priv = dev->dev_private; uint32_t instdone[I915_NUM_INSTDONE_REG]; u32 eir = I915_READ(EIR); - int pipe; + int pipe, i; if (!eir) return; @@ -1322,9 +1335,9 @@ static void i915_report_and_clear_eir(struct drm_device *dev) pr_err(" IPEIR: 0x%08x\n", I915_READ(IPEIR_I965)); pr_err(" IPEHR: 0x%08x\n", I915_READ(IPEHR_I965)); - pr_err(" INSTDONE: 0x%08x\n", instdone[0]); + for (i = 0; i < ARRAY_SIZE(instdone); i++) + pr_err(" INSTDONE_%d: 0x%08x\n", i, instdone[i]); pr_err(" INSTPS: 0x%08x\n", I915_READ(INSTPS)); - pr_err(" INSTDONE1: 0x%08x\n", instdone[1]); pr_err(" ACTHD: 0x%08x\n", I915_READ(ACTHD_I965)); I915_WRITE(IPEIR_I965, ipeir); POSTING_READ(IPEIR_I965); @@ -1358,12 +1371,13 @@ static void i915_report_and_clear_eir(struct drm_device *dev) if (eir & I915_ERROR_INSTRUCTION) { pr_err("instruction error\n"); pr_err(" INSTPM: 0x%08x\n", I915_READ(INSTPM)); + for (i = 0; i < ARRAY_SIZE(instdone); i++) + pr_err(" INSTDONE_%d: 0x%08x\n", i, instdone[i]); if (INTEL_INFO(dev)->gen < 4) { u32 ipeir = I915_READ(IPEIR); pr_err(" IPEIR: 0x%08x\n", I915_READ(IPEIR)); pr_err(" IPEHR: 0x%08x\n", I915_READ(IPEHR)); - pr_err(" INSTDONE: 0x%08x\n", instdone[0]); pr_err(" ACTHD: 0x%08x\n", I915_READ(ACTHD)); I915_WRITE(IPEIR, ipeir); POSTING_READ(IPEIR); @@ -1372,9 +1386,7 @@ static void i915_report_and_clear_eir(struct drm_device *dev) pr_err(" IPEIR: 0x%08x\n", I915_READ(IPEIR_I965)); pr_err(" IPEHR: 0x%08x\n", I915_READ(IPEHR_I965)); - pr_err(" INSTDONE: 0x%08x\n", instdone[0]); pr_err(" INSTPS: 0x%08x\n", I915_READ(INSTPS)); - pr_err(" INSTDONE1: 0x%08x\n", instdone[1]); pr_err(" ACTHD: 0x%08x\n", I915_READ(ACTHD_I965)); I915_WRITE(IPEIR_I965, ipeir); POSTING_READ(IPEIR_I965); @@ -1718,18 +1730,15 @@ void i915_hangcheck_elapsed(unsigned long data) } i915_get_extra_instdone(dev, instdone); - if (memcmp(dev_priv->last_acthd, acthd, sizeof(acthd)) == 0 && - dev_priv->last_instdone == instdone[0] && - dev_priv->last_instdone1 == instdone[1]) { + memcmp(dev_priv->prev_instdone, instdone, sizeof(instdone)) == 0) { if (i915_hangcheck_hung(dev)) return; } else { dev_priv->hangcheck_count = 0; memcpy(dev_priv->last_acthd, acthd, sizeof(acthd)); - dev_priv->last_instdone = instdone[0]; - dev_priv->last_instdone1 = instdone[1]; + memcpy(dev_priv->prev_instdone, instdone, sizeof(instdone)); } repeat: -- cgit v0.10.2 From 7788a765205f63abcb8645c16c85a968bd578f4f Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Fri, 24 Aug 2012 19:18:18 +0100 Subject: drm/i915: Avoid unbinding due to an interrupted pin_and_fence during execbuffer If we need to stall in order to complete the pin_and_fence operation during execbuffer reservation, there is a high likelihood that the operation will be interrupted by a signal (thanks X!). In order to simplify the cleanup along that error path, the object was unconditionally unbound and the error propagated. However, being interrupted here is far more common than I would like and so we can strive to avoid the extra work by eliminating the forced unbind. v2: In discussion over the indecent colour of the new functions and unwind path, we realised that we can use the new unreserve function to clean up the code even further. Signed-off-by: Chris Wilson Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/i915_gem_execbuffer.c b/drivers/gpu/drm/i915/i915_gem_execbuffer.c index dc87563..e6b2205 100644 --- a/drivers/gpu/drm/i915/i915_gem_execbuffer.c +++ b/drivers/gpu/drm/i915/i915_gem_execbuffer.c @@ -331,7 +331,8 @@ i915_gem_execbuffer_relocate(struct drm_device *dev, return ret; } -#define __EXEC_OBJECT_HAS_FENCE (1<<31) +#define __EXEC_OBJECT_HAS_PIN (1<<31) +#define __EXEC_OBJECT_HAS_FENCE (1<<30) static int need_reloc_mappable(struct drm_i915_gem_object *obj) @@ -341,9 +342,10 @@ need_reloc_mappable(struct drm_i915_gem_object *obj) } static int -pin_and_fence_object(struct drm_i915_gem_object *obj, - struct intel_ring_buffer *ring) +i915_gem_execbuffer_reserve_object(struct drm_i915_gem_object *obj, + struct intel_ring_buffer *ring) { + struct drm_i915_private *dev_priv = obj->base.dev->dev_private; struct drm_i915_gem_exec_object2 *entry = obj->exec_entry; bool has_fenced_gpu_access = INTEL_INFO(ring->dev)->gen < 4; bool need_fence, need_mappable; @@ -359,11 +361,13 @@ pin_and_fence_object(struct drm_i915_gem_object *obj, if (ret) return ret; + entry->flags |= __EXEC_OBJECT_HAS_PIN; + if (has_fenced_gpu_access) { if (entry->flags & EXEC_OBJECT_NEEDS_FENCE) { ret = i915_gem_object_get_fence(obj); if (ret) - goto err_unpin; + return ret; if (i915_gem_object_pin_fence(obj)) entry->flags |= __EXEC_OBJECT_HAS_FENCE; @@ -372,12 +376,35 @@ pin_and_fence_object(struct drm_i915_gem_object *obj, } } + /* Ensure ppgtt mapping exists if needed */ + if (dev_priv->mm.aliasing_ppgtt && !obj->has_aliasing_ppgtt_mapping) { + i915_ppgtt_bind_object(dev_priv->mm.aliasing_ppgtt, + obj, obj->cache_level); + + obj->has_aliasing_ppgtt_mapping = 1; + } + entry->offset = obj->gtt_offset; return 0; +} -err_unpin: - i915_gem_object_unpin(obj); - return ret; +static void +i915_gem_execbuffer_unreserve_object(struct drm_i915_gem_object *obj) +{ + struct drm_i915_gem_exec_object2 *entry; + + if (!obj->gtt_space) + return; + + entry = obj->exec_entry; + + if (entry->flags & __EXEC_OBJECT_HAS_FENCE) + i915_gem_object_unpin_fence(obj); + + if (entry->flags & __EXEC_OBJECT_HAS_PIN) + i915_gem_object_unpin(obj); + + entry->flags &= ~(__EXEC_OBJECT_HAS_FENCE | __EXEC_OBJECT_HAS_PIN); } static int @@ -385,11 +412,10 @@ i915_gem_execbuffer_reserve(struct intel_ring_buffer *ring, struct drm_file *file, struct list_head *objects) { - drm_i915_private_t *dev_priv = ring->dev->dev_private; struct drm_i915_gem_object *obj; - int ret, retry; - bool has_fenced_gpu_access = INTEL_INFO(ring->dev)->gen < 4; struct list_head ordered_objects; + bool has_fenced_gpu_access = INTEL_INFO(ring->dev)->gen < 4; + int retry; INIT_LIST_HEAD(&ordered_objects); while (!list_empty(objects)) { @@ -427,12 +453,12 @@ i915_gem_execbuffer_reserve(struct intel_ring_buffer *ring, * 2. Bind new objects. * 3. Decrement pin count. * - * This avoid unnecessary unbinding of later objects in order to makr + * This avoid unnecessary unbinding of later objects in order to make * room for the earlier objects *unless* we need to defragment. */ retry = 0; do { - ret = 0; + int ret = 0; /* Unbind any ill-fitting objects or pin. */ list_for_each_entry(obj, objects, exec_list) { @@ -452,7 +478,7 @@ i915_gem_execbuffer_reserve(struct intel_ring_buffer *ring, (need_mappable && !obj->map_and_fenceable)) ret = i915_gem_object_unbind(obj); else - ret = pin_and_fence_object(obj, ring); + ret = i915_gem_execbuffer_reserve_object(obj, ring); if (ret) goto err; } @@ -462,46 +488,14 @@ i915_gem_execbuffer_reserve(struct intel_ring_buffer *ring, if (obj->gtt_space) continue; - ret = pin_and_fence_object(obj, ring); - if (ret) { - int ret_ignore; - - /* This can potentially raise a harmless - * -EINVAL if we failed to bind in the above - * call. It cannot raise -EINTR since we know - * that the bo is freshly bound and so will - * not need to be flushed or waited upon. - */ - ret_ignore = i915_gem_object_unbind(obj); - (void)ret_ignore; - WARN_ON(obj->gtt_space); - break; - } + ret = i915_gem_execbuffer_reserve_object(obj, ring); + if (ret) + goto err; } - /* Decrement pin count for bound objects */ - list_for_each_entry(obj, objects, exec_list) { - struct drm_i915_gem_exec_object2 *entry; - - if (!obj->gtt_space) - continue; - - entry = obj->exec_entry; - if (entry->flags & __EXEC_OBJECT_HAS_FENCE) { - i915_gem_object_unpin_fence(obj); - entry->flags &= ~__EXEC_OBJECT_HAS_FENCE; - } - - i915_gem_object_unpin(obj); - - /* ... and ensure ppgtt mapping exist if needed. */ - if (dev_priv->mm.aliasing_ppgtt && !obj->has_aliasing_ppgtt_mapping) { - i915_ppgtt_bind_object(dev_priv->mm.aliasing_ppgtt, - obj, obj->cache_level); - - obj->has_aliasing_ppgtt_mapping = 1; - } - } +err: /* Decrement pin count for bound objects */ + list_for_each_entry(obj, objects, exec_list) + i915_gem_execbuffer_unreserve_object(obj); if (ret != -ENOSPC || retry++) return ret; @@ -510,24 +504,6 @@ i915_gem_execbuffer_reserve(struct intel_ring_buffer *ring, if (ret) return ret; } while (1); - -err: - list_for_each_entry_continue_reverse(obj, objects, exec_list) { - struct drm_i915_gem_exec_object2 *entry; - - if (!obj->gtt_space) - continue; - - entry = obj->exec_entry; - if (entry->flags & __EXEC_OBJECT_HAS_FENCE) { - i915_gem_object_unpin_fence(obj); - entry->flags &= ~__EXEC_OBJECT_HAS_FENCE; - } - - i915_gem_object_unpin(obj); - } - - return ret; } static int -- cgit v0.10.2 From cd7988eea561a70a4f98e431c1395f913672d626 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Sun, 26 Aug 2012 20:33:18 +0200 Subject: drm/i915: disable rc6 on ilk when vt-d is enabled It blows up. And hopefully this is the root-cause of the mysterious rc6 related hang on ilk. For reference, the commit that enabled rc6 on ilk again is: commit 456470eb583f063ee84c6818251e638598be0fb8 Author: Daniel Vetter Date: Wed Aug 8 23:35:40 2012 +0200 drm/i915: enable rc6 on ilk again Reported-by: Dave Airlie Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c index c0721ff..ebe3498 100644 --- a/drivers/gpu/drm/i915/intel_pm.c +++ b/drivers/gpu/drm/i915/intel_pm.c @@ -2372,6 +2372,11 @@ int intel_enable_rc6(const struct drm_device *dev) return i915_enable_rc6; if (INTEL_INFO(dev)->gen == 5) { +#ifdef CONFIG_INTEL_IOMMU + /* Disable rc6 on ilk if VT-d is on. */ + if (intel_iommu_gfx_mapped) + return false; +#endif DRM_DEBUG_DRIVER("Ironlake: only RC6 available\n"); return INTEL_RC6_ENABLE; } -- cgit v0.10.2 From d7c3b937bdf45f0b844400b7bf6fd3ed50bac604 Mon Sep 17 00:00:00 2001 From: Sedat Dilek Date: Mon, 27 Aug 2012 14:02:37 +0200 Subject: drm/i915: Remove __GFP_NO_KSWAPD When I pulled-in today's drm-intel-next into linux-next (next-20120824) I saw this build-breakage: drivers/gpu/drm/i915/i915_gem.c: In function 'i915_gem_object_get_pages_gtt': drivers/gpu/drm/i915/i915_gem.c:1778:40: error: '__GFP_NO_KSWAPD' undeclared (first use in this function) drivers/gpu/drm/i915/i915_gem.c:1778:40: note: each undeclared identifier is reported only once for each function it appears in This is caused by commit ba099ef165f8 ("mm: remove __GFP_NO_KSWAPD") and commit b6beae2c2014 ("mm: remove __GFP_NO_KSWAPD fixes") in linux-next (next-20120824). Fix this by removing __GFP_NO_KSWAPD from drm/i915 driver. Signed-off-by: Sedat Dilek Reviewed-by: Chris Wilson Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index 719a933..ac050e6 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -1775,7 +1775,7 @@ i915_gem_object_get_pages_gtt(struct drm_i915_gem_object *obj) /* Fail silently without starting the shrinker */ mapping = obj->base.filp->f_path.dentry->d_inode->i_mapping; gfp = mapping_gfp_mask(mapping); - gfp |= __GFP_NORETRY | __GFP_NOWARN | __GFP_NO_KSWAPD; + gfp |= __GFP_NORETRY | __GFP_NOWARN; gfp &= ~(__GFP_IO | __GFP_WAIT); for (i = 0; i < page_count; i++) { page = shmem_read_mapping_page_gfp(mapping, i, gfp); @@ -1788,7 +1788,7 @@ i915_gem_object_get_pages_gtt(struct drm_i915_gem_object *obj) * our own buffer, now let the real VM do its job and * go down in flames if truly OOM. */ - gfp &= ~(__GFP_NORETRY | __GFP_NOWARN | __GFP_NO_KSWAPD); + gfp &= ~(__GFP_NORETRY | __GFP_NOWARN); gfp |= __GFP_IO | __GFP_WAIT; i915_gem_shrink_all(dev_priv); @@ -1796,7 +1796,7 @@ i915_gem_object_get_pages_gtt(struct drm_i915_gem_object *obj) if (IS_ERR(page)) goto err_pages; - gfp |= __GFP_NORETRY | __GFP_NOWARN | __GFP_NO_KSWAPD; + gfp |= __GFP_NORETRY | __GFP_NOWARN; gfp &= ~(__GFP_IO | __GFP_WAIT); } -- cgit v0.10.2 From 85f9e50d815a70780699752c4f2dc1198c2c0b61 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Fri, 31 Aug 2012 21:42:26 +0200 Subject: drm/i915: move i915_get_extra_insdone out of CONFIG_DEBUG_FS block Otherwise it just won't compile ... Reported-by: Fengguang Wu Reviewed-by: Ben Widawsky Signed-off-by: Daniel Vetter Signed-off-by: Dave Airlie diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index 34dc715..a9f74f3 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -853,6 +853,35 @@ static void i915_error_work_func(struct work_struct *work) } } +/* NB: please notice the memset */ +static void i915_get_extra_instdone(struct drm_device *dev, + uint32_t *instdone) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + memset(instdone, 0, sizeof(*instdone) * I915_NUM_INSTDONE_REG); + + switch(INTEL_INFO(dev)->gen) { + case 2: + case 3: + instdone[0] = I915_READ(INSTDONE); + break; + case 4: + case 5: + case 6: + instdone[0] = I915_READ(INSTDONE_I965); + instdone[1] = I915_READ(INSTDONE1); + break; + default: + WARN_ONCE(1, "Unsupported platform\n"); + case 7: + instdone[0] = I915_READ(GEN7_INSTDONE_1); + instdone[1] = I915_READ(GEN7_SC_INSTDONE); + instdone[2] = I915_READ(GEN7_SAMPLER_INSTDONE); + instdone[3] = I915_READ(GEN7_ROW_INSTDONE); + break; + } +} + #ifdef CONFIG_DEBUG_FS static struct drm_i915_error_object * i915_error_object_create(struct drm_i915_private *dev_priv, @@ -1070,36 +1099,6 @@ i915_error_first_batchbuffer(struct drm_i915_private *dev_priv, return NULL; } -/* NB: please notice the memset */ -static void i915_get_extra_instdone(struct drm_device *dev, - uint32_t *instdone) -{ - struct drm_i915_private *dev_priv = dev->dev_private; - memset(instdone, 0, sizeof(*instdone) * I915_NUM_INSTDONE_REG); - - switch(INTEL_INFO(dev)->gen) { - case 2: - case 3: - instdone[0] = I915_READ(INSTDONE); - break; - case 4: - case 5: - case 6: - instdone[0] = I915_READ(INSTDONE_I965); - instdone[1] = I915_READ(INSTDONE1); - break; - default: - WARN_ONCE(1, "Unsupported platform\n"); - case 7: - instdone[0] = I915_READ(GEN7_INSTDONE_1); - instdone[1] = I915_READ(GEN7_SC_INSTDONE); - instdone[2] = I915_READ(GEN7_SAMPLER_INSTDONE); - instdone[3] = I915_READ(GEN7_ROW_INSTDONE); - break; - } -} - - static void i915_record_ring_state(struct drm_device *dev, struct drm_i915_error_state *error, struct intel_ring_buffer *ring) -- cgit v0.10.2 From 4772eaebcdf86dd65630339dbe58316b90f80aed Mon Sep 17 00:00:00 2001 From: Paulo Zanoni Date: Fri, 17 Aug 2012 18:35:41 -0300 Subject: drm/i915: add gen7_render_ring_flush For now, just a copy of gen6_render_ring_flush. Different gens have different workarounds, so we want different functions. Signed-off-by: Paulo Zanoni Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/intel_ringbuffer.c b/drivers/gpu/drm/i915/intel_ringbuffer.c index ac93643..074b7d6 100644 --- a/drivers/gpu/drm/i915/intel_ringbuffer.c +++ b/drivers/gpu/drm/i915/intel_ringbuffer.c @@ -258,6 +258,54 @@ gen6_render_ring_flush(struct intel_ring_buffer *ring, } static int +gen7_render_ring_flush(struct intel_ring_buffer *ring, + u32 invalidate_domains, u32 flush_domains) +{ + u32 flags = 0; + struct pipe_control *pc = ring->private; + u32 scratch_addr = pc->gtt_offset + 128; + int ret; + + /* Just flush everything. Experiments have shown that reducing the + * number of bits based on the write domains has little performance + * impact. + */ + if (flush_domains) { + flags |= PIPE_CONTROL_RENDER_TARGET_CACHE_FLUSH; + flags |= PIPE_CONTROL_DEPTH_CACHE_FLUSH; + /* + * Ensure that any following seqno writes only happen + * when the render cache is indeed flushed. + */ + flags |= PIPE_CONTROL_CS_STALL; + } + if (invalidate_domains) { + flags |= PIPE_CONTROL_TLB_INVALIDATE; + flags |= PIPE_CONTROL_INSTRUCTION_CACHE_INVALIDATE; + flags |= PIPE_CONTROL_TEXTURE_CACHE_INVALIDATE; + flags |= PIPE_CONTROL_VF_CACHE_INVALIDATE; + flags |= PIPE_CONTROL_CONST_CACHE_INVALIDATE; + flags |= PIPE_CONTROL_STATE_CACHE_INVALIDATE; + /* + * TLB invalidate requires a post-sync write. + */ + flags |= PIPE_CONTROL_QW_WRITE; + } + + ret = intel_ring_begin(ring, 4); + if (ret) + return ret; + + intel_ring_emit(ring, GFX_OP_PIPE_CONTROL(4)); + intel_ring_emit(ring, flags); + intel_ring_emit(ring, scratch_addr | PIPE_CONTROL_GLOBAL_GTT); + intel_ring_emit(ring, 0); + intel_ring_advance(ring); + + return 0; +} + +static int gen6_render_ring_flush__wa(struct intel_ring_buffer *ring, u32 invalidate_domains, u32 flush_domains) { @@ -1385,7 +1433,7 @@ int intel_init_render_ring_buffer(struct drm_device *dev) if (INTEL_INFO(dev)->gen >= 6) { ring->add_request = gen6_add_request; - ring->flush = gen6_render_ring_flush; + ring->flush = gen7_render_ring_flush; if (INTEL_INFO(dev)->gen == 6) ring->flush = gen6_render_ring_flush__wa; ring->irq_get = gen6_ring_get_irq; -- cgit v0.10.2 From b31115092724925a434905dc3dbf83a2e752ba4b Mon Sep 17 00:00:00 2001 From: Paulo Zanoni Date: Fri, 17 Aug 2012 18:35:42 -0300 Subject: drm/i915: add workarounds directly to gen6_render_ring_flush Since gen 7+ now run the new gen7_render_ring_flush function. Signed-off-by: Paulo Zanoni Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/intel_ringbuffer.c b/drivers/gpu/drm/i915/intel_ringbuffer.c index 074b7d6..42a4b85 100644 --- a/drivers/gpu/drm/i915/intel_ringbuffer.c +++ b/drivers/gpu/drm/i915/intel_ringbuffer.c @@ -218,6 +218,11 @@ gen6_render_ring_flush(struct intel_ring_buffer *ring, u32 scratch_addr = pc->gtt_offset + 128; int ret; + /* Force SNB workarounds for PIPE_CONTROL flushes */ + ret = intel_emit_post_sync_nonzero_flush(ring); + if (ret) + return ret; + /* Just flush everything. Experiments have shown that reducing the * number of bits based on the write domains has little performance * impact. @@ -305,20 +310,6 @@ gen7_render_ring_flush(struct intel_ring_buffer *ring, return 0; } -static int -gen6_render_ring_flush__wa(struct intel_ring_buffer *ring, - u32 invalidate_domains, u32 flush_domains) -{ - int ret; - - /* Force SNB workarounds for PIPE_CONTROL flushes */ - ret = intel_emit_post_sync_nonzero_flush(ring); - if (ret) - return ret; - - return gen6_render_ring_flush(ring, invalidate_domains, flush_domains); -} - static void ring_write_tail(struct intel_ring_buffer *ring, u32 value) { @@ -1435,7 +1426,7 @@ int intel_init_render_ring_buffer(struct drm_device *dev) ring->add_request = gen6_add_request; ring->flush = gen7_render_ring_flush; if (INTEL_INFO(dev)->gen == 6) - ring->flush = gen6_render_ring_flush__wa; + ring->flush = gen6_render_ring_flush; ring->irq_get = gen6_ring_get_irq; ring->irq_put = gen6_ring_put_irq; ring->irq_enable_mask = GT_USER_INTERRUPT; -- cgit v0.10.2 From f39876317a69a104eeaed002d4085348e871bfd1 Mon Sep 17 00:00:00 2001 From: Paulo Zanoni Date: Fri, 17 Aug 2012 18:35:43 -0300 Subject: drm/i915: add workarounds to gen7_render_ring_flush From Bspec, Vol 2a, Section 1.9.3.4 "PIPE_CONTROL", intro section detailing the various workarounds: "[DevIVB {W/A}, DevHSW {W/A}]: Pipe_control with CS-stall bit set must be issued before a pipe-control command that has the State Cache Invalidate bit set." Note that public Bspec has different numbering, it's Vol2Part1, Section 1.10.4.1 "PIPE_CONTROL" there. There's also a second workaround for the PIPE_CONTROL command itself: "[DevIVB, DevVLV, DevHSW] {WA}: Every 4th PIPE_CONTROL command, not counting the PIPE_CONTROL with only read-cache-invalidate bit(s) set, must have a CS_STALL bit set" For simplicity we simply set the CS_STALL bit on every pipe_control on gen7+ Note that this massively helps on some hsw machines, together with the following patch to unconditionally set the CS_STALL bit on every pipe_control it prevents a gpu hang every few seconds. This is a regression that has been introduced in the pipe_control cleanup: commit 6c6cf5aa9c583478b19e23149feaa92d01fb8c2d Author: Chris Wilson Date: Fri Jul 20 18:02:28 2012 +0100 drm/i915: Only apply the SNB pipe control w/a to gen6 It looks like the massive snb pipe_control workaround also papered over any issues on ivb and hsw. Signed-off-by: Paulo Zanoni [danvet: squashed both workarounds together, pimped commit message with Bsepc citations, regression commit citation and changed the comment in the code a bit to clarify that we unconditionally set CS_STALL to avoid being hurt by trying to be clever.] Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/intel_ringbuffer.c b/drivers/gpu/drm/i915/intel_ringbuffer.c index 42a4b85..55cdb4d 100644 --- a/drivers/gpu/drm/i915/intel_ringbuffer.c +++ b/drivers/gpu/drm/i915/intel_ringbuffer.c @@ -263,6 +263,25 @@ gen6_render_ring_flush(struct intel_ring_buffer *ring, } static int +gen7_render_ring_cs_stall_wa(struct intel_ring_buffer *ring) +{ + int ret; + + ret = intel_ring_begin(ring, 4); + if (ret) + return ret; + + intel_ring_emit(ring, GFX_OP_PIPE_CONTROL(4)); + intel_ring_emit(ring, PIPE_CONTROL_CS_STALL | + PIPE_CONTROL_STALL_AT_SCOREBOARD); + intel_ring_emit(ring, 0); + intel_ring_emit(ring, 0); + intel_ring_advance(ring); + + return 0; +} + +static int gen7_render_ring_flush(struct intel_ring_buffer *ring, u32 invalidate_domains, u32 flush_domains) { @@ -271,6 +290,16 @@ gen7_render_ring_flush(struct intel_ring_buffer *ring, u32 scratch_addr = pc->gtt_offset + 128; int ret; + /* + * Ensure that any following seqno writes only happen when the render + * cache is indeed flushed. + * + * Workaround: 4th PIPE_CONTROL command (except the ones with only + * read-cache invalidate bits set) must have the CS_STALL bit set. We + * don't try to be clever and just set it unconditionally. + */ + flags |= PIPE_CONTROL_CS_STALL; + /* Just flush everything. Experiments have shown that reducing the * number of bits based on the write domains has little performance * impact. @@ -278,11 +307,6 @@ gen7_render_ring_flush(struct intel_ring_buffer *ring, if (flush_domains) { flags |= PIPE_CONTROL_RENDER_TARGET_CACHE_FLUSH; flags |= PIPE_CONTROL_DEPTH_CACHE_FLUSH; - /* - * Ensure that any following seqno writes only happen - * when the render cache is indeed flushed. - */ - flags |= PIPE_CONTROL_CS_STALL; } if (invalidate_domains) { flags |= PIPE_CONTROL_TLB_INVALIDATE; @@ -295,6 +319,11 @@ gen7_render_ring_flush(struct intel_ring_buffer *ring, * TLB invalidate requires a post-sync write. */ flags |= PIPE_CONTROL_QW_WRITE; + + /* Workaround: we must issue a pipe_control with CS-stall bit + * set before a pipe_control command that has the state cache + * invalidate bit set. */ + gen7_render_ring_cs_stall_wa(ring); } ret = intel_ring_begin(ring, 4); -- cgit v0.10.2 From 20e4d407fbe39bd15f6d4ded25e8c307789ecc80 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Wed, 8 Aug 2012 23:35:39 +0200 Subject: drm/ips: move drps/ips/ilk related variables into dev_priv->ips Like with the equivalent change for gen6+ rps state, this helps in clarifying the code (and in fixing a few places that have fallen through the cracks in the locking review). Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 58b43db..f16ab5e 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -839,22 +839,26 @@ typedef struct drm_i915_private { u8 max_delay; } rps; - - u8 cur_delay; - u8 min_delay; - u8 max_delay; - u8 fmax; - u8 fstart; - - u64 last_count1; - unsigned long last_time1; - unsigned long chipset_power; - u64 last_count2; - struct timespec last_time2; - unsigned long gfx_power; - int c_m; - int r_t; - u8 corr; + /* ilk-only ips/rps state. Everything in here is protected by the global + * mchdev_lock in intel_pm.c */ + struct { + u8 cur_delay; + u8 min_delay; + u8 max_delay; + u8 fmax; + u8 fstart; + + u64 last_count1; + unsigned long last_time1; + unsigned long chipset_power; + u64 last_count2; + struct timespec last_time2; + unsigned long gfx_power; + u8 corr; + + int c_m; + int r_t; + } ips; enum no_fbc_reason no_fbc_reason; diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index a9f74f3..d601013 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -310,7 +310,7 @@ static void ironlake_handle_rps_change(struct drm_device *dev) I915_WRITE16(MEMINTRSTS, I915_READ(MEMINTRSTS)); - new_delay = dev_priv->cur_delay; + new_delay = dev_priv->ips.cur_delay; I915_WRITE16(MEMINTRSTS, MEMINT_EVAL_CHG); busy_up = I915_READ(RCPREVBSYTUPAVG); @@ -320,19 +320,19 @@ static void ironlake_handle_rps_change(struct drm_device *dev) /* Handle RCS change request from hw */ if (busy_up > max_avg) { - if (dev_priv->cur_delay != dev_priv->max_delay) - new_delay = dev_priv->cur_delay - 1; - if (new_delay < dev_priv->max_delay) - new_delay = dev_priv->max_delay; + if (dev_priv->ips.cur_delay != dev_priv->ips.max_delay) + new_delay = dev_priv->ips.cur_delay - 1; + if (new_delay < dev_priv->ips.max_delay) + new_delay = dev_priv->ips.max_delay; } else if (busy_down < min_avg) { - if (dev_priv->cur_delay != dev_priv->min_delay) - new_delay = dev_priv->cur_delay + 1; - if (new_delay > dev_priv->min_delay) - new_delay = dev_priv->min_delay; + if (dev_priv->ips.cur_delay != dev_priv->ips.min_delay) + new_delay = dev_priv->ips.cur_delay + 1; + if (new_delay > dev_priv->ips.min_delay) + new_delay = dev_priv->ips.min_delay; } if (ironlake_set_drps(dev, new_delay)) - dev_priv->cur_delay = new_delay; + dev_priv->ips.cur_delay = new_delay; spin_unlock_irqrestore(&mchdev_lock, flags); diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c index a465deb..8dc802c 100644 --- a/drivers/gpu/drm/i915/intel_pm.c +++ b/drivers/gpu/drm/i915/intel_pm.c @@ -593,7 +593,7 @@ static void i915_ironlake_get_mem_freq(struct drm_device *dev) break; } - dev_priv->r_t = dev_priv->mem_freq; + dev_priv->ips.r_t = dev_priv->mem_freq; switch (csipll & 0x3ff) { case 0x00c: @@ -625,11 +625,11 @@ static void i915_ironlake_get_mem_freq(struct drm_device *dev) } if (dev_priv->fsb_freq == 3200) { - dev_priv->c_m = 0; + dev_priv->ips.c_m = 0; } else if (dev_priv->fsb_freq > 3200 && dev_priv->fsb_freq <= 4800) { - dev_priv->c_m = 1; + dev_priv->ips.c_m = 1; } else { - dev_priv->c_m = 2; + dev_priv->ips.c_m = 2; } } @@ -2162,12 +2162,6 @@ err_unref: /** * Lock protecting IPS related data structures - * - i915_mch_dev - * - dev_priv->max_delay - * - dev_priv->min_delay - * - dev_priv->fmax - * - dev_priv->gpu_busy - * - dev_priv->gfx_power */ DEFINE_SPINLOCK(mchdev_lock); @@ -2230,12 +2224,12 @@ static void ironlake_enable_drps(struct drm_device *dev) vstart = (I915_READ(PXVFREQ_BASE + (fstart * 4)) & PXVFREQ_PX_MASK) >> PXVFREQ_PX_SHIFT; - dev_priv->fmax = fmax; /* IPS callback will increase this */ - dev_priv->fstart = fstart; + dev_priv->ips.fmax = fmax; /* IPS callback will increase this */ + dev_priv->ips.fstart = fstart; - dev_priv->max_delay = fstart; - dev_priv->min_delay = fmin; - dev_priv->cur_delay = fstart; + dev_priv->ips.max_delay = fstart; + dev_priv->ips.min_delay = fmin; + dev_priv->ips.cur_delay = fstart; DRM_DEBUG_DRIVER("fmax: %d, fmin: %d, fstart: %d\n", fmax, fmin, fstart); @@ -2258,11 +2252,11 @@ static void ironlake_enable_drps(struct drm_device *dev) ironlake_set_drps(dev, fstart); - dev_priv->last_count1 = I915_READ(0x112e4) + I915_READ(0x112e8) + + dev_priv->ips.last_count1 = I915_READ(0x112e4) + I915_READ(0x112e8) + I915_READ(0x112e0); - dev_priv->last_time1 = jiffies_to_msecs(jiffies); - dev_priv->last_count2 = I915_READ(0x112f4); - getrawmonotonic(&dev_priv->last_time2); + dev_priv->ips.last_time1 = jiffies_to_msecs(jiffies); + dev_priv->ips.last_count2 = I915_READ(0x112f4); + getrawmonotonic(&dev_priv->ips.last_time2); spin_unlock_irq(&mchdev_lock); } @@ -2284,7 +2278,7 @@ static void ironlake_disable_drps(struct drm_device *dev) I915_WRITE(DEIMR, I915_READ(DEIMR) | DE_PCU_EVENT); /* Go back to the starting frequency */ - ironlake_set_drps(dev, dev_priv->fstart); + ironlake_set_drps(dev, dev_priv->ips.fstart); mdelay(1); rgvswctl |= MEMCTL_CMD_STS; I915_WRITE(MEMSWCTL, rgvswctl); @@ -2741,7 +2735,7 @@ unsigned long i915_chipset_val(struct drm_i915_private *dev_priv) assert_spin_locked(&mchdev_lock); - diff1 = now - dev_priv->last_time1; + diff1 = now - dev_priv->ips.last_time1; /* Prevent division-by-zero if we are asking too fast. * Also, we don't get interesting results if we are polling @@ -2749,7 +2743,7 @@ unsigned long i915_chipset_val(struct drm_i915_private *dev_priv) * in such cases. */ if (diff1 <= 10) - return dev_priv->chipset_power; + return dev_priv->ips.chipset_power; count1 = I915_READ(DMIEC); count2 = I915_READ(DDREC); @@ -2758,16 +2752,16 @@ unsigned long i915_chipset_val(struct drm_i915_private *dev_priv) total_count = count1 + count2 + count3; /* FIXME: handle per-counter overflow */ - if (total_count < dev_priv->last_count1) { - diff = ~0UL - dev_priv->last_count1; + if (total_count < dev_priv->ips.last_count1) { + diff = ~0UL - dev_priv->ips.last_count1; diff += total_count; } else { - diff = total_count - dev_priv->last_count1; + diff = total_count - dev_priv->ips.last_count1; } for (i = 0; i < ARRAY_SIZE(cparams); i++) { - if (cparams[i].i == dev_priv->c_m && - cparams[i].t == dev_priv->r_t) { + if (cparams[i].i == dev_priv->ips.c_m && + cparams[i].t == dev_priv->ips.r_t) { m = cparams[i].m; c = cparams[i].c; break; @@ -2778,10 +2772,10 @@ unsigned long i915_chipset_val(struct drm_i915_private *dev_priv) ret = ((m * diff) + c); ret = div_u64(ret, 10); - dev_priv->last_count1 = total_count; - dev_priv->last_time1 = now; + dev_priv->ips.last_count1 = total_count; + dev_priv->ips.last_time1 = now; - dev_priv->chipset_power = ret; + dev_priv->ips.chipset_power = ret; return ret; } @@ -2952,7 +2946,7 @@ static void __i915_update_gfx_val(struct drm_i915_private *dev_priv) assert_spin_locked(&mchdev_lock); getrawmonotonic(&now); - diff1 = timespec_sub(now, dev_priv->last_time2); + diff1 = timespec_sub(now, dev_priv->ips.last_time2); /* Don't divide by 0 */ diffms = diff1.tv_sec * 1000 + diff1.tv_nsec / 1000000; @@ -2961,20 +2955,20 @@ static void __i915_update_gfx_val(struct drm_i915_private *dev_priv) count = I915_READ(GFXEC); - if (count < dev_priv->last_count2) { - diff = ~0UL - dev_priv->last_count2; + if (count < dev_priv->ips.last_count2) { + diff = ~0UL - dev_priv->ips.last_count2; diff += count; } else { - diff = count - dev_priv->last_count2; + diff = count - dev_priv->ips.last_count2; } - dev_priv->last_count2 = count; - dev_priv->last_time2 = now; + dev_priv->ips.last_count2 = count; + dev_priv->ips.last_time2 = now; /* More magic constants... */ diff = diff * 1181; diff = div_u64(diff, diffms * 10); - dev_priv->gfx_power = diff; + dev_priv->ips.gfx_power = diff; } void i915_update_gfx_val(struct drm_i915_private *dev_priv) @@ -3016,14 +3010,14 @@ unsigned long i915_gfx_val(struct drm_i915_private *dev_priv) corr = corr * ((150142 * state1) / 10000 - 78642); corr /= 100000; - corr2 = (corr * dev_priv->corr); + corr2 = (corr * dev_priv->ips.corr); state2 = (corr2 * state1) / 10000; state2 /= 100; /* convert to mW */ __i915_update_gfx_val(dev_priv); - return dev_priv->gfx_power + state2; + return dev_priv->ips.gfx_power + state2; } /** @@ -3071,8 +3065,8 @@ bool i915_gpu_raise(void) } dev_priv = i915_mch_dev; - if (dev_priv->max_delay > dev_priv->fmax) - dev_priv->max_delay--; + if (dev_priv->ips.max_delay > dev_priv->ips.fmax) + dev_priv->ips.max_delay--; out_unlock: spin_unlock_irq(&mchdev_lock); @@ -3099,8 +3093,8 @@ bool i915_gpu_lower(void) } dev_priv = i915_mch_dev; - if (dev_priv->max_delay < dev_priv->min_delay) - dev_priv->max_delay++; + if (dev_priv->ips.max_delay < dev_priv->ips.min_delay) + dev_priv->ips.max_delay++; out_unlock: spin_unlock_irq(&mchdev_lock); @@ -3154,9 +3148,9 @@ bool i915_gpu_turbo_disable(void) } dev_priv = i915_mch_dev; - dev_priv->max_delay = dev_priv->fstart; + dev_priv->ips.max_delay = dev_priv->ips.fstart; - if (!ironlake_set_drps(dev_priv->dev, dev_priv->fstart)) + if (!ironlake_set_drps(dev_priv->dev, dev_priv->ips.fstart)) ret = false; out_unlock: @@ -3271,7 +3265,7 @@ static void intel_init_emon(struct drm_device *dev) lcfuse = I915_READ(LCFUSE02); - dev_priv->corr = (lcfuse & LCFUSE_HIV_MASK); + dev_priv->ips.corr = (lcfuse & LCFUSE_HIV_MASK); } void intel_disable_gt_powersave(struct drm_device *dev) -- cgit v0.10.2 From be2cde9a6d922e5e43efd2ad39bc43ce70a5d79b Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Thu, 30 Aug 2012 13:26:48 +0200 Subject: drm/i915: add a tracepoint for gpu frequency changes We've had and still have too many issues where the gpu turbo doesn't quite to what it's supposed to do (or what we want it to do). Adding a tracepoint to track when the desired gpu frequency changes should help a lot in characterizing and understanding problematic workloads. Also, this should be fairly interesting for power tuning (and especially noticing when the gpu is stuck in high frequencies, as has happened in the past) and hence for integration into powertop and similar tools. Cc: Arjan van de Ven Reviewed-by: Chris Wilson Acked-by: Paul Menzel Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/i915_trace.h b/drivers/gpu/drm/i915/i915_trace.h index 3c4093d..8134421 100644 --- a/drivers/gpu/drm/i915/i915_trace.h +++ b/drivers/gpu/drm/i915/i915_trace.h @@ -430,6 +430,21 @@ TRACE_EVENT(i915_reg_rw, (u32)(__entry->val >> 32)) ); +TRACE_EVENT(intel_gpu_freq_change, + TP_PROTO(u32 freq), + TP_ARGS(freq), + + TP_STRUCT__entry( + __field(u32, freq) + ), + + TP_fast_assign( + __entry->freq = freq; + ), + + TP_printk("new_freq=%u", __entry->freq) +); + #endif /* _I915_TRACE_H_ */ /* This part must be outside protection */ diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c index 8dc802c..7ae3582 100644 --- a/drivers/gpu/drm/i915/intel_pm.c +++ b/drivers/gpu/drm/i915/intel_pm.c @@ -2337,6 +2337,8 @@ void gen6_set_rps(struct drm_device *dev, u8 val) I915_WRITE(GEN6_RP_INTERRUPT_LIMITS, limits); dev_priv->rps.cur_delay = val; + + trace_intel_gpu_freq_change(val * 50); } static void gen6_disable_rps(struct drm_device *dev) -- cgit v0.10.2 From 5ab140a4ac08c895b67c2755e6f988ea92b5818b Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Fri, 24 Aug 2012 17:26:20 +0200 Subject: drm/i915: align vlv forcewake with common lore For some odd reasons, the vlv forcewake code is rather different from all other platforms, with no clear justification. Adjust things: - Don't check whether the gt is awake already (and bail out early), we need to grab a forcewake anyway. Otherwise the chip might go to sleep too early. And this would also screw up our forcewake accounting. - Like all other platforms, check whether the gt has cleared the forcewake bit in the _ACK register before setting it again. - Use _MASKED_BIT_ENABLE/DISABLE macros - Only use bit0 of the forcewake reg, not all 16 bits. - check the gtfifodb reg like on all other platforms in _put. - Drop the POSTING_READs for consistency. v2: Failure to git add ... again. v3: Fixup the spelling fail a bit. Tested-by: "Purushothaman, Vijay A" Tested-by: "Widawsky, Benjamin" Signed-Off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c index 7ae3582..82f74f3 100644 --- a/drivers/gpu/drm/i915/intel_pm.c +++ b/drivers/gpu/drm/i915/intel_pm.c @@ -4069,12 +4069,10 @@ int __gen6_gt_wait_for_fifo(struct drm_i915_private *dev_priv) static void vlv_force_wake_get(struct drm_i915_private *dev_priv) { - /* Already awake? */ - if ((I915_READ(0x130094) & 0xa1) == 0xa1) - return; + if (wait_for_atomic_us((I915_READ_NOTRACE(FORCEWAKE_ACK_VLV) & 1) == 0, 500)) + DRM_ERROR("Force wake wait timed out\n"); - I915_WRITE_NOTRACE(FORCEWAKE_VLV, 0xffffffff); - POSTING_READ(FORCEWAKE_VLV); + I915_WRITE_NOTRACE(FORCEWAKE_VLV, _MASKED_BIT_ENABLE(1)); if (wait_for_atomic_us((I915_READ_NOTRACE(FORCEWAKE_ACK_VLV) & 1), 500)) DRM_ERROR("Force wake wait timed out\n"); @@ -4084,9 +4082,9 @@ static void vlv_force_wake_get(struct drm_i915_private *dev_priv) static void vlv_force_wake_put(struct drm_i915_private *dev_priv) { - I915_WRITE_NOTRACE(FORCEWAKE_VLV, 0xffff0000); - /* FIXME: confirm VLV behavior with Punit folks */ - POSTING_READ(FORCEWAKE_VLV); + I915_WRITE_NOTRACE(FORCEWAKE_VLV, _MASKED_BIT_DISABLE(1)); + /* The below doubles as a POSTING_READ */ + gen6_gt_check_fifodbg(dev_priv); } void intel_gt_init(struct drm_device *dev) -- cgit v0.10.2 From 8a038fd633817b5a05ed46052695d490fccfa5b9 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Fri, 24 Aug 2012 17:26:21 +0200 Subject: drm/i915: differ error message between forcwake timeouts danvet: in the force wake, both DRM_ERRORs have the same string. useful for .txt shrinkage, horrible for debugging Acked-by: Paul Menzel Reviewed-by: Ben Widawsky Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c index 82f74f3..ec7fbf9 100644 --- a/drivers/gpu/drm/i915/intel_pm.c +++ b/drivers/gpu/drm/i915/intel_pm.c @@ -3963,13 +3963,13 @@ static void __gen6_gt_force_wake_get(struct drm_i915_private *dev_priv) forcewake_ack = FORCEWAKE_ACK; if (wait_for_atomic_us((I915_READ_NOTRACE(forcewake_ack) & 1) == 0, 500)) - DRM_ERROR("Force wake wait timed out\n"); + DRM_ERROR("Timed out waiting for forcewake old ack to clear.\n"); I915_WRITE_NOTRACE(FORCEWAKE, 1); POSTING_READ(FORCEWAKE); if (wait_for_atomic_us((I915_READ_NOTRACE(forcewake_ack) & 1), 500)) - DRM_ERROR("Force wake wait timed out\n"); + DRM_ERROR("Timed out waiting for forcewake to ack request.\n"); __gen6_gt_wait_for_thread_c0(dev_priv); } @@ -3984,13 +3984,13 @@ static void __gen6_gt_force_wake_mt_get(struct drm_i915_private *dev_priv) forcewake_ack = FORCEWAKE_MT_ACK; if (wait_for_atomic_us((I915_READ_NOTRACE(forcewake_ack) & 1) == 0, 500)) - DRM_ERROR("Force wake wait timed out\n"); + DRM_ERROR("Timed out waiting for forcewake old ack to clear.\n"); I915_WRITE_NOTRACE(FORCEWAKE_MT, _MASKED_BIT_ENABLE(1)); POSTING_READ(FORCEWAKE_MT); if (wait_for_atomic_us((I915_READ_NOTRACE(forcewake_ack) & 1), 500)) - DRM_ERROR("Force wake wait timed out\n"); + DRM_ERROR("Timed out waiting for forcewake to ack request.\n"); __gen6_gt_wait_for_thread_c0(dev_priv); } @@ -4070,12 +4070,12 @@ int __gen6_gt_wait_for_fifo(struct drm_i915_private *dev_priv) static void vlv_force_wake_get(struct drm_i915_private *dev_priv) { if (wait_for_atomic_us((I915_READ_NOTRACE(FORCEWAKE_ACK_VLV) & 1) == 0, 500)) - DRM_ERROR("Force wake wait timed out\n"); + DRM_ERROR("Timed out waiting for forcewake old ack to clear.\n"); I915_WRITE_NOTRACE(FORCEWAKE_VLV, _MASKED_BIT_ENABLE(1)); if (wait_for_atomic_us((I915_READ_NOTRACE(FORCEWAKE_ACK_VLV) & 1), 500)) - DRM_ERROR("Force wake wait timed out\n"); + DRM_ERROR("Timed out waiting for forcewake to ack request.\n"); __gen6_gt_wait_for_thread_c0(dev_priv); } -- cgit v0.10.2 From a2c7f6fd4d8d6dff8a561e7bd76b951808143310 Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Sat, 1 Sep 2012 20:51:22 +0100 Subject: drm/i915: Convert remaining debugfs iterators over rings to for_each_ring() For code consolidation and future maintainability. Signed-off-by: Chris Wilson Reviewed-and-Tested-by: Ben Widawsky Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/i915_debugfs.c b/drivers/gpu/drm/i915/i915_debugfs.c index 3d886af..274a328 100644 --- a/drivers/gpu/drm/i915/i915_debugfs.c +++ b/drivers/gpu/drm/i915/i915_debugfs.c @@ -353,40 +353,22 @@ static int i915_gem_request_info(struct seq_file *m, void *data) struct drm_info_node *node = (struct drm_info_node *) m->private; struct drm_device *dev = node->minor->dev; drm_i915_private_t *dev_priv = dev->dev_private; + struct intel_ring_buffer *ring; struct drm_i915_gem_request *gem_request; - int ret, count; + int ret, count, i; ret = mutex_lock_interruptible(&dev->struct_mutex); if (ret) return ret; count = 0; - if (!list_empty(&dev_priv->ring[RCS].request_list)) { - seq_printf(m, "Render requests:\n"); - list_for_each_entry(gem_request, - &dev_priv->ring[RCS].request_list, - list) { - seq_printf(m, " %d @ %d\n", - gem_request->seqno, - (int) (jiffies - gem_request->emitted_jiffies)); - } - count++; - } - if (!list_empty(&dev_priv->ring[VCS].request_list)) { - seq_printf(m, "BSD requests:\n"); - list_for_each_entry(gem_request, - &dev_priv->ring[VCS].request_list, - list) { - seq_printf(m, " %d @ %d\n", - gem_request->seqno, - (int) (jiffies - gem_request->emitted_jiffies)); - } - count++; - } - if (!list_empty(&dev_priv->ring[BCS].request_list)) { - seq_printf(m, "BLT requests:\n"); + for_each_ring(ring, dev_priv, i) { + if (list_empty(&ring->request_list)) + continue; + + seq_printf(m, "%s requests:\n", ring->name); list_for_each_entry(gem_request, - &dev_priv->ring[BCS].request_list, + &ring->request_list, list) { seq_printf(m, " %d @ %d\n", gem_request->seqno, @@ -416,14 +398,15 @@ static int i915_gem_seqno_info(struct seq_file *m, void *data) struct drm_info_node *node = (struct drm_info_node *) m->private; struct drm_device *dev = node->minor->dev; drm_i915_private_t *dev_priv = dev->dev_private; + struct intel_ring_buffer *ring; int ret, i; ret = mutex_lock_interruptible(&dev->struct_mutex); if (ret) return ret; - for (i = 0; i < I915_NUM_RINGS; i++) - i915_ring_seqno_info(m, &dev_priv->ring[i]); + for_each_ring(ring, dev_priv, i) + i915_ring_seqno_info(m, ring); mutex_unlock(&dev->struct_mutex); @@ -436,6 +419,7 @@ static int i915_interrupt_info(struct seq_file *m, void *data) struct drm_info_node *node = (struct drm_info_node *) m->private; struct drm_device *dev = node->minor->dev; drm_i915_private_t *dev_priv = dev->dev_private; + struct intel_ring_buffer *ring; int ret, i, pipe; ret = mutex_lock_interruptible(&dev->struct_mutex); @@ -513,13 +497,13 @@ static int i915_interrupt_info(struct seq_file *m, void *data) } seq_printf(m, "Interrupts received: %d\n", atomic_read(&dev_priv->irq_received)); - for (i = 0; i < I915_NUM_RINGS; i++) { + for_each_ring(ring, dev_priv, i) { if (IS_GEN6(dev) || IS_GEN7(dev)) { - seq_printf(m, "Graphics Interrupt mask (%s): %08x\n", - dev_priv->ring[i].name, - I915_READ_IMR(&dev_priv->ring[i])); + seq_printf(m, + "Graphics Interrupt mask (%s): %08x\n", + ring->name, I915_READ_IMR(ring)); } - i915_ring_seqno_info(m, &dev_priv->ring[i]); + i915_ring_seqno_info(m, ring); } mutex_unlock(&dev->struct_mutex); @@ -1529,9 +1513,7 @@ static int i915_ppgtt_info(struct seq_file *m, void *data) if (INTEL_INFO(dev)->gen == 6) seq_printf(m, "GFX_MODE: 0x%08x\n", I915_READ(GFX_MODE)); - for (i = 0; i < I915_NUM_RINGS; i++) { - ring = &dev_priv->ring[i]; - + for_each_ring(ring, dev_priv, i) { seq_printf(m, "%s\n", ring->name); if (INTEL_INFO(dev)->gen == 7) seq_printf(m, "GFX_MODE: 0x%08x\n", I915_READ(RING_MODE_GEN7(ring))); -- cgit v0.10.2 From ff04b35af0e40956764596e3d032f786e5451238 Mon Sep 17 00:00:00 2001 From: Jani Nikula Date: Wed, 29 Aug 2012 14:08:42 +0300 Subject: drm/i915: only enable sdvo hotplug irq if needed MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Avoid constant wakeups caused by noisy irq lines when we don't even care about the irq. This should be particularly useful for i945g/gm where the hotplug has been disabled: commit 768b107e4b3be0acf6f58e914afe4f337c00932b Author: Daniel Vetter Date: Fri May 4 11:29:56 2012 +0200 drm/i915: disable sdvo hotplug on i945g/gm v2: While at it, remove the bogus hotplug_active read, and do not mask hotplug_active[0] before checking whether the irq is needed, per discussion with Daniel on IRC. Bugzilla: https://bugzilla.kernel.org/show_bug.cgi?id=38442 Tested-by: Dominik Köppl Signed-off-by: Jani Nikula Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/intel_sdvo.c b/drivers/gpu/drm/i915/intel_sdvo.c index 434b1d1..9a31211 100644 --- a/drivers/gpu/drm/i915/intel_sdvo.c +++ b/drivers/gpu/drm/i915/intel_sdvo.c @@ -2570,7 +2570,6 @@ bool intel_sdvo_init(struct drm_device *dev, uint32_t sdvo_reg, bool is_sdvob) hotplug_mask = intel_sdvo->is_sdvob ? SDVOB_HOTPLUG_INT_STATUS_I915 : SDVOC_HOTPLUG_INT_STATUS_I915; } - dev_priv->hotplug_supported_mask |= hotplug_mask; drm_encoder_helper_add(&intel_encoder->base, &intel_sdvo_helper_funcs); @@ -2578,14 +2577,6 @@ bool intel_sdvo_init(struct drm_device *dev, uint32_t sdvo_reg, bool is_sdvob) if (!intel_sdvo_get_capabilities(intel_sdvo, &intel_sdvo->caps)) goto err; - /* Set up hotplug command - note paranoia about contents of reply. - * We assume that the hardware is in a sane state, and only touch - * the bits we think we understand. - */ - intel_sdvo_get_value(intel_sdvo, SDVO_CMD_GET_ACTIVE_HOT_PLUG, - &intel_sdvo->hotplug_active, 2); - intel_sdvo->hotplug_active[0] &= ~0x3; - if (intel_sdvo_output_setup(intel_sdvo, intel_sdvo->caps.output_flags) != true) { DRM_DEBUG_KMS("SDVO output failed to setup on %s\n", @@ -2593,6 +2584,12 @@ bool intel_sdvo_init(struct drm_device *dev, uint32_t sdvo_reg, bool is_sdvob) goto err; } + /* Only enable the hotplug irq if we need it, to work around noisy + * hotplug lines. + */ + if (intel_sdvo->hotplug_active[0]) + dev_priv->hotplug_supported_mask |= hotplug_mask; + intel_sdvo_select_ddc_bus(dev_priv, intel_sdvo, sdvo_reg); /* Set the input timing to the screen. Assume always input 0. */ -- cgit v0.10.2 From 5fa7ac9c9cadd598a22c09660c270bdb92da1e91 Mon Sep 17 00:00:00 2001 From: Jani Nikula Date: Wed, 29 Aug 2012 16:43:58 +0300 Subject: drm/i915: fix sdvo hotplug support check and activation The sdvo hotplug support check and activation has worked by coincidence for TMDS0. The boolean value returned by intel_sdvo_supports_hotplug() was masked with a bit shifted by device number, which also should have been one of SDVO_OUTPUT_* bits instead. Boolean true masked with 1 shifted by 0 just happened to match SDVO_OUTPUT_TMDS0... Get hotplug support as a bit mask, check the correct bits for support, and use the correct bits for activating hotplug support. Signed-off-by: Jani Nikula Reviewed-by: Simon Farnsworth Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/intel_sdvo.c b/drivers/gpu/drm/i915/intel_sdvo.c index 9a31211..6c9a857 100644 --- a/drivers/gpu/drm/i915/intel_sdvo.c +++ b/drivers/gpu/drm/i915/intel_sdvo.c @@ -97,7 +97,7 @@ struct intel_sdvo { /* * Hotplug activation bits for this device */ - uint8_t hotplug_active[2]; + uint16_t hotplug_active; /** * This is used to select the color range of RBG outputs in HDMI mode. @@ -1251,25 +1251,29 @@ static bool intel_sdvo_get_capabilities(struct intel_sdvo *intel_sdvo, struct in return true; } -static int intel_sdvo_supports_hotplug(struct intel_sdvo *intel_sdvo) +static uint16_t intel_sdvo_get_hotplug_support(struct intel_sdvo *intel_sdvo) { struct drm_device *dev = intel_sdvo->base.base.dev; - u8 response[2]; + uint16_t hotplug; /* HW Erratum: SDVO Hotplug is broken on all i945G chips, there's noise * on the line. */ if (IS_I945G(dev) || IS_I945GM(dev)) - return false; + return 0; + + if (!intel_sdvo_get_value(intel_sdvo, SDVO_CMD_GET_HOT_PLUG_SUPPORT, + &hotplug, sizeof(hotplug))) + return 0; - return intel_sdvo_get_value(intel_sdvo, SDVO_CMD_GET_HOT_PLUG_SUPPORT, - &response, 2) && response[0]; + return hotplug; } static void intel_sdvo_enable_hotplug(struct intel_encoder *encoder) { struct intel_sdvo *intel_sdvo = to_intel_sdvo(&encoder->base); - intel_sdvo_write_cmd(intel_sdvo, SDVO_CMD_SET_ACTIVE_HOT_PLUG, &intel_sdvo->hotplug_active, 2); + intel_sdvo_write_cmd(intel_sdvo, SDVO_CMD_SET_ACTIVE_HOT_PLUG, + &intel_sdvo->hotplug_active, 2); } static bool @@ -2061,17 +2065,18 @@ intel_sdvo_dvi_init(struct intel_sdvo *intel_sdvo, int device) intel_connector = &intel_sdvo_connector->base; connector = &intel_connector->base; - if (intel_sdvo_supports_hotplug(intel_sdvo) & (1 << device)) { + if (intel_sdvo_get_hotplug_support(intel_sdvo) & + intel_sdvo_connector->output_flag) { connector->polled = DRM_CONNECTOR_POLL_HPD; - intel_sdvo->hotplug_active[0] |= 1 << device; + intel_sdvo->hotplug_active |= intel_sdvo_connector->output_flag; /* Some SDVO devices have one-shot hotplug interrupts. * Ensure that they get re-enabled when an interrupt happens. */ intel_encoder->hot_plug = intel_sdvo_enable_hotplug; intel_sdvo_enable_hotplug(intel_encoder); - } - else + } else { connector->polled = DRM_CONNECTOR_POLL_CONNECT | DRM_CONNECTOR_POLL_DISCONNECT; + } encoder->encoder_type = DRM_MODE_ENCODER_TMDS; connector->connector_type = DRM_MODE_CONNECTOR_DVID; @@ -2587,7 +2592,7 @@ bool intel_sdvo_init(struct drm_device *dev, uint32_t sdvo_reg, bool is_sdvob) /* Only enable the hotplug irq if we need it, to work around noisy * hotplug lines. */ - if (intel_sdvo->hotplug_active[0]) + if (intel_sdvo->hotplug_active) dev_priv->hotplug_supported_mask |= hotplug_mask; intel_sdvo_select_ddc_bus(dev_priv, intel_sdvo, sdvo_reg); -- cgit v0.10.2 From b67a4376d53e1ae6eaef05389cfd0e6e5b7097db Mon Sep 17 00:00:00 2001 From: Ben Widawsky Date: Sat, 1 Sep 2012 22:59:47 -0700 Subject: drm/i915: Extract forcewake ack timeout It's used all over the place, and we want to be able to play around with the value, apparently. Note that it doesn't touch other timeouts of the same value (like gtfifo, and thread C0 wait). Signed-off-by: Ben Widawsky Reviewed-by: Chris Wilson [danvet: fixup conflict with vlv forcewake patches.] Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c index ec7fbf9..fd8d37f 100644 --- a/drivers/gpu/drm/i915/intel_pm.c +++ b/drivers/gpu/drm/i915/intel_pm.c @@ -31,6 +31,8 @@ #include "../../../platform/x86/intel_ips.h" #include +#define FORCEWAKE_ACK_TIMEOUT_US 500 + /* FBC, or Frame Buffer Compression, is a technique employed to compress the * framebuffer contents in-memory, aiming at reducing the required bandwidth * during in-memory transfers and, therefore, reduce the power packet. @@ -3962,13 +3964,15 @@ static void __gen6_gt_force_wake_get(struct drm_i915_private *dev_priv) else forcewake_ack = FORCEWAKE_ACK; - if (wait_for_atomic_us((I915_READ_NOTRACE(forcewake_ack) & 1) == 0, 500)) + if (wait_for_atomic_us((I915_READ_NOTRACE(forcewake_ack) & 1) == 0, + FORCEWAKE_ACK_TIMEOUT_US)) DRM_ERROR("Timed out waiting for forcewake old ack to clear.\n"); I915_WRITE_NOTRACE(FORCEWAKE, 1); POSTING_READ(FORCEWAKE); - if (wait_for_atomic_us((I915_READ_NOTRACE(forcewake_ack) & 1), 500)) + if (wait_for_atomic_us((I915_READ_NOTRACE(forcewake_ack) & 1), + FORCEWAKE_ACK_TIMEOUT_US)) DRM_ERROR("Timed out waiting for forcewake to ack request.\n"); __gen6_gt_wait_for_thread_c0(dev_priv); @@ -3983,13 +3987,15 @@ static void __gen6_gt_force_wake_mt_get(struct drm_i915_private *dev_priv) else forcewake_ack = FORCEWAKE_MT_ACK; - if (wait_for_atomic_us((I915_READ_NOTRACE(forcewake_ack) & 1) == 0, 500)) + if (wait_for_atomic_us((I915_READ_NOTRACE(forcewake_ack) & 1) == 0, + FORCEWAKE_ACK_TIMEOUT_US)) DRM_ERROR("Timed out waiting for forcewake old ack to clear.\n"); I915_WRITE_NOTRACE(FORCEWAKE_MT, _MASKED_BIT_ENABLE(1)); POSTING_READ(FORCEWAKE_MT); - if (wait_for_atomic_us((I915_READ_NOTRACE(forcewake_ack) & 1), 500)) + if (wait_for_atomic_us((I915_READ_NOTRACE(forcewake_ack) & 1), + FORCEWAKE_ACK_TIMEOUT_US)) DRM_ERROR("Timed out waiting for forcewake to ack request.\n"); __gen6_gt_wait_for_thread_c0(dev_priv); @@ -4069,12 +4075,14 @@ int __gen6_gt_wait_for_fifo(struct drm_i915_private *dev_priv) static void vlv_force_wake_get(struct drm_i915_private *dev_priv) { - if (wait_for_atomic_us((I915_READ_NOTRACE(FORCEWAKE_ACK_VLV) & 1) == 0, 500)) + if (wait_for_atomic_us((I915_READ_NOTRACE(FORCEWAKE_ACK_VLV) & 1) == 0, + FORCEWAKE_ACK_TIMEOUT_US)) DRM_ERROR("Timed out waiting for forcewake old ack to clear.\n"); I915_WRITE_NOTRACE(FORCEWAKE_VLV, _MASKED_BIT_ENABLE(1)); - if (wait_for_atomic_us((I915_READ_NOTRACE(FORCEWAKE_ACK_VLV) & 1), 500)) + if (wait_for_atomic_us((I915_READ_NOTRACE(FORCEWAKE_ACK_VLV) & 1), + FORCEWAKE_ACK_TIMEOUT_US)) DRM_ERROR("Timed out waiting for forcewake to ack request.\n"); __gen6_gt_wait_for_thread_c0(dev_priv); -- cgit v0.10.2 From 0cc2764cc4a4bd73df55f8893c871778cf7ddd0f Mon Sep 17 00:00:00 2001 From: Ben Widawsky Date: Sat, 1 Sep 2012 22:59:48 -0700 Subject: drm/i915: use cpu_relax() in wait_for_atomic As part of the advice given to us from the hardware designers regarding the maximum wait time on the forcewake handshake we need to move from us granularity to ms granularity. In earlier patches to do this, Jani noticed that wait_for_us was properly converted to use cpu_relax(), but wait_for was not. The issue has existed since the introduction of the macro: commit 913d8d110078788c14812dce8bb62c37946821d2 Author: Chris Wilson Date: Sat Aug 7 11:01:35 2010 +0100 drm/i915: Ensure that while(INREG()) are bounded (v2) CC: Jani Nikula Signed-off-by: Ben Widawsky Reviewed-by: Chris Wilson Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index ee0beb4..3f7eefc 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -41,7 +41,11 @@ ret__ = -ETIMEDOUT; \ break; \ } \ - if (W && drm_can_sleep()) msleep(W); \ + if (W && drm_can_sleep()) { \ + msleep(W); \ + } else { \ + cpu_relax(); \ + } \ } \ ret__; \ }) -- cgit v0.10.2 From 057d3860613813b2e8507bb932f9a523ad2cca2f Mon Sep 17 00:00:00 2001 From: Ben Widawsky Date: Sat, 1 Sep 2012 22:59:49 -0700 Subject: drm/i915: Change forcewake timeout to 2ms A designer familiar with the hardware has stated that the forcewake timeout can theoretically be as high as a little over 1ms. Therefore we modify our code to use 2ms (appropriate fudge and because we don't want to round down). Hopefully this can't prevent spurious timeouts. Signed-off-by: Ben Widawsky Acked-by: Chris Wilson [danvet: again fix conflict with vlv patch.] Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c index fd8d37f..31e4738 100644 --- a/drivers/gpu/drm/i915/intel_pm.c +++ b/drivers/gpu/drm/i915/intel_pm.c @@ -31,7 +31,7 @@ #include "../../../platform/x86/intel_ips.h" #include -#define FORCEWAKE_ACK_TIMEOUT_US 500 +#define FORCEWAKE_ACK_TIMEOUT_MS 2 /* FBC, or Frame Buffer Compression, is a technique employed to compress the * framebuffer contents in-memory, aiming at reducing the required bandwidth @@ -3964,15 +3964,15 @@ static void __gen6_gt_force_wake_get(struct drm_i915_private *dev_priv) else forcewake_ack = FORCEWAKE_ACK; - if (wait_for_atomic_us((I915_READ_NOTRACE(forcewake_ack) & 1) == 0, - FORCEWAKE_ACK_TIMEOUT_US)) + if (wait_for_atomic((I915_READ_NOTRACE(forcewake_ack) & 1) == 0, + FORCEWAKE_ACK_TIMEOUT_MS)) DRM_ERROR("Timed out waiting for forcewake old ack to clear.\n"); I915_WRITE_NOTRACE(FORCEWAKE, 1); POSTING_READ(FORCEWAKE); - if (wait_for_atomic_us((I915_READ_NOTRACE(forcewake_ack) & 1), - FORCEWAKE_ACK_TIMEOUT_US)) + if (wait_for_atomic((I915_READ_NOTRACE(forcewake_ack) & 1), + FORCEWAKE_ACK_TIMEOUT_MS)) DRM_ERROR("Timed out waiting for forcewake to ack request.\n"); __gen6_gt_wait_for_thread_c0(dev_priv); @@ -3987,15 +3987,15 @@ static void __gen6_gt_force_wake_mt_get(struct drm_i915_private *dev_priv) else forcewake_ack = FORCEWAKE_MT_ACK; - if (wait_for_atomic_us((I915_READ_NOTRACE(forcewake_ack) & 1) == 0, - FORCEWAKE_ACK_TIMEOUT_US)) + if (wait_for_atomic((I915_READ_NOTRACE(forcewake_ack) & 1) == 0, + FORCEWAKE_ACK_TIMEOUT_MS)) DRM_ERROR("Timed out waiting for forcewake old ack to clear.\n"); I915_WRITE_NOTRACE(FORCEWAKE_MT, _MASKED_BIT_ENABLE(1)); POSTING_READ(FORCEWAKE_MT); - if (wait_for_atomic_us((I915_READ_NOTRACE(forcewake_ack) & 1), - FORCEWAKE_ACK_TIMEOUT_US)) + if (wait_for_atomic((I915_READ_NOTRACE(forcewake_ack) & 1), + FORCEWAKE_ACK_TIMEOUT_MS)) DRM_ERROR("Timed out waiting for forcewake to ack request.\n"); __gen6_gt_wait_for_thread_c0(dev_priv); @@ -4075,14 +4075,14 @@ int __gen6_gt_wait_for_fifo(struct drm_i915_private *dev_priv) static void vlv_force_wake_get(struct drm_i915_private *dev_priv) { - if (wait_for_atomic_us((I915_READ_NOTRACE(FORCEWAKE_ACK_VLV) & 1) == 0, - FORCEWAKE_ACK_TIMEOUT_US)) + if (wait_for_atomic((I915_READ_NOTRACE(FORCEWAKE_ACK_VLV) & 1) == 0, + FORCEWAKE_ACK_TIMEOUT_MS)) DRM_ERROR("Timed out waiting for forcewake old ack to clear.\n"); I915_WRITE_NOTRACE(FORCEWAKE_VLV, _MASKED_BIT_ENABLE(1)); - if (wait_for_atomic_us((I915_READ_NOTRACE(FORCEWAKE_ACK_VLV) & 1), - FORCEWAKE_ACK_TIMEOUT_US)) + if (wait_for_atomic((I915_READ_NOTRACE(FORCEWAKE_ACK_VLV) & 1), + FORCEWAKE_ACK_TIMEOUT_MS)) DRM_ERROR("Timed out waiting for forcewake to ack request.\n"); __gen6_gt_wait_for_thread_c0(dev_priv); -- cgit v0.10.2 From 8dee3eea3ccd3b6c00a8d3a08dd715d6adf737dd Mon Sep 17 00:00:00 2001 From: Ben Widawsky Date: Sat, 1 Sep 2012 22:59:50 -0700 Subject: drm/i915: Never read FORCEWAKE The same designer from the previous patch has told us to never read FORCEWAKE. We only do this for the POSTING_READ(), so simply change that to something within the same cacheline (for no reason in particular other than it sounds nice). In the _mt case we can leverage the gtfifodbg check for the POSTING_READ. This partially reverts commit 6af2d180f82151cf3d58952e35a4f96e45bc453a Author: Daniel Vetter Date: Thu Jul 26 16:24:50 2012 +0200 drm/i915: fix forcewake related hangs on snb v2: commit message, comments about posting read from (Daniel) Note: vlv forcewake doesn't need any changes for this special treatment since FORCEWAKE_VLV is in a totally different register range, and the readback FORCEWAKE_ACK_VLV readback that follows is in the same range. Signed-off-by: Ben Widawsky Acked-by: Chris Wilson [danvet: Added note.] Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c index 31e4738..36c6409 100644 --- a/drivers/gpu/drm/i915/intel_pm.c +++ b/drivers/gpu/drm/i915/intel_pm.c @@ -3969,7 +3969,7 @@ static void __gen6_gt_force_wake_get(struct drm_i915_private *dev_priv) DRM_ERROR("Timed out waiting for forcewake old ack to clear.\n"); I915_WRITE_NOTRACE(FORCEWAKE, 1); - POSTING_READ(FORCEWAKE); + POSTING_READ(ECOBUS); /* something from same cacheline, but !FORCEWAKE */ if (wait_for_atomic((I915_READ_NOTRACE(forcewake_ack) & 1), FORCEWAKE_ACK_TIMEOUT_MS)) @@ -3992,7 +3992,7 @@ static void __gen6_gt_force_wake_mt_get(struct drm_i915_private *dev_priv) DRM_ERROR("Timed out waiting for forcewake old ack to clear.\n"); I915_WRITE_NOTRACE(FORCEWAKE_MT, _MASKED_BIT_ENABLE(1)); - POSTING_READ(FORCEWAKE_MT); + POSTING_READ(ECOBUS); /* something from same cacheline, but !FORCEWAKE */ if (wait_for_atomic((I915_READ_NOTRACE(forcewake_ack) & 1), FORCEWAKE_ACK_TIMEOUT_MS)) @@ -4029,14 +4029,14 @@ void gen6_gt_check_fifodbg(struct drm_i915_private *dev_priv) static void __gen6_gt_force_wake_put(struct drm_i915_private *dev_priv) { I915_WRITE_NOTRACE(FORCEWAKE, 0); - POSTING_READ(FORCEWAKE); + /* gen6_gt_check_fifodbg doubles as the POSTING_READ */ gen6_gt_check_fifodbg(dev_priv); } static void __gen6_gt_force_wake_mt_put(struct drm_i915_private *dev_priv) { I915_WRITE_NOTRACE(FORCEWAKE_MT, _MASKED_BIT_DISABLE(1)); - POSTING_READ(FORCEWAKE_MT); + /* gen6_gt_check_fifodbg doubles as the POSTING_READ */ gen6_gt_check_fifodbg(dev_priv); } -- cgit v0.10.2 From 95ca19cf8cbf6163805dc9dc6a83f73b3e75ea13 Mon Sep 17 00:00:00 2001 From: Sedat Dilek Date: Fri, 24 Aug 2012 21:20:03 +0200 Subject: drm: udl: usb: Fix recursive Kconfig dependency In drivers/usb/Kconfig "config USB_ARCH_HAS_HCD" is within "if USB_SUPPORT" statement. In drivers/gpu/drm/Kconfig "config DRM_USB" depends on USB_ARCH_HAS_HCD but selects USB_SUPPORT which leads to the error for udl Kconfig: $ yes "" | make oldconfig scripts/kconfig/conf --oldconfig Kconfig drivers/gpu/drm/udl/Kconfig:1:error: recursive dependency detected! drivers/gpu/drm/udl/Kconfig:1: symbol DRM_UDL depends on USB_ARCH_HAS_HCD drivers/usb/Kconfig:76: symbol USB_ARCH_HAS_HCD depends on USB_SUPPORT drivers/usb/Kconfig:58: symbol USB_SUPPORT is selected by DRM_USB drivers/gpu/drm/Kconfig:22: symbol DRM_USB is selected by DRM_UDL Fix this by changing from select to depends on USB_SUPPORT in "config DRM_USB". This is a follow-up fix to df0b344300724e00db9fff7eb6406eb91f450b91 in Dave's drm-next GIT branch. [ v2: Restore old status, but change from select to depends on USB_SUPPORT ] Signed-off-by: Sedat Dilek Signed-off-by: Dave Airlie diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig index 3a8c683..0cbdc45 100644 --- a/drivers/gpu/drm/Kconfig +++ b/drivers/gpu/drm/Kconfig @@ -22,9 +22,8 @@ menuconfig DRM config DRM_USB tristate depends on DRM - depends on USB_ARCH_HAS_HCD + depends on USB_SUPPORT && USB_ARCH_HAS_HCD select USB - select USB_SUPPORT config DRM_KMS_HELPER tristate -- cgit v0.10.2 From 8c3f929b6147e142efc58d5d03dc6fa703b14a5d Mon Sep 17 00:00:00 2001 From: Ben Widawsky Date: Sun, 2 Sep 2012 00:24:40 -0700 Subject: drm/i915: Enable some sysfs stuff without CONFIG_PM The original patch was actually incorrect in stubbing out the sysfs for l3 parity. commit 5ab3633d6907018b0b830a720e877c3884d679c3 Author: Hunt Xu Date: Sun Jul 1 03:45:07 2012 +0000 drm/i915: make rc6 in sysfs functions conditional Unfortunately Hunt didn't respond to my review comments, and Daniel sucked in the patch again ignoring. Worst of all, I'm too lazy to write the patch for what I originally wanted, which was to keep rc6 sysfs even without CONFIG_PM. This simpler patch does enough to enable us to add more sysfs entries though. Signed-off-by: Ben Widawsky Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/i915_sysfs.c b/drivers/gpu/drm/i915/i915_sysfs.c index c5ee7ee..da733a3 100644 --- a/drivers/gpu/drm/i915/i915_sysfs.c +++ b/drivers/gpu/drm/i915/i915_sysfs.c @@ -93,6 +93,7 @@ static struct attribute_group rc6_attr_group = { .name = power_group_name, .attrs = rc6_attrs }; +#endif static int l3_access_valid(struct drm_device *dev, loff_t offset) { @@ -206,13 +207,14 @@ void i915_setup_sysfs(struct drm_device *dev) { int ret; +#ifdef CONFIG_PM if (INTEL_INFO(dev)->gen >= 6) { ret = sysfs_merge_group(&dev->primary->kdev.kobj, &rc6_attr_group); if (ret) DRM_ERROR("RC6 residency sysfs setup failed\n"); } - +#endif if (HAS_L3_GPU_CACHE(dev)) { ret = device_create_bin_file(&dev->primary->kdev, &dpf_attrs); if (ret) @@ -225,14 +227,3 @@ void i915_teardown_sysfs(struct drm_device *dev) device_remove_bin_file(&dev->primary->kdev, &dpf_attrs); sysfs_unmerge_group(&dev->primary->kdev.kobj, &rc6_attr_group); } -#else -void i915_setup_sysfs(struct drm_device *dev) -{ - return; -} - -void i915_teardown_sysfs(struct drm_device *dev) -{ - return; -} -#endif /* CONFIG_PM */ -- cgit v0.10.2 From 76e5a89c0a01c43b09512da633910dd0d8b398be Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Fri, 29 Jun 2012 22:39:33 +0200 Subject: drm/i915: add crtc->enable/disable vfuncs insted of dpms Because that's what we're essentially calling. This is the first step in untangling the crtc_helper induced dpms handling mess we have - at the crtc level we only have 2 states and the magic is just in selecting which one (and atm there isn't even much magic, but on recent platforms where not even the crt output has more than 2 states we could do better). Reviewed-by: Jesse Barnes Signed-Off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index ed3ba70..fb46c6f 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -240,7 +240,6 @@ struct drm_i915_error_state { }; struct drm_i915_display_funcs { - void (*dpms)(struct drm_crtc *crtc, int mode); bool (*fbc_enabled)(struct drm_device *dev); void (*enable_fbc)(struct drm_crtc *crtc, unsigned long interval); void (*disable_fbc)(struct drm_device *dev); @@ -256,6 +255,8 @@ struct drm_i915_display_funcs { struct drm_display_mode *adjusted_mode, int x, int y, struct drm_framebuffer *old_fb); + void (*crtc_enable)(struct drm_crtc *crtc); + void (*crtc_disable)(struct drm_crtc *crtc); void (*off)(struct drm_crtc *crtc); void (*write_eld)(struct drm_connector *connector, struct drm_crtc *crtc); diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 42c5758..adc9868 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -3342,30 +3342,6 @@ static void ironlake_crtc_disable(struct drm_crtc *crtc) mutex_unlock(&dev->struct_mutex); } -static void ironlake_crtc_dpms(struct drm_crtc *crtc, int mode) -{ - struct intel_crtc *intel_crtc = to_intel_crtc(crtc); - int pipe = intel_crtc->pipe; - int plane = intel_crtc->plane; - - /* XXX: When our outputs are all unaware of DPMS modes other than off - * and on, we should map those modes to DRM_MODE_DPMS_OFF in the CRTC. - */ - switch (mode) { - case DRM_MODE_DPMS_ON: - case DRM_MODE_DPMS_STANDBY: - case DRM_MODE_DPMS_SUSPEND: - DRM_DEBUG_KMS("crtc %d/%d dpms on\n", pipe, plane); - ironlake_crtc_enable(crtc); - break; - - case DRM_MODE_DPMS_OFF: - DRM_DEBUG_KMS("crtc %d/%d dpms off\n", pipe, plane); - ironlake_crtc_disable(crtc); - break; - } -} - static void ironlake_crtc_off(struct drm_crtc *crtc) { struct intel_crtc *intel_crtc = to_intel_crtc(crtc); @@ -3445,23 +3421,6 @@ static void i9xx_crtc_disable(struct drm_crtc *crtc) intel_update_watermarks(dev); } -static void i9xx_crtc_dpms(struct drm_crtc *crtc, int mode) -{ - /* XXX: When our outputs are all unaware of DPMS modes other than off - * and on, we should map those modes to DRM_MODE_DPMS_OFF in the CRTC. - */ - switch (mode) { - case DRM_MODE_DPMS_ON: - case DRM_MODE_DPMS_STANDBY: - case DRM_MODE_DPMS_SUSPEND: - i9xx_crtc_enable(crtc); - break; - case DRM_MODE_DPMS_OFF: - i9xx_crtc_disable(crtc); - break; - } -} - static void i9xx_crtc_off(struct drm_crtc *crtc) { } @@ -3483,7 +3442,20 @@ static void intel_crtc_dpms(struct drm_crtc *crtc, int mode) intel_crtc->dpms_mode = mode; - dev_priv->display.dpms(crtc, mode); + /* XXX: When our outputs are all unaware of DPMS modes other than off + * and on, we should map those modes to DRM_MODE_DPMS_OFF in the CRTC. + */ + switch (mode) { + case DRM_MODE_DPMS_ON: + case DRM_MODE_DPMS_STANDBY: + case DRM_MODE_DPMS_SUSPEND: + dev_priv->display.crtc_enable(crtc); + break; + + case DRM_MODE_DPMS_OFF: + dev_priv->display.crtc_disable(crtc); + break; + } if (!dev->primary->master) return; @@ -6971,13 +6943,15 @@ static void intel_init_display(struct drm_device *dev) /* We always want a DPMS function */ if (HAS_PCH_SPLIT(dev)) { - dev_priv->display.dpms = ironlake_crtc_dpms; dev_priv->display.crtc_mode_set = ironlake_crtc_mode_set; + dev_priv->display.crtc_enable = ironlake_crtc_enable; + dev_priv->display.crtc_disable = ironlake_crtc_disable; dev_priv->display.off = ironlake_crtc_off; dev_priv->display.update_plane = ironlake_update_plane; } else { - dev_priv->display.dpms = i9xx_crtc_dpms; dev_priv->display.crtc_mode_set = i9xx_crtc_mode_set; + dev_priv->display.crtc_enable = i9xx_crtc_enable; + dev_priv->display.crtc_disable = i9xx_crtc_disable; dev_priv->display.off = i9xx_crtc_off; dev_priv->display.update_plane = i9xx_update_plane; } -- cgit v0.10.2 From eae307a5304f8c0956b8eab75596abba7ad58a5c Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Fri, 29 Jun 2012 22:53:09 +0200 Subject: drm/i915: rip out crtc prepare/commit indirection Just impendance matching with the the crtc helper stuff. ... and somehow the design of this all ended up in this commit here, too ;-) The big plan is that this new set of crtc display_funcs take full responsibility of modeset operations for the entire display output pipeline (by calling down into object-specific callbacks and functions). The platform-specific callbacks simply know best what the proper order is. This has the drawback that we can't do minimal change-overs any more if a modeset just disables one encoder in a cloned configuration (because we will only expose a disable/enable action that takes down/sets up the entire crtc including all encoders). Imo that's the only sane way to do it though: - The use-case for this is pretty minimal, even when presenting (at least sane people) should use a dual-screen output so that you can see your notes on your panel. Clone mode is imo BS. - With all the clone mode constrains, shared resources, and special ordering requirements (which differ even on the same platform sometimes for different outputs) there's no way we'd get this right for all cases. Especially since this is a under-used feature. - And to top it off: On haswell even dp link re-training requires us to take down the entire display pipe - otherwise the chip dies. So the only sane way is to do a full modeset on every crtc where the output config changes in any way. To support global modeset (i.e. set the configuration for all crtcs at once) we'd then add one more function to allocate global and shared objects in the best ways (e.g. fdi links, pch plls, ...). The crtc functions would then simply use the pre-allocated stuff (and shouldn't be able to fail, ever). We could even do all the object pinning in there (and maybe try to defragment the global gtt if we fail)! Reviewed-by: Jesse Barnes Signed-Off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index adc9868..04bec4b 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -3500,34 +3500,6 @@ static void intel_crtc_disable(struct drm_crtc *crtc) } } -/* Prepare for a mode set. - * - * Note we could be a lot smarter here. We need to figure out which outputs - * will be enabled, which disabled (in short, how the config will changes) - * and perform the minimum necessary steps to accomplish that, e.g. updating - * watermarks, FBC configuration, making sure PLLs are programmed correctly, - * panel fitting is in the proper state, etc. - */ -static void i9xx_crtc_prepare(struct drm_crtc *crtc) -{ - i9xx_crtc_disable(crtc); -} - -static void i9xx_crtc_commit(struct drm_crtc *crtc) -{ - i9xx_crtc_enable(crtc); -} - -static void ironlake_crtc_prepare(struct drm_crtc *crtc) -{ - ironlake_crtc_disable(crtc); -} - -static void ironlake_crtc_commit(struct drm_crtc *crtc) -{ - ironlake_crtc_enable(crtc); -} - void intel_encoder_prepare(struct drm_encoder *encoder) { struct drm_encoder_helper_funcs *encoder_funcs = encoder->helper_private; @@ -6626,13 +6598,8 @@ static void intel_crtc_init(struct drm_device *dev, int pipe) intel_crtc->active = true; /* force the pipe off on setup_init_config */ intel_crtc->bpp = 24; /* default for pre-Ironlake */ - if (HAS_PCH_SPLIT(dev)) { - intel_helper_funcs.prepare = ironlake_crtc_prepare; - intel_helper_funcs.commit = ironlake_crtc_commit; - } else { - intel_helper_funcs.prepare = i9xx_crtc_prepare; - intel_helper_funcs.commit = i9xx_crtc_commit; - } + intel_helper_funcs.prepare = dev_priv->display.crtc_disable; + intel_helper_funcs.commit = dev_priv->display.crtc_enable; drm_crtc_helper_add(&intel_crtc->base, &intel_helper_funcs); } -- cgit v0.10.2 From ef9c3aee60442d1eaf81fbd7b63f256410cc0aa9 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Fri, 29 Jun 2012 22:40:09 +0200 Subject: drm/i915: add direct encoder disable/enable infrastructure Just prep work, not yet put to some use. Note that because we're still using the crtc helper to switch modes (and their complicated way to do partial modesets), we need to call the encoder's disable function unconditionally. But once this is cleaned up we shouldn't call the encoder's disable function unconditionally any more, because then we know that we'll only call it if the encoder is actually enabled. Also note that we then need to be careful about which crtc we're filtering the encoder list on: We want to filter on the crtc of the _current_ mode, not the one we're about to set up. For the enabling side we need to do the same trick. And again, we should be able to simplify this quite a bit when things have settled into place. Also note that this simply does not take cloning into account, so dpms needs to be handled specially for the few outputs where we even bother with it. Reviewed-by: Jesse Barnes Signed-Off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 04bec4b..82aaded 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -3209,13 +3209,16 @@ static void ironlake_crtc_enable(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); + struct intel_encoder *encoder; int pipe = intel_crtc->pipe; int plane = intel_crtc->plane; u32 temp; bool is_pch_port; + /* XXX: For compatability with the crtc helper code, call the encoder's + * enable function unconditionally for now. */ if (intel_crtc->active) - return; + goto encoders; intel_crtc->active = true; intel_update_watermarks(dev); @@ -3262,6 +3265,12 @@ static void ironlake_crtc_enable(struct drm_crtc *crtc) mutex_unlock(&dev->struct_mutex); intel_crtc_update_cursor(crtc, true); + +encoders: + for_each_encoder_on_crtc(dev, crtc, encoder) { + if (encoder->enable) + encoder->enable(encoder); + } } static void ironlake_crtc_disable(struct drm_crtc *crtc) @@ -3269,10 +3278,18 @@ static void ironlake_crtc_disable(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); + struct intel_encoder *encoder; int pipe = intel_crtc->pipe; int plane = intel_crtc->plane; u32 reg, temp; + /* XXX: For compatability with the crtc helper code, call the encoder's + * disable function unconditionally for now. */ + for_each_encoder_on_crtc(dev, crtc, encoder) { + if (encoder->disable) + encoder->disable(encoder); + } + if (!intel_crtc->active) return; @@ -3371,11 +3388,14 @@ static void i9xx_crtc_enable(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); + struct intel_encoder *encoder; int pipe = intel_crtc->pipe; int plane = intel_crtc->plane; + /* XXX: For compatability with the crtc helper code, call the encoder's + * enable function unconditionally for now. */ if (intel_crtc->active) - return; + goto encoders; intel_crtc->active = true; intel_update_watermarks(dev); @@ -3390,6 +3410,12 @@ static void i9xx_crtc_enable(struct drm_crtc *crtc) /* Give the overlay scaler a chance to enable if it's on this pipe */ intel_crtc_dpms_overlay(intel_crtc, true); intel_crtc_update_cursor(crtc, true); + +encoders: + for_each_encoder_on_crtc(dev, crtc, encoder) { + if (encoder->enable) + encoder->enable(encoder); + } } static void i9xx_crtc_disable(struct drm_crtc *crtc) @@ -3397,9 +3423,17 @@ static void i9xx_crtc_disable(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); + struct intel_encoder *encoder; int pipe = intel_crtc->pipe; int plane = intel_crtc->plane; + /* XXX: For compatability with the crtc helper code, call the encoder's + * disable function unconditionally for now. */ + for_each_encoder_on_crtc(dev, crtc, encoder) { + if (encoder->disable) + encoder->disable(encoder); + } + if (!intel_crtc->active) return; diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index 95f635b..9a5adcc 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -141,6 +141,8 @@ struct intel_encoder { */ bool cloneable; void (*hot_plug)(struct intel_encoder *); + void (*enable)(struct intel_encoder *); + void (*disable)(struct intel_encoder *); int crtc_mask; }; -- cgit v0.10.2 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 diff --git a/drivers/gpu/drm/i915/intel_ddi.c b/drivers/gpu/drm/i915/intel_ddi.c index 170e386..38a7006 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 82aaded..cbd356f 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 9a5adcc..759dcba 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 e4c37bb..acddaaa 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 v0.10.2 From 6b5756a046b6917e2aba4166af3bca0982c65d8d Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Sat, 30 Jun 2012 10:33:44 +0200 Subject: drm/i915/tv: convert to encoder enable/disable Like hdmi tv outputs are simple: They only have 2 states and can't be cloned. Hence give it the same treatment. Reviewed-by: Jesse Barnes Signed-Off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/intel_tv.c b/drivers/gpu/drm/i915/intel_tv.c index 95653a5..281e0cf 100644 --- a/drivers/gpu/drm/i915/intel_tv.c +++ b/drivers/gpu/drm/i915/intel_tv.c @@ -837,21 +837,21 @@ static struct intel_tv *intel_attached_tv(struct drm_connector *connector) } static void -intel_tv_dpms(struct drm_encoder *encoder, int mode) +intel_enable_tv(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; - switch (mode) { - case DRM_MODE_DPMS_ON: - I915_WRITE(TV_CTL, I915_READ(TV_CTL) | TV_ENC_ENABLE); - break; - case DRM_MODE_DPMS_STANDBY: - case DRM_MODE_DPMS_SUSPEND: - case DRM_MODE_DPMS_OFF: - I915_WRITE(TV_CTL, I915_READ(TV_CTL) & ~TV_ENC_ENABLE); - break; - } + I915_WRITE(TV_CTL, I915_READ(TV_CTL) | TV_ENC_ENABLE); +} + +static void +intel_disable_tv(struct intel_encoder *encoder) +{ + struct drm_device *dev = encoder->base.dev; + struct drm_i915_private *dev_priv = dev->dev_private; + + I915_WRITE(TV_CTL, I915_READ(TV_CTL) & ~TV_ENC_ENABLE); } static const struct tv_mode * @@ -1478,15 +1478,15 @@ out: } static const struct drm_encoder_helper_funcs intel_tv_helper_funcs = { - .dpms = intel_tv_dpms, .mode_fixup = intel_tv_mode_fixup, - .prepare = intel_encoder_prepare, + .prepare = intel_encoder_noop, .mode_set = intel_tv_mode_set, - .commit = intel_encoder_commit, + .commit = intel_encoder_noop, + .disable = intel_encoder_disable, }; static const struct drm_connector_funcs intel_tv_connector_funcs = { - .dpms = drm_helper_connector_dpms, + .dpms = intel_connector_dpms, .detect = intel_tv_detect, .destroy = intel_tv_destroy, .set_property = intel_tv_set_property, @@ -1616,6 +1616,9 @@ intel_tv_init(struct drm_device *dev) drm_encoder_init(dev, &intel_encoder->base, &intel_tv_enc_funcs, DRM_MODE_ENCODER_TVDAC); + intel_encoder->enable = intel_enable_tv; + intel_encoder->disable = intel_disable_tv; + intel_connector_attach_encoder(intel_connector, intel_encoder); intel_encoder->type = INTEL_OUTPUT_TVOUT; intel_encoder->crtc_mask = (1 << 0) | (1 << 1); -- cgit v0.10.2 From c22834ecbb9ff4af9d82c40aa0984c5a67b7e497 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Sat, 30 Jun 2012 15:31:28 +0200 Subject: drm/i915/lvds: convert to encoder disable/enable With the previous patch LVDS is also a simple case. Treat it accordingly. Reviewed-by: Jesse Barnes Signed-Off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/intel_lvds.c b/drivers/gpu/drm/i915/intel_lvds.c index d789fda..3baa224 100644 --- a/drivers/gpu/drm/i915/intel_lvds.c +++ b/drivers/gpu/drm/i915/intel_lvds.c @@ -68,10 +68,11 @@ static struct intel_lvds *intel_attached_lvds(struct drm_connector *connector) /** * Sets the power state for the panel. */ -static void intel_lvds_enable(struct intel_lvds *intel_lvds) +static void intel_enable_lvds(struct intel_encoder *encoder) { - struct drm_device *dev = intel_lvds->base.base.dev; - struct intel_crtc *intel_crtc = to_intel_crtc(intel_lvds->base.base.crtc); + struct drm_device *dev = encoder->base.dev; + struct intel_lvds *intel_lvds = to_intel_lvds(&encoder->base); + struct intel_crtc *intel_crtc = to_intel_crtc(encoder->base.crtc); struct drm_i915_private *dev_priv = dev->dev_private; u32 ctl_reg, lvds_reg, stat_reg; @@ -111,9 +112,10 @@ static void intel_lvds_enable(struct intel_lvds *intel_lvds) intel_panel_enable_backlight(dev, intel_crtc->pipe); } -static void intel_lvds_disable(struct intel_lvds *intel_lvds) +static void intel_disable_lvds(struct intel_encoder *encoder) { - struct drm_device *dev = intel_lvds->base.base.dev; + struct drm_device *dev = encoder->base.dev; + struct intel_lvds *intel_lvds = to_intel_lvds(&encoder->base); struct drm_i915_private *dev_priv = dev->dev_private; u32 ctl_reg, lvds_reg, stat_reg; @@ -142,18 +144,6 @@ static void intel_lvds_disable(struct intel_lvds *intel_lvds) POSTING_READ(lvds_reg); } -static void intel_lvds_dpms(struct drm_encoder *encoder, int mode) -{ - struct intel_lvds *intel_lvds = to_intel_lvds(encoder); - - if (mode == DRM_MODE_DPMS_ON) - intel_lvds_enable(intel_lvds); - else - intel_lvds_disable(intel_lvds); - - /* XXX: We never power down the LVDS pairs. */ -} - static int intel_lvds_mode_valid(struct drm_connector *connector, struct drm_display_mode *mode) { @@ -405,23 +395,6 @@ out: return true; } -static void intel_lvds_prepare(struct drm_encoder *encoder) -{ - struct intel_lvds *intel_lvds = to_intel_lvds(encoder); - - intel_lvds_disable(intel_lvds); -} - -static void intel_lvds_commit(struct drm_encoder *encoder) -{ - struct intel_lvds *intel_lvds = to_intel_lvds(encoder); - - /* Always do a full power on as we do not know what state - * we were left in. - */ - intel_lvds_enable(intel_lvds); -} - static void intel_lvds_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode) @@ -596,11 +569,11 @@ static int intel_lvds_set_property(struct drm_connector *connector, } static const struct drm_encoder_helper_funcs intel_lvds_helper_funcs = { - .dpms = intel_lvds_dpms, .mode_fixup = intel_lvds_mode_fixup, - .prepare = intel_lvds_prepare, + .prepare = intel_encoder_noop, .mode_set = intel_lvds_mode_set, - .commit = intel_lvds_commit, + .commit = intel_encoder_noop, + .disable = intel_encoder_disable, }; static const struct drm_connector_helper_funcs intel_lvds_connector_helper_funcs = { @@ -610,7 +583,7 @@ static const struct drm_connector_helper_funcs intel_lvds_connector_helper_funcs }; static const struct drm_connector_funcs intel_lvds_connector_funcs = { - .dpms = drm_helper_connector_dpms, + .dpms = intel_connector_dpms, .detect = intel_lvds_detect, .fill_modes = drm_helper_probe_single_connector_modes, .set_property = intel_lvds_set_property, @@ -964,6 +937,9 @@ bool intel_lvds_init(struct drm_device *dev) drm_encoder_init(dev, &intel_encoder->base, &intel_lvds_enc_funcs, DRM_MODE_ENCODER_LVDS); + intel_encoder->enable = intel_enable_lvds; + intel_encoder->disable = intel_disable_lvds; + intel_connector_attach_encoder(intel_connector, intel_encoder); intel_encoder->type = INTEL_OUTPUT_LVDS; -- cgit v0.10.2 From e8cb455876fa8f67c6aba394d0a14b697bf04cc3 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Sun, 1 Jul 2012 13:05:48 +0200 Subject: drm/i915/dp: convert to encoder disable/enable DP is the first encoder which isn't simple. As commit d240f20f545fa4ed78ce48d1eb62ab529f2b1467 Author: Jesse Barnes Date: Fri Aug 13 15:43:26 2010 -0700 drm/i915: make sure eDP PLL is enabled at the right time discovered, we need to enable the eDP PLL for the cpu port _before_ we enable the pipes and planes. After a few more commits the current solution is to enable the PLL in the dp mode_set function (because this is the only encoder callback the crtc helper code calls before it calls the crtc's commit function). Now I suspect that we actually should enable/disable the entire cpu eDP port before/after planes, but thanks to how the crtc helper code assumes that you can disable an encoder without disabling it's crtc right away, this won't work. The result is that the current prepare/commit hooks don't touch the eDP PLL, but instead it get's frobbed in dp_mode_set and in the dp dpms function. Hence we need to keep things (at least for now) bug-for-bug compatible by using our own special dp dpms function and keep everything else more-or-less as-is (just using our own infrastrucutre now). This mess can only be cleaned up once we control the entire modeset sequence and can move things around freely. v2: Squash unsupported dpms modes to OFF at the beginning of the DP dpms function. v3: Need to set the dpms state to off in dp_disable, otherwise this breaks the newly added WARNs ... v4: Rebased against edp panel off sequence changes in 3.6-rc2 Reviewed-by: Jesse Barnes Signed-Off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index 977d9d2..7ee954c 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -1250,10 +1250,9 @@ static void intel_dp_sink_dpms(struct intel_dp *intel_dp, int mode) } } -static void intel_dp_prepare(struct drm_encoder *encoder) +static void intel_disable_dp(struct intel_encoder *encoder) { - struct intel_dp *intel_dp = enc_to_intel_dp(encoder); - + struct intel_dp *intel_dp = enc_to_intel_dp(&encoder->base); /* Make sure the panel is off before trying to change the mode. But also * ensure that we have vdd while we switch off the panel. */ @@ -1262,62 +1261,64 @@ static void intel_dp_prepare(struct drm_encoder *encoder) intel_dp_sink_dpms(intel_dp, DRM_MODE_DPMS_ON); ironlake_edp_panel_off(intel_dp); intel_dp_link_down(intel_dp); + + intel_dp->dpms_mode = DRM_MODE_DPMS_OFF; } -static void intel_dp_commit(struct drm_encoder *encoder) +static void intel_enable_dp(struct intel_encoder *encoder) { - struct intel_dp *intel_dp = enc_to_intel_dp(encoder); - struct drm_device *dev = encoder->dev; - struct intel_crtc *intel_crtc = to_intel_crtc(intel_dp->base.base.crtc); + struct intel_dp *intel_dp = enc_to_intel_dp(&encoder->base); + struct drm_device *dev = encoder->base.dev; + struct drm_i915_private *dev_priv = dev->dev_private; + uint32_t dp_reg = I915_READ(intel_dp->output_reg); ironlake_edp_panel_vdd_on(intel_dp); intel_dp_sink_dpms(intel_dp, DRM_MODE_DPMS_ON); - intel_dp_start_link_train(intel_dp); - ironlake_edp_panel_on(intel_dp); - ironlake_edp_panel_vdd_off(intel_dp, true); - intel_dp_complete_link_train(intel_dp); + if (!(dp_reg & DP_PORT_EN)) { + intel_dp_start_link_train(intel_dp); + ironlake_edp_panel_on(intel_dp); + ironlake_edp_panel_vdd_off(intel_dp, true); + intel_dp_complete_link_train(intel_dp); + } else + ironlake_edp_panel_vdd_off(intel_dp, false); ironlake_edp_backlight_on(intel_dp); intel_dp->dpms_mode = DRM_MODE_DPMS_ON; - - if (HAS_PCH_CPT(dev)) - intel_cpt_verify_modeset(dev, intel_crtc->pipe); } static void -intel_dp_dpms(struct drm_encoder *encoder, int mode) +intel_dp_dpms(struct drm_connector *connector, int mode) { - struct intel_dp *intel_dp = enc_to_intel_dp(encoder); - struct drm_device *dev = encoder->dev; - struct drm_i915_private *dev_priv = dev->dev_private; - uint32_t dp_reg = I915_READ(intel_dp->output_reg); + struct intel_dp *intel_dp = intel_attached_dp(connector); + + /* DP supports only 2 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 (!intel_dp->base.base.crtc) { + intel_dp->base.connectors_active = false; + return; + } if (mode != DRM_MODE_DPMS_ON) { - /* Switching the panel off requires vdd. */ - ironlake_edp_panel_vdd_on(intel_dp); - ironlake_edp_backlight_off(intel_dp); - intel_dp_sink_dpms(intel_dp, mode); - ironlake_edp_panel_off(intel_dp); - intel_dp_link_down(intel_dp); + intel_encoder_dpms(&intel_dp->base, mode); + WARN_ON(intel_dp->dpms_mode != DRM_MODE_DPMS_OFF); if (is_cpu_edp(intel_dp)) - ironlake_edp_pll_off(encoder); + ironlake_edp_pll_off(&intel_dp->base.base); } else { if (is_cpu_edp(intel_dp)) - ironlake_edp_pll_on(encoder); + ironlake_edp_pll_on(&intel_dp->base.base); - ironlake_edp_panel_vdd_on(intel_dp); - intel_dp_sink_dpms(intel_dp, mode); - if (!(dp_reg & DP_PORT_EN)) { - intel_dp_start_link_train(intel_dp); - ironlake_edp_panel_on(intel_dp); - ironlake_edp_panel_vdd_off(intel_dp, true); - intel_dp_complete_link_train(intel_dp); - } else - ironlake_edp_panel_vdd_off(intel_dp, false); - ironlake_edp_backlight_on(intel_dp); + intel_encoder_dpms(&intel_dp->base, mode); + WARN_ON(intel_dp->dpms_mode != DRM_MODE_DPMS_ON); } - intel_dp->dpms_mode = mode; } /* @@ -2347,15 +2348,15 @@ static void intel_dp_encoder_destroy(struct drm_encoder *encoder) } static const struct drm_encoder_helper_funcs intel_dp_helper_funcs = { - .dpms = intel_dp_dpms, .mode_fixup = intel_dp_mode_fixup, - .prepare = intel_dp_prepare, + .prepare = intel_encoder_noop, .mode_set = intel_dp_mode_set, - .commit = intel_dp_commit, + .commit = intel_encoder_noop, + .disable = intel_encoder_disable, }; static const struct drm_connector_funcs intel_dp_connector_funcs = { - .dpms = drm_helper_connector_dpms, + .dpms = intel_dp_dpms, .detect = intel_dp_detect, .fill_modes = drm_helper_probe_single_connector_modes, .set_property = intel_dp_set_property, @@ -2486,6 +2487,9 @@ intel_dp_init(struct drm_device *dev, int output_reg, enum port port) intel_connector_attach_encoder(intel_connector, intel_encoder); drm_sysfs_connector_add(connector); + intel_encoder->enable = intel_enable_dp; + intel_encoder->disable = intel_disable_dp; + /* Set up the DDC bus. */ switch (port) { case PORT_A: -- cgit v0.10.2 From 2124604b09b1f405d438815d797530c43269cc58 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Sun, 1 Jul 2012 14:58:27 +0200 Subject: drm/i915/crt: convert to encoder disable/enable CRT is the first output which can be cloned, hence we cannot (yet) move the dpms handling over to disable/enable. This requires some more smarts in intel_crtc_dpms first to set the display pipe status depening upon encoder->connectors_active of all connected encoders. Because that will happen in a separate step, don't touch the dpms functions, yet. v2: Be careful about clearing the _DISABLE flags for intermediate dpms modes - otherwise we might clobber the crt state when another (cloned) connector gets enabled. Reviewed-by: Jesse Barnes Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/intel_crt.c b/drivers/gpu/drm/i915/intel_crt.c index 1d05b2d..543ea40 100644 --- a/drivers/gpu/drm/i915/intel_crt.c +++ b/drivers/gpu/drm/i915/intel_crt.c @@ -61,6 +61,29 @@ static struct intel_crt *intel_encoder_to_crt(struct intel_encoder *encoder) return container_of(encoder, struct intel_crt, base); } +static void intel_disable_crt(struct intel_encoder *encoder) +{ + struct drm_i915_private *dev_priv = encoder->base.dev->dev_private; + struct intel_crt *crt = intel_encoder_to_crt(encoder); + u32 temp; + + temp = I915_READ(crt->adpa_reg); + temp &= ~(ADPA_HSYNC_CNTL_DISABLE | ADPA_VSYNC_CNTL_DISABLE); + temp &= ~ADPA_DAC_ENABLE; + I915_WRITE(crt->adpa_reg, temp); +} + +static void intel_enable_crt(struct intel_encoder *encoder) +{ + struct drm_i915_private *dev_priv = encoder->base.dev->dev_private; + struct intel_crt *crt = intel_encoder_to_crt(encoder); + u32 temp; + + temp = I915_READ(crt->adpa_reg); + temp |= ADPA_DAC_ENABLE; + I915_WRITE(crt->adpa_reg, temp); +} + static void pch_crt_dpms(struct drm_encoder *encoder, int mode) { struct drm_device *dev = encoder->dev; @@ -575,18 +598,20 @@ static void intel_crt_reset(struct drm_connector *connector) static const struct drm_encoder_helper_funcs pch_encoder_funcs = { .mode_fixup = intel_crt_mode_fixup, - .prepare = intel_encoder_prepare, - .commit = intel_encoder_commit, + .prepare = intel_encoder_noop, + .commit = intel_encoder_noop, .mode_set = intel_crt_mode_set, .dpms = pch_crt_dpms, + .disable = intel_encoder_disable, }; static const struct drm_encoder_helper_funcs gmch_encoder_funcs = { .mode_fixup = intel_crt_mode_fixup, - .prepare = intel_encoder_prepare, - .commit = intel_encoder_commit, + .prepare = intel_encoder_noop, + .commit = intel_encoder_noop, .mode_set = intel_crt_mode_set, .dpms = gmch_crt_dpms, + .disable = intel_encoder_disable, }; static const struct drm_connector_funcs intel_crt_connector_funcs = { @@ -682,6 +707,9 @@ void intel_crt_init(struct drm_device *dev) else crt->adpa_reg = ADPA; + crt->base.disable = intel_disable_crt; + crt->base.enable = intel_enable_crt; + drm_encoder_helper_add(&crt->base.base, encoder_helper_funcs); drm_connector_helper_add(connector, &intel_crt_connector_helper_funcs); -- cgit v0.10.2 From ce22c320b8cad000f1b82da6e03848d59c5cf215 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Sun, 1 Jul 2012 15:31:04 +0200 Subject: drm/i915/sdvo: convert to encoder disable/enable Similar to crt, this doesn't convert the dpms functions. Also similar to crt, we don't switch of the display pipe for the intermediate modes, only DPMS_OFF is truely off. Reviewed-by: Jesse Barnes Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/intel_sdvo.c b/drivers/gpu/drm/i915/intel_sdvo.c index f3762f8..20feaa3 100644 --- a/drivers/gpu/drm/i915/intel_sdvo.c +++ b/drivers/gpu/drm/i915/intel_sdvo.c @@ -1142,6 +1142,56 @@ static void intel_sdvo_mode_set(struct drm_encoder *encoder, intel_sdvo_write_sdvox(intel_sdvo, sdvox); } +static void intel_disable_sdvo(struct intel_encoder *encoder) +{ + struct drm_i915_private *dev_priv = encoder->base.dev->dev_private; + struct intel_sdvo *intel_sdvo = to_intel_sdvo(&encoder->base); + u32 temp; + + intel_sdvo_set_active_outputs(intel_sdvo, 0); + if (0) + intel_sdvo_set_encoder_power_state(intel_sdvo, + DRM_MODE_DPMS_OFF); + + temp = I915_READ(intel_sdvo->sdvo_reg); + if ((temp & SDVO_ENABLE) != 0) { + intel_sdvo_write_sdvox(intel_sdvo, temp & ~SDVO_ENABLE); + } +} + +static void intel_enable_sdvo(struct intel_encoder *encoder) +{ + struct drm_device *dev = encoder->base.dev; + struct drm_i915_private *dev_priv = dev->dev_private; + struct intel_sdvo *intel_sdvo = to_intel_sdvo(&encoder->base); + struct intel_crtc *intel_crtc = to_intel_crtc(encoder->base.crtc); + u32 temp; + bool input1, input2; + int i; + u8 status; + + temp = I915_READ(intel_sdvo->sdvo_reg); + if ((temp & SDVO_ENABLE) == 0) + intel_sdvo_write_sdvox(intel_sdvo, temp | SDVO_ENABLE); + for (i = 0; i < 2; i++) + intel_wait_for_vblank(dev, intel_crtc->pipe); + + status = intel_sdvo_get_trained_inputs(intel_sdvo, &input1, &input2); + /* Warn if the device reported failure to sync. + * A lot of SDVO devices fail to notify of sync, but it's + * a given it the status is a success, we succeeded. + */ + if (status == SDVO_CMD_STATUS_SUCCESS && !input1) { + DRM_DEBUG_KMS("First %s output reported failure to " + "sync\n", SDVO_NAME(intel_sdvo)); + } + + if (0) + intel_sdvo_set_encoder_power_state(intel_sdvo, + DRM_MODE_DPMS_ON); + intel_sdvo_set_active_outputs(intel_sdvo, intel_sdvo->attached_output); +} + static void intel_sdvo_dpms(struct drm_encoder *encoder, int mode) { struct drm_device *dev = encoder->dev; @@ -1847,9 +1897,10 @@ done: static const struct drm_encoder_helper_funcs intel_sdvo_helper_funcs = { .dpms = intel_sdvo_dpms, .mode_fixup = intel_sdvo_mode_fixup, - .prepare = intel_encoder_prepare, + .prepare = intel_encoder_noop, .mode_set = intel_sdvo_mode_set, - .commit = intel_encoder_commit, + .commit = intel_encoder_noop, + .disable = intel_encoder_disable }; static const struct drm_connector_funcs intel_sdvo_connector_funcs = { @@ -2576,6 +2627,9 @@ bool intel_sdvo_init(struct drm_device *dev, uint32_t sdvo_reg, bool is_sdvob) drm_encoder_helper_add(&intel_encoder->base, &intel_sdvo_helper_funcs); + intel_encoder->disable = intel_disable_sdvo; + intel_encoder->enable = intel_enable_sdvo; + /* In default case sdvo lvds is false */ if (!intel_sdvo_get_capabilities(intel_sdvo, &intel_sdvo->caps)) goto err; -- cgit v0.10.2 From 19c63fa8070b09f409f64c5b3bcd0444e6e67b3b Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Wed, 11 Jul 2012 09:48:04 +0200 Subject: drm/i915/dvo: convert to encoder disable/enable Similar to the sdvo conversion. Reviewed-by: Jesse Barnes Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/intel_dvo.c b/drivers/gpu/drm/i915/intel_dvo.c index 227551f..4ad988f 100644 --- a/drivers/gpu/drm/i915/intel_dvo.c +++ b/drivers/gpu/drm/i915/intel_dvo.c @@ -105,6 +105,30 @@ static struct intel_dvo *intel_attached_dvo(struct drm_connector *connector) struct intel_dvo, base); } +static void intel_disable_dvo(struct intel_encoder *encoder) +{ + struct drm_i915_private *dev_priv = encoder->base.dev->dev_private; + struct intel_dvo *intel_dvo = enc_to_intel_dvo(&encoder->base); + u32 dvo_reg = intel_dvo->dev.dvo_reg; + u32 temp = I915_READ(dvo_reg); + + intel_dvo->dev.dev_ops->dpms(&intel_dvo->dev, false); + I915_WRITE(dvo_reg, temp & ~DVO_ENABLE); + I915_READ(dvo_reg); +} + +static void intel_enable_dvo(struct intel_encoder *encoder) +{ + struct drm_i915_private *dev_priv = encoder->base.dev->dev_private; + struct intel_dvo *intel_dvo = enc_to_intel_dvo(&encoder->base); + u32 dvo_reg = intel_dvo->dev.dvo_reg; + u32 temp = I915_READ(dvo_reg); + + I915_WRITE(dvo_reg, temp | DVO_ENABLE); + I915_READ(dvo_reg); + intel_dvo->dev.dev_ops->dpms(&intel_dvo->dev, true); +} + static void intel_dvo_dpms(struct drm_encoder *encoder, int mode) { struct drm_i915_private *dev_priv = encoder->dev->dev_private; @@ -277,9 +301,10 @@ static void intel_dvo_destroy(struct drm_connector *connector) static const struct drm_encoder_helper_funcs intel_dvo_helper_funcs = { .dpms = intel_dvo_dpms, .mode_fixup = intel_dvo_mode_fixup, - .prepare = intel_encoder_prepare, + .prepare = intel_encoder_noop, .mode_set = intel_dvo_mode_set, - .commit = intel_encoder_commit, + .commit = intel_encoder_noop, + .disable = intel_encoder_disable, }; static const struct drm_connector_funcs intel_dvo_connector_funcs = { @@ -372,6 +397,9 @@ void intel_dvo_init(struct drm_device *dev) drm_encoder_init(dev, &intel_encoder->base, &intel_dvo_enc_funcs, encoder_type); + intel_encoder->disable = intel_disable_dvo; + intel_encoder->enable = intel_enable_dvo; + /* Now, try to find a controller */ for (i = 0; i < ARRAY_SIZE(intel_dvo_devices); i++) { struct drm_connector *connector = &intel_connector->base; -- cgit v0.10.2 From b2cabb0e1d205708dab11bd9e1b97fd080537096 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Sun, 1 Jul 2012 22:42:24 +0200 Subject: drm/i915: convert dpms functions of dvo/sdvo/crt Yeah, big patch but I couldn't come up with a neat idea of how to split it up further, that wouldn't break dpms on cloned configs somehow. But the changes in dvo/sdvo/crt are all pretty much orthonogal, so it's not too bad a patch. These are the only encoders that support cloning, which requires a few special changes compared to the previous patches. - Compute the desired state of the display pipe by walking all connected encoders and checking whether any has active connectors. To make this clearer, drop the old mode parameter to the crtc dpms function and rename it to intel_crtc_update_dpms. - There's the curious case of intel_crtc->dpms_mode. With the previous patches to remove the overlay pipe A code and to rework the load detect pipe code, the big users are gone. We still keep it to avoid enabling the pipe twice, but we duplicate this logic with crtc->active, too. Still, leave this for now and just push a fake dpms mode into it that reflects the state of the display pipe. Changes in the encoder dpms functions: - We clamp the dpms state to the supported range right away. This is escpecially important for the VGA outputs, where only older hw supports the intermediate states. This (and the crt->adpa_reg patch) allows us to unify the crt dpms code again between all variants (gmch, vlv and pch). - We only enable/disable the output for dvo/sdvo and leave the encoder running. The encoder will be disabled/enabled when we switch the state of the entire output pipeline (which will happen right away for non-cloned setups). This way the duplication is reduced and strange interaction when disabling output ports at the wrong time avoided. The dpms code for all three types of connectors contains a bit of duplicated logic, but I think keeping these special cases separate is simpler: CRT is the only one that hanldes intermediate dpms state (which requires extra logic to enable/disable things in the right order), and introducing some abstraction just to share the code between dvo and sdvo smells like overkill. We can do that once someone bothers to implement cloning for the more modern outputs. But I doubt that this will ever happen. v2: s/crtc/crt/_set_dpms, noticed by Paulo Zanoni. Reviewed-by: Jesse Barnes Signed-Off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/intel_crt.c b/drivers/gpu/drm/i915/intel_crt.c index 543ea40..76646c7 100644 --- a/drivers/gpu/drm/i915/intel_crt.c +++ b/drivers/gpu/drm/i915/intel_crt.c @@ -84,13 +84,17 @@ static void intel_enable_crt(struct intel_encoder *encoder) I915_WRITE(crt->adpa_reg, temp); } -static void pch_crt_dpms(struct drm_encoder *encoder, int mode) +/* Note: The caller is required to filter out dpms modes not supported by the + * platform. */ +static void intel_crt_set_dpms(struct intel_encoder *encoder, int mode) { - struct drm_device *dev = encoder->dev; + struct drm_device *dev = encoder->base.dev; struct drm_i915_private *dev_priv = dev->dev_private; + struct intel_crt *crt = intel_encoder_to_crt(encoder); u32 temp; - temp = I915_READ(PCH_ADPA); + temp = I915_READ(crt->adpa_reg); + temp &= ~(ADPA_HSYNC_CNTL_DISABLE | ADPA_VSYNC_CNTL_DISABLE); temp &= ~ADPA_DAC_ENABLE; switch (mode) { @@ -98,44 +102,59 @@ static void pch_crt_dpms(struct drm_encoder *encoder, int mode) temp |= ADPA_DAC_ENABLE; break; case DRM_MODE_DPMS_STANDBY: + temp |= ADPA_DAC_ENABLE | ADPA_HSYNC_CNTL_DISABLE; + break; case DRM_MODE_DPMS_SUSPEND: + temp |= ADPA_DAC_ENABLE | ADPA_VSYNC_CNTL_DISABLE; + break; case DRM_MODE_DPMS_OFF: - /* Just leave port enable cleared */ + temp |= ADPA_HSYNC_CNTL_DISABLE | ADPA_VSYNC_CNTL_DISABLE; break; } - I915_WRITE(PCH_ADPA, temp); + I915_WRITE(crt->adpa_reg, temp); } -static void gmch_crt_dpms(struct drm_encoder *encoder, int mode) +static void intel_crt_dpms(struct drm_connector *connector, int mode) { - struct drm_device *dev = encoder->dev; - struct drm_i915_private *dev_priv = dev->dev_private; - u32 temp; - - temp = I915_READ(ADPA); - temp &= ~(ADPA_HSYNC_CNTL_DISABLE | ADPA_VSYNC_CNTL_DISABLE); - temp &= ~ADPA_DAC_ENABLE; + struct drm_device *dev = connector->dev; + struct intel_encoder *encoder = intel_attached_encoder(connector); + struct drm_crtc *crtc; + int old_dpms; - if (IS_VALLEYVIEW(dev) && mode != DRM_MODE_DPMS_ON) + /* PCH platforms and VLV only support on/off. */ + if (INTEL_INFO(dev)->gen < 5 && mode != DRM_MODE_DPMS_ON) mode = DRM_MODE_DPMS_OFF; - switch (mode) { - case DRM_MODE_DPMS_ON: - temp |= ADPA_DAC_ENABLE; - break; - case DRM_MODE_DPMS_STANDBY: - temp |= ADPA_DAC_ENABLE | ADPA_HSYNC_CNTL_DISABLE; - break; - case DRM_MODE_DPMS_SUSPEND: - temp |= ADPA_DAC_ENABLE | ADPA_VSYNC_CNTL_DISABLE; - break; - case DRM_MODE_DPMS_OFF: - temp |= ADPA_HSYNC_CNTL_DISABLE | ADPA_VSYNC_CNTL_DISABLE; - break; + if (mode == connector->dpms) + return; + + old_dpms = connector->dpms; + connector->dpms = mode; + + /* Only need to change hw state when actually enabled */ + crtc = encoder->base.crtc; + if (!crtc) { + encoder->connectors_active = false; + return; } - I915_WRITE(ADPA, temp); + /* We need the pipe to run for anything but OFF. */ + if (mode == DRM_MODE_DPMS_OFF) + encoder->connectors_active = false; + else + encoder->connectors_active = true; + + if (mode < old_dpms) { + /* From off to on, enable the pipe first. */ + intel_crtc_update_dpms(crtc); + + intel_crt_set_dpms(encoder, mode); + } else { + intel_crt_set_dpms(encoder, mode); + + intel_crtc_update_dpms(crtc); + } } static int intel_crt_mode_valid(struct drm_connector *connector, @@ -596,27 +615,17 @@ static void intel_crt_reset(struct drm_connector *connector) * Routines for controlling stuff on the analog port */ -static const struct drm_encoder_helper_funcs pch_encoder_funcs = { - .mode_fixup = intel_crt_mode_fixup, - .prepare = intel_encoder_noop, - .commit = intel_encoder_noop, - .mode_set = intel_crt_mode_set, - .dpms = pch_crt_dpms, - .disable = intel_encoder_disable, -}; - -static const struct drm_encoder_helper_funcs gmch_encoder_funcs = { +static const struct drm_encoder_helper_funcs crt_encoder_funcs = { .mode_fixup = intel_crt_mode_fixup, .prepare = intel_encoder_noop, .commit = intel_encoder_noop, .mode_set = intel_crt_mode_set, - .dpms = gmch_crt_dpms, .disable = intel_encoder_disable, }; static const struct drm_connector_funcs intel_crt_connector_funcs = { .reset = intel_crt_reset, - .dpms = drm_helper_connector_dpms, + .dpms = intel_crt_dpms, .detect = intel_crt_detect, .fill_modes = drm_helper_probe_single_connector_modes, .destroy = intel_crt_destroy, @@ -657,7 +666,6 @@ void intel_crt_init(struct drm_device *dev) struct intel_crt *crt; struct intel_connector *intel_connector; struct drm_i915_private *dev_priv = dev->dev_private; - const struct drm_encoder_helper_funcs *encoder_helper_funcs; /* Skip machines without VGA that falsely report hotplug events */ if (dmi_check_system(intel_no_crt)) @@ -696,11 +704,6 @@ void intel_crt_init(struct drm_device *dev) connector->doublescan_allowed = 0; if (HAS_PCH_SPLIT(dev)) - encoder_helper_funcs = &pch_encoder_funcs; - else - encoder_helper_funcs = &gmch_encoder_funcs; - - if (HAS_PCH_SPLIT(dev)) crt->adpa_reg = PCH_ADPA; else if (IS_VALLEYVIEW(dev)) crt->adpa_reg = VLV_ADPA; @@ -710,7 +713,7 @@ void intel_crt_init(struct drm_device *dev) crt->base.disable = intel_disable_crt; crt->base.enable = intel_enable_crt; - drm_encoder_helper_add(&crt->base.base, encoder_helper_funcs); + drm_encoder_helper_add(&crt->base.base, &crt_encoder_funcs); drm_connector_helper_add(connector, &intel_crt_connector_helper_funcs); drm_sysfs_connector_add(connector); diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index cbd356f..0d48ebe 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -3462,34 +3462,31 @@ static void i9xx_crtc_off(struct drm_crtc *crtc) /** * Sets the power management mode of the pipe and plane. */ -static void intel_crtc_dpms(struct drm_crtc *crtc, int mode) +void intel_crtc_update_dpms(struct drm_crtc *crtc) { struct drm_device *dev = crtc->dev; struct drm_i915_private *dev_priv = dev->dev_private; struct drm_i915_master_private *master_priv; struct intel_crtc *intel_crtc = to_intel_crtc(crtc); + struct intel_encoder *intel_encoder; int pipe = intel_crtc->pipe; - bool enabled; + bool enabled, enable = false; + int mode; + + for_each_encoder_on_crtc(dev, crtc, intel_encoder) + enable |= intel_encoder->connectors_active; + + mode = enable ? DRM_MODE_DPMS_ON : DRM_MODE_DPMS_OFF; if (intel_crtc->dpms_mode == mode) return; intel_crtc->dpms_mode = mode; - /* XXX: When our outputs are all unaware of DPMS modes other than off - * and on, we should map those modes to DRM_MODE_DPMS_OFF in the CRTC. - */ - switch (mode) { - case DRM_MODE_DPMS_ON: - case DRM_MODE_DPMS_STANDBY: - case DRM_MODE_DPMS_SUSPEND: + if (enable) dev_priv->display.crtc_enable(crtc); - break; - - case DRM_MODE_DPMS_OFF: + else dev_priv->display.crtc_disable(crtc); - break; - } if (!dev->primary->master) return; @@ -3498,7 +3495,7 @@ static void intel_crtc_dpms(struct drm_crtc *crtc, int mode) if (!master_priv->sarea_priv) return; - enabled = crtc->enabled && mode != DRM_MODE_DPMS_OFF; + enabled = crtc->enabled && enable; switch (pipe) { case 0: @@ -3517,11 +3514,12 @@ static void intel_crtc_dpms(struct drm_crtc *crtc, int mode) static void intel_crtc_disable(struct drm_crtc *crtc) { - struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private; struct drm_device *dev = crtc->dev; struct drm_i915_private *dev_priv = dev->dev_private; - crtc_funcs->dpms(crtc, DRM_MODE_DPMS_OFF); + /* crtc->disable is only called when we have no encoders, hence this + * will disable the pipe. */ + intel_crtc_update_dpms(crtc); dev_priv->display.off(crtc); assert_plane_disabled(dev->dev_private, to_intel_crtc(crtc)->plane); @@ -3581,11 +3579,11 @@ 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); + intel_crtc_update_dpms(encoder->base.crtc); } else { encoder->connectors_active = false; - intel_crtc_dpms(encoder->base.crtc, DRM_MODE_DPMS_OFF); + intel_crtc_update_dpms(encoder->base.crtc); } } @@ -6609,7 +6607,6 @@ static void intel_crtc_reset(struct drm_crtc *crtc) } static struct drm_crtc_helper_funcs intel_helper_funcs = { - .dpms = intel_crtc_dpms, .mode_fixup = intel_crtc_mode_fixup, .mode_set = intel_crtc_mode_set, .mode_set_base = intel_pipe_set_base, diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index 759dcba..2456245 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -411,6 +411,7 @@ extern void intel_panel_destroy_backlight(struct drm_device *dev); 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_crtc_update_dpms(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); diff --git a/drivers/gpu/drm/i915/intel_dvo.c b/drivers/gpu/drm/i915/intel_dvo.c index 4ad988f..c55a13e 100644 --- a/drivers/gpu/drm/i915/intel_dvo.c +++ b/drivers/gpu/drm/i915/intel_dvo.c @@ -129,21 +129,39 @@ static void intel_enable_dvo(struct intel_encoder *encoder) intel_dvo->dev.dev_ops->dpms(&intel_dvo->dev, true); } -static void intel_dvo_dpms(struct drm_encoder *encoder, int mode) +static void intel_dvo_dpms(struct drm_connector *connector, int mode) { - struct drm_i915_private *dev_priv = encoder->dev->dev_private; - struct intel_dvo *intel_dvo = enc_to_intel_dvo(encoder); - u32 dvo_reg = intel_dvo->dev.dvo_reg; - u32 temp = I915_READ(dvo_reg); + struct intel_dvo *intel_dvo = intel_attached_dvo(connector); + struct drm_crtc *crtc; + + /* dvo supports only 2 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 */ + crtc = intel_dvo->base.base.crtc; + if (!crtc) { + intel_dvo->base.connectors_active = false; + return; + } if (mode == DRM_MODE_DPMS_ON) { - I915_WRITE(dvo_reg, temp | DVO_ENABLE); - I915_READ(dvo_reg); + intel_dvo->base.connectors_active = true; + + intel_crtc_update_dpms(crtc); + intel_dvo->dev.dev_ops->dpms(&intel_dvo->dev, true); } else { intel_dvo->dev.dev_ops->dpms(&intel_dvo->dev, false); - I915_WRITE(dvo_reg, temp & ~DVO_ENABLE); - I915_READ(dvo_reg); + + intel_dvo->base.connectors_active = false; + + intel_crtc_update_dpms(crtc); } } @@ -299,7 +317,6 @@ static void intel_dvo_destroy(struct drm_connector *connector) } static const struct drm_encoder_helper_funcs intel_dvo_helper_funcs = { - .dpms = intel_dvo_dpms, .mode_fixup = intel_dvo_mode_fixup, .prepare = intel_encoder_noop, .mode_set = intel_dvo_mode_set, @@ -308,7 +325,7 @@ static const struct drm_encoder_helper_funcs intel_dvo_helper_funcs = { }; static const struct drm_connector_funcs intel_dvo_connector_funcs = { - .dpms = drm_helper_connector_dpms, + .dpms = intel_dvo_dpms, .detect = intel_dvo_detect, .destroy = intel_dvo_destroy, .fill_modes = drm_helper_probe_single_connector_modes, diff --git a/drivers/gpu/drm/i915/intel_sdvo.c b/drivers/gpu/drm/i915/intel_sdvo.c index 20feaa3..a01c470 100644 --- a/drivers/gpu/drm/i915/intel_sdvo.c +++ b/drivers/gpu/drm/i915/intel_sdvo.c @@ -1192,51 +1192,44 @@ static void intel_enable_sdvo(struct intel_encoder *encoder) intel_sdvo_set_active_outputs(intel_sdvo, intel_sdvo->attached_output); } -static void intel_sdvo_dpms(struct drm_encoder *encoder, int mode) +static void intel_sdvo_dpms(struct drm_connector *connector, int mode) { - struct drm_device *dev = encoder->dev; - struct drm_i915_private *dev_priv = dev->dev_private; - struct intel_sdvo *intel_sdvo = to_intel_sdvo(encoder); - struct intel_crtc *intel_crtc = to_intel_crtc(encoder->crtc); - u32 temp; + struct drm_crtc *crtc; + struct intel_sdvo *intel_sdvo = intel_attached_sdvo(connector); + + /* dvo supports only 2 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 */ + crtc = intel_sdvo->base.base.crtc; + if (!crtc) { + intel_sdvo->base.connectors_active = false; + return; + } if (mode != DRM_MODE_DPMS_ON) { intel_sdvo_set_active_outputs(intel_sdvo, 0); if (0) intel_sdvo_set_encoder_power_state(intel_sdvo, mode); - if (mode == DRM_MODE_DPMS_OFF) { - temp = I915_READ(intel_sdvo->sdvo_reg); - if ((temp & SDVO_ENABLE) != 0) { - intel_sdvo_write_sdvox(intel_sdvo, temp & ~SDVO_ENABLE); - } - } + intel_sdvo->base.connectors_active = false; + + intel_crtc_update_dpms(crtc); } else { - bool input1, input2; - int i; - u8 status; - - temp = I915_READ(intel_sdvo->sdvo_reg); - if ((temp & SDVO_ENABLE) == 0) - intel_sdvo_write_sdvox(intel_sdvo, temp | SDVO_ENABLE); - for (i = 0; i < 2; i++) - intel_wait_for_vblank(dev, intel_crtc->pipe); - - status = intel_sdvo_get_trained_inputs(intel_sdvo, &input1, &input2); - /* Warn if the device reported failure to sync. - * A lot of SDVO devices fail to notify of sync, but it's - * a given it the status is a success, we succeeded. - */ - if (status == SDVO_CMD_STATUS_SUCCESS && !input1) { - DRM_DEBUG_KMS("First %s output reported failure to " - "sync\n", SDVO_NAME(intel_sdvo)); - } + intel_sdvo->base.connectors_active = true; + + intel_crtc_update_dpms(crtc); if (0) intel_sdvo_set_encoder_power_state(intel_sdvo, mode); intel_sdvo_set_active_outputs(intel_sdvo, intel_sdvo->attached_output); } - return; } static int intel_sdvo_mode_valid(struct drm_connector *connector, @@ -1895,7 +1888,6 @@ done: } static const struct drm_encoder_helper_funcs intel_sdvo_helper_funcs = { - .dpms = intel_sdvo_dpms, .mode_fixup = intel_sdvo_mode_fixup, .prepare = intel_encoder_noop, .mode_set = intel_sdvo_mode_set, @@ -1904,7 +1896,7 @@ static const struct drm_encoder_helper_funcs intel_sdvo_helper_funcs = { }; static const struct drm_connector_funcs intel_sdvo_connector_funcs = { - .dpms = drm_helper_connector_dpms, + .dpms = intel_sdvo_dpms, .detect = intel_sdvo_detect, .fill_modes = drm_helper_probe_single_connector_modes, .set_property = intel_sdvo_set_property, -- cgit v0.10.2 From fa5c73b1b20ffc7e9df83a5e763af1e3b7cc96d3 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Sun, 1 Jul 2012 23:24:36 +0200 Subject: drm/i915: rip out encoder->disable/enable checks All encoders are now converted so there's no need for these checks any more. Reviewed-by: Jesse Barnes Signed-Off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 0d48ebe..4805385 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -3267,10 +3267,8 @@ static void ironlake_crtc_enable(struct drm_crtc *crtc) intel_crtc_update_cursor(crtc, true); encoders: - for_each_encoder_on_crtc(dev, crtc, encoder) { - if (encoder->enable) - encoder->enable(encoder); - } + for_each_encoder_on_crtc(dev, crtc, encoder) + encoder->enable(encoder); } static void ironlake_crtc_disable(struct drm_crtc *crtc) @@ -3285,10 +3283,8 @@ static void ironlake_crtc_disable(struct drm_crtc *crtc) /* XXX: For compatability with the crtc helper code, call the encoder's * disable function unconditionally for now. */ - for_each_encoder_on_crtc(dev, crtc, encoder) { - if (encoder->disable) - encoder->disable(encoder); - } + for_each_encoder_on_crtc(dev, crtc, encoder) + encoder->disable(encoder); if (!intel_crtc->active) return; @@ -3412,10 +3408,8 @@ static void i9xx_crtc_enable(struct drm_crtc *crtc) intel_crtc_update_cursor(crtc, true); encoders: - for_each_encoder_on_crtc(dev, crtc, encoder) { - if (encoder->enable) - encoder->enable(encoder); - } + for_each_encoder_on_crtc(dev, crtc, encoder) + encoder->enable(encoder); } static void i9xx_crtc_disable(struct drm_crtc *crtc) @@ -3429,10 +3423,8 @@ static void i9xx_crtc_disable(struct drm_crtc *crtc) /* XXX: For compatability with the crtc helper code, call the encoder's * disable function unconditionally for now. */ - for_each_encoder_on_crtc(dev, crtc, encoder) { - if (encoder->disable) - encoder->disable(encoder); - } + for_each_encoder_on_crtc(dev, crtc, encoder) + encoder->disable(encoder); if (!intel_crtc->active) return; -- cgit v0.10.2 From 61b77ddda6cf6f1f6f543339cfeee4c623f82784 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Mon, 2 Jul 2012 00:16:19 +0200 Subject: drm/i915: clean up encoder_prepare/commit We no longer need them. And now that all encoders are converted, we can finally move the cpt modeset check to the right place - at the end of the crtc_enable function. Reviewed-by: Jesse Barnes Signed-Off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 4805385..07077b1 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -3269,6 +3269,9 @@ static void ironlake_crtc_enable(struct drm_crtc *crtc) encoders: for_each_encoder_on_crtc(dev, crtc, encoder) encoder->enable(encoder); + + if (HAS_PCH_CPT(dev)) + intel_cpt_verify_modeset(dev, intel_crtc->pipe); } static void ironlake_crtc_disable(struct drm_crtc *crtc) @@ -3524,26 +3527,6 @@ static void intel_crtc_disable(struct drm_crtc *crtc) } } -void intel_encoder_prepare(struct drm_encoder *encoder) -{ - struct drm_encoder_helper_funcs *encoder_funcs = encoder->helper_private; - /* lvds has its own version of prepare see intel_lvds_prepare */ - encoder_funcs->dpms(encoder, DRM_MODE_DPMS_OFF); -} - -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_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); - - if (HAS_PCH_CPT(dev)) - intel_cpt_verify_modeset(dev, intel_crtc->pipe); -} - void intel_encoder_noop(struct drm_encoder *encoder) { } diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index 2456245..e59cac3 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -412,8 +412,6 @@ 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_crtc_update_dpms(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); -- cgit v0.10.2 From 50f56119efcb7ad763c14a9b6c2c8e684050cab0 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Mon, 2 Jul 2012 09:35:43 +0200 Subject: drm/i915: copy&paste drm_crtc_helper_set_config And the following static functions required by it: drm_encoder_crtc_ok, drm_crtc_helper_disable No changes safe for the s/drm/intel prefix change. Acked-by: Jesse Barnes Signed-Off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 07077b1..33a5191 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -6590,12 +6590,324 @@ static struct drm_crtc_helper_funcs intel_helper_funcs = { .disable = intel_crtc_disable, }; +static bool intel_encoder_crtc_ok(struct drm_encoder *encoder, + struct drm_crtc *crtc) +{ + struct drm_device *dev; + struct drm_crtc *tmp; + int crtc_mask = 1; + + WARN(!crtc, "checking null crtc?\n"); + + dev = crtc->dev; + + list_for_each_entry(tmp, &dev->mode_config.crtc_list, head) { + if (tmp == crtc) + break; + crtc_mask <<= 1; + } + + if (encoder->possible_crtcs & crtc_mask) + return true; + return false; +} + +static int +intel_crtc_helper_disable(struct drm_crtc *crtc) +{ + struct drm_device *dev = crtc->dev; + struct drm_connector *connector; + struct drm_encoder *encoder; + + /* Decouple all encoders and their attached connectors from this crtc */ + list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { + if (encoder->crtc != crtc) + continue; + + list_for_each_entry(connector, &dev->mode_config.connector_list, head) { + if (connector->encoder != encoder) + continue; + + connector->encoder = NULL; + } + } + + drm_helper_disable_unused_functions(dev); + return 0; +} + +static int intel_crtc_set_config(struct drm_mode_set *set) +{ + struct drm_device *dev; + struct drm_crtc *save_crtcs, *new_crtc, *crtc; + struct drm_encoder *save_encoders, *new_encoder, *encoder; + struct drm_framebuffer *old_fb = NULL; + bool mode_changed = false; /* if true do a full mode set */ + bool fb_changed = false; /* if true and !mode_changed just do a flip */ + struct drm_connector *save_connectors, *connector; + int count = 0, ro, fail = 0; + struct drm_crtc_helper_funcs *crtc_funcs; + struct drm_mode_set save_set; + int ret; + int i; + + DRM_DEBUG_KMS("\n"); + + if (!set) + return -EINVAL; + + if (!set->crtc) + return -EINVAL; + + if (!set->crtc->helper_private) + return -EINVAL; + + crtc_funcs = set->crtc->helper_private; + + if (!set->mode) + set->fb = NULL; + + if (set->fb) { + DRM_DEBUG_KMS("[CRTC:%d] [FB:%d] #connectors=%d (x y) (%i %i)\n", + set->crtc->base.id, set->fb->base.id, + (int)set->num_connectors, set->x, set->y); + } else { + DRM_DEBUG_KMS("[CRTC:%d] [NOFB]\n", set->crtc->base.id); + return intel_crtc_helper_disable(set->crtc); + } + + dev = set->crtc->dev; + + /* Allocate space for the backup of all (non-pointer) crtc, encoder and + * connector data. */ + save_crtcs = kzalloc(dev->mode_config.num_crtc * + sizeof(struct drm_crtc), GFP_KERNEL); + if (!save_crtcs) + return -ENOMEM; + + save_encoders = kzalloc(dev->mode_config.num_encoder * + sizeof(struct drm_encoder), GFP_KERNEL); + if (!save_encoders) { + kfree(save_crtcs); + return -ENOMEM; + } + + save_connectors = kzalloc(dev->mode_config.num_connector * + sizeof(struct drm_connector), GFP_KERNEL); + if (!save_connectors) { + kfree(save_crtcs); + kfree(save_encoders); + return -ENOMEM; + } + + /* Copy data. Note that driver private data is not affected. + * Should anything bad happen only the expected state is + * restored, not the drivers personal bookkeeping. + */ + count = 0; + list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { + save_crtcs[count++] = *crtc; + } + + count = 0; + list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { + save_encoders[count++] = *encoder; + } + + count = 0; + list_for_each_entry(connector, &dev->mode_config.connector_list, head) { + save_connectors[count++] = *connector; + } + + save_set.crtc = set->crtc; + save_set.mode = &set->crtc->mode; + save_set.x = set->crtc->x; + save_set.y = set->crtc->y; + save_set.fb = set->crtc->fb; + + /* We should be able to check here if the fb has the same properties + * and then just flip_or_move it */ + if (set->crtc->fb != set->fb) { + /* If we have no fb then treat it as a full mode set */ + if (set->crtc->fb == NULL) { + DRM_DEBUG_KMS("crtc has no fb, full mode set\n"); + mode_changed = true; + } else if (set->fb == NULL) { + mode_changed = true; + } else if (set->fb->depth != set->crtc->fb->depth) { + mode_changed = true; + } else if (set->fb->bits_per_pixel != + set->crtc->fb->bits_per_pixel) { + mode_changed = true; + } else + fb_changed = true; + } + + if (set->x != set->crtc->x || set->y != set->crtc->y) + fb_changed = true; + + if (set->mode && !drm_mode_equal(set->mode, &set->crtc->mode)) { + DRM_DEBUG_KMS("modes are different, full mode set\n"); + drm_mode_debug_printmodeline(&set->crtc->mode); + drm_mode_debug_printmodeline(set->mode); + mode_changed = true; + } + + /* a) traverse passed in connector list and get encoders for them */ + count = 0; + list_for_each_entry(connector, &dev->mode_config.connector_list, head) { + struct drm_connector_helper_funcs *connector_funcs = + connector->helper_private; + new_encoder = connector->encoder; + for (ro = 0; ro < set->num_connectors; ro++) { + if (set->connectors[ro] == connector) { + new_encoder = connector_funcs->best_encoder(connector); + /* if we can't get an encoder for a connector + we are setting now - then fail */ + if (new_encoder == NULL) + /* don't break so fail path works correct */ + fail = 1; + break; + } + } + + if (new_encoder != connector->encoder) { + DRM_DEBUG_KMS("encoder changed, full mode switch\n"); + mode_changed = true; + /* If the encoder is reused for another connector, then + * the appropriate crtc will be set later. + */ + if (connector->encoder) + connector->encoder->crtc = NULL; + connector->encoder = new_encoder; + } + } + + if (fail) { + ret = -EINVAL; + goto fail; + } + + count = 0; + list_for_each_entry(connector, &dev->mode_config.connector_list, head) { + if (!connector->encoder) + continue; + + if (connector->encoder->crtc == set->crtc) + new_crtc = NULL; + else + new_crtc = connector->encoder->crtc; + + for (ro = 0; ro < set->num_connectors; ro++) { + if (set->connectors[ro] == connector) + new_crtc = set->crtc; + } + + /* Make sure the new CRTC will work with the encoder */ + if (new_crtc && + !intel_encoder_crtc_ok(connector->encoder, new_crtc)) { + ret = -EINVAL; + goto fail; + } + if (new_crtc != connector->encoder->crtc) { + DRM_DEBUG_KMS("crtc changed, full mode switch\n"); + mode_changed = true; + connector->encoder->crtc = new_crtc; + } + if (new_crtc) { + DRM_DEBUG_KMS("[CONNECTOR:%d:%s] to [CRTC:%d]\n", + connector->base.id, drm_get_connector_name(connector), + new_crtc->base.id); + } else { + DRM_DEBUG_KMS("[CONNECTOR:%d:%s] to [NOCRTC]\n", + connector->base.id, drm_get_connector_name(connector)); + } + } + + /* mode_set_base is not a required function */ + if (fb_changed && !crtc_funcs->mode_set_base) + mode_changed = true; + + if (mode_changed) { + set->crtc->enabled = drm_helper_crtc_in_use(set->crtc); + if (set->crtc->enabled) { + DRM_DEBUG_KMS("attempting to set mode from" + " userspace\n"); + drm_mode_debug_printmodeline(set->mode); + old_fb = set->crtc->fb; + set->crtc->fb = set->fb; + if (!drm_crtc_helper_set_mode(set->crtc, set->mode, + set->x, set->y, + old_fb)) { + DRM_ERROR("failed to set mode on [CRTC:%d]\n", + set->crtc->base.id); + set->crtc->fb = old_fb; + ret = -EINVAL; + goto fail; + } + DRM_DEBUG_KMS("Setting connector DPMS state to on\n"); + for (i = 0; i < set->num_connectors; i++) { + DRM_DEBUG_KMS("\t[CONNECTOR:%d:%s] set DPMS on\n", set->connectors[i]->base.id, + drm_get_connector_name(set->connectors[i])); + set->connectors[i]->funcs->dpms(set->connectors[i], DRM_MODE_DPMS_ON); + } + } + drm_helper_disable_unused_functions(dev); + } else if (fb_changed) { + set->crtc->x = set->x; + set->crtc->y = set->y; + + old_fb = set->crtc->fb; + if (set->crtc->fb != set->fb) + set->crtc->fb = set->fb; + ret = crtc_funcs->mode_set_base(set->crtc, + set->x, set->y, old_fb); + if (ret != 0) { + set->crtc->fb = old_fb; + goto fail; + } + } + + kfree(save_connectors); + kfree(save_encoders); + kfree(save_crtcs); + return 0; + +fail: + /* Restore all previous data. */ + count = 0; + list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { + *crtc = save_crtcs[count++]; + } + + count = 0; + list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { + *encoder = save_encoders[count++]; + } + + count = 0; + list_for_each_entry(connector, &dev->mode_config.connector_list, head) { + *connector = save_connectors[count++]; + } + + /* Try to restore the config */ + if (mode_changed && + !drm_crtc_helper_set_mode(save_set.crtc, save_set.mode, save_set.x, + save_set.y, save_set.fb)) + DRM_ERROR("failed to restore config after modeset failure\n"); + + kfree(save_connectors); + kfree(save_encoders); + kfree(save_crtcs); + return ret; +} + static const struct drm_crtc_funcs intel_crtc_funcs = { .reset = intel_crtc_reset, .cursor_set = intel_crtc_cursor_set, .cursor_move = intel_crtc_cursor_move, .gamma_set = intel_crtc_gamma_set, - .set_config = drm_crtc_helper_set_config, + .set_config = intel_crtc_set_config, .destroy = intel_crtc_destroy, .page_flip = intel_crtc_page_flip, }; -- cgit v0.10.2 From 4f660f49b9d68e53d85f5ce4f5bbf167257e7fc2 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Mon, 2 Jul 2012 09:47:37 +0200 Subject: drm/i915: call set_base directly And drop the check, we always have it. Reviewed-by: Jesse Barnes Signed-Off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 33a5191..5dbe03a 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -6584,7 +6584,6 @@ static void intel_crtc_reset(struct drm_crtc *crtc) static struct drm_crtc_helper_funcs intel_helper_funcs = { .mode_fixup = intel_crtc_mode_fixup, .mode_set = intel_crtc_mode_set, - .mode_set_base = intel_pipe_set_base, .mode_set_base_atomic = intel_pipe_set_base_atomic, .load_lut = intel_crtc_load_lut, .disable = intel_crtc_disable, @@ -6646,7 +6645,6 @@ static int intel_crtc_set_config(struct drm_mode_set *set) bool fb_changed = false; /* if true and !mode_changed just do a flip */ struct drm_connector *save_connectors, *connector; int count = 0, ro, fail = 0; - struct drm_crtc_helper_funcs *crtc_funcs; struct drm_mode_set save_set; int ret; int i; @@ -6662,7 +6660,6 @@ static int intel_crtc_set_config(struct drm_mode_set *set) if (!set->crtc->helper_private) return -EINVAL; - crtc_funcs = set->crtc->helper_private; if (!set->mode) set->fb = NULL; @@ -6824,10 +6821,6 @@ static int intel_crtc_set_config(struct drm_mode_set *set) } } - /* mode_set_base is not a required function */ - if (fb_changed && !crtc_funcs->mode_set_base) - mode_changed = true; - if (mode_changed) { set->crtc->enabled = drm_helper_crtc_in_use(set->crtc); if (set->crtc->enabled) { @@ -6860,8 +6853,8 @@ static int intel_crtc_set_config(struct drm_mode_set *set) old_fb = set->crtc->fb; if (set->crtc->fb != set->fb) set->crtc->fb = set->fb; - ret = crtc_funcs->mode_set_base(set->crtc, - set->x, set->y, old_fb); + ret = intel_pipe_set_base(set->crtc, + set->x, set->y, old_fb); if (ret != 0) { set->crtc->fb = old_fb; goto fail; -- cgit v0.10.2 From 6d832d189bb671cfa2c681d97625bab2d6463c66 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Mon, 2 Jul 2012 10:45:45 +0200 Subject: drm/i915: inline intel_best_encoder Also kill the error-path, we have a fixed connector->encoder mapping. Unfortunately we can't rip out all the ->best_encoder callbacks, these are all still used by the fb_helper. Neat helper layering violation there. Reviewed-by: Jesse Barnes Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 5dbe03a..9d5c575 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -6644,7 +6644,7 @@ static int intel_crtc_set_config(struct drm_mode_set *set) bool mode_changed = false; /* if true do a full mode set */ bool fb_changed = false; /* if true and !mode_changed just do a flip */ struct drm_connector *save_connectors, *connector; - int count = 0, ro, fail = 0; + int count = 0, ro; struct drm_mode_set save_set; int ret; int i; @@ -6660,7 +6660,6 @@ static int intel_crtc_set_config(struct drm_mode_set *set) if (!set->crtc->helper_private) return -EINVAL; - if (!set->mode) set->fb = NULL; @@ -6753,17 +6752,11 @@ static int intel_crtc_set_config(struct drm_mode_set *set) /* a) traverse passed in connector list and get encoders for them */ count = 0; list_for_each_entry(connector, &dev->mode_config.connector_list, head) { - struct drm_connector_helper_funcs *connector_funcs = - connector->helper_private; new_encoder = connector->encoder; for (ro = 0; ro < set->num_connectors; ro++) { if (set->connectors[ro] == connector) { - new_encoder = connector_funcs->best_encoder(connector); - /* if we can't get an encoder for a connector - we are setting now - then fail */ - if (new_encoder == NULL) - /* don't break so fail path works correct */ - fail = 1; + new_encoder = + &intel_attached_encoder(connector)->base; break; } } @@ -6780,11 +6773,6 @@ static int intel_crtc_set_config(struct drm_mode_set *set) } } - if (fail) { - ret = -EINVAL; - goto fail; - } - count = 0; list_for_each_entry(connector, &dev->mode_config.connector_list, head) { if (!connector->encoder) -- cgit v0.10.2 From a6778b3cfd7711951d8973286b783bc061281256 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Mon, 2 Jul 2012 09:56:42 +0200 Subject: drm/i915: copy&paste drm_crtc_helper_set_mode Together with the static helper functions drm_crtc_prepare_encoders and drm_encoder_disable (which will be simplified in the next patch, but for now are 1:1 copies). Again, no changes beside new names for these functions. Also call our new set_mode instead of the crtc helper one now in all the places we've done so far. v2: Call the function just intel_set_mode to better differentia it from intel_crtc_mode_set which really only does the ->mode_set step of the entire modeset sequence on one crtc. Whereas this function does the global change. Acked-by: Jesse Barnes Signed-Off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 9d5c575..3e119a6 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -5767,7 +5767,7 @@ bool intel_get_load_detect_pipe(struct drm_connector *connector, goto fail; } - if (!drm_crtc_helper_set_mode(crtc, mode, 0, 0, old_fb)) { + if (!intel_set_mode(crtc, mode, 0, 0, old_fb)) { DRM_DEBUG_KMS("failed to set mode on load-detect pipe\n"); if (old->release_fb) old->release_fb->funcs->destroy(old->release_fb); @@ -6635,6 +6635,157 @@ intel_crtc_helper_disable(struct drm_crtc *crtc) return 0; } +static void +intel_encoder_disable_helper(struct drm_encoder *encoder) +{ + struct drm_encoder_helper_funcs *encoder_funcs = encoder->helper_private; + + if (encoder_funcs->disable) + (*encoder_funcs->disable)(encoder); + else + (*encoder_funcs->dpms)(encoder, DRM_MODE_DPMS_OFF); +} + +static void +intel_crtc_prepare_encoders(struct drm_device *dev) +{ + struct drm_encoder_helper_funcs *encoder_funcs; + struct drm_encoder *encoder; + + list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { + encoder_funcs = encoder->helper_private; + /* Disable unused encoders */ + if (encoder->crtc == NULL) + intel_encoder_disable_helper(encoder); + /* Disable encoders whose CRTC is about to change */ + if (encoder_funcs->get_crtc && + encoder->crtc != (*encoder_funcs->get_crtc)(encoder)) + intel_encoder_disable_helper(encoder); + } +} + +bool intel_set_mode(struct drm_crtc *crtc, + struct drm_display_mode *mode, + int x, int y, struct drm_framebuffer *old_fb) +{ + struct drm_device *dev = crtc->dev; + struct drm_display_mode *adjusted_mode, saved_mode, saved_hwmode; + struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private; + struct drm_encoder_helper_funcs *encoder_funcs; + int saved_x, saved_y; + struct drm_encoder *encoder; + bool ret = true; + + crtc->enabled = drm_helper_crtc_in_use(crtc); + if (!crtc->enabled) + return true; + + adjusted_mode = drm_mode_duplicate(dev, mode); + if (!adjusted_mode) + return false; + + saved_hwmode = crtc->hwmode; + saved_mode = crtc->mode; + saved_x = crtc->x; + saved_y = crtc->y; + + /* Update crtc values up front so the driver can rely on them for mode + * setting. + */ + crtc->mode = *mode; + crtc->x = x; + crtc->y = y; + + /* Pass our mode to the connectors and the CRTC to give them a chance to + * adjust it according to limitations or connector properties, and also + * a chance to reject the mode entirely. + */ + list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { + + if (encoder->crtc != crtc) + continue; + encoder_funcs = encoder->helper_private; + if (!(ret = encoder_funcs->mode_fixup(encoder, mode, + adjusted_mode))) { + DRM_DEBUG_KMS("Encoder fixup failed\n"); + goto done; + } + } + + if (!(ret = crtc_funcs->mode_fixup(crtc, mode, adjusted_mode))) { + DRM_DEBUG_KMS("CRTC fixup failed\n"); + goto done; + } + DRM_DEBUG_KMS("[CRTC:%d]\n", crtc->base.id); + + /* Prepare the encoders and CRTCs before setting the mode. */ + list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { + + if (encoder->crtc != crtc) + continue; + encoder_funcs = encoder->helper_private; + /* Disable the encoders as the first thing we do. */ + encoder_funcs->prepare(encoder); + } + + intel_crtc_prepare_encoders(dev); + + crtc_funcs->prepare(crtc); + + /* Set up the DPLL and any encoders state that needs to adjust or depend + * on the DPLL. + */ + ret = !crtc_funcs->mode_set(crtc, mode, adjusted_mode, x, y, old_fb); + if (!ret) + goto done; + + list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { + + if (encoder->crtc != crtc) + continue; + + DRM_DEBUG_KMS("[ENCODER:%d:%s] set [MODE:%d:%s]\n", + encoder->base.id, drm_get_encoder_name(encoder), + mode->base.id, mode->name); + encoder_funcs = encoder->helper_private; + encoder_funcs->mode_set(encoder, mode, adjusted_mode); + } + + /* Now enable the clocks, plane, pipe, and connectors that we set up. */ + crtc_funcs->commit(crtc); + + list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { + + if (encoder->crtc != crtc) + continue; + + encoder_funcs = encoder->helper_private; + encoder_funcs->commit(encoder); + + } + + /* Store real post-adjustment hardware mode. */ + crtc->hwmode = *adjusted_mode; + + /* Calculate and store various constants which + * are later needed by vblank and swap-completion + * timestamping. They are derived from true hwmode. + */ + drm_calc_timestamping_constants(crtc); + + /* FIXME: add subpixel order */ +done: + drm_mode_destroy(dev, adjusted_mode); + if (!ret) { + crtc->hwmode = saved_hwmode; + crtc->mode = saved_mode; + crtc->x = saved_x; + crtc->y = saved_y; + } + + return ret; +} + static int intel_crtc_set_config(struct drm_mode_set *set) { struct drm_device *dev; @@ -6817,9 +6968,8 @@ static int intel_crtc_set_config(struct drm_mode_set *set) drm_mode_debug_printmodeline(set->mode); old_fb = set->crtc->fb; set->crtc->fb = set->fb; - if (!drm_crtc_helper_set_mode(set->crtc, set->mode, - set->x, set->y, - old_fb)) { + if (!intel_set_mode(set->crtc, set->mode, + set->x, set->y, old_fb)) { DRM_ERROR("failed to set mode on [CRTC:%d]\n", set->crtc->base.id); set->crtc->fb = old_fb; @@ -6873,8 +7023,8 @@ fail: /* Try to restore the config */ if (mode_changed && - !drm_crtc_helper_set_mode(save_set.crtc, save_set.mode, save_set.x, - save_set.y, save_set.fb)) + !intel_set_mode(save_set.crtc, save_set.mode, + save_set.x, save_set.y, save_set.fb)) DRM_ERROR("failed to restore config after modeset failure\n"); kfree(save_connectors); diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index 7ee954c..2abaaf6 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -2312,9 +2312,8 @@ intel_dp_set_property(struct drm_connector *connector, done: if (intel_dp->base.base.crtc) { struct drm_crtc *crtc = intel_dp->base.base.crtc; - drm_crtc_helper_set_mode(crtc, &crtc->mode, - crtc->x, crtc->y, - crtc->fb); + intel_set_mode(crtc, &crtc->mode, + crtc->x, crtc->y, crtc->fb); } return 0; diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index e59cac3..c28fada 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -410,6 +410,8 @@ extern void intel_panel_disable_backlight(struct drm_device *dev); extern void intel_panel_destroy_backlight(struct drm_device *dev); extern enum drm_connector_status intel_panel_detect(struct drm_device *dev); +extern bool intel_set_mode(struct drm_crtc *crtc, struct drm_display_mode *mode, + int x, int y, struct drm_framebuffer *old_fb); extern void intel_crtc_load_lut(struct drm_crtc *crtc); extern void intel_crtc_update_dpms(struct drm_crtc *crtc); extern void intel_encoder_noop(struct drm_encoder *encoder); diff --git a/drivers/gpu/drm/i915/intel_hdmi.c b/drivers/gpu/drm/i915/intel_hdmi.c index acddaaa..ef84097 100644 --- a/drivers/gpu/drm/i915/intel_hdmi.c +++ b/drivers/gpu/drm/i915/intel_hdmi.c @@ -869,9 +869,8 @@ intel_hdmi_set_property(struct drm_connector *connector, done: if (intel_hdmi->base.base.crtc) { struct drm_crtc *crtc = intel_hdmi->base.base.crtc; - drm_crtc_helper_set_mode(crtc, &crtc->mode, - crtc->x, crtc->y, - crtc->fb); + intel_set_mode(crtc, &crtc->mode, + crtc->x, crtc->y, crtc->fb); } return 0; diff --git a/drivers/gpu/drm/i915/intel_lvds.c b/drivers/gpu/drm/i915/intel_lvds.c index 3baa224..fad82b2 100644 --- a/drivers/gpu/drm/i915/intel_lvds.c +++ b/drivers/gpu/drm/i915/intel_lvds.c @@ -560,8 +560,8 @@ static int intel_lvds_set_property(struct drm_connector *connector, * If the CRTC is enabled, the display will be changed * according to the new panel fitting mode. */ - drm_crtc_helper_set_mode(crtc, &crtc->mode, - crtc->x, crtc->y, crtc->fb); + intel_set_mode(crtc, &crtc->mode, + crtc->x, crtc->y, crtc->fb); } } diff --git a/drivers/gpu/drm/i915/intel_sdvo.c b/drivers/gpu/drm/i915/intel_sdvo.c index a01c470..88fb30d 100644 --- a/drivers/gpu/drm/i915/intel_sdvo.c +++ b/drivers/gpu/drm/i915/intel_sdvo.c @@ -1879,8 +1879,8 @@ set_value: done: if (intel_sdvo->base.base.crtc) { struct drm_crtc *crtc = intel_sdvo->base.base.crtc; - drm_crtc_helper_set_mode(crtc, &crtc->mode, crtc->x, - crtc->y, crtc->fb); + intel_set_mode(crtc, &crtc->mode, + crtc->x, crtc->y, crtc->fb); } return 0; diff --git a/drivers/gpu/drm/i915/intel_tv.c b/drivers/gpu/drm/i915/intel_tv.c index 281e0cf..16cb114 100644 --- a/drivers/gpu/drm/i915/intel_tv.c +++ b/drivers/gpu/drm/i915/intel_tv.c @@ -1471,8 +1471,8 @@ intel_tv_set_property(struct drm_connector *connector, struct drm_property *prop } if (changed && crtc) - drm_crtc_helper_set_mode(crtc, &crtc->mode, crtc->x, - crtc->y, crtc->fb); + intel_set_mode(crtc, &crtc->mode, + crtc->x, crtc->y, crtc->fb); out: return ret; } -- cgit v0.10.2 From 821112aa413285ae8a49f787ba01ab30e930ad85 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Mon, 2 Jul 2012 10:16:35 +0200 Subject: drm/i915: simplify intel_crtc_prepare_encoders - We don't have the ->get_crtc callback. - Call intel_encoder->disable directly. Reviewed-by: Jesse Barnes Signed-Off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 3e119a6..343f991 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -6636,31 +6636,14 @@ intel_crtc_helper_disable(struct drm_crtc *crtc) } static void -intel_encoder_disable_helper(struct drm_encoder *encoder) -{ - struct drm_encoder_helper_funcs *encoder_funcs = encoder->helper_private; - - if (encoder_funcs->disable) - (*encoder_funcs->disable)(encoder); - else - (*encoder_funcs->dpms)(encoder, DRM_MODE_DPMS_OFF); -} - -static void intel_crtc_prepare_encoders(struct drm_device *dev) { - struct drm_encoder_helper_funcs *encoder_funcs; - struct drm_encoder *encoder; + struct intel_encoder *encoder; - list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { - encoder_funcs = encoder->helper_private; + list_for_each_entry(encoder, &dev->mode_config.encoder_list, base.head) { /* Disable unused encoders */ - if (encoder->crtc == NULL) - intel_encoder_disable_helper(encoder); - /* Disable encoders whose CRTC is about to change */ - if (encoder_funcs->get_crtc && - encoder->crtc != (*encoder_funcs->get_crtc)(encoder)) - intel_encoder_disable_helper(encoder); + if (encoder->base.crtc == NULL) + encoder->disable(encoder); } } -- cgit v0.10.2 From c9deac9776b0a95b876bd845ea359d5975c3ba80 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Mon, 2 Jul 2012 10:21:35 +0200 Subject: drm/i915: rip out encoder->prepare/commit With the new infrastructure we're doing this when enabling/disabling the entire display pipe. Reviewed-by: Jesse Barnes Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/intel_crt.c b/drivers/gpu/drm/i915/intel_crt.c index 76646c7..2e25810 100644 --- a/drivers/gpu/drm/i915/intel_crt.c +++ b/drivers/gpu/drm/i915/intel_crt.c @@ -617,8 +617,6 @@ static void intel_crt_reset(struct drm_connector *connector) static const struct drm_encoder_helper_funcs crt_encoder_funcs = { .mode_fixup = intel_crt_mode_fixup, - .prepare = intel_encoder_noop, - .commit = intel_encoder_noop, .mode_set = intel_crt_mode_set, .disable = intel_encoder_disable, }; diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 343f991..d37a851 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -3527,10 +3527,6 @@ static void intel_crtc_disable(struct drm_crtc *crtc) } } -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); @@ -6701,16 +6697,6 @@ bool intel_set_mode(struct drm_crtc *crtc, } DRM_DEBUG_KMS("[CRTC:%d]\n", crtc->base.id); - /* Prepare the encoders and CRTCs before setting the mode. */ - list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { - - if (encoder->crtc != crtc) - continue; - encoder_funcs = encoder->helper_private; - /* Disable the encoders as the first thing we do. */ - encoder_funcs->prepare(encoder); - } - intel_crtc_prepare_encoders(dev); crtc_funcs->prepare(crtc); @@ -6737,16 +6723,6 @@ bool intel_set_mode(struct drm_crtc *crtc, /* Now enable the clocks, plane, pipe, and connectors that we set up. */ crtc_funcs->commit(crtc); - list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { - - if (encoder->crtc != crtc) - continue; - - encoder_funcs = encoder->helper_private; - encoder_funcs->commit(encoder); - - } - /* Store real post-adjustment hardware mode. */ crtc->hwmode = *adjusted_mode; diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index 2abaaf6..ff993a0 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -2348,9 +2348,7 @@ static void intel_dp_encoder_destroy(struct drm_encoder *encoder) static const struct drm_encoder_helper_funcs intel_dp_helper_funcs = { .mode_fixup = intel_dp_mode_fixup, - .prepare = intel_encoder_noop, .mode_set = intel_dp_mode_set, - .commit = intel_encoder_noop, .disable = intel_encoder_disable, }; diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index c28fada..673e8d4 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -414,7 +414,6 @@ extern bool intel_set_mode(struct drm_crtc *crtc, struct drm_display_mode *mode, int x, int y, struct drm_framebuffer *old_fb); extern void intel_crtc_load_lut(struct drm_crtc *crtc); extern void intel_crtc_update_dpms(struct drm_crtc *crtc); -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); diff --git a/drivers/gpu/drm/i915/intel_dvo.c b/drivers/gpu/drm/i915/intel_dvo.c index c55a13e..84c0867 100644 --- a/drivers/gpu/drm/i915/intel_dvo.c +++ b/drivers/gpu/drm/i915/intel_dvo.c @@ -318,9 +318,7 @@ static void intel_dvo_destroy(struct drm_connector *connector) static const struct drm_encoder_helper_funcs intel_dvo_helper_funcs = { .mode_fixup = intel_dvo_mode_fixup, - .prepare = intel_encoder_noop, .mode_set = intel_dvo_mode_set, - .commit = intel_encoder_noop, .disable = intel_encoder_disable, }; diff --git a/drivers/gpu/drm/i915/intel_hdmi.c b/drivers/gpu/drm/i915/intel_hdmi.c index ef84097..c9535ce 100644 --- a/drivers/gpu/drm/i915/intel_hdmi.c +++ b/drivers/gpu/drm/i915/intel_hdmi.c @@ -885,17 +885,13 @@ static void intel_hdmi_destroy(struct drm_connector *connector) static const struct drm_encoder_helper_funcs intel_hdmi_helper_funcs_hsw = { .mode_fixup = intel_hdmi_mode_fixup, - .prepare = intel_encoder_noop, .mode_set = intel_ddi_mode_set, - .commit = intel_encoder_noop, .disable = intel_encoder_disable, }; static const struct drm_encoder_helper_funcs intel_hdmi_helper_funcs = { .mode_fixup = intel_hdmi_mode_fixup, - .prepare = intel_encoder_noop, .mode_set = intel_hdmi_mode_set, - .commit = intel_encoder_noop, .disable = intel_encoder_disable, }; diff --git a/drivers/gpu/drm/i915/intel_lvds.c b/drivers/gpu/drm/i915/intel_lvds.c index fad82b2..5fc7abc 100644 --- a/drivers/gpu/drm/i915/intel_lvds.c +++ b/drivers/gpu/drm/i915/intel_lvds.c @@ -570,9 +570,7 @@ static int intel_lvds_set_property(struct drm_connector *connector, static const struct drm_encoder_helper_funcs intel_lvds_helper_funcs = { .mode_fixup = intel_lvds_mode_fixup, - .prepare = intel_encoder_noop, .mode_set = intel_lvds_mode_set, - .commit = intel_encoder_noop, .disable = intel_encoder_disable, }; diff --git a/drivers/gpu/drm/i915/intel_sdvo.c b/drivers/gpu/drm/i915/intel_sdvo.c index 88fb30d..8cdc674 100644 --- a/drivers/gpu/drm/i915/intel_sdvo.c +++ b/drivers/gpu/drm/i915/intel_sdvo.c @@ -1889,9 +1889,7 @@ done: static const struct drm_encoder_helper_funcs intel_sdvo_helper_funcs = { .mode_fixup = intel_sdvo_mode_fixup, - .prepare = intel_encoder_noop, .mode_set = intel_sdvo_mode_set, - .commit = intel_encoder_noop, .disable = intel_encoder_disable }; diff --git a/drivers/gpu/drm/i915/intel_tv.c b/drivers/gpu/drm/i915/intel_tv.c index 16cb114..5dc08b9 100644 --- a/drivers/gpu/drm/i915/intel_tv.c +++ b/drivers/gpu/drm/i915/intel_tv.c @@ -1479,9 +1479,7 @@ out: static const struct drm_encoder_helper_funcs intel_tv_helper_funcs = { .mode_fixup = intel_tv_mode_fixup, - .prepare = intel_encoder_noop, .mode_set = intel_tv_mode_set, - .commit = intel_encoder_noop, .disable = intel_encoder_disable, }; -- cgit v0.10.2 From dbf2b54e78c6976af1f92cd370055a777ae99208 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Mon, 2 Jul 2012 11:18:29 +0200 Subject: drm/i915: call crtc functions directly Instead of going through the crtc helper function tables. Reviewed-by: Jesse Barnes Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index d37a851..ca17010 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -6578,8 +6578,6 @@ static void intel_crtc_reset(struct drm_crtc *crtc) } static struct drm_crtc_helper_funcs intel_helper_funcs = { - .mode_fixup = intel_crtc_mode_fixup, - .mode_set = intel_crtc_mode_set, .mode_set_base_atomic = intel_pipe_set_base_atomic, .load_lut = intel_crtc_load_lut, .disable = intel_crtc_disable, @@ -6648,8 +6646,8 @@ bool intel_set_mode(struct drm_crtc *crtc, int x, int y, struct drm_framebuffer *old_fb) { struct drm_device *dev = crtc->dev; + drm_i915_private_t *dev_priv = dev->dev_private; struct drm_display_mode *adjusted_mode, saved_mode, saved_hwmode; - struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private; struct drm_encoder_helper_funcs *encoder_funcs; int saved_x, saved_y; struct drm_encoder *encoder; @@ -6691,7 +6689,7 @@ bool intel_set_mode(struct drm_crtc *crtc, } } - if (!(ret = crtc_funcs->mode_fixup(crtc, mode, adjusted_mode))) { + if (!(ret = intel_crtc_mode_fixup(crtc, mode, adjusted_mode))) { DRM_DEBUG_KMS("CRTC fixup failed\n"); goto done; } @@ -6699,12 +6697,12 @@ bool intel_set_mode(struct drm_crtc *crtc, intel_crtc_prepare_encoders(dev); - crtc_funcs->prepare(crtc); + dev_priv->display.crtc_disable(crtc); /* Set up the DPLL and any encoders state that needs to adjust or depend * on the DPLL. */ - ret = !crtc_funcs->mode_set(crtc, mode, adjusted_mode, x, y, old_fb); + ret = !intel_crtc_mode_set(crtc, mode, adjusted_mode, x, y, old_fb); if (!ret) goto done; @@ -6721,7 +6719,7 @@ bool intel_set_mode(struct drm_crtc *crtc, } /* Now enable the clocks, plane, pipe, and connectors that we set up. */ - crtc_funcs->commit(crtc); + dev_priv->display.crtc_enable(crtc); /* Store real post-adjustment hardware mode. */ crtc->hwmode = *adjusted_mode; @@ -7055,9 +7053,6 @@ static void intel_crtc_init(struct drm_device *dev, int pipe) intel_crtc->active = true; /* force the pipe off on setup_init_config */ intel_crtc->bpp = 24; /* default for pre-Ironlake */ - intel_helper_funcs.prepare = dev_priv->display.crtc_disable; - intel_helper_funcs.commit = dev_priv->display.crtc_enable; - drm_crtc_helper_add(&intel_crtc->base, &intel_helper_funcs); } -- cgit v0.10.2 From 08a48469691a1b9ed6ee92e726b66d4e59ffc253 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Mon, 2 Jul 2012 11:43:47 +0200 Subject: drm/i915: WARN when trying to enabled an unused crtc This is the first tiny step towards cross-checking the entire modeset state machine with WARNs. A crtc can only be enabled when it's actually in use, i.e. crtc->active imlies crtc->enabled. Unfortunately we can't (yet) check this when disabling the crtc, because the crtc helpers are a bit slopy with updating state and unconditionally update crtc->enabled before changing the hw state. Fixing that requires quite some more work. Reviewed-by: Jesse Barnes Signed-Off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index ca17010..5ab8694 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -3215,6 +3215,8 @@ static void ironlake_crtc_enable(struct drm_crtc *crtc) u32 temp; bool is_pch_port; + WARN_ON(!crtc->enabled); + /* XXX: For compatability with the crtc helper code, call the encoder's * enable function unconditionally for now. */ if (intel_crtc->active) @@ -3391,6 +3393,8 @@ static void i9xx_crtc_enable(struct drm_crtc *crtc) int pipe = intel_crtc->pipe; int plane = intel_crtc->plane; + WARN_ON(!crtc->enabled); + /* XXX: For compatability with the crtc helper code, call the encoder's * enable function unconditionally for now. */ if (intel_crtc->active) diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index 673e8d4..36991de 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -158,7 +158,15 @@ struct intel_crtc { enum plane plane; u8 lut_r[256], lut_g[256], lut_b[256]; int dpms_mode; - bool active; /* is the crtc on? independent of the dpms mode */ + /* + * Whether the crtc and the connected output pipeline is active. Implies + * that crtc->enabled is set, i.e. the current mode configuration has + * some outputs connected to this crtc. + * + * Atm crtc->enabled is unconditionally updated _before_ the hw state is + * changed, hence we can only check this when enabling the crtc. + */ + bool active; bool primary_disabled; /* is the crtc obscured by a plane? */ bool lowfreq_avail; struct intel_overlay *overlay; -- cgit v0.10.2 From f0947c376f6944ade7079df9f84bbc958a893e0f Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Mon, 2 Jul 2012 13:10:34 +0200 Subject: drm/i915: Add interfaces to read out encoder/connector hw state It is all glorious if we try really hard to only enable/disable an entire display pipe to ensure that everyting happens in the right order. But if we don't know the output configuration when the driver takes over, this will all be for vain because we'll make the hw angry right on the first modeset - we don't know what outputs/ports are enabled and hence have to disable everything in a rather ad-hoc way. Hence we need to be able to read out the current hw state, so that we can properly tear down the current hw state on the first modeset. Obviously this is also a nice preparation for the fastboot work, where we try to avoid the modeset on driver load if it matches what the hw is currently using. Furthermore we'll be using these functions to cross-check the actual hw state with what we think it should be, to ensure that the modeset state machine actually works as advertised. This patch only contains the interface definitions and a little helper for the simple case where we have a 1:1 encoder to connector mapping. Reviewed-by: Jesse Barnes Signed-Off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 5ab8694..7e7569b 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -3584,6 +3584,17 @@ void intel_connector_dpms(struct drm_connector *connector, int mode) encoder->connectors_active = false; } +/* Simple connector->get_hw_state implementation for encoders that support only + * one connector and no cloning and hence the encoder state determines the state + * of the connector. */ +bool intel_connector_get_hw_state(struct intel_connector *connector) +{ + enum pipe pipe; + struct intel_encoder *encoder = connector->encoder; + + return encoder->get_hw_state(encoder, &pipe); +} + 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 36991de..c39c705 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -144,12 +144,19 @@ struct intel_encoder { void (*hot_plug)(struct intel_encoder *); void (*enable)(struct intel_encoder *); void (*disable)(struct intel_encoder *); + /* Read out the current hw state of this connector, returning true if + * the encoder is active. If the encoder is enabled it also set the pipe + * it is connected to in the pipe parameter. */ + bool (*get_hw_state)(struct intel_encoder *, enum pipe *pipe); int crtc_mask; }; struct intel_connector { struct drm_connector base; struct intel_encoder *encoder; + /* Reads out the current hw, returning true if the connector is enabled + * and active (i.e. dpms ON state). */ + bool (*get_hw_state)(struct intel_connector *); }; struct intel_crtc { @@ -426,6 +433,7 @@ 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); +extern bool intel_connector_get_hw_state(struct intel_connector *connector); static inline struct intel_encoder *intel_attached_encoder(struct drm_connector *connector) { -- cgit v0.10.2 From 19d8fe154497bc48e25cbed61066731daca3df43 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Mon, 2 Jul 2012 13:26:27 +0200 Subject: drm/i915/dp: implement get_hw_state Also add some macros to make the pipe computation a bit easier. v2: I've mixed up the CPT and !CPT PORT_TO_PIPE macro variants ... Reviewed-by: Jesse Barnes Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h index fd6a26a..1e5f77a 100644 --- a/drivers/gpu/drm/i915/i915_reg.h +++ b/drivers/gpu/drm/i915/i915_reg.h @@ -4029,6 +4029,8 @@ #define PORT_TRANS_C_SEL_CPT (2<<29) #define PORT_TRANS_SEL_MASK (3<<29) #define PORT_TRANS_SEL_CPT(pipe) ((pipe) << 29) +#define PORT_TO_PIPE(val) (((val) & (1<<30)) >> 30) +#define PORT_TO_PIPE_CPT(val) (((val) & PORT_TRANS_SEL_MASK) >> 29) #define TRANS_DP_CTL_A 0xe0300 #define TRANS_DP_CTL_B 0xe1300 diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index ff993a0..e3928b9 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -1250,6 +1250,54 @@ static void intel_dp_sink_dpms(struct intel_dp *intel_dp, int mode) } } +static bool intel_dp_get_hw_state(struct intel_encoder *encoder, + enum pipe *pipe) +{ + struct intel_dp *intel_dp = enc_to_intel_dp(&encoder->base); + struct drm_device *dev = encoder->base.dev; + struct drm_i915_private *dev_priv = dev->dev_private; + u32 tmp = I915_READ(intel_dp->output_reg); + + if (!(tmp & DP_PORT_EN)) + return false; + + if (is_cpu_edp(intel_dp) && IS_GEN7(dev)) { + *pipe = PORT_TO_PIPE_CPT(tmp); + } else if (!HAS_PCH_CPT(dev) || is_cpu_edp(intel_dp)) { + *pipe = PORT_TO_PIPE(tmp); + } else { + u32 trans_sel; + u32 trans_dp; + int i; + + switch (intel_dp->output_reg) { + case PCH_DP_B: + trans_sel = TRANS_DP_PORT_SEL_B; + break; + case PCH_DP_C: + trans_sel = TRANS_DP_PORT_SEL_C; + break; + case PCH_DP_D: + trans_sel = TRANS_DP_PORT_SEL_D; + break; + default: + return true; + } + + for_each_pipe(i) { + trans_dp = I915_READ(TRANS_DP_CTL(i)); + if ((trans_dp & TRANS_DP_PORT_SEL_MASK) == trans_sel) { + *pipe = i; + return true; + } + } + } + + DRM_DEBUG_KMS("No pipe for dp port 0x%x found\n", intel_dp->output_reg); + + return true; +} + static void intel_disable_dp(struct intel_encoder *encoder) { struct intel_dp *intel_dp = enc_to_intel_dp(&encoder->base); @@ -2486,6 +2534,8 @@ intel_dp_init(struct drm_device *dev, int output_reg, enum port port) intel_encoder->enable = intel_enable_dp; intel_encoder->disable = intel_disable_dp; + intel_encoder->get_hw_state = intel_dp_get_hw_state; + intel_connector->get_hw_state = intel_connector_get_hw_state; /* Set up the DDC bus. */ switch (port) { -- cgit v0.10.2 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 diff --git a/drivers/gpu/drm/i915/intel_ddi.c b/drivers/gpu/drm/i915/intel_ddi.c index 38a7006..bfe3754 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 c39c705..4daa7e6 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 c9535ce..e7d5078 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 v0.10.2 From 9a8ee983e0b77a8b07d3f20a7627202e5f9a7ba5 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Mon, 2 Jul 2012 13:34:59 +0200 Subject: drm/i915/tv: implement get_hw_state Reviewed-by: Jesse Barnes Signed-Off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/intel_tv.c b/drivers/gpu/drm/i915/intel_tv.c index 5dc08b9..98ad7fd 100644 --- a/drivers/gpu/drm/i915/intel_tv.c +++ b/drivers/gpu/drm/i915/intel_tv.c @@ -836,6 +836,21 @@ static struct intel_tv *intel_attached_tv(struct drm_connector *connector) base); } +static bool +intel_tv_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; + u32 tmp = I915_READ(TV_CTL); + + if (!(tmp & TV_ENC_ENABLE)) + return false; + + *pipe = PORT_TO_PIPE(tmp); + + return true; +} + static void intel_enable_tv(struct intel_encoder *encoder) { @@ -1616,6 +1631,8 @@ intel_tv_init(struct drm_device *dev) intel_encoder->enable = intel_enable_tv; intel_encoder->disable = intel_disable_tv; + intel_encoder->get_hw_state = intel_tv_get_hw_state; + intel_connector->get_hw_state = intel_connector_get_hw_state; intel_connector_attach_encoder(intel_connector, intel_encoder); intel_encoder->type = INTEL_OUTPUT_TVOUT; -- cgit v0.10.2 From b1dc332c4d1524fe5d901ca91aa4c4e776526b4f Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Mon, 2 Jul 2012 21:09:00 +0200 Subject: drm/i915/lvds: implement get_hw_state Reviewed-by: Jesse Barnes Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/intel_lvds.c b/drivers/gpu/drm/i915/intel_lvds.c index 5fc7abc..52f9e7f 100644 --- a/drivers/gpu/drm/i915/intel_lvds.c +++ b/drivers/gpu/drm/i915/intel_lvds.c @@ -65,6 +65,32 @@ static struct intel_lvds *intel_attached_lvds(struct drm_connector *connector) struct intel_lvds, base); } +static bool intel_lvds_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; + u32 lvds_reg, tmp; + + if (HAS_PCH_SPLIT(dev)) { + lvds_reg = PCH_LVDS; + } else { + lvds_reg = LVDS; + } + + tmp = I915_READ(lvds_reg); + + if (!(tmp & LVDS_PORT_EN)) + return false; + + if (HAS_PCH_CPT(dev)) + *pipe = PORT_TO_PIPE_CPT(tmp); + else + *pipe = PORT_TO_PIPE(tmp); + + return true; +} + /** * Sets the power state for the panel. */ @@ -937,6 +963,8 @@ bool intel_lvds_init(struct drm_device *dev) intel_encoder->enable = intel_enable_lvds; intel_encoder->disable = intel_disable_lvds; + intel_encoder->get_hw_state = intel_lvds_get_hw_state; + intel_connector->get_hw_state = intel_connector_get_hw_state; intel_connector_attach_encoder(intel_connector, intel_encoder); intel_encoder->type = INTEL_OUTPUT_LVDS; -- cgit v0.10.2 From e403fc941aa94ff6cf598c632e5c02e96eed022e Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Mon, 2 Jul 2012 13:41:21 +0200 Subject: drm/i915/crt: implement get_hw_state Note that even though this connector is cloneable we still can use the exact same test to check whether the connector is on or whether the encoder is enabled - both the dpms code and the encoder disable/enable frob the exact same hw state. For dvo/sdvo outputs, this will be different. Reviewed-by: Jesse Barnes Signed-Off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/intel_crt.c b/drivers/gpu/drm/i915/intel_crt.c index 2e25810..b0fadd0 100644 --- a/drivers/gpu/drm/i915/intel_crt.c +++ b/drivers/gpu/drm/i915/intel_crt.c @@ -61,6 +61,27 @@ static struct intel_crt *intel_encoder_to_crt(struct intel_encoder *encoder) return container_of(encoder, struct intel_crt, base); } +static bool intel_crt_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_crt *crt = intel_encoder_to_crt(encoder); + u32 tmp; + + tmp = I915_READ(crt->adpa_reg); + + if (!(tmp & ADPA_DAC_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_disable_crt(struct intel_encoder *encoder) { struct drm_i915_private *dev_priv = encoder->base.dev->dev_private; @@ -710,6 +731,8 @@ void intel_crt_init(struct drm_device *dev) crt->base.disable = intel_disable_crt; crt->base.enable = intel_enable_crt; + crt->base.get_hw_state = intel_crt_get_hw_state; + intel_connector->get_hw_state = intel_connector_get_hw_state; drm_encoder_helper_add(&crt->base.base, &crt_encoder_funcs); drm_connector_helper_add(connector, &intel_crt_connector_helper_funcs); -- cgit v0.10.2 From 4ac41f47f8f6ba1d05b39783ea0819435074ef37 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Mon, 2 Jul 2012 14:54:00 +0200 Subject: drm/i915/sdvo: implement get_hw_state SDVO is the first real special case - we support multiple outputs on the same encoder and the encoder dpms state isn't the same as when just disabling the outputs when the encoder is cloned. Hence we need a real connector get_hw_state function which inquires the sdvo encoder about its active outputs. Reviewed-by: Jesse Barnes Signed-Off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/intel_sdvo.c b/drivers/gpu/drm/i915/intel_sdvo.c index 8cdc674..4a735a5 100644 --- a/drivers/gpu/drm/i915/intel_sdvo.c +++ b/drivers/gpu/drm/i915/intel_sdvo.c @@ -628,6 +628,14 @@ static bool intel_sdvo_set_active_outputs(struct intel_sdvo *intel_sdvo, &outputs, sizeof(outputs)); } +static bool intel_sdvo_get_active_outputs(struct intel_sdvo *intel_sdvo, + u16 *outputs) +{ + return intel_sdvo_get_value(intel_sdvo, + SDVO_CMD_GET_ACTIVE_OUTPUTS, + outputs, sizeof(*outputs)); +} + static bool intel_sdvo_set_encoder_power_state(struct intel_sdvo *intel_sdvo, int mode) { @@ -1142,6 +1150,42 @@ static void intel_sdvo_mode_set(struct drm_encoder *encoder, intel_sdvo_write_sdvox(intel_sdvo, sdvox); } +static bool intel_sdvo_connector_get_hw_state(struct intel_connector *connector) +{ + struct intel_sdvo_connector *intel_sdvo_connector = + to_intel_sdvo_connector(&connector->base); + struct intel_sdvo *intel_sdvo = intel_attached_sdvo(&connector->base); + u16 active_outputs; + + intel_sdvo_get_active_outputs(intel_sdvo, &active_outputs); + + if (active_outputs & intel_sdvo_connector->output_flag) + return true; + else + return false; +} + +static bool intel_sdvo_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_sdvo *intel_sdvo = to_intel_sdvo(&encoder->base); + u32 tmp; + + tmp = I915_READ(intel_sdvo->sdvo_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_disable_sdvo(struct intel_encoder *encoder) { struct drm_i915_private *dev_priv = encoder->base.dev->dev_private; @@ -2066,6 +2110,7 @@ intel_sdvo_connector_init(struct intel_sdvo_connector *connector, connector->base.base.interlace_allowed = 1; connector->base.base.doublescan_allowed = 0; connector->base.base.display_info.subpixel_order = SubPixelHorizontalRGB; + connector->base.get_hw_state = intel_sdvo_connector_get_hw_state; intel_connector_attach_encoder(&connector->base, &encoder->base); drm_sysfs_connector_add(&connector->base.base); @@ -2619,6 +2664,7 @@ bool intel_sdvo_init(struct drm_device *dev, uint32_t sdvo_reg, bool is_sdvob) intel_encoder->disable = intel_disable_sdvo; intel_encoder->enable = intel_enable_sdvo; + intel_encoder->get_hw_state = intel_sdvo_get_hw_state; /* In default case sdvo lvds is false */ if (!intel_sdvo_get_capabilities(intel_sdvo, &intel_sdvo->caps)) -- cgit v0.10.2 From 732ce74f4adfcdac84862fb74c6897b4a152d5e1 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Mon, 2 Jul 2012 15:09:45 +0200 Subject: drm/i915/dvo: implement get_hw_state Similar to the sdvo code we poke the dvo encoder whether the output is active. Safe that dvo encoders are not standardized, so this requires a new callback into the dvo chip driver. Hence implement that for all 6 dvo drivers. v2: With the newly added ns2501 we now have 6 dvo drivers instead of just 5 ... Acked-by: Jesse Barnes Signed-Off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/dvo.h b/drivers/gpu/drm/i915/dvo.h index 0fa839e..74b5efc 100644 --- a/drivers/gpu/drm/i915/dvo.h +++ b/drivers/gpu/drm/i915/dvo.h @@ -114,6 +114,12 @@ struct intel_dvo_dev_ops { */ enum drm_connector_status (*detect)(struct intel_dvo_device *dvo); + /* + * Probe the current hw status, returning true if the connected output + * is active. + */ + bool (*get_hw_state)(struct intel_dvo_device *dev); + /** * Query the device for the modes it provides. * diff --git a/drivers/gpu/drm/i915/dvo_ch7017.c b/drivers/gpu/drm/i915/dvo_ch7017.c index 71e7650..86b27d1 100644 --- a/drivers/gpu/drm/i915/dvo_ch7017.c +++ b/drivers/gpu/drm/i915/dvo_ch7017.c @@ -359,6 +359,18 @@ static void ch7017_dpms(struct intel_dvo_device *dvo, bool enable) msleep(20); } +static bool ch7017_get_hw_state(struct intel_dvo_device *dvo) +{ + uint8_t val; + + ch7017_read(dvo, CH7017_LVDS_POWER_DOWN, &val); + + if (val & CH7017_LVDS_POWER_DOWN_EN) + return false; + else + return true; +} + static void ch7017_dump_regs(struct intel_dvo_device *dvo) { uint8_t val; @@ -396,6 +408,7 @@ struct intel_dvo_dev_ops ch7017_ops = { .mode_valid = ch7017_mode_valid, .mode_set = ch7017_mode_set, .dpms = ch7017_dpms, + .get_hw_state = ch7017_get_hw_state, .dump_regs = ch7017_dump_regs, .destroy = ch7017_destroy, }; diff --git a/drivers/gpu/drm/i915/dvo_ch7xxx.c b/drivers/gpu/drm/i915/dvo_ch7xxx.c index c1dea5b..38f3a6c 100644 --- a/drivers/gpu/drm/i915/dvo_ch7xxx.c +++ b/drivers/gpu/drm/i915/dvo_ch7xxx.c @@ -297,6 +297,18 @@ static void ch7xxx_dpms(struct intel_dvo_device *dvo, bool enable) ch7xxx_writeb(dvo, CH7xxx_PM, CH7xxx_PM_FPD); } +static bool ch7xxx_get_hw_state(struct intel_dvo_device *dvo) +{ + u8 val; + + ch7xxx_readb(dvo, CH7xxx_PM, &val); + + if (val & CH7xxx_PM_FPD) + return false; + else + return true; +} + static void ch7xxx_dump_regs(struct intel_dvo_device *dvo) { int i; @@ -326,6 +338,7 @@ struct intel_dvo_dev_ops ch7xxx_ops = { .mode_valid = ch7xxx_mode_valid, .mode_set = ch7xxx_mode_set, .dpms = ch7xxx_dpms, + .get_hw_state = ch7xxx_get_hw_state, .dump_regs = ch7xxx_dump_regs, .destroy = ch7xxx_destroy, }; diff --git a/drivers/gpu/drm/i915/dvo_ivch.c b/drivers/gpu/drm/i915/dvo_ivch.c index fa8ff6b..baaf65b 100644 --- a/drivers/gpu/drm/i915/dvo_ivch.c +++ b/drivers/gpu/drm/i915/dvo_ivch.c @@ -323,6 +323,20 @@ static void ivch_dpms(struct intel_dvo_device *dvo, bool enable) udelay(16 * 1000); } +static bool ivch_get_hw_state(struct intel_dvo_device *dvo) +{ + uint16_t vr01; + + /* Set the new power state of the panel. */ + if (!ivch_read(dvo, VR01, &vr01)) + return false; + + if (vr01 & VR01_LCD_ENABLE) + return true; + else + return false; +} + static void ivch_mode_set(struct intel_dvo_device *dvo, struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode) @@ -413,6 +427,7 @@ static void ivch_destroy(struct intel_dvo_device *dvo) struct intel_dvo_dev_ops ivch_ops = { .init = ivch_init, .dpms = ivch_dpms, + .get_hw_state = ivch_get_hw_state, .mode_valid = ivch_mode_valid, .mode_set = ivch_mode_set, .detect = ivch_detect, diff --git a/drivers/gpu/drm/i915/dvo_ns2501.c b/drivers/gpu/drm/i915/dvo_ns2501.c index c4d9f2f..c4a255b 100644 --- a/drivers/gpu/drm/i915/dvo_ns2501.c +++ b/drivers/gpu/drm/i915/dvo_ns2501.c @@ -493,6 +493,20 @@ static void ns2501_mode_set(struct intel_dvo_device *dvo, } /* set the NS2501 power state */ +static bool ns2501_get_hw_state(struct intel_dvo_device *dvo) +{ + unsigned char ch; + + if (!ns2501_readb(dvo, NS2501_REG8, &ch)) + return false; + + if (ch & NS2501_8_PD) + return true; + else + return false; +} + +/* set the NS2501 power state */ static void ns2501_dpms(struct intel_dvo_device *dvo, bool enable) { bool ok; @@ -568,6 +582,7 @@ struct intel_dvo_dev_ops ns2501_ops = { .mode_valid = ns2501_mode_valid, .mode_set = ns2501_mode_set, .dpms = ns2501_dpms, + .get_hw_state = ns2501_get_hw_state, .dump_regs = ns2501_dump_regs, .destroy = ns2501_destroy, }; diff --git a/drivers/gpu/drm/i915/dvo_sil164.c b/drivers/gpu/drm/i915/dvo_sil164.c index cc24c1c..4debd32 100644 --- a/drivers/gpu/drm/i915/dvo_sil164.c +++ b/drivers/gpu/drm/i915/dvo_sil164.c @@ -226,6 +226,21 @@ static void sil164_dpms(struct intel_dvo_device *dvo, bool enable) return; } +static bool sil164_get_hw_state(struct intel_dvo_device *dvo) +{ + int ret; + unsigned char ch; + + ret = sil164_readb(dvo, SIL164_REG8, &ch); + if (ret == false) + return false; + + if (ch & SIL164_8_PD) + return true; + else + return false; +} + static void sil164_dump_regs(struct intel_dvo_device *dvo) { uint8_t val; @@ -258,6 +273,7 @@ struct intel_dvo_dev_ops sil164_ops = { .mode_valid = sil164_mode_valid, .mode_set = sil164_mode_set, .dpms = sil164_dpms, + .get_hw_state = sil164_get_hw_state, .dump_regs = sil164_dump_regs, .destroy = sil164_destroy, }; diff --git a/drivers/gpu/drm/i915/dvo_tfp410.c b/drivers/gpu/drm/i915/dvo_tfp410.c index 097b3e8..e17f1b0 100644 --- a/drivers/gpu/drm/i915/dvo_tfp410.c +++ b/drivers/gpu/drm/i915/dvo_tfp410.c @@ -249,6 +249,19 @@ static void tfp410_dpms(struct intel_dvo_device *dvo, bool enable) tfp410_writeb(dvo, TFP410_CTL_1, ctl1); } +static bool tfp410_get_hw_state(struct intel_dvo_device *dvo) +{ + uint8_t ctl1; + + if (!tfp410_readb(dvo, TFP410_CTL_1, &ctl1)) + return false; + + if (ctl1 & TFP410_CTL_1_PD) + return true; + else + return false; +} + static void tfp410_dump_regs(struct intel_dvo_device *dvo) { uint8_t val, val2; @@ -299,6 +312,7 @@ struct intel_dvo_dev_ops tfp410_ops = { .mode_valid = tfp410_mode_valid, .mode_set = tfp410_mode_set, .dpms = tfp410_dpms, + .get_hw_state = tfp410_get_hw_state, .dump_regs = tfp410_dump_regs, .destroy = tfp410_destroy, }; diff --git a/drivers/gpu/drm/i915/intel_dvo.c b/drivers/gpu/drm/i915/intel_dvo.c index 84c0867..e9397b7 100644 --- a/drivers/gpu/drm/i915/intel_dvo.c +++ b/drivers/gpu/drm/i915/intel_dvo.c @@ -105,6 +105,31 @@ static struct intel_dvo *intel_attached_dvo(struct drm_connector *connector) struct intel_dvo, base); } +static bool intel_dvo_connector_get_hw_state(struct intel_connector *connector) +{ + struct intel_dvo *intel_dvo = intel_attached_dvo(&connector->base); + + return intel_dvo->dev.dev_ops->get_hw_state(&intel_dvo->dev); +} + +static bool intel_dvo_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_dvo *intel_dvo = enc_to_intel_dvo(&encoder->base); + u32 tmp; + + tmp = I915_READ(intel_dvo->dev.dvo_reg); + + if (!(tmp & DVO_ENABLE)) + return false; + + *pipe = PORT_TO_PIPE(tmp); + + return true; +} + static void intel_disable_dvo(struct intel_encoder *encoder) { struct drm_i915_private *dev_priv = encoder->base.dev->dev_private; @@ -414,6 +439,8 @@ void intel_dvo_init(struct drm_device *dev) intel_encoder->disable = intel_disable_dvo; intel_encoder->enable = intel_enable_dvo; + intel_encoder->get_hw_state = intel_dvo_get_hw_state; + intel_connector->get_hw_state = intel_dvo_connector_get_hw_state; /* Now, try to find a controller */ for (i = 0; i < ARRAY_SIZE(intel_dvo_devices); i++) { -- cgit v0.10.2 From 24929352481f085c5f85d4d4cbc919ddf106d381 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Mon, 2 Jul 2012 20:28:59 +0200 Subject: drm/i915: read out the modeset hw state at load and resume time ... instead of resetting a few things and hoping that this will work out. To properly disable the output pipelines at the initial modeset after resume or boot up we need to have an accurate picture of which outputs are enabled and connected to which crtcs. Otherwise we risk disabling things at the wrong time, which can lead to hangs (or at least royally confused panels), both requiring a walk to the reset button to fix. Hence read out the hw state with the freshly introduce get_hw_state functions and then sanitize it afterwards. For a full modeset readout (which would allow us to avoid the initial modeset at boot up) a few things are still missing: - Reading out the mode from the pipe, especially the dotclock computation is quite some fun. - Reading out the parameters for the stolen memory framebuffer and wrapping it up. - Reading out the pch pll connections - luckily the disable code simply bails out if the crtc doesn't have a pch pll attached (even for configurations that would need one). This patch here turned up tons of smelly stuff around resume: We restore tons of register in seemingly random way (well, not quite, but we're not too careful either), which leaves the hw in a rather ill-defined state: E.g. the port registers are sometimes unconditionally restore (lvds, crt), leaving us with an active encoder/connector but no active pipe connected to it. Luckily the hw state sanitizer detects this madness and fixes things up a bit. v2: When checking whether an encoder with active connectors has a crtc wire up to it, check for both the crtc _and_ it's active state. v3: - Extract intel_sanitize_encoder. - Manually disable active encoders without an active pipe. v4: Correclty fix up the pipe<->plane mapping on machines where we switch pipes/planes. Noticed by Chris Wilson, who also provided the fixup. v5: Spelling fix in a comment, noticed by Paulo Zanoni Reviewed-by: Jesse Barnes Signed-Off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c index 7ebb13b..4abac6d 100644 --- a/drivers/gpu/drm/i915/i915_drv.c +++ b/drivers/gpu/drm/i915/i915_drv.c @@ -543,6 +543,7 @@ static int i915_drm_thaw(struct drm_device *dev) mutex_unlock(&dev->struct_mutex); intel_modeset_init_hw(dev); + intel_modeset_setup_hw_state(dev); drm_mode_config_reset(dev); drm_irq_install(dev); diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index fb46c6f..9fce782 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -1539,6 +1539,7 @@ extern void intel_modeset_init(struct drm_device *dev); extern void intel_modeset_gem_init(struct drm_device *dev); extern void intel_modeset_cleanup(struct drm_device *dev); extern int intel_modeset_vga_set_state(struct drm_device *dev, bool state); +extern void intel_modeset_setup_hw_state(struct drm_device *dev); extern bool intel_fbc_enabled(struct drm_device *dev); extern void intel_disable_fbc(struct drm_device *dev); extern bool ironlake_set_drps(struct drm_device *dev, u8 val); diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 7e7569b..8c8dbc9 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -3589,7 +3589,7 @@ void intel_connector_dpms(struct drm_connector *connector, int mode) * of the connector. */ bool intel_connector_get_hw_state(struct intel_connector *connector) { - enum pipe pipe; + enum pipe pipe = 0; struct intel_encoder *encoder = connector->encoder; return encoder->get_hw_state(encoder, &pipe); @@ -6533,65 +6533,6 @@ free_work: return ret; } -static void intel_sanitize_modesetting(struct drm_device *dev, - int pipe, int plane) -{ - 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(i) { - reg = PIPECONF(i); - I915_WRITE(reg, I915_READ(reg) & ~PIPECONF_FRAME_START_DELAY_MASK); - } - - if (HAS_PCH_SPLIT(dev)) - return; - - /* Who knows what state these registers were left in by the BIOS or - * grub? - * - * If we leave the registers in a conflicting state (e.g. with the - * display plane reading from the other pipe than the one we intend - * to use) then when we attempt to teardown the active mode, we will - * not disable the pipes and planes in the correct order -- leaving - * a plane reading from a disabled pipe and possibly leading to - * undefined behaviour. - */ - - reg = DSPCNTR(plane); - val = I915_READ(reg); - - if ((val & DISPLAY_PLANE_ENABLE) == 0) - return; - if (!!(val & DISPPLANE_SEL_PIPE_MASK) == pipe) - return; - - /* This display plane is active and attached to the other CPU pipe. */ - pipe = !pipe; - - /* Disable the plane and wait for it to stop reading from the pipe. */ - intel_disable_plane(dev_priv, plane, pipe); - intel_disable_pipe(dev_priv, pipe); -} - -static void intel_crtc_reset(struct drm_crtc *crtc) -{ - struct drm_device *dev = crtc->dev; - struct intel_crtc *intel_crtc = to_intel_crtc(crtc); - - /* Reset flags back to the 'unknown' status so that they - * will be correctly set on the initial modeset. - */ - intel_crtc->dpms_mode = -1; - - /* We need to fix up any BIOS configuration that conflicts with - * our expectations. - */ - intel_sanitize_modesetting(dev, intel_crtc->pipe, intel_crtc->plane); -} - static struct drm_crtc_helper_funcs intel_helper_funcs = { .mode_set_base_atomic = intel_pipe_set_base_atomic, .load_lut = intel_crtc_load_lut, @@ -7006,7 +6947,6 @@ fail: } static const struct drm_crtc_funcs intel_crtc_funcs = { - .reset = intel_crtc_reset, .cursor_set = intel_crtc_cursor_set, .cursor_move = intel_crtc_cursor_move, .gamma_set = intel_crtc_gamma_set, @@ -7064,8 +7004,6 @@ static void intel_crtc_init(struct drm_device *dev, int pipe) dev_priv->plane_to_crtc_mapping[intel_crtc->plane] = &intel_crtc->base; dev_priv->pipe_to_crtc_mapping[intel_crtc->pipe] = &intel_crtc->base; - intel_crtc_reset(&intel_crtc->base); - intel_crtc->active = true; /* force the pipe off on setup_init_config */ intel_crtc->bpp = 24; /* default for pre-Ironlake */ drm_crtc_helper_add(&intel_crtc->base, &intel_helper_funcs); @@ -7273,9 +7211,6 @@ static void intel_setup_outputs(struct drm_device *dev) intel_encoder_clones(encoder); } - /* disable all the possible outputs/crtcs before entering KMS mode */ - drm_helper_disable_unused_functions(dev); - if (HAS_PCH_IBX(dev) || HAS_PCH_CPT(dev)) ironlake_init_pch_refclk(dev); } @@ -7634,11 +7569,222 @@ void intel_modeset_init(struct drm_device *dev) intel_setup_outputs(dev); } +static void +intel_connector_break_all_links(struct intel_connector *connector) +{ + connector->base.dpms = DRM_MODE_DPMS_OFF; + connector->base.encoder = NULL; + connector->encoder->connectors_active = false; + connector->encoder->base.crtc = NULL; +} + +static void intel_sanitize_crtc(struct intel_crtc *crtc) +{ + struct drm_device *dev = crtc->base.dev; + struct drm_i915_private *dev_priv = dev->dev_private; + u32 reg, val; + + /* Clear the dpms state for compatibility with code still using that + * deprecated state variable. */ + crtc->dpms_mode = -1; + + /* Clear any frame start delays used for debugging left by the BIOS */ + reg = PIPECONF(crtc->pipe); + I915_WRITE(reg, I915_READ(reg) & ~PIPECONF_FRAME_START_DELAY_MASK); + + /* We need to sanitize the plane -> pipe mapping first because this will + * disable the crtc (and hence change the state) if it is wrong. */ + if (!HAS_PCH_SPLIT(dev)) { + struct intel_connector *connector; + bool plane; + + reg = DSPCNTR(crtc->plane); + val = I915_READ(reg); + + if ((val & DISPLAY_PLANE_ENABLE) == 0 && + (!!(val & DISPPLANE_SEL_PIPE_MASK) == crtc->pipe)) + goto ok; + + DRM_DEBUG_KMS("[CRTC:%d] wrong plane connection detected!\n", + crtc->base.base.id); + + /* Pipe has the wrong plane attached and the plane is active. + * Temporarily change the plane mapping and disable everything + * ... */ + plane = crtc->plane; + crtc->plane = !plane; + dev_priv->display.crtc_disable(&crtc->base); + crtc->plane = plane; + + /* ... and break all links. */ + list_for_each_entry(connector, &dev->mode_config.connector_list, + base.head) { + if (connector->encoder->base.crtc != &crtc->base) + continue; + + intel_connector_break_all_links(connector); + } + + WARN_ON(crtc->active); + crtc->base.enabled = false; + } +ok: + + /* Adjust the state of the output pipe according to whether we + * have active connectors/encoders. */ + intel_crtc_update_dpms(&crtc->base); + + if (crtc->active != crtc->base.enabled) { + struct intel_encoder *encoder; + + /* This can happen either due to bugs in the get_hw_state + * functions or because the pipe is force-enabled due to the + * pipe A quirk. */ + DRM_DEBUG_KMS("[CRTC:%d] hw state adjusted, was %s, now %s\n", + crtc->base.base.id, + crtc->base.enabled ? "enabled" : "disabled", + crtc->active ? "enabled" : "disabled"); + + crtc->base.enabled = crtc->active; + + /* Because we only establish the connector -> encoder -> + * crtc links if something is active, this means the + * crtc is now deactivated. Break the links. connector + * -> encoder links are only establish when things are + * actually up, hence no need to break them. */ + WARN_ON(crtc->active); + + for_each_encoder_on_crtc(dev, &crtc->base, encoder) { + WARN_ON(encoder->connectors_active); + encoder->base.crtc = NULL; + } + } +} + +static void intel_sanitize_encoder(struct intel_encoder *encoder) +{ + struct intel_connector *connector; + struct drm_device *dev = encoder->base.dev; + + /* We need to check both for a crtc link (meaning that the + * encoder is active and trying to read from a pipe) and the + * pipe itself being active. */ + bool has_active_crtc = encoder->base.crtc && + to_intel_crtc(encoder->base.crtc)->active; + + if (encoder->connectors_active && !has_active_crtc) { + DRM_DEBUG_KMS("[ENCODER:%d:%s] has active connectors but no active pipe!\n", + encoder->base.base.id, + drm_get_encoder_name(&encoder->base)); + + /* Connector is active, but has no active pipe. This is + * fallout from our resume register restoring. Disable + * the encoder manually again. */ + if (encoder->base.crtc) { + DRM_DEBUG_KMS("[ENCODER:%d:%s] manually disabled\n", + encoder->base.base.id, + drm_get_encoder_name(&encoder->base)); + encoder->disable(encoder); + } + + /* Inconsistent output/port/pipe state happens presumably due to + * a bug in one of the get_hw_state functions. Or someplace else + * in our code, like the register restore mess on resume. Clamp + * things to off as a safer default. */ + list_for_each_entry(connector, + &dev->mode_config.connector_list, + base.head) { + if (connector->encoder != encoder) + continue; + + intel_connector_break_all_links(connector); + } + } + /* Enabled encoders without active connectors will be fixed in + * the crtc fixup. */ +} + +/* Scan out the current hw modeset state, sanitizes it and maps it into the drm + * and i915 state tracking structures. */ +void intel_modeset_setup_hw_state(struct drm_device *dev) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + enum pipe pipe; + u32 tmp; + struct intel_crtc *crtc; + struct intel_encoder *encoder; + struct intel_connector *connector; + + for_each_pipe(pipe) { + crtc = to_intel_crtc(dev_priv->pipe_to_crtc_mapping[pipe]); + + tmp = I915_READ(PIPECONF(pipe)); + if (tmp & PIPECONF_ENABLE) + crtc->active = true; + else + crtc->active = false; + + crtc->base.enabled = crtc->active; + + DRM_DEBUG_KMS("[CRTC:%d] hw state readout: %s\n", + crtc->base.base.id, + crtc->active ? "enabled" : "disabled"); + } + + list_for_each_entry(encoder, &dev->mode_config.encoder_list, + base.head) { + pipe = 0; + + if (encoder->get_hw_state(encoder, &pipe)) { + encoder->base.crtc = + dev_priv->pipe_to_crtc_mapping[pipe]; + } else { + encoder->base.crtc = NULL; + } + + encoder->connectors_active = false; + DRM_DEBUG_KMS("[ENCODER:%d:%s] hw state readout: %s, pipe=%i\n", + encoder->base.base.id, + drm_get_encoder_name(&encoder->base), + encoder->base.crtc ? "enabled" : "disabled", + pipe); + } + + list_for_each_entry(connector, &dev->mode_config.connector_list, + base.head) { + if (connector->get_hw_state(connector)) { + connector->base.dpms = DRM_MODE_DPMS_ON; + connector->encoder->connectors_active = true; + connector->base.encoder = &connector->encoder->base; + } else { + connector->base.dpms = DRM_MODE_DPMS_OFF; + connector->base.encoder = NULL; + } + DRM_DEBUG_KMS("[CONNECTOR:%d:%s] hw state readout: %s\n", + connector->base.base.id, + drm_get_connector_name(&connector->base), + connector->base.encoder ? "enabled" : "disabled"); + } + + /* HW state is read out, now we need to sanitize this mess. */ + list_for_each_entry(encoder, &dev->mode_config.encoder_list, + base.head) { + intel_sanitize_encoder(encoder); + } + + for_each_pipe(pipe) { + crtc = to_intel_crtc(dev_priv->pipe_to_crtc_mapping[pipe]); + intel_sanitize_crtc(crtc); + } +} + void intel_modeset_gem_init(struct drm_device *dev) { intel_modeset_init_hw(dev); intel_setup_overlay(dev); + + intel_modeset_setup_hw_state(dev); } void intel_modeset_cleanup(struct drm_device *dev) -- cgit v0.10.2 From 0a91ca29215a41760cca03999498959d0e05d9b3 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Mon, 2 Jul 2012 21:54:27 +0200 Subject: drm/i915: check connector hw/sw state Atm we can only check the connector state after a dpms call - while doing modeset with the copy&pasted crtc helper code things are too ill-defined for proper checking. But the idea is very much to call this check from the modeset code, too. v2: Fix dpms check and don't presume that if the hw isn't on that it must not be linked up with an encoder (it could simply be switched off with the dpms state). Reviewed-by: Jesse Barnes Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/intel_crt.c b/drivers/gpu/drm/i915/intel_crt.c index b0fadd0..c002d26 100644 --- a/drivers/gpu/drm/i915/intel_crt.c +++ b/drivers/gpu/drm/i915/intel_crt.c @@ -176,6 +176,8 @@ static void intel_crt_dpms(struct drm_connector *connector, int mode) intel_crtc_update_dpms(crtc); } + + intel_connector_check_state(to_intel_connector(connector)); } static int intel_crt_mode_valid(struct drm_connector *connector, diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 8c8dbc9..15e4a65 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -3562,6 +3562,41 @@ void intel_encoder_dpms(struct intel_encoder *encoder, int mode) } } +/* Cross check the actual hw state with our own modeset state tracking (and it's + * internal consistency). */ +void intel_connector_check_state(struct intel_connector *connector) +{ + if (connector->get_hw_state(connector)) { + struct intel_encoder *encoder = connector->encoder; + struct drm_crtc *crtc; + bool encoder_enabled; + enum pipe pipe; + + DRM_DEBUG_KMS("[CONNECTOR:%d:%s]\n", + connector->base.base.id, + drm_get_connector_name(&connector->base)); + + WARN(connector->base.dpms == DRM_MODE_DPMS_OFF, + "wrong connector dpms state\n"); + WARN(connector->base.encoder != &encoder->base, + "active connector not linked to encoder\n"); + WARN(!encoder->connectors_active, + "encoder->connectors_active not set\n"); + + encoder_enabled = encoder->get_hw_state(encoder, &pipe); + WARN(!encoder_enabled, "encoder not enabled\n"); + if (WARN_ON(!encoder->base.crtc)) + return; + + crtc = encoder->base.crtc; + + WARN(!crtc->enabled, "crtc not enabled\n"); + WARN(!to_intel_crtc(crtc)->active, "crtc not active\n"); + WARN(pipe != to_intel_crtc(crtc)->pipe, + "encoder active on the wrong pipe\n"); + } +} + /* Even simpler default implementation, if there's really no special case to * consider. */ void intel_connector_dpms(struct drm_connector *connector, int mode) @@ -3582,6 +3617,8 @@ void intel_connector_dpms(struct drm_connector *connector, int mode) intel_encoder_dpms(encoder, mode); else encoder->connectors_active = false; + + intel_connector_check_state(to_intel_connector(connector)); } /* Simple connector->get_hw_state implementation for encoders that support only diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index e3928b9..96fd1e7 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -1367,6 +1367,8 @@ intel_dp_dpms(struct drm_connector *connector, int mode) intel_encoder_dpms(&intel_dp->base, mode); WARN_ON(intel_dp->dpms_mode != DRM_MODE_DPMS_ON); } + + intel_connector_check_state(to_intel_connector(connector)); } /* diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index 4daa7e6..e2116d9 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -434,6 +434,7 @@ 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); extern bool intel_connector_get_hw_state(struct intel_connector *connector); +extern void intel_connector_check_state(struct intel_connector *); static inline struct intel_encoder *intel_attached_encoder(struct drm_connector *connector) { diff --git a/drivers/gpu/drm/i915/intel_dvo.c b/drivers/gpu/drm/i915/intel_dvo.c index e9397b7..17dc8be 100644 --- a/drivers/gpu/drm/i915/intel_dvo.c +++ b/drivers/gpu/drm/i915/intel_dvo.c @@ -188,6 +188,8 @@ static void intel_dvo_dpms(struct drm_connector *connector, int mode) intel_crtc_update_dpms(crtc); } + + intel_connector_check_state(to_intel_connector(connector)); } static int intel_dvo_mode_valid(struct drm_connector *connector, diff --git a/drivers/gpu/drm/i915/intel_sdvo.c b/drivers/gpu/drm/i915/intel_sdvo.c index 4a735a5..198bb89 100644 --- a/drivers/gpu/drm/i915/intel_sdvo.c +++ b/drivers/gpu/drm/i915/intel_sdvo.c @@ -1274,6 +1274,8 @@ static void intel_sdvo_dpms(struct drm_connector *connector, int mode) intel_sdvo_set_encoder_power_state(intel_sdvo, mode); intel_sdvo_set_active_outputs(intel_sdvo, intel_sdvo->attached_output); } + + intel_connector_check_state(to_intel_connector(connector)); } static int intel_sdvo_mode_valid(struct drm_connector *connector, -- cgit v0.10.2 From 84bb65bded92028a6c803fef332586fedbe092b2 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Tue, 3 Jul 2012 13:19:00 +0200 Subject: drm/i915: rip out intel_crtc->dpms_mode Afaict this has been used for two things: - To prevent the crtc enable code from being run twice. We have now intel_crtc->active to track this in a more precise way. - To ensure the code copes correctly with the unknown hw state after boot and resume. Thanks to the hw state readout and sanitize code we have now a better way to handle this. The only thing it still does is complicate our modeset state space. Having outlived its usefullness, let it just die. Reviewed-by: Jesse Barnes Signed-Off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 15e4a65..ad03db4 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -3470,18 +3470,10 @@ void intel_crtc_update_dpms(struct drm_crtc *crtc) struct intel_encoder *intel_encoder; int pipe = intel_crtc->pipe; bool enabled, enable = false; - int mode; for_each_encoder_on_crtc(dev, crtc, intel_encoder) enable |= intel_encoder->connectors_active; - mode = enable ? DRM_MODE_DPMS_ON : DRM_MODE_DPMS_OFF; - - if (intel_crtc->dpms_mode == mode) - return; - - intel_crtc->dpms_mode = mode; - if (enable) dev_priv->display.crtc_enable(crtc); else @@ -5037,11 +5029,6 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc, x, y, old_fb); drm_vblank_post_modeset(dev, pipe); - if (ret) - intel_crtc->dpms_mode = DRM_MODE_DPMS_OFF; - else - intel_crtc->dpms_mode = DRM_MODE_DPMS_ON; - return ret; } @@ -7621,10 +7608,6 @@ static void intel_sanitize_crtc(struct intel_crtc *crtc) struct drm_i915_private *dev_priv = dev->dev_private; u32 reg, val; - /* Clear the dpms state for compatibility with code still using that - * deprecated state variable. */ - crtc->dpms_mode = -1; - /* Clear any frame start delays used for debugging left by the BIOS */ reg = PIPECONF(crtc->pipe); I915_WRITE(reg, I915_READ(reg) & ~PIPECONF_FRAME_START_DELAY_MASK); diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index e2116d9..a7d79de 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -164,7 +164,6 @@ struct intel_crtc { enum pipe pipe; enum plane plane; u8 lut_r[256], lut_g[256], lut_b[256]; - int dpms_mode; /* * Whether the crtc and the connected output pipeline is active. Implies * that crtc->enabled is set, i.e. the current mode configuration has -- cgit v0.10.2 From 24e804ba9731bf4b05bc17a6702d802e76b3bbc4 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Thu, 26 Jul 2012 19:25:46 +0200 Subject: drm/i915: rip out intel_dp->dpms_mode We now track the connector state in encoder->connectors_active, and because the DP output can't be cloned, that is sufficient to track the link state. Hence use this instead of adding yet another modeset state variable with dubious semantics at driver load and resume time. Also, connectors_active should only ever be set when the encoder is linked to a crtc, hence convert that crtc test into a WARN. v2: Rebase on top of struct intel_dp moving. v3: The rebase accidentally killed the newly-introduced intel_dp->port Noticed by Paulo Zanoni. Reviewed-by: Jesse Barnes Signed-Off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index 96fd1e7..b07d4b4 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -1309,8 +1309,6 @@ static void intel_disable_dp(struct intel_encoder *encoder) intel_dp_sink_dpms(intel_dp, DRM_MODE_DPMS_ON); ironlake_edp_panel_off(intel_dp); intel_dp_link_down(intel_dp); - - intel_dp->dpms_mode = DRM_MODE_DPMS_OFF; } static void intel_enable_dp(struct intel_encoder *encoder) @@ -1330,8 +1328,6 @@ static void intel_enable_dp(struct intel_encoder *encoder) } else ironlake_edp_panel_vdd_off(intel_dp, false); ironlake_edp_backlight_on(intel_dp); - - intel_dp->dpms_mode = DRM_MODE_DPMS_ON; } static void @@ -1356,7 +1352,6 @@ intel_dp_dpms(struct drm_connector *connector, int mode) if (mode != DRM_MODE_DPMS_ON) { intel_encoder_dpms(&intel_dp->base, mode); - WARN_ON(intel_dp->dpms_mode != DRM_MODE_DPMS_OFF); if (is_cpu_edp(intel_dp)) ironlake_edp_pll_off(&intel_dp->base.base); @@ -1365,7 +1360,6 @@ intel_dp_dpms(struct drm_connector *connector, int mode) ironlake_edp_pll_on(&intel_dp->base.base); intel_encoder_dpms(&intel_dp->base, mode); - WARN_ON(intel_dp->dpms_mode != DRM_MODE_DPMS_ON); } intel_connector_check_state(to_intel_connector(connector)); @@ -2069,10 +2063,10 @@ intel_dp_check_link_status(struct intel_dp *intel_dp) u8 sink_irq_vector; u8 link_status[DP_LINK_STATUS_SIZE]; - if (intel_dp->dpms_mode != DRM_MODE_DPMS_ON) + if (!intel_dp->base.connectors_active) return; - if (!intel_dp->base.base.crtc) + if (WARN_ON(!intel_dp->base.base.crtc)) return; /* Try to read receiver status if the link appears to be up */ @@ -2490,7 +2484,6 @@ intel_dp_init(struct drm_device *dev, int output_reg, enum port port) intel_dp->output_reg = output_reg; intel_dp->port = port; - intel_dp->dpms_mode = -1; intel_connector = kzalloc(sizeof(struct intel_connector), GFP_KERNEL); if (!intel_connector) { diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index a7d79de..c080c829 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -324,7 +324,6 @@ struct intel_dp { enum hdmi_force_audio force_audio; enum port port; uint32_t color_range; - int dpms_mode; uint8_t link_bw; uint8_t lane_count; uint8_t dpcd[DP_RECEIVER_CAP_SIZE]; -- cgit v0.10.2 From 7fad798e16fecddd41c6a91728a09f0b9507e40c Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Wed, 4 Jul 2012 17:51:47 +0200 Subject: drm/i915: ensure the force pipe A quirk is actually followed Many BIOSen forget to turn on the pipe A after resume (because they actually don't turn on anything), so we have to do that ourselves when sanitizing the hw state. I've discovered this due to the recent addition of a pipe WARN that takes the force quirk into account. v2: Actually try to enable the pipe with a proper configuration instead of simpyl switching it on with whatever random state the bios left it in after resume. v3: Fixup rebase conflict - the load_detect functions have lost their encoder argument. Reviewed-by: Jesse Barnes Signed-Off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index ad03db4..090ba2d 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -7602,6 +7602,33 @@ intel_connector_break_all_links(struct intel_connector *connector) connector->encoder->base.crtc = NULL; } +static void intel_enable_pipe_a(struct drm_device *dev) +{ + struct intel_connector *connector; + struct drm_connector *crt = NULL; + struct intel_load_detect_pipe load_detect_temp; + + /* We can't just switch on the pipe A, we need to set things up with a + * proper mode and output configuration. As a gross hack, enable pipe A + * by enabling the load detect pipe once. */ + list_for_each_entry(connector, + &dev->mode_config.connector_list, + base.head) { + if (connector->encoder->type == INTEL_OUTPUT_ANALOG) { + crt = &connector->base; + break; + } + } + + if (!crt) + return; + + if (intel_get_load_detect_pipe(crt, NULL, &load_detect_temp)) + intel_release_load_detect_pipe(crt, &load_detect_temp); + + +} + static void intel_sanitize_crtc(struct intel_crtc *crtc) { struct drm_device *dev = crtc->base.dev; @@ -7650,6 +7677,15 @@ static void intel_sanitize_crtc(struct intel_crtc *crtc) } ok: + if (dev_priv->quirks & QUIRK_PIPEA_FORCE && + crtc->pipe == PIPE_A && !crtc->active) { + /* BIOS forgot to enable pipe A, this mostly happens after + * resume. Force-enable the pipe to fix this, the update_dpms + * call below we restore the pipe to the right state, but leave + * the required bits on. */ + intel_enable_pipe_a(dev); + } + /* Adjust the state of the output pipe according to whether we * have active connectors/encoders. */ intel_crtc_update_dpms(&crtc->base); -- cgit v0.10.2 From d9e55608cdcd875c4c1f319c1a5a438c7cc55f57 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Wed, 4 Jul 2012 22:16:09 +0200 Subject: drm/i915: introduce struct intel_set_config intel_crtc_set_config is an unwidly beast and is in serious need of some function extraction. To facilitate that, introduce a struct to keep track of all the state involved. Atm it doesn't do much more than keep track of all the allocated memory. v2: Apply some bikeshed to intel_set_config_free, as suggested by Jesse Barnes. Reviewed-by: Jesse Barnes Signed-Off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 090ba2d..f77140d 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -6723,17 +6723,29 @@ done: return ret; } +static void intel_set_config_free(struct intel_set_config *config) +{ + if (!config) + return; + + kfree(config->save_connectors); + kfree(config->save_encoders); + kfree(config->save_crtcs); + kfree(config); +} + static int intel_crtc_set_config(struct drm_mode_set *set) { struct drm_device *dev; - struct drm_crtc *save_crtcs, *new_crtc, *crtc; - struct drm_encoder *save_encoders, *new_encoder, *encoder; + struct drm_crtc *new_crtc, *crtc; + struct drm_encoder *new_encoder, *encoder; struct drm_framebuffer *old_fb = NULL; bool mode_changed = false; /* if true do a full mode set */ bool fb_changed = false; /* if true and !mode_changed just do a flip */ - struct drm_connector *save_connectors, *connector; + struct drm_connector *connector; int count = 0, ro; struct drm_mode_set save_set; + struct intel_set_config *config; int ret; int i; @@ -6762,27 +6774,27 @@ static int intel_crtc_set_config(struct drm_mode_set *set) dev = set->crtc->dev; + ret = -ENOMEM; + config = kzalloc(sizeof(*config), GFP_KERNEL); + if (!config) + goto out_config; + /* Allocate space for the backup of all (non-pointer) crtc, encoder and * connector data. */ - save_crtcs = kzalloc(dev->mode_config.num_crtc * - sizeof(struct drm_crtc), GFP_KERNEL); - if (!save_crtcs) - return -ENOMEM; + config->save_crtcs = kzalloc(dev->mode_config.num_crtc * + sizeof(struct drm_crtc), GFP_KERNEL); + if (!config->save_crtcs) + goto out_config; - save_encoders = kzalloc(dev->mode_config.num_encoder * - sizeof(struct drm_encoder), GFP_KERNEL); - if (!save_encoders) { - kfree(save_crtcs); - return -ENOMEM; - } + config->save_encoders = kzalloc(dev->mode_config.num_encoder * + sizeof(struct drm_encoder), GFP_KERNEL); + if (!config->save_encoders) + goto out_config; - save_connectors = kzalloc(dev->mode_config.num_connector * - sizeof(struct drm_connector), GFP_KERNEL); - if (!save_connectors) { - kfree(save_crtcs); - kfree(save_encoders); - return -ENOMEM; - } + config->save_connectors = kzalloc(dev->mode_config.num_connector * + sizeof(struct drm_connector), GFP_KERNEL); + if (!config->save_connectors) + goto out_config; /* Copy data. Note that driver private data is not affected. * Should anything bad happen only the expected state is @@ -6790,17 +6802,17 @@ static int intel_crtc_set_config(struct drm_mode_set *set) */ count = 0; list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { - save_crtcs[count++] = *crtc; + config->save_crtcs[count++] = *crtc; } count = 0; list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { - save_encoders[count++] = *encoder; + config->save_encoders[count++] = *encoder; } count = 0; list_for_each_entry(connector, &dev->mode_config.connector_list, head) { - save_connectors[count++] = *connector; + config->save_connectors[count++] = *connector; } save_set.crtc = set->crtc; @@ -6936,26 +6948,25 @@ static int intel_crtc_set_config(struct drm_mode_set *set) } } - kfree(save_connectors); - kfree(save_encoders); - kfree(save_crtcs); + intel_set_config_free(config); + return 0; fail: /* Restore all previous data. */ count = 0; list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { - *crtc = save_crtcs[count++]; + *crtc = config->save_crtcs[count++]; } count = 0; list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { - *encoder = save_encoders[count++]; + *encoder = config->save_encoders[count++]; } count = 0; list_for_each_entry(connector, &dev->mode_config.connector_list, head) { - *connector = save_connectors[count++]; + *connector = config->save_connectors[count++]; } /* Try to restore the config */ @@ -6964,9 +6975,8 @@ fail: save_set.x, save_set.y, save_set.fb)) DRM_ERROR("failed to restore config after modeset failure\n"); - kfree(save_connectors); - kfree(save_encoders); - kfree(save_crtcs); +out_config: + intel_set_config_free(config); return ret; } diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index c080c829..1cb64dd 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -423,6 +423,12 @@ extern void intel_panel_disable_backlight(struct drm_device *dev); extern void intel_panel_destroy_backlight(struct drm_device *dev); extern enum drm_connector_status intel_panel_detect(struct drm_device *dev); +struct intel_set_config { + struct drm_connector *save_connectors; + struct drm_encoder *save_encoders; + struct drm_crtc *save_crtcs; +}; + extern bool intel_set_mode(struct drm_crtc *crtc, struct drm_display_mode *mode, int x, int y, struct drm_framebuffer *old_fb); extern void intel_crtc_load_lut(struct drm_crtc *crtc); -- cgit v0.10.2 From 85f9eb71fec3322dc3670207ca5c1706467e701c Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Wed, 4 Jul 2012 22:24:08 +0200 Subject: drm/i915: extract modeset config save/restore code At the end this won't be of much use to us, but meanwhile just extract it to get a better overview of what exactly set_config does. Reviewed-by: Jesse Barnes Signed-Off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index f77140d..b60745d 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -6734,11 +6734,82 @@ static void intel_set_config_free(struct intel_set_config *config) kfree(config); } +static int intel_set_config_save_state(struct drm_device *dev, + struct intel_set_config *config) +{ + struct drm_crtc *crtc; + struct drm_encoder *encoder; + struct drm_connector *connector; + int count; + + /* Allocate space for the backup of all (non-pointer) crtc, encoder and + * connector data. */ + config->save_crtcs = kzalloc(dev->mode_config.num_crtc * + sizeof(struct drm_crtc), GFP_KERNEL); + if (!config->save_crtcs) + return -ENOMEM; + + config->save_encoders = kzalloc(dev->mode_config.num_encoder * + sizeof(struct drm_encoder), GFP_KERNEL); + if (!config->save_encoders) + return -ENOMEM; + + config->save_connectors = kzalloc(dev->mode_config.num_connector * + sizeof(struct drm_connector), GFP_KERNEL); + if (!config->save_connectors) + return -ENOMEM; + + /* Copy data. Note that driver private data is not affected. + * Should anything bad happen only the expected state is + * restored, not the drivers personal bookkeeping. + */ + count = 0; + list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { + config->save_crtcs[count++] = *crtc; + } + + count = 0; + list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { + config->save_encoders[count++] = *encoder; + } + + count = 0; + list_for_each_entry(connector, &dev->mode_config.connector_list, head) { + config->save_connectors[count++] = *connector; + } + + return 0; +} + +static void intel_set_config_restore_state(struct drm_device *dev, + struct intel_set_config *config) +{ + struct drm_crtc *crtc; + struct drm_encoder *encoder; + struct drm_connector *connector; + int count; + + count = 0; + list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { + *crtc = config->save_crtcs[count++]; + } + + count = 0; + list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { + *encoder = config->save_encoders[count++]; + } + + count = 0; + list_for_each_entry(connector, &dev->mode_config.connector_list, head) { + *connector = config->save_connectors[count++]; + } +} + static int intel_crtc_set_config(struct drm_mode_set *set) { struct drm_device *dev; - struct drm_crtc *new_crtc, *crtc; - struct drm_encoder *new_encoder, *encoder; + struct drm_crtc *new_crtc; + struct drm_encoder *new_encoder; struct drm_framebuffer *old_fb = NULL; bool mode_changed = false; /* if true do a full mode set */ bool fb_changed = false; /* if true and !mode_changed just do a flip */ @@ -6779,42 +6850,10 @@ static int intel_crtc_set_config(struct drm_mode_set *set) if (!config) goto out_config; - /* Allocate space for the backup of all (non-pointer) crtc, encoder and - * connector data. */ - config->save_crtcs = kzalloc(dev->mode_config.num_crtc * - sizeof(struct drm_crtc), GFP_KERNEL); - if (!config->save_crtcs) - goto out_config; - - config->save_encoders = kzalloc(dev->mode_config.num_encoder * - sizeof(struct drm_encoder), GFP_KERNEL); - if (!config->save_encoders) - goto out_config; - - config->save_connectors = kzalloc(dev->mode_config.num_connector * - sizeof(struct drm_connector), GFP_KERNEL); - if (!config->save_connectors) + ret = intel_set_config_save_state(dev, config); + if (ret) goto out_config; - /* Copy data. Note that driver private data is not affected. - * Should anything bad happen only the expected state is - * restored, not the drivers personal bookkeeping. - */ - count = 0; - list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { - config->save_crtcs[count++] = *crtc; - } - - count = 0; - list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { - config->save_encoders[count++] = *encoder; - } - - count = 0; - list_for_each_entry(connector, &dev->mode_config.connector_list, head) { - config->save_connectors[count++] = *connector; - } - save_set.crtc = set->crtc; save_set.mode = &set->crtc->mode; save_set.x = set->crtc->x; @@ -6953,21 +6992,7 @@ static int intel_crtc_set_config(struct drm_mode_set *set) return 0; fail: - /* Restore all previous data. */ - count = 0; - list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { - *crtc = config->save_crtcs[count++]; - } - - count = 0; - list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { - *encoder = config->save_encoders[count++]; - } - - count = 0; - list_for_each_entry(connector, &dev->mode_config.connector_list, head) { - *connector = config->save_connectors[count++]; - } + intel_set_config_restore_state(dev, config); /* Try to restore the config */ if (mode_changed && -- cgit v0.10.2 From 5e2b584ed1563c4c5199510710df6eaa1a36f3b1 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Wed, 4 Jul 2012 22:41:29 +0200 Subject: drm/i915: extract intel_set_config_compute_mode_changes This computes what exactly changed in the modeset configuration, i.e. whether a full modeset is required or only an update of the framebuffer base address or no change at all. In the future we might add more checks for e.g. when only the output mode changed, so that we could do a minimal modeset for outputs that support this. Like the lvds/eDP panels where we only need to update the panel fitter. Reviewed-by: Jesse Barnes Signed-Off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index b60745d..4ec0ccb 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -6805,14 +6805,46 @@ static void intel_set_config_restore_state(struct drm_device *dev, } } +static void +intel_set_config_compute_mode_changes(struct drm_mode_set *set, + struct intel_set_config *config) +{ + + /* We should be able to check here if the fb has the same properties + * and then just flip_or_move it */ + if (set->crtc->fb != set->fb) { + /* If we have no fb then treat it as a full mode set */ + if (set->crtc->fb == NULL) { + DRM_DEBUG_KMS("crtc has no fb, full mode set\n"); + config->mode_changed = true; + } else if (set->fb == NULL) { + config->mode_changed = true; + } else if (set->fb->depth != set->crtc->fb->depth) { + config->mode_changed = true; + } else if (set->fb->bits_per_pixel != + set->crtc->fb->bits_per_pixel) { + config->mode_changed = true; + } else + config->fb_changed = true; + } + + if (set->x != set->crtc->x || set->y != set->crtc->y) + config->fb_changed = true; + + if (set->mode && !drm_mode_equal(set->mode, &set->crtc->mode)) { + DRM_DEBUG_KMS("modes are different, full mode set\n"); + drm_mode_debug_printmodeline(&set->crtc->mode); + drm_mode_debug_printmodeline(set->mode); + config->mode_changed = true; + } +} + static int intel_crtc_set_config(struct drm_mode_set *set) { struct drm_device *dev; struct drm_crtc *new_crtc; struct drm_encoder *new_encoder; struct drm_framebuffer *old_fb = NULL; - bool mode_changed = false; /* if true do a full mode set */ - bool fb_changed = false; /* if true and !mode_changed just do a flip */ struct drm_connector *connector; int count = 0, ro; struct drm_mode_set save_set; @@ -6860,33 +6892,11 @@ static int intel_crtc_set_config(struct drm_mode_set *set) save_set.y = set->crtc->y; save_set.fb = set->crtc->fb; - /* We should be able to check here if the fb has the same properties - * and then just flip_or_move it */ - if (set->crtc->fb != set->fb) { - /* If we have no fb then treat it as a full mode set */ - if (set->crtc->fb == NULL) { - DRM_DEBUG_KMS("crtc has no fb, full mode set\n"); - mode_changed = true; - } else if (set->fb == NULL) { - mode_changed = true; - } else if (set->fb->depth != set->crtc->fb->depth) { - mode_changed = true; - } else if (set->fb->bits_per_pixel != - set->crtc->fb->bits_per_pixel) { - mode_changed = true; - } else - fb_changed = true; - } - - if (set->x != set->crtc->x || set->y != set->crtc->y) - fb_changed = true; - - if (set->mode && !drm_mode_equal(set->mode, &set->crtc->mode)) { - DRM_DEBUG_KMS("modes are different, full mode set\n"); - drm_mode_debug_printmodeline(&set->crtc->mode); - drm_mode_debug_printmodeline(set->mode); - mode_changed = true; - } + /* Compute whether we need a full modeset, only an fb base update or no + * change at all. In the future we might also check whether only the + * mode changed, e.g. for LVDS where we only change the panel fitter in + * such cases. */ + intel_set_config_compute_mode_changes(set, config); /* a) traverse passed in connector list and get encoders for them */ count = 0; @@ -6902,7 +6912,7 @@ static int intel_crtc_set_config(struct drm_mode_set *set) if (new_encoder != connector->encoder) { DRM_DEBUG_KMS("encoder changed, full mode switch\n"); - mode_changed = true; + config->mode_changed = true; /* If the encoder is reused for another connector, then * the appropriate crtc will be set later. */ @@ -6930,12 +6940,11 @@ static int intel_crtc_set_config(struct drm_mode_set *set) /* Make sure the new CRTC will work with the encoder */ if (new_crtc && !intel_encoder_crtc_ok(connector->encoder, new_crtc)) { - ret = -EINVAL; - goto fail; + return -EINVAL; } if (new_crtc != connector->encoder->crtc) { DRM_DEBUG_KMS("crtc changed, full mode switch\n"); - mode_changed = true; + config->mode_changed = true; connector->encoder->crtc = new_crtc; } if (new_crtc) { @@ -6948,7 +6957,7 @@ static int intel_crtc_set_config(struct drm_mode_set *set) } } - if (mode_changed) { + if (config->mode_changed) { set->crtc->enabled = drm_helper_crtc_in_use(set->crtc); if (set->crtc->enabled) { DRM_DEBUG_KMS("attempting to set mode from" @@ -6972,7 +6981,7 @@ static int intel_crtc_set_config(struct drm_mode_set *set) } } drm_helper_disable_unused_functions(dev); - } else if (fb_changed) { + } else if (config->fb_changed) { set->crtc->x = set->x; set->crtc->y = set->y; @@ -6995,7 +7004,7 @@ fail: intel_set_config_restore_state(dev, config); /* Try to restore the config */ - if (mode_changed && + if (config->mode_changed && !intel_set_mode(save_set.crtc, save_set.mode, save_set.x, save_set.y, save_set.fb)) DRM_ERROR("failed to restore config after modeset failure\n"); diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index 1cb64dd..fad11c8 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -427,6 +427,9 @@ struct intel_set_config { struct drm_connector *save_connectors; struct drm_encoder *save_encoders; struct drm_crtc *save_crtcs; + + bool fb_changed; + bool mode_changed; }; extern bool intel_set_mode(struct drm_crtc *crtc, struct drm_display_mode *mode, -- cgit v0.10.2 From 2e43105183bcecfbe56733785c05bf182df60082 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Wed, 4 Jul 2012 22:42:15 +0200 Subject: drm/i915: extract intel_set_config_update_output_state Note that this function already clobbers the mode config state, so we have to clean things up if something fails. Reviewed-by: Jesse Barnes Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 4ec0ccb..c10fb2e 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -6839,64 +6839,15 @@ intel_set_config_compute_mode_changes(struct drm_mode_set *set, } } -static int intel_crtc_set_config(struct drm_mode_set *set) +static int +intel_set_config_update_output_state(struct drm_device *dev, + struct drm_mode_set *set, + struct intel_set_config *config) { - struct drm_device *dev; struct drm_crtc *new_crtc; struct drm_encoder *new_encoder; - struct drm_framebuffer *old_fb = NULL; struct drm_connector *connector; - int count = 0, ro; - struct drm_mode_set save_set; - struct intel_set_config *config; - int ret; - int i; - - DRM_DEBUG_KMS("\n"); - - if (!set) - return -EINVAL; - - if (!set->crtc) - return -EINVAL; - - if (!set->crtc->helper_private) - return -EINVAL; - - if (!set->mode) - set->fb = NULL; - - if (set->fb) { - DRM_DEBUG_KMS("[CRTC:%d] [FB:%d] #connectors=%d (x y) (%i %i)\n", - set->crtc->base.id, set->fb->base.id, - (int)set->num_connectors, set->x, set->y); - } else { - DRM_DEBUG_KMS("[CRTC:%d] [NOFB]\n", set->crtc->base.id); - return intel_crtc_helper_disable(set->crtc); - } - - dev = set->crtc->dev; - - ret = -ENOMEM; - config = kzalloc(sizeof(*config), GFP_KERNEL); - if (!config) - goto out_config; - - ret = intel_set_config_save_state(dev, config); - if (ret) - goto out_config; - - save_set.crtc = set->crtc; - save_set.mode = &set->crtc->mode; - save_set.x = set->crtc->x; - save_set.y = set->crtc->y; - save_set.fb = set->crtc->fb; - - /* Compute whether we need a full modeset, only an fb base update or no - * change at all. In the future we might also check whether only the - * mode changed, e.g. for LVDS where we only change the panel fitter in - * such cases. */ - intel_set_config_compute_mode_changes(set, config); + int count, ro; /* a) traverse passed in connector list and get encoders for them */ count = 0; @@ -6957,6 +6908,68 @@ static int intel_crtc_set_config(struct drm_mode_set *set) } } + return 0; +} + +static int intel_crtc_set_config(struct drm_mode_set *set) +{ + struct drm_device *dev; + struct drm_framebuffer *old_fb = NULL; + struct drm_mode_set save_set; + struct intel_set_config *config; + int ret; + int i; + + DRM_DEBUG_KMS("\n"); + + if (!set) + return -EINVAL; + + if (!set->crtc) + return -EINVAL; + + if (!set->crtc->helper_private) + return -EINVAL; + + if (!set->mode) + set->fb = NULL; + + if (set->fb) { + DRM_DEBUG_KMS("[CRTC:%d] [FB:%d] #connectors=%d (x y) (%i %i)\n", + set->crtc->base.id, set->fb->base.id, + (int)set->num_connectors, set->x, set->y); + } else { + DRM_DEBUG_KMS("[CRTC:%d] [NOFB]\n", set->crtc->base.id); + return intel_crtc_helper_disable(set->crtc); + } + + dev = set->crtc->dev; + + ret = -ENOMEM; + config = kzalloc(sizeof(*config), GFP_KERNEL); + if (!config) + goto out_config; + + ret = intel_set_config_save_state(dev, config); + if (ret) + goto out_config; + + save_set.crtc = set->crtc; + save_set.mode = &set->crtc->mode; + save_set.x = set->crtc->x; + save_set.y = set->crtc->y; + save_set.fb = set->crtc->fb; + + /* Compute whether we need a full modeset, only an fb base update or no + * change at all. In the future we might also check whether only the + * mode changed, e.g. for LVDS where we only change the panel fitter in + * such cases. */ + intel_set_config_compute_mode_changes(set, config); + + ret = intel_set_config_update_output_state(dev, set, config); + if (ret) + goto fail; + if (config->mode_changed) { set->crtc->enabled = drm_helper_crtc_in_use(set->crtc); if (set->crtc->enabled) { -- cgit v0.10.2 From 431e50f799b356a899a63924883f2b0d1bfaf7b3 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Tue, 10 Jul 2012 17:53:42 +0200 Subject: drm/i915: implement crtc helper semantics relied upon by the fb helper Yikes! But yeah, we have to do this until someone volunteers to clean up the fb helper and rid it of its incetious relationship with the crtc helper code. Reviewed-by: Jesse Barnes Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index c10fb2e..71cb90d 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -6934,6 +6934,12 @@ static int intel_crtc_set_config(struct drm_mode_set *set) if (!set->mode) set->fb = NULL; + /* The fb helper likes to play gross jokes with ->mode_set_config. + * Unfortunately the crtc helper doesn't do much at all for this case, + * so we have to cope with this madness until the fb helper is fixed up. */ + if (set->fb && set->num_connectors == 0) + return 0; + if (set->fb) { DRM_DEBUG_KMS("[CRTC:%d] [FB:%d] #connectors=%d (x y) (%i %i)\n", set->crtc->base.id, set->fb->base.id, -- cgit v0.10.2 From 835c5873d6e38cb317091ace7c1f3c40cc0607ce Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Tue, 10 Jul 2012 18:11:08 +0200 Subject: drm/i915: don't update the fb base if there is no fb Otherwise we'll set_fb complains pretty loudly if we the crtc is off and userspace moves the NULL fb around a bit. Yeah, this actually happens in the wild ... Reviewed-by: Jesse Barnes Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 71cb90d..01d871b 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -6828,7 +6828,7 @@ intel_set_config_compute_mode_changes(struct drm_mode_set *set, config->fb_changed = true; } - if (set->x != set->crtc->x || set->y != set->crtc->y) + if (set->fb && (set->x != set->crtc->x || set->y != set->crtc->y)) config->fb_changed = true; if (set->mode && !drm_mode_equal(set->mode, &set->crtc->mode)) { -- cgit v0.10.2 From 8d3e375e77f09174038b9bfb02b8423cbb187959 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Thu, 5 Jul 2012 16:09:09 +0200 Subject: drm/i915: convert pointless error checks in set_config to BUGs Because they all are, the ioctl command never calls us with any of these violated. Also drop a equally pointless empty debug message (and also in set_cursor, while we're at it). With all these changes, intel_crtc_set_config is neatly condensed down to it's essence, the actual modeset code (or fb update calling code) v2: The fb helper code is actually stretching ->set_config semantics a bit, it calls it with set->mode == NULL but set->fb != NULL. Reviewed-by: Jesse Barnes Signed-Off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 01d871b..9485c5a 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -5462,8 +5462,6 @@ static int intel_crtc_cursor_set(struct drm_crtc *crtc, uint32_t addr; int ret; - DRM_DEBUG_KMS("\n"); - /* if we want to turn off the cursor ignore width and height */ if (!handle) { DRM_DEBUG_KMS("cursor off\n"); @@ -6920,16 +6918,9 @@ static int intel_crtc_set_config(struct drm_mode_set *set) int ret; int i; - DRM_DEBUG_KMS("\n"); - - if (!set) - return -EINVAL; - - if (!set->crtc) - return -EINVAL; - - if (!set->crtc->helper_private) - return -EINVAL; + BUG_ON(!set); + BUG_ON(!set->crtc); + BUG_ON(!set->crtc->helper_private); if (!set->mode) set->fb = NULL; -- cgit v0.10.2 From 1aa4b628ee63f55db96c7e820257b6e4948abb74 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Thu, 5 Jul 2012 16:20:48 +0200 Subject: drm/i915: don't save all the encoder/crtc state in set_config We actually only touch the connector -> encoder and encoder -> crtc linking. So it's enough to just save/restore that. While at it, also switch to kcalloc to allocate these arrays (omission in the commit message spotted by Jesse Barnes). Reviewed-by: Jesse Barnes Signed-Off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 9485c5a..a2ce117 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -6726,8 +6726,8 @@ static void intel_set_config_free(struct intel_set_config *config) if (!config) return; - kfree(config->save_connectors); - kfree(config->save_encoders); + kfree(config->save_connector_encoders); + kfree(config->save_encoder_crtcs); kfree(config->save_crtcs); kfree(config); } @@ -6742,19 +6742,21 @@ static int intel_set_config_save_state(struct drm_device *dev, /* Allocate space for the backup of all (non-pointer) crtc, encoder and * connector data. */ - config->save_crtcs = kzalloc(dev->mode_config.num_crtc * + config->save_crtcs = kcalloc(dev->mode_config.num_crtc, sizeof(struct drm_crtc), GFP_KERNEL); if (!config->save_crtcs) return -ENOMEM; - config->save_encoders = kzalloc(dev->mode_config.num_encoder * - sizeof(struct drm_encoder), GFP_KERNEL); - if (!config->save_encoders) + config->save_encoder_crtcs = + kcalloc(dev->mode_config.num_encoder, + sizeof(struct drm_crtc *), GFP_KERNEL); + if (!config->save_encoder_crtcs) return -ENOMEM; - config->save_connectors = kzalloc(dev->mode_config.num_connector * - sizeof(struct drm_connector), GFP_KERNEL); - if (!config->save_connectors) + config->save_connector_encoders = + kcalloc(dev->mode_config.num_connector, + sizeof(struct drm_encoder *), GFP_KERNEL); + if (!config->save_connector_encoders) return -ENOMEM; /* Copy data. Note that driver private data is not affected. @@ -6768,12 +6770,12 @@ static int intel_set_config_save_state(struct drm_device *dev, count = 0; list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { - config->save_encoders[count++] = *encoder; + config->save_encoder_crtcs[count++] = encoder->crtc; } count = 0; list_for_each_entry(connector, &dev->mode_config.connector_list, head) { - config->save_connectors[count++] = *connector; + config->save_connector_encoders[count++] = connector->encoder; } return 0; @@ -6794,12 +6796,12 @@ static void intel_set_config_restore_state(struct drm_device *dev, count = 0; list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { - *encoder = config->save_encoders[count++]; + encoder->crtc = config->save_encoder_crtcs[count++]; } count = 0; list_for_each_entry(connector, &dev->mode_config.connector_list, head) { - *connector = config->save_connectors[count++]; + connector->encoder = config->save_connector_encoders[count++]; } } diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index fad11c8..4946282 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -424,8 +424,8 @@ extern void intel_panel_destroy_backlight(struct drm_device *dev); extern enum drm_connector_status intel_panel_detect(struct drm_device *dev); struct intel_set_config { - struct drm_connector *save_connectors; - struct drm_encoder *save_encoders; + struct drm_encoder **save_connector_encoders; + struct drm_crtc **save_encoder_crtcs; struct drm_crtc *save_crtcs; bool fb_changed; -- cgit v0.10.2 From 9a935856992d68b9194f825ce6e115df7ecc5bca Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Thu, 5 Jul 2012 22:34:27 +0200 Subject: drm/i915: stage modeset output changes This is the core of the new modeset logic. The current code which is based upon the crtc helper code first updates all the link of the new display pipeline and then calls the lower-level set_mode function to execute the required callbacks to get there. The issue with this approach is that for disabling we need to know the _current_ display pipe state, not the new one. Hence we need to stage the new state of the display pipe and only update it once we have disabled the current configuration and before we start to update the hw registers with the new configuration. This patch here just prepares the ground by switching the new output state computation to these staging pointers. To make it clearer, rename the old update_output_state function to stage_output_state. A few peculiarities: - We're also calling the set_mode function at various places to update properties. Hence after a successfule modeset we need to stage the current configuration (for otherwise we might fall back again). This happens automatically because as part of the (successful) modeset we need to copy the staged state to the real one. But for the hw readout code we need to make sure that this happens, too. - Teach the new staged output state computation code the required smarts to handle the disabling of outputs. The current code handles this in a special case, but to better handle global modeset changes covering more than one crtc, we want to do this all in the same low-level modeset code. - The actual modeset code is still a bit ugly and wants to know the new crtc->enabled state a bit early. Follow-on patches will clean that up, for now we have to apply the staged output configuration early, outside of the set_mode functions. - Improve/add comments in stage_output_state. Essentially all that is left to do now is move the disabling code into set_mode and then move the staged state update code also into set_mode, at the right place between disabling things and calling the mode_set callbacks for the new configuration. v2: Disabling a crtc works by passing in a NULL mode or fb, userspace doesn't hand in the list of connectors. We therefore need to detect this case manually and tear down all the output links. v3: Properly update the output staging pointers after having read out the hw state. v4: Simplify the code, add more DRM_DEBUG_KMS output and check a few assumptions with WARN_ON. Essentially all things that I've noticed while debugging issues in other places of the code. v4: Correctly disable the old set of connectors when enabling an already enabled crtc on a new set of crtc. Reported by Paulo Zanoni. Reviewed-by: Jesse Barnes Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index a2ce117..5031e0c 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -6619,6 +6619,51 @@ intel_crtc_prepare_encoders(struct drm_device *dev) } } +/** + * intel_modeset_update_staged_output_state + * + * Updates the staged output configuration state, e.g. after we've read out the + * current hw state. + */ +static void intel_modeset_update_staged_output_state(struct drm_device *dev) +{ + struct intel_encoder *encoder; + struct intel_connector *connector; + + list_for_each_entry(connector, &dev->mode_config.connector_list, + base.head) { + connector->new_encoder = + to_intel_encoder(connector->base.encoder); + } + + list_for_each_entry(encoder, &dev->mode_config.encoder_list, + base.head) { + encoder->new_crtc = + to_intel_crtc(encoder->base.crtc); + } +} + +/** + * intel_modeset_commit_output_state + * + * This function copies the stage display pipe configuration to the real one. + */ +static void intel_modeset_commit_output_state(struct drm_device *dev) +{ + struct intel_encoder *encoder; + struct intel_connector *connector; + + list_for_each_entry(connector, &dev->mode_config.connector_list, + base.head) { + connector->base.encoder = &connector->new_encoder->base; + } + + list_for_each_entry(encoder, &dev->mode_config.encoder_list, + base.head) { + encoder->base.crtc = &encoder->new_crtc->base; + } +} + bool intel_set_mode(struct drm_crtc *crtc, struct drm_display_mode *mode, int x, int y, struct drm_framebuffer *old_fb) @@ -6785,8 +6830,8 @@ static void intel_set_config_restore_state(struct drm_device *dev, struct intel_set_config *config) { struct drm_crtc *crtc; - struct drm_encoder *encoder; - struct drm_connector *connector; + struct intel_encoder *encoder; + struct intel_connector *connector; int count; count = 0; @@ -6795,13 +6840,15 @@ static void intel_set_config_restore_state(struct drm_device *dev, } count = 0; - list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { - encoder->crtc = config->save_encoder_crtcs[count++]; + list_for_each_entry(encoder, &dev->mode_config.encoder_list, base.head) { + encoder->new_crtc = + to_intel_crtc(config->save_encoder_crtcs[count++]); } count = 0; - list_for_each_entry(connector, &dev->mode_config.connector_list, head) { - connector->encoder = config->save_connector_encoders[count++]; + list_for_each_entry(connector, &dev->mode_config.connector_list, base.head) { + connector->new_encoder = + to_intel_encoder(config->save_connector_encoders[count++]); } } @@ -6840,73 +6887,106 @@ intel_set_config_compute_mode_changes(struct drm_mode_set *set, } static int -intel_set_config_update_output_state(struct drm_device *dev, - struct drm_mode_set *set, - struct intel_set_config *config) +intel_modeset_stage_output_state(struct drm_device *dev, + struct drm_mode_set *set, + struct intel_set_config *config) { struct drm_crtc *new_crtc; - struct drm_encoder *new_encoder; - struct drm_connector *connector; + struct intel_connector *connector; + struct intel_encoder *encoder; int count, ro; - /* a) traverse passed in connector list and get encoders for them */ + /* The upper layers ensure that we either disabl a crtc or have a list + * of connectors. For paranoia, double-check this. */ + WARN_ON(!set->fb && (set->num_connectors != 0)); + WARN_ON(set->fb && (set->num_connectors == 0)); + count = 0; - list_for_each_entry(connector, &dev->mode_config.connector_list, head) { - new_encoder = connector->encoder; + list_for_each_entry(connector, &dev->mode_config.connector_list, + base.head) { + /* Otherwise traverse passed in connector list and get encoders + * for them. */ for (ro = 0; ro < set->num_connectors; ro++) { - if (set->connectors[ro] == connector) { - new_encoder = - &intel_attached_encoder(connector)->base; + if (set->connectors[ro] == &connector->base) { + connector->new_encoder = connector->encoder; break; } } - if (new_encoder != connector->encoder) { + /* If we disable the crtc, disable all its connectors. Also, if + * the connector is on the changing crtc but not on the new + * connector list, disable it. */ + if ((!set->fb || ro == set->num_connectors) && + connector->base.encoder && + connector->base.encoder->crtc == set->crtc) { + connector->new_encoder = NULL; + + DRM_DEBUG_KMS("[CONNECTOR:%d:%s] to [NOCRTC]\n", + connector->base.base.id, + drm_get_connector_name(&connector->base)); + } + + + if (&connector->new_encoder->base != connector->base.encoder) { DRM_DEBUG_KMS("encoder changed, full mode switch\n"); config->mode_changed = true; - /* If the encoder is reused for another connector, then - * the appropriate crtc will be set later. - */ - if (connector->encoder) - connector->encoder->crtc = NULL; - connector->encoder = new_encoder; } + + /* Disable all disconnected encoders. */ + if (connector->base.status == connector_status_disconnected) + connector->new_encoder = NULL; } + /* connector->new_encoder is now updated for all connectors. */ + /* Update crtc of enabled connectors. */ count = 0; - list_for_each_entry(connector, &dev->mode_config.connector_list, head) { - if (!connector->encoder) + list_for_each_entry(connector, &dev->mode_config.connector_list, + base.head) { + if (!connector->new_encoder) continue; - if (connector->encoder->crtc == set->crtc) - new_crtc = NULL; - else - new_crtc = connector->encoder->crtc; + new_crtc = connector->new_encoder->base.crtc; for (ro = 0; ro < set->num_connectors; ro++) { - if (set->connectors[ro] == connector) + if (set->connectors[ro] == &connector->base) new_crtc = set->crtc; } /* Make sure the new CRTC will work with the encoder */ - if (new_crtc && - !intel_encoder_crtc_ok(connector->encoder, new_crtc)) { + if (!intel_encoder_crtc_ok(&connector->new_encoder->base, + new_crtc)) { return -EINVAL; } - if (new_crtc != connector->encoder->crtc) { + connector->encoder->new_crtc = to_intel_crtc(new_crtc); + + DRM_DEBUG_KMS("[CONNECTOR:%d:%s] to [CRTC:%d]\n", + connector->base.base.id, + drm_get_connector_name(&connector->base), + new_crtc->base.id); + } + + /* Check for any encoders that needs to be disabled. */ + list_for_each_entry(encoder, &dev->mode_config.encoder_list, + base.head) { + list_for_each_entry(connector, + &dev->mode_config.connector_list, + base.head) { + if (connector->new_encoder == encoder) { + WARN_ON(!connector->new_encoder->new_crtc); + + goto next_encoder; + } + } + encoder->new_crtc = NULL; +next_encoder: + /* Only now check for crtc changes so we don't miss encoders + * that will be disabled. */ + if (&encoder->new_crtc->base != encoder->base.crtc) { DRM_DEBUG_KMS("crtc changed, full mode switch\n"); config->mode_changed = true; - connector->encoder->crtc = new_crtc; - } - if (new_crtc) { - DRM_DEBUG_KMS("[CONNECTOR:%d:%s] to [CRTC:%d]\n", - connector->base.id, drm_get_connector_name(connector), - new_crtc->base.id); - } else { - DRM_DEBUG_KMS("[CONNECTOR:%d:%s] to [NOCRTC]\n", - connector->base.id, drm_get_connector_name(connector)); } } + /* Now we've also updated encoder->new_crtc for all encoders. */ return 0; } @@ -6965,11 +7045,13 @@ static int intel_crtc_set_config(struct drm_mode_set *set) * such cases. */ intel_set_config_compute_mode_changes(set, config); - ret = intel_set_config_update_output_state(dev, set, config); + ret = intel_modeset_stage_output_state(dev, set, config); if (ret) goto fail; if (config->mode_changed) { + intel_modeset_commit_output_state(dev); + set->crtc->enabled = drm_helper_crtc_in_use(set->crtc); if (set->crtc->enabled) { DRM_DEBUG_KMS("attempting to set mode from" @@ -7015,6 +7097,8 @@ static int intel_crtc_set_config(struct drm_mode_set *set) fail: intel_set_config_restore_state(dev, config); + intel_modeset_commit_output_state(dev); + /* Try to restore the config */ if (config->mode_changed && !intel_set_mode(save_set.crtc, save_set.mode, @@ -7888,6 +7972,8 @@ void intel_modeset_setup_hw_state(struct drm_device *dev) crtc = to_intel_crtc(dev_priv->pipe_to_crtc_mapping[pipe]); intel_sanitize_crtc(crtc); } + + intel_modeset_update_staged_output_state(dev); } void intel_modeset_gem_init(struct drm_device *dev) diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index 4946282..ae807af 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -133,6 +133,12 @@ struct intel_fbdev { struct intel_encoder { struct drm_encoder base; + /* + * The new crtc this encoder will be driven from. Only differs from + * base->crtc while a modeset is in progress. + */ + struct intel_crtc *new_crtc; + int type; bool needs_tv_clock; /* @@ -153,7 +159,17 @@ struct intel_encoder { struct intel_connector { struct drm_connector base; + /* + * The fixed encoder this connector is connected to. + */ struct intel_encoder *encoder; + + /* + * The new encoder this connector will be driven. Only differs from + * encoder while a modeset is in progress. + */ + struct intel_encoder *new_encoder; + /* Reads out the current hw, returning true if the connector is enabled * and active (i.e. dpms ON state). */ bool (*get_hw_state)(struct intel_connector *); -- cgit v0.10.2 From 94352cf9a5328bb1a44288e6c2c1276695f8a356 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Thu, 5 Jul 2012 22:51:56 +0200 Subject: drm/i915: push crtc->fb update into pipe_set_base Passing in the old fb, having overwritten the current fb, leads to some neatly convoluted code. It's much simpler if we defer the crtc->fb update to the place that updates the hw, in pipe_set_base. This way we also don't need to restore anything in case something fails - we only update crtc->fb once things have succeeded. The real reason for this change is that now we keep the old fb assigned to crtc->fb, which allows us to finally move the crtc disable case into the common low-level set_mode function in the next patch. Also don't clobber crtc->x and crtc->y, we neatly pass these down the callchain already. Unfortunately we can't do the same with crtc->mode, because that one is being used in the mode_set callbacks. v2: Don't restore the drm_crtc object any more on failed modesets, since we've lose an fb reference otherwise. Also (and this is the reason this has been found), this totally confused the modeset state tracking, since it clobbers crtc->enabled. Issue reported by Paulo Zanoni. v3: Rip out the entire crtc saving into struct intel_set_config, not just the restoring part. Reviewed-by: Jesse Barnes Signed-Off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 5031e0c..9ce8c59 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -2201,16 +2201,17 @@ intel_finish_fb(struct drm_framebuffer *old_fb) static int intel_pipe_set_base(struct drm_crtc *crtc, int x, int y, - struct drm_framebuffer *old_fb) + struct drm_framebuffer *fb) { struct drm_device *dev = crtc->dev; struct drm_i915_private *dev_priv = dev->dev_private; struct drm_i915_master_private *master_priv; struct intel_crtc *intel_crtc = to_intel_crtc(crtc); + struct drm_framebuffer *old_fb; int ret; /* no fb bound */ - if (!crtc->fb) { + if (!fb) { DRM_ERROR("No FB bound\n"); return 0; } @@ -2224,7 +2225,7 @@ intel_pipe_set_base(struct drm_crtc *crtc, int x, int y, mutex_lock(&dev->struct_mutex); ret = intel_pin_and_fence_fb_obj(dev, - to_intel_framebuffer(crtc->fb)->obj, + to_intel_framebuffer(fb)->obj, NULL); if (ret != 0) { mutex_unlock(&dev->struct_mutex); @@ -2232,17 +2233,20 @@ intel_pipe_set_base(struct drm_crtc *crtc, int x, int y, return ret; } - if (old_fb) - intel_finish_fb(old_fb); + if (crtc->fb) + intel_finish_fb(crtc->fb); - ret = dev_priv->display.update_plane(crtc, crtc->fb, x, y); + ret = dev_priv->display.update_plane(crtc, fb, x, y); if (ret) { - intel_unpin_fb_obj(to_intel_framebuffer(crtc->fb)->obj); + intel_unpin_fb_obj(to_intel_framebuffer(fb)->obj); mutex_unlock(&dev->struct_mutex); DRM_ERROR("failed to update base address\n"); return ret; } + old_fb = crtc->fb; + crtc->fb = fb; + if (old_fb) { intel_wait_for_vblank(dev, intel_crtc->pipe); intel_unpin_fb_obj(to_intel_framebuffer(old_fb)->obj); @@ -3777,6 +3781,7 @@ static inline bool intel_panel_use_ssc(struct drm_i915_private *dev_priv) * true if they don't match). */ static bool intel_choose_pipe_bpp_dither(struct drm_crtc *crtc, + struct drm_framebuffer *fb, unsigned int *pipe_bpp, struct drm_display_mode *mode) { @@ -3846,7 +3851,7 @@ static bool intel_choose_pipe_bpp_dither(struct drm_crtc *crtc, * also stays within the max display bpc discovered above. */ - switch (crtc->fb->depth) { + switch (fb->depth) { case 8: bpc = 8; /* since we go through a colormap */ break; @@ -4265,7 +4270,7 @@ static int i9xx_crtc_mode_set(struct drm_crtc *crtc, struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode, int x, int y, - struct drm_framebuffer *old_fb) + struct drm_framebuffer *fb) { struct drm_device *dev = crtc->dev; struct drm_i915_private *dev_priv = dev->dev_private; @@ -4455,7 +4460,7 @@ static int i9xx_crtc_mode_set(struct drm_crtc *crtc, I915_WRITE(DSPCNTR(plane), dspcntr); POSTING_READ(DSPCNTR(plane)); - ret = intel_pipe_set_base(crtc, x, y, old_fb); + ret = intel_pipe_set_base(crtc, x, y, fb); intel_update_watermarks(dev); @@ -4613,7 +4618,7 @@ static int ironlake_crtc_mode_set(struct drm_crtc *crtc, struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode, int x, int y, - struct drm_framebuffer *old_fb) + struct drm_framebuffer *fb) { struct drm_device *dev = crtc->dev; struct drm_i915_private *dev_priv = dev->dev_private; @@ -4733,7 +4738,7 @@ static int ironlake_crtc_mode_set(struct drm_crtc *crtc, /* determine panel color depth */ temp = I915_READ(PIPECONF(pipe)); temp &= ~PIPE_BPC_MASK; - dither = intel_choose_pipe_bpp_dither(crtc, &pipe_bpp, mode); + dither = intel_choose_pipe_bpp_dither(crtc, fb, &pipe_bpp, mode); switch (pipe_bpp) { case 18: temp |= PIPE_6BPC; @@ -5002,7 +5007,7 @@ static int ironlake_crtc_mode_set(struct drm_crtc *crtc, I915_WRITE(DSPCNTR(plane), dspcntr); POSTING_READ(DSPCNTR(plane)); - ret = intel_pipe_set_base(crtc, x, y, old_fb); + ret = intel_pipe_set_base(crtc, x, y, fb); intel_update_watermarks(dev); @@ -5015,7 +5020,7 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc, struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode, int x, int y, - struct drm_framebuffer *old_fb) + struct drm_framebuffer *fb) { struct drm_device *dev = crtc->dev; struct drm_i915_private *dev_priv = dev->dev_private; @@ -5026,7 +5031,7 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc, drm_vblank_pre_modeset(dev, pipe); ret = dev_priv->display.crtc_mode_set(crtc, mode, adjusted_mode, - x, y, old_fb); + x, y, fb); drm_vblank_post_modeset(dev, pipe); return ret; @@ -5718,7 +5723,7 @@ bool intel_get_load_detect_pipe(struct drm_connector *connector, struct drm_encoder *encoder = &intel_encoder->base; struct drm_crtc *crtc = NULL; struct drm_device *dev = encoder->dev; - struct drm_framebuffer *old_fb; + struct drm_framebuffer *fb; int i = -1; DRM_DEBUG_KMS("[CONNECTOR:%d:%s], [ENCODER:%d:%s]\n", @@ -5779,8 +5784,6 @@ bool intel_get_load_detect_pipe(struct drm_connector *connector, if (!mode) mode = &load_detect_mode; - old_fb = crtc->fb; - /* We need a framebuffer large enough to accommodate all accesses * that the plane may generate whilst we perform load detection. * We can not rely on the fbcon either being present (we get called @@ -5788,19 +5791,19 @@ bool intel_get_load_detect_pipe(struct drm_connector *connector, * not even exist) or that it is large enough to satisfy the * requested mode. */ - crtc->fb = mode_fits_in_fbdev(dev, mode); - if (crtc->fb == NULL) { + fb = mode_fits_in_fbdev(dev, mode); + if (fb == NULL) { DRM_DEBUG_KMS("creating tmp fb for load-detection\n"); - crtc->fb = intel_framebuffer_create_for_mode(dev, mode, 24, 32); - old->release_fb = crtc->fb; + fb = intel_framebuffer_create_for_mode(dev, mode, 24, 32); + old->release_fb = fb; } else DRM_DEBUG_KMS("reusing fbdev for load-detection framebuffer\n"); - if (IS_ERR(crtc->fb)) { + if (IS_ERR(fb)) { DRM_DEBUG_KMS("failed to allocate framebuffer for load-detection\n"); goto fail; } - if (!intel_set_mode(crtc, mode, 0, 0, old_fb)) { + if (!intel_set_mode(crtc, mode, 0, 0, fb)) { DRM_DEBUG_KMS("failed to set mode on load-detect pipe\n"); if (old->release_fb) old->release_fb->funcs->destroy(old->release_fb); @@ -5814,7 +5817,6 @@ bool intel_get_load_detect_pipe(struct drm_connector *connector, fail: connector->encoder = NULL; encoder->crtc = NULL; - crtc->fb = old_fb; return false; } @@ -6666,13 +6668,12 @@ static void intel_modeset_commit_output_state(struct drm_device *dev) bool intel_set_mode(struct drm_crtc *crtc, struct drm_display_mode *mode, - int x, int y, struct drm_framebuffer *old_fb) + int x, int y, struct drm_framebuffer *fb) { struct drm_device *dev = crtc->dev; drm_i915_private_t *dev_priv = dev->dev_private; struct drm_display_mode *adjusted_mode, saved_mode, saved_hwmode; struct drm_encoder_helper_funcs *encoder_funcs; - int saved_x, saved_y; struct drm_encoder *encoder; bool ret = true; @@ -6686,15 +6687,11 @@ bool intel_set_mode(struct drm_crtc *crtc, saved_hwmode = crtc->hwmode; saved_mode = crtc->mode; - saved_x = crtc->x; - saved_y = crtc->y; /* Update crtc values up front so the driver can rely on them for mode * setting. */ crtc->mode = *mode; - crtc->x = x; - crtc->y = y; /* Pass our mode to the connectors and the CRTC to give them a chance to * adjust it according to limitations or connector properties, and also @@ -6725,7 +6722,7 @@ bool intel_set_mode(struct drm_crtc *crtc, /* Set up the DPLL and any encoders state that needs to adjust or depend * on the DPLL. */ - ret = !intel_crtc_mode_set(crtc, mode, adjusted_mode, x, y, old_fb); + ret = !intel_crtc_mode_set(crtc, mode, adjusted_mode, x, y, fb); if (!ret) goto done; @@ -6741,6 +6738,9 @@ bool intel_set_mode(struct drm_crtc *crtc, encoder_funcs->mode_set(encoder, mode, adjusted_mode); } + crtc->x = x; + crtc->y = y; + /* Now enable the clocks, plane, pipe, and connectors that we set up. */ dev_priv->display.crtc_enable(crtc); @@ -6759,8 +6759,6 @@ done: if (!ret) { crtc->hwmode = saved_hwmode; crtc->mode = saved_mode; - crtc->x = saved_x; - crtc->y = saved_y; } return ret; @@ -6773,25 +6771,16 @@ static void intel_set_config_free(struct intel_set_config *config) kfree(config->save_connector_encoders); kfree(config->save_encoder_crtcs); - kfree(config->save_crtcs); kfree(config); } static int intel_set_config_save_state(struct drm_device *dev, struct intel_set_config *config) { - struct drm_crtc *crtc; struct drm_encoder *encoder; struct drm_connector *connector; int count; - /* Allocate space for the backup of all (non-pointer) crtc, encoder and - * connector data. */ - config->save_crtcs = kcalloc(dev->mode_config.num_crtc, - sizeof(struct drm_crtc), GFP_KERNEL); - if (!config->save_crtcs) - return -ENOMEM; - config->save_encoder_crtcs = kcalloc(dev->mode_config.num_encoder, sizeof(struct drm_crtc *), GFP_KERNEL); @@ -6809,11 +6798,6 @@ static int intel_set_config_save_state(struct drm_device *dev, * restored, not the drivers personal bookkeeping. */ count = 0; - list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { - config->save_crtcs[count++] = *crtc; - } - - count = 0; list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { config->save_encoder_crtcs[count++] = encoder->crtc; } @@ -6829,17 +6813,11 @@ static int intel_set_config_save_state(struct drm_device *dev, static void intel_set_config_restore_state(struct drm_device *dev, struct intel_set_config *config) { - struct drm_crtc *crtc; struct intel_encoder *encoder; struct intel_connector *connector; int count; count = 0; - list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { - *crtc = config->save_crtcs[count++]; - } - - count = 0; list_for_each_entry(encoder, &dev->mode_config.encoder_list, base.head) { encoder->new_crtc = to_intel_crtc(config->save_encoder_crtcs[count++]); @@ -6994,7 +6972,6 @@ next_encoder: static int intel_crtc_set_config(struct drm_mode_set *set) { struct drm_device *dev; - struct drm_framebuffer *old_fb = NULL; struct drm_mode_set save_set; struct intel_set_config *config; int ret; @@ -7057,13 +7034,10 @@ static int intel_crtc_set_config(struct drm_mode_set *set) DRM_DEBUG_KMS("attempting to set mode from" " userspace\n"); drm_mode_debug_printmodeline(set->mode); - old_fb = set->crtc->fb; - set->crtc->fb = set->fb; if (!intel_set_mode(set->crtc, set->mode, - set->x, set->y, old_fb)) { + set->x, set->y, set->fb)) { DRM_ERROR("failed to set mode on [CRTC:%d]\n", set->crtc->base.id); - set->crtc->fb = old_fb; ret = -EINVAL; goto fail; } @@ -7076,18 +7050,8 @@ static int intel_crtc_set_config(struct drm_mode_set *set) } drm_helper_disable_unused_functions(dev); } else if (config->fb_changed) { - set->crtc->x = set->x; - set->crtc->y = set->y; - - old_fb = set->crtc->fb; - if (set->crtc->fb != set->fb) - set->crtc->fb = set->fb; ret = intel_pipe_set_base(set->crtc, - set->x, set->y, old_fb); - if (ret != 0) { - set->crtc->fb = old_fb; - goto fail; - } + set->x, set->y, set->fb); } intel_set_config_free(config); diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index ae807af..1aa0b9c 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -442,7 +442,6 @@ extern enum drm_connector_status intel_panel_detect(struct drm_device *dev); struct intel_set_config { struct drm_encoder **save_connector_encoders; struct drm_crtc **save_encoder_crtcs; - struct drm_crtc *save_crtcs; bool fb_changed; bool mode_changed; -- cgit v0.10.2 From ba1c28c900f1aeb9a353f230a99c68cd4e550dde Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Thu, 5 Jul 2012 23:27:42 +0200 Subject: drm/i915: remove crtc disabling special case Originally this has been introduced in commit 6eebd6bb5f1ea04f04019e5c39f87a0f17ffb472 Author: Chris Wilson Date: Mon Nov 28 21:10:05 2011 +0000 drm: Fix lack of CRTC disable for drm_crtc_helper_set_config(.fb=NULL) With the improvements of the output state staging and no longer overwriting crtc->fb before the hw state is updated we can now handle crtc disabling as part of the normal modeset sequence. Reviewed-by: Jesse Barnes Signed-Off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 9ce8c59..0f2c2e1 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -6585,30 +6585,6 @@ static bool intel_encoder_crtc_ok(struct drm_encoder *encoder, return false; } -static int -intel_crtc_helper_disable(struct drm_crtc *crtc) -{ - struct drm_device *dev = crtc->dev; - struct drm_connector *connector; - struct drm_encoder *encoder; - - /* Decouple all encoders and their attached connectors from this crtc */ - list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { - if (encoder->crtc != crtc) - continue; - - list_for_each_entry(connector, &dev->mode_config.connector_list, head) { - if (connector->encoder != encoder) - continue; - - connector->encoder = NULL; - } - } - - drm_helper_disable_unused_functions(dev); - return 0; -} - static void intel_crtc_prepare_encoders(struct drm_device *dev) { @@ -6996,7 +6972,6 @@ static int intel_crtc_set_config(struct drm_mode_set *set) (int)set->num_connectors, set->x, set->y); } else { DRM_DEBUG_KMS("[CRTC:%d] [NOFB]\n", set->crtc->base.id); - return intel_crtc_helper_disable(set->crtc); } dev = set->crtc->dev; -- cgit v0.10.2 From 87f1faa630b2045ccb6ab29f18c62279ddd7ef17 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Thu, 5 Jul 2012 23:36:17 +0200 Subject: drm/i915: move output commit and crtc disabling into set_mode It's rather pointless to compute crtc->enabled twice right away ;-) The only thing we really have to be careful about is that we frob the dpms state only after a successful modeset and when we've actually haven't just disabled the crtc. Hooray for convoluted interfaces ... Reviewed-by: Jesse Barnes Signed-Off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 0f2c2e1..0bfc799 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -6653,9 +6653,13 @@ bool intel_set_mode(struct drm_crtc *crtc, struct drm_encoder *encoder; bool ret = true; + intel_modeset_commit_output_state(dev); + crtc->enabled = drm_helper_crtc_in_use(crtc); - if (!crtc->enabled) + if (!crtc->enabled) { + drm_helper_disable_unused_functions(dev); return true; + } adjusted_mode = drm_mode_duplicate(dev, mode); if (!adjusted_mode) @@ -7002,20 +7006,21 @@ static int intel_crtc_set_config(struct drm_mode_set *set) goto fail; if (config->mode_changed) { - intel_modeset_commit_output_state(dev); - - set->crtc->enabled = drm_helper_crtc_in_use(set->crtc); - if (set->crtc->enabled) { + if (set->mode) { DRM_DEBUG_KMS("attempting to set mode from" " userspace\n"); drm_mode_debug_printmodeline(set->mode); - if (!intel_set_mode(set->crtc, set->mode, - set->x, set->y, set->fb)) { - DRM_ERROR("failed to set mode on [CRTC:%d]\n", - set->crtc->base.id); - ret = -EINVAL; - goto fail; - } + } + + if (!intel_set_mode(set->crtc, set->mode, + set->x, set->y, set->fb)) { + DRM_ERROR("failed to set mode on [CRTC:%d]\n", + set->crtc->base.id); + ret = -EINVAL; + goto fail; + } + + if (set->crtc->enabled) { DRM_DEBUG_KMS("Setting connector DPMS state to on\n"); for (i = 0; i < set->num_connectors; i++) { DRM_DEBUG_KMS("\t[CONNECTOR:%d:%s] set DPMS on\n", set->connectors[i]->base.id, @@ -7023,7 +7028,6 @@ static int intel_crtc_set_config(struct drm_mode_set *set) set->connectors[i]->funcs->dpms(set->connectors[i], DRM_MODE_DPMS_ON); } } - drm_helper_disable_unused_functions(dev); } else if (config->fb_changed) { ret = intel_pipe_set_base(set->crtc, set->x, set->y, set->fb); @@ -7036,8 +7040,6 @@ static int intel_crtc_set_config(struct drm_mode_set *set) fail: intel_set_config_restore_state(dev, config); - intel_modeset_commit_output_state(dev); - /* Try to restore the config */ if (config->mode_changed && !intel_set_mode(save_set.crtc, save_set.mode, -- cgit v0.10.2 From 7758a11340cc8845509303b0bee60401738da82e Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Sun, 8 Jul 2012 19:40:39 +0200 Subject: drm/i915: extract adjusted mode computation While at it, adjust a few things: - Only assigng the new mode to crtc->mode right before calling the mode_set callbacks - none of the previous callbacks depend upon this, they all use the mode argument (as they should). - Check encoder->new_crtc instead of the current crtc to check whether the encoder will be used. This prepares for moving the staged output committing further down in the sequence. Follow-on patches will fix up individual ->mode_fixup callbacks (only tv and lvds are affected though). Reviewed-by: Jesse Barnes Signed-Off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 0bfc799..c8fb926 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -6642,6 +6642,48 @@ static void intel_modeset_commit_output_state(struct drm_device *dev) } } +static struct drm_display_mode * +intel_modeset_adjusted_mode(struct drm_crtc *crtc, + struct drm_display_mode *mode) +{ + struct drm_device *dev = crtc->dev; + struct drm_display_mode *adjusted_mode; + struct drm_encoder_helper_funcs *encoder_funcs; + struct intel_encoder *encoder; + + adjusted_mode = drm_mode_duplicate(dev, mode); + if (!adjusted_mode) + return ERR_PTR(-ENOMEM); + + /* Pass our mode to the connectors and the CRTC to give them a chance to + * adjust it according to limitations or connector properties, and also + * a chance to reject the mode entirely. + */ + list_for_each_entry(encoder, &dev->mode_config.encoder_list, + base.head) { + + if (&encoder->new_crtc->base != crtc) + continue; + encoder_funcs = encoder->base.helper_private; + if (!(encoder_funcs->mode_fixup(&encoder->base, mode, + adjusted_mode))) { + DRM_DEBUG_KMS("Encoder fixup failed\n"); + goto fail; + } + } + + if (!(intel_crtc_mode_fixup(crtc, mode, adjusted_mode))) { + DRM_DEBUG_KMS("CRTC fixup failed\n"); + goto fail; + } + DRM_DEBUG_KMS("[CRTC:%d]\n", crtc->base.id); + + return adjusted_mode; +fail: + drm_mode_destroy(dev, adjusted_mode); + return ERR_PTR(-EINVAL); +} + bool intel_set_mode(struct drm_crtc *crtc, struct drm_display_mode *mode, int x, int y, struct drm_framebuffer *fb) @@ -6661,44 +6703,21 @@ bool intel_set_mode(struct drm_crtc *crtc, return true; } - adjusted_mode = drm_mode_duplicate(dev, mode); - if (!adjusted_mode) - return false; saved_hwmode = crtc->hwmode; saved_mode = crtc->mode; - /* Update crtc values up front so the driver can rely on them for mode - * setting. - */ - crtc->mode = *mode; - - /* Pass our mode to the connectors and the CRTC to give them a chance to - * adjust it according to limitations or connector properties, and also - * a chance to reject the mode entirely. - */ - list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { - - if (encoder->crtc != crtc) - continue; - encoder_funcs = encoder->helper_private; - if (!(ret = encoder_funcs->mode_fixup(encoder, mode, - adjusted_mode))) { - DRM_DEBUG_KMS("Encoder fixup failed\n"); - goto done; - } - } - - if (!(ret = intel_crtc_mode_fixup(crtc, mode, adjusted_mode))) { - DRM_DEBUG_KMS("CRTC fixup failed\n"); - goto done; + adjusted_mode = intel_modeset_adjusted_mode(crtc, mode); + if (IS_ERR(adjusted_mode)) { + return false; } - DRM_DEBUG_KMS("[CRTC:%d]\n", crtc->base.id); intel_crtc_prepare_encoders(dev); dev_priv->display.crtc_disable(crtc); + crtc->mode = *mode; + /* Set up the DPLL and any encoders state that needs to adjust or depend * on the DPLL. */ -- cgit v0.10.2 From 6ed0f796c22cd638fac60f29ff72b3a7a03485e6 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Sun, 8 Jul 2012 19:41:43 +0200 Subject: drm/i915: use staged outuput config in tv->mode_fixup The "is this encoder cloned" check will be reused by the lvds encoder, hence exract it. v2: Be a bit more careful about that we need to check the new, staged ouput configuration in the check_non_cloned helper ... v3: Kill the double negation with s/!non_cloned/is_cloned/, suggested by Jesse Barnes. Reviewed-by: Jesse Barnes Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index c8fb926..3eec29c 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -6563,6 +6563,28 @@ static struct drm_crtc_helper_funcs intel_helper_funcs = { .disable = intel_crtc_disable, }; +bool intel_encoder_check_is_cloned(struct intel_encoder *encoder) +{ + struct intel_encoder *other_encoder; + struct drm_crtc *crtc = &encoder->new_crtc->base; + + if (WARN_ON(!crtc)) + return false; + + list_for_each_entry(other_encoder, + &crtc->dev->mode_config.encoder_list, + base.head) { + + if (&other_encoder->new_crtc->base != crtc || + encoder == other_encoder) + continue; + else + return true; + } + + return false; +} + static bool intel_encoder_crtc_ok(struct drm_encoder *encoder, struct drm_crtc *crtc) { diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index 1aa0b9c..8686f47 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -454,6 +454,7 @@ extern void intel_crtc_update_dpms(struct drm_crtc *crtc); 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 bool intel_encoder_check_is_cloned(struct intel_encoder *encoder); extern void intel_connector_dpms(struct drm_connector *, int mode); extern bool intel_connector_get_hw_state(struct intel_connector *connector); extern void intel_connector_check_state(struct intel_connector *); diff --git a/drivers/gpu/drm/i915/intel_tv.c b/drivers/gpu/drm/i915/intel_tv.c index 98ad7fd..003ef9e 100644 --- a/drivers/gpu/drm/i915/intel_tv.c +++ b/drivers/gpu/drm/i915/intel_tv.c @@ -910,17 +910,14 @@ intel_tv_mode_fixup(struct drm_encoder *encoder, const struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode) { - struct drm_device *dev = encoder->dev; struct intel_tv *intel_tv = enc_to_intel_tv(encoder); const struct tv_mode *tv_mode = intel_tv_mode_find(intel_tv); - struct intel_encoder *other_encoder; if (!tv_mode) return false; - for_each_encoder_on_crtc(dev, encoder->crtc, other_encoder) - if (&other_encoder->base != encoder) - return false; + if (intel_encoder_check_is_cloned(&intel_tv->base)) + return false; adjusted_mode->clock = tv_mode->clock; return true; -- cgit v0.10.2 From e24c5c2900ba45e2fa5ed8575ab75f6d4c7e8156 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Sun, 8 Jul 2012 20:17:15 +0200 Subject: drm/i915: use staged outuput config in lvds->mode_fixup - Use the check_cloned helper from the previous patch. - Use encoder->new_crtc to check crtc properties. v2: Kill the double negation with s/!non_cloned/is_cloned, suggested by Jesse Barnes. Reviewed-by: Jesse Barnes Signed-Off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/intel_lvds.c b/drivers/gpu/drm/i915/intel_lvds.c index 52f9e7f..25b892d 100644 --- a/drivers/gpu/drm/i915/intel_lvds.c +++ b/drivers/gpu/drm/i915/intel_lvds.c @@ -250,9 +250,8 @@ static bool intel_lvds_mode_fixup(struct drm_encoder *encoder, { 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); struct intel_lvds *intel_lvds = to_intel_lvds(encoder); - struct intel_encoder *tmp_encoder; + struct intel_crtc *intel_crtc = intel_lvds->base.new_crtc; u32 pfit_control = 0, pfit_pgm_ratios = 0, border = 0; int pipe; @@ -262,14 +261,8 @@ static bool intel_lvds_mode_fixup(struct drm_encoder *encoder, return false; } - /* Should never happen!! */ - for_each_encoder_on_crtc(dev, encoder->crtc, tmp_encoder) { - if (&tmp_encoder->base != encoder) { - DRM_ERROR("Can't enable LVDS and another " - "encoder on the same pipe\n"); - return false; - } - } + if (intel_encoder_check_is_cloned(&intel_lvds->base)) + return false; /* * We have timings from the BIOS for the panel, put them in -- cgit v0.10.2 From e2e1ed41ed6e3012464e8a8e7cf7bbcb6d3f7412 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Sun, 8 Jul 2012 21:14:38 +0200 Subject: drm/i915: compute masks of crtcs affected in set_mode This is definetely a bit more generic than currently required, but if we keep track of all crtcs that need to be disabled/enable (because they loose an encoder or something similar), crtcs that get completely disabled and those that we need to do an actual mode change nicely prepares us for global modeset operations on multiple crtcs. The only big thing missing here would be a global resource allocation step (for e.g. pch plls), which would equally frob these bitmasks if e.g. a crtc only needs a new pll. Or if we need to enable dithering on an another pipe due to bandwidth constrains somewhere. These masks aren't yet put to use in this patch, this will follow in the next one. v2-v5: Fix up the computations for good (hopefully). v6: Fixup a confusion reported by Damien Lespiau: I've conserved the (imo braindead) behaviour of the crtc helper to disable _any_ disconnected outputs if we do a modeset, even when that newly disabled connector isn't connected to the crtc being changed by the modeset. The effect of that is that we could disable an arbitrary number of unrelated crtcs, which I haven't taken into account when writing this code. Fix this up. Reviewed-by: Jesse Barnes Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 3eec29c..d354aa0 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -6706,6 +6706,98 @@ fail: return ERR_PTR(-EINVAL); } +/* Computes which crtcs are affected and sets the relevant bits in the mask. For + * simplicity we use the crtc's pipe number (because it's easier to obtain). */ +static void +intel_modeset_affected_pipes(struct drm_crtc *crtc, unsigned *modeset_pipes, + unsigned *prepare_pipes, unsigned *disable_pipes) +{ + struct intel_crtc *intel_crtc; + struct drm_device *dev = crtc->dev; + struct intel_encoder *encoder; + struct intel_connector *connector; + struct drm_crtc *tmp_crtc; + + *disable_pipes = *modeset_pipes = *prepare_pipes = 0; + + /* Check which crtcs have changed outputs connected to them, these need + * to be part of the prepare_pipes mask. We don't (yet) support global + * modeset across multiple crtcs, so modeset_pipes will only have one + * bit set at most. */ + list_for_each_entry(connector, &dev->mode_config.connector_list, + base.head) { + if (connector->base.encoder == &connector->new_encoder->base) + continue; + + if (connector->base.encoder) { + tmp_crtc = connector->base.encoder->crtc; + + *prepare_pipes |= 1 << to_intel_crtc(tmp_crtc)->pipe; + } + + if (connector->new_encoder) + *prepare_pipes |= + 1 << connector->new_encoder->new_crtc->pipe; + } + + list_for_each_entry(encoder, &dev->mode_config.encoder_list, + base.head) { + if (encoder->base.crtc == &encoder->new_crtc->base) + continue; + + if (encoder->base.crtc) { + tmp_crtc = encoder->base.crtc; + + *prepare_pipes |= 1 << to_intel_crtc(tmp_crtc)->pipe; + } + + if (encoder->new_crtc) + *prepare_pipes |= 1 << encoder->new_crtc->pipe; + } + + /* Check for any pipes that will be fully disabled ... */ + list_for_each_entry(intel_crtc, &dev->mode_config.crtc_list, + base.head) { + bool used = false; + + /* Don't try to disable disabled crtcs. */ + if (!intel_crtc->base.enabled) + continue; + + list_for_each_entry(encoder, &dev->mode_config.encoder_list, + base.head) { + if (encoder->new_crtc == intel_crtc) + used = true; + } + + if (!used) + *disable_pipes |= 1 << intel_crtc->pipe; + } + + + /* set_mode is also used to update properties on life display pipes. */ + intel_crtc = to_intel_crtc(crtc); + if (crtc->enabled) + *prepare_pipes |= 1 << intel_crtc->pipe; + + /* We only support modeset on one single crtc, hence we need to do that + * only for the passed in crtc iff we change anything else than just + * disable crtcs. + * + * This is actually not true, to be fully compatible with the old crtc + * helper we automatically disable _any_ output (i.e. doesn't need to be + * connected to the crtc we're modesetting on) if it's disconnected. + * Which is a rather nutty api (since changed the output configuration + * without userspace's explicit request can lead to confusion), but + * alas. Hence we currently need to modeset on all pipes we prepare. */ + if (*prepare_pipes) + *modeset_pipes = *prepare_pipes; + + /* ... and mask these out. */ + *modeset_pipes &= ~(*disable_pipes); + *prepare_pipes &= ~(*disable_pipes); +} + bool intel_set_mode(struct drm_crtc *crtc, struct drm_display_mode *mode, int x, int y, struct drm_framebuffer *fb) @@ -6715,8 +6807,12 @@ bool intel_set_mode(struct drm_crtc *crtc, struct drm_display_mode *adjusted_mode, saved_mode, saved_hwmode; struct drm_encoder_helper_funcs *encoder_funcs; struct drm_encoder *encoder; + unsigned disable_pipe, prepare_pipes, modeset_pipes; bool ret = true; + intel_modeset_affected_pipes(crtc, &modeset_pipes, + &prepare_pipes, &disable_pipe); + intel_modeset_commit_output_state(dev); crtc->enabled = drm_helper_crtc_in_use(crtc); -- cgit v0.10.2 From 25c5b2665fe4cc5a93edd29b62e7c05c15dddd26 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Sun, 8 Jul 2012 22:08:04 +0200 Subject: drm/i915: implement new set_mode code flow ... using the pipe masks from the previous patch. Well, not quite: - We still need to call the disable_unused_functions helper, until we've moved the call to commit_output_state further down and adjusted intel_crtc_disable a bit. The next patch will do that. - Because we don't support (yet) mode changes on more than one crtc at a time, some of the modeset_pipes checks are a bit hackish - but that only needs fixing once we incorporate global modeset support. Reviewed-by: Jesse Barnes Signed-Off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index d354aa0..16cbf95 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -6798,6 +6798,12 @@ intel_modeset_affected_pipes(struct drm_crtc *crtc, unsigned *modeset_pipes, *prepare_pipes &= ~(*disable_pipes); } +#define for_each_intel_crtc_masked(dev, mask, intel_crtc) \ + list_for_each_entry((intel_crtc), \ + &(dev)->mode_config.crtc_list, \ + base.head) \ + if (mask & (1 <<(intel_crtc)->pipe)) \ + bool intel_set_mode(struct drm_crtc *crtc, struct drm_display_mode *mode, int x, int y, struct drm_framebuffer *fb) @@ -6807,73 +6813,92 @@ bool intel_set_mode(struct drm_crtc *crtc, struct drm_display_mode *adjusted_mode, saved_mode, saved_hwmode; struct drm_encoder_helper_funcs *encoder_funcs; struct drm_encoder *encoder; - unsigned disable_pipe, prepare_pipes, modeset_pipes; + struct intel_crtc *intel_crtc; + unsigned disable_pipes, prepare_pipes, modeset_pipes; bool ret = true; intel_modeset_affected_pipes(crtc, &modeset_pipes, - &prepare_pipes, &disable_pipe); + &prepare_pipes, &disable_pipes); + + DRM_DEBUG_KMS("set mode pipe masks: modeset: %x, prepare: %x, disable: %x\n", + modeset_pipes, prepare_pipes, disable_pipes); intel_modeset_commit_output_state(dev); crtc->enabled = drm_helper_crtc_in_use(crtc); - if (!crtc->enabled) { - drm_helper_disable_unused_functions(dev); - return true; - } + for_each_intel_crtc_masked(dev, disable_pipes, intel_crtc) + drm_helper_disable_unused_functions(dev); saved_hwmode = crtc->hwmode; saved_mode = crtc->mode; - adjusted_mode = intel_modeset_adjusted_mode(crtc, mode); - if (IS_ERR(adjusted_mode)) { - return false; - } + /* Hack: Because we don't (yet) support global modeset on multiple + * crtcs, we don't keep track of the new mode for more than one crtc. + * Hence simply check whether any bit is set in modeset_pipes in all the + * pieces of code that are not yet converted to deal with mutliple crtcs + * changing their mode at the same time. */ + adjusted_mode = NULL; + if (modeset_pipes) { + adjusted_mode = intel_modeset_adjusted_mode(crtc, mode); + if (IS_ERR(adjusted_mode)) { + return false; + } - intel_crtc_prepare_encoders(dev); + intel_crtc_prepare_encoders(dev); + } - dev_priv->display.crtc_disable(crtc); + for_each_intel_crtc_masked(dev, prepare_pipes, intel_crtc) + dev_priv->display.crtc_disable(&intel_crtc->base); - crtc->mode = *mode; + if (modeset_pipes) { + crtc->mode = *mode; + crtc->x = x; + crtc->y = y; + } /* Set up the DPLL and any encoders state that needs to adjust or depend * on the DPLL. */ - ret = !intel_crtc_mode_set(crtc, mode, adjusted_mode, x, y, fb); - if (!ret) - goto done; + for_each_intel_crtc_masked(dev, modeset_pipes, intel_crtc) { + ret = !intel_crtc_mode_set(&intel_crtc->base, + mode, adjusted_mode, + x, y, fb); + if (!ret) + goto done; - list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { + list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { - if (encoder->crtc != crtc) - continue; + if (encoder->crtc != &intel_crtc->base) + continue; - DRM_DEBUG_KMS("[ENCODER:%d:%s] set [MODE:%d:%s]\n", - encoder->base.id, drm_get_encoder_name(encoder), - mode->base.id, mode->name); - encoder_funcs = encoder->helper_private; - encoder_funcs->mode_set(encoder, mode, adjusted_mode); + DRM_DEBUG_KMS("[ENCODER:%d:%s] set [MODE:%d:%s]\n", + encoder->base.id, drm_get_encoder_name(encoder), + mode->base.id, mode->name); + encoder_funcs = encoder->helper_private; + encoder_funcs->mode_set(encoder, mode, adjusted_mode); + } } - crtc->x = x; - crtc->y = y; - /* Now enable the clocks, plane, pipe, and connectors that we set up. */ - dev_priv->display.crtc_enable(crtc); + for_each_intel_crtc_masked(dev, prepare_pipes, intel_crtc) + dev_priv->display.crtc_enable(&intel_crtc->base); - /* Store real post-adjustment hardware mode. */ - crtc->hwmode = *adjusted_mode; + if (modeset_pipes) { + /* Store real post-adjustment hardware mode. */ + crtc->hwmode = *adjusted_mode; - /* Calculate and store various constants which - * are later needed by vblank and swap-completion - * timestamping. They are derived from true hwmode. - */ - drm_calc_timestamping_constants(crtc); + /* Calculate and store various constants which + * are later needed by vblank and swap-completion + * timestamping. They are derived from true hwmode. + */ + drm_calc_timestamping_constants(crtc); + } /* FIXME: add subpixel order */ done: drm_mode_destroy(dev, adjusted_mode); - if (!ret) { + if (!ret && crtc->enabled) { crtc->hwmode = saved_hwmode; crtc->mode = saved_mode; } @@ -6881,6 +6906,8 @@ done: return ret; } +#undef for_each_intel_crtc_masked + static void intel_set_config_free(struct intel_set_config *config) { if (!config) -- cgit v0.10.2 From 976f8a20139b1f238ab3676a732acf87fbd38c6a Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Sun, 8 Jul 2012 22:34:21 +0200 Subject: drm/i915: push commit_output_state past crtc disabling This requires a few changes - We still need a noop function for crtc->disable, becuase the fb helper is a bit too intimate with the crtc helper. - We need to clear crtc->fb ourselves in intel_crtc_disable now that we no longer rely on the helper's disable_unused_functions to do that. - We need to split out the sare update code, becuase the crtc code can't call update_dpms any more, it needs to disable the crtc unconditionally. This is because we now keep onto the encoder -> crtc mapping of the (still) active output pipe configuration. - To check that we really disable a crtc that still has encoders, insert a WARN_ON(!enabled) in the crtc disable function. - Lastly, we need to walk over all crtcs to update their enabled state after having called commit_output_state - for all disabled crtcs the crtc helper code has done that for us previously. v2: Update connector dpms and encoder->connectors_active after disabling the crtc, too. v3: Noop-out intel_encoder_disable. Similarly to the crtc disable callback used by the crtc helper code we can't simply remove all these encoder callbacks: The fb helper (which we still use) has a rather incetious relationship with the crtc helper code ... Reviewed-by: Jesse Barnes Signed-Off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 16cbf95..b843bc0 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -3462,26 +3462,13 @@ static void i9xx_crtc_off(struct drm_crtc *crtc) { } -/** - * Sets the power management mode of the pipe and plane. - */ -void intel_crtc_update_dpms(struct drm_crtc *crtc) +static void intel_crtc_update_sarea(struct drm_crtc *crtc, + bool enabled) { struct drm_device *dev = crtc->dev; - struct drm_i915_private *dev_priv = dev->dev_private; struct drm_i915_master_private *master_priv; struct intel_crtc *intel_crtc = to_intel_crtc(crtc); - struct intel_encoder *intel_encoder; int pipe = intel_crtc->pipe; - bool enabled, enable = false; - - for_each_encoder_on_crtc(dev, crtc, intel_encoder) - enable |= intel_encoder->connectors_active; - - if (enable) - dev_priv->display.crtc_enable(crtc); - else - dev_priv->display.crtc_disable(crtc); if (!dev->primary->master) return; @@ -3490,8 +3477,6 @@ void intel_crtc_update_dpms(struct drm_crtc *crtc) if (!master_priv->sarea_priv) return; - enabled = crtc->enabled && enable; - switch (pipe) { case 0: master_priv->sarea_priv->pipeA_w = enabled ? crtc->mode.hdisplay : 0; @@ -3507,14 +3492,42 @@ void intel_crtc_update_dpms(struct drm_crtc *crtc) } } +/** + * Sets the power management mode of the pipe and plane. + */ +void intel_crtc_update_dpms(struct drm_crtc *crtc) +{ + struct drm_device *dev = crtc->dev; + struct drm_i915_private *dev_priv = dev->dev_private; + struct intel_encoder *intel_encoder; + bool enable = false; + + for_each_encoder_on_crtc(dev, crtc, intel_encoder) + enable |= intel_encoder->connectors_active; + + if (enable) + dev_priv->display.crtc_enable(crtc); + else + dev_priv->display.crtc_disable(crtc); + + intel_crtc_update_sarea(crtc, enable); +} + +static void intel_crtc_noop(struct drm_crtc *crtc) +{ +} + static void intel_crtc_disable(struct drm_crtc *crtc) { struct drm_device *dev = crtc->dev; + struct drm_connector *connector; struct drm_i915_private *dev_priv = dev->dev_private; - /* crtc->disable is only called when we have no encoders, hence this - * will disable the pipe. */ - intel_crtc_update_dpms(crtc); + /* crtc should still be enabled when we disable it. */ + WARN_ON(!crtc->enabled); + + dev_priv->display.crtc_disable(crtc); + intel_crtc_update_sarea(crtc, false); dev_priv->display.off(crtc); assert_plane_disabled(dev->dev_private, to_intel_crtc(crtc)->plane); @@ -3524,14 +3537,24 @@ static void intel_crtc_disable(struct drm_crtc *crtc) mutex_lock(&dev->struct_mutex); intel_unpin_fb_obj(to_intel_framebuffer(crtc->fb)->obj); mutex_unlock(&dev->struct_mutex); + crtc->fb = NULL; + } + + /* Update computed state. */ + list_for_each_entry(connector, &dev->mode_config.connector_list, head) { + if (!connector->encoder || !connector->encoder->crtc) + continue; + + if (connector->encoder->crtc != crtc) + continue; + + connector->dpms = DRM_MODE_DPMS_OFF; + to_intel_encoder(connector->encoder)->connectors_active = false; } } 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) @@ -6560,7 +6583,7 @@ free_work: static struct drm_crtc_helper_funcs intel_helper_funcs = { .mode_set_base_atomic = intel_pipe_set_base_atomic, .load_lut = intel_crtc_load_lut, - .disable = intel_crtc_disable, + .disable = intel_crtc_noop, }; bool intel_encoder_check_is_cloned(struct intel_encoder *encoder) @@ -6823,12 +6846,14 @@ bool intel_set_mode(struct drm_crtc *crtc, DRM_DEBUG_KMS("set mode pipe masks: modeset: %x, prepare: %x, disable: %x\n", modeset_pipes, prepare_pipes, disable_pipes); - intel_modeset_commit_output_state(dev); + for_each_intel_crtc_masked(dev, disable_pipes, intel_crtc) + intel_crtc_disable(&intel_crtc->base); - crtc->enabled = drm_helper_crtc_in_use(crtc); + intel_modeset_commit_output_state(dev); - for_each_intel_crtc_masked(dev, disable_pipes, intel_crtc) - drm_helper_disable_unused_functions(dev); + list_for_each_entry(intel_crtc, &dev->mode_config.crtc_list, + base.head) + intel_crtc->base.enabled = drm_helper_crtc_in_use(crtc); saved_hwmode = crtc->hwmode; saved_mode = crtc->mode; -- cgit v0.10.2 From 1f70385510991992f3aef339982ca790faa52b06 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Wed, 11 Jul 2012 16:51:39 +0200 Subject: drm/i915: s/intel_encoder_disable/intel_encoder_noop Because that's what it is. Unfortunately we can't rip this out because the fb helper has an incetious relationship with the crtc helper - it likes to call disable_unused_functions, among other things. Reviewed-by: Jesse Barnes Signed-Off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/intel_crt.c b/drivers/gpu/drm/i915/intel_crt.c index c002d26..cca266f 100644 --- a/drivers/gpu/drm/i915/intel_crt.c +++ b/drivers/gpu/drm/i915/intel_crt.c @@ -641,7 +641,7 @@ static void intel_crt_reset(struct drm_connector *connector) static const struct drm_encoder_helper_funcs crt_encoder_funcs = { .mode_fixup = intel_crt_mode_fixup, .mode_set = intel_crt_mode_set, - .disable = intel_encoder_disable, + .disable = intel_encoder_noop, }; static const struct drm_connector_funcs intel_crt_connector_funcs = { diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index b843bc0..9a06611 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -3553,7 +3553,7 @@ static void intel_crtc_disable(struct drm_crtc *crtc) } } -void intel_encoder_disable(struct drm_encoder *encoder) +void intel_encoder_noop(struct drm_encoder *encoder) { } diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index b07d4b4..3b1c407 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -2393,7 +2393,7 @@ static void intel_dp_encoder_destroy(struct drm_encoder *encoder) static const struct drm_encoder_helper_funcs intel_dp_helper_funcs = { .mode_fixup = intel_dp_mode_fixup, .mode_set = intel_dp_mode_set, - .disable = intel_encoder_disable, + .disable = intel_encoder_noop, }; static const struct drm_connector_funcs intel_dp_connector_funcs = { diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index 8686f47..5137f9b 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -451,7 +451,7 @@ extern bool intel_set_mode(struct drm_crtc *crtc, struct drm_display_mode *mode, int x, int y, struct drm_framebuffer *old_fb); extern void intel_crtc_load_lut(struct drm_crtc *crtc); extern void intel_crtc_update_dpms(struct drm_crtc *crtc); -extern void intel_encoder_disable(struct drm_encoder *encoder); +extern void intel_encoder_noop(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 bool intel_encoder_check_is_cloned(struct intel_encoder *encoder); diff --git a/drivers/gpu/drm/i915/intel_dvo.c b/drivers/gpu/drm/i915/intel_dvo.c index 17dc8be..aa1bf57 100644 --- a/drivers/gpu/drm/i915/intel_dvo.c +++ b/drivers/gpu/drm/i915/intel_dvo.c @@ -346,7 +346,7 @@ static void intel_dvo_destroy(struct drm_connector *connector) static const struct drm_encoder_helper_funcs intel_dvo_helper_funcs = { .mode_fixup = intel_dvo_mode_fixup, .mode_set = intel_dvo_mode_set, - .disable = intel_encoder_disable, + .disable = intel_encoder_noop, }; static const struct drm_connector_funcs intel_dvo_connector_funcs = { diff --git a/drivers/gpu/drm/i915/intel_hdmi.c b/drivers/gpu/drm/i915/intel_hdmi.c index e7d5078..7acf2d9 100644 --- a/drivers/gpu/drm/i915/intel_hdmi.c +++ b/drivers/gpu/drm/i915/intel_hdmi.c @@ -907,13 +907,13 @@ static void intel_hdmi_destroy(struct drm_connector *connector) static const struct drm_encoder_helper_funcs intel_hdmi_helper_funcs_hsw = { .mode_fixup = intel_hdmi_mode_fixup, .mode_set = intel_ddi_mode_set, - .disable = intel_encoder_disable, + .disable = intel_encoder_noop, }; static const struct drm_encoder_helper_funcs intel_hdmi_helper_funcs = { .mode_fixup = intel_hdmi_mode_fixup, .mode_set = intel_hdmi_mode_set, - .disable = intel_encoder_disable, + .disable = intel_encoder_noop, }; static const struct drm_connector_funcs intel_hdmi_connector_funcs = { diff --git a/drivers/gpu/drm/i915/intel_lvds.c b/drivers/gpu/drm/i915/intel_lvds.c index 25b892d..5646895 100644 --- a/drivers/gpu/drm/i915/intel_lvds.c +++ b/drivers/gpu/drm/i915/intel_lvds.c @@ -590,7 +590,7 @@ static int intel_lvds_set_property(struct drm_connector *connector, static const struct drm_encoder_helper_funcs intel_lvds_helper_funcs = { .mode_fixup = intel_lvds_mode_fixup, .mode_set = intel_lvds_mode_set, - .disable = intel_encoder_disable, + .disable = intel_encoder_noop, }; static const struct drm_connector_helper_funcs intel_lvds_connector_helper_funcs = { diff --git a/drivers/gpu/drm/i915/intel_sdvo.c b/drivers/gpu/drm/i915/intel_sdvo.c index 198bb89..6f53871 100644 --- a/drivers/gpu/drm/i915/intel_sdvo.c +++ b/drivers/gpu/drm/i915/intel_sdvo.c @@ -1936,7 +1936,7 @@ done: static const struct drm_encoder_helper_funcs intel_sdvo_helper_funcs = { .mode_fixup = intel_sdvo_mode_fixup, .mode_set = intel_sdvo_mode_set, - .disable = intel_encoder_disable + .disable = intel_encoder_noop, }; static const struct drm_connector_funcs intel_sdvo_connector_funcs = { diff --git a/drivers/gpu/drm/i915/intel_tv.c b/drivers/gpu/drm/i915/intel_tv.c index 003ef9e..d2c5c8f 100644 --- a/drivers/gpu/drm/i915/intel_tv.c +++ b/drivers/gpu/drm/i915/intel_tv.c @@ -1492,7 +1492,7 @@ out: static const struct drm_encoder_helper_funcs intel_tv_helper_funcs = { .mode_fixup = intel_tv_mode_fixup, .mode_set = intel_tv_mode_set, - .disable = intel_encoder_disable, + .disable = intel_encoder_noop, }; static const struct drm_connector_funcs intel_tv_connector_funcs = { -- cgit v0.10.2 From 284637d9229dc1115947bb04008730844afbc059 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Mon, 9 Jul 2012 09:51:57 +0200 Subject: drm/i915: WARN if the pipe won't turn off This seems to be the symptom of a few neat bugs, hence be more obnoxious when this fails. Reviewed-by: Jesse Barnes Signed-Off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 9a06611..453bc7c 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -1006,7 +1006,7 @@ void intel_wait_for_pipe_off(struct drm_device *dev, int pipe) /* Wait for the Pipe State to go off */ if (wait_for((I915_READ(reg) & I965_PIPECONF_ACTIVE) == 0, 100)) - DRM_DEBUG_KMS("pipe_off wait timed out\n"); + WARN(1, "pipe_off wait timed out\n"); } else { u32 last_line, line_mask; int reg = PIPEDSL(pipe); @@ -1024,7 +1024,7 @@ void intel_wait_for_pipe_off(struct drm_device *dev, int pipe) } 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"); + WARN(1, "pipe_off wait timed out\n"); } } -- cgit v0.10.2 From fc303101dc9bfe2b079798c948f6ad4245c51e57 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Mon, 9 Jul 2012 10:40:58 +0200 Subject: drm/i915: switch the load detect code to the staged modeset config Now that set_mode also disables crtcs and expects it's new configuration in the staged output links we need to adjust the load detect code a bit. Reviewed-by: Jesse Barnes Signed-Off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 453bc7c..e3d3abf 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -5796,8 +5796,8 @@ bool intel_get_load_detect_pipe(struct drm_connector *connector, return false; } - encoder->crtc = crtc; - connector->encoder = encoder; + intel_encoder->new_crtc = to_intel_crtc(crtc); + to_intel_connector(connector)->new_encoder = intel_encoder; intel_crtc = to_intel_crtc(crtc); old->dpms_mode = connector->dpms; @@ -5849,16 +5849,17 @@ void intel_release_load_detect_pipe(struct drm_connector *connector, struct intel_encoder *intel_encoder = intel_attached_encoder(connector); struct drm_encoder *encoder = &intel_encoder->base; - struct drm_device *dev = encoder->dev; DRM_DEBUG_KMS("[CONNECTOR:%d:%s], [ENCODER:%d:%s]\n", connector->base.id, drm_get_connector_name(connector), encoder->base.id, drm_get_encoder_name(encoder)); if (old->load_detect_temp) { - connector->encoder = NULL; - encoder->crtc = NULL; - drm_helper_disable_unused_functions(dev); + struct drm_crtc *crtc = encoder->crtc; + + to_intel_connector(connector)->new_encoder = NULL; + intel_encoder->new_crtc = NULL; + intel_set_mode(crtc, NULL, 0, 0, NULL); if (old->release_fb) old->release_fb->funcs->destroy(old->release_fb); -- cgit v0.10.2 From ea9d758d6ddb9f4bb9639619100743e8f5fa85a0 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Tue, 10 Jul 2012 10:42:52 +0200 Subject: drm/i915: push commit_output_state past the crtc/encoder preparing With this change we can (finally!) rip out a few of the temporary hacks and clean up a few other things: - Kill intel_crtc_prepare_encoders, now unused. - Kill the hacks in the crtc_disable/enable functions to always call the encoder callbacks, we now always call the crtc functions with the right encoder -> crtc links. - Also push down the crtc->enable, encoder and connector dpms state updates. Unfortunately we can't add a WARN in the crtc_disable callbacks to ensure that the crtc is always still enabled when disabling an output pipe - the crtc sanitizer of the hw readout path can hit this when it needs to disable an active pipe without any enabled outputs. - Only call crtc->disable if the pipe is already enabled - again avoids running afoul of the new WARN. v2: Copy&paste our own version of crtc_in_use, too. v3: We need to update the dpms an encoder->connectors_active states, too. v4: I've forgotten to kill the unconditional encoder->disable calls in the crtc_disable functions. v5: Rip out leftover debug printk. v6: Properly clear intel_encoder->connectors_active. This wasn't properly cleared when disabling an encoder because it was no longer on the new connector list, but the crtc was still enabled (i.e. switching the encoder of an active crtc). Reported by Jani Nikula. v7: Don't clobber the encoder->connectors_active state of untouched encoders. Since X likes to first disable all outputs with dpms off before setting a new framebuffer, this hit a few warnings. Reported by Paulo Zanoni. v8: Kill the now stale comment warning that intel_crtc->active is not always updated at the right times. Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index e3d3abf..2a393cb 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -3221,10 +3221,8 @@ static void ironlake_crtc_enable(struct drm_crtc *crtc) WARN_ON(!crtc->enabled); - /* XXX: For compatability with the crtc helper code, call the encoder's - * enable function unconditionally for now. */ if (intel_crtc->active) - goto encoders; + return; intel_crtc->active = true; intel_update_watermarks(dev); @@ -3272,7 +3270,6 @@ static void ironlake_crtc_enable(struct drm_crtc *crtc) intel_crtc_update_cursor(crtc, true); -encoders: for_each_encoder_on_crtc(dev, crtc, encoder) encoder->enable(encoder); @@ -3290,14 +3287,13 @@ static void ironlake_crtc_disable(struct drm_crtc *crtc) int plane = intel_crtc->plane; u32 reg, temp; - /* XXX: For compatability with the crtc helper code, call the encoder's - * disable function unconditionally for now. */ - for_each_encoder_on_crtc(dev, crtc, encoder) - encoder->disable(encoder); if (!intel_crtc->active) return; + for_each_encoder_on_crtc(dev, crtc, encoder) + encoder->disable(encoder); + intel_crtc_wait_for_pending_flips(crtc); drm_vblank_off(dev, pipe); intel_crtc_update_cursor(crtc, false); @@ -3399,10 +3395,8 @@ static void i9xx_crtc_enable(struct drm_crtc *crtc) WARN_ON(!crtc->enabled); - /* XXX: For compatability with the crtc helper code, call the encoder's - * enable function unconditionally for now. */ if (intel_crtc->active) - goto encoders; + return; intel_crtc->active = true; intel_update_watermarks(dev); @@ -3418,7 +3412,6 @@ static void i9xx_crtc_enable(struct drm_crtc *crtc) intel_crtc_dpms_overlay(intel_crtc, true); intel_crtc_update_cursor(crtc, true); -encoders: for_each_encoder_on_crtc(dev, crtc, encoder) encoder->enable(encoder); } @@ -3432,14 +3425,13 @@ static void i9xx_crtc_disable(struct drm_crtc *crtc) int pipe = intel_crtc->pipe; int plane = intel_crtc->plane; - /* XXX: For compatability with the crtc helper code, call the encoder's - * disable function unconditionally for now. */ - for_each_encoder_on_crtc(dev, crtc, encoder) - encoder->disable(encoder); if (!intel_crtc->active) return; + for_each_encoder_on_crtc(dev, crtc, encoder) + encoder->disable(encoder); + /* Give the overlay scaler a chance to disable if it's on this pipe */ intel_crtc_wait_for_pending_flips(crtc); drm_vblank_off(dev, pipe); @@ -6631,18 +6623,6 @@ static bool intel_encoder_crtc_ok(struct drm_encoder *encoder, return false; } -static void -intel_crtc_prepare_encoders(struct drm_device *dev) -{ - struct intel_encoder *encoder; - - list_for_each_entry(encoder, &dev->mode_config.encoder_list, base.head) { - /* Disable unused encoders */ - if (encoder->base.crtc == NULL) - encoder->disable(encoder); - } -} - /** * intel_modeset_update_staged_output_state * @@ -6822,6 +6802,60 @@ intel_modeset_affected_pipes(struct drm_crtc *crtc, unsigned *modeset_pipes, *prepare_pipes &= ~(*disable_pipes); } +static bool intel_crtc_in_use(struct drm_crtc *crtc) +{ + struct drm_encoder *encoder; + struct drm_device *dev = crtc->dev; + + list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) + if (encoder->crtc == crtc) + return true; + + return false; +} + +static void +intel_modeset_update_state(struct drm_device *dev, unsigned prepare_pipes) +{ + struct intel_encoder *intel_encoder; + struct intel_crtc *intel_crtc; + struct drm_connector *connector; + + list_for_each_entry(intel_encoder, &dev->mode_config.encoder_list, + base.head) { + if (!intel_encoder->base.crtc) + continue; + + intel_crtc = to_intel_crtc(intel_encoder->base.crtc); + + if (prepare_pipes & (1 << intel_crtc->pipe)) + intel_encoder->connectors_active = false; + } + + intel_modeset_commit_output_state(dev); + + /* Update computed state. */ + list_for_each_entry(intel_crtc, &dev->mode_config.crtc_list, + base.head) { + intel_crtc->base.enabled = intel_crtc_in_use(&intel_crtc->base); + } + + list_for_each_entry(connector, &dev->mode_config.connector_list, head) { + if (!connector->encoder || !connector->encoder->crtc) + continue; + + intel_crtc = to_intel_crtc(connector->encoder->crtc); + + if (prepare_pipes & (1 << intel_crtc->pipe)) { + connector->dpms = DRM_MODE_DPMS_ON; + + intel_encoder = to_intel_encoder(connector->encoder); + intel_encoder->connectors_active = true; + } + } + +} + #define for_each_intel_crtc_masked(dev, mask, intel_crtc) \ list_for_each_entry((intel_crtc), \ &(dev)->mode_config.crtc_list, \ @@ -6850,12 +6884,6 @@ bool intel_set_mode(struct drm_crtc *crtc, for_each_intel_crtc_masked(dev, disable_pipes, intel_crtc) intel_crtc_disable(&intel_crtc->base); - intel_modeset_commit_output_state(dev); - - list_for_each_entry(intel_crtc, &dev->mode_config.crtc_list, - base.head) - intel_crtc->base.enabled = drm_helper_crtc_in_use(crtc); - saved_hwmode = crtc->hwmode; saved_mode = crtc->mode; @@ -6870,12 +6898,12 @@ bool intel_set_mode(struct drm_crtc *crtc, if (IS_ERR(adjusted_mode)) { return false; } - - intel_crtc_prepare_encoders(dev); } - for_each_intel_crtc_masked(dev, prepare_pipes, intel_crtc) - dev_priv->display.crtc_disable(&intel_crtc->base); + for_each_intel_crtc_masked(dev, prepare_pipes, intel_crtc) { + if (intel_crtc->base.enabled) + dev_priv->display.crtc_disable(&intel_crtc->base); + } if (modeset_pipes) { crtc->mode = *mode; @@ -6883,6 +6911,10 @@ bool intel_set_mode(struct drm_crtc *crtc, crtc->y = y; } + /* Only after disabling all output pipelines that will be changed can we + * update the the output configuration. */ + intel_modeset_update_state(dev, prepare_pipes); + /* Set up the DPLL and any encoders state that needs to adjust or depend * on the DPLL. */ diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index 5137f9b..d03e777 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -184,9 +184,6 @@ struct intel_crtc { * Whether the crtc and the connected output pipeline is active. Implies * that crtc->enabled is set, i.e. the current mode configuration has * some outputs connected to this crtc. - * - * Atm crtc->enabled is unconditionally updated _before_ the hw state is - * changed, hence we can only check this when enabling the crtc. */ bool active; bool primary_disabled; /* is the crtc obscured by a plane? */ -- cgit v0.10.2 From a261b246ebd552fd5d5a8ed84cc931bb821c427f Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Thu, 26 Jul 2012 19:21:47 +0200 Subject: drm/i915: disable all crtcs at suspend time We need this to avoid confusing the hw state readout code with the cpt pch plls at resume time: We'd read the new pipe state (which is disabled), but still believe that we have a life pll connected to that pipe (from before the suspend). Hence properly disable pipes to clear out all the residual state. This has the neat side-effect that we don't enable ports prematurely by restoring bogus state from the saved register values. Reviewed-by: Jesse Barnes Signed-Off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c index 4abac6d..fe7512a 100644 --- a/drivers/gpu/drm/i915/i915_drv.c +++ b/drivers/gpu/drm/i915/i915_drv.c @@ -470,6 +470,9 @@ static int i915_drm_freeze(struct drm_device *dev) "GEM idle failed, resume might fail\n"); return error; } + + intel_modeset_disable(dev); + drm_irq_uninstall(dev); } diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 2a393cb..4c0ec18 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -3545,6 +3545,16 @@ static void intel_crtc_disable(struct drm_crtc *crtc) } } +void intel_modeset_disable(struct drm_device *dev) +{ + struct drm_crtc *crtc; + + list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { + if (crtc->enabled) + intel_crtc_disable(crtc); + } +} + void intel_encoder_noop(struct drm_encoder *encoder) { } diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index d03e777..2061399 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -446,6 +446,7 @@ struct intel_set_config { extern bool intel_set_mode(struct drm_crtc *crtc, struct drm_display_mode *mode, int x, int y, struct drm_framebuffer *old_fb); +extern void intel_modeset_disable(struct drm_device *dev); extern void intel_crtc_load_lut(struct drm_crtc *crtc); extern void intel_crtc_update_dpms(struct drm_crtc *crtc); extern void intel_encoder_noop(struct drm_encoder *encoder); -- cgit v0.10.2 From 9dc10f37e326d5d789929e1886b5a8089ccee5a7 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Wed, 29 Aug 2012 22:58:07 +0200 Subject: drm/i915: no longer call drm_helper_resume_force_mode Since this only calls crtc helper functions, of which a shocking amount are NULL. Now the curious thing is how the new modeset code worked with this function call still present: Thanks to the hw state readout and the suspend fixes to properly quiescent the register state, nothing is actually enabled at resume (if the bios doesn't set up anything). Which means resume_force_mode doesn't actually do anything and hence nothing blows up at resume time. The other reason things do work is that the fbcon layer has it's own resume notifier callback, which restores the mode. And thanks to the force vt switch at suspend/resume, that then forces X to restore it's own mode. Hence everything still worked (as long as the bios doesn't enable anything). And we can just kill the call to resume_force_mode. The upside of both this patch and the preceeding patch to quiescent the modeset state is that our resume path is much simpler: - We now longer restore bogus register values (which most often would enable the backlight a bit and a few ports), causing flickering. - We now longer call resume_force_mode to restore a mode that the fbcon layer would overwrite right away anyway. Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c index fe7512a..cd6697c 100644 --- a/drivers/gpu/drm/i915/i915_drv.c +++ b/drivers/gpu/drm/i915/i915_drv.c @@ -549,11 +549,6 @@ static int i915_drm_thaw(struct drm_device *dev) intel_modeset_setup_hw_state(dev); drm_mode_config_reset(dev); drm_irq_install(dev); - - /* Resume the modeset for every activated CRTC */ - mutex_lock(&dev->mode_config.mutex); - drm_helper_resume_force_mode(dev); - mutex_unlock(&dev->mode_config.mutex); } intel_opregion_init(dev); -- cgit v0.10.2 From 8af6cf88a5ad7fa41d4eb700cc97f01d0bcd2a69 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Tue, 10 Jul 2012 09:50:11 +0200 Subject: drm/i915: add tons of modeset state checks ... let's see whether this catches anything earlier and I can track down a few bugs. v2: Add more checks and also add DRM_DEBUG_KMS output so that it's clear which connector/encoder/crtc is being checked atm. Which proved rather useful for debugging ... v3: Add a WARN in the common encoder dpms function, now that also modeset changes properly update the dpms state ... v4: Properly add a short explanation for each WARN, to avoid the need to correlate dmesg lines with source lines accurately. Suggested by Chris Wilson. v5: Also dump (expected, found) for state checks (or wherever it's not apparent from the test what exactly mismatches with expectations). Again suggested by Chris Wilson. v6: Due to an issue reported by Paulo Zanoni I've noticed that the encoder checking is by far not as strict as it could and should be. Improve this. Reviewed-by: Jesse Barnes Signed-Off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 4c0ec18..d15fa0c 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -3637,7 +3637,7 @@ void intel_connector_dpms(struct drm_connector *connector, int mode) if (encoder->base.crtc) intel_encoder_dpms(encoder, mode); else - encoder->connectors_active = false; + WARN_ON(encoder->connectors_active != false); intel_connector_check_state(to_intel_connector(connector)); } @@ -6872,6 +6872,104 @@ intel_modeset_update_state(struct drm_device *dev, unsigned prepare_pipes) base.head) \ if (mask & (1 <<(intel_crtc)->pipe)) \ +static void +intel_modeset_check_state(struct drm_device *dev) +{ + struct intel_crtc *crtc; + struct intel_encoder *encoder; + struct intel_connector *connector; + + list_for_each_entry(connector, &dev->mode_config.connector_list, + base.head) { + /* This also checks the encoder/connector hw state with the + * ->get_hw_state callbacks. */ + intel_connector_check_state(connector); + + WARN(&connector->new_encoder->base != connector->base.encoder, + "connector's staged encoder doesn't match current encoder\n"); + } + + list_for_each_entry(encoder, &dev->mode_config.encoder_list, + base.head) { + bool enabled = false; + bool active = false; + enum pipe pipe, tracked_pipe; + + DRM_DEBUG_KMS("[ENCODER:%d:%s]\n", + encoder->base.base.id, + drm_get_encoder_name(&encoder->base)); + + WARN(&encoder->new_crtc->base != encoder->base.crtc, + "encoder's stage crtc doesn't match current crtc\n"); + WARN(encoder->connectors_active && !encoder->base.crtc, + "encoder's active_connectors set, but no crtc\n"); + + list_for_each_entry(connector, &dev->mode_config.connector_list, + base.head) { + if (connector->base.encoder != &encoder->base) + continue; + enabled = true; + if (connector->base.dpms != DRM_MODE_DPMS_OFF) + active = true; + } + WARN(!!encoder->base.crtc != enabled, + "encoder's enabled state mismatch " + "(expected %i, found %i)\n", + !!encoder->base.crtc, enabled); + WARN(active && !encoder->base.crtc, + "active encoder with no crtc\n"); + + WARN(encoder->connectors_active != active, + "encoder's computed active state doesn't match tracked active state " + "(expected %i, found %i)\n", active, encoder->connectors_active); + + active = encoder->get_hw_state(encoder, &pipe); + WARN(active != encoder->connectors_active, + "encoder's hw state doesn't match sw tracking " + "(expected %i, found %i)\n", + encoder->connectors_active, active); + + if (!encoder->base.crtc) + continue; + + tracked_pipe = to_intel_crtc(encoder->base.crtc)->pipe; + WARN(active && pipe != tracked_pipe, + "active encoder's pipe doesn't match" + "(expected %i, found %i)\n", + tracked_pipe, pipe); + + } + + list_for_each_entry(crtc, &dev->mode_config.crtc_list, + base.head) { + bool enabled = false; + bool active = false; + + DRM_DEBUG_KMS("[CRTC:%d]\n", + crtc->base.base.id); + + WARN(crtc->active && !crtc->base.enabled, + "active crtc, but not enabled in sw tracking\n"); + + list_for_each_entry(encoder, &dev->mode_config.encoder_list, + base.head) { + if (encoder->base.crtc != &crtc->base) + continue; + enabled = true; + if (encoder->connectors_active) + active = true; + } + WARN(active != crtc->active, + "crtc's computed active state doesn't match tracked active state " + "(expected %i, found %i)\n", active, crtc->active); + WARN(enabled != crtc->base.enabled, + "crtc's computed enabled state doesn't match tracked enabled state " + "(expected %i, found %i)\n", enabled, crtc->base.enabled); + + assert_pipe(dev->dev_private, crtc->pipe, crtc->active); + } +} + bool intel_set_mode(struct drm_crtc *crtc, struct drm_display_mode *mode, int x, int y, struct drm_framebuffer *fb) @@ -6969,6 +7067,8 @@ done: if (!ret && crtc->enabled) { crtc->hwmode = saved_hwmode; crtc->mode = saved_mode; + } else { + intel_modeset_check_state(dev); } return ret; @@ -8147,6 +8247,8 @@ void intel_modeset_setup_hw_state(struct drm_device *dev) } intel_modeset_update_staged_output_state(dev); + + intel_modeset_check_state(dev); } void intel_modeset_gem_init(struct drm_device *dev) -- cgit v0.10.2 From b980514c9adf403e3f43ead08196f5ce0e61fd05 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Fri, 31 Aug 2012 17:37:33 +0200 Subject: drm/i915: improve modeset state checking after dpms calls Now that we have solid modeset state tracking and checking code in place, we can do the Full Monty also after dpms calls. Reviewed-by: Jesse Barnes Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/intel_crt.c b/drivers/gpu/drm/i915/intel_crt.c index cca266f..b9f08f6 100644 --- a/drivers/gpu/drm/i915/intel_crt.c +++ b/drivers/gpu/drm/i915/intel_crt.c @@ -177,7 +177,7 @@ static void intel_crt_dpms(struct drm_connector *connector, int mode) intel_crtc_update_dpms(crtc); } - intel_connector_check_state(to_intel_connector(connector)); + intel_modeset_check_state(connector->dev); } static int intel_crt_mode_valid(struct drm_connector *connector, diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index d15fa0c..44318bf 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -3585,7 +3585,7 @@ void intel_encoder_dpms(struct intel_encoder *encoder, int mode) /* Cross check the actual hw state with our own modeset state tracking (and it's * internal consistency). */ -void intel_connector_check_state(struct intel_connector *connector) +static void intel_connector_check_state(struct intel_connector *connector) { if (connector->get_hw_state(connector)) { struct intel_encoder *encoder = connector->encoder; @@ -3639,7 +3639,7 @@ void intel_connector_dpms(struct drm_connector *connector, int mode) else WARN_ON(encoder->connectors_active != false); - intel_connector_check_state(to_intel_connector(connector)); + intel_modeset_check_state(connector->dev); } /* Simple connector->get_hw_state implementation for encoders that support only @@ -6872,7 +6872,7 @@ intel_modeset_update_state(struct drm_device *dev, unsigned prepare_pipes) base.head) \ if (mask & (1 <<(intel_crtc)->pipe)) \ -static void +void intel_modeset_check_state(struct drm_device *dev) { struct intel_crtc *crtc; diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index 3b1c407..d391e67 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -1362,7 +1362,7 @@ intel_dp_dpms(struct drm_connector *connector, int mode) intel_encoder_dpms(&intel_dp->base, mode); } - intel_connector_check_state(to_intel_connector(connector)); + intel_modeset_check_state(connector->dev); } /* diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index 2061399..19e6928 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -455,7 +455,8 @@ extern void intel_encoder_dpms(struct intel_encoder *encoder, int mode); extern bool intel_encoder_check_is_cloned(struct intel_encoder *encoder); extern void intel_connector_dpms(struct drm_connector *, int mode); extern bool intel_connector_get_hw_state(struct intel_connector *connector); -extern void intel_connector_check_state(struct intel_connector *); +extern void intel_modeset_check_state(struct drm_device *dev); + static inline struct intel_encoder *intel_attached_encoder(struct drm_connector *connector) { diff --git a/drivers/gpu/drm/i915/intel_dvo.c b/drivers/gpu/drm/i915/intel_dvo.c index aa1bf57..4f1fdcc 100644 --- a/drivers/gpu/drm/i915/intel_dvo.c +++ b/drivers/gpu/drm/i915/intel_dvo.c @@ -189,7 +189,7 @@ static void intel_dvo_dpms(struct drm_connector *connector, int mode) intel_crtc_update_dpms(crtc); } - intel_connector_check_state(to_intel_connector(connector)); + intel_modeset_check_state(connector->dev); } static int intel_dvo_mode_valid(struct drm_connector *connector, diff --git a/drivers/gpu/drm/i915/intel_sdvo.c b/drivers/gpu/drm/i915/intel_sdvo.c index 6f53871..07d3921 100644 --- a/drivers/gpu/drm/i915/intel_sdvo.c +++ b/drivers/gpu/drm/i915/intel_sdvo.c @@ -1275,7 +1275,7 @@ static void intel_sdvo_dpms(struct drm_connector *connector, int mode) intel_sdvo_set_active_outputs(intel_sdvo, intel_sdvo->attached_output); } - intel_connector_check_state(to_intel_connector(connector)); + intel_modeset_check_state(connector->dev); } static int intel_sdvo_mode_valid(struct drm_connector *connector, -- cgit v0.10.2 From e04190e0ecb236c51af181c18c545ea076fb9cca Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Fri, 7 Sep 2012 10:14:52 +0200 Subject: drm/fb helper: don't call drm_helper_connector_dpms directly Yet again a case where the fb helper is too intimate with the crtc helper and calls a crtc helepr function directly instead of going through the interface vtable. This fixes console blanking in drm/i915 with the new i915-specific modeset code. Reported-by: Jesse Barnes Tested-by: Jesse Barnes Acked-by: Dave Airlie Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c index eb79515..b5d05f5 100644 --- a/drivers/gpu/drm/drm_fb_helper.c +++ b/drivers/gpu/drm/drm_fb_helper.c @@ -330,7 +330,7 @@ static void drm_fb_helper_dpms(struct fb_info *info, int dpms_mode) /* Walk the connectors & encoders on this fb turning them on/off */ for (j = 0; j < fb_helper->connector_count; j++) { connector = fb_helper->connector_info[j]->connector; - drm_helper_connector_dpms(connector, dpms_mode); + connector->funcs->dpms(connector, dpms_mode); drm_connector_property_set_value(connector, dev->mode_config.dpms_property, dpms_mode); } -- cgit v0.10.2 From fb30edf5e4b43745f33a1df5fec32721f63026c2 Mon Sep 17 00:00:00 2001 From: David Herrmann Date: Tue, 11 Sep 2012 18:35:11 +0000 Subject: drm: make buffer management work without DRM_MASTER DRM users should be able to create/destroy/manage dumb- and frame-buffers without DRM_MASTER. These ioctls do not affect modesetting so there is no reason to protect them by drm-master. Particularly, destroying buffers should always be possible as a client has only access to buffers that they created. Hence, there is no reason to prevent a client from destroying the buffers, considering a simple close() would destroy them, anyway. Furthermore, a display-server currently cannot shutdown correctly if it does not have DRM_MASTER. If some other display-server becomes active (or the kernel console), then the background display-server is unable to destroy its buffers. Under special curcumstances (like monitor reconfiguration) this might even happen during runtime. Signed-off-by: David Herrmann Signed-off-by: Dave Airlie diff --git a/drivers/gpu/drm/drm_drv.c b/drivers/gpu/drm/drm_drv.c index 9238de4..1490e76 100644 --- a/drivers/gpu/drm/drm_drv.c +++ b/drivers/gpu/drm/drm_drv.c @@ -156,14 +156,14 @@ static struct drm_ioctl_desc drm_ioctls[] = { DRM_IOCTL_DEF(DRM_IOCTL_MODE_SETPROPERTY, drm_mode_connector_property_set_ioctl, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED), DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETPROPBLOB, drm_mode_getblob_ioctl, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED), DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETFB, drm_mode_getfb, DRM_CONTROL_ALLOW|DRM_UNLOCKED), - DRM_IOCTL_DEF(DRM_IOCTL_MODE_ADDFB, drm_mode_addfb, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED), - DRM_IOCTL_DEF(DRM_IOCTL_MODE_ADDFB2, drm_mode_addfb2, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED), - DRM_IOCTL_DEF(DRM_IOCTL_MODE_RMFB, drm_mode_rmfb, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED), + DRM_IOCTL_DEF(DRM_IOCTL_MODE_ADDFB, drm_mode_addfb, DRM_CONTROL_ALLOW|DRM_UNLOCKED), + DRM_IOCTL_DEF(DRM_IOCTL_MODE_ADDFB2, drm_mode_addfb2, DRM_CONTROL_ALLOW|DRM_UNLOCKED), + DRM_IOCTL_DEF(DRM_IOCTL_MODE_RMFB, drm_mode_rmfb, DRM_CONTROL_ALLOW|DRM_UNLOCKED), DRM_IOCTL_DEF(DRM_IOCTL_MODE_PAGE_FLIP, drm_mode_page_flip_ioctl, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED), DRM_IOCTL_DEF(DRM_IOCTL_MODE_DIRTYFB, drm_mode_dirtyfb_ioctl, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED), - DRM_IOCTL_DEF(DRM_IOCTL_MODE_CREATE_DUMB, drm_mode_create_dumb_ioctl, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED), - DRM_IOCTL_DEF(DRM_IOCTL_MODE_MAP_DUMB, drm_mode_mmap_dumb_ioctl, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED), - DRM_IOCTL_DEF(DRM_IOCTL_MODE_DESTROY_DUMB, drm_mode_destroy_dumb_ioctl, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED), + DRM_IOCTL_DEF(DRM_IOCTL_MODE_CREATE_DUMB, drm_mode_create_dumb_ioctl, DRM_CONTROL_ALLOW|DRM_UNLOCKED), + DRM_IOCTL_DEF(DRM_IOCTL_MODE_MAP_DUMB, drm_mode_mmap_dumb_ioctl, DRM_CONTROL_ALLOW|DRM_UNLOCKED), + DRM_IOCTL_DEF(DRM_IOCTL_MODE_DESTROY_DUMB, drm_mode_destroy_dumb_ioctl, DRM_CONTROL_ALLOW|DRM_UNLOCKED), DRM_IOCTL_DEF(DRM_IOCTL_MODE_OBJ_GETPROPERTIES, drm_mode_obj_get_properties_ioctl, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED), DRM_IOCTL_DEF(DRM_IOCTL_MODE_OBJ_SETPROPERTY, drm_mode_obj_set_property_ioctl, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED), }; -- cgit v0.10.2 From 4a1897d268bcde8a8d0ba7d3d887d70e66b4d6f1 Mon Sep 17 00:00:00 2001 From: Russell King - ARM Linux Date: Fri, 10 Aug 2012 22:52:18 +0000 Subject: drm: consistently name interlaced modes At the moment, there is an inconsistency in the way modes are named. Modes with timings parsed from the EDID information will call drm_mode_set_name(), which will name the mode using this form: x eg, 1920x1080i for an interlaced mode, or 1920x1080 for a progressive mode. However, timings parsed using the tables in drm_edid_modes.h do not have the 'i' suffix. You are left to deduce that they're interlaced from xrandr's output by the lower vertical refresh frequencies. This patch changes the interlaced mode names in drm_edid_modes.h to follow the style set by drm_mode_set_name(), which makes it clear in xrandr which modes are interlaced and which are not (as xrandr groups the refresh rates on a line according to the name field.) Signed-off-by: Russell King Reviewed-by: Alex Deucher Reviewed-by: Adam Jackson Signed-off-by: Dave Airlie diff --git a/drivers/gpu/drm/drm_edid_modes.h b/drivers/gpu/drm/drm_edid_modes.h index ff98a7e..57459b3 100644 --- a/drivers/gpu/drm/drm_edid_modes.h +++ b/drivers/gpu/drm/drm_edid_modes.h @@ -89,7 +89,7 @@ static const struct drm_display_mode drm_dmt_modes[] = { 976, 1088, 0, 480, 486, 494, 517, 0, DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, /* 1024x768@43Hz, interlace */ - { DRM_MODE("1024x768", DRM_MODE_TYPE_DRIVER, 44900, 1024, 1032, + { DRM_MODE("1024x768i", DRM_MODE_TYPE_DRIVER, 44900, 1024, 1032, 1208, 1264, 0, 768, 768, 772, 817, 0, DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC | DRM_MODE_FLAG_INTERLACE) }, @@ -395,7 +395,7 @@ static const struct drm_display_mode edid_est_modes[] = { { DRM_MODE("1024x768", DRM_MODE_TYPE_DRIVER, 65000, 1024, 1048, 1184, 1344, 0, 768, 771, 777, 806, 0, DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, /* 1024x768@60Hz */ - { DRM_MODE("1024x768", DRM_MODE_TYPE_DRIVER,44900, 1024, 1032, + { DRM_MODE("1024x768i", DRM_MODE_TYPE_DRIVER,44900, 1024, 1032, 1208, 1264, 0, 768, 768, 776, 817, 0, DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC | DRM_MODE_FLAG_INTERLACE) }, /* 1024x768@43Hz */ { DRM_MODE("832x624", DRM_MODE_TYPE_DRIVER, 57284, 832, 864, @@ -506,17 +506,17 @@ static const struct drm_display_mode edid_cea_modes[] = { 1430, 1650, 0, 720, 725, 730, 750, 0, DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, /* 5 - 1920x1080i@60Hz */ - { DRM_MODE("1920x1080", DRM_MODE_TYPE_DRIVER, 74250, 1920, 2008, + { DRM_MODE("1920x1080i", DRM_MODE_TYPE_DRIVER, 74250, 1920, 2008, 2052, 2200, 0, 1080, 1084, 1094, 1125, 0, DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC | DRM_MODE_FLAG_INTERLACE) }, /* 6 - 1440x480i@60Hz */ - { DRM_MODE("1440x480", DRM_MODE_TYPE_DRIVER, 27000, 1440, 1478, + { DRM_MODE("1440x480i", DRM_MODE_TYPE_DRIVER, 27000, 1440, 1478, 1602, 1716, 0, 480, 488, 494, 525, 0, DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC | DRM_MODE_FLAG_INTERLACE | DRM_MODE_FLAG_DBLCLK) }, /* 7 - 1440x480i@60Hz */ - { DRM_MODE("1440x480", DRM_MODE_TYPE_DRIVER, 27000, 1440, 1478, + { DRM_MODE("1440x480i", DRM_MODE_TYPE_DRIVER, 27000, 1440, 1478, 1602, 1716, 0, 480, 488, 494, 525, 0, DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC | DRM_MODE_FLAG_INTERLACE | DRM_MODE_FLAG_DBLCLK) }, @@ -531,12 +531,12 @@ static const struct drm_display_mode edid_cea_modes[] = { DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC | DRM_MODE_FLAG_DBLCLK) }, /* 10 - 2880x480i@60Hz */ - { DRM_MODE("2880x480", DRM_MODE_TYPE_DRIVER, 54000, 2880, 2956, + { DRM_MODE("2880x480i", DRM_MODE_TYPE_DRIVER, 54000, 2880, 2956, 3204, 3432, 0, 480, 488, 494, 525, 0, DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC | DRM_MODE_FLAG_INTERLACE) }, /* 11 - 2880x480i@60Hz */ - { DRM_MODE("2880x480", DRM_MODE_TYPE_DRIVER, 54000, 2880, 2956, + { DRM_MODE("2880x480i", DRM_MODE_TYPE_DRIVER, 54000, 2880, 2956, 3204, 3432, 0, 480, 488, 494, 525, 0, DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC | DRM_MODE_FLAG_INTERLACE) }, @@ -573,17 +573,17 @@ static const struct drm_display_mode edid_cea_modes[] = { 1760, 1980, 0, 720, 725, 730, 750, 0, DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, /* 20 - 1920x1080i@50Hz */ - { DRM_MODE("1920x1080", DRM_MODE_TYPE_DRIVER, 74250, 1920, 2448, + { DRM_MODE("1920x1080i", DRM_MODE_TYPE_DRIVER, 74250, 1920, 2448, 2492, 2640, 0, 1080, 1084, 1094, 1125, 0, DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC | DRM_MODE_FLAG_INTERLACE) }, /* 21 - 1440x576i@50Hz */ - { DRM_MODE("1440x576", DRM_MODE_TYPE_DRIVER, 27000, 1440, 1464, + { DRM_MODE("1440x576i", DRM_MODE_TYPE_DRIVER, 27000, 1440, 1464, 1590, 1728, 0, 576, 580, 586, 625, 0, DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC | DRM_MODE_FLAG_INTERLACE | DRM_MODE_FLAG_DBLCLK) }, /* 22 - 1440x576i@50Hz */ - { DRM_MODE("1440x576", DRM_MODE_TYPE_DRIVER, 27000, 1440, 1464, + { DRM_MODE("1440x576i", DRM_MODE_TYPE_DRIVER, 27000, 1440, 1464, 1590, 1728, 0, 576, 580, 586, 625, 0, DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC | DRM_MODE_FLAG_INTERLACE | DRM_MODE_FLAG_DBLCLK) }, @@ -598,12 +598,12 @@ static const struct drm_display_mode edid_cea_modes[] = { DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC | DRM_MODE_FLAG_DBLCLK) }, /* 25 - 2880x576i@50Hz */ - { DRM_MODE("2880x576", DRM_MODE_TYPE_DRIVER, 54000, 2880, 2928, + { DRM_MODE("2880x576i", DRM_MODE_TYPE_DRIVER, 54000, 2880, 2928, 3180, 3456, 0, 576, 580, 586, 625, 0, DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC | DRM_MODE_FLAG_INTERLACE) }, /* 26 - 2880x576i@50Hz */ - { DRM_MODE("2880x576", DRM_MODE_TYPE_DRIVER, 54000, 2880, 2928, + { DRM_MODE("2880x576i", DRM_MODE_TYPE_DRIVER, 54000, 2880, 2928, 3180, 3456, 0, 576, 580, 586, 625, 0, DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC | DRM_MODE_FLAG_INTERLACE) }, @@ -656,12 +656,12 @@ static const struct drm_display_mode edid_cea_modes[] = { 3184, 3456, 0, 576, 581, 586, 625, 0, DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, /* 39 - 1920x1080i@50Hz */ - { DRM_MODE("1920x1080", DRM_MODE_TYPE_DRIVER, 72000, 1920, 1952, + { DRM_MODE("1920x1080i", DRM_MODE_TYPE_DRIVER, 72000, 1920, 1952, 2120, 2304, 0, 1080, 1126, 1136, 1250, 0, DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC | DRM_MODE_FLAG_INTERLACE) }, /* 40 - 1920x1080i@100Hz */ - { DRM_MODE("1920x1080", DRM_MODE_TYPE_DRIVER, 148500, 1920, 2448, + { DRM_MODE("1920x1080i", DRM_MODE_TYPE_DRIVER, 148500, 1920, 2448, 2492, 2640, 0, 1080, 1084, 1094, 1125, 0, DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC | DRM_MODE_FLAG_INTERLACE) }, @@ -688,7 +688,7 @@ static const struct drm_display_mode edid_cea_modes[] = { DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC | DRM_MODE_FLAG_DBLCLK) }, /* 46 - 1920x1080i@120Hz */ - { DRM_MODE("1920x1080", DRM_MODE_TYPE_DRIVER, 148500, 1920, 2008, + { DRM_MODE("1920x1080i", DRM_MODE_TYPE_DRIVER, 148500, 1920, 2008, 2052, 2200, 0, 1080, 1084, 1094, 1125, 0, DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC | DRM_MODE_FLAG_INTERLACE) }, @@ -705,12 +705,12 @@ static const struct drm_display_mode edid_cea_modes[] = { 798, 858, 0, 480, 489, 495, 525, 0, DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, /* 50 - 1440x480i@120Hz */ - { DRM_MODE("1440x480", DRM_MODE_TYPE_DRIVER, 54000, 1440, 1478, + { DRM_MODE("1440x480i", DRM_MODE_TYPE_DRIVER, 54000, 1440, 1478, 1602, 1716, 0, 480, 488, 494, 525, 0, DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC | DRM_MODE_FLAG_INTERLACE | DRM_MODE_FLAG_DBLCLK) }, /* 51 - 1440x480i@120Hz */ - { DRM_MODE("1440x480", DRM_MODE_TYPE_DRIVER, 54000, 1440, 1478, + { DRM_MODE("1440x480i", DRM_MODE_TYPE_DRIVER, 54000, 1440, 1478, 1602, 1716, 0, 480, 488, 494, 525, 0, DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC | DRM_MODE_FLAG_INTERLACE | DRM_MODE_FLAG_DBLCLK) }, @@ -723,12 +723,12 @@ static const struct drm_display_mode edid_cea_modes[] = { 796, 864, 0, 576, 581, 586, 625, 0, DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, /* 54 - 1440x576i@200Hz */ - { DRM_MODE("1440x576", DRM_MODE_TYPE_DRIVER, 108000, 1440, 1464, + { DRM_MODE("1440x576i", DRM_MODE_TYPE_DRIVER, 108000, 1440, 1464, 1590, 1728, 0, 576, 580, 586, 625, 0, DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC | DRM_MODE_FLAG_INTERLACE | DRM_MODE_FLAG_DBLCLK) }, /* 55 - 1440x576i@200Hz */ - { DRM_MODE("1440x576", DRM_MODE_TYPE_DRIVER, 108000, 1440, 1464, + { DRM_MODE("1440x576i", DRM_MODE_TYPE_DRIVER, 108000, 1440, 1464, 1590, 1728, 0, 576, 580, 586, 625, 0, DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC | DRM_MODE_FLAG_INTERLACE | DRM_MODE_FLAG_DBLCLK) }, @@ -741,12 +741,12 @@ static const struct drm_display_mode edid_cea_modes[] = { 798, 858, 0, 480, 489, 495, 525, 0, DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, /* 58 - 1440x480i@240 */ - { DRM_MODE("1440x480", DRM_MODE_TYPE_DRIVER, 108000, 1440, 1478, + { DRM_MODE("1440x480i", DRM_MODE_TYPE_DRIVER, 108000, 1440, 1478, 1602, 1716, 0, 480, 488, 494, 525, 0, DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC | DRM_MODE_FLAG_INTERLACE | DRM_MODE_FLAG_DBLCLK) }, /* 59 - 1440x480i@240 */ - { DRM_MODE("1440x480", DRM_MODE_TYPE_DRIVER, 108000, 1440, 1478, + { DRM_MODE("1440x480i", DRM_MODE_TYPE_DRIVER, 108000, 1440, 1478, 1602, 1716, 0, 480, 488, 494, 525, 0, DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC | DRM_MODE_FLAG_INTERLACE | DRM_MODE_FLAG_DBLCLK) }, -- cgit v0.10.2 From 9e50b9d55e9c38440175a0f27f77708e2270b140 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Thu, 16 Aug 2012 14:55:04 +0000 Subject: drm: edid: Add some bounds checking MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Make sure drm_detect_hdmi_monitor() and drm_detect_monitor_audio() don't access beyond the extension block. Signed-off-by: Ville Syrjälä Reviewed-by: Adam Jackson Signed-off-by: Dave Airlie diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c index bcc4725..7f39253 100644 --- a/drivers/gpu/drm/drm_edid.c +++ b/drivers/gpu/drm/drm_edid.c @@ -1517,16 +1517,57 @@ do_cea_modes (struct drm_connector *connector, u8 *db, u8 len) } static int +cea_db_payload_len(const u8 *db) +{ + return db[0] & 0x1f; +} + +static int +cea_db_tag(const u8 *db) +{ + return db[0] >> 5; +} + +static int +cea_revision(const u8 *cea) +{ + return cea[1]; +} + +static int +cea_db_offsets(const u8 *cea, int *start, int *end) +{ + /* Data block offset in CEA extension block */ + *start = 4; + *end = cea[2]; + if (*end == 0) + *end = 127; + if (*end < 4 || *end > 127) + return -ERANGE; + return 0; +} + +#define for_each_cea_db(cea, i, start, end) \ + for ((i) = (start); (i) < (end) && (i) + cea_db_payload_len(&(cea)[(i)]) < (end); (i) += cea_db_payload_len(&(cea)[(i)]) + 1) + +static int add_cea_modes(struct drm_connector *connector, struct edid *edid) { u8 * cea = drm_find_cea_extension(edid); u8 * db, dbl; int modes = 0; - if (cea && cea[1] >= 3) { - for (db = cea + 4; db < cea + cea[2]; db += dbl + 1) { - dbl = db[0] & 0x1f; - if (((db[0] & 0xe0) >> 5) == VIDEO_BLOCK) + if (cea && cea_revision(cea) >= 3) { + int i, start, end; + + if (cea_db_offsets(cea, &start, &end)) + return 0; + + for_each_cea_db(cea, i, start, end) { + db = &cea[i]; + dbl = cea_db_payload_len(db); + + if (cea_db_tag(db) == VIDEO_BLOCK) modes += do_cea_modes (connector, db+1, dbl); } } @@ -1617,19 +1658,29 @@ void drm_edid_to_eld(struct drm_connector *connector, struct edid *edid) eld[18] = edid->prod_code[0]; eld[19] = edid->prod_code[1]; - if (cea[1] >= 3) - for (db = cea + 4; db < cea + cea[2]; db += dbl + 1) { - dbl = db[0] & 0x1f; - - switch ((db[0] & 0xe0) >> 5) { + if (cea_revision(cea) >= 3) { + int i, start, end; + + if (cea_db_offsets(cea, &start, &end)) { + start = 0; + end = 0; + } + + for_each_cea_db(cea, i, start, end) { + db = &cea[i]; + dbl = cea_db_payload_len(db); + + switch (cea_db_tag(db)) { case AUDIO_BLOCK: /* Audio Data Block, contains SADs */ sad_count = dbl / 3; - memcpy(eld + 20 + mnl, &db[1], dbl); + if (dbl >= 1) + memcpy(eld + 20 + mnl, &db[1], dbl); break; case SPEAKER_BLOCK: - /* Speaker Allocation Data Block */ - eld[7] = db[1]; + /* Speaker Allocation Data Block */ + if (dbl >= 1) + eld[7] = db[1]; break; case VENDOR_BLOCK: /* HDMI Vendor-Specific Data Block */ @@ -1640,6 +1691,7 @@ void drm_edid_to_eld(struct drm_connector *connector, struct edid *edid) break; } } + } eld[5] |= sad_count << 4; eld[2] = (20 + mnl + sad_count * 3 + 3) / 4; @@ -1725,19 +1777,16 @@ bool drm_detect_hdmi_monitor(struct edid *edid) if (!edid_ext) goto end; - /* Data block offset in CEA extension block */ - start_offset = 4; - end_offset = edid_ext[2]; + if (cea_db_offsets(edid_ext, &start_offset, &end_offset)) + goto end; /* * Because HDMI identifier is in Vendor Specific Block, * search it from all data blocks of CEA extension. */ - for (i = start_offset; i < end_offset; - /* Increased by data block len */ - i += ((edid_ext[i] & 0x1f) + 1)) { + for_each_cea_db(edid_ext, i, start_offset, end_offset) { /* Find vendor specific block */ - if ((edid_ext[i] >> 5) == VENDOR_BLOCK) { + if (cea_db_tag(&edid_ext[i]) == VENDOR_BLOCK) { hdmi_id = edid_ext[i + 1] | (edid_ext[i + 2] << 8) | edid_ext[i + 3] << 16; /* Find HDMI identifier */ @@ -1780,15 +1829,13 @@ bool drm_detect_monitor_audio(struct edid *edid) goto end; } - /* Data block offset in CEA extension block */ - start_offset = 4; - end_offset = edid_ext[2]; + if (cea_db_offsets(edid_ext, &start_offset, &end_offset)) + goto end; - for (i = start_offset; i < end_offset; - i += ((edid_ext[i] & 0x1f) + 1)) { - if ((edid_ext[i] >> 5) == AUDIO_BLOCK) { + for_each_cea_db(edid_ext, i, start_offset, end_offset) { + if (cea_db_tag(&edid_ext[i]) == AUDIO_BLOCK) { has_audio = true; - for (j = 1; j < (edid_ext[i] & 0x1f); j += 3) + for (j = 1; j < cea_db_payload_len(&edid_ext[i]) + 1; j += 3) DRM_DEBUG_KMS("CEA audio format %d\n", (edid_ext[i + j] >> 3) & 0xf); goto end; -- cgit v0.10.2 From 8504072a2a47c80344c1cf81537d1d433a979fc6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Thu, 16 Aug 2012 14:55:05 +0000 Subject: drm: edid: Add bounds checking to HDMI VSDB parsing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The length of HDMI VSDB must be at least 5 bytes. Other than the minimum, nothing else about the length is specified. Check the length before accessing any additional field beyond the minimum length. Signed-off-by: Ville Syrjälä Reviewed-by: Adam Jackson Signed-off-by: Dave Airlie diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c index 7f39253..eadaf48 100644 --- a/drivers/gpu/drm/drm_edid.c +++ b/drivers/gpu/drm/drm_edid.c @@ -1576,19 +1576,28 @@ add_cea_modes(struct drm_connector *connector, struct edid *edid) } static void -parse_hdmi_vsdb(struct drm_connector *connector, uint8_t *db) +parse_hdmi_vsdb(struct drm_connector *connector, const u8 *db) { - connector->eld[5] |= (db[6] >> 7) << 1; /* Supports_AI */ + u8 len = cea_db_payload_len(db); - connector->dvi_dual = db[6] & 1; - connector->max_tmds_clock = db[7] * 5; - - connector->latency_present[0] = db[8] >> 7; - connector->latency_present[1] = (db[8] >> 6) & 1; - connector->video_latency[0] = db[9]; - connector->audio_latency[0] = db[10]; - connector->video_latency[1] = db[11]; - connector->audio_latency[1] = db[12]; + if (len >= 6) { + connector->eld[5] |= (db[6] >> 7) << 1; /* Supports_AI */ + connector->dvi_dual = db[6] & 1; + } + if (len >= 7) + connector->max_tmds_clock = db[7] * 5; + if (len >= 8) { + connector->latency_present[0] = db[8] >> 7; + connector->latency_present[1] = (db[8] >> 6) & 1; + } + if (len >= 9) + connector->video_latency[0] = db[9]; + if (len >= 10) + connector->audio_latency[0] = db[10]; + if (len >= 11) + connector->video_latency[1] = db[11]; + if (len >= 12) + connector->audio_latency[1] = db[12]; DRM_LOG_KMS("HDMI: DVI dual %d, " "max TMDS clock %d, " @@ -1684,7 +1693,7 @@ void drm_edid_to_eld(struct drm_connector *connector, struct edid *edid) break; case VENDOR_BLOCK: /* HDMI Vendor-Specific Data Block */ - if (db[1] == 0x03 && db[2] == 0x0c && db[3] == 0) + if (dbl >= 5 && db[1] == 0x03 && db[2] == 0x0c && db[3] == 0) parse_hdmi_vsdb(connector, db); break; default: -- cgit v0.10.2 From 14f77fdd58773245a0eae4243c039a420a52c820 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Thu, 16 Aug 2012 14:55:06 +0000 Subject: drm: edid: Refactor HDMI VSDB detection MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There are two slightly different pieces of code for HDMI VSDB detection. Unify the code into a single helper function. Also fix a bug where drm_detect_hdmi_monitor() would stop looking for the HDMI VSDB after the first vendor specific block is found, whether or not that block happened to be the HDMI VSDB. The standard allows for any number of vendor specific blocks to be present. Signed-off-by: Ville Syrjälä Reviewed-by: Adam Jackson Signed-off-by: Dave Airlie diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c index eadaf48..265a086 100644 --- a/drivers/gpu/drm/drm_edid.c +++ b/drivers/gpu/drm/drm_edid.c @@ -1621,6 +1621,21 @@ monitor_name(struct detailed_timing *t, void *data) *(u8 **)data = t->data.other_data.data.str.str; } +static bool cea_db_is_hdmi_vsdb(const u8 *db) +{ + int hdmi_id; + + if (cea_db_tag(db) != VENDOR_BLOCK) + return false; + + if (cea_db_payload_len(db) < 5) + return false; + + hdmi_id = db[1] | (db[2] << 8) | (db[3] << 16); + + return hdmi_id == HDMI_IDENTIFIER; +} + /** * drm_edid_to_eld - build ELD from EDID * @connector: connector corresponding to the HDMI/DP sink @@ -1693,7 +1708,7 @@ void drm_edid_to_eld(struct drm_connector *connector, struct edid *edid) break; case VENDOR_BLOCK: /* HDMI Vendor-Specific Data Block */ - if (dbl >= 5 && db[1] == 0x03 && db[2] == 0x0c && db[3] == 0) + if (cea_db_is_hdmi_vsdb(db)) parse_hdmi_vsdb(connector, db); break; default: @@ -1778,35 +1793,26 @@ EXPORT_SYMBOL(drm_select_eld); bool drm_detect_hdmi_monitor(struct edid *edid) { u8 *edid_ext; - int i, hdmi_id; + int i; int start_offset, end_offset; - bool is_hdmi = false; edid_ext = drm_find_cea_extension(edid); if (!edid_ext) - goto end; + return false; if (cea_db_offsets(edid_ext, &start_offset, &end_offset)) - goto end; + return false; /* * Because HDMI identifier is in Vendor Specific Block, * search it from all data blocks of CEA extension. */ for_each_cea_db(edid_ext, i, start_offset, end_offset) { - /* Find vendor specific block */ - if (cea_db_tag(&edid_ext[i]) == VENDOR_BLOCK) { - hdmi_id = edid_ext[i + 1] | (edid_ext[i + 2] << 8) | - edid_ext[i + 3] << 16; - /* Find HDMI identifier */ - if (hdmi_id == HDMI_IDENTIFIER) - is_hdmi = true; - break; - } + if (cea_db_is_hdmi_vsdb(&edid_ext[i])) + return true; } -end: - return is_hdmi; + return false; } EXPORT_SYMBOL(drm_detect_hdmi_monitor); -- cgit v0.10.2 From 9a6a4b4757175271bc3a289fcb5c69e897039379 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Wed, 5 Sep 2012 12:04:19 +0000 Subject: drm: use %*ph to dump small buffers Signed-off-by: Andy Shevchenko Signed-off-by: Dave Airlie diff --git a/drivers/gpu/drm/nouveau/nouveau_dp.c b/drivers/gpu/drm/nouveau/nouveau_dp.c index 7e289d2..e754aa3 100644 --- a/drivers/gpu/drm/nouveau/nouveau_dp.c +++ b/drivers/gpu/drm/nouveau/nouveau_dp.c @@ -289,9 +289,7 @@ dp_link_train_update(struct drm_device *dev, struct dp_state *dp, u32 delay) if (ret) return ret; - NV_DEBUG_KMS(dev, "status %02x %02x %02x %02x %02x %02x\n", - dp->stat[0], dp->stat[1], dp->stat[2], dp->stat[3], - dp->stat[4], dp->stat[5]); + NV_DEBUG_KMS(dev, "status %*ph\n", 6, dp->stat); return 0; } diff --git a/drivers/gpu/drm/radeon/atombios_dp.c b/drivers/gpu/drm/radeon/atombios_dp.c index 7712cf5..8a74e1b 100644 --- a/drivers/gpu/drm/radeon/atombios_dp.c +++ b/drivers/gpu/drm/radeon/atombios_dp.c @@ -658,9 +658,7 @@ static bool radeon_dp_get_link_status(struct radeon_connector *radeon_connector, return false; } - DRM_DEBUG_KMS("link status %02x %02x %02x %02x %02x %02x\n", - link_status[0], link_status[1], link_status[2], - link_status[3], link_status[4], link_status[5]); + DRM_DEBUG_KMS("link status %*ph\n", 6, link_status); return true; } diff --git a/drivers/gpu/drm/udl/udl_main.c b/drivers/gpu/drm/udl/udl_main.c index 4c2d836..40bf468 100644 --- a/drivers/gpu/drm/udl/udl_main.c +++ b/drivers/gpu/drm/udl/udl_main.c @@ -41,11 +41,8 @@ static int udl_parse_vendor_descriptor(struct drm_device *dev, total_len = usb_get_descriptor(usbdev, 0x5f, /* vendor specific */ 0, desc, MAX_VENDOR_DESCRIPTOR_SIZE); if (total_len > 5) { - DRM_INFO("vendor descriptor length:%x data:%02x %02x %02x %02x" \ - "%02x %02x %02x %02x %02x %02x %02x\n", - total_len, desc[0], - desc[1], desc[2], desc[3], desc[4], desc[5], desc[6], - desc[7], desc[8], desc[9], desc[10]); + DRM_INFO("vendor descriptor length:%x data:%*ph\n", + total_len, 11, desc); if ((desc[0] != total_len) || /* descriptor length */ (desc[1] != 0x5f) || /* vendor descriptor type */ -- cgit v0.10.2 From 93eb58d53fb14e9eeb1b42f8c04dbd7afc09adf7 Mon Sep 17 00:00:00 2001 From: "Robert P. J. Day" Date: Thu, 30 Aug 2012 11:23:46 +0000 Subject: drm: Remove unnecessary test for ARM. Since arch/arm/include/asm/pgtable.h contains: #define io_remap_pfn_range(vma,from,pfn,size,prot) \ remap_pfn_range(vma, from, pfn, size, prot) there is no point treating ARM as a special case in distinguishing between remap_pfn_range() and io_remap_pfn_range(). Signed-off-by: Robert P. J. Day Signed-off-by: Dave Airlie diff --git a/drivers/gpu/drm/drm_vm.c b/drivers/gpu/drm/drm_vm.c index 3f06166..6fed215 100644 --- a/drivers/gpu/drm/drm_vm.c +++ b/drivers/gpu/drm/drm_vm.c @@ -619,20 +619,11 @@ int drm_mmap_locked(struct file *filp, struct vm_area_struct *vma) offset = drm_core_get_reg_ofs(dev); vma->vm_flags |= VM_IO; /* not in core dump */ vma->vm_page_prot = drm_io_prot(map->type, vma); -#if !defined(__arm__) if (io_remap_pfn_range(vma, vma->vm_start, (map->offset + offset) >> PAGE_SHIFT, vma->vm_end - vma->vm_start, vma->vm_page_prot)) return -EAGAIN; -#else - if (remap_pfn_range(vma, vma->vm_start, - (map->offset + offset) >> PAGE_SHIFT, - vma->vm_end - vma->vm_start, - vma->vm_page_prot)) - return -EAGAIN; -#endif - DRM_DEBUG(" Type = %d; start = 0x%lx, end = 0x%lx," " offset = 0x%llx\n", map->type, -- cgit v0.10.2 From cd004b3f4cd4169815c82bf9e424fda06978898a Mon Sep 17 00:00:00 2001 From: Shirish S Date: Thu, 30 Aug 2012 07:04:06 +0000 Subject: drm: edid: add support for E-DDC The current logic for probing ddc is limited to 2 blocks (256 bytes), this patch adds support for the 4 block (512) data. To do this, a single 8-bit segment index is passed to the display via the I2C address 30h. Data from the selected segment is then immediately read via the regular DDC2 address using a repeated I2C 'START' signal. Signed-off-by: Shirish S Reviewed-by: Jean Delvare Reviewed-by: Daniel Vetter Reviewed-by: Ville Syrjala Signed-off-by: Dave Airlie diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c index 265a086..acb489f 100644 --- a/drivers/gpu/drm/drm_edid.c +++ b/drivers/gpu/drm/drm_edid.c @@ -254,6 +254,8 @@ drm_do_probe_ddc_edid(struct i2c_adapter *adapter, unsigned char *buf, int block, int len) { unsigned char start = block * EDID_LENGTH; + unsigned char segment = block >> 1; + unsigned char xfers = segment ? 3 : 2; int ret, retries = 5; /* The core i2c driver will automatically retry the transfer if the @@ -265,6 +267,11 @@ drm_do_probe_ddc_edid(struct i2c_adapter *adapter, unsigned char *buf, do { struct i2c_msg msgs[] = { { + .addr = DDC_SEGMENT_ADDR, + .flags = 0, + .len = 1, + .buf = &segment, + }, { .addr = DDC_ADDR, .flags = 0, .len = 1, @@ -276,15 +283,21 @@ drm_do_probe_ddc_edid(struct i2c_adapter *adapter, unsigned char *buf, .buf = buf, } }; - ret = i2c_transfer(adapter, msgs, 2); + + /* + * Avoid sending the segment addr to not upset non-compliant ddc + * monitors. + */ + ret = i2c_transfer(adapter, &msgs[3 - xfers], xfers); + if (ret == -ENXIO) { DRM_DEBUG_KMS("drm: skipping non-existent adapter %s\n", adapter->name); break; } - } while (ret != 2 && --retries); + } while (ret != xfers && --retries); - return ret == 2 ? 0 : -1; + return ret == xfers ? 0 : -1; } static bool drm_edid_is_zero(u8 *in_edid, int length) -- cgit v0.10.2 From 3a9950781cccc0a2c219c9369e585945f1de1c0f Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Mon, 27 Aug 2012 07:07:43 +0000 Subject: vmwgfx: remove useless set memory to zero use memset() The memory return by kzalloc() or kmem_cache_zalloc() has already be set to zero, so remove useless memset(0). Signed-off-by: Wei Yongjun Signed-off-by: Dave Airlie diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c index 4d9edea..3e47f75 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c @@ -437,7 +437,6 @@ static int vmw_driver_load(struct drm_device *dev, unsigned long chipset) DRM_ERROR("Failed allocating a device private struct.\n"); return -ENOMEM; } - memset(dev_priv, 0, sizeof(*dev_priv)); pci_set_master(dev->pdev); diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c index c50724b..5474394 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c @@ -483,7 +483,6 @@ static int do_surface_dirty_sou(struct vmw_private *dev_priv, } /* only need to do this once */ - memset(cmd, 0, fifo_size); cmd->header.id = cpu_to_le32(SVGA_3D_CMD_BLIT_SURFACE_TO_SCREEN); cmd->header.size = cpu_to_le32(fifo_size - sizeof(cmd->header)); -- cgit v0.10.2 From 09e7dcf081b1100d1cdff57fa9eb25c3a834c9d6 Mon Sep 17 00:00:00 2001 From: Emil Goode Date: Wed, 5 Sep 2012 23:51:47 +0200 Subject: gma500: Remove unused variable MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch removes a unused struct psb_intel_connector Sparse gives a warning: drivers/gpu/drm/gma500/cdv_intel_hdmi.c:142:30: warning: unused variable ‘psb_intel_connector’ [-Wunused-variable] Signed-off-by: Emil Goode Signed-off-by: Dave Airlie diff --git a/drivers/gpu/drm/gma500/cdv_intel_hdmi.c b/drivers/gpu/drm/gma500/cdv_intel_hdmi.c index b1b77bb..7272a46 100644 --- a/drivers/gpu/drm/gma500/cdv_intel_hdmi.c +++ b/drivers/gpu/drm/gma500/cdv_intel_hdmi.c @@ -139,8 +139,6 @@ static enum drm_connector_status cdv_hdmi_detect( { struct psb_intel_encoder *psb_intel_encoder = psb_intel_attached_encoder(connector); - struct psb_intel_connector *psb_intel_connector = - to_psb_intel_connector(connector); struct mid_intel_hdmi_priv *hdmi_priv = psb_intel_encoder->dev_priv; struct edid *edid = NULL; enum drm_connector_status status = connector_status_disconnected; -- cgit v0.10.2 From 0b2443ed4e07d7973e4554a2cc166bc35447b59e Mon Sep 17 00:00:00 2001 From: Jerome Glisse Date: Thu, 9 Aug 2012 11:25:51 -0400 Subject: drm/edid: limit printk when facing bad edid Limit printing bad edid information at one time per connector. Connector that are connected to a bad monitor/kvm will likely stay connected to the same bad monitor/kvm and it makes no sense to keep printing the bad edid message. Signed-off-by: Jerome Glisse Reviewed-by: Adam Jackson Signed-off-by: Dave Airlie diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c index acb489f..de2a0ed 100644 --- a/drivers/gpu/drm/drm_edid.c +++ b/drivers/gpu/drm/drm_edid.c @@ -158,7 +158,7 @@ MODULE_PARM_DESC(edid_fixup, * Sanity check the EDID block (base or extension). Return 0 if the block * doesn't check out, or 1 if it's valid. */ -bool drm_edid_block_valid(u8 *raw_edid, int block) +bool drm_edid_block_valid(u8 *raw_edid, int block, bool print_bad_edid) { int i; u8 csum = 0; @@ -181,7 +181,9 @@ bool drm_edid_block_valid(u8 *raw_edid, int block) for (i = 0; i < EDID_LENGTH; i++) csum += raw_edid[i]; if (csum) { - DRM_ERROR("EDID checksum is invalid, remainder is %d\n", csum); + if (print_bad_edid) { + DRM_ERROR("EDID checksum is invalid, remainder is %d\n", csum); + } /* allow CEA to slide through, switches mangle this */ if (raw_edid[0] != 0x02) @@ -207,7 +209,7 @@ bool drm_edid_block_valid(u8 *raw_edid, int block) return 1; bad: - if (raw_edid) { + if (raw_edid && print_bad_edid) { printk(KERN_ERR "Raw EDID:\n"); print_hex_dump(KERN_ERR, " \t", DUMP_PREFIX_NONE, 16, 1, raw_edid, EDID_LENGTH, false); @@ -231,7 +233,7 @@ bool drm_edid_is_valid(struct edid *edid) return false; for (i = 0; i <= edid->extensions; i++) - if (!drm_edid_block_valid(raw + i * EDID_LENGTH, i)) + if (!drm_edid_block_valid(raw + i * EDID_LENGTH, i, true)) return false; return true; @@ -316,6 +318,7 @@ drm_do_get_edid(struct drm_connector *connector, struct i2c_adapter *adapter) { int i, j = 0, valid_extensions = 0; u8 *block, *new; + bool print_bad_edid = !connector->bad_edid_counter || (drm_debug & DRM_UT_KMS); if ((block = kmalloc(EDID_LENGTH, GFP_KERNEL)) == NULL) return NULL; @@ -324,7 +327,7 @@ drm_do_get_edid(struct drm_connector *connector, struct i2c_adapter *adapter) for (i = 0; i < 4; i++) { if (drm_do_probe_ddc_edid(adapter, block, 0, EDID_LENGTH)) goto out; - if (drm_edid_block_valid(block, 0)) + if (drm_edid_block_valid(block, 0, print_bad_edid)) break; if (i == 0 && drm_edid_is_zero(block, EDID_LENGTH)) { connector->null_edid_counter++; @@ -349,7 +352,7 @@ drm_do_get_edid(struct drm_connector *connector, struct i2c_adapter *adapter) block + (valid_extensions + 1) * EDID_LENGTH, j, EDID_LENGTH)) goto out; - if (drm_edid_block_valid(block + (valid_extensions + 1) * EDID_LENGTH, j)) { + if (drm_edid_block_valid(block + (valid_extensions + 1) * EDID_LENGTH, j, print_bad_edid)) { valid_extensions++; break; } @@ -372,8 +375,11 @@ drm_do_get_edid(struct drm_connector *connector, struct i2c_adapter *adapter) return block; carp: - dev_warn(connector->dev->dev, "%s: EDID block %d invalid.\n", - drm_get_connector_name(connector), j); + if (print_bad_edid) { + dev_warn(connector->dev->dev, "%s: EDID block %d invalid.\n", + drm_get_connector_name(connector), j); + } + connector->bad_edid_counter++; out: kfree(block); diff --git a/drivers/gpu/drm/drm_edid_load.c b/drivers/gpu/drm/drm_edid_load.c index 186832e..ea9cdab 100644 --- a/drivers/gpu/drm/drm_edid_load.c +++ b/drivers/gpu/drm/drm_edid_load.c @@ -123,6 +123,7 @@ static u8 *edid_load(struct drm_connector *connector, char *name, int fwsize, expected; int builtin = 0, err = 0; int i, valid_extensions = 0; + bool print_bad_edid = !connector->bad_edid_counter || (drm_debug & DRM_UT_KMS); pdev = platform_device_register_simple(connector_name, -1, NULL, 0); if (IS_ERR(pdev)) { @@ -173,7 +174,8 @@ static u8 *edid_load(struct drm_connector *connector, char *name, } memcpy(edid, fwdata, fwsize); - if (!drm_edid_block_valid(edid, 0)) { + if (!drm_edid_block_valid(edid, 0, print_bad_edid)) { + connector->bad_edid_counter++; DRM_ERROR("Base block of EDID firmware \"%s\" is invalid ", name); kfree(edid); @@ -185,7 +187,7 @@ static u8 *edid_load(struct drm_connector *connector, char *name, if (i != valid_extensions + 1) memcpy(edid + (valid_extensions + 1) * EDID_LENGTH, edid + i * EDID_LENGTH, EDID_LENGTH); - if (drm_edid_block_valid(edid + i * EDID_LENGTH, i)) + if (drm_edid_block_valid(edid + i * EDID_LENGTH, i, print_bad_edid)) valid_extensions++; } diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h index 617d87a..316ce64 100644 --- a/include/drm/drm_crtc.h +++ b/include/drm/drm_crtc.h @@ -590,6 +590,7 @@ struct drm_connector { int video_latency[2]; /* [0]: progressive, [1]: interlaced */ int audio_latency[2]; int null_edid_counter; /* needed to workaround some HW bugs where we get all 0s */ + unsigned bad_edid_counter; }; /** @@ -1032,7 +1033,7 @@ extern int drm_add_modes_noedid(struct drm_connector *connector, int hdisplay, int vdisplay); extern int drm_edid_header_is_valid(const u8 *raw_edid); -extern bool drm_edid_block_valid(u8 *raw_edid, int block); +extern bool drm_edid_block_valid(u8 *raw_edid, int block, bool print_bad_edid); extern bool drm_edid_is_valid(struct edid *edid); struct drm_display_mode *drm_mode_find_dmt(struct drm_device *dev, int hsize, int vsize, int fresh, -- cgit v0.10.2 From 6c4c86f51c985a146ff7185abff102fc535f6fa8 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Mon, 10 Sep 2012 21:58:30 +0200 Subject: drm/i915: correctly update crtc->x/y in set_base While reworking the modeset sequence, this got lost in commit 25c5b2665fe4cc5a93edd29b62e7c05c15dddd26 Author: Daniel Vetter Date: Sun Jul 8 22:08:04 2012 +0200 drm/i915: implement new set_mode code flow I've noticed this because some Xorg versions seem to set up a new mode with every crtc at (0,0) and then pan to the right multi-monitor setup. And since some hacks of mine added more calls to mode_set using the stored crtc->x/y my multi-screen setup blew up. Reviewed-by: Jesse Barnes Signed-Off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index e061acd..5576b7c 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -2246,6 +2246,8 @@ intel_pipe_set_base(struct drm_crtc *crtc, int x, int y, old_fb = crtc->fb; crtc->fb = fb; + crtc->x = x; + crtc->y = y; if (old_fb) { intel_wait_for_vblank(dev, intel_crtc->pipe); @@ -7020,11 +7022,11 @@ bool intel_set_mode(struct drm_crtc *crtc, dev_priv->display.crtc_disable(&intel_crtc->base); } - if (modeset_pipes) { + /* crtc->mode is already used by the ->mode_set callbacks, hence we need + * to set it here already despite that we pass it down the callchain. + */ + if (modeset_pipes) crtc->mode = *mode; - crtc->x = x; - crtc->y = y; - } /* Only after disabling all output pipelines that will be changed can we * update the the output configuration. */ -- cgit v0.10.2 From 3b7a89fce3e3dc96b549d6d829387b4439044d0d Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Mon, 17 Sep 2012 22:27:21 +0200 Subject: drm/i915: fix OOPS in lid_notify This goes back to commit c1c7af60892070e4b82ad63bbfb95ae745056de0 Author: Jesse Barnes Date: Thu Sep 10 15:28:03 2009 -0700 drm/i915: force mode set at lid open time It was used to fix an issue on a i915GM based Thinkpad X41, which somehow clobbered the modeset state at lid close time. Since then massive amounts of things changed: Tons of fixes to the modeset sequence, OpRegion support, better integration with the acpi code. Especially OpRegion /should/ allow us to control the display hw cooperatively with the firmware, without the firmware clobbering the hw state behind our backs. So it's dubious whether we still need this. The second issue is that it's unclear who's responsibility it actually is to restore the mode - Chris Wilson suggests to just emit a hotplug event and let userspace figure things out. The real reason I've stumbled over this is that the new modeset code breaks drm_helper_resume_force_mode - it OOPSes derefing a NULL vfunc pointer. The reason this wasn't caught in testing earlier is that in commit c9354c85c1c7bac788ce57d3c17f2016c1c45b1d Author: Linus Torvalds Date: Mon Nov 2 09:29:55 2009 -0800 i915: fix intel graphics suspend breakage due to resume/lid event confusion logic was added to _not_ restore the modeset state after a resume. And since most machines are configured to auto-suspend on lid-close, this neatly papered over the issue. Summarizing, this shouldn't be required on any platform supporting OpRegion. And none of the really old machines I have here seem to require it either. Hence I'm inclined to just rip it out. But in case that there are really firmwares out there that clobber the hw state, replace it with a call to intel_modset_check_state. This will ensure that we catch any issues as soon as they happen. Cc: Chris Wilson Acked-by: Jesse Barnes Signed-off-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/intel_lvds.c b/drivers/gpu/drm/i915/intel_lvds.c index 5646895..dc9f9ff 100644 --- a/drivers/gpu/drm/i915/intel_lvds.c +++ b/drivers/gpu/drm/i915/intel_lvds.c @@ -527,7 +527,7 @@ static int intel_lid_notify(struct notifier_block *nb, unsigned long val, dev_priv->modeset_on_lid = 0; mutex_lock(&dev->mode_config.mutex); - drm_helper_resume_force_mode(dev); + intel_modeset_check_state(dev); mutex_unlock(&dev->mode_config.mutex); return NOTIFY_OK; -- cgit v0.10.2 From b9d474500546160dd6af35f60cd8bc20edd13807 Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Wed, 27 Jun 2012 15:30:18 +0200 Subject: DRM: Add DRM GEM CMA helper Many embedded drm devices do not have a IOMMU and no dedicated memory for graphics. These devices use CMA (Contiguous Memory Allocator) backed graphics memory. This patch provides helper functions to be able to share the code. The code technically does not depend on CMA as the backend allocator, the name has been chosen because CMA makes for a nice, short but still descriptive function prefix. Signed-off-by: Sascha Hauer Tested-by: Lars-Peter Clausen [Make DRM_GEM_CMA_HELPER a boolean Kconfig option] Signed-off-by: Laurent Pinchart diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig index 0cbdc45..2df146d 100644 --- a/drivers/gpu/drm/Kconfig +++ b/drivers/gpu/drm/Kconfig @@ -54,6 +54,12 @@ config DRM_TTM GPU memory types. Will be enabled automatically if a device driver uses it. +config DRM_GEM_CMA_HELPER + bool + depends on DRM + help + Choose this if you need the GEM CMA helper functions + config DRM_TDFX tristate "3dfx Banshee/Voodoo3+" depends on DRM && PCI diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile index f65f65e..2fcedbb 100644 --- a/drivers/gpu/drm/Makefile +++ b/drivers/gpu/drm/Makefile @@ -15,6 +15,7 @@ drm-y := drm_auth.o drm_buffer.o drm_bufs.o drm_cache.o \ drm_trace_points.o drm_global.o drm_prime.o drm-$(CONFIG_COMPAT) += drm_ioc32.o +drm-$(CONFIG_DRM_GEM_CMA_HELPER) += drm_gem_cma_helper.o drm-usb-y := drm_usb.o diff --git a/drivers/gpu/drm/drm_gem_cma_helper.c b/drivers/gpu/drm/drm_gem_cma_helper.c new file mode 100644 index 0000000..1aa8fee --- /dev/null +++ b/drivers/gpu/drm/drm_gem_cma_helper.c @@ -0,0 +1,251 @@ +/* + * drm gem CMA (contiguous memory allocator) helper functions + * + * Copyright (C) 2012 Sascha Hauer, Pengutronix + * + * Based on Samsung Exynos code + * + * Copyright (c) 2011 Samsung Electronics Co., Ltd. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include + +#include +#include +#include + +static unsigned int get_gem_mmap_offset(struct drm_gem_object *obj) +{ + return (unsigned int)obj->map_list.hash.key << PAGE_SHIFT; +} + +static void drm_gem_cma_buf_destroy(struct drm_device *drm, + struct drm_gem_cma_object *cma_obj) +{ + dma_free_writecombine(drm->dev, cma_obj->base.size, cma_obj->vaddr, + cma_obj->paddr); +} + +/* + * drm_gem_cma_create - allocate an object with the given size + * + * returns a struct drm_gem_cma_object* on success or ERR_PTR values + * on failure. + */ +struct drm_gem_cma_object *drm_gem_cma_create(struct drm_device *drm, + unsigned int size) +{ + struct drm_gem_cma_object *cma_obj; + struct drm_gem_object *gem_obj; + int ret; + + size = round_up(size, PAGE_SIZE); + + cma_obj = kzalloc(sizeof(*cma_obj), GFP_KERNEL); + if (!cma_obj) + return ERR_PTR(-ENOMEM); + + cma_obj->vaddr = dma_alloc_writecombine(drm->dev, size, + &cma_obj->paddr, GFP_KERNEL | __GFP_NOWARN); + if (!cma_obj->vaddr) { + dev_err(drm->dev, "failed to allocate buffer with size %d\n", size); + ret = -ENOMEM; + goto err_dma_alloc; + } + + gem_obj = &cma_obj->base; + + ret = drm_gem_object_init(drm, gem_obj, size); + if (ret) + goto err_obj_init; + + ret = drm_gem_create_mmap_offset(gem_obj); + if (ret) + goto err_create_mmap_offset; + + return cma_obj; + +err_create_mmap_offset: + drm_gem_object_release(gem_obj); + +err_obj_init: + drm_gem_cma_buf_destroy(drm, cma_obj); + +err_dma_alloc: + kfree(cma_obj); + + return ERR_PTR(ret); +} +EXPORT_SYMBOL_GPL(drm_gem_cma_create); + +/* + * drm_gem_cma_create_with_handle - allocate an object with the given + * size and create a gem handle on it + * + * returns a struct drm_gem_cma_object* on success or ERR_PTR values + * on failure. + */ +static struct drm_gem_cma_object *drm_gem_cma_create_with_handle( + struct drm_file *file_priv, + struct drm_device *drm, unsigned int size, + unsigned int *handle) +{ + struct drm_gem_cma_object *cma_obj; + struct drm_gem_object *gem_obj; + int ret; + + cma_obj = drm_gem_cma_create(drm, size); + if (IS_ERR(cma_obj)) + return cma_obj; + + gem_obj = &cma_obj->base; + + /* + * allocate a id of idr table where the obj is registered + * and handle has the id what user can see. + */ + ret = drm_gem_handle_create(file_priv, gem_obj, handle); + if (ret) + goto err_handle_create; + + /* drop reference from allocate - handle holds it now. */ + drm_gem_object_unreference_unlocked(gem_obj); + + return cma_obj; + +err_handle_create: + drm_gem_cma_free_object(gem_obj); + + return ERR_PTR(ret); +} + +/* + * drm_gem_cma_free_object - (struct drm_driver)->gem_free_object callback + * function + */ +void drm_gem_cma_free_object(struct drm_gem_object *gem_obj) +{ + struct drm_gem_cma_object *cma_obj; + + if (gem_obj->map_list.map) + drm_gem_free_mmap_offset(gem_obj); + + drm_gem_object_release(gem_obj); + + cma_obj = to_drm_gem_cma_obj(gem_obj); + + drm_gem_cma_buf_destroy(gem_obj->dev, cma_obj); + + kfree(cma_obj); +} +EXPORT_SYMBOL_GPL(drm_gem_cma_free_object); + +/* + * drm_gem_cma_dumb_create - (struct drm_driver)->dumb_create callback + * function + * + * This aligns the pitch and size arguments to the minimum required. wrap + * this into your own function if you need bigger alignment. + */ +int drm_gem_cma_dumb_create(struct drm_file *file_priv, + struct drm_device *dev, struct drm_mode_create_dumb *args) +{ + struct drm_gem_cma_object *cma_obj; + int min_pitch = DIV_ROUND_UP(args->width * args->bpp, 8); + + if (args->pitch < min_pitch) + args->pitch = min_pitch; + + if (args->size < args->pitch * args->height) + args->size = args->pitch * args->height; + + cma_obj = drm_gem_cma_create_with_handle(file_priv, dev, + args->size, &args->handle); + if (IS_ERR(cma_obj)) + return PTR_ERR(cma_obj); + + return 0; +} +EXPORT_SYMBOL_GPL(drm_gem_cma_dumb_create); + +/* + * drm_gem_cma_dumb_map_offset - (struct drm_driver)->dumb_map_offset callback + * function + */ +int drm_gem_cma_dumb_map_offset(struct drm_file *file_priv, + struct drm_device *drm, uint32_t handle, uint64_t *offset) +{ + struct drm_gem_object *gem_obj; + + mutex_lock(&drm->struct_mutex); + + gem_obj = drm_gem_object_lookup(drm, file_priv, handle); + if (!gem_obj) { + dev_err(drm->dev, "failed to lookup gem object\n"); + mutex_unlock(&drm->struct_mutex); + return -EINVAL; + } + + *offset = get_gem_mmap_offset(gem_obj); + + drm_gem_object_unreference(gem_obj); + + mutex_unlock(&drm->struct_mutex); + + return 0; +} +EXPORT_SYMBOL_GPL(drm_gem_cma_dumb_map_offset); + +const struct vm_operations_struct drm_gem_cma_vm_ops = { + .open = drm_gem_vm_open, + .close = drm_gem_vm_close, +}; +EXPORT_SYMBOL_GPL(drm_gem_cma_vm_ops); + +/* + * drm_gem_cma_mmap - (struct file_operation)->mmap callback function + */ +int drm_gem_cma_mmap(struct file *filp, struct vm_area_struct *vma) +{ + struct drm_gem_object *gem_obj; + struct drm_gem_cma_object *cma_obj; + int ret; + + ret = drm_gem_mmap(filp, vma); + if (ret) + return ret; + + gem_obj = vma->vm_private_data; + cma_obj = to_drm_gem_cma_obj(gem_obj); + + ret = remap_pfn_range(vma, vma->vm_start, cma_obj->paddr >> PAGE_SHIFT, + vma->vm_end - vma->vm_start, vma->vm_page_prot); + if (ret) + drm_gem_vm_close(vma); + + return ret; +} +EXPORT_SYMBOL_GPL(drm_gem_cma_mmap); + +/* + * drm_gem_cma_dumb_destroy - (struct drm_driver)->dumb_destroy callback function + */ +int drm_gem_cma_dumb_destroy(struct drm_file *file_priv, + struct drm_device *drm, unsigned int handle) +{ + return drm_gem_handle_delete(file_priv, handle); +} +EXPORT_SYMBOL_GPL(drm_gem_cma_dumb_destroy); diff --git a/include/drm/drm_gem_cma_helper.h b/include/drm/drm_gem_cma_helper.h new file mode 100644 index 0000000..f0f6b1a --- /dev/null +++ b/include/drm/drm_gem_cma_helper.h @@ -0,0 +1,44 @@ +#ifndef __DRM_GEM_CMA_HELPER_H__ +#define __DRM_GEM_CMA_HELPER_H__ + +struct drm_gem_cma_object { + struct drm_gem_object base; + dma_addr_t paddr; + void *vaddr; +}; + +static inline struct drm_gem_cma_object * +to_drm_gem_cma_obj(struct drm_gem_object *gem_obj) +{ + return container_of(gem_obj, struct drm_gem_cma_object, base); +} + +/* free gem object. */ +void drm_gem_cma_free_object(struct drm_gem_object *gem_obj); + +/* create memory region for drm framebuffer. */ +int drm_gem_cma_dumb_create(struct drm_file *file_priv, + struct drm_device *drm, struct drm_mode_create_dumb *args); + +/* map memory region for drm framebuffer to user space. */ +int drm_gem_cma_dumb_map_offset(struct drm_file *file_priv, + struct drm_device *drm, uint32_t handle, uint64_t *offset); + +/* set vm_flags and we can change the vm attribute to other one at here. */ +int drm_gem_cma_mmap(struct file *filp, struct vm_area_struct *vma); + +/* + * destroy memory region allocated. + * - a gem handle and physical memory region pointed by a gem object + * would be released by drm_gem_handle_delete(). + */ +int drm_gem_cma_dumb_destroy(struct drm_file *file_priv, + struct drm_device *drm, unsigned int handle); + +/* allocate physical memory. */ +struct drm_gem_cma_object *drm_gem_cma_create(struct drm_device *drm, + unsigned int size); + +extern const struct vm_operations_struct drm_gem_cma_vm_ops; + +#endif /* __DRM_GEM_CMA_HELPER_H__ */ -- cgit v0.10.2 From 2e3b3c42f06ff6801b3d7839757bbdb231619083 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Mon, 2 Jul 2012 16:37:47 +0200 Subject: DRM: Add DRM KMS/FB CMA helper This patchset introduces a set of helper function for implementing the KMS framebuffer layer for drivers which use the DRM GEM CMA helper function. Signed-off-by: Lars-Peter Clausen Tested-by: Sascha Hauer Acked-by: Sascha Hauer [Make DRM_KMS_CMA_HELPER a boolean Kconfig option] Signed-off-by: Laurent Pinchart diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig index 2df146d..7da5f08 100644 --- a/drivers/gpu/drm/Kconfig +++ b/drivers/gpu/drm/Kconfig @@ -60,6 +60,15 @@ config DRM_GEM_CMA_HELPER help Choose this if you need the GEM CMA helper functions +config DRM_KMS_CMA_HELPER + bool + select DRM_GEM_CMA_HELPER + select FB_SYS_FILLRECT + select FB_SYS_COPYAREA + select FB_SYS_IMAGEBLIT + help + Choose this if you need the KMS CMA helper functions + config DRM_TDFX tristate "3dfx Banshee/Voodoo3+" depends on DRM && PCI diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile index 2fcedbb..58961b9 100644 --- a/drivers/gpu/drm/Makefile +++ b/drivers/gpu/drm/Makefile @@ -21,6 +21,7 @@ drm-usb-y := drm_usb.o drm_kms_helper-y := drm_fb_helper.o drm_crtc_helper.o drm_dp_i2c_helper.o drm_kms_helper-$(CONFIG_DRM_LOAD_EDID_FIRMWARE) += drm_edid_load.o +drm_kms_helper-$(CONFIG_DRM_KMS_CMA_HELPER) += drm_fb_cma_helper.o obj-$(CONFIG_DRM_KMS_HELPER) += drm_kms_helper.o diff --git a/drivers/gpu/drm/drm_fb_cma_helper.c b/drivers/gpu/drm/drm_fb_cma_helper.c new file mode 100644 index 0000000..09e11a5 --- /dev/null +++ b/drivers/gpu/drm/drm_fb_cma_helper.c @@ -0,0 +1,406 @@ +/* + * drm kms/fb cma (contiguous memory allocator) helper functions + * + * Copyright (C) 2012 Analog Device Inc. + * Author: Lars-Peter Clausen + * + * Based on udl_fbdev.c + * Copyright (C) 2012 Red Hat + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include + +struct drm_fb_cma { + struct drm_framebuffer fb; + struct drm_gem_cma_object *obj[4]; +}; + +struct drm_fbdev_cma { + struct drm_fb_helper fb_helper; + struct drm_fb_cma *fb; +}; + +static inline struct drm_fbdev_cma *to_fbdev_cma(struct drm_fb_helper *helper) +{ + return container_of(helper, struct drm_fbdev_cma, fb_helper); +} + +static inline struct drm_fb_cma *to_fb_cma(struct drm_framebuffer *fb) +{ + return container_of(fb, struct drm_fb_cma, fb); +} + +static void drm_fb_cma_destroy(struct drm_framebuffer *fb) +{ + struct drm_fb_cma *fb_cma = to_fb_cma(fb); + int i; + + for (i = 0; i < 4; i++) { + if (fb_cma->obj[i]) + drm_gem_object_unreference_unlocked(&fb_cma->obj[i]->base); + } + + drm_framebuffer_cleanup(fb); + kfree(fb_cma); +} + +static int drm_fb_cma_create_handle(struct drm_framebuffer *fb, + struct drm_file *file_priv, unsigned int *handle) +{ + struct drm_fb_cma *fb_cma = to_fb_cma(fb); + + return drm_gem_handle_create(file_priv, + &fb_cma->obj[0]->base, handle); +} + +static struct drm_framebuffer_funcs drm_fb_cma_funcs = { + .destroy = drm_fb_cma_destroy, + .create_handle = drm_fb_cma_create_handle, +}; + +static struct drm_fb_cma *drm_fb_cma_alloc(struct drm_device *dev, + struct drm_mode_fb_cmd2 *mode_cmd, struct drm_gem_cma_object **obj, + unsigned int num_planes) +{ + struct drm_fb_cma *fb_cma; + int ret; + int i; + + fb_cma = kzalloc(sizeof(*fb_cma), GFP_KERNEL); + if (!fb_cma) + return ERR_PTR(-ENOMEM); + + ret = drm_framebuffer_init(dev, &fb_cma->fb, &drm_fb_cma_funcs); + if (ret) { + dev_err(dev->dev, "Failed to initalize framebuffer: %d\n", ret); + kfree(fb_cma); + return ERR_PTR(ret); + } + + drm_helper_mode_fill_fb_struct(&fb_cma->fb, mode_cmd); + + for (i = 0; i < num_planes; i++) + fb_cma->obj[i] = obj[i]; + + return fb_cma; +} + +/** + * drm_fb_cma_create() - (struct drm_mode_config_funcs *)->fb_create callback function + * + * If your hardware has special alignment or pitch requirements these should be + * checked before calling this function. + */ +struct drm_framebuffer *drm_fb_cma_create(struct drm_device *dev, + struct drm_file *file_priv, struct drm_mode_fb_cmd2 *mode_cmd) +{ + struct drm_fb_cma *fb_cma; + struct drm_gem_cma_object *objs[4]; + struct drm_gem_object *obj; + unsigned int hsub; + unsigned int vsub; + int ret; + int i; + + hsub = drm_format_horz_chroma_subsampling(mode_cmd->pixel_format); + vsub = drm_format_vert_chroma_subsampling(mode_cmd->pixel_format); + + for (i = 0; i < drm_format_num_planes(mode_cmd->pixel_format); i++) { + unsigned int width = mode_cmd->width / (i ? hsub : 1); + unsigned int height = mode_cmd->height / (i ? vsub : 1); + unsigned int min_size; + + obj = drm_gem_object_lookup(dev, file_priv, mode_cmd->handles[i]); + if (!obj) { + dev_err(dev->dev, "Failed to lookup GEM object\n"); + ret = -ENXIO; + goto err_gem_object_unreference; + } + + min_size = (height - 1) * mode_cmd->pitches[i] + + width * drm_format_plane_cpp(mode_cmd->pixel_format, i) + + mode_cmd->offsets[i]; + + if (obj->size < min_size) { + drm_gem_object_unreference_unlocked(obj); + ret = -EINVAL; + goto err_gem_object_unreference; + } + objs[i] = to_drm_gem_cma_obj(obj); + } + + fb_cma = drm_fb_cma_alloc(dev, mode_cmd, objs, i); + if (IS_ERR(fb_cma)) { + ret = PTR_ERR(fb_cma); + goto err_gem_object_unreference; + } + + return &fb_cma->fb; + +err_gem_object_unreference: + for (i--; i >= 0; i--) + drm_gem_object_unreference_unlocked(&objs[i]->base); + return ERR_PTR(ret); +} +EXPORT_SYMBOL_GPL(drm_fb_cma_create); + +/** + * drm_fb_cma_get_gem_obj() - Get CMA GEM object for framebuffer + * @fb: The framebuffer + * @plane: Which plane + * + * Return the CMA GEM object for given framebuffer. + * + * This function will usually be called from the CRTC callback functions. + */ +struct drm_gem_cma_object *drm_fb_cma_get_gem_obj(struct drm_framebuffer *fb, + unsigned int plane) +{ + struct drm_fb_cma *fb_cma = to_fb_cma(fb); + + if (plane >= 4) + return NULL; + + return fb_cma->obj[plane]; +} +EXPORT_SYMBOL_GPL(drm_fb_cma_get_gem_obj); + +static struct fb_ops drm_fbdev_cma_ops = { + .owner = THIS_MODULE, + .fb_fillrect = sys_fillrect, + .fb_copyarea = sys_copyarea, + .fb_imageblit = sys_imageblit, + .fb_check_var = drm_fb_helper_check_var, + .fb_set_par = drm_fb_helper_set_par, + .fb_blank = drm_fb_helper_blank, + .fb_pan_display = drm_fb_helper_pan_display, + .fb_setcmap = drm_fb_helper_setcmap, +}; + +static int drm_fbdev_cma_create(struct drm_fb_helper *helper, + struct drm_fb_helper_surface_size *sizes) +{ + struct drm_fbdev_cma *fbdev_cma = to_fbdev_cma(helper); + struct drm_mode_fb_cmd2 mode_cmd = { 0 }; + struct drm_device *dev = helper->dev; + struct drm_gem_cma_object *obj; + struct drm_framebuffer *fb; + unsigned int bytes_per_pixel; + unsigned long offset; + struct fb_info *fbi; + size_t size; + int ret; + + DRM_DEBUG_KMS("surface width(%d), height(%d) and bpp(%d\n", + sizes->surface_width, sizes->surface_height, + sizes->surface_bpp); + + bytes_per_pixel = DIV_ROUND_UP(sizes->surface_bpp, 8); + + mode_cmd.width = sizes->surface_width; + mode_cmd.height = sizes->surface_height; + mode_cmd.pitches[0] = sizes->surface_width * bytes_per_pixel; + mode_cmd.pixel_format = drm_mode_legacy_fb_format(sizes->surface_bpp, + sizes->surface_depth); + + size = mode_cmd.pitches[0] * mode_cmd.height; + obj = drm_gem_cma_create(dev, size); + if (!obj) + return -ENOMEM; + + fbi = framebuffer_alloc(0, dev->dev); + if (!fbi) { + dev_err(dev->dev, "Failed to allocate framebuffer info.\n"); + ret = -ENOMEM; + goto err_drm_gem_cma_free_object; + } + + fbdev_cma->fb = drm_fb_cma_alloc(dev, &mode_cmd, &obj, 1); + if (IS_ERR(fbdev_cma->fb)) { + dev_err(dev->dev, "Failed to allocate DRM framebuffer.\n"); + ret = PTR_ERR(fbdev_cma->fb); + goto err_framebuffer_release; + } + + fb = &fbdev_cma->fb->fb; + helper->fb = fb; + helper->fbdev = fbi; + + fbi->par = helper; + fbi->flags = FBINFO_FLAG_DEFAULT; + fbi->fbops = &drm_fbdev_cma_ops; + + ret = fb_alloc_cmap(&fbi->cmap, 256, 0); + if (ret) { + dev_err(dev->dev, "Failed to allocate color map.\n"); + goto err_drm_fb_cma_destroy; + } + + drm_fb_helper_fill_fix(fbi, fb->pitches[0], fb->depth); + drm_fb_helper_fill_var(fbi, helper, fb->width, fb->height); + + offset = fbi->var.xoffset * bytes_per_pixel; + offset += fbi->var.yoffset * fb->pitches[0]; + + dev->mode_config.fb_base = (resource_size_t)obj->paddr; + fbi->screen_base = obj->vaddr + offset; + fbi->fix.smem_start = (unsigned long)(obj->paddr + offset); + fbi->screen_size = size; + fbi->fix.smem_len = size; + + return 0; + +err_drm_fb_cma_destroy: + drm_fb_cma_destroy(fb); +err_framebuffer_release: + framebuffer_release(fbi); +err_drm_gem_cma_free_object: + drm_gem_cma_free_object(&obj->base); + return ret; +} + +static int drm_fbdev_cma_probe(struct drm_fb_helper *helper, + struct drm_fb_helper_surface_size *sizes) +{ + int ret = 0; + + if (!helper->fb) { + ret = drm_fbdev_cma_create(helper, sizes); + if (ret < 0) + return ret; + ret = 1; + } + + return ret; +} + +static struct drm_fb_helper_funcs drm_fb_cma_helper_funcs = { + .fb_probe = drm_fbdev_cma_probe, +}; + +/** + * drm_fbdev_cma_init() - Allocate and initializes a drm_fbdev_cma struct + * @dev: DRM device + * @preferred_bpp: Preferred bits per pixel for the device + * @num_crtc: Number of CRTCs + * @max_conn_count: Maximum number of connectors + * + * Returns a newly allocated drm_fbdev_cma struct or a ERR_PTR. + */ +struct drm_fbdev_cma *drm_fbdev_cma_init(struct drm_device *dev, + unsigned int preferred_bpp, unsigned int num_crtc, + unsigned int max_conn_count) +{ + struct drm_fbdev_cma *fbdev_cma; + struct drm_fb_helper *helper; + int ret; + + fbdev_cma = kzalloc(sizeof(*fbdev_cma), GFP_KERNEL); + if (!fbdev_cma) { + dev_err(dev->dev, "Failed to allocate drm fbdev.\n"); + return ERR_PTR(-ENOMEM); + } + + fbdev_cma->fb_helper.funcs = &drm_fb_cma_helper_funcs; + helper = &fbdev_cma->fb_helper; + + ret = drm_fb_helper_init(dev, helper, num_crtc, max_conn_count); + if (ret < 0) { + dev_err(dev->dev, "Failed to initialize drm fb helper.\n"); + goto err_free; + } + + ret = drm_fb_helper_single_add_all_connectors(helper); + if (ret < 0) { + dev_err(dev->dev, "Failed to add connectors.\n"); + goto err_drm_fb_helper_fini; + + } + + ret = drm_fb_helper_initial_config(helper, preferred_bpp); + if (ret < 0) { + dev_err(dev->dev, "Failed to set inital hw configuration.\n"); + goto err_drm_fb_helper_fini; + } + + return fbdev_cma; + +err_drm_fb_helper_fini: + drm_fb_helper_fini(helper); +err_free: + kfree(fbdev_cma); + + return ERR_PTR(ret); +} +EXPORT_SYMBOL_GPL(drm_fbdev_cma_init); + +/** + * drm_fbdev_cma_fini() - Free drm_fbdev_cma struct + * @fbdev_cma: The drm_fbdev_cma struct + */ +void drm_fbdev_cma_fini(struct drm_fbdev_cma *fbdev_cma) +{ + if (fbdev_cma->fb_helper.fbdev) { + struct fb_info *info; + int ret; + + info = fbdev_cma->fb_helper.fbdev; + ret = unregister_framebuffer(info); + if (ret < 0) + DRM_DEBUG_KMS("failed unregister_framebuffer()\n"); + + if (info->cmap.len) + fb_dealloc_cmap(&info->cmap); + + framebuffer_release(info); + } + + if (fbdev_cma->fb) + drm_fb_cma_destroy(&fbdev_cma->fb->fb); + + drm_fb_helper_fini(&fbdev_cma->fb_helper); + kfree(fbdev_cma); +} +EXPORT_SYMBOL_GPL(drm_fbdev_cma_fini); + +/** + * drm_fbdev_cma_restore_mode() - Restores initial framebuffer mode + * @fbdev_cma: The drm_fbdev_cma struct, may be NULL + * + * This function is usually called from the DRM drivers lastclose callback. + */ +void drm_fbdev_cma_restore_mode(struct drm_fbdev_cma *fbdev_cma) +{ + if (fbdev_cma) + drm_fb_helper_restore_fbdev_mode(&fbdev_cma->fb_helper); +} +EXPORT_SYMBOL_GPL(drm_fbdev_cma_restore_mode); + +/** + * drm_fbdev_cma_hotplug_event() - Poll for hotpulug events + * @fbdev_cma: The drm_fbdev_cma struct, may be NULL + * + * This function is usually called from the DRM drivers output_poll_changed + * callback. + */ +void drm_fbdev_cma_hotplug_event(struct drm_fbdev_cma *fbdev_cma) +{ + if (fbdev_cma) + drm_fb_helper_hotplug_event(&fbdev_cma->fb_helper); +} +EXPORT_SYMBOL_GPL(drm_fbdev_cma_hotplug_event); diff --git a/include/drm/drm_fb_cma_helper.h b/include/drm/drm_fb_cma_helper.h new file mode 100644 index 0000000..76c7098 --- /dev/null +++ b/include/drm/drm_fb_cma_helper.h @@ -0,0 +1,27 @@ +#ifndef __DRM_FB_CMA_HELPER_H__ +#define __DRM_FB_CMA_HELPER_H__ + +struct drm_fbdev_cma; +struct drm_gem_cma_object; + +struct drm_framebuffer; +struct drm_device; +struct drm_file; +struct drm_mode_fb_cmd2; + +struct drm_fbdev_cma *drm_fbdev_cma_init(struct drm_device *dev, + unsigned int preferred_bpp, unsigned int num_crtc, + unsigned int max_conn_count); +void drm_fbdev_cma_fini(struct drm_fbdev_cma *fbdev_cma); + +void drm_fbdev_cma_restore_mode(struct drm_fbdev_cma *fbdev_cma); +void drm_fbdev_cma_hotplug_event(struct drm_fbdev_cma *fbdev_cma); + +struct drm_framebuffer *drm_fb_cma_create(struct drm_device *dev, + struct drm_file *file_priv, struct drm_mode_fb_cmd2 *mode_cmd); + +struct drm_gem_cma_object *drm_fb_cma_get_gem_obj(struct drm_framebuffer *fb, + unsigned int plane); + +#endif + -- cgit v0.10.2 From ba623f6a5a419ac31806e77682da38a9f9b5b462 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Fri, 18 May 2012 23:47:40 +0200 Subject: drm: Add NV24 and NV42 pixel formats Signed-off-by: Laurent Pinchart diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c index 08a7aa7..28d0900 100644 --- a/drivers/gpu/drm/drm_crtc.c +++ b/drivers/gpu/drm/drm_crtc.c @@ -2169,6 +2169,8 @@ static int format_check(const struct drm_mode_fb_cmd2 *r) case DRM_FORMAT_NV21: case DRM_FORMAT_NV16: case DRM_FORMAT_NV61: + case DRM_FORMAT_NV24: + case DRM_FORMAT_NV42: case DRM_FORMAT_YUV410: case DRM_FORMAT_YVU410: case DRM_FORMAT_YUV411: @@ -3718,6 +3720,8 @@ int drm_format_num_planes(uint32_t format) case DRM_FORMAT_NV21: case DRM_FORMAT_NV16: case DRM_FORMAT_NV61: + case DRM_FORMAT_NV24: + case DRM_FORMAT_NV42: return 2; default: return 1; @@ -3751,6 +3755,8 @@ int drm_format_plane_cpp(uint32_t format, int plane) case DRM_FORMAT_NV21: case DRM_FORMAT_NV16: case DRM_FORMAT_NV61: + case DRM_FORMAT_NV24: + case DRM_FORMAT_NV42: return plane ? 2 : 1; case DRM_FORMAT_YUV410: case DRM_FORMAT_YVU410: diff --git a/include/drm/drm_fourcc.h b/include/drm/drm_fourcc.h index bdf0152..fac7235 100644 --- a/include/drm/drm_fourcc.h +++ b/include/drm/drm_fourcc.h @@ -106,6 +106,8 @@ #define DRM_FORMAT_NV21 fourcc_code('N', 'V', '2', '1') /* 2x2 subsampled Cb:Cr plane */ #define DRM_FORMAT_NV16 fourcc_code('N', 'V', '1', '6') /* 2x1 subsampled Cr:Cb plane */ #define DRM_FORMAT_NV61 fourcc_code('N', 'V', '6', '1') /* 2x1 subsampled Cb:Cr plane */ +#define DRM_FORMAT_NV24 fourcc_code('N', 'V', '2', '4') /* non-subsampled Cr:Cb plane */ +#define DRM_FORMAT_NV42 fourcc_code('N', 'V', '4', '2') /* non-subsampled Cb:Cr plane */ /* 2 non contiguous plane YCbCr */ #define DRM_FORMAT_NV12M fourcc_code('N', 'M', '1', '2') /* 2x2 subsampled Cr:Cb plane */ -- cgit v0.10.2 From 51c1327876f35d61c8bdd81fc96e1b501c9380ee Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Thu, 26 Apr 2012 13:53:59 +0200 Subject: drm: Renesas SH Mobile DRM driver The SH Mobile LCD controller (LCDC) DRM driver supports the main graphics plane in RGB and YUV formats, as well as the overlay planes (in alpha-blending mode only). Only flat panel outputs using the parallel interface are supported. Support for SYS panels, HDMI and DSI is currently not implemented. Signed-off-by: Laurent Pinchart Reviewed-by: Sascha Hauer diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig index 7da5f08..18321b68b 100644 --- a/drivers/gpu/drm/Kconfig +++ b/drivers/gpu/drm/Kconfig @@ -208,3 +208,5 @@ source "drivers/gpu/drm/ast/Kconfig" source "drivers/gpu/drm/mgag200/Kconfig" source "drivers/gpu/drm/cirrus/Kconfig" + +source "drivers/gpu/drm/shmobile/Kconfig" diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile index 58961b9..2ff5cef 100644 --- a/drivers/gpu/drm/Makefile +++ b/drivers/gpu/drm/Makefile @@ -47,4 +47,5 @@ obj-$(CONFIG_DRM_EXYNOS) +=exynos/ obj-$(CONFIG_DRM_GMA500) += gma500/ obj-$(CONFIG_DRM_UDL) += udl/ obj-$(CONFIG_DRM_AST) += ast/ +obj-$(CONFIG_DRM_SHMOBILE) +=shmobile/ obj-y += i2c/ diff --git a/drivers/gpu/drm/shmobile/Kconfig b/drivers/gpu/drm/shmobile/Kconfig new file mode 100644 index 0000000..7e7d52b --- /dev/null +++ b/drivers/gpu/drm/shmobile/Kconfig @@ -0,0 +1,10 @@ +config DRM_SHMOBILE + tristate "DRM Support for SH Mobile" + depends on DRM && (SUPERH || ARCH_SHMOBILE) + select DRM_KMS_HELPER + select DRM_KMS_CMA_HELPER + select DRM_GEM_CMA_HELPER + help + Choose this option if you have an SH Mobile chipset. + If M is selected the module will be called shmob-drm. + diff --git a/drivers/gpu/drm/shmobile/Makefile b/drivers/gpu/drm/shmobile/Makefile new file mode 100644 index 0000000..4c3eeb3 --- /dev/null +++ b/drivers/gpu/drm/shmobile/Makefile @@ -0,0 +1,7 @@ +shmob-drm-y := shmob_drm_backlight.o \ + shmob_drm_crtc.o \ + shmob_drm_drv.o \ + shmob_drm_kms.o \ + shmob_drm_plane.o + +obj-$(CONFIG_DRM_SHMOBILE) += shmob-drm.o diff --git a/drivers/gpu/drm/shmobile/shmob_drm_backlight.c b/drivers/gpu/drm/shmobile/shmob_drm_backlight.c new file mode 100644 index 0000000..463aee1 --- /dev/null +++ b/drivers/gpu/drm/shmobile/shmob_drm_backlight.c @@ -0,0 +1,90 @@ +/* + * shmob_drm_backlight.c -- SH Mobile DRM Backlight + * + * Copyright (C) 2012 Renesas Corporation + * + * Laurent Pinchart (laurent.pinchart@ideasonboard.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include + +#include "shmob_drm_backlight.h" +#include "shmob_drm_crtc.h" +#include "shmob_drm_drv.h" + +static int shmob_drm_backlight_update(struct backlight_device *bdev) +{ + struct shmob_drm_connector *scon = bl_get_data(bdev); + struct shmob_drm_device *sdev = scon->connector.dev->dev_private; + const struct shmob_drm_backlight_data *bdata = &sdev->pdata->backlight; + int brightness = bdev->props.brightness; + + if (bdev->props.power != FB_BLANK_UNBLANK || + bdev->props.state & BL_CORE_SUSPENDED) + brightness = 0; + + return bdata->set_brightness(brightness); +} + +static int shmob_drm_backlight_get_brightness(struct backlight_device *bdev) +{ + struct shmob_drm_connector *scon = bl_get_data(bdev); + struct shmob_drm_device *sdev = scon->connector.dev->dev_private; + const struct shmob_drm_backlight_data *bdata = &sdev->pdata->backlight; + + return bdata->get_brightness(); +} + +static const struct backlight_ops shmob_drm_backlight_ops = { + .options = BL_CORE_SUSPENDRESUME, + .update_status = shmob_drm_backlight_update, + .get_brightness = shmob_drm_backlight_get_brightness, +}; + +void shmob_drm_backlight_dpms(struct shmob_drm_connector *scon, int mode) +{ + if (scon->backlight == NULL) + return; + + scon->backlight->props.power = mode == DRM_MODE_DPMS_ON + ? FB_BLANK_UNBLANK : FB_BLANK_POWERDOWN; + backlight_update_status(scon->backlight); +} + +int shmob_drm_backlight_init(struct shmob_drm_connector *scon) +{ + struct shmob_drm_device *sdev = scon->connector.dev->dev_private; + const struct shmob_drm_backlight_data *bdata = &sdev->pdata->backlight; + struct drm_connector *connector = &scon->connector; + struct drm_device *dev = connector->dev; + struct backlight_device *backlight; + + if (!bdata->max_brightness) + return 0; + + backlight = backlight_device_register(bdata->name, dev->dev, scon, + &shmob_drm_backlight_ops, NULL); + if (IS_ERR(backlight)) { + dev_err(dev->dev, "unable to register backlight device: %ld\n", + PTR_ERR(backlight)); + return PTR_ERR(backlight); + } + + backlight->props.max_brightness = bdata->max_brightness; + backlight->props.brightness = bdata->max_brightness; + backlight->props.power = FB_BLANK_POWERDOWN; + backlight_update_status(backlight); + + scon->backlight = backlight; + return 0; +} + +void shmob_drm_backlight_exit(struct shmob_drm_connector *scon) +{ + backlight_device_unregister(scon->backlight); +} diff --git a/drivers/gpu/drm/shmobile/shmob_drm_backlight.h b/drivers/gpu/drm/shmobile/shmob_drm_backlight.h new file mode 100644 index 0000000..9477595 --- /dev/null +++ b/drivers/gpu/drm/shmobile/shmob_drm_backlight.h @@ -0,0 +1,23 @@ +/* + * shmob_drm_backlight.h -- SH Mobile DRM Backlight + * + * Copyright (C) 2012 Renesas Corporation + * + * Laurent Pinchart (laurent.pinchart@ideasonboard.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#ifndef __SHMOB_DRM_BACKLIGHT_H__ +#define __SHMOB_DRM_BACKLIGHT_H__ + +struct shmob_drm_connector; + +void shmob_drm_backlight_dpms(struct shmob_drm_connector *scon, int mode); +int shmob_drm_backlight_init(struct shmob_drm_connector *scon); +void shmob_drm_backlight_exit(struct shmob_drm_connector *scon); + +#endif /* __SHMOB_DRM_BACKLIGHT_H__ */ diff --git a/drivers/gpu/drm/shmobile/shmob_drm_crtc.c b/drivers/gpu/drm/shmobile/shmob_drm_crtc.c new file mode 100644 index 0000000..0e7a930 --- /dev/null +++ b/drivers/gpu/drm/shmobile/shmob_drm_crtc.c @@ -0,0 +1,763 @@ +/* + * shmob_drm_crtc.c -- SH Mobile DRM CRTCs + * + * Copyright (C) 2012 Renesas Corporation + * + * Laurent Pinchart (laurent.pinchart@ideasonboard.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include +#include + +#include +#include +#include +#include +#include + +#include