CHROMIUM: drm/exynos: Always DPMS Off in encoder prepare and train dp link in DPMS On
authorDaniel Kurtz <djkurtz@chromium.org>
Wed, 10 Apr 2013 17:42:06 +0000 (01:42 +0800)
committerChromeBot <chrome-bot@google.com>
Fri, 12 Apr 2013 18:14:18 +0000 (11:14 -0700)
In drm_crtc_helper_set_mode(), the basic sequence for encoders and crtcs
is:
->prepare()
->mode_set()
->commit()

For other drm drivers, .prepare does dpms(Off), and .commit does dpms(On).
Thus, the intention is for the .mode_set() callback is supposed to operate properly
in the dpms(Off) state.

For some reason, exynos was not doing dpms(Off) in .prepare, but it was
doing the dpms(On) in .commit, before calling an explicit
panel_ops->commit().

However, the dpms(On) in encoder .commit() never actually does anything,
since the .dpms routines call power on/off routines that are smart and
silently ignore requests that don't change the power state.
In particular, this dpms(On) wasn't able to actually commit changes,
such as enabling video and training the DP link.  Instead, we were
essentially relying on a explicit call to panel_ops->commit() in
encoder->commit() to do the commit.

Also, this means that when exynos_drm_encoder_mode_set calls
panel_ops->set_mode(), the current dpms/power state is whatever it was
before the call to drm_crtc_helper_set_mode().  This is almost always
DPMS(On).

DP link training uses the AUX channel which is only available when
DPMS(On).  A recent change to the dp driver to move link training to the
dp panel_ops->mode_set() seemed to work because, as noted above, we are
almost always in DPMS(On) for DP when we do mode_sets.

There is, however, one exception.  It is possible for userspace to explicit
turn DPMS(Off) the DP pipe.  To save power, for instance.  If this is
followed by a suspend/resume cycle, the resume path, will end up
calling DP mode_set() with dpms(Off).  This causes a soft lockup since the
AUX channel is not enabled when we try to do link training.

Instead, we just always do DPMS(Off) in .prepare(), get rid of the
explicit DP mode_set(), and let the encoder .commit call our DPMS(On)
which will train the link.

Signed-off-by: Daniel Kurtz <djkurtz@chromium.org>
BUG=chromium:189108
TEST=(1) On daisy:
  set_short_powerd_timeouts ; echo 1 > /var/lib/power_manager/disable_idle_suspend
  wait ~20 seconds for powerd to turn off the LCD
  ./run_remote_tests.sh --board=daisy --remote=$IP power_Resume/control$
TEST=(2) Same as (1) but with HDMI monitor attached too
  => In this case powerd will switch to "presentation mode", so the turn
   LCD off time is ~45 seconds (and it takes two timeouts).
TEST=(3) suspend_stress_test   should survive even after LCD powers off

Change-Id: I3cd09d5b9555a9e05efc5a66478ab86f2216fc6b
Reviewed-on: https://gerrit.chromium.org/gerrit/47753
Reviewed-by: Daniel Kurtz <djkurtz@chromium.org>
Tested-by: Daniel Kurtz <djkurtz@chromium.org>
Commit-Queue: Daniel Kurtz <djkurtz@chromium.org>

drivers/gpu/drm/exynos/exynos_dp_core.c
drivers/gpu/drm/exynos/exynos_drm_encoder.c

index 1e85a5b..1cf5289 100644 (file)
@@ -1022,12 +1022,6 @@ static int exynos_dp_power_on(struct exynos_dp_device *dp)
        if (dp->irq < 0 && !exynos_dp_detect_hpd(dp))
                schedule_work(&dp->hotplug_work);
 
-       /*
-        * These calls are required to make sure we train the dp link when dpms
-        * off/on is called from userspace. In the boot and resume cases, the
-        * link training is handled via the modeset, but unfortunately modeset
-        * isn't being called in the dpms off/on case.
-        */
        exynos_dp_train_link(dp);
        exynos_dp_commit(dp);
 
@@ -1096,19 +1090,11 @@ static int exynos_dp_subdrv_probe(void *ctx, struct drm_device *drm_dev)
        return 0;
 }
 
-static void exynos_dp_mode_set(void *ctx, struct drm_display_mode *mode)
-{
-       struct exynos_dp_device *dp = ctx;
-
-       exynos_dp_train_link(dp);
-}
-
 static struct exynos_panel_ops dp_panel_ops = {
        .subdrv_probe = exynos_dp_subdrv_probe,
        .is_connected = exynos_dp_is_connected,
        .check_timing = exynos_dp_check_timing,
        .dpms = exynos_dp_dpms,
-       .mode_set = exynos_dp_mode_set,
        .commit = exynos_dp_commit,
 };
 
index f6bf5b1..ff70ef7 100644 (file)
@@ -110,7 +110,7 @@ static void exynos_drm_encoder_prepare(struct drm_encoder *encoder)
        DRM_DEBUG_KMS("[ENCODER:%d:%s]\n", DRM_BASE_ID(encoder),
                        drm_get_encoder_name(encoder));
 
-       /* drm framework doesn't check NULL. */
+       exynos_drm_encoder_dpms(encoder, DRM_MODE_DPMS_OFF);
 }
 
 static void exynos_drm_encoder_commit(struct drm_encoder *encoder)