summaryrefslogtreecommitdiffstats
path: root/drivers/gpu/drm/i915/intel_display.c
diff options
context:
space:
mode:
authorDave Airlie <airlied@redhat.com>2013-10-25 09:35:04 +0100
committerDave Airlie <airlied@redhat.com>2013-10-25 09:35:04 +0100
commitbe51e4a78155ff6c5d9299bf726e86b554e21117 (patch)
tree70d5260c64490a98a7ef894402715aa0244d30e2 /drivers/gpu/drm/i915/intel_display.c
parent5cab51cb3381157e652bb85e3e87adb44497c672 (diff)
parent6da7f10d296f4ac625f96b39eef22c41398727e3 (diff)
downloadblackbird-obmc-linux-be51e4a78155ff6c5d9299bf726e86b554e21117.tar.gz
blackbird-obmc-linux-be51e4a78155ff6c5d9299bf726e86b554e21117.zip
Merge tag 'drm-intel-next-2013-10-18' of git://people.freedesktop.org/~danvet/drm-intel into drm-next
- CRC support from Damien and He Shuang. Long term this should allow us to test an awful lot modesetting corner cases automatically. So for me as the maintainer this is really big. - HDMI audio fix from Jani. - VLV dpll computation code refactoring from Ville. - Fixups for the gpu booster from last time around (Chris). - Some cleanups in the context code from Ben. - More watermark work from Ville (we'll be getting there ...). - vblank timestamp improvements from Ville. - CONFIG_FB=n support, including drm core changes to make the fbdev helpers optional. - DP link training improvements (Jani). - mmio vtable from Ben, prep work for future hw. * tag 'drm-intel-next-2013-10-18' of git://people.freedesktop.org/~danvet/drm-intel: (132 commits) drm/i915/dp: don't mention eDP bpp clamping if it doesn't affect bpp drm/i915: remove dead code in ironlake_crtc_mode_set drm/i915: crc support for hsw drm/i915: fix CRC debugfs setup drm/i915: wait one vblank when disabling CRCs drm/i915: use ->get_vblank_counter for the crc frame counter drm/i915: wire up CRC interrupt for ilk/snb drm/i915: add CRC #defines for ilk/snb drm/i915: extract display_pipe_crc_update drm/i915: don't Oops in debugfs for I915_FBDEV=n drm/i915: set HDMI pixel clock in audio configuration drm/i915: pass mode to ELD write vfuncs cpufreq: Add dummy cpufreq_cpu_get/put for CONFIG_CPU_FREQ=n drm/i915: check gem bo size when creating framebuffers drm/i915: Use unsigned long for obj->user_pin_count drm/i915: prevent tiling changes on framebuffer backing storage drm/i915: grab dev->struct_mutex around framebuffer_init drm/i915: vlv: fix VGA hotplug after modeset drm: add support for additional stereo 3D modes drm/i915: preserve dispaly init order on ByT ...
Diffstat (limited to 'drivers/gpu/drm/i915/intel_display.c')
-rw-r--r--drivers/gpu/drm/i915/intel_display.c400
1 files changed, 240 insertions, 160 deletions
diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c
index 617b963dfb67..8905f83166f2 100644
--- a/drivers/gpu/drm/i915/intel_display.c
+++ b/drivers/gpu/drm/i915/intel_display.c
@@ -309,32 +309,30 @@ static const intel_limit_t intel_limits_ironlake_dual_lvds_100m = {
.p2_slow = 7, .p2_fast = 7 },
};
-static const intel_limit_t intel_limits_vlv_dac = {
- .dot = { .min = 25000, .max = 270000 },
+static const intel_limit_t intel_limits_vlv = {
+ /*
+ * These are the data rate limits (measured in fast clocks)
+ * since those are the strictest limits we have. The fast
+ * clock and actual rate limits are more relaxed, so checking
+ * them would make no difference.
+ */
+ .dot = { .min = 25000 * 5, .max = 270000 * 5 },
.vco = { .min = 4000000, .max = 6000000 },
.n = { .min = 1, .max = 7 },
- .m = { .min = 22, .max = 450 }, /* guess */
.m1 = { .min = 2, .max = 3 },
.m2 = { .min = 11, .max = 156 },
- .p = { .min = 10, .max = 30 },
- .p1 = { .min = 1, .max = 3 },
- .p2 = { .dot_limit = 270000,
- .p2_slow = 2, .p2_fast = 20 },
-};
-
-static const intel_limit_t intel_limits_vlv_hdmi = {
- .dot = { .min = 25000, .max = 270000 },
- .vco = { .min = 4000000, .max = 6000000 },
- .n = { .min = 1, .max = 7 },
- .m = { .min = 60, .max = 300 }, /* guess */
- .m1 = { .min = 2, .max = 3 },
- .m2 = { .min = 11, .max = 156 },
- .p = { .min = 10, .max = 30 },
.p1 = { .min = 2, .max = 3 },
- .p2 = { .dot_limit = 270000,
- .p2_slow = 2, .p2_fast = 20 },
+ .p2 = { .p2_slow = 2, .p2_fast = 20 }, /* slow=min, fast=max */
};
+static void vlv_clock(int refclk, intel_clock_t *clock)
+{
+ clock->m = clock->m1 * clock->m2;
+ clock->p = clock->p1 * clock->p2;
+ clock->vco = DIV_ROUND_CLOSEST(refclk * clock->m, clock->n);
+ clock->dot = DIV_ROUND_CLOSEST(clock->vco, clock->p);
+}
+
/**
* Returns whether any output on the specified pipe is of the specified type
*/
@@ -410,10 +408,7 @@ static const intel_limit_t *intel_limit(struct drm_crtc *crtc, int refclk)
else
limit = &intel_limits_pineview_sdvo;
} else if (IS_VALLEYVIEW(dev)) {
- if (intel_pipe_has_type(crtc, INTEL_OUTPUT_ANALOG))
- limit = &intel_limits_vlv_dac;
- else
- limit = &intel_limits_vlv_hdmi;
+ limit = &intel_limits_vlv;
} else if (!IS_GEN2(dev)) {
if (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS))
limit = &intel_limits_i9xx_lvds;
@@ -435,8 +430,8 @@ static void pineview_clock(int refclk, intel_clock_t *clock)
{
clock->m = clock->m2 + 2;
clock->p = clock->p1 * clock->p2;
- clock->vco = refclk * clock->m / clock->n;
- clock->dot = clock->vco / clock->p;
+ clock->vco = DIV_ROUND_CLOSEST(refclk * clock->m, clock->n);
+ clock->dot = DIV_ROUND_CLOSEST(clock->vco, clock->p);
}
static uint32_t i9xx_dpll_compute_m(struct dpll *dpll)
@@ -448,8 +443,8 @@ static void i9xx_clock(int refclk, intel_clock_t *clock)
{
clock->m = i9xx_dpll_compute_m(clock);
clock->p = clock->p1 * clock->p2;
- clock->vco = refclk * clock->m / (clock->n + 2);
- clock->dot = clock->vco / clock->p;
+ clock->vco = DIV_ROUND_CLOSEST(refclk * clock->m, clock->n + 2);
+ clock->dot = DIV_ROUND_CLOSEST(clock->vco, clock->p);
}
#define INTELPllInvalid(s) do { /* DRM_DEBUG(s); */ return false; } while (0)
@@ -462,20 +457,26 @@ static bool intel_PLL_is_valid(struct drm_device *dev,
const intel_limit_t *limit,
const intel_clock_t *clock)
{
+ if (clock->n < limit->n.min || limit->n.max < clock->n)
+ INTELPllInvalid("n out of range\n");
if (clock->p1 < limit->p1.min || limit->p1.max < clock->p1)
INTELPllInvalid("p1 out of range\n");
- if (clock->p < limit->p.min || limit->p.max < clock->p)
- INTELPllInvalid("p out of range\n");
if (clock->m2 < limit->m2.min || limit->m2.max < clock->m2)
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 && !IS_PINEVIEW(dev))
- INTELPllInvalid("m1 <= m2\n");
- if (clock->m < limit->m.min || limit->m.max < clock->m)
- INTELPllInvalid("m out of range\n");
- if (clock->n < limit->n.min || limit->n.max < clock->n)
- INTELPllInvalid("n out of range\n");
+
+ if (!IS_PINEVIEW(dev) && !IS_VALLEYVIEW(dev))
+ if (clock->m1 <= clock->m2)
+ INTELPllInvalid("m1 <= m2\n");
+
+ if (!IS_VALLEYVIEW(dev)) {
+ if (clock->p < limit->p.min || limit->p.max < clock->p)
+ INTELPllInvalid("p out of range\n");
+ if (clock->m < limit->m.min || limit->m.max < clock->m)
+ INTELPllInvalid("m out of range\n");
+ }
+
if (clock->vco < limit->vco.min || limit->vco.max < clock->vco)
INTELPllInvalid("vco out of range\n");
/* XXX: We may need to be checking "Dot clock" depending on the multiplier,
@@ -669,68 +670,56 @@ vlv_find_best_dpll(const intel_limit_t *limit, struct drm_crtc *crtc,
int target, int refclk, intel_clock_t *match_clock,
intel_clock_t *best_clock)
{
- u32 p1, p2, m1, m2, vco, bestn, bestm1, bestm2, bestp1, bestp2;
- u32 m, n, fastclk;
- u32 updrate, minupdate, p;
- unsigned long bestppm, ppm, absppm;
- int dotclk, flag;
-
- flag = 0;
- dotclk = target * 1000;
- bestppm = 1000000;
- ppm = absppm = 0;
- fastclk = dotclk / (2*100);
- updrate = 0;
- minupdate = 19200;
- n = p = p1 = p2 = m = m1 = m2 = vco = bestn = 0;
- bestm1 = bestm2 = bestp1 = bestp2 = 0;
+ struct drm_device *dev = crtc->dev;
+ intel_clock_t clock;
+ unsigned int bestppm = 1000000;
+ /* min update 19.2 MHz */
+ int max_n = min(limit->n.max, refclk / 19200);
+ bool found = false;
+
+ target *= 5; /* fast clock */
+
+ memset(best_clock, 0, sizeof(*best_clock));
/* based on hardware requirement, prefer smaller n to precision */
- for (n = limit->n.min; n <= ((refclk) / minupdate); n++) {
- updrate = refclk / n;
- for (p1 = limit->p1.max; p1 > limit->p1.min; p1--) {
- for (p2 = limit->p2.p2_fast+1; p2 > 0; p2--) {
- if (p2 > 10)
- p2 = p2 - 1;
- p = p1 * p2;
+ for (clock.n = limit->n.min; clock.n <= max_n; clock.n++) {
+ for (clock.p1 = limit->p1.max; clock.p1 >= limit->p1.min; clock.p1--) {
+ for (clock.p2 = limit->p2.p2_fast; clock.p2 >= limit->p2.p2_slow;
+ clock.p2 -= clock.p2 > 10 ? 2 : 1) {
+ clock.p = clock.p1 * clock.p2;
/* based on hardware requirement, prefer bigger m1,m2 values */
- for (m1 = limit->m1.min; m1 <= limit->m1.max; m1++) {
- m2 = DIV_ROUND_CLOSEST(fastclk * p * n, refclk * m1);
- m = m1 * m2;
- vco = updrate * m;
+ for (clock.m1 = limit->m1.min; clock.m1 <= limit->m1.max; clock.m1++) {
+ unsigned int ppm, diff;
+
+ clock.m2 = DIV_ROUND_CLOSEST(target * clock.p * clock.n,
+ refclk * clock.m1);
- if (vco < limit->vco.min || vco >= limit->vco.max)
+ vlv_clock(refclk, &clock);
+
+ if (!intel_PLL_is_valid(dev, limit,
+ &clock))
continue;
- ppm = 1000000 * ((vco / p) - fastclk) / fastclk;
- absppm = (ppm > 0) ? ppm : (-ppm);
- if (absppm < 100 && ((p1 * p2) > (bestp1 * bestp2))) {
+ diff = abs(clock.dot - target);
+ ppm = div_u64(1000000ULL * diff, target);
+
+ if (ppm < 100 && clock.p > best_clock->p) {
bestppm = 0;
- flag = 1;
- }
- if (absppm < bestppm - 10) {
- bestppm = absppm;
- flag = 1;
+ *best_clock = clock;
+ found = true;
}
- if (flag) {
- bestn = n;
- bestm1 = m1;
- bestm2 = m2;
- bestp1 = p1;
- bestp2 = p2;
- flag = 0;
+
+ if (bestppm >= 10 && ppm < bestppm - 10) {
+ bestppm = ppm;
+ *best_clock = clock;
+ found = true;
}
}
}
}
}
- best_clock->n = bestn;
- best_clock->m1 = bestm1;
- best_clock->m2 = bestm2;
- best_clock->p1 = bestp1;
- best_clock->p2 = bestp2;
- return true;
+ return found;
}
bool intel_crtc_active(struct drm_crtc *crtc)
@@ -811,6 +800,25 @@ void intel_wait_for_vblank(struct drm_device *dev, int pipe)
DRM_DEBUG_KMS("vblank wait timed out\n");
}
+static bool pipe_dsl_stopped(struct drm_device *dev, enum pipe pipe)
+{
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ u32 reg = PIPEDSL(pipe);
+ u32 line1, line2;
+ u32 line_mask;
+
+ if (IS_GEN2(dev))
+ line_mask = DSL_LINEMASK_GEN2;
+ else
+ line_mask = DSL_LINEMASK_GEN3;
+
+ line1 = I915_READ(reg) & line_mask;
+ mdelay(5);
+ line2 = I915_READ(reg) & line_mask;
+
+ return line1 == line2;
+}
+
/*
* intel_wait_for_pipe_off - wait for pipe to turn off
* @dev: drm device
@@ -842,22 +850,8 @@ void intel_wait_for_pipe_off(struct drm_device *dev, int pipe)
100))
WARN(1, "pipe_off wait timed out\n");
} else {
- u32 last_line, line_mask;
- int reg = PIPEDSL(pipe);
- unsigned long timeout = jiffies + msecs_to_jiffies(100);
-
- if (IS_GEN2(dev))
- line_mask = DSL_LINEMASK_GEN2;
- else
- line_mask = DSL_LINEMASK_GEN3;
-
/* Wait for the display line to settle */
- do {
- last_line = I915_READ(reg) & line_mask;
- mdelay(5);
- } while (((I915_READ(reg) & line_mask) != last_line) &&
- time_after(timeout, jiffies));
- if (time_after(jiffies, timeout))
+ if (wait_for(pipe_dsl_stopped(dev, pipe), 100))
WARN(1, "pipe_off wait timed out\n");
}
}
@@ -1823,63 +1817,75 @@ static void intel_disable_pipe(struct drm_i915_private *dev_priv,
* Plane regs are double buffered, going from enabled->disabled needs a
* trigger in order to latch. The display address reg provides this.
*/
-void intel_flush_display_plane(struct drm_i915_private *dev_priv,
- enum plane plane)
+void intel_flush_primary_plane(struct drm_i915_private *dev_priv,
+ enum plane plane)
{
- if (dev_priv->info->gen >= 4)
- I915_WRITE(DSPSURF(plane), I915_READ(DSPSURF(plane)));
- else
- I915_WRITE(DSPADDR(plane), I915_READ(DSPADDR(plane)));
+ u32 reg = dev_priv->info->gen >= 4 ? DSPSURF(plane) : DSPADDR(plane);
+
+ I915_WRITE(reg, I915_READ(reg));
+ POSTING_READ(reg);
}
/**
- * intel_enable_plane - enable a display plane on a given pipe
+ * intel_enable_primary_plane - enable the primary plane on a given pipe
* @dev_priv: i915 private structure
* @plane: plane to enable
* @pipe: pipe being fed
*
* Enable @plane on @pipe, making sure that @pipe is running first.
*/
-static void intel_enable_plane(struct drm_i915_private *dev_priv,
- enum plane plane, enum pipe pipe)
+static void intel_enable_primary_plane(struct drm_i915_private *dev_priv,
+ enum plane plane, enum pipe pipe)
{
+ struct intel_crtc *intel_crtc =
+ to_intel_crtc(dev_priv->pipe_to_crtc_mapping[pipe]);
int reg;
u32 val;
/* If the pipe isn't enabled, we can't pump pixels and may hang */
assert_pipe_enabled(dev_priv, pipe);
+ WARN(intel_crtc->primary_enabled, "Primary plane already enabled\n");
+
+ intel_crtc->primary_enabled = true;
+
reg = DSPCNTR(plane);
val = I915_READ(reg);
if (val & DISPLAY_PLANE_ENABLE)
return;
I915_WRITE(reg, val | DISPLAY_PLANE_ENABLE);
- intel_flush_display_plane(dev_priv, plane);
+ intel_flush_primary_plane(dev_priv, plane);
intel_wait_for_vblank(dev_priv->dev, pipe);
}
/**
- * intel_disable_plane - disable a display plane
+ * intel_disable_primary_plane - disable the primary plane
* @dev_priv: i915 private structure
* @plane: plane to disable
* @pipe: pipe consuming the data
*
* Disable @plane; should be an independent operation.
*/
-static void intel_disable_plane(struct drm_i915_private *dev_priv,
- enum plane plane, enum pipe pipe)
+static void intel_disable_primary_plane(struct drm_i915_private *dev_priv,
+ enum plane plane, enum pipe pipe)
{
+ struct intel_crtc *intel_crtc =
+ to_intel_crtc(dev_priv->pipe_to_crtc_mapping[pipe]);
int reg;
u32 val;
+ WARN(!intel_crtc->primary_enabled, "Primary plane already disabled\n");
+
+ intel_crtc->primary_enabled = false;
+
reg = DSPCNTR(plane);
val = I915_READ(reg);
if ((val & DISPLAY_PLANE_ENABLE) == 0)
return;
I915_WRITE(reg, val & ~DISPLAY_PLANE_ENABLE);
- intel_flush_display_plane(dev_priv, plane);
+ intel_flush_primary_plane(dev_priv, plane);
intel_wait_for_vblank(dev_priv->dev, pipe);
}
@@ -1915,10 +1921,7 @@ intel_pin_and_fence_fb_obj(struct drm_device *dev,
alignment = 0;
break;
case I915_TILING_Y:
- /* Despite that we check this in framebuffer_init userspace can
- * screw us over and change the tiling after the fact. Only
- * pinned buffers can't change their tiling. */
- DRM_DEBUG_DRIVER("Y tiled not allowed for scan out buffers\n");
+ WARN(1, "Y tiled bo slipped through, driver bug!\n");
return -EINVAL;
default:
BUG();
@@ -3332,7 +3335,7 @@ static void intel_disable_planes(struct drm_crtc *crtc)
intel_plane_disable(&intel_plane->base);
}
-static void hsw_enable_ips(struct intel_crtc *crtc)
+void hsw_enable_ips(struct intel_crtc *crtc)
{
struct drm_i915_private *dev_priv = crtc->base.dev->dev_private;
@@ -3345,9 +3348,17 @@ static void hsw_enable_ips(struct intel_crtc *crtc)
* for a vblank, so all we need to do here is to enable the IPS bit. */
assert_plane_enabled(dev_priv, crtc->plane);
I915_WRITE(IPS_CTL, IPS_ENABLE);
+
+ /* The bit only becomes 1 in the next vblank, so this wait here is
+ * essentially intel_wait_for_vblank. If we don't have this and don't
+ * wait for vblanks until the end of crtc_enable, then the HW state
+ * readout code will complain that the expected IPS_CTL value is not the
+ * one we read. */
+ if (wait_for(I915_READ_NOTRACE(IPS_CTL) & IPS_ENABLE, 50))
+ DRM_ERROR("Timed out waiting for IPS enable\n");
}
-static void hsw_disable_ips(struct intel_crtc *crtc)
+void hsw_disable_ips(struct intel_crtc *crtc)
{
struct drm_device *dev = crtc->base.dev;
struct drm_i915_private *dev_priv = dev->dev_private;
@@ -3454,7 +3465,7 @@ static void ironlake_crtc_enable(struct drm_crtc *crtc)
intel_update_watermarks(crtc);
intel_enable_pipe(dev_priv, pipe,
intel_crtc->config.has_pch_encoder, false);
- intel_enable_plane(dev_priv, plane, pipe);
+ intel_enable_primary_plane(dev_priv, plane, pipe);
intel_enable_planes(crtc);
intel_crtc_update_cursor(crtc, true);
@@ -3496,7 +3507,7 @@ static void haswell_crtc_enable_planes(struct drm_crtc *crtc)
int pipe = intel_crtc->pipe;
int plane = intel_crtc->plane;
- intel_enable_plane(dev_priv, plane, pipe);
+ intel_enable_primary_plane(dev_priv, plane, pipe);
intel_enable_planes(crtc);
intel_crtc_update_cursor(crtc, true);
@@ -3526,7 +3537,7 @@ static void haswell_crtc_disable_planes(struct drm_crtc *crtc)
intel_crtc_update_cursor(crtc, false);
intel_disable_planes(crtc);
- intel_disable_plane(dev_priv, plane, pipe);
+ intel_disable_primary_plane(dev_priv, plane, pipe);
}
/*
@@ -3665,7 +3676,7 @@ static void ironlake_crtc_disable(struct drm_crtc *crtc)
intel_crtc_update_cursor(crtc, false);
intel_disable_planes(crtc);
- intel_disable_plane(dev_priv, plane, pipe);
+ intel_disable_primary_plane(dev_priv, plane, pipe);
if (intel_crtc->config.has_pch_encoder)
intel_set_pch_fifo_underrun_reporting(dev, pipe, false);
@@ -3873,7 +3884,7 @@ static void valleyview_crtc_enable(struct drm_crtc *crtc)
intel_update_watermarks(crtc);
intel_enable_pipe(dev_priv, pipe, false, is_dsi);
- intel_enable_plane(dev_priv, plane, pipe);
+ intel_enable_primary_plane(dev_priv, plane, pipe);
intel_enable_planes(crtc);
intel_crtc_update_cursor(crtc, true);
@@ -3911,7 +3922,7 @@ static void i9xx_crtc_enable(struct drm_crtc *crtc)
intel_update_watermarks(crtc);
intel_enable_pipe(dev_priv, pipe, false, false);
- intel_enable_plane(dev_priv, plane, pipe);
+ intel_enable_primary_plane(dev_priv, plane, pipe);
intel_enable_planes(crtc);
/* The fixup needs to happen before cursor is enabled */
if (IS_G4X(dev))
@@ -3967,7 +3978,7 @@ static void i9xx_crtc_disable(struct drm_crtc *crtc)
intel_crtc_dpms_overlay(intel_crtc, false);
intel_crtc_update_cursor(crtc, false);
intel_disable_planes(crtc);
- intel_disable_plane(dev_priv, plane, pipe);
+ intel_disable_primary_plane(dev_priv, plane, pipe);
intel_disable_pipe(dev_priv, pipe);
@@ -5201,10 +5212,10 @@ static void vlv_crtc_clock_get(struct intel_crtc *crtc,
clock.p1 = (mdiv >> DPIO_P1_SHIFT) & 7;
clock.p2 = (mdiv >> DPIO_P2_SHIFT) & 0x1f;
- clock.vco = refclk * clock.m1 * clock.m2 / clock.n;
- clock.dot = 2 * clock.vco / (clock.p1 * clock.p2);
+ vlv_clock(refclk, &clock);
- pipe_config->port_clock = clock.dot / 10;
+ /* clock.dot is the fast clock */
+ pipe_config->port_clock = clock.dot / 5;
}
static bool i9xx_get_pipe_config(struct intel_crtc *crtc,
@@ -6061,11 +6072,6 @@ static int ironlake_crtc_mode_set(struct drm_crtc *crtc,
else
intel_crtc->lowfreq_avail = false;
- if (intel_crtc->config.has_pch_encoder) {
- pll = intel_crtc_to_shared_dpll(intel_crtc);
-
- }
-
intel_set_pipe_timings(intel_crtc);
if (intel_crtc->config.has_pch_encoder) {
@@ -6712,6 +6718,44 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc,
return 0;
}
+static struct {
+ int clock;
+ u32 config;
+} hdmi_audio_clock[] = {
+ { DIV_ROUND_UP(25200 * 1000, 1001), AUD_CONFIG_PIXEL_CLOCK_HDMI_25175 },
+ { 25200, AUD_CONFIG_PIXEL_CLOCK_HDMI_25200 }, /* default per bspec */
+ { 27000, AUD_CONFIG_PIXEL_CLOCK_HDMI_27000 },
+ { 27000 * 1001 / 1000, AUD_CONFIG_PIXEL_CLOCK_HDMI_27027 },
+ { 54000, AUD_CONFIG_PIXEL_CLOCK_HDMI_54000 },
+ { 54000 * 1001 / 1000, AUD_CONFIG_PIXEL_CLOCK_HDMI_54054 },
+ { DIV_ROUND_UP(74250 * 1000, 1001), AUD_CONFIG_PIXEL_CLOCK_HDMI_74176 },
+ { 74250, AUD_CONFIG_PIXEL_CLOCK_HDMI_74250 },
+ { DIV_ROUND_UP(148500 * 1000, 1001), AUD_CONFIG_PIXEL_CLOCK_HDMI_148352 },
+ { 148500, AUD_CONFIG_PIXEL_CLOCK_HDMI_148500 },
+};
+
+/* get AUD_CONFIG_PIXEL_CLOCK_HDMI_* value for mode */
+static u32 audio_config_hdmi_pixel_clock(struct drm_display_mode *mode)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(hdmi_audio_clock); i++) {
+ if (mode->clock == hdmi_audio_clock[i].clock)
+ break;
+ }
+
+ if (i == ARRAY_SIZE(hdmi_audio_clock)) {
+ DRM_DEBUG_KMS("HDMI audio pixel clock setting for %d not found, falling back to defaults\n", mode->clock);
+ i = 1;
+ }
+
+ DRM_DEBUG_KMS("Configuring HDMI audio for pixel clock %d (0x%08x)\n",
+ hdmi_audio_clock[i].clock,
+ hdmi_audio_clock[i].config);
+
+ return hdmi_audio_clock[i].config;
+}
+
static bool intel_eld_uptodate(struct drm_connector *connector,
int reg_eldv, uint32_t bits_eldv,
int reg_elda, uint32_t bits_elda,
@@ -6742,7 +6786,8 @@ static bool intel_eld_uptodate(struct drm_connector *connector,
}
static void g4x_write_eld(struct drm_connector *connector,
- struct drm_crtc *crtc)
+ struct drm_crtc *crtc,
+ struct drm_display_mode *mode)
{
struct drm_i915_private *dev_priv = connector->dev->dev_private;
uint8_t *eld = connector->eld;
@@ -6782,7 +6827,8 @@ static void g4x_write_eld(struct drm_connector *connector,
}
static void haswell_write_eld(struct drm_connector *connector,
- struct drm_crtc *crtc)
+ struct drm_crtc *crtc,
+ struct drm_display_mode *mode)
{
struct drm_i915_private *dev_priv = connector->dev->dev_private;
uint8_t *eld = connector->eld;
@@ -6835,8 +6881,9 @@ static void haswell_write_eld(struct drm_connector *connector,
DRM_DEBUG_DRIVER("ELD: DisplayPort detected\n");
eld[5] |= (1 << 2); /* Conn_Type, 0x1 = DisplayPort */
I915_WRITE(aud_config, AUD_CONFIG_N_VALUE_INDEX); /* 0x1 = DP */
- } else
- I915_WRITE(aud_config, 0);
+ } else {
+ I915_WRITE(aud_config, audio_config_hdmi_pixel_clock(mode));
+ }
if (intel_eld_uptodate(connector,
aud_cntrl_st2, eldv,
@@ -6869,7 +6916,8 @@ static void haswell_write_eld(struct drm_connector *connector,
}
static void ironlake_write_eld(struct drm_connector *connector,
- struct drm_crtc *crtc)
+ struct drm_crtc *crtc,
+ struct drm_display_mode *mode)
{
struct drm_i915_private *dev_priv = connector->dev->dev_private;
uint8_t *eld = connector->eld;
@@ -6913,8 +6961,9 @@ static void ironlake_write_eld(struct drm_connector *connector,
DRM_DEBUG_DRIVER("ELD: DisplayPort detected\n");
eld[5] |= (1 << 2); /* Conn_Type, 0x1 = DisplayPort */
I915_WRITE(aud_config, AUD_CONFIG_N_VALUE_INDEX); /* 0x1 = DP */
- } else
- I915_WRITE(aud_config, 0);
+ } else {
+ I915_WRITE(aud_config, audio_config_hdmi_pixel_clock(mode));
+ }
if (intel_eld_uptodate(connector,
aud_cntrl_st2, eldv,
@@ -6964,7 +7013,7 @@ void intel_write_eld(struct drm_encoder *encoder,
connector->eld[6] = drm_av_sync_delay(connector, mode) / 2;
if (dev_priv->display.write_eld)
- dev_priv->display.write_eld(connector, crtc);
+ dev_priv->display.write_eld(connector, crtc, mode);
}
static void i845_update_cursor(struct drm_crtc *crtc, u32 base)
@@ -7271,14 +7320,21 @@ intel_framebuffer_create(struct drm_device *dev,
return ERR_PTR(-ENOMEM);
}
+ ret = i915_mutex_lock_interruptible(dev);
+ if (ret)
+ goto err;
+
ret = intel_framebuffer_init(dev, intel_fb, mode_cmd, obj);
- if (ret) {
- drm_gem_object_unreference_unlocked(&obj->base);
- kfree(intel_fb);
- return ERR_PTR(ret);
- }
+ mutex_unlock(&dev->struct_mutex);
+ if (ret)
+ goto err;
return &intel_fb->base;
+err:
+ drm_gem_object_unreference_unlocked(&obj->base);
+ kfree(intel_fb);
+
+ return ERR_PTR(ret);
}
static u32
@@ -7321,6 +7377,7 @@ static struct drm_framebuffer *
mode_fits_in_fbdev(struct drm_device *dev,
struct drm_display_mode *mode)
{
+#ifdef CONFIG_DRM_I915_FBDEV
struct drm_i915_private *dev_priv = dev->dev_private;
struct drm_i915_gem_object *obj;
struct drm_framebuffer *fb;
@@ -7341,6 +7398,9 @@ mode_fits_in_fbdev(struct drm_device *dev,
return NULL;
return fb;
+#else
+ return NULL;
+#endif
}
bool intel_get_load_detect_pipe(struct drm_connector *connector,
@@ -9866,7 +9926,13 @@ static void intel_setup_outputs(struct drm_device *dev)
if (I915_READ(PCH_DP_D) & DP_DETECTED)
intel_dp_init(dev, PCH_DP_D, PORT_D);
} else if (IS_VALLEYVIEW(dev)) {
- /* Check for built-in panel first. Shares lanes with HDMI on SDVOC */
+ if (I915_READ(VLV_DISPLAY_BASE + GEN4_HDMIB) & SDVO_DETECTED) {
+ intel_hdmi_init(dev, VLV_DISPLAY_BASE + GEN4_HDMIB,
+ PORT_B);
+ if (I915_READ(VLV_DISPLAY_BASE + DP_B) & DP_DETECTED)
+ intel_dp_init(dev, VLV_DISPLAY_BASE + DP_B, PORT_B);
+ }
+
if (I915_READ(VLV_DISPLAY_BASE + GEN4_HDMIC) & SDVO_DETECTED) {
intel_hdmi_init(dev, VLV_DISPLAY_BASE + GEN4_HDMIC,
PORT_C);
@@ -9875,13 +9941,6 @@ static void intel_setup_outputs(struct drm_device *dev)
PORT_C);
}
- if (I915_READ(VLV_DISPLAY_BASE + GEN4_HDMIB) & SDVO_DETECTED) {
- intel_hdmi_init(dev, VLV_DISPLAY_BASE + GEN4_HDMIB,
- PORT_B);
- if (I915_READ(VLV_DISPLAY_BASE + DP_B) & DP_DETECTED)
- intel_dp_init(dev, VLV_DISPLAY_BASE + DP_B, PORT_B);
- }
-
intel_dsi_init(dev);
} else if (SUPPORTS_DIGITAL_OUTPUTS(dev)) {
bool found = false;
@@ -9938,6 +9997,7 @@ static void intel_setup_outputs(struct drm_device *dev)
void intel_framebuffer_fini(struct intel_framebuffer *fb)
{
drm_framebuffer_cleanup(&fb->base);
+ WARN_ON(!fb->obj->framebuffer_references--);
drm_gem_object_unreference_unlocked(&fb->obj->base);
}
@@ -9969,9 +10029,12 @@ int intel_framebuffer_init(struct drm_device *dev,
struct drm_mode_fb_cmd2 *mode_cmd,
struct drm_i915_gem_object *obj)
{
+ int aligned_height, tile_height;
int pitch_limit;
int ret;
+ WARN_ON(!mutex_is_locked(&dev->struct_mutex));
+
if (obj->tiling_mode == I915_TILING_Y) {
DRM_DEBUG("hardware does not support tiling Y\n");
return -EINVAL;
@@ -10060,8 +10123,16 @@ int intel_framebuffer_init(struct drm_device *dev,
if (mode_cmd->offsets[0] != 0)
return -EINVAL;
+ tile_height = IS_GEN2(dev) ? 16 : 8;
+ aligned_height = ALIGN(mode_cmd->height,
+ obj->tiling_mode ? tile_height : 1);
+ /* FIXME drm helper for size checks (especially planar formats)? */
+ if (obj->base.size < aligned_height * mode_cmd->pitches[0])
+ return -EINVAL;
+
drm_helper_mode_fill_fb_struct(&intel_fb->base, mode_cmd);
intel_fb->obj = obj;
+ intel_fb->obj->framebuffer_references++;
ret = drm_framebuffer_init(dev, &intel_fb->base, &intel_fb_funcs);
if (ret) {
@@ -10087,9 +10158,15 @@ intel_user_framebuffer_create(struct drm_device *dev,
return intel_framebuffer_create(dev, mode_cmd, obj);
}
+#ifndef CONFIG_DRM_I915_FBDEV
+static inline void intel_fbdev_output_poll_changed(struct drm_device *dev)
+{
+}
+#endif
+
static const struct drm_mode_config_funcs intel_mode_funcs = {
.fb_create = intel_user_framebuffer_create,
- .output_poll_changed = intel_fb_output_poll_changed,
+ .output_poll_changed = intel_fbdev_output_poll_changed,
};
/* Set up chip specific display functions */
@@ -10304,8 +10381,7 @@ static struct intel_quirk intel_quirks[] = {
/* ThinkPad T60 needs pipe A force quirk (bug #16494) */
{ 0x2782, 0x17aa, 0x201a, quirk_pipea_force },
- /* 830/845 need to leave pipe A & dpll A up */
- { 0x2562, PCI_ANY_ID, PCI_ANY_ID, quirk_pipea_force },
+ /* 830 needs to leave pipe A & dpll A up */
{ 0x3577, PCI_ANY_ID, PCI_ANY_ID, quirk_pipea_force },
/* Lenovo U160 cannot use SSC on LVDS */
@@ -10674,7 +10750,7 @@ void i915_redisable_vga(struct drm_device *dev)
(I915_READ(HSW_PWR_WELL_DRIVER) & HSW_PWR_WELL_STATE_ENABLED) == 0)
return;
- if (I915_READ(vga_reg) != VGA_DISP_DISABLE) {
+ if (!(I915_READ(vga_reg) & VGA_DISP_DISABLE)) {
DRM_DEBUG_KMS("Something enabled VGA plane, disabling it\n");
i915_disable_vga(dev);
i915_disable_vga_mem(dev);
@@ -10698,6 +10774,7 @@ static void intel_modeset_readout_hw_state(struct drm_device *dev)
&crtc->config);
crtc->base.enabled = crtc->active;
+ crtc->primary_enabled = crtc->active;
DRM_DEBUG_KMS("[CRTC:%d] hw state readout: %s\n",
crtc->base.base.id,
@@ -10738,11 +10815,11 @@ static void intel_modeset_readout_hw_state(struct drm_device *dev)
}
encoder->connectors_active = false;
- DRM_DEBUG_KMS("[ENCODER:%d:%s] hw state readout: %s, pipe=%i\n",
+ DRM_DEBUG_KMS("[ENCODER:%d:%s] hw state readout: %s, pipe %c\n",
encoder->base.base.id,
drm_get_encoder_name(&encoder->base),
encoder->base.crtc ? "enabled" : "disabled",
- pipe);
+ pipe_name(pipe));
}
list_for_each_entry(connector, &dev->mode_config.connector_list,
@@ -10815,6 +10892,9 @@ void intel_modeset_setup_hw_state(struct drm_device *dev,
pll->on = false;
}
+ if (IS_HASWELL(dev))
+ ilk_wm_get_hw_state(dev);
+
if (force_restore) {
i915_redisable_vga(dev);
OpenPOWER on IntegriCloud