Commit a1ceb677 authored by Daniel Vetter's avatar Daniel Vetter

Merge the modeset-rework, basic conversion into drm-intel-next

As a quick reference I'll detail the motivation and design of the new code a
bit here (mostly stitched together from patchbomb announcements and commits
introducing the new concepts).

The crtc helper code has the fundamental assumption that encoders and crtcs can
be enabled/disabled in any order, as long as we take care of depencies (which
means that enabled encoders need an enabled crtc to feed them data,
essentially).

Our hw works differently. We already have tons of ugly cases where crtc code
enables encoder hw (or encoder->mode_set enables stuff that should only be
enabled in enocder->commit) to work around these issues. But on the disable
side we can't pull off similar tricks - there we actually need to rework the
modeset sequence that controls all this. And this is also the real motivation
why I've finally undertaken this rewrite: eDP on my shiny new Ivybridge
Ultrabook is broken, and it's broken due to the wrong disable sequence ...

The new code introduces a few interfaces and concepts:

- Add new encoder->enable/disable functions which are directly called from the
crtc->enable/disable function. This ensures that the encoder's can be
enabled/disabled at a very specific in the modeset sequence, controlled by our
platform specific code (instead of the crtc helper code calling them at a time
it deems convenient).

- Rework the dpms code - our code has mostly 1:1 connector:encoder mappings and
does support cloning on only a few encoders, so we can simplify things quite a
bit.

- Also only ever disable/enable the entire output pipeline. This ensures that
we obey the right sequence of enabling/disabling things, trying to be clever
here mostly just complicates the code and results in bugs. For cloneable
encoders this requires a bit of special handling to ensure that outputs can
still be disabled individually, but it simplifies the common case.

- Add infrastructure to read out the current hw state. No amount of careful
ordering will help us if we brick the hw on the initial modeset setup. Which
could happen if we just randomly disable things, oblivious to the state set up
by the bios. Hence we need to be able to read that out. As a benefit, we grow a
few generic functions useful to cross-check our modeset code with actual hw
state.

With all this in place, we can copy&paste the crtc helper code into the
drm/i915 driver and start to rework it:

- As detailed above, the new code only disables/enables an entire output pipe.
As a preparation for global mode-changes (e.g. reassigning shared resources) it
keeps track of which pipes need to be touched by a set of bitmasks.

- To ensure that we correctly disable the current display pipes, we need to
know the currently active connector/encoder/crtc linking. The old crtc helper
simply overwrote these links with the new setup, the new code stages the new
links in ->new_* pointers. Those get commited to the real linking pointers once
the old output configuration has been torn down, before the ->mode_set
callbacks are called.

- Finally the code adds tons of self-consistency checks by employing the new hw
state readout functions to cross-check the actual hw state with what the
datastructure think it should be. These checks are done both after every
modeset and after the hw state has been read out and sanitized at boot/resume
time. All these checks greatly helped in tracking down regressions and bugs in
the new code.

With this new basis, a lot of cleanups and improvements to the code are now
possible (besides the DP fixes that ultimately made me write this), but not yet
done:

- I think we should create struct intel_mode and use it as the adjusted mode
everywhere to store little pieces like needs_tvclock, pipe dithering values or
dp link parameters. That would still be a layering violation, but at least we
wouldn't need to recompute these kinds of things in intel_display.c. Especially
the port bpc computation needed for selecting the pipe bpc and dithering
settings in intel_display.c is rather gross.

- In a related rework we could implement ->mode_valid in terms of ->mode_fixup
in a generic way - I've hunted down too many bugs where ->mode_valid did the
right thing, but ->mode_fixup didn't. Or vice versa, resulting in funny bugs
for user-supplied modes.

- Ditch the idea to rework the hdp handling in the common crtc helper code and
just move things to i915.ko. Which would rid us of the ->detect crtc helper
dependencies.

- LVDS wire pair and pll enabling is all done in the crtc->mode_set function
currently. We should be able to move this to the crtc_enable callbacks (or in
the case of the LVDS wire pair enabling, into some encoder callback).

Last, but not least, this new code should also help in enabling a few neat
features: The hw state readout code prepares (but there are still big pieces
missing) for fastboot, i.e. avoiding the inital modeset at boot-up and just
taking over the configuration left behind by the bios. We also should be able
to extend the configuration checks in the beginning of the modeset sequence and
make better decisions about shared resources (which is the entire point behind
the atomic/global modeset ioctl).
Tested-by: default avatarJani Nikula <jani.nikula@intel.com>
Tested-by: default avatarBen Widawsky <ben@bwidawsk.net>
Tested-by: default avatarDamien Lespiau <damien.lespiau@intel.com>
Tested-by: default avatarRodrigo Vivi <rodrigo.vivi@gmail.com>
Acked-by: default avatarChris Wilson <chris@chris-wilson.co.uk>
Tested-by: default avatarVijay Purushothaman <vijay.a.purushothaman@intel.com>
Acked-by: default avatarVijay Purushothaman <vijay.a.purushothaman@intel.com>
Tested-by: default avatarPaulo Zanoni <paulo.r.zanoni@intel.com>
Acked-by: default avatarPaulo Zanoni <paulo.r.zanoni@intel.com>
Tested-by: default avatarJesse Barnes <jbarnes@virtuousgeek.org>
Signed-off-by: default avatarDaniel Vetter <daniel.vetter@ffwll.ch>
parents 8c3f929b b980514c
...@@ -114,6 +114,12 @@ struct intel_dvo_dev_ops { ...@@ -114,6 +114,12 @@ struct intel_dvo_dev_ops {
*/ */
enum drm_connector_status (*detect)(struct intel_dvo_device *dvo); enum drm_connector_status (*detect)(struct intel_dvo_device *dvo);
/*
* Probe the current hw status, returning true if the connected output
* is active.
*/
bool (*get_hw_state)(struct intel_dvo_device *dev);
/** /**
* Query the device for the modes it provides. * Query the device for the modes it provides.
* *
......
...@@ -359,6 +359,18 @@ static void ch7017_dpms(struct intel_dvo_device *dvo, bool enable) ...@@ -359,6 +359,18 @@ static void ch7017_dpms(struct intel_dvo_device *dvo, bool enable)
msleep(20); msleep(20);
} }
static bool ch7017_get_hw_state(struct intel_dvo_device *dvo)
{
uint8_t val;
ch7017_read(dvo, CH7017_LVDS_POWER_DOWN, &val);
if (val & CH7017_LVDS_POWER_DOWN_EN)
return false;
else
return true;
}
static void ch7017_dump_regs(struct intel_dvo_device *dvo) static void ch7017_dump_regs(struct intel_dvo_device *dvo)
{ {
uint8_t val; uint8_t val;
...@@ -396,6 +408,7 @@ struct intel_dvo_dev_ops ch7017_ops = { ...@@ -396,6 +408,7 @@ struct intel_dvo_dev_ops ch7017_ops = {
.mode_valid = ch7017_mode_valid, .mode_valid = ch7017_mode_valid,
.mode_set = ch7017_mode_set, .mode_set = ch7017_mode_set,
.dpms = ch7017_dpms, .dpms = ch7017_dpms,
.get_hw_state = ch7017_get_hw_state,
.dump_regs = ch7017_dump_regs, .dump_regs = ch7017_dump_regs,
.destroy = ch7017_destroy, .destroy = ch7017_destroy,
}; };
...@@ -297,6 +297,18 @@ static void ch7xxx_dpms(struct intel_dvo_device *dvo, bool enable) ...@@ -297,6 +297,18 @@ static void ch7xxx_dpms(struct intel_dvo_device *dvo, bool enable)
ch7xxx_writeb(dvo, CH7xxx_PM, CH7xxx_PM_FPD); ch7xxx_writeb(dvo, CH7xxx_PM, CH7xxx_PM_FPD);
} }
static bool ch7xxx_get_hw_state(struct intel_dvo_device *dvo)
{
u8 val;
ch7xxx_readb(dvo, CH7xxx_PM, &val);
if (val & CH7xxx_PM_FPD)
return false;
else
return true;
}
static void ch7xxx_dump_regs(struct intel_dvo_device *dvo) static void ch7xxx_dump_regs(struct intel_dvo_device *dvo)
{ {
int i; int i;
...@@ -326,6 +338,7 @@ struct intel_dvo_dev_ops ch7xxx_ops = { ...@@ -326,6 +338,7 @@ struct intel_dvo_dev_ops ch7xxx_ops = {
.mode_valid = ch7xxx_mode_valid, .mode_valid = ch7xxx_mode_valid,
.mode_set = ch7xxx_mode_set, .mode_set = ch7xxx_mode_set,
.dpms = ch7xxx_dpms, .dpms = ch7xxx_dpms,
.get_hw_state = ch7xxx_get_hw_state,
.dump_regs = ch7xxx_dump_regs, .dump_regs = ch7xxx_dump_regs,
.destroy = ch7xxx_destroy, .destroy = ch7xxx_destroy,
}; };
...@@ -323,6 +323,20 @@ static void ivch_dpms(struct intel_dvo_device *dvo, bool enable) ...@@ -323,6 +323,20 @@ static void ivch_dpms(struct intel_dvo_device *dvo, bool enable)
udelay(16 * 1000); udelay(16 * 1000);
} }
static bool ivch_get_hw_state(struct intel_dvo_device *dvo)
{
uint16_t vr01;
/* Set the new power state of the panel. */
if (!ivch_read(dvo, VR01, &vr01))
return false;
if (vr01 & VR01_LCD_ENABLE)
return true;
else
return false;
}
static void ivch_mode_set(struct intel_dvo_device *dvo, static void ivch_mode_set(struct intel_dvo_device *dvo,
struct drm_display_mode *mode, struct drm_display_mode *mode,
struct drm_display_mode *adjusted_mode) struct drm_display_mode *adjusted_mode)
...@@ -413,6 +427,7 @@ static void ivch_destroy(struct intel_dvo_device *dvo) ...@@ -413,6 +427,7 @@ static void ivch_destroy(struct intel_dvo_device *dvo)
struct intel_dvo_dev_ops ivch_ops = { struct intel_dvo_dev_ops ivch_ops = {
.init = ivch_init, .init = ivch_init,
.dpms = ivch_dpms, .dpms = ivch_dpms,
.get_hw_state = ivch_get_hw_state,
.mode_valid = ivch_mode_valid, .mode_valid = ivch_mode_valid,
.mode_set = ivch_mode_set, .mode_set = ivch_mode_set,
.detect = ivch_detect, .detect = ivch_detect,
......
...@@ -492,6 +492,20 @@ static void ns2501_mode_set(struct intel_dvo_device *dvo, ...@@ -492,6 +492,20 @@ static void ns2501_mode_set(struct intel_dvo_device *dvo,
restore_dvo(dvo); restore_dvo(dvo);
} }
/* set the NS2501 power state */
static bool ns2501_get_hw_state(struct intel_dvo_device *dvo)
{
unsigned char ch;
if (!ns2501_readb(dvo, NS2501_REG8, &ch))
return false;
if (ch & NS2501_8_PD)
return true;
else
return false;
}
/* set the NS2501 power state */ /* set the NS2501 power state */
static void ns2501_dpms(struct intel_dvo_device *dvo, bool enable) static void ns2501_dpms(struct intel_dvo_device *dvo, bool enable)
{ {
...@@ -568,6 +582,7 @@ struct intel_dvo_dev_ops ns2501_ops = { ...@@ -568,6 +582,7 @@ struct intel_dvo_dev_ops ns2501_ops = {
.mode_valid = ns2501_mode_valid, .mode_valid = ns2501_mode_valid,
.mode_set = ns2501_mode_set, .mode_set = ns2501_mode_set,
.dpms = ns2501_dpms, .dpms = ns2501_dpms,
.get_hw_state = ns2501_get_hw_state,
.dump_regs = ns2501_dump_regs, .dump_regs = ns2501_dump_regs,
.destroy = ns2501_destroy, .destroy = ns2501_destroy,
}; };
...@@ -226,6 +226,21 @@ static void sil164_dpms(struct intel_dvo_device *dvo, bool enable) ...@@ -226,6 +226,21 @@ static void sil164_dpms(struct intel_dvo_device *dvo, bool enable)
return; return;
} }
static bool sil164_get_hw_state(struct intel_dvo_device *dvo)
{
int ret;
unsigned char ch;
ret = sil164_readb(dvo, SIL164_REG8, &ch);
if (ret == false)
return false;
if (ch & SIL164_8_PD)
return true;
else
return false;
}
static void sil164_dump_regs(struct intel_dvo_device *dvo) static void sil164_dump_regs(struct intel_dvo_device *dvo)
{ {
uint8_t val; uint8_t val;
...@@ -258,6 +273,7 @@ struct intel_dvo_dev_ops sil164_ops = { ...@@ -258,6 +273,7 @@ struct intel_dvo_dev_ops sil164_ops = {
.mode_valid = sil164_mode_valid, .mode_valid = sil164_mode_valid,
.mode_set = sil164_mode_set, .mode_set = sil164_mode_set,
.dpms = sil164_dpms, .dpms = sil164_dpms,
.get_hw_state = sil164_get_hw_state,
.dump_regs = sil164_dump_regs, .dump_regs = sil164_dump_regs,
.destroy = sil164_destroy, .destroy = sil164_destroy,
}; };
...@@ -249,6 +249,19 @@ static void tfp410_dpms(struct intel_dvo_device *dvo, bool enable) ...@@ -249,6 +249,19 @@ static void tfp410_dpms(struct intel_dvo_device *dvo, bool enable)
tfp410_writeb(dvo, TFP410_CTL_1, ctl1); tfp410_writeb(dvo, TFP410_CTL_1, ctl1);
} }
static bool tfp410_get_hw_state(struct intel_dvo_device *dvo)
{
uint8_t ctl1;
if (!tfp410_readb(dvo, TFP410_CTL_1, &ctl1))
return false;
if (ctl1 & TFP410_CTL_1_PD)
return true;
else
return false;
}
static void tfp410_dump_regs(struct intel_dvo_device *dvo) static void tfp410_dump_regs(struct intel_dvo_device *dvo)
{ {
uint8_t val, val2; uint8_t val, val2;
...@@ -299,6 +312,7 @@ struct intel_dvo_dev_ops tfp410_ops = { ...@@ -299,6 +312,7 @@ struct intel_dvo_dev_ops tfp410_ops = {
.mode_valid = tfp410_mode_valid, .mode_valid = tfp410_mode_valid,
.mode_set = tfp410_mode_set, .mode_set = tfp410_mode_set,
.dpms = tfp410_dpms, .dpms = tfp410_dpms,
.get_hw_state = tfp410_get_hw_state,
.dump_regs = tfp410_dump_regs, .dump_regs = tfp410_dump_regs,
.destroy = tfp410_destroy, .destroy = tfp410_destroy,
}; };
...@@ -470,6 +470,9 @@ static int i915_drm_freeze(struct drm_device *dev) ...@@ -470,6 +470,9 @@ static int i915_drm_freeze(struct drm_device *dev)
"GEM idle failed, resume might fail\n"); "GEM idle failed, resume might fail\n");
return error; return error;
} }
intel_modeset_disable(dev);
drm_irq_uninstall(dev); drm_irq_uninstall(dev);
} }
...@@ -543,13 +546,9 @@ static int i915_drm_thaw(struct drm_device *dev) ...@@ -543,13 +546,9 @@ static int i915_drm_thaw(struct drm_device *dev)
mutex_unlock(&dev->struct_mutex); mutex_unlock(&dev->struct_mutex);
intel_modeset_init_hw(dev); intel_modeset_init_hw(dev);
intel_modeset_setup_hw_state(dev);
drm_mode_config_reset(dev); drm_mode_config_reset(dev);
drm_irq_install(dev); drm_irq_install(dev);
/* Resume the modeset for every activated CRTC */
mutex_lock(&dev->mode_config.mutex);
drm_helper_resume_force_mode(dev);
mutex_unlock(&dev->mode_config.mutex);
} }
intel_opregion_init(dev); intel_opregion_init(dev);
......
...@@ -241,7 +241,6 @@ struct drm_i915_error_state { ...@@ -241,7 +241,6 @@ struct drm_i915_error_state {
}; };
struct drm_i915_display_funcs { struct drm_i915_display_funcs {
void (*dpms)(struct drm_crtc *crtc, int mode);
bool (*fbc_enabled)(struct drm_device *dev); bool (*fbc_enabled)(struct drm_device *dev);
void (*enable_fbc)(struct drm_crtc *crtc, unsigned long interval); void (*enable_fbc)(struct drm_crtc *crtc, unsigned long interval);
void (*disable_fbc)(struct drm_device *dev); void (*disable_fbc)(struct drm_device *dev);
...@@ -257,6 +256,8 @@ struct drm_i915_display_funcs { ...@@ -257,6 +256,8 @@ struct drm_i915_display_funcs {
struct drm_display_mode *adjusted_mode, struct drm_display_mode *adjusted_mode,
int x, int y, int x, int y,
struct drm_framebuffer *old_fb); struct drm_framebuffer *old_fb);
void (*crtc_enable)(struct drm_crtc *crtc);
void (*crtc_disable)(struct drm_crtc *crtc);
void (*off)(struct drm_crtc *crtc); void (*off)(struct drm_crtc *crtc);
void (*write_eld)(struct drm_connector *connector, void (*write_eld)(struct drm_connector *connector,
struct drm_crtc *crtc); struct drm_crtc *crtc);
...@@ -1550,6 +1551,7 @@ extern void intel_modeset_init(struct drm_device *dev); ...@@ -1550,6 +1551,7 @@ extern void intel_modeset_init(struct drm_device *dev);
extern void intel_modeset_gem_init(struct drm_device *dev); extern void intel_modeset_gem_init(struct drm_device *dev);
extern void intel_modeset_cleanup(struct drm_device *dev); extern void intel_modeset_cleanup(struct drm_device *dev);
extern int intel_modeset_vga_set_state(struct drm_device *dev, bool state); extern int intel_modeset_vga_set_state(struct drm_device *dev, bool state);
extern void intel_modeset_setup_hw_state(struct drm_device *dev);
extern bool intel_fbc_enabled(struct drm_device *dev); extern bool intel_fbc_enabled(struct drm_device *dev);
extern void intel_disable_fbc(struct drm_device *dev); extern void intel_disable_fbc(struct drm_device *dev);
extern bool ironlake_set_drps(struct drm_device *dev, u8 val); extern bool ironlake_set_drps(struct drm_device *dev, u8 val);
......
...@@ -4037,6 +4037,8 @@ ...@@ -4037,6 +4037,8 @@
#define PORT_TRANS_C_SEL_CPT (2<<29) #define PORT_TRANS_C_SEL_CPT (2<<29)
#define PORT_TRANS_SEL_MASK (3<<29) #define PORT_TRANS_SEL_MASK (3<<29)
#define PORT_TRANS_SEL_CPT(pipe) ((pipe) << 29) #define PORT_TRANS_SEL_CPT(pipe) ((pipe) << 29)
#define PORT_TO_PIPE(val) (((val) & (1<<30)) >> 30)
#define PORT_TO_PIPE_CPT(val) (((val) & PORT_TRANS_SEL_MASK) >> 29)
#define TRANS_DP_CTL_A 0xe0300 #define TRANS_DP_CTL_A 0xe0300
#define TRANS_DP_CTL_B 0xe1300 #define TRANS_DP_CTL_B 0xe1300
......
...@@ -61,42 +61,63 @@ static struct intel_crt *intel_encoder_to_crt(struct intel_encoder *encoder) ...@@ -61,42 +61,63 @@ static struct intel_crt *intel_encoder_to_crt(struct intel_encoder *encoder)
return container_of(encoder, struct intel_crt, base); return container_of(encoder, struct intel_crt, base);
} }
static void pch_crt_dpms(struct drm_encoder *encoder, int mode) static bool intel_crt_get_hw_state(struct intel_encoder *encoder,
enum pipe *pipe)
{ {
struct drm_device *dev = encoder->dev; struct drm_device *dev = encoder->base.dev;
struct drm_i915_private *dev_priv = dev->dev_private; struct drm_i915_private *dev_priv = dev->dev_private;
struct intel_crt *crt = intel_encoder_to_crt(encoder);
u32 tmp;
tmp = I915_READ(crt->adpa_reg);
if (!(tmp & ADPA_DAC_ENABLE))
return false;
if (HAS_PCH_CPT(dev))
*pipe = PORT_TO_PIPE_CPT(tmp);
else
*pipe = PORT_TO_PIPE(tmp);
return true;
}
static void intel_disable_crt(struct intel_encoder *encoder)
{
struct drm_i915_private *dev_priv = encoder->base.dev->dev_private;
struct intel_crt *crt = intel_encoder_to_crt(encoder);
u32 temp; u32 temp;
temp = I915_READ(PCH_ADPA); temp = I915_READ(crt->adpa_reg);
temp &= ~(ADPA_HSYNC_CNTL_DISABLE | ADPA_VSYNC_CNTL_DISABLE);
temp &= ~ADPA_DAC_ENABLE; temp &= ~ADPA_DAC_ENABLE;
I915_WRITE(crt->adpa_reg, temp);
}
switch (mode) { static void intel_enable_crt(struct intel_encoder *encoder)
case DRM_MODE_DPMS_ON: {
temp |= ADPA_DAC_ENABLE; struct drm_i915_private *dev_priv = encoder->base.dev->dev_private;
break; struct intel_crt *crt = intel_encoder_to_crt(encoder);
case DRM_MODE_DPMS_STANDBY: u32 temp;
case DRM_MODE_DPMS_SUSPEND:
case DRM_MODE_DPMS_OFF:
/* Just leave port enable cleared */
break;
}
I915_WRITE(PCH_ADPA, temp); temp = I915_READ(crt->adpa_reg);
temp |= ADPA_DAC_ENABLE;
I915_WRITE(crt->adpa_reg, temp);
} }
static void gmch_crt_dpms(struct drm_encoder *encoder, int mode) /* Note: The caller is required to filter out dpms modes not supported by the
* platform. */
static void intel_crt_set_dpms(struct intel_encoder *encoder, int mode)
{ {
struct drm_device *dev = encoder->dev; struct drm_device *dev = encoder->base.dev;
struct drm_i915_private *dev_priv = dev->dev_private; struct drm_i915_private *dev_priv = dev->dev_private;
struct intel_crt *crt = intel_encoder_to_crt(encoder);
u32 temp; u32 temp;
temp = I915_READ(ADPA); temp = I915_READ(crt->adpa_reg);
temp &= ~(ADPA_HSYNC_CNTL_DISABLE | ADPA_VSYNC_CNTL_DISABLE); temp &= ~(ADPA_HSYNC_CNTL_DISABLE | ADPA_VSYNC_CNTL_DISABLE);
temp &= ~ADPA_DAC_ENABLE; temp &= ~ADPA_DAC_ENABLE;
if (IS_VALLEYVIEW(dev) && mode != DRM_MODE_DPMS_ON)
mode = DRM_MODE_DPMS_OFF;
switch (mode) { switch (mode) {
case DRM_MODE_DPMS_ON: case DRM_MODE_DPMS_ON:
temp |= ADPA_DAC_ENABLE; temp |= ADPA_DAC_ENABLE;
...@@ -112,7 +133,51 @@ static void gmch_crt_dpms(struct drm_encoder *encoder, int mode) ...@@ -112,7 +133,51 @@ static void gmch_crt_dpms(struct drm_encoder *encoder, int mode)
break; break;
} }
I915_WRITE(ADPA, temp); I915_WRITE(crt->adpa_reg, temp);
}
static void intel_crt_dpms(struct drm_connector *connector, int mode)
{
struct drm_device *dev = connector->dev;
struct intel_encoder *encoder = intel_attached_encoder(connector);
struct drm_crtc *crtc;
int old_dpms;
/* PCH platforms and VLV only support on/off. */
if (INTEL_INFO(dev)->gen < 5 && mode != DRM_MODE_DPMS_ON)
mode = DRM_MODE_DPMS_OFF;
if (mode == connector->dpms)
return;
old_dpms = connector->dpms;
connector->dpms = mode;
/* Only need to change hw state when actually enabled */
crtc = encoder->base.crtc;
if (!crtc) {
encoder->connectors_active = false;
return;
}
/* We need the pipe to run for anything but OFF. */
if (mode == DRM_MODE_DPMS_OFF)
encoder->connectors_active = false;
else
encoder->connectors_active = true;
if (mode < old_dpms) {
/* From off to on, enable the pipe first. */
intel_crtc_update_dpms(crtc);
intel_crt_set_dpms(encoder, mode);
} else {
intel_crt_set_dpms(encoder, mode);
intel_crtc_update_dpms(crtc);
}
intel_modeset_check_state(connector->dev);
} }
static int intel_crt_mode_valid(struct drm_connector *connector, static int intel_crt_mode_valid(struct drm_connector *connector,
...@@ -603,25 +668,15 @@ static void intel_crt_reset(struct drm_connector *connector) ...@@ -603,25 +668,15 @@ static void intel_crt_reset(struct drm_connector *connector)
* Routines for controlling stuff on the analog port * Routines for controlling stuff on the analog port
*/ */
static const struct drm_encoder_helper_funcs pch_encoder_funcs = { static const struct drm_encoder_helper_funcs crt_encoder_funcs = {
.mode_fixup = intel_crt_mode_fixup,
.prepare = intel_encoder_prepare,
.commit = intel_encoder_commit,
.mode_set = intel_crt_mode_set,
.dpms = pch_crt_dpms,
};
static const struct drm_encoder_helper_funcs gmch_encoder_funcs = {
.mode_fixup = intel_crt_mode_fixup, .mode_fixup = intel_crt_mode_fixup,
.prepare = intel_encoder_prepare,
.commit = intel_encoder_commit,
.mode_set = intel_crt_mode_set, .mode_set = intel_crt_mode_set,
.dpms = gmch_crt_dpms, .disable = intel_encoder_noop,
}; };
static const struct drm_connector_funcs intel_crt_connector_funcs = { static const struct drm_connector_funcs intel_crt_connector_funcs = {
.reset = intel_crt_reset, .reset = intel_crt_reset,
.dpms = drm_helper_connector_dpms, .dpms = intel_crt_dpms,
.detect = intel_crt_detect, .detect = intel_crt_detect,
.fill_modes = drm_helper_probe_single_connector_modes, .fill_modes = drm_helper_probe_single_connector_modes,
.destroy = intel_crt_destroy, .destroy = intel_crt_destroy,
...@@ -662,7 +717,6 @@ void intel_crt_init(struct drm_device *dev) ...@@ -662,7 +717,6 @@ void intel_crt_init(struct drm_device *dev)
struct intel_crt *crt; struct intel_crt *crt;
struct intel_connector *intel_connector; struct intel_connector *intel_connector;
struct drm_i915_private *dev_priv = dev->dev_private; struct drm_i915_private *dev_priv = dev->dev_private;
const struct drm_encoder_helper_funcs *encoder_helper_funcs;
/* Skip machines without VGA that falsely report hotplug events */ /* Skip machines without VGA that falsely report hotplug events */
if (dmi_check_system(intel_no_crt)) if (dmi_check_system(intel_no_crt))
...@@ -700,11 +754,6 @@ void intel_crt_init(struct drm_device *dev) ...@@ -700,11 +754,6 @@ void intel_crt_init(struct drm_device *dev)
connector->interlace_allowed = 1; connector->interlace_allowed = 1;
connector->doublescan_allowed = 0; connector->doublescan_allowed = 0;
if (HAS_PCH_SPLIT(dev))
encoder_helper_funcs = &pch_encoder_funcs;
else
encoder_helper_funcs = &gmch_encoder_funcs;
if (HAS_PCH_SPLIT(dev)) if (HAS_PCH_SPLIT(dev))
crt->adpa_reg = PCH_ADPA; crt->adpa_reg = PCH_ADPA;
else if (IS_VALLEYVIEW(dev)) else if (IS_VALLEYVIEW(dev))
...@@ -712,7 +761,12 @@ void intel_crt_init(struct drm_device *dev) ...@@ -712,7 +761,12 @@ void intel_crt_init(struct drm_device *dev)
else else
crt->adpa_reg = ADPA; crt->adpa_reg = ADPA;
drm_encoder_helper_add(&crt->base.base, encoder_helper_funcs); crt->base.disable = intel_disable_crt;
crt->base.enable = intel_enable_crt;
crt->base.get_hw_state = intel_crt_get_hw_state;
intel_connector->get_hw_state = intel_connector_get_hw_state;
drm_encoder_helper_add(&crt->base.base, &crt_encoder_funcs);
drm_connector_helper_add(connector, &intel_crt_connector_helper_funcs); drm_connector_helper_add(connector, &intel_crt_connector_helper_funcs);
drm_sysfs_connector_add(connector); drm_sysfs_connector_add(connector);
......
...@@ -757,26 +757,63 @@ void intel_ddi_mode_set(struct drm_encoder *encoder, ...@@ -757,26 +757,63 @@ void intel_ddi_mode_set(struct drm_encoder *encoder,
intel_hdmi->set_infoframes(encoder, adjusted_mode); intel_hdmi->set_infoframes(encoder, adjusted_mode);
} }
void intel_ddi_dpms(struct drm_encoder *encoder, int mode) bool intel_ddi_get_hw_state(struct intel_encoder *encoder,
enum pipe *pipe)
{ {
struct drm_device *dev = encoder->dev; struct drm_device *dev = encoder->base.dev;
struct drm_i915_private *dev_priv = dev->dev_private; struct drm_i915_private *dev_priv = dev->dev_private;
struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(encoder); struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(&encoder->base);
u32 tmp;
int i;
tmp = I915_READ(DDI_BUF_CTL(intel_hdmi->ddi_port));
if (!(tmp & DDI_BUF_CTL_ENABLE))
return false;
for_each_pipe(i) {
tmp = I915_READ(DDI_FUNC_CTL(i));
if ((tmp & PIPE_DDI_PORT_MASK)
== PIPE_DDI_SELECT_PORT(intel_hdmi->ddi_port)) {
*pipe = i;
return true;
}
}
DRM_DEBUG_KMS("No pipe for ddi port %i found\n", intel_hdmi->ddi_port);
return true;
}
void intel_enable_ddi(struct intel_encoder *encoder)
{
struct drm_device *dev = encoder->base.dev;
struct drm_i915_private *dev_priv = dev->dev_private;
struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(&encoder->base);
int port = intel_hdmi->ddi_port; int port = intel_hdmi->ddi_port;
u32 temp; u32 temp;
temp = I915_READ(DDI_BUF_CTL(port)); temp = I915_READ(DDI_BUF_CTL(port));
if (mode != DRM_MODE_DPMS_ON) {
temp &= ~DDI_BUF_CTL_ENABLE;
} else {
temp |= DDI_BUF_CTL_ENABLE; temp |= DDI_BUF_CTL_ENABLE;
}
/* Enable DDI_BUF_CTL. In HDMI/DVI mode, the port width, /* Enable DDI_BUF_CTL. In HDMI/DVI mode, the port width,
* and swing/emphasis values are ignored so nothing special needs * and swing/emphasis values are ignored so nothing special needs
* to be done besides enabling the port. * to be done besides enabling the port.
*/ */
I915_WRITE(DDI_BUF_CTL(port), I915_WRITE(DDI_BUF_CTL(port), temp);
temp); }
void intel_disable_ddi(struct intel_encoder *encoder)
{
struct drm_device *dev = encoder->base.dev;
struct drm_i915_private *dev_priv = dev->dev_private;
struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(&encoder->base);
int port = intel_hdmi->ddi_port;
u32 temp;
temp = I915_READ(DDI_BUF_CTL(port));
temp &= ~DDI_BUF_CTL_ENABLE;
I915_WRITE(DDI_BUF_CTL(port), temp);
} }
...@@ -1006,7 +1006,7 @@ void intel_wait_for_pipe_off(struct drm_device *dev, int pipe) ...@@ -1006,7 +1006,7 @@ void intel_wait_for_pipe_off(struct drm_device *dev, int pipe)
/* Wait for the Pipe State to go off */ /* Wait for the Pipe State to go off */
if (wait_for((I915_READ(reg) & I965_PIPECONF_ACTIVE) == 0, if (wait_for((I915_READ(reg) & I965_PIPECONF_ACTIVE) == 0,
100)) 100))
DRM_DEBUG_KMS("pipe_off wait timed out\n"); WARN(1, "pipe_off wait timed out\n");
} else { } else {
u32 last_line, line_mask; u32 last_line, line_mask;
int reg = PIPEDSL(pipe); int reg = PIPEDSL(pipe);
...@@ -1024,7 +1024,7 @@ void intel_wait_for_pipe_off(struct drm_device *dev, int pipe) ...@@ -1024,7 +1024,7 @@ void intel_wait_for_pipe_off(struct drm_device *dev, int pipe)
} while (((I915_READ(reg) & line_mask) != last_line) && } while (((I915_READ(reg) & line_mask) != last_line) &&
time_after(timeout, jiffies)); time_after(timeout, jiffies));
if (time_after(jiffies, timeout)) if (time_after(jiffies, timeout))
DRM_DEBUG_KMS("pipe_off wait timed out\n"); WARN(1, "pipe_off wait timed out\n");
} }
} }
...@@ -2201,16 +2201,17 @@ intel_finish_fb(struct drm_framebuffer *old_fb) ...@@ -2201,16 +2201,17 @@ intel_finish_fb(struct drm_framebuffer *old_fb)
static int static int
intel_pipe_set_base(struct drm_crtc *crtc, int x, int y, intel_pipe_set_base(struct drm_crtc *crtc, int x, int y,
struct drm_framebuffer *old_fb) struct drm_framebuffer *fb)
{ {
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;
struct drm_i915_master_private *master_priv; struct drm_i915_master_private *master_priv;
struct intel_crtc *intel_crtc = to_intel_crtc(crtc); struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
struct drm_framebuffer *old_fb;
int ret; int ret;
/* no fb bound */ /* no fb bound */
if (!crtc->fb) { if (!fb) {
DRM_ERROR("No FB bound\n"); DRM_ERROR("No FB bound\n");
return 0; return 0;
} }
...@@ -2224,7 +2225,7 @@ intel_pipe_set_base(struct drm_crtc *crtc, int x, int y, ...@@ -2224,7 +2225,7 @@ intel_pipe_set_base(struct drm_crtc *crtc, int x, int y,
mutex_lock(&dev->struct_mutex); mutex_lock(&dev->struct_mutex);
ret = intel_pin_and_fence_fb_obj(dev, ret = intel_pin_and_fence_fb_obj(dev,
to_intel_framebuffer(crtc->fb)->obj, to_intel_framebuffer(fb)->obj,
NULL); NULL);
if (ret != 0) { if (ret != 0) {
mutex_unlock(&dev->struct_mutex); mutex_unlock(&dev->struct_mutex);
...@@ -2232,17 +2233,20 @@ intel_pipe_set_base(struct drm_crtc *crtc, int x, int y, ...@@ -2232,17 +2233,20 @@ intel_pipe_set_base(struct drm_crtc *crtc, int x, int y,
return ret; return ret;
} }
if (old_fb) if (crtc->fb)
intel_finish_fb(old_fb); intel_finish_fb(crtc->fb);
ret = dev_priv->display.update_plane(crtc, crtc->fb, x, y); ret = dev_priv->display.update_plane(crtc, fb, x, y);
if (ret) { if (ret) {
intel_unpin_fb_obj(to_intel_framebuffer(crtc->fb)->obj); intel_unpin_fb_obj(to_intel_framebuffer(fb)->obj);
mutex_unlock(&dev->struct_mutex); mutex_unlock(&dev->struct_mutex);
DRM_ERROR("failed to update base address\n"); DRM_ERROR("failed to update base address\n");
return ret; return ret;
} }
old_fb = crtc->fb;
crtc->fb = fb;
if (old_fb) { if (old_fb) {
intel_wait_for_vblank(dev, intel_crtc->pipe); intel_wait_for_vblank(dev, intel_crtc->pipe);
intel_unpin_fb_obj(to_intel_framebuffer(old_fb)->obj); intel_unpin_fb_obj(to_intel_framebuffer(old_fb)->obj);
...@@ -3209,11 +3213,14 @@ static void ironlake_crtc_enable(struct drm_crtc *crtc) ...@@ -3209,11 +3213,14 @@ static void ironlake_crtc_enable(struct drm_crtc *crtc)
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;
struct intel_crtc *intel_crtc = to_intel_crtc(crtc); struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
struct intel_encoder *encoder;
int pipe = intel_crtc->pipe; int pipe = intel_crtc->pipe;
int plane = intel_crtc->plane; int plane = intel_crtc->plane;
u32 temp; u32 temp;
bool is_pch_port; bool is_pch_port;
WARN_ON(!crtc->enabled);
if (intel_crtc->active) if (intel_crtc->active)
return; return;
...@@ -3262,6 +3269,12 @@ static void ironlake_crtc_enable(struct drm_crtc *crtc) ...@@ -3262,6 +3269,12 @@ static void ironlake_crtc_enable(struct drm_crtc *crtc)
mutex_unlock(&dev->struct_mutex); mutex_unlock(&dev->struct_mutex);
intel_crtc_update_cursor(crtc, true); intel_crtc_update_cursor(crtc, true);
for_each_encoder_on_crtc(dev, crtc, encoder)
encoder->enable(encoder);
if (HAS_PCH_CPT(dev))
intel_cpt_verify_modeset(dev, intel_crtc->pipe);
} }
static void ironlake_crtc_disable(struct drm_crtc *crtc) static void ironlake_crtc_disable(struct drm_crtc *crtc)
...@@ -3269,13 +3282,18 @@ static void ironlake_crtc_disable(struct drm_crtc *crtc) ...@@ -3269,13 +3282,18 @@ static void ironlake_crtc_disable(struct drm_crtc *crtc)
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;
struct intel_crtc *intel_crtc = to_intel_crtc(crtc); struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
struct intel_encoder *encoder;
int pipe = intel_crtc->pipe; int pipe = intel_crtc->pipe;
int plane = intel_crtc->plane; int plane = intel_crtc->plane;
u32 reg, temp; u32 reg, temp;
if (!intel_crtc->active) if (!intel_crtc->active)
return; return;
for_each_encoder_on_crtc(dev, crtc, encoder)
encoder->disable(encoder);
intel_crtc_wait_for_pending_flips(crtc); intel_crtc_wait_for_pending_flips(crtc);
drm_vblank_off(dev, pipe); drm_vblank_off(dev, pipe);
intel_crtc_update_cursor(crtc, false); intel_crtc_update_cursor(crtc, false);
...@@ -3342,30 +3360,6 @@ static void ironlake_crtc_disable(struct drm_crtc *crtc) ...@@ -3342,30 +3360,6 @@ static void ironlake_crtc_disable(struct drm_crtc *crtc)
mutex_unlock(&dev->struct_mutex); mutex_unlock(&dev->struct_mutex);
} }
static void ironlake_crtc_dpms(struct drm_crtc *crtc, int mode)
{
struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
int pipe = intel_crtc->pipe;
int plane = intel_crtc->plane;
/* 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_KMS("crtc %d/%d dpms on\n", pipe, plane);
ironlake_crtc_enable(crtc);
break;
case DRM_MODE_DPMS_OFF:
DRM_DEBUG_KMS("crtc %d/%d dpms off\n", pipe, plane);
ironlake_crtc_disable(crtc);
break;
}
}
static void ironlake_crtc_off(struct drm_crtc *crtc) static void ironlake_crtc_off(struct drm_crtc *crtc)
{ {
struct intel_crtc *intel_crtc = to_intel_crtc(crtc); struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
...@@ -3395,9 +3389,12 @@ static void i9xx_crtc_enable(struct drm_crtc *crtc) ...@@ -3395,9 +3389,12 @@ static void i9xx_crtc_enable(struct drm_crtc *crtc)
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;
struct intel_crtc *intel_crtc = to_intel_crtc(crtc); struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
struct intel_encoder *encoder;
int pipe = intel_crtc->pipe; int pipe = intel_crtc->pipe;
int plane = intel_crtc->plane; int plane = intel_crtc->plane;
WARN_ON(!crtc->enabled);
if (intel_crtc->active) if (intel_crtc->active)
return; return;
...@@ -3414,6 +3411,9 @@ static void i9xx_crtc_enable(struct drm_crtc *crtc) ...@@ -3414,6 +3411,9 @@ 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); intel_crtc_update_cursor(crtc, true);
for_each_encoder_on_crtc(dev, crtc, encoder)
encoder->enable(encoder);
} }
static void i9xx_crtc_disable(struct drm_crtc *crtc) static void i9xx_crtc_disable(struct drm_crtc *crtc)
...@@ -3421,12 +3421,17 @@ static void i9xx_crtc_disable(struct drm_crtc *crtc) ...@@ -3421,12 +3421,17 @@ static void i9xx_crtc_disable(struct drm_crtc *crtc)
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;
struct intel_crtc *intel_crtc = to_intel_crtc(crtc); struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
struct intel_encoder *encoder;
int pipe = intel_crtc->pipe; int pipe = intel_crtc->pipe;
int plane = intel_crtc->plane; int plane = intel_crtc->plane;
if (!intel_crtc->active) if (!intel_crtc->active)
return; return;
for_each_encoder_on_crtc(dev, crtc, encoder)
encoder->disable(encoder);
/* 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_wait_for_pending_flips(crtc); intel_crtc_wait_for_pending_flips(crtc);
drm_vblank_off(dev, pipe); drm_vblank_off(dev, pipe);
...@@ -3445,45 +3450,17 @@ static void i9xx_crtc_disable(struct drm_crtc *crtc) ...@@ -3445,45 +3450,17 @@ static void i9xx_crtc_disable(struct drm_crtc *crtc)
intel_update_watermarks(dev); intel_update_watermarks(dev);
} }
static void i9xx_crtc_dpms(struct drm_crtc *crtc, int mode)
{
/* 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:
i9xx_crtc_enable(crtc);
break;
case DRM_MODE_DPMS_OFF:
i9xx_crtc_disable(crtc);
break;
}
}
static void i9xx_crtc_off(struct drm_crtc *crtc) static void i9xx_crtc_off(struct drm_crtc *crtc)
{ {
} }
/** static void intel_crtc_update_sarea(struct drm_crtc *crtc,
* Sets the power management mode of the pipe and plane. bool enabled)
*/
static void intel_crtc_dpms(struct drm_crtc *crtc, int mode)
{ {
struct drm_device *dev = crtc->dev; struct drm_device *dev = crtc->dev;
struct drm_i915_private *dev_priv = dev->dev_private;
struct drm_i915_master_private *master_priv; struct drm_i915_master_private *master_priv;
struct intel_crtc *intel_crtc = to_intel_crtc(crtc); struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
int pipe = intel_crtc->pipe; int pipe = intel_crtc->pipe;
bool enabled;
if (intel_crtc->dpms_mode == mode)
return;
intel_crtc->dpms_mode = mode;
dev_priv->display.dpms(crtc, mode);
if (!dev->primary->master) if (!dev->primary->master)
return; return;
...@@ -3492,8 +3469,6 @@ static void intel_crtc_dpms(struct drm_crtc *crtc, int mode) ...@@ -3492,8 +3469,6 @@ static void intel_crtc_dpms(struct drm_crtc *crtc, int mode)
if (!master_priv->sarea_priv) if (!master_priv->sarea_priv)
return; return;
enabled = crtc->enabled && mode != DRM_MODE_DPMS_OFF;
switch (pipe) { switch (pipe) {
case 0: case 0:
master_priv->sarea_priv->pipeA_w = enabled ? crtc->mode.hdisplay : 0; master_priv->sarea_priv->pipeA_w = enabled ? crtc->mode.hdisplay : 0;
...@@ -3509,13 +3484,42 @@ static void intel_crtc_dpms(struct drm_crtc *crtc, int mode) ...@@ -3509,13 +3484,42 @@ static void intel_crtc_dpms(struct drm_crtc *crtc, int mode)
} }
} }
/**
* Sets the power management mode of the pipe and plane.
*/
void intel_crtc_update_dpms(struct drm_crtc *crtc)
{
struct drm_device *dev = crtc->dev;
struct drm_i915_private *dev_priv = dev->dev_private;
struct intel_encoder *intel_encoder;
bool enable = false;
for_each_encoder_on_crtc(dev, crtc, intel_encoder)
enable |= intel_encoder->connectors_active;
if (enable)
dev_priv->display.crtc_enable(crtc);
else
dev_priv->display.crtc_disable(crtc);
intel_crtc_update_sarea(crtc, enable);
}
static void intel_crtc_noop(struct drm_crtc *crtc)
{
}
static void intel_crtc_disable(struct drm_crtc *crtc) static void intel_crtc_disable(struct drm_crtc *crtc)
{ {
struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private;
struct drm_device *dev = crtc->dev; struct drm_device *dev = crtc->dev;
struct drm_connector *connector;
struct drm_i915_private *dev_priv = dev->dev_private; struct drm_i915_private *dev_priv = dev->dev_private;
crtc_funcs->dpms(crtc, DRM_MODE_DPMS_OFF); /* crtc should still be enabled when we disable it. */
WARN_ON(!crtc->enabled);
dev_priv->display.crtc_disable(crtc);
intel_crtc_update_sarea(crtc, false);
dev_priv->display.off(crtc); dev_priv->display.off(crtc);
assert_plane_disabled(dev->dev_private, to_intel_crtc(crtc)->plane); assert_plane_disabled(dev->dev_private, to_intel_crtc(crtc)->plane);
...@@ -3525,63 +3529,128 @@ static void intel_crtc_disable(struct drm_crtc *crtc) ...@@ -3525,63 +3529,128 @@ static void intel_crtc_disable(struct drm_crtc *crtc)
mutex_lock(&dev->struct_mutex); mutex_lock(&dev->struct_mutex);
intel_unpin_fb_obj(to_intel_framebuffer(crtc->fb)->obj); intel_unpin_fb_obj(to_intel_framebuffer(crtc->fb)->obj);
mutex_unlock(&dev->struct_mutex); mutex_unlock(&dev->struct_mutex);
crtc->fb = NULL;
}
/* Update computed state. */
list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
if (!connector->encoder || !connector->encoder->crtc)
continue;
if (connector->encoder->crtc != crtc)
continue;
connector->dpms = DRM_MODE_DPMS_OFF;
to_intel_encoder(connector->encoder)->connectors_active = false;
} }
} }
/* Prepare for a mode set. void intel_modeset_disable(struct drm_device *dev)
*
* Note we could be a lot smarter here. We need to figure out which outputs
* will be enabled, which disabled (in short, how the config will changes)
* and perform the minimum necessary steps to accomplish that, e.g. updating
* watermarks, FBC configuration, making sure PLLs are programmed correctly,
* panel fitting is in the proper state, etc.
*/
static void i9xx_crtc_prepare(struct drm_crtc *crtc)
{ {
i9xx_crtc_disable(crtc); struct drm_crtc *crtc;
list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
if (crtc->enabled)
intel_crtc_disable(crtc);
}
} }
static void i9xx_crtc_commit(struct drm_crtc *crtc) void intel_encoder_noop(struct drm_encoder *encoder)
{ {
i9xx_crtc_enable(crtc);
} }
static void ironlake_crtc_prepare(struct drm_crtc *crtc) void intel_encoder_destroy(struct drm_encoder *encoder)
{ {
ironlake_crtc_disable(crtc); struct intel_encoder *intel_encoder = to_intel_encoder(encoder);
drm_encoder_cleanup(encoder);
kfree(intel_encoder);
} }
static void ironlake_crtc_commit(struct drm_crtc *crtc) /* Simple dpms helper for encodres with just one connector, no cloning and only
* one kind of off state. It clamps all !ON modes to fully OFF and changes the
* state of the entire output pipe. */
void intel_encoder_dpms(struct intel_encoder *encoder, int mode)
{ {
ironlake_crtc_enable(crtc); if (mode == DRM_MODE_DPMS_ON) {
encoder->connectors_active = true;
intel_crtc_update_dpms(encoder->base.crtc);
} else {
encoder->connectors_active = false;
intel_crtc_update_dpms(encoder->base.crtc);
}
} }
void intel_encoder_prepare(struct drm_encoder *encoder) /* Cross check the actual hw state with our own modeset state tracking (and it's
* internal consistency). */
static void intel_connector_check_state(struct intel_connector *connector)
{ {
struct drm_encoder_helper_funcs *encoder_funcs = encoder->helper_private; if (connector->get_hw_state(connector)) {
/* lvds has its own version of prepare see intel_lvds_prepare */ struct intel_encoder *encoder = connector->encoder;
encoder_funcs->dpms(encoder, DRM_MODE_DPMS_OFF); struct drm_crtc *crtc;
bool encoder_enabled;
enum pipe pipe;
DRM_DEBUG_KMS("[CONNECTOR:%d:%s]\n",
connector->base.base.id,
drm_get_connector_name(&connector->base));
WARN(connector->base.dpms == DRM_MODE_DPMS_OFF,
"wrong connector dpms state\n");
WARN(connector->base.encoder != &encoder->base,
"active connector not linked to encoder\n");
WARN(!encoder->connectors_active,
"encoder->connectors_active not set\n");
encoder_enabled = encoder->get_hw_state(encoder, &pipe);
WARN(!encoder_enabled, "encoder not enabled\n");
if (WARN_ON(!encoder->base.crtc))
return;
crtc = encoder->base.crtc;
WARN(!crtc->enabled, "crtc not enabled\n");
WARN(!to_intel_crtc(crtc)->active, "crtc not active\n");
WARN(pipe != to_intel_crtc(crtc)->pipe,
"encoder active on the wrong pipe\n");
}
} }
void intel_encoder_commit(struct drm_encoder *encoder) /* Even simpler default implementation, if there's really no special case to
* consider. */
void intel_connector_dpms(struct drm_connector *connector, int mode)
{ {
struct drm_encoder_helper_funcs *encoder_funcs = encoder->helper_private; struct intel_encoder *encoder = intel_attached_encoder(connector);
struct drm_device *dev = encoder->dev;
struct intel_crtc *intel_crtc = to_intel_crtc(encoder->crtc);
/* lvds has its own version of commit see intel_lvds_commit */ /* All the simple cases only support two dpms states. */
encoder_funcs->dpms(encoder, DRM_MODE_DPMS_ON); if (mode != DRM_MODE_DPMS_ON)
mode = DRM_MODE_DPMS_OFF;
if (HAS_PCH_CPT(dev)) if (mode == connector->dpms)
intel_cpt_verify_modeset(dev, intel_crtc->pipe); return;
connector->dpms = mode;
/* Only need to change hw state when actually enabled */
if (encoder->base.crtc)
intel_encoder_dpms(encoder, mode);
else
WARN_ON(encoder->connectors_active != false);
intel_modeset_check_state(connector->dev);
} }
void intel_encoder_destroy(struct drm_encoder *encoder) /* Simple connector->get_hw_state implementation for encoders that support only
* one connector and no cloning and hence the encoder state determines the state
* of the connector. */
bool intel_connector_get_hw_state(struct intel_connector *connector)
{ {
struct intel_encoder *intel_encoder = to_intel_encoder(encoder); enum pipe pipe = 0;
struct intel_encoder *encoder = connector->encoder;
drm_encoder_cleanup(encoder); return encoder->get_hw_state(encoder, &pipe);
kfree(intel_encoder);
} }
static bool intel_crtc_mode_fixup(struct drm_crtc *crtc, static bool intel_crtc_mode_fixup(struct drm_crtc *crtc,
...@@ -3744,6 +3813,7 @@ static inline bool intel_panel_use_ssc(struct drm_i915_private *dev_priv) ...@@ -3744,6 +3813,7 @@ static inline bool intel_panel_use_ssc(struct drm_i915_private *dev_priv)
* true if they don't match). * true if they don't match).
*/ */
static bool intel_choose_pipe_bpp_dither(struct drm_crtc *crtc, static bool intel_choose_pipe_bpp_dither(struct drm_crtc *crtc,
struct drm_framebuffer *fb,
unsigned int *pipe_bpp, unsigned int *pipe_bpp,
struct drm_display_mode *mode) struct drm_display_mode *mode)
{ {
...@@ -3813,7 +3883,7 @@ static bool intel_choose_pipe_bpp_dither(struct drm_crtc *crtc, ...@@ -3813,7 +3883,7 @@ static bool intel_choose_pipe_bpp_dither(struct drm_crtc *crtc,
* also stays within the max display bpc discovered above. * also stays within the max display bpc discovered above.
*/ */
switch (crtc->fb->depth) { switch (fb->depth) {
case 8: case 8:
bpc = 8; /* since we go through a colormap */ bpc = 8; /* since we go through a colormap */
break; break;
...@@ -4232,7 +4302,7 @@ static int i9xx_crtc_mode_set(struct drm_crtc *crtc, ...@@ -4232,7 +4302,7 @@ static int i9xx_crtc_mode_set(struct drm_crtc *crtc,
struct drm_display_mode *mode, struct drm_display_mode *mode,
struct drm_display_mode *adjusted_mode, struct drm_display_mode *adjusted_mode,
int x, int y, int x, int y,
struct drm_framebuffer *old_fb) struct drm_framebuffer *fb)
{ {
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;
...@@ -4422,7 +4492,7 @@ static int i9xx_crtc_mode_set(struct drm_crtc *crtc, ...@@ -4422,7 +4492,7 @@ static int i9xx_crtc_mode_set(struct drm_crtc *crtc,
I915_WRITE(DSPCNTR(plane), dspcntr); I915_WRITE(DSPCNTR(plane), dspcntr);
POSTING_READ(DSPCNTR(plane)); POSTING_READ(DSPCNTR(plane));
ret = intel_pipe_set_base(crtc, x, y, old_fb); ret = intel_pipe_set_base(crtc, x, y, fb);
intel_update_watermarks(dev); intel_update_watermarks(dev);
...@@ -4580,7 +4650,7 @@ static int ironlake_crtc_mode_set(struct drm_crtc *crtc, ...@@ -4580,7 +4650,7 @@ static int ironlake_crtc_mode_set(struct drm_crtc *crtc,
struct drm_display_mode *mode, struct drm_display_mode *mode,
struct drm_display_mode *adjusted_mode, struct drm_display_mode *adjusted_mode,
int x, int y, int x, int y,
struct drm_framebuffer *old_fb) struct drm_framebuffer *fb)
{ {
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;
...@@ -4700,7 +4770,7 @@ static int ironlake_crtc_mode_set(struct drm_crtc *crtc, ...@@ -4700,7 +4770,7 @@ static int ironlake_crtc_mode_set(struct drm_crtc *crtc,
/* determine panel color depth */ /* determine panel color depth */
temp = I915_READ(PIPECONF(pipe)); temp = I915_READ(PIPECONF(pipe));
temp &= ~PIPE_BPC_MASK; temp &= ~PIPE_BPC_MASK;
dither = intel_choose_pipe_bpp_dither(crtc, &pipe_bpp, mode); dither = intel_choose_pipe_bpp_dither(crtc, fb, &pipe_bpp, mode);
switch (pipe_bpp) { switch (pipe_bpp) {
case 18: case 18:
temp |= PIPE_6BPC; temp |= PIPE_6BPC;
...@@ -4969,7 +5039,7 @@ static int ironlake_crtc_mode_set(struct drm_crtc *crtc, ...@@ -4969,7 +5039,7 @@ static int ironlake_crtc_mode_set(struct drm_crtc *crtc,
I915_WRITE(DSPCNTR(plane), dspcntr); I915_WRITE(DSPCNTR(plane), dspcntr);
POSTING_READ(DSPCNTR(plane)); POSTING_READ(DSPCNTR(plane));
ret = intel_pipe_set_base(crtc, x, y, old_fb); ret = intel_pipe_set_base(crtc, x, y, fb);
intel_update_watermarks(dev); intel_update_watermarks(dev);
...@@ -4982,7 +5052,7 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc, ...@@ -4982,7 +5052,7 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc,
struct drm_display_mode *mode, struct drm_display_mode *mode,
struct drm_display_mode *adjusted_mode, struct drm_display_mode *adjusted_mode,
int x, int y, int x, int y,
struct drm_framebuffer *old_fb) struct drm_framebuffer *fb)
{ {
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;
...@@ -4993,14 +5063,9 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc, ...@@ -4993,14 +5063,9 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc,
drm_vblank_pre_modeset(dev, pipe); drm_vblank_pre_modeset(dev, pipe);
ret = dev_priv->display.crtc_mode_set(crtc, mode, adjusted_mode, ret = dev_priv->display.crtc_mode_set(crtc, mode, adjusted_mode,
x, y, old_fb); x, y, fb);
drm_vblank_post_modeset(dev, pipe); drm_vblank_post_modeset(dev, pipe);
if (ret)
intel_crtc->dpms_mode = DRM_MODE_DPMS_OFF;
else
intel_crtc->dpms_mode = DRM_MODE_DPMS_ON;
return ret; return ret;
} }
...@@ -5434,8 +5499,6 @@ static int intel_crtc_cursor_set(struct drm_crtc *crtc, ...@@ -5434,8 +5499,6 @@ static int intel_crtc_cursor_set(struct drm_crtc *crtc,
uint32_t addr; uint32_t addr;
int ret; int ret;
DRM_DEBUG_KMS("\n");
/* if we want to turn off the cursor ignore width and height */ /* if we want to turn off the cursor ignore width and height */
if (!handle) { if (!handle) {
DRM_DEBUG_KMS("cursor off\n"); DRM_DEBUG_KMS("cursor off\n");
...@@ -5692,7 +5755,7 @@ bool intel_get_load_detect_pipe(struct drm_connector *connector, ...@@ -5692,7 +5755,7 @@ bool intel_get_load_detect_pipe(struct drm_connector *connector,
struct drm_encoder *encoder = &intel_encoder->base; struct drm_encoder *encoder = &intel_encoder->base;
struct drm_crtc *crtc = NULL; struct drm_crtc *crtc = NULL;
struct drm_device *dev = encoder->dev; struct drm_device *dev = encoder->dev;
struct drm_framebuffer *old_fb; struct drm_framebuffer *fb;
int i = -1; int i = -1;
DRM_DEBUG_KMS("[CONNECTOR:%d:%s], [ENCODER:%d:%s]\n", DRM_DEBUG_KMS("[CONNECTOR:%d:%s], [ENCODER:%d:%s]\n",
...@@ -5742,8 +5805,8 @@ bool intel_get_load_detect_pipe(struct drm_connector *connector, ...@@ -5742,8 +5805,8 @@ bool intel_get_load_detect_pipe(struct drm_connector *connector,
return false; return false;
} }
encoder->crtc = crtc; intel_encoder->new_crtc = to_intel_crtc(crtc);
connector->encoder = encoder; to_intel_connector(connector)->new_encoder = intel_encoder;
intel_crtc = to_intel_crtc(crtc); intel_crtc = to_intel_crtc(crtc);
old->dpms_mode = connector->dpms; old->dpms_mode = connector->dpms;
...@@ -5753,8 +5816,6 @@ bool intel_get_load_detect_pipe(struct drm_connector *connector, ...@@ -5753,8 +5816,6 @@ bool intel_get_load_detect_pipe(struct drm_connector *connector,
if (!mode) if (!mode)
mode = &load_detect_mode; mode = &load_detect_mode;
old_fb = crtc->fb;
/* We need a framebuffer large enough to accommodate all accesses /* We need a framebuffer large enough to accommodate all accesses
* that the plane may generate whilst we perform load detection. * that the plane may generate whilst we perform load detection.
* We can not rely on the fbcon either being present (we get called * We can not rely on the fbcon either being present (we get called
...@@ -5762,19 +5823,19 @@ bool intel_get_load_detect_pipe(struct drm_connector *connector, ...@@ -5762,19 +5823,19 @@ bool intel_get_load_detect_pipe(struct drm_connector *connector,
* not even exist) or that it is large enough to satisfy the * not even exist) or that it is large enough to satisfy the
* requested mode. * requested mode.
*/ */
crtc->fb = mode_fits_in_fbdev(dev, mode); fb = mode_fits_in_fbdev(dev, mode);
if (crtc->fb == NULL) { if (fb == NULL) {
DRM_DEBUG_KMS("creating tmp fb for load-detection\n"); DRM_DEBUG_KMS("creating tmp fb for load-detection\n");
crtc->fb = intel_framebuffer_create_for_mode(dev, mode, 24, 32); fb = intel_framebuffer_create_for_mode(dev, mode, 24, 32);
old->release_fb = crtc->fb; old->release_fb = fb;
} else } else
DRM_DEBUG_KMS("reusing fbdev for load-detection framebuffer\n"); DRM_DEBUG_KMS("reusing fbdev for load-detection framebuffer\n");
if (IS_ERR(crtc->fb)) { if (IS_ERR(fb)) {
DRM_DEBUG_KMS("failed to allocate framebuffer for load-detection\n"); DRM_DEBUG_KMS("failed to allocate framebuffer for load-detection\n");
goto fail; goto fail;
} }
if (!drm_crtc_helper_set_mode(crtc, mode, 0, 0, old_fb)) { if (!intel_set_mode(crtc, mode, 0, 0, fb)) {
DRM_DEBUG_KMS("failed to set mode on load-detect pipe\n"); DRM_DEBUG_KMS("failed to set mode on load-detect pipe\n");
if (old->release_fb) if (old->release_fb)
old->release_fb->funcs->destroy(old->release_fb); old->release_fb->funcs->destroy(old->release_fb);
...@@ -5788,7 +5849,6 @@ bool intel_get_load_detect_pipe(struct drm_connector *connector, ...@@ -5788,7 +5849,6 @@ bool intel_get_load_detect_pipe(struct drm_connector *connector,
fail: fail:
connector->encoder = NULL; connector->encoder = NULL;
encoder->crtc = NULL; encoder->crtc = NULL;
crtc->fb = old_fb;
return false; return false;
} }
...@@ -5798,16 +5858,17 @@ void intel_release_load_detect_pipe(struct drm_connector *connector, ...@@ -5798,16 +5858,17 @@ void intel_release_load_detect_pipe(struct drm_connector *connector,
struct intel_encoder *intel_encoder = struct intel_encoder *intel_encoder =
intel_attached_encoder(connector); intel_attached_encoder(connector);
struct drm_encoder *encoder = &intel_encoder->base; struct drm_encoder *encoder = &intel_encoder->base;
struct drm_device *dev = encoder->dev;
DRM_DEBUG_KMS("[CONNECTOR:%d:%s], [ENCODER:%d:%s]\n", DRM_DEBUG_KMS("[CONNECTOR:%d:%s], [ENCODER:%d:%s]\n",
connector->base.id, drm_get_connector_name(connector), connector->base.id, drm_get_connector_name(connector),
encoder->base.id, drm_get_encoder_name(encoder)); encoder->base.id, drm_get_encoder_name(encoder));
if (old->load_detect_temp) { if (old->load_detect_temp) {
connector->encoder = NULL; struct drm_crtc *crtc = encoder->crtc;
encoder->crtc = NULL;
drm_helper_disable_unused_functions(dev); to_intel_connector(connector)->new_encoder = NULL;
intel_encoder->new_crtc = NULL;
intel_set_mode(crtc, NULL, 0, 0, NULL);
if (old->release_fb) if (old->release_fb)
old->release_fb->funcs->destroy(old->release_fb); old->release_fb->funcs->destroy(old->release_fb);
...@@ -6529,146 +6590,866 @@ static int intel_crtc_page_flip(struct drm_crtc *crtc, ...@@ -6529,146 +6590,866 @@ static int intel_crtc_page_flip(struct drm_crtc *crtc,
return ret; return ret;
} }
static void intel_sanitize_modesetting(struct drm_device *dev, static struct drm_crtc_helper_funcs intel_helper_funcs = {
int pipe, int plane) .mode_set_base_atomic = intel_pipe_set_base_atomic,
.load_lut = intel_crtc_load_lut,
.disable = intel_crtc_noop,
};
bool intel_encoder_check_is_cloned(struct intel_encoder *encoder)
{ {
struct drm_i915_private *dev_priv = dev->dev_private; struct intel_encoder *other_encoder;
u32 reg, val; struct drm_crtc *crtc = &encoder->new_crtc->base;
int i;
/* Clear any frame start delays used for debugging left by the BIOS */ if (WARN_ON(!crtc))
for_each_pipe(i) { return false;
reg = PIPECONF(i);
I915_WRITE(reg, I915_READ(reg) & ~PIPECONF_FRAME_START_DELAY_MASK); list_for_each_entry(other_encoder,
&crtc->dev->mode_config.encoder_list,
base.head) {
if (&other_encoder->new_crtc->base != crtc ||
encoder == other_encoder)
continue;
else
return true;
} }
if (HAS_PCH_SPLIT(dev)) return false;
return; }
/* Who knows what state these registers were left in by the BIOS or static bool intel_encoder_crtc_ok(struct drm_encoder *encoder,
* grub? struct drm_crtc *crtc)
* {
* If we leave the registers in a conflicting state (e.g. with the struct drm_device *dev;
* display plane reading from the other pipe than the one we intend struct drm_crtc *tmp;
* to use) then when we attempt to teardown the active mode, we will int crtc_mask = 1;
* not disable the pipes and planes in the correct order -- leaving
* a plane reading from a disabled pipe and possibly leading to
* undefined behaviour.
*/
reg = DSPCNTR(plane); WARN(!crtc, "checking null crtc?\n");
val = I915_READ(reg);
if ((val & DISPLAY_PLANE_ENABLE) == 0) dev = crtc->dev;
return;
if (!!(val & DISPPLANE_SEL_PIPE_MASK) == pipe)
return;
/* This display plane is active and attached to the other CPU pipe. */ list_for_each_entry(tmp, &dev->mode_config.crtc_list, head) {
pipe = !pipe; if (tmp == crtc)
break;
crtc_mask <<= 1;
}
/* Disable the plane and wait for it to stop reading from the pipe. */ if (encoder->possible_crtcs & crtc_mask)
intel_disable_plane(dev_priv, plane, pipe); return true;
intel_disable_pipe(dev_priv, pipe); return false;
} }
static void intel_crtc_reset(struct drm_crtc *crtc) /**
* intel_modeset_update_staged_output_state
*
* Updates the staged output configuration state, e.g. after we've read out the
* current hw state.
*/
static void intel_modeset_update_staged_output_state(struct drm_device *dev)
{ {
struct drm_device *dev = crtc->dev; struct intel_encoder *encoder;
struct intel_crtc *intel_crtc = to_intel_crtc(crtc); struct intel_connector *connector;
/* Reset flags back to the 'unknown' status so that they list_for_each_entry(connector, &dev->mode_config.connector_list,
* will be correctly set on the initial modeset. base.head) {
*/ connector->new_encoder =
intel_crtc->dpms_mode = -1; to_intel_encoder(connector->base.encoder);
}
/* We need to fix up any BIOS configuration that conflicts with list_for_each_entry(encoder, &dev->mode_config.encoder_list,
* our expectations. base.head) {
*/ encoder->new_crtc =
intel_sanitize_modesetting(dev, intel_crtc->pipe, intel_crtc->plane); to_intel_crtc(encoder->base.crtc);
}
} }
static struct drm_crtc_helper_funcs intel_helper_funcs = { /**
.dpms = intel_crtc_dpms, * intel_modeset_commit_output_state
.mode_fixup = intel_crtc_mode_fixup, *
.mode_set = intel_crtc_mode_set, * This function copies the stage display pipe configuration to the real one.
.mode_set_base = intel_pipe_set_base, */
.mode_set_base_atomic = intel_pipe_set_base_atomic, static void intel_modeset_commit_output_state(struct drm_device *dev)
.load_lut = intel_crtc_load_lut, {
.disable = intel_crtc_disable, struct intel_encoder *encoder;
}; struct intel_connector *connector;
static const struct drm_crtc_funcs intel_crtc_funcs = { list_for_each_entry(connector, &dev->mode_config.connector_list,
.reset = intel_crtc_reset, base.head) {
.cursor_set = intel_crtc_cursor_set, connector->base.encoder = &connector->new_encoder->base;
.cursor_move = intel_crtc_cursor_move, }
.gamma_set = intel_crtc_gamma_set,
.set_config = drm_crtc_helper_set_config,
.destroy = intel_crtc_destroy,
.page_flip = intel_crtc_page_flip,
};
static void intel_pch_pll_init(struct drm_device *dev) list_for_each_entry(encoder, &dev->mode_config.encoder_list,
base.head) {
encoder->base.crtc = &encoder->new_crtc->base;
}
}
static struct drm_display_mode *
intel_modeset_adjusted_mode(struct drm_crtc *crtc,
struct drm_display_mode *mode)
{ {
drm_i915_private_t *dev_priv = dev->dev_private; struct drm_device *dev = crtc->dev;
int i; struct drm_display_mode *adjusted_mode;
struct drm_encoder_helper_funcs *encoder_funcs;
struct intel_encoder *encoder;
if (dev_priv->num_pch_pll == 0) { adjusted_mode = drm_mode_duplicate(dev, mode);
DRM_DEBUG_KMS("No PCH PLLs on this hardware, skipping initialisation\n"); if (!adjusted_mode)
return; return ERR_PTR(-ENOMEM);
/* Pass our mode to the connectors and the CRTC to give them a chance to
* adjust it according to limitations or connector properties, and also
* a chance to reject the mode entirely.
*/
list_for_each_entry(encoder, &dev->mode_config.encoder_list,
base.head) {
if (&encoder->new_crtc->base != crtc)
continue;
encoder_funcs = encoder->base.helper_private;
if (!(encoder_funcs->mode_fixup(&encoder->base, mode,
adjusted_mode))) {
DRM_DEBUG_KMS("Encoder fixup failed\n");
goto fail;
}
} }
for (i = 0; i < dev_priv->num_pch_pll; i++) { if (!(intel_crtc_mode_fixup(crtc, mode, adjusted_mode))) {
dev_priv->pch_plls[i].pll_reg = _PCH_DPLL(i); DRM_DEBUG_KMS("CRTC fixup failed\n");
dev_priv->pch_plls[i].fp0_reg = _PCH_FP0(i); goto fail;
dev_priv->pch_plls[i].fp1_reg = _PCH_FP1(i);
} }
DRM_DEBUG_KMS("[CRTC:%d]\n", crtc->base.id);
return adjusted_mode;
fail:
drm_mode_destroy(dev, adjusted_mode);
return ERR_PTR(-EINVAL);
} }
static void intel_crtc_init(struct drm_device *dev, int pipe) /* Computes which crtcs are affected and sets the relevant bits in the mask. For
* simplicity we use the crtc's pipe number (because it's easier to obtain). */
static void
intel_modeset_affected_pipes(struct drm_crtc *crtc, unsigned *modeset_pipes,
unsigned *prepare_pipes, unsigned *disable_pipes)
{ {
drm_i915_private_t *dev_priv = dev->dev_private;
struct intel_crtc *intel_crtc; struct intel_crtc *intel_crtc;
int i; struct drm_device *dev = crtc->dev;
struct intel_encoder *encoder;
struct intel_connector *connector;
struct drm_crtc *tmp_crtc;
intel_crtc = kzalloc(sizeof(struct intel_crtc) + (INTELFB_CONN_LIMIT * sizeof(struct drm_connector *)), GFP_KERNEL); *disable_pipes = *modeset_pipes = *prepare_pipes = 0;
if (intel_crtc == NULL)
return;
drm_crtc_init(dev, &intel_crtc->base, &intel_crtc_funcs); /* Check which crtcs have changed outputs connected to them, these need
* to be part of the prepare_pipes mask. We don't (yet) support global
* modeset across multiple crtcs, so modeset_pipes will only have one
* bit set at most. */
list_for_each_entry(connector, &dev->mode_config.connector_list,
base.head) {
if (connector->base.encoder == &connector->new_encoder->base)
continue;
drm_mode_crtc_set_gamma_size(&intel_crtc->base, 256); if (connector->base.encoder) {
for (i = 0; i < 256; i++) { tmp_crtc = connector->base.encoder->crtc;
intel_crtc->lut_r[i] = i;
intel_crtc->lut_g[i] = i; *prepare_pipes |= 1 << to_intel_crtc(tmp_crtc)->pipe;
intel_crtc->lut_b[i] = i;
} }
/* Swap pipes & planes for FBC on pre-965 */ if (connector->new_encoder)
intel_crtc->pipe = pipe; *prepare_pipes |=
intel_crtc->plane = pipe; 1 << connector->new_encoder->new_crtc->pipe;
if (IS_MOBILE(dev) && IS_GEN3(dev)) {
DRM_DEBUG_KMS("swapping pipes & planes for FBC\n");
intel_crtc->plane = !pipe;
} }
BUG_ON(pipe >= ARRAY_SIZE(dev_priv->plane_to_crtc_mapping) || list_for_each_entry(encoder, &dev->mode_config.encoder_list,
dev_priv->plane_to_crtc_mapping[intel_crtc->plane] != NULL); base.head) {
dev_priv->plane_to_crtc_mapping[intel_crtc->plane] = &intel_crtc->base; if (encoder->base.crtc == &encoder->new_crtc->base)
dev_priv->pipe_to_crtc_mapping[intel_crtc->pipe] = &intel_crtc->base; continue;
intel_crtc_reset(&intel_crtc->base); if (encoder->base.crtc) {
intel_crtc->active = true; /* force the pipe off on setup_init_config */ tmp_crtc = encoder->base.crtc;
intel_crtc->bpp = 24; /* default for pre-Ironlake */
if (HAS_PCH_SPLIT(dev)) { *prepare_pipes |= 1 << to_intel_crtc(tmp_crtc)->pipe;
intel_helper_funcs.prepare = ironlake_crtc_prepare;
intel_helper_funcs.commit = ironlake_crtc_commit;
} else {
intel_helper_funcs.prepare = i9xx_crtc_prepare;
intel_helper_funcs.commit = i9xx_crtc_commit;
} }
if (encoder->new_crtc)
*prepare_pipes |= 1 << encoder->new_crtc->pipe;
}
/* Check for any pipes that will be fully disabled ... */
list_for_each_entry(intel_crtc, &dev->mode_config.crtc_list,
base.head) {
bool used = false;
/* Don't try to disable disabled crtcs. */
if (!intel_crtc->base.enabled)
continue;
list_for_each_entry(encoder, &dev->mode_config.encoder_list,
base.head) {
if (encoder->new_crtc == intel_crtc)
used = true;
}
if (!used)
*disable_pipes |= 1 << intel_crtc->pipe;
}
/* set_mode is also used to update properties on life display pipes. */
intel_crtc = to_intel_crtc(crtc);
if (crtc->enabled)
*prepare_pipes |= 1 << intel_crtc->pipe;
/* We only support modeset on one single crtc, hence we need to do that
* only for the passed in crtc iff we change anything else than just
* disable crtcs.
*
* This is actually not true, to be fully compatible with the old crtc
* helper we automatically disable _any_ output (i.e. doesn't need to be
* connected to the crtc we're modesetting on) if it's disconnected.
* Which is a rather nutty api (since changed the output configuration
* without userspace's explicit request can lead to confusion), but
* alas. Hence we currently need to modeset on all pipes we prepare. */
if (*prepare_pipes)
*modeset_pipes = *prepare_pipes;
/* ... and mask these out. */
*modeset_pipes &= ~(*disable_pipes);
*prepare_pipes &= ~(*disable_pipes);
}
static bool intel_crtc_in_use(struct drm_crtc *crtc)
{
struct drm_encoder *encoder;
struct drm_device *dev = crtc->dev;
list_for_each_entry(encoder, &dev->mode_config.encoder_list, head)
if (encoder->crtc == crtc)
return true;
return false;
}
static void
intel_modeset_update_state(struct drm_device *dev, unsigned prepare_pipes)
{
struct intel_encoder *intel_encoder;
struct intel_crtc *intel_crtc;
struct drm_connector *connector;
list_for_each_entry(intel_encoder, &dev->mode_config.encoder_list,
base.head) {
if (!intel_encoder->base.crtc)
continue;
intel_crtc = to_intel_crtc(intel_encoder->base.crtc);
if (prepare_pipes & (1 << intel_crtc->pipe))
intel_encoder->connectors_active = false;
}
intel_modeset_commit_output_state(dev);
/* Update computed state. */
list_for_each_entry(intel_crtc, &dev->mode_config.crtc_list,
base.head) {
intel_crtc->base.enabled = intel_crtc_in_use(&intel_crtc->base);
}
list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
if (!connector->encoder || !connector->encoder->crtc)
continue;
intel_crtc = to_intel_crtc(connector->encoder->crtc);
if (prepare_pipes & (1 << intel_crtc->pipe)) {
connector->dpms = DRM_MODE_DPMS_ON;
intel_encoder = to_intel_encoder(connector->encoder);
intel_encoder->connectors_active = true;
}
}
}
#define for_each_intel_crtc_masked(dev, mask, intel_crtc) \
list_for_each_entry((intel_crtc), \
&(dev)->mode_config.crtc_list, \
base.head) \
if (mask & (1 <<(intel_crtc)->pipe)) \
void
intel_modeset_check_state(struct drm_device *dev)
{
struct intel_crtc *crtc;
struct intel_encoder *encoder;
struct intel_connector *connector;
list_for_each_entry(connector, &dev->mode_config.connector_list,
base.head) {
/* This also checks the encoder/connector hw state with the
* ->get_hw_state callbacks. */
intel_connector_check_state(connector);
WARN(&connector->new_encoder->base != connector->base.encoder,
"connector's staged encoder doesn't match current encoder\n");
}
list_for_each_entry(encoder, &dev->mode_config.encoder_list,
base.head) {
bool enabled = false;
bool active = false;
enum pipe pipe, tracked_pipe;
DRM_DEBUG_KMS("[ENCODER:%d:%s]\n",
encoder->base.base.id,
drm_get_encoder_name(&encoder->base));
WARN(&encoder->new_crtc->base != encoder->base.crtc,
"encoder's stage crtc doesn't match current crtc\n");
WARN(encoder->connectors_active && !encoder->base.crtc,
"encoder's active_connectors set, but no crtc\n");
list_for_each_entry(connector, &dev->mode_config.connector_list,
base.head) {
if (connector->base.encoder != &encoder->base)
continue;
enabled = true;
if (connector->base.dpms != DRM_MODE_DPMS_OFF)
active = true;
}
WARN(!!encoder->base.crtc != enabled,
"encoder's enabled state mismatch "
"(expected %i, found %i)\n",
!!encoder->base.crtc, enabled);
WARN(active && !encoder->base.crtc,
"active encoder with no crtc\n");
WARN(encoder->connectors_active != active,
"encoder's computed active state doesn't match tracked active state "
"(expected %i, found %i)\n", active, encoder->connectors_active);
active = encoder->get_hw_state(encoder, &pipe);
WARN(active != encoder->connectors_active,
"encoder's hw state doesn't match sw tracking "
"(expected %i, found %i)\n",
encoder->connectors_active, active);
if (!encoder->base.crtc)
continue;
tracked_pipe = to_intel_crtc(encoder->base.crtc)->pipe;
WARN(active && pipe != tracked_pipe,
"active encoder's pipe doesn't match"
"(expected %i, found %i)\n",
tracked_pipe, pipe);
}
list_for_each_entry(crtc, &dev->mode_config.crtc_list,
base.head) {
bool enabled = false;
bool active = false;
DRM_DEBUG_KMS("[CRTC:%d]\n",
crtc->base.base.id);
WARN(crtc->active && !crtc->base.enabled,
"active crtc, but not enabled in sw tracking\n");
list_for_each_entry(encoder, &dev->mode_config.encoder_list,
base.head) {
if (encoder->base.crtc != &crtc->base)
continue;
enabled = true;
if (encoder->connectors_active)
active = true;
}
WARN(active != crtc->active,
"crtc's computed active state doesn't match tracked active state "
"(expected %i, found %i)\n", active, crtc->active);
WARN(enabled != crtc->base.enabled,
"crtc's computed enabled state doesn't match tracked enabled state "
"(expected %i, found %i)\n", enabled, crtc->base.enabled);
assert_pipe(dev->dev_private, crtc->pipe, crtc->active);
}
}
bool intel_set_mode(struct drm_crtc *crtc,
struct drm_display_mode *mode,
int x, int y, struct drm_framebuffer *fb)
{
struct drm_device *dev = crtc->dev;
drm_i915_private_t *dev_priv = dev->dev_private;
struct drm_display_mode *adjusted_mode, saved_mode, saved_hwmode;
struct drm_encoder_helper_funcs *encoder_funcs;
struct drm_encoder *encoder;
struct intel_crtc *intel_crtc;
unsigned disable_pipes, prepare_pipes, modeset_pipes;
bool ret = true;
intel_modeset_affected_pipes(crtc, &modeset_pipes,
&prepare_pipes, &disable_pipes);
DRM_DEBUG_KMS("set mode pipe masks: modeset: %x, prepare: %x, disable: %x\n",
modeset_pipes, prepare_pipes, disable_pipes);
for_each_intel_crtc_masked(dev, disable_pipes, intel_crtc)
intel_crtc_disable(&intel_crtc->base);
saved_hwmode = crtc->hwmode;
saved_mode = crtc->mode;
/* Hack: Because we don't (yet) support global modeset on multiple
* crtcs, we don't keep track of the new mode for more than one crtc.
* Hence simply check whether any bit is set in modeset_pipes in all the
* pieces of code that are not yet converted to deal with mutliple crtcs
* changing their mode at the same time. */
adjusted_mode = NULL;
if (modeset_pipes) {
adjusted_mode = intel_modeset_adjusted_mode(crtc, mode);
if (IS_ERR(adjusted_mode)) {
return false;
}
}
for_each_intel_crtc_masked(dev, prepare_pipes, intel_crtc) {
if (intel_crtc->base.enabled)
dev_priv->display.crtc_disable(&intel_crtc->base);
}
if (modeset_pipes) {
crtc->mode = *mode;
crtc->x = x;
crtc->y = y;
}
/* Only after disabling all output pipelines that will be changed can we
* update the the output configuration. */
intel_modeset_update_state(dev, prepare_pipes);
/* Set up the DPLL and any encoders state that needs to adjust or depend
* on the DPLL.
*/
for_each_intel_crtc_masked(dev, modeset_pipes, intel_crtc) {
ret = !intel_crtc_mode_set(&intel_crtc->base,
mode, adjusted_mode,
x, y, fb);
if (!ret)
goto done;
list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
if (encoder->crtc != &intel_crtc->base)
continue;
DRM_DEBUG_KMS("[ENCODER:%d:%s] set [MODE:%d:%s]\n",
encoder->base.id, drm_get_encoder_name(encoder),
mode->base.id, mode->name);
encoder_funcs = encoder->helper_private;
encoder_funcs->mode_set(encoder, mode, adjusted_mode);
}
}
/* Now enable the clocks, plane, pipe, and connectors that we set up. */
for_each_intel_crtc_masked(dev, prepare_pipes, intel_crtc)
dev_priv->display.crtc_enable(&intel_crtc->base);
if (modeset_pipes) {
/* Store real post-adjustment hardware mode. */
crtc->hwmode = *adjusted_mode;
/* Calculate and store various constants which
* are later needed by vblank and swap-completion
* timestamping. They are derived from true hwmode.
*/
drm_calc_timestamping_constants(crtc);
}
/* FIXME: add subpixel order */
done:
drm_mode_destroy(dev, adjusted_mode);
if (!ret && crtc->enabled) {
crtc->hwmode = saved_hwmode;
crtc->mode = saved_mode;
} else {
intel_modeset_check_state(dev);
}
return ret;
}
#undef for_each_intel_crtc_masked
static void intel_set_config_free(struct intel_set_config *config)
{
if (!config)
return;
kfree(config->save_connector_encoders);
kfree(config->save_encoder_crtcs);
kfree(config);
}
static int intel_set_config_save_state(struct drm_device *dev,
struct intel_set_config *config)
{
struct drm_encoder *encoder;
struct drm_connector *connector;
int count;
config->save_encoder_crtcs =
kcalloc(dev->mode_config.num_encoder,
sizeof(struct drm_crtc *), GFP_KERNEL);
if (!config->save_encoder_crtcs)
return -ENOMEM;
config->save_connector_encoders =
kcalloc(dev->mode_config.num_connector,
sizeof(struct drm_encoder *), GFP_KERNEL);
if (!config->save_connector_encoders)
return -ENOMEM;
/* Copy data. Note that driver private data is not affected.
* Should anything bad happen only the expected state is
* restored, not the drivers personal bookkeeping.
*/
count = 0;
list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
config->save_encoder_crtcs[count++] = encoder->crtc;
}
count = 0;
list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
config->save_connector_encoders[count++] = connector->encoder;
}
return 0;
}
static void intel_set_config_restore_state(struct drm_device *dev,
struct intel_set_config *config)
{
struct intel_encoder *encoder;
struct intel_connector *connector;
int count;
count = 0;
list_for_each_entry(encoder, &dev->mode_config.encoder_list, base.head) {
encoder->new_crtc =
to_intel_crtc(config->save_encoder_crtcs[count++]);
}
count = 0;
list_for_each_entry(connector, &dev->mode_config.connector_list, base.head) {
connector->new_encoder =
to_intel_encoder(config->save_connector_encoders[count++]);
}
}
static void
intel_set_config_compute_mode_changes(struct drm_mode_set *set,
struct intel_set_config *config)
{
/* We should be able to check here if the fb has the same properties
* and then just flip_or_move it */
if (set->crtc->fb != set->fb) {
/* If we have no fb then treat it as a full mode set */
if (set->crtc->fb == NULL) {
DRM_DEBUG_KMS("crtc has no fb, full mode set\n");
config->mode_changed = true;
} else if (set->fb == NULL) {
config->mode_changed = true;
} else if (set->fb->depth != set->crtc->fb->depth) {
config->mode_changed = true;
} else if (set->fb->bits_per_pixel !=
set->crtc->fb->bits_per_pixel) {
config->mode_changed = true;
} else
config->fb_changed = true;
}
if (set->fb && (set->x != set->crtc->x || set->y != set->crtc->y))
config->fb_changed = true;
if (set->mode && !drm_mode_equal(set->mode, &set->crtc->mode)) {
DRM_DEBUG_KMS("modes are different, full mode set\n");
drm_mode_debug_printmodeline(&set->crtc->mode);
drm_mode_debug_printmodeline(set->mode);
config->mode_changed = true;
}
}
static int
intel_modeset_stage_output_state(struct drm_device *dev,
struct drm_mode_set *set,
struct intel_set_config *config)
{
struct drm_crtc *new_crtc;
struct intel_connector *connector;
struct intel_encoder *encoder;
int count, ro;
/* The upper layers ensure that we either disabl a crtc or have a list
* of connectors. For paranoia, double-check this. */
WARN_ON(!set->fb && (set->num_connectors != 0));
WARN_ON(set->fb && (set->num_connectors == 0));
count = 0;
list_for_each_entry(connector, &dev->mode_config.connector_list,
base.head) {
/* Otherwise traverse passed in connector list and get encoders
* for them. */
for (ro = 0; ro < set->num_connectors; ro++) {
if (set->connectors[ro] == &connector->base) {
connector->new_encoder = connector->encoder;
break;
}
}
/* If we disable the crtc, disable all its connectors. Also, if
* the connector is on the changing crtc but not on the new
* connector list, disable it. */
if ((!set->fb || ro == set->num_connectors) &&
connector->base.encoder &&
connector->base.encoder->crtc == set->crtc) {
connector->new_encoder = NULL;
DRM_DEBUG_KMS("[CONNECTOR:%d:%s] to [NOCRTC]\n",
connector->base.base.id,
drm_get_connector_name(&connector->base));
}
if (&connector->new_encoder->base != connector->base.encoder) {
DRM_DEBUG_KMS("encoder changed, full mode switch\n");
config->mode_changed = true;
}
/* Disable all disconnected encoders. */
if (connector->base.status == connector_status_disconnected)
connector->new_encoder = NULL;
}
/* connector->new_encoder is now updated for all connectors. */
/* Update crtc of enabled connectors. */
count = 0;
list_for_each_entry(connector, &dev->mode_config.connector_list,
base.head) {
if (!connector->new_encoder)
continue;
new_crtc = connector->new_encoder->base.crtc;
for (ro = 0; ro < set->num_connectors; ro++) {
if (set->connectors[ro] == &connector->base)
new_crtc = set->crtc;
}
/* Make sure the new CRTC will work with the encoder */
if (!intel_encoder_crtc_ok(&connector->new_encoder->base,
new_crtc)) {
return -EINVAL;
}
connector->encoder->new_crtc = to_intel_crtc(new_crtc);
DRM_DEBUG_KMS("[CONNECTOR:%d:%s] to [CRTC:%d]\n",
connector->base.base.id,
drm_get_connector_name(&connector->base),
new_crtc->base.id);
}
/* Check for any encoders that needs to be disabled. */
list_for_each_entry(encoder, &dev->mode_config.encoder_list,
base.head) {
list_for_each_entry(connector,
&dev->mode_config.connector_list,
base.head) {
if (connector->new_encoder == encoder) {
WARN_ON(!connector->new_encoder->new_crtc);
goto next_encoder;
}
}
encoder->new_crtc = NULL;
next_encoder:
/* Only now check for crtc changes so we don't miss encoders
* that will be disabled. */
if (&encoder->new_crtc->base != encoder->base.crtc) {
DRM_DEBUG_KMS("crtc changed, full mode switch\n");
config->mode_changed = true;
}
}
/* Now we've also updated encoder->new_crtc for all encoders. */
return 0;
}
static int intel_crtc_set_config(struct drm_mode_set *set)
{
struct drm_device *dev;
struct drm_mode_set save_set;
struct intel_set_config *config;
int ret;
int i;
BUG_ON(!set);
BUG_ON(!set->crtc);
BUG_ON(!set->crtc->helper_private);
if (!set->mode)
set->fb = NULL;
/* The fb helper likes to play gross jokes with ->mode_set_config.
* Unfortunately the crtc helper doesn't do much at all for this case,
* so we have to cope with this madness until the fb helper is fixed up. */
if (set->fb && set->num_connectors == 0)
return 0;
if (set->fb) {
DRM_DEBUG_KMS("[CRTC:%d] [FB:%d] #connectors=%d (x y) (%i %i)\n",
set->crtc->base.id, set->fb->base.id,
(int)set->num_connectors, set->x, set->y);
} else {
DRM_DEBUG_KMS("[CRTC:%d] [NOFB]\n", set->crtc->base.id);
}
dev = set->crtc->dev;
ret = -ENOMEM;
config = kzalloc(sizeof(*config), GFP_KERNEL);
if (!config)
goto out_config;
ret = intel_set_config_save_state(dev, config);
if (ret)
goto out_config;
save_set.crtc = set->crtc;
save_set.mode = &set->crtc->mode;
save_set.x = set->crtc->x;
save_set.y = set->crtc->y;
save_set.fb = set->crtc->fb;
/* Compute whether we need a full modeset, only an fb base update or no
* change at all. In the future we might also check whether only the
* mode changed, e.g. for LVDS where we only change the panel fitter in
* such cases. */
intel_set_config_compute_mode_changes(set, config);
ret = intel_modeset_stage_output_state(dev, set, config);
if (ret)
goto fail;
if (config->mode_changed) {
if (set->mode) {
DRM_DEBUG_KMS("attempting to set mode from"
" userspace\n");
drm_mode_debug_printmodeline(set->mode);
}
if (!intel_set_mode(set->crtc, set->mode,
set->x, set->y, set->fb)) {
DRM_ERROR("failed to set mode on [CRTC:%d]\n",
set->crtc->base.id);
ret = -EINVAL;
goto fail;
}
if (set->crtc->enabled) {
DRM_DEBUG_KMS("Setting connector DPMS state to on\n");
for (i = 0; i < set->num_connectors; i++) {
DRM_DEBUG_KMS("\t[CONNECTOR:%d:%s] set DPMS on\n", set->connectors[i]->base.id,
drm_get_connector_name(set->connectors[i]));
set->connectors[i]->funcs->dpms(set->connectors[i], DRM_MODE_DPMS_ON);
}
}
} else if (config->fb_changed) {
ret = intel_pipe_set_base(set->crtc,
set->x, set->y, set->fb);
}
intel_set_config_free(config);
return 0;
fail:
intel_set_config_restore_state(dev, config);
/* Try to restore the config */
if (config->mode_changed &&
!intel_set_mode(save_set.crtc, save_set.mode,
save_set.x, save_set.y, save_set.fb))
DRM_ERROR("failed to restore config after modeset failure\n");
out_config:
intel_set_config_free(config);
return ret;
}
static const struct drm_crtc_funcs intel_crtc_funcs = {
.cursor_set = intel_crtc_cursor_set,
.cursor_move = intel_crtc_cursor_move,
.gamma_set = intel_crtc_gamma_set,
.set_config = intel_crtc_set_config,
.destroy = intel_crtc_destroy,
.page_flip = intel_crtc_page_flip,
};
static void intel_pch_pll_init(struct drm_device *dev)
{
drm_i915_private_t *dev_priv = dev->dev_private;
int i;
if (dev_priv->num_pch_pll == 0) {
DRM_DEBUG_KMS("No PCH PLLs on this hardware, skipping initialisation\n");
return;
}
for (i = 0; i < dev_priv->num_pch_pll; i++) {
dev_priv->pch_plls[i].pll_reg = _PCH_DPLL(i);
dev_priv->pch_plls[i].fp0_reg = _PCH_FP0(i);
dev_priv->pch_plls[i].fp1_reg = _PCH_FP1(i);
}
}
static void intel_crtc_init(struct drm_device *dev, int pipe)
{
drm_i915_private_t *dev_priv = dev->dev_private;
struct intel_crtc *intel_crtc;
int i;
intel_crtc = kzalloc(sizeof(struct intel_crtc) + (INTELFB_CONN_LIMIT * sizeof(struct drm_connector *)), GFP_KERNEL);
if (intel_crtc == NULL)
return;
drm_crtc_init(dev, &intel_crtc->base, &intel_crtc_funcs);
drm_mode_crtc_set_gamma_size(&intel_crtc->base, 256);
for (i = 0; i < 256; i++) {
intel_crtc->lut_r[i] = i;
intel_crtc->lut_g[i] = i;
intel_crtc->lut_b[i] = i;
}
/* Swap pipes & planes for FBC on pre-965 */
intel_crtc->pipe = pipe;
intel_crtc->plane = pipe;
if (IS_MOBILE(dev) && IS_GEN3(dev)) {
DRM_DEBUG_KMS("swapping pipes & planes for FBC\n");
intel_crtc->plane = !pipe;
}
BUG_ON(pipe >= ARRAY_SIZE(dev_priv->plane_to_crtc_mapping) ||
dev_priv->plane_to_crtc_mapping[intel_crtc->plane] != NULL);
dev_priv->plane_to_crtc_mapping[intel_crtc->plane] = &intel_crtc->base;
dev_priv->pipe_to_crtc_mapping[intel_crtc->pipe] = &intel_crtc->base;
intel_crtc->bpp = 24; /* default for pre-Ironlake */
drm_crtc_helper_add(&intel_crtc->base, &intel_helper_funcs); drm_crtc_helper_add(&intel_crtc->base, &intel_helper_funcs);
} }
...@@ -6874,9 +7655,6 @@ static void intel_setup_outputs(struct drm_device *dev) ...@@ -6874,9 +7655,6 @@ static void intel_setup_outputs(struct drm_device *dev)
intel_encoder_clones(encoder); intel_encoder_clones(encoder);
} }
/* disable all the possible outputs/crtcs before entering KMS mode */
drm_helper_disable_unused_functions(dev);
if (HAS_PCH_IBX(dev) || HAS_PCH_CPT(dev)) if (HAS_PCH_IBX(dev) || HAS_PCH_CPT(dev))
ironlake_init_pch_refclk(dev); ironlake_init_pch_refclk(dev);
} }
...@@ -6978,13 +7756,15 @@ static void intel_init_display(struct drm_device *dev) ...@@ -6978,13 +7756,15 @@ static void intel_init_display(struct drm_device *dev)
/* We always want a DPMS function */ /* We always want a DPMS function */
if (HAS_PCH_SPLIT(dev)) { if (HAS_PCH_SPLIT(dev)) {
dev_priv->display.dpms = ironlake_crtc_dpms;
dev_priv->display.crtc_mode_set = ironlake_crtc_mode_set; dev_priv->display.crtc_mode_set = ironlake_crtc_mode_set;
dev_priv->display.crtc_enable = ironlake_crtc_enable;
dev_priv->display.crtc_disable = ironlake_crtc_disable;
dev_priv->display.off = ironlake_crtc_off; dev_priv->display.off = ironlake_crtc_off;
dev_priv->display.update_plane = ironlake_update_plane; dev_priv->display.update_plane = ironlake_update_plane;
} else { } else {
dev_priv->display.dpms = i9xx_crtc_dpms;
dev_priv->display.crtc_mode_set = i9xx_crtc_mode_set; dev_priv->display.crtc_mode_set = i9xx_crtc_mode_set;
dev_priv->display.crtc_enable = i9xx_crtc_enable;
dev_priv->display.crtc_disable = i9xx_crtc_disable;
dev_priv->display.off = i9xx_crtc_off; dev_priv->display.off = i9xx_crtc_off;
dev_priv->display.update_plane = i9xx_update_plane; dev_priv->display.update_plane = i9xx_update_plane;
} }
...@@ -7233,11 +8013,258 @@ void intel_modeset_init(struct drm_device *dev) ...@@ -7233,11 +8013,258 @@ void intel_modeset_init(struct drm_device *dev)
intel_setup_outputs(dev); intel_setup_outputs(dev);
} }
static void
intel_connector_break_all_links(struct intel_connector *connector)
{
connector->base.dpms = DRM_MODE_DPMS_OFF;
connector->base.encoder = NULL;
connector->encoder->connectors_active = false;
connector->encoder->base.crtc = NULL;
}
static void intel_enable_pipe_a(struct drm_device *dev)
{
struct intel_connector *connector;
struct drm_connector *crt = NULL;
struct intel_load_detect_pipe load_detect_temp;
/* We can't just switch on the pipe A, we need to set things up with a
* proper mode and output configuration. As a gross hack, enable pipe A
* by enabling the load detect pipe once. */
list_for_each_entry(connector,
&dev->mode_config.connector_list,
base.head) {
if (connector->encoder->type == INTEL_OUTPUT_ANALOG) {
crt = &connector->base;
break;
}
}
if (!crt)
return;
if (intel_get_load_detect_pipe(crt, NULL, &load_detect_temp))
intel_release_load_detect_pipe(crt, &load_detect_temp);
}
static void intel_sanitize_crtc(struct intel_crtc *crtc)
{
struct drm_device *dev = crtc->base.dev;
struct drm_i915_private *dev_priv = dev->dev_private;
u32 reg, val;
/* Clear any frame start delays used for debugging left by the BIOS */
reg = PIPECONF(crtc->pipe);
I915_WRITE(reg, I915_READ(reg) & ~PIPECONF_FRAME_START_DELAY_MASK);
/* We need to sanitize the plane -> pipe mapping first because this will
* disable the crtc (and hence change the state) if it is wrong. */
if (!HAS_PCH_SPLIT(dev)) {
struct intel_connector *connector;
bool plane;
reg = DSPCNTR(crtc->plane);
val = I915_READ(reg);
if ((val & DISPLAY_PLANE_ENABLE) == 0 &&
(!!(val & DISPPLANE_SEL_PIPE_MASK) == crtc->pipe))
goto ok;
DRM_DEBUG_KMS("[CRTC:%d] wrong plane connection detected!\n",
crtc->base.base.id);
/* Pipe has the wrong plane attached and the plane is active.
* Temporarily change the plane mapping and disable everything
* ... */
plane = crtc->plane;
crtc->plane = !plane;
dev_priv->display.crtc_disable(&crtc->base);
crtc->plane = plane;
/* ... and break all links. */
list_for_each_entry(connector, &dev->mode_config.connector_list,
base.head) {
if (connector->encoder->base.crtc != &crtc->base)
continue;
intel_connector_break_all_links(connector);
}
WARN_ON(crtc->active);
crtc->base.enabled = false;
}
ok:
if (dev_priv->quirks & QUIRK_PIPEA_FORCE &&
crtc->pipe == PIPE_A && !crtc->active) {
/* BIOS forgot to enable pipe A, this mostly happens after
* resume. Force-enable the pipe to fix this, the update_dpms
* call below we restore the pipe to the right state, but leave
* the required bits on. */
intel_enable_pipe_a(dev);
}
/* Adjust the state of the output pipe according to whether we
* have active connectors/encoders. */
intel_crtc_update_dpms(&crtc->base);
if (crtc->active != crtc->base.enabled) {
struct intel_encoder *encoder;
/* This can happen either due to bugs in the get_hw_state
* functions or because the pipe is force-enabled due to the
* pipe A quirk. */
DRM_DEBUG_KMS("[CRTC:%d] hw state adjusted, was %s, now %s\n",
crtc->base.base.id,
crtc->base.enabled ? "enabled" : "disabled",
crtc->active ? "enabled" : "disabled");
crtc->base.enabled = crtc->active;
/* Because we only establish the connector -> encoder ->
* crtc links if something is active, this means the
* crtc is now deactivated. Break the links. connector
* -> encoder links are only establish when things are
* actually up, hence no need to break them. */
WARN_ON(crtc->active);
for_each_encoder_on_crtc(dev, &crtc->base, encoder) {
WARN_ON(encoder->connectors_active);
encoder->base.crtc = NULL;
}
}
}
static void intel_sanitize_encoder(struct intel_encoder *encoder)
{
struct intel_connector *connector;
struct drm_device *dev = encoder->base.dev;
/* We need to check both for a crtc link (meaning that the
* encoder is active and trying to read from a pipe) and the
* pipe itself being active. */
bool has_active_crtc = encoder->base.crtc &&
to_intel_crtc(encoder->base.crtc)->active;
if (encoder->connectors_active && !has_active_crtc) {
DRM_DEBUG_KMS("[ENCODER:%d:%s] has active connectors but no active pipe!\n",
encoder->base.base.id,
drm_get_encoder_name(&encoder->base));
/* Connector is active, but has no active pipe. This is
* fallout from our resume register restoring. Disable
* the encoder manually again. */
if (encoder->base.crtc) {
DRM_DEBUG_KMS("[ENCODER:%d:%s] manually disabled\n",
encoder->base.base.id,
drm_get_encoder_name(&encoder->base));
encoder->disable(encoder);
}
/* Inconsistent output/port/pipe state happens presumably due to
* a bug in one of the get_hw_state functions. Or someplace else
* in our code, like the register restore mess on resume. Clamp
* things to off as a safer default. */
list_for_each_entry(connector,
&dev->mode_config.connector_list,
base.head) {
if (connector->encoder != encoder)
continue;
intel_connector_break_all_links(connector);
}
}
/* Enabled encoders without active connectors will be fixed in
* the crtc fixup. */
}
/* Scan out the current hw modeset state, sanitizes it and maps it into the drm
* and i915 state tracking structures. */
void intel_modeset_setup_hw_state(struct drm_device *dev)
{
struct drm_i915_private *dev_priv = dev->dev_private;
enum pipe pipe;
u32 tmp;
struct intel_crtc *crtc;
struct intel_encoder *encoder;
struct intel_connector *connector;
for_each_pipe(pipe) {
crtc = to_intel_crtc(dev_priv->pipe_to_crtc_mapping[pipe]);
tmp = I915_READ(PIPECONF(pipe));
if (tmp & PIPECONF_ENABLE)
crtc->active = true;
else
crtc->active = false;
crtc->base.enabled = crtc->active;
DRM_DEBUG_KMS("[CRTC:%d] hw state readout: %s\n",
crtc->base.base.id,
crtc->active ? "enabled" : "disabled");
}
list_for_each_entry(encoder, &dev->mode_config.encoder_list,
base.head) {
pipe = 0;
if (encoder->get_hw_state(encoder, &pipe)) {
encoder->base.crtc =
dev_priv->pipe_to_crtc_mapping[pipe];
} else {
encoder->base.crtc = NULL;
}
encoder->connectors_active = false;
DRM_DEBUG_KMS("[ENCODER:%d:%s] hw state readout: %s, pipe=%i\n",
encoder->base.base.id,
drm_get_encoder_name(&encoder->base),
encoder->base.crtc ? "enabled" : "disabled",
pipe);
}
list_for_each_entry(connector, &dev->mode_config.connector_list,
base.head) {
if (connector->get_hw_state(connector)) {
connector->base.dpms = DRM_MODE_DPMS_ON;
connector->encoder->connectors_active = true;
connector->base.encoder = &connector->encoder->base;
} else {
connector->base.dpms = DRM_MODE_DPMS_OFF;
connector->base.encoder = NULL;
}
DRM_DEBUG_KMS("[CONNECTOR:%d:%s] hw state readout: %s\n",
connector->base.base.id,
drm_get_connector_name(&connector->base),
connector->base.encoder ? "enabled" : "disabled");
}
/* HW state is read out, now we need to sanitize this mess. */
list_for_each_entry(encoder, &dev->mode_config.encoder_list,
base.head) {
intel_sanitize_encoder(encoder);
}
for_each_pipe(pipe) {
crtc = to_intel_crtc(dev_priv->pipe_to_crtc_mapping[pipe]);
intel_sanitize_crtc(crtc);
}
intel_modeset_update_staged_output_state(dev);
intel_modeset_check_state(dev);
}
void intel_modeset_gem_init(struct drm_device *dev) void intel_modeset_gem_init(struct drm_device *dev)
{ {
intel_modeset_init_hw(dev); intel_modeset_init_hw(dev);
intel_setup_overlay(dev); intel_setup_overlay(dev);
intel_modeset_setup_hw_state(dev);
} }
void intel_modeset_cleanup(struct drm_device *dev) void intel_modeset_cleanup(struct drm_device *dev)
......
...@@ -1248,10 +1248,57 @@ static void intel_dp_sink_dpms(struct intel_dp *intel_dp, int mode) ...@@ -1248,10 +1248,57 @@ static void intel_dp_sink_dpms(struct intel_dp *intel_dp, int mode)
} }
} }
static void intel_dp_prepare(struct drm_encoder *encoder) static bool intel_dp_get_hw_state(struct intel_encoder *encoder,
enum pipe *pipe)
{ {
struct intel_dp *intel_dp = enc_to_intel_dp(encoder); struct intel_dp *intel_dp = enc_to_intel_dp(&encoder->base);
struct drm_device *dev = encoder->base.dev;
struct drm_i915_private *dev_priv = dev->dev_private;
u32 tmp = I915_READ(intel_dp->output_reg);
if (!(tmp & DP_PORT_EN))
return false;
if (is_cpu_edp(intel_dp) && IS_GEN7(dev)) {
*pipe = PORT_TO_PIPE_CPT(tmp);
} else if (!HAS_PCH_CPT(dev) || is_cpu_edp(intel_dp)) {
*pipe = PORT_TO_PIPE(tmp);
} else {
u32 trans_sel;
u32 trans_dp;
int i;
switch (intel_dp->output_reg) {
case PCH_DP_B:
trans_sel = TRANS_DP_PORT_SEL_B;
break;
case PCH_DP_C:
trans_sel = TRANS_DP_PORT_SEL_C;
break;
case PCH_DP_D:
trans_sel = TRANS_DP_PORT_SEL_D;
break;
default:
return true;
}
for_each_pipe(i) {
trans_dp = I915_READ(TRANS_DP_CTL(i));
if ((trans_dp & TRANS_DP_PORT_SEL_MASK) == trans_sel) {
*pipe = i;
return true;
}
}
}
DRM_DEBUG_KMS("No pipe for dp port 0x%x found\n", intel_dp->output_reg);
return true;
}
static void intel_disable_dp(struct intel_encoder *encoder)
{
struct intel_dp *intel_dp = enc_to_intel_dp(&encoder->base);
/* Make sure the panel is off before trying to change the mode. But also /* Make sure the panel is off before trying to change the mode. But also
* ensure that we have vdd while we switch off the panel. */ * ensure that we have vdd while we switch off the panel. */
...@@ -1262,60 +1309,58 @@ static void intel_dp_prepare(struct drm_encoder *encoder) ...@@ -1262,60 +1309,58 @@ static void intel_dp_prepare(struct drm_encoder *encoder)
intel_dp_link_down(intel_dp); intel_dp_link_down(intel_dp);
} }
static void intel_dp_commit(struct drm_encoder *encoder) static void intel_enable_dp(struct intel_encoder *encoder)
{ {
struct intel_dp *intel_dp = enc_to_intel_dp(encoder); struct intel_dp *intel_dp = enc_to_intel_dp(&encoder->base);
struct drm_device *dev = encoder->dev; struct drm_device *dev = encoder->base.dev;
struct intel_crtc *intel_crtc = to_intel_crtc(intel_dp->base.base.crtc); struct drm_i915_private *dev_priv = dev->dev_private;
uint32_t dp_reg = I915_READ(intel_dp->output_reg);
ironlake_edp_panel_vdd_on(intel_dp); ironlake_edp_panel_vdd_on(intel_dp);
intel_dp_sink_dpms(intel_dp, DRM_MODE_DPMS_ON); intel_dp_sink_dpms(intel_dp, DRM_MODE_DPMS_ON);
if (!(dp_reg & DP_PORT_EN)) {
intel_dp_start_link_train(intel_dp); intel_dp_start_link_train(intel_dp);
ironlake_edp_panel_on(intel_dp); ironlake_edp_panel_on(intel_dp);
ironlake_edp_panel_vdd_off(intel_dp, true); ironlake_edp_panel_vdd_off(intel_dp, true);
intel_dp_complete_link_train(intel_dp); intel_dp_complete_link_train(intel_dp);
} else
ironlake_edp_panel_vdd_off(intel_dp, false);
ironlake_edp_backlight_on(intel_dp); ironlake_edp_backlight_on(intel_dp);
intel_dp->dpms_mode = DRM_MODE_DPMS_ON;
if (HAS_PCH_CPT(dev))
intel_cpt_verify_modeset(dev, intel_crtc->pipe);
} }
static void static void
intel_dp_dpms(struct drm_encoder *encoder, int mode) intel_dp_dpms(struct drm_connector *connector, int mode)
{ {
struct intel_dp *intel_dp = enc_to_intel_dp(encoder); struct intel_dp *intel_dp = intel_attached_dp(connector);
struct drm_device *dev = encoder->dev;
struct drm_i915_private *dev_priv = dev->dev_private; /* DP supports only 2 dpms states. */
uint32_t dp_reg = I915_READ(intel_dp->output_reg); if (mode != DRM_MODE_DPMS_ON)
mode = DRM_MODE_DPMS_OFF;
if (mode == connector->dpms)
return;
connector->dpms = mode;
/* Only need to change hw state when actually enabled */
if (!intel_dp->base.base.crtc) {
intel_dp->base.connectors_active = false;
return;
}
if (mode != DRM_MODE_DPMS_ON) { if (mode != DRM_MODE_DPMS_ON) {
/* Switching the panel off requires vdd. */ intel_encoder_dpms(&intel_dp->base, mode);
ironlake_edp_panel_vdd_on(intel_dp);
ironlake_edp_backlight_off(intel_dp);
intel_dp_sink_dpms(intel_dp, mode);
ironlake_edp_panel_off(intel_dp);
intel_dp_link_down(intel_dp);
if (is_cpu_edp(intel_dp)) if (is_cpu_edp(intel_dp))
ironlake_edp_pll_off(encoder); ironlake_edp_pll_off(&intel_dp->base.base);
} else { } else {
if (is_cpu_edp(intel_dp)) if (is_cpu_edp(intel_dp))
ironlake_edp_pll_on(encoder); ironlake_edp_pll_on(&intel_dp->base.base);
ironlake_edp_panel_vdd_on(intel_dp); intel_encoder_dpms(&intel_dp->base, mode);
intel_dp_sink_dpms(intel_dp, mode);
if (!(dp_reg & DP_PORT_EN)) {
intel_dp_start_link_train(intel_dp);
ironlake_edp_panel_on(intel_dp);
ironlake_edp_panel_vdd_off(intel_dp, true);
intel_dp_complete_link_train(intel_dp);
} else
ironlake_edp_panel_vdd_off(intel_dp, false);
ironlake_edp_backlight_on(intel_dp);
} }
intel_dp->dpms_mode = mode;
intel_modeset_check_state(connector->dev);
} }
/* /*
...@@ -2016,10 +2061,10 @@ intel_dp_check_link_status(struct intel_dp *intel_dp) ...@@ -2016,10 +2061,10 @@ intel_dp_check_link_status(struct intel_dp *intel_dp)
u8 sink_irq_vector; u8 sink_irq_vector;
u8 link_status[DP_LINK_STATUS_SIZE]; u8 link_status[DP_LINK_STATUS_SIZE];
if (intel_dp->dpms_mode != DRM_MODE_DPMS_ON) if (!intel_dp->base.connectors_active)
return; return;
if (!intel_dp->base.base.crtc) if (WARN_ON(!intel_dp->base.base.crtc))
return; return;
/* Try to read receiver status if the link appears to be up */ /* Try to read receiver status if the link appears to be up */
...@@ -2305,9 +2350,8 @@ intel_dp_set_property(struct drm_connector *connector, ...@@ -2305,9 +2350,8 @@ intel_dp_set_property(struct drm_connector *connector,
done: done:
if (intel_dp->base.base.crtc) { if (intel_dp->base.base.crtc) {
struct drm_crtc *crtc = intel_dp->base.base.crtc; struct drm_crtc *crtc = intel_dp->base.base.crtc;
drm_crtc_helper_set_mode(crtc, &crtc->mode, intel_set_mode(crtc, &crtc->mode,
crtc->x, crtc->y, crtc->x, crtc->y, crtc->fb);
crtc->fb);
} }
return 0; return 0;
...@@ -2341,15 +2385,13 @@ static void intel_dp_encoder_destroy(struct drm_encoder *encoder) ...@@ -2341,15 +2385,13 @@ static void intel_dp_encoder_destroy(struct drm_encoder *encoder)
} }
static const struct drm_encoder_helper_funcs intel_dp_helper_funcs = { static const struct drm_encoder_helper_funcs intel_dp_helper_funcs = {
.dpms = intel_dp_dpms,
.mode_fixup = intel_dp_mode_fixup, .mode_fixup = intel_dp_mode_fixup,
.prepare = intel_dp_prepare,
.mode_set = intel_dp_mode_set, .mode_set = intel_dp_mode_set,
.commit = intel_dp_commit, .disable = intel_encoder_noop,
}; };
static const struct drm_connector_funcs intel_dp_connector_funcs = { static const struct drm_connector_funcs intel_dp_connector_funcs = {
.dpms = drm_helper_connector_dpms, .dpms = intel_dp_dpms,
.detect = intel_dp_detect, .detect = intel_dp_detect,
.fill_modes = drm_helper_probe_single_connector_modes, .fill_modes = drm_helper_probe_single_connector_modes,
.set_property = intel_dp_set_property, .set_property = intel_dp_set_property,
...@@ -2436,7 +2478,6 @@ intel_dp_init(struct drm_device *dev, int output_reg, enum port port) ...@@ -2436,7 +2478,6 @@ intel_dp_init(struct drm_device *dev, int output_reg, enum port port)
intel_dp->output_reg = output_reg; intel_dp->output_reg = output_reg;
intel_dp->port = port; intel_dp->port = port;
intel_dp->dpms_mode = -1;
intel_connector = kzalloc(sizeof(struct intel_connector), GFP_KERNEL); intel_connector = kzalloc(sizeof(struct intel_connector), GFP_KERNEL);
if (!intel_connector) { if (!intel_connector) {
...@@ -2480,6 +2521,11 @@ intel_dp_init(struct drm_device *dev, int output_reg, enum port port) ...@@ -2480,6 +2521,11 @@ intel_dp_init(struct drm_device *dev, int output_reg, enum port port)
intel_connector_attach_encoder(intel_connector, intel_encoder); intel_connector_attach_encoder(intel_connector, intel_encoder);
drm_sysfs_connector_add(connector); drm_sysfs_connector_add(connector);
intel_encoder->enable = intel_enable_dp;
intel_encoder->disable = intel_disable_dp;
intel_encoder->get_hw_state = intel_dp_get_hw_state;
intel_connector->get_hw_state = intel_connector_get_hw_state;
/* Set up the DDC bus. */ /* Set up the DDC bus. */
switch (port) { switch (port) {
case PORT_A: case PORT_A:
......
...@@ -137,6 +137,12 @@ struct intel_fbdev { ...@@ -137,6 +137,12 @@ struct intel_fbdev {
struct intel_encoder { struct intel_encoder {
struct drm_encoder base; struct drm_encoder base;
/*
* The new crtc this encoder will be driven from. Only differs from
* base->crtc while a modeset is in progress.
*/
struct intel_crtc *new_crtc;
int type; int type;
bool needs_tv_clock; bool needs_tv_clock;
/* /*
...@@ -144,13 +150,33 @@ struct intel_encoder { ...@@ -144,13 +150,33 @@ struct intel_encoder {
* simple flag is enough to compute the possible_clones mask. * simple flag is enough to compute the possible_clones mask.
*/ */
bool cloneable; bool cloneable;
bool connectors_active;
void (*hot_plug)(struct intel_encoder *); void (*hot_plug)(struct intel_encoder *);
void (*enable)(struct intel_encoder *);
void (*disable)(struct intel_encoder *);
/* Read out the current hw state of this connector, returning true if
* the encoder is active. If the encoder is enabled it also set the pipe
* it is connected to in the pipe parameter. */
bool (*get_hw_state)(struct intel_encoder *, enum pipe *pipe);
int crtc_mask; int crtc_mask;
}; };
struct intel_connector { struct intel_connector {
struct drm_connector base; struct drm_connector base;
/*
* The fixed encoder this connector is connected to.
*/
struct intel_encoder *encoder; struct intel_encoder *encoder;
/*
* The new encoder this connector will be driven. Only differs from
* encoder while a modeset is in progress.
*/
struct intel_encoder *new_encoder;
/* Reads out the current hw, returning true if the connector is enabled
* and active (i.e. dpms ON state). */
bool (*get_hw_state)(struct intel_connector *);
}; };
struct intel_crtc { struct intel_crtc {
...@@ -158,8 +184,12 @@ struct intel_crtc { ...@@ -158,8 +184,12 @@ struct intel_crtc {
enum pipe pipe; enum pipe pipe;
enum plane plane; enum plane plane;
u8 lut_r[256], lut_g[256], lut_b[256]; u8 lut_r[256], lut_g[256], lut_b[256];
int dpms_mode; /*
bool active; /* is the crtc on? independent of the dpms mode */ * Whether the crtc and the connected output pipeline is active. Implies
* that crtc->enabled is set, i.e. the current mode configuration has
* some outputs connected to this crtc.
*/
bool active;
bool primary_disabled; /* is the crtc obscured by a plane? */ bool primary_disabled; /* is the crtc obscured by a plane? */
bool lowfreq_avail; bool lowfreq_avail;
struct intel_overlay *overlay; struct intel_overlay *overlay;
...@@ -311,7 +341,6 @@ struct intel_dp { ...@@ -311,7 +341,6 @@ struct intel_dp {
enum hdmi_force_audio force_audio; enum hdmi_force_audio force_audio;
enum port port; enum port port;
uint32_t color_range; uint32_t color_range;
int dpms_mode;
uint8_t link_bw; uint8_t link_bw;
uint8_t lane_count; uint8_t lane_count;
uint8_t dpcd[DP_RECEIVER_CAP_SIZE]; uint8_t dpcd[DP_RECEIVER_CAP_SIZE];
...@@ -413,10 +442,27 @@ extern void intel_panel_disable_backlight(struct drm_device *dev); ...@@ -413,10 +442,27 @@ extern void intel_panel_disable_backlight(struct drm_device *dev);
extern void intel_panel_destroy_backlight(struct drm_device *dev); extern void intel_panel_destroy_backlight(struct drm_device *dev);
extern enum drm_connector_status intel_panel_detect(struct drm_device *dev); extern enum drm_connector_status intel_panel_detect(struct drm_device *dev);
struct intel_set_config {
struct drm_encoder **save_connector_encoders;
struct drm_crtc **save_encoder_crtcs;
bool fb_changed;
bool mode_changed;
};
extern bool intel_set_mode(struct drm_crtc *crtc, struct drm_display_mode *mode,
int x, int y, struct drm_framebuffer *old_fb);
extern void intel_modeset_disable(struct drm_device *dev);
extern void intel_crtc_load_lut(struct drm_crtc *crtc); extern void intel_crtc_load_lut(struct drm_crtc *crtc);
extern void intel_encoder_prepare(struct drm_encoder *encoder); extern void intel_crtc_update_dpms(struct drm_crtc *crtc);
extern void intel_encoder_commit(struct drm_encoder *encoder); extern void intel_encoder_noop(struct drm_encoder *encoder);
extern void intel_encoder_destroy(struct drm_encoder *encoder); extern void intel_encoder_destroy(struct drm_encoder *encoder);
extern void intel_encoder_dpms(struct intel_encoder *encoder, int mode);
extern bool intel_encoder_check_is_cloned(struct intel_encoder *encoder);
extern void intel_connector_dpms(struct drm_connector *, int mode);
extern bool intel_connector_get_hw_state(struct intel_connector *connector);
extern void intel_modeset_check_state(struct drm_device *dev);
static inline struct intel_encoder *intel_attached_encoder(struct drm_connector *connector) static inline struct intel_encoder *intel_attached_encoder(struct drm_connector *connector)
{ {
...@@ -523,7 +569,10 @@ extern void intel_disable_gt_powersave(struct drm_device *dev); ...@@ -523,7 +569,10 @@ extern void intel_disable_gt_powersave(struct drm_device *dev);
extern void gen6_gt_check_fifodbg(struct drm_i915_private *dev_priv); extern void gen6_gt_check_fifodbg(struct drm_i915_private *dev_priv);
extern void ironlake_teardown_rc6(struct drm_device *dev); extern void ironlake_teardown_rc6(struct drm_device *dev);
extern void intel_ddi_dpms(struct drm_encoder *encoder, int mode); extern void intel_enable_ddi(struct intel_encoder *encoder);
extern void intel_disable_ddi(struct intel_encoder *encoder);
extern bool intel_ddi_get_hw_state(struct intel_encoder *encoder,
enum pipe *pipe);
extern void intel_ddi_mode_set(struct drm_encoder *encoder, extern void intel_ddi_mode_set(struct drm_encoder *encoder,
struct drm_display_mode *mode, struct drm_display_mode *mode,
struct drm_display_mode *adjusted_mode); struct drm_display_mode *adjusted_mode);
......
...@@ -105,22 +105,91 @@ static struct intel_dvo *intel_attached_dvo(struct drm_connector *connector) ...@@ -105,22 +105,91 @@ static struct intel_dvo *intel_attached_dvo(struct drm_connector *connector)
struct intel_dvo, base); struct intel_dvo, base);
} }
static void intel_dvo_dpms(struct drm_encoder *encoder, int mode) static bool intel_dvo_connector_get_hw_state(struct intel_connector *connector)
{ {
struct drm_i915_private *dev_priv = encoder->dev->dev_private; struct intel_dvo *intel_dvo = intel_attached_dvo(&connector->base);
struct intel_dvo *intel_dvo = enc_to_intel_dvo(encoder);
return intel_dvo->dev.dev_ops->get_hw_state(&intel_dvo->dev);
}
static bool intel_dvo_get_hw_state(struct intel_encoder *encoder,
enum pipe *pipe)
{
struct drm_device *dev = encoder->base.dev;
struct drm_i915_private *dev_priv = dev->dev_private;
struct intel_dvo *intel_dvo = enc_to_intel_dvo(&encoder->base);
u32 tmp;
tmp = I915_READ(intel_dvo->dev.dvo_reg);
if (!(tmp & DVO_ENABLE))
return false;
*pipe = PORT_TO_PIPE(tmp);
return true;
}
static void intel_disable_dvo(struct intel_encoder *encoder)
{
struct drm_i915_private *dev_priv = encoder->base.dev->dev_private;
struct intel_dvo *intel_dvo = enc_to_intel_dvo(&encoder->base);
u32 dvo_reg = intel_dvo->dev.dvo_reg;
u32 temp = I915_READ(dvo_reg);
intel_dvo->dev.dev_ops->dpms(&intel_dvo->dev, false);
I915_WRITE(dvo_reg, temp & ~DVO_ENABLE);
I915_READ(dvo_reg);
}
static void intel_enable_dvo(struct intel_encoder *encoder)
{
struct drm_i915_private *dev_priv = encoder->base.dev->dev_private;
struct intel_dvo *intel_dvo = enc_to_intel_dvo(&encoder->base);
u32 dvo_reg = intel_dvo->dev.dvo_reg; u32 dvo_reg = intel_dvo->dev.dvo_reg;
u32 temp = I915_READ(dvo_reg); u32 temp = I915_READ(dvo_reg);
if (mode == DRM_MODE_DPMS_ON) {
I915_WRITE(dvo_reg, temp | DVO_ENABLE); I915_WRITE(dvo_reg, temp | DVO_ENABLE);
I915_READ(dvo_reg); I915_READ(dvo_reg);
intel_dvo->dev.dev_ops->dpms(&intel_dvo->dev, true);
}
static void intel_dvo_dpms(struct drm_connector *connector, int mode)
{
struct intel_dvo *intel_dvo = intel_attached_dvo(connector);
struct drm_crtc *crtc;
/* dvo supports only 2 dpms states. */
if (mode != DRM_MODE_DPMS_ON)
mode = DRM_MODE_DPMS_OFF;
if (mode == connector->dpms)
return;
connector->dpms = mode;
/* Only need to change hw state when actually enabled */
crtc = intel_dvo->base.base.crtc;
if (!crtc) {
intel_dvo->base.connectors_active = false;
return;
}
if (mode == DRM_MODE_DPMS_ON) {
intel_dvo->base.connectors_active = true;
intel_crtc_update_dpms(crtc);
intel_dvo->dev.dev_ops->dpms(&intel_dvo->dev, true); intel_dvo->dev.dev_ops->dpms(&intel_dvo->dev, true);
} else { } else {
intel_dvo->dev.dev_ops->dpms(&intel_dvo->dev, false); intel_dvo->dev.dev_ops->dpms(&intel_dvo->dev, false);
I915_WRITE(dvo_reg, temp & ~DVO_ENABLE);
I915_READ(dvo_reg); intel_dvo->base.connectors_active = false;
intel_crtc_update_dpms(crtc);
} }
intel_modeset_check_state(connector->dev);
} }
static int intel_dvo_mode_valid(struct drm_connector *connector, static int intel_dvo_mode_valid(struct drm_connector *connector,
...@@ -275,15 +344,13 @@ static void intel_dvo_destroy(struct drm_connector *connector) ...@@ -275,15 +344,13 @@ static void intel_dvo_destroy(struct drm_connector *connector)
} }
static const struct drm_encoder_helper_funcs intel_dvo_helper_funcs = { static const struct drm_encoder_helper_funcs intel_dvo_helper_funcs = {
.dpms = intel_dvo_dpms,
.mode_fixup = intel_dvo_mode_fixup, .mode_fixup = intel_dvo_mode_fixup,
.prepare = intel_encoder_prepare,
.mode_set = intel_dvo_mode_set, .mode_set = intel_dvo_mode_set,
.commit = intel_encoder_commit, .disable = intel_encoder_noop,
}; };
static const struct drm_connector_funcs intel_dvo_connector_funcs = { static const struct drm_connector_funcs intel_dvo_connector_funcs = {
.dpms = drm_helper_connector_dpms, .dpms = intel_dvo_dpms,
.detect = intel_dvo_detect, .detect = intel_dvo_detect,
.destroy = intel_dvo_destroy, .destroy = intel_dvo_destroy,
.fill_modes = drm_helper_probe_single_connector_modes, .fill_modes = drm_helper_probe_single_connector_modes,
...@@ -372,6 +439,11 @@ void intel_dvo_init(struct drm_device *dev) ...@@ -372,6 +439,11 @@ void intel_dvo_init(struct drm_device *dev)
drm_encoder_init(dev, &intel_encoder->base, drm_encoder_init(dev, &intel_encoder->base,
&intel_dvo_enc_funcs, encoder_type); &intel_dvo_enc_funcs, encoder_type);
intel_encoder->disable = intel_disable_dvo;
intel_encoder->enable = intel_enable_dvo;
intel_encoder->get_hw_state = intel_dvo_get_hw_state;
intel_connector->get_hw_state = intel_dvo_connector_get_hw_state;
/* Now, try to find a controller */ /* Now, try to find a controller */
for (i = 0; i < ARRAY_SIZE(intel_dvo_devices); i++) { for (i = 0; i < ARRAY_SIZE(intel_dvo_devices); i++) {
struct drm_connector *connector = &intel_connector->base; struct drm_connector *connector = &intel_connector->base;
......
...@@ -601,11 +601,32 @@ static void intel_hdmi_mode_set(struct drm_encoder *encoder, ...@@ -601,11 +601,32 @@ static void intel_hdmi_mode_set(struct drm_encoder *encoder,
intel_hdmi->set_infoframes(encoder, adjusted_mode); intel_hdmi->set_infoframes(encoder, adjusted_mode);
} }
static void intel_hdmi_dpms(struct drm_encoder *encoder, int mode) static bool intel_hdmi_get_hw_state(struct intel_encoder *encoder,
enum pipe *pipe)
{ {
struct drm_device *dev = encoder->dev; struct drm_device *dev = encoder->base.dev;
struct drm_i915_private *dev_priv = dev->dev_private; struct drm_i915_private *dev_priv = dev->dev_private;
struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(encoder); struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(&encoder->base);
u32 tmp;
tmp = I915_READ(intel_hdmi->sdvox_reg);
if (!(tmp & SDVO_ENABLE))
return false;
if (HAS_PCH_CPT(dev))
*pipe = PORT_TO_PIPE_CPT(tmp);
else
*pipe = PORT_TO_PIPE(tmp);
return true;
}
static void intel_enable_hdmi(struct intel_encoder *encoder)
{
struct drm_device *dev = encoder->base.dev;
struct drm_i915_private *dev_priv = dev->dev_private;
struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(&encoder->base);
u32 temp; u32 temp;
u32 enable_bits = SDVO_ENABLE; u32 enable_bits = SDVO_ENABLE;
...@@ -617,10 +638,55 @@ static void intel_hdmi_dpms(struct drm_encoder *encoder, int mode) ...@@ -617,10 +638,55 @@ static void intel_hdmi_dpms(struct drm_encoder *encoder, int mode)
/* HW workaround for IBX, we need to move the port to transcoder A /* HW workaround for IBX, we need to move the port to transcoder A
* before disabling it. */ * before disabling it. */
if (HAS_PCH_IBX(dev)) { if (HAS_PCH_IBX(dev)) {
struct drm_crtc *crtc = encoder->crtc; struct drm_crtc *crtc = encoder->base.crtc;
int pipe = crtc ? to_intel_crtc(crtc)->pipe : -1;
/* Restore the transcoder select bit. */
if (pipe == PIPE_B)
enable_bits |= SDVO_PIPE_B_SELECT;
}
/* HW workaround, need to toggle enable bit off and on for 12bpc, but
* we do this anyway which shows more stable in testing.
*/
if (HAS_PCH_SPLIT(dev)) {
I915_WRITE(intel_hdmi->sdvox_reg, temp & ~SDVO_ENABLE);
POSTING_READ(intel_hdmi->sdvox_reg);
}
temp |= enable_bits;
I915_WRITE(intel_hdmi->sdvox_reg, temp);
POSTING_READ(intel_hdmi->sdvox_reg);
/* HW workaround, need to write this twice for issue that may result
* in first write getting masked.
*/
if (HAS_PCH_SPLIT(dev)) {
I915_WRITE(intel_hdmi->sdvox_reg, temp);
POSTING_READ(intel_hdmi->sdvox_reg);
}
}
static void intel_disable_hdmi(struct intel_encoder *encoder)
{
struct drm_device *dev = encoder->base.dev;
struct drm_i915_private *dev_priv = dev->dev_private;
struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(&encoder->base);
u32 temp;
u32 enable_bits = SDVO_ENABLE;
if (intel_hdmi->has_audio)
enable_bits |= SDVO_AUDIO_ENABLE;
temp = I915_READ(intel_hdmi->sdvox_reg);
/* HW workaround for IBX, we need to move the port to transcoder A
* before disabling it. */
if (HAS_PCH_IBX(dev)) {
struct drm_crtc *crtc = encoder->base.crtc;
int pipe = crtc ? to_intel_crtc(crtc)->pipe : -1; int pipe = crtc ? to_intel_crtc(crtc)->pipe : -1;
if (mode != DRM_MODE_DPMS_ON) {
if (temp & SDVO_PIPE_B_SELECT) { if (temp & SDVO_PIPE_B_SELECT) {
temp &= ~SDVO_PIPE_B_SELECT; temp &= ~SDVO_PIPE_B_SELECT;
I915_WRITE(intel_hdmi->sdvox_reg, temp); I915_WRITE(intel_hdmi->sdvox_reg, temp);
...@@ -637,11 +703,6 @@ static void intel_hdmi_dpms(struct drm_encoder *encoder, int mode) ...@@ -637,11 +703,6 @@ static void intel_hdmi_dpms(struct drm_encoder *encoder, int mode)
else else
msleep(50); msleep(50);
} }
} else {
/* Restore the transcoder select bit. */
if (pipe == PIPE_B)
enable_bits |= SDVO_PIPE_B_SELECT;
}
} }
/* HW workaround, need to toggle enable bit off and on for 12bpc, but /* HW workaround, need to toggle enable bit off and on for 12bpc, but
...@@ -652,11 +713,7 @@ static void intel_hdmi_dpms(struct drm_encoder *encoder, int mode) ...@@ -652,11 +713,7 @@ static void intel_hdmi_dpms(struct drm_encoder *encoder, int mode)
POSTING_READ(intel_hdmi->sdvox_reg); POSTING_READ(intel_hdmi->sdvox_reg);
} }
if (mode != DRM_MODE_DPMS_ON) {
temp &= ~enable_bits; temp &= ~enable_bits;
} else {
temp |= enable_bits;
}
I915_WRITE(intel_hdmi->sdvox_reg, temp); I915_WRITE(intel_hdmi->sdvox_reg, temp);
POSTING_READ(intel_hdmi->sdvox_reg); POSTING_READ(intel_hdmi->sdvox_reg);
...@@ -830,9 +887,8 @@ intel_hdmi_set_property(struct drm_connector *connector, ...@@ -830,9 +887,8 @@ intel_hdmi_set_property(struct drm_connector *connector,
done: done:
if (intel_hdmi->base.base.crtc) { if (intel_hdmi->base.base.crtc) {
struct drm_crtc *crtc = intel_hdmi->base.base.crtc; struct drm_crtc *crtc = intel_hdmi->base.base.crtc;
drm_crtc_helper_set_mode(crtc, &crtc->mode, intel_set_mode(crtc, &crtc->mode,
crtc->x, crtc->y, crtc->x, crtc->y, crtc->fb);
crtc->fb);
} }
return 0; return 0;
...@@ -846,23 +902,19 @@ static void intel_hdmi_destroy(struct drm_connector *connector) ...@@ -846,23 +902,19 @@ static void intel_hdmi_destroy(struct drm_connector *connector)
} }
static const struct drm_encoder_helper_funcs intel_hdmi_helper_funcs_hsw = { static const struct drm_encoder_helper_funcs intel_hdmi_helper_funcs_hsw = {
.dpms = intel_ddi_dpms,
.mode_fixup = intel_hdmi_mode_fixup, .mode_fixup = intel_hdmi_mode_fixup,
.prepare = intel_encoder_prepare,
.mode_set = intel_ddi_mode_set, .mode_set = intel_ddi_mode_set,
.commit = intel_encoder_commit, .disable = intel_encoder_noop,
}; };
static const struct drm_encoder_helper_funcs intel_hdmi_helper_funcs = { static const struct drm_encoder_helper_funcs intel_hdmi_helper_funcs = {
.dpms = intel_hdmi_dpms,
.mode_fixup = intel_hdmi_mode_fixup, .mode_fixup = intel_hdmi_mode_fixup,
.prepare = intel_encoder_prepare,
.mode_set = intel_hdmi_mode_set, .mode_set = intel_hdmi_mode_set,
.commit = intel_encoder_commit, .disable = intel_encoder_noop,
}; };
static const struct drm_connector_funcs intel_hdmi_connector_funcs = { static const struct drm_connector_funcs intel_hdmi_connector_funcs = {
.dpms = drm_helper_connector_dpms, .dpms = intel_connector_dpms,
.detect = intel_hdmi_detect, .detect = intel_hdmi_detect,
.fill_modes = drm_helper_probe_single_connector_modes, .fill_modes = drm_helper_probe_single_connector_modes,
.set_property = intel_hdmi_set_property, .set_property = intel_hdmi_set_property,
...@@ -961,10 +1013,21 @@ void intel_hdmi_init(struct drm_device *dev, int sdvox_reg, enum port port) ...@@ -961,10 +1013,21 @@ void intel_hdmi_init(struct drm_device *dev, int sdvox_reg, enum port port)
intel_hdmi->set_infoframes = cpt_set_infoframes; intel_hdmi->set_infoframes = cpt_set_infoframes;
} }
if (IS_HASWELL(dev)) if (IS_HASWELL(dev)) {
drm_encoder_helper_add(&intel_encoder->base, &intel_hdmi_helper_funcs_hsw); intel_encoder->enable = intel_enable_ddi;
else intel_encoder->disable = intel_disable_ddi;
drm_encoder_helper_add(&intel_encoder->base, &intel_hdmi_helper_funcs); intel_encoder->get_hw_state = intel_ddi_get_hw_state;
drm_encoder_helper_add(&intel_encoder->base,
&intel_hdmi_helper_funcs_hsw);
} else {
intel_encoder->enable = intel_enable_hdmi;
intel_encoder->disable = intel_disable_hdmi;
intel_encoder->get_hw_state = intel_hdmi_get_hw_state;
drm_encoder_helper_add(&intel_encoder->base,
&intel_hdmi_helper_funcs);
}
intel_connector->get_hw_state = intel_connector_get_hw_state;
intel_hdmi_add_properties(intel_hdmi, connector); intel_hdmi_add_properties(intel_hdmi, connector);
......
...@@ -65,13 +65,40 @@ static struct intel_lvds *intel_attached_lvds(struct drm_connector *connector) ...@@ -65,13 +65,40 @@ static struct intel_lvds *intel_attached_lvds(struct drm_connector *connector)
struct intel_lvds, base); struct intel_lvds, base);
} }
static bool intel_lvds_get_hw_state(struct intel_encoder *encoder,
enum pipe *pipe)
{
struct drm_device *dev = encoder->base.dev;
struct drm_i915_private *dev_priv = dev->dev_private;
u32 lvds_reg, tmp;
if (HAS_PCH_SPLIT(dev)) {
lvds_reg = PCH_LVDS;
} else {
lvds_reg = LVDS;
}
tmp = I915_READ(lvds_reg);
if (!(tmp & LVDS_PORT_EN))
return false;
if (HAS_PCH_CPT(dev))
*pipe = PORT_TO_PIPE_CPT(tmp);
else
*pipe = PORT_TO_PIPE(tmp);
return true;
}
/** /**
* Sets the power state for the panel. * Sets the power state for the panel.
*/ */
static void intel_lvds_enable(struct intel_lvds *intel_lvds) static void intel_enable_lvds(struct intel_encoder *encoder)
{ {
struct drm_device *dev = intel_lvds->base.base.dev; struct drm_device *dev = encoder->base.dev;
struct intel_crtc *intel_crtc = to_intel_crtc(intel_lvds->base.base.crtc); struct intel_lvds *intel_lvds = to_intel_lvds(&encoder->base);
struct intel_crtc *intel_crtc = to_intel_crtc(encoder->base.crtc);
struct drm_i915_private *dev_priv = dev->dev_private; struct drm_i915_private *dev_priv = dev->dev_private;
u32 ctl_reg, lvds_reg, stat_reg; u32 ctl_reg, lvds_reg, stat_reg;
...@@ -111,9 +138,10 @@ static void intel_lvds_enable(struct intel_lvds *intel_lvds) ...@@ -111,9 +138,10 @@ static void intel_lvds_enable(struct intel_lvds *intel_lvds)
intel_panel_enable_backlight(dev, intel_crtc->pipe); intel_panel_enable_backlight(dev, intel_crtc->pipe);
} }
static void intel_lvds_disable(struct intel_lvds *intel_lvds) static void intel_disable_lvds(struct intel_encoder *encoder)
{ {
struct drm_device *dev = intel_lvds->base.base.dev; struct drm_device *dev = encoder->base.dev;
struct intel_lvds *intel_lvds = to_intel_lvds(&encoder->base);
struct drm_i915_private *dev_priv = dev->dev_private; struct drm_i915_private *dev_priv = dev->dev_private;
u32 ctl_reg, lvds_reg, stat_reg; u32 ctl_reg, lvds_reg, stat_reg;
...@@ -142,18 +170,6 @@ static void intel_lvds_disable(struct intel_lvds *intel_lvds) ...@@ -142,18 +170,6 @@ static void intel_lvds_disable(struct intel_lvds *intel_lvds)
POSTING_READ(lvds_reg); POSTING_READ(lvds_reg);
} }
static void intel_lvds_dpms(struct drm_encoder *encoder, int mode)
{
struct intel_lvds *intel_lvds = to_intel_lvds(encoder);
if (mode == DRM_MODE_DPMS_ON)
intel_lvds_enable(intel_lvds);
else
intel_lvds_disable(intel_lvds);
/* XXX: We never power down the LVDS pairs. */
}
static int intel_lvds_mode_valid(struct drm_connector *connector, static int intel_lvds_mode_valid(struct drm_connector *connector,
struct drm_display_mode *mode) struct drm_display_mode *mode)
{ {
...@@ -234,9 +250,8 @@ static bool intel_lvds_mode_fixup(struct drm_encoder *encoder, ...@@ -234,9 +250,8 @@ static bool intel_lvds_mode_fixup(struct drm_encoder *encoder,
{ {
struct drm_device *dev = encoder->dev; struct drm_device *dev = encoder->dev;
struct drm_i915_private *dev_priv = dev->dev_private; struct drm_i915_private *dev_priv = dev->dev_private;
struct intel_crtc *intel_crtc = to_intel_crtc(encoder->crtc);
struct intel_lvds *intel_lvds = to_intel_lvds(encoder); struct intel_lvds *intel_lvds = to_intel_lvds(encoder);
struct intel_encoder *tmp_encoder; struct intel_crtc *intel_crtc = intel_lvds->base.new_crtc;
u32 pfit_control = 0, pfit_pgm_ratios = 0, border = 0; u32 pfit_control = 0, pfit_pgm_ratios = 0, border = 0;
int pipe; int pipe;
...@@ -246,14 +261,8 @@ static bool intel_lvds_mode_fixup(struct drm_encoder *encoder, ...@@ -246,14 +261,8 @@ static bool intel_lvds_mode_fixup(struct drm_encoder *encoder,
return false; return false;
} }
/* Should never happen!! */ if (intel_encoder_check_is_cloned(&intel_lvds->base))
for_each_encoder_on_crtc(dev, encoder->crtc, tmp_encoder) {
if (&tmp_encoder->base != encoder) {
DRM_ERROR("Can't enable LVDS and another "
"encoder on the same pipe\n");
return false; return false;
}
}
/* /*
* We have timings from the BIOS for the panel, put them in * We have timings from the BIOS for the panel, put them in
...@@ -405,23 +414,6 @@ static bool intel_lvds_mode_fixup(struct drm_encoder *encoder, ...@@ -405,23 +414,6 @@ static bool intel_lvds_mode_fixup(struct drm_encoder *encoder,
return true; return true;
} }
static void intel_lvds_prepare(struct drm_encoder *encoder)
{
struct intel_lvds *intel_lvds = to_intel_lvds(encoder);
intel_lvds_disable(intel_lvds);
}
static void intel_lvds_commit(struct drm_encoder *encoder)
{
struct intel_lvds *intel_lvds = to_intel_lvds(encoder);
/* Always do a full power on as we do not know what state
* we were left in.
*/
intel_lvds_enable(intel_lvds);
}
static void intel_lvds_mode_set(struct drm_encoder *encoder, static void intel_lvds_mode_set(struct drm_encoder *encoder,
struct drm_display_mode *mode, struct drm_display_mode *mode,
struct drm_display_mode *adjusted_mode) struct drm_display_mode *adjusted_mode)
...@@ -587,7 +579,7 @@ static int intel_lvds_set_property(struct drm_connector *connector, ...@@ -587,7 +579,7 @@ static int intel_lvds_set_property(struct drm_connector *connector,
* If the CRTC is enabled, the display will be changed * If the CRTC is enabled, the display will be changed
* according to the new panel fitting mode. * according to the new panel fitting mode.
*/ */
drm_crtc_helper_set_mode(crtc, &crtc->mode, intel_set_mode(crtc, &crtc->mode,
crtc->x, crtc->y, crtc->fb); crtc->x, crtc->y, crtc->fb);
} }
} }
...@@ -596,11 +588,9 @@ static int intel_lvds_set_property(struct drm_connector *connector, ...@@ -596,11 +588,9 @@ static int intel_lvds_set_property(struct drm_connector *connector,
} }
static const struct drm_encoder_helper_funcs intel_lvds_helper_funcs = { static const struct drm_encoder_helper_funcs intel_lvds_helper_funcs = {
.dpms = intel_lvds_dpms,
.mode_fixup = intel_lvds_mode_fixup, .mode_fixup = intel_lvds_mode_fixup,
.prepare = intel_lvds_prepare,
.mode_set = intel_lvds_mode_set, .mode_set = intel_lvds_mode_set,
.commit = intel_lvds_commit, .disable = intel_encoder_noop,
}; };
static const struct drm_connector_helper_funcs intel_lvds_connector_helper_funcs = { static const struct drm_connector_helper_funcs intel_lvds_connector_helper_funcs = {
...@@ -610,7 +600,7 @@ static const struct drm_connector_helper_funcs intel_lvds_connector_helper_funcs ...@@ -610,7 +600,7 @@ static const struct drm_connector_helper_funcs intel_lvds_connector_helper_funcs
}; };
static const struct drm_connector_funcs intel_lvds_connector_funcs = { static const struct drm_connector_funcs intel_lvds_connector_funcs = {
.dpms = drm_helper_connector_dpms, .dpms = intel_connector_dpms,
.detect = intel_lvds_detect, .detect = intel_lvds_detect,
.fill_modes = drm_helper_probe_single_connector_modes, .fill_modes = drm_helper_probe_single_connector_modes,
.set_property = intel_lvds_set_property, .set_property = intel_lvds_set_property,
...@@ -964,6 +954,11 @@ bool intel_lvds_init(struct drm_device *dev) ...@@ -964,6 +954,11 @@ bool intel_lvds_init(struct drm_device *dev)
drm_encoder_init(dev, &intel_encoder->base, &intel_lvds_enc_funcs, drm_encoder_init(dev, &intel_encoder->base, &intel_lvds_enc_funcs,
DRM_MODE_ENCODER_LVDS); DRM_MODE_ENCODER_LVDS);
intel_encoder->enable = intel_enable_lvds;
intel_encoder->disable = intel_disable_lvds;
intel_encoder->get_hw_state = intel_lvds_get_hw_state;
intel_connector->get_hw_state = intel_connector_get_hw_state;
intel_connector_attach_encoder(intel_connector, intel_encoder); intel_connector_attach_encoder(intel_connector, intel_encoder);
intel_encoder->type = INTEL_OUTPUT_LVDS; intel_encoder->type = INTEL_OUTPUT_LVDS;
......
...@@ -628,6 +628,14 @@ static bool intel_sdvo_set_active_outputs(struct intel_sdvo *intel_sdvo, ...@@ -628,6 +628,14 @@ static bool intel_sdvo_set_active_outputs(struct intel_sdvo *intel_sdvo,
&outputs, sizeof(outputs)); &outputs, sizeof(outputs));
} }
static bool intel_sdvo_get_active_outputs(struct intel_sdvo *intel_sdvo,
u16 *outputs)
{
return intel_sdvo_get_value(intel_sdvo,
SDVO_CMD_GET_ACTIVE_OUTPUTS,
outputs, sizeof(*outputs));
}
static bool intel_sdvo_set_encoder_power_state(struct intel_sdvo *intel_sdvo, static bool intel_sdvo_set_encoder_power_state(struct intel_sdvo *intel_sdvo,
int mode) int mode)
{ {
...@@ -1142,26 +1150,66 @@ static void intel_sdvo_mode_set(struct drm_encoder *encoder, ...@@ -1142,26 +1150,66 @@ static void intel_sdvo_mode_set(struct drm_encoder *encoder,
intel_sdvo_write_sdvox(intel_sdvo, sdvox); intel_sdvo_write_sdvox(intel_sdvo, sdvox);
} }
static void intel_sdvo_dpms(struct drm_encoder *encoder, int mode) static bool intel_sdvo_connector_get_hw_state(struct intel_connector *connector)
{ {
struct drm_device *dev = encoder->dev; struct intel_sdvo_connector *intel_sdvo_connector =
to_intel_sdvo_connector(&connector->base);
struct intel_sdvo *intel_sdvo = intel_attached_sdvo(&connector->base);
u16 active_outputs;
intel_sdvo_get_active_outputs(intel_sdvo, &active_outputs);
if (active_outputs & intel_sdvo_connector->output_flag)
return true;
else
return false;
}
static bool intel_sdvo_get_hw_state(struct intel_encoder *encoder,
enum pipe *pipe)
{
struct drm_device *dev = encoder->base.dev;
struct drm_i915_private *dev_priv = dev->dev_private; struct drm_i915_private *dev_priv = dev->dev_private;
struct intel_sdvo *intel_sdvo = to_intel_sdvo(encoder); struct intel_sdvo *intel_sdvo = to_intel_sdvo(&encoder->base);
struct intel_crtc *intel_crtc = to_intel_crtc(encoder->crtc); u32 tmp;
tmp = I915_READ(intel_sdvo->sdvo_reg);
if (!(tmp & SDVO_ENABLE))
return false;
if (HAS_PCH_CPT(dev))
*pipe = PORT_TO_PIPE_CPT(tmp);
else
*pipe = PORT_TO_PIPE(tmp);
return true;
}
static void intel_disable_sdvo(struct intel_encoder *encoder)
{
struct drm_i915_private *dev_priv = encoder->base.dev->dev_private;
struct intel_sdvo *intel_sdvo = to_intel_sdvo(&encoder->base);
u32 temp; u32 temp;
if (mode != DRM_MODE_DPMS_ON) {
intel_sdvo_set_active_outputs(intel_sdvo, 0); intel_sdvo_set_active_outputs(intel_sdvo, 0);
if (0) if (0)
intel_sdvo_set_encoder_power_state(intel_sdvo, mode); intel_sdvo_set_encoder_power_state(intel_sdvo,
DRM_MODE_DPMS_OFF);
if (mode == DRM_MODE_DPMS_OFF) {
temp = I915_READ(intel_sdvo->sdvo_reg); temp = I915_READ(intel_sdvo->sdvo_reg);
if ((temp & SDVO_ENABLE) != 0) { if ((temp & SDVO_ENABLE) != 0) {
intel_sdvo_write_sdvox(intel_sdvo, temp & ~SDVO_ENABLE); intel_sdvo_write_sdvox(intel_sdvo, temp & ~SDVO_ENABLE);
} }
} }
} else {
static void intel_enable_sdvo(struct intel_encoder *encoder)
{
struct drm_device *dev = encoder->base.dev;
struct drm_i915_private *dev_priv = dev->dev_private;
struct intel_sdvo *intel_sdvo = to_intel_sdvo(&encoder->base);
struct intel_crtc *intel_crtc = to_intel_crtc(encoder->base.crtc);
u32 temp;
bool input1, input2; bool input1, input2;
int i; int i;
u8 status; u8 status;
...@@ -1182,11 +1230,52 @@ static void intel_sdvo_dpms(struct drm_encoder *encoder, int mode) ...@@ -1182,11 +1230,52 @@ static void intel_sdvo_dpms(struct drm_encoder *encoder, int mode)
"sync\n", SDVO_NAME(intel_sdvo)); "sync\n", SDVO_NAME(intel_sdvo));
} }
if (0)
intel_sdvo_set_encoder_power_state(intel_sdvo,
DRM_MODE_DPMS_ON);
intel_sdvo_set_active_outputs(intel_sdvo, intel_sdvo->attached_output);
}
static void intel_sdvo_dpms(struct drm_connector *connector, int mode)
{
struct drm_crtc *crtc;
struct intel_sdvo *intel_sdvo = intel_attached_sdvo(connector);
/* dvo supports only 2 dpms states. */
if (mode != DRM_MODE_DPMS_ON)
mode = DRM_MODE_DPMS_OFF;
if (mode == connector->dpms)
return;
connector->dpms = mode;
/* Only need to change hw state when actually enabled */
crtc = intel_sdvo->base.base.crtc;
if (!crtc) {
intel_sdvo->base.connectors_active = false;
return;
}
if (mode != DRM_MODE_DPMS_ON) {
intel_sdvo_set_active_outputs(intel_sdvo, 0);
if (0)
intel_sdvo_set_encoder_power_state(intel_sdvo, mode);
intel_sdvo->base.connectors_active = false;
intel_crtc_update_dpms(crtc);
} else {
intel_sdvo->base.connectors_active = true;
intel_crtc_update_dpms(crtc);
if (0) if (0)
intel_sdvo_set_encoder_power_state(intel_sdvo, mode); intel_sdvo_set_encoder_power_state(intel_sdvo, mode);
intel_sdvo_set_active_outputs(intel_sdvo, intel_sdvo->attached_output); intel_sdvo_set_active_outputs(intel_sdvo, intel_sdvo->attached_output);
} }
return;
intel_modeset_check_state(connector->dev);
} }
static int intel_sdvo_mode_valid(struct drm_connector *connector, static int intel_sdvo_mode_valid(struct drm_connector *connector,
...@@ -1838,8 +1927,8 @@ intel_sdvo_set_property(struct drm_connector *connector, ...@@ -1838,8 +1927,8 @@ intel_sdvo_set_property(struct drm_connector *connector,
done: done:
if (intel_sdvo->base.base.crtc) { if (intel_sdvo->base.base.crtc) {
struct drm_crtc *crtc = intel_sdvo->base.base.crtc; struct drm_crtc *crtc = intel_sdvo->base.base.crtc;
drm_crtc_helper_set_mode(crtc, &crtc->mode, crtc->x, intel_set_mode(crtc, &crtc->mode,
crtc->y, crtc->fb); crtc->x, crtc->y, crtc->fb);
} }
return 0; return 0;
...@@ -1847,15 +1936,13 @@ intel_sdvo_set_property(struct drm_connector *connector, ...@@ -1847,15 +1936,13 @@ intel_sdvo_set_property(struct drm_connector *connector,
} }
static const struct drm_encoder_helper_funcs intel_sdvo_helper_funcs = { static const struct drm_encoder_helper_funcs intel_sdvo_helper_funcs = {
.dpms = intel_sdvo_dpms,
.mode_fixup = intel_sdvo_mode_fixup, .mode_fixup = intel_sdvo_mode_fixup,
.prepare = intel_encoder_prepare,
.mode_set = intel_sdvo_mode_set, .mode_set = intel_sdvo_mode_set,
.commit = intel_encoder_commit, .disable = intel_encoder_noop,
}; };
static const struct drm_connector_funcs intel_sdvo_connector_funcs = { static const struct drm_connector_funcs intel_sdvo_connector_funcs = {
.dpms = drm_helper_connector_dpms, .dpms = intel_sdvo_dpms,
.detect = intel_sdvo_detect, .detect = intel_sdvo_detect,
.fill_modes = drm_helper_probe_single_connector_modes, .fill_modes = drm_helper_probe_single_connector_modes,
.set_property = intel_sdvo_set_property, .set_property = intel_sdvo_set_property,
...@@ -2027,6 +2114,7 @@ intel_sdvo_connector_init(struct intel_sdvo_connector *connector, ...@@ -2027,6 +2114,7 @@ intel_sdvo_connector_init(struct intel_sdvo_connector *connector,
connector->base.base.interlace_allowed = 1; connector->base.base.interlace_allowed = 1;
connector->base.base.doublescan_allowed = 0; connector->base.base.doublescan_allowed = 0;
connector->base.base.display_info.subpixel_order = SubPixelHorizontalRGB; connector->base.base.display_info.subpixel_order = SubPixelHorizontalRGB;
connector->base.get_hw_state = intel_sdvo_connector_get_hw_state;
intel_connector_attach_encoder(&connector->base, &encoder->base); intel_connector_attach_encoder(&connector->base, &encoder->base);
drm_sysfs_connector_add(&connector->base.base); drm_sysfs_connector_add(&connector->base.base);
...@@ -2578,6 +2666,10 @@ bool intel_sdvo_init(struct drm_device *dev, uint32_t sdvo_reg, bool is_sdvob) ...@@ -2578,6 +2666,10 @@ bool intel_sdvo_init(struct drm_device *dev, uint32_t sdvo_reg, bool is_sdvob)
drm_encoder_helper_add(&intel_encoder->base, &intel_sdvo_helper_funcs); drm_encoder_helper_add(&intel_encoder->base, &intel_sdvo_helper_funcs);
intel_encoder->disable = intel_disable_sdvo;
intel_encoder->enable = intel_enable_sdvo;
intel_encoder->get_hw_state = intel_sdvo_get_hw_state;
/* In default case sdvo lvds is false */ /* In default case sdvo lvds is false */
if (!intel_sdvo_get_capabilities(intel_sdvo, &intel_sdvo->caps)) if (!intel_sdvo_get_capabilities(intel_sdvo, &intel_sdvo->caps))
goto err; goto err;
......
...@@ -836,22 +836,37 @@ static struct intel_tv *intel_attached_tv(struct drm_connector *connector) ...@@ -836,22 +836,37 @@ static struct intel_tv *intel_attached_tv(struct drm_connector *connector)
base); base);
} }
static bool
intel_tv_get_hw_state(struct intel_encoder *encoder, enum pipe *pipe)
{
struct drm_device *dev = encoder->base.dev;
struct drm_i915_private *dev_priv = dev->dev_private;
u32 tmp = I915_READ(TV_CTL);
if (!(tmp & TV_ENC_ENABLE))
return false;
*pipe = PORT_TO_PIPE(tmp);
return true;
}
static void static void
intel_tv_dpms(struct drm_encoder *encoder, int mode) intel_enable_tv(struct intel_encoder *encoder)
{ {
struct drm_device *dev = encoder->dev; struct drm_device *dev = encoder->base.dev;
struct drm_i915_private *dev_priv = dev->dev_private; struct drm_i915_private *dev_priv = dev->dev_private;
switch (mode) {
case DRM_MODE_DPMS_ON:
I915_WRITE(TV_CTL, I915_READ(TV_CTL) | TV_ENC_ENABLE); I915_WRITE(TV_CTL, I915_READ(TV_CTL) | TV_ENC_ENABLE);
break; }
case DRM_MODE_DPMS_STANDBY:
case DRM_MODE_DPMS_SUSPEND: static void
case DRM_MODE_DPMS_OFF: intel_disable_tv(struct intel_encoder *encoder)
{
struct drm_device *dev = encoder->base.dev;
struct drm_i915_private *dev_priv = dev->dev_private;
I915_WRITE(TV_CTL, I915_READ(TV_CTL) & ~TV_ENC_ENABLE); I915_WRITE(TV_CTL, I915_READ(TV_CTL) & ~TV_ENC_ENABLE);
break;
}
} }
static const struct tv_mode * static const struct tv_mode *
...@@ -895,16 +910,13 @@ intel_tv_mode_fixup(struct drm_encoder *encoder, ...@@ -895,16 +910,13 @@ intel_tv_mode_fixup(struct drm_encoder *encoder,
const struct drm_display_mode *mode, const struct drm_display_mode *mode,
struct drm_display_mode *adjusted_mode) struct drm_display_mode *adjusted_mode)
{ {
struct drm_device *dev = encoder->dev;
struct intel_tv *intel_tv = enc_to_intel_tv(encoder); struct intel_tv *intel_tv = enc_to_intel_tv(encoder);
const struct tv_mode *tv_mode = intel_tv_mode_find(intel_tv); const struct tv_mode *tv_mode = intel_tv_mode_find(intel_tv);
struct intel_encoder *other_encoder;
if (!tv_mode) if (!tv_mode)
return false; return false;
for_each_encoder_on_crtc(dev, encoder->crtc, other_encoder) if (intel_encoder_check_is_cloned(&intel_tv->base))
if (&other_encoder->base != encoder)
return false; return false;
adjusted_mode->clock = tv_mode->clock; adjusted_mode->clock = tv_mode->clock;
...@@ -1471,22 +1483,20 @@ intel_tv_set_property(struct drm_connector *connector, struct drm_property *prop ...@@ -1471,22 +1483,20 @@ intel_tv_set_property(struct drm_connector *connector, struct drm_property *prop
} }
if (changed && crtc) if (changed && crtc)
drm_crtc_helper_set_mode(crtc, &crtc->mode, crtc->x, intel_set_mode(crtc, &crtc->mode,
crtc->y, crtc->fb); crtc->x, crtc->y, crtc->fb);
out: out:
return ret; return ret;
} }
static const struct drm_encoder_helper_funcs intel_tv_helper_funcs = { static const struct drm_encoder_helper_funcs intel_tv_helper_funcs = {
.dpms = intel_tv_dpms,
.mode_fixup = intel_tv_mode_fixup, .mode_fixup = intel_tv_mode_fixup,
.prepare = intel_encoder_prepare,
.mode_set = intel_tv_mode_set, .mode_set = intel_tv_mode_set,
.commit = intel_encoder_commit, .disable = intel_encoder_noop,
}; };
static const struct drm_connector_funcs intel_tv_connector_funcs = { static const struct drm_connector_funcs intel_tv_connector_funcs = {
.dpms = drm_helper_connector_dpms, .dpms = intel_connector_dpms,
.detect = intel_tv_detect, .detect = intel_tv_detect,
.destroy = intel_tv_destroy, .destroy = intel_tv_destroy,
.set_property = intel_tv_set_property, .set_property = intel_tv_set_property,
...@@ -1616,6 +1626,11 @@ intel_tv_init(struct drm_device *dev) ...@@ -1616,6 +1626,11 @@ intel_tv_init(struct drm_device *dev)
drm_encoder_init(dev, &intel_encoder->base, &intel_tv_enc_funcs, drm_encoder_init(dev, &intel_encoder->base, &intel_tv_enc_funcs,
DRM_MODE_ENCODER_TVDAC); DRM_MODE_ENCODER_TVDAC);
intel_encoder->enable = intel_enable_tv;
intel_encoder->disable = intel_disable_tv;
intel_encoder->get_hw_state = intel_tv_get_hw_state;
intel_connector->get_hw_state = intel_connector_get_hw_state;
intel_connector_attach_encoder(intel_connector, intel_encoder); intel_connector_attach_encoder(intel_connector, intel_encoder);
intel_encoder->type = INTEL_OUTPUT_TVOUT; intel_encoder->type = INTEL_OUTPUT_TVOUT;
intel_encoder->crtc_mask = (1 << 0) | (1 << 1); intel_encoder->crtc_mask = (1 << 0) | (1 << 1);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment