1/*
2 * Copyright 2003 Red Hat, Inc. All Rights Reserved.
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software"),
6 * to deal in the Software without restriction, including without limitation
7 * the rights to use, copy, modify, merge, publish, distribute, sub license,
8 * and/or sell copies of the Software, and to permit persons to whom the
9 * Software is furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice (including the
12 * next paragraph) shall be included in all copies or substantial portions
13 * of the Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
18 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
21 * DEALINGS IN THE SOFTWARE.
22 */
23
24#ifdef HAVE_CONFIG_H
25#include "config.h"
26#endif
27#include <sys/mman.h>
28
29#include "xf86.h"
30#include "xf86_OSproc.h"
31#include "xf86fbman.h"
32
33#ifdef HAVE_DRI
34#include "xf86drm.h"
35#endif
36
37#include "drm_fourcc.h"
38#include "via_driver.h"
39#ifdef HAVE_DRI
40#include "via_drm.h"
41#else
42#include "drm_fourcc.h"
43#endif
44
45/*
46 *	Isolate the wonders of X memory allocation and DRI memory allocation
47 *	and 4.3 or 4.4 differences in one abstraction.
48 *
49 *	The pool code indicates who provided the memory:
50 *	0  -  nobody
51 *	1  -  xf86 linear
52 *	2  -  DRM
53 */
54int
55viaOffScreenLinear(struct buffer_object *obj, ScrnInfoPtr pScrn,
56                   unsigned long size)
57{
58    int depth = pScrn->bitsPerPixel >> 3;
59    FBLinearPtr linear;
60
61    linear = xf86AllocateOffscreenLinear(pScrn->pScreen,
62                                        (size + depth - 1) / depth,
63                                        32, NULL, NULL, NULL);
64    if (!linear)
65        return BadAlloc;
66    obj->offset = linear->offset * depth;
67    obj->handle = (unsigned long) linear;
68    obj->domain = TTM_PL_FLAG_VRAM;
69    obj->size = size;
70    return Success;
71}
72
73struct buffer_object *
74drm_bo_alloc_surface(ScrnInfoPtr pScrn, unsigned int width, unsigned int height,
75                    int format, unsigned int alignment, int domain)
76{
77    struct buffer_object *obj = NULL;
78    int pitch;
79
80    switch (format) {
81    case DRM_FORMAT_C8:
82        pitch = width;
83        break;
84
85    case DRM_FORMAT_XRGB1555:
86    case DRM_FORMAT_RGB565:
87        pitch = width * 2;
88        break;
89
90    case DRM_FORMAT_RGB888:
91        pitch = width * 3;
92        break;
93
94    case DRM_FORMAT_XRGB2101010:
95    case DRM_FORMAT_XRGB8888:
96        pitch = width * 4;
97        break;
98    }
99
100    pitch = ALIGN_TO(pitch, alignment);
101    obj = drm_bo_alloc(pScrn, pitch * height, alignment, domain);
102    if (!obj->pitch)
103        obj->pitch = pitch;
104    return obj;
105}
106
107struct buffer_object *
108drm_bo_alloc(ScrnInfoPtr pScrn, unsigned int size, unsigned int alignment, int domain)
109{
110    struct buffer_object *obj = NULL;
111    VIAPtr pVia = VIAPTR(pScrn);
112    int ret = 0;
113
114    obj = xnfcalloc(1, sizeof(*obj));
115    if (obj) {
116        switch (domain) {
117        case TTM_PL_FLAG_TT:
118        case TTM_PL_FLAG_VRAM:
119            if (pVia->directRenderingType == DRI_NONE) {
120                if (Success != viaOffScreenLinear(obj, pScrn, size)) {
121                    ErrorF("Linear memory allocation failed\n");
122                    ret = -ENOMEM;
123                } else
124                    DEBUG(ErrorF("%lu bytes of Linear memory allocated at %lx, handle %lu\n", obj->size, obj->offset, obj->handle));
125#ifdef HAVE_DRI
126            } else if (pVia->directRenderingType == DRI_1) {
127                drm_via_mem_t drm;
128
129                size = ALIGN_TO(size, alignment);
130                drm.context = DRIGetContext(pScrn->pScreen);
131                drm.size = size;
132                drm.type = (domain == TTM_PL_FLAG_TT ? VIA_MEM_AGP : VIA_MEM_VIDEO);
133                ret = drmCommandWriteRead(pVia->drmmode.fd, DRM_VIA_ALLOCMEM,
134                                            &drm, sizeof(drm_via_mem_t));
135                if (!ret && (size == drm.size)) {
136                    if (domain == TTM_PL_FLAG_VRAM)
137                        drm.offset -= pVia->FBFreeStart;
138                    obj->offset = ALIGN_TO(drm.offset, alignment);
139                    obj->handle = drm.index;
140                    obj->domain = domain;
141                    obj->size = drm.size;
142                    DEBUG(ErrorF("%lu bytes of DRI memory allocated at %lx, handle %lu\n",
143                                obj->size, obj->offset, obj->handle));
144                }
145            } else if (pVia->directRenderingType == DRI_2) {
146                struct drm_via_gem_object args;
147
148                /* Some day this will be moved to libdrm. */
149                args.domains = domain;
150                args.alignment = alignment;
151                args.pitch = 0;
152                args.size = size;
153                ret = drmCommandWriteRead(pVia->drmmode.fd, DRM_VIA_GEM_CREATE,
154                                        &args, sizeof(struct drm_via_gem_object));
155                if (!ret) {
156                    /* Okay the X server expects to know the offset because
157                     * of non-KMS. Once we have KMS working the offset
158                     * will not be valid. */
159                    obj->map_offset = args.map_handle;
160                    obj->offset = args.offset;
161                    obj->handle = args.handle;
162                    obj->pitch = args.pitch;
163                    obj->size = args.size;
164                    obj->domain = domain;
165                    DEBUG(ErrorF("%lu bytes of DRI2 memory allocated at %lx, handle %lu\n",
166                                obj->size, obj->offset, obj->handle));
167                }
168#endif
169            }
170            break;
171
172        case TTM_PL_FLAG_SYSTEM:
173        default:
174            ret = -ENXIO;
175            break;
176        }
177
178        if (ret) {
179            DEBUG(ErrorF("DRM memory allocation failed %d\n", ret));
180            free(obj);
181            obj = NULL;
182        };
183    }
184    return obj;
185}
186
187void*
188drm_bo_map(ScrnInfoPtr pScrn, struct buffer_object *obj)
189{
190    VIAPtr pVia = VIAPTR(pScrn);
191
192    if (pVia->directRenderingType == DRI_2) {
193        obj->ptr = mmap(0, obj->size, PROT_READ | PROT_WRITE,
194                        MAP_SHARED, pVia->drmmode.fd, obj->map_offset);
195        if (obj->ptr == MAP_FAILED) {
196            DEBUG(ErrorF("mmap failed with error %d\n", -errno));
197            obj->ptr = NULL;
198        }
199    } else {
200        switch (obj->domain) {
201#ifdef HAVE_DRI
202        case TTM_PL_FLAG_TT:
203            obj->ptr = (pVia->agpMappedAddr + obj->offset);
204            break;
205#endif
206        case TTM_PL_FLAG_VRAM:
207            obj->ptr = (pVia->FBBase + obj->offset);
208            break;
209        default:
210            obj->ptr = NULL;
211            break;
212        }
213    }
214    return obj->ptr;
215}
216
217void
218drm_bo_unmap(ScrnInfoPtr pScrn, struct buffer_object *obj)
219{
220    VIAPtr pVia = VIAPTR(pScrn);
221
222    if (pVia->directRenderingType == DRI_2) {
223        munmap(obj->ptr, obj->size);
224    }
225
226    obj->ptr = NULL;
227}
228
229void
230drm_bo_free(ScrnInfoPtr pScrn, struct buffer_object *obj)
231{
232    VIAPtr pVia = VIAPTR(pScrn);
233
234    if (obj) {
235        DEBUG(ErrorF("Freed %lu (pool %d)\n", obj->offset, obj->domain));
236        switch (obj->domain) {
237        case TTM_PL_FLAG_VRAM:
238        case TTM_PL_FLAG_TT:
239            if (pVia->directRenderingType == DRI_NONE) {
240                FBLinearPtr linear = (FBLinearPtr) obj->handle;
241
242                xf86FreeOffscreenLinear(linear);
243#ifdef HAVE_DRI
244            } else if (pVia->directRenderingType == DRI_1) {
245                drm_via_mem_t drm;
246
247                drm.index = obj->handle;
248                if (drmCommandWrite(pVia->drmmode.fd, DRM_VIA_FREEMEM,
249                                    &drm, sizeof(drm_via_mem_t)) < 0)
250                    ErrorF("DRM failed to free for handle %lu.\n", obj->handle);
251            } else  if (pVia->directRenderingType == DRI_2) {
252                struct drm_gem_close close;
253
254                close.handle = obj->handle;
255                if (drmIoctl(pVia->drmmode.fd, DRM_IOCTL_GEM_CLOSE, &close) < 0)
256                    ErrorF("DRM failed to free for handle %lu.\n", obj->handle);
257#endif
258            }
259            break;
260
261        default:
262            break;
263        }
264        free(obj);
265    }
266}
267
268Bool
269drm_bo_manager_init(ScrnInfoPtr pScrn)
270{
271    VIAPtr pVia = VIAPTR(pScrn);
272    Bool ret = TRUE;
273
274    if (pVia->directRenderingType == DRI_2)
275        return ret;
276    ret = umsCreate(pScrn);
277#ifdef HAVE_DRI
278    if (pVia->directRenderingType == DRI_1)
279        ret = VIADRIKernelInit(pScrn);
280#endif
281    return ret;
282}
283