| .. | .. |
|---|
| 36 | 36 | #include <drm/drm_crtc.h> |
|---|
| 37 | 37 | #include <drm/drm_device.h> |
|---|
| 38 | 38 | #include <linux/kgdb.h> |
|---|
| 39 | +#include <linux/vgaarb.h> |
|---|
| 39 | 40 | |
|---|
| 40 | 41 | enum mode_set_atomic { |
|---|
| 41 | 42 | LEAVE_ATOMIC_MODE_SET, |
|---|
| 42 | 43 | ENTER_ATOMIC_MODE_SET, |
|---|
| 43 | | -}; |
|---|
| 44 | | - |
|---|
| 45 | | -struct drm_fb_offset { |
|---|
| 46 | | - int x, y; |
|---|
| 47 | | -}; |
|---|
| 48 | | - |
|---|
| 49 | | -struct drm_fb_helper_crtc { |
|---|
| 50 | | - struct drm_mode_set mode_set; |
|---|
| 51 | | - struct drm_display_mode *desired_mode; |
|---|
| 52 | | - int x, y; |
|---|
| 53 | | - int rotation; |
|---|
| 54 | 44 | }; |
|---|
| 55 | 45 | |
|---|
| 56 | 46 | /** |
|---|
| .. | .. |
|---|
| 67 | 57 | * according to the largest width/height (so it is large enough for all CRTCs |
|---|
| 68 | 58 | * to scanout). But the fbdev width/height is sized to the minimum width/ |
|---|
| 69 | 59 | * height of all the displays. This ensures that fbcon fits on the smallest |
|---|
| 70 | | - * of the attached displays. |
|---|
| 71 | | - * |
|---|
| 72 | | - * So what is passed to drm_fb_helper_fill_var() should be fb_width/fb_height, |
|---|
| 73 | | - * rather than the surface size. |
|---|
| 60 | + * of the attached displays. fb_width/fb_height is used by |
|---|
| 61 | + * drm_fb_helper_fill_info() to fill out the &fb_info.var structure. |
|---|
| 74 | 62 | */ |
|---|
| 75 | 63 | struct drm_fb_helper_surface_size { |
|---|
| 76 | 64 | u32 fb_width; |
|---|
| .. | .. |
|---|
| 103 | 91 | */ |
|---|
| 104 | 92 | int (*fb_probe)(struct drm_fb_helper *helper, |
|---|
| 105 | 93 | struct drm_fb_helper_surface_size *sizes); |
|---|
| 106 | | - |
|---|
| 107 | | - /** |
|---|
| 108 | | - * @initial_config: |
|---|
| 109 | | - * |
|---|
| 110 | | - * Driver callback to setup an initial fbdev display configuration. |
|---|
| 111 | | - * Drivers can use this callback to tell the fbdev emulation what the |
|---|
| 112 | | - * preferred initial configuration is. This is useful to implement |
|---|
| 113 | | - * smooth booting where the fbdev (and subsequently all userspace) never |
|---|
| 114 | | - * changes the mode, but always inherits the existing configuration. |
|---|
| 115 | | - * |
|---|
| 116 | | - * This callback is optional. |
|---|
| 117 | | - * |
|---|
| 118 | | - * RETURNS: |
|---|
| 119 | | - * |
|---|
| 120 | | - * The driver should return true if a suitable initial configuration has |
|---|
| 121 | | - * been filled out and false when the fbdev helper should fall back to |
|---|
| 122 | | - * the default probing logic. |
|---|
| 123 | | - */ |
|---|
| 124 | | - bool (*initial_config)(struct drm_fb_helper *fb_helper, |
|---|
| 125 | | - struct drm_fb_helper_crtc **crtcs, |
|---|
| 126 | | - struct drm_display_mode **modes, |
|---|
| 127 | | - struct drm_fb_offset *offsets, |
|---|
| 128 | | - bool *enabled, int width, int height); |
|---|
| 129 | | -}; |
|---|
| 130 | | - |
|---|
| 131 | | -struct drm_fb_helper_connector { |
|---|
| 132 | | - struct drm_connector *connector; |
|---|
| 133 | 94 | }; |
|---|
| 134 | 95 | |
|---|
| 135 | 96 | /** |
|---|
| 136 | 97 | * struct drm_fb_helper - main structure to emulate fbdev on top of KMS |
|---|
| 137 | 98 | * @fb: Scanout framebuffer object |
|---|
| 138 | 99 | * @dev: DRM device |
|---|
| 139 | | - * @crtc_count: number of possible CRTCs |
|---|
| 140 | | - * @crtc_info: per-CRTC helper state (mode, x/y offset, etc) |
|---|
| 141 | | - * @connector_count: number of connected connectors |
|---|
| 142 | | - * @connector_info_alloc_count: size of connector_info |
|---|
| 143 | 100 | * @funcs: driver callbacks for fb helper |
|---|
| 144 | 101 | * @fbdev: emulated fbdev device info struct |
|---|
| 145 | 102 | * @pseudo_palette: fake palette of 16 colors |
|---|
| .. | .. |
|---|
| 171 | 128 | |
|---|
| 172 | 129 | struct drm_framebuffer *fb; |
|---|
| 173 | 130 | struct drm_device *dev; |
|---|
| 174 | | - int crtc_count; |
|---|
| 175 | | - struct drm_fb_helper_crtc *crtc_info; |
|---|
| 176 | | - int connector_count; |
|---|
| 177 | | - int connector_info_alloc_count; |
|---|
| 178 | | - /** |
|---|
| 179 | | - * @sw_rotations: |
|---|
| 180 | | - * Bitmask of all rotations requested for panel-orientation which |
|---|
| 181 | | - * could not be handled in hardware. If only one bit is set |
|---|
| 182 | | - * fbdev->fbcon_rotate_hint gets set to the requested rotation. |
|---|
| 183 | | - */ |
|---|
| 184 | | - int sw_rotations; |
|---|
| 185 | | - /** |
|---|
| 186 | | - * @connector_info: |
|---|
| 187 | | - * |
|---|
| 188 | | - * Array of per-connector information. Do not iterate directly, but use |
|---|
| 189 | | - * drm_fb_helper_for_each_connector. |
|---|
| 190 | | - */ |
|---|
| 191 | | - struct drm_fb_helper_connector **connector_info; |
|---|
| 192 | 131 | const struct drm_fb_helper_funcs *funcs; |
|---|
| 193 | 132 | struct fb_info *fbdev; |
|---|
| 194 | 133 | u32 pseudo_palette[17]; |
|---|
| .. | .. |
|---|
| 274 | 213 | #ifdef CONFIG_DRM_FBDEV_EMULATION |
|---|
| 275 | 214 | void drm_fb_helper_prepare(struct drm_device *dev, struct drm_fb_helper *helper, |
|---|
| 276 | 215 | const struct drm_fb_helper_funcs *funcs); |
|---|
| 277 | | -int drm_fb_helper_init(struct drm_device *dev, |
|---|
| 278 | | - struct drm_fb_helper *helper, int max_conn); |
|---|
| 216 | +int drm_fb_helper_init(struct drm_device *dev, struct drm_fb_helper *helper); |
|---|
| 279 | 217 | void drm_fb_helper_fini(struct drm_fb_helper *helper); |
|---|
| 280 | 218 | int drm_fb_helper_blank(int blank, struct fb_info *info); |
|---|
| 281 | 219 | int drm_fb_helper_pan_display(struct fb_var_screeninfo *var, |
|---|
| .. | .. |
|---|
| 288 | 226 | |
|---|
| 289 | 227 | struct fb_info *drm_fb_helper_alloc_fbi(struct drm_fb_helper *fb_helper); |
|---|
| 290 | 228 | void drm_fb_helper_unregister_fbi(struct drm_fb_helper *fb_helper); |
|---|
| 291 | | -void drm_fb_helper_fill_var(struct fb_info *info, struct drm_fb_helper *fb_helper, |
|---|
| 292 | | - uint32_t fb_width, uint32_t fb_height); |
|---|
| 293 | | -void drm_fb_helper_fill_fix(struct fb_info *info, uint32_t pitch, |
|---|
| 294 | | - uint32_t depth); |
|---|
| 295 | | - |
|---|
| 296 | | -void drm_fb_helper_unlink_fbi(struct drm_fb_helper *fb_helper); |
|---|
| 229 | +void drm_fb_helper_fill_info(struct fb_info *info, |
|---|
| 230 | + struct drm_fb_helper *fb_helper, |
|---|
| 231 | + struct drm_fb_helper_surface_size *sizes); |
|---|
| 297 | 232 | |
|---|
| 298 | 233 | void drm_fb_helper_deferred_io(struct fb_info *info, |
|---|
| 299 | 234 | struct list_head *pagelist); |
|---|
| 300 | | -int drm_fb_helper_defio_init(struct drm_fb_helper *fb_helper); |
|---|
| 301 | 235 | |
|---|
| 302 | 236 | ssize_t drm_fb_helper_sys_read(struct fb_info *info, char __user *buf, |
|---|
| 303 | 237 | size_t count, loff_t *ppos); |
|---|
| .. | .. |
|---|
| 329 | 263 | |
|---|
| 330 | 264 | int drm_fb_helper_hotplug_event(struct drm_fb_helper *fb_helper); |
|---|
| 331 | 265 | int drm_fb_helper_initial_config(struct drm_fb_helper *fb_helper, int bpp_sel); |
|---|
| 332 | | -int drm_fb_helper_single_add_all_connectors(struct drm_fb_helper *fb_helper); |
|---|
| 333 | 266 | int drm_fb_helper_debug_enter(struct fb_info *info); |
|---|
| 334 | 267 | int drm_fb_helper_debug_leave(struct fb_info *info); |
|---|
| 335 | | -struct drm_display_mode * |
|---|
| 336 | | -drm_has_preferred_mode(struct drm_fb_helper_connector *fb_connector, |
|---|
| 337 | | - int width, int height); |
|---|
| 338 | | -struct drm_display_mode * |
|---|
| 339 | | -drm_pick_cmdline_mode(struct drm_fb_helper_connector *fb_helper_conn); |
|---|
| 340 | | - |
|---|
| 341 | | -int drm_fb_helper_add_one_connector(struct drm_fb_helper *fb_helper, struct drm_connector *connector); |
|---|
| 342 | | -int drm_fb_helper_remove_one_connector(struct drm_fb_helper *fb_helper, |
|---|
| 343 | | - struct drm_connector *connector); |
|---|
| 344 | | - |
|---|
| 345 | | -int drm_fb_helper_fbdev_setup(struct drm_device *dev, |
|---|
| 346 | | - struct drm_fb_helper *fb_helper, |
|---|
| 347 | | - const struct drm_fb_helper_funcs *funcs, |
|---|
| 348 | | - unsigned int preferred_bpp, |
|---|
| 349 | | - unsigned int max_conn_count); |
|---|
| 350 | | -void drm_fb_helper_fbdev_teardown(struct drm_device *dev); |
|---|
| 351 | 268 | |
|---|
| 352 | 269 | void drm_fb_helper_lastclose(struct drm_device *dev); |
|---|
| 353 | 270 | void drm_fb_helper_output_poll_changed(struct drm_device *dev); |
|---|
| 354 | 271 | |
|---|
| 355 | | -int drm_fb_helper_generic_probe(struct drm_fb_helper *fb_helper, |
|---|
| 356 | | - struct drm_fb_helper_surface_size *sizes); |
|---|
| 357 | | -int drm_fbdev_generic_setup(struct drm_device *dev, unsigned int preferred_bpp); |
|---|
| 272 | +void drm_fbdev_generic_setup(struct drm_device *dev, |
|---|
| 273 | + unsigned int preferred_bpp); |
|---|
| 358 | 274 | #else |
|---|
| 359 | 275 | static inline void drm_fb_helper_prepare(struct drm_device *dev, |
|---|
| 360 | 276 | struct drm_fb_helper *helper, |
|---|
| .. | .. |
|---|
| 363 | 279 | } |
|---|
| 364 | 280 | |
|---|
| 365 | 281 | static inline int drm_fb_helper_init(struct drm_device *dev, |
|---|
| 366 | | - struct drm_fb_helper *helper, |
|---|
| 367 | | - int max_conn) |
|---|
| 282 | + struct drm_fb_helper *helper) |
|---|
| 368 | 283 | { |
|---|
| 369 | 284 | /* So drivers can use it to free the struct */ |
|---|
| 370 | 285 | helper->dev = dev; |
|---|
| .. | .. |
|---|
| 417 | 332 | { |
|---|
| 418 | 333 | } |
|---|
| 419 | 334 | |
|---|
| 420 | | -static inline void drm_fb_helper_fill_var(struct fb_info *info, |
|---|
| 421 | | - struct drm_fb_helper *fb_helper, |
|---|
| 422 | | - uint32_t fb_width, uint32_t fb_height) |
|---|
| 423 | | -{ |
|---|
| 424 | | -} |
|---|
| 425 | | - |
|---|
| 426 | | -static inline void drm_fb_helper_fill_fix(struct fb_info *info, uint32_t pitch, |
|---|
| 427 | | - uint32_t depth) |
|---|
| 335 | +static inline void |
|---|
| 336 | +drm_fb_helper_fill_info(struct fb_info *info, |
|---|
| 337 | + struct drm_fb_helper *fb_helper, |
|---|
| 338 | + struct drm_fb_helper_surface_size *sizes) |
|---|
| 428 | 339 | { |
|---|
| 429 | 340 | } |
|---|
| 430 | 341 | |
|---|
| .. | .. |
|---|
| 438 | 349 | unsigned long arg) |
|---|
| 439 | 350 | { |
|---|
| 440 | 351 | return 0; |
|---|
| 441 | | -} |
|---|
| 442 | | - |
|---|
| 443 | | -static inline void drm_fb_helper_unlink_fbi(struct drm_fb_helper *fb_helper) |
|---|
| 444 | | -{ |
|---|
| 445 | 352 | } |
|---|
| 446 | 353 | |
|---|
| 447 | 354 | static inline void drm_fb_helper_deferred_io(struct fb_info *info, |
|---|
| .. | .. |
|---|
| 519 | 426 | return 0; |
|---|
| 520 | 427 | } |
|---|
| 521 | 428 | |
|---|
| 522 | | -static inline int |
|---|
| 523 | | -drm_fb_helper_single_add_all_connectors(struct drm_fb_helper *fb_helper) |
|---|
| 524 | | -{ |
|---|
| 525 | | - return 0; |
|---|
| 526 | | -} |
|---|
| 527 | | - |
|---|
| 528 | 429 | static inline int drm_fb_helper_debug_enter(struct fb_info *info) |
|---|
| 529 | 430 | { |
|---|
| 530 | 431 | return 0; |
|---|
| .. | .. |
|---|
| 535 | 436 | return 0; |
|---|
| 536 | 437 | } |
|---|
| 537 | 438 | |
|---|
| 538 | | -static inline struct drm_display_mode * |
|---|
| 539 | | -drm_has_preferred_mode(struct drm_fb_helper_connector *fb_connector, |
|---|
| 540 | | - int width, int height) |
|---|
| 541 | | -{ |
|---|
| 542 | | - return NULL; |
|---|
| 543 | | -} |
|---|
| 544 | | - |
|---|
| 545 | | -static inline struct drm_display_mode * |
|---|
| 546 | | -drm_pick_cmdline_mode(struct drm_fb_helper_connector *fb_helper_conn, |
|---|
| 547 | | - int width, int height) |
|---|
| 548 | | -{ |
|---|
| 549 | | - return NULL; |
|---|
| 550 | | -} |
|---|
| 551 | | - |
|---|
| 552 | | -static inline int |
|---|
| 553 | | -drm_fb_helper_add_one_connector(struct drm_fb_helper *fb_helper, |
|---|
| 554 | | - struct drm_connector *connector) |
|---|
| 555 | | -{ |
|---|
| 556 | | - return 0; |
|---|
| 557 | | -} |
|---|
| 558 | | - |
|---|
| 559 | | -static inline int |
|---|
| 560 | | -drm_fb_helper_remove_one_connector(struct drm_fb_helper *fb_helper, |
|---|
| 561 | | - struct drm_connector *connector) |
|---|
| 562 | | -{ |
|---|
| 563 | | - return 0; |
|---|
| 564 | | -} |
|---|
| 565 | | - |
|---|
| 566 | | -static inline int |
|---|
| 567 | | -drm_fb_helper_fbdev_setup(struct drm_device *dev, |
|---|
| 568 | | - struct drm_fb_helper *fb_helper, |
|---|
| 569 | | - const struct drm_fb_helper_funcs *funcs, |
|---|
| 570 | | - unsigned int preferred_bpp, |
|---|
| 571 | | - unsigned int max_conn_count) |
|---|
| 572 | | -{ |
|---|
| 573 | | - /* So drivers can use it to free the struct */ |
|---|
| 574 | | - dev->fb_helper = fb_helper; |
|---|
| 575 | | - |
|---|
| 576 | | - return 0; |
|---|
| 577 | | -} |
|---|
| 578 | | - |
|---|
| 579 | | -static inline void drm_fb_helper_fbdev_teardown(struct drm_device *dev) |
|---|
| 580 | | -{ |
|---|
| 581 | | - dev->fb_helper = NULL; |
|---|
| 582 | | -} |
|---|
| 583 | | - |
|---|
| 584 | 439 | static inline void drm_fb_helper_lastclose(struct drm_device *dev) |
|---|
| 585 | 440 | { |
|---|
| 586 | 441 | } |
|---|
| .. | .. |
|---|
| 589 | 444 | { |
|---|
| 590 | 445 | } |
|---|
| 591 | 446 | |
|---|
| 592 | | -static inline int |
|---|
| 593 | | -drm_fb_helper_generic_probe(struct drm_fb_helper *fb_helper, |
|---|
| 594 | | - struct drm_fb_helper_surface_size *sizes) |
|---|
| 595 | | -{ |
|---|
| 596 | | - return 0; |
|---|
| 597 | | -} |
|---|
| 598 | | - |
|---|
| 599 | | -static inline int |
|---|
| 447 | +static inline void |
|---|
| 600 | 448 | drm_fbdev_generic_setup(struct drm_device *dev, unsigned int preferred_bpp) |
|---|
| 601 | 449 | { |
|---|
| 602 | | - return 0; |
|---|
| 603 | 450 | } |
|---|
| 604 | 451 | |
|---|
| 605 | 452 | #endif |
|---|
| 606 | 453 | |
|---|
| 454 | +/** |
|---|
| 455 | + * drm_fb_helper_remove_conflicting_framebuffers - remove firmware-configured framebuffers |
|---|
| 456 | + * @a: memory range, users of which are to be removed |
|---|
| 457 | + * @name: requesting driver name |
|---|
| 458 | + * @primary: also kick vga16fb if present |
|---|
| 459 | + * |
|---|
| 460 | + * This function removes framebuffer devices (initialized by firmware/bootloader) |
|---|
| 461 | + * which use memory range described by @a. If @a is NULL all such devices are |
|---|
| 462 | + * removed. |
|---|
| 463 | + */ |
|---|
| 607 | 464 | static inline int |
|---|
| 608 | 465 | drm_fb_helper_remove_conflicting_framebuffers(struct apertures_struct *a, |
|---|
| 609 | 466 | const char *name, bool primary) |
|---|
| .. | .. |
|---|
| 615 | 472 | #endif |
|---|
| 616 | 473 | } |
|---|
| 617 | 474 | |
|---|
| 475 | +/** |
|---|
| 476 | + * drm_fb_helper_remove_conflicting_pci_framebuffers - remove firmware-configured framebuffers for PCI devices |
|---|
| 477 | + * @pdev: PCI device |
|---|
| 478 | + * @name: requesting driver name |
|---|
| 479 | + * |
|---|
| 480 | + * This function removes framebuffer devices (eg. initialized by firmware) |
|---|
| 481 | + * using memory range configured for any of @pdev's memory bars. |
|---|
| 482 | + * |
|---|
| 483 | + * The function assumes that PCI device with shadowed ROM drives a primary |
|---|
| 484 | + * display and so kicks out vga16fb. |
|---|
| 485 | + */ |
|---|
| 486 | +static inline int |
|---|
| 487 | +drm_fb_helper_remove_conflicting_pci_framebuffers(struct pci_dev *pdev, |
|---|
| 488 | + const char *name) |
|---|
| 489 | +{ |
|---|
| 490 | + int ret = 0; |
|---|
| 491 | + |
|---|
| 492 | + /* |
|---|
| 493 | + * WARNING: Apparently we must kick fbdev drivers before vgacon, |
|---|
| 494 | + * otherwise the vga fbdev driver falls over. |
|---|
| 495 | + */ |
|---|
| 496 | +#if IS_REACHABLE(CONFIG_FB) |
|---|
| 497 | + ret = remove_conflicting_pci_framebuffers(pdev, name); |
|---|
| 498 | +#endif |
|---|
| 499 | + if (ret == 0) |
|---|
| 500 | + ret = vga_remove_vgacon(pdev); |
|---|
| 501 | + return ret; |
|---|
| 502 | +} |
|---|
| 503 | + |
|---|
| 618 | 504 | #endif |
|---|