Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

drm: Introduce drm_fb_helper_prepare()

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>
Signed-off-by: Dave Airlie <airlied@redhat.com>

authored by

Thierry Reding and committed by
Dave Airlie
10a23102 3a493879

+72 -28
+1 -1
drivers/gpu/drm/armada/armada_fbdev.c
··· 149 149 150 150 priv->fbdev = fbh; 151 151 152 - fbh->funcs = &armada_fb_helper_funcs; 152 + drm_fb_helper_prepare(dev, fbh, &armada_fb_helper_funcs); 153 153 154 154 ret = drm_fb_helper_init(dev, fbh, 1, 1); 155 155 if (ret) {
+3 -1
drivers/gpu/drm/ast/ast_fb.c
··· 328 328 return -ENOMEM; 329 329 330 330 ast->fbdev = afbdev; 331 - afbdev->helper.funcs = &ast_fb_helper_funcs; 332 331 spin_lock_init(&afbdev->dirty_lock); 332 + 333 + drm_fb_helper_prepare(dev, &afbdev->helper, &ast_fb_helper_funcs); 334 + 333 335 ret = drm_fb_helper_init(dev, &afbdev->helper, 334 336 1, 1); 335 337 if (ret) {
+2 -1
drivers/gpu/drm/bochs/bochs_fbdev.c
··· 189 189 { 190 190 int ret; 191 191 192 - bochs->fb.helper.funcs = &bochs_fb_helper_funcs; 192 + drm_fb_helper_prepare(bochs->dev, &bochs->fb.helper, 193 + &bochs_fb_helper_funcs); 193 194 194 195 ret = drm_fb_helper_init(bochs->dev, &bochs->fb.helper, 195 196 1, 1);
+3 -1
drivers/gpu/drm/cirrus/cirrus_fbdev.c
··· 306 306 return -ENOMEM; 307 307 308 308 cdev->mode_info.gfbdev = gfbdev; 309 - gfbdev->helper.funcs = &cirrus_fb_helper_funcs; 310 309 spin_lock_init(&gfbdev->dirty_lock); 310 + 311 + drm_fb_helper_prepare(cdev->dev, &gfbdev->helper, 312 + &cirrus_fb_helper_funcs); 311 313 312 314 ret = drm_fb_helper_init(cdev->dev, &gfbdev->helper, 313 315 cdev->num_crtc, CIRRUSFB_CONN_LIMIT);
+2 -1
drivers/gpu/drm/drm_fb_cma_helper.c
··· 354 354 return ERR_PTR(-ENOMEM); 355 355 } 356 356 357 - fbdev_cma->fb_helper.funcs = &drm_fb_cma_helper_funcs; 358 357 helper = &fbdev_cma->fb_helper; 358 + 359 + drm_fb_helper_prepare(dev, helper, &drm_fb_cma_helper_funcs); 359 360 360 361 ret = drm_fb_helper_init(dev, helper, num_crtc, max_conn_count); 361 362 if (ret < 0) {
+37 -10
drivers/gpu/drm/drm_fb_helper.c
··· 49 49 * helper functions used by many drivers to implement the kernel mode setting 50 50 * interfaces. 51 51 * 52 - * Initialization is done as a three-step process with drm_fb_helper_init(), 53 - * drm_fb_helper_single_add_all_connectors() and drm_fb_helper_initial_config(). 54 - * Drivers with fancier requirements than the default behaviour can override the 55 - * second step with their own code. Teardown is done with drm_fb_helper_fini(). 52 + * Initialization is done as a four-step process with drm_fb_helper_prepare(), 53 + * drm_fb_helper_init(), drm_fb_helper_single_add_all_connectors() and 54 + * drm_fb_helper_initial_config(). Drivers with fancier requirements than the 55 + * default behaviour can override the third step with their own code. 56 + * Teardown is done with drm_fb_helper_fini(). 56 57 * 57 58 * At runtime drivers should restore the fbdev console by calling 58 59 * drm_fb_helper_restore_fbdev_mode() from their ->lastclose callback. They ··· 64 63 * 65 64 * All other functions exported by the fb helper library can be used to 66 65 * implement the fbdev driver interface by the driver. 66 + * 67 + * It is possible, though perhaps somewhat tricky, to implement race-free 68 + * hotplug detection using the fbdev helpers. The drm_fb_helper_prepare() 69 + * helper must be called first to initialize the minimum required to make 70 + * hotplug detection work. Drivers also need to make sure to properly set up 71 + * the dev->mode_config.funcs member. After calling drm_kms_helper_poll_init() 72 + * it is safe to enable interrupts and start processing hotplug events. At the 73 + * same time, drivers should initialize all modeset objects such as CRTCs, 74 + * encoders and connectors. To finish up the fbdev helper initialization, the 75 + * drm_fb_helper_init() function is called. To probe for all attached displays 76 + * and set up an initial configuration using the detected hardware, drivers 77 + * should call drm_fb_helper_single_add_all_connectors() followed by 78 + * drm_fb_helper_initial_config(). 67 79 */ 68 80 69 81 /** ··· 542 528 } 543 529 544 530 /** 531 + * drm_fb_helper_prepare - setup a drm_fb_helper structure 532 + * @dev: DRM device 533 + * @helper: driver-allocated fbdev helper structure to set up 534 + * @funcs: pointer to structure of functions associate with this helper 535 + * 536 + * Sets up the bare minimum to make the framebuffer helper usable. This is 537 + * useful to implement race-free initialization of the polling helpers. 538 + */ 539 + void drm_fb_helper_prepare(struct drm_device *dev, struct drm_fb_helper *helper, 540 + const struct drm_fb_helper_funcs *funcs) 541 + { 542 + INIT_LIST_HEAD(&helper->kernel_fb_list); 543 + helper->funcs = funcs; 544 + helper->dev = dev; 545 + } 546 + EXPORT_SYMBOL(drm_fb_helper_prepare); 547 + 548 + /** 545 549 * drm_fb_helper_init - initialize a drm_fb_helper structure 546 550 * @dev: drm device 547 551 * @fb_helper: driver-allocated fbdev helper structure to initialize ··· 571 539 * nor register the fbdev. This is only done in drm_fb_helper_initial_config() 572 540 * to allow driver writes more control over the exact init sequence. 573 541 * 574 - * Drivers must set fb_helper->funcs before calling 575 - * drm_fb_helper_initial_config(). 542 + * Drivers must call drm_fb_helper_prepare() before calling this function. 576 543 * 577 544 * RETURNS: 578 545 * Zero if everything went ok, nonzero otherwise. ··· 585 554 586 555 if (!max_conn_count) 587 556 return -EINVAL; 588 - 589 - fb_helper->dev = dev; 590 - 591 - INIT_LIST_HEAD(&fb_helper->kernel_fb_list); 592 557 593 558 fb_helper->crtc_info = kcalloc(crtc_count, sizeof(struct drm_fb_helper_crtc), GFP_KERNEL); 594 559 if (!fb_helper->crtc_info)
+2 -1
drivers/gpu/drm/exynos/exynos_drm_fbdev.c
··· 266 266 return -ENOMEM; 267 267 268 268 private->fb_helper = helper = &fbdev->drm_fb_helper; 269 - helper->funcs = &exynos_drm_fb_helper_funcs; 269 + 270 + drm_fb_helper_prepare(dev, helper, &exynos_drm_fb_helper_funcs); 270 271 271 272 num_crtc = dev->mode_config.num_crtc; 272 273
+2 -1
drivers/gpu/drm/gma500/framebuffer.c
··· 600 600 } 601 601 602 602 dev_priv->fbdev = fbdev; 603 - fbdev->psb_fb_helper.funcs = &psb_fb_helper_funcs; 603 + 604 + drm_fb_helper_prepare(dev, &fbdev->psb_fb_helper, &psb_fb_helper_funcs); 604 605 605 606 drm_fb_helper_init(dev, &fbdev->psb_fb_helper, dev_priv->ops->crtcs, 606 607 INTELFB_CONN_LIMIT);
+2 -1
drivers/gpu/drm/i915/intel_fbdev.c
··· 623 623 if (ifbdev == NULL) 624 624 return -ENOMEM; 625 625 626 - ifbdev->helper.funcs = &intel_fb_helper_funcs; 626 + drm_fb_helper_prepare(dev, &ifbdev->helper, &intel_fb_helper_funcs); 627 + 627 628 if (!intel_fbdev_init_bios(dev, ifbdev)) 628 629 ifbdev->preferred_bpp = 32; 629 630
+2 -1
drivers/gpu/drm/mgag200/mgag200_fb.c
··· 293 293 return -ENOMEM; 294 294 295 295 mdev->mfbdev = mfbdev; 296 - mfbdev->helper.funcs = &mga_fb_helper_funcs; 297 296 spin_lock_init(&mfbdev->dirty_lock); 297 + 298 + drm_fb_helper_prepare(mdev->dev, &mfbdev->helper, &mga_fb_helper_funcs); 298 299 299 300 ret = drm_fb_helper_init(mdev->dev, &mfbdev->helper, 300 301 mdev->num_crtc, MGAG200FB_CONN_LIMIT);
+1 -1
drivers/gpu/drm/msm/msm_fbdev.c
··· 197 197 198 198 helper = &fbdev->base; 199 199 200 - helper->funcs = &msm_fb_helper_funcs; 200 + drm_fb_helper_prepare(dev, helper, &msm_fb_helper_funcs); 201 201 202 202 ret = drm_fb_helper_init(dev, helper, 203 203 priv->num_crtcs, priv->num_connectors);
+2 -1
drivers/gpu/drm/nouveau/nouveau_fbcon.c
··· 464 464 465 465 fbcon->dev = dev; 466 466 drm->fbcon = fbcon; 467 - fbcon->helper.funcs = &nouveau_fbcon_helper_funcs; 467 + 468 + drm_fb_helper_prepare(dev, &fbcon->helper, &nouveau_fbcon_helper_funcs); 468 469 469 470 ret = drm_fb_helper_init(dev, &fbcon->helper, 470 471 dev->mode_config.num_crtc, 4);
+1 -1
drivers/gpu/drm/omapdrm/omap_fbdev.c
··· 325 325 326 326 helper = &fbdev->base; 327 327 328 - helper->funcs = &omap_fb_helper_funcs; 328 + drm_fb_helper_prepare(dev, helper, &omap_fb_helper_funcs); 329 329 330 330 ret = drm_fb_helper_init(dev, helper, 331 331 priv->num_crtcs, priv->num_connectors);
+4 -1
drivers/gpu/drm/qxl/qxl_fb.c
··· 676 676 677 677 qfbdev->qdev = qdev; 678 678 qdev->mode_info.qfbdev = qfbdev; 679 - qfbdev->helper.funcs = &qxl_fb_helper_funcs; 680 679 spin_lock_init(&qfbdev->delayed_ops_lock); 681 680 INIT_LIST_HEAD(&qfbdev->delayed_ops); 681 + 682 + drm_fb_helper_prepare(qdev->ddev, &qfbdev->helper, 683 + &qxl_fb_helper_funcs); 684 + 682 685 ret = drm_fb_helper_init(qdev->ddev, &qfbdev->helper, 683 686 qxl_num_crtc /* num_crtc - QXL supports just 1 */, 684 687 QXLFB_CONN_LIMIT);
+3 -1
drivers/gpu/drm/radeon/radeon_fb.c
··· 353 353 354 354 rfbdev->rdev = rdev; 355 355 rdev->mode_info.rfbdev = rfbdev; 356 - rfbdev->helper.funcs = &radeon_fb_helper_funcs; 356 + 357 + drm_fb_helper_prepare(rdev->ddev, &rfbdev->helper, 358 + &radeon_fb_helper_funcs); 357 359 358 360 ret = drm_fb_helper_init(rdev->ddev, &rfbdev->helper, 359 361 rdev->num_crtc,
+1 -3
drivers/gpu/drm/tegra/fb.c
··· 276 276 unsigned int num_crtc, 277 277 unsigned int max_connectors) 278 278 { 279 - struct drm_fb_helper *helper; 280 279 struct tegra_fbdev *fbdev; 281 280 int err; 282 281 ··· 285 286 return ERR_PTR(-ENOMEM); 286 287 } 287 288 288 - fbdev->base.funcs = &tegra_fb_helper_funcs; 289 - helper = &fbdev->base; 289 + drm_fb_helper_prepare(drm, &fbdev->base, &tegra_fb_helper_funcs); 290 290 291 291 err = drm_fb_helper_init(drm, &fbdev->base, num_crtc, max_connectors); 292 292 if (err < 0) {
+2 -1
drivers/gpu/drm/udl/udl_fb.c
··· 583 583 return -ENOMEM; 584 584 585 585 udl->fbdev = ufbdev; 586 - ufbdev->helper.funcs = &udl_fb_helper_funcs; 586 + 587 + drm_fb_helper_prepare(dev, &ufbdev->helper, &udl_fb_helper_funcs); 587 588 588 589 ret = drm_fb_helper_init(dev, &ufbdev->helper, 589 590 1, 1);
+2
include/drm/drm_fb_helper.h
··· 97 97 bool delayed_hotplug; 98 98 }; 99 99 100 + void drm_fb_helper_prepare(struct drm_device *dev, struct drm_fb_helper *helper, 101 + const struct drm_fb_helper_funcs *funcs); 100 102 int drm_fb_helper_init(struct drm_device *dev, 101 103 struct drm_fb_helper *helper, int crtc_count, 102 104 int max_conn);