xref: /haiku/src/add-ons/accelerants/3dfx/overlay.cpp (revision 002f37b0cca92e4cf72857c72ac95db5a8b09615)
1 /*
2  * Copyright 2010 Haiku, Inc.  All rights reserved.
3  * Distributed under the terms of the MIT license.
4  *
5  * Authors:
6  *		Gerald Zajac
7  */
8 
9 #include "accelerant.h"
10 #include "3dfx.h"
11 
12 #include <stdlib.h>
13 
14 
15 
16 uint32
17 OverlayCount(const display_mode *mode)
18 {
19 	(void)mode;		// avoid compiler warning for unused arg
20 
21 	return 1;
22 }
23 
24 
25 const uint32*
26 OverlaySupportedSpaces(const display_mode* mode)
27 {
28 	(void)mode;		// avoid compiler warning for unused arg
29 
30 	static const uint32 kSupportedSpaces[] = {B_RGB16, B_YCbCr422, 0};
31 
32 	return kSupportedSpaces;
33 }
34 
35 
36 uint32
37 OverlaySupportedFeatures(uint32 colorSpace)
38 {
39 	(void)colorSpace;		// avoid compiler warning for unused arg
40 
41 	return B_OVERLAY_COLOR_KEY
42 		| B_OVERLAY_HORIZONTAL_FILTERING
43 		| B_OVERLAY_VERTICAL_FILTERING;
44 }
45 
46 
47 const overlay_buffer*
48 AllocateOverlayBuffer(color_space colorSpace, uint16 width, uint16 height)
49 {
50 	SharedInfo& si = *gInfo.sharedInfo;
51 
52 	TRACE("AllocateOverlayBuffer() width %u, height %u, colorSpace 0x%lx\n",
53 		width, height, colorSpace);
54 
55 	si.overlayLock.Acquire();
56 
57 	// Note:  When allocating buffers, buffer allocation starts at the end of
58 	// video memory, and works backward to the end of the video frame buffer.
59 	// The allocated buffers are recorded in a linked list of OverlayBuffer
60 	// objects which are ordered by the buffer address with the first object
61 	// in the list having the highest buffer address.
62 
63 	uint32 bytesPerPixel;
64 
65 	switch (colorSpace) {
66 		case B_YCbCr422:
67 		case B_RGB16:
68 			bytesPerPixel = 2;
69 			break;
70 		default:
71 			si.overlayLock.Release();
72 			TRACE("AllocateOverlayBuffer() unsupported color space 0x%x\n",
73 				colorSpace);
74 			return NULL;
75 	}
76 
77 	// Calculate required buffer size as a multiple of 1K.
78 	uint32 buffSize = (width * bytesPerPixel * height + 0x3ff) & ~0x3ff;
79 
80 	// If the buffer area starts at the end of the video memory, the Voodoo5
81 	// chip has the following display problems:  displays a pink/red band at
82 	// the bottom of the overlay display, leaves some artifacts at the top of
83 	// screen, and messes up the displayed cursor when a hardware cursor is
84 	// used (cursor image is at beginning of video memory).  I don't know
85 	// whether the Voodoo5 goes beyond the buffer area or whether this is some
86 	// sort of memory mapping problem;  nevertheless, adding 4k or 8K to the
87 	// buffer size solves the problem.  Thus, 16K is added to the buffer size.
88 
89 	if (si.chipType == VOODOO_5)
90 		buffSize += 16 * 1024;
91 
92 	OverlayBuffer* ovBuff = si.overlayBuffer;
93 	OverlayBuffer* prevOvBuff = NULL;
94 
95 	// If no buffers have been allocated, prevBuffAddr calculated here will be
96 	// the address where the buffer area will start.
97 
98 	uint32 prevBuffAddr = si.videoMemAddr + si.frameBufferOffset
99 						+ si.maxFrameBufferSize;
100 
101 	while (ovBuff != NULL) {
102 		// Test if there is sufficient space between the end of the current
103 		// buffer and the start of the previous buffer to allocate the new
104 		// buffer.
105 
106 		uint32 currentBuffEndAddr = (addr_t)ovBuff->buffer + ovBuff->size;
107 		if ((prevBuffAddr - currentBuffEndAddr) >= buffSize)
108 			break;		// sufficient space for the new buffer
109 
110 		prevBuffAddr = (uint32)ovBuff->buffer;
111 		prevOvBuff = ovBuff;
112 		ovBuff = ovBuff->nextBuffer;
113 	}
114 
115 	OverlayBuffer* nextOvBuff = ovBuff;
116 
117 	if (ovBuff == NULL) {
118 		// No space between any current buffers of the required size was found;
119 		// thus space must be allocated between the last buffer and the end of
120 		// the video frame buffer.  Compute where current video frame buffer
121 		// ends so that it can be determined if there is sufficient space for
122 		// the new buffer to be created.
123 
124 		uint32 fbEndAddr = si.videoMemAddr + si.frameBufferOffset
125 			+ (si.displayMode.virtual_width * si.displayMode.bytesPerPixel
126 			* si.displayMode.virtual_height);
127 
128 		if (buffSize > prevBuffAddr - fbEndAddr) {
129 			si.overlayLock.Release();
130 			TRACE("AllocateOverlayBuffer() insuffcient space for %ld (0x%lx) "
131 					"byte buffer\n", buffSize, buffSize);
132 			return NULL;	// insufficient space for buffer
133 		}
134 
135 		nextOvBuff = NULL;
136 	}
137 
138 	ovBuff = (OverlayBuffer*)malloc(sizeof(OverlayBuffer));
139 	if (ovBuff == NULL) {
140 		si.overlayLock.Release();
141 		return NULL;		// memory not available for OverlayBuffer struct
142 	}
143 
144 	ovBuff->nextBuffer = nextOvBuff;
145 	ovBuff->size = buffSize;
146 	ovBuff->space = colorSpace;
147 	ovBuff->width = width;
148 	ovBuff->height = height;
149 	ovBuff->bytes_per_row = width * bytesPerPixel;
150 	ovBuff->buffer = (void*)(prevBuffAddr - buffSize);
151 	ovBuff->buffer_dma = (void*)(si.videoMemPCI
152 		+ ((addr_t)ovBuff->buffer - si.videoMemAddr));
153 
154 	if (prevOvBuff == NULL)
155 		si.overlayBuffer = ovBuff;
156 	else
157 		prevOvBuff->nextBuffer = ovBuff;
158 
159 	si.overlayLock.Release();
160 	TRACE("AllocateOverlayBuffer() allocated %ld (0x%lx) byte buffer at 0x%lx\n",
161 		buffSize, buffSize, ovBuff->buffer);
162 	return ovBuff;
163 }
164 
165 
166 status_t
167 ReleaseOverlayBuffer(const overlay_buffer* buffer)
168 {
169 	SharedInfo& si = *gInfo.sharedInfo;
170 
171 	TRACE("ReleaseOverlayBuffer() called\n");
172 
173 	if (buffer == NULL)
174 		return B_BAD_VALUE;
175 
176 	// Find the buffer to be released.
177 
178 	OverlayBuffer* ovBuff = si.overlayBuffer;
179 	OverlayBuffer* prevOvBuff = NULL;
180 
181 	while (ovBuff != NULL) {
182 		if (ovBuff->buffer == buffer->buffer) {
183 			// Buffer to be released has been found.  Remove the OverlayBuffer
184 			// object from the chain of overlay buffers.
185 
186 			if (prevOvBuff == NULL)
187 				si.overlayBuffer = ovBuff->nextBuffer;
188 			else
189 				prevOvBuff->nextBuffer = ovBuff->nextBuffer;
190 
191 			free(ovBuff);
192 			TRACE("ReleaseOverlayBuffer() return OK\n");
193 			return B_OK;
194 		}
195 
196 		prevOvBuff = ovBuff;
197 		ovBuff = ovBuff->nextBuffer;
198 	}
199 
200 	return B_ERROR;		// buffer to be released not found in chain of buffers
201 }
202 
203 
204 status_t
205 GetOverlayConstraints(const display_mode* mode, const overlay_buffer* buffer,
206 					overlay_constraints* constraints)
207 {
208 	if ((mode == NULL) || (buffer == NULL) || (constraints == NULL))
209 		return B_ERROR;
210 
211 	// Position (values are in pixels)
212 	constraints->view.h_alignment = 0;
213 	constraints->view.v_alignment = 0;
214 
215 	switch (buffer->space) {
216 		case B_YCbCr422:
217 		case B_RGB16:
218 			constraints->view.width_alignment = 7;
219 			break;
220 		default:
221 			TRACE("GetOverlayConstraints() color space 0x%x out of range\n",
222 				buffer->space);
223 			return B_BAD_VALUE;
224 	}
225 
226 	constraints->view.height_alignment = 0;
227 
228 	//Size
229 	constraints->view.width.min = 4;
230 	constraints->view.height.min = 4;
231 	constraints->view.width.max = buffer->width;
232 	constraints->view.height.max = buffer->height;
233 
234 	// Scaler output restrictions
235 	constraints->window.h_alignment = 0;
236 	constraints->window.v_alignment = 0;
237 	constraints->window.width_alignment = 0;
238 	constraints->window.height_alignment = 0;
239 	constraints->window.width.min = 2;
240 	constraints->window.width.max = mode->virtual_width;
241 	constraints->window.height.min = 2;
242 	constraints->window.height.max = mode->virtual_height;
243 
244 	constraints->h_scale.min = 1.0;
245 	constraints->h_scale.max = 8.0;
246 	constraints->v_scale.min = 1.0;
247 	constraints->v_scale.max = 8.0;
248 
249 	return B_OK;
250 }
251 
252 
253 overlay_token
254 AllocateOverlay(void)
255 {
256 	SharedInfo& si = *gInfo.sharedInfo;
257 
258 	TRACE("AllocateOverlay() called\n");
259 
260 	// There is only a single overlay channel;  thus, check if it is already
261 	// allocated.
262 
263 	if (atomic_or(&si.overlayAllocated, 1) != 0) {
264 		TRACE("AllocateOverlay() overlay channel already in use\n");
265 		return NULL;
266 	}
267 	TRACE("AllocateOverlay() Overlay allocated, overlayToken: %d\n",
268 		si.overlayToken);
269 	return (overlay_token)++si.overlayToken;
270 }
271 
272 
273 status_t
274 ReleaseOverlay(overlay_token overlayToken)
275 {
276 	SharedInfo& si = *gInfo.sharedInfo;
277 
278 	TRACE("ReleaseOverlay() called\n");
279 
280 	if (overlayToken != (overlay_token)si.overlayToken)
281 		return B_BAD_VALUE;
282 
283 	TDFX_StopOverlay();
284 
285 	atomic_and(&si.overlayAllocated, 0);	// mark overlay as unallocated
286 
287 	TRACE("ReleaseOverlay() return OK\n");
288 	return B_OK;
289 }
290 
291 
292 status_t
293 ConfigureOverlay (overlay_token overlayToken, const overlay_buffer* buffer,
294 	const overlay_window* window, const overlay_view* view)
295 {
296 	SharedInfo& si = *gInfo.sharedInfo;
297 
298 	if (overlayToken != (overlay_token)si.overlayToken)
299 		return B_BAD_VALUE;
300 
301 	if (buffer == NULL)
302 		return B_BAD_VALUE;
303 
304 	if (window == NULL || view == NULL) {
305 		TDFX_StopOverlay();
306 		TRACE("ConfigureOverlay() hide only\n");
307 		return B_OK;
308 	}
309 
310 	// Program the overlay hardware.
311 	if (!TDFX_DisplayOverlay(window, buffer, view)) {
312 		TRACE("ConfigureOverlay(), call to TDFX_DisplayOverlay() returned error\n");
313 		return B_ERROR;
314 	}
315 
316 	return B_OK;
317 }
318