Merge branch 'fix/hda' into for-linus
[pandora-kernel.git] / drivers / gpu / drm / i915 / intel_display.c
index 73e7b9c..508838e 100644 (file)
@@ -25,6 +25,7 @@
  */
 
 #include <linux/i2c.h>
+#include <linux/kernel.h>
 #include "drmP.h"
 #include "intel_drv.h"
 #include "i915_drm.h"
@@ -34,6 +35,7 @@
 #include "drm_crtc_helper.h"
 
 bool intel_pipe_has_type (struct drm_crtc *crtc, int type);
+static void intel_update_watermarks(struct drm_device *dev);
 
 typedef struct {
     /* given values */
@@ -814,24 +816,21 @@ intel_find_pll_g4x_dp(const intel_limit_t *limit, struct drm_crtc *crtc,
 {
     intel_clock_t clock;
     if (target < 200000) {
-       clock.dot = 161670;
-       clock.p = 20;
        clock.p1 = 2;
        clock.p2 = 10;
-       clock.n = 0x01;
-       clock.m = 97;
-       clock.m1 = 0x10;
-       clock.m2 = 0x05;
+       clock.n = 2;
+       clock.m1 = 23;
+       clock.m2 = 8;
     } else {
-       clock.dot = 270000;
-       clock.p = 10;
        clock.p1 = 1;
        clock.p2 = 10;
-       clock.n = 0x02;
-       clock.m = 108;
-       clock.m1 = 0x12;
-       clock.m2 = 0x06;
+       clock.n = 1;
+       clock.m1 = 14;
+       clock.m2 = 2;
     }
+    clock.m = 5 * (clock.m1 + 2) + (clock.m2 + 2);
+    clock.p = (clock.p1 * clock.p2);
+    clock.dot = 96000 * clock.m / (clock.n + 2) / clock.p;
     memcpy(best_clock, &clock, sizeof(intel_clock_t));
     return true;
 }
@@ -1005,7 +1004,7 @@ static void igdng_crtc_dpms(struct drm_crtc *crtc, int mode)
        struct drm_i915_private *dev_priv = dev->dev_private;
        struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
        int pipe = intel_crtc->pipe;
-       int plane = intel_crtc->pipe;
+       int plane = intel_crtc->plane;
        int pch_dpll_reg = (pipe == 0) ? PCH_DPLL_A : PCH_DPLL_B;
        int pipeconf_reg = (pipe == 0) ? PIPEACONF : PIPEBCONF;
        int dspcntr_reg = (plane == 0) ? DSPACNTR : DSPBCNTR;
@@ -1335,8 +1334,10 @@ static void i9xx_crtc_dpms(struct drm_crtc *crtc, int mode)
 
                /* Give the overlay scaler a chance to enable if it's on this pipe */
                //intel_crtc_dpms_video(crtc, true); TODO
+               intel_update_watermarks(dev);
        break;
        case DRM_MODE_DPMS_OFF:
+               intel_update_watermarks(dev);
                /* Give the overlay scaler a chance to disable if it's on this pipe */
                //intel_crtc_dpms_video(crtc, FALSE); TODO
 
@@ -1515,7 +1516,6 @@ static int intel_get_core_clock_speed(struct drm_device *dev)
        return 0; /* Silence gcc warning */
 }
 
-
 /**
  * Return the pipe currently connected to the panel fitter,
  * or -1 if the panel fitter is not present or not in use
@@ -1574,7 +1574,7 @@ igdng_compute_m_n(int bytes_per_pixel, int nlanes,
 
        temp = (u64) DATA_N * pixel_clock;
        temp = div_u64(temp, link_clock);
-       m_n->gmch_m = (temp * bytes_per_pixel) / nlanes;
+       m_n->gmch_m = div_u64(temp * bytes_per_pixel, nlanes);
        m_n->gmch_n = DATA_N;
        fdi_reduce_ratio(&m_n->gmch_m, &m_n->gmch_n);
 
@@ -1585,6 +1585,420 @@ igdng_compute_m_n(int bytes_per_pixel, int nlanes,
 }
 
 
+struct intel_watermark_params {
+       unsigned long fifo_size;
+       unsigned long max_wm;
+       unsigned long default_wm;
+       unsigned long guard_size;
+       unsigned long cacheline_size;
+};
+
+/* IGD has different values for various configs */
+static struct intel_watermark_params igd_display_wm = {
+       IGD_DISPLAY_FIFO,
+       IGD_MAX_WM,
+       IGD_DFT_WM,
+       IGD_GUARD_WM,
+       IGD_FIFO_LINE_SIZE
+};
+static struct intel_watermark_params igd_display_hplloff_wm = {
+       IGD_DISPLAY_FIFO,
+       IGD_MAX_WM,
+       IGD_DFT_HPLLOFF_WM,
+       IGD_GUARD_WM,
+       IGD_FIFO_LINE_SIZE
+};
+static struct intel_watermark_params igd_cursor_wm = {
+       IGD_CURSOR_FIFO,
+       IGD_CURSOR_MAX_WM,
+       IGD_CURSOR_DFT_WM,
+       IGD_CURSOR_GUARD_WM,
+       IGD_FIFO_LINE_SIZE,
+};
+static struct intel_watermark_params igd_cursor_hplloff_wm = {
+       IGD_CURSOR_FIFO,
+       IGD_CURSOR_MAX_WM,
+       IGD_CURSOR_DFT_WM,
+       IGD_CURSOR_GUARD_WM,
+       IGD_FIFO_LINE_SIZE
+};
+static struct intel_watermark_params i945_wm_info = {
+       I915_FIFO_LINE_SIZE,
+       I915_MAX_WM,
+       1,
+       0,
+       IGD_FIFO_LINE_SIZE
+};
+static struct intel_watermark_params i915_wm_info = {
+       I945_FIFO_SIZE,
+       I915_MAX_WM,
+       1,
+       0,
+       I915_FIFO_LINE_SIZE
+};
+static struct intel_watermark_params i855_wm_info = {
+       I855GM_FIFO_SIZE,
+       I915_MAX_WM,
+       1,
+       0,
+       I830_FIFO_LINE_SIZE
+};
+static struct intel_watermark_params i830_wm_info = {
+       I830_FIFO_SIZE,
+       I915_MAX_WM,
+       1,
+       0,
+       I830_FIFO_LINE_SIZE
+};
+
+static unsigned long intel_calculate_wm(unsigned long clock_in_khz,
+                                       struct intel_watermark_params *wm,
+                                       int pixel_size,
+                                       unsigned long latency_ns)
+{
+       unsigned long bytes_required, wm_size;
+
+       bytes_required = (clock_in_khz * pixel_size * latency_ns) / 1000000;
+       bytes_required /= wm->cacheline_size;
+       wm_size = wm->fifo_size - bytes_required - wm->guard_size;
+
+       if (wm_size > wm->max_wm)
+               wm_size = wm->max_wm;
+       if (wm_size == 0)
+               wm_size = wm->default_wm;
+       return wm_size;
+}
+
+struct cxsr_latency {
+       int is_desktop;
+       unsigned long fsb_freq;
+       unsigned long mem_freq;
+       unsigned long display_sr;
+       unsigned long display_hpll_disable;
+       unsigned long cursor_sr;
+       unsigned long cursor_hpll_disable;
+};
+
+static struct cxsr_latency cxsr_latency_table[] = {
+       {1, 800, 400, 3382, 33382, 3983, 33983},    /* DDR2-400 SC */
+       {1, 800, 667, 3354, 33354, 3807, 33807},    /* DDR2-667 SC */
+       {1, 800, 800, 3347, 33347, 3763, 33763},    /* DDR2-800 SC */
+
+       {1, 667, 400, 3400, 33400, 4021, 34021},    /* DDR2-400 SC */
+       {1, 667, 667, 3372, 33372, 3845, 33845},    /* DDR2-667 SC */
+       {1, 667, 800, 3386, 33386, 3822, 33822},    /* DDR2-800 SC */
+
+       {1, 400, 400, 3472, 33472, 4173, 34173},    /* DDR2-400 SC */
+       {1, 400, 667, 3443, 33443, 3996, 33996},    /* DDR2-667 SC */
+       {1, 400, 800, 3430, 33430, 3946, 33946},    /* DDR2-800 SC */
+
+       {0, 800, 400, 3438, 33438, 4065, 34065},    /* DDR2-400 SC */
+       {0, 800, 667, 3410, 33410, 3889, 33889},    /* DDR2-667 SC */
+       {0, 800, 800, 3403, 33403, 3845, 33845},    /* DDR2-800 SC */
+
+       {0, 667, 400, 3456, 33456, 4103, 34106},    /* DDR2-400 SC */
+       {0, 667, 667, 3428, 33428, 3927, 33927},    /* DDR2-667 SC */
+       {0, 667, 800, 3443, 33443, 3905, 33905},    /* DDR2-800 SC */
+
+       {0, 400, 400, 3528, 33528, 4255, 34255},    /* DDR2-400 SC */
+       {0, 400, 667, 3500, 33500, 4079, 34079},    /* DDR2-667 SC */
+       {0, 400, 800, 3487, 33487, 4029, 34029},    /* DDR2-800 SC */
+};
+
+static struct cxsr_latency *intel_get_cxsr_latency(int is_desktop, int fsb,
+                                                  int mem)
+{
+       int i;
+       struct cxsr_latency *latency;
+
+       if (fsb == 0 || mem == 0)
+               return NULL;
+
+       for (i = 0; i < ARRAY_SIZE(cxsr_latency_table); i++) {
+               latency = &cxsr_latency_table[i];
+               if (is_desktop == latency->is_desktop &&
+                       fsb == latency->fsb_freq && mem == latency->mem_freq)
+                       break;
+       }
+       if (i >= ARRAY_SIZE(cxsr_latency_table)) {
+               DRM_DEBUG("Unknown FSB/MEM found, disable CxSR\n");
+               return NULL;
+       }
+       return latency;
+}
+
+static void igd_disable_cxsr(struct drm_device *dev)
+{
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       u32 reg;
+
+       /* deactivate cxsr */
+       reg = I915_READ(DSPFW3);
+       reg &= ~(IGD_SELF_REFRESH_EN);
+       I915_WRITE(DSPFW3, reg);
+       DRM_INFO("Big FIFO is disabled\n");
+}
+
+static void igd_enable_cxsr(struct drm_device *dev, unsigned long clock,
+                           int pixel_size)
+{
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       u32 reg;
+       unsigned long wm;
+       struct cxsr_latency *latency;
+
+       latency = intel_get_cxsr_latency(IS_IGDG(dev), dev_priv->fsb_freq,
+               dev_priv->mem_freq);
+       if (!latency) {
+               DRM_DEBUG("Unknown FSB/MEM found, disable CxSR\n");
+               igd_disable_cxsr(dev);
+               return;
+       }
+
+       /* Display SR */
+       wm = intel_calculate_wm(clock, &igd_display_wm, pixel_size,
+                               latency->display_sr);
+       reg = I915_READ(DSPFW1);
+       reg &= 0x7fffff;
+       reg |= wm << 23;
+       I915_WRITE(DSPFW1, reg);
+       DRM_DEBUG("DSPFW1 register is %x\n", reg);
+
+       /* cursor SR */
+       wm = intel_calculate_wm(clock, &igd_cursor_wm, pixel_size,
+                               latency->cursor_sr);
+       reg = I915_READ(DSPFW3);
+       reg &= ~(0x3f << 24);
+       reg |= (wm & 0x3f) << 24;
+       I915_WRITE(DSPFW3, reg);
+
+       /* Display HPLL off SR */
+       wm = intel_calculate_wm(clock, &igd_display_hplloff_wm,
+               latency->display_hpll_disable, I915_FIFO_LINE_SIZE);
+       reg = I915_READ(DSPFW3);
+       reg &= 0xfffffe00;
+       reg |= wm & 0x1ff;
+       I915_WRITE(DSPFW3, reg);
+
+       /* cursor HPLL off SR */
+       wm = intel_calculate_wm(clock, &igd_cursor_hplloff_wm, pixel_size,
+                               latency->cursor_hpll_disable);
+       reg = I915_READ(DSPFW3);
+       reg &= ~(0x3f << 16);
+       reg |= (wm & 0x3f) << 16;
+       I915_WRITE(DSPFW3, reg);
+       DRM_DEBUG("DSPFW3 register is %x\n", reg);
+
+       /* activate cxsr */
+       reg = I915_READ(DSPFW3);
+       reg |= IGD_SELF_REFRESH_EN;
+       I915_WRITE(DSPFW3, reg);
+
+       DRM_INFO("Big FIFO is enabled\n");
+
+       return;
+}
+
+const static int latency_ns = 5000; /* default for non-igd platforms */
+
+
+static void i965_update_wm(struct drm_device *dev)
+{
+       struct drm_i915_private *dev_priv = dev->dev_private;
+
+       DRM_DEBUG("Setting FIFO watermarks - A: 8, B: 8, C: 8, SR 8\n");
+
+       /* 965 has limitations... */
+       I915_WRITE(DSPFW1, (8 << 16) | (8 << 8) | (8 << 0));
+       I915_WRITE(DSPFW2, (8 << 8) | (8 << 0));
+}
+
+static void i9xx_update_wm(struct drm_device *dev, int planea_clock,
+                          int planeb_clock, int sr_hdisplay, int pixel_size)
+{
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       uint32_t fwater_lo = I915_READ(FW_BLC) & MM_FIFO_WATERMARK;
+       uint32_t fwater_hi = I915_READ(FW_BLC2) & LM_FIFO_WATERMARK;
+       int bsize, asize, cwm, bwm = 1, awm = 1, srwm = 1;
+       uint32_t dsparb = I915_READ(DSPARB);
+       int planea_entries, planeb_entries;
+       struct intel_watermark_params *wm_params;
+       unsigned long line_time_us;
+       int sr_clock, sr_entries = 0;
+
+       if (IS_I965GM(dev) || IS_I945GM(dev))
+               wm_params = &i945_wm_info;
+       else if (IS_I9XX(dev))
+               wm_params = &i915_wm_info;
+       else
+               wm_params = &i855_wm_info;
+
+       planea_entries = intel_calculate_wm(planea_clock, wm_params,
+                                           pixel_size, latency_ns);
+       planeb_entries = intel_calculate_wm(planeb_clock, wm_params,
+                                           pixel_size, latency_ns);
+
+       DRM_DEBUG("FIFO entries - A: %d, B: %d\n", planea_entries,
+                 planeb_entries);
+
+       if (IS_I9XX(dev)) {
+               asize = dsparb & 0x7f;
+               bsize = (dsparb >> DSPARB_CSTART_SHIFT) & 0x7f;
+       } else {
+               asize = dsparb & 0x1ff;
+               bsize = (dsparb >> DSPARB_BEND_SHIFT) & 0x1ff;
+       }
+       DRM_DEBUG("FIFO size - A: %d, B: %d\n", asize, bsize);
+
+       /* Two extra entries for padding */
+       awm = asize - (planea_entries + 2);
+       bwm = bsize - (planeb_entries + 2);
+
+       /* Sanity check against potentially bad FIFO allocations */
+       if (awm <= 0) {
+               /* pipe is on but has too few FIFO entries */
+               if (planea_entries != 0)
+                       DRM_DEBUG("plane A needs more FIFO entries\n");
+               awm = 1;
+       }
+       if (bwm <= 0) {
+               if (planeb_entries != 0)
+                       DRM_DEBUG("plane B needs more FIFO entries\n");
+               bwm = 1;
+       }
+
+       /*
+        * Overlay gets an aggressive default since video jitter is bad.
+        */
+       cwm = 2;
+
+       /* Calc sr entries for one pipe configs */
+       if (!planea_clock || !planeb_clock) {
+               sr_clock = planea_clock ? planea_clock : planeb_clock;
+               line_time_us = (sr_hdisplay * 1000) / sr_clock;
+               sr_entries = (((latency_ns / line_time_us) + 1) * pixel_size *
+                             sr_hdisplay) / 1000;
+               sr_entries = roundup(sr_entries / wm_params->cacheline_size, 1);
+               if (sr_entries < wm_params->fifo_size)
+                       srwm = wm_params->fifo_size - sr_entries;
+       }
+
+       DRM_DEBUG("Setting FIFO watermarks - A: %d, B: %d, C: %d, SR %d\n",
+                 awm, bwm, cwm, srwm);
+
+       fwater_lo = fwater_lo | ((bwm & 0x3f) << 16) | (awm & 0x3f);
+       fwater_hi = fwater_hi | (cwm & 0x1f);
+
+       I915_WRITE(FW_BLC, fwater_lo);
+       I915_WRITE(FW_BLC2, fwater_hi);
+       if (IS_I9XX(dev))
+               I915_WRITE(FW_BLC_SELF, FW_BLC_SELF_EN | (srwm & 0x3f));
+}
+
+static void i830_update_wm(struct drm_device *dev, int planea_clock,
+                          int pixel_size)
+{
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       uint32_t dsparb = I915_READ(DSPARB);
+       uint32_t fwater_lo = I915_READ(FW_BLC) & MM_FIFO_WATERMARK;
+       unsigned int asize, awm;
+       int planea_entries;
+
+       planea_entries = intel_calculate_wm(planea_clock, &i830_wm_info,
+                                           pixel_size, latency_ns);
+
+       asize = dsparb & 0x7f;
+
+       awm = asize - planea_entries;
+
+       fwater_lo = fwater_lo | awm;
+
+       I915_WRITE(FW_BLC, fwater_lo);
+}
+
+/**
+ * intel_update_watermarks - update FIFO watermark values based on current modes
+ *
+ * Calculate watermark values for the various WM regs based on current mode
+ * and plane configuration.
+ *
+ * There are several cases to deal with here:
+ *   - normal (i.e. non-self-refresh)
+ *   - self-refresh (SR) mode
+ *   - lines are large relative to FIFO size (buffer can hold up to 2)
+ *   - lines are small relative to FIFO size (buffer can hold more than 2
+ *     lines), so need to account for TLB latency
+ *
+ *   The normal calculation is:
+ *     watermark = dotclock * bytes per pixel * latency
+ *   where latency is platform & configuration dependent (we assume pessimal
+ *   values here).
+ *
+ *   The SR calculation is:
+ *     watermark = (trunc(latency/line time)+1) * surface width *
+ *       bytes per pixel
+ *   where
+ *     line time = htotal / dotclock
+ *   and latency is assumed to be high, as above.
+ *
+ * The final value programmed to the register should always be rounded up,
+ * and include an extra 2 entries to account for clock crossings.
+ *
+ * We don't use the sprite, so we can ignore that.  And on Crestline we have
+ * to set the non-SR watermarks to 8.
+  */
+static void intel_update_watermarks(struct drm_device *dev)
+{
+       struct drm_crtc *crtc;
+       struct intel_crtc *intel_crtc;
+       int sr_hdisplay = 0;
+       unsigned long planea_clock = 0, planeb_clock = 0, sr_clock = 0;
+       int enabled = 0, pixel_size = 0;
+
+       if (DSPARB_HWCONTROL(dev))
+               return;
+
+       /* Get the clock config from both planes */
+       list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
+               intel_crtc = to_intel_crtc(crtc);
+               if (crtc->enabled) {
+                       enabled++;
+                       if (intel_crtc->plane == 0) {
+                               DRM_DEBUG("plane A (pipe %d) clock: %d\n",
+                                         intel_crtc->pipe, crtc->mode.clock);
+                               planea_clock = crtc->mode.clock;
+                       } else {
+                               DRM_DEBUG("plane B (pipe %d) clock: %d\n",
+                                         intel_crtc->pipe, crtc->mode.clock);
+                               planeb_clock = crtc->mode.clock;
+                       }
+                       sr_hdisplay = crtc->mode.hdisplay;
+                       sr_clock = crtc->mode.clock;
+                       if (crtc->fb)
+                               pixel_size = crtc->fb->bits_per_pixel / 8;
+                       else
+                               pixel_size = 4; /* by default */
+               }
+       }
+
+       if (enabled <= 0)
+               return;
+
+       /* Single pipe configs can enable self refresh */
+       if (enabled == 1 && IS_IGD(dev))
+               igd_enable_cxsr(dev, sr_clock, pixel_size);
+       else if (IS_IGD(dev))
+               igd_disable_cxsr(dev);
+
+       if (IS_I965G(dev))
+               i965_update_wm(dev);
+       else if (IS_I9XX(dev) || IS_MOBILE(dev))
+               i9xx_update_wm(dev, planea_clock, planeb_clock, sr_hdisplay,
+                              pixel_size);
+       else
+               i830_update_wm(dev, planea_clock, pixel_size);
+}
+
 static int intel_crtc_mode_set(struct drm_crtc *crtc,
                               struct drm_display_mode *mode,
                               struct drm_display_mode *adjusted_mode,
@@ -1951,6 +2365,9 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc,
 
        /* Flush the plane changes */
        ret = intel_pipe_set_base(crtc, x, y, old_fb);
+
+       intel_update_watermarks(dev);
+
        drm_vblank_post_modeset(dev, pipe);
 
        return ret;
@@ -2439,6 +2856,7 @@ static void intel_crtc_init(struct drm_device *dev, int pipe)
 
        drm_mode_crtc_set_gamma_size(&intel_crtc->base, 256);
        intel_crtc->pipe = pipe;
+       intel_crtc->plane = pipe;
        for (i = 0; i < 256; i++) {
                intel_crtc->lut_r[i] = i;
                intel_crtc->lut_g[i] = i;