Merge branches 'hammer2' and 'master' of ssh://crater.dragonflybsd.org/repository...
[dragonfly.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 /** @file drm_scatter.c
31  * Allocation of memory for scatter-gather mappings by the graphics chip.
32  *
33  * The memory allocated here is then made into an aperture in the card
34  * by drm_ati_pcigart_init().
35  */
36
37 #include "dev/drm/drmP.h"
38
39 static void drm_sg_alloc_cb(void *arg, bus_dma_segment_t *segs,
40                             int nsegs, int error);
41
42 int
43 drm_sg_alloc(struct drm_device *dev, struct drm_scatter_gather *request)
44 {
45         struct drm_sg_mem *entry;
46         struct drm_dma_handle *dmah;
47         unsigned long pages;
48         int ret;
49
50         if (dev->sg)
51                 return EINVAL;
52
53         entry = malloc(sizeof(*entry), DRM_MEM_SGLISTS, M_WAITOK | M_ZERO);
54         if (!entry)
55                 return ENOMEM;
56
57         pages = round_page(request->size) / PAGE_SIZE;
58         DRM_DEBUG("sg size=%ld pages=%ld\n", request->size, pages);
59
60         entry->pages = pages;
61
62         entry->busaddr = malloc(pages * sizeof(*entry->busaddr), DRM_MEM_PAGES,
63             M_WAITOK | M_ZERO);
64         if (!entry->busaddr) {
65                 free(entry, DRM_MEM_SGLISTS);
66                 return ENOMEM;
67         }
68
69         dmah = malloc(sizeof(struct drm_dma_handle), DRM_MEM_DMA,
70             M_ZERO | M_NOWAIT);
71         if (dmah == NULL) {
72                 free(entry->busaddr, DRM_MEM_PAGES);
73                 free(entry, DRM_MEM_SGLISTS);
74                 return ENOMEM;
75         }
76
77         ret = bus_dma_tag_create(NULL, PAGE_SIZE, 0, /* tag, align, boundary */
78             BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, /* lowaddr, highaddr */
79             NULL, NULL, /* filtfunc, filtfuncargs */
80             request->size, pages, /* maxsize, nsegs */
81             PAGE_SIZE, 0, /* maxsegsize, flags */
82             &dmah->tag);
83         if (ret != 0) {
84                 free(dmah, DRM_MEM_DMA);
85                 free(entry->busaddr, DRM_MEM_PAGES);
86                 free(entry, DRM_MEM_SGLISTS);
87                 return ENOMEM;
88         }
89
90         /* XXX BUS_DMA_NOCACHE */
91         ret = bus_dmamem_alloc(dmah->tag, &dmah->vaddr,
92             BUS_DMA_WAITOK | BUS_DMA_ZERO , &dmah->map);
93         if (ret != 0) {
94                 bus_dma_tag_destroy(dmah->tag);
95                 free(dmah, DRM_MEM_DMA);
96                 free(entry->busaddr, DRM_MEM_PAGES);
97                 free(entry, DRM_MEM_SGLISTS);
98                 return ENOMEM;
99         }
100
101         ret = bus_dmamap_load(dmah->tag, dmah->map, dmah->vaddr,
102             request->size, drm_sg_alloc_cb, entry, BUS_DMA_NOWAIT);
103         if (ret != 0) {
104                 bus_dmamem_free(dmah->tag, dmah->vaddr, dmah->map);
105                 bus_dma_tag_destroy(dmah->tag);
106                 free(dmah, DRM_MEM_DMA);
107                 free(entry->busaddr, DRM_MEM_PAGES);
108                 free(entry, DRM_MEM_SGLISTS);
109                 return ENOMEM;
110         }
111
112         entry->dmah = dmah;
113         entry->handle = (unsigned long)dmah->vaddr;
114         
115         DRM_DEBUG("sg alloc handle  = %08lx\n", entry->handle);
116
117         entry->virtual = (void *)entry->handle;
118         request->handle = entry->handle;
119
120         DRM_LOCK();
121         if (dev->sg) {
122                 DRM_UNLOCK();
123                 drm_sg_cleanup(entry);
124                 return EINVAL;
125         }
126         dev->sg = entry;
127         DRM_UNLOCK();
128
129         return 0;
130 }
131
132 static void
133 drm_sg_alloc_cb(void *arg, bus_dma_segment_t *segs, int nsegs, int error)
134 {
135         struct drm_sg_mem *entry = arg;
136         int i;
137
138         if (error != 0)
139             return;
140
141         for(i = 0 ; i < nsegs ; i++) {
142                 entry->busaddr[i] = segs[i].ds_addr;
143         }
144 }
145
146 int
147 drm_sg_alloc_ioctl(struct drm_device *dev, void *data,
148                    struct drm_file *file_priv)
149 {
150         struct drm_scatter_gather *request = data;
151
152         DRM_DEBUG("\n");
153
154         return drm_sg_alloc(dev, request);
155 }
156
157 void
158 drm_sg_cleanup(struct drm_sg_mem *entry)
159 {
160         struct drm_dma_handle *dmah = entry->dmah;
161
162         bus_dmamap_unload(dmah->tag, dmah->map);
163         bus_dmamem_free(dmah->tag, dmah->vaddr, dmah->map);
164         bus_dma_tag_destroy(dmah->tag);
165         free(dmah, DRM_MEM_DMA);
166         free(entry->busaddr, DRM_MEM_PAGES);
167         free(entry, DRM_MEM_SGLISTS);
168 }
169
170 int
171 drm_sg_free(struct drm_device *dev, void *data, struct drm_file *file_priv)
172 {
173         struct drm_scatter_gather *request = data;
174         struct drm_sg_mem *entry;
175
176         DRM_LOCK();
177         entry = dev->sg;
178         dev->sg = NULL;
179         DRM_UNLOCK();
180
181         if (!entry || entry->handle != request->handle)
182                 return EINVAL;
183
184         DRM_DEBUG("sg free virtual = 0x%lx\n", entry->handle);
185
186         drm_sg_cleanup(entry);
187
188         return 0;
189 }