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/* QXL cmd/ring handling */ 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci#include <linux/delay.h> 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci#include <drm/drm_util.h> 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci#include "qxl_drv.h" 3362306a36Sopenharmony_ci#include "qxl_object.h" 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_cistatic int qxl_reap_surface_id(struct qxl_device *qdev, int max_to_reap); 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_cistruct ring { 3862306a36Sopenharmony_ci struct qxl_ring_header header; 3962306a36Sopenharmony_ci uint8_t elements[]; 4062306a36Sopenharmony_ci}; 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_cistruct qxl_ring { 4362306a36Sopenharmony_ci struct ring *ring; 4462306a36Sopenharmony_ci int element_size; 4562306a36Sopenharmony_ci int n_elements; 4662306a36Sopenharmony_ci int prod_notify; 4762306a36Sopenharmony_ci wait_queue_head_t *push_event; 4862306a36Sopenharmony_ci spinlock_t lock; 4962306a36Sopenharmony_ci}; 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_civoid qxl_ring_free(struct qxl_ring *ring) 5262306a36Sopenharmony_ci{ 5362306a36Sopenharmony_ci kfree(ring); 5462306a36Sopenharmony_ci} 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_cistruct qxl_ring * 5762306a36Sopenharmony_ciqxl_ring_create(struct qxl_ring_header *header, 5862306a36Sopenharmony_ci int element_size, 5962306a36Sopenharmony_ci int n_elements, 6062306a36Sopenharmony_ci int prod_notify, 6162306a36Sopenharmony_ci wait_queue_head_t *push_event) 6262306a36Sopenharmony_ci{ 6362306a36Sopenharmony_ci struct qxl_ring *ring; 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci ring = kmalloc(sizeof(*ring), GFP_KERNEL); 6662306a36Sopenharmony_ci if (!ring) 6762306a36Sopenharmony_ci return NULL; 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci ring->ring = (struct ring *)header; 7062306a36Sopenharmony_ci ring->element_size = element_size; 7162306a36Sopenharmony_ci ring->n_elements = n_elements; 7262306a36Sopenharmony_ci ring->prod_notify = prod_notify; 7362306a36Sopenharmony_ci ring->push_event = push_event; 7462306a36Sopenharmony_ci spin_lock_init(&ring->lock); 7562306a36Sopenharmony_ci return ring; 7662306a36Sopenharmony_ci} 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_cistatic int qxl_check_header(struct qxl_ring *ring) 7962306a36Sopenharmony_ci{ 8062306a36Sopenharmony_ci int ret; 8162306a36Sopenharmony_ci struct qxl_ring_header *header = &(ring->ring->header); 8262306a36Sopenharmony_ci unsigned long flags; 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci spin_lock_irqsave(&ring->lock, flags); 8562306a36Sopenharmony_ci ret = header->prod - header->cons < header->num_items; 8662306a36Sopenharmony_ci if (ret == 0) 8762306a36Sopenharmony_ci header->notify_on_cons = header->cons + 1; 8862306a36Sopenharmony_ci spin_unlock_irqrestore(&ring->lock, flags); 8962306a36Sopenharmony_ci return ret; 9062306a36Sopenharmony_ci} 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ciint qxl_check_idle(struct qxl_ring *ring) 9362306a36Sopenharmony_ci{ 9462306a36Sopenharmony_ci int ret; 9562306a36Sopenharmony_ci struct qxl_ring_header *header = &(ring->ring->header); 9662306a36Sopenharmony_ci unsigned long flags; 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci spin_lock_irqsave(&ring->lock, flags); 9962306a36Sopenharmony_ci ret = header->prod == header->cons; 10062306a36Sopenharmony_ci spin_unlock_irqrestore(&ring->lock, flags); 10162306a36Sopenharmony_ci return ret; 10262306a36Sopenharmony_ci} 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ciint qxl_ring_push(struct qxl_ring *ring, 10562306a36Sopenharmony_ci const void *new_elt, bool interruptible) 10662306a36Sopenharmony_ci{ 10762306a36Sopenharmony_ci struct qxl_ring_header *header = &(ring->ring->header); 10862306a36Sopenharmony_ci uint8_t *elt; 10962306a36Sopenharmony_ci int idx, ret; 11062306a36Sopenharmony_ci unsigned long flags; 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci spin_lock_irqsave(&ring->lock, flags); 11362306a36Sopenharmony_ci if (header->prod - header->cons == header->num_items) { 11462306a36Sopenharmony_ci header->notify_on_cons = header->cons + 1; 11562306a36Sopenharmony_ci mb(); 11662306a36Sopenharmony_ci spin_unlock_irqrestore(&ring->lock, flags); 11762306a36Sopenharmony_ci if (!drm_can_sleep()) { 11862306a36Sopenharmony_ci while (!qxl_check_header(ring)) 11962306a36Sopenharmony_ci udelay(1); 12062306a36Sopenharmony_ci } else { 12162306a36Sopenharmony_ci if (interruptible) { 12262306a36Sopenharmony_ci ret = wait_event_interruptible(*ring->push_event, 12362306a36Sopenharmony_ci qxl_check_header(ring)); 12462306a36Sopenharmony_ci if (ret) 12562306a36Sopenharmony_ci return ret; 12662306a36Sopenharmony_ci } else { 12762306a36Sopenharmony_ci wait_event(*ring->push_event, 12862306a36Sopenharmony_ci qxl_check_header(ring)); 12962306a36Sopenharmony_ci } 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ci } 13262306a36Sopenharmony_ci spin_lock_irqsave(&ring->lock, flags); 13362306a36Sopenharmony_ci } 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci idx = header->prod & (ring->n_elements - 1); 13662306a36Sopenharmony_ci elt = ring->ring->elements + idx * ring->element_size; 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci memcpy((void *)elt, new_elt, ring->element_size); 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci header->prod++; 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci mb(); 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci if (header->prod == header->notify_on_prod) 14562306a36Sopenharmony_ci outb(0, ring->prod_notify); 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci spin_unlock_irqrestore(&ring->lock, flags); 14862306a36Sopenharmony_ci return 0; 14962306a36Sopenharmony_ci} 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_cistatic bool qxl_ring_pop(struct qxl_ring *ring, 15262306a36Sopenharmony_ci void *element) 15362306a36Sopenharmony_ci{ 15462306a36Sopenharmony_ci volatile struct qxl_ring_header *header = &(ring->ring->header); 15562306a36Sopenharmony_ci volatile uint8_t *ring_elt; 15662306a36Sopenharmony_ci int idx; 15762306a36Sopenharmony_ci unsigned long flags; 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci spin_lock_irqsave(&ring->lock, flags); 16062306a36Sopenharmony_ci if (header->cons == header->prod) { 16162306a36Sopenharmony_ci header->notify_on_prod = header->cons + 1; 16262306a36Sopenharmony_ci spin_unlock_irqrestore(&ring->lock, flags); 16362306a36Sopenharmony_ci return false; 16462306a36Sopenharmony_ci } 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci idx = header->cons & (ring->n_elements - 1); 16762306a36Sopenharmony_ci ring_elt = ring->ring->elements + idx * ring->element_size; 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci memcpy(element, (void *)ring_elt, ring->element_size); 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ci header->cons++; 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci spin_unlock_irqrestore(&ring->lock, flags); 17462306a36Sopenharmony_ci return true; 17562306a36Sopenharmony_ci} 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ciint 17862306a36Sopenharmony_ciqxl_push_command_ring_release(struct qxl_device *qdev, struct qxl_release *release, 17962306a36Sopenharmony_ci uint32_t type, bool interruptible) 18062306a36Sopenharmony_ci{ 18162306a36Sopenharmony_ci struct qxl_command cmd; 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_ci cmd.type = type; 18462306a36Sopenharmony_ci cmd.data = qxl_bo_physical_address(qdev, release->release_bo, release->release_offset); 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ci return qxl_ring_push(qdev->command_ring, &cmd, interruptible); 18762306a36Sopenharmony_ci} 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ciint 19062306a36Sopenharmony_ciqxl_push_cursor_ring_release(struct qxl_device *qdev, struct qxl_release *release, 19162306a36Sopenharmony_ci uint32_t type, bool interruptible) 19262306a36Sopenharmony_ci{ 19362306a36Sopenharmony_ci struct qxl_command cmd; 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci cmd.type = type; 19662306a36Sopenharmony_ci cmd.data = qxl_bo_physical_address(qdev, release->release_bo, release->release_offset); 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ci return qxl_ring_push(qdev->cursor_ring, &cmd, interruptible); 19962306a36Sopenharmony_ci} 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_cibool qxl_queue_garbage_collect(struct qxl_device *qdev, bool flush) 20262306a36Sopenharmony_ci{ 20362306a36Sopenharmony_ci if (!qxl_check_idle(qdev->release_ring)) { 20462306a36Sopenharmony_ci schedule_work(&qdev->gc_work); 20562306a36Sopenharmony_ci if (flush) 20662306a36Sopenharmony_ci flush_work(&qdev->gc_work); 20762306a36Sopenharmony_ci return true; 20862306a36Sopenharmony_ci } 20962306a36Sopenharmony_ci return false; 21062306a36Sopenharmony_ci} 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ciint qxl_garbage_collect(struct qxl_device *qdev) 21362306a36Sopenharmony_ci{ 21462306a36Sopenharmony_ci struct qxl_release *release; 21562306a36Sopenharmony_ci uint64_t id, next_id; 21662306a36Sopenharmony_ci int i = 0; 21762306a36Sopenharmony_ci union qxl_release_info *info; 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ci while (qxl_ring_pop(qdev->release_ring, &id)) { 22062306a36Sopenharmony_ci DRM_DEBUG_DRIVER("popped %lld\n", id); 22162306a36Sopenharmony_ci while (id) { 22262306a36Sopenharmony_ci release = qxl_release_from_id_locked(qdev, id); 22362306a36Sopenharmony_ci if (release == NULL) 22462306a36Sopenharmony_ci break; 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ci info = qxl_release_map(qdev, release); 22762306a36Sopenharmony_ci next_id = info->next; 22862306a36Sopenharmony_ci qxl_release_unmap(qdev, release, info); 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_ci DRM_DEBUG_DRIVER("popped %lld, next %lld\n", id, 23162306a36Sopenharmony_ci next_id); 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ci switch (release->type) { 23462306a36Sopenharmony_ci case QXL_RELEASE_DRAWABLE: 23562306a36Sopenharmony_ci case QXL_RELEASE_SURFACE_CMD: 23662306a36Sopenharmony_ci case QXL_RELEASE_CURSOR_CMD: 23762306a36Sopenharmony_ci break; 23862306a36Sopenharmony_ci default: 23962306a36Sopenharmony_ci DRM_ERROR("unexpected release type\n"); 24062306a36Sopenharmony_ci break; 24162306a36Sopenharmony_ci } 24262306a36Sopenharmony_ci id = next_id; 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ci qxl_release_free(qdev, release); 24562306a36Sopenharmony_ci ++i; 24662306a36Sopenharmony_ci } 24762306a36Sopenharmony_ci } 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_ci wake_up_all(&qdev->release_event); 25062306a36Sopenharmony_ci DRM_DEBUG_DRIVER("%d\n", i); 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci return i; 25362306a36Sopenharmony_ci} 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ciint qxl_alloc_bo_reserved(struct qxl_device *qdev, 25662306a36Sopenharmony_ci struct qxl_release *release, 25762306a36Sopenharmony_ci unsigned long size, 25862306a36Sopenharmony_ci struct qxl_bo **_bo) 25962306a36Sopenharmony_ci{ 26062306a36Sopenharmony_ci struct qxl_bo *bo; 26162306a36Sopenharmony_ci int ret; 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ci ret = qxl_bo_create(qdev, size, false /* not kernel - device */, 26462306a36Sopenharmony_ci false, QXL_GEM_DOMAIN_VRAM, 0, NULL, &bo); 26562306a36Sopenharmony_ci if (ret) { 26662306a36Sopenharmony_ci DRM_ERROR("failed to allocate VRAM BO\n"); 26762306a36Sopenharmony_ci return ret; 26862306a36Sopenharmony_ci } 26962306a36Sopenharmony_ci ret = qxl_release_list_add(release, bo); 27062306a36Sopenharmony_ci if (ret) 27162306a36Sopenharmony_ci goto out_unref; 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_ci *_bo = bo; 27462306a36Sopenharmony_ci return 0; 27562306a36Sopenharmony_ciout_unref: 27662306a36Sopenharmony_ci qxl_bo_unref(&bo); 27762306a36Sopenharmony_ci return ret; 27862306a36Sopenharmony_ci} 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_cistatic int wait_for_io_cmd_user(struct qxl_device *qdev, uint8_t val, long port, bool intr) 28162306a36Sopenharmony_ci{ 28262306a36Sopenharmony_ci int irq_num; 28362306a36Sopenharmony_ci long addr = qdev->io_base + port; 28462306a36Sopenharmony_ci int ret; 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_ci mutex_lock(&qdev->async_io_mutex); 28762306a36Sopenharmony_ci irq_num = atomic_read(&qdev->irq_received_io_cmd); 28862306a36Sopenharmony_ci if (qdev->last_sent_io_cmd > irq_num) { 28962306a36Sopenharmony_ci if (intr) 29062306a36Sopenharmony_ci ret = wait_event_interruptible_timeout(qdev->io_cmd_event, 29162306a36Sopenharmony_ci atomic_read(&qdev->irq_received_io_cmd) > irq_num, 5*HZ); 29262306a36Sopenharmony_ci else 29362306a36Sopenharmony_ci ret = wait_event_timeout(qdev->io_cmd_event, 29462306a36Sopenharmony_ci atomic_read(&qdev->irq_received_io_cmd) > irq_num, 5*HZ); 29562306a36Sopenharmony_ci /* 0 is timeout, just bail the "hw" has gone away */ 29662306a36Sopenharmony_ci if (ret <= 0) 29762306a36Sopenharmony_ci goto out; 29862306a36Sopenharmony_ci irq_num = atomic_read(&qdev->irq_received_io_cmd); 29962306a36Sopenharmony_ci } 30062306a36Sopenharmony_ci outb(val, addr); 30162306a36Sopenharmony_ci qdev->last_sent_io_cmd = irq_num + 1; 30262306a36Sopenharmony_ci if (intr) 30362306a36Sopenharmony_ci ret = wait_event_interruptible_timeout(qdev->io_cmd_event, 30462306a36Sopenharmony_ci atomic_read(&qdev->irq_received_io_cmd) > irq_num, 5*HZ); 30562306a36Sopenharmony_ci else 30662306a36Sopenharmony_ci ret = wait_event_timeout(qdev->io_cmd_event, 30762306a36Sopenharmony_ci atomic_read(&qdev->irq_received_io_cmd) > irq_num, 5*HZ); 30862306a36Sopenharmony_ciout: 30962306a36Sopenharmony_ci if (ret > 0) 31062306a36Sopenharmony_ci ret = 0; 31162306a36Sopenharmony_ci mutex_unlock(&qdev->async_io_mutex); 31262306a36Sopenharmony_ci return ret; 31362306a36Sopenharmony_ci} 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_cistatic void wait_for_io_cmd(struct qxl_device *qdev, uint8_t val, long port) 31662306a36Sopenharmony_ci{ 31762306a36Sopenharmony_ci int ret; 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_cirestart: 32062306a36Sopenharmony_ci ret = wait_for_io_cmd_user(qdev, val, port, false); 32162306a36Sopenharmony_ci if (ret == -ERESTARTSYS) 32262306a36Sopenharmony_ci goto restart; 32362306a36Sopenharmony_ci} 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_ciint qxl_io_update_area(struct qxl_device *qdev, struct qxl_bo *surf, 32662306a36Sopenharmony_ci const struct qxl_rect *area) 32762306a36Sopenharmony_ci{ 32862306a36Sopenharmony_ci int surface_id; 32962306a36Sopenharmony_ci uint32_t surface_width, surface_height; 33062306a36Sopenharmony_ci int ret; 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_ci if (!surf->hw_surf_alloc) 33362306a36Sopenharmony_ci DRM_ERROR("got io update area with no hw surface\n"); 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_ci if (surf->is_primary) 33662306a36Sopenharmony_ci surface_id = 0; 33762306a36Sopenharmony_ci else 33862306a36Sopenharmony_ci surface_id = surf->surface_id; 33962306a36Sopenharmony_ci surface_width = surf->surf.width; 34062306a36Sopenharmony_ci surface_height = surf->surf.height; 34162306a36Sopenharmony_ci 34262306a36Sopenharmony_ci if (area->left < 0 || area->top < 0 || 34362306a36Sopenharmony_ci area->right > surface_width || area->bottom > surface_height) 34462306a36Sopenharmony_ci return -EINVAL; 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_ci mutex_lock(&qdev->update_area_mutex); 34762306a36Sopenharmony_ci qdev->ram_header->update_area = *area; 34862306a36Sopenharmony_ci qdev->ram_header->update_surface = surface_id; 34962306a36Sopenharmony_ci ret = wait_for_io_cmd_user(qdev, 0, QXL_IO_UPDATE_AREA_ASYNC, true); 35062306a36Sopenharmony_ci mutex_unlock(&qdev->update_area_mutex); 35162306a36Sopenharmony_ci return ret; 35262306a36Sopenharmony_ci} 35362306a36Sopenharmony_ci 35462306a36Sopenharmony_civoid qxl_io_notify_oom(struct qxl_device *qdev) 35562306a36Sopenharmony_ci{ 35662306a36Sopenharmony_ci outb(0, qdev->io_base + QXL_IO_NOTIFY_OOM); 35762306a36Sopenharmony_ci} 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_civoid qxl_io_flush_release(struct qxl_device *qdev) 36062306a36Sopenharmony_ci{ 36162306a36Sopenharmony_ci outb(0, qdev->io_base + QXL_IO_FLUSH_RELEASE); 36262306a36Sopenharmony_ci} 36362306a36Sopenharmony_ci 36462306a36Sopenharmony_civoid qxl_io_flush_surfaces(struct qxl_device *qdev) 36562306a36Sopenharmony_ci{ 36662306a36Sopenharmony_ci wait_for_io_cmd(qdev, 0, QXL_IO_FLUSH_SURFACES_ASYNC); 36762306a36Sopenharmony_ci} 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_civoid qxl_io_destroy_primary(struct qxl_device *qdev) 37062306a36Sopenharmony_ci{ 37162306a36Sopenharmony_ci wait_for_io_cmd(qdev, 0, QXL_IO_DESTROY_PRIMARY_ASYNC); 37262306a36Sopenharmony_ci qdev->primary_bo->is_primary = false; 37362306a36Sopenharmony_ci drm_gem_object_put(&qdev->primary_bo->tbo.base); 37462306a36Sopenharmony_ci qdev->primary_bo = NULL; 37562306a36Sopenharmony_ci} 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_civoid qxl_io_create_primary(struct qxl_device *qdev, struct qxl_bo *bo) 37862306a36Sopenharmony_ci{ 37962306a36Sopenharmony_ci struct qxl_surface_create *create; 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_ci if (WARN_ON(qdev->primary_bo)) 38262306a36Sopenharmony_ci return; 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_ci DRM_DEBUG_DRIVER("qdev %p, ram_header %p\n", qdev, qdev->ram_header); 38562306a36Sopenharmony_ci create = &qdev->ram_header->create_surface; 38662306a36Sopenharmony_ci create->format = bo->surf.format; 38762306a36Sopenharmony_ci create->width = bo->surf.width; 38862306a36Sopenharmony_ci create->height = bo->surf.height; 38962306a36Sopenharmony_ci create->stride = bo->surf.stride; 39062306a36Sopenharmony_ci create->mem = qxl_bo_physical_address(qdev, bo, 0); 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_ci DRM_DEBUG_DRIVER("mem = %llx, from %p\n", create->mem, bo->kptr); 39362306a36Sopenharmony_ci 39462306a36Sopenharmony_ci create->flags = QXL_SURF_FLAG_KEEP_DATA; 39562306a36Sopenharmony_ci create->type = QXL_SURF_TYPE_PRIMARY; 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_ci wait_for_io_cmd(qdev, 0, QXL_IO_CREATE_PRIMARY_ASYNC); 39862306a36Sopenharmony_ci qdev->primary_bo = bo; 39962306a36Sopenharmony_ci qdev->primary_bo->is_primary = true; 40062306a36Sopenharmony_ci drm_gem_object_get(&qdev->primary_bo->tbo.base); 40162306a36Sopenharmony_ci} 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_civoid qxl_io_memslot_add(struct qxl_device *qdev, uint8_t id) 40462306a36Sopenharmony_ci{ 40562306a36Sopenharmony_ci DRM_DEBUG_DRIVER("qxl_memslot_add %d\n", id); 40662306a36Sopenharmony_ci wait_for_io_cmd(qdev, id, QXL_IO_MEMSLOT_ADD_ASYNC); 40762306a36Sopenharmony_ci} 40862306a36Sopenharmony_ci 40962306a36Sopenharmony_civoid qxl_io_reset(struct qxl_device *qdev) 41062306a36Sopenharmony_ci{ 41162306a36Sopenharmony_ci outb(0, qdev->io_base + QXL_IO_RESET); 41262306a36Sopenharmony_ci} 41362306a36Sopenharmony_ci 41462306a36Sopenharmony_civoid qxl_io_monitors_config(struct qxl_device *qdev) 41562306a36Sopenharmony_ci{ 41662306a36Sopenharmony_ci wait_for_io_cmd(qdev, 0, QXL_IO_MONITORS_CONFIG_ASYNC); 41762306a36Sopenharmony_ci} 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_ciint qxl_surface_id_alloc(struct qxl_device *qdev, 42062306a36Sopenharmony_ci struct qxl_bo *surf) 42162306a36Sopenharmony_ci{ 42262306a36Sopenharmony_ci uint32_t handle; 42362306a36Sopenharmony_ci int idr_ret; 42462306a36Sopenharmony_ci int count = 0; 42562306a36Sopenharmony_ciagain: 42662306a36Sopenharmony_ci idr_preload(GFP_ATOMIC); 42762306a36Sopenharmony_ci spin_lock(&qdev->surf_id_idr_lock); 42862306a36Sopenharmony_ci idr_ret = idr_alloc(&qdev->surf_id_idr, NULL, 1, 0, GFP_NOWAIT); 42962306a36Sopenharmony_ci spin_unlock(&qdev->surf_id_idr_lock); 43062306a36Sopenharmony_ci idr_preload_end(); 43162306a36Sopenharmony_ci if (idr_ret < 0) 43262306a36Sopenharmony_ci return idr_ret; 43362306a36Sopenharmony_ci handle = idr_ret; 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_ci if (handle >= qdev->rom->n_surfaces) { 43662306a36Sopenharmony_ci count++; 43762306a36Sopenharmony_ci spin_lock(&qdev->surf_id_idr_lock); 43862306a36Sopenharmony_ci idr_remove(&qdev->surf_id_idr, handle); 43962306a36Sopenharmony_ci spin_unlock(&qdev->surf_id_idr_lock); 44062306a36Sopenharmony_ci qxl_reap_surface_id(qdev, 2); 44162306a36Sopenharmony_ci goto again; 44262306a36Sopenharmony_ci } 44362306a36Sopenharmony_ci surf->surface_id = handle; 44462306a36Sopenharmony_ci 44562306a36Sopenharmony_ci spin_lock(&qdev->surf_id_idr_lock); 44662306a36Sopenharmony_ci qdev->last_alloced_surf_id = handle; 44762306a36Sopenharmony_ci spin_unlock(&qdev->surf_id_idr_lock); 44862306a36Sopenharmony_ci return 0; 44962306a36Sopenharmony_ci} 45062306a36Sopenharmony_ci 45162306a36Sopenharmony_civoid qxl_surface_id_dealloc(struct qxl_device *qdev, 45262306a36Sopenharmony_ci uint32_t surface_id) 45362306a36Sopenharmony_ci{ 45462306a36Sopenharmony_ci spin_lock(&qdev->surf_id_idr_lock); 45562306a36Sopenharmony_ci idr_remove(&qdev->surf_id_idr, surface_id); 45662306a36Sopenharmony_ci spin_unlock(&qdev->surf_id_idr_lock); 45762306a36Sopenharmony_ci} 45862306a36Sopenharmony_ci 45962306a36Sopenharmony_ciint qxl_hw_surface_alloc(struct qxl_device *qdev, 46062306a36Sopenharmony_ci struct qxl_bo *surf) 46162306a36Sopenharmony_ci{ 46262306a36Sopenharmony_ci struct qxl_surface_cmd *cmd; 46362306a36Sopenharmony_ci struct qxl_release *release; 46462306a36Sopenharmony_ci int ret; 46562306a36Sopenharmony_ci 46662306a36Sopenharmony_ci if (surf->hw_surf_alloc) 46762306a36Sopenharmony_ci return 0; 46862306a36Sopenharmony_ci 46962306a36Sopenharmony_ci ret = qxl_alloc_surface_release_reserved(qdev, QXL_SURFACE_CMD_CREATE, 47062306a36Sopenharmony_ci NULL, 47162306a36Sopenharmony_ci &release); 47262306a36Sopenharmony_ci if (ret) 47362306a36Sopenharmony_ci return ret; 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_ci ret = qxl_release_reserve_list(release, true); 47662306a36Sopenharmony_ci if (ret) { 47762306a36Sopenharmony_ci qxl_release_free(qdev, release); 47862306a36Sopenharmony_ci return ret; 47962306a36Sopenharmony_ci } 48062306a36Sopenharmony_ci cmd = (struct qxl_surface_cmd *)qxl_release_map(qdev, release); 48162306a36Sopenharmony_ci cmd->type = QXL_SURFACE_CMD_CREATE; 48262306a36Sopenharmony_ci cmd->flags = QXL_SURF_FLAG_KEEP_DATA; 48362306a36Sopenharmony_ci cmd->u.surface_create.format = surf->surf.format; 48462306a36Sopenharmony_ci cmd->u.surface_create.width = surf->surf.width; 48562306a36Sopenharmony_ci cmd->u.surface_create.height = surf->surf.height; 48662306a36Sopenharmony_ci cmd->u.surface_create.stride = surf->surf.stride; 48762306a36Sopenharmony_ci cmd->u.surface_create.data = qxl_bo_physical_address(qdev, surf, 0); 48862306a36Sopenharmony_ci cmd->surface_id = surf->surface_id; 48962306a36Sopenharmony_ci qxl_release_unmap(qdev, release, &cmd->release_info); 49062306a36Sopenharmony_ci 49162306a36Sopenharmony_ci surf->surf_create = release; 49262306a36Sopenharmony_ci 49362306a36Sopenharmony_ci /* no need to add a release to the fence for this surface bo, 49462306a36Sopenharmony_ci since it is only released when we ask to destroy the surface 49562306a36Sopenharmony_ci and it would never signal otherwise */ 49662306a36Sopenharmony_ci qxl_release_fence_buffer_objects(release); 49762306a36Sopenharmony_ci qxl_push_command_ring_release(qdev, release, QXL_CMD_SURFACE, false); 49862306a36Sopenharmony_ci 49962306a36Sopenharmony_ci surf->hw_surf_alloc = true; 50062306a36Sopenharmony_ci spin_lock(&qdev->surf_id_idr_lock); 50162306a36Sopenharmony_ci idr_replace(&qdev->surf_id_idr, surf, surf->surface_id); 50262306a36Sopenharmony_ci spin_unlock(&qdev->surf_id_idr_lock); 50362306a36Sopenharmony_ci return 0; 50462306a36Sopenharmony_ci} 50562306a36Sopenharmony_ci 50662306a36Sopenharmony_ciint qxl_hw_surface_dealloc(struct qxl_device *qdev, 50762306a36Sopenharmony_ci struct qxl_bo *surf) 50862306a36Sopenharmony_ci{ 50962306a36Sopenharmony_ci struct qxl_surface_cmd *cmd; 51062306a36Sopenharmony_ci struct qxl_release *release; 51162306a36Sopenharmony_ci int ret; 51262306a36Sopenharmony_ci int id; 51362306a36Sopenharmony_ci 51462306a36Sopenharmony_ci if (!surf->hw_surf_alloc) 51562306a36Sopenharmony_ci return 0; 51662306a36Sopenharmony_ci 51762306a36Sopenharmony_ci ret = qxl_alloc_surface_release_reserved(qdev, QXL_SURFACE_CMD_DESTROY, 51862306a36Sopenharmony_ci surf->surf_create, 51962306a36Sopenharmony_ci &release); 52062306a36Sopenharmony_ci if (ret) 52162306a36Sopenharmony_ci return ret; 52262306a36Sopenharmony_ci 52362306a36Sopenharmony_ci surf->surf_create = NULL; 52462306a36Sopenharmony_ci /* remove the surface from the idr, but not the surface id yet */ 52562306a36Sopenharmony_ci spin_lock(&qdev->surf_id_idr_lock); 52662306a36Sopenharmony_ci idr_replace(&qdev->surf_id_idr, NULL, surf->surface_id); 52762306a36Sopenharmony_ci spin_unlock(&qdev->surf_id_idr_lock); 52862306a36Sopenharmony_ci surf->hw_surf_alloc = false; 52962306a36Sopenharmony_ci 53062306a36Sopenharmony_ci id = surf->surface_id; 53162306a36Sopenharmony_ci surf->surface_id = 0; 53262306a36Sopenharmony_ci 53362306a36Sopenharmony_ci release->surface_release_id = id; 53462306a36Sopenharmony_ci cmd = (struct qxl_surface_cmd *)qxl_release_map(qdev, release); 53562306a36Sopenharmony_ci cmd->type = QXL_SURFACE_CMD_DESTROY; 53662306a36Sopenharmony_ci cmd->surface_id = id; 53762306a36Sopenharmony_ci qxl_release_unmap(qdev, release, &cmd->release_info); 53862306a36Sopenharmony_ci 53962306a36Sopenharmony_ci qxl_release_fence_buffer_objects(release); 54062306a36Sopenharmony_ci qxl_push_command_ring_release(qdev, release, QXL_CMD_SURFACE, false); 54162306a36Sopenharmony_ci 54262306a36Sopenharmony_ci return 0; 54362306a36Sopenharmony_ci} 54462306a36Sopenharmony_ci 54562306a36Sopenharmony_cistatic int qxl_update_surface(struct qxl_device *qdev, struct qxl_bo *surf) 54662306a36Sopenharmony_ci{ 54762306a36Sopenharmony_ci struct qxl_rect rect; 54862306a36Sopenharmony_ci int ret; 54962306a36Sopenharmony_ci 55062306a36Sopenharmony_ci /* if we are evicting, we need to make sure the surface is up 55162306a36Sopenharmony_ci to date */ 55262306a36Sopenharmony_ci rect.left = 0; 55362306a36Sopenharmony_ci rect.right = surf->surf.width; 55462306a36Sopenharmony_ci rect.top = 0; 55562306a36Sopenharmony_ci rect.bottom = surf->surf.height; 55662306a36Sopenharmony_ciretry: 55762306a36Sopenharmony_ci ret = qxl_io_update_area(qdev, surf, &rect); 55862306a36Sopenharmony_ci if (ret == -ERESTARTSYS) 55962306a36Sopenharmony_ci goto retry; 56062306a36Sopenharmony_ci return ret; 56162306a36Sopenharmony_ci} 56262306a36Sopenharmony_ci 56362306a36Sopenharmony_cistatic void qxl_surface_evict_locked(struct qxl_device *qdev, struct qxl_bo *surf, bool do_update_area) 56462306a36Sopenharmony_ci{ 56562306a36Sopenharmony_ci /* no need to update area if we are just freeing the surface normally */ 56662306a36Sopenharmony_ci if (do_update_area) 56762306a36Sopenharmony_ci qxl_update_surface(qdev, surf); 56862306a36Sopenharmony_ci 56962306a36Sopenharmony_ci /* nuke the surface id at the hw */ 57062306a36Sopenharmony_ci qxl_hw_surface_dealloc(qdev, surf); 57162306a36Sopenharmony_ci} 57262306a36Sopenharmony_ci 57362306a36Sopenharmony_civoid qxl_surface_evict(struct qxl_device *qdev, struct qxl_bo *surf, bool do_update_area) 57462306a36Sopenharmony_ci{ 57562306a36Sopenharmony_ci mutex_lock(&qdev->surf_evict_mutex); 57662306a36Sopenharmony_ci qxl_surface_evict_locked(qdev, surf, do_update_area); 57762306a36Sopenharmony_ci mutex_unlock(&qdev->surf_evict_mutex); 57862306a36Sopenharmony_ci} 57962306a36Sopenharmony_ci 58062306a36Sopenharmony_cistatic int qxl_reap_surf(struct qxl_device *qdev, struct qxl_bo *surf, bool stall) 58162306a36Sopenharmony_ci{ 58262306a36Sopenharmony_ci long ret; 58362306a36Sopenharmony_ci 58462306a36Sopenharmony_ci ret = qxl_bo_reserve(surf); 58562306a36Sopenharmony_ci if (ret) 58662306a36Sopenharmony_ci return ret; 58762306a36Sopenharmony_ci 58862306a36Sopenharmony_ci if (stall) 58962306a36Sopenharmony_ci mutex_unlock(&qdev->surf_evict_mutex); 59062306a36Sopenharmony_ci 59162306a36Sopenharmony_ci if (stall) { 59262306a36Sopenharmony_ci ret = dma_resv_wait_timeout(surf->tbo.base.resv, 59362306a36Sopenharmony_ci DMA_RESV_USAGE_BOOKKEEP, true, 59462306a36Sopenharmony_ci 15 * HZ); 59562306a36Sopenharmony_ci if (ret > 0) 59662306a36Sopenharmony_ci ret = 0; 59762306a36Sopenharmony_ci else if (ret == 0) 59862306a36Sopenharmony_ci ret = -EBUSY; 59962306a36Sopenharmony_ci } else { 60062306a36Sopenharmony_ci ret = dma_resv_test_signaled(surf->tbo.base.resv, 60162306a36Sopenharmony_ci DMA_RESV_USAGE_BOOKKEEP); 60262306a36Sopenharmony_ci ret = ret ? -EBUSY : 0; 60362306a36Sopenharmony_ci } 60462306a36Sopenharmony_ci 60562306a36Sopenharmony_ci if (stall) 60662306a36Sopenharmony_ci mutex_lock(&qdev->surf_evict_mutex); 60762306a36Sopenharmony_ci if (ret) { 60862306a36Sopenharmony_ci qxl_bo_unreserve(surf); 60962306a36Sopenharmony_ci return ret; 61062306a36Sopenharmony_ci } 61162306a36Sopenharmony_ci 61262306a36Sopenharmony_ci qxl_surface_evict_locked(qdev, surf, true); 61362306a36Sopenharmony_ci qxl_bo_unreserve(surf); 61462306a36Sopenharmony_ci return 0; 61562306a36Sopenharmony_ci} 61662306a36Sopenharmony_ci 61762306a36Sopenharmony_cistatic int qxl_reap_surface_id(struct qxl_device *qdev, int max_to_reap) 61862306a36Sopenharmony_ci{ 61962306a36Sopenharmony_ci int num_reaped = 0; 62062306a36Sopenharmony_ci int i, ret; 62162306a36Sopenharmony_ci bool stall = false; 62262306a36Sopenharmony_ci int start = 0; 62362306a36Sopenharmony_ci 62462306a36Sopenharmony_ci mutex_lock(&qdev->surf_evict_mutex); 62562306a36Sopenharmony_ciagain: 62662306a36Sopenharmony_ci 62762306a36Sopenharmony_ci spin_lock(&qdev->surf_id_idr_lock); 62862306a36Sopenharmony_ci start = qdev->last_alloced_surf_id + 1; 62962306a36Sopenharmony_ci spin_unlock(&qdev->surf_id_idr_lock); 63062306a36Sopenharmony_ci 63162306a36Sopenharmony_ci for (i = start; i < start + qdev->rom->n_surfaces; i++) { 63262306a36Sopenharmony_ci void *objptr; 63362306a36Sopenharmony_ci int surfid = i % qdev->rom->n_surfaces; 63462306a36Sopenharmony_ci 63562306a36Sopenharmony_ci /* this avoids the case where the objects is in the 63662306a36Sopenharmony_ci idr but has been evicted half way - its makes 63762306a36Sopenharmony_ci the idr lookup atomic with the eviction */ 63862306a36Sopenharmony_ci spin_lock(&qdev->surf_id_idr_lock); 63962306a36Sopenharmony_ci objptr = idr_find(&qdev->surf_id_idr, surfid); 64062306a36Sopenharmony_ci spin_unlock(&qdev->surf_id_idr_lock); 64162306a36Sopenharmony_ci 64262306a36Sopenharmony_ci if (!objptr) 64362306a36Sopenharmony_ci continue; 64462306a36Sopenharmony_ci 64562306a36Sopenharmony_ci ret = qxl_reap_surf(qdev, objptr, stall); 64662306a36Sopenharmony_ci if (ret == 0) 64762306a36Sopenharmony_ci num_reaped++; 64862306a36Sopenharmony_ci if (num_reaped >= max_to_reap) 64962306a36Sopenharmony_ci break; 65062306a36Sopenharmony_ci } 65162306a36Sopenharmony_ci if (num_reaped == 0 && stall == false) { 65262306a36Sopenharmony_ci stall = true; 65362306a36Sopenharmony_ci goto again; 65462306a36Sopenharmony_ci } 65562306a36Sopenharmony_ci 65662306a36Sopenharmony_ci mutex_unlock(&qdev->surf_evict_mutex); 65762306a36Sopenharmony_ci if (num_reaped) { 65862306a36Sopenharmony_ci usleep_range(500, 1000); 65962306a36Sopenharmony_ci qxl_queue_garbage_collect(qdev, true); 66062306a36Sopenharmony_ci } 66162306a36Sopenharmony_ci 66262306a36Sopenharmony_ci return 0; 66362306a36Sopenharmony_ci} 664