1/*
2	Copyright 1999, Be Incorporated.   All Rights Reserved.
3	This file may be used under the terms of the Be Sample Code License.
4
5	Other authors:
6	Mark Watson,
7	Rudolf Cornelissen 10/2002-1/2016.
8*/
9
10#define MODULE_BIT 0x00800000
11
12#include <string.h>
13#include <unistd.h>
14#include "acc_std.h"
15
16static status_t init_common(int the_fd);
17
18/* Initialization code shared between primary and cloned accelerants */
19static status_t init_common(int the_fd) {
20	status_t result;
21	nv_get_private_data gpd;
22
23	// LOG not available from here to next LOG: NULL si
24
25	/* memorize the file descriptor */
26	fd = the_fd;
27	/* set the magic number so the driver knows we're for real */
28	gpd.magic = NV_PRIVATE_DATA_MAGIC;
29	/* contact driver and get a pointer to the registers and shared data */
30	result = ioctl(fd, NV_GET_PRIVATE_DATA, &gpd, sizeof(gpd));
31	if (result != B_OK)
32		goto error0;
33
34	/* clone the shared area for our use */
35	shared_info_area = clone_area(DRIVER_PREFIX " shared", (void **)&si, B_ANY_ADDRESS,
36		B_READ_AREA | B_WRITE_AREA, gpd.shared_info_area);
37	if (shared_info_area < 0) {
38			result = shared_info_area;
39			goto error0;
40	}
41	// LOG is now available, si !NULL
42	LOG(4,("init_common: logmask 0x%08x, memory %dMB, hardcursor %d, usebios %d, switchhead %d, force_pci %d\n",
43		si->settings.logmask, si->settings.memory, si->settings.hardcursor, si->settings.usebios, si->settings.switchhead, si->settings.force_pci));
44	LOG(4,("init_common: dumprom %d, unhide_fw %d, pgm_panel %d, dma_acc %d, tv_output %d, vga_on_tv %d\n",
45		si->settings.dumprom, si->settings.unhide_fw, si->settings.pgm_panel, si->settings.dma_acc, si->settings.tv_output, si->settings.vga_on_tv));
46	LOG(4,("init_common: force_sync %d, gpu_clk %dMhz, ram_clk %dMhz, force_ws %d, block_acc %d, check_edid %d\n",
47		si->settings.force_sync, si->settings.gpu_clk, si->settings.ram_clk, si->settings.force_ws, si->settings.block_acc, si->settings.check_edid));
48
49 	/*Check for R4.5.0 and if it is running, use work around*/
50 	{
51 		if (si->use_clone_bugfix)
52 		{
53 			/*check for R4.5.0 bug and attempt to work around*/
54 			LOG(2,("InitACC: Found R4.5.0 bug - attempting to work around\n"));
55 			regs = si->clone_bugfix_regs;
56 		}
57 		else
58 		{
59			/* clone the memory mapped registers for our use  - does not work on <4.5.2 (but is better this way)*/
60			regs_area = clone_area(DRIVER_PREFIX " regs", (void **)&regs, B_ANY_ADDRESS,
61				B_READ_AREA | B_WRITE_AREA, si->regs_area);
62			if (regs_area < 0) {
63				result = regs_area;
64				goto error1;
65			}
66 		}
67 	}
68
69	/* all done */
70	goto error0;
71
72error1:
73	delete_area(shared_info_area);
74error0:
75	return result;
76}
77
78/* Clean up code shared between primary and cloned accelrants */
79static void uninit_common(void) {
80	/* release the memory mapped registers */
81	delete_area(regs_area);
82	/* a little cheap paranoia */
83	regs = 0;
84	/* release our copy of the shared info from the kernel driver */
85	delete_area(shared_info_area);
86	/* more cheap paranoia */
87	si = 0;
88}
89
90/*
91Initialize the accelerant.  the_fd is the file handle of the device (in
92/dev/graphics) that has been opened by the app_server (or some test harness).
93We need to determine if the kernel driver and the accelerant are compatible.
94If they are, get the accelerant ready to handle other hook functions and
95report success or failure.
96*/
97status_t INIT_ACCELERANT(int the_fd)
98{
99	status_t result;
100	int pointer_reservation; //mem reserved for pointer
101	int cnt; 				 //used for iteration through the overlay buffers
102
103	if (0) {
104		time_t now = time (NULL);
105		// LOG not available from here to next LOG: NULL si
106		MSG(("INIT_ACCELERANT: %s", ctime (&now)));
107	}
108
109	/* note that we're the primary accelerant (accelerantIsClone is global) */
110	accelerantIsClone = 0;
111
112	/* do the initialization common to both the primary and the clones */
113	result = init_common(the_fd);
114
115	/* bail out if the common initialization failed */
116	if (result != B_OK)
117		goto error0;
118	// LOG now available: !NULL si
119
120	/* ensure that INIT_ACCELERANT is executed just once (copies should be clones) */
121	if (si->accelerant_in_use)
122	{
123		result = B_NOT_ALLOWED;
124		goto error1;
125	}
126
127	/* call the device specific init code */
128	result = nv_general_powerup();
129
130	/* bail out if it failed */
131	if (result != B_OK)
132		goto error1;
133
134	/*
135	Now would be a good time to figure out what video modes your card supports.
136	We'll place the list of modes in another shared area so all of the copies
137	of the driver can see them.  The primary copy of the accelerant (ie the one
138	initialized with this routine) will own the "one true copy" of the list.
139	Everybody else get's a read-only clone.
140	*/
141	result = create_mode_list();
142	if (result != B_OK)
143		goto error1;
144
145	/*
146	Put the cursor at the start of the frame buffer.
147	Nvidia cursor is 32x32 16 color? takes up 4096 bytes of RAM.
148	*/
149	/* Initialize the rest of the cursor information while we're here */
150	si->cursor.width = 16;
151	si->cursor.height = 16;
152	si->cursor.hot_x = 0;
153	si->cursor.hot_y = 0;
154	si->cursor.x = 0;
155	si->cursor.y = 0;
156	si->cursor.dh_right = false;
157
158	/*
159	Put the frame buffer immediately following the cursor data. We store this
160	info in a frame_buffer_config structure to make it convienient to return
161	to the app_server later.
162	*/
163	pointer_reservation = 0;
164	/* Nvidia hardcursor needs 2kB space */
165	if (si->settings.hardcursor) pointer_reservation = 2048;
166
167	si->fbc.frame_buffer = (void *)((char *)si->framebuffer+pointer_reservation);
168	si->fbc.frame_buffer_dma = (void *)((char *)si->framebuffer_pci+pointer_reservation);
169
170	/* count of issued parameters or commands */
171	si->engine.last_idle = si->engine.count = 0;
172	/* no 3D clones are currently loaded */
173	si->engine.threeD.clones = 0;
174	/* tell 3D add-ons that they should reload their rendering states and surfaces */
175	si->engine.threeD.reload = 0xffffffff;
176	INIT_BEN(si->engine.lock);
177
178	INIT_BEN(si->overlay.lock);
179	for (cnt = 0; cnt < MAXBUFFERS; cnt++)
180	{
181		/* make sure overlay buffers are 'marked' as being free */
182		si->overlay.myBuffer[cnt].buffer = NULL;
183		si->overlay.myBuffer[cnt].buffer_dma = NULL;
184	}
185
186	/* make sure overlay unit is 'marked' as being free */
187	si->overlay.myToken = NULL;
188
189	/* note that overlay is not in use (for nv_bes_move_overlay()) */
190	si->overlay.active = false;
191
192	/* initialise various cursor stuff */
193	head1_cursor_init();
194	if (si->ps.secondary_head) head2_cursor_init();
195
196	/* ensure cursor state */
197	head1_cursor_hide();
198	if (si->ps.secondary_head) head2_cursor_hide();
199
200	/* ensure DPMS state */
201	si->dpms_flags = B_DPMS_ON;
202
203	/* ensure TVout state:
204	 * TVencoder is on head to be assigned primary, no dualhead switch mode active. */
205	//fixme: actually check on what CRTC TVout was active during boot (if any)...
206	si->dm.flags = TV_PRIMARY;
207
208	/* make sure a possible 3D add-on will block rendering and re-initialize itself.
209	 * note: update in _this_ order only */
210	/* SET_DISPLAY_MODE will reset this flag when it's done. */
211	si->engine.threeD.mode_changing = true;
212	/* every 3D add-on will reset this bit-flag when it's done. */
213	si->engine.threeD.newmode = 0xffffffff;
214
215	/* we did not detect the Haiku ScreenPrefs app yet */
216	si->haiku_prefs_used = false;
217	si->Haiku_switch_head = false;
218
219	/* a winner! */
220	result = B_OK;
221	/* ensure that INIT_ACCELERANT won't be executed again (copies should be clones) */
222	si->accelerant_in_use = true;
223	goto error0;
224
225error1:
226	/*
227	Initialization failed after init_common() succeeded, so we need to clean
228	up before quiting.
229	*/
230	uninit_common();
231
232error0:
233	return result;
234}
235
236/*
237Return the number of bytes required to hold the information required
238to clone the device.
239*/
240ssize_t ACCELERANT_CLONE_INFO_SIZE(void) {
241	/*
242	Since we're passing the name of the device as the only required
243	info, return the size of the name buffer
244	*/
245	return B_OS_NAME_LENGTH; // apsed, was MAX_NV_DEVICE_NAME_LENGTH;
246}
247
248
249/*
250Return the info required to clone the device.  void *data points to
251a buffer at least ACCELERANT_CLONE_INFO_SIZE() bytes in length.
252*/
253void GET_ACCELERANT_CLONE_INFO(void *data) {
254	nv_device_name dn;
255	status_t result;
256
257	/* call the kernel driver to get the device name */
258	dn.magic = NV_PRIVATE_DATA_MAGIC;
259	/* store the returned info directly into the passed buffer */
260	dn.name = (char *)data;
261	result = ioctl(fd, NV_DEVICE_NAME, &dn, sizeof(dn));
262}
263
264/*
265Initialize a copy of the accelerant as a clone.  void *data points to
266a copy of the data returned by GET_ACCELERANT_CLONE_INFO().
267*/
268status_t CLONE_ACCELERANT(void *data)
269{
270	status_t result;
271	char path[MAXPATHLEN];
272
273	/* the data is the device name */
274	/* Note: the R4 graphics driver kit is in error here (missing trailing '/') */
275	strcpy(path, "/dev/");
276	strcat(path, (const char *)data);
277	/* open the device, the permissions aren't important */
278	fd = open(path, B_READ_WRITE);
279	if (fd < 0)
280	{
281		/* we can't use LOG because we didn't get the shared_info struct.. */
282		char     fname[64];
283		FILE    *myhand = NULL;
284
285		sprintf (fname, "/boot/home/" DRIVER_PREFIX ".accelerant.0.log");
286		myhand=fopen(fname,"a+");
287		fprintf(myhand, "CLONE_ACCELERANT: couldn't open kerneldriver %s! Aborting.\n", path);
288		fclose(myhand);
289
290		/* abort with resultcode from open attempt on kerneldriver */
291		result = fd;
292		goto error0;
293	}
294
295	/* note that we're a clone accelerant */
296	accelerantIsClone = 1;
297
298	/* call the shared initialization code */
299	result = init_common(fd);
300
301	/* bail out if the common initialization failed */
302	if (result != B_OK) goto error1;
303
304	/* ensure that INIT_ACCELERANT is executed first (i.e. primary accelerant exists) */
305	if (!(si->accelerant_in_use))
306	{
307		result = B_NOT_ALLOWED;
308		goto error2;
309	}
310
311	/* setup CRTC and DAC functions access */
312	//fixme: setup_virtualized_heads is a problem for clones: needs to be run
313	//for each clone if the mode is changed!
314	if (si->ps.secondary_head)
315		setup_virtualized_heads(si->crtc_switch_mode);
316	else
317		setup_virtualized_heads(si->ps.crtc2_prim);
318
319	/* get shared area for display modes */
320	result = my_mode_list_area = clone_area(
321		DRIVER_PREFIX " cloned display_modes",
322		(void **)&my_mode_list,
323		B_ANY_ADDRESS,
324		B_READ_AREA,
325		si->mode_area
326	);
327	if (result < B_OK) goto error2;
328
329	/* all done */
330	LOG(4,("CLONE_ACCELERANT: cloning was succesfull.\n"));
331
332	result = B_OK;
333	goto error0;
334
335error2:
336	/* free up the areas we cloned */
337	uninit_common();
338error1:
339	/* close the device we opened */
340	close(fd);
341error0:
342	return result;
343}
344
345void UNINIT_ACCELERANT(void)
346{
347	if (accelerantIsClone)
348	{
349		LOG(4,("UNINIT_ACCELERANT: shutting down clone accelerant.\n"));
350	}
351	else
352	{
353		LOG(4,("UNINIT_ACCELERANT: shutting down primary accelerant.\n"));
354
355		/* delete benaphores ONLY if we are the primary accelerant */
356		DELETE_BEN(si->engine.lock);
357		DELETE_BEN(si->overlay.lock);
358
359		/* ensure that INIT_ACCELERANT can be executed again */
360		si->accelerant_in_use = false;
361	}
362
363	/* free our mode list area */
364	delete_area(my_mode_list_area);
365	/* paranoia */
366	my_mode_list = 0;
367	/* release our cloned data */
368	uninit_common();
369	/* close the file handle ONLY if we're the clone */
370	if (accelerantIsClone) close(fd);
371}
372