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