brintos

brintos / linux-shallow public Read only

0
0
Text · 18.8 KiB · 49a1ac4 Raw
701 lines · c
1/*2 * Copyright © 2007 David Airlie3 *4 * Permission is hereby granted, free of charge, to any person obtaining a5 * copy of this software and associated documentation files (the "Software"),6 * to deal in the Software without restriction, including without limitation7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,8 * and/or sell copies of the Software, and to permit persons to whom the9 * Software is furnished to do so, subject to the following conditions:10 *11 * The above copyright notice and this permission notice (including the next12 * paragraph) shall be included in all copies or substantial portions of the13 * Software.14 *15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL18 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING20 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER21 * DEALINGS IN THE SOFTWARE.22 *23 * Authors:24 *     David Airlie25 */26 27#include <linux/console.h>28#include <linux/delay.h>29#include <linux/errno.h>30#include <linux/fb.h>31#include <linux/init.h>32#include <linux/kernel.h>33#include <linux/mm.h>34#include <linux/module.h>35#include <linux/string.h>36#include <linux/sysrq.h>37#include <linux/tty.h>38#include <linux/vga_switcheroo.h>39 40#include <drm/drm_crtc.h>41#include <drm/drm_crtc_helper.h>42#include <drm/drm_fb_helper.h>43#include <drm/drm_fourcc.h>44#include <drm/drm_gem_framebuffer_helper.h>45 46#include "gem/i915_gem_mman.h"47#include "gem/i915_gem_object.h"48 49#include "i915_drv.h"50#include "intel_display_types.h"51#include "intel_fb.h"52#include "intel_fb_pin.h"53#include "intel_fbdev.h"54#include "intel_fbdev_fb.h"55#include "intel_frontbuffer.h"56 57struct intel_fbdev {58	struct drm_fb_helper helper;59	struct intel_framebuffer *fb;60	struct i915_vma *vma;61	unsigned long vma_flags;62	int preferred_bpp;63 64	/* Whether or not fbdev hpd processing is temporarily suspended */65	bool hpd_suspended: 1;66	/* Set when a hotplug was received while HPD processing was suspended */67	bool hpd_waiting: 1;68 69	/* Protects hpd_suspended */70	struct mutex hpd_lock;71};72 73static struct intel_fbdev *to_intel_fbdev(struct drm_fb_helper *fb_helper)74{75	return container_of(fb_helper, struct intel_fbdev, helper);76}77 78static struct intel_frontbuffer *to_frontbuffer(struct intel_fbdev *ifbdev)79{80	return ifbdev->fb->frontbuffer;81}82 83static void intel_fbdev_invalidate(struct intel_fbdev *ifbdev)84{85	intel_frontbuffer_invalidate(to_frontbuffer(ifbdev), ORIGIN_CPU);86}87 88FB_GEN_DEFAULT_DEFERRED_IOMEM_OPS(intel_fbdev,89				  drm_fb_helper_damage_range,90				  drm_fb_helper_damage_area)91 92static int intel_fbdev_set_par(struct fb_info *info)93{94	struct intel_fbdev *ifbdev = to_intel_fbdev(info->par);95	int ret;96 97	ret = drm_fb_helper_set_par(info);98	if (ret == 0)99		intel_fbdev_invalidate(ifbdev);100 101	return ret;102}103 104static int intel_fbdev_blank(int blank, struct fb_info *info)105{106	struct intel_fbdev *ifbdev = to_intel_fbdev(info->par);107	int ret;108 109	ret = drm_fb_helper_blank(blank, info);110	if (ret == 0)111		intel_fbdev_invalidate(ifbdev);112 113	return ret;114}115 116static int intel_fbdev_pan_display(struct fb_var_screeninfo *var,117				   struct fb_info *info)118{119	struct intel_fbdev *ifbdev = to_intel_fbdev(info->par);120	int ret;121 122	ret = drm_fb_helper_pan_display(var, info);123	if (ret == 0)124		intel_fbdev_invalidate(ifbdev);125 126	return ret;127}128 129static int intel_fbdev_mmap(struct fb_info *info, struct vm_area_struct *vma)130{131	struct intel_fbdev *fbdev = to_intel_fbdev(info->par);132	struct drm_gem_object *bo = drm_gem_fb_get_obj(&fbdev->fb->base, 0);133	struct drm_i915_gem_object *obj = to_intel_bo(bo);134 135	return i915_gem_fb_mmap(obj, vma);136}137 138static void intel_fbdev_fb_destroy(struct fb_info *info)139{140	struct drm_fb_helper *fb_helper = info->par;141	struct intel_fbdev *ifbdev = container_of(fb_helper, struct intel_fbdev, helper);142 143	drm_fb_helper_fini(&ifbdev->helper);144 145	/*146	 * We rely on the object-free to release the VMA pinning for147	 * the info->screen_base mmaping. Leaking the VMA is simpler than148	 * trying to rectify all the possible error paths leading here.149	 */150	intel_fb_unpin_vma(ifbdev->vma, ifbdev->vma_flags);151	drm_framebuffer_remove(&ifbdev->fb->base);152 153	drm_client_release(&fb_helper->client);154	drm_fb_helper_unprepare(&ifbdev->helper);155	kfree(ifbdev);156}157 158__diag_push();159__diag_ignore_all("-Woverride-init", "Allow field initialization overrides for fb ops");160 161static const struct fb_ops intelfb_ops = {162	.owner = THIS_MODULE,163	__FB_DEFAULT_DEFERRED_OPS_RDWR(intel_fbdev),164	DRM_FB_HELPER_DEFAULT_OPS,165	.fb_set_par = intel_fbdev_set_par,166	.fb_blank = intel_fbdev_blank,167	.fb_pan_display = intel_fbdev_pan_display,168	__FB_DEFAULT_DEFERRED_OPS_DRAW(intel_fbdev),169	.fb_mmap = intel_fbdev_mmap,170	.fb_destroy = intel_fbdev_fb_destroy,171};172 173__diag_pop();174 175static int intelfb_create(struct drm_fb_helper *helper,176			  struct drm_fb_helper_surface_size *sizes)177{178	struct intel_fbdev *ifbdev = to_intel_fbdev(helper);179	struct intel_framebuffer *fb = ifbdev->fb;180	struct drm_device *dev = helper->dev;181	struct drm_i915_private *dev_priv = to_i915(dev);182	const struct i915_gtt_view view = {183		.type = I915_GTT_VIEW_NORMAL,184	};185	intel_wakeref_t wakeref;186	struct fb_info *info;187	struct i915_vma *vma;188	unsigned long flags = 0;189	bool prealloc = false;190	struct drm_i915_gem_object *obj;191	int ret;192 193	mutex_lock(&ifbdev->hpd_lock);194	ret = ifbdev->hpd_suspended ? -EAGAIN : 0;195	mutex_unlock(&ifbdev->hpd_lock);196	if (ret)197		return ret;198 199	ifbdev->fb = NULL;200 201	if (fb &&202	    (sizes->fb_width > fb->base.width ||203	     sizes->fb_height > fb->base.height)) {204		drm_dbg_kms(&dev_priv->drm,205			    "BIOS fb too small (%dx%d), we require (%dx%d),"206			    " releasing it\n",207			    fb->base.width, fb->base.height,208			    sizes->fb_width, sizes->fb_height);209		drm_framebuffer_put(&fb->base);210		fb = NULL;211	}212	if (!fb || drm_WARN_ON(dev, !intel_fb_obj(&fb->base))) {213		drm_dbg_kms(&dev_priv->drm,214			    "no BIOS fb, allocating a new one\n");215		fb = intel_fbdev_fb_alloc(helper, sizes);216		if (IS_ERR(fb))217			return PTR_ERR(fb);218	} else {219		drm_dbg_kms(&dev_priv->drm, "re-using BIOS fb\n");220		prealloc = true;221		sizes->fb_width = fb->base.width;222		sizes->fb_height = fb->base.height;223	}224 225	wakeref = intel_runtime_pm_get(&dev_priv->runtime_pm);226 227	/* Pin the GGTT vma for our access via info->screen_base.228	 * This also validates that any existing fb inherited from the229	 * BIOS is suitable for own access.230	 */231	vma = intel_fb_pin_to_ggtt(&fb->base, &view,232				   fb->min_alignment, 0,233				   false, &flags);234	if (IS_ERR(vma)) {235		ret = PTR_ERR(vma);236		goto out_unlock;237	}238 239	info = drm_fb_helper_alloc_info(helper);240	if (IS_ERR(info)) {241		drm_err(&dev_priv->drm, "Failed to allocate fb_info (%pe)\n", info);242		ret = PTR_ERR(info);243		goto out_unpin;244	}245 246	ifbdev->helper.fb = &fb->base;247 248	info->fbops = &intelfb_ops;249 250	obj = intel_fb_obj(&fb->base);251 252	ret = intel_fbdev_fb_fill_info(dev_priv, info, obj, vma);253	if (ret)254		goto out_unpin;255 256	drm_fb_helper_fill_info(info, &ifbdev->helper, sizes);257 258	/* If the object is shmemfs backed, it will have given us zeroed pages.259	 * If the object is stolen however, it will be full of whatever260	 * garbage was left in there.261	 */262	if (!i915_gem_object_is_shmem(obj) && !prealloc)263		memset_io(info->screen_base, 0, info->screen_size);264 265	/* Use default scratch pixmap (info->pixmap.flags = FB_PIXMAP_SYSTEM) */266 267	drm_dbg_kms(&dev_priv->drm, "allocated %dx%d fb: 0x%08x\n",268		    fb->base.width, fb->base.height,269		    i915_ggtt_offset(vma));270	ifbdev->fb = fb;271	ifbdev->vma = vma;272	ifbdev->vma_flags = flags;273 274	intel_runtime_pm_put(&dev_priv->runtime_pm, wakeref);275 276	return 0;277 278out_unpin:279	intel_fb_unpin_vma(vma, flags);280out_unlock:281	intel_runtime_pm_put(&dev_priv->runtime_pm, wakeref);282	return ret;283}284 285static int intelfb_dirty(struct drm_fb_helper *helper, struct drm_clip_rect *clip)286{287	if (!(clip->x1 < clip->x2 && clip->y1 < clip->y2))288		return 0;289 290	if (helper->fb->funcs->dirty)291		return helper->fb->funcs->dirty(helper->fb, NULL, 0, 0, clip, 1);292 293	return 0;294}295 296static const struct drm_fb_helper_funcs intel_fb_helper_funcs = {297	.fb_probe = intelfb_create,298	.fb_dirty = intelfb_dirty,299};300 301/*302 * Build an intel_fbdev struct using a BIOS allocated framebuffer, if possible.303 * The core display code will have read out the current plane configuration,304 * so we use that to figure out if there's an object for us to use as the305 * fb, and if so, we re-use it for the fbdev configuration.306 *307 * Note we only support a single fb shared across pipes for boot (mostly for308 * fbcon), so we just find the biggest and use that.309 */310static bool intel_fbdev_init_bios(struct drm_device *dev,311				  struct intel_fbdev *ifbdev)312{313	struct drm_i915_private *i915 = to_i915(dev);314	struct intel_framebuffer *fb = NULL;315	struct intel_crtc *crtc;316	unsigned int max_size = 0;317 318	/* Find the largest fb */319	for_each_intel_crtc(dev, crtc) {320		struct intel_crtc_state *crtc_state =321			to_intel_crtc_state(crtc->base.state);322		struct intel_plane *plane =323			to_intel_plane(crtc->base.primary);324		struct intel_plane_state *plane_state =325			to_intel_plane_state(plane->base.state);326		struct drm_i915_gem_object *obj =327			intel_fb_obj(plane_state->uapi.fb);328 329		if (!crtc_state->uapi.active) {330			drm_dbg_kms(&i915->drm,331				    "[CRTC:%d:%s] not active, skipping\n",332				    crtc->base.base.id, crtc->base.name);333			continue;334		}335 336		if (!obj) {337			drm_dbg_kms(&i915->drm,338				    "[PLANE:%d:%s] no fb, skipping\n",339				    plane->base.base.id, plane->base.name);340			continue;341		}342 343		if (intel_bo_to_drm_bo(obj)->size > max_size) {344			drm_dbg_kms(&i915->drm,345				    "found possible fb from [PLANE:%d:%s]\n",346				    plane->base.base.id, plane->base.name);347			fb = to_intel_framebuffer(plane_state->uapi.fb);348			max_size = intel_bo_to_drm_bo(obj)->size;349		}350	}351 352	if (!fb) {353		drm_dbg_kms(&i915->drm,354			    "no active fbs found, not using BIOS config\n");355		goto out;356	}357 358	/* Now make sure all the pipes will fit into it */359	for_each_intel_crtc(dev, crtc) {360		struct intel_crtc_state *crtc_state =361			to_intel_crtc_state(crtc->base.state);362		struct intel_plane *plane =363			to_intel_plane(crtc->base.primary);364		unsigned int cur_size;365 366		if (!crtc_state->uapi.active) {367			drm_dbg_kms(&i915->drm,368				    "[CRTC:%d:%s] not active, skipping\n",369				    crtc->base.base.id, crtc->base.name);370			continue;371		}372 373		drm_dbg_kms(&i915->drm, "checking [PLANE:%d:%s] for BIOS fb\n",374			    plane->base.base.id, plane->base.name);375 376		/*377		 * See if the plane fb we found above will fit on this378		 * pipe.  Note we need to use the selected fb's pitch and bpp379		 * rather than the current pipe's, since they differ.380		 */381		cur_size = crtc_state->uapi.adjusted_mode.crtc_hdisplay;382		cur_size = cur_size * fb->base.format->cpp[0];383		if (fb->base.pitches[0] < cur_size) {384			drm_dbg_kms(&i915->drm,385				    "fb not wide enough for [PLANE:%d:%s] (%d vs %d)\n",386				    plane->base.base.id, plane->base.name,387				    cur_size, fb->base.pitches[0]);388			fb = NULL;389			break;390		}391 392		cur_size = crtc_state->uapi.adjusted_mode.crtc_vdisplay;393		cur_size = intel_fb_align_height(&fb->base, 0, cur_size);394		cur_size *= fb->base.pitches[0];395		drm_dbg_kms(&i915->drm,396			    "[CRTC:%d:%s] area: %dx%d, bpp: %d, size: %d\n",397			    crtc->base.base.id, crtc->base.name,398			    crtc_state->uapi.adjusted_mode.crtc_hdisplay,399			    crtc_state->uapi.adjusted_mode.crtc_vdisplay,400			    fb->base.format->cpp[0] * 8,401			    cur_size);402 403		if (cur_size > max_size) {404			drm_dbg_kms(&i915->drm,405				    "fb not big enough for [PLANE:%d:%s] (%d vs %d)\n",406				    plane->base.base.id, plane->base.name,407				    cur_size, max_size);408			fb = NULL;409			break;410		}411 412		drm_dbg_kms(&i915->drm,413			    "fb big enough [PLANE:%d:%s] (%d >= %d)\n",414			    plane->base.base.id, plane->base.name,415			    max_size, cur_size);416	}417 418	if (!fb) {419		drm_dbg_kms(&i915->drm,420			    "BIOS fb not suitable for all pipes, not using\n");421		goto out;422	}423 424	ifbdev->preferred_bpp = fb->base.format->cpp[0] * 8;425	ifbdev->fb = fb;426 427	drm_framebuffer_get(&ifbdev->fb->base);428 429	/* Final pass to check if any active pipes don't have fbs */430	for_each_intel_crtc(dev, crtc) {431		struct intel_crtc_state *crtc_state =432			to_intel_crtc_state(crtc->base.state);433		struct intel_plane *plane =434			to_intel_plane(crtc->base.primary);435		struct intel_plane_state *plane_state =436			to_intel_plane_state(plane->base.state);437 438		if (!crtc_state->uapi.active)439			continue;440 441		drm_WARN(dev, !plane_state->uapi.fb,442			 "re-used BIOS config but lost an fb on [PLANE:%d:%s]\n",443			 plane->base.base.id, plane->base.name);444	}445 446 447	drm_dbg_kms(&i915->drm, "using BIOS fb for initial console\n");448	return true;449 450out:451 452	return false;453}454 455static void intel_fbdev_suspend_worker(struct work_struct *work)456{457	intel_fbdev_set_suspend(&container_of(work,458					      struct drm_i915_private,459					      display.fbdev.suspend_work)->drm,460				FBINFO_STATE_RUNNING,461				true);462}463 464/* Suspends/resumes fbdev processing of incoming HPD events. When resuming HPD465 * processing, fbdev will perform a full connector reprobe if a hotplug event466 * was received while HPD was suspended.467 */468static void intel_fbdev_hpd_set_suspend(struct drm_i915_private *i915, int state)469{470	struct intel_fbdev *ifbdev = i915->display.fbdev.fbdev;471	bool send_hpd = false;472 473	mutex_lock(&ifbdev->hpd_lock);474	ifbdev->hpd_suspended = state == FBINFO_STATE_SUSPENDED;475	send_hpd = !ifbdev->hpd_suspended && ifbdev->hpd_waiting;476	ifbdev->hpd_waiting = false;477	mutex_unlock(&ifbdev->hpd_lock);478 479	if (send_hpd) {480		drm_dbg_kms(&i915->drm, "Handling delayed fbcon HPD event\n");481		drm_fb_helper_hotplug_event(&ifbdev->helper);482	}483}484 485void intel_fbdev_set_suspend(struct drm_device *dev, int state, bool synchronous)486{487	struct drm_i915_private *dev_priv = to_i915(dev);488	struct intel_fbdev *ifbdev = dev_priv->display.fbdev.fbdev;489	struct fb_info *info;490 491	if (!ifbdev)492		return;493 494	if (drm_WARN_ON(&dev_priv->drm, !HAS_DISPLAY(dev_priv)))495		return;496 497	if (!ifbdev->vma)498		goto set_suspend;499 500	info = ifbdev->helper.info;501 502	if (synchronous) {503		/* Flush any pending work to turn the console on, and then504		 * wait to turn it off. It must be synchronous as we are505		 * about to suspend or unload the driver.506		 *507		 * Note that from within the work-handler, we cannot flush508		 * ourselves, so only flush outstanding work upon suspend!509		 */510		if (state != FBINFO_STATE_RUNNING)511			flush_work(&dev_priv->display.fbdev.suspend_work);512 513		console_lock();514	} else {515		/*516		 * The console lock can be pretty contented on resume due517		 * to all the printk activity.  Try to keep it out of the hot518		 * path of resume if possible.519		 */520		drm_WARN_ON(dev, state != FBINFO_STATE_RUNNING);521		if (!console_trylock()) {522			/* Don't block our own workqueue as this can523			 * be run in parallel with other i915.ko tasks.524			 */525			queue_work(dev_priv->unordered_wq,526				   &dev_priv->display.fbdev.suspend_work);527			return;528		}529	}530 531	/* On resume from hibernation: If the object is shmemfs backed, it has532	 * been restored from swap. If the object is stolen however, it will be533	 * full of whatever garbage was left in there.534	 */535	if (state == FBINFO_STATE_RUNNING &&536	    !i915_gem_object_is_shmem(intel_fb_obj(&ifbdev->fb->base)))537		memset_io(info->screen_base, 0, info->screen_size);538 539	drm_fb_helper_set_suspend(&ifbdev->helper, state);540	console_unlock();541 542set_suspend:543	intel_fbdev_hpd_set_suspend(dev_priv, state);544}545 546static int intel_fbdev_output_poll_changed(struct drm_device *dev)547{548	struct intel_fbdev *ifbdev = to_i915(dev)->display.fbdev.fbdev;549	bool send_hpd;550 551	if (!ifbdev)552		return -EINVAL;553 554	mutex_lock(&ifbdev->hpd_lock);555	send_hpd = !ifbdev->hpd_suspended;556	ifbdev->hpd_waiting = true;557	mutex_unlock(&ifbdev->hpd_lock);558 559	if (send_hpd && (ifbdev->vma || ifbdev->helper.deferred_setup))560		drm_fb_helper_hotplug_event(&ifbdev->helper);561 562	return 0;563}564 565static int intel_fbdev_restore_mode(struct drm_i915_private *dev_priv)566{567	struct intel_fbdev *ifbdev = dev_priv->display.fbdev.fbdev;568	int ret;569 570	if (!ifbdev)571		return -EINVAL;572 573	if (!ifbdev->vma)574		return -ENOMEM;575 576	ret = drm_fb_helper_restore_fbdev_mode_unlocked(&ifbdev->helper);577	if (ret)578		return ret;579 580	intel_fbdev_invalidate(ifbdev);581 582	return 0;583}584 585/*586 * Fbdev client and struct drm_client_funcs587 */588 589static void intel_fbdev_client_unregister(struct drm_client_dev *client)590{591	struct drm_fb_helper *fb_helper = drm_fb_helper_from_client(client);592	struct drm_device *dev = fb_helper->dev;593	struct pci_dev *pdev = to_pci_dev(dev->dev);594 595	if (fb_helper->info) {596		vga_switcheroo_client_fb_set(pdev, NULL);597		drm_fb_helper_unregister_info(fb_helper);598	} else {599		drm_fb_helper_unprepare(fb_helper);600		drm_client_release(&fb_helper->client);601		kfree(fb_helper);602	}603}604 605static int intel_fbdev_client_restore(struct drm_client_dev *client)606{607	struct drm_i915_private *dev_priv = to_i915(client->dev);608	int ret;609 610	ret = intel_fbdev_restore_mode(dev_priv);611	if (ret)612		return ret;613 614	vga_switcheroo_process_delayed_switch();615 616	return 0;617}618 619static int intel_fbdev_client_hotplug(struct drm_client_dev *client)620{621	struct drm_fb_helper *fb_helper = drm_fb_helper_from_client(client);622	struct drm_device *dev = client->dev;623	struct pci_dev *pdev = to_pci_dev(dev->dev);624	int ret;625 626	if (dev->fb_helper)627		return intel_fbdev_output_poll_changed(dev);628 629	ret = drm_fb_helper_init(dev, fb_helper);630	if (ret)631		goto err_drm_err;632 633	ret = drm_fb_helper_initial_config(fb_helper);634	if (ret)635		goto err_drm_fb_helper_fini;636 637	vga_switcheroo_client_fb_set(pdev, fb_helper->info);638 639	return 0;640 641err_drm_fb_helper_fini:642	drm_fb_helper_fini(fb_helper);643err_drm_err:644	drm_err(dev, "Failed to setup i915 fbdev emulation (ret=%d)\n", ret);645	return ret;646}647 648static const struct drm_client_funcs intel_fbdev_client_funcs = {649	.owner		= THIS_MODULE,650	.unregister	= intel_fbdev_client_unregister,651	.restore	= intel_fbdev_client_restore,652	.hotplug	= intel_fbdev_client_hotplug,653};654 655void intel_fbdev_setup(struct drm_i915_private *i915)656{657	struct drm_device *dev = &i915->drm;658	struct intel_fbdev *ifbdev;659	int ret;660 661	if (!HAS_DISPLAY(i915))662		return;663 664	ifbdev = kzalloc(sizeof(*ifbdev), GFP_KERNEL);665	if (!ifbdev)666		return;667	drm_fb_helper_prepare(dev, &ifbdev->helper, 32, &intel_fb_helper_funcs);668 669	i915->display.fbdev.fbdev = ifbdev;670	INIT_WORK(&i915->display.fbdev.suspend_work, intel_fbdev_suspend_worker);671	mutex_init(&ifbdev->hpd_lock);672	if (intel_fbdev_init_bios(dev, ifbdev))673		ifbdev->helper.preferred_bpp = ifbdev->preferred_bpp;674	else675		ifbdev->preferred_bpp = ifbdev->helper.preferred_bpp;676 677	ret = drm_client_init(dev, &ifbdev->helper.client, "intel-fbdev",678			      &intel_fbdev_client_funcs);679	if (ret) {680		drm_err(dev, "Failed to register client: %d\n", ret);681		goto err_drm_fb_helper_unprepare;682	}683 684	drm_client_register(&ifbdev->helper.client);685 686	return;687 688err_drm_fb_helper_unprepare:689	drm_fb_helper_unprepare(&ifbdev->helper);690	mutex_destroy(&ifbdev->hpd_lock);691	kfree(ifbdev);692}693 694struct intel_framebuffer *intel_fbdev_framebuffer(struct intel_fbdev *fbdev)695{696	if (!fbdev || !fbdev->helper.fb)697		return NULL;698 699	return to_intel_framebuffer(fbdev->helper.fb);700}701