summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--sys/dev/fb/s3_pci.c564
-rw-r--r--sys/modules/Makefile2
-rw-r--r--sys/modules/s3/Makefile9
3 files changed, 574 insertions, 1 deletions
diff --git a/sys/dev/fb/s3_pci.c b/sys/dev/fb/s3_pci.c
new file mode 100644
index 0000000..86d8df1
--- /dev/null
+++ b/sys/dev/fb/s3_pci.c
@@ -0,0 +1,564 @@
+/*-
+ * Copyright (c) 2000 Alcove - Nicolas Souchu <nsouch@freebsd.org>
+ * All rights reserved.
+ *
+ * Code based on Peter Horton <pdh@colonel-panic.com> patch.
+ *
+ * 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 AUTHOR 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 AUTHOR 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.
+ *
+ * $FreeBSD$
+ *
+ */
+
+/* Enable LFB on S3 cards that has only VESA 1.2 BIOS */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <machine/bus_pio.h>
+#include <machine/bus_memio.h>
+#include <machine/bus.h>
+
+#include <vm/vm.h>
+#include <vm/vm_extern.h>
+#include <vm/vm_kern.h>
+#include <vm/pmap.h>
+
+#include <sys/uio.h>
+#include <sys/module.h>
+#include <sys/bus.h>
+#include <sys/rman.h>
+#include <machine/resource.h>
+
+#include <sys/malloc.h>
+#include <sys/fbio.h>
+
+#include <pci/pcireg.h>
+#include <pci/pcivar.h>
+
+#include <machine/md_var.h>
+#include <machine/vm86.h>
+#include <machine/pc/bios.h>
+#include <machine/pc/vesa.h>
+
+#include <dev/fb/fbreg.h>
+#include <dev/fb/vgareg.h>
+
+#define S3PCI_DEBUG 1
+
+#define PCI_S3_VENDOR_ID 0x5333
+
+#define S3_CONFIG_IO 0x3c0 /* VGA standard config io ports */
+#define S3_CONFIG_IO_SIZE 0x20
+
+#define S3_ENHANCED_IO 0x4ae8 /* Extended config register */
+#define S3_ENHANCED_IO_SIZE 1
+
+#define S3_CRTC_ADDR 0x14
+#define S3_CRTC_VALUE 0x15
+
+#define PCI_BASE_MEMORY 0x10
+
+#define outb_p(value, offset) bus_space_write_1(sc->st, sc->sh, offset, value)
+#define inb_p(offset) (bus_space_read_1(sc->st, sc->sh, offset))
+#define outb_enh(value, offset) bus_space_write_1(sc->enh_st, sc->enh_sh, \
+ offset, value)
+#define inb_enh(offset) (bus_space_read_1(sc->enh_st, sc->enh_sh, offset))
+
+struct s3pci_softc {
+ bus_space_tag_t st;
+ bus_space_handle_t sh;
+ bus_space_tag_t enh_st;
+ bus_space_handle_t enh_sh;
+ struct resource *port_res;
+ struct resource *enh_res;
+ struct resource *mem_res;
+ u_long mem_base;
+ u_long mem_size;
+};
+
+static int s3lfb_error(void);
+static vi_probe_t s3lfb_probe;
+static vi_init_t s3lfb_init;
+static vi_get_info_t s3lfb_get_info;
+static vi_query_mode_t s3lfb_query_mode;
+static vi_set_mode_t s3lfb_set_mode;
+static vi_save_font_t s3lfb_save_font;
+static vi_load_font_t s3lfb_load_font;
+static vi_show_font_t s3lfb_show_font;
+static vi_save_palette_t s3lfb_save_palette;
+static vi_load_palette_t s3lfb_load_palette;
+static vi_set_border_t s3lfb_set_border;
+static vi_save_state_t s3lfb_save_state;
+static vi_load_state_t s3lfb_load_state;
+static vi_set_win_org_t s3lfb_set_origin;
+static vi_read_hw_cursor_t s3lfb_read_hw_cursor;
+static vi_set_hw_cursor_t s3lfb_set_hw_cursor;
+static vi_set_hw_cursor_shape_t s3lfb_set_hw_cursor_shape;
+static vi_blank_display_t s3lfb_blank_display;
+static vi_mmap_t s3lfb_mmap;
+static vi_ioctl_t s3lfb_ioctl;
+static vi_clear_t s3lfb_clear;
+static vi_fill_rect_t s3lfb_fill_rect;
+static vi_bitblt_t s3lfb_bitblt;
+static vi_diag_t s3lfb_diag;
+
+static video_switch_t s3lfbvidsw = {
+ s3lfb_probe,
+ s3lfb_init,
+ s3lfb_get_info,
+ s3lfb_query_mode,
+ s3lfb_set_mode,
+ s3lfb_save_font,
+ s3lfb_load_font,
+ s3lfb_show_font,
+ s3lfb_save_palette,
+ s3lfb_load_palette,
+ s3lfb_set_border,
+ s3lfb_save_state,
+ s3lfb_load_state,
+ s3lfb_set_origin,
+ s3lfb_read_hw_cursor,
+ s3lfb_set_hw_cursor,
+ s3lfb_set_hw_cursor_shape,
+ s3lfb_blank_display,
+ s3lfb_mmap,
+ s3lfb_ioctl,
+ s3lfb_clear,
+ s3lfb_fill_rect,
+ s3lfb_bitblt,
+ s3lfb_error,
+ s3lfb_error,
+ s3lfb_diag,
+};
+
+static video_switch_t *prevvidsw;
+static device_t s3pci_dev = NULL;
+
+static int
+s3lfb_probe(int unit, video_adapter_t **adpp, void *arg, int flags)
+{
+ return (*prevvidsw->probe)(unit, adpp, arg, flags);
+}
+
+static int
+s3lfb_init(int unit, video_adapter_t *adp, int flags)
+{
+ return (*prevvidsw->init)(unit, adp, flags);
+}
+
+static int
+s3lfb_get_info(video_adapter_t *adp, int mode, video_info_t *info)
+{
+ device_t dev = s3pci_dev; /* XXX */
+ struct s3pci_softc *sc = (struct s3pci_softc *)device_get_softc(dev);
+ int error;
+
+ if ((error = (*prevvidsw->get_info)(adp, mode, info)))
+ return error;
+
+#if 0
+ /* Don't use linear addressing with text modes
+ */
+ if ((mode > M_VESA_BASE) &&
+ (info->vi_flags & V_INFO_GRAPHICS) &&
+ !(info->vi_flags & V_INFO_LINEAR)) {
+
+ info->vi_flags |= V_INFO_LINEAR;
+ info->vi_buffer = sc->mem_base;
+
+ } else {
+ info->vi_buffer = 0;
+ }
+#endif
+
+ return 0;
+}
+
+static int
+s3lfb_query_mode(video_adapter_t *adp, video_info_t *info)
+{
+ return (*prevvidsw->query_mode)(adp, info);
+}
+
+static vm_offset_t
+s3lfb_map_buffer(u_int paddr, size_t size)
+{
+ vm_offset_t vaddr;
+ u_int off;
+
+ off = paddr - trunc_page(paddr);
+ vaddr = (vm_offset_t)pmap_mapdev(paddr - off, size + off);
+
+ return (vaddr + off);
+}
+
+static int
+s3lfb_set_mode(video_adapter_t *adp, int mode)
+{
+ device_t dev = s3pci_dev; /* XXX */
+ struct s3pci_softc *sc = (struct s3pci_softc *)device_get_softc(dev);
+ u_long cr59, cr5A;
+ unsigned char tmp;
+ int error;
+
+ /* First, set the mode as if it was a classic VESA card
+ */
+ if ((error = (*prevvidsw->set_mode)(adp, mode)))
+ return error;
+
+ /* If not in a linear mode (according to s3lfb_get_info() called
+ * by vesa_set_mode in the (*vidsw[adp->va_index]->get_info)...
+ * sequence, return with no error
+ */
+#if 0
+ if (!(adp->va_info.vi_flags & V_INFO_LINEAR))
+ return 0;
+#endif
+
+ if ((mode <= M_VESA_BASE) ||
+ !(adp->va_info.vi_flags & V_INFO_GRAPHICS) ||
+ (adp->va_info.vi_flags & V_INFO_LINEAR))
+ return 0;
+
+ /* Ok, now apply the configuration to the card */
+
+ outb_p(0x38, S3_CRTC_ADDR); outb_p(0x48, S3_CRTC_VALUE);
+ outb_p(0x39, S3_CRTC_ADDR); outb_p(0xa5, S3_CRTC_VALUE);
+
+ /* check that CR47 is read/write */
+
+#if 0
+ outb_p(0x47, S3_CRTC_ADDR); outb_p(0xff, S3_CRTC_VALUE);
+ tmp = inb_p(S3_CRTC_VALUE);
+ outb_p(0x00, S3_CRTC_VALUE);
+ if ((tmp != 0xff) || (inb_p(S3_CRTC_VALUE)))
+ {
+ /* lock S3 registers */
+
+ outb_p(0x39, S3_CRTC_ADDR); outb_p(0x5a, S3_CRTC_VALUE);
+ outb_p(0x38, S3_CRTC_ADDR); outb_p(0x00, S3_CRTC_VALUE);
+
+ return ENXIO;
+ }
+#endif
+
+ /* enable enhanced register access */
+
+ outb_p(0x40, S3_CRTC_ADDR);
+ outb_p(inb_p(S3_CRTC_VALUE) | 1, S3_CRTC_VALUE);
+
+ /* enable enhanced functions */
+
+ outb_enh(inb_enh(0) | 1, 0x0);
+
+ /* enable enhanced mode memory mapping */
+
+ outb_p(0x31, S3_CRTC_ADDR);
+ outb_p(inb_p(S3_CRTC_VALUE) | 8, S3_CRTC_VALUE);
+
+ /* enable linear frame buffer and set address window to max */
+
+ outb_p(0x58, S3_CRTC_ADDR);
+ outb_p(inb_p(S3_CRTC_VALUE) | 0x13, S3_CRTC_VALUE);
+
+ /* disabled enhanced register access */
+
+ outb_p(0x40, S3_CRTC_ADDR);
+ outb_p(inb_p(S3_CRTC_VALUE) & ~1, S3_CRTC_VALUE);
+
+ /* lock S3 registers */
+
+ outb_p(0x39, S3_CRTC_ADDR); outb_p(0x5a, S3_CRTC_VALUE);
+ outb_p(0x38, S3_CRTC_ADDR); outb_p(0x00, S3_CRTC_VALUE);
+
+ adp->va_info.vi_flags |= V_INFO_LINEAR;
+ adp->va_info.vi_buffer = sc->mem_base;
+ adp->va_buffer = s3lfb_map_buffer(adp->va_info.vi_buffer,
+ adp->va_info.vi_buffer_size);
+ adp->va_buffer_size = adp->va_info.vi_buffer_size;
+ adp->va_window = adp->va_buffer;
+ adp->va_window_size = adp->va_info.vi_buffer_size/adp->va_info.vi_planes;
+ adp->va_window_gran = adp->va_info.vi_buffer_size/adp->va_info.vi_planes;
+
+ return 0;
+}
+
+static int
+s3lfb_save_font(video_adapter_t *adp, int page, int fontsize, u_char *data,
+ int ch, int count)
+{
+ return (*prevvidsw->save_font)(adp, page, fontsize, data, ch, count);
+}
+
+static int
+s3lfb_load_font(video_adapter_t *adp, int page, int fontsize, u_char *data,
+ int ch, int count)
+{
+ return (*prevvidsw->load_font)(adp, page, fontsize, data, ch, count);
+}
+
+static int
+s3lfb_show_font(video_adapter_t *adp, int page)
+{
+ return (*prevvidsw->show_font)(adp, page);
+}
+
+static int
+s3lfb_save_palette(video_adapter_t *adp, u_char *palette)
+{
+ return (*prevvidsw->save_palette)(adp, palette);
+}
+
+static int
+s3lfb_load_palette(video_adapter_t *adp, u_char *palette)
+{
+ return (*prevvidsw->load_palette)(adp, palette);
+}
+
+static int
+s3lfb_set_border(video_adapter_t *adp, int color)
+{
+ return (*prevvidsw->set_border)(adp, color);
+}
+
+static int
+s3lfb_save_state(video_adapter_t *adp, void *p, size_t size)
+{
+ return (*prevvidsw->save_state)(adp, p, size);
+}
+
+static int
+s3lfb_load_state(video_adapter_t *adp, void *p)
+{
+ return (*prevvidsw->load_state)(adp, p);
+}
+
+static int
+s3lfb_set_origin(video_adapter_t *adp, off_t offset)
+{
+ return (*prevvidsw->set_win_org)(adp, offset);
+}
+
+static int
+s3lfb_read_hw_cursor(video_adapter_t *adp, int *col, int *row)
+{
+ return (*prevvidsw->read_hw_cursor)(adp, col, row);
+}
+
+static int
+s3lfb_set_hw_cursor(video_adapter_t *adp, int col, int row)
+{
+ return (*prevvidsw->set_hw_cursor)(adp, col, row);
+}
+
+static int
+s3lfb_set_hw_cursor_shape(video_adapter_t *adp, int base, int height,
+ int celsize, int blink)
+{
+ return (*prevvidsw->set_hw_cursor_shape)(adp, base, height,
+ celsize, blink);
+}
+
+static int
+s3lfb_blank_display(video_adapter_t *adp, int mode)
+{
+ return (*prevvidsw->blank_display)(adp, mode);
+}
+
+static int
+s3lfb_mmap(video_adapter_t *adp, vm_offset_t offset, int prot)
+{
+ return (*prevvidsw->mmap)(adp, offset, prot);
+}
+
+static int
+s3lfb_clear(video_adapter_t *adp)
+{
+ return (*prevvidsw->clear)(adp);
+}
+
+static int
+s3lfb_fill_rect(video_adapter_t *adp, int val, int x, int y, int cx, int cy)
+{
+ return (*prevvidsw->fill_rect)(adp, val, x, y, cx, cy);
+}
+
+static int
+s3lfb_bitblt(video_adapter_t *adp,...)
+{
+ return (*prevvidsw->bitblt)(adp); /* XXX */
+}
+
+static int
+s3lfb_ioctl(video_adapter_t *adp, u_long cmd, caddr_t arg)
+{
+ return (*prevvidsw->ioctl)(adp, cmd, arg);
+}
+
+static int
+s3lfb_diag(video_adapter_t *adp, int level)
+{
+ return (*prevvidsw->diag)(adp, level);
+}
+
+static int
+s3lfb_error(void)
+{
+ return 1;
+}
+
+/***********************************/
+/* PCI detection/attachement stuff */
+/***********************************/
+
+static int
+s3pci_probe(device_t dev)
+{
+ u_int32_t vendor, class, subclass, device_id;
+
+ device_id = pci_get_devid(dev);
+ vendor = device_id & 0xffff;
+ class = pci_get_class(dev);
+ subclass = pci_get_subclass(dev);
+
+ if ((class != PCIC_DISPLAY) || (subclass != PCIS_DISPLAY_VGA) ||
+ (vendor != PCI_S3_VENDOR_ID))
+ return ENXIO;
+
+ device_set_desc(dev, "S3 graphic card");
+
+ bus_set_resource(dev, SYS_RES_IOPORT, 0,
+ S3_CONFIG_IO, S3_CONFIG_IO_SIZE);
+ bus_set_resource(dev, SYS_RES_IOPORT, 1,
+ S3_ENHANCED_IO, S3_ENHANCED_IO_SIZE);
+
+ return 0;
+
+};
+
+static int
+s3pci_attach(device_t dev)
+{
+ struct s3pci_softc* sc = (struct s3pci_softc*)device_get_softc(dev);
+ video_adapter_t *adp;
+
+#if 0
+ unsigned char tmp;
+#endif
+ int rid, i;
+
+ if (s3pci_dev) {
+ printf("%s: driver already attached!\n", __FUNCTION__);
+ goto error;
+ }
+
+ /* Allocate resources
+ */
+ rid = 0;
+ if (!(sc->port_res = bus_alloc_resource(dev, SYS_RES_IOPORT, &rid,
+ 0ul, ~0ul, 0, RF_ACTIVE | RF_SHAREABLE))) {
+ printf("%s: port resource allocation failed!\n", __FUNCTION__);
+ goto error;
+ }
+ sc->st = rman_get_bustag(sc->port_res);
+ sc->sh = rman_get_bushandle(sc->port_res);
+
+ rid = 1;
+ if (!(sc->enh_res = bus_alloc_resource(dev, SYS_RES_IOPORT, &rid,
+ 0ul, ~0ul, 0, RF_ACTIVE | RF_SHAREABLE))) {
+ printf("%s: enhanced port resource allocation failed!\n",
+ __FUNCTION__);
+ goto error;
+ }
+ sc->enh_st = rman_get_bustag(sc->enh_res);
+ sc->enh_sh = rman_get_bushandle(sc->enh_res);
+
+ rid = PCI_BASE_MEMORY;
+ if (!(sc->mem_res = bus_alloc_resource(dev, SYS_RES_MEMORY, &rid,
+ 0, ~0, 1, RF_ACTIVE))) {
+
+ printf("%s: mem resource allocation failed!\n", __FUNCTION__);
+ goto error;
+ }
+
+ /* The memory base address will be our LFB base address
+ */
+ /* sc->mem_base = (u_long)rman_get_virtual(sc->mem_res); */
+ sc->mem_base = bus_get_resource_start(dev, SYS_RES_MEMORY, rid);
+ sc->mem_size = bus_get_resource_count(dev, SYS_RES_MEMORY, rid);
+
+ /* Attach the driver to the VGA/VESA framework
+ */
+ for (i = 0; (adp = vid_get_adapter(i)) != NULL; ++i) {
+ if ((adp->va_type == KD_VGA))
+ break;
+ }
+
+ /* If the VESA module hasn't been loaded, or VGA doesn't
+ * exist, abort
+ */
+ if ((adp == NULL) || !(adp->va_flags & V_ADP_VESA)) {
+ printf("%s: VGA adapter not found or VESA module not loaded!\n",
+ __FUNCTION__);
+ goto error;
+ }
+
+ /* Replace the VESA video switch by owers
+ */
+ prevvidsw = vidsw[adp->va_index];
+ vidsw[adp->va_index] = &s3lfbvidsw;
+
+ /* Remember who we are on the bus */
+ s3pci_dev = (void *)dev; /* XXX */
+
+ return 0;
+
+error:
+ if (sc->mem_res)
+ bus_release_resource(dev, SYS_RES_MEMORY, PCI_BASE_MEMORY, sc->mem_res);
+
+ if (sc->enh_res)
+ bus_release_resource(dev, SYS_RES_IOPORT, 1, sc->enh_res);
+
+ if (sc->port_res)
+ bus_release_resource(dev, SYS_RES_IOPORT, 0, sc->port_res);
+
+ return ENXIO;
+};
+
+static device_method_t s3pci_methods[] = {
+
+ DEVMETHOD(device_probe, s3pci_probe),
+ DEVMETHOD(device_attach, s3pci_attach),
+ {0,0}
+};
+
+static driver_t s3pci_driver = {
+ "s3pci",
+ s3pci_methods,
+ sizeof(struct s3pci_softc),
+};
+
+static devclass_t s3pci_devclass;
+
+DRIVER_MODULE(s3pci, pci, s3pci_driver, s3pci_devclass, 0, 0);
diff --git a/sys/modules/Makefile b/sys/modules/Makefile
index a10b490..a0e9a70 100644
--- a/sys/modules/Makefile
+++ b/sys/modules/Makefile
@@ -19,7 +19,7 @@ SUBDIR= 3dfx accf_data accf_http agp aha amr an aue \
# XXX some of these can move to the general case when de-i386'ed
.if ${MACHINE_ARCH} == "i386"
SUBDIR+=aac aic asr bktr coff el fpu gnufpu ibcs2 linprocfs mly pecoff ray \
- splash streams svr4 vesa wi
+ s3 splash streams svr4 vesa wi
.endif
.if ${MACHINE} == "pc98"
diff --git a/sys/modules/s3/Makefile b/sys/modules/s3/Makefile
new file mode 100644
index 0000000..ac42a42
--- /dev/null
+++ b/sys/modules/s3/Makefile
@@ -0,0 +1,9 @@
+# $FreeBSD$
+
+.PATH: ${.CURDIR}/../../dev/fb
+KMOD = s3
+SRCS = device_if.h bus_if.h isa_if.h pci_if.h
+SRCS += s3pci.c
+KMODDEPS = vesa
+
+.include <bsd.kmod.mk>
OpenPOWER on IntegriCloud