]> CyberLeo.Net >> Repos - FreeBSD/releng/8.1.git/blob - sys/dev/drm/drm_scatter.c
Copy stable/8 to releng/8.1 in preparation for 8.1-RC1.
[FreeBSD/releng/8.1.git] / sys / dev / drm / drm_scatter.c
1 /*-
2  * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California.
3  * All Rights Reserved.
4  *
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:
11  *
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
14  * Software.
15  *
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.
23  *
24  * Authors:
25  *   Gareth Hughes <gareth@valinux.com>
26  *   Eric Anholt <anholt@FreeBSD.org>
27  *
28  */
29
30 #include <sys/cdefs.h>
31 __FBSDID("$FreeBSD$");
32
33 /** @file drm_scatter.c
34  * Allocation of memory for scatter-gather mappings by the graphics chip.
35  *
36  * The memory allocated here is then made into an aperture in the card
37  * by drm_ati_pcigart_init().
38  */
39
40 #include "dev/drm/drmP.h"
41
42 static void drm_sg_alloc_cb(void *arg, bus_dma_segment_t *segs,
43                             int nsegs, int error);
44
45 int
46 drm_sg_alloc(struct drm_device *dev, struct drm_scatter_gather *request)
47 {
48         struct drm_sg_mem *entry;
49         struct drm_dma_handle *dmah;
50         int ret;
51
52         if (dev->sg)
53                 return EINVAL;
54
55         entry = malloc(sizeof(*entry), DRM_MEM_SGLISTS, M_WAITOK | M_ZERO);
56         entry->pages = round_page(request->size) / PAGE_SIZE;
57         DRM_DEBUG("sg size=%ld pages=%d\n", request->size, entry->pages);
58
59         entry->busaddr = malloc(entry->pages * sizeof(*entry->busaddr),
60             DRM_MEM_PAGES, M_WAITOK | M_ZERO);
61         dmah = malloc(sizeof(struct drm_dma_handle), DRM_MEM_DMA,
62             M_WAITOK | M_ZERO);
63         entry->dmah = dmah;
64
65         ret = bus_dma_tag_create(NULL, PAGE_SIZE, 0, /* tag, align, boundary */
66             BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, /* lowaddr, highaddr */
67             NULL, NULL, /* filtfunc, filtfuncargs */
68             request->size, entry->pages, /* maxsize, nsegs */
69             PAGE_SIZE, 0, /* maxsegsize, flags */
70             NULL, NULL, /* lockfunc, lockfuncargs */
71             &dmah->tag);
72         if (ret != 0) {
73                 drm_sg_cleanup(entry);
74                 return ENOMEM;
75         }
76
77         ret = bus_dmamem_alloc(dmah->tag, &dmah->vaddr,
78             BUS_DMA_WAITOK | BUS_DMA_ZERO | BUS_DMA_NOCACHE, &dmah->map);
79         if (ret != 0) {
80                 drm_sg_cleanup(entry);
81                 return ENOMEM;
82         }
83
84         entry->handle = (unsigned long)dmah->vaddr;
85         entry->virtual = dmah->vaddr;
86
87         ret = bus_dmamap_load(dmah->tag, dmah->map, dmah->vaddr,
88             request->size, drm_sg_alloc_cb, entry, BUS_DMA_NOWAIT);
89         if (ret != 0) {
90                 drm_sg_cleanup(entry);
91                 return ENOMEM;
92         }
93
94         DRM_LOCK();
95         if (dev->sg) {
96                 DRM_UNLOCK();
97                 drm_sg_cleanup(entry);
98                 return EINVAL;
99         }
100         dev->sg = entry;
101         DRM_UNLOCK();
102
103         DRM_DEBUG("handle=%08lx, kva=%p, contents=%08lx\n", entry->handle,
104             entry->virtual, *(unsigned long *)entry->virtual);
105
106         request->handle = entry->handle;
107
108         return 0;
109 }
110
111 static void
112 drm_sg_alloc_cb(void *arg, bus_dma_segment_t *segs, int nsegs, int error)
113 {
114         struct drm_sg_mem *entry = arg;
115         int i;
116
117         if (error != 0)
118             return;
119
120         for(i = 0 ; i < nsegs ; i++) {
121                 entry->busaddr[i] = segs[i].ds_addr;
122                 DRM_DEBUG("segment %d @ 0x%016lx\n", i,
123                     (unsigned long)segs[i].ds_addr);
124         }
125 }
126
127 int
128 drm_sg_alloc_ioctl(struct drm_device *dev, void *data,
129                    struct drm_file *file_priv)
130 {
131         struct drm_scatter_gather *request = data;
132
133         DRM_DEBUG("\n");
134
135         return drm_sg_alloc(dev, request);
136 }
137
138 void
139 drm_sg_cleanup(struct drm_sg_mem *entry)
140 {
141         struct drm_dma_handle *dmah = entry->dmah;
142
143         if (dmah->map != NULL)
144                 bus_dmamap_unload(dmah->tag, dmah->map);
145         if (dmah->vaddr != NULL)
146                 bus_dmamem_free(dmah->tag, dmah->vaddr, dmah->map);
147         if (dmah->tag != NULL)
148                 bus_dma_tag_destroy(dmah->tag);
149         free(dmah, DRM_MEM_DMA);
150         free(entry->busaddr, DRM_MEM_PAGES);
151         free(entry, DRM_MEM_SGLISTS);
152 }
153
154 int
155 drm_sg_free(struct drm_device *dev, void *data, struct drm_file *file_priv)
156 {
157         struct drm_scatter_gather *request = data;
158         struct drm_sg_mem *entry;
159
160         DRM_LOCK();
161         entry = dev->sg;
162         dev->sg = NULL;
163         DRM_UNLOCK();
164
165         if (!entry || entry->handle != request->handle)
166                 return EINVAL;
167
168         DRM_DEBUG("sg free virtual = 0x%lx\n", entry->handle);
169
170         drm_sg_cleanup(entry);
171
172         return 0;
173 }