blob: 7efb11fbebcdaf9950a51f457793da1b21558558 [file] [log] [blame]
Gerd Hoffmann0c480642019-10-23 07:50:38 +02001// Simple framebuffer vgabios for use with qemu ramfb device
2//
3// Copyright (C) 2019 Gerd Hoffmann <kraxel@redhat.com>
4//
5// This file may be distributed under the terms of the GNU LGPLv3 license.
6
Gerd Hoffmannd9a8b862017-11-15 14:43:10 +01007#include "biosvar.h" // GET_BDA
8#include "output.h" // dprintf
9#include "string.h" // memset16_far
10#include "vgautil.h" // VBE_total_memory
11#include "std/pmm.h" // struct pmmheader
12#include "byteorder.h"
13#include "fw/paravirt.h"
14
15/* ---------------------------------------------------------------------- */
16/* minimal qemu fc_cfg support bits, requires dma support */
17
18#define QEMU_CFG_FILE_DIR 0x19
19
20struct QemuCfgFile {
21 u32 size; /* file size */
22 u16 select; /* write this to 0x510 to read it */
23 u16 reserved;
24 char name[56];
25};
26
27static void
28qemu_cfg_dma_transfer(void *address, u32 length, u32 control)
29{
30 QemuCfgDmaAccess access;
31
32 if (length == 0) {
33 return;
34 }
35
36 access.address = cpu_to_be64((u64)(u32)address);
37 access.length = cpu_to_be32(length);
38 access.control = cpu_to_be32(control);
39
40 barrier();
41
42 outl(cpu_to_be32((u32)&access), PORT_QEMU_CFG_DMA_ADDR_LOW);
43
44 while(be32_to_cpu(access.control) & ~QEMU_CFG_DMA_CTL_ERROR)
45 /* wait */;
46}
47
48static void
49qemu_cfg_read(void *buf, int len)
50{
51 qemu_cfg_dma_transfer(buf, len, QEMU_CFG_DMA_CTL_READ);
52}
53
54static void
55qemu_cfg_read_entry(void *buf, int e, int len)
56{
57 u32 control = (e << 16) | QEMU_CFG_DMA_CTL_SELECT
58 | QEMU_CFG_DMA_CTL_READ;
59 qemu_cfg_dma_transfer(buf, len, control);
60}
61
62static void
63qemu_cfg_write_entry(void *buf, int e, int len)
64{
65 u32 control = (e << 16) | QEMU_CFG_DMA_CTL_SELECT
66 | QEMU_CFG_DMA_CTL_WRITE;
67 qemu_cfg_dma_transfer(buf, len, control);
68}
69
70static int
71qemu_cfg_find_file(const char *filename)
72{
73 u32 count, e, select;
74
75 qemu_cfg_read_entry(&count, QEMU_CFG_FILE_DIR, sizeof(count));
76 count = be32_to_cpu(count);
77 for (select = 0, e = 0; e < count; e++) {
78 struct QemuCfgFile qfile;
79 qemu_cfg_read(&qfile, sizeof(qfile));
80 if (memcmp_far(GET_SEG(SS), qfile.name,
81 GET_SEG(CS), filename, 10) == 0)
82 select = be16_to_cpu(qfile.select);
83 }
84 return select;
85}
86
87/* ---------------------------------------------------------------------- */
88
89#define FRAMEBUFFER_WIDTH 1024
90#define FRAMEBUFFER_HEIGHT 768
91#define FRAMEBUFFER_BPP 4
92#define FRAMEBUFFER_STRIDE (FRAMEBUFFER_BPP * FRAMEBUFFER_WIDTH)
93#define FRAMEBUFFER_SIZE (FRAMEBUFFER_STRIDE * FRAMEBUFFER_HEIGHT)
94
95struct QemuRAMFBCfg {
96 u64 addr;
97 u32 fourcc;
98 u32 flags;
99 u32 width;
100 u32 height;
101 u32 stride;
102};
103
104#define fourcc_code(a, b, c, d) ((u32)(a) | ((u32)(b) << 8) | \
105 ((u32)(c) << 16) | ((u32)(d) << 24))
106
107#define DRM_FORMAT_RGB565 fourcc_code('R', 'G', '1', '6') /* [15:0] R:G:B 5:6:5 little endian */
108#define DRM_FORMAT_RGB888 fourcc_code('R', 'G', '2', '4') /* [23:0] R:G:B little endian */
109#define DRM_FORMAT_XRGB8888 fourcc_code('X', 'R', '2', '4') /* [31:0] x:R:G:B 8:8:8:8 little endian */
110
111static u32
112allocate_framebuffer(void)
113{
114 u32 res = allocate_pmm(FRAMEBUFFER_SIZE, 1, 1);
115 if (!res)
116 return 0;
117 dprintf(1, "ramfb: framebuffer allocated at %x\n", res);
118 return res;
119}
120
121int
122ramfb_setup(void)
123{
124 dprintf(1, "ramfb: init\n");
125
126 if (GET_GLOBAL(HaveRunInit))
127 return 0;
128
129 u32 select = qemu_cfg_find_file("etc/ramfb");
130 if (select == 0) {
131 dprintf(1, "ramfb: fw_cfg (etc/ramfb) file not found\n");
132 return -1;
133 }
134
135 dprintf(1, "ramfb: fw_cfg (etc/ramfb) file at slot 0x%x\n", select);
136 u32 fb = allocate_framebuffer();
137 if (!fb) {
138 dprintf(1, "ramfb: allocating framebuffer failed\n");
139 return -1;
140 }
141
142 u64 addr = fb;
143 u8 bpp = FRAMEBUFFER_BPP * 8;
144 u32 xlines = FRAMEBUFFER_WIDTH;
145 u32 ylines = FRAMEBUFFER_HEIGHT;
146 u32 linelength = FRAMEBUFFER_STRIDE;
147 dprintf(1, "Found FB @ %llx %dx%d with %d bpp (%d stride)\n"
148 , addr, xlines, ylines, bpp, linelength);
149
150 if (!addr || addr > 0xffffffff
151 || (bpp != 15 && bpp != 16 && bpp != 24 && bpp != 32)) {
152 dprintf(1, "Unable to use FB\n");
153 return -1;
154 }
155
156 cbvga_setup_modes(addr, bpp, xlines, ylines, linelength);
157
158 struct QemuRAMFBCfg cfg = {
159 .addr = cpu_to_be64(fb),
160 .fourcc = cpu_to_be32(DRM_FORMAT_XRGB8888),
161 .flags = cpu_to_be32(0),
162 .width = cpu_to_be32(FRAMEBUFFER_WIDTH),
163 .height = cpu_to_be32(FRAMEBUFFER_HEIGHT),
164 .stride = cpu_to_be32(FRAMEBUFFER_STRIDE),
165 };
166 qemu_cfg_write_entry(&cfg, select, sizeof(cfg));
167
168 return 0;
169}