Home | History | Annotate | Line # | Download | only in drm
      1 /*	$NetBSD: bus_dma_hacks.h,v 1.27 2026/02/09 10:48:23 jmcneill Exp $	*/
      2 
      3 /*-
      4  * Copyright (c) 2013 The NetBSD Foundation, Inc.
      5  * All rights reserved.
      6  *
      7  * This code is derived from software contributed to The NetBSD Foundation
      8  * by Taylor R. Campbell.
      9  *
     10  * Redistribution and use in source and binary forms, with or without
     11  * modification, are permitted provided that the following conditions
     12  * are met:
     13  * 1. Redistributions of source code must retain the above copyright
     14  *    notice, this list of conditions and the following disclaimer.
     15  * 2. Redistributions in binary form must reproduce the above copyright
     16  *    notice, this list of conditions and the following disclaimer in the
     17  *    documentation and/or other materials provided with the distribution.
     18  *
     19  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
     20  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
     21  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     22  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
     23  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     24  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
     25  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
     26  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
     27  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
     28  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
     29  * POSSIBILITY OF SUCH DAMAGE.
     30  */
     31 
     32 #ifndef	_DRM_BUS_DMA_HACKS_H_
     33 #define	_DRM_BUS_DMA_HACKS_H_
     34 
     35 #include <sys/cdefs.h>
     36 #include <sys/bus.h>
     37 #include <sys/kmem.h>
     38 #include <sys/queue.h>
     39 
     40 #include <uvm/uvm.h>
     41 #include <uvm/uvm_extern.h>
     42 
     43 #include <linux/mm_types.h>	/* XXX struct page */
     44 
     45 #if defined(__i386__) || defined(__x86_64__)
     46 #  include <x86/bus_private.h>
     47 #  include <x86/machdep.h>
     48 #  define	PHYS_TO_BUS_MEM(dmat, paddr)	((bus_addr_t)(paddr))
     49 #  define	BUS_MEM_TO_PHYS(dmat, baddr)	((paddr_t)(baddr))
     50 #elif defined(__arm__) || defined(__aarch64__)
     51 static inline bus_addr_t
     52 PHYS_TO_BUS_MEM(bus_dma_tag_t dmat, paddr_t pa)
     53 {
     54 	unsigned i;
     55 
     56 	if (dmat->_nranges == 0)
     57 		return (bus_addr_t)pa;
     58 
     59 	for (i = 0; i < dmat->_nranges; i++) {
     60 		const struct arm32_dma_range *dr = &dmat->_ranges[i];
     61 
     62 		if (dr->dr_sysbase <= pa && pa - dr->dr_sysbase <= dr->dr_len)
     63 			return pa - dr->dr_sysbase + dr->dr_busbase;
     64 	}
     65 	panic("paddr has no bus address in dma tag %p: %"PRIxPADDR, dmat, pa);
     66 }
     67 static inline paddr_t
     68 BUS_MEM_TO_PHYS(bus_dma_tag_t dmat, bus_addr_t ba)
     69 {
     70 	unsigned i;
     71 
     72 	if (dmat->_nranges == 0)
     73 		return (paddr_t)ba;
     74 
     75 	for (i = 0; i < dmat->_nranges; i++) {
     76 		const struct arm32_dma_range *dr = &dmat->_ranges[i];
     77 
     78 		if (dr->dr_busbase <= ba && ba - dr->dr_busbase <= dr->dr_len)
     79 			return ba - dr->dr_busbase + dr->dr_sysbase;
     80 	}
     81 	panic("bus addr has no bus address in dma tag %p: %"PRIxPADDR, dmat,
     82 	    ba);
     83 }
     84 #elif defined(__sparc__) || defined(__sparc64__)
     85 #  define	PHYS_TO_BUS_MEM(dmat, paddr)	((bus_addr_t)(paddr))
     86 #  define	BUS_MEM_TO_PHYS(dmat, baddr)	((paddr_t)(baddr))
     87 #elif defined(__powerpc__)
     88 #include "opt_ppcarch.h"
     89 #elif defined(__alpha__)
     90 #  define	PHYS_TO_BUS_MEM(dmat, paddr)				      \
     91 	((bus_addr_t)(paddr) | (dmat)->_wbase)
     92 #  define	BUS_MEM_TO_PHYS(dmat, baddr)				      \
     93 	((paddr_t)((baddr) & ~(bus_addr_t)(dmat)->_wbase))
     94 #else
     95 #  error DRM GEM/TTM need new MI bus_dma APIs!  Halp!
     96 #endif
     97 
     98 static inline int
     99 bus_dmamem_pgfl(bus_dma_tag_t tag)
    100 {
    101 #if defined(__i386__) || defined(__x86_64__)
    102 	return x86_select_freelist(tag->_bounce_alloc_hi - 1);
    103 #elif defined(__powerpc__) && (defined(PPC_OEA) || defined(PPC_OEA64_BRIDGE))
    104 	return VM_FREELIST_DIRECT_MAPPED;	/* first 3GB */
    105 #else
    106 	return VM_FREELIST_DEFAULT;
    107 #endif
    108 }
    109 
    110 static inline bool
    111 bus_dmatag_bounces_paddr(bus_dma_tag_t dmat, paddr_t pa)
    112 {
    113 #if defined(__i386__) || defined(__x86_64__)
    114 	return pa < dmat->_bounce_alloc_lo || dmat->_bounce_alloc_hi <= pa;
    115 #elif defined(__arm__) || defined(__aarch64__)
    116 	unsigned i;
    117 
    118 	for (i = 0; i < dmat->_nranges; i++) {
    119 		const struct arm32_dma_range *dr = &dmat->_ranges[i];
    120 		if (dr->dr_sysbase <= pa && pa - dr->dr_sysbase <= dr->dr_len)
    121 			return false;
    122 	}
    123 	return true;
    124 #elif defined(__powerpc__)
    125 	if (dmat->_bounce_thresh_min != 0 && pa < dmat->_bounce_thresh_min) {
    126 		return true;
    127 	}
    128 	if (dmat->_bounce_thresh_max != 0 && pa >= dmat->_bounce_thresh_max) {
    129 		return true;
    130 	}
    131 	return false;
    132 #elif defined(__sparc__) || defined(__sparc64__)
    133 	return false;		/* no bounce buffers ever */
    134 #elif defined(__alpha__)
    135 	return (dmat->_wsize == 0 ? false : pa >= dmat->_wsize);
    136 #endif
    137 }
    138 
    139 #define MAX_STACK_SEGS 32	/* XXXMRG: 512 bytes on 16 byte seg platforms */
    140 
    141 /*
    142  * XXX This should really take an array of struct vm_page pointers, but
    143  * Linux drm code stores arrays of struct page pointers, and these two
    144  * types (struct page ** and struct vm_page **) are not compatible so
    145  * naive conversion would violate strict aliasing rules.
    146  */
    147 static inline int
    148 bus_dmamap_load_pages(bus_dma_tag_t tag, bus_dmamap_t map,
    149     struct page **pgs, bus_size_t size, int flags)
    150 {
    151 	km_flag_t kmflags;
    152 	bus_dma_segment_t *segs;
    153 	bus_dma_segment_t stacksegs[MAX_STACK_SEGS];
    154 	int nsegs, seg;
    155 	struct vm_page *page;
    156 	int error;
    157 
    158 	KASSERT((size & (PAGE_SIZE - 1)) == 0);
    159 
    160 	if ((size >> PAGE_SHIFT) > INT_MAX)
    161 		return ENOMEM;
    162 	nsegs = size >> PAGE_SHIFT;
    163 
    164 	KASSERT(nsegs <= (SIZE_MAX / sizeof(segs[0])));
    165 	if (nsegs > MAX_STACK_SEGS) {
    166 		switch (flags & (BUS_DMA_WAITOK|BUS_DMA_NOWAIT)) {
    167 		case BUS_DMA_WAITOK:
    168 			kmflags = KM_SLEEP;
    169 			break;
    170 		case BUS_DMA_NOWAIT:
    171 			kmflags = KM_NOSLEEP;
    172 			break;
    173 		default:
    174 			panic("invalid flags: %d", flags);
    175 		}
    176 		segs = kmem_alloc((nsegs * sizeof(segs[0])), kmflags);
    177 		if (segs == NULL)
    178 			return ENOMEM;
    179 	} else {
    180 		segs = stacksegs;
    181 	}
    182 
    183 	for (seg = 0; seg < nsegs; seg++) {
    184 		page = &pgs[seg]->p_vmp;
    185 		paddr_t paddr = VM_PAGE_TO_PHYS(page);
    186 		bus_addr_t baddr = PHYS_TO_BUS_MEM(tag, paddr);
    187 
    188 		segs[seg].ds_addr = baddr;
    189 		segs[seg].ds_len = PAGE_SIZE;
    190 	}
    191 
    192 	error = bus_dmamap_load_raw(tag, map, segs, nsegs, size, flags);
    193 	if (error)
    194 		goto fail0;
    195 
    196 	/* Success!  */
    197 	error = 0;
    198 	goto out;
    199 
    200 fail1: __unused
    201 	bus_dmamap_unload(tag, map);
    202 fail0:	KASSERT(error);
    203 out:	if (segs != stacksegs) {
    204 		KASSERT(nsegs > MAX_STACK_SEGS);
    205 		kmem_free(segs, (nsegs * sizeof(segs[0])));
    206 	}
    207 	return error;
    208 }
    209 
    210 static inline int
    211 bus_dmamem_export_pages(bus_dma_tag_t dmat, const bus_dma_segment_t *segs,
    212     int nsegs, struct page **pgs, unsigned npgs)
    213 {
    214 	int seg;
    215 	unsigned pg;
    216 
    217 	pg = 0;
    218 	for (seg = 0; seg < nsegs; seg++) {
    219 		bus_addr_t baddr = segs[seg].ds_addr;
    220 		bus_size_t len = segs[seg].ds_len;
    221 
    222 		while (len >= PAGE_SIZE) {
    223 			paddr_t paddr = BUS_MEM_TO_PHYS(dmat, baddr);
    224 
    225 			KASSERT(pg < npgs);
    226 			pgs[pg++] = container_of(PHYS_TO_VM_PAGE(paddr),
    227 			    struct page, p_vmp);
    228 
    229 			baddr += PAGE_SIZE;
    230 			len -= PAGE_SIZE;
    231 		}
    232 		KASSERT(len == 0);
    233 	}
    234 	KASSERT(pg == npgs);
    235 
    236 	return 0;
    237 }
    238 
    239 static inline int
    240 bus_dmamem_import_pages(bus_dma_tag_t dmat, bus_dma_segment_t *segs,
    241     int nsegs, int *rsegs, struct page *const *pgs, unsigned npgs)
    242 {
    243 	int seg;
    244 	unsigned i;
    245 
    246 	seg = 0;
    247 	for (i = 0; i < npgs; i++) {
    248 		paddr_t paddr = VM_PAGE_TO_PHYS(&pgs[i]->p_vmp);
    249 		bus_addr_t baddr = PHYS_TO_BUS_MEM(dmat, paddr);
    250 
    251 		if (seg > 0 && segs[seg - 1].ds_addr + PAGE_SIZE == baddr) {
    252 			segs[seg - 1].ds_len += PAGE_SIZE;
    253 		} else {
    254 			KASSERT(seg < nsegs);
    255 			segs[seg].ds_addr = baddr;
    256 			segs[seg].ds_len = PAGE_SIZE;
    257 			seg++;
    258 			KASSERT(seg <= nsegs);
    259 		}
    260 	}
    261 	KASSERT(seg <= nsegs);
    262 	*rsegs = seg;
    263 
    264 	return 0;
    265 }
    266 
    267 #endif	/* _DRM_BUS_DMA_HACKS_H_ */
    268