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
16uint32
17OverlayCount(const display_mode *mode)
18{
19	(void)mode;		// avoid compiler warning for unused arg
20
21	return 1;
22}
23
24
25const uint32*
26OverlaySupportedSpaces(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
36uint32
37OverlaySupportedFeatures(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
47const overlay_buffer*
48AllocateOverlayBuffer(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	addr_t 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		addr_t currentBuffEndAddr = (addr_t)ovBuff->buffer + ovBuff->size;
107		if ((prevBuffAddr - currentBuffEndAddr) >= buffSize)
108			break;		// sufficient space for the new buffer
109
110		prevBuffAddr = (addr_t)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		addr_t 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
166status_t
167ReleaseOverlayBuffer(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
204status_t
205GetOverlayConstraints(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
253overlay_token
254AllocateOverlay(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
273status_t
274ReleaseOverlay(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
292status_t
293ConfigureOverlay (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