Commit 7b006203 authored by Zack Rusin's avatar Zack Rusin

drm/vmwgfx: Implement virtual crc generation

crc checksums are used to validate the output. Normally they're part
of the actual display hardware but on virtual stack there's nothing
to automatically generate them.

Implement crc generation for the vmwgfx stack. This works only on
screen targets, where it's possibly to easily make sure that the
guest side contents of the surface matches the host sides output.

Just like the vblank support, crc generation can only be enabled via:
guestinfo.vmwgfx.vkms_enable = "TRUE"
option in the vmx file.

Makes IGT's kms_pipe_crc_basic pass and allows a huge number of other
IGT tests which require CRC generation of the output to actually run
on vmwgfx. Makes it possible to actually validate a lof of the kms and
drm functionality with vmwgfx.
Signed-off-by: default avatarZack Rusin <zack.rusin@broadcom.com>
Acked-by: default avatarMartin Krastev <martin.krastev@broadcom.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20240412025511.78553-3-zack.rusin@broadcom.com
parent cd2eb57d
......@@ -1198,6 +1198,7 @@ static void vmw_driver_unload(struct drm_device *dev)
vmw_svga_disable(dev_priv);
vmw_vkms_cleanup(dev_priv);
vmw_kms_close(dev_priv);
vmw_overlay_close(dev_priv);
......
......@@ -616,6 +616,7 @@ struct vmw_private {
uint32 *devcaps;
bool vkms_enabled;
struct workqueue_struct *crc_workq;
/*
* mksGuestStat instance-descriptor and pid arrays
......@@ -811,6 +812,7 @@ void vmw_resource_mob_attach(struct vmw_resource *res);
void vmw_resource_mob_detach(struct vmw_resource *res);
void vmw_resource_dirty_update(struct vmw_resource *res, pgoff_t start,
pgoff_t end);
int vmw_resource_clean(struct vmw_resource *res);
int vmw_resources_clean(struct vmw_bo *vbo, pgoff_t start,
pgoff_t end, pgoff_t *num_prefault);
......
......@@ -40,14 +40,14 @@
void vmw_du_init(struct vmw_display_unit *du)
{
hrtimer_init(&du->vkms.timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
du->vkms.timer.function = &vmw_vkms_vblank_simulate;
vmw_vkms_crtc_init(&du->crtc);
}
void vmw_du_cleanup(struct vmw_display_unit *du)
{
struct vmw_private *dev_priv = vmw_priv(du->primary.dev);
hrtimer_cancel(&du->vkms.timer);
vmw_vkms_crtc_cleanup(&du->crtc);
drm_plane_cleanup(&du->primary);
if (vmw_cmd_supported(dev_priv))
drm_plane_cleanup(&du->cursor.base);
......@@ -963,6 +963,7 @@ int vmw_du_crtc_atomic_check(struct drm_crtc *crtc,
void vmw_du_crtc_atomic_begin(struct drm_crtc *crtc,
struct drm_atomic_state *state)
{
vmw_vkms_crtc_atomic_begin(crtc, state);
}
/**
......@@ -2029,6 +2030,29 @@ vmw_kms_create_hotplug_mode_update_property(struct vmw_private *dev_priv)
"hotplug_mode_update", 0, 1);
}
static void
vmw_atomic_commit_tail(struct drm_atomic_state *old_state)
{
struct vmw_private *vmw = vmw_priv(old_state->dev);
struct drm_crtc *crtc;
struct drm_crtc_state *old_crtc_state;
int i;
drm_atomic_helper_commit_tail(old_state);
if (vmw->vkms_enabled) {
for_each_old_crtc_in_state(old_state, crtc, old_crtc_state, i) {
struct vmw_display_unit *du = vmw_crtc_to_du(crtc);
(void)old_crtc_state;
flush_work(&du->vkms.crc_generator_work);
}
}
}
static const struct drm_mode_config_helper_funcs vmw_mode_config_helpers = {
.atomic_commit_tail = vmw_atomic_commit_tail,
};
int vmw_kms_init(struct vmw_private *dev_priv)
{
struct drm_device *dev = &dev_priv->drm;
......@@ -2048,6 +2072,7 @@ int vmw_kms_init(struct vmw_private *dev_priv)
dev->mode_config.max_width = dev_priv->texture_max_width;
dev->mode_config.max_height = dev_priv->texture_max_height;
dev->mode_config.preferred_depth = dev_priv->assume_16bpp ? 16 : 32;
dev->mode_config.helper_private = &vmw_mode_config_helpers;
drm_mode_create_suggested_offset_properties(dev);
vmw_kms_create_hotplug_mode_update_property(dev_priv);
......
......@@ -378,9 +378,22 @@ struct vmw_display_unit {
int set_gui_y;
struct {
struct work_struct crc_generator_work;
struct hrtimer timer;
ktime_t period_ns;
struct drm_pending_vblank_event *event;
/* protects concurrent access to the vblank handler */
atomic_t atomic_lock;
/* protected by @atomic_lock */
bool crc_enabled;
struct vmw_surface *surface;
/* protects concurrent access to the crc worker */
spinlock_t crc_state_lock;
/* protected by @crc_state_lock */
bool crc_pending;
u64 frame_start;
u64 frame_end;
} vkms;
};
......
......@@ -1064,6 +1064,22 @@ void vmw_resource_dirty_update(struct vmw_resource *res, pgoff_t start,
end << PAGE_SHIFT);
}
int vmw_resource_clean(struct vmw_resource *res)
{
int ret = 0;
if (res->res_dirty) {
if (!res->func->clean)
return -EINVAL;
ret = res->func->clean(res);
if (ret)
return ret;
res->res_dirty = false;
}
return ret;
}
/**
* vmw_resources_clean - Clean resources intersecting a mob range
* @vbo: The mob buffer object
......@@ -1080,6 +1096,7 @@ int vmw_resources_clean(struct vmw_bo *vbo, pgoff_t start,
unsigned long res_start = start << PAGE_SHIFT;
unsigned long res_end = end << PAGE_SHIFT;
unsigned long last_cleaned = 0;
int ret;
/*
* Find the resource with lowest backup_offset that intersects the
......@@ -1106,18 +1123,9 @@ int vmw_resources_clean(struct vmw_bo *vbo, pgoff_t start,
* intersecting the range.
*/
while (found) {
if (found->res_dirty) {
int ret;
if (!found->func->clean)
return -EINVAL;
ret = found->func->clean(found);
if (ret)
return ret;
found->res_dirty = false;
}
ret = vmw_resource_clean(found);
if (ret)
return ret;
last_cleaned = found->guest_memory_offset + found->guest_memory_size;
cur = rb_next(&found->mob_node);
if (!cur)
......
......@@ -409,11 +409,6 @@ static void vmw_stdu_crtc_mode_set_nofb(struct drm_crtc *crtc)
crtc->x, crtc->y);
}
static void vmw_stdu_crtc_helper_prepare(struct drm_crtc *crtc)
{
}
static void vmw_stdu_crtc_atomic_disable(struct drm_crtc *crtc,
struct drm_atomic_state *state)
{
......@@ -783,6 +778,9 @@ static const struct drm_crtc_funcs vmw_stdu_crtc_funcs = {
.enable_vblank = vmw_vkms_enable_vblank,
.disable_vblank = vmw_vkms_disable_vblank,
.get_vblank_timestamp = vmw_vkms_get_vblank_timestamp,
.get_crc_sources = vmw_vkms_get_crc_sources,
.set_crc_source = vmw_vkms_set_crc_source,
.verify_crc_source = vmw_vkms_verify_crc_source,
};
......@@ -1414,6 +1412,17 @@ vmw_stdu_primary_plane_atomic_update(struct drm_plane *plane,
vmw_fence_obj_unreference(&fence);
}
static void
vmw_stdu_crtc_atomic_flush(struct drm_crtc *crtc,
struct drm_atomic_state *state)
{
struct vmw_private *vmw = vmw_priv(crtc->dev);
struct vmw_screen_target_display_unit *stdu = vmw_crtc_to_stdu(crtc);
if (vmw->vkms_enabled)
vmw_vkms_set_crc_surface(crtc, stdu->display_srf);
vmw_vkms_crtc_atomic_flush(crtc, state);
}
static const struct drm_plane_funcs vmw_stdu_plane_funcs = {
.update_plane = drm_atomic_helper_update_plane,
......@@ -1454,11 +1463,10 @@ drm_plane_helper_funcs vmw_stdu_primary_plane_helper_funcs = {
};
static const struct drm_crtc_helper_funcs vmw_stdu_crtc_helper_funcs = {
.prepare = vmw_stdu_crtc_helper_prepare,
.mode_set_nofb = vmw_stdu_crtc_mode_set_nofb,
.atomic_check = vmw_du_crtc_atomic_check,
.atomic_begin = vmw_du_crtc_atomic_begin,
.atomic_flush = vmw_vkms_crtc_atomic_flush,
.atomic_flush = vmw_stdu_crtc_atomic_flush,
.atomic_enable = vmw_vkms_crtc_atomic_enable,
.atomic_disable = vmw_stdu_crtc_atomic_disable,
};
......
This diff is collapsed.
......@@ -32,22 +32,44 @@
#include <linux/hrtimer_types.h>
#include <linux/types.h>
struct vmw_private;
struct drm_crtc;
struct drm_atomic_state;
struct drm_crtc;
struct vmw_private;
struct vmw_surface;
void vmw_vkms_init(struct vmw_private *vmw);
void vmw_vkms_cleanup(struct vmw_private *vmw);
void vmw_vkms_modeset_lock(struct drm_crtc *crtc);
bool vmw_vkms_modeset_lock_relaxed(struct drm_crtc *crtc);
bool vmw_vkms_vblank_trylock(struct drm_crtc *crtc);
void vmw_vkms_unlock(struct drm_crtc *crtc);
bool vmw_vkms_get_vblank_timestamp(struct drm_crtc *crtc,
int *max_error,
ktime_t *vblank_time,
bool in_vblank_irq);
int vmw_vkms_enable_vblank(struct drm_crtc *crtc);
void vmw_vkms_disable_vblank(struct drm_crtc *crtc);
void vmw_vkms_crtc_init(struct drm_crtc *crtc);
void vmw_vkms_crtc_cleanup(struct drm_crtc *crtc);
void vmw_vkms_crtc_atomic_begin(struct drm_crtc *crtc,
struct drm_atomic_state *state);
void vmw_vkms_crtc_atomic_flush(struct drm_crtc *crtc, struct drm_atomic_state *state);
enum hrtimer_restart vmw_vkms_vblank_simulate(struct hrtimer *timer);
void vmw_vkms_crtc_atomic_enable(struct drm_crtc *crtc,
struct drm_atomic_state *state);
void vmw_vkms_crtc_atomic_disable(struct drm_crtc *crtc,
struct drm_atomic_state *state);
const char *const *vmw_vkms_get_crc_sources(struct drm_crtc *crtc,
size_t *count);
int vmw_vkms_verify_crc_source(struct drm_crtc *crtc,
const char *src_name,
size_t *values_cnt);
int vmw_vkms_set_crc_source(struct drm_crtc *crtc,
const char *src_name);
void vmw_vkms_set_crc_surface(struct drm_crtc *crtc,
struct vmw_surface *surf);
#endif
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment