drm/i915: Install fence register for tiled scanout on i915
[safe/jmp/linux-2.6] / drivers / gpu / drm / i915 / intel_display.c
index dce1abf..3e1c781 100644 (file)
@@ -56,11 +56,13 @@ typedef struct {
 } intel_p2_t;
 
 #define INTEL_P2_NUM                 2
-
-typedef struct {
+typedef struct intel_limit intel_limit_t;
+struct intel_limit {
     intel_range_t   dot, vco, n, m, m1, m2, p, p1;
     intel_p2_t     p2;
-} intel_limit_t;
+    bool (* find_pll)(const intel_limit_t *, struct drm_crtc *,
+                     int, int, intel_clock_t *);
+};
 
 #define I8XX_DOT_MIN             25000
 #define I8XX_DOT_MAX            350000
@@ -90,18 +92,32 @@ typedef struct {
 #define I9XX_DOT_MAX            400000
 #define I9XX_VCO_MIN           1400000
 #define I9XX_VCO_MAX           2800000
-#define I9XX_N_MIN                   3
-#define I9XX_N_MAX                   8
+#define IGD_VCO_MIN            1700000
+#define IGD_VCO_MAX            3500000
+#define I9XX_N_MIN                   1
+#define I9XX_N_MAX                   6
+/* IGD's Ncounter is a ring counter */
+#define IGD_N_MIN                    3
+#define IGD_N_MAX                    6
 #define I9XX_M_MIN                  70
 #define I9XX_M_MAX                 120
+#define IGD_M_MIN                    2
+#define IGD_M_MAX                  256
 #define I9XX_M1_MIN                 10
-#define I9XX_M1_MAX                 20
+#define I9XX_M1_MAX                 22
 #define I9XX_M2_MIN                  5
 #define I9XX_M2_MAX                  9
+/* IGD M1 is reserved, and must be 0 */
+#define IGD_M1_MIN                   0
+#define IGD_M1_MAX                   0
+#define IGD_M2_MIN                   0
+#define IGD_M2_MAX                   254
 #define I9XX_P_SDVO_DAC_MIN          5
 #define I9XX_P_SDVO_DAC_MAX         80
 #define I9XX_P_LVDS_MIN                      7
 #define I9XX_P_LVDS_MAX                     98
+#define IGD_P_LVDS_MIN               7
+#define IGD_P_LVDS_MAX              112
 #define I9XX_P1_MIN                  1
 #define I9XX_P1_MAX                  8
 #define I9XX_P2_SDVO_DAC_SLOW               10
@@ -115,6 +131,130 @@ typedef struct {
 #define INTEL_LIMIT_I8XX_LVDS      1
 #define INTEL_LIMIT_I9XX_SDVO_DAC   2
 #define INTEL_LIMIT_I9XX_LVDS      3
+#define INTEL_LIMIT_G4X_SDVO       4
+#define INTEL_LIMIT_G4X_HDMI_DAC   5
+#define INTEL_LIMIT_G4X_SINGLE_CHANNEL_LVDS   6
+#define INTEL_LIMIT_G4X_DUAL_CHANNEL_LVDS   7
+#define INTEL_LIMIT_IGD_SDVO_DAC    8
+#define INTEL_LIMIT_IGD_LVDS       9
+#define INTEL_LIMIT_IGDNG_SDVO_DAC  10
+#define INTEL_LIMIT_IGDNG_LVDS     11
+
+/*The parameter is for SDVO on G4x platform*/
+#define G4X_DOT_SDVO_MIN           25000
+#define G4X_DOT_SDVO_MAX           270000
+#define G4X_VCO_MIN                1750000
+#define G4X_VCO_MAX                3500000
+#define G4X_N_SDVO_MIN             1
+#define G4X_N_SDVO_MAX             4
+#define G4X_M_SDVO_MIN             104
+#define G4X_M_SDVO_MAX             138
+#define G4X_M1_SDVO_MIN            17
+#define G4X_M1_SDVO_MAX            23
+#define G4X_M2_SDVO_MIN            5
+#define G4X_M2_SDVO_MAX            11
+#define G4X_P_SDVO_MIN             10
+#define G4X_P_SDVO_MAX             30
+#define G4X_P1_SDVO_MIN            1
+#define G4X_P1_SDVO_MAX            3
+#define G4X_P2_SDVO_SLOW           10
+#define G4X_P2_SDVO_FAST           10
+#define G4X_P2_SDVO_LIMIT          270000
+
+/*The parameter is for HDMI_DAC on G4x platform*/
+#define G4X_DOT_HDMI_DAC_MIN           22000
+#define G4X_DOT_HDMI_DAC_MAX           400000
+#define G4X_N_HDMI_DAC_MIN             1
+#define G4X_N_HDMI_DAC_MAX             4
+#define G4X_M_HDMI_DAC_MIN             104
+#define G4X_M_HDMI_DAC_MAX             138
+#define G4X_M1_HDMI_DAC_MIN            16
+#define G4X_M1_HDMI_DAC_MAX            23
+#define G4X_M2_HDMI_DAC_MIN            5
+#define G4X_M2_HDMI_DAC_MAX            11
+#define G4X_P_HDMI_DAC_MIN             5
+#define G4X_P_HDMI_DAC_MAX             80
+#define G4X_P1_HDMI_DAC_MIN            1
+#define G4X_P1_HDMI_DAC_MAX            8
+#define G4X_P2_HDMI_DAC_SLOW           10
+#define G4X_P2_HDMI_DAC_FAST           5
+#define G4X_P2_HDMI_DAC_LIMIT          165000
+
+/*The parameter is for SINGLE_CHANNEL_LVDS on G4x platform*/
+#define G4X_DOT_SINGLE_CHANNEL_LVDS_MIN           20000
+#define G4X_DOT_SINGLE_CHANNEL_LVDS_MAX           115000
+#define G4X_N_SINGLE_CHANNEL_LVDS_MIN             1
+#define G4X_N_SINGLE_CHANNEL_LVDS_MAX             3
+#define G4X_M_SINGLE_CHANNEL_LVDS_MIN             104
+#define G4X_M_SINGLE_CHANNEL_LVDS_MAX             138
+#define G4X_M1_SINGLE_CHANNEL_LVDS_MIN            17
+#define G4X_M1_SINGLE_CHANNEL_LVDS_MAX            23
+#define G4X_M2_SINGLE_CHANNEL_LVDS_MIN            5
+#define G4X_M2_SINGLE_CHANNEL_LVDS_MAX            11
+#define G4X_P_SINGLE_CHANNEL_LVDS_MIN             28
+#define G4X_P_SINGLE_CHANNEL_LVDS_MAX             112
+#define G4X_P1_SINGLE_CHANNEL_LVDS_MIN            2
+#define G4X_P1_SINGLE_CHANNEL_LVDS_MAX            8
+#define G4X_P2_SINGLE_CHANNEL_LVDS_SLOW           14
+#define G4X_P2_SINGLE_CHANNEL_LVDS_FAST           14
+#define G4X_P2_SINGLE_CHANNEL_LVDS_LIMIT          0
+
+/*The parameter is for DUAL_CHANNEL_LVDS on G4x platform*/
+#define G4X_DOT_DUAL_CHANNEL_LVDS_MIN           80000
+#define G4X_DOT_DUAL_CHANNEL_LVDS_MAX           224000
+#define G4X_N_DUAL_CHANNEL_LVDS_MIN             1
+#define G4X_N_DUAL_CHANNEL_LVDS_MAX             3
+#define G4X_M_DUAL_CHANNEL_LVDS_MIN             104
+#define G4X_M_DUAL_CHANNEL_LVDS_MAX             138
+#define G4X_M1_DUAL_CHANNEL_LVDS_MIN            17
+#define G4X_M1_DUAL_CHANNEL_LVDS_MAX            23
+#define G4X_M2_DUAL_CHANNEL_LVDS_MIN            5
+#define G4X_M2_DUAL_CHANNEL_LVDS_MAX            11
+#define G4X_P_DUAL_CHANNEL_LVDS_MIN             14
+#define G4X_P_DUAL_CHANNEL_LVDS_MAX             42
+#define G4X_P1_DUAL_CHANNEL_LVDS_MIN            2
+#define G4X_P1_DUAL_CHANNEL_LVDS_MAX            6
+#define G4X_P2_DUAL_CHANNEL_LVDS_SLOW           7
+#define G4X_P2_DUAL_CHANNEL_LVDS_FAST           7
+#define G4X_P2_DUAL_CHANNEL_LVDS_LIMIT          0
+
+/* IGDNG */
+/* as we calculate clock using (register_value + 2) for
+   N/M1/M2, so here the range value for them is (actual_value-2).
+ */
+#define IGDNG_DOT_MIN         25000
+#define IGDNG_DOT_MAX         350000
+#define IGDNG_VCO_MIN         1760000
+#define IGDNG_VCO_MAX         3510000
+#define IGDNG_N_MIN           1
+#define IGDNG_N_MAX           5
+#define IGDNG_M_MIN           79
+#define IGDNG_M_MAX           118
+#define IGDNG_M1_MIN          12
+#define IGDNG_M1_MAX          23
+#define IGDNG_M2_MIN          5
+#define IGDNG_M2_MAX          9
+#define IGDNG_P_SDVO_DAC_MIN  5
+#define IGDNG_P_SDVO_DAC_MAX  80
+#define IGDNG_P_LVDS_MIN      28
+#define IGDNG_P_LVDS_MAX      112
+#define IGDNG_P1_MIN          1
+#define IGDNG_P1_MAX          8
+#define IGDNG_P2_SDVO_DAC_SLOW 10
+#define IGDNG_P2_SDVO_DAC_FAST 5
+#define IGDNG_P2_LVDS_SLOW    14 /* single channel */
+#define IGDNG_P2_LVDS_FAST    7  /* double channel */
+#define IGDNG_P2_DOT_LIMIT    225000 /* 225Mhz */
+
+static bool
+intel_find_best_PLL(const intel_limit_t *limit, struct drm_crtc *crtc,
+                   int target, int refclk, intel_clock_t *best_clock);
+static bool
+intel_g4x_find_best_PLL(const intel_limit_t *limit, struct drm_crtc *crtc,
+                       int target, int refclk, intel_clock_t *best_clock);
+static bool
+intel_igdng_find_best_PLL(const intel_limit_t *limit, struct drm_crtc *crtc,
+                       int target, int refclk, intel_clock_t *best_clock);
 
 static const intel_limit_t intel_limits[] = {
     { /* INTEL_LIMIT_I8XX_DVO_DAC */
@@ -128,6 +268,7 @@ static const intel_limit_t intel_limits[] = {
         .p1  = { .min = I8XX_P1_MIN,           .max = I8XX_P1_MAX },
        .p2  = { .dot_limit = I8XX_P2_SLOW_LIMIT,
                 .p2_slow = I8XX_P2_SLOW,       .p2_fast = I8XX_P2_FAST },
+       .find_pll = intel_find_best_PLL,
     },
     { /* INTEL_LIMIT_I8XX_LVDS */
         .dot = { .min = I8XX_DOT_MIN,          .max = I8XX_DOT_MAX },
@@ -140,6 +281,7 @@ static const intel_limit_t intel_limits[] = {
         .p1  = { .min = I8XX_P1_LVDS_MIN,      .max = I8XX_P1_LVDS_MAX },
        .p2  = { .dot_limit = I8XX_P2_SLOW_LIMIT,
                 .p2_slow = I8XX_P2_LVDS_SLOW,  .p2_fast = I8XX_P2_LVDS_FAST },
+       .find_pll = intel_find_best_PLL,
     },
     { /* INTEL_LIMIT_I9XX_SDVO_DAC */
         .dot = { .min = I9XX_DOT_MIN,          .max = I9XX_DOT_MAX },
@@ -152,6 +294,7 @@ static const intel_limit_t intel_limits[] = {
         .p1  = { .min = I9XX_P1_MIN,           .max = I9XX_P1_MAX },
        .p2  = { .dot_limit = I9XX_P2_SDVO_DAC_SLOW_LIMIT,
                 .p2_slow = I9XX_P2_SDVO_DAC_SLOW,      .p2_fast = I9XX_P2_SDVO_DAC_FAST },
+       .find_pll = intel_find_best_PLL,
     },
     { /* INTEL_LIMIT_I9XX_LVDS */
         .dot = { .min = I9XX_DOT_MIN,          .max = I9XX_DOT_MAX },
@@ -167,19 +310,199 @@ static const intel_limit_t intel_limits[] = {
         */
        .p2  = { .dot_limit = I9XX_P2_LVDS_SLOW_LIMIT,
                 .p2_slow = I9XX_P2_LVDS_SLOW,  .p2_fast = I9XX_P2_LVDS_FAST },
+       .find_pll = intel_find_best_PLL,
+    },
+    /* below parameter and function is for G4X Chipset Family*/
+    { /* INTEL_LIMIT_G4X_SDVO */
+       .dot = { .min = G4X_DOT_SDVO_MIN,       .max = G4X_DOT_SDVO_MAX },
+       .vco = { .min = G4X_VCO_MIN,            .max = G4X_VCO_MAX},
+       .n   = { .min = G4X_N_SDVO_MIN,         .max = G4X_N_SDVO_MAX },
+       .m   = { .min = G4X_M_SDVO_MIN,         .max = G4X_M_SDVO_MAX },
+       .m1  = { .min = G4X_M1_SDVO_MIN,        .max = G4X_M1_SDVO_MAX },
+       .m2  = { .min = G4X_M2_SDVO_MIN,        .max = G4X_M2_SDVO_MAX },
+       .p   = { .min = G4X_P_SDVO_MIN,         .max = G4X_P_SDVO_MAX },
+       .p1  = { .min = G4X_P1_SDVO_MIN,        .max = G4X_P1_SDVO_MAX},
+       .p2  = { .dot_limit = G4X_P2_SDVO_LIMIT,
+                .p2_slow = G4X_P2_SDVO_SLOW,
+                .p2_fast = G4X_P2_SDVO_FAST
+       },
+       .find_pll = intel_g4x_find_best_PLL,
+    },
+    { /* INTEL_LIMIT_G4X_HDMI_DAC */
+       .dot = { .min = G4X_DOT_HDMI_DAC_MIN,   .max = G4X_DOT_HDMI_DAC_MAX },
+       .vco = { .min = G4X_VCO_MIN,            .max = G4X_VCO_MAX},
+       .n   = { .min = G4X_N_HDMI_DAC_MIN,     .max = G4X_N_HDMI_DAC_MAX },
+       .m   = { .min = G4X_M_HDMI_DAC_MIN,     .max = G4X_M_HDMI_DAC_MAX },
+       .m1  = { .min = G4X_M1_HDMI_DAC_MIN,    .max = G4X_M1_HDMI_DAC_MAX },
+       .m2  = { .min = G4X_M2_HDMI_DAC_MIN,    .max = G4X_M2_HDMI_DAC_MAX },
+       .p   = { .min = G4X_P_HDMI_DAC_MIN,     .max = G4X_P_HDMI_DAC_MAX },
+       .p1  = { .min = G4X_P1_HDMI_DAC_MIN,    .max = G4X_P1_HDMI_DAC_MAX},
+       .p2  = { .dot_limit = G4X_P2_HDMI_DAC_LIMIT,
+                .p2_slow = G4X_P2_HDMI_DAC_SLOW,
+                .p2_fast = G4X_P2_HDMI_DAC_FAST
+       },
+       .find_pll = intel_g4x_find_best_PLL,
+    },
+    { /* INTEL_LIMIT_G4X_SINGLE_CHANNEL_LVDS */
+       .dot = { .min = G4X_DOT_SINGLE_CHANNEL_LVDS_MIN,
+                .max = G4X_DOT_SINGLE_CHANNEL_LVDS_MAX },
+       .vco = { .min = G4X_VCO_MIN,
+                .max = G4X_VCO_MAX },
+       .n   = { .min = G4X_N_SINGLE_CHANNEL_LVDS_MIN,
+                .max = G4X_N_SINGLE_CHANNEL_LVDS_MAX },
+       .m   = { .min = G4X_M_SINGLE_CHANNEL_LVDS_MIN,
+                .max = G4X_M_SINGLE_CHANNEL_LVDS_MAX },
+       .m1  = { .min = G4X_M1_SINGLE_CHANNEL_LVDS_MIN,
+                .max = G4X_M1_SINGLE_CHANNEL_LVDS_MAX },
+       .m2  = { .min = G4X_M2_SINGLE_CHANNEL_LVDS_MIN,
+                .max = G4X_M2_SINGLE_CHANNEL_LVDS_MAX },
+       .p   = { .min = G4X_P_SINGLE_CHANNEL_LVDS_MIN,
+                .max = G4X_P_SINGLE_CHANNEL_LVDS_MAX },
+       .p1  = { .min = G4X_P1_SINGLE_CHANNEL_LVDS_MIN,
+                .max = G4X_P1_SINGLE_CHANNEL_LVDS_MAX },
+       .p2  = { .dot_limit = G4X_P2_SINGLE_CHANNEL_LVDS_LIMIT,
+                .p2_slow = G4X_P2_SINGLE_CHANNEL_LVDS_SLOW,
+                .p2_fast = G4X_P2_SINGLE_CHANNEL_LVDS_FAST
+       },
+       .find_pll = intel_g4x_find_best_PLL,
+    },
+    { /* INTEL_LIMIT_G4X_DUAL_CHANNEL_LVDS */
+       .dot = { .min = G4X_DOT_DUAL_CHANNEL_LVDS_MIN,
+                .max = G4X_DOT_DUAL_CHANNEL_LVDS_MAX },
+       .vco = { .min = G4X_VCO_MIN,
+                .max = G4X_VCO_MAX },
+       .n   = { .min = G4X_N_DUAL_CHANNEL_LVDS_MIN,
+                .max = G4X_N_DUAL_CHANNEL_LVDS_MAX },
+       .m   = { .min = G4X_M_DUAL_CHANNEL_LVDS_MIN,
+                .max = G4X_M_DUAL_CHANNEL_LVDS_MAX },
+       .m1  = { .min = G4X_M1_DUAL_CHANNEL_LVDS_MIN,
+                .max = G4X_M1_DUAL_CHANNEL_LVDS_MAX },
+       .m2  = { .min = G4X_M2_DUAL_CHANNEL_LVDS_MIN,
+                .max = G4X_M2_DUAL_CHANNEL_LVDS_MAX },
+       .p   = { .min = G4X_P_DUAL_CHANNEL_LVDS_MIN,
+                .max = G4X_P_DUAL_CHANNEL_LVDS_MAX },
+       .p1  = { .min = G4X_P1_DUAL_CHANNEL_LVDS_MIN,
+                .max = G4X_P1_DUAL_CHANNEL_LVDS_MAX },
+       .p2  = { .dot_limit = G4X_P2_DUAL_CHANNEL_LVDS_LIMIT,
+                .p2_slow = G4X_P2_DUAL_CHANNEL_LVDS_SLOW,
+                .p2_fast = G4X_P2_DUAL_CHANNEL_LVDS_FAST
+       },
+       .find_pll = intel_g4x_find_best_PLL,
+    },
+    { /* INTEL_LIMIT_IGD_SDVO */
+        .dot = { .min = I9XX_DOT_MIN,          .max = I9XX_DOT_MAX},
+        .vco = { .min = IGD_VCO_MIN,           .max = IGD_VCO_MAX },
+        .n   = { .min = IGD_N_MIN,             .max = IGD_N_MAX },
+        .m   = { .min = IGD_M_MIN,             .max = IGD_M_MAX },
+        .m1  = { .min = IGD_M1_MIN,            .max = IGD_M1_MAX },
+        .m2  = { .min = IGD_M2_MIN,            .max = IGD_M2_MAX },
+        .p   = { .min = I9XX_P_SDVO_DAC_MIN,    .max = I9XX_P_SDVO_DAC_MAX },
+        .p1  = { .min = I9XX_P1_MIN,           .max = I9XX_P1_MAX },
+       .p2  = { .dot_limit = I9XX_P2_SDVO_DAC_SLOW_LIMIT,
+                .p2_slow = I9XX_P2_SDVO_DAC_SLOW,      .p2_fast = I9XX_P2_SDVO_DAC_FAST },
+       .find_pll = intel_find_best_PLL,
+    },
+    { /* INTEL_LIMIT_IGD_LVDS */
+        .dot = { .min = I9XX_DOT_MIN,          .max = I9XX_DOT_MAX },
+        .vco = { .min = IGD_VCO_MIN,           .max = IGD_VCO_MAX },
+        .n   = { .min = IGD_N_MIN,             .max = IGD_N_MAX },
+        .m   = { .min = IGD_M_MIN,             .max = IGD_M_MAX },
+        .m1  = { .min = IGD_M1_MIN,            .max = IGD_M1_MAX },
+        .m2  = { .min = IGD_M2_MIN,            .max = IGD_M2_MAX },
+        .p   = { .min = IGD_P_LVDS_MIN,        .max = IGD_P_LVDS_MAX },
+        .p1  = { .min = I9XX_P1_MIN,           .max = I9XX_P1_MAX },
+       /* IGD only supports single-channel mode. */
+       .p2  = { .dot_limit = I9XX_P2_LVDS_SLOW_LIMIT,
+                .p2_slow = I9XX_P2_LVDS_SLOW,  .p2_fast = I9XX_P2_LVDS_SLOW },
+       .find_pll = intel_find_best_PLL,
+    },
+    { /* INTEL_LIMIT_IGDNG_SDVO_DAC */
+       .dot = { .min = IGDNG_DOT_MIN,          .max = IGDNG_DOT_MAX },
+       .vco = { .min = IGDNG_VCO_MIN,          .max = IGDNG_VCO_MAX },
+       .n   = { .min = IGDNG_N_MIN,            .max = IGDNG_N_MAX },
+       .m   = { .min = IGDNG_M_MIN,            .max = IGDNG_M_MAX },
+       .m1  = { .min = IGDNG_M1_MIN,           .max = IGDNG_M1_MAX },
+       .m2  = { .min = IGDNG_M2_MIN,           .max = IGDNG_M2_MAX },
+       .p   = { .min = IGDNG_P_SDVO_DAC_MIN,   .max = IGDNG_P_SDVO_DAC_MAX },
+       .p1  = { .min = IGDNG_P1_MIN,           .max = IGDNG_P1_MAX },
+       .p2  = { .dot_limit = IGDNG_P2_DOT_LIMIT,
+                .p2_slow = IGDNG_P2_SDVO_DAC_SLOW,
+                .p2_fast = IGDNG_P2_SDVO_DAC_FAST },
+       .find_pll = intel_igdng_find_best_PLL,
+    },
+    { /* INTEL_LIMIT_IGDNG_LVDS */
+       .dot = { .min = IGDNG_DOT_MIN,          .max = IGDNG_DOT_MAX },
+       .vco = { .min = IGDNG_VCO_MIN,          .max = IGDNG_VCO_MAX },
+       .n   = { .min = IGDNG_N_MIN,            .max = IGDNG_N_MAX },
+       .m   = { .min = IGDNG_M_MIN,            .max = IGDNG_M_MAX },
+       .m1  = { .min = IGDNG_M1_MIN,           .max = IGDNG_M1_MAX },
+       .m2  = { .min = IGDNG_M2_MIN,           .max = IGDNG_M2_MAX },
+       .p   = { .min = IGDNG_P_LVDS_MIN,       .max = IGDNG_P_LVDS_MAX },
+       .p1  = { .min = IGDNG_P1_MIN,           .max = IGDNG_P1_MAX },
+       .p2  = { .dot_limit = IGDNG_P2_DOT_LIMIT,
+                .p2_slow = IGDNG_P2_LVDS_SLOW,
+                .p2_fast = IGDNG_P2_LVDS_FAST },
+       .find_pll = intel_igdng_find_best_PLL,
     },
 };
 
+static const intel_limit_t *intel_igdng_limit(struct drm_crtc *crtc)
+{
+       const intel_limit_t *limit;
+       if (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS))
+               limit = &intel_limits[INTEL_LIMIT_IGDNG_LVDS];
+       else
+               limit = &intel_limits[INTEL_LIMIT_IGDNG_SDVO_DAC];
+
+       return limit;
+}
+
+static const intel_limit_t *intel_g4x_limit(struct drm_crtc *crtc)
+{
+       struct drm_device *dev = crtc->dev;
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       const intel_limit_t *limit;
+
+       if (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS)) {
+               if ((I915_READ(LVDS) & LVDS_CLKB_POWER_MASK) ==
+                   LVDS_CLKB_POWER_UP)
+                       /* LVDS with dual channel */
+                       limit = &intel_limits
+                                       [INTEL_LIMIT_G4X_DUAL_CHANNEL_LVDS];
+               else
+                       /* LVDS with dual channel */
+                       limit = &intel_limits
+                                       [INTEL_LIMIT_G4X_SINGLE_CHANNEL_LVDS];
+       } else if (intel_pipe_has_type(crtc, INTEL_OUTPUT_HDMI) ||
+                  intel_pipe_has_type(crtc, INTEL_OUTPUT_ANALOG)) {
+               limit = &intel_limits[INTEL_LIMIT_G4X_HDMI_DAC];
+       } else if (intel_pipe_has_type(crtc, INTEL_OUTPUT_SDVO)) {
+               limit = &intel_limits[INTEL_LIMIT_G4X_SDVO];
+       } else /* The option is for other outputs */
+               limit = &intel_limits[INTEL_LIMIT_I9XX_SDVO_DAC];
+
+       return limit;
+}
+
 static const intel_limit_t *intel_limit(struct drm_crtc *crtc)
 {
        struct drm_device *dev = crtc->dev;
        const intel_limit_t *limit;
 
-       if (IS_I9XX(dev)) {
+       if (IS_IGDNG(dev))
+               limit = intel_igdng_limit(crtc);
+       else if (IS_G4X(dev)) {
+               limit = intel_g4x_limit(crtc);
+       } else if (IS_I9XX(dev) && !IS_IGD(dev)) {
                if (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS))
                        limit = &intel_limits[INTEL_LIMIT_I9XX_LVDS];
                else
                        limit = &intel_limits[INTEL_LIMIT_I9XX_SDVO_DAC];
+       } else if (IS_IGD(dev)) {
+               if (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS))
+                       limit = &intel_limits[INTEL_LIMIT_IGD_LVDS];
+               else
+                       limit = &intel_limits[INTEL_LIMIT_IGD_SDVO_DAC];
        } else {
                if (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS))
                        limit = &intel_limits[INTEL_LIMIT_I8XX_LVDS];
@@ -189,35 +512,27 @@ static const intel_limit_t *intel_limit(struct drm_crtc *crtc)
        return limit;
 }
 
-/** Derive the pixel clock for the given refclk and divisors for 8xx chips. */
-
-static void i8xx_clock(int refclk, intel_clock_t *clock)
+/* m1 is reserved as 0 in IGD, n is a ring counter */
+static void igd_clock(int refclk, intel_clock_t *clock)
 {
-       clock->m = 5 * (clock->m1 + 2) + (clock->m2 + 2);
+       clock->m = clock->m2 + 2;
        clock->p = clock->p1 * clock->p2;
-       clock->vco = refclk * clock->m / (clock->n + 2);
+       clock->vco = refclk * clock->m / clock->n;
        clock->dot = clock->vco / clock->p;
 }
 
-/** Derive the pixel clock for the given refclk and divisors for 9xx chips. */
-
-static void i9xx_clock(int refclk, intel_clock_t *clock)
+static void intel_clock(struct drm_device *dev, int refclk, intel_clock_t *clock)
 {
+       if (IS_IGD(dev)) {
+               igd_clock(refclk, clock);
+               return;
+       }
        clock->m = 5 * (clock->m1 + 2) + (clock->m2 + 2);
        clock->p = clock->p1 * clock->p2;
        clock->vco = refclk * clock->m / (clock->n + 2);
        clock->dot = clock->vco / clock->p;
 }
 
-static void intel_clock(struct drm_device *dev, int refclk,
-                       intel_clock_t *clock)
-{
-       if (IS_I9XX(dev))
-               i9xx_clock (refclk, clock);
-       else
-               i8xx_clock (refclk, clock);
-}
-
 /**
  * Returns whether any output on the specified pipe is of the specified type
  */
@@ -238,7 +553,7 @@ bool intel_pipe_has_type (struct drm_crtc *crtc, int type)
     return false;
 }
 
-#define INTELPllInvalid(s)   { /* ErrorF (s) */; return false; }
+#define INTELPllInvalid(s)   do { /* DRM_DEBUG(s); */ return false; } while (0)
 /**
  * Returns whether the given set of divisors are valid for a given refclk with
  * the given connectors.
@@ -247,6 +562,7 @@ bool intel_pipe_has_type (struct drm_crtc *crtc, int type)
 static bool intel_PLL_is_valid(struct drm_crtc *crtc, intel_clock_t *clock)
 {
        const intel_limit_t *limit = intel_limit (crtc);
+       struct drm_device *dev = crtc->dev;
 
        if (clock->p1  < limit->p1.min  || limit->p1.max  < clock->p1)
                INTELPllInvalid ("p1 out of range\n");
@@ -256,7 +572,7 @@ static bool intel_PLL_is_valid(struct drm_crtc *crtc, intel_clock_t *clock)
                INTELPllInvalid ("m2 out of range\n");
        if (clock->m1  < limit->m1.min  || limit->m1.max  < clock->m1)
                INTELPllInvalid ("m1 out of range\n");
-       if (clock->m1 <= clock->m2)
+       if (clock->m1 <= clock->m2 && !IS_IGD(dev))
                INTELPllInvalid ("m1 <= m2\n");
        if (clock->m   < limit->m.min   || limit->m.max   < clock->m)
                INTELPllInvalid ("m out of range\n");
@@ -273,18 +589,14 @@ static bool intel_PLL_is_valid(struct drm_crtc *crtc, intel_clock_t *clock)
        return true;
 }
 
-/**
- * Returns a set of divisors for the desired target clock with the given
- * refclk, or FALSE.  The returned values represent the clock equation:
- * reflck * (5 * (m1 + 2) + (m2 + 2)) / (n + 2) / p1 / p2.
- */
-static bool intel_find_best_PLL(struct drm_crtc *crtc, int target,
-                               int refclk, intel_clock_t *best_clock)
+static bool
+intel_find_best_PLL(const intel_limit_t *limit, struct drm_crtc *crtc,
+                   int target, int refclk, intel_clock_t *best_clock)
+
 {
        struct drm_device *dev = crtc->dev;
        struct drm_i915_private *dev_priv = dev->dev_private;
        intel_clock_t clock;
-       const intel_limit_t *limit = intel_limit(crtc);
        int err = target;
 
        if (IS_I9XX(dev) && intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS) &&
@@ -310,8 +622,10 @@ static bool intel_find_best_PLL(struct drm_crtc *crtc, int target,
        memset (best_clock, 0, sizeof (*best_clock));
 
        for (clock.m1 = limit->m1.min; clock.m1 <= limit->m1.max; clock.m1++) {
-               for (clock.m2 = limit->m2.min; clock.m2 < clock.m1 &&
-                            clock.m2 <= limit->m2.max; clock.m2++) {
+               for (clock.m2 = limit->m2.min; clock.m2 <= limit->m2.max; clock.m2++) {
+                       /* m1 is always 0 in IGD */
+                       if (clock.m2 >= clock.m1 && !IS_IGD(dev))
+                               break;
                        for (clock.n = limit->n.min; clock.n <= limit->n.max;
                             clock.n++) {
                                for (clock.p1 = limit->p1.min;
@@ -336,14 +650,128 @@ static bool intel_find_best_PLL(struct drm_crtc *crtc, int target,
        return (err != target);
 }
 
+static bool
+intel_g4x_find_best_PLL(const intel_limit_t *limit, struct drm_crtc *crtc,
+                       int target, int refclk, intel_clock_t *best_clock)
+{
+       struct drm_device *dev = crtc->dev;
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       intel_clock_t clock;
+       int max_n;
+       bool found;
+       /* approximately equals target * 0.00488 */
+       int err_most = (target >> 8) + (target >> 10);
+       found = false;
+
+       if (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS)) {
+               if ((I915_READ(LVDS) & LVDS_CLKB_POWER_MASK) ==
+                   LVDS_CLKB_POWER_UP)
+                       clock.p2 = limit->p2.p2_fast;
+               else
+                       clock.p2 = limit->p2.p2_slow;
+       } else {
+               if (target < limit->p2.dot_limit)
+                       clock.p2 = limit->p2.p2_slow;
+               else
+                       clock.p2 = limit->p2.p2_fast;
+       }
+
+       memset(best_clock, 0, sizeof(*best_clock));
+       max_n = limit->n.max;
+       /* based on hardware requriment prefer smaller n to precision */
+       for (clock.n = limit->n.min; clock.n <= max_n; clock.n++) {
+               /* based on hardware requirment prefere larger m1,m2, p1 */
+               for (clock.m1 = limit->m1.max;
+                    clock.m1 >= limit->m1.min; clock.m1--) {
+                       for (clock.m2 = limit->m2.max;
+                            clock.m2 >= limit->m2.min; clock.m2--) {
+                               for (clock.p1 = limit->p1.max;
+                                    clock.p1 >= limit->p1.min; clock.p1--) {
+                                       int this_err;
+
+                                       intel_clock(dev, refclk, &clock);
+                                       if (!intel_PLL_is_valid(crtc, &clock))
+                                               continue;
+                                       this_err = abs(clock.dot - target) ;
+                                       if (this_err < err_most) {
+                                               *best_clock = clock;
+                                               err_most = this_err;
+                                               max_n = clock.n;
+                                               found = true;
+                                       }
+                               }
+                       }
+               }
+       }
+       return found;
+}
+
+static bool
+intel_igdng_find_best_PLL(const intel_limit_t *limit, struct drm_crtc *crtc,
+                       int target, int refclk, intel_clock_t *best_clock)
+{
+       struct drm_device *dev = crtc->dev;
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       intel_clock_t clock;
+       int max_n;
+       bool found;
+       int err_most = 47;
+       found = false;
+
+       if (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS)) {
+               if ((I915_READ(LVDS) & LVDS_CLKB_POWER_MASK) ==
+                   LVDS_CLKB_POWER_UP)
+                       clock.p2 = limit->p2.p2_fast;
+               else
+                       clock.p2 = limit->p2.p2_slow;
+       } else {
+               if (target < limit->p2.dot_limit)
+                       clock.p2 = limit->p2.p2_slow;
+               else
+                       clock.p2 = limit->p2.p2_fast;
+       }
+
+       memset(best_clock, 0, sizeof(*best_clock));
+       max_n = limit->n.max;
+       /* based on hardware requriment prefer smaller n to precision */
+       for (clock.n = limit->n.min; clock.n <= max_n; clock.n++) {
+               /* based on hardware requirment prefere larger m1,m2, p1 */
+               for (clock.m1 = limit->m1.max;
+                    clock.m1 >= limit->m1.min; clock.m1--) {
+                       for (clock.m2 = limit->m2.max;
+                            clock.m2 >= limit->m2.min; clock.m2--) {
+                               for (clock.p1 = limit->p1.max;
+                                    clock.p1 >= limit->p1.min; clock.p1--) {
+                                       int this_err;
+
+                                       intel_clock(dev, refclk, &clock);
+                                       if (!intel_PLL_is_valid(crtc, &clock))
+                                               continue;
+                                       this_err = abs((10000 - (target*10000/clock.dot)));
+                                       if (this_err < err_most) {
+                                               *best_clock = clock;
+                                               err_most = this_err;
+                                               max_n = clock.n;
+                                               found = true;
+                                               /* found on first matching */
+                                               goto out;
+                                       }
+                               }
+                       }
+               }
+       }
+out:
+       return found;
+}
+
 void
 intel_wait_for_vblank(struct drm_device *dev)
 {
        /* Wait for 20ms, i.e. one cycle at 50hz. */
-       udelay(20000);
+       mdelay(20);
 }
 
-static void
+static int
 intel_pipe_set_base(struct drm_crtc *crtc, int x, int y,
                    struct drm_framebuffer *old_fb)
 {
@@ -359,13 +787,24 @@ intel_pipe_set_base(struct drm_crtc *crtc, int x, int y,
        int dspbase = (pipe == 0 ? DSPAADDR : DSPBADDR);
        int dspsurf = (pipe == 0 ? DSPASURF : DSPBSURF);
        int dspstride = (pipe == 0) ? DSPASTRIDE : DSPBSTRIDE;
+       int dsptileoff = (pipe == 0 ? DSPATILEOFF : DSPBTILEOFF);
        int dspcntr_reg = (pipe == 0) ? DSPACNTR : DSPBCNTR;
        u32 dspcntr, alignment;
+       int ret;
 
        /* no fb bound */
        if (!crtc->fb) {
                DRM_DEBUG("No FB bound\n");
-               return;
+               return 0;
+       }
+
+       switch (pipe) {
+       case 0:
+       case 1:
+               break;
+       default:
+               DRM_ERROR("Can't update pipe %d in SAREA\n", pipe);
+               return -EINVAL;
        }
 
        intel_fb = to_intel_framebuffer(crtc->fb);
@@ -377,28 +816,42 @@ intel_pipe_set_base(struct drm_crtc *crtc, int x, int y,
                alignment = 64 * 1024;
                break;
        case I915_TILING_X:
-               if (IS_I9XX(dev))
-                       alignment = 1024 * 1024;
-               else
-                       alignment = 512 * 1024;
+               /* pin() will align the object as required by fence */
+               alignment = 0;
                break;
        case I915_TILING_Y:
                /* FIXME: Is this true? */
                DRM_ERROR("Y tiled not allowed for scan out buffers\n");
-               return;
+               return -EINVAL;
        default:
                BUG();
        }
 
-       if (i915_gem_object_pin(intel_fb->obj, alignment))
-               return;
-
-       i915_gem_object_set_to_gtt_domain(intel_fb->obj, 1);
+       mutex_lock(&dev->struct_mutex);
+       ret = i915_gem_object_pin(obj, alignment);
+       if (ret != 0) {
+               mutex_unlock(&dev->struct_mutex);
+               return ret;
+       }
 
-       Start = obj_priv->gtt_offset;
-       Offset = y * crtc->fb->pitch + x * (crtc->fb->bits_per_pixel / 8);
+       ret = i915_gem_object_set_to_gtt_domain(obj, 1);
+       if (ret != 0) {
+               i915_gem_object_unpin(obj);
+               mutex_unlock(&dev->struct_mutex);
+               return ret;
+       }
 
-       I915_WRITE(dspstride, crtc->fb->pitch);
+       /* Pre-i965 needs to install a fence for tiled scan-out */
+       if (!IS_I965G(dev) &&
+           obj_priv->fence_reg == I915_FENCE_REG_NONE &&
+           obj_priv->tiling_mode != I915_TILING_NONE) {
+               ret = i915_gem_object_get_fence_reg(obj);
+               if (ret != 0) {
+                       i915_gem_object_unpin(obj);
+                       mutex_unlock(&dev->struct_mutex);
+                       return ret;
+               }
+       }
 
        dspcntr = I915_READ(dspcntr_reg);
        /* Mask out pixel format bits in case we change it */
@@ -419,16 +872,30 @@ intel_pipe_set_base(struct drm_crtc *crtc, int x, int y,
                break;
        default:
                DRM_ERROR("Unknown color depth\n");
-               return;
+               i915_gem_object_unpin(obj);
+               mutex_unlock(&dev->struct_mutex);
+               return -EINVAL;
+       }
+       if (IS_I965G(dev)) {
+               if (obj_priv->tiling_mode != I915_TILING_NONE)
+                       dspcntr |= DISPPLANE_TILED;
+               else
+                       dspcntr &= ~DISPPLANE_TILED;
        }
+
        I915_WRITE(dspcntr_reg, dspcntr);
 
+       Start = obj_priv->gtt_offset;
+       Offset = y * crtc->fb->pitch + x * (crtc->fb->bits_per_pixel / 8);
+
        DRM_DEBUG("Writing base %08lX %08lX %d %d\n", Start, Offset, x, y);
+       I915_WRITE(dspstride, crtc->fb->pitch);
        if (IS_I965G(dev)) {
                I915_WRITE(dspbase, Offset);
                I915_READ(dspbase);
                I915_WRITE(dspsurf, Start);
                I915_READ(dspsurf);
+               I915_WRITE(dsptileoff, (y << 16) | x);
        } else {
                I915_WRITE(dspbase, Start + Offset);
                I915_READ(dspbase);
@@ -440,41 +907,312 @@ intel_pipe_set_base(struct drm_crtc *crtc, int x, int y,
                intel_fb = to_intel_framebuffer(old_fb);
                i915_gem_object_unpin(intel_fb->obj);
        }
+       mutex_unlock(&dev->struct_mutex);
 
        if (!dev->primary->master)
-               return;
+               return 0;
 
        master_priv = dev->primary->master->driver_priv;
        if (!master_priv->sarea_priv)
-               return;
+               return 0;
 
-       switch (pipe) {
-       case 0:
-               master_priv->sarea_priv->pipeA_x = x;
-               master_priv->sarea_priv->pipeA_y = y;
-               break;
-       case 1:
+       if (pipe) {
                master_priv->sarea_priv->pipeB_x = x;
                master_priv->sarea_priv->pipeB_y = y;
-               break;
-       default:
-               DRM_ERROR("Can't update pipe %d in SAREA\n", pipe);
-               break;
+       } else {
+               master_priv->sarea_priv->pipeA_x = x;
+               master_priv->sarea_priv->pipeA_y = y;
        }
+
+       return 0;
 }
 
+static void igdng_crtc_dpms(struct drm_crtc *crtc, int mode)
+{
+       struct drm_device *dev = crtc->dev;
+       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 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;
+       int dspbase_reg = (plane == 0) ? DSPAADDR : DSPBADDR;
+       int fdi_tx_reg = (pipe == 0) ? FDI_TXA_CTL : FDI_TXB_CTL;
+       int fdi_rx_reg = (pipe == 0) ? FDI_RXA_CTL : FDI_RXB_CTL;
+       int fdi_rx_iir_reg = (pipe == 0) ? FDI_RXA_IIR : FDI_RXB_IIR;
+       int fdi_rx_imr_reg = (pipe == 0) ? FDI_RXA_IMR : FDI_RXB_IMR;
+       int transconf_reg = (pipe == 0) ? TRANSACONF : TRANSBCONF;
+       int pf_ctl_reg = (pipe == 0) ? PFA_CTL_1 : PFB_CTL_1;
+       int cpu_htot_reg = (pipe == 0) ? HTOTAL_A : HTOTAL_B;
+       int cpu_hblank_reg = (pipe == 0) ? HBLANK_A : HBLANK_B;
+       int cpu_hsync_reg = (pipe == 0) ? HSYNC_A : HSYNC_B;
+       int cpu_vtot_reg = (pipe == 0) ? VTOTAL_A : VTOTAL_B;
+       int cpu_vblank_reg = (pipe == 0) ? VBLANK_A : VBLANK_B;
+       int cpu_vsync_reg = (pipe == 0) ? VSYNC_A : VSYNC_B;
+       int trans_htot_reg = (pipe == 0) ? TRANS_HTOTAL_A : TRANS_HTOTAL_B;
+       int trans_hblank_reg = (pipe == 0) ? TRANS_HBLANK_A : TRANS_HBLANK_B;
+       int trans_hsync_reg = (pipe == 0) ? TRANS_HSYNC_A : TRANS_HSYNC_B;
+       int trans_vtot_reg = (pipe == 0) ? TRANS_VTOTAL_A : TRANS_VTOTAL_B;
+       int trans_vblank_reg = (pipe == 0) ? TRANS_VBLANK_A : TRANS_VBLANK_B;
+       int trans_vsync_reg = (pipe == 0) ? TRANS_VSYNC_A : TRANS_VSYNC_B;
+       u32 temp;
+       int tries = 5, j;
 
+       /* XXX: When our outputs are all unaware of DPMS modes other than off
+        * and on, we should map those modes to DRM_MODE_DPMS_OFF in the CRTC.
+        */
+       switch (mode) {
+       case DRM_MODE_DPMS_ON:
+       case DRM_MODE_DPMS_STANDBY:
+       case DRM_MODE_DPMS_SUSPEND:
+               DRM_DEBUG("crtc %d dpms on\n", pipe);
+               /* enable PCH DPLL */
+               temp = I915_READ(pch_dpll_reg);
+               if ((temp & DPLL_VCO_ENABLE) == 0) {
+                       I915_WRITE(pch_dpll_reg, temp | DPLL_VCO_ENABLE);
+                       I915_READ(pch_dpll_reg);
+               }
 
-/**
- * Sets the power management mode of the pipe and plane.
- *
- * This code should probably grow support for turning the cursor off and back
- * on appropriately at the same time as we're turning the pipe off/on.
- */
-static void intel_crtc_dpms(struct drm_crtc *crtc, int mode)
+               /* enable PCH FDI RX PLL, wait warmup plus DMI latency */
+               temp = I915_READ(fdi_rx_reg);
+               I915_WRITE(fdi_rx_reg, temp | FDI_RX_PLL_ENABLE |
+                               FDI_SEL_PCDCLK |
+                               FDI_DP_PORT_WIDTH_X4); /* default 4 lanes */
+               I915_READ(fdi_rx_reg);
+               udelay(200);
+
+               /* Enable CPU FDI TX PLL, always on for IGDNG */
+               temp = I915_READ(fdi_tx_reg);
+               if ((temp & FDI_TX_PLL_ENABLE) == 0) {
+                       I915_WRITE(fdi_tx_reg, temp | FDI_TX_PLL_ENABLE);
+                       I915_READ(fdi_tx_reg);
+                       udelay(100);
+               }
+
+               /* Enable CPU pipe */
+               temp = I915_READ(pipeconf_reg);
+               if ((temp & PIPEACONF_ENABLE) == 0) {
+                       I915_WRITE(pipeconf_reg, temp | PIPEACONF_ENABLE);
+                       I915_READ(pipeconf_reg);
+                       udelay(100);
+               }
+
+               /* configure and enable CPU plane */
+               temp = I915_READ(dspcntr_reg);
+               if ((temp & DISPLAY_PLANE_ENABLE) == 0) {
+                       I915_WRITE(dspcntr_reg, temp | DISPLAY_PLANE_ENABLE);
+                       /* Flush the plane changes */
+                       I915_WRITE(dspbase_reg, I915_READ(dspbase_reg));
+               }
+
+               /* enable CPU FDI TX and PCH FDI RX */
+               temp = I915_READ(fdi_tx_reg);
+               temp |= FDI_TX_ENABLE;
+               temp |= FDI_DP_PORT_WIDTH_X4; /* default */
+               temp &= ~FDI_LINK_TRAIN_NONE;
+               temp |= FDI_LINK_TRAIN_PATTERN_1;
+               I915_WRITE(fdi_tx_reg, temp);
+               I915_READ(fdi_tx_reg);
+
+               temp = I915_READ(fdi_rx_reg);
+               temp &= ~FDI_LINK_TRAIN_NONE;
+               temp |= FDI_LINK_TRAIN_PATTERN_1;
+               I915_WRITE(fdi_rx_reg, temp | FDI_RX_ENABLE);
+               I915_READ(fdi_rx_reg);
+
+               udelay(150);
+
+               /* Train FDI. */
+               /* umask FDI RX Interrupt symbol_lock and bit_lock bit
+                  for train result */
+               temp = I915_READ(fdi_rx_imr_reg);
+               temp &= ~FDI_RX_SYMBOL_LOCK;
+               temp &= ~FDI_RX_BIT_LOCK;
+               I915_WRITE(fdi_rx_imr_reg, temp);
+               I915_READ(fdi_rx_imr_reg);
+               udelay(150);
+
+               temp = I915_READ(fdi_rx_iir_reg);
+               DRM_DEBUG("FDI_RX_IIR 0x%x\n", temp);
+
+               if ((temp & FDI_RX_BIT_LOCK) == 0) {
+                       for (j = 0; j < tries; j++) {
+                               temp = I915_READ(fdi_rx_iir_reg);
+                               DRM_DEBUG("FDI_RX_IIR 0x%x\n", temp);
+                               if (temp & FDI_RX_BIT_LOCK)
+                                       break;
+                               udelay(200);
+                       }
+                       if (j != tries)
+                               I915_WRITE(fdi_rx_iir_reg,
+                                               temp | FDI_RX_BIT_LOCK);
+                       else
+                               DRM_DEBUG("train 1 fail\n");
+               } else {
+                       I915_WRITE(fdi_rx_iir_reg,
+                                       temp | FDI_RX_BIT_LOCK);
+                       DRM_DEBUG("train 1 ok 2!\n");
+               }
+               temp = I915_READ(fdi_tx_reg);
+               temp &= ~FDI_LINK_TRAIN_NONE;
+               temp |= FDI_LINK_TRAIN_PATTERN_2;
+               I915_WRITE(fdi_tx_reg, temp);
+
+               temp = I915_READ(fdi_rx_reg);
+               temp &= ~FDI_LINK_TRAIN_NONE;
+               temp |= FDI_LINK_TRAIN_PATTERN_2;
+               I915_WRITE(fdi_rx_reg, temp);
+
+               udelay(150);
+
+               temp = I915_READ(fdi_rx_iir_reg);
+               DRM_DEBUG("FDI_RX_IIR 0x%x\n", temp);
+
+               if ((temp & FDI_RX_SYMBOL_LOCK) == 0) {
+                       for (j = 0; j < tries; j++) {
+                               temp = I915_READ(fdi_rx_iir_reg);
+                               DRM_DEBUG("FDI_RX_IIR 0x%x\n", temp);
+                               if (temp & FDI_RX_SYMBOL_LOCK)
+                                       break;
+                               udelay(200);
+                       }
+                       if (j != tries) {
+                               I915_WRITE(fdi_rx_iir_reg,
+                                               temp | FDI_RX_SYMBOL_LOCK);
+                               DRM_DEBUG("train 2 ok 1!\n");
+                       } else
+                               DRM_DEBUG("train 2 fail\n");
+               } else {
+                       I915_WRITE(fdi_rx_iir_reg, temp | FDI_RX_SYMBOL_LOCK);
+                       DRM_DEBUG("train 2 ok 2!\n");
+               }
+               DRM_DEBUG("train done\n");
+
+               /* set transcoder timing */
+               I915_WRITE(trans_htot_reg, I915_READ(cpu_htot_reg));
+               I915_WRITE(trans_hblank_reg, I915_READ(cpu_hblank_reg));
+               I915_WRITE(trans_hsync_reg, I915_READ(cpu_hsync_reg));
+
+               I915_WRITE(trans_vtot_reg, I915_READ(cpu_vtot_reg));
+               I915_WRITE(trans_vblank_reg, I915_READ(cpu_vblank_reg));
+               I915_WRITE(trans_vsync_reg, I915_READ(cpu_vsync_reg));
+
+               /* enable PCH transcoder */
+               temp = I915_READ(transconf_reg);
+               I915_WRITE(transconf_reg, temp | TRANS_ENABLE);
+               I915_READ(transconf_reg);
+
+               while ((I915_READ(transconf_reg) & TRANS_STATE_ENABLE) == 0)
+                       ;
+
+               /* enable normal */
+
+               temp = I915_READ(fdi_tx_reg);
+               temp &= ~FDI_LINK_TRAIN_NONE;
+               I915_WRITE(fdi_tx_reg, temp | FDI_LINK_TRAIN_NONE |
+                               FDI_TX_ENHANCE_FRAME_ENABLE);
+               I915_READ(fdi_tx_reg);
+
+               temp = I915_READ(fdi_rx_reg);
+               temp &= ~FDI_LINK_TRAIN_NONE;
+               I915_WRITE(fdi_rx_reg, temp | FDI_LINK_TRAIN_NONE |
+                               FDI_RX_ENHANCE_FRAME_ENABLE);
+               I915_READ(fdi_rx_reg);
+
+               /* wait one idle pattern time */
+               udelay(100);
+
+               intel_crtc_load_lut(crtc);
+
+       break;
+       case DRM_MODE_DPMS_OFF:
+               DRM_DEBUG("crtc %d dpms off\n", pipe);
+
+               /* Disable the VGA plane that we never use */
+               I915_WRITE(CPU_VGACNTRL, VGA_DISP_DISABLE);
+
+               /* Disable display plane */
+               temp = I915_READ(dspcntr_reg);
+               if ((temp & DISPLAY_PLANE_ENABLE) != 0) {
+                       I915_WRITE(dspcntr_reg, temp & ~DISPLAY_PLANE_ENABLE);
+                       /* Flush the plane changes */
+                       I915_WRITE(dspbase_reg, I915_READ(dspbase_reg));
+                       I915_READ(dspbase_reg);
+               }
+
+               /* disable cpu pipe, disable after all planes disabled */
+               temp = I915_READ(pipeconf_reg);
+               if ((temp & PIPEACONF_ENABLE) != 0) {
+                       I915_WRITE(pipeconf_reg, temp & ~PIPEACONF_ENABLE);
+                       I915_READ(pipeconf_reg);
+                       /* wait for cpu pipe off, pipe state */
+                       while ((I915_READ(pipeconf_reg) & I965_PIPECONF_ACTIVE) != 0)
+                               ;
+               } else
+                       DRM_DEBUG("crtc %d is disabled\n", pipe);
+
+               /* IGDNG-A : disable cpu panel fitter ? */
+               temp = I915_READ(pf_ctl_reg);
+               if ((temp & PF_ENABLE) != 0) {
+                       I915_WRITE(pf_ctl_reg, temp & ~PF_ENABLE);
+                       I915_READ(pf_ctl_reg);
+               }
+
+               /* disable CPU FDI tx and PCH FDI rx */
+               temp = I915_READ(fdi_tx_reg);
+               I915_WRITE(fdi_tx_reg, temp & ~FDI_TX_ENABLE);
+               I915_READ(fdi_tx_reg);
+
+               temp = I915_READ(fdi_rx_reg);
+               I915_WRITE(fdi_rx_reg, temp & ~FDI_RX_ENABLE);
+               I915_READ(fdi_rx_reg);
+
+               /* still set train pattern 1 */
+               temp = I915_READ(fdi_tx_reg);
+               temp &= ~FDI_LINK_TRAIN_NONE;
+               temp |= FDI_LINK_TRAIN_PATTERN_1;
+               I915_WRITE(fdi_tx_reg, temp);
+
+               temp = I915_READ(fdi_rx_reg);
+               temp &= ~FDI_LINK_TRAIN_NONE;
+               temp |= FDI_LINK_TRAIN_PATTERN_1;
+               I915_WRITE(fdi_rx_reg, temp);
+
+               /* disable PCH transcoder */
+               temp = I915_READ(transconf_reg);
+               if ((temp & TRANS_ENABLE) != 0) {
+                       I915_WRITE(transconf_reg, temp & ~TRANS_ENABLE);
+                       I915_READ(transconf_reg);
+                       /* wait for PCH transcoder off, transcoder state */
+                       while ((I915_READ(transconf_reg) & TRANS_STATE_ENABLE) != 0)
+                               ;
+               }
+
+               /* disable PCH DPLL */
+               temp = I915_READ(pch_dpll_reg);
+               if ((temp & DPLL_VCO_ENABLE) != 0) {
+                       I915_WRITE(pch_dpll_reg, temp & ~DPLL_VCO_ENABLE);
+                       I915_READ(pch_dpll_reg);
+               }
+
+               temp = I915_READ(fdi_rx_reg);
+               if ((temp & FDI_RX_PLL_ENABLE) != 0) {
+                       temp &= ~FDI_SEL_PCDCLK;
+                       temp &= ~FDI_RX_PLL_ENABLE;
+                       I915_WRITE(fdi_rx_reg, temp);
+                       I915_READ(fdi_rx_reg);
+               }
+
+               /* Wait for the clocks to turn off. */
+               udelay(150);
+               break;
+       }
+}
+
+static void i9xx_crtc_dpms(struct drm_crtc *crtc, int mode)
 {
        struct drm_device *dev = crtc->dev;
-       struct drm_i915_master_private *master_priv;
        struct drm_i915_private *dev_priv = dev->dev_private;
        struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
        int pipe = intel_crtc->pipe;
@@ -483,7 +1221,6 @@ static void intel_crtc_dpms(struct drm_crtc *crtc, int mode)
        int dspbase_reg = (pipe == 0) ? DSPAADDR : DSPBADDR;
        int pipeconf_reg = (pipe == 0) ? PIPEACONF : PIPEBCONF;
        u32 temp;
-       bool enabled;
 
        /* XXX: When our outputs are all unaware of DPMS modes other than off
         * and on, we should map those modes to DRM_MODE_DPMS_OFF in the CRTC.
@@ -568,6 +1305,26 @@ static void intel_crtc_dpms(struct drm_crtc *crtc, int mode)
                udelay(150);
                break;
        }
+}
+
+/**
+ * Sets the power management mode of the pipe and plane.
+ *
+ * This code should probably grow support for turning the cursor off and back
+ * on appropriately at the same time as we're turning the pipe off/on.
+ */
+static void intel_crtc_dpms(struct drm_crtc *crtc, int mode)
+{
+       struct drm_device *dev = crtc->dev;
+       struct drm_i915_master_private *master_priv;
+       struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
+       int pipe = intel_crtc->pipe;
+       bool enabled;
+
+       if (IS_IGDNG(dev))
+               igdng_crtc_dpms(crtc, mode);
+       else
+               i9xx_crtc_dpms(crtc, mode);
 
        if (!dev->primary->master)
                return;
@@ -625,6 +1382,12 @@ static bool intel_crtc_mode_fixup(struct drm_crtc *crtc,
                                  struct drm_display_mode *mode,
                                  struct drm_display_mode *adjusted_mode)
 {
+       struct drm_device *dev = crtc->dev;
+       if (IS_IGDNG(dev)) {
+               /* FDI link clock is fixed at 2.7G */
+               if (mode->clock * 3 > 27000 * 4)
+                       return MODE_CLOCK_HIGH;
+       }
        return true;
 }
 
@@ -640,7 +1403,7 @@ static int intel_get_core_clock_speed(struct drm_device *dev)
                return 400000;
        else if (IS_I915G(dev))
                return 333000;
-       else if (IS_I945GM(dev) || IS_845G(dev))
+       else if (IS_I945GM(dev) || IS_845G(dev) || IS_IGDGM(dev))
                return 200000;
        else if (IS_I915GM(dev)) {
                u16 gcfgc = 0;
@@ -708,11 +1471,53 @@ static int intel_panel_fitter_pipe (struct drm_device *dev)
        return 1;
 }
 
-static void intel_crtc_mode_set(struct drm_crtc *crtc,
-                               struct drm_display_mode *mode,
-                               struct drm_display_mode *adjusted_mode,
-                               int x, int y,
-                               struct drm_framebuffer *old_fb)
+struct fdi_m_n {
+       u32        tu;
+       u32        gmch_m;
+       u32        gmch_n;
+       u32        link_m;
+       u32        link_n;
+};
+
+static void
+fdi_reduce_ratio(u32 *num, u32 *den)
+{
+       while (*num > 0xffffff || *den > 0xffffff) {
+               *num >>= 1;
+               *den >>= 1;
+       }
+}
+
+#define DATA_N 0x800000
+#define LINK_N 0x80000
+
+static void
+igdng_compute_m_n(int bytes_per_pixel, int nlanes,
+               int pixel_clock, int link_clock,
+               struct fdi_m_n *m_n)
+{
+       u64 temp;
+
+       m_n->tu = 64; /* default size */
+
+       temp = (u64) DATA_N * pixel_clock;
+       temp = div_u64(temp, link_clock);
+       m_n->gmch_m = (temp * bytes_per_pixel) / nlanes;
+       m_n->gmch_n = DATA_N;
+       fdi_reduce_ratio(&m_n->gmch_m, &m_n->gmch_n);
+
+       temp = (u64) LINK_N * pixel_clock;
+       m_n->link_m = div_u64(temp, link_clock);
+       m_n->link_n = LINK_N;
+       fdi_reduce_ratio(&m_n->link_m, &m_n->link_n);
+}
+
+
+static int intel_crtc_mode_set(struct drm_crtc *crtc,
+                              struct drm_display_mode *mode,
+                              struct drm_display_mode *adjusted_mode,
+                              int x, int y,
+                              struct drm_framebuffer *old_fb)
 {
        struct drm_device *dev = crtc->dev;
        struct drm_i915_private *dev_priv = dev->dev_private;
@@ -732,13 +1537,26 @@ static void intel_crtc_mode_set(struct drm_crtc *crtc,
        int dspsize_reg = (pipe == 0) ? DSPASIZE : DSPBSIZE;
        int dsppos_reg = (pipe == 0) ? DSPAPOS : DSPBPOS;
        int pipesrc_reg = (pipe == 0) ? PIPEASRC : PIPEBSRC;
-       int refclk;
+       int refclk, num_outputs = 0;
        intel_clock_t clock;
        u32 dpll = 0, fp = 0, dspcntr, pipeconf;
        bool ok, is_sdvo = false, is_dvo = false;
        bool is_crt = false, is_lvds = false, is_tv = false;
        struct drm_mode_config *mode_config = &dev->mode_config;
        struct drm_connector *connector;
+       const intel_limit_t *limit;
+       int ret;
+       struct fdi_m_n m_n = {0};
+       int data_m1_reg = (pipe == 0) ? PIPEA_DATA_M1 : PIPEB_DATA_M1;
+       int data_n1_reg = (pipe == 0) ? PIPEA_DATA_N1 : PIPEB_DATA_N1;
+       int link_m1_reg = (pipe == 0) ? PIPEA_LINK_M1 : PIPEB_LINK_M1;
+       int link_n1_reg = (pipe == 0) ? PIPEA_LINK_N1 : PIPEB_LINK_N1;
+       int pch_fp_reg = (pipe == 0) ? PCH_FPA0 : PCH_FPB0;
+       int pch_dpll_reg = (pipe == 0) ? PCH_DPLL_A : PCH_DPLL_B;
+       int fdi_rx_reg = (pipe == 0) ? FDI_RXA_CTL : FDI_RXB_CTL;
+       int lvds_reg = LVDS;
+       u32 temp;
+       int sdvo_pixel_multiply;
 
        drm_vblank_pre_modeset(dev, pipe);
 
@@ -755,6 +1573,8 @@ static void intel_crtc_mode_set(struct drm_crtc *crtc,
                case INTEL_OUTPUT_SDVO:
                case INTEL_OUTPUT_HDMI:
                        is_sdvo = true;
+                       if (intel_output->needs_tv_clock)
+                               is_tv = true;
                        break;
                case INTEL_OUTPUT_DVO:
                        is_dvo = true;
@@ -766,23 +1586,69 @@ static void intel_crtc_mode_set(struct drm_crtc *crtc,
                        is_crt = true;
                        break;
                }
+
+               num_outputs++;
        }
 
-       if (IS_I9XX(dev)) {
+       if (is_lvds && dev_priv->lvds_use_ssc && num_outputs < 2) {
+               refclk = dev_priv->lvds_ssc_freq * 1000;
+               DRM_DEBUG("using SSC reference clock of %d MHz\n", refclk / 1000);
+       } else if (IS_I9XX(dev)) {
                refclk = 96000;
+               if (IS_IGDNG(dev))
+                       refclk = 120000; /* 120Mhz refclk */
        } else {
                refclk = 48000;
        }
 
-       ok = intel_find_best_PLL(crtc, adjusted_mode->clock, refclk, &clock);
+       /*
+        * Returns a set of divisors for the desired target clock with the given
+        * refclk, or FALSE.  The returned values represent the clock equation:
+        * reflck * (5 * (m1 + 2) + (m2 + 2)) / (n + 2) / p1 / p2.
+        */
+       limit = intel_limit(crtc);
+       ok = limit->find_pll(limit, crtc, adjusted_mode->clock, refclk, &clock);
        if (!ok) {
                DRM_ERROR("Couldn't find PLL settings for mode!\n");
-               return;
+               drm_vblank_post_modeset(dev, pipe);
+               return -EINVAL;
+       }
+
+       /* SDVO TV has fixed PLL values depend on its clock range,
+          this mirrors vbios setting. */
+       if (is_sdvo && is_tv) {
+               if (adjusted_mode->clock >= 100000
+                               && adjusted_mode->clock < 140500) {
+                       clock.p1 = 2;
+                       clock.p2 = 10;
+                       clock.n = 3;
+                       clock.m1 = 16;
+                       clock.m2 = 8;
+               } else if (adjusted_mode->clock >= 140500
+                               && adjusted_mode->clock <= 200000) {
+                       clock.p1 = 1;
+                       clock.p2 = 10;
+                       clock.n = 6;
+                       clock.m1 = 12;
+                       clock.m2 = 8;
+               }
        }
 
-       fp = clock.n << 16 | clock.m1 << 8 | clock.m2;
+       /* FDI link */
+       if (IS_IGDNG(dev))
+               igdng_compute_m_n(3, 4, /* lane num 4 */
+                               adjusted_mode->clock,
+                               270000, /* lane clock */
+                               &m_n);
+
+       if (IS_IGD(dev))
+               fp = (1 << clock.n) << 16 | clock.m1 << 8 | clock.m2;
+       else
+               fp = clock.n << 16 | clock.m1 << 8 | clock.m2;
+
+       if (!IS_IGDNG(dev))
+               dpll = DPLL_VGA_MODE_DIS;
 
-       dpll = DPLL_VGA_MODE_DIS;
        if (IS_I9XX(dev)) {
                if (is_lvds)
                        dpll |= DPLLB_MODE_LVDS;
@@ -790,14 +1656,22 @@ static void intel_crtc_mode_set(struct drm_crtc *crtc,
                        dpll |= DPLLB_MODE_DAC_SERIAL;
                if (is_sdvo) {
                        dpll |= DPLL_DVO_HIGH_SPEED;
-                       if (IS_I945G(dev) || IS_I945GM(dev)) {
-                               int sdvo_pixel_multiply = adjusted_mode->clock / mode->clock;
+                       sdvo_pixel_multiply = adjusted_mode->clock / mode->clock;
+                       if (IS_I945G(dev) || IS_I945GM(dev))
                                dpll |= (sdvo_pixel_multiply - 1) << SDVO_MULTIPLIER_SHIFT_HIRES;
-                       }
+                       else if (IS_IGDNG(dev))
+                               dpll |= (sdvo_pixel_multiply - 1) << PLL_REF_SDVO_HDMI_MULTIPLIER_SHIFT;
                }
 
                /* compute bitmask from p1 value */
-               dpll |= (1 << (clock.p1 - 1)) << 16;
+               if (IS_IGD(dev))
+                       dpll |= (1 << (clock.p1 - 1)) << DPLL_FPA01_P1_POST_DIV_SHIFT_IGD;
+               else {
+                       dpll |= (1 << (clock.p1 - 1)) << DPLL_FPA01_P1_POST_DIV_SHIFT;
+                       /* also FPA1 */
+                       if (IS_IGDNG(dev))
+                               dpll |= (1 << (clock.p1 - 1)) << DPLL_FPA1_P1_POST_DIV_SHIFT;
+               }
                switch (clock.p2) {
                case 5:
                        dpll |= DPLL_DAC_SERIAL_P2_CLOCK_DIV_5;
@@ -812,7 +1686,7 @@ static void intel_crtc_mode_set(struct drm_crtc *crtc,
                        dpll |= DPLLB_LVDS_P2_CLOCK_DIV_14;
                        break;
                }
-               if (IS_I965G(dev))
+               if (IS_I965G(dev) && !IS_IGDNG(dev))
                        dpll |= (6 << PLL_LOAD_PULSE_PHASE_SHIFT);
        } else {
                if (is_lvds) {
@@ -827,11 +1701,14 @@ static void intel_crtc_mode_set(struct drm_crtc *crtc,
                }
        }
 
-       if (is_tv) {
+       if (is_sdvo && is_tv)
+               dpll |= PLL_REF_INPUT_TVCLKINBC;
+       else if (is_tv)
                /* XXX: just matching BIOS for now */
-/*     dpll |= PLL_REF_INPUT_TVCLKINBC; */
+               /*      dpll |= PLL_REF_INPUT_TVCLKINBC; */
                dpll |= 3;
-       }
+       else if (is_lvds && dev_priv->lvds_use_ssc && num_outputs < 2)
+               dpll |= PLLB_REF_INPUT_SPREADSPECTRUMIN;
        else
                dpll |= PLL_REF_INPUT_DREFCLK;
 
@@ -841,10 +1718,14 @@ static void intel_crtc_mode_set(struct drm_crtc *crtc,
        /* Set up the display plane register */
        dspcntr = DISPPLANE_GAMMA_ENABLE;
 
-       if (pipe == 0)
-               dspcntr |= DISPPLANE_SEL_PIPE_A;
-       else
-               dspcntr |= DISPPLANE_SEL_PIPE_B;
+       /* IGDNG's plane is forced to pipe, bit 24 is to
+          enable color space conversion */
+       if (!IS_IGDNG(dev)) {
+               if (pipe == 0)
+                       dspcntr |= DISPPLANE_SEL_PIPE_A;
+               else
+                       dspcntr |= DISPPLANE_SEL_PIPE_B;
+       }
 
        if (pipe == 0 && !IS_I965G(dev)) {
                /* Enable pixel doubling when the dot clock is > 90% of the (display)
@@ -865,12 +1746,17 @@ static void intel_crtc_mode_set(struct drm_crtc *crtc,
 
 
        /* Disable the panel fitter if it was on our pipe */
-       if (intel_panel_fitter_pipe(dev) == pipe)
+       if (!IS_IGDNG(dev) && intel_panel_fitter_pipe(dev) == pipe)
                I915_WRITE(PFIT_CONTROL, 0);
 
        DRM_DEBUG("Mode for pipe %c:\n", pipe == 0 ? 'A' : 'B');
        drm_mode_debug_printmodeline(mode);
 
+       /* assign to IGDNG registers */
+       if (IS_IGDNG(dev)) {
+               fp_reg = pch_fp_reg;
+               dpll_reg = pch_dpll_reg;
+       }
 
        if (dpll & DPLL_VCO_ENABLE) {
                I915_WRITE(fp_reg, fp);
@@ -879,13 +1765,33 @@ static void intel_crtc_mode_set(struct drm_crtc *crtc,
                udelay(150);
        }
 
+       if (IS_IGDNG(dev)) {
+               /* enable PCH clock reference source */
+               /* XXX need to change the setting for other outputs */
+               u32 temp;
+               temp = I915_READ(PCH_DREF_CONTROL);
+               temp &= ~DREF_NONSPREAD_SOURCE_MASK;
+               temp |= DREF_NONSPREAD_CK505_ENABLE;
+               temp &= ~DREF_SSC_SOURCE_MASK;
+               temp |= DREF_SSC_SOURCE_ENABLE;
+               temp &= ~DREF_SSC1_ENABLE;
+               /* if no eDP, disable source output to CPU */
+               temp &= ~DREF_CPU_SOURCE_OUTPUT_MASK;
+               temp |= DREF_CPU_SOURCE_OUTPUT_DISABLE;
+               I915_WRITE(PCH_DREF_CONTROL, temp);
+       }
+
        /* The LVDS pin pair needs to be on before the DPLLs are enabled.
         * This is an exception to the general rule that mode_set doesn't turn
         * things on.
         */
        if (is_lvds) {
-               u32 lvds = I915_READ(LVDS);
+               u32 lvds;
 
+               if (IS_IGDNG(dev))
+                       lvds_reg = PCH_LVDS;
+
+               lvds = I915_READ(lvds_reg);
                lvds |= LVDS_PORT_EN | LVDS_A0A2_CLKA_POWER_UP | LVDS_PIPEB_SELECT;
                /* Set the B0-B3 data pairs corresponding to whether we're going to
                 * set the DPLLs for dual-channel mode or not.
@@ -900,8 +1806,8 @@ static void intel_crtc_mode_set(struct drm_crtc *crtc,
                 * panels behave in the two modes.
                 */
 
-               I915_WRITE(LVDS, lvds);
-               I915_READ(LVDS);
+               I915_WRITE(lvds_reg, lvds);
+               I915_READ(lvds_reg);
        }
 
        I915_WRITE(fp_reg, fp);
@@ -910,8 +1816,8 @@ static void intel_crtc_mode_set(struct drm_crtc *crtc,
        /* Wait for the clocks to stabilize. */
        udelay(150);
 
-       if (IS_I965G(dev)) {
-               int sdvo_pixel_multiply = adjusted_mode->clock / mode->clock;
+       if (IS_I965G(dev) && !IS_IGDNG(dev)) {
+               sdvo_pixel_multiply = adjusted_mode->clock / mode->clock;
                I915_WRITE(dpll_md_reg, (0 << DPLL_MD_UDI_DIVIDER_SHIFT) |
                           ((sdvo_pixel_multiply - 1) << DPLL_MD_UDI_MULTIPLIER_SHIFT));
        } else {
@@ -937,9 +1843,25 @@ static void intel_crtc_mode_set(struct drm_crtc *crtc,
        /* pipesrc and dspsize control the size that is scaled from, which should
         * always be the user's requested size.
         */
-       I915_WRITE(dspsize_reg, ((mode->vdisplay - 1) << 16) | (mode->hdisplay - 1));
-       I915_WRITE(dsppos_reg, 0);
+       if (!IS_IGDNG(dev)) {
+               I915_WRITE(dspsize_reg, ((mode->vdisplay - 1) << 16) |
+                               (mode->hdisplay - 1));
+               I915_WRITE(dsppos_reg, 0);
+       }
        I915_WRITE(pipesrc_reg, ((mode->hdisplay - 1) << 16) | (mode->vdisplay - 1));
+
+       if (IS_IGDNG(dev)) {
+               I915_WRITE(data_m1_reg, TU_SIZE(m_n.tu) | m_n.gmch_m);
+               I915_WRITE(data_n1_reg, TU_SIZE(m_n.tu) | m_n.gmch_n);
+               I915_WRITE(link_m1_reg, m_n.link_m);
+               I915_WRITE(link_n1_reg, m_n.link_n);
+
+                /* enable FDI RX PLL too */
+               temp = I915_READ(fdi_rx_reg);
+               I915_WRITE(fdi_rx_reg, temp | FDI_RX_PLL_ENABLE);
+               udelay(200);
+       }
+
        I915_WRITE(pipeconf_reg, pipeconf);
        I915_READ(pipeconf_reg);
 
@@ -948,9 +1870,10 @@ static void intel_crtc_mode_set(struct drm_crtc *crtc,
        I915_WRITE(dspcntr_reg, dspcntr);
 
        /* Flush the plane changes */
-       intel_pipe_set_base(crtc, x, y, old_fb);
-
+       ret = intel_pipe_set_base(crtc, x, y, old_fb);
        drm_vblank_post_modeset(dev, pipe);
+
+       return ret;
 }
 
 /** Loads the palette/gamma unit for the CRTC with the prepared values */
@@ -966,6 +1889,11 @@ void intel_crtc_load_lut(struct drm_crtc *crtc)
        if (!crtc->enabled)
                return;
 
+       /* use legacy palette for IGDNG */
+       if (IS_IGDNG(dev))
+               palreg = (intel_crtc->pipe == 0) ? LGC_PALETTE_A :
+                                                  LGC_PALETTE_B;
+
        for (i = 0; i < 256; i++) {
                I915_WRITE(palreg + 4 * i,
                           (intel_crtc->lut_r[i] << 16) |
@@ -987,7 +1915,7 @@ static int intel_crtc_cursor_set(struct drm_crtc *crtc,
        int pipe = intel_crtc->pipe;
        uint32_t control = (pipe == 0) ? CURACNTR : CURBCNTR;
        uint32_t base = (pipe == 0) ? CURABASE : CURBBASE;
-       uint32_t temp;
+       uint32_t temp = I915_READ(control);
        size_t addr;
        int ret;
 
@@ -996,9 +1924,15 @@ static int intel_crtc_cursor_set(struct drm_crtc *crtc,
        /* if we want to turn off the cursor ignore width and height */
        if (!handle) {
                DRM_DEBUG("cursor off\n");
-               temp = CURSOR_MODE_DISABLE;
+               if (IS_MOBILE(dev) || IS_I9XX(dev)) {
+                       temp &= ~(CURSOR_MODE | MCURSOR_GAMMA_ENABLE);
+                       temp |= CURSOR_MODE_DISABLE;
+               } else {
+                       temp &= ~(CURSOR_ENABLE | CURSOR_GAMMA_ENABLE);
+               }
                addr = 0;
                bo = NULL;
+               mutex_lock(&dev->struct_mutex);
                goto finish;
        }
 
@@ -1021,26 +1955,36 @@ 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->cursor_needs_physical) {
                ret = i915_gem_object_pin(bo, PAGE_SIZE);
                if (ret) {
                        DRM_ERROR("failed to pin cursor bo\n");
-                       goto fail;
+                       goto fail_locked;
                }
                addr = obj_priv->gtt_offset;
        } else {
                ret = i915_gem_attach_phys_object(dev, bo, (pipe == 0) ? I915_GEM_PHYS_CURSOR_0 : I915_GEM_PHYS_CURSOR_1);
                if (ret) {
                        DRM_ERROR("failed to attach phys object\n");
-                       goto fail;
+                       goto fail_locked;
                }
                addr = obj_priv->phys_obj->handle->busaddr;
        }
 
-       temp = 0;
-       /* set the pipe for the cursor */
-       temp |= (pipe << 28);
-       temp |= CURSOR_MODE_64_ARGB_AX | MCURSOR_GAMMA_ENABLE;
+       if (!IS_I9XX(dev))
+               I915_WRITE(CURSIZE, (height << 12) | width);
+
+       /* Hooray for CUR*CNTR differences */
+       if (IS_MOBILE(dev) || IS_I9XX(dev)) {
+               temp &= ~(CURSOR_MODE | MCURSOR_PIPE_SELECT);
+               temp |= CURSOR_MODE_64_ARGB_AX | MCURSOR_GAMMA_ENABLE;
+               temp |= (pipe << 28); /* Connect to correct pipe */
+       } else {
+               temp &= ~(CURSOR_FORMAT_MASK);
+               temp |= CURSOR_ENABLE;
+               temp |= CURSOR_FORMAT_ARGB | CURSOR_GAMMA_ENABLE;
+       }
 
  finish:
        I915_WRITE(control, temp);
@@ -1052,10 +1996,9 @@ static int intel_crtc_cursor_set(struct drm_crtc *crtc,
                                i915_gem_detach_phys_object(dev, intel_crtc->cursor_bo);
                } else
                        i915_gem_object_unpin(intel_crtc->cursor_bo);
-               mutex_lock(&dev->struct_mutex);
                drm_gem_object_unreference(intel_crtc->cursor_bo);
-               mutex_unlock(&dev->struct_mutex);
        }
+       mutex_unlock(&dev->struct_mutex);
 
        intel_crtc->cursor_addr = addr;
        intel_crtc->cursor_bo = bo;
@@ -1063,6 +2006,7 @@ static int intel_crtc_cursor_set(struct drm_crtc *crtc,
        return 0;
 fail:
        mutex_lock(&dev->struct_mutex);
+fail_locked:
        drm_gem_object_unreference(bo);
        mutex_unlock(&dev->struct_mutex);
        return ret;
@@ -1078,16 +2022,16 @@ static int intel_crtc_cursor_move(struct drm_crtc *crtc, int x, int y)
        uint32_t adder;
 
        if (x < 0) {
-               temp |= (CURSOR_POS_SIGN << CURSOR_X_SHIFT);
+               temp |= CURSOR_POS_SIGN << CURSOR_X_SHIFT;
                x = -x;
        }
        if (y < 0) {
-               temp |= (CURSOR_POS_SIGN << CURSOR_Y_SHIFT);
+               temp |= CURSOR_POS_SIGN << CURSOR_Y_SHIFT;
                y = -y;
        }
 
-       temp |= ((x & CURSOR_POS_MASK) << CURSOR_X_SHIFT);
-       temp |= ((y & CURSOR_POS_MASK) << CURSOR_Y_SHIFT);
+       temp |= x << CURSOR_X_SHIFT;
+       temp |= y << CURSOR_Y_SHIFT;
 
        adder = intel_crtc->cursor_addr;
        I915_WRITE((pipe == 0) ? CURAPOS : CURBPOS, temp);
@@ -1204,6 +2148,7 @@ struct drm_crtc *intel_get_load_detect_pipe(struct intel_output *intel_output,
        }
 
        encoder->crtc = crtc;
+       intel_output->base.encoder = encoder;
        intel_output->load_detect_temp = true;
 
        intel_crtc = to_intel_crtc(crtc);
@@ -1239,6 +2184,7 @@ void intel_release_load_detect_pipe(struct intel_output *intel_output, int dpms_
 
        if (intel_output->load_detect_temp) {
                encoder->crtc = NULL;
+               intel_output->base.encoder = NULL;
                intel_output->load_detect_temp = false;
                crtc->enabled = drm_helper_crtc_in_use(crtc);
                drm_helper_disable_unused_functions(dev);
@@ -1268,10 +2214,20 @@ static int intel_crtc_clock_get(struct drm_device *dev, struct drm_crtc *crtc)
                fp = I915_READ((pipe == 0) ? FPA1 : FPB1);
 
        clock.m1 = (fp & FP_M1_DIV_MASK) >> FP_M1_DIV_SHIFT;
-       clock.m2 = (fp & FP_M2_DIV_MASK) >> FP_M2_DIV_SHIFT;
-       clock.n = (fp & FP_N_DIV_MASK) >> FP_N_DIV_SHIFT;
+       if (IS_IGD(dev)) {
+               clock.n = ffs((fp & FP_N_IGD_DIV_MASK) >> FP_N_DIV_SHIFT) - 1;
+               clock.m2 = (fp & FP_M2_IGD_DIV_MASK) >> FP_M2_DIV_SHIFT;
+       } else {
+               clock.n = (fp & FP_N_DIV_MASK) >> FP_N_DIV_SHIFT;
+               clock.m2 = (fp & FP_M2_DIV_MASK) >> FP_M2_DIV_SHIFT;
+       }
+
        if (IS_I9XX(dev)) {
-               clock.p1 = ffs((dpll & DPLL_FPA01_P1_POST_DIV_MASK) >>
+               if (IS_IGD(dev))
+                       clock.p1 = ffs((dpll & DPLL_FPA01_P1_POST_DIV_MASK_IGD) >>
+                               DPLL_FPA01_P1_POST_DIV_SHIFT_IGD);
+               else
+                       clock.p1 = ffs((dpll & DPLL_FPA01_P1_POST_DIV_MASK) >>
                               DPLL_FPA01_P1_POST_DIV_SHIFT);
 
                switch (dpll & DPLL_MODE_MASK) {
@@ -1290,7 +2246,7 @@ static int intel_crtc_clock_get(struct drm_device *dev, struct drm_crtc *crtc)
                }
 
                /* XXX: Handle the 100Mhz refclk */
-               i9xx_clock(96000, &clock);
+               intel_clock(dev, 96000, &clock);
        } else {
                bool is_lvds = (pipe == 1) && (I915_READ(LVDS) & LVDS_PORT_EN);
 
@@ -1302,9 +2258,9 @@ static int intel_crtc_clock_get(struct drm_device *dev, struct drm_crtc *crtc)
                        if ((dpll & PLL_REF_INPUT_MASK) ==
                            PLLB_REF_INPUT_SPREADSPECTRUMIN) {
                                /* XXX: might not be 66MHz */
-                               i8xx_clock(66000, &clock);
+                               intel_clock(dev, 66000, &clock);
                        } else
-                               i8xx_clock(48000, &clock);
+                               intel_clock(dev, 48000, &clock);
                } else {
                        if (dpll & PLL_P1_DIVIDE_BY_TWO)
                                clock.p1 = 2;
@@ -1317,7 +2273,7 @@ static int intel_crtc_clock_get(struct drm_device *dev, struct drm_crtc *crtc)
                        else
                                clock.p2 = 2;
 
-                       i8xx_clock(48000, &clock);
+                       intel_clock(dev, 48000, &clock);
                }
        }
 
@@ -1366,6 +2322,8 @@ static void intel_crtc_destroy(struct drm_crtc *crtc)
 {
        struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
 
+       if (intel_crtc->mode_set.mode)
+               drm_mode_destroy(crtc->dev, intel_crtc->mode_set.mode);
        drm_crtc_cleanup(crtc);
        kfree(intel_crtc);
 }
@@ -1422,6 +2380,37 @@ static void intel_crtc_init(struct drm_device *dev, int pipe)
        }
 }
 
+int intel_get_pipe_from_crtc_id(struct drm_device *dev, void *data,
+                               struct drm_file *file_priv)
+{
+       drm_i915_private_t *dev_priv = dev->dev_private;
+       struct drm_i915_get_pipe_from_crtc_id *pipe_from_crtc_id = data;
+       struct drm_crtc *crtc = NULL;
+       int pipe = -1;
+
+       if (!dev_priv) {
+               DRM_ERROR("called with no initialization\n");
+               return -EINVAL;
+       }
+
+       list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
+               struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
+               if (crtc->base.id == pipe_from_crtc_id->crtc_id) {
+                       pipe = intel_crtc->pipe;
+                       break;
+               }
+       }
+
+       if (pipe == -1) {
+               DRM_ERROR("no such CRTC id\n");
+               return -EINVAL;
+       }
+
+       pipe_from_crtc_id->pipe = pipe;
+
+       return 0;
+}
+
 struct drm_crtc *intel_get_crtc_from_pipe(struct drm_device *dev, int pipe)
 {
        struct drm_crtc *crtc = NULL;
@@ -1461,15 +2450,40 @@ static void intel_setup_outputs(struct drm_device *dev)
        if (IS_MOBILE(dev) && !IS_I830(dev))
                intel_lvds_init(dev);
 
-       if (IS_I9XX(dev)) {
+       if (IS_IGDNG(dev)) {
+               int found;
+
+               if (I915_READ(HDMIB) & PORT_DETECTED) {
+                       /* check SDVOB */
+                       /* found = intel_sdvo_init(dev, HDMIB); */
+                       found = 0;
+                       if (!found)
+                               intel_hdmi_init(dev, HDMIB);
+               }
+
+               if (I915_READ(HDMIC) & PORT_DETECTED)
+                       intel_hdmi_init(dev, HDMIC);
+
+               if (I915_READ(HDMID) & PORT_DETECTED)
+                       intel_hdmi_init(dev, HDMID);
+
+       } else if (IS_I9XX(dev)) {
                int found;
+               u32 reg;
 
                if (I915_READ(SDVOB) & SDVO_DETECTED) {
                        found = intel_sdvo_init(dev, SDVOB);
                        if (!found && SUPPORTS_INTEGRATED_HDMI(dev))
                                intel_hdmi_init(dev, SDVOB);
                }
-               if (!IS_G4X(dev) || (I915_READ(SDVOB) & SDVO_DETECTED)) {
+
+               /* Before G4X SDVOC doesn't have its own detect register */
+               if (IS_G4X(dev))
+                       reg = SDVOC;
+               else
+                       reg = SDVOB;
+
+               if (I915_READ(reg) & SDVO_DETECTED) {
                        found = intel_sdvo_init(dev, SDVOC);
                        if (!found && SUPPORTS_INTEGRATED_HDMI(dev))
                                intel_hdmi_init(dev, SDVOC);
@@ -1477,7 +2491,7 @@ static void intel_setup_outputs(struct drm_device *dev)
        } else
                intel_dvo_init(dev);
 
-       if (IS_I9XX(dev) && IS_MOBILE(dev))
+       if (IS_I9XX(dev) && IS_MOBILE(dev) && !IS_IGDNG(dev))
                intel_tv_init(dev);
 
        list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
@@ -1596,7 +2610,9 @@ intel_user_framebuffer_create(struct drm_device *dev,
 
        ret = intel_framebuffer_create(dev, mode_cmd, &fb, obj);
        if (ret) {
+               mutex_lock(&dev->struct_mutex);
                drm_gem_object_unreference(obj);
+               mutex_unlock(&dev->struct_mutex);
                return NULL;
        }