drm/radeon: Implement client-based fbdev emulation
Implement fbdev emulation on top of struct drm_client and its helpers. Replaces ad-hoc interfaces for restoring and closing fbdev emulation with per-client callbacks for hotplugging, restoring and unregistering. A single function, radeon_fbdev_setup(), starts fbdev emulation after the DRM device has been registered. Hence, fbdev acts like a regular DRM client. The setup call prepares the fbdev emulation and invokes connector hotplugging. The first successful hotplug event initializes fbdev emulation with a framebuffer, device file, etc. Unregistering depends on the hotplug status. Fully initialized emulation is cleaned up through drm_fb_helper_unregister_info() and fb_destroy. For prepared-only setups, unregistering unprepares the emulation and releases all resources. In both cases, fbdev emulation will be cleaned up. Reviewed-by: Alex Deucher <alexander.deucher@amd.com> Signed-off-by: Thomas Zimmermann <tzimmermann@suse.de> Signed-off-by: Alex Deucher <alexander.deucher@amd.com>
This commit is contained in:
parent
3a745f6ac1
commit
e317a69fe8
6 changed files with 92 additions and 69 deletions
|
@ -34,7 +34,6 @@
|
||||||
#include <drm/drm_device.h>
|
#include <drm/drm_device.h>
|
||||||
#include <drm/drm_drv.h>
|
#include <drm/drm_drv.h>
|
||||||
#include <drm/drm_edid.h>
|
#include <drm/drm_edid.h>
|
||||||
#include <drm/drm_fb_helper.h>
|
|
||||||
#include <drm/drm_fourcc.h>
|
#include <drm/drm_fourcc.h>
|
||||||
#include <drm/drm_framebuffer.h>
|
#include <drm/drm_framebuffer.h>
|
||||||
#include <drm/drm_gem_framebuffer_helper.h>
|
#include <drm/drm_gem_framebuffer_helper.h>
|
||||||
|
@ -1354,7 +1353,6 @@ radeon_user_framebuffer_create(struct drm_device *dev,
|
||||||
|
|
||||||
static const struct drm_mode_config_funcs radeon_mode_funcs = {
|
static const struct drm_mode_config_funcs radeon_mode_funcs = {
|
||||||
.fb_create = radeon_user_framebuffer_create,
|
.fb_create = radeon_user_framebuffer_create,
|
||||||
.output_poll_changed = drm_fb_helper_output_poll_changed,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static const struct drm_prop_enum_list radeon_tmds_pll_enum_list[] =
|
static const struct drm_prop_enum_list radeon_tmds_pll_enum_list[] =
|
||||||
|
@ -1642,7 +1640,6 @@ int radeon_modeset_init(struct radeon_device *rdev)
|
||||||
/* setup afmt */
|
/* setup afmt */
|
||||||
radeon_afmt_init(rdev);
|
radeon_afmt_init(rdev);
|
||||||
|
|
||||||
radeon_fbdev_init(rdev);
|
|
||||||
drm_kms_helper_poll_init(rdev->ddev);
|
drm_kms_helper_poll_init(rdev->ddev);
|
||||||
|
|
||||||
/* do pm late init */
|
/* do pm late init */
|
||||||
|
@ -1657,7 +1654,6 @@ void radeon_modeset_fini(struct radeon_device *rdev)
|
||||||
drm_kms_helper_poll_fini(rdev->ddev);
|
drm_kms_helper_poll_fini(rdev->ddev);
|
||||||
radeon_hpd_fini(rdev);
|
radeon_hpd_fini(rdev);
|
||||||
drm_helper_force_disable_all(rdev->ddev);
|
drm_helper_force_disable_all(rdev->ddev);
|
||||||
radeon_fbdev_fini(rdev);
|
|
||||||
radeon_afmt_fini(rdev);
|
radeon_afmt_fini(rdev);
|
||||||
drm_mode_config_cleanup(rdev->ddev);
|
drm_mode_config_cleanup(rdev->ddev);
|
||||||
rdev->mode_info.mode_config_initialized = false;
|
rdev->mode_info.mode_config_initialized = false;
|
||||||
|
|
|
@ -341,6 +341,8 @@ static int radeon_pci_probe(struct pci_dev *pdev,
|
||||||
if (ret)
|
if (ret)
|
||||||
goto err_agp;
|
goto err_agp;
|
||||||
|
|
||||||
|
radeon_fbdev_setup(dev->dev_private);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
err_agp:
|
err_agp:
|
||||||
|
@ -595,7 +597,6 @@ static const struct drm_driver kms_driver = {
|
||||||
.load = radeon_driver_load_kms,
|
.load = radeon_driver_load_kms,
|
||||||
.open = radeon_driver_open_kms,
|
.open = radeon_driver_open_kms,
|
||||||
.postclose = radeon_driver_postclose_kms,
|
.postclose = radeon_driver_postclose_kms,
|
||||||
.lastclose = radeon_driver_lastclose_kms,
|
|
||||||
.unload = radeon_driver_unload_kms,
|
.unload = radeon_driver_unload_kms,
|
||||||
.ioctls = radeon_ioctls_kms,
|
.ioctls = radeon_ioctls_kms,
|
||||||
.num_ioctls = ARRAY_SIZE(radeon_ioctls_kms),
|
.num_ioctls = ARRAY_SIZE(radeon_ioctls_kms),
|
||||||
|
|
|
@ -120,7 +120,6 @@ long radeon_drm_ioctl(struct file *filp,
|
||||||
|
|
||||||
int radeon_driver_load_kms(struct drm_device *dev, unsigned long flags);
|
int radeon_driver_load_kms(struct drm_device *dev, unsigned long flags);
|
||||||
void radeon_driver_unload_kms(struct drm_device *dev);
|
void radeon_driver_unload_kms(struct drm_device *dev);
|
||||||
void radeon_driver_lastclose_kms(struct drm_device *dev);
|
|
||||||
int radeon_driver_open_kms(struct drm_device *dev, struct drm_file *file_priv);
|
int radeon_driver_open_kms(struct drm_device *dev, struct drm_file *file_priv);
|
||||||
void radeon_driver_postclose_kms(struct drm_device *dev,
|
void radeon_driver_postclose_kms(struct drm_device *dev,
|
||||||
struct drm_file *file_priv);
|
struct drm_file *file_priv);
|
||||||
|
|
|
@ -24,19 +24,16 @@
|
||||||
* David Airlie
|
* David Airlie
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <linux/module.h>
|
|
||||||
#include <linux/pci.h>
|
#include <linux/pci.h>
|
||||||
#include <linux/pm_runtime.h>
|
#include <linux/pm_runtime.h>
|
||||||
#include <linux/slab.h>
|
|
||||||
#include <linux/vga_switcheroo.h>
|
#include <linux/vga_switcheroo.h>
|
||||||
|
|
||||||
#include <drm/drm_crtc.h>
|
|
||||||
#include <drm/drm_crtc_helper.h>
|
#include <drm/drm_crtc_helper.h>
|
||||||
|
#include <drm/drm_drv.h>
|
||||||
#include <drm/drm_fb_helper.h>
|
#include <drm/drm_fb_helper.h>
|
||||||
#include <drm/drm_fourcc.h>
|
#include <drm/drm_fourcc.h>
|
||||||
#include <drm/drm_framebuffer.h>
|
#include <drm/drm_framebuffer.h>
|
||||||
#include <drm/drm_gem_framebuffer_helper.h>
|
#include <drm/drm_gem_framebuffer_helper.h>
|
||||||
#include <drm/radeon_drm.h>
|
|
||||||
|
|
||||||
#include "radeon.h"
|
#include "radeon.h"
|
||||||
|
|
||||||
|
@ -179,14 +176,16 @@ static void radeon_fbdev_fb_destroy(struct fb_info *info)
|
||||||
struct drm_framebuffer *fb = fb_helper->fb;
|
struct drm_framebuffer *fb = fb_helper->fb;
|
||||||
struct drm_gem_object *gobj = drm_gem_fb_get_obj(fb, 0);
|
struct drm_gem_object *gobj = drm_gem_fb_get_obj(fb, 0);
|
||||||
|
|
||||||
|
drm_fb_helper_fini(fb_helper);
|
||||||
|
|
||||||
drm_framebuffer_unregister_private(fb);
|
drm_framebuffer_unregister_private(fb);
|
||||||
drm_framebuffer_cleanup(fb);
|
drm_framebuffer_cleanup(fb);
|
||||||
kfree(fb);
|
kfree(fb);
|
||||||
fb_helper->fb = NULL;
|
|
||||||
|
|
||||||
radeon_fbdev_destroy_pinned_object(gobj);
|
radeon_fbdev_destroy_pinned_object(gobj);
|
||||||
|
|
||||||
drm_fb_helper_fini(fb_helper);
|
drm_client_release(&fb_helper->client);
|
||||||
|
drm_fb_helper_unprepare(fb_helper);
|
||||||
|
kfree(fb_helper);
|
||||||
}
|
}
|
||||||
|
|
||||||
static const struct fb_ops radeon_fbdev_fb_ops = {
|
static const struct fb_ops radeon_fbdev_fb_ops = {
|
||||||
|
@ -279,7 +278,6 @@ static int radeon_fbdev_fb_helper_fb_probe(struct drm_fb_helper *fb_helper,
|
||||||
DRM_INFO("fb depth is %d\n", fb->format->depth);
|
DRM_INFO("fb depth is %d\n", fb->format->depth);
|
||||||
DRM_INFO(" pitch is %d\n", fb->pitches[0]);
|
DRM_INFO(" pitch is %d\n", fb->pitches[0]);
|
||||||
|
|
||||||
vga_switcheroo_client_fb_set(rdev->pdev, info);
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
err_drm_framebuffer_unregister_private:
|
err_drm_framebuffer_unregister_private:
|
||||||
|
@ -297,59 +295,107 @@ static const struct drm_fb_helper_funcs radeon_fbdev_fb_helper_funcs = {
|
||||||
.fb_probe = radeon_fbdev_fb_helper_fb_probe,
|
.fb_probe = radeon_fbdev_fb_helper_fb_probe,
|
||||||
};
|
};
|
||||||
|
|
||||||
int radeon_fbdev_init(struct radeon_device *rdev)
|
/*
|
||||||
|
* Fbdev client and struct drm_client_funcs
|
||||||
|
*/
|
||||||
|
|
||||||
|
static void radeon_fbdev_client_unregister(struct drm_client_dev *client)
|
||||||
|
{
|
||||||
|
struct drm_fb_helper *fb_helper = drm_fb_helper_from_client(client);
|
||||||
|
struct drm_device *dev = fb_helper->dev;
|
||||||
|
struct radeon_device *rdev = dev->dev_private;
|
||||||
|
|
||||||
|
if (fb_helper->info) {
|
||||||
|
vga_switcheroo_client_fb_set(rdev->pdev, NULL);
|
||||||
|
drm_fb_helper_unregister_info(fb_helper);
|
||||||
|
} else {
|
||||||
|
drm_client_release(&fb_helper->client);
|
||||||
|
drm_fb_helper_unprepare(fb_helper);
|
||||||
|
kfree(fb_helper);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int radeon_fbdev_client_restore(struct drm_client_dev *client)
|
||||||
|
{
|
||||||
|
drm_fb_helper_lastclose(client->dev);
|
||||||
|
vga_switcheroo_process_delayed_switch();
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int radeon_fbdev_client_hotplug(struct drm_client_dev *client)
|
||||||
|
{
|
||||||
|
struct drm_fb_helper *fb_helper = drm_fb_helper_from_client(client);
|
||||||
|
struct drm_device *dev = client->dev;
|
||||||
|
struct radeon_device *rdev = dev->dev_private;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
if (dev->fb_helper)
|
||||||
|
return drm_fb_helper_hotplug_event(dev->fb_helper);
|
||||||
|
|
||||||
|
ret = drm_fb_helper_init(dev, fb_helper);
|
||||||
|
if (ret)
|
||||||
|
goto err_drm_err;
|
||||||
|
|
||||||
|
if (!drm_drv_uses_atomic_modeset(dev))
|
||||||
|
drm_helper_disable_unused_functions(dev);
|
||||||
|
|
||||||
|
ret = drm_fb_helper_initial_config(fb_helper);
|
||||||
|
if (ret)
|
||||||
|
goto err_drm_fb_helper_fini;
|
||||||
|
|
||||||
|
vga_switcheroo_client_fb_set(rdev->pdev, fb_helper->info);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
err_drm_fb_helper_fini:
|
||||||
|
drm_fb_helper_fini(fb_helper);
|
||||||
|
err_drm_err:
|
||||||
|
drm_err(dev, "Failed to setup radeon fbdev emulation (ret=%d)\n", ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct drm_client_funcs radeon_fbdev_client_funcs = {
|
||||||
|
.owner = THIS_MODULE,
|
||||||
|
.unregister = radeon_fbdev_client_unregister,
|
||||||
|
.restore = radeon_fbdev_client_restore,
|
||||||
|
.hotplug = radeon_fbdev_client_hotplug,
|
||||||
|
};
|
||||||
|
|
||||||
|
void radeon_fbdev_setup(struct radeon_device *rdev)
|
||||||
{
|
{
|
||||||
struct drm_fb_helper *fb_helper;
|
struct drm_fb_helper *fb_helper;
|
||||||
int bpp_sel = 32;
|
int bpp_sel = 32;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
/* don't enable fbdev if no connectors */
|
if (rdev->mc.real_vram_size <= (8 * 1024 * 1024))
|
||||||
if (list_empty(&rdev->ddev->mode_config.connector_list))
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
/* select 8 bpp console on 8MB cards, or 16 bpp on RN50 or 32MB */
|
|
||||||
if (rdev->mc.real_vram_size <= (8*1024*1024))
|
|
||||||
bpp_sel = 8;
|
bpp_sel = 8;
|
||||||
else if (ASIC_IS_RN50(rdev) ||
|
else if (ASIC_IS_RN50(rdev) || rdev->mc.real_vram_size <= (32 * 1024 * 1024))
|
||||||
rdev->mc.real_vram_size <= (32*1024*1024))
|
|
||||||
bpp_sel = 16;
|
bpp_sel = 16;
|
||||||
|
|
||||||
fb_helper = kzalloc(sizeof(*fb_helper), GFP_KERNEL);
|
fb_helper = kzalloc(sizeof(*fb_helper), GFP_KERNEL);
|
||||||
if (!fb_helper)
|
if (!fb_helper)
|
||||||
return -ENOMEM;
|
return;
|
||||||
|
|
||||||
drm_fb_helper_prepare(rdev->ddev, fb_helper, bpp_sel, &radeon_fbdev_fb_helper_funcs);
|
drm_fb_helper_prepare(rdev->ddev, fb_helper, bpp_sel, &radeon_fbdev_fb_helper_funcs);
|
||||||
|
|
||||||
ret = drm_fb_helper_init(rdev->ddev, fb_helper);
|
ret = drm_client_init(rdev->ddev, &fb_helper->client, "radeon-fbdev",
|
||||||
|
&radeon_fbdev_client_funcs);
|
||||||
|
if (ret) {
|
||||||
|
drm_err(rdev->ddev, "Failed to register client: %d\n", ret);
|
||||||
|
goto err_drm_client_init;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = radeon_fbdev_client_hotplug(&fb_helper->client);
|
||||||
if (ret)
|
if (ret)
|
||||||
goto free;
|
drm_dbg_kms(rdev->ddev, "client hotplug ret=%d\n", ret);
|
||||||
|
|
||||||
/* disable all the possible outputs/crtcs before entering KMS mode */
|
drm_client_register(&fb_helper->client);
|
||||||
drm_helper_disable_unused_functions(rdev->ddev);
|
|
||||||
|
|
||||||
ret = drm_fb_helper_initial_config(fb_helper);
|
return;
|
||||||
if (ret)
|
|
||||||
goto fini;
|
|
||||||
|
|
||||||
return 0;
|
err_drm_client_init:
|
||||||
|
|
||||||
fini:
|
|
||||||
drm_fb_helper_fini(fb_helper);
|
|
||||||
free:
|
|
||||||
drm_fb_helper_unprepare(fb_helper);
|
drm_fb_helper_unprepare(fb_helper);
|
||||||
kfree(fb_helper);
|
kfree(fb_helper);
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
void radeon_fbdev_fini(struct radeon_device *rdev)
|
|
||||||
{
|
|
||||||
if (!rdev->ddev->fb_helper)
|
|
||||||
return;
|
|
||||||
|
|
||||||
drm_fb_helper_unregister_info(rdev->ddev->fb_helper);
|
|
||||||
drm_fb_helper_unprepare(rdev->ddev->fb_helper);
|
|
||||||
kfree(rdev->ddev->fb_helper);
|
|
||||||
rdev->ddev->fb_helper = NULL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void radeon_fbdev_set_suspend(struct radeon_device *rdev, int state)
|
void radeon_fbdev_set_suspend(struct radeon_device *rdev, int state)
|
||||||
|
|
|
@ -32,7 +32,6 @@
|
||||||
#include <linux/uaccess.h>
|
#include <linux/uaccess.h>
|
||||||
#include <linux/vga_switcheroo.h>
|
#include <linux/vga_switcheroo.h>
|
||||||
|
|
||||||
#include <drm/drm_fb_helper.h>
|
|
||||||
#include <drm/drm_file.h>
|
#include <drm/drm_file.h>
|
||||||
#include <drm/drm_ioctl.h>
|
#include <drm/drm_ioctl.h>
|
||||||
#include <drm/radeon_drm.h>
|
#include <drm/radeon_drm.h>
|
||||||
|
@ -622,23 +621,6 @@ int radeon_info_ioctl(struct drm_device *dev, void *data, struct drm_file *filp)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Outdated mess for old drm with Xorg being in charge (void function now).
|
|
||||||
*/
|
|
||||||
/**
|
|
||||||
* radeon_driver_lastclose_kms - drm callback for last close
|
|
||||||
*
|
|
||||||
* @dev: drm dev pointer
|
|
||||||
*
|
|
||||||
* Switch vga_switcheroo state after last close (all asics).
|
|
||||||
*/
|
|
||||||
void radeon_driver_lastclose_kms(struct drm_device *dev)
|
|
||||||
{
|
|
||||||
drm_fb_helper_lastclose(dev);
|
|
||||||
vga_switcheroo_process_delayed_switch();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* radeon_driver_open_kms - drm callback for open
|
* radeon_driver_open_kms - drm callback for open
|
||||||
*
|
*
|
||||||
|
|
|
@ -939,8 +939,7 @@ void dce4_program_fmt(struct drm_encoder *encoder);
|
||||||
void dce8_program_fmt(struct drm_encoder *encoder);
|
void dce8_program_fmt(struct drm_encoder *encoder);
|
||||||
|
|
||||||
/* fbdev layer */
|
/* fbdev layer */
|
||||||
int radeon_fbdev_init(struct radeon_device *rdev);
|
void radeon_fbdev_setup(struct radeon_device *rdev);
|
||||||
void radeon_fbdev_fini(struct radeon_device *rdev);
|
|
||||||
void radeon_fbdev_set_suspend(struct radeon_device *rdev, int state);
|
void radeon_fbdev_set_suspend(struct radeon_device *rdev, int state);
|
||||||
bool radeon_fbdev_robj_is_fb(struct radeon_device *rdev, struct radeon_bo *robj);
|
bool radeon_fbdev_robj_is_fb(struct radeon_device *rdev, struct radeon_bo *robj);
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue