Edit

IABSD.fr/xenocara/driver/xf86-video-amdgpu/src/amdgpu_bo_helper.c

Branch :

  • Show log

    Commit

  • Author : jsg
    Date : 2026-03-12 04:31:28
    Hash : bedc11b3
    Message : update to xf86-video-amdgpu 25.0.0 tested by matthieu and myself on multiple machines

  • driver/xf86-video-amdgpu/src/amdgpu_bo_helper.c
  • /*
     * Copyright 2012  Advanced Micro Devices, Inc.
     *
     * Permission is hereby granted, free of charge, to any person obtaining a
     * copy of this software and associated documentation files (the "Software"),
     * to deal in the Software without restriction, including without limitation
     * the rights to use, copy, modify, merge, publish, distribute, sublicense,
     * and/or sell copies of the Software, and to permit persons to whom the
     * Software is furnished to do so, subject to the following conditions:
     *
     * The above copyright notice and this permission notice shall be included in
     * all copies or substantial portions of the Software.
     *
     * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
     * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
     * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
     * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
     * OTHER DEALINGS IN THE SOFTWARE.
     */
    
    #ifdef HAVE_CONFIG_H
    #include "config.h"
    #endif
    #include <xorg-server.h>
    #include <sys/mman.h>
    #include <gbm.h>
    #include "amdgpu_drv.h"
    #include "amdgpu_bo_helper.h"
    #include "amdgpu_glamor.h"
    #include "amdgpu_pixmap.h"
    
    static uint32_t
    amdgpu_get_gbm_format(int depth, int bitsPerPixel)
    {
    	switch (depth) {
    #ifdef GBM_FORMAT_R8
    	case 8:
    		return GBM_FORMAT_R8;
    #endif
    	case 15:
    		return GBM_FORMAT_ARGB1555;
    	case 16:
    		return GBM_FORMAT_RGB565;
    	case 32:
    		return GBM_FORMAT_ARGB8888;
    	case 30:
    		return GBM_FORMAT_XRGB2101010;
    	case 24:
    		if (bitsPerPixel == 32)
    			return GBM_FORMAT_XRGB8888;
    		/* fall through */
    	default:
    		ErrorF("%s: Unsupported depth/bpp %d/%d\n", __func__,
    		       depth, bitsPerPixel);
    		return ~0U;
    	}
    }
    
    /* Calculate appropriate pitch for a pixmap and allocate a BO that can hold it.
     */
    struct amdgpu_buffer *amdgpu_alloc_pixmap_bo(ScrnInfoPtr pScrn, int width,
    					      int height, int depth, int usage_hint,
    					      int bitsPerPixel, int *new_pitch)
    {
    	AMDGPUInfoPtr info = AMDGPUPTR(pScrn);
    	struct amdgpu_buffer *pixmap_buffer;
    
    	if (!(usage_hint & AMDGPU_CREATE_PIXMAP_GTT) && info->gbm) {
    		uint32_t bo_use = GBM_BO_USE_RENDERING;
    		uint32_t gbm_format = amdgpu_get_gbm_format(depth, bitsPerPixel);
    
    		if (gbm_format == ~0U)
    			return NULL;
    
    		pixmap_buffer = (struct amdgpu_buffer *)calloc(1, sizeof(struct amdgpu_buffer));
    		if (!pixmap_buffer) {
    			return NULL;
    		}
    		pixmap_buffer->ref_count = 1;
    
    		if (usage_hint & AMDGPU_CREATE_PIXMAP_SCANOUT)
    			bo_use |= GBM_BO_USE_SCANOUT;
    
    #ifdef HAVE_GBM_BO_USE_FRONT_RENDERING
    		if (usage_hint & AMDGPU_CREATE_PIXMAP_FRONT)
    			bo_use |= GBM_BO_USE_FRONT_RENDERING;
    #endif
    
    #ifdef HAVE_GBM_BO_USE_LINEAR
    		if (usage_hint == CREATE_PIXMAP_USAGE_SHARED ||
    		    (usage_hint & AMDGPU_CREATE_PIXMAP_LINEAR)) {
    			bo_use |= GBM_BO_USE_LINEAR;
    		}
    #endif
    
    		pixmap_buffer->bo.gbm = gbm_bo_create(info->gbm, width, height,
    						      gbm_format,
    						      bo_use);
    		if (!pixmap_buffer->bo.gbm) {
    			free(pixmap_buffer);
    			return NULL;
    		}
    
    		pixmap_buffer->flags |= AMDGPU_BO_FLAGS_GBM;
    
    		if (new_pitch)
    			*new_pitch = gbm_bo_get_stride(pixmap_buffer->bo.gbm);
    	} else {
    		AMDGPUEntPtr pAMDGPUEnt = AMDGPUEntPriv(pScrn);
    		unsigned cpp = (bitsPerPixel + 7) / 8;
    		unsigned pitch = cpp *
    			AMDGPU_ALIGN(width, drmmode_get_pitch_align(pScrn, cpp));
    		uint32_t domain = (usage_hint & AMDGPU_CREATE_PIXMAP_GTT) ?
    			AMDGPU_GEM_DOMAIN_GTT : AMDGPU_GEM_DOMAIN_VRAM;
    
    		pixmap_buffer = amdgpu_bo_open(pAMDGPUEnt->pDev, pitch * height,
    					       4096, domain);
    
    		if (new_pitch)
    			*new_pitch = pitch;
    	}
    
    	return pixmap_buffer;
    }
    
    /* Clear the pixmap contents to black */
    void
    amdgpu_pixmap_clear(PixmapPtr pixmap)
    {
    	ScreenPtr screen = pixmap->drawable.pScreen;
    	AMDGPUInfoPtr info = AMDGPUPTR(xf86ScreenToScrn(screen));
    	GCPtr gc = GetScratchGC(pixmap->drawable.depth, screen);
    	xRectangle rect;
    
    	ValidateGC(&pixmap->drawable, gc);
    	rect.x = 0;
    	rect.y = 0;
    	rect.width = pixmap->drawable.width;
    	rect.height = pixmap->drawable.height;
    	info->force_accel = TRUE;
    	gc->ops->PolyFillRect(&pixmap->drawable, gc, 1, &rect);
    	info->force_accel = FALSE;
    	FreeScratchGC(gc);
    }
    
    Bool amdgpu_bo_get_handle(struct amdgpu_buffer *bo, uint32_t *handle)
    {
    	if (bo->flags & AMDGPU_BO_FLAGS_GBM) {
    		*handle = gbm_bo_get_handle(bo->bo.gbm).u32;
    		return TRUE;
    	}
    
    	return amdgpu_bo_export(bo->bo.amdgpu, amdgpu_bo_handle_type_kms,
    				handle) == 0;
    }
    
    #ifdef USE_GLAMOR
    
    static void amdgpu_pixmap_do_get_tiling_info(PixmapPtr pixmap)
    {
    	struct amdgpu_pixmap *priv = amdgpu_get_pixmap_private(pixmap);
    	ScreenPtr screen = pixmap->drawable.pScreen;
    	ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
    	AMDGPUEntPtr pAMDGPUEnt = AMDGPUEntPriv(scrn);
    	struct drm_amdgpu_gem_metadata gem_metadata;
    
    	gem_metadata.handle = priv->handle;
    	gem_metadata.op = AMDGPU_GEM_METADATA_OP_GET_METADATA;
    
    	if (drmCommandWriteRead(pAMDGPUEnt->fd, DRM_AMDGPU_GEM_METADATA,
    				&gem_metadata, sizeof(gem_metadata)) == 0)
    		priv->tiling_info = gem_metadata.data.tiling_info;
    }
    
    #endif
    
    uint64_t amdgpu_pixmap_get_tiling_info(PixmapPtr pixmap)
    {
    	struct amdgpu_pixmap *priv = amdgpu_get_pixmap_private(pixmap);
    	uint32_t handle;
    
    	if (!priv || !priv->handle_valid) {
    		amdgpu_pixmap_get_handle(pixmap, &handle);
    		priv = amdgpu_get_pixmap_private(pixmap);
    	}
    
    	return priv->tiling_info;
    }
    
    Bool amdgpu_pixmap_get_handle(PixmapPtr pixmap, uint32_t *handle)
    {
    	ScreenPtr screen = pixmap->drawable.pScreen;
    	ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
    	AMDGPUInfoPtr info = AMDGPUPTR(scrn);
    	struct amdgpu_pixmap *priv;
    
    	if (info->shadow_fb)
    		return FALSE;
    
    	priv = amdgpu_get_pixmap_private(pixmap);
    	if (!priv) {
    		priv = calloc(1, sizeof(*priv));
    		amdgpu_set_pixmap_private(pixmap, priv);
    	}
    
    	if (priv->handle_valid)
    		goto success;
    	
    #ifdef USE_GLAMOR
    	if (info->use_glamor) {
    		AMDGPUEntPtr pAMDGPUEnt = AMDGPUEntPriv(scrn);
    		CARD16 stride;
    		CARD32 size;
    		int fd, r;
    
    		fd = glamor_fd_from_pixmap(screen, pixmap, &stride, &size);
    		if (fd < 0)
    			return FALSE;
    
    		r = drmPrimeFDToHandle(pAMDGPUEnt->fd, fd, &priv->handle);
    		close(fd);
    		if (r)
    			return FALSE;
    
    		amdgpu_pixmap_do_get_tiling_info(pixmap);
    		goto success;
    	}
    #endif
    
    	if (!priv->bo || !amdgpu_bo_get_handle(priv->bo, &priv->handle))
    		return FALSE;
    
     success:
    	priv->handle_valid = TRUE;
    	*handle = priv->handle;
    	return TRUE;
    }
    
    int amdgpu_bo_map(ScrnInfoPtr pScrn, struct amdgpu_buffer *bo)
    {
    	int ret = 0;
    
    	if (bo->flags & AMDGPU_BO_FLAGS_GBM) {
    		AMDGPUEntPtr pAMDGPUEnt = AMDGPUEntPriv(pScrn);
    		uint32_t handle, stride, height;
    		union drm_amdgpu_gem_mmap args;
    		int fd = pAMDGPUEnt->fd;
    		void *ptr;
    
    		handle = gbm_bo_get_handle(bo->bo.gbm).u32;
    		stride = gbm_bo_get_stride(bo->bo.gbm);
    		height = gbm_bo_get_height(bo->bo.gbm);
    
    		memset(&args, 0, sizeof(union drm_amdgpu_gem_mmap));
    		args.in.handle = handle;
    
    		ret = drmCommandWriteRead(fd, DRM_AMDGPU_GEM_MMAP,
    					&args, sizeof(args));
    		if (ret) {
    			ErrorF("Failed to get the mmap offset\n");
    			return ret;
    		}
    
    		ptr = mmap(NULL, stride * height,
    			PROT_READ | PROT_WRITE, MAP_SHARED,
    			fd, args.out.addr_ptr);
    
    		if (!ptr) {
    			ErrorF("Failed to mmap the bo\n");
    			return -1;
    		}
    
    		bo->cpu_ptr = ptr;
    	} else
    		ret = amdgpu_bo_cpu_map(bo->bo.amdgpu, &bo->cpu_ptr);
    
    	return ret;
    }
    
    void amdgpu_bo_unmap(struct amdgpu_buffer *bo)
    {
    	if (!bo->cpu_ptr)
    		return;
    
    	if (bo->flags & AMDGPU_BO_FLAGS_GBM) {
    		uint32_t stride, height;
    		stride = gbm_bo_get_stride(bo->bo.gbm);
    		height = gbm_bo_get_height(bo->bo.gbm);
    		munmap(bo->cpu_ptr, stride * height);
    	} else
    		amdgpu_bo_cpu_unmap(bo->bo.amdgpu);
    }
    
    struct amdgpu_buffer *amdgpu_bo_open(amdgpu_device_handle pDev,
    				       uint32_t alloc_size,
    				       uint32_t phys_alignment,
    				       uint32_t domains)
    {
    	struct amdgpu_bo_alloc_request alloc_request;
    	struct amdgpu_buffer *bo = NULL;
    
    	memset(&alloc_request, 0, sizeof(struct amdgpu_bo_alloc_request));
    
    	bo = (struct amdgpu_buffer *)calloc(1, sizeof(struct amdgpu_buffer));
    	if (!bo)
    		return NULL;
    
    	alloc_request.alloc_size = alloc_size;
    	alloc_request.phys_alignment = phys_alignment;
    	alloc_request.preferred_heap = domains;
    
    	if (amdgpu_bo_alloc(pDev, &alloc_request, &bo->bo.amdgpu)) {
    		free(bo);
    		return NULL;
    	}
    
    	bo->ref_count = 1;
    
    	return bo;
    }
    
    void amdgpu_bo_ref(struct amdgpu_buffer *buffer)
    {
    	buffer->ref_count++;
    }
    
    void amdgpu_bo_unref(struct amdgpu_buffer **buffer)
    {
    	struct amdgpu_buffer *buf = *buffer;
    
    	buf->ref_count--;
    	if (buf->ref_count) {
    		return;
    	}
    
    	amdgpu_bo_unmap(buf);
    
    	if (buf->flags & AMDGPU_BO_FLAGS_GBM) {
    		gbm_bo_destroy(buf->bo.gbm);
    	} else {
    		amdgpu_bo_free(buf->bo.amdgpu);
    	}
    	free(buf);
    	*buffer = NULL;
    }
    
    int amdgpu_query_bo_size(amdgpu_bo_handle buf_handle, uint32_t *size)
    {
    	struct amdgpu_bo_info buffer_info;
    	int ret;
    
    	memset(&buffer_info, 0, sizeof(struct amdgpu_bo_info));
    	ret = amdgpu_bo_query_info(buf_handle, &buffer_info);
    	if (ret)
    		*size = 0;
    	else
    		*size = (uint32_t)(buffer_info.alloc_size);
    
    	return ret;
    }
    
    int amdgpu_query_heap_size(amdgpu_device_handle pDev,
    			    uint32_t heap,
    			    uint64_t *heap_size,
    			    uint64_t *max_allocation)
    {
    	struct amdgpu_heap_info heap_info;
    	int ret;
    
    	memset(&heap_info, 0, sizeof(struct amdgpu_heap_info));
    	ret = amdgpu_query_heap_info(pDev, heap, 0, &heap_info);
    	if (ret) {
    		*heap_size = 0;
    		*max_allocation = 0;
    	} else {
    		*heap_size = heap_info.heap_size;
    		*max_allocation = heap_info.max_allocation;
    	}
    
    	return ret;
    }
    
    struct amdgpu_buffer *amdgpu_gem_bo_open_prime(amdgpu_device_handle pDev,
    						 int fd_handle,
    						 uint32_t size)
    {
    	struct amdgpu_buffer *bo = NULL;
    	struct amdgpu_bo_import_result buffer = {0};
    
    	bo = (struct amdgpu_buffer *)calloc(1, sizeof(struct amdgpu_buffer));
    	if (!bo)
    		return NULL;
    
    	if (amdgpu_bo_import(pDev, amdgpu_bo_handle_type_dma_buf_fd,
    			     (uint32_t)fd_handle, &buffer)) {
    		free(bo);
    		return FALSE;
    	}
    	bo->bo.amdgpu = buffer.buf_handle;
    	bo->ref_count = 1;
    
    	return bo;
    }
    
    
    Bool amdgpu_set_shared_pixmap_backing(PixmapPtr ppix, void *fd_handle)
    {
    	ScrnInfoPtr pScrn = xf86ScreenToScrn(ppix->drawable.pScreen);
    	AMDGPUInfoPtr info = AMDGPUPTR(pScrn);
    	AMDGPUEntPtr pAMDGPUEnt = AMDGPUEntPriv(pScrn);
    	struct amdgpu_buffer *pixmap_buffer = NULL;
    	int ihandle = (int)(long)fd_handle;
    	uint32_t size = ppix->devKind * ppix->drawable.height;
    	Bool ret;
    
    	if (ihandle == -1)
    		return amdgpu_set_pixmap_bo(ppix, NULL);
    
    	if (info->gbm) {
    		struct amdgpu_buffer *bo;
    		struct gbm_import_fd_data data;
    		uint32_t bo_use = GBM_BO_USE_RENDERING;
    
    		data.format = amdgpu_get_gbm_format(ppix->drawable.depth,
    						    ppix->drawable.bitsPerPixel);
    		if (data.format == ~0U)
    			return FALSE;
    
    		bo = calloc(1, sizeof(struct amdgpu_buffer));
    		if (!bo)
    			return FALSE;
    		bo->ref_count = 1;
    
    		data.fd = ihandle;
    		data.width = ppix->drawable.width;
    		data.height = ppix->drawable.height;
    		data.stride = ppix->devKind;
    
    		if (ppix->drawable.bitsPerPixel == pScrn->bitsPerPixel)
    			bo_use |= GBM_BO_USE_SCANOUT;
    
    		bo->bo.gbm = gbm_bo_import(info->gbm, GBM_BO_IMPORT_FD, &data,
    					   bo_use);
    		if (!bo->bo.gbm) {
    			free(bo);
    			return FALSE;
    		}
    
    		bo->flags |= AMDGPU_BO_FLAGS_GBM;
    
    #ifdef USE_GLAMOR
    		if (info->use_glamor &&
    		    !amdgpu_glamor_create_textured_pixmap(ppix, bo)) {
    			amdgpu_bo_unref(&bo);
    			return FALSE;
    		}
    #endif
    
    		ret = amdgpu_set_pixmap_bo(ppix, bo);
    		/* amdgpu_set_pixmap_bo increments ref_count if it succeeds */
    		amdgpu_bo_unref(&bo);
    		return ret;
    	}
    
    	pixmap_buffer = amdgpu_gem_bo_open_prime(pAMDGPUEnt->pDev, ihandle, size);
    	if (!pixmap_buffer) {
    		return FALSE;
    	}
    
    	close(ihandle);
    
    	ret = amdgpu_set_pixmap_bo(ppix, pixmap_buffer);
    
    	/* we have a reference from the alloc and one from set pixmap bo,
    	   drop one */
    	amdgpu_bo_unref(&pixmap_buffer);
    
    	return ret;
    }