Merge branch 'drm-next' of git://people.freedesktop.org/~airlied/linux
[linux-2.6-microblaze.git] / drivers / gpu / drm / i915 / intel_crt.c
index c8f1c0d..893f301 100644 (file)
@@ -46,6 +46,7 @@
 struct intel_crt {
        struct intel_encoder base;
        bool force_hotplug_required;
+       u32 adpa_reg;
 };
 
 static struct intel_crt *intel_attached_crt(struct drm_connector *connector)
@@ -54,42 +55,68 @@ static struct intel_crt *intel_attached_crt(struct drm_connector *connector)
                            struct intel_crt, base);
 }
 
-static void pch_crt_dpms(struct drm_encoder *encoder, int mode)
+static struct intel_crt *intel_encoder_to_crt(struct intel_encoder *encoder)
 {
-       struct drm_device *dev = encoder->dev;
+       return container_of(encoder, struct intel_crt, base);
+}
+
+static bool intel_crt_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_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;
 
-       temp = I915_READ(PCH_ADPA);
+       temp = I915_READ(crt->adpa_reg);
+       temp &= ~(ADPA_HSYNC_CNTL_DISABLE | ADPA_VSYNC_CNTL_DISABLE);
        temp &= ~ADPA_DAC_ENABLE;
+       I915_WRITE(crt->adpa_reg, temp);
+}
 
-       switch (mode) {
-       case DRM_MODE_DPMS_ON:
-               temp |= ADPA_DAC_ENABLE;
-               break;
-       case DRM_MODE_DPMS_STANDBY:
-       case DRM_MODE_DPMS_SUSPEND:
-       case DRM_MODE_DPMS_OFF:
-               /* Just leave port enable cleared */
-               break;
-       }
+static void intel_enable_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;
 
-       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 intel_crt *crt = intel_encoder_to_crt(encoder);
        u32 temp;
 
-       temp = I915_READ(ADPA);
+       temp = I915_READ(crt->adpa_reg);
        temp &= ~(ADPA_HSYNC_CNTL_DISABLE | ADPA_VSYNC_CNTL_DISABLE);
        temp &= ~ADPA_DAC_ENABLE;
 
-       if (IS_VALLEYVIEW(dev) && mode != DRM_MODE_DPMS_ON)
-               mode = DRM_MODE_DPMS_OFF;
-
        switch (mode) {
        case DRM_MODE_DPMS_ON:
                temp |= ADPA_DAC_ENABLE;
@@ -105,7 +132,51 @@ static void gmch_crt_dpms(struct drm_encoder *encoder, int mode)
                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,
@@ -144,19 +215,15 @@ static void intel_crt_mode_set(struct drm_encoder *encoder,
 
        struct drm_device *dev = encoder->dev;
        struct drm_crtc *crtc = encoder->crtc;
+       struct intel_crt *crt =
+               intel_encoder_to_crt(to_intel_encoder(encoder));
        struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
        struct drm_i915_private *dev_priv = dev->dev_private;
        int dpll_md_reg;
        u32 adpa, dpll_md;
-       u32 adpa_reg;
 
        dpll_md_reg = DPLL_MD(intel_crtc->pipe);
 
-       if (HAS_PCH_SPLIT(dev))
-               adpa_reg = PCH_ADPA;
-       else
-               adpa_reg = ADPA;
-
        /*
         * Disable separate mode multiplier used when cloning SDVO to CRT
         * XXX this needs to be adjusted when we really are cloning
@@ -184,7 +251,7 @@ static void intel_crt_mode_set(struct drm_encoder *encoder,
        if (!HAS_PCH_SPLIT(dev))
                I915_WRITE(BCLRPAT(intel_crtc->pipe), 0);
 
-       I915_WRITE(adpa_reg, adpa);
+       I915_WRITE(crt->adpa_reg, adpa);
 }
 
 static bool intel_ironlake_crt_detect_hotplug(struct drm_connector *connector)
@@ -544,14 +611,12 @@ intel_crt_detect(struct drm_connector *connector, bool force)
                return connector->status;
 
        /* for pre-945g platforms use load detect */
-       if (intel_get_load_detect_pipe(&crt->base, connector, NULL,
-                                      &tmp)) {
+       if (intel_get_load_detect_pipe(connector, NULL, &tmp)) {
                if (intel_crt_detect_ddc(connector))
                        status = connector_status_connected;
                else
                        status = intel_crt_load_detect(crt);
-               intel_release_load_detect_pipe(&crt->base, connector,
-                                              &tmp);
+               intel_release_load_detect_pipe(connector, &tmp);
        } else
                status = connector_status_unknown;
 
@@ -602,25 +667,15 @@ static void intel_crt_reset(struct drm_connector *connector)
  * 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,
-       .prepare = intel_encoder_prepare,
-       .commit = intel_encoder_commit,
-       .mode_set = intel_crt_mode_set,
-       .dpms = gmch_crt_dpms,
+       .disable = intel_encoder_noop,
 };
 
 static const struct drm_connector_funcs intel_crt_connector_funcs = {
        .reset = intel_crt_reset,
-       .dpms = drm_helper_connector_dpms,
+       .dpms = intel_crt_dpms,
        .detect = intel_crt_detect,
        .fill_modes = drm_helper_probe_single_connector_modes,
        .destroy = intel_crt_destroy,
@@ -661,7 +716,6 @@ void intel_crt_init(struct drm_device *dev)
        struct intel_crt *crt;
        struct intel_connector *intel_connector;
        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 */
        if (dmi_check_system(intel_no_crt))
@@ -687,13 +741,11 @@ void intel_crt_init(struct drm_device *dev)
        intel_connector_attach_encoder(intel_connector, &crt->base);
 
        crt->base.type = INTEL_OUTPUT_ANALOG;
-       crt->base.clone_mask = (1 << INTEL_SDVO_NON_TV_CLONE_BIT |
-                               1 << INTEL_ANALOG_CLONE_BIT |
-                               1 << INTEL_SDVO_LVDS_CLONE_BIT);
+       crt->base.cloneable = true;
        if (IS_HASWELL(dev))
                crt->base.crtc_mask = (1 << 0);
        else
-               crt->base.crtc_mask = (1 << 0) | (1 << 1);
+               crt->base.crtc_mask = (1 << 0) | (1 << 1) | (1 << 2);
 
        if (IS_GEN2(dev))
                connector->interlace_allowed = 0;
@@ -702,11 +754,18 @@ void intel_crt_init(struct drm_device *dev)
        connector->doublescan_allowed = 0;
 
        if (HAS_PCH_SPLIT(dev))
-               encoder_helper_funcs = &pch_encoder_funcs;
+               crt->adpa_reg = PCH_ADPA;
+       else if (IS_VALLEYVIEW(dev))
+               crt->adpa_reg = VLV_ADPA;
        else
-               encoder_helper_funcs = &gmch_encoder_funcs;
+               crt->adpa_reg = ADPA;
+
+       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, encoder_helper_funcs);
+       drm_encoder_helper_add(&crt->base.base, &crt_encoder_funcs);
        drm_connector_helper_add(connector, &intel_crt_connector_helper_funcs);
 
        drm_sysfs_connector_add(connector);