summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorNicolas Chauvet <kwizart@gmail.com>2014-07-01 00:04:33 +0200
committerNicolas Chauvet <kwizart@gmail.com>2014-07-07 16:21:57 +0200
commit892dd7f98230d1ed9886162f1593d314a48c91c1 (patch)
treeba9106b09f12338668f4f483d85739e6e185e880
parent417cd071bef94967fbab21c95222b0f828a7189a (diff)
downloadkernel-892dd7f98230d1ed9886162f1593d314a48c91c1.tar.gz
kernel-892dd7f98230d1ed9886162f1593d314a48c91c1.tar.xz
kernel-892dd7f98230d1ed9886162f1593d314a48c91c1.zip
Add tagr drm patchez
-rw-r--r--linux-arm-tagr-01_04-drm_fb-helper.patch143
-rw-r--r--linux-arm-tagr-02_04-drm_constify_struct.patch250
-rw-r--r--linux-arm-tagr-03_04-drm-introduce.patch390
-rw-r--r--linux-arm-tagr-04_04-drm-race_free.patch179
4 files changed, 962 insertions, 0 deletions
diff --git a/linux-arm-tagr-01_04-drm_fb-helper.patch b/linux-arm-tagr-01_04-drm_fb-helper.patch
new file mode 100644
index 00000000..4f882e9d
--- /dev/null
+++ b/linux-arm-tagr-01_04-drm_fb-helper.patch
@@ -0,0 +1,143 @@
+From: Daniel Vetter <daniel.vetter@ffwll.ch>
+
+Some drivers need to be able to have a perfect race-free fbcon setup.
+Current drivers only enable hotplug processing after the call to
+drm_fb_helper_initial_config which leaves a tiny but important race.
+
+This race is especially noticable on embedded platforms where the
+driver itself enables the voltage for the hdmi output, since only then
+will monitors (after a bit of delay, as usual) respond by asserting
+the hpd pin.
+
+Most of the infrastructure is already there with the split-out
+drm_fb_helper_init. And drm_fb_helper_initial_config already has all
+the required locking to handle concurrent hpd events since
+
+commit 53f1904bced78d7c00f5d874c662ec3ac85d0f9f
+Author: Daniel Vetter <daniel.vetter@ffwll.ch>
+Date: Thu Mar 20 14:26:35 2014 +0100
+
+ drm/fb-helper: improve drm_fb_helper_initial_config locking
+
+The only missing bit is making drm_fb_helper_hotplug_event save
+against concurrent calls of drm_fb_helper_initial_config. The only
+unprotected bit is the check for fb_helper->fb.
+
+With that drivers can first initialize the fb helper, then enabel
+hotplug processing and then set up the initial config all in a
+completely race-free manner. Update kerneldoc and convert i915 as a
+proof of concept.
+
+Feature requested by Thierry since his tegra driver atm reliably boots
+slowly enough to misses the hotplug event for an external hdmi screen,
+but also reliably boots to quickly for the hpd pin to be asserted when
+the fb helper calls into the hdmi ->detect function.
+
+Cc: Thierry Reding <treding@nvidia.com>
+Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch>
+Signed-off-by: Thierry Reding <treding@nvidia.com>
+---
+Changes in v3:
+- remove an additional occurrence of i915's enable_hotplug_processing
+ that was introduced after the original patch
+
+ drivers/gpu/drm/drm_fb_helper.c | 11 +++++------
+ drivers/gpu/drm/i915/i915_dma.c | 3 ---
+ drivers/gpu/drm/i915/i915_drv.c | 2 --
+ drivers/gpu/drm/i915/i915_drv.h | 1 -
+ drivers/gpu/drm/i915/i915_irq.c | 4 ----
+ 5 files changed, 5 insertions(+), 16 deletions(-)
+
+diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c
+index d5d8cea1a679..13a098c9af31 100644
+--- a/drivers/gpu/drm/drm_fb_helper.c
++++ b/drivers/gpu/drm/drm_fb_helper.c
+@@ -1613,8 +1613,10 @@ EXPORT_SYMBOL(drm_fb_helper_initial_config);
+ * either the output polling work or a work item launched from the driver's
+ * hotplug interrupt).
+ *
+- * Note that the driver must ensure that this is only called _after_ the fb has
+- * been fully set up, i.e. after the call to drm_fb_helper_initial_config.
++ * Note that drivers may call this even before calling
++ * drm_fb_helper_initial_config but only aftert drm_fb_helper_init. This allows
++ * for a race-free fbcon setup and will make sure that the fbdev emulation will
++ * not miss any hotplug events.
+ *
+ * RETURNS:
+ * 0 on success and a non-zero error code otherwise.
+@@ -1624,11 +1626,8 @@ int drm_fb_helper_hotplug_event(struct drm_fb_helper *fb_helper)
+ struct drm_device *dev = fb_helper->dev;
+ u32 max_width, max_height;
+
+- if (!fb_helper->fb)
+- return 0;
+-
+ mutex_lock(&fb_helper->dev->mode_config.mutex);
+- if (!drm_fb_helper_is_bound(fb_helper)) {
++ if (!fb_helper->fb || !drm_fb_helper_is_bound(fb_helper)) {
+ fb_helper->delayed_hotplug = true;
+ mutex_unlock(&fb_helper->dev->mode_config.mutex);
+ return 0;
+diff --git a/drivers/gpu/drm/i915/i915_dma.c b/drivers/gpu/drm/i915/i915_dma.c
+index 5e583a1838f8..84b55665bd87 100644
+--- a/drivers/gpu/drm/i915/i915_dma.c
++++ b/drivers/gpu/drm/i915/i915_dma.c
+@@ -1377,9 +1377,6 @@ static int i915_load_modeset_init(struct drm_device *dev)
+ */
+ intel_fbdev_initial_config(dev);
+
+- /* Only enable hotplug handling once the fbdev is fully set up. */
+- dev_priv->enable_hotplug_processing = true;
+-
+ drm_kms_helper_poll_init(dev);
+
+ return 0;
+diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c
+index 6eb45ac7a7d5..b0955fffca98 100644
+--- a/drivers/gpu/drm/i915/i915_drv.c
++++ b/drivers/gpu/drm/i915/i915_drv.c
+@@ -521,7 +521,6 @@ static int i915_drm_freeze(struct drm_device *dev)
+ }
+
+ intel_runtime_pm_disable_interrupts(dev);
+- dev_priv->enable_hotplug_processing = false;
+
+ intel_suspend_gt_powersave(dev);
+
+@@ -659,7 +658,6 @@ static int __i915_drm_thaw(struct drm_device *dev, bool restore_gtt_mappings)
+ * notifications.
+ * */
+ intel_hpd_init(dev);
+- dev_priv->enable_hotplug_processing = true;
+ /* Config may have changed between suspend and resume */
+ drm_helper_hpd_irq_event(dev);
+ }
+diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h
+index 8cea59649ef2..df6a98cd702f 100644
+--- a/drivers/gpu/drm/i915/i915_drv.h
++++ b/drivers/gpu/drm/i915/i915_drv.h
+@@ -1404,7 +1404,6 @@ struct drm_i915_private {
+ u32 pipestat_irq_mask[I915_MAX_PIPES];
+
+ struct work_struct hotplug_work;
+- bool enable_hotplug_processing;
+ struct {
+ unsigned long hpd_last_jiffies;
+ int hpd_cnt;
+diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c
+index 1c1ec22bc7ef..c0d7674c45cd 100644
+--- a/drivers/gpu/drm/i915/i915_irq.c
++++ b/drivers/gpu/drm/i915/i915_irq.c
+@@ -1109,10 +1109,6 @@ static void i915_hotplug_work_func(struct work_struct *work)
+ bool changed = false;
+ u32 hpd_event_bits;
+
+- /* HPD irq before everything is fully set up. */
+- if (!dev_priv->enable_hotplug_processing)
+- return;
+-
+ mutex_lock(&mode_config->mutex);
+ DRM_DEBUG_KMS("running encoder hotplug functions\n");
+
+--
+2.0.0
diff --git a/linux-arm-tagr-02_04-drm_constify_struct.patch b/linux-arm-tagr-02_04-drm_constify_struct.patch
new file mode 100644
index 00000000..4640bcc3
--- /dev/null
+++ b/linux-arm-tagr-02_04-drm_constify_struct.patch
@@ -0,0 +1,250 @@
+From: Thierry Reding <treding@nvidia.com>
+
+There's no need for this to be modifiable. Make it const so that it can
+be put into the .rodata section.
+
+Reviewed-by: Daniel Vetter <daniel.vetter@ffwll.ch>
+Signed-off-by: Thierry Reding <treding@nvidia.com>
+---
+ drivers/gpu/drm/armada/armada_fbdev.c | 2 +-
+ drivers/gpu/drm/ast/ast_fb.c | 2 +-
+ drivers/gpu/drm/bochs/bochs_fbdev.c | 2 +-
+ drivers/gpu/drm/cirrus/cirrus_fbdev.c | 2 +-
+ drivers/gpu/drm/drm_fb_cma_helper.c | 2 +-
+ drivers/gpu/drm/exynos/exynos_drm_fbdev.c | 2 +-
+ drivers/gpu/drm/gma500/framebuffer.c | 2 +-
+ drivers/gpu/drm/i915/intel_fbdev.c | 2 +-
+ drivers/gpu/drm/mgag200/mgag200_fb.c | 2 +-
+ drivers/gpu/drm/msm/msm_fbdev.c | 2 +-
+ drivers/gpu/drm/nouveau/nouveau_fbcon.c | 2 +-
+ drivers/gpu/drm/omapdrm/omap_fbdev.c | 2 +-
+ drivers/gpu/drm/qxl/qxl_fb.c | 2 +-
+ drivers/gpu/drm/radeon/radeon_fb.c | 2 +-
+ drivers/gpu/drm/tegra/fb.c | 2 +-
+ drivers/gpu/drm/udl/udl_fb.c | 2 +-
+ include/drm/drm_fb_helper.h | 2 +-
+ 17 files changed, 17 insertions(+), 17 deletions(-)
+
+diff --git a/drivers/gpu/drm/armada/armada_fbdev.c b/drivers/gpu/drm/armada/armada_fbdev.c
+index fd166f532ab9..a7c947cd9386 100644
+--- a/drivers/gpu/drm/armada/armada_fbdev.c
++++ b/drivers/gpu/drm/armada/armada_fbdev.c
+@@ -131,7 +131,7 @@ static int armada_fb_probe(struct drm_fb_helper *fbh,
+ return ret;
+ }
+
+-static struct drm_fb_helper_funcs armada_fb_helper_funcs = {
++static const struct drm_fb_helper_funcs armada_fb_helper_funcs = {
+ .gamma_set = armada_drm_crtc_gamma_set,
+ .gamma_get = armada_drm_crtc_gamma_get,
+ .fb_probe = armada_fb_probe,
+diff --git a/drivers/gpu/drm/ast/ast_fb.c b/drivers/gpu/drm/ast/ast_fb.c
+index a28640f47c27..2113894e4ff8 100644
+--- a/drivers/gpu/drm/ast/ast_fb.c
++++ b/drivers/gpu/drm/ast/ast_fb.c
+@@ -287,7 +287,7 @@ static void ast_fb_gamma_get(struct drm_crtc *crtc, u16 *red, u16 *green,
+ *blue = ast_crtc->lut_b[regno] << 8;
+ }
+
+-static struct drm_fb_helper_funcs ast_fb_helper_funcs = {
++static const struct drm_fb_helper_funcs ast_fb_helper_funcs = {
+ .gamma_set = ast_fb_gamma_set,
+ .gamma_get = ast_fb_gamma_get,
+ .fb_probe = astfb_create,
+diff --git a/drivers/gpu/drm/bochs/bochs_fbdev.c b/drivers/gpu/drm/bochs/bochs_fbdev.c
+index 561b84474122..17e5c17f2730 100644
+--- a/drivers/gpu/drm/bochs/bochs_fbdev.c
++++ b/drivers/gpu/drm/bochs/bochs_fbdev.c
+@@ -179,7 +179,7 @@ void bochs_fb_gamma_get(struct drm_crtc *crtc, u16 *red, u16 *green,
+ *blue = regno;
+ }
+
+-static struct drm_fb_helper_funcs bochs_fb_helper_funcs = {
++static const struct drm_fb_helper_funcs bochs_fb_helper_funcs = {
+ .gamma_set = bochs_fb_gamma_set,
+ .gamma_get = bochs_fb_gamma_get,
+ .fb_probe = bochsfb_create,
+diff --git a/drivers/gpu/drm/cirrus/cirrus_fbdev.c b/drivers/gpu/drm/cirrus/cirrus_fbdev.c
+index 32bbba0a787b..2bd0291168e4 100644
+--- a/drivers/gpu/drm/cirrus/cirrus_fbdev.c
++++ b/drivers/gpu/drm/cirrus/cirrus_fbdev.c
+@@ -288,7 +288,7 @@ static int cirrus_fbdev_destroy(struct drm_device *dev,
+ return 0;
+ }
+
+-static struct drm_fb_helper_funcs cirrus_fb_helper_funcs = {
++static const struct drm_fb_helper_funcs cirrus_fb_helper_funcs = {
+ .gamma_set = cirrus_crtc_fb_gamma_set,
+ .gamma_get = cirrus_crtc_fb_gamma_get,
+ .fb_probe = cirrusfb_create,
+diff --git a/drivers/gpu/drm/drm_fb_cma_helper.c b/drivers/gpu/drm/drm_fb_cma_helper.c
+index f27c883be391..cb01e1606384 100644
+--- a/drivers/gpu/drm/drm_fb_cma_helper.c
++++ b/drivers/gpu/drm/drm_fb_cma_helper.c
+@@ -327,7 +327,7 @@ err_drm_gem_cma_free_object:
+ return ret;
+ }
+
+-static struct drm_fb_helper_funcs drm_fb_cma_helper_funcs = {
++static const struct drm_fb_helper_funcs drm_fb_cma_helper_funcs = {
+ .fb_probe = drm_fbdev_cma_create,
+ };
+
+diff --git a/drivers/gpu/drm/exynos/exynos_drm_fbdev.c b/drivers/gpu/drm/exynos/exynos_drm_fbdev.c
+index d771b467cf0c..fc25fe75aa77 100644
+--- a/drivers/gpu/drm/exynos/exynos_drm_fbdev.c
++++ b/drivers/gpu/drm/exynos/exynos_drm_fbdev.c
+@@ -225,7 +225,7 @@ out:
+ return ret;
+ }
+
+-static struct drm_fb_helper_funcs exynos_drm_fb_helper_funcs = {
++static const struct drm_fb_helper_funcs exynos_drm_fb_helper_funcs = {
+ .fb_probe = exynos_drm_fbdev_create,
+ };
+
+diff --git a/drivers/gpu/drm/gma500/framebuffer.c b/drivers/gpu/drm/gma500/framebuffer.c
+index e7fcc148f333..76e4d777d01d 100644
+--- a/drivers/gpu/drm/gma500/framebuffer.c
++++ b/drivers/gpu/drm/gma500/framebuffer.c
+@@ -561,7 +561,7 @@ static int psbfb_probe(struct drm_fb_helper *helper,
+ return psbfb_create(psb_fbdev, sizes);
+ }
+
+-static struct drm_fb_helper_funcs psb_fb_helper_funcs = {
++static const struct drm_fb_helper_funcs psb_fb_helper_funcs = {
+ .gamma_set = psbfb_gamma_set,
+ .gamma_get = psbfb_gamma_get,
+ .fb_probe = psbfb_probe,
+diff --git a/drivers/gpu/drm/i915/intel_fbdev.c b/drivers/gpu/drm/i915/intel_fbdev.c
+index 226fbc7d9464..c14073094024 100644
+--- a/drivers/gpu/drm/i915/intel_fbdev.c
++++ b/drivers/gpu/drm/i915/intel_fbdev.c
+@@ -478,7 +478,7 @@ out:
+ return true;
+ }
+
+-static struct drm_fb_helper_funcs intel_fb_helper_funcs = {
++static const struct drm_fb_helper_funcs intel_fb_helper_funcs = {
+ .initial_config = intel_fb_initial_config,
+ .gamma_set = intel_crtc_fb_gamma_set,
+ .gamma_get = intel_crtc_fb_gamma_get,
+diff --git a/drivers/gpu/drm/mgag200/mgag200_fb.c b/drivers/gpu/drm/mgag200/mgag200_fb.c
+index 13b7dd83faa9..a4319aba9180 100644
+--- a/drivers/gpu/drm/mgag200/mgag200_fb.c
++++ b/drivers/gpu/drm/mgag200/mgag200_fb.c
+@@ -272,7 +272,7 @@ static int mga_fbdev_destroy(struct drm_device *dev,
+ return 0;
+ }
+
+-static struct drm_fb_helper_funcs mga_fb_helper_funcs = {
++static const struct drm_fb_helper_funcs mga_fb_helper_funcs = {
+ .gamma_set = mga_crtc_fb_gamma_set,
+ .gamma_get = mga_crtc_fb_gamma_get,
+ .fb_probe = mgag200fb_create,
+diff --git a/drivers/gpu/drm/msm/msm_fbdev.c b/drivers/gpu/drm/msm/msm_fbdev.c
+index a752ab83b810..c229bf6e865e 100644
+--- a/drivers/gpu/drm/msm/msm_fbdev.c
++++ b/drivers/gpu/drm/msm/msm_fbdev.c
+@@ -177,7 +177,7 @@ static void msm_crtc_fb_gamma_get(struct drm_crtc *crtc,
+ DBG("fbdev: get gamma");
+ }
+
+-static struct drm_fb_helper_funcs msm_fb_helper_funcs = {
++static const struct drm_fb_helper_funcs msm_fb_helper_funcs = {
+ .gamma_set = msm_crtc_fb_gamma_set,
+ .gamma_get = msm_crtc_fb_gamma_get,
+ .fb_probe = msm_fbdev_create,
+diff --git a/drivers/gpu/drm/nouveau/nouveau_fbcon.c b/drivers/gpu/drm/nouveau/nouveau_fbcon.c
+index 64a42cfd3717..8e9c07b7fc89 100644
+--- a/drivers/gpu/drm/nouveau/nouveau_fbcon.c
++++ b/drivers/gpu/drm/nouveau/nouveau_fbcon.c
+@@ -438,7 +438,7 @@ void nouveau_fbcon_gpu_lockup(struct fb_info *info)
+ info->flags |= FBINFO_HWACCEL_DISABLED;
+ }
+
+-static struct drm_fb_helper_funcs nouveau_fbcon_helper_funcs = {
++static const struct drm_fb_helper_funcs nouveau_fbcon_helper_funcs = {
+ .gamma_set = nouveau_fbcon_gamma_set,
+ .gamma_get = nouveau_fbcon_gamma_get,
+ .fb_probe = nouveau_fbcon_create,
+diff --git a/drivers/gpu/drm/omapdrm/omap_fbdev.c b/drivers/gpu/drm/omapdrm/omap_fbdev.c
+index 1388ca7f87e8..4cb12083eb12 100644
+--- a/drivers/gpu/drm/omapdrm/omap_fbdev.c
++++ b/drivers/gpu/drm/omapdrm/omap_fbdev.c
+@@ -281,7 +281,7 @@ fail:
+ return ret;
+ }
+
+-static struct drm_fb_helper_funcs omap_fb_helper_funcs = {
++static const struct drm_fb_helper_funcs omap_fb_helper_funcs = {
+ .fb_probe = omap_fbdev_create,
+ };
+
+diff --git a/drivers/gpu/drm/qxl/qxl_fb.c b/drivers/gpu/drm/qxl/qxl_fb.c
+index f437b30ce689..cf89614c72be 100644
+--- a/drivers/gpu/drm/qxl/qxl_fb.c
++++ b/drivers/gpu/drm/qxl/qxl_fb.c
+@@ -660,7 +660,7 @@ static int qxl_fbdev_destroy(struct drm_device *dev, struct qxl_fbdev *qfbdev)
+ return 0;
+ }
+
+-static struct drm_fb_helper_funcs qxl_fb_helper_funcs = {
++static const struct drm_fb_helper_funcs qxl_fb_helper_funcs = {
+ .fb_probe = qxl_fb_find_or_create_single,
+ };
+
+diff --git a/drivers/gpu/drm/radeon/radeon_fb.c b/drivers/gpu/drm/radeon/radeon_fb.c
+index 665ced3b7313..ad97afdbc4c7 100644
+--- a/drivers/gpu/drm/radeon/radeon_fb.c
++++ b/drivers/gpu/drm/radeon/radeon_fb.c
+@@ -331,7 +331,7 @@ static int radeon_fbdev_destroy(struct drm_device *dev, struct radeon_fbdev *rfb
+ return 0;
+ }
+
+-static struct drm_fb_helper_funcs radeon_fb_helper_funcs = {
++static const struct drm_fb_helper_funcs radeon_fb_helper_funcs = {
+ .gamma_set = radeon_crtc_fb_gamma_set,
+ .gamma_get = radeon_crtc_fb_gamma_get,
+ .fb_probe = radeonfb_create,
+diff --git a/drivers/gpu/drm/tegra/fb.c b/drivers/gpu/drm/tegra/fb.c
+index 9798a7080322..f7cf47bf0afb 100644
+--- a/drivers/gpu/drm/tegra/fb.c
++++ b/drivers/gpu/drm/tegra/fb.c
+@@ -267,7 +267,7 @@ release:
+ return err;
+ }
+
+-static struct drm_fb_helper_funcs tegra_fb_helper_funcs = {
++static const struct drm_fb_helper_funcs tegra_fb_helper_funcs = {
+ .fb_probe = tegra_fbdev_probe,
+ };
+
+diff --git a/drivers/gpu/drm/udl/udl_fb.c b/drivers/gpu/drm/udl/udl_fb.c
+index 377176372da8..0647c8cc368b 100644
+--- a/drivers/gpu/drm/udl/udl_fb.c
++++ b/drivers/gpu/drm/udl/udl_fb.c
+@@ -550,7 +550,7 @@ out:
+ return ret;
+ }
+
+-static struct drm_fb_helper_funcs udl_fb_helper_funcs = {
++static const struct drm_fb_helper_funcs udl_fb_helper_funcs = {
+ .fb_probe = udlfb_create,
+ };
+
+diff --git a/include/drm/drm_fb_helper.h b/include/drm/drm_fb_helper.h
+index 7997246d4039..a47df7ca38e8 100644
+--- a/include/drm/drm_fb_helper.h
++++ b/include/drm/drm_fb_helper.h
+@@ -87,7 +87,7 @@ struct drm_fb_helper {
+ struct drm_fb_helper_crtc *crtc_info;
+ int connector_count;
+ struct drm_fb_helper_connector **connector_info;
+- struct drm_fb_helper_funcs *funcs;
++ const struct drm_fb_helper_funcs *funcs;
+ struct fb_info *fbdev;
+ u32 pseudo_palette[17];
+ struct list_head kernel_fb_list;
+--
+2.0.0
diff --git a/linux-arm-tagr-03_04-drm-introduce.patch b/linux-arm-tagr-03_04-drm-introduce.patch
new file mode 100644
index 00000000..6c0b6c6f
--- /dev/null
+++ b/linux-arm-tagr-03_04-drm-introduce.patch
@@ -0,0 +1,390 @@
+From: Thierry Reding <treding@nvidia.com>
+
+To implement hotplug detection in a race-free manner, drivers must call
+drm_kms_helper_poll_init() before hotplug events can be triggered. Such
+events can be triggered right after any of the encoders or connectors
+are initialized. At the same time, if the drm_fb_helper_hotplug_event()
+helper is used by a driver, then the poll helper requires some parts of
+the FB helper to be initialized to prevent a crash.
+
+At the same time, drm_fb_helper_init() requires information that is not
+necessarily available at such an early stage (number of CRTCs and
+connectors), so it cannot be used yet.
+
+Add a new helper, drm_fb_helper_prepare(), that initializes the bare
+minimum needed to allow drm_kms_helper_poll_init() to execute and any
+subsequent hotplug events to be processed properly.
+
+Reviewed-by: Daniel Vetter <daniel.vetter@ffwll.ch>
+Signed-off-by: Thierry Reding <treding@nvidia.com>
+---
+Changes in v3:
+- fix inconsistency "second" -> "third" (Daniel Vetter)
+
+Changes in v2:
+- improve kernel-doc (Daniel Vetter)
+
+ drivers/gpu/drm/armada/armada_fbdev.c | 2 +-
+ drivers/gpu/drm/ast/ast_fb.c | 4 ++-
+ drivers/gpu/drm/bochs/bochs_fbdev.c | 3 +-
+ drivers/gpu/drm/cirrus/cirrus_fbdev.c | 4 ++-
+ drivers/gpu/drm/drm_fb_cma_helper.c | 3 +-
+ drivers/gpu/drm/drm_fb_helper.c | 47 ++++++++++++++++++++++++-------
+ drivers/gpu/drm/exynos/exynos_drm_fbdev.c | 3 +-
+ drivers/gpu/drm/gma500/framebuffer.c | 3 +-
+ drivers/gpu/drm/i915/intel_fbdev.c | 3 +-
+ drivers/gpu/drm/mgag200/mgag200_fb.c | 3 +-
+ drivers/gpu/drm/msm/msm_fbdev.c | 2 +-
+ drivers/gpu/drm/nouveau/nouveau_fbcon.c | 3 +-
+ drivers/gpu/drm/omapdrm/omap_fbdev.c | 2 +-
+ drivers/gpu/drm/qxl/qxl_fb.c | 5 +++-
+ drivers/gpu/drm/radeon/radeon_fb.c | 4 ++-
+ drivers/gpu/drm/tegra/fb.c | 4 +--
+ drivers/gpu/drm/udl/udl_fb.c | 3 +-
+ include/drm/drm_fb_helper.h | 2 ++
+ 18 files changed, 72 insertions(+), 28 deletions(-)
+
+diff --git a/drivers/gpu/drm/armada/armada_fbdev.c b/drivers/gpu/drm/armada/armada_fbdev.c
+index a7c947cd9386..7838e731b0de 100644
+--- a/drivers/gpu/drm/armada/armada_fbdev.c
++++ b/drivers/gpu/drm/armada/armada_fbdev.c
+@@ -149,7 +149,7 @@ int armada_fbdev_init(struct drm_device *dev)
+
+ priv->fbdev = fbh;
+
+- fbh->funcs = &armada_fb_helper_funcs;
++ drm_fb_helper_prepare(dev, fbh, &armada_fb_helper_funcs);
+
+ ret = drm_fb_helper_init(dev, fbh, 1, 1);
+ if (ret) {
+diff --git a/drivers/gpu/drm/ast/ast_fb.c b/drivers/gpu/drm/ast/ast_fb.c
+index 2113894e4ff8..cba45c774552 100644
+--- a/drivers/gpu/drm/ast/ast_fb.c
++++ b/drivers/gpu/drm/ast/ast_fb.c
+@@ -328,8 +328,10 @@ int ast_fbdev_init(struct drm_device *dev)
+ return -ENOMEM;
+
+ ast->fbdev = afbdev;
+- afbdev->helper.funcs = &ast_fb_helper_funcs;
+ spin_lock_init(&afbdev->dirty_lock);
++
++ drm_fb_helper_prepare(dev, &afbdev->helper, &ast_fb_helper_funcs);
++
+ ret = drm_fb_helper_init(dev, &afbdev->helper,
+ 1, 1);
+ if (ret) {
+diff --git a/drivers/gpu/drm/bochs/bochs_fbdev.c b/drivers/gpu/drm/bochs/bochs_fbdev.c
+index 17e5c17f2730..19cf3e9413b6 100644
+--- a/drivers/gpu/drm/bochs/bochs_fbdev.c
++++ b/drivers/gpu/drm/bochs/bochs_fbdev.c
+@@ -189,7 +189,8 @@ int bochs_fbdev_init(struct bochs_device *bochs)
+ {
+ int ret;
+
+- bochs->fb.helper.funcs = &bochs_fb_helper_funcs;
++ drm_fb_helper_prepare(bochs->dev, &bochs->fb.helper,
++ &bochs_fb_helper_funcs);
+
+ ret = drm_fb_helper_init(bochs->dev, &bochs->fb.helper,
+ 1, 1);
+diff --git a/drivers/gpu/drm/cirrus/cirrus_fbdev.c b/drivers/gpu/drm/cirrus/cirrus_fbdev.c
+index 2bd0291168e4..2a135f253e29 100644
+--- a/drivers/gpu/drm/cirrus/cirrus_fbdev.c
++++ b/drivers/gpu/drm/cirrus/cirrus_fbdev.c
+@@ -306,9 +306,11 @@ int cirrus_fbdev_init(struct cirrus_device *cdev)
+ return -ENOMEM;
+
+ cdev->mode_info.gfbdev = gfbdev;
+- gfbdev->helper.funcs = &cirrus_fb_helper_funcs;
+ spin_lock_init(&gfbdev->dirty_lock);
+
++ drm_fb_helper_prepare(cdev->dev, &gfbdev->helper,
++ &cirrus_fb_helper_funcs);
++
+ ret = drm_fb_helper_init(cdev->dev, &gfbdev->helper,
+ cdev->num_crtc, CIRRUSFB_CONN_LIMIT);
+ if (ret) {
+diff --git a/drivers/gpu/drm/drm_fb_cma_helper.c b/drivers/gpu/drm/drm_fb_cma_helper.c
+index cb01e1606384..cc0ae047ed3b 100644
+--- a/drivers/gpu/drm/drm_fb_cma_helper.c
++++ b/drivers/gpu/drm/drm_fb_cma_helper.c
+@@ -354,9 +354,10 @@ struct drm_fbdev_cma *drm_fbdev_cma_init(struct drm_device *dev,
+ return ERR_PTR(-ENOMEM);
+ }
+
+- fbdev_cma->fb_helper.funcs = &drm_fb_cma_helper_funcs;
+ helper = &fbdev_cma->fb_helper;
+
++ drm_fb_helper_prepare(dev, helper, &drm_fb_cma_helper_funcs);
++
+ ret = drm_fb_helper_init(dev, helper, num_crtc, max_conn_count);
+ if (ret < 0) {
+ dev_err(dev->dev, "Failed to initialize drm fb helper.\n");
+diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c
+index 13a098c9af31..14aef947b3d2 100644
+--- a/drivers/gpu/drm/drm_fb_helper.c
++++ b/drivers/gpu/drm/drm_fb_helper.c
+@@ -49,10 +49,11 @@ static LIST_HEAD(kernel_fb_helper_list);
+ * helper functions used by many drivers to implement the kernel mode setting
+ * interfaces.
+ *
+- * Initialization is done as a three-step process with drm_fb_helper_init(),
+- * drm_fb_helper_single_add_all_connectors() and drm_fb_helper_initial_config().
+- * Drivers with fancier requirements than the default behaviour can override the
+- * second step with their own code. Teardown is done with drm_fb_helper_fini().
++ * Initialization is done as a four-step process with drm_fb_helper_prepare(),
++ * drm_fb_helper_init(), drm_fb_helper_single_add_all_connectors() and
++ * drm_fb_helper_initial_config(). Drivers with fancier requirements than the
++ * default behaviour can override the third step with their own code.
++ * Teardown is done with drm_fb_helper_fini().
+ *
+ * At runtime drivers should restore the fbdev console by calling
+ * drm_fb_helper_restore_fbdev_mode() from their ->lastclose callback. They
+@@ -63,6 +64,19 @@ static LIST_HEAD(kernel_fb_helper_list);
+ *
+ * All other functions exported by the fb helper library can be used to
+ * implement the fbdev driver interface by the driver.
++ *
++ * It is possible, though perhaps somewhat tricky, to implement race-free
++ * hotplug detection using the fbdev helpers. The drm_fb_helper_prepare()
++ * helper must be called first to initialize the minimum required to make
++ * hotplug detection work. Drivers also need to make sure to properly set up
++ * the dev->mode_config.funcs member. After calling drm_kms_helper_poll_init()
++ * it is safe to enable interrupts and start processing hotplug events. At the
++ * same time, drivers should initialize all modeset objects such as CRTCs,
++ * encoders and connectors. To finish up the fbdev helper initialization, the
++ * drm_fb_helper_init() function is called. To probe for all attached displays
++ * and set up an initial configuration using the detected hardware, drivers
++ * should call drm_fb_helper_single_add_all_connectors() followed by
++ * drm_fb_helper_initial_config().
+ */
+
+ /**
+@@ -531,6 +545,24 @@ static void drm_fb_helper_crtc_free(struct drm_fb_helper *helper)
+ }
+
+ /**
++ * drm_fb_helper_prepare - setup a drm_fb_helper structure
++ * @dev: DRM device
++ * @helper: driver-allocated fbdev helper structure to set up
++ * @funcs: pointer to structure of functions associate with this helper
++ *
++ * Sets up the bare minimum to make the framebuffer helper usable. This is
++ * useful to implement race-free initialization of the polling helpers.
++ */
++void drm_fb_helper_prepare(struct drm_device *dev, struct drm_fb_helper *helper,
++ const struct drm_fb_helper_funcs *funcs)
++{
++ INIT_LIST_HEAD(&helper->kernel_fb_list);
++ helper->funcs = funcs;
++ helper->dev = dev;
++}
++EXPORT_SYMBOL(drm_fb_helper_prepare);
++
++/**
+ * drm_fb_helper_init - initialize a drm_fb_helper structure
+ * @dev: drm device
+ * @fb_helper: driver-allocated fbdev helper structure to initialize
+@@ -542,8 +574,7 @@ static void drm_fb_helper_crtc_free(struct drm_fb_helper *helper)
+ * nor register the fbdev. This is only done in drm_fb_helper_initial_config()
+ * to allow driver writes more control over the exact init sequence.
+ *
+- * Drivers must set fb_helper->funcs before calling
+- * drm_fb_helper_initial_config().
++ * Drivers must call drm_fb_helper_prepare() before calling this function.
+ *
+ * RETURNS:
+ * Zero if everything went ok, nonzero otherwise.
+@@ -558,10 +589,6 @@ int drm_fb_helper_init(struct drm_device *dev,
+ if (!max_conn_count)
+ return -EINVAL;
+
+- fb_helper->dev = dev;
+-
+- INIT_LIST_HEAD(&fb_helper->kernel_fb_list);
+-
+ fb_helper->crtc_info = kcalloc(crtc_count, sizeof(struct drm_fb_helper_crtc), GFP_KERNEL);
+ if (!fb_helper->crtc_info)
+ return -ENOMEM;
+diff --git a/drivers/gpu/drm/exynos/exynos_drm_fbdev.c b/drivers/gpu/drm/exynos/exynos_drm_fbdev.c
+index fc25fe75aa77..32e63f60e1d1 100644
+--- a/drivers/gpu/drm/exynos/exynos_drm_fbdev.c
++++ b/drivers/gpu/drm/exynos/exynos_drm_fbdev.c
+@@ -266,7 +266,8 @@ int exynos_drm_fbdev_init(struct drm_device *dev)
+ return -ENOMEM;
+
+ private->fb_helper = helper = &fbdev->drm_fb_helper;
+- helper->funcs = &exynos_drm_fb_helper_funcs;
++
++ drm_fb_helper_prepare(dev, helper, &exynos_drm_fb_helper_funcs);
+
+ num_crtc = dev->mode_config.num_crtc;
+
+diff --git a/drivers/gpu/drm/gma500/framebuffer.c b/drivers/gpu/drm/gma500/framebuffer.c
+index 76e4d777d01d..d0dd3bea8aa5 100644
+--- a/drivers/gpu/drm/gma500/framebuffer.c
++++ b/drivers/gpu/drm/gma500/framebuffer.c
+@@ -600,7 +600,8 @@ int psb_fbdev_init(struct drm_device *dev)
+ }
+
+ dev_priv->fbdev = fbdev;
+- fbdev->psb_fb_helper.funcs = &psb_fb_helper_funcs;
++
++ drm_fb_helper_prepare(dev, &fbdev->psb_fb_helper, &psb_fb_helper_funcs);
+
+ drm_fb_helper_init(dev, &fbdev->psb_fb_helper, dev_priv->ops->crtcs,
+ INTELFB_CONN_LIMIT);
+diff --git a/drivers/gpu/drm/i915/intel_fbdev.c b/drivers/gpu/drm/i915/intel_fbdev.c
+index c14073094024..44e17fd781b8 100644
+--- a/drivers/gpu/drm/i915/intel_fbdev.c
++++ b/drivers/gpu/drm/i915/intel_fbdev.c
+@@ -649,7 +649,8 @@ int intel_fbdev_init(struct drm_device *dev)
+ if (ifbdev == NULL)
+ return -ENOMEM;
+
+- ifbdev->helper.funcs = &intel_fb_helper_funcs;
++ drm_fb_helper_prepare(dev, &ifbdev->helper, &intel_fb_helper_funcs);
++
+ if (!intel_fbdev_init_bios(dev, ifbdev))
+ ifbdev->preferred_bpp = 32;
+
+diff --git a/drivers/gpu/drm/mgag200/mgag200_fb.c b/drivers/gpu/drm/mgag200/mgag200_fb.c
+index a4319aba9180..5451dc58eff1 100644
+--- a/drivers/gpu/drm/mgag200/mgag200_fb.c
++++ b/drivers/gpu/drm/mgag200/mgag200_fb.c
+@@ -293,9 +293,10 @@ int mgag200_fbdev_init(struct mga_device *mdev)
+ return -ENOMEM;
+
+ mdev->mfbdev = mfbdev;
+- mfbdev->helper.funcs = &mga_fb_helper_funcs;
+ spin_lock_init(&mfbdev->dirty_lock);
+
++ drm_fb_helper_prepare(mdev->dev, &mfbdev->helper, &mga_fb_helper_funcs);
++
+ ret = drm_fb_helper_init(mdev->dev, &mfbdev->helper,
+ mdev->num_crtc, MGAG200FB_CONN_LIMIT);
+ if (ret)
+diff --git a/drivers/gpu/drm/msm/msm_fbdev.c b/drivers/gpu/drm/msm/msm_fbdev.c
+index c229bf6e865e..c7d6e85a7bf0 100644
+--- a/drivers/gpu/drm/msm/msm_fbdev.c
++++ b/drivers/gpu/drm/msm/msm_fbdev.c
+@@ -197,7 +197,7 @@ struct drm_fb_helper *msm_fbdev_init(struct drm_device *dev)
+
+ helper = &fbdev->base;
+
+- helper->funcs = &msm_fb_helper_funcs;
++ drm_fb_helper_prepare(dev, helper, &msm_fb_helper_funcs);
+
+ ret = drm_fb_helper_init(dev, helper,
+ priv->num_crtcs, priv->num_connectors);
+diff --git a/drivers/gpu/drm/nouveau/nouveau_fbcon.c b/drivers/gpu/drm/nouveau/nouveau_fbcon.c
+index 8e9c07b7fc89..afe706a20f97 100644
+--- a/drivers/gpu/drm/nouveau/nouveau_fbcon.c
++++ b/drivers/gpu/drm/nouveau/nouveau_fbcon.c
+@@ -464,7 +464,8 @@ nouveau_fbcon_init(struct drm_device *dev)
+
+ fbcon->dev = dev;
+ drm->fbcon = fbcon;
+- fbcon->helper.funcs = &nouveau_fbcon_helper_funcs;
++
++ drm_fb_helper_prepare(dev, &fbcon->helper, &nouveau_fbcon_helper_funcs);
+
+ ret = drm_fb_helper_init(dev, &fbcon->helper,
+ dev->mode_config.num_crtc, 4);
+diff --git a/drivers/gpu/drm/omapdrm/omap_fbdev.c b/drivers/gpu/drm/omapdrm/omap_fbdev.c
+index 4cb12083eb12..8436c6857cda 100644
+--- a/drivers/gpu/drm/omapdrm/omap_fbdev.c
++++ b/drivers/gpu/drm/omapdrm/omap_fbdev.c
+@@ -325,7 +325,7 @@ struct drm_fb_helper *omap_fbdev_init(struct drm_device *dev)
+
+ helper = &fbdev->base;
+
+- helper->funcs = &omap_fb_helper_funcs;
++ drm_fb_helper_prepare(dev, helper, &omap_fb_helper_funcs);
+
+ ret = drm_fb_helper_init(dev, helper,
+ priv->num_crtcs, priv->num_connectors);
+diff --git a/drivers/gpu/drm/qxl/qxl_fb.c b/drivers/gpu/drm/qxl/qxl_fb.c
+index cf89614c72be..df567888bb1e 100644
+--- a/drivers/gpu/drm/qxl/qxl_fb.c
++++ b/drivers/gpu/drm/qxl/qxl_fb.c
+@@ -676,9 +676,12 @@ int qxl_fbdev_init(struct qxl_device *qdev)
+
+ qfbdev->qdev = qdev;
+ qdev->mode_info.qfbdev = qfbdev;
+- qfbdev->helper.funcs = &qxl_fb_helper_funcs;
+ spin_lock_init(&qfbdev->delayed_ops_lock);
+ INIT_LIST_HEAD(&qfbdev->delayed_ops);
++
++ drm_fb_helper_prepare(qdev->ddev, &qfbdev->helper,
++ &qxl_fb_helper_funcs);
++
+ ret = drm_fb_helper_init(qdev->ddev, &qfbdev->helper,
+ qxl_num_crtc /* num_crtc - QXL supports just 1 */,
+ QXLFB_CONN_LIMIT);
+diff --git a/drivers/gpu/drm/radeon/radeon_fb.c b/drivers/gpu/drm/radeon/radeon_fb.c
+index ad97afdbc4c7..db598d712901 100644
+--- a/drivers/gpu/drm/radeon/radeon_fb.c
++++ b/drivers/gpu/drm/radeon/radeon_fb.c
+@@ -353,7 +353,9 @@ int radeon_fbdev_init(struct radeon_device *rdev)
+
+ rfbdev->rdev = rdev;
+ rdev->mode_info.rfbdev = rfbdev;
+- rfbdev->helper.funcs = &radeon_fb_helper_funcs;
++
++ drm_fb_helper_prepare(rdev->ddev, &rfbdev->helper,
++ &radeon_fb_helper_funcs);
+
+ ret = drm_fb_helper_init(rdev->ddev, &rfbdev->helper,
+ rdev->num_crtc,
+diff --git a/drivers/gpu/drm/tegra/fb.c b/drivers/gpu/drm/tegra/fb.c
+index f7cf47bf0afb..d5d53aa79ced 100644
+--- a/drivers/gpu/drm/tegra/fb.c
++++ b/drivers/gpu/drm/tegra/fb.c
+@@ -276,7 +276,6 @@ static struct tegra_fbdev *tegra_fbdev_create(struct drm_device *drm,
+ unsigned int num_crtc,
+ unsigned int max_connectors)
+ {
+- struct drm_fb_helper *helper;
+ struct tegra_fbdev *fbdev;
+ int err;
+
+@@ -286,8 +285,7 @@ static struct tegra_fbdev *tegra_fbdev_create(struct drm_device *drm,
+ return ERR_PTR(-ENOMEM);
+ }
+
+- fbdev->base.funcs = &tegra_fb_helper_funcs;
+- helper = &fbdev->base;
++ drm_fb_helper_prepare(drm, &fbdev->base, &tegra_fb_helper_funcs);
+
+ err = drm_fb_helper_init(drm, &fbdev->base, num_crtc, max_connectors);
+ if (err < 0) {
+diff --git a/drivers/gpu/drm/udl/udl_fb.c b/drivers/gpu/drm/udl/udl_fb.c
+index 0647c8cc368b..d1da339843ca 100644
+--- a/drivers/gpu/drm/udl/udl_fb.c
++++ b/drivers/gpu/drm/udl/udl_fb.c
+@@ -583,7 +583,8 @@ int udl_fbdev_init(struct drm_device *dev)
+ return -ENOMEM;
+
+ udl->fbdev = ufbdev;
+- ufbdev->helper.funcs = &udl_fb_helper_funcs;
++
++ drm_fb_helper_prepare(dev, &ufbdev->helper, &udl_fb_helper_funcs);
+
+ ret = drm_fb_helper_init(dev, &ufbdev->helper,
+ 1, 1);
+diff --git a/include/drm/drm_fb_helper.h b/include/drm/drm_fb_helper.h
+index a47df7ca38e8..1cf587f1f927 100644
+--- a/include/drm/drm_fb_helper.h
++++ b/include/drm/drm_fb_helper.h
+@@ -97,6 +97,8 @@ struct drm_fb_helper {
+ bool delayed_hotplug;
+ };
+
++void drm_fb_helper_prepare(struct drm_device *dev, struct drm_fb_helper *helper,
++ const struct drm_fb_helper_funcs *funcs);
+ int drm_fb_helper_init(struct drm_device *dev,
+ struct drm_fb_helper *helper, int crtc_count,
+ int max_conn);
+--
+2.0.0
diff --git a/linux-arm-tagr-04_04-drm-race_free.patch b/linux-arm-tagr-04_04-drm-race_free.patch
new file mode 100644
index 00000000..e7f9bcc7
--- /dev/null
+++ b/linux-arm-tagr-04_04-drm-race_free.patch
@@ -0,0 +1,179 @@
+From: Thierry Reding <treding@nvidia.com>
+
+A race condition currently exists on Tegra, where it can happen that a
+monitor attached via HDMI isn't detected during the initial FB helper
+setup, but the hotplug event happens too early to be processed by the
+poll helpers because they haven't been initialized yet. This happens
+because on some boards the HDMI driver can control the regulator that
+supplies the +5V pin on the HDMI connector. Therefore depending on the
+timing between the initialization of the HDMI driver and the rest of
+DRM, it's possible that the monitor returns the hotplug signal right
+within the window where we would miss it.
+
+Unfortunately, drm_kms_helper_poll_init() will wreak havoc when called
+before at least some parts of the FB helpers have been set up.
+
+This commit fixes this by splitting out the minimum of initialization
+required to make drm_kms_helper_poll_init() work into a separate
+function that can be called early. It is then safe to move all of the
+poll helper initialization to an earlier point in time (before the
+HDMI output driver has a chance to enable the +5V supply). That way if
+the hotplug signal is returned before the initial FB helper setup, the
+monitor will be forcefully detected at that point, and if the hotplug
+signal is returned after that it will be properly handled by the poll
+helpers.
+
+Signed-off-by: Thierry Reding <treding@nvidia.com>
+---
+ drivers/gpu/drm/tegra/drm.c | 8 ++++++--
+ drivers/gpu/drm/tegra/drm.h | 1 +
+ drivers/gpu/drm/tegra/fb.c | 47 ++++++++++++++++++++++++++++++---------------
+ 3 files changed, 39 insertions(+), 17 deletions(-)
+
+diff --git a/drivers/gpu/drm/tegra/drm.c b/drivers/gpu/drm/tegra/drm.c
+index 3396f9f6a9f7..fd736efd14bd 100644
+--- a/drivers/gpu/drm/tegra/drm.c
++++ b/drivers/gpu/drm/tegra/drm.c
+@@ -40,6 +40,12 @@ static int tegra_drm_load(struct drm_device *drm, unsigned long flags)
+
+ drm_mode_config_init(drm);
+
++ err = tegra_drm_fb_prepare(drm);
++ if (err < 0)
++ return err;
++
++ drm_kms_helper_poll_init(drm);
++
+ err = host1x_device_init(device);
+ if (err < 0)
+ return err;
+@@ -59,8 +65,6 @@ static int tegra_drm_load(struct drm_device *drm, unsigned long flags)
+ if (err < 0)
+ return err;
+
+- drm_kms_helper_poll_init(drm);
+-
+ return 0;
+ }
+
+diff --git a/drivers/gpu/drm/tegra/drm.h b/drivers/gpu/drm/tegra/drm.h
+index 6b8fe9d86ed4..0d30689dff01 100644
+--- a/drivers/gpu/drm/tegra/drm.h
++++ b/drivers/gpu/drm/tegra/drm.h
+@@ -280,6 +280,7 @@ struct tegra_bo *tegra_fb_get_plane(struct drm_framebuffer *framebuffer,
+ unsigned int index);
+ bool tegra_fb_is_bottom_up(struct drm_framebuffer *framebuffer);
+ bool tegra_fb_is_tiled(struct drm_framebuffer *framebuffer);
++int tegra_drm_fb_prepare(struct drm_device *drm);
+ int tegra_drm_fb_init(struct drm_device *drm);
+ void tegra_drm_fb_exit(struct drm_device *drm);
+ #ifdef CONFIG_DRM_TEGRA_FBDEV
+diff --git a/drivers/gpu/drm/tegra/fb.c b/drivers/gpu/drm/tegra/fb.c
+index d5d53aa79ced..fc1528e0bda1 100644
+--- a/drivers/gpu/drm/tegra/fb.c
++++ b/drivers/gpu/drm/tegra/fb.c
+@@ -271,13 +271,9 @@ static const struct drm_fb_helper_funcs tegra_fb_helper_funcs = {
+ .fb_probe = tegra_fbdev_probe,
+ };
+
+-static struct tegra_fbdev *tegra_fbdev_create(struct drm_device *drm,
+- unsigned int preferred_bpp,
+- unsigned int num_crtc,
+- unsigned int max_connectors)
++static struct tegra_fbdev *tegra_fbdev_create(struct drm_device *drm)
+ {
+ struct tegra_fbdev *fbdev;
+- int err;
+
+ fbdev = kzalloc(sizeof(*fbdev), GFP_KERNEL);
+ if (!fbdev) {
+@@ -287,10 +283,21 @@ static struct tegra_fbdev *tegra_fbdev_create(struct drm_device *drm,
+
+ drm_fb_helper_prepare(drm, &fbdev->base, &tegra_fb_helper_funcs);
+
++ return fbdev;
++}
++
++static int tegra_fbdev_init(struct tegra_fbdev *fbdev,
++ unsigned int preferred_bpp,
++ unsigned int num_crtc,
++ unsigned int max_connectors)
++{
++ struct drm_device *drm = fbdev->base.dev;
++ int err;
++
+ err = drm_fb_helper_init(drm, &fbdev->base, num_crtc, max_connectors);
+ if (err < 0) {
+ dev_err(drm->dev, "failed to initialize DRM FB helper\n");
+- goto free;
++ return err;
+ }
+
+ err = drm_fb_helper_single_add_all_connectors(&fbdev->base);
+@@ -299,21 +306,17 @@ static struct tegra_fbdev *tegra_fbdev_create(struct drm_device *drm,
+ goto fini;
+ }
+
+- drm_helper_disable_unused_functions(drm);
+-
+ err = drm_fb_helper_initial_config(&fbdev->base, preferred_bpp);
+ if (err < 0) {
+ dev_err(drm->dev, "failed to set initial configuration\n");
+ goto fini;
+ }
+
+- return fbdev;
++ return 0;
+
+ fini:
+ drm_fb_helper_fini(&fbdev->base);
+-free:
+- kfree(fbdev);
+- return ERR_PTR(err);
++ return err;
+ }
+
+ static void tegra_fbdev_free(struct tegra_fbdev *fbdev)
+@@ -364,7 +367,7 @@ static const struct drm_mode_config_funcs tegra_drm_mode_funcs = {
+ #endif
+ };
+
+-int tegra_drm_fb_init(struct drm_device *drm)
++int tegra_drm_fb_prepare(struct drm_device *drm)
+ {
+ #ifdef CONFIG_DRM_TEGRA_FBDEV
+ struct tegra_drm *tegra = drm->dev_private;
+@@ -379,8 +382,7 @@ int tegra_drm_fb_init(struct drm_device *drm)
+ drm->mode_config.funcs = &tegra_drm_mode_funcs;
+
+ #ifdef CONFIG_DRM_TEGRA_FBDEV
+- tegra->fbdev = tegra_fbdev_create(drm, 32, drm->mode_config.num_crtc,
+- drm->mode_config.num_connector);
++ tegra->fbdev = tegra_fbdev_create(drm);
+ if (IS_ERR(tegra->fbdev))
+ return PTR_ERR(tegra->fbdev);
+ #endif
+@@ -388,6 +390,21 @@ int tegra_drm_fb_init(struct drm_device *drm)
+ return 0;
+ }
+
++int tegra_drm_fb_init(struct drm_device *drm)
++{
++#ifdef CONFIG_DRM_TEGRA_FBDEV
++ struct tegra_drm *tegra = drm->dev_private;
++ int err;
++
++ err = tegra_fbdev_init(tegra->fbdev, 32, drm->mode_config.num_crtc,
++ drm->mode_config.num_connector);
++ if (err < 0)
++ return err;
++#endif
++
++ return 0;
++}
++
+ void tegra_drm_fb_exit(struct drm_device *drm)
+ {
+ #ifdef CONFIG_DRM_TEGRA_FBDEV
+--
+2.0.0