root/sys/arch/prep/stand/boot/vreset.c
/* $NetBSD: vreset.c,v 1.11 2023/05/06 21:34:40 andvar Exp $ */
/*-
 * Copyright (c) 2006 The NetBSD Foundation, Inc.
 * All rights reserved.
 *
 * This code is derived from software contributed to The NetBSD Foundation
 * by Tim Rightnour
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

#ifdef VGA_RESET
#include <lib/libsa/stand.h>
#include "boot.h"
#include "iso_font.h"

#define VGA_SR_PORT     0x3c4
#define VGA_CR_PORT     0x3d4
#define VGA_CR_DATA     0x3d5
#define VGA_GR_PORT     0x3ce
#define VGA_GR_DATA     0x3cf
#define SRREGS  4
#define CRREGS  24
#define GRREGS  9
#define LINES 25
#define COLS 80
#define PCI_VENDOR_S3           0x5333
#define PCI_VENDOR_CIRRUS       0x1013
#define PCI_VENDOR_DIAMOND      0x100E
#define PCI_VENDOR_MATROX       0x102B
#define PCI_VENDOR_PARADISE     0x101C

static void write_attr(u_int8_t, u_int8_t, u_int8_t);
static void set_text_regs(void);
static void set_text_clut(int);
static void load_font(u_int8_t *);
static void unlock_S3(void);
static void clear_video_memory(void);

extern char *videomem;

typedef struct vga_reg {
        u_int8_t idx;
        u_int8_t val;
} vga_reg_t;

static vga_reg_t SR_regs[SRREGS] = {
        /* idx  val */
        { 0x1,  0x0 },  /* 01: clocking mode */
        { 0x2,  0x3 },  /* 02: map mask */
        { 0x3,  0x0 },  /* 03: character map select */
        { 0x4,  0x2 }   /* 04: memory mode */
};

static vga_reg_t CR_regs[CRREGS] = {
        /* idx  val */
        { 0x0,  0x61 }, /* 00: horizontal total */
        { 0x1,  0x4f }, /* 01: horizontal display-enable end */
        { 0x2,  0x50 }, /* 02: start horizontal blanking */
        { 0x3,  0x82 }, /* 03: display skew control / end horizontal blanking */
        { 0x4,  0x55 }, /* 04: start horizontal retrace pulse */
        { 0x5,  0x81 }, /* 05: horizontal retrace delay / end horiz. retrace */
        { 0x6,  0xf0 }, /* 06: vertical total */
        { 0x7,  0x1f }, /* 07: overflow register */
        { 0x8,  0x00 }, /* 08: preset row scan */
        { 0x9,  0x4f }, /* 09: overflow / maximum scan line */
        { 0xa,  0x0d }, /* 0A: cursor off / cursor start */
        { 0xb,  0x0e }, /* 0B: cursor skew / cursor end */
        { 0xc,  0x00 }, /* 0C: start regenerative buffer address high */
        { 0xd,  0x00 }, /* 0D: start regenerative buffer address low */
        { 0xe,  0x00 }, /* 0E: cursor location high */
        { 0xf,  0x00 }, /* 0F: cursor location low */
        { 0x10, 0x9a }, /* 10: vertical retrace start */
        { 0x11, 0x8c }, /* 11: vertical interrupt / vertical retrace end */
        { 0x12, 0x8f }, /* 12: vertical display enable end */
        { 0x13, 0x28 }, /* 13: logical line width */
        { 0x14, 0x1f }, /* 14: underline location */
        { 0x15, 0x97 }, /* 15: start vertical blanking */
        { 0x16, 0x00 }, /* 16: end vertical blanking */
        { 0x17, 0xa3 }, /* 17: CRT mode control */
};

static vga_reg_t GR_regs[GRREGS] = {
        /* idx  val */
        { 0x0,  0x00 }, /* 00: set/reset map */
        { 0x1,  0x00 }, /* 01: enable set/reset */
        { 0x2,  0x00 }, /* 02: color compare */
        { 0x3,  0x00 }, /* 03: data rotate */
        { 0x4,  0x00 }, /* 04: read map select */
        { 0x5,  0x10 }, /* 05: graphics mode */
        { 0x6,  0x0e }, /* 06: miscellaneous */
        { 0x7,  0x00 }, /* 07: color don't care */
        { 0x8,  0xff }, /* 08: bit mask */
};

/* video DAC palette registers */
/* XXX only set up 16 colors used by internal palette in ATC registers */
static const u_int8_t vga_dacpal[] = {
        /* R     G     B */
        0x00, 0x00, 0x00,       /* BLACK        */
        0x00, 0x00, 0x2a,       /* BLUE         */
        0x00, 0x2a, 0x00,       /* GREEN        */
        0x00, 0x2a, 0x2a,       /* CYAN         */
        0x2a, 0x00, 0x00,       /* RED          */
        0x2a, 0x00, 0x2a,       /* MAGENTA      */
        0x2a, 0x15, 0x00,       /* BROWN        */
        0x2a, 0x2a, 0x2a,       /* LIGHTGREY    */
        0x15, 0x15, 0x15,       /* DARKGREY     */
        0x15, 0x15, 0x3f,       /* LIGHTBLUE    */
        0x15, 0x3f, 0x15,       /* LIGHTGREEN   */
        0x15, 0x3f, 0x3f,       /* LIGHTCYAN    */
        0x3f, 0x15, 0x15,       /* LIGHTRED     */
        0x3f, 0x15, 0x3f,       /* LIGHTMAGENTA */
        0x3f, 0x3f, 0x15,       /* YELLOW       */
        0x3f, 0x3f, 0x3f        /* WHITE        */
};

static const u_int8_t vga_atc[] = {
        0x00,   /* 00: internal palette  0 */
        0x01,   /* 01: internal palette  1 */
        0x02,   /* 02: internal palette  2 */
        0x03,   /* 03: internal palette  3 */
        0x04,   /* 04: internal palette  4 */
        0x05,   /* 05: internal palette  5 */
        0x14,   /* 06: internal palette  6 */
        0x07,   /* 07: internal palette  7 */
        0x38,   /* 08: internal palette  8 */
        0x39,   /* 09: internal palette  9 */
        0x3a,   /* 0A: internal palette 10 */
        0x3b,   /* 0B: internal palette 11 */
        0x3c,   /* 0C: internal palette 12 */
        0x3d,   /* 0D: internal palette 13 */
        0x3e,   /* 0E: internal palette 14 */
        0x3f,   /* 0F: internal palette 15 */
        0x0c,   /* 10: attribute mode control */
        0x00,   /* 11: overscan color */
        0x0f,   /* 12: color plane enable */
        0x08,   /* 13: horizontal PEL panning */
        0x00    /* 14: color select */
};

void
vga_reset(u_char *ISA_mem)
{
        int slot, cardfound;

        /* check if we are in text mode, if so, punt */
        outb(VGA_GR_PORT, 0x06);
        if ((inb(VGA_GR_DATA) & 0x01) == 0)
                return;

        /* guess not, we lose. */
        slot = -1;
        while ((slot = scan_PCI(slot)) > -1) {
                cardfound = 0;
                switch (PCI_vendor(slot)) {
                case PCI_VENDOR_CIRRUS:
                        unlockVideo(slot);
                        outw(VGA_SR_PORT, 0x0612); /* unlock ext regs */
                        outw(VGA_SR_PORT, 0x0700); /* reset ext sequence mode */
                        cardfound++;
                        break;
                case PCI_VENDOR_PARADISE:
                        unlockVideo(slot);
                        outw(VGA_GR_PORT, 0x0f05); /* unlock registers */
                        outw(VGA_SR_PORT, 0x0648);
                        outw(VGA_CR_PORT, 0x2985);
                        outw(VGA_CR_PORT, 0x34a6);
                        outb(VGA_GR_PORT, 0x0b); /* disable linear addressing */
                        outb(VGA_GR_DATA, inb(VGA_GR_DATA) & ~0x30);
                        outw(VGA_SR_PORT, 0x1400);
                        outb(VGA_GR_PORT, 0x0e); /* disable 256 color mode */
                        outb(VGA_GR_DATA, inb(VGA_GR_DATA) & ~0x01);
                        outb(0xd00, 0xff); /* enable auto-centering */
                        if (!(inb(0xd01) & 0x03)) {
                                outb(VGA_CR_PORT, 0x33);
                                outb(VGA_CR_DATA, inb(VGA_CR_DATA) & ~0x90);
                                outb(VGA_CR_PORT, 0x32);
                                outb(VGA_CR_DATA, inb(VGA_CR_DATA) | 0x04);
                                outw(VGA_CR_PORT, 0x0250);
                                outw(VGA_CR_PORT, 0x07ba);
                                outw(VGA_CR_PORT, 0x0900);
                                outw(VGA_CR_PORT, 0x15e7);
                                outw(VGA_CR_PORT, 0x2a95);
                        }
                        outw(VGA_CR_PORT, 0x34a0);
                        cardfound++;
                        break;
                case PCI_VENDOR_S3:
                        unlockVideo(slot);
                        unlock_S3();
                        cardfound++;
                        break;
                default:
                        break;
                }
                if (cardfound) {
                        outw(VGA_SR_PORT, 0x0120); /* disable video */
                        set_text_regs();
                        set_text_clut(0);
                        load_font(ISA_mem);
                        set_text_regs();
                        outw(VGA_SR_PORT, 0x0100); /* re-enable video */
                        clear_video_memory();

                        if (PCI_vendor(slot) == PCI_VENDOR_S3)
                                outb(0x3c2, 0x63);      /* ??? */
                        delay(1000);
                }
        }
        return;
}

/* write something to a VGA attribute register */
static void
write_attr(u_int8_t index, u_int8_t data, u_int8_t videoOn)
{

        (void)inb(0x3da);       /* reset attr addr toggle */
        if (videoOn)
                outb(0x3c0, (index & 0x1F) | 0x20);
        else
                outb(0x3c0, (index & 0x1F));
        outb(0x3c0, data);
}

static void
set_text_regs(void)
{
        int i;

        for (i = 0; i < SRREGS; i++) {
                outb(VGA_SR_PORT, SR_regs[i].idx);
                outb(VGA_SR_PORT + 1, SR_regs[i].val);
        }
        for (i = 0; i < CRREGS; i++) {
                outb(VGA_CR_PORT, CR_regs[i].idx);
                outb(VGA_CR_PORT + 1, CR_regs[i].val);
        }
        for (i = 0; i < GRREGS; i++) {
                outb(VGA_GR_PORT, GR_regs[i].idx);
                outb(VGA_GR_PORT + 1, GR_regs[i].val);
        }

        outb(0x3c2, 0x67);  /* MISC */
        outb(0x3c6, 0xff);  /* MASK */

        for (i = 0; i < 0x14; i++)
                write_attr(i, vga_atc[i], 0); 
        write_attr(0x14, 0x00, 1); /* color select; video on  */
}

static void
set_text_clut(int shift)
{
        int i;

        outb(0x3C6, 0xFF);
        inb(0x3C7);
        outb(0x3C8, 0);
        inb(0x3C7);

        for (i = 0; i < (16 * 3); ) {
                outb(0x3c9, vga_dacpal[i++] << shift);
                outb(0x3c9, vga_dacpal[i++] << shift);
                outb(0x3c9, vga_dacpal[i++] << shift);
        }
}

static void
load_font(u_int8_t *ISA_mem)
{
        int i, j;
        u_int8_t *font_page = (u_int8_t *)&ISA_mem[0xA0000];

        outb(0x3C2, 0x67);
        inb(0x3DA);  /* Reset Attr toggle */

        outb(0x3C0, 0x30);
        outb(0x3C0, 0x01);      /* graphics mode */
        outw(0x3C4, 0x0001);    /* reset sequencer */
        outw(0x3C4, 0x0204);    /* write to plane 2 */
        outw(0x3C4, 0x0406);    /* enable plane graphics */
        outw(0x3C4, 0x0003);    /* reset sequencer */
        outw(0x3CE, 0x0402);    /* read plane 2 */
        outw(0x3CE, 0x0500);    /* write mode 0, read mode 0 */
        outw(0x3CE, 0x0605);    /* set graphics mode */

        for (i = 0;  i < sizeof(font);  i += 16) {
                for (j = 0;  j < 16;  j++) {
                        __asm__ volatile("eieio" ::: "memory");
                        font_page[(2*i)+j] = font[i+j];
                }
        }
}

static void
unlock_S3(void)
{
        int s3_devid;

        outw(VGA_CR_PORT, 0x3848);
        outw(VGA_CR_PORT, 0x39a5);
        outb(VGA_CR_PORT, 0x2d);
        s3_devid = inb(VGA_CR_DATA) << 8;
        outb(VGA_CR_PORT, 0x2e);
        s3_devid |= inb(VGA_CR_DATA);

        if (s3_devid != 0x8812) {
                /* from the S3 manual */
                outb(0x46E8, 0x10);  /* Put into setup mode */
                outb(0x3C3, 0x10);
                outb(0x102, 0x01);   /* Enable registers */
                outb(0x46E8, 0x08);  /* Enable video */
                outb(0x3C3, 0x08);
                outb(0x4AE8, 0x00);
                outb(VGA_CR_PORT, 0x38);  /* Unlock all registers */
                outb(VGA_CR_DATA, 0x48);
                outb(VGA_CR_PORT, 0x39);
                outb(VGA_CR_DATA, 0xA5);
                outb(VGA_CR_PORT, 0x40);
                outb(VGA_CR_DATA, inb(0x3D5)|0x01);
                outb(VGA_CR_PORT, 0x33);
                outb(VGA_CR_DATA, inb(0x3D5)&~0x52);
                outb(VGA_CR_PORT, 0x35);
                outb(VGA_CR_DATA, inb(0x3D5)&~0x30);
                outb(VGA_CR_PORT, 0x3A);
                outb(VGA_CR_DATA, 0x00);
                outb(VGA_CR_PORT, 0x53);
                outb(VGA_CR_DATA, 0x00);
                outb(VGA_CR_PORT, 0x31);
                outb(VGA_CR_DATA, inb(0x3D5)&~0x4B);
                outb(VGA_CR_PORT, 0x58);

                outb(VGA_CR_DATA, 0);

                outb(VGA_CR_PORT, 0x54);
                outb(VGA_CR_DATA, 0x38);
                outb(VGA_CR_PORT, 0x60);
                outb(VGA_CR_DATA, 0x07);
                outb(VGA_CR_PORT, 0x61);
                outb(VGA_CR_DATA, 0x80);
                outb(VGA_CR_PORT, 0x62);
                outb(VGA_CR_DATA, 0xA1);
                outb(VGA_CR_PORT, 0x69);  /* High order bits for cursor address */
                outb(VGA_CR_DATA, 0);

                outb(VGA_CR_PORT, 0x32);
                outb(VGA_CR_DATA, inb(0x3D5)&~0x10);
        } else {
                /* IBM Portable 860 */
                outw(VGA_SR_PORT, 0x0806);
                outw(VGA_SR_PORT, 0x1041);
                outw(VGA_SR_PORT, 0x1128);
                outw(VGA_CR_PORT, 0x4000);
                outw(VGA_CR_PORT, 0x3100);
                outw(VGA_CR_PORT, 0x3a05);
                outw(VGA_CR_PORT, 0x6688);
                outw(VGA_CR_PORT, 0x5800); /* disable linear addressing */
                outw(VGA_CR_PORT, 0x4500); /* disable H/W cursor */
                outw(VGA_SR_PORT, 0x5410); /* enable auto-centering */
                outw(VGA_SR_PORT, 0x561f);
                outw(VGA_SR_PORT, 0x1b80); /* lock DCLK selection */
                outw(VGA_CR_PORT, 0x3900); /* lock S3 registers */
                outw(VGA_CR_PORT, 0x3800);
        }
}

static void
clear_video_memory(void)
{
        int i, j;

        for (i = 0;  i < LINES; i++) {
                for (j = 0; j < COLS; j++) {
                        videomem[((i * COLS)+j) * 2] = 0x20; /* space */
                        videomem[((i * COLS)+j) * 2 + 1] = 0x07; /* fg/bg */
                }
        }
}

#endif /* VGA_RESET */