qxl: hook monitors_config updates into crtc, not encoder.

The encoder callbacks are only called in case the video mode changes.
So any layout changes without mode changes will go unnoticed.

Add qxl_crtc_update_monitors_config(), based on the old
qxl_write_monitors_config_for_encoder() function.  Hook it into the
enable, disable and flush atomic crtc callbacks.  Remove monitors_config
updates from all other places.

Fixes: https://bugzilla.redhat.com/show_bug.cgi?id=1544322
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
Reviewed-by: Dave Airlie <airlied@redhat.com>
Link: http://patchwork.freedesktop.org/patch/msgid/20180420071904.24276-4-kraxel@redhat.com
This commit is contained in:
Gerd Hoffmann 2018-04-20 09:19:03 +02:00
parent 998010bfae
commit a6d3c4d798
2 changed files with 66 additions and 92 deletions

View File

@ -371,6 +371,7 @@ void qxl_io_flush_surfaces(struct qxl_device *qdev)
void qxl_io_destroy_primary(struct qxl_device *qdev)
{
wait_for_io_cmd(qdev, 0, QXL_IO_DESTROY_PRIMARY_ASYNC);
qdev->primary_created = false;
}
void qxl_io_create_primary(struct qxl_device *qdev,
@ -396,6 +397,7 @@ void qxl_io_create_primary(struct qxl_device *qdev,
create->type = QXL_SURF_TYPE_PRIMARY;
wait_for_io_cmd(qdev, 0, QXL_IO_CREATE_PRIMARY_ASYNC);
qdev->primary_created = true;
}
void qxl_io_memslot_add(struct qxl_device *qdev, uint8_t id)

View File

@ -281,6 +281,66 @@ static void qxl_send_monitors_config(struct qxl_device *qdev)
qxl_io_monitors_config(qdev);
}
static void qxl_crtc_update_monitors_config(struct drm_crtc *crtc,
const char *reason)
{
struct drm_device *dev = crtc->dev;
struct qxl_device *qdev = dev->dev_private;
struct qxl_crtc *qcrtc = to_qxl_crtc(crtc);
struct qxl_head head;
int oldcount, i = qcrtc->index;
if (!qdev->primary_created) {
DRM_DEBUG_KMS("no primary surface, skip (%s)\n", reason);
return;
}
if (!qdev->monitors_config ||
qdev->monitors_config->max_allowed <= i)
return;
head.id = i;
head.flags = 0;
oldcount = qdev->monitors_config->count;
if (crtc->state->active) {
struct drm_display_mode *mode = &crtc->mode;
head.width = mode->hdisplay;
head.height = mode->vdisplay;
head.x = crtc->x;
head.y = crtc->y;
if (qdev->monitors_config->count < i + 1)
qdev->monitors_config->count = i + 1;
} else if (i > 0) {
head.width = 0;
head.height = 0;
head.x = 0;
head.y = 0;
if (qdev->monitors_config->count == i + 1)
qdev->monitors_config->count = i;
} else {
DRM_DEBUG_KMS("inactive head 0, skip (%s)\n", reason);
return;
}
if (head.width == qdev->monitors_config->heads[i].width &&
head.height == qdev->monitors_config->heads[i].height &&
head.x == qdev->monitors_config->heads[i].x &&
head.y == qdev->monitors_config->heads[i].y &&
oldcount == qdev->monitors_config->count)
return;
DRM_DEBUG_KMS("head %d, %dx%d, at +%d+%d, %s (%s)\n",
i, head.width, head.height, head.x, head.y,
crtc->state->active ? "on" : "off", reason);
if (oldcount != qdev->monitors_config->count)
DRM_DEBUG_KMS("active heads %d -> %d (%d total)\n",
oldcount, qdev->monitors_config->count,
qdev->monitors_config->max_allowed);
qdev->monitors_config->heads[i] = head;
qxl_send_monitors_config(qdev);
}
static void qxl_crtc_atomic_flush(struct drm_crtc *crtc,
struct drm_crtc_state *old_crtc_state)
{
@ -296,6 +356,8 @@ static void qxl_crtc_atomic_flush(struct drm_crtc *crtc,
drm_crtc_send_vblank_event(crtc, event);
spin_unlock_irqrestore(&dev->event_lock, flags);
}
qxl_crtc_update_monitors_config(crtc, "flush");
}
static void qxl_crtc_destroy(struct drm_crtc *crtc)
@ -401,55 +463,20 @@ static bool qxl_crtc_mode_fixup(struct drm_crtc *crtc,
return true;
}
static void qxl_monitors_config_set(struct qxl_device *qdev,
int index,
unsigned x, unsigned y,
unsigned width, unsigned height,
unsigned surf_id)
{
DRM_DEBUG_KMS("%d:%dx%d+%d+%d\n", index, width, height, x, y);
qdev->monitors_config->heads[index].x = x;
qdev->monitors_config->heads[index].y = y;
qdev->monitors_config->heads[index].width = width;
qdev->monitors_config->heads[index].height = height;
qdev->monitors_config->heads[index].surface_id = surf_id;
}
static void qxl_mode_set_nofb(struct drm_crtc *crtc)
{
struct qxl_device *qdev = crtc->dev->dev_private;
struct qxl_crtc *qcrtc = to_qxl_crtc(crtc);
struct drm_display_mode *mode = &crtc->mode;
DRM_DEBUG("Mode set (%d,%d)\n",
mode->hdisplay, mode->vdisplay);
qxl_monitors_config_set(qdev, qcrtc->index, 0, 0,
mode->hdisplay, mode->vdisplay, 0);
}
static void qxl_crtc_atomic_enable(struct drm_crtc *crtc,
struct drm_crtc_state *old_state)
{
DRM_DEBUG("\n");
qxl_crtc_update_monitors_config(crtc, "enable");
}
static void qxl_crtc_atomic_disable(struct drm_crtc *crtc,
struct drm_crtc_state *old_state)
{
struct qxl_crtc *qcrtc = to_qxl_crtc(crtc);
struct qxl_device *qdev = crtc->dev->dev_private;
qxl_monitors_config_set(qdev, qcrtc->index, 0, 0, 0, 0, 0);
qxl_send_monitors_config(qdev);
qxl_crtc_update_monitors_config(crtc, "disable");
}
static const struct drm_crtc_helper_funcs qxl_crtc_helper_funcs = {
.mode_fixup = qxl_crtc_mode_fixup,
.mode_set_nofb = qxl_mode_set_nofb,
.atomic_flush = qxl_crtc_atomic_flush,
.atomic_enable = qxl_crtc_atomic_enable,
.atomic_disable = qxl_crtc_atomic_disable,
@ -939,61 +966,8 @@ static void qxl_enc_prepare(struct drm_encoder *encoder)
DRM_DEBUG("\n");
}
static void qxl_write_monitors_config_for_encoder(struct qxl_device *qdev,
struct drm_encoder *encoder)
{
int i;
struct qxl_output *output = drm_encoder_to_qxl_output(encoder);
struct qxl_head *head;
struct drm_display_mode *mode;
BUG_ON(!encoder);
/* TODO: ugly, do better */
i = output->index;
if (!qdev->monitors_config ||
qdev->monitors_config->max_allowed <= i) {
DRM_ERROR(
"head number too large or missing monitors config: %p, %d",
qdev->monitors_config,
qdev->monitors_config ?
qdev->monitors_config->max_allowed : -1);
return;
}
if (!encoder->crtc) {
DRM_ERROR("missing crtc on encoder %p\n", encoder);
return;
}
if (i != 0)
DRM_DEBUG("missing for multiple monitors: no head holes\n");
head = &qdev->monitors_config->heads[i];
head->id = i;
if (encoder->crtc->enabled) {
mode = &encoder->crtc->mode;
head->width = mode->hdisplay;
head->height = mode->vdisplay;
head->x = encoder->crtc->x;
head->y = encoder->crtc->y;
if (qdev->monitors_config->count < i + 1)
qdev->monitors_config->count = i + 1;
} else {
head->width = 0;
head->height = 0;
head->x = 0;
head->y = 0;
}
DRM_DEBUG_KMS("setting head %d to +%d+%d %dx%d out of %d\n",
i, head->x, head->y, head->width, head->height, qdev->monitors_config->count);
head->flags = 0;
/* TODO - somewhere else to call this for multiple monitors
* (config_commit?) */
qxl_send_monitors_config(qdev);
}
static void qxl_enc_commit(struct drm_encoder *encoder)
{
struct qxl_device *qdev = encoder->dev->dev_private;
qxl_write_monitors_config_for_encoder(qdev, encoder);
DRM_DEBUG("\n");
}
@ -1080,8 +1054,6 @@ static enum drm_connector_status qxl_conn_detect(
qxl_head_enabled(&qdev->client_monitors_config->heads[output->index]);
DRM_DEBUG("#%d connected: %d\n", output->index, connected);
if (!connected)
qxl_monitors_config_set(qdev, output->index, 0, 0, 0, 0, 0);
return connected ? connector_status_connected
: connector_status_disconnected;