Merge branch 'drm-intel-fixes' into drm-intel-next
authorKeith Packard <keithp@keithp.com>
Fri, 29 Jul 2011 23:24:10 +0000 (16:24 -0700)
committerKeith Packard <keithp@keithp.com>
Fri, 29 Jul 2011 23:24:10 +0000 (16:24 -0700)
1  2 
drivers/gpu/drm/i915/i915_debugfs.c
drivers/gpu/drm/i915/i915_drv.h
drivers/gpu/drm/i915/i915_reg.h
drivers/gpu/drm/i915/i915_suspend.c
drivers/gpu/drm/i915/intel_display.c
drivers/gpu/drm/i915/intel_hdmi.c

index e2662497d50f582d32bf94abda2f2ff2adf9978f,782b781df9e04aac89f0792eb67cc3971ff3a9dc..5bf7bf57c641f9ceb2c8f30eb15092714e8f02bb
@@@ -865,7 -865,7 +865,7 @@@ static int i915_cur_delayinfo(struct se
                           MEMSTAT_VID_SHIFT);
                seq_printf(m, "Current P-state: %d\n",
                           (rgvstat & MEMSTAT_PSTATE_MASK) >> MEMSTAT_PSTATE_SHIFT);
 -      } else if (IS_GEN6(dev)) {
 +      } else if (IS_GEN6(dev) || IS_GEN7(dev)) {
                u32 gt_perf_status = I915_READ(GEN6_GT_PERF_STATUS);
                u32 rp_state_limits = I915_READ(GEN6_RP_STATE_LIMITS);
                u32 rp_state_cap = I915_READ(GEN6_RP_STATE_CAP);
@@@ -1123,44 -1123,6 +1123,44 @@@ static int i915_emon_status(struct seq_
        return 0;
  }
  
 +static int i915_ring_freq_table(struct seq_file *m, void *unused)
 +{
 +      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;
 +      int ret;
 +      int gpu_freq, ia_freq;
 +
 +      if (!(IS_GEN6(dev) || IS_GEN7(dev))) {
 +              seq_printf(m, "unsupported on this chipset\n");
 +              return 0;
 +      }
 +
 +      ret = mutex_lock_interruptible(&dev->struct_mutex);
 +      if (ret)
 +              return ret;
 +
 +      seq_printf(m, "GPU freq (MHz)\tEffective CPU freq (MHz)\n");
 +
 +      for (gpu_freq = dev_priv->min_delay; gpu_freq <= dev_priv->max_delay;
 +           gpu_freq++) {
 +              I915_WRITE(GEN6_PCODE_DATA, gpu_freq);
 +              I915_WRITE(GEN6_PCODE_MAILBOX, GEN6_PCODE_READY |
 +                         GEN6_PCODE_READ_MIN_FREQ_TABLE);
 +              if (wait_for((I915_READ(GEN6_PCODE_MAILBOX) &
 +                            GEN6_PCODE_READY) == 0, 10)) {
 +                      DRM_ERROR("pcode read of freq table timed out\n");
 +                      continue;
 +              }
 +              ia_freq = I915_READ(GEN6_PCODE_DATA);
 +              seq_printf(m, "%d\t\t%d\n", gpu_freq * 50, ia_freq * 100);
 +      }
 +
 +      mutex_unlock(&dev->struct_mutex);
 +
 +      return 0;
 +}
 +
  static int i915_gfxec(struct seq_file *m, void *unused)
  {
        struct drm_info_node *node = (struct drm_info_node *) m->private;
@@@ -1338,6 -1300,76 +1338,76 @@@ static const struct file_operations i91
        .llseek = default_llseek,
  };
  
+ static int
+ i915_max_freq_open(struct inode *inode,
+                  struct file *filp)
+ {
+       filp->private_data = inode->i_private;
+       return 0;
+ }
+ static ssize_t
+ i915_max_freq_read(struct file *filp,
+                  char __user *ubuf,
+                  size_t max,
+                  loff_t *ppos)
+ {
+       struct drm_device *dev = filp->private_data;
+       drm_i915_private_t *dev_priv = dev->dev_private;
+       char buf[80];
+       int len;
+       len = snprintf(buf, sizeof (buf),
+                      "max freq: %d\n", dev_priv->max_delay * 50);
+       if (len > sizeof (buf))
+               len = sizeof (buf);
+       return simple_read_from_buffer(ubuf, max, ppos, buf, len);
+ }
+ static ssize_t
+ i915_max_freq_write(struct file *filp,
+                 const char __user *ubuf,
+                 size_t cnt,
+                 loff_t *ppos)
+ {
+       struct drm_device *dev = filp->private_data;
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       char buf[20];
+       int val = 1;
+       if (cnt > 0) {
+               if (cnt > sizeof (buf) - 1)
+                       return -EINVAL;
+               if (copy_from_user(buf, ubuf, cnt))
+                       return -EFAULT;
+               buf[cnt] = 0;
+               val = simple_strtoul(buf, NULL, 0);
+       }
+       DRM_DEBUG_DRIVER("Manually setting max freq to %d\n", val);
+       /*
+        * 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);
+       return cnt;
+ }
+ static const struct file_operations i915_max_freq_fops = {
+       .owner = THIS_MODULE,
+       .open = i915_max_freq_open,
+       .read = i915_max_freq_read,
+       .write = i915_max_freq_write,
+       .llseek = default_llseek,
+ };
  /* As the drm_debugfs_init() routines are called before dev->dev_private is
   * allocated we need to hook into the minor for release. */
  static int
@@@ -1437,6 -1469,21 +1507,21 @@@ static int i915_forcewake_create(struc
        return drm_add_fake_info_node(minor, ent, &i915_forcewake_fops);
  }
  
+ static int i915_max_freq_create(struct dentry *root, struct drm_minor *minor)
+ {
+       struct drm_device *dev = minor->dev;
+       struct dentry *ent;
+       ent = debugfs_create_file("i915_max_freq",
+                                 S_IRUGO | S_IWUSR,
+                                 root, dev,
+                                 &i915_max_freq_fops);
+       if (IS_ERR(ent))
+               return PTR_ERR(ent);
+       return drm_add_fake_info_node(minor, ent, &i915_max_freq_fops);
+ }
  static struct drm_info_list i915_debugfs_list[] = {
        {"i915_capabilities", i915_capabilities, 0},
        {"i915_gem_objects", i915_gem_object_info, 0},
        {"i915_inttoext_table", i915_inttoext_table, 0},
        {"i915_drpc_info", i915_drpc_info, 0},
        {"i915_emon_status", i915_emon_status, 0},
 +      {"i915_ring_freq_table", i915_ring_freq_table, 0},
        {"i915_gfxec", i915_gfxec, 0},
        {"i915_fbc_status", i915_fbc_status, 0},
        {"i915_sr_status", i915_sr_status, 0},
@@@ -1488,6 -1534,9 +1573,9 @@@ int i915_debugfs_init(struct drm_minor 
                return ret;
  
        ret = i915_forcewake_create(minor->debugfs_root, minor);
+       if (ret)
+               return ret;
+       ret = i915_max_freq_create(minor->debugfs_root, minor);
        if (ret)
                return ret;
  
@@@ -1504,6 -1553,8 +1592,8 @@@ void i915_debugfs_cleanup(struct drm_mi
                                 1, minor);
        drm_debugfs_remove_files((struct drm_info_list *) &i915_wedged_fops,
                                 1, minor);
+       drm_debugfs_remove_files((struct drm_info_list *) &i915_max_freq_fops,
+                                1, minor);
  }
  
  #endif /* CONFIG_DEBUG_FS */
index 6867e193d85e6f9e8b9ab07eadef040f42904624,e0d0e278f62d098a4f24e5a400756923bda74378..feb4f164fd1b35264b6673201d68e0f22494f025
@@@ -214,8 -214,6 +214,8 @@@ struct drm_i915_display_funcs 
        int (*queue_flip)(struct drm_device *dev, struct drm_crtc *crtc,
                          struct drm_framebuffer *fb,
                          struct drm_i915_gem_object *obj);
 +      int (*update_plane)(struct drm_crtc *crtc, struct drm_framebuffer *fb,
 +                          int x, int y);
        /* clock updates for mode set */
        /* cursor updates */
        /* render clock increase/decrease */
@@@ -267,7 -265,6 +267,7 @@@ enum intel_pch 
  #define QUIRK_LVDS_SSC_DISABLE (1<<1)
  
  struct intel_fbdev;
 +struct intel_fbc_work;
  
  typedef struct drm_i915_private {
        struct drm_device *dev;
        int relative_constants_mode;
  
        void __iomem *regs;
 +      u32 gt_fifo_count;
  
        struct intel_gmbus {
                struct i2c_adapter adapter;
        uint32_t last_instdone1;
  
        unsigned long cfb_size;
 -      unsigned long cfb_pitch;
 -      unsigned long cfb_offset;
 -      int cfb_fence;
 -      int cfb_plane;
 +      unsigned int cfb_fb;
 +      enum plane cfb_plane;
        int cfb_y;
 +      struct intel_fbc_work *fbc_work;
  
        struct intel_opregion opregion;
  
        u32 savePIPEB_LINK_M1;
        u32 savePIPEB_LINK_N1;
        u32 saveMCHBAR_RENDER_STANDBY;
+       u32 savePCH_PORT_HOTPLUG;
  
        struct {
                /** Bridge to intel-gtt-ko */
@@@ -989,16 -987,15 +990,16 @@@ struct drm_i915_file_private 
  
  extern struct drm_ioctl_desc i915_ioctls[];
  extern int i915_max_ioctl;
 -extern unsigned int i915_fbpercrtc;
 -extern int i915_panel_ignore_lid;
 -extern unsigned int i915_powersave;
 -extern unsigned int i915_semaphores;
 -extern unsigned int i915_lvds_downclock;
 -extern unsigned int i915_panel_use_ssc;
 -extern int i915_vbt_sdvo_panel_type;
 -extern unsigned int i915_enable_rc6;
 -extern unsigned int i915_enable_fbc;
 +extern unsigned int i915_fbpercrtc __always_unused;
 +extern int i915_panel_ignore_lid __read_mostly;
 +extern unsigned int i915_powersave __read_mostly;
 +extern unsigned int i915_semaphores __read_mostly;
 +extern unsigned int i915_lvds_downclock __read_mostly;
 +extern unsigned int i915_panel_use_ssc __read_mostly;
 +extern int i915_vbt_sdvo_panel_type __read_mostly;
 +extern unsigned int i915_enable_rc6 __read_mostly;
 +extern unsigned int i915_enable_fbc __read_mostly;
 +extern bool i915_enable_hangcheck __read_mostly;
  
  extern int i915_suspend(struct drm_device *dev, pm_message_t state);
  extern int i915_resume(struct drm_device *dev);
@@@ -1168,7 -1165,7 +1169,7 @@@ void i915_gem_clflush_object(struct drm
  int __must_check i915_gem_object_set_domain(struct drm_i915_gem_object *obj,
                                            uint32_t read_domains,
                                            uint32_t write_domain);
 -int __must_check i915_gem_object_flush_gpu(struct drm_i915_gem_object *obj);
 +int __must_check i915_gem_object_finish_gpu(struct drm_i915_gem_object *obj);
  int __must_check i915_gem_init_ringbuffer(struct drm_device *dev);
  void i915_gem_cleanup_ringbuffer(struct drm_device *dev);
  void i915_gem_do_init(struct drm_device *dev,
@@@ -1187,8 -1184,7 +1188,8 @@@ int __must_chec
  i915_gem_object_set_to_gtt_domain(struct drm_i915_gem_object *obj,
                                  bool write);
  int __must_check
 -i915_gem_object_set_to_display_plane(struct drm_i915_gem_object *obj,
 +i915_gem_object_pin_to_display_plane(struct drm_i915_gem_object *obj,
 +                                   u32 alignment,
                                     struct intel_ring_buffer *pipelined);
  int i915_gem_attach_phys_object(struct drm_device *dev,
                                struct drm_i915_gem_object *obj,
@@@ -1204,14 -1200,9 +1205,14 @@@ i915_gem_get_unfenced_gtt_alignment(str
                                    uint32_t size,
                                    int tiling_mode);
  
 +int i915_gem_object_set_cache_level(struct drm_i915_gem_object *obj,
 +                                  enum i915_cache_level cache_level);
 +
  /* i915_gem_gtt.c */
  void i915_gem_restore_gtt_mappings(struct drm_device *dev);
  int __must_check i915_gem_gtt_bind_object(struct drm_i915_gem_object *obj);
 +void i915_gem_gtt_rebind_object(struct drm_i915_gem_object *obj,
 +                              enum i915_cache_level cache_level);
  void i915_gem_gtt_unbind_object(struct drm_i915_gem_object *obj);
  
  /* i915_gem_evict.c */
@@@ -1293,8 -1284,12 +1294,8 @@@ extern void intel_modeset_init(struct d
  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 i8xx_disable_fbc(struct drm_device *dev);
 -extern void g4x_disable_fbc(struct drm_device *dev);
 -extern void ironlake_disable_fbc(struct drm_device *dev);
 -extern void intel_disable_fbc(struct drm_device *dev);
 -extern void intel_enable_fbc(struct drm_crtc *crtc, unsigned long interval);
  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);
  extern void ironlake_enable_rc6(struct drm_device *dev);
  extern void gen6_set_rps(struct drm_device *dev, u8 val);
index a900809baf2a9f3d886908580f7f85978261de02,a7f7a347c7003d8e0562199bdf8ff65b37f8468a..be67a596eee5b8b1504e0f21be9755b2616d376d
  #define   DPFC_CTL_PLANEA     (0<<30)
  #define   DPFC_CTL_PLANEB     (1<<30)
  #define   DPFC_CTL_FENCE_EN   (1<<29)
 +#define   DPFC_CTL_PERSISTENT_MODE    (1<<25)
  #define   DPFC_SR_EN          (1<<10)
  #define   DPFC_CTL_LIMIT_1X   (0<<6)
  #define   DPFC_CTL_LIMIT_2X   (1<<6)
  #define _TRANSA_DP_LINK_M2       0xe0048
  #define _TRANSA_DP_LINK_N2       0xe004c
  
+ /* Per-transcoder DIP controls */
+ #define _VIDEO_DIP_CTL_A         0xe0200
+ #define _VIDEO_DIP_DATA_A        0xe0208
+ #define _VIDEO_DIP_GCP_A         0xe0210
+ #define _VIDEO_DIP_CTL_B         0xe1200
+ #define _VIDEO_DIP_DATA_B        0xe1208
+ #define _VIDEO_DIP_GCP_B         0xe1210
+ #define TVIDEO_DIP_CTL(pipe) _PIPE(pipe, _VIDEO_DIP_CTL_A, _VIDEO_DIP_CTL_B)
+ #define TVIDEO_DIP_DATA(pipe) _PIPE(pipe, _VIDEO_DIP_DATA_A, _VIDEO_DIP_DATA_B)
+ #define TVIDEO_DIP_GCP(pipe) _PIPE(pipe, _VIDEO_DIP_GCP_A, _VIDEO_DIP_GCP_B)
  #define _TRANS_HTOTAL_B          0xe1000
  #define _TRANS_HBLANK_B          0xe1004
  #define _TRANS_HSYNC_B           0xe1008
  #define TRANS_CHICKEN2(pipe) _PIPE(pipe, _TRANSA_CHICKEN2, _TRANSB_CHICKEN2)
  #define   TRANS_AUTOTRAIN_GEN_STALL_DIS       (1<<31)
  
+ #define SOUTH_CHICKEN1                0xc2000
+ #define  FDIA_PHASE_SYNC_SHIFT_OVR    19
+ #define  FDIA_PHASE_SYNC_SHIFT_EN     18
+ #define FDI_PHASE_SYNC_OVR(pipe) (1<<(FDIA_PHASE_SYNC_SHIFT_OVR - ((pipe) * 2)))
+ #define FDI_PHASE_SYNC_EN(pipe) (1<<(FDIA_PHASE_SYNC_SHIFT_EN - ((pipe) * 2)))
  #define SOUTH_CHICKEN2                0xc2004
  #define  DPLS_EDP_PPS_FIX_DIS (1<<0)
  
  #define  FORCEWAKE_ACK                                0x130090
  
  #define  GT_FIFO_FREE_ENTRIES                 0x120008
 +#define    GT_FIFO_NUM_RESERVED_ENTRIES               20
  
  #define GEN6_RPNSWREQ                         0xA008
  #define   GEN6_TURBO_DISABLE                  (1<<31)
  #define GEN6_PCODE_MAILBOX                    0x138124
  #define   GEN6_PCODE_READY                    (1<<31)
  #define   GEN6_READ_OC_PARAMS                 0xc
 -#define   GEN6_PCODE_WRITE_MIN_FREQ_TABLE     0x9
 +#define   GEN6_PCODE_WRITE_MIN_FREQ_TABLE     0x8
 +#define   GEN6_PCODE_READ_MIN_FREQ_TABLE      0x9
  #define GEN6_PCODE_DATA                               0x138128
 +#define   GEN6_PCODE_FREQ_IA_RATIO_SHIFT      8
  
  #endif /* _I915_REG_H_ */
index 285758603ac82e4e0378f458e3d5a8161a9f5ac7,27693c05c6d2248d473556dbbc472a131fdd51cb..87677d60d0df21758b9a70c51e5f23388e21a5e6
@@@ -760,13 -760,15 +760,13 @@@ static void i915_restore_display(struc
        /* FIXME: restore TV & SDVO state */
  
        /* only restore FBC info on the platform that supports FBC*/
 +      intel_disable_fbc(dev);
        if (I915_HAS_FBC(dev)) {
                if (HAS_PCH_SPLIT(dev)) {
 -                      ironlake_disable_fbc(dev);
                        I915_WRITE(ILK_DPFC_CB_BASE, dev_priv->saveDPFC_CB_BASE);
                } else if (IS_GM45(dev)) {
 -                      g4x_disable_fbc(dev);
                        I915_WRITE(DPFC_CB_BASE, dev_priv->saveDPFC_CB_BASE);
                } else {
 -                      i8xx_disable_fbc(dev);
                        I915_WRITE(FBC_CFB_BASE, dev_priv->saveFBC_CFB_BASE);
                        I915_WRITE(FBC_LL_BASE, dev_priv->saveFBC_LL_BASE);
                        I915_WRITE(FBC_CONTROL2, dev_priv->saveFBC_CONTROL2);
@@@ -812,6 -814,7 +812,7 @@@ int i915_save_state(struct drm_device *
                dev_priv->saveFDI_RXB_IMR = I915_READ(_FDI_RXB_IMR);
                dev_priv->saveMCHBAR_RENDER_STANDBY =
                        I915_READ(RSTDBYCTL);
+               dev_priv->savePCH_PORT_HOTPLUG = I915_READ(PCH_PORT_HOTPLUG);
        } else {
                dev_priv->saveIER = I915_READ(IER);
                dev_priv->saveIMR = I915_READ(IMR);
@@@ -863,6 -866,7 +864,7 @@@ int i915_restore_state(struct drm_devic
                I915_WRITE(GTIMR, dev_priv->saveGTIMR);
                I915_WRITE(_FDI_RXA_IMR, dev_priv->saveFDI_RXA_IMR);
                I915_WRITE(_FDI_RXB_IMR, dev_priv->saveFDI_RXB_IMR);
+               I915_WRITE(PCH_PORT_HOTPLUG, dev_priv->savePCH_PORT_HOTPLUG);
        } else {
                I915_WRITE(IER, dev_priv->saveIER);
                I915_WRITE(IMR, dev_priv->saveIMR);
                intel_init_emon(dev);
        }
  
 -      if (IS_GEN6(dev))
 +      if (IS_GEN6(dev)) {
                gen6_enable_rps(dev_priv);
 +              gen6_update_ring_freq(dev_priv);
 +      }
  
        mutex_lock(&dev->struct_mutex);
  
index ce908ec02900be3a1227beed5d751ab23979855e,53164606918f3420e20094558ac332e88cca308c..2bf5bb63fe41f3730eb86000e77673b208839702
@@@ -24,7 -24,6 +24,7 @@@
   *    Eric Anholt <eric@anholt.net>
   */
  
 +#include <linux/cpufreq.h>
  #include <linux/module.h>
  #include <linux/input.h>
  #include <linux/i2c.h>
@@@ -1176,15 -1175,12 +1176,15 @@@ static void intel_enable_transcoder(str
  
        reg = TRANSCONF(pipe);
        val = I915_READ(reg);
 -      /*
 -       * make the BPC in transcoder be consistent with
 -       * that in pipeconf reg.
 -       */
 -      val &= ~PIPE_BPC_MASK;
 -      val |= I915_READ(PIPECONF(pipe)) & PIPE_BPC_MASK;
 +
 +      if (HAS_PCH_IBX(dev_priv->dev)) {
 +              /*
 +               * make the BPC in transcoder be consistent with
 +               * that in pipeconf reg.
 +               */
 +              val &= ~PIPE_BPC_MASK;
 +              val |= I915_READ(PIPECONF(pipe)) & PIPE_BPC_MASK;
 +      }
        I915_WRITE(reg, val | TRANS_ENABLE);
        if (wait_for(I915_READ(reg) & TRANS_STATE_ENABLE, 100))
                DRM_ERROR("failed to enable transcoder %d\n", pipe);
@@@ -1408,28 -1404,6 +1408,28 @@@ static void intel_disable_pch_ports(str
        disable_pch_hdmi(dev_priv, pipe, HDMID);
  }
  
 +static void i8xx_disable_fbc(struct drm_device *dev)
 +{
 +      struct drm_i915_private *dev_priv = dev->dev_private;
 +      u32 fbc_ctl;
 +
 +      /* Disable compression */
 +      fbc_ctl = I915_READ(FBC_CONTROL);
 +      if ((fbc_ctl & FBC_CTL_EN) == 0)
 +              return;
 +
 +      fbc_ctl &= ~FBC_CTL_EN;
 +      I915_WRITE(FBC_CONTROL, fbc_ctl);
 +
 +      /* Wait for compressing bit to clear */
 +      if (wait_for((I915_READ(FBC_STATUS) & FBC_STAT_COMPRESSING) == 0, 10)) {
 +              DRM_DEBUG_KMS("FBC idle timed out\n");
 +              return;
 +      }
 +
 +      DRM_DEBUG_KMS("disabled FBC\n");
 +}
 +
  static void i8xx_enable_fbc(struct drm_crtc *crtc, unsigned long interval)
  {
        struct drm_device *dev = crtc->dev;
        struct intel_framebuffer *intel_fb = to_intel_framebuffer(fb);
        struct drm_i915_gem_object *obj = intel_fb->obj;
        struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
 +      int cfb_pitch;
        int plane, i;
        u32 fbc_ctl, fbc_ctl2;
  
 -      if (fb->pitch == dev_priv->cfb_pitch &&
 -          obj->fence_reg == dev_priv->cfb_fence &&
 -          intel_crtc->plane == dev_priv->cfb_plane &&
 -          I915_READ(FBC_CONTROL) & FBC_CTL_EN)
 -              return;
 -
 -      i8xx_disable_fbc(dev);
 -
 -      dev_priv->cfb_pitch = dev_priv->cfb_size / FBC_LL_SIZE;
 -
 -      if (fb->pitch < dev_priv->cfb_pitch)
 -              dev_priv->cfb_pitch = fb->pitch;
 +      cfb_pitch = dev_priv->cfb_size / FBC_LL_SIZE;
 +      if (fb->pitch < cfb_pitch)
 +              cfb_pitch = fb->pitch;
  
        /* FBC_CTL wants 64B units */
 -      dev_priv->cfb_pitch = (dev_priv->cfb_pitch / 64) - 1;
 -      dev_priv->cfb_fence = obj->fence_reg;
 -      dev_priv->cfb_plane = intel_crtc->plane;
 -      plane = dev_priv->cfb_plane == 0 ? FBC_CTL_PLANEA : FBC_CTL_PLANEB;
 +      cfb_pitch = (cfb_pitch / 64) - 1;
 +      plane = intel_crtc->plane == 0 ? FBC_CTL_PLANEA : FBC_CTL_PLANEB;
  
        /* Clear old tags */
        for (i = 0; i < (FBC_LL_SIZE / 32) + 1; i++)
                I915_WRITE(FBC_TAG + (i * 4), 0);
  
        /* Set it up... */
 -      fbc_ctl2 = FBC_CTL_FENCE_DBL | FBC_CTL_IDLE_IMM | plane;
 -      if (obj->tiling_mode != I915_TILING_NONE)
 -              fbc_ctl2 |= FBC_CTL_CPU_FENCE;
 +      fbc_ctl2 = FBC_CTL_FENCE_DBL | FBC_CTL_IDLE_IMM | FBC_CTL_CPU_FENCE;
 +      fbc_ctl2 |= plane;
        I915_WRITE(FBC_CONTROL2, fbc_ctl2);
        I915_WRITE(FBC_FENCE_OFF, crtc->y);
  
        fbc_ctl = FBC_CTL_EN | FBC_CTL_PERIODIC;
        if (IS_I945GM(dev))
                fbc_ctl |= FBC_CTL_C3_IDLE; /* 945 needs special SR handling */
 -      fbc_ctl |= (dev_priv->cfb_pitch & 0xff) << FBC_CTL_STRIDE_SHIFT;
 +      fbc_ctl |= (cfb_pitch & 0xff) << FBC_CTL_STRIDE_SHIFT;
        fbc_ctl |= (interval & 0x2fff) << FBC_CTL_INTERVAL_SHIFT;
 -      if (obj->tiling_mode != I915_TILING_NONE)
 -              fbc_ctl |= dev_priv->cfb_fence;
 -      I915_WRITE(FBC_CONTROL, fbc_ctl);
 -
 -      DRM_DEBUG_KMS("enabled FBC, pitch %ld, yoff %d, plane %d, ",
 -                    dev_priv->cfb_pitch, crtc->y, dev_priv->cfb_plane);
 -}
 -
 -void i8xx_disable_fbc(struct drm_device *dev)
 -{
 -      struct drm_i915_private *dev_priv = dev->dev_private;
 -      u32 fbc_ctl;
 -
 -      /* Disable compression */
 -      fbc_ctl = I915_READ(FBC_CONTROL);
 -      if ((fbc_ctl & FBC_CTL_EN) == 0)
 -              return;
 -
 -      fbc_ctl &= ~FBC_CTL_EN;
 +      fbc_ctl |= obj->fence_reg;
        I915_WRITE(FBC_CONTROL, fbc_ctl);
  
 -      /* Wait for compressing bit to clear */
 -      if (wait_for((I915_READ(FBC_STATUS) & FBC_STAT_COMPRESSING) == 0, 10)) {
 -              DRM_DEBUG_KMS("FBC idle timed out\n");
 -              return;
 -      }
 -
 -      DRM_DEBUG_KMS("disabled FBC\n");
 +      DRM_DEBUG_KMS("enabled FBC, pitch %d, yoff %d, plane %d, ",
 +                    cfb_pitch, crtc->y, intel_crtc->plane);
  }
  
  static bool i8xx_fbc_enabled(struct drm_device *dev)
@@@ -1492,9 -1500,30 +1492,9 @@@ static void g4x_enable_fbc(struct drm_c
        unsigned long stall_watermark = 200;
        u32 dpfc_ctl;
  
 -      dpfc_ctl = I915_READ(DPFC_CONTROL);
 -      if (dpfc_ctl & DPFC_CTL_EN) {
 -              if (dev_priv->cfb_pitch == dev_priv->cfb_pitch / 64 - 1 &&
 -                  dev_priv->cfb_fence == obj->fence_reg &&
 -                  dev_priv->cfb_plane == intel_crtc->plane &&
 -                  dev_priv->cfb_y == crtc->y)
 -                      return;
 -
 -              I915_WRITE(DPFC_CONTROL, dpfc_ctl & ~DPFC_CTL_EN);
 -              intel_wait_for_vblank(dev, intel_crtc->pipe);
 -      }
 -
 -      dev_priv->cfb_pitch = (dev_priv->cfb_pitch / 64) - 1;
 -      dev_priv->cfb_fence = obj->fence_reg;
 -      dev_priv->cfb_plane = intel_crtc->plane;
 -      dev_priv->cfb_y = crtc->y;
 -
        dpfc_ctl = plane | DPFC_SR_EN | DPFC_CTL_LIMIT_1X;
 -      if (obj->tiling_mode != I915_TILING_NONE) {
 -              dpfc_ctl |= DPFC_CTL_FENCE_EN | dev_priv->cfb_fence;
 -              I915_WRITE(DPFC_CHICKEN, DPFC_HT_MODIFY);
 -      } else {
 -              I915_WRITE(DPFC_CHICKEN, ~DPFC_HT_MODIFY);
 -      }
 +      dpfc_ctl |= DPFC_CTL_FENCE_EN | obj->fence_reg;
 +      I915_WRITE(DPFC_CHICKEN, DPFC_HT_MODIFY);
  
        I915_WRITE(DPFC_RECOMP_CTL, DPFC_RECOMP_STALL_EN |
                   (stall_watermark << DPFC_RECOMP_STALL_WM_SHIFT) |
        DRM_DEBUG_KMS("enabled fbc on plane %d\n", intel_crtc->plane);
  }
  
 -void g4x_disable_fbc(struct drm_device *dev)
 +static void g4x_disable_fbc(struct drm_device *dev)
  {
        struct drm_i915_private *dev_priv = dev->dev_private;
        u32 dpfc_ctl;
@@@ -1562,12 -1591,32 +1562,12 @@@ static void ironlake_enable_fbc(struct 
        u32 dpfc_ctl;
  
        dpfc_ctl = I915_READ(ILK_DPFC_CONTROL);
 -      if (dpfc_ctl & DPFC_CTL_EN) {
 -              if (dev_priv->cfb_pitch == dev_priv->cfb_pitch / 64 - 1 &&
 -                  dev_priv->cfb_fence == obj->fence_reg &&
 -                  dev_priv->cfb_plane == intel_crtc->plane &&
 -                  dev_priv->cfb_offset == obj->gtt_offset &&
 -                  dev_priv->cfb_y == crtc->y)
 -                      return;
 -
 -              I915_WRITE(ILK_DPFC_CONTROL, dpfc_ctl & ~DPFC_CTL_EN);
 -              intel_wait_for_vblank(dev, intel_crtc->pipe);
 -      }
 -
 -      dev_priv->cfb_pitch = (dev_priv->cfb_pitch / 64) - 1;
 -      dev_priv->cfb_fence = obj->fence_reg;
 -      dev_priv->cfb_plane = intel_crtc->plane;
 -      dev_priv->cfb_offset = obj->gtt_offset;
 -      dev_priv->cfb_y = crtc->y;
 -
        dpfc_ctl &= DPFC_RESERVED;
        dpfc_ctl |= (plane | DPFC_CTL_LIMIT_1X);
 -      if (obj->tiling_mode != I915_TILING_NONE) {
 -              dpfc_ctl |= (DPFC_CTL_FENCE_EN | dev_priv->cfb_fence);
 -              I915_WRITE(ILK_DPFC_CHICKEN, DPFC_HT_MODIFY);
 -      } else {
 -              I915_WRITE(ILK_DPFC_CHICKEN, ~DPFC_HT_MODIFY);
 -      }
 +      /* Set persistent mode for front-buffer rendering, ala X. */
 +      dpfc_ctl |= DPFC_CTL_PERSISTENT_MODE;
 +      dpfc_ctl |= (DPFC_CTL_FENCE_EN | obj->fence_reg);
 +      I915_WRITE(ILK_DPFC_CHICKEN, DPFC_HT_MODIFY);
  
        I915_WRITE(ILK_DPFC_RECOMP_CTL, DPFC_RECOMP_STALL_EN |
                   (stall_watermark << DPFC_RECOMP_STALL_WM_SHIFT) |
  
        if (IS_GEN6(dev)) {
                I915_WRITE(SNB_DPFC_CTL_SA,
 -                         SNB_CPU_FENCE_ENABLE | dev_priv->cfb_fence);
 +                         SNB_CPU_FENCE_ENABLE | obj->fence_reg);
                I915_WRITE(DPFC_CPU_FENCE_OFFSET, crtc->y);
                sandybridge_blit_fbc_update(dev);
        }
        DRM_DEBUG_KMS("enabled fbc on plane %d\n", intel_crtc->plane);
  }
  
 -void ironlake_disable_fbc(struct drm_device *dev)
 +static void ironlake_disable_fbc(struct drm_device *dev)
  {
        struct drm_i915_private *dev_priv = dev->dev_private;
        u32 dpfc_ctl;
@@@ -1619,109 -1668,24 +1619,109 @@@ bool intel_fbc_enabled(struct drm_devic
        return dev_priv->display.fbc_enabled(dev);
  }
  
 -void intel_enable_fbc(struct drm_crtc *crtc, unsigned long interval)
 +static void intel_fbc_work_fn(struct work_struct *__work)
  {
 -      struct drm_i915_private *dev_priv = crtc->dev->dev_private;
 +      struct intel_fbc_work *work =
 +              container_of(to_delayed_work(__work),
 +                           struct intel_fbc_work, work);
 +      struct drm_device *dev = work->crtc->dev;
 +      struct drm_i915_private *dev_priv = dev->dev_private;
 +
 +      mutex_lock(&dev->struct_mutex);
 +      if (work == dev_priv->fbc_work) {
 +              /* Double check that we haven't switched fb without cancelling
 +               * the prior work.
 +               */
 +              if (work->crtc->fb == work->fb) {
 +                      dev_priv->display.enable_fbc(work->crtc,
 +                                                   work->interval);
 +
 +                      dev_priv->cfb_plane = to_intel_crtc(work->crtc)->plane;
 +                      dev_priv->cfb_fb = work->crtc->fb->base.id;
 +                      dev_priv->cfb_y = work->crtc->y;
 +              }
 +
 +              dev_priv->fbc_work = NULL;
 +      }
 +      mutex_unlock(&dev->struct_mutex);
 +
 +      kfree(work);
 +}
 +
 +static void intel_cancel_fbc_work(struct drm_i915_private *dev_priv)
 +{
 +      if (dev_priv->fbc_work == NULL)
 +              return;
 +
 +      DRM_DEBUG_KMS("cancelling pending FBC enable\n");
 +
 +      /* Synchronisation is provided by struct_mutex and checking of
 +       * dev_priv->fbc_work, so we can perform the cancellation
 +       * entirely asynchronously.
 +       */
 +      if (cancel_delayed_work(&dev_priv->fbc_work->work))
 +              /* tasklet was killed before being run, clean up */
 +              kfree(dev_priv->fbc_work);
 +
 +      /* Mark the work as no longer wanted so that if it does
 +       * wake-up (because the work was already running and waiting
 +       * for our mutex), it will discover that is no longer
 +       * necessary to run.
 +       */
 +      dev_priv->fbc_work = NULL;
 +}
 +
 +static void intel_enable_fbc(struct drm_crtc *crtc, unsigned long interval)
 +{
 +      struct intel_fbc_work *work;
 +      struct drm_device *dev = crtc->dev;
 +      struct drm_i915_private *dev_priv = dev->dev_private;
  
        if (!dev_priv->display.enable_fbc)
                return;
  
 -      dev_priv->display.enable_fbc(crtc, interval);
 +      intel_cancel_fbc_work(dev_priv);
 +
 +      work = kzalloc(sizeof *work, GFP_KERNEL);
 +      if (work == NULL) {
 +              dev_priv->display.enable_fbc(crtc, interval);
 +              return;
 +      }
 +
 +      work->crtc = crtc;
 +      work->fb = crtc->fb;
 +      work->interval = interval;
 +      INIT_DELAYED_WORK(&work->work, intel_fbc_work_fn);
 +
 +      dev_priv->fbc_work = work;
 +
 +      DRM_DEBUG_KMS("scheduling delayed FBC enable\n");
 +
 +      /* Delay the actual enabling to let pageflipping cease and the
 +       * display to settle before starting the compression. Note that
 +       * this delay also serves a second purpose: it allows for a
 +       * vblank to pass after disabling the FBC before we attempt
 +       * to modify the control registers.
 +       *
 +       * A more complicated solution would involve tracking vblanks
 +       * following the termination of the page-flipping sequence
 +       * and indeed performing the enable as a co-routine and not
 +       * waiting synchronously upon the vblank.
 +       */
 +      schedule_delayed_work(&work->work, msecs_to_jiffies(50));
  }
  
  void intel_disable_fbc(struct drm_device *dev)
  {
        struct drm_i915_private *dev_priv = dev->dev_private;
  
 +      intel_cancel_fbc_work(dev_priv);
 +
        if (!dev_priv->display.disable_fbc)
                return;
  
        dev_priv->display.disable_fbc(dev);
 +      dev_priv->cfb_plane = -1;
  }
  
  /**
@@@ -1820,13 -1784,8 +1820,13 @@@ static void intel_update_fbc(struct drm
                dev_priv->no_fbc_reason = FBC_BAD_PLANE;
                goto out_disable;
        }
 -      if (obj->tiling_mode != I915_TILING_X) {
 -              DRM_DEBUG_KMS("framebuffer not tiled, disabling compression\n");
 +
 +      /* The use of a CPU fence is mandatory in order to detect writes
 +       * by the CPU to the scanout and trigger updates to the FBC.
 +       */
 +      if (obj->tiling_mode != I915_TILING_X ||
 +          obj->fence_reg == I915_FENCE_REG_NONE) {
 +              DRM_DEBUG_KMS("framebuffer not tiled or fenced, disabling compression\n");
                dev_priv->no_fbc_reason = FBC_NOT_TILED;
                goto out_disable;
        }
        if (in_dbg_master())
                goto out_disable;
  
 +      /* If the scanout has not changed, don't modify the FBC settings.
 +       * Note that we make the fundamental assumption that the fb->obj
 +       * cannot be unpinned (and have its GTT offset and fence revoked)
 +       * without first being decoupled from the scanout and FBC disabled.
 +       */
 +      if (dev_priv->cfb_plane == intel_crtc->plane &&
 +          dev_priv->cfb_fb == fb->base.id &&
 +          dev_priv->cfb_y == crtc->y)
 +              return;
 +
 +      if (intel_fbc_enabled(dev)) {
 +              /* We update FBC along two paths, after changing fb/crtc
 +               * configuration (modeswitching) and after page-flipping
 +               * finishes. For the latter, we know that not only did
 +               * we disable the FBC at the start of the page-flip
 +               * sequence, but also more than one vblank has passed.
 +               *
 +               * For the former case of modeswitching, it is possible
 +               * to switch between two FBC valid configurations
 +               * instantaneously so we do need to disable the FBC
 +               * before we can modify its control registers. We also
 +               * have to wait for the next vblank for that to take
 +               * effect. However, since we delay enabling FBC we can
 +               * assume that a vblank has passed since disabling and
 +               * that we can safely alter the registers in the deferred
 +               * callback.
 +               *
 +               * In the scenario that we go from a valid to invalid
 +               * and then back to valid FBC configuration we have
 +               * no strict enforcement that a vblank occurred since
 +               * disabling the FBC. However, along all current pipe
 +               * disabling paths we do need to wait for a vblank at
 +               * some point. And we wait before enabling FBC anyway.
 +               */
 +              DRM_DEBUG_KMS("disabling active FBC for update\n");
 +              intel_disable_fbc(dev);
 +      }
 +
        intel_enable_fbc(crtc, 500);
        return;
  
@@@ -1915,10 -1836,14 +1915,10 @@@ intel_pin_and_fence_fb_obj(struct drm_d
        }
  
        dev_priv->mm.interruptible = false;
 -      ret = i915_gem_object_pin(obj, alignment, true);
 +      ret = i915_gem_object_pin_to_display_plane(obj, alignment, pipelined);
        if (ret)
                goto err_interruptible;
  
 -      ret = i915_gem_object_set_to_display_plane(obj, pipelined);
 -      if (ret)
 -              goto err_unpin;
 -
        /* Install a fence for tiled scan-out. Pre-i965 always needs a
         * fence, whereas 965+ only requires a fence if using
         * framebuffer compression.  For simplicity, we always install
@@@ -1940,8 -1865,10 +1940,8 @@@ err_interruptible
        return ret;
  }
  
 -/* Assume fb object is pinned & idle & fenced and just update base pointers */
 -static int
 -intel_pipe_set_base_atomic(struct drm_crtc *crtc, struct drm_framebuffer *fb,
 -                         int x, int y, enum mode_set_atomic state)
 +static int i9xx_update_plane(struct drm_crtc *crtc, struct drm_framebuffer *fb,
 +                           int x, int y)
  {
        struct drm_device *dev = crtc->dev;
        struct drm_i915_private *dev_priv = dev->dev_private;
                dspcntr |= DISPPLANE_32BPP_NO_ALPHA;
                break;
        default:
 -              DRM_ERROR("Unknown color depth\n");
 +              DRM_ERROR("Unknown color depth %d\n", fb->bits_per_pixel);
                return -EINVAL;
        }
        if (INTEL_INFO(dev)->gen >= 4) {
                        dspcntr &= ~DISPPLANE_TILED;
        }
  
 -      if (HAS_PCH_SPLIT(dev))
 -              /* must disable */
 -              dspcntr |= DISPPLANE_TRICKLE_FEED_DISABLE;
 -
        I915_WRITE(reg, dspcntr);
  
        Start = obj->gtt_offset;
                I915_WRITE(DSPADDR(plane), Start + Offset);
        POSTING_READ(reg);
  
 +      return 0;
 +}
 +
 +static int ironlake_update_plane(struct drm_crtc *crtc,
 +                               struct drm_framebuffer *fb, int x, int y)
 +{
 +      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_framebuffer *intel_fb;
 +      struct drm_i915_gem_object *obj;
 +      int plane = intel_crtc->plane;
 +      unsigned long Start, Offset;
 +      u32 dspcntr;
 +      u32 reg;
 +
 +      switch (plane) {
 +      case 0:
 +      case 1:
 +              break;
 +      default:
 +              DRM_ERROR("Can't update plane %d in SAREA\n", plane);
 +              return -EINVAL;
 +      }
 +
 +      intel_fb = to_intel_framebuffer(fb);
 +      obj = intel_fb->obj;
 +
 +      reg = DSPCNTR(plane);
 +      dspcntr = I915_READ(reg);
 +      /* Mask out pixel format bits in case we change it */
 +      dspcntr &= ~DISPPLANE_PIXFORMAT_MASK;
 +      switch (fb->bits_per_pixel) {
 +      case 8:
 +              dspcntr |= DISPPLANE_8BPP;
 +              break;
 +      case 16:
 +              if (fb->depth != 16)
 +                      return -EINVAL;
 +
 +              dspcntr |= DISPPLANE_16BPP;
 +              break;
 +      case 24:
 +      case 32:
 +              if (fb->depth == 24)
 +                      dspcntr |= DISPPLANE_32BPP_NO_ALPHA;
 +              else if (fb->depth == 30)
 +                      dspcntr |= DISPPLANE_32BPP_30BIT_NO_ALPHA;
 +              else
 +                      return -EINVAL;
 +              break;
 +      default:
 +              DRM_ERROR("Unknown color depth %d\n", fb->bits_per_pixel);
 +              return -EINVAL;
 +      }
 +
 +      if (obj->tiling_mode != I915_TILING_NONE)
 +              dspcntr |= DISPPLANE_TILED;
 +      else
 +              dspcntr &= ~DISPPLANE_TILED;
 +
 +      /* must disable */
 +      dspcntr |= DISPPLANE_TRICKLE_FEED_DISABLE;
 +
 +      I915_WRITE(reg, dspcntr);
 +
 +      Start = obj->gtt_offset;
 +      Offset = y * fb->pitch + x * (fb->bits_per_pixel / 8);
 +
 +      DRM_DEBUG_KMS("Writing base %08lX %08lX %d %d %d\n",
 +                    Start, Offset, x, y, fb->pitch);
 +      I915_WRITE(DSPSTRIDE(plane), fb->pitch);
 +      I915_WRITE(DSPSURF(plane), Start);
 +      I915_WRITE(DSPTILEOFF(plane), (y << 16) | x);
 +      I915_WRITE(DSPADDR(plane), Offset);
 +      POSTING_READ(reg);
 +
 +      return 0;
 +}
 +
 +/* Assume fb object is pinned & idle & fenced and just update base pointers */
 +static int
 +intel_pipe_set_base_atomic(struct drm_crtc *crtc, struct drm_framebuffer *fb,
 +                         int x, int y, enum mode_set_atomic state)
 +{
 +      struct drm_device *dev = crtc->dev;
 +      struct drm_i915_private *dev_priv = dev->dev_private;
 +      int ret;
 +
 +      ret = dev_priv->display.update_plane(crtc, fb, x, y);
 +      if (ret)
 +              return ret;
 +
        intel_update_fbc(dev);
        intel_increase_pllclock(crtc);
  
@@@ -2159,7 -1997,7 +2159,7 @@@ intel_pipe_set_base(struct drm_crtc *cr
                 * This should only fail upon a hung GPU, in which case we
                 * can safely continue.
                 */
 -              ret = i915_gem_object_flush_gpu(obj);
 +              ret = i915_gem_object_finish_gpu(obj);
                (void) ret;
        }
  
@@@ -2275,6 -2113,18 +2275,18 @@@ static void intel_fdi_normal_train(stru
                           FDI_FE_ERRC_ENABLE);
  }
  
+ static void cpt_phase_pointer_enable(struct drm_device *dev, int pipe)
+ {
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       u32 flags = I915_READ(SOUTH_CHICKEN1);
+       flags |= FDI_PHASE_SYNC_OVR(pipe);
+       I915_WRITE(SOUTH_CHICKEN1, flags); /* once to unlock... */
+       flags |= FDI_PHASE_SYNC_EN(pipe);
+       I915_WRITE(SOUTH_CHICKEN1, flags); /* then again to enable */
+       POSTING_READ(SOUTH_CHICKEN1);
+ }
  /* The FDI link training functions for ILK/Ibexpeak. */
  static void ironlake_fdi_link_train(struct drm_crtc *crtc)
  {
@@@ -2425,6 -2275,9 +2437,9 @@@ static void gen6_fdi_link_train(struct 
        POSTING_READ(reg);
        udelay(150);
  
+       if (HAS_PCH_CPT(dev))
+               cpt_phase_pointer_enable(dev, pipe);
        for (i = 0; i < 4; i++ ) {
                reg = FDI_TX_CTL(pipe);
                temp = I915_READ(reg);
@@@ -2541,6 -2394,9 +2556,9 @@@ static void ivb_manual_fdi_link_train(s
        POSTING_READ(reg);
        udelay(150);
  
+       if (HAS_PCH_CPT(dev))
+               cpt_phase_pointer_enable(dev, pipe);
        for (i = 0; i < 4; i++ ) {
                reg = FDI_TX_CTL(pipe);
                temp = I915_READ(reg);
@@@ -2650,6 -2506,17 +2668,17 @@@ static void ironlake_fdi_pll_enable(str
        }
  }
  
+ static void cpt_phase_pointer_disable(struct drm_device *dev, int pipe)
+ {
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       u32 flags = I915_READ(SOUTH_CHICKEN1);
+       flags &= ~(FDI_PHASE_SYNC_EN(pipe));
+       I915_WRITE(SOUTH_CHICKEN1, flags); /* once to disable... */
+       flags &= ~(FDI_PHASE_SYNC_OVR(pipe));
+       I915_WRITE(SOUTH_CHICKEN1, flags); /* then again to lock */
+       POSTING_READ(SOUTH_CHICKEN1);
+ }
  static void ironlake_fdi_disable(struct drm_crtc *crtc)
  {
        struct drm_device *dev = crtc->dev;
                I915_WRITE(FDI_RX_CHICKEN(pipe),
                           I915_READ(FDI_RX_CHICKEN(pipe) &
                                     ~FDI_RX_PHASE_SYNC_POINTER_EN));
+       } else if (HAS_PCH_CPT(dev)) {
+               cpt_phase_pointer_disable(dev, pipe);
        }
  
        /* still set train pattern 1 */
@@@ -2811,7 -2680,6 +2842,7 @@@ static void ironlake_pch_enable(struct 
        /* For PCH DP, enable TRANS_DP_CTL */
        if (HAS_PCH_CPT(dev) &&
            intel_pipe_has_type(crtc, INTEL_OUTPUT_DISPLAYPORT)) {
 +              u32 bpc = (I915_READ(PIPECONF(pipe)) & PIPE_BPC_MASK) >> 5;
                reg = TRANS_DP_CTL(pipe);
                temp = I915_READ(reg);
                temp &= ~(TRANS_DP_PORT_SEL_MASK |
                          TRANS_DP_BPC_MASK);
                temp |= (TRANS_DP_OUTPUT_ENABLE |
                         TRANS_DP_ENH_FRAMING);
 -              temp |= TRANS_DP_8BPC;
 +              temp |= bpc << 9; /* same format but at 11:9 */
  
                if (crtc->mode.flags & DRM_MODE_FLAG_PHSYNC)
                        temp |= TRANS_DP_HSYNC_ACTIVE_HIGH;
@@@ -2926,8 -2794,9 +2957,8 @@@ static void ironlake_crtc_disable(struc
  
        intel_disable_plane(dev_priv, plane, pipe);
  
 -      if (dev_priv->cfb_plane == plane &&
 -          dev_priv->display.disable_fbc)
 -              dev_priv->display.disable_fbc(dev);
 +      if (dev_priv->cfb_plane == plane)
 +              intel_disable_fbc(dev);
  
        intel_disable_pipe(dev_priv, pipe);
  
@@@ -3091,8 -2960,9 +3122,8 @@@ static void i9xx_crtc_disable(struct dr
        intel_crtc_dpms_overlay(intel_crtc, false);
        intel_crtc_update_cursor(crtc, false);
  
 -      if (dev_priv->cfb_plane == plane &&
 -          dev_priv->display.disable_fbc)
 -              dev_priv->display.disable_fbc(dev);
 +      if (dev_priv->cfb_plane == plane)
 +              intel_disable_fbc(dev);
  
        intel_disable_plane(dev_priv, plane, pipe);
        intel_disable_pipe(dev_priv, pipe);
@@@ -4501,133 -4371,6 +4532,133 @@@ static inline bool intel_panel_use_ssc(
                && !(dev_priv->quirks & QUIRK_LVDS_SSC_DISABLE);
  }
  
 +/**
 + * intel_choose_pipe_bpp_dither - figure out what color depth the pipe should send
 + * @crtc: CRTC structure
 + *
 + * A pipe may be connected to one or more outputs.  Based on the depth of the
 + * attached framebuffer, choose a good color depth to use on the pipe.
 + *
 + * If possible, match the pipe depth to the fb depth.  In some cases, this
 + * isn't ideal, because the connected output supports a lesser or restricted
 + * set of depths.  Resolve that here:
 + *    LVDS typically supports only 6bpc, so clamp down in that case
 + *    HDMI supports only 8bpc or 12bpc, so clamp to 8bpc with dither for 10bpc
 + *    Displays may support a restricted set as well, check EDID and clamp as
 + *      appropriate.
 + *
 + * RETURNS:
 + * Dithering requirement (i.e. false if display bpc and pipe bpc match,
 + * true if they don't match).
 + */
 +static bool intel_choose_pipe_bpp_dither(struct drm_crtc *crtc,
 +                                       unsigned int *pipe_bpp)
 +{
 +      struct drm_device *dev = crtc->dev;
 +      struct drm_i915_private *dev_priv = dev->dev_private;
 +      struct drm_encoder *encoder;
 +      struct drm_connector *connector;
 +      unsigned int display_bpc = UINT_MAX, bpc;
 +
 +      /* Walk the encoders & connectors on this crtc, get min bpc */
 +      list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
 +              struct intel_encoder *intel_encoder = to_intel_encoder(encoder);
 +
 +              if (encoder->crtc != crtc)
 +                      continue;
 +
 +              if (intel_encoder->type == INTEL_OUTPUT_LVDS) {
 +                      unsigned int lvds_bpc;
 +
 +                      if ((I915_READ(PCH_LVDS) & LVDS_A3_POWER_MASK) ==
 +                          LVDS_A3_POWER_UP)
 +                              lvds_bpc = 8;
 +                      else
 +                              lvds_bpc = 6;
 +
 +                      if (lvds_bpc < display_bpc) {
 +                              DRM_DEBUG_DRIVER("clamping display bpc (was %d) to LVDS (%d)\n", display_bpc, lvds_bpc);
 +                              display_bpc = lvds_bpc;
 +                      }
 +                      continue;
 +              }
 +
 +              if (intel_encoder->type == INTEL_OUTPUT_EDP) {
 +                      /* Use VBT settings if we have an eDP panel */
 +                      unsigned int edp_bpc = dev_priv->edp.bpp / 3;
 +
 +                      if (edp_bpc < display_bpc) {
 +                              DRM_DEBUG_DRIVER("clamping display bpc (was %d) to eDP (%d)\n", display_bpc, edp_bpc);
 +                              display_bpc = edp_bpc;
 +                      }
 +                      continue;
 +              }
 +
 +              /* Not one of the known troublemakers, check the EDID */
 +              list_for_each_entry(connector, &dev->mode_config.connector_list,
 +                                  head) {
 +                      if (connector->encoder != encoder)
 +                              continue;
 +
 +                      if (connector->display_info.bpc < display_bpc) {
 +                              DRM_DEBUG_DRIVER("clamping display bpc (was %d) to EDID reported max of %d\n", display_bpc, connector->display_info.bpc);
 +                              display_bpc = connector->display_info.bpc;
 +                      }
 +              }
 +
 +              /*
 +               * HDMI is either 12 or 8, so if the display lets 10bpc sneak
 +               * through, clamp it down.  (Note: >12bpc will be caught below.)
 +               */
 +              if (intel_encoder->type == INTEL_OUTPUT_HDMI) {
 +                      if (display_bpc > 8 && display_bpc < 12) {
 +                              DRM_DEBUG_DRIVER("forcing bpc to 12 for HDMI\n");
 +                              display_bpc = 12;
 +                      } else {
 +                              DRM_DEBUG_DRIVER("forcing bpc to 8 for HDMI\n");
 +                              display_bpc = 8;
 +                      }
 +              }
 +      }
 +
 +      /*
 +       * We could just drive the pipe at the highest bpc all the time and
 +       * enable dithering as needed, but that costs bandwidth.  So choose
 +       * the minimum value that expresses the full color range of the fb but
 +       * also stays within the max display bpc discovered above.
 +       */
 +
 +      switch (crtc->fb->depth) {
 +      case 8:
 +              bpc = 8; /* since we go through a colormap */
 +              break;
 +      case 15:
 +      case 16:
 +              bpc = 6; /* min is 18bpp */
 +              break;
 +      case 24:
 +              bpc = min((unsigned int)8, display_bpc);
 +              break;
 +      case 30:
 +              bpc = min((unsigned int)10, display_bpc);
 +              break;
 +      case 48:
 +              bpc = min((unsigned int)12, display_bpc);
 +              break;
 +      default:
 +              DRM_DEBUG("unsupported depth, assuming 24 bits\n");
 +              bpc = min((unsigned int)8, display_bpc);
 +              break;
 +      }
 +
 +      DRM_DEBUG_DRIVER("setting pipe bpc to %d (max display bpc %d)\n",
 +                       bpc, display_bpc);
 +
 +      *pipe_bpp = bpc * 3;
 +
 +      return display_bpc != bpc;
 +}
 +
  static int i9xx_crtc_mode_set(struct drm_crtc *crtc,
                              struct drm_display_mode *mode,
                              struct drm_display_mode *adjusted_mode,
@@@ -5040,9 -4783,7 +5071,9 @@@ static int ironlake_crtc_mode_set(struc
        struct fdi_m_n m_n = {0};
        u32 temp;
        u32 lvds_sync = 0;
 -      int target_clock, pixel_multiplier, lane, link_bw, bpp, factor;
 +      int target_clock, pixel_multiplier, lane, link_bw, factor;
 +      unsigned int pipe_bpp;
 +      bool dither;
  
        list_for_each_entry(encoder, &mode_config->encoder_list, base.head) {
                if (encoder->base.crtc != crtc)
        /* determine panel color depth */
        temp = I915_READ(PIPECONF(pipe));
        temp &= ~PIPE_BPC_MASK;
 -      if (is_lvds) {
 -              /* the BPC will be 6 if it is 18-bit LVDS panel */
 -              if ((I915_READ(PCH_LVDS) & LVDS_A3_POWER_MASK) == LVDS_A3_POWER_UP)
 -                      temp |= PIPE_8BPC;
 -              else
 -                      temp |= PIPE_6BPC;
 -      } else if (has_edp_encoder) {
 -              switch (dev_priv->edp.bpp/3) {
 -              case 8:
 -                      temp |= PIPE_8BPC;
 -                      break;
 -              case 10:
 -                      temp |= PIPE_10BPC;
 -                      break;
 -              case 6:
 -                      temp |= PIPE_6BPC;
 -                      break;
 -              case 12:
 -                      temp |= PIPE_12BPC;
 -                      break;
 -              }
 -      } else
 -              temp |= PIPE_8BPC;
 -      I915_WRITE(PIPECONF(pipe), temp);
 -
 -      switch (temp & PIPE_BPC_MASK) {
 -      case PIPE_8BPC:
 -              bpp = 24;
 +      dither = intel_choose_pipe_bpp_dither(crtc, &pipe_bpp);
 +      switch (pipe_bpp) {
 +      case 18:
 +              temp |= PIPE_6BPC;
                break;
 -      case PIPE_10BPC:
 -              bpp = 30;
 +      case 24:
 +              temp |= PIPE_8BPC;
                break;
 -      case PIPE_6BPC:
 -              bpp = 18;
 +      case 30:
 +              temp |= PIPE_10BPC;
                break;
 -      case PIPE_12BPC:
 -              bpp = 36;
 +      case 36:
 +              temp |= PIPE_12BPC;
                break;
        default:
 -              DRM_ERROR("unknown pipe bpc value\n");
 -              bpp = 24;
 +              WARN(1, "intel_choose_pipe_bpp returned invalid value\n");
 +              temp |= PIPE_8BPC;
 +              pipe_bpp = 24;
 +              break;
        }
  
 +      intel_crtc->bpp = pipe_bpp;
 +      I915_WRITE(PIPECONF(pipe), temp);
 +
        if (!lane) {
                /*
                 * Account for spread spectrum to avoid
                 * oversubscribing the link. Max center spread
                 * is 2.5%; use 5% for safety's sake.
                 */
 -              u32 bps = target_clock * bpp * 21 / 20;
 +              u32 bps = target_clock * intel_crtc->bpp * 21 / 20;
                lane = bps / (link_bw * 8) + 1;
        }
  
  
        if (pixel_multiplier > 1)
                link_bw *= pixel_multiplier;
 -      ironlake_compute_m_n(bpp, lane, target_clock, link_bw, &m_n);
 +      ironlake_compute_m_n(intel_crtc->bpp, lane, target_clock, link_bw,
 +                           &m_n);
  
        /* Ironlake: try to setup display ref clock before DPLL
         * enabling. This is only under driver's control after
        } else if (is_sdvo && is_tv)
                factor = 20;
  
-       if (clock.m1 < factor * clock.n)
+       if (clock.m < factor * clock.n)
                fp |= FP_CB_TUNE;
  
        dpll = 0;
                I915_WRITE(PCH_LVDS, temp);
        }
  
 -      /* set the dithering flag and clear for anything other than a panel. */
        pipeconf &= ~PIPECONF_DITHER_EN;
        pipeconf &= ~PIPECONF_DITHER_TYPE_MASK;
 -      if (dev_priv->lvds_dither && (is_lvds || has_edp_encoder)) {
 +      if ((is_lvds && dev_priv->lvds_dither) || dither) {
                pipeconf |= PIPECONF_DITHER_EN;
                pipeconf |= PIPECONF_DITHER_TYPE_ST1;
        }
 -
        if (is_dp || intel_encoder_is_pch_edp(&has_edp_encoder->base)) {
                intel_dp_set_m_n(crtc, mode, adjusted_mode);
        } else {
@@@ -5738,15 -5499,21 +5769,15 @@@ static int intel_crtc_cursor_set(struc
                        goto fail_locked;
                }
  
 -              ret = i915_gem_object_pin(obj, PAGE_SIZE, true);
 -              if (ret) {
 -                      DRM_ERROR("failed to pin cursor bo\n");
 -                      goto fail_locked;
 -              }
 -
 -              ret = i915_gem_object_set_to_gtt_domain(obj, 0);
 +              ret = i915_gem_object_pin_to_display_plane(obj, 0, NULL);
                if (ret) {
                        DRM_ERROR("failed to move cursor bo into the GTT\n");
 -                      goto fail_unpin;
 +                      goto fail_locked;
                }
  
                ret = i915_gem_object_put_fence(obj);
                if (ret) {
 -                      DRM_ERROR("failed to move cursor bo into the GTT\n");
 +                      DRM_ERROR("failed to release fence for cursor");
                        goto fail_unpin;
                }
  
@@@ -6449,7 -6216,6 +6480,7 @@@ static void intel_unpin_work_fn(struct 
        drm_gem_object_unreference(&work->pending_flip_obj->base);
        drm_gem_object_unreference(&work->old_fb_obj->base);
  
 +      intel_update_fbc(work->dev);
        mutex_unlock(&work->dev->struct_mutex);
        kfree(work);
  }
@@@ -6814,7 -6580,6 +6845,7 @@@ static int intel_crtc_page_flip(struct 
        if (ret)
                goto cleanup_pending;
  
 +      intel_disable_fbc(dev);
        mutex_unlock(&dev->struct_mutex);
  
        trace_i915_flip_request(intel_crtc->plane, obj);
@@@ -6943,7 -6708,6 +6974,7 @@@ static void intel_crtc_init(struct drm_
  
        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 */
  
        if (HAS_PCH_SPLIT(dev)) {
                intel_helper_funcs.prepare = ironlake_crtc_prepare;
@@@ -7170,11 -6934,6 +7201,11 @@@ int intel_framebuffer_init(struct drm_d
        switch (mode_cmd->bpp) {
        case 8:
        case 16:
 +              /* Only pre-ILK can handle 5:5:5 */
 +              if (mode_cmd->depth == 15 && !HAS_PCH_SPLIT(dev))
 +                      return -EINVAL;
 +              break;
 +
        case 24:
        case 32:
                break;
@@@ -7589,59 -7348,6 +7620,59 @@@ void gen6_enable_rps(struct drm_i915_pr
        mutex_unlock(&dev_priv->dev->struct_mutex);
  }
  
 +void gen6_update_ring_freq(struct drm_i915_private *dev_priv)
 +{
 +      int min_freq = 15;
 +      int gpu_freq, ia_freq, max_ia_freq;
 +      int scaling_factor = 180;
 +
 +      max_ia_freq = cpufreq_quick_get_max(0);
 +      /*
 +       * Default to measured freq if none found, PCU will ensure we don't go
 +       * over
 +       */
 +      if (!max_ia_freq)
 +              max_ia_freq = tsc_khz;
 +
 +      /* Convert from kHz to MHz */
 +      max_ia_freq /= 1000;
 +
 +      mutex_lock(&dev_priv->dev->struct_mutex);
 +
 +      /*
 +       * For each potential GPU frequency, load a ring frequency we'd like
 +       * 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;
 +           gpu_freq--) {
 +              int diff = dev_priv->max_delay - gpu_freq;
 +
 +              /*
 +               * For GPU frequencies less than 750MHz, just use the lowest
 +               * ring freq.
 +               */
 +              if (gpu_freq < min_freq)
 +                      ia_freq = 800;
 +              else
 +                      ia_freq = max_ia_freq - ((diff * scaling_factor) / 2);
 +              ia_freq = DIV_ROUND_CLOSEST(ia_freq, 100);
 +
 +              I915_WRITE(GEN6_PCODE_DATA,
 +                         (ia_freq << GEN6_PCODE_FREQ_IA_RATIO_SHIFT) |
 +                         gpu_freq);
 +              I915_WRITE(GEN6_PCODE_MAILBOX, GEN6_PCODE_READY |
 +                         GEN6_PCODE_WRITE_MIN_FREQ_TABLE);
 +              if (wait_for((I915_READ(GEN6_PCODE_MAILBOX) &
 +                            GEN6_PCODE_READY) == 0, 10)) {
 +                      DRM_ERROR("pcode write of freq table timed out\n");
 +                      continue;
 +              }
 +      }
 +
 +      mutex_unlock(&dev_priv->dev->struct_mutex);
 +}
 +
  static void ironlake_init_clock_gating(struct drm_device *dev)
  {
        struct drm_i915_private *dev_priv = dev->dev_private;
@@@ -8006,11 -7712,9 +8037,11 @@@ static void intel_init_display(struct d
        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.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.update_plane = i9xx_update_plane;
        }
  
        if (I915_HAS_FBC(dev)) {
@@@ -8219,6 -7923,9 +8250,9 @@@ struct intel_quirk intel_quirks[] = 
  
        /* Lenovo U160 cannot use SSC on LVDS */
        { 0x0046, 0x17aa, 0x3920, quirk_ssc_force_disable },
+       /* Sony Vaio Y cannot use SSC on LVDS */
+       { 0x0046, 0x104d, 0x9076, quirk_ssc_force_disable },
  };
  
  static void intel_init_quirks(struct drm_device *dev)
@@@ -8307,10 -8014,8 +8341,10 @@@ void intel_modeset_init(struct drm_devi
                intel_init_emon(dev);
        }
  
 -      if (IS_GEN6(dev))
 +      if (IS_GEN6(dev) || IS_GEN7(dev)) {
                gen6_enable_rps(dev_priv);
 +              gen6_update_ring_freq(dev_priv);
 +      }
  
        INIT_WORK(&dev_priv->idle_work, intel_idle_update);
        setup_timer(&dev_priv->idle_timer, intel_gpu_idle_timer,
@@@ -8346,11 -8051,12 +8380,11 @@@ void intel_modeset_cleanup(struct drm_d
                intel_increase_pllclock(crtc);
        }
  
 -      if (dev_priv->display.disable_fbc)
 -              dev_priv->display.disable_fbc(dev);
 +      intel_disable_fbc(dev);
  
        if (IS_IRONLAKE_M(dev))
                ironlake_disable_drps(dev);
 -      if (IS_GEN6(dev))
 +      if (IS_GEN6(dev) || IS_GEN7(dev))
                gen6_disable_rps(dev);
  
        if (IS_IRONLAKE_M(dev))
        drm_irq_uninstall(dev);
        cancel_work_sync(&dev_priv->hotplug_work);
  
 +      /* 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);
index 1ed8e6903915ff542e79ef2cd8becf89b3ba8912,c220255ef1d97b49cb971b3afcfe17a95a717f3c..105e2b892f3a8e247a660fa6820ded468e20a5ab
@@@ -112,6 -112,40 +112,40 @@@ static void intel_hdmi_set_avi_infofram
                   VIDEO_DIP_ENABLE_AVI);
  }
  
+ static void intel_ironlake_hdmi_set_avi_infoframe(struct drm_encoder *encoder)
+ {
+       struct dip_infoframe avi_if = {
+               .type = DIP_TYPE_AVI,
+               .ver = DIP_VERSION_AVI,
+               .len = DIP_LEN_AVI,
+       };
+       uint32_t *data = (uint32_t *)&avi_if;
+       struct drm_device *dev = encoder->dev;
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(encoder);
+       struct drm_crtc *crtc = encoder->crtc;
+       struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
+       int reg = TVIDEO_DIP_CTL(intel_crtc->pipe);
+       unsigned i;
+       if (!intel_hdmi->has_hdmi_sink)
+               return;
+       intel_wait_for_vblank(dev, intel_crtc->pipe);
+       I915_WRITE(reg, VIDEO_DIP_SELECT_AVI);
+       intel_dip_infoframe_csum(&avi_if);
+       for (i = 0; i < sizeof(avi_if); i += 4) {
+               I915_WRITE(TVIDEO_DIP_DATA(intel_crtc->pipe), *data);
+               data++;
+       }
+       I915_WRITE(reg, VIDEO_DIP_ENABLE | VIDEO_DIP_SELECT_AVI |
+                  VIDEO_DIP_FREQ_VSYNC | (DIP_LEN_AVI << 8) |
+                  VIDEO_DIP_ENABLE_AVI);
+ }
  static void intel_hdmi_mode_set(struct drm_encoder *encoder,
                                struct drm_display_mode *mode,
                                struct drm_display_mode *adjusted_mode)
        u32 sdvox;
  
        sdvox = SDVO_ENCODING_HDMI | SDVO_BORDER_ENABLE;
 -      sdvox |= intel_hdmi->color_range;
 +      if (!HAS_PCH_SPLIT(dev))
 +              sdvox |= intel_hdmi->color_range;
        if (adjusted_mode->flags & DRM_MODE_FLAG_PVSYNC)
                sdvox |= SDVO_VSYNC_ACTIVE_HIGH;
        if (adjusted_mode->flags & DRM_MODE_FLAG_PHSYNC)
                sdvox |= SDVO_HSYNC_ACTIVE_HIGH;
  
 +      if (intel_crtc->bpp > 24)
 +              sdvox |= COLOR_FORMAT_12bpc;
 +      else
 +              sdvox |= COLOR_FORMAT_8bpc;
 +
        /* Required on CPT */
        if (intel_hdmi->has_hdmi_sink && HAS_PCH_CPT(dev))
                sdvox |= HDMI_MODE_SELECT;
        I915_WRITE(intel_hdmi->sdvox_reg, sdvox);
        POSTING_READ(intel_hdmi->sdvox_reg);
  
-       intel_hdmi_set_avi_infoframe(encoder);
+       if (HAS_PCH_SPLIT(dev))
+               intel_ironlake_hdmi_set_avi_infoframe(encoder);
+       else
+               intel_hdmi_set_avi_infoframe(encoder);
  }
  
  static void intel_hdmi_dpms(struct drm_encoder *encoder, int mode)
This page took 0.062376 seconds and 5 git commands to generate.