drm/vmwgfx: Use modeset display memory validation for layout ioctl

Call the same display memory validation function which is used by
modeset_check. This ensure consistency that kernel change preferred
mode/topology only if supported.

Also change the internal function to use drm_rect instead of
drm_vmw_rect.

Signed-off-by: Deepak Rawat <drawat@vmware.com>
Reviewed-by: Thomas Hellstrom <thellstrom@vmware.com>
Signed-off-by: Thomas Hellstrom <thellstrom@vmware.com>
This commit is contained in:
Deepak Rawat 2018-06-20 11:17:16 +02:00 committed by Thomas Hellstrom
parent 0a80eb4c12
commit 5e24133ea3

View File

@ -1968,13 +1968,15 @@ void vmw_disable_vblank(struct drm_device *dev, unsigned int pipe)
{ {
} }
/**
/* * vmw_du_update_layout - Update the display unit with topology from resolution
* Small shared kms functions. * plugin and generate DRM uevent
* @dev_priv: device private
* @num_rects: number of drm_rect in rects
* @rects: toplogy to update
*/ */
static int vmw_du_update_layout(struct vmw_private *dev_priv,
static int vmw_du_update_layout(struct vmw_private *dev_priv, unsigned num, unsigned int num_rects, struct drm_rect *rects)
struct drm_vmw_rect *rects)
{ {
struct drm_device *dev = dev_priv->dev; struct drm_device *dev = dev_priv->dev;
struct vmw_display_unit *du; struct vmw_display_unit *du;
@ -1982,26 +1984,14 @@ static int vmw_du_update_layout(struct vmw_private *dev_priv, unsigned num,
mutex_lock(&dev->mode_config.mutex); mutex_lock(&dev->mode_config.mutex);
#if 0
{
unsigned int i;
DRM_INFO("%s: new layout ", __func__);
for (i = 0; i < num; i++)
DRM_INFO("(%i, %i %ux%u) ", rects[i].x, rects[i].y,
rects[i].w, rects[i].h);
DRM_INFO("\n");
}
#endif
list_for_each_entry(con, &dev->mode_config.connector_list, head) { list_for_each_entry(con, &dev->mode_config.connector_list, head) {
du = vmw_connector_to_du(con); du = vmw_connector_to_du(con);
if (num > du->unit) { if (num_rects > du->unit) {
du->pref_width = rects[du->unit].w; du->pref_width = drm_rect_width(&rects[du->unit]);
du->pref_height = rects[du->unit].h; du->pref_height = drm_rect_height(&rects[du->unit]);
du->pref_active = true; du->pref_active = true;
du->gui_x = rects[du->unit].x; du->gui_x = rects[du->unit].x1;
du->gui_y = rects[du->unit].y; du->gui_y = rects[du->unit].y1;
drm_object_property_set_value drm_object_property_set_value
(&con->base, dev->mode_config.suggested_x_property, (&con->base, dev->mode_config.suggested_x_property,
du->gui_x); du->gui_x);
@ -2322,7 +2312,25 @@ vmw_du_connector_atomic_get_property(struct drm_connector *connector,
return 0; return 0;
} }
/**
* vmw_kms_update_layout_ioctl - Handler for DRM_VMW_UPDATE_LAYOUT ioctl
* @dev: drm device for the ioctl
* @data: data pointer for the ioctl
* @file_priv: drm file for the ioctl call
*
* Update preferred topology of display unit as per ioctl request. The topology
* is expressed as array of drm_vmw_rect.
* e.g.
* [0 0 640 480] [640 0 800 600] [0 480 640 480]
*
* NOTE:
* The x and y offset (upper left) in drm_vmw_rect cannot be less than 0. Beside
* device limit on topology, x + w and y + h (lower right) cannot be greater
* than INT_MAX. So topology beyond these limits will return with error.
*
* Returns:
* Zero on success, negative errno on failure.
*/
int vmw_kms_update_layout_ioctl(struct drm_device *dev, void *data, int vmw_kms_update_layout_ioctl(struct drm_device *dev, void *data,
struct drm_file *file_priv) struct drm_file *file_priv)
{ {
@ -2331,15 +2339,12 @@ int vmw_kms_update_layout_ioctl(struct drm_device *dev, void *data,
(struct drm_vmw_update_layout_arg *)data; (struct drm_vmw_update_layout_arg *)data;
void __user *user_rects; void __user *user_rects;
struct drm_vmw_rect *rects; struct drm_vmw_rect *rects;
struct drm_rect *drm_rects;
unsigned rects_size; unsigned rects_size;
int ret; int ret, i;
int i;
u64 total_pixels = 0;
struct drm_mode_config *mode_config = &dev->mode_config;
struct drm_vmw_rect bounding_box = {0};
if (!arg->num_outputs) { if (!arg->num_outputs) {
struct drm_vmw_rect def_rect = {0, 0, 800, 600}; struct drm_rect def_rect = {0, 0, 800, 600};
vmw_du_update_layout(dev_priv, 1, &def_rect); vmw_du_update_layout(dev_priv, 1, &def_rect);
return 0; return 0;
} }
@ -2358,52 +2363,29 @@ int vmw_kms_update_layout_ioctl(struct drm_device *dev, void *data,
goto out_free; goto out_free;
} }
for (i = 0; i < arg->num_outputs; ++i) { drm_rects = (struct drm_rect *)rects;
if (rects[i].x < 0 ||
rects[i].y < 0 || for (i = 0; i < arg->num_outputs; i++) {
rects[i].x + rects[i].w > mode_config->max_width || struct drm_vmw_rect curr_rect;
rects[i].y + rects[i].h > mode_config->max_height) {
DRM_ERROR("Invalid GUI layout.\n"); /* Verify user-space for overflow as kernel use drm_rect */
ret = -EINVAL; if ((rects[i].x + rects[i].w > INT_MAX) ||
(rects[i].y + rects[i].h > INT_MAX)) {
ret = -ERANGE;
goto out_free; goto out_free;
} }
/* curr_rect = rects[i];
* bounding_box.w and bunding_box.h are used as drm_rects[i].x1 = curr_rect.x;
* lower-right coordinates drm_rects[i].y1 = curr_rect.y;
*/ drm_rects[i].x2 = curr_rect.x + curr_rect.w;
if (rects[i].x + rects[i].w > bounding_box.w) drm_rects[i].y2 = curr_rect.y + curr_rect.h;
bounding_box.w = rects[i].x + rects[i].w;
if (rects[i].y + rects[i].h > bounding_box.h)
bounding_box.h = rects[i].y + rects[i].h;
total_pixels += (u64) rects[i].w * (u64) rects[i].h;
} }
if (dev_priv->active_display_unit == vmw_du_screen_target) { ret = vmw_kms_check_display_memory(dev, arg->num_outputs, drm_rects);
/*
* For Screen Targets, the limits for a toplogy are:
* 1. Bounding box (assuming 32bpp) must be < prim_bb_mem
* 2. Total pixels (assuming 32bpp) must be < prim_bb_mem
*/
u64 bb_mem = (u64) bounding_box.w * bounding_box.h * 4;
u64 pixel_mem = total_pixels * 4;
if (bb_mem > dev_priv->prim_bb_mem) { if (ret == 0)
DRM_ERROR("Topology is beyond supported limits.\n"); vmw_du_update_layout(dev_priv, arg->num_outputs, drm_rects);
ret = -EINVAL;
goto out_free;
}
if (pixel_mem > dev_priv->prim_bb_mem) {
DRM_ERROR("Combined output size too large\n");
ret = -EINVAL;
goto out_free;
}
}
vmw_du_update_layout(dev_priv, arg->num_outputs, rects);
out_free: out_free:
kfree(rects); kfree(rects);