blob: 69dfd46e550a8865734def85c62d675211657001 [file] [log] [blame]
Gerd Hoffmann34b6ecc2019-02-25 10:51:37 +01001// QEMU ATI VGABIOS Extension.
2//
3// This file may be distributed under the terms of the GNU LGPLv3 license.
4
5#include "biosvar.h" // GET_GLOBAL
6#include "bregs.h" // struct bregs
7#include "hw/pci.h" // pci_config_readl
8#include "hw/pci_regs.h" // PCI_BASE_ADDRESS_0
9#include "output.h" // dprintf
10#include "stdvga.h" // VGAREG_SEQU_ADDRESS
11#include "string.h" // memset16_far
12#include "vgabios.h" // SET_VGA
13#include "vgautil.h" // VBE_total_memory
14#include "vgafb.h" // memset_high
15
16#include "svgamodes.h"
17
18#define MM_INDEX 0x0000
19#define MM_DATA 0x0004
20#define CRTC_GEN_CNTL 0x0050
21#define CRTC_EXT_CNTL 0x0054
Gerd Hoffmann588eb122019-03-18 15:43:53 +010022#define GPIO_VGA_DDC 0x0060
23#define GPIO_DVI_DDC 0x0064
Gerd Hoffmann020bc4f2019-06-24 13:35:46 +020024#define GPIO_MONID 0x0068
Gerd Hoffmann34b6ecc2019-02-25 10:51:37 +010025#define CRTC_H_TOTAL_DISP 0x0200
26#define CRTC_V_TOTAL_DISP 0x0208
27#define CRTC_OFFSET 0x0224
28#define CRTC_PITCH 0x022c
29
30/* CRTC control values (CRTC_GEN_CNTL) */
31#define CRTC2_EXT_DISP_EN 0x01000000
32#define CRTC2_EN 0x02000000
33
34#define CRTC_PIX_WIDTH_MASK 0x00000700
35#define CRTC_PIX_WIDTH_4BPP 0x00000100
36#define CRTC_PIX_WIDTH_8BPP 0x00000200
37#define CRTC_PIX_WIDTH_15BPP 0x00000300
38#define CRTC_PIX_WIDTH_16BPP 0x00000400
39#define CRTC_PIX_WIDTH_24BPP 0x00000500
40#define CRTC_PIX_WIDTH_32BPP 0x00000600
41
42/* CRTC_EXT_CNTL */
43#define CRT_CRTC_DISPLAY_DIS 0x00000400
44#define CRT_CRTC_ON 0x00008000
45
46static u32 ati_io_addr VAR16 = 0;
Gerd Hoffmannae9a9792019-06-24 12:08:32 +020047static u32 ati_i2c_reg VAR16;
48static u32 ati_i2c_bit_scl_out VAR16;
49static u32 ati_i2c_bit_sda_out VAR16;
50static u32 ati_i2c_bit_sda_in VAR16;
Gerd Hoffmann020bc4f2019-06-24 13:35:46 +020051static u32 ati_i2c_bit_enable VAR16 = -1;
Gerd Hoffmannae9a9792019-06-24 12:08:32 +020052
Gerd Hoffmann34b6ecc2019-02-25 10:51:37 +010053
54int
55is_ati_mode(struct vgamode_s *vmode_g)
56{
57 unsigned int mcount = GET_GLOBAL(svga_mcount);
58
59 return (vmode_g >= &svga_modes[0].info &&
60 vmode_g <= &svga_modes[mcount-1].info);
61}
62
63struct vgamode_s *
64ati_find_mode(int mode)
65{
66 u32 io_addr = GET_GLOBAL(ati_io_addr);
67 struct generic_svga_mode *table_g = svga_modes;
68 unsigned int mcount = GET_GLOBAL(svga_mcount);
69
70 if (io_addr) {
71 while (table_g < &svga_modes[mcount]) {
72 if (GET_GLOBAL(table_g->mode) == mode)
73 return &table_g->info;
74 table_g++;
75 }
76 }
77
78 return stdvga_find_mode(mode);
79}
80
81void
82ati_list_modes(u16 seg, u16 *dest, u16 *last)
83{
84 u32 io_addr = GET_GLOBAL(ati_io_addr);
85 unsigned int mcount = GET_GLOBAL(svga_mcount);
86
87 dprintf(1, "%s: ati ext %s\n", __func__, io_addr ? "yes" : "no");
88 if (io_addr) {
89 int i;
90 for (i=0; i<mcount && dest<last; i++) {
91 u16 mode = GET_GLOBAL(svga_modes[i].mode);
92 if (mode == 0xffff)
93 continue;
94 SET_FARVAR(seg, *dest, mode);
95 dest++;
96 }
97 }
98
99 stdvga_list_modes(seg, dest, last);
100}
101
102/****************************************************************
103 * Mode setting
104 ****************************************************************/
105
106static inline void ati_write(u32 reg, u32 val)
107{
108 u32 io_addr = GET_GLOBAL(ati_io_addr);
109
110 if (reg < 0x100) {
111 outl(val, io_addr + reg);
112 } else {
113 outl(reg, io_addr + MM_INDEX);
114 outl(val, io_addr + MM_DATA);
115 }
116}
117
Gerd Hoffmann588eb122019-03-18 15:43:53 +0100118static inline u32 ati_read(u32 reg)
119{
120 u32 io_addr = GET_GLOBAL(ati_io_addr);
121 u32 val;
122
123 if (reg < 0x100) {
124 val = inl(io_addr + reg);
125 } else {
126 outl(reg, io_addr + MM_INDEX);
Gerd Hoffmann136e2612019-06-24 12:09:31 +0200127 val = inl(io_addr + MM_DATA);
Gerd Hoffmann588eb122019-03-18 15:43:53 +0100128 }
129 return val;
130}
131
Gerd Hoffmann34b6ecc2019-02-25 10:51:37 +0100132static void ati_clear(u32 offset, u32 size)
133{
134 u8 data[64];
135 void *datap = MAKE_FLATPTR(GET_SEG(SS), data);
136 void *fb = (void*)(GET_GLOBAL(VBE_framebuffer) + offset);
137 u32 i, pos;
138
139 for (i = 0; i < sizeof(data); i++)
140 data[i] = 0;
141 for (pos = 0; pos < size; pos += sizeof(data)) {
142 memcpy_high(fb, datap, sizeof(data));
143 fb += sizeof(data);
144 }
145}
146
147static int
148ati_ext_mode(struct generic_svga_mode *table, int flags)
149{
150 u32 width = GET_GLOBAL(table->info.width);
151 u32 height = GET_GLOBAL(table->info.height);
152 u32 depth = GET_GLOBAL(table->info.depth);
153 u32 stride = width;
154 u32 offset = 0;
155 u32 pxmask = 0;
156 u32 bytes = 0;
157
158 dprintf(1, "%s: 0x%x, %dx%d-%d\n", __func__,
159 GET_GLOBAL(table->mode),
160 width, height, depth);
161
162 switch (depth) {
163 case 8: pxmask = CRTC_PIX_WIDTH_8BPP; bytes = 1; break;
164 case 15: pxmask = CRTC_PIX_WIDTH_15BPP; bytes = 2; break;
165 case 16: pxmask = CRTC_PIX_WIDTH_16BPP; bytes = 2; break;
166 case 24: pxmask = CRTC_PIX_WIDTH_24BPP; bytes = 3; break;
167 case 32: pxmask = CRTC_PIX_WIDTH_32BPP; bytes = 4; break;
168 }
169
170 /* disable display */
171 ati_write(CRTC_EXT_CNTL, CRT_CRTC_DISPLAY_DIS);
172
173 /* modeset */
174 ati_write(CRTC_GEN_CNTL, CRTC2_EXT_DISP_EN | CRTC2_EN | pxmask);
175 ati_write(CRTC_H_TOTAL_DISP, ((width / 8) - 1) << 16);
176 ati_write(CRTC_V_TOTAL_DISP, (height - 1) << 16);
177 ati_write(CRTC_OFFSET, offset);
178 ati_write(CRTC_PITCH, stride / 8);
179
180 /* clear screen */
181 if (!(flags & MF_NOCLEARMEM)) {
182 u32 size = width * height * bytes;
183 ati_clear(offset, size);
184 }
185
186 /* enable display */
187 ati_write(CRTC_EXT_CNTL, 0);
188
189 return 0;
190}
191
192int
193ati_set_mode(struct vgamode_s *vmode_g, int flags)
194{
195 struct generic_svga_mode *table_g =
196 container_of(vmode_g, struct generic_svga_mode, info);
197
198 if (is_ati_mode(vmode_g)) {
199 return ati_ext_mode(table_g, flags);
200 }
201
202 ati_write(CRTC_GEN_CNTL, 0);
203 return stdvga_set_mode(vmode_g, flags);
204}
205
206/****************************************************************
Gerd Hoffmann588eb122019-03-18 15:43:53 +0100207 * edid
208 ****************************************************************/
209
210static void
211ati_i2c_set_scl_sda(int scl, int sda)
212{
Gerd Hoffmann020bc4f2019-06-24 13:35:46 +0200213 u32 enable = GET_GLOBAL(ati_i2c_bit_enable);
Gerd Hoffmann588eb122019-03-18 15:43:53 +0100214 u32 data = 0;
215
Gerd Hoffmann020bc4f2019-06-24 13:35:46 +0200216 if (enable != -1)
217 data |= (1 << enable);
Gerd Hoffmann588eb122019-03-18 15:43:53 +0100218 if (!scl)
Gerd Hoffmannae9a9792019-06-24 12:08:32 +0200219 data |= (1 << GET_GLOBAL(ati_i2c_bit_scl_out));
Gerd Hoffmann588eb122019-03-18 15:43:53 +0100220 if (!sda)
Gerd Hoffmannae9a9792019-06-24 12:08:32 +0200221 data |= (1 << GET_GLOBAL(ati_i2c_bit_sda_out));
222 ati_write(GET_GLOBAL(ati_i2c_reg), data);
Gerd Hoffmann588eb122019-03-18 15:43:53 +0100223}
224
225static int
226ati_i2c_get_sda(void)
227{
Gerd Hoffmannae9a9792019-06-24 12:08:32 +0200228 u32 data = ati_read(GET_GLOBAL(ati_i2c_reg));
Gerd Hoffmann588eb122019-03-18 15:43:53 +0100229
Gerd Hoffmannae9a9792019-06-24 12:08:32 +0200230 return data & (1 << GET_GLOBAL(ati_i2c_bit_sda_in)) ? 1 : 0;
Gerd Hoffmann588eb122019-03-18 15:43:53 +0100231}
232
233static void ati_i2c_start(void)
234{
235 ati_i2c_set_scl_sda(1, 1);
236 ati_i2c_set_scl_sda(1, 0);
237 ati_i2c_set_scl_sda(0, 0);
238}
239
240static void ati_i2c_ack(void)
241{
242 ati_i2c_set_scl_sda(0, 0);
243 ati_i2c_set_scl_sda(1, 0);
244 ati_i2c_set_scl_sda(0, 0);
245}
246
247static void ati_i2c_stop(void)
248{
249 ati_i2c_set_scl_sda(0, 0);
250 ati_i2c_set_scl_sda(1, 0);
251 ati_i2c_set_scl_sda(1, 1);
252}
253
254static void ati_i2c_send_byte(u8 byte)
255{
256 int i, bit;
257
258 for (i = 0; i < 8; i++) {
259 bit = (1 << (7-i)) & byte ? 1 : 0;
260 ati_i2c_set_scl_sda(0, bit);
261 ati_i2c_set_scl_sda(1, bit);
262 ati_i2c_set_scl_sda(0, bit);
263 }
264}
265
266static u8 ati_i2c_recv_byte(void)
267{
268 u8 byte = 0;
269 int i, bit;
270
271 for (i = 0; i < 8; i++) {
272 ati_i2c_set_scl_sda(0, 1);
273 ati_i2c_set_scl_sda(1, 1);
274 bit = ati_i2c_get_sda();
275 ati_i2c_set_scl_sda(0, 1);
276 if (bit)
277 byte |= (1 << (7-i));
278 }
279
280 return byte;
281}
282
283static void ati_i2c_edid(void)
284{
285 u8 byte;
286 int i;
287
Gerd Hoffmann588eb122019-03-18 15:43:53 +0100288 ati_i2c_start();
289 ati_i2c_send_byte(0x50 << 1 | 1);
290 ati_i2c_ack();
291 for (i = 0; i < 128; i++) {
292 byte = ati_i2c_recv_byte();
293 ati_i2c_ack();
294 SET_VGA(VBE_edid[i], byte);
295 }
296 ati_i2c_stop();
297}
298
Gerd Hoffmannae9a9792019-06-24 12:08:32 +0200299static void ati_i2c_edid_radeon(void)
300{
301 int valid;
302
303 SET_VGA(ati_i2c_bit_scl_out, 17);
304 SET_VGA(ati_i2c_bit_sda_out, 16);
305 SET_VGA(ati_i2c_bit_sda_in, 8);
306
Gerd Hoffmannaf573dc2019-06-24 12:17:28 +0200307 dprintf(1, "ati: reading edid blob (radeon vga) ... \n");
308 SET_VGA(ati_i2c_reg, GPIO_VGA_DDC);
309 ati_i2c_edid();
310 valid = (GET_GLOBAL(VBE_edid[0]) == 0x00 &&
311 GET_GLOBAL(VBE_edid[1]) == 0xff);
312 dprintf(1, "ati: ... %s\n", valid ? "good" : "invalid");
313 if (valid)
314 return;
315
Gerd Hoffmannae9a9792019-06-24 12:08:32 +0200316 dprintf(1, "ati: reading edid blob (radeon dvi) ... \n");
317 SET_VGA(ati_i2c_reg, GPIO_DVI_DDC);
318 ati_i2c_edid();
319 valid = (GET_GLOBAL(VBE_edid[0]) == 0x00 &&
320 GET_GLOBAL(VBE_edid[1]) == 0xff);
321 dprintf(1, "ati: ... %s\n", valid ? "good" : "invalid");
322}
323
Gerd Hoffmann020bc4f2019-06-24 13:35:46 +0200324static void ati_i2c_edid_rage128(void)
325{
326 int valid;
327
328 SET_VGA(ati_i2c_bit_enable, 25);
329 SET_VGA(ati_i2c_bit_scl_out, 18);
330 SET_VGA(ati_i2c_bit_sda_out, 17);
331 SET_VGA(ati_i2c_bit_sda_in, 9);
332 SET_VGA(ati_i2c_reg, GPIO_MONID);
333
334 dprintf(1, "ati: reading edid blob (rage128) ... \n");
335 ati_i2c_edid();
336 valid = (GET_GLOBAL(VBE_edid[0]) == 0x00 &&
337 GET_GLOBAL(VBE_edid[1]) == 0xff);
338 dprintf(1, "ati: ... %s\n", valid ? "good" : "invalid");
339}
340
Gerd Hoffmann588eb122019-03-18 15:43:53 +0100341/****************************************************************
Gerd Hoffmann34b6ecc2019-02-25 10:51:37 +0100342 * init
343 ****************************************************************/
344
345int
346ati_setup(void)
347{
348 int ret = stdvga_setup();
349 if (ret)
350 return ret;
351
352 dprintf(1, "%s:%d\n", __func__, __LINE__);
353
354 if (GET_GLOBAL(HaveRunInit))
355 return 0;
356
357 int bdf = GET_GLOBAL(VgaBDF);
358 if (!CONFIG_VGA_PCI || bdf == 0)
359 return 0;
360
361 u32 bar = pci_config_readl(bdf, PCI_BASE_ADDRESS_0);
362 u32 lfb_addr = bar & PCI_BASE_ADDRESS_MEM_MASK;
363 pci_config_writel(bdf, PCI_BASE_ADDRESS_0, ~0);
364 u32 barmask = pci_config_readl(bdf, PCI_BASE_ADDRESS_0);
365 u32 totalmem = ~(barmask & PCI_BASE_ADDRESS_MEM_MASK) + 1;
366 pci_config_writel(bdf, PCI_BASE_ADDRESS_0, bar);
367
368 bar = pci_config_readl(bdf, PCI_BASE_ADDRESS_1);
369 u32 io_addr = bar & PCI_BASE_ADDRESS_IO_MASK;
370
371 bar = pci_config_readl(bdf, PCI_BASE_ADDRESS_2);
372 u32 mmio_addr = bar & PCI_BASE_ADDRESS_MEM_MASK;
373
374 dprintf(1, "ati: bdf %02x:%02x.%x, lfb 0x%x, %d MB, io 0x%x, mmio 0x%x\n",
375 pci_bdf_to_bus(bdf), pci_bdf_to_dev(bdf), pci_bdf_to_fn(bdf),
376 lfb_addr, totalmem / (1024 * 1024), io_addr, mmio_addr);
377
378 SET_VGA(VBE_framebuffer, lfb_addr);
379 SET_VGA(VBE_total_memory, totalmem);
380 SET_VGA(ati_io_addr, io_addr);
381
382 // Validate modes
383 struct generic_svga_mode *m = svga_modes;
384 unsigned int mcount = GET_GLOBAL(svga_mcount);
385 for (; m < &svga_modes[mcount]; m++) {
386 u8 memmodel = GET_GLOBAL(m->info.memmodel);
387 u16 width = GET_GLOBAL(m->info.width);
388 u16 height = GET_GLOBAL(m->info.height);
389 u32 mem = (height * DIV_ROUND_UP(width * vga_bpp(&m->info), 8)
390 * stdvga_vram_ratio(&m->info));
391
392 if (width % 8 != 0 ||
393 width > 0x7ff * 8 ||
394 height > 0xfff ||
395 mem > totalmem ||
396 memmodel != MM_DIRECT) {
Gerd Hoffmannce52a312019-06-24 12:09:52 +0200397 dprintf(3, "ati: removing mode 0x%x\n", GET_GLOBAL(m->mode));
Gerd Hoffmann34b6ecc2019-02-25 10:51:37 +0100398 SET_VGA(m->mode, 0xffff);
399 }
400 }
401
Gerd Hoffmann588eb122019-03-18 15:43:53 +0100402 u16 device = pci_config_readw(bdf, PCI_DEVICE_ID);
403 switch (device) {
Gerd Hoffmann020bc4f2019-06-24 13:35:46 +0200404 case 0x5046:
405 ati_i2c_edid_rage128();
406 break;
Gerd Hoffmann588eb122019-03-18 15:43:53 +0100407 case 0x5159:
Gerd Hoffmannae9a9792019-06-24 12:08:32 +0200408 ati_i2c_edid_radeon();
Gerd Hoffmann588eb122019-03-18 15:43:53 +0100409 break;
410 }
411
Gerd Hoffmann34b6ecc2019-02-25 10:51:37 +0100412 return 0;
413}