1 /*
2 * Copyright (c) 2020-2021 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15 /*
16 * Simple virtio-mmio gpu driver, without hardware accelarator.
17 * Using only synchronous request/response, no IRQ.
18 */
19
20 #include "osal.h"
21 #include "osal_io.h"
22 #include "hdf_device_desc.h"
23 #include "fb.h"
24 #include "los_vm_phys.h"
25 #include "los_vm_iomap.h"
26 #include "virtmmio.h"
27
28 #define VIRTIO_GPU_F_EDID (1 << 1)
29
30 #define VIRTQ_CONTROL_QSZ 4
31 #define VIRTQ_CURSOR_QSZ 2
32 #define NORMAL_CMD_ENTRIES 2
33
34 #define FB_WIDTH_DFT 800
35 #define FB_HEIGHT_DFT 480
36 #define GPU_DFT_RATE (1000 / 30) /* ms, 30Hz */
37 #define PIXEL_BYTES 4
38
39 #define RESOURCEID_FB 1
40
41 enum VirtgpuCtrlType {
42 /* 2d commands */
43 VIRTIO_GPU_CMD_GET_DISPLAY_INFO = 0x0100,
44 VIRTIO_GPU_CMD_RESOURCE_CREATE_2D,
45 VIRTIO_GPU_CMD_RESOURCE_UNREF,
46 VIRTIO_GPU_CMD_SET_SCANOUT,
47 VIRTIO_GPU_CMD_RESOURCE_FLUSH,
48 VIRTIO_GPU_CMD_TRANSFER_TO_HOST_2D,
49 VIRTIO_GPU_CMD_RESOURCE_ATTACH_BACKING,
50 VIRTIO_GPU_CMD_RESOURCE_DETACH_BACKING,
51 VIRTIO_GPU_CMD_GET_CAPSET_INFO,
52 VIRTIO_GPU_CMD_GET_CAPSET,
53 VIRTIO_GPU_CMD_GET_EDID,
54 /* cursor commands */
55 VIRTIO_GPU_CMD_UPDATE_CURSOR = 0x0300,
56 VIRTIO_GPU_CMD_MOVE_CURSOR,
57 /* success responses */
58 VIRTIO_GPU_RESP_OK_NODATA = 0x1100,
59 VIRTIO_GPU_RESP_OK_DISPLAY_INFO,
60 VIRTIO_GPU_RESP_OK_CAPSET_INFO,
61 VIRTIO_GPU_RESP_OK_CAPSET,
62 VIRTIO_GPU_RESP_OK_EDID,
63 /* error responses */
64 VIRTIO_GPU_RESP_ERR_UNSPEC = 0x1200,
65 VIRTIO_GPU_RESP_ERR_OUT_OF_MEMORY,
66 VIRTIO_GPU_RESP_ERR_INVALID_SCANOUT_ID,
67 VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID,
68 VIRTIO_GPU_RESP_ERR_INVALID_CONTEXT_ID,
69 VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER,
70 };
71
72 enum VirtgpuFormats {
73 VIRTIO_GPU_FORMAT_B8G8R8A8_UNORM = 1,
74 VIRTIO_GPU_FORMAT_B8G8R8X8_UNORM,
75 VIRTIO_GPU_FORMAT_A8R8G8B8_UNORM,
76 VIRTIO_GPU_FORMAT_X8R8G8B8_UNORM,
77
78 VIRTIO_GPU_FORMAT_R8G8B8A8_UNORM = 67,
79 VIRTIO_GPU_FORMAT_X8B8G8R8_UNORM,
80
81 VIRTIO_GPU_FORMAT_A8B8G8R8_UNORM = 121,
82 VIRTIO_GPU_FORMAT_R8G8B8X8_UNORM = 134,
83 };
84
85 struct VirtgpuCtrlHdr {
86 uint32_t type;
87 #define VIRTIO_GPU_FLAG_FENCE (1 << 0)
88 uint32_t flags;
89 uint64_t fenceId;
90 uint32_t ctxId;
91 uint32_t padding;
92 };
93
94 struct VirtgpuRect {
95 uint32_t x;
96 uint32_t y;
97 uint32_t width;
98 uint32_t height;
99 };
100
101 struct VirtgpuResourceFlush {
102 struct VirtgpuCtrlHdr hdr;
103 struct VirtgpuRect r;
104 uint32_t resourceId;
105 uint32_t padding;
106 };
107
108 struct VirtgpuTransferToHost2D {
109 struct VirtgpuCtrlHdr hdr;
110 struct VirtgpuRect r;
111 uint64_t offset;
112 uint32_t resourceId;
113 uint32_t padding;
114 };
115
116 struct Virtgpu {
117 struct VirtmmioDev dev;
118 OSAL_DECLARE_TIMER(timer); /* refresh timer */
119
120 struct VirtgpuRect screen;
121 uint8_t *fb; /* frame buffer */
122 bool edid;
123
124 /*
125 * Normal operations(timer refresh) request/response buffers.
126 * We do not wait for their completion, so they must be static memory.
127 * When an operation happened, the last one must already done.
128 * Response is shared and ignored.
129 *
130 * control queue 4 descs: 0-trans_req 1-trans_resp 2-flush_req 3-flush_resp
131 * 0-... (30Hz is enough to avoid override)
132 */
133 struct VirtgpuResourceFlush flushReq;
134 struct VirtgpuTransferToHost2D transReq;
135 struct VirtgpuCtrlHdr resp;
136 };
137 static struct Virtgpu *g_virtGpu; /* fb module need this data, using global for simplicity */
138
ErrString(int err)139 static const char *ErrString(int err)
140 {
141 switch (err) {
142 case VIRTIO_GPU_RESP_ERR_UNSPEC: return "unspec";
143 case VIRTIO_GPU_RESP_ERR_OUT_OF_MEMORY: return "out of memory";
144 case VIRTIO_GPU_RESP_ERR_INVALID_SCANOUT_ID: return "invalid scanout ID";
145 case VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID: return "invalid resource ID";
146 case VIRTIO_GPU_RESP_ERR_INVALID_CONTEXT_ID: return "invalid context ID";
147 case VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER: return "invalid parameter";
148 default: break;
149 }
150 return "unknown error";
151 }
152
Feature0(uint32_t features,uint32_t * supported,void * dev)153 static bool Feature0(uint32_t features, uint32_t *supported, void *dev)
154 {
155 struct Virtgpu *gpu = dev;
156
157 if (features & VIRTIO_GPU_F_EDID) {
158 *supported |= VIRTIO_GPU_F_EDID;
159 gpu->edid = true;
160 }
161
162 return true;
163 }
164
Feature1(uint32_t features,uint32_t * supported,void * dev)165 static bool Feature1(uint32_t features, uint32_t *supported, void *dev)
166 {
167 (void)dev;
168 if (features & VIRTIO_F_VERSION_1) {
169 *supported |= VIRTIO_F_VERSION_1;
170 } else {
171 HDF_LOGE("[%s]virtio-gpu has no VERSION_1 feature", __func__);
172 return false;
173 }
174
175 return true;
176 }
177
NotifyAndWaitResponse(unsigned queue,struct Virtq * q,const void * req,volatile void * resp)178 static bool NotifyAndWaitResponse(unsigned queue, struct Virtq *q, const void *req, volatile void *resp)
179 {
180 const struct VirtgpuCtrlHdr *a = req;
181 volatile struct VirtgpuCtrlHdr *b = resp;
182
183 /* always use desc[0] [1] ([2]) for request-wait-response */
184 q->avail->ring[q->avail->index % q->qsz] = 0;
185 DSB;
186 q->avail->index++;
187 OSAL_WRITEL(queue, g_virtGpu->dev.base + VIRTMMIO_REG_QUEUENOTIFY);
188
189 /* spin for response */
190 while ((q->last == q->used->index) ||
191 ((a->flags == VIRTIO_GPU_FLAG_FENCE) && (a->fenceId != b->fenceId))) {
192 DSB;
193 }
194 q->last++;
195
196 if ((b->type < VIRTIO_GPU_RESP_OK_NODATA) || (b->type > VIRTIO_GPU_RESP_OK_EDID)) {
197 HDF_LOGE("[%s]virtio-gpu command=0x%x error=0x%x: %s", __func__, a->type, b->type, ErrString(b->type));
198 return false;
199 }
200
201 return true;
202 }
203
RequestResponse(unsigned queue,const void * req,size_t reqSize,volatile void * resp,size_t respSize)204 static bool RequestResponse(unsigned queue, const void *req, size_t reqSize, volatile void *resp, size_t respSize)
205 {
206 struct Virtq *q = &g_virtGpu->dev.vq[queue];
207 uint16_t idx = 0;
208
209 /* NOTE: We need these data physical continuous. They came from kernel stack, so they must. */
210 q->desc[idx].pAddr = VMM_TO_DMA_ADDR((VADDR_T)req);
211 q->desc[idx].len = reqSize;
212 q->desc[idx].flag = VIRTQ_DESC_F_NEXT;
213 q->desc[idx].next = idx + 1;
214 idx++;
215 q->desc[idx].pAddr = VMM_TO_DMA_ADDR((VADDR_T)resp);
216 q->desc[idx].len = respSize;
217 q->desc[idx].flag = VIRTQ_DESC_F_WRITE;
218
219 return NotifyAndWaitResponse(queue, q, req, resp);
220 }
221
RequestDataResponse(const void * req,size_t reqSize,const void * data,size_t dataSize,volatile void * resp,size_t respSize)222 static bool RequestDataResponse(const void *req, size_t reqSize, const void *data,
223 size_t dataSize, volatile void *resp, size_t respSize)
224 {
225 struct Virtq *q = &g_virtGpu->dev.vq[0];
226 uint16_t idx = 0;
227
228 q->desc[idx].pAddr = VMM_TO_DMA_ADDR((VADDR_T)req);
229 q->desc[idx].len = reqSize;
230 q->desc[idx].flag = VIRTQ_DESC_F_NEXT;
231 q->desc[idx].next = idx + 1;
232 idx++;
233 q->desc[idx].pAddr = VMM_TO_DMA_ADDR((VADDR_T)data);
234 q->desc[idx].len = dataSize;
235 q->desc[idx].flag = VIRTQ_DESC_F_NEXT;
236 q->desc[idx].next = idx + 1;
237 idx++;
238 q->desc[idx].pAddr = VMM_TO_DMA_ADDR((VADDR_T)resp);
239 q->desc[idx].len = respSize;
240 q->desc[idx].flag = VIRTQ_DESC_F_WRITE;
241
242 return NotifyAndWaitResponse(0, q, req, resp);
243 }
244
245 /* For normal display refresh, do not wait response */
RequestNoResponse(unsigned queue,const void * req,size_t reqSize,bool notify)246 static void RequestNoResponse(unsigned queue, const void *req, size_t reqSize, bool notify)
247 {
248 struct Virtq *q = &g_virtGpu->dev.vq[queue];
249 uint16_t head = q->last % q->qsz; /* `last` record next writable desc entry for request */
250
251 /* QEMU is busy for the full queue, give up this request */
252 if (abs(q->avail->index - (volatile uint16_t)q->used->index) >= VIRTQ_CONTROL_QSZ) {
253 return;
254 }
255
256 /* other fields initiated by PopulateVirtQ */
257 q->desc[head].pAddr = VMM_TO_DMA_ADDR((VADDR_T)req);
258 q->desc[head].len = reqSize;
259 q->last += NORMAL_CMD_ENTRIES;
260
261 q->avail->ring[q->avail->index % q->qsz] = head;
262 DSB;
263 q->avail->index++;
264
265 if (notify) {
266 OSAL_WRITEL(queue, g_virtGpu->dev.base + VIRTMMIO_REG_QUEUENOTIFY);
267 }
268 }
269
270 #define VIRTIO_GPU_MAX_SCANOUTS 16
271 struct VirtgpuRespDisplayInfo {
272 struct VirtgpuCtrlHdr hdr;
273 struct {
274 struct VirtgpuRect r;
275 uint32_t enabled;
276 uint32_t flags;
277 } pmodes[VIRTIO_GPU_MAX_SCANOUTS];
278 };
CMDGetDisplayInfo(void)279 static void CMDGetDisplayInfo(void)
280 {
281 struct VirtgpuCtrlHdr req = {
282 .type = VIRTIO_GPU_CMD_GET_DISPLAY_INFO
283 };
284 struct VirtgpuRespDisplayInfo resp = { 0 };
285
286 if (!RequestResponse(0, &req, sizeof(req), &resp, sizeof(resp))) {
287 goto DEFAULT;
288 }
289
290 if (resp.pmodes[0].enabled) {
291 g_virtGpu->screen = resp.pmodes[0].r;
292 return;
293 } else {
294 HDF_LOGE("[%s]scanout 0 not enabled", __func__);
295 }
296
297 DEFAULT:
298 g_virtGpu->screen.x = g_virtGpu->screen.y = 0;
299 g_virtGpu->screen.width = FB_WIDTH_DFT;
300 g_virtGpu->screen.height = FB_HEIGHT_DFT;
301 }
302
303 /* reserved for future use */
304 struct VirtgpuGetEdid {
305 struct VirtgpuCtrlHdr hdr;
306 uint32_t scanout;
307 uint32_t padding;
308 };
309 struct VirtgpuRespEdid {
310 struct VirtgpuCtrlHdr hdr;
311 uint32_t size;
312 uint32_t padding;
313 uint8_t edid[1024];
314 };
CMDGetEdid(void)315 static void CMDGetEdid(void)
316 {
317 struct VirtgpuGetEdid req = {
318 .hdr.type = VIRTIO_GPU_CMD_GET_EDID
319 };
320 struct VirtgpuRespEdid resp = { 0 };
321
322 if (!RequestResponse(0, &req, sizeof(req), &resp, sizeof(resp))) {
323 goto DEFAULT;
324 }
325
326 DEFAULT:
327 return;
328 }
329
330 struct VirtgpuResourceCreate2D {
331 struct VirtgpuCtrlHdr hdr;
332 uint32_t resourceId;
333 uint32_t format;
334 uint32_t width;
335 uint32_t height;
336 };
CMDResourceCreate2D(uint32_t resourceId)337 static bool CMDResourceCreate2D(uint32_t resourceId)
338 {
339 struct VirtgpuResourceCreate2D req = {
340 .hdr.type = VIRTIO_GPU_CMD_RESOURCE_CREATE_2D,
341 .resourceId = resourceId,
342 .format = VIRTIO_GPU_FORMAT_R8G8B8A8_UNORM, /* sRGB, byte order: RGBARGBA... */
343 .width = (resourceId == RESOURCEID_FB) ? g_virtGpu->screen.width : 0,
344 .height = (resourceId == RESOURCEID_FB) ? g_virtGpu->screen.height : 0
345 };
346 struct VirtgpuCtrlHdr resp = { 0 };
347
348 return RequestResponse(0, &req, sizeof(req), &resp, sizeof(resp));
349 }
350
351 struct VirtgpuSetScanout {
352 struct VirtgpuCtrlHdr hdr;
353 struct VirtgpuRect r;
354 uint32_t scanoutId;
355 uint32_t resourceId;
356 };
CMDSetScanout(const struct VirtgpuRect * r)357 static bool CMDSetScanout(const struct VirtgpuRect *r)
358 {
359 struct VirtgpuSetScanout req = {
360 .hdr.type = VIRTIO_GPU_CMD_SET_SCANOUT,
361 .r = *r,
362 .resourceId = RESOURCEID_FB
363 };
364 struct VirtgpuCtrlHdr resp = { 0 };
365
366 return RequestResponse(0, &req, sizeof(req), &resp, sizeof(resp));
367 }
368
CMDTransferToHost(uint32_t resourceId,const struct VirtgpuRect * r)369 static bool CMDTransferToHost(uint32_t resourceId, const struct VirtgpuRect *r)
370 {
371 struct VirtgpuTransferToHost2D req = {
372 .hdr.type = VIRTIO_GPU_CMD_TRANSFER_TO_HOST_2D,
373 .hdr.flags = VIRTIO_GPU_FLAG_FENCE,
374 .hdr.fenceId = r->x + r->y + r->width + r->height,
375 .r = *r,
376 .resourceId = resourceId,
377 };
378 struct VirtgpuCtrlHdr resp = { 0 };
379
380 return RequestResponse(0, &req, sizeof(req), &resp, sizeof(resp));
381 }
382
CMDResourceFlush(void)383 static bool CMDResourceFlush(void)
384 {
385 struct VirtgpuResourceFlush req = {
386 .hdr.type = VIRTIO_GPU_CMD_RESOURCE_FLUSH,
387 .r = g_virtGpu->screen,
388 .resourceId = RESOURCEID_FB,
389 };
390 struct VirtgpuCtrlHdr resp = { 0 };
391
392 return RequestResponse(0, &req, sizeof(req), &resp, sizeof(resp));
393 }
394
395 struct VirtgpuResourceAttachBacking {
396 struct VirtgpuCtrlHdr hdr;
397 uint32_t resourceId;
398 uint32_t nrEntries;
399 };
400 struct VirtgpuMemEntry {
401 uint64_t addr;
402 uint32_t length;
403 uint32_t padding;
404 }; /* vaddr's physical address should be continuous */
CMDResourceAttachBacking(uint32_t resourceId,uint64_t vaddr,uint32_t len)405 static bool CMDResourceAttachBacking(uint32_t resourceId, uint64_t vaddr, uint32_t len)
406 {
407 struct VirtgpuResourceAttachBacking req = {
408 .hdr.type = VIRTIO_GPU_CMD_RESOURCE_ATTACH_BACKING,
409 .resourceId = resourceId,
410 .nrEntries = 1
411 };
412 struct VirtgpuMemEntry data = {
413 .addr = VMM_TO_DMA_ADDR(vaddr),
414 .length = len,
415 };
416 struct VirtgpuCtrlHdr resp = { 0 };
417
418 return RequestDataResponse(&req, sizeof(req), &data, sizeof(data), &resp, sizeof(resp));
419 }
420
NormOpsRefresh(uintptr_t arg)421 static void NormOpsRefresh(uintptr_t arg)
422 {
423 (void)arg;
424 RequestNoResponse(0, &g_virtGpu->transReq, sizeof(g_virtGpu->transReq), false);
425 RequestNoResponse(0, &g_virtGpu->flushReq, sizeof(g_virtGpu->flushReq), true);
426 }
427
428 /* fit user-space page size mmap */
VirtgpuFbPageSize(void)429 static inline size_t VirtgpuFbPageSize(void)
430 {
431 return ALIGN(g_virtGpu->screen.width * g_virtGpu->screen.height * PIXEL_BYTES, PAGE_SIZE);
432 }
433
PopulateVirtQ(void)434 static void PopulateVirtQ(void)
435 {
436 struct Virtq *q = NULL;
437 int i, n;
438 uint16_t qsz;
439
440 for (n = 0; n < VIRTQ_NUM; n++) {
441 if (n) {
442 qsz = VIRTQ_CURSOR_QSZ;
443 } else {
444 qsz = VIRTQ_CONTROL_QSZ;
445 }
446 q = &g_virtGpu->dev.vq[n];
447
448 for (i = 0; i < qsz; i += NORMAL_CMD_ENTRIES) {
449 q->desc[i].flag = VIRTQ_DESC_F_NEXT;
450 q->desc[i].next = i + 1;
451 q->desc[i + 1].pAddr = VMM_TO_DMA_ADDR((VADDR_T)&g_virtGpu->resp);
452 q->desc[i + 1].len = sizeof(g_virtGpu->resp);
453 q->desc[i + 1].flag = VIRTQ_DESC_F_WRITE;
454 }
455 /* change usage to record our next writable index */
456 q->last = 0;
457 }
458
459 g_virtGpu->transReq.hdr.type = VIRTIO_GPU_CMD_TRANSFER_TO_HOST_2D;
460 g_virtGpu->transReq.r = g_virtGpu->screen;
461 g_virtGpu->transReq.resourceId = RESOURCEID_FB;
462
463 g_virtGpu->flushReq.hdr.type = VIRTIO_GPU_CMD_RESOURCE_FLUSH;
464 g_virtGpu->flushReq.r = g_virtGpu->screen;
465 g_virtGpu->flushReq.resourceId = RESOURCEID_FB;
466 }
467
VirtgpuBeginNormDisplay(void)468 static bool VirtgpuBeginNormDisplay(void)
469 {
470 int32_t ret;
471
472 if (!CMDTransferToHost(RESOURCEID_FB, &g_virtGpu->screen)) {
473 return false;
474 }
475 if (!CMDResourceFlush()) {
476 return false;
477 }
478
479 /* now we can fix queue entries to avoid redundant when do normal OPs */
480 PopulateVirtQ();
481
482 if ((ret = OsalTimerStartLoop(&g_virtGpu->timer)) != HDF_SUCCESS) {
483 HDF_LOGE("[%s]start timer failed: %d\n", __func__, ret);
484 return false;
485 }
486 return true;
487 }
488
489 /* unified DeInit for InitDev, HDF and fb */
VirtgpuDeInit(struct Virtgpu * gpu)490 static void VirtgpuDeInit(struct Virtgpu *gpu)
491 {
492 if (gpu->timer.realTimer) {
493 OsalTimerDelete(&gpu->timer);
494 }
495 if (gpu->fb) {
496 LOS_PhysPagesFreeContiguous(gpu->fb, VirtgpuFbPageSize() / PAGE_SIZE);
497 }
498 LOS_DmaMemFree(gpu);
499 g_virtGpu = NULL;
500 }
501
VirtgpuInitDev(void)502 static struct Virtgpu *VirtgpuInitDev(void)
503 {
504 struct Virtgpu *gpu = NULL;
505 VADDR_T base;
506 uint16_t qsz[VIRTQ_NUM];
507 int32_t ret, len;
508
509 /* NOTE: For simplicity, alloc all these data from physical continuous memory. */
510 len = sizeof(struct Virtgpu) + VirtqSize(VIRTQ_CONTROL_QSZ) + VirtqSize(VIRTQ_CURSOR_QSZ);
511 gpu = LOS_DmaMemAlloc(NULL, len, sizeof(void *), DMA_CACHE);
512 if (gpu == NULL) {
513 HDF_LOGE("[%s]alloc gpu memory failed", __func__);
514 return NULL;
515 }
516
517 if (!VirtmmioDiscover(VIRTMMIO_DEVICE_ID_GPU, &gpu->dev)) {
518 goto ERR_OUT;
519 }
520
521 VirtmmioInitBegin(&gpu->dev);
522
523 if (!VirtmmioNegotiate(&gpu->dev, Feature0, Feature1, gpu)) {
524 goto ERR_OUT1;
525 }
526
527 base = ALIGN((VADDR_T)gpu + sizeof(struct Virtgpu), VIRTQ_ALIGN_DESC);
528 qsz[0] = VIRTQ_CONTROL_QSZ;
529 qsz[1] = VIRTQ_CURSOR_QSZ;
530 if (VirtmmioConfigQueue(&gpu->dev, base, qsz, VIRTQ_NUM) == 0) {
531 goto ERR_OUT1;
532 }
533
534 /* framebuffer can be modified at any time, so we need a full screen refresh timer */
535 ret = OsalTimerCreate(&gpu->timer, GPU_DFT_RATE, NormOpsRefresh, 0);
536 if (ret != HDF_SUCCESS) {
537 HDF_LOGE("[%s]create timer failed: %d", __func__, ret);
538 goto ERR_OUT1;
539 }
540
541 for (int i = 0; i < VIRTQ_NUM; i++) { /* hint device not using IRQ */
542 gpu->dev.vq[i].avail->flag = VIRTQ_AVAIL_F_NO_INTERRUPT;
543 }
544
545 VritmmioInitEnd(&gpu->dev); /* now virt queue can be used */
546 return gpu;
547
548 ERR_OUT1:
549 VirtmmioInitFailed(&gpu->dev);
550 ERR_OUT:
551 VirtgpuDeInit(gpu);
552 return NULL;
553 }
554
HdfVirtgpuInit(struct HdfDeviceObject * device)555 static int32_t HdfVirtgpuInit(struct HdfDeviceObject *device)
556 {
557 int32_t ret;
558
559 if (device == NULL) {
560 HDF_LOGE("[%s]device is null", __func__);
561 return HDF_ERR_INVALID_PARAM;
562 }
563
564 g_virtGpu = VirtgpuInitDev();
565 if (g_virtGpu == NULL) {
566 return HDF_FAILURE;
567 }
568 device->priv = g_virtGpu;
569
570 /* frame buffer resource are initiated here, using virt queue mechanism */
571 if ((ret = fb_register(0, 0)) != 0) {
572 HDF_LOGE("[%s]framebuffer register failed: %d", __func__, ret);
573 return HDF_FAILURE;
574 }
575
576 if (!VirtgpuBeginNormDisplay()) {
577 return HDF_FAILURE;
578 }
579
580 return HDF_SUCCESS;
581 }
582
HdfVirtgpuRelease(struct HdfDeviceObject * deviceObject)583 static void HdfVirtgpuRelease(struct HdfDeviceObject *deviceObject)
584 {
585 if (deviceObject) {
586 if (deviceObject->priv) {
587 VirtgpuDeInit(deviceObject->priv);
588 }
589 }
590 }
591
592 struct HdfDriverEntry g_virtGpuEntry = {
593 .moduleVersion = 1,
594 .moduleName = "HDF_VIRTIO_GPU",
595 .Init = HdfVirtgpuInit,
596 .Release = HdfVirtgpuRelease,
597 };
598
599 HDF_INIT(g_virtGpuEntry);
600
601
602 /*
603 * video/fb.h interface implementation
604 */
605
VirtgpuInitResourceHelper(uint32_t resourceId)606 static bool VirtgpuInitResourceHelper(uint32_t resourceId)
607 {
608 uint64_t va;
609 uint32_t len, w, h;
610
611 if (!CMDResourceCreate2D(resourceId)) {
612 return false;
613 }
614
615 if (resourceId == RESOURCEID_FB) {
616 va = (uint64_t)g_virtGpu->fb;
617 w = g_virtGpu->screen.width;
618 h = g_virtGpu->screen.height;
619 } else {
620 HDF_LOGE("[%s]error resource ID: %u", __func__, resourceId);
621 return false;
622 }
623 len = w * h * PIXEL_BYTES;
624 if (!CMDResourceAttachBacking(resourceId, va, len)) {
625 return false;
626 }
627
628 if (resourceId == RESOURCEID_FB) {
629 struct VirtgpuRect r = { 0, 0, w, h };
630 return CMDSetScanout(&r);
631 }
632 return true;
633 }
634
VirtgpuInitResource(void)635 static bool VirtgpuInitResource(void)
636 {
637 /* Framebuffer must be physical continuous. fb_register will zero the buffer */
638 g_virtGpu->fb = LOS_PhysPagesAllocContiguous(VirtgpuFbPageSize() / PAGE_SIZE);
639 if (g_virtGpu->fb == NULL) {
640 HDF_LOGE("[%s]alloc framebuffer memory fail", __func__);
641 return false;
642 }
643 if (!VirtgpuInitResourceHelper(RESOURCEID_FB)) {
644 return false;
645 }
646
647 return true;
648 }
649
up_fbinitialize(int display)650 int up_fbinitialize(int display)
651 {
652 if (display != 0) {
653 return -1;
654 }
655
656 CMDGetDisplayInfo();
657 if (g_virtGpu->edid) {
658 CMDGetEdid();
659 }
660
661 if (!VirtgpuInitResource()) {
662 return -1;
663 }
664
665 return 0;
666 }
667
FbGetVideoInfo(struct fb_vtable_s * vtable,struct fb_videoinfo_s * vinfo)668 static int FbGetVideoInfo(struct fb_vtable_s *vtable, struct fb_videoinfo_s *vinfo)
669 {
670 (void)vtable;
671 vinfo->fmt = FB_FMT_RGB32; /* sRGB */
672 vinfo->xres = g_virtGpu->screen.width;
673 vinfo->yres = g_virtGpu->screen.height;
674 vinfo->nplanes = 1;
675 return 0;
676 }
677
678 #define BYTE_BITS 8
FbGetPlaneInfo(struct fb_vtable_s * vtable,int planeno,struct fb_planeinfo_s * pinfo)679 static int FbGetPlaneInfo(struct fb_vtable_s *vtable, int planeno, struct fb_planeinfo_s *pinfo)
680 {
681 if (planeno != 0) {
682 return -1;
683 }
684 (void)vtable;
685
686 pinfo->fbmem = g_virtGpu->fb;
687 pinfo->stride = g_virtGpu->screen.width * PIXEL_BYTES;
688 pinfo->fblen = pinfo->stride * g_virtGpu->screen.height;
689 pinfo->display = 0;
690 pinfo->bpp = PIXEL_BYTES * BYTE_BITS;
691 return 0;
692 }
693
694 #ifdef CONFIG_FB_OVERLAY
FbGetOverlayInfo(struct fb_vtable_s * v,int overlayno,struct fb_overlayinfo_s * info)695 static int FbGetOverlayInfo(struct fb_vtable_s *v, int overlayno, struct fb_overlayinfo_s *info)
696 {
697 (void)v;
698 if (overlayno != 0) {
699 return -1;
700 }
701
702 info->fbmem = g_virtGpu->fb;
703 info->memphys = (void *)VMM_TO_DMA_ADDR((VADDR_T)g_virtGpu->fb);
704 info->stride = g_virtGpu->screen.width * PIXEL_BYTES;
705 info->fblen = info->stride * g_virtGpu->screen.height;
706 info->overlay = 0;
707 info->bpp = PIXEL_BYTES * BYTE_BITS;
708 info->accl = 0;
709 return 0;
710 }
711 #endif
712
713 /* expect windows manager deal with concurrent access */
FbOpen(struct fb_vtable_s * vtable)714 static int FbOpen(struct fb_vtable_s *vtable)
715 {
716 (void)vtable;
717 return 0;
718 }
719
FbRelease(struct fb_vtable_s * vtable)720 static int FbRelease(struct fb_vtable_s *vtable)
721 {
722 (void)vtable;
723 return 0;
724 }
725
FbMmap(struct fb_vtable_s * vtable,LosVmMapRegion * region)726 static ssize_t FbMmap(struct fb_vtable_s *vtable, LosVmMapRegion *region)
727 {
728 (void)vtable;
729 int n;
730
731 if ((region->range.size + (region->pgOff << PAGE_SHIFT)) > VirtgpuFbPageSize()) {
732 HDF_LOGE("[%s]mmap size + pgOff exceed framebuffer size", __func__);
733 return -1;
734 }
735 if (region->regionFlags & VM_MAP_REGION_FLAG_PERM_EXECUTE) {
736 HDF_LOGE("[%s]cannot set execute flag", __func__);
737 return -1;
738 }
739
740 region->regionFlags |= VM_MAP_REGION_FLAG_UNCACHED;
741 n = LOS_ArchMmuMap(®ion->space->archMmu, region->range.base,
742 VMM_TO_DMA_ADDR((VADDR_T)g_virtGpu->fb + (region->pgOff << PAGE_SHIFT)),
743 region->range.size >> PAGE_SHIFT, region->regionFlags);
744 if (n != (region->range.size >> PAGE_SHIFT)) {
745 HDF_LOGE("[%s]mmu map error: %d", __func__, n);
746 return -1;
747 }
748
749 return 0;
750 }
751
752 /* used to happy video/fb.h configure */
FbDummy(struct fb_vtable_s * v,int * s)753 static int FbDummy(struct fb_vtable_s *v, int *s)
754 {
755 (void)v;
756 (void)s;
757 HDF_LOGE("[%s]unsupported method", __func__);
758 return -1;
759 }
760
761 static struct fb_vtable_s g_virtGpuFbOps = {
762 .getvideoinfo = FbGetVideoInfo,
763 .getplaneinfo = FbGetPlaneInfo,
764 .fb_open = FbOpen,
765 .fb_release = FbRelease,
766 #ifdef CONFIG_FB_CMAP
767 .getcmap = (int (*)(struct fb_vtable_s *, struct fb_cmap_s *))FbDummy,
768 .putcmap = (int (*)(struct fb_vtable_s *, const struct fb_cmap_s *))FbDummy,
769 #endif
770 #ifdef CONFIG_FB_OVERLAY
771 .getoverlayinfo = FbGetOverlayInfo,
772 .settransp = (int (*)(struct fb_vtable_s *, const struct fb_overlayinfo_s *))FbDummy,
773 .setchromakey = (int (*)(struct fb_vtable_s *, const struct fb_overlayinfo_s *))FbDummy,
774 .setcolor = (int (*)(struct fb_vtable_s *, const struct fb_overlayinfo_s *))FbDummy,
775 .setblank = (int (*)(struct fb_vtable_s *, const struct fb_overlayinfo_s *))FbDummy,
776 .setarea = (int (*)(struct fb_vtable_s *, const struct fb_overlayinfo_s *))FbDummy,
777 # ifdef CONFIG_FB_OVERLAY_BLIT
778 .blit = (int (*)(struct fb_vtable_s *, const struct fb_overlayblit_s *))FbDummy,
779 .blend = (int (*)(struct fb_vtable_s *, const struct fb_overlayblend_s *))FbDummy,
780 # endif
781 .fb_pan_display = (int (*)(struct fb_vtable_s *, struct fb_overlayinfo_s *))FbDummy,
782 #endif
783 .fb_mmap = FbMmap
784 };
785
up_fbgetvplane(int display,int vplane)786 struct fb_vtable_s *up_fbgetvplane(int display, int vplane)
787 {
788 if ((display != 0) || (vplane != 0)) {
789 return NULL;
790 }
791 return &g_virtGpuFbOps;
792 }
793
up_fbuninitialize(int display)794 void up_fbuninitialize(int display)
795 {
796 if (display != 0) {
797 return;
798 }
799
800 if (g_virtGpu) {
801 VirtgpuDeInit(g_virtGpu);
802 }
803 }
804
VirtgpuGetXres(void)805 uint32_t VirtgpuGetXres(void)
806 {
807 return g_virtGpu->screen.width;
808 }
809
VirtgpuGetYres(void)810 uint32_t VirtgpuGetYres(void)
811 {
812 return g_virtGpu->screen.height;
813 }