18c2ecf20Sopenharmony_ci/*
28c2ecf20Sopenharmony_ci * \file drm_memory.c
38c2ecf20Sopenharmony_ci * Memory management wrappers for DRM
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * \author Rickard E. (Rik) Faith <faith@valinux.com>
68c2ecf20Sopenharmony_ci * \author Gareth Hughes <gareth@valinux.com>
78c2ecf20Sopenharmony_ci */
88c2ecf20Sopenharmony_ci
98c2ecf20Sopenharmony_ci/*
108c2ecf20Sopenharmony_ci * Created: Thu Feb  4 14:00:34 1999 by faith@valinux.com
118c2ecf20Sopenharmony_ci *
128c2ecf20Sopenharmony_ci * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
138c2ecf20Sopenharmony_ci * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California.
148c2ecf20Sopenharmony_ci * All Rights Reserved.
158c2ecf20Sopenharmony_ci *
168c2ecf20Sopenharmony_ci * Permission is hereby granted, free of charge, to any person obtaining a
178c2ecf20Sopenharmony_ci * copy of this software and associated documentation files (the "Software"),
188c2ecf20Sopenharmony_ci * to deal in the Software without restriction, including without limitation
198c2ecf20Sopenharmony_ci * the rights to use, copy, modify, merge, publish, distribute, sublicense,
208c2ecf20Sopenharmony_ci * and/or sell copies of the Software, and to permit persons to whom the
218c2ecf20Sopenharmony_ci * Software is furnished to do so, subject to the following conditions:
228c2ecf20Sopenharmony_ci *
238c2ecf20Sopenharmony_ci * The above copyright notice and this permission notice (including the next
248c2ecf20Sopenharmony_ci * paragraph) shall be included in all copies or substantial portions of the
258c2ecf20Sopenharmony_ci * Software.
268c2ecf20Sopenharmony_ci *
278c2ecf20Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
288c2ecf20Sopenharmony_ci * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
298c2ecf20Sopenharmony_ci * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
308c2ecf20Sopenharmony_ci * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
318c2ecf20Sopenharmony_ci * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
328c2ecf20Sopenharmony_ci * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
338c2ecf20Sopenharmony_ci * OTHER DEALINGS IN THE SOFTWARE.
348c2ecf20Sopenharmony_ci */
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_ci#include <linux/export.h>
378c2ecf20Sopenharmony_ci#include <linux/highmem.h>
388c2ecf20Sopenharmony_ci#include <linux/pci.h>
398c2ecf20Sopenharmony_ci#include <linux/vmalloc.h>
408c2ecf20Sopenharmony_ci#include <xen/xen.h>
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_ci#include <drm/drm_agpsupport.h>
438c2ecf20Sopenharmony_ci#include <drm/drm_cache.h>
448c2ecf20Sopenharmony_ci#include <drm/drm_device.h>
458c2ecf20Sopenharmony_ci
468c2ecf20Sopenharmony_ci#include "drm_legacy.h"
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_AGP)
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_ci#ifdef HAVE_PAGE_AGP
518c2ecf20Sopenharmony_ci# include <asm/agp.h>
528c2ecf20Sopenharmony_ci#else
538c2ecf20Sopenharmony_ci# ifdef __powerpc__
548c2ecf20Sopenharmony_ci#  define PAGE_AGP	pgprot_noncached_wc(PAGE_KERNEL)
558c2ecf20Sopenharmony_ci# else
568c2ecf20Sopenharmony_ci#  define PAGE_AGP	PAGE_KERNEL
578c2ecf20Sopenharmony_ci# endif
588c2ecf20Sopenharmony_ci#endif
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_cistatic void *agp_remap(unsigned long offset, unsigned long size,
618c2ecf20Sopenharmony_ci		       struct drm_device *dev)
628c2ecf20Sopenharmony_ci{
638c2ecf20Sopenharmony_ci	unsigned long i, num_pages =
648c2ecf20Sopenharmony_ci	    PAGE_ALIGN(size) / PAGE_SIZE;
658c2ecf20Sopenharmony_ci	struct drm_agp_mem *agpmem;
668c2ecf20Sopenharmony_ci	struct page **page_map;
678c2ecf20Sopenharmony_ci	struct page **phys_page_map;
688c2ecf20Sopenharmony_ci	void *addr;
698c2ecf20Sopenharmony_ci
708c2ecf20Sopenharmony_ci	size = PAGE_ALIGN(size);
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_ci#ifdef __alpha__
738c2ecf20Sopenharmony_ci	offset -= dev->hose->mem_space->start;
748c2ecf20Sopenharmony_ci#endif
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_ci	list_for_each_entry(agpmem, &dev->agp->memory, head)
778c2ecf20Sopenharmony_ci		if (agpmem->bound <= offset
788c2ecf20Sopenharmony_ci		    && (agpmem->bound + (agpmem->pages << PAGE_SHIFT)) >=
798c2ecf20Sopenharmony_ci		    (offset + size))
808c2ecf20Sopenharmony_ci			break;
818c2ecf20Sopenharmony_ci	if (&agpmem->head == &dev->agp->memory)
828c2ecf20Sopenharmony_ci		return NULL;
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_ci	/*
858c2ecf20Sopenharmony_ci	 * OK, we're mapping AGP space on a chipset/platform on which memory accesses by
868c2ecf20Sopenharmony_ci	 * the CPU do not get remapped by the GART.  We fix this by using the kernel's
878c2ecf20Sopenharmony_ci	 * page-table instead (that's probably faster anyhow...).
888c2ecf20Sopenharmony_ci	 */
898c2ecf20Sopenharmony_ci	/* note: use vmalloc() because num_pages could be large... */
908c2ecf20Sopenharmony_ci	page_map = vmalloc(array_size(num_pages, sizeof(struct page *)));
918c2ecf20Sopenharmony_ci	if (!page_map)
928c2ecf20Sopenharmony_ci		return NULL;
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_ci	phys_page_map = (agpmem->memory->pages + (offset - agpmem->bound) / PAGE_SIZE);
958c2ecf20Sopenharmony_ci	for (i = 0; i < num_pages; ++i)
968c2ecf20Sopenharmony_ci		page_map[i] = phys_page_map[i];
978c2ecf20Sopenharmony_ci	addr = vmap(page_map, num_pages, VM_IOREMAP, PAGE_AGP);
988c2ecf20Sopenharmony_ci	vfree(page_map);
998c2ecf20Sopenharmony_ci
1008c2ecf20Sopenharmony_ci	return addr;
1018c2ecf20Sopenharmony_ci}
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_ci/** Wrapper around agp_free_memory() */
1048c2ecf20Sopenharmony_civoid drm_free_agp(struct agp_memory *handle, int pages)
1058c2ecf20Sopenharmony_ci{
1068c2ecf20Sopenharmony_ci	agp_free_memory(handle);
1078c2ecf20Sopenharmony_ci}
1088c2ecf20Sopenharmony_ci
1098c2ecf20Sopenharmony_ci/** Wrapper around agp_bind_memory() */
1108c2ecf20Sopenharmony_ciint drm_bind_agp(struct agp_memory *handle, unsigned int start)
1118c2ecf20Sopenharmony_ci{
1128c2ecf20Sopenharmony_ci	return agp_bind_memory(handle, start);
1138c2ecf20Sopenharmony_ci}
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_ci/** Wrapper around agp_unbind_memory() */
1168c2ecf20Sopenharmony_ciint drm_unbind_agp(struct agp_memory *handle)
1178c2ecf20Sopenharmony_ci{
1188c2ecf20Sopenharmony_ci	return agp_unbind_memory(handle);
1198c2ecf20Sopenharmony_ci}
1208c2ecf20Sopenharmony_ci
1218c2ecf20Sopenharmony_ci#else /*  CONFIG_AGP  */
1228c2ecf20Sopenharmony_cistatic inline void *agp_remap(unsigned long offset, unsigned long size,
1238c2ecf20Sopenharmony_ci			      struct drm_device *dev)
1248c2ecf20Sopenharmony_ci{
1258c2ecf20Sopenharmony_ci	return NULL;
1268c2ecf20Sopenharmony_ci}
1278c2ecf20Sopenharmony_ci
1288c2ecf20Sopenharmony_ci#endif /* CONFIG_AGP */
1298c2ecf20Sopenharmony_ci
1308c2ecf20Sopenharmony_civoid drm_legacy_ioremap(struct drm_local_map *map, struct drm_device *dev)
1318c2ecf20Sopenharmony_ci{
1328c2ecf20Sopenharmony_ci	if (dev->agp && dev->agp->cant_use_aperture && map->type == _DRM_AGP)
1338c2ecf20Sopenharmony_ci		map->handle = agp_remap(map->offset, map->size, dev);
1348c2ecf20Sopenharmony_ci	else
1358c2ecf20Sopenharmony_ci		map->handle = ioremap(map->offset, map->size);
1368c2ecf20Sopenharmony_ci}
1378c2ecf20Sopenharmony_ciEXPORT_SYMBOL(drm_legacy_ioremap);
1388c2ecf20Sopenharmony_ci
1398c2ecf20Sopenharmony_civoid drm_legacy_ioremap_wc(struct drm_local_map *map, struct drm_device *dev)
1408c2ecf20Sopenharmony_ci{
1418c2ecf20Sopenharmony_ci	if (dev->agp && dev->agp->cant_use_aperture && map->type == _DRM_AGP)
1428c2ecf20Sopenharmony_ci		map->handle = agp_remap(map->offset, map->size, dev);
1438c2ecf20Sopenharmony_ci	else
1448c2ecf20Sopenharmony_ci		map->handle = ioremap_wc(map->offset, map->size);
1458c2ecf20Sopenharmony_ci}
1468c2ecf20Sopenharmony_ciEXPORT_SYMBOL(drm_legacy_ioremap_wc);
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_civoid drm_legacy_ioremapfree(struct drm_local_map *map, struct drm_device *dev)
1498c2ecf20Sopenharmony_ci{
1508c2ecf20Sopenharmony_ci	if (!map->handle || !map->size)
1518c2ecf20Sopenharmony_ci		return;
1528c2ecf20Sopenharmony_ci
1538c2ecf20Sopenharmony_ci	if (dev->agp && dev->agp->cant_use_aperture && map->type == _DRM_AGP)
1548c2ecf20Sopenharmony_ci		vunmap(map->handle);
1558c2ecf20Sopenharmony_ci	else
1568c2ecf20Sopenharmony_ci		iounmap(map->handle);
1578c2ecf20Sopenharmony_ci}
1588c2ecf20Sopenharmony_ciEXPORT_SYMBOL(drm_legacy_ioremapfree);
1598c2ecf20Sopenharmony_ci
1608c2ecf20Sopenharmony_cibool drm_need_swiotlb(int dma_bits)
1618c2ecf20Sopenharmony_ci{
1628c2ecf20Sopenharmony_ci	struct resource *tmp;
1638c2ecf20Sopenharmony_ci	resource_size_t max_iomem = 0;
1648c2ecf20Sopenharmony_ci
1658c2ecf20Sopenharmony_ci	/*
1668c2ecf20Sopenharmony_ci	 * Xen paravirtual hosts require swiotlb regardless of requested dma
1678c2ecf20Sopenharmony_ci	 * transfer size.
1688c2ecf20Sopenharmony_ci	 *
1698c2ecf20Sopenharmony_ci	 * NOTE: Really, what it requires is use of the dma_alloc_coherent
1708c2ecf20Sopenharmony_ci	 *       allocator used in ttm_dma_populate() instead of
1718c2ecf20Sopenharmony_ci	 *       ttm_populate_and_map_pages(), which bounce buffers so much in
1728c2ecf20Sopenharmony_ci	 *       Xen it leads to swiotlb buffer exhaustion.
1738c2ecf20Sopenharmony_ci	 */
1748c2ecf20Sopenharmony_ci	if (xen_pv_domain())
1758c2ecf20Sopenharmony_ci		return true;
1768c2ecf20Sopenharmony_ci
1778c2ecf20Sopenharmony_ci	/*
1788c2ecf20Sopenharmony_ci	 * Enforce dma_alloc_coherent when memory encryption is active as well
1798c2ecf20Sopenharmony_ci	 * for the same reasons as for Xen paravirtual hosts.
1808c2ecf20Sopenharmony_ci	 */
1818c2ecf20Sopenharmony_ci	if (mem_encrypt_active())
1828c2ecf20Sopenharmony_ci		return true;
1838c2ecf20Sopenharmony_ci
1848c2ecf20Sopenharmony_ci	for (tmp = iomem_resource.child; tmp; tmp = tmp->sibling) {
1858c2ecf20Sopenharmony_ci		max_iomem = max(max_iomem,  tmp->end);
1868c2ecf20Sopenharmony_ci	}
1878c2ecf20Sopenharmony_ci
1888c2ecf20Sopenharmony_ci	return max_iomem > ((u64)1 << dma_bits);
1898c2ecf20Sopenharmony_ci}
1908c2ecf20Sopenharmony_ciEXPORT_SYMBOL(drm_need_swiotlb);
191