lx_memory.c revision 7f419768
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 "xf86.h"
30#include "geode.h"
31#include "cim/cim_regs.h"
32
33#define ALIGN(x,y)   (((x) + (y) - 1) / (y) * (y))
34#define LX_CB_PITCH   544
35
36/* Geode offscreen memory allocation functions.  This is
37   overengineered for the simple hardware that we have, but
38   there are multiple functions that may want to independently
39   allocate and free memory (crtc->shadow_alloc and Xv).  This
40   provides a semi-robust mechanism for doing that.
41*/
42
43/* Return the number of free bytes */
44
45unsigned int
46GeodeOffscreenFreeSize(GeodeRec * pGeode)
47{
48    GeodeMemPtr ptr = pGeode->offscreenList;
49
50    if (ptr == NULL)
51        return pGeode->offscreenSize;
52
53    for (; ptr->next; ptr = ptr->next);
54    return (pGeode->offscreenStart + pGeode->offscreenSize)
55        - (ptr->offset + ptr->size);
56}
57
58void
59GeodeFreeOffscreen(GeodeRec * pGeode, GeodeMemPtr ptr)
60{
61    /* There is a clear memory leak here, but
62     * but it is unlikely that the first block of
63     * "allocated" memory is going to be released
64     * individually.
65     */
66
67    if (ptr->prev == NULL)
68        pGeode->offscreenList = ptr->next;
69    else
70        ptr->prev->next = ptr->next;
71
72    if (ptr->next)
73        ptr->next->prev = ptr->prev;
74
75    free(ptr);
76}
77
78/* Allocate the "rest" of the offscreen memory - this is for
79   situations where we have very little video memory, and we
80   want to take as much of it as we can for EXA
81*/
82
83static GeodeMemPtr
84GeodeAllocRemainder(GeodeRec * pGeode)
85{
86    GeodeMemPtr nptr, ptr = pGeode->offscreenList;
87
88    if (!pGeode->offscreenList) {
89        pGeode->offscreenList = calloc(1, sizeof(*nptr));
90        pGeode->offscreenList->offset = pGeode->offscreenStart;
91        pGeode->offscreenList->size = pGeode->offscreenSize;
92        pGeode->offscreenList->next = NULL;
93        pGeode->offscreenList->prev = NULL;
94
95        return pGeode->offscreenList;
96    }
97
98    /* Go to the end of the list of allocated stuff */
99    for (; ptr->next; ptr = ptr->next);
100
101    nptr = calloc(1, sizeof(*nptr));
102    nptr->offset = ptr->offset + ptr->size;
103    nptr->size = pGeode->offscreenSize -
104        (nptr->offset - pGeode->offscreenStart);
105
106    nptr->next = ptr->next;
107    nptr->prev = ptr;
108    ptr->next = nptr;
109
110    return nptr;
111}
112
113/* Allocate 'size' bytes of offscreen memory.
114*/
115
116GeodeMemPtr
117GeodeAllocOffscreen(GeodeRec * pGeode, int size, int align)
118{
119    GeodeMemPtr ptr = pGeode->offscreenList;
120    GeodeMemPtr nptr;
121
122    unsigned int offset;
123
124    if (!pGeode->offscreenList) {
125
126        if (size > pGeode->offscreenSize)
127            return NULL;
128
129        offset = ALIGN(pGeode->offscreenStart, align);
130
131        pGeode->offscreenList = calloc(1, sizeof(*nptr));
132        pGeode->offscreenList->offset = offset;
133        pGeode->offscreenList->size = size;
134        pGeode->offscreenList->next = NULL;
135
136        return pGeode->offscreenList;
137    }
138
139    while (ptr) {
140        unsigned int gap;
141
142        if (ptr->next == NULL)
143            gap = pGeode->offscreenSize + pGeode->offscreenStart;
144
145        else
146            gap = ptr->next->offset;
147
148        gap = gap - (ptr->offset + ptr->size);
149        gap = ALIGN(gap, align);
150
151        if (size < gap) {
152            offset = ptr->offset + ptr->size;
153            offset = ALIGN(ptr->offset + ptr->size, align);
154
155            nptr = calloc(1, sizeof(*nptr));
156            nptr->offset = offset;
157            nptr->size = size;
158            nptr->next = ptr->next;
159            nptr->prev = ptr;
160            ptr->next = nptr;
161
162            return nptr;
163        }
164
165        ptr = ptr->next;
166    }
167
168    return NULL;
169}
170
171/* Carve out the space for the visible screen, and carve out
172   the usual suspects that need offscreen memory
173*/
174
175#define MAX(a,b) ((a) > (b) ? (a) : (b))
176
177void
178LXInitOffscreen(ScrnInfoPtr pScrni)
179{
180    GeodeRec *pGeode = GEODEPTR(pScrni);
181    unsigned int fbavail;
182    GeodeMemPtr ptr;
183
184    /* The scratch buffer is always used */
185    fbavail = pGeode->FBAvail - GP3_SCRATCH_BUFFER_SIZE;
186
187    pGeode->displaySize = MAX(pScrni->virtualX, pScrni->virtualY)
188        * pGeode->Pitch;
189
190    pGeode->offscreenStart = pGeode->displaySize;
191    pGeode->offscreenSize = fbavail - pGeode->displaySize;
192
193    /* Allocate the usual memory suspects */
194    if (pGeode->tryCompression) {
195        int size = pScrni->virtualY * LX_CB_PITCH;
196
197        /* The compression buffer needs to be 16 byte aligned */
198        ptr = GeodeAllocOffscreen(pGeode, size, 16);
199
200        if (ptr != NULL) {
201            pGeode->CBData.compression_offset = ptr->offset;
202            pGeode->CBData.size = LX_CB_PITCH;
203            pGeode->CBData.pitch = LX_CB_PITCH;
204
205            pGeode->Compression = TRUE;
206        }
207        else {
208            xf86DrvMsg(pScrni->scrnIndex, X_ERROR,
209                       "Not enough memory for compression\n");
210            pGeode->Compression = FALSE;
211        }
212    }
213
214    if (pGeode->tryHWCursor) {
215        ptr = GeodeAllocOffscreen(pGeode,
216                                  LX_CURSOR_HW_WIDTH * 4 * LX_CURSOR_HW_HEIGHT,
217                                  4);
218
219        if (ptr != NULL) {
220            pGeode->CursorStartOffset = ptr->offset;
221            pGeode->HWCursor = TRUE;
222        }
223        else {
224            xf86DrvMsg(pScrni->scrnIndex, X_ERROR,
225                       "Not enough memory for the hardware cursor\n");
226            pGeode->HWCursor = FALSE;
227        }
228    }
229
230    if (!pGeode->NoAccel && pGeode->pExa) {
231        int size;
232
233        /* Try to get the scratch buffer for blending */
234        pGeode->exaBfrOffset = 0;
235
236        if (pGeode->exaBfrSz > 0) {
237            ptr = GeodeAllocOffscreen(pGeode, pGeode->exaBfrSz, 4);
238            if (ptr != NULL)
239                pGeode->exaBfrOffset = ptr->offset;
240        }
241
242        pGeode->pExa->offScreenBase = 0;
243        pGeode->pExa->memorySize = 0;
244
245        /* This might cause complaints - in order to avoid using
246           xorg.conf as much as possible, we make assumptions about
247           what a "default" memory map would look like.  After
248           discussion, we agreed that the default driver should assume
249           the user will want to use rotation and video overlays, and
250           EXA will get whatever is leftover.
251         */
252
253        /* Get the amount of offscreen memory still left */
254        size = GeodeOffscreenFreeSize(pGeode);
255
256        /* Align the size to a K boundary */
257        size &= ~1023;
258
259        /* Allocate the EXA offscreen space */
260        ptr = GeodeAllocOffscreen(pGeode, size, 4);
261
262        if (ptr == NULL) {
263            /* If we couldn't allocate what we wanted,
264             * then allocate whats left */
265
266            ptr = GeodeAllocRemainder(pGeode);
267        }
268
269        if (ptr != NULL) {
270            pGeode->pExa->offScreenBase = ptr->offset;
271            pGeode->pExa->memorySize = ptr->offset + ptr->size;
272        }
273    }
274
275    /* Show the memory map for diagnostic purposes */
276
277    xf86DrvMsg(pScrni->scrnIndex, X_INFO, "LX video memory:\n");
278    xf86DrvMsg(pScrni->scrnIndex, X_INFO, " Display: 0x%x bytes\n",
279               pGeode->displaySize);
280
281    if (pGeode->Compression)
282        xf86DrvMsg(pScrni->scrnIndex, X_INFO, " Compression: 0x%x bytes\n",
283                   pScrni->virtualY * LX_CB_PITCH);
284
285    if (pGeode->HWCursor)
286        xf86DrvMsg(pScrni->scrnIndex, X_INFO, " Cursor: 0x%x bytes\n",
287                   LX_CURSOR_HW_WIDTH * 4 * LX_CURSOR_HW_HEIGHT);
288
289    if (pGeode->exaBfrSz)
290        xf86DrvMsg(pScrni->scrnIndex, X_INFO, " ExaBfrSz: 0x%x bytes\n",
291                   pGeode->exaBfrSz);
292
293    if (pGeode->pExa && pGeode->pExa->offScreenBase)
294        xf86DrvMsg(pScrni->scrnIndex, X_INFO, " EXA: 0x%x bytes\n",
295                   (unsigned int) (pGeode->pExa->memorySize -
296                                   pGeode->pExa->offScreenBase));
297
298    xf86DrvMsg(pScrni->scrnIndex, X_INFO, " FREE: 0x%x bytes\n",
299               GeodeOffscreenFreeSize(pGeode));
300}
301
302/* Called as we go down, so blitz everybody */
303
304void
305GeodeCloseOffscreen(ScrnInfoPtr pScrni)
306{
307    GeodeRec *pGeode = GEODEPTR(pScrni);
308    GeodeMemPtr ptr = pGeode->offscreenList;
309    GeodeMemPtr nptr;
310
311    while (ptr) {
312        nptr = ptr->next;
313        free(ptr);
314        ptr = nptr;
315    }
316
317    pGeode->offscreenList = NULL;
318}
319