]> git.karo-electronics.de Git - karo-tx-linux.git/blobdiff - drivers/gpu/drm/i915/intel_display.c
Merge tag 'drm-intel-next-2014-02-14' of ssh://git.freedesktop.org/git/drm-intel...
[karo-tx-linux.git] / drivers / gpu / drm / i915 / intel_display.c
index 0f4cbd0aa59e03cd2ea1030d09069a97ee5cd25e..f19e6ea36dc4f0eb28462d8744fb6d51b97d5951 100644 (file)
@@ -51,7 +51,10 @@ static void ironlake_pch_clock_get(struct intel_crtc *crtc,
 
 static int intel_set_mode(struct drm_crtc *crtc, struct drm_display_mode *mode,
                          int x, int y, struct drm_framebuffer *old_fb);
-
+static int intel_framebuffer_init(struct drm_device *dev,
+                                 struct intel_framebuffer *ifb,
+                                 struct drm_mode_fb_cmd2 *mode_cmd,
+                                 struct drm_i915_gem_object *obj);
 
 typedef struct {
        int     min, max;
@@ -1030,7 +1033,7 @@ static void assert_fdi_tx_pll_enabled(struct drm_i915_private *dev_priv,
        u32 val;
 
        /* ILK FDI PLL is always enabled */
-       if (dev_priv->info->gen == 5)
+       if (INTEL_INFO(dev_priv->dev)->gen == 5)
                return;
 
        /* On Haswell, DDI ports are responsible for the FDI PLL setup */
@@ -1189,7 +1192,7 @@ static void assert_sprites_disabled(struct drm_i915_private *dev_priv,
        u32 val;
 
        if (IS_VALLEYVIEW(dev)) {
-               for (i = 0; i < dev_priv->num_plane; i++) {
+               for (i = 0; i < INTEL_INFO(dev)->num_sprites; i++) {
                        reg = SPCNTR(pipe, i);
                        val = I915_READ(reg);
                        WARN((val & SP_ENABLE),
@@ -1443,7 +1446,7 @@ static void i9xx_enable_pll(struct intel_crtc *crtc)
        assert_pipe_disabled(dev_priv, crtc->pipe);
 
        /* No really, not for ILK+ */
-       BUG_ON(dev_priv->info->gen >= 5);
+       BUG_ON(INTEL_INFO(dev)->gen >= 5);
 
        /* PLL is protected by panel, make sure we can write it */
        if (IS_MOBILE(dev) && !IS_I830(dev))
@@ -1549,11 +1552,12 @@ void vlv_wait_port_ready(struct drm_i915_private *dev_priv,
  */
 static void ironlake_enable_shared_dpll(struct intel_crtc *crtc)
 {
-       struct drm_i915_private *dev_priv = crtc->base.dev->dev_private;
+       struct drm_device *dev = crtc->base.dev;
+       struct drm_i915_private *dev_priv = dev->dev_private;
        struct intel_shared_dpll *pll = intel_crtc_to_shared_dpll(crtc);
 
        /* PCH PLLs only available on ILK, SNB and IVB */
-       BUG_ON(dev_priv->info->gen < 5);
+       BUG_ON(INTEL_INFO(dev)->gen < 5);
        if (WARN_ON(pll == NULL))
                return;
 
@@ -1578,11 +1582,12 @@ static void ironlake_enable_shared_dpll(struct intel_crtc *crtc)
 
 static void intel_disable_shared_dpll(struct intel_crtc *crtc)
 {
-       struct drm_i915_private *dev_priv = crtc->base.dev->dev_private;
+       struct drm_device *dev = crtc->base.dev;
+       struct drm_i915_private *dev_priv = dev->dev_private;
        struct intel_shared_dpll *pll = intel_crtc_to_shared_dpll(crtc);
 
        /* PCH only available on ILK+ */
-       BUG_ON(dev_priv->info->gen < 5);
+       BUG_ON(INTEL_INFO(dev)->gen < 5);
        if (WARN_ON(pll == NULL))
               return;
 
@@ -1617,7 +1622,7 @@ static void ironlake_enable_pch_transcoder(struct drm_i915_private *dev_priv,
        uint32_t reg, val, pipeconf_val;
 
        /* PCH only available on ILK+ */
-       BUG_ON(dev_priv->info->gen < 5);
+       BUG_ON(INTEL_INFO(dev)->gen < 5);
 
        /* Make sure PCH DPLL is enabled */
        assert_shared_dpll_enabled(dev_priv,
@@ -1670,7 +1675,7 @@ static void lpt_enable_pch_transcoder(struct drm_i915_private *dev_priv,
        u32 val, pipeconf_val;
 
        /* PCH only available on ILK+ */
-       BUG_ON(dev_priv->info->gen < 5);
+       BUG_ON(INTEL_INFO(dev_priv->dev)->gen < 5);
 
        /* FDI must be feeding us bits for PCH ports */
        assert_fdi_tx_enabled(dev_priv, (enum pipe) cpu_transcoder);
@@ -1744,21 +1749,16 @@ static void lpt_disable_pch_transcoder(struct drm_i915_private *dev_priv)
 
 /**
  * intel_enable_pipe - enable a pipe, asserting requirements
- * @dev_priv: i915 private structure
- * @pipe: pipe to enable
- * @pch_port: on ILK+, is this pipe driving a PCH port or not
+ * @crtc: crtc responsible for the pipe
  *
- * Enable @pipe, making sure that various hardware specific requirements
+ * Enable @crtc's pipe, making sure that various hardware specific requirements
  * are met, if applicable, e.g. PLL enabled, LVDS pairs enabled, etc.
- *
- * @pipe should be %PIPE_A or %PIPE_B.
- *
- * Will wait until the pipe is actually running (i.e. first vblank) before
- * returning.
  */
-static void intel_enable_pipe(struct drm_i915_private *dev_priv, enum pipe pipe,
-                             bool pch_port, bool dsi)
+static void intel_enable_pipe(struct intel_crtc *crtc)
 {
+       struct drm_device *dev = crtc->base.dev;
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       enum pipe pipe = crtc->pipe;
        enum transcoder cpu_transcoder = intel_pipe_to_cpu_transcoder(dev_priv,
                                                                      pipe);
        enum pipe pch_transcoder;
@@ -1780,12 +1780,12 @@ static void intel_enable_pipe(struct drm_i915_private *dev_priv, enum pipe pipe,
         * need the check.
         */
        if (!HAS_PCH_SPLIT(dev_priv->dev))
-               if (dsi)
+               if (intel_pipe_has_type(&crtc->base, INTEL_OUTPUT_DSI))
                        assert_dsi_pll_enabled(dev_priv);
                else
                        assert_pll_enabled(dev_priv, pipe);
        else {
-               if (pch_port) {
+               if (crtc->config.has_pch_encoder) {
                        /* if driving the PCH, we need FDI enabled */
                        assert_fdi_rx_pll_enabled(dev_priv, pch_transcoder);
                        assert_fdi_tx_pll_enabled(dev_priv,
@@ -1796,11 +1796,24 @@ static void intel_enable_pipe(struct drm_i915_private *dev_priv, enum pipe pipe,
 
        reg = PIPECONF(cpu_transcoder);
        val = I915_READ(reg);
-       if (val & PIPECONF_ENABLE)
+       if (val & PIPECONF_ENABLE) {
+               WARN_ON(!(pipe == PIPE_A &&
+                         dev_priv->quirks & QUIRK_PIPEA_FORCE));
                return;
+       }
 
        I915_WRITE(reg, val | PIPECONF_ENABLE);
-       intel_wait_for_vblank(dev_priv->dev, pipe);
+       POSTING_READ(reg);
+
+       /*
+        * There's no guarantee the pipe will really start running now. It
+        * depends on the Gen, the output type and the relative order between
+        * pipe and plane enabling. Avoid waiting on HSW+ since it's not
+        * necessary.
+        * TODO: audit the previous gens.
+        */
+       if (INTEL_INFO(dev)->gen <= 7 && !IS_HASWELL(dev))
+               intel_wait_for_vblank(dev_priv->dev, pipe);
 }
 
 /**
@@ -1851,7 +1864,8 @@ static void intel_disable_pipe(struct drm_i915_private *dev_priv,
 void intel_flush_primary_plane(struct drm_i915_private *dev_priv,
                               enum plane plane)
 {
-       u32 reg = dev_priv->info->gen >= 4 ? DSPSURF(plane) : DSPADDR(plane);
+       struct drm_device *dev = dev_priv->dev;
+       u32 reg = INTEL_INFO(dev)->gen >= 4 ? DSPSURF(plane) : DSPADDR(plane);
 
        I915_WRITE(reg, I915_READ(reg));
        POSTING_READ(reg);
@@ -1929,6 +1943,14 @@ static bool need_vtd_wa(struct drm_device *dev)
        return false;
 }
 
+static int intel_align_height(struct drm_device *dev, int height, bool tiled)
+{
+       int tile_height;
+
+       tile_height = tiled ? (IS_GEN2(dev) ? 16 : 8) : 1;
+       return ALIGN(height, tile_height);
+}
+
 int
 intel_pin_and_fence_fb_obj(struct drm_device *dev,
                           struct drm_i915_gem_object *obj,
@@ -2299,33 +2321,6 @@ intel_finish_fb(struct drm_framebuffer *old_fb)
        return ret;
 }
 
-static void intel_crtc_update_sarea_pos(struct drm_crtc *crtc, int x, int y)
-{
-       struct drm_device *dev = crtc->dev;
-       struct drm_i915_master_private *master_priv;
-       struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
-
-       if (!dev->primary->master)
-               return;
-
-       master_priv = dev->primary->master->driver_priv;
-       if (!master_priv->sarea_priv)
-               return;
-
-       switch (intel_crtc->pipe) {
-       case 0:
-               master_priv->sarea_priv->pipeA_x = x;
-               master_priv->sarea_priv->pipeA_y = y;
-               break;
-       case 1:
-               master_priv->sarea_priv->pipeB_x = x;
-               master_priv->sarea_priv->pipeB_y = y;
-               break;
-       default:
-               break;
-       }
-}
-
 static int
 intel_pipe_set_base(struct drm_crtc *crtc, int x, int y,
                    struct drm_framebuffer *fb)
@@ -2413,8 +2408,6 @@ intel_pipe_set_base(struct drm_crtc *crtc, int x, int y,
        intel_edp_psr_update(dev);
        mutex_unlock(&dev->struct_mutex);
 
-       intel_crtc_update_sarea_pos(crtc, x, y);
-
        return 0;
 }
 
@@ -3587,8 +3580,7 @@ static void ironlake_crtc_enable(struct drm_crtc *crtc)
        intel_crtc_load_lut(crtc);
 
        intel_update_watermarks(crtc);
-       intel_enable_pipe(dev_priv, pipe,
-                         intel_crtc->config.has_pch_encoder, false);
+       intel_enable_pipe(intel_crtc);
        intel_enable_primary_plane(dev_priv, plane, pipe);
        intel_enable_planes(crtc);
        intel_crtc_update_cursor(crtc, true);
@@ -3733,8 +3725,7 @@ static void haswell_crtc_enable(struct drm_crtc *crtc)
        intel_ddi_enable_transcoder_func(crtc);
 
        intel_update_watermarks(crtc);
-       intel_enable_pipe(dev_priv, pipe,
-                         intel_crtc->config.has_pch_encoder, false);
+       intel_enable_pipe(intel_crtc);
 
        if (intel_crtc->config.has_pch_encoder)
                lpt_pch_enable(crtc);
@@ -3748,16 +3739,6 @@ static void haswell_crtc_enable(struct drm_crtc *crtc)
         * to change the workaround. */
        haswell_mode_set_planes_workaround(intel_crtc);
        haswell_crtc_enable_planes(crtc);
-
-       /*
-        * There seems to be a race in PCH platform hw (at least on some
-        * outputs) where an enabled pipe still completes any pageflip right
-        * away (as if the pipe is off) instead of waiting for vblank. As soon
-        * as the first vblank happend, everything works as expected. Hence just
-        * wait for one vblank before returning to avoid strange things
-        * happening.
-        */
-       intel_wait_for_vblank(dev, intel_crtc->pipe);
 }
 
 static void ironlake_pfit_disable(struct intel_crtc *crtc)
@@ -4169,7 +4150,7 @@ static void valleyview_crtc_enable(struct drm_crtc *crtc)
        intel_crtc_load_lut(crtc);
 
        intel_update_watermarks(crtc);
-       intel_enable_pipe(dev_priv, pipe, false, is_dsi);
+       intel_enable_pipe(intel_crtc);
        intel_set_cpu_fifo_underrun_reporting(dev, pipe, true);
        intel_enable_primary_plane(dev_priv, plane, pipe);
        intel_enable_planes(crtc);
@@ -4208,7 +4189,7 @@ static void i9xx_crtc_enable(struct drm_crtc *crtc)
        intel_crtc_load_lut(crtc);
 
        intel_update_watermarks(crtc);
-       intel_enable_pipe(dev_priv, pipe, false, false);
+       intel_enable_pipe(intel_crtc);
        intel_set_cpu_fifo_underrun_reporting(dev, pipe, true);
        intel_enable_primary_plane(dev_priv, plane, pipe);
        intel_enable_planes(crtc);
@@ -5256,25 +5237,23 @@ static void intel_get_pipe_timings(struct intel_crtc *crtc,
        pipe_config->requested_mode.hdisplay = pipe_config->pipe_src_w;
 }
 
-static void intel_crtc_mode_from_pipe_config(struct intel_crtc *intel_crtc,
-                                            struct intel_crtc_config *pipe_config)
+void intel_mode_from_pipe_config(struct drm_display_mode *mode,
+                                struct intel_crtc_config *pipe_config)
 {
-       struct drm_crtc *crtc = &intel_crtc->base;
+       mode->hdisplay = pipe_config->adjusted_mode.crtc_hdisplay;
+       mode->htotal = pipe_config->adjusted_mode.crtc_htotal;
+       mode->hsync_start = pipe_config->adjusted_mode.crtc_hsync_start;
+       mode->hsync_end = pipe_config->adjusted_mode.crtc_hsync_end;
 
-       crtc->mode.hdisplay = pipe_config->adjusted_mode.crtc_hdisplay;
-       crtc->mode.htotal = pipe_config->adjusted_mode.crtc_htotal;
-       crtc->mode.hsync_start = pipe_config->adjusted_mode.crtc_hsync_start;
-       crtc->mode.hsync_end = pipe_config->adjusted_mode.crtc_hsync_end;
+       mode->vdisplay = pipe_config->adjusted_mode.crtc_vdisplay;
+       mode->vtotal = pipe_config->adjusted_mode.crtc_vtotal;
+       mode->vsync_start = pipe_config->adjusted_mode.crtc_vsync_start;
+       mode->vsync_end = pipe_config->adjusted_mode.crtc_vsync_end;
 
-       crtc->mode.vdisplay = pipe_config->adjusted_mode.crtc_vdisplay;
-       crtc->mode.vtotal = pipe_config->adjusted_mode.crtc_vtotal;
-       crtc->mode.vsync_start = pipe_config->adjusted_mode.crtc_vsync_start;
-       crtc->mode.vsync_end = pipe_config->adjusted_mode.crtc_vsync_end;
+       mode->flags = pipe_config->adjusted_mode.flags;
 
-       crtc->mode.flags = pipe_config->adjusted_mode.flags;
-
-       crtc->mode.clock = pipe_config->adjusted_mode.crtc_clock;
-       crtc->mode.flags |= pipe_config->adjusted_mode.flags;
+       mode->clock = pipe_config->adjusted_mode.crtc_clock;
+       mode->flags |= pipe_config->adjusted_mode.flags;
 }
 
 static void i9xx_set_pipeconf(struct intel_crtc *intel_crtc)
@@ -7577,7 +7556,7 @@ static int intel_crtc_cursor_set(struct drm_crtc *crtc,
 
        /* we only need to pin inside GTT if cursor is non-phy */
        mutex_lock(&dev->struct_mutex);
-       if (!dev_priv->info->cursor_needs_physical) {
+       if (!INTEL_INFO(dev)->cursor_needs_physical) {
                unsigned alignment;
 
                if (obj->tiling_mode) {
@@ -7625,7 +7604,7 @@ static int intel_crtc_cursor_set(struct drm_crtc *crtc,
 
  finish:
        if (intel_crtc->cursor_bo) {
-               if (dev_priv->info->cursor_needs_physical) {
+               if (INTEL_INFO(dev)->cursor_needs_physical) {
                        if (intel_crtc->cursor_bo != obj)
                                i915_gem_detach_phys_object(dev, intel_crtc->cursor_bo);
                } else
@@ -7687,10 +7666,10 @@ static struct drm_display_mode load_detect_mode = {
                 704, 832, 0, 480, 489, 491, 520, 0, DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC),
 };
 
-static struct drm_framebuffer *
-intel_framebuffer_create(struct drm_device *dev,
-                        struct drm_mode_fb_cmd2 *mode_cmd,
-                        struct drm_i915_gem_object *obj)
+struct drm_framebuffer *
+__intel_framebuffer_create(struct drm_device *dev,
+                          struct drm_mode_fb_cmd2 *mode_cmd,
+                          struct drm_i915_gem_object *obj)
 {
        struct intel_framebuffer *intel_fb;
        int ret;
@@ -7701,12 +7680,7 @@ intel_framebuffer_create(struct drm_device *dev,
                return ERR_PTR(-ENOMEM);
        }
 
-       ret = i915_mutex_lock_interruptible(dev);
-       if (ret)
-               goto err;
-
        ret = intel_framebuffer_init(dev, intel_fb, mode_cmd, obj);
-       mutex_unlock(&dev->struct_mutex);
        if (ret)
                goto err;
 
@@ -7718,6 +7692,23 @@ err:
        return ERR_PTR(ret);
 }
 
+struct drm_framebuffer *
+intel_framebuffer_create(struct drm_device *dev,
+                        struct drm_mode_fb_cmd2 *mode_cmd,
+                        struct drm_i915_gem_object *obj)
+{
+       struct drm_framebuffer *fb;
+       int ret;
+
+       ret = i915_mutex_lock_interruptible(dev);
+       if (ret)
+               return ERR_PTR(ret);
+       fb = __intel_framebuffer_create(dev, mode_cmd, obj);
+       mutex_unlock(&dev->struct_mutex);
+
+       return fb;
+}
+
 static u32
 intel_framebuffer_pitch_for_width(int width, int bpp)
 {
@@ -7763,14 +7754,16 @@ mode_fits_in_fbdev(struct drm_device *dev,
        struct drm_i915_gem_object *obj;
        struct drm_framebuffer *fb;
 
-       if (dev_priv->fbdev == NULL)
+       if (!dev_priv->fbdev)
                return NULL;
 
-       obj = dev_priv->fbdev->ifb.obj;
-       if (obj == NULL)
+       if (!dev_priv->fbdev->fb)
                return NULL;
 
-       fb = &dev_priv->fbdev->ifb.base;
+       obj = dev_priv->fbdev->fb->obj;
+       BUG_ON(!obj);
+
+       fb = &dev_priv->fbdev->fb->base;
        if (fb->pitches[0] < intel_framebuffer_pitch_for_width(mode->hdisplay,
                                                               fb->bits_per_pixel))
                return NULL;
@@ -8220,7 +8213,7 @@ void intel_mark_idle(struct drm_device *dev)
                intel_decrease_pllclock(crtc);
        }
 
-       if (dev_priv->info->gen >= 6)
+       if (INTEL_INFO(dev)->gen >= 6)
                gen6_rps_idle(dev->dev_private);
 }
 
@@ -10384,8 +10377,7 @@ static bool has_edp_a(struct drm_device *dev)
        if ((I915_READ(DP_A) & DP_DETECTED) == 0)
                return false;
 
-       if (IS_GEN5(dev) &&
-           (I915_READ(ILK_DISPLAY_CHICKEN_FUSES) & ILK_eDP_A_DISABLE))
+       if (IS_GEN5(dev) && (I915_READ(FUSE_STRAP) & ILK_eDP_A_DISABLE))
                return false;
 
        return true;
@@ -10538,18 +10530,13 @@ static void intel_setup_outputs(struct drm_device *dev)
        drm_helper_move_panel_connectors_to_head(dev);
 }
 
-void intel_framebuffer_fini(struct intel_framebuffer *fb)
-{
-       drm_framebuffer_cleanup(&fb->base);
-       WARN_ON(!fb->obj->framebuffer_references--);
-       drm_gem_object_unreference_unlocked(&fb->obj->base);
-}
-
 static void intel_user_framebuffer_destroy(struct drm_framebuffer *fb)
 {
        struct intel_framebuffer *intel_fb = to_intel_framebuffer(fb);
 
-       intel_framebuffer_fini(intel_fb);
+       drm_framebuffer_cleanup(fb);
+       WARN_ON(!intel_fb->obj->framebuffer_references--);
+       drm_gem_object_unreference_unlocked(&intel_fb->obj->base);
        kfree(intel_fb);
 }
 
@@ -10573,7 +10560,7 @@ int intel_framebuffer_init(struct drm_device *dev,
                           struct drm_mode_fb_cmd2 *mode_cmd,
                           struct drm_i915_gem_object *obj)
 {
-       int aligned_height, tile_height;
+       int aligned_height;
        int pitch_limit;
        int ret;
 
@@ -10667,9 +10654,8 @@ int intel_framebuffer_init(struct drm_device *dev,
        if (mode_cmd->offsets[0] != 0)
                return -EINVAL;
 
-       tile_height = IS_GEN2(dev) ? 16 : 8;
-       aligned_height = ALIGN(mode_cmd->height,
-                              obj->tiling_mode ? tile_height : 1);
+       aligned_height = intel_align_height(dev, mode_cmd->height,
+                                           obj->tiling_mode);
        /* FIXME drm helper for size checks (especially planar formats)? */
        if (obj->base.size < aligned_height * mode_cmd->pitches[0])
                return -EINVAL;
@@ -11049,7 +11035,7 @@ void intel_modeset_init(struct drm_device *dev)
 
        for_each_pipe(i) {
                intel_crtc_init(dev, i);
-               for (j = 0; j < dev_priv->num_plane; j++) {
+               for (j = 0; j < INTEL_INFO(dev)->num_sprites; j++) {
                        ret = intel_plane_init(dev, i, j);
                        if (ret)
                                DRM_DEBUG_KMS("pipe %c sprite %c init failed: %d\n",
@@ -11069,6 +11055,8 @@ void intel_modeset_init(struct drm_device *dev)
 
        /* Just in case the BIOS is doing something questionable. */
        intel_disable_fbc(dev);
+
+       intel_modeset_setup_hw_state(dev, false);
 }
 
 static void
@@ -11375,8 +11363,7 @@ void intel_modeset_setup_hw_state(struct drm_device *dev,
        list_for_each_entry(crtc, &dev->mode_config.crtc_list,
                            base.head) {
                if (crtc->active && i915.fastboot) {
-                       intel_crtc_mode_from_pipe_config(crtc, &crtc->config);
-
+                       intel_mode_from_pipe_config(&crtc->base.mode, &crtc->config);
                        DRM_DEBUG_KMS("[CRTC:%d] found active mode: ",
                                      crtc->base.base.id);
                        drm_mode_debug_printmodeline(&crtc->base.mode);
@@ -11436,10 +11423,14 @@ void intel_modeset_gem_init(struct drm_device *dev)
        intel_modeset_init_hw(dev);
 
        intel_setup_overlay(dev);
+}
 
-       mutex_lock(&dev->mode_config.mutex);
-       intel_modeset_setup_hw_state(dev, false);
-       mutex_unlock(&dev->mode_config.mutex);
+void intel_connector_unregister(struct intel_connector *intel_connector)
+{
+       struct drm_connector *connector = &intel_connector->base;
+
+       intel_panel_destroy_backlight(connector);
+       drm_sysfs_connector_remove(connector);
 }
 
 void intel_modeset_cleanup(struct drm_device *dev)
@@ -11486,8 +11477,10 @@ void intel_modeset_cleanup(struct drm_device *dev)
 
        /* destroy the backlight and sysfs files before encoders/connectors */
        list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
-               intel_panel_destroy_backlight(connector);
-               drm_sysfs_connector_remove(connector);
+               struct intel_connector *intel_connector;
+
+               intel_connector = to_intel_connector(connector);
+               intel_connector->unregister(intel_connector);
        }
 
        drm_mode_config_cleanup(dev);
@@ -11520,12 +11513,24 @@ int intel_modeset_vga_set_state(struct drm_device *dev, bool state)
        unsigned reg = INTEL_INFO(dev)->gen >= 6 ? SNB_GMCH_CTRL : INTEL_GMCH_CTRL;
        u16 gmch_ctrl;
 
-       pci_read_config_word(dev_priv->bridge_dev, reg, &gmch_ctrl);
+       if (pci_read_config_word(dev_priv->bridge_dev, reg, &gmch_ctrl)) {
+               DRM_ERROR("failed to read control word\n");
+               return -EIO;
+       }
+
+       if (!!(gmch_ctrl & INTEL_GMCH_VGA_DISABLE) == !state)
+               return 0;
+
        if (state)
                gmch_ctrl &= ~INTEL_GMCH_VGA_DISABLE;
        else
                gmch_ctrl |= INTEL_GMCH_VGA_DISABLE;
-       pci_write_config_word(dev_priv->bridge_dev, reg, gmch_ctrl);
+
+       if (pci_write_config_word(dev_priv->bridge_dev, reg, gmch_ctrl)) {
+               DRM_ERROR("failed to write control word\n");
+               return -EIO;
+       }
+
        return 0;
 }