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