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:
parent
e2f958d00b
commit
35f3288c45
@ -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];
|
||||
};
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user