diff options
Diffstat (limited to 'drivers/gpu/drm/i915/display/intel_fbdev.c')
| -rw-r--r-- | drivers/gpu/drm/i915/display/intel_fbdev.c | 270 | 
1 files changed, 151 insertions, 119 deletions
diff --git a/drivers/gpu/drm/i915/display/intel_fbdev.c b/drivers/gpu/drm/i915/display/intel_fbdev.c index 99894a855ef0..bda702c2cab8 100644 --- a/drivers/gpu/drm/i915/display/intel_fbdev.c +++ b/drivers/gpu/drm/i915/display/intel_fbdev.c @@ -24,7 +24,6 @@   *     David Airlie   */ -#include <linux/async.h>  #include <linux/console.h>  #include <linux/delay.h>  #include <linux/errno.h> @@ -39,6 +38,7 @@  #include <linux/vga_switcheroo.h>  #include <drm/drm_crtc.h> +#include <drm/drm_crtc_helper.h>  #include <drm/drm_fb_helper.h>  #include <drm/drm_fourcc.h>  #include <drm/drm_gem_framebuffer_helper.h> @@ -58,7 +58,6 @@ struct intel_fbdev {  	struct intel_framebuffer *fb;  	struct i915_vma *vma;  	unsigned long vma_flags; -	async_cookie_t cookie;  	int preferred_bpp;  	/* Whether or not fbdev hpd processing is temporarily suspended */ @@ -135,6 +134,29 @@ static int intel_fbdev_mmap(struct fb_info *info, struct vm_area_struct *vma)  	return i915_gem_fb_mmap(obj, vma);  } +static void intel_fbdev_fb_destroy(struct fb_info *info) +{ +	struct drm_fb_helper *fb_helper = info->par; +	struct intel_fbdev *ifbdev = container_of(fb_helper, struct intel_fbdev, helper); + +	drm_fb_helper_fini(&ifbdev->helper); + +	/* +	 * We rely on the object-free to release the VMA pinning for +	 * the info->screen_base mmaping. Leaking the VMA is simpler than +	 * trying to rectify all the possible error paths leading here. +	 */ +	intel_unpin_fb_vma(ifbdev->vma, ifbdev->vma_flags); +	drm_framebuffer_remove(&ifbdev->fb->base); + +	drm_client_release(&fb_helper->client); +	drm_fb_helper_unprepare(&ifbdev->helper); +	kfree(ifbdev); +} + +__diag_push(); +__diag_ignore_all("-Woverride-init", "Allow field initialization overrides for fb ops"); +  static const struct fb_ops intelfb_ops = {  	.owner = THIS_MODULE,  	__FB_DEFAULT_DEFERRED_OPS_RDWR(intel_fbdev), @@ -144,8 +166,11 @@ static const struct fb_ops intelfb_ops = {  	.fb_pan_display = intel_fbdev_pan_display,  	__FB_DEFAULT_DEFERRED_OPS_DRAW(intel_fbdev),  	.fb_mmap = intel_fbdev_mmap, +	.fb_destroy = intel_fbdev_fb_destroy,  }; +__diag_pop(); +  static int intelfb_create(struct drm_fb_helper *helper,  			  struct drm_fb_helper_surface_size *sizes)  { @@ -153,7 +178,6 @@ static int intelfb_create(struct drm_fb_helper *helper,  	struct intel_framebuffer *intel_fb = ifbdev->fb;  	struct drm_device *dev = helper->dev;  	struct drm_i915_private *dev_priv = to_i915(dev); -	struct pci_dev *pdev = to_pci_dev(dev_priv->drm.dev);  	const struct i915_gtt_view view = {  		.type = I915_GTT_VIEW_NORMAL,  	}; @@ -245,7 +269,7 @@ static int intelfb_create(struct drm_fb_helper *helper,  	ifbdev->vma_flags = flags;  	intel_runtime_pm_put(&dev_priv->runtime_pm, wakeref); -	vga_switcheroo_client_fb_set(pdev, info); +  	return 0;  out_unpin: @@ -271,25 +295,6 @@ static const struct drm_fb_helper_funcs intel_fb_helper_funcs = {  	.fb_dirty = intelfb_dirty,  }; -static void intel_fbdev_destroy(struct intel_fbdev *ifbdev) -{ -	/* We rely on the object-free to release the VMA pinning for -	 * the info->screen_base mmaping. Leaking the VMA is simpler than -	 * trying to rectify all the possible error paths leading here. -	 */ - -	drm_fb_helper_fini(&ifbdev->helper); - -	if (ifbdev->vma) -		intel_unpin_fb_vma(ifbdev->vma, ifbdev->vma_flags); - -	if (ifbdev->fb) -		drm_framebuffer_remove(&ifbdev->fb->base); - -	drm_fb_helper_unprepare(&ifbdev->helper); -	kfree(ifbdev); -} -  /*   * Build an intel_fbdev struct using a BIOS allocated framebuffer, if possible.   * The core display code will have read out the current plane configuration, @@ -453,93 +458,6 @@ static void intel_fbdev_suspend_worker(struct work_struct *work)  				true);  } -int intel_fbdev_init(struct drm_device *dev) -{ -	struct drm_i915_private *dev_priv = to_i915(dev); -	struct intel_fbdev *ifbdev; -	int ret; - -	if (drm_WARN_ON(dev, !HAS_DISPLAY(dev_priv))) -		return -ENODEV; - -	ifbdev = kzalloc(sizeof(struct intel_fbdev), GFP_KERNEL); -	if (ifbdev == NULL) -		return -ENOMEM; - -	mutex_init(&ifbdev->hpd_lock); -	drm_fb_helper_prepare(dev, &ifbdev->helper, 32, &intel_fb_helper_funcs); - -	if (intel_fbdev_init_bios(dev, ifbdev)) -		ifbdev->helper.preferred_bpp = ifbdev->preferred_bpp; -	else -		ifbdev->preferred_bpp = ifbdev->helper.preferred_bpp; - -	ret = drm_fb_helper_init(dev, &ifbdev->helper); -	if (ret) { -		kfree(ifbdev); -		return ret; -	} - -	dev_priv->display.fbdev.fbdev = ifbdev; -	INIT_WORK(&dev_priv->display.fbdev.suspend_work, intel_fbdev_suspend_worker); - -	return 0; -} - -static void intel_fbdev_initial_config(void *data, async_cookie_t cookie) -{ -	struct intel_fbdev *ifbdev = data; - -	/* Due to peculiar init order wrt to hpd handling this is separate. */ -	if (drm_fb_helper_initial_config(&ifbdev->helper)) -		intel_fbdev_unregister(to_i915(ifbdev->helper.dev)); -} - -void intel_fbdev_initial_config_async(struct drm_i915_private *dev_priv) -{ -	struct intel_fbdev *ifbdev = dev_priv->display.fbdev.fbdev; - -	if (!ifbdev) -		return; - -	ifbdev->cookie = async_schedule(intel_fbdev_initial_config, ifbdev); -} - -static void intel_fbdev_sync(struct intel_fbdev *ifbdev) -{ -	if (!ifbdev->cookie) -		return; - -	/* Only serialises with all preceding async calls, hence +1 */ -	async_synchronize_cookie(ifbdev->cookie + 1); -	ifbdev->cookie = 0; -} - -void intel_fbdev_unregister(struct drm_i915_private *dev_priv) -{ -	struct intel_fbdev *ifbdev = dev_priv->display.fbdev.fbdev; - -	if (!ifbdev) -		return; - -	intel_fbdev_set_suspend(&dev_priv->drm, FBINFO_STATE_SUSPENDED, true); - -	if (!current_is_async()) -		intel_fbdev_sync(ifbdev); - -	drm_fb_helper_unregister_info(&ifbdev->helper); -} - -void intel_fbdev_fini(struct drm_i915_private *dev_priv) -{ -	struct intel_fbdev *ifbdev = fetch_and_zero(&dev_priv->display.fbdev.fbdev); - -	if (!ifbdev) -		return; - -	intel_fbdev_destroy(ifbdev); -} -  /* Suspends/resumes fbdev processing of incoming HPD events. When resuming HPD   * processing, fbdev will perform a full connector reprobe if a hotplug event   * was received while HPD was suspended. @@ -622,15 +540,13 @@ set_suspend:  	intel_fbdev_hpd_set_suspend(dev_priv, state);  } -void intel_fbdev_output_poll_changed(struct drm_device *dev) +static int intel_fbdev_output_poll_changed(struct drm_device *dev)  {  	struct intel_fbdev *ifbdev = to_i915(dev)->display.fbdev.fbdev;  	bool send_hpd;  	if (!ifbdev) -		return; - -	intel_fbdev_sync(ifbdev); +		return -EINVAL;  	mutex_lock(&ifbdev->hpd_lock);  	send_hpd = !ifbdev->hpd_suspended; @@ -639,21 +555,137 @@ void intel_fbdev_output_poll_changed(struct drm_device *dev)  	if (send_hpd && (ifbdev->vma || ifbdev->helper.deferred_setup))  		drm_fb_helper_hotplug_event(&ifbdev->helper); + +	return 0;  } -void intel_fbdev_restore_mode(struct drm_i915_private *dev_priv) +static int intel_fbdev_restore_mode(struct drm_i915_private *dev_priv)  {  	struct intel_fbdev *ifbdev = dev_priv->display.fbdev.fbdev; +	int ret;  	if (!ifbdev) -		return; +		return -EINVAL; -	intel_fbdev_sync(ifbdev);  	if (!ifbdev->vma) +		return -ENOMEM; + +	ret = drm_fb_helper_restore_fbdev_mode_unlocked(&ifbdev->helper); +	if (ret) +		return ret; + +	intel_fbdev_invalidate(ifbdev); + +	return 0; +} + +/* + * Fbdev client and struct drm_client_funcs + */ + +static void intel_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 pci_dev *pdev = to_pci_dev(dev->dev); + +	if (fb_helper->info) { +		vga_switcheroo_client_fb_set(pdev, NULL); +		drm_fb_helper_unregister_info(fb_helper); +	} else { +		drm_fb_helper_unprepare(fb_helper); +		drm_client_release(&fb_helper->client); +		kfree(fb_helper); +	} +} + +static int intel_fbdev_client_restore(struct drm_client_dev *client) +{ +	struct drm_i915_private *dev_priv = to_i915(client->dev); +	int ret; + +	ret = intel_fbdev_restore_mode(dev_priv); +	if (ret) +		return ret; + +	vga_switcheroo_process_delayed_switch(); + +	return 0; +} + +static int intel_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 pci_dev *pdev = to_pci_dev(dev->dev); +	int ret; + +	if (dev->fb_helper) +		return intel_fbdev_output_poll_changed(dev); + +	ret = drm_fb_helper_init(dev, fb_helper); +	if (ret) +		goto err_drm_err; + +	ret = drm_fb_helper_initial_config(fb_helper); +	if (ret) +		goto err_drm_fb_helper_fini; + +	vga_switcheroo_client_fb_set(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 i915 fbdev emulation (ret=%d)\n", ret); +	return ret; +} + +static const struct drm_client_funcs intel_fbdev_client_funcs = { +	.owner		= THIS_MODULE, +	.unregister	= intel_fbdev_client_unregister, +	.restore	= intel_fbdev_client_restore, +	.hotplug	= intel_fbdev_client_hotplug, +}; + +void intel_fbdev_setup(struct drm_i915_private *i915) +{ +	struct drm_device *dev = &i915->drm; +	struct intel_fbdev *ifbdev; +	int ret; + +	if (!HAS_DISPLAY(i915))  		return; -	if (drm_fb_helper_restore_fbdev_mode_unlocked(&ifbdev->helper) == 0) -		intel_fbdev_invalidate(ifbdev); +	ifbdev = kzalloc(sizeof(*ifbdev), GFP_KERNEL); +	if (!ifbdev) +		return; +	drm_fb_helper_prepare(dev, &ifbdev->helper, 32, &intel_fb_helper_funcs); + +	i915->display.fbdev.fbdev = ifbdev; +	INIT_WORK(&i915->display.fbdev.suspend_work, intel_fbdev_suspend_worker); +	mutex_init(&ifbdev->hpd_lock); +	if (intel_fbdev_init_bios(dev, ifbdev)) +		ifbdev->helper.preferred_bpp = ifbdev->preferred_bpp; +	else +		ifbdev->preferred_bpp = ifbdev->helper.preferred_bpp; + +	ret = drm_client_init(dev, &ifbdev->helper.client, "intel-fbdev", +			      &intel_fbdev_client_funcs); +	if (ret) { +		drm_err(dev, "Failed to register client: %d\n", ret); +		goto err_drm_fb_helper_unprepare; +	} + +	drm_client_register(&ifbdev->helper.client); + +	return; + +err_drm_fb_helper_unprepare: +	drm_fb_helper_unprepare(&ifbdev->helper); +	mutex_destroy(&ifbdev->hpd_lock); +	kfree(ifbdev);  }  struct intel_framebuffer *intel_fbdev_framebuffer(struct intel_fbdev *fbdev)  |