xref: /haiku/src/add-ons/kernel/drivers/graphics/virtio/virtio_gpu.cpp (revision b90dc7a4f3bc54e560dc944af0d4c62e849c52f7)
1 /*
2  * Copyright 2023, Jérôme Duval, jerome.duval@gmail.com.
3  * Distributed under the terms of the MIT License.
4  */
5 
6 
7 #include <new>
8 
9 #include <graphic_driver.h>
10 
11 #include <lock.h>
12 #include <virtio.h>
13 #include <virtio_info.h>
14 
15 #include <util/AutoLock.h>
16 
17 #include "viogpu.h"
18 
19 
20 #define VIRTIO_GPU_DRIVER_MODULE_NAME "drivers/graphics/virtio_gpu/driver_v1"
21 #define VIRTIO_GPU_DEVICE_MODULE_NAME "drivers/graphics/virtio_gpu/device_v1"
22 #define VIRTIO_GPU_DEVICE_ID_GENERATOR	"virtio_gpu/device_id"
23 
24 
25 typedef struct {
26 	device_node*			node;
27 	::virtio_device			virtio_device;
28 	virtio_device_interface*	virtio;
29 
30 	uint64 					features;
31 
32 	::virtio_queue			controlQueue;
33 	mutex					commandLock;
34 	area_id					commandArea;
35 	addr_t					commandBuffer;
36 	phys_addr_t				commandPhysAddr;
37 	sem_id					commandDone;
38 	uint64					fenceId;
39 
40 	::virtio_queue			cursorQueue;
41 
42 	int						displayResourceId;
43 	uint32					framebufferWidth;
44 	uint32					framebufferHeight;
45 	area_id					framebufferArea;
46 	addr_t					framebuffer;
47 	size_t					framebufferSize;
48 	uint32					displayWidth;
49 	uint32					displayHeight;
50 
51 	thread_id				updateThread;
52 	bool					updateThreadRunning;
53 
54 	area_id					sharedArea;
55 	virtio_gpu_shared_info* sharedInfo;
56 } virtio_gpu_driver_info;
57 
58 
59 typedef struct {
60 	virtio_gpu_driver_info*		info;
61 } virtio_gpu_handle;
62 
63 
64 #include <stdio.h>
65 #include <string.h>
66 #include <stdlib.h>
67 
68 #include <fs/devfs.h>
69 
70 #define ROUND_TO_PAGE_SIZE(x) (((x) + (B_PAGE_SIZE) - 1) & ~((B_PAGE_SIZE) - 1))
71 
72 
73 #define DEVICE_NAME				"virtio_gpu"
74 #define ACCELERANT_NAME	"virtio_gpu.accelerant"
75 //#define TRACE_VIRTIO_GPU
76 #ifdef TRACE_VIRTIO_GPU
77 #	define TRACE(x...) dprintf(DEVICE_NAME ": " x)
78 #else
79 #	define TRACE(x...) ;
80 #endif
81 #define ERROR(x...)			dprintf("\33[33m" DEVICE_NAME ":\33[0m " x)
82 #define CALLED() 			TRACE("CALLED %s\n", __PRETTY_FUNCTION__)
83 
84 
85 static device_manager_info* sDeviceManager;
86 
87 
88 static void virtio_gpu_vqwait(void* driverCookie, void* cookie);
89 
90 
91 const char*
get_feature_name(uint64 feature)92 get_feature_name(uint64 feature)
93 {
94 	switch (feature) {
95 		case VIRTIO_GPU_F_VIRGL:
96 			return "virgl";
97 		case VIRTIO_GPU_F_EDID:
98 			return "edid";
99 		case VIRTIO_GPU_F_RESOURCE_UUID:
100 			return "res_uuid";
101 		case VIRTIO_GPU_F_RESOURCE_BLOB:
102 			return "res_blob";
103 	}
104 	return NULL;
105 }
106 
107 
108 static status_t
virtio_gpu_drain_queues(virtio_gpu_driver_info * info)109 virtio_gpu_drain_queues(virtio_gpu_driver_info* info)
110 {
111 	while (info->virtio->queue_dequeue(info->controlQueue, NULL, NULL))
112 		;
113 
114 	while (info->virtio->queue_dequeue(info->cursorQueue, NULL, NULL))
115 		;
116 
117 	return B_OK;
118 }
119 
120 
121 status_t
virtio_gpu_send_cmd(virtio_gpu_driver_info * info,void * cmd,size_t cmdSize,void * response,size_t responseSize)122 virtio_gpu_send_cmd(virtio_gpu_driver_info* info, void *cmd, size_t cmdSize, void *response,
123     size_t responseSize)
124 {
125 	struct virtio_gpu_ctrl_hdr *hdr = (struct virtio_gpu_ctrl_hdr *)info->commandBuffer;
126 	struct virtio_gpu_ctrl_hdr *responseHdr = (struct virtio_gpu_ctrl_hdr *)response;
127 
128 	memcpy((void*)info->commandBuffer, cmd, cmdSize);
129 	memset((void*)(info->commandBuffer + cmdSize), 0, responseSize);
130 	hdr->flags |= VIRTIO_GPU_FLAG_FENCE;
131 	hdr->fence_id = ++info->fenceId;
132 
133 	physical_entry entries[] {
134 		{ info->commandPhysAddr, cmdSize },
135 		{ info->commandPhysAddr + cmdSize, responseSize },
136 	};
137 	if (!info->virtio->queue_is_empty(info->controlQueue))
138 		return B_ERROR;
139 
140 	status_t status = info->virtio->queue_request_v(info->controlQueue, entries, 1, 1, NULL);
141 	if (status != B_OK)
142 		return status;
143 
144 	acquire_sem(info->commandDone);
145 
146 	while (!info->virtio->queue_dequeue(info->controlQueue, NULL, NULL))
147 		spin(10);
148 
149 	memcpy(response, (void*)(info->commandBuffer + cmdSize), responseSize);
150 
151 	if (responseHdr->fence_id != info->fenceId) {
152 		ERROR("response fence id not right\n");
153 	}
154 	return B_OK;
155 }
156 
157 
158 status_t
virtio_gpu_get_display_info(virtio_gpu_driver_info * info)159 virtio_gpu_get_display_info(virtio_gpu_driver_info* info)
160 {
161 	CALLED();
162 	struct virtio_gpu_ctrl_hdr hdr = {};
163 	struct virtio_gpu_resp_display_info displayInfo = {};
164 
165 	hdr.type = VIRTIO_GPU_CMD_GET_DISPLAY_INFO;
166 
167 	virtio_gpu_send_cmd(info, &hdr, sizeof(hdr), &displayInfo, sizeof(displayInfo));
168 
169 	if (displayInfo.hdr.type != VIRTIO_GPU_RESP_OK_DISPLAY_INFO) {
170 		ERROR("failed getting display info\n");
171 		return B_ERROR;
172 	}
173 
174 	if (!displayInfo.pmodes[0].enabled) {
175 		ERROR("pmodes[0] is not enabled\n");
176 		return B_BAD_VALUE;
177 	}
178 
179 	info->displayWidth = displayInfo.pmodes[0].r.width;
180 	info->displayHeight = displayInfo.pmodes[0].r.height;
181 	TRACE("virtio_gpu_get_display_info width %" B_PRIu32 " height %" B_PRIu32 "\n",
182 		info->displayWidth, info->displayHeight);
183 
184 	return B_OK;
185 }
186 
187 
188 status_t
virtio_gpu_get_edids(virtio_gpu_driver_info * info,int scanout)189 virtio_gpu_get_edids(virtio_gpu_driver_info* info, int scanout)
190 {
191 	CALLED();
192 	struct virtio_gpu_cmd_get_edid getEdid = {};
193 	struct virtio_gpu_resp_edid response = {};
194 	getEdid.hdr.type = VIRTIO_GPU_CMD_GET_EDID;
195 	getEdid.scanout = scanout;
196 
197 	virtio_gpu_send_cmd(info, &getEdid, sizeof(getEdid), &response, sizeof(response));
198 
199 	if (response.hdr.type != VIRTIO_GPU_RESP_OK_EDID) {
200 		ERROR("failed getting edids %d\n", response.hdr.type);
201 		return B_ERROR;
202 	}
203 
204 	info->sharedInfo->has_edid = true;
205 	memcpy(&info->sharedInfo->edid_raw, response.edid, sizeof(edid1_raw));
206 	TRACE("virtio_gpu_get_edids success\n");
207 
208 	return B_OK;
209 }
210 
211 
212 status_t
virtio_gpu_create_2d(virtio_gpu_driver_info * info,int resourceId,int width,int height)213 virtio_gpu_create_2d(virtio_gpu_driver_info* info, int resourceId, int width, int height)
214 {
215 	CALLED();
216 	struct virtio_gpu_resource_create_2d resource = {};
217 	struct virtio_gpu_ctrl_hdr response = {};
218 
219 	resource.hdr.type = VIRTIO_GPU_CMD_RESOURCE_CREATE_2D;
220 	resource.resource_id = resourceId;
221 	resource.format = VIRTIO_GPU_FORMAT_B8G8R8X8_UNORM;
222 	resource.width = width;
223 	resource.height = height;
224 
225 	virtio_gpu_send_cmd(info, &resource, sizeof(resource), &response, sizeof(response));
226 
227 	if (response.type != VIRTIO_GPU_RESP_OK_NODATA) {
228 		ERROR("viogpu_create_2d: failed %d\n", response.type);
229 		return B_ERROR;
230 	}
231 
232 	return B_OK;
233 }
234 
235 
236 status_t
virtio_gpu_unref(virtio_gpu_driver_info * info,int resourceId)237 virtio_gpu_unref(virtio_gpu_driver_info* info, int resourceId)
238 {
239 	CALLED();
240 	struct virtio_gpu_resource_unref resource = {};
241 	struct virtio_gpu_ctrl_hdr response = {};
242 
243 	resource.hdr.type = VIRTIO_GPU_CMD_RESOURCE_UNREF;
244 	resource.resource_id = resourceId;
245 
246 	virtio_gpu_send_cmd(info, &resource, sizeof(resource), &response, sizeof(response));
247 
248 	if (response.type != VIRTIO_GPU_RESP_OK_NODATA) {
249 		ERROR("virtio_gpu_unref: failed %d\n", response.type);
250 		return B_ERROR;
251 	}
252 
253 	return B_OK;
254 }
255 
256 
257 status_t
virtio_gpu_attach_backing(virtio_gpu_driver_info * info,int resourceId)258 virtio_gpu_attach_backing(virtio_gpu_driver_info* info, int resourceId)
259 {
260 	CALLED();
261 	struct virtio_gpu_resource_attach_backing_entries {
262 		struct virtio_gpu_resource_attach_backing backing;
263 		struct virtio_gpu_mem_entry entries[16];
264 	} _PACKED backing = {};
265 	struct virtio_gpu_ctrl_hdr response = {};
266 
267 	physical_entry entries[16] = {};
268 	status_t status = get_memory_map((void*)info->framebuffer, info->framebufferSize, entries, 16);
269 	if (status != B_OK) {
270 		ERROR("virtio_gpu_attach_backing get_memory_map failed: %s\n", strerror(status));
271 		return status;
272 	}
273 
274 	backing.backing.hdr.type = VIRTIO_GPU_CMD_RESOURCE_ATTACH_BACKING;
275 	backing.backing.resource_id = resourceId;
276 	for (int i = 0; i < 16; i++) {
277 		if (entries[i].size == 0)
278 			break;
279 		TRACE("virtio_gpu_attach_backing %d %" B_PRIxPHYSADDR " %" B_PRIxPHYSADDR "\n", i,
280 			entries[i].address, entries[i].size);
281 		backing.entries[i].addr = entries[i].address;
282 		backing.entries[i].length = entries[i].size;
283 		backing.backing.nr_entries++;
284 	}
285 
286 	virtio_gpu_send_cmd(info, &backing, sizeof(backing), &response, sizeof(response));
287 
288 	if (response.type != VIRTIO_GPU_RESP_OK_NODATA) {
289 		ERROR("virtio_gpu_attach_backing failed: %d\n", response.type);
290 		return B_ERROR;
291 	}
292 
293 	return B_OK;
294 }
295 
296 
297 status_t
virtio_gpu_detach_backing(virtio_gpu_driver_info * info,int resourceId)298 virtio_gpu_detach_backing(virtio_gpu_driver_info* info, int resourceId)
299 {
300 	CALLED();
301 	struct virtio_gpu_resource_detach_backing backing;
302 	struct virtio_gpu_ctrl_hdr response = {};
303 
304 	backing.hdr.type = VIRTIO_GPU_CMD_RESOURCE_DETACH_BACKING;
305 	backing.resource_id = resourceId;
306 
307 	virtio_gpu_send_cmd(info, &backing, sizeof(backing), &response, sizeof(response));
308 
309 	if (response.type != VIRTIO_GPU_RESP_OK_NODATA) {
310 		ERROR("virtio_gpu_detach_backing failed: %d\n", response.type);
311 		return B_ERROR;
312 	}
313 
314 	return B_OK;
315 }
316 
317 
318 status_t
virtio_gpu_set_scanout(virtio_gpu_driver_info * info,int scanoutId,int resourceId,uint32 width,uint32 height)319 virtio_gpu_set_scanout(virtio_gpu_driver_info* info, int scanoutId, int resourceId,
320     uint32 width, uint32 height)
321 {
322 	CALLED();
323 	struct virtio_gpu_set_scanout set_scanout = {};
324 	struct virtio_gpu_ctrl_hdr response = {};
325 
326 	set_scanout.hdr.type = VIRTIO_GPU_CMD_SET_SCANOUT;
327 	set_scanout.scanout_id = scanoutId;
328 	set_scanout.resource_id = resourceId;
329 	set_scanout.r.width = width;
330 	set_scanout.r.height = height;
331 
332 	virtio_gpu_send_cmd(info, &set_scanout, sizeof(set_scanout), &response, sizeof(response));
333 
334 	if (response.type != VIRTIO_GPU_RESP_OK_NODATA) {
335 		ERROR("virtio_gpu_set_scanout failed %d\n", response.type);
336 		return B_ERROR;
337 	}
338 
339 	return B_OK;
340 }
341 
342 
343 status_t
virtio_gpu_transfer_to_host_2d(virtio_gpu_driver_info * info,int resourceId,uint32 width,uint32 height)344 virtio_gpu_transfer_to_host_2d(virtio_gpu_driver_info* info, int resourceId,
345     uint32 width, uint32 height)
346 {
347 	struct virtio_gpu_transfer_to_host_2d transferToHost = {};
348 	struct virtio_gpu_ctrl_hdr response = {};
349 
350 	transferToHost.hdr.type = VIRTIO_GPU_CMD_TRANSFER_TO_HOST_2D;
351 	transferToHost.resource_id = resourceId;
352 	transferToHost.r.width = width;
353 	transferToHost.r.height = height;
354 
355 	virtio_gpu_send_cmd(info, &transferToHost, sizeof(transferToHost), &response,
356 		sizeof(response));
357 
358 	if (response.type != VIRTIO_GPU_RESP_OK_NODATA) {
359 		ERROR("virtio_gpu_transfer_to_host_2d failed %d\n", response.type);
360 		return B_ERROR;
361 	}
362 
363 	return B_OK;
364 }
365 
366 
367 status_t
virtio_gpu_flush_resource(virtio_gpu_driver_info * info,int resourceId,uint32 width,uint32 height)368 virtio_gpu_flush_resource(virtio_gpu_driver_info* info, int resourceId, uint32 width,
369     uint32 height)
370 {
371 	struct virtio_gpu_resource_flush resourceFlush = {};
372 	struct virtio_gpu_ctrl_hdr response = {};
373 
374 	resourceFlush.hdr.type = VIRTIO_GPU_CMD_RESOURCE_FLUSH;
375 	resourceFlush.resource_id = resourceId;
376 	resourceFlush.r.width = width;
377 	resourceFlush.r.height = height;
378 
379 	virtio_gpu_send_cmd(info, &resourceFlush, sizeof(resourceFlush), &response, sizeof(response));
380 
381 	if (response.type != VIRTIO_GPU_RESP_OK_NODATA) {
382 		ERROR("virtio_gpu_flush_resource failed %d\n", response.type);
383 		return B_ERROR;
384 	}
385 
386 	return B_OK;
387 }
388 
389 
390 status_t
virtio_update_thread(void * arg)391 virtio_update_thread(void *arg)
392 {
393 	virtio_gpu_driver_info* info = (virtio_gpu_driver_info*)arg;
394 
395 	while (info->updateThreadRunning) {
396 		bigtime_t start = system_time();
397 		MutexLocker commandLocker(&info->commandLock);
398 		virtio_gpu_transfer_to_host_2d(info, info->displayResourceId, info->displayWidth,
399 			info->displayHeight);
400 		virtio_gpu_flush_resource(info, info->displayResourceId, info->displayWidth, info->displayHeight);
401 		bigtime_t delay = system_time() - start;
402 		if (delay < 20000)
403 			snooze(20000 - delay);
404 	}
405 	return B_OK;
406 }
407 
408 
409 status_t
virtio_gpu_set_display_mode(virtio_gpu_driver_info * info,display_mode * mode)410 virtio_gpu_set_display_mode(virtio_gpu_driver_info* info, display_mode *mode)
411 {
412 	CALLED();
413 
414 	int newResourceId = info->displayResourceId + 1;
415 
416 	// create framebuffer area
417 	TRACE("virtio_gpu_set_display_mode %" B_PRIu16 " %" B_PRIu16 "\n", mode->virtual_width,
418 		mode->virtual_height);
419 
420 	status_t status = virtio_gpu_create_2d(info, newResourceId, mode->virtual_width, mode->virtual_height);
421 	if (status != B_OK)
422 		return status;
423 
424 	status = virtio_gpu_attach_backing(info, newResourceId);
425 	if (status != B_OK)
426 		return status;
427 
428 	status = virtio_gpu_unref(info, info->displayResourceId);
429 	if (status != B_OK)
430 		return status;
431 
432 	info->displayResourceId = newResourceId;
433 	info->displayWidth = mode->virtual_width;
434 	info->displayHeight = mode->virtual_height;
435 
436 	status = virtio_gpu_set_scanout(info, 0, 0, 0, 0);
437 	if (status != B_OK)
438 		return status;
439 
440 	status = virtio_gpu_set_scanout(info, 0, info->displayResourceId, info->displayWidth, info->displayHeight);
441 	if (status != B_OK)
442 		return status;
443 
444 	status = virtio_gpu_transfer_to_host_2d(info, info->displayResourceId, info->displayWidth, info->displayHeight);
445 	if (status != B_OK)
446 		return status;
447 
448 	status = virtio_gpu_flush_resource(info, info->displayResourceId, info->displayWidth, info->displayHeight);
449 	if (status != B_OK)
450 		return status;
451 
452 	{
453 		virtio_gpu_shared_info& sharedInfo = *info->sharedInfo;
454 		sharedInfo.frame_buffer_area = info->framebufferArea;
455 		sharedInfo.frame_buffer = (uint8*)info->framebuffer;
456 		sharedInfo.bytes_per_row = info->displayWidth * 4;
457 		sharedInfo.current_mode.virtual_width = info->displayWidth;
458 		sharedInfo.current_mode.virtual_height = info->displayHeight;
459 		sharedInfo.current_mode.space = B_RGB32;
460 	}
461 
462 	return B_OK;
463 }
464 
465 
466 //	#pragma mark - device module API
467 
468 
469 static status_t
virtio_gpu_init_device(void * _info,void ** _cookie)470 virtio_gpu_init_device(void* _info, void** _cookie)
471 {
472 	CALLED();
473 	virtio_gpu_driver_info* info = (virtio_gpu_driver_info*)_info;
474 
475 	device_node* parent = sDeviceManager->get_parent_node(info->node);
476 	sDeviceManager->get_driver(parent, (driver_module_info**)&info->virtio,
477 		(void**)&info->virtio_device);
478 	sDeviceManager->put_node(parent);
479 
480 	info->virtio->negotiate_features(info->virtio_device, VIRTIO_GPU_F_EDID,
481 		 &info->features, &get_feature_name);
482 
483 	// TODO read config
484 
485 	// Setup queues
486 	::virtio_queue virtioQueues[2];
487 	status_t status = info->virtio->alloc_queues(info->virtio_device, 2,
488 		virtioQueues, NULL);
489 	if (status != B_OK) {
490 		ERROR("queue allocation failed (%s)\n", strerror(status));
491 		return status;
492 	}
493 
494 	info->controlQueue = virtioQueues[0];
495 	info->cursorQueue = virtioQueues[1];
496 
497 	// create command buffer area
498 	info->commandArea = create_area("virtiogpu command buffer", (void**)&info->commandBuffer,
499 		B_ANY_KERNEL_BLOCK_ADDRESS, B_PAGE_SIZE,
500 		B_FULL_LOCK, B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA);
501 	if (info->commandArea < B_OK) {
502 		status = info->commandArea;
503 		goto err1;
504 	}
505 
506 	physical_entry entry;
507 	status = get_memory_map((void*)info->commandBuffer, B_PAGE_SIZE, &entry, 1);
508 	if (status != B_OK)
509 		goto err2;
510 
511 	info->commandPhysAddr = entry.address;
512 	mutex_init(&info->commandLock, "virtiogpu command lock");
513 
514 	// Setup interrupt
515 	status = info->virtio->setup_interrupt(info->virtio_device, NULL, info);
516 	if (status != B_OK) {
517 		ERROR("interrupt setup failed (%s)\n", strerror(status));
518 		goto err3;
519 	}
520 
521 	status = info->virtio->queue_setup_interrupt(info->controlQueue,
522 		virtio_gpu_vqwait, info);
523 	if (status != B_OK) {
524 		ERROR("queue interrupt setup failed (%s)\n", strerror(status));
525 		goto err3;
526 	}
527 
528 	*_cookie = info;
529 	return B_OK;
530 
531 err3:
532 err2:
533 	delete_area(info->commandArea);
534 err1:
535 	return status;
536 }
537 
538 
539 static void
virtio_gpu_uninit_device(void * _cookie)540 virtio_gpu_uninit_device(void* _cookie)
541 {
542 	CALLED();
543 	virtio_gpu_driver_info* info = (virtio_gpu_driver_info*)_cookie;
544 
545 	info->virtio->free_interrupts(info->virtio_device);
546 
547 	mutex_destroy(&info->commandLock);
548 
549 	delete_area(info->commandArea);
550 	info->commandArea = -1;
551 	info->virtio->free_queues(info->virtio_device);
552 }
553 
554 
555 static status_t
virtio_gpu_open(void * _info,const char * path,int openMode,void ** _cookie)556 virtio_gpu_open(void* _info, const char* path, int openMode, void** _cookie)
557 {
558 	CALLED();
559 	virtio_gpu_driver_info* info = (virtio_gpu_driver_info*)_info;
560 	status_t status;
561 	size_t sharedSize = (sizeof(virtio_gpu_shared_info) + 7) & ~7;
562 	MutexLocker commandLocker;
563 
564 	virtio_gpu_handle* handle = (virtio_gpu_handle*)malloc(
565 		sizeof(virtio_gpu_handle));
566 	if (handle == NULL)
567 		return B_NO_MEMORY;
568 
569 	info->commandDone = create_sem(1, "virtio_gpu_command");
570 	if (info->commandDone < B_OK)
571 		goto error;
572 
573 	info->sharedArea = create_area("virtio_gpu shared info",
574 		(void**)&info->sharedInfo, B_ANY_KERNEL_ADDRESS,
575 		ROUND_TO_PAGE_SIZE(sharedSize), B_FULL_LOCK,
576 		B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA | B_CLONEABLE_AREA);
577 	if (info->sharedArea < 0)
578 		goto error;
579 	memset(info->sharedInfo, 0, sizeof(virtio_gpu_shared_info));
580 
581 	commandLocker.SetTo(&info->commandLock, false, true);
582 
583 	status = virtio_gpu_get_display_info(info);
584 	if (status != B_OK)
585 		goto error;
586 
587 	if ((info->features & VIRTIO_GPU_F_EDID) != 0)
588 		virtio_gpu_get_edids(info, 0);
589 
590 	// so we can fit every mode
591 	info->framebufferWidth = 3840;
592 	info->framebufferHeight = 2160;
593 
594 	// create framebuffer area
595 	info->framebufferSize = 4 * info->framebufferWidth * info->framebufferHeight;
596 	info->framebufferArea = create_area("virtio_gpu framebuffer", (void**)&info->framebuffer,
597 		B_ANY_KERNEL_ADDRESS, info->framebufferSize,
598 		B_FULL_LOCK | B_CONTIGUOUS, B_READ_AREA | B_WRITE_AREA);
599 	if (info->framebufferArea < B_OK) {
600 		status = info->framebufferArea;
601 		goto error;
602 	}
603 
604 	info->displayResourceId = 1;
605 	status = virtio_gpu_create_2d(info, info->displayResourceId, info->displayWidth,
606 		info->displayHeight);
607 	if (status != B_OK)
608 		goto error;
609 
610 	status = virtio_gpu_attach_backing(info, info->displayResourceId);
611 	if (status != B_OK)
612 		goto error;
613 
614 	status = virtio_gpu_set_scanout(info, 0, info->displayResourceId, info->displayWidth,
615 		info->displayHeight);
616 	if (status != B_OK)
617 		goto error;
618 
619 	{
620 		virtio_gpu_shared_info& sharedInfo = *info->sharedInfo;
621 		sharedInfo.frame_buffer_area = info->framebufferArea;
622 		sharedInfo.frame_buffer = (uint8*)info->framebuffer;
623 		sharedInfo.bytes_per_row = info->displayWidth * 4;
624 		sharedInfo.current_mode.virtual_width = info->displayWidth;
625 		sharedInfo.current_mode.virtual_height = info->displayHeight;
626 		sharedInfo.current_mode.space = B_RGB32;
627 	}
628 	info->updateThreadRunning = true;
629 	info->updateThread = spawn_kernel_thread(virtio_update_thread, "virtio_gpu update",
630 		B_DISPLAY_PRIORITY, info);
631 	if (info->updateThread < B_OK)
632 		goto error;
633 	resume_thread(info->updateThread);
634 
635 	handle->info = info;
636 
637 	*_cookie = handle;
638 	return B_OK;
639 
640 error:
641 	delete_area(info->framebufferArea);
642 	info->framebufferArea = -1;
643 	delete_sem(info->commandDone);
644 	info->commandDone = -1;
645 	free(handle);
646 	return B_ERROR;
647 }
648 
649 
650 static status_t
virtio_gpu_close(void * cookie)651 virtio_gpu_close(void* cookie)
652 {
653 	virtio_gpu_handle* handle = (virtio_gpu_handle*)cookie;
654 	CALLED();
655 
656 	virtio_gpu_driver_info* info = handle->info;
657 	info->updateThreadRunning = false;
658 	delete_sem(info->commandDone);
659 	info->commandDone  = -1;
660 
661 	return B_OK;
662 }
663 
664 
665 static status_t
virtio_gpu_free(void * cookie)666 virtio_gpu_free(void* cookie)
667 {
668 	CALLED();
669 	virtio_gpu_handle* handle = (virtio_gpu_handle*)cookie;
670 
671 	virtio_gpu_driver_info* info = handle->info;
672 	int32 result;
673 	wait_for_thread(info->updateThread, &result);
674 	info->updateThread = -1;
675 	virtio_gpu_drain_queues(info);
676 	free(handle);
677 	return B_OK;
678 }
679 
680 
681 static void
virtio_gpu_vqwait(void * driverCookie,void * cookie)682 virtio_gpu_vqwait(void* driverCookie, void* cookie)
683 {
684 	CALLED();
685 	virtio_gpu_driver_info* info = (virtio_gpu_driver_info*)cookie;
686 
687 	release_sem_etc(info->commandDone, 1, B_DO_NOT_RESCHEDULE);
688 }
689 
690 
691 static status_t
virtio_gpu_read(void * cookie,off_t pos,void * buffer,size_t * _length)692 virtio_gpu_read(void* cookie, off_t pos, void* buffer, size_t* _length)
693 {
694 	*_length = 0;
695 	return B_NOT_ALLOWED;
696 }
697 
698 
699 static status_t
virtio_gpu_write(void * cookie,off_t pos,const void * buffer,size_t * _length)700 virtio_gpu_write(void* cookie, off_t pos, const void* buffer,
701 	size_t* _length)
702 {
703 	*_length = 0;
704 	return B_NOT_ALLOWED;
705 }
706 
707 
708 static status_t
virtio_gpu_ioctl(void * cookie,uint32 op,void * buffer,size_t length)709 virtio_gpu_ioctl(void* cookie, uint32 op, void* buffer, size_t length)
710 {
711 	CALLED();
712 	virtio_gpu_handle* handle = (virtio_gpu_handle*)cookie;
713 	virtio_gpu_driver_info* info = handle->info;
714 
715 	// TRACE("ioctl(op = %lx)\n", op);
716 
717 	switch (op) {
718 		case B_GET_ACCELERANT_SIGNATURE:
719 			dprintf(DEVICE_NAME ": acc: %s\n", ACCELERANT_NAME);
720 			if (user_strlcpy((char*)buffer, ACCELERANT_NAME,
721 					B_FILE_NAME_LENGTH) < B_OK)
722 				return B_BAD_ADDRESS;
723 
724 			return B_OK;
725 
726 		// needed to share data between kernel and accelerant
727 		case VIRTIO_GPU_GET_PRIVATE_DATA:
728 			return user_memcpy(buffer, &info->sharedArea, sizeof(area_id));
729 
730 		case VIRTIO_GPU_SET_DISPLAY_MODE:
731 		{
732 			if (length != sizeof(display_mode))
733 				return B_BAD_VALUE;
734 
735 			display_mode mode;
736 			if (user_memcpy(&mode, buffer, sizeof(display_mode)) != B_OK)
737 				return B_BAD_ADDRESS;
738 
739 			MutexLocker commandLocker(&info->commandLock);
740 
741 			return virtio_gpu_set_display_mode(info, &mode);
742 		}
743 		default:
744 			ERROR("ioctl: unknown message %" B_PRIx32 "\n", op);
745 			break;
746 	}
747 
748 
749 	return B_DEV_INVALID_IOCTL;
750 }
751 
752 
753 //	#pragma mark - driver module API
754 
755 
756 static float
virtio_gpu_supports_device(device_node * parent)757 virtio_gpu_supports_device(device_node* parent)
758 {
759 	CALLED();
760 	const char* bus;
761 	uint16 deviceType;
762 
763 	// make sure parent is really the Virtio bus manager
764 	if (sDeviceManager->get_attr_string(parent, B_DEVICE_BUS, &bus, false))
765 		return -1;
766 
767 	if (strcmp(bus, "virtio"))
768 		return 0.0;
769 
770 	// check whether it's really a Virtio GPU device
771 	if (sDeviceManager->get_attr_uint16(parent, VIRTIO_DEVICE_TYPE_ITEM,
772 			&deviceType, true) != B_OK || deviceType != VIRTIO_DEVICE_ID_GPU)
773 		return 0.0;
774 
775 	TRACE("Virtio gpu device found!\n");
776 
777 	return 0.6;
778 }
779 
780 
781 static status_t
virtio_gpu_register_device(device_node * node)782 virtio_gpu_register_device(device_node* node)
783 {
784 	CALLED();
785 
786 	device_attr attrs[] = {
787 		{ B_DEVICE_PRETTY_NAME, B_STRING_TYPE, {.string = "Virtio GPU"} },
788 		{ NULL }
789 	};
790 
791 	return sDeviceManager->register_node(node, VIRTIO_GPU_DRIVER_MODULE_NAME,
792 		attrs, NULL, NULL);
793 }
794 
795 
796 static status_t
virtio_gpu_init_driver(device_node * node,void ** cookie)797 virtio_gpu_init_driver(device_node* node, void** cookie)
798 {
799 	CALLED();
800 
801 	virtio_gpu_driver_info* info = (virtio_gpu_driver_info*)malloc(
802 		sizeof(virtio_gpu_driver_info));
803 	if (info == NULL)
804 		return B_NO_MEMORY;
805 
806 	memset(info, 0, sizeof(*info));
807 
808 	info->node = node;
809 
810 	*cookie = info;
811 	return B_OK;
812 }
813 
814 
815 static void
virtio_gpu_uninit_driver(void * _cookie)816 virtio_gpu_uninit_driver(void* _cookie)
817 {
818 	CALLED();
819 	virtio_gpu_driver_info* info = (virtio_gpu_driver_info*)_cookie;
820 	free(info);
821 }
822 
823 
824 static status_t
virtio_gpu_register_child_devices(void * _cookie)825 virtio_gpu_register_child_devices(void* _cookie)
826 {
827 	CALLED();
828 	virtio_gpu_driver_info* info = (virtio_gpu_driver_info*)_cookie;
829 	status_t status;
830 
831 	int32 id = sDeviceManager->create_id(VIRTIO_GPU_DEVICE_ID_GENERATOR);
832 	if (id < 0)
833 		return id;
834 
835 	char name[64];
836 	snprintf(name, sizeof(name), "graphics/virtio/%" B_PRId32,
837 		id);
838 
839 	status = sDeviceManager->publish_device(info->node, name,
840 		VIRTIO_GPU_DEVICE_MODULE_NAME);
841 
842 	return status;
843 }
844 
845 
846 //	#pragma mark -
847 
848 
849 module_dependency module_dependencies[] = {
850 	{B_DEVICE_MANAGER_MODULE_NAME, (module_info**)&sDeviceManager},
851 	{}
852 };
853 
854 struct device_module_info sVirtioGpuDevice = {
855 	{
856 		VIRTIO_GPU_DEVICE_MODULE_NAME,
857 		0,
858 		NULL
859 	},
860 
861 	virtio_gpu_init_device,
862 	virtio_gpu_uninit_device,
863 	NULL, // remove,
864 
865 	virtio_gpu_open,
866 	virtio_gpu_close,
867 	virtio_gpu_free,
868 	virtio_gpu_read,
869 	virtio_gpu_write,
870 	NULL,	// io
871 	virtio_gpu_ioctl,
872 
873 	NULL,	// select
874 	NULL,	// deselect
875 };
876 
877 struct driver_module_info sVirtioGpuDriver = {
878 	{
879 		VIRTIO_GPU_DRIVER_MODULE_NAME,
880 		0,
881 		NULL
882 	},
883 
884 	virtio_gpu_supports_device,
885 	virtio_gpu_register_device,
886 	virtio_gpu_init_driver,
887 	virtio_gpu_uninit_driver,
888 	virtio_gpu_register_child_devices,
889 	NULL,	// rescan
890 	NULL,	// removed
891 };
892 
893 module_info* modules[] = {
894 	(module_info*)&sVirtioGpuDriver,
895 	(module_info*)&sVirtioGpuDevice,
896 	NULL
897 };
898