xref: /haiku/src/add-ons/accelerants/via/Overlay.c (revision 4bd0c1066b227cec4b79883bdef697c7a27f2e90)
1 /* Written by Rudolf Cornelissen 05/2002-4/2006 */
2 
3 /* Note on 'missing features' in BeOS 5.0.3 and DANO:
4  * BeOS needs to define more colorspaces! It would be nice if BeOS would support the FourCC 'definitions'
5  * of colorspaces. These colorspaces are 32bit words, so it could be simply done (or is it already so?)
6  */
7 
8 #define MODULE_BIT 0x00000400
9 
10 #include "acc_std.h"
11 
12 /* define the supported overlay input colorspaces */
13 /* It would be nice to have the YUV4:2:0 2-plane mode implemented also later on, but the Be colorspace
14  * definitions (in GraphicsDefs.h, R5.0.3 and DANO5.1d0) do not include this one... */
15 static uint32 overlay_colorspaces [] = { (uint32)B_YCbCr422, (uint32)B_NO_COLOR_SPACE };
16 
17 uint32 OVERLAY_COUNT(const display_mode *dm)
18 // This method is never used AFAIK though it *is* exported on R5.0.3 and DANO.
19 // Does someone know howto invoke it?
20 {
21 	LOG(4,("Overlay: count called\n"));
22 
23 	/* check for NULL pointer */
24 	if (dm == NULL)
25 	{
26 		LOG(4,("Overlay: No display mode specified!\n"));
27 	}
28 	/* apparantly overlay count should report the number of 'overlay units' on the card */
29 	return 1;
30 }
31 
32 const uint32 *OVERLAY_SUPPORTED_SPACES(const display_mode *dm)
33 // This method is never used AFAIK though it *is* exported on R5.0.3 and DANO.
34 // Does someone know howto invoke it?
35 {
36 	LOG(4,("Overlay: supported_spaces called.\n"));
37 
38 	/* check for NULL pointer */
39 	if (dm == NULL)
40 	{
41 		LOG(4,("Overlay: No display mode specified!\n"));
42 		return NULL;
43 	}
44 
45 	/* assuming interlaced VGA is not supported */
46 	if (dm->timing.flags & B_TIMING_INTERLACED)
47 	{
48 		return NULL;
49 	}
50 	/* return a B_NO_COLOR_SPACE terminated list */
51 	return &overlay_colorspaces[0];
52 }
53 
54 uint32 OVERLAY_SUPPORTED_FEATURES(uint32 a_color_space)
55 // This method is never used AFAIK. On R5.0.3 and DANO it is not even exported!
56 {
57 	LOG(4,("Overlay: supported_features: color_space $%08x\n",a_color_space));
58 
59 	/* check what features are supported for the current overlaybitmap colorspace */
60 	switch (a_color_space)
61 	{
62 	default:
63 			return
64 				( B_OVERLAY_COLOR_KEY 			 |
65 				  B_OVERLAY_HORIZONTAL_FILTERING |
66 				  B_OVERLAY_VERTICAL_FILTERING );
67 	}
68 }
69 
70 const overlay_buffer *ALLOCATE_OVERLAY_BUFFER(color_space cs, uint16 width, uint16 height)
71 {
72 	int offset = 0;					/* used to determine next buffer to create */
73 	uintptr_t adress, adress2, temp32;	/* used to calculate buffer adresses */
74 	uint32 oldsize = 0;				/* used to 'squeeze' new buffers between already existing ones */
75 	int cnt;						/* loopcounter */
76 
77 	/* acquire the shared benaphore */
78 	AQUIRE_BEN(si->overlay.lock)
79 
80 	LOG(4,("Overlay: cardRAM_start = $%p\n", (uint8*)si->framebuffer));
81 	LOG(4,("Overlay: cardRAM_start_DMA = $%p\n", (uint8*)si->framebuffer_pci));
82 	LOG(4,("Overlay: cardRAM_size = %3.3fMb\n",(si->ps.memory_size / (1024.0 * 1024.0))));
83 
84 	/* find first empty slot (room for another buffer?) */
85 	for (offset = 0; offset < MAXBUFFERS; offset++)
86 	{
87 		if (si->overlay.myBuffer[offset].buffer == NULL) break;
88 	}
89 
90 	LOG(4,("Overlay: Allocate_buffer offset = %d\n",offset));
91 
92 	if (offset < MAXBUFFERS)
93 	/* setup new scaler input buffer */
94 	{
95 		switch (cs)
96 		{
97 			case B_YCbCr422:
98 					/* check if slopspace is needed: VIA CLE266 needs ~0x0007. */
99 					si->overlay.myBuffer[offset].width = ((width + 0x0007) & ~0x0007);
100 					si->overlay.myBuffer[offset].bytes_per_row = 2 * si->overlay.myBuffer[offset].width;
101 
102 					/* check if the requested horizontal pitch is supported: */
103 					//fixme: tune for VIA... (overruled below, this should probably be a bytes_per_row check)
104 					if (si->overlay.myBuffer[offset].width > 4088)
105 					{
106 						LOG(4,("Overlay: Sorry, requested buffer pitch not supported, aborted\n"));
107 
108 						/* release the shared benaphore */
109 						RELEASE_BEN(si->overlay.lock)
110 
111 						return NULL;
112 					}
113 					break;
114 			default:
115 					/* unsupported colorspace! */
116 					LOG(4,("Overlay: Sorry, colorspace $%08x not supported, aborted\n",cs));
117 
118 					/* release the shared benaphore */
119 					RELEASE_BEN(si->overlay.lock)
120 
121 					return NULL;
122 					break;
123 		}
124 
125 		/* check if the requested buffer width is supported */
126 		if (si->overlay.myBuffer[offset].width > 1024)
127 		{
128 			LOG(4,("Overlay: Sorry, requested buffer width not supported, aborted\n"));
129 
130 			/* release the shared benaphore */
131 			RELEASE_BEN(si->overlay.lock)
132 
133 			return NULL;
134 		}
135 		/* check if the requested buffer height is supported */
136 		if (height > 1024)
137 		{
138 			LOG(4,("Overlay: Sorry, requested buffer height not supported, aborted\n"));
139 
140 			/* release the shared benaphore */
141 			RELEASE_BEN(si->overlay.lock)
142 
143 			return NULL;
144 		}
145 
146 		/* store slopspace (in pixels) for each bitmap for use by 'overlay unit' (BES) */
147 		si->overlay.myBufInfo[offset].slopspace = si->overlay.myBuffer[offset].width - width;
148 
149 		si->overlay.myBuffer[offset].space = cs;
150 		si->overlay.myBuffer[offset].height = height;
151 
152 		/* we define the overlay buffers to reside 'in the back' of the cards RAM */
153 		/* NOTE to app programmers:
154 		 * Beware that an app using overlay needs to track workspace switches and screenprefs
155 		 * changes. If such an action is detected, the app needs to reset it's pointers to the
156 		 * newly created overlay bitmaps, which will be assigned by BeOS automatically after such
157 		 * an event. (Also the app needs to respect the new overlay_constraints that will be applicable!)
158 		 *
159 		 * It is entirely possible that new bitmaps may *not* be re-setup at all, or less of them
160 		 * than previously setup by the app might be re-setup. This is due to cardRAM restraints then.
161 		 * This means that the app should also check for NULL pointers returned by the bitmaps,
162 		 * and if this happens, it needs to fallback to single buffered overlay or even fallback to
163 		 * bitmap output for the new situation. */
164 
165 		/* Another NOTE for app programmers:
166 		 * A *positive* side-effect of assigning the first overlay buffer exactly at the end of the
167 		 * cardRAM is that apps that try to write beyond the buffer's space get a segfault immediately.
168 		 * This *greatly* simplifies tracking such errors!
169 		 * Of course such errors may lead to strange effects in the app or driver behaviour if they are
170 		 * not hunted down and removed.. */
171 
172 		/* calculate first free RAM adress in card:
173 		 * Driver setup is as follows:
174 		 * card base: 		- hardware cursor bitmap (if used),
175 		 * directly above	- screen memory for both heads */
176 		adress2 = (((uintptr_t)((uint8*)si->fbc.frame_buffer)) +	/* cursor already included here */
177 			(si->fbc.bytes_per_row * si->dm.virtual_height));	/* size in bytes of screen(s) */
178 		LOG(4,("Overlay: first free cardRAM virtual adress $%08x\n", adress2));
179 
180 		/* calculate 'preliminary' buffer size including slopspace */
181 		oldsize = si->overlay.myBufInfo[offset].size;
182 		si->overlay.myBufInfo[offset].size =
183 			si->overlay.myBuffer[offset].bytes_per_row * si->overlay.myBuffer[offset].height;
184 
185 		/* calculate virtual memory adress that would be needed for a new bitmap */
186 		/* NOTE to app programmers:
187 		 * For testing app behaviour regarding workspace switches or screen prefs changes to settings
188 		 * that do not have enough cardRAM left for allocation of overlay bitmaps, you need a card with
189 		 * a low amount of RAM. Or you can set in the file skel.settings for example:
190 		 * memory 8 #8Mb RAM on card
191 		 * and reboot (this simulates 8Mb RAM on the card).
192 		 *
193 		 * If you switch now to settings: 1600x1200x32bit (single head) the app needs to fallback to
194 		 * bitmap output or maybe single buffered overlay output if small bitmaps are used. */
195 
196 		adress = (((uintptr_t)((uint8*)si->framebuffer)) + si->ps.memory_size);
197 		for (cnt = 0; cnt <= offset; cnt++)
198 		{
199 			adress -= si->overlay.myBufInfo[cnt].size;
200 		}
201 
202 		/* the > G200 scalers require buffers to be aligned to 16 byte pages cardRAM offset, G200 can do with
203 		 * 8 byte pages cardRAM offset. Compatible settings used, has no real downside consequences here */
204 
205 		/* Check if we need to modify the buffers starting adress and thus the size */
206 		/* calculate 'would be' cardRAM offset */
207 		temp32 = (adress - ((uintptr_t)((vuint32 *)si->framebuffer)));
208 		/* check if it is aligned */
209 		if (temp32 != (temp32 & 0xfffffff0))
210 		{
211 			/* update the (already calculated) buffersize to get it aligned */
212 			si->overlay.myBufInfo[offset].size += (temp32 - (temp32 & 0xfffffff0));
213 			/* update the (already calculated) adress to get it aligned */
214 			adress -= (temp32 - (temp32 & 0xfffffff0));
215 		}
216 		LOG(4,("Overlay: new buffer needs virtual adress $%08x\n", adress));
217 
218 		/* First check now if buffer to be defined is 'last one' in memory (speaking backwards):
219 		 * this is done to prevent a large buffer getting created in the space a small buffer
220 		 * occupied earlier, if not all buffers created were deleted.
221 		 * Note also that the app can delete the buffers in any order desired. */
222 
223 		/* NOTE to app programmers:
224 		 * If you are going to delete a overlay buffer you created, you should delete them *all* and
225 		 * then re-create only the new ones needed. This way you are sure not to get unused memory-
226 		 * space in between your overlay buffers for instance, so cardRAM is used 'to the max'.
227 		 * If you don't, you might not get a buffer at all if you are trying to set up a larger one
228 		 * than before.
229 		 * (Indeed: not all buffers *have* to be of the same type and size...) */
230 
231 		for (cnt = offset; cnt < MAXBUFFERS; cnt++)
232 		{
233 			if (si->overlay.myBuffer[cnt].buffer != NULL)
234 			{
235 				/* Check if the new buffer would fit into the space the single old one used here */
236 				if (si->overlay.myBufInfo[offset].size <= oldsize)
237 				{
238 					/* It does, so we reset to the old size and adresses to prevent the space from shrinking
239 					 * if we get here again... */
240 					adress -= (oldsize - si->overlay.myBufInfo[offset].size);
241 					si->overlay.myBufInfo[offset].size = oldsize;
242 					LOG(4,("Overlay: 'squeezing' in buffer:\n"
243 						   "Overlay: resetting it to virtual adress $%08x and size $%08x\n", adress,oldsize));
244 					/* force exiting the FOR loop */
245 					cnt = MAXBUFFERS;
246 				}
247 				else
248 				{
249 					/* nogo, sorry */
250 					LOG(4,("Overlay: Other buffer(s) exist after this one:\n"
251 						   "Overlay: not enough space to 'squeeze' this one in, aborted\n"));
252 
253 					/* Reset to the old size to prevent the space from 'growing' if we get here again... */
254 					si->overlay.myBufInfo[offset].size = oldsize;
255 
256 					/* release the shared benaphore */
257 					RELEASE_BEN(si->overlay.lock)
258 
259 					return NULL;
260 				}
261 			}
262 		}
263 
264 		/* check if we have enough space to setup this new bitmap
265 		 * (preventing overlap of desktop RAMspace & overlay bitmap RAMspace here) */
266 		if (adress < adress2)
267 		/* nope, sorry */
268 		{
269 			LOG(4,("Overlay: Sorry, no more space for buffers: aborted\n"));
270 
271 			/* release the shared benaphore */
272 			RELEASE_BEN(si->overlay.lock)
273 
274 			return NULL;
275 		}
276 		/* continue buffer setup */
277 		si->overlay.myBuffer[offset].buffer = (void *) adress;
278 
279 		/* calculate physical memory adress (for dma use) */
280 		adress = (((uintptr_t)((uint8*)si->framebuffer_pci)) + si->ps.memory_size);
281 		for (cnt = 0; cnt <= offset; cnt++)
282 		{
283 			adress -= si->overlay.myBufInfo[cnt].size;
284 		}
285 		/* this adress is already aligned to the scaler's requirements (via the already modified sizes) */
286 		si->overlay.myBuffer[offset].buffer_dma = (void *) adress;
287 
288 		LOG(4,("Overlay: New buffer: addr $%p, dma_addr $%p, color space $%p\n",
289 			(uint8*)si->overlay.myBuffer[offset].buffer,
290 			(uint8*)si->overlay.myBuffer[offset].buffer_dma, cs));
291 		LOG(4,("Overlay: New buffer's size is $%08x\n", si->overlay.myBufInfo[offset].size));
292 
293 		/* release the shared benaphore */
294 		RELEASE_BEN(si->overlay.lock)
295 
296 		return &si->overlay.myBuffer[offset];
297 	}
298 	else
299 	/* sorry, no more room for buffers */
300 	{
301 		LOG(4,("Overlay: Sorry, no more space for buffers: aborted\n"));
302 
303 		/* release the shared benaphore */
304 		RELEASE_BEN(si->overlay.lock)
305 
306 		return NULL;
307 	}
308 }
309 
310 status_t RELEASE_OVERLAY_BUFFER(const overlay_buffer *ob)
311 /* Note that the user can delete the buffers in any order desired! */
312 {
313 	int offset = 0;
314 
315 	if (ob != NULL)
316 	{
317 		/* find the buffer */
318 		for (offset = 0; offset < MAXBUFFERS; offset++)
319 		{
320 			if (si->overlay.myBuffer[offset].buffer == ob->buffer) break;
321 		}
322 
323 		if (offset < MAXBUFFERS)
324 		/* delete current buffer */
325 		{
326 			si->overlay.myBuffer[offset].buffer = NULL;
327 			si->overlay.myBuffer[offset].buffer_dma = NULL;
328 
329 			LOG(4,("Overlay: Release_buffer offset = %d, buffer released\n",offset));
330 
331 			return B_OK;
332 		}
333 		else
334 		{
335 			/* this is no buffer of ours! */
336 			LOG(4,("Overlay: Release_overlay_buffer: not ours, aborted!\n"));
337 
338 			return B_ERROR;
339 		}
340 	}
341 	else
342 	/* no buffer specified! */
343 	{
344 		LOG(4,("Overlay: Release_overlay_buffer: no buffer specified, aborted!\n"));
345 
346 		return B_ERROR;
347 	}
348 }
349 
350 status_t GET_OVERLAY_CONSTRAINTS
351 	(const display_mode *dm, const overlay_buffer *ob, overlay_constraints *oc)
352 {
353 	int offset = 0;
354 
355 	LOG(4,("Overlay: Get_overlay_constraints called\n"));
356 
357 	/* check for NULL pointers */
358 	if ((dm == NULL) || (ob == NULL) || (oc == NULL))
359 	{
360 		LOG(4,("Overlay: Get_overlay_constraints: Null pointer(s) detected!\n"));
361 		return B_ERROR;
362 	}
363 
364 	/* find the buffer */
365 	for (offset = 0; offset < MAXBUFFERS; offset++)
366 	{
367 		if (si->overlay.myBuffer[offset].buffer == ob->buffer) break;
368 	}
369 
370 	if (offset < MAXBUFFERS)
371 	{
372 		/* scaler input (values are in pixels) */
373 		oc->view.h_alignment = 0;
374 		oc->view.v_alignment = 0;
375 
376 		switch (ob->space)
377 		{
378 			case B_YCbCr422:
379 					/* Note: this has to be in sync with the slopspace setup during buffer allocation.. */
380 					oc->view.width_alignment = 7;
381 					break;
382 			default:
383 					/* we should not be here, but set the worst-case value just to be safe anyway */
384 					oc->view.width_alignment = 7;
385 					break;
386 		}
387 
388 		oc->view.height_alignment = 0;
389 		oc->view.width.min = 1;
390 		oc->view.height.min = 2; /* two fields */
391 		oc->view.width.max = ob->width;
392 		oc->view.height.max = ob->height;
393 
394 		/* scaler output restrictions */
395 		oc->window.h_alignment = 0;
396 		oc->window.v_alignment = 0;
397 		oc->window.width_alignment = 0;
398 		oc->window.height_alignment = 0;
399 		oc->window.width.min = 2;
400 		/* GeForce cards can output upto and including 2046 pixels in width */
401 		//fixme: how about TNT?
402 		if (dm->virtual_width > 2046)
403 		{
404 			oc->window.width.max = 2046;
405 		}
406 		else
407 		{
408 			oc->window.width.max = dm->virtual_width;
409 		}
410 		oc->window.height.min = 2;
411 		/* GeForce cards can output upto and including 2046 pixels in height */
412 		//fixme: how about TNT?
413 		if (dm->virtual_height > 2046)
414 		{
415 			oc->window.height.max = 2046;
416 		}
417 		else
418 		{
419 			oc->window.height.max = dm->virtual_height;
420 		}
421 
422 		/* VIA scaling restrictions */
423 		/* VIA has a 'tricked' 1/16 minimum */
424 		oc->h_scale.min = 0.0625;
425 		oc->v_scale.min = 0.0625;
426 		/* all cards have a upscaling limit of 8.0 (see official nVidia specsheets) */
427 		//checkout.. (fixme)
428 		oc->h_scale.max = 8.0;
429 		oc->v_scale.max = 8.0;
430 
431 		return B_OK;
432 	}
433 	else
434 	{
435 		/* this is no buffer of ours! */
436 		LOG(4,("Overlay: Get_overlay_constraints: buffer is not ours, aborted!\n"));
437 
438 		return B_ERROR;
439 	}
440 }
441 
442 overlay_token ALLOCATE_OVERLAY(void)
443 {
444 	uint32 tmpToken;
445 	LOG(4,("Overlay: Allocate_overlay called: "));
446 
447 	/* come up with a token */
448 	tmpToken = 0x12345678;
449 
450 	/* acquire the shared benaphore */
451 	AQUIRE_BEN(si->overlay.lock)
452 
453 	/* overlay unit already in use? */
454 	if (si->overlay.myToken == NULL)
455 	/* overlay unit is available */
456 	{
457 		LOG(4,("succesfull\n"));
458 
459 		si->overlay.myToken = &tmpToken;
460 
461 		/* release the shared benaphore */
462 		RELEASE_BEN(si->overlay.lock)
463 
464 		return si->overlay.myToken;
465 	}
466 	else
467 	/* sorry, overlay unit is occupied */
468 	{
469 		LOG(4,("failed: already in use!\n"));
470 
471 		/* release the shared benaphore */
472 		RELEASE_BEN(si->overlay.lock)
473 
474 		return NULL;
475 	}
476 }
477 
478 status_t RELEASE_OVERLAY(overlay_token ot)
479 {
480 	LOG(4,("Overlay: Release_overlay called: "));
481 
482 	/* is this call for real? */
483 	if ((ot == NULL) || (si->overlay.myToken == NULL) || (ot != si->overlay.myToken))
484 	/* nope, abort */
485 	{
486 		LOG(4,("failed, not in use!\n"));
487 
488 		return B_ERROR;
489 	}
490 	else
491 	/* call is for real */
492 	{
493 
494 		eng_release_bes();
495 
496 		LOG(4,("succesfull\n"));
497 
498 		si->overlay.myToken = NULL;
499 		return B_OK;
500 	}
501 }
502 
503 status_t CONFIGURE_OVERLAY
504 	(overlay_token ot, const overlay_buffer *ob, const overlay_window *ow, const overlay_view *ov)
505 {
506 	int offset = 0; /* used for buffer index */
507 
508 	LOG(4,("Overlay: Configure_overlay called: "));
509 
510 	/* Note:
511 	 * When a Workspace switch, screen prefs change, or overlay app shutdown occurs, BeOS will
512 	 * release all overlay buffers. The buffer currently displayed at that moment, may need some
513 	 * 'hardware releasing' in the CONFIGURE_OVERLAY routine. This is why CONFIGURE_OVERLAY gets
514 	 * called one more time then, with a null pointer for overlay_window and overlay_view, while
515 	 * the currently displayed overlay_buffer is given.
516 	 * The G200-G550 do not need to do anything on such an occasion, so we simply return if we
517 	 * get called then. */
518 	if ((ow == NULL) || (ov == NULL))
519 	{
520 		LOG(4,("output properties changed\n"));
521 
522 		return B_OK;
523 	}
524 
525 	/* Note:
526 	 * If during overlay use the screen prefs are changed, or the workspace has changed, it
527 	 * may be that we were not able to re-allocate the requested overlay buffers (or only partly)
528 	 * due to lack of cardRAM. If the app does not respond properly to this, we might end up
529 	 * with a NULL pointer instead of a overlay_buffer to work with here.
530 	 * Of course, we need to abort then to prevent the system from 'going down'.
531 	 * The app will probably crash because it will want to write into this non-existant buffer
532 	 * at some point. */
533 	if (ob == NULL)
534 	{
535 		LOG(4,("no overlay buffer specified\n"));
536 
537 		return B_ERROR;
538 	}
539 
540 	/* is this call done by the app that owns us? */
541 	if ((ot == NULL) || (si->overlay.myToken == NULL) || (ot != si->overlay.myToken))
542 	/* nope, abort */
543 	{
544 		LOG(4,("failed\n"));
545 
546 		return B_ERROR;
547 	}
548 	else
549 	/* call is for real */
550 	{
551 		/* find the buffer's offset */
552 		for (offset = 0; offset < MAXBUFFERS; offset++)
553 		{
554 			if (si->overlay.myBuffer[offset].buffer == ob->buffer) break;
555 		}
556 
557 		if (offset < MAXBUFFERS)
558 		{
559 			LOG(4,("succesfull, switching to buffer %d\n", offset));
560 
561 			/* program overlay hardware */
562 			eng_configure_bes(ob, ow, ov, offset);
563 
564 			return B_OK;
565 		}
566 		else
567 		{
568 			/* this is no buffer of ours! */
569 			LOG(4,("buffer is not ours, aborted!\n"));
570 
571 			return B_ERROR;
572 		}
573 	}
574 }
575