162306a36Sopenharmony_ci/* 262306a36Sopenharmony_ci * Copyright 2013 Red Hat Inc. 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * Permission is hereby granted, free of charge, to any person obtaining a 562306a36Sopenharmony_ci * copy of this software and associated documentation files (the "Software"), 662306a36Sopenharmony_ci * to deal in the Software without restriction, including without limitation 762306a36Sopenharmony_ci * the rights to use, copy, modify, merge, publish, distribute, sublicense, 862306a36Sopenharmony_ci * and/or sell copies of the Software, and to permit persons to whom the 962306a36Sopenharmony_ci * Software is furnished to do so, subject to the following conditions: 1062306a36Sopenharmony_ci * 1162306a36Sopenharmony_ci * The above copyright notice and this permission notice shall be included in 1262306a36Sopenharmony_ci * all copies or substantial portions of the Software. 1362306a36Sopenharmony_ci * 1462306a36Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 1562306a36Sopenharmony_ci * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 1662306a36Sopenharmony_ci * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 1762306a36Sopenharmony_ci * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR 1862306a36Sopenharmony_ci * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 1962306a36Sopenharmony_ci * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 2062306a36Sopenharmony_ci * OTHER DEALINGS IN THE SOFTWARE. 2162306a36Sopenharmony_ci * 2262306a36Sopenharmony_ci * Authors: Dave Airlie 2362306a36Sopenharmony_ci * Alon Levy 2462306a36Sopenharmony_ci */ 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci#include <linux/crc32.h> 2762306a36Sopenharmony_ci#include <linux/delay.h> 2862306a36Sopenharmony_ci#include <linux/iosys-map.h> 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci#include <drm/drm_drv.h> 3162306a36Sopenharmony_ci#include <drm/drm_atomic.h> 3262306a36Sopenharmony_ci#include <drm/drm_atomic_helper.h> 3362306a36Sopenharmony_ci#include <drm/drm_edid.h> 3462306a36Sopenharmony_ci#include <drm/drm_framebuffer.h> 3562306a36Sopenharmony_ci#include <drm/drm_gem_framebuffer_helper.h> 3662306a36Sopenharmony_ci#include <drm/drm_plane_helper.h> 3762306a36Sopenharmony_ci#include <drm/drm_probe_helper.h> 3862306a36Sopenharmony_ci#include <drm/drm_simple_kms_helper.h> 3962306a36Sopenharmony_ci#include <drm/drm_gem_atomic_helper.h> 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci#include "qxl_drv.h" 4262306a36Sopenharmony_ci#include "qxl_object.h" 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_cistatic bool qxl_head_enabled(struct qxl_head *head) 4562306a36Sopenharmony_ci{ 4662306a36Sopenharmony_ci return head->width && head->height; 4762306a36Sopenharmony_ci} 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_cistatic int qxl_alloc_client_monitors_config(struct qxl_device *qdev, 5062306a36Sopenharmony_ci unsigned int count) 5162306a36Sopenharmony_ci{ 5262306a36Sopenharmony_ci if (qdev->client_monitors_config && 5362306a36Sopenharmony_ci count > qdev->client_monitors_config->count) { 5462306a36Sopenharmony_ci kfree(qdev->client_monitors_config); 5562306a36Sopenharmony_ci qdev->client_monitors_config = NULL; 5662306a36Sopenharmony_ci } 5762306a36Sopenharmony_ci if (!qdev->client_monitors_config) { 5862306a36Sopenharmony_ci qdev->client_monitors_config = kzalloc( 5962306a36Sopenharmony_ci struct_size(qdev->client_monitors_config, 6062306a36Sopenharmony_ci heads, count), GFP_KERNEL); 6162306a36Sopenharmony_ci if (!qdev->client_monitors_config) 6262306a36Sopenharmony_ci return -ENOMEM; 6362306a36Sopenharmony_ci } 6462306a36Sopenharmony_ci qdev->client_monitors_config->count = count; 6562306a36Sopenharmony_ci return 0; 6662306a36Sopenharmony_ci} 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_cienum { 6962306a36Sopenharmony_ci MONITORS_CONFIG_MODIFIED, 7062306a36Sopenharmony_ci MONITORS_CONFIG_UNCHANGED, 7162306a36Sopenharmony_ci MONITORS_CONFIG_BAD_CRC, 7262306a36Sopenharmony_ci MONITORS_CONFIG_ERROR, 7362306a36Sopenharmony_ci}; 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_cistatic int qxl_display_copy_rom_client_monitors_config(struct qxl_device *qdev) 7662306a36Sopenharmony_ci{ 7762306a36Sopenharmony_ci int i; 7862306a36Sopenharmony_ci int num_monitors; 7962306a36Sopenharmony_ci uint32_t crc; 8062306a36Sopenharmony_ci int status = MONITORS_CONFIG_UNCHANGED; 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci num_monitors = qdev->rom->client_monitors_config.count; 8362306a36Sopenharmony_ci crc = crc32(0, (const uint8_t *)&qdev->rom->client_monitors_config, 8462306a36Sopenharmony_ci sizeof(qdev->rom->client_monitors_config)); 8562306a36Sopenharmony_ci if (crc != qdev->rom->client_monitors_config_crc) 8662306a36Sopenharmony_ci return MONITORS_CONFIG_BAD_CRC; 8762306a36Sopenharmony_ci if (!num_monitors) { 8862306a36Sopenharmony_ci DRM_DEBUG_KMS("no client monitors configured\n"); 8962306a36Sopenharmony_ci return status; 9062306a36Sopenharmony_ci } 9162306a36Sopenharmony_ci if (num_monitors > qxl_num_crtc) { 9262306a36Sopenharmony_ci DRM_DEBUG_KMS("client monitors list will be truncated: %d < %d\n", 9362306a36Sopenharmony_ci qxl_num_crtc, num_monitors); 9462306a36Sopenharmony_ci num_monitors = qxl_num_crtc; 9562306a36Sopenharmony_ci } else { 9662306a36Sopenharmony_ci num_monitors = qdev->rom->client_monitors_config.count; 9762306a36Sopenharmony_ci } 9862306a36Sopenharmony_ci if (qdev->client_monitors_config 9962306a36Sopenharmony_ci && (num_monitors != qdev->client_monitors_config->count)) { 10062306a36Sopenharmony_ci status = MONITORS_CONFIG_MODIFIED; 10162306a36Sopenharmony_ci } 10262306a36Sopenharmony_ci if (qxl_alloc_client_monitors_config(qdev, num_monitors)) { 10362306a36Sopenharmony_ci status = MONITORS_CONFIG_ERROR; 10462306a36Sopenharmony_ci return status; 10562306a36Sopenharmony_ci } 10662306a36Sopenharmony_ci /* we copy max from the client but it isn't used */ 10762306a36Sopenharmony_ci qdev->client_monitors_config->max_allowed = qxl_num_crtc; 10862306a36Sopenharmony_ci for (i = 0 ; i < qdev->client_monitors_config->count ; ++i) { 10962306a36Sopenharmony_ci struct qxl_urect *c_rect = 11062306a36Sopenharmony_ci &qdev->rom->client_monitors_config.heads[i]; 11162306a36Sopenharmony_ci struct qxl_head *client_head = 11262306a36Sopenharmony_ci &qdev->client_monitors_config->heads[i]; 11362306a36Sopenharmony_ci if (client_head->x != c_rect->left) { 11462306a36Sopenharmony_ci client_head->x = c_rect->left; 11562306a36Sopenharmony_ci status = MONITORS_CONFIG_MODIFIED; 11662306a36Sopenharmony_ci } 11762306a36Sopenharmony_ci if (client_head->y != c_rect->top) { 11862306a36Sopenharmony_ci client_head->y = c_rect->top; 11962306a36Sopenharmony_ci status = MONITORS_CONFIG_MODIFIED; 12062306a36Sopenharmony_ci } 12162306a36Sopenharmony_ci if (client_head->width != c_rect->right - c_rect->left) { 12262306a36Sopenharmony_ci client_head->width = c_rect->right - c_rect->left; 12362306a36Sopenharmony_ci status = MONITORS_CONFIG_MODIFIED; 12462306a36Sopenharmony_ci } 12562306a36Sopenharmony_ci if (client_head->height != c_rect->bottom - c_rect->top) { 12662306a36Sopenharmony_ci client_head->height = c_rect->bottom - c_rect->top; 12762306a36Sopenharmony_ci status = MONITORS_CONFIG_MODIFIED; 12862306a36Sopenharmony_ci } 12962306a36Sopenharmony_ci if (client_head->surface_id != 0) { 13062306a36Sopenharmony_ci client_head->surface_id = 0; 13162306a36Sopenharmony_ci status = MONITORS_CONFIG_MODIFIED; 13262306a36Sopenharmony_ci } 13362306a36Sopenharmony_ci if (client_head->id != i) { 13462306a36Sopenharmony_ci client_head->id = i; 13562306a36Sopenharmony_ci status = MONITORS_CONFIG_MODIFIED; 13662306a36Sopenharmony_ci } 13762306a36Sopenharmony_ci if (client_head->flags != 0) { 13862306a36Sopenharmony_ci client_head->flags = 0; 13962306a36Sopenharmony_ci status = MONITORS_CONFIG_MODIFIED; 14062306a36Sopenharmony_ci } 14162306a36Sopenharmony_ci DRM_DEBUG_KMS("read %dx%d+%d+%d\n", client_head->width, client_head->height, 14262306a36Sopenharmony_ci client_head->x, client_head->y); 14362306a36Sopenharmony_ci } 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci return status; 14662306a36Sopenharmony_ci} 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_cistatic void qxl_update_offset_props(struct qxl_device *qdev) 14962306a36Sopenharmony_ci{ 15062306a36Sopenharmony_ci struct drm_device *dev = &qdev->ddev; 15162306a36Sopenharmony_ci struct drm_connector *connector; 15262306a36Sopenharmony_ci struct qxl_output *output; 15362306a36Sopenharmony_ci struct qxl_head *head; 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci list_for_each_entry(connector, &dev->mode_config.connector_list, head) { 15662306a36Sopenharmony_ci output = drm_connector_to_qxl_output(connector); 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci head = &qdev->client_monitors_config->heads[output->index]; 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci drm_object_property_set_value(&connector->base, 16162306a36Sopenharmony_ci dev->mode_config.suggested_x_property, head->x); 16262306a36Sopenharmony_ci drm_object_property_set_value(&connector->base, 16362306a36Sopenharmony_ci dev->mode_config.suggested_y_property, head->y); 16462306a36Sopenharmony_ci } 16562306a36Sopenharmony_ci} 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_civoid qxl_display_read_client_monitors_config(struct qxl_device *qdev) 16862306a36Sopenharmony_ci{ 16962306a36Sopenharmony_ci struct drm_device *dev = &qdev->ddev; 17062306a36Sopenharmony_ci struct drm_modeset_acquire_ctx ctx; 17162306a36Sopenharmony_ci int status, retries, ret; 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci for (retries = 0; retries < 10; retries++) { 17462306a36Sopenharmony_ci status = qxl_display_copy_rom_client_monitors_config(qdev); 17562306a36Sopenharmony_ci if (status != MONITORS_CONFIG_BAD_CRC) 17662306a36Sopenharmony_ci break; 17762306a36Sopenharmony_ci udelay(5); 17862306a36Sopenharmony_ci } 17962306a36Sopenharmony_ci if (status == MONITORS_CONFIG_ERROR) { 18062306a36Sopenharmony_ci DRM_DEBUG_KMS("ignoring client monitors config: error"); 18162306a36Sopenharmony_ci return; 18262306a36Sopenharmony_ci } 18362306a36Sopenharmony_ci if (status == MONITORS_CONFIG_BAD_CRC) { 18462306a36Sopenharmony_ci DRM_DEBUG_KMS("ignoring client monitors config: bad crc"); 18562306a36Sopenharmony_ci return; 18662306a36Sopenharmony_ci } 18762306a36Sopenharmony_ci if (status == MONITORS_CONFIG_UNCHANGED) { 18862306a36Sopenharmony_ci DRM_DEBUG_KMS("ignoring client monitors config: unchanged"); 18962306a36Sopenharmony_ci return; 19062306a36Sopenharmony_ci } 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci DRM_MODESET_LOCK_ALL_BEGIN(dev, ctx, DRM_MODESET_ACQUIRE_INTERRUPTIBLE, ret); 19362306a36Sopenharmony_ci qxl_update_offset_props(qdev); 19462306a36Sopenharmony_ci DRM_MODESET_LOCK_ALL_END(dev, ctx, ret); 19562306a36Sopenharmony_ci if (!drm_helper_hpd_irq_event(dev)) { 19662306a36Sopenharmony_ci /* notify that the monitor configuration changed, to 19762306a36Sopenharmony_ci adjust at the arbitrary resolution */ 19862306a36Sopenharmony_ci drm_kms_helper_hotplug_event(dev); 19962306a36Sopenharmony_ci } 20062306a36Sopenharmony_ci} 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_cistatic int qxl_check_mode(struct qxl_device *qdev, 20362306a36Sopenharmony_ci unsigned int width, 20462306a36Sopenharmony_ci unsigned int height) 20562306a36Sopenharmony_ci{ 20662306a36Sopenharmony_ci unsigned int stride; 20762306a36Sopenharmony_ci unsigned int size; 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci if (check_mul_overflow(width, 4u, &stride)) 21062306a36Sopenharmony_ci return -EINVAL; 21162306a36Sopenharmony_ci if (check_mul_overflow(stride, height, &size)) 21262306a36Sopenharmony_ci return -EINVAL; 21362306a36Sopenharmony_ci if (size > qdev->vram_size) 21462306a36Sopenharmony_ci return -ENOMEM; 21562306a36Sopenharmony_ci return 0; 21662306a36Sopenharmony_ci} 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_cistatic int qxl_check_framebuffer(struct qxl_device *qdev, 21962306a36Sopenharmony_ci struct qxl_bo *bo) 22062306a36Sopenharmony_ci{ 22162306a36Sopenharmony_ci return qxl_check_mode(qdev, bo->surf.width, bo->surf.height); 22262306a36Sopenharmony_ci} 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_cistatic int qxl_add_mode(struct drm_connector *connector, 22562306a36Sopenharmony_ci unsigned int width, 22662306a36Sopenharmony_ci unsigned int height, 22762306a36Sopenharmony_ci bool preferred) 22862306a36Sopenharmony_ci{ 22962306a36Sopenharmony_ci struct drm_device *dev = connector->dev; 23062306a36Sopenharmony_ci struct qxl_device *qdev = to_qxl(dev); 23162306a36Sopenharmony_ci struct drm_display_mode *mode = NULL; 23262306a36Sopenharmony_ci int rc; 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci rc = qxl_check_mode(qdev, width, height); 23562306a36Sopenharmony_ci if (rc != 0) 23662306a36Sopenharmony_ci return 0; 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci mode = drm_cvt_mode(dev, width, height, 60, false, false, false); 23962306a36Sopenharmony_ci if (preferred) 24062306a36Sopenharmony_ci mode->type |= DRM_MODE_TYPE_PREFERRED; 24162306a36Sopenharmony_ci mode->hdisplay = width; 24262306a36Sopenharmony_ci mode->vdisplay = height; 24362306a36Sopenharmony_ci drm_mode_set_name(mode); 24462306a36Sopenharmony_ci drm_mode_probed_add(connector, mode); 24562306a36Sopenharmony_ci return 1; 24662306a36Sopenharmony_ci} 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_cistatic int qxl_add_monitors_config_modes(struct drm_connector *connector) 24962306a36Sopenharmony_ci{ 25062306a36Sopenharmony_ci struct drm_device *dev = connector->dev; 25162306a36Sopenharmony_ci struct qxl_device *qdev = to_qxl(dev); 25262306a36Sopenharmony_ci struct qxl_output *output = drm_connector_to_qxl_output(connector); 25362306a36Sopenharmony_ci int h = output->index; 25462306a36Sopenharmony_ci struct qxl_head *head; 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_ci if (!qdev->monitors_config) 25762306a36Sopenharmony_ci return 0; 25862306a36Sopenharmony_ci if (h >= qxl_num_crtc) 25962306a36Sopenharmony_ci return 0; 26062306a36Sopenharmony_ci if (!qdev->client_monitors_config) 26162306a36Sopenharmony_ci return 0; 26262306a36Sopenharmony_ci if (h >= qdev->client_monitors_config->count) 26362306a36Sopenharmony_ci return 0; 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ci head = &qdev->client_monitors_config->heads[h]; 26662306a36Sopenharmony_ci DRM_DEBUG_KMS("head %d is %dx%d\n", h, head->width, head->height); 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_ci return qxl_add_mode(connector, head->width, head->height, true); 26962306a36Sopenharmony_ci} 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_cistatic struct mode_size { 27262306a36Sopenharmony_ci int w; 27362306a36Sopenharmony_ci int h; 27462306a36Sopenharmony_ci} extra_modes[] = { 27562306a36Sopenharmony_ci { 720, 480}, 27662306a36Sopenharmony_ci {1152, 768}, 27762306a36Sopenharmony_ci {1280, 854}, 27862306a36Sopenharmony_ci}; 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_cistatic int qxl_add_extra_modes(struct drm_connector *connector) 28162306a36Sopenharmony_ci{ 28262306a36Sopenharmony_ci int i, ret = 0; 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(extra_modes); i++) 28562306a36Sopenharmony_ci ret += qxl_add_mode(connector, 28662306a36Sopenharmony_ci extra_modes[i].w, 28762306a36Sopenharmony_ci extra_modes[i].h, 28862306a36Sopenharmony_ci false); 28962306a36Sopenharmony_ci return ret; 29062306a36Sopenharmony_ci} 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_cistatic void qxl_send_monitors_config(struct qxl_device *qdev) 29362306a36Sopenharmony_ci{ 29462306a36Sopenharmony_ci int i; 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_ci BUG_ON(!qdev->ram_header->monitors_config); 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_ci if (qdev->monitors_config->count == 0) 29962306a36Sopenharmony_ci return; 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_ci for (i = 0 ; i < qdev->monitors_config->count ; ++i) { 30262306a36Sopenharmony_ci struct qxl_head *head = &qdev->monitors_config->heads[i]; 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_ci if (head->y > 8192 || head->x > 8192 || 30562306a36Sopenharmony_ci head->width > 8192 || head->height > 8192) { 30662306a36Sopenharmony_ci DRM_ERROR("head %d wrong: %dx%d+%d+%d\n", 30762306a36Sopenharmony_ci i, head->width, head->height, 30862306a36Sopenharmony_ci head->x, head->y); 30962306a36Sopenharmony_ci return; 31062306a36Sopenharmony_ci } 31162306a36Sopenharmony_ci } 31262306a36Sopenharmony_ci qxl_io_monitors_config(qdev); 31362306a36Sopenharmony_ci} 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_cistatic void qxl_crtc_update_monitors_config(struct drm_crtc *crtc, 31662306a36Sopenharmony_ci const char *reason) 31762306a36Sopenharmony_ci{ 31862306a36Sopenharmony_ci struct drm_device *dev = crtc->dev; 31962306a36Sopenharmony_ci struct qxl_device *qdev = to_qxl(dev); 32062306a36Sopenharmony_ci struct qxl_crtc *qcrtc = to_qxl_crtc(crtc); 32162306a36Sopenharmony_ci struct qxl_head head; 32262306a36Sopenharmony_ci int oldcount, i = qcrtc->index; 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_ci if (!qdev->primary_bo) { 32562306a36Sopenharmony_ci DRM_DEBUG_KMS("no primary surface, skip (%s)\n", reason); 32662306a36Sopenharmony_ci return; 32762306a36Sopenharmony_ci } 32862306a36Sopenharmony_ci 32962306a36Sopenharmony_ci if (!qdev->monitors_config || qxl_num_crtc <= i) 33062306a36Sopenharmony_ci return; 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_ci head.id = i; 33362306a36Sopenharmony_ci head.flags = 0; 33462306a36Sopenharmony_ci head.surface_id = 0; 33562306a36Sopenharmony_ci oldcount = qdev->monitors_config->count; 33662306a36Sopenharmony_ci if (crtc->state->active) { 33762306a36Sopenharmony_ci struct drm_display_mode *mode = &crtc->mode; 33862306a36Sopenharmony_ci 33962306a36Sopenharmony_ci head.width = mode->hdisplay; 34062306a36Sopenharmony_ci head.height = mode->vdisplay; 34162306a36Sopenharmony_ci head.x = crtc->x; 34262306a36Sopenharmony_ci head.y = crtc->y; 34362306a36Sopenharmony_ci if (qdev->monitors_config->count < i + 1) 34462306a36Sopenharmony_ci qdev->monitors_config->count = i + 1; 34562306a36Sopenharmony_ci if (qdev->primary_bo == qdev->dumb_shadow_bo) 34662306a36Sopenharmony_ci head.x += qdev->dumb_heads[i].x; 34762306a36Sopenharmony_ci } else if (i > 0) { 34862306a36Sopenharmony_ci head.width = 0; 34962306a36Sopenharmony_ci head.height = 0; 35062306a36Sopenharmony_ci head.x = 0; 35162306a36Sopenharmony_ci head.y = 0; 35262306a36Sopenharmony_ci if (qdev->monitors_config->count == i + 1) 35362306a36Sopenharmony_ci qdev->monitors_config->count = i; 35462306a36Sopenharmony_ci } else { 35562306a36Sopenharmony_ci DRM_DEBUG_KMS("inactive head 0, skip (%s)\n", reason); 35662306a36Sopenharmony_ci return; 35762306a36Sopenharmony_ci } 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_ci if (head.width == qdev->monitors_config->heads[i].width && 36062306a36Sopenharmony_ci head.height == qdev->monitors_config->heads[i].height && 36162306a36Sopenharmony_ci head.x == qdev->monitors_config->heads[i].x && 36262306a36Sopenharmony_ci head.y == qdev->monitors_config->heads[i].y && 36362306a36Sopenharmony_ci oldcount == qdev->monitors_config->count) 36462306a36Sopenharmony_ci return; 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_ci DRM_DEBUG_KMS("head %d, %dx%d, at +%d+%d, %s (%s)\n", 36762306a36Sopenharmony_ci i, head.width, head.height, head.x, head.y, 36862306a36Sopenharmony_ci crtc->state->active ? "on" : "off", reason); 36962306a36Sopenharmony_ci if (oldcount != qdev->monitors_config->count) 37062306a36Sopenharmony_ci DRM_DEBUG_KMS("active heads %d -> %d (%d total)\n", 37162306a36Sopenharmony_ci oldcount, qdev->monitors_config->count, 37262306a36Sopenharmony_ci qxl_num_crtc); 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_ci qdev->monitors_config->heads[i] = head; 37562306a36Sopenharmony_ci qdev->monitors_config->max_allowed = qxl_num_crtc; 37662306a36Sopenharmony_ci qxl_send_monitors_config(qdev); 37762306a36Sopenharmony_ci} 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_cistatic void qxl_crtc_atomic_flush(struct drm_crtc *crtc, 38062306a36Sopenharmony_ci struct drm_atomic_state *state) 38162306a36Sopenharmony_ci{ 38262306a36Sopenharmony_ci qxl_crtc_update_monitors_config(crtc, "flush"); 38362306a36Sopenharmony_ci} 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_cistatic void qxl_crtc_destroy(struct drm_crtc *crtc) 38662306a36Sopenharmony_ci{ 38762306a36Sopenharmony_ci struct qxl_crtc *qxl_crtc = to_qxl_crtc(crtc); 38862306a36Sopenharmony_ci 38962306a36Sopenharmony_ci qxl_bo_unref(&qxl_crtc->cursor_bo); 39062306a36Sopenharmony_ci drm_crtc_cleanup(crtc); 39162306a36Sopenharmony_ci kfree(qxl_crtc); 39262306a36Sopenharmony_ci} 39362306a36Sopenharmony_ci 39462306a36Sopenharmony_cistatic const struct drm_crtc_funcs qxl_crtc_funcs = { 39562306a36Sopenharmony_ci .set_config = drm_atomic_helper_set_config, 39662306a36Sopenharmony_ci .destroy = qxl_crtc_destroy, 39762306a36Sopenharmony_ci .page_flip = drm_atomic_helper_page_flip, 39862306a36Sopenharmony_ci .reset = drm_atomic_helper_crtc_reset, 39962306a36Sopenharmony_ci .atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state, 40062306a36Sopenharmony_ci .atomic_destroy_state = drm_atomic_helper_crtc_destroy_state, 40162306a36Sopenharmony_ci}; 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_cistatic int qxl_framebuffer_surface_dirty(struct drm_framebuffer *fb, 40462306a36Sopenharmony_ci struct drm_file *file_priv, 40562306a36Sopenharmony_ci unsigned int flags, unsigned int color, 40662306a36Sopenharmony_ci struct drm_clip_rect *clips, 40762306a36Sopenharmony_ci unsigned int num_clips) 40862306a36Sopenharmony_ci{ 40962306a36Sopenharmony_ci /* TODO: vmwgfx where this was cribbed from had locking. Why? */ 41062306a36Sopenharmony_ci struct qxl_device *qdev = to_qxl(fb->dev); 41162306a36Sopenharmony_ci struct drm_clip_rect norect; 41262306a36Sopenharmony_ci struct qxl_bo *qobj; 41362306a36Sopenharmony_ci struct drm_modeset_acquire_ctx ctx; 41462306a36Sopenharmony_ci bool is_primary; 41562306a36Sopenharmony_ci int inc = 1, ret; 41662306a36Sopenharmony_ci 41762306a36Sopenharmony_ci DRM_MODESET_LOCK_ALL_BEGIN(fb->dev, ctx, DRM_MODESET_ACQUIRE_INTERRUPTIBLE, ret); 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_ci qobj = gem_to_qxl_bo(fb->obj[0]); 42062306a36Sopenharmony_ci /* if we aren't primary surface ignore this */ 42162306a36Sopenharmony_ci is_primary = qobj->shadow ? qobj->shadow->is_primary : qobj->is_primary; 42262306a36Sopenharmony_ci if (!is_primary) 42362306a36Sopenharmony_ci goto out_lock_end; 42462306a36Sopenharmony_ci 42562306a36Sopenharmony_ci if (!num_clips) { 42662306a36Sopenharmony_ci num_clips = 1; 42762306a36Sopenharmony_ci clips = &norect; 42862306a36Sopenharmony_ci norect.x1 = norect.y1 = 0; 42962306a36Sopenharmony_ci norect.x2 = fb->width; 43062306a36Sopenharmony_ci norect.y2 = fb->height; 43162306a36Sopenharmony_ci } else if (flags & DRM_MODE_FB_DIRTY_ANNOTATE_COPY) { 43262306a36Sopenharmony_ci num_clips /= 2; 43362306a36Sopenharmony_ci inc = 2; /* skip source rects */ 43462306a36Sopenharmony_ci } 43562306a36Sopenharmony_ci 43662306a36Sopenharmony_ci qxl_draw_dirty_fb(qdev, fb, qobj, flags, color, 43762306a36Sopenharmony_ci clips, num_clips, inc, 0); 43862306a36Sopenharmony_ci 43962306a36Sopenharmony_ciout_lock_end: 44062306a36Sopenharmony_ci DRM_MODESET_LOCK_ALL_END(fb->dev, ctx, ret); 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_ci return 0; 44362306a36Sopenharmony_ci} 44462306a36Sopenharmony_ci 44562306a36Sopenharmony_cistatic const struct drm_framebuffer_funcs qxl_fb_funcs = { 44662306a36Sopenharmony_ci .destroy = drm_gem_fb_destroy, 44762306a36Sopenharmony_ci .dirty = qxl_framebuffer_surface_dirty, 44862306a36Sopenharmony_ci .create_handle = drm_gem_fb_create_handle, 44962306a36Sopenharmony_ci}; 45062306a36Sopenharmony_ci 45162306a36Sopenharmony_cistatic void qxl_crtc_atomic_enable(struct drm_crtc *crtc, 45262306a36Sopenharmony_ci struct drm_atomic_state *state) 45362306a36Sopenharmony_ci{ 45462306a36Sopenharmony_ci qxl_crtc_update_monitors_config(crtc, "enable"); 45562306a36Sopenharmony_ci} 45662306a36Sopenharmony_ci 45762306a36Sopenharmony_cistatic void qxl_crtc_atomic_disable(struct drm_crtc *crtc, 45862306a36Sopenharmony_ci struct drm_atomic_state *state) 45962306a36Sopenharmony_ci{ 46062306a36Sopenharmony_ci qxl_crtc_update_monitors_config(crtc, "disable"); 46162306a36Sopenharmony_ci} 46262306a36Sopenharmony_ci 46362306a36Sopenharmony_cistatic const struct drm_crtc_helper_funcs qxl_crtc_helper_funcs = { 46462306a36Sopenharmony_ci .atomic_flush = qxl_crtc_atomic_flush, 46562306a36Sopenharmony_ci .atomic_enable = qxl_crtc_atomic_enable, 46662306a36Sopenharmony_ci .atomic_disable = qxl_crtc_atomic_disable, 46762306a36Sopenharmony_ci}; 46862306a36Sopenharmony_ci 46962306a36Sopenharmony_cistatic int qxl_primary_atomic_check(struct drm_plane *plane, 47062306a36Sopenharmony_ci struct drm_atomic_state *state) 47162306a36Sopenharmony_ci{ 47262306a36Sopenharmony_ci struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state, 47362306a36Sopenharmony_ci plane); 47462306a36Sopenharmony_ci struct qxl_device *qdev = to_qxl(plane->dev); 47562306a36Sopenharmony_ci struct qxl_bo *bo; 47662306a36Sopenharmony_ci 47762306a36Sopenharmony_ci if (!new_plane_state->crtc || !new_plane_state->fb) 47862306a36Sopenharmony_ci return 0; 47962306a36Sopenharmony_ci 48062306a36Sopenharmony_ci bo = gem_to_qxl_bo(new_plane_state->fb->obj[0]); 48162306a36Sopenharmony_ci 48262306a36Sopenharmony_ci return qxl_check_framebuffer(qdev, bo); 48362306a36Sopenharmony_ci} 48462306a36Sopenharmony_ci 48562306a36Sopenharmony_cistatic int qxl_primary_apply_cursor(struct qxl_device *qdev, 48662306a36Sopenharmony_ci struct drm_plane_state *plane_state) 48762306a36Sopenharmony_ci{ 48862306a36Sopenharmony_ci struct drm_framebuffer *fb = plane_state->fb; 48962306a36Sopenharmony_ci struct qxl_crtc *qcrtc = to_qxl_crtc(plane_state->crtc); 49062306a36Sopenharmony_ci struct qxl_cursor_cmd *cmd; 49162306a36Sopenharmony_ci struct qxl_release *release; 49262306a36Sopenharmony_ci int ret = 0; 49362306a36Sopenharmony_ci 49462306a36Sopenharmony_ci if (!qcrtc->cursor_bo) 49562306a36Sopenharmony_ci return 0; 49662306a36Sopenharmony_ci 49762306a36Sopenharmony_ci ret = qxl_alloc_release_reserved(qdev, sizeof(*cmd), 49862306a36Sopenharmony_ci QXL_RELEASE_CURSOR_CMD, 49962306a36Sopenharmony_ci &release, NULL); 50062306a36Sopenharmony_ci if (ret) 50162306a36Sopenharmony_ci return ret; 50262306a36Sopenharmony_ci 50362306a36Sopenharmony_ci ret = qxl_release_list_add(release, qcrtc->cursor_bo); 50462306a36Sopenharmony_ci if (ret) 50562306a36Sopenharmony_ci goto out_free_release; 50662306a36Sopenharmony_ci 50762306a36Sopenharmony_ci ret = qxl_release_reserve_list(release, false); 50862306a36Sopenharmony_ci if (ret) 50962306a36Sopenharmony_ci goto out_free_release; 51062306a36Sopenharmony_ci 51162306a36Sopenharmony_ci cmd = (struct qxl_cursor_cmd *)qxl_release_map(qdev, release); 51262306a36Sopenharmony_ci cmd->type = QXL_CURSOR_SET; 51362306a36Sopenharmony_ci cmd->u.set.position.x = plane_state->crtc_x + fb->hot_x; 51462306a36Sopenharmony_ci cmd->u.set.position.y = plane_state->crtc_y + fb->hot_y; 51562306a36Sopenharmony_ci 51662306a36Sopenharmony_ci cmd->u.set.shape = qxl_bo_physical_address(qdev, qcrtc->cursor_bo, 0); 51762306a36Sopenharmony_ci 51862306a36Sopenharmony_ci cmd->u.set.visible = 1; 51962306a36Sopenharmony_ci qxl_release_unmap(qdev, release, &cmd->release_info); 52062306a36Sopenharmony_ci 52162306a36Sopenharmony_ci qxl_release_fence_buffer_objects(release); 52262306a36Sopenharmony_ci qxl_push_cursor_ring_release(qdev, release, QXL_CMD_CURSOR, false); 52362306a36Sopenharmony_ci 52462306a36Sopenharmony_ci return ret; 52562306a36Sopenharmony_ci 52662306a36Sopenharmony_ciout_free_release: 52762306a36Sopenharmony_ci qxl_release_free(qdev, release); 52862306a36Sopenharmony_ci return ret; 52962306a36Sopenharmony_ci} 53062306a36Sopenharmony_ci 53162306a36Sopenharmony_cistatic int qxl_primary_move_cursor(struct qxl_device *qdev, 53262306a36Sopenharmony_ci struct drm_plane_state *plane_state) 53362306a36Sopenharmony_ci{ 53462306a36Sopenharmony_ci struct drm_framebuffer *fb = plane_state->fb; 53562306a36Sopenharmony_ci struct qxl_crtc *qcrtc = to_qxl_crtc(plane_state->crtc); 53662306a36Sopenharmony_ci struct qxl_cursor_cmd *cmd; 53762306a36Sopenharmony_ci struct qxl_release *release; 53862306a36Sopenharmony_ci int ret = 0; 53962306a36Sopenharmony_ci 54062306a36Sopenharmony_ci if (!qcrtc->cursor_bo) 54162306a36Sopenharmony_ci return 0; 54262306a36Sopenharmony_ci 54362306a36Sopenharmony_ci ret = qxl_alloc_release_reserved(qdev, sizeof(*cmd), 54462306a36Sopenharmony_ci QXL_RELEASE_CURSOR_CMD, 54562306a36Sopenharmony_ci &release, NULL); 54662306a36Sopenharmony_ci if (ret) 54762306a36Sopenharmony_ci return ret; 54862306a36Sopenharmony_ci 54962306a36Sopenharmony_ci ret = qxl_release_reserve_list(release, true); 55062306a36Sopenharmony_ci if (ret) { 55162306a36Sopenharmony_ci qxl_release_free(qdev, release); 55262306a36Sopenharmony_ci return ret; 55362306a36Sopenharmony_ci } 55462306a36Sopenharmony_ci 55562306a36Sopenharmony_ci cmd = (struct qxl_cursor_cmd *)qxl_release_map(qdev, release); 55662306a36Sopenharmony_ci cmd->type = QXL_CURSOR_MOVE; 55762306a36Sopenharmony_ci cmd->u.position.x = plane_state->crtc_x + fb->hot_x; 55862306a36Sopenharmony_ci cmd->u.position.y = plane_state->crtc_y + fb->hot_y; 55962306a36Sopenharmony_ci qxl_release_unmap(qdev, release, &cmd->release_info); 56062306a36Sopenharmony_ci 56162306a36Sopenharmony_ci qxl_release_fence_buffer_objects(release); 56262306a36Sopenharmony_ci qxl_push_cursor_ring_release(qdev, release, QXL_CMD_CURSOR, false); 56362306a36Sopenharmony_ci return ret; 56462306a36Sopenharmony_ci} 56562306a36Sopenharmony_ci 56662306a36Sopenharmony_cistatic struct qxl_bo *qxl_create_cursor(struct qxl_device *qdev, 56762306a36Sopenharmony_ci struct qxl_bo *user_bo, 56862306a36Sopenharmony_ci int hot_x, int hot_y) 56962306a36Sopenharmony_ci{ 57062306a36Sopenharmony_ci static const u32 size = 64 * 64 * 4; 57162306a36Sopenharmony_ci struct qxl_bo *cursor_bo; 57262306a36Sopenharmony_ci struct iosys_map cursor_map; 57362306a36Sopenharmony_ci struct iosys_map user_map; 57462306a36Sopenharmony_ci struct qxl_cursor cursor; 57562306a36Sopenharmony_ci int ret; 57662306a36Sopenharmony_ci 57762306a36Sopenharmony_ci if (!user_bo) 57862306a36Sopenharmony_ci return NULL; 57962306a36Sopenharmony_ci 58062306a36Sopenharmony_ci ret = qxl_bo_create(qdev, sizeof(struct qxl_cursor) + size, 58162306a36Sopenharmony_ci false, true, QXL_GEM_DOMAIN_VRAM, 1, 58262306a36Sopenharmony_ci NULL, &cursor_bo); 58362306a36Sopenharmony_ci if (ret) 58462306a36Sopenharmony_ci goto err; 58562306a36Sopenharmony_ci 58662306a36Sopenharmony_ci ret = qxl_bo_vmap(cursor_bo, &cursor_map); 58762306a36Sopenharmony_ci if (ret) 58862306a36Sopenharmony_ci goto err_unref; 58962306a36Sopenharmony_ci 59062306a36Sopenharmony_ci ret = qxl_bo_vmap(user_bo, &user_map); 59162306a36Sopenharmony_ci if (ret) 59262306a36Sopenharmony_ci goto err_unmap; 59362306a36Sopenharmony_ci 59462306a36Sopenharmony_ci cursor.header.unique = 0; 59562306a36Sopenharmony_ci cursor.header.type = SPICE_CURSOR_TYPE_ALPHA; 59662306a36Sopenharmony_ci cursor.header.width = 64; 59762306a36Sopenharmony_ci cursor.header.height = 64; 59862306a36Sopenharmony_ci cursor.header.hot_spot_x = hot_x; 59962306a36Sopenharmony_ci cursor.header.hot_spot_y = hot_y; 60062306a36Sopenharmony_ci cursor.data_size = size; 60162306a36Sopenharmony_ci cursor.chunk.next_chunk = 0; 60262306a36Sopenharmony_ci cursor.chunk.prev_chunk = 0; 60362306a36Sopenharmony_ci cursor.chunk.data_size = size; 60462306a36Sopenharmony_ci if (cursor_map.is_iomem) { 60562306a36Sopenharmony_ci memcpy_toio(cursor_map.vaddr_iomem, 60662306a36Sopenharmony_ci &cursor, sizeof(cursor)); 60762306a36Sopenharmony_ci memcpy_toio(cursor_map.vaddr_iomem + sizeof(cursor), 60862306a36Sopenharmony_ci user_map.vaddr, size); 60962306a36Sopenharmony_ci } else { 61062306a36Sopenharmony_ci memcpy(cursor_map.vaddr, 61162306a36Sopenharmony_ci &cursor, sizeof(cursor)); 61262306a36Sopenharmony_ci memcpy(cursor_map.vaddr + sizeof(cursor), 61362306a36Sopenharmony_ci user_map.vaddr, size); 61462306a36Sopenharmony_ci } 61562306a36Sopenharmony_ci 61662306a36Sopenharmony_ci qxl_bo_vunmap(user_bo); 61762306a36Sopenharmony_ci qxl_bo_vunmap(cursor_bo); 61862306a36Sopenharmony_ci return cursor_bo; 61962306a36Sopenharmony_ci 62062306a36Sopenharmony_cierr_unmap: 62162306a36Sopenharmony_ci qxl_bo_vunmap(cursor_bo); 62262306a36Sopenharmony_cierr_unref: 62362306a36Sopenharmony_ci qxl_bo_unpin(cursor_bo); 62462306a36Sopenharmony_ci qxl_bo_unref(&cursor_bo); 62562306a36Sopenharmony_cierr: 62662306a36Sopenharmony_ci return NULL; 62762306a36Sopenharmony_ci} 62862306a36Sopenharmony_ci 62962306a36Sopenharmony_cistatic void qxl_free_cursor(struct qxl_bo *cursor_bo) 63062306a36Sopenharmony_ci{ 63162306a36Sopenharmony_ci if (!cursor_bo) 63262306a36Sopenharmony_ci return; 63362306a36Sopenharmony_ci 63462306a36Sopenharmony_ci qxl_bo_unpin(cursor_bo); 63562306a36Sopenharmony_ci qxl_bo_unref(&cursor_bo); 63662306a36Sopenharmony_ci} 63762306a36Sopenharmony_ci 63862306a36Sopenharmony_cistatic void qxl_primary_atomic_update(struct drm_plane *plane, 63962306a36Sopenharmony_ci struct drm_atomic_state *state) 64062306a36Sopenharmony_ci{ 64162306a36Sopenharmony_ci struct drm_plane_state *new_state = drm_atomic_get_new_plane_state(state, 64262306a36Sopenharmony_ci plane); 64362306a36Sopenharmony_ci struct qxl_device *qdev = to_qxl(plane->dev); 64462306a36Sopenharmony_ci struct qxl_bo *bo = gem_to_qxl_bo(new_state->fb->obj[0]); 64562306a36Sopenharmony_ci struct qxl_bo *primary; 64662306a36Sopenharmony_ci struct drm_clip_rect norect = { 64762306a36Sopenharmony_ci .x1 = 0, 64862306a36Sopenharmony_ci .y1 = 0, 64962306a36Sopenharmony_ci .x2 = new_state->fb->width, 65062306a36Sopenharmony_ci .y2 = new_state->fb->height 65162306a36Sopenharmony_ci }; 65262306a36Sopenharmony_ci uint32_t dumb_shadow_offset = 0; 65362306a36Sopenharmony_ci 65462306a36Sopenharmony_ci primary = bo->shadow ? bo->shadow : bo; 65562306a36Sopenharmony_ci 65662306a36Sopenharmony_ci if (!primary->is_primary) { 65762306a36Sopenharmony_ci if (qdev->primary_bo) 65862306a36Sopenharmony_ci qxl_io_destroy_primary(qdev); 65962306a36Sopenharmony_ci qxl_io_create_primary(qdev, primary); 66062306a36Sopenharmony_ci qxl_primary_apply_cursor(qdev, plane->state); 66162306a36Sopenharmony_ci } 66262306a36Sopenharmony_ci 66362306a36Sopenharmony_ci if (bo->is_dumb) 66462306a36Sopenharmony_ci dumb_shadow_offset = 66562306a36Sopenharmony_ci qdev->dumb_heads[new_state->crtc->index].x; 66662306a36Sopenharmony_ci 66762306a36Sopenharmony_ci qxl_draw_dirty_fb(qdev, new_state->fb, bo, 0, 0, &norect, 1, 1, 66862306a36Sopenharmony_ci dumb_shadow_offset); 66962306a36Sopenharmony_ci} 67062306a36Sopenharmony_ci 67162306a36Sopenharmony_cistatic void qxl_primary_atomic_disable(struct drm_plane *plane, 67262306a36Sopenharmony_ci struct drm_atomic_state *state) 67362306a36Sopenharmony_ci{ 67462306a36Sopenharmony_ci struct drm_plane_state *old_state = drm_atomic_get_old_plane_state(state, 67562306a36Sopenharmony_ci plane); 67662306a36Sopenharmony_ci struct qxl_device *qdev = to_qxl(plane->dev); 67762306a36Sopenharmony_ci 67862306a36Sopenharmony_ci if (old_state->fb) { 67962306a36Sopenharmony_ci struct qxl_bo *bo = gem_to_qxl_bo(old_state->fb->obj[0]); 68062306a36Sopenharmony_ci 68162306a36Sopenharmony_ci if (bo->shadow) 68262306a36Sopenharmony_ci bo = bo->shadow; 68362306a36Sopenharmony_ci if (bo->is_primary) 68462306a36Sopenharmony_ci qxl_io_destroy_primary(qdev); 68562306a36Sopenharmony_ci } 68662306a36Sopenharmony_ci} 68762306a36Sopenharmony_ci 68862306a36Sopenharmony_cistatic void qxl_cursor_atomic_update(struct drm_plane *plane, 68962306a36Sopenharmony_ci struct drm_atomic_state *state) 69062306a36Sopenharmony_ci{ 69162306a36Sopenharmony_ci struct drm_plane_state *old_state = drm_atomic_get_old_plane_state(state, 69262306a36Sopenharmony_ci plane); 69362306a36Sopenharmony_ci struct drm_plane_state *new_state = drm_atomic_get_new_plane_state(state, 69462306a36Sopenharmony_ci plane); 69562306a36Sopenharmony_ci struct qxl_device *qdev = to_qxl(plane->dev); 69662306a36Sopenharmony_ci struct drm_framebuffer *fb = new_state->fb; 69762306a36Sopenharmony_ci 69862306a36Sopenharmony_ci if (fb != old_state->fb) { 69962306a36Sopenharmony_ci qxl_primary_apply_cursor(qdev, new_state); 70062306a36Sopenharmony_ci } else { 70162306a36Sopenharmony_ci qxl_primary_move_cursor(qdev, new_state); 70262306a36Sopenharmony_ci } 70362306a36Sopenharmony_ci} 70462306a36Sopenharmony_ci 70562306a36Sopenharmony_cistatic void qxl_cursor_atomic_disable(struct drm_plane *plane, 70662306a36Sopenharmony_ci struct drm_atomic_state *state) 70762306a36Sopenharmony_ci{ 70862306a36Sopenharmony_ci struct drm_plane_state *old_state = drm_atomic_get_old_plane_state(state, 70962306a36Sopenharmony_ci plane); 71062306a36Sopenharmony_ci struct qxl_device *qdev = to_qxl(plane->dev); 71162306a36Sopenharmony_ci struct qxl_crtc *qcrtc; 71262306a36Sopenharmony_ci struct qxl_release *release; 71362306a36Sopenharmony_ci struct qxl_cursor_cmd *cmd; 71462306a36Sopenharmony_ci int ret; 71562306a36Sopenharmony_ci 71662306a36Sopenharmony_ci ret = qxl_alloc_release_reserved(qdev, sizeof(*cmd), 71762306a36Sopenharmony_ci QXL_RELEASE_CURSOR_CMD, 71862306a36Sopenharmony_ci &release, NULL); 71962306a36Sopenharmony_ci if (ret) 72062306a36Sopenharmony_ci return; 72162306a36Sopenharmony_ci 72262306a36Sopenharmony_ci ret = qxl_release_reserve_list(release, true); 72362306a36Sopenharmony_ci if (ret) { 72462306a36Sopenharmony_ci qxl_release_free(qdev, release); 72562306a36Sopenharmony_ci return; 72662306a36Sopenharmony_ci } 72762306a36Sopenharmony_ci 72862306a36Sopenharmony_ci cmd = (struct qxl_cursor_cmd *)qxl_release_map(qdev, release); 72962306a36Sopenharmony_ci cmd->type = QXL_CURSOR_HIDE; 73062306a36Sopenharmony_ci qxl_release_unmap(qdev, release, &cmd->release_info); 73162306a36Sopenharmony_ci 73262306a36Sopenharmony_ci qxl_release_fence_buffer_objects(release); 73362306a36Sopenharmony_ci qxl_push_cursor_ring_release(qdev, release, QXL_CMD_CURSOR, false); 73462306a36Sopenharmony_ci 73562306a36Sopenharmony_ci qcrtc = to_qxl_crtc(old_state->crtc); 73662306a36Sopenharmony_ci qxl_free_cursor(qcrtc->cursor_bo); 73762306a36Sopenharmony_ci qcrtc->cursor_bo = NULL; 73862306a36Sopenharmony_ci} 73962306a36Sopenharmony_ci 74062306a36Sopenharmony_cistatic void qxl_update_dumb_head(struct qxl_device *qdev, 74162306a36Sopenharmony_ci int index, struct qxl_bo *bo) 74262306a36Sopenharmony_ci{ 74362306a36Sopenharmony_ci uint32_t width, height; 74462306a36Sopenharmony_ci 74562306a36Sopenharmony_ci if (index >= qdev->monitors_config->max_allowed) 74662306a36Sopenharmony_ci return; 74762306a36Sopenharmony_ci 74862306a36Sopenharmony_ci if (bo && bo->is_dumb) { 74962306a36Sopenharmony_ci width = bo->surf.width; 75062306a36Sopenharmony_ci height = bo->surf.height; 75162306a36Sopenharmony_ci } else { 75262306a36Sopenharmony_ci width = 0; 75362306a36Sopenharmony_ci height = 0; 75462306a36Sopenharmony_ci } 75562306a36Sopenharmony_ci 75662306a36Sopenharmony_ci if (qdev->dumb_heads[index].width == width && 75762306a36Sopenharmony_ci qdev->dumb_heads[index].height == height) 75862306a36Sopenharmony_ci return; 75962306a36Sopenharmony_ci 76062306a36Sopenharmony_ci DRM_DEBUG("#%d: %dx%d -> %dx%d\n", index, 76162306a36Sopenharmony_ci qdev->dumb_heads[index].width, 76262306a36Sopenharmony_ci qdev->dumb_heads[index].height, 76362306a36Sopenharmony_ci width, height); 76462306a36Sopenharmony_ci qdev->dumb_heads[index].width = width; 76562306a36Sopenharmony_ci qdev->dumb_heads[index].height = height; 76662306a36Sopenharmony_ci} 76762306a36Sopenharmony_ci 76862306a36Sopenharmony_cistatic void qxl_calc_dumb_shadow(struct qxl_device *qdev, 76962306a36Sopenharmony_ci struct qxl_surface *surf) 77062306a36Sopenharmony_ci{ 77162306a36Sopenharmony_ci struct qxl_head *head; 77262306a36Sopenharmony_ci int i; 77362306a36Sopenharmony_ci 77462306a36Sopenharmony_ci memset(surf, 0, sizeof(*surf)); 77562306a36Sopenharmony_ci for (i = 0; i < qdev->monitors_config->max_allowed; i++) { 77662306a36Sopenharmony_ci head = qdev->dumb_heads + i; 77762306a36Sopenharmony_ci head->x = surf->width; 77862306a36Sopenharmony_ci surf->width += head->width; 77962306a36Sopenharmony_ci if (surf->height < head->height) 78062306a36Sopenharmony_ci surf->height = head->height; 78162306a36Sopenharmony_ci } 78262306a36Sopenharmony_ci if (surf->width < 64) 78362306a36Sopenharmony_ci surf->width = 64; 78462306a36Sopenharmony_ci if (surf->height < 64) 78562306a36Sopenharmony_ci surf->height = 64; 78662306a36Sopenharmony_ci surf->format = SPICE_SURFACE_FMT_32_xRGB; 78762306a36Sopenharmony_ci surf->stride = surf->width * 4; 78862306a36Sopenharmony_ci 78962306a36Sopenharmony_ci if (!qdev->dumb_shadow_bo || 79062306a36Sopenharmony_ci qdev->dumb_shadow_bo->surf.width != surf->width || 79162306a36Sopenharmony_ci qdev->dumb_shadow_bo->surf.height != surf->height) 79262306a36Sopenharmony_ci DRM_DEBUG("%dx%d\n", surf->width, surf->height); 79362306a36Sopenharmony_ci} 79462306a36Sopenharmony_ci 79562306a36Sopenharmony_cistatic void qxl_prepare_shadow(struct qxl_device *qdev, struct qxl_bo *user_bo, 79662306a36Sopenharmony_ci int crtc_index) 79762306a36Sopenharmony_ci{ 79862306a36Sopenharmony_ci struct qxl_surface surf; 79962306a36Sopenharmony_ci 80062306a36Sopenharmony_ci qxl_update_dumb_head(qdev, crtc_index, 80162306a36Sopenharmony_ci user_bo); 80262306a36Sopenharmony_ci qxl_calc_dumb_shadow(qdev, &surf); 80362306a36Sopenharmony_ci if (!qdev->dumb_shadow_bo || 80462306a36Sopenharmony_ci qdev->dumb_shadow_bo->surf.width != surf.width || 80562306a36Sopenharmony_ci qdev->dumb_shadow_bo->surf.height != surf.height) { 80662306a36Sopenharmony_ci if (qdev->dumb_shadow_bo) { 80762306a36Sopenharmony_ci qxl_bo_unpin(qdev->dumb_shadow_bo); 80862306a36Sopenharmony_ci drm_gem_object_put 80962306a36Sopenharmony_ci (&qdev->dumb_shadow_bo->tbo.base); 81062306a36Sopenharmony_ci qdev->dumb_shadow_bo = NULL; 81162306a36Sopenharmony_ci } 81262306a36Sopenharmony_ci qxl_bo_create(qdev, surf.height * surf.stride, 81362306a36Sopenharmony_ci true, true, QXL_GEM_DOMAIN_SURFACE, 0, 81462306a36Sopenharmony_ci &surf, &qdev->dumb_shadow_bo); 81562306a36Sopenharmony_ci } 81662306a36Sopenharmony_ci if (user_bo->shadow != qdev->dumb_shadow_bo) { 81762306a36Sopenharmony_ci if (user_bo->shadow) { 81862306a36Sopenharmony_ci qxl_bo_unpin(user_bo->shadow); 81962306a36Sopenharmony_ci drm_gem_object_put 82062306a36Sopenharmony_ci (&user_bo->shadow->tbo.base); 82162306a36Sopenharmony_ci user_bo->shadow = NULL; 82262306a36Sopenharmony_ci } 82362306a36Sopenharmony_ci drm_gem_object_get(&qdev->dumb_shadow_bo->tbo.base); 82462306a36Sopenharmony_ci user_bo->shadow = qdev->dumb_shadow_bo; 82562306a36Sopenharmony_ci qxl_bo_pin(user_bo->shadow); 82662306a36Sopenharmony_ci } 82762306a36Sopenharmony_ci} 82862306a36Sopenharmony_ci 82962306a36Sopenharmony_cistatic int qxl_plane_prepare_fb(struct drm_plane *plane, 83062306a36Sopenharmony_ci struct drm_plane_state *new_state) 83162306a36Sopenharmony_ci{ 83262306a36Sopenharmony_ci struct qxl_device *qdev = to_qxl(plane->dev); 83362306a36Sopenharmony_ci struct drm_gem_object *obj; 83462306a36Sopenharmony_ci struct qxl_bo *user_bo; 83562306a36Sopenharmony_ci int ret; 83662306a36Sopenharmony_ci 83762306a36Sopenharmony_ci if (!new_state->fb) 83862306a36Sopenharmony_ci return 0; 83962306a36Sopenharmony_ci 84062306a36Sopenharmony_ci obj = new_state->fb->obj[0]; 84162306a36Sopenharmony_ci user_bo = gem_to_qxl_bo(obj); 84262306a36Sopenharmony_ci 84362306a36Sopenharmony_ci if (plane->type == DRM_PLANE_TYPE_PRIMARY && 84462306a36Sopenharmony_ci user_bo->is_dumb) { 84562306a36Sopenharmony_ci qxl_prepare_shadow(qdev, user_bo, new_state->crtc->index); 84662306a36Sopenharmony_ci } 84762306a36Sopenharmony_ci 84862306a36Sopenharmony_ci if (plane->type == DRM_PLANE_TYPE_CURSOR && 84962306a36Sopenharmony_ci plane->state->fb != new_state->fb) { 85062306a36Sopenharmony_ci struct qxl_crtc *qcrtc = to_qxl_crtc(new_state->crtc); 85162306a36Sopenharmony_ci struct qxl_bo *old_cursor_bo = qcrtc->cursor_bo; 85262306a36Sopenharmony_ci 85362306a36Sopenharmony_ci qcrtc->cursor_bo = qxl_create_cursor(qdev, user_bo, 85462306a36Sopenharmony_ci new_state->fb->hot_x, 85562306a36Sopenharmony_ci new_state->fb->hot_y); 85662306a36Sopenharmony_ci qxl_free_cursor(old_cursor_bo); 85762306a36Sopenharmony_ci } 85862306a36Sopenharmony_ci 85962306a36Sopenharmony_ci ret = qxl_bo_pin(user_bo); 86062306a36Sopenharmony_ci if (ret) 86162306a36Sopenharmony_ci return ret; 86262306a36Sopenharmony_ci 86362306a36Sopenharmony_ci return drm_gem_plane_helper_prepare_fb(plane, new_state); 86462306a36Sopenharmony_ci} 86562306a36Sopenharmony_ci 86662306a36Sopenharmony_cistatic void qxl_plane_cleanup_fb(struct drm_plane *plane, 86762306a36Sopenharmony_ci struct drm_plane_state *old_state) 86862306a36Sopenharmony_ci{ 86962306a36Sopenharmony_ci struct drm_gem_object *obj; 87062306a36Sopenharmony_ci struct qxl_bo *user_bo; 87162306a36Sopenharmony_ci 87262306a36Sopenharmony_ci if (!old_state->fb) { 87362306a36Sopenharmony_ci /* 87462306a36Sopenharmony_ci * we never executed prepare_fb, so there's nothing to 87562306a36Sopenharmony_ci * unpin. 87662306a36Sopenharmony_ci */ 87762306a36Sopenharmony_ci return; 87862306a36Sopenharmony_ci } 87962306a36Sopenharmony_ci 88062306a36Sopenharmony_ci obj = old_state->fb->obj[0]; 88162306a36Sopenharmony_ci user_bo = gem_to_qxl_bo(obj); 88262306a36Sopenharmony_ci qxl_bo_unpin(user_bo); 88362306a36Sopenharmony_ci 88462306a36Sopenharmony_ci if (old_state->fb != plane->state->fb && user_bo->shadow) { 88562306a36Sopenharmony_ci qxl_bo_unpin(user_bo->shadow); 88662306a36Sopenharmony_ci drm_gem_object_put(&user_bo->shadow->tbo.base); 88762306a36Sopenharmony_ci user_bo->shadow = NULL; 88862306a36Sopenharmony_ci } 88962306a36Sopenharmony_ci} 89062306a36Sopenharmony_ci 89162306a36Sopenharmony_cistatic const uint32_t qxl_cursor_plane_formats[] = { 89262306a36Sopenharmony_ci DRM_FORMAT_ARGB8888, 89362306a36Sopenharmony_ci}; 89462306a36Sopenharmony_ci 89562306a36Sopenharmony_cistatic const struct drm_plane_helper_funcs qxl_cursor_helper_funcs = { 89662306a36Sopenharmony_ci .atomic_update = qxl_cursor_atomic_update, 89762306a36Sopenharmony_ci .atomic_disable = qxl_cursor_atomic_disable, 89862306a36Sopenharmony_ci .prepare_fb = qxl_plane_prepare_fb, 89962306a36Sopenharmony_ci .cleanup_fb = qxl_plane_cleanup_fb, 90062306a36Sopenharmony_ci}; 90162306a36Sopenharmony_ci 90262306a36Sopenharmony_cistatic const struct drm_plane_funcs qxl_cursor_plane_funcs = { 90362306a36Sopenharmony_ci .update_plane = drm_atomic_helper_update_plane, 90462306a36Sopenharmony_ci .disable_plane = drm_atomic_helper_disable_plane, 90562306a36Sopenharmony_ci .destroy = drm_plane_helper_destroy, 90662306a36Sopenharmony_ci .reset = drm_atomic_helper_plane_reset, 90762306a36Sopenharmony_ci .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state, 90862306a36Sopenharmony_ci .atomic_destroy_state = drm_atomic_helper_plane_destroy_state, 90962306a36Sopenharmony_ci}; 91062306a36Sopenharmony_ci 91162306a36Sopenharmony_cistatic const uint32_t qxl_primary_plane_formats[] = { 91262306a36Sopenharmony_ci DRM_FORMAT_XRGB8888, 91362306a36Sopenharmony_ci DRM_FORMAT_ARGB8888, 91462306a36Sopenharmony_ci}; 91562306a36Sopenharmony_ci 91662306a36Sopenharmony_cistatic const struct drm_plane_helper_funcs primary_helper_funcs = { 91762306a36Sopenharmony_ci .atomic_check = qxl_primary_atomic_check, 91862306a36Sopenharmony_ci .atomic_update = qxl_primary_atomic_update, 91962306a36Sopenharmony_ci .atomic_disable = qxl_primary_atomic_disable, 92062306a36Sopenharmony_ci .prepare_fb = qxl_plane_prepare_fb, 92162306a36Sopenharmony_ci .cleanup_fb = qxl_plane_cleanup_fb, 92262306a36Sopenharmony_ci}; 92362306a36Sopenharmony_ci 92462306a36Sopenharmony_cistatic const struct drm_plane_funcs qxl_primary_plane_funcs = { 92562306a36Sopenharmony_ci .update_plane = drm_atomic_helper_update_plane, 92662306a36Sopenharmony_ci .disable_plane = drm_atomic_helper_disable_plane, 92762306a36Sopenharmony_ci .destroy = drm_plane_helper_destroy, 92862306a36Sopenharmony_ci .reset = drm_atomic_helper_plane_reset, 92962306a36Sopenharmony_ci .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state, 93062306a36Sopenharmony_ci .atomic_destroy_state = drm_atomic_helper_plane_destroy_state, 93162306a36Sopenharmony_ci}; 93262306a36Sopenharmony_ci 93362306a36Sopenharmony_cistatic struct drm_plane *qxl_create_plane(struct qxl_device *qdev, 93462306a36Sopenharmony_ci unsigned int possible_crtcs, 93562306a36Sopenharmony_ci enum drm_plane_type type) 93662306a36Sopenharmony_ci{ 93762306a36Sopenharmony_ci const struct drm_plane_helper_funcs *helper_funcs = NULL; 93862306a36Sopenharmony_ci struct drm_plane *plane; 93962306a36Sopenharmony_ci const struct drm_plane_funcs *funcs; 94062306a36Sopenharmony_ci const uint32_t *formats; 94162306a36Sopenharmony_ci int num_formats; 94262306a36Sopenharmony_ci int err; 94362306a36Sopenharmony_ci 94462306a36Sopenharmony_ci if (type == DRM_PLANE_TYPE_PRIMARY) { 94562306a36Sopenharmony_ci funcs = &qxl_primary_plane_funcs; 94662306a36Sopenharmony_ci formats = qxl_primary_plane_formats; 94762306a36Sopenharmony_ci num_formats = ARRAY_SIZE(qxl_primary_plane_formats); 94862306a36Sopenharmony_ci helper_funcs = &primary_helper_funcs; 94962306a36Sopenharmony_ci } else if (type == DRM_PLANE_TYPE_CURSOR) { 95062306a36Sopenharmony_ci funcs = &qxl_cursor_plane_funcs; 95162306a36Sopenharmony_ci formats = qxl_cursor_plane_formats; 95262306a36Sopenharmony_ci helper_funcs = &qxl_cursor_helper_funcs; 95362306a36Sopenharmony_ci num_formats = ARRAY_SIZE(qxl_cursor_plane_formats); 95462306a36Sopenharmony_ci } else { 95562306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 95662306a36Sopenharmony_ci } 95762306a36Sopenharmony_ci 95862306a36Sopenharmony_ci plane = kzalloc(sizeof(*plane), GFP_KERNEL); 95962306a36Sopenharmony_ci if (!plane) 96062306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 96162306a36Sopenharmony_ci 96262306a36Sopenharmony_ci err = drm_universal_plane_init(&qdev->ddev, plane, possible_crtcs, 96362306a36Sopenharmony_ci funcs, formats, num_formats, 96462306a36Sopenharmony_ci NULL, type, NULL); 96562306a36Sopenharmony_ci if (err) 96662306a36Sopenharmony_ci goto free_plane; 96762306a36Sopenharmony_ci 96862306a36Sopenharmony_ci drm_plane_helper_add(plane, helper_funcs); 96962306a36Sopenharmony_ci 97062306a36Sopenharmony_ci return plane; 97162306a36Sopenharmony_ci 97262306a36Sopenharmony_cifree_plane: 97362306a36Sopenharmony_ci kfree(plane); 97462306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 97562306a36Sopenharmony_ci} 97662306a36Sopenharmony_ci 97762306a36Sopenharmony_cistatic int qdev_crtc_init(struct drm_device *dev, int crtc_id) 97862306a36Sopenharmony_ci{ 97962306a36Sopenharmony_ci struct qxl_crtc *qxl_crtc; 98062306a36Sopenharmony_ci struct drm_plane *primary, *cursor; 98162306a36Sopenharmony_ci struct qxl_device *qdev = to_qxl(dev); 98262306a36Sopenharmony_ci int r; 98362306a36Sopenharmony_ci 98462306a36Sopenharmony_ci qxl_crtc = kzalloc(sizeof(struct qxl_crtc), GFP_KERNEL); 98562306a36Sopenharmony_ci if (!qxl_crtc) 98662306a36Sopenharmony_ci return -ENOMEM; 98762306a36Sopenharmony_ci 98862306a36Sopenharmony_ci primary = qxl_create_plane(qdev, 1 << crtc_id, DRM_PLANE_TYPE_PRIMARY); 98962306a36Sopenharmony_ci if (IS_ERR(primary)) { 99062306a36Sopenharmony_ci r = -ENOMEM; 99162306a36Sopenharmony_ci goto free_mem; 99262306a36Sopenharmony_ci } 99362306a36Sopenharmony_ci 99462306a36Sopenharmony_ci cursor = qxl_create_plane(qdev, 1 << crtc_id, DRM_PLANE_TYPE_CURSOR); 99562306a36Sopenharmony_ci if (IS_ERR(cursor)) { 99662306a36Sopenharmony_ci r = -ENOMEM; 99762306a36Sopenharmony_ci goto clean_primary; 99862306a36Sopenharmony_ci } 99962306a36Sopenharmony_ci 100062306a36Sopenharmony_ci r = drm_crtc_init_with_planes(dev, &qxl_crtc->base, primary, cursor, 100162306a36Sopenharmony_ci &qxl_crtc_funcs, NULL); 100262306a36Sopenharmony_ci if (r) 100362306a36Sopenharmony_ci goto clean_cursor; 100462306a36Sopenharmony_ci 100562306a36Sopenharmony_ci qxl_crtc->index = crtc_id; 100662306a36Sopenharmony_ci drm_crtc_helper_add(&qxl_crtc->base, &qxl_crtc_helper_funcs); 100762306a36Sopenharmony_ci return 0; 100862306a36Sopenharmony_ci 100962306a36Sopenharmony_ciclean_cursor: 101062306a36Sopenharmony_ci drm_plane_cleanup(cursor); 101162306a36Sopenharmony_ci kfree(cursor); 101262306a36Sopenharmony_ciclean_primary: 101362306a36Sopenharmony_ci drm_plane_cleanup(primary); 101462306a36Sopenharmony_ci kfree(primary); 101562306a36Sopenharmony_cifree_mem: 101662306a36Sopenharmony_ci kfree(qxl_crtc); 101762306a36Sopenharmony_ci return r; 101862306a36Sopenharmony_ci} 101962306a36Sopenharmony_ci 102062306a36Sopenharmony_cistatic int qxl_conn_get_modes(struct drm_connector *connector) 102162306a36Sopenharmony_ci{ 102262306a36Sopenharmony_ci struct drm_device *dev = connector->dev; 102362306a36Sopenharmony_ci struct qxl_device *qdev = to_qxl(dev); 102462306a36Sopenharmony_ci struct qxl_output *output = drm_connector_to_qxl_output(connector); 102562306a36Sopenharmony_ci unsigned int pwidth = 1024; 102662306a36Sopenharmony_ci unsigned int pheight = 768; 102762306a36Sopenharmony_ci int ret = 0; 102862306a36Sopenharmony_ci 102962306a36Sopenharmony_ci if (qdev->client_monitors_config) { 103062306a36Sopenharmony_ci struct qxl_head *head; 103162306a36Sopenharmony_ci head = &qdev->client_monitors_config->heads[output->index]; 103262306a36Sopenharmony_ci if (head->width) 103362306a36Sopenharmony_ci pwidth = head->width; 103462306a36Sopenharmony_ci if (head->height) 103562306a36Sopenharmony_ci pheight = head->height; 103662306a36Sopenharmony_ci } 103762306a36Sopenharmony_ci 103862306a36Sopenharmony_ci ret += drm_add_modes_noedid(connector, 8192, 8192); 103962306a36Sopenharmony_ci ret += qxl_add_extra_modes(connector); 104062306a36Sopenharmony_ci ret += qxl_add_monitors_config_modes(connector); 104162306a36Sopenharmony_ci drm_set_preferred_mode(connector, pwidth, pheight); 104262306a36Sopenharmony_ci return ret; 104362306a36Sopenharmony_ci} 104462306a36Sopenharmony_ci 104562306a36Sopenharmony_cistatic enum drm_mode_status qxl_conn_mode_valid(struct drm_connector *connector, 104662306a36Sopenharmony_ci struct drm_display_mode *mode) 104762306a36Sopenharmony_ci{ 104862306a36Sopenharmony_ci struct drm_device *ddev = connector->dev; 104962306a36Sopenharmony_ci struct qxl_device *qdev = to_qxl(ddev); 105062306a36Sopenharmony_ci 105162306a36Sopenharmony_ci if (qxl_check_mode(qdev, mode->hdisplay, mode->vdisplay) != 0) 105262306a36Sopenharmony_ci return MODE_BAD; 105362306a36Sopenharmony_ci 105462306a36Sopenharmony_ci return MODE_OK; 105562306a36Sopenharmony_ci} 105662306a36Sopenharmony_ci 105762306a36Sopenharmony_cistatic struct drm_encoder *qxl_best_encoder(struct drm_connector *connector) 105862306a36Sopenharmony_ci{ 105962306a36Sopenharmony_ci struct qxl_output *qxl_output = 106062306a36Sopenharmony_ci drm_connector_to_qxl_output(connector); 106162306a36Sopenharmony_ci 106262306a36Sopenharmony_ci DRM_DEBUG("\n"); 106362306a36Sopenharmony_ci return &qxl_output->enc; 106462306a36Sopenharmony_ci} 106562306a36Sopenharmony_ci 106662306a36Sopenharmony_cistatic const struct drm_connector_helper_funcs qxl_connector_helper_funcs = { 106762306a36Sopenharmony_ci .get_modes = qxl_conn_get_modes, 106862306a36Sopenharmony_ci .mode_valid = qxl_conn_mode_valid, 106962306a36Sopenharmony_ci .best_encoder = qxl_best_encoder, 107062306a36Sopenharmony_ci}; 107162306a36Sopenharmony_ci 107262306a36Sopenharmony_cistatic enum drm_connector_status qxl_conn_detect( 107362306a36Sopenharmony_ci struct drm_connector *connector, 107462306a36Sopenharmony_ci bool force) 107562306a36Sopenharmony_ci{ 107662306a36Sopenharmony_ci struct qxl_output *output = 107762306a36Sopenharmony_ci drm_connector_to_qxl_output(connector); 107862306a36Sopenharmony_ci struct drm_device *ddev = connector->dev; 107962306a36Sopenharmony_ci struct qxl_device *qdev = to_qxl(ddev); 108062306a36Sopenharmony_ci bool connected = false; 108162306a36Sopenharmony_ci 108262306a36Sopenharmony_ci /* The first monitor is always connected */ 108362306a36Sopenharmony_ci if (!qdev->client_monitors_config) { 108462306a36Sopenharmony_ci if (output->index == 0) 108562306a36Sopenharmony_ci connected = true; 108662306a36Sopenharmony_ci } else 108762306a36Sopenharmony_ci connected = qdev->client_monitors_config->count > output->index && 108862306a36Sopenharmony_ci qxl_head_enabled(&qdev->client_monitors_config->heads[output->index]); 108962306a36Sopenharmony_ci 109062306a36Sopenharmony_ci DRM_DEBUG("#%d connected: %d\n", output->index, connected); 109162306a36Sopenharmony_ci 109262306a36Sopenharmony_ci return connected ? connector_status_connected 109362306a36Sopenharmony_ci : connector_status_disconnected; 109462306a36Sopenharmony_ci} 109562306a36Sopenharmony_ci 109662306a36Sopenharmony_cistatic void qxl_conn_destroy(struct drm_connector *connector) 109762306a36Sopenharmony_ci{ 109862306a36Sopenharmony_ci struct qxl_output *qxl_output = 109962306a36Sopenharmony_ci drm_connector_to_qxl_output(connector); 110062306a36Sopenharmony_ci 110162306a36Sopenharmony_ci drm_connector_unregister(connector); 110262306a36Sopenharmony_ci drm_connector_cleanup(connector); 110362306a36Sopenharmony_ci kfree(qxl_output); 110462306a36Sopenharmony_ci} 110562306a36Sopenharmony_ci 110662306a36Sopenharmony_cistatic const struct drm_connector_funcs qxl_connector_funcs = { 110762306a36Sopenharmony_ci .detect = qxl_conn_detect, 110862306a36Sopenharmony_ci .fill_modes = drm_helper_probe_single_connector_modes, 110962306a36Sopenharmony_ci .destroy = qxl_conn_destroy, 111062306a36Sopenharmony_ci .reset = drm_atomic_helper_connector_reset, 111162306a36Sopenharmony_ci .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, 111262306a36Sopenharmony_ci .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, 111362306a36Sopenharmony_ci}; 111462306a36Sopenharmony_ci 111562306a36Sopenharmony_cistatic int qxl_mode_create_hotplug_mode_update_property(struct qxl_device *qdev) 111662306a36Sopenharmony_ci{ 111762306a36Sopenharmony_ci if (qdev->hotplug_mode_update_property) 111862306a36Sopenharmony_ci return 0; 111962306a36Sopenharmony_ci 112062306a36Sopenharmony_ci qdev->hotplug_mode_update_property = 112162306a36Sopenharmony_ci drm_property_create_range(&qdev->ddev, DRM_MODE_PROP_IMMUTABLE, 112262306a36Sopenharmony_ci "hotplug_mode_update", 0, 1); 112362306a36Sopenharmony_ci 112462306a36Sopenharmony_ci return 0; 112562306a36Sopenharmony_ci} 112662306a36Sopenharmony_ci 112762306a36Sopenharmony_cistatic int qdev_output_init(struct drm_device *dev, int num_output) 112862306a36Sopenharmony_ci{ 112962306a36Sopenharmony_ci struct qxl_device *qdev = to_qxl(dev); 113062306a36Sopenharmony_ci struct qxl_output *qxl_output; 113162306a36Sopenharmony_ci struct drm_connector *connector; 113262306a36Sopenharmony_ci struct drm_encoder *encoder; 113362306a36Sopenharmony_ci int ret; 113462306a36Sopenharmony_ci 113562306a36Sopenharmony_ci qxl_output = kzalloc(sizeof(struct qxl_output), GFP_KERNEL); 113662306a36Sopenharmony_ci if (!qxl_output) 113762306a36Sopenharmony_ci return -ENOMEM; 113862306a36Sopenharmony_ci 113962306a36Sopenharmony_ci qxl_output->index = num_output; 114062306a36Sopenharmony_ci 114162306a36Sopenharmony_ci connector = &qxl_output->base; 114262306a36Sopenharmony_ci encoder = &qxl_output->enc; 114362306a36Sopenharmony_ci drm_connector_init(dev, &qxl_output->base, 114462306a36Sopenharmony_ci &qxl_connector_funcs, DRM_MODE_CONNECTOR_VIRTUAL); 114562306a36Sopenharmony_ci 114662306a36Sopenharmony_ci ret = drm_simple_encoder_init(dev, &qxl_output->enc, 114762306a36Sopenharmony_ci DRM_MODE_ENCODER_VIRTUAL); 114862306a36Sopenharmony_ci if (ret) { 114962306a36Sopenharmony_ci drm_err(dev, "drm_simple_encoder_init() failed, error %d\n", 115062306a36Sopenharmony_ci ret); 115162306a36Sopenharmony_ci goto err_drm_connector_cleanup; 115262306a36Sopenharmony_ci } 115362306a36Sopenharmony_ci 115462306a36Sopenharmony_ci /* we get HPD via client monitors config */ 115562306a36Sopenharmony_ci connector->polled = DRM_CONNECTOR_POLL_HPD; 115662306a36Sopenharmony_ci encoder->possible_crtcs = 1 << num_output; 115762306a36Sopenharmony_ci drm_connector_attach_encoder(&qxl_output->base, 115862306a36Sopenharmony_ci &qxl_output->enc); 115962306a36Sopenharmony_ci drm_connector_helper_add(connector, &qxl_connector_helper_funcs); 116062306a36Sopenharmony_ci 116162306a36Sopenharmony_ci drm_object_attach_property(&connector->base, 116262306a36Sopenharmony_ci qdev->hotplug_mode_update_property, 0); 116362306a36Sopenharmony_ci drm_object_attach_property(&connector->base, 116462306a36Sopenharmony_ci dev->mode_config.suggested_x_property, 0); 116562306a36Sopenharmony_ci drm_object_attach_property(&connector->base, 116662306a36Sopenharmony_ci dev->mode_config.suggested_y_property, 0); 116762306a36Sopenharmony_ci return 0; 116862306a36Sopenharmony_ci 116962306a36Sopenharmony_cierr_drm_connector_cleanup: 117062306a36Sopenharmony_ci drm_connector_cleanup(&qxl_output->base); 117162306a36Sopenharmony_ci kfree(qxl_output); 117262306a36Sopenharmony_ci return ret; 117362306a36Sopenharmony_ci} 117462306a36Sopenharmony_ci 117562306a36Sopenharmony_cistatic struct drm_framebuffer * 117662306a36Sopenharmony_ciqxl_user_framebuffer_create(struct drm_device *dev, 117762306a36Sopenharmony_ci struct drm_file *file_priv, 117862306a36Sopenharmony_ci const struct drm_mode_fb_cmd2 *mode_cmd) 117962306a36Sopenharmony_ci{ 118062306a36Sopenharmony_ci return drm_gem_fb_create_with_funcs(dev, file_priv, mode_cmd, 118162306a36Sopenharmony_ci &qxl_fb_funcs); 118262306a36Sopenharmony_ci} 118362306a36Sopenharmony_ci 118462306a36Sopenharmony_cistatic const struct drm_mode_config_funcs qxl_mode_funcs = { 118562306a36Sopenharmony_ci .fb_create = qxl_user_framebuffer_create, 118662306a36Sopenharmony_ci .atomic_check = drm_atomic_helper_check, 118762306a36Sopenharmony_ci .atomic_commit = drm_atomic_helper_commit, 118862306a36Sopenharmony_ci}; 118962306a36Sopenharmony_ci 119062306a36Sopenharmony_ciint qxl_create_monitors_object(struct qxl_device *qdev) 119162306a36Sopenharmony_ci{ 119262306a36Sopenharmony_ci int ret; 119362306a36Sopenharmony_ci struct drm_gem_object *gobj; 119462306a36Sopenharmony_ci struct iosys_map map; 119562306a36Sopenharmony_ci int monitors_config_size = sizeof(struct qxl_monitors_config) + 119662306a36Sopenharmony_ci qxl_num_crtc * sizeof(struct qxl_head); 119762306a36Sopenharmony_ci 119862306a36Sopenharmony_ci ret = qxl_gem_object_create(qdev, monitors_config_size, 0, 119962306a36Sopenharmony_ci QXL_GEM_DOMAIN_VRAM, 120062306a36Sopenharmony_ci false, false, NULL, &gobj); 120162306a36Sopenharmony_ci if (ret) { 120262306a36Sopenharmony_ci DRM_ERROR("%s: failed to create gem ret=%d\n", __func__, ret); 120362306a36Sopenharmony_ci return -ENOMEM; 120462306a36Sopenharmony_ci } 120562306a36Sopenharmony_ci qdev->monitors_config_bo = gem_to_qxl_bo(gobj); 120662306a36Sopenharmony_ci 120762306a36Sopenharmony_ci ret = qxl_bo_vmap(qdev->monitors_config_bo, &map); 120862306a36Sopenharmony_ci if (ret) 120962306a36Sopenharmony_ci return ret; 121062306a36Sopenharmony_ci 121162306a36Sopenharmony_ci qdev->monitors_config = qdev->monitors_config_bo->kptr; 121262306a36Sopenharmony_ci qdev->ram_header->monitors_config = 121362306a36Sopenharmony_ci qxl_bo_physical_address(qdev, qdev->monitors_config_bo, 0); 121462306a36Sopenharmony_ci 121562306a36Sopenharmony_ci memset(qdev->monitors_config, 0, monitors_config_size); 121662306a36Sopenharmony_ci qdev->dumb_heads = kcalloc(qxl_num_crtc, sizeof(qdev->dumb_heads[0]), 121762306a36Sopenharmony_ci GFP_KERNEL); 121862306a36Sopenharmony_ci if (!qdev->dumb_heads) { 121962306a36Sopenharmony_ci qxl_destroy_monitors_object(qdev); 122062306a36Sopenharmony_ci return -ENOMEM; 122162306a36Sopenharmony_ci } 122262306a36Sopenharmony_ci return 0; 122362306a36Sopenharmony_ci} 122462306a36Sopenharmony_ci 122562306a36Sopenharmony_ciint qxl_destroy_monitors_object(struct qxl_device *qdev) 122662306a36Sopenharmony_ci{ 122762306a36Sopenharmony_ci int ret; 122862306a36Sopenharmony_ci 122962306a36Sopenharmony_ci if (!qdev->monitors_config_bo) 123062306a36Sopenharmony_ci return 0; 123162306a36Sopenharmony_ci 123262306a36Sopenharmony_ci kfree(qdev->dumb_heads); 123362306a36Sopenharmony_ci qdev->dumb_heads = NULL; 123462306a36Sopenharmony_ci 123562306a36Sopenharmony_ci qdev->monitors_config = NULL; 123662306a36Sopenharmony_ci qdev->ram_header->monitors_config = 0; 123762306a36Sopenharmony_ci 123862306a36Sopenharmony_ci ret = qxl_bo_vunmap(qdev->monitors_config_bo); 123962306a36Sopenharmony_ci if (ret) 124062306a36Sopenharmony_ci return ret; 124162306a36Sopenharmony_ci 124262306a36Sopenharmony_ci qxl_bo_unref(&qdev->monitors_config_bo); 124362306a36Sopenharmony_ci return 0; 124462306a36Sopenharmony_ci} 124562306a36Sopenharmony_ci 124662306a36Sopenharmony_ciint qxl_modeset_init(struct qxl_device *qdev) 124762306a36Sopenharmony_ci{ 124862306a36Sopenharmony_ci int i; 124962306a36Sopenharmony_ci int ret; 125062306a36Sopenharmony_ci 125162306a36Sopenharmony_ci ret = drmm_mode_config_init(&qdev->ddev); 125262306a36Sopenharmony_ci if (ret) 125362306a36Sopenharmony_ci return ret; 125462306a36Sopenharmony_ci 125562306a36Sopenharmony_ci ret = qxl_create_monitors_object(qdev); 125662306a36Sopenharmony_ci if (ret) 125762306a36Sopenharmony_ci return ret; 125862306a36Sopenharmony_ci 125962306a36Sopenharmony_ci qdev->ddev.mode_config.funcs = (void *)&qxl_mode_funcs; 126062306a36Sopenharmony_ci 126162306a36Sopenharmony_ci /* modes will be validated against the framebuffer size */ 126262306a36Sopenharmony_ci qdev->ddev.mode_config.min_width = 0; 126362306a36Sopenharmony_ci qdev->ddev.mode_config.min_height = 0; 126462306a36Sopenharmony_ci qdev->ddev.mode_config.max_width = 8192; 126562306a36Sopenharmony_ci qdev->ddev.mode_config.max_height = 8192; 126662306a36Sopenharmony_ci 126762306a36Sopenharmony_ci drm_mode_create_suggested_offset_properties(&qdev->ddev); 126862306a36Sopenharmony_ci qxl_mode_create_hotplug_mode_update_property(qdev); 126962306a36Sopenharmony_ci 127062306a36Sopenharmony_ci for (i = 0 ; i < qxl_num_crtc; ++i) { 127162306a36Sopenharmony_ci qdev_crtc_init(&qdev->ddev, i); 127262306a36Sopenharmony_ci qdev_output_init(&qdev->ddev, i); 127362306a36Sopenharmony_ci } 127462306a36Sopenharmony_ci 127562306a36Sopenharmony_ci qxl_display_read_client_monitors_config(qdev); 127662306a36Sopenharmony_ci 127762306a36Sopenharmony_ci drm_mode_config_reset(&qdev->ddev); 127862306a36Sopenharmony_ci return 0; 127962306a36Sopenharmony_ci} 128062306a36Sopenharmony_ci 128162306a36Sopenharmony_civoid qxl_modeset_fini(struct qxl_device *qdev) 128262306a36Sopenharmony_ci{ 128362306a36Sopenharmony_ci if (qdev->dumb_shadow_bo) { 128462306a36Sopenharmony_ci qxl_bo_unpin(qdev->dumb_shadow_bo); 128562306a36Sopenharmony_ci drm_gem_object_put(&qdev->dumb_shadow_bo->tbo.base); 128662306a36Sopenharmony_ci qdev->dumb_shadow_bo = NULL; 128762306a36Sopenharmony_ci } 128862306a36Sopenharmony_ci qxl_destroy_monitors_object(qdev); 128962306a36Sopenharmony_ci} 1290