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

drm/amd/display: Initial documentation for AMDgpu DC

[Why]
Documentation is helpful for the community to understand our code.
This change does some high-level documentation of some DM interfaces
with DRM, and the amdgpu base driver.

[How]
An entry for AMDgpu DC has been added to Documentation/gpu/drivers.rst
TOC. amdgpu-dc.rst is created to pull in inline doc-strings, which:
- Provides an overview for "What is DM?"
- Documents AMDgpu DM lifecyle
- Documents IRQ management
- Documents atomic_check and commit_tail interfaces

Signed-off-by: Leo Li <sunpeng.li@amd.com>
Reviewed-by: David Francis <David.Francis@amd.com>
Acked-by: Leo Li <sunpeng.li@amd.com>
Signed-off-by: Alex Deucher <alexander.deucher@amd.com>

authored by

Leo Li and committed by
Alex Deucher
b8592b48 4f712911

+320 -41
+68
Documentation/gpu/amdgpu-dc.rst
··· 1 + =================================== 2 + drm/amd/display - Display Core (DC) 3 + =================================== 4 + 5 + *placeholder - general description of supported platforms, what dc is, etc.* 6 + 7 + Because it is partially shared with other operating systems, the Display Core 8 + Driver is divided in two pieces. 9 + 10 + 1. **Display Core (DC)** contains the OS-agnostic components. Things like 11 + hardware programming and resource management are handled here. 12 + 2. **Display Manager (DM)** contains the OS-dependent components. Hooks to the 13 + amdgpu base driver and DRM are implemented here. 14 + 15 + It doesn't help that the entire package is frequently referred to as DC. But 16 + with the context in mind, it should be clear. 17 + 18 + When CONFIG_DRM_AMD_DC is enabled, DC will be initialized by default for 19 + supported ASICs. To force disable, set `amdgpu.dc=0` on kernel command line. 20 + Likewise, to force enable on unsupported ASICs, set `amdgpu.dc=1`. 21 + 22 + To determine if DC is loaded, search dmesg for the following entry: 23 + 24 + ``Display Core initialized with <version number here>`` 25 + 26 + AMDgpu Display Manager 27 + ====================== 28 + 29 + .. kernel-doc:: drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c 30 + :doc: overview 31 + 32 + .. kernel-doc:: drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h 33 + :internal: 34 + 35 + Lifecycle 36 + --------- 37 + 38 + .. kernel-doc:: drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c 39 + :doc: DM Lifecycle 40 + 41 + .. kernel-doc:: drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c 42 + :functions: dm_hw_init dm_hw_fini 43 + 44 + Interrupts 45 + ---------- 46 + 47 + .. kernel-doc:: drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_irq.c 48 + :doc: overview 49 + 50 + .. kernel-doc:: drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_irq.c 51 + :internal: 52 + 53 + .. kernel-doc:: drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c 54 + :functions: register_hpd_handlers dm_crtc_high_irq dm_pflip_high_irq 55 + 56 + Atomic Implementation 57 + --------------------- 58 + 59 + .. kernel-doc:: drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c 60 + :doc: atomic 61 + 62 + .. kernel-doc:: drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c 63 + :functions: amdgpu_dm_atomic_check amdgpu_dm_atomic_commit_tail 64 + 65 + Display Core 66 + ============ 67 + 68 + **WIP**
+1
Documentation/gpu/drivers.rst
··· 5 5 .. toctree:: 6 6 7 7 amdgpu 8 + amdgpu-dc 8 9 i915 9 10 meson 10 11 pl111
+87 -14
drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
··· 76 76 #define FIRMWARE_RAVEN_DMCU "amdgpu/raven_dmcu.bin" 77 77 MODULE_FIRMWARE(FIRMWARE_RAVEN_DMCU); 78 78 79 + /** 80 + * DOC: overview 81 + * 82 + * The AMDgpu display manager, **amdgpu_dm** (or even simpler, 83 + * **dm**) sits between DRM and DC. It acts as a liason, converting DRM 84 + * requests into DC requests, and DC responses into DRM responses. 85 + * 86 + * The root control structure is &struct amdgpu_display_manager. 87 + */ 88 + 79 89 /* basic init/fini API */ 80 90 static int amdgpu_dm_init(struct amdgpu_device *adev); 81 91 static void amdgpu_dm_fini(struct amdgpu_device *adev); ··· 389 379 390 380 } 391 381 392 - /* 393 - * Init display KMS 394 - * 395 - * Returns 0 on success 396 - */ 397 382 static int amdgpu_dm_init(struct amdgpu_device *adev) 398 383 { 399 384 struct dc_init_data init_data; ··· 665 660 drm_modeset_unlock(&dev->mode_config.connection_mutex); 666 661 } 667 662 663 + /** 664 + * dm_hw_init() - Initialize DC device 665 + * @handle: The base driver device containing the amdpgu_dm device. 666 + * 667 + * Initialize the &struct amdgpu_display_manager device. This involves calling 668 + * the initializers of each DM component, then populating the struct with them. 669 + * 670 + * Although the function implies hardware initialization, both hardware and 671 + * software are initialized here. Splitting them out to their relevant init 672 + * hooks is a future TODO item. 673 + * 674 + * Some notable things that are initialized here: 675 + * 676 + * - Display Core, both software and hardware 677 + * - DC modules that we need (freesync and color management) 678 + * - DRM software states 679 + * - Interrupt sources and handlers 680 + * - Vblank support 681 + * - Debug FS entries, if enabled 682 + */ 668 683 static int dm_hw_init(void *handle) 669 684 { 670 685 struct amdgpu_device *adev = (struct amdgpu_device *)handle; ··· 695 670 return 0; 696 671 } 697 672 673 + /** 674 + * dm_hw_fini() - Teardown DC device 675 + * @handle: The base driver device containing the amdpgu_dm device. 676 + * 677 + * Teardown components within &struct amdgpu_display_manager that require 678 + * cleanup. This involves cleaning up the DRM device, DC, and any modules that 679 + * were loaded. Also flush IRQ workqueues and disable them. 680 + */ 698 681 static int dm_hw_fini(void *handle) 699 682 { 700 683 struct amdgpu_device *adev = (struct amdgpu_device *)handle; ··· 928 895 return ret; 929 896 } 930 897 898 + /** 899 + * DOC: DM Lifecycle 900 + * 901 + * DM (and consequently DC) is registered in the amdgpu base driver as a IP 902 + * block. When CONFIG_DRM_AMD_DC is enabled, the DM device IP block is added to 903 + * the base driver's device list to be initialized and torn down accordingly. 904 + * 905 + * The functions to do so are provided as hooks in &struct amd_ip_funcs. 906 + */ 907 + 931 908 static const struct amd_ip_funcs amdgpu_dm_funcs = { 932 909 .name = "dm", 933 910 .early_init = dm_early_init, ··· 1004 961 drm_atomic_state_default_release(state); 1005 962 kfree(dm_state); 1006 963 } 964 + 965 + /** 966 + * DOC: atomic 967 + * 968 + * *WIP* 969 + */ 1007 970 1008 971 static const struct drm_mode_config_funcs amdgpu_dm_mode_funcs = { 1009 972 .fb_create = amdgpu_display_user_framebuffer_create, ··· 4591 4542 /*TODO Handle EINTR, reenable IRQ*/ 4592 4543 } 4593 4544 4545 + /** 4546 + * amdgpu_dm_atomic_commit_tail() - AMDgpu DM's commit tail implementation. 4547 + * @state: The atomic state to commit 4548 + * 4549 + * This will tell DC to commit the constructed DC state from atomic_check, 4550 + * programming the hardware. Any failures here implies a hardware failure, since 4551 + * atomic check should have filtered anything non-kosher. 4552 + */ 4594 4553 static void amdgpu_dm_atomic_commit_tail(struct drm_atomic_state *state) 4595 4554 { 4596 4555 struct drm_device *dev = state->dev; ··· 5451 5394 return update_type; 5452 5395 } 5453 5396 5397 + /** 5398 + * amdgpu_dm_atomic_check() - Atomic check implementation for AMDgpu DM. 5399 + * @dev: The DRM device 5400 + * @state: The atomic state to commit 5401 + * 5402 + * Validate that the given atomic state is programmable by DC into hardware. 5403 + * This involves constructing a &struct dc_state reflecting the new hardware 5404 + * state we wish to commit, then querying DC to see if it is programmable. It's 5405 + * important not to modify the existing DC state. Otherwise, atomic_check 5406 + * may unexpectedly commit hardware changes. 5407 + * 5408 + * When validating the DC state, it's important that the right locks are 5409 + * acquired. For full updates case which removes/adds/updates streams on one 5410 + * CRTC while flipping on another CRTC, acquiring global lock will guarantee 5411 + * that any such full update commit will wait for completion of any outstanding 5412 + * flip using DRMs synchronization events. See 5413 + * dm_determine_update_type_for_commit() 5414 + * 5415 + * Note that DM adds the affected connectors for all CRTCs in state, when that 5416 + * might not seem necessary. This is because DC stream creation requires the 5417 + * DC sink, which is tied to the DRM connector state. Cleaning this up should 5418 + * be possible but non-trivial - a possible TODO item. 5419 + * 5420 + * Return: -Error code if validation failed. 5421 + */ 5454 5422 static int amdgpu_dm_atomic_check(struct drm_device *dev, 5455 5423 struct drm_atomic_state *state) 5456 5424 { ··· 5578 5496 lock_and_validation_needed = true; 5579 5497 } 5580 5498 5581 - /* 5582 - * For full updates case when 5583 - * removing/adding/updating streams on one CRTC while flipping 5584 - * on another CRTC, 5585 - * acquiring global lock will guarantee that any such full 5586 - * update commit 5587 - * will wait for completion of any outstanding flip using DRMs 5588 - * synchronization events. 5589 - */ 5590 5499 update_type = dm_determine_update_type_for_commit(dc, state); 5591 5500 5592 5501 if (overall_update_type < update_type)
+62 -14
drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h
··· 59 59 enum dc_irq_source irq_src; 60 60 }; 61 61 62 + /** 63 + * struct irq_list_head - Linked-list for low context IRQ handlers. 64 + * 65 + * @head: The list_head within &struct handler_data 66 + * @work: A work_struct containing the deferred handler work 67 + */ 62 68 struct irq_list_head { 63 69 struct list_head head; 64 70 /* In case this interrupt needs post-processing, 'work' will be queued*/ 65 71 struct work_struct work; 66 72 }; 67 73 74 + /** 75 + * struct dm_compressor_info - Buffer info used by frame buffer compression 76 + * @cpu_addr: MMIO cpu addr 77 + * @bo_ptr: Pointer to the buffer object 78 + * @gpu_addr: MMIO gpu addr 79 + */ 68 80 struct dm_comressor_info { 69 81 void *cpu_addr; 70 82 struct amdgpu_bo *bo_ptr; 71 83 uint64_t gpu_addr; 72 84 }; 73 85 86 + /** 87 + * struct amdgpu_display_manager - Central amdgpu display manager device 88 + * 89 + * @dc: Display Core control structure 90 + * @adev: AMDGPU base driver structure 91 + * @ddev: DRM base driver structure 92 + * @display_indexes_num: Max number of display streams supported 93 + * @irq_handler_list_table_lock: Synchronizes access to IRQ tables 94 + * @backlight_dev: Backlight control device 95 + * @cached_state: Caches device atomic state for suspend/resume 96 + * @compressor: Frame buffer compression buffer. See &struct dm_comressor_info 97 + */ 74 98 struct amdgpu_display_manager { 99 + 75 100 struct dc *dc; 101 + 102 + /** 103 + * @cgs_device: 104 + * 105 + * The Common Graphics Services device. It provides an interface for 106 + * accessing registers. 107 + */ 76 108 struct cgs_device *cgs_device; 77 109 78 - struct amdgpu_device *adev; /*AMD base driver*/ 79 - struct drm_device *ddev; /*DRM base driver*/ 110 + struct amdgpu_device *adev; 111 + struct drm_device *ddev; 80 112 u16 display_indexes_num; 81 113 82 - /* 83 - * 'irq_source_handler_table' holds a list of handlers 84 - * per (DAL) IRQ source. 114 + /** 115 + * @irq_handler_list_low_tab: 85 116 * 86 - * Each IRQ source may need to be handled at different contexts. 87 - * By 'context' we mean, for example: 88 - * - The ISR context, which is the direct interrupt handler. 89 - * - The 'deferred' context - this is the post-processing of the 90 - * interrupt, but at a lower priority. 117 + * Low priority IRQ handler table. 118 + * 119 + * It is a n*m table consisting of n IRQ sources, and m handlers per IRQ 120 + * source. Low priority IRQ handlers are deferred to a workqueue to be 121 + * processed. Hence, they can sleep. 91 122 * 92 123 * Note that handlers are called in the same order as they were 93 124 * registered (FIFO). 94 125 */ 95 126 struct irq_list_head irq_handler_list_low_tab[DAL_IRQ_SOURCES_NUMBER]; 127 + 128 + /** 129 + * @irq_handler_list_high_tab: 130 + * 131 + * High priority IRQ handler table. 132 + * 133 + * It is a n*m table, same as &irq_handler_list_low_tab. However, 134 + * handlers in this table are not deferred and are called immediately. 135 + */ 96 136 struct list_head irq_handler_list_high_tab[DAL_IRQ_SOURCES_NUMBER]; 97 137 138 + /** 139 + * @pflip_params: 140 + * 141 + * Page flip IRQ parameters, passed to registered handlers when 142 + * triggered. 143 + */ 98 144 struct common_irq_params 99 145 pflip_params[DC_IRQ_SOURCE_PFLIP_LAST - DC_IRQ_SOURCE_PFLIP_FIRST + 1]; 100 146 147 + /** 148 + * @vblank_params: 149 + * 150 + * Vertical blanking IRQ parameters, passed to registered handlers when 151 + * triggered. 152 + */ 101 153 struct common_irq_params 102 154 vblank_params[DC_IRQ_SOURCE_VBLANK6 - DC_IRQ_SOURCE_VBLANK1 + 1]; 103 155 104 - /* this spin lock synchronizes access to 'irq_handler_list_table' */ 105 156 spinlock_t irq_handler_list_table_lock; 106 157 107 158 struct backlight_device *backlight_dev; ··· 161 110 162 111 struct mod_freesync *freesync_module; 163 112 164 - /** 165 - * Caches device atomic state for suspend/resume 166 - */ 167 113 struct drm_atomic_state *cached_state; 168 114 169 115 struct dm_comressor_info compressor;
+102 -13
drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_irq.c
··· 32 32 #include "amdgpu_dm.h" 33 33 #include "amdgpu_dm_irq.h" 34 34 35 + /** 36 + * DOC: overview 37 + * 38 + * DM provides another layer of IRQ management on top of what the base driver 39 + * already provides. This is something that could be cleaned up, and is a 40 + * future TODO item. 41 + * 42 + * The base driver provides IRQ source registration with DRM, handler 43 + * registration into the base driver's IRQ table, and a handler callback 44 + * amdgpu_irq_handler(), with which DRM calls on interrupts. This generic 45 + * handler looks up the IRQ table, and calls the respective 46 + * &amdgpu_irq_src_funcs.process hookups. 47 + * 48 + * What DM provides on top are two IRQ tables specifically for top-half and 49 + * bottom-half IRQ handling, with the bottom-half implementing workqueues: 50 + * 51 + * - &amdgpu_display_manager.irq_handler_list_high_tab 52 + * - &amdgpu_display_manager.irq_handler_list_low_tab 53 + * 54 + * They override the base driver's IRQ table, and the effect can be seen 55 + * in the hooks that DM provides for &amdgpu_irq_src_funcs.process. They 56 + * are all set to the DM generic handler amdgpu_dm_irq_handler(), which looks up 57 + * DM's IRQ tables. However, in order for base driver to recognize this hook, DM 58 + * still needs to register the IRQ with the base driver. See 59 + * dce110_register_irq_handlers() and dcn10_register_irq_handlers(). 60 + * 61 + * To expose DC's hardware interrupt toggle to the base driver, DM implements 62 + * &amdgpu_irq_src_funcs.set hooks. Base driver calls it through 63 + * amdgpu_irq_update() to enable or disable the interrupt. 64 + */ 65 + 35 66 /****************************************************************************** 36 67 * Private declarations. 37 68 *****************************************************************************/ 38 69 70 + /** 71 + * struct amdgpu_dm_irq_handler_data - Data for DM interrupt handlers. 72 + * 73 + * @list: Linked list entry referencing the next/previous handler 74 + * @handler: Handler function 75 + * @handler_arg: Argument passed to the handler when triggered 76 + * @dm: DM which this handler belongs to 77 + * @irq_source: DC interrupt source that this handler is registered for 78 + */ 39 79 struct amdgpu_dm_irq_handler_data { 40 80 struct list_head list; 41 81 interrupt_handler handler; 42 82 void *handler_arg; 43 83 44 - /* DM which this handler belongs to */ 45 84 struct amdgpu_display_manager *dm; 46 85 /* DAL irq source which registered for this interrupt. */ 47 86 enum dc_irq_source irq_source; ··· 107 68 } 108 69 109 70 /** 110 - * dm_irq_work_func - Handle an IRQ outside of the interrupt handler proper. 71 + * dm_irq_work_func() - Handle an IRQ outside of the interrupt handler proper. 111 72 * 112 73 * @work: work struct 113 74 */ ··· 138 99 * (The most common use is HPD interrupt) */ 139 100 } 140 101 141 - /** 142 - * Remove a handler and return a pointer to hander list from which the 102 + /* 103 + * Remove a handler and return a pointer to handler list from which the 143 104 * handler was removed. 144 105 */ 145 106 static struct list_head *remove_irq_handler(struct amdgpu_device *adev, ··· 242 203 * Note: caller is responsible for input validation. 243 204 *****************************************************************************/ 244 205 206 + /** 207 + * amdgpu_dm_irq_register_interrupt() - Register a handler within DM. 208 + * @adev: The base driver device containing the DM device. 209 + * @int_params: Interrupt parameters containing the source, and handler context 210 + * @ih: Function pointer to the interrupt handler to register 211 + * @handler_args: Arguments passed to the handler when the interrupt occurs 212 + * 213 + * Register an interrupt handler for the given IRQ source, under the given 214 + * context. The context can either be high or low. High context handlers are 215 + * executed directly within ISR context, while low context is executed within a 216 + * workqueue, thereby allowing operations that sleep. 217 + * 218 + * Registered handlers are called in a FIFO manner, i.e. the most recently 219 + * registered handler will be called first. 220 + * 221 + * Return: Handler data &struct amdgpu_dm_irq_handler_data containing the IRQ 222 + * source, handler function, and args 223 + */ 245 224 void *amdgpu_dm_irq_register_interrupt(struct amdgpu_device *adev, 246 225 struct dc_interrupt_params *int_params, 247 226 void (*ih)(void *), ··· 318 261 return handler_data; 319 262 } 320 263 264 + /** 265 + * amdgpu_dm_irq_unregister_interrupt() - Remove a handler from the DM IRQ table 266 + * @adev: The base driver device containing the DM device 267 + * @irq_source: IRQ source to remove the given handler from 268 + * @ih: Function pointer to the interrupt handler to unregister 269 + * 270 + * Go through both low and high context IRQ tables, and find the given handler 271 + * for the given irq source. If found, remove it. Otherwise, do nothing. 272 + */ 321 273 void amdgpu_dm_irq_unregister_interrupt(struct amdgpu_device *adev, 322 274 enum dc_irq_source irq_source, 323 275 void *ih) ··· 361 295 } 362 296 } 363 297 298 + /** 299 + * amdgpu_dm_irq_init() - Initialize DM IRQ management 300 + * @adev: The base driver device containing the DM device 301 + * 302 + * Initialize DM's high and low context IRQ tables. 303 + * 304 + * The N by M table contains N IRQ sources, with M 305 + * &struct amdgpu_dm_irq_handler_data hooked together in a linked list. The 306 + * list_heads are initialized here. When an interrupt n is triggered, all m 307 + * handlers are called in sequence, FIFO according to registration order. 308 + * 309 + * The low context table requires special steps to initialize, since handlers 310 + * will be deferred to a workqueue. See &struct irq_list_head. 311 + */ 364 312 int amdgpu_dm_irq_init(struct amdgpu_device *adev) 365 313 { 366 314 int src; ··· 397 317 return 0; 398 318 } 399 319 400 - /* DM IRQ and timer resource release */ 320 + /** 321 + * amdgpu_dm_irq_fini() - Tear down DM IRQ management 322 + * @adev: The base driver device containing the DM device 323 + * 324 + * Flush all work within the low context IRQ table. 325 + */ 401 326 void amdgpu_dm_irq_fini(struct amdgpu_device *adev) 402 327 { 403 328 int src; ··· 499 414 return 0; 500 415 } 501 416 502 - /** 417 + /* 503 418 * amdgpu_dm_irq_schedule_work - schedule all work items registered for the 504 419 * "irq_source". 505 420 */ ··· 524 439 525 440 } 526 441 527 - /** amdgpu_dm_irq_immediate_work 528 - * Callback high irq work immediately, don't send to work queue 442 + /* 443 + * amdgpu_dm_irq_immediate_work 444 + * Callback high irq work immediately, don't send to work queue 529 445 */ 530 446 static void amdgpu_dm_irq_immediate_work(struct amdgpu_device *adev, 531 447 enum dc_irq_source irq_source) ··· 553 467 DM_IRQ_TABLE_UNLOCK(adev, irq_table_flags); 554 468 } 555 469 556 - /* 557 - * amdgpu_dm_irq_handler 470 + /** 471 + * amdgpu_dm_irq_handler - Generic DM IRQ handler 472 + * @adev: amdgpu base driver device containing the DM device 473 + * @source: Unused 474 + * @entry: Data about the triggered interrupt 558 475 * 559 - * Generic IRQ handler, calls all registered high irq work immediately, and 560 - * schedules work for low irq 476 + * Calls all registered high irq work immediately, and schedules work for low 477 + * irq. The DM IRQ table is used to find the corresponding handlers. 561 478 */ 562 479 static int amdgpu_dm_irq_handler(struct amdgpu_device *adev, 563 480 struct amdgpu_irq_src *source, ··· 702 613 adev->hpd_irq.funcs = &dm_hpd_irq_funcs; 703 614 } 704 615 705 - /* 616 + /** 706 617 * amdgpu_dm_hpd_init - hpd setup callback. 707 618 * 708 619 * @adev: amdgpu_device pointer