From d3216a0c3133d8e88ec45c7ecd9f38b421f90c03 Mon Sep 17 00:00:00 2001 From: Thomas Hellstrom Date: Tue, 5 Oct 2010 12:42:59 +0200 Subject: drm/vmwgfx: Really support other depths than 32 Also add some sanity checks. Signed-off-by: Thomas Hellstrom Signed-off-by: Dave Airlie --- drivers/gpu/drm/vmwgfx/vmwgfx_kms.c | 106 +++++++++++++++++++++++++++++------- 1 file changed, 85 insertions(+), 21 deletions(-) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c index e882ba099f0..f30223cafad 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c @@ -471,16 +471,55 @@ static struct drm_framebuffer_funcs vmw_framebuffer_surface_funcs = { .create_handle = vmw_framebuffer_create_handle, }; -int vmw_kms_new_framebuffer_surface(struct vmw_private *dev_priv, - struct vmw_surface *surface, - struct vmw_framebuffer **out, - unsigned width, unsigned height) +static int vmw_kms_new_framebuffer_surface(struct vmw_private *dev_priv, + struct vmw_surface *surface, + struct vmw_framebuffer **out, + const struct drm_mode_fb_cmd + *mode_cmd) { struct drm_device *dev = dev_priv->dev; struct vmw_framebuffer_surface *vfbs; + enum SVGA3dSurfaceFormat format; int ret; + /* + * Sanity checks. + */ + + if (unlikely(surface->mip_levels[0] != 1 || + surface->num_sizes != 1 || + surface->sizes[0].width < mode_cmd->width || + surface->sizes[0].height < mode_cmd->height || + surface->sizes[0].depth != 1)) { + DRM_ERROR("Incompatible surface dimensions " + "for requested mode.\n"); + return -EINVAL; + } + + switch (mode_cmd->depth) { + case 32: + format = SVGA3D_A8R8G8B8; + break; + case 24: + format = SVGA3D_X8R8G8B8; + break; + case 16: + format = SVGA3D_R5G6B5; + break; + case 15: + format = SVGA3D_A1R5G5B5; + break; + default: + DRM_ERROR("Invalid color depth: %d\n", mode_cmd->depth); + return -EINVAL; + } + + if (unlikely(format != surface->format)) { + DRM_ERROR("Invalid surface format for requested mode.\n"); + return -EINVAL; + } + vfbs = kzalloc(sizeof(*vfbs), GFP_KERNEL); if (!vfbs) { ret = -ENOMEM; @@ -498,11 +537,11 @@ int vmw_kms_new_framebuffer_surface(struct vmw_private *dev_priv, } /* XXX get the first 3 from the surface info */ - vfbs->base.base.bits_per_pixel = 32; - vfbs->base.base.pitch = width * 32 / 4; - vfbs->base.base.depth = 24; - vfbs->base.base.width = width; - vfbs->base.base.height = height; + vfbs->base.base.bits_per_pixel = mode_cmd->bpp; + vfbs->base.base.pitch = mode_cmd->pitch; + vfbs->base.base.depth = mode_cmd->depth; + vfbs->base.base.width = mode_cmd->width; + vfbs->base.base.height = mode_cmd->height; vfbs->base.pin = &vmw_surface_dmabuf_pin; vfbs->base.unpin = &vmw_surface_dmabuf_unpin; vfbs->surface = surface; @@ -659,16 +698,25 @@ static int vmw_framebuffer_dmabuf_unpin(struct vmw_framebuffer *vfb) return vmw_dmabuf_from_vram(dev_priv, vfbd->buffer); } -int vmw_kms_new_framebuffer_dmabuf(struct vmw_private *dev_priv, - struct vmw_dma_buffer *dmabuf, - struct vmw_framebuffer **out, - unsigned width, unsigned height) +static int vmw_kms_new_framebuffer_dmabuf(struct vmw_private *dev_priv, + struct vmw_dma_buffer *dmabuf, + struct vmw_framebuffer **out, + const struct drm_mode_fb_cmd + *mode_cmd) { struct drm_device *dev = dev_priv->dev; struct vmw_framebuffer_dmabuf *vfbd; + unsigned int requested_size; int ret; + requested_size = mode_cmd->height * mode_cmd->pitch; + if (unlikely(requested_size > dmabuf->base.num_pages * PAGE_SIZE)) { + DRM_ERROR("Screen buffer object size is too small " + "for requested mode.\n"); + return -EINVAL; + } + vfbd = kzalloc(sizeof(*vfbd), GFP_KERNEL); if (!vfbd) { ret = -ENOMEM; @@ -685,12 +733,11 @@ int vmw_kms_new_framebuffer_dmabuf(struct vmw_private *dev_priv, goto out_err3; } - /* XXX get the first 3 from the surface info */ - vfbd->base.base.bits_per_pixel = 32; - vfbd->base.base.pitch = width * vfbd->base.base.bits_per_pixel / 8; - vfbd->base.base.depth = 24; - vfbd->base.base.width = width; - vfbd->base.base.height = height; + vfbd->base.base.bits_per_pixel = mode_cmd->bpp; + vfbd->base.base.pitch = mode_cmd->pitch; + vfbd->base.base.depth = mode_cmd->depth; + vfbd->base.base.width = mode_cmd->width; + vfbd->base.base.height = mode_cmd->height; vfbd->base.pin = vmw_framebuffer_dmabuf_pin; vfbd->base.unpin = vmw_framebuffer_dmabuf_unpin; vfbd->buffer = dmabuf; @@ -719,8 +766,25 @@ static struct drm_framebuffer *vmw_kms_fb_create(struct drm_device *dev, struct vmw_framebuffer *vfb = NULL; struct vmw_surface *surface = NULL; struct vmw_dma_buffer *bo = NULL; + unsigned int required_size; int ret; + /** + * This code should be conditioned on Screen Objects not being used. + * If screen objects are used, we can allocate a GMR to hold the + * requested framebuffer. + */ + + required_size = mode_cmd->pitch * mode_cmd->height; + if (unlikely(required_size > dev_priv->vram_size)) { + DRM_ERROR("VRAM size is too small for requested mode.\n"); + return NULL; + } + + /** + * End conditioned code. + */ + ret = vmw_user_surface_lookup_handle(dev_priv, tfile, mode_cmd->handle, &surface); if (ret) @@ -730,7 +794,7 @@ static struct drm_framebuffer *vmw_kms_fb_create(struct drm_device *dev, goto err_not_scanout; ret = vmw_kms_new_framebuffer_surface(dev_priv, surface, &vfb, - mode_cmd->width, mode_cmd->height); + mode_cmd); /* vmw_user_surface_lookup takes one ref so does new_fb */ vmw_surface_unreference(&surface); @@ -751,7 +815,7 @@ try_dmabuf: } ret = vmw_kms_new_framebuffer_dmabuf(dev_priv, bo, &vfb, - mode_cmd->width, mode_cmd->height); + mode_cmd); /* vmw_user_dmabuf_lookup takes one ref so does new_fb */ vmw_dmabuf_unreference(&bo); -- cgit v1.2.3-70-g09d2 From 094e0fa8b96c9fab5df9597e728d82f3d87ee471 Mon Sep 17 00:00:00 2001 From: Thomas Hellstrom Date: Tue, 5 Oct 2010 12:43:00 +0200 Subject: drm/vmwgfx: Fix ACPI S3 & S4 functionality. Don't suspend or hibernate when there are 3D resources active since we can't restore the device's 3D state. Instead fail with an error message. In other cases, make sure we re-enable the fifo and unlock ttm on resume. Signed-off-by: Thomas Hellstrom Signed-off-by: Dave Airlie --- drivers/gpu/drm/vmwgfx/vmwgfx_drv.c | 38 +++++++++++++++++++++++++++++++++++-- drivers/gpu/drm/vmwgfx/vmwgfx_drv.h | 1 + 2 files changed, 37 insertions(+), 2 deletions(-) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c index 5c845b6ec49..91eeade9212 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c @@ -751,15 +751,40 @@ static int vmwgfx_pm_notifier(struct notifier_block *nb, unsigned long val, * Buffer contents is moved to swappable memory. */ ttm_bo_swapout_all(&dev_priv->bdev); + + /** + * Release 3d reference held by fbdev and potentially + * stop fifo. + */ + dev_priv->suspended = true; + if (dev_priv->enable_fb) + vmw_3d_resource_dec(dev_priv); + break; case PM_POST_HIBERNATION: case PM_POST_SUSPEND: + case PM_POST_RESTORE: + if (!dev_priv->suspended) { + printk(KERN_WARNING + "[%s] Driver is not suspended at resume" + " point.\n", VMWGFX_DRIVER_NAME); + + break; + } + + /** + * Reclaim 3d reference held by fbdev and potentially + * start fifo. + */ + if (dev_priv->enable_fb) + vmw_3d_resource_inc(dev_priv); + + dev_priv->suspended = false; ttm_suspend_unlock(&vmaster->lock); + break; case PM_RESTORE_PREPARE: break; - case PM_POST_RESTORE: - break; default: break; } @@ -772,6 +797,15 @@ static int vmwgfx_pm_notifier(struct notifier_block *nb, unsigned long val, int vmw_pci_suspend(struct pci_dev *pdev, pm_message_t state) { + struct drm_device *dev = pci_get_drvdata(pdev); + struct vmw_private *dev_priv = vmw_priv(dev); + + if (dev_priv->num_3d_resources != 0) { + DRM_INFO("Can't suspend or hibernate " + "while 3D resources are active.\n"); + return -EBUSY; + } + pci_save_state(pdev); pci_disable_device(pdev); pci_set_power_state(pdev, PCI_D3hot); diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h index 58de6393f61..132cc248d22 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h @@ -286,6 +286,7 @@ struct vmw_private { struct vmw_master *active_master; struct vmw_master fbdev_master; struct notifier_block pm_nb; + bool suspended; struct mutex release_mutex; uint32_t num_3d_resources; -- cgit v1.2.3-70-g09d2 From 7fbd721ad35f8ffec8d9a82e0e4b390cb6c9f4f7 Mon Sep 17 00:00:00 2001 From: Thomas Hellstrom Date: Tue, 5 Oct 2010 12:43:01 +0200 Subject: drm/vmwgfx: Add new-style PM hooks to improve hibernation behavior Add the new-style PM hooks prepare and complete. This allows us to power up the device again after the hibernation image has been created, and display output will thus be active until the VM is finally powered off. Signed-off-by: Thomas Hellstrom Signed-off-by: Dave Airlie --- drivers/gpu/drm/vmwgfx/vmwgfx_drv.c | 113 +++++++++++++++++++++++++----------- 1 file changed, 79 insertions(+), 34 deletions(-) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c index 91eeade9212..f3e481f9aa8 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c @@ -752,34 +752,10 @@ static int vmwgfx_pm_notifier(struct notifier_block *nb, unsigned long val, */ ttm_bo_swapout_all(&dev_priv->bdev); - /** - * Release 3d reference held by fbdev and potentially - * stop fifo. - */ - dev_priv->suspended = true; - if (dev_priv->enable_fb) - vmw_3d_resource_dec(dev_priv); - break; case PM_POST_HIBERNATION: case PM_POST_SUSPEND: case PM_POST_RESTORE: - if (!dev_priv->suspended) { - printk(KERN_WARNING - "[%s] Driver is not suspended at resume" - " point.\n", VMWGFX_DRIVER_NAME); - - break; - } - - /** - * Reclaim 3d reference held by fbdev and potentially - * start fifo. - */ - if (dev_priv->enable_fb) - vmw_3d_resource_inc(dev_priv); - - dev_priv->suspended = false; ttm_suspend_unlock(&vmaster->lock); break; @@ -795,7 +771,7 @@ static int vmwgfx_pm_notifier(struct notifier_block *nb, unsigned long val, * These might not be needed with the virtual SVGA device. */ -int vmw_pci_suspend(struct pci_dev *pdev, pm_message_t state) +static int vmw_pci_suspend(struct pci_dev *pdev, pm_message_t state) { struct drm_device *dev = pci_get_drvdata(pdev); struct vmw_private *dev_priv = vmw_priv(dev); @@ -812,13 +788,81 @@ int vmw_pci_suspend(struct pci_dev *pdev, pm_message_t state) return 0; } -int vmw_pci_resume(struct pci_dev *pdev) +static int vmw_pci_resume(struct pci_dev *pdev) { pci_set_power_state(pdev, PCI_D0); pci_restore_state(pdev); return pci_enable_device(pdev); } +static int vmw_pm_suspend(struct device *kdev) +{ + struct pci_dev *pdev = to_pci_dev(kdev); + struct pm_message dummy; + + dummy.event = 0; + + return vmw_pci_suspend(pdev, dummy); +} + +static int vmw_pm_resume(struct device *kdev) +{ + struct pci_dev *pdev = to_pci_dev(kdev); + + return vmw_pci_resume(pdev); +} + +static int vmw_pm_prepare(struct device *kdev) +{ + struct pci_dev *pdev = to_pci_dev(kdev); + struct drm_device *dev = pci_get_drvdata(pdev); + struct vmw_private *dev_priv = vmw_priv(dev); + + /** + * Release 3d reference held by fbdev and potentially + * stop fifo. + */ + dev_priv->suspended = true; + if (dev_priv->enable_fb) + vmw_3d_resource_dec(dev_priv); + + if (dev_priv->num_3d_resources != 0) { + + DRM_INFO("Can't suspend or hibernate " + "while 3D resources are active.\n"); + + if (dev_priv->enable_fb) + vmw_3d_resource_inc(dev_priv); + dev_priv->suspended = false; + return -EBUSY; + } + + return 0; +} + +static void vmw_pm_complete(struct device *kdev) +{ + struct pci_dev *pdev = to_pci_dev(kdev); + struct drm_device *dev = pci_get_drvdata(pdev); + struct vmw_private *dev_priv = vmw_priv(dev); + + /** + * Reclaim 3d reference held by fbdev and potentially + * start fifo. + */ + if (dev_priv->enable_fb) + vmw_3d_resource_inc(dev_priv); + + dev_priv->suspended = false; +} + +static const struct dev_pm_ops vmw_pm_ops = { + .prepare = vmw_pm_prepare, + .complete = vmw_pm_complete, + .suspend = vmw_pm_suspend, + .resume = vmw_pm_resume, +}; + static struct drm_driver driver = { .driver_features = DRIVER_HAVE_IRQ | DRIVER_IRQ_SHARED | DRIVER_MODESET, @@ -852,15 +896,16 @@ static struct drm_driver driver = { #if defined(CONFIG_COMPAT) .compat_ioctl = drm_compat_ioctl, #endif - }, + }, .pci_driver = { - .name = VMWGFX_DRIVER_NAME, - .id_table = vmw_pci_id_list, - .probe = vmw_probe, - .remove = vmw_remove, - .suspend = vmw_pci_suspend, - .resume = vmw_pci_resume - }, + .name = VMWGFX_DRIVER_NAME, + .id_table = vmw_pci_id_list, + .probe = vmw_probe, + .remove = vmw_remove, + .driver = { + .pm = &vmw_pm_ops + } + }, .name = VMWGFX_DRIVER_NAME, .desc = VMWGFX_DRIVER_DESC, .date = VMWGFX_DRIVER_DATE, -- cgit v1.2.3-70-g09d2 From 02b001624f0384540299d9288fdaf37b7d37c814 Mon Sep 17 00:00:00 2001 From: Thomas Hellstrom Date: Tue, 5 Oct 2010 12:43:02 +0200 Subject: drm: vmwgfx: Add a struct drm_file parameter to the dirty framebuffer callback This is needed for the callback to identify the caller and take appropriate locks if needed. Signed-off-by: Thomas Hellstrom Signed-off-by: Dave Airlie --- drivers/gpu/drm/drm_crtc.c | 3 ++- drivers/gpu/drm/vmwgfx/vmwgfx_kms.c | 2 ++ include/drm/drm_crtc.h | 3 ++- 3 files changed, 6 insertions(+), 2 deletions(-) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c index 37e0b4fa482..6985cb1da72 100644 --- a/drivers/gpu/drm/drm_crtc.c +++ b/drivers/gpu/drm/drm_crtc.c @@ -1854,7 +1854,8 @@ int drm_mode_dirtyfb_ioctl(struct drm_device *dev, } if (fb->funcs->dirty) { - ret = fb->funcs->dirty(fb, flags, r->color, clips, num_clips); + ret = fb->funcs->dirty(fb, file_priv, flags, r->color, + clips, num_clips); } else { ret = -ENOSYS; goto out_err2; diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c index f30223cafad..073b3e1c9cc 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c @@ -392,6 +392,7 @@ out_unlock: int vmw_framebuffer_surface_dirty(struct drm_framebuffer *framebuffer, + struct drm_file *file_priv, unsigned flags, unsigned color, struct drm_clip_rect *clips, unsigned num_clips) @@ -583,6 +584,7 @@ void vmw_framebuffer_dmabuf_destroy(struct drm_framebuffer *framebuffer) } int vmw_framebuffer_dmabuf_dirty(struct drm_framebuffer *framebuffer, + struct drm_file *file_priv, unsigned flags, unsigned color, struct drm_clip_rect *clips, unsigned num_clips) diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h index 3e5a51af757..15c4796fd46 100644 --- a/include/drm/drm_crtc.h +++ b/include/drm/drm_crtc.h @@ -221,7 +221,8 @@ struct drm_framebuffer_funcs { * the semantics and arguments have a one to one mapping * on this function. */ - int (*dirty)(struct drm_framebuffer *framebuffer, unsigned flags, + int (*dirty)(struct drm_framebuffer *framebuffer, + struct drm_file *file_priv, unsigned flags, unsigned color, struct drm_clip_rect *clips, unsigned num_clips); }; -- cgit v1.2.3-70-g09d2 From 3a939a5ece3030e60c966a885c8e9bd329c4faf7 Mon Sep 17 00:00:00 2001 From: Thomas Hellstrom Date: Tue, 5 Oct 2010 12:43:03 +0200 Subject: drm/vmwgfx: Take the ttm lock around the dirty ioctl This makes sure noone accesses the fifo while it's taken down using the dirty ioctl. Also make sure all workqueues are idled before the fifo is taken down. Signed-off-by: Thomas Hellstrom Signed-off-by: Dave Airlie --- drivers/gpu/drm/vmwgfx/vmwgfx_drv.c | 5 ++- drivers/gpu/drm/vmwgfx/vmwgfx_drv.h | 3 ++ drivers/gpu/drm/vmwgfx/vmwgfx_kms.c | 84 +++++++++++++++++++++++++++++++++---- 3 files changed, 84 insertions(+), 8 deletions(-) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c index f3e481f9aa8..201c34d1f3e 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c @@ -597,6 +597,8 @@ static void vmw_lastclose(struct drm_device *dev) static void vmw_master_init(struct vmw_master *vmaster) { ttm_lock_init(&vmaster->lock); + INIT_LIST_HEAD(&vmaster->fb_surf); + mutex_init(&vmaster->fb_surf_mutex); } static int vmw_master_create(struct drm_device *dev, @@ -608,7 +610,7 @@ static int vmw_master_create(struct drm_device *dev, if (unlikely(vmaster == NULL)) return -ENOMEM; - ttm_lock_init(&vmaster->lock); + vmw_master_init(vmaster); ttm_lock_set_kill(&vmaster->lock, true, SIGTERM); master->driver_priv = vmaster; @@ -699,6 +701,7 @@ static void vmw_master_drop(struct drm_device *dev, vmw_fp->locked_master = drm_master_get(file_priv->master); ret = ttm_vt_lock(&vmaster->lock, false, vmw_fp->tfile); + vmw_kms_idle_workqueues(vmaster); if (unlikely((ret != 0))) { DRM_ERROR("Unable to lock TTM at VT switch.\n"); diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h index 132cc248d22..0ab53d98310 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h @@ -151,6 +151,8 @@ struct vmw_overlay; struct vmw_master { struct ttm_lock lock; + struct mutex fb_surf_mutex; + struct list_head fb_surf; }; struct vmw_vga_topology_state { @@ -519,6 +521,7 @@ void vmw_kms_write_svga(struct vmw_private *vmw_priv, unsigned bbp, unsigned depth); int vmw_kms_update_layout_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv); +void vmw_kms_idle_workqueues(struct vmw_master *vmaster); u32 vmw_get_vblank_counter(struct drm_device *dev, int crtc); /** diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c index 073b3e1c9cc..82bd3d8c0e4 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c @@ -332,18 +332,55 @@ struct vmw_framebuffer_surface { struct delayed_work d_work; struct mutex work_lock; bool present_fs; + struct list_head head; + struct drm_master *master; }; +/** + * vmw_kms_idle_workqueues - Flush workqueues on this master + * + * @vmaster - Pointer identifying the master, for the surfaces of which + * we idle the dirty work queues. + * + * This function should be called with the ttm lock held in exclusive mode + * to idle all dirty work queues before the fifo is taken down. + * + * The work task may actually requeue itself, but after the flush returns we're + * sure that there's nothing to present, since the ttm lock is held in + * exclusive mode, so the fifo will never get used. + */ + +void vmw_kms_idle_workqueues(struct vmw_master *vmaster) +{ + struct vmw_framebuffer_surface *entry; + + mutex_lock(&vmaster->fb_surf_mutex); + list_for_each_entry(entry, &vmaster->fb_surf, head) { + if (cancel_delayed_work_sync(&entry->d_work)) + (void) entry->d_work.work.func(&entry->d_work.work); + + (void) cancel_delayed_work_sync(&entry->d_work); + } + mutex_unlock(&vmaster->fb_surf_mutex); +} + void vmw_framebuffer_surface_destroy(struct drm_framebuffer *framebuffer) { - struct vmw_framebuffer_surface *vfb = + struct vmw_framebuffer_surface *vfbs = vmw_framebuffer_to_vfbs(framebuffer); + struct vmw_master *vmaster = vmw_master(vfbs->master); + - cancel_delayed_work_sync(&vfb->d_work); + mutex_lock(&vmaster->fb_surf_mutex); + list_del(&vfbs->head); + mutex_unlock(&vmaster->fb_surf_mutex); + + cancel_delayed_work_sync(&vfbs->d_work); + drm_master_put(&vfbs->master); drm_framebuffer_cleanup(framebuffer); - vmw_surface_unreference(&vfb->surface); + vmw_surface_unreference(&vfbs->surface); - kfree(framebuffer); + kfree(vfbs); } static void vmw_framebuffer_present_fs_callback(struct work_struct *work) @@ -362,6 +399,12 @@ static void vmw_framebuffer_present_fs_callback(struct work_struct *work) SVGA3dCopyRect cr; } *cmd; + /** + * Strictly we should take the ttm_lock in read mode before accessing + * the fifo, to make sure the fifo is present and up. However, + * instead we flush all workqueues under the ttm lock in exclusive mode + * before taking down the fifo. + */ mutex_lock(&vfbs->work_lock); if (!vfbs->present_fs) goto out_unlock; @@ -398,12 +441,14 @@ int vmw_framebuffer_surface_dirty(struct drm_framebuffer *framebuffer, unsigned num_clips) { struct vmw_private *dev_priv = vmw_priv(framebuffer->dev); + struct vmw_master *vmaster = vmw_master(file_priv->master); struct vmw_framebuffer_surface *vfbs = vmw_framebuffer_to_vfbs(framebuffer); struct vmw_surface *surf = vfbs->surface; struct drm_clip_rect norect; SVGA3dCopyRect *cr; int i, inc = 1; + int ret; struct { SVGA3dCmdHeader header; @@ -411,6 +456,13 @@ int vmw_framebuffer_surface_dirty(struct drm_framebuffer *framebuffer, SVGA3dCopyRect cr; } *cmd; + if (unlikely(vfbs->master != file_priv->master)) + return -EINVAL; + + ret = ttm_read_lock(&vmaster->lock, true); + if (unlikely(ret != 0)) + return ret; + if (!num_clips || !(dev_priv->fifo.capabilities & SVGA_FIFO_CAP_SCREEN_OBJECT)) { @@ -426,6 +478,7 @@ int vmw_framebuffer_surface_dirty(struct drm_framebuffer *framebuffer, */ vmw_framebuffer_present_fs_callback(&vfbs->d_work.work); } + ttm_read_unlock(&vmaster->lock); return 0; } @@ -443,6 +496,7 @@ int vmw_framebuffer_surface_dirty(struct drm_framebuffer *framebuffer, cmd = vmw_fifo_reserve(dev_priv, sizeof(*cmd) + (num_clips - 1) * sizeof(cmd->cr)); if (unlikely(cmd == NULL)) { DRM_ERROR("Fifo reserve failed.\n"); + ttm_read_unlock(&vmaster->lock); return -ENOMEM; } @@ -462,7 +516,7 @@ int vmw_framebuffer_surface_dirty(struct drm_framebuffer *framebuffer, } vmw_fifo_commit(dev_priv, sizeof(*cmd) + (num_clips - 1) * sizeof(cmd->cr)); - + ttm_read_unlock(&vmaster->lock); return 0; } @@ -473,6 +527,7 @@ static struct drm_framebuffer_funcs vmw_framebuffer_surface_funcs = { }; static int vmw_kms_new_framebuffer_surface(struct vmw_private *dev_priv, + struct drm_file *file_priv, struct vmw_surface *surface, struct vmw_framebuffer **out, const struct drm_mode_fb_cmd @@ -482,6 +537,7 @@ static int vmw_kms_new_framebuffer_surface(struct vmw_private *dev_priv, struct drm_device *dev = dev_priv->dev; struct vmw_framebuffer_surface *vfbs; enum SVGA3dSurfaceFormat format; + struct vmw_master *vmaster = vmw_master(file_priv->master); int ret; /* @@ -546,8 +602,14 @@ static int vmw_kms_new_framebuffer_surface(struct vmw_private *dev_priv, vfbs->base.pin = &vmw_surface_dmabuf_pin; vfbs->base.unpin = &vmw_surface_dmabuf_unpin; vfbs->surface = surface; + vfbs->master = drm_master_get(file_priv->master); mutex_init(&vfbs->work_lock); + + mutex_lock(&vmaster->fb_surf_mutex); INIT_DELAYED_WORK(&vfbs->d_work, &vmw_framebuffer_present_fs_callback); + list_add_tail(&vfbs->head, &vmaster->fb_surf); + mutex_unlock(&vmaster->fb_surf_mutex); + *out = &vfbs->base; return 0; @@ -590,13 +652,19 @@ int vmw_framebuffer_dmabuf_dirty(struct drm_framebuffer *framebuffer, unsigned num_clips) { struct vmw_private *dev_priv = vmw_priv(framebuffer->dev); + struct vmw_master *vmaster = vmw_master(file_priv->master); struct drm_clip_rect norect; + int ret; struct { uint32_t header; SVGAFifoCmdUpdate body; } *cmd; int i, increment = 1; + ret = ttm_read_lock(&vmaster->lock, true); + if (unlikely(ret != 0)) + return ret; + if (!num_clips) { num_clips = 1; clips = &norect; @@ -611,6 +679,7 @@ int vmw_framebuffer_dmabuf_dirty(struct drm_framebuffer *framebuffer, cmd = vmw_fifo_reserve(dev_priv, sizeof(*cmd) * num_clips); if (unlikely(cmd == NULL)) { DRM_ERROR("Fifo reserve failed.\n"); + ttm_read_unlock(&vmaster->lock); return -ENOMEM; } @@ -623,6 +692,7 @@ int vmw_framebuffer_dmabuf_dirty(struct drm_framebuffer *framebuffer, } vmw_fifo_commit(dev_priv, sizeof(*cmd) * num_clips); + ttm_read_unlock(&vmaster->lock); return 0; } @@ -795,8 +865,8 @@ static struct drm_framebuffer *vmw_kms_fb_create(struct drm_device *dev, if (!surface->scanout) goto err_not_scanout; - ret = vmw_kms_new_framebuffer_surface(dev_priv, surface, &vfb, - mode_cmd); + ret = vmw_kms_new_framebuffer_surface(dev_priv, file_priv, surface, + &vfb, mode_cmd); /* vmw_user_surface_lookup takes one ref so does new_fb */ vmw_surface_unreference(&surface); -- cgit v1.2.3-70-g09d2 From e133e7371231e49c3e7d626e2251cb6f7c3ca1ad Mon Sep 17 00:00:00 2001 From: Thomas Hellstrom Date: Tue, 5 Oct 2010 12:43:04 +0200 Subject: drm/vmwgfx: Prune modes based on available VRAM size This needs to be reviewed once we support screen objects and don't rely on VRAM for the frame-buffer. Also fix some integer overflow issues pointed out by Michel Daenzer. Signed-off-by: Thomas Hellstrom Signed-off-by: Dave Airlie --- drivers/gpu/drm/vmwgfx/vmwgfx_drv.h | 3 +++ drivers/gpu/drm/vmwgfx/vmwgfx_fb.c | 7 +++++++ drivers/gpu/drm/vmwgfx/vmwgfx_kms.c | 11 +++++++++-- drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c | 28 +++++++++++++++++++--------- 4 files changed, 38 insertions(+), 11 deletions(-) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h index 0ab53d98310..a10d0ad3103 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h @@ -522,6 +522,9 @@ void vmw_kms_write_svga(struct vmw_private *vmw_priv, int vmw_kms_update_layout_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv); void vmw_kms_idle_workqueues(struct vmw_master *vmaster); +bool vmw_kms_validate_mode_vram(struct vmw_private *dev_priv, + uint32_t pitch, + uint32_t height); u32 vmw_get_vblank_counter(struct drm_device *dev, int crtc); /** diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_fb.c b/drivers/gpu/drm/vmwgfx/vmwgfx_fb.c index 409e172f4ab..086ef049026 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_fb.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_fb.c @@ -144,6 +144,13 @@ static int vmw_fb_check_var(struct fb_var_screeninfo *var, return -EINVAL; } + if (!vmw_kms_validate_mode_vram(vmw_priv, + info->fix.line_length, + var->yoffset + var->yres)) { + DRM_ERROR("Requested geom can not fit in framebuffer\n"); + return -EINVAL; + } + return 0; } diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c index 82bd3d8c0e4..5fb68f3981a 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c @@ -838,7 +838,7 @@ static struct drm_framebuffer *vmw_kms_fb_create(struct drm_device *dev, struct vmw_framebuffer *vfb = NULL; struct vmw_surface *surface = NULL; struct vmw_dma_buffer *bo = NULL; - unsigned int required_size; + u64 required_size; int ret; /** @@ -848,7 +848,7 @@ static struct drm_framebuffer *vmw_kms_fb_create(struct drm_device *dev, */ required_size = mode_cmd->pitch * mode_cmd->height; - if (unlikely(required_size > dev_priv->vram_size)) { + if (unlikely(required_size > (u64) dev_priv->vram_size)) { DRM_ERROR("VRAM size is too small for requested mode.\n"); return NULL; } @@ -1133,6 +1133,13 @@ out_unlock: return ret; } +bool vmw_kms_validate_mode_vram(struct vmw_private *dev_priv, + uint32_t pitch, + uint32_t height) +{ + return ((u64) pitch * (u64) height) < (u64) dev_priv->vram_size; +} + u32 vmw_get_vblank_counter(struct drm_device *dev, int crtc) { return 0; diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c b/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c index 11cb39e3acc..a01c47ddb5b 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c @@ -427,7 +427,9 @@ static int vmw_ldu_connector_fill_modes(struct drm_connector *connector, { struct vmw_legacy_display_unit *ldu = vmw_connector_to_ldu(connector); struct drm_device *dev = connector->dev; + struct vmw_private *dev_priv = vmw_priv(dev); struct drm_display_mode *mode = NULL; + struct drm_display_mode *bmode; struct drm_display_mode prefmode = { DRM_MODE("preferred", DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -443,22 +445,30 @@ static int vmw_ldu_connector_fill_modes(struct drm_connector *connector, mode->hdisplay = ldu->pref_width; mode->vdisplay = ldu->pref_height; mode->vrefresh = drm_mode_vrefresh(mode); - drm_mode_probed_add(connector, mode); + if (vmw_kms_validate_mode_vram(dev_priv, mode->hdisplay * 2, + mode->vdisplay)) { + drm_mode_probed_add(connector, mode); - if (ldu->pref_mode) { - list_del_init(&ldu->pref_mode->head); - drm_mode_destroy(dev, ldu->pref_mode); - } + if (ldu->pref_mode) { + list_del_init(&ldu->pref_mode->head); + drm_mode_destroy(dev, ldu->pref_mode); + } - ldu->pref_mode = mode; + ldu->pref_mode = mode; + } } for (i = 0; vmw_ldu_connector_builtin[i].type != 0; i++) { - if (vmw_ldu_connector_builtin[i].hdisplay > max_width || - vmw_ldu_connector_builtin[i].vdisplay > max_height) + bmode = &vmw_ldu_connector_builtin[i]; + if (bmode->hdisplay > max_width || + bmode->vdisplay > max_height) + continue; + + if (!vmw_kms_validate_mode_vram(dev_priv, bmode->hdisplay * 2, + bmode->vdisplay)) continue; - mode = drm_mode_duplicate(dev, &vmw_ldu_connector_builtin[i]); + mode = drm_mode_duplicate(dev, bmode); if (!mode) return 0; mode->vrefresh = drm_mode_vrefresh(mode); -- cgit v1.2.3-70-g09d2 From 09e2601b5a7bb817fb07df6f430289484a9a2cad Mon Sep 17 00:00:00 2001 From: Thomas Hellstrom Date: Tue, 5 Oct 2010 12:43:05 +0200 Subject: drm/vmwgfx: Don't flush fb if we're in the suspended state. Signed-off-by: Thomas Hellstrom Signed-off-by: Dave Airlie --- drivers/gpu/drm/vmwgfx/vmwgfx_fb.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_fb.c b/drivers/gpu/drm/vmwgfx/vmwgfx_fb.c index 086ef049026..b27a9f2887d 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_fb.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_fb.c @@ -212,6 +212,9 @@ static void vmw_fb_dirty_flush(struct vmw_fb_par *par) SVGAFifoCmdUpdate body; } *cmd; + if (vmw_priv->suspended) + return; + spin_lock_irqsave(&par->dirty.lock, flags); if (!par->dirty.active) { spin_unlock_irqrestore(&par->dirty.lock, flags); -- cgit v1.2.3-70-g09d2 From 30f47fc85d524d25d63da9e6d77e55ab99c6cc4a Mon Sep 17 00:00:00 2001 From: Thomas Hellstrom Date: Tue, 5 Oct 2010 12:43:06 +0200 Subject: drm/vmwgfx: Add a parameter to get the max fb size This can be used by the X server to restrict mode resolutions and size of root pixmap. Bump minor to announce this availability. Bump driver date. Signed-off-by: Thomas Hellstrom Signed-off-by: Dave Airlie --- drivers/gpu/drm/vmwgfx/vmwgfx_drv.h | 4 ++-- drivers/gpu/drm/vmwgfx/vmwgfx_ioctl.c | 3 +++ include/drm/vmwgfx_drm.h | 1 + 3 files changed, 6 insertions(+), 2 deletions(-) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h index a10d0ad3103..217ba1fe5dd 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h @@ -39,9 +39,9 @@ #include "ttm/ttm_execbuf_util.h" #include "ttm/ttm_module.h" -#define VMWGFX_DRIVER_DATE "20100209" +#define VMWGFX_DRIVER_DATE "20100723" #define VMWGFX_DRIVER_MAJOR 1 -#define VMWGFX_DRIVER_MINOR 2 +#define VMWGFX_DRIVER_MINOR 3 #define VMWGFX_DRIVER_PATCHLEVEL 0 #define VMWGFX_FILE_PAGE_OFFSET 0x00100000 #define VMWGFX_FIFO_STATIC_SIZE (1024*1024) diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_ioctl.c b/drivers/gpu/drm/vmwgfx/vmwgfx_ioctl.c index 1c7a316454d..570d57775a5 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_ioctl.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_ioctl.c @@ -54,6 +54,9 @@ int vmw_getparam_ioctl(struct drm_device *dev, void *data, case DRM_VMW_PARAM_FIFO_CAPS: param->value = dev_priv->fifo.capabilities; break; + case DRM_VMW_PARAM_MAX_FB_SIZE: + param->value = dev_priv->vram_size; + break; default: DRM_ERROR("Illegal vmwgfx get param request: %d\n", param->param); diff --git a/include/drm/vmwgfx_drm.h b/include/drm/vmwgfx_drm.h index 4d0842391ed..650e6bf6f69 100644 --- a/include/drm/vmwgfx_drm.h +++ b/include/drm/vmwgfx_drm.h @@ -72,6 +72,7 @@ #define DRM_VMW_PARAM_FIFO_OFFSET 3 #define DRM_VMW_PARAM_HW_CAPS 4 #define DRM_VMW_PARAM_FIFO_CAPS 5 +#define DRM_VMW_PARAM_MAX_FB_SIZE 6 /** * struct drm_vmw_getparam_arg -- cgit v1.2.3-70-g09d2 From 73558ead6467f0590fe58a03a16a94d2a934178b Mon Sep 17 00:00:00 2001 From: Thomas Hellstrom Date: Tue, 5 Oct 2010 12:43:07 +0200 Subject: drm/vmwgfx: Add modinfo version Signed-off-by: Thomas Hellstrom Signed-off-by: Dave Airlie --- drivers/gpu/drm/vmwgfx/vmwgfx_drv.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c index 201c34d1f3e..f2942b3c59c 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c @@ -942,3 +942,7 @@ module_exit(vmwgfx_exit); MODULE_AUTHOR("VMware Inc. and others"); MODULE_DESCRIPTION("Standalone drm driver for the VMware SVGA device"); MODULE_LICENSE("GPL and additional rights"); +MODULE_VERSION(__stringify(VMWGFX_DRIVER_MAJOR) "." + __stringify(VMWGFX_DRIVER_MINOR) "." + __stringify(VMWGFX_DRIVER_PATCHLEVEL) "." + "0"); -- cgit v1.2.3-70-g09d2 From 029e50bfc231741c533128e7e806850557f8e97f Mon Sep 17 00:00:00 2001 From: Thomas Hellstrom Date: Tue, 5 Oct 2010 12:43:08 +0200 Subject: drm/vmwgfx: Save at least one screen layout Save at least one screen layout during vga save to avoid odd things happening during restore. Signed-off-by: Thomas Hellstrom Signed-off-by: Dave Airlie --- drivers/gpu/drm/vmwgfx/vmwgfx_kms.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c index 5fb68f3981a..87c6e6156d7 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c @@ -1025,6 +1025,9 @@ int vmw_kms_save_vga(struct vmw_private *vmw_priv) vmw_priv->num_displays = vmw_read(vmw_priv, SVGA_REG_NUM_GUEST_DISPLAYS); + if (vmw_priv->num_displays == 0) + vmw_priv->num_displays = 1; + for (i = 0; i < vmw_priv->num_displays; ++i) { save = &vmw_priv->vga_save[i]; vmw_write(vmw_priv, SVGA_REG_DISPLAY_ID, i); -- cgit v1.2.3-70-g09d2 From 8aea528736bf83ba0cdde67a3c0ca0250581eade Mon Sep 17 00:00:00 2001 From: Thomas Hellstrom Date: Tue, 5 Oct 2010 12:43:09 +0200 Subject: drm/vmwgfx: Bump minor and driver date Signed-off-by: Thomas Hellstrom Signed-off-by: Dave Airlie --- drivers/gpu/drm/vmwgfx/vmwgfx_drv.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/gpu/drm') diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h index 217ba1fe5dd..9d55fa8cd0f 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h @@ -39,9 +39,9 @@ #include "ttm/ttm_execbuf_util.h" #include "ttm/ttm_module.h" -#define VMWGFX_DRIVER_DATE "20100723" +#define VMWGFX_DRIVER_DATE "20100927" #define VMWGFX_DRIVER_MAJOR 1 -#define VMWGFX_DRIVER_MINOR 3 +#define VMWGFX_DRIVER_MINOR 4 #define VMWGFX_DRIVER_PATCHLEVEL 0 #define VMWGFX_FILE_PAGE_OFFSET 0x00100000 #define VMWGFX_FIFO_STATIC_SIZE (1024*1024) -- cgit v1.2.3-70-g09d2