blob: 61b329d26ceadcd9699357e83024c8ac20807529 [file] [log] [blame]
Vladimir Serbinenko7905f922013-11-25 11:20:20 +01001/*
2 * This file is part of the coreboot project.
3 *
4 * Copyright (C) 2013 Vladimir Serbinenko
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License as
8 * published by the Free Software Foundation; version 2 of
9 * the License, or (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
Vladimir Serbinenko7905f922013-11-25 11:20:20 +010015 */
16
17#include <stdint.h>
18#include <delay.h>
19#include <edid.h>
20#include <stdlib.h>
21#include <string.h>
22#include <arch/io.h>
23
24#include <boot/coreboot_tables.h>
25#include <console/console.h>
26#include <device/device.h>
27#include <device/pci.h>
28#include <device/pci_ids.h>
29#include <device/pci_ops.h>
30
31#include <pc80/vga.h>
32#include <pc80/vga_io.h>
33
34static int width = CONFIG_DRIVERS_EMULATION_QEMU_BOCHS_XRES;
35static int height = CONFIG_DRIVERS_EMULATION_QEMU_BOCHS_YRES;
36static u32 addr = 0;
37
38enum
39 {
40 VGA_CR_HTOTAL = 0x00,
41 VGA_CR_HORIZ_END = 0x01,
42 VGA_CR_HBLANK_START = 0x02,
43 VGA_CR_HBLANK_END = 0x03,
44 VGA_CR_HORIZ_SYNC_PULSE_START = 0x04,
45 VGA_CR_HORIZ_SYNC_PULSE_END = 0x05,
46 VGA_CR_VERT_TOTAL = 0x06,
47 VGA_CR_OVERFLOW = 0x07,
48 VGA_CR_BYTE_PANNING = 0x08,
49 VGA_CR_CELL_HEIGHT = 0x09,
50 VGA_CR_CURSOR_START = 0x0a,
51 VGA_CR_CURSOR_END = 0x0b,
52 VGA_CR_START_ADDR_HIGH_REGISTER = 0x0c,
53 VGA_CR_START_ADDR_LOW_REGISTER = 0x0d,
54 VGA_CR_CURSOR_ADDR_HIGH = 0x0e,
55 VGA_CR_CURSOR_ADDR_LOW = 0x0f,
56 VGA_CR_VSYNC_START = 0x10,
57 VGA_CR_VSYNC_END = 0x11,
58 VGA_CR_VDISPLAY_END = 0x12,
59 VGA_CR_PITCH = 0x13,
60 VGA_CR_UNDERLINE_LOCATION = 0x14,
61 VGA_CR_VERTICAL_BLANK_START = 0x15,
62 VGA_CR_VERTICAL_BLANK_END = 0x16,
63 VGA_CR_MODE = 0x17,
64 VGA_CR_LINE_COMPARE = 0x18,
65 };
66
67#define VGA_IO_MISC_COLOR 0x01
68
69#define VGA_CR_WIDTH_DIVISOR 8
70
71#define VGA_CR_OVERFLOW_VERT_DISPLAY_ENABLE_END1_SHIFT 7
72#define VGA_CR_OVERFLOW_VERT_DISPLAY_ENABLE_END1_MASK 0x02
73#define VGA_CR_OVERFLOW_VERT_DISPLAY_ENABLE_END2_SHIFT 3
74#define VGA_CR_OVERFLOW_VERT_DISPLAY_ENABLE_END2_MASK 0x40
75
76#define VGA_CR_OVERFLOW_VERT_TOTAL1_SHIFT 8
77#define VGA_CR_OVERFLOW_VERT_TOTAL1_MASK 0x01
78#define VGA_CR_OVERFLOW_VERT_TOTAL2_SHIFT 4
79#define VGA_CR_OVERFLOW_VERT_TOTAL2_MASK 0x20
80
81#define VGA_CR_OVERFLOW_VSYNC_START1_SHIFT 6
82#define VGA_CR_OVERFLOW_VSYNC_START1_MASK 0x04
83#define VGA_CR_OVERFLOW_VSYNC_START2_SHIFT 2
84#define VGA_CR_OVERFLOW_VSYNC_START2_MASK 0x80
85
86#define VGA_CR_OVERFLOW_HEIGHT1_SHIFT 7
87#define VGA_CR_OVERFLOW_HEIGHT1_MASK 0x02
88#define VGA_CR_OVERFLOW_HEIGHT2_SHIFT 3
89#define VGA_CR_OVERFLOW_HEIGHT2_MASK 0xc0
90#define VGA_CR_OVERFLOW_LINE_COMPARE_SHIFT 4
91#define VGA_CR_OVERFLOW_LINE_COMPARE_MASK 0x10
92
93#define VGA_CR_CELL_HEIGHT_LINE_COMPARE_MASK 0x40
94#define VGA_CR_CELL_HEIGHT_LINE_COMPARE_SHIFT 3
95#define VGA_CR_CELL_HEIGHT_VERTICAL_BLANK_MASK 0x20
96#define VGA_CR_CELL_HEIGHT_VERTICAL_BLANK_SHIFT 4
97#define VGA_CR_CELL_HEIGHT_DOUBLE_SCAN 0x80
98enum
99 {
100 VGA_CR_CURSOR_START_DISABLE = (1 << 5)
101 };
102
103#define VGA_CR_PITCH_DIVISOR 8
104
105enum
106 {
107 VGA_CR_MODE_NO_CGA = 0x01,
108 VGA_CR_MODE_NO_HERCULES = 0x02,
109 VGA_CR_MODE_ADDRESS_WRAP = 0x20,
110 VGA_CR_MODE_BYTE_MODE = 0x40,
111 VGA_CR_MODE_TIMING_ENABLE = 0x80
112 };
113
114enum
115 {
116 VGA_SR_RESET = 0,
117 VGA_SR_CLOCKING_MODE = 1,
118 VGA_SR_MAP_MASK_REGISTER = 2,
119 VGA_SR_CHAR_MAP_SELECT = 3,
120 VGA_SR_MEMORY_MODE = 4,
121 };
122
123enum
124 {
125 VGA_SR_RESET_ASYNC = 1,
126 VGA_SR_RESET_SYNC = 2
127 };
128
129enum
130 {
131 VGA_SR_CLOCKING_MODE_8_DOT_CLOCK = 1
132 };
133
134enum
135 {
136 VGA_SR_MEMORY_MODE_NORMAL = 0,
137 VGA_SR_MEMORY_MODE_EXTERNAL_VIDEO_MEMORY = 2,
138 VGA_SR_MEMORY_MODE_SEQUENTIAL_ADDRESSING = 4,
139 VGA_SR_MEMORY_MODE_CHAIN4 = 8,
140 };
141
142enum
143 {
144 VGA_GR_SET_RESET_PLANE = 0,
145 VGA_GR_SET_RESET_PLANE_ENABLE = 1,
146 VGA_GR_COLOR_COMPARE = 2,
147 VGA_GR_READ_MAP_REGISTER = 4,
148 VGA_GR_MODE = 5,
149 VGA_GR_GR6 = 6,
150 VGA_GR_COLOR_COMPARE_DISABLE = 7,
151 VGA_GR_BITMASK = 8,
152 VGA_GR_MAX
153 };
154
155enum
156 {
157 VGA_TEXT_TEXT_PLANE = 0,
158 VGA_TEXT_ATTR_PLANE = 1,
159 VGA_TEXT_FONT_PLANE = 2
160 };
161
162enum
163 {
164 VGA_GR_GR6_GRAPHICS_MODE = 1,
165 VGA_GR_GR6_MMAP_A0 = (1 << 2),
166 VGA_GR_GR6_MMAP_CGA = (3 << 2)
167 };
168
169enum
170 {
171 VGA_GR_MODE_READ_MODE1 = 0x08,
172 VGA_GR_MODE_ODD_EVEN = 0x10,
173 VGA_GR_MODE_ODD_EVEN_SHIFT = 0x20,
174 VGA_GR_MODE_256_COLOR = 0x40
175 };
176
177#define CIRRUS_CR_EXTENDED_DISPLAY 0x1b
178#define CIRRUS_CR_EXTENDED_OVERLAY 0x1d
179
180#define CIRRUS_CR_EXTENDED_DISPLAY_PITCH_MASK 0x10
181#define CIRRUS_CR_EXTENDED_DISPLAY_PITCH_SHIFT 4
182#define CIRRUS_CR_EXTENDED_DISPLAY_START_MASK1 0x1
183#define CIRRUS_CR_EXTENDED_DISPLAY_START_SHIFT1 16
184#define CIRRUS_CR_EXTENDED_DISPLAY_START_MASK2 0xc
185#define CIRRUS_CR_EXTENDED_DISPLAY_START_SHIFT2 15
186
187#define CIRRUS_CR_EXTENDED_OVERLAY_DISPLAY_START_MASK 0x80
188#define CIRRUS_CR_EXTENDED_OVERLAY_DISPLAY_START_SHIFT 12
189#define CIRRUS_SR_EXTENDED_MODE 7
190#define CIRRUS_SR_EXTENDED_MODE_LFB_ENABLE 0xf0
191#define CIRRUS_SR_EXTENDED_MODE_ENABLE_EXT 0x01
192#define CIRRUS_SR_EXTENDED_MODE_32BPP 0x08
193#define CIRRUS_HIDDEN_DAC_888COLOR 0xc5
194
195static void
196write_hidden_dac (uint8_t data)
197{
198 inb (0x3c8);
199 inb (0x3c6);
200 inb (0x3c6);
201 inb (0x3c6);
202 inb (0x3c6);
203 outb (data, 0x3c6);
204}
205
Nico Huber6d8266b2017-05-20 16:46:01 +0200206static void cirrus_init_linear_fb(struct device *dev)
Vladimir Serbinenko7905f922013-11-25 11:20:20 +0100207{
208 uint8_t cr_ext, cr_overlay;
209 unsigned pitch = (width * 4) / VGA_CR_PITCH_DIVISOR;
210 uint8_t sr_ext = 0, hidden_dac = 0;
211 unsigned vdisplay_end = height - 2;
212 unsigned line_compare = 0x3ff;
213 uint8_t overflow, cell_height_reg;
214 unsigned horizontal_end = width / VGA_CR_WIDTH_DIVISOR;
215 unsigned horizontal_total = horizontal_end + 40;
216 unsigned horizontal_blank_start = horizontal_end;
217 unsigned horizontal_sync_pulse_start = horizontal_end + 3;
218 unsigned horizontal_sync_pulse_end = 0;
219
220 unsigned horizontal_blank_end = 0;
221 unsigned vertical_blank_start = height + 1;
222 unsigned vertical_blank_end = 0;
223 unsigned vertical_sync_start = height + 3;
224 unsigned vertical_sync_end = 0;
225 unsigned vertical_total = height + 40;
226
227 /* find lfb pci bar */
228 addr = pci_read_config32(dev, PCI_BASE_ADDRESS_0);
229 addr &= ~PCI_BASE_ADDRESS_MEM_ATTR_MASK;
230 printk(BIOS_DEBUG, "QEMU VGA: cirrus framebuffer @ %x (pci bar 0)\n",
231 addr);
232
233 vga_misc_write (VGA_IO_MISC_COLOR);
234
235 vga_sr_write (VGA_SR_MEMORY_MODE,
236 VGA_SR_MEMORY_MODE_NORMAL);
237
238 vga_sr_write (VGA_SR_MAP_MASK_REGISTER,
239 (1 << VGA_TEXT_TEXT_PLANE)
240 | (1 << VGA_TEXT_ATTR_PLANE));
241
242 vga_sr_write (VGA_SR_CLOCKING_MODE,
243 VGA_SR_CLOCKING_MODE_8_DOT_CLOCK);
244
245 vga_palette_disable();
246
247 /* Disable CR0-7 write protection. */
248 vga_cr_write (VGA_CR_VSYNC_END, 0);
249
250 overflow = ((vertical_total >> VGA_CR_OVERFLOW_VERT_TOTAL1_SHIFT)
251 & VGA_CR_OVERFLOW_VERT_TOTAL1_MASK)
252 | ((vertical_total >> VGA_CR_OVERFLOW_VERT_TOTAL2_SHIFT)
253 & VGA_CR_OVERFLOW_VERT_TOTAL2_MASK)
254 | ((vertical_sync_start >> VGA_CR_OVERFLOW_VSYNC_START2_SHIFT)
255 & VGA_CR_OVERFLOW_VSYNC_START2_MASK)
256 | ((vertical_sync_start >> VGA_CR_OVERFLOW_VSYNC_START1_SHIFT)
257 & VGA_CR_OVERFLOW_VSYNC_START1_MASK)
258 | ((vdisplay_end >> VGA_CR_OVERFLOW_VERT_DISPLAY_ENABLE_END1_SHIFT)
259 & VGA_CR_OVERFLOW_VERT_DISPLAY_ENABLE_END1_MASK)
260 | ((vdisplay_end >> VGA_CR_OVERFLOW_VERT_DISPLAY_ENABLE_END2_SHIFT)
261 & VGA_CR_OVERFLOW_VERT_DISPLAY_ENABLE_END2_MASK)
262 | ((vertical_sync_start >> VGA_CR_OVERFLOW_VSYNC_START1_SHIFT)
263 & VGA_CR_OVERFLOW_VSYNC_START1_MASK)
264 | ((line_compare >> VGA_CR_OVERFLOW_LINE_COMPARE_SHIFT)
265 & VGA_CR_OVERFLOW_LINE_COMPARE_MASK);
266
267 cell_height_reg = ((vertical_blank_start
268 >> VGA_CR_CELL_HEIGHT_VERTICAL_BLANK_SHIFT)
269 & VGA_CR_CELL_HEIGHT_VERTICAL_BLANK_MASK)
270 | ((line_compare >> VGA_CR_CELL_HEIGHT_LINE_COMPARE_SHIFT)
271 & VGA_CR_CELL_HEIGHT_LINE_COMPARE_MASK);
272
273 vga_cr_write (VGA_CR_HTOTAL, horizontal_total - 1);
274 vga_cr_write (VGA_CR_HORIZ_END, horizontal_end - 1);
275 vga_cr_write (VGA_CR_HBLANK_START, horizontal_blank_start - 1);
276 vga_cr_write (VGA_CR_HBLANK_END, horizontal_blank_end);
277 vga_cr_write (VGA_CR_HORIZ_SYNC_PULSE_START,
278 horizontal_sync_pulse_start);
279 vga_cr_write (VGA_CR_HORIZ_SYNC_PULSE_END,
280 horizontal_sync_pulse_end);
281 vga_cr_write (VGA_CR_VERT_TOTAL, vertical_total & 0xff);
282 vga_cr_write (VGA_CR_OVERFLOW, overflow);
283 vga_cr_write (VGA_CR_CELL_HEIGHT, cell_height_reg);
284 vga_cr_write (VGA_CR_VSYNC_START, vertical_sync_start & 0xff);
285 vga_cr_write (VGA_CR_VSYNC_END, vertical_sync_end & 0x0f);
286 vga_cr_write (VGA_CR_VDISPLAY_END, vdisplay_end & 0xff);
287 vga_cr_write (VGA_CR_PITCH, pitch & 0xff);
288 vga_cr_write (VGA_CR_VERTICAL_BLANK_START, vertical_blank_start & 0xff);
289 vga_cr_write (VGA_CR_VERTICAL_BLANK_END, vertical_blank_end & 0xff);
290 vga_cr_write (VGA_CR_LINE_COMPARE, line_compare & 0xff);
291
292 vga_gr_write (VGA_GR_MODE, VGA_GR_MODE_256_COLOR | VGA_GR_MODE_READ_MODE1);
293 vga_gr_write (VGA_GR_GR6, VGA_GR_GR6_GRAPHICS_MODE);
294
295 vga_sr_write (VGA_SR_MEMORY_MODE, VGA_SR_MEMORY_MODE_NORMAL);
296
297 vga_cr_write (CIRRUS_CR_EXTENDED_DISPLAY,
298 (pitch >> CIRRUS_CR_EXTENDED_DISPLAY_PITCH_SHIFT)
299 & CIRRUS_CR_EXTENDED_DISPLAY_PITCH_MASK);
300
301 vga_cr_write (VGA_CR_MODE, VGA_CR_MODE_TIMING_ENABLE
302 | VGA_CR_MODE_BYTE_MODE
303 | VGA_CR_MODE_NO_HERCULES | VGA_CR_MODE_NO_CGA);
304
305 vga_cr_write (VGA_CR_START_ADDR_LOW_REGISTER, 0);
306 vga_cr_write (VGA_CR_START_ADDR_HIGH_REGISTER, 0);
307
308 cr_ext = vga_cr_read (CIRRUS_CR_EXTENDED_DISPLAY);
309 cr_ext &= ~(CIRRUS_CR_EXTENDED_DISPLAY_START_MASK1
310 | CIRRUS_CR_EXTENDED_DISPLAY_START_MASK2);
311 vga_cr_write (CIRRUS_CR_EXTENDED_DISPLAY, cr_ext);
312
313 cr_overlay = vga_cr_read (CIRRUS_CR_EXTENDED_OVERLAY);
314 cr_overlay &= ~(CIRRUS_CR_EXTENDED_OVERLAY_DISPLAY_START_MASK);
315 vga_cr_write (CIRRUS_CR_EXTENDED_OVERLAY, cr_overlay);
316
317 sr_ext = CIRRUS_SR_EXTENDED_MODE_LFB_ENABLE
318 | CIRRUS_SR_EXTENDED_MODE_ENABLE_EXT
319 | CIRRUS_SR_EXTENDED_MODE_32BPP;
320 hidden_dac = CIRRUS_HIDDEN_DAC_888COLOR;
321 vga_sr_write (CIRRUS_SR_EXTENDED_MODE, sr_ext);
322 write_hidden_dac (hidden_dac);
323
324
325 struct edid edid;
Gerd Hoffmann0bc3e3252015-09-04 12:58:00 +0200326 edid.mode.ha = width;
327 edid.mode.va = height;
Gerd Hoffmann2177f1b2014-08-27 11:23:35 +0200328 edid.panel_bits_per_color = 8;
329 edid.panel_bits_per_pixel = 24;
Julius Werner2b6db972016-04-06 12:50:40 -0700330 edid_set_framebuffer_bits_per_pixel(&edid, 32, 0);
Vladimir Serbinenko7905f922013-11-25 11:20:20 +0100331 set_vbe_mode_info_valid(&edid, addr);
Nico Huber6d8266b2017-05-20 16:46:01 +0200332}
Vladimir Serbinenkodb7d04d2014-02-22 10:35:45 +0100333
Nico Huber6d8266b2017-05-20 16:46:01 +0200334static void cirrus_init_text_mode(struct device *dev)
335{
336 vga_misc_write(0x1);
Vladimir Serbinenkodb7d04d2014-02-22 10:35:45 +0100337 vga_textmode_init();
Nico Huber6d8266b2017-05-20 16:46:01 +0200338}
339
340static void cirrus_init(struct device *dev)
341{
342 if (IS_ENABLED(CONFIG_LINEAR_FRAMEBUFFER))
343 cirrus_init_linear_fb(dev);
344 else if (IS_ENABLED(CONFIG_VGA_TEXT_FRAMEBUFFER))
345 cirrus_init_text_mode(dev);
Vladimir Serbinenko7905f922013-11-25 11:20:20 +0100346}
347
348static struct device_operations qemu_cirrus_graph_ops = {
349 .read_resources = pci_dev_read_resources,
350 .set_resources = pci_dev_set_resources,
351 .enable_resources = pci_dev_enable_resources,
352 .init = cirrus_init,
353 .scan_bus = 0,
354};
355
356static const struct pci_driver qemu_cirrus_driver __pci_driver = {
357 .ops = &qemu_cirrus_graph_ops,
358 .vendor = 0x1013,
359 .device = 0x00b8,
360};