lx_memory.c revision c744f008
1/* Copyright (c) 2008 Advanced Micro Devices, Inc.
2 *
3 * Permission is hereby granted, free of charge, to any person obtaining a copy
4 * of this software and associated documentation files (the "Software"), to
5 * deal in the Software without restriction, including without limitation the
6 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
7 * sell copies of the Software, and to permit persons to whom the Software is
8 * furnished to do so, subject to the following conditions:
9 *
10 * The above copyright notice and this permission notice shall be included in
11 * all copies or substantial portions of the Software.
12 *
13 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
18 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
19 * IN THE SOFTWARE.
20 *
21 * Neither the name of the Advanced Micro Devices, Inc. nor the names of its
22 * contributors may be used to endorse or promote products derived from this
23 * software without specific prior written permission.
24 */
25#if HAVE_CONFIG_H
26#include "config.h"
27#endif
28
29#include "xorg-server.h"
30
31#include "xf86.h"
32#include "geode.h"
33#include "cim/cim_regs.h"
34
35#define ALIGN(x,y)   (((x) + (y) - 1) / (y) * (y))
36#define LX_CB_PITCH   544
37
38/* Geode offscreen memory allocation functions.  This is
39   overengineered for the simple hardware that we have, but
40   there are multiple functions that may want to independently
41   allocate and free memory (crtc->shadow_alloc and Xv).  This
42   provides a semi-robust mechanism for doing that.
43*/
44
45/* Return the number of free bytes */
46
47unsigned int
48GeodeOffscreenFreeSize(GeodeRec * pGeode)
49{
50    GeodeMemPtr ptr = pGeode->offscreenList;
51
52    if (ptr == NULL)
53        return pGeode->offscreenSize;
54
55    for (; ptr->next; ptr = ptr->next);
56    return (pGeode->offscreenStart + pGeode->offscreenSize)
57        - (ptr->offset + ptr->size);
58}
59
60void
61GeodeFreeOffscreen(GeodeRec * pGeode, GeodeMemPtr ptr)
62{
63    /* There is a clear memory leak here, but
64     * but it is unlikely that the first block of
65     * "allocated" memory is going to be released
66     * individually.
67     */
68
69    if (ptr->prev == NULL)
70        pGeode->offscreenList = ptr->next;
71    else
72        ptr->prev->next = ptr->next;
73
74    if (ptr->next)
75        ptr->next->prev = ptr->prev;
76
77    free(ptr);
78}
79
80/* Allocate the "rest" of the offscreen memory - this is for
81   situations where we have very little video memory, and we
82   want to take as much of it as we can for EXA
83*/
84
85static GeodeMemPtr
86GeodeAllocRemainder(GeodeRec * pGeode)
87{
88    GeodeMemPtr nptr, ptr = pGeode->offscreenList;
89
90    if (!pGeode->offscreenList) {
91        pGeode->offscreenList = calloc(1, sizeof(*nptr));
92        pGeode->offscreenList->offset = pGeode->offscreenStart;
93        pGeode->offscreenList->size = pGeode->offscreenSize;
94        pGeode->offscreenList->next = NULL;
95        pGeode->offscreenList->prev = NULL;
96
97        return pGeode->offscreenList;
98    }
99
100    /* Go to the end of the list of allocated stuff */
101    for (; ptr->next; ptr = ptr->next);
102
103    nptr = calloc(1, sizeof(*nptr));
104    nptr->offset = ptr->offset + ptr->size;
105    nptr->size = pGeode->offscreenSize -
106        (nptr->offset - pGeode->offscreenStart);
107
108    nptr->next = ptr->next;
109    nptr->prev = ptr;
110    ptr->next = nptr;
111
112    return nptr;
113}
114
115/* Allocate 'size' bytes of offscreen memory.
116*/
117
118GeodeMemPtr
119GeodeAllocOffscreen(GeodeRec * pGeode, int size, int align)
120{
121    GeodeMemPtr ptr = pGeode->offscreenList;
122    GeodeMemPtr nptr;
123
124    unsigned int offset;
125
126    if (!pGeode->offscreenList) {
127
128        if (size > pGeode->offscreenSize)
129            return NULL;
130
131        offset = ALIGN(pGeode->offscreenStart, align);
132
133        pGeode->offscreenList = calloc(1, sizeof(*nptr));
134        pGeode->offscreenList->offset = offset;
135        pGeode->offscreenList->size = size;
136        pGeode->offscreenList->next = NULL;
137
138        return pGeode->offscreenList;
139    }
140
141    while (ptr) {
142        unsigned int gap;
143
144        if (ptr->next == NULL)
145            gap = pGeode->offscreenSize + pGeode->offscreenStart;
146
147        else
148            gap = ptr->next->offset;
149
150        gap = gap - (ptr->offset + ptr->size);
151        gap = ALIGN(gap, align);
152
153        if (size < gap) {
154            offset = ptr->offset + ptr->size;
155            offset = ALIGN(ptr->offset + ptr->size, align);
156
157            nptr = calloc(1, sizeof(*nptr));
158            nptr->offset = offset;
159            nptr->size = size;
160            nptr->next = ptr->next;
161            nptr->prev = ptr;
162            ptr->next = nptr;
163
164            return nptr;
165        }
166
167        ptr = ptr->next;
168    }
169
170    return NULL;
171}
172
173/* Carve out the space for the visible screen, and carve out
174   the usual suspects that need offscreen memory
175*/
176
177#define MAX(a,b) ((a) > (b) ? (a) : (b))
178
179void
180LXInitOffscreen(ScrnInfoPtr pScrni)
181{
182    GeodeRec *pGeode = GEODEPTR(pScrni);
183    unsigned int fbavail;
184    GeodeMemPtr ptr;
185
186    /* The scratch buffer is always used */
187    fbavail = pGeode->FBAvail - GP3_SCRATCH_BUFFER_SIZE;
188
189    pGeode->displaySize = MAX(pScrni->virtualX, pScrni->virtualY)
190        * pGeode->Pitch;
191
192    pGeode->offscreenStart = pGeode->displaySize;
193    pGeode->offscreenSize = fbavail - pGeode->displaySize;
194
195    /* Allocate the usual memory suspects */
196    if (pGeode->tryCompression) {
197        int size = pScrni->virtualY * LX_CB_PITCH;
198
199        /* The compression buffer needs to be 16 byte aligned */
200        ptr = GeodeAllocOffscreen(pGeode, size, 16);
201
202        if (ptr != NULL) {
203            pGeode->CBData.compression_offset = ptr->offset;
204            pGeode->CBData.size = LX_CB_PITCH;
205            pGeode->CBData.pitch = LX_CB_PITCH;
206
207            pGeode->Compression = TRUE;
208        }
209        else {
210            xf86DrvMsg(pScrni->scrnIndex, X_ERROR,
211                       "Not enough memory for compression\n");
212            pGeode->Compression = FALSE;
213        }
214    }
215
216    if (pGeode->tryHWCursor) {
217        ptr = GeodeAllocOffscreen(pGeode,
218                                  LX_CURSOR_HW_WIDTH * 4 * LX_CURSOR_HW_HEIGHT,
219                                  4);
220
221        if (ptr != NULL) {
222            pGeode->CursorStartOffset = ptr->offset;
223            pGeode->HWCursor = TRUE;
224        }
225        else {
226            xf86DrvMsg(pScrni->scrnIndex, X_ERROR,
227                       "Not enough memory for the hardware cursor\n");
228            pGeode->HWCursor = FALSE;
229        }
230    }
231
232    if (!pGeode->NoAccel && pGeode->pExa) {
233        int size;
234
235        /* Try to get the scratch buffer for blending */
236        pGeode->exaBfrOffset = 0;
237
238        if (pGeode->exaBfrSz > 0) {
239            ptr = GeodeAllocOffscreen(pGeode, pGeode->exaBfrSz, 4);
240            if (ptr != NULL)
241                pGeode->exaBfrOffset = ptr->offset;
242        }
243
244        pGeode->pExa->offScreenBase = 0;
245        pGeode->pExa->memorySize = 0;
246
247        /* This might cause complaints - in order to avoid using
248           xorg.conf as much as possible, we make assumptions about
249           what a "default" memory map would look like.  After
250           discussion, we agreed that the default driver should assume
251           the user will want to use rotation and video overlays, and
252           EXA will get whatever is leftover.
253         */
254
255        /* Get the amount of offscreen memory still left */
256        size = GeodeOffscreenFreeSize(pGeode);
257
258        /* Align the size to a K boundary */
259        size &= ~1023;
260
261        /* Allocate the EXA offscreen space */
262        ptr = GeodeAllocOffscreen(pGeode, size, 4);
263
264        if (ptr == NULL) {
265            /* If we couldn't allocate what we wanted,
266             * then allocate what's left */
267
268            ptr = GeodeAllocRemainder(pGeode);
269        }
270
271        if (ptr != NULL) {
272            pGeode->pExa->offScreenBase = ptr->offset;
273            pGeode->pExa->memorySize = ptr->offset + ptr->size;
274        }
275    }
276
277    /* Show the memory map for diagnostic purposes */
278
279    xf86DrvMsg(pScrni->scrnIndex, X_INFO, "LX video memory:\n");
280    xf86DrvMsg(pScrni->scrnIndex, X_INFO, " Display: 0x%x bytes\n",
281               pGeode->displaySize);
282
283    if (pGeode->Compression)
284        xf86DrvMsg(pScrni->scrnIndex, X_INFO, " Compression: 0x%x bytes\n",
285                   pScrni->virtualY * LX_CB_PITCH);
286
287    if (pGeode->HWCursor)
288        xf86DrvMsg(pScrni->scrnIndex, X_INFO, " Cursor: 0x%x bytes\n",
289                   LX_CURSOR_HW_WIDTH * 4 * LX_CURSOR_HW_HEIGHT);
290
291    if (pGeode->exaBfrSz)
292        xf86DrvMsg(pScrni->scrnIndex, X_INFO, " ExaBfrSz: 0x%x bytes\n",
293                   pGeode->exaBfrSz);
294
295    if (pGeode->pExa && pGeode->pExa->offScreenBase)
296        xf86DrvMsg(pScrni->scrnIndex, X_INFO, " EXA: 0x%x bytes\n",
297                   (unsigned int) (pGeode->pExa->memorySize -
298                                   pGeode->pExa->offScreenBase));
299
300    xf86DrvMsg(pScrni->scrnIndex, X_INFO, " FREE: 0x%x bytes\n",
301               GeodeOffscreenFreeSize(pGeode));
302}
303
304/* Called as we go down, so blitz everybody */
305
306void
307GeodeCloseOffscreen(ScrnInfoPtr pScrni)
308{
309    GeodeRec *pGeode = GEODEPTR(pScrni);
310    GeodeMemPtr ptr = pGeode->offscreenList;
311    GeodeMemPtr nptr;
312
313    while (ptr) {
314        nptr = ptr->next;
315        free(ptr);
316        ptr = nptr;
317    }
318
319    pGeode->offscreenList = NULL;
320}
321