drm/i915: Share crtc setup and teardown between dpms and disable/enable
This closes a couple of corner cases where we introduced and forgot about a couple of routines that need to be called when disabling the crtc and then re-enabling it. The code needs to be moved again so that the common bits are shared across generations. Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
This commit is contained in:
@@ -44,7 +44,7 @@
|
|||||||
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_update_watermarks(struct drm_device *dev);
|
||||||
static void intel_increase_pllclock(struct drm_crtc *crtc);
|
static void intel_increase_pllclock(struct drm_crtc *crtc);
|
||||||
static void intel_crtc_update_cursor(struct drm_crtc *crtc);
|
static void intel_crtc_update_cursor(struct drm_crtc *crtc, bool on);
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
/* given values */
|
/* given values */
|
||||||
@@ -1927,6 +1927,26 @@ static void intel_flush_display_plane(struct drm_device *dev,
|
|||||||
I915_WRITE(reg, I915_READ(reg));
|
I915_WRITE(reg, I915_READ(reg));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* When we disable a pipe, we need to clear any pending scanline wait events
|
||||||
|
* to avoid hanging the ring, which we assume we are waiting on.
|
||||||
|
*/
|
||||||
|
static void intel_clear_scanline_wait(struct drm_device *dev)
|
||||||
|
{
|
||||||
|
struct drm_i915_private *dev_priv = dev->dev_private;
|
||||||
|
u32 tmp;
|
||||||
|
|
||||||
|
if (IS_GEN2(dev))
|
||||||
|
/* Can't break the hang on i8xx */
|
||||||
|
return;
|
||||||
|
|
||||||
|
tmp = I915_READ(PRB0_CTL);
|
||||||
|
if (tmp & RING_WAIT) {
|
||||||
|
I915_WRITE(PRB0_CTL, tmp);
|
||||||
|
POSTING_READ(PRB0_CTL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void ironlake_crtc_enable(struct drm_crtc *crtc)
|
static void ironlake_crtc_enable(struct drm_crtc *crtc)
|
||||||
{
|
{
|
||||||
struct drm_device *dev = crtc->dev;
|
struct drm_device *dev = crtc->dev;
|
||||||
@@ -1936,6 +1956,8 @@ static void ironlake_crtc_enable(struct drm_crtc *crtc)
|
|||||||
int plane = intel_crtc->plane;
|
int plane = intel_crtc->plane;
|
||||||
u32 reg, temp;
|
u32 reg, temp;
|
||||||
|
|
||||||
|
intel_update_watermarks(dev);
|
||||||
|
|
||||||
if (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS)) {
|
if (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS)) {
|
||||||
temp = I915_READ(PCH_LVDS);
|
temp = I915_READ(PCH_LVDS);
|
||||||
if ((temp & LVDS_PORT_EN) == 0)
|
if ((temp & LVDS_PORT_EN) == 0)
|
||||||
@@ -2082,6 +2104,7 @@ static void ironlake_crtc_enable(struct drm_crtc *crtc)
|
|||||||
|
|
||||||
intel_crtc_load_lut(crtc);
|
intel_crtc_load_lut(crtc);
|
||||||
intel_update_fbc(dev);
|
intel_update_fbc(dev);
|
||||||
|
intel_crtc_update_cursor(crtc, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void ironlake_crtc_disable(struct drm_crtc *crtc)
|
static void ironlake_crtc_disable(struct drm_crtc *crtc)
|
||||||
@@ -2094,6 +2117,7 @@ static void ironlake_crtc_disable(struct drm_crtc *crtc)
|
|||||||
u32 reg, temp;
|
u32 reg, temp;
|
||||||
|
|
||||||
drm_vblank_off(dev, pipe);
|
drm_vblank_off(dev, pipe);
|
||||||
|
intel_crtc_update_cursor(crtc, false);
|
||||||
|
|
||||||
/* Disable display plane */
|
/* Disable display plane */
|
||||||
reg = DSPCNTR(plane);
|
reg = DSPCNTR(plane);
|
||||||
@@ -2220,6 +2244,10 @@ static void ironlake_crtc_disable(struct drm_crtc *crtc)
|
|||||||
/* Wait for the clocks to turn off. */
|
/* Wait for the clocks to turn off. */
|
||||||
POSTING_READ(reg);
|
POSTING_READ(reg);
|
||||||
udelay(100);
|
udelay(100);
|
||||||
|
|
||||||
|
intel_update_watermarks(dev);
|
||||||
|
intel_update_fbc(dev);
|
||||||
|
intel_clear_scanline_wait(dev);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void ironlake_crtc_dpms(struct drm_crtc *crtc, int mode)
|
static void ironlake_crtc_dpms(struct drm_crtc *crtc, int mode)
|
||||||
@@ -2270,6 +2298,8 @@ static void i9xx_crtc_enable(struct drm_crtc *crtc)
|
|||||||
int plane = intel_crtc->plane;
|
int plane = intel_crtc->plane;
|
||||||
u32 reg, temp;
|
u32 reg, temp;
|
||||||
|
|
||||||
|
intel_update_watermarks(dev);
|
||||||
|
|
||||||
/* Enable the DPLL */
|
/* Enable the DPLL */
|
||||||
reg = DPLL(pipe);
|
reg = DPLL(pipe);
|
||||||
temp = I915_READ(reg);
|
temp = I915_READ(reg);
|
||||||
@@ -2312,6 +2342,7 @@ static void i9xx_crtc_enable(struct drm_crtc *crtc)
|
|||||||
|
|
||||||
/* Give the overlay scaler a chance to enable if it's on this pipe */
|
/* Give the overlay scaler a chance to enable if it's on this pipe */
|
||||||
intel_crtc_dpms_overlay(intel_crtc, true);
|
intel_crtc_dpms_overlay(intel_crtc, true);
|
||||||
|
intel_crtc_update_cursor(crtc, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void i9xx_crtc_disable(struct drm_crtc *crtc)
|
static void i9xx_crtc_disable(struct drm_crtc *crtc)
|
||||||
@@ -2325,6 +2356,7 @@ static void i9xx_crtc_disable(struct drm_crtc *crtc)
|
|||||||
|
|
||||||
/* Give the overlay scaler a chance to disable if it's on this pipe */
|
/* Give the overlay scaler a chance to disable if it's on this pipe */
|
||||||
intel_crtc_dpms_overlay(intel_crtc, false);
|
intel_crtc_dpms_overlay(intel_crtc, false);
|
||||||
|
intel_crtc_update_cursor(crtc, false);
|
||||||
drm_vblank_off(dev, pipe);
|
drm_vblank_off(dev, pipe);
|
||||||
|
|
||||||
if (dev_priv->cfb_plane == plane &&
|
if (dev_priv->cfb_plane == plane &&
|
||||||
@@ -2346,7 +2378,7 @@ static void i9xx_crtc_disable(struct drm_crtc *crtc)
|
|||||||
|
|
||||||
/* Don't disable pipe A or pipe A PLLs if needed */
|
/* Don't disable pipe A or pipe A PLLs if needed */
|
||||||
if (pipe == 0 && (dev_priv->quirks & QUIRK_PIPEA_FORCE))
|
if (pipe == 0 && (dev_priv->quirks & QUIRK_PIPEA_FORCE))
|
||||||
return;
|
goto done;
|
||||||
|
|
||||||
/* Next, disable display pipes */
|
/* Next, disable display pipes */
|
||||||
reg = PIPECONF(pipe);
|
reg = PIPECONF(pipe);
|
||||||
@@ -2368,6 +2400,11 @@ static void i9xx_crtc_disable(struct drm_crtc *crtc)
|
|||||||
POSTING_READ(reg);
|
POSTING_READ(reg);
|
||||||
udelay(150);
|
udelay(150);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
done:
|
||||||
|
intel_update_fbc(dev);
|
||||||
|
intel_update_watermarks(dev);
|
||||||
|
intel_clear_scanline_wait(dev);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void i9xx_crtc_dpms(struct drm_crtc *crtc, int mode)
|
static void i9xx_crtc_dpms(struct drm_crtc *crtc, int mode)
|
||||||
@@ -2387,26 +2424,6 @@ static void i9xx_crtc_dpms(struct drm_crtc *crtc, int mode)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* When we disable a pipe, we need to clear any pending scanline wait events
|
|
||||||
* to avoid hanging the ring, which we assume we are waiting on.
|
|
||||||
*/
|
|
||||||
static void intel_clear_scanline_wait(struct drm_device *dev)
|
|
||||||
{
|
|
||||||
struct drm_i915_private *dev_priv = dev->dev_private;
|
|
||||||
u32 tmp;
|
|
||||||
|
|
||||||
if (IS_GEN2(dev))
|
|
||||||
/* Can't break the hang on i8xx */
|
|
||||||
return;
|
|
||||||
|
|
||||||
tmp = I915_READ(PRB0_CTL);
|
|
||||||
if (tmp & RING_WAIT) {
|
|
||||||
I915_WRITE(PRB0_CTL, tmp);
|
|
||||||
POSTING_READ(PRB0_CTL);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the power management mode of the pipe and plane.
|
* Sets the power management mode of the pipe and plane.
|
||||||
*/
|
*/
|
||||||
@@ -2423,34 +2440,9 @@ static void intel_crtc_dpms(struct drm_crtc *crtc, int mode)
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
intel_crtc->dpms_mode = mode;
|
intel_crtc->dpms_mode = mode;
|
||||||
intel_crtc->cursor_on = mode == DRM_MODE_DPMS_ON;
|
|
||||||
|
|
||||||
/* When switching on the display, ensure that SR is disabled
|
|
||||||
* with multiple pipes prior to enabling to new pipe.
|
|
||||||
*
|
|
||||||
* When switching off the display, make sure the cursor is
|
|
||||||
* properly hidden and there are no pending waits prior to
|
|
||||||
* disabling the pipe.
|
|
||||||
*/
|
|
||||||
if (mode == DRM_MODE_DPMS_ON)
|
|
||||||
intel_update_watermarks(dev);
|
|
||||||
else
|
|
||||||
intel_crtc_update_cursor(crtc);
|
|
||||||
|
|
||||||
dev_priv->display.dpms(crtc, mode);
|
dev_priv->display.dpms(crtc, mode);
|
||||||
|
|
||||||
if (mode == DRM_MODE_DPMS_ON) {
|
|
||||||
intel_crtc_update_cursor(crtc);
|
|
||||||
} else {
|
|
||||||
/* XXX Note that this is not a complete solution, but a hack
|
|
||||||
* to avoid the most frequently hit hang.
|
|
||||||
*/
|
|
||||||
intel_clear_scanline_wait(dev);
|
|
||||||
|
|
||||||
intel_update_watermarks(dev);
|
|
||||||
}
|
|
||||||
intel_update_fbc(dev);
|
|
||||||
|
|
||||||
if (!dev->primary->master)
|
if (!dev->primary->master)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@@ -2485,50 +2477,22 @@ static void intel_crtc_dpms(struct drm_crtc *crtc, int mode)
|
|||||||
*/
|
*/
|
||||||
static void i9xx_crtc_prepare(struct drm_crtc *crtc)
|
static void i9xx_crtc_prepare(struct drm_crtc *crtc)
|
||||||
{
|
{
|
||||||
struct drm_device *dev = crtc->dev;
|
|
||||||
struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
|
|
||||||
|
|
||||||
intel_crtc->cursor_on = false;
|
|
||||||
intel_crtc_update_cursor(crtc);
|
|
||||||
|
|
||||||
i9xx_crtc_disable(crtc);
|
i9xx_crtc_disable(crtc);
|
||||||
intel_clear_scanline_wait(dev);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void i9xx_crtc_commit(struct drm_crtc *crtc)
|
static void i9xx_crtc_commit(struct drm_crtc *crtc)
|
||||||
{
|
{
|
||||||
struct drm_device *dev = crtc->dev;
|
|
||||||
struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
|
|
||||||
|
|
||||||
intel_update_watermarks(dev);
|
|
||||||
i9xx_crtc_enable(crtc);
|
i9xx_crtc_enable(crtc);
|
||||||
|
|
||||||
intel_crtc->cursor_on = true;
|
|
||||||
intel_crtc_update_cursor(crtc);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void ironlake_crtc_prepare(struct drm_crtc *crtc)
|
static void ironlake_crtc_prepare(struct drm_crtc *crtc)
|
||||||
{
|
{
|
||||||
struct drm_device *dev = crtc->dev;
|
|
||||||
struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
|
|
||||||
|
|
||||||
intel_crtc->cursor_on = false;
|
|
||||||
intel_crtc_update_cursor(crtc);
|
|
||||||
|
|
||||||
ironlake_crtc_disable(crtc);
|
ironlake_crtc_disable(crtc);
|
||||||
intel_clear_scanline_wait(dev);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void ironlake_crtc_commit(struct drm_crtc *crtc)
|
static void ironlake_crtc_commit(struct drm_crtc *crtc)
|
||||||
{
|
{
|
||||||
struct drm_device *dev = crtc->dev;
|
|
||||||
struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
|
|
||||||
|
|
||||||
intel_update_watermarks(dev);
|
|
||||||
ironlake_crtc_enable(crtc);
|
ironlake_crtc_enable(crtc);
|
||||||
|
|
||||||
intel_crtc->cursor_on = true;
|
|
||||||
intel_crtc_update_cursor(crtc);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void intel_encoder_prepare (struct drm_encoder *encoder)
|
void intel_encoder_prepare (struct drm_encoder *encoder)
|
||||||
@@ -3615,7 +3579,7 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Ensure that the cursor is valid for the new mode before changing... */
|
/* Ensure that the cursor is valid for the new mode before changing... */
|
||||||
intel_crtc_update_cursor(crtc);
|
intel_crtc_update_cursor(crtc, true);
|
||||||
|
|
||||||
if (is_lvds && dev_priv->lvds_downclock_avail) {
|
if (is_lvds && dev_priv->lvds_downclock_avail) {
|
||||||
has_reduced_clock = limit->find_pll(limit, crtc,
|
has_reduced_clock = limit->find_pll(limit, crtc,
|
||||||
@@ -4225,7 +4189,8 @@ static void i9xx_update_cursor(struct drm_crtc *crtc, u32 base)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* If no-part of the cursor is visible on the framebuffer, then the GPU may hang... */
|
/* 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)
|
static void intel_crtc_update_cursor(struct drm_crtc *crtc,
|
||||||
|
bool on)
|
||||||
{
|
{
|
||||||
struct drm_device *dev = crtc->dev;
|
struct drm_device *dev = crtc->dev;
|
||||||
struct drm_i915_private *dev_priv = dev->dev_private;
|
struct drm_i915_private *dev_priv = dev->dev_private;
|
||||||
@@ -4238,7 +4203,7 @@ static void intel_crtc_update_cursor(struct drm_crtc *crtc)
|
|||||||
|
|
||||||
pos = 0;
|
pos = 0;
|
||||||
|
|
||||||
if (intel_crtc->cursor_on && crtc->fb) {
|
if (on && crtc->enabled && crtc->fb) {
|
||||||
base = intel_crtc->cursor_addr;
|
base = intel_crtc->cursor_addr;
|
||||||
if (x > (int) crtc->fb->width)
|
if (x > (int) crtc->fb->width)
|
||||||
base = 0;
|
base = 0;
|
||||||
@@ -4370,7 +4335,7 @@ static int intel_crtc_cursor_set(struct drm_crtc *crtc,
|
|||||||
intel_crtc->cursor_width = width;
|
intel_crtc->cursor_width = width;
|
||||||
intel_crtc->cursor_height = height;
|
intel_crtc->cursor_height = height;
|
||||||
|
|
||||||
intel_crtc_update_cursor(crtc);
|
intel_crtc_update_cursor(crtc, true);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
fail_unpin:
|
fail_unpin:
|
||||||
@@ -4389,7 +4354,7 @@ static int intel_crtc_cursor_move(struct drm_crtc *crtc, int x, int y)
|
|||||||
intel_crtc->cursor_x = x;
|
intel_crtc->cursor_x = x;
|
||||||
intel_crtc->cursor_y = y;
|
intel_crtc->cursor_y = y;
|
||||||
|
|
||||||
intel_crtc_update_cursor(crtc);
|
intel_crtc_update_cursor(crtc, true);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@@ -180,7 +180,7 @@ struct intel_crtc {
|
|||||||
uint32_t cursor_addr;
|
uint32_t cursor_addr;
|
||||||
int16_t cursor_x, cursor_y;
|
int16_t cursor_x, cursor_y;
|
||||||
int16_t cursor_width, cursor_height;
|
int16_t cursor_width, cursor_height;
|
||||||
bool cursor_visible, cursor_on;
|
bool cursor_visible;
|
||||||
};
|
};
|
||||||
|
|
||||||
#define to_intel_crtc(x) container_of(x, struct intel_crtc, base)
|
#define to_intel_crtc(x) container_of(x, struct intel_crtc, base)
|
||||||
|
Reference in New Issue
Block a user