xref: /haiku/src/add-ons/accelerants/ati/overlay.cpp (revision 1deede7388b04dbeec5af85cae7164735ea9e70d)
1 /*
2 	Copyright 2011 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 "rage128.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_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 	// If Mach 64 chip, check hardware limits.
56 
57 	if (MACH64_FAMILY(si.chipType)) {
58 		if (height > 2048 || width > 768
59 				|| (width > 384 && si.chipType < MACH64_264VTB)
60 				|| (width > 720 && (si.chipType < MACH64_264GTPRO
61 				|| si.chipType > MACH64_264LTPRO)))
62 			return NULL;
63 	}
64 
65 	si.overlayLock.Acquire();
66 
67 	// Note:  When allocating buffers, buffer allocation starts at the end of
68 	// video memory, and works backward to the end of the video frame buffer.
69 	// The allocated buffers are recorded in a linked list of OverlayBuffer
70 	// objects which are ordered by the buffer address with the first object
71 	// in the list having the highest buffer address.
72 
73 	if (colorSpace != B_YCbCr422) {
74 		si.overlayLock.Release();
75 		TRACE("AllocateOverlayBuffer() unsupported color space 0x%x\n",
76 			colorSpace);
77 		return NULL;
78 	}
79 
80 	uint32 bytesPerPixel = 2;		// B_YCbCr422 uses 2 bytes per pixel
81 
82 	// Calculate required buffer size as a multiple of 1K.
83 	uint32 buffSize = (width * bytesPerPixel * height + 0x3ff) & ~0x3ff;
84 
85 	OverlayBuffer* ovBuff = si.overlayBuffer;
86 	OverlayBuffer* prevOvBuff = NULL;
87 
88 	// If no buffers have been allocated, prevBuffAddr calculated here will be
89 	// the address where the buffer area will start.  Leave a gap of about 4K
90 	// between where the overlay buffers will start and the cursor image;
91 	// thus, if the overlay buffer overflows it will be less apt to affect the
92 	// cursor.
93 
94 	addr_t prevBuffAddr = (si.videoMemAddr + si.cursorOffset - 0xfff) & ~0xfff;
95 
96 	while (ovBuff != NULL) {
97 		// Test if there is sufficient space between the end of the current
98 		// buffer and the start of the previous buffer to allocate the new
99 		// buffer.
100 
101 		addr_t currentBuffEndAddr = (addr_t)ovBuff->buffer + ovBuff->size;
102 		if ((prevBuffAddr - currentBuffEndAddr) >= buffSize)
103 			break;		// sufficient space for the new buffer
104 
105 		prevBuffAddr = (addr_t)ovBuff->buffer;
106 		prevOvBuff = ovBuff;
107 		ovBuff = ovBuff->nextBuffer;
108 	}
109 
110 	OverlayBuffer* nextOvBuff = ovBuff;
111 
112 	if (ovBuff == NULL) {
113 		// No space between any current buffers of the required size was found;
114 		// thus space must be allocated between the last buffer and the end of
115 		// the video frame buffer.  Compute where current video frame buffer
116 		// ends so that it can be determined if there is sufficient space for
117 		// the new buffer to be created.
118 
119 		addr_t fbEndAddr = si.videoMemAddr + si.frameBufferOffset
120 			+ (si.displayMode.virtual_width
121 			* ((si.displayMode.bitsPerPixel + 7) / 8)	// bytes per pixel
122 			* si.displayMode.virtual_height);
123 
124 		if (buffSize > prevBuffAddr - fbEndAddr) {
125 			si.overlayLock.Release();
126 			TRACE("AllocateOverlayBuffer() insuffcient space for %ld (0x%lx) "
127 					"byte buffer\n", buffSize, buffSize);
128 			return NULL;	// insufficient space for buffer
129 		}
130 
131 		nextOvBuff = NULL;
132 	}
133 
134 	ovBuff = (OverlayBuffer*)malloc(sizeof(OverlayBuffer));
135 	if (ovBuff == NULL) {
136 		si.overlayLock.Release();
137 		return NULL;		// memory not available for OverlayBuffer struct
138 	}
139 
140 	ovBuff->nextBuffer = nextOvBuff;
141 	ovBuff->size = buffSize;
142 	ovBuff->space = colorSpace;
143 	ovBuff->width = width;
144 	ovBuff->height = height;
145 	ovBuff->bytes_per_row = width * bytesPerPixel;
146 	ovBuff->buffer = (void*)(prevBuffAddr - buffSize);
147 	ovBuff->buffer_dma = (void*)(si.videoMemPCI
148 		+ ((addr_t)ovBuff->buffer - si.videoMemAddr));
149 
150 	if (prevOvBuff == NULL)
151 		si.overlayBuffer = ovBuff;
152 	else
153 		prevOvBuff->nextBuffer = ovBuff;
154 
155 	si.overlayLock.Release();
156 	TRACE("AllocateOverlayBuffer() allocated %ld (0x%lx) byte buffer at 0x%lx\n",
157 		buffSize, buffSize, ovBuff->buffer);
158 	return ovBuff;
159 }
160 
161 
162 status_t
163 ReleaseOverlayBuffer(const overlay_buffer* buffer)
164 {
165 	SharedInfo& si = *gInfo.sharedInfo;
166 
167 	if (buffer == NULL)
168 		return B_BAD_VALUE;
169 
170 	// Find the buffer to be released.
171 
172 	OverlayBuffer* ovBuff = si.overlayBuffer;
173 	OverlayBuffer* prevOvBuff = NULL;
174 
175 	while (ovBuff != NULL) {
176 		if (ovBuff->buffer == buffer->buffer) {
177 			// Buffer to be released has been found.  Remove the OverlayBuffer
178 			// object from the chain of overlay buffers.
179 
180 			if (prevOvBuff == NULL)
181 				si.overlayBuffer = ovBuff->nextBuffer;
182 			else
183 				prevOvBuff->nextBuffer = ovBuff->nextBuffer;
184 
185 			free(ovBuff);
186 			return B_OK;
187 		}
188 
189 		prevOvBuff = ovBuff;
190 		ovBuff = ovBuff->nextBuffer;
191 	}
192 
193 	TRACE("ReleaseOverlayBuffer() buffer to release at 0x%lx not found\n",
194 		buffer->buffer);
195 	return B_ERROR;		// buffer to be released not found in chain of buffers
196 }
197 
198 
199 status_t
200 GetOverlayConstraints(const display_mode* mode, const overlay_buffer* buffer,
201 					overlay_constraints* constraints)
202 {
203 	if ((mode == NULL) || (buffer == NULL) || (constraints == NULL))
204 		return B_ERROR;
205 
206 	// Position (values are in pixels)
207 	constraints->view.h_alignment = 0;
208 	constraints->view.v_alignment = 0;
209 
210 	if (buffer->space == B_YCbCr422)
211 		constraints->view.width_alignment = 7;
212 	else {
213 		TRACE("GetOverlayConstraints() color space 0x%x out of range\n",
214 			buffer->space);
215 		return B_BAD_VALUE;
216 	}
217 
218 	constraints->view.height_alignment = 0;
219 
220 	//Size
221 	constraints->view.width.min = 4;
222 	constraints->view.height.min = 4;
223 	constraints->view.width.max = buffer->width;
224 	constraints->view.height.max = buffer->height;
225 
226 	// Scaler output restrictions
227 	constraints->window.h_alignment = 0;
228 	constraints->window.v_alignment = 0;
229 	constraints->window.width_alignment = 0;
230 	constraints->window.height_alignment = 0;
231 	constraints->window.width.min = 2;
232 	constraints->window.width.max = mode->virtual_width;
233 	constraints->window.height.min = 2;
234 	constraints->window.height.max = mode->virtual_height;
235 
236 	constraints->h_scale.min = 1.0;
237 	constraints->h_scale.max = 8.0;
238 	constraints->v_scale.min = 1.0;
239 	constraints->v_scale.max = 8.0;
240 
241 	return B_OK;
242 }
243 
244 
245 overlay_token
246 AllocateOverlay(void)
247 {
248 	SharedInfo& si = *gInfo.sharedInfo;
249 
250 	// There is only a single overlay channel;  thus, check if it is already
251 	// allocated.
252 
253 	if (atomic_or(&si.overlayAllocated, 1) != 0) {
254 		TRACE("AllocateOverlay() overlay channel already in use\n");
255 		return NULL;
256 	}
257 
258 	return (overlay_token)(addr_t)++si.overlayToken;
259 }
260 
261 
262 status_t
263 ReleaseOverlay(overlay_token overlayToken)
264 {
265 	SharedInfo& si = *gInfo.sharedInfo;
266 
267 	if (overlayToken != (overlay_token)(addr_t)si.overlayToken) {
268 		TRACE("ReleaseOverlay() error - no overlay previously allocated\n");
269 		return B_BAD_VALUE;
270 	}
271 
272 	if (MACH64_FAMILY(si.chipType))
273 		Mach64_StopOverlay();
274 	else
275 		Rage128_StopOverlay();
276 
277 	atomic_and(&si.overlayAllocated, 0);	// mark overlay as unallocated
278 	return B_OK;
279 }
280 
281 
282 status_t
283 ConfigureOverlay(overlay_token overlayToken, const overlay_buffer* buffer,
284 	const overlay_window* window, const overlay_view* view)
285 {
286 	SharedInfo& si = *gInfo.sharedInfo;
287 
288 	if (overlayToken != (overlay_token)(addr_t)si.overlayToken)
289 		return B_BAD_VALUE;
290 
291 	if (buffer == NULL)
292 		return B_BAD_VALUE;
293 
294 	if (window == NULL || view == NULL) {
295 		if (MACH64_FAMILY(si.chipType))
296 			Mach64_StopOverlay();
297 		else
298 			Rage128_StopOverlay();
299 
300 		return B_OK;
301 	}
302 
303 	// Program the overlay hardware.
304 	if (MACH64_FAMILY(si.chipType)) {
305 		if (!Mach64_DisplayOverlay(window, buffer)) {
306 			TRACE("ConfigureOverlay(), call to Mach64_DisplayOverlay() "
307 				"returned error\n");
308 			return B_ERROR;
309 		}
310 	} else {
311 		if (!Rage128_DisplayOverlay(window, buffer)) {
312 			TRACE("ConfigureOverlay(), call to Rage128_DisplayOverlay() "
313 				"returned error\n");
314 			return B_ERROR;
315 		}
316 	}
317 
318 	return B_OK;
319 }
320