summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLeo Chen <yinchiuan@google.com>2022-12-09 09:13:21 +0000
committerTreehugger Robot <android-test-infra-autosubmit@system.gserviceaccount.com>2023-07-06 00:07:08 +0000
commit4090ff7a7f4519c1692a1a519d6f77dcc7aec9dd (patch)
treef04729217356a1b5ad9cede59cf534f972646a70
parent93a49544de49790195c69d78250f047667b348c6 (diff)
downloaddisplay-4090ff7a7f4519c1692a1a519d6f77dcc7aec9dd.tar.gz
drm: samsung: rewrite the disable_outputs
This patch rewrite the disable outputs for touch. Now adopting the following sequence. Touch should be disabled before decon. 1. Disable all bridges (include touch) 2. Disable crtcs (decon) 3. Disable encoders (dsim) 4. post disable on all bridges Bug: 255678834 Test: Single tap to wake up Change-Id: I53d5b8d1906e80c98cbec1c09100b9a040700048 Signed-off-by: Leo Chen <yinchiuan@google.com>
-rw-r--r--samsung/exynos_drm_crtc.c83
-rw-r--r--samsung/exynos_drm_crtc.h6
-rw-r--r--samsung/exynos_drm_fb.c169
3 files changed, 244 insertions, 14 deletions
diff --git a/samsung/exynos_drm_crtc.c b/samsung/exynos_drm_crtc.c
index e8ad5dc..2726dfa 100644
--- a/samsung/exynos_drm_crtc.c
+++ b/samsung/exynos_drm_crtc.c
@@ -15,6 +15,7 @@
#include <drm/drm_atomic.h>
#include <drm/drm_atomic_helper.h>
+#include <drm/drm_bridge.h>
#include <drm/drm_encoder.h>
#include <drm/drm_color_mgmt.h>
#include <drm/drm_crtc_helper.h>
@@ -1050,3 +1051,85 @@ void exynos_crtc_wait_for_flip_done(struct drm_atomic_state *old_state)
old_crtc_state, new_crtc_state);
}
}
+
+bool exynos_crtc_needs_disable(struct drm_crtc_state *old_state,
+ struct drm_crtc_state *new_state)
+{
+ /*
+ * No new_state means the CRTC is off, so the only criteria is whether
+ * it's currently active or in self refresh mode.
+ */
+ if (!new_state)
+ return drm_atomic_crtc_effectively_active(old_state);
+
+ /*
+ * We need to run through the crtc_funcs->disable() function if the CRTC
+ * is currently on, if it's transitioning to self refresh mode, or if
+ * it's in self refresh mode and needs to be fully disabled.
+ */
+ return old_state->active ||
+ (old_state->self_refresh_active && !new_state->enable) ||
+ new_state->self_refresh_active;
+}
+
+void exynos_crtc_set_mode(struct drm_device *dev,
+ struct drm_atomic_state *old_state)
+{
+ struct drm_crtc *crtc;
+ struct drm_crtc_state *new_crtc_state;
+ struct drm_connector *connector;
+ struct drm_connector_state *new_conn_state;
+ int i;
+
+ for_each_new_crtc_in_state(old_state, crtc, new_crtc_state, i) {
+ const struct drm_crtc_helper_funcs *funcs;
+
+ if (!new_crtc_state->mode_changed)
+ continue;
+
+ funcs = crtc->helper_private;
+
+ if (new_crtc_state->enable && funcs->mode_set_nofb) {
+ DRM_DEBUG_ATOMIC("modeset on [CRTC:%u:%s]\n",
+ crtc->base.id, crtc->name);
+
+ funcs->mode_set_nofb(crtc);
+ }
+ }
+
+ for_each_new_connector_in_state(old_state, connector, new_conn_state, i) {
+ const struct drm_encoder_helper_funcs *funcs;
+ struct drm_encoder *encoder;
+ struct drm_display_mode *mode, *adjusted_mode;
+ struct drm_bridge *bridge;
+
+ if (!new_conn_state->best_encoder)
+ continue;
+
+ encoder = new_conn_state->best_encoder;
+ funcs = encoder->helper_private;
+ new_crtc_state = new_conn_state->crtc->state;
+ mode = &new_crtc_state->mode;
+ adjusted_mode = &new_crtc_state->adjusted_mode;
+
+ if (!new_crtc_state->mode_changed)
+ continue;
+
+ DRM_DEBUG_ATOMIC("modeset on [ENCODER:%u:%s]\n",
+ encoder->base.id, encoder->name);
+
+ /*
+ * Each encoder has at most one connector (since we always steal
+ * it away), so we won't call mode_set hooks twice.
+ */
+ if (funcs && funcs->atomic_mode_set) {
+ funcs->atomic_mode_set(encoder, new_crtc_state,
+ new_conn_state);
+ } else if (funcs && funcs->mode_set) {
+ funcs->mode_set(encoder, mode, adjusted_mode);
+ }
+
+ bridge = drm_bridge_chain_get_first_bridge(encoder);
+ drm_bridge_chain_mode_set(bridge, mode, adjusted_mode);
+ }
+}
diff --git a/samsung/exynos_drm_crtc.h b/samsung/exynos_drm_crtc.h
index 7d13e92..42d4100 100644
--- a/samsung/exynos_drm_crtc.h
+++ b/samsung/exynos_drm_crtc.h
@@ -59,4 +59,10 @@ void exynos_drm_crtc_te_handler(struct drm_crtc *crtc);
void exynos_crtc_handle_event(struct exynos_drm_crtc *exynos_crtc);
void exynos_crtc_wait_for_flip_done(struct drm_atomic_state *old_state);
+
+bool exynos_crtc_needs_disable(struct drm_crtc_state *old_state,
+ struct drm_crtc_state *new_state);
+
+void exynos_crtc_set_mode(struct drm_device *dev,
+ struct drm_atomic_state *old_state);
#endif
diff --git a/samsung/exynos_drm_fb.c b/samsung/exynos_drm_fb.c
index 8b5398e..a6007d9 100644
--- a/samsung/exynos_drm_fb.c
+++ b/samsung/exynos_drm_fb.c
@@ -15,6 +15,7 @@
#include <drm/drm_atomic.h>
#include <drm/drm_atomic_helper.h>
+#include <drm/drm_bridge.h>
#include <drm/drm_crtc.h>
#include <drm/drm_crtc_helper.h>
#include <drm/drm_fb_helper.h>
@@ -545,20 +546,64 @@ static void exynos_rmem_free(struct decon_device *decon)
decon->fb_handover.phys_size = 0;
}
-
-static void exynos_atomic_commit_tail(struct drm_atomic_state *old_state)
+static void
+exynos_disable_outputs(struct drm_device *dev, struct drm_atomic_state *old_state,
+ unsigned int *disabling_crtc_mask)
{
- int i;
- struct drm_device *dev = old_state->dev;
struct decon_device *decon;
+ struct drm_connector *connector;
+ struct drm_connector_state *old_conn_state, *new_conn_state;
struct drm_crtc *crtc;
struct drm_crtc_state *old_crtc_state, *new_crtc_state;
- struct drm_connector *connector;
- struct drm_connector_state *old_conn_state;
- struct drm_connector_state *new_conn_state;
- unsigned int disabling_crtc_mask = 0;
+ int i;
- DPU_ATRACE_BEGIN("exynos_atomic_commit_tail");
+ for_each_oldnew_connector_in_state(old_state, connector, old_conn_state,
+ new_conn_state, i) {
+ struct drm_encoder *encoder;
+ struct drm_bridge *bridge;
+
+ /* Shut down everything that's in the changeset and currently
+ * still on. So need to check the old, saved state.
+ */
+ if (!old_conn_state->crtc)
+ continue;
+
+ old_crtc_state = drm_atomic_get_old_crtc_state(old_state, old_conn_state->crtc);
+
+ if (new_conn_state->crtc)
+ new_crtc_state = drm_atomic_get_new_crtc_state(
+ old_state,
+ new_conn_state->crtc);
+ else
+ new_crtc_state = NULL;
+
+ /* TODO(b/237120310): needs cleanup after we identify mode_changed handling */
+ if (old_crtc_state->self_refresh_active && new_crtc_state &&
+ new_crtc_state->mode_changed)
+ old_crtc_state->active = true;
+
+ if (!exynos_crtc_needs_disable(old_crtc_state, new_crtc_state) ||
+ !drm_atomic_crtc_needs_modeset(old_conn_state->crtc->state))
+ continue;
+
+ encoder = old_conn_state->best_encoder;
+
+ /* We shouldn't get this far if we didn't previously have
+ * an encoder.. but WARN_ON() rather than explode.
+ */
+ if (WARN_ON(!encoder))
+ continue;
+
+ DRM_DEBUG_ATOMIC("disabling bridge chain [ENCODER:%u:%s]\n",
+ encoder->base.id, encoder->name);
+
+ /*
+ * Each encoder has at most one connector (since we always steal
+ * it away), so we won't call disable hooks twice.
+ */
+ bridge = drm_bridge_chain_get_first_bridge(encoder);
+ drm_atomic_bridge_chain_disable(bridge, old_state);
+ }
for_each_oldnew_crtc_in_state(old_state, crtc, old_crtc_state,
new_crtc_state, i) {
@@ -588,7 +633,7 @@ static void exynos_atomic_commit_tail(struct drm_atomic_state *old_state)
if (drm_atomic_crtc_effectively_active(old_crtc_state) && !new_crtc_state->active) {
/* keep runtime vote while disabling is taking place */
pm_runtime_get_sync(decon->dev);
- disabling_crtc_mask |= drm_crtc_mask(crtc);
+ *disabling_crtc_mask |= drm_crtc_mask(crtc);
}
if (old_crtc_state->active && drm_atomic_crtc_needs_modeset(new_crtc_state)) {
@@ -606,13 +651,109 @@ static void exynos_atomic_commit_tail(struct drm_atomic_state *old_state)
DPU_ATRACE_END("crtc_disable");
}
- /* TODO(b/237120310): needs cleanup after we identify mode_changed handling */
- if (old_crtc_state->self_refresh_active && new_crtc_state->mode_changed)
- old_crtc_state->active = true;
}
+ for_each_oldnew_connector_in_state(old_state, connector, old_conn_state,
+ new_conn_state, i) {
+ const struct drm_encoder_helper_funcs *funcs;
+ struct drm_encoder *encoder;
+ struct drm_bridge *bridge;
+
+ /* Shut down everything that's in the changeset and currently
+ * still on. So need to check the old, saved state.
+ */
+ if (!old_conn_state->crtc)
+ continue;
+
+ old_crtc_state = drm_atomic_get_old_crtc_state(old_state, old_conn_state->crtc);
+
+ if (new_conn_state->crtc)
+ new_crtc_state = drm_atomic_get_new_crtc_state(
+ old_state,
+ new_conn_state->crtc);
+ else
+ new_crtc_state = NULL;
+
+ if (!exynos_crtc_needs_disable(old_crtc_state, new_crtc_state) ||
+ !drm_atomic_crtc_needs_modeset(old_conn_state->crtc->state))
+ continue;
+
+ encoder = old_conn_state->best_encoder;
+
+ /* We shouldn't get this far if we didn't previously have
+ * an encoder.. but WARN_ON() rather than explode.
+ */
+ if (WARN_ON(!encoder))
+ continue;
+
+ funcs = encoder->helper_private;
+
+ DRM_DEBUG_ATOMIC("disabling [ENCODER:%u:%s]\n",
+ encoder->base.id, encoder->name);
+
+ /*
+ * Each encoder has at most one connector (since we always steal
+ * it away), so we won't call disable hooks twice.
+ */
+ bridge = drm_bridge_chain_get_first_bridge(encoder);
+
+ /* Right function depends upon target state. */
+ if (funcs) {
+ if (funcs->atomic_disable)
+ funcs->atomic_disable(encoder, old_state);
+ else if (new_conn_state->crtc && funcs->prepare)
+ funcs->prepare(encoder);
+ else if (funcs->disable)
+ funcs->disable(encoder);
+ else if (funcs->dpms)
+ funcs->dpms(encoder, DRM_MODE_DPMS_OFF);
+ }
+
+ drm_atomic_bridge_chain_post_disable(bridge, old_state);
+ }
+}
+
+/**
+ * exynos_drm_atomic_helper_commit_modeset_disables - modeset commit to disable outputs
+ * @dev: DRM device
+ * @old_state: atomic state object with old state structures
+ * @disabling_crtc_mask: the disabling crtc mask
+ *
+ * This function shuts down all the outputs that need to be shut down and
+ * prepares them (if required) with the new mode.
+ *
+ * For compatibility with legacy CRTC helpers this should be called before
+ * drm_atomic_helper_commit_planes(), which is what the default commit function
+ * does. But drivers with different needs can group the modeset commits together
+ * and do the plane commits at the end. This is useful for drivers doing runtime
+ * PM since planes updates then only happen when the CRTC is actually enabled.
+ */
+static void exynos_drm_atomic_helper_commit_modeset_disables(struct drm_device *dev,
+ struct drm_atomic_state *old_state, unsigned int *disabling_crtc_mask)
+{
+ exynos_disable_outputs(dev, old_state, disabling_crtc_mask);
+
+ drm_atomic_helper_update_legacy_modeset_state(dev, old_state);
+ drm_atomic_helper_calc_timestamping_constants(old_state);
+
+ exynos_crtc_set_mode(dev, old_state);
+}
+
+static void exynos_atomic_commit_tail(struct drm_atomic_state *old_state)
+{
+ int i;
+ struct drm_device *dev = old_state->dev;
+ struct decon_device *decon;
+ struct drm_crtc *crtc;
+ struct drm_crtc_state *new_crtc_state;
+ struct drm_connector *connector;
+ struct drm_connector_state *old_conn_state;
+ struct drm_connector_state *new_conn_state;
+ unsigned int disabling_crtc_mask = 0;
+
+ DPU_ATRACE_BEGIN("exynos_atomic_commit_tail");
DPU_ATRACE_BEGIN("modeset");
- drm_atomic_helper_commit_modeset_disables(dev, old_state);
+ exynos_drm_atomic_helper_commit_modeset_disables(dev, old_state, &disabling_crtc_mask);
exynos_atomic_bts_pre_update(dev, old_state);