backend-drm: delay mode switches until the last commit is completed
Changing the mode will destoy the GBM surface for the output. As a result all corresponding BOs are deleted regardless of the drm_fb refcount. While a commit is pending, the last_state may contain a reference to such a BO. So delay the mode switch until the commit is finished and the reference is release. Signed-off-by: Michael Olbrich <m.olbrich@pengutronix.de>
This commit is contained in:
committed by
Daniel Stone
parent
a2684005b6
commit
6275a0fb32
@@ -545,6 +545,7 @@ struct drm_output {
|
|||||||
bool destroy_pending;
|
bool destroy_pending;
|
||||||
bool disable_pending;
|
bool disable_pending;
|
||||||
bool dpms_off_pending;
|
bool dpms_off_pending;
|
||||||
|
bool mode_switch_pending;
|
||||||
|
|
||||||
uint32_t gbm_cursor_handle[2];
|
uint32_t gbm_cursor_handle[2];
|
||||||
struct drm_fb *gbm_cursor_fb[2];
|
struct drm_fb *gbm_cursor_fb[2];
|
||||||
|
|||||||
@@ -259,6 +259,8 @@ drm_output_get_disable_state(struct drm_pending_state *pending_state,
|
|||||||
return output_state;
|
return output_state;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
drm_output_apply_mode(struct drm_output *output);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Mark a drm_output_state (the output's last state) as complete. This handles
|
* Mark a drm_output_state (the output's last state) as complete. This handles
|
||||||
@@ -287,18 +289,24 @@ drm_output_update_complete(struct drm_output *output, uint32_t flags,
|
|||||||
output->destroy_pending = false;
|
output->destroy_pending = false;
|
||||||
output->disable_pending = false;
|
output->disable_pending = false;
|
||||||
output->dpms_off_pending = false;
|
output->dpms_off_pending = false;
|
||||||
|
output->mode_switch_pending = false;
|
||||||
drm_output_destroy(&output->base);
|
drm_output_destroy(&output->base);
|
||||||
return;
|
return;
|
||||||
} else if (output->disable_pending) {
|
} else if (output->disable_pending) {
|
||||||
output->disable_pending = false;
|
output->disable_pending = false;
|
||||||
output->dpms_off_pending = false;
|
output->dpms_off_pending = false;
|
||||||
|
output->mode_switch_pending = false;
|
||||||
weston_output_disable(&output->base);
|
weston_output_disable(&output->base);
|
||||||
return;
|
return;
|
||||||
} else if (output->dpms_off_pending) {
|
} else if (output->dpms_off_pending) {
|
||||||
struct drm_pending_state *pending = drm_pending_state_alloc(device);
|
struct drm_pending_state *pending = drm_pending_state_alloc(device);
|
||||||
output->dpms_off_pending = false;
|
output->dpms_off_pending = false;
|
||||||
|
output->mode_switch_pending = false;
|
||||||
drm_output_get_disable_state(pending, output);
|
drm_output_get_disable_state(pending, output);
|
||||||
drm_pending_state_apply_sync(pending);
|
drm_pending_state_apply_sync(pending);
|
||||||
|
} else if (output->mode_switch_pending) {
|
||||||
|
output->mode_switch_pending = false;
|
||||||
|
drm_output_apply_mode(output);
|
||||||
}
|
}
|
||||||
if (output->state_cur->dpms == WESTON_DPMS_OFF &&
|
if (output->state_cur->dpms == WESTON_DPMS_OFF &&
|
||||||
output->base.repaint_status != REPAINT_AWAITING_COMPLETION) {
|
output->base.repaint_status != REPAINT_AWAITING_COMPLETION) {
|
||||||
@@ -687,8 +695,6 @@ static int
|
|||||||
drm_output_switch_mode(struct weston_output *output_base, struct weston_mode *mode)
|
drm_output_switch_mode(struct weston_output *output_base, struct weston_mode *mode)
|
||||||
{
|
{
|
||||||
struct drm_output *output = to_drm_output(output_base);
|
struct drm_output *output = to_drm_output(output_base);
|
||||||
struct drm_device *device = output->device;
|
|
||||||
struct drm_backend *b = device->backend;
|
|
||||||
struct drm_mode *drm_mode;
|
struct drm_mode *drm_mode;
|
||||||
|
|
||||||
assert(output);
|
assert(output);
|
||||||
@@ -709,6 +715,20 @@ drm_output_switch_mode(struct weston_output *output_base, struct weston_mode *mo
|
|||||||
output->base.current_mode->flags =
|
output->base.current_mode->flags =
|
||||||
WL_OUTPUT_MODE_CURRENT | WL_OUTPUT_MODE_PREFERRED;
|
WL_OUTPUT_MODE_CURRENT | WL_OUTPUT_MODE_PREFERRED;
|
||||||
|
|
||||||
|
if (output->page_flip_pending || output->atomic_complete_pending) {
|
||||||
|
output->mode_switch_pending = true;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return drm_output_apply_mode(output);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
drm_output_apply_mode(struct drm_output *output)
|
||||||
|
{
|
||||||
|
struct drm_device *device = output->device;
|
||||||
|
struct drm_backend *b = device->backend;
|
||||||
|
|
||||||
/* XXX: This drops our current buffer too early, before we've started
|
/* XXX: This drops our current buffer too early, before we've started
|
||||||
* displaying it. Ideally this should be much more atomic and
|
* displaying it. Ideally this should be much more atomic and
|
||||||
* integrated with a full repaint cycle, rather than doing a
|
* integrated with a full repaint cycle, rather than doing a
|
||||||
|
|||||||
Reference in New Issue
Block a user