2
0
mirror of https://github.com/edk2-porting/linux-next.git synced 2024-12-28 15:13:55 +08:00

staging: vboxvideo: Atomic phase 1: convert cursor to universal plane

In preparation for atomic conversion, let's use the transitional atomic
helpers drm_plane_helper_update/disable.

Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
Hans de Goede 2018-09-29 14:18:15 +02:00 committed by Greg Kroah-Hartman
parent e2f958d00b
commit 35f3288c45
2 changed files with 186 additions and 205 deletions

View File

@ -116,11 +116,6 @@ struct vbox_private {
* encompassing all screen ones or is the fbdev console active?
*/
bool single_framebuffer;
u32 cursor_width;
u32 cursor_height;
u32 cursor_hot_x;
u32 cursor_hot_y;
size_t cursor_data_size;
u8 cursor_data[CURSOR_DATA_SIZE];
};

View File

@ -34,16 +34,12 @@
#include <linux/export.h>
#include <drm/drm_crtc_helper.h>
#include <drm/drm_plane_helper.h>
#include <drm/drm_atomic_helper.h>
#include "vbox_drv.h"
#include "vboxvideo.h"
#include "hgsmi_channels.h"
static int vbox_cursor_set2(struct drm_crtc *crtc, struct drm_file *file_priv,
u32 handle, u32 width, u32 height,
s32 hot_x, s32 hot_y);
static int vbox_cursor_move(struct drm_crtc *crtc, int x, int y);
/**
* Set a graphics mode. Poke any required values into registers, do an HGSMI
* mode set and tell the host we support advanced graphics functions.
@ -318,14 +314,166 @@ static void vbox_crtc_destroy(struct drm_crtc *crtc)
}
static const struct drm_crtc_funcs vbox_crtc_funcs = {
.cursor_move = vbox_cursor_move,
.cursor_set2 = vbox_cursor_set2,
.reset = vbox_crtc_reset,
.set_config = drm_crtc_helper_set_config,
/* .gamma_set = vbox_crtc_gamma_set, */
.destroy = vbox_crtc_destroy,
};
static int vbox_cursor_atomic_check(struct drm_plane *plane,
struct drm_plane_state *new_state)
{
u32 width = new_state->crtc_w;
u32 height = new_state->crtc_h;
if (!new_state->fb)
return 0;
if (width > VBOX_MAX_CURSOR_WIDTH || height > VBOX_MAX_CURSOR_HEIGHT ||
width == 0 || height == 0)
return -EINVAL;
return 0;
}
/**
* Copy the ARGB image and generate the mask, which is needed in case the host
* does not support ARGB cursors. The mask is a 1BPP bitmap with the bit set
* if the corresponding alpha value in the ARGB image is greater than 0xF0.
*/
static void copy_cursor_image(u8 *src, u8 *dst, u32 width, u32 height,
size_t mask_size)
{
size_t line_size = (width + 7) / 8;
u32 i, j;
memcpy(dst + mask_size, src, width * height * 4);
for (i = 0; i < height; ++i)
for (j = 0; j < width; ++j)
if (((u32 *)src)[i * width + j] > 0xf0000000)
dst[i * line_size + j / 8] |= (0x80 >> (j % 8));
}
static void vbox_cursor_atomic_update(struct drm_plane *plane,
struct drm_plane_state *old_state)
{
struct vbox_private *vbox =
container_of(plane->dev, struct vbox_private, ddev);
struct vbox_crtc *vbox_crtc = to_vbox_crtc(plane->state->crtc);
struct drm_framebuffer *fb = plane->state->fb;
struct vbox_bo *bo = gem_to_vbox_bo(to_vbox_framebuffer(fb)->obj);
u32 width = plane->state->crtc_w;
u32 height = plane->state->crtc_h;
size_t data_size, mask_size;
u32 flags;
u8 *src;
/*
* VirtualBox uses the host windowing system to draw the cursor so
* moves are a no-op, we only need to upload new cursor sprites.
*/
if (fb == old_state->fb)
return;
mutex_lock(&vbox->hw_mutex);
vbox_crtc->cursor_enabled = true;
/* pinning is done in prepare/cleanup framebuffer */
src = vbox_bo_kmap(bo);
if (IS_ERR(src)) {
DRM_WARN("Could not kmap cursor bo, skipping update\n");
return;
}
/*
* The mask must be calculated based on the alpha
* channel, one bit per ARGB word, and must be 32-bit
* padded.
*/
mask_size = ((width + 7) / 8 * height + 3) & ~3;
data_size = width * height * 4 + mask_size;
copy_cursor_image(src, vbox->cursor_data, width, height, mask_size);
vbox_bo_kunmap(bo);
flags = VBOX_MOUSE_POINTER_VISIBLE | VBOX_MOUSE_POINTER_SHAPE |
VBOX_MOUSE_POINTER_ALPHA;
hgsmi_update_pointer_shape(vbox->guest_pool, flags,
min_t(u32, max(fb->hot_x, 0), width),
min_t(u32, max(fb->hot_y, 0), height),
width, height, vbox->cursor_data, data_size);
mutex_unlock(&vbox->hw_mutex);
}
void vbox_cursor_atomic_disable(struct drm_plane *plane,
struct drm_plane_state *old_state)
{
struct vbox_private *vbox =
container_of(plane->dev, struct vbox_private, ddev);
struct vbox_crtc *vbox_crtc = to_vbox_crtc(old_state->crtc);
bool cursor_enabled = false;
struct drm_crtc *crtci;
mutex_lock(&vbox->hw_mutex);
vbox_crtc->cursor_enabled = false;
list_for_each_entry(crtci, &vbox->ddev.mode_config.crtc_list, head) {
if (to_vbox_crtc(crtci)->cursor_enabled)
cursor_enabled = true;
}
if (!cursor_enabled)
hgsmi_update_pointer_shape(vbox->guest_pool, 0, 0, 0,
0, 0, NULL, 0);
mutex_unlock(&vbox->hw_mutex);
}
static int vbox_cursor_prepare_fb(struct drm_plane *plane,
struct drm_plane_state *new_state)
{
struct vbox_bo *bo;
if (!new_state->fb)
return 0;
bo = gem_to_vbox_bo(to_vbox_framebuffer(new_state->fb)->obj);
return vbox_bo_pin(bo, TTM_PL_FLAG_SYSTEM);
}
static void vbox_cursor_cleanup_fb(struct drm_plane *plane,
struct drm_plane_state *old_state)
{
struct vbox_bo *bo;
if (!plane->state->fb)
return;
bo = gem_to_vbox_bo(to_vbox_framebuffer(plane->state->fb)->obj);
vbox_bo_unpin(bo);
}
static const uint32_t vbox_cursor_plane_formats[] = {
DRM_FORMAT_ARGB8888,
};
static const struct drm_plane_helper_funcs vbox_cursor_helper_funcs = {
.atomic_check = vbox_cursor_atomic_check,
.atomic_update = vbox_cursor_atomic_update,
.atomic_disable = vbox_cursor_atomic_disable,
.prepare_fb = vbox_cursor_prepare_fb,
.cleanup_fb = vbox_cursor_cleanup_fb,
};
static const struct drm_plane_funcs vbox_cursor_plane_funcs = {
.update_plane = drm_plane_helper_update,
.disable_plane = drm_plane_helper_disable,
.destroy = drm_primary_helper_destroy,
};
static const uint32_t vbox_primary_plane_formats[] = {
DRM_FORMAT_XRGB8888,
DRM_FORMAT_ARGB8888,
@ -352,6 +500,11 @@ static struct drm_plane *vbox_create_plane(struct vbox_private *vbox,
funcs = &vbox_primary_plane_funcs;
formats = vbox_primary_plane_formats;
num_formats = ARRAY_SIZE(vbox_primary_plane_formats);
} else if (type == DRM_PLANE_TYPE_CURSOR) {
funcs = &vbox_cursor_plane_funcs;
formats = vbox_cursor_plane_formats;
helper_funcs = &vbox_cursor_helper_funcs;
num_formats = ARRAY_SIZE(vbox_cursor_plane_formats);
} else {
return ERR_PTR(-EINVAL);
}
@ -379,10 +532,17 @@ static struct vbox_crtc *vbox_crtc_init(struct drm_device *dev, unsigned int i)
{
struct vbox_private *vbox =
container_of(dev, struct vbox_private, ddev);
struct drm_plane *cursor = NULL;
struct vbox_crtc *vbox_crtc;
struct drm_plane *primary;
u32 caps = 0;
int ret;
ret = hgsmi_query_conf(vbox->guest_pool,
VBOX_VBVA_CONF32_CURSOR_CAPABILITIES, &caps);
if (ret)
return ERR_PTR(ret);
vbox_crtc = kzalloc(sizeof(*vbox_crtc), GFP_KERNEL);
if (!vbox_crtc)
return ERR_PTR(-ENOMEM);
@ -393,18 +553,33 @@ static struct vbox_crtc *vbox_crtc_init(struct drm_device *dev, unsigned int i)
goto free_mem;
}
if ((caps & VBOX_VBVA_CURSOR_CAPABILITY_HARDWARE)) {
cursor = vbox_create_plane(vbox, 1 << i, DRM_PLANE_TYPE_CURSOR);
if (IS_ERR(cursor)) {
ret = PTR_ERR(cursor);
goto clean_primary;
}
} else {
DRM_WARN("VirtualBox host is too old, no cursor support\n");
}
vbox_crtc->crtc_id = i;
ret = drm_crtc_init_with_planes(dev, &vbox_crtc->base, primary, NULL,
ret = drm_crtc_init_with_planes(dev, &vbox_crtc->base, primary, cursor,
&vbox_crtc_funcs, NULL);
if (ret)
goto clean_primary;
goto clean_cursor;
drm_mode_crtc_set_gamma_size(&vbox_crtc->base, 256);
drm_crtc_helper_add(&vbox_crtc->base, &vbox_crtc_helper_funcs);
return vbox_crtc;
clean_cursor:
if (cursor) {
drm_plane_cleanup(cursor);
kfree(cursor);
}
clean_primary:
drm_plane_cleanup(primary);
kfree(primary);
@ -721,13 +896,12 @@ int vbox_mode_init(struct vbox_private *vbox)
drm_mode_config_init(dev);
dev->mode_config.funcs = (void *)&vbox_mode_funcs;
dev->mode_config.min_width = 64;
dev->mode_config.min_height = 64;
dev->mode_config.min_width = 0;
dev->mode_config.min_height = 0;
dev->mode_config.preferred_depth = 24;
dev->mode_config.max_width = VBE_DISPI_MAX_XRES;
dev->mode_config.max_height = VBE_DISPI_MAX_YRES;
/* vbox_cursor_init(dev); */
for (i = 0; i < vbox->num_crtcs; ++i) {
vbox_crtc = vbox_crtc_init(dev, i);
if (IS_ERR(vbox_crtc)) {
@ -754,192 +928,4 @@ err_drm_mode_cleanup:
void vbox_mode_fini(struct vbox_private *vbox)
{
drm_mode_config_cleanup(&vbox->ddev);
/* vbox_cursor_fini(dev); */
}
/**
* Copy the ARGB image and generate the mask, which is needed in case the host
* does not support ARGB cursors. The mask is a 1BPP bitmap with the bit set
* if the corresponding alpha value in the ARGB image is greater than 0xF0.
*/
static void copy_cursor_image(u8 *src, u8 *dst, u32 width, u32 height,
size_t mask_size)
{
size_t line_size = (width + 7) / 8;
u32 i, j;
memcpy(dst + mask_size, src, width * height * 4);
for (i = 0; i < height; ++i)
for (j = 0; j < width; ++j)
if (((u32 *)src)[i * width + j] > 0xf0000000)
dst[i * line_size + j / 8] |= (0x80 >> (j % 8));
}
static int vbox_cursor_set2(struct drm_crtc *crtc, struct drm_file *file_priv,
u32 handle, u32 width, u32 height,
s32 hot_x, s32 hot_y)
{
struct vbox_private *vbox = crtc->dev->dev_private;
struct vbox_crtc *vbox_crtc = to_vbox_crtc(crtc);
struct ttm_bo_kmap_obj uobj_map;
size_t data_size, mask_size;
struct drm_gem_object *obj;
u32 flags, caps = 0;
struct vbox_bo *bo;
bool src_isiomem;
u8 *dst = NULL;
u8 *src;
int ret;
/*
* Re-set this regularly as in 5.0.20 and earlier the information was
* lost on save and restore.
*/
hgsmi_update_input_mapping(vbox->guest_pool, 0, 0,
vbox->input_mapping_width,
vbox->input_mapping_height);
if (!handle) {
bool cursor_enabled = false;
struct drm_crtc *crtci;
/* Hide cursor. */
vbox_crtc->cursor_enabled = false;
list_for_each_entry(crtci, &vbox->ddev.mode_config.crtc_list,
head) {
if (to_vbox_crtc(crtci)->cursor_enabled)
cursor_enabled = true;
}
if (!cursor_enabled)
hgsmi_update_pointer_shape(vbox->guest_pool, 0, 0, 0,
0, 0, NULL, 0);
return 0;
}
vbox_crtc->cursor_enabled = true;
if (width > VBOX_MAX_CURSOR_WIDTH || height > VBOX_MAX_CURSOR_HEIGHT ||
width == 0 || height == 0)
return -EINVAL;
ret = hgsmi_query_conf(vbox->guest_pool,
VBOX_VBVA_CONF32_CURSOR_CAPABILITIES, &caps);
if (ret)
return ret;
if (!(caps & VBOX_VBVA_CURSOR_CAPABILITY_HARDWARE)) {
/*
* -EINVAL means cursor_set2() not supported, -EAGAIN means
* retry at once.
*/
return -EBUSY;
}
obj = drm_gem_object_lookup(file_priv, handle);
if (!obj) {
DRM_ERROR("Cannot find cursor object %x for crtc\n", handle);
return -ENOENT;
}
bo = gem_to_vbox_bo(obj);
ret = vbox_bo_reserve(bo, false);
if (ret)
goto out_unref_obj;
/*
* The mask must be calculated based on the alpha
* channel, one bit per ARGB word, and must be 32-bit
* padded.
*/
mask_size = ((width + 7) / 8 * height + 3) & ~3;
data_size = width * height * 4 + mask_size;
vbox->cursor_hot_x = min_t(u32, max(hot_x, 0), width);
vbox->cursor_hot_y = min_t(u32, max(hot_y, 0), height);
vbox->cursor_width = width;
vbox->cursor_height = height;
vbox->cursor_data_size = data_size;
dst = vbox->cursor_data;
ret = ttm_bo_kmap(&bo->bo, 0, bo->bo.num_pages, &uobj_map);
if (ret) {
vbox->cursor_data_size = 0;
goto out_unreserve_bo;
}
src = ttm_kmap_obj_virtual(&uobj_map, &src_isiomem);
if (src_isiomem) {
DRM_ERROR("src cursor bo not in main memory\n");
ret = -EIO;
goto out_unmap_bo;
}
copy_cursor_image(src, dst, width, height, mask_size);
flags = VBOX_MOUSE_POINTER_VISIBLE | VBOX_MOUSE_POINTER_SHAPE |
VBOX_MOUSE_POINTER_ALPHA;
ret = hgsmi_update_pointer_shape(vbox->guest_pool, flags,
vbox->cursor_hot_x, vbox->cursor_hot_y,
width, height, dst, data_size);
out_unmap_bo:
ttm_bo_kunmap(&uobj_map);
out_unreserve_bo:
vbox_bo_unreserve(bo);
out_unref_obj:
drm_gem_object_put_unlocked(obj);
return ret;
}
static int vbox_cursor_move(struct drm_crtc *crtc, int x, int y)
{
struct vbox_private *vbox = crtc->dev->dev_private;
u32 flags = VBOX_MOUSE_POINTER_VISIBLE |
VBOX_MOUSE_POINTER_SHAPE | VBOX_MOUSE_POINTER_ALPHA;
s32 crtc_x =
vbox->single_framebuffer ? crtc->x : to_vbox_crtc(crtc)->x_hint;
s32 crtc_y =
vbox->single_framebuffer ? crtc->y : to_vbox_crtc(crtc)->y_hint;
u32 host_x, host_y;
u32 hot_x = 0;
u32 hot_y = 0;
int ret;
/*
* We compare these to unsigned later and don't
* need to handle negative.
*/
if (x + crtc_x < 0 || y + crtc_y < 0 || vbox->cursor_data_size == 0)
return 0;
ret = hgsmi_cursor_position(vbox->guest_pool, true, x + crtc_x,
y + crtc_y, &host_x, &host_y);
/*
* The only reason we have vbox_cursor_move() is that some older clients
* might use DRM_IOCTL_MODE_CURSOR instead of DRM_IOCTL_MODE_CURSOR2 and
* use DRM_MODE_CURSOR_MOVE to set the hot-spot.
*
* However VirtualBox 5.0.20 and earlier has a bug causing it to return
* 0,0 as host cursor location after a save and restore.
*
* To work around this we ignore a 0, 0 return, since missing the odd
* time when it legitimately happens is not going to hurt much.
*/
if (ret || (host_x == 0 && host_y == 0))
return ret;
if (x + crtc_x < host_x)
hot_x = min(host_x - x - crtc_x, vbox->cursor_width);
if (y + crtc_y < host_y)
hot_y = min(host_y - y - crtc_y, vbox->cursor_height);
if (hot_x == vbox->cursor_hot_x && hot_y == vbox->cursor_hot_y)
return 0;
vbox->cursor_hot_x = hot_x;
vbox->cursor_hot_y = hot_y;
return hgsmi_update_pointer_shape(vbox->guest_pool, flags,
hot_x, hot_y, vbox->cursor_width, vbox->cursor_height,
vbox->cursor_data, vbox->cursor_data_size);
}