Commit ec7f4961 authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'drm-fixes-2022-05-14' of git://anongit.freedesktop.org/drm/drm

Pull more drm fixes from Dave Airlie:
 "Turns out I was right, some fixes hadn't made it to me yet. The vmwgfx
  ones also popped up later, but all seem like bad enough things to fix.
  The dma-buf, vc4 and nouveau ones are all pretty small.

  The fbdev fixes are a bit more complicated: a fix to cleanup fbdev
  devices properly, uncovered some use-after-free bugs in existing
  drivers. Then the fix for those bugs wasn't correct. This reverts that
  fix, and puts the proper fixes in place in the drivers to avoid the
  use-after-frees.

  This has had a fair number of eyes on it at this stage, and I'm
  confident enough that it puts things in the right place, and is less
  dangerous than reverting our way out of the initial change at this
  stage.

  fbdev:
   - revert NULL deref fix that turned into a use-after-free
   - prevent use-after-free in fbdev
   - efifb/simplefb/vesafb: fix cleanup paths to avoid use-after-frees

  dma-buf:
   - fix panic in stats setup

  vc4:
   - fix hdmi build

  nouveau:
   - tegra iommu present fix
   - fix leak in backlight name

  vmwgfx:
   - Black screen due to fences using FIFO checks on SVGA3
   - Random black screens on boot due to uninitialized drm_mode_fb_cmd2
   - Hangs on SVGA3 due to command buffers being used with gbobjects"

* tag 'drm-fixes-2022-05-14' of git://anongit.freedesktop.org/drm/drm:
  drm/vmwgfx: Disable command buffers on svga3 without gbobjects
  drm/vmwgfx: Initialize drm_mode_fb_cmd2
  drm/vmwgfx: Fix fencing on SVGAv3
  drm/vc4: hdmi: Fix build error for implicit function declaration
  dma-buf: call dma_buf_stats_setup after dmabuf is in valid list
  fbdev: efifb: Fix a use-after-free due early fb_info cleanup
  drm/nouveau: Fix a potential theorical leak in nouveau_get_backlight_name()
  drm/nouveau/tegra: Stop using iommu_present()
  fbdev: vesafb: Cleanup fb_info in .fb_destroy rather than .remove
  fbdev: efifb: Cleanup fb_info in .fb_destroy rather than .remove
  fbdev: simplefb: Cleanup fb_info in .fb_destroy rather than .remove
  fbdev: Prevent possible use-after-free in fb_release()
  Revert "fbdev: Make fb_release() return -ENODEV if fbdev was unregistered"
parents d928e8f3 eb7bac39
...@@ -543,10 +543,6 @@ struct dma_buf *dma_buf_export(const struct dma_buf_export_info *exp_info) ...@@ -543,10 +543,6 @@ struct dma_buf *dma_buf_export(const struct dma_buf_export_info *exp_info)
file->f_mode |= FMODE_LSEEK; file->f_mode |= FMODE_LSEEK;
dmabuf->file = file; dmabuf->file = file;
ret = dma_buf_stats_setup(dmabuf);
if (ret)
goto err_sysfs;
mutex_init(&dmabuf->lock); mutex_init(&dmabuf->lock);
INIT_LIST_HEAD(&dmabuf->attachments); INIT_LIST_HEAD(&dmabuf->attachments);
...@@ -554,6 +550,10 @@ struct dma_buf *dma_buf_export(const struct dma_buf_export_info *exp_info) ...@@ -554,6 +550,10 @@ struct dma_buf *dma_buf_export(const struct dma_buf_export_info *exp_info)
list_add(&dmabuf->list_node, &db_list.head); list_add(&dmabuf->list_node, &db_list.head);
mutex_unlock(&db_list.lock); mutex_unlock(&db_list.lock);
ret = dma_buf_stats_setup(dmabuf);
if (ret)
goto err_sysfs;
return dmabuf; return dmabuf;
err_sysfs: err_sysfs:
......
...@@ -46,8 +46,9 @@ static bool ...@@ -46,8 +46,9 @@ static bool
nouveau_get_backlight_name(char backlight_name[BL_NAME_SIZE], nouveau_get_backlight_name(char backlight_name[BL_NAME_SIZE],
struct nouveau_backlight *bl) struct nouveau_backlight *bl)
{ {
const int nb = ida_simple_get(&bl_ida, 0, 0, GFP_KERNEL); const int nb = ida_alloc_max(&bl_ida, 99, GFP_KERNEL);
if (nb < 0 || nb >= 100)
if (nb < 0)
return false; return false;
if (nb > 0) if (nb > 0)
snprintf(backlight_name, BL_NAME_SIZE, "nv_backlight%d", nb); snprintf(backlight_name, BL_NAME_SIZE, "nv_backlight%d", nb);
...@@ -414,7 +415,7 @@ nouveau_backlight_init(struct drm_connector *connector) ...@@ -414,7 +415,7 @@ nouveau_backlight_init(struct drm_connector *connector)
nv_encoder, ops, &props); nv_encoder, ops, &props);
if (IS_ERR(bl->dev)) { if (IS_ERR(bl->dev)) {
if (bl->id >= 0) if (bl->id >= 0)
ida_simple_remove(&bl_ida, bl->id); ida_free(&bl_ida, bl->id);
ret = PTR_ERR(bl->dev); ret = PTR_ERR(bl->dev);
goto fail_alloc; goto fail_alloc;
} }
...@@ -442,7 +443,7 @@ nouveau_backlight_fini(struct drm_connector *connector) ...@@ -442,7 +443,7 @@ nouveau_backlight_fini(struct drm_connector *connector)
return; return;
if (bl->id >= 0) if (bl->id >= 0)
ida_simple_remove(&bl_ida, bl->id); ida_free(&bl_ida, bl->id);
backlight_device_unregister(bl->dev); backlight_device_unregister(bl->dev);
nv_conn->backlight = NULL; nv_conn->backlight = NULL;
......
...@@ -123,7 +123,7 @@ nvkm_device_tegra_probe_iommu(struct nvkm_device_tegra *tdev) ...@@ -123,7 +123,7 @@ nvkm_device_tegra_probe_iommu(struct nvkm_device_tegra *tdev)
mutex_init(&tdev->iommu.mutex); mutex_init(&tdev->iommu.mutex);
if (iommu_present(&platform_bus_type)) { if (device_iommu_mapped(dev)) {
tdev->iommu.domain = iommu_domain_alloc(&platform_bus_type); tdev->iommu.domain = iommu_domain_alloc(&platform_bus_type);
if (!tdev->iommu.domain) if (!tdev->iommu.domain)
goto error; goto error;
......
...@@ -38,6 +38,7 @@ ...@@ -38,6 +38,7 @@
#include <drm/drm_scdc_helper.h> #include <drm/drm_scdc_helper.h>
#include <linux/clk.h> #include <linux/clk.h>
#include <linux/component.h> #include <linux/component.h>
#include <linux/gpio/consumer.h>
#include <linux/i2c.h> #include <linux/i2c.h>
#include <linux/of_address.h> #include <linux/of_address.h>
#include <linux/of_gpio.h> #include <linux/of_gpio.h>
......
...@@ -528,7 +528,7 @@ int vmw_cmd_send_fence(struct vmw_private *dev_priv, uint32_t *seqno) ...@@ -528,7 +528,7 @@ int vmw_cmd_send_fence(struct vmw_private *dev_priv, uint32_t *seqno)
*seqno = atomic_add_return(1, &dev_priv->marker_seq); *seqno = atomic_add_return(1, &dev_priv->marker_seq);
} while (*seqno == 0); } while (*seqno == 0);
if (!(vmw_fifo_caps(dev_priv) & SVGA_FIFO_CAP_FENCE)) { if (!vmw_has_fences(dev_priv)) {
/* /*
* Don't request hardware to send a fence. The * Don't request hardware to send a fence. The
...@@ -675,11 +675,14 @@ int vmw_cmd_emit_dummy_query(struct vmw_private *dev_priv, ...@@ -675,11 +675,14 @@ int vmw_cmd_emit_dummy_query(struct vmw_private *dev_priv,
*/ */
bool vmw_cmd_supported(struct vmw_private *vmw) bool vmw_cmd_supported(struct vmw_private *vmw)
{ {
if ((vmw->capabilities & (SVGA_CAP_COMMAND_BUFFERS | bool has_cmdbufs =
SVGA_CAP_CMD_BUFFERS_2)) != 0) (vmw->capabilities & (SVGA_CAP_COMMAND_BUFFERS |
return true; SVGA_CAP_CMD_BUFFERS_2)) != 0;
if (vmw_is_svga_v3(vmw))
return (has_cmdbufs &&
(vmw->capabilities & SVGA_CAP_GBOBJECTS) != 0);
/* /*
* We have FIFO cmd's * We have FIFO cmd's
*/ */
return vmw->fifo_mem != NULL; return has_cmdbufs || vmw->fifo_mem != NULL;
} }
...@@ -1679,4 +1679,12 @@ static inline void vmw_irq_status_write(struct vmw_private *vmw, ...@@ -1679,4 +1679,12 @@ static inline void vmw_irq_status_write(struct vmw_private *vmw,
outl(status, vmw->io_start + SVGA_IRQSTATUS_PORT); outl(status, vmw->io_start + SVGA_IRQSTATUS_PORT);
} }
static inline bool vmw_has_fences(struct vmw_private *vmw)
{
if ((vmw->capabilities & (SVGA_CAP_COMMAND_BUFFERS |
SVGA_CAP_CMD_BUFFERS_2)) != 0)
return true;
return (vmw_fifo_caps(vmw) & SVGA_FIFO_CAP_FENCE) != 0;
}
#endif #endif
...@@ -483,7 +483,7 @@ static int vmw_fb_kms_detach(struct vmw_fb_par *par, ...@@ -483,7 +483,7 @@ static int vmw_fb_kms_detach(struct vmw_fb_par *par,
static int vmw_fb_kms_framebuffer(struct fb_info *info) static int vmw_fb_kms_framebuffer(struct fb_info *info)
{ {
struct drm_mode_fb_cmd2 mode_cmd; struct drm_mode_fb_cmd2 mode_cmd = {0};
struct vmw_fb_par *par = info->par; struct vmw_fb_par *par = info->par;
struct fb_var_screeninfo *var = &info->var; struct fb_var_screeninfo *var = &info->var;
struct drm_framebuffer *cur_fb; struct drm_framebuffer *cur_fb;
......
...@@ -82,6 +82,22 @@ fman_from_fence(struct vmw_fence_obj *fence) ...@@ -82,6 +82,22 @@ fman_from_fence(struct vmw_fence_obj *fence)
return container_of(fence->base.lock, struct vmw_fence_manager, lock); return container_of(fence->base.lock, struct vmw_fence_manager, lock);
} }
static u32 vmw_fence_goal_read(struct vmw_private *vmw)
{
if ((vmw->capabilities2 & SVGA_CAP2_EXTRA_REGS) != 0)
return vmw_read(vmw, SVGA_REG_FENCE_GOAL);
else
return vmw_fifo_mem_read(vmw, SVGA_FIFO_FENCE_GOAL);
}
static void vmw_fence_goal_write(struct vmw_private *vmw, u32 value)
{
if ((vmw->capabilities2 & SVGA_CAP2_EXTRA_REGS) != 0)
vmw_write(vmw, SVGA_REG_FENCE_GOAL, value);
else
vmw_fifo_mem_write(vmw, SVGA_FIFO_FENCE_GOAL, value);
}
/* /*
* Note on fencing subsystem usage of irqs: * Note on fencing subsystem usage of irqs:
* Typically the vmw_fences_update function is called * Typically the vmw_fences_update function is called
...@@ -392,7 +408,7 @@ static bool vmw_fence_goal_new_locked(struct vmw_fence_manager *fman, ...@@ -392,7 +408,7 @@ static bool vmw_fence_goal_new_locked(struct vmw_fence_manager *fman,
if (likely(!fman->seqno_valid)) if (likely(!fman->seqno_valid))
return false; return false;
goal_seqno = vmw_fifo_mem_read(fman->dev_priv, SVGA_FIFO_FENCE_GOAL); goal_seqno = vmw_fence_goal_read(fman->dev_priv);
if (likely(passed_seqno - goal_seqno >= VMW_FENCE_WRAP)) if (likely(passed_seqno - goal_seqno >= VMW_FENCE_WRAP))
return false; return false;
...@@ -400,8 +416,7 @@ static bool vmw_fence_goal_new_locked(struct vmw_fence_manager *fman, ...@@ -400,8 +416,7 @@ static bool vmw_fence_goal_new_locked(struct vmw_fence_manager *fman,
list_for_each_entry(fence, &fman->fence_list, head) { list_for_each_entry(fence, &fman->fence_list, head) {
if (!list_empty(&fence->seq_passed_actions)) { if (!list_empty(&fence->seq_passed_actions)) {
fman->seqno_valid = true; fman->seqno_valid = true;
vmw_fifo_mem_write(fman->dev_priv, vmw_fence_goal_write(fman->dev_priv,
SVGA_FIFO_FENCE_GOAL,
fence->base.seqno); fence->base.seqno);
break; break;
} }
...@@ -434,13 +449,12 @@ static bool vmw_fence_goal_check_locked(struct vmw_fence_obj *fence) ...@@ -434,13 +449,12 @@ static bool vmw_fence_goal_check_locked(struct vmw_fence_obj *fence)
if (dma_fence_is_signaled_locked(&fence->base)) if (dma_fence_is_signaled_locked(&fence->base))
return false; return false;
goal_seqno = vmw_fifo_mem_read(fman->dev_priv, SVGA_FIFO_FENCE_GOAL); goal_seqno = vmw_fence_goal_read(fman->dev_priv);
if (likely(fman->seqno_valid && if (likely(fman->seqno_valid &&
goal_seqno - fence->base.seqno < VMW_FENCE_WRAP)) goal_seqno - fence->base.seqno < VMW_FENCE_WRAP))
return false; return false;
vmw_fifo_mem_write(fman->dev_priv, SVGA_FIFO_FENCE_GOAL, vmw_fence_goal_write(fman->dev_priv, fence->base.seqno);
fence->base.seqno);
fman->seqno_valid = true; fman->seqno_valid = true;
return true; return true;
......
...@@ -32,6 +32,14 @@ ...@@ -32,6 +32,14 @@
#define VMW_FENCE_WRAP (1 << 24) #define VMW_FENCE_WRAP (1 << 24)
static u32 vmw_irqflag_fence_goal(struct vmw_private *vmw)
{
if ((vmw->capabilities2 & SVGA_CAP2_EXTRA_REGS) != 0)
return SVGA_IRQFLAG_REG_FENCE_GOAL;
else
return SVGA_IRQFLAG_FENCE_GOAL;
}
/** /**
* vmw_thread_fn - Deferred (process context) irq handler * vmw_thread_fn - Deferred (process context) irq handler
* *
...@@ -96,7 +104,7 @@ static irqreturn_t vmw_irq_handler(int irq, void *arg) ...@@ -96,7 +104,7 @@ static irqreturn_t vmw_irq_handler(int irq, void *arg)
wake_up_all(&dev_priv->fifo_queue); wake_up_all(&dev_priv->fifo_queue);
if ((masked_status & (SVGA_IRQFLAG_ANY_FENCE | if ((masked_status & (SVGA_IRQFLAG_ANY_FENCE |
SVGA_IRQFLAG_FENCE_GOAL)) && vmw_irqflag_fence_goal(dev_priv))) &&
!test_and_set_bit(VMW_IRQTHREAD_FENCE, dev_priv->irqthread_pending)) !test_and_set_bit(VMW_IRQTHREAD_FENCE, dev_priv->irqthread_pending))
ret = IRQ_WAKE_THREAD; ret = IRQ_WAKE_THREAD;
...@@ -137,8 +145,7 @@ bool vmw_seqno_passed(struct vmw_private *dev_priv, ...@@ -137,8 +145,7 @@ bool vmw_seqno_passed(struct vmw_private *dev_priv,
if (likely(dev_priv->last_read_seqno - seqno < VMW_FENCE_WRAP)) if (likely(dev_priv->last_read_seqno - seqno < VMW_FENCE_WRAP))
return true; return true;
if (!(vmw_fifo_caps(dev_priv) & SVGA_FIFO_CAP_FENCE) && if (!vmw_has_fences(dev_priv) && vmw_fifo_idle(dev_priv, seqno))
vmw_fifo_idle(dev_priv, seqno))
return true; return true;
/** /**
...@@ -160,6 +167,7 @@ int vmw_fallback_wait(struct vmw_private *dev_priv, ...@@ -160,6 +167,7 @@ int vmw_fallback_wait(struct vmw_private *dev_priv,
unsigned long timeout) unsigned long timeout)
{ {
struct vmw_fifo_state *fifo_state = dev_priv->fifo; struct vmw_fifo_state *fifo_state = dev_priv->fifo;
bool fifo_down = false;
uint32_t count = 0; uint32_t count = 0;
uint32_t signal_seq; uint32_t signal_seq;
...@@ -176,12 +184,14 @@ int vmw_fallback_wait(struct vmw_private *dev_priv, ...@@ -176,12 +184,14 @@ int vmw_fallback_wait(struct vmw_private *dev_priv,
*/ */
if (fifo_idle) { if (fifo_idle) {
down_read(&fifo_state->rwsem);
if (dev_priv->cman) { if (dev_priv->cman) {
ret = vmw_cmdbuf_idle(dev_priv->cman, interruptible, ret = vmw_cmdbuf_idle(dev_priv->cman, interruptible,
10*HZ); 10*HZ);
if (ret) if (ret)
goto out_err; goto out_err;
} else if (fifo_state) {
down_read(&fifo_state->rwsem);
fifo_down = true;
} }
} }
...@@ -218,12 +228,12 @@ int vmw_fallback_wait(struct vmw_private *dev_priv, ...@@ -218,12 +228,12 @@ int vmw_fallback_wait(struct vmw_private *dev_priv,
} }
} }
finish_wait(&dev_priv->fence_queue, &__wait); finish_wait(&dev_priv->fence_queue, &__wait);
if (ret == 0 && fifo_idle) if (ret == 0 && fifo_idle && fifo_state)
vmw_fence_write(dev_priv, signal_seq); vmw_fence_write(dev_priv, signal_seq);
wake_up_all(&dev_priv->fence_queue); wake_up_all(&dev_priv->fence_queue);
out_err: out_err:
if (fifo_idle) if (fifo_down)
up_read(&fifo_state->rwsem); up_read(&fifo_state->rwsem);
return ret; return ret;
...@@ -266,13 +276,13 @@ void vmw_seqno_waiter_remove(struct vmw_private *dev_priv) ...@@ -266,13 +276,13 @@ void vmw_seqno_waiter_remove(struct vmw_private *dev_priv)
void vmw_goal_waiter_add(struct vmw_private *dev_priv) void vmw_goal_waiter_add(struct vmw_private *dev_priv)
{ {
vmw_generic_waiter_add(dev_priv, SVGA_IRQFLAG_FENCE_GOAL, vmw_generic_waiter_add(dev_priv, vmw_irqflag_fence_goal(dev_priv),
&dev_priv->goal_queue_waiters); &dev_priv->goal_queue_waiters);
} }
void vmw_goal_waiter_remove(struct vmw_private *dev_priv) void vmw_goal_waiter_remove(struct vmw_private *dev_priv)
{ {
vmw_generic_waiter_remove(dev_priv, SVGA_IRQFLAG_FENCE_GOAL, vmw_generic_waiter_remove(dev_priv, vmw_irqflag_fence_goal(dev_priv),
&dev_priv->goal_queue_waiters); &dev_priv->goal_queue_waiters);
} }
......
...@@ -1344,7 +1344,6 @@ vmw_kms_new_framebuffer(struct vmw_private *dev_priv, ...@@ -1344,7 +1344,6 @@ vmw_kms_new_framebuffer(struct vmw_private *dev_priv,
ret = vmw_kms_new_framebuffer_surface(dev_priv, surface, &vfb, ret = vmw_kms_new_framebuffer_surface(dev_priv, surface, &vfb,
mode_cmd, mode_cmd,
is_bo_proxy); is_bo_proxy);
/* /*
* vmw_create_bo_proxy() adds a reference that is no longer * vmw_create_bo_proxy() adds a reference that is no longer
* needed * needed
...@@ -1385,13 +1384,16 @@ static struct drm_framebuffer *vmw_kms_fb_create(struct drm_device *dev, ...@@ -1385,13 +1384,16 @@ static struct drm_framebuffer *vmw_kms_fb_create(struct drm_device *dev,
ret = vmw_user_lookup_handle(dev_priv, file_priv, ret = vmw_user_lookup_handle(dev_priv, file_priv,
mode_cmd->handles[0], mode_cmd->handles[0],
&surface, &bo); &surface, &bo);
if (ret) if (ret) {
DRM_ERROR("Invalid buffer object handle %u (0x%x).\n",
mode_cmd->handles[0], mode_cmd->handles[0]);
goto err_out; goto err_out;
}
if (!bo && if (!bo &&
!vmw_kms_srf_ok(dev_priv, mode_cmd->width, mode_cmd->height)) { !vmw_kms_srf_ok(dev_priv, mode_cmd->width, mode_cmd->height)) {
DRM_ERROR("Surface size cannot exceed %dx%d", DRM_ERROR("Surface size cannot exceed %dx%d\n",
dev_priv->texture_max_width, dev_priv->texture_max_width,
dev_priv->texture_max_height); dev_priv->texture_max_height);
goto err_out; goto err_out;
......
...@@ -1434,10 +1434,7 @@ fb_release(struct inode *inode, struct file *file) ...@@ -1434,10 +1434,7 @@ fb_release(struct inode *inode, struct file *file)
__acquires(&info->lock) __acquires(&info->lock)
__releases(&info->lock) __releases(&info->lock)
{ {
struct fb_info * const info = file_fb_info(file); struct fb_info * const info = file->private_data;
if (!info)
return -ENODEV;
lock_fb_info(info); lock_fb_info(info);
if (info->fbops->fb_release) if (info->fbops->fb_release)
......
...@@ -80,6 +80,10 @@ void framebuffer_release(struct fb_info *info) ...@@ -80,6 +80,10 @@ void framebuffer_release(struct fb_info *info)
{ {
if (!info) if (!info)
return; return;
if (WARN_ON(refcount_read(&info->count)))
return;
kfree(info->apertures); kfree(info->apertures);
kfree(info); kfree(info);
} }
......
...@@ -243,6 +243,10 @@ static void efifb_show_boot_graphics(struct fb_info *info) ...@@ -243,6 +243,10 @@ static void efifb_show_boot_graphics(struct fb_info *info)
static inline void efifb_show_boot_graphics(struct fb_info *info) {} static inline void efifb_show_boot_graphics(struct fb_info *info) {}
#endif #endif
/*
* fb_ops.fb_destroy is called by the last put_fb_info() call at the end
* of unregister_framebuffer() or fb_release(). Do any cleanup here.
*/
static void efifb_destroy(struct fb_info *info) static void efifb_destroy(struct fb_info *info)
{ {
if (efifb_pci_dev) if (efifb_pci_dev)
...@@ -254,10 +258,13 @@ static void efifb_destroy(struct fb_info *info) ...@@ -254,10 +258,13 @@ static void efifb_destroy(struct fb_info *info)
else else
memunmap(info->screen_base); memunmap(info->screen_base);
} }
if (request_mem_succeeded) if (request_mem_succeeded)
release_mem_region(info->apertures->ranges[0].base, release_mem_region(info->apertures->ranges[0].base,
info->apertures->ranges[0].size); info->apertures->ranges[0].size);
fb_dealloc_cmap(&info->cmap); fb_dealloc_cmap(&info->cmap);
framebuffer_release(info);
} }
static const struct fb_ops efifb_ops = { static const struct fb_ops efifb_ops = {
...@@ -620,9 +627,9 @@ static int efifb_remove(struct platform_device *pdev) ...@@ -620,9 +627,9 @@ static int efifb_remove(struct platform_device *pdev)
{ {
struct fb_info *info = platform_get_drvdata(pdev); struct fb_info *info = platform_get_drvdata(pdev);
/* efifb_destroy takes care of info cleanup */
unregister_framebuffer(info); unregister_framebuffer(info);
sysfs_remove_groups(&pdev->dev.kobj, efifb_groups); sysfs_remove_groups(&pdev->dev.kobj, efifb_groups);
framebuffer_release(info);
return 0; return 0;
} }
......
...@@ -84,6 +84,10 @@ struct simplefb_par { ...@@ -84,6 +84,10 @@ struct simplefb_par {
static void simplefb_clocks_destroy(struct simplefb_par *par); static void simplefb_clocks_destroy(struct simplefb_par *par);
static void simplefb_regulators_destroy(struct simplefb_par *par); static void simplefb_regulators_destroy(struct simplefb_par *par);
/*
* fb_ops.fb_destroy is called by the last put_fb_info() call at the end
* of unregister_framebuffer() or fb_release(). Do any cleanup here.
*/
static void simplefb_destroy(struct fb_info *info) static void simplefb_destroy(struct fb_info *info)
{ {
struct simplefb_par *par = info->par; struct simplefb_par *par = info->par;
...@@ -94,6 +98,8 @@ static void simplefb_destroy(struct fb_info *info) ...@@ -94,6 +98,8 @@ static void simplefb_destroy(struct fb_info *info)
if (info->screen_base) if (info->screen_base)
iounmap(info->screen_base); iounmap(info->screen_base);
framebuffer_release(info);
if (mem) if (mem)
release_mem_region(mem->start, resource_size(mem)); release_mem_region(mem->start, resource_size(mem));
} }
...@@ -545,8 +551,8 @@ static int simplefb_remove(struct platform_device *pdev) ...@@ -545,8 +551,8 @@ static int simplefb_remove(struct platform_device *pdev)
{ {
struct fb_info *info = platform_get_drvdata(pdev); struct fb_info *info = platform_get_drvdata(pdev);
/* simplefb_destroy takes care of info cleanup */
unregister_framebuffer(info); unregister_framebuffer(info);
framebuffer_release(info);
return 0; return 0;
} }
......
...@@ -179,6 +179,10 @@ static int vesafb_setcolreg(unsigned regno, unsigned red, unsigned green, ...@@ -179,6 +179,10 @@ static int vesafb_setcolreg(unsigned regno, unsigned red, unsigned green,
return err; return err;
} }
/*
* fb_ops.fb_destroy is called by the last put_fb_info() call at the end
* of unregister_framebuffer() or fb_release(). Do any cleanup here.
*/
static void vesafb_destroy(struct fb_info *info) static void vesafb_destroy(struct fb_info *info)
{ {
struct vesafb_par *par = info->par; struct vesafb_par *par = info->par;
...@@ -188,6 +192,8 @@ static void vesafb_destroy(struct fb_info *info) ...@@ -188,6 +192,8 @@ static void vesafb_destroy(struct fb_info *info)
if (info->screen_base) if (info->screen_base)
iounmap(info->screen_base); iounmap(info->screen_base);
release_mem_region(info->apertures->ranges[0].base, info->apertures->ranges[0].size); release_mem_region(info->apertures->ranges[0].base, info->apertures->ranges[0].size);
framebuffer_release(info);
} }
static struct fb_ops vesafb_ops = { static struct fb_ops vesafb_ops = {
...@@ -484,10 +490,10 @@ static int vesafb_remove(struct platform_device *pdev) ...@@ -484,10 +490,10 @@ static int vesafb_remove(struct platform_device *pdev)
{ {
struct fb_info *info = platform_get_drvdata(pdev); struct fb_info *info = platform_get_drvdata(pdev);
/* vesafb_destroy takes care of info cleanup */
unregister_framebuffer(info); unregister_framebuffer(info);
if (((struct vesafb_par *)(info->par))->region) if (((struct vesafb_par *)(info->par))->region)
release_region(0x3c0, 32); release_region(0x3c0, 32);
framebuffer_release(info);
return 0; return 0;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment