Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at v5.14-rc6 323 lines 8.0 kB view raw
1// SPDX-License-Identifier: GPL-2.0-or-later 2/* 3 */ 4 5#include <linux/pci.h> 6 7#include <drm/drm_drv.h> 8#include <drm/drm_fourcc.h> 9 10#include <video/vga.h> 11#include "bochs.h" 12 13/* ---------------------------------------------------------------------- */ 14 15static void bochs_vga_writeb(struct bochs_device *bochs, u16 ioport, u8 val) 16{ 17 if (WARN_ON(ioport < 0x3c0 || ioport > 0x3df)) 18 return; 19 20 if (bochs->mmio) { 21 int offset = ioport - 0x3c0 + 0x400; 22 writeb(val, bochs->mmio + offset); 23 } else { 24 outb(val, ioport); 25 } 26} 27 28static u8 bochs_vga_readb(struct bochs_device *bochs, u16 ioport) 29{ 30 if (WARN_ON(ioport < 0x3c0 || ioport > 0x3df)) 31 return 0xff; 32 33 if (bochs->mmio) { 34 int offset = ioport - 0x3c0 + 0x400; 35 return readb(bochs->mmio + offset); 36 } else { 37 return inb(ioport); 38 } 39} 40 41static u16 bochs_dispi_read(struct bochs_device *bochs, u16 reg) 42{ 43 u16 ret = 0; 44 45 if (bochs->mmio) { 46 int offset = 0x500 + (reg << 1); 47 ret = readw(bochs->mmio + offset); 48 } else { 49 outw(reg, VBE_DISPI_IOPORT_INDEX); 50 ret = inw(VBE_DISPI_IOPORT_DATA); 51 } 52 return ret; 53} 54 55static void bochs_dispi_write(struct bochs_device *bochs, u16 reg, u16 val) 56{ 57 if (bochs->mmio) { 58 int offset = 0x500 + (reg << 1); 59 writew(val, bochs->mmio + offset); 60 } else { 61 outw(reg, VBE_DISPI_IOPORT_INDEX); 62 outw(val, VBE_DISPI_IOPORT_DATA); 63 } 64} 65 66static void bochs_hw_set_big_endian(struct bochs_device *bochs) 67{ 68 if (bochs->qext_size < 8) 69 return; 70 71 writel(0xbebebebe, bochs->mmio + 0x604); 72} 73 74static void bochs_hw_set_little_endian(struct bochs_device *bochs) 75{ 76 if (bochs->qext_size < 8) 77 return; 78 79 writel(0x1e1e1e1e, bochs->mmio + 0x604); 80} 81 82#ifdef __BIG_ENDIAN 83#define bochs_hw_set_native_endian(_b) bochs_hw_set_big_endian(_b) 84#else 85#define bochs_hw_set_native_endian(_b) bochs_hw_set_little_endian(_b) 86#endif 87 88static int bochs_get_edid_block(void *data, u8 *buf, 89 unsigned int block, size_t len) 90{ 91 struct bochs_device *bochs = data; 92 size_t i, start = block * EDID_LENGTH; 93 94 if (start + len > 0x400 /* vga register offset */) 95 return -1; 96 97 for (i = 0; i < len; i++) { 98 buf[i] = readb(bochs->mmio + start + i); 99 } 100 return 0; 101} 102 103int bochs_hw_load_edid(struct bochs_device *bochs) 104{ 105 u8 header[8]; 106 107 if (!bochs->mmio) 108 return -1; 109 110 /* check header to detect whenever edid support is enabled in qemu */ 111 bochs_get_edid_block(bochs, header, 0, ARRAY_SIZE(header)); 112 if (drm_edid_header_is_valid(header) != 8) 113 return -1; 114 115 kfree(bochs->edid); 116 bochs->edid = drm_do_get_edid(&bochs->connector, 117 bochs_get_edid_block, bochs); 118 if (bochs->edid == NULL) 119 return -1; 120 121 return 0; 122} 123 124int bochs_hw_init(struct drm_device *dev) 125{ 126 struct bochs_device *bochs = dev->dev_private; 127 struct pci_dev *pdev = to_pci_dev(dev->dev); 128 unsigned long addr, size, mem, ioaddr, iosize; 129 u16 id; 130 131 if (pdev->resource[2].flags & IORESOURCE_MEM) { 132 /* mmio bar with vga and bochs registers present */ 133 if (pci_request_region(pdev, 2, "bochs-drm") != 0) { 134 DRM_ERROR("Cannot request mmio region\n"); 135 return -EBUSY; 136 } 137 ioaddr = pci_resource_start(pdev, 2); 138 iosize = pci_resource_len(pdev, 2); 139 bochs->mmio = ioremap(ioaddr, iosize); 140 if (bochs->mmio == NULL) { 141 DRM_ERROR("Cannot map mmio region\n"); 142 return -ENOMEM; 143 } 144 } else { 145 ioaddr = VBE_DISPI_IOPORT_INDEX; 146 iosize = 2; 147 if (!request_region(ioaddr, iosize, "bochs-drm")) { 148 DRM_ERROR("Cannot request ioports\n"); 149 return -EBUSY; 150 } 151 bochs->ioports = 1; 152 } 153 154 id = bochs_dispi_read(bochs, VBE_DISPI_INDEX_ID); 155 mem = bochs_dispi_read(bochs, VBE_DISPI_INDEX_VIDEO_MEMORY_64K) 156 * 64 * 1024; 157 if ((id & 0xfff0) != VBE_DISPI_ID0) { 158 DRM_ERROR("ID mismatch\n"); 159 return -ENODEV; 160 } 161 162 if ((pdev->resource[0].flags & IORESOURCE_MEM) == 0) 163 return -ENODEV; 164 addr = pci_resource_start(pdev, 0); 165 size = pci_resource_len(pdev, 0); 166 if (addr == 0) 167 return -ENODEV; 168 if (size != mem) { 169 DRM_ERROR("Size mismatch: pci=%ld, bochs=%ld\n", 170 size, mem); 171 size = min(size, mem); 172 } 173 174 if (pci_request_region(pdev, 0, "bochs-drm") != 0) 175 DRM_WARN("Cannot request framebuffer, boot fb still active?\n"); 176 177 bochs->fb_map = ioremap(addr, size); 178 if (bochs->fb_map == NULL) { 179 DRM_ERROR("Cannot map framebuffer\n"); 180 return -ENOMEM; 181 } 182 bochs->fb_base = addr; 183 bochs->fb_size = size; 184 185 DRM_INFO("Found bochs VGA, ID 0x%x.\n", id); 186 DRM_INFO("Framebuffer size %ld kB @ 0x%lx, %s @ 0x%lx.\n", 187 size / 1024, addr, 188 bochs->ioports ? "ioports" : "mmio", 189 ioaddr); 190 191 if (bochs->mmio && pdev->revision >= 2) { 192 bochs->qext_size = readl(bochs->mmio + 0x600); 193 if (bochs->qext_size < 4 || bochs->qext_size > iosize) { 194 bochs->qext_size = 0; 195 goto noext; 196 } 197 DRM_DEBUG("Found qemu ext regs, size %ld\n", 198 bochs->qext_size); 199 bochs_hw_set_native_endian(bochs); 200 } 201 202noext: 203 return 0; 204} 205 206void bochs_hw_fini(struct drm_device *dev) 207{ 208 struct bochs_device *bochs = dev->dev_private; 209 210 /* TODO: shot down existing vram mappings */ 211 212 if (bochs->mmio) 213 iounmap(bochs->mmio); 214 if (bochs->ioports) 215 release_region(VBE_DISPI_IOPORT_INDEX, 2); 216 if (bochs->fb_map) 217 iounmap(bochs->fb_map); 218 pci_release_regions(to_pci_dev(dev->dev)); 219 kfree(bochs->edid); 220} 221 222void bochs_hw_blank(struct bochs_device *bochs, bool blank) 223{ 224 DRM_DEBUG_DRIVER("hw_blank %d\n", blank); 225 /* discard ar_flip_flop */ 226 (void)bochs_vga_readb(bochs, VGA_IS1_RC); 227 /* blank or unblank; we need only update index and set 0x20 */ 228 bochs_vga_writeb(bochs, VGA_ATT_W, blank ? 0 : 0x20); 229} 230 231void bochs_hw_setmode(struct bochs_device *bochs, 232 struct drm_display_mode *mode) 233{ 234 int idx; 235 236 if (!drm_dev_enter(bochs->dev, &idx)) 237 return; 238 239 bochs->xres = mode->hdisplay; 240 bochs->yres = mode->vdisplay; 241 bochs->bpp = 32; 242 bochs->stride = mode->hdisplay * (bochs->bpp / 8); 243 bochs->yres_virtual = bochs->fb_size / bochs->stride; 244 245 DRM_DEBUG_DRIVER("%dx%d @ %d bpp, vy %d\n", 246 bochs->xres, bochs->yres, bochs->bpp, 247 bochs->yres_virtual); 248 249 bochs_hw_blank(bochs, false); 250 251 bochs_dispi_write(bochs, VBE_DISPI_INDEX_ENABLE, 0); 252 bochs_dispi_write(bochs, VBE_DISPI_INDEX_BPP, bochs->bpp); 253 bochs_dispi_write(bochs, VBE_DISPI_INDEX_XRES, bochs->xres); 254 bochs_dispi_write(bochs, VBE_DISPI_INDEX_YRES, bochs->yres); 255 bochs_dispi_write(bochs, VBE_DISPI_INDEX_BANK, 0); 256 bochs_dispi_write(bochs, VBE_DISPI_INDEX_VIRT_WIDTH, bochs->xres); 257 bochs_dispi_write(bochs, VBE_DISPI_INDEX_VIRT_HEIGHT, 258 bochs->yres_virtual); 259 bochs_dispi_write(bochs, VBE_DISPI_INDEX_X_OFFSET, 0); 260 bochs_dispi_write(bochs, VBE_DISPI_INDEX_Y_OFFSET, 0); 261 262 bochs_dispi_write(bochs, VBE_DISPI_INDEX_ENABLE, 263 VBE_DISPI_ENABLED | VBE_DISPI_LFB_ENABLED); 264 265 drm_dev_exit(idx); 266} 267 268void bochs_hw_setformat(struct bochs_device *bochs, 269 const struct drm_format_info *format) 270{ 271 int idx; 272 273 if (!drm_dev_enter(bochs->dev, &idx)) 274 return; 275 276 DRM_DEBUG_DRIVER("format %c%c%c%c\n", 277 (format->format >> 0) & 0xff, 278 (format->format >> 8) & 0xff, 279 (format->format >> 16) & 0xff, 280 (format->format >> 24) & 0xff); 281 282 switch (format->format) { 283 case DRM_FORMAT_XRGB8888: 284 bochs_hw_set_little_endian(bochs); 285 break; 286 case DRM_FORMAT_BGRX8888: 287 bochs_hw_set_big_endian(bochs); 288 break; 289 default: 290 /* should not happen */ 291 DRM_ERROR("%s: Huh? Got framebuffer format 0x%x", 292 __func__, format->format); 293 break; 294 } 295 296 drm_dev_exit(idx); 297} 298 299void bochs_hw_setbase(struct bochs_device *bochs, 300 int x, int y, int stride, u64 addr) 301{ 302 unsigned long offset; 303 unsigned int vx, vy, vwidth, idx; 304 305 if (!drm_dev_enter(bochs->dev, &idx)) 306 return; 307 308 bochs->stride = stride; 309 offset = (unsigned long)addr + 310 y * bochs->stride + 311 x * (bochs->bpp / 8); 312 vy = offset / bochs->stride; 313 vx = (offset % bochs->stride) * 8 / bochs->bpp; 314 vwidth = stride * 8 / bochs->bpp; 315 316 DRM_DEBUG_DRIVER("x %d, y %d, addr %llx -> offset %lx, vx %d, vy %d\n", 317 x, y, addr, offset, vx, vy); 318 bochs_dispi_write(bochs, VBE_DISPI_INDEX_VIRT_WIDTH, vwidth); 319 bochs_dispi_write(bochs, VBE_DISPI_INDEX_X_OFFSET, vx); 320 bochs_dispi_write(bochs, VBE_DISPI_INDEX_Y_OFFSET, vy); 321 322 drm_dev_exit(idx); 323}