diff options
Diffstat (limited to 'drivers/gpu/drm/vmwgfx')
-rw-r--r-- | drivers/gpu/drm/vmwgfx/device_include/svga3d_surfacedefs.h | 20 | ||||
-rw-r--r-- | drivers/gpu/drm/vmwgfx/vmwgfx_drv.c | 15 | ||||
-rw-r--r-- | drivers/gpu/drm/vmwgfx/vmwgfx_drv.h | 11 | ||||
-rw-r--r-- | drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c | 32 | ||||
-rw-r--r-- | drivers/gpu/drm/vmwgfx/vmwgfx_fb.c | 6 | ||||
-rw-r--r-- | drivers/gpu/drm/vmwgfx/vmwgfx_fence.c | 87 | ||||
-rw-r--r-- | drivers/gpu/drm/vmwgfx/vmwgfx_fence.h | 2 | ||||
-rw-r--r-- | drivers/gpu/drm/vmwgfx/vmwgfx_kms.c | 162 | ||||
-rw-r--r-- | drivers/gpu/drm/vmwgfx/vmwgfx_kms.h | 16 | ||||
-rw-r--r-- | drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c | 19 | ||||
-rw-r--r-- | drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c | 182 | ||||
-rw-r--r-- | drivers/gpu/drm/vmwgfx/vmwgfx_stdu.c | 456 |
12 files changed, 531 insertions, 477 deletions
diff --git a/drivers/gpu/drm/vmwgfx/device_include/svga3d_surfacedefs.h b/drivers/gpu/drm/vmwgfx/device_include/svga3d_surfacedefs.h index 58704f0a4..531d22025 100644 --- a/drivers/gpu/drm/vmwgfx/device_include/svga3d_surfacedefs.h +++ b/drivers/gpu/drm/vmwgfx/device_include/svga3d_surfacedefs.h @@ -25,6 +25,8 @@ * **************************************************************************/ +#include <linux/kernel.h> + #ifdef __KERNEL__ #include <drm/vmwgfx_drm.h> @@ -36,7 +38,6 @@ #define ARRAY_SIZE(_A) (sizeof(_A) / sizeof((_A)[0])) #endif /* ARRAY_SIZE */ -#define DIV_ROUND_UP(x, y) (((x) + (y) - 1) / (y)) #define max_t(type, x, y) ((x) > (y) ? (x) : (y)) #define surf_size_struct SVGA3dSize #define u32 uint32 @@ -987,12 +988,12 @@ svga3dsurface_get_size_in_blocks(const struct svga3d_surface_desc *desc, const surf_size_struct *pixel_size, surf_size_struct *block_size) { - block_size->width = DIV_ROUND_UP(pixel_size->width, - desc->block_size.width); - block_size->height = DIV_ROUND_UP(pixel_size->height, - desc->block_size.height); - block_size->depth = DIV_ROUND_UP(pixel_size->depth, - desc->block_size.depth); + block_size->width = __KERNEL_DIV_ROUND_UP(pixel_size->width, + desc->block_size.width); + block_size->height = __KERNEL_DIV_ROUND_UP(pixel_size->height, + desc->block_size.height); + block_size->depth = __KERNEL_DIV_ROUND_UP(pixel_size->depth, + desc->block_size.depth); } static inline bool @@ -1100,8 +1101,9 @@ svga3dsurface_get_pixel_offset(SVGA3dSurfaceFormat format, const struct svga3d_surface_desc *desc = svga3dsurface_get_desc(format); const u32 bw = desc->block_size.width, bh = desc->block_size.height; const u32 bd = desc->block_size.depth; - const u32 rowstride = DIV_ROUND_UP(width, bw) * desc->bytes_per_block; - const u32 imgstride = DIV_ROUND_UP(height, bh) * rowstride; + const u32 rowstride = __KERNEL_DIV_ROUND_UP(width, bw) * + desc->bytes_per_block; + const u32 imgstride = __KERNEL_DIV_ROUND_UP(height, bh) * rowstride; const u32 offset = (z / bd * imgstride + y / bh * rowstride + x / bw * desc->bytes_per_block); diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c index 24fb348a4..f2cf92318 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c @@ -195,7 +195,7 @@ static const struct drm_ioctl_desc vmw_ioctls[] = { DRM_MASTER | DRM_AUTH), VMW_IOCTL_DEF(VMW_UPDATE_LAYOUT, vmw_kms_update_layout_ioctl, - DRM_MASTER), + DRM_MASTER | DRM_CONTROL_ALLOW), VMW_IOCTL_DEF(VMW_CREATE_SHADER, vmw_shader_define_ioctl, DRM_AUTH | DRM_RENDER_ALLOW), @@ -628,6 +628,7 @@ static int vmw_driver_load(struct drm_device *dev, unsigned long chipset) mutex_init(&dev_priv->cmdbuf_mutex); mutex_init(&dev_priv->release_mutex); mutex_init(&dev_priv->binding_mutex); + mutex_init(&dev_priv->global_kms_state_mutex); rwlock_init(&dev_priv->resource_lock); ttm_lock_init(&dev_priv->reservation_sem); spin_lock_init(&dev_priv->hw_lock); @@ -972,15 +973,6 @@ static int vmw_driver_unload(struct drm_device *dev) return 0; } -static void vmw_preclose(struct drm_device *dev, - struct drm_file *file_priv) -{ - struct vmw_fpriv *vmw_fp = vmw_fpriv(file_priv); - struct vmw_private *dev_priv = vmw_priv(dev); - - vmw_event_fence_fpriv_gone(dev_priv->fman, &vmw_fp->fence_events); -} - static void vmw_postclose(struct drm_device *dev, struct drm_file *file_priv) { @@ -1011,7 +1003,6 @@ static int vmw_driver_open(struct drm_device *dev, struct drm_file *file_priv) if (unlikely(vmw_fp == NULL)) return ret; - INIT_LIST_HEAD(&vmw_fp->fence_events); vmw_fp->tfile = ttm_object_file_init(dev_priv->tdev, 10); if (unlikely(vmw_fp->tfile == NULL)) goto out_no_tfile; @@ -1214,6 +1205,7 @@ static int vmw_master_set(struct drm_device *dev, } dev_priv->active_master = vmaster; + drm_sysfs_hotplug_event(dev); return 0; } @@ -1501,7 +1493,6 @@ static struct drm_driver driver = { .master_set = vmw_master_set, .master_drop = vmw_master_drop, .open = vmw_driver_open, - .preclose = vmw_preclose, .postclose = vmw_postclose, .set_busid = drm_pci_set_busid, diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h index 469cdd520..6db358a85 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h @@ -40,9 +40,9 @@ #include <drm/ttm/ttm_module.h> #include "vmwgfx_fence.h" -#define VMWGFX_DRIVER_DATE "20150810" +#define VMWGFX_DRIVER_DATE "20160210" #define VMWGFX_DRIVER_MAJOR 2 -#define VMWGFX_DRIVER_MINOR 9 +#define VMWGFX_DRIVER_MINOR 10 #define VMWGFX_DRIVER_PATCHLEVEL 0 #define VMWGFX_FILE_PAGE_OFFSET 0x00100000 #define VMWGFX_FIFO_STATIC_SIZE (1024*1024) @@ -80,7 +80,6 @@ struct vmw_fpriv { struct drm_master *locked_master; struct ttm_object_file *tfile; - struct list_head fence_events; bool gb_aware; }; @@ -408,8 +407,12 @@ struct vmw_private { void *fb_info; enum vmw_display_unit_type active_display_unit; struct vmw_legacy_display *ldu_priv; - struct vmw_screen_object_display *sou_priv; struct vmw_overlay *overlay_priv; + struct drm_property *hotplug_mode_update_property; + struct drm_property *implicit_placement_property; + unsigned num_implicit; + struct vmw_framebuffer *implicit_fb; + struct mutex global_kms_state_mutex; /* * Context and surface management. diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c b/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c index 5da5de0cb..1a1a87cbf 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c @@ -3009,6 +3009,26 @@ out_unref: return ret; } +/** + * vmw_cmd_dx_genmips - Validate an SVGA_3D_CMD_DX_GENMIPS command + * + * @dev_priv: Pointer to a device private struct. + * @sw_context: The software context being used for this batch. + * @header: Pointer to the command header in the command stream. + */ +static int vmw_cmd_dx_genmips(struct vmw_private *dev_priv, + struct vmw_sw_context *sw_context, + SVGA3dCmdHeader *header) +{ + struct { + SVGA3dCmdHeader header; + SVGA3dCmdDXGenMips body; + } *cmd = container_of(header, typeof(*cmd), header); + + return vmw_view_id_val_add(sw_context, vmw_view_sr, + cmd->body.shaderResourceViewId); +} + static int vmw_cmd_check_not_3d(struct vmw_private *dev_priv, struct vmw_sw_context *sw_context, void *buf, uint32_t *size) @@ -3273,19 +3293,19 @@ static const struct vmw_cmd_entry vmw_cmd_entries[SVGA_3D_CMD_MAX] = { &vmw_cmd_dx_cid_check, true, false, true), VMW_CMD_DEF(SVGA_3D_CMD_DX_DEFINE_QUERY, &vmw_cmd_dx_define_query, true, false, true), - VMW_CMD_DEF(SVGA_3D_CMD_DX_DESTROY_QUERY, &vmw_cmd_ok, + VMW_CMD_DEF(SVGA_3D_CMD_DX_DESTROY_QUERY, &vmw_cmd_dx_cid_check, true, false, true), VMW_CMD_DEF(SVGA_3D_CMD_DX_BIND_QUERY, &vmw_cmd_dx_bind_query, true, false, true), VMW_CMD_DEF(SVGA_3D_CMD_DX_SET_QUERY_OFFSET, - &vmw_cmd_ok, true, false, true), - VMW_CMD_DEF(SVGA_3D_CMD_DX_BEGIN_QUERY, &vmw_cmd_ok, + &vmw_cmd_dx_cid_check, true, false, true), + VMW_CMD_DEF(SVGA_3D_CMD_DX_BEGIN_QUERY, &vmw_cmd_dx_cid_check, true, false, true), - VMW_CMD_DEF(SVGA_3D_CMD_DX_END_QUERY, &vmw_cmd_ok, + VMW_CMD_DEF(SVGA_3D_CMD_DX_END_QUERY, &vmw_cmd_dx_cid_check, true, false, true), VMW_CMD_DEF(SVGA_3D_CMD_DX_READBACK_QUERY, &vmw_cmd_invalid, true, false, true), - VMW_CMD_DEF(SVGA_3D_CMD_DX_SET_PREDICATION, &vmw_cmd_invalid, + VMW_CMD_DEF(SVGA_3D_CMD_DX_SET_PREDICATION, &vmw_cmd_dx_cid_check, true, false, true), VMW_CMD_DEF(SVGA_3D_CMD_DX_SET_VIEWPORTS, &vmw_cmd_dx_cid_check, true, false, true), @@ -3297,7 +3317,7 @@ static const struct vmw_cmd_entry vmw_cmd_entries[SVGA_3D_CMD_MAX] = { &vmw_cmd_dx_clear_depthstencil_view, true, false, true), VMW_CMD_DEF(SVGA_3D_CMD_DX_PRED_COPY, &vmw_cmd_invalid, true, false, true), - VMW_CMD_DEF(SVGA_3D_CMD_DX_GENMIPS, &vmw_cmd_invalid, + VMW_CMD_DEF(SVGA_3D_CMD_DX_GENMIPS, &vmw_cmd_dx_genmips, true, false, true), VMW_CMD_DEF(SVGA_3D_CMD_DX_UPDATE_SUBRESOURCE, &vmw_cmd_dx_check_subresource, true, false, true), diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_fb.c b/drivers/gpu/drm/vmwgfx/vmwgfx_fb.c index 3b1faf786..679a4cb98 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_fb.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_fb.c @@ -573,9 +573,9 @@ static int vmw_fb_set_par(struct fb_info *info) mode = old_mode; old_mode = NULL; } else if (!vmw_kms_validate_mode_vram(vmw_priv, - mode->hdisplay * - (var->bits_per_pixel + 7) / 8, - mode->vdisplay)) { + mode->hdisplay * + DIV_ROUND_UP(var->bits_per_pixel, 8), + mode->vdisplay)) { drm_mode_destroy(vmw_priv->dev, mode); return -EINVAL; } diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_fence.c b/drivers/gpu/drm/vmwgfx/vmwgfx_fence.c index 8e689b439..e959df6ed 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_fence.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_fence.c @@ -71,7 +71,6 @@ struct vmw_user_fence { */ struct vmw_event_fence_action { struct vmw_fence_action action; - struct list_head fpriv_head; struct drm_pending_event *event; struct vmw_fence_obj *fence; @@ -808,44 +807,6 @@ int vmw_fence_obj_unref_ioctl(struct drm_device *dev, void *data, } /** - * vmw_event_fence_fpriv_gone - Remove references to struct drm_file objects - * - * @fman: Pointer to a struct vmw_fence_manager - * @event_list: Pointer to linked list of struct vmw_event_fence_action objects - * with pointers to a struct drm_file object about to be closed. - * - * This function removes all pending fence events with references to a - * specific struct drm_file object about to be closed. The caller is required - * to pass a list of all struct vmw_event_fence_action objects with such - * events attached. This function is typically called before the - * struct drm_file object's event management is taken down. - */ -void vmw_event_fence_fpriv_gone(struct vmw_fence_manager *fman, - struct list_head *event_list) -{ - struct vmw_event_fence_action *eaction; - struct drm_pending_event *event; - unsigned long irq_flags; - - while (1) { - spin_lock_irqsave(&fman->lock, irq_flags); - if (list_empty(event_list)) - goto out_unlock; - eaction = list_first_entry(event_list, - struct vmw_event_fence_action, - fpriv_head); - list_del_init(&eaction->fpriv_head); - event = eaction->event; - eaction->event = NULL; - spin_unlock_irqrestore(&fman->lock, irq_flags); - event->destroy(event); - } -out_unlock: - spin_unlock_irqrestore(&fman->lock, irq_flags); -} - - -/** * vmw_event_fence_action_seq_passed * * @action: The struct vmw_fence_action embedded in a struct @@ -879,10 +840,8 @@ static void vmw_event_fence_action_seq_passed(struct vmw_fence_action *action) *eaction->tv_usec = tv.tv_usec; } - list_del_init(&eaction->fpriv_head); - list_add_tail(&eaction->event->link, &file_priv->event_list); + drm_send_event_locked(dev, eaction->event); eaction->event = NULL; - wake_up_all(&file_priv->event_wait); spin_unlock_irqrestore(&dev->event_lock, irq_flags); } @@ -899,12 +858,6 @@ static void vmw_event_fence_action_cleanup(struct vmw_fence_action *action) { struct vmw_event_fence_action *eaction = container_of(action, struct vmw_event_fence_action, action); - struct vmw_fence_manager *fman = fman_from_fence(eaction->fence); - unsigned long irq_flags; - - spin_lock_irqsave(&fman->lock, irq_flags); - list_del(&eaction->fpriv_head); - spin_unlock_irqrestore(&fman->lock, irq_flags); vmw_fence_obj_unreference(&eaction->fence); kfree(eaction); @@ -984,8 +937,6 @@ int vmw_event_fence_action_queue(struct drm_file *file_priv, { struct vmw_event_fence_action *eaction; struct vmw_fence_manager *fman = fman_from_fence(fence); - struct vmw_fpriv *vmw_fp = vmw_fpriv(file_priv); - unsigned long irq_flags; eaction = kzalloc(sizeof(*eaction), GFP_KERNEL); if (unlikely(eaction == NULL)) @@ -1002,10 +953,6 @@ int vmw_event_fence_action_queue(struct drm_file *file_priv, eaction->tv_sec = tv_sec; eaction->tv_usec = tv_usec; - spin_lock_irqsave(&fman->lock, irq_flags); - list_add_tail(&eaction->fpriv_head, &vmw_fp->fence_events); - spin_unlock_irqrestore(&fman->lock, irq_flags); - vmw_fence_obj_add_action(fence, &eaction->action); return 0; @@ -1025,38 +972,26 @@ static int vmw_event_fence_action_create(struct drm_file *file_priv, struct vmw_event_fence_pending *event; struct vmw_fence_manager *fman = fman_from_fence(fence); struct drm_device *dev = fman->dev_priv->dev; - unsigned long irq_flags; int ret; - spin_lock_irqsave(&dev->event_lock, irq_flags); - - ret = (file_priv->event_space < sizeof(event->event)) ? -EBUSY : 0; - if (likely(ret == 0)) - file_priv->event_space -= sizeof(event->event); - - spin_unlock_irqrestore(&dev->event_lock, irq_flags); - - if (unlikely(ret != 0)) { - DRM_ERROR("Failed to allocate event space for this file.\n"); - goto out_no_space; - } - - event = kzalloc(sizeof(*event), GFP_KERNEL); if (unlikely(event == NULL)) { DRM_ERROR("Failed to allocate an event.\n"); ret = -ENOMEM; - goto out_no_event; + goto out_no_space; } event->event.base.type = DRM_VMW_EVENT_FENCE_SIGNALED; event->event.base.length = sizeof(*event); event->event.user_data = user_data; - event->base.event = &event->event.base; - event->base.file_priv = file_priv; - event->base.destroy = (void (*) (struct drm_pending_event *)) kfree; + ret = drm_event_reserve_init(dev, file_priv, &event->base, &event->event.base); + if (unlikely(ret != 0)) { + DRM_ERROR("Failed to allocate event space for this file.\n"); + kfree(event); + goto out_no_space; + } if (flags & DRM_VMW_FE_FLAG_REQ_TIME) ret = vmw_event_fence_action_queue(file_priv, fence, @@ -1076,11 +1011,7 @@ static int vmw_event_fence_action_create(struct drm_file *file_priv, return 0; out_no_queue: - event->base.destroy(&event->base); -out_no_event: - spin_lock_irqsave(&dev->event_lock, irq_flags); - file_priv->event_space += sizeof(*event); - spin_unlock_irqrestore(&dev->event_lock, irq_flags); + drm_event_cancel_free(dev, &event->base); out_no_space: return ret; } diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_fence.h b/drivers/gpu/drm/vmwgfx/vmwgfx_fence.h index 8be6c29f5..83ae301ee 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_fence.h +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_fence.h @@ -116,8 +116,6 @@ extern int vmw_fence_obj_unref_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv); extern int vmw_fence_event_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv); -extern void vmw_event_fence_fpriv_gone(struct vmw_fence_manager *fman, - struct list_head *event_list); extern int vmw_event_fence_action_queue(struct drm_file *filee_priv, struct vmw_fence_obj *fence, struct drm_pending_event *event, diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c index b221a8c40..b07543b5c 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c @@ -236,8 +236,8 @@ int vmw_du_crtc_cursor_move(struct drm_crtc *crtc, int x, int y) struct vmw_display_unit *du = vmw_crtc_to_du(crtc); bool shown = du->cursor_surface || du->cursor_dmabuf ? true : false; - du->cursor_x = x + crtc->x; - du->cursor_y = y + crtc->y; + du->cursor_x = x + du->set_gui_x; + du->cursor_y = y + du->set_gui_y; /* * FIXME: Unclear whether there's any global state touched by the @@ -663,9 +663,8 @@ static int vmw_framebuffer_dmabuf_dirty(struct drm_framebuffer *framebuffer, break; case vmw_du_screen_object: ret = vmw_kms_sou_do_dmabuf_dirty(dev_priv, &vfbd->base, - clips, num_clips, increment, - true, - NULL); + clips, NULL, num_clips, + increment, true, NULL); break; case vmw_du_legacy: ret = vmw_kms_ldu_do_dmabuf_dirty(dev_priv, &vfbd->base, 0, 0, @@ -1109,6 +1108,22 @@ int vmw_kms_present(struct vmw_private *dev_priv, return 0; } +static void +vmw_kms_create_hotplug_mode_update_property(struct vmw_private *dev_priv) +{ + if (dev_priv->hotplug_mode_update_property) + return; + + dev_priv->hotplug_mode_update_property = + drm_property_create_range(dev_priv->dev, + DRM_MODE_PROP_IMMUTABLE, + "hotplug_mode_update", 0, 1); + + if (!dev_priv->hotplug_mode_update_property) + return; + +} + int vmw_kms_init(struct vmw_private *dev_priv) { struct drm_device *dev = dev_priv->dev; @@ -1121,6 +1136,9 @@ 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; + drm_mode_create_suggested_offset_properties(dev); + vmw_kms_create_hotplug_mode_update_property(dev_priv); + ret = vmw_kms_stdu_init_display(dev_priv); if (ret) { ret = vmw_kms_sou_init_display(dev_priv); @@ -1360,15 +1378,28 @@ static int vmw_du_update_layout(struct vmw_private *dev_priv, unsigned num, du->pref_active = true; du->gui_x = rects[du->unit].x; du->gui_y = rects[du->unit].y; + drm_object_property_set_value + (&con->base, dev->mode_config.suggested_x_property, + du->gui_x); + drm_object_property_set_value + (&con->base, dev->mode_config.suggested_y_property, + du->gui_y); } else { du->pref_width = 800; du->pref_height = 600; du->pref_active = false; + drm_object_property_set_value + (&con->base, dev->mode_config.suggested_x_property, + 0); + drm_object_property_set_value + (&con->base, dev->mode_config.suggested_y_property, + 0); } con->status = vmw_du_connector_detect(con, true); } mutex_unlock(&dev->mode_config.mutex); + drm_sysfs_hotplug_event(dev); return 0; } @@ -1591,6 +1622,12 @@ int vmw_du_connector_set_property(struct drm_connector *connector, struct drm_property *property, uint64_t val) { + struct vmw_display_unit *du = vmw_connector_to_du(connector); + struct vmw_private *dev_priv = vmw_priv(connector->dev); + + if (property == dev_priv->implicit_placement_property) + du->is_implicit = val; + return 0; } @@ -2096,3 +2133,118 @@ int vmw_kms_fbdev_init_data(struct vmw_private *dev_priv, return 0; } + +/** + * vmw_kms_del_active - unregister a crtc binding to the implicit framebuffer + * + * @dev_priv: Pointer to a device private struct. + * @du: The display unit of the crtc. + */ +void vmw_kms_del_active(struct vmw_private *dev_priv, + struct vmw_display_unit *du) +{ + mutex_lock(&dev_priv->global_kms_state_mutex); + if (du->active_implicit) { + if (--(dev_priv->num_implicit) == 0) + dev_priv->implicit_fb = NULL; + du->active_implicit = false; + } + mutex_unlock(&dev_priv->global_kms_state_mutex); +} + +/** + * vmw_kms_add_active - register a crtc binding to an implicit framebuffer + * + * @vmw_priv: Pointer to a device private struct. + * @du: The display unit of the crtc. + * @vfb: The implicit framebuffer + * + * Registers a binding to an implicit framebuffer. + */ +void vmw_kms_add_active(struct vmw_private *dev_priv, + struct vmw_display_unit *du, + struct vmw_framebuffer *vfb) +{ + mutex_lock(&dev_priv->global_kms_state_mutex); + WARN_ON_ONCE(!dev_priv->num_implicit && dev_priv->implicit_fb); + + if (!du->active_implicit && du->is_implicit) { + dev_priv->implicit_fb = vfb; + du->active_implicit = true; + dev_priv->num_implicit++; + } + mutex_unlock(&dev_priv->global_kms_state_mutex); +} + +/** + * vmw_kms_screen_object_flippable - Check whether we can page-flip a crtc. + * + * @dev_priv: Pointer to device-private struct. + * @crtc: The crtc we want to flip. + * + * Returns true or false depending whether it's OK to flip this crtc + * based on the criterion that we must not have more than one implicit + * frame-buffer at any one time. + */ +bool vmw_kms_crtc_flippable(struct vmw_private *dev_priv, + struct drm_crtc *crtc) +{ + struct vmw_display_unit *du = vmw_crtc_to_du(crtc); + bool ret; + + mutex_lock(&dev_priv->global_kms_state_mutex); + ret = !du->is_implicit || dev_priv->num_implicit == 1; + mutex_unlock(&dev_priv->global_kms_state_mutex); + + return ret; +} + +/** + * vmw_kms_update_implicit_fb - Update the implicit fb. + * + * @dev_priv: Pointer to device-private struct. + * @crtc: The crtc the new implicit frame-buffer is bound to. + */ +void vmw_kms_update_implicit_fb(struct vmw_private *dev_priv, + struct drm_crtc *crtc) +{ + struct vmw_display_unit *du = vmw_crtc_to_du(crtc); + struct vmw_framebuffer *vfb; + + mutex_lock(&dev_priv->global_kms_state_mutex); + + if (!du->is_implicit) + goto out_unlock; + + vfb = vmw_framebuffer_to_vfb(crtc->primary->fb); + WARN_ON_ONCE(dev_priv->num_implicit != 1 && + dev_priv->implicit_fb != vfb); + + dev_priv->implicit_fb = vfb; +out_unlock: + mutex_unlock(&dev_priv->global_kms_state_mutex); +} + +/** + * vmw_kms_create_implicit_placement_proparty - Set up the implicit placement + * property. + * + * @dev_priv: Pointer to a device private struct. + * @immutable: Whether the property is immutable. + * + * Sets up the implicit placement property unless it's already set up. + */ +void +vmw_kms_create_implicit_placement_property(struct vmw_private *dev_priv, + bool immutable) +{ + if (dev_priv->implicit_placement_property) + return; + + dev_priv->implicit_placement_property = + drm_property_create_range(dev_priv->dev, + immutable ? + DRM_MODE_PROP_IMMUTABLE : 0, + "implicit_placement", 0, 1); + +} diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.h b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.h index edd815035..57203212c 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.h +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.h @@ -178,6 +178,9 @@ struct vmw_display_unit { int gui_x; int gui_y; bool is_implicit; + bool active_implicit; + int set_gui_x; + int set_gui_y; }; #define vmw_crtc_to_du(x) \ @@ -254,6 +257,18 @@ int vmw_kms_fbdev_init_data(struct vmw_private *dev_priv, struct drm_crtc **p_crtc, struct drm_display_mode **p_mode); void vmw_guess_mode_timing(struct drm_display_mode *mode); +void vmw_kms_del_active(struct vmw_private *dev_priv, + struct vmw_display_unit *du); +void vmw_kms_add_active(struct vmw_private *dev_priv, + struct vmw_display_unit *du, + struct vmw_framebuffer *vfb); +bool vmw_kms_crtc_flippable(struct vmw_private *dev_priv, + struct drm_crtc *crtc); +void vmw_kms_update_implicit_fb(struct vmw_private *dev_priv, + struct drm_crtc *crtc); +void vmw_kms_create_implicit_placement_property(struct vmw_private *dev_priv, + bool immutable); + /* * Legacy display unit functions - vmwgfx_ldu.c @@ -287,6 +302,7 @@ int vmw_kms_sou_do_surface_dirty(struct vmw_private *dev_priv, int vmw_kms_sou_do_dmabuf_dirty(struct vmw_private *dev_priv, struct vmw_framebuffer *framebuffer, struct drm_clip_rect *clips, + struct drm_vmw_rect *vclips, unsigned num_clips, int increment, bool interruptible, struct vmw_fence_obj **out_fence); diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c b/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c index b6fa44fe8..63ccd9871 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c @@ -288,6 +288,8 @@ static int vmw_ldu_crtc_set_config(struct drm_mode_set *set) crtc->y = set->y; crtc->mode = *mode; crtc->enabled = true; + ldu->base.set_gui_x = set->x; + ldu->base.set_gui_y = set->y; vmw_ldu_add_active(dev_priv, ldu, vfb); @@ -375,8 +377,19 @@ static int vmw_ldu_init(struct vmw_private *dev_priv, unsigned unit) drm_mode_crtc_set_gamma_size(crtc, 256); drm_object_attach_property(&connector->base, - dev->mode_config.dirty_info_property, - 1); + dev->mode_config.dirty_info_property, + 1); + drm_object_attach_property(&connector->base, + dev_priv->hotplug_mode_update_property, 1); + drm_object_attach_property(&connector->base, + dev->mode_config.suggested_x_property, 0); + drm_object_attach_property(&connector->base, + dev->mode_config.suggested_y_property, 0); + if (dev_priv->implicit_placement_property) + drm_object_attach_property + (&connector->base, + dev_priv->implicit_placement_property, + 1); return 0; } @@ -412,6 +425,8 @@ int vmw_kms_ldu_init_display(struct vmw_private *dev_priv) if (ret != 0) goto err_vblank_cleanup; + vmw_kms_create_implicit_placement_property(dev_priv, true); + if (dev_priv->capabilities & SVGA_CAP_MULTIMON) for (i = 0; i < VMWGFX_NUM_DISPLAY_UNITS; ++i) vmw_ldu_init(dev_priv, i); diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c b/drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c index c5a1a08b0..b74eae2b8 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c @@ -74,19 +74,6 @@ struct vmw_kms_sou_dirty_cmd { SVGA3dCmdBlitSurfaceToScreen body; }; - -/* - * Other structs. - */ - -struct vmw_screen_object_display { - unsigned num_implicit; - - struct vmw_framebuffer *implicit_fb; - SVGAFifoCmdDefineGMRFB cur; - struct vmw_dma_buffer *pinned_gmrfb; -}; - /** * Display unit using screen objects. */ @@ -97,7 +84,6 @@ struct vmw_screen_object_unit { struct vmw_dma_buffer *buffer; /**< Backing store buffer */ bool defined; - bool active_implicit; }; static void vmw_sou_destroy(struct vmw_screen_object_unit *sou) @@ -116,33 +102,6 @@ static void vmw_sou_crtc_destroy(struct drm_crtc *crtc) vmw_sou_destroy(vmw_crtc_to_sou(crtc)); } -static void vmw_sou_del_active(struct vmw_private *vmw_priv, - struct vmw_screen_object_unit *sou) -{ - struct vmw_screen_object_display *ld = vmw_priv->sou_priv; - - if (sou->active_implicit) { - if (--(ld->num_implicit) == 0) - ld->implicit_fb = NULL; - sou->active_implicit = false; - } -} - -static void vmw_sou_add_active(struct vmw_private *vmw_priv, - struct vmw_screen_object_unit *sou, - struct vmw_framebuffer *vfb) -{ - struct vmw_screen_object_display *ld = vmw_priv->sou_priv; - - BUG_ON(!ld->num_implicit && ld->implicit_fb); - - if (!sou->active_implicit && sou->base.is_implicit) { - ld->implicit_fb = vfb; - sou->active_implicit = true; - ld->num_implicit++; - } -} - /** * Send the fifo command to create a screen. */ @@ -185,6 +144,8 @@ static int vmw_sou_fifo_create(struct vmw_private *dev_priv, cmd->obj.root.x = sou->base.gui_x; cmd->obj.root.y = sou->base.gui_y; } + sou->base.set_gui_x = cmd->obj.root.x; + sou->base.set_gui_y = cmd->obj.root.y; /* Ok to assume that buffer is pinned in vram */ vmw_bo_get_guest_ptr(&sou->buffer->base, &cmd->obj.backingStore.ptr); @@ -323,15 +284,18 @@ static int vmw_sou_crtc_set_config(struct drm_mode_set *set) return -EINVAL; } - /* sou only supports one fb active at the time */ + /* Only one active implicit frame-buffer at a time. */ + mutex_lock(&dev_priv->global_kms_state_mutex); if (sou->base.is_implicit && - dev_priv->sou_priv->implicit_fb && vfb && - !(dev_priv->sou_priv->num_implicit == 1 && - sou->active_implicit) && - dev_priv->sou_priv->implicit_fb != vfb) { - DRM_ERROR("Multiple framebuffers not supported\n"); + dev_priv->implicit_fb && vfb && + !(dev_priv->num_implicit == 1 && + sou->base.active_implicit) && + dev_priv->implicit_fb != vfb) { + mutex_unlock(&dev_priv->global_kms_state_mutex); + DRM_ERROR("Multiple implicit framebuffers not supported.\n"); return -EINVAL; } + mutex_unlock(&dev_priv->global_kms_state_mutex); /* since they always map one to one these are safe */ connector = &sou->base.connector; @@ -351,7 +315,7 @@ static int vmw_sou_crtc_set_config(struct drm_mode_set *set) crtc->y = 0; crtc->enabled = false; - vmw_sou_del_active(dev_priv, sou); + vmw_kms_del_active(dev_priv, &sou->base); vmw_sou_backing_free(dev_priv, sou); @@ -415,7 +379,7 @@ static int vmw_sou_crtc_set_config(struct drm_mode_set *set) return ret; } - vmw_sou_add_active(dev_priv, sou, vfb); + vmw_kms_add_active(dev_priv, &sou->base, vfb); connector->encoder = encoder; encoder->crtc = crtc; @@ -428,39 +392,6 @@ static int vmw_sou_crtc_set_config(struct drm_mode_set *set) return 0; } -/** - * Returns if this unit can be page flipped. - * Must be called with the mode_config mutex held. - */ -static bool vmw_sou_screen_object_flippable(struct vmw_private *dev_priv, - struct drm_crtc *crtc) -{ - struct vmw_screen_object_unit *sou = vmw_crtc_to_sou(crtc); - - if (!sou->base.is_implicit) - return true; - - if (dev_priv->sou_priv->num_implicit != 1) - return false; - - return true; -} - -/** - * Update the implicit fb to the current fb of this crtc. - * Must be called with the mode_config mutex held. - */ -static void vmw_sou_update_implicit_fb(struct vmw_private *dev_priv, - struct drm_crtc *crtc) -{ - struct vmw_screen_object_unit *sou = vmw_crtc_to_sou(crtc); - - BUG_ON(!sou->base.is_implicit); - - dev_priv->sou_priv->implicit_fb = - vmw_framebuffer_to_vfb(sou->base.crtc.primary->fb); -} - static int vmw_sou_crtc_page_flip(struct drm_crtc *crtc, struct drm_framebuffer *fb, struct drm_pending_vblank_event *event, @@ -470,30 +401,27 @@ static int vmw_sou_crtc_page_flip(struct drm_crtc *crtc, struct drm_framebuffer *old_fb = crtc->primary->fb; struct vmw_framebuffer *vfb = vmw_framebuffer_to_vfb(fb); struct vmw_fence_obj *fence = NULL; - struct drm_clip_rect clips; + struct drm_vmw_rect vclips; int ret; - /* require ScreenObject support for page flipping */ - if (!dev_priv->sou_priv) - return -ENOSYS; - - if (!vmw_sou_screen_object_flippable(dev_priv, crtc)) + if (!vmw_kms_crtc_flippable(dev_priv, crtc)) return -EINVAL; crtc->primary->fb = fb; /* do a full screen dirty update */ - clips.x1 = clips.y1 = 0; - clips.x2 = fb->width; - clips.y2 = fb->height; + vclips.x = crtc->x; + vclips.y = crtc->y; + vclips.w = crtc->mode.hdisplay; + vclips.h = crtc->mode.vdisplay; if (vfb->dmabuf) ret = vmw_kms_sou_do_dmabuf_dirty(dev_priv, vfb, - &clips, 1, 1, + NULL, &vclips, 1, 1, true, &fence); else ret = vmw_kms_sou_do_surface_dirty(dev_priv, vfb, - &clips, NULL, NULL, + NULL, &vclips, NULL, 0, 0, 1, 1, &fence); @@ -521,7 +449,7 @@ static int vmw_sou_crtc_page_flip(struct drm_crtc *crtc, vmw_fence_obj_unreference(&fence); if (vmw_crtc_to_du(crtc)->is_implicit) - vmw_sou_update_implicit_fb(dev_priv, crtc); + vmw_kms_update_implicit_fb(dev_priv, crtc); return ret; @@ -586,13 +514,12 @@ static int vmw_sou_init(struct vmw_private *dev_priv, unsigned unit) encoder = &sou->base.encoder; connector = &sou->base.connector; - sou->active_implicit = false; - + sou->base.active_implicit = false; sou->base.pref_active = (unit == 0); sou->base.pref_width = dev_priv->initial_width; sou->base.pref_height = dev_priv->initial_height; sou->base.pref_mode = NULL; - sou->base.is_implicit = true; + sou->base.is_implicit = false; drm_connector_init(dev, connector, &vmw_sou_connector_funcs, DRM_MODE_CONNECTOR_VIRTUAL); @@ -611,8 +538,19 @@ static int vmw_sou_init(struct vmw_private *dev_priv, unsigned unit) drm_mode_crtc_set_gamma_size(crtc, 256); drm_object_attach_property(&connector->base, - dev->mode_config.dirty_info_property, - 1); + dev->mode_config.dirty_info_property, + 1); + drm_object_attach_property(&connector->base, + dev_priv->hotplug_mode_update_property, 1); + drm_object_attach_property(&connector->base, + dev->mode_config.suggested_x_property, 0); + drm_object_attach_property(&connector->base, + dev->mode_config.suggested_y_property, 0); + if (dev_priv->implicit_placement_property) + drm_object_attach_property + (&connector->base, + dev_priv->implicit_placement_property, + sou->base.is_implicit); return 0; } @@ -622,11 +560,6 @@ int vmw_kms_sou_init_display(struct vmw_private *dev_priv) struct drm_device *dev = dev_priv->dev; int i, ret; - if (dev_priv->sou_priv) { - DRM_INFO("sou system already on\n"); - return -EINVAL; - } - if (!(dev_priv->capabilities & SVGA_CAP_SCREEN_OBJECT_2)) { DRM_INFO("Not using screen objects," " missing cap SCREEN_OBJECT_2\n"); @@ -634,21 +567,19 @@ int vmw_kms_sou_init_display(struct vmw_private *dev_priv) } ret = -ENOMEM; - dev_priv->sou_priv = kmalloc(sizeof(*dev_priv->sou_priv), GFP_KERNEL); - if (unlikely(!dev_priv->sou_priv)) - goto err_no_mem; - - dev_priv->sou_priv->num_implicit = 0; - dev_priv->sou_priv->implicit_fb = NULL; + dev_priv->num_implicit = 0; + dev_priv->implicit_fb = NULL; ret = drm_vblank_init(dev, VMWGFX_NUM_DISPLAY_UNITS); if (unlikely(ret != 0)) - goto err_free; + return ret; ret = drm_mode_create_dirty_info_property(dev); if (unlikely(ret != 0)) goto err_vblank_cleanup; + vmw_kms_create_implicit_placement_property(dev_priv, false); + for (i = 0; i < VMWGFX_NUM_DISPLAY_UNITS; ++i) vmw_sou_init(dev_priv, i); @@ -660,10 +591,6 @@ int vmw_kms_sou_init_display(struct vmw_private *dev_priv) err_vblank_cleanup: drm_vblank_cleanup(dev); -err_free: - kfree(dev_priv->sou_priv); - dev_priv->sou_priv = NULL; -err_no_mem: return ret; } @@ -671,13 +598,8 @@ int vmw_kms_sou_close_display(struct vmw_private *dev_priv) { struct drm_device *dev = dev_priv->dev; - if (!dev_priv->sou_priv) - return -ENOSYS; - drm_vblank_cleanup(dev); - kfree(dev_priv->sou_priv); - return 0; } @@ -738,6 +660,11 @@ static void vmw_sou_surface_fifo_commit(struct vmw_kms_dirty *dirty) SVGASignedRect *blit = (SVGASignedRect *) &cmd[1]; int i; + if (!dirty->num_hits) { + vmw_fifo_commit(dirty->dev_priv, 0); + return; + } + cmd->header.id = SVGA_3D_CMD_BLIT_SURFACE_TO_SCREEN; cmd->header.size = sizeof(cmd->body) + region_size; @@ -875,6 +802,11 @@ int vmw_kms_sou_do_surface_dirty(struct vmw_private *dev_priv, */ static void vmw_sou_dmabuf_fifo_commit(struct vmw_kms_dirty *dirty) { + if (!dirty->num_hits) { + vmw_fifo_commit(dirty->dev_priv, 0); + return; + } + vmw_fifo_commit(dirty->dev_priv, sizeof(struct vmw_kms_sou_dmabuf_blit) * dirty->num_hits); @@ -909,6 +841,8 @@ static void vmw_sou_dmabuf_clip(struct vmw_kms_dirty *dirty) * @dev_priv: Pointer to the device private structure. * @framebuffer: Pointer to the dma-buffer backed framebuffer. * @clips: Array of clip rects. + * @vclips: Alternate array of clip rects. Either @clips or @vclips must + * be NULL. * @num_clips: Number of clip rects in @clips. * @increment: Increment to use when looping over @clips. * @interruptible: Whether to perform waits interruptible if possible. @@ -922,6 +856,7 @@ static void vmw_sou_dmabuf_clip(struct vmw_kms_dirty *dirty) int vmw_kms_sou_do_dmabuf_dirty(struct vmw_private *dev_priv, struct vmw_framebuffer *framebuffer, struct drm_clip_rect *clips, + struct drm_vmw_rect *vclips, unsigned num_clips, int increment, bool interruptible, struct vmw_fence_obj **out_fence) @@ -945,7 +880,7 @@ int vmw_kms_sou_do_dmabuf_dirty(struct vmw_private *dev_priv, dirty.clip = vmw_sou_dmabuf_clip; dirty.fifo_reserve_size = sizeof(struct vmw_kms_sou_dmabuf_blit) * num_clips; - ret = vmw_kms_helper_dirty(dev_priv, framebuffer, clips, NULL, + ret = vmw_kms_helper_dirty(dev_priv, framebuffer, clips, vclips, 0, 0, num_clips, increment, &dirty); vmw_kms_helper_buffer_finish(dev_priv, NULL, buf, out_fence, NULL); @@ -967,6 +902,11 @@ out_revert: */ static void vmw_sou_readback_fifo_commit(struct vmw_kms_dirty *dirty) { + if (!dirty->num_hits) { + vmw_fifo_commit(dirty->dev_priv, 0); + return; + } + vmw_fifo_commit(dirty->dev_priv, sizeof(struct vmw_kms_sou_readback_blit) * dirty->num_hits); diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_stdu.c b/drivers/gpu/drm/vmwgfx/vmwgfx_stdu.c index 4ef5ffd71..9ca818fb0 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_stdu.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_stdu.c @@ -96,7 +96,6 @@ struct vmw_stdu_surface_copy { * content_vfbs dimensions, then this is a pointer into the * corresponding field in content_vfbs. If not, then this * is a separate buffer to which content_vfbs will blit to. - * @content_fb: holds the rendered content, can be a surface or DMA buffer * @content_type: content_fb type * @defined: true if the current display unit has been initialized */ @@ -104,8 +103,6 @@ struct vmw_screen_target_display_unit { struct vmw_display_unit base; struct vmw_surface *display_srf; - struct drm_framebuffer *content_fb; - enum stdu_content_type content_fb_type; bool defined; @@ -122,22 +119,6 @@ static void vmw_stdu_destroy(struct vmw_screen_target_display_unit *stdu); *****************************************************************************/ /** - * vmw_stdu_pin_display - pins the resource associated with the display surface - * - * @stdu: contains the display surface - * - * Since the display surface can either be a private surface allocated by us, - * or it can point to the content surface, we use this function to not pin the - * same resource twice. - */ -static int vmw_stdu_pin_display(struct vmw_screen_target_display_unit *stdu) -{ - return vmw_resource_pin(&stdu->display_srf->res, false); -} - - - -/** * vmw_stdu_unpin_display - unpins the resource associated with display surface * * @stdu: contains the display surface @@ -153,13 +134,7 @@ static void vmw_stdu_unpin_display(struct vmw_screen_target_display_unit *stdu) struct vmw_resource *res = &stdu->display_srf->res; vmw_resource_unpin(res); - - if (stdu->content_fb_type != SAME_AS_DISPLAY) { - vmw_resource_unreference(&res); - stdu->content_fb_type = SAME_AS_DISPLAY; - } - - stdu->display_srf = NULL; + vmw_surface_unreference(&stdu->display_srf); } } @@ -185,6 +160,9 @@ static void vmw_stdu_crtc_destroy(struct drm_crtc *crtc) * * @dev_priv: VMW DRM device * @stdu: display unit to create a Screen Target for + * @mode: The mode to set. + * @crtc_x: X coordinate of screen target relative to framebuffer origin. + * @crtc_y: Y coordinate of screen target relative to framebuffer origin. * * Creates a STDU that we can used later. This function is called whenever the * framebuffer size changes. @@ -193,7 +171,9 @@ static void vmw_stdu_crtc_destroy(struct drm_crtc *crtc) * 0 on success, error code on failure */ static int vmw_stdu_define_st(struct vmw_private *dev_priv, - struct vmw_screen_target_display_unit *stdu) + struct vmw_screen_target_display_unit *stdu, + struct drm_display_mode *mode, + int crtc_x, int crtc_y) { struct { SVGA3dCmdHeader header; @@ -211,17 +191,19 @@ static int vmw_stdu_define_st(struct vmw_private *dev_priv, cmd->header.size = sizeof(cmd->body); cmd->body.stid = stdu->base.unit; - cmd->body.width = stdu->display_srf->base_size.width; - cmd->body.height = stdu->display_srf->base_size.height; + cmd->body.width = mode->hdisplay; + cmd->body.height = mode->vdisplay; cmd->body.flags = (0 == cmd->body.stid) ? SVGA_STFLAG_PRIMARY : 0; cmd->body.dpi = 0; - cmd->body.xRoot = stdu->base.crtc.x; - cmd->body.yRoot = stdu->base.crtc.y; - - if (!stdu->base.is_implicit) { + if (stdu->base.is_implicit) { + cmd->body.xRoot = crtc_x; + cmd->body.yRoot = crtc_y; + } else { cmd->body.xRoot = stdu->base.gui_x; cmd->body.yRoot = stdu->base.gui_y; } + stdu->base.set_gui_x = cmd->body.xRoot; + stdu->base.set_gui_y = cmd->body.yRoot; vmw_fifo_commit(dev_priv, sizeof(*cmd)); @@ -392,126 +374,43 @@ static int vmw_stdu_destroy_st(struct vmw_private *dev_priv, return ret; } - - /** - * vmw_stdu_crtc_set_config - Sets a mode + * vmw_stdu_bind_fb - Bind an fb to a defined screen target * - * @set: mode parameters - * - * This function is the device-specific portion of the DRM CRTC mode set. - * For the SVGA device, we do this by defining a Screen Target, binding a - * GB Surface to that target, and finally update the screen target. + * @dev_priv: Pointer to a device private struct. + * @crtc: The crtc holding the screen target. + * @mode: The mode currently used by the screen target. Must be non-NULL. + * @new_fb: The new framebuffer to bind. Must be non-NULL. * * RETURNS: - * 0 on success, error code otherwise + * 0 on success, error code on failure. */ -static int vmw_stdu_crtc_set_config(struct drm_mode_set *set) +static int vmw_stdu_bind_fb(struct vmw_private *dev_priv, + struct drm_crtc *crtc, + struct drm_display_mode *mode, + struct drm_framebuffer *new_fb) { - struct vmw_private *dev_priv; - struct vmw_screen_target_display_unit *stdu; - struct vmw_framebuffer *vfb; + struct vmw_screen_target_display_unit *stdu = vmw_crtc_to_stdu(crtc); + struct vmw_framebuffer *vfb = vmw_framebuffer_to_vfb(new_fb); + struct vmw_surface *new_display_srf = NULL; + enum stdu_content_type new_content_type; struct vmw_framebuffer_surface *new_vfbs; - struct drm_display_mode *mode; - struct drm_framebuffer *new_fb; - struct drm_crtc *crtc; - struct drm_encoder *encoder; - struct drm_connector *connector; - int ret; - - - if (!set || !set->crtc) - return -EINVAL; - - crtc = set->crtc; - crtc->x = set->x; - crtc->y = set->y; - stdu = vmw_crtc_to_stdu(crtc); - mode = set->mode; - new_fb = set->fb; - dev_priv = vmw_priv(crtc->dev); - - - if (set->num_connectors > 1) { - DRM_ERROR("Too many connectors\n"); - return -EINVAL; - } - - if (set->num_connectors == 1 && - set->connectors[0] != &stdu->base.connector) { - DRM_ERROR("Connectors don't match %p %p\n", - set->connectors[0], &stdu->base.connector); - return -EINVAL; - } - - - /* Since they always map one to one these are safe */ - connector = &stdu->base.connector; - encoder = &stdu->base.encoder; - - - /* - * After this point the CRTC will be considered off unless a new fb - * is bound - */ - if (stdu->defined) { - /* Unbind current surface by binding an invalid one */ - ret = vmw_stdu_bind_st(dev_priv, stdu, NULL); - if (unlikely(ret != 0)) - return ret; - - /* Update Screen Target, display will now be blank */ - if (crtc->primary->fb) { - vmw_stdu_update_st(dev_priv, stdu); - if (unlikely(ret != 0)) - return ret; - } - - crtc->primary->fb = NULL; - crtc->enabled = false; - encoder->crtc = NULL; - connector->encoder = NULL; - - vmw_stdu_unpin_display(stdu); - stdu->content_fb = NULL; - stdu->content_fb_type = SAME_AS_DISPLAY; - - ret = vmw_stdu_destroy_st(dev_priv, stdu); - /* The hardware is hung, give up */ - if (unlikely(ret != 0)) - return ret; - } - - - /* Any of these conditions means the caller wants CRTC off */ - if (set->num_connectors == 0 || !mode || !new_fb) - return 0; - - - if (set->x + mode->hdisplay > new_fb->width || - set->y + mode->vdisplay > new_fb->height) { - DRM_ERROR("Set outside of framebuffer\n"); - return -EINVAL; - } + int ret; - stdu->content_fb = new_fb; - vfb = vmw_framebuffer_to_vfb(stdu->content_fb); + WARN_ON_ONCE(!stdu->defined); - if (vfb->dmabuf) - stdu->content_fb_type = SEPARATE_DMA; + if (!vfb->dmabuf && new_fb->width == mode->hdisplay && + new_fb->height == mode->vdisplay) + new_content_type = SAME_AS_DISPLAY; + else if (vfb->dmabuf) + new_content_type = SEPARATE_DMA; + else + new_content_type = SEPARATE_SURFACE; - /* - * If the requested mode is different than the width and height - * of the FB or if the content buffer is a DMA buf, then allocate - * a display FB that matches the dimension of the mode - */ - if (mode->hdisplay != new_fb->width || - mode->vdisplay != new_fb->height || - stdu->content_fb_type != SAME_AS_DISPLAY) { + if (new_content_type != SAME_AS_DISPLAY && + !stdu->display_srf) { struct vmw_surface content_srf; struct drm_vmw_size display_base_size = {0}; - struct vmw_surface *display_srf; - display_base_size.width = mode->hdisplay; display_base_size.height = mode->vdisplay; @@ -521,7 +420,7 @@ static int vmw_stdu_crtc_set_config(struct drm_mode_set *set) * If content buffer is a DMA buf, then we have to construct * surface info */ - if (stdu->content_fb_type == SEPARATE_DMA) { + if (new_content_type == SEPARATE_DMA) { switch (new_fb->bits_per_pixel) { case 32: @@ -538,17 +437,13 @@ static int vmw_stdu_crtc_set_config(struct drm_mode_set *set) default: DRM_ERROR("Invalid format\n"); - ret = -EINVAL; - goto err_unref_content; + return -EINVAL; } content_srf.flags = 0; content_srf.mip_levels[0] = 1; content_srf.multisample_count = 0; } else { - - stdu->content_fb_type = SEPARATE_SURFACE; - new_vfbs = vmw_framebuffer_to_vfbs(new_fb); content_srf = *new_vfbs->surface; } @@ -563,26 +458,139 @@ static int vmw_stdu_crtc_set_config(struct drm_mode_set *set) content_srf.multisample_count, 0, display_base_size, - &display_srf); + &new_display_srf); if (unlikely(ret != 0)) { - DRM_ERROR("Cannot allocate a display FB.\n"); - goto err_unref_content; + DRM_ERROR("Could not allocate screen target surface.\n"); + return ret; } - - stdu->display_srf = display_srf; - } else { + } else if (new_content_type == SAME_AS_DISPLAY) { new_vfbs = vmw_framebuffer_to_vfbs(new_fb); - stdu->display_srf = new_vfbs->surface; + new_display_srf = vmw_surface_reference(new_vfbs->surface); } + if (new_display_srf) { + /* Pin new surface before flipping */ + ret = vmw_resource_pin(&new_display_srf->res, false); + if (ret) + goto out_srf_unref; + + ret = vmw_stdu_bind_st(dev_priv, stdu, &new_display_srf->res); + if (ret) + goto out_srf_unpin; + + /* Unpin and unreference old surface */ + vmw_stdu_unpin_display(stdu); - ret = vmw_stdu_pin_display(stdu); - if (unlikely(ret != 0)) { - stdu->display_srf = NULL; - goto err_unref_content; + /* Transfer the reference */ + stdu->display_srf = new_display_srf; + new_display_srf = NULL; } - vmw_svga_enable(dev_priv); + crtc->primary->fb = new_fb; + stdu->content_fb_type = new_content_type; + return 0; + +out_srf_unpin: + vmw_resource_unpin(&new_display_srf->res); +out_srf_unref: + vmw_surface_unreference(&new_display_srf); + return ret; +} + +/** + * vmw_stdu_crtc_set_config - Sets a mode + * + * @set: mode parameters + * + * This function is the device-specific portion of the DRM CRTC mode set. + * For the SVGA device, we do this by defining a Screen Target, binding a + * GB Surface to that target, and finally update the screen target. + * + * RETURNS: + * 0 on success, error code otherwise + */ +static int vmw_stdu_crtc_set_config(struct drm_mode_set *set) +{ + struct vmw_private *dev_priv; + struct vmw_framebuffer *vfb; + struct vmw_screen_target_display_unit *stdu; + struct drm_display_mode *mode; + struct drm_framebuffer *new_fb; + struct drm_crtc *crtc; + struct drm_encoder *encoder; + struct drm_connector *connector; + bool turning_off; + int ret; + + + if (!set || !set->crtc) + return -EINVAL; + + crtc = set->crtc; + stdu = vmw_crtc_to_stdu(crtc); + mode = set->mode; + new_fb = set->fb; + dev_priv = vmw_priv(crtc->dev); + turning_off = set->num_connectors == 0 || !mode || !new_fb; + vfb = (new_fb) ? vmw_framebuffer_to_vfb(new_fb) : NULL; + + if (set->num_connectors > 1) { + DRM_ERROR("Too many connectors\n"); + return -EINVAL; + } + + if (set->num_connectors == 1 && + set->connectors[0] != &stdu->base.connector) { + DRM_ERROR("Connectors don't match %p %p\n", + set->connectors[0], &stdu->base.connector); + return -EINVAL; + } + + if (!turning_off && (set->x + mode->hdisplay > new_fb->width || + set->y + mode->vdisplay > new_fb->height)) { + DRM_ERROR("Set outside of framebuffer\n"); + return -EINVAL; + } + + /* Only one active implicit frame-buffer at a time. */ + mutex_lock(&dev_priv->global_kms_state_mutex); + if (!turning_off && stdu->base.is_implicit && dev_priv->implicit_fb && + !(dev_priv->num_implicit == 1 && stdu->base.active_implicit) + && dev_priv->implicit_fb != vfb) { + mutex_unlock(&dev_priv->global_kms_state_mutex); + DRM_ERROR("Multiple implicit framebuffers not supported.\n"); + return -EINVAL; + } + mutex_unlock(&dev_priv->global_kms_state_mutex); + + /* Since they always map one to one these are safe */ + connector = &stdu->base.connector; + encoder = &stdu->base.encoder; + + if (stdu->defined) { + ret = vmw_stdu_bind_st(dev_priv, stdu, NULL); + if (ret) + return ret; + + vmw_stdu_unpin_display(stdu); + (void) vmw_stdu_update_st(dev_priv, stdu); + vmw_kms_del_active(dev_priv, &stdu->base); + + ret = vmw_stdu_destroy_st(dev_priv, stdu); + if (ret) + return ret; + + crtc->primary->fb = NULL; + crtc->enabled = false; + encoder->crtc = NULL; + connector->encoder = NULL; + stdu->content_fb_type = SAME_AS_DISPLAY; + crtc->x = set->x; + crtc->y = set->y; + } + + if (turning_off) + return 0; /* * Steps to displaying a surface, assume surface is already @@ -592,35 +600,33 @@ static int vmw_stdu_crtc_set_config(struct drm_mode_set *set) * 3. update that screen target (this is done later by * vmw_kms_stdu_do_surface_dirty_or_present) */ - ret = vmw_stdu_define_st(dev_priv, stdu); - if (unlikely(ret != 0)) - goto err_unpin_display_and_content; + /* + * Note on error handling: We can't really restore the crtc to + * it's original state on error, but we at least update the + * current state to what's submitted to hardware to enable + * future recovery. + */ + vmw_svga_enable(dev_priv); + ret = vmw_stdu_define_st(dev_priv, stdu, mode, set->x, set->y); + if (ret) + return ret; - ret = vmw_stdu_bind_st(dev_priv, stdu, &stdu->display_srf->res); - if (unlikely(ret != 0)) - goto err_unpin_destroy_st; + crtc->x = set->x; + crtc->y = set->y; + crtc->mode = *mode; + ret = vmw_stdu_bind_fb(dev_priv, crtc, mode, new_fb); + if (ret) + return ret; + vmw_kms_add_active(dev_priv, &stdu->base, vfb); + crtc->enabled = true; connector->encoder = encoder; encoder->crtc = crtc; - crtc->mode = *mode; - crtc->primary->fb = new_fb; - crtc->enabled = true; - - return ret; - -err_unpin_destroy_st: - vmw_stdu_destroy_st(dev_priv, stdu); -err_unpin_display_and_content: - vmw_stdu_unpin_display(stdu); -err_unref_content: - stdu->content_fb = NULL; - return ret; + return 0; } - - /** * vmw_stdu_crtc_page_flip - Binds a buffer to a screen target * @@ -648,59 +654,34 @@ static int vmw_stdu_crtc_page_flip(struct drm_crtc *crtc, { struct vmw_private *dev_priv = vmw_priv(crtc->dev); struct vmw_screen_target_display_unit *stdu; + struct drm_vmw_rect vclips; + struct vmw_framebuffer *vfb = vmw_framebuffer_to_vfb(new_fb); int ret; - if (crtc == NULL) - return -EINVAL; - dev_priv = vmw_priv(crtc->dev); stdu = vmw_crtc_to_stdu(crtc); - crtc->primary->fb = new_fb; - stdu->content_fb = new_fb; - - if (stdu->display_srf) { - /* - * If the display surface is the same as the content surface - * then remove the reference - */ - if (stdu->content_fb_type == SAME_AS_DISPLAY) { - if (stdu->defined) { - /* Unbind the current surface */ - ret = vmw_stdu_bind_st(dev_priv, stdu, NULL); - if (unlikely(ret != 0)) - goto err_out; - } - vmw_stdu_unpin_display(stdu); - stdu->display_srf = NULL; - } - } - - - if (!new_fb) { - /* Blanks the display */ - (void) vmw_stdu_update_st(dev_priv, stdu); - - return 0; - } + if (!stdu->defined || !vmw_kms_crtc_flippable(dev_priv, crtc)) + return -EINVAL; - if (stdu->content_fb_type == SAME_AS_DISPLAY) { - stdu->display_srf = vmw_framebuffer_to_vfbs(new_fb)->surface; - ret = vmw_stdu_pin_display(stdu); - if (ret) { - stdu->display_srf = NULL; - goto err_out; - } + ret = vmw_stdu_bind_fb(dev_priv, crtc, &crtc->mode, new_fb); + if (ret) + return ret; - /* Bind display surface */ - ret = vmw_stdu_bind_st(dev_priv, stdu, &stdu->display_srf->res); - if (unlikely(ret != 0)) - goto err_unpin_display_and_content; - } + if (stdu->base.is_implicit) + vmw_kms_update_implicit_fb(dev_priv, crtc); - /* Update display surface: after this point everything is bound */ - ret = vmw_stdu_update_st(dev_priv, stdu); - if (unlikely(ret != 0)) + vclips.x = crtc->x; + vclips.y = crtc->y; + vclips.w = crtc->mode.hdisplay; + vclips.h = crtc->mode.vdisplay; + if (vfb->dmabuf) + ret = vmw_kms_stdu_dma(dev_priv, NULL, vfb, NULL, NULL, &vclips, + 1, 1, true, false); + else + ret = vmw_kms_stdu_surface_dirty(dev_priv, vfb, NULL, &vclips, + NULL, 0, 0, 1, 1, NULL); + if (ret) return ret; if (event) { @@ -721,14 +702,7 @@ static int vmw_stdu_crtc_page_flip(struct drm_crtc *crtc, vmw_fifo_flush(dev_priv, false); } - return ret; - -err_unpin_display_and_content: - vmw_stdu_unpin_display(stdu); -err_out: - crtc->primary->fb = NULL; - stdu->content_fb = NULL; - return ret; + return 0; } @@ -1138,7 +1112,7 @@ static int vmw_stdu_init(struct vmw_private *dev_priv, unsigned unit) stdu->base.pref_active = (unit == 0); stdu->base.pref_width = dev_priv->initial_width; stdu->base.pref_height = dev_priv->initial_height; - stdu->base.is_implicit = true; + stdu->base.is_implicit = false; drm_connector_init(dev, connector, &vmw_stdu_connector_funcs, DRM_MODE_CONNECTOR_VIRTUAL); @@ -1159,7 +1133,17 @@ static int vmw_stdu_init(struct vmw_private *dev_priv, unsigned unit) drm_object_attach_property(&connector->base, dev->mode_config.dirty_info_property, 1); - + drm_object_attach_property(&connector->base, + dev_priv->hotplug_mode_update_property, 1); + drm_object_attach_property(&connector->base, + dev->mode_config.suggested_x_property, 0); + drm_object_attach_property(&connector->base, + dev->mode_config.suggested_y_property, 0); + if (dev_priv->implicit_placement_property) + drm_object_attach_property + (&connector->base, + dev_priv->implicit_placement_property, + stdu->base.is_implicit); return 0; } @@ -1224,6 +1208,8 @@ int vmw_kms_stdu_init_display(struct vmw_private *dev_priv) dev_priv->active_display_unit = vmw_du_screen_target; + vmw_kms_create_implicit_placement_property(dev_priv, false); + for (i = 0; i < VMWGFX_NUM_DISPLAY_UNITS; ++i) { ret = vmw_stdu_init(dev_priv, i); |