1 // SPDX-License-Identifier: MIT 2 /* Copyright © 2024 Intel Corporation */ 3 4 #include <linux/fb.h> 5 6 #include <drm/drm_gem.h> 7 #include <drm/intel/display_parent_interface.h> 8 9 #include "intel_fb.h" 10 #include "xe_bo.h" 11 #include "xe_display_bo.h" 12 #include "xe_pxp.h" 13 #include "xe_ttm_stolen_mgr.h" 14 #include "xe_wa.h" 15 16 #include <generated/xe_device_wa_oob.h> 17 18 static bool xe_display_bo_is_protected(struct drm_gem_object *obj) 19 { 20 return xe_bo_is_protected(gem_to_xe_bo(obj)); 21 } 22 23 static int xe_display_bo_read_from_page(struct drm_gem_object *obj, u64 offset, void *dst, int size) 24 { 25 struct xe_bo *bo = gem_to_xe_bo(obj); 26 27 return xe_bo_read(bo, offset, dst, size); 28 } 29 30 static int xe_display_bo_framebuffer_init(struct drm_gem_object *obj, 31 struct drm_mode_fb_cmd2 *mode_cmd) 32 { 33 struct xe_bo *bo = gem_to_xe_bo(obj); 34 struct xe_device *xe = to_xe_device(bo->ttm.base.dev); 35 int ret; 36 37 /* 38 * Some modifiers require physical alignment of 64KiB VRAM pages; 39 * require that the BO in those cases is created correctly. 40 */ 41 if (XE_IOCTL_DBG(xe, intel_fb_needs_64k_phys(mode_cmd->modifier[0]) && 42 !(bo->flags & XE_BO_FLAG_NEEDS_64K))) 43 return -EINVAL; 44 45 xe_bo_get(bo); 46 47 ret = ttm_bo_reserve(&bo->ttm, true, false, NULL); 48 if (ret) 49 goto err; 50 51 if (!(bo->flags & XE_BO_FLAG_FORCE_WC)) { 52 /* 53 * XE_BO_FLAG_FORCE_WC should ideally be set at creation, or is 54 * automatically set when creating FB. We cannot change caching 55 * mode when the bo is VM_BINDed, so we can only set 56 * coherency with display when unbound. 57 */ 58 if (XE_IOCTL_DBG(xe, xe_bo_is_vm_bound(bo))) { 59 ttm_bo_unreserve(&bo->ttm); 60 ret = -EINVAL; 61 goto err; 62 } 63 bo->flags |= XE_BO_FLAG_FORCE_WC; 64 } 65 ttm_bo_unreserve(&bo->ttm); 66 return 0; 67 68 err: 69 xe_bo_put(bo); 70 return ret; 71 } 72 73 static void xe_display_bo_framebuffer_fini(struct drm_gem_object *obj) 74 { 75 struct xe_bo *bo = gem_to_xe_bo(obj); 76 77 if (bo->flags & XE_BO_FLAG_PINNED) { 78 /* Unpin our kernel fb first */ 79 xe_bo_lock(bo, false); 80 xe_bo_unpin(bo); 81 xe_bo_unlock(bo); 82 } 83 xe_bo_put(bo); 84 } 85 86 static struct drm_gem_object * 87 xe_display_bo_framebuffer_lookup(struct drm_device *drm, 88 struct drm_file *filp, 89 const struct drm_mode_fb_cmd2 *mode_cmd) 90 { 91 struct xe_device *xe = to_xe_device(drm); 92 struct xe_bo *bo; 93 struct drm_gem_object *gem = drm_gem_object_lookup(filp, mode_cmd->handles[0]); 94 95 if (!gem) 96 return ERR_PTR(-ENOENT); 97 98 bo = gem_to_xe_bo(gem); 99 /* Require vram placement or dma-buf import */ 100 if (IS_DGFX(xe) && 101 !xe_bo_can_migrate(bo, XE_PL_VRAM0) && 102 bo->ttm.type != ttm_bo_type_sg) { 103 drm_gem_object_put(gem); 104 return ERR_PTR(-EREMOTE); 105 } 106 107 return gem; 108 } 109 110 #if IS_ENABLED(CONFIG_DRM_FBDEV_EMULATION) 111 /* 112 * FIXME: There shouldn't be any reason to have XE_PAGE_SIZE stride 113 * alignment. The same 64 as i915 uses should be fine, and we shouldn't need to 114 * have driver specific values. However, dropping the stride alignment to 64 115 * leads to underflowing the bo pin count in the atomic cleanup work. 116 */ 117 static u32 xe_display_bo_fbdev_pitch_align(u32 stride) 118 { 119 return ALIGN(stride, XE_PAGE_SIZE); 120 } 121 122 bool xe_display_bo_fbdev_prefer_stolen(struct xe_device *xe, unsigned int size) 123 { 124 struct ttm_resource_manager *stolen; 125 126 stolen = ttm_manager_type(&xe->ttm, XE_PL_STOLEN); 127 if (!stolen) 128 return false; 129 130 if (IS_DGFX(xe)) 131 return false; 132 133 if (XE_DEVICE_WA(xe, 22019338487_display)) 134 return false; 135 136 /* 137 * If the FB is too big, just don't use it since fbdev is not very 138 * important and we should probably use that space with FBC or other 139 * features. 140 */ 141 return stolen->size >= (size * 2) >> PAGE_SHIFT; 142 } 143 144 static struct drm_gem_object *xe_display_bo_fbdev_create(struct drm_device *drm, int size) 145 { 146 struct xe_device *xe = to_xe_device(drm); 147 struct xe_bo *obj; 148 149 obj = ERR_PTR(-ENODEV); 150 151 if (xe_display_bo_fbdev_prefer_stolen(xe, size)) { 152 obj = xe_bo_create_pin_map_novm(xe, xe_device_get_root_tile(xe), 153 size, 154 ttm_bo_type_kernel, 155 XE_BO_FLAG_FORCE_WC | 156 XE_BO_FLAG_STOLEN | 157 XE_BO_FLAG_GGTT, 158 false); 159 if (!IS_ERR(obj)) 160 drm_info(&xe->drm, "Allocated fbdev into stolen\n"); 161 else 162 drm_info(&xe->drm, "Allocated fbdev into stolen failed: %li\n", PTR_ERR(obj)); 163 } else { 164 drm_info(&xe->drm, "Allocating fbdev: Stolen memory not preferred.\n"); 165 } 166 167 if (IS_ERR(obj)) { 168 obj = xe_bo_create_pin_map_novm(xe, xe_device_get_root_tile(xe), size, 169 ttm_bo_type_kernel, 170 XE_BO_FLAG_FORCE_WC | 171 XE_BO_FLAG_VRAM_IF_DGFX(xe_device_get_root_tile(xe)) | 172 XE_BO_FLAG_GGTT, 173 false); 174 } 175 176 if (IS_ERR(obj)) { 177 drm_err(&xe->drm, "failed to allocate framebuffer (%pe)\n", obj); 178 return ERR_PTR(-ENOMEM); 179 } 180 181 return &obj->ttm.base; 182 } 183 184 static void xe_display_bo_fbdev_destroy(struct drm_gem_object *obj) 185 { 186 xe_bo_unpin_map_no_vm(gem_to_xe_bo(obj)); 187 } 188 189 static int xe_display_bo_fbdev_fill_info(struct drm_gem_object *_obj, struct fb_info *info, 190 struct i915_vma *vma) 191 { 192 struct xe_bo *obj = gem_to_xe_bo(_obj); 193 struct pci_dev *pdev = to_pci_dev(_obj->dev->dev); 194 195 if (!(obj->flags & XE_BO_FLAG_SYSTEM)) { 196 if (obj->flags & XE_BO_FLAG_STOLEN) 197 info->fix.smem_start = xe_ttm_stolen_io_offset(obj, 0); 198 else 199 info->fix.smem_start = 200 pci_resource_start(pdev, 2) + 201 xe_bo_addr(obj, 0, XE_PAGE_SIZE); 202 203 info->fix.smem_len = obj->ttm.base.size; 204 } else { 205 /* XXX: Pure fiction, as the BO may not be physically accessible.. */ 206 info->fix.smem_start = 0; 207 info->fix.smem_len = obj->ttm.base.size; 208 } 209 XE_WARN_ON(iosys_map_is_null(&obj->vmap)); 210 211 info->screen_base = obj->vmap.vaddr_iomem; 212 info->screen_size = obj->ttm.base.size; 213 214 return 0; 215 } 216 #endif 217 218 const struct intel_display_bo_interface xe_display_bo_interface = { 219 .is_protected = xe_display_bo_is_protected, 220 .key_check = xe_pxp_obj_key_check, 221 .fb_mmap = drm_gem_prime_mmap, 222 .read_from_page = xe_display_bo_read_from_page, 223 .framebuffer_init = xe_display_bo_framebuffer_init, 224 .framebuffer_fini = xe_display_bo_framebuffer_fini, 225 .framebuffer_lookup = xe_display_bo_framebuffer_lookup, 226 #if IS_ENABLED(CONFIG_DRM_FBDEV_EMULATION) 227 .fbdev_create = xe_display_bo_fbdev_create, 228 .fbdev_destroy = xe_display_bo_fbdev_destroy, 229 .fbdev_fill_info = xe_display_bo_fbdev_fill_info, 230 .fbdev_pitch_align = xe_display_bo_fbdev_pitch_align, 231 #endif 232 }; 233