[PATCH weston v5 36/42] compositor-drm: Move DPMS into output state

Daniel Stone daniels at collabora.com
Wed Nov 16 14:25:28 UTC 2016


Extend drm_output_state to also cover DPMS, so we can use it to enable
and disable outputs, and always keep a coherent state.

Signed-off-by: Daniel Stone <daniels at collabora.com>

Differential Revision: https://phabricator.freedesktop.org/D1501
---
 libweston/compositor-drm.c | 142 ++++++++++++++++++++++++++++++++++++---------
 1 file changed, 116 insertions(+), 26 deletions(-)

diff --git a/libweston/compositor-drm.c b/libweston/compositor-drm.c
index a368792..d8b28ac 100644
--- a/libweston/compositor-drm.c
+++ b/libweston/compositor-drm.c
@@ -227,6 +227,7 @@ struct drm_edid {
 struct drm_output_state {
 	struct drm_output *output;
 
+	enum dpms_enum dpms;
 	struct wl_list plane_list;
 };
 
@@ -297,12 +298,11 @@ struct drm_output {
 	drmModePropertyPtr dpms_prop;
 	uint32_t gbm_format;
 
-	enum dpms_enum dpms;
-
 	int vblank_pending;
 	int page_flip_pending;
 	int destroy_pending;
 	int disable_pending;
+	int dpms_off_pending;
 
 	struct gbm_surface *gbm_surface;
 	struct drm_fb *gbm_cursor_fb[2];
@@ -1086,6 +1086,7 @@ drm_output_state_alloc(struct drm_output *output)
 	struct drm_output_state *state = calloc(1, sizeof(*state));
 
 	state->output = output;
+	state->dpms = WESTON_DPMS_OFF;
 	wl_list_init(&state->plane_list);
 
 	return state;
@@ -1137,6 +1138,23 @@ drm_output_state_free(struct drm_output_state *state)
 	free(state);
 }
 
+static struct drm_output_state *
+drm_output_get_disable_state(struct drm_output *output)
+{
+	struct drm_output_state *state;
+
+	if (!output->state_cur)
+		state = drm_output_state_alloc(output);
+	else
+		state = drm_output_state_duplicate(output->state_cur,
+						   DRM_OUTPUT_STATE_CLEAR_PLANES);
+	state->dpms = WESTON_DPMS_OFF;
+
+	return state;
+}
+
+static int drm_output_apply_state(struct drm_output_state *state);
+
 /**
  * Mark a drm_output_state (the output's last state) as complete. This handles
  * any post-completion actions such as updating the repaint timer, disabling the
@@ -1147,6 +1165,7 @@ drm_output_update_complete(struct drm_output *output, uint32_t flags,
 			   unsigned int sec, unsigned int usec)
 {
 	struct drm_plane_state *ps;
+	struct drm_output_state *os;
 	struct timespec ts;
 
 	wl_list_for_each(ps, &output->state_cur->plane_list, link)
@@ -1162,6 +1181,9 @@ drm_output_update_complete(struct drm_output *output, uint32_t flags,
 	else if (output->disable_pending) {
 		weston_output_disable(&output->base);
 		goto out;
+	} else if (output->dpms_off_pending) {
+		os = drm_output_get_disable_state(output);
+		drm_output_apply_state(os);
 	}
 
 	ts.tv_sec = sec;
@@ -1176,6 +1198,7 @@ drm_output_update_complete(struct drm_output *output, uint32_t flags,
 out:
 	output->destroy_pending = 0;
 	output->disable_pending = 0;
+	output->dpms_off_pending = 0;
 }
 
 /**
@@ -1222,7 +1245,6 @@ drm_output_assign_state(struct drm_output_state *state,
 	}
 }
 
-
 static int
 drm_view_transform_supported(struct weston_view *ev)
 {
@@ -1509,7 +1531,41 @@ drm_output_apply_state(struct drm_output_state *state)
 	struct drm_mode *mode;
 	int ret = 0;
 
-	scanout_state = drm_output_state_get_plane(state, scanout_plane);
+	if (state->dpms != WESTON_DPMS_ON) {
+		wl_list_for_each(ps, &state->plane_list, link) {
+			p = ps->plane;
+			assert(ps->fb == NULL);
+			assert(ps->output == NULL);
+
+			if (p->type != WDRM_PLANE_TYPE_OVERLAY)
+				continue;
+
+			ret = drmModeSetPlane(backend->drm.fd, p->plane_id,
+					      0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
+			if (ret)
+				weston_log("drmModeSetPlane failed disable: %m\n");
+		}
+
+		if (output->cursor_plane) {
+			ret = drmModeSetCursor(backend->drm.fd, output->crtc_id,
+					       0, 0, 0);
+			if (ret)
+				weston_log("drmModeSetCursor failed disable: %m\n");
+		}
+
+		ret = drmModeSetCrtc(backend->drm.fd, output->crtc_id, 0, 0, 0,
+				     &output->connector_id, 0, NULL);
+		if (ret)
+			weston_log("drmModeSetCrtc failed disabling: %m\n");
+
+		drm_output_assign_state(state,
+					DRM_OUTPUT_STATE_UPDATE_SYNCHRONOUS);
+
+		return 0;
+	}
+
+	scanout_state =
+		drm_output_state_get_existing_plane(state, scanout_plane);
 
 	/* The legacy SetCrtc API doesn't allow us to do scaling, and the
 	 * legacy PageFlip API doesn't allow us to do clipping either. */
@@ -1534,7 +1590,6 @@ drm_output_apply_state(struct drm_output_state *state)
 			weston_log("set mode failed: %m\n");
 			goto err;
 		}
-		output->base.set_dpms(&output->base, WESTON_DPMS_ON);
 	}
 
 	if (drmModePageFlip(backend->drm.fd, output->crtc_id,
@@ -1596,6 +1651,18 @@ drm_output_apply_state(struct drm_output_state *state)
 		}
 	}
 
+	if (output->dpms_prop &&
+	    output->state_cur->dpms != output->state_pending->dpms) {
+		ret = drmModeConnectorSetProperty(backend->drm.fd,
+						  output->connector_id,
+						  output->dpms_prop->prop_id,
+						  output->state_pending->dpms);
+		if (ret) {
+			weston_log("DRM: DPMS: failed property set for %s\n",
+				   output->base.name);
+		}
+	}
+
 	drm_output_assign_state(output->state_pending,
 				DRM_OUTPUT_STATE_UPDATE_ASYNCHRONOUS);
 
@@ -1624,6 +1691,7 @@ drm_output_repaint(struct weston_output *output_base,
 		output->state_pending =
 			drm_output_state_duplicate(output->state_cur,
 						   DRM_OUTPUT_STATE_CLEAR_PLANES);
+	output->state_pending->dpms = WESTON_DPMS_ON;
 
 	drm_output_render(output, damage);
 	scanout_state = drm_output_state_get_plane(output->state_pending,
@@ -1751,6 +1819,9 @@ drm_output_update_msc(struct drm_output *output, unsigned int seq)
 }
 
 static void
+drm_output_destroy(struct weston_output *base);
+
+static void
 vblank_handler(int fd, unsigned int frame, unsigned int sec, unsigned int usec,
 	       void *data)
 {
@@ -1773,9 +1844,6 @@ vblank_handler(int fd, unsigned int frame, unsigned int sec, unsigned int usec,
 }
 
 static void
-drm_output_destroy(struct weston_output *base);
-
-static void
 page_flip_handler(int fd, unsigned int frame,
 		  unsigned int sec, unsigned int usec, void *data)
 {
@@ -2727,8 +2795,6 @@ drm_output_find_special_plane(struct drm_backend *b, struct drm_output *output,
 static void
 drm_plane_destroy(struct drm_plane *plane)
 {
-	drmModeSetPlane(plane->backend->drm.fd, plane->plane_id, 0, 0, 0,
-			0, 0, 0, 0, 0, 0, 0, 0);
 	drm_plane_state_free(plane->state_cur, true);
 	weston_plane_release(&plane->base);
 	wl_list_remove(&plane->link);
@@ -2921,23 +2987,44 @@ drm_get_prop(int fd, drmModeConnectorPtr connector, const char *name)
 static void
 drm_set_dpms(struct weston_output *output_base, enum dpms_enum level)
 {
-	struct drm_output *output = to_drm_output(output_base);
-	struct weston_compositor *ec = output_base->compositor;
-	struct drm_backend *b = to_drm_backend(ec);
+	struct drm_output *output =
+		container_of(output_base, struct drm_output, base);
 	int ret;
 
-	if (!output->dpms_prop)
+
+	/* As we throw everything away when disabling, just send us back through
+	 * a repaint cycle. */
+	if (level == WESTON_DPMS_ON) {
+		if (output->dpms_off_pending) {
+			output->dpms_off_pending = 0;
+			assert(!output->state_cur ||
+				output->state_cur->dpms != level);
+		}
+
+		if (output->state_cur && output->state_cur->dpms == level)
+			return;
+
+		weston_log("asked for DPMS on; scheduling repaint\n");
+		weston_output_schedule_repaint(output_base);
 		return;
+	}
 
-	ret = drmModeConnectorSetProperty(b->drm.fd, output->connector_id,
-				 	  output->dpms_prop->prop_id, level);
-	if (ret) {
-		weston_log("DRM: DPMS: failed property set for %s\n",
-			   output->base.name);
+	if (!output->state_cur || output->state_cur->dpms == WESTON_DPMS_OFF)
+		return;
+
+	/* If we've already got a request in the pipeline (common when we've
+	 * just finished a repaint cycle, and it's the animation destroy
+ 	 * callback which is asking us to disable), then we need to park our
+	 * DPMS request until that request has quiesced. */
+	if (output->state_last) {
+		output->dpms_off_pending = 1;
 		return;
 	}
 
-	output->dpms = level;
+	output->state_pending = drm_output_get_disable_state(output);
+	ret = drm_output_apply_state(output->state_pending);
+	if (ret != 0)
+		weston_log("drm_set_dpms: couldn't disable output?\n");
 }
 
 static const char * const connector_type_names[] = {
@@ -3745,7 +3832,7 @@ static int
 drm_output_disable(struct weston_output *base)
 {
 	struct drm_output *output = to_drm_output(base);
-	struct drm_backend *b = to_drm_backend(base->compositor);
+	int ret;
 
 	if (output->page_flip_pending || output->vblank_pending) {
 		output->disable_pending = 1;
@@ -3757,11 +3844,12 @@ drm_output_disable(struct weston_output *base)
 
 	output->disable_pending = 0;
 
-	weston_log("Disabling output %s\n", output->base.name);
-	drmModeSetCrtc(b->drm.fd, output->crtc_id,
-		       0, 0, 0, 0, 0, NULL);
-	drm_output_state_free(output->state_cur);
-	output->state_cur = NULL;
+	output->state_pending = drm_output_get_disable_state(output);
+	ret = drm_output_apply_state(output->state_pending);
+	if (ret) {
+		weston_log("Couldn't disable output output %s\n",
+			   output->base.name);
+	}
 
 	return 0;
 }
@@ -4055,6 +4143,8 @@ session_notify(struct wl_listener *listener, void *data)
 					output->crtc_id, 0, 0,
 					0, 0, 0, 0, 0, 0, 0, 0);
 		}
+
+		/* XXX: invalidate state_cur everywhere */
 	}
 }
 
-- 
2.9.3



More information about the wayland-devel mailing list