|
@@ -31,6 +31,7 @@
|
|
|
#include <linux/kernel.h>
|
|
|
#include <linux/slab.h>
|
|
|
#include <linux/vgaarb.h>
|
|
|
+#include <drm/drm_edid.h>
|
|
|
#include "drmP.h"
|
|
|
#include "intel_drv.h"
|
|
|
#include "i915_drm.h"
|
|
@@ -42,39 +43,39 @@
|
|
|
|
|
|
#define HAS_eDP (intel_pipe_has_type(crtc, INTEL_OUTPUT_EDP))
|
|
|
|
|
|
-bool intel_pipe_has_type (struct drm_crtc *crtc, int type);
|
|
|
+bool intel_pipe_has_type(struct drm_crtc *crtc, int type);
|
|
|
static void intel_update_watermarks(struct drm_device *dev);
|
|
|
static void intel_increase_pllclock(struct drm_crtc *crtc);
|
|
|
static void intel_crtc_update_cursor(struct drm_crtc *crtc, bool on);
|
|
|
|
|
|
typedef struct {
|
|
|
- /* given values */
|
|
|
- int n;
|
|
|
- int m1, m2;
|
|
|
- int p1, p2;
|
|
|
- /* derived values */
|
|
|
- int dot;
|
|
|
- int vco;
|
|
|
- int m;
|
|
|
- int p;
|
|
|
+ /* given values */
|
|
|
+ int n;
|
|
|
+ int m1, m2;
|
|
|
+ int p1, p2;
|
|
|
+ /* derived values */
|
|
|
+ int dot;
|
|
|
+ int vco;
|
|
|
+ int m;
|
|
|
+ int p;
|
|
|
} intel_clock_t;
|
|
|
|
|
|
typedef struct {
|
|
|
- int min, max;
|
|
|
+ int min, max;
|
|
|
} intel_range_t;
|
|
|
|
|
|
typedef struct {
|
|
|
- int dot_limit;
|
|
|
- int p2_slow, p2_fast;
|
|
|
+ int dot_limit;
|
|
|
+ int p2_slow, p2_fast;
|
|
|
} intel_p2_t;
|
|
|
|
|
|
#define INTEL_P2_NUM 2
|
|
|
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;
|
|
|
- bool (* find_pll)(const intel_limit_t *, struct drm_crtc *,
|
|
|
- int, int, intel_clock_t *);
|
|
|
+ intel_range_t dot, vco, n, m, m1, m2, p, p1;
|
|
|
+ intel_p2_t p2;
|
|
|
+ bool (* find_pll)(const intel_limit_t *, struct drm_crtc *,
|
|
|
+ int, int, intel_clock_t *);
|
|
|
};
|
|
|
|
|
|
/* FDI */
|
|
@@ -105,56 +106,56 @@ intel_fdi_link_freq(struct drm_device *dev)
|
|
|
}
|
|
|
|
|
|
static const intel_limit_t intel_limits_i8xx_dvo = {
|
|
|
- .dot = { .min = 25000, .max = 350000 },
|
|
|
- .vco = { .min = 930000, .max = 1400000 },
|
|
|
- .n = { .min = 3, .max = 16 },
|
|
|
- .m = { .min = 96, .max = 140 },
|
|
|
- .m1 = { .min = 18, .max = 26 },
|
|
|
- .m2 = { .min = 6, .max = 16 },
|
|
|
- .p = { .min = 4, .max = 128 },
|
|
|
- .p1 = { .min = 2, .max = 33 },
|
|
|
+ .dot = { .min = 25000, .max = 350000 },
|
|
|
+ .vco = { .min = 930000, .max = 1400000 },
|
|
|
+ .n = { .min = 3, .max = 16 },
|
|
|
+ .m = { .min = 96, .max = 140 },
|
|
|
+ .m1 = { .min = 18, .max = 26 },
|
|
|
+ .m2 = { .min = 6, .max = 16 },
|
|
|
+ .p = { .min = 4, .max = 128 },
|
|
|
+ .p1 = { .min = 2, .max = 33 },
|
|
|
.p2 = { .dot_limit = 165000,
|
|
|
.p2_slow = 4, .p2_fast = 2 },
|
|
|
.find_pll = intel_find_best_PLL,
|
|
|
};
|
|
|
|
|
|
static const intel_limit_t intel_limits_i8xx_lvds = {
|
|
|
- .dot = { .min = 25000, .max = 350000 },
|
|
|
- .vco = { .min = 930000, .max = 1400000 },
|
|
|
- .n = { .min = 3, .max = 16 },
|
|
|
- .m = { .min = 96, .max = 140 },
|
|
|
- .m1 = { .min = 18, .max = 26 },
|
|
|
- .m2 = { .min = 6, .max = 16 },
|
|
|
- .p = { .min = 4, .max = 128 },
|
|
|
- .p1 = { .min = 1, .max = 6 },
|
|
|
+ .dot = { .min = 25000, .max = 350000 },
|
|
|
+ .vco = { .min = 930000, .max = 1400000 },
|
|
|
+ .n = { .min = 3, .max = 16 },
|
|
|
+ .m = { .min = 96, .max = 140 },
|
|
|
+ .m1 = { .min = 18, .max = 26 },
|
|
|
+ .m2 = { .min = 6, .max = 16 },
|
|
|
+ .p = { .min = 4, .max = 128 },
|
|
|
+ .p1 = { .min = 1, .max = 6 },
|
|
|
.p2 = { .dot_limit = 165000,
|
|
|
.p2_slow = 14, .p2_fast = 7 },
|
|
|
.find_pll = intel_find_best_PLL,
|
|
|
};
|
|
|
|
|
|
static const intel_limit_t intel_limits_i9xx_sdvo = {
|
|
|
- .dot = { .min = 20000, .max = 400000 },
|
|
|
- .vco = { .min = 1400000, .max = 2800000 },
|
|
|
- .n = { .min = 1, .max = 6 },
|
|
|
- .m = { .min = 70, .max = 120 },
|
|
|
- .m1 = { .min = 10, .max = 22 },
|
|
|
- .m2 = { .min = 5, .max = 9 },
|
|
|
- .p = { .min = 5, .max = 80 },
|
|
|
- .p1 = { .min = 1, .max = 8 },
|
|
|
+ .dot = { .min = 20000, .max = 400000 },
|
|
|
+ .vco = { .min = 1400000, .max = 2800000 },
|
|
|
+ .n = { .min = 1, .max = 6 },
|
|
|
+ .m = { .min = 70, .max = 120 },
|
|
|
+ .m1 = { .min = 10, .max = 22 },
|
|
|
+ .m2 = { .min = 5, .max = 9 },
|
|
|
+ .p = { .min = 5, .max = 80 },
|
|
|
+ .p1 = { .min = 1, .max = 8 },
|
|
|
.p2 = { .dot_limit = 200000,
|
|
|
.p2_slow = 10, .p2_fast = 5 },
|
|
|
.find_pll = intel_find_best_PLL,
|
|
|
};
|
|
|
|
|
|
static const intel_limit_t intel_limits_i9xx_lvds = {
|
|
|
- .dot = { .min = 20000, .max = 400000 },
|
|
|
- .vco = { .min = 1400000, .max = 2800000 },
|
|
|
- .n = { .min = 1, .max = 6 },
|
|
|
- .m = { .min = 70, .max = 120 },
|
|
|
- .m1 = { .min = 10, .max = 22 },
|
|
|
- .m2 = { .min = 5, .max = 9 },
|
|
|
- .p = { .min = 7, .max = 98 },
|
|
|
- .p1 = { .min = 1, .max = 8 },
|
|
|
+ .dot = { .min = 20000, .max = 400000 },
|
|
|
+ .vco = { .min = 1400000, .max = 2800000 },
|
|
|
+ .n = { .min = 1, .max = 6 },
|
|
|
+ .m = { .min = 70, .max = 120 },
|
|
|
+ .m1 = { .min = 10, .max = 22 },
|
|
|
+ .m2 = { .min = 5, .max = 9 },
|
|
|
+ .p = { .min = 7, .max = 98 },
|
|
|
+ .p1 = { .min = 1, .max = 8 },
|
|
|
.p2 = { .dot_limit = 112000,
|
|
|
.p2_slow = 14, .p2_fast = 7 },
|
|
|
.find_pll = intel_find_best_PLL,
|
|
@@ -222,44 +223,44 @@ static const intel_limit_t intel_limits_g4x_dual_channel_lvds = {
|
|
|
};
|
|
|
|
|
|
static const intel_limit_t intel_limits_g4x_display_port = {
|
|
|
- .dot = { .min = 161670, .max = 227000 },
|
|
|
- .vco = { .min = 1750000, .max = 3500000},
|
|
|
- .n = { .min = 1, .max = 2 },
|
|
|
- .m = { .min = 97, .max = 108 },
|
|
|
- .m1 = { .min = 0x10, .max = 0x12 },
|
|
|
- .m2 = { .min = 0x05, .max = 0x06 },
|
|
|
- .p = { .min = 10, .max = 20 },
|
|
|
- .p1 = { .min = 1, .max = 2},
|
|
|
- .p2 = { .dot_limit = 0,
|
|
|
+ .dot = { .min = 161670, .max = 227000 },
|
|
|
+ .vco = { .min = 1750000, .max = 3500000},
|
|
|
+ .n = { .min = 1, .max = 2 },
|
|
|
+ .m = { .min = 97, .max = 108 },
|
|
|
+ .m1 = { .min = 0x10, .max = 0x12 },
|
|
|
+ .m2 = { .min = 0x05, .max = 0x06 },
|
|
|
+ .p = { .min = 10, .max = 20 },
|
|
|
+ .p1 = { .min = 1, .max = 2},
|
|
|
+ .p2 = { .dot_limit = 0,
|
|
|
.p2_slow = 10, .p2_fast = 10 },
|
|
|
- .find_pll = intel_find_pll_g4x_dp,
|
|
|
+ .find_pll = intel_find_pll_g4x_dp,
|
|
|
};
|
|
|
|
|
|
static const intel_limit_t intel_limits_pineview_sdvo = {
|
|
|
- .dot = { .min = 20000, .max = 400000},
|
|
|
- .vco = { .min = 1700000, .max = 3500000 },
|
|
|
+ .dot = { .min = 20000, .max = 400000},
|
|
|
+ .vco = { .min = 1700000, .max = 3500000 },
|
|
|
/* Pineview's Ncounter is a ring counter */
|
|
|
- .n = { .min = 3, .max = 6 },
|
|
|
- .m = { .min = 2, .max = 256 },
|
|
|
+ .n = { .min = 3, .max = 6 },
|
|
|
+ .m = { .min = 2, .max = 256 },
|
|
|
/* Pineview only has one combined m divider, which we treat as m2. */
|
|
|
- .m1 = { .min = 0, .max = 0 },
|
|
|
- .m2 = { .min = 0, .max = 254 },
|
|
|
- .p = { .min = 5, .max = 80 },
|
|
|
- .p1 = { .min = 1, .max = 8 },
|
|
|
+ .m1 = { .min = 0, .max = 0 },
|
|
|
+ .m2 = { .min = 0, .max = 254 },
|
|
|
+ .p = { .min = 5, .max = 80 },
|
|
|
+ .p1 = { .min = 1, .max = 8 },
|
|
|
.p2 = { .dot_limit = 200000,
|
|
|
.p2_slow = 10, .p2_fast = 5 },
|
|
|
.find_pll = intel_find_best_PLL,
|
|
|
};
|
|
|
|
|
|
static const intel_limit_t intel_limits_pineview_lvds = {
|
|
|
- .dot = { .min = 20000, .max = 400000 },
|
|
|
- .vco = { .min = 1700000, .max = 3500000 },
|
|
|
- .n = { .min = 3, .max = 6 },
|
|
|
- .m = { .min = 2, .max = 256 },
|
|
|
- .m1 = { .min = 0, .max = 0 },
|
|
|
- .m2 = { .min = 0, .max = 254 },
|
|
|
- .p = { .min = 7, .max = 112 },
|
|
|
- .p1 = { .min = 1, .max = 8 },
|
|
|
+ .dot = { .min = 20000, .max = 400000 },
|
|
|
+ .vco = { .min = 1700000, .max = 3500000 },
|
|
|
+ .n = { .min = 3, .max = 6 },
|
|
|
+ .m = { .min = 2, .max = 256 },
|
|
|
+ .m1 = { .min = 0, .max = 0 },
|
|
|
+ .m2 = { .min = 0, .max = 254 },
|
|
|
+ .p = { .min = 7, .max = 112 },
|
|
|
+ .p1 = { .min = 1, .max = 8 },
|
|
|
.p2 = { .dot_limit = 112000,
|
|
|
.p2_slow = 14, .p2_fast = 14 },
|
|
|
.find_pll = intel_find_best_PLL,
|
|
@@ -321,7 +322,7 @@ static const intel_limit_t intel_limits_ironlake_single_lvds_100m = {
|
|
|
.m1 = { .min = 12, .max = 22 },
|
|
|
.m2 = { .min = 5, .max = 9 },
|
|
|
.p = { .min = 28, .max = 112 },
|
|
|
- .p1 = { .min = 2,.max = 8 },
|
|
|
+ .p1 = { .min = 2, .max = 8 },
|
|
|
.p2 = { .dot_limit = 225000,
|
|
|
.p2_slow = 14, .p2_fast = 14 },
|
|
|
.find_pll = intel_g4x_find_best_PLL,
|
|
@@ -335,24 +336,24 @@ static const intel_limit_t intel_limits_ironlake_dual_lvds_100m = {
|
|
|
.m1 = { .min = 12, .max = 22 },
|
|
|
.m2 = { .min = 5, .max = 9 },
|
|
|
.p = { .min = 14, .max = 42 },
|
|
|
- .p1 = { .min = 2,.max = 6 },
|
|
|
+ .p1 = { .min = 2, .max = 6 },
|
|
|
.p2 = { .dot_limit = 225000,
|
|
|
.p2_slow = 7, .p2_fast = 7 },
|
|
|
.find_pll = intel_g4x_find_best_PLL,
|
|
|
};
|
|
|
|
|
|
static const intel_limit_t intel_limits_ironlake_display_port = {
|
|
|
- .dot = { .min = 25000, .max = 350000 },
|
|
|
- .vco = { .min = 1760000, .max = 3510000},
|
|
|
- .n = { .min = 1, .max = 2 },
|
|
|
- .m = { .min = 81, .max = 90 },
|
|
|
- .m1 = { .min = 12, .max = 22 },
|
|
|
- .m2 = { .min = 5, .max = 9 },
|
|
|
- .p = { .min = 10, .max = 20 },
|
|
|
- .p1 = { .min = 1, .max = 2},
|
|
|
- .p2 = { .dot_limit = 0,
|
|
|
+ .dot = { .min = 25000, .max = 350000 },
|
|
|
+ .vco = { .min = 1760000, .max = 3510000},
|
|
|
+ .n = { .min = 1, .max = 2 },
|
|
|
+ .m = { .min = 81, .max = 90 },
|
|
|
+ .m1 = { .min = 12, .max = 22 },
|
|
|
+ .m2 = { .min = 5, .max = 9 },
|
|
|
+ .p = { .min = 10, .max = 20 },
|
|
|
+ .p1 = { .min = 1, .max = 2},
|
|
|
+ .p2 = { .dot_limit = 0,
|
|
|
.p2_slow = 10, .p2_fast = 10 },
|
|
|
- .find_pll = intel_find_pll_ironlake_dp,
|
|
|
+ .find_pll = intel_find_pll_ironlake_dp,
|
|
|
};
|
|
|
|
|
|
static const intel_limit_t *intel_ironlake_limit(struct drm_crtc *crtc,
|
|
@@ -404,7 +405,7 @@ static const intel_limit_t *intel_g4x_limit(struct drm_crtc *crtc)
|
|
|
limit = &intel_limits_g4x_hdmi;
|
|
|
} else if (intel_pipe_has_type(crtc, INTEL_OUTPUT_SDVO)) {
|
|
|
limit = &intel_limits_g4x_sdvo;
|
|
|
- } else if (intel_pipe_has_type (crtc, INTEL_OUTPUT_DISPLAYPORT)) {
|
|
|
+ } else if (intel_pipe_has_type(crtc, INTEL_OUTPUT_DISPLAYPORT)) {
|
|
|
limit = &intel_limits_g4x_display_port;
|
|
|
} else /* The option is for other outputs */
|
|
|
limit = &intel_limits_i9xx_sdvo;
|
|
@@ -488,26 +489,26 @@ static bool intel_PLL_is_valid(struct drm_device *dev,
|
|
|
const intel_clock_t *clock)
|
|
|
{
|
|
|
if (clock->p1 < limit->p1.min || limit->p1.max < clock->p1)
|
|
|
- INTELPllInvalid ("p1 out of range\n");
|
|
|
+ INTELPllInvalid("p1 out of range\n");
|
|
|
if (clock->p < limit->p.min || limit->p.max < clock->p)
|
|
|
- INTELPllInvalid ("p out of range\n");
|
|
|
+ INTELPllInvalid("p out of range\n");
|
|
|
if (clock->m2 < limit->m2.min || limit->m2.max < clock->m2)
|
|
|
- INTELPllInvalid ("m2 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");
|
|
|
+ INTELPllInvalid("m1 out of range\n");
|
|
|
if (clock->m1 <= clock->m2 && !IS_PINEVIEW(dev))
|
|
|
- INTELPllInvalid ("m1 <= m2\n");
|
|
|
+ INTELPllInvalid("m1 <= m2\n");
|
|
|
if (clock->m < limit->m.min || limit->m.max < clock->m)
|
|
|
- INTELPllInvalid ("m out of range\n");
|
|
|
+ INTELPllInvalid("m out of range\n");
|
|
|
if (clock->n < limit->n.min || limit->n.max < clock->n)
|
|
|
- INTELPllInvalid ("n out of range\n");
|
|
|
+ INTELPllInvalid("n out of range\n");
|
|
|
if (clock->vco < limit->vco.min || limit->vco.max < clock->vco)
|
|
|
- INTELPllInvalid ("vco out of range\n");
|
|
|
+ INTELPllInvalid("vco out of range\n");
|
|
|
/* XXX: We may need to be checking "Dot clock" depending on the multiplier,
|
|
|
* connector, etc., rather than just a single range.
|
|
|
*/
|
|
|
if (clock->dot < limit->dot.min || limit->dot.max < clock->dot)
|
|
|
- INTELPllInvalid ("dot out of range\n");
|
|
|
+ INTELPllInvalid("dot out of range\n");
|
|
|
|
|
|
return true;
|
|
|
}
|
|
@@ -542,7 +543,7 @@ intel_find_best_PLL(const intel_limit_t *limit, struct drm_crtc *crtc,
|
|
|
clock.p2 = limit->p2.p2_fast;
|
|
|
}
|
|
|
|
|
|
- memset (best_clock, 0, sizeof (*best_clock));
|
|
|
+ memset(best_clock, 0, sizeof(*best_clock));
|
|
|
|
|
|
for (clock.m1 = limit->m1.min; clock.m1 <= limit->m1.max;
|
|
|
clock.m1++) {
|
|
@@ -802,6 +803,19 @@ static void assert_pch_pll(struct drm_i915_private *dev_priv,
|
|
|
u32 val;
|
|
|
bool cur_state;
|
|
|
|
|
|
+ if (HAS_PCH_CPT(dev_priv->dev)) {
|
|
|
+ u32 pch_dpll;
|
|
|
+
|
|
|
+ pch_dpll = I915_READ(PCH_DPLL_SEL);
|
|
|
+
|
|
|
+ /* Make sure the selected PLL is enabled to the transcoder */
|
|
|
+ WARN(!((pch_dpll >> (4 * pipe)) & 8),
|
|
|
+ "transcoder %d PLL not enabled\n", pipe);
|
|
|
+
|
|
|
+ /* Convert the transcoder pipe number to a pll pipe number */
|
|
|
+ pipe = (pch_dpll >> (4 * pipe)) & 1;
|
|
|
+ }
|
|
|
+
|
|
|
reg = PCH_DPLL(pipe);
|
|
|
val = I915_READ(reg);
|
|
|
cur_state = !!(val & DPLL_VCO_ENABLE);
|
|
@@ -1171,6 +1185,9 @@ static void intel_enable_pch_pll(struct drm_i915_private *dev_priv,
|
|
|
int reg;
|
|
|
u32 val;
|
|
|
|
|
|
+ if (pipe > 1)
|
|
|
+ return;
|
|
|
+
|
|
|
/* PCH only available on ILK+ */
|
|
|
BUG_ON(dev_priv->info->gen < 5);
|
|
|
|
|
@@ -1191,6 +1208,9 @@ static void intel_disable_pch_pll(struct drm_i915_private *dev_priv,
|
|
|
int reg;
|
|
|
u32 val;
|
|
|
|
|
|
+ if (pipe > 1)
|
|
|
+ return;
|
|
|
+
|
|
|
/* PCH only available on ILK+ */
|
|
|
BUG_ON(dev_priv->info->gen < 5);
|
|
|
|
|
@@ -1256,7 +1276,7 @@ static void intel_disable_transcoder(struct drm_i915_private *dev_priv,
|
|
|
I915_WRITE(reg, val);
|
|
|
/* wait for PCH transcoder off, transcoder state */
|
|
|
if (wait_for((I915_READ(reg) & TRANS_STATE_ENABLE) == 0, 50))
|
|
|
- DRM_ERROR("failed to disable transcoder\n");
|
|
|
+ DRM_ERROR("failed to disable transcoder %d\n", pipe);
|
|
|
}
|
|
|
|
|
|
/**
|
|
@@ -2085,6 +2105,7 @@ static int ironlake_update_plane(struct drm_crtc *crtc,
|
|
|
switch (plane) {
|
|
|
case 0:
|
|
|
case 1:
|
|
|
+ case 2:
|
|
|
break;
|
|
|
default:
|
|
|
DRM_ERROR("Can't update plane %d in SAREA\n", plane);
|
|
@@ -2184,6 +2205,10 @@ intel_pipe_set_base(struct drm_crtc *crtc, int x, int y,
|
|
|
case 0:
|
|
|
case 1:
|
|
|
break;
|
|
|
+ case 2:
|
|
|
+ if (IS_IVYBRIDGE(dev))
|
|
|
+ break;
|
|
|
+ /* fall through otherwise */
|
|
|
default:
|
|
|
DRM_ERROR("no plane for crtc\n");
|
|
|
return -EINVAL;
|
|
@@ -2440,7 +2465,7 @@ static void ironlake_fdi_link_train(struct drm_crtc *crtc)
|
|
|
|
|
|
}
|
|
|
|
|
|
-static const int snb_b_fdi_train_param [] = {
|
|
|
+static const int snb_b_fdi_train_param[] = {
|
|
|
FDI_LINK_TRAIN_400MV_0DB_SNB_B,
|
|
|
FDI_LINK_TRAIN_400MV_6DB_SNB_B,
|
|
|
FDI_LINK_TRAIN_600MV_3_5DB_SNB_B,
|
|
@@ -2496,7 +2521,7 @@ static void gen6_fdi_link_train(struct drm_crtc *crtc)
|
|
|
if (HAS_PCH_CPT(dev))
|
|
|
cpt_phase_pointer_enable(dev, pipe);
|
|
|
|
|
|
- for (i = 0; i < 4; i++ ) {
|
|
|
+ for (i = 0; i < 4; i++) {
|
|
|
reg = FDI_TX_CTL(pipe);
|
|
|
temp = I915_READ(reg);
|
|
|
temp &= ~FDI_LINK_TRAIN_VOL_EMP_MASK;
|
|
@@ -2545,7 +2570,7 @@ static void gen6_fdi_link_train(struct drm_crtc *crtc)
|
|
|
POSTING_READ(reg);
|
|
|
udelay(150);
|
|
|
|
|
|
- for (i = 0; i < 4; i++ ) {
|
|
|
+ for (i = 0; i < 4; i++) {
|
|
|
reg = FDI_TX_CTL(pipe);
|
|
|
temp = I915_READ(reg);
|
|
|
temp &= ~FDI_LINK_TRAIN_VOL_EMP_MASK;
|
|
@@ -2600,6 +2625,7 @@ static void ivb_manual_fdi_link_train(struct drm_crtc *crtc)
|
|
|
temp |= FDI_LINK_TRAIN_PATTERN_1_IVB;
|
|
|
temp &= ~FDI_LINK_TRAIN_VOL_EMP_MASK;
|
|
|
temp |= FDI_LINK_TRAIN_400MV_0DB_SNB_B;
|
|
|
+ temp |= FDI_COMPOSITE_SYNC;
|
|
|
I915_WRITE(reg, temp | FDI_TX_ENABLE);
|
|
|
|
|
|
reg = FDI_RX_CTL(pipe);
|
|
@@ -2607,6 +2633,7 @@ static void ivb_manual_fdi_link_train(struct drm_crtc *crtc)
|
|
|
temp &= ~FDI_LINK_TRAIN_AUTO;
|
|
|
temp &= ~FDI_LINK_TRAIN_PATTERN_MASK_CPT;
|
|
|
temp |= FDI_LINK_TRAIN_PATTERN_1_CPT;
|
|
|
+ temp |= FDI_COMPOSITE_SYNC;
|
|
|
I915_WRITE(reg, temp | FDI_RX_ENABLE);
|
|
|
|
|
|
POSTING_READ(reg);
|
|
@@ -2615,7 +2642,7 @@ static void ivb_manual_fdi_link_train(struct drm_crtc *crtc)
|
|
|
if (HAS_PCH_CPT(dev))
|
|
|
cpt_phase_pointer_enable(dev, pipe);
|
|
|
|
|
|
- for (i = 0; i < 4; i++ ) {
|
|
|
+ for (i = 0; i < 4; i++) {
|
|
|
reg = FDI_TX_CTL(pipe);
|
|
|
temp = I915_READ(reg);
|
|
|
temp &= ~FDI_LINK_TRAIN_VOL_EMP_MASK;
|
|
@@ -2657,7 +2684,7 @@ static void ivb_manual_fdi_link_train(struct drm_crtc *crtc)
|
|
|
POSTING_READ(reg);
|
|
|
udelay(150);
|
|
|
|
|
|
- for (i = 0; i < 4; i++ ) {
|
|
|
+ for (i = 0; i < 4; i++) {
|
|
|
reg = FDI_TX_CTL(pipe);
|
|
|
temp = I915_READ(reg);
|
|
|
temp &= ~FDI_LINK_TRAIN_VOL_EMP_MASK;
|
|
@@ -2866,7 +2893,7 @@ static void ironlake_pch_enable(struct drm_crtc *crtc)
|
|
|
struct drm_i915_private *dev_priv = dev->dev_private;
|
|
|
struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
|
|
|
int pipe = intel_crtc->pipe;
|
|
|
- u32 reg, temp;
|
|
|
+ u32 reg, temp, transc_sel;
|
|
|
|
|
|
/* For PCH output, training FDI link */
|
|
|
dev_priv->display.fdi_link_train(crtc);
|
|
@@ -2874,12 +2901,21 @@ static void ironlake_pch_enable(struct drm_crtc *crtc)
|
|
|
intel_enable_pch_pll(dev_priv, pipe);
|
|
|
|
|
|
if (HAS_PCH_CPT(dev)) {
|
|
|
+ transc_sel = intel_crtc->use_pll_a ? TRANSC_DPLLA_SEL :
|
|
|
+ TRANSC_DPLLB_SEL;
|
|
|
+
|
|
|
/* Be sure PCH DPLL SEL is set */
|
|
|
temp = I915_READ(PCH_DPLL_SEL);
|
|
|
- if (pipe == 0 && (temp & TRANSA_DPLL_ENABLE) == 0)
|
|
|
+ if (pipe == 0) {
|
|
|
+ temp &= ~(TRANSA_DPLLB_SEL);
|
|
|
temp |= (TRANSA_DPLL_ENABLE | TRANSA_DPLLA_SEL);
|
|
|
- else if (pipe == 1 && (temp & TRANSB_DPLL_ENABLE) == 0)
|
|
|
+ } else if (pipe == 1) {
|
|
|
+ temp &= ~(TRANSB_DPLLB_SEL);
|
|
|
temp |= (TRANSB_DPLL_ENABLE | TRANSB_DPLLB_SEL);
|
|
|
+ } else if (pipe == 2) {
|
|
|
+ temp &= ~(TRANSC_DPLLB_SEL);
|
|
|
+ temp |= (TRANSC_DPLL_ENABLE | transc_sel);
|
|
|
+ }
|
|
|
I915_WRITE(PCH_DPLL_SEL, temp);
|
|
|
}
|
|
|
|
|
@@ -2935,6 +2971,24 @@ static void ironlake_pch_enable(struct drm_crtc *crtc)
|
|
|
intel_enable_transcoder(dev_priv, pipe);
|
|
|
}
|
|
|
|
|
|
+void intel_cpt_verify_modeset(struct drm_device *dev, int pipe)
|
|
|
+{
|
|
|
+ struct drm_i915_private *dev_priv = dev->dev_private;
|
|
|
+ int dslreg = PIPEDSL(pipe), tc2reg = TRANS_CHICKEN2(pipe);
|
|
|
+ u32 temp;
|
|
|
+
|
|
|
+ temp = I915_READ(dslreg);
|
|
|
+ udelay(500);
|
|
|
+ if (wait_for(I915_READ(dslreg) != temp, 5)) {
|
|
|
+ /* Without this, mode sets may fail silently on FDI */
|
|
|
+ I915_WRITE(tc2reg, TRANS_AUTOTRAIN_GEN_STALL_DIS);
|
|
|
+ udelay(250);
|
|
|
+ I915_WRITE(tc2reg, 0);
|
|
|
+ if (wait_for(I915_READ(dslreg) != temp, 5))
|
|
|
+ DRM_ERROR("mode set failed: pipe %d stuck\n", pipe);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
static void ironlake_crtc_enable(struct drm_crtc *crtc)
|
|
|
{
|
|
|
struct drm_device *dev = crtc->dev;
|
|
@@ -3045,13 +3099,13 @@ static void ironlake_crtc_disable(struct drm_crtc *crtc)
|
|
|
temp = I915_READ(PCH_DPLL_SEL);
|
|
|
switch (pipe) {
|
|
|
case 0:
|
|
|
- temp &= ~(TRANSA_DPLL_ENABLE | TRANSA_DPLLA_SEL);
|
|
|
+ temp &= ~(TRANSA_DPLL_ENABLE | TRANSA_DPLLB_SEL);
|
|
|
break;
|
|
|
case 1:
|
|
|
temp &= ~(TRANSB_DPLL_ENABLE | TRANSB_DPLLB_SEL);
|
|
|
break;
|
|
|
case 2:
|
|
|
- /* FIXME: manage transcoder PLLs? */
|
|
|
+ /* C shares PLL A or B */
|
|
|
temp &= ~(TRANSC_DPLL_ENABLE | TRANSC_DPLLB_SEL);
|
|
|
break;
|
|
|
default:
|
|
@@ -3061,7 +3115,8 @@ static void ironlake_crtc_disable(struct drm_crtc *crtc)
|
|
|
}
|
|
|
|
|
|
/* disable PCH DPLL */
|
|
|
- intel_disable_pch_pll(dev_priv, pipe);
|
|
|
+ if (!intel_crtc->no_pll)
|
|
|
+ intel_disable_pch_pll(dev_priv, pipe);
|
|
|
|
|
|
/* Switch from PCDclk to Rawclk */
|
|
|
reg = FDI_RX_CTL(pipe);
|
|
@@ -3293,18 +3348,25 @@ static void ironlake_crtc_commit(struct drm_crtc *crtc)
|
|
|
ironlake_crtc_enable(crtc);
|
|
|
}
|
|
|
|
|
|
-void intel_encoder_prepare (struct drm_encoder *encoder)
|
|
|
+void intel_encoder_prepare(struct drm_encoder *encoder)
|
|
|
{
|
|
|
struct drm_encoder_helper_funcs *encoder_funcs = encoder->helper_private;
|
|
|
/* lvds has its own version of prepare see intel_lvds_prepare */
|
|
|
encoder_funcs->dpms(encoder, DRM_MODE_DPMS_OFF);
|
|
|
}
|
|
|
|
|
|
-void intel_encoder_commit (struct drm_encoder *encoder)
|
|
|
+void intel_encoder_commit(struct drm_encoder *encoder)
|
|
|
{
|
|
|
struct drm_encoder_helper_funcs *encoder_funcs = encoder->helper_private;
|
|
|
+ struct drm_device *dev = encoder->dev;
|
|
|
+ struct intel_encoder *intel_encoder = to_intel_encoder(encoder);
|
|
|
+ struct intel_crtc *intel_crtc = to_intel_crtc(intel_encoder->base.crtc);
|
|
|
+
|
|
|
/* lvds has its own version of commit see intel_lvds_commit */
|
|
|
encoder_funcs->dpms(encoder, DRM_MODE_DPMS_ON);
|
|
|
+
|
|
|
+ if (HAS_PCH_CPT(dev))
|
|
|
+ intel_cpt_verify_modeset(dev, intel_crtc->pipe);
|
|
|
}
|
|
|
|
|
|
void intel_encoder_destroy(struct drm_encoder *encoder)
|
|
@@ -4478,6 +4540,20 @@ static void sandybridge_update_wm(struct drm_device *dev)
|
|
|
enabled |= 2;
|
|
|
}
|
|
|
|
|
|
+ /* IVB has 3 pipes */
|
|
|
+ if (IS_IVYBRIDGE(dev) &&
|
|
|
+ g4x_compute_wm0(dev, 2,
|
|
|
+ &sandybridge_display_wm_info, latency,
|
|
|
+ &sandybridge_cursor_wm_info, latency,
|
|
|
+ &plane_wm, &cursor_wm)) {
|
|
|
+ I915_WRITE(WM0_PIPEC_IVB,
|
|
|
+ (plane_wm << WM0_PIPE_PLANE_SHIFT) | cursor_wm);
|
|
|
+ DRM_DEBUG_KMS("FIFO watermarks For pipe C -"
|
|
|
+ " plane %d, cursor: %d\n",
|
|
|
+ plane_wm, cursor_wm);
|
|
|
+ enabled |= 3;
|
|
|
+ }
|
|
|
+
|
|
|
/*
|
|
|
* Calculate and update the self-refresh watermark only when one
|
|
|
* display plane is used.
|
|
@@ -4584,7 +4660,9 @@ static void intel_update_watermarks(struct drm_device *dev)
|
|
|
|
|
|
static inline bool intel_panel_use_ssc(struct drm_i915_private *dev_priv)
|
|
|
{
|
|
|
- return dev_priv->lvds_use_ssc && i915_panel_use_ssc
|
|
|
+ if (i915_panel_use_ssc >= 0)
|
|
|
+ return i915_panel_use_ssc != 0;
|
|
|
+ return dev_priv->lvds_use_ssc
|
|
|
&& !(dev_priv->quirks & QUIRK_LVDS_SSC_DISABLE);
|
|
|
}
|
|
|
|
|
@@ -5107,36 +5185,52 @@ static int i9xx_crtc_mode_set(struct drm_crtc *crtc,
|
|
|
return ret;
|
|
|
}
|
|
|
|
|
|
-static void ironlake_update_pch_refclk(struct drm_device *dev)
|
|
|
+/*
|
|
|
+ * Initialize reference clocks when the driver loads
|
|
|
+ */
|
|
|
+void ironlake_init_pch_refclk(struct drm_device *dev)
|
|
|
{
|
|
|
struct drm_i915_private *dev_priv = dev->dev_private;
|
|
|
struct drm_mode_config *mode_config = &dev->mode_config;
|
|
|
- struct drm_crtc *crtc;
|
|
|
struct intel_encoder *encoder;
|
|
|
- struct intel_encoder *has_edp_encoder = NULL;
|
|
|
u32 temp;
|
|
|
bool has_lvds = false;
|
|
|
+ bool has_cpu_edp = false;
|
|
|
+ bool has_pch_edp = false;
|
|
|
+ bool has_panel = false;
|
|
|
+ bool has_ck505 = false;
|
|
|
+ bool can_ssc = false;
|
|
|
|
|
|
/* We need to take the global config into account */
|
|
|
- list_for_each_entry(crtc, &mode_config->crtc_list, head) {
|
|
|
- if (!crtc->enabled)
|
|
|
- continue;
|
|
|
-
|
|
|
- list_for_each_entry(encoder, &mode_config->encoder_list,
|
|
|
- base.head) {
|
|
|
- if (encoder->base.crtc != crtc)
|
|
|
- continue;
|
|
|
-
|
|
|
- switch (encoder->type) {
|
|
|
- case INTEL_OUTPUT_LVDS:
|
|
|
- has_lvds = true;
|
|
|
- case INTEL_OUTPUT_EDP:
|
|
|
- has_edp_encoder = encoder;
|
|
|
- break;
|
|
|
- }
|
|
|
+ list_for_each_entry(encoder, &mode_config->encoder_list,
|
|
|
+ base.head) {
|
|
|
+ switch (encoder->type) {
|
|
|
+ case INTEL_OUTPUT_LVDS:
|
|
|
+ has_panel = true;
|
|
|
+ has_lvds = true;
|
|
|
+ break;
|
|
|
+ case INTEL_OUTPUT_EDP:
|
|
|
+ has_panel = true;
|
|
|
+ if (intel_encoder_is_pch_edp(&encoder->base))
|
|
|
+ has_pch_edp = true;
|
|
|
+ else
|
|
|
+ has_cpu_edp = true;
|
|
|
+ break;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ if (HAS_PCH_IBX(dev)) {
|
|
|
+ has_ck505 = dev_priv->display_clock_mode;
|
|
|
+ can_ssc = has_ck505;
|
|
|
+ } else {
|
|
|
+ has_ck505 = false;
|
|
|
+ can_ssc = true;
|
|
|
+ }
|
|
|
+
|
|
|
+ DRM_DEBUG_KMS("has_panel %d has_lvds %d has_pch_edp %d has_cpu_edp %d has_ck505 %d\n",
|
|
|
+ has_panel, has_lvds, has_pch_edp, has_cpu_edp,
|
|
|
+ has_ck505);
|
|
|
+
|
|
|
/* Ironlake: try to setup display ref clock before DPLL
|
|
|
* enabling. This is only under driver's control after
|
|
|
* PCH B stepping, previous chipset stepping should be
|
|
@@ -5145,43 +5239,102 @@ static void ironlake_update_pch_refclk(struct drm_device *dev)
|
|
|
temp = I915_READ(PCH_DREF_CONTROL);
|
|
|
/* Always enable nonspread source */
|
|
|
temp &= ~DREF_NONSPREAD_SOURCE_MASK;
|
|
|
- temp |= DREF_NONSPREAD_SOURCE_ENABLE;
|
|
|
- temp &= ~DREF_SSC_SOURCE_MASK;
|
|
|
- temp |= DREF_SSC_SOURCE_ENABLE;
|
|
|
- I915_WRITE(PCH_DREF_CONTROL, temp);
|
|
|
|
|
|
- POSTING_READ(PCH_DREF_CONTROL);
|
|
|
- udelay(200);
|
|
|
+ if (has_ck505)
|
|
|
+ temp |= DREF_NONSPREAD_CK505_ENABLE;
|
|
|
+ else
|
|
|
+ temp |= DREF_NONSPREAD_SOURCE_ENABLE;
|
|
|
|
|
|
- if (has_edp_encoder) {
|
|
|
- if (intel_panel_use_ssc(dev_priv)) {
|
|
|
- temp |= DREF_SSC1_ENABLE;
|
|
|
- I915_WRITE(PCH_DREF_CONTROL, temp);
|
|
|
+ if (has_panel) {
|
|
|
+ temp &= ~DREF_SSC_SOURCE_MASK;
|
|
|
+ temp |= DREF_SSC_SOURCE_ENABLE;
|
|
|
|
|
|
- POSTING_READ(PCH_DREF_CONTROL);
|
|
|
- udelay(200);
|
|
|
+ /* SSC must be turned on before enabling the CPU output */
|
|
|
+ if (intel_panel_use_ssc(dev_priv) && can_ssc) {
|
|
|
+ DRM_DEBUG_KMS("Using SSC on panel\n");
|
|
|
+ temp |= DREF_SSC1_ENABLE;
|
|
|
}
|
|
|
+
|
|
|
+ /* Get SSC going before enabling the outputs */
|
|
|
+ I915_WRITE(PCH_DREF_CONTROL, temp);
|
|
|
+ POSTING_READ(PCH_DREF_CONTROL);
|
|
|
+ udelay(200);
|
|
|
+
|
|
|
temp &= ~DREF_CPU_SOURCE_OUTPUT_MASK;
|
|
|
|
|
|
/* Enable CPU source on CPU attached eDP */
|
|
|
- if (!intel_encoder_is_pch_edp(&has_edp_encoder->base)) {
|
|
|
- if (intel_panel_use_ssc(dev_priv))
|
|
|
+ if (has_cpu_edp) {
|
|
|
+ if (intel_panel_use_ssc(dev_priv) && can_ssc) {
|
|
|
+ DRM_DEBUG_KMS("Using SSC on eDP\n");
|
|
|
temp |= DREF_CPU_SOURCE_OUTPUT_DOWNSPREAD;
|
|
|
+ }
|
|
|
else
|
|
|
temp |= DREF_CPU_SOURCE_OUTPUT_NONSPREAD;
|
|
|
- } else {
|
|
|
- /* Enable SSC on PCH eDP if needed */
|
|
|
- if (intel_panel_use_ssc(dev_priv)) {
|
|
|
- DRM_ERROR("enabling SSC on PCH\n");
|
|
|
- temp |= DREF_SUPERSPREAD_SOURCE_ENABLE;
|
|
|
- }
|
|
|
- }
|
|
|
+ } else
|
|
|
+ temp |= DREF_CPU_SOURCE_OUTPUT_DISABLE;
|
|
|
+
|
|
|
+ I915_WRITE(PCH_DREF_CONTROL, temp);
|
|
|
+ POSTING_READ(PCH_DREF_CONTROL);
|
|
|
+ udelay(200);
|
|
|
+ } else {
|
|
|
+ DRM_DEBUG_KMS("Disabling SSC entirely\n");
|
|
|
+
|
|
|
+ temp &= ~DREF_CPU_SOURCE_OUTPUT_MASK;
|
|
|
+
|
|
|
+ /* Turn off CPU output */
|
|
|
+ temp |= DREF_CPU_SOURCE_OUTPUT_DISABLE;
|
|
|
+
|
|
|
+ I915_WRITE(PCH_DREF_CONTROL, temp);
|
|
|
+ POSTING_READ(PCH_DREF_CONTROL);
|
|
|
+ udelay(200);
|
|
|
+
|
|
|
+ /* Turn off the SSC source */
|
|
|
+ temp &= ~DREF_SSC_SOURCE_MASK;
|
|
|
+ temp |= DREF_SSC_SOURCE_DISABLE;
|
|
|
+
|
|
|
+ /* Turn off SSC1 */
|
|
|
+ temp &= ~ DREF_SSC1_ENABLE;
|
|
|
+
|
|
|
I915_WRITE(PCH_DREF_CONTROL, temp);
|
|
|
POSTING_READ(PCH_DREF_CONTROL);
|
|
|
udelay(200);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+static int ironlake_get_refclk(struct drm_crtc *crtc)
|
|
|
+{
|
|
|
+ struct drm_device *dev = crtc->dev;
|
|
|
+ struct drm_i915_private *dev_priv = dev->dev_private;
|
|
|
+ struct intel_encoder *encoder;
|
|
|
+ struct drm_mode_config *mode_config = &dev->mode_config;
|
|
|
+ struct intel_encoder *edp_encoder = NULL;
|
|
|
+ int num_connectors = 0;
|
|
|
+ bool is_lvds = false;
|
|
|
+
|
|
|
+ list_for_each_entry(encoder, &mode_config->encoder_list, base.head) {
|
|
|
+ if (encoder->base.crtc != crtc)
|
|
|
+ continue;
|
|
|
+
|
|
|
+ switch (encoder->type) {
|
|
|
+ case INTEL_OUTPUT_LVDS:
|
|
|
+ is_lvds = true;
|
|
|
+ break;
|
|
|
+ case INTEL_OUTPUT_EDP:
|
|
|
+ edp_encoder = encoder;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ num_connectors++;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (is_lvds && intel_panel_use_ssc(dev_priv) && num_connectors < 2) {
|
|
|
+ DRM_DEBUG_KMS("using SSC reference clock of %d MHz\n",
|
|
|
+ dev_priv->lvds_ssc_freq);
|
|
|
+ return dev_priv->lvds_ssc_freq * 1000;
|
|
|
+ }
|
|
|
+
|
|
|
+ return 120000;
|
|
|
+}
|
|
|
+
|
|
|
static int ironlake_crtc_mode_set(struct drm_crtc *crtc,
|
|
|
struct drm_display_mode *mode,
|
|
|
struct drm_display_mode *adjusted_mode,
|
|
@@ -5241,16 +5394,7 @@ static int ironlake_crtc_mode_set(struct drm_crtc *crtc,
|
|
|
num_connectors++;
|
|
|
}
|
|
|
|
|
|
- if (is_lvds && intel_panel_use_ssc(dev_priv) && num_connectors < 2) {
|
|
|
- refclk = dev_priv->lvds_ssc_freq * 1000;
|
|
|
- DRM_DEBUG_KMS("using SSC reference clock of %d MHz\n",
|
|
|
- refclk / 1000);
|
|
|
- } else {
|
|
|
- refclk = 96000;
|
|
|
- if (!has_edp_encoder ||
|
|
|
- intel_encoder_is_pch_edp(&has_edp_encoder->base))
|
|
|
- refclk = 120000; /* 120Mhz refclk */
|
|
|
- }
|
|
|
+ refclk = ironlake_get_refclk(crtc);
|
|
|
|
|
|
/*
|
|
|
* Returns a set of divisors for the desired target clock with the given
|
|
@@ -5377,8 +5521,6 @@ static int ironlake_crtc_mode_set(struct drm_crtc *crtc,
|
|
|
ironlake_compute_m_n(intel_crtc->bpp, lane, target_clock, link_bw,
|
|
|
&m_n);
|
|
|
|
|
|
- ironlake_update_pch_refclk(dev);
|
|
|
-
|
|
|
fp = clock.n << 16 | clock.m1 << 8 | clock.m2;
|
|
|
if (has_reduced_clock)
|
|
|
fp2 = reduced_clock.n << 16 | reduced_clock.m1 << 8 |
|
|
@@ -5450,39 +5592,32 @@ static int ironlake_crtc_mode_set(struct drm_crtc *crtc,
|
|
|
/* Set up the display plane register */
|
|
|
dspcntr = DISPPLANE_GAMMA_ENABLE;
|
|
|
|
|
|
- DRM_DEBUG_KMS("Mode for pipe %c:\n", pipe == 0 ? 'A' : 'B');
|
|
|
+ DRM_DEBUG_KMS("Mode for pipe %d:\n", pipe);
|
|
|
drm_mode_debug_printmodeline(mode);
|
|
|
|
|
|
/* PCH eDP needs FDI, but CPU eDP does not */
|
|
|
- if (!has_edp_encoder || intel_encoder_is_pch_edp(&has_edp_encoder->base)) {
|
|
|
- I915_WRITE(PCH_FP0(pipe), fp);
|
|
|
- I915_WRITE(PCH_DPLL(pipe), dpll & ~DPLL_VCO_ENABLE);
|
|
|
-
|
|
|
- POSTING_READ(PCH_DPLL(pipe));
|
|
|
- udelay(150);
|
|
|
- }
|
|
|
+ if (!intel_crtc->no_pll) {
|
|
|
+ if (!has_edp_encoder ||
|
|
|
+ intel_encoder_is_pch_edp(&has_edp_encoder->base)) {
|
|
|
+ I915_WRITE(PCH_FP0(pipe), fp);
|
|
|
+ I915_WRITE(PCH_DPLL(pipe), dpll & ~DPLL_VCO_ENABLE);
|
|
|
|
|
|
- /* enable transcoder DPLL */
|
|
|
- if (HAS_PCH_CPT(dev)) {
|
|
|
- temp = I915_READ(PCH_DPLL_SEL);
|
|
|
- switch (pipe) {
|
|
|
- case 0:
|
|
|
- temp |= TRANSA_DPLL_ENABLE | TRANSA_DPLLA_SEL;
|
|
|
- break;
|
|
|
- case 1:
|
|
|
- temp |= TRANSB_DPLL_ENABLE | TRANSB_DPLLB_SEL;
|
|
|
- break;
|
|
|
- case 2:
|
|
|
- /* FIXME: manage transcoder PLLs? */
|
|
|
- temp |= TRANSC_DPLL_ENABLE | TRANSC_DPLLB_SEL;
|
|
|
- break;
|
|
|
- default:
|
|
|
- BUG();
|
|
|
+ POSTING_READ(PCH_DPLL(pipe));
|
|
|
+ udelay(150);
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ if (dpll == (I915_READ(PCH_DPLL(0)) & 0x7fffffff) &&
|
|
|
+ fp == I915_READ(PCH_FP0(0))) {
|
|
|
+ intel_crtc->use_pll_a = true;
|
|
|
+ DRM_DEBUG_KMS("using pipe a dpll\n");
|
|
|
+ } else if (dpll == (I915_READ(PCH_DPLL(1)) & 0x7fffffff) &&
|
|
|
+ fp == I915_READ(PCH_FP0(1))) {
|
|
|
+ intel_crtc->use_pll_a = false;
|
|
|
+ DRM_DEBUG_KMS("using pipe b dpll\n");
|
|
|
+ } else {
|
|
|
+ DRM_DEBUG_KMS("no matching PLL configuration for pipe 2\n");
|
|
|
+ return -EINVAL;
|
|
|
}
|
|
|
- I915_WRITE(PCH_DPLL_SEL, temp);
|
|
|
-
|
|
|
- POSTING_READ(PCH_DPLL_SEL);
|
|
|
- udelay(150);
|
|
|
}
|
|
|
|
|
|
/* The LVDS pin pair needs to be on before the DPLLs are enabled.
|
|
@@ -5492,17 +5627,13 @@ static int ironlake_crtc_mode_set(struct drm_crtc *crtc,
|
|
|
if (is_lvds) {
|
|
|
temp = I915_READ(PCH_LVDS);
|
|
|
temp |= LVDS_PORT_EN | LVDS_A0A2_CLKA_POWER_UP;
|
|
|
- if (pipe == 1) {
|
|
|
- if (HAS_PCH_CPT(dev))
|
|
|
- temp |= PORT_TRANS_B_SEL_CPT;
|
|
|
- else
|
|
|
- temp |= LVDS_PIPEB_SELECT;
|
|
|
- } else {
|
|
|
- if (HAS_PCH_CPT(dev))
|
|
|
- temp &= ~PORT_TRANS_SEL_MASK;
|
|
|
- else
|
|
|
- temp &= ~LVDS_PIPEB_SELECT;
|
|
|
- }
|
|
|
+ if (HAS_PCH_CPT(dev))
|
|
|
+ temp |= PORT_TRANS_SEL_CPT(pipe);
|
|
|
+ else if (pipe == 1)
|
|
|
+ temp |= LVDS_PIPEB_SELECT;
|
|
|
+ else
|
|
|
+ temp &= ~LVDS_PIPEB_SELECT;
|
|
|
+
|
|
|
/* set the corresponsding LVDS_BORDER bit */
|
|
|
temp |= dev_priv->lvds_border_bits;
|
|
|
/* Set the B0-B3 data pairs corresponding to whether we're going to
|
|
@@ -5552,8 +5683,9 @@ static int ironlake_crtc_mode_set(struct drm_crtc *crtc,
|
|
|
I915_WRITE(TRANSDPLINK_N1(pipe), 0);
|
|
|
}
|
|
|
|
|
|
- if (!has_edp_encoder ||
|
|
|
- intel_encoder_is_pch_edp(&has_edp_encoder->base)) {
|
|
|
+ if (!intel_crtc->no_pll &&
|
|
|
+ (!has_edp_encoder ||
|
|
|
+ intel_encoder_is_pch_edp(&has_edp_encoder->base))) {
|
|
|
I915_WRITE(PCH_DPLL(pipe), dpll);
|
|
|
|
|
|
/* Wait for the clocks to stabilize. */
|
|
@@ -5569,18 +5701,20 @@ static int ironlake_crtc_mode_set(struct drm_crtc *crtc,
|
|
|
}
|
|
|
|
|
|
intel_crtc->lowfreq_avail = false;
|
|
|
- if (is_lvds && has_reduced_clock && i915_powersave) {
|
|
|
- I915_WRITE(PCH_FP1(pipe), fp2);
|
|
|
- intel_crtc->lowfreq_avail = true;
|
|
|
- if (HAS_PIPE_CXSR(dev)) {
|
|
|
- DRM_DEBUG_KMS("enabling CxSR downclocking\n");
|
|
|
- pipeconf |= PIPECONF_CXSR_DOWNCLOCK;
|
|
|
- }
|
|
|
- } else {
|
|
|
- I915_WRITE(PCH_FP1(pipe), fp);
|
|
|
- if (HAS_PIPE_CXSR(dev)) {
|
|
|
- DRM_DEBUG_KMS("disabling CxSR downclocking\n");
|
|
|
- pipeconf &= ~PIPECONF_CXSR_DOWNCLOCK;
|
|
|
+ if (!intel_crtc->no_pll) {
|
|
|
+ if (is_lvds && has_reduced_clock && i915_powersave) {
|
|
|
+ I915_WRITE(PCH_FP1(pipe), fp2);
|
|
|
+ intel_crtc->lowfreq_avail = true;
|
|
|
+ if (HAS_PIPE_CXSR(dev)) {
|
|
|
+ DRM_DEBUG_KMS("enabling CxSR downclocking\n");
|
|
|
+ pipeconf |= PIPECONF_CXSR_DOWNCLOCK;
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ I915_WRITE(PCH_FP1(pipe), fp);
|
|
|
+ if (HAS_PIPE_CXSR(dev)) {
|
|
|
+ DRM_DEBUG_KMS("disabling CxSR downclocking\n");
|
|
|
+ pipeconf &= ~PIPECONF_CXSR_DOWNCLOCK;
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
|
|
@@ -5677,6 +5811,131 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc,
|
|
|
return ret;
|
|
|
}
|
|
|
|
|
|
+static void g4x_write_eld(struct drm_connector *connector,
|
|
|
+ struct drm_crtc *crtc)
|
|
|
+{
|
|
|
+ struct drm_i915_private *dev_priv = connector->dev->dev_private;
|
|
|
+ uint8_t *eld = connector->eld;
|
|
|
+ uint32_t eldv;
|
|
|
+ uint32_t len;
|
|
|
+ uint32_t i;
|
|
|
+
|
|
|
+ i = I915_READ(G4X_AUD_VID_DID);
|
|
|
+
|
|
|
+ if (i == INTEL_AUDIO_DEVBLC || i == INTEL_AUDIO_DEVCL)
|
|
|
+ eldv = G4X_ELDV_DEVCL_DEVBLC;
|
|
|
+ else
|
|
|
+ eldv = G4X_ELDV_DEVCTG;
|
|
|
+
|
|
|
+ i = I915_READ(G4X_AUD_CNTL_ST);
|
|
|
+ i &= ~(eldv | G4X_ELD_ADDR);
|
|
|
+ len = (i >> 9) & 0x1f; /* ELD buffer size */
|
|
|
+ I915_WRITE(G4X_AUD_CNTL_ST, i);
|
|
|
+
|
|
|
+ if (!eld[0])
|
|
|
+ return;
|
|
|
+
|
|
|
+ len = min_t(uint8_t, eld[2], len);
|
|
|
+ DRM_DEBUG_DRIVER("ELD size %d\n", len);
|
|
|
+ for (i = 0; i < len; i++)
|
|
|
+ I915_WRITE(G4X_HDMIW_HDMIEDID, *((uint32_t *)eld + i));
|
|
|
+
|
|
|
+ i = I915_READ(G4X_AUD_CNTL_ST);
|
|
|
+ i |= eldv;
|
|
|
+ I915_WRITE(G4X_AUD_CNTL_ST, i);
|
|
|
+}
|
|
|
+
|
|
|
+static void ironlake_write_eld(struct drm_connector *connector,
|
|
|
+ struct drm_crtc *crtc)
|
|
|
+{
|
|
|
+ struct drm_i915_private *dev_priv = connector->dev->dev_private;
|
|
|
+ uint8_t *eld = connector->eld;
|
|
|
+ uint32_t eldv;
|
|
|
+ uint32_t i;
|
|
|
+ int len;
|
|
|
+ int hdmiw_hdmiedid;
|
|
|
+ int aud_cntl_st;
|
|
|
+ int aud_cntrl_st2;
|
|
|
+
|
|
|
+ if (IS_IVYBRIDGE(connector->dev)) {
|
|
|
+ hdmiw_hdmiedid = GEN7_HDMIW_HDMIEDID_A;
|
|
|
+ aud_cntl_st = GEN7_AUD_CNTRL_ST_A;
|
|
|
+ aud_cntrl_st2 = GEN7_AUD_CNTRL_ST2;
|
|
|
+ } else {
|
|
|
+ hdmiw_hdmiedid = GEN5_HDMIW_HDMIEDID_A;
|
|
|
+ aud_cntl_st = GEN5_AUD_CNTL_ST_A;
|
|
|
+ aud_cntrl_st2 = GEN5_AUD_CNTL_ST2;
|
|
|
+ }
|
|
|
+
|
|
|
+ i = to_intel_crtc(crtc)->pipe;
|
|
|
+ hdmiw_hdmiedid += i * 0x100;
|
|
|
+ aud_cntl_st += i * 0x100;
|
|
|
+
|
|
|
+ DRM_DEBUG_DRIVER("ELD on pipe %c\n", pipe_name(i));
|
|
|
+
|
|
|
+ i = I915_READ(aud_cntl_st);
|
|
|
+ i = (i >> 29) & 0x3; /* DIP_Port_Select, 0x1 = PortB */
|
|
|
+ if (!i) {
|
|
|
+ DRM_DEBUG_DRIVER("Audio directed to unknown port\n");
|
|
|
+ /* operate blindly on all ports */
|
|
|
+ eldv = GEN5_ELD_VALIDB;
|
|
|
+ eldv |= GEN5_ELD_VALIDB << 4;
|
|
|
+ eldv |= GEN5_ELD_VALIDB << 8;
|
|
|
+ } else {
|
|
|
+ DRM_DEBUG_DRIVER("ELD on port %c\n", 'A' + i);
|
|
|
+ eldv = GEN5_ELD_VALIDB << ((i - 1) * 4);
|
|
|
+ }
|
|
|
+
|
|
|
+ i = I915_READ(aud_cntrl_st2);
|
|
|
+ i &= ~eldv;
|
|
|
+ I915_WRITE(aud_cntrl_st2, i);
|
|
|
+
|
|
|
+ if (!eld[0])
|
|
|
+ return;
|
|
|
+
|
|
|
+ if (intel_pipe_has_type(crtc, INTEL_OUTPUT_DISPLAYPORT)) {
|
|
|
+ DRM_DEBUG_DRIVER("ELD: DisplayPort detected\n");
|
|
|
+ eld[5] |= (1 << 2); /* Conn_Type, 0x1 = DisplayPort */
|
|
|
+ }
|
|
|
+
|
|
|
+ i = I915_READ(aud_cntl_st);
|
|
|
+ i &= ~GEN5_ELD_ADDRESS;
|
|
|
+ I915_WRITE(aud_cntl_st, i);
|
|
|
+
|
|
|
+ len = min_t(uint8_t, eld[2], 21); /* 84 bytes of hw ELD buffer */
|
|
|
+ DRM_DEBUG_DRIVER("ELD size %d\n", len);
|
|
|
+ for (i = 0; i < len; i++)
|
|
|
+ I915_WRITE(hdmiw_hdmiedid, *((uint32_t *)eld + i));
|
|
|
+
|
|
|
+ i = I915_READ(aud_cntrl_st2);
|
|
|
+ i |= eldv;
|
|
|
+ I915_WRITE(aud_cntrl_st2, i);
|
|
|
+}
|
|
|
+
|
|
|
+void intel_write_eld(struct drm_encoder *encoder,
|
|
|
+ struct drm_display_mode *mode)
|
|
|
+{
|
|
|
+ struct drm_crtc *crtc = encoder->crtc;
|
|
|
+ struct drm_connector *connector;
|
|
|
+ struct drm_device *dev = encoder->dev;
|
|
|
+ struct drm_i915_private *dev_priv = dev->dev_private;
|
|
|
+
|
|
|
+ connector = drm_select_eld(encoder, mode);
|
|
|
+ if (!connector)
|
|
|
+ return;
|
|
|
+
|
|
|
+ DRM_DEBUG_DRIVER("ELD on [CONNECTOR:%d:%s], [ENCODER:%d:%s]\n",
|
|
|
+ connector->base.id,
|
|
|
+ drm_get_connector_name(connector),
|
|
|
+ connector->encoder->base.id,
|
|
|
+ drm_get_encoder_name(connector->encoder));
|
|
|
+
|
|
|
+ connector->eld[6] = drm_av_sync_delay(connector, mode) / 2;
|
|
|
+
|
|
|
+ if (dev_priv->display.write_eld)
|
|
|
+ dev_priv->display.write_eld(connector, crtc);
|
|
|
+}
|
|
|
+
|
|
|
/** Loads the palette/gamma unit for the CRTC with the prepared values */
|
|
|
void intel_crtc_load_lut(struct drm_crtc *crtc)
|
|
|
{
|
|
@@ -5758,6 +6017,31 @@ static void i9xx_update_cursor(struct drm_crtc *crtc, u32 base)
|
|
|
I915_WRITE(CURBASE(pipe), base);
|
|
|
}
|
|
|
|
|
|
+static void ivb_update_cursor(struct drm_crtc *crtc, u32 base)
|
|
|
+{
|
|
|
+ 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;
|
|
|
+ bool visible = base != 0;
|
|
|
+
|
|
|
+ if (intel_crtc->cursor_visible != visible) {
|
|
|
+ uint32_t cntl = I915_READ(CURCNTR_IVB(pipe));
|
|
|
+ if (base) {
|
|
|
+ cntl &= ~CURSOR_MODE;
|
|
|
+ cntl |= CURSOR_MODE_64_ARGB_AX | MCURSOR_GAMMA_ENABLE;
|
|
|
+ } else {
|
|
|
+ cntl &= ~(CURSOR_MODE | MCURSOR_GAMMA_ENABLE);
|
|
|
+ cntl |= CURSOR_MODE_DISABLE;
|
|
|
+ }
|
|
|
+ I915_WRITE(CURCNTR_IVB(pipe), cntl);
|
|
|
+
|
|
|
+ intel_crtc->cursor_visible = visible;
|
|
|
+ }
|
|
|
+ /* and commit changes on next vblank */
|
|
|
+ I915_WRITE(CURBASE_IVB(pipe), base);
|
|
|
+}
|
|
|
+
|
|
|
/* If no-part of the cursor is visible on the framebuffer, then the GPU may hang... */
|
|
|
static void intel_crtc_update_cursor(struct drm_crtc *crtc,
|
|
|
bool on)
|
|
@@ -5805,11 +6089,16 @@ static void intel_crtc_update_cursor(struct drm_crtc *crtc,
|
|
|
if (!visible && !intel_crtc->cursor_visible)
|
|
|
return;
|
|
|
|
|
|
- I915_WRITE(CURPOS(pipe), pos);
|
|
|
- if (IS_845G(dev) || IS_I865G(dev))
|
|
|
- i845_update_cursor(crtc, base);
|
|
|
- else
|
|
|
- i9xx_update_cursor(crtc, base);
|
|
|
+ if (IS_IVYBRIDGE(dev)) {
|
|
|
+ I915_WRITE(CURPOS_IVB(pipe), pos);
|
|
|
+ ivb_update_cursor(crtc, base);
|
|
|
+ } else {
|
|
|
+ I915_WRITE(CURPOS(pipe), pos);
|
|
|
+ if (IS_845G(dev) || IS_I865G(dev))
|
|
|
+ i845_update_cursor(crtc, base);
|
|
|
+ else
|
|
|
+ i9xx_update_cursor(crtc, base);
|
|
|
+ }
|
|
|
|
|
|
if (visible)
|
|
|
intel_mark_busy(dev, to_intel_framebuffer(crtc->fb)->obj);
|
|
@@ -7071,6 +7360,8 @@ static void intel_crtc_init(struct drm_device *dev, int pipe)
|
|
|
intel_crtc->bpp = 24; /* default for pre-Ironlake */
|
|
|
|
|
|
if (HAS_PCH_SPLIT(dev)) {
|
|
|
+ if (pipe == 2 && IS_IVYBRIDGE(dev))
|
|
|
+ intel_crtc->no_pll = true;
|
|
|
intel_helper_funcs.prepare = ironlake_crtc_prepare;
|
|
|
intel_helper_funcs.commit = ironlake_crtc_commit;
|
|
|
} else {
|
|
@@ -7250,6 +7541,9 @@ static void intel_setup_outputs(struct drm_device *dev)
|
|
|
|
|
|
/* disable all the possible outputs/crtcs before entering KMS mode */
|
|
|
drm_helper_disable_unused_functions(dev);
|
|
|
+
|
|
|
+ if (HAS_PCH_SPLIT(dev))
|
|
|
+ ironlake_init_pch_refclk(dev);
|
|
|
}
|
|
|
|
|
|
static void intel_user_framebuffer_destroy(struct drm_framebuffer *fb)
|
|
@@ -7494,6 +7788,10 @@ void gen6_disable_rps(struct drm_device *dev)
|
|
|
I915_WRITE(GEN6_RPNSWREQ, 1 << 31);
|
|
|
I915_WRITE(GEN6_PMINTRMSK, 0xffffffff);
|
|
|
I915_WRITE(GEN6_PMIER, 0);
|
|
|
+ /* Complete PM interrupt masking here doesn't race with the rps work
|
|
|
+ * item again unmasking PM interrupts because that is using a different
|
|
|
+ * register (PMIMR) to mask PM interrupts. The only risk is in leaving
|
|
|
+ * stale bits in PMIIR and PMIMR which gen6_enable_rps will clean up. */
|
|
|
|
|
|
spin_lock_irq(&dev_priv->rps_lock);
|
|
|
dev_priv->pm_iir = 0;
|
|
@@ -8154,7 +8452,7 @@ static void intel_init_display(struct drm_device *dev)
|
|
|
}
|
|
|
|
|
|
/* Returns the core display clock speed */
|
|
|
- if (IS_I945G(dev) || (IS_G33(dev) && ! IS_PINEVIEW_M(dev)))
|
|
|
+ if (IS_I945G(dev) || (IS_G33(dev) && !IS_PINEVIEW_M(dev)))
|
|
|
dev_priv->display.get_display_clock_speed =
|
|
|
i945_get_display_clock_speed;
|
|
|
else if (IS_I915G(dev))
|
|
@@ -8193,6 +8491,7 @@ static void intel_init_display(struct drm_device *dev)
|
|
|
}
|
|
|
dev_priv->display.fdi_link_train = ironlake_fdi_link_train;
|
|
|
dev_priv->display.init_clock_gating = ironlake_init_clock_gating;
|
|
|
+ dev_priv->display.write_eld = ironlake_write_eld;
|
|
|
} else if (IS_GEN6(dev)) {
|
|
|
if (SNB_READ_WM0_LATENCY()) {
|
|
|
dev_priv->display.update_wm = sandybridge_update_wm;
|
|
@@ -8203,6 +8502,7 @@ static void intel_init_display(struct drm_device *dev)
|
|
|
}
|
|
|
dev_priv->display.fdi_link_train = gen6_fdi_link_train;
|
|
|
dev_priv->display.init_clock_gating = gen6_init_clock_gating;
|
|
|
+ dev_priv->display.write_eld = ironlake_write_eld;
|
|
|
} else if (IS_IVYBRIDGE(dev)) {
|
|
|
/* FIXME: detect B0+ stepping and use auto training */
|
|
|
dev_priv->display.fdi_link_train = ivb_manual_fdi_link_train;
|
|
@@ -8214,7 +8514,7 @@ static void intel_init_display(struct drm_device *dev)
|
|
|
dev_priv->display.update_wm = NULL;
|
|
|
}
|
|
|
dev_priv->display.init_clock_gating = ivybridge_init_clock_gating;
|
|
|
-
|
|
|
+ dev_priv->display.write_eld = ironlake_write_eld;
|
|
|
} else
|
|
|
dev_priv->display.update_wm = NULL;
|
|
|
} else if (IS_PINEVIEW(dev)) {
|
|
@@ -8225,7 +8525,7 @@ static void intel_init_display(struct drm_device *dev)
|
|
|
DRM_INFO("failed to find known CxSR latency "
|
|
|
"(found ddr%s fsb freq %d, mem freq %d), "
|
|
|
"disabling CxSR\n",
|
|
|
- (dev_priv->is_ddr3 == 1) ? "3": "2",
|
|
|
+ (dev_priv->is_ddr3 == 1) ? "3" : "2",
|
|
|
dev_priv->fsb_freq, dev_priv->mem_freq);
|
|
|
/* Disable CxSR and never update its watermark again */
|
|
|
pineview_disable_cxsr(dev);
|
|
@@ -8234,6 +8534,7 @@ static void intel_init_display(struct drm_device *dev)
|
|
|
dev_priv->display.update_wm = pineview_update_wm;
|
|
|
dev_priv->display.init_clock_gating = gen3_init_clock_gating;
|
|
|
} else if (IS_G4X(dev)) {
|
|
|
+ dev_priv->display.write_eld = g4x_write_eld;
|
|
|
dev_priv->display.update_wm = g4x_update_wm;
|
|
|
dev_priv->display.init_clock_gating = g4x_init_clock_gating;
|
|
|
} else if (IS_GEN4(dev)) {
|
|
@@ -8294,7 +8595,7 @@ static void intel_init_display(struct drm_device *dev)
|
|
|
* resume, or other times. This quirk makes sure that's the case for
|
|
|
* affected systems.
|
|
|
*/
|
|
|
-static void quirk_pipea_force (struct drm_device *dev)
|
|
|
+static void quirk_pipea_force(struct drm_device *dev)
|
|
|
{
|
|
|
struct drm_i915_private *dev_priv = dev->dev_private;
|
|
|
|
|
@@ -8322,7 +8623,7 @@ struct intel_quirk intel_quirks[] = {
|
|
|
/* HP Compaq 2730p needs pipe A force quirk (LP: #291555) */
|
|
|
{ 0x2a42, 0x103c, 0x30eb, quirk_pipea_force },
|
|
|
/* HP Mini needs pipe A force quirk (LP: #322104) */
|
|
|
- { 0x27ae,0x103c, 0x361a, quirk_pipea_force },
|
|
|
+ { 0x27ae, 0x103c, 0x361a, quirk_pipea_force },
|
|
|
|
|
|
/* Thinkpad R31 needs pipe A force quirk */
|
|
|
{ 0x3577, 0x1014, 0x0505, quirk_pipea_force },
|
|
@@ -8488,6 +8789,7 @@ void intel_modeset_cleanup(struct drm_device *dev)
|
|
|
* enqueue unpin/hotplug work. */
|
|
|
drm_irq_uninstall(dev);
|
|
|
cancel_work_sync(&dev_priv->hotplug_work);
|
|
|
+ cancel_work_sync(&dev_priv->rps_work);
|
|
|
|
|
|
/* flush any delayed tasks or pending work */
|
|
|
flush_scheduled_work();
|
|
@@ -8573,7 +8875,7 @@ struct intel_display_error_state {
|
|
|
struct intel_display_error_state *
|
|
|
intel_display_capture_error_state(struct drm_device *dev)
|
|
|
{
|
|
|
- drm_i915_private_t *dev_priv = dev->dev_private;
|
|
|
+ drm_i915_private_t *dev_priv = dev->dev_private;
|
|
|
struct intel_display_error_state *error;
|
|
|
int i;
|
|
|
|
|
@@ -8589,7 +8891,7 @@ intel_display_capture_error_state(struct drm_device *dev)
|
|
|
error->plane[i].control = I915_READ(DSPCNTR(i));
|
|
|
error->plane[i].stride = I915_READ(DSPSTRIDE(i));
|
|
|
error->plane[i].size = I915_READ(DSPSIZE(i));
|
|
|
- error->plane[i].pos= I915_READ(DSPPOS(i));
|
|
|
+ error->plane[i].pos = I915_READ(DSPPOS(i));
|
|
|
error->plane[i].addr = I915_READ(DSPADDR(i));
|
|
|
if (INTEL_INFO(dev)->gen >= 4) {
|
|
|
error->plane[i].surface = I915_READ(DSPSURF(i));
|