From 6aed8ec3f76a22217c9ae183d32b1aa990bed069 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Sun, 20 Jan 2013 17:32:21 +0100 Subject: drm: review locking for drm_fb_helper_restore_fbdev_mode ... it's required. Fix up exynos and the cma helper, and add a corresponding WARN_ON to drm_fb_helper_restore_fbdev_mode. Note that tegra calls the fbdev cma helper restore function also from it's driver-load callback. Which is a bit against current practice, since usually the call is only from ->lastclose, and initial setup is done by drm_fb_helper_initial_config. Also add the relevant drm DocBook entry. v2: Add promised WARN to restore_fbdev_mode. Reviewed-by: Rob Clark Signed-off-by: Daniel Vetter --- drivers/gpu/drm/drm_fb_helper.c | 11 +++++++++++ 1 file changed, 11 insertions(+) (limited to 'drivers/gpu/drm/drm_fb_helper.c') diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c index 0c6e25e979dd..77378a1e907e 100644 --- a/drivers/gpu/drm/drm_fb_helper.c +++ b/drivers/gpu/drm/drm_fb_helper.c @@ -239,10 +239,21 @@ int drm_fb_helper_debug_leave(struct fb_info *info) } EXPORT_SYMBOL(drm_fb_helper_debug_leave); +/** + * drm_fb_helper_restore_fbdev_mode - restore fbdev configuration + * @fb_helper: fbcon to restore + * + * This should be called from driver's drm->lastclose callback when implementing + * an fbcon on top of kms using this helper. This ensures that the user isn't + * greeted with a black screen when e.g. X dies. + */ bool drm_fb_helper_restore_fbdev_mode(struct drm_fb_helper *fb_helper) { bool error = false; int i, ret; + + drm_warn_on_modeset_not_all_locked(fb_helper->dev); + for (i = 0; i < fb_helper->crtc_count; i++) { struct drm_mode_set *mode_set = &fb_helper->crtc_info[i].mode_set; ret = drm_mode_set_config_internal(mode_set); -- cgit v1.2.3 From d21bf469d5301d025cd82997bb1529bcdc7086af Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Sun, 20 Jan 2013 18:09:52 +0100 Subject: drm/fb-helper: kill drm_fb_helper_restore It's only used internally for the sysrq and panic handlers provided by the drm fb helper implementation. Hence just inline it, kill the export and remove the confusing kerneldoc. Driver's are supposed to call drm_fb_helper_restore_fbdev_mode on lastclose. Note that locking is totally fubar - the sysrq case doesn't take any locks at all. The panic handler probably shouldn't take any locks since it'll only make things worse. Otoh it's probably better to switch things over to the atomic modeset callbacks (and disable the panic handler for those drivers which don't implement it). But that's both better done in separate patches. Reviewed-by: Rob Clark Signed-off-by: Daniel Vetter --- drivers/gpu/drm/drm_fb_helper.c | 23 ++++++++--------------- include/drm/drm_fb_helper.h | 1 - 2 files changed, 8 insertions(+), 16 deletions(-) (limited to 'drivers/gpu/drm/drm_fb_helper.c') diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c index 77378a1e907e..14a81fc3e01a 100644 --- a/drivers/gpu/drm/drm_fb_helper.c +++ b/drivers/gpu/drm/drm_fb_helper.c @@ -264,6 +264,10 @@ bool drm_fb_helper_restore_fbdev_mode(struct drm_fb_helper *fb_helper) } EXPORT_SYMBOL(drm_fb_helper_restore_fbdev_mode); +/* + * restore fbcon display for all kms driver's using this helper, used for sysrq + * and panic handling. + */ static bool drm_fb_helper_force_kernel_mode(void) { bool ret, error = false; @@ -302,20 +306,6 @@ static struct notifier_block paniced = { .notifier_call = drm_fb_helper_panic, }; -/** - * drm_fb_helper_restore - restore the framebuffer console (kernel) config - * - * Restore's the kernel's fbcon mode, used for lastclose & panic paths. - */ -void drm_fb_helper_restore(void) -{ - bool ret; - ret = drm_fb_helper_force_kernel_mode(); - if (ret == true) - DRM_ERROR("Failed to restore crtc configuration\n"); -} -EXPORT_SYMBOL(drm_fb_helper_restore); - static bool drm_fb_helper_is_bound(struct drm_fb_helper *fb_helper) { struct drm_device *dev = fb_helper->dev; @@ -337,7 +327,10 @@ static bool drm_fb_helper_is_bound(struct drm_fb_helper *fb_helper) #ifdef CONFIG_MAGIC_SYSRQ static void drm_fb_helper_restore_work_fn(struct work_struct *ignored) { - drm_fb_helper_restore(); + bool ret; + ret = drm_fb_helper_force_kernel_mode(); + if (ret == true) + DRM_ERROR("Failed to restore crtc configuration\n"); } static DECLARE_WORK(drm_fb_helper_restore_work, drm_fb_helper_restore_work_fn); diff --git a/include/drm/drm_fb_helper.h b/include/drm/drm_fb_helper.h index 5120b01c2eeb..ba32505efb02 100644 --- a/include/drm/drm_fb_helper.h +++ b/include/drm/drm_fb_helper.h @@ -103,7 +103,6 @@ int drm_fb_helper_setcolreg(unsigned regno, struct fb_info *info); bool drm_fb_helper_restore_fbdev_mode(struct drm_fb_helper *fb_helper); -void drm_fb_helper_restore(void); void drm_fb_helper_fill_var(struct fb_info *info, struct drm_fb_helper *fb_helper, uint32_t fb_width, uint32_t fb_height); void drm_fb_helper_fill_fix(struct fb_info *info, uint32_t pitch, -- cgit v1.2.3 From 43c8a849a1e92fba6ea4493ed950d2a2e31ac87c Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Sun, 20 Jan 2013 18:18:07 +0100 Subject: drm/fb-helper: unexport drm_fb_helper_panic It doesn't even show up in any header files and only used iternally. Originally it was (ab)used to restore the fbcon on lastclose, but that died with commit e8e7a2b8ccfdae0d4cb6bd25824bbedcd42da316 Author: Dave Airlie Date: Thu Apr 21 22:18:32 2011 +0100 drm/i915: restore only the mode of this driver on lastclose (v2) Reviewed-by: Rob Clark Signed-off-by: Daniel Vetter --- drivers/gpu/drm/drm_fb_helper.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'drivers/gpu/drm/drm_fb_helper.c') diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c index 14a81fc3e01a..eaab092b995d 100644 --- a/drivers/gpu/drm/drm_fb_helper.c +++ b/drivers/gpu/drm/drm_fb_helper.c @@ -287,7 +287,7 @@ static bool drm_fb_helper_force_kernel_mode(void) return error; } -int drm_fb_helper_panic(struct notifier_block *n, unsigned long ununsed, +static int drm_fb_helper_panic(struct notifier_block *n, unsigned long ununsed, void *panic_str) { /* @@ -300,7 +300,6 @@ int drm_fb_helper_panic(struct notifier_block *n, unsigned long ununsed, pr_err("panic occurred, switching back to text console\n"); return drm_fb_helper_force_kernel_mode(); } -EXPORT_SYMBOL(drm_fb_helper_panic); static struct notifier_block paniced = { .notifier_call = drm_fb_helper_panic, -- cgit v1.2.3 From de1ace5b56d11db26abe69fd19754f91c326763f Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Sun, 20 Jan 2013 21:50:49 +0100 Subject: drm/fb-helper: unexport drm_fb_helper_single_fb_probe Not called by anyone, and really, shouldn't be. Drivers are supposed either drm_fb_helper_initial_config or drm_fb_helper_hotplug_event. Originally this was done differently, but is now consolidated in the helper functions and no longer done by drivers directly. Reviewed-by: Rob Clark Signed-off-by: Daniel Vetter --- drivers/gpu/drm/drm_fb_helper.c | 5 ++--- include/drm/drm_fb_helper.h | 3 --- 2 files changed, 2 insertions(+), 6 deletions(-) (limited to 'drivers/gpu/drm/drm_fb_helper.c') diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c index eaab092b995d..f5d362680f2b 100644 --- a/drivers/gpu/drm/drm_fb_helper.c +++ b/drivers/gpu/drm/drm_fb_helper.c @@ -754,8 +754,8 @@ int drm_fb_helper_pan_display(struct fb_var_screeninfo *var, } EXPORT_SYMBOL(drm_fb_helper_pan_display); -int drm_fb_helper_single_fb_probe(struct drm_fb_helper *fb_helper, - int preferred_bpp) +static int drm_fb_helper_single_fb_probe(struct drm_fb_helper *fb_helper, + int preferred_bpp) { int new_fb = 0; int crtc_count = 0; @@ -870,7 +870,6 @@ int drm_fb_helper_single_fb_probe(struct drm_fb_helper *fb_helper, return 0; } -EXPORT_SYMBOL(drm_fb_helper_single_fb_probe); void drm_fb_helper_fill_fix(struct fb_info *info, uint32_t pitch, uint32_t depth) diff --git a/include/drm/drm_fb_helper.h b/include/drm/drm_fb_helper.h index ba32505efb02..973402d6effe 100644 --- a/include/drm/drm_fb_helper.h +++ b/include/drm/drm_fb_helper.h @@ -82,9 +82,6 @@ struct drm_fb_helper { bool delayed_hotplug; }; -int drm_fb_helper_single_fb_probe(struct drm_fb_helper *helper, - int preferred_bpp); - int drm_fb_helper_init(struct drm_device *dev, struct drm_fb_helper *helper, int crtc_count, int max_conn); -- cgit v1.2.3 From 76a39dbfb2d1bc45219839e5a95d4ceaf6ca114f Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Sun, 20 Jan 2013 23:12:54 +0100 Subject: drm/fb-helper: don't disable everything in initial_config This should be done in the drivers for two reasons: - it gets in the way of fastboot efforts - it links the fb helpers with the crtc helpers instead of going through the real interface vfuncs, forcing i915 to fake all the ->disable callbacks used by the crtc helper to avoid ugly Oopsen v2: Resolve conflicts since drivers still call drm_fb_helper_single_add_all_connectors. Reviewed-by: Rob Clark Signed-off-by: Daniel Vetter --- drivers/gpu/drm/ast/ast_fb.c | 5 +++++ drivers/gpu/drm/cirrus/cirrus_fbdev.c | 4 ++++ drivers/gpu/drm/drm_fb_cma_helper.c | 3 +++ drivers/gpu/drm/drm_fb_helper.c | 3 --- drivers/gpu/drm/exynos/exynos_drm_fbdev.c | 3 +++ drivers/gpu/drm/gma500/framebuffer.c | 4 ++++ drivers/gpu/drm/i915/intel_fb.c | 3 +++ drivers/gpu/drm/mgag200/mgag200_fb.c | 5 +++++ drivers/gpu/drm/nouveau/nouveau_fbcon.c | 3 +++ drivers/gpu/drm/radeon/radeon_fb.c | 4 ++++ drivers/gpu/drm/udl/udl_fb.c | 4 ++++ drivers/staging/omapdrm/omap_fbdev.c | 4 ++++ 12 files changed, 42 insertions(+), 3 deletions(-) (limited to 'drivers/gpu/drm/drm_fb_helper.c') diff --git a/drivers/gpu/drm/ast/ast_fb.c b/drivers/gpu/drm/ast/ast_fb.c index 3e6584b940dc..81763cad9940 100644 --- a/drivers/gpu/drm/ast/ast_fb.c +++ b/drivers/gpu/drm/ast/ast_fb.c @@ -40,6 +40,7 @@ #include #include #include +#include #include "ast_drv.h" static void ast_dirty_update(struct ast_fbdev *afbdev, @@ -314,6 +315,10 @@ int ast_fbdev_init(struct drm_device *dev) } drm_fb_helper_single_add_all_connectors(&afbdev->helper); + + /* disable all the possible outputs/crtcs before entering KMS mode */ + drm_helper_disable_unused_functions(dev); + drm_fb_helper_initial_config(&afbdev->helper, 32); return 0; } diff --git a/drivers/gpu/drm/cirrus/cirrus_fbdev.c b/drivers/gpu/drm/cirrus/cirrus_fbdev.c index 3daea0f638c3..b96605c6e1b6 100644 --- a/drivers/gpu/drm/cirrus/cirrus_fbdev.c +++ b/drivers/gpu/drm/cirrus/cirrus_fbdev.c @@ -11,6 +11,7 @@ #include #include #include +#include #include @@ -291,6 +292,9 @@ int cirrus_fbdev_init(struct cirrus_device *cdev) return ret; } drm_fb_helper_single_add_all_connectors(&gfbdev->helper); + + /* disable all the possible outputs/crtcs before entering KMS mode */ + drm_helper_disable_unused_functions(cdev->dev); drm_fb_helper_initial_config(&gfbdev->helper, bpp_sel); return 0; diff --git a/drivers/gpu/drm/drm_fb_cma_helper.c b/drivers/gpu/drm/drm_fb_cma_helper.c index 1b6ba2d4d60c..ef3d33a8a7e2 100644 --- a/drivers/gpu/drm/drm_fb_cma_helper.c +++ b/drivers/gpu/drm/drm_fb_cma_helper.c @@ -333,6 +333,9 @@ struct drm_fbdev_cma *drm_fbdev_cma_init(struct drm_device *dev, } + /* disable all the possible outputs/crtcs before entering KMS mode */ + drm_helper_disable_unused_functions(dev); + ret = drm_fb_helper_initial_config(helper, preferred_bpp); if (ret < 0) { dev_err(dev->dev, "Failed to set inital hw configuration.\n"); diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c index f5d362680f2b..d841b68aaa3e 100644 --- a/drivers/gpu/drm/drm_fb_helper.c +++ b/drivers/gpu/drm/drm_fb_helper.c @@ -1360,9 +1360,6 @@ bool drm_fb_helper_initial_config(struct drm_fb_helper *fb_helper, int bpp_sel) struct drm_device *dev = fb_helper->dev; int count = 0; - /* disable all the possible outputs/crtcs before entering KMS mode */ - drm_helper_disable_unused_functions(fb_helper->dev); - drm_fb_helper_parse_command_line(fb_helper); count = drm_fb_helper_probe_connector_modes(fb_helper, diff --git a/drivers/gpu/drm/exynos/exynos_drm_fbdev.c b/drivers/gpu/drm/exynos/exynos_drm_fbdev.c index 086d0f79785f..fe2a0f068af7 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_fbdev.c +++ b/drivers/gpu/drm/exynos/exynos_drm_fbdev.c @@ -295,6 +295,9 @@ int exynos_drm_fbdev_init(struct drm_device *dev) } + /* disable all the possible outputs/crtcs before entering KMS mode */ + drm_helper_disable_unused_functions(dev); + ret = drm_fb_helper_initial_config(helper, PREFERRED_BPP); if (ret < 0) { DRM_ERROR("failed to set up hw configuration.\n"); diff --git a/drivers/gpu/drm/gma500/framebuffer.c b/drivers/gpu/drm/gma500/framebuffer.c index c1ef37e2efdf..fee3bf85af4a 100644 --- a/drivers/gpu/drm/gma500/framebuffer.c +++ b/drivers/gpu/drm/gma500/framebuffer.c @@ -616,6 +616,10 @@ int psb_fbdev_init(struct drm_device *dev) INTELFB_CONN_LIMIT); drm_fb_helper_single_add_all_connectors(&fbdev->psb_fb_helper); + + /* disable all the possible outputs/crtcs before entering KMS mode */ + drm_helper_disable_unused_functions(dev); + drm_fb_helper_initial_config(&fbdev->psb_fb_helper, 32); return 0; } diff --git a/drivers/gpu/drm/i915/intel_fb.c b/drivers/gpu/drm/i915/intel_fb.c index 1c510da04d16..e67061249934 100644 --- a/drivers/gpu/drm/i915/intel_fb.c +++ b/drivers/gpu/drm/i915/intel_fb.c @@ -258,6 +258,9 @@ void intel_fbdev_initial_config(struct drm_device *dev) { drm_i915_private_t *dev_priv = dev->dev_private; + /* disable all the possible outputs/crtcs before entering KMS mode */ + drm_helper_disable_unused_functions(dev); + /* Due to peculiar init order wrt to hpd handling this is separate. */ drm_fb_helper_initial_config(&dev_priv->fbdev->helper, 32); } diff --git a/drivers/gpu/drm/mgag200/mgag200_fb.c b/drivers/gpu/drm/mgag200/mgag200_fb.c index 5c69b432f99a..5bded5b74eaf 100644 --- a/drivers/gpu/drm/mgag200/mgag200_fb.c +++ b/drivers/gpu/drm/mgag200/mgag200_fb.c @@ -13,6 +13,7 @@ #include #include #include +#include #include @@ -278,6 +279,10 @@ int mgag200_fbdev_init(struct mga_device *mdev) return ret; } drm_fb_helper_single_add_all_connectors(&mfbdev->helper); + + /* disable all the possible outputs/crtcs before entering KMS mode */ + drm_helper_disable_unused_functions(mdev->dev); + drm_fb_helper_initial_config(&mfbdev->helper, 32); return 0; diff --git a/drivers/gpu/drm/nouveau/nouveau_fbcon.c b/drivers/gpu/drm/nouveau/nouveau_fbcon.c index d4ecb4deb484..b1ebfe30f912 100644 --- a/drivers/gpu/drm/nouveau/nouveau_fbcon.c +++ b/drivers/gpu/drm/nouveau/nouveau_fbcon.c @@ -491,6 +491,9 @@ nouveau_fbcon_init(struct drm_device *dev) else preferred_bpp = 32; + /* disable all the possible outputs/crtcs before entering KMS mode */ + drm_helper_disable_unused_functions(dev); + drm_fb_helper_initial_config(&fbcon->helper, preferred_bpp); return 0; } diff --git a/drivers/gpu/drm/radeon/radeon_fb.c b/drivers/gpu/drm/radeon/radeon_fb.c index 515e5ee1f9ee..b48d1c8cf9eb 100644 --- a/drivers/gpu/drm/radeon/radeon_fb.c +++ b/drivers/gpu/drm/radeon/radeon_fb.c @@ -379,6 +379,10 @@ int radeon_fbdev_init(struct radeon_device *rdev) } drm_fb_helper_single_add_all_connectors(&rfbdev->helper); + + /* disable all the possible outputs/crtcs before entering KMS mode */ + drm_helper_disable_unused_functions(rdev->ddev); + drm_fb_helper_initial_config(&rfbdev->helper, bpp_sel); return 0; } diff --git a/drivers/gpu/drm/udl/udl_fb.c b/drivers/gpu/drm/udl/udl_fb.c index b9feec9d08d3..cf5d05a0d955 100644 --- a/drivers/gpu/drm/udl/udl_fb.c +++ b/drivers/gpu/drm/udl/udl_fb.c @@ -619,6 +619,10 @@ int udl_fbdev_init(struct drm_device *dev) } drm_fb_helper_single_add_all_connectors(&ufbdev->helper); + + /* disable all the possible outputs/crtcs before entering KMS mode */ + drm_helper_disable_unused_functions(dev); + drm_fb_helper_initial_config(&ufbdev->helper, bpp_sel); return 0; } diff --git a/drivers/staging/omapdrm/omap_fbdev.c b/drivers/staging/omapdrm/omap_fbdev.c index 2728e37e02be..7e66eb138315 100644 --- a/drivers/staging/omapdrm/omap_fbdev.c +++ b/drivers/staging/omapdrm/omap_fbdev.c @@ -369,6 +369,10 @@ struct drm_fb_helper *omap_fbdev_init(struct drm_device *dev) } drm_fb_helper_single_add_all_connectors(helper); + + /* disable all the possible outputs/crtcs before entering KMS mode */ + drm_helper_disable_unused_functions(dev); + drm_fb_helper_initial_config(helper, 32); priv->fbdev = helper; -- cgit v1.2.3 From 7e53f3a423146745a4e4bb93362d488dfad502a8 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Mon, 21 Jan 2013 10:52:17 +0100 Subject: drm/fb-helper: fixup set_config semantics While doing the modeset rework for drm/i915 I've noticed that the fb helper is very liberal with the semantics of the ->set_config interface: - It doesn't bother clearing stale modes (e.g. when unplugging a screen). - It unconditionally sets the fb, even if no mode will be set on a given crtc. - The initial setup is a bit fun since we need to pick crtcs to decide the desired fb size, but also should set the modeset->fb pointer. Explain what's going on in the fixup code after the fb is allocated. The crtc helper didn't really care, but the new i915 modeset infrastructure did, so I've had to add a bunch of special-cases to catch this. Fix this all up and enforce the interface by converting the checks in drm/i915/intel_display.c to BUG_ONs. v2: Fix commit message spell fail spotted by Rob Clark. Reviewed-by: Rob Clark Signed-off-by: Daniel Vetter --- drivers/gpu/drm/drm_fb_helper.c | 27 +++++++++++++++++++++++---- drivers/gpu/drm/i915/intel_display.c | 11 +++-------- 2 files changed, 26 insertions(+), 12 deletions(-) (limited to 'drivers/gpu/drm/drm_fb_helper.c') diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c index d841b68aaa3e..809ef99f910e 100644 --- a/drivers/gpu/drm/drm_fb_helper.c +++ b/drivers/gpu/drm/drm_fb_helper.c @@ -689,7 +689,6 @@ int drm_fb_helper_set_par(struct fb_info *info) struct drm_fb_helper *fb_helper = info->par; struct drm_device *dev = fb_helper->dev; struct fb_var_screeninfo *var = &info->var; - struct drm_crtc *crtc; int ret; int i; @@ -700,7 +699,6 @@ int drm_fb_helper_set_par(struct fb_info *info) drm_modeset_lock_all(dev); for (i = 0; i < fb_helper->crtc_count; i++) { - crtc = fb_helper->crtc_info[i].mode_set.crtc; ret = drm_mode_set_config_internal(&fb_helper->crtc_info[i].mode_set); if (ret) { drm_modeset_unlock_all(dev); @@ -841,9 +839,17 @@ static int drm_fb_helper_single_fb_probe(struct drm_fb_helper *fb_helper, info = fb_helper->fbdev; - /* set the fb pointer */ + /* + * Set the fb pointer - usually drm_setup_crtcs does this for hotplug + * events, but at init time drm_setup_crtcs needs to be called before + * the fb is allocated (since we need to figure out the desired size of + * the fb before we can allocate it ...). Hence we need to fix things up + * here again. + */ for (i = 0; i < fb_helper->crtc_count; i++) - fb_helper->crtc_info[i].mode_set.fb = fb_helper->fb; + if (fb_helper->crtc_info[i].mode_set.num_connectors) + fb_helper->crtc_info[i].mode_set.fb = fb_helper->fb; + if (new_fb) { info->var.pixclock = 0; @@ -1314,6 +1320,7 @@ static void drm_setup_crtcs(struct drm_fb_helper *fb_helper) for (i = 0; i < fb_helper->crtc_count; i++) { modeset = &fb_helper->crtc_info[i].mode_set; modeset->num_connectors = 0; + modeset->fb = NULL; } for (i = 0; i < fb_helper->connector_count; i++) { @@ -1330,9 +1337,21 @@ static void drm_setup_crtcs(struct drm_fb_helper *fb_helper) modeset->mode = drm_mode_duplicate(dev, fb_crtc->desired_mode); modeset->connectors[modeset->num_connectors++] = fb_helper->connector_info[i]->connector; + modeset->fb = fb_helper->fb; } } + /* Clear out any old modes if there are no more connected outputs. */ + for (i = 0; i < fb_helper->crtc_count; i++) { + modeset = &fb_helper->crtc_info[i].mode_set; + if (modeset->num_connectors == 0) { + BUG_ON(modeset->fb); + BUG_ON(modeset->num_connectors); + if (modeset->mode) + drm_mode_destroy(dev, modeset->mode); + modeset->mode = NULL; + } + } out: kfree(crtcs); kfree(modes); diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 24f2654338d2..ca8d5929063e 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -7978,14 +7978,9 @@ static int intel_crtc_set_config(struct drm_mode_set *set) BUG_ON(!set->crtc); BUG_ON(!set->crtc->helper_private); - if (!set->mode) - set->fb = NULL; - - /* The fb helper likes to play gross jokes with ->mode_set_config. - * Unfortunately the crtc helper doesn't do much at all for this case, - * so we have to cope with this madness until the fb helper is fixed up. */ - if (set->fb && set->num_connectors == 0) - return 0; + /* Enforce sane interface api - has been abused by the fb helper. */ + BUG_ON(!set->mode && set->fb); + BUG_ON(set->fb && set->num_connectors == 0); if (set->fb) { DRM_DEBUG_KMS("[CRTC:%d] [FB:%d] #connectors=%d (x y) (%i %i)\n", -- cgit v1.2.3 From 2180c3c7e76536ce8ff0fd957a11dbfaa1e93403 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Mon, 21 Jan 2013 23:12:36 +0100 Subject: drm/fb-helper: directly call set_par from the hotplug handler The idea behind calling down into the driver's ->fb_probe function on each hotplug seems to be able to reallocate the backing storage (if e.g. a screen with higher resolution gets added). But that requires quite a bit of work in the fb helper itself, since currently we limit new screens to the currently allocated fb. An no kms driver supports fbdev fb resizing. So don't bother and start to simplify the code by calling drm_fb_helper_set_par directly from the fbdev hotplug function, since that's the only thing left in drm_fb_helper_single_fb_probe which does not concern itself with fb allocation and initial setup. Follow-on patches will streamline the initial setup code. Reviewed-by: Rob Clark Signed-off-by: Daniel Vetter --- drivers/gpu/drm/drm_fb_helper.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers/gpu/drm/drm_fb_helper.c') diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c index 809ef99f910e..535c2cadf4d1 100644 --- a/drivers/gpu/drm/drm_fb_helper.c +++ b/drivers/gpu/drm/drm_fb_helper.c @@ -859,8 +859,6 @@ static int drm_fb_helper_single_fb_probe(struct drm_fb_helper *fb_helper, dev_info(fb_helper->dev->dev, "fb%d: %s frame buffer device\n", info->node, info->fix.id); - } else { - drm_fb_helper_set_par(info); } /* Switch back to kernel console on panic */ @@ -1436,7 +1434,9 @@ int drm_fb_helper_hotplug_event(struct drm_fb_helper *fb_helper) drm_setup_crtcs(fb_helper); drm_modeset_unlock_all(dev); - return drm_fb_helper_single_fb_probe(fb_helper, bpp_sel); + drm_fb_helper_set_par(fb_helper->fbdev); + + return 0; } EXPORT_SYMBOL(drm_fb_helper_hotplug_event); -- cgit v1.2.3 From 8acf658ad6246be7913647e6e76da6a539bff425 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Mon, 21 Jan 2013 23:38:37 +0100 Subject: drm/fb-helper: streamline drm_fb_helper_single_fb_probe No need to check whether we've allocated a new fb since we're not always doing that. Also, we always need to register the fbdev and add it to the panic notifier. Reviewed-by: Rob Clark Signed-off-by: Daniel Vetter --- drivers/gpu/drm/drm_fb_helper.c | 29 +++++++++++++++-------------- 1 file changed, 15 insertions(+), 14 deletions(-) (limited to 'drivers/gpu/drm/drm_fb_helper.c') diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c index 535c2cadf4d1..e0d4b04c0eb5 100644 --- a/drivers/gpu/drm/drm_fb_helper.c +++ b/drivers/gpu/drm/drm_fb_helper.c @@ -752,10 +752,14 @@ int drm_fb_helper_pan_display(struct fb_var_screeninfo *var, } EXPORT_SYMBOL(drm_fb_helper_pan_display); +/* + * Allocates the backing storage through the ->fb_probe callback and then + * registers the fbdev and sets up the panic notifier. + */ static int drm_fb_helper_single_fb_probe(struct drm_fb_helper *fb_helper, int preferred_bpp) { - int new_fb = 0; + int ret = 0; int crtc_count = 0; int i; struct fb_info *info; @@ -833,9 +837,9 @@ static int drm_fb_helper_single_fb_probe(struct drm_fb_helper *fb_helper, } /* push down into drivers */ - new_fb = (*fb_helper->funcs->fb_probe)(fb_helper, &sizes); - if (new_fb < 0) - return new_fb; + ret = (*fb_helper->funcs->fb_probe)(fb_helper, &sizes); + if (ret < 0) + return ret; info = fb_helper->fbdev; @@ -851,15 +855,12 @@ static int drm_fb_helper_single_fb_probe(struct drm_fb_helper *fb_helper, fb_helper->crtc_info[i].mode_set.fb = fb_helper->fb; - if (new_fb) { - info->var.pixclock = 0; - if (register_framebuffer(info) < 0) - return -EINVAL; - - dev_info(fb_helper->dev->dev, "fb%d: %s frame buffer device\n", - info->node, info->fix.id); + info->var.pixclock = 0; + if (register_framebuffer(info) < 0) + return -EINVAL; - } + dev_info(fb_helper->dev->dev, "fb%d: %s frame buffer device\n", + info->node, info->fix.id); /* Switch back to kernel console on panic */ /* multi card linked list maybe */ @@ -869,8 +870,8 @@ static int drm_fb_helper_single_fb_probe(struct drm_fb_helper *fb_helper, &paniced); register_sysrq_key('v', &sysrq_drm_fb_helper_restore_op); } - if (new_fb) - list_add(&fb_helper->kernel_fb_list, &kernel_fb_helper_list); + + list_add(&fb_helper->kernel_fb_list, &kernel_fb_helper_list); return 0; } -- cgit v1.2.3 From 207fd32970b1def91b11ae28f6bebffc792db714 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Sun, 20 Jan 2013 22:13:14 +0100 Subject: drm/fb-helper: improve kerneldoc Now that the fbdev helper interface for drivers is trimmed down, update the kerneldoc for all the remaining exported functions. I've tried to beat the DocBook a bit by reordering the function references a bit into a more sensible ordering. But that didn't work out at all. Hence just extend the in-code DOC: section a bit. Also remove the LOCKING: sections - especially for the setup functions they're totally bogus. But that's not a documentation problem, but simply an artifact of the current rather hazardous locking around drm init and even more so around fbdev setup ... v2: Some further improvements: - Also add documentation for drm_fb_helper_single_add_all_connectors, Dave Airlie didn't want me to kill this one from the fb helper interface. - Update docs for drm_fb_helper_fill_var/fix - they should be used from the driver's ->fb_probe callback to setup the fbdev info structure. - Clarify what the ->fb_probe callback should all do - it needs to setup both the fbdev info and allocate the drm framebuffer used as backing storage. - Add basic documentaation for the drm_fb_helper_funcs driver callback vfunc. v3: Implement clarifications Laurent Pinchart suggested in his review. v4: Fix another mispelling Laurent spotted. Cc: Laurent Pinchart Acked-by: Laurent Pinchart Signed-off-by: Daniel Vetter --- Documentation/DocBook/drm.tmpl | 1 + drivers/gpu/drm/drm_fb_helper.c | 148 ++++++++++++++++++++++++++++++++++++---- include/drm/drm_fb_helper.h | 12 ++++ 3 files changed, 146 insertions(+), 15 deletions(-) (limited to 'drivers/gpu/drm/drm_fb_helper.c') diff --git a/Documentation/DocBook/drm.tmpl b/Documentation/DocBook/drm.tmpl index b26de523ab70..51e1904ac4c7 100644 --- a/Documentation/DocBook/drm.tmpl +++ b/Documentation/DocBook/drm.tmpl @@ -2143,6 +2143,7 @@ void intel_crt_init(struct drm_device *dev) fbdev Helper Functions Reference !Pdrivers/gpu/drm/drm_fb_helper.c fbdev helpers !Edrivers/gpu/drm/drm_fb_helper.c +!Iinclude/drm/drm_fb_helper.h Display Port Helper Functions Reference diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c index e0d4b04c0eb5..94f304d8b795 100644 --- a/drivers/gpu/drm/drm_fb_helper.c +++ b/drivers/gpu/drm/drm_fb_helper.c @@ -52,9 +52,36 @@ static LIST_HEAD(kernel_fb_helper_list); * mode setting driver. They can be used mostly independantely from the crtc * 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 beheviour can override the + * second 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 + * should also notify the fb helper code from updates to the output + * configuration by calling drm_fb_helper_hotplug_event(). For easier + * integration with the output polling code in drm_crtc_helper.c the modeset + * code proves a ->output_poll_changed callback. + * + * All other functions exported by the fb helper library can be used to + * implement the fbdev driver interface by the driver. */ -/* simple single crtc case helper function */ +/** + * drm_fb_helper_single_add_all_connectors() - add all connectors to fbdev + * emulation helper + * @fb_helper: fbdev initialized with drm_fb_helper_init + * + * This functions adds all the available connectors for use with the given + * fb_helper. This is a separate step to allow drivers to freely assign + * connectors to the fbdev, e.g. if some are reserved for special purposes or + * not adequate to be used for the fbcon. + * + * Since this is part of the initial setup before the fbdev is published, no + * locking is required. + */ int drm_fb_helper_single_add_all_connectors(struct drm_fb_helper *fb_helper) { struct drm_device *dev = fb_helper->dev; @@ -163,6 +190,10 @@ static void drm_fb_helper_restore_lut_atomic(struct drm_crtc *crtc) crtc->funcs->gamma_set(crtc, r_base, g_base, b_base, 0, crtc->gamma_size); } +/** + * drm_fb_helper_debug_enter - implementation for ->fb_debug_enter + * @info: fbdev registered by the helper + */ int drm_fb_helper_debug_enter(struct fb_info *info) { struct drm_fb_helper *helper = info->par; @@ -208,6 +239,10 @@ static struct drm_framebuffer *drm_mode_config_fb(struct drm_crtc *crtc) return NULL; } +/** + * drm_fb_helper_debug_leave - implementation for ->fb_debug_leave + * @info: fbdev registered by the helper + */ int drm_fb_helper_debug_leave(struct fb_info *info) { struct drm_fb_helper *helper = info->par; @@ -243,9 +278,9 @@ EXPORT_SYMBOL(drm_fb_helper_debug_leave); * drm_fb_helper_restore_fbdev_mode - restore fbdev configuration * @fb_helper: fbcon to restore * - * This should be called from driver's drm->lastclose callback when implementing - * an fbcon on top of kms using this helper. This ensures that the user isn't - * greeted with a black screen when e.g. X dies. + * This should be called from driver's drm ->lastclose callback + * when implementing an fbcon on top of kms using this helper. This ensures that + * the user isn't greeted with a black screen when e.g. X dies. */ bool drm_fb_helper_restore_fbdev_mode(struct drm_fb_helper *fb_helper) { @@ -381,6 +416,11 @@ static void drm_fb_helper_dpms(struct fb_info *info, int dpms_mode) drm_modeset_unlock_all(dev); } +/** + * drm_fb_helper_blank - implementation for ->fb_blank + * @blank: desired blanking state + * @info: fbdev registered by the helper + */ int drm_fb_helper_blank(int blank, struct fb_info *info) { switch (blank) { @@ -424,6 +464,24 @@ static void drm_fb_helper_crtc_free(struct drm_fb_helper *helper) kfree(helper->crtc_info); } +/** + * drm_fb_helper_init - initialize a drm_fb_helper structure + * @dev: drm device + * @fb_helper: driver-allocated fbdev helper structure to initialize + * @crtc_count: maximum number of crtcs to support in this fbdev emulation + * @max_conn_count: max connector count + * + * This allocates the structures for the fbdev helper with the given limits. + * Note that this won't yet touch the hardware (through the driver interfaces) + * 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(). + * + * RETURNS: + * Zero if everything went ok, nonzero otherwise. + */ int drm_fb_helper_init(struct drm_device *dev, struct drm_fb_helper *fb_helper, int crtc_count, int max_conn_count) @@ -552,6 +610,11 @@ static int setcolreg(struct drm_crtc *crtc, u16 red, u16 green, return 0; } +/** + * drm_fb_helper_setcmap - implementation for ->fb_setcmap + * @cmap: cmap to set + * @info: fbdev registered by the helper + */ int drm_fb_helper_setcmap(struct fb_cmap *cmap, struct fb_info *info) { struct drm_fb_helper *fb_helper = info->par; @@ -591,6 +654,11 @@ int drm_fb_helper_setcmap(struct fb_cmap *cmap, struct fb_info *info) } EXPORT_SYMBOL(drm_fb_helper_setcmap); +/** + * drm_fb_helper_check_var - implementation for ->fb_check_var + * @var: screeninfo to check + * @info: fbdev registered by the helper + */ int drm_fb_helper_check_var(struct fb_var_screeninfo *var, struct fb_info *info) { @@ -683,7 +751,14 @@ int drm_fb_helper_check_var(struct fb_var_screeninfo *var, } EXPORT_SYMBOL(drm_fb_helper_check_var); -/* this will let fbcon do the mode init */ +/** + * drm_fb_helper_set_par - implementation for ->fb_set_par + * @info: fbdev registered by the helper + * + * This will let fbcon do the mode init and is called at initialization time by + * the fbdev core when registering the driver, and later on through the hotplug + * callback. + */ int drm_fb_helper_set_par(struct fb_info *info) { struct drm_fb_helper *fb_helper = info->par; @@ -715,6 +790,11 @@ int drm_fb_helper_set_par(struct fb_info *info) } EXPORT_SYMBOL(drm_fb_helper_set_par); +/** + * drm_fb_helper_pan_display - implementation for ->fb_pan_display + * @var: updated screen information + * @info: fbdev registered by the helper + */ int drm_fb_helper_pan_display(struct fb_var_screeninfo *var, struct fb_info *info) { @@ -753,8 +833,9 @@ int drm_fb_helper_pan_display(struct fb_var_screeninfo *var, EXPORT_SYMBOL(drm_fb_helper_pan_display); /* - * Allocates the backing storage through the ->fb_probe callback and then - * registers the fbdev and sets up the panic notifier. + * Allocates the backing storage and sets up the fbdev info structure through + * the ->fb_probe callback and then registers the fbdev and sets up the panic + * notifier. */ static int drm_fb_helper_single_fb_probe(struct drm_fb_helper *fb_helper, int preferred_bpp) @@ -876,6 +957,19 @@ static int drm_fb_helper_single_fb_probe(struct drm_fb_helper *fb_helper, return 0; } +/** + * drm_fb_helper_fill_fix - initializes fixed fbdev information + * @info: fbdev registered by the helper + * @pitch: desired pitch + * @depth: desired depth + * + * Helper to fill in the fixed fbdev information useful for a non-accelerated + * fbdev emulations. Drivers which support acceleration methods which impose + * additional constraints need to set up their own limits. + * + * Drivers should call this (or their equivalent setup code) from their + * ->fb_probe callback. + */ void drm_fb_helper_fill_fix(struct fb_info *info, uint32_t pitch, uint32_t depth) { @@ -896,6 +990,20 @@ void drm_fb_helper_fill_fix(struct fb_info *info, uint32_t pitch, } EXPORT_SYMBOL(drm_fb_helper_fill_fix); +/** + * drm_fb_helper_fill_var - initalizes variable fbdev information + * @info: fbdev instance to set up + * @fb_helper: fb helper instance to use as template + * @fb_width: desired fb width + * @fb_height: desired fb height + * + * Sets up the variable fbdev metainformation from the given fb helper instance + * and the drm framebuffer allocated in fb_helper->fb. + * + * Drivers should call this (or their equivalent setup code) from their + * ->fb_probe callback after having allocated the fbdev backing + * storage framebuffer. + */ void drm_fb_helper_fill_var(struct fb_info *info, struct drm_fb_helper *fb_helper, uint32_t fb_width, uint32_t fb_height) { @@ -1358,18 +1466,23 @@ out: } /** - * drm_helper_initial_config - setup a sane initial connector configuration + * drm_fb_helper_initial_config - setup a sane initial connector configuration * @fb_helper: fb_helper device struct * @bpp_sel: bpp value to use for the framebuffer configuration * - * LOCKING: - * Called at init time by the driver to set up the @fb_helper initial - * configuration, must take the mode config lock. - * * Scans the CRTCs and connectors and tries to put together an initial setup. * At the moment, this is a cloned configuration across all heads with * a new framebuffer object as the backing store. * + * Note that this also registers the fbdev and so allows userspace to call into + * the driver through the fbdev interfaces. + * + * This function will call down into the ->fb_probe callback to let + * the driver allocate and initialize the fbdev info structure and the drm + * framebuffer used to back the fbdev. drm_fb_helper_fill_var() and + * drm_fb_helper_fill_fix() are provided as helpers to setup simple default + * values for the fbdev info structure. + * * RETURNS: * Zero if everything went ok, nonzero otherwise. */ @@ -1400,12 +1513,17 @@ EXPORT_SYMBOL(drm_fb_helper_initial_config); * probing all the outputs attached to the fb * @fb_helper: the drm_fb_helper * - * LOCKING: - * Called at runtime, must take mode config lock. - * * Scan the connectors attached to the fb_helper and try to put together a * setup after *notification of a change in output configuration. * + * Called at runtime, takes the mode config locks to be able to check/change the + * modeset configuration. Must be run from process context (which usually means + * 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. + * * RETURNS: * 0 on success and a non-zero error code otherwise. */ diff --git a/include/drm/drm_fb_helper.h b/include/drm/drm_fb_helper.h index 973402d6effe..31e5b97c2e89 100644 --- a/include/drm/drm_fb_helper.h +++ b/include/drm/drm_fb_helper.h @@ -48,6 +48,18 @@ struct drm_fb_helper_surface_size { u32 surface_depth; }; +/** + * struct drm_fb_helper_funcs - driver callbacks for the fbdev emulation library + * @gamma_set: - Set the given gamma lut register on the given crtc. + * @gamma_get: - Read the given gamma lut register on the given crtc, used to + * save the current lut when force-restoring the fbdev for e.g. + * kdbg. + * @fb_probe: - Driver callback to allocate and initialize the fbdev info + * structure. Futhermore it also needs to allocate the drm + * framebuffer used to back the fbdev. + * + * Driver callbacks used by the fbdev emulation helper library. + */ struct drm_fb_helper_funcs { void (*gamma_set)(struct drm_crtc *crtc, u16 red, u16 green, u16 blue, int regno); -- cgit v1.2.3 From 1b1d5397058f06bc5bd87d43ed93f34b28546ea4 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Thu, 24 Jan 2013 16:42:07 +0100 Subject: drm/fb-helper: don't sleep for screen unblank when an oopps is in progress Otherwise the system will burn even brighter and worse, leave the user wondering what's going on exactly. Since we already have a panic handler which will (try) to restore the entire fbdev console mode, we can just bail out. Inspired by a patch from Konstantin Khlebnikov. The callchain leading to this, cut&pasted from Konstantin's original patch: callstack: panic() bust_spinlocks(1) unblank_screen() vc->vc_sw->con_blank() fbcon_blank() fb_blank() info->fbops->fb_blank() drm_fb_helper_blank() drm_fb_helper_dpms() drm_modeset_lock_all() mutex_lock(&dev->mode_config.mutex) Note that the entire locking in the fb helper around panic/sysrq and kdbg is ... non-existant. So we have a decent change of blowing up everything. But since reworking this ties in with funny concepts like the fbdev notifier chain or the impressive things which happen around console_lock while oopsing, I'll leave that as an exercise for braver souls than me. v2: Drop the -EBUSY return value I've copied, we don't need it since the we'll take care of things ourselves anyway. Cc: Konstantin Khlebnikov Cc: Andrew Morton References: https://patchwork.kernel.org/patch/1878181/ Reviewed-by: Rob Clark Signed-off-by: Daniel Vetter --- drivers/gpu/drm/drm_fb_helper.c | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'drivers/gpu/drm/drm_fb_helper.c') diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c index 94f304d8b795..59d6b9bf204b 100644 --- a/drivers/gpu/drm/drm_fb_helper.c +++ b/drivers/gpu/drm/drm_fb_helper.c @@ -390,6 +390,14 @@ static void drm_fb_helper_dpms(struct fb_info *info, int dpms_mode) struct drm_connector *connector; int i, j; + /* + * fbdev->blank can be called from irq context in case of a panic. + * Since we already have our own special panic handler which will + * restore the fbdev console mode completely, just bail out early. + */ + if (oops_in_progress) + return; + /* * For each CRTC in this fb, turn the connectors on/off. */ -- cgit v1.2.3