mirror of
				https://github.com/torvalds/linux.git
				synced 2025-10-31 16:48:26 +02:00 
			
		
		
		
	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_drv.h> | ||||
| #include <drm/drm_edid.h> | ||||
| #include <drm/drm_fb_helper.h> | ||||
| #include <drm/drm_fourcc.h> | ||||
| #include <drm/drm_framebuffer.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 = { | ||||
| 	.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[] = | ||||
|  | @ -1642,7 +1640,6 @@ int radeon_modeset_init(struct radeon_device *rdev) | |||
| 	/* setup afmt */ | ||||
| 	radeon_afmt_init(rdev); | ||||
| 
 | ||||
| 	radeon_fbdev_init(rdev); | ||||
| 	drm_kms_helper_poll_init(rdev->ddev); | ||||
| 
 | ||||
| 	/* do pm late init */ | ||||
|  | @ -1657,7 +1654,6 @@ void radeon_modeset_fini(struct radeon_device *rdev) | |||
| 		drm_kms_helper_poll_fini(rdev->ddev); | ||||
| 		radeon_hpd_fini(rdev); | ||||
| 		drm_helper_force_disable_all(rdev->ddev); | ||||
| 		radeon_fbdev_fini(rdev); | ||||
| 		radeon_afmt_fini(rdev); | ||||
| 		drm_mode_config_cleanup(rdev->ddev); | ||||
| 		rdev->mode_info.mode_config_initialized = false; | ||||
|  |  | |||
|  | @ -341,6 +341,8 @@ static int radeon_pci_probe(struct pci_dev *pdev, | |||
| 	if (ret) | ||||
| 		goto err_agp; | ||||
| 
 | ||||
| 	radeon_fbdev_setup(dev->dev_private); | ||||
| 
 | ||||
| 	return 0; | ||||
| 
 | ||||
| err_agp: | ||||
|  | @ -595,7 +597,6 @@ static const struct drm_driver kms_driver = { | |||
| 	.load = radeon_driver_load_kms, | ||||
| 	.open = radeon_driver_open_kms, | ||||
| 	.postclose = radeon_driver_postclose_kms, | ||||
| 	.lastclose = radeon_driver_lastclose_kms, | ||||
| 	.unload = radeon_driver_unload_kms, | ||||
| 	.ioctls = 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); | ||||
| 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); | ||||
| void radeon_driver_postclose_kms(struct drm_device *dev, | ||||
| 				 struct drm_file *file_priv); | ||||
|  |  | |||
|  | @ -24,19 +24,16 @@ | |||
|  *     David Airlie | ||||
|  */ | ||||
| 
 | ||||
| #include <linux/module.h> | ||||
| #include <linux/pci.h> | ||||
| #include <linux/pm_runtime.h> | ||||
| #include <linux/slab.h> | ||||
| #include <linux/vga_switcheroo.h> | ||||
| 
 | ||||
| #include <drm/drm_crtc.h> | ||||
| #include <drm/drm_crtc_helper.h> | ||||
| #include <drm/drm_drv.h> | ||||
| #include <drm/drm_fb_helper.h> | ||||
| #include <drm/drm_fourcc.h> | ||||
| #include <drm/drm_framebuffer.h> | ||||
| #include <drm/drm_gem_framebuffer_helper.h> | ||||
| #include <drm/radeon_drm.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_gem_object *gobj = drm_gem_fb_get_obj(fb, 0); | ||||
| 
 | ||||
| 	drm_fb_helper_fini(fb_helper); | ||||
| 
 | ||||
| 	drm_framebuffer_unregister_private(fb); | ||||
| 	drm_framebuffer_cleanup(fb); | ||||
| 	kfree(fb); | ||||
| 	fb_helper->fb = NULL; | ||||
| 
 | ||||
| 	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 = { | ||||
|  | @ -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("   pitch is %d\n", fb->pitches[0]); | ||||
| 
 | ||||
| 	vga_switcheroo_client_fb_set(rdev->pdev, info); | ||||
| 	return 0; | ||||
| 
 | ||||
| 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, | ||||
| }; | ||||
| 
 | ||||
| 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; | ||||
| 	int bpp_sel = 32; | ||||
| 	int ret; | ||||
| 
 | ||||
| 	/* don't enable fbdev if no connectors */ | ||||
| 	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)) | ||||
| 	if (rdev->mc.real_vram_size <= (8 * 1024 * 1024)) | ||||
| 		bpp_sel = 8; | ||||
| 	else if (ASIC_IS_RN50(rdev) || | ||||
| 		 rdev->mc.real_vram_size <= (32*1024*1024)) | ||||
| 	else if (ASIC_IS_RN50(rdev) || rdev->mc.real_vram_size <= (32 * 1024 * 1024)) | ||||
| 		bpp_sel = 16; | ||||
| 
 | ||||
| 	fb_helper = kzalloc(sizeof(*fb_helper), GFP_KERNEL); | ||||
| 	if (!fb_helper) | ||||
| 		return -ENOMEM; | ||||
| 
 | ||||
| 		return; | ||||
| 	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) | ||||
| 		goto free; | ||||
| 		drm_dbg_kms(rdev->ddev, "client hotplug ret=%d\n", ret); | ||||
| 
 | ||||
| 	/* disable all the possible outputs/crtcs before entering KMS mode */ | ||||
| 	drm_helper_disable_unused_functions(rdev->ddev); | ||||
| 	drm_client_register(&fb_helper->client); | ||||
| 
 | ||||
| 	ret = drm_fb_helper_initial_config(fb_helper); | ||||
| 	if (ret) | ||||
| 		goto fini; | ||||
| 	return; | ||||
| 
 | ||||
| 	return 0; | ||||
| 
 | ||||
| fini: | ||||
| 	drm_fb_helper_fini(fb_helper); | ||||
| free: | ||||
| err_drm_client_init: | ||||
| 	drm_fb_helper_unprepare(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) | ||||
|  |  | |||
|  | @ -32,7 +32,6 @@ | |||
| #include <linux/uaccess.h> | ||||
| #include <linux/vga_switcheroo.h> | ||||
| 
 | ||||
| #include <drm/drm_fb_helper.h> | ||||
| #include <drm/drm_file.h> | ||||
| #include <drm/drm_ioctl.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; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| /*
 | ||||
|  * 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 | ||||
|  * | ||||
|  |  | |||
|  | @ -939,8 +939,7 @@ void dce4_program_fmt(struct drm_encoder *encoder); | |||
| void dce8_program_fmt(struct drm_encoder *encoder); | ||||
| 
 | ||||
| /* fbdev layer */ | ||||
| int radeon_fbdev_init(struct radeon_device *rdev); | ||||
| void radeon_fbdev_fini(struct radeon_device *rdev); | ||||
| void radeon_fbdev_setup(struct radeon_device *rdev); | ||||
| 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); | ||||
| 
 | ||||
|  |  | |||
		Loading…
	
		Reference in a new issue
	
	 Thomas Zimmermann
						Thomas Zimmermann