mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-11-01 17:08:10 +00:00
Merge branch 'qxl-fixes' of git://people.freedesktop.org/~airlied/linux
Pull qxl drm fixes from Dave Airlie: "Okay as I warned, the qxl driver was running a bit free and loose with its ttm object reservations and the new lockdep enabled reservation tracking shone a bright light into it, it also with the new reservations mutexes hits a possible deadlock during boot. The first patch is a real fix to render the console correctly as the driver used to just drop irq renderering as too hard, this also fixes a sleeping while atomic warning. The other two patches are the big ugly ones that redo how the driver allocates objects and reserves them and makes things all work properly, I've tested this in a VM, and compared to the current code which hits a lockdep warning and the sleep while atomic warning before failing. So sorry this is coming in late, I should have tested qxl before merging the mutex code, but I'd rather just fix qxl with this than revert the reservations code at this point" * 'qxl-fixes' of git://people.freedesktop.org/~airlied/linux: qxl: convert qxl driver to proper use for reservations qxl: allow creation of pre-pinned objects and use for releases. drm/qxl: add delayed fb operations
This commit is contained in:
commit
2408c2e529
13 changed files with 867 additions and 516 deletions
|
@ -179,9 +179,10 @@ qxl_push_command_ring_release(struct qxl_device *qdev, struct qxl_release *relea
|
||||||
uint32_t type, bool interruptible)
|
uint32_t type, bool interruptible)
|
||||||
{
|
{
|
||||||
struct qxl_command cmd;
|
struct qxl_command cmd;
|
||||||
|
struct qxl_bo_list *entry = list_first_entry(&release->bos, struct qxl_bo_list, tv.head);
|
||||||
|
|
||||||
cmd.type = type;
|
cmd.type = type;
|
||||||
cmd.data = qxl_bo_physical_address(qdev, release->bos[0], release->release_offset);
|
cmd.data = qxl_bo_physical_address(qdev, to_qxl_bo(entry->tv.bo), release->release_offset);
|
||||||
|
|
||||||
return qxl_ring_push(qdev->command_ring, &cmd, interruptible);
|
return qxl_ring_push(qdev->command_ring, &cmd, interruptible);
|
||||||
}
|
}
|
||||||
|
@ -191,9 +192,10 @@ qxl_push_cursor_ring_release(struct qxl_device *qdev, struct qxl_release *releas
|
||||||
uint32_t type, bool interruptible)
|
uint32_t type, bool interruptible)
|
||||||
{
|
{
|
||||||
struct qxl_command cmd;
|
struct qxl_command cmd;
|
||||||
|
struct qxl_bo_list *entry = list_first_entry(&release->bos, struct qxl_bo_list, tv.head);
|
||||||
|
|
||||||
cmd.type = type;
|
cmd.type = type;
|
||||||
cmd.data = qxl_bo_physical_address(qdev, release->bos[0], release->release_offset);
|
cmd.data = qxl_bo_physical_address(qdev, to_qxl_bo(entry->tv.bo), release->release_offset);
|
||||||
|
|
||||||
return qxl_ring_push(qdev->cursor_ring, &cmd, interruptible);
|
return qxl_ring_push(qdev->cursor_ring, &cmd, interruptible);
|
||||||
}
|
}
|
||||||
|
@ -214,7 +216,6 @@ int qxl_garbage_collect(struct qxl_device *qdev)
|
||||||
struct qxl_release *release;
|
struct qxl_release *release;
|
||||||
uint64_t id, next_id;
|
uint64_t id, next_id;
|
||||||
int i = 0;
|
int i = 0;
|
||||||
int ret;
|
|
||||||
union qxl_release_info *info;
|
union qxl_release_info *info;
|
||||||
|
|
||||||
while (qxl_ring_pop(qdev->release_ring, &id)) {
|
while (qxl_ring_pop(qdev->release_ring, &id)) {
|
||||||
|
@ -224,17 +225,10 @@ int qxl_garbage_collect(struct qxl_device *qdev)
|
||||||
if (release == NULL)
|
if (release == NULL)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
ret = qxl_release_reserve(qdev, release, false);
|
|
||||||
if (ret) {
|
|
||||||
qxl_io_log(qdev, "failed to reserve release on garbage collect %lld\n", id);
|
|
||||||
DRM_ERROR("failed to reserve release %lld\n", id);
|
|
||||||
}
|
|
||||||
|
|
||||||
info = qxl_release_map(qdev, release);
|
info = qxl_release_map(qdev, release);
|
||||||
next_id = info->next;
|
next_id = info->next;
|
||||||
qxl_release_unmap(qdev, release, info);
|
qxl_release_unmap(qdev, release, info);
|
||||||
|
|
||||||
qxl_release_unreserve(qdev, release);
|
|
||||||
QXL_INFO(qdev, "popped %lld, next %lld\n", id,
|
QXL_INFO(qdev, "popped %lld, next %lld\n", id,
|
||||||
next_id);
|
next_id);
|
||||||
|
|
||||||
|
@ -259,27 +253,29 @@ int qxl_garbage_collect(struct qxl_device *qdev)
|
||||||
return i;
|
return i;
|
||||||
}
|
}
|
||||||
|
|
||||||
int qxl_alloc_bo_reserved(struct qxl_device *qdev, unsigned long size,
|
int qxl_alloc_bo_reserved(struct qxl_device *qdev,
|
||||||
|
struct qxl_release *release,
|
||||||
|
unsigned long size,
|
||||||
struct qxl_bo **_bo)
|
struct qxl_bo **_bo)
|
||||||
{
|
{
|
||||||
struct qxl_bo *bo;
|
struct qxl_bo *bo;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
ret = qxl_bo_create(qdev, size, false /* not kernel - device */,
|
ret = qxl_bo_create(qdev, size, false /* not kernel - device */,
|
||||||
QXL_GEM_DOMAIN_VRAM, NULL, &bo);
|
false, QXL_GEM_DOMAIN_VRAM, NULL, &bo);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
DRM_ERROR("failed to allocate VRAM BO\n");
|
DRM_ERROR("failed to allocate VRAM BO\n");
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
ret = qxl_bo_reserve(bo, false);
|
ret = qxl_release_list_add(release, bo);
|
||||||
if (unlikely(ret != 0))
|
if (ret)
|
||||||
goto out_unref;
|
goto out_unref;
|
||||||
|
|
||||||
*_bo = bo;
|
*_bo = bo;
|
||||||
return 0;
|
return 0;
|
||||||
out_unref:
|
out_unref:
|
||||||
qxl_bo_unref(&bo);
|
qxl_bo_unref(&bo);
|
||||||
return 0;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int wait_for_io_cmd_user(struct qxl_device *qdev, uint8_t val, long port, bool intr)
|
static int wait_for_io_cmd_user(struct qxl_device *qdev, uint8_t val, long port, bool intr)
|
||||||
|
@ -503,6 +499,10 @@ int qxl_hw_surface_alloc(struct qxl_device *qdev,
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
|
ret = qxl_release_reserve_list(release, true);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
cmd = (struct qxl_surface_cmd *)qxl_release_map(qdev, release);
|
cmd = (struct qxl_surface_cmd *)qxl_release_map(qdev, release);
|
||||||
cmd->type = QXL_SURFACE_CMD_CREATE;
|
cmd->type = QXL_SURFACE_CMD_CREATE;
|
||||||
cmd->u.surface_create.format = surf->surf.format;
|
cmd->u.surface_create.format = surf->surf.format;
|
||||||
|
@ -524,14 +524,11 @@ int qxl_hw_surface_alloc(struct qxl_device *qdev,
|
||||||
|
|
||||||
surf->surf_create = release;
|
surf->surf_create = release;
|
||||||
|
|
||||||
/* no need to add a release to the fence for this bo,
|
/* no need to add a release to the fence for this surface bo,
|
||||||
since it is only released when we ask to destroy the surface
|
since it is only released when we ask to destroy the surface
|
||||||
and it would never signal otherwise */
|
and it would never signal otherwise */
|
||||||
qxl_fence_releaseable(qdev, release);
|
|
||||||
|
|
||||||
qxl_push_command_ring_release(qdev, release, QXL_CMD_SURFACE, false);
|
qxl_push_command_ring_release(qdev, release, QXL_CMD_SURFACE, false);
|
||||||
|
qxl_release_fence_buffer_objects(release);
|
||||||
qxl_release_unreserve(qdev, release);
|
|
||||||
|
|
||||||
surf->hw_surf_alloc = true;
|
surf->hw_surf_alloc = true;
|
||||||
spin_lock(&qdev->surf_id_idr_lock);
|
spin_lock(&qdev->surf_id_idr_lock);
|
||||||
|
@ -573,12 +570,9 @@ int qxl_hw_surface_dealloc(struct qxl_device *qdev,
|
||||||
cmd->surface_id = id;
|
cmd->surface_id = id;
|
||||||
qxl_release_unmap(qdev, release, &cmd->release_info);
|
qxl_release_unmap(qdev, release, &cmd->release_info);
|
||||||
|
|
||||||
qxl_fence_releaseable(qdev, release);
|
|
||||||
|
|
||||||
qxl_push_command_ring_release(qdev, release, QXL_CMD_SURFACE, false);
|
qxl_push_command_ring_release(qdev, release, QXL_CMD_SURFACE, false);
|
||||||
|
|
||||||
qxl_release_unreserve(qdev, release);
|
qxl_release_fence_buffer_objects(release);
|
||||||
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -179,7 +179,7 @@ static void qxl_crtc_destroy(struct drm_crtc *crtc)
|
||||||
kfree(qxl_crtc);
|
kfree(qxl_crtc);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static int
|
||||||
qxl_hide_cursor(struct qxl_device *qdev)
|
qxl_hide_cursor(struct qxl_device *qdev)
|
||||||
{
|
{
|
||||||
struct qxl_release *release;
|
struct qxl_release *release;
|
||||||
|
@ -188,14 +188,22 @@ qxl_hide_cursor(struct qxl_device *qdev)
|
||||||
|
|
||||||
ret = qxl_alloc_release_reserved(qdev, sizeof(*cmd), QXL_RELEASE_CURSOR_CMD,
|
ret = qxl_alloc_release_reserved(qdev, sizeof(*cmd), QXL_RELEASE_CURSOR_CMD,
|
||||||
&release, NULL);
|
&release, NULL);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
ret = qxl_release_reserve_list(release, true);
|
||||||
|
if (ret) {
|
||||||
|
qxl_release_free(qdev, release);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
cmd = (struct qxl_cursor_cmd *)qxl_release_map(qdev, release);
|
cmd = (struct qxl_cursor_cmd *)qxl_release_map(qdev, release);
|
||||||
cmd->type = QXL_CURSOR_HIDE;
|
cmd->type = QXL_CURSOR_HIDE;
|
||||||
qxl_release_unmap(qdev, release, &cmd->release_info);
|
qxl_release_unmap(qdev, release, &cmd->release_info);
|
||||||
|
|
||||||
qxl_fence_releaseable(qdev, release);
|
|
||||||
qxl_push_cursor_ring_release(qdev, release, QXL_CMD_CURSOR, false);
|
qxl_push_cursor_ring_release(qdev, release, QXL_CMD_CURSOR, false);
|
||||||
qxl_release_unreserve(qdev, release);
|
qxl_release_fence_buffer_objects(release);
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int qxl_crtc_cursor_set2(struct drm_crtc *crtc,
|
static int qxl_crtc_cursor_set2(struct drm_crtc *crtc,
|
||||||
|
@ -216,10 +224,8 @@ static int qxl_crtc_cursor_set2(struct drm_crtc *crtc,
|
||||||
|
|
||||||
int size = 64*64*4;
|
int size = 64*64*4;
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
if (!handle) {
|
if (!handle)
|
||||||
qxl_hide_cursor(qdev);
|
return qxl_hide_cursor(qdev);
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
obj = drm_gem_object_lookup(crtc->dev, file_priv, handle);
|
obj = drm_gem_object_lookup(crtc->dev, file_priv, handle);
|
||||||
if (!obj) {
|
if (!obj) {
|
||||||
|
@ -234,8 +240,9 @@ static int qxl_crtc_cursor_set2(struct drm_crtc *crtc,
|
||||||
goto out_unref;
|
goto out_unref;
|
||||||
|
|
||||||
ret = qxl_bo_pin(user_bo, QXL_GEM_DOMAIN_CPU, NULL);
|
ret = qxl_bo_pin(user_bo, QXL_GEM_DOMAIN_CPU, NULL);
|
||||||
|
qxl_bo_unreserve(user_bo);
|
||||||
if (ret)
|
if (ret)
|
||||||
goto out_unreserve;
|
goto out_unref;
|
||||||
|
|
||||||
ret = qxl_bo_kmap(user_bo, &user_ptr);
|
ret = qxl_bo_kmap(user_bo, &user_ptr);
|
||||||
if (ret)
|
if (ret)
|
||||||
|
@ -246,14 +253,20 @@ static int qxl_crtc_cursor_set2(struct drm_crtc *crtc,
|
||||||
&release, NULL);
|
&release, NULL);
|
||||||
if (ret)
|
if (ret)
|
||||||
goto out_kunmap;
|
goto out_kunmap;
|
||||||
ret = qxl_alloc_bo_reserved(qdev, sizeof(struct qxl_cursor) + size,
|
|
||||||
&cursor_bo);
|
ret = qxl_alloc_bo_reserved(qdev, release, sizeof(struct qxl_cursor) + size,
|
||||||
|
&cursor_bo);
|
||||||
if (ret)
|
if (ret)
|
||||||
goto out_free_release;
|
goto out_free_release;
|
||||||
ret = qxl_bo_kmap(cursor_bo, (void **)&cursor);
|
|
||||||
|
ret = qxl_release_reserve_list(release, false);
|
||||||
if (ret)
|
if (ret)
|
||||||
goto out_free_bo;
|
goto out_free_bo;
|
||||||
|
|
||||||
|
ret = qxl_bo_kmap(cursor_bo, (void **)&cursor);
|
||||||
|
if (ret)
|
||||||
|
goto out_backoff;
|
||||||
|
|
||||||
cursor->header.unique = 0;
|
cursor->header.unique = 0;
|
||||||
cursor->header.type = SPICE_CURSOR_TYPE_ALPHA;
|
cursor->header.type = SPICE_CURSOR_TYPE_ALPHA;
|
||||||
cursor->header.width = 64;
|
cursor->header.width = 64;
|
||||||
|
@ -269,11 +282,7 @@ static int qxl_crtc_cursor_set2(struct drm_crtc *crtc,
|
||||||
|
|
||||||
qxl_bo_kunmap(cursor_bo);
|
qxl_bo_kunmap(cursor_bo);
|
||||||
|
|
||||||
/* finish with the userspace bo */
|
|
||||||
qxl_bo_kunmap(user_bo);
|
qxl_bo_kunmap(user_bo);
|
||||||
qxl_bo_unpin(user_bo);
|
|
||||||
qxl_bo_unreserve(user_bo);
|
|
||||||
drm_gem_object_unreference_unlocked(obj);
|
|
||||||
|
|
||||||
cmd = (struct qxl_cursor_cmd *)qxl_release_map(qdev, release);
|
cmd = (struct qxl_cursor_cmd *)qxl_release_map(qdev, release);
|
||||||
cmd->type = QXL_CURSOR_SET;
|
cmd->type = QXL_CURSOR_SET;
|
||||||
|
@ -281,30 +290,35 @@ static int qxl_crtc_cursor_set2(struct drm_crtc *crtc,
|
||||||
cmd->u.set.position.y = qcrtc->cur_y;
|
cmd->u.set.position.y = qcrtc->cur_y;
|
||||||
|
|
||||||
cmd->u.set.shape = qxl_bo_physical_address(qdev, cursor_bo, 0);
|
cmd->u.set.shape = qxl_bo_physical_address(qdev, cursor_bo, 0);
|
||||||
qxl_release_add_res(qdev, release, cursor_bo);
|
|
||||||
|
|
||||||
cmd->u.set.visible = 1;
|
cmd->u.set.visible = 1;
|
||||||
qxl_release_unmap(qdev, release, &cmd->release_info);
|
qxl_release_unmap(qdev, release, &cmd->release_info);
|
||||||
|
|
||||||
qxl_fence_releaseable(qdev, release);
|
|
||||||
qxl_push_cursor_ring_release(qdev, release, QXL_CMD_CURSOR, false);
|
qxl_push_cursor_ring_release(qdev, release, QXL_CMD_CURSOR, false);
|
||||||
qxl_release_unreserve(qdev, release);
|
qxl_release_fence_buffer_objects(release);
|
||||||
|
|
||||||
|
/* finish with the userspace bo */
|
||||||
|
ret = qxl_bo_reserve(user_bo, false);
|
||||||
|
if (!ret) {
|
||||||
|
qxl_bo_unpin(user_bo);
|
||||||
|
qxl_bo_unreserve(user_bo);
|
||||||
|
}
|
||||||
|
drm_gem_object_unreference_unlocked(obj);
|
||||||
|
|
||||||
qxl_bo_unreserve(cursor_bo);
|
|
||||||
qxl_bo_unref(&cursor_bo);
|
qxl_bo_unref(&cursor_bo);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
|
out_backoff:
|
||||||
|
qxl_release_backoff_reserve_list(release);
|
||||||
out_free_bo:
|
out_free_bo:
|
||||||
qxl_bo_unref(&cursor_bo);
|
qxl_bo_unref(&cursor_bo);
|
||||||
out_free_release:
|
out_free_release:
|
||||||
qxl_release_unreserve(qdev, release);
|
|
||||||
qxl_release_free(qdev, release);
|
qxl_release_free(qdev, release);
|
||||||
out_kunmap:
|
out_kunmap:
|
||||||
qxl_bo_kunmap(user_bo);
|
qxl_bo_kunmap(user_bo);
|
||||||
out_unpin:
|
out_unpin:
|
||||||
qxl_bo_unpin(user_bo);
|
qxl_bo_unpin(user_bo);
|
||||||
out_unreserve:
|
|
||||||
qxl_bo_unreserve(user_bo);
|
|
||||||
out_unref:
|
out_unref:
|
||||||
drm_gem_object_unreference_unlocked(obj);
|
drm_gem_object_unreference_unlocked(obj);
|
||||||
return ret;
|
return ret;
|
||||||
|
@ -322,6 +336,14 @@ static int qxl_crtc_cursor_move(struct drm_crtc *crtc,
|
||||||
|
|
||||||
ret = qxl_alloc_release_reserved(qdev, sizeof(*cmd), QXL_RELEASE_CURSOR_CMD,
|
ret = qxl_alloc_release_reserved(qdev, sizeof(*cmd), QXL_RELEASE_CURSOR_CMD,
|
||||||
&release, NULL);
|
&release, NULL);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
ret = qxl_release_reserve_list(release, true);
|
||||||
|
if (ret) {
|
||||||
|
qxl_release_free(qdev, release);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
qcrtc->cur_x = x;
|
qcrtc->cur_x = x;
|
||||||
qcrtc->cur_y = y;
|
qcrtc->cur_y = y;
|
||||||
|
@ -332,9 +354,9 @@ static int qxl_crtc_cursor_move(struct drm_crtc *crtc,
|
||||||
cmd->u.position.y = qcrtc->cur_y;
|
cmd->u.position.y = qcrtc->cur_y;
|
||||||
qxl_release_unmap(qdev, release, &cmd->release_info);
|
qxl_release_unmap(qdev, release, &cmd->release_info);
|
||||||
|
|
||||||
qxl_fence_releaseable(qdev, release);
|
|
||||||
qxl_push_cursor_ring_release(qdev, release, QXL_CMD_CURSOR, false);
|
qxl_push_cursor_ring_release(qdev, release, QXL_CMD_CURSOR, false);
|
||||||
qxl_release_unreserve(qdev, release);
|
qxl_release_fence_buffer_objects(release);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -23,25 +23,29 @@
|
||||||
#include "qxl_drv.h"
|
#include "qxl_drv.h"
|
||||||
#include "qxl_object.h"
|
#include "qxl_object.h"
|
||||||
|
|
||||||
|
static int alloc_clips(struct qxl_device *qdev,
|
||||||
|
struct qxl_release *release,
|
||||||
|
unsigned num_clips,
|
||||||
|
struct qxl_bo **clips_bo)
|
||||||
|
{
|
||||||
|
int size = sizeof(struct qxl_clip_rects) + sizeof(struct qxl_rect) * num_clips;
|
||||||
|
|
||||||
|
return qxl_alloc_bo_reserved(qdev, release, size, clips_bo);
|
||||||
|
}
|
||||||
|
|
||||||
/* returns a pointer to the already allocated qxl_rect array inside
|
/* returns a pointer to the already allocated qxl_rect array inside
|
||||||
* the qxl_clip_rects. This is *not* the same as the memory allocated
|
* the qxl_clip_rects. This is *not* the same as the memory allocated
|
||||||
* on the device, it is offset to qxl_clip_rects.chunk.data */
|
* on the device, it is offset to qxl_clip_rects.chunk.data */
|
||||||
static struct qxl_rect *drawable_set_clipping(struct qxl_device *qdev,
|
static struct qxl_rect *drawable_set_clipping(struct qxl_device *qdev,
|
||||||
struct qxl_drawable *drawable,
|
struct qxl_drawable *drawable,
|
||||||
unsigned num_clips,
|
unsigned num_clips,
|
||||||
struct qxl_bo **clips_bo,
|
struct qxl_bo *clips_bo)
|
||||||
struct qxl_release *release)
|
|
||||||
{
|
{
|
||||||
struct qxl_clip_rects *dev_clips;
|
struct qxl_clip_rects *dev_clips;
|
||||||
int ret;
|
int ret;
|
||||||
int size = sizeof(*dev_clips) + sizeof(struct qxl_rect) * num_clips;
|
|
||||||
ret = qxl_alloc_bo_reserved(qdev, size, clips_bo);
|
|
||||||
if (ret)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
ret = qxl_bo_kmap(*clips_bo, (void **)&dev_clips);
|
ret = qxl_bo_kmap(clips_bo, (void **)&dev_clips);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
qxl_bo_unref(clips_bo);
|
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
dev_clips->num_rects = num_clips;
|
dev_clips->num_rects = num_clips;
|
||||||
|
@ -52,20 +56,34 @@ static struct qxl_rect *drawable_set_clipping(struct qxl_device *qdev,
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
make_drawable(struct qxl_device *qdev, int surface, uint8_t type,
|
alloc_drawable(struct qxl_device *qdev, struct qxl_release **release)
|
||||||
const struct qxl_rect *rect,
|
|
||||||
struct qxl_release **release)
|
|
||||||
{
|
{
|
||||||
struct qxl_drawable *drawable;
|
int ret;
|
||||||
int i, ret;
|
ret = qxl_alloc_release_reserved(qdev, sizeof(struct qxl_drawable),
|
||||||
|
|
||||||
ret = qxl_alloc_release_reserved(qdev, sizeof(*drawable),
|
|
||||||
QXL_RELEASE_DRAWABLE, release,
|
QXL_RELEASE_DRAWABLE, release,
|
||||||
NULL);
|
NULL);
|
||||||
if (ret)
|
return ret;
|
||||||
return ret;
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
free_drawable(struct qxl_device *qdev, struct qxl_release *release)
|
||||||
|
{
|
||||||
|
qxl_release_free(qdev, release);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* release needs to be reserved at this point */
|
||||||
|
static int
|
||||||
|
make_drawable(struct qxl_device *qdev, int surface, uint8_t type,
|
||||||
|
const struct qxl_rect *rect,
|
||||||
|
struct qxl_release *release)
|
||||||
|
{
|
||||||
|
struct qxl_drawable *drawable;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
drawable = (struct qxl_drawable *)qxl_release_map(qdev, release);
|
||||||
|
if (!drawable)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
drawable = (struct qxl_drawable *)qxl_release_map(qdev, *release);
|
|
||||||
drawable->type = type;
|
drawable->type = type;
|
||||||
|
|
||||||
drawable->surface_id = surface; /* Only primary for now */
|
drawable->surface_id = surface; /* Only primary for now */
|
||||||
|
@ -91,14 +109,23 @@ make_drawable(struct qxl_device *qdev, int surface, uint8_t type,
|
||||||
drawable->bbox = *rect;
|
drawable->bbox = *rect;
|
||||||
|
|
||||||
drawable->mm_time = qdev->rom->mm_clock;
|
drawable->mm_time = qdev->rom->mm_clock;
|
||||||
qxl_release_unmap(qdev, *release, &drawable->release_info);
|
qxl_release_unmap(qdev, release, &drawable->release_info);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int qxl_palette_create_1bit(struct qxl_bo **palette_bo,
|
static int alloc_palette_object(struct qxl_device *qdev,
|
||||||
|
struct qxl_release *release,
|
||||||
|
struct qxl_bo **palette_bo)
|
||||||
|
{
|
||||||
|
return qxl_alloc_bo_reserved(qdev, release,
|
||||||
|
sizeof(struct qxl_palette) + sizeof(uint32_t) * 2,
|
||||||
|
palette_bo);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int qxl_palette_create_1bit(struct qxl_bo *palette_bo,
|
||||||
|
struct qxl_release *release,
|
||||||
const struct qxl_fb_image *qxl_fb_image)
|
const struct qxl_fb_image *qxl_fb_image)
|
||||||
{
|
{
|
||||||
struct qxl_device *qdev = qxl_fb_image->qdev;
|
|
||||||
const struct fb_image *fb_image = &qxl_fb_image->fb_image;
|
const struct fb_image *fb_image = &qxl_fb_image->fb_image;
|
||||||
uint32_t visual = qxl_fb_image->visual;
|
uint32_t visual = qxl_fb_image->visual;
|
||||||
const uint32_t *pseudo_palette = qxl_fb_image->pseudo_palette;
|
const uint32_t *pseudo_palette = qxl_fb_image->pseudo_palette;
|
||||||
|
@ -108,12 +135,7 @@ static int qxl_palette_create_1bit(struct qxl_bo **palette_bo,
|
||||||
static uint64_t unique; /* we make no attempt to actually set this
|
static uint64_t unique; /* we make no attempt to actually set this
|
||||||
* correctly globaly, since that would require
|
* correctly globaly, since that would require
|
||||||
* tracking all of our palettes. */
|
* tracking all of our palettes. */
|
||||||
|
ret = qxl_bo_kmap(palette_bo, (void **)&pal);
|
||||||
ret = qxl_alloc_bo_reserved(qdev,
|
|
||||||
sizeof(struct qxl_palette) + sizeof(uint32_t) * 2,
|
|
||||||
palette_bo);
|
|
||||||
|
|
||||||
ret = qxl_bo_kmap(*palette_bo, (void **)&pal);
|
|
||||||
pal->num_ents = 2;
|
pal->num_ents = 2;
|
||||||
pal->unique = unique++;
|
pal->unique = unique++;
|
||||||
if (visual == FB_VISUAL_TRUECOLOR || visual == FB_VISUAL_DIRECTCOLOR) {
|
if (visual == FB_VISUAL_TRUECOLOR || visual == FB_VISUAL_DIRECTCOLOR) {
|
||||||
|
@ -126,7 +148,7 @@ static int qxl_palette_create_1bit(struct qxl_bo **palette_bo,
|
||||||
}
|
}
|
||||||
pal->ents[0] = bgcolor;
|
pal->ents[0] = bgcolor;
|
||||||
pal->ents[1] = fgcolor;
|
pal->ents[1] = fgcolor;
|
||||||
qxl_bo_kunmap(*palette_bo);
|
qxl_bo_kunmap(palette_bo);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -144,44 +166,63 @@ void qxl_draw_opaque_fb(const struct qxl_fb_image *qxl_fb_image,
|
||||||
const char *src = fb_image->data;
|
const char *src = fb_image->data;
|
||||||
int depth = fb_image->depth;
|
int depth = fb_image->depth;
|
||||||
struct qxl_release *release;
|
struct qxl_release *release;
|
||||||
struct qxl_bo *image_bo;
|
|
||||||
struct qxl_image *image;
|
struct qxl_image *image;
|
||||||
int ret;
|
int ret;
|
||||||
|
struct qxl_drm_image *dimage;
|
||||||
|
struct qxl_bo *palette_bo = NULL;
|
||||||
if (stride == 0)
|
if (stride == 0)
|
||||||
stride = depth * width / 8;
|
stride = depth * width / 8;
|
||||||
|
|
||||||
|
ret = alloc_drawable(qdev, &release);
|
||||||
|
if (ret)
|
||||||
|
return;
|
||||||
|
|
||||||
|
ret = qxl_image_alloc_objects(qdev, release,
|
||||||
|
&dimage,
|
||||||
|
height, stride);
|
||||||
|
if (ret)
|
||||||
|
goto out_free_drawable;
|
||||||
|
|
||||||
|
if (depth == 1) {
|
||||||
|
ret = alloc_palette_object(qdev, release, &palette_bo);
|
||||||
|
if (ret)
|
||||||
|
goto out_free_image;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* do a reservation run over all the objects we just allocated */
|
||||||
|
ret = qxl_release_reserve_list(release, true);
|
||||||
|
if (ret)
|
||||||
|
goto out_free_palette;
|
||||||
|
|
||||||
rect.left = x;
|
rect.left = x;
|
||||||
rect.right = x + width;
|
rect.right = x + width;
|
||||||
rect.top = y;
|
rect.top = y;
|
||||||
rect.bottom = y + height;
|
rect.bottom = y + height;
|
||||||
|
|
||||||
ret = make_drawable(qdev, 0, QXL_DRAW_COPY, &rect, &release);
|
ret = make_drawable(qdev, 0, QXL_DRAW_COPY, &rect, release);
|
||||||
if (ret)
|
|
||||||
return;
|
|
||||||
|
|
||||||
ret = qxl_image_create(qdev, release, &image_bo,
|
|
||||||
(const uint8_t *)src, 0, 0,
|
|
||||||
width, height, depth, stride);
|
|
||||||
if (ret) {
|
if (ret) {
|
||||||
qxl_release_unreserve(qdev, release);
|
qxl_release_backoff_reserve_list(release);
|
||||||
|
goto out_free_palette;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = qxl_image_init(qdev, release, dimage,
|
||||||
|
(const uint8_t *)src, 0, 0,
|
||||||
|
width, height, depth, stride);
|
||||||
|
if (ret) {
|
||||||
|
qxl_release_backoff_reserve_list(release);
|
||||||
qxl_release_free(qdev, release);
|
qxl_release_free(qdev, release);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (depth == 1) {
|
if (depth == 1) {
|
||||||
struct qxl_bo *palette_bo;
|
|
||||||
void *ptr;
|
void *ptr;
|
||||||
ret = qxl_palette_create_1bit(&palette_bo, qxl_fb_image);
|
ret = qxl_palette_create_1bit(palette_bo, release, qxl_fb_image);
|
||||||
qxl_release_add_res(qdev, release, palette_bo);
|
|
||||||
|
|
||||||
ptr = qxl_bo_kmap_atomic_page(qdev, image_bo, 0);
|
ptr = qxl_bo_kmap_atomic_page(qdev, dimage->bo, 0);
|
||||||
image = ptr;
|
image = ptr;
|
||||||
image->u.bitmap.palette =
|
image->u.bitmap.palette =
|
||||||
qxl_bo_physical_address(qdev, palette_bo, 0);
|
qxl_bo_physical_address(qdev, palette_bo, 0);
|
||||||
qxl_bo_kunmap_atomic_page(qdev, image_bo, ptr);
|
qxl_bo_kunmap_atomic_page(qdev, dimage->bo, ptr);
|
||||||
qxl_bo_unreserve(palette_bo);
|
|
||||||
qxl_bo_unref(&palette_bo);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
drawable = (struct qxl_drawable *)qxl_release_map(qdev, release);
|
drawable = (struct qxl_drawable *)qxl_release_map(qdev, release);
|
||||||
|
@ -199,16 +240,20 @@ void qxl_draw_opaque_fb(const struct qxl_fb_image *qxl_fb_image,
|
||||||
drawable->u.copy.mask.bitmap = 0;
|
drawable->u.copy.mask.bitmap = 0;
|
||||||
|
|
||||||
drawable->u.copy.src_bitmap =
|
drawable->u.copy.src_bitmap =
|
||||||
qxl_bo_physical_address(qdev, image_bo, 0);
|
qxl_bo_physical_address(qdev, dimage->bo, 0);
|
||||||
qxl_release_unmap(qdev, release, &drawable->release_info);
|
qxl_release_unmap(qdev, release, &drawable->release_info);
|
||||||
|
|
||||||
qxl_release_add_res(qdev, release, image_bo);
|
|
||||||
qxl_bo_unreserve(image_bo);
|
|
||||||
qxl_bo_unref(&image_bo);
|
|
||||||
|
|
||||||
qxl_fence_releaseable(qdev, release);
|
|
||||||
qxl_push_command_ring_release(qdev, release, QXL_CMD_DRAW, false);
|
qxl_push_command_ring_release(qdev, release, QXL_CMD_DRAW, false);
|
||||||
qxl_release_unreserve(qdev, release);
|
qxl_release_fence_buffer_objects(release);
|
||||||
|
|
||||||
|
out_free_palette:
|
||||||
|
if (palette_bo)
|
||||||
|
qxl_bo_unref(&palette_bo);
|
||||||
|
out_free_image:
|
||||||
|
qxl_image_free_objects(qdev, dimage);
|
||||||
|
out_free_drawable:
|
||||||
|
if (ret)
|
||||||
|
free_drawable(qdev, release);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* push a draw command using the given clipping rectangles as
|
/* push a draw command using the given clipping rectangles as
|
||||||
|
@ -243,10 +288,14 @@ void qxl_draw_dirty_fb(struct qxl_device *qdev,
|
||||||
int depth = qxl_fb->base.bits_per_pixel;
|
int depth = qxl_fb->base.bits_per_pixel;
|
||||||
uint8_t *surface_base;
|
uint8_t *surface_base;
|
||||||
struct qxl_release *release;
|
struct qxl_release *release;
|
||||||
struct qxl_bo *image_bo;
|
|
||||||
struct qxl_bo *clips_bo;
|
struct qxl_bo *clips_bo;
|
||||||
|
struct qxl_drm_image *dimage;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
|
ret = alloc_drawable(qdev, &release);
|
||||||
|
if (ret)
|
||||||
|
return;
|
||||||
|
|
||||||
left = clips->x1;
|
left = clips->x1;
|
||||||
right = clips->x2;
|
right = clips->x2;
|
||||||
top = clips->y1;
|
top = clips->y1;
|
||||||
|
@ -263,36 +312,52 @@ void qxl_draw_dirty_fb(struct qxl_device *qdev,
|
||||||
|
|
||||||
width = right - left;
|
width = right - left;
|
||||||
height = bottom - top;
|
height = bottom - top;
|
||||||
|
|
||||||
|
ret = alloc_clips(qdev, release, num_clips, &clips_bo);
|
||||||
|
if (ret)
|
||||||
|
goto out_free_drawable;
|
||||||
|
|
||||||
|
ret = qxl_image_alloc_objects(qdev, release,
|
||||||
|
&dimage,
|
||||||
|
height, stride);
|
||||||
|
if (ret)
|
||||||
|
goto out_free_clips;
|
||||||
|
|
||||||
|
/* do a reservation run over all the objects we just allocated */
|
||||||
|
ret = qxl_release_reserve_list(release, true);
|
||||||
|
if (ret)
|
||||||
|
goto out_free_image;
|
||||||
|
|
||||||
drawable_rect.left = left;
|
drawable_rect.left = left;
|
||||||
drawable_rect.right = right;
|
drawable_rect.right = right;
|
||||||
drawable_rect.top = top;
|
drawable_rect.top = top;
|
||||||
drawable_rect.bottom = bottom;
|
drawable_rect.bottom = bottom;
|
||||||
|
|
||||||
ret = make_drawable(qdev, 0, QXL_DRAW_COPY, &drawable_rect,
|
ret = make_drawable(qdev, 0, QXL_DRAW_COPY, &drawable_rect,
|
||||||
&release);
|
release);
|
||||||
if (ret)
|
if (ret)
|
||||||
return;
|
goto out_release_backoff;
|
||||||
|
|
||||||
ret = qxl_bo_kmap(bo, (void **)&surface_base);
|
ret = qxl_bo_kmap(bo, (void **)&surface_base);
|
||||||
if (ret)
|
if (ret)
|
||||||
goto out_unref;
|
goto out_release_backoff;
|
||||||
|
|
||||||
ret = qxl_image_create(qdev, release, &image_bo, surface_base,
|
|
||||||
left, top, width, height, depth, stride);
|
ret = qxl_image_init(qdev, release, dimage, surface_base,
|
||||||
|
left, top, width, height, depth, stride);
|
||||||
qxl_bo_kunmap(bo);
|
qxl_bo_kunmap(bo);
|
||||||
if (ret)
|
if (ret)
|
||||||
goto out_unref;
|
goto out_release_backoff;
|
||||||
|
|
||||||
|
rects = drawable_set_clipping(qdev, drawable, num_clips, clips_bo);
|
||||||
|
if (!rects)
|
||||||
|
goto out_release_backoff;
|
||||||
|
|
||||||
rects = drawable_set_clipping(qdev, drawable, num_clips, &clips_bo, release);
|
|
||||||
if (!rects) {
|
|
||||||
qxl_bo_unref(&image_bo);
|
|
||||||
goto out_unref;
|
|
||||||
}
|
|
||||||
drawable = (struct qxl_drawable *)qxl_release_map(qdev, release);
|
drawable = (struct qxl_drawable *)qxl_release_map(qdev, release);
|
||||||
|
|
||||||
drawable->clip.type = SPICE_CLIP_TYPE_RECTS;
|
drawable->clip.type = SPICE_CLIP_TYPE_RECTS;
|
||||||
drawable->clip.data = qxl_bo_physical_address(qdev,
|
drawable->clip.data = qxl_bo_physical_address(qdev,
|
||||||
clips_bo, 0);
|
clips_bo, 0);
|
||||||
qxl_release_add_res(qdev, release, clips_bo);
|
|
||||||
|
|
||||||
drawable->u.copy.src_area.top = 0;
|
drawable->u.copy.src_area.top = 0;
|
||||||
drawable->u.copy.src_area.bottom = height;
|
drawable->u.copy.src_area.bottom = height;
|
||||||
|
@ -306,11 +371,9 @@ void qxl_draw_dirty_fb(struct qxl_device *qdev,
|
||||||
drawable->u.copy.mask.pos.y = 0;
|
drawable->u.copy.mask.pos.y = 0;
|
||||||
drawable->u.copy.mask.bitmap = 0;
|
drawable->u.copy.mask.bitmap = 0;
|
||||||
|
|
||||||
drawable->u.copy.src_bitmap = qxl_bo_physical_address(qdev, image_bo, 0);
|
drawable->u.copy.src_bitmap = qxl_bo_physical_address(qdev, dimage->bo, 0);
|
||||||
qxl_release_unmap(qdev, release, &drawable->release_info);
|
qxl_release_unmap(qdev, release, &drawable->release_info);
|
||||||
qxl_release_add_res(qdev, release, image_bo);
|
|
||||||
qxl_bo_unreserve(image_bo);
|
|
||||||
qxl_bo_unref(&image_bo);
|
|
||||||
clips_ptr = clips;
|
clips_ptr = clips;
|
||||||
for (i = 0; i < num_clips; i++, clips_ptr += inc) {
|
for (i = 0; i < num_clips; i++, clips_ptr += inc) {
|
||||||
rects[i].left = clips_ptr->x1;
|
rects[i].left = clips_ptr->x1;
|
||||||
|
@ -319,17 +382,22 @@ void qxl_draw_dirty_fb(struct qxl_device *qdev,
|
||||||
rects[i].bottom = clips_ptr->y2;
|
rects[i].bottom = clips_ptr->y2;
|
||||||
}
|
}
|
||||||
qxl_bo_kunmap(clips_bo);
|
qxl_bo_kunmap(clips_bo);
|
||||||
qxl_bo_unreserve(clips_bo);
|
|
||||||
qxl_bo_unref(&clips_bo);
|
|
||||||
|
|
||||||
qxl_fence_releaseable(qdev, release);
|
|
||||||
qxl_push_command_ring_release(qdev, release, QXL_CMD_DRAW, false);
|
qxl_push_command_ring_release(qdev, release, QXL_CMD_DRAW, false);
|
||||||
qxl_release_unreserve(qdev, release);
|
qxl_release_fence_buffer_objects(release);
|
||||||
return;
|
|
||||||
|
out_release_backoff:
|
||||||
|
if (ret)
|
||||||
|
qxl_release_backoff_reserve_list(release);
|
||||||
|
out_free_image:
|
||||||
|
qxl_image_free_objects(qdev, dimage);
|
||||||
|
out_free_clips:
|
||||||
|
qxl_bo_unref(&clips_bo);
|
||||||
|
out_free_drawable:
|
||||||
|
/* only free drawable on error */
|
||||||
|
if (ret)
|
||||||
|
free_drawable(qdev, release);
|
||||||
|
|
||||||
out_unref:
|
|
||||||
qxl_release_unreserve(qdev, release);
|
|
||||||
qxl_release_free(qdev, release);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void qxl_draw_copyarea(struct qxl_device *qdev,
|
void qxl_draw_copyarea(struct qxl_device *qdev,
|
||||||
|
@ -342,22 +410,36 @@ void qxl_draw_copyarea(struct qxl_device *qdev,
|
||||||
struct qxl_release *release;
|
struct qxl_release *release;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
|
ret = alloc_drawable(qdev, &release);
|
||||||
|
if (ret)
|
||||||
|
return;
|
||||||
|
|
||||||
|
/* do a reservation run over all the objects we just allocated */
|
||||||
|
ret = qxl_release_reserve_list(release, true);
|
||||||
|
if (ret)
|
||||||
|
goto out_free_release;
|
||||||
|
|
||||||
rect.left = dx;
|
rect.left = dx;
|
||||||
rect.top = dy;
|
rect.top = dy;
|
||||||
rect.right = dx + width;
|
rect.right = dx + width;
|
||||||
rect.bottom = dy + height;
|
rect.bottom = dy + height;
|
||||||
ret = make_drawable(qdev, 0, QXL_COPY_BITS, &rect, &release);
|
ret = make_drawable(qdev, 0, QXL_COPY_BITS, &rect, release);
|
||||||
if (ret)
|
if (ret) {
|
||||||
return;
|
qxl_release_backoff_reserve_list(release);
|
||||||
|
goto out_free_release;
|
||||||
|
}
|
||||||
|
|
||||||
drawable = (struct qxl_drawable *)qxl_release_map(qdev, release);
|
drawable = (struct qxl_drawable *)qxl_release_map(qdev, release);
|
||||||
drawable->u.copy_bits.src_pos.x = sx;
|
drawable->u.copy_bits.src_pos.x = sx;
|
||||||
drawable->u.copy_bits.src_pos.y = sy;
|
drawable->u.copy_bits.src_pos.y = sy;
|
||||||
|
|
||||||
qxl_release_unmap(qdev, release, &drawable->release_info);
|
qxl_release_unmap(qdev, release, &drawable->release_info);
|
||||||
qxl_fence_releaseable(qdev, release);
|
|
||||||
qxl_push_command_ring_release(qdev, release, QXL_CMD_DRAW, false);
|
qxl_push_command_ring_release(qdev, release, QXL_CMD_DRAW, false);
|
||||||
qxl_release_unreserve(qdev, release);
|
qxl_release_fence_buffer_objects(release);
|
||||||
|
|
||||||
|
out_free_release:
|
||||||
|
if (ret)
|
||||||
|
free_drawable(qdev, release);
|
||||||
}
|
}
|
||||||
|
|
||||||
void qxl_draw_fill(struct qxl_draw_fill *qxl_draw_fill_rec)
|
void qxl_draw_fill(struct qxl_draw_fill *qxl_draw_fill_rec)
|
||||||
|
@ -370,10 +452,21 @@ void qxl_draw_fill(struct qxl_draw_fill *qxl_draw_fill_rec)
|
||||||
struct qxl_release *release;
|
struct qxl_release *release;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
ret = make_drawable(qdev, 0, QXL_DRAW_FILL, &rect, &release);
|
ret = alloc_drawable(qdev, &release);
|
||||||
if (ret)
|
if (ret)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
/* do a reservation run over all the objects we just allocated */
|
||||||
|
ret = qxl_release_reserve_list(release, true);
|
||||||
|
if (ret)
|
||||||
|
goto out_free_release;
|
||||||
|
|
||||||
|
ret = make_drawable(qdev, 0, QXL_DRAW_FILL, &rect, release);
|
||||||
|
if (ret) {
|
||||||
|
qxl_release_backoff_reserve_list(release);
|
||||||
|
goto out_free_release;
|
||||||
|
}
|
||||||
|
|
||||||
drawable = (struct qxl_drawable *)qxl_release_map(qdev, release);
|
drawable = (struct qxl_drawable *)qxl_release_map(qdev, release);
|
||||||
drawable->u.fill.brush.type = SPICE_BRUSH_TYPE_SOLID;
|
drawable->u.fill.brush.type = SPICE_BRUSH_TYPE_SOLID;
|
||||||
drawable->u.fill.brush.u.color = color;
|
drawable->u.fill.brush.u.color = color;
|
||||||
|
@ -384,7 +477,11 @@ void qxl_draw_fill(struct qxl_draw_fill *qxl_draw_fill_rec)
|
||||||
drawable->u.fill.mask.bitmap = 0;
|
drawable->u.fill.mask.bitmap = 0;
|
||||||
|
|
||||||
qxl_release_unmap(qdev, release, &drawable->release_info);
|
qxl_release_unmap(qdev, release, &drawable->release_info);
|
||||||
qxl_fence_releaseable(qdev, release);
|
|
||||||
qxl_push_command_ring_release(qdev, release, QXL_CMD_DRAW, false);
|
qxl_push_command_ring_release(qdev, release, QXL_CMD_DRAW, false);
|
||||||
qxl_release_unreserve(qdev, release);
|
qxl_release_fence_buffer_objects(release);
|
||||||
|
|
||||||
|
out_free_release:
|
||||||
|
if (ret)
|
||||||
|
free_drawable(qdev, release);
|
||||||
}
|
}
|
||||||
|
|
|
@ -42,6 +42,9 @@
|
||||||
#include <ttm/ttm_placement.h>
|
#include <ttm/ttm_placement.h>
|
||||||
#include <ttm/ttm_module.h>
|
#include <ttm/ttm_module.h>
|
||||||
|
|
||||||
|
/* just for ttm_validate_buffer */
|
||||||
|
#include <ttm/ttm_execbuf_util.h>
|
||||||
|
|
||||||
#include <drm/qxl_drm.h>
|
#include <drm/qxl_drm.h>
|
||||||
#include "qxl_dev.h"
|
#include "qxl_dev.h"
|
||||||
|
|
||||||
|
@ -118,9 +121,9 @@ struct qxl_bo {
|
||||||
uint32_t surface_id;
|
uint32_t surface_id;
|
||||||
struct qxl_fence fence; /* per bo fence - list of releases */
|
struct qxl_fence fence; /* per bo fence - list of releases */
|
||||||
struct qxl_release *surf_create;
|
struct qxl_release *surf_create;
|
||||||
atomic_t reserve_count;
|
|
||||||
};
|
};
|
||||||
#define gem_to_qxl_bo(gobj) container_of((gobj), struct qxl_bo, gem_base)
|
#define gem_to_qxl_bo(gobj) container_of((gobj), struct qxl_bo, gem_base)
|
||||||
|
#define to_qxl_bo(tobj) container_of((tobj), struct qxl_bo, tbo)
|
||||||
|
|
||||||
struct qxl_gem {
|
struct qxl_gem {
|
||||||
struct mutex mutex;
|
struct mutex mutex;
|
||||||
|
@ -128,12 +131,7 @@ struct qxl_gem {
|
||||||
};
|
};
|
||||||
|
|
||||||
struct qxl_bo_list {
|
struct qxl_bo_list {
|
||||||
struct list_head lhead;
|
struct ttm_validate_buffer tv;
|
||||||
struct qxl_bo *bo;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct qxl_reloc_list {
|
|
||||||
struct list_head bos;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct qxl_crtc {
|
struct qxl_crtc {
|
||||||
|
@ -195,10 +193,20 @@ enum {
|
||||||
struct qxl_release {
|
struct qxl_release {
|
||||||
int id;
|
int id;
|
||||||
int type;
|
int type;
|
||||||
int bo_count;
|
|
||||||
uint32_t release_offset;
|
uint32_t release_offset;
|
||||||
uint32_t surface_release_id;
|
uint32_t surface_release_id;
|
||||||
struct qxl_bo *bos[QXL_MAX_RES];
|
struct ww_acquire_ctx ticket;
|
||||||
|
struct list_head bos;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct qxl_drm_chunk {
|
||||||
|
struct list_head head;
|
||||||
|
struct qxl_bo *bo;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct qxl_drm_image {
|
||||||
|
struct qxl_bo *bo;
|
||||||
|
struct list_head chunk_list;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct qxl_fb_image {
|
struct qxl_fb_image {
|
||||||
|
@ -314,6 +322,7 @@ struct qxl_device {
|
||||||
struct workqueue_struct *gc_queue;
|
struct workqueue_struct *gc_queue;
|
||||||
struct work_struct gc_work;
|
struct work_struct gc_work;
|
||||||
|
|
||||||
|
struct work_struct fb_work;
|
||||||
};
|
};
|
||||||
|
|
||||||
/* forward declaration for QXL_INFO_IO */
|
/* forward declaration for QXL_INFO_IO */
|
||||||
|
@ -433,12 +442,19 @@ int qxl_mmap(struct file *filp, struct vm_area_struct *vma);
|
||||||
|
|
||||||
/* qxl image */
|
/* qxl image */
|
||||||
|
|
||||||
int qxl_image_create(struct qxl_device *qdev,
|
int qxl_image_init(struct qxl_device *qdev,
|
||||||
struct qxl_release *release,
|
struct qxl_release *release,
|
||||||
struct qxl_bo **image_bo,
|
struct qxl_drm_image *dimage,
|
||||||
const uint8_t *data,
|
const uint8_t *data,
|
||||||
int x, int y, int width, int height,
|
int x, int y, int width, int height,
|
||||||
int depth, int stride);
|
int depth, int stride);
|
||||||
|
int
|
||||||
|
qxl_image_alloc_objects(struct qxl_device *qdev,
|
||||||
|
struct qxl_release *release,
|
||||||
|
struct qxl_drm_image **image_ptr,
|
||||||
|
int height, int stride);
|
||||||
|
void qxl_image_free_objects(struct qxl_device *qdev, struct qxl_drm_image *dimage);
|
||||||
|
|
||||||
void qxl_update_screen(struct qxl_device *qxl);
|
void qxl_update_screen(struct qxl_device *qxl);
|
||||||
|
|
||||||
/* qxl io operations (qxl_cmd.c) */
|
/* qxl io operations (qxl_cmd.c) */
|
||||||
|
@ -459,20 +475,15 @@ int qxl_ring_push(struct qxl_ring *ring, const void *new_elt, bool interruptible
|
||||||
void qxl_io_flush_release(struct qxl_device *qdev);
|
void qxl_io_flush_release(struct qxl_device *qdev);
|
||||||
void qxl_io_flush_surfaces(struct qxl_device *qdev);
|
void qxl_io_flush_surfaces(struct qxl_device *qdev);
|
||||||
|
|
||||||
int qxl_release_reserve(struct qxl_device *qdev,
|
|
||||||
struct qxl_release *release, bool no_wait);
|
|
||||||
void qxl_release_unreserve(struct qxl_device *qdev,
|
|
||||||
struct qxl_release *release);
|
|
||||||
union qxl_release_info *qxl_release_map(struct qxl_device *qdev,
|
union qxl_release_info *qxl_release_map(struct qxl_device *qdev,
|
||||||
struct qxl_release *release);
|
struct qxl_release *release);
|
||||||
void qxl_release_unmap(struct qxl_device *qdev,
|
void qxl_release_unmap(struct qxl_device *qdev,
|
||||||
struct qxl_release *release,
|
struct qxl_release *release,
|
||||||
union qxl_release_info *info);
|
union qxl_release_info *info);
|
||||||
/*
|
int qxl_release_list_add(struct qxl_release *release, struct qxl_bo *bo);
|
||||||
* qxl_bo_add_resource.
|
int qxl_release_reserve_list(struct qxl_release *release, bool no_intr);
|
||||||
*
|
void qxl_release_backoff_reserve_list(struct qxl_release *release);
|
||||||
*/
|
void qxl_release_fence_buffer_objects(struct qxl_release *release);
|
||||||
void qxl_bo_add_resource(struct qxl_bo *main_bo, struct qxl_bo *resource);
|
|
||||||
|
|
||||||
int qxl_alloc_surface_release_reserved(struct qxl_device *qdev,
|
int qxl_alloc_surface_release_reserved(struct qxl_device *qdev,
|
||||||
enum qxl_surface_cmd_type surface_cmd_type,
|
enum qxl_surface_cmd_type surface_cmd_type,
|
||||||
|
@ -481,15 +492,16 @@ int qxl_alloc_surface_release_reserved(struct qxl_device *qdev,
|
||||||
int qxl_alloc_release_reserved(struct qxl_device *qdev, unsigned long size,
|
int qxl_alloc_release_reserved(struct qxl_device *qdev, unsigned long size,
|
||||||
int type, struct qxl_release **release,
|
int type, struct qxl_release **release,
|
||||||
struct qxl_bo **rbo);
|
struct qxl_bo **rbo);
|
||||||
int qxl_fence_releaseable(struct qxl_device *qdev,
|
|
||||||
struct qxl_release *release);
|
|
||||||
int
|
int
|
||||||
qxl_push_command_ring_release(struct qxl_device *qdev, struct qxl_release *release,
|
qxl_push_command_ring_release(struct qxl_device *qdev, struct qxl_release *release,
|
||||||
uint32_t type, bool interruptible);
|
uint32_t type, bool interruptible);
|
||||||
int
|
int
|
||||||
qxl_push_cursor_ring_release(struct qxl_device *qdev, struct qxl_release *release,
|
qxl_push_cursor_ring_release(struct qxl_device *qdev, struct qxl_release *release,
|
||||||
uint32_t type, bool interruptible);
|
uint32_t type, bool interruptible);
|
||||||
int qxl_alloc_bo_reserved(struct qxl_device *qdev, unsigned long size,
|
int qxl_alloc_bo_reserved(struct qxl_device *qdev,
|
||||||
|
struct qxl_release *release,
|
||||||
|
unsigned long size,
|
||||||
struct qxl_bo **_bo);
|
struct qxl_bo **_bo);
|
||||||
/* qxl drawing commands */
|
/* qxl drawing commands */
|
||||||
|
|
||||||
|
@ -510,15 +522,9 @@ void qxl_draw_copyarea(struct qxl_device *qdev,
|
||||||
u32 sx, u32 sy,
|
u32 sx, u32 sy,
|
||||||
u32 dx, u32 dy);
|
u32 dx, u32 dy);
|
||||||
|
|
||||||
uint64_t
|
|
||||||
qxl_release_alloc(struct qxl_device *qdev, int type,
|
|
||||||
struct qxl_release **ret);
|
|
||||||
|
|
||||||
void qxl_release_free(struct qxl_device *qdev,
|
void qxl_release_free(struct qxl_device *qdev,
|
||||||
struct qxl_release *release);
|
struct qxl_release *release);
|
||||||
void qxl_release_add_res(struct qxl_device *qdev,
|
|
||||||
struct qxl_release *release,
|
|
||||||
struct qxl_bo *bo);
|
|
||||||
/* used by qxl_debugfs_release */
|
/* used by qxl_debugfs_release */
|
||||||
struct qxl_release *qxl_release_from_id_locked(struct qxl_device *qdev,
|
struct qxl_release *qxl_release_from_id_locked(struct qxl_device *qdev,
|
||||||
uint64_t id);
|
uint64_t id);
|
||||||
|
@ -561,7 +567,7 @@ void qxl_surface_evict(struct qxl_device *qdev, struct qxl_bo *surf, bool freein
|
||||||
int qxl_update_surface(struct qxl_device *qdev, struct qxl_bo *surf);
|
int qxl_update_surface(struct qxl_device *qdev, struct qxl_bo *surf);
|
||||||
|
|
||||||
/* qxl_fence.c */
|
/* qxl_fence.c */
|
||||||
int qxl_fence_add_release(struct qxl_fence *qfence, uint32_t rel_id);
|
void qxl_fence_add_release_locked(struct qxl_fence *qfence, uint32_t rel_id);
|
||||||
int qxl_fence_remove_release(struct qxl_fence *qfence, uint32_t rel_id);
|
int qxl_fence_remove_release(struct qxl_fence *qfence, uint32_t rel_id);
|
||||||
int qxl_fence_init(struct qxl_device *qdev, struct qxl_fence *qfence);
|
int qxl_fence_init(struct qxl_device *qdev, struct qxl_fence *qfence);
|
||||||
void qxl_fence_fini(struct qxl_fence *qfence);
|
void qxl_fence_fini(struct qxl_fence *qfence);
|
||||||
|
|
|
@ -37,12 +37,29 @@
|
||||||
|
|
||||||
#define QXL_DIRTY_DELAY (HZ / 30)
|
#define QXL_DIRTY_DELAY (HZ / 30)
|
||||||
|
|
||||||
|
#define QXL_FB_OP_FILLRECT 1
|
||||||
|
#define QXL_FB_OP_COPYAREA 2
|
||||||
|
#define QXL_FB_OP_IMAGEBLIT 3
|
||||||
|
|
||||||
|
struct qxl_fb_op {
|
||||||
|
struct list_head head;
|
||||||
|
int op_type;
|
||||||
|
union {
|
||||||
|
struct fb_fillrect fr;
|
||||||
|
struct fb_copyarea ca;
|
||||||
|
struct fb_image ib;
|
||||||
|
} op;
|
||||||
|
void *img_data;
|
||||||
|
};
|
||||||
|
|
||||||
struct qxl_fbdev {
|
struct qxl_fbdev {
|
||||||
struct drm_fb_helper helper;
|
struct drm_fb_helper helper;
|
||||||
struct qxl_framebuffer qfb;
|
struct qxl_framebuffer qfb;
|
||||||
struct list_head fbdev_list;
|
struct list_head fbdev_list;
|
||||||
struct qxl_device *qdev;
|
struct qxl_device *qdev;
|
||||||
|
|
||||||
|
spinlock_t delayed_ops_lock;
|
||||||
|
struct list_head delayed_ops;
|
||||||
void *shadow;
|
void *shadow;
|
||||||
int size;
|
int size;
|
||||||
|
|
||||||
|
@ -164,8 +181,69 @@ static struct fb_deferred_io qxl_defio = {
|
||||||
.deferred_io = qxl_deferred_io,
|
.deferred_io = qxl_deferred_io,
|
||||||
};
|
};
|
||||||
|
|
||||||
static void qxl_fb_fillrect(struct fb_info *info,
|
static void qxl_fb_delayed_fillrect(struct qxl_fbdev *qfbdev,
|
||||||
const struct fb_fillrect *fb_rect)
|
const struct fb_fillrect *fb_rect)
|
||||||
|
{
|
||||||
|
struct qxl_fb_op *op;
|
||||||
|
unsigned long flags;
|
||||||
|
|
||||||
|
op = kmalloc(sizeof(struct qxl_fb_op), GFP_ATOMIC | __GFP_NOWARN);
|
||||||
|
if (!op)
|
||||||
|
return;
|
||||||
|
|
||||||
|
op->op.fr = *fb_rect;
|
||||||
|
op->img_data = NULL;
|
||||||
|
op->op_type = QXL_FB_OP_FILLRECT;
|
||||||
|
|
||||||
|
spin_lock_irqsave(&qfbdev->delayed_ops_lock, flags);
|
||||||
|
list_add_tail(&op->head, &qfbdev->delayed_ops);
|
||||||
|
spin_unlock_irqrestore(&qfbdev->delayed_ops_lock, flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void qxl_fb_delayed_copyarea(struct qxl_fbdev *qfbdev,
|
||||||
|
const struct fb_copyarea *fb_copy)
|
||||||
|
{
|
||||||
|
struct qxl_fb_op *op;
|
||||||
|
unsigned long flags;
|
||||||
|
|
||||||
|
op = kmalloc(sizeof(struct qxl_fb_op), GFP_ATOMIC | __GFP_NOWARN);
|
||||||
|
if (!op)
|
||||||
|
return;
|
||||||
|
|
||||||
|
op->op.ca = *fb_copy;
|
||||||
|
op->img_data = NULL;
|
||||||
|
op->op_type = QXL_FB_OP_COPYAREA;
|
||||||
|
|
||||||
|
spin_lock_irqsave(&qfbdev->delayed_ops_lock, flags);
|
||||||
|
list_add_tail(&op->head, &qfbdev->delayed_ops);
|
||||||
|
spin_unlock_irqrestore(&qfbdev->delayed_ops_lock, flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void qxl_fb_delayed_imageblit(struct qxl_fbdev *qfbdev,
|
||||||
|
const struct fb_image *fb_image)
|
||||||
|
{
|
||||||
|
struct qxl_fb_op *op;
|
||||||
|
unsigned long flags;
|
||||||
|
uint32_t size = fb_image->width * fb_image->height * (fb_image->depth >= 8 ? fb_image->depth / 8 : 1);
|
||||||
|
|
||||||
|
op = kmalloc(sizeof(struct qxl_fb_op) + size, GFP_ATOMIC | __GFP_NOWARN);
|
||||||
|
if (!op)
|
||||||
|
return;
|
||||||
|
|
||||||
|
op->op.ib = *fb_image;
|
||||||
|
op->img_data = (void *)(op + 1);
|
||||||
|
op->op_type = QXL_FB_OP_IMAGEBLIT;
|
||||||
|
|
||||||
|
memcpy(op->img_data, fb_image->data, size);
|
||||||
|
|
||||||
|
op->op.ib.data = op->img_data;
|
||||||
|
spin_lock_irqsave(&qfbdev->delayed_ops_lock, flags);
|
||||||
|
list_add_tail(&op->head, &qfbdev->delayed_ops);
|
||||||
|
spin_unlock_irqrestore(&qfbdev->delayed_ops_lock, flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void qxl_fb_fillrect_internal(struct fb_info *info,
|
||||||
|
const struct fb_fillrect *fb_rect)
|
||||||
{
|
{
|
||||||
struct qxl_fbdev *qfbdev = info->par;
|
struct qxl_fbdev *qfbdev = info->par;
|
||||||
struct qxl_device *qdev = qfbdev->qdev;
|
struct qxl_device *qdev = qfbdev->qdev;
|
||||||
|
@ -203,17 +281,28 @@ static void qxl_fb_fillrect(struct fb_info *info,
|
||||||
qxl_draw_fill_rec.rect = rect;
|
qxl_draw_fill_rec.rect = rect;
|
||||||
qxl_draw_fill_rec.color = color;
|
qxl_draw_fill_rec.color = color;
|
||||||
qxl_draw_fill_rec.rop = rop;
|
qxl_draw_fill_rec.rop = rop;
|
||||||
if (!drm_can_sleep()) {
|
|
||||||
qxl_io_log(qdev,
|
|
||||||
"%s: TODO use RCU, mysterious locks with spin_lock\n",
|
|
||||||
__func__);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
qxl_draw_fill(&qxl_draw_fill_rec);
|
qxl_draw_fill(&qxl_draw_fill_rec);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void qxl_fb_copyarea(struct fb_info *info,
|
static void qxl_fb_fillrect(struct fb_info *info,
|
||||||
const struct fb_copyarea *region)
|
const struct fb_fillrect *fb_rect)
|
||||||
|
{
|
||||||
|
struct qxl_fbdev *qfbdev = info->par;
|
||||||
|
struct qxl_device *qdev = qfbdev->qdev;
|
||||||
|
|
||||||
|
if (!drm_can_sleep()) {
|
||||||
|
qxl_fb_delayed_fillrect(qfbdev, fb_rect);
|
||||||
|
schedule_work(&qdev->fb_work);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
/* make sure any previous work is done */
|
||||||
|
flush_work(&qdev->fb_work);
|
||||||
|
qxl_fb_fillrect_internal(info, fb_rect);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void qxl_fb_copyarea_internal(struct fb_info *info,
|
||||||
|
const struct fb_copyarea *region)
|
||||||
{
|
{
|
||||||
struct qxl_fbdev *qfbdev = info->par;
|
struct qxl_fbdev *qfbdev = info->par;
|
||||||
|
|
||||||
|
@ -223,37 +312,89 @@ static void qxl_fb_copyarea(struct fb_info *info,
|
||||||
region->dx, region->dy);
|
region->dx, region->dy);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void qxl_fb_copyarea(struct fb_info *info,
|
||||||
|
const struct fb_copyarea *region)
|
||||||
|
{
|
||||||
|
struct qxl_fbdev *qfbdev = info->par;
|
||||||
|
struct qxl_device *qdev = qfbdev->qdev;
|
||||||
|
|
||||||
|
if (!drm_can_sleep()) {
|
||||||
|
qxl_fb_delayed_copyarea(qfbdev, region);
|
||||||
|
schedule_work(&qdev->fb_work);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
/* make sure any previous work is done */
|
||||||
|
flush_work(&qdev->fb_work);
|
||||||
|
qxl_fb_copyarea_internal(info, region);
|
||||||
|
}
|
||||||
|
|
||||||
static void qxl_fb_imageblit_safe(struct qxl_fb_image *qxl_fb_image)
|
static void qxl_fb_imageblit_safe(struct qxl_fb_image *qxl_fb_image)
|
||||||
{
|
{
|
||||||
qxl_draw_opaque_fb(qxl_fb_image, 0);
|
qxl_draw_opaque_fb(qxl_fb_image, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void qxl_fb_imageblit_internal(struct fb_info *info,
|
||||||
|
const struct fb_image *image)
|
||||||
|
{
|
||||||
|
struct qxl_fbdev *qfbdev = info->par;
|
||||||
|
struct qxl_fb_image qxl_fb_image;
|
||||||
|
|
||||||
|
/* ensure proper order rendering operations - TODO: must do this
|
||||||
|
* for everything. */
|
||||||
|
qxl_fb_image_init(&qxl_fb_image, qfbdev->qdev, info, image);
|
||||||
|
qxl_fb_imageblit_safe(&qxl_fb_image);
|
||||||
|
}
|
||||||
|
|
||||||
static void qxl_fb_imageblit(struct fb_info *info,
|
static void qxl_fb_imageblit(struct fb_info *info,
|
||||||
const struct fb_image *image)
|
const struct fb_image *image)
|
||||||
{
|
{
|
||||||
struct qxl_fbdev *qfbdev = info->par;
|
struct qxl_fbdev *qfbdev = info->par;
|
||||||
struct qxl_device *qdev = qfbdev->qdev;
|
struct qxl_device *qdev = qfbdev->qdev;
|
||||||
struct qxl_fb_image qxl_fb_image;
|
|
||||||
|
|
||||||
if (!drm_can_sleep()) {
|
if (!drm_can_sleep()) {
|
||||||
/* we cannot do any ttm_bo allocation since that will fail on
|
qxl_fb_delayed_imageblit(qfbdev, image);
|
||||||
* ioremap_wc..__get_vm_area_node, so queue the work item
|
schedule_work(&qdev->fb_work);
|
||||||
* instead This can happen from printk inside an interrupt
|
|
||||||
* context, i.e.: smp_apic_timer_interrupt..check_cpu_stall */
|
|
||||||
qxl_io_log(qdev,
|
|
||||||
"%s: TODO use RCU, mysterious locks with spin_lock\n",
|
|
||||||
__func__);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
/* make sure any previous work is done */
|
||||||
|
flush_work(&qdev->fb_work);
|
||||||
|
qxl_fb_imageblit_internal(info, image);
|
||||||
|
}
|
||||||
|
|
||||||
/* ensure proper order of rendering operations - TODO: must do this
|
static void qxl_fb_work(struct work_struct *work)
|
||||||
* for everything. */
|
{
|
||||||
qxl_fb_image_init(&qxl_fb_image, qfbdev->qdev, info, image);
|
struct qxl_device *qdev = container_of(work, struct qxl_device, fb_work);
|
||||||
qxl_fb_imageblit_safe(&qxl_fb_image);
|
unsigned long flags;
|
||||||
|
struct qxl_fb_op *entry, *tmp;
|
||||||
|
struct qxl_fbdev *qfbdev = qdev->mode_info.qfbdev;
|
||||||
|
|
||||||
|
/* since the irq context just adds entries to the end of the
|
||||||
|
list dropping the lock should be fine, as entry isn't modified
|
||||||
|
in the operation code */
|
||||||
|
spin_lock_irqsave(&qfbdev->delayed_ops_lock, flags);
|
||||||
|
list_for_each_entry_safe(entry, tmp, &qfbdev->delayed_ops, head) {
|
||||||
|
spin_unlock_irqrestore(&qfbdev->delayed_ops_lock, flags);
|
||||||
|
switch (entry->op_type) {
|
||||||
|
case QXL_FB_OP_FILLRECT:
|
||||||
|
qxl_fb_fillrect_internal(qfbdev->helper.fbdev, &entry->op.fr);
|
||||||
|
break;
|
||||||
|
case QXL_FB_OP_COPYAREA:
|
||||||
|
qxl_fb_copyarea_internal(qfbdev->helper.fbdev, &entry->op.ca);
|
||||||
|
break;
|
||||||
|
case QXL_FB_OP_IMAGEBLIT:
|
||||||
|
qxl_fb_imageblit_internal(qfbdev->helper.fbdev, &entry->op.ib);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
spin_lock_irqsave(&qfbdev->delayed_ops_lock, flags);
|
||||||
|
list_del(&entry->head);
|
||||||
|
kfree(entry);
|
||||||
|
}
|
||||||
|
spin_unlock_irqrestore(&qfbdev->delayed_ops_lock, flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
int qxl_fb_init(struct qxl_device *qdev)
|
int qxl_fb_init(struct qxl_device *qdev)
|
||||||
{
|
{
|
||||||
|
INIT_WORK(&qdev->fb_work, qxl_fb_work);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -536,7 +677,8 @@ int qxl_fbdev_init(struct qxl_device *qdev)
|
||||||
qfbdev->qdev = qdev;
|
qfbdev->qdev = qdev;
|
||||||
qdev->mode_info.qfbdev = qfbdev;
|
qdev->mode_info.qfbdev = qfbdev;
|
||||||
qfbdev->helper.funcs = &qxl_fb_helper_funcs;
|
qfbdev->helper.funcs = &qxl_fb_helper_funcs;
|
||||||
|
spin_lock_init(&qfbdev->delayed_ops_lock);
|
||||||
|
INIT_LIST_HEAD(&qfbdev->delayed_ops);
|
||||||
ret = drm_fb_helper_init(qdev->ddev, &qfbdev->helper,
|
ret = drm_fb_helper_init(qdev->ddev, &qfbdev->helper,
|
||||||
qxl_num_crtc /* num_crtc - QXL supports just 1 */,
|
qxl_num_crtc /* num_crtc - QXL supports just 1 */,
|
||||||
QXLFB_CONN_LIMIT);
|
QXLFB_CONN_LIMIT);
|
||||||
|
|
|
@ -49,17 +49,11 @@
|
||||||
|
|
||||||
For some reason every so often qxl hw fails to release, things go wrong.
|
For some reason every so often qxl hw fails to release, things go wrong.
|
||||||
*/
|
*/
|
||||||
|
/* must be called with the fence lock held */
|
||||||
|
void qxl_fence_add_release_locked(struct qxl_fence *qfence, uint32_t rel_id)
|
||||||
int qxl_fence_add_release(struct qxl_fence *qfence, uint32_t rel_id)
|
|
||||||
{
|
{
|
||||||
struct qxl_bo *bo = container_of(qfence, struct qxl_bo, fence);
|
|
||||||
|
|
||||||
spin_lock(&bo->tbo.bdev->fence_lock);
|
|
||||||
radix_tree_insert(&qfence->tree, rel_id, qfence);
|
radix_tree_insert(&qfence->tree, rel_id, qfence);
|
||||||
qfence->num_active_releases++;
|
qfence->num_active_releases++;
|
||||||
spin_unlock(&bo->tbo.bdev->fence_lock);
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int qxl_fence_remove_release(struct qxl_fence *qfence, uint32_t rel_id)
|
int qxl_fence_remove_release(struct qxl_fence *qfence, uint32_t rel_id)
|
||||||
|
|
|
@ -55,7 +55,7 @@ int qxl_gem_object_create(struct qxl_device *qdev, int size,
|
||||||
/* At least align on page size */
|
/* At least align on page size */
|
||||||
if (alignment < PAGE_SIZE)
|
if (alignment < PAGE_SIZE)
|
||||||
alignment = PAGE_SIZE;
|
alignment = PAGE_SIZE;
|
||||||
r = qxl_bo_create(qdev, size, kernel, initial_domain, surf, &qbo);
|
r = qxl_bo_create(qdev, size, kernel, false, initial_domain, surf, &qbo);
|
||||||
if (r) {
|
if (r) {
|
||||||
if (r != -ERESTARTSYS)
|
if (r != -ERESTARTSYS)
|
||||||
DRM_ERROR(
|
DRM_ERROR(
|
||||||
|
|
|
@ -30,31 +30,100 @@
|
||||||
#include "qxl_object.h"
|
#include "qxl_object.h"
|
||||||
|
|
||||||
static int
|
static int
|
||||||
qxl_image_create_helper(struct qxl_device *qdev,
|
qxl_allocate_chunk(struct qxl_device *qdev,
|
||||||
struct qxl_release *release,
|
struct qxl_release *release,
|
||||||
struct qxl_bo **image_bo,
|
struct qxl_drm_image *image,
|
||||||
const uint8_t *data,
|
unsigned int chunk_size)
|
||||||
int width, int height,
|
|
||||||
int depth, unsigned int hash,
|
|
||||||
int stride)
|
|
||||||
{
|
{
|
||||||
|
struct qxl_drm_chunk *chunk;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
chunk = kmalloc(sizeof(struct qxl_drm_chunk), GFP_KERNEL);
|
||||||
|
if (!chunk)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
ret = qxl_alloc_bo_reserved(qdev, release, chunk_size, &chunk->bo);
|
||||||
|
if (ret) {
|
||||||
|
kfree(chunk);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
list_add_tail(&chunk->head, &image->chunk_list);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
qxl_image_alloc_objects(struct qxl_device *qdev,
|
||||||
|
struct qxl_release *release,
|
||||||
|
struct qxl_drm_image **image_ptr,
|
||||||
|
int height, int stride)
|
||||||
|
{
|
||||||
|
struct qxl_drm_image *image;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
image = kmalloc(sizeof(struct qxl_drm_image), GFP_KERNEL);
|
||||||
|
if (!image)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
INIT_LIST_HEAD(&image->chunk_list);
|
||||||
|
|
||||||
|
ret = qxl_alloc_bo_reserved(qdev, release, sizeof(struct qxl_image), &image->bo);
|
||||||
|
if (ret) {
|
||||||
|
kfree(image);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = qxl_allocate_chunk(qdev, release, image, sizeof(struct qxl_data_chunk) + stride * height);
|
||||||
|
if (ret) {
|
||||||
|
qxl_bo_unref(&image->bo);
|
||||||
|
kfree(image);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
*image_ptr = image;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void qxl_image_free_objects(struct qxl_device *qdev, struct qxl_drm_image *dimage)
|
||||||
|
{
|
||||||
|
struct qxl_drm_chunk *chunk, *tmp;
|
||||||
|
|
||||||
|
list_for_each_entry_safe(chunk, tmp, &dimage->chunk_list, head) {
|
||||||
|
qxl_bo_unref(&chunk->bo);
|
||||||
|
kfree(chunk);
|
||||||
|
}
|
||||||
|
|
||||||
|
qxl_bo_unref(&dimage->bo);
|
||||||
|
kfree(dimage);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
qxl_image_init_helper(struct qxl_device *qdev,
|
||||||
|
struct qxl_release *release,
|
||||||
|
struct qxl_drm_image *dimage,
|
||||||
|
const uint8_t *data,
|
||||||
|
int width, int height,
|
||||||
|
int depth, unsigned int hash,
|
||||||
|
int stride)
|
||||||
|
{
|
||||||
|
struct qxl_drm_chunk *drv_chunk;
|
||||||
struct qxl_image *image;
|
struct qxl_image *image;
|
||||||
struct qxl_data_chunk *chunk;
|
struct qxl_data_chunk *chunk;
|
||||||
int i;
|
int i;
|
||||||
int chunk_stride;
|
int chunk_stride;
|
||||||
int linesize = width * depth / 8;
|
int linesize = width * depth / 8;
|
||||||
struct qxl_bo *chunk_bo;
|
struct qxl_bo *chunk_bo, *image_bo;
|
||||||
int ret;
|
|
||||||
void *ptr;
|
void *ptr;
|
||||||
/* Chunk */
|
/* Chunk */
|
||||||
/* FIXME: Check integer overflow */
|
/* FIXME: Check integer overflow */
|
||||||
/* TODO: variable number of chunks */
|
/* TODO: variable number of chunks */
|
||||||
|
|
||||||
|
drv_chunk = list_first_entry(&dimage->chunk_list, struct qxl_drm_chunk, head);
|
||||||
|
|
||||||
|
chunk_bo = drv_chunk->bo;
|
||||||
chunk_stride = stride; /* TODO: should use linesize, but it renders
|
chunk_stride = stride; /* TODO: should use linesize, but it renders
|
||||||
wrong (check the bitmaps are sent correctly
|
wrong (check the bitmaps are sent correctly
|
||||||
first) */
|
first) */
|
||||||
ret = qxl_alloc_bo_reserved(qdev, sizeof(*chunk) + height * chunk_stride,
|
|
||||||
&chunk_bo);
|
|
||||||
|
|
||||||
ptr = qxl_bo_kmap_atomic_page(qdev, chunk_bo, 0);
|
ptr = qxl_bo_kmap_atomic_page(qdev, chunk_bo, 0);
|
||||||
chunk = ptr;
|
chunk = ptr;
|
||||||
chunk->data_size = height * chunk_stride;
|
chunk->data_size = height * chunk_stride;
|
||||||
|
@ -102,7 +171,6 @@ qxl_image_create_helper(struct qxl_device *qdev,
|
||||||
while (remain > 0) {
|
while (remain > 0) {
|
||||||
page_base = out_offset & PAGE_MASK;
|
page_base = out_offset & PAGE_MASK;
|
||||||
page_offset = offset_in_page(out_offset);
|
page_offset = offset_in_page(out_offset);
|
||||||
|
|
||||||
size = min((int)(PAGE_SIZE - page_offset), remain);
|
size = min((int)(PAGE_SIZE - page_offset), remain);
|
||||||
|
|
||||||
ptr = qxl_bo_kmap_atomic_page(qdev, chunk_bo, page_base);
|
ptr = qxl_bo_kmap_atomic_page(qdev, chunk_bo, page_base);
|
||||||
|
@ -116,14 +184,10 @@ qxl_image_create_helper(struct qxl_device *qdev,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
qxl_bo_kunmap(chunk_bo);
|
qxl_bo_kunmap(chunk_bo);
|
||||||
|
|
||||||
/* Image */
|
image_bo = dimage->bo;
|
||||||
ret = qxl_alloc_bo_reserved(qdev, sizeof(*image), image_bo);
|
ptr = qxl_bo_kmap_atomic_page(qdev, image_bo, 0);
|
||||||
|
|
||||||
ptr = qxl_bo_kmap_atomic_page(qdev, *image_bo, 0);
|
|
||||||
image = ptr;
|
image = ptr;
|
||||||
|
|
||||||
image->descriptor.id = 0;
|
image->descriptor.id = 0;
|
||||||
|
@ -154,23 +218,20 @@ qxl_image_create_helper(struct qxl_device *qdev,
|
||||||
image->u.bitmap.stride = chunk_stride;
|
image->u.bitmap.stride = chunk_stride;
|
||||||
image->u.bitmap.palette = 0;
|
image->u.bitmap.palette = 0;
|
||||||
image->u.bitmap.data = qxl_bo_physical_address(qdev, chunk_bo, 0);
|
image->u.bitmap.data = qxl_bo_physical_address(qdev, chunk_bo, 0);
|
||||||
qxl_release_add_res(qdev, release, chunk_bo);
|
|
||||||
qxl_bo_unreserve(chunk_bo);
|
|
||||||
qxl_bo_unref(&chunk_bo);
|
|
||||||
|
|
||||||
qxl_bo_kunmap_atomic_page(qdev, *image_bo, ptr);
|
qxl_bo_kunmap_atomic_page(qdev, image_bo, ptr);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int qxl_image_create(struct qxl_device *qdev,
|
int qxl_image_init(struct qxl_device *qdev,
|
||||||
struct qxl_release *release,
|
struct qxl_release *release,
|
||||||
struct qxl_bo **image_bo,
|
struct qxl_drm_image *dimage,
|
||||||
const uint8_t *data,
|
const uint8_t *data,
|
||||||
int x, int y, int width, int height,
|
int x, int y, int width, int height,
|
||||||
int depth, int stride)
|
int depth, int stride)
|
||||||
{
|
{
|
||||||
data += y * stride + x * (depth / 8);
|
data += y * stride + x * (depth / 8);
|
||||||
return qxl_image_create_helper(qdev, release, image_bo, data,
|
return qxl_image_init_helper(qdev, release, dimage, data,
|
||||||
width, height, depth, 0, stride);
|
width, height, depth, 0, stride);
|
||||||
}
|
}
|
||||||
|
|
|
@ -68,55 +68,60 @@ static int qxl_map_ioctl(struct drm_device *dev, void *data,
|
||||||
&qxl_map->offset);
|
&qxl_map->offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct qxl_reloc_info {
|
||||||
|
int type;
|
||||||
|
struct qxl_bo *dst_bo;
|
||||||
|
uint32_t dst_offset;
|
||||||
|
struct qxl_bo *src_bo;
|
||||||
|
int src_offset;
|
||||||
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* dst must be validated, i.e. whole bo on vram/surfacesram (right now all bo's
|
* dst must be validated, i.e. whole bo on vram/surfacesram (right now all bo's
|
||||||
* are on vram).
|
* are on vram).
|
||||||
* *(dst + dst_off) = qxl_bo_physical_address(src, src_off)
|
* *(dst + dst_off) = qxl_bo_physical_address(src, src_off)
|
||||||
*/
|
*/
|
||||||
static void
|
static void
|
||||||
apply_reloc(struct qxl_device *qdev, struct qxl_bo *dst, uint64_t dst_off,
|
apply_reloc(struct qxl_device *qdev, struct qxl_reloc_info *info)
|
||||||
struct qxl_bo *src, uint64_t src_off)
|
|
||||||
{
|
{
|
||||||
void *reloc_page;
|
void *reloc_page;
|
||||||
|
reloc_page = qxl_bo_kmap_atomic_page(qdev, info->dst_bo, info->dst_offset & PAGE_MASK);
|
||||||
reloc_page = qxl_bo_kmap_atomic_page(qdev, dst, dst_off & PAGE_MASK);
|
*(uint64_t *)(reloc_page + (info->dst_offset & ~PAGE_MASK)) = qxl_bo_physical_address(qdev,
|
||||||
*(uint64_t *)(reloc_page + (dst_off & ~PAGE_MASK)) = qxl_bo_physical_address(qdev,
|
info->src_bo,
|
||||||
src, src_off);
|
info->src_offset);
|
||||||
qxl_bo_kunmap_atomic_page(qdev, dst, reloc_page);
|
qxl_bo_kunmap_atomic_page(qdev, info->dst_bo, reloc_page);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
apply_surf_reloc(struct qxl_device *qdev, struct qxl_bo *dst, uint64_t dst_off,
|
apply_surf_reloc(struct qxl_device *qdev, struct qxl_reloc_info *info)
|
||||||
struct qxl_bo *src)
|
|
||||||
{
|
{
|
||||||
uint32_t id = 0;
|
uint32_t id = 0;
|
||||||
void *reloc_page;
|
void *reloc_page;
|
||||||
|
|
||||||
if (src && !src->is_primary)
|
if (info->src_bo && !info->src_bo->is_primary)
|
||||||
id = src->surface_id;
|
id = info->src_bo->surface_id;
|
||||||
|
|
||||||
reloc_page = qxl_bo_kmap_atomic_page(qdev, dst, dst_off & PAGE_MASK);
|
reloc_page = qxl_bo_kmap_atomic_page(qdev, info->dst_bo, info->dst_offset & PAGE_MASK);
|
||||||
*(uint32_t *)(reloc_page + (dst_off & ~PAGE_MASK)) = id;
|
*(uint32_t *)(reloc_page + (info->dst_offset & ~PAGE_MASK)) = id;
|
||||||
qxl_bo_kunmap_atomic_page(qdev, dst, reloc_page);
|
qxl_bo_kunmap_atomic_page(qdev, info->dst_bo, reloc_page);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* return holding the reference to this object */
|
/* return holding the reference to this object */
|
||||||
static struct qxl_bo *qxlhw_handle_to_bo(struct qxl_device *qdev,
|
static struct qxl_bo *qxlhw_handle_to_bo(struct qxl_device *qdev,
|
||||||
struct drm_file *file_priv, uint64_t handle,
|
struct drm_file *file_priv, uint64_t handle,
|
||||||
struct qxl_reloc_list *reloc_list)
|
struct qxl_release *release)
|
||||||
{
|
{
|
||||||
struct drm_gem_object *gobj;
|
struct drm_gem_object *gobj;
|
||||||
struct qxl_bo *qobj;
|
struct qxl_bo *qobj;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
gobj = drm_gem_object_lookup(qdev->ddev, file_priv, handle);
|
gobj = drm_gem_object_lookup(qdev->ddev, file_priv, handle);
|
||||||
if (!gobj) {
|
if (!gobj)
|
||||||
DRM_ERROR("bad bo handle %lld\n", handle);
|
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
|
||||||
qobj = gem_to_qxl_bo(gobj);
|
qobj = gem_to_qxl_bo(gobj);
|
||||||
|
|
||||||
ret = qxl_bo_list_add(reloc_list, qobj);
|
ret = qxl_release_list_add(release, qobj);
|
||||||
if (ret)
|
if (ret)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
|
@ -129,6 +134,155 @@ static struct qxl_bo *qxlhw_handle_to_bo(struct qxl_device *qdev,
|
||||||
* However, the command as passed from user space must *not* contain the initial
|
* However, the command as passed from user space must *not* contain the initial
|
||||||
* QXLReleaseInfo struct (first XXX bytes)
|
* QXLReleaseInfo struct (first XXX bytes)
|
||||||
*/
|
*/
|
||||||
|
static int qxl_process_single_command(struct qxl_device *qdev,
|
||||||
|
struct drm_qxl_command *cmd,
|
||||||
|
struct drm_file *file_priv)
|
||||||
|
{
|
||||||
|
struct qxl_reloc_info *reloc_info;
|
||||||
|
int release_type;
|
||||||
|
struct qxl_release *release;
|
||||||
|
struct qxl_bo *cmd_bo;
|
||||||
|
void *fb_cmd;
|
||||||
|
int i, j, ret, num_relocs;
|
||||||
|
int unwritten;
|
||||||
|
|
||||||
|
switch (cmd->type) {
|
||||||
|
case QXL_CMD_DRAW:
|
||||||
|
release_type = QXL_RELEASE_DRAWABLE;
|
||||||
|
break;
|
||||||
|
case QXL_CMD_SURFACE:
|
||||||
|
case QXL_CMD_CURSOR:
|
||||||
|
default:
|
||||||
|
DRM_DEBUG("Only draw commands in execbuffers\n");
|
||||||
|
return -EINVAL;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cmd->command_size > PAGE_SIZE - sizeof(union qxl_release_info))
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
if (!access_ok(VERIFY_READ,
|
||||||
|
(void *)(unsigned long)cmd->command,
|
||||||
|
cmd->command_size))
|
||||||
|
return -EFAULT;
|
||||||
|
|
||||||
|
reloc_info = kmalloc(sizeof(struct qxl_reloc_info) * cmd->relocs_num, GFP_KERNEL);
|
||||||
|
if (!reloc_info)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
ret = qxl_alloc_release_reserved(qdev,
|
||||||
|
sizeof(union qxl_release_info) +
|
||||||
|
cmd->command_size,
|
||||||
|
release_type,
|
||||||
|
&release,
|
||||||
|
&cmd_bo);
|
||||||
|
if (ret)
|
||||||
|
goto out_free_reloc;
|
||||||
|
|
||||||
|
/* TODO copy slow path code from i915 */
|
||||||
|
fb_cmd = qxl_bo_kmap_atomic_page(qdev, cmd_bo, (release->release_offset & PAGE_SIZE));
|
||||||
|
unwritten = __copy_from_user_inatomic_nocache(fb_cmd + sizeof(union qxl_release_info) + (release->release_offset & ~PAGE_SIZE), (void *)(unsigned long)cmd->command, cmd->command_size);
|
||||||
|
|
||||||
|
{
|
||||||
|
struct qxl_drawable *draw = fb_cmd;
|
||||||
|
draw->mm_time = qdev->rom->mm_clock;
|
||||||
|
}
|
||||||
|
|
||||||
|
qxl_bo_kunmap_atomic_page(qdev, cmd_bo, fb_cmd);
|
||||||
|
if (unwritten) {
|
||||||
|
DRM_ERROR("got unwritten %d\n", unwritten);
|
||||||
|
ret = -EFAULT;
|
||||||
|
goto out_free_release;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* fill out reloc info structs */
|
||||||
|
num_relocs = 0;
|
||||||
|
for (i = 0; i < cmd->relocs_num; ++i) {
|
||||||
|
struct drm_qxl_reloc reloc;
|
||||||
|
|
||||||
|
if (DRM_COPY_FROM_USER(&reloc,
|
||||||
|
&((struct drm_qxl_reloc *)(uintptr_t)cmd->relocs)[i],
|
||||||
|
sizeof(reloc))) {
|
||||||
|
ret = -EFAULT;
|
||||||
|
goto out_free_bos;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* add the bos to the list of bos to validate -
|
||||||
|
need to validate first then process relocs? */
|
||||||
|
if (reloc.reloc_type != QXL_RELOC_TYPE_BO && reloc.reloc_type != QXL_RELOC_TYPE_SURF) {
|
||||||
|
DRM_DEBUG("unknown reloc type %d\n", reloc_info[i].type);
|
||||||
|
|
||||||
|
ret = -EINVAL;
|
||||||
|
goto out_free_bos;
|
||||||
|
}
|
||||||
|
reloc_info[i].type = reloc.reloc_type;
|
||||||
|
|
||||||
|
if (reloc.dst_handle) {
|
||||||
|
reloc_info[i].dst_bo = qxlhw_handle_to_bo(qdev, file_priv,
|
||||||
|
reloc.dst_handle, release);
|
||||||
|
if (!reloc_info[i].dst_bo) {
|
||||||
|
ret = -EINVAL;
|
||||||
|
reloc_info[i].src_bo = NULL;
|
||||||
|
goto out_free_bos;
|
||||||
|
}
|
||||||
|
reloc_info[i].dst_offset = reloc.dst_offset;
|
||||||
|
} else {
|
||||||
|
reloc_info[i].dst_bo = cmd_bo;
|
||||||
|
reloc_info[i].dst_offset = reloc.dst_offset + release->release_offset;
|
||||||
|
}
|
||||||
|
num_relocs++;
|
||||||
|
|
||||||
|
/* reserve and validate the reloc dst bo */
|
||||||
|
if (reloc.reloc_type == QXL_RELOC_TYPE_BO || reloc.src_handle > 0) {
|
||||||
|
reloc_info[i].src_bo =
|
||||||
|
qxlhw_handle_to_bo(qdev, file_priv,
|
||||||
|
reloc.src_handle, release);
|
||||||
|
if (!reloc_info[i].src_bo) {
|
||||||
|
if (reloc_info[i].dst_bo != cmd_bo)
|
||||||
|
drm_gem_object_unreference_unlocked(&reloc_info[i].dst_bo->gem_base);
|
||||||
|
ret = -EINVAL;
|
||||||
|
goto out_free_bos;
|
||||||
|
}
|
||||||
|
reloc_info[i].src_offset = reloc.src_offset;
|
||||||
|
} else {
|
||||||
|
reloc_info[i].src_bo = NULL;
|
||||||
|
reloc_info[i].src_offset = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* validate all buffers */
|
||||||
|
ret = qxl_release_reserve_list(release, false);
|
||||||
|
if (ret)
|
||||||
|
goto out_free_bos;
|
||||||
|
|
||||||
|
for (i = 0; i < cmd->relocs_num; ++i) {
|
||||||
|
if (reloc_info[i].type == QXL_RELOC_TYPE_BO)
|
||||||
|
apply_reloc(qdev, &reloc_info[i]);
|
||||||
|
else if (reloc_info[i].type == QXL_RELOC_TYPE_SURF)
|
||||||
|
apply_surf_reloc(qdev, &reloc_info[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = qxl_push_command_ring_release(qdev, release, cmd->type, true);
|
||||||
|
if (ret)
|
||||||
|
qxl_release_backoff_reserve_list(release);
|
||||||
|
else
|
||||||
|
qxl_release_fence_buffer_objects(release);
|
||||||
|
|
||||||
|
out_free_bos:
|
||||||
|
for (j = 0; j < num_relocs; j++) {
|
||||||
|
if (reloc_info[j].dst_bo != cmd_bo)
|
||||||
|
drm_gem_object_unreference_unlocked(&reloc_info[j].dst_bo->gem_base);
|
||||||
|
if (reloc_info[j].src_bo && reloc_info[j].src_bo != cmd_bo)
|
||||||
|
drm_gem_object_unreference_unlocked(&reloc_info[j].src_bo->gem_base);
|
||||||
|
}
|
||||||
|
out_free_release:
|
||||||
|
if (ret)
|
||||||
|
qxl_release_free(qdev, release);
|
||||||
|
out_free_reloc:
|
||||||
|
kfree(reloc_info);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
static int qxl_execbuffer_ioctl(struct drm_device *dev, void *data,
|
static int qxl_execbuffer_ioctl(struct drm_device *dev, void *data,
|
||||||
struct drm_file *file_priv)
|
struct drm_file *file_priv)
|
||||||
{
|
{
|
||||||
|
@ -136,144 +290,21 @@ static int qxl_execbuffer_ioctl(struct drm_device *dev, void *data,
|
||||||
struct drm_qxl_execbuffer *execbuffer = data;
|
struct drm_qxl_execbuffer *execbuffer = data;
|
||||||
struct drm_qxl_command user_cmd;
|
struct drm_qxl_command user_cmd;
|
||||||
int cmd_num;
|
int cmd_num;
|
||||||
struct qxl_bo *reloc_src_bo;
|
int ret;
|
||||||
struct qxl_bo *reloc_dst_bo;
|
|
||||||
struct drm_qxl_reloc reloc;
|
|
||||||
void *fb_cmd;
|
|
||||||
int i, ret;
|
|
||||||
struct qxl_reloc_list reloc_list;
|
|
||||||
int unwritten;
|
|
||||||
uint32_t reloc_dst_offset;
|
|
||||||
INIT_LIST_HEAD(&reloc_list.bos);
|
|
||||||
|
|
||||||
for (cmd_num = 0; cmd_num < execbuffer->commands_num; ++cmd_num) {
|
for (cmd_num = 0; cmd_num < execbuffer->commands_num; ++cmd_num) {
|
||||||
struct qxl_release *release;
|
|
||||||
struct qxl_bo *cmd_bo;
|
|
||||||
int release_type;
|
|
||||||
struct drm_qxl_command *commands =
|
struct drm_qxl_command *commands =
|
||||||
(struct drm_qxl_command *)(uintptr_t)execbuffer->commands;
|
(struct drm_qxl_command *)(uintptr_t)execbuffer->commands;
|
||||||
|
|
||||||
if (DRM_COPY_FROM_USER(&user_cmd, &commands[cmd_num],
|
if (DRM_COPY_FROM_USER(&user_cmd, &commands[cmd_num],
|
||||||
sizeof(user_cmd)))
|
sizeof(user_cmd)))
|
||||||
return -EFAULT;
|
return -EFAULT;
|
||||||
switch (user_cmd.type) {
|
|
||||||
case QXL_CMD_DRAW:
|
|
||||||
release_type = QXL_RELEASE_DRAWABLE;
|
|
||||||
break;
|
|
||||||
case QXL_CMD_SURFACE:
|
|
||||||
case QXL_CMD_CURSOR:
|
|
||||||
default:
|
|
||||||
DRM_DEBUG("Only draw commands in execbuffers\n");
|
|
||||||
return -EINVAL;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (user_cmd.command_size > PAGE_SIZE - sizeof(union qxl_release_info))
|
ret = qxl_process_single_command(qdev, &user_cmd, file_priv);
|
||||||
return -EINVAL;
|
|
||||||
|
|
||||||
if (!access_ok(VERIFY_READ,
|
|
||||||
(void *)(unsigned long)user_cmd.command,
|
|
||||||
user_cmd.command_size))
|
|
||||||
return -EFAULT;
|
|
||||||
|
|
||||||
ret = qxl_alloc_release_reserved(qdev,
|
|
||||||
sizeof(union qxl_release_info) +
|
|
||||||
user_cmd.command_size,
|
|
||||||
release_type,
|
|
||||||
&release,
|
|
||||||
&cmd_bo);
|
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
/* TODO copy slow path code from i915 */
|
|
||||||
fb_cmd = qxl_bo_kmap_atomic_page(qdev, cmd_bo, (release->release_offset & PAGE_SIZE));
|
|
||||||
unwritten = __copy_from_user_inatomic_nocache(fb_cmd + sizeof(union qxl_release_info) + (release->release_offset & ~PAGE_SIZE), (void *)(unsigned long)user_cmd.command, user_cmd.command_size);
|
|
||||||
|
|
||||||
{
|
|
||||||
struct qxl_drawable *draw = fb_cmd;
|
|
||||||
|
|
||||||
draw->mm_time = qdev->rom->mm_clock;
|
|
||||||
}
|
|
||||||
qxl_bo_kunmap_atomic_page(qdev, cmd_bo, fb_cmd);
|
|
||||||
if (unwritten) {
|
|
||||||
DRM_ERROR("got unwritten %d\n", unwritten);
|
|
||||||
qxl_release_unreserve(qdev, release);
|
|
||||||
qxl_release_free(qdev, release);
|
|
||||||
return -EFAULT;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (i = 0 ; i < user_cmd.relocs_num; ++i) {
|
|
||||||
if (DRM_COPY_FROM_USER(&reloc,
|
|
||||||
&((struct drm_qxl_reloc *)(uintptr_t)user_cmd.relocs)[i],
|
|
||||||
sizeof(reloc))) {
|
|
||||||
qxl_bo_list_unreserve(&reloc_list, true);
|
|
||||||
qxl_release_unreserve(qdev, release);
|
|
||||||
qxl_release_free(qdev, release);
|
|
||||||
return -EFAULT;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* add the bos to the list of bos to validate -
|
|
||||||
need to validate first then process relocs? */
|
|
||||||
if (reloc.dst_handle) {
|
|
||||||
reloc_dst_bo = qxlhw_handle_to_bo(qdev, file_priv,
|
|
||||||
reloc.dst_handle, &reloc_list);
|
|
||||||
if (!reloc_dst_bo) {
|
|
||||||
qxl_bo_list_unreserve(&reloc_list, true);
|
|
||||||
qxl_release_unreserve(qdev, release);
|
|
||||||
qxl_release_free(qdev, release);
|
|
||||||
return -EINVAL;
|
|
||||||
}
|
|
||||||
reloc_dst_offset = 0;
|
|
||||||
} else {
|
|
||||||
reloc_dst_bo = cmd_bo;
|
|
||||||
reloc_dst_offset = release->release_offset;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* reserve and validate the reloc dst bo */
|
|
||||||
if (reloc.reloc_type == QXL_RELOC_TYPE_BO || reloc.src_handle > 0) {
|
|
||||||
reloc_src_bo =
|
|
||||||
qxlhw_handle_to_bo(qdev, file_priv,
|
|
||||||
reloc.src_handle, &reloc_list);
|
|
||||||
if (!reloc_src_bo) {
|
|
||||||
if (reloc_dst_bo != cmd_bo)
|
|
||||||
drm_gem_object_unreference_unlocked(&reloc_dst_bo->gem_base);
|
|
||||||
qxl_bo_list_unreserve(&reloc_list, true);
|
|
||||||
qxl_release_unreserve(qdev, release);
|
|
||||||
qxl_release_free(qdev, release);
|
|
||||||
return -EINVAL;
|
|
||||||
}
|
|
||||||
} else
|
|
||||||
reloc_src_bo = NULL;
|
|
||||||
if (reloc.reloc_type == QXL_RELOC_TYPE_BO) {
|
|
||||||
apply_reloc(qdev, reloc_dst_bo, reloc_dst_offset + reloc.dst_offset,
|
|
||||||
reloc_src_bo, reloc.src_offset);
|
|
||||||
} else if (reloc.reloc_type == QXL_RELOC_TYPE_SURF) {
|
|
||||||
apply_surf_reloc(qdev, reloc_dst_bo, reloc_dst_offset + reloc.dst_offset, reloc_src_bo);
|
|
||||||
} else {
|
|
||||||
DRM_ERROR("unknown reloc type %d\n", reloc.reloc_type);
|
|
||||||
return -EINVAL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (reloc_src_bo && reloc_src_bo != cmd_bo) {
|
|
||||||
qxl_release_add_res(qdev, release, reloc_src_bo);
|
|
||||||
drm_gem_object_unreference_unlocked(&reloc_src_bo->gem_base);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (reloc_dst_bo != cmd_bo)
|
|
||||||
drm_gem_object_unreference_unlocked(&reloc_dst_bo->gem_base);
|
|
||||||
}
|
|
||||||
qxl_fence_releaseable(qdev, release);
|
|
||||||
|
|
||||||
ret = qxl_push_command_ring_release(qdev, release, user_cmd.type, true);
|
|
||||||
if (ret == -ERESTARTSYS) {
|
|
||||||
qxl_release_unreserve(qdev, release);
|
|
||||||
qxl_release_free(qdev, release);
|
|
||||||
qxl_bo_list_unreserve(&reloc_list, true);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
qxl_release_unreserve(qdev, release);
|
|
||||||
}
|
}
|
||||||
qxl_bo_list_unreserve(&reloc_list, 0);
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -305,7 +336,7 @@ static int qxl_update_area_ioctl(struct drm_device *dev, void *data,
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
if (!qobj->pin_count) {
|
if (!qobj->pin_count) {
|
||||||
qxl_ttm_placement_from_domain(qobj, qobj->type);
|
qxl_ttm_placement_from_domain(qobj, qobj->type, false);
|
||||||
ret = ttm_bo_validate(&qobj->tbo, &qobj->placement,
|
ret = ttm_bo_validate(&qobj->tbo, &qobj->placement,
|
||||||
true, false);
|
true, false);
|
||||||
if (unlikely(ret))
|
if (unlikely(ret))
|
||||||
|
|
|
@ -51,20 +51,21 @@ bool qxl_ttm_bo_is_qxl_bo(struct ttm_buffer_object *bo)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void qxl_ttm_placement_from_domain(struct qxl_bo *qbo, u32 domain)
|
void qxl_ttm_placement_from_domain(struct qxl_bo *qbo, u32 domain, bool pinned)
|
||||||
{
|
{
|
||||||
u32 c = 0;
|
u32 c = 0;
|
||||||
|
u32 pflag = pinned ? TTM_PL_FLAG_NO_EVICT : 0;
|
||||||
|
|
||||||
qbo->placement.fpfn = 0;
|
qbo->placement.fpfn = 0;
|
||||||
qbo->placement.lpfn = 0;
|
qbo->placement.lpfn = 0;
|
||||||
qbo->placement.placement = qbo->placements;
|
qbo->placement.placement = qbo->placements;
|
||||||
qbo->placement.busy_placement = qbo->placements;
|
qbo->placement.busy_placement = qbo->placements;
|
||||||
if (domain == QXL_GEM_DOMAIN_VRAM)
|
if (domain == QXL_GEM_DOMAIN_VRAM)
|
||||||
qbo->placements[c++] = TTM_PL_FLAG_CACHED | TTM_PL_FLAG_VRAM;
|
qbo->placements[c++] = TTM_PL_FLAG_CACHED | TTM_PL_FLAG_VRAM | pflag;
|
||||||
if (domain == QXL_GEM_DOMAIN_SURFACE)
|
if (domain == QXL_GEM_DOMAIN_SURFACE)
|
||||||
qbo->placements[c++] = TTM_PL_FLAG_CACHED | TTM_PL_FLAG_PRIV0;
|
qbo->placements[c++] = TTM_PL_FLAG_CACHED | TTM_PL_FLAG_PRIV0 | pflag;
|
||||||
if (domain == QXL_GEM_DOMAIN_CPU)
|
if (domain == QXL_GEM_DOMAIN_CPU)
|
||||||
qbo->placements[c++] = TTM_PL_MASK_CACHING | TTM_PL_FLAG_SYSTEM;
|
qbo->placements[c++] = TTM_PL_MASK_CACHING | TTM_PL_FLAG_SYSTEM | pflag;
|
||||||
if (!c)
|
if (!c)
|
||||||
qbo->placements[c++] = TTM_PL_MASK_CACHING | TTM_PL_FLAG_SYSTEM;
|
qbo->placements[c++] = TTM_PL_MASK_CACHING | TTM_PL_FLAG_SYSTEM;
|
||||||
qbo->placement.num_placement = c;
|
qbo->placement.num_placement = c;
|
||||||
|
@ -73,7 +74,7 @@ void qxl_ttm_placement_from_domain(struct qxl_bo *qbo, u32 domain)
|
||||||
|
|
||||||
|
|
||||||
int qxl_bo_create(struct qxl_device *qdev,
|
int qxl_bo_create(struct qxl_device *qdev,
|
||||||
unsigned long size, bool kernel, u32 domain,
|
unsigned long size, bool kernel, bool pinned, u32 domain,
|
||||||
struct qxl_surface *surf,
|
struct qxl_surface *surf,
|
||||||
struct qxl_bo **bo_ptr)
|
struct qxl_bo **bo_ptr)
|
||||||
{
|
{
|
||||||
|
@ -99,15 +100,15 @@ int qxl_bo_create(struct qxl_device *qdev,
|
||||||
}
|
}
|
||||||
bo->gem_base.driver_private = NULL;
|
bo->gem_base.driver_private = NULL;
|
||||||
bo->type = domain;
|
bo->type = domain;
|
||||||
bo->pin_count = 0;
|
bo->pin_count = pinned ? 1 : 0;
|
||||||
bo->surface_id = 0;
|
bo->surface_id = 0;
|
||||||
qxl_fence_init(qdev, &bo->fence);
|
qxl_fence_init(qdev, &bo->fence);
|
||||||
INIT_LIST_HEAD(&bo->list);
|
INIT_LIST_HEAD(&bo->list);
|
||||||
atomic_set(&bo->reserve_count, 0);
|
|
||||||
if (surf)
|
if (surf)
|
||||||
bo->surf = *surf;
|
bo->surf = *surf;
|
||||||
|
|
||||||
qxl_ttm_placement_from_domain(bo, domain);
|
qxl_ttm_placement_from_domain(bo, domain, pinned);
|
||||||
|
|
||||||
r = ttm_bo_init(&qdev->mman.bdev, &bo->tbo, size, type,
|
r = ttm_bo_init(&qdev->mman.bdev, &bo->tbo, size, type,
|
||||||
&bo->placement, 0, !kernel, NULL, size,
|
&bo->placement, 0, !kernel, NULL, size,
|
||||||
|
@ -228,7 +229,7 @@ struct qxl_bo *qxl_bo_ref(struct qxl_bo *bo)
|
||||||
int qxl_bo_pin(struct qxl_bo *bo, u32 domain, u64 *gpu_addr)
|
int qxl_bo_pin(struct qxl_bo *bo, u32 domain, u64 *gpu_addr)
|
||||||
{
|
{
|
||||||
struct qxl_device *qdev = (struct qxl_device *)bo->gem_base.dev->dev_private;
|
struct qxl_device *qdev = (struct qxl_device *)bo->gem_base.dev->dev_private;
|
||||||
int r, i;
|
int r;
|
||||||
|
|
||||||
if (bo->pin_count) {
|
if (bo->pin_count) {
|
||||||
bo->pin_count++;
|
bo->pin_count++;
|
||||||
|
@ -236,9 +237,7 @@ int qxl_bo_pin(struct qxl_bo *bo, u32 domain, u64 *gpu_addr)
|
||||||
*gpu_addr = qxl_bo_gpu_offset(bo);
|
*gpu_addr = qxl_bo_gpu_offset(bo);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
qxl_ttm_placement_from_domain(bo, domain);
|
qxl_ttm_placement_from_domain(bo, domain, true);
|
||||||
for (i = 0; i < bo->placement.num_placement; i++)
|
|
||||||
bo->placements[i] |= TTM_PL_FLAG_NO_EVICT;
|
|
||||||
r = ttm_bo_validate(&bo->tbo, &bo->placement, false, false);
|
r = ttm_bo_validate(&bo->tbo, &bo->placement, false, false);
|
||||||
if (likely(r == 0)) {
|
if (likely(r == 0)) {
|
||||||
bo->pin_count = 1;
|
bo->pin_count = 1;
|
||||||
|
@ -317,53 +316,6 @@ int qxl_bo_check_id(struct qxl_device *qdev, struct qxl_bo *bo)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void qxl_bo_list_unreserve(struct qxl_reloc_list *reloc_list, bool failed)
|
|
||||||
{
|
|
||||||
struct qxl_bo_list *entry, *sf;
|
|
||||||
|
|
||||||
list_for_each_entry_safe(entry, sf, &reloc_list->bos, lhead) {
|
|
||||||
qxl_bo_unreserve(entry->bo);
|
|
||||||
list_del(&entry->lhead);
|
|
||||||
kfree(entry);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int qxl_bo_list_add(struct qxl_reloc_list *reloc_list, struct qxl_bo *bo)
|
|
||||||
{
|
|
||||||
struct qxl_bo_list *entry;
|
|
||||||
int ret;
|
|
||||||
|
|
||||||
list_for_each_entry(entry, &reloc_list->bos, lhead) {
|
|
||||||
if (entry->bo == bo)
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
entry = kmalloc(sizeof(struct qxl_bo_list), GFP_KERNEL);
|
|
||||||
if (!entry)
|
|
||||||
return -ENOMEM;
|
|
||||||
|
|
||||||
entry->bo = bo;
|
|
||||||
list_add(&entry->lhead, &reloc_list->bos);
|
|
||||||
|
|
||||||
ret = qxl_bo_reserve(bo, false);
|
|
||||||
if (ret)
|
|
||||||
return ret;
|
|
||||||
|
|
||||||
if (!bo->pin_count) {
|
|
||||||
qxl_ttm_placement_from_domain(bo, bo->type);
|
|
||||||
ret = ttm_bo_validate(&bo->tbo, &bo->placement,
|
|
||||||
true, false);
|
|
||||||
if (ret)
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* allocate a surface for reserved + validated buffers */
|
|
||||||
ret = qxl_bo_check_id(bo->gem_base.dev->dev_private, bo);
|
|
||||||
if (ret)
|
|
||||||
return ret;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int qxl_surf_evict(struct qxl_device *qdev)
|
int qxl_surf_evict(struct qxl_device *qdev)
|
||||||
{
|
{
|
||||||
return ttm_bo_evict_mm(&qdev->mman.bdev, TTM_PL_PRIV0);
|
return ttm_bo_evict_mm(&qdev->mman.bdev, TTM_PL_PRIV0);
|
||||||
|
|
|
@ -88,7 +88,7 @@ static inline int qxl_bo_wait(struct qxl_bo *bo, u32 *mem_type,
|
||||||
|
|
||||||
extern int qxl_bo_create(struct qxl_device *qdev,
|
extern int qxl_bo_create(struct qxl_device *qdev,
|
||||||
unsigned long size,
|
unsigned long size,
|
||||||
bool kernel, u32 domain,
|
bool kernel, bool pinned, u32 domain,
|
||||||
struct qxl_surface *surf,
|
struct qxl_surface *surf,
|
||||||
struct qxl_bo **bo_ptr);
|
struct qxl_bo **bo_ptr);
|
||||||
extern int qxl_bo_kmap(struct qxl_bo *bo, void **ptr);
|
extern int qxl_bo_kmap(struct qxl_bo *bo, void **ptr);
|
||||||
|
@ -99,9 +99,7 @@ extern struct qxl_bo *qxl_bo_ref(struct qxl_bo *bo);
|
||||||
extern void qxl_bo_unref(struct qxl_bo **bo);
|
extern void qxl_bo_unref(struct qxl_bo **bo);
|
||||||
extern int qxl_bo_pin(struct qxl_bo *bo, u32 domain, u64 *gpu_addr);
|
extern int qxl_bo_pin(struct qxl_bo *bo, u32 domain, u64 *gpu_addr);
|
||||||
extern int qxl_bo_unpin(struct qxl_bo *bo);
|
extern int qxl_bo_unpin(struct qxl_bo *bo);
|
||||||
extern void qxl_ttm_placement_from_domain(struct qxl_bo *qbo, u32 domain);
|
extern void qxl_ttm_placement_from_domain(struct qxl_bo *qbo, u32 domain, bool pinned);
|
||||||
extern bool qxl_ttm_bo_is_qxl_bo(struct ttm_buffer_object *bo);
|
extern bool qxl_ttm_bo_is_qxl_bo(struct ttm_buffer_object *bo);
|
||||||
|
|
||||||
extern int qxl_bo_list_add(struct qxl_reloc_list *reloc_list, struct qxl_bo *bo);
|
|
||||||
extern void qxl_bo_list_unreserve(struct qxl_reloc_list *reloc_list, bool failed);
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -38,7 +38,8 @@
|
||||||
|
|
||||||
static const int release_size_per_bo[] = { RELEASE_SIZE, SURFACE_RELEASE_SIZE, RELEASE_SIZE };
|
static const int release_size_per_bo[] = { RELEASE_SIZE, SURFACE_RELEASE_SIZE, RELEASE_SIZE };
|
||||||
static const int releases_per_bo[] = { RELEASES_PER_BO, SURFACE_RELEASES_PER_BO, RELEASES_PER_BO };
|
static const int releases_per_bo[] = { RELEASES_PER_BO, SURFACE_RELEASES_PER_BO, RELEASES_PER_BO };
|
||||||
uint64_t
|
|
||||||
|
static uint64_t
|
||||||
qxl_release_alloc(struct qxl_device *qdev, int type,
|
qxl_release_alloc(struct qxl_device *qdev, int type,
|
||||||
struct qxl_release **ret)
|
struct qxl_release **ret)
|
||||||
{
|
{
|
||||||
|
@ -53,9 +54,9 @@ qxl_release_alloc(struct qxl_device *qdev, int type,
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
release->type = type;
|
release->type = type;
|
||||||
release->bo_count = 0;
|
|
||||||
release->release_offset = 0;
|
release->release_offset = 0;
|
||||||
release->surface_release_id = 0;
|
release->surface_release_id = 0;
|
||||||
|
INIT_LIST_HEAD(&release->bos);
|
||||||
|
|
||||||
idr_preload(GFP_KERNEL);
|
idr_preload(GFP_KERNEL);
|
||||||
spin_lock(&qdev->release_idr_lock);
|
spin_lock(&qdev->release_idr_lock);
|
||||||
|
@ -77,20 +78,20 @@ void
|
||||||
qxl_release_free(struct qxl_device *qdev,
|
qxl_release_free(struct qxl_device *qdev,
|
||||||
struct qxl_release *release)
|
struct qxl_release *release)
|
||||||
{
|
{
|
||||||
int i;
|
struct qxl_bo_list *entry, *tmp;
|
||||||
|
QXL_INFO(qdev, "release %d, type %d\n", release->id,
|
||||||
QXL_INFO(qdev, "release %d, type %d, %d bos\n", release->id,
|
release->type);
|
||||||
release->type, release->bo_count);
|
|
||||||
|
|
||||||
if (release->surface_release_id)
|
if (release->surface_release_id)
|
||||||
qxl_surface_id_dealloc(qdev, release->surface_release_id);
|
qxl_surface_id_dealloc(qdev, release->surface_release_id);
|
||||||
|
|
||||||
for (i = 0 ; i < release->bo_count; ++i) {
|
list_for_each_entry_safe(entry, tmp, &release->bos, tv.head) {
|
||||||
|
struct qxl_bo *bo = to_qxl_bo(entry->tv.bo);
|
||||||
QXL_INFO(qdev, "release %llx\n",
|
QXL_INFO(qdev, "release %llx\n",
|
||||||
release->bos[i]->tbo.addr_space_offset
|
entry->tv.bo->addr_space_offset
|
||||||
- DRM_FILE_OFFSET);
|
- DRM_FILE_OFFSET);
|
||||||
qxl_fence_remove_release(&release->bos[i]->fence, release->id);
|
qxl_fence_remove_release(&bo->fence, release->id);
|
||||||
qxl_bo_unref(&release->bos[i]);
|
qxl_bo_unref(&bo);
|
||||||
}
|
}
|
||||||
spin_lock(&qdev->release_idr_lock);
|
spin_lock(&qdev->release_idr_lock);
|
||||||
idr_remove(&qdev->release_idr, release->id);
|
idr_remove(&qdev->release_idr, release->id);
|
||||||
|
@ -98,83 +99,117 @@ qxl_release_free(struct qxl_device *qdev,
|
||||||
kfree(release);
|
kfree(release);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
|
||||||
qxl_release_add_res(struct qxl_device *qdev, struct qxl_release *release,
|
|
||||||
struct qxl_bo *bo)
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
for (i = 0; i < release->bo_count; i++)
|
|
||||||
if (release->bos[i] == bo)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (release->bo_count >= QXL_MAX_RES) {
|
|
||||||
DRM_ERROR("exceeded max resource on a qxl_release item\n");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
release->bos[release->bo_count++] = qxl_bo_ref(bo);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int qxl_release_bo_alloc(struct qxl_device *qdev,
|
static int qxl_release_bo_alloc(struct qxl_device *qdev,
|
||||||
struct qxl_bo **bo)
|
struct qxl_bo **bo)
|
||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
ret = qxl_bo_create(qdev, PAGE_SIZE, false, QXL_GEM_DOMAIN_VRAM, NULL,
|
/* pin releases bo's they are too messy to evict */
|
||||||
|
ret = qxl_bo_create(qdev, PAGE_SIZE, false, true,
|
||||||
|
QXL_GEM_DOMAIN_VRAM, NULL,
|
||||||
bo);
|
bo);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
int qxl_release_reserve(struct qxl_device *qdev,
|
int qxl_release_list_add(struct qxl_release *release, struct qxl_bo *bo)
|
||||||
struct qxl_release *release, bool no_wait)
|
{
|
||||||
|
struct qxl_bo_list *entry;
|
||||||
|
|
||||||
|
list_for_each_entry(entry, &release->bos, tv.head) {
|
||||||
|
if (entry->tv.bo == &bo->tbo)
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
entry = kmalloc(sizeof(struct qxl_bo_list), GFP_KERNEL);
|
||||||
|
if (!entry)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
qxl_bo_ref(bo);
|
||||||
|
entry->tv.bo = &bo->tbo;
|
||||||
|
list_add_tail(&entry->tv.head, &release->bos);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int qxl_release_validate_bo(struct qxl_bo *bo)
|
||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
if (atomic_inc_return(&release->bos[0]->reserve_count) == 1) {
|
|
||||||
ret = qxl_bo_reserve(release->bos[0], no_wait);
|
if (!bo->pin_count) {
|
||||||
|
qxl_ttm_placement_from_domain(bo, bo->type, false);
|
||||||
|
ret = ttm_bo_validate(&bo->tbo, &bo->placement,
|
||||||
|
true, false);
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* allocate a surface for reserved + validated buffers */
|
||||||
|
ret = qxl_bo_check_id(bo->gem_base.dev->dev_private, bo);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int qxl_release_reserve_list(struct qxl_release *release, bool no_intr)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
struct qxl_bo_list *entry;
|
||||||
|
|
||||||
|
/* if only one object on the release its the release itself
|
||||||
|
since these objects are pinned no need to reserve */
|
||||||
|
if (list_is_singular(&release->bos))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
ret = ttm_eu_reserve_buffers(&release->ticket, &release->bos);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
list_for_each_entry(entry, &release->bos, tv.head) {
|
||||||
|
struct qxl_bo *bo = to_qxl_bo(entry->tv.bo);
|
||||||
|
|
||||||
|
ret = qxl_release_validate_bo(bo);
|
||||||
|
if (ret) {
|
||||||
|
ttm_eu_backoff_reservation(&release->ticket, &release->bos);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void qxl_release_unreserve(struct qxl_device *qdev,
|
void qxl_release_backoff_reserve_list(struct qxl_release *release)
|
||||||
struct qxl_release *release)
|
|
||||||
{
|
{
|
||||||
if (atomic_dec_and_test(&release->bos[0]->reserve_count))
|
/* if only one object on the release its the release itself
|
||||||
qxl_bo_unreserve(release->bos[0]);
|
since these objects are pinned no need to reserve */
|
||||||
|
if (list_is_singular(&release->bos))
|
||||||
|
return;
|
||||||
|
|
||||||
|
ttm_eu_backoff_reservation(&release->ticket, &release->bos);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
int qxl_alloc_surface_release_reserved(struct qxl_device *qdev,
|
int qxl_alloc_surface_release_reserved(struct qxl_device *qdev,
|
||||||
enum qxl_surface_cmd_type surface_cmd_type,
|
enum qxl_surface_cmd_type surface_cmd_type,
|
||||||
struct qxl_release *create_rel,
|
struct qxl_release *create_rel,
|
||||||
struct qxl_release **release)
|
struct qxl_release **release)
|
||||||
{
|
{
|
||||||
int ret;
|
|
||||||
|
|
||||||
if (surface_cmd_type == QXL_SURFACE_CMD_DESTROY && create_rel) {
|
if (surface_cmd_type == QXL_SURFACE_CMD_DESTROY && create_rel) {
|
||||||
int idr_ret;
|
int idr_ret;
|
||||||
|
struct qxl_bo_list *entry = list_first_entry(&create_rel->bos, struct qxl_bo_list, tv.head);
|
||||||
struct qxl_bo *bo;
|
struct qxl_bo *bo;
|
||||||
union qxl_release_info *info;
|
union qxl_release_info *info;
|
||||||
|
|
||||||
/* stash the release after the create command */
|
/* stash the release after the create command */
|
||||||
idr_ret = qxl_release_alloc(qdev, QXL_RELEASE_SURFACE_CMD, release);
|
idr_ret = qxl_release_alloc(qdev, QXL_RELEASE_SURFACE_CMD, release);
|
||||||
bo = qxl_bo_ref(create_rel->bos[0]);
|
bo = qxl_bo_ref(to_qxl_bo(entry->tv.bo));
|
||||||
|
|
||||||
(*release)->release_offset = create_rel->release_offset + 64;
|
(*release)->release_offset = create_rel->release_offset + 64;
|
||||||
|
|
||||||
qxl_release_add_res(qdev, *release, bo);
|
qxl_release_list_add(*release, bo);
|
||||||
|
|
||||||
ret = qxl_release_reserve(qdev, *release, false);
|
|
||||||
if (ret) {
|
|
||||||
DRM_ERROR("release reserve failed\n");
|
|
||||||
goto out_unref;
|
|
||||||
}
|
|
||||||
info = qxl_release_map(qdev, *release);
|
info = qxl_release_map(qdev, *release);
|
||||||
info->id = idr_ret;
|
info->id = idr_ret;
|
||||||
qxl_release_unmap(qdev, *release, info);
|
qxl_release_unmap(qdev, *release, info);
|
||||||
|
|
||||||
|
|
||||||
out_unref:
|
|
||||||
qxl_bo_unref(&bo);
|
qxl_bo_unref(&bo);
|
||||||
return ret;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
return qxl_alloc_release_reserved(qdev, sizeof(struct qxl_surface_cmd),
|
return qxl_alloc_release_reserved(qdev, sizeof(struct qxl_surface_cmd),
|
||||||
|
@ -187,7 +222,7 @@ int qxl_alloc_release_reserved(struct qxl_device *qdev, unsigned long size,
|
||||||
{
|
{
|
||||||
struct qxl_bo *bo;
|
struct qxl_bo *bo;
|
||||||
int idr_ret;
|
int idr_ret;
|
||||||
int ret;
|
int ret = 0;
|
||||||
union qxl_release_info *info;
|
union qxl_release_info *info;
|
||||||
int cur_idx;
|
int cur_idx;
|
||||||
|
|
||||||
|
@ -216,11 +251,6 @@ int qxl_alloc_release_reserved(struct qxl_device *qdev, unsigned long size,
|
||||||
mutex_unlock(&qdev->release_mutex);
|
mutex_unlock(&qdev->release_mutex);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* pin releases bo's they are too messy to evict */
|
|
||||||
ret = qxl_bo_reserve(qdev->current_release_bo[cur_idx], false);
|
|
||||||
qxl_bo_pin(qdev->current_release_bo[cur_idx], QXL_GEM_DOMAIN_VRAM, NULL);
|
|
||||||
qxl_bo_unreserve(qdev->current_release_bo[cur_idx]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bo = qxl_bo_ref(qdev->current_release_bo[cur_idx]);
|
bo = qxl_bo_ref(qdev->current_release_bo[cur_idx]);
|
||||||
|
@ -231,36 +261,18 @@ int qxl_alloc_release_reserved(struct qxl_device *qdev, unsigned long size,
|
||||||
if (rbo)
|
if (rbo)
|
||||||
*rbo = bo;
|
*rbo = bo;
|
||||||
|
|
||||||
qxl_release_add_res(qdev, *release, bo);
|
|
||||||
|
|
||||||
ret = qxl_release_reserve(qdev, *release, false);
|
|
||||||
mutex_unlock(&qdev->release_mutex);
|
mutex_unlock(&qdev->release_mutex);
|
||||||
if (ret)
|
|
||||||
goto out_unref;
|
qxl_release_list_add(*release, bo);
|
||||||
|
|
||||||
info = qxl_release_map(qdev, *release);
|
info = qxl_release_map(qdev, *release);
|
||||||
info->id = idr_ret;
|
info->id = idr_ret;
|
||||||
qxl_release_unmap(qdev, *release, info);
|
qxl_release_unmap(qdev, *release, info);
|
||||||
|
|
||||||
out_unref:
|
|
||||||
qxl_bo_unref(&bo);
|
qxl_bo_unref(&bo);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
int qxl_fence_releaseable(struct qxl_device *qdev,
|
|
||||||
struct qxl_release *release)
|
|
||||||
{
|
|
||||||
int i, ret;
|
|
||||||
for (i = 0; i < release->bo_count; i++) {
|
|
||||||
if (!release->bos[i]->tbo.sync_obj)
|
|
||||||
release->bos[i]->tbo.sync_obj = &release->bos[i]->fence;
|
|
||||||
ret = qxl_fence_add_release(&release->bos[i]->fence, release->id);
|
|
||||||
if (ret)
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct qxl_release *qxl_release_from_id_locked(struct qxl_device *qdev,
|
struct qxl_release *qxl_release_from_id_locked(struct qxl_device *qdev,
|
||||||
uint64_t id)
|
uint64_t id)
|
||||||
{
|
{
|
||||||
|
@ -273,10 +285,7 @@ struct qxl_release *qxl_release_from_id_locked(struct qxl_device *qdev,
|
||||||
DRM_ERROR("failed to find id in release_idr\n");
|
DRM_ERROR("failed to find id in release_idr\n");
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
if (release->bo_count < 1) {
|
|
||||||
DRM_ERROR("read a released resource with 0 bos\n");
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
return release;
|
return release;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -285,9 +294,12 @@ union qxl_release_info *qxl_release_map(struct qxl_device *qdev,
|
||||||
{
|
{
|
||||||
void *ptr;
|
void *ptr;
|
||||||
union qxl_release_info *info;
|
union qxl_release_info *info;
|
||||||
struct qxl_bo *bo = release->bos[0];
|
struct qxl_bo_list *entry = list_first_entry(&release->bos, struct qxl_bo_list, tv.head);
|
||||||
|
struct qxl_bo *bo = to_qxl_bo(entry->tv.bo);
|
||||||
|
|
||||||
ptr = qxl_bo_kmap_atomic_page(qdev, bo, release->release_offset & PAGE_SIZE);
|
ptr = qxl_bo_kmap_atomic_page(qdev, bo, release->release_offset & PAGE_SIZE);
|
||||||
|
if (!ptr)
|
||||||
|
return NULL;
|
||||||
info = ptr + (release->release_offset & ~PAGE_SIZE);
|
info = ptr + (release->release_offset & ~PAGE_SIZE);
|
||||||
return info;
|
return info;
|
||||||
}
|
}
|
||||||
|
@ -296,9 +308,51 @@ void qxl_release_unmap(struct qxl_device *qdev,
|
||||||
struct qxl_release *release,
|
struct qxl_release *release,
|
||||||
union qxl_release_info *info)
|
union qxl_release_info *info)
|
||||||
{
|
{
|
||||||
struct qxl_bo *bo = release->bos[0];
|
struct qxl_bo_list *entry = list_first_entry(&release->bos, struct qxl_bo_list, tv.head);
|
||||||
|
struct qxl_bo *bo = to_qxl_bo(entry->tv.bo);
|
||||||
void *ptr;
|
void *ptr;
|
||||||
|
|
||||||
ptr = ((void *)info) - (release->release_offset & ~PAGE_SIZE);
|
ptr = ((void *)info) - (release->release_offset & ~PAGE_SIZE);
|
||||||
qxl_bo_kunmap_atomic_page(qdev, bo, ptr);
|
qxl_bo_kunmap_atomic_page(qdev, bo, ptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void qxl_release_fence_buffer_objects(struct qxl_release *release)
|
||||||
|
{
|
||||||
|
struct ttm_validate_buffer *entry;
|
||||||
|
struct ttm_buffer_object *bo;
|
||||||
|
struct ttm_bo_global *glob;
|
||||||
|
struct ttm_bo_device *bdev;
|
||||||
|
struct ttm_bo_driver *driver;
|
||||||
|
struct qxl_bo *qbo;
|
||||||
|
|
||||||
|
/* if only one object on the release its the release itself
|
||||||
|
since these objects are pinned no need to reserve */
|
||||||
|
if (list_is_singular(&release->bos))
|
||||||
|
return;
|
||||||
|
|
||||||
|
bo = list_first_entry(&release->bos, struct ttm_validate_buffer, head)->bo;
|
||||||
|
bdev = bo->bdev;
|
||||||
|
driver = bdev->driver;
|
||||||
|
glob = bo->glob;
|
||||||
|
|
||||||
|
spin_lock(&glob->lru_lock);
|
||||||
|
spin_lock(&bdev->fence_lock);
|
||||||
|
|
||||||
|
list_for_each_entry(entry, &release->bos, head) {
|
||||||
|
bo = entry->bo;
|
||||||
|
qbo = to_qxl_bo(bo);
|
||||||
|
|
||||||
|
if (!entry->bo->sync_obj)
|
||||||
|
entry->bo->sync_obj = &qbo->fence;
|
||||||
|
|
||||||
|
qxl_fence_add_release_locked(&qbo->fence, release->id);
|
||||||
|
|
||||||
|
ttm_bo_add_to_lru(bo);
|
||||||
|
ww_mutex_unlock(&bo->resv->lock);
|
||||||
|
entry->reserved = false;
|
||||||
|
}
|
||||||
|
spin_unlock(&bdev->fence_lock);
|
||||||
|
spin_unlock(&glob->lru_lock);
|
||||||
|
ww_acquire_fini(&release->ticket);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -206,7 +206,7 @@ static void qxl_evict_flags(struct ttm_buffer_object *bo,
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
qbo = container_of(bo, struct qxl_bo, tbo);
|
qbo = container_of(bo, struct qxl_bo, tbo);
|
||||||
qxl_ttm_placement_from_domain(qbo, QXL_GEM_DOMAIN_CPU);
|
qxl_ttm_placement_from_domain(qbo, QXL_GEM_DOMAIN_CPU, false);
|
||||||
*placement = qbo->placement;
|
*placement = qbo->placement;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue