Commit | Line | Data |
---|---|---|
0a6659bd GH |
1 | /* |
2 | * This program is free software; you can redistribute it and/or modify | |
3 | * it under the terms of the GNU General Public License as published by | |
4 | * the Free Software Foundation; either version 2 of the License, or | |
5 | * (at your option) any later version. | |
6 | */ | |
7 | ||
8 | #include "bochs.h" | |
9 | ||
10 | /* ---------------------------------------------------------------------- */ | |
11 | ||
12 | static void bochs_vga_writeb(struct bochs_device *bochs, u16 ioport, u8 val) | |
13 | { | |
14 | if (WARN_ON(ioport < 0x3c0 || ioport > 0x3df)) | |
15 | return; | |
16 | ||
17 | if (bochs->mmio) { | |
18 | int offset = ioport - 0x3c0 + 0x400; | |
19 | writeb(val, bochs->mmio + offset); | |
20 | } else { | |
21 | outb(val, ioport); | |
22 | } | |
23 | } | |
24 | ||
25 | static u16 bochs_dispi_read(struct bochs_device *bochs, u16 reg) | |
26 | { | |
27 | u16 ret = 0; | |
28 | ||
29 | if (bochs->mmio) { | |
30 | int offset = 0x500 + (reg << 1); | |
31 | ret = readw(bochs->mmio + offset); | |
32 | } else { | |
33 | outw(reg, VBE_DISPI_IOPORT_INDEX); | |
34 | ret = inw(VBE_DISPI_IOPORT_DATA); | |
35 | } | |
36 | return ret; | |
37 | } | |
38 | ||
39 | static void bochs_dispi_write(struct bochs_device *bochs, u16 reg, u16 val) | |
40 | { | |
41 | if (bochs->mmio) { | |
42 | int offset = 0x500 + (reg << 1); | |
43 | writew(val, bochs->mmio + offset); | |
44 | } else { | |
45 | outw(reg, VBE_DISPI_IOPORT_INDEX); | |
46 | outw(val, VBE_DISPI_IOPORT_DATA); | |
47 | } | |
48 | } | |
49 | ||
50 | int bochs_hw_init(struct drm_device *dev, uint32_t flags) | |
51 | { | |
52 | struct bochs_device *bochs = dev->dev_private; | |
53 | struct pci_dev *pdev = dev->pdev; | |
9ecdb039 | 54 | unsigned long addr, size, mem, ioaddr, iosize, qext_size; |
0a6659bd GH |
55 | u16 id; |
56 | ||
fbd2f9fe | 57 | if (pdev->resource[2].flags & IORESOURCE_MEM) { |
0a6659bd GH |
58 | /* mmio bar with vga and bochs registers present */ |
59 | if (pci_request_region(pdev, 2, "bochs-drm") != 0) { | |
60 | DRM_ERROR("Cannot request mmio region\n"); | |
61 | return -EBUSY; | |
62 | } | |
63 | ioaddr = pci_resource_start(pdev, 2); | |
64 | iosize = pci_resource_len(pdev, 2); | |
65 | bochs->mmio = ioremap(ioaddr, iosize); | |
66 | if (bochs->mmio == NULL) { | |
67 | DRM_ERROR("Cannot map mmio region\n"); | |
68 | return -ENOMEM; | |
69 | } | |
70 | } else { | |
71 | ioaddr = VBE_DISPI_IOPORT_INDEX; | |
72 | iosize = 2; | |
73 | if (!request_region(ioaddr, iosize, "bochs-drm")) { | |
74 | DRM_ERROR("Cannot request ioports\n"); | |
75 | return -EBUSY; | |
76 | } | |
77 | bochs->ioports = 1; | |
78 | } | |
79 | ||
80 | id = bochs_dispi_read(bochs, VBE_DISPI_INDEX_ID); | |
81 | mem = bochs_dispi_read(bochs, VBE_DISPI_INDEX_VIDEO_MEMORY_64K) | |
82 | * 64 * 1024; | |
83 | if ((id & 0xfff0) != VBE_DISPI_ID0) { | |
84 | DRM_ERROR("ID mismatch\n"); | |
85 | return -ENODEV; | |
86 | } | |
87 | ||
88 | if ((pdev->resource[0].flags & IORESOURCE_MEM) == 0) | |
89 | return -ENODEV; | |
90 | addr = pci_resource_start(pdev, 0); | |
91 | size = pci_resource_len(pdev, 0); | |
92 | if (addr == 0) | |
93 | return -ENODEV; | |
94 | if (size != mem) { | |
95 | DRM_ERROR("Size mismatch: pci=%ld, bochs=%ld\n", | |
96 | size, mem); | |
97 | size = min(size, mem); | |
98 | } | |
99 | ||
100 | if (pci_request_region(pdev, 0, "bochs-drm") != 0) { | |
101 | DRM_ERROR("Cannot request framebuffer\n"); | |
102 | return -EBUSY; | |
103 | } | |
104 | ||
105 | bochs->fb_map = ioremap(addr, size); | |
106 | if (bochs->fb_map == NULL) { | |
107 | DRM_ERROR("Cannot map framebuffer\n"); | |
108 | return -ENOMEM; | |
109 | } | |
110 | bochs->fb_base = addr; | |
111 | bochs->fb_size = size; | |
112 | ||
113 | DRM_INFO("Found bochs VGA, ID 0x%x.\n", id); | |
114 | DRM_INFO("Framebuffer size %ld kB @ 0x%lx, %s @ 0x%lx.\n", | |
115 | size / 1024, addr, | |
116 | bochs->ioports ? "ioports" : "mmio", | |
117 | ioaddr); | |
9ecdb039 GH |
118 | |
119 | if (bochs->mmio && pdev->revision >= 2) { | |
120 | qext_size = readl(bochs->mmio + 0x600); | |
121 | if (qext_size < 4 || qext_size > iosize) | |
122 | goto noext; | |
123 | DRM_DEBUG("Found qemu ext regs, size %ld\n", qext_size); | |
124 | if (qext_size >= 8) { | |
125 | #ifdef __BIG_ENDIAN | |
126 | writel(0xbebebebe, bochs->mmio + 0x604); | |
127 | #else | |
128 | writel(0x1e1e1e1e, bochs->mmio + 0x604); | |
129 | #endif | |
130 | DRM_DEBUG(" qext endian: 0x%x\n", | |
131 | readl(bochs->mmio + 0x604)); | |
132 | } | |
133 | } | |
134 | ||
135 | noext: | |
0a6659bd GH |
136 | return 0; |
137 | } | |
138 | ||
139 | void bochs_hw_fini(struct drm_device *dev) | |
140 | { | |
141 | struct bochs_device *bochs = dev->dev_private; | |
142 | ||
143 | if (bochs->mmio) | |
144 | iounmap(bochs->mmio); | |
145 | if (bochs->ioports) | |
146 | release_region(VBE_DISPI_IOPORT_INDEX, 2); | |
147 | if (bochs->fb_map) | |
148 | iounmap(bochs->fb_map); | |
149 | pci_release_regions(dev->pdev); | |
150 | } | |
151 | ||
152 | void bochs_hw_setmode(struct bochs_device *bochs, | |
153 | struct drm_display_mode *mode) | |
154 | { | |
155 | bochs->xres = mode->hdisplay; | |
156 | bochs->yres = mode->vdisplay; | |
157 | bochs->bpp = 32; | |
158 | bochs->stride = mode->hdisplay * (bochs->bpp / 8); | |
159 | bochs->yres_virtual = bochs->fb_size / bochs->stride; | |
160 | ||
161 | DRM_DEBUG_DRIVER("%dx%d @ %d bpp, vy %d\n", | |
162 | bochs->xres, bochs->yres, bochs->bpp, | |
163 | bochs->yres_virtual); | |
164 | ||
165 | bochs_vga_writeb(bochs, 0x3c0, 0x20); /* unblank */ | |
166 | ||
564b687b | 167 | bochs_dispi_write(bochs, VBE_DISPI_INDEX_ENABLE, 0); |
0a6659bd GH |
168 | bochs_dispi_write(bochs, VBE_DISPI_INDEX_BPP, bochs->bpp); |
169 | bochs_dispi_write(bochs, VBE_DISPI_INDEX_XRES, bochs->xres); | |
170 | bochs_dispi_write(bochs, VBE_DISPI_INDEX_YRES, bochs->yres); | |
171 | bochs_dispi_write(bochs, VBE_DISPI_INDEX_BANK, 0); | |
172 | bochs_dispi_write(bochs, VBE_DISPI_INDEX_VIRT_WIDTH, bochs->xres); | |
173 | bochs_dispi_write(bochs, VBE_DISPI_INDEX_VIRT_HEIGHT, | |
174 | bochs->yres_virtual); | |
175 | bochs_dispi_write(bochs, VBE_DISPI_INDEX_X_OFFSET, 0); | |
176 | bochs_dispi_write(bochs, VBE_DISPI_INDEX_Y_OFFSET, 0); | |
177 | ||
178 | bochs_dispi_write(bochs, VBE_DISPI_INDEX_ENABLE, | |
179 | VBE_DISPI_ENABLED | VBE_DISPI_LFB_ENABLED); | |
180 | } | |
181 | ||
182 | void bochs_hw_setbase(struct bochs_device *bochs, | |
183 | int x, int y, u64 addr) | |
184 | { | |
185 | unsigned long offset = (unsigned long)addr + | |
186 | y * bochs->stride + | |
187 | x * (bochs->bpp / 8); | |
188 | int vy = offset / bochs->stride; | |
189 | int vx = (offset % bochs->stride) * 8 / bochs->bpp; | |
190 | ||
191 | DRM_DEBUG_DRIVER("x %d, y %d, addr %llx -> offset %lx, vx %d, vy %d\n", | |
192 | x, y, addr, offset, vx, vy); | |
193 | bochs_dispi_write(bochs, VBE_DISPI_INDEX_X_OFFSET, vx); | |
194 | bochs_dispi_write(bochs, VBE_DISPI_INDEX_Y_OFFSET, vy); | |
195 | } |