2 * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California.
5 * Permission is hereby granted, free of charge, to any person obtaining a
6 * copy of this software and associated documentation files (the "Software"),
7 * to deal in the Software without restriction, including without limitation
8 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
9 * and/or sell copies of the Software, and to permit persons to whom the
10 * Software is furnished to do so, subject to the following conditions:
12 * The above copyright notice and this permission notice (including the next
13 * paragraph) shall be included in all copies or substantial portions of the
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
19 * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
20 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
21 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
22 * DEALINGS IN THE SOFTWARE.
25 * Gareth Hughes <gareth@valinux.com>
29 /** @file ati_pcigart.c
30 * Implementation of ATI's PCIGART, which provides an aperture in card virtual
31 * address space with addresses remapped to system memory.
36 #define ATI_PCIGART_PAGE_SIZE 4096 /* PCI GART page size */
37 #define ATI_PCIGART_PAGE_MASK (~(ATI_PCIGART_PAGE_SIZE-1))
39 #define ATI_PCIE_WRITE 0x4
40 #define ATI_PCIE_READ 0x8
42 #if defined(__FreeBSD__)
44 drm_ati_alloc_pcigart_table_cb(void *arg
, bus_dma_segment_t
*segs
,
47 struct drm_dma_handle
*dmah
= arg
;
52 DRM_KASSERT(nsegs
== 1,
53 ("drm_ati_alloc_pcigart_table_cb: bad dma segment count"));
55 dmah
->busaddr
= segs
[0].ds_addr
;
60 drm_ati_alloc_pcigart_table(struct drm_device
*dev
,
61 struct drm_ati_pcigart_info
*gart_info
)
63 struct drm_dma_handle
*dmah
;
65 #if defined(__NetBSD__)
69 dmah
= malloc(sizeof(struct drm_dma_handle
), DRM_MEM_DMA
,
74 #if defined(__FreeBSD__)
76 ret
= bus_dma_tag_create(NULL
, PAGE_SIZE
, 0, /* tag, align, boundary */
77 gart_info
->table_mask
, BUS_SPACE_MAXADDR
, /* lowaddr, highaddr */
78 NULL
, NULL
, /* filtfunc, filtfuncargs */
79 gart_info
->table_size
, 1, /* maxsize, nsegs */
80 gart_info
->table_size
, /* maxsegsize */
81 BUS_DMA_ALLOCNOW
, NULL
, NULL
, /* flags, lockfunc, lockfuncargs */
84 free(dmah
, DRM_MEM_DMA
);
88 flags
= BUS_DMA_NOWAIT
| BUS_DMA_ZERO
;
89 if (gart_info
->gart_reg_if
== DRM_ATI_GART_IGP
)
90 flags
|= BUS_DMA_NOCACHE
;
92 ret
= bus_dmamem_alloc(dmah
->tag
, &dmah
->vaddr
, flags
, &dmah
->map
);
94 bus_dma_tag_destroy(dmah
->tag
);
95 free(dmah
, DRM_MEM_DMA
);
100 ret
= bus_dmamap_load(dmah
->tag
, dmah
->map
, dmah
->vaddr
,
101 gart_info
->table_size
, drm_ati_alloc_pcigart_table_cb
, dmah
, 0);
103 bus_dmamem_free(dmah
->tag
, dmah
->vaddr
, dmah
->map
);
104 bus_dma_tag_destroy(dmah
->tag
);
105 free(dmah
, DRM_MEM_DMA
);
109 #elif defined(__NetBSD__)
110 dmah
->tag
= dev
->pa
.pa_dmat
;
112 flags
= BUS_DMA_NOWAIT
;
113 if (gart_info
->gart_reg_if
== DRM_ATI_GART_IGP
)
114 flags
|= BUS_DMA_NOCACHE
;
116 ret
= bus_dmamem_alloc(dmah
->tag
, gart_info
->table_size
, PAGE_SIZE
,
117 0, dmah
->segs
, 1, &nsegs
, flags
);
119 printf("drm: unable to allocate %zu bytes of DMA, error %d\n",
120 (size_t)gart_info
->table_size
, ret
);
122 free(dmah
, DRM_MEM_DMA
);
126 printf("drm: bad segment count\n");
127 bus_dmamem_free(dmah
->tag
, dmah
->segs
, 1);
129 free(dmah
, DRM_MEM_DMA
);
133 ret
= bus_dmamem_map(dmah
->tag
, dmah
->segs
, nsegs
,
134 gart_info
->table_size
, &dmah
->vaddr
,
135 flags
| BUS_DMA_COHERENT
);
137 printf("drm: Unable to map DMA, error %d\n", ret
);
138 bus_dmamem_free(dmah
->tag
, dmah
->segs
, 1);
140 free(dmah
, DRM_MEM_DMA
);
144 ret
= bus_dmamap_create(dmah
->tag
, gart_info
->table_size
, 1,
145 gart_info
->table_size
, 0,
146 BUS_DMA_NOWAIT
| BUS_DMA_ALLOCNOW
, &dmah
->map
);
148 printf("drm: Unable to create DMA map, error %d\n", ret
);
149 bus_dmamem_unmap(dmah
->tag
, dmah
->vaddr
, gart_info
->table_size
);
150 bus_dmamem_free(dmah
->tag
, dmah
->segs
, 1);
152 free(dmah
, DRM_MEM_DMA
);
156 ret
= bus_dmamap_load(dmah
->tag
, dmah
->map
, dmah
->vaddr
,
157 gart_info
->table_size
, NULL
, BUS_DMA_NOWAIT
);
159 printf("drm: Unable to load DMA map, error %d\n", ret
);
160 bus_dmamap_destroy(dmah
->tag
, dmah
->map
);
161 bus_dmamem_unmap(dmah
->tag
, dmah
->vaddr
, gart_info
->table_size
);
162 bus_dmamem_free(dmah
->tag
, dmah
->segs
, 1);
164 free(dmah
, DRM_MEM_DMA
);
167 dmah
->busaddr
= dmah
->map
->dm_segs
[0].ds_addr
;
168 dmah
->size
= gart_info
->table_size
;
172 * Mirror here FreeBSD doing BUS_DMA_ZERO.
173 * But I see this same memset() is done in drm_ati_pcigart_init(),
174 * so maybe this is not needed.
176 memset(dmah
->vaddr
, 0, gart_info
->table_size
);
180 dev
->sg
->dmah
= dmah
;
186 drm_ati_free_pcigart_table(struct drm_device
*dev
,
187 struct drm_ati_pcigart_info
*gart_info
)
189 struct drm_dma_handle
*dmah
= dev
->sg
->dmah
;
191 #if defined(__FreeBSD__)
192 bus_dmamem_free(dmah
->tag
, dmah
->vaddr
, dmah
->map
);
193 bus_dma_tag_destroy(dmah
->tag
);
194 #elif defined(__NetBSD__)
195 bus_dmamap_unload(dmah
->tag
, dmah
->map
);
196 bus_dmamap_destroy(dmah
->tag
, dmah
->map
);
197 bus_dmamem_unmap(dmah
->tag
, dmah
->vaddr
, dmah
->size
);
198 bus_dmamem_free(dmah
->tag
, dmah
->segs
, 1);
201 free(dmah
, DRM_MEM_DMA
);
202 dev
->sg
->dmah
= NULL
;
206 drm_ati_pcigart_cleanup(struct drm_device
*dev
,
207 struct drm_ati_pcigart_info
*gart_info
)
209 /* we need to support large memory configurations */
210 if (dev
->sg
== NULL
) {
211 DRM_ERROR("no scatter/gather memory!\n");
215 if (gart_info
->bus_addr
) {
216 if (gart_info
->gart_table_location
== DRM_ATI_GART_MAIN
) {
217 gart_info
->bus_addr
= 0;
219 drm_ati_free_pcigart_table(dev
, gart_info
);
227 drm_ati_pcigart_init(struct drm_device
*dev
,
228 struct drm_ati_pcigart_info
*gart_info
)
230 void *address
= NULL
;
232 u32
*pci_gart
, page_base
;
233 dma_addr_t bus_address
= 0;
234 dma_addr_t entry_addr
;
238 /* we need to support large memory configurations */
239 if (dev
->sg
== NULL
) {
240 DRM_ERROR("no scatter/gather memory!\n");
244 if (gart_info
->gart_table_location
== DRM_ATI_GART_MAIN
) {
245 DRM_DEBUG("PCI: no table in VRAM: using normal RAM\n");
247 ret
= drm_ati_alloc_pcigart_table(dev
, gart_info
);
249 DRM_ERROR("cannot allocate PCI GART page!\n");
253 address
= (void *)dev
->sg
->dmah
->vaddr
;
254 bus_address
= dev
->sg
->dmah
->busaddr
;
256 address
= gart_info
->addr
;
257 bus_address
= gart_info
->bus_addr
;
258 DRM_DEBUG("PCI: Gart Table: VRAM %08X mapped at %08lX\n",
259 (unsigned int)bus_address
, (unsigned long)address
);
262 pci_gart
= (u32
*) address
;
264 max_pages
= (gart_info
->table_size
/ sizeof(u32
));
265 pages
= (dev
->sg
->pages
<= max_pages
)
266 ? dev
->sg
->pages
: max_pages
;
268 memset(pci_gart
, 0, max_pages
* sizeof(u32
));
270 DRM_KASSERT(PAGE_SIZE
>= ATI_PCIGART_PAGE_SIZE
, ("page size too small"));
272 for (i
= 0; i
< pages
; i
++) {
273 entry_addr
= dev
->sg
->busaddr
[i
];
274 for (j
= 0; j
< (PAGE_SIZE
/ ATI_PCIGART_PAGE_SIZE
); j
++) {
275 page_base
= (u32
) entry_addr
& ATI_PCIGART_PAGE_MASK
;
276 switch(gart_info
->gart_reg_if
) {
277 case DRM_ATI_GART_IGP
:
279 (upper_32_bits(entry_addr
) & 0xff) << 4;
282 case DRM_ATI_GART_PCIE
:
285 (upper_32_bits(entry_addr
) & 0xff) << 24;
286 page_base
|= ATI_PCIE_READ
| ATI_PCIE_WRITE
;
289 case DRM_ATI_GART_PCI
:
292 *pci_gart
= cpu_to_le32(page_base
);
294 entry_addr
+= ATI_PCIGART_PAGE_SIZE
;
301 gart_info
->addr
= address
;
302 gart_info
->bus_addr
= bus_address
;