lx_memory.c revision 170d5fdc
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    free(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 = calloc(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 = calloc(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 = calloc(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 = calloc(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	/* Align the size to a K boundary */
251	size &= ~1023;
252
253	/* Allocate the EXA offscreen space */
254	ptr = GeodeAllocOffscreen(pGeode, size, 4);
255
256	if (ptr == NULL) {
257	    /* If we couldn't allocate what we wanted,
258	     * then allocate whats left */
259
260	    ptr = GeodeAllocRemainder(pGeode);
261	}
262
263	if (ptr != NULL) {
264	    pGeode->pExa->offScreenBase = ptr->offset;
265	    pGeode->pExa->memorySize = ptr->offset + ptr->size;
266	}
267    }
268
269    /* Show the memory map for diagnostic purposes */
270
271    xf86DrvMsg(pScrni->scrnIndex, X_INFO, "LX video memory:\n");
272    xf86DrvMsg(pScrni->scrnIndex, X_INFO, " Display: 0x%x bytes\n",
273	pGeode->displaySize);
274
275    if (pGeode->Compression)
276	xf86DrvMsg(pScrni->scrnIndex, X_INFO, " Compression: 0x%x bytes\n",
277	    pScrni->virtualY * LX_CB_PITCH);
278
279    if (pGeode->HWCursor)
280	xf86DrvMsg(pScrni->scrnIndex, X_INFO, " Cursor: 0x%x bytes\n",
281	    LX_CURSOR_HW_WIDTH * 4 * LX_CURSOR_HW_HEIGHT);
282
283    if (pGeode->exaBfrSz)
284	xf86DrvMsg(pScrni->scrnIndex, X_INFO, " ExaBfrSz: 0x%x bytes\n",
285	    pGeode->exaBfrSz);
286
287    if (pGeode->pExa && pGeode->pExa->offScreenBase)
288	xf86DrvMsg(pScrni->scrnIndex, X_INFO, " EXA: 0x%x bytes\n",
289	    (unsigned int)(pGeode->pExa->memorySize -
290		pGeode->pExa->offScreenBase));
291
292    xf86DrvMsg(pScrni->scrnIndex, X_INFO, " FREE: 0x%x bytes\n",
293	GeodeOffscreenFreeSize(pGeode));
294}
295
296/* Called as we go down, so blitz everybody */
297
298void
299GeodeCloseOffscreen(ScrnInfoPtr pScrni)
300{
301    GeodeRec *pGeode = GEODEPTR(pScrni);
302    GeodeMemPtr ptr = pGeode->offscreenList;
303    GeodeMemPtr nptr;
304
305    while (ptr) {
306	nptr = ptr->next;
307	free(ptr);
308	ptr = nptr;
309    }
310
311    pGeode->offscreenList = NULL;
312}
313