1/* $NetBSD: vreset.c,v 1.11 2023/05/06 21:34:40 andvar Exp $ */
2/*-
3 * Copyright (c) 2006 The NetBSD Foundation, Inc.
4 * All rights reserved.
5 *
6 * This code is derived from software contributed to The NetBSD Foundation
7 * by Tim Rightnour
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
12 * 1. Redistributions of source code must retain the above copyright
13 *    notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 *    notice, this list of conditions and the following disclaimer in the
16 *    documentation and/or other materials provided with the distribution.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
19 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
20 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
21 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
22 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
23 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
26 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28 * POSSIBILITY OF SUCH DAMAGE.
29 */
30
31#ifdef VGA_RESET
32#include <lib/libsa/stand.h>
33#include "boot.h"
34#include "iso_font.h"
35
36#define VGA_SR_PORT	0x3c4
37#define VGA_CR_PORT	0x3d4
38#define VGA_CR_DATA	0x3d5
39#define VGA_GR_PORT	0x3ce
40#define VGA_GR_DATA	0x3cf
41#define SRREGS	4
42#define CRREGS	24
43#define GRREGS	9
44#define LINES 25
45#define COLS 80
46#define PCI_VENDOR_S3		0x5333
47#define PCI_VENDOR_CIRRUS	0x1013
48#define PCI_VENDOR_DIAMOND	0x100E
49#define PCI_VENDOR_MATROX	0x102B
50#define PCI_VENDOR_PARADISE	0x101C
51
52static void write_attr(u_int8_t, u_int8_t, u_int8_t);
53static void set_text_regs(void);
54static void set_text_clut(int);
55static void load_font(u_int8_t *);
56static void unlock_S3(void);
57static void clear_video_memory(void);
58
59extern char *videomem;
60
61typedef struct vga_reg {
62	u_int8_t idx;
63	u_int8_t val;
64} vga_reg_t;
65
66static vga_reg_t SR_regs[SRREGS] = {
67	/* idx  val */
68	{ 0x1,	0x0 },	/* 01: clocking mode */
69	{ 0x2,	0x3 },	/* 02: map mask */
70	{ 0x3,	0x0 },	/* 03: character map select */
71	{ 0x4,	0x2 }	/* 04: memory mode */
72};
73
74static vga_reg_t CR_regs[CRREGS] = {
75	/* idx  val */
76	{ 0x0,	0x61 }, /* 00: horizontal total */
77	{ 0x1,	0x4f }, /* 01: horizontal display-enable end */
78	{ 0x2,	0x50 }, /* 02: start horizontal blanking */
79	{ 0x3,	0x82 }, /* 03: display skew control / end horizontal blanking */
80	{ 0x4,	0x55 }, /* 04: start horizontal retrace pulse */
81	{ 0x5,	0x81 }, /* 05: horizontal retrace delay / end horiz. retrace */
82	{ 0x6,	0xf0 }, /* 06: vertical total */
83	{ 0x7,	0x1f }, /* 07: overflow register */
84	{ 0x8,	0x00 }, /* 08: preset row scan */
85	{ 0x9,	0x4f }, /* 09: overflow / maximum scan line */
86	{ 0xa,	0x0d }, /* 0A: cursor off / cursor start */
87	{ 0xb,	0x0e }, /* 0B: cursor skew / cursor end */
88	{ 0xc,	0x00 }, /* 0C: start regenerative buffer address high */
89	{ 0xd,	0x00 }, /* 0D: start regenerative buffer address low */
90	{ 0xe,	0x00 }, /* 0E: cursor location high */
91	{ 0xf,	0x00 }, /* 0F: cursor location low */
92	{ 0x10, 0x9a }, /* 10: vertical retrace start */
93	{ 0x11, 0x8c }, /* 11: vertical interrupt / vertical retrace end */
94	{ 0x12, 0x8f }, /* 12: vertical display enable end */
95	{ 0x13, 0x28 }, /* 13: logical line width */
96	{ 0x14, 0x1f }, /* 14: underline location */
97	{ 0x15, 0x97 }, /* 15: start vertical blanking */
98	{ 0x16, 0x00 }, /* 16: end vertical blanking */
99	{ 0x17, 0xa3 }, /* 17: CRT mode control */
100};
101
102static vga_reg_t GR_regs[GRREGS] = {
103	/* idx  val */
104	{ 0x0,	0x00 }, /* 00: set/reset map */
105	{ 0x1,	0x00 }, /* 01: enable set/reset */
106	{ 0x2,	0x00 }, /* 02: color compare */
107	{ 0x3,	0x00 }, /* 03: data rotate */
108	{ 0x4,	0x00 }, /* 04: read map select */
109	{ 0x5,	0x10 }, /* 05: graphics mode */
110	{ 0x6,	0x0e }, /* 06: miscellaneous */
111	{ 0x7,	0x00 }, /* 07: color don't care */
112	{ 0x8,	0xff }, /* 08: bit mask */
113};
114
115/* video DAC palette registers */
116/* XXX only set up 16 colors used by internal palette in ATC registers */
117static const u_int8_t vga_dacpal[] = {
118	/* R     G     B */
119	0x00, 0x00, 0x00,	/* BLACK        */
120	0x00, 0x00, 0x2a,	/* BLUE         */
121	0x00, 0x2a, 0x00,	/* GREEN        */
122	0x00, 0x2a, 0x2a,	/* CYAN         */
123	0x2a, 0x00, 0x00,	/* RED          */
124	0x2a, 0x00, 0x2a,	/* MAGENTA      */
125	0x2a, 0x15, 0x00,	/* BROWN        */
126	0x2a, 0x2a, 0x2a,	/* LIGHTGREY    */
127	0x15, 0x15, 0x15,	/* DARKGREY     */
128	0x15, 0x15, 0x3f,	/* LIGHTBLUE    */
129	0x15, 0x3f, 0x15,	/* LIGHTGREEN   */
130	0x15, 0x3f, 0x3f,	/* LIGHTCYAN    */
131	0x3f, 0x15, 0x15,	/* LIGHTRED     */
132	0x3f, 0x15, 0x3f,	/* LIGHTMAGENTA */
133	0x3f, 0x3f, 0x15,	/* YELLOW       */
134	0x3f, 0x3f, 0x3f	/* WHITE        */
135};
136
137static const u_int8_t vga_atc[] = {
138	0x00,	/* 00: internal palette  0 */
139	0x01,	/* 01: internal palette  1 */
140	0x02,	/* 02: internal palette  2 */
141	0x03,	/* 03: internal palette  3 */
142	0x04,	/* 04: internal palette  4 */
143	0x05,	/* 05: internal palette  5 */
144	0x14,	/* 06: internal palette  6 */
145	0x07,	/* 07: internal palette  7 */
146	0x38,	/* 08: internal palette  8 */
147	0x39,	/* 09: internal palette  9 */
148	0x3a,	/* 0A: internal palette 10 */
149	0x3b,	/* 0B: internal palette 11 */
150	0x3c,	/* 0C: internal palette 12 */
151	0x3d,	/* 0D: internal palette 13 */
152	0x3e,	/* 0E: internal palette 14 */
153	0x3f,	/* 0F: internal palette 15 */
154	0x0c,	/* 10: attribute mode control */
155	0x00,	/* 11: overscan color */
156	0x0f,	/* 12: color plane enable */
157	0x08,	/* 13: horizontal PEL panning */
158	0x00	/* 14: color select */
159};
160
161void
162vga_reset(u_char *ISA_mem)
163{
164	int slot, cardfound;
165
166	/* check if we are in text mode, if so, punt */
167	outb(VGA_GR_PORT, 0x06);
168	if ((inb(VGA_GR_DATA) & 0x01) == 0)
169		return;
170
171	/* guess not, we lose. */
172	slot = -1;
173	while ((slot = scan_PCI(slot)) > -1) {
174		cardfound = 0;
175		switch (PCI_vendor(slot)) {
176		case PCI_VENDOR_CIRRUS:
177			unlockVideo(slot);
178			outw(VGA_SR_PORT, 0x0612); /* unlock ext regs */
179			outw(VGA_SR_PORT, 0x0700); /* reset ext sequence mode */
180			cardfound++;
181			break;
182		case PCI_VENDOR_PARADISE:
183			unlockVideo(slot);
184			outw(VGA_GR_PORT, 0x0f05); /* unlock registers */
185			outw(VGA_SR_PORT, 0x0648);
186			outw(VGA_CR_PORT, 0x2985);
187			outw(VGA_CR_PORT, 0x34a6);
188			outb(VGA_GR_PORT, 0x0b); /* disable linear addressing */
189			outb(VGA_GR_DATA, inb(VGA_GR_DATA) & ~0x30);
190			outw(VGA_SR_PORT, 0x1400);
191			outb(VGA_GR_PORT, 0x0e); /* disable 256 color mode */
192			outb(VGA_GR_DATA, inb(VGA_GR_DATA) & ~0x01);
193			outb(0xd00, 0xff); /* enable auto-centering */
194			if (!(inb(0xd01) & 0x03)) {
195				outb(VGA_CR_PORT, 0x33);
196				outb(VGA_CR_DATA, inb(VGA_CR_DATA) & ~0x90);
197				outb(VGA_CR_PORT, 0x32);
198				outb(VGA_CR_DATA, inb(VGA_CR_DATA) | 0x04);
199				outw(VGA_CR_PORT, 0x0250);
200				outw(VGA_CR_PORT, 0x07ba);
201				outw(VGA_CR_PORT, 0x0900);
202				outw(VGA_CR_PORT, 0x15e7);
203				outw(VGA_CR_PORT, 0x2a95);
204			}
205			outw(VGA_CR_PORT, 0x34a0);
206			cardfound++;
207			break;
208		case PCI_VENDOR_S3:
209			unlockVideo(slot);
210			unlock_S3();
211			cardfound++;
212			break;
213		default:
214			break;
215		}
216		if (cardfound) {
217			outw(VGA_SR_PORT, 0x0120); /* disable video */
218			set_text_regs();
219			set_text_clut(0);
220			load_font(ISA_mem);
221			set_text_regs();
222			outw(VGA_SR_PORT, 0x0100); /* re-enable video */
223			clear_video_memory();
224
225			if (PCI_vendor(slot) == PCI_VENDOR_S3)
226				outb(0x3c2, 0x63);	/* ??? */
227			delay(1000);
228		}
229	}
230	return;
231}
232
233/* write something to a VGA attribute register */
234static void
235write_attr(u_int8_t index, u_int8_t data, u_int8_t videoOn)
236{
237
238	(void)inb(0x3da);	/* reset attr addr toggle */
239	if (videoOn)
240		outb(0x3c0, (index & 0x1F) | 0x20);
241	else
242		outb(0x3c0, (index & 0x1F));
243	outb(0x3c0, data);
244}
245
246static void
247set_text_regs(void)
248{
249	int i;
250
251	for (i = 0; i < SRREGS; i++) {
252		outb(VGA_SR_PORT, SR_regs[i].idx);
253		outb(VGA_SR_PORT + 1, SR_regs[i].val);
254	}
255	for (i = 0; i < CRREGS; i++) {
256		outb(VGA_CR_PORT, CR_regs[i].idx);
257		outb(VGA_CR_PORT + 1, CR_regs[i].val);
258	}
259	for (i = 0; i < GRREGS; i++) {
260		outb(VGA_GR_PORT, GR_regs[i].idx);
261		outb(VGA_GR_PORT + 1, GR_regs[i].val);
262	}
263
264	outb(0x3c2, 0x67);  /* MISC */
265	outb(0x3c6, 0xff);  /* MASK */
266
267	for (i = 0; i < 0x14; i++)
268		write_attr(i, vga_atc[i], 0);
269	write_attr(0x14, 0x00, 1); /* color select; video on  */
270}
271
272static void
273set_text_clut(int shift)
274{
275	int i;
276
277	outb(0x3C6, 0xFF);
278	inb(0x3C7);
279	outb(0x3C8, 0);
280	inb(0x3C7);
281
282	for (i = 0; i < (16 * 3); ) {
283		outb(0x3c9, vga_dacpal[i++] << shift);
284		outb(0x3c9, vga_dacpal[i++] << shift);
285		outb(0x3c9, vga_dacpal[i++] << shift);
286	}
287}
288
289static void
290load_font(u_int8_t *ISA_mem)
291{
292	int i, j;
293	u_int8_t *font_page = (u_int8_t *)&ISA_mem[0xA0000];
294
295	outb(0x3C2, 0x67);
296	inb(0x3DA);  /* Reset Attr toggle */
297
298	outb(0x3C0, 0x30);
299	outb(0x3C0, 0x01);	/* graphics mode */
300	outw(0x3C4, 0x0001);	/* reset sequencer */
301	outw(0x3C4, 0x0204);	/* write to plane 2 */
302	outw(0x3C4, 0x0406);	/* enable plane graphics */
303	outw(0x3C4, 0x0003);	/* reset sequencer */
304	outw(0x3CE, 0x0402);	/* read plane 2 */
305	outw(0x3CE, 0x0500);	/* write mode 0, read mode 0 */
306	outw(0x3CE, 0x0605);	/* set graphics mode */
307
308	for (i = 0;  i < sizeof(font);  i += 16) {
309		for (j = 0;  j < 16;  j++) {
310			__asm__ volatile("eieio" ::: "memory");
311			font_page[(2*i)+j] = font[i+j];
312		}
313	}
314}
315
316static void
317unlock_S3(void)
318{
319	int s3_devid;
320
321	outw(VGA_CR_PORT, 0x3848);
322	outw(VGA_CR_PORT, 0x39a5);
323	outb(VGA_CR_PORT, 0x2d);
324	s3_devid = inb(VGA_CR_DATA) << 8;
325	outb(VGA_CR_PORT, 0x2e);
326	s3_devid |= inb(VGA_CR_DATA);
327
328	if (s3_devid != 0x8812) {
329		/* from the S3 manual */
330		outb(0x46E8, 0x10);  /* Put into setup mode */
331		outb(0x3C3, 0x10);
332		outb(0x102, 0x01);   /* Enable registers */
333		outb(0x46E8, 0x08);  /* Enable video */
334		outb(0x3C3, 0x08);
335		outb(0x4AE8, 0x00);
336                outb(VGA_CR_PORT, 0x38);  /* Unlock all registers */
337		outb(VGA_CR_DATA, 0x48);
338		outb(VGA_CR_PORT, 0x39);
339		outb(VGA_CR_DATA, 0xA5);
340		outb(VGA_CR_PORT, 0x40);
341		outb(VGA_CR_DATA, inb(0x3D5)|0x01);
342		outb(VGA_CR_PORT, 0x33);
343		outb(VGA_CR_DATA, inb(0x3D5)&~0x52);
344		outb(VGA_CR_PORT, 0x35);
345		outb(VGA_CR_DATA, inb(0x3D5)&~0x30);
346		outb(VGA_CR_PORT, 0x3A);
347		outb(VGA_CR_DATA, 0x00);
348		outb(VGA_CR_PORT, 0x53);
349		outb(VGA_CR_DATA, 0x00);
350		outb(VGA_CR_PORT, 0x31);
351		outb(VGA_CR_DATA, inb(0x3D5)&~0x4B);
352		outb(VGA_CR_PORT, 0x58);
353
354		outb(VGA_CR_DATA, 0);
355
356		outb(VGA_CR_PORT, 0x54);
357		outb(VGA_CR_DATA, 0x38);
358		outb(VGA_CR_PORT, 0x60);
359		outb(VGA_CR_DATA, 0x07);
360		outb(VGA_CR_PORT, 0x61);
361		outb(VGA_CR_DATA, 0x80);
362		outb(VGA_CR_PORT, 0x62);
363		outb(VGA_CR_DATA, 0xA1);
364		outb(VGA_CR_PORT, 0x69);  /* High order bits for cursor address */
365		outb(VGA_CR_DATA, 0);
366
367		outb(VGA_CR_PORT, 0x32);
368		outb(VGA_CR_DATA, inb(0x3D5)&~0x10);
369	} else {
370		/* IBM Portable 860 */
371		outw(VGA_SR_PORT, 0x0806);
372		outw(VGA_SR_PORT, 0x1041);
373		outw(VGA_SR_PORT, 0x1128);
374		outw(VGA_CR_PORT, 0x4000);
375		outw(VGA_CR_PORT, 0x3100);
376		outw(VGA_CR_PORT, 0x3a05);
377		outw(VGA_CR_PORT, 0x6688);
378		outw(VGA_CR_PORT, 0x5800); /* disable linear addressing */
379		outw(VGA_CR_PORT, 0x4500); /* disable H/W cursor */
380		outw(VGA_SR_PORT, 0x5410); /* enable auto-centering */
381		outw(VGA_SR_PORT, 0x561f);
382		outw(VGA_SR_PORT, 0x1b80); /* lock DCLK selection */
383		outw(VGA_CR_PORT, 0x3900); /* lock S3 registers */
384		outw(VGA_CR_PORT, 0x3800);
385	}
386}
387
388static void
389clear_video_memory(void)
390{
391	int i, j;
392
393	for (i = 0;  i < LINES; i++) {
394		for (j = 0; j < COLS; j++) {
395			videomem[((i * COLS)+j) * 2] = 0x20; /* space */
396			videomem[((i * COLS)+j) * 2 + 1] = 0x07; /* fg/bg */
397		}
398	}
399}
400
401#endif /* VGA_RESET */
402