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