drm-misc-next for 6.19: UAPI Changes: - panfrost: Add PANFROST_BO_SYNC ioctl - panthor: Add PANTHOR_BO_SYNC ioctl Core Changes: - atomic: Add drm_device pointer to drm_private_obj - bridge: Introduce drm_bridge_unplug, drm_bridge_enter, and drm_bridge_exit - dma-buf: Improve sg_table debugging - dma-fence: Add new helpers, and use them when needed - dp_mst: Avoid out-of-bounds access with VCPI==0 - gem: Reduce page table overhead with transparent huge pages - panic: Report invalid panic modes - sched: Add TODO entries - ttm: Various cleanups - vblank: Various refactoring and cleanups - Kconfig cleanups - Removed support for kdb Driver Changes: - amdxdna: Fix race conditions at suspend, Improve handling of zero tail pointers, Fix cu_idx being overwritten during command setup - ast: Support imported cursor buffers - - panthor: Enable timestamp propagation, Multiple improvements and fixes to improve the overall robustness, notably of the scheduler. - panels: - panel-edp: Support for CSW MNE007QB3-1, AUO B140HAN06.4, AUO B140QAX01.H Signed-off-by: Dave Airlie <airlied@redhat.com> [airlied: fix mm conflict] From: Maxime Ripard <mripard@redhat.com> Link: https://patch.msgid.link/20251212-spectacular-agama-of-abracadabra-aaef32@penduick
Unified · Split
| @@ -155,7 +155,12 @@ drm_gem_object_init() will create an shmfs file of the · 154 unmodified lines | ||
| 155 | 155 | requested size and store it into the struct :c:type:`struct |
| 156 | 156 | drm_gem_object <drm_gem_object>` filp field. The memory is |
| 157 | 157 | used as either main storage for the object when the graphics hardware |
| 158 | −uses system memory directly or as a backing store otherwise. | |
| 158 | +uses system memory directly or as a backing store otherwise. Drivers | |
| 159 | +can call drm_gem_huge_mnt_create() to create, mount and use a huge | |
| 160 | +shmem mountpoint instead of the default one ('shm_mnt'). For builds | |
| 161 | +with CONFIG_TRANSPARENT_HUGEPAGE enabled, further calls to | |
| 162 | +drm_gem_object_init() will let shmem allocate huge pages when | |
| 163 | +possible. | |
| 159 | 164 | |
| 160 | 165 | Drivers are responsible for the actual physical pages allocation by |
| 161 | 166 | calling shmem_read_mapping_page_gfp() for each page. |
| @@ -290,15 +295,27 @@ The open and close operations must update the GEM object reference · 128 unmodified lines | ||
| 290 | 295 | count. Drivers can use the drm_gem_vm_open() and drm_gem_vm_close() helper |
| 291 | 296 | functions directly as open and close handlers. |
| 292 | 297 | |
| 293 | −The fault operation handler is responsible for mapping individual pages | |
| 294 | −to userspace when a page fault occurs. Depending on the memory | |
| 295 | −allocation scheme, drivers can allocate pages at fault time, or can | |
| 296 | −decide to allocate memory for the GEM object at the time the object is | |
| 297 | −created. | |
| 298 | +The fault operation handler is responsible for mapping pages to | |
| 299 | +userspace when a page fault occurs. Depending on the memory allocation | |
| 300 | +scheme, drivers can allocate pages at fault time, or can decide to | |
| 301 | +allocate memory for the GEM object at the time the object is created. | |
| 298 | 302 | |
| 299 | 303 | Drivers that want to map the GEM object upfront instead of handling page |
| 300 | 304 | faults can implement their own mmap file operation handler. |
| 301 | 305 | |
| 306 | +In order to reduce page table overhead, if the internal shmem mountpoint | |
| 307 | +"shm_mnt" is configured to use transparent huge pages (for builds with | |
| 308 | +CONFIG_TRANSPARENT_HUGEPAGE enabled) and if the shmem backing store | |
| 309 | +managed to allocate a huge page for a faulty address, the fault handler | |
| 310 | +will first attempt to insert that huge page into the VMA before falling | |
| 311 | +back to individual page insertion. mmap() user address alignment for GEM | |
| 312 | +objects is handled by providing a custom get_unmapped_area file | |
| 313 | +operation which forwards to the shmem backing store. For most drivers, | |
| 314 | +which don't create a huge mountpoint by default or through a module | |
| 315 | +parameter, transparent huge pages can be enabled by either setting the | |
| 316 | +"transparent_hugepage_shmem" kernel parameter or the | |
| 317 | +"/sys/kernel/mm/transparent_hugepage/shmem_enabled" sysfs knob. | |
| 318 | + | |
| 302 | 319 | For platforms without MMU the GEM core provides a helper method |
| 303 | 320 | drm_gem_dma_get_unmapped_area(). The mmap() routines will call this to get a |
| 304 | 321 | proposed address for the mapping. |
| @@ -878,6 +878,51 @@ Contact: Christian König · 877 unmodified lines | ||
| 878 | 878 | |
| 879 | 879 | Level: Starter |
| 880 | 880 | |
| 881 | +DRM GPU Scheduler | |
| 882 | +================= | |
| 883 | + | |
| 884 | +Provide a universal successor for drm_sched_resubmit_jobs() | |
| 885 | +----------------------------------------------------------- | |
| 886 | + | |
| 887 | +drm_sched_resubmit_jobs() is deprecated. Main reason being that it leads to | |
| 888 | +reinitializing dma_fences. See that function's docu for details. The better | |
| 889 | +approach for valid resubmissions by amdgpu and Xe is (apparently) to figure out | |
| 890 | +which job (and, through association: which entity) caused the hang. Then, the | |
| 891 | +job's buffer data, together with all other jobs' buffer data currently in the | |
| 892 | +same hardware ring, must be invalidated. This can for example be done by | |
| 893 | +overwriting it. amdgpu currently determines which jobs are in the ring and need | |
| 894 | +to be overwritten by keeping copies of the job. Xe obtains that information by | |
| 895 | +directly accessing drm_sched's pending_list. | |
| 896 | + | |
| 897 | +Tasks: | |
| 898 | + | |
| 899 | +1. implement scheduler functionality through which the driver can obtain the | |
| 900 | + information which *broken* jobs are currently in the hardware ring. | |
| 901 | +2. Such infrastructure would then typically be used in | |
| 902 | + drm_sched_backend_ops.timedout_job(). Document that. | |
| 903 | +3. Port a driver as first user. | |
| 904 | +4. Document the new alternative in the docu of deprecated | |
| 905 | + drm_sched_resubmit_jobs(). | |
| 906 | + | |
| 907 | +Contact: Christian König <christian.koenig@amd.com> | |
| 908 | + Philipp Stanner <phasta@kernel.org> | |
| 909 | + | |
| 910 | +Level: Advanced | |
| 911 | + | |
| 912 | +Add locking for runqueues | |
| 913 | +------------------------- | |
| 914 | + | |
| 915 | +There is an old FIXME by Sima in include/drm/gpu_scheduler.h. It details that | |
| 916 | +struct drm_sched_rq is read at many places without any locks, not even with a | |
| 917 | +READ_ONCE. At XDC 2025 no one could really tell why that is the case, whether | |
| 918 | +locks are needed and whether they could be added. (But for real, that should | |
| 919 | +probably be locked!). Check whether it's possible to add locks everywhere, and | |
| 920 | +do so if yes. | |
| 921 | + | |
| 922 | +Contact: Philipp Stanner <phasta@kernel.org> | |
| 923 | + | |
| 924 | +Level: Intermediate | |
| 925 | + | |
| 881 | 926 | Outside DRM |
| 882 | 927 | =========== |
| 883 | 928 | |
| @@ -889,34 +889,6 @@ in the virtual console layer. On resuming kernel execution, the kernel · 888 unmodified lines | ||
| 889 | 889 | debugger calls kgdboc_post_exp_handler() which in turn calls |
| 890 | 890 | con_debug_leave(). |
| 891 | 891 | |
| 892 | −Any video driver that wants to be compatible with the kernel debugger | |
| 893 | −and the atomic kms callbacks must implement the ``mode_set_base_atomic``, | |
| 894 | −``fb_debug_enter`` and ``fb_debug_leave operations``. For the | |
| 895 | −``fb_debug_enter`` and ``fb_debug_leave`` the option exists to use the | |
| 896 | −generic drm fb helper functions or implement something custom for the | |
| 897 | −hardware. The following example shows the initialization of the | |
| 898 | −.mode_set_base_atomic operation in | |
| 899 | −drivers/gpu/drm/i915/intel_display.c:: | |
| 900 | − | |
| 901 | − | |
| 902 | − static const struct drm_crtc_helper_funcs intel_helper_funcs = { | |
| 903 | − [...] | |
| 904 | − .mode_set_base_atomic = intel_pipe_set_base_atomic, | |
| 905 | − [...] | |
| 906 | − }; | |
| 907 | − | |
| 908 | − | |
| 909 | −Here is an example of how the i915 driver initializes the | |
| 910 | −fb_debug_enter and fb_debug_leave functions to use the generic drm | |
| 911 | −helpers in ``drivers/gpu/drm/i915/intel_fb.c``:: | |
| 912 | − | |
| 913 | − | |
| 914 | − static struct fb_ops intelfb_ops = { | |
| 915 | − [...] | |
| 916 | − .fb_debug_enter = drm_fb_helper_debug_enter, | |
| 917 | − .fb_debug_leave = drm_fb_helper_debug_leave, | |
| 918 | − [...] | |
| 919 | − }; | |
| 920 | 892 | |
| 921 | 893 | |
| 922 | 894 | Credits |
| @@ -39,7 +39,6 @@ static int aie2_send_mgmt_msg_wait(struct amdxdna_dev_hdl *ndev, · 38 unmodified lines | ||
| 39 | 39 | if (!ndev->mgmt_chann) |
| 40 | 40 | return -ENODEV; |
| 41 | 41 | |
| 42 | − drm_WARN_ON(&xdna->ddev, xdna->rpm_on && !mutex_is_locked(&xdna->dev_lock)); | |
| 43 | 42 | ret = xdna_send_msg_wait(xdna, ndev->mgmt_chann, msg); |
| 44 | 43 | if (ret == -ETIME) { |
| 45 | 44 | xdna_mailbox_stop_channel(ndev->mgmt_chann); |
| @@ -59,8 +58,15 @@ static int aie2_send_mgmt_msg_wait(struct amdxdna_dev_hdl *ndev, · 13 unmodified lines | ||
| 59 | 58 | int aie2_suspend_fw(struct amdxdna_dev_hdl *ndev) |
| 60 | 59 | { |
| 61 | 60 | DECLARE_AIE2_MSG(suspend, MSG_OP_SUSPEND); |
| 61 | + int ret; | |
| 62 | 62 | |
| 63 | − return aie2_send_mgmt_msg_wait(ndev, &msg); | |
| 63 | + ret = aie2_send_mgmt_msg_wait(ndev, &msg); | |
| 64 | + if (ret) { | |
| 65 | + XDNA_ERR(ndev->xdna, "Failed to suspend fw, ret %d", ret); | |
| 66 | + return ret; | |
| 67 | + } | |
| 68 | + | |
| 69 | + return aie2_psp_waitmode_poll(ndev->psp_hdl); | |
| 64 | 70 | } |
| 65 | 71 | |
| 66 | 72 | int aie2_resume_fw(struct amdxdna_dev_hdl *ndev) |
| @@ -646,6 +652,7 @@ aie2_cmdlist_fill_npu_cf(struct amdxdna_gem_obj *cmd_bo, void *slot, size_t *siz · 579 unmodified lines | ||
| 646 | 652 | u32 cmd_len; |
| 647 | 653 | void *cmd; |
| 648 | 654 | |
| 655 | + memset(npu_slot, 0, sizeof(*npu_slot)); | |
| 649 | 656 | cmd = amdxdna_cmd_get_payload(cmd_bo, &cmd_len); |
| 650 | 657 | if (*size < sizeof(*npu_slot) + cmd_len) |
| 651 | 658 | return -EINVAL; |
| @@ -654,7 +661,6 @@ aie2_cmdlist_fill_npu_cf(struct amdxdna_gem_obj *cmd_bo, void *slot, size_t *siz · 2 unmodified lines | ||
| 654 | 661 | if (npu_slot->cu_idx == INVALID_CU_IDX) |
| 655 | 662 | return -EINVAL; |
| 656 | 663 | |
| 657 | − memset(npu_slot, 0, sizeof(*npu_slot)); | |
| 658 | 664 | npu_slot->type = EXEC_NPU_TYPE_NON_ELF; |
| 659 | 665 | npu_slot->arg_cnt = cmd_len / sizeof(u32); |
| 660 | 666 | memcpy(npu_slot->args, cmd, cmd_len); |
| @@ -671,6 +677,7 @@ aie2_cmdlist_fill_npu_dpu(struct amdxdna_gem_obj *cmd_bo, void *slot, size_t *si · 10 unmodified lines | ||
| 671 | 677 | u32 cmd_len; |
| 672 | 678 | u32 arg_sz; |
| 673 | 679 | |
| 680 | + memset(npu_slot, 0, sizeof(*npu_slot)); | |
| 674 | 681 | sn = amdxdna_cmd_get_payload(cmd_bo, &cmd_len); |
| 675 | 682 | arg_sz = cmd_len - sizeof(*sn); |
| 676 | 683 | if (cmd_len < sizeof(*sn) || arg_sz > MAX_NPU_ARGS_SIZE) |
| @@ -683,7 +690,6 @@ aie2_cmdlist_fill_npu_dpu(struct amdxdna_gem_obj *cmd_bo, void *slot, size_t *si · 6 unmodified lines | ||
| 683 | 690 | if (npu_slot->cu_idx == INVALID_CU_IDX) |
| 684 | 691 | return -EINVAL; |
| 685 | 692 | |
| 686 | − memset(npu_slot, 0, sizeof(*npu_slot)); | |
| 687 | 693 | npu_slot->type = EXEC_NPU_TYPE_PARTIAL_ELF; |
| 688 | 694 | npu_slot->inst_buf_addr = sn->buffer; |
| 689 | 695 | npu_slot->inst_size = sn->buffer_size; |
| @@ -703,6 +709,7 @@ aie2_cmdlist_fill_npu_preempt(struct amdxdna_gem_obj *cmd_bo, void *slot, size_t · 13 unmodified lines | ||
| 703 | 709 | u32 cmd_len; |
| 704 | 710 | u32 arg_sz; |
| 705 | 711 | |
| 712 | + memset(npu_slot, 0, sizeof(*npu_slot)); | |
| 706 | 713 | pd = amdxdna_cmd_get_payload(cmd_bo, &cmd_len); |
| 707 | 714 | arg_sz = cmd_len - sizeof(*pd); |
| 708 | 715 | if (cmd_len < sizeof(*pd) || arg_sz > MAX_NPU_ARGS_SIZE) |
| @@ -715,7 +722,6 @@ aie2_cmdlist_fill_npu_preempt(struct amdxdna_gem_obj *cmd_bo, void *slot, size_t · 6 unmodified lines | ||
| 715 | 722 | if (npu_slot->cu_idx == INVALID_CU_IDX) |
| 716 | 723 | return -EINVAL; |
| 717 | 724 | |
| 718 | − memset(npu_slot, 0, sizeof(*npu_slot)); | |
| 719 | 725 | npu_slot->type = EXEC_NPU_TYPE_PREEMPT; |
| 720 | 726 | npu_slot->inst_buf_addr = pd->inst_buf; |
| 721 | 727 | npu_slot->save_buf_addr = pd->save_buf; |
| @@ -739,6 +745,7 @@ aie2_cmdlist_fill_npu_elf(struct amdxdna_gem_obj *cmd_bo, void *slot, size_t *si · 17 unmodified lines | ||
| 739 | 745 | u32 cmd_len; |
| 740 | 746 | u32 arg_sz; |
| 741 | 747 | |
| 748 | + memset(npu_slot, 0, sizeof(*npu_slot)); | |
| 742 | 749 | pd = amdxdna_cmd_get_payload(cmd_bo, &cmd_len); |
| 743 | 750 | arg_sz = cmd_len - sizeof(*pd); |
| 744 | 751 | if (cmd_len < sizeof(*pd) || arg_sz > MAX_NPU_ARGS_SIZE) |
| @@ -747,7 +754,6 @@ aie2_cmdlist_fill_npu_elf(struct amdxdna_gem_obj *cmd_bo, void *slot, size_t *si · 2 unmodified lines | ||
| 747 | 754 | if (*size < sizeof(*npu_slot) + arg_sz) |
| 748 | 755 | return -EINVAL; |
| 749 | 756 | |
| 750 | − memset(npu_slot, 0, sizeof(*npu_slot)); | |
| 751 | 757 | npu_slot->type = EXEC_NPU_TYPE_ELF; |
| 752 | 758 | npu_slot->inst_buf_addr = pd->inst_buf; |
| 753 | 759 | npu_slot->save_buf_addr = pd->save_buf; |
| @@ -322,7 +322,7 @@ static int aie2_xrs_set_dft_dpm_level(struct drm_device *ddev, u32 dpm_level) · 321 unmodified lines | ||
| 322 | 322 | if (ndev->pw_mode != POWER_MODE_DEFAULT || ndev->dpm_level == dpm_level) |
| 323 | 323 | return 0; |
| 324 | 324 | |
| 325 | − return ndev->priv->hw_ops.set_dpm(ndev, dpm_level); | |
| 325 | + return aie2_pm_set_dpm(ndev, dpm_level); | |
| 326 | 326 | } |
| 327 | 327 | |
| 328 | 328 | static struct xrs_action_ops aie2_xrs_actions = { |
| @@ -70,6 +70,7 @@ enum psp_reg_idx { · 69 unmodified lines | ||
| 70 | 70 | PSP_INTR_REG = PSP_NUM_IN_REGS, |
| 71 | 71 | PSP_STATUS_REG, |
| 72 | 72 | PSP_RESP_REG, |
| 73 | + PSP_PWAITMODE_REG, | |
| 73 | 74 | PSP_MAX_REGS /* Keep this at the end */ |
| 74 | 75 | }; |
| 75 | 76 | |
| @@ -285,11 +286,13 @@ int npu4_set_dpm(struct amdxdna_dev_hdl *ndev, u32 dpm_level); · 209 unmodified lines | ||
| 285 | 286 | /* aie2_pm.c */ |
| 286 | 287 | int aie2_pm_init(struct amdxdna_dev_hdl *ndev); |
| 287 | 288 | int aie2_pm_set_mode(struct amdxdna_dev_hdl *ndev, enum amdxdna_power_mode_type target); |
| 289 | +int aie2_pm_set_dpm(struct amdxdna_dev_hdl *ndev, u32 dpm_level); | |
| 288 | 290 | |
| 289 | 291 | /* aie2_psp.c */ |
| 290 | 292 | struct psp_device *aie2m_psp_create(struct drm_device *ddev, struct psp_config *conf); |
| 291 | 293 | int aie2_psp_start(struct psp_device *psp); |
| 292 | 294 | void aie2_psp_stop(struct psp_device *psp); |
| 295 | +int aie2_psp_waitmode_poll(struct psp_device *psp); | |
| 293 | 296 | |
| 294 | 297 | /* aie2_error.c */ |
| 295 | 298 | int aie2_error_async_events_alloc(struct amdxdna_dev_hdl *ndev); |
| @@ -10,6 +10,7 @@ · 9 unmodified lines | ||
| 10 | 10 | |
| 11 | 11 | #include "aie2_pci.h" |
| 12 | 12 | #include "amdxdna_pci_drv.h" |
| 13 | +#include "amdxdna_pm.h" | |
| 13 | 14 | |
| 14 | 15 | #define AIE2_CLK_GATING_ENABLE 1 |
| 15 | 16 | #define AIE2_CLK_GATING_DISABLE 0 |
| @@ -26,6 +27,20 @@ static int aie2_pm_set_clk_gating(struct amdxdna_dev_hdl *ndev, u32 val) · 10 unmodified lines | ||
| 26 | 27 | return 0; |
| 27 | 28 | } |
| 28 | 29 | |
| 30 | +int aie2_pm_set_dpm(struct amdxdna_dev_hdl *ndev, u32 dpm_level) | |
| 31 | +{ | |
| 32 | + int ret; | |
| 33 | + | |
| 34 | + ret = amdxdna_pm_resume_get(ndev->xdna); | |
| 35 | + if (ret) | |
| 36 | + return ret; | |
| 37 | + | |
| 38 | + ret = ndev->priv->hw_ops.set_dpm(ndev, dpm_level); | |
| 39 | + amdxdna_pm_suspend_put(ndev->xdna); | |
| 40 | + | |
| 41 | + return ret; | |
| 42 | +} | |
| 43 | + | |
| 29 | 44 | int aie2_pm_init(struct amdxdna_dev_hdl *ndev) |
| 30 | 45 | { |
| 31 | 46 | int ret; |
| @@ -94,7 +109,7 @@ int aie2_pm_set_mode(struct amdxdna_dev_hdl *ndev, enum amdxdna_power_mode_type · 62 unmodified lines | ||
| 94 | 109 | return -EOPNOTSUPP; |
| 95 | 110 | } |
| 96 | 111 | |
| 97 | − ret = ndev->priv->hw_ops.set_dpm(ndev, dpm_level); | |
| 112 | + ret = aie2_pm_set_dpm(ndev, dpm_level); | |
| 98 | 113 | if (ret) |
| 99 | 114 | return ret; |
| 100 | 115 | |
| @@ -76,6 +76,21 @@ static int psp_exec(struct psp_device *psp, u32 *reg_vals) · 75 unmodified lines | ||
| 76 | 76 | return 0; |
| 77 | 77 | } |
| 78 | 78 | |
| 79 | +int aie2_psp_waitmode_poll(struct psp_device *psp) | |
| 80 | +{ | |
| 81 | + struct amdxdna_dev *xdna = to_xdna_dev(psp->ddev); | |
| 82 | + u32 mode_reg; | |
| 83 | + int ret; | |
| 84 | + | |
| 85 | + ret = readx_poll_timeout(readl, PSP_REG(psp, PSP_PWAITMODE_REG), mode_reg, | |
| 86 | + (mode_reg & 0x1) == 1, | |
| 87 | + PSP_POLL_INTERVAL, PSP_POLL_TIMEOUT); | |
| 88 | + if (ret) | |
| 89 | + XDNA_ERR(xdna, "fw waitmode reg error, ret %d", ret); | |
| 90 | + | |
| 91 | + return ret; | |
| 92 | +} | |
| 93 | + | |
| 79 | 94 | void aie2_psp_stop(struct psp_device *psp) |
| 80 | 95 | { |
| 81 | 96 | u32 reg_vals[PSP_NUM_IN_REGS] = { PSP_RELEASE_TMR, }; |
| @@ -11,7 +11,6 @@ · 10 unmodified lines | ||
| 11 | 11 | |
| 12 | 12 | #include "aie2_pci.h" |
| 13 | 13 | #include "amdxdna_pci_drv.h" |
| 14 | −#include "amdxdna_pm.h" | |
| 15 | 14 | |
| 16 | 15 | #define SMU_RESULT_OK 1 |
| 17 | 16 | |
| @@ -67,16 +66,12 @@ int npu1_set_dpm(struct amdxdna_dev_hdl *ndev, u32 dpm_level) · 49 unmodified lines | ||
| 67 | 66 | u32 freq; |
| 68 | 67 | int ret; |
| 69 | 68 | |
| 70 | − ret = amdxdna_pm_resume_get(ndev->xdna); | |
| 71 | − if (ret) | |
| 72 | − return ret; | |
| 73 | − | |
| 74 | 69 | ret = aie2_smu_exec(ndev, AIE2_SMU_SET_MPNPUCLK_FREQ, |
| 75 | 70 | ndev->priv->dpm_clk_tbl[dpm_level].npuclk, &freq); |
| 76 | 71 | if (ret) { |
| 77 | 72 | XDNA_ERR(ndev->xdna, "Set npu clock to %d failed, ret %d\n", |
| 78 | 73 | ndev->priv->dpm_clk_tbl[dpm_level].npuclk, ret); |
| 79 | − goto suspend_put; | |
| 74 | + return ret; | |
| 80 | 75 | } |
| 81 | 76 | ndev->npuclk_freq = freq; |
| 82 | 77 | |
| @@ -85,10 +80,9 @@ int npu1_set_dpm(struct amdxdna_dev_hdl *ndev, u32 dpm_level) · 2 unmodified lines | ||
| 85 | 80 | if (ret) { |
| 86 | 81 | XDNA_ERR(ndev->xdna, "Set h clock to %d failed, ret %d\n", |
| 87 | 82 | ndev->priv->dpm_clk_tbl[dpm_level].hclk, ret); |
| 88 | − goto suspend_put; | |
| 83 | + return ret; | |
| 89 | 84 | } |
| 90 | 85 | |
| 91 | − amdxdna_pm_suspend_put(ndev->xdna); | |
| 92 | 86 | ndev->hclk_freq = freq; |
| 93 | 87 | ndev->dpm_level = dpm_level; |
| 94 | 88 | ndev->max_tops = 2 * ndev->total_col; |
| @@ -98,35 +92,26 @@ int npu1_set_dpm(struct amdxdna_dev_hdl *ndev, u32 dpm_level) · 3 unmodified lines | ||
| 98 | 92 | ndev->npuclk_freq, ndev->hclk_freq); |
| 99 | 93 | |
| 100 | 94 | return 0; |
| 101 | − | |
| 102 | −suspend_put: | |
| 103 | − amdxdna_pm_suspend_put(ndev->xdna); | |
| 104 | − return ret; | |
| 105 | 95 | } |
| 106 | 96 | |
| 107 | 97 | int npu4_set_dpm(struct amdxdna_dev_hdl *ndev, u32 dpm_level) |
| 108 | 98 | { |
| 109 | 99 | int ret; |
| 110 | 100 | |
| 111 | − ret = amdxdna_pm_resume_get(ndev->xdna); | |
| 112 | − if (ret) | |
| 113 | − return ret; | |
| 114 | − | |
| 115 | 101 | ret = aie2_smu_exec(ndev, AIE2_SMU_SET_HARD_DPMLEVEL, dpm_level, NULL); |
| 116 | 102 | if (ret) { |
| 117 | 103 | XDNA_ERR(ndev->xdna, "Set hard dpm level %d failed, ret %d ", |
| 118 | 104 | dpm_level, ret); |
| 119 | − goto suspend_put; | |
| 105 | + return ret; | |
| 120 | 106 | } |
| 121 | 107 | |
| 122 | 108 | ret = aie2_smu_exec(ndev, AIE2_SMU_SET_SOFT_DPMLEVEL, dpm_level, NULL); |
| 123 | 109 | if (ret) { |
| 124 | 110 | XDNA_ERR(ndev->xdna, "Set soft dpm level %d failed, ret %d", |
| 125 | 111 | dpm_level, ret); |
| 126 | − goto suspend_put; | |
| 112 | + return ret; | |
| 127 | 113 | } |
| 128 | 114 | |
| 129 | − amdxdna_pm_suspend_put(ndev->xdna); | |
| 130 | 115 | ndev->npuclk_freq = ndev->priv->dpm_clk_tbl[dpm_level].npuclk; |
| 131 | 116 | ndev->hclk_freq = ndev->priv->dpm_clk_tbl[dpm_level].hclk; |
| 132 | 117 | ndev->dpm_level = dpm_level; |
| @@ -137,10 +122,6 @@ int npu4_set_dpm(struct amdxdna_dev_hdl *ndev, u32 dpm_level) · 4 unmodified lines | ||
| 137 | 122 | ndev->npuclk_freq, ndev->hclk_freq); |
| 138 | 123 | |
| 139 | 124 | return 0; |
| 140 | − | |
| 141 | −suspend_put: | |
| 142 | − amdxdna_pm_suspend_put(ndev->xdna); | |
| 143 | − return ret; | |
| 144 | 125 | } |
| 145 | 126 | |
| 146 | 127 | int aie2_smu_init(struct amdxdna_dev_hdl *ndev) |
| @@ -112,22 +112,6 @@ static u32 mailbox_reg_read(struct mailbox_channel *mb_chann, u32 mbox_reg) · 111 unmodified lines | ||
| 112 | 112 | return readl(ringbuf_addr); |
| 113 | 113 | } |
| 114 | 114 | |
| 115 | −static int mailbox_reg_read_non_zero(struct mailbox_channel *mb_chann, u32 mbox_reg, u32 *val) | |
| 116 | −{ | |
| 117 | − struct xdna_mailbox_res *mb_res = &mb_chann->mb->res; | |
| 118 | − void __iomem *ringbuf_addr = mb_res->mbox_base + mbox_reg; | |
| 119 | − int ret, value; | |
| 120 | − | |
| 121 | − /* Poll till value is not zero */ | |
| 122 | − ret = readx_poll_timeout(readl, ringbuf_addr, value, | |
| 123 | − value, 1 /* us */, 100); | |
| 124 | − if (ret < 0) | |
| 125 | − return ret; | |
| 126 | − | |
| 127 | − *val = value; | |
| 128 | − return 0; | |
| 129 | −} | |
| 130 | − | |
| 131 | 115 | static inline void |
| 132 | 116 | mailbox_set_headptr(struct mailbox_channel *mb_chann, u32 headptr_val) |
| 133 | 117 | { |
| @@ -286,8 +270,7 @@ static int mailbox_get_msg(struct mailbox_channel *mb_chann) · 152 unmodified lines | ||
| 286 | 270 | u32 start_addr; |
| 287 | 271 | int ret; |
| 288 | 272 | |
| 289 | − if (mailbox_reg_read_non_zero(mb_chann, mb_chann->res[CHAN_RES_I2X].mb_tail_ptr_reg, &tail)) | |
| 290 | − return -EINVAL; | |
| 273 | + tail = mailbox_get_tailptr(mb_chann, CHAN_RES_I2X); | |
| 291 | 274 | head = mb_chann->i2x_head; |
| 292 | 275 | ringbuf_size = mailbox_get_ringbuf_size(mb_chann, CHAN_RES_I2X); |
| 293 | 276 | start_addr = mb_chann->res[CHAN_RES_I2X].rb_start_addr; |
| @@ -101,7 +101,6 @@ struct amdxdna_dev { · 100 unmodified lines | ||
| 101 | 101 | struct amdxdna_fw_ver fw_ver; |
| 102 | 102 | struct rw_semaphore notifier_lock; /* for mmu notifier*/ |
| 103 | 103 | struct workqueue_struct *notifier_wq; |
| 104 | − bool rpm_on; | |
| 105 | 104 | }; |
| 106 | 105 | |
| 107 | 106 | /* |
| @@ -15,14 +15,9 @@ int amdxdna_pm_suspend(struct device *dev) · 14 unmodified lines | ||
| 15 | 15 | { |
| 16 | 16 | struct amdxdna_dev *xdna = to_xdna_dev(dev_get_drvdata(dev)); |
| 17 | 17 | int ret = -EOPNOTSUPP; |
| 18 | − bool rpm; | |
| 19 | 18 | |
| 20 | − if (xdna->dev_info->ops->suspend) { | |
| 21 | − rpm = xdna->rpm_on; | |
| 22 | − xdna->rpm_on = false; | |
| 19 | + if (xdna->dev_info->ops->suspend) | |
| 23 | 20 | ret = xdna->dev_info->ops->suspend(xdna); |
| 24 | − xdna->rpm_on = rpm; | |
| 25 | − } | |
| 26 | 21 | |
| 27 | 22 | XDNA_DBG(xdna, "Suspend done ret %d", ret); |
| 28 | 23 | return ret; |
| @@ -32,14 +27,9 @@ int amdxdna_pm_resume(struct device *dev) · 3 unmodified lines | ||
| 32 | 27 | { |
| 33 | 28 | struct amdxdna_dev *xdna = to_xdna_dev(dev_get_drvdata(dev)); |
| 34 | 29 | int ret = -EOPNOTSUPP; |
| 35 | − bool rpm; | |
| 36 | 30 | |
| 37 | − if (xdna->dev_info->ops->resume) { | |
| 38 | − rpm = xdna->rpm_on; | |
| 39 | − xdna->rpm_on = false; | |
| 31 | + if (xdna->dev_info->ops->resume) | |
| 40 | 32 | ret = xdna->dev_info->ops->resume(xdna); |
| 41 | − xdna->rpm_on = rpm; | |
| 42 | − } | |
| 43 | 33 | |
| 44 | 34 | XDNA_DBG(xdna, "Resume done ret %d", ret); |
| 45 | 35 | return ret; |
| @@ -50,9 +40,6 @@ int amdxdna_pm_resume_get(struct amdxdna_dev *xdna) · 4 unmodified lines | ||
| 50 | 40 | struct device *dev = xdna->ddev.dev; |
| 51 | 41 | int ret; |
| 52 | 42 | |
| 53 | − if (!xdna->rpm_on) | |
| 54 | − return 0; | |
| 55 | − | |
| 56 | 43 | ret = pm_runtime_resume_and_get(dev); |
| 57 | 44 | if (ret) { |
| 58 | 45 | XDNA_ERR(xdna, "Resume failed: %d", ret); |
| @@ -66,9 +53,6 @@ void amdxdna_pm_suspend_put(struct amdxdna_dev *xdna) · 7 unmodified lines | ||
| 66 | 53 | { |
| 67 | 54 | struct device *dev = xdna->ddev.dev; |
| 68 | 55 | |
| 69 | − if (!xdna->rpm_on) | |
| 70 | − return; | |
| 71 | − | |
| 72 | 56 | pm_runtime_put_autosuspend(dev); |
| 73 | 57 | } |
| 74 | 58 | |
| @@ -81,14 +65,12 @@ void amdxdna_pm_init(struct amdxdna_dev *xdna) · 6 unmodified lines | ||
| 81 | 65 | pm_runtime_use_autosuspend(dev); |
| 82 | 66 | pm_runtime_allow(dev); |
| 83 | 67 | pm_runtime_put_autosuspend(dev); |
| 84 | − xdna->rpm_on = true; | |
| 85 | 68 | } |
| 86 | 69 | |
| 87 | 70 | void amdxdna_pm_fini(struct amdxdna_dev *xdna) |
| 88 | 71 | { |
| 89 | 72 | struct device *dev = xdna->ddev.dev; |
| 90 | 73 | |
| 91 | − xdna->rpm_on = false; | |
| 92 | 74 | pm_runtime_get_noresume(dev); |
| 93 | 75 | pm_runtime_forbid(dev); |
| 94 | 76 | } |
| @@ -13,6 +13,7 @@ · 12 unmodified lines | ||
| 13 | 13 | #include "amdxdna_pci_drv.h" |
| 14 | 14 | |
| 15 | 15 | /* Address definition from NPU1 docs */ |
| 16 | +#define MPNPU_PWAITMODE 0x3010034 | |
| 16 | 17 | #define MPNPU_PUB_SEC_INTR 0x3010090 |
| 17 | 18 | #define MPNPU_PUB_PWRMGMT_INTR 0x3010094 |
| 18 | 19 | #define MPNPU_PUB_SCRATCH2 0x30100A0 |
| @@ -92,6 +93,7 @@ static const struct amdxdna_dev_priv npu1_dev_priv = { · 73 unmodified lines | ||
| 92 | 93 | DEFINE_BAR_OFFSET(PSP_INTR_REG, NPU1_PSP, MPNPU_PUB_SEC_INTR), |
| 93 | 94 | DEFINE_BAR_OFFSET(PSP_STATUS_REG, NPU1_PSP, MPNPU_PUB_SCRATCH2), |
| 94 | 95 | DEFINE_BAR_OFFSET(PSP_RESP_REG, NPU1_PSP, MPNPU_PUB_SCRATCH3), |
| 96 | + DEFINE_BAR_OFFSET(PSP_PWAITMODE_REG, NPU1_PSP, MPNPU_PWAITMODE), | |
| 95 | 97 | }, |
| 96 | 98 | .smu_regs_off = { |
| 97 | 99 | DEFINE_BAR_OFFSET(SMU_CMD_REG, NPU1_SMU, MPNPU_PUB_SCRATCH5), |
| @@ -13,6 +13,7 @@ · 12 unmodified lines | ||
| 13 | 13 | #include "amdxdna_pci_drv.h" |
| 14 | 14 | |
| 15 | 15 | /* NPU Public Registers on MpNPUAxiXbar (refer to Diag npu_registers.h) */ |
| 16 | +#define MPNPU_PWAITMODE 0x301003C | |
| 16 | 17 | #define MPNPU_PUB_SEC_INTR 0x3010060 |
| 17 | 18 | #define MPNPU_PUB_PWRMGMT_INTR 0x3010064 |
| 18 | 19 | #define MPNPU_PUB_SCRATCH0 0x301006C |
| @@ -85,6 +86,7 @@ static const struct amdxdna_dev_priv npu2_dev_priv = { · 66 unmodified lines | ||
| 85 | 86 | DEFINE_BAR_OFFSET(PSP_INTR_REG, NPU2_PSP, MP0_C2PMSG_73), |
| 86 | 87 | DEFINE_BAR_OFFSET(PSP_STATUS_REG, NPU2_PSP, MP0_C2PMSG_123), |
| 87 | 88 | DEFINE_BAR_OFFSET(PSP_RESP_REG, NPU2_REG, MPNPU_PUB_SCRATCH3), |
| 89 | + DEFINE_BAR_OFFSET(PSP_PWAITMODE_REG, NPU2_REG, MPNPU_PWAITMODE), | |
| 88 | 90 | }, |
| 89 | 91 | .smu_regs_off = { |
| 90 | 92 | DEFINE_BAR_OFFSET(SMU_CMD_REG, NPU2_SMU, MP1_C2PMSG_0), |
| @@ -13,6 +13,7 @@ · 12 unmodified lines | ||
| 13 | 13 | #include "amdxdna_pci_drv.h" |
| 14 | 14 | |
| 15 | 15 | /* NPU Public Registers on MpNPUAxiXbar (refer to Diag npu_registers.h) */ |
| 16 | +#define MPNPU_PWAITMODE 0x301003C | |
| 16 | 17 | #define MPNPU_PUB_SEC_INTR 0x3010060 |
| 17 | 18 | #define MPNPU_PUB_PWRMGMT_INTR 0x3010064 |
| 18 | 19 | #define MPNPU_PUB_SCRATCH0 0x301006C |
| @@ -116,6 +117,7 @@ static const struct amdxdna_dev_priv npu4_dev_priv = { · 97 unmodified lines | ||
| 116 | 117 | DEFINE_BAR_OFFSET(PSP_INTR_REG, NPU4_PSP, MP0_C2PMSG_73), |
| 117 | 118 | DEFINE_BAR_OFFSET(PSP_STATUS_REG, NPU4_PSP, MP0_C2PMSG_123), |
| 118 | 119 | DEFINE_BAR_OFFSET(PSP_RESP_REG, NPU4_REG, MPNPU_PUB_SCRATCH3), |
| 120 | + DEFINE_BAR_OFFSET(PSP_PWAITMODE_REG, NPU4_REG, MPNPU_PWAITMODE), | |
| 119 | 121 | }, |
| 120 | 122 | .smu_regs_off = { |
| 121 | 123 | DEFINE_BAR_OFFSET(SMU_CMD_REG, NPU4_SMU, MP1_C2PMSG_0), |
| @@ -13,6 +13,7 @@ · 12 unmodified lines | ||
| 13 | 13 | #include "amdxdna_pci_drv.h" |
| 14 | 14 | |
| 15 | 15 | /* NPU Public Registers on MpNPUAxiXbar (refer to Diag npu_registers.h) */ |
| 16 | +#define MPNPU_PWAITMODE 0x301003C | |
| 16 | 17 | #define MPNPU_PUB_SEC_INTR 0x3010060 |
| 17 | 18 | #define MPNPU_PUB_PWRMGMT_INTR 0x3010064 |
| 18 | 19 | #define MPNPU_PUB_SCRATCH0 0x301006C |
| @@ -85,6 +86,7 @@ static const struct amdxdna_dev_priv npu5_dev_priv = { · 66 unmodified lines | ||
| 85 | 86 | DEFINE_BAR_OFFSET(PSP_INTR_REG, NPU5_PSP, MP0_C2PMSG_73), |
| 86 | 87 | DEFINE_BAR_OFFSET(PSP_STATUS_REG, NPU5_PSP, MP0_C2PMSG_123), |
| 87 | 88 | DEFINE_BAR_OFFSET(PSP_RESP_REG, NPU5_REG, MPNPU_PUB_SCRATCH3), |
| 89 | + DEFINE_BAR_OFFSET(PSP_PWAITMODE_REG, NPU5_REG, MPNPU_PWAITMODE), | |
| 88 | 90 | }, |
| 89 | 91 | .smu_regs_off = { |
| 90 | 92 | DEFINE_BAR_OFFSET(SMU_CMD_REG, NPU5_SMU, MP1_C2PMSG_0), |
| @@ -13,6 +13,7 @@ · 12 unmodified lines | ||
| 13 | 13 | #include "amdxdna_pci_drv.h" |
| 14 | 14 | |
| 15 | 15 | /* NPU Public Registers on MpNPUAxiXbar (refer to Diag npu_registers.h) */ |
| 16 | +#define MPNPU_PWAITMODE 0x301003C | |
| 16 | 17 | #define MPNPU_PUB_SEC_INTR 0x3010060 |
| 17 | 18 | #define MPNPU_PUB_PWRMGMT_INTR 0x3010064 |
| 18 | 19 | #define MPNPU_PUB_SCRATCH0 0x301006C |
| @@ -85,6 +86,7 @@ static const struct amdxdna_dev_priv npu6_dev_priv = { · 66 unmodified lines | ||
| 85 | 86 | DEFINE_BAR_OFFSET(PSP_INTR_REG, NPU6_PSP, MP0_C2PMSG_73), |
| 86 | 87 | DEFINE_BAR_OFFSET(PSP_STATUS_REG, NPU6_PSP, MP0_C2PMSG_123), |
| 87 | 88 | DEFINE_BAR_OFFSET(PSP_RESP_REG, NPU6_REG, MPNPU_PUB_SCRATCH3), |
| 89 | + DEFINE_BAR_OFFSET(PSP_PWAITMODE_REG, NPU6_REG, MPNPU_PWAITMODE), | |
| 88 | 90 | }, |
| 89 | 91 | .smu_regs_off = { |
| 90 | 92 | DEFINE_BAR_OFFSET(SMU_CMD_REG, NPU6_SMU, MP1_C2PMSG_0), |
| @@ -55,7 +55,7 @@ config DMABUF_MOVE_NOTIFY · 54 unmodified lines | ||
| 55 | 55 | config DMABUF_DEBUG |
| 56 | 56 | bool "DMA-BUF debug checks" |
| 57 | 57 | depends on DMA_SHARED_BUFFER |
| 58 | − default y if DMA_API_DEBUG | |
| 58 | + default y if DEBUG | |
| 59 | 59 | help |
| 60 | 60 | This option enables additional checks for DMA-BUF importers and |
| 61 | 61 | exporters. Specifically it validates that importers do not peek at the |
| @@ -35,6 +35,12 @@ · 34 unmodified lines | ||
| 35 | 35 | |
| 36 | 36 | #include "dma-buf-sysfs-stats.h" |
| 37 | 37 | |
| 38 | +/* Wrapper to hide the sg_table page link from the importer */ | |
| 39 | +struct dma_buf_sg_table_wrapper { | |
| 40 | + struct sg_table *original; | |
| 41 | + struct sg_table wrapper; | |
| 42 | +}; | |
| 43 | + | |
| 38 | 44 | static inline int is_dma_buf_file(struct file *); |
| 39 | 45 | |
| 40 | 46 | static DEFINE_MUTEX(dmabuf_list_mutex); |
| @@ -820,21 +826,59 @@ void dma_buf_put(struct dma_buf *dmabuf) · 779 unmodified lines | ||
| 820 | 826 | } |
| 821 | 827 | EXPORT_SYMBOL_NS_GPL(dma_buf_put, "DMA_BUF"); |
| 822 | 828 | |
| 823 | −static void mangle_sg_table(struct sg_table *sg_table) | |
| 829 | +static int dma_buf_wrap_sg_table(struct sg_table **sg_table) | |
| 824 | 830 | { |
| 825 | −#ifdef CONFIG_DMABUF_DEBUG | |
| 826 | − int i; | |
| 827 | − struct scatterlist *sg; | |
| 828 | − | |
| 829 | − /* To catch abuse of the underlying struct page by importers mix | |
| 830 | − * up the bits, but take care to preserve the low SG_ bits to | |
| 831 | − * not corrupt the sgt. The mixing is undone on unmap | |
| 832 | − * before passing the sgt back to the exporter. | |
| 831 | + struct scatterlist *to_sg, *from_sg; | |
| 832 | + struct sg_table *from = *sg_table; | |
| 833 | + struct dma_buf_sg_table_wrapper *to; | |
| 834 | + int i, ret; | |
| 835 | + | |
| 836 | + if (!IS_ENABLED(CONFIG_DMABUF_DEBUG)) | |
| 837 | + return 0; | |
| 838 | + | |
| 839 | + /* | |
| 840 | + * To catch abuse of the underlying struct page by importers copy the | |
| 841 | + * sg_table without copying the page_link and give only the copy back to | |
| 842 | + * the importer. | |
| 833 | 843 | */ |
| 834 | − for_each_sgtable_sg(sg_table, sg, i) | |
| 835 | − sg->page_link ^= ~0xffUL; | |
| 836 | −#endif | |
| 844 | + to = kzalloc(sizeof(*to), GFP_KERNEL); | |
| 845 | + if (!to) | |
| 846 | + return -ENOMEM; | |
| 847 | + | |
| 848 | + ret = sg_alloc_table(&to->wrapper, from->nents, GFP_KERNEL); | |
| 849 | + if (ret) | |
| 850 | + goto free_to; | |
| 851 | + | |
| 852 | + to_sg = to->wrapper.sgl; | |
| 853 | + for_each_sgtable_dma_sg(from, from_sg, i) { | |
| 854 | + to_sg->offset = 0; | |
| 855 | + to_sg->length = 0; | |
| 856 | + sg_assign_page(to_sg, NULL); | |
| 857 | + sg_dma_address(to_sg) = sg_dma_address(from_sg); | |
| 858 | + sg_dma_len(to_sg) = sg_dma_len(from_sg); | |
| 859 | + to_sg = sg_next(to_sg); | |
| 860 | + } | |
| 837 | 861 | |
| 862 | + to->original = from; | |
| 863 | + *sg_table = &to->wrapper; | |
| 864 | + return 0; | |
| 865 | + | |
| 866 | +free_to: | |
| 867 | + kfree(to); | |
| 868 | + return ret; | |
| 869 | +} | |
| 870 | + | |
| 871 | +static void dma_buf_unwrap_sg_table(struct sg_table **sg_table) | |
| 872 | +{ | |
| 873 | + struct dma_buf_sg_table_wrapper *copy; | |
| 874 | + | |
| 875 | + if (!IS_ENABLED(CONFIG_DMABUF_DEBUG)) | |
| 876 | + return; | |
| 877 | + | |
| 878 | + copy = container_of(*sg_table, typeof(*copy), wrapper); | |
| 879 | + *sg_table = copy->original; | |
| 880 | + sg_free_table(©->wrapper); | |
| 881 | + kfree(copy); | |
| 838 | 882 | } |
| 839 | 883 | |
| 840 | 884 | static inline bool |
| @@ -1131,10 +1175,11 @@ struct sg_table *dma_buf_map_attachment(struct dma_buf_attachment *attach, · 290 unmodified lines | ||
| 1131 | 1175 | if (ret < 0) |
| 1132 | 1176 | goto error_unmap; |
| 1133 | 1177 | } |
| 1134 | − mangle_sg_table(sg_table); | |
| 1178 | + ret = dma_buf_wrap_sg_table(&sg_table); | |
| 1179 | + if (ret) | |
| 1180 | + goto error_unmap; | |
| 1135 | 1181 | |
| 1136 | −#ifdef CONFIG_DMA_API_DEBUG | |
| 1137 | − { | |
| 1182 | + if (IS_ENABLED(CONFIG_DMA_API_DEBUG)) { | |
| 1138 | 1183 | struct scatterlist *sg; |
| 1139 | 1184 | u64 addr; |
| 1140 | 1185 | int len; |
| @@ -1146,10 +1191,10 @@ struct sg_table *dma_buf_map_attachment(struct dma_buf_attachment *attach, · 5 unmodified lines | ||
| 1146 | 1191 | if (!PAGE_ALIGNED(addr) || !PAGE_ALIGNED(len)) { |
| 1147 | 1192 | pr_debug("%s: addr %llx or len %x is not page aligned!\n", |
| 1148 | 1193 | __func__, addr, len); |
| 1194 | + break; | |
| 1149 | 1195 | } |
| 1150 | 1196 | } |
| 1151 | 1197 | } |
| 1152 | −#endif /* CONFIG_DMA_API_DEBUG */ | |
| 1153 | 1198 | return sg_table; |
| 1154 | 1199 | |
| 1155 | 1200 | error_unmap: |
| @@ -1213,7 +1258,7 @@ void dma_buf_unmap_attachment(struct dma_buf_attachment *attach, · 57 unmodified lines | ||
| 1213 | 1258 | |
| 1214 | 1259 | dma_resv_assert_held(attach->dmabuf->resv); |
| 1215 | 1260 | |
| 1216 | − mangle_sg_table(sg_table); | |
| 1261 | + dma_buf_unwrap_sg_table(&sg_table); | |
| 1217 | 1262 | attach->dmabuf->ops->unmap_dma_buf(attach, sg_table, direction); |
| 1218 | 1263 | |
| 1219 | 1264 | if (dma_buf_pin_on_map(attach)) |
| @@ -358,11 +358,8 @@ void __dma_fence_might_wait(void) · 357 unmodified lines | ||
| 358 | 358 | * |
| 359 | 359 | * Unlike dma_fence_signal_timestamp(), this function must be called with |
| 360 | 360 | * &dma_fence.lock held. |
| 361 | − * | |
| 362 | − * Returns 0 on success and a negative error value when @fence has been | |
| 363 | − * signalled already. | |
| 364 | 361 | */ |
| 365 | −int dma_fence_signal_timestamp_locked(struct dma_fence *fence, | |
| 362 | +void dma_fence_signal_timestamp_locked(struct dma_fence *fence, | |
| 366 | 363 | ktime_t timestamp) |
| 367 | 364 | { |
| 368 | 365 | struct dma_fence_cb *cur, *tmp; |
| @@ -372,7 +369,7 @@ int dma_fence_signal_timestamp_locked(struct dma_fence *fence, · 3 unmodified lines | ||
| 372 | 369 | |
| 373 | 370 | if (unlikely(test_and_set_bit(DMA_FENCE_FLAG_SIGNALED_BIT, |
| 374 | 371 | &fence->flags))) |
| 375 | − return -EINVAL; | |
| 372 | + return; | |
| 376 | 373 | |
| 377 | 374 | /* Stash the cb_list before replacing it with the timestamp */ |
| 378 | 375 | list_replace(&fence->cb_list, &cb_list); |
| @@ -385,8 +382,6 @@ int dma_fence_signal_timestamp_locked(struct dma_fence *fence, · 6 unmodified lines | ||
| 385 | 382 | INIT_LIST_HEAD(&cur->node); |
| 386 | 383 | cur->func(fence, cur); |
| 387 | 384 | } |
| 388 | − | |
| 389 | − return 0; | |
| 390 | 385 | } |
| 391 | 386 | EXPORT_SYMBOL(dma_fence_signal_timestamp_locked); |
| 392 | 387 | |
| @@ -401,23 +396,17 @@ EXPORT_SYMBOL(dma_fence_signal_timestamp_locked); · 8 unmodified lines | ||
| 401 | 396 | * can only go from the unsignaled to the signaled state and not back, it will |
| 402 | 397 | * only be effective the first time. Set the timestamp provided as the fence |
| 403 | 398 | * signal timestamp. |
| 404 | − * | |
| 405 | − * Returns 0 on success and a negative error value when @fence has been | |
| 406 | − * signalled already. | |
| 407 | 399 | */ |
| 408 | −int dma_fence_signal_timestamp(struct dma_fence *fence, ktime_t timestamp) | |
| 400 | +void dma_fence_signal_timestamp(struct dma_fence *fence, ktime_t timestamp) | |
| 409 | 401 | { |
| 410 | 402 | unsigned long flags; |
| 411 | − int ret; | |
| 412 | 403 | |
| 413 | 404 | if (WARN_ON(!fence)) |
| 414 | − return -EINVAL; | |
| 405 | + return; | |
| 415 | 406 | |
| 416 | 407 | spin_lock_irqsave(fence->lock, flags); |
| 417 | − ret = dma_fence_signal_timestamp_locked(fence, timestamp); | |
| 408 | + dma_fence_signal_timestamp_locked(fence, timestamp); | |
| 418 | 409 | spin_unlock_irqrestore(fence->lock, flags); |
| 419 | − | |
| 420 | − return ret; | |
| 421 | 410 | } |
| 422 | 411 | EXPORT_SYMBOL(dma_fence_signal_timestamp); |
| 423 | 412 | |
| @@ -433,16 +422,57 @@ EXPORT_SYMBOL(dma_fence_signal_timestamp); · 9 unmodified lines | ||
| 433 | 422 | * |
| 434 | 423 | * Unlike dma_fence_signal(), this function must be called with &dma_fence.lock |
| 435 | 424 | * held. |
| 436 | − * | |
| 437 | − * Returns 0 on success and a negative error value when @fence has been | |
| 438 | − * signalled already. | |
| 439 | 425 | */ |
| 440 | −int dma_fence_signal_locked(struct dma_fence *fence) | |
| 426 | +void dma_fence_signal_locked(struct dma_fence *fence) | |
| 441 | 427 | { |
| 442 | − return dma_fence_signal_timestamp_locked(fence, ktime_get()); | |
| 428 | + dma_fence_signal_timestamp_locked(fence, ktime_get()); | |
| 443 | 429 | } |
| 444 | 430 | EXPORT_SYMBOL(dma_fence_signal_locked); |
| 445 | 431 | |
| 432 | +/** | |
| 433 | + * dma_fence_check_and_signal_locked - signal the fence if it's not yet signaled | |
| 434 | + * @fence: the fence to check and signal | |
| 435 | + * | |
| 436 | + * Checks whether a fence was signaled and signals it if it was not yet signaled. | |
| 437 | + * | |
| 438 | + * Unlike dma_fence_check_and_signal(), this function must be called with | |
| 439 | + * &struct dma_fence.lock being held. | |
| 440 | + * | |
| 441 | + * Return: true if fence has been signaled already, false otherwise. | |
| 442 | + */ | |
| 443 | +bool dma_fence_check_and_signal_locked(struct dma_fence *fence) | |
| 444 | +{ | |
| 445 | + bool ret; | |
| 446 | + | |
| 447 | + ret = dma_fence_test_signaled_flag(fence); | |
| 448 | + dma_fence_signal_locked(fence); | |
| 449 | + | |
| 450 | + return ret; | |
| 451 | +} | |
| 452 | +EXPORT_SYMBOL(dma_fence_check_and_signal_locked); | |
| 453 | + | |
| 454 | +/** | |
| 455 | + * dma_fence_check_and_signal - signal the fence if it's not yet signaled | |
| 456 | + * @fence: the fence to check and signal | |
| 457 | + * | |
| 458 | + * Checks whether a fence was signaled and signals it if it was not yet signaled. | |
| 459 | + * All this is done in a race-free manner. | |
| 460 | + * | |
| 461 | + * Return: true if fence has been signaled already, false otherwise. | |
| 462 | + */ | |
| 463 | +bool dma_fence_check_and_signal(struct dma_fence *fence) | |
| 464 | +{ | |
| 465 | + unsigned long flags; | |
| 466 | + bool ret; | |
| 467 | + | |
| 468 | + spin_lock_irqsave(fence->lock, flags); | |
| 469 | + ret = dma_fence_check_and_signal_locked(fence); | |
| 470 | + spin_unlock_irqrestore(fence->lock, flags); | |
| 471 | + | |
| 472 | + return ret; | |
| 473 | +} | |
| 474 | +EXPORT_SYMBOL(dma_fence_check_and_signal); | |
| 475 | + | |
| 446 | 476 | /** |
| 447 | 477 | * dma_fence_signal - signal completion of a fence |
| 448 | 478 | * @fence: the fence to signal |
| @@ -452,28 +482,22 @@ EXPORT_SYMBOL(dma_fence_signal_locked); · 3 unmodified lines | ||
| 452 | 482 | * dma_fence_add_callback(). Can be called multiple times, but since a fence |
| 453 | 483 | * can only go from the unsignaled to the signaled state and not back, it will |
| 454 | 484 | * only be effective the first time. |
| 455 | − * | |
| 456 | − * Returns 0 on success and a negative error value when @fence has been | |
| 457 | − * signalled already. | |
| 458 | 485 | */ |
| 459 | −int dma_fence_signal(struct dma_fence *fence) | |
| 486 | +void dma_fence_signal(struct dma_fence *fence) | |
| 460 | 487 | { |
| 461 | 488 | unsigned long flags; |
| 462 | − int ret; | |
| 463 | 489 | bool tmp; |
| 464 | 490 | |
| 465 | 491 | if (WARN_ON(!fence)) |
| 466 | − return -EINVAL; | |
| 492 | + return; | |
| 467 | 493 | |
| 468 | 494 | tmp = dma_fence_begin_signalling(); |
| 469 | 495 | |
| 470 | 496 | spin_lock_irqsave(fence->lock, flags); |
| 471 | − ret = dma_fence_signal_timestamp_locked(fence, ktime_get()); | |
| 497 | + dma_fence_signal_timestamp_locked(fence, ktime_get()); | |
| 472 | 498 | spin_unlock_irqrestore(fence->lock, flags); |
| 473 | 499 | |
| 474 | 500 | dma_fence_end_signalling(tmp); |
| 475 | − | |
| 476 | − return ret; | |
| 477 | 501 | } |
| 478 | 502 | EXPORT_SYMBOL(dma_fence_signal); |
| 479 | 503 | |
| @@ -543,7 +567,7 @@ void dma_fence_release(struct kref *kref) · 63 unmodified lines | ||
| 543 | 567 | trace_dma_fence_destroy(fence); |
| 544 | 568 | |
| 545 | 569 | if (!list_empty(&fence->cb_list) && |
| 546 | − !test_bit(DMA_FENCE_FLAG_SIGNALED_BIT, &fence->flags)) { | |
| 570 | + !dma_fence_test_signaled_flag(fence)) { | |
| 547 | 571 | const char __rcu *timeline; |
| 548 | 572 | const char __rcu *driver; |
| 549 | 573 | unsigned long flags; |
| @@ -600,7 +624,7 @@ static bool __dma_fence_enable_signaling(struct dma_fence *fence) · 50 unmodified lines | ||
| 600 | 624 | was_set = test_and_set_bit(DMA_FENCE_FLAG_ENABLE_SIGNAL_BIT, |
| 601 | 625 | &fence->flags); |
| 602 | 626 | |
| 603 | − if (test_bit(DMA_FENCE_FLAG_SIGNALED_BIT, &fence->flags)) | |
| 627 | + if (dma_fence_test_signaled_flag(fence)) | |
| 604 | 628 | return false; |
| 605 | 629 | |
| 606 | 630 | if (!was_set && fence->ops->enable_signaling) { |
| @@ -664,7 +688,7 @@ int dma_fence_add_callback(struct dma_fence *fence, struct dma_fence_cb *cb, · 57 unmodified lines | ||
| 664 | 688 | if (WARN_ON(!fence || !func)) |
| 665 | 689 | return -EINVAL; |
| 666 | 690 | |
| 667 | − if (test_bit(DMA_FENCE_FLAG_SIGNALED_BIT, &fence->flags)) { | |
| 691 | + if (dma_fence_test_signaled_flag(fence)) { | |
| 668 | 692 | INIT_LIST_HEAD(&cb->node); |
| 669 | 693 | return -ENOENT; |
| 670 | 694 | } |
| @@ -781,7 +805,7 @@ dma_fence_default_wait(struct dma_fence *fence, bool intr, signed long timeout) · 110 unmodified lines | ||
| 781 | 805 | |
| 782 | 806 | spin_lock_irqsave(fence->lock, flags); |
| 783 | 807 | |
| 784 | − if (test_bit(DMA_FENCE_FLAG_SIGNALED_BIT, &fence->flags)) | |
| 808 | + if (dma_fence_test_signaled_flag(fence)) | |
| 785 | 809 | goto out; |
| 786 | 810 | |
| 787 | 811 | if (intr && signal_pending(current)) { |
| @@ -798,7 +822,7 @@ dma_fence_default_wait(struct dma_fence *fence, bool intr, signed long timeout) · 10 unmodified lines | ||
| 798 | 822 | cb.task = current; |
| 799 | 823 | list_add(&cb.base.node, &fence->cb_list); |
| 800 | 824 | |
| 801 | − while (!test_bit(DMA_FENCE_FLAG_SIGNALED_BIT, &fence->flags) && ret > 0) { | |
| 825 | + while (!dma_fence_test_signaled_flag(fence) && ret > 0) { | |
| 802 | 826 | if (intr) |
| 803 | 827 | __set_current_state(TASK_INTERRUPTIBLE); |
| 804 | 828 | else |
| @@ -830,7 +854,7 @@ dma_fence_test_signaled_any(struct dma_fence **fences, uint32_t count, · 25 unmodified lines | ||
| 830 | 854 | |
| 831 | 855 | for (i = 0; i < count; ++i) { |
| 832 | 856 | struct dma_fence *fence = fences[i]; |
| 833 | − if (test_bit(DMA_FENCE_FLAG_SIGNALED_BIT, &fence->flags)) { | |
| 857 | + if (dma_fence_test_signaled_flag(fence)) { | |
| 834 | 858 | if (idx) |
| 835 | 859 | *idx = i; |
| 836 | 860 | return true; |
| @@ -1108,7 +1132,7 @@ const char __rcu *dma_fence_driver_name(struct dma_fence *fence) · 271 unmodified lines | ||
| 1108 | 1132 | RCU_LOCKDEP_WARN(!rcu_read_lock_held(), |
| 1109 | 1133 | "RCU protection is required for safe access to returned string"); |
| 1110 | 1134 | |
| 1111 | − if (!test_bit(DMA_FENCE_FLAG_SIGNALED_BIT, &fence->flags)) | |
| 1135 | + if (!dma_fence_test_signaled_flag(fence)) | |
| 1112 | 1136 | return fence->ops->get_driver_name(fence); |
| 1113 | 1137 | else |
| 1114 | 1138 | return "detached-driver"; |
| @@ -1140,7 +1164,7 @@ const char __rcu *dma_fence_timeline_name(struct dma_fence *fence) · 25 unmodified lines | ||
| 1140 | 1164 | RCU_LOCKDEP_WARN(!rcu_read_lock_held(), |
| 1141 | 1165 | "RCU protection is required for safe access to returned string"); |
| 1142 | 1166 | |
| 1143 | − if (!test_bit(DMA_FENCE_FLAG_SIGNALED_BIT, &fence->flags)) | |
| 1167 | + if (!dma_fence_test_signaled_flag(fence)) | |
| 1144 | 1168 | return fence->ops->get_timeline_name(fence); |
| 1145 | 1169 | else |
| 1146 | 1170 | return "signaled-timeline"; |
| @@ -126,7 +126,7 @@ static int test_signaling(void *arg) · 125 unmodified lines | ||
| 126 | 126 | goto err_free; |
| 127 | 127 | } |
| 128 | 128 | |
| 129 | − if (dma_fence_signal(f)) { | |
| 129 | + if (dma_fence_check_and_signal(f)) { | |
| 130 | 130 | pr_err("Fence reported being already signaled\n"); |
| 131 | 131 | goto err_free; |
| 132 | 132 | } |
| @@ -136,7 +136,7 @@ static int test_signaling(void *arg) · 3 unmodified lines | ||
| 136 | 136 | goto err_free; |
| 137 | 137 | } |
| 138 | 138 | |
| 139 | − if (!dma_fence_signal(f)) { | |
| 139 | + if (!dma_fence_test_signaled_flag(f)) { | |
| 140 | 140 | pr_err("Fence reported not being already signaled\n"); |
| 141 | 141 | goto err_free; |
| 142 | 142 | } |
| @@ -265,164 +265,83 @@ config DRM_SCHED · 264 unmodified lines | ||
| 265 | 265 | tristate |
| 266 | 266 | depends on DRM |
| 267 | 267 | |
| 268 | −source "drivers/gpu/drm/sysfb/Kconfig" | |
| 268 | +# Separate option as not all DRM drivers use it | |
| 269 | +config DRM_PANEL_BACKLIGHT_QUIRKS | |
| 270 | + tristate | |
| 269 | 271 | |
| 270 | −source "drivers/gpu/drm/arm/Kconfig" | |
| 272 | +config DRM_LIB_RANDOM | |
| 273 | + bool | |
| 274 | + default n | |
| 271 | 275 | |
| 272 | −source "drivers/gpu/drm/radeon/Kconfig" | |
| 276 | +config DRM_PRIVACY_SCREEN | |
| 277 | + bool | |
| 278 | + default n | |
| 273 | 279 | |
| 280 | +# DRM driver Kconfig files, sorted | |
| 281 | +source "drivers/gpu/drm/adp/Kconfig" | |
| 274 | 282 | source "drivers/gpu/drm/amd/amdgpu/Kconfig" |
| 275 | − | |
| 276 | −source "drivers/gpu/drm/nouveau/Kconfig" | |
| 277 | − | |
| 278 | −source "drivers/gpu/drm/nova/Kconfig" | |
| 279 | − | |
| 280 | −source "drivers/gpu/drm/i915/Kconfig" | |
| 281 | − | |
| 282 | −source "drivers/gpu/drm/xe/Kconfig" | |
| 283 | − | |
| 284 | −source "drivers/gpu/drm/kmb/Kconfig" | |
| 285 | − | |
| 286 | −config DRM_VGEM | |
| 287 | − tristate "Virtual GEM provider" | |
| 288 | − depends on DRM && MMU | |
| 289 | − select DRM_GEM_SHMEM_HELPER | |
| 290 | − help | |
| 291 | − Choose this option to get a virtual graphics memory manager, | |
| 292 | − as used by Mesa's software renderer for enhanced performance. | |
| 293 | − If M is selected the module will be called vgem. | |
| 294 | − | |
| 295 | −source "drivers/gpu/drm/vkms/Kconfig" | |
| 296 | − | |
| 297 | −source "drivers/gpu/drm/exynos/Kconfig" | |
| 298 | − | |
| 299 | −source "drivers/gpu/drm/rockchip/Kconfig" | |
| 300 | − | |
| 301 | −source "drivers/gpu/drm/vmwgfx/Kconfig" | |
| 302 | − | |
| 303 | −source "drivers/gpu/drm/gma500/Kconfig" | |
| 304 | − | |
| 305 | −source "drivers/gpu/drm/udl/Kconfig" | |
| 306 | − | |
| 307 | −source "drivers/gpu/drm/ast/Kconfig" | |
| 308 | − | |
| 309 | −source "drivers/gpu/drm/mgag200/Kconfig" | |
| 310 | − | |
| 283 | +source "drivers/gpu/drm/arm/Kconfig" | |
| 311 | 284 | source "drivers/gpu/drm/armada/Kconfig" |
| 312 | − | |
| 285 | +source "drivers/gpu/drm/aspeed/Kconfig" | |
| 286 | +source "drivers/gpu/drm/ast/Kconfig" | |
| 313 | 287 | source "drivers/gpu/drm/atmel-hlcdc/Kconfig" |
| 314 | − | |
| 315 | −source "drivers/gpu/drm/renesas/Kconfig" | |
| 316 | − | |
| 317 | −source "drivers/gpu/drm/sun4i/Kconfig" | |
| 318 | − | |
| 319 | −source "drivers/gpu/drm/omapdrm/Kconfig" | |
| 320 | − | |
| 321 | −source "drivers/gpu/drm/tilcdc/Kconfig" | |
| 322 | − | |
| 323 | −source "drivers/gpu/drm/qxl/Kconfig" | |
| 324 | − | |
| 325 | −source "drivers/gpu/drm/virtio/Kconfig" | |
| 326 | − | |
| 327 | −source "drivers/gpu/drm/msm/Kconfig" | |
| 328 | − | |
| 329 | −source "drivers/gpu/drm/fsl-dcu/Kconfig" | |
| 330 | − | |
| 331 | −source "drivers/gpu/drm/tegra/Kconfig" | |
| 332 | − | |
| 333 | −source "drivers/gpu/drm/stm/Kconfig" | |
| 334 | − | |
| 335 | −source "drivers/gpu/drm/panel/Kconfig" | |
| 336 | − | |
| 337 | 288 | source "drivers/gpu/drm/bridge/Kconfig" |
| 338 | − | |
| 339 | −source "drivers/gpu/drm/sti/Kconfig" | |
| 340 | − | |
| 341 | −source "drivers/gpu/drm/imx/Kconfig" | |
| 342 | − | |
| 343 | −source "drivers/gpu/drm/ingenic/Kconfig" | |
| 344 | − | |
| 345 | −source "drivers/gpu/drm/v3d/Kconfig" | |
| 346 | − | |
| 347 | −source "drivers/gpu/drm/vc4/Kconfig" | |
| 348 | − | |
| 349 | −source "drivers/gpu/drm/loongson/Kconfig" | |
| 350 | − | |
| 351 | 289 | source "drivers/gpu/drm/etnaviv/Kconfig" |
| 352 | − | |
| 290 | +source "drivers/gpu/drm/exynos/Kconfig" | |
| 291 | +source "drivers/gpu/drm/fsl-dcu/Kconfig" | |
| 292 | +source "drivers/gpu/drm/gma500/Kconfig" | |
| 293 | +source "drivers/gpu/drm/gud/Kconfig" | |
| 353 | 294 | source "drivers/gpu/drm/hisilicon/Kconfig" |
| 354 | − | |
| 295 | +source "drivers/gpu/drm/hyperv/Kconfig" | |
| 296 | +source "drivers/gpu/drm/i915/Kconfig" | |
| 297 | +source "drivers/gpu/drm/imagination/Kconfig" | |
| 298 | +source "drivers/gpu/drm/imx/Kconfig" | |
| 299 | +source "drivers/gpu/drm/ingenic/Kconfig" | |
| 300 | +source "drivers/gpu/drm/kmb/Kconfig" | |
| 301 | +source "drivers/gpu/drm/lima/Kconfig" | |
| 355 | 302 | source "drivers/gpu/drm/logicvc/Kconfig" |
| 356 | − | |
| 303 | +source "drivers/gpu/drm/loongson/Kconfig" | |
| 304 | +source "drivers/gpu/drm/mcde/Kconfig" | |
| 357 | 305 | source "drivers/gpu/drm/mediatek/Kconfig" |
| 358 | − | |
| 359 | −source "drivers/gpu/drm/mxsfb/Kconfig" | |
| 360 | − | |
| 361 | 306 | source "drivers/gpu/drm/meson/Kconfig" |
| 362 | − | |
| 363 | −source "drivers/gpu/drm/tiny/Kconfig" | |
| 364 | − | |
| 365 | −source "drivers/gpu/drm/pl111/Kconfig" | |
| 366 | − | |
| 367 | −source "drivers/gpu/drm/tve200/Kconfig" | |
| 368 | − | |
| 369 | −source "drivers/gpu/drm/xen/Kconfig" | |
| 370 | − | |
| 371 | −source "drivers/gpu/drm/vboxvideo/Kconfig" | |
| 372 | − | |
| 373 | −source "drivers/gpu/drm/lima/Kconfig" | |
| 374 | − | |
| 307 | +source "drivers/gpu/drm/mgag200/Kconfig" | |
| 308 | +source "drivers/gpu/drm/msm/Kconfig" | |
| 309 | +source "drivers/gpu/drm/mxsfb/Kconfig" | |
| 310 | +source "drivers/gpu/drm/nouveau/Kconfig" | |
| 311 | +source "drivers/gpu/drm/nova/Kconfig" | |
| 312 | +source "drivers/gpu/drm/omapdrm/Kconfig" | |
| 313 | +source "drivers/gpu/drm/panel/Kconfig" | |
| 375 | 314 | source "drivers/gpu/drm/panfrost/Kconfig" |
| 376 | − | |
| 377 | 315 | source "drivers/gpu/drm/panthor/Kconfig" |
| 378 | − | |
| 379 | −source "drivers/gpu/drm/aspeed/Kconfig" | |
| 380 | − | |
| 381 | −source "drivers/gpu/drm/mcde/Kconfig" | |
| 382 | − | |
| 383 | −source "drivers/gpu/drm/tidss/Kconfig" | |
| 384 | − | |
| 385 | −source "drivers/gpu/drm/adp/Kconfig" | |
| 386 | − | |
| 387 | −source "drivers/gpu/drm/xlnx/Kconfig" | |
| 388 | − | |
| 389 | −source "drivers/gpu/drm/gud/Kconfig" | |
| 390 | − | |
| 316 | +source "drivers/gpu/drm/pl111/Kconfig" | |
| 317 | +source "drivers/gpu/drm/qxl/Kconfig" | |
| 318 | +source "drivers/gpu/drm/radeon/Kconfig" | |
| 319 | +source "drivers/gpu/drm/renesas/Kconfig" | |
| 320 | +source "drivers/gpu/drm/rockchip/Kconfig" | |
| 391 | 321 | source "drivers/gpu/drm/sitronix/Kconfig" |
| 392 | − | |
| 393 | 322 | source "drivers/gpu/drm/solomon/Kconfig" |
| 394 | − | |
| 395 | 323 | source "drivers/gpu/drm/sprd/Kconfig" |
| 396 | − | |
| 397 | −source "drivers/gpu/drm/imagination/Kconfig" | |
| 398 | − | |
| 324 | +source "drivers/gpu/drm/sti/Kconfig" | |
| 325 | +source "drivers/gpu/drm/stm/Kconfig" | |
| 326 | +source "drivers/gpu/drm/sun4i/Kconfig" | |
| 327 | +source "drivers/gpu/drm/sysfb/Kconfig" | |
| 328 | +source "drivers/gpu/drm/tegra/Kconfig" | |
| 329 | +source "drivers/gpu/drm/tidss/Kconfig" | |
| 330 | +source "drivers/gpu/drm/tilcdc/Kconfig" | |
| 331 | +source "drivers/gpu/drm/tiny/Kconfig" | |
| 332 | +source "drivers/gpu/drm/tve200/Kconfig" | |
| 399 | 333 | source "drivers/gpu/drm/tyr/Kconfig" |
| 400 | − | |
| 401 | −config DRM_HYPERV | |
| 402 | − tristate "DRM Support for Hyper-V synthetic video device" | |
| 403 | − depends on DRM && PCI && HYPERV_VMBUS | |
| 404 | − select DRM_CLIENT_SELECTION | |
| 405 | − select DRM_KMS_HELPER | |
| 406 | − select DRM_GEM_SHMEM_HELPER | |
| 407 | − help | |
| 408 | − This is a KMS driver for Hyper-V synthetic video device. Choose this | |
| 409 | − option if you would like to enable drm driver for Hyper-V virtual | |
| 410 | − machine. Unselect Hyper-V framebuffer driver (CONFIG_FB_HYPERV) so | |
| 411 | − that DRM driver is used by default. | |
| 412 | − | |
| 413 | − If M is selected the module will be called hyperv_drm. | |
| 414 | − | |
| 415 | −# Separate option as not all DRM drivers use it | |
| 416 | −config DRM_PANEL_BACKLIGHT_QUIRKS | |
| 417 | − tristate | |
| 418 | − | |
| 419 | −config DRM_LIB_RANDOM | |
| 420 | − bool | |
| 421 | − default n | |
| 422 | − | |
| 423 | −config DRM_PRIVACY_SCREEN | |
| 424 | − bool | |
| 425 | − default n | |
| 334 | +source "drivers/gpu/drm/udl/Kconfig" | |
| 335 | +source "drivers/gpu/drm/v3d/Kconfig" | |
| 336 | +source "drivers/gpu/drm/vboxvideo/Kconfig" | |
| 337 | +source "drivers/gpu/drm/vc4/Kconfig" | |
| 338 | +source "drivers/gpu/drm/vgem/Kconfig" | |
| 339 | +source "drivers/gpu/drm/virtio/Kconfig" | |
| 340 | +source "drivers/gpu/drm/vkms/Kconfig" | |
| 341 | +source "drivers/gpu/drm/vmwgfx/Kconfig" | |
| 342 | +source "drivers/gpu/drm/xe/Kconfig" | |
| 343 | +source "drivers/gpu/drm/xen/Kconfig" | |
| 344 | +source "drivers/gpu/drm/xlnx/Kconfig" | |
| 426 | 345 | |
| 427 | 346 | endif |
| 428 | 347 | |
| @@ -1838,7 +1838,7 @@ static void dce_v10_0_grph_enable(struct drm_crtc *crtc, bool enable) · 1837 unmodified lines | ||
| 1838 | 1838 | |
| 1839 | 1839 | static int dce_v10_0_crtc_do_set_base(struct drm_crtc *crtc, |
| 1840 | 1840 | struct drm_framebuffer *fb, |
| 1841 | − int x, int y, int atomic) | |
| 1841 | + int x, int y) | |
| 1842 | 1842 | { |
| 1843 | 1843 | struct amdgpu_crtc *amdgpu_crtc = to_amdgpu_crtc(crtc); |
| 1844 | 1844 | struct drm_device *dev = crtc->dev; |
| @@ -1855,15 +1855,12 @@ static int dce_v10_0_crtc_do_set_base(struct drm_crtc *crtc, · 10 unmodified lines | ||
| 1855 | 1855 | bool bypass_lut = false; |
| 1856 | 1856 | |
| 1857 | 1857 | /* no fb bound */ |
| 1858 | − if (!atomic && !crtc->primary->fb) { | |
| 1858 | + if (!crtc->primary->fb) { | |
| 1859 | 1859 | DRM_DEBUG_KMS("No FB bound\n"); |
| 1860 | 1860 | return 0; |
| 1861 | 1861 | } |
| 1862 | 1862 | |
| 1863 | − if (atomic) | |
| 1864 | − target_fb = fb; | |
| 1865 | − else | |
| 1866 | − target_fb = crtc->primary->fb; | |
| 1863 | + target_fb = crtc->primary->fb; | |
| 1867 | 1864 | |
| 1868 | 1865 | /* If atomic, assume fb object is pinned & idle & fenced and |
| 1869 | 1866 | * just update base pointers |
| @@ -1874,13 +1871,11 @@ static int dce_v10_0_crtc_do_set_base(struct drm_crtc *crtc, · 4 unmodified lines | ||
| 1874 | 1871 | if (unlikely(r != 0)) |
| 1875 | 1872 | return r; |
| 1876 | 1873 | |
| 1877 | − if (!atomic) { | |
| 1878 | − abo->flags |= AMDGPU_GEM_CREATE_VRAM_CONTIGUOUS; | |
| 1879 | − r = amdgpu_bo_pin(abo, AMDGPU_GEM_DOMAIN_VRAM); | |
| 1880 | − if (unlikely(r != 0)) { | |
| 1881 | − amdgpu_bo_unreserve(abo); | |
| 1882 | − return -EINVAL; | |
| 1883 | − } | |
| 1874 | + abo->flags |= AMDGPU_GEM_CREATE_VRAM_CONTIGUOUS; | |
| 1875 | + r = amdgpu_bo_pin(abo, AMDGPU_GEM_DOMAIN_VRAM); | |
| 1876 | + if (unlikely(r != 0)) { | |
| 1877 | + amdgpu_bo_unreserve(abo); | |
| 1878 | + return -EINVAL; | |
| 1884 | 1879 | } |
| 1885 | 1880 | fb_location = amdgpu_bo_gpu_offset(abo); |
| 1886 | 1881 | |
| @@ -2068,7 +2063,7 @@ static int dce_v10_0_crtc_do_set_base(struct drm_crtc *crtc, · 181 unmodified lines | ||
| 2068 | 2063 | /* set pageflip to happen anywhere in vblank interval */ |
| 2069 | 2064 | WREG32(mmMASTER_UPDATE_MODE + amdgpu_crtc->crtc_offset, 0); |
| 2070 | 2065 | |
| 2071 | − if (!atomic && fb && fb != crtc->primary->fb) { | |
| 2066 | + if (fb && fb != crtc->primary->fb) { | |
| 2072 | 2067 | abo = gem_to_amdgpu_bo(fb->obj[0]); |
| 2073 | 2068 | r = amdgpu_bo_reserve(abo, true); |
| 2074 | 2069 | if (unlikely(r != 0)) |
| @@ -2611,7 +2606,7 @@ static int dce_v10_0_crtc_mode_set(struct drm_crtc *crtc, · 536 unmodified lines | ||
| 2611 | 2606 | |
| 2612 | 2607 | amdgpu_atombios_crtc_set_pll(crtc, adjusted_mode); |
| 2613 | 2608 | amdgpu_atombios_crtc_set_dtd_timing(crtc, adjusted_mode); |
| 2614 | − dce_v10_0_crtc_do_set_base(crtc, old_fb, x, y, 0); | |
| 2609 | + dce_v10_0_crtc_do_set_base(crtc, old_fb, x, y); | |
| 2615 | 2610 | amdgpu_atombios_crtc_overscan_setup(crtc, mode, adjusted_mode); |
| 2616 | 2611 | amdgpu_atombios_crtc_scaler_setup(crtc); |
| 2617 | 2612 | dce_v10_0_cursor_reset(crtc); |
| @@ -2659,14 +2654,7 @@ static bool dce_v10_0_crtc_mode_fixup(struct drm_crtc *crtc, · 41 unmodified lines | ||
| 2659 | 2654 | static int dce_v10_0_crtc_set_base(struct drm_crtc *crtc, int x, int y, |
| 2660 | 2655 | struct drm_framebuffer *old_fb) |
| 2661 | 2656 | { |
| 2662 | − return dce_v10_0_crtc_do_set_base(crtc, old_fb, x, y, 0); | |
| 2663 | −} | |
| 2664 | − | |
| 2665 | −static int dce_v10_0_crtc_set_base_atomic(struct drm_crtc *crtc, | |
| 2666 | − struct drm_framebuffer *fb, | |
| 2667 | − int x, int y, enum mode_set_atomic state) | |
| 2668 | −{ | |
| 2669 | − return dce_v10_0_crtc_do_set_base(crtc, fb, x, y, 1); | |
| 2657 | + return dce_v10_0_crtc_do_set_base(crtc, old_fb, x, y); | |
| 2670 | 2658 | } |
| 2671 | 2659 | |
| 2672 | 2660 | static const struct drm_crtc_helper_funcs dce_v10_0_crtc_helper_funcs = { |
| @@ -2674,7 +2662,6 @@ static const struct drm_crtc_helper_funcs dce_v10_0_crtc_helper_funcs = { · 1 unmodified line | ||
| 2674 | 2662 | .mode_fixup = dce_v10_0_crtc_mode_fixup, |
| 2675 | 2663 | .mode_set = dce_v10_0_crtc_mode_set, |
| 2676 | 2664 | .mode_set_base = dce_v10_0_crtc_set_base, |
| 2677 | − .mode_set_base_atomic = dce_v10_0_crtc_set_base_atomic, | |
| 2678 | 2665 | .prepare = dce_v10_0_crtc_prepare, |
| 2679 | 2666 | .commit = dce_v10_0_crtc_commit, |
| 2680 | 2667 | .disable = dce_v10_0_crtc_disable, |
| @@ -1876,7 +1876,7 @@ static void dce_v6_0_grph_enable(struct drm_crtc *crtc, bool enable) · 1875 unmodified lines | ||
| 1876 | 1876 | |
| 1877 | 1877 | static int dce_v6_0_crtc_do_set_base(struct drm_crtc *crtc, |
| 1878 | 1878 | struct drm_framebuffer *fb, |
| 1879 | − int x, int y, int atomic) | |
| 1879 | + int x, int y) | |
| 1880 | 1880 | { |
| 1881 | 1881 | struct amdgpu_crtc *amdgpu_crtc = to_amdgpu_crtc(crtc); |
| 1882 | 1882 | struct drm_device *dev = crtc->dev; |
| @@ -1892,15 +1892,12 @@ static int dce_v6_0_crtc_do_set_base(struct drm_crtc *crtc, · 9 unmodified lines | ||
| 1892 | 1892 | bool bypass_lut = false; |
| 1893 | 1893 | |
| 1894 | 1894 | /* no fb bound */ |
| 1895 | − if (!atomic && !crtc->primary->fb) { | |
| 1895 | + if (!crtc->primary->fb) { | |
| 1896 | 1896 | DRM_DEBUG_KMS("No FB bound\n"); |
| 1897 | 1897 | return 0; |
| 1898 | 1898 | } |
| 1899 | 1899 | |
| 1900 | − if (atomic) | |
| 1901 | − target_fb = fb; | |
| 1902 | − else | |
| 1903 | − target_fb = crtc->primary->fb; | |
| 1900 | + target_fb = crtc->primary->fb; | |
| 1904 | 1901 | |
| 1905 | 1902 | /* If atomic, assume fb object is pinned & idle & fenced and |
| 1906 | 1903 | * just update base pointers |
| @@ -1911,13 +1908,11 @@ static int dce_v6_0_crtc_do_set_base(struct drm_crtc *crtc, · 4 unmodified lines | ||
| 1911 | 1908 | if (unlikely(r != 0)) |
| 1912 | 1909 | return r; |
| 1913 | 1910 | |
| 1914 | − if (!atomic) { | |
| 1915 | − abo->flags |= AMDGPU_GEM_CREATE_VRAM_CONTIGUOUS; | |
| 1916 | − r = amdgpu_bo_pin(abo, AMDGPU_GEM_DOMAIN_VRAM); | |
| 1917 | − if (unlikely(r != 0)) { | |
| 1918 | − amdgpu_bo_unreserve(abo); | |
| 1919 | − return -EINVAL; | |
| 1920 | − } | |
| 1911 | + abo->flags |= AMDGPU_GEM_CREATE_VRAM_CONTIGUOUS; | |
| 1912 | + r = amdgpu_bo_pin(abo, AMDGPU_GEM_DOMAIN_VRAM); | |
| 1913 | + if (unlikely(r != 0)) { | |
| 1914 | + amdgpu_bo_unreserve(abo); | |
| 1915 | + return -EINVAL; | |
| 1921 | 1916 | } |
| 1922 | 1917 | fb_location = amdgpu_bo_gpu_offset(abo); |
| 1923 | 1918 | |
| @@ -2083,7 +2078,7 @@ static int dce_v6_0_crtc_do_set_base(struct drm_crtc *crtc, · 159 unmodified lines | ||
| 2083 | 2078 | /* set pageflip to happen anywhere in vblank interval */ |
| 2084 | 2079 | WREG32(mmMASTER_UPDATE_MODE + amdgpu_crtc->crtc_offset, 0); |
| 2085 | 2080 | |
| 2086 | − if (!atomic && fb && fb != crtc->primary->fb) { | |
| 2081 | + if (fb && fb != crtc->primary->fb) { | |
| 2087 | 2082 | abo = gem_to_amdgpu_bo(fb->obj[0]); |
| 2088 | 2083 | r = amdgpu_bo_reserve(abo, true); |
| 2089 | 2084 | if (unlikely(r != 0)) |
| @@ -2578,7 +2573,7 @@ static int dce_v6_0_crtc_mode_set(struct drm_crtc *crtc, · 488 unmodified lines | ||
| 2578 | 2573 | |
| 2579 | 2574 | amdgpu_atombios_crtc_set_pll(crtc, adjusted_mode); |
| 2580 | 2575 | amdgpu_atombios_crtc_set_dtd_timing(crtc, adjusted_mode); |
| 2581 | − dce_v6_0_crtc_do_set_base(crtc, old_fb, x, y, 0); | |
| 2576 | + dce_v6_0_crtc_do_set_base(crtc, old_fb, x, y); | |
| 2582 | 2577 | amdgpu_atombios_crtc_overscan_setup(crtc, mode, adjusted_mode); |
| 2583 | 2578 | amdgpu_atombios_crtc_scaler_setup(crtc); |
| 2584 | 2579 | dce_v6_0_cursor_reset(crtc); |
| @@ -2626,14 +2621,7 @@ static bool dce_v6_0_crtc_mode_fixup(struct drm_crtc *crtc, · 41 unmodified lines | ||
| 2626 | 2621 | static int dce_v6_0_crtc_set_base(struct drm_crtc *crtc, int x, int y, |
| 2627 | 2622 | struct drm_framebuffer *old_fb) |
| 2628 | 2623 | { |
| 2629 | − return dce_v6_0_crtc_do_set_base(crtc, old_fb, x, y, 0); | |
| 2630 | −} | |
| 2631 | − | |
| 2632 | −static int dce_v6_0_crtc_set_base_atomic(struct drm_crtc *crtc, | |
| 2633 | − struct drm_framebuffer *fb, | |
| 2634 | − int x, int y, enum mode_set_atomic state) | |
| 2635 | −{ | |
| 2636 | − return dce_v6_0_crtc_do_set_base(crtc, fb, x, y, 1); | |
| 2624 | + return dce_v6_0_crtc_do_set_base(crtc, old_fb, x, y); | |
| 2637 | 2625 | } |
| 2638 | 2626 | |
| 2639 | 2627 | static const struct drm_crtc_helper_funcs dce_v6_0_crtc_helper_funcs = { |
| @@ -2641,7 +2629,6 @@ static const struct drm_crtc_helper_funcs dce_v6_0_crtc_helper_funcs = { · 1 unmodified line | ||
| 2641 | 2629 | .mode_fixup = dce_v6_0_crtc_mode_fixup, |
| 2642 | 2630 | .mode_set = dce_v6_0_crtc_mode_set, |
| 2643 | 2631 | .mode_set_base = dce_v6_0_crtc_set_base, |
| 2644 | − .mode_set_base_atomic = dce_v6_0_crtc_set_base_atomic, | |
| 2645 | 2632 | .prepare = dce_v6_0_crtc_prepare, |
| 2646 | 2633 | .commit = dce_v6_0_crtc_commit, |
| 2647 | 2634 | .disable = dce_v6_0_crtc_disable, |
| @@ -1785,7 +1785,7 @@ static void dce_v8_0_grph_enable(struct drm_crtc *crtc, bool enable) · 1784 unmodified lines | ||
| 1785 | 1785 | |
| 1786 | 1786 | static int dce_v8_0_crtc_do_set_base(struct drm_crtc *crtc, |
| 1787 | 1787 | struct drm_framebuffer *fb, |
| 1788 | − int x, int y, int atomic) | |
| 1788 | + int x, int y) | |
| 1789 | 1789 | { |
| 1790 | 1790 | struct amdgpu_crtc *amdgpu_crtc = to_amdgpu_crtc(crtc); |
| 1791 | 1791 | struct drm_device *dev = crtc->dev; |
| @@ -1802,15 +1802,12 @@ static int dce_v8_0_crtc_do_set_base(struct drm_crtc *crtc, · 10 unmodified lines | ||
| 1802 | 1802 | bool bypass_lut = false; |
| 1803 | 1803 | |
| 1804 | 1804 | /* no fb bound */ |
| 1805 | − if (!atomic && !crtc->primary->fb) { | |
| 1805 | + if (!crtc->primary->fb) { | |
| 1806 | 1806 | DRM_DEBUG_KMS("No FB bound\n"); |
| 1807 | 1807 | return 0; |
| 1808 | 1808 | } |
| 1809 | 1809 | |
| 1810 | − if (atomic) | |
| 1811 | − target_fb = fb; | |
| 1812 | − else | |
| 1813 | − target_fb = crtc->primary->fb; | |
| 1810 | + target_fb = crtc->primary->fb; | |
| 1814 | 1811 | |
| 1815 | 1812 | /* If atomic, assume fb object is pinned & idle & fenced and |
| 1816 | 1813 | * just update base pointers |
| @@ -1821,13 +1818,11 @@ static int dce_v8_0_crtc_do_set_base(struct drm_crtc *crtc, · 4 unmodified lines | ||
| 1821 | 1818 | if (unlikely(r != 0)) |
| 1822 | 1819 | return r; |
| 1823 | 1820 | |
| 1824 | − if (!atomic) { | |
| 1825 | − abo->flags |= AMDGPU_GEM_CREATE_VRAM_CONTIGUOUS; | |
| 1826 | − r = amdgpu_bo_pin(abo, AMDGPU_GEM_DOMAIN_VRAM); | |
| 1827 | − if (unlikely(r != 0)) { | |
| 1828 | − amdgpu_bo_unreserve(abo); | |
| 1829 | − return -EINVAL; | |
| 1830 | − } | |
| 1821 | + abo->flags |= AMDGPU_GEM_CREATE_VRAM_CONTIGUOUS; | |
| 1822 | + r = amdgpu_bo_pin(abo, AMDGPU_GEM_DOMAIN_VRAM); | |
| 1823 | + if (unlikely(r != 0)) { | |
| 1824 | + amdgpu_bo_unreserve(abo); | |
| 1825 | + return -EINVAL; | |
| 1831 | 1826 | } |
| 1832 | 1827 | fb_location = amdgpu_bo_gpu_offset(abo); |
| 1833 | 1828 | |
| @@ -1995,7 +1990,7 @@ static int dce_v8_0_crtc_do_set_base(struct drm_crtc *crtc, · 161 unmodified lines | ||
| 1995 | 1990 | /* set pageflip to happen anywhere in vblank interval */ |
| 1996 | 1991 | WREG32(mmMASTER_UPDATE_MODE + amdgpu_crtc->crtc_offset, 0); |
| 1997 | 1992 | |
| 1998 | − if (!atomic && fb && fb != crtc->primary->fb) { | |
| 1993 | + if (fb && fb != crtc->primary->fb) { | |
| 1999 | 1994 | abo = gem_to_amdgpu_bo(fb->obj[0]); |
| 2000 | 1995 | r = amdgpu_bo_reserve(abo, true); |
| 2001 | 1996 | if (unlikely(r != 0)) |
| @@ -2537,7 +2532,7 @@ static int dce_v8_0_crtc_mode_set(struct drm_crtc *crtc, · 535 unmodified lines | ||
| 2537 | 2532 | |
| 2538 | 2533 | amdgpu_atombios_crtc_set_pll(crtc, adjusted_mode); |
| 2539 | 2534 | amdgpu_atombios_crtc_set_dtd_timing(crtc, adjusted_mode); |
| 2540 | − dce_v8_0_crtc_do_set_base(crtc, old_fb, x, y, 0); | |
| 2535 | + dce_v8_0_crtc_do_set_base(crtc, old_fb, x, y); | |
| 2541 | 2536 | amdgpu_atombios_crtc_overscan_setup(crtc, mode, adjusted_mode); |
| 2542 | 2537 | amdgpu_atombios_crtc_scaler_setup(crtc); |
| 2543 | 2538 | dce_v8_0_cursor_reset(crtc); |
| @@ -2585,14 +2580,7 @@ static bool dce_v8_0_crtc_mode_fixup(struct drm_crtc *crtc, · 41 unmodified lines | ||
| 2585 | 2580 | static int dce_v8_0_crtc_set_base(struct drm_crtc *crtc, int x, int y, |
| 2586 | 2581 | struct drm_framebuffer *old_fb) |
| 2587 | 2582 | { |
| 2588 | − return dce_v8_0_crtc_do_set_base(crtc, old_fb, x, y, 0); | |
| 2589 | −} | |
| 2590 | − | |
| 2591 | −static int dce_v8_0_crtc_set_base_atomic(struct drm_crtc *crtc, | |
| 2592 | − struct drm_framebuffer *fb, | |
| 2593 | − int x, int y, enum mode_set_atomic state) | |
| 2594 | −{ | |
| 2595 | − return dce_v8_0_crtc_do_set_base(crtc, fb, x, y, 1); | |
| 2583 | + return dce_v8_0_crtc_do_set_base(crtc, old_fb, x, y); | |
| 2596 | 2584 | } |
| 2597 | 2585 | |
| 2598 | 2586 | static const struct drm_crtc_helper_funcs dce_v8_0_crtc_helper_funcs = { |
| @@ -2600,7 +2588,6 @@ static const struct drm_crtc_helper_funcs dce_v8_0_crtc_helper_funcs = { · 1 unmodified line | ||
| 2600 | 2588 | .mode_fixup = dce_v8_0_crtc_mode_fixup, |
| 2601 | 2589 | .mode_set = dce_v8_0_crtc_mode_set, |
| 2602 | 2590 | .mode_set_base = dce_v8_0_crtc_set_base, |
| 2603 | − .mode_set_base_atomic = dce_v8_0_crtc_set_base_atomic, | |
| 2604 | 2591 | .prepare = dce_v8_0_crtc_prepare, |
| 2605 | 2592 | .commit = dce_v8_0_crtc_commit, |
| 2606 | 2593 | .disable = dce_v8_0_crtc_disable, |
| @@ -1987,10 +1987,10 @@ kfd_process_gpuid_from_node(struct kfd_process *p, struct kfd_node *node, · 1986 unmodified lines | ||
| 1987 | 1987 | return -EINVAL; |
| 1988 | 1988 | } |
| 1989 | 1989 | |
| 1990 | −static int signal_eviction_fence(struct kfd_process *p) | |
| 1990 | +static bool signal_eviction_fence(struct kfd_process *p) | |
| 1991 | 1991 | { |
| 1992 | 1992 | struct dma_fence *ef; |
| 1993 | − int ret; | |
| 1993 | + bool ret; | |
| 1994 | 1994 | |
| 1995 | 1995 | rcu_read_lock(); |
| 1996 | 1996 | ef = dma_fence_get_rcu_safe(&p->ef); |
| @@ -1998,7 +1998,7 @@ static int signal_eviction_fence(struct kfd_process *p) · 1 unmodified line | ||
| 1998 | 1998 | if (!ef) |
| 1999 | 1999 | return -EINVAL; |
| 2000 | 2000 | |
| 2001 | − ret = dma_fence_signal(ef); | |
| 2001 | + ret = dma_fence_check_and_signal(ef); | |
| 2002 | 2002 | dma_fence_put(ef); |
| 2003 | 2003 | |
| 2004 | 2004 | return ret; |
| @@ -28,6 +28,7 @@ · 27 unmodified lines | ||
| 28 | 28 | #include <drm/drm_damage_helper.h> |
| 29 | 29 | #include <drm/drm_format_helper.h> |
| 30 | 30 | #include <drm/drm_gem_atomic_helper.h> |
| 31 | +#include <drm/drm_gem_framebuffer_helper.h> | |
| 31 | 32 | #include <drm/drm_print.h> |
| 32 | 33 | |
| 33 | 34 | #include "ast_drv.h" |
| @@ -181,6 +182,62 @@ static int ast_cursor_plane_helper_atomic_check(struct drm_plane *plane, · 147 unmodified lines | ||
| 181 | 182 | return 0; |
| 182 | 183 | } |
| 183 | 184 | |
| 185 | +static const u8 *ast_cursor_plane_get_argb4444(struct ast_cursor_plane *ast_cursor_plane, | |
| 186 | + struct drm_shadow_plane_state *shadow_plane_state, | |
| 187 | + const struct drm_rect *clip) | |
| 188 | +{ | |
| 189 | + struct drm_plane_state *plane_state = &shadow_plane_state->base; | |
| 190 | + struct drm_framebuffer *fb = plane_state->fb; | |
| 191 | + u8 *argb4444 = NULL; | |
| 192 | + | |
| 193 | + if (drm_gem_fb_begin_cpu_access(fb, DMA_FROM_DEVICE) == 0) { | |
| 194 | + switch (fb->format->format) { | |
| 195 | + case DRM_FORMAT_ARGB4444: | |
| 196 | + if (shadow_plane_state->data[0].is_iomem) { | |
| 197 | + struct iosys_map argb4444_dst[DRM_FORMAT_MAX_PLANES] = { | |
| 198 | + IOSYS_MAP_INIT_VADDR(ast_cursor_plane->argb4444), | |
| 199 | + }; | |
| 200 | + unsigned int argb4444_dst_pitch[DRM_FORMAT_MAX_PLANES] = { | |
| 201 | + AST_HWC_PITCH, | |
| 202 | + }; | |
| 203 | + | |
| 204 | + drm_fb_memcpy(argb4444_dst, argb4444_dst_pitch, | |
| 205 | + shadow_plane_state->data, fb, clip); | |
| 206 | + argb4444 = argb4444_dst[0].vaddr; | |
| 207 | + } else { | |
| 208 | + argb4444 = shadow_plane_state->data[0].vaddr; | |
| 209 | + } | |
| 210 | + break; | |
| 211 | + case DRM_FORMAT_ARGB8888: | |
| 212 | + { | |
| 213 | + struct iosys_map argb4444_dst[DRM_FORMAT_MAX_PLANES] = { | |
| 214 | + IOSYS_MAP_INIT_VADDR(ast_cursor_plane->argb4444), | |
| 215 | + }; | |
| 216 | + unsigned int argb4444_dst_pitch[DRM_FORMAT_MAX_PLANES] = { | |
| 217 | + AST_HWC_PITCH, | |
| 218 | + }; | |
| 219 | + | |
| 220 | + drm_fb_argb8888_to_argb4444(argb4444_dst, argb4444_dst_pitch, | |
| 221 | + shadow_plane_state->data, fb, clip, | |
| 222 | + &shadow_plane_state->fmtcnv_state); | |
| 223 | + argb4444 = argb4444_dst[0].vaddr; | |
| 224 | + } | |
| 225 | + break; | |
| 226 | + } | |
| 227 | + | |
| 228 | + drm_gem_fb_end_cpu_access(fb, DMA_FROM_DEVICE); | |
| 229 | + } else { | |
| 230 | + /* | |
| 231 | + * Fall back to white square if GEM object is not ready. Gives | |
| 232 | + * the user an indication where the cursor is located. | |
| 233 | + */ | |
| 234 | + memset(ast_cursor_plane->argb4444, 0xff, sizeof(ast_cursor_plane->argb4444)); | |
| 235 | + argb4444 = ast_cursor_plane->argb4444; | |
| 236 | + } | |
| 237 | + | |
| 238 | + return argb4444; | |
| 239 | +} | |
| 240 | + | |
| 184 | 241 | static void ast_cursor_plane_helper_atomic_update(struct drm_plane *plane, |
| 185 | 242 | struct drm_atomic_state *state) |
| 186 | 243 | { |
| @@ -205,29 +262,13 @@ static void ast_cursor_plane_helper_atomic_update(struct drm_plane *plane, · 18 unmodified lines | ||
| 205 | 262 | */ |
| 206 | 263 | |
| 207 | 264 | if (drm_atomic_helper_damage_merged(old_plane_state, plane_state, &damage)) { |
| 208 | − u8 *argb4444; | |
| 265 | + const u8 *argb4444 = ast_cursor_plane_get_argb4444(ast_cursor_plane, | |
| 266 | + shadow_plane_state, | |
| 267 | + &damage); | |
| 209 | 268 | |
| 210 | − switch (fb->format->format) { | |
| 211 | − case DRM_FORMAT_ARGB4444: | |
| 212 | − argb4444 = shadow_plane_state->data[0].vaddr; | |
| 213 | − break; | |
| 214 | − default: | |
| 215 | − argb4444 = ast_cursor_plane->argb4444; | |
| 216 | − { | |
| 217 | − struct iosys_map argb4444_dst[DRM_FORMAT_MAX_PLANES] = { | |
| 218 | − IOSYS_MAP_INIT_VADDR(argb4444), | |
| 219 | − }; | |
| 220 | − unsigned int argb4444_dst_pitch[DRM_FORMAT_MAX_PLANES] = { | |
| 221 | − AST_HWC_PITCH, | |
| 222 | − }; | |
| 269 | + if (argb4444) | |
| 270 | + ast_set_cursor_image(ast, argb4444, fb->width, fb->height); | |
| 223 | 271 | |
| 224 | − drm_fb_argb8888_to_argb4444(argb4444_dst, argb4444_dst_pitch, | |
| 225 | − shadow_plane_state->data, fb, &damage, | |
| 226 | − &shadow_plane_state->fmtcnv_state); | |
| 227 | − } | |
| 228 | − break; | |
| 229 | − } | |
| 230 | − ast_set_cursor_image(ast, argb4444, fb->width, fb->height); | |
| 231 | 272 | ast_set_cursor_base(ast, dst_off); |
| 232 | 273 | } |
| 233 | 274 | |
| @@ -406,6 +406,10 @@ static void sn65dsi83_reset_work(struct work_struct *ws) · 405 unmodified lines | ||
| 406 | 406 | { |
| 407 | 407 | struct sn65dsi83 *ctx = container_of(ws, struct sn65dsi83, reset_work); |
| 408 | 408 | int ret; |
| 409 | + int idx; | |
| 410 | + | |
| 411 | + if (!drm_bridge_enter(&ctx->bridge, &idx)) | |
| 412 | + return; | |
| 409 | 413 | |
| 410 | 414 | /* Reset the pipe */ |
| 411 | 415 | ret = sn65dsi83_reset_pipe(ctx); |
| @@ -415,12 +419,18 @@ static void sn65dsi83_reset_work(struct work_struct *ws) · 3 unmodified lines | ||
| 415 | 419 | } |
| 416 | 420 | if (ctx->irq) |
| 417 | 421 | enable_irq(ctx->irq); |
| 422 | + | |
| 423 | + drm_bridge_exit(idx); | |
| 418 | 424 | } |
| 419 | 425 | |
| 420 | 426 | static void sn65dsi83_handle_errors(struct sn65dsi83 *ctx) |
| 421 | 427 | { |
| 422 | 428 | unsigned int irq_stat; |
| 423 | 429 | int ret; |
| 430 | + int idx; | |
| 431 | + | |
| 432 | + if (!drm_bridge_enter(&ctx->bridge, &idx)) | |
| 433 | + return; | |
| 424 | 434 | |
| 425 | 435 | /* |
| 426 | 436 | * Schedule a reset in case of: |
| @@ -448,6 +458,8 @@ static void sn65dsi83_handle_errors(struct sn65dsi83 *ctx) · 21 unmodified lines | ||
| 448 | 458 | |
| 449 | 459 | schedule_work(&ctx->reset_work); |
| 450 | 460 | } |
| 461 | + | |
| 462 | + drm_bridge_exit(idx); | |
| 451 | 463 | } |
| 452 | 464 | |
| 453 | 465 | static void sn65dsi83_monitor_work(struct work_struct *work) |
| @@ -470,6 +482,37 @@ static void sn65dsi83_monitor_stop(struct sn65dsi83 *ctx) · 16 unmodified lines | ||
| 470 | 482 | cancel_delayed_work_sync(&ctx->monitor_work); |
| 471 | 483 | } |
| 472 | 484 | |
| 485 | +/* | |
| 486 | + * Release resources taken by sn65dsi83_atomic_pre_enable(). | |
| 487 | + * | |
| 488 | + * Invoked by sn65dsi83_atomic_disable() normally, or by devres after | |
| 489 | + * sn65dsi83_remove() in case this happens befora atomic_disable. | |
| 490 | + */ | |
| 491 | +static void sn65dsi83_release_resources(void *data) | |
| 492 | +{ | |
| 493 | + struct sn65dsi83 *ctx = (struct sn65dsi83 *)data; | |
| 494 | + int ret; | |
| 495 | + | |
| 496 | + if (ctx->irq) { | |
| 497 | + /* Disable irq */ | |
| 498 | + regmap_write(ctx->regmap, REG_IRQ_EN, 0x0); | |
| 499 | + regmap_write(ctx->regmap, REG_IRQ_GLOBAL, 0x0); | |
| 500 | + } else { | |
| 501 | + /* Stop the polling task */ | |
| 502 | + sn65dsi83_monitor_stop(ctx); | |
| 503 | + } | |
| 504 | + | |
| 505 | + /* Put the chip in reset, pull EN line low, and assure 10ms reset low timing. */ | |
| 506 | + gpiod_set_value_cansleep(ctx->enable_gpio, 0); | |
| 507 | + usleep_range(10000, 11000); | |
| 508 | + | |
| 509 | + ret = regulator_disable(ctx->vcc); | |
| 510 | + if (ret) | |
| 511 | + dev_err(ctx->dev, "Failed to disable vcc: %d\n", ret); | |
| 512 | + | |
| 513 | + regcache_mark_dirty(ctx->regmap); | |
| 514 | +} | |
| 515 | + | |
| 473 | 516 | static void sn65dsi83_atomic_pre_enable(struct drm_bridge *bridge, |
| 474 | 517 | struct drm_atomic_state *state) |
| 475 | 518 | { |
| @@ -485,11 +528,15 @@ static void sn65dsi83_atomic_pre_enable(struct drm_bridge *bridge, · 9 unmodified lines | ||
| 485 | 528 | __le16 le16val; |
| 486 | 529 | u16 val; |
| 487 | 530 | int ret; |
| 531 | + int idx; | |
| 532 | + | |
| 533 | + if (!drm_bridge_enter(bridge, &idx)) | |
| 534 | + return; | |
| 488 | 535 | |
| 489 | 536 | ret = regulator_enable(ctx->vcc); |
| 490 | 537 | if (ret) { |
| 491 | 538 | dev_err(ctx->dev, "Failed to enable vcc: %d\n", ret); |
| 492 | − return; | |
| 539 | + goto err_exit; | |
| 493 | 540 | } |
| 494 | 541 | |
| 495 | 542 | /* Deassert reset */ |
| @@ -632,7 +679,7 @@ static void sn65dsi83_atomic_pre_enable(struct drm_bridge *bridge, · 136 unmodified lines | ||
| 632 | 679 | dev_err(ctx->dev, "failed to lock PLL, ret=%i\n", ret); |
| 633 | 680 | /* On failure, disable PLL again and exit. */ |
| 634 | 681 | regmap_write(ctx->regmap, REG_RC_PLL_EN, 0x00); |
| 635 | − return; | |
| 682 | + goto err_add_action; | |
| 636 | 683 | } |
| 637 | 684 | |
| 638 | 685 | /* Trigger reset after CSR register update. */ |
| @@ -640,6 +687,11 @@ static void sn65dsi83_atomic_pre_enable(struct drm_bridge *bridge, · 1 unmodified line | ||
| 640 | 687 | |
| 641 | 688 | /* Wait for 10ms after soft reset as specified in datasheet */ |
| 642 | 689 | usleep_range(10000, 12000); |
| 690 | + | |
| 691 | +err_add_action: | |
| 692 | + devm_add_action(ctx->dev, sn65dsi83_release_resources, ctx); | |
| 693 | +err_exit: | |
| 694 | + drm_bridge_exit(idx); | |
| 643 | 695 | } |
| 644 | 696 | |
| 645 | 697 | static void sn65dsi83_atomic_enable(struct drm_bridge *bridge, |
| @@ -647,6 +699,10 @@ static void sn65dsi83_atomic_enable(struct drm_bridge *bridge, · 1 unmodified line | ||
| 647 | 699 | { |
| 648 | 700 | struct sn65dsi83 *ctx = bridge_to_sn65dsi83(bridge); |
| 649 | 701 | unsigned int pval; |
| 702 | + int idx; | |
| 703 | + | |
| 704 | + if (!drm_bridge_enter(bridge, &idx)) | |
| 705 | + return; | |
| 650 | 706 | |
| 651 | 707 | /* Clear all errors that got asserted during initialization. */ |
| 652 | 708 | regmap_read(ctx->regmap, REG_IRQ_STAT, &pval); |
| @@ -666,32 +722,22 @@ static void sn65dsi83_atomic_enable(struct drm_bridge *bridge, · 13 unmodified lines | ||
| 666 | 722 | /* Use the polling task */ |
| 667 | 723 | sn65dsi83_monitor_start(ctx); |
| 668 | 724 | } |
| 725 | + | |
| 726 | + drm_bridge_exit(idx); | |
| 669 | 727 | } |
| 670 | 728 | |
| 671 | 729 | static void sn65dsi83_atomic_disable(struct drm_bridge *bridge, |
| 672 | 730 | struct drm_atomic_state *state) |
| 673 | 731 | { |
| 674 | 732 | struct sn65dsi83 *ctx = bridge_to_sn65dsi83(bridge); |
| 675 | − int ret; | |
| 733 | + int idx; | |
| 676 | 734 | |
| 677 | − if (ctx->irq) { | |
| 678 | − /* Disable irq */ | |
| 679 | − regmap_write(ctx->regmap, REG_IRQ_EN, 0x0); | |
| 680 | − regmap_write(ctx->regmap, REG_IRQ_GLOBAL, 0x0); | |
| 681 | − } else { | |
| 682 | − /* Stop the polling task */ | |
| 683 | − sn65dsi83_monitor_stop(ctx); | |
| 684 | − } | |
| 685 | − | |
| 686 | − /* Put the chip in reset, pull EN line low, and assure 10ms reset low timing. */ | |
| 687 | − gpiod_set_value_cansleep(ctx->enable_gpio, 0); | |
| 688 | − usleep_range(10000, 11000); | |
| 735 | + if (!drm_bridge_enter(bridge, &idx)) | |
| 736 | + return; | |
| 689 | 737 | |
| 690 | − ret = regulator_disable(ctx->vcc); | |
| 691 | − if (ret) | |
| 692 | − dev_err(ctx->dev, "Failed to disable vcc: %d\n", ret); | |
| 738 | + devm_release_action(ctx->dev, sn65dsi83_release_resources, ctx); | |
| 693 | 739 | |
| 694 | − regcache_mark_dirty(ctx->regmap); | |
| 740 | + drm_bridge_exit(idx); | |
| 695 | 741 | } |
| 696 | 742 | |
| 697 | 743 | static enum drm_mode_status |
| @@ -1012,7 +1058,7 @@ static void sn65dsi83_remove(struct i2c_client *client) · 314 unmodified lines | ||
| 1012 | 1058 | { |
| 1013 | 1059 | struct sn65dsi83 *ctx = i2c_get_clientdata(client); |
| 1014 | 1060 | |
| 1015 | − drm_bridge_remove(&ctx->bridge); | |
| 1061 | + drm_bridge_unplug(&ctx->bridge); | |
| 1016 | 1062 | } |
| 1017 | 1063 | |
| 1018 | 1064 | static const struct i2c_device_id sn65dsi83_id[] = { |
| @@ -4572,7 +4572,8 @@ int drm_dp_atomic_release_time_slots(struct drm_atomic_state *state, · 4571 unmodified lines | ||
| 4572 | 4572 | if (!payload->delete) { |
| 4573 | 4573 | payload->pbn = 0; |
| 4574 | 4574 | payload->delete = true; |
| 4575 | − topology_state->payload_mask &= ~BIT(payload->vcpi - 1); | |
| 4575 | + if (payload->vcpi > 0) | |
| 4576 | + topology_state->payload_mask &= ~BIT(payload->vcpi - 1); | |
| 4576 | 4577 | } |
| 4577 | 4578 | |
| 4578 | 4579 | return 0; |
| @@ -906,6 +906,7 @@ drm_atomic_private_obj_init(struct drm_device *dev, · 905 unmodified lines | ||
| 906 | 906 | |
| 907 | 907 | drm_modeset_lock_init(&obj->lock); |
| 908 | 908 | |
| 909 | + obj->dev = dev; | |
| 909 | 910 | obj->state = state; |
| 910 | 911 | obj->funcs = funcs; |
| 911 | 912 | list_add_tail(&obj->head, &dev->mode_config.privobj_list); |
| @@ -27,6 +27,7 @@ · 26 unmodified lines | ||
| 27 | 27 | #include <linux/media-bus-format.h> |
| 28 | 28 | #include <linux/module.h> |
| 29 | 29 | #include <linux/mutex.h> |
| 30 | +#include <linux/srcu.h> | |
| 30 | 31 | |
| 31 | 32 | #include <drm/drm_atomic_state_helper.h> |
| 32 | 33 | #include <drm/drm_bridge.h> |
| @@ -202,6 +203,67 @@ static DEFINE_MUTEX(bridge_lock); · 169 unmodified lines | ||
| 202 | 203 | static LIST_HEAD(bridge_list); |
| 203 | 204 | static LIST_HEAD(bridge_lingering_list); |
| 204 | 205 | |
| 206 | +DEFINE_STATIC_SRCU(drm_bridge_unplug_srcu); | |
| 207 | + | |
| 208 | +/** | |
| 209 | + * drm_bridge_enter - Enter DRM bridge critical section | |
| 210 | + * @bridge: DRM bridge | |
| 211 | + * @idx: Pointer to index that will be passed to the matching drm_bridge_exit() | |
| 212 | + * | |
| 213 | + * This function marks and protects the beginning of a section that should not | |
| 214 | + * be entered after the bridge has been unplugged. The section end is marked | |
| 215 | + * with drm_bridge_exit(). Calls to this function can be nested. | |
| 216 | + * | |
| 217 | + * Returns: | |
| 218 | + * True if it is OK to enter the section, false otherwise. | |
| 219 | + */ | |
| 220 | +bool drm_bridge_enter(struct drm_bridge *bridge, int *idx) | |
| 221 | +{ | |
| 222 | + *idx = srcu_read_lock(&drm_bridge_unplug_srcu); | |
| 223 | + | |
| 224 | + if (bridge->unplugged) { | |
| 225 | + srcu_read_unlock(&drm_bridge_unplug_srcu, *idx); | |
| 226 | + return false; | |
| 227 | + } | |
| 228 | + | |
| 229 | + return true; | |
| 230 | +} | |
| 231 | +EXPORT_SYMBOL(drm_bridge_enter); | |
| 232 | + | |
| 233 | +/** | |
| 234 | + * drm_bridge_exit - Exit DRM bridge critical section | |
| 235 | + * @idx: index returned by drm_bridge_enter() | |
| 236 | + * | |
| 237 | + * This function marks the end of a section that should not be entered after | |
| 238 | + * the bridge has been unplugged. | |
| 239 | + */ | |
| 240 | +void drm_bridge_exit(int idx) | |
| 241 | +{ | |
| 242 | + srcu_read_unlock(&drm_bridge_unplug_srcu, idx); | |
| 243 | +} | |
| 244 | +EXPORT_SYMBOL(drm_bridge_exit); | |
| 245 | + | |
| 246 | +/** | |
| 247 | + * drm_bridge_unplug - declare a DRM bridge was unplugged and remove it | |
| 248 | + * @bridge: DRM bridge | |
| 249 | + * | |
| 250 | + * This tells the bridge has been physically unplugged and no operations on | |
| 251 | + * device resources must be done anymore. Entry-points can use | |
| 252 | + * drm_bridge_enter() and drm_bridge_exit() to protect device resources in | |
| 253 | + * a race free manner. | |
| 254 | + * | |
| 255 | + * Also unregisters the bridge. | |
| 256 | + */ | |
| 257 | +void drm_bridge_unplug(struct drm_bridge *bridge) | |
| 258 | +{ | |
| 259 | + bridge->unplugged = true; | |
| 260 | + | |
| 261 | + synchronize_srcu(&drm_bridge_unplug_srcu); | |
| 262 | + | |
| 263 | + drm_bridge_remove(bridge); | |
| 264 | +} | |
| 265 | +EXPORT_SYMBOL(drm_bridge_unplug); | |
| 266 | + | |
| 205 | 267 | static void __drm_bridge_free(struct kref *kref) |
| 206 | 268 | { |
| 207 | 269 | struct drm_bridge *bridge = container_of(kref, struct drm_bridge, refcount); |
| @@ -75,9 +75,6 @@ MODULE_PARM_DESC(drm_leak_fbdev_smem, · 74 unmodified lines | ||
| 75 | 75 | "Allow unsafe leaking fbdev physical smem address [default=false]"); |
| 76 | 76 | #endif |
| 77 | 77 | |
| 78 | −static LIST_HEAD(kernel_fb_helper_list); | |
| 79 | −static DEFINE_MUTEX(kernel_fb_helper_lock); | |
| 80 | − | |
| 81 | 78 | /** |
| 82 | 79 | * DOC: fbdev helpers |
| 83 | 80 | * |
| @@ -115,101 +112,6 @@ static DEFINE_MUTEX(kernel_fb_helper_lock); · 31 unmodified lines | ||
| 115 | 112 | * mmap page writes. |
| 116 | 113 | */ |
| 117 | 114 | |
| 118 | −static void drm_fb_helper_restore_lut_atomic(struct drm_crtc *crtc) | |
| 119 | −{ | |
| 120 | − uint16_t *r_base, *g_base, *b_base; | |
| 121 | − | |
| 122 | − if (crtc->funcs->gamma_set == NULL) | |
| 123 | − return; | |
| 124 | − | |
| 125 | − r_base = crtc->gamma_store; | |
| 126 | − g_base = r_base + crtc->gamma_size; | |
| 127 | − b_base = g_base + crtc->gamma_size; | |
| 128 | − | |
| 129 | − crtc->funcs->gamma_set(crtc, r_base, g_base, b_base, | |
| 130 | − crtc->gamma_size, NULL); | |
| 131 | −} | |
| 132 | − | |
| 133 | −/** | |
| 134 | − * drm_fb_helper_debug_enter - implementation for &fb_ops.fb_debug_enter | |
| 135 | − * @info: fbdev registered by the helper | |
| 136 | − */ | |
| 137 | −int drm_fb_helper_debug_enter(struct fb_info *info) | |
| 138 | −{ | |
| 139 | − struct drm_fb_helper *helper = info->par; | |
| 140 | − const struct drm_crtc_helper_funcs *funcs; | |
| 141 | − struct drm_mode_set *mode_set; | |
| 142 | − | |
| 143 | − list_for_each_entry(helper, &kernel_fb_helper_list, kernel_fb_list) { | |
| 144 | − mutex_lock(&helper->client.modeset_mutex); | |
| 145 | − drm_client_for_each_modeset(mode_set, &helper->client) { | |
| 146 | − if (!mode_set->crtc->enabled) | |
| 147 | − continue; | |
| 148 | − | |
| 149 | − funcs = mode_set->crtc->helper_private; | |
| 150 | − if (funcs->mode_set_base_atomic == NULL) | |
| 151 | − continue; | |
| 152 | − | |
| 153 | − if (drm_drv_uses_atomic_modeset(mode_set->crtc->dev)) | |
| 154 | − continue; | |
| 155 | − | |
| 156 | − funcs->mode_set_base_atomic(mode_set->crtc, | |
| 157 | − mode_set->fb, | |
| 158 | − mode_set->x, | |
| 159 | − mode_set->y, | |
| 160 | − ENTER_ATOMIC_MODE_SET); | |
| 161 | − } | |
| 162 | − mutex_unlock(&helper->client.modeset_mutex); | |
| 163 | − } | |
| 164 | − | |
| 165 | − return 0; | |
| 166 | −} | |
| 167 | −EXPORT_SYMBOL(drm_fb_helper_debug_enter); | |
| 168 | − | |
| 169 | −/** | |
| 170 | − * drm_fb_helper_debug_leave - implementation for &fb_ops.fb_debug_leave | |
| 171 | − * @info: fbdev registered by the helper | |
| 172 | − */ | |
| 173 | −int drm_fb_helper_debug_leave(struct fb_info *info) | |
| 174 | −{ | |
| 175 | − struct drm_fb_helper *helper = info->par; | |
| 176 | − struct drm_client_dev *client = &helper->client; | |
| 177 | − struct drm_device *dev = helper->dev; | |
| 178 | − struct drm_crtc *crtc; | |
| 179 | − const struct drm_crtc_helper_funcs *funcs; | |
| 180 | − struct drm_mode_set *mode_set; | |
| 181 | − struct drm_framebuffer *fb; | |
| 182 | − | |
| 183 | − mutex_lock(&client->modeset_mutex); | |
| 184 | − drm_client_for_each_modeset(mode_set, client) { | |
| 185 | − crtc = mode_set->crtc; | |
| 186 | − if (drm_drv_uses_atomic_modeset(crtc->dev)) | |
| 187 | − continue; | |
| 188 | − | |
| 189 | − funcs = crtc->helper_private; | |
| 190 | − fb = crtc->primary->fb; | |
| 191 | − | |
| 192 | − if (!crtc->enabled) | |
| 193 | − continue; | |
| 194 | − | |
| 195 | − if (!fb) { | |
| 196 | − drm_err(dev, "no fb to restore?\n"); | |
| 197 | − continue; | |
| 198 | − } | |
| 199 | − | |
| 200 | − if (funcs->mode_set_base_atomic == NULL) | |
| 201 | − continue; | |
| 202 | − | |
| 203 | − drm_fb_helper_restore_lut_atomic(mode_set->crtc); | |
| 204 | − funcs->mode_set_base_atomic(mode_set->crtc, fb, crtc->x, | |
| 205 | − crtc->y, LEAVE_ATOMIC_MODE_SET); | |
| 206 | − } | |
| 207 | − mutex_unlock(&client->modeset_mutex); | |
| 208 | − | |
| 209 | − return 0; | |
| 210 | −} | |
| 211 | −EXPORT_SYMBOL(drm_fb_helper_debug_leave); | |
| 212 | − | |
| 213 | 115 | static int |
| 214 | 116 | __drm_fb_helper_restore_fbdev_mode_unlocked(struct drm_fb_helper *fb_helper, |
| 215 | 117 | bool force) |
| @@ -397,7 +299,6 @@ void drm_fb_helper_prepare(struct drm_device *dev, struct drm_fb_helper *helper, · 181 unmodified lines | ||
| 397 | 299 | if (!preferred_bpp) |
| 398 | 300 | preferred_bpp = 32; |
| 399 | 301 | |
| 400 | − INIT_LIST_HEAD(&helper->kernel_fb_list); | |
| 401 | 302 | spin_lock_init(&helper->damage_lock); |
| 402 | 303 | INIT_WORK(&helper->resume_work, drm_fb_helper_resume_worker); |
| 403 | 304 | INIT_WORK(&helper->damage_work, drm_fb_helper_damage_work); |
| @@ -534,11 +435,6 @@ void drm_fb_helper_fini(struct drm_fb_helper *fb_helper) · 130 unmodified lines | ||
| 534 | 435 | |
| 535 | 436 | drm_fb_helper_release_info(fb_helper); |
| 536 | 437 | |
| 537 | − mutex_lock(&kernel_fb_helper_lock); | |
| 538 | − if (!list_empty(&fb_helper->kernel_fb_list)) | |
| 539 | − list_del(&fb_helper->kernel_fb_list); | |
| 540 | − mutex_unlock(&kernel_fb_helper_lock); | |
| 541 | − | |
| 542 | 438 | if (!fb_helper->client.funcs) |
| 543 | 439 | drm_client_release(&fb_helper->client); |
| 544 | 440 | } |
| @@ -1766,10 +1662,6 @@ __drm_fb_helper_initial_config_and_unlock(struct drm_fb_helper *fb_helper) · 1221 unmodified lines | ||
| 1766 | 1662 | drm_info(dev, "fb%d: %s frame buffer device\n", |
| 1767 | 1663 | info->node, info->fix.id); |
| 1768 | 1664 | |
| 1769 | − mutex_lock(&kernel_fb_helper_lock); | |
| 1770 | − list_add(&fb_helper->kernel_fb_list, &kernel_fb_helper_list); | |
| 1771 | − mutex_unlock(&kernel_fb_helper_lock); | |
| 1772 | − | |
| 1773 | 1665 | return 0; |
| 1774 | 1666 | |
| 1775 | 1667 | err_drm_fb_helper_release_info: |
| @@ -29,6 +29,9 @@ · 28 unmodified lines | ||
| 29 | 29 | #include <linux/export.h> |
| 30 | 30 | #include <linux/file.h> |
| 31 | 31 | #include <linux/fs.h> |
| 32 | +#ifdef CONFIG_TRANSPARENT_HUGEPAGE | |
| 33 | +#include <linux/fs_context.h> | |
| 34 | +#endif | |
| 32 | 35 | #include <linux/iosys-map.h> |
| 33 | 36 | #include <linux/mem_encrypt.h> |
| 34 | 37 | #include <linux/mm.h> |
| @@ -36,6 +39,7 @@ · 1 unmodified line | ||
| 36 | 39 | #include <linux/module.h> |
| 37 | 40 | #include <linux/pagemap.h> |
| 38 | 41 | #include <linux/pagevec.h> |
| 42 | +#include <linux/sched/mm.h> | |
| 39 | 43 | #include <linux/shmem_fs.h> |
| 40 | 44 | #include <linux/slab.h> |
| 41 | 45 | #include <linux/string_helpers.h> |
| @@ -81,6 +85,60 @@ · 39 unmodified lines | ||
| 81 | 85 | * up at a later date, and as our interface with shmfs for memory allocation. |
| 82 | 86 | */ |
| 83 | 87 | |
| 88 | +#ifdef CONFIG_TRANSPARENT_HUGEPAGE | |
| 89 | +static void drm_gem_huge_mnt_free(struct drm_device *dev, void *data) | |
| 90 | +{ | |
| 91 | + kern_unmount(dev->huge_mnt); | |
| 92 | +} | |
| 93 | + | |
| 94 | +/** | |
| 95 | + * drm_gem_huge_mnt_create - Create, mount and use a huge tmpfs mountpoint | |
| 96 | + * @dev: DRM device that will use the huge tmpfs mountpoint | |
| 97 | + * @value: huge tmpfs mount option value | |
| 98 | + * | |
| 99 | + * This function creates and mounts a dedicated huge tmpfs mountpoint for the | |
| 100 | + * lifetime of the DRM device @dev which is used at GEM object initialization | |
| 101 | + * with drm_gem_object_init(). | |
| 102 | + * | |
| 103 | + * The most common option for @value is "within_size" which only allocates huge | |
| 104 | + * pages if the page will be fully within the GEM object size. "always", | |
| 105 | + * "advise" and "never" are supported too but the latter would just create a | |
| 106 | + * mountpoint similar to the default one (`shm_mnt`). See shmemfs and | |
| 107 | + * Transparent Hugepage for more information. | |
| 108 | + * | |
| 109 | + * Returns: | |
| 110 | + * 0 on success or a negative error code on failure. | |
| 111 | + */ | |
| 112 | +int drm_gem_huge_mnt_create(struct drm_device *dev, const char *value) | |
| 113 | +{ | |
| 114 | + struct file_system_type *type; | |
| 115 | + struct fs_context *fc; | |
| 116 | + int ret; | |
| 117 | + | |
| 118 | + if (unlikely(drm_gem_get_huge_mnt(dev))) | |
| 119 | + return 0; | |
| 120 | + | |
| 121 | + type = get_fs_type("tmpfs"); | |
| 122 | + if (unlikely(!type)) | |
| 123 | + return -EOPNOTSUPP; | |
| 124 | + fc = fs_context_for_mount(type, SB_KERNMOUNT); | |
| 125 | + if (IS_ERR(fc)) | |
| 126 | + return PTR_ERR(fc); | |
| 127 | + ret = vfs_parse_fs_string(fc, "source", "tmpfs"); | |
| 128 | + if (unlikely(ret)) | |
| 129 | + return -ENOPARAM; | |
| 130 | + ret = vfs_parse_fs_string(fc, "huge", value); | |
| 131 | + if (unlikely(ret)) | |
| 132 | + return -ENOPARAM; | |
| 133 | + | |
| 134 | + dev->huge_mnt = fc_mount_longterm(fc); | |
| 135 | + put_fs_context(fc); | |
| 136 | + | |
| 137 | + return drmm_add_action_or_reset(dev, drm_gem_huge_mnt_free, NULL); | |
| 138 | +} | |
| 139 | +EXPORT_SYMBOL_GPL(drm_gem_huge_mnt_create); | |
| 140 | +#endif | |
| 141 | + | |
| 84 | 142 | static void |
| 85 | 143 | drm_gem_init_release(struct drm_device *dev, void *ptr) |
| 86 | 144 | { |
| @@ -113,29 +171,28 @@ drm_gem_init(struct drm_device *dev) · 26 unmodified lines | ||
| 113 | 171 | } |
| 114 | 172 | |
| 115 | 173 | /** |
| 116 | − * drm_gem_object_init_with_mnt - initialize an allocated shmem-backed GEM | |
| 117 | − * object in a given shmfs mountpoint | |
| 174 | + * drm_gem_object_init - initialize an allocated shmem-backed GEM object | |
| 118 | 175 | * |
| 119 | 176 | * @dev: drm_device the object should be initialized for |
| 120 | 177 | * @obj: drm_gem_object to initialize |
| 121 | 178 | * @size: object size |
| 122 | − * @gemfs: tmpfs mount where the GEM object will be created. If NULL, use | |
| 123 | − * the usual tmpfs mountpoint (`shm_mnt`). | |
| 124 | 179 | * |
| 125 | 180 | * Initialize an already allocated GEM object of the specified size with |
| 126 | − * shmfs backing store. | |
| 181 | + * shmfs backing store. A huge mountpoint can be used by calling | |
| 182 | + * drm_gem_huge_mnt_create() beforehand. | |
| 127 | 183 | */ |
| 128 | −int drm_gem_object_init_with_mnt(struct drm_device *dev, | |
| 129 | − struct drm_gem_object *obj, size_t size, | |
| 130 | − struct vfsmount *gemfs) | |
| 184 | +int drm_gem_object_init(struct drm_device *dev, struct drm_gem_object *obj, | |
| 185 | + size_t size) | |
| 131 | 186 | { |
| 187 | + struct vfsmount *huge_mnt; | |
| 132 | 188 | struct file *filp; |
| 133 | 189 | |
| 134 | 190 | drm_gem_private_object_init(dev, obj, size); |
| 135 | 191 | |
| 136 | − if (gemfs) | |
| 137 | − filp = shmem_file_setup_with_mnt(gemfs, "drm mm object", size, | |
| 138 | − VM_NORESERVE); | |
| 192 | + huge_mnt = drm_gem_get_huge_mnt(dev); | |
| 193 | + if (huge_mnt) | |
| 194 | + filp = shmem_file_setup_with_mnt(huge_mnt, "drm mm object", | |
| 195 | + size, VM_NORESERVE); | |
| 139 | 196 | else |
| 140 | 197 | filp = shmem_file_setup("drm mm object", size, VM_NORESERVE); |
| 141 | 198 | |
| @@ -146,22 +203,6 @@ int drm_gem_object_init_with_mnt(struct drm_device *dev, · 4 unmodified lines | ||
| 146 | 203 | |
| 147 | 204 | return 0; |
| 148 | 205 | } |
| 149 | −EXPORT_SYMBOL(drm_gem_object_init_with_mnt); | |
| 150 | − | |
| 151 | −/** | |
| 152 | − * drm_gem_object_init - initialize an allocated shmem-backed GEM object | |
| 153 | − * @dev: drm_device the object should be initialized for | |
| 154 | − * @obj: drm_gem_object to initialize | |
| 155 | − * @size: object size | |
| 156 | − * | |
| 157 | − * Initialize an already allocated GEM object of the specified size with | |
| 158 | − * shmfs backing store. | |
| 159 | − */ | |
| 160 | −int drm_gem_object_init(struct drm_device *dev, struct drm_gem_object *obj, | |
| 161 | − size_t size) | |
| 162 | −{ | |
| 163 | − return drm_gem_object_init_with_mnt(dev, obj, size, NULL); | |
| 164 | −} | |
| 165 | 206 | EXPORT_SYMBOL(drm_gem_object_init); |
| 166 | 207 | |
| 167 | 208 | /** |
| @@ -1181,36 +1222,27 @@ err_drm_gem_object_put: · 1013 unmodified lines | ||
| 1181 | 1222 | } |
| 1182 | 1223 | EXPORT_SYMBOL(drm_gem_mmap_obj); |
| 1183 | 1224 | |
| 1184 | −/** | |
| 1185 | − * drm_gem_mmap - memory map routine for GEM objects | |
| 1186 | − * @filp: DRM file pointer | |
| 1187 | − * @vma: VMA for the area to be mapped | |
| 1188 | − * | |
| 1189 | − * If a driver supports GEM object mapping, mmap calls on the DRM file | |
| 1190 | − * descriptor will end up here. | |
| 1191 | − * | |
| 1192 | − * Look up the GEM object based on the offset passed in (vma->vm_pgoff will | |
| 1193 | − * contain the fake offset we created when the GTT map ioctl was called on | |
| 1194 | − * the object) and map it with a call to drm_gem_mmap_obj(). | |
| 1195 | − * | |
| 1196 | − * If the caller is not granted access to the buffer object, the mmap will fail | |
| 1197 | − * with EACCES. Please see the vma manager for more information. | |
| 1225 | +/* | |
| 1226 | + * Look up a GEM object in offset space based on the exact start address. The | |
| 1227 | + * caller must be granted access to the object. Returns a GEM object on success | |
| 1228 | + * or a negative error code on failure. The returned GEM object needs to be | |
| 1229 | + * released with drm_gem_object_put(). | |
| 1198 | 1230 | */ |
| 1199 | −int drm_gem_mmap(struct file *filp, struct vm_area_struct *vma) | |
| 1231 | +static struct drm_gem_object * | |
| 1232 | +drm_gem_object_lookup_at_offset(struct file *filp, unsigned long start, | |
| 1233 | + unsigned long pages) | |
| 1200 | 1234 | { |
| 1201 | 1235 | struct drm_file *priv = filp->private_data; |
| 1202 | 1236 | struct drm_device *dev = priv->minor->dev; |
| 1203 | 1237 | struct drm_gem_object *obj = NULL; |
| 1204 | 1238 | struct drm_vma_offset_node *node; |
| 1205 | − int ret; | |
| 1206 | 1239 | |
| 1207 | 1240 | if (drm_dev_is_unplugged(dev)) |
| 1208 | − return -ENODEV; | |
| 1241 | + return ERR_PTR(-ENODEV); | |
| 1209 | 1242 | |
| 1210 | 1243 | drm_vma_offset_lock_lookup(dev->vma_offset_manager); |
| 1211 | 1244 | node = drm_vma_offset_exact_lookup_locked(dev->vma_offset_manager, |
| 1212 | − vma->vm_pgoff, | |
| 1213 | − vma_pages(vma)); | |
| 1245 | + start, pages); | |
| 1214 | 1246 | if (likely(node)) { |
| 1215 | 1247 | obj = container_of(node, struct drm_gem_object, vma_node); |
| 1216 | 1248 | /* |
| @@ -1229,14 +1261,88 @@ int drm_gem_mmap(struct file *filp, struct vm_area_struct *vma) · 12 unmodified lines | ||
| 1229 | 1261 | drm_vma_offset_unlock_lookup(dev->vma_offset_manager); |
| 1230 | 1262 | |
| 1231 | 1263 | if (!obj) |
| 1232 | − return -EINVAL; | |
| 1264 | + return ERR_PTR(-EINVAL); | |
| 1233 | 1265 | |
| 1234 | 1266 | if (!drm_vma_node_is_allowed(node, priv)) { |
| 1235 | 1267 | drm_gem_object_put(obj); |
| 1236 | − return -EACCES; | |
| 1268 | + return ERR_PTR(-EACCES); | |
| 1237 | 1269 | } |
| 1238 | 1270 | |
| 1239 | − ret = drm_gem_mmap_obj(obj, drm_vma_node_size(node) << PAGE_SHIFT, | |
| 1271 | + return obj; | |
| 1272 | +} | |
| 1273 | + | |
| 1274 | +#ifdef CONFIG_MMU | |
| 1275 | +/** | |
| 1276 | + * drm_gem_get_unmapped_area - get memory mapping region routine for GEM objects | |
| 1277 | + * @filp: DRM file pointer | |
| 1278 | + * @uaddr: User address hint | |
| 1279 | + * @len: Mapping length | |
| 1280 | + * @pgoff: Offset (in pages) | |
| 1281 | + * @flags: Mapping flags | |
| 1282 | + * | |
| 1283 | + * If a driver supports GEM object mapping, before ending up in drm_gem_mmap(), | |
| 1284 | + * mmap calls on the DRM file descriptor will first try to find a free linear | |
| 1285 | + * address space large enough for a mapping. Since GEM objects are backed by | |
| 1286 | + * shmem buffers, this should preferably be handled by the shmem virtual memory | |
| 1287 | + * filesystem which can appropriately align addresses to huge page sizes when | |
| 1288 | + * needed. | |
| 1289 | + * | |
| 1290 | + * Look up the GEM object based on the offset passed in (vma->vm_pgoff will | |
| 1291 | + * contain the fake offset we created) and call shmem_get_unmapped_area() with | |
| 1292 | + * the right file pointer. | |
| 1293 | + * | |
| 1294 | + * If a GEM object is not available at the given offset or if the caller is not | |
| 1295 | + * granted access to it, fall back to mm_get_unmapped_area(). | |
| 1296 | + */ | |
| 1297 | +unsigned long drm_gem_get_unmapped_area(struct file *filp, unsigned long uaddr, | |
| 1298 | + unsigned long len, unsigned long pgoff, | |
| 1299 | + unsigned long flags) | |
| 1300 | +{ | |
| 1301 | + struct drm_gem_object *obj; | |
| 1302 | + unsigned long ret; | |
| 1303 | + | |
| 1304 | + obj = drm_gem_object_lookup_at_offset(filp, pgoff, len >> PAGE_SHIFT); | |
| 1305 | + if (IS_ERR(obj) || !obj->filp || !obj->filp->f_op->get_unmapped_area) | |
| 1306 | + return mm_get_unmapped_area(filp, uaddr, len, 0, | |
| 1307 | + flags); | |
| 1308 | + | |
| 1309 | + ret = obj->filp->f_op->get_unmapped_area(obj->filp, uaddr, len, 0, | |
| 1310 | + flags); | |
| 1311 | + | |
| 1312 | + drm_gem_object_put(obj); | |
| 1313 | + | |
| 1314 | + return ret; | |
| 1315 | +} | |
| 1316 | +EXPORT_SYMBOL_GPL(drm_gem_get_unmapped_area); | |
| 1317 | +#endif | |
| 1318 | + | |
| 1319 | +/** | |
| 1320 | + * drm_gem_mmap - memory map routine for GEM objects | |
| 1321 | + * @filp: DRM file pointer | |
| 1322 | + * @vma: VMA for the area to be mapped | |
| 1323 | + * | |
| 1324 | + * If a driver supports GEM object mapping, mmap calls on the DRM file | |
| 1325 | + * descriptor will end up here. | |
| 1326 | + * | |
| 1327 | + * Look up the GEM object based on the offset passed in (vma->vm_pgoff will | |
| 1328 | + * contain the fake offset we created) and map it with a call to | |
| 1329 | + * drm_gem_mmap_obj(). | |
| 1330 | + * | |
| 1331 | + * If the caller is not granted access to the buffer object, the mmap will fail | |
| 1332 | + * with EACCES. Please see the vma manager for more information. | |
| 1333 | + */ | |
| 1334 | +int drm_gem_mmap(struct file *filp, struct vm_area_struct *vma) | |
| 1335 | +{ | |
| 1336 | + struct drm_gem_object *obj; | |
| 1337 | + int ret; | |
| 1338 | + | |
| 1339 | + obj = drm_gem_object_lookup_at_offset(filp, vma->vm_pgoff, | |
| 1340 | + vma_pages(vma)); | |
| 1341 | + if (IS_ERR(obj)) | |
| 1342 | + return PTR_ERR(obj); | |
| 1343 | + | |
| 1344 | + ret = drm_gem_mmap_obj(obj, | |
| 1345 | + drm_vma_node_size(&obj->vma_node) << PAGE_SHIFT, | |
| 1240 | 1346 | vma); |
| 1241 | 1347 | |
| 1242 | 1348 | drm_gem_object_put(obj); |
| @@ -50,7 +50,7 @@ static const struct drm_gem_object_funcs drm_gem_shmem_funcs = { · 49 unmodified lines | ||
| 50 | 50 | }; |
| 51 | 51 | |
| 52 | 52 | static int __drm_gem_shmem_init(struct drm_device *dev, struct drm_gem_shmem_object *shmem, |
| 53 | − size_t size, bool private, struct vfsmount *gemfs) | |
| 53 | + size_t size, bool private) | |
| 54 | 54 | { |
| 55 | 55 | struct drm_gem_object *obj = &shmem->base; |
| 56 | 56 | int ret = 0; |
| @@ -62,7 +62,7 @@ static int __drm_gem_shmem_init(struct drm_device *dev, struct drm_gem_shmem_obj · 5 unmodified lines | ||
| 62 | 62 | drm_gem_private_object_init(dev, obj, size); |
| 63 | 63 | shmem->map_wc = false; /* dma-buf mappings use always writecombine */ |
| 64 | 64 | } else { |
| 65 | − ret = drm_gem_object_init_with_mnt(dev, obj, size, gemfs); | |
| 65 | + ret = drm_gem_object_init(dev, obj, size); | |
| 66 | 66 | } |
| 67 | 67 | if (ret) { |
| 68 | 68 | drm_gem_private_object_fini(obj); |
| @@ -103,13 +103,12 @@ err_release: · 34 unmodified lines | ||
| 103 | 103 | */ |
| 104 | 104 | int drm_gem_shmem_init(struct drm_device *dev, struct drm_gem_shmem_object *shmem, size_t size) |
| 105 | 105 | { |
| 106 | − return __drm_gem_shmem_init(dev, shmem, size, false, NULL); | |
| 106 | + return __drm_gem_shmem_init(dev, shmem, size, false); | |
| 107 | 107 | } |
| 108 | 108 | EXPORT_SYMBOL_GPL(drm_gem_shmem_init); |
| 109 | 109 | |
| 110 | 110 | static struct drm_gem_shmem_object * |
| 111 | −__drm_gem_shmem_create(struct drm_device *dev, size_t size, bool private, | |
| 112 | − struct vfsmount *gemfs) | |
| 111 | +__drm_gem_shmem_create(struct drm_device *dev, size_t size, bool private) | |
| 113 | 112 | { |
| 114 | 113 | struct drm_gem_shmem_object *shmem; |
| 115 | 114 | struct drm_gem_object *obj; |
| @@ -129,7 +128,7 @@ __drm_gem_shmem_create(struct drm_device *dev, size_t size, bool private, · 13 unmodified lines | ||
| 129 | 128 | obj = &shmem->base; |
| 130 | 129 | } |
| 131 | 130 | |
| 132 | − ret = __drm_gem_shmem_init(dev, shmem, size, private, gemfs); | |
| 131 | + ret = __drm_gem_shmem_init(dev, shmem, size, private); | |
| 133 | 132 | if (ret) { |
| 134 | 133 | kfree(obj); |
| 135 | 134 | return ERR_PTR(ret); |
| @@ -150,31 +149,10 @@ __drm_gem_shmem_create(struct drm_device *dev, size_t size, bool private, · 14 unmodified lines | ||
| 150 | 149 | */ |
| 151 | 150 | struct drm_gem_shmem_object *drm_gem_shmem_create(struct drm_device *dev, size_t size) |
| 152 | 151 | { |
| 153 | − return __drm_gem_shmem_create(dev, size, false, NULL); | |
| 152 | + return __drm_gem_shmem_create(dev, size, false); | |
| 154 | 153 | } |
| 155 | 154 | EXPORT_SYMBOL_GPL(drm_gem_shmem_create); |
| 156 | 155 | |
| 157 | −/** | |
| 158 | − * drm_gem_shmem_create_with_mnt - Allocate an object with the given size in a | |
| 159 | − * given mountpoint | |
| 160 | − * @dev: DRM device | |
| 161 | − * @size: Size of the object to allocate | |
| 162 | − * @gemfs: tmpfs mount where the GEM object will be created | |
| 163 | − * | |
| 164 | − * This function creates a shmem GEM object in a given tmpfs mountpoint. | |
| 165 | − * | |
| 166 | − * Returns: | |
| 167 | − * A struct drm_gem_shmem_object * on success or an ERR_PTR()-encoded negative | |
| 168 | − * error code on failure. | |
| 169 | − */ | |
| 170 | −struct drm_gem_shmem_object *drm_gem_shmem_create_with_mnt(struct drm_device *dev, | |
| 171 | − size_t size, | |
| 172 | − struct vfsmount *gemfs) | |
| 173 | −{ | |
| 174 | − return __drm_gem_shmem_create(dev, size, false, gemfs); | |
| 175 | −} | |
| 176 | −EXPORT_SYMBOL_GPL(drm_gem_shmem_create_with_mnt); | |
| 177 | − | |
| 178 | 156 | /** |
| 179 | 157 | * drm_gem_shmem_release - Release resources associated with a shmem GEM object. |
| 180 | 158 | * @shmem: shmem GEM object |
| @@ -567,6 +545,26 @@ int drm_gem_shmem_dumb_create(struct drm_file *file, struct drm_device *dev, · 386 unmodified lines | ||
| 567 | 545 | } |
| 568 | 546 | EXPORT_SYMBOL_GPL(drm_gem_shmem_dumb_create); |
| 569 | 547 | |
| 548 | +static bool drm_gem_shmem_try_map_pmd(struct vm_fault *vmf, unsigned long addr, | |
| 549 | + struct page *page) | |
| 550 | +{ | |
| 551 | +#ifdef CONFIG_ARCH_SUPPORTS_PMD_PFNMAP | |
| 552 | + unsigned long pfn = page_to_pfn(page); | |
| 553 | + unsigned long paddr = pfn << PAGE_SHIFT; | |
| 554 | + bool aligned = (addr & ~PMD_MASK) == (paddr & ~PMD_MASK); | |
| 555 | + | |
| 556 | + if (aligned && | |
| 557 | + pmd_none(*vmf->pmd) && | |
| 558 | + folio_test_pmd_mappable(page_folio(page))) { | |
| 559 | + pfn &= PMD_MASK >> PAGE_SHIFT; | |
| 560 | + if (vmf_insert_pfn_pmd(vmf, pfn, false) == VM_FAULT_NOPAGE) | |
| 561 | + return true; | |
| 562 | + } | |
| 563 | +#endif | |
| 564 | + | |
| 565 | + return false; | |
| 566 | +} | |
| 567 | + | |
| 570 | 568 | static vm_fault_t drm_gem_shmem_fault(struct vm_fault *vmf) |
| 571 | 569 | { |
| 572 | 570 | struct vm_area_struct *vma = vmf->vma; |
| @@ -574,11 +572,12 @@ static vm_fault_t drm_gem_shmem_fault(struct vm_fault *vmf) · 1 unmodified line | ||
| 574 | 572 | struct drm_gem_shmem_object *shmem = to_drm_gem_shmem_obj(obj); |
| 575 | 573 | loff_t num_pages = obj->size >> PAGE_SHIFT; |
| 576 | 574 | vm_fault_t ret; |
| 577 | − struct page *page; | |
| 575 | + struct page **pages = shmem->pages; | |
| 578 | 576 | pgoff_t page_offset; |
| 577 | + unsigned long pfn; | |
| 579 | 578 | |
| 580 | − /* We don't use vmf->pgoff since that has the fake offset */ | |
| 581 | − page_offset = (vmf->address - vma->vm_start) >> PAGE_SHIFT; | |
| 579 | + /* Offset to faulty address in the VMA. */ | |
| 580 | + page_offset = vmf->pgoff - vma->vm_pgoff; | |
| 582 | 581 | |
| 583 | 582 | dma_resv_lock(shmem->base.resv, NULL); |
| 584 | 583 | |
| @@ -586,12 +585,18 @@ static vm_fault_t drm_gem_shmem_fault(struct vm_fault *vmf) · 1 unmodified line | ||
| 586 | 585 | drm_WARN_ON_ONCE(obj->dev, !shmem->pages) || |
| 587 | 586 | shmem->madv < 0) { |
| 588 | 587 | ret = VM_FAULT_SIGBUS; |
| 589 | − } else { | |
| 590 | − page = shmem->pages[page_offset]; | |
| 588 | + goto out; | |
| 589 | + } | |
| 591 | 590 | |
| 592 | − ret = vmf_insert_pfn(vma, vmf->address, page_to_pfn(page)); | |
| 591 | + if (drm_gem_shmem_try_map_pmd(vmf, vmf->address, pages[page_offset])) { | |
| 592 | + ret = VM_FAULT_NOPAGE; | |
| 593 | + goto out; | |
| 593 | 594 | } |
| 594 | 595 | |
| 596 | + pfn = page_to_pfn(pages[page_offset]); | |
| 597 | + ret = vmf_insert_pfn(vma, vmf->address, pfn); | |
| 598 | + | |
| 599 | + out: | |
| 595 | 600 | dma_resv_unlock(shmem->base.resv); |
| 596 | 601 | |
| 597 | 602 | return ret; |
| @@ -824,7 +829,7 @@ drm_gem_shmem_prime_import_sg_table(struct drm_device *dev, · 226 unmodified lines | ||
| 824 | 829 | size_t size = PAGE_ALIGN(attach->dmabuf->size); |
| 825 | 830 | struct drm_gem_shmem_object *shmem; |
| 826 | 831 | |
| 827 | − shmem = __drm_gem_shmem_create(dev, size, true, NULL); | |
| 832 | + shmem = __drm_gem_shmem_create(dev, size, true); | |
| 828 | 833 | if (IS_ERR(shmem)) |
| 829 | 834 | return ERR_CAST(shmem); |
| 830 | 835 | |
| @@ -872,7 +877,7 @@ struct drm_gem_object *drm_gem_shmem_prime_import_no_map(struct drm_device *dev, · 41 unmodified lines | ||
| 872 | 877 | |
| 873 | 878 | size = PAGE_ALIGN(attach->dmabuf->size); |
| 874 | 879 | |
| 875 | − shmem = __drm_gem_shmem_create(dev, size, true, NULL); | |
| 880 | + shmem = __drm_gem_shmem_create(dev, size, true); | |
| 876 | 881 | if (IS_ERR(shmem)) { |
| 877 | 882 | ret = PTR_ERR(shmem); |
| 878 | 883 | goto fail_detach; |
| @@ -39,12 +39,6 @@ MODULE_AUTHOR("Jocelyn Falempe"); · 38 unmodified lines | ||
| 39 | 39 | MODULE_DESCRIPTION("DRM panic handler"); |
| 40 | 40 | MODULE_LICENSE("GPL"); |
| 41 | 41 | |
| 42 | −static char drm_panic_screen[16] = CONFIG_DRM_PANIC_SCREEN; | |
| 43 | −module_param_string(panic_screen, drm_panic_screen, sizeof(drm_panic_screen), 0644); | |
| 44 | −MODULE_PARM_DESC(panic_screen, | |
| 45 | − "Choose what will be displayed by drm_panic, 'user' or 'kmsg' [default=" | |
| 46 | − CONFIG_DRM_PANIC_SCREEN "]"); | |
| 47 | − | |
| 48 | 42 | /** |
| 49 | 43 | * DOC: overview |
| 50 | 44 | * |
| @@ -813,14 +807,59 @@ static void draw_panic_static_qr_code(struct drm_scanout_buffer *sb) · 762 unmodified lines | ||
| 813 | 807 | draw_panic_static_user(sb); |
| 814 | 808 | } |
| 815 | 809 | #else |
| 816 | −static void draw_panic_static_qr_code(struct drm_scanout_buffer *sb) | |
| 810 | +static void drm_panic_qr_init(void) {}; | |
| 811 | +static void drm_panic_qr_exit(void) {}; | |
| 812 | +#endif | |
| 813 | + | |
| 814 | +enum drm_panic_type { | |
| 815 | + DRM_PANIC_TYPE_KMSG, | |
| 816 | + DRM_PANIC_TYPE_USER, | |
| 817 | + DRM_PANIC_TYPE_QR, | |
| 818 | +}; | |
| 819 | + | |
| 820 | +static enum drm_panic_type drm_panic_type = -1; | |
| 821 | + | |
| 822 | +static const char *drm_panic_type_map[] = { | |
| 823 | + [DRM_PANIC_TYPE_KMSG] = "kmsg", | |
| 824 | + [DRM_PANIC_TYPE_USER] = "user", | |
| 825 | +#if IS_ENABLED(CONFIG_DRM_PANIC_SCREEN_QR_CODE) | |
| 826 | + [DRM_PANIC_TYPE_QR] = "qr", | |
| 827 | +#endif | |
| 828 | +}; | |
| 829 | + | |
| 830 | +static int drm_panic_type_set(const char *val, const struct kernel_param *kp) | |
| 817 | 831 | { |
| 818 | − draw_panic_static_user(sb); | |
| 832 | + unsigned int i; | |
| 833 | + | |
| 834 | + for (i = 0; i < ARRAY_SIZE(drm_panic_type_map); i++) { | |
| 835 | + if (!strcmp(val, drm_panic_type_map[i])) { | |
| 836 | + drm_panic_type = i; | |
| 837 | + return 0; | |
| 838 | + } | |
| 839 | + } | |
| 840 | + | |
| 841 | + return -EINVAL; | |
| 819 | 842 | } |
| 820 | 843 | |
| 821 | −static void drm_panic_qr_init(void) {}; | |
| 822 | −static void drm_panic_qr_exit(void) {}; | |
| 844 | +static int drm_panic_type_get(char *buffer, const struct kernel_param *kp) | |
| 845 | +{ | |
| 846 | + return scnprintf(buffer, PAGE_SIZE, "%s\n", | |
| 847 | + drm_panic_type_map[drm_panic_type]); | |
| 848 | +} | |
| 849 | + | |
| 850 | +static const struct kernel_param_ops drm_panic_ops = { | |
| 851 | + .set = drm_panic_type_set, | |
| 852 | + .get = drm_panic_type_get, | |
| 853 | +}; | |
| 854 | + | |
| 855 | +module_param_cb(panic_screen, &drm_panic_ops, NULL, 0644); | |
| 856 | +MODULE_PARM_DESC(panic_screen, | |
| 857 | +#if IS_ENABLED(CONFIG_DRM_PANIC_SCREEN_QR_CODE) | |
| 858 | + "Choose what will be displayed by drm_panic, 'user', 'kmsg' or 'qr' [default=" | |
| 859 | +#else | |
| 860 | + "Choose what will be displayed by drm_panic, 'user' or 'kmsg' [default=" | |
| 823 | 861 | #endif |
| 862 | + CONFIG_DRM_PANIC_SCREEN "]"); | |
| 824 | 863 | |
| 825 | 864 | /* |
| 826 | 865 | * drm_panic_is_format_supported() |
| @@ -838,11 +877,19 @@ static bool drm_panic_is_format_supported(const struct drm_format_info *format) · 11 unmodified lines | ||
| 838 | 877 | |
| 839 | 878 | static void draw_panic_dispatch(struct drm_scanout_buffer *sb) |
| 840 | 879 | { |
| 841 | − if (!strcmp(drm_panic_screen, "kmsg")) { | |
| 880 | + switch (drm_panic_type) { | |
| 881 | + case DRM_PANIC_TYPE_KMSG: | |
| 842 | 882 | draw_panic_static_kmsg(sb); |
| 843 | − } else if (!strcmp(drm_panic_screen, "qr_code")) { | |
| 883 | + break; | |
| 884 | + | |
| 885 | +#if IS_ENABLED(CONFIG_DRM_PANIC_SCREEN_QR_CODE) | |
| 886 | + case DRM_PANIC_TYPE_QR: | |
| 844 | 887 | draw_panic_static_qr_code(sb); |
| 845 | − } else { | |
| 888 | + break; | |
| 889 | +#endif | |
| 890 | + | |
| 891 | + case DRM_PANIC_TYPE_USER: | |
| 892 | + default: | |
| 846 | 893 | draw_panic_static_user(sb); |
| 847 | 894 | } |
| 848 | 895 | } |
| @@ -1025,6 +1072,8 @@ void drm_panic_unregister(struct drm_device *dev) · 176 unmodified lines | ||
| 1025 | 1072 | */ |
| 1026 | 1073 | void __init drm_panic_init(void) |
| 1027 | 1074 | { |
| 1075 | + if (drm_panic_type == -1) | |
| 1076 | + drm_panic_type_set(CONFIG_DRM_PANIC_SCREEN, NULL); | |
| 1028 | 1077 | drm_panic_qr_init(); |
| 1029 | 1078 | } |
| 1030 | 1079 | |
| @@ -1867,9 +1867,9 @@ int drm_plane_create_color_pipeline_property(struct drm_plane *plane, · 1866 unmodified lines | ||
| 1867 | 1867 | prop = drm_property_create_enum(plane->dev, DRM_MODE_PROP_ATOMIC, |
| 1868 | 1868 | "COLOR_PIPELINE", |
| 1869 | 1869 | all_pipelines, len); |
| 1870 | − if (IS_ERR(prop)) { | |
| 1870 | + if (!prop) { | |
| 1871 | 1871 | kfree(all_pipelines); |
| 1872 | − return PTR_ERR(prop); | |
| 1872 | + return -ENOMEM; | |
| 1873 | 1873 | } |
| 1874 | 1874 | |
| 1875 | 1875 | drm_object_attach_property(&plane->base, prop, 0); |
| @@ -551,7 +551,7 @@ int drm_vblank_init(struct drm_device *dev, unsigned int num_crtcs) · 550 unmodified lines | ||
| 551 | 551 | dev->num_crtcs = num_crtcs; |
| 552 | 552 | |
| 553 | 553 | for (i = 0; i < num_crtcs; i++) { |
| 554 | − struct drm_vblank_crtc *vblank = &dev->vblank[i]; | |
| 554 | + struct drm_vblank_crtc *vblank = drm_vblank_crtc(dev, i); | |
| 555 | 555 | |
| 556 | 556 | vblank->dev = dev; |
| 557 | 557 | vblank->pipe = i; |
| @@ -605,7 +605,9 @@ EXPORT_SYMBOL(drm_dev_has_vblank); · 47 unmodified lines | ||
| 605 | 605 | */ |
| 606 | 606 | wait_queue_head_t *drm_crtc_vblank_waitqueue(struct drm_crtc *crtc) |
| 607 | 607 | { |
| 608 | − return &crtc->dev->vblank[drm_crtc_index(crtc)].queue; | |
| 608 | + struct drm_vblank_crtc *vblank = drm_crtc_vblank_crtc(crtc); | |
| 609 | + | |
| 610 | + return &vblank->queue; | |
| 609 | 611 | } |
| 610 | 612 | EXPORT_SYMBOL(drm_crtc_vblank_waitqueue); |
| 611 | 613 | |
| @@ -710,7 +712,6 @@ drm_crtc_vblank_helper_get_vblank_timestamp_internal( · 98 unmodified lines | ||
| 710 | 712 | { |
| 711 | 713 | struct drm_device *dev = crtc->dev; |
| 712 | 714 | unsigned int pipe = crtc->index; |
| 713 | − struct drm_vblank_crtc *vblank = &dev->vblank[pipe]; | |
| 714 | 715 | struct timespec64 ts_etime, ts_vblank_time; |
| 715 | 716 | ktime_t stime, etime; |
| 716 | 717 | bool vbl_status; |
| @@ -729,10 +730,13 @@ drm_crtc_vblank_helper_get_vblank_timestamp_internal( · 12 unmodified lines | ||
| 729 | 730 | return false; |
| 730 | 731 | } |
| 731 | 732 | |
| 732 | − if (drm_drv_uses_atomic_modeset(dev)) | |
| 733 | + if (drm_drv_uses_atomic_modeset(dev)) { | |
| 734 | + struct drm_vblank_crtc *vblank = drm_crtc_vblank_crtc(crtc); | |
| 735 | + | |
| 733 | 736 | mode = &vblank->hwmode; |
| 734 | − else | |
| 737 | + } else { | |
| 735 | 738 | mode = &crtc->hwmode; |
| 739 | + } | |
| 736 | 740 | |
| 737 | 741 | /* If mode timing undefined, just return as no-op: |
| 738 | 742 | * Happens during initial modesetting of a crtc. |
| @@ -1285,29 +1289,27 @@ void drm_crtc_vblank_put(struct drm_crtc *crtc) · 546 unmodified lines | ||
| 1285 | 1289 | EXPORT_SYMBOL(drm_crtc_vblank_put); |
| 1286 | 1290 | |
| 1287 | 1291 | /** |
| 1288 | − * drm_wait_one_vblank - wait for one vblank | |
| 1289 | − * @dev: DRM device | |
| 1290 | − * @pipe: CRTC index | |
| 1292 | + * drm_crtc_wait_one_vblank - wait for one vblank | |
| 1293 | + * @crtc: DRM crtc | |
| 1291 | 1294 | * |
| 1292 | − * This waits for one vblank to pass on @pipe, using the irq driver interfaces. | |
| 1293 | − * It is a failure to call this when the vblank irq for @pipe is disabled, e.g. | |
| 1295 | + * This waits for one vblank to pass on @crtc, using the irq driver interfaces. | |
| 1296 | + * It is a failure to call this when the vblank irq for @crtc is disabled, e.g. | |
| 1294 | 1297 | * due to lack of driver support or because the crtc is off. |
| 1295 | 1298 | * |
| 1296 | − * This is the legacy version of drm_crtc_wait_one_vblank(). | |
| 1299 | + * Returns: 0 on success, negative error on failures. | |
| 1297 | 1300 | */ |
| 1298 | −void drm_wait_one_vblank(struct drm_device *dev, unsigned int pipe) | |
| 1301 | +int drm_crtc_wait_one_vblank(struct drm_crtc *crtc) | |
| 1299 | 1302 | { |
| 1300 | − struct drm_vblank_crtc *vblank = drm_vblank_crtc(dev, pipe); | |
| 1303 | + struct drm_device *dev = crtc->dev; | |
| 1304 | + int pipe = drm_crtc_index(crtc); | |
| 1305 | + struct drm_vblank_crtc *vblank = drm_crtc_vblank_crtc(crtc); | |
| 1301 | 1306 | int ret; |
| 1302 | 1307 | u64 last; |
| 1303 | 1308 | |
| 1304 | − if (drm_WARN_ON(dev, pipe >= dev->num_crtcs)) | |
| 1305 | − return; | |
| 1306 | − | |
| 1307 | 1309 | ret = drm_vblank_get(dev, pipe); |
| 1308 | 1310 | if (drm_WARN(dev, ret, "vblank not available on crtc %i, ret=%i\n", |
| 1309 | 1311 | pipe, ret)) |
| 1310 | − return; | |
| 1312 | + return ret; | |
| 1311 | 1313 | |
| 1312 | 1314 | last = drm_vblank_count(dev, pipe); |
| 1313 | 1315 | |
| @@ -1318,20 +1320,8 @@ void drm_wait_one_vblank(struct drm_device *dev, unsigned int pipe) · 4 unmodified lines | ||
| 1318 | 1320 | drm_WARN(dev, ret == 0, "vblank wait timed out on crtc %i\n", pipe); |
| 1319 | 1321 | |
| 1320 | 1322 | drm_vblank_put(dev, pipe); |
| 1321 | −} | |
| 1322 | −EXPORT_SYMBOL(drm_wait_one_vblank); | |
| 1323 | 1323 | |
| 1324 | −/** | |
| 1325 | − * drm_crtc_wait_one_vblank - wait for one vblank | |
| 1326 | − * @crtc: DRM crtc | |
| 1327 | − * | |
| 1328 | − * This waits for one vblank to pass on @crtc, using the irq driver interfaces. | |
| 1329 | − * It is a failure to call this when the vblank irq for @crtc is disabled, e.g. | |
| 1330 | − * due to lack of driver support or because the crtc is off. | |
| 1331 | − */ | |
| 1332 | −void drm_crtc_wait_one_vblank(struct drm_crtc *crtc) | |
| 1333 | −{ | |
| 1334 | − drm_wait_one_vblank(crtc->dev, drm_crtc_index(crtc)); | |
| 1324 | + return ret ? 0 : -ETIMEDOUT; | |
| 1335 | 1325 | } |
| 1336 | 1326 | EXPORT_SYMBOL(drm_crtc_wait_one_vblank); |
| 1337 | 1327 | |
| @@ -1794,7 +1784,7 @@ int drm_wait_vblank_ioctl(struct drm_device *dev, void *data, · 456 unmodified lines | ||
| 1794 | 1784 | if (pipe >= dev->num_crtcs) |
| 1795 | 1785 | return -EINVAL; |
| 1796 | 1786 | |
| 1797 | − vblank = &dev->vblank[pipe]; | |
| 1787 | + vblank = drm_vblank_crtc(dev, pipe); | |
| 1798 | 1788 | |
| 1799 | 1789 | /* If the counter is currently enabled and accurate, short-circuit |
| 1800 | 1790 | * queries to return the cached timestamp of the last vblank. |
| @@ -0,0 +1,14 @@ | ||
| 1 | +# SPDX-License-Identifier: GPL-2.0-only | |
| 2 | +config DRM_HYPERV | |
| 3 | + tristate "DRM Support for Hyper-V synthetic video device" | |
| 4 | + depends on DRM && PCI && HYPERV_VMBUS | |
| 5 | + select DRM_CLIENT_SELECTION | |
| 6 | + select DRM_KMS_HELPER | |
| 7 | + select DRM_GEM_SHMEM_HELPER | |
| 8 | + help | |
| 9 | + This is a KMS driver for Hyper-V synthetic video device. Choose this | |
| 10 | + option if you would like to enable drm driver for Hyper-V virtual | |
| 11 | + machine. Unselect Hyper-V framebuffer driver (CONFIG_FB_HYPERV) so | |
| 12 | + that DRM driver is used by default. | |
| 13 | + | |
| 14 | + If M is selected the module will be called hyperv_drm. | |
| @@ -170,8 +170,7 @@ gem-y += \ · 169 unmodified lines | ||
| 170 | 170 | gem/i915_gem_ttm_move.o \ |
| 171 | 171 | gem/i915_gem_ttm_pm.o \ |
| 172 | 172 | gem/i915_gem_userptr.o \ |
| 173 | − gem/i915_gem_wait.o \ | |
| 174 | − gem/i915_gemfs.o | |
| 173 | + gem/i915_gem_wait.o | |
| 175 | 174 | i915-y += \ |
| 176 | 175 | $(gem-y) \ |
| 177 | 176 | i915_active.o \ |
| @@ -348,12 +348,13 @@ struct drm_i915_gem_object { · 347 unmodified lines | ||
| 348 | 348 | */ |
| 349 | 349 | #define I915_BO_ALLOC_GPU_ONLY BIT(6) |
| 350 | 350 | #define I915_BO_ALLOC_CCS_AUX BIT(7) |
| 351 | +#define I915_BO_ALLOC_NOTHP BIT(8) | |
| 351 | 352 | /* |
| 352 | 353 | * Object is allowed to retain its initial data and will not be cleared on first |
| 353 | 354 | * access if used along with I915_BO_ALLOC_USER. This is mainly to keep |
| 354 | 355 | * preallocated framebuffer data intact while transitioning it to i915drmfb. |
| 355 | 356 | */ |
| 356 | −#define I915_BO_PREALLOC BIT(8) | |
| 357 | +#define I915_BO_PREALLOC BIT(9) | |
| 357 | 358 | #define I915_BO_ALLOC_FLAGS (I915_BO_ALLOC_CONTIGUOUS | \ |
| 358 | 359 | I915_BO_ALLOC_VOLATILE | \ |
| 359 | 360 | I915_BO_ALLOC_CPU_CLEAR | \ |
| @@ -362,10 +363,11 @@ struct drm_i915_gem_object { · 2 unmodified lines | ||
| 362 | 363 | I915_BO_ALLOC_PM_EARLY | \ |
| 363 | 364 | I915_BO_ALLOC_GPU_ONLY | \ |
| 364 | 365 | I915_BO_ALLOC_CCS_AUX | \ |
| 366 | + I915_BO_ALLOC_NOTHP | \ | |
| 365 | 367 | I915_BO_PREALLOC) |
| 366 | −#define I915_BO_READONLY BIT(9) | |
| 367 | −#define I915_TILING_QUIRK_BIT 10 /* unknown swizzling; do not release! */ | |
| 368 | −#define I915_BO_PROTECTED BIT(11) | |
| 368 | +#define I915_BO_READONLY BIT(10) | |
| 369 | +#define I915_TILING_QUIRK_BIT 11 /* unknown swizzling; do not release! */ | |
| 370 | +#define I915_BO_PROTECTED BIT(12) | |
| 369 | 371 | /** |
| 370 | 372 | * @mem_flags - Mutable placement-related flags |
| 371 | 373 | * |
| @@ -9,14 +9,16 @@ · 8 unmodified lines | ||
| 9 | 9 | #include <linux/uio.h> |
| 10 | 10 | |
| 11 | 11 | #include <drm/drm_cache.h> |
| 12 | +#include <drm/drm_gem.h> | |
| 13 | +#include <drm/drm_print.h> | |
| 12 | 14 | |
| 13 | 15 | #include "gem/i915_gem_region.h" |
| 14 | 16 | #include "i915_drv.h" |
| 15 | 17 | #include "i915_gem_object.h" |
| 16 | 18 | #include "i915_gem_tiling.h" |
| 17 | −#include "i915_gemfs.h" | |
| 18 | 19 | #include "i915_scatterlist.h" |
| 19 | 20 | #include "i915_trace.h" |
| 21 | +#include "i915_utils.h" | |
| 20 | 22 | |
| 21 | 23 | /* |
| 22 | 24 | * Move folios to appropriate lru and release the batch, decrementing the |
| @@ -494,9 +496,11 @@ const struct drm_i915_gem_object_ops i915_gem_shmem_ops = { · 471 unmodified lines | ||
| 494 | 496 | |
| 495 | 497 | static int __create_shmem(struct drm_i915_private *i915, |
| 496 | 498 | struct drm_gem_object *obj, |
| 497 | − resource_size_t size) | |
| 499 | + resource_size_t size, | |
| 500 | + unsigned int flags) | |
| 498 | 501 | { |
| 499 | − unsigned long flags = VM_NORESERVE; | |
| 502 | + unsigned long shmem_flags = VM_NORESERVE; | |
| 503 | + struct vfsmount *huge_mnt; | |
| 500 | 504 | struct file *filp; |
| 501 | 505 | |
| 502 | 506 | drm_gem_private_object_init(&i915->drm, obj, size); |
| @@ -515,11 +519,12 @@ static int __create_shmem(struct drm_i915_private *i915, · 12 unmodified lines | ||
| 515 | 519 | if (BITS_PER_LONG == 64 && size > MAX_LFS_FILESIZE) |
| 516 | 520 | return -E2BIG; |
| 517 | 521 | |
| 518 | − if (i915->mm.gemfs) | |
| 519 | − filp = shmem_file_setup_with_mnt(i915->mm.gemfs, "i915", size, | |
| 520 | − flags); | |
| 522 | + huge_mnt = drm_gem_get_huge_mnt(&i915->drm); | |
| 523 | + if (!(flags & I915_BO_ALLOC_NOTHP) && huge_mnt) | |
| 524 | + filp = shmem_file_setup_with_mnt(huge_mnt, "i915", size, | |
| 525 | + shmem_flags); | |
| 521 | 526 | else |
| 522 | − filp = shmem_file_setup("i915", size, flags); | |
| 527 | + filp = shmem_file_setup("i915", size, shmem_flags); | |
| 523 | 528 | if (IS_ERR(filp)) |
| 524 | 529 | return PTR_ERR(filp); |
| 525 | 530 | |
| @@ -548,7 +553,7 @@ static int shmem_object_init(struct intel_memory_region *mem, · 22 unmodified lines | ||
| 548 | 553 | gfp_t mask; |
| 549 | 554 | int ret; |
| 550 | 555 | |
| 551 | − ret = __create_shmem(i915, &obj->base, size); | |
| 556 | + ret = __create_shmem(i915, &obj->base, size, flags); | |
| 552 | 557 | if (ret) |
| 553 | 558 | return ret; |
| 554 | 559 | |
| @@ -644,21 +649,40 @@ fail: · 89 unmodified lines | ||
| 644 | 649 | |
| 645 | 650 | static int init_shmem(struct intel_memory_region *mem) |
| 646 | 651 | { |
| 647 | − i915_gemfs_init(mem->i915); | |
| 648 | − intel_memory_region_set_name(mem, "system"); | |
| 652 | + struct drm_i915_private *i915 = mem->i915; | |
| 649 | 653 | |
| 650 | − return 0; /* We have fallback to the kernel mnt if gemfs init failed. */ | |
| 651 | −} | |
| 654 | + /* | |
| 655 | + * By creating our own shmemfs mountpoint, we can pass in | |
| 656 | + * mount flags that better match our usecase. | |
| 657 | + * | |
| 658 | + * One example, although it is probably better with a per-file | |
| 659 | + * control, is selecting huge page allocations ("huge=within_size"). | |
| 660 | + * However, we only do so on platforms which benefit from it, or to | |
| 661 | + * offset the overhead of iommu lookups, where with latter it is a net | |
| 662 | + * win even on platforms which would otherwise see some performance | |
| 663 | + * regressions such a slow reads issue on Broadwell and Skylake. | |
| 664 | + */ | |
| 652 | 665 | |
| 653 | −static int release_shmem(struct intel_memory_region *mem) | |
| 654 | −{ | |
| 655 | − i915_gemfs_fini(mem->i915); | |
| 656 | − return 0; | |
| 666 | + if (GRAPHICS_VER(i915) < 11 && !i915_vtd_active(i915)) | |
| 667 | + goto no_thp; | |
| 668 | + | |
| 669 | + drm_gem_huge_mnt_create(&i915->drm, "within_size"); | |
| 670 | + if (drm_gem_get_huge_mnt(&i915->drm)) | |
| 671 | + drm_info(&i915->drm, "Using Transparent Hugepages\n"); | |
| 672 | + else | |
| 673 | + drm_notice(&i915->drm, | |
| 674 | + "Transparent Hugepage support is recommended for optimal performance%s\n", | |
| 675 | + GRAPHICS_VER(i915) >= 11 ? " on this platform!" : | |
| 676 | + " when IOMMU is enabled!"); | |
| 677 | + | |
| 678 | + no_thp: | |
| 679 | + intel_memory_region_set_name(mem, "system"); | |
| 680 | + | |
| 681 | + return 0; /* We have fallback to the kernel mnt if huge mnt failed. */ | |
| 657 | 682 | } |
| 658 | 683 | |
| 659 | 684 | static const struct intel_memory_region_ops shmem_region_ops = { |
| 660 | 685 | .init = init_shmem, |
| 661 | − .release = release_shmem, | |
| 662 | 686 | .init_object = shmem_object_init, |
| 663 | 687 | }; |
| 664 | 688 | |
| @@ -1,71 +0,0 @@ | ||
| 1 | −// SPDX-License-Identifier: MIT | |
| 2 | −/* | |
| 3 | − * Copyright © 2017 Intel Corporation | |
| 4 | − */ | |
| 5 | − | |
| 6 | −#include <linux/fs.h> | |
| 7 | −#include <linux/mount.h> | |
| 8 | −#include <linux/fs_context.h> | |
| 9 | − | |
| 10 | −#include <drm/drm_print.h> | |
| 11 | − | |
| 12 | −#include "i915_drv.h" | |
| 13 | −#include "i915_gemfs.h" | |
| 14 | −#include "i915_utils.h" | |
| 15 | − | |
| 16 | −void i915_gemfs_init(struct drm_i915_private *i915) | |
| 17 | −{ | |
| 18 | − struct file_system_type *type; | |
| 19 | − struct fs_context *fc; | |
| 20 | − struct vfsmount *gemfs; | |
| 21 | − int ret; | |
| 22 | − | |
| 23 | − /* | |
| 24 | − * By creating our own shmemfs mountpoint, we can pass in | |
| 25 | − * mount flags that better match our usecase. | |
| 26 | − * | |
| 27 | − * One example, although it is probably better with a per-file | |
| 28 | − * control, is selecting huge page allocations ("huge=within_size"). | |
| 29 | − * However, we only do so on platforms which benefit from it, or to | |
| 30 | − * offset the overhead of iommu lookups, where with latter it is a net | |
| 31 | − * win even on platforms which would otherwise see some performance | |
| 32 | − * regressions such a slow reads issue on Broadwell and Skylake. | |
| 33 | − */ | |
| 34 | − | |
| 35 | − if (GRAPHICS_VER(i915) < 11 && !i915_vtd_active(i915)) | |
| 36 | − return; | |
| 37 | − | |
| 38 | − if (!IS_ENABLED(CONFIG_TRANSPARENT_HUGEPAGE)) | |
| 39 | − goto err; | |
| 40 | − | |
| 41 | − type = get_fs_type("tmpfs"); | |
| 42 | − if (!type) | |
| 43 | − goto err; | |
| 44 | − | |
| 45 | − fc = fs_context_for_mount(type, SB_KERNMOUNT); | |
| 46 | − if (IS_ERR(fc)) | |
| 47 | − goto err; | |
| 48 | − ret = vfs_parse_fs_string(fc, "source", "tmpfs"); | |
| 49 | − if (!ret) | |
| 50 | − ret = vfs_parse_fs_string(fc, "huge", "within_size"); | |
| 51 | − if (!ret) | |
| 52 | − gemfs = fc_mount_longterm(fc); | |
| 53 | − put_fs_context(fc); | |
| 54 | − if (ret) | |
| 55 | − goto err; | |
| 56 | − | |
| 57 | − i915->mm.gemfs = gemfs; | |
| 58 | − drm_info(&i915->drm, "Using Transparent Hugepages\n"); | |
| 59 | − return; | |
| 60 | − | |
| 61 | −err: | |
| 62 | − drm_notice(&i915->drm, | |
| 63 | − "Transparent Hugepage support is recommended for optimal performance%s\n", | |
| 64 | − GRAPHICS_VER(i915) >= 11 ? " on this platform!" : | |
| 65 | − " when IOMMU is enabled!"); | |
| 66 | −} | |
| 67 | − | |
| 68 | −void i915_gemfs_fini(struct drm_i915_private *i915) | |
| 69 | −{ | |
| 70 | − kern_unmount(i915->mm.gemfs); | |
| 71 | −} | |
| @@ -1,14 +0,0 @@ | ||
| 1 | −/* SPDX-License-Identifier: MIT */ | |
| 2 | −/* | |
| 3 | − * Copyright © 2017 Intel Corporation | |
| 4 | − */ | |
| 5 | − | |
| 6 | −#ifndef __I915_GEMFS_H__ | |
| 7 | −#define __I915_GEMFS_H__ | |
| 8 | − | |
| 9 | −struct drm_i915_private; | |
| 10 | − | |
| 11 | −void i915_gemfs_init(struct drm_i915_private *i915); | |
| 12 | −void i915_gemfs_fini(struct drm_i915_private *i915); | |
| 13 | − | |
| 14 | −#endif | |
| @@ -1316,7 +1316,7 @@ typedef struct drm_i915_gem_object * · 1315 unmodified lines | ||
| 1316 | 1316 | |
| 1317 | 1317 | static inline bool igt_can_allocate_thp(struct drm_i915_private *i915) |
| 1318 | 1318 | { |
| 1319 | − return i915->mm.gemfs && has_transparent_hugepage(); | |
| 1319 | + return !!drm_gem_get_huge_mnt(&i915->drm); | |
| 1320 | 1320 | } |
| 1321 | 1321 | |
| 1322 | 1322 | static struct drm_i915_gem_object * |
| @@ -1761,7 +1761,6 @@ static int igt_tmpfs_fallback(void *arg) · 438 unmodified lines | ||
| 1761 | 1761 | struct drm_i915_private *i915 = arg; |
| 1762 | 1762 | struct i915_address_space *vm; |
| 1763 | 1763 | struct i915_gem_context *ctx; |
| 1764 | − struct vfsmount *gemfs = i915->mm.gemfs; | |
| 1765 | 1764 | struct drm_i915_gem_object *obj; |
| 1766 | 1765 | struct i915_vma *vma; |
| 1767 | 1766 | struct file *file; |
| @@ -1779,15 +1778,8 @@ static int igt_tmpfs_fallback(void *arg) · 11 unmodified lines | ||
| 1779 | 1778 | } |
| 1780 | 1779 | vm = i915_gem_context_get_eb_vm(ctx); |
| 1781 | 1780 | |
| 1782 | − /* | |
| 1783 | − * Make sure that we don't burst into a ball of flames upon falling back | |
| 1784 | − * to tmpfs, which we rely on if on the off-chance we encounter a failure | |
| 1785 | − * when setting up gemfs. | |
| 1786 | − */ | |
| 1787 | − | |
| 1788 | − i915->mm.gemfs = NULL; | |
| 1789 | − | |
| 1790 | − obj = i915_gem_object_create_shmem(i915, PAGE_SIZE); | |
| 1781 | + obj = i915_gem_object_create_region(i915->mm.regions[INTEL_REGION_SMEM], | |
| 1782 | + PAGE_SIZE, 0, I915_BO_ALLOC_NOTHP); | |
| 1791 | 1783 | if (IS_ERR(obj)) { |
| 1792 | 1784 | err = PTR_ERR(obj); |
| 1793 | 1785 | goto out_restore; |
| @@ -1819,7 +1811,6 @@ static int igt_tmpfs_fallback(void *arg) · 25 unmodified lines | ||
| 1819 | 1811 | out_put: |
| 1820 | 1812 | i915_gem_object_put(obj); |
| 1821 | 1813 | out_restore: |
| 1822 | − i915->mm.gemfs = gemfs; | |
| 1823 | 1814 | |
| 1824 | 1815 | i915_vm_put(vm); |
| 1825 | 1816 | out: |
| @@ -141,11 +141,6 @@ struct i915_gem_mm { · 140 unmodified lines | ||
| 141 | 141 | */ |
| 142 | 142 | atomic_t free_count; |
| 143 | 143 | |
| 144 | − /** | |
| 145 | − * tmpfs instance used for shmem backed objects | |
| 146 | − */ | |
| 147 | − struct vfsmount *gemfs; | |
| 148 | − | |
| 149 | 144 | struct intel_memory_region *regions[INTEL_REGION_UNKNOWN]; |
| 150 | 145 | |
| 151 | 146 | struct notifier_block oom_notifier; |
| @@ -837,7 +837,7 @@ nv_crtc_gamma_set(struct drm_crtc *crtc, u16 *r, u16 *g, u16 *b, · 836 unmodified lines | ||
| 837 | 837 | static int |
| 838 | 838 | nv04_crtc_do_mode_set_base(struct drm_crtc *crtc, |
| 839 | 839 | struct drm_framebuffer *passed_fb, |
| 840 | − int x, int y, bool atomic) | |
| 840 | + int x, int y) | |
| 841 | 841 | { |
| 842 | 842 | struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); |
| 843 | 843 | struct drm_device *dev = crtc->dev; |
| @@ -850,19 +850,12 @@ nv04_crtc_do_mode_set_base(struct drm_crtc *crtc, · 6 unmodified lines | ||
| 850 | 850 | NV_DEBUG(drm, "index %d\n", nv_crtc->index); |
| 851 | 851 | |
| 852 | 852 | /* no fb bound */ |
| 853 | − if (!atomic && !crtc->primary->fb) { | |
| 853 | + if (!crtc->primary->fb) { | |
| 854 | 854 | NV_DEBUG(drm, "No FB bound\n"); |
| 855 | 855 | return 0; |
| 856 | 856 | } |
| 857 | 857 | |
| 858 | − /* If atomic, we want to switch to the fb we were passed, so | |
| 859 | − * now we update pointers to do that. | |
| 860 | − */ | |
| 861 | − if (atomic) { | |
| 862 | − drm_fb = passed_fb; | |
| 863 | − } else { | |
| 864 | − drm_fb = crtc->primary->fb; | |
| 865 | − } | |
| 858 | + drm_fb = crtc->primary->fb; | |
| 866 | 859 | |
| 867 | 860 | nvbo = nouveau_gem_object(drm_fb->obj[0]); |
| 868 | 861 | nv_crtc->fb.offset = nvbo->offset; |
| @@ -920,15 +913,7 @@ nv04_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y, · 51 unmodified lines | ||
| 920 | 913 | int ret = nv_crtc_swap_fbs(crtc, old_fb); |
| 921 | 914 | if (ret) |
| 922 | 915 | return ret; |
| 923 | − return nv04_crtc_do_mode_set_base(crtc, old_fb, x, y, false); | |
| 924 | −} | |
| 925 | − | |
| 926 | −static int | |
| 927 | −nv04_crtc_mode_set_base_atomic(struct drm_crtc *crtc, | |
| 928 | − struct drm_framebuffer *fb, | |
| 929 | − int x, int y, enum mode_set_atomic state) | |
| 930 | −{ | |
| 931 | − return nv04_crtc_do_mode_set_base(crtc, fb, x, y, true); | |
| 916 | + return nv04_crtc_do_mode_set_base(crtc, old_fb, x, y); | |
| 932 | 917 | } |
| 933 | 918 | |
| 934 | 919 | static void nv04_cursor_upload(struct drm_device *dev, struct nouveau_bo *src, |
| @@ -1274,7 +1259,6 @@ static const struct drm_crtc_helper_funcs nv04_crtc_helper_funcs = { · 339 unmodified lines | ||
| 1274 | 1259 | .commit = nv_crtc_commit, |
| 1275 | 1260 | .mode_set = nv_crtc_mode_set, |
| 1276 | 1261 | .mode_set_base = nv04_crtc_mode_set_base, |
| 1277 | − .mode_set_base_atomic = nv04_crtc_mode_set_base_atomic, | |
| 1278 | 1262 | .disable = nv_crtc_disable, |
| 1279 | 1263 | .get_scanout_position = nouveau_display_scanoutpos, |
| 1280 | 1264 | }; |
| @@ -1880,6 +1880,7 @@ static const struct panel_delay delay_80_500_e50_d50 = { · 1879 unmodified lines | ||
| 1880 | 1880 | */ |
| 1881 | 1881 | static const struct edp_panel_entry edp_panels[] = { |
| 1882 | 1882 | EDP_PANEL_ENTRY('A', 'U', 'O', 0x04a4, &delay_200_500_e50, "B122UAN01.0"), |
| 1883 | + EDP_PANEL_ENTRY('A', 'U', 'O', 0x0ba4, &delay_200_500_e50, "B140QAX01.H"), | |
| 1883 | 1884 | EDP_PANEL_ENTRY('A', 'U', 'O', 0x105c, &delay_200_500_e50, "B116XTN01.0"), |
| 1884 | 1885 | EDP_PANEL_ENTRY('A', 'U', 'O', 0x1062, &delay_200_500_e50, "B120XAN01.0"), |
| 1885 | 1886 | EDP_PANEL_ENTRY('A', 'U', 'O', 0x125c, &delay_200_500_e50, "Unknown"), |
| @@ -1904,6 +1905,7 @@ static const struct edp_panel_entry edp_panels[] = { · 18 unmodified lines | ||
| 1904 | 1905 | EDP_PANEL_ENTRY('A', 'U', 'O', 0x615c, &delay_200_500_e50, "B116XAN06.1"), |
| 1905 | 1906 | EDP_PANEL_ENTRY('A', 'U', 'O', 0x635c, &delay_200_500_e50, "B116XAN06.3"), |
| 1906 | 1907 | EDP_PANEL_ENTRY('A', 'U', 'O', 0x639c, &delay_200_500_e50, "B140HAK02.7"), |
| 1908 | + EDP_PANEL_ENTRY('A', 'U', 'O', 0x643d, &delay_200_500_e50, "B140HAN06.4"), | |
| 1907 | 1909 | EDP_PANEL_ENTRY('A', 'U', 'O', 0x723c, &delay_200_500_e50, "B140XTN07.2"), |
| 1908 | 1910 | EDP_PANEL_ENTRY('A', 'U', 'O', 0x73aa, &delay_200_500_e50, "B116XTN02.3"), |
| 1909 | 1911 | EDP_PANEL_ENTRY('A', 'U', 'O', 0x8594, &delay_200_500_e50, "B133UAN01.0"), |
| @@ -2033,6 +2035,7 @@ static const struct edp_panel_entry edp_panels[] = { · 123 unmodified lines | ||
| 2033 | 2035 | EDP_PANEL_ENTRY('C', 'S', 'W', 0x1462, &delay_200_500_e50, "MNE007QS5-2"), |
| 2034 | 2036 | EDP_PANEL_ENTRY('C', 'S', 'W', 0x1468, &delay_200_500_e50, "MNE007QB2-2"), |
| 2035 | 2037 | EDP_PANEL_ENTRY('C', 'S', 'W', 0x146e, &delay_80_500_e50_d50, "MNE007QB3-1"), |
| 2038 | + EDP_PANEL_ENTRY('C', 'S', 'W', 0x147c, &delay_200_500_e50_d100, "MNE007QB3-1"), | |
| 2036 | 2039 | EDP_PANEL_ENTRY('C', 'S', 'W', 0x1519, &delay_200_500_e80_d50, "MNF601BS1-3"), |
| 2037 | 2040 | |
| 2038 | 2041 | EDP_PANEL_ENTRY('E', 'T', 'C', 0x0000, &delay_50_500_e200_d200_po2e335, "LP079QX1-SP0V"), |
| @@ -12,6 +12,7 @@ · 11 unmodified lines | ||
| 12 | 12 | #include "panfrost_device.h" |
| 13 | 13 | #include "panfrost_devfreq.h" |
| 14 | 14 | #include "panfrost_features.h" |
| 15 | +#include "panfrost_gem.h" | |
| 15 | 16 | #include "panfrost_issues.h" |
| 16 | 17 | #include "panfrost_gpu.h" |
| 17 | 18 | #include "panfrost_job.h" |
| @@ -267,6 +268,8 @@ int panfrost_device_init(struct panfrost_device *pfdev) · 249 unmodified lines | ||
| 267 | 268 | if (err) |
| 268 | 269 | goto out_job; |
| 269 | 270 | |
| 271 | + panfrost_gem_init(pfdev); | |
| 272 | + | |
| 270 | 273 | return 0; |
| 271 | 274 | out_job: |
| 272 | 275 | panfrost_jm_fini(pfdev); |
| @@ -79,6 +79,7 @@ struct panfrost_features { · 78 unmodified lines | ||
| 79 | 79 | u32 thread_max_workgroup_sz; |
| 80 | 80 | u32 thread_max_barrier_sz; |
| 81 | 81 | u32 coherency_features; |
| 82 | + u32 selected_coherency; | |
| 82 | 83 | u32 afbc_features; |
| 83 | 84 | u32 texture_features[4]; |
| 84 | 85 | u32 js_features[16]; |
| @@ -95,6 +95,7 @@ static int panfrost_ioctl_get_param(struct drm_device *ddev, void *data, struct · 94 unmodified lines | ||
| 95 | 95 | PANFROST_FEATURE_ARRAY(JS_FEATURES, js_features, 15); |
| 96 | 96 | PANFROST_FEATURE(NR_CORE_GROUPS, nr_core_groups); |
| 97 | 97 | PANFROST_FEATURE(THREAD_TLS_ALLOC, thread_tls_alloc); |
| 98 | + PANFROST_FEATURE(SELECTED_COHERENCY, selected_coherency); | |
| 98 | 99 | |
| 99 | 100 | case DRM_PANFROST_PARAM_SYSTEM_TIMESTAMP: |
| 100 | 101 | ret = panfrost_ioctl_query_timestamp(pfdev, ¶m->value); |
| @@ -125,6 +126,10 @@ static int panfrost_ioctl_get_param(struct drm_device *ddev, void *data, struct · 24 unmodified lines | ||
| 125 | 126 | return 0; |
| 126 | 127 | } |
| 127 | 128 | |
| 129 | +#define PANFROST_BO_FLAGS (PANFROST_BO_NOEXEC | \ | |
| 130 | + PANFROST_BO_HEAP | \ | |
| 131 | + PANFROST_BO_WB_MMAP) | |
| 132 | + | |
| 128 | 133 | static int panfrost_ioctl_create_bo(struct drm_device *dev, void *data, |
| 129 | 134 | struct drm_file *file) |
| 130 | 135 | { |
| @@ -134,8 +139,7 @@ static int panfrost_ioctl_create_bo(struct drm_device *dev, void *data, · 3 unmodified lines | ||
| 134 | 139 | struct panfrost_gem_mapping *mapping; |
| 135 | 140 | int ret; |
| 136 | 141 | |
| 137 | − if (!args->size || args->pad || | |
| 138 | − (args->flags & ~(PANFROST_BO_NOEXEC | PANFROST_BO_HEAP))) | |
| 142 | + if (!args->size || args->pad || (args->flags & ~PANFROST_BO_FLAGS)) | |
| 139 | 143 | return -EINVAL; |
| 140 | 144 | |
| 141 | 145 | /* Heaps should never be executable */ |
| @@ -579,6 +583,91 @@ static int panfrost_ioctl_jm_ctx_destroy(struct drm_device *dev, void *data, · 437 unmodified lines | ||
| 579 | 583 | return panfrost_jm_ctx_destroy(file, args->handle); |
| 580 | 584 | } |
| 581 | 585 | |
| 586 | +static int panfrost_ioctl_sync_bo(struct drm_device *ddev, void *data, | |
| 587 | + struct drm_file *file) | |
| 588 | +{ | |
| 589 | + struct drm_panfrost_sync_bo *args = data; | |
| 590 | + struct drm_panfrost_bo_sync_op *ops; | |
| 591 | + struct drm_gem_object *obj; | |
| 592 | + int ret; | |
| 593 | + u32 i; | |
| 594 | + | |
| 595 | + if (args->pad) | |
| 596 | + return -EINVAL; | |
| 597 | + | |
| 598 | + if (!args->op_count) | |
| 599 | + return 0; | |
| 600 | + | |
| 601 | + ops = kvmalloc_array(args->op_count, sizeof(*ops), GFP_KERNEL); | |
| 602 | + if (!ops) { | |
| 603 | + DRM_DEBUG("Failed to allocate incoming BO sync ops array\n"); | |
| 604 | + return -ENOMEM; | |
| 605 | + } | |
| 606 | + | |
| 607 | + if (copy_from_user(ops, (void __user *)(uintptr_t)args->ops, | |
| 608 | + args->op_count * sizeof(*ops))) { | |
| 609 | + DRM_DEBUG("Failed to copy in BO sync ops\n"); | |
| 610 | + ret = -EFAULT; | |
| 611 | + goto err_ops; | |
| 612 | + } | |
| 613 | + | |
| 614 | + for (i = 0; i < args->op_count; i++) { | |
| 615 | + obj = drm_gem_object_lookup(file, ops[i].handle); | |
| 616 | + if (!obj) { | |
| 617 | + ret = -ENOENT; | |
| 618 | + goto err_ops; | |
| 619 | + } | |
| 620 | + | |
| 621 | + ret = panfrost_gem_sync(obj, ops[i].type, | |
| 622 | + ops[i].offset, ops[i].size); | |
| 623 | + | |
| 624 | + drm_gem_object_put(obj); | |
| 625 | + | |
| 626 | + if (ret) | |
| 627 | + goto err_ops; | |
| 628 | + } | |
| 629 | + | |
| 630 | +err_ops: | |
| 631 | + kvfree(ops); | |
| 632 | + | |
| 633 | + return ret; | |
| 634 | +} | |
| 635 | + | |
| 636 | +static int panfrost_ioctl_query_bo_info(struct drm_device *dev, void *data, | |
| 637 | + struct drm_file *file_priv) | |
| 638 | +{ | |
| 639 | + struct drm_panfrost_query_bo_info *args = data; | |
| 640 | + struct drm_gem_object *gem_obj; | |
| 641 | + struct panfrost_gem_object *bo; | |
| 642 | + | |
| 643 | + gem_obj = drm_gem_object_lookup(file_priv, args->handle); | |
| 644 | + if (!gem_obj) { | |
| 645 | + DRM_DEBUG("Failed to look up GEM BO %d\n", args->handle); | |
| 646 | + return -ENOENT; | |
| 647 | + } | |
| 648 | + | |
| 649 | + bo = to_panfrost_bo(gem_obj); | |
| 650 | + args->pad = 0; | |
| 651 | + args->create_flags = 0; | |
| 652 | + args->extra_flags = 0; | |
| 653 | + | |
| 654 | + if (drm_gem_is_imported(gem_obj)) { | |
| 655 | + args->extra_flags |= DRM_PANFROST_BO_IS_IMPORTED; | |
| 656 | + } else { | |
| 657 | + if (bo->noexec) | |
| 658 | + args->create_flags |= PANFROST_BO_NOEXEC; | |
| 659 | + | |
| 660 | + if (bo->is_heap) | |
| 661 | + args->create_flags |= PANFROST_BO_HEAP; | |
| 662 | + | |
| 663 | + if (!bo->base.map_wc) | |
| 664 | + args->create_flags |= PANFROST_BO_WB_MMAP; | |
| 665 | + } | |
| 666 | + | |
| 667 | + drm_gem_object_put(gem_obj); | |
| 668 | + return 0; | |
| 669 | +} | |
| 670 | + | |
| 582 | 671 | int panfrost_unstable_ioctl_check(void) |
| 583 | 672 | { |
| 584 | 673 | if (!unstable_ioctls) |
| @@ -648,6 +737,8 @@ static const struct drm_ioctl_desc panfrost_drm_driver_ioctls[] = { · 63 unmodified lines | ||
| 648 | 737 | PANFROST_IOCTL(SET_LABEL_BO, set_label_bo, DRM_RENDER_ALLOW), |
| 649 | 738 | PANFROST_IOCTL(JM_CTX_CREATE, jm_ctx_create, DRM_RENDER_ALLOW), |
| 650 | 739 | PANFROST_IOCTL(JM_CTX_DESTROY, jm_ctx_destroy, DRM_RENDER_ALLOW), |
| 740 | + PANFROST_IOCTL(SYNC_BO, sync_bo, DRM_RENDER_ALLOW), | |
| 741 | + PANFROST_IOCTL(QUERY_BO_INFO, query_bo_info, DRM_RENDER_ALLOW), | |
| 651 | 742 | }; |
| 652 | 743 | |
| 653 | 744 | static void panfrost_gpu_show_fdinfo(struct panfrost_device *pfdev, |
| @@ -837,6 +928,9 @@ static void panfrost_debugfs_init(struct drm_minor *minor) · 183 unmodified lines | ||
| 837 | 928 | * - 1.4 - adds SET_LABEL_BO |
| 838 | 929 | * - 1.5 - adds JM_CTX_{CREATE,DESTROY} ioctls and extend SUBMIT to allow |
| 839 | 930 | * context creation with configurable priorities/affinity |
| 931 | + * - 1.6 - adds PANFROST_BO_MAP_WB, PANFROST_IOCTL_SYNC_BO, | |
| 932 | + * PANFROST_IOCTL_QUERY_BO_INFO and | |
| 933 | + * DRM_PANFROST_PARAM_SELECTED_COHERENCY | |
| 840 | 934 | */ |
| 841 | 935 | static const struct drm_driver panfrost_drm_driver = { |
| 842 | 936 | .driver_features = DRIVER_RENDER | DRIVER_GEM | DRIVER_SYNCOBJ, |
| @@ -849,15 +943,22 @@ static const struct drm_driver panfrost_drm_driver = { · 6 unmodified lines | ||
| 849 | 943 | .name = "panfrost", |
| 850 | 944 | .desc = "panfrost DRM", |
| 851 | 945 | .major = 1, |
| 852 | − .minor = 5, | |
| 946 | + .minor = 6, | |
| 853 | 947 | |
| 854 | 948 | .gem_create_object = panfrost_gem_create_object, |
| 949 | + .gem_prime_import = panfrost_gem_prime_import, | |
| 855 | 950 | .gem_prime_import_sg_table = panfrost_gem_prime_import_sg_table, |
| 856 | 951 | #ifdef CONFIG_DEBUG_FS |
| 857 | 952 | .debugfs_init = panfrost_debugfs_init, |
| 858 | 953 | #endif |
| 859 | 954 | }; |
| 860 | 955 | |
| 956 | +#ifdef CONFIG_TRANSPARENT_HUGEPAGE | |
| 957 | +bool panfrost_transparent_hugepage = true; | |
| 958 | +module_param_named(transparent_hugepage, panfrost_transparent_hugepage, bool, 0400); | |
| 959 | +MODULE_PARM_DESC(transparent_hugepage, "Use a dedicated tmpfs mount point with Transparent Hugepage enabled (true = default)"); | |
| 960 | +#endif | |
| 961 | + | |
| 861 | 962 | static int panfrost_probe(struct platform_device *pdev) |
| 862 | 963 | { |
| 863 | 964 | struct panfrost_device *pfdev; |
| @@ -0,0 +1,9 @@ | ||
| 1 | +/* SPDX-License-Identifier: GPL-2.0 or MIT */ | |
| 2 | +/* Copyright 2025 Amazon.com, Inc. or its affiliates */ | |
| 3 | + | |
| 4 | +#ifndef __PANFROST_DRV_H__ | |
| 5 | +#define __PANFROST_DRV_H__ | |
| 6 | + | |
| 7 | +extern bool panfrost_transparent_hugepage; | |
| 8 | + | |
| 9 | +#endif | |
| @@ -1,5 +1,6 @@ | ||
| 1 | 1 | // SPDX-License-Identifier: GPL-2.0 |
| 2 | 2 | /* Copyright 2019 Linaro, Ltd, Rob Herring <robh@kernel.org> */ |
| 3 | +/* Copyright 2025 Amazon.com, Inc. or its affiliates */ | |
| 3 | 4 | |
| 4 | 5 | #include <linux/cleanup.h> |
| 5 | 6 | #include <linux/err.h> |
| @@ -10,9 +11,26 @@ · 4 unmodified lines | ||
| 10 | 11 | #include <drm/panfrost_drm.h> |
| 11 | 12 | #include <drm/drm_print.h> |
| 12 | 13 | #include "panfrost_device.h" |
| 14 | +#include "panfrost_drv.h" | |
| 13 | 15 | #include "panfrost_gem.h" |
| 14 | 16 | #include "panfrost_mmu.h" |
| 15 | 17 | |
| 18 | +void panfrost_gem_init(struct panfrost_device *pfdev) | |
| 19 | +{ | |
| 20 | + int err; | |
| 21 | + | |
| 22 | + if (IS_ENABLED(CONFIG_TRANSPARENT_HUGEPAGE) && | |
| 23 | + !panfrost_transparent_hugepage) | |
| 24 | + return; | |
| 25 | + | |
| 26 | + err = drm_gem_huge_mnt_create(&pfdev->base, "within_size"); | |
| 27 | + if (drm_gem_get_huge_mnt(&pfdev->base)) | |
| 28 | + drm_info(&pfdev->base, "Using Transparent Hugepage\n"); | |
| 29 | + else if (err) | |
| 30 | + drm_warn(&pfdev->base, "Can't use Transparent Hugepage (%d)\n", | |
| 31 | + err); | |
| 32 | +} | |
| 33 | + | |
| 16 | 34 | #ifdef CONFIG_DEBUG_FS |
| 17 | 35 | static void panfrost_gem_debugfs_bo_add(struct panfrost_device *pfdev, |
| 18 | 36 | struct panfrost_gem_object *bo) |
| @@ -259,6 +277,128 @@ static size_t panfrost_gem_rss(struct drm_gem_object *obj) · 240 unmodified lines | ||
| 259 | 277 | return 0; |
| 260 | 278 | } |
| 261 | 279 | |
| 280 | +static struct sg_table * | |
| 281 | +panfrost_gem_prime_map_dma_buf(struct dma_buf_attachment *attach, | |
| 282 | + enum dma_data_direction dir) | |
| 283 | +{ | |
| 284 | + struct sg_table *sgt = drm_gem_map_dma_buf(attach, dir); | |
| 285 | + | |
| 286 | + if (!IS_ERR(sgt)) | |
| 287 | + attach->priv = sgt; | |
| 288 | + | |
| 289 | + return sgt; | |
| 290 | +} | |
| 291 | + | |
| 292 | +static void | |
| 293 | +panfrost_gem_prime_unmap_dma_buf(struct dma_buf_attachment *attach, | |
| 294 | + struct sg_table *sgt, | |
| 295 | + enum dma_data_direction dir) | |
| 296 | +{ | |
| 297 | + attach->priv = NULL; | |
| 298 | + drm_gem_unmap_dma_buf(attach, sgt, dir); | |
| 299 | +} | |
| 300 | + | |
| 301 | +static int | |
| 302 | +panfrost_gem_prime_begin_cpu_access(struct dma_buf *dma_buf, | |
| 303 | + enum dma_data_direction dir) | |
| 304 | +{ | |
| 305 | + struct drm_gem_object *obj = dma_buf->priv; | |
| 306 | + struct drm_device *dev = obj->dev; | |
| 307 | + struct drm_gem_shmem_object *shmem = to_drm_gem_shmem_obj(obj); | |
| 308 | + struct dma_buf_attachment *attach; | |
| 309 | + | |
| 310 | + dma_resv_lock(obj->resv, NULL); | |
| 311 | + if (shmem->sgt) | |
| 312 | + dma_sync_sgtable_for_cpu(dev->dev, shmem->sgt, dir); | |
| 313 | + | |
| 314 | + if (shmem->vaddr) | |
| 315 | + invalidate_kernel_vmap_range(shmem->vaddr, shmem->base.size); | |
| 316 | + | |
| 317 | + list_for_each_entry(attach, &dma_buf->attachments, node) { | |
| 318 | + struct sg_table *sgt = attach->priv; | |
| 319 | + | |
| 320 | + if (sgt) | |
| 321 | + dma_sync_sgtable_for_cpu(attach->dev, sgt, dir); | |
| 322 | + } | |
| 323 | + dma_resv_unlock(obj->resv); | |
| 324 | + | |
| 325 | + return 0; | |
| 326 | +} | |
| 327 | + | |
| 328 | +static int | |
| 329 | +panfrost_gem_prime_end_cpu_access(struct dma_buf *dma_buf, | |
| 330 | + enum dma_data_direction dir) | |
| 331 | +{ | |
| 332 | + struct drm_gem_object *obj = dma_buf->priv; | |
| 333 | + struct drm_device *dev = obj->dev; | |
| 334 | + struct drm_gem_shmem_object *shmem = to_drm_gem_shmem_obj(obj); | |
| 335 | + struct dma_buf_attachment *attach; | |
| 336 | + | |
| 337 | + dma_resv_lock(obj->resv, NULL); | |
| 338 | + list_for_each_entry(attach, &dma_buf->attachments, node) { | |
| 339 | + struct sg_table *sgt = attach->priv; | |
| 340 | + | |
| 341 | + if (sgt) | |
| 342 | + dma_sync_sgtable_for_device(attach->dev, sgt, dir); | |
| 343 | + } | |
| 344 | + | |
| 345 | + if (shmem->vaddr) | |
| 346 | + flush_kernel_vmap_range(shmem->vaddr, shmem->base.size); | |
| 347 | + | |
| 348 | + if (shmem->sgt) | |
| 349 | + dma_sync_sgtable_for_device(dev->dev, shmem->sgt, dir); | |
| 350 | + | |
| 351 | + dma_resv_unlock(obj->resv); | |
| 352 | + return 0; | |
| 353 | +} | |
| 354 | + | |
| 355 | +static const struct dma_buf_ops panfrost_dma_buf_ops = { | |
| 356 | + .attach = drm_gem_map_attach, | |
| 357 | + .detach = drm_gem_map_detach, | |
| 358 | + .map_dma_buf = panfrost_gem_prime_map_dma_buf, | |
| 359 | + .unmap_dma_buf = panfrost_gem_prime_unmap_dma_buf, | |
| 360 | + .release = drm_gem_dmabuf_release, | |
| 361 | + .mmap = drm_gem_dmabuf_mmap, | |
| 362 | + .vmap = drm_gem_dmabuf_vmap, | |
| 363 | + .vunmap = drm_gem_dmabuf_vunmap, | |
| 364 | + .begin_cpu_access = panfrost_gem_prime_begin_cpu_access, | |
| 365 | + .end_cpu_access = panfrost_gem_prime_end_cpu_access, | |
| 366 | +}; | |
| 367 | + | |
| 368 | +static struct dma_buf * | |
| 369 | +panfrost_gem_prime_export(struct drm_gem_object *obj, int flags) | |
| 370 | +{ | |
| 371 | + struct drm_device *dev = obj->dev; | |
| 372 | + struct dma_buf_export_info exp_info = { | |
| 373 | + .exp_name = KBUILD_MODNAME, | |
| 374 | + .owner = THIS_MODULE, | |
| 375 | + .ops = &panfrost_dma_buf_ops, | |
| 376 | + .size = obj->size, | |
| 377 | + .flags = flags, | |
| 378 | + .priv = obj, | |
| 379 | + .resv = obj->resv, | |
| 380 | + }; | |
| 381 | + | |
| 382 | + return drm_gem_dmabuf_export(dev, &exp_info); | |
| 383 | +} | |
| 384 | + | |
| 385 | +struct drm_gem_object * | |
| 386 | +panfrost_gem_prime_import(struct drm_device *dev, | |
| 387 | + struct dma_buf *dma_buf) | |
| 388 | +{ | |
| 389 | + struct drm_gem_object *obj = dma_buf->priv; | |
| 390 | + | |
| 391 | + if (dma_buf->ops == &panfrost_dma_buf_ops && obj->dev == dev) { | |
| 392 | + /* Importing dmabuf exported from our own gem increases | |
| 393 | + * refcount on gem itself instead of f_count of dmabuf. | |
| 394 | + */ | |
| 395 | + drm_gem_object_get(obj); | |
| 396 | + return obj; | |
| 397 | + } | |
| 398 | + | |
| 399 | + return drm_gem_prime_import(dev, dma_buf); | |
| 400 | +} | |
| 401 | + | |
| 262 | 402 | static const struct drm_gem_object_funcs panfrost_gem_funcs = { |
| 263 | 403 | .free = panfrost_gem_free_object, |
| 264 | 404 | .open = panfrost_gem_open, |
| @@ -267,6 +407,7 @@ static const struct drm_gem_object_funcs panfrost_gem_funcs = { · 2 unmodified lines | ||
| 267 | 407 | .pin = panfrost_gem_pin, |
| 268 | 408 | .unpin = drm_gem_shmem_object_unpin, |
| 269 | 409 | .get_sg_table = drm_gem_shmem_object_get_sg_table, |
| 410 | + .export = panfrost_gem_prime_export, | |
| 270 | 411 | .vmap = drm_gem_shmem_object_vmap, |
| 271 | 412 | .vunmap = drm_gem_shmem_object_vunmap, |
| 272 | 413 | .mmap = drm_gem_shmem_object_mmap, |
| @@ -303,12 +444,42 @@ struct drm_gem_object *panfrost_gem_create_object(struct drm_device *dev, size_t · 30 unmodified lines | ||
| 303 | 444 | return &obj->base.base; |
| 304 | 445 | } |
| 305 | 446 | |
| 447 | +static bool | |
| 448 | +should_map_wc(struct panfrost_gem_object *bo) | |
| 449 | +{ | |
| 450 | + struct panfrost_device *pfdev = to_panfrost_device(bo->base.base.dev); | |
| 451 | + | |
| 452 | + /* We can't do uncached mappings if the device is coherent, | |
| 453 | + * because the zeroing done by the shmem layer at page allocation | |
| 454 | + * time happens on a cached mapping which isn't CPU-flushed (at least | |
| 455 | + * not on Arm64 where the flush is deferred to PTE setup time, and | |
| 456 | + * only done conditionally based on the mapping permissions). We can't | |
| 457 | + * rely on dma_map_sgtable()/dma_sync_sgtable_for_xxx() either to flush | |
| 458 | + * those, because they are NOPed if dma_dev_coherent() returns true. | |
| 459 | + */ | |
| 460 | + if (pfdev->coherent) | |
| 461 | + return false; | |
| 462 | + | |
| 463 | + /* Cached mappings are explicitly requested, so no write-combine. */ | |
| 464 | + if (bo->wb_mmap) | |
| 465 | + return false; | |
| 466 | + | |
| 467 | + /* The default is write-combine. */ | |
| 468 | + return true; | |
| 469 | +} | |
| 470 | + | |
| 306 | 471 | struct panfrost_gem_object * |
| 307 | 472 | panfrost_gem_create(struct drm_device *dev, size_t size, u32 flags) |
| 308 | 473 | { |
| 309 | 474 | struct drm_gem_shmem_object *shmem; |
| 310 | 475 | struct panfrost_gem_object *bo; |
| 311 | 476 | |
| 477 | + /* The heap buffer is not supposed to be CPU-visible, so don't allow | |
| 478 | + * WB_MMAP on those. | |
| 479 | + */ | |
| 480 | + if ((flags & PANFROST_BO_HEAP) && (flags & PANFROST_BO_WB_MMAP)) | |
| 481 | + return ERR_PTR(-EINVAL); | |
| 482 | + | |
| 312 | 483 | /* Round up heap allocations to 2MB to keep fault handling simple */ |
| 313 | 484 | if (flags & PANFROST_BO_HEAP) |
| 314 | 485 | size = roundup(size, SZ_2M); |
| @@ -320,6 +491,8 @@ panfrost_gem_create(struct drm_device *dev, size_t size, u32 flags) · 5 unmodified lines | ||
| 320 | 491 | bo = to_panfrost_bo(&shmem->base); |
| 321 | 492 | bo->noexec = !!(flags & PANFROST_BO_NOEXEC); |
| 322 | 493 | bo->is_heap = !!(flags & PANFROST_BO_HEAP); |
| 494 | + bo->wb_mmap = !!(flags & PANFROST_BO_WB_MMAP); | |
| 495 | + bo->base.map_wc = should_map_wc(bo); | |
| 323 | 496 | |
| 324 | 497 | return bo; |
| 325 | 498 | } |
| @@ -366,6 +539,90 @@ panfrost_gem_set_label(struct drm_gem_object *obj, const char *label) · 40 unmodified lines | ||
| 366 | 539 | kfree_const(old_label); |
| 367 | 540 | } |
| 368 | 541 | |
| 542 | +int | |
| 543 | +panfrost_gem_sync(struct drm_gem_object *obj, u32 type, u32 offset, u32 size) | |
| 544 | +{ | |
| 545 | + struct panfrost_gem_object *bo = to_panfrost_bo(obj); | |
| 546 | + struct drm_gem_shmem_object *shmem = &bo->base; | |
| 547 | + const struct drm_device *dev = shmem->base.dev; | |
| 548 | + struct sg_table *sgt; | |
| 549 | + struct scatterlist *sgl; | |
| 550 | + unsigned int count; | |
| 551 | + | |
| 552 | + /* Make sure the range is in bounds. */ | |
| 553 | + if (offset + size < offset || offset + size > shmem->base.size) | |
| 554 | + return -EINVAL; | |
| 555 | + | |
| 556 | + /* Disallow CPU-cache maintenance on imported buffers. */ | |
| 557 | + if (drm_gem_is_imported(&shmem->base)) | |
| 558 | + return -EINVAL; | |
| 559 | + | |
| 560 | + switch (type) { | |
| 561 | + case PANFROST_BO_SYNC_CPU_CACHE_FLUSH: | |
| 562 | + case PANFROST_BO_SYNC_CPU_CACHE_FLUSH_AND_INVALIDATE: | |
| 563 | + break; | |
| 564 | + | |
| 565 | + default: | |
| 566 | + return -EINVAL; | |
| 567 | + } | |
| 568 | + | |
| 569 | + /* Don't bother if it's WC-mapped */ | |
| 570 | + if (shmem->map_wc) | |
| 571 | + return 0; | |
| 572 | + | |
| 573 | + /* Nothing to do if the size is zero. */ | |
| 574 | + if (size == 0) | |
| 575 | + return 0; | |
| 576 | + | |
| 577 | + sgt = drm_gem_shmem_get_pages_sgt(shmem); | |
| 578 | + if (IS_ERR(sgt)) | |
| 579 | + return PTR_ERR(sgt); | |
| 580 | + | |
| 581 | + for_each_sgtable_dma_sg(sgt, sgl, count) { | |
| 582 | + if (size == 0) | |
| 583 | + break; | |
| 584 | + | |
| 585 | + dma_addr_t paddr = sg_dma_address(sgl); | |
| 586 | + size_t len = sg_dma_len(sgl); | |
| 587 | + | |
| 588 | + if (len <= offset) { | |
| 589 | + offset -= len; | |
| 590 | + continue; | |
| 591 | + } | |
| 592 | + | |
| 593 | + paddr += offset; | |
| 594 | + len -= offset; | |
| 595 | + len = min_t(size_t, len, size); | |
| 596 | + size -= len; | |
| 597 | + offset = 0; | |
| 598 | + | |
| 599 | + /* It's unclear whether dma_sync_xxx() is the right API to do CPU | |
| 600 | + * cache maintenance given an IOMMU can register their own | |
| 601 | + * implementation doing more than just CPU cache flushes/invalidation, | |
| 602 | + * and what we really care about here is CPU caches only, but that's | |
| 603 | + * the best we have that is both arch-agnostic and does at least the | |
| 604 | + * CPU cache maintenance on a <page,offset,size> tuple. | |
| 605 | + * | |
| 606 | + * Also, I wish we could do a single | |
| 607 | + * | |
| 608 | + * dma_sync_single_for_device(BIDIR) | |
| 609 | + * | |
| 610 | + * and get a flush+invalidate, but that's not how it's implemented | |
| 611 | + * in practice (at least on arm64), so we have to make it | |
| 612 | + * | |
| 613 | + * dma_sync_single_for_device(TO_DEVICE) | |
| 614 | + * dma_sync_single_for_cpu(FROM_DEVICE) | |
| 615 | + * | |
| 616 | + * for the flush+invalidate case. | |
| 617 | + */ | |
| 618 | + dma_sync_single_for_device(dev->dev, paddr, len, DMA_TO_DEVICE); | |
| 619 | + if (type == PANFROST_BO_SYNC_CPU_CACHE_FLUSH_AND_INVALIDATE) | |
| 620 | + dma_sync_single_for_cpu(dev->dev, paddr, len, DMA_FROM_DEVICE); | |
| 621 | + } | |
| 622 | + | |
| 623 | + return 0; | |
| 624 | +} | |
| 625 | + | |
| 369 | 626 | void |
| 370 | 627 | panfrost_gem_internal_set_label(struct drm_gem_object *obj, const char *label) |
| 371 | 628 | { |
| @@ -98,6 +98,11 @@ struct panfrost_gem_object { · 97 unmodified lines | ||
| 98 | 98 | bool noexec :1; |
| 99 | 99 | bool is_heap :1; |
| 100 | 100 | |
| 101 | + /* On coherent devices, this reflects the creation flags, not the true | |
| 102 | + * cacheability attribute of the mapping. | |
| 103 | + */ | |
| 104 | + bool wb_mmap :1; | |
| 105 | + | |
| 101 | 106 | #ifdef CONFIG_DEBUG_FS |
| 102 | 107 | struct panfrost_gem_debugfs debugfs; |
| 103 | 108 | #endif |
| @@ -124,12 +129,17 @@ drm_mm_node_to_panfrost_mapping(struct drm_mm_node *node) · 20 unmodified lines | ||
| 124 | 129 | return container_of(node, struct panfrost_gem_mapping, mmnode); |
| 125 | 130 | } |
| 126 | 131 | |
| 132 | +void panfrost_gem_init(struct panfrost_device *pfdev); | |
| 133 | + | |
| 127 | 134 | struct drm_gem_object *panfrost_gem_create_object(struct drm_device *dev, size_t size); |
| 128 | 135 | |
| 129 | 136 | struct drm_gem_object * |
| 130 | 137 | panfrost_gem_prime_import_sg_table(struct drm_device *dev, |
| 131 | 138 | struct dma_buf_attachment *attach, |
| 132 | 139 | struct sg_table *sgt); |
| 140 | +struct drm_gem_object * | |
| 141 | +panfrost_gem_prime_import(struct drm_device *dev, | |
| 142 | + struct dma_buf *dma_buf); | |
| 133 | 143 | |
| 134 | 144 | struct panfrost_gem_object * |
| 135 | 145 | panfrost_gem_create(struct drm_device *dev, size_t size, u32 flags); |
| @@ -148,6 +158,8 @@ int panfrost_gem_shrinker_init(struct drm_device *dev); · 12 unmodified lines | ||
| 148 | 158 | void panfrost_gem_shrinker_cleanup(struct drm_device *dev); |
| 149 | 159 | |
| 150 | 160 | void panfrost_gem_set_label(struct drm_gem_object *obj, const char *label); |
| 161 | +int panfrost_gem_sync(struct drm_gem_object *obj, u32 type, | |
| 162 | + u32 offset, u32 size); | |
| 151 | 163 | void panfrost_gem_internal_set_label(struct drm_gem_object *obj, const char *label); |
| 152 | 164 | |
| 153 | 165 | #ifdef CONFIG_DEBUG_FS |
| @@ -159,8 +159,8 @@ static void panfrost_gpu_init_quirks(struct panfrost_device *pfdev) · 158 unmodified lines | ||
| 159 | 159 | pfdev->features.revision >= 0x2000) |
| 160 | 160 | quirks |= JM_MAX_JOB_THROTTLE_LIMIT << JM_JOB_THROTTLE_LIMIT_SHIFT; |
| 161 | 161 | else if (panfrost_model_eq(pfdev, 0x6000) && |
| 162 | − pfdev->features.coherency_features == COHERENCY_ACE) | |
| 163 | − quirks |= (COHERENCY_ACE_LITE | COHERENCY_ACE) << | |
| 162 | + pfdev->features.coherency_features == BIT(COHERENCY_ACE)) | |
| 163 | + quirks |= (BIT(COHERENCY_ACE_LITE) | BIT(COHERENCY_ACE)) << | |
| 164 | 164 | JM_FORCE_COHERENCY_FEATURES_SHIFT; |
| 165 | 165 | |
| 166 | 166 | if (panfrost_has_hw_feature(pfdev, HW_FEATURE_IDVS_GROUP_SIZE)) |
| @@ -263,7 +263,27 @@ static int panfrost_gpu_init_features(struct panfrost_device *pfdev) · 96 unmodified lines | ||
| 263 | 263 | pfdev->features.max_threads = gpu_read(pfdev, GPU_THREAD_MAX_THREADS); |
| 264 | 264 | pfdev->features.thread_max_workgroup_sz = gpu_read(pfdev, GPU_THREAD_MAX_WORKGROUP_SIZE); |
| 265 | 265 | pfdev->features.thread_max_barrier_sz = gpu_read(pfdev, GPU_THREAD_MAX_BARRIER_SIZE); |
| 266 | − pfdev->features.coherency_features = gpu_read(pfdev, GPU_COHERENCY_FEATURES); | |
| 266 | + | |
| 267 | + if (panfrost_has_hw_feature(pfdev, HW_FEATURE_COHERENCY_REG)) | |
| 268 | + pfdev->features.coherency_features = gpu_read(pfdev, GPU_COHERENCY_FEATURES); | |
| 269 | + else | |
| 270 | + pfdev->features.coherency_features = BIT(COHERENCY_ACE_LITE); | |
| 271 | + | |
| 272 | + BUILD_BUG_ON(COHERENCY_ACE_LITE != DRM_PANFROST_GPU_COHERENCY_ACE_LITE); | |
| 273 | + BUILD_BUG_ON(COHERENCY_ACE != DRM_PANFROST_GPU_COHERENCY_ACE); | |
| 274 | + BUILD_BUG_ON(COHERENCY_NONE != DRM_PANFROST_GPU_COHERENCY_NONE); | |
| 275 | + | |
| 276 | + if (!pfdev->coherent) { | |
| 277 | + pfdev->features.selected_coherency = COHERENCY_NONE; | |
| 278 | + } else if (pfdev->features.coherency_features & BIT(COHERENCY_ACE)) { | |
| 279 | + pfdev->features.selected_coherency = COHERENCY_ACE; | |
| 280 | + } else if (pfdev->features.coherency_features & BIT(COHERENCY_ACE_LITE)) { | |
| 281 | + pfdev->features.selected_coherency = COHERENCY_ACE_LITE; | |
| 282 | + } else { | |
| 283 | + drm_WARN(&pfdev->base, true, "No known coherency protocol supported"); | |
| 284 | + pfdev->features.selected_coherency = COHERENCY_NONE; | |
| 285 | + } | |
| 286 | + | |
| 267 | 287 | pfdev->features.afbc_features = gpu_read(pfdev, GPU_AFBC_FEATURES); |
| 268 | 288 | for (i = 0; i < 4; i++) |
| 269 | 289 | pfdev->features.texture_features[i] = gpu_read(pfdev, GPU_TEXTURE_FEATURES(i)); |
| @@ -102,9 +102,15 @@ · 101 unmodified lines | ||
| 102 | 102 | #define GPU_L2_PRESENT_LO 0x120 /* (RO) Level 2 cache present bitmap, low word */ |
| 103 | 103 | #define GPU_L2_PRESENT_HI 0x124 /* (RO) Level 2 cache present bitmap, high word */ |
| 104 | 104 | |
| 105 | +/* GPU_COHERENCY_FEATURES is a bitmask of BIT(COHERENCY_xxx) values encoding the | |
| 106 | + * set of supported coherency protocols. GPU_COHERENCY_ENABLE is passed a | |
| 107 | + * COHERENCY_xxx value. | |
| 108 | + */ | |
| 105 | 109 | #define GPU_COHERENCY_FEATURES 0x300 /* (RO) Coherency features present */ |
| 106 | −#define COHERENCY_ACE_LITE BIT(0) | |
| 107 | −#define COHERENCY_ACE BIT(1) | |
| 110 | +#define GPU_COHERENCY_ENABLE 0x304 /* (RW) Coherency protocol selection */ | |
| 111 | +#define COHERENCY_ACE_LITE 0 | |
| 112 | +#define COHERENCY_ACE 1 | |
| 113 | +#define COHERENCY_NONE 31 | |
| 108 | 114 | |
| 109 | 115 | #define GPU_STACK_PRESENT_LO 0xE00 /* (RO) Core stack present bitmap, low word */ |
| 110 | 116 | #define GPU_STACK_PRESENT_HI 0xE04 /* (RO) Core stack present bitmap, high word */ |
| @@ -18,6 +18,7 @@ · 17 unmodified lines | ||
| 18 | 18 | #include "panthor_devfreq.h" |
| 19 | 19 | #include "panthor_device.h" |
| 20 | 20 | #include "panthor_fw.h" |
| 21 | +#include "panthor_gem.h" | |
| 21 | 22 | #include "panthor_gpu.h" |
| 22 | 23 | #include "panthor_hw.h" |
| 23 | 24 | #include "panthor_mmu.h" |
| @@ -27,6 +28,12 @@ · 3 unmodified lines | ||
| 27 | 28 | |
| 28 | 29 | static int panthor_gpu_coherency_init(struct panthor_device *ptdev) |
| 29 | 30 | { |
| 31 | + BUILD_BUG_ON(GPU_COHERENCY_NONE != DRM_PANTHOR_GPU_COHERENCY_NONE); | |
| 32 | + BUILD_BUG_ON(GPU_COHERENCY_ACE_LITE != DRM_PANTHOR_GPU_COHERENCY_ACE_LITE); | |
| 33 | + BUILD_BUG_ON(GPU_COHERENCY_ACE != DRM_PANTHOR_GPU_COHERENCY_ACE); | |
| 34 | + | |
| 35 | + /* Start with no coherency, and update it if the device is flagged coherent. */ | |
| 36 | + ptdev->gpu_info.selected_coherency = GPU_COHERENCY_NONE; | |
| 30 | 37 | ptdev->coherent = device_get_dma_attr(ptdev->base.dev) == DEV_DMA_COHERENT; |
| 31 | 38 | |
| 32 | 39 | if (!ptdev->coherent) |
| @@ -36,8 +43,10 @@ static int panthor_gpu_coherency_init(struct panthor_device *ptdev) · 3 unmodified lines | ||
| 36 | 43 | * ACE protocol has never been supported for command stream frontend GPUs. |
| 37 | 44 | */ |
| 38 | 45 | if ((gpu_read(ptdev, GPU_COHERENCY_FEATURES) & |
| 39 | − GPU_COHERENCY_PROT_BIT(ACE_LITE))) | |
| 46 | + GPU_COHERENCY_PROT_BIT(ACE_LITE))) { | |
| 47 | + ptdev->gpu_info.selected_coherency = GPU_COHERENCY_ACE_LITE; | |
| 40 | 48 | return 0; |
| 49 | + } | |
| 41 | 50 | |
| 42 | 51 | drm_err(&ptdev->base, "Coherency not supported by the device"); |
| 43 | 52 | return -ENOTSUPP; |
| @@ -294,6 +303,8 @@ int panthor_device_init(struct panthor_device *ptdev) · 250 unmodified lines | ||
| 294 | 303 | if (ret) |
| 295 | 304 | goto err_unplug_fw; |
| 296 | 305 | |
| 306 | + panthor_gem_init(ptdev); | |
| 307 | + | |
| 297 | 308 | /* ~3 frames */ |
| 298 | 309 | pm_runtime_set_autosuspend_delay(ptdev->base.dev, 50); |
| 299 | 310 | pm_runtime_use_autosuspend(ptdev->base.dev); |
| @@ -177,7 +177,8 @@ panthor_get_uobj_array(const struct drm_panthor_obj_array *in, u32 min_stride, · 176 unmodified lines | ||
| 177 | 177 | PANTHOR_UOBJ_DECL(struct drm_panthor_sync_op, timeline_value), \ |
| 178 | 178 | PANTHOR_UOBJ_DECL(struct drm_panthor_queue_submit, syncs), \ |
| 179 | 179 | PANTHOR_UOBJ_DECL(struct drm_panthor_queue_create, ringbuf_size), \ |
| 180 | − PANTHOR_UOBJ_DECL(struct drm_panthor_vm_bind_op, syncs)) | |
| 180 | + PANTHOR_UOBJ_DECL(struct drm_panthor_vm_bind_op, syncs), \ | |
| 181 | + PANTHOR_UOBJ_DECL(struct drm_panthor_bo_sync_op, size)) | |
| 181 | 182 | |
| 182 | 183 | /** |
| 183 | 184 | * PANTHOR_UOBJ_SET() - Copy a kernel object to a user object. |
| @@ -901,7 +902,8 @@ static int panthor_ioctl_vm_destroy(struct drm_device *ddev, void *data, · 717 unmodified lines | ||
| 901 | 902 | return panthor_vm_pool_destroy_vm(pfile->vms, args->id); |
| 902 | 903 | } |
| 903 | 904 | |
| 904 | −#define PANTHOR_BO_FLAGS DRM_PANTHOR_BO_NO_MMAP | |
| 905 | +#define PANTHOR_BO_FLAGS (DRM_PANTHOR_BO_NO_MMAP | \ | |
| 906 | + DRM_PANTHOR_BO_WB_MMAP) | |
| 905 | 907 | |
| 906 | 908 | static int panthor_ioctl_bo_create(struct drm_device *ddev, void *data, |
| 907 | 909 | struct drm_file *file) |
| @@ -920,6 +922,10 @@ static int panthor_ioctl_bo_create(struct drm_device *ddev, void *data, · 12 unmodified lines | ||
| 920 | 922 | goto out_dev_exit; |
| 921 | 923 | } |
| 922 | 924 | |
| 925 | + if ((args->flags & DRM_PANTHOR_BO_NO_MMAP) && | |
| 926 | + (args->flags & DRM_PANTHOR_BO_WB_MMAP)) | |
| 927 | + return -EINVAL; | |
| 928 | + | |
| 923 | 929 | if (args->exclusive_vm_id) { |
| 924 | 930 | vm = panthor_vm_pool_get_vm(pfile->vms, args->exclusive_vm_id); |
| 925 | 931 | if (!vm) { |
| @@ -1396,6 +1402,66 @@ static int panthor_ioctl_set_user_mmio_offset(struct drm_device *ddev, · 470 unmodified lines | ||
| 1396 | 1402 | return 0; |
| 1397 | 1403 | } |
| 1398 | 1404 | |
| 1405 | +static int panthor_ioctl_bo_sync(struct drm_device *ddev, void *data, | |
| 1406 | + struct drm_file *file) | |
| 1407 | +{ | |
| 1408 | + struct drm_panthor_bo_sync *args = data; | |
| 1409 | + struct drm_panthor_bo_sync_op *ops; | |
| 1410 | + struct drm_gem_object *obj; | |
| 1411 | + int ret; | |
| 1412 | + | |
| 1413 | + if (!args->ops.count) | |
| 1414 | + return 0; | |
| 1415 | + | |
| 1416 | + ret = PANTHOR_UOBJ_GET_ARRAY(ops, &args->ops); | |
| 1417 | + if (ret) | |
| 1418 | + return ret; | |
| 1419 | + | |
| 1420 | + for (u32 i = 0; i < args->ops.count; i++) { | |
| 1421 | + obj = drm_gem_object_lookup(file, ops[i].handle); | |
| 1422 | + if (!obj) { | |
| 1423 | + ret = -ENOENT; | |
| 1424 | + goto err_ops; | |
| 1425 | + } | |
| 1426 | + | |
| 1427 | + ret = panthor_gem_sync(obj, ops[i].type, ops[i].offset, | |
| 1428 | + ops[i].size); | |
| 1429 | + | |
| 1430 | + drm_gem_object_put(obj); | |
| 1431 | + | |
| 1432 | + if (ret) | |
| 1433 | + goto err_ops; | |
| 1434 | + } | |
| 1435 | + | |
| 1436 | +err_ops: | |
| 1437 | + kvfree(ops); | |
| 1438 | + | |
| 1439 | + return ret; | |
| 1440 | +} | |
| 1441 | + | |
| 1442 | +static int panthor_ioctl_bo_query_info(struct drm_device *ddev, void *data, | |
| 1443 | + struct drm_file *file) | |
| 1444 | +{ | |
| 1445 | + struct drm_panthor_bo_query_info *args = data; | |
| 1446 | + struct panthor_gem_object *bo; | |
| 1447 | + struct drm_gem_object *obj; | |
| 1448 | + | |
| 1449 | + obj = drm_gem_object_lookup(file, args->handle); | |
| 1450 | + if (!obj) | |
| 1451 | + return -ENOENT; | |
| 1452 | + | |
| 1453 | + bo = to_panthor_bo(obj); | |
| 1454 | + args->pad = 0; | |
| 1455 | + args->create_flags = bo->flags; | |
| 1456 | + | |
| 1457 | + args->extra_flags = 0; | |
| 1458 | + if (drm_gem_is_imported(&bo->base.base)) | |
| 1459 | + args->extra_flags |= DRM_PANTHOR_BO_IS_IMPORTED; | |
| 1460 | + | |
| 1461 | + drm_gem_object_put(obj); | |
| 1462 | + return 0; | |
| 1463 | +} | |
| 1464 | + | |
| 1399 | 1465 | static int |
| 1400 | 1466 | panthor_open(struct drm_device *ddev, struct drm_file *file) |
| 1401 | 1467 | { |
| @@ -1470,6 +1536,8 @@ static const struct drm_ioctl_desc panthor_drm_driver_ioctls[] = { · 68 unmodified lines | ||
| 1470 | 1536 | PANTHOR_IOCTL(GROUP_SUBMIT, group_submit, DRM_RENDER_ALLOW), |
| 1471 | 1537 | PANTHOR_IOCTL(BO_SET_LABEL, bo_set_label, DRM_RENDER_ALLOW), |
| 1472 | 1538 | PANTHOR_IOCTL(SET_USER_MMIO_OFFSET, set_user_mmio_offset, DRM_RENDER_ALLOW), |
| 1539 | + PANTHOR_IOCTL(BO_SYNC, bo_sync, DRM_RENDER_ALLOW), | |
| 1540 | + PANTHOR_IOCTL(BO_QUERY_INFO, bo_query_info, DRM_RENDER_ALLOW), | |
| 1473 | 1541 | }; |
| 1474 | 1542 | |
| 1475 | 1543 | static int panthor_mmap(struct file *filp, struct vm_area_struct *vma) |
| @@ -1559,6 +1627,7 @@ static const struct file_operations panthor_drm_driver_fops = { · 83 unmodified lines | ||
| 1559 | 1627 | .read = drm_read, |
| 1560 | 1628 | .llseek = noop_llseek, |
| 1561 | 1629 | .mmap = panthor_mmap, |
| 1630 | + .get_unmapped_area = drm_gem_get_unmapped_area, | |
| 1562 | 1631 | .show_fdinfo = drm_show_fdinfo, |
| 1563 | 1632 | .fop_flags = FOP_UNSIGNED_OFFSET, |
| 1564 | 1633 | }; |
| @@ -1604,6 +1673,11 @@ static void panthor_debugfs_init(struct drm_minor *minor) · 39 unmodified lines | ||
| 1604 | 1673 | * - 1.3 - adds DRM_PANTHOR_GROUP_STATE_INNOCENT flag |
| 1605 | 1674 | * - 1.4 - adds DRM_IOCTL_PANTHOR_BO_SET_LABEL ioctl |
| 1606 | 1675 | * - 1.5 - adds DRM_PANTHOR_SET_USER_MMIO_OFFSET ioctl |
| 1676 | + * - 1.6 - enables GLB_COUNTER_EN | |
| 1677 | + * - 1.7 - adds DRM_PANTHOR_BO_WB_MMAP flag | |
| 1678 | + * - adds DRM_IOCTL_PANTHOR_BO_SYNC ioctl | |
| 1679 | + * - adds DRM_IOCTL_PANTHOR_BO_QUERY_INFO ioctl | |
| 1680 | + * - adds drm_panthor_gpu_info::selected_coherency | |
| 1607 | 1681 | */ |
| 1608 | 1682 | static const struct drm_driver panthor_drm_driver = { |
| 1609 | 1683 | .driver_features = DRIVER_RENDER | DRIVER_GEM | DRIVER_SYNCOBJ | |
| @@ -1617,15 +1691,22 @@ static const struct drm_driver panthor_drm_driver = { · 7 unmodified lines | ||
| 1617 | 1691 | .name = "panthor", |
| 1618 | 1692 | .desc = "Panthor DRM driver", |
| 1619 | 1693 | .major = 1, |
| 1620 | − .minor = 5, | |
| 1694 | + .minor = 7, | |
| 1621 | 1695 | |
| 1622 | 1696 | .gem_create_object = panthor_gem_create_object, |
| 1623 | 1697 | .gem_prime_import_sg_table = drm_gem_shmem_prime_import_sg_table, |
| 1698 | + .gem_prime_import = panthor_gem_prime_import, | |
| 1624 | 1699 | #ifdef CONFIG_DEBUG_FS |
| 1625 | 1700 | .debugfs_init = panthor_debugfs_init, |
| 1626 | 1701 | #endif |
| 1627 | 1702 | }; |
| 1628 | 1703 | |
| 1704 | +#ifdef CONFIG_TRANSPARENT_HUGEPAGE | |
| 1705 | +bool panthor_transparent_hugepage = true; | |
| 1706 | +module_param_named(transparent_hugepage, panthor_transparent_hugepage, bool, 0400); | |
| 1707 | +MODULE_PARM_DESC(transparent_hugepage, "Use a dedicated tmpfs mount point with Transparent Hugepage enabled (true = default)"); | |
| 1708 | +#endif | |
| 1709 | + | |
| 1629 | 1710 | static int panthor_probe(struct platform_device *pdev) |
| 1630 | 1711 | { |
| 1631 | 1712 | struct panthor_device *ptdev; |
| @@ -0,0 +1,9 @@ | ||
| 1 | +/* SPDX-License-Identifier: GPL-2.0 or MIT */ | |
| 2 | +/* Copyright 2025 Amazon.com, Inc. or its affiliates */ | |
| 3 | + | |
| 4 | +#ifndef __PANTHOR_DRV_H__ | |
| 5 | +#define __PANTHOR_DRV_H__ | |
| 6 | + | |
| 7 | +extern bool panthor_transparent_hugepage; | |
| 8 | + | |
| 9 | +#endif | |
| @@ -1044,7 +1044,8 @@ static void panthor_fw_init_global_iface(struct panthor_device *ptdev) · 1043 unmodified lines | ||
| 1044 | 1044 | if (panthor_fw_has_glb_state(ptdev)) |
| 1045 | 1045 | glb_iface->input->ack_irq_mask |= GLB_STATE_MASK; |
| 1046 | 1046 | |
| 1047 | − panthor_fw_update_reqs(glb_iface, req, GLB_IDLE_EN, GLB_IDLE_EN); | |
| 1047 | + panthor_fw_update_reqs(glb_iface, req, GLB_IDLE_EN | GLB_COUNTER_EN, | |
| 1048 | + GLB_IDLE_EN | GLB_COUNTER_EN); | |
| 1048 | 1049 | panthor_fw_toggle_reqs(glb_iface, req, ack, |
| 1049 | 1050 | GLB_CFG_ALLOC_EN | |
| 1050 | 1051 | GLB_CFG_POWEROFF_TIMER | |
| @@ -1187,7 +1188,6 @@ void panthor_fw_pre_reset(struct panthor_device *ptdev, bool on_hang) · 136 unmodified lines | ||
| 1187 | 1188 | else |
| 1188 | 1189 | ptdev->reset.fast = true; |
| 1189 | 1190 | } |
| 1190 | − panthor_fw_stop(ptdev); | |
| 1191 | 1191 | |
| 1192 | 1192 | panthor_job_irq_suspend(&ptdev->fw->irq); |
| 1193 | 1193 | panthor_fw_stop(ptdev); |
| @@ -1,6 +1,7 @@ | ||
| 1 | 1 | // SPDX-License-Identifier: GPL-2.0 or MIT |
| 2 | 2 | /* Copyright 2019 Linaro, Ltd, Rob Herring <robh@kernel.org> */ |
| 3 | 3 | /* Copyright 2023 Collabora ltd. */ |
| 4 | +/* Copyright 2025 Amazon.com, Inc. or its affiliates */ | |
| 4 | 5 | |
| 5 | 6 | #include <linux/cleanup.h> |
| 6 | 7 | #include <linux/dma-buf.h> |
| @@ -12,10 +13,27 @@ · 5 unmodified lines | ||
| 12 | 13 | #include <drm/panthor_drm.h> |
| 13 | 14 | |
| 14 | 15 | #include "panthor_device.h" |
| 16 | +#include "panthor_drv.h" | |
| 15 | 17 | #include "panthor_fw.h" |
| 16 | 18 | #include "panthor_gem.h" |
| 17 | 19 | #include "panthor_mmu.h" |
| 18 | 20 | |
| 21 | +void panthor_gem_init(struct panthor_device *ptdev) | |
| 22 | +{ | |
| 23 | + int err; | |
| 24 | + | |
| 25 | + if (IS_ENABLED(CONFIG_TRANSPARENT_HUGEPAGE) && | |
| 26 | + !panthor_transparent_hugepage) | |
| 27 | + return; | |
| 28 | + | |
| 29 | + err = drm_gem_huge_mnt_create(&ptdev->base, "within_size"); | |
| 30 | + if (drm_gem_get_huge_mnt(&ptdev->base)) | |
| 31 | + drm_info(&ptdev->base, "Using Transparent Hugepage\n"); | |
| 32 | + else if (err) | |
| 33 | + drm_warn(&ptdev->base, "Can't use Transparent Hugepage (%d)\n", | |
| 34 | + err); | |
| 35 | +} | |
| 36 | + | |
| 19 | 37 | #ifdef CONFIG_DEBUG_FS |
| 20 | 38 | static void panthor_gem_debugfs_bo_init(struct panthor_gem_object *bo) |
| 21 | 39 | { |
| @@ -59,6 +77,39 @@ static void panthor_gem_debugfs_set_usage_flags(struct panthor_gem_object *bo, u · 37 unmodified lines | ||
| 59 | 77 | static void panthor_gem_debugfs_bo_init(struct panthor_gem_object *bo) {} |
| 60 | 78 | #endif |
| 61 | 79 | |
| 80 | +static bool | |
| 81 | +should_map_wc(struct panthor_gem_object *bo, struct panthor_vm *exclusive_vm) | |
| 82 | +{ | |
| 83 | + struct panthor_device *ptdev = container_of(bo->base.base.dev, struct panthor_device, base); | |
| 84 | + | |
| 85 | + /* We can't do uncached mappings if the device is coherent, | |
| 86 | + * because the zeroing done by the shmem layer at page allocation | |
| 87 | + * time happens on a cached mapping which isn't CPU-flushed (at least | |
| 88 | + * not on Arm64 where the flush is deferred to PTE setup time, and | |
| 89 | + * only done conditionally based on the mapping permissions). We can't | |
| 90 | + * rely on dma_map_sgtable()/dma_sync_sgtable_for_xxx() either to flush | |
| 91 | + * those, because they are NOPed if dma_dev_coherent() returns true. | |
| 92 | + * | |
| 93 | + * FIXME: Note that this problem is going to pop up again when we | |
| 94 | + * decide to support mapping buffers with the NO_MMAP flag as | |
| 95 | + * non-shareable (AKA buffers accessed only by the GPU), because we | |
| 96 | + * need the same CPU flush to happen after page allocation, otherwise | |
| 97 | + * there's a risk of data leak or late corruption caused by a dirty | |
| 98 | + * cacheline being evicted. At this point we'll need a way to force | |
| 99 | + * CPU cache maintenance regardless of whether the device is coherent | |
| 100 | + * or not. | |
| 101 | + */ | |
| 102 | + if (ptdev->coherent) | |
| 103 | + return false; | |
| 104 | + | |
| 105 | + /* Cached mappings are explicitly requested, so no write-combine. */ | |
| 106 | + if (bo->flags & DRM_PANTHOR_BO_WB_MMAP) | |
| 107 | + return false; | |
| 108 | + | |
| 109 | + /* The default is write-combine. */ | |
| 110 | + return true; | |
| 111 | +} | |
| 112 | + | |
| 62 | 113 | static void panthor_gem_free_object(struct drm_gem_object *obj) |
| 63 | 114 | { |
| 64 | 115 | struct panthor_gem_object *bo = to_panthor_bo(obj); |
| @@ -145,6 +196,7 @@ panthor_kernel_bo_create(struct panthor_device *ptdev, struct panthor_vm *vm, · 80 unmodified lines | ||
| 145 | 196 | bo = to_panthor_bo(&obj->base); |
| 146 | 197 | kbo->obj = &obj->base; |
| 147 | 198 | bo->flags = bo_flags; |
| 199 | + bo->base.map_wc = should_map_wc(bo, vm); | |
| 148 | 200 | bo->exclusive_vm_root_gem = panthor_vm_root_gem(vm); |
| 149 | 201 | drm_gem_object_get(bo->exclusive_vm_root_gem); |
| 150 | 202 | bo->base.base.resv = bo->exclusive_vm_root_gem->resv; |
| @@ -184,14 +236,130 @@ err_free_bo: · 33 unmodified lines | ||
| 184 | 236 | return ERR_PTR(ret); |
| 185 | 237 | } |
| 186 | 238 | |
| 239 | +static struct sg_table * | |
| 240 | +panthor_gem_prime_map_dma_buf(struct dma_buf_attachment *attach, | |
| 241 | + enum dma_data_direction dir) | |
| 242 | +{ | |
| 243 | + struct sg_table *sgt = drm_gem_map_dma_buf(attach, dir); | |
| 244 | + | |
| 245 | + if (!IS_ERR(sgt)) | |
| 246 | + attach->priv = sgt; | |
| 247 | + | |
| 248 | + return sgt; | |
| 249 | +} | |
| 250 | + | |
| 251 | +static void | |
| 252 | +panthor_gem_prime_unmap_dma_buf(struct dma_buf_attachment *attach, | |
| 253 | + struct sg_table *sgt, | |
| 254 | + enum dma_data_direction dir) | |
| 255 | +{ | |
| 256 | + attach->priv = NULL; | |
| 257 | + drm_gem_unmap_dma_buf(attach, sgt, dir); | |
| 258 | +} | |
| 259 | + | |
| 260 | +static int | |
| 261 | +panthor_gem_prime_begin_cpu_access(struct dma_buf *dma_buf, | |
| 262 | + enum dma_data_direction dir) | |
| 263 | +{ | |
| 264 | + struct drm_gem_object *obj = dma_buf->priv; | |
| 265 | + struct drm_device *dev = obj->dev; | |
| 266 | + struct drm_gem_shmem_object *shmem = to_drm_gem_shmem_obj(obj); | |
| 267 | + struct dma_buf_attachment *attach; | |
| 268 | + | |
| 269 | + dma_resv_lock(obj->resv, NULL); | |
| 270 | + if (shmem->sgt) | |
| 271 | + dma_sync_sgtable_for_cpu(dev->dev, shmem->sgt, dir); | |
| 272 | + | |
| 273 | + if (shmem->vaddr) | |
| 274 | + invalidate_kernel_vmap_range(shmem->vaddr, shmem->base.size); | |
| 275 | + | |
| 276 | + list_for_each_entry(attach, &dma_buf->attachments, node) { | |
| 277 | + struct sg_table *sgt = attach->priv; | |
| 278 | + | |
| 279 | + if (sgt) | |
| 280 | + dma_sync_sgtable_for_cpu(attach->dev, sgt, dir); | |
| 281 | + } | |
| 282 | + dma_resv_unlock(obj->resv); | |
| 283 | + | |
| 284 | + return 0; | |
| 285 | +} | |
| 286 | + | |
| 287 | +static int | |
| 288 | +panthor_gem_prime_end_cpu_access(struct dma_buf *dma_buf, | |
| 289 | + enum dma_data_direction dir) | |
| 290 | +{ | |
| 291 | + struct drm_gem_object *obj = dma_buf->priv; | |
| 292 | + struct drm_device *dev = obj->dev; | |
| 293 | + struct drm_gem_shmem_object *shmem = to_drm_gem_shmem_obj(obj); | |
| 294 | + struct dma_buf_attachment *attach; | |
| 295 | + | |
| 296 | + dma_resv_lock(obj->resv, NULL); | |
| 297 | + list_for_each_entry(attach, &dma_buf->attachments, node) { | |
| 298 | + struct sg_table *sgt = attach->priv; | |
| 299 | + | |
| 300 | + if (sgt) | |
| 301 | + dma_sync_sgtable_for_device(attach->dev, sgt, dir); | |
| 302 | + } | |
| 303 | + | |
| 304 | + if (shmem->vaddr) | |
| 305 | + flush_kernel_vmap_range(shmem->vaddr, shmem->base.size); | |
| 306 | + | |
| 307 | + if (shmem->sgt) | |
| 308 | + dma_sync_sgtable_for_device(dev->dev, shmem->sgt, dir); | |
| 309 | + | |
| 310 | + dma_resv_unlock(obj->resv); | |
| 311 | + return 0; | |
| 312 | +} | |
| 313 | + | |
| 314 | +static const struct dma_buf_ops panthor_dma_buf_ops = { | |
| 315 | + .attach = drm_gem_map_attach, | |
| 316 | + .detach = drm_gem_map_detach, | |
| 317 | + .map_dma_buf = panthor_gem_prime_map_dma_buf, | |
| 318 | + .unmap_dma_buf = panthor_gem_prime_unmap_dma_buf, | |
| 319 | + .release = drm_gem_dmabuf_release, | |
| 320 | + .mmap = drm_gem_dmabuf_mmap, | |
| 321 | + .vmap = drm_gem_dmabuf_vmap, | |
| 322 | + .vunmap = drm_gem_dmabuf_vunmap, | |
| 323 | + .begin_cpu_access = panthor_gem_prime_begin_cpu_access, | |
| 324 | + .end_cpu_access = panthor_gem_prime_end_cpu_access, | |
| 325 | +}; | |
| 326 | + | |
| 187 | 327 | static struct dma_buf * |
| 188 | 328 | panthor_gem_prime_export(struct drm_gem_object *obj, int flags) |
| 189 | 329 | { |
| 330 | + struct drm_device *dev = obj->dev; | |
| 331 | + struct dma_buf_export_info exp_info = { | |
| 332 | + .exp_name = KBUILD_MODNAME, | |
| 333 | + .owner = THIS_MODULE, | |
| 334 | + .ops = &panthor_dma_buf_ops, | |
| 335 | + .size = obj->size, | |
| 336 | + .flags = flags, | |
| 337 | + .priv = obj, | |
| 338 | + .resv = obj->resv, | |
| 339 | + }; | |
| 340 | + | |
| 190 | 341 | /* We can't export GEMs that have an exclusive VM. */ |
| 191 | 342 | if (to_panthor_bo(obj)->exclusive_vm_root_gem) |
| 192 | 343 | return ERR_PTR(-EINVAL); |
| 193 | 344 | |
| 194 | − return drm_gem_prime_export(obj, flags); | |
| 345 | + return drm_gem_dmabuf_export(dev, &exp_info); | |
| 346 | +} | |
| 347 | + | |
| 348 | +struct drm_gem_object * | |
| 349 | +panthor_gem_prime_import(struct drm_device *dev, | |
| 350 | + struct dma_buf *dma_buf) | |
| 351 | +{ | |
| 352 | + struct drm_gem_object *obj = dma_buf->priv; | |
| 353 | + | |
| 354 | + if (dma_buf->ops == &panthor_dma_buf_ops && obj->dev == dev) { | |
| 355 | + /* Importing dmabuf exported from our own gem increases | |
| 356 | + * refcount on gem itself instead of f_count of dmabuf. | |
| 357 | + */ | |
| 358 | + drm_gem_object_get(obj); | |
| 359 | + return obj; | |
| 360 | + } | |
| 361 | + | |
| 362 | + return drm_gem_prime_import(dev, dma_buf); | |
| 195 | 363 | } |
| 196 | 364 | |
| 197 | 365 | static enum drm_gem_object_status panthor_gem_status(struct drm_gem_object *obj) |
| @@ -229,7 +397,6 @@ static const struct drm_gem_object_funcs panthor_gem_funcs = { · 31 unmodified lines | ||
| 229 | 397 | */ |
| 230 | 398 | struct drm_gem_object *panthor_gem_create_object(struct drm_device *ddev, size_t size) |
| 231 | 399 | { |
| 232 | − struct panthor_device *ptdev = container_of(ddev, struct panthor_device, base); | |
| 233 | 400 | struct panthor_gem_object *obj; |
| 234 | 401 | |
| 235 | 402 | obj = kzalloc(sizeof(*obj), GFP_KERNEL); |
| @@ -237,7 +404,6 @@ struct drm_gem_object *panthor_gem_create_object(struct drm_device *ddev, size_t · 1 unmodified line | ||
| 237 | 404 | return ERR_PTR(-ENOMEM); |
| 238 | 405 | |
| 239 | 406 | obj->base.base.funcs = &panthor_gem_funcs; |
| 240 | − obj->base.map_wc = !ptdev->coherent; | |
| 241 | 407 | mutex_init(&obj->label.lock); |
| 242 | 408 | |
| 243 | 409 | panthor_gem_debugfs_bo_init(obj); |
| @@ -272,6 +438,7 @@ panthor_gem_create_with_handle(struct drm_file *file, · 28 unmodified lines | ||
| 272 | 438 | |
| 273 | 439 | bo = to_panthor_bo(&shmem->base); |
| 274 | 440 | bo->flags = flags; |
| 441 | + bo->base.map_wc = should_map_wc(bo, exclusive_vm); | |
| 275 | 442 | |
| 276 | 443 | if (exclusive_vm) { |
| 277 | 444 | bo->exclusive_vm_root_gem = panthor_vm_root_gem(exclusive_vm); |
| @@ -349,6 +516,91 @@ panthor_gem_kernel_bo_set_label(struct panthor_kernel_bo *bo, const char *label) · 71 unmodified lines | ||
| 349 | 516 | panthor_gem_bo_set_label(bo->obj, str); |
| 350 | 517 | } |
| 351 | 518 | |
| 519 | +int | |
| 520 | +panthor_gem_sync(struct drm_gem_object *obj, u32 type, | |
| 521 | + u64 offset, u64 size) | |
| 522 | +{ | |
| 523 | + struct panthor_gem_object *bo = to_panthor_bo(obj); | |
| 524 | + struct drm_gem_shmem_object *shmem = &bo->base; | |
| 525 | + const struct drm_device *dev = shmem->base.dev; | |
| 526 | + struct sg_table *sgt; | |
| 527 | + struct scatterlist *sgl; | |
| 528 | + unsigned int count; | |
| 529 | + | |
| 530 | + /* Make sure the range is in bounds. */ | |
| 531 | + if (offset + size < offset || offset + size > shmem->base.size) | |
| 532 | + return -EINVAL; | |
| 533 | + | |
| 534 | + /* Disallow CPU-cache maintenance on imported buffers. */ | |
| 535 | + if (drm_gem_is_imported(&shmem->base)) | |
| 536 | + return -EINVAL; | |
| 537 | + | |
| 538 | + switch (type) { | |
| 539 | + case DRM_PANTHOR_BO_SYNC_CPU_CACHE_FLUSH: | |
| 540 | + case DRM_PANTHOR_BO_SYNC_CPU_CACHE_FLUSH_AND_INVALIDATE: | |
| 541 | + break; | |
| 542 | + | |
| 543 | + default: | |
| 544 | + return -EINVAL; | |
| 545 | + } | |
| 546 | + | |
| 547 | + /* Don't bother if it's WC-mapped */ | |
| 548 | + if (shmem->map_wc) | |
| 549 | + return 0; | |
| 550 | + | |
| 551 | + /* Nothing to do if the size is zero. */ | |
| 552 | + if (size == 0) | |
| 553 | + return 0; | |
| 554 | + | |
| 555 | + sgt = drm_gem_shmem_get_pages_sgt(shmem); | |
| 556 | + if (IS_ERR(sgt)) | |
| 557 | + return PTR_ERR(sgt); | |
| 558 | + | |
| 559 | + for_each_sgtable_dma_sg(sgt, sgl, count) { | |
| 560 | + if (size == 0) | |
| 561 | + break; | |
| 562 | + | |
| 563 | + dma_addr_t paddr = sg_dma_address(sgl); | |
| 564 | + size_t len = sg_dma_len(sgl); | |
| 565 | + | |
| 566 | + if (len <= offset) { | |
| 567 | + offset -= len; | |
| 568 | + continue; | |
| 569 | + } | |
| 570 | + | |
| 571 | + paddr += offset; | |
| 572 | + len -= offset; | |
| 573 | + len = min_t(size_t, len, size); | |
| 574 | + size -= len; | |
| 575 | + offset = 0; | |
| 576 | + | |
| 577 | + /* It's unclear whether dma_sync_xxx() is the right API to do CPU | |
| 578 | + * cache maintenance given an IOMMU can register their own | |
| 579 | + * implementation doing more than just CPU cache flushes/invalidation, | |
| 580 | + * and what we really care about here is CPU caches only, but that's | |
| 581 | + * the best we have that is both arch-agnostic and does at least the | |
| 582 | + * CPU cache maintenance on a <page,offset,size> tuple. | |
| 583 | + * | |
| 584 | + * Also, I wish we could do a single | |
| 585 | + * | |
| 586 | + * dma_sync_single_for_device(BIDIR) | |
| 587 | + * | |
| 588 | + * and get a flush+invalidate, but that's not how it's implemented | |
| 589 | + * in practice (at least on arm64), so we have to make it | |
| 590 | + * | |
| 591 | + * dma_sync_single_for_device(TO_DEVICE) | |
| 592 | + * dma_sync_single_for_cpu(FROM_DEVICE) | |
| 593 | + * | |
| 594 | + * for the flush+invalidate case. | |
| 595 | + */ | |
| 596 | + dma_sync_single_for_device(dev->dev, paddr, len, DMA_TO_DEVICE); | |
| 597 | + if (type == DRM_PANTHOR_BO_SYNC_CPU_CACHE_FLUSH_AND_INVALIDATE) | |
| 598 | + dma_sync_single_for_cpu(dev->dev, paddr, len, DMA_FROM_DEVICE); | |
| 599 | + } | |
| 600 | + | |
| 601 | + return 0; | |
| 602 | +} | |
| 603 | + | |
| 352 | 604 | #ifdef CONFIG_DEBUG_FS |
| 353 | 605 | struct gem_size_totals { |
| 354 | 606 | size_t size; |
| @@ -136,6 +136,8 @@ struct panthor_gem_object *to_panthor_bo(struct drm_gem_object *obj) · 135 unmodified lines | ||
| 136 | 136 | return container_of(to_drm_gem_shmem_obj(obj), struct panthor_gem_object, base); |
| 137 | 137 | } |
| 138 | 138 | |
| 139 | +void panthor_gem_init(struct panthor_device *ptdev); | |
| 140 | + | |
| 139 | 141 | struct drm_gem_object *panthor_gem_create_object(struct drm_device *ddev, size_t size); |
| 140 | 142 | |
| 141 | 143 | int |
| @@ -146,6 +148,12 @@ panthor_gem_create_with_handle(struct drm_file *file, · 4 unmodified lines | ||
| 146 | 148 | |
| 147 | 149 | void panthor_gem_bo_set_label(struct drm_gem_object *obj, const char *label); |
| 148 | 150 | void panthor_gem_kernel_bo_set_label(struct panthor_kernel_bo *bo, const char *label); |
| 151 | +int panthor_gem_sync(struct drm_gem_object *obj, | |
| 152 | + u32 type, u64 offset, u64 size); | |
| 153 | + | |
| 154 | +struct drm_gem_object * | |
| 155 | +panthor_gem_prime_import(struct drm_device *dev, | |
| 156 | + struct dma_buf *dma_buf); | |
| 149 | 157 | |
| 150 | 158 | static inline u64 |
| 151 | 159 | panthor_kernel_bo_gpuva(struct panthor_kernel_bo *bo) |
| @@ -51,7 +51,7 @@ struct panthor_gpu { · 50 unmodified lines | ||
| 51 | 51 | static void panthor_gpu_coherency_set(struct panthor_device *ptdev) |
| 52 | 52 | { |
| 53 | 53 | gpu_write(ptdev, GPU_COHERENCY_PROTOCOL, |
| 54 | − ptdev->coherent ? GPU_COHERENCY_PROT_BIT(ACE_LITE) : GPU_COHERENCY_NONE); | |
| 54 | + ptdev->gpu_info.selected_coherency); | |
| 55 | 55 | } |
| 56 | 56 | |
| 57 | 57 | static void panthor_gpu_l2_config_set(struct panthor_device *ptdev) |
| @@ -289,38 +289,42 @@ int panthor_gpu_l2_power_on(struct panthor_device *ptdev) · 231 unmodified lines | ||
| 289 | 289 | int panthor_gpu_flush_caches(struct panthor_device *ptdev, |
| 290 | 290 | u32 l2, u32 lsc, u32 other) |
| 291 | 291 | { |
| 292 | − bool timedout = false; | |
| 293 | 292 | unsigned long flags; |
| 293 | + int ret = 0; | |
| 294 | 294 | |
| 295 | 295 | /* Serialize cache flush operations. */ |
| 296 | 296 | guard(mutex)(&ptdev->gpu->cache_flush_lock); |
| 297 | 297 | |
| 298 | 298 | spin_lock_irqsave(&ptdev->gpu->reqs_lock, flags); |
| 299 | − if (!drm_WARN_ON(&ptdev->base, | |
| 300 | − ptdev->gpu->pending_reqs & GPU_IRQ_CLEAN_CACHES_COMPLETED)) { | |
| 299 | + if (!(ptdev->gpu->pending_reqs & GPU_IRQ_CLEAN_CACHES_COMPLETED)) { | |
| 301 | 300 | ptdev->gpu->pending_reqs |= GPU_IRQ_CLEAN_CACHES_COMPLETED; |
| 302 | 301 | gpu_write(ptdev, GPU_CMD, GPU_FLUSH_CACHES(l2, lsc, other)); |
| 302 | + } else { | |
| 303 | + ret = -EIO; | |
| 303 | 304 | } |
| 304 | 305 | spin_unlock_irqrestore(&ptdev->gpu->reqs_lock, flags); |
| 305 | 306 | |
| 307 | + if (ret) | |
| 308 | + return ret; | |
| 309 | + | |
| 306 | 310 | if (!wait_event_timeout(ptdev->gpu->reqs_acked, |
| 307 | 311 | !(ptdev->gpu->pending_reqs & GPU_IRQ_CLEAN_CACHES_COMPLETED), |
| 308 | 312 | msecs_to_jiffies(100))) { |
| 309 | 313 | spin_lock_irqsave(&ptdev->gpu->reqs_lock, flags); |
| 310 | 314 | if ((ptdev->gpu->pending_reqs & GPU_IRQ_CLEAN_CACHES_COMPLETED) != 0 && |
| 311 | 315 | !(gpu_read(ptdev, GPU_INT_RAWSTAT) & GPU_IRQ_CLEAN_CACHES_COMPLETED)) |
| 312 | − timedout = true; | |
| 316 | + ret = -ETIMEDOUT; | |
| 313 | 317 | else |
| 314 | 318 | ptdev->gpu->pending_reqs &= ~GPU_IRQ_CLEAN_CACHES_COMPLETED; |
| 315 | 319 | spin_unlock_irqrestore(&ptdev->gpu->reqs_lock, flags); |
| 316 | 320 | } |
| 317 | 321 | |
| 318 | − if (timedout) { | |
| 322 | + if (ret) { | |
| 323 | + panthor_device_schedule_reset(ptdev); | |
| 319 | 324 | drm_err(&ptdev->base, "Flush caches timeout"); |
| 320 | − return -ETIMEDOUT; | |
| 321 | 325 | } |
| 322 | 326 | |
| 323 | − return 0; | |
| 327 | + return ret; | |
| 324 | 328 | } |
| 325 | 329 | |
| 326 | 330 | /** |
| @@ -360,6 +364,7 @@ int panthor_gpu_soft_reset(struct panthor_device *ptdev) · 33 unmodified lines | ||
| 360 | 364 | return -ETIMEDOUT; |
| 361 | 365 | } |
| 362 | 366 | |
| 367 | + ptdev->gpu->pending_reqs = 0; | |
| 363 | 368 | return 0; |
| 364 | 369 | } |
| 365 | 370 | |
| @@ -375,6 +375,15 @@ struct panthor_vm { · 374 unmodified lines | ||
| 375 | 375 | * flagged as faulty as a result. |
| 376 | 376 | */ |
| 377 | 377 | bool unhandled_fault; |
| 378 | + | |
| 379 | + /** @locked_region: Information about the currently locked region currently. */ | |
| 380 | + struct { | |
| 381 | + /** @locked_region.start: Start of the locked region. */ | |
| 382 | + u64 start; | |
| 383 | + | |
| 384 | + /** @locked_region.size: Size of the locked region. */ | |
| 385 | + u64 size; | |
| 386 | + } locked_region; | |
| 378 | 387 | }; |
| 379 | 388 | |
| 380 | 389 | /** |
| @@ -510,27 +519,27 @@ static int wait_ready(struct panthor_device *ptdev, u32 as_nr) · 129 unmodified lines | ||
| 510 | 519 | return ret; |
| 511 | 520 | } |
| 512 | 521 | |
| 513 | −static int write_cmd(struct panthor_device *ptdev, u32 as_nr, u32 cmd) | |
| 522 | +static int as_send_cmd_and_wait(struct panthor_device *ptdev, u32 as_nr, u32 cmd) | |
| 514 | 523 | { |
| 515 | 524 | int status; |
| 516 | 525 | |
| 517 | 526 | /* write AS_COMMAND when MMU is ready to accept another command */ |
| 518 | 527 | status = wait_ready(ptdev, as_nr); |
| 519 | − if (!status) | |
| 528 | + if (!status) { | |
| 520 | 529 | gpu_write(ptdev, AS_COMMAND(as_nr), cmd); |
| 530 | + status = wait_ready(ptdev, as_nr); | |
| 531 | + } | |
| 521 | 532 | |
| 522 | 533 | return status; |
| 523 | 534 | } |
| 524 | 535 | |
| 525 | −static void lock_region(struct panthor_device *ptdev, u32 as_nr, | |
| 526 | − u64 region_start, u64 size) | |
| 536 | +static u64 pack_region_range(struct panthor_device *ptdev, u64 region_start, u64 size) | |
| 527 | 537 | { |
| 528 | 538 | u8 region_width; |
| 529 | − u64 region; | |
| 530 | 539 | u64 region_end = region_start + size; |
| 531 | 540 | |
| 532 | − if (!size) | |
| 533 | − return; | |
| 541 | + if (drm_WARN_ON_ONCE(&ptdev->base, !size)) | |
| 542 | + return 0; | |
| 534 | 543 | |
| 535 | 544 | /* |
| 536 | 545 | * The locked region is a naturally aligned power of 2 block encoded as |
| @@ -549,106 +558,51 @@ static void lock_region(struct panthor_device *ptdev, u32 as_nr, · 12 unmodified lines | ||
| 549 | 558 | */ |
| 550 | 559 | region_start &= GENMASK_ULL(63, region_width); |
| 551 | 560 | |
| 552 | − region = region_width | region_start; | |
| 553 | − | |
| 554 | − /* Lock the region that needs to be updated */ | |
| 555 | − gpu_write64(ptdev, AS_LOCKADDR(as_nr), region); | |
| 556 | − write_cmd(ptdev, as_nr, AS_COMMAND_LOCK); | |
| 557 | −} | |
| 558 | − | |
| 559 | −static int mmu_hw_do_operation_locked(struct panthor_device *ptdev, int as_nr, | |
| 560 | − u64 iova, u64 size, u32 op) | |
| 561 | −{ | |
| 562 | − const u32 l2_flush_op = CACHE_CLEAN | CACHE_INV; | |
| 563 | − u32 lsc_flush_op; | |
| 564 | − int ret; | |
| 565 | − | |
| 566 | − lockdep_assert_held(&ptdev->mmu->as.slots_lock); | |
| 567 | − | |
| 568 | − switch (op) { | |
| 569 | − case AS_COMMAND_FLUSH_MEM: | |
| 570 | − lsc_flush_op = CACHE_CLEAN | CACHE_INV; | |
| 571 | − break; | |
| 572 | − case AS_COMMAND_FLUSH_PT: | |
| 573 | − lsc_flush_op = 0; | |
| 574 | − break; | |
| 575 | − default: | |
| 576 | − drm_WARN(&ptdev->base, 1, "Unexpected AS_COMMAND: %d", op); | |
| 577 | − return -EINVAL; | |
| 578 | − } | |
| 579 | − | |
| 580 | − if (as_nr < 0) | |
| 581 | − return 0; | |
| 582 | − | |
| 583 | − /* | |
| 584 | − * If the AS number is greater than zero, then we can be sure | |
| 585 | − * the device is up and running, so we don't need to explicitly | |
| 586 | − * power it up | |
| 587 | − */ | |
| 588 | − | |
| 589 | − lock_region(ptdev, as_nr, iova, size); | |
| 590 | − | |
| 591 | − ret = wait_ready(ptdev, as_nr); | |
| 592 | − if (ret) | |
| 593 | − return ret; | |
| 594 | − | |
| 595 | − ret = panthor_gpu_flush_caches(ptdev, l2_flush_op, lsc_flush_op, 0); | |
| 596 | − if (ret) | |
| 597 | − return ret; | |
| 598 | − | |
| 599 | − /* | |
| 600 | − * Explicitly unlock the region as the AS is not unlocked automatically | |
| 601 | − * at the end of the GPU_CONTROL cache flush command, unlike | |
| 602 | − * AS_COMMAND_FLUSH_MEM or AS_COMMAND_FLUSH_PT. | |
| 603 | − */ | |
| 604 | − write_cmd(ptdev, as_nr, AS_COMMAND_UNLOCK); | |
| 605 | − | |
| 606 | − /* Wait for the unlock command to complete */ | |
| 607 | − return wait_ready(ptdev, as_nr); | |
| 608 | −} | |
| 609 | − | |
| 610 | −static int mmu_hw_do_operation(struct panthor_vm *vm, | |
| 611 | − u64 iova, u64 size, u32 op) | |
| 612 | −{ | |
| 613 | − struct panthor_device *ptdev = vm->ptdev; | |
| 614 | − int ret; | |
| 615 | − | |
| 616 | − mutex_lock(&ptdev->mmu->as.slots_lock); | |
| 617 | − ret = mmu_hw_do_operation_locked(ptdev, vm->as.id, iova, size, op); | |
| 618 | − mutex_unlock(&ptdev->mmu->as.slots_lock); | |
| 619 | − | |
| 620 | − return ret; | |
| 561 | + return region_width | region_start; | |
| 621 | 562 | } |
| 622 | 563 | |
| 623 | 564 | static int panthor_mmu_as_enable(struct panthor_device *ptdev, u32 as_nr, |
| 624 | 565 | u64 transtab, u64 transcfg, u64 memattr) |
| 625 | 566 | { |
| 626 | − int ret; | |
| 627 | − | |
| 628 | − ret = mmu_hw_do_operation_locked(ptdev, as_nr, 0, ~0ULL, AS_COMMAND_FLUSH_MEM); | |
| 629 | − if (ret) | |
| 630 | − return ret; | |
| 631 | − | |
| 632 | 567 | gpu_write64(ptdev, AS_TRANSTAB(as_nr), transtab); |
| 633 | 568 | gpu_write64(ptdev, AS_MEMATTR(as_nr), memattr); |
| 634 | 569 | gpu_write64(ptdev, AS_TRANSCFG(as_nr), transcfg); |
| 635 | 570 | |
| 636 | − return write_cmd(ptdev, as_nr, AS_COMMAND_UPDATE); | |
| 571 | + return as_send_cmd_and_wait(ptdev, as_nr, AS_COMMAND_UPDATE); | |
| 637 | 572 | } |
| 638 | 573 | |
| 639 | −static int panthor_mmu_as_disable(struct panthor_device *ptdev, u32 as_nr) | |
| 574 | +static int panthor_mmu_as_disable(struct panthor_device *ptdev, u32 as_nr, | |
| 575 | + bool recycle_slot) | |
| 640 | 576 | { |
| 577 | + struct panthor_vm *vm = ptdev->mmu->as.slots[as_nr].vm; | |
| 641 | 578 | int ret; |
| 642 | 579 | |
| 643 | − ret = mmu_hw_do_operation_locked(ptdev, as_nr, 0, ~0ULL, AS_COMMAND_FLUSH_MEM); | |
| 580 | + lockdep_assert_held(&ptdev->mmu->as.slots_lock); | |
| 581 | + | |
| 582 | + /* Flush+invalidate RW caches, invalidate RO ones. */ | |
| 583 | + ret = panthor_gpu_flush_caches(ptdev, CACHE_CLEAN | CACHE_INV, | |
| 584 | + CACHE_CLEAN | CACHE_INV, CACHE_INV); | |
| 644 | 585 | if (ret) |
| 645 | 586 | return ret; |
| 646 | 587 | |
| 588 | + if (vm && vm->locked_region.size) { | |
| 589 | + /* Unlock the region if there's a lock pending. */ | |
| 590 | + ret = as_send_cmd_and_wait(ptdev, vm->as.id, AS_COMMAND_UNLOCK); | |
| 591 | + if (ret) | |
| 592 | + return ret; | |
| 593 | + } | |
| 594 | + | |
| 595 | + /* If the slot is going to be used immediately, don't bother changing | |
| 596 | + * the config. | |
| 597 | + */ | |
| 598 | + if (recycle_slot) | |
| 599 | + return 0; | |
| 600 | + | |
| 647 | 601 | gpu_write64(ptdev, AS_TRANSTAB(as_nr), 0); |
| 648 | 602 | gpu_write64(ptdev, AS_MEMATTR(as_nr), 0); |
| 649 | 603 | gpu_write64(ptdev, AS_TRANSCFG(as_nr), AS_TRANSCFG_ADRMODE_UNMAPPED); |
| 650 | 604 | |
| 651 | − return write_cmd(ptdev, as_nr, AS_COMMAND_UPDATE); | |
| 605 | + return as_send_cmd_and_wait(ptdev, as_nr, AS_COMMAND_UPDATE); | |
| 652 | 606 | } |
| 653 | 607 | |
| 654 | 608 | static u32 panthor_mmu_fault_mask(struct panthor_device *ptdev, u32 value) |
| @@ -722,6 +676,10 @@ int panthor_vm_active(struct panthor_vm *vm) · 67 unmodified lines | ||
| 722 | 676 | if (refcount_inc_not_zero(&vm->as.active_cnt)) |
| 723 | 677 | goto out_dev_exit; |
| 724 | 678 | |
| 679 | + /* Make sure we don't race with lock/unlock_region() calls | |
| 680 | + * happening around VM bind operations. | |
| 681 | + */ | |
| 682 | + mutex_lock(&vm->op_lock); | |
| 725 | 683 | mutex_lock(&ptdev->mmu->as.slots_lock); |
| 726 | 684 | |
| 727 | 685 | if (refcount_inc_not_zero(&vm->as.active_cnt)) |
| @@ -759,6 +717,11 @@ int panthor_vm_active(struct panthor_vm *vm) · 31 unmodified lines | ||
| 759 | 717 | |
| 760 | 718 | drm_WARN_ON(&ptdev->base, refcount_read(&lru_vm->as.active_cnt)); |
| 761 | 719 | as = lru_vm->as.id; |
| 720 | + | |
| 721 | + ret = panthor_mmu_as_disable(ptdev, as, true); | |
| 722 | + if (ret) | |
| 723 | + goto out_unlock; | |
| 724 | + | |
| 762 | 725 | panthor_vm_release_as_locked(lru_vm); |
| 763 | 726 | } |
| 764 | 727 | |
| @@ -789,6 +752,10 @@ out_enable_as: · 24 unmodified lines | ||
| 789 | 752 | gpu_write(ptdev, MMU_INT_MASK, ~ptdev->mmu->as.faulty_mask); |
| 790 | 753 | } |
| 791 | 754 | |
| 755 | + /* The VM update is guarded by ::op_lock, which we take at the beginning | |
| 756 | + * of this function, so we don't expect any locked region here. | |
| 757 | + */ | |
| 758 | + drm_WARN_ON(&vm->ptdev->base, vm->locked_region.size > 0); | |
| 792 | 759 | ret = panthor_mmu_as_enable(vm->ptdev, vm->as.id, transtab, transcfg, vm->memattr); |
| 793 | 760 | |
| 794 | 761 | out_make_active: |
| @@ -799,6 +766,7 @@ out_make_active: · 4 unmodified lines | ||
| 799 | 766 | |
| 800 | 767 | out_unlock: |
| 801 | 768 | mutex_unlock(&ptdev->mmu->as.slots_lock); |
| 769 | + mutex_unlock(&vm->op_lock); | |
| 802 | 770 | |
| 803 | 771 | out_dev_exit: |
| 804 | 772 | drm_dev_exit(cookie); |
| @@ -882,44 +850,64 @@ static size_t get_pgsize(u64 addr, size_t size, size_t *count) · 77 unmodified lines | ||
| 882 | 850 | return SZ_2M; |
| 883 | 851 | } |
| 884 | 852 | |
| 885 | −static int panthor_vm_flush_range(struct panthor_vm *vm, u64 iova, u64 size) | |
| 853 | +static void panthor_vm_declare_unusable(struct panthor_vm *vm) | |
| 886 | 854 | { |
| 887 | 855 | struct panthor_device *ptdev = vm->ptdev; |
| 888 | − int ret = 0, cookie; | |
| 889 | − | |
| 890 | − if (vm->as.id < 0) | |
| 891 | − return 0; | |
| 856 | + int cookie; | |
| 892 | 857 | |
| 893 | − /* If the device is unplugged, we just silently skip the flush. */ | |
| 894 | − if (!drm_dev_enter(&ptdev->base, &cookie)) | |
| 895 | − return 0; | |
| 896 | − | |
| 897 | − ret = mmu_hw_do_operation(vm, iova, size, AS_COMMAND_FLUSH_PT); | |
| 858 | + if (vm->unusable) | |
| 859 | + return; | |
| 898 | 860 | |
| 899 | − drm_dev_exit(cookie); | |
| 900 | − return ret; | |
| 861 | + vm->unusable = true; | |
| 862 | + mutex_lock(&ptdev->mmu->as.slots_lock); | |
| 863 | + if (vm->as.id >= 0 && drm_dev_enter(&ptdev->base, &cookie)) { | |
| 864 | + panthor_mmu_as_disable(ptdev, vm->as.id, false); | |
| 865 | + drm_dev_exit(cookie); | |
| 866 | + } | |
| 867 | + mutex_unlock(&ptdev->mmu->as.slots_lock); | |
| 901 | 868 | } |
| 902 | 869 | |
| 903 | −static int panthor_vm_unmap_pages(struct panthor_vm *vm, u64 iova, u64 size) | |
| 870 | +static void panthor_vm_unmap_pages(struct panthor_vm *vm, u64 iova, u64 size) | |
| 904 | 871 | { |
| 905 | 872 | struct panthor_device *ptdev = vm->ptdev; |
| 906 | 873 | struct io_pgtable_ops *ops = vm->pgtbl_ops; |
| 907 | 874 | u64 start_iova = iova; |
| 908 | 875 | u64 offset = 0; |
| 909 | 876 | |
| 877 | + if (!size) | |
| 878 | + return; | |
| 879 | + | |
| 880 | + drm_WARN_ON(&ptdev->base, | |
| 881 | + (iova < vm->locked_region.start) || | |
| 882 | + (iova + size > vm->locked_region.start + vm->locked_region.size)); | |
| 883 | + | |
| 910 | 884 | while (offset < size) { |
| 911 | 885 | size_t unmapped_sz = 0, pgcount; |
| 912 | 886 | size_t pgsize = get_pgsize(iova + offset, size - offset, &pgcount); |
| 913 | 887 | |
| 914 | 888 | unmapped_sz = ops->unmap_pages(ops, iova + offset, pgsize, pgcount, NULL); |
| 889 | + if (drm_WARN_ON_ONCE(&ptdev->base, unmapped_sz != pgsize * pgcount)) { | |
| 890 | + /* Gracefully handle sparsely unmapped regions to avoid leaving | |
| 891 | + * page table pages behind when the drm_gpuvm and VM page table | |
| 892 | + * are out-of-sync. This is not supposed to happen, hence the | |
| 893 | + * above WARN_ON(). | |
| 894 | + */ | |
| 895 | + while (!ops->iova_to_phys(ops, iova + unmapped_sz) && | |
| 896 | + unmapped_sz < pgsize * pgcount) | |
| 897 | + unmapped_sz += SZ_4K; | |
| 898 | + | |
| 899 | + /* We're passed the point where we can try to fix things, | |
| 900 | + * so flag the VM unusable to make sure it's not going | |
| 901 | + * to be used anymore. | |
| 902 | + */ | |
| 903 | + panthor_vm_declare_unusable(vm); | |
| 915 | 904 | |
| 916 | − if (drm_WARN_ON(&ptdev->base, unmapped_sz != pgsize * pgcount)) { | |
| 917 | − drm_err(&ptdev->base, "failed to unmap range %llx-%llx (requested range %llx-%llx)\n", | |
| 918 | − iova + offset + unmapped_sz, | |
| 919 | − iova + offset + pgsize * pgcount, | |
| 920 | − iova, iova + size); | |
| 921 | − panthor_vm_flush_range(vm, iova, offset + unmapped_sz); | |
| 922 | − return -EINVAL; | |
| 905 | + /* If we don't make progress, we're screwed. That also means | |
| 906 | + * something else prevents us from unmapping the region, but | |
| 907 | + * there's not much we can do here: time for debugging. | |
| 908 | + */ | |
| 909 | + if (drm_WARN_ON_ONCE(&ptdev->base, !unmapped_sz)) | |
| 910 | + return; | |
| 923 | 911 | } |
| 924 | 912 | |
| 925 | 913 | drm_dbg(&ptdev->base, |
| @@ -929,8 +917,6 @@ static int panthor_vm_unmap_pages(struct panthor_vm *vm, u64 iova, u64 size) · 3 unmodified lines | ||
| 929 | 917 | |
| 930 | 918 | offset += unmapped_sz; |
| 931 | 919 | } |
| 932 | − | |
| 933 | − return panthor_vm_flush_range(vm, iova, size); | |
| 934 | 920 | } |
| 935 | 921 | |
| 936 | 922 | static int |
| @@ -948,6 +934,10 @@ panthor_vm_map_pages(struct panthor_vm *vm, u64 iova, int prot, · 11 unmodified lines | ||
| 948 | 934 | if (!size) |
| 949 | 935 | return 0; |
| 950 | 936 | |
| 937 | + drm_WARN_ON(&ptdev->base, | |
| 938 | + (iova < vm->locked_region.start) || | |
| 939 | + (iova + size > vm->locked_region.start + vm->locked_region.size)); | |
| 940 | + | |
| 951 | 941 | for_each_sgtable_dma_sg(sgt, sgl, count) { |
| 952 | 942 | dma_addr_t paddr = sg_dma_address(sgl); |
| 953 | 943 | size_t len = sg_dma_len(sgl); |
| @@ -978,16 +968,17 @@ panthor_vm_map_pages(struct panthor_vm *vm, u64 iova, int prot, · 24 unmodified lines | ||
| 978 | 968 | paddr += mapped; |
| 979 | 969 | len -= mapped; |
| 980 | 970 | |
| 981 | − if (drm_WARN_ON(&ptdev->base, !ret && !mapped)) | |
| 971 | + /* If nothing was mapped, consider it an ENOMEM. */ | |
| 972 | + if (!ret && !mapped) | |
| 982 | 973 | ret = -ENOMEM; |
| 983 | 974 | |
| 984 | − if (ret) { | |
| 985 | − /* If something failed, unmap what we've already mapped before | |
| 986 | − * returning. The unmap call is not supposed to fail. | |
| 975 | + /* If something fails, we stop there, and flag the VM unusable. */ | |
| 976 | + if (drm_WARN_ON_ONCE(&ptdev->base, ret)) { | |
| 977 | + /* Unmap what we've already mapped to avoid leaving page | |
| 978 | + * table pages behind. | |
| 987 | 979 | */ |
| 988 | − drm_WARN_ON(&ptdev->base, | |
| 989 | − panthor_vm_unmap_pages(vm, start_iova, | |
| 990 | − iova - start_iova)); | |
| 980 | + panthor_vm_unmap_pages(vm, start_iova, iova - start_iova); | |
| 981 | + panthor_vm_declare_unusable(vm); | |
| 991 | 982 | return ret; |
| 992 | 983 | } |
| 993 | 984 | } |
| @@ -998,7 +989,7 @@ panthor_vm_map_pages(struct panthor_vm *vm, u64 iova, int prot, · 4 unmodified lines | ||
| 998 | 989 | offset = 0; |
| 999 | 990 | } |
| 1000 | 991 | |
| 1001 | − return panthor_vm_flush_range(vm, start_iova, iova - start_iova); | |
| 992 | + return 0; | |
| 1002 | 993 | } |
| 1003 | 994 | |
| 1004 | 995 | static int flags_to_prot(u32 flags) |
| @@ -1641,6 +1632,62 @@ static const char *access_type_name(struct panthor_device *ptdev, · 636 unmodified lines | ||
| 1641 | 1632 | } |
| 1642 | 1633 | } |
| 1643 | 1634 | |
| 1635 | +static int panthor_vm_lock_region(struct panthor_vm *vm, u64 start, u64 size) | |
| 1636 | +{ | |
| 1637 | + struct panthor_device *ptdev = vm->ptdev; | |
| 1638 | + int ret = 0; | |
| 1639 | + | |
| 1640 | + mutex_lock(&ptdev->mmu->as.slots_lock); | |
| 1641 | + drm_WARN_ON(&ptdev->base, vm->locked_region.start || vm->locked_region.size); | |
| 1642 | + if (vm->as.id >= 0 && size) { | |
| 1643 | + /* Lock the region that needs to be updated */ | |
| 1644 | + gpu_write64(ptdev, AS_LOCKADDR(vm->as.id), | |
| 1645 | + pack_region_range(ptdev, start, size)); | |
| 1646 | + | |
| 1647 | + /* If the lock succeeded, update the locked_region info. */ | |
| 1648 | + ret = as_send_cmd_and_wait(ptdev, vm->as.id, AS_COMMAND_LOCK); | |
| 1649 | + } | |
| 1650 | + | |
| 1651 | + if (!ret) { | |
| 1652 | + vm->locked_region.start = start; | |
| 1653 | + vm->locked_region.size = size; | |
| 1654 | + } | |
| 1655 | + mutex_unlock(&ptdev->mmu->as.slots_lock); | |
| 1656 | + | |
| 1657 | + return ret; | |
| 1658 | +} | |
| 1659 | + | |
| 1660 | +static void panthor_vm_unlock_region(struct panthor_vm *vm) | |
| 1661 | +{ | |
| 1662 | + struct panthor_device *ptdev = vm->ptdev; | |
| 1663 | + | |
| 1664 | + mutex_lock(&ptdev->mmu->as.slots_lock); | |
| 1665 | + if (vm->as.id >= 0) { | |
| 1666 | + int ret; | |
| 1667 | + | |
| 1668 | + /* flush+invalidate RW caches and invalidate RO ones. | |
| 1669 | + * TODO: See if we can use FLUSH_PA_RANGE when the physical | |
| 1670 | + * range is narrow enough and the HW supports it. | |
| 1671 | + */ | |
| 1672 | + ret = panthor_gpu_flush_caches(ptdev, CACHE_CLEAN | CACHE_INV, | |
| 1673 | + CACHE_CLEAN | CACHE_INV, | |
| 1674 | + CACHE_INV); | |
| 1675 | + | |
| 1676 | + /* Unlock the region if the flush is effective. */ | |
| 1677 | + if (!ret) | |
| 1678 | + ret = as_send_cmd_and_wait(ptdev, vm->as.id, AS_COMMAND_UNLOCK); | |
| 1679 | + | |
| 1680 | + /* If we fail to flush or unlock the region, schedule a GPU reset | |
| 1681 | + * to unblock the situation. | |
| 1682 | + */ | |
| 1683 | + if (ret) | |
| 1684 | + panthor_device_schedule_reset(ptdev); | |
| 1685 | + } | |
| 1686 | + vm->locked_region.start = 0; | |
| 1687 | + vm->locked_region.size = 0; | |
| 1688 | + mutex_unlock(&ptdev->mmu->as.slots_lock); | |
| 1689 | +} | |
| 1690 | + | |
| 1644 | 1691 | static void panthor_mmu_irq_handler(struct panthor_device *ptdev, u32 status) |
| 1645 | 1692 | { |
| 1646 | 1693 | bool has_unhandled_faults = false; |
| @@ -1701,7 +1748,7 @@ static void panthor_mmu_irq_handler(struct panthor_device *ptdev, u32 status) · 54 unmodified lines | ||
| 1701 | 1748 | ptdev->mmu->as.slots[as].vm->unhandled_fault = true; |
| 1702 | 1749 | |
| 1703 | 1750 | /* Disable the MMU to kill jobs on this AS. */ |
| 1704 | − panthor_mmu_as_disable(ptdev, as); | |
| 1751 | + panthor_mmu_as_disable(ptdev, as, false); | |
| 1705 | 1752 | mutex_unlock(&ptdev->mmu->as.slots_lock); |
| 1706 | 1753 | |
| 1707 | 1754 | status &= ~mask; |
| @@ -1730,7 +1777,8 @@ void panthor_mmu_suspend(struct panthor_device *ptdev) · 22 unmodified lines | ||
| 1730 | 1777 | struct panthor_vm *vm = ptdev->mmu->as.slots[i].vm; |
| 1731 | 1778 | |
| 1732 | 1779 | if (vm) { |
| 1733 | − drm_WARN_ON(&ptdev->base, panthor_mmu_as_disable(ptdev, i)); | |
| 1780 | + drm_WARN_ON(&ptdev->base, | |
| 1781 | + panthor_mmu_as_disable(ptdev, i, false)); | |
| 1734 | 1782 | panthor_vm_release_as_locked(vm); |
| 1735 | 1783 | } |
| 1736 | 1784 | } |
| @@ -1845,12 +1893,13 @@ static void panthor_vm_free(struct drm_gpuvm *gpuvm) · 108 unmodified lines | ||
| 1845 | 1893 | drm_sched_entity_destroy(&vm->entity); |
| 1846 | 1894 | drm_sched_fini(&vm->sched); |
| 1847 | 1895 | |
| 1896 | + mutex_lock(&vm->op_lock); | |
| 1848 | 1897 | mutex_lock(&ptdev->mmu->as.slots_lock); |
| 1849 | 1898 | if (vm->as.id >= 0) { |
| 1850 | 1899 | int cookie; |
| 1851 | 1900 | |
| 1852 | 1901 | if (drm_dev_enter(&ptdev->base, &cookie)) { |
| 1853 | − panthor_mmu_as_disable(ptdev, vm->as.id); | |
| 1902 | + panthor_mmu_as_disable(ptdev, vm->as.id, false); | |
| 1854 | 1903 | drm_dev_exit(cookie); |
| 1855 | 1904 | } |
| 1856 | 1905 | |
| @@ -1859,6 +1908,7 @@ static void panthor_vm_free(struct drm_gpuvm *gpuvm) · 2 unmodified lines | ||
| 1859 | 1908 | list_del(&vm->as.lru_node); |
| 1860 | 1909 | } |
| 1861 | 1910 | mutex_unlock(&ptdev->mmu->as.slots_lock); |
| 1911 | + mutex_unlock(&vm->op_lock); | |
| 1862 | 1912 | |
| 1863 | 1913 | free_io_pgtable_ops(vm->pgtbl_ops); |
| 1864 | 1914 | |
| @@ -2060,12 +2110,9 @@ static int panthor_gpuva_sm_step_remap(struct drm_gpuva_op *op, · 195 unmodified lines | ||
| 2060 | 2110 | struct panthor_vm_op_ctx *op_ctx = vm->op_ctx; |
| 2061 | 2111 | struct panthor_vma *prev_vma = NULL, *next_vma = NULL; |
| 2062 | 2112 | u64 unmap_start, unmap_range; |
| 2063 | − int ret; | |
| 2064 | 2113 | |
| 2065 | 2114 | drm_gpuva_op_remap_to_unmap_range(&op->remap, &unmap_start, &unmap_range); |
| 2066 | − ret = panthor_vm_unmap_pages(vm, unmap_start, unmap_range); | |
| 2067 | − if (ret) | |
| 2068 | − return ret; | |
| 2115 | + panthor_vm_unmap_pages(vm, unmap_start, unmap_range); | |
| 2069 | 2116 | |
| 2070 | 2117 | if (op->remap.prev) { |
| 2071 | 2118 | prev_vma = panthor_vm_op_ctx_get_vma(op_ctx); |
| @@ -2103,13 +2150,9 @@ static int panthor_gpuva_sm_step_unmap(struct drm_gpuva_op *op, · 31 unmodified lines | ||
| 2103 | 2150 | { |
| 2104 | 2151 | struct panthor_vma *unmap_vma = container_of(op->unmap.va, struct panthor_vma, base); |
| 2105 | 2152 | struct panthor_vm *vm = priv; |
| 2106 | − int ret; | |
| 2107 | − | |
| 2108 | − ret = panthor_vm_unmap_pages(vm, unmap_vma->base.va.addr, | |
| 2109 | − unmap_vma->base.va.range); | |
| 2110 | − if (drm_WARN_ON(&vm->ptdev->base, ret)) | |
| 2111 | − return ret; | |
| 2112 | 2153 | |
| 2154 | + panthor_vm_unmap_pages(vm, unmap_vma->base.va.addr, | |
| 2155 | + unmap_vma->base.va.range); | |
| 2113 | 2156 | drm_gpuva_unmap(&op->unmap); |
| 2114 | 2157 | panthor_vma_unlink(unmap_vma); |
| 2115 | 2158 | return 0; |
| @@ -2154,6 +2197,11 @@ panthor_vm_exec_op(struct panthor_vm *vm, struct panthor_vm_op_ctx *op, · 38 unmodified lines | ||
| 2154 | 2197 | |
| 2155 | 2198 | mutex_lock(&vm->op_lock); |
| 2156 | 2199 | vm->op_ctx = op; |
| 2200 | + | |
| 2201 | + ret = panthor_vm_lock_region(vm, op->va.addr, op->va.range); | |
| 2202 | + if (ret) | |
| 2203 | + goto out; | |
| 2204 | + | |
| 2157 | 2205 | switch (op_type) { |
| 2158 | 2206 | case DRM_PANTHOR_VM_BIND_OP_TYPE_MAP: { |
| 2159 | 2207 | const struct drm_gpuvm_map_req map_req = { |
| @@ -2181,8 +2229,11 @@ panthor_vm_exec_op(struct panthor_vm *vm, struct panthor_vm_op_ctx *op, · 21 unmodified lines | ||
| 2181 | 2229 | break; |
| 2182 | 2230 | } |
| 2183 | 2231 | |
| 2232 | + panthor_vm_unlock_region(vm); | |
| 2233 | + | |
| 2234 | +out: | |
| 2184 | 2235 | if (ret && flag_vm_unusable_on_failure) |
| 2185 | − vm->unusable = true; | |
| 2236 | + panthor_vm_declare_unusable(vm); | |
| 2186 | 2237 | |
| 2187 | 2238 | vm->op_ctx = NULL; |
| 2188 | 2239 | mutex_unlock(&vm->op_lock); |
| @@ -2694,7 +2745,8 @@ void panthor_mmu_unplug(struct panthor_device *ptdev) · 505 unmodified lines | ||
| 2694 | 2745 | struct panthor_vm *vm = ptdev->mmu->as.slots[i].vm; |
| 2695 | 2746 | |
| 2696 | 2747 | if (vm) { |
| 2697 | − drm_WARN_ON(&ptdev->base, panthor_mmu_as_disable(ptdev, i)); | |
| 2748 | + drm_WARN_ON(&ptdev->base, | |
| 2749 | + panthor_mmu_as_disable(ptdev, i, false)); | |
| 2698 | 2750 | panthor_vm_release_as_locked(vm); |
| 2699 | 2751 | } |
| 2700 | 2752 | } |
| @@ -108,15 +108,6 @@ struct panthor_csg_slot { · 107 unmodified lines | ||
| 108 | 108 | |
| 109 | 109 | /** @priority: Group priority. */ |
| 110 | 110 | u8 priority; |
| 111 | − | |
| 112 | − /** | |
| 113 | − * @idle: True if the group bound to this slot is idle. | |
| 114 | − * | |
| 115 | − * A group is idle when it has nothing waiting for execution on | |
| 116 | − * all its queues, or when queues are blocked waiting for something | |
| 117 | − * to happen (synchronization object). | |
| 118 | − */ | |
| 119 | − bool idle; | |
| 120 | 111 | }; |
| 121 | 112 | |
| 122 | 113 | /** |
| @@ -878,8 +869,11 @@ panthor_queue_get_syncwait_obj(struct panthor_group *group, struct panthor_queue · 755 unmodified lines | ||
| 878 | 869 | struct iosys_map map; |
| 879 | 870 | int ret; |
| 880 | 871 | |
| 881 | − if (queue->syncwait.kmap) | |
| 882 | − return queue->syncwait.kmap + queue->syncwait.offset; | |
| 872 | + if (queue->syncwait.kmap) { | |
| 873 | + bo = container_of(queue->syncwait.obj, | |
| 874 | + struct panthor_gem_object, base.base); | |
| 875 | + goto out_sync; | |
| 876 | + } | |
| 883 | 877 | |
| 884 | 878 | bo = panthor_vm_get_bo_for_va(group->vm, |
| 885 | 879 | queue->syncwait.gpu_va, |
| @@ -896,6 +890,17 @@ panthor_queue_get_syncwait_obj(struct panthor_group *group, struct panthor_queue · 10 unmodified lines | ||
| 896 | 890 | if (drm_WARN_ON(&ptdev->base, !queue->syncwait.kmap)) |
| 897 | 891 | goto err_put_syncwait_obj; |
| 898 | 892 | |
| 893 | +out_sync: | |
| 894 | + /* Make sure the CPU caches are invalidated before the seqno is read. | |
| 895 | + * drm_gem_shmem_sync() is a NOP if map_wc=true, so no need to check | |
| 896 | + * it here. | |
| 897 | + */ | |
| 898 | + panthor_gem_sync(&bo->base.base, queue->syncwait.offset, | |
| 899 | + queue->syncwait.sync64 ? | |
| 900 | + sizeof(struct panthor_syncobj_64b) : | |
| 901 | + sizeof(struct panthor_syncobj_32b), | |
| 902 | + DRM_PANTHOR_BO_SYNC_CPU_CACHE_FLUSH_AND_INVALIDATE); | |
| 903 | + | |
| 899 | 904 | return queue->syncwait.kmap + queue->syncwait.offset; |
| 900 | 905 | |
| 901 | 906 | err_put_syncwait_obj: |
| @@ -908,9 +913,8 @@ static void group_free_queue(struct panthor_group *group, struct panthor_queue * · 6 unmodified lines | ||
| 908 | 913 | if (IS_ERR_OR_NULL(queue)) |
| 909 | 914 | return; |
| 910 | 915 | |
| 911 | − /* This should have been disabled before that point. */ | |
| 912 | − drm_WARN_ON(&group->ptdev->base, | |
| 913 | − disable_delayed_work_sync(&queue->timeout.work)); | |
| 916 | + /* Disable the timeout before tearing down drm_sched components. */ | |
| 917 | + disable_delayed_work_sync(&queue->timeout.work); | |
| 914 | 918 | |
| 915 | 919 | if (queue->entity.fence_context) |
| 916 | 920 | drm_sched_entity_destroy(&queue->entity); |
| @@ -1062,28 +1066,11 @@ group_unbind_locked(struct panthor_group *group) · 145 unmodified lines | ||
| 1062 | 1066 | static bool |
| 1063 | 1067 | group_is_idle(struct panthor_group *group) |
| 1064 | 1068 | { |
| 1065 | − struct panthor_device *ptdev = group->ptdev; | |
| 1066 | − u32 inactive_queues; | |
| 1067 | − | |
| 1068 | − if (group->csg_id >= 0) | |
| 1069 | − return ptdev->scheduler->csg_slots[group->csg_id].idle; | |
| 1069 | + u32 inactive_queues = group->idle_queues | group->blocked_queues; | |
| 1070 | 1070 | |
| 1071 | − inactive_queues = group->idle_queues | group->blocked_queues; | |
| 1072 | 1071 | return hweight32(inactive_queues) == group->queue_count; |
| 1073 | 1072 | } |
| 1074 | 1073 | |
| 1075 | −static void | |
| 1076 | −queue_reset_timeout_locked(struct panthor_queue *queue) | |
| 1077 | −{ | |
| 1078 | − lockdep_assert_held(&queue->fence_ctx.lock); | |
| 1079 | − | |
| 1080 | − if (queue->timeout.remaining != MAX_SCHEDULE_TIMEOUT) { | |
| 1081 | − mod_delayed_work(queue->scheduler.timeout_wq, | |
| 1082 | − &queue->timeout.work, | |
| 1083 | − msecs_to_jiffies(JOB_TIMEOUT_MS)); | |
| 1084 | − } | |
| 1085 | −} | |
| 1086 | − | |
| 1087 | 1074 | static bool |
| 1088 | 1075 | group_can_run(struct panthor_group *group) |
| 1089 | 1076 | { |
| @@ -1100,6 +1087,18 @@ queue_timeout_is_suspended(struct panthor_queue *queue) · 10 unmodified lines | ||
| 1100 | 1087 | return queue->timeout.remaining != MAX_SCHEDULE_TIMEOUT; |
| 1101 | 1088 | } |
| 1102 | 1089 | |
| 1090 | +static void | |
| 1091 | +queue_reset_timeout_locked(struct panthor_queue *queue) | |
| 1092 | +{ | |
| 1093 | + lockdep_assert_held(&queue->fence_ctx.lock); | |
| 1094 | + | |
| 1095 | + if (!queue_timeout_is_suspended(queue)) { | |
| 1096 | + mod_delayed_work(queue->scheduler.timeout_wq, | |
| 1097 | + &queue->timeout.work, | |
| 1098 | + msecs_to_jiffies(JOB_TIMEOUT_MS)); | |
| 1099 | + } | |
| 1100 | +} | |
| 1101 | + | |
| 1103 | 1102 | static void |
| 1104 | 1103 | queue_suspend_timeout_locked(struct panthor_queue *queue) |
| 1105 | 1104 | { |
| @@ -1200,12 +1199,10 @@ cs_slot_prog_locked(struct panthor_device *ptdev, u32 csg_id, u32 cs_id) · 94 unmodified lines | ||
| 1200 | 1199 | panthor_fw_update_reqs(cs_iface, req, |
| 1201 | 1200 | CS_IDLE_SYNC_WAIT | |
| 1202 | 1201 | CS_IDLE_EMPTY | |
| 1203 | − CS_STATE_START | | |
| 1204 | − CS_EXTRACT_EVENT, | |
| 1202 | + CS_STATE_START, | |
| 1205 | 1203 | CS_IDLE_SYNC_WAIT | |
| 1206 | 1204 | CS_IDLE_EMPTY | |
| 1207 | − CS_STATE_MASK | | |
| 1208 | − CS_EXTRACT_EVENT); | |
| 1205 | + CS_STATE_MASK); | |
| 1209 | 1206 | if (queue->iface.input->insert != queue->iface.input->extract) |
| 1210 | 1207 | queue_resume_timeout(queue); |
| 1211 | 1208 | } |
| @@ -1725,17 +1722,6 @@ static bool cs_slot_process_irq_locked(struct panthor_device *ptdev, · 513 unmodified lines | ||
| 1725 | 1722 | return (events & (CS_FAULT | CS_TILER_OOM)) != 0; |
| 1726 | 1723 | } |
| 1727 | 1724 | |
| 1728 | −static void csg_slot_sync_idle_state_locked(struct panthor_device *ptdev, u32 csg_id) | |
| 1729 | −{ | |
| 1730 | − struct panthor_csg_slot *csg_slot = &ptdev->scheduler->csg_slots[csg_id]; | |
| 1731 | − struct panthor_fw_csg_iface *csg_iface; | |
| 1732 | − | |
| 1733 | − lockdep_assert_held(&ptdev->scheduler->lock); | |
| 1734 | − | |
| 1735 | − csg_iface = panthor_fw_get_csg_iface(ptdev, csg_id); | |
| 1736 | − csg_slot->idle = csg_iface->output->status_state & CSG_STATUS_STATE_IS_IDLE; | |
| 1737 | −} | |
| 1738 | − | |
| 1739 | 1725 | static void csg_slot_process_idle_event_locked(struct panthor_device *ptdev, u32 csg_id) |
| 1740 | 1726 | { |
| 1741 | 1727 | struct panthor_scheduler *sched = ptdev->scheduler; |
| @@ -1997,10 +1983,8 @@ static int csgs_upd_ctx_apply_locked(struct panthor_device *ptdev, · 255 unmodified lines | ||
| 1997 | 1983 | if (acked & CSG_STATE_MASK) |
| 1998 | 1984 | csg_slot_sync_state_locked(ptdev, csg_id); |
| 1999 | 1985 | |
| 2000 | − if (acked & CSG_STATUS_UPDATE) { | |
| 1986 | + if (acked & CSG_STATUS_UPDATE) | |
| 2001 | 1987 | csg_slot_sync_queues_state_locked(ptdev, csg_id); |
| 2002 | − csg_slot_sync_idle_state_locked(ptdev, csg_id); | |
| 2003 | − } | |
| 2004 | 1988 | |
| 2005 | 1989 | if (ret && acked != req_mask && |
| 2006 | 1990 | ((csg_iface->input->req ^ csg_iface->output->ack) & req_mask) != 0) { |
| @@ -2020,10 +2004,10 @@ struct panthor_sched_tick_ctx { · 13 unmodified lines | ||
| 2020 | 2004 | struct list_head groups[PANTHOR_CSG_PRIORITY_COUNT]; |
| 2021 | 2005 | u32 idle_group_count; |
| 2022 | 2006 | u32 group_count; |
| 2023 | − enum panthor_csg_priority min_priority; | |
| 2024 | 2007 | struct panthor_vm *vms[MAX_CS_PER_CSG]; |
| 2025 | 2008 | u32 as_count; |
| 2026 | 2009 | bool immediate_tick; |
| 2010 | + bool stop_tick; | |
| 2027 | 2011 | u32 csg_upd_failed_mask; |
| 2028 | 2012 | }; |
| 2029 | 2013 | |
| @@ -2066,17 +2050,21 @@ tick_ctx_pick_groups_from_list(const struct panthor_scheduler *sched, · 36 unmodified lines | ||
| 2066 | 2050 | if (!owned_by_tick_ctx) |
| 2067 | 2051 | group_get(group); |
| 2068 | 2052 | |
| 2069 | − list_move_tail(&group->run_node, &ctx->groups[group->priority]); | |
| 2070 | 2053 | ctx->group_count++; |
| 2054 | + | |
| 2055 | + /* If we have more than one active group with the same priority, | |
| 2056 | + * we need to keep ticking to rotate the CSG priority. | |
| 2057 | + */ | |
| 2071 | 2058 | if (group_is_idle(group)) |
| 2072 | 2059 | ctx->idle_group_count++; |
| 2060 | + else if (!list_empty(&ctx->groups[group->priority])) | |
| 2061 | + ctx->stop_tick = false; | |
| 2062 | + | |
| 2063 | + list_move_tail(&group->run_node, &ctx->groups[group->priority]); | |
| 2073 | 2064 | |
| 2074 | 2065 | if (i == ctx->as_count) |
| 2075 | 2066 | ctx->vms[ctx->as_count++] = group->vm; |
| 2076 | 2067 | |
| 2077 | − if (ctx->min_priority > group->priority) | |
| 2078 | − ctx->min_priority = group->priority; | |
| 2079 | − | |
| 2080 | 2068 | if (tick_ctx_is_full(sched, ctx)) |
| 2081 | 2069 | return; |
| 2082 | 2070 | } |
| @@ -2085,31 +2073,22 @@ tick_ctx_pick_groups_from_list(const struct panthor_scheduler *sched, · 2 unmodified lines | ||
| 2085 | 2073 | static void |
| 2086 | 2074 | tick_ctx_insert_old_group(struct panthor_scheduler *sched, |
| 2087 | 2075 | struct panthor_sched_tick_ctx *ctx, |
| 2088 | − struct panthor_group *group, | |
| 2089 | − bool full_tick) | |
| 2076 | + struct panthor_group *group) | |
| 2090 | 2077 | { |
| 2091 | 2078 | struct panthor_csg_slot *csg_slot = &sched->csg_slots[group->csg_id]; |
| 2092 | 2079 | struct panthor_group *other_group; |
| 2093 | 2080 | |
| 2094 | − if (!full_tick) { | |
| 2095 | − list_add_tail(&group->run_node, &ctx->old_groups[group->priority]); | |
| 2096 | − return; | |
| 2097 | − } | |
| 2098 | − | |
| 2099 | − /* Rotate to make sure groups with lower CSG slot | |
| 2100 | − * priorities have a chance to get a higher CSG slot | |
| 2101 | − * priority next time they get picked. This priority | |
| 2102 | − * has an impact on resource request ordering, so it's | |
| 2103 | − * important to make sure we don't let one group starve | |
| 2104 | − * all other groups with the same group priority. | |
| 2105 | − */ | |
| 2081 | + /* Class groups in descending priority order so we can easily rotate. */ | |
| 2106 | 2082 | list_for_each_entry(other_group, |
| 2107 | 2083 | &ctx->old_groups[csg_slot->group->priority], |
| 2108 | 2084 | run_node) { |
| 2109 | 2085 | struct panthor_csg_slot *other_csg_slot = &sched->csg_slots[other_group->csg_id]; |
| 2110 | 2086 | |
| 2111 | − if (other_csg_slot->priority > csg_slot->priority) { | |
| 2112 | − list_add_tail(&csg_slot->group->run_node, &other_group->run_node); | |
| 2087 | + /* Our group has a higher prio than the one we're testing against, | |
| 2088 | + * place it just before. | |
| 2089 | + */ | |
| 2090 | + if (csg_slot->priority > other_csg_slot->priority) { | |
| 2091 | + list_add_tail(&group->run_node, &other_group->run_node); | |
| 2113 | 2092 | return; |
| 2114 | 2093 | } |
| 2115 | 2094 | } |
| @@ -2119,8 +2098,7 @@ tick_ctx_insert_old_group(struct panthor_scheduler *sched, · 3 unmodified lines | ||
| 2119 | 2098 | |
| 2120 | 2099 | static void |
| 2121 | 2100 | tick_ctx_init(struct panthor_scheduler *sched, |
| 2122 | − struct panthor_sched_tick_ctx *ctx, | |
| 2123 | − bool full_tick) | |
| 2101 | + struct panthor_sched_tick_ctx *ctx) | |
| 2124 | 2102 | { |
| 2125 | 2103 | struct panthor_device *ptdev = sched->ptdev; |
| 2126 | 2104 | struct panthor_csg_slots_upd_ctx upd_ctx; |
| @@ -2130,7 +2108,7 @@ tick_ctx_init(struct panthor_scheduler *sched, · 3 unmodified lines | ||
| 2130 | 2108 | memset(ctx, 0, sizeof(*ctx)); |
| 2131 | 2109 | csgs_upd_ctx_init(&upd_ctx); |
| 2132 | 2110 | |
| 2133 | − ctx->min_priority = PANTHOR_CSG_PRIORITY_COUNT; | |
| 2111 | + ctx->stop_tick = true; | |
| 2134 | 2112 | for (i = 0; i < ARRAY_SIZE(ctx->groups); i++) { |
| 2135 | 2113 | INIT_LIST_HEAD(&ctx->groups[i]); |
| 2136 | 2114 | INIT_LIST_HEAD(&ctx->old_groups[i]); |
| @@ -2158,7 +2136,7 @@ tick_ctx_init(struct panthor_scheduler *sched, · 21 unmodified lines | ||
| 2158 | 2136 | group->fatal_queues |= GENMASK(group->queue_count - 1, 0); |
| 2159 | 2137 | } |
| 2160 | 2138 | |
| 2161 | − tick_ctx_insert_old_group(sched, ctx, group, full_tick); | |
| 2139 | + tick_ctx_insert_old_group(sched, ctx, group); | |
| 2162 | 2140 | csgs_upd_ctx_queue_reqs(ptdev, &upd_ctx, i, |
| 2163 | 2141 | csg_iface->output->ack ^ CSG_STATUS_UPDATE, |
| 2164 | 2142 | CSG_STATUS_UPDATE); |
| @@ -2442,32 +2420,18 @@ static u64 · 277 unmodified lines | ||
| 2442 | 2420 | tick_ctx_update_resched_target(struct panthor_scheduler *sched, |
| 2443 | 2421 | const struct panthor_sched_tick_ctx *ctx) |
| 2444 | 2422 | { |
| 2445 | − /* We had space left, no need to reschedule until some external event happens. */ | |
| 2446 | − if (!tick_ctx_is_full(sched, ctx)) | |
| 2447 | − goto no_tick; | |
| 2448 | − | |
| 2449 | − /* If idle groups were scheduled, no need to wake up until some external | |
| 2450 | − * event happens (group unblocked, new job submitted, ...). | |
| 2451 | − */ | |
| 2452 | − if (ctx->idle_group_count) | |
| 2453 | − goto no_tick; | |
| 2423 | + u64 resched_target; | |
| 2454 | 2424 | |
| 2455 | − if (drm_WARN_ON(&sched->ptdev->base, ctx->min_priority >= PANTHOR_CSG_PRIORITY_COUNT)) | |
| 2425 | + if (ctx->stop_tick) | |
| 2456 | 2426 | goto no_tick; |
| 2457 | 2427 | |
| 2458 | − /* If there are groups of the same priority waiting, we need to | |
| 2459 | − * keep the scheduler ticking, otherwise, we'll just wait for | |
| 2460 | − * new groups with higher priority to be queued. | |
| 2461 | − */ | |
| 2462 | − if (!list_empty(&sched->groups.runnable[ctx->min_priority])) { | |
| 2463 | − u64 resched_target = sched->last_tick + sched->tick_period; | |
| 2428 | + resched_target = sched->last_tick + sched->tick_period; | |
| 2464 | 2429 | |
| 2465 | − if (time_before64(sched->resched_target, sched->last_tick) || | |
| 2466 | − time_before64(resched_target, sched->resched_target)) | |
| 2467 | − sched->resched_target = resched_target; | |
| 2430 | + if (time_before64(sched->resched_target, sched->last_tick) || | |
| 2431 | + time_before64(resched_target, sched->resched_target)) | |
| 2432 | + sched->resched_target = resched_target; | |
| 2468 | 2433 | |
| 2469 | − return sched->resched_target - sched->last_tick; | |
| 2470 | − } | |
| 2434 | + return sched->resched_target - sched->last_tick; | |
| 2471 | 2435 | |
| 2472 | 2436 | no_tick: |
| 2473 | 2437 | sched->resched_target = U64_MAX; |
| @@ -2480,9 +2444,11 @@ static void tick_work(struct work_struct *work) · 6 unmodified lines | ||
| 2480 | 2444 | tick_work.work); |
| 2481 | 2445 | struct panthor_device *ptdev = sched->ptdev; |
| 2482 | 2446 | struct panthor_sched_tick_ctx ctx; |
| 2447 | + u64 resched_target = sched->resched_target; | |
| 2483 | 2448 | u64 remaining_jiffies = 0, resched_delay; |
| 2484 | 2449 | u64 now = get_jiffies_64(); |
| 2485 | 2450 | int prio, ret, cookie; |
| 2451 | + bool full_tick; | |
| 2486 | 2452 | |
| 2487 | 2453 | if (!drm_dev_enter(&ptdev->base, &cookie)) |
| 2488 | 2454 | return; |
| @@ -2491,18 +2457,24 @@ static void tick_work(struct work_struct *work) · 2 unmodified lines | ||
| 2491 | 2457 | if (drm_WARN_ON(&ptdev->base, ret)) |
| 2492 | 2458 | goto out_dev_exit; |
| 2493 | 2459 | |
| 2494 | − if (time_before64(now, sched->resched_target)) | |
| 2495 | − remaining_jiffies = sched->resched_target - now; | |
| 2460 | + /* If the tick is stopped, calculate when the next tick would be */ | |
| 2461 | + if (resched_target == U64_MAX) | |
| 2462 | + resched_target = sched->last_tick + sched->tick_period; | |
| 2463 | + | |
| 2464 | + if (time_before64(now, resched_target)) | |
| 2465 | + remaining_jiffies = resched_target - now; | |
| 2466 | + | |
| 2467 | + full_tick = remaining_jiffies == 0; | |
| 2496 | 2468 | |
| 2497 | 2469 | mutex_lock(&sched->lock); |
| 2498 | 2470 | if (panthor_device_reset_is_pending(sched->ptdev)) |
| 2499 | 2471 | goto out_unlock; |
| 2500 | 2472 | |
| 2501 | − tick_ctx_init(sched, &ctx, remaining_jiffies != 0); | |
| 2473 | + tick_ctx_init(sched, &ctx); | |
| 2502 | 2474 | if (ctx.csg_upd_failed_mask) |
| 2503 | 2475 | goto out_cleanup_ctx; |
| 2504 | 2476 | |
| 2505 | − if (remaining_jiffies) { | |
| 2477 | + if (!full_tick) { | |
| 2506 | 2478 | /* Scheduling forced in the middle of a tick. Only RT groups |
| 2507 | 2479 | * can preempt non-RT ones. Currently running RT groups can't be |
| 2508 | 2480 | * preempted. |
| @@ -2524,9 +2496,29 @@ static void tick_work(struct work_struct *work) · 15 unmodified lines | ||
| 2524 | 2496 | for (prio = PANTHOR_CSG_PRIORITY_COUNT - 1; |
| 2525 | 2497 | prio >= 0 && !tick_ctx_is_full(sched, &ctx); |
| 2526 | 2498 | prio--) { |
| 2499 | + struct panthor_group *old_highest_prio_group = | |
| 2500 | + list_first_entry_or_null(&ctx.old_groups[prio], | |
| 2501 | + struct panthor_group, run_node); | |
| 2502 | + | |
| 2503 | + /* Pull out the group with the highest prio for rotation. */ | |
| 2504 | + if (old_highest_prio_group) | |
| 2505 | + list_del(&old_highest_prio_group->run_node); | |
| 2506 | + | |
| 2507 | + /* Re-insert old active groups so they get a chance to run with higher prio. */ | |
| 2508 | + tick_ctx_pick_groups_from_list(sched, &ctx, &ctx.old_groups[prio], true, true); | |
| 2509 | + | |
| 2510 | + /* Fill the remaining slots with runnable groups. */ | |
| 2527 | 2511 | tick_ctx_pick_groups_from_list(sched, &ctx, &sched->groups.runnable[prio], |
| 2528 | 2512 | true, false); |
| 2529 | − tick_ctx_pick_groups_from_list(sched, &ctx, &ctx.old_groups[prio], true, true); | |
| 2513 | + | |
| 2514 | + /* Re-insert the old group with the highest prio, and give it a chance to be | |
| 2515 | + * scheduled again (but with a lower prio) if there's room left. | |
| 2516 | + */ | |
| 2517 | + if (old_highest_prio_group) { | |
| 2518 | + list_add_tail(&old_highest_prio_group->run_node, &ctx.old_groups[prio]); | |
| 2519 | + tick_ctx_pick_groups_from_list(sched, &ctx, &ctx.old_groups[prio], | |
| 2520 | + true, true); | |
| 2521 | + } | |
| 2530 | 2522 | } |
| 2531 | 2523 | |
| 2532 | 2524 | /* If we have free CSG slots left, pick idle groups */ |
| @@ -2651,14 +2643,33 @@ static void sync_upd_work(struct work_struct *work) · 118 unmodified lines | ||
| 2651 | 2643 | sched_queue_delayed_work(sched, tick, 0); |
| 2652 | 2644 | } |
| 2653 | 2645 | |
| 2646 | +static void sched_resume_tick(struct panthor_device *ptdev) | |
| 2647 | +{ | |
| 2648 | + struct panthor_scheduler *sched = ptdev->scheduler; | |
| 2649 | + u64 delay_jiffies, now; | |
| 2650 | + | |
| 2651 | + drm_WARN_ON(&ptdev->base, sched->resched_target != U64_MAX); | |
| 2652 | + | |
| 2653 | + /* Scheduler tick was off, recalculate the resched_target based on the | |
| 2654 | + * last tick event, and queue the scheduler work. | |
| 2655 | + */ | |
| 2656 | + now = get_jiffies_64(); | |
| 2657 | + sched->resched_target = sched->last_tick + sched->tick_period; | |
| 2658 | + if (sched->used_csg_slot_count == sched->csg_slot_count && | |
| 2659 | + time_before64(now, sched->resched_target)) | |
| 2660 | + delay_jiffies = min_t(unsigned long, sched->resched_target - now, ULONG_MAX); | |
| 2661 | + else | |
| 2662 | + delay_jiffies = 0; | |
| 2663 | + | |
| 2664 | + sched_queue_delayed_work(sched, tick, delay_jiffies); | |
| 2665 | +} | |
| 2666 | + | |
| 2654 | 2667 | static void group_schedule_locked(struct panthor_group *group, u32 queue_mask) |
| 2655 | 2668 | { |
| 2656 | 2669 | struct panthor_device *ptdev = group->ptdev; |
| 2657 | 2670 | struct panthor_scheduler *sched = ptdev->scheduler; |
| 2658 | 2671 | struct list_head *queue = &sched->groups.runnable[group->priority]; |
| 2659 | − u64 delay_jiffies = 0; | |
| 2660 | 2672 | bool was_idle; |
| 2661 | − u64 now; | |
| 2662 | 2673 | |
| 2663 | 2674 | if (!group_can_run(group)) |
| 2664 | 2675 | return; |
| @@ -2703,13 +2714,7 @@ static void group_schedule_locked(struct panthor_group *group, u32 queue_mask) · 38 unmodified lines | ||
| 2703 | 2714 | /* Scheduler tick was off, recalculate the resched_target based on the |
| 2704 | 2715 | * last tick event, and queue the scheduler work. |
| 2705 | 2716 | */ |
| 2706 | − now = get_jiffies_64(); | |
| 2707 | − sched->resched_target = sched->last_tick + sched->tick_period; | |
| 2708 | − if (sched->used_csg_slot_count == sched->csg_slot_count && | |
| 2709 | − time_before64(now, sched->resched_target)) | |
| 2710 | − delay_jiffies = min_t(unsigned long, sched->resched_target - now, ULONG_MAX); | |
| 2711 | − | |
| 2712 | − sched_queue_delayed_work(sched, tick, delay_jiffies); | |
| 2717 | + sched_resume_tick(ptdev); | |
| 2713 | 2718 | } |
| 2714 | 2719 | |
| 2715 | 2720 | static void queue_stop(struct panthor_queue *queue, |
| @@ -2767,13 +2772,6 @@ static void panthor_group_start(struct panthor_group *group) · 51 unmodified lines | ||
| 2767 | 2772 | group_put(group); |
| 2768 | 2773 | } |
| 2769 | 2774 | |
| 2770 | −static void panthor_sched_immediate_tick(struct panthor_device *ptdev) | |
| 2771 | −{ | |
| 2772 | − struct panthor_scheduler *sched = ptdev->scheduler; | |
| 2773 | − | |
| 2774 | − sched_queue_delayed_work(sched, tick, 0); | |
| 2775 | −} | |
| 2776 | − | |
| 2777 | 2775 | /** |
| 2778 | 2776 | * panthor_sched_report_mmu_fault() - Report MMU faults to the scheduler. |
| 2779 | 2777 | */ |
| @@ -2781,13 +2779,13 @@ void panthor_sched_report_mmu_fault(struct panthor_device *ptdev) · 1 unmodified line | ||
| 2781 | 2779 | { |
| 2782 | 2780 | /* Force a tick to immediately kill faulty groups. */ |
| 2783 | 2781 | if (ptdev->scheduler) |
| 2784 | − panthor_sched_immediate_tick(ptdev); | |
| 2782 | + sched_queue_delayed_work(ptdev->scheduler, tick, 0); | |
| 2785 | 2783 | } |
| 2786 | 2784 | |
| 2787 | 2785 | void panthor_sched_resume(struct panthor_device *ptdev) |
| 2788 | 2786 | { |
| 2789 | 2787 | /* Force a tick to re-evaluate after a resume. */ |
| 2790 | − panthor_sched_immediate_tick(ptdev); | |
| 2788 | + sched_queue_delayed_work(ptdev->scheduler, tick, 0); | |
| 2791 | 2789 | } |
| 2792 | 2790 | |
| 2793 | 2791 | void panthor_sched_suspend(struct panthor_device *ptdev) |
| @@ -2943,8 +2941,6 @@ void panthor_sched_pre_reset(struct panthor_device *ptdev) · 149 unmodified lines | ||
| 2943 | 2941 | * new jobs while we're resetting. |
| 2944 | 2942 | */ |
| 2945 | 2943 | for (i = 0; i < ARRAY_SIZE(sched->groups.runnable); i++) { |
| 2946 | − /* All groups should be in the idle lists. */ | |
| 2947 | − drm_WARN_ON(&ptdev->base, !list_empty(&sched->groups.runnable[i])); | |
| 2948 | 2944 | list_for_each_entry_safe(group, group_tmp, &sched->groups.runnable[i], run_node) |
| 2949 | 2945 | panthor_group_stop(group); |
| 2950 | 2946 | } |
| @@ -3343,6 +3339,18 @@ queue_run_job(struct drm_sched_job *sched_job) · 392 unmodified lines | ||
| 3343 | 3339 | if (group->csg_id < 0) { |
| 3344 | 3340 | group_schedule_locked(group, BIT(job->queue_idx)); |
| 3345 | 3341 | } else { |
| 3342 | + u32 queue_mask = BIT(job->queue_idx); | |
| 3343 | + bool resume_tick = group_is_idle(group) && | |
| 3344 | + (group->idle_queues & queue_mask) && | |
| 3345 | + !(group->blocked_queues & queue_mask) && | |
| 3346 | + sched->resched_target == U64_MAX; | |
| 3347 | + | |
| 3348 | + /* We just added something to the queue, so it's no longer idle. */ | |
| 3349 | + group->idle_queues &= ~queue_mask; | |
| 3350 | + | |
| 3351 | + if (resume_tick) | |
| 3352 | + sched_resume_tick(ptdev); | |
| 3353 | + | |
| 3346 | 3354 | gpu_write(ptdev, CSF_DOORBELL(queue->doorbell_id), 1); |
| 3347 | 3355 | if (!sched->pm.has_ref && |
| 3348 | 3356 | !(group->blocked_queues & BIT(job->queue_idx))) { |
| @@ -138,7 +138,7 @@ static void pl111_display_enable(struct drm_simple_display_pipe *pipe, · 137 unmodified lines | ||
| 138 | 138 | |
| 139 | 139 | ret = clk_set_rate(priv->clk, mode->clock * 1000); |
| 140 | 140 | if (ret) { |
| 141 | − dev_err(drm->dev, | |
| 141 | + drm_err(drm, | |
| 142 | 142 | "Failed to set pixel clock rate to %d: %d\n", |
| 143 | 143 | mode->clock * 1000, ret); |
| 144 | 144 | } |
| @@ -553,7 +553,7 @@ pl111_init_clock_divider(struct drm_device *drm) · 408 unmodified lines | ||
| 553 | 553 | int ret; |
| 554 | 554 | |
| 555 | 555 | if (IS_ERR(parent)) { |
| 556 | − dev_err(drm->dev, "CLCD: unable to get clcdclk.\n"); | |
| 556 | + drm_err(drm, "CLCD: unable to get clcdclk.\n"); | |
| 557 | 557 | return PTR_ERR(parent); |
| 558 | 558 | } |
| 559 | 559 | |
| @@ -55,6 +55,7 @@ · 54 unmodified lines | ||
| 55 | 55 | #include <drm/drm_gem_framebuffer_helper.h> |
| 56 | 56 | #include <drm/drm_of.h> |
| 57 | 57 | #include <drm/drm_panel.h> |
| 58 | +#include <drm/drm_print.h> | |
| 58 | 59 | #include <drm/drm_probe_helper.h> |
| 59 | 60 | #include <drm/drm_vblank.h> |
| 60 | 61 | |
| @@ -98,7 +99,7 @@ static int pl111_modeset_init(struct drm_device *dev) · 37 unmodified lines | ||
| 98 | 99 | struct drm_panel *tmp_panel; |
| 99 | 100 | struct drm_bridge *tmp_bridge; |
| 100 | 101 | |
| 101 | − dev_dbg(dev->dev, "checking endpoint %d\n", i); | |
| 102 | + drm_dbg(dev, "checking endpoint %d\n", i); | |
| 102 | 103 | |
| 103 | 104 | ret = drm_of_find_panel_or_bridge(dev->dev->of_node, |
| 104 | 105 | 0, i, |
| @@ -114,18 +115,18 @@ static int pl111_modeset_init(struct drm_device *dev) · 9 unmodified lines | ||
| 114 | 115 | defer = true; |
| 115 | 116 | } else if (ret != -ENODEV) { |
| 116 | 117 | /* Continue, maybe something else is working */ |
| 117 | − dev_err(dev->dev, | |
| 118 | + drm_err(dev, | |
| 118 | 119 | "endpoint %d returns %d\n", i, ret); |
| 119 | 120 | } |
| 120 | 121 | } |
| 121 | 122 | |
| 122 | 123 | if (tmp_panel) { |
| 123 | − dev_info(dev->dev, | |
| 124 | + drm_info(dev, | |
| 124 | 125 | "found panel on endpoint %d\n", i); |
| 125 | 126 | panel = tmp_panel; |
| 126 | 127 | } |
| 127 | 128 | if (tmp_bridge) { |
| 128 | − dev_info(dev->dev, | |
| 129 | + drm_info(dev, | |
| 129 | 130 | "found bridge on endpoint %d\n", i); |
| 130 | 131 | bridge = tmp_bridge; |
| 131 | 132 | } |
| @@ -149,9 +150,9 @@ static int pl111_modeset_init(struct drm_device *dev) · 17 unmodified lines | ||
| 149 | 150 | goto finish; |
| 150 | 151 | } |
| 151 | 152 | } else if (bridge) { |
| 152 | − dev_info(dev->dev, "Using non-panel bridge\n"); | |
| 153 | + drm_info(dev, "Using non-panel bridge\n"); | |
| 153 | 154 | } else { |
| 154 | − dev_err(dev->dev, "No bridge, exiting\n"); | |
| 155 | + drm_err(dev, "No bridge, exiting\n"); | |
| 155 | 156 | return -ENODEV; |
| 156 | 157 | } |
| 157 | 158 | |
| @@ -163,7 +164,7 @@ static int pl111_modeset_init(struct drm_device *dev) · 5 unmodified lines | ||
| 163 | 164 | |
| 164 | 165 | ret = pl111_display_init(dev); |
| 165 | 166 | if (ret != 0) { |
| 166 | − dev_err(dev->dev, "Failed to init display\n"); | |
| 167 | + drm_err(dev, "Failed to init display\n"); | |
| 167 | 168 | goto out_bridge; |
| 168 | 169 | } |
| 169 | 170 | |
| @@ -175,7 +176,7 @@ static int pl111_modeset_init(struct drm_device *dev) · 5 unmodified lines | ||
| 175 | 176 | if (!priv->variant->broken_vblank) { |
| 176 | 177 | ret = drm_vblank_init(dev, 1); |
| 177 | 178 | if (ret != 0) { |
| 178 | − dev_err(dev->dev, "Failed to init vblank\n"); | |
| 179 | + drm_err(dev, "Failed to init vblank\n"); | |
| 179 | 180 | goto out_bridge; |
| 180 | 181 | } |
| 181 | 182 | } |
| @@ -255,13 +256,13 @@ static int pl111_amba_probe(struct amba_device *amba_dev, · 73 unmodified lines | ||
| 255 | 256 | |
| 256 | 257 | ret = of_reserved_mem_device_init(dev); |
| 257 | 258 | if (!ret) { |
| 258 | − dev_info(dev, "using device-specific reserved memory\n"); | |
| 259 | + drm_info(drm, "using device-specific reserved memory\n"); | |
| 259 | 260 | priv->use_device_memory = true; |
| 260 | 261 | } |
| 261 | 262 | |
| 262 | 263 | if (of_property_read_u32(dev->of_node, "max-memory-bandwidth", |
| 263 | 264 | &priv->memory_bw)) { |
| 264 | − dev_info(dev, "no max memory bandwidth specified, assume unlimited\n"); | |
| 265 | + drm_info(drm, "no max memory bandwidth specified, assume unlimited\n"); | |
| 265 | 266 | priv->memory_bw = 0; |
| 266 | 267 | } |
| 267 | 268 | |
| @@ -276,17 +277,17 @@ static int pl111_amba_probe(struct amba_device *amba_dev, · 8 unmodified lines | ||
| 276 | 277 | |
| 277 | 278 | priv->regs = devm_ioremap_resource(dev, &amba_dev->res); |
| 278 | 279 | if (IS_ERR(priv->regs)) { |
| 279 | − dev_err(dev, "%s failed mmio\n", __func__); | |
| 280 | + drm_err(drm, "%s failed mmio\n", __func__); | |
| 280 | 281 | ret = PTR_ERR(priv->regs); |
| 281 | 282 | goto dev_put; |
| 282 | 283 | } |
| 283 | 284 | |
| 284 | 285 | /* This may override some variant settings */ |
| 285 | − ret = pl111_versatile_init(dev, priv); | |
| 286 | + ret = pl111_versatile_init(drm, priv); | |
| 286 | 287 | if (ret) |
| 287 | 288 | goto dev_put; |
| 288 | 289 | |
| 289 | − pl111_nomadik_init(dev); | |
| 290 | + pl111_nomadik_init(drm); | |
| 290 | 291 | |
| 291 | 292 | /* turn off interrupts before requesting the irq */ |
| 292 | 293 | writel(0, priv->regs + priv->ienb); |
| @@ -294,7 +295,7 @@ static int pl111_amba_probe(struct amba_device *amba_dev, · 1 unmodified line | ||
| 294 | 295 | ret = devm_request_irq(dev, amba_dev->irq[0], pl111_irq, 0, |
| 295 | 296 | variant->name, priv); |
| 296 | 297 | if (ret != 0) { |
| 297 | − dev_err(dev, "%s failed irq %d\n", __func__, ret); | |
| 298 | + drm_err(drm, "%s failed irq %d\n", __func__, ret); | |
| 298 | 299 | return ret; |
| 299 | 300 | } |
| 300 | 301 | |
| @@ -9,7 +9,7 @@ · 8 unmodified lines | ||
| 9 | 9 | #define PMU_CTRL_OFFSET 0x0000 |
| 10 | 10 | #define PMU_CTRL_LCDNDIF BIT(26) |
| 11 | 11 | |
| 12 | −void pl111_nomadik_init(struct device *dev) | |
| 12 | +void pl111_nomadik_init(struct drm_device *dev) | |
| 13 | 13 | { |
| 14 | 14 | struct regmap *pmu_regmap; |
| 15 | 15 | |
| @@ -31,6 +31,6 @@ void pl111_nomadik_init(struct device *dev) · 15 unmodified lines | ||
| 31 | 31 | PMU_CTRL_OFFSET, |
| 32 | 32 | PMU_CTRL_LCDNDIF, |
| 33 | 33 | 0); |
| 34 | − dev_info(dev, "set Nomadik PMU mux to CLCD mode\n"); | |
| 34 | + drm_info(dev, "set Nomadik PMU mux to CLCD mode\n"); | |
| 35 | 35 | } |
| 36 | 36 | EXPORT_SYMBOL_GPL(pl111_nomadik_init); |
| @@ -8,11 +8,11 @@ struct device; · 7 unmodified lines | ||
| 8 | 8 | |
| 9 | 9 | #ifdef CONFIG_ARCH_NOMADIK |
| 10 | 10 | |
| 11 | −void pl111_nomadik_init(struct device *dev); | |
| 11 | +void pl111_nomadik_init(struct drm_device *dev); | |
| 12 | 12 | |
| 13 | 13 | #else |
| 14 | 14 | |
| 15 | −static inline void pl111_nomadik_init(struct device *dev) | |
| 15 | +static inline void pl111_nomadik_init(struct drm_device *dev) | |
| 16 | 16 | { |
| 17 | 17 | } |
| 18 | 18 | |
| @@ -20,6 +20,7 @@ · 19 unmodified lines | ||
| 20 | 20 | #include <linux/vexpress.h> |
| 21 | 21 | |
| 22 | 22 | #include <drm/drm_fourcc.h> |
| 23 | +#include <drm/drm_print.h> | |
| 23 | 24 | |
| 24 | 25 | #include "pl111_versatile.h" |
| 25 | 26 | #include "pl111_drm.h" |
| @@ -116,7 +117,7 @@ static void pl111_integrator_enable(struct drm_device *drm, u32 format) · 90 unmodified lines | ||
| 116 | 117 | { |
| 117 | 118 | u32 val; |
| 118 | 119 | |
| 119 | − dev_info(drm->dev, "enable Integrator CLCD connectors\n"); | |
| 120 | + drm_info(drm, "enable Integrator CLCD connectors\n"); | |
| 120 | 121 | |
| 121 | 122 | /* FIXME: really needed? */ |
| 122 | 123 | val = INTEGRATOR_CLCD_LCD_STATIC1 | INTEGRATOR_CLCD_LCD_STATIC2 | |
| @@ -134,7 +135,7 @@ static void pl111_integrator_enable(struct drm_device *drm, u32 format) · 11 unmodified lines | ||
| 134 | 135 | val |= INTEGRATOR_CLCD_LCDMUX_VGA555; |
| 135 | 136 | break; |
| 136 | 137 | default: |
| 137 | − dev_err(drm->dev, "unhandled format on Integrator 0x%08x\n", | |
| 138 | + drm_err(drm, "unhandled format on Integrator 0x%08x\n", | |
| 138 | 139 | format); |
| 139 | 140 | break; |
| 140 | 141 | } |
| @@ -156,7 +157,7 @@ static void pl111_impd1_enable(struct drm_device *drm, u32 format) · 15 unmodified lines | ||
| 156 | 157 | { |
| 157 | 158 | u32 val; |
| 158 | 159 | |
| 159 | − dev_info(drm->dev, "enable IM-PD1 CLCD connectors\n"); | |
| 160 | + drm_info(drm, "enable IM-PD1 CLCD connectors\n"); | |
| 160 | 161 | val = IMPD1_CTRL_DISP_VGA | IMPD1_CTRL_DISP_ENABLE; |
| 161 | 162 | |
| 162 | 163 | regmap_update_bits(versatile_syscon_map, |
| @@ -167,7 +168,7 @@ static void pl111_impd1_enable(struct drm_device *drm, u32 format) · 4 unmodified lines | ||
| 167 | 168 | |
| 168 | 169 | static void pl111_impd1_disable(struct drm_device *drm) |
| 169 | 170 | { |
| 170 | − dev_info(drm->dev, "disable IM-PD1 CLCD connectors\n"); | |
| 171 | + drm_info(drm, "disable IM-PD1 CLCD connectors\n"); | |
| 171 | 172 | |
| 172 | 173 | regmap_update_bits(versatile_syscon_map, |
| 173 | 174 | IMPD1_CTRL_OFFSET, |
| @@ -194,7 +195,7 @@ static void pl111_impd1_disable(struct drm_device *drm) · 20 unmodified lines | ||
| 194 | 195 | |
| 195 | 196 | static void pl111_versatile_disable(struct drm_device *drm) |
| 196 | 197 | { |
| 197 | − dev_info(drm->dev, "disable Versatile CLCD connectors\n"); | |
| 198 | + drm_info(drm, "disable Versatile CLCD connectors\n"); | |
| 198 | 199 | regmap_update_bits(versatile_syscon_map, |
| 199 | 200 | SYS_CLCD, |
| 200 | 201 | SYS_CLCD_CONNECTOR_MASK, |
| @@ -205,7 +206,7 @@ static void pl111_versatile_enable(struct drm_device *drm, u32 format) · 4 unmodified lines | ||
| 205 | 206 | { |
| 206 | 207 | u32 val = 0; |
| 207 | 208 | |
| 208 | − dev_info(drm->dev, "enable Versatile CLCD connectors\n"); | |
| 209 | + drm_info(drm, "enable Versatile CLCD connectors\n"); | |
| 209 | 210 | |
| 210 | 211 | switch (format) { |
| 211 | 212 | case DRM_FORMAT_ABGR8888: |
| @@ -227,7 +228,7 @@ static void pl111_versatile_enable(struct drm_device *drm, u32 format) · 15 unmodified lines | ||
| 227 | 228 | val |= SYS_CLCD_MODE_5551; |
| 228 | 229 | break; |
| 229 | 230 | default: |
| 230 | − dev_err(drm->dev, "unhandled format on Versatile 0x%08x\n", | |
| 231 | + drm_err(drm, "unhandled format on Versatile 0x%08x\n", | |
| 231 | 232 | format); |
| 232 | 233 | break; |
| 233 | 234 | } |
| @@ -247,7 +248,7 @@ static void pl111_versatile_enable(struct drm_device *drm, u32 format) · 13 unmodified lines | ||
| 247 | 248 | |
| 248 | 249 | static void pl111_realview_clcd_disable(struct drm_device *drm) |
| 249 | 250 | { |
| 250 | − dev_info(drm->dev, "disable RealView CLCD connectors\n"); | |
| 251 | + drm_info(drm, "disable RealView CLCD connectors\n"); | |
| 251 | 252 | regmap_update_bits(versatile_syscon_map, |
| 252 | 253 | SYS_CLCD, |
| 253 | 254 | SYS_CLCD_CONNECTOR_MASK, |
| @@ -256,7 +257,7 @@ static void pl111_realview_clcd_disable(struct drm_device *drm) · 2 unmodified lines | ||
| 256 | 257 | |
| 257 | 258 | static void pl111_realview_clcd_enable(struct drm_device *drm, u32 format) |
| 258 | 259 | { |
| 259 | − dev_info(drm->dev, "enable RealView CLCD connectors\n"); | |
| 260 | + drm_info(drm, "enable RealView CLCD connectors\n"); | |
| 260 | 261 | regmap_update_bits(versatile_syscon_map, |
| 261 | 262 | SYS_CLCD, |
| 262 | 263 | SYS_CLCD_CONNECTOR_MASK, |
| @@ -376,7 +377,7 @@ static const struct pl111_variant_data pl111_vexpress = { · 113 unmodified lines | ||
| 376 | 377 | #define VEXPRESS_FPGAMUX_DAUGHTERBOARD_1 0x01 |
| 377 | 378 | #define VEXPRESS_FPGAMUX_DAUGHTERBOARD_2 0x02 |
| 378 | 379 | |
| 379 | −static int pl111_vexpress_clcd_init(struct device *dev, struct device_node *np, | |
| 380 | +static int pl111_vexpress_clcd_init(struct drm_device *dev, struct device_node *np, | |
| 380 | 381 | struct pl111_drm_dev_private *priv) |
| 381 | 382 | { |
| 382 | 383 | struct platform_device *pdev; |
| @@ -433,22 +434,22 @@ static int pl111_vexpress_clcd_init(struct device *dev, struct device_node *np, · 50 unmodified lines | ||
| 433 | 434 | mux_motherboard = false; |
| 434 | 435 | |
| 435 | 436 | if (mux_motherboard) { |
| 436 | − dev_info(dev, "DVI muxed to motherboard CLCD\n"); | |
| 437 | + drm_info(dev, "DVI muxed to motherboard CLCD\n"); | |
| 437 | 438 | val = VEXPRESS_FPGAMUX_MOTHERBOARD; |
| 438 | − } else if (ct_clcd == dev->of_node) { | |
| 439 | − dev_info(dev, | |
| 439 | + } else if (ct_clcd == dev->dev->of_node) { | |
| 440 | + drm_info(dev, | |
| 440 | 441 | "DVI muxed to daughterboard 1 (core tile) CLCD\n"); |
| 441 | 442 | val = VEXPRESS_FPGAMUX_DAUGHTERBOARD_1; |
| 442 | 443 | } else { |
| 443 | − dev_info(dev, "core tile graphics present\n"); | |
| 444 | − dev_info(dev, "this device will be deactivated\n"); | |
| 444 | + drm_info(dev, "core tile graphics present\n"); | |
| 445 | + drm_info(dev, "this device will be deactivated\n"); | |
| 445 | 446 | return -ENODEV; |
| 446 | 447 | } |
| 447 | 448 | |
| 448 | 449 | /* Call into deep Vexpress configuration API */ |
| 449 | 450 | pdev = of_find_device_by_node(np); |
| 450 | 451 | if (!pdev) { |
| 451 | − dev_err(dev, "can't find the sysreg device, deferring\n"); | |
| 452 | + drm_err(dev, "can't find the sysreg device, deferring\n"); | |
| 452 | 453 | return -EPROBE_DEFER; |
| 453 | 454 | } |
| 454 | 455 | |
| @@ -461,17 +462,17 @@ static int pl111_vexpress_clcd_init(struct device *dev, struct device_node *np, · 6 unmodified lines | ||
| 461 | 462 | ret = regmap_write(map, 0, val); |
| 462 | 463 | platform_device_put(pdev); |
| 463 | 464 | if (ret) { |
| 464 | − dev_err(dev, "error setting DVI muxmode\n"); | |
| 465 | + drm_err(dev, "error setting DVI muxmode\n"); | |
| 465 | 466 | return -ENODEV; |
| 466 | 467 | } |
| 467 | 468 | |
| 468 | 469 | priv->variant = &pl111_vexpress; |
| 469 | − dev_info(dev, "initializing Versatile Express PL111\n"); | |
| 470 | + drm_info(dev, "initializing Versatile Express PL111\n"); | |
| 470 | 471 | |
| 471 | 472 | return 0; |
| 472 | 473 | } |
| 473 | 474 | |
| 474 | −int pl111_versatile_init(struct device *dev, struct pl111_drm_dev_private *priv) | |
| 475 | +int pl111_versatile_init(struct drm_device *dev, struct pl111_drm_dev_private *priv) | |
| 475 | 476 | { |
| 476 | 477 | const struct of_device_id *clcd_id; |
| 477 | 478 | enum versatile_clcd versatile_clcd_type; |
| @@ -492,7 +493,7 @@ int pl111_versatile_init(struct device *dev, struct pl111_drm_dev_private *priv) · 14 unmodified lines | ||
| 492 | 493 | int ret = pl111_vexpress_clcd_init(dev, np, priv); |
| 493 | 494 | of_node_put(np); |
| 494 | 495 | if (ret) |
| 495 | − dev_err(dev, "Versatile Express init failed - %d", ret); | |
| 496 | + drm_err(dev, "Versatile Express init failed - %d", ret); | |
| 496 | 497 | return ret; |
| 497 | 498 | } |
| 498 | 499 | |
| @@ -511,7 +512,7 @@ int pl111_versatile_init(struct device *dev, struct pl111_drm_dev_private *priv) · 12 unmodified lines | ||
| 511 | 512 | map = syscon_node_to_regmap(np); |
| 512 | 513 | of_node_put(np); |
| 513 | 514 | if (IS_ERR(map)) { |
| 514 | − dev_err(dev, "no Versatile syscon regmap\n"); | |
| 515 | + drm_err(dev, "no Versatile syscon regmap\n"); | |
| 515 | 516 | return PTR_ERR(map); |
| 516 | 517 | } |
| 517 | 518 | |
| @@ -520,14 +521,14 @@ int pl111_versatile_init(struct device *dev, struct pl111_drm_dev_private *priv) · 2 unmodified lines | ||
| 520 | 521 | versatile_syscon_map = map; |
| 521 | 522 | priv->variant = &pl110_integrator; |
| 522 | 523 | priv->variant_display_enable = pl111_integrator_enable; |
| 523 | − dev_info(dev, "set up callbacks for Integrator PL110\n"); | |
| 524 | + drm_info(dev, "set up callbacks for Integrator PL110\n"); | |
| 524 | 525 | break; |
| 525 | 526 | case INTEGRATOR_IMPD1: |
| 526 | 527 | versatile_syscon_map = map; |
| 527 | 528 | priv->variant = &pl110_impd1; |
| 528 | 529 | priv->variant_display_enable = pl111_impd1_enable; |
| 529 | 530 | priv->variant_display_disable = pl111_impd1_disable; |
| 530 | − dev_info(dev, "set up callbacks for IM-PD1 PL110\n"); | |
| 531 | + drm_info(dev, "set up callbacks for IM-PD1 PL110\n"); | |
| 531 | 532 | break; |
| 532 | 533 | case VERSATILE_CLCD: |
| 533 | 534 | versatile_syscon_map = map; |
| @@ -542,7 +543,7 @@ int pl111_versatile_init(struct device *dev, struct pl111_drm_dev_private *priv) · 8 unmodified lines | ||
| 542 | 543 | */ |
| 543 | 544 | priv->ienb = CLCD_PL111_IENB; |
| 544 | 545 | priv->ctrl = CLCD_PL111_CNTL; |
| 545 | − dev_info(dev, "set up callbacks for Versatile PL110\n"); | |
| 546 | + drm_info(dev, "set up callbacks for Versatile PL110\n"); | |
| 546 | 547 | break; |
| 547 | 548 | case REALVIEW_CLCD_EB: |
| 548 | 549 | case REALVIEW_CLCD_PB1176: |
| @@ -553,10 +554,10 @@ int pl111_versatile_init(struct device *dev, struct pl111_drm_dev_private *priv) · 4 unmodified lines | ||
| 553 | 554 | priv->variant = &pl111_realview; |
| 554 | 555 | priv->variant_display_enable = pl111_realview_clcd_enable; |
| 555 | 556 | priv->variant_display_disable = pl111_realview_clcd_disable; |
| 556 | − dev_info(dev, "set up callbacks for RealView PL111\n"); | |
| 557 | + drm_info(dev, "set up callbacks for RealView PL111\n"); | |
| 557 | 558 | break; |
| 558 | 559 | default: |
| 559 | − dev_info(dev, "unknown Versatile system controller\n"); | |
| 560 | + drm_info(dev, "unknown Versatile system controller\n"); | |
| 560 | 561 | break; |
| 561 | 562 | } |
| 562 | 563 | |
| @@ -7,6 +7,6 @@ · 6 unmodified lines | ||
| 7 | 7 | struct device; |
| 8 | 8 | struct pl111_drm_dev_private; |
| 9 | 9 | |
| 10 | −int pl111_versatile_init(struct device *dev, struct pl111_drm_dev_private *priv); | |
| 10 | +int pl111_versatile_init(struct drm_device *dev, struct pl111_drm_dev_private *priv); | |
| 11 | 11 | |
| 12 | 12 | #endif |
| @@ -1133,7 +1133,7 @@ static void atombios_crtc_set_pll(struct drm_crtc *crtc, struct drm_display_mode · 1132 unmodified lines | ||
| 1133 | 1133 | |
| 1134 | 1134 | static int dce4_crtc_do_set_base(struct drm_crtc *crtc, |
| 1135 | 1135 | struct drm_framebuffer *fb, |
| 1136 | − int x, int y, int atomic) | |
| 1136 | + int x, int y) | |
| 1137 | 1137 | { |
| 1138 | 1138 | struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc); |
| 1139 | 1139 | struct drm_device *dev = crtc->dev; |
| @@ -1150,33 +1150,23 @@ static int dce4_crtc_do_set_base(struct drm_crtc *crtc, · 10 unmodified lines | ||
| 1150 | 1150 | bool bypass_lut = false; |
| 1151 | 1151 | |
| 1152 | 1152 | /* no fb bound */ |
| 1153 | − if (!atomic && !crtc->primary->fb) { | |
| 1153 | + if (!crtc->primary->fb) { | |
| 1154 | 1154 | DRM_DEBUG_KMS("No FB bound\n"); |
| 1155 | 1155 | return 0; |
| 1156 | 1156 | } |
| 1157 | 1157 | |
| 1158 | − if (atomic) | |
| 1159 | − target_fb = fb; | |
| 1160 | − else | |
| 1161 | − target_fb = crtc->primary->fb; | |
| 1158 | + target_fb = crtc->primary->fb; | |
| 1162 | 1159 | |
| 1163 | − /* If atomic, assume fb object is pinned & idle & fenced and | |
| 1164 | − * just update base pointers | |
| 1165 | − */ | |
| 1166 | 1160 | obj = target_fb->obj[0]; |
| 1167 | 1161 | rbo = gem_to_radeon_bo(obj); |
| 1168 | 1162 | r = radeon_bo_reserve(rbo, false); |
| 1169 | 1163 | if (unlikely(r != 0)) |
| 1170 | 1164 | return r; |
| 1171 | 1165 | |
| 1172 | − if (atomic) | |
| 1173 | − fb_location = radeon_bo_gpu_offset(rbo); | |
| 1174 | − else { | |
| 1175 | − r = radeon_bo_pin(rbo, RADEON_GEM_DOMAIN_VRAM, &fb_location); | |
| 1176 | − if (unlikely(r != 0)) { | |
| 1177 | − radeon_bo_unreserve(rbo); | |
| 1178 | − return -EINVAL; | |
| 1179 | − } | |
| 1166 | + r = radeon_bo_pin(rbo, RADEON_GEM_DOMAIN_VRAM, &fb_location); | |
| 1167 | + if (unlikely(r != 0)) { | |
| 1168 | + radeon_bo_unreserve(rbo); | |
| 1169 | + return -EINVAL; | |
| 1180 | 1170 | } |
| 1181 | 1171 | |
| 1182 | 1172 | radeon_bo_get_tiling_flags(rbo, &tiling_flags, NULL); |
| @@ -1437,7 +1427,7 @@ static int dce4_crtc_do_set_base(struct drm_crtc *crtc, · 254 unmodified lines | ||
| 1437 | 1427 | /* set pageflip to happen anywhere in vblank interval */ |
| 1438 | 1428 | WREG32(EVERGREEN_MASTER_UPDATE_MODE + radeon_crtc->crtc_offset, 0); |
| 1439 | 1429 | |
| 1440 | − if (!atomic && fb && fb != crtc->primary->fb) { | |
| 1430 | + if (fb && fb != crtc->primary->fb) { | |
| 1441 | 1431 | rbo = gem_to_radeon_bo(fb->obj[0]); |
| 1442 | 1432 | r = radeon_bo_reserve(rbo, false); |
| 1443 | 1433 | if (unlikely(r != 0)) |
| @@ -1454,7 +1444,7 @@ static int dce4_crtc_do_set_base(struct drm_crtc *crtc, · 10 unmodified lines | ||
| 1454 | 1444 | |
| 1455 | 1445 | static int avivo_crtc_do_set_base(struct drm_crtc *crtc, |
| 1456 | 1446 | struct drm_framebuffer *fb, |
| 1457 | − int x, int y, int atomic) | |
| 1447 | + int x, int y) | |
| 1458 | 1448 | { |
| 1459 | 1449 | struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc); |
| 1460 | 1450 | struct drm_device *dev = crtc->dev; |
| @@ -1470,15 +1460,12 @@ static int avivo_crtc_do_set_base(struct drm_crtc *crtc, · 9 unmodified lines | ||
| 1470 | 1460 | bool bypass_lut = false; |
| 1471 | 1461 | |
| 1472 | 1462 | /* no fb bound */ |
| 1473 | − if (!atomic && !crtc->primary->fb) { | |
| 1463 | + if (!crtc->primary->fb) { | |
| 1474 | 1464 | DRM_DEBUG_KMS("No FB bound\n"); |
| 1475 | 1465 | return 0; |
| 1476 | 1466 | } |
| 1477 | 1467 | |
| 1478 | − if (atomic) | |
| 1479 | − target_fb = fb; | |
| 1480 | − else | |
| 1481 | − target_fb = crtc->primary->fb; | |
| 1468 | + target_fb = crtc->primary->fb; | |
| 1482 | 1469 | |
| 1483 | 1470 | obj = target_fb->obj[0]; |
| 1484 | 1471 | rbo = gem_to_radeon_bo(obj); |
| @@ -1486,17 +1473,10 @@ static int avivo_crtc_do_set_base(struct drm_crtc *crtc, · 1 unmodified line | ||
| 1486 | 1473 | if (unlikely(r != 0)) |
| 1487 | 1474 | return r; |
| 1488 | 1475 | |
| 1489 | − /* If atomic, assume fb object is pinned & idle & fenced and | |
| 1490 | − * just update base pointers | |
| 1491 | − */ | |
| 1492 | − if (atomic) | |
| 1493 | − fb_location = radeon_bo_gpu_offset(rbo); | |
| 1494 | − else { | |
| 1495 | − r = radeon_bo_pin(rbo, RADEON_GEM_DOMAIN_VRAM, &fb_location); | |
| 1496 | − if (unlikely(r != 0)) { | |
| 1497 | − radeon_bo_unreserve(rbo); | |
| 1498 | − return -EINVAL; | |
| 1499 | − } | |
| 1476 | + r = radeon_bo_pin(rbo, RADEON_GEM_DOMAIN_VRAM, &fb_location); | |
| 1477 | + if (unlikely(r != 0)) { | |
| 1478 | + radeon_bo_unreserve(rbo); | |
| 1479 | + return -EINVAL; | |
| 1500 | 1480 | } |
| 1501 | 1481 | radeon_bo_get_tiling_flags(rbo, &tiling_flags, NULL); |
| 1502 | 1482 | radeon_bo_unreserve(rbo); |
| @@ -1645,7 +1625,7 @@ static int avivo_crtc_do_set_base(struct drm_crtc *crtc, · 142 unmodified lines | ||
| 1645 | 1625 | /* set pageflip to happen only at start of vblank interval (front porch) */ |
| 1646 | 1626 | WREG32(AVIVO_D1MODE_MASTER_UPDATE_MODE + radeon_crtc->crtc_offset, 3); |
| 1647 | 1627 | |
| 1648 | − if (!atomic && fb && fb != crtc->primary->fb) { | |
| 1628 | + if (fb && fb != crtc->primary->fb) { | |
| 1649 | 1629 | rbo = gem_to_radeon_bo(fb->obj[0]); |
| 1650 | 1630 | r = radeon_bo_reserve(rbo, false); |
| 1651 | 1631 | if (unlikely(r != 0)) |
| @@ -1667,26 +1647,11 @@ int atombios_crtc_set_base(struct drm_crtc *crtc, int x, int y, · 15 unmodified lines | ||
| 1667 | 1647 | struct radeon_device *rdev = dev->dev_private; |
| 1668 | 1648 | |
| 1669 | 1649 | if (ASIC_IS_DCE4(rdev)) |
| 1670 | − return dce4_crtc_do_set_base(crtc, old_fb, x, y, 0); | |
| 1671 | − else if (ASIC_IS_AVIVO(rdev)) | |
| 1672 | − return avivo_crtc_do_set_base(crtc, old_fb, x, y, 0); | |
| 1673 | − else | |
| 1674 | − return radeon_crtc_do_set_base(crtc, old_fb, x, y, 0); | |
| 1675 | −} | |
| 1676 | − | |
| 1677 | −int atombios_crtc_set_base_atomic(struct drm_crtc *crtc, | |
| 1678 | − struct drm_framebuffer *fb, | |
| 1679 | − int x, int y, enum mode_set_atomic state) | |
| 1680 | −{ | |
| 1681 | − struct drm_device *dev = crtc->dev; | |
| 1682 | − struct radeon_device *rdev = dev->dev_private; | |
| 1683 | − | |
| 1684 | − if (ASIC_IS_DCE4(rdev)) | |
| 1685 | − return dce4_crtc_do_set_base(crtc, fb, x, y, 1); | |
| 1650 | + return dce4_crtc_do_set_base(crtc, old_fb, x, y); | |
| 1686 | 1651 | else if (ASIC_IS_AVIVO(rdev)) |
| 1687 | − return avivo_crtc_do_set_base(crtc, fb, x, y, 1); | |
| 1652 | + return avivo_crtc_do_set_base(crtc, old_fb, x, y); | |
| 1688 | 1653 | else |
| 1689 | − return radeon_crtc_do_set_base(crtc, fb, x, y, 1); | |
| 1654 | + return radeon_crtc_do_set_base(crtc, old_fb, x, y); | |
| 1690 | 1655 | } |
| 1691 | 1656 | |
| 1692 | 1657 | /* properly set additional regs when using atombios */ |
| @@ -2215,7 +2180,6 @@ static const struct drm_crtc_helper_funcs atombios_helper_funcs = { · 522 unmodified lines | ||
| 2215 | 2180 | .mode_fixup = atombios_crtc_mode_fixup, |
| 2216 | 2181 | .mode_set = atombios_crtc_mode_set, |
| 2217 | 2182 | .mode_set_base = atombios_crtc_set_base, |
| 2218 | − .mode_set_base_atomic = atombios_crtc_set_base_atomic, | |
| 2219 | 2183 | .prepare = atombios_crtc_prepare, |
| 2220 | 2184 | .commit = atombios_crtc_commit, |
| 2221 | 2185 | .disable = atombios_crtc_disable, |
| @@ -360,19 +360,12 @@ static void radeon_crtc_dpms(struct drm_crtc *crtc, int mode) · 359 unmodified lines | ||
| 360 | 360 | int radeon_crtc_set_base(struct drm_crtc *crtc, int x, int y, |
| 361 | 361 | struct drm_framebuffer *old_fb) |
| 362 | 362 | { |
| 363 | − return radeon_crtc_do_set_base(crtc, old_fb, x, y, 0); | |
| 364 | −} | |
| 365 | − | |
| 366 | −int radeon_crtc_set_base_atomic(struct drm_crtc *crtc, | |
| 367 | − struct drm_framebuffer *fb, | |
| 368 | − int x, int y, enum mode_set_atomic state) | |
| 369 | −{ | |
| 370 | − return radeon_crtc_do_set_base(crtc, fb, x, y, 1); | |
| 363 | + return radeon_crtc_do_set_base(crtc, old_fb, x, y); | |
| 371 | 364 | } |
| 372 | 365 | |
| 373 | 366 | int radeon_crtc_do_set_base(struct drm_crtc *crtc, |
| 374 | 367 | struct drm_framebuffer *fb, |
| 375 | − int x, int y, int atomic) | |
| 368 | + int x, int y) | |
| 376 | 369 | { |
| 377 | 370 | struct drm_device *dev = crtc->dev; |
| 378 | 371 | struct radeon_device *rdev = dev->dev_private; |
| @@ -390,15 +383,12 @@ int radeon_crtc_do_set_base(struct drm_crtc *crtc, · 11 unmodified lines | ||
| 390 | 383 | |
| 391 | 384 | DRM_DEBUG_KMS("\n"); |
| 392 | 385 | /* no fb bound */ |
| 393 | − if (!atomic && !crtc->primary->fb) { | |
| 386 | + if (!crtc->primary->fb) { | |
| 394 | 387 | DRM_DEBUG_KMS("No FB bound\n"); |
| 395 | 388 | return 0; |
| 396 | 389 | } |
| 397 | 390 | |
| 398 | − if (atomic) | |
| 399 | − target_fb = fb; | |
| 400 | − else | |
| 401 | − target_fb = crtc->primary->fb; | |
| 391 | + target_fb = crtc->primary->fb; | |
| 402 | 392 | |
| 403 | 393 | switch (target_fb->format->cpp[0] * 8) { |
| 404 | 394 | case 8: |
| @@ -445,7 +435,7 @@ retry: · 40 unmodified lines | ||
| 445 | 435 | * We don't shutdown the display controller because new buffer |
| 446 | 436 | * will end up in same spot. |
| 447 | 437 | */ |
| 448 | − if (!atomic && fb && fb != crtc->primary->fb) { | |
| 438 | + if (fb && fb != crtc->primary->fb) { | |
| 449 | 439 | struct radeon_bo *old_rbo; |
| 450 | 440 | unsigned long nsize, osize; |
| 451 | 441 | |
| @@ -555,7 +545,7 @@ retry: · 103 unmodified lines | ||
| 555 | 545 | WREG32(RADEON_CRTC_OFFSET + radeon_crtc->crtc_offset, crtc_offset); |
| 556 | 546 | WREG32(RADEON_CRTC_PITCH + radeon_crtc->crtc_offset, crtc_pitch); |
| 557 | 547 | |
| 558 | − if (!atomic && fb && fb != crtc->primary->fb) { | |
| 548 | + if (fb && fb != crtc->primary->fb) { | |
| 559 | 549 | rbo = gem_to_radeon_bo(fb->obj[0]); |
| 560 | 550 | r = radeon_bo_reserve(rbo, false); |
| 561 | 551 | if (unlikely(r != 0)) |
| @@ -1108,7 +1098,6 @@ static const struct drm_crtc_helper_funcs legacy_helper_funcs = { · 546 unmodified lines | ||
| 1108 | 1098 | .mode_fixup = radeon_crtc_mode_fixup, |
| 1109 | 1099 | .mode_set = radeon_crtc_mode_set, |
| 1110 | 1100 | .mode_set_base = radeon_crtc_set_base, |
| 1111 | − .mode_set_base_atomic = radeon_crtc_set_base_atomic, | |
| 1112 | 1101 | .prepare = radeon_crtc_prepare, |
| 1113 | 1102 | .commit = radeon_crtc_commit, |
| 1114 | 1103 | .disable = radeon_crtc_disable, |
| @@ -804,10 +804,6 @@ extern bool radeon_encoder_is_digital(struct drm_encoder *encoder); · 803 unmodified lines | ||
| 804 | 804 | extern void radeon_crtc_load_lut(struct drm_crtc *crtc); |
| 805 | 805 | extern int atombios_crtc_set_base(struct drm_crtc *crtc, int x, int y, |
| 806 | 806 | struct drm_framebuffer *old_fb); |
| 807 | −extern int atombios_crtc_set_base_atomic(struct drm_crtc *crtc, | |
| 808 | − struct drm_framebuffer *fb, | |
| 809 | − int x, int y, | |
| 810 | − enum mode_set_atomic state); | |
| 811 | 807 | extern int atombios_crtc_mode_set(struct drm_crtc *crtc, |
| 812 | 808 | struct drm_display_mode *mode, |
| 813 | 809 | struct drm_display_mode *adjusted_mode, |
| @@ -817,13 +813,9 @@ extern void atombios_crtc_dpms(struct drm_crtc *crtc, int mode); · 3 unmodified lines | ||
| 817 | 813 | |
| 818 | 814 | extern int radeon_crtc_set_base(struct drm_crtc *crtc, int x, int y, |
| 819 | 815 | struct drm_framebuffer *old_fb); |
| 820 | −extern int radeon_crtc_set_base_atomic(struct drm_crtc *crtc, | |
| 821 | − struct drm_framebuffer *fb, | |
| 822 | − int x, int y, | |
| 823 | − enum mode_set_atomic state); | |
| 824 | 816 | extern int radeon_crtc_do_set_base(struct drm_crtc *crtc, |
| 825 | 817 | struct drm_framebuffer *fb, |
| 826 | − int x, int y, int atomic); | |
| 818 | + int x, int y); | |
| 827 | 819 | extern int radeon_crtc_cursor_set2(struct drm_crtc *crtc, |
| 828 | 820 | struct drm_file *file_priv, |
| 829 | 821 | uint32_t handle, |
| @@ -692,7 +692,7 @@ static int threaded_fence_signal(void *arg) · 691 unmodified lines | ||
| 692 | 692 | |
| 693 | 693 | msleep(20); |
| 694 | 694 | |
| 695 | − return dma_fence_signal(fence); | |
| 695 | + return dma_fence_check_and_signal(fence) ? -EINVAL : 0; | |
| 696 | 696 | } |
| 697 | 697 | |
| 698 | 698 | static void ttm_bo_validate_move_fence_not_signaled(struct kunit *test) |
| @@ -268,8 +268,8 @@ static void ttm_bo_release(struct kref *kref) · 267 unmodified lines | ||
| 268 | 268 | 30 * HZ); |
| 269 | 269 | } |
| 270 | 270 | |
| 271 | − if (bo->bdev->funcs->release_notify) | |
| 272 | − bo->bdev->funcs->release_notify(bo); | |
| 271 | + if (bdev->funcs->release_notify) | |
| 272 | + bdev->funcs->release_notify(bo); | |
| 273 | 273 | |
| 274 | 274 | drm_vma_offset_remove(bdev->vma_manager, &bo->base.vma_node); |
| 275 | 275 | ttm_mem_io_free(bdev, bo->resource); |
| @@ -283,7 +283,7 @@ static void ttm_bo_release(struct kref *kref) · 7 unmodified lines | ||
| 283 | 283 | ttm_bo_flush_all_fences(bo); |
| 284 | 284 | bo->deleted = true; |
| 285 | 285 | |
| 286 | − spin_lock(&bo->bdev->lru_lock); | |
| 286 | + spin_lock(&bdev->lru_lock); | |
| 287 | 287 | |
| 288 | 288 | /* |
| 289 | 289 | * Make pinned bos immediately available to |
| @@ -299,7 +299,7 @@ static void ttm_bo_release(struct kref *kref) · 9 unmodified lines | ||
| 299 | 299 | } |
| 300 | 300 | |
| 301 | 301 | kref_init(&bo->kref); |
| 302 | − spin_unlock(&bo->bdev->lru_lock); | |
| 302 | + spin_unlock(&bdev->lru_lock); | |
| 303 | 303 | |
| 304 | 304 | INIT_WORK(&bo->delayed_delete, ttm_bo_delayed_delete); |
| 305 | 305 | |
| @@ -359,7 +359,6 @@ static int ttm_bo_bounce_temp_buffer(struct ttm_buffer_object *bo, · 53 unmodified lines | ||
| 359 | 359 | static int ttm_bo_evict(struct ttm_buffer_object *bo, |
| 360 | 360 | struct ttm_operation_ctx *ctx) |
| 361 | 361 | { |
| 362 | − struct ttm_device *bdev = bo->bdev; | |
| 363 | 362 | struct ttm_resource *evict_mem; |
| 364 | 363 | struct ttm_placement placement; |
| 365 | 364 | struct ttm_place hop; |
| @@ -370,7 +369,7 @@ static int ttm_bo_evict(struct ttm_buffer_object *bo, · 4 unmodified lines | ||
| 370 | 369 | dma_resv_assert_held(bo->base.resv); |
| 371 | 370 | |
| 372 | 371 | placement.num_placement = 0; |
| 373 | − bdev->funcs->evict_flags(bo, &placement); | |
| 372 | + bo->bdev->funcs->evict_flags(bo, &placement); | |
| 374 | 373 | |
| 375 | 374 | if (!placement.num_placement) { |
| 376 | 375 | ret = ttm_bo_wait_ctx(bo, ctx); |
| @@ -423,16 +422,16 @@ bool ttm_bo_eviction_valuable(struct ttm_buffer_object *bo, · 46 unmodified lines | ||
| 423 | 422 | const struct ttm_place *place) |
| 424 | 423 | { |
| 425 | 424 | struct ttm_resource *res = bo->resource; |
| 426 | − struct ttm_device *bdev = bo->bdev; | |
| 427 | 425 | |
| 428 | 426 | dma_resv_assert_held(bo->base.resv); |
| 429 | − if (bo->resource->mem_type == TTM_PL_SYSTEM) | |
| 427 | + | |
| 428 | + if (res->mem_type == TTM_PL_SYSTEM) | |
| 430 | 429 | return true; |
| 431 | 430 | |
| 432 | 431 | /* Don't evict this BO if it's outside of the |
| 433 | 432 | * requested placement range |
| 434 | 433 | */ |
| 435 | − return ttm_resource_intersects(bdev, res, place, bo->base.size); | |
| 434 | + return ttm_resource_intersects(bo->bdev, res, place, bo->base.size); | |
| 436 | 435 | } |
| 437 | 436 | EXPORT_SYMBOL(ttm_bo_eviction_valuable); |
| 438 | 437 | |
| @@ -1027,7 +1026,7 @@ int ttm_bo_init_validate(struct ttm_device *bdev, struct ttm_buffer_object *bo, · 588 unmodified lines | ||
| 1027 | 1026 | struct sg_table *sg, struct dma_resv *resv, |
| 1028 | 1027 | void (*destroy) (struct ttm_buffer_object *)) |
| 1029 | 1028 | { |
| 1030 | − struct ttm_operation_ctx ctx = { interruptible, false }; | |
| 1029 | + struct ttm_operation_ctx ctx = { .interruptible = interruptible }; | |
| 1031 | 1030 | int ret; |
| 1032 | 1031 | |
| 1033 | 1032 | ret = ttm_bo_init_reserved(bdev, bo, type, placement, alignment, &ctx, |
| @@ -1108,10 +1107,13 @@ struct ttm_bo_swapout_walk { · 74 unmodified lines | ||
| 1108 | 1107 | static s64 |
| 1109 | 1108 | ttm_bo_swapout_cb(struct ttm_lru_walk *walk, struct ttm_buffer_object *bo) |
| 1110 | 1109 | { |
| 1111 | − struct ttm_place place = {.mem_type = bo->resource->mem_type}; | |
| 1110 | + struct ttm_resource *res = bo->resource; | |
| 1111 | + struct ttm_place place = { .mem_type = res->mem_type }; | |
| 1112 | 1112 | struct ttm_bo_swapout_walk *swapout_walk = |
| 1113 | 1113 | container_of(walk, typeof(*swapout_walk), walk); |
| 1114 | 1114 | struct ttm_operation_ctx *ctx = walk->arg.ctx; |
| 1115 | + struct ttm_device *bdev = bo->bdev; | |
| 1116 | + struct ttm_tt *tt = bo->ttm; | |
| 1115 | 1117 | s64 ret; |
| 1116 | 1118 | |
| 1117 | 1119 | /* |
| @@ -1120,20 +1122,19 @@ ttm_bo_swapout_cb(struct ttm_lru_walk *walk, struct ttm_buffer_object *bo) · 2 unmodified lines | ||
| 1120 | 1122 | * The driver may use the fact that we're moving from SYSTEM |
| 1121 | 1123 | * as an indication that we're about to swap out. |
| 1122 | 1124 | */ |
| 1123 | − if (bo->pin_count || !bo->bdev->funcs->eviction_valuable(bo, &place)) { | |
| 1125 | + if (bo->pin_count || !bdev->funcs->eviction_valuable(bo, &place)) { | |
| 1124 | 1126 | ret = -EBUSY; |
| 1125 | 1127 | goto out; |
| 1126 | 1128 | } |
| 1127 | 1129 | |
| 1128 | − if (!bo->ttm || !ttm_tt_is_populated(bo->ttm) || | |
| 1129 | − bo->ttm->page_flags & TTM_TT_FLAG_EXTERNAL || | |
| 1130 | − bo->ttm->page_flags & TTM_TT_FLAG_SWAPPED) { | |
| 1130 | + if (!tt || !ttm_tt_is_populated(tt) || | |
| 1131 | + tt->page_flags & (TTM_TT_FLAG_EXTERNAL | TTM_TT_FLAG_SWAPPED)) { | |
| 1131 | 1132 | ret = -EBUSY; |
| 1132 | 1133 | goto out; |
| 1133 | 1134 | } |
| 1134 | 1135 | |
| 1135 | 1136 | if (bo->deleted) { |
| 1136 | − pgoff_t num_pages = bo->ttm->num_pages; | |
| 1137 | + pgoff_t num_pages = tt->num_pages; | |
| 1137 | 1138 | |
| 1138 | 1139 | ret = ttm_bo_wait_ctx(bo, ctx); |
| 1139 | 1140 | if (ret) |
| @@ -1147,7 +1148,7 @@ ttm_bo_swapout_cb(struct ttm_lru_walk *walk, struct ttm_buffer_object *bo) · 7 unmodified lines | ||
| 1147 | 1148 | /* |
| 1148 | 1149 | * Move to system cached |
| 1149 | 1150 | */ |
| 1150 | − if (bo->resource->mem_type != TTM_PL_SYSTEM) { | |
| 1151 | + if (res->mem_type != TTM_PL_SYSTEM) { | |
| 1151 | 1152 | struct ttm_resource *evict_mem; |
| 1152 | 1153 | struct ttm_place hop; |
| 1153 | 1154 | |
| @@ -1174,21 +1175,21 @@ ttm_bo_swapout_cb(struct ttm_lru_walk *walk, struct ttm_buffer_object *bo) · 20 unmodified lines | ||
| 1174 | 1175 | goto out; |
| 1175 | 1176 | |
| 1176 | 1177 | ttm_bo_unmap_virtual(bo); |
| 1177 | − if (bo->bdev->funcs->swap_notify) | |
| 1178 | − bo->bdev->funcs->swap_notify(bo); | |
| 1178 | + if (bdev->funcs->swap_notify) | |
| 1179 | + bdev->funcs->swap_notify(bo); | |
| 1179 | 1180 | |
| 1180 | − if (ttm_tt_is_populated(bo->ttm)) { | |
| 1181 | − spin_lock(&bo->bdev->lru_lock); | |
| 1182 | − ttm_resource_del_bulk_move(bo->resource, bo); | |
| 1183 | − spin_unlock(&bo->bdev->lru_lock); | |
| 1181 | + if (ttm_tt_is_populated(tt)) { | |
| 1182 | + spin_lock(&bdev->lru_lock); | |
| 1183 | + ttm_resource_del_bulk_move(res, bo); | |
| 1184 | + spin_unlock(&bdev->lru_lock); | |
| 1184 | 1185 | |
| 1185 | − ret = ttm_tt_swapout(bo->bdev, bo->ttm, swapout_walk->gfp_flags); | |
| 1186 | + ret = ttm_tt_swapout(bdev, tt, swapout_walk->gfp_flags); | |
| 1186 | 1187 | |
| 1187 | − spin_lock(&bo->bdev->lru_lock); | |
| 1188 | + spin_lock(&bdev->lru_lock); | |
| 1188 | 1189 | if (ret) |
| 1189 | − ttm_resource_add_bulk_move(bo->resource, bo); | |
| 1190 | − ttm_resource_move_to_lru_tail(bo->resource); | |
| 1191 | − spin_unlock(&bo->bdev->lru_lock); | |
| 1190 | + ttm_resource_add_bulk_move(res, bo); | |
| 1191 | + ttm_resource_move_to_lru_tail(res); | |
| 1192 | + spin_unlock(&bdev->lru_lock); | |
| 1192 | 1193 | } |
| 1193 | 1194 | |
| 1194 | 1195 | out: |
| @@ -1261,6 +1262,7 @@ void ttm_bo_tt_destroy(struct ttm_buffer_object *bo) · 66 unmodified lines | ||
| 1261 | 1262 | int ttm_bo_populate(struct ttm_buffer_object *bo, |
| 1262 | 1263 | struct ttm_operation_ctx *ctx) |
| 1263 | 1264 | { |
| 1265 | + struct ttm_device *bdev = bo->bdev; | |
| 1264 | 1266 | struct ttm_tt *tt = bo->ttm; |
| 1265 | 1267 | bool swapped; |
| 1266 | 1268 | int ret; |
| @@ -1271,16 +1273,16 @@ int ttm_bo_populate(struct ttm_buffer_object *bo, · 4 unmodified lines | ||
| 1271 | 1273 | return 0; |
| 1272 | 1274 | |
| 1273 | 1275 | swapped = ttm_tt_is_swapped(tt); |
| 1274 | − ret = ttm_tt_populate(bo->bdev, tt, ctx); | |
| 1276 | + ret = ttm_tt_populate(bdev, tt, ctx); | |
| 1275 | 1277 | if (ret) |
| 1276 | 1278 | return ret; |
| 1277 | 1279 | |
| 1278 | 1280 | if (swapped && !ttm_tt_is_swapped(tt) && !bo->pin_count && |
| 1279 | 1281 | bo->resource) { |
| 1280 | − spin_lock(&bo->bdev->lru_lock); | |
| 1282 | + spin_lock(&bdev->lru_lock); | |
| 1281 | 1283 | ttm_resource_add_bulk_move(bo->resource, bo); |
| 1282 | 1284 | ttm_resource_move_to_lru_tail(bo->resource); |
| 1283 | − spin_unlock(&bo->bdev->lru_lock); | |
| 1285 | + spin_unlock(&bdev->lru_lock); | |
| 1284 | 1286 | } |
| 1285 | 1287 | |
| 1286 | 1288 | return 0; |
| @@ -174,13 +174,13 @@ int ttm_bo_move_memcpy(struct ttm_buffer_object *bo, · 173 unmodified lines | ||
| 174 | 174 | |
| 175 | 175 | dst_iter = ttm_kmap_iter_linear_io_init(&_dst_iter.io, bdev, dst_mem); |
| 176 | 176 | if (PTR_ERR(dst_iter) == -EINVAL && dst_man->use_tt) |
| 177 | − dst_iter = ttm_kmap_iter_tt_init(&_dst_iter.tt, bo->ttm); | |
| 177 | + dst_iter = ttm_kmap_iter_tt_init(&_dst_iter.tt, ttm); | |
| 178 | 178 | if (IS_ERR(dst_iter)) |
| 179 | 179 | return PTR_ERR(dst_iter); |
| 180 | 180 | |
| 181 | 181 | src_iter = ttm_kmap_iter_linear_io_init(&_src_iter.io, bdev, src_mem); |
| 182 | 182 | if (PTR_ERR(src_iter) == -EINVAL && src_man->use_tt) |
| 183 | − src_iter = ttm_kmap_iter_tt_init(&_src_iter.tt, bo->ttm); | |
| 183 | + src_iter = ttm_kmap_iter_tt_init(&_src_iter.tt, ttm); | |
| 184 | 184 | if (IS_ERR(src_iter)) { |
| 185 | 185 | ret = PTR_ERR(src_iter); |
| 186 | 186 | goto out_src_iter; |
| @@ -318,11 +318,11 @@ static int ttm_bo_ioremap(struct ttm_buffer_object *bo, · 131 unmodified lines | ||
| 318 | 318 | { |
| 319 | 319 | struct ttm_resource *mem = bo->resource; |
| 320 | 320 | |
| 321 | − if (bo->resource->bus.addr) { | |
| 321 | + if (mem->bus.addr) { | |
| 322 | 322 | map->bo_kmap_type = ttm_bo_map_premapped; |
| 323 | − map->virtual = ((u8 *)bo->resource->bus.addr) + offset; | |
| 323 | + map->virtual = ((u8 *)mem->bus.addr) + offset; | |
| 324 | 324 | } else { |
| 325 | − resource_size_t res = bo->resource->bus.offset + offset; | |
| 325 | + resource_size_t res = mem->bus.offset + offset; | |
| 326 | 326 | |
| 327 | 327 | map->bo_kmap_type = ttm_bo_map_iomap; |
| 328 | 328 | if (mem->bus.caching == ttm_write_combined) |
| @@ -343,13 +343,10 @@ static int ttm_bo_kmap_ttm(struct ttm_buffer_object *bo, · 14 unmodified lines | ||
| 343 | 343 | struct ttm_bo_kmap_obj *map) |
| 344 | 344 | { |
| 345 | 345 | struct ttm_resource *mem = bo->resource; |
| 346 | − struct ttm_operation_ctx ctx = { | |
| 347 | − .interruptible = false, | |
| 348 | − .no_wait_gpu = false | |
| 349 | − }; | |
| 346 | + struct ttm_operation_ctx ctx = { }; | |
| 350 | 347 | struct ttm_tt *ttm = bo->ttm; |
| 351 | 348 | struct ttm_resource_manager *man = |
| 352 | − ttm_manager_type(bo->bdev, bo->resource->mem_type); | |
| 349 | + ttm_manager_type(bo->bdev, mem->mem_type); | |
| 353 | 350 | pgprot_t prot; |
| 354 | 351 | int ret; |
| 355 | 352 | |
| @@ -428,20 +425,21 @@ int ttm_bo_kmap(struct ttm_buffer_object *bo, · 72 unmodified lines | ||
| 428 | 425 | unsigned long start_page, unsigned long num_pages, |
| 429 | 426 | struct ttm_bo_kmap_obj *map) |
| 430 | 427 | { |
| 428 | + struct ttm_resource *res = bo->resource; | |
| 431 | 429 | unsigned long offset, size; |
| 432 | 430 | int ret; |
| 433 | 431 | |
| 434 | 432 | map->virtual = NULL; |
| 435 | 433 | map->bo = bo; |
| 436 | − if (num_pages > PFN_UP(bo->resource->size)) | |
| 434 | + if (num_pages > PFN_UP(res->size)) | |
| 437 | 435 | return -EINVAL; |
| 438 | − if ((start_page + num_pages) > PFN_UP(bo->resource->size)) | |
| 436 | + if ((start_page + num_pages) > PFN_UP(res->size)) | |
| 439 | 437 | return -EINVAL; |
| 440 | 438 | |
| 441 | − ret = ttm_mem_io_reserve(bo->bdev, bo->resource); | |
| 439 | + ret = ttm_mem_io_reserve(bo->bdev, res); | |
| 442 | 440 | if (ret) |
| 443 | 441 | return ret; |
| 444 | − if (!bo->resource->bus.is_iomem) { | |
| 442 | + if (!res->bus.is_iomem) { | |
| 445 | 443 | return ttm_bo_kmap_ttm(bo, start_page, num_pages, map); |
| 446 | 444 | } else { |
| 447 | 445 | offset = start_page << PAGE_SHIFT; |
| @@ -530,10 +528,7 @@ int ttm_bo_vmap(struct ttm_buffer_object *bo, struct iosys_map *map) · 82 unmodified lines | ||
| 530 | 528 | iosys_map_set_vaddr_iomem(map, vaddr_iomem); |
| 531 | 529 | |
| 532 | 530 | } else { |
| 533 | − struct ttm_operation_ctx ctx = { | |
| 534 | − .interruptible = false, | |
| 535 | − .no_wait_gpu = false | |
| 536 | − }; | |
| 531 | + struct ttm_operation_ctx ctx = { }; | |
| 537 | 532 | struct ttm_tt *ttm = bo->ttm; |
| 538 | 533 | pgprot_t prot; |
| 539 | 534 | void *vaddr; |
| @@ -581,7 +576,7 @@ void ttm_bo_vunmap(struct ttm_buffer_object *bo, struct iosys_map *map) · 41 unmodified lines | ||
| 581 | 576 | iounmap(map->vaddr_iomem); |
| 582 | 577 | iosys_map_clear(map); |
| 583 | 578 | |
| 584 | − ttm_mem_io_free(bo->bdev, bo->resource); | |
| 579 | + ttm_mem_io_free(bo->bdev, mem); | |
| 585 | 580 | } |
| 586 | 581 | EXPORT_SYMBOL(ttm_bo_vunmap); |
| 587 | 582 | |
| @@ -644,12 +639,11 @@ static int ttm_bo_move_to_ghost(struct ttm_buffer_object *bo, · 56 unmodified lines | ||
| 644 | 639 | static void ttm_bo_move_pipeline_evict(struct ttm_buffer_object *bo, |
| 645 | 640 | struct dma_fence *fence) |
| 646 | 641 | { |
| 647 | − struct ttm_device *bdev = bo->bdev; | |
| 648 | 642 | struct ttm_resource_manager *from; |
| 649 | 643 | struct dma_fence *tmp; |
| 650 | 644 | int i; |
| 651 | 645 | |
| 652 | − from = ttm_manager_type(bdev, bo->resource->mem_type); | |
| 646 | + from = ttm_manager_type(bo->bdev, bo->resource->mem_type); | |
| 653 | 647 | |
| 654 | 648 | /** |
| 655 | 649 | * BO doesn't have a TTM we need to bind/unbind. Just remember |
| @@ -743,8 +737,8 @@ EXPORT_SYMBOL(ttm_bo_move_accel_cleanup); · 87 unmodified lines | ||
| 743 | 737 | void ttm_bo_move_sync_cleanup(struct ttm_buffer_object *bo, |
| 744 | 738 | struct ttm_resource *new_mem) |
| 745 | 739 | { |
| 746 | − struct ttm_device *bdev = bo->bdev; | |
| 747 | − struct ttm_resource_manager *man = ttm_manager_type(bdev, new_mem->mem_type); | |
| 740 | + struct ttm_resource_manager *man = | |
| 741 | + ttm_manager_type(bo->bdev, new_mem->mem_type); | |
| 748 | 742 | int ret; |
| 749 | 743 | |
| 750 | 744 | ret = ttm_bo_wait_free_node(bo, man->use_tt); |
| @@ -848,13 +842,12 @@ static int ttm_lru_walk_ticketlock(struct ttm_bo_lru_cursor *curs, · 97 unmodified lines | ||
| 848 | 842 | struct ttm_buffer_object *bo) |
| 849 | 843 | { |
| 850 | 844 | struct ttm_lru_walk_arg *arg = curs->arg; |
| 851 | − struct dma_resv *resv = bo->base.resv; | |
| 852 | 845 | int ret; |
| 853 | 846 | |
| 854 | 847 | if (arg->ctx->interruptible) |
| 855 | − ret = dma_resv_lock_interruptible(resv, arg->ticket); | |
| 848 | + ret = dma_resv_lock_interruptible(bo->base.resv, arg->ticket); | |
| 856 | 849 | else |
| 857 | − ret = dma_resv_lock(resv, arg->ticket); | |
| 850 | + ret = dma_resv_lock(bo->base.resv, arg->ticket); | |
| 858 | 851 | |
| 859 | 852 | if (!ret) { |
| 860 | 853 | curs->needs_unlock = true; |
| @@ -1098,7 +1091,7 @@ long ttm_bo_shrink(struct ttm_operation_ctx *ctx, struct ttm_buffer_object *bo, · 237 unmodified lines | ||
| 1098 | 1091 | .num_placement = 1, |
| 1099 | 1092 | .placement = &sys_placement_flags, |
| 1100 | 1093 | }; |
| 1101 | − struct ttm_tt *tt = bo->ttm; | |
| 1094 | + struct ttm_device *bdev = bo->bdev; | |
| 1102 | 1095 | long lret; |
| 1103 | 1096 | |
| 1104 | 1097 | dma_resv_assert_held(bo->base.resv); |
| @@ -1120,19 +1113,19 @@ long ttm_bo_shrink(struct ttm_operation_ctx *ctx, struct ttm_buffer_object *bo, · 15 unmodified lines | ||
| 1120 | 1113 | return lret; |
| 1121 | 1114 | |
| 1122 | 1115 | if (bo->bulk_move) { |
| 1123 | − spin_lock(&bo->bdev->lru_lock); | |
| 1116 | + spin_lock(&bdev->lru_lock); | |
| 1124 | 1117 | ttm_resource_del_bulk_move(bo->resource, bo); |
| 1125 | − spin_unlock(&bo->bdev->lru_lock); | |
| 1118 | + spin_unlock(&bdev->lru_lock); | |
| 1126 | 1119 | } |
| 1127 | 1120 | |
| 1128 | − lret = ttm_tt_backup(bo->bdev, tt, (struct ttm_backup_flags) | |
| 1121 | + lret = ttm_tt_backup(bdev, bo->ttm, (struct ttm_backup_flags) | |
| 1129 | 1122 | {.purge = flags.purge, |
| 1130 | 1123 | .writeback = flags.writeback}); |
| 1131 | 1124 | |
| 1132 | 1125 | if (lret <= 0 && bo->bulk_move) { |
| 1133 | − spin_lock(&bo->bdev->lru_lock); | |
| 1126 | + spin_lock(&bdev->lru_lock); | |
| 1134 | 1127 | ttm_resource_add_bulk_move(bo->resource, bo); |
| 1135 | − spin_unlock(&bo->bdev->lru_lock); | |
| 1128 | + spin_unlock(&bdev->lru_lock); | |
| 1136 | 1129 | } |
| 1137 | 1130 | |
| 1138 | 1131 | if (lret < 0 && lret != -EINTR) |
| @@ -186,7 +186,6 @@ vm_fault_t ttm_bo_vm_fault_reserved(struct vm_fault *vmf, · 185 unmodified lines | ||
| 186 | 186 | { |
| 187 | 187 | struct vm_area_struct *vma = vmf->vma; |
| 188 | 188 | struct ttm_buffer_object *bo = vma->vm_private_data; |
| 189 | − struct ttm_device *bdev = bo->bdev; | |
| 190 | 189 | unsigned long page_offset; |
| 191 | 190 | unsigned long page_last; |
| 192 | 191 | unsigned long pfn; |
| @@ -205,7 +204,7 @@ vm_fault_t ttm_bo_vm_fault_reserved(struct vm_fault *vmf, · 12 unmodified lines | ||
| 205 | 204 | if (unlikely(ret != 0)) |
| 206 | 205 | return ret; |
| 207 | 206 | |
| 208 | − err = ttm_mem_io_reserve(bdev, bo->resource); | |
| 207 | + err = ttm_mem_io_reserve(bo->bdev, bo->resource); | |
| 209 | 208 | if (unlikely(err != 0)) |
| 210 | 209 | return VM_FAULT_SIGBUS; |
| 211 | 210 | |
| @@ -293,7 +292,6 @@ vm_fault_t ttm_bo_vm_dummy_page(struct vm_fault *vmf, pgprot_t prot) · 81 unmodified lines | ||
| 293 | 292 | { |
| 294 | 293 | struct vm_area_struct *vma = vmf->vma; |
| 295 | 294 | struct ttm_buffer_object *bo = vma->vm_private_data; |
| 296 | − struct drm_device *ddev = bo->base.dev; | |
| 297 | 295 | vm_fault_t ret = VM_FAULT_NOPAGE; |
| 298 | 296 | unsigned long address; |
| 299 | 297 | unsigned long pfn; |
| @@ -305,7 +303,8 @@ vm_fault_t ttm_bo_vm_dummy_page(struct vm_fault *vmf, pgprot_t prot) · 5 unmodified lines | ||
| 305 | 303 | return VM_FAULT_OOM; |
| 306 | 304 | |
| 307 | 305 | /* Set the page to be freed using drmm release action */ |
| 308 | − if (drmm_add_action_or_reset(ddev, ttm_bo_release_dummy_page, page)) | |
| 306 | + if (drmm_add_action_or_reset(bo->base.dev, ttm_bo_release_dummy_page, | |
| 307 | + page)) | |
| 309 | 308 | return VM_FAULT_OOM; |
| 310 | 309 | |
| 311 | 310 | pfn = page_to_pfn(page); |
| @@ -322,10 +321,9 @@ EXPORT_SYMBOL(ttm_bo_vm_dummy_page); · 10 unmodified lines | ||
| 322 | 321 | vm_fault_t ttm_bo_vm_fault(struct vm_fault *vmf) |
| 323 | 322 | { |
| 324 | 323 | struct vm_area_struct *vma = vmf->vma; |
| 325 | − pgprot_t prot; | |
| 326 | 324 | struct ttm_buffer_object *bo = vma->vm_private_data; |
| 327 | − struct drm_device *ddev = bo->base.dev; | |
| 328 | 325 | vm_fault_t ret; |
| 326 | + pgprot_t prot; | |
| 329 | 327 | int idx; |
| 330 | 328 | |
| 331 | 329 | ret = ttm_bo_vm_reserve(bo, vmf); |
| @@ -333,7 +331,7 @@ vm_fault_t ttm_bo_vm_fault(struct vm_fault *vmf) · 1 unmodified line | ||
| 333 | 331 | return ret; |
| 334 | 332 | |
| 335 | 333 | prot = vma->vm_page_prot; |
| 336 | − if (drm_dev_enter(ddev, &idx)) { | |
| 334 | + if (drm_dev_enter(bo->base.dev, &idx)) { | |
| 337 | 335 | ret = ttm_bo_vm_fault_reserved(vmf, prot, TTM_BO_VM_NUM_PREFAULT); |
| 338 | 336 | drm_dev_exit(idx); |
| 339 | 337 | } else { |
| @@ -135,10 +135,7 @@ out: · 134 unmodified lines | ||
| 135 | 135 | */ |
| 136 | 136 | int ttm_device_prepare_hibernation(struct ttm_device *bdev) |
| 137 | 137 | { |
| 138 | − struct ttm_operation_ctx ctx = { | |
| 139 | − .interruptible = false, | |
| 140 | − .no_wait_gpu = false, | |
| 141 | − }; | |
| 138 | + struct ttm_operation_ctx ctx = { }; | |
| 142 | 139 | int ret; |
| 143 | 140 | |
| 144 | 141 | do { |
| @@ -845,32 +845,34 @@ EXPORT_SYMBOL(ttm_pool_alloc); · 844 unmodified lines | ||
| 845 | 845 | int ttm_pool_restore_and_alloc(struct ttm_pool *pool, struct ttm_tt *tt, |
| 846 | 846 | const struct ttm_operation_ctx *ctx) |
| 847 | 847 | { |
| 848 | + struct ttm_pool_tt_restore *restore = tt->restore; | |
| 848 | 849 | struct ttm_pool_alloc_state alloc; |
| 849 | 850 | |
| 850 | 851 | if (WARN_ON(!ttm_tt_is_backed_up(tt))) |
| 851 | 852 | return -EINVAL; |
| 852 | 853 | |
| 853 | − if (!tt->restore) { | |
| 854 | + if (!restore) { | |
| 854 | 855 | gfp_t gfp = GFP_KERNEL | __GFP_NOWARN; |
| 855 | 856 | |
| 856 | 857 | ttm_pool_alloc_state_init(tt, &alloc); |
| 857 | 858 | if (ctx->gfp_retry_mayfail) |
| 858 | 859 | gfp |= __GFP_RETRY_MAYFAIL; |
| 859 | 860 | |
| 860 | − tt->restore = kzalloc(sizeof(*tt->restore), gfp); | |
| 861 | − if (!tt->restore) | |
| 861 | + restore = kzalloc(sizeof(*restore), gfp); | |
| 862 | + if (!restore) | |
| 862 | 863 | return -ENOMEM; |
| 863 | 864 | |
| 864 | − tt->restore->snapshot_alloc = alloc; | |
| 865 | − tt->restore->pool = pool; | |
| 866 | − tt->restore->restored_pages = 1; | |
| 867 | − } else { | |
| 868 | − struct ttm_pool_tt_restore *restore = tt->restore; | |
| 869 | − int ret; | |
| 865 | + restore->snapshot_alloc = alloc; | |
| 866 | + restore->pool = pool; | |
| 867 | + restore->restored_pages = 1; | |
| 870 | 868 | |
| 869 | + tt->restore = restore; | |
| 870 | + } else { | |
| 871 | 871 | alloc = restore->snapshot_alloc; |
| 872 | − if (ttm_pool_restore_valid(tt->restore)) { | |
| 873 | − ret = ttm_pool_restore_commit(restore, tt->backup, ctx, &alloc); | |
| 872 | + if (ttm_pool_restore_valid(restore)) { | |
| 873 | + int ret = ttm_pool_restore_commit(restore, tt->backup, | |
| 874 | + ctx, &alloc); | |
| 875 | + | |
| 874 | 876 | if (ret) |
| 875 | 877 | return ret; |
| 876 | 878 | } |
| @@ -878,7 +880,7 @@ int ttm_pool_restore_and_alloc(struct ttm_pool *pool, struct ttm_tt *tt, · 1 unmodified line | ||
| 878 | 880 | return 0; |
| 879 | 881 | } |
| 880 | 882 | |
| 881 | − return __ttm_pool_alloc(pool, tt, ctx, &alloc, tt->restore); | |
| 883 | + return __ttm_pool_alloc(pool, tt, ctx, &alloc, restore); | |
| 882 | 884 | } |
| 883 | 885 | |
| 884 | 886 | /** |
| @@ -446,9 +446,6 @@ bool ttm_resource_intersects(struct ttm_device *bdev, · 445 unmodified lines | ||
| 446 | 446 | { |
| 447 | 447 | struct ttm_resource_manager *man; |
| 448 | 448 | |
| 449 | − if (!res) | |
| 450 | − return false; | |
| 451 | − | |
| 452 | 449 | man = ttm_manager_type(bdev, res->mem_type); |
| 453 | 450 | if (!place || !man->func->intersects) |
| 454 | 451 | return true; |
| @@ -548,10 +545,7 @@ EXPORT_SYMBOL(ttm_resource_manager_init); · 93 unmodified lines | ||
| 548 | 545 | int ttm_resource_manager_evict_all(struct ttm_device *bdev, |
| 549 | 546 | struct ttm_resource_manager *man) |
| 550 | 547 | { |
| 551 | − struct ttm_operation_ctx ctx = { | |
| 552 | − .interruptible = false, | |
| 553 | − .no_wait_gpu = false, | |
| 554 | − }; | |
| 548 | + struct ttm_operation_ctx ctx = { }; | |
| 555 | 549 | struct dma_fence *fence; |
| 556 | 550 | int ret, i; |
| 557 | 551 | |
| @@ -628,11 +622,11 @@ ttm_resource_cursor_check_bulk(struct ttm_resource_cursor *cursor, · 70 unmodified lines | ||
| 628 | 622 | struct ttm_lru_item *next_lru) |
| 629 | 623 | { |
| 630 | 624 | struct ttm_resource *next = ttm_lru_item_to_res(next_lru); |
| 631 | − struct ttm_lru_bulk_move *bulk = NULL; | |
| 632 | − struct ttm_buffer_object *bo = next->bo; | |
| 625 | + struct ttm_lru_bulk_move *bulk; | |
| 633 | 626 | |
| 634 | 627 | lockdep_assert_held(&cursor->man->bdev->lru_lock); |
| 635 | − bulk = bo->bulk_move; | |
| 628 | + | |
| 629 | + bulk = next->bo->bulk_move; | |
| 636 | 630 | |
| 637 | 631 | if (cursor->bulk != bulk) { |
| 638 | 632 | if (bulk) { |
| @@ -456,7 +456,7 @@ EXPORT_SYMBOL_FOR_TESTS_ONLY(ttm_tt_unpopulate); · 455 unmodified lines | ||
| 456 | 456 | /* Test the shrinker functions and dump the result */ |
| 457 | 457 | static int ttm_tt_debugfs_shrink_show(struct seq_file *m, void *data) |
| 458 | 458 | { |
| 459 | − struct ttm_operation_ctx ctx = { false, false }; | |
| 459 | + struct ttm_operation_ctx ctx = { }; | |
| 460 | 460 | |
| 461 | 461 | seq_printf(m, "%d\n", ttm_global_swapout(&ctx, GFP_KERNEL)); |
| 462 | 462 | return 0; |
| @@ -13,8 +13,7 @@ v3d-y := \ · 12 unmodified lines | ||
| 13 | 13 | v3d_trace_points.o \ |
| 14 | 14 | v3d_sched.o \ |
| 15 | 15 | v3d_sysfs.o \ |
| 16 | − v3d_submit.o \ | |
| 17 | − v3d_gemfs.o | |
| 16 | + v3d_submit.o | |
| 18 | 17 | |
| 19 | 18 | v3d-$(CONFIG_DEBUG_FS) += v3d_debugfs.o |
| 20 | 19 | |
| @@ -114,7 +114,7 @@ v3d_bo_create_finish(struct drm_gem_object *obj) · 113 unmodified lines | ||
| 114 | 114 | if (IS_ERR(sgt)) |
| 115 | 115 | return PTR_ERR(sgt); |
| 116 | 116 | |
| 117 | − if (!v3d->gemfs) | |
| 117 | + if (!drm_gem_get_huge_mnt(obj->dev)) | |
| 118 | 118 | align = SZ_4K; |
| 119 | 119 | else if (obj->size >= SZ_1M) |
| 120 | 120 | align = SZ_1M; |
| @@ -150,12 +150,10 @@ struct v3d_bo *v3d_bo_create(struct drm_device *dev, struct drm_file *file_priv, · 29 unmodified lines | ||
| 150 | 150 | size_t unaligned_size) |
| 151 | 151 | { |
| 152 | 152 | struct drm_gem_shmem_object *shmem_obj; |
| 153 | − struct v3d_dev *v3d = to_v3d_dev(dev); | |
| 154 | 153 | struct v3d_bo *bo; |
| 155 | 154 | int ret; |
| 156 | 155 | |
| 157 | − shmem_obj = drm_gem_shmem_create_with_mnt(dev, unaligned_size, | |
| 158 | − v3d->gemfs); | |
| 156 | + shmem_obj = drm_gem_shmem_create(dev, unaligned_size); | |
| 159 | 157 | if (IS_ERR(shmem_obj)) |
| 160 | 158 | return ERR_CAST(shmem_obj); |
| 161 | 159 | bo = to_v3d_bo(&shmem_obj->base); |
| @@ -107,7 +107,7 @@ static int v3d_get_param_ioctl(struct drm_device *dev, void *data, · 106 unmodified lines | ||
| 107 | 107 | args->value = v3d->perfmon_info.max_counters; |
| 108 | 108 | return 0; |
| 109 | 109 | case DRM_V3D_PARAM_SUPPORTS_SUPER_PAGES: |
| 110 | − args->value = !!v3d->gemfs; | |
| 110 | + args->value = !!drm_gem_get_huge_mnt(dev); | |
| 111 | 111 | return 0; |
| 112 | 112 | case DRM_V3D_PARAM_GLOBAL_RESET_COUNTER: |
| 113 | 113 | mutex_lock(&v3d->reset_lock); |
| @@ -158,11 +158,6 @@ struct v3d_dev { · 157 unmodified lines | ||
| 158 | 158 | struct drm_mm mm; |
| 159 | 159 | spinlock_t mm_lock; |
| 160 | 160 | |
| 161 | − /* | |
| 162 | − * tmpfs instance used for shmem backed objects | |
| 163 | − */ | |
| 164 | − struct vfsmount *gemfs; | |
| 165 | − | |
| 166 | 161 | struct work_struct overflow_mem_work; |
| 167 | 162 | |
| 168 | 163 | struct v3d_queue_state queue[V3D_MAX_QUEUES]; |
| @@ -569,6 +564,7 @@ extern const struct dma_fence_ops v3d_fence_ops; · 400 unmodified lines | ||
| 569 | 564 | struct dma_fence *v3d_fence_create(struct v3d_dev *v3d, enum v3d_queue q); |
| 570 | 565 | |
| 571 | 566 | /* v3d_gem.c */ |
| 567 | +extern bool super_pages; | |
| 572 | 568 | int v3d_gem_init(struct drm_device *dev); |
| 573 | 569 | void v3d_gem_destroy(struct drm_device *dev); |
| 574 | 570 | void v3d_reset_sms(struct v3d_dev *v3d); |
| @@ -576,11 +572,6 @@ void v3d_reset(struct v3d_dev *v3d); · 1 unmodified line | ||
| 576 | 572 | void v3d_invalidate_caches(struct v3d_dev *v3d); |
| 577 | 573 | void v3d_clean_caches(struct v3d_dev *v3d); |
| 578 | 574 | |
| 579 | −/* v3d_gemfs.c */ | |
| 580 | −extern bool super_pages; | |
| 581 | −void v3d_gemfs_init(struct v3d_dev *v3d); | |
| 582 | −void v3d_gemfs_fini(struct v3d_dev *v3d); | |
| 583 | − | |
| 584 | 575 | /* v3d_submit.c */ |
| 585 | 576 | void v3d_job_cleanup(struct v3d_job *job); |
| 586 | 577 | void v3d_job_put(struct v3d_job *job); |
| @@ -259,6 +259,24 @@ v3d_invalidate_caches(struct v3d_dev *v3d) · 258 unmodified lines | ||
| 259 | 259 | v3d_invalidate_slices(v3d, 0); |
| 260 | 260 | } |
| 261 | 261 | |
| 262 | +static void | |
| 263 | +v3d_huge_mnt_init(struct v3d_dev *v3d) | |
| 264 | +{ | |
| 265 | + int err = 0; | |
| 266 | + | |
| 267 | + if (IS_ENABLED(CONFIG_TRANSPARENT_HUGEPAGE) && super_pages) | |
| 268 | + err = drm_gem_huge_mnt_create(&v3d->drm, "within_size"); | |
| 269 | + | |
| 270 | + if (drm_gem_get_huge_mnt(&v3d->drm)) | |
| 271 | + drm_info(&v3d->drm, "Using Transparent Hugepages\n"); | |
| 272 | + else if (err) | |
| 273 | + drm_warn(&v3d->drm, "Can't use Transparent Hugepages (%d)\n", | |
| 274 | + err); | |
| 275 | + else | |
| 276 | + drm_notice(&v3d->drm, | |
| 277 | + "Transparent Hugepage support is recommended for optimal performance on this platform!\n"); | |
| 278 | +} | |
| 279 | + | |
| 262 | 280 | int |
| 263 | 281 | v3d_gem_init(struct drm_device *dev) |
| 264 | 282 | { |
| @@ -310,7 +328,7 @@ v3d_gem_init(struct drm_device *dev) · 45 unmodified lines | ||
| 310 | 328 | v3d_init_hw_state(v3d); |
| 311 | 329 | v3d_mmu_set_page_table(v3d); |
| 312 | 330 | |
| 313 | − v3d_gemfs_init(v3d); | |
| 331 | + v3d_huge_mnt_init(v3d); | |
| 314 | 332 | |
| 315 | 333 | ret = v3d_sched_init(v3d); |
| 316 | 334 | if (ret) { |
| @@ -330,7 +348,6 @@ v3d_gem_destroy(struct drm_device *dev) · 13 unmodified lines | ||
| 330 | 348 | enum v3d_queue q; |
| 331 | 349 | |
| 332 | 350 | v3d_sched_fini(v3d); |
| 333 | − v3d_gemfs_fini(v3d); | |
| 334 | 351 | |
| 335 | 352 | /* Waiting for jobs to finish would need to be done before |
| 336 | 353 | * unregistering V3D. |
| @@ -1,62 +0,0 @@ | ||
| 1 | −// SPDX-License-Identifier: GPL-2.0+ | |
| 2 | −/* Copyright (C) 2024 Raspberry Pi */ | |
| 3 | − | |
| 4 | −#include <linux/fs.h> | |
| 5 | −#include <linux/mount.h> | |
| 6 | −#include <linux/fs_context.h> | |
| 7 | − | |
| 8 | −#include <drm/drm_print.h> | |
| 9 | − | |
| 10 | −#include "v3d_drv.h" | |
| 11 | − | |
| 12 | −void v3d_gemfs_init(struct v3d_dev *v3d) | |
| 13 | −{ | |
| 14 | − struct file_system_type *type; | |
| 15 | − struct fs_context *fc; | |
| 16 | − struct vfsmount *gemfs; | |
| 17 | − int ret; | |
| 18 | − | |
| 19 | − /* | |
| 20 | − * By creating our own shmemfs mountpoint, we can pass in | |
| 21 | − * mount flags that better match our usecase. However, we | |
| 22 | − * only do so on platforms which benefit from it. | |
| 23 | − */ | |
| 24 | − if (!IS_ENABLED(CONFIG_TRANSPARENT_HUGEPAGE)) | |
| 25 | − goto err; | |
| 26 | − | |
| 27 | − /* The user doesn't want to enable Super Pages */ | |
| 28 | − if (!super_pages) | |
| 29 | − goto err; | |
| 30 | − | |
| 31 | − type = get_fs_type("tmpfs"); | |
| 32 | − if (!type) | |
| 33 | − goto err; | |
| 34 | − | |
| 35 | − fc = fs_context_for_mount(type, SB_KERNMOUNT); | |
| 36 | − if (IS_ERR(fc)) | |
| 37 | − goto err; | |
| 38 | − ret = vfs_parse_fs_string(fc, "source", "tmpfs"); | |
| 39 | − if (!ret) | |
| 40 | − ret = vfs_parse_fs_string(fc, "huge", "within_size"); | |
| 41 | − if (!ret) | |
| 42 | − gemfs = fc_mount_longterm(fc); | |
| 43 | − put_fs_context(fc); | |
| 44 | − if (ret) | |
| 45 | − goto err; | |
| 46 | − | |
| 47 | − v3d->gemfs = gemfs; | |
| 48 | − drm_info(&v3d->drm, "Using Transparent Hugepages\n"); | |
| 49 | − | |
| 50 | − return; | |
| 51 | − | |
| 52 | −err: | |
| 53 | − v3d->gemfs = NULL; | |
| 54 | − drm_notice(&v3d->drm, | |
| 55 | − "Transparent Hugepage support is recommended for optimal performance on this platform!\n"); | |
| 56 | −} | |
| 57 | − | |
| 58 | −void v3d_gemfs_fini(struct v3d_dev *v3d) | |
| 59 | −{ | |
| 60 | − if (v3d->gemfs) | |
| 61 | − kern_unmount(v3d->gemfs); | |
| 62 | −} | |
| @@ -0,0 +1,9 @@ | ||
| 1 | +# SPDX-License-Identifier: GPL-2.0-only | |
| 2 | +config DRM_VGEM | |
| 3 | + tristate "Virtual GEM provider" | |
| 4 | + depends on DRM && MMU | |
| 5 | + select DRM_GEM_SHMEM_HELPER | |
| 6 | + help | |
| 7 | + Choose this option to get a virtual graphics memory manager, | |
| 8 | + as used by Mesa's software renderer for enhanced performance. | |
| 9 | + If M is selected the module will be called vgem. | |
| @@ -85,7 +85,6 @@ void xe_hw_fence_irq_finish(struct xe_hw_fence_irq *irq) · 84 unmodified lines | ||
| 85 | 85 | { |
| 86 | 86 | struct xe_hw_fence *fence, *next; |
| 87 | 87 | unsigned long flags; |
| 88 | − int err; | |
| 89 | 88 | bool tmp; |
| 90 | 89 | |
| 91 | 90 | if (XE_WARN_ON(!list_empty(&irq->pending))) { |
| @@ -93,9 +92,8 @@ void xe_hw_fence_irq_finish(struct xe_hw_fence_irq *irq) · 1 unmodified line | ||
| 93 | 92 | spin_lock_irqsave(&irq->lock, flags); |
| 94 | 93 | list_for_each_entry_safe(fence, next, &irq->pending, irq_link) { |
| 95 | 94 | list_del_init(&fence->irq_link); |
| 96 | − err = dma_fence_signal_locked(&fence->dma); | |
| 95 | + XE_WARN_ON(dma_fence_check_and_signal_locked(&fence->dma)); | |
| 97 | 96 | dma_fence_put(&fence->dma); |
| 98 | − XE_WARN_ON(err); | |
| 99 | 97 | } |
| 100 | 98 | spin_unlock_irqrestore(&irq->lock, flags); |
| 101 | 99 | dma_fence_end_signalling(tmp); |
| @@ -2289,28 +2289,6 @@ static bool fbcon_blank(struct vc_data *vc, enum vesa_blank_mode blank, · 2288 unmodified lines | ||
| 2289 | 2289 | return false; |
| 2290 | 2290 | } |
| 2291 | 2291 | |
| 2292 | −static void fbcon_debug_enter(struct vc_data *vc) | |
| 2293 | −{ | |
| 2294 | − struct fb_info *info = fbcon_info_from_console(vc->vc_num); | |
| 2295 | − struct fbcon_par *par = info->fbcon_par; | |
| 2296 | − | |
| 2297 | − par->save_graphics = par->graphics; | |
| 2298 | − par->graphics = 0; | |
| 2299 | − if (info->fbops->fb_debug_enter) | |
| 2300 | − info->fbops->fb_debug_enter(info); | |
| 2301 | − fbcon_set_palette(vc, color_table); | |
| 2302 | −} | |
| 2303 | − | |
| 2304 | −static void fbcon_debug_leave(struct vc_data *vc) | |
| 2305 | −{ | |
| 2306 | − struct fb_info *info = fbcon_info_from_console(vc->vc_num); | |
| 2307 | − struct fbcon_par *par = info->fbcon_par; | |
| 2308 | − | |
| 2309 | − par->graphics = par->save_graphics; | |
| 2310 | − if (info->fbops->fb_debug_leave) | |
| 2311 | − info->fbops->fb_debug_leave(info); | |
| 2312 | −} | |
| 2313 | − | |
| 2314 | 2292 | static int fbcon_get_font(struct vc_data *vc, struct console_font *font, unsigned int vpitch) |
| 2315 | 2293 | { |
| 2316 | 2294 | u8 *fontdata = vc->vc_font.data; |
| @@ -3214,8 +3192,6 @@ static const struct consw fb_con = { · 897 unmodified lines | ||
| 3214 | 3192 | .con_set_palette = fbcon_set_palette, |
| 3215 | 3193 | .con_invert_region = fbcon_invert_region, |
| 3216 | 3194 | .con_resize = fbcon_resize, |
| 3217 | − .con_debug_enter = fbcon_debug_enter, | |
| 3218 | − .con_debug_leave = fbcon_debug_leave, | |
| 3219 | 3195 | }; |
| 3220 | 3196 | |
| 3221 | 3197 | static ssize_t rotate_store(struct device *device, |
| @@ -79,7 +79,6 @@ struct fbcon_par { · 78 unmodified lines | ||
| 79 | 79 | int cursor_reset; |
| 80 | 80 | int blank_state; |
| 81 | 81 | int graphics; |
| 82 | − int save_graphics; /* for debug enter/leave */ | |
| 83 | 82 | bool initialized; |
| 84 | 83 | int rotate; |
| 85 | 84 | int cur_rotate; |
| @@ -339,6 +339,11 @@ struct drm_private_state_funcs { · 338 unmodified lines | ||
| 339 | 339 | * drm_atomic_helper_wait_for_dependencies(). |
| 340 | 340 | */ |
| 341 | 341 | struct drm_private_obj { |
| 342 | + /** | |
| 343 | + * @dev: parent DRM device | |
| 344 | + */ | |
| 345 | + struct drm_device *dev; | |
| 346 | + | |
| 342 | 347 | /** |
| 343 | 348 | * @head: List entry used to attach a private object to a &drm_device |
| 344 | 349 | * (queued to &drm_mode_config.privobj_list). |
| @@ -1143,6 +1143,14 @@ struct drm_bridge { · 1142 unmodified lines | ||
| 1143 | 1143 | */ |
| 1144 | 1144 | struct kref refcount; |
| 1145 | 1145 | |
| 1146 | + /** | |
| 1147 | + * @unplugged: | |
| 1148 | + * | |
| 1149 | + * Flag to tell if the bridge has been unplugged. | |
| 1150 | + * See drm_bridge_enter() and drm_bridge_unplug(). | |
| 1151 | + */ | |
| 1152 | + bool unplugged; | |
| 1153 | + | |
| 1146 | 1154 | /** @driver_private: pointer to the bridge driver's internal context */ |
| 1147 | 1155 | void *driver_private; |
| 1148 | 1156 | /** @ops: bitmask of operations supported by the bridge */ |
| @@ -1278,6 +1286,10 @@ drm_priv_to_bridge(struct drm_private_obj *priv) · 129 unmodified lines | ||
| 1278 | 1286 | return container_of(priv, struct drm_bridge, base); |
| 1279 | 1287 | } |
| 1280 | 1288 | |
| 1289 | +bool drm_bridge_enter(struct drm_bridge *bridge, int *idx); | |
| 1290 | +void drm_bridge_exit(int idx); | |
| 1291 | +void drm_bridge_unplug(struct drm_bridge *bridge); | |
| 1292 | + | |
| 1281 | 1293 | struct drm_bridge *drm_bridge_get(struct drm_bridge *bridge); |
| 1282 | 1294 | void drm_bridge_put(struct drm_bridge *bridge); |
| 1283 | 1295 | |
| @@ -3,6 +3,9 @@ · 2 unmodified lines | ||
| 3 | 3 | |
| 4 | 4 | #include <linux/list.h> |
| 5 | 5 | #include <linux/kref.h> |
| 6 | +#ifdef CONFIG_TRANSPARENT_HUGEPAGE | |
| 7 | +#include <linux/mount.h> | |
| 8 | +#endif | |
| 6 | 9 | #include <linux/mutex.h> |
| 7 | 10 | #include <linux/idr.h> |
| 8 | 11 | #include <linux/sched.h> |
| @@ -168,6 +171,18 @@ struct drm_device { · 159 unmodified lines | ||
| 168 | 171 | */ |
| 169 | 172 | struct drm_master *master; |
| 170 | 173 | |
| 174 | +#ifdef CONFIG_TRANSPARENT_HUGEPAGE | |
| 175 | + /** | |
| 176 | + * @huge_mnt: | |
| 177 | + * | |
| 178 | + * Huge tmpfs mountpoint used at GEM object initialization | |
| 179 | + * drm_gem_object_init(). Drivers can call drm_gem_huge_mnt_create() to | |
| 180 | + * create, mount and use it. The default tmpfs mountpoint (`shm_mnt`) is | |
| 181 | + * used if NULL. | |
| 182 | + */ | |
| 183 | + struct vfsmount *huge_mnt; | |
| 184 | +#endif | |
| 185 | + | |
| 171 | 186 | /** |
| 172 | 187 | * @driver_features: per-device driver features |
| 173 | 188 | * |
| @@ -166,13 +166,6 @@ struct drm_fb_helper { · 165 unmodified lines | ||
| 166 | 166 | */ |
| 167 | 167 | struct mutex lock; |
| 168 | 168 | |
| 169 | − /** | |
| 170 | − * @kernel_fb_list: | |
| 171 | − * | |
| 172 | − * Entry on the global kernel_fb_helper_list, used for kgdb entry/exit. | |
| 173 | − */ | |
| 174 | − struct list_head kernel_fb_list; | |
| 175 | − | |
| 176 | 169 | /** |
| 177 | 170 | * @delayed_hotplug: |
| 178 | 171 | * |
| @@ -236,8 +229,6 @@ drm_fb_helper_from_client(struct drm_client_dev *client) · 57 unmodified lines | ||
| 236 | 229 | .fb_setcmap = drm_fb_helper_setcmap, \ |
| 237 | 230 | .fb_blank = drm_fb_helper_blank, \ |
| 238 | 231 | .fb_pan_display = drm_fb_helper_pan_display, \ |
| 239 | − .fb_debug_enter = drm_fb_helper_debug_enter, \ | |
| 240 | − .fb_debug_leave = drm_fb_helper_debug_leave, \ | |
| 241 | 232 | .fb_ioctl = drm_fb_helper_ioctl |
| 242 | 233 | |
| 243 | 234 | #ifdef CONFIG_DRM_FBDEV_EMULATION |
| @@ -280,8 +271,6 @@ int drm_fb_helper_ioctl(struct fb_info *info, unsigned int cmd, · 36 unmodified lines | ||
| 280 | 271 | |
| 281 | 272 | int drm_fb_helper_hotplug_event(struct drm_fb_helper *fb_helper); |
| 282 | 273 | int drm_fb_helper_initial_config(struct drm_fb_helper *fb_helper); |
| 283 | −int drm_fb_helper_debug_enter(struct fb_info *info); | |
| 284 | −int drm_fb_helper_debug_leave(struct fb_info *info); | |
| 285 | 274 | #else |
| 286 | 275 | static inline void drm_fb_helper_prepare(struct drm_device *dev, |
| 287 | 276 | struct drm_fb_helper *helper, |
| @@ -387,16 +376,6 @@ static inline int drm_fb_helper_initial_config(struct drm_fb_helper *fb_helper) · 99 unmodified lines | ||
| 387 | 376 | { |
| 388 | 377 | return 0; |
| 389 | 378 | } |
| 390 | − | |
| 391 | −static inline int drm_fb_helper_debug_enter(struct fb_info *info) | |
| 392 | −{ | |
| 393 | − return 0; | |
| 394 | −} | |
| 395 | − | |
| 396 | −static inline int drm_fb_helper_debug_leave(struct fb_info *info) | |
| 397 | −{ | |
| 398 | − return 0; | |
| 399 | −} | |
| 400 | 379 | #endif |
| 401 | 380 | |
| 402 | 381 | #endif |
| @@ -40,6 +40,9 @@ · 39 unmodified lines | ||
| 40 | 40 | #include <linux/list.h> |
| 41 | 41 | #include <linux/mutex.h> |
| 42 | 42 | |
| 43 | +#ifdef CONFIG_TRANSPARENT_HUGEPAGE | |
| 44 | +#include <drm/drm_device.h> | |
| 45 | +#endif | |
| 43 | 46 | #include <drm/drm_vma_manager.h> |
| 44 | 47 | |
| 45 | 48 | struct iosys_map; |
| @@ -469,6 +472,7 @@ struct drm_gem_object { · 423 unmodified lines | ||
| 469 | 472 | .poll = drm_poll,\ |
| 470 | 473 | .read = drm_read,\ |
| 471 | 474 | .llseek = noop_llseek,\ |
| 475 | + .get_unmapped_area = drm_gem_get_unmapped_area,\ | |
| 472 | 476 | .mmap = drm_gem_mmap, \ |
| 473 | 477 | .fop_flags = FOP_UNSIGNED_OFFSET |
| 474 | 478 | |
| @@ -491,13 +495,40 @@ struct drm_gem_object { · 16 unmodified lines | ||
| 491 | 495 | DRM_GEM_FOPS,\ |
| 492 | 496 | } |
| 493 | 497 | |
| 498 | +#ifdef CONFIG_TRANSPARENT_HUGEPAGE | |
| 499 | +int drm_gem_huge_mnt_create(struct drm_device *dev, const char *value); | |
| 500 | +#else | |
| 501 | +static inline int drm_gem_huge_mnt_create(struct drm_device *dev, | |
| 502 | + const char *value) | |
| 503 | +{ | |
| 504 | + return 0; | |
| 505 | +} | |
| 506 | +#endif | |
| 507 | + | |
| 508 | +/** | |
| 509 | + * drm_gem_get_huge_mnt - Get the huge tmpfs mountpoint used by a DRM device | |
| 510 | + * @dev: DRM device | |
| 511 | + | |
| 512 | + * This function gets the huge tmpfs mountpoint used by DRM device @dev. A huge | |
| 513 | + * tmpfs mountpoint is used instead of `shm_mnt` after a successful call to | |
| 514 | + * drm_gem_huge_mnt_create() when CONFIG_TRANSPARENT_HUGEPAGE is enabled. | |
| 515 | + | |
| 516 | + * Returns: | |
| 517 | + * The huge tmpfs mountpoint in use, NULL otherwise. | |
| 518 | + */ | |
| 519 | +static inline struct vfsmount *drm_gem_get_huge_mnt(struct drm_device *dev) | |
| 520 | +{ | |
| 521 | +#ifdef CONFIG_TRANSPARENT_HUGEPAGE | |
| 522 | + return dev->huge_mnt; | |
| 523 | +#else | |
| 524 | + return NULL; | |
| 525 | +#endif | |
| 526 | +} | |
| 527 | + | |
| 494 | 528 | void drm_gem_object_release(struct drm_gem_object *obj); |
| 495 | 529 | void drm_gem_object_free(struct kref *kref); |
| 496 | 530 | int drm_gem_object_init(struct drm_device *dev, |
| 497 | 531 | struct drm_gem_object *obj, size_t size); |
| 498 | −int drm_gem_object_init_with_mnt(struct drm_device *dev, | |
| 499 | − struct drm_gem_object *obj, size_t size, | |
| 500 | − struct vfsmount *gemfs); | |
| 501 | 532 | void drm_gem_private_object_init(struct drm_device *dev, |
| 502 | 533 | struct drm_gem_object *obj, size_t size); |
| 503 | 534 | void drm_gem_private_object_fini(struct drm_gem_object *obj); |
| @@ -507,6 +538,14 @@ int drm_gem_mmap_obj(struct drm_gem_object *obj, unsigned long obj_size, · 3 unmodified lines | ||
| 507 | 538 | struct vm_area_struct *vma); |
| 508 | 539 | int drm_gem_mmap(struct file *filp, struct vm_area_struct *vma); |
| 509 | 540 | |
| 541 | +#ifdef CONFIG_MMU | |
| 542 | +unsigned long drm_gem_get_unmapped_area(struct file *filp, unsigned long uaddr, | |
| 543 | + unsigned long len, unsigned long pgoff, | |
| 544 | + unsigned long flags); | |
| 545 | +#else | |
| 546 | +#define drm_gem_get_unmapped_area NULL | |
| 547 | +#endif | |
| 548 | + | |
| 510 | 549 | /** |
| 511 | 550 | * drm_gem_object_get - acquire a GEM buffer object reference |
| 512 | 551 | * @obj: GEM buffer object |
| @@ -109,9 +109,6 @@ struct drm_gem_shmem_object { · 108 unmodified lines | ||
| 109 | 109 | |
| 110 | 110 | int drm_gem_shmem_init(struct drm_device *dev, struct drm_gem_shmem_object *shmem, size_t size); |
| 111 | 111 | struct drm_gem_shmem_object *drm_gem_shmem_create(struct drm_device *dev, size_t size); |
| 112 | −struct drm_gem_shmem_object *drm_gem_shmem_create_with_mnt(struct drm_device *dev, | |
| 113 | − size_t size, | |
| 114 | − struct vfsmount *gemfs); | |
| 115 | 112 | void drm_gem_shmem_release(struct drm_gem_shmem_object *shmem); |
| 116 | 113 | void drm_gem_shmem_free(struct drm_gem_shmem_object *shmem); |
| 117 | 114 | |
| @@ -52,11 +52,6 @@ struct drm_scanout_buffer; · 51 unmodified lines | ||
| 52 | 52 | struct drm_writeback_connector; |
| 53 | 53 | struct drm_writeback_job; |
| 54 | 54 | |
| 55 | −enum mode_set_atomic { | |
| 56 | − LEAVE_ATOMIC_MODE_SET, | |
| 57 | − ENTER_ATOMIC_MODE_SET, | |
| 58 | −}; | |
| 59 | − | |
| 60 | 55 | /** |
| 61 | 56 | * struct drm_crtc_helper_funcs - helper operations for CRTCs |
| 62 | 57 | * |
| @@ -253,24 +248,6 @@ struct drm_crtc_helper_funcs { · 190 unmodified lines | ||
| 253 | 248 | int (*mode_set_base)(struct drm_crtc *crtc, int x, int y, |
| 254 | 249 | struct drm_framebuffer *old_fb); |
| 255 | 250 | |
| 256 | − /** | |
| 257 | − * @mode_set_base_atomic: | |
| 258 | − * | |
| 259 | − * This callback is used by the fbdev helpers to set a new framebuffer | |
| 260 | − * and scanout without sleeping, i.e. from an atomic calling context. It | |
| 261 | − * is only used to implement kgdb support. | |
| 262 | − * | |
| 263 | − * This callback is optional and only needed for kgdb support in the fbdev | |
| 264 | − * helpers. | |
| 265 | − * | |
| 266 | − * RETURNS: | |
| 267 | − * | |
| 268 | − * 0 on success or a negative error code on failure. | |
| 269 | − */ | |
| 270 | − int (*mode_set_base_atomic)(struct drm_crtc *crtc, | |
| 271 | − struct drm_framebuffer *fb, int x, int y, | |
| 272 | − enum mode_set_atomic); | |
| 273 | − | |
| 274 | 251 | /** |
| 275 | 252 | * @disable: |
| 276 | 253 | * |
| @@ -302,8 +302,7 @@ bool drm_handle_vblank(struct drm_device *dev, unsigned int pipe); · 301 unmodified lines | ||
| 302 | 302 | bool drm_crtc_handle_vblank(struct drm_crtc *crtc); |
| 303 | 303 | int drm_crtc_vblank_get(struct drm_crtc *crtc); |
| 304 | 304 | void drm_crtc_vblank_put(struct drm_crtc *crtc); |
| 305 | −void drm_wait_one_vblank(struct drm_device *dev, unsigned int pipe); | |
| 306 | −void drm_crtc_wait_one_vblank(struct drm_crtc *crtc); | |
| 305 | +int drm_crtc_wait_one_vblank(struct drm_crtc *crtc); | |
| 307 | 306 | void drm_crtc_vblank_off(struct drm_crtc *crtc); |
| 308 | 307 | void drm_crtc_vblank_reset(struct drm_crtc *crtc); |
| 309 | 308 | void drm_crtc_vblank_on_config(struct drm_crtc *crtc, |
| @@ -364,11 +364,12 @@ static inline void dma_fence_end_signalling(bool cookie) {} · 363 unmodified lines | ||
| 364 | 364 | static inline void __dma_fence_might_wait(void) {} |
| 365 | 365 | #endif |
| 366 | 366 | |
| 367 | −int dma_fence_signal(struct dma_fence *fence); | |
| 368 | −int dma_fence_signal_locked(struct dma_fence *fence); | |
| 369 | −int dma_fence_signal_timestamp(struct dma_fence *fence, ktime_t timestamp); | |
| 370 | −int dma_fence_signal_timestamp_locked(struct dma_fence *fence, | |
| 371 | − ktime_t timestamp); | |
| 367 | +void dma_fence_signal(struct dma_fence *fence); | |
| 368 | +bool dma_fence_check_and_signal(struct dma_fence *fence); | |
| 369 | +bool dma_fence_check_and_signal_locked(struct dma_fence *fence); | |
| 370 | +void dma_fence_signal_locked(struct dma_fence *fence); | |
| 371 | +void dma_fence_signal_timestamp(struct dma_fence *fence, ktime_t timestamp); | |
| 372 | +void dma_fence_signal_timestamp_locked(struct dma_fence *fence, ktime_t timestamp); | |
| 372 | 373 | signed long dma_fence_default_wait(struct dma_fence *fence, |
| 373 | 374 | bool intr, signed long timeout); |
| 374 | 375 | int dma_fence_add_callback(struct dma_fence *fence, |
| @@ -401,6 +402,26 @@ void dma_fence_enable_sw_signaling(struct dma_fence *fence); · 26 unmodified lines | ||
| 401 | 402 | const char __rcu *dma_fence_driver_name(struct dma_fence *fence); |
| 402 | 403 | const char __rcu *dma_fence_timeline_name(struct dma_fence *fence); |
| 403 | 404 | |
| 405 | +/* | |
| 406 | + * dma_fence_test_signaled_flag - Only check whether a fence is signaled yet. | |
| 407 | + * @fence: the fence to check | |
| 408 | + * | |
| 409 | + * This function just checks whether @fence is signaled, without interacting | |
| 410 | + * with the fence in any way. The user must, therefore, ensure through other | |
| 411 | + * means that fences get signaled eventually. | |
| 412 | + * | |
| 413 | + * This function uses test_bit(), which is thread-safe. Naturally, this function | |
| 414 | + * should be used opportunistically; a fence could get signaled at any moment | |
| 415 | + * after the check is done. | |
| 416 | + * | |
| 417 | + * Return: true if signaled, false otherwise. | |
| 418 | + */ | |
| 419 | +static inline bool | |
| 420 | +dma_fence_test_signaled_flag(struct dma_fence *fence) | |
| 421 | +{ | |
| 422 | + return test_bit(DMA_FENCE_FLAG_SIGNALED_BIT, &fence->flags); | |
| 423 | +} | |
| 424 | + | |
| 404 | 425 | /** |
| 405 | 426 | * dma_fence_is_signaled_locked - Return an indication if the fence |
| 406 | 427 | * is signaled yet. |
| @@ -418,7 +439,7 @@ const char __rcu *dma_fence_timeline_name(struct dma_fence *fence); · 11 unmodified lines | ||
| 418 | 439 | static inline bool |
| 419 | 440 | dma_fence_is_signaled_locked(struct dma_fence *fence) |
| 420 | 441 | { |
| 421 | − if (test_bit(DMA_FENCE_FLAG_SIGNALED_BIT, &fence->flags)) | |
| 442 | + if (dma_fence_test_signaled_flag(fence)) | |
| 422 | 443 | return true; |
| 423 | 444 | |
| 424 | 445 | if (fence->ops->signaled && fence->ops->signaled(fence)) { |
| @@ -448,7 +469,7 @@ dma_fence_is_signaled_locked(struct dma_fence *fence) · 23 unmodified lines | ||
| 448 | 469 | static inline bool |
| 449 | 470 | dma_fence_is_signaled(struct dma_fence *fence) |
| 450 | 471 | { |
| 451 | − if (test_bit(DMA_FENCE_FLAG_SIGNALED_BIT, &fence->flags)) | |
| 472 | + if (dma_fence_test_signaled_flag(fence)) | |
| 452 | 473 | return true; |
| 453 | 474 | |
| 454 | 475 | if (fence->ops->signaled && fence->ops->signaled(fence)) { |
| @@ -304,10 +304,6 @@ struct fb_ops { · 303 unmodified lines | ||
| 304 | 304 | |
| 305 | 305 | /* teardown any resources to do with this framebuffer */ |
| 306 | 306 | void (*fb_destroy)(struct fb_info *info); |
| 307 | − | |
| 308 | − /* called at KDB enter and leave time to prepare the console */ | |
| 309 | − int (*fb_debug_enter)(struct fb_info *info); | |
| 310 | − int (*fb_debug_leave)(struct fb_info *info); | |
| 311 | 307 | }; |
| 312 | 308 | |
| 313 | 309 | #ifdef CONFIG_FB_TILEBLITTING |
| @@ -24,6 +24,8 @@ extern "C" { · 23 unmodified lines | ||
| 24 | 24 | #define DRM_PANFROST_SET_LABEL_BO 0x09 |
| 25 | 25 | #define DRM_PANFROST_JM_CTX_CREATE 0x0a |
| 26 | 26 | #define DRM_PANFROST_JM_CTX_DESTROY 0x0b |
| 27 | +#define DRM_PANFROST_SYNC_BO 0x0c | |
| 28 | +#define DRM_PANFROST_QUERY_BO_INFO 0x0d | |
| 27 | 29 | |
| 28 | 30 | #define DRM_IOCTL_PANFROST_SUBMIT DRM_IOW(DRM_COMMAND_BASE + DRM_PANFROST_SUBMIT, struct drm_panfrost_submit) |
| 29 | 31 | #define DRM_IOCTL_PANFROST_WAIT_BO DRM_IOW(DRM_COMMAND_BASE + DRM_PANFROST_WAIT_BO, struct drm_panfrost_wait_bo) |
| @@ -35,6 +37,8 @@ extern "C" { · 5 unmodified lines | ||
| 35 | 37 | #define DRM_IOCTL_PANFROST_SET_LABEL_BO DRM_IOWR(DRM_COMMAND_BASE + DRM_PANFROST_SET_LABEL_BO, struct drm_panfrost_set_label_bo) |
| 36 | 38 | #define DRM_IOCTL_PANFROST_JM_CTX_CREATE DRM_IOWR(DRM_COMMAND_BASE + DRM_PANFROST_JM_CTX_CREATE, struct drm_panfrost_jm_ctx_create) |
| 37 | 39 | #define DRM_IOCTL_PANFROST_JM_CTX_DESTROY DRM_IOWR(DRM_COMMAND_BASE + DRM_PANFROST_JM_CTX_DESTROY, struct drm_panfrost_jm_ctx_destroy) |
| 40 | +#define DRM_IOCTL_PANFROST_SYNC_BO DRM_IOWR(DRM_COMMAND_BASE + DRM_PANFROST_SYNC_BO, struct drm_panfrost_sync_bo) | |
| 41 | +#define DRM_IOCTL_PANFROST_QUERY_BO_INFO DRM_IOWR(DRM_COMMAND_BASE + DRM_PANFROST_QUERY_BO_INFO, struct drm_panfrost_query_bo_info) | |
| 38 | 42 | |
| 39 | 43 | /* |
| 40 | 44 | * Unstable ioctl(s): only exposed when the unsafe unstable_ioctls module |
| @@ -120,9 +124,12 @@ struct drm_panfrost_wait_bo { · 79 unmodified lines | ||
| 120 | 124 | __s64 timeout_ns; |
| 121 | 125 | }; |
| 122 | 126 | |
| 123 | −/* Valid flags to pass to drm_panfrost_create_bo */ | |
| 127 | +/* Valid flags to pass to drm_panfrost_create_bo. | |
| 128 | + * PANFROST_BO_WB_MMAP can't be set if PANFROST_BO_HEAP is. | |
| 129 | + */ | |
| 124 | 130 | #define PANFROST_BO_NOEXEC 1 |
| 125 | 131 | #define PANFROST_BO_HEAP 2 |
| 132 | +#define PANFROST_BO_WB_MMAP 4 | |
| 126 | 133 | |
| 127 | 134 | /** |
| 128 | 135 | * struct drm_panfrost_create_bo - ioctl argument for creating Panfrost BOs. |
| @@ -228,6 +235,13 @@ enum drm_panfrost_param { · 99 unmodified lines | ||
| 228 | 235 | DRM_PANFROST_PARAM_SYSTEM_TIMESTAMP, |
| 229 | 236 | DRM_PANFROST_PARAM_SYSTEM_TIMESTAMP_FREQUENCY, |
| 230 | 237 | DRM_PANFROST_PARAM_ALLOWED_JM_CTX_PRIORITIES, |
| 238 | + DRM_PANFROST_PARAM_SELECTED_COHERENCY, | |
| 239 | +}; | |
| 240 | + | |
| 241 | +enum drm_panfrost_gpu_coherency { | |
| 242 | + DRM_PANFROST_GPU_COHERENCY_ACE_LITE = 0, | |
| 243 | + DRM_PANFROST_GPU_COHERENCY_ACE = 1, | |
| 244 | + DRM_PANFROST_GPU_COHERENCY_NONE = 31, | |
| 231 | 245 | }; |
| 232 | 246 | |
| 233 | 247 | struct drm_panfrost_get_param { |
| @@ -301,6 +315,66 @@ struct drm_panfrost_set_label_bo { · 67 unmodified lines | ||
| 301 | 315 | __u64 label; |
| 302 | 316 | }; |
| 303 | 317 | |
| 318 | +/* Valid flags to pass to drm_panfrost_bo_sync_op */ | |
| 319 | +#define PANFROST_BO_SYNC_CPU_CACHE_FLUSH 0 | |
| 320 | +#define PANFROST_BO_SYNC_CPU_CACHE_FLUSH_AND_INVALIDATE 1 | |
| 321 | + | |
| 322 | +/** | |
| 323 | + * struct drm_panthor_bo_flush_map_op - BO map sync op | |
| 324 | + */ | |
| 325 | +struct drm_panfrost_bo_sync_op { | |
| 326 | + /** @handle: Handle of the buffer object to sync. */ | |
| 327 | + __u32 handle; | |
| 328 | + | |
| 329 | + /** @type: Type of sync operation. */ | |
| 330 | + __u32 type; | |
| 331 | + | |
| 332 | + /** | |
| 333 | + * @offset: Offset into the BO at which the sync range starts. | |
| 334 | + * | |
| 335 | + * This will be rounded down to the nearest cache line as needed. | |
| 336 | + */ | |
| 337 | + __u32 offset; | |
| 338 | + | |
| 339 | + /** | |
| 340 | + * @size: Size of the range to sync | |
| 341 | + * | |
| 342 | + * @size + @offset will be rounded up to the nearest cache line as | |
| 343 | + * needed. | |
| 344 | + */ | |
| 345 | + __u32 size; | |
| 346 | +}; | |
| 347 | + | |
| 348 | +/** | |
| 349 | + * struct drm_panfrost_sync_bo - ioctl argument for syncing BO maps | |
| 350 | + */ | |
| 351 | +struct drm_panfrost_sync_bo { | |
| 352 | + /** Array of struct drm_panfrost_bo_sync_op */ | |
| 353 | + __u64 ops; | |
| 354 | + | |
| 355 | + /** Number of BO sync ops */ | |
| 356 | + __u32 op_count; | |
| 357 | + | |
| 358 | + __u32 pad; | |
| 359 | +}; | |
| 360 | + | |
| 361 | +/** BO comes from a different subsystem. */ | |
| 362 | +#define DRM_PANFROST_BO_IS_IMPORTED (1 << 0) | |
| 363 | + | |
| 364 | +struct drm_panfrost_query_bo_info { | |
| 365 | + /** Handle of the object being queried. */ | |
| 366 | + __u32 handle; | |
| 367 | + | |
| 368 | + /** Extra flags that are not coming from the BO_CREATE ioctl(). */ | |
| 369 | + __u32 extra_flags; | |
| 370 | + | |
| 371 | + /** Flags passed at creation time. */ | |
| 372 | + __u32 create_flags; | |
| 373 | + | |
| 374 | + /** Will be zero on return. */ | |
| 375 | + __u32 pad; | |
| 376 | +}; | |
| 377 | + | |
| 304 | 378 | /* Definitions for coredump decoding in user space */ |
| 305 | 379 | #define PANFROSTDUMP_MAJOR 1 |
| 306 | 380 | #define PANFROSTDUMP_MINOR 0 |
| @@ -144,6 +144,16 @@ enum drm_panthor_ioctl_id { · 143 unmodified lines | ||
| 144 | 144 | * pgoff_t size. |
| 145 | 145 | */ |
| 146 | 146 | DRM_PANTHOR_SET_USER_MMIO_OFFSET, |
| 147 | + | |
| 148 | + /** @DRM_PANTHOR_BO_SYNC: Sync BO data to/from the device */ | |
| 149 | + DRM_PANTHOR_BO_SYNC, | |
| 150 | + | |
| 151 | + /** | |
| 152 | + * @DRM_PANTHOR_BO_QUERY_INFO: Query information about a BO. | |
| 153 | + * | |
| 154 | + * This is useful for imported BOs. | |
| 155 | + */ | |
| 156 | + DRM_PANTHOR_BO_QUERY_INFO, | |
| 147 | 157 | }; |
| 148 | 158 | |
| 149 | 159 | /** |
| @@ -245,6 +255,26 @@ enum drm_panthor_dev_query_type { · 95 unmodified lines | ||
| 245 | 255 | DRM_PANTHOR_DEV_QUERY_GROUP_PRIORITIES_INFO, |
| 246 | 256 | }; |
| 247 | 257 | |
| 258 | +/** | |
| 259 | + * enum drm_panthor_gpu_coherency: Type of GPU coherency | |
| 260 | + */ | |
| 261 | +enum drm_panthor_gpu_coherency { | |
| 262 | + /** | |
| 263 | + * @DRM_PANTHOR_GPU_COHERENCY_ACE_LITE: ACE Lite coherency. | |
| 264 | + */ | |
| 265 | + DRM_PANTHOR_GPU_COHERENCY_ACE_LITE = 0, | |
| 266 | + | |
| 267 | + /** | |
| 268 | + * @DRM_PANTHOR_GPU_COHERENCY_ACE: ACE coherency. | |
| 269 | + */ | |
| 270 | + DRM_PANTHOR_GPU_COHERENCY_ACE = 1, | |
| 271 | + | |
| 272 | + /** | |
| 273 | + * @DRM_PANTHOR_GPU_COHERENCY_NONE: No coherency. | |
| 274 | + */ | |
| 275 | + DRM_PANTHOR_GPU_COHERENCY_NONE = 31, | |
| 276 | +}; | |
| 277 | + | |
| 248 | 278 | /** |
| 249 | 279 | * struct drm_panthor_gpu_info - GPU information |
| 250 | 280 | * |
| @@ -301,7 +331,16 @@ struct drm_panthor_gpu_info { · 50 unmodified lines | ||
| 301 | 331 | */ |
| 302 | 332 | __u32 thread_max_barrier_size; |
| 303 | 333 | |
| 304 | − /** @coherency_features: Coherency features. */ | |
| 334 | + /** | |
| 335 | + * @coherency_features: Coherency features. | |
| 336 | + * | |
| 337 | + * Combination of drm_panthor_gpu_coherency flags. | |
| 338 | + * | |
| 339 | + * Note that this is just what the coherency protocols supported by the | |
| 340 | + * GPU, but the actual coherency in place depends on the SoC | |
| 341 | + * integration and is reflected by | |
| 342 | + * drm_panthor_gpu_info::selected_coherency. | |
| 343 | + */ | |
| 305 | 344 | __u32 coherency_features; |
| 306 | 345 | |
| 307 | 346 | /** @texture_features: Texture features. */ |
| @@ -310,8 +349,12 @@ struct drm_panthor_gpu_info { · 2 unmodified lines | ||
| 310 | 349 | /** @as_present: Bitmask encoding the number of address-space exposed by the MMU. */ |
| 311 | 350 | __u32 as_present; |
| 312 | 351 | |
| 313 | − /** @pad0: MBZ. */ | |
| 314 | − __u32 pad0; | |
| 352 | + /** | |
| 353 | + * @select_coherency: Coherency selected for this device. | |
| 354 | + * | |
| 355 | + * One of drm_panthor_gpu_coherency. | |
| 356 | + */ | |
| 357 | + __u32 selected_coherency; | |
| 315 | 358 | |
| 316 | 359 | /** @shader_present: Bitmask encoding the shader cores exposed by the GPU. */ |
| 317 | 360 | __u64 shader_present; |
| @@ -638,6 +681,15 @@ struct drm_panthor_vm_get_state { · 320 unmodified lines | ||
| 638 | 681 | enum drm_panthor_bo_flags { |
| 639 | 682 | /** @DRM_PANTHOR_BO_NO_MMAP: The buffer object will never be CPU-mapped in userspace. */ |
| 640 | 683 | DRM_PANTHOR_BO_NO_MMAP = (1 << 0), |
| 684 | + | |
| 685 | + /** | |
| 686 | + * @DRM_PANTHOR_BO_WB_MMAP: Force "Write-Back Cacheable" CPU mapping. | |
| 687 | + * | |
| 688 | + * CPU map the buffer object in userspace by forcing the "Write-Back | |
| 689 | + * Cacheable" cacheability attribute. The mapping otherwise uses the | |
| 690 | + * "Non-Cacheable" attribute if the GPU is not IO coherent. | |
| 691 | + */ | |
| 692 | + DRM_PANTHOR_BO_WB_MMAP = (1 << 1), | |
| 641 | 693 | }; |
| 642 | 694 | |
| 643 | 695 | /** |
| @@ -1040,6 +1092,101 @@ struct drm_panthor_set_user_mmio_offset { · 396 unmodified lines | ||
| 1040 | 1092 | __u64 offset; |
| 1041 | 1093 | }; |
| 1042 | 1094 | |
| 1095 | +/** | |
| 1096 | + * enum drm_panthor_bo_sync_op_type - BO sync type | |
| 1097 | + */ | |
| 1098 | +enum drm_panthor_bo_sync_op_type { | |
| 1099 | + /** @DRM_PANTHOR_BO_SYNC_CPU_CACHE_FLUSH: Flush CPU caches. */ | |
| 1100 | + DRM_PANTHOR_BO_SYNC_CPU_CACHE_FLUSH = 0, | |
| 1101 | + | |
| 1102 | + /** @DRM_PANTHOR_BO_SYNC_CPU_CACHE_FLUSH_AND_INVALIDATE: Flush and invalidate CPU caches. */ | |
| 1103 | + DRM_PANTHOR_BO_SYNC_CPU_CACHE_FLUSH_AND_INVALIDATE = 1, | |
| 1104 | +}; | |
| 1105 | + | |
| 1106 | +/** | |
| 1107 | + * struct drm_panthor_bo_sync_op - BO map sync op | |
| 1108 | + */ | |
| 1109 | +struct drm_panthor_bo_sync_op { | |
| 1110 | + /** @handle: Handle of the buffer object to sync. */ | |
| 1111 | + __u32 handle; | |
| 1112 | + | |
| 1113 | + /** @type: Type of operation. */ | |
| 1114 | + __u32 type; | |
| 1115 | + | |
| 1116 | + /** | |
| 1117 | + * @offset: Offset into the BO at which the sync range starts. | |
| 1118 | + * | |
| 1119 | + * This will be rounded down to the nearest cache line as needed. | |
| 1120 | + */ | |
| 1121 | + __u64 offset; | |
| 1122 | + | |
| 1123 | + /** | |
| 1124 | + * @size: Size of the range to sync | |
| 1125 | + * | |
| 1126 | + * @size + @offset will be rounded up to the nearest cache line as | |
| 1127 | + * needed. | |
| 1128 | + */ | |
| 1129 | + __u64 size; | |
| 1130 | +}; | |
| 1131 | + | |
| 1132 | +/** | |
| 1133 | + * struct drm_panthor_bo_sync - BO map sync request | |
| 1134 | + */ | |
| 1135 | +struct drm_panthor_bo_sync { | |
| 1136 | + /** | |
| 1137 | + * @ops: Array of struct drm_panthor_bo_sync_op sync operations. | |
| 1138 | + */ | |
| 1139 | + struct drm_panthor_obj_array ops; | |
| 1140 | +}; | |
| 1141 | + | |
| 1142 | +/** | |
| 1143 | + * enum drm_panthor_bo_extra_flags - Set of flags returned on a BO_QUERY_INFO request | |
| 1144 | + * | |
| 1145 | + * Those are flags reflecting BO properties that are not directly coming from the flags | |
| 1146 | + * passed are creation time, or information on BOs that were imported from other drivers. | |
| 1147 | + */ | |
| 1148 | +enum drm_panthor_bo_extra_flags { | |
| 1149 | + /** | |
| 1150 | + * @DRM_PANTHOR_BO_IS_IMPORTED: BO has been imported from an external driver. | |
| 1151 | + * | |
| 1152 | + * Note that imported dma-buf handles are not flagged as imported if they | |
| 1153 | + * where exported by panthor. Only buffers that are coming from other drivers | |
| 1154 | + * (dma heaps, other GPUs, display controllers, V4L, ...). | |
| 1155 | + * | |
| 1156 | + * It's also important to note that all imported BOs are mapped cached and can't | |
| 1157 | + * be considered IO-coherent even if the GPU is. This means they require explicit | |
| 1158 | + * syncs that must go through the DRM_PANTHOR_BO_SYNC ioctl (userland cache | |
| 1159 | + * maintenance is not allowed in that case, because extra operations might be | |
| 1160 | + * needed to make changes visible to the CPU/device, like buffer migration when the | |
| 1161 | + * exporter is a GPU with its own VRAM). | |
| 1162 | + */ | |
| 1163 | + DRM_PANTHOR_BO_IS_IMPORTED = (1 << 0), | |
| 1164 | +}; | |
| 1165 | + | |
| 1166 | +/** | |
| 1167 | + * struct drm_panthor_bo_query_info - Query BO info | |
| 1168 | + */ | |
| 1169 | +struct drm_panthor_bo_query_info { | |
| 1170 | + /** @handle: Handle of the buffer object to query flags on. */ | |
| 1171 | + __u32 handle; | |
| 1172 | + | |
| 1173 | + /** | |
| 1174 | + * @extra_flags: Combination of enum drm_panthor_bo_extra_flags flags. | |
| 1175 | + */ | |
| 1176 | + __u32 extra_flags; | |
| 1177 | + | |
| 1178 | + /** | |
| 1179 | + * @create_flags: Flags passed at creation time. | |
| 1180 | + * | |
| 1181 | + * Combination of enum drm_panthor_bo_flags flags. | |
| 1182 | + * Will be zero if the buffer comes from a different driver. | |
| 1183 | + */ | |
| 1184 | + __u32 create_flags; | |
| 1185 | + | |
| 1186 | + /** @pad: Will be zero on return. */ | |
| 1187 | + __u32 pad; | |
| 1188 | +}; | |
| 1189 | + | |
| 1043 | 1190 | /** |
| 1044 | 1191 | * DRM_IOCTL_PANTHOR() - Build a Panthor IOCTL number |
| 1045 | 1192 | * @__access: Access type. Must be R, W or RW. |
| @@ -1086,6 +1233,10 @@ enum { · 40 unmodified lines | ||
| 1086 | 1233 | DRM_IOCTL_PANTHOR(WR, BO_SET_LABEL, bo_set_label), |
| 1087 | 1234 | DRM_IOCTL_PANTHOR_SET_USER_MMIO_OFFSET = |
| 1088 | 1235 | DRM_IOCTL_PANTHOR(WR, SET_USER_MMIO_OFFSET, set_user_mmio_offset), |
| 1236 | + DRM_IOCTL_PANTHOR_BO_SYNC = | |
| 1237 | + DRM_IOCTL_PANTHOR(WR, BO_SYNC, bo_sync), | |
| 1238 | + DRM_IOCTL_PANTHOR_BO_QUERY_INFO = | |
| 1239 | + DRM_IOCTL_PANTHOR(WR, BO_QUERY_INFO, bo_query_info), | |
| 1089 | 1240 | }; |
| 1090 | 1241 | |
| 1091 | 1242 | #if defined(__cplusplus) |