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