summaryrefslogtreecommitdiff
path: root/drivers/gpu/drm/drm_fb_helper.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/gpu/drm/drm_fb_helper.c')
-rw-r--r--drivers/gpu/drm/drm_fb_helper.c322
1 files changed, 93 insertions, 229 deletions
diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c
index 61a5d450cc20..4a7f72044ab8 100644
--- a/drivers/gpu/drm/drm_fb_helper.c
+++ b/drivers/gpu/drm/drm_fb_helper.c
@@ -30,9 +30,7 @@
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/console.h>
-#include <linux/pci.h>
-#include <linux/sysrq.h>
-#include <linux/vga_switcheroo.h>
+#include <linux/export.h>
#include <drm/drm_atomic.h>
#include <drm/drm_drv.h>
@@ -44,6 +42,7 @@
#include <drm/drm_vblank.h>
#include "drm_internal.h"
+#include "drm_crtc_internal.h"
static bool drm_fbdev_emulation = true;
module_param_named(fbdev_emulation, drm_fbdev_emulation, bool, 0600);
@@ -85,20 +84,8 @@ static DEFINE_MUTEX(kernel_fb_helper_lock);
* The fb helper functions are useful to provide an fbdev on top of a drm kernel
* mode setting driver. They can be used mostly independently from the crtc
* helper functions used by many drivers to implement the kernel mode setting
- * interfaces.
- *
- * Drivers that support a dumb buffer with a virtual address and mmap support,
- * should try out the generic fbdev emulation using drm_fbdev_generic_setup().
- * It will automatically set up deferred I/O if the driver requires a shadow
- * buffer.
- *
- * Existing fbdev implementations should restore the fbdev console by using
- * drm_fb_helper_lastclose() as their &drm_driver.lastclose callback.
- * They should also notify the fb helper code from updates to the output
- * configuration by using drm_fb_helper_output_poll_changed() as their
- * &drm_mode_config_funcs.output_poll_changed callback. New implementations
- * of fbdev should be build on top of struct &drm_client_funcs, which handles
- * this automatically. Setting the old callbacks should be avoided.
+ * interfaces. Drivers that use one of the shared memory managers, TTM, SHMEM,
+ * DMA, should instead use the corresponding fbdev emulation.
*
* For suspend/resume consider using drm_mode_config_helper_suspend() and
* drm_mode_config_helper_resume() which takes care of fbdev as well.
@@ -126,9 +113,6 @@ static DEFINE_MUTEX(kernel_fb_helper_lock);
* atomic context. If drm_fb_helper_deferred_io() is used as the deferred_io
* callback it will also schedule dirty_work with the damage collected from the
* mmap page writes.
- *
- * Deferred I/O is not compatible with SHMEM. Such drivers should request an
- * fbdev shadow buffer and call drm_fbdev_generic_setup() instead.
*/
static void drm_fb_helper_restore_lut_atomic(struct drm_crtc *crtc)
@@ -259,62 +243,30 @@ __drm_fb_helper_restore_fbdev_mode_unlocked(struct drm_fb_helper *fb_helper,
if (do_delayed)
drm_fb_helper_hotplug_event(fb_helper);
+ if (fb_helper->funcs->fb_restore)
+ fb_helper->funcs->fb_restore(fb_helper);
+
return ret;
}
/**
* drm_fb_helper_restore_fbdev_mode_unlocked - restore fbdev configuration
* @fb_helper: driver-allocated fbdev helper, can be NULL
+ * @force: ignore present DRM master
*
- * This should be called from driver's drm &drm_driver.lastclose callback
- * when implementing an fbcon on top of kms using this helper. This ensures that
- * the user isn't greeted with a black screen when e.g. X dies.
+ * This helper should be called from fbdev emulation's &drm_client_funcs.restore
+ * callback. It ensures that the user isn't greeted with a black screen when the
+ * userspace compositor releases the display device.
*
- * RETURNS:
- * Zero if everything went ok, negative error code otherwise.
+ * Returns:
+ * 0 on success, or a negative errno code otherwise.
*/
-int drm_fb_helper_restore_fbdev_mode_unlocked(struct drm_fb_helper *fb_helper)
+int drm_fb_helper_restore_fbdev_mode_unlocked(struct drm_fb_helper *fb_helper, bool force)
{
- return __drm_fb_helper_restore_fbdev_mode_unlocked(fb_helper, false);
+ return __drm_fb_helper_restore_fbdev_mode_unlocked(fb_helper, force);
}
EXPORT_SYMBOL(drm_fb_helper_restore_fbdev_mode_unlocked);
-#ifdef CONFIG_MAGIC_SYSRQ
-/* emergency restore, don't bother with error reporting */
-static void drm_fb_helper_restore_work_fn(struct work_struct *ignored)
-{
- struct drm_fb_helper *helper;
-
- mutex_lock(&kernel_fb_helper_lock);
- list_for_each_entry(helper, &kernel_fb_helper_list, kernel_fb_list) {
- struct drm_device *dev = helper->dev;
-
- if (dev->switch_power_state == DRM_SWITCH_POWER_OFF)
- continue;
-
- mutex_lock(&helper->lock);
- drm_client_modeset_commit_locked(&helper->client);
- mutex_unlock(&helper->lock);
- }
- mutex_unlock(&kernel_fb_helper_lock);
-}
-
-static DECLARE_WORK(drm_fb_helper_restore_work, drm_fb_helper_restore_work_fn);
-
-static void drm_fb_helper_sysrq(int dummy1)
-{
- schedule_work(&drm_fb_helper_restore_work);
-}
-
-static const struct sysrq_key_op sysrq_drm_fb_helper_restore_op = {
- .handler = drm_fb_helper_sysrq,
- .help_msg = "force-fb(v)",
- .action_msg = "Restore framebuffer console",
-};
-#else
-static const struct sysrq_key_op sysrq_drm_fb_helper_restore_op = { };
-#endif
-
static void drm_fb_helper_dpms(struct fb_info *info, int dpms_mode)
{
struct drm_fb_helper *fb_helper = info->par;
@@ -378,6 +330,10 @@ static void drm_fb_helper_fb_dirty(struct drm_fb_helper *helper)
unsigned long flags;
int ret;
+ mutex_lock(&helper->lock);
+ drm_client_modeset_wait_for_vblank(&helper->client, 0);
+ mutex_unlock(&helper->lock);
+
if (drm_WARN_ON_ONCE(dev, !helper->funcs->fb_dirty))
return;
@@ -501,20 +457,7 @@ int drm_fb_helper_init(struct drm_device *dev,
}
EXPORT_SYMBOL(drm_fb_helper_init);
-/**
- * drm_fb_helper_alloc_info - allocate fb_info and some of its members
- * @fb_helper: driver-allocated fbdev helper
- *
- * A helper to alloc fb_info and the member cmap. Called by the driver
- * within the fb_probe fb_helper callback function. Drivers do not
- * need to release the allocated fb_info structure themselves, this is
- * automatically done when calling drm_fb_helper_fini().
- *
- * RETURNS:
- * fb_info pointer if things went okay, pointer containing error code
- * otherwise
- */
-struct fb_info *drm_fb_helper_alloc_info(struct drm_fb_helper *fb_helper)
+static struct fb_info *drm_fb_helper_alloc_info(struct drm_fb_helper *fb_helper)
{
struct device *dev = fb_helper->dev->dev;
struct fb_info *info;
@@ -524,6 +467,9 @@ struct fb_info *drm_fb_helper_alloc_info(struct drm_fb_helper *fb_helper)
if (!info)
return ERR_PTR(-ENOMEM);
+ if (!drm_leak_fbdev_smem)
+ info->flags |= FBINFO_HIDE_SMEM_START;
+
ret = fb_alloc_cmap(&info->cmap, 256, 0);
if (ret)
goto err_release;
@@ -531,23 +477,15 @@ struct fb_info *drm_fb_helper_alloc_info(struct drm_fb_helper *fb_helper)
fb_helper->info = info;
info->skip_vt_switch = true;
+ info->skip_panic = drm_panic_is_enabled(fb_helper->dev);
return info;
err_release:
framebuffer_release(info);
return ERR_PTR(ret);
}
-EXPORT_SYMBOL(drm_fb_helper_alloc_info);
-/**
- * drm_fb_helper_release_info - release fb_info and its members
- * @fb_helper: driver-allocated fbdev helper
- *
- * A helper to release fb_info and the member cmap. Drivers do not
- * need to release the allocated fb_info structure themselves, this is
- * automatically done when calling drm_fb_helper_fini().
- */
-void drm_fb_helper_release_info(struct drm_fb_helper *fb_helper)
+static void drm_fb_helper_release_info(struct drm_fb_helper *fb_helper)
{
struct fb_info *info = fb_helper->info;
@@ -560,11 +498,10 @@ void drm_fb_helper_release_info(struct drm_fb_helper *fb_helper)
fb_dealloc_cmap(&info->cmap);
framebuffer_release(info);
}
-EXPORT_SYMBOL(drm_fb_helper_release_info);
/**
* drm_fb_helper_unregister_info - unregister fb_info framebuffer device
- * @fb_helper: driver-allocated fbdev helper, can be NULL
+ * @fb_helper: driver-allocated fbdev helper, must not be NULL
*
* A wrapper around unregister_framebuffer, to release the fb_info
* framebuffer device. This must be called before releasing all resources for
@@ -572,8 +509,7 @@ EXPORT_SYMBOL(drm_fb_helper_release_info);
*/
void drm_fb_helper_unregister_info(struct drm_fb_helper *fb_helper)
{
- if (fb_helper && fb_helper->info)
- unregister_framebuffer(fb_helper->info);
+ unregister_framebuffer(fb_helper->info);
}
EXPORT_SYMBOL(drm_fb_helper_unregister_info);
@@ -599,11 +535,8 @@ void drm_fb_helper_fini(struct drm_fb_helper *fb_helper)
drm_fb_helper_release_info(fb_helper);
mutex_lock(&kernel_fb_helper_lock);
- if (!list_empty(&fb_helper->kernel_fb_list)) {
+ if (!list_empty(&fb_helper->kernel_fb_list))
list_del(&fb_helper->kernel_fb_list);
- if (list_empty(&kernel_fb_helper_list))
- unregister_sysrq_key('v', &sysrq_drm_fb_helper_restore_op);
- }
mutex_unlock(&kernel_fb_helper_lock);
if (!fb_helper->client.funcs)
@@ -628,6 +561,17 @@ static void drm_fb_helper_add_damage_clip(struct drm_fb_helper *helper, u32 x, u
static void drm_fb_helper_damage(struct drm_fb_helper *helper, u32 x, u32 y,
u32 width, u32 height)
{
+ /*
+ * This function may be invoked by panic() to flush the frame
+ * buffer, where all CPUs except the panic CPU are stopped.
+ * During the following schedule_work(), the panic CPU needs
+ * the worker_pool lock, which might be held by a stopped CPU,
+ * causing schedule_work() and panic() to block. Return early on
+ * oops_in_progress to prevent this blocking.
+ */
+ if (oops_in_progress)
+ return;
+
drm_fb_helper_add_damage_clip(helper, x, y, width, height);
schedule_work(&helper->damage_work);
@@ -692,6 +636,7 @@ void drm_fb_helper_damage_area(struct fb_info *info, u32 x, u32 y, u32 width, u3
}
EXPORT_SYMBOL(drm_fb_helper_damage_area);
+#ifdef CONFIG_FB_DEFERRED_IO
/**
* drm_fb_helper_deferred_io() - fbdev deferred_io callback function
* @info: fb_info struct pointer
@@ -735,6 +680,7 @@ void drm_fb_helper_deferred_io(struct fb_info *info, struct list_head *pagerefli
}
}
EXPORT_SYMBOL(drm_fb_helper_deferred_io);
+#endif
/**
* drm_fb_helper_set_suspend - wrapper around fb_set_suspend
@@ -747,7 +693,12 @@ EXPORT_SYMBOL(drm_fb_helper_deferred_io);
*/
void drm_fb_helper_set_suspend(struct drm_fb_helper *fb_helper, bool suspend)
{
- if (fb_helper && fb_helper->info)
+ if (!fb_helper || !fb_helper->info)
+ return;
+
+ if (fb_helper->funcs->fb_set_suspend)
+ fb_helper->funcs->fb_set_suspend(fb_helper, suspend);
+ else
fb_set_suspend(fb_helper->info, suspend);
}
EXPORT_SYMBOL(drm_fb_helper_set_suspend);
@@ -793,7 +744,7 @@ void drm_fb_helper_set_suspend_unlocked(struct drm_fb_helper *fb_helper,
}
}
- fb_set_suspend(fb_helper->info, suspend);
+ drm_fb_helper_set_suspend(fb_helper, suspend);
console_unlock();
}
EXPORT_SYMBOL(drm_fb_helper_set_suspend_unlocked);
@@ -1052,15 +1003,9 @@ int drm_fb_helper_ioctl(struct fb_info *info, unsigned int cmd,
unsigned long arg)
{
struct drm_fb_helper *fb_helper = info->par;
- struct drm_device *dev = fb_helper->dev;
- struct drm_crtc *crtc;
int ret = 0;
- mutex_lock(&fb_helper->lock);
- if (!drm_master_internal_acquire(dev)) {
- ret = -EBUSY;
- goto unlock;
- }
+ guard(mutex)(&fb_helper->lock);
switch (cmd) {
case FBIO_WAITFORVSYNC:
@@ -1080,28 +1025,12 @@ int drm_fb_helper_ioctl(struct fb_info *info, unsigned int cmd,
* make. If we're not smart enough here, one should
* just consider switch the userspace to KMS.
*/
- crtc = fb_helper->client.modesets[0].crtc;
-
- /*
- * Only wait for a vblank event if the CRTC is
- * enabled, otherwise just don't do anythintg,
- * not even report an error.
- */
- ret = drm_crtc_vblank_get(crtc);
- if (!ret) {
- drm_crtc_wait_one_vblank(crtc);
- drm_crtc_vblank_put(crtc);
- }
-
- ret = 0;
+ ret = drm_client_modeset_wait_for_vblank(&fb_helper->client, 0);
break;
default:
ret = -ENOTTY;
}
- drm_master_internal_release(dev);
-unlock:
- mutex_unlock(&fb_helper->lock);
return ret;
}
EXPORT_SYMBOL(drm_fb_helper_ioctl);
@@ -1330,9 +1259,9 @@ int drm_fb_helper_set_par(struct fb_info *info)
* the KDSET IOCTL with KD_TEXT, and only after that drops the master
* status when exiting.
*
- * In the past this was caught by drm_fb_helper_lastclose(), but on
- * modern systems where logind always keeps a drm fd open to orchestrate
- * the vt switching, this doesn't work.
+ * In the past this was caught by drm_fb_helper_restore_fbdev_mode_unlocked(),
+ * but on modern systems where logind always keeps a drm fd open to
+ * orchestrate the vt switching, this doesn't work.
*
* To not break the userspace ABI we have this special case here, which
* is only used for the above case. Everything else uses the normal
@@ -1347,14 +1276,14 @@ int drm_fb_helper_set_par(struct fb_info *info)
}
EXPORT_SYMBOL(drm_fb_helper_set_par);
-static void pan_set(struct drm_fb_helper *fb_helper, int x, int y)
+static void pan_set(struct drm_fb_helper *fb_helper, int dx, int dy)
{
struct drm_mode_set *mode_set;
mutex_lock(&fb_helper->client.modeset_mutex);
drm_client_for_each_modeset(mode_set, &fb_helper->client) {
- mode_set->x = x;
- mode_set->y = y;
+ mode_set->x += dx;
+ mode_set->y += dy;
}
mutex_unlock(&fb_helper->client.modeset_mutex);
}
@@ -1363,16 +1292,18 @@ static int pan_display_atomic(struct fb_var_screeninfo *var,
struct fb_info *info)
{
struct drm_fb_helper *fb_helper = info->par;
- int ret;
+ int ret, dx, dy;
- pan_set(fb_helper, var->xoffset, var->yoffset);
+ dx = var->xoffset - info->var.xoffset;
+ dy = var->yoffset - info->var.yoffset;
+ pan_set(fb_helper, dx, dy);
ret = drm_client_modeset_commit_locked(&fb_helper->client);
if (!ret) {
info->var.xoffset = var->xoffset;
info->var.yoffset = var->yoffset;
} else
- pan_set(fb_helper, info->var.xoffset, info->var.yoffset);
+ pan_set(fb_helper, -dx, -dy);
return ret;
}
@@ -1440,67 +1371,27 @@ unlock:
EXPORT_SYMBOL(drm_fb_helper_pan_display);
static uint32_t drm_fb_helper_find_format(struct drm_fb_helper *fb_helper, const uint32_t *formats,
- size_t format_count, uint32_t bpp, uint32_t depth)
+ size_t format_count, unsigned int color_mode)
{
struct drm_device *dev = fb_helper->dev;
uint32_t format;
size_t i;
- /*
- * Do not consider YUV or other complicated formats
- * for framebuffers. This means only legacy formats
- * are supported (fmt->depth is a legacy field), but
- * the framebuffer emulation can only deal with such
- * formats, specifically RGB/BGA formats.
- */
- format = drm_mode_legacy_fb_format(bpp, depth);
- if (!format)
- goto err;
+ format = drm_driver_color_mode_format(dev, color_mode);
+ if (!format) {
+ drm_info(dev, "unsupported color mode of %d\n", color_mode);
+ return DRM_FORMAT_INVALID;
+ }
for (i = 0; i < format_count; ++i) {
if (formats[i] == format)
return format;
}
-
-err:
- /* We found nothing. */
- drm_warn(dev, "bpp/depth value of %u/%u not supported\n", bpp, depth);
+ drm_warn(dev, "format %p4cc not supported\n", &format);
return DRM_FORMAT_INVALID;
}
-static uint32_t drm_fb_helper_find_color_mode_format(struct drm_fb_helper *fb_helper,
- const uint32_t *formats, size_t format_count,
- unsigned int color_mode)
-{
- struct drm_device *dev = fb_helper->dev;
- uint32_t bpp, depth;
-
- switch (color_mode) {
- case 1:
- case 2:
- case 4:
- case 8:
- case 16:
- case 24:
- bpp = depth = color_mode;
- break;
- case 15:
- bpp = 16;
- depth = 15;
- break;
- case 32:
- bpp = 32;
- depth = 24;
- break;
- default:
- drm_info(dev, "unsupported color mode of %d\n", color_mode);
- return DRM_FORMAT_INVALID;
- }
-
- return drm_fb_helper_find_format(fb_helper, formats, format_count, bpp, depth);
-}
-
static int __drm_fb_helper_find_sizes(struct drm_fb_helper *fb_helper,
struct drm_fb_helper_surface_size *sizes)
{
@@ -1530,10 +1421,10 @@ static int __drm_fb_helper_find_sizes(struct drm_fb_helper *fb_helper,
if (!cmdline_mode->bpp_specified)
continue;
- surface_format = drm_fb_helper_find_color_mode_format(fb_helper,
- plane->format_types,
- plane->format_count,
- cmdline_mode->bpp);
+ surface_format = drm_fb_helper_find_format(fb_helper,
+ plane->format_types,
+ plane->format_count,
+ cmdline_mode->bpp);
if (surface_format != DRM_FORMAT_INVALID)
break; /* found supported format */
}
@@ -1543,10 +1434,10 @@ static int __drm_fb_helper_find_sizes(struct drm_fb_helper *fb_helper,
break; /* found supported format */
/* try preferred color mode */
- surface_format = drm_fb_helper_find_color_mode_format(fb_helper,
- plane->format_types,
- plane->format_count,
- fb_helper->preferred_bpp);
+ surface_format = drm_fb_helper_find_format(fb_helper,
+ plane->format_types,
+ plane->format_count,
+ fb_helper->preferred_bpp);
if (surface_format != DRM_FORMAT_INVALID)
break; /* found supported format */
}
@@ -1647,7 +1538,7 @@ static int drm_fb_helper_find_sizes(struct drm_fb_helper *fb_helper,
/*
* Allocates the backing storage and sets up the fbdev info structure through
- * the ->fb_probe callback.
+ * the ->fbdev_probe callback.
*/
static int drm_fb_helper_single_fb_probe(struct drm_fb_helper *fb_helper)
{
@@ -1656,6 +1547,9 @@ static int drm_fb_helper_single_fb_probe(struct drm_fb_helper *fb_helper)
struct drm_fb_helper_surface_size sizes;
int ret;
+ if (drm_WARN_ON(dev, !dev->driver->fbdev_probe))
+ return -EINVAL;
+
ret = drm_fb_helper_find_sizes(fb_helper, &sizes);
if (ret) {
/* First time: disable all crtc's.. */
@@ -1665,16 +1559,12 @@ static int drm_fb_helper_single_fb_probe(struct drm_fb_helper *fb_helper)
}
/* push down into drivers */
- ret = (*fb_helper->funcs->fb_probe)(fb_helper, &sizes);
+ ret = dev->driver->fbdev_probe(fb_helper, &sizes);
if (ret < 0)
return ret;
strcpy(fb_helper->fb->comm, "[fbcon]");
- /* Set the fb info for vgaswitcheroo clients. Does nothing otherwise. */
- if (dev_is_pci(dev->dev))
- vga_switcheroo_client_fb_set(to_pci_dev(dev->dev), fb_helper->info);
-
return 0;
}
@@ -1737,7 +1627,7 @@ static void drm_fb_helper_fill_var(struct fb_info *info,
* instance and the drm framebuffer allocated in &drm_fb_helper.fb.
*
* Drivers should call this (or their equivalent setup code) from their
- * &drm_fb_helper_funcs.fb_probe callback after having allocated the fbdev
+ * &drm_driver.fbdev_probe callback after having allocated the fbdev
* backing storage framebuffer.
*/
void drm_fb_helper_fill_info(struct fb_info *info,
@@ -1843,6 +1733,11 @@ __drm_fb_helper_initial_config_and_unlock(struct drm_fb_helper *fb_helper)
height = dev->mode_config.max_height;
drm_client_modeset_probe(&fb_helper->client, width, height);
+
+ info = drm_fb_helper_alloc_info(fb_helper);
+ if (IS_ERR(info))
+ return PTR_ERR(info);
+
ret = drm_fb_helper_single_fb_probe(fb_helper);
if (ret < 0) {
if (ret == -EAGAIN) {
@@ -1851,18 +1746,14 @@ __drm_fb_helper_initial_config_and_unlock(struct drm_fb_helper *fb_helper)
}
mutex_unlock(&fb_helper->lock);
- return ret;
+ goto err_drm_fb_helper_release_info;
}
drm_setup_crtcs_fb(fb_helper);
fb_helper->deferred_setup = false;
- info = fb_helper->info;
info->var.pixclock = 0;
- if (!drm_leak_fbdev_smem)
- info->flags |= FBINFO_HIDE_SMEM_START;
-
/* Need to drop locks to avoid recursive deadlock in
* register_framebuffer. This is ok because the only thing left to do is
* register the fbdev emulation instance in kernel_fb_helper_list. */
@@ -1876,13 +1767,14 @@ __drm_fb_helper_initial_config_and_unlock(struct drm_fb_helper *fb_helper)
info->node, info->fix.id);
mutex_lock(&kernel_fb_helper_lock);
- if (list_empty(&kernel_fb_helper_list))
- register_sysrq_key('v', &sysrq_drm_fb_helper_restore_op);
-
list_add(&fb_helper->kernel_fb_list, &kernel_fb_helper_list);
mutex_unlock(&kernel_fb_helper_lock);
return 0;
+
+err_drm_fb_helper_release_info:
+ drm_fb_helper_release_info(fb_helper);
+ return ret;
}
/**
@@ -1896,7 +1788,7 @@ __drm_fb_helper_initial_config_and_unlock(struct drm_fb_helper *fb_helper)
* Note that this also registers the fbdev and so allows userspace to call into
* the driver through the fbdev interfaces.
*
- * This function will call down into the &drm_fb_helper_funcs.fb_probe callback
+ * This function will call down into the &drm_driver.fbdev_probe callback
* to let the driver allocate and initialize the fbdev info structure and the
* drm framebuffer used to back the fbdev. drm_fb_helper_fill_info() is provided
* as a helper to setup simple default values for the fbdev info structure.
@@ -1992,31 +1884,3 @@ int drm_fb_helper_hotplug_event(struct drm_fb_helper *fb_helper)
return 0;
}
EXPORT_SYMBOL(drm_fb_helper_hotplug_event);
-
-/**
- * drm_fb_helper_lastclose - DRM driver lastclose helper for fbdev emulation
- * @dev: DRM device
- *
- * This function can be used as the &drm_driver->lastclose callback for drivers
- * that only need to call drm_fb_helper_restore_fbdev_mode_unlocked().
- */
-void drm_fb_helper_lastclose(struct drm_device *dev)
-{
- drm_fb_helper_restore_fbdev_mode_unlocked(dev->fb_helper);
-}
-EXPORT_SYMBOL(drm_fb_helper_lastclose);
-
-/**
- * drm_fb_helper_output_poll_changed - DRM mode config \.output_poll_changed
- * helper for fbdev emulation
- * @dev: DRM device
- *
- * This function can be used as the
- * &drm_mode_config_funcs.output_poll_changed callback for drivers that only
- * need to call drm_fbdev.hotplug_event().
- */
-void drm_fb_helper_output_poll_changed(struct drm_device *dev)
-{
- drm_fb_helper_hotplug_event(dev->fb_helper);
-}
-EXPORT_SYMBOL(drm_fb_helper_output_poll_changed);