diff --git a/drivers/gpu/drm/vmwgfx/device_include/svga_reg.h b/drivers/gpu/drm/vmwgfx/device_include/svga_reg.h index 88e72bf9a534a419cfeebba793578532dfcc43bf..cdd48a3763db66089c606b22b0e48061fcb79f5d 100644 --- a/drivers/gpu/drm/vmwgfx/device_include/svga_reg.h +++ b/drivers/gpu/drm/vmwgfx/device_include/svga_reg.h @@ -672,8 +672,34 @@ SVGASignedPoint; * SVGA_CAP_GBOBJECTS -- * Enable guest-backed objects and surfaces. * - * SVGA_CAP_CMD_BUFFERS_3 -- - * Enable support for command buffers in a mob. + * SVGA_CAP_DX -- + * Enable support for DX commands, and command buffers in a mob. + * + * SVGA_CAP_HP_CMD_QUEUE -- + * Enable support for the high priority command queue, and the + * ScreenCopy command. + * + * SVGA_CAP_NO_BB_RESTRICTION -- + * Allow ScreenTargets to be defined without regard to the 32-bpp + * bounding-box memory restrictions. ie: + * + * The summed memory usage of all screens (assuming they were defined as + * 32-bpp) must always be less than the value of the + * SVGA_REG_MAX_PRIMARY_MEM register. + * + * If this cap is not present, the 32-bpp bounding box around all screens + * must additionally be under the value of the SVGA_REG_MAX_PRIMARY_MEM + * register. + * + * If the cap is present, the bounding box restriction is lifted (and only + * the screen-sum limit applies). + * + * (Note that this is a slight lie... there is still a sanity limit on any + * dimension of the topology to be less than SVGA_SCREEN_ROOT_LIMIT, even + * when SVGA_CAP_NO_BB_RESTRICTION is present, but that should be + * large enough to express any possible topology without holes between + * monitors.) + * */ #define SVGA_CAP_NONE 0x00000000 @@ -699,6 +725,7 @@ SVGASignedPoint; #define SVGA_CAP_GBOBJECTS 0x08000000 #define SVGA_CAP_DX 0x10000000 #define SVGA_CAP_HP_CMD_QUEUE 0x20000000 +#define SVGA_CAP_NO_BB_RESTRICTION 0x40000000 #define SVGA_CAP_CMD_RESERVED 0x80000000 diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c index 6b8541f9215d62f9c0c2928fea18d4c413f53d7a..a7c9a017fc3cb3b31670f796daeeb395413e370e 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c @@ -1507,66 +1507,178 @@ static struct drm_framebuffer *vmw_kms_fb_create(struct drm_device *dev, return &vfb->base; } +/** + * vmw_kms_check_display_memory - Validates display memory required for a + * topology + * @dev: DRM device + * @num_rects: number of drm_rect in rects + * @rects: array of drm_rect representing the topology to validate indexed by + * crtc index. + * + * Returns: + * 0 on success otherwise negative error code + */ +static int vmw_kms_check_display_memory(struct drm_device *dev, + uint32_t num_rects, + struct drm_rect *rects) +{ + struct vmw_private *dev_priv = vmw_priv(dev); + struct drm_mode_config *mode_config = &dev->mode_config; + struct drm_rect bounding_box = {0}; + u64 total_pixels = 0, pixel_mem, bb_mem; + int i; + + for (i = 0; i < num_rects; i++) { + /* + * Currently this check is limiting the topology within max + * texture/screentarget size. This should change in future when + * user-space support multiple fb with topology. + */ + if (rects[i].x1 < 0 || rects[i].y1 < 0 || + rects[i].x2 > mode_config->max_width || + rects[i].y2 > mode_config->max_height) { + DRM_ERROR("Invalid GUI layout.\n"); + return -EINVAL; + } + + /* Bounding box upper left is at (0,0). */ + if (rects[i].x2 > bounding_box.x2) + bounding_box.x2 = rects[i].x2; + + if (rects[i].y2 > bounding_box.y2) + bounding_box.y2 = rects[i].y2; + + total_pixels += (u64) drm_rect_width(&rects[i]) * + (u64) drm_rect_height(&rects[i]); + } + + /* Virtual svga device primary limits are always in 32-bpp. */ + pixel_mem = total_pixels * 4; + + /* + * For HV10 and below prim_bb_mem is vram size. When + * SVGA_REG_MAX_PRIMARY_BOUNDING_BOX_MEM is not present vram size is + * limit on primary bounding box + */ + if (pixel_mem > dev_priv->prim_bb_mem) { + DRM_ERROR("Combined output size too large.\n"); + return -EINVAL; + } + /* SVGA_CAP_NO_BB_RESTRICTION is available for STDU only. */ + if (dev_priv->active_display_unit != vmw_du_screen_target || + !(dev_priv->capabilities & SVGA_CAP_NO_BB_RESTRICTION)) { + bb_mem = (u64) bounding_box.x2 * bounding_box.y2 * 4; + + if (bb_mem > dev_priv->prim_bb_mem) { + DRM_ERROR("Topology is beyond supported limits.\n"); + return -EINVAL; + } + } + + return 0; +} /** - * vmw_kms_atomic_check_modeset- validate state object for modeset changes - * + * vmw_kms_check_topology - Validates topology in drm_atomic_state * @dev: DRM device * @state: the driver state object * - * This is a simple wrapper around drm_atomic_helper_check_modeset() for - * us to assign a value to mode->crtc_clock so that - * drm_calc_timestamping_constants() won't throw an error message - * - * RETURNS - * Zero for success or -errno + * Returns: + * 0 on success otherwise negative error code */ -static int -vmw_kms_atomic_check_modeset(struct drm_device *dev, - struct drm_atomic_state *state) +static int vmw_kms_check_topology(struct drm_device *dev, + struct drm_atomic_state *state) { - struct drm_crtc_state *new_crtc_state; - struct drm_plane_state *new_plane_state; - struct drm_plane *plane; + struct drm_crtc_state *old_crtc_state, *new_crtc_state; + struct drm_rect *rects; struct drm_crtc *crtc; - struct vmw_private *dev_priv = vmw_priv(dev); - int i, ret, cpp = 0; + uint32_t i; + int ret = 0; - ret = drm_atomic_helper_check(dev, state); + rects = kcalloc(dev->mode_config.num_crtc, sizeof(struct drm_rect), + GFP_KERNEL); + if (!rects) + return -ENOMEM; - /* If this is not a STDU, then no more checking is necessary */ - if (ret || dev_priv->active_display_unit != vmw_du_screen_target) - return ret; + drm_for_each_crtc(crtc, dev) { + struct vmw_display_unit *du = vmw_crtc_to_du(crtc); + struct drm_crtc_state *crtc_state = crtc->state; - for_each_new_plane_in_state(state, plane, new_plane_state, i) { - if (new_plane_state->fb) { - int current_cpp = new_plane_state->fb->pitches[0] / - new_plane_state->fb->width; + i = drm_crtc_index(crtc); - if (cpp == 0) - cpp = current_cpp; - else if (current_cpp != cpp) - return -EINVAL; + if (crtc_state && crtc_state->enable) { + /* + * There could be a race condition with update of gui_x/ + * gui_y. Those are protected by dev->mode_config.mutex. + */ + rects[i].x1 = du->gui_x; + rects[i].y1 = du->gui_y; + rects[i].x2 = du->gui_x + crtc_state->mode.hdisplay; + rects[i].y2 = du->gui_y + crtc_state->mode.vdisplay; } } - for_each_new_crtc_in_state(state, crtc, new_crtc_state, i) { - unsigned long requested_bb_mem = 0; + /* Determine change to topology due to new atomic state */ + for_each_oldnew_crtc_in_state(state, crtc, old_crtc_state, + new_crtc_state, i) { + struct vmw_display_unit *du = vmw_crtc_to_du(crtc); + + if (!new_crtc_state->enable && old_crtc_state->enable) { + rects[i].x1 = 0; + rects[i].y1 = 0; + rects[i].x2 = 0; + rects[i].y2 = 0; + } - if (drm_atomic_crtc_needs_modeset(new_crtc_state)) { - requested_bb_mem += new_crtc_state->mode.hdisplay * - new_crtc_state->mode.vdisplay * - cpp; + if (new_crtc_state->enable) { + /* If display unit is not active cannot enable CRTC */ + if (!du->pref_active) { + ret = -EINVAL; + goto clean; + } - if (requested_bb_mem > dev_priv->prim_bb_mem) - return -EINVAL; + rects[i].x1 = du->gui_x; + rects[i].y1 = du->gui_y; + rects[i].x2 = du->gui_x + new_crtc_state->mode.hdisplay; + rects[i].y2 = du->gui_y + new_crtc_state->mode.vdisplay; } } + ret = vmw_kms_check_display_memory(dev, dev->mode_config.num_crtc, + rects); + +clean: + kfree(rects); return ret; } +/** + * vmw_kms_atomic_check_modeset- validate state object for modeset changes + * + * @dev: DRM device + * @state: the driver state object + * + * This is a simple wrapper around drm_atomic_helper_check_modeset() for + * us to assign a value to mode->crtc_clock so that + * drm_calc_timestamping_constants() won't throw an error message + * + * Returns: + * Zero for success or -errno + */ +static int +vmw_kms_atomic_check_modeset(struct drm_device *dev, + struct drm_atomic_state *state) +{ + int ret; + + ret = drm_atomic_helper_check(dev, state); + if (ret) + return ret; + + return vmw_kms_check_topology(dev, state); +} + static const struct drm_mode_config_funcs vmw_kms_funcs = { .fb_create = vmw_kms_fb_create, .atomic_check = vmw_kms_atomic_check_modeset,