/*- * Copyright (c) 1999 FreeBSD(98) port team. * All rights reserved. * * 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 as * the first lines of this file unmodified. * 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. * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. * * $FreeBSD$ */ #include "opt_gdc.h" #include "opt_fb.h" #include "opt_syscons.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef LINE30 #include #endif #include #include #define TEXT_GDC 0x60 #define GRAPHIC_GDC 0xa0 #define ROW 25 #define COL 80 #define DRIVER_NAME "gdc" /* cdev driver declaration */ #define GDC_UNIT(dev) dev2unit(dev) #define GDC_MKMINOR(unit) (unit) typedef struct gdc_softc { video_adapter_t *adp; struct resource *res_tgdc, *res_ggdc; struct resource *res_egc, *res_pegc, *res_grcg, *res_kcg; struct resource *res_tmem, *res_gmem1, *res_gmem2; #ifdef FB_INSTALL_CDEV genfb_softc_t gensc; #endif } gdc_softc_t; #define GDC_SOFTC(unit) \ ((gdc_softc_t *)devclass_get_softc(gdc_devclass, unit)) static bus_addr_t gdc_iat[] = {0, 2, 4, 6, 8, 10, 12, 14}; static devclass_t gdc_devclass; static int gdc_probe_unit(int unit, gdc_softc_t *sc, int flags); static int gdc_attach_unit(int unit, gdc_softc_t *sc, int flags); static int gdc_alloc_resource(device_t dev); static int gdc_release_resource(device_t dev); #ifdef FB_INSTALL_CDEV static d_open_t gdcopen; static d_close_t gdcclose; static d_read_t gdcread; static d_write_t gdcwrite; static d_ioctl_t gdcioctl; static d_mmap_t gdcmmap; static struct cdevsw gdc_cdevsw = { .d_version = D_VERSION, .d_flags = D_NEEDGIANT, .d_open = gdcopen, .d_close = gdcclose, .d_read = gdcread, .d_write = gdcwrite, .d_ioctl = gdcioctl, .d_mmap = gdcmmap, .d_name = DRIVER_NAME, }; #endif /* FB_INSTALL_CDEV */ static void gdc_identify(driver_t *driver, device_t parent) { BUS_ADD_CHILD(parent, ISA_ORDER_SPECULATIVE, DRIVER_NAME, 0); } static int gdcprobe(device_t dev) { int error; /* Check isapnp ids */ if (isa_get_vendorid(dev)) return (ENXIO); device_set_desc(dev, "Generic GDC"); error = gdc_alloc_resource(dev); if (error) return (error); error = gdc_probe_unit(device_get_unit(dev), device_get_softc(dev), device_get_flags(dev)); gdc_release_resource(dev); return (error); } static int gdc_attach(device_t dev) { gdc_softc_t *sc; int error; error = gdc_alloc_resource(dev); if (error) return (error); sc = device_get_softc(dev); error = gdc_attach_unit(device_get_unit(dev), sc, device_get_flags(dev)); if (error) { gdc_release_resource(dev); return error; } #ifdef FB_INSTALL_CDEV /* attach a virtual frame buffer device */ error = fb_attach(GDC_MKMINOR(device_get_unit(dev)), sc->adp, &gdc_cdevsw); if (error) { gdc_release_resource(dev); return error; } #endif /* FB_INSTALL_CDEV */ if (bootverbose) vidd_diag(sc->adp, bootverbose); return 0; } static int gdc_probe_unit(int unit, gdc_softc_t *sc, int flags) { video_switch_t *sw; sw = vid_get_switch(DRIVER_NAME); if (sw == NULL) return ENXIO; return (*sw->probe)(unit, &sc->adp, NULL, flags); } static int gdc_attach_unit(int unit, gdc_softc_t *sc, int flags) { video_switch_t *sw; sw = vid_get_switch(DRIVER_NAME); if (sw == NULL) return ENXIO; return (*sw->init)(unit, sc->adp, flags); } static int gdc_alloc_resource(device_t dev) { int rid; gdc_softc_t *sc; sc = device_get_softc(dev); /* TEXT GDC */ rid = 0; bus_set_resource(dev, SYS_RES_IOPORT, rid, TEXT_GDC, 1); sc->res_tgdc = isa_alloc_resourcev(dev, SYS_RES_IOPORT, &rid, gdc_iat, 8, RF_ACTIVE); if (sc->res_tgdc == NULL) { gdc_release_resource(dev); return (ENXIO); } isa_load_resourcev(sc->res_tgdc, gdc_iat, 8); /* GRAPHIC GDC */ rid = 8; bus_set_resource(dev, SYS_RES_IOPORT, rid, GRAPHIC_GDC, 1); sc->res_ggdc = isa_alloc_resourcev(dev, SYS_RES_IOPORT, &rid, gdc_iat, 8, RF_ACTIVE); if (sc->res_ggdc == NULL) { gdc_release_resource(dev); return (ENXIO); } isa_load_resourcev(sc->res_ggdc, gdc_iat, 8); /* EGC */ rid = 16; bus_set_resource(dev, SYS_RES_IOPORT, rid, 0x4a0, 1); sc->res_egc = isa_alloc_resourcev(dev, SYS_RES_IOPORT, &rid, gdc_iat, 8, RF_ACTIVE); if (sc->res_egc == NULL) { gdc_release_resource(dev); return (ENXIO); } isa_load_resourcev(sc->res_egc, gdc_iat, 8); /* PEGC */ rid = 24; bus_set_resource(dev, SYS_RES_IOPORT, rid, 0x9a0, 1); sc->res_pegc = isa_alloc_resourcev(dev, SYS_RES_IOPORT, &rid, gdc_iat, 8, RF_ACTIVE); if (sc->res_pegc == NULL) { gdc_release_resource(dev); return (ENXIO); } isa_load_resourcev(sc->res_pegc, gdc_iat, 8); /* CRTC/GRCG */ rid = 32; bus_set_resource(dev, SYS_RES_IOPORT, rid, 0x70, 1); sc->res_grcg = isa_alloc_resourcev(dev, SYS_RES_IOPORT, &rid, gdc_iat, 8, RF_ACTIVE); if (sc->res_grcg == NULL) { gdc_release_resource(dev); return (ENXIO); } isa_load_resourcev(sc->res_grcg, gdc_iat, 8); /* KCG */ rid = 40; bus_set_resource(dev, SYS_RES_IOPORT, rid, 0xa1, 1); sc->res_kcg = isa_alloc_resourcev(dev, SYS_RES_IOPORT, &rid, gdc_iat, 8, RF_ACTIVE); if (sc->res_kcg == NULL) { gdc_release_resource(dev); return (ENXIO); } isa_load_resourcev(sc->res_kcg, gdc_iat, 8); /* TEXT Memory */ rid = 0; sc->res_tmem = bus_alloc_resource(dev, SYS_RES_MEMORY, &rid, 0xa0000, 0xa4fff, 0x5000, RF_ACTIVE); if (sc->res_tmem == NULL) { gdc_release_resource(dev); return (ENXIO); } /* GRAPHIC Memory */ rid = 1; sc->res_gmem1 = bus_alloc_resource(dev, SYS_RES_MEMORY, &rid, 0xa8000, 0xbffff, 0x18000, RF_ACTIVE); if (sc->res_gmem1 == NULL) { gdc_release_resource(dev); return (ENXIO); } rid = 2; sc->res_gmem2 = bus_alloc_resource(dev, SYS_RES_MEMORY, &rid, 0xe0000, 0xe7fff, 0x8000, RF_ACTIVE); if (sc->res_gmem2 == NULL) { gdc_release_resource(dev); return (ENXIO); } return (0); } static int gdc_release_resource(device_t dev) { gdc_softc_t *sc; sc = device_get_softc(dev); if (sc->res_tgdc) bus_release_resource(dev, SYS_RES_IOPORT, 0, sc->res_tgdc); if (sc->res_ggdc) bus_release_resource(dev, SYS_RES_IOPORT, 8, sc->res_ggdc); if (sc->res_egc) bus_release_resource(dev, SYS_RES_IOPORT, 16, sc->res_egc); if (sc->res_pegc) bus_release_resource(dev, SYS_RES_IOPORT, 24, sc->res_pegc); if (sc->res_grcg) bus_release_resource(dev, SYS_RES_IOPORT, 32, sc->res_grcg); if (sc->res_kcg) bus_release_resource(dev, SYS_RES_IOPORT, 40, sc->res_kcg); if (sc->res_tmem) bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->res_tmem); if (sc->res_gmem1) bus_release_resource(dev, SYS_RES_MEMORY, 1, sc->res_gmem1); if (sc->res_gmem2) bus_release_resource(dev, SYS_RES_MEMORY, 2, sc->res_gmem2); return (0); } /* cdev driver functions */ #ifdef FB_INSTALL_CDEV static int gdcopen(struct cdev *dev, int flag, int mode, struct thread *td) { gdc_softc_t *sc; sc = GDC_SOFTC(GDC_UNIT(dev)); if (sc == NULL) return ENXIO; if (mode & (O_CREAT | O_APPEND | O_TRUNC)) return ENODEV; return genfbopen(&sc->gensc, sc->adp, flag, mode, td); } static int gdcclose(struct cdev *dev, int flag, int mode, struct thread *td) { gdc_softc_t *sc; sc = GDC_SOFTC(GDC_UNIT(dev)); return genfbclose(&sc->gensc, sc->adp, flag, mode, td); } static int gdcread(struct cdev *dev, struct uio *uio, int flag) { gdc_softc_t *sc; sc = GDC_SOFTC(GDC_UNIT(dev)); return genfbread(&sc->gensc, sc->adp, uio, flag); } static int gdcwrite(struct cdev *dev, struct uio *uio, int flag) { gdc_softc_t *sc; sc = GDC_SOFTC(GDC_UNIT(dev)); return genfbread(&sc->gensc, sc->adp, uio, flag); } static int gdcioctl(struct cdev *dev, u_long cmd, caddr_t arg, int flag, struct thread *td) { gdc_softc_t *sc; sc = GDC_SOFTC(GDC_UNIT(dev)); return genfbioctl(&sc->gensc, sc->adp, cmd, arg, flag, td); } static int gdcmmap(struct cdev *dev, vm_ooffset_t offset, vm_paddr_t *paddr, int prot, vm_memattr_t *memattr) { gdc_softc_t *sc; sc = GDC_SOFTC(GDC_UNIT(dev)); return genfbmmap(&sc->gensc, sc->adp, offset, paddr, prot, memattr); } #endif /* FB_INSTALL_CDEV */ static device_method_t gdc_methods[] = { DEVMETHOD(device_identify, gdc_identify), DEVMETHOD(device_probe, gdcprobe), DEVMETHOD(device_attach, gdc_attach), { 0, 0 } }; static driver_t gdcdriver = { DRIVER_NAME, gdc_methods, sizeof(gdc_softc_t), }; DRIVER_MODULE(gdc, isa, gdcdriver, gdc_devclass, 0, 0); /* LOW-LEVEL */ #include #define TEXT_BUF_BASE 0x000a0000 #define TEXT_BUF_SIZE 0x00008000 #define GRAPHICS_BUF_BASE 0x000a8000 #define GRAPHICS_BUF_SIZE 0x00040000 #define VIDEO_BUF_BASE 0x000a0000 #define VIDEO_BUF_SIZE 0x00048000 #define probe_done(adp) ((adp)->va_flags & V_ADP_PROBED) #define init_done(adp) ((adp)->va_flags & V_ADP_INITIALIZED) #define config_done(adp) ((adp)->va_flags & V_ADP_REGISTERED) /* * NOTE: `va_window' should have a virtual address, but is initialized * with a physical address in the following table, they will be * converted at run-time. */ static video_adapter_t adapter_init_value[] = { { 0, KD_PC98, "gdc", /* va_type, va_name */ 0, 0, /* va_unit, va_minor */ V_ADP_COLOR | V_ADP_MODECHANGE | V_ADP_BORDER, TEXT_GDC, 16, TEXT_GDC, /* va_io*, XXX */ VIDEO_BUF_BASE, VIDEO_BUF_SIZE, /* va_mem* */ TEXT_BUF_BASE, TEXT_BUF_SIZE, TEXT_BUF_SIZE, 0, /* va_window* */ 0, 0, /* va_buffer, va_buffer_size */ 0, M_PC98_80x25, 0, /* va_*mode* */ }, }; static video_adapter_t biosadapter[1]; /* video driver declarations */ static int gdc_configure(int flags); static int gdc_err(video_adapter_t *adp, ...); static vi_probe_t gdc_probe; static vi_init_t gdc_init; static vi_get_info_t gdc_get_info; static vi_query_mode_t gdc_query_mode; static vi_set_mode_t gdc_set_mode; static vi_set_border_t gdc_set_border; static vi_save_state_t gdc_save_state; static vi_load_state_t gdc_load_state; static vi_read_hw_cursor_t gdc_read_hw_cursor; static vi_set_hw_cursor_t gdc_set_hw_cursor; static vi_set_hw_cursor_shape_t gdc_set_hw_cursor_shape; static vi_blank_display_t gdc_blank_display; static vi_mmap_t gdc_mmap_buf; static vi_ioctl_t gdc_dev_ioctl; static vi_clear_t gdc_clear; static vi_fill_rect_t gdc_fill_rect; static vi_bitblt_t gdc_bitblt; static vi_diag_t gdc_diag; static vi_save_palette_t gdc_save_palette; static vi_load_palette_t gdc_load_palette; static vi_set_win_org_t gdc_set_origin; static video_switch_t gdcvidsw = { gdc_probe, gdc_init, gdc_get_info, gdc_query_mode, gdc_set_mode, (vi_save_font_t *)gdc_err, (vi_load_font_t *)gdc_err, (vi_show_font_t *)gdc_err, gdc_save_palette, gdc_load_palette, gdc_set_border, gdc_save_state, gdc_load_state, gdc_set_origin, gdc_read_hw_cursor, gdc_set_hw_cursor, gdc_set_hw_cursor_shape, gdc_blank_display, gdc_mmap_buf, gdc_dev_ioctl, gdc_clear, gdc_fill_rect, gdc_bitblt, (int (*)(void))gdc_err, (int (*)(void))gdc_err, gdc_diag, }; VIDEO_DRIVER(gdc, gdcvidsw, gdc_configure); /* GDC BIOS standard video modes */ #define EOT (-1) #define NA (-2) static video_info_t bios_vmode[] = { { M_PC98_80x25, V_INFO_COLOR, 80, 25, 8, 16, 4, 1, TEXT_BUF_BASE, TEXT_BUF_SIZE, TEXT_BUF_SIZE, 0, 0, V_INFO_MM_TEXT }, #ifdef LINE30 { M_PC98_80x30, V_INFO_COLOR, 80, 30, 8, 16, 4, 1, TEXT_BUF_BASE, TEXT_BUF_SIZE, TEXT_BUF_SIZE, 0, 0, V_INFO_MM_TEXT }, #endif #ifndef GDC_NOGRAPHICS { M_PC98_EGC640x400, V_INFO_COLOR | V_INFO_GRAPHICS, 640, 400, 8, 16, 4, 4, GRAPHICS_BUF_BASE, GRAPHICS_BUF_SIZE, GRAPHICS_BUF_SIZE, 0, 0, V_INFO_MM_PLANAR }, { M_PC98_PEGC640x400, V_INFO_COLOR | V_INFO_GRAPHICS | V_INFO_VESA, 640, 400, 8, 16, 8, 1, GRAPHICS_BUF_BASE, 0x00008000, 0x00008000, 0, 0, V_INFO_MM_PACKED, 1 }, #ifdef LINE30 { M_PC98_PEGC640x480, V_INFO_COLOR | V_INFO_GRAPHICS | V_INFO_VESA, 640, 480, 8, 16, 8, 1, GRAPHICS_BUF_BASE, 0x00008000, 0x00008000, 0, 0, V_INFO_MM_PACKED, 1 }, #endif #endif { EOT }, }; static int gdc_init_done = FALSE; /* local functions */ static int map_gen_mode_num(int type, int color, int mode); static int probe_adapters(void); #define prologue(adp, flag, err) \ if (!gdc_init_done || !((adp)->va_flags & (flag))) \ return (err) /* a backdoor for the console driver */ static int gdc_configure(int flags) { probe_adapters(); biosadapter[0].va_flags |= V_ADP_INITIALIZED; if (!config_done(&biosadapter[0])) { if (vid_register(&biosadapter[0]) < 0) return 1; biosadapter[0].va_flags |= V_ADP_REGISTERED; } return 1; } /* local subroutines */ /* map a generic video mode to a known mode number */ static int map_gen_mode_num(int type, int color, int mode) { static struct { int from; int to; } mode_map[] = { { M_TEXT_80x25, M_PC98_80x25, }, #ifdef LINE30 { M_TEXT_80x30, M_PC98_80x30, }, #endif }; int i; for (i = 0; i < sizeof(mode_map)/sizeof(mode_map[0]); ++i) { if (mode_map[i].from == mode) return mode_map[i].to; } return mode; } static int verify_adapter(video_adapter_t *adp) { #ifndef GDC_NOGRAPHICS int i; if (PC98_SYSTEM_PARAMETER(0x45c) & 0x40) { /* PEGC exists */ adp->va_flags |= V_ADP_VESA; /* XXX */ } else { for (i = 0; bios_vmode[i].vi_mode != EOT; ++i) { if (bios_vmode[i].vi_flags & V_INFO_VESA) bios_vmode[i].vi_mode = NA; } } #endif return 0; } /* probe video adapters and return the number of detected adapters */ static int probe_adapters(void) { video_info_t info; /* do this test only once */ if (gdc_init_done) return 1; gdc_init_done = TRUE; biosadapter[0] = adapter_init_value[0]; biosadapter[0].va_flags |= V_ADP_PROBED; biosadapter[0].va_mode = biosadapter[0].va_initial_mode = biosadapter[0].va_initial_bios_mode; if ((PC98_SYSTEM_PARAMETER(0x597) & 0x80) || (PC98_SYSTEM_PARAMETER(0x458) & 0x80)) { gdc_FH = (inb(0x9a8) & 1) ? _31KHZ : _24KHZ; } else { gdc_FH = _24KHZ; } gdc_get_info(&biosadapter[0], biosadapter[0].va_initial_mode, &info); initialize_gdc(T25_G400, info.vi_flags & V_INFO_GRAPHICS); biosadapter[0].va_window = BIOS_PADDRTOVADDR(info.vi_window); biosadapter[0].va_window_size = info.vi_window_size; biosadapter[0].va_window_gran = info.vi_window_gran; biosadapter[0].va_buffer = 0; biosadapter[0].va_buffer_size = 0; if (info.vi_flags & V_INFO_GRAPHICS) { switch (info.vi_depth/info.vi_planes) { case 1: biosadapter[0].va_line_width = info.vi_width/8; break; case 2: biosadapter[0].va_line_width = info.vi_width/4; break; case 4: biosadapter[0].va_line_width = info.vi_width/2; break; case 8: default: /* shouldn't happen */ biosadapter[0].va_line_width = info.vi_width; break; } } else { biosadapter[0].va_line_width = info.vi_width; } bcopy(&info, &biosadapter[0].va_info, sizeof(info)); verify_adapter(&biosadapter[0]); return 1; } static void master_gdc_cmd(unsigned int cmd) { while ( (inb(TEXT_GDC) & 2) != 0); outb(TEXT_GDC+2, cmd); } static void master_gdc_prm(unsigned int pmtr) { while ( (inb(TEXT_GDC) & 2) != 0); outb(TEXT_GDC, pmtr); } static void master_gdc_word_prm(unsigned int wpmtr) { master_gdc_prm(wpmtr & 0x00ff); master_gdc_prm((wpmtr >> 8) & 0x00ff); } #ifdef LINE30 static void master_gdc_fifo_empty(void) { while ( (inb(TEXT_GDC) & 4) == 0); } #endif static void master_gdc_wait_vsync(void) { while ( (inb(TEXT_GDC) & 0x20) != 0); while ( (inb(TEXT_GDC) & 0x20) == 0); } static void gdc_cmd(unsigned int cmd) { while ( (inb(GRAPHIC_GDC) & 2) != 0); outb( GRAPHIC_GDC+2, cmd); } #ifdef LINE30 static void gdc_prm(unsigned int pmtr) { while ( (inb(GRAPHIC_GDC) & 2) != 0); outb( GRAPHIC_GDC, pmtr); } static void gdc_word_prm(unsigned int wpmtr) { gdc_prm(wpmtr & 0x00ff); gdc_prm((wpmtr >> 8) & 0x00ff); } static void gdc_fifo_empty(void) { while ( (inb(GRAPHIC_GDC) & 0x04) == 0); } #endif static void gdc_wait_vsync(void) { while ( (inb(GRAPHIC_GDC) & 0x20) != 0); while ( (inb(GRAPHIC_GDC) & 0x20) == 0); } #ifdef LINE30 static int check_gdc_clock(void) { if ((inb(IO_SYSPORT) & 0x80) == 0){ return _5MHZ; } else { return _2_5MHZ; } } #endif static void initialize_gdc(unsigned int mode, int isGraph) { #ifdef LINE30 /* start 30line initialize */ int m_mode, s_mode, gdc_clock, hsync_clock; gdc_clock = check_gdc_clock(); m_mode = (mode == T25_G400) ? _25L : _30L; s_mode = 2*mode+gdc_clock; gdc_INFO = m_mode; master_gdc_wait_vsync(); if ((PC98_SYSTEM_PARAMETER(0x597) & 0x80) || (PC98_SYSTEM_PARAMETER(0x458) & 0x80)) { if (PC98_SYSTEM_PARAMETER(0x481) & 0x08) { hsync_clock = (m_mode == _25L) ? gdc_FH : _31KHZ; outb(0x9a8, (hsync_clock == _31KHZ) ? 1 : 0); } else { hsync_clock = gdc_FH; } } else { hsync_clock = _24KHZ; } if ((gdc_clock == _2_5MHZ) && (slave_param[hsync_clock][s_mode][GDC_LF] > 400)) { outb(0x6a, 0x83); outb(0x6a, 0x85); gdc_clock = _5MHZ; s_mode = 2*mode+gdc_clock; } master_gdc_cmd(_GDC_RESET); master_gdc_cmd(_GDC_MASTER); gdc_cmd(_GDC_RESET); gdc_cmd(_GDC_SLAVE); /* GDC Master */ master_gdc_cmd(_GDC_SYNC); master_gdc_prm(0x00); /* flush less */ /* text & graph */ master_gdc_prm(master_param[hsync_clock][m_mode][GDC_CR]); master_gdc_word_prm(((master_param[hsync_clock][m_mode][GDC_HFP] << 10) + (master_param[hsync_clock][m_mode][GDC_VS] << 5) + master_param[hsync_clock][m_mode][GDC_HS])); master_gdc_prm(master_param[hsync_clock][m_mode][GDC_HBP]); master_gdc_prm(master_param[hsync_clock][m_mode][GDC_VFP]); master_gdc_word_prm(((master_param[hsync_clock][m_mode][GDC_VBP] << 10) + (master_param[hsync_clock][m_mode][GDC_LF]))); master_gdc_fifo_empty(); master_gdc_cmd(_GDC_PITCH); master_gdc_prm(MasterPCH); master_gdc_fifo_empty(); /* GDC slave */ gdc_cmd(_GDC_SYNC); gdc_prm(0x06); gdc_prm(slave_param[hsync_clock][s_mode][GDC_CR]); gdc_word_prm((slave_param[hsync_clock][s_mode][GDC_HFP] << 10) + (slave_param[hsync_clock][s_mode][GDC_VS] << 5) + (slave_param[hsync_clock][s_mode][GDC_HS])); gdc_prm(slave_param[hsync_clock][s_mode][GDC_HBP]); gdc_prm(slave_param[hsync_clock][s_mode][GDC_VFP]); gdc_word_prm((slave_param[hsync_clock][s_mode][GDC_VBP] << 10) + (slave_param[hsync_clock][s_mode][GDC_LF])); gdc_fifo_empty(); gdc_cmd(_GDC_PITCH); gdc_prm(SlavePCH[gdc_clock]); gdc_fifo_empty(); /* set Master GDC scroll param */ master_gdc_wait_vsync(); master_gdc_wait_vsync(); master_gdc_wait_vsync(); master_gdc_cmd(_GDC_SCROLL); master_gdc_word_prm(0); master_gdc_word_prm((master_param[hsync_clock][m_mode][GDC_LF] << 4) | 0x0000); master_gdc_fifo_empty(); /* set Slave GDC scroll param */ gdc_wait_vsync(); gdc_cmd(_GDC_SCROLL); gdc_word_prm(0); if (gdc_clock == _5MHZ) { gdc_word_prm((SlaveScrlLF[mode] << 4) | 0x4000); } else { gdc_word_prm(SlaveScrlLF[mode] << 4); } gdc_fifo_empty(); gdc_word_prm(0); if (gdc_clock == _5MHZ) { gdc_word_prm((SlaveScrlLF[mode] << 4) | 0x4000); } else { gdc_word_prm(SlaveScrlLF[mode] << 4); } gdc_fifo_empty(); /* sync start */ gdc_cmd(isGraph ? _GDC_START : _GDC_STOP); gdc_wait_vsync(); gdc_wait_vsync(); gdc_wait_vsync(); master_gdc_cmd(isGraph ? _GDC_STOP : _GDC_START); #else master_gdc_wait_vsync(); master_gdc_cmd(isGraph ? _GDC_STOP : _GDC_START); /* text */ gdc_wait_vsync(); gdc_cmd(isGraph ? _GDC_START : _GDC_STOP); /* graphics */ #endif } #ifndef GDC_NOGRAPHICS static u_char b_palette[] = { /* R G B */ 0x00, 0x00, 0x00, /* 0 */ 0x00, 0x00, 0x7f, /* 1 */ 0x7f, 0x00, 0x00, /* 2 */ 0x7f, 0x00, 0x7f, /* 3 */ 0x00, 0x7f, 0x00, /* 4 */ 0x00, 0x7f, 0x7f, /* 5 */ 0x7f, 0x7f, 0x00, /* 6 */ 0x7f, 0x7f, 0x7f, /* 7 */ 0x40, 0x40, 0x40, /* 8 */ 0x00, 0x00, 0xff, /* 9 */ 0xff, 0x00, 0x00, /* 10 */ 0xff, 0x00, 0xff, /* 11 */ 0x00, 0xff, 0x00, /* 12 */ 0x00, 0xff, 0xff, /* 13 */ 0xff, 0xff, 0x00, /* 14 */ 0xff, 0xff, 0xff, /* 15 */ }; #endif static int gdc_load_palette(video_adapter_t *adp, u_char *palette) { #ifndef GDC_NOGRAPHICS int i; if (adp->va_info.vi_flags & V_INFO_VESA) { gdc_wait_vsync(); for (i = 0; i < 256; ++i) { outb(0xa8, i); outb(0xac, *palette++); /* R */ outb(0xaa, *palette++); /* G */ outb(0xae, *palette++); /* B */ } } else { /* * XXX - Even though PC-98 text color is independent of palette, * we should set palette in text mode. * Because the background color of text mode is palette 0's one. */ outb(0x6a, 1); /* 16 colors mode */ bcopy(palette, b_palette, sizeof(b_palette)); gdc_wait_vsync(); for (i = 0; i < 16; ++i) { outb(0xa8, i); outb(0xac, *palette++ >> 4); /* R */ outb(0xaa, *palette++ >> 4); /* G */ outb(0xae, *palette++ >> 4); /* B */ } } #endif return 0; } static int gdc_save_palette(video_adapter_t *adp, u_char *palette) { #ifndef GDC_NOGRAPHICS int i; if (adp->va_info.vi_flags & V_INFO_VESA) { for (i = 0; i < 256; ++i) { outb(0xa8, i); *palette++ = inb(0xac); /* R */ *palette++ = inb(0xaa); /* G */ *palette++ = inb(0xae); /* B */ } } else { bcopy(b_palette, palette, sizeof(b_palette)); } #endif return 0; } static int gdc_set_origin(video_adapter_t *adp, off_t offset) { #ifndef GDC_NOGRAPHICS if (adp->va_info.vi_flags & V_INFO_VESA) { writew(BIOS_PADDRTOVADDR(0x000e0004), offset >> 15); } #endif return 0; } /* entry points */ static int gdc_err(video_adapter_t *adp, ...) { return ENODEV; } static int gdc_probe(int unit, video_adapter_t **adpp, void *arg, int flags) { probe_adapters(); if (unit >= 1) return ENXIO; *adpp = &biosadapter[unit]; return 0; } static int gdc_init(int unit, video_adapter_t *adp, int flags) { if ((unit >= 1) || (adp == NULL) || !probe_done(adp)) return ENXIO; if (!init_done(adp)) { /* nothing to do really... */ adp->va_flags |= V_ADP_INITIALIZED; } if (!config_done(adp)) { if (vid_register(adp) < 0) return ENXIO; adp->va_flags |= V_ADP_REGISTERED; } return 0; } /* * get_info(): * Return the video_info structure of the requested video mode. */ static int gdc_get_info(video_adapter_t *adp, int mode, video_info_t *info) { int i; if (!gdc_init_done) return ENXIO; mode = map_gen_mode_num(adp->va_type, adp->va_flags & V_ADP_COLOR, mode); for (i = 0; bios_vmode[i].vi_mode != EOT; ++i) { if (bios_vmode[i].vi_mode == NA) continue; if (mode == bios_vmode[i].vi_mode) { *info = bios_vmode[i]; info->vi_buffer_size = info->vi_window_size*info->vi_planes; return 0; } } return EINVAL; } /* * query_mode(): * Find a video mode matching the requested parameters. * Fields filled with 0 are considered "don't care" fields and * match any modes. */ static int gdc_query_mode(video_adapter_t *adp, video_info_t *info) { int i; if (!gdc_init_done) return ENXIO; for (i = 0; bios_vmode[i].vi_mode != EOT; ++i) { if (bios_vmode[i].vi_mode == NA) continue; if ((info->vi_width != 0) && (info->vi_width != bios_vmode[i].vi_width)) continue; if ((info->vi_height != 0) && (info->vi_height != bios_vmode[i].vi_height)) continue; if ((info->vi_cwidth != 0) && (info->vi_cwidth != bios_vmode[i].vi_cwidth)) continue; if ((info->vi_cheight != 0) && (info->vi_cheight != bios_vmode[i].vi_cheight)) continue; if ((info->vi_depth != 0) && (info->vi_depth != bios_vmode[i].vi_depth)) continue; if ((info->vi_planes != 0) && (info->vi_planes != bios_vmode[i].vi_planes)) continue; /* XXX: should check pixel format, memory model */ if ((info->vi_flags != 0) && (info->vi_flags != bios_vmode[i].vi_flags)) continue; /* verify if this mode is supported on this adapter */ if (gdc_get_info(adp, bios_vmode[i].vi_mode, info)) continue; return 0; } return ENODEV; } /* * set_mode(): * Change the video mode. */ static int gdc_set_mode(video_adapter_t *adp, int mode) { video_info_t info; prologue(adp, V_ADP_MODECHANGE, ENODEV); mode = map_gen_mode_num(adp->va_type, adp->va_flags & V_ADP_COLOR, mode); if (gdc_get_info(adp, mode, &info)) return EINVAL; switch (info.vi_mode) { #ifndef GDC_NOGRAPHICS case M_PC98_PEGC640x480: /* PEGC 640x480 */ initialize_gdc(T30_G480, info.vi_flags & V_INFO_GRAPHICS); break; case M_PC98_PEGC640x400: /* PEGC 640x400 */ case M_PC98_EGC640x400: /* EGC GRAPHICS */ #endif case M_PC98_80x25: /* VGA TEXT */ initialize_gdc(T25_G400, info.vi_flags & V_INFO_GRAPHICS); break; case M_PC98_80x30: /* VGA TEXT */ initialize_gdc(T30_G400, info.vi_flags & V_INFO_GRAPHICS); break; default: break; } #ifndef GDC_NOGRAPHICS if (info.vi_flags & V_INFO_VESA) { outb(0x6a, 0x07); /* enable mode F/F change */ outb(0x6a, 0x21); /* enhanced graphics */ if (info.vi_height > 400) outb(0x6a, 0x69); /* 800 lines */ writeb(BIOS_PADDRTOVADDR(0x000e0100), 0); /* packed pixel */ } else { if (adp->va_flags & V_ADP_VESA) { outb(0x6a, 0x07); /* enable mode F/F change */ outb(0x6a, 0x20); /* normal graphics */ outb(0x6a, 0x68); /* 400 lines */ } outb(0x6a, 1); /* 16 colors */ } #endif adp->va_mode = mode; adp->va_flags &= ~V_ADP_COLOR; adp->va_flags |= (info.vi_flags & V_INFO_COLOR) ? V_ADP_COLOR : 0; #if 0 adp->va_crtc_addr = (adp->va_flags & V_ADP_COLOR) ? COLOR_CRTC : MONO_CRTC; #endif adp->va_window = BIOS_PADDRTOVADDR(info.vi_window); adp->va_window_size = info.vi_window_size; adp->va_window_gran = info.vi_window_gran; if (info.vi_buffer_size == 0) { adp->va_buffer = 0; adp->va_buffer_size = 0; } else { adp->va_buffer = BIOS_PADDRTOVADDR(info.vi_buffer); adp->va_buffer_size = info.vi_buffer_size; } if (info.vi_flags & V_INFO_GRAPHICS) { switch (info.vi_depth/info.vi_planes) { case 1: adp->va_line_width = info.vi_width/8; break; case 2: adp->va_line_width = info.vi_width/4; break; case 4: adp->va_line_width = info.vi_width/2; break; case 8: default: /* shouldn't happen */ adp->va_line_width = info.vi_width; break; } } else { adp->va_line_width = info.vi_width; } bcopy(&info, &adp->va_info, sizeof(info)); /* move hardware cursor out of the way */ vidd_set_hw_cursor(adp, -1, -1); return 0; } /* * set_border(): * Change the border color. */ static int gdc_set_border(video_adapter_t *adp, int color) { outb(0x6c, color << 4); return 0; } /* * save_state(): * Read video card register values. */ static int gdc_save_state(video_adapter_t *adp, void *p, size_t size) { return ENODEV; } /* * load_state(): * Set video card registers at once. */ static int gdc_load_state(video_adapter_t *adp, void *p) { return ENODEV; } /* * read_hw_cursor(): * Read the position of the hardware text cursor. */ static int gdc_read_hw_cursor(video_adapter_t *adp, int *col, int *row) { u_int16_t off; int s; if (!gdc_init_done) return ENXIO; if (adp->va_info.vi_flags & V_INFO_GRAPHICS) return ENODEV; s = spltty(); master_gdc_cmd(0xe0); /* _GDC_CSRR */ while((inb(TEXT_GDC + 0) & 0x1) == 0) {} /* GDC wait */ off = inb(TEXT_GDC + 2); /* EADl */ off |= (inb(TEXT_GDC + 2) << 8); /* EADh */ inb(TEXT_GDC + 2); /* dummy */ inb(TEXT_GDC + 2); /* dummy */ inb(TEXT_GDC + 2); /* dummy */ splx(s); if (off >= ROW*COL) off = 0; *row = off / adp->va_info.vi_width; *col = off % adp->va_info.vi_width; return 0; } /* * set_hw_cursor(): * Move the hardware text cursor. If col and row are both -1, * the cursor won't be shown. */ static int gdc_set_hw_cursor(video_adapter_t *adp, int col, int row) { u_int16_t off; int s; if (!gdc_init_done) return ENXIO; if ((col == -1) && (row == -1)) { off = -1; } else { if (adp->va_info.vi_flags & V_INFO_GRAPHICS) return ENODEV; off = row*adp->va_info.vi_width + col; } s = spltty(); master_gdc_cmd(0x49); /* _GDC_CSRW */ master_gdc_word_prm(off); splx(s); return 0; } /* * set_hw_cursor_shape(): * Change the shape of the hardware text cursor. If the height is zero * or negative, the cursor won't be shown. */ static int gdc_set_hw_cursor_shape(video_adapter_t *adp, int base, int height, int celsize, int blink) { int start; int end; int s; if (!gdc_init_done) return ENXIO; start = celsize - (base + height); end = celsize - base - 1; #if 0 /* * muPD7220 GDC has anomaly that if end == celsize - 1 then start * must be 0, otherwise the cursor won't be correctly shown * in the first row in the screen. We shall set end to celsize - 2; * if end == celsize -1 && start > 0. XXX */ if ((end == celsize - 1) && (start > 0) && (start < end)) --end; #endif s = spltty(); master_gdc_cmd(0x4b); /* _GDC_CSRFORM */ master_gdc_prm(((height > 0) ? 0x80 : 0) /* cursor on/off */ | ((celsize - 1) & 0x1f)); /* cel size */ master_gdc_word_prm(((end & 0x1f) << 11) /* end line */ | (12 << 6) /* blink rate */ | (blink ? 0 : 0x20) /* blink on/off */ | (start & 0x1f)); /* start line */ splx(s); return 0; } /* * blank_display() * Put the display in power save/power off mode. */ static int gdc_blank_display(video_adapter_t *adp, int mode) { int s; static int standby = 0; if (!gdc_init_done) return ENXIO; s = splhigh(); switch (mode) { case V_DISPLAY_SUSPEND: case V_DISPLAY_STAND_BY: outb(0x09a2, 0x80 | 0x40); /* V/H-SYNC mask */ if (inb(0x09a2) == (0x80 | 0x40)) standby = 1; /* FALLTHROUGH */ case V_DISPLAY_BLANK: while (!(inb(TEXT_GDC) & 0x20)) /* V-SYNC wait */ ; outb(TEXT_GDC + 8, 0x0e); /* DISP off */ break; case V_DISPLAY_ON: while (!(inb(TEXT_GDC) & 0x20)) /* V-SYNC wait */ ; outb(TEXT_GDC + 8, 0x0f); /* DISP on */ if (standby) { outb(0x09a2, 0x00); /* V/H-SYNC unmask */ standby = 0; } break; } splx(s); return 0; } /* * mmap(): * Mmap frame buffer. */ static int gdc_mmap_buf(video_adapter_t *adp, vm_ooffset_t offset, vm_offset_t *paddr, int prot, vm_memattr_t *memattr) { /* FIXME: is this correct? XXX */ if (offset > VIDEO_BUF_SIZE - PAGE_SIZE) return -1; *paddr = adp->va_info.vi_window + offset; return 0; } #ifndef GDC_NOGRAPHICS static void planar_fill(video_adapter_t *adp, int val) { outb(0x7c, 0x80); /* GRCG on & TDW mode */ outb(0x7e, 0); /* tile B */ outb(0x7e, 0); /* tile R */ outb(0x7e, 0); /* tile G */ outb(0x7e, 0); /* tile I */ fillw_io(0, adp->va_window, 0x8000 / 2); /* XXX */ outb(0x7c, 0); /* GRCG off */ } static void packed_fill(video_adapter_t *adp, int val) { int length; int at; /* position in the frame buffer */ int l; at = 0; length = adp->va_line_width*adp->va_info.vi_height; while (length > 0) { l = imin(length, adp->va_window_size); vidd_set_win_org(adp, at); bzero_io(adp->va_window, l); length -= l; at += l; } } static int gdc_clear(video_adapter_t *adp) { switch (adp->va_info.vi_mem_model) { case V_INFO_MM_TEXT: /* do nothing? XXX */ break; case V_INFO_MM_PLANAR: planar_fill(adp, 0); break; case V_INFO_MM_PACKED: packed_fill(adp, 0); break; } return 0; } #else /* GDC_NOGRAPHICS */ static int gdc_clear(video_adapter_t *adp) { return 0; } #endif /* GDC_NOGRAPHICS */ static int gdc_fill_rect(video_adapter_t *adp, int val, int x, int y, int cx, int cy) { return ENODEV; } static int gdc_bitblt(video_adapter_t *adp,...) { /* FIXME */ return ENODEV; } static int gdc_dev_ioctl(video_adapter_t *adp, u_long cmd, caddr_t arg) { switch (cmd) { case FBIO_GETWINORG: /* get frame buffer window origin */ *(u_int *)arg = 0; return 0; case FBIO_SETWINORG: /* set frame buffer window origin */ case FBIO_SETDISPSTART: /* set display start address */ case FBIO_SETLINEWIDTH: /* set scan line length in pixel */ case FBIO_GETPALETTE: /* get color palette */ case FBIO_SETPALETTE: /* set color palette */ case FBIOGETCMAP: /* get color palette */ case FBIOPUTCMAP: /* set color palette */ return ENODEV; case FBIOGTYPE: /* get frame buffer type info. */ ((struct fbtype *)arg)->fb_type = fb_type(adp->va_type); ((struct fbtype *)arg)->fb_height = adp->va_info.vi_height; ((struct fbtype *)arg)->fb_width = adp->va_info.vi_width; ((struct fbtype *)arg)->fb_depth = adp->va_info.vi_depth; if ((adp->va_info.vi_depth <= 1) || (adp->va_info.vi_depth > 8)) ((struct fbtype *)arg)->fb_cmsize = 0; else ((struct fbtype *)arg)->fb_cmsize = 1 << adp->va_info.vi_depth; ((struct fbtype *)arg)->fb_size = adp->va_buffer_size; return 0; default: return fb_commonioctl(adp, cmd, arg); } } /* * diag(): * Print some information about the video adapter and video modes, * with requested level of details. */ static int gdc_diag(video_adapter_t *adp, int level) { #if defined(FB_DEBUG) && FB_DEBUG > 1 int i; #endif if (!gdc_init_done) return ENXIO; fb_dump_adp_info(DRIVER_NAME, adp, level); #if defined(FB_DEBUG) && FB_DEBUG > 1 for (i = 0; bios_vmode[i].vi_mode != EOT; ++i) { if (bios_vmode[i].vi_mode == NA) continue; if (get_mode_param(bios_vmode[i].vi_mode) == NULL) continue; fb_dump_mode_info(DRIVER_NAME, adp, &bios_vmode[i], level); } #endif return 0; }