} 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
#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
#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 */
.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 },
.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 },
.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 },
*/
.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];
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
*/
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.
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");
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");
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) &&
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;
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)
{
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);
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 */
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);
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;
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.
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;
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;
}
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;
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;
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);
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;
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;
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;
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) {
}
}
- 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;
/* 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)
/* 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);
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.
* 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);
/* 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 {
/* 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);
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 */
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) |
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;
/* 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;
}
}
/* 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);
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;
return 0;
fail:
mutex_lock(&dev->struct_mutex);
+fail_locked:
drm_gem_object_unreference(bo);
mutex_unlock(&dev->struct_mutex);
return ret;
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);
}
encoder->crtc = crtc;
+ intel_output->base.encoder = encoder;
intel_output->load_detect_temp = true;
intel_crtc = to_intel_crtc(crtc);
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);
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) {
}
/* 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);
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;
else
clock.p2 = 2;
- i8xx_clock(48000, &clock);
+ intel_clock(dev, 48000, &clock);
}
}
{
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);
}
}
}
+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;
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);
} 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) {
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;
}