2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
4 * Copyright (c) 2000 Alcove - Nicolas Souchu <nsouch@freebsd.org>
7 * Code based on Peter Horton <pdh@colonel-panic.com> patch.
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
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.
18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 #include <sys/cdefs.h>
32 __FBSDID("$FreeBSD$");
34 /* Enable LFB on S3 cards that has only VESA 1.2 BIOS */
36 #include <sys/param.h>
37 #include <sys/systm.h>
38 #include <sys/kernel.h>
39 #include <machine/bus.h>
42 #include <vm/vm_extern.h>
43 #include <vm/vm_kern.h>
47 #include <sys/module.h>
50 #include <machine/resource.h>
52 #include <sys/malloc.h>
55 #include <dev/pci/pcireg.h>
56 #include <dev/pci/pcivar.h>
58 #include <machine/md_var.h>
59 #include <machine/pc/bios.h>
60 #include <dev/fb/vesa.h>
62 #include <dev/fb/fbreg.h>
63 #include <dev/fb/vgareg.h>
67 #define PCI_S3_VENDOR_ID 0x5333
69 #define S3_CONFIG_IO 0x3c0 /* VGA standard config io ports */
70 #define S3_CONFIG_IO_SIZE 0x20
72 #define S3_ENHANCED_IO 0x4ae8 /* Extended config register */
73 #define S3_ENHANCED_IO_SIZE 1
75 #define S3_CRTC_ADDR 0x14
76 #define S3_CRTC_VALUE 0x15
78 #define PCI_BASE_MEMORY 0x10
80 #define outb_p(value, offset) bus_space_write_1(sc->st, sc->sh, offset, value)
81 #define inb_p(offset) (bus_space_read_1(sc->st, sc->sh, offset))
82 #define outb_enh(value, offset) bus_space_write_1(sc->enh_st, sc->enh_sh, \
84 #define inb_enh(offset) (bus_space_read_1(sc->enh_st, sc->enh_sh, offset))
88 bus_space_handle_t sh;
89 bus_space_tag_t enh_st;
90 bus_space_handle_t enh_sh;
91 struct resource *port_res;
92 struct resource *enh_res;
93 struct resource *mem_res;
98 static int s3lfb_error(void);
99 static vi_probe_t s3lfb_probe;
100 static vi_init_t s3lfb_init;
101 static vi_get_info_t s3lfb_get_info;
102 static vi_query_mode_t s3lfb_query_mode;
103 static vi_set_mode_t s3lfb_set_mode;
104 static vi_save_font_t s3lfb_save_font;
105 static vi_load_font_t s3lfb_load_font;
106 static vi_show_font_t s3lfb_show_font;
107 static vi_save_palette_t s3lfb_save_palette;
108 static vi_load_palette_t s3lfb_load_palette;
109 static vi_set_border_t s3lfb_set_border;
110 static vi_save_state_t s3lfb_save_state;
111 static vi_load_state_t s3lfb_load_state;
112 static vi_set_win_org_t s3lfb_set_origin;
113 static vi_read_hw_cursor_t s3lfb_read_hw_cursor;
114 static vi_set_hw_cursor_t s3lfb_set_hw_cursor;
115 static vi_set_hw_cursor_shape_t s3lfb_set_hw_cursor_shape;
116 static vi_blank_display_t s3lfb_blank_display;
117 static vi_mmap_t s3lfb_mmap;
118 static vi_ioctl_t s3lfb_ioctl;
119 static vi_clear_t s3lfb_clear;
120 static vi_fill_rect_t s3lfb_fill_rect;
121 static vi_bitblt_t s3lfb_bitblt;
122 static vi_diag_t s3lfb_diag;
124 static video_switch_t s3lfbvidsw = {
139 s3lfb_read_hw_cursor,
141 s3lfb_set_hw_cursor_shape,
153 static video_switch_t *prevvidsw;
154 static device_t s3pci_dev = NULL;
157 s3lfb_probe(int unit, video_adapter_t **adpp, void *arg, int flags)
159 return (*prevvidsw->probe)(unit, adpp, arg, flags);
163 s3lfb_init(int unit, video_adapter_t *adp, int flags)
165 return (*prevvidsw->init)(unit, adp, flags);
169 s3lfb_get_info(video_adapter_t *adp, int mode, video_info_t *info)
172 device_t dev = s3pci_dev; /* XXX */
173 struct s3pci_softc *sc = (struct s3pci_softc *)device_get_softc(dev);
177 if ((error = (*prevvidsw->get_info)(adp, mode, info)))
181 /* Don't use linear addressing with text modes
183 if ((mode > M_VESA_BASE) &&
184 (info->vi_flags & V_INFO_GRAPHICS) &&
185 !(info->vi_flags & V_INFO_LINEAR)) {
187 info->vi_flags |= V_INFO_LINEAR;
188 info->vi_buffer = sc->mem_base;
199 s3lfb_query_mode(video_adapter_t *adp, video_info_t *info)
201 return (*prevvidsw->query_mode)(adp, info);
205 s3lfb_map_buffer(u_int paddr, size_t size)
210 off = paddr - trunc_page(paddr);
211 vaddr = (vm_offset_t)pmap_mapdev(paddr - off, size + off);
213 return (vaddr + off);
217 s3lfb_set_mode(video_adapter_t *adp, int mode)
219 device_t dev = s3pci_dev; /* XXX */
220 struct s3pci_softc *sc = (struct s3pci_softc *)device_get_softc(dev);
226 /* First, set the mode as if it was a classic VESA card
228 if ((error = (*prevvidsw->set_mode)(adp, mode)))
231 /* If not in a linear mode (according to s3lfb_get_info() called
232 * by vesa_set_mode in the (*vidsw[adp->va_index]->get_info)...
233 * sequence, return with no error
236 if (!(adp->va_info.vi_flags & V_INFO_LINEAR))
240 if ((mode <= M_VESA_BASE) ||
241 !(adp->va_info.vi_flags & V_INFO_GRAPHICS) ||
242 (adp->va_info.vi_flags & V_INFO_LINEAR))
245 /* Ok, now apply the configuration to the card */
247 outb_p(0x38, S3_CRTC_ADDR); outb_p(0x48, S3_CRTC_VALUE);
248 outb_p(0x39, S3_CRTC_ADDR); outb_p(0xa5, S3_CRTC_VALUE);
250 /* check that CR47 is read/write */
253 outb_p(0x47, S3_CRTC_ADDR); outb_p(0xff, S3_CRTC_VALUE);
254 tmp = inb_p(S3_CRTC_VALUE);
255 outb_p(0x00, S3_CRTC_VALUE);
256 if ((tmp != 0xff) || (inb_p(S3_CRTC_VALUE)))
258 /* lock S3 registers */
260 outb_p(0x39, S3_CRTC_ADDR); outb_p(0x5a, S3_CRTC_VALUE);
261 outb_p(0x38, S3_CRTC_ADDR); outb_p(0x00, S3_CRTC_VALUE);
267 /* enable enhanced register access */
269 outb_p(0x40, S3_CRTC_ADDR);
270 outb_p(inb_p(S3_CRTC_VALUE) | 1, S3_CRTC_VALUE);
272 /* enable enhanced functions */
274 outb_enh(inb_enh(0) | 1, 0x0);
276 /* enable enhanced mode memory mapping */
278 outb_p(0x31, S3_CRTC_ADDR);
279 outb_p(inb_p(S3_CRTC_VALUE) | 8, S3_CRTC_VALUE);
281 /* enable linear frame buffer and set address window to max */
283 outb_p(0x58, S3_CRTC_ADDR);
284 outb_p(inb_p(S3_CRTC_VALUE) | 0x13, S3_CRTC_VALUE);
286 /* disabled enhanced register access */
288 outb_p(0x40, S3_CRTC_ADDR);
289 outb_p(inb_p(S3_CRTC_VALUE) & ~1, S3_CRTC_VALUE);
291 /* lock S3 registers */
293 outb_p(0x39, S3_CRTC_ADDR); outb_p(0x5a, S3_CRTC_VALUE);
294 outb_p(0x38, S3_CRTC_ADDR); outb_p(0x00, S3_CRTC_VALUE);
296 adp->va_info.vi_flags |= V_INFO_LINEAR;
297 adp->va_info.vi_buffer = sc->mem_base;
298 adp->va_buffer = s3lfb_map_buffer(adp->va_info.vi_buffer,
299 adp->va_info.vi_buffer_size);
300 adp->va_buffer_size = adp->va_info.vi_buffer_size;
301 adp->va_window = adp->va_buffer;
302 adp->va_window_size = adp->va_info.vi_buffer_size/adp->va_info.vi_planes;
303 adp->va_window_gran = adp->va_info.vi_buffer_size/adp->va_info.vi_planes;
309 s3lfb_save_font(video_adapter_t *adp, int page, int fontsize, int fontwidth,
310 u_char *data, int ch, int count)
312 return (*prevvidsw->save_font)(adp, page, fontsize, fontwidth, data,
317 s3lfb_load_font(video_adapter_t *adp, int page, int fontsize, int fontwidth,
318 u_char *data, int ch, int count)
320 return (*prevvidsw->load_font)(adp, page, fontsize, fontwidth, data,
325 s3lfb_show_font(video_adapter_t *adp, int page)
327 return (*prevvidsw->show_font)(adp, page);
331 s3lfb_save_palette(video_adapter_t *adp, u_char *palette)
333 return (*prevvidsw->save_palette)(adp, palette);
337 s3lfb_load_palette(video_adapter_t *adp, u_char *palette)
339 return (*prevvidsw->load_palette)(adp, palette);
343 s3lfb_set_border(video_adapter_t *adp, int color)
345 return (*prevvidsw->set_border)(adp, color);
349 s3lfb_save_state(video_adapter_t *adp, void *p, size_t size)
351 return (*prevvidsw->save_state)(adp, p, size);
355 s3lfb_load_state(video_adapter_t *adp, void *p)
357 return (*prevvidsw->load_state)(adp, p);
361 s3lfb_set_origin(video_adapter_t *adp, off_t offset)
363 return (*prevvidsw->set_win_org)(adp, offset);
367 s3lfb_read_hw_cursor(video_adapter_t *adp, int *col, int *row)
369 return (*prevvidsw->read_hw_cursor)(adp, col, row);
373 s3lfb_set_hw_cursor(video_adapter_t *adp, int col, int row)
375 return (*prevvidsw->set_hw_cursor)(adp, col, row);
379 s3lfb_set_hw_cursor_shape(video_adapter_t *adp, int base, int height,
380 int celsize, int blink)
382 return (*prevvidsw->set_hw_cursor_shape)(adp, base, height,
387 s3lfb_blank_display(video_adapter_t *adp, int mode)
389 return (*prevvidsw->blank_display)(adp, mode);
393 s3lfb_mmap(video_adapter_t *adp, vm_ooffset_t offset, vm_paddr_t *paddr,
394 int prot, vm_memattr_t *memattr)
396 return (*prevvidsw->mmap)(adp, offset, paddr, prot, memattr);
400 s3lfb_clear(video_adapter_t *adp)
402 return (*prevvidsw->clear)(adp);
406 s3lfb_fill_rect(video_adapter_t *adp, int val, int x, int y, int cx, int cy)
408 return (*prevvidsw->fill_rect)(adp, val, x, y, cx, cy);
412 s3lfb_bitblt(video_adapter_t *adp,...)
414 return (*prevvidsw->bitblt)(adp); /* XXX */
418 s3lfb_ioctl(video_adapter_t *adp, u_long cmd, caddr_t arg)
420 return (*prevvidsw->ioctl)(adp, cmd, arg);
424 s3lfb_diag(video_adapter_t *adp, int level)
426 return (*prevvidsw->diag)(adp, level);
435 /***********************************/
436 /* PCI detection/attachement stuff */
437 /***********************************/
440 s3pci_probe(device_t dev)
442 u_int32_t vendor, class, subclass, device_id;
444 device_id = pci_get_devid(dev);
445 vendor = device_id & 0xffff;
446 class = pci_get_class(dev);
447 subclass = pci_get_subclass(dev);
449 if ((class != PCIC_DISPLAY) || (subclass != PCIS_DISPLAY_VGA) ||
450 (vendor != PCI_S3_VENDOR_ID))
453 device_set_desc(dev, "S3 graphic card");
455 bus_set_resource(dev, SYS_RES_IOPORT, 0,
456 S3_CONFIG_IO, S3_CONFIG_IO_SIZE);
457 bus_set_resource(dev, SYS_RES_IOPORT, 1,
458 S3_ENHANCED_IO, S3_ENHANCED_IO_SIZE);
460 return BUS_PROBE_DEFAULT;
465 s3pci_attach(device_t dev)
467 struct s3pci_softc* sc = (struct s3pci_softc*)device_get_softc(dev);
468 video_adapter_t *adp;
476 printf("%s: driver already attached!\n", __func__);
480 /* Allocate resources
483 if (!(sc->port_res = bus_alloc_resource_any(dev, SYS_RES_IOPORT, &rid,
484 RF_ACTIVE | RF_SHAREABLE))) {
485 printf("%s: port resource allocation failed!\n", __func__);
488 sc->st = rman_get_bustag(sc->port_res);
489 sc->sh = rman_get_bushandle(sc->port_res);
492 if (!(sc->enh_res = bus_alloc_resource_any(dev, SYS_RES_IOPORT, &rid,
493 RF_ACTIVE | RF_SHAREABLE))) {
494 printf("%s: enhanced port resource allocation failed!\n",
498 sc->enh_st = rman_get_bustag(sc->enh_res);
499 sc->enh_sh = rman_get_bushandle(sc->enh_res);
501 rid = PCI_BASE_MEMORY;
502 if (!(sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
505 printf("%s: mem resource allocation failed!\n", __func__);
509 /* The memory base address will be our LFB base address
511 /* sc->mem_base = (u_long)rman_get_virtual(sc->mem_res); */
512 sc->mem_base = bus_get_resource_start(dev, SYS_RES_MEMORY, rid);
513 sc->mem_size = bus_get_resource_count(dev, SYS_RES_MEMORY, rid);
515 /* Attach the driver to the VGA/VESA framework
517 for (i = 0; (adp = vid_get_adapter(i)) != NULL; ++i) {
518 if (adp->va_type == KD_VGA)
522 /* If the VESA module hasn't been loaded, or VGA doesn't
525 if ((adp == NULL) || !(adp->va_flags & V_ADP_VESA)) {
526 printf("%s: VGA adapter not found or VESA module not loaded!\n",
531 /* Replace the VESA video switch by owers
533 prevvidsw = vidsw[adp->va_index];
534 vidsw[adp->va_index] = &s3lfbvidsw;
536 /* Remember who we are on the bus */
537 s3pci_dev = (void *)dev; /* XXX */
543 bus_release_resource(dev, SYS_RES_MEMORY, PCI_BASE_MEMORY, sc->mem_res);
546 bus_release_resource(dev, SYS_RES_IOPORT, 1, sc->enh_res);
549 bus_release_resource(dev, SYS_RES_IOPORT, 0, sc->port_res);
554 static device_method_t s3pci_methods[] = {
556 DEVMETHOD(device_probe, s3pci_probe),
557 DEVMETHOD(device_attach, s3pci_attach),
561 static driver_t s3pci_driver = {
564 sizeof(struct s3pci_softc),
567 static devclass_t s3pci_devclass;
569 DRIVER_MODULE(s3pci, pci, s3pci_driver, s3pci_devclass, 0, 0);