1/*- 2 * SPDX-License-Identifier: BSD-2-Clause 3 * 4 * Copyright (c) 2000 Alcove - Nicolas Souchu <nsouch@freebsd.org> 5 * All rights reserved. 6 * 7 * Code based on Peter Horton <pdh@colonel-panic.com> patch. 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 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 28 * SUCH DAMAGE. 29 */ 30 31#include <sys/cdefs.h> 32/* Enable LFB on S3 cards that has only VESA 1.2 BIOS */ 33 34#include <sys/param.h> 35#include <sys/systm.h> 36#include <sys/kernel.h> 37#include <machine/bus.h> 38 39#include <vm/vm.h> 40#include <vm/vm_extern.h> 41#include <vm/vm_kern.h> 42#include <vm/pmap.h> 43 44#include <sys/uio.h> 45#include <sys/module.h> 46#include <sys/bus.h> 47#include <sys/rman.h> 48#include <machine/resource.h> 49 50#include <sys/malloc.h> 51#include <sys/fbio.h> 52 53#include <dev/pci/pcireg.h> 54#include <dev/pci/pcivar.h> 55 56#include <machine/md_var.h> 57#include <machine/pc/bios.h> 58#include <dev/fb/vesa.h> 59 60#include <dev/fb/fbreg.h> 61#include <dev/fb/vgareg.h> 62 63#define S3PCI_DEBUG 1 64 65#define PCI_S3_VENDOR_ID 0x5333 66 67#define S3_CONFIG_IO 0x3c0 /* VGA standard config io ports */ 68#define S3_CONFIG_IO_SIZE 0x20 69 70#define S3_ENHANCED_IO 0x4ae8 /* Extended config register */ 71#define S3_ENHANCED_IO_SIZE 1 72 73#define S3_CRTC_ADDR 0x14 74#define S3_CRTC_VALUE 0x15 75 76#define PCI_BASE_MEMORY 0x10 77 78#define outb_p(value, offset) bus_space_write_1(sc->st, sc->sh, offset, value) 79#define inb_p(offset) (bus_space_read_1(sc->st, sc->sh, offset)) 80#define outb_enh(value, offset) bus_space_write_1(sc->enh_st, sc->enh_sh, \ 81 offset, value) 82#define inb_enh(offset) (bus_space_read_1(sc->enh_st, sc->enh_sh, offset)) 83 84struct s3pci_softc { 85 bus_space_tag_t st; 86 bus_space_handle_t sh; 87 bus_space_tag_t enh_st; 88 bus_space_handle_t enh_sh; 89 struct resource *port_res; 90 struct resource *enh_res; 91 struct resource *mem_res; 92 u_long mem_base; 93 u_long mem_size; 94}; 95 96static int s3lfb_error(void); 97static vi_probe_t s3lfb_probe; 98static vi_init_t s3lfb_init; 99static vi_get_info_t s3lfb_get_info; 100static vi_query_mode_t s3lfb_query_mode; 101static vi_set_mode_t s3lfb_set_mode; 102static vi_save_font_t s3lfb_save_font; 103static vi_load_font_t s3lfb_load_font; 104static vi_show_font_t s3lfb_show_font; 105static vi_save_palette_t s3lfb_save_palette; 106static vi_load_palette_t s3lfb_load_palette; 107static vi_set_border_t s3lfb_set_border; 108static vi_save_state_t s3lfb_save_state; 109static vi_load_state_t s3lfb_load_state; 110static vi_set_win_org_t s3lfb_set_origin; 111static vi_read_hw_cursor_t s3lfb_read_hw_cursor; 112static vi_set_hw_cursor_t s3lfb_set_hw_cursor; 113static vi_set_hw_cursor_shape_t s3lfb_set_hw_cursor_shape; 114static vi_blank_display_t s3lfb_blank_display; 115static vi_mmap_t s3lfb_mmap; 116static vi_ioctl_t s3lfb_ioctl; 117static vi_clear_t s3lfb_clear; 118static vi_fill_rect_t s3lfb_fill_rect; 119static vi_bitblt_t s3lfb_bitblt; 120static vi_diag_t s3lfb_diag; 121 122static video_switch_t s3lfbvidsw = { 123 s3lfb_probe, 124 s3lfb_init, 125 s3lfb_get_info, 126 s3lfb_query_mode, 127 s3lfb_set_mode, 128 s3lfb_save_font, 129 s3lfb_load_font, 130 s3lfb_show_font, 131 s3lfb_save_palette, 132 s3lfb_load_palette, 133 s3lfb_set_border, 134 s3lfb_save_state, 135 s3lfb_load_state, 136 s3lfb_set_origin, 137 s3lfb_read_hw_cursor, 138 s3lfb_set_hw_cursor, 139 s3lfb_set_hw_cursor_shape, 140 s3lfb_blank_display, 141 s3lfb_mmap, 142 s3lfb_ioctl, 143 s3lfb_clear, 144 s3lfb_fill_rect, 145 s3lfb_bitblt, 146 s3lfb_error, 147 s3lfb_error, 148 s3lfb_diag, 149}; 150 151static video_switch_t *prevvidsw; 152static device_t s3pci_dev = NULL; 153 154static int 155s3lfb_probe(int unit, video_adapter_t **adpp, void *arg, int flags) 156{ 157 return (*prevvidsw->probe)(unit, adpp, arg, flags); 158} 159 160static int 161s3lfb_init(int unit, video_adapter_t *adp, int flags) 162{ 163 return (*prevvidsw->init)(unit, adp, flags); 164} 165 166static int 167s3lfb_get_info(video_adapter_t *adp, int mode, video_info_t *info) 168{ 169#if 0 170 device_t dev = s3pci_dev; /* XXX */ 171 struct s3pci_softc *sc = (struct s3pci_softc *)device_get_softc(dev); 172#endif 173 int error; 174 175 if ((error = (*prevvidsw->get_info)(adp, mode, info))) 176 return error; 177 178#if 0 179 /* Don't use linear addressing with text modes 180 */ 181 if ((mode > M_VESA_BASE) && 182 (info->vi_flags & V_INFO_GRAPHICS) && 183 !(info->vi_flags & V_INFO_LINEAR)) { 184 185 info->vi_flags |= V_INFO_LINEAR; 186 info->vi_buffer = sc->mem_base; 187 188 } else { 189 info->vi_buffer = 0; 190 } 191#endif 192 193 return 0; 194} 195 196static int 197s3lfb_query_mode(video_adapter_t *adp, video_info_t *info) 198{ 199 return (*prevvidsw->query_mode)(adp, info); 200} 201 202static vm_offset_t 203s3lfb_map_buffer(u_int paddr, size_t size) 204{ 205 vm_offset_t vaddr; 206 u_int off; 207 208 off = paddr - trunc_page(paddr); 209 vaddr = (vm_offset_t)pmap_mapdev(paddr - off, size + off); 210 211 return (vaddr + off); 212} 213 214static int 215s3lfb_set_mode(video_adapter_t *adp, int mode) 216{ 217 device_t dev = s3pci_dev; /* XXX */ 218 struct s3pci_softc *sc = (struct s3pci_softc *)device_get_softc(dev); 219#if 0 220 unsigned char tmp; 221#endif 222 int error; 223 224 /* First, set the mode as if it was a classic VESA card 225 */ 226 if ((error = (*prevvidsw->set_mode)(adp, mode))) 227 return error; 228 229 /* If not in a linear mode (according to s3lfb_get_info() called 230 * by vesa_set_mode in the (*vidsw[adp->va_index]->get_info)... 231 * sequence, return with no error 232 */ 233#if 0 234 if (!(adp->va_info.vi_flags & V_INFO_LINEAR)) 235 return 0; 236#endif 237 238 if ((mode <= M_VESA_BASE) || 239 !(adp->va_info.vi_flags & V_INFO_GRAPHICS) || 240 (adp->va_info.vi_flags & V_INFO_LINEAR)) 241 return 0; 242 243 /* Ok, now apply the configuration to the card */ 244 245 outb_p(0x38, S3_CRTC_ADDR); outb_p(0x48, S3_CRTC_VALUE); 246 outb_p(0x39, S3_CRTC_ADDR); outb_p(0xa5, S3_CRTC_VALUE); 247 248 /* check that CR47 is read/write */ 249 250#if 0 251 outb_p(0x47, S3_CRTC_ADDR); outb_p(0xff, S3_CRTC_VALUE); 252 tmp = inb_p(S3_CRTC_VALUE); 253 outb_p(0x00, S3_CRTC_VALUE); 254 if ((tmp != 0xff) || (inb_p(S3_CRTC_VALUE))) 255 { 256 /* lock S3 registers */ 257 258 outb_p(0x39, S3_CRTC_ADDR); outb_p(0x5a, S3_CRTC_VALUE); 259 outb_p(0x38, S3_CRTC_ADDR); outb_p(0x00, S3_CRTC_VALUE); 260 261 return ENXIO; 262 } 263#endif 264 265 /* enable enhanced register access */ 266 267 outb_p(0x40, S3_CRTC_ADDR); 268 outb_p(inb_p(S3_CRTC_VALUE) | 1, S3_CRTC_VALUE); 269 270 /* enable enhanced functions */ 271 272 outb_enh(inb_enh(0) | 1, 0x0); 273 274 /* enable enhanced mode memory mapping */ 275 276 outb_p(0x31, S3_CRTC_ADDR); 277 outb_p(inb_p(S3_CRTC_VALUE) | 8, S3_CRTC_VALUE); 278 279 /* enable linear frame buffer and set address window to max */ 280 281 outb_p(0x58, S3_CRTC_ADDR); 282 outb_p(inb_p(S3_CRTC_VALUE) | 0x13, S3_CRTC_VALUE); 283 284 /* disabled enhanced register access */ 285 286 outb_p(0x40, S3_CRTC_ADDR); 287 outb_p(inb_p(S3_CRTC_VALUE) & ~1, S3_CRTC_VALUE); 288 289 /* lock S3 registers */ 290 291 outb_p(0x39, S3_CRTC_ADDR); outb_p(0x5a, S3_CRTC_VALUE); 292 outb_p(0x38, S3_CRTC_ADDR); outb_p(0x00, S3_CRTC_VALUE); 293 294 adp->va_info.vi_flags |= V_INFO_LINEAR; 295 adp->va_info.vi_buffer = sc->mem_base; 296 adp->va_buffer = s3lfb_map_buffer(adp->va_info.vi_buffer, 297 adp->va_info.vi_buffer_size); 298 adp->va_buffer_size = adp->va_info.vi_buffer_size; 299 adp->va_window = adp->va_buffer; 300 adp->va_window_size = adp->va_info.vi_buffer_size/adp->va_info.vi_planes; 301 adp->va_window_gran = adp->va_info.vi_buffer_size/adp->va_info.vi_planes; 302 303 return 0; 304} 305 306static int 307s3lfb_save_font(video_adapter_t *adp, int page, int fontsize, int fontwidth, 308 u_char *data, int ch, int count) 309{ 310 return (*prevvidsw->save_font)(adp, page, fontsize, fontwidth, data, 311 ch, count); 312} 313 314static int 315s3lfb_load_font(video_adapter_t *adp, int page, int fontsize, int fontwidth, 316 u_char *data, int ch, int count) 317{ 318 return (*prevvidsw->load_font)(adp, page, fontsize, fontwidth, data, 319 ch, count); 320} 321 322static int 323s3lfb_show_font(video_adapter_t *adp, int page) 324{ 325 return (*prevvidsw->show_font)(adp, page); 326} 327 328static int 329s3lfb_save_palette(video_adapter_t *adp, u_char *palette) 330{ 331 return (*prevvidsw->save_palette)(adp, palette); 332} 333 334static int 335s3lfb_load_palette(video_adapter_t *adp, u_char *palette) 336{ 337 return (*prevvidsw->load_palette)(adp, palette); 338} 339 340static int 341s3lfb_set_border(video_adapter_t *adp, int color) 342{ 343 return (*prevvidsw->set_border)(adp, color); 344} 345 346static int 347s3lfb_save_state(video_adapter_t *adp, void *p, size_t size) 348{ 349 return (*prevvidsw->save_state)(adp, p, size); 350} 351 352static int 353s3lfb_load_state(video_adapter_t *adp, void *p) 354{ 355 return (*prevvidsw->load_state)(adp, p); 356} 357 358static int 359s3lfb_set_origin(video_adapter_t *adp, off_t offset) 360{ 361 return (*prevvidsw->set_win_org)(adp, offset); 362} 363 364static int 365s3lfb_read_hw_cursor(video_adapter_t *adp, int *col, int *row) 366{ 367 return (*prevvidsw->read_hw_cursor)(adp, col, row); 368} 369 370static int 371s3lfb_set_hw_cursor(video_adapter_t *adp, int col, int row) 372{ 373 return (*prevvidsw->set_hw_cursor)(adp, col, row); 374} 375 376static int 377s3lfb_set_hw_cursor_shape(video_adapter_t *adp, int base, int height, 378 int celsize, int blink) 379{ 380 return (*prevvidsw->set_hw_cursor_shape)(adp, base, height, 381 celsize, blink); 382} 383 384static int 385s3lfb_blank_display(video_adapter_t *adp, int mode) 386{ 387 return (*prevvidsw->blank_display)(adp, mode); 388} 389 390static int 391s3lfb_mmap(video_adapter_t *adp, vm_ooffset_t offset, vm_paddr_t *paddr, 392 int prot, vm_memattr_t *memattr) 393{ 394 return (*prevvidsw->mmap)(adp, offset, paddr, prot, memattr); 395} 396 397static int 398s3lfb_clear(video_adapter_t *adp) 399{ 400 return (*prevvidsw->clear)(adp); 401} 402 403static int 404s3lfb_fill_rect(video_adapter_t *adp, int val, int x, int y, int cx, int cy) 405{ 406 return (*prevvidsw->fill_rect)(adp, val, x, y, cx, cy); 407} 408 409static int 410s3lfb_bitblt(video_adapter_t *adp,...) 411{ 412 return (*prevvidsw->bitblt)(adp); /* XXX */ 413} 414 415static int 416s3lfb_ioctl(video_adapter_t *adp, u_long cmd, caddr_t arg) 417{ 418 return (*prevvidsw->ioctl)(adp, cmd, arg); 419} 420 421static int 422s3lfb_diag(video_adapter_t *adp, int level) 423{ 424 return (*prevvidsw->diag)(adp, level); 425} 426 427static int 428s3lfb_error(void) 429{ 430 return 1; 431} 432 433/***********************************/ 434/* PCI detection/attachement stuff */ 435/***********************************/ 436 437static int 438s3pci_probe(device_t dev) 439{ 440 u_int32_t vendor, class, subclass, device_id; 441 442 device_id = pci_get_devid(dev); 443 vendor = device_id & 0xffff; 444 class = pci_get_class(dev); 445 subclass = pci_get_subclass(dev); 446 447 if ((class != PCIC_DISPLAY) || (subclass != PCIS_DISPLAY_VGA) || 448 (vendor != PCI_S3_VENDOR_ID)) 449 return ENXIO; 450 451 device_set_desc(dev, "S3 graphic card"); 452 453 bus_set_resource(dev, SYS_RES_IOPORT, 0, 454 S3_CONFIG_IO, S3_CONFIG_IO_SIZE); 455 bus_set_resource(dev, SYS_RES_IOPORT, 1, 456 S3_ENHANCED_IO, S3_ENHANCED_IO_SIZE); 457 458 return BUS_PROBE_DEFAULT; 459 460}; 461 462static int 463s3pci_attach(device_t dev) 464{ 465 struct s3pci_softc* sc = (struct s3pci_softc*)device_get_softc(dev); 466 video_adapter_t *adp; 467 468#if 0 469 unsigned char tmp; 470#endif 471 int rid, i; 472 473 if (s3pci_dev) { 474 printf("%s: driver already attached!\n", __func__); 475 goto error; 476 } 477 478 /* Allocate resources 479 */ 480 rid = 0; 481 if (!(sc->port_res = bus_alloc_resource_any(dev, SYS_RES_IOPORT, &rid, 482 RF_ACTIVE | RF_SHAREABLE))) { 483 printf("%s: port resource allocation failed!\n", __func__); 484 goto error; 485 } 486 sc->st = rman_get_bustag(sc->port_res); 487 sc->sh = rman_get_bushandle(sc->port_res); 488 489 rid = 1; 490 if (!(sc->enh_res = bus_alloc_resource_any(dev, SYS_RES_IOPORT, &rid, 491 RF_ACTIVE | RF_SHAREABLE))) { 492 printf("%s: enhanced port resource allocation failed!\n", 493 __func__); 494 goto error; 495 } 496 sc->enh_st = rman_get_bustag(sc->enh_res); 497 sc->enh_sh = rman_get_bushandle(sc->enh_res); 498 499 rid = PCI_BASE_MEMORY; 500 if (!(sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, 501 RF_ACTIVE))) { 502 503 printf("%s: mem resource allocation failed!\n", __func__); 504 goto error; 505 } 506 507 /* The memory base address will be our LFB base address 508 */ 509 /* sc->mem_base = (u_long)rman_get_virtual(sc->mem_res); */ 510 sc->mem_base = bus_get_resource_start(dev, SYS_RES_MEMORY, rid); 511 sc->mem_size = bus_get_resource_count(dev, SYS_RES_MEMORY, rid); 512 513 /* Attach the driver to the VGA/VESA framework 514 */ 515 for (i = 0; (adp = vid_get_adapter(i)) != NULL; ++i) { 516 if (adp->va_type == KD_VGA) 517 break; 518 } 519 520 /* If the VESA module hasn't been loaded, or VGA doesn't 521 * exist, abort 522 */ 523 if ((adp == NULL) || !(adp->va_flags & V_ADP_VESA)) { 524 printf("%s: VGA adapter not found or VESA module not loaded!\n", 525 __func__); 526 goto error; 527 } 528 529 /* Replace the VESA video switch by owers 530 */ 531 prevvidsw = vidsw[adp->va_index]; 532 vidsw[adp->va_index] = &s3lfbvidsw; 533 534 /* Remember who we are on the bus */ 535 s3pci_dev = (void *)dev; /* XXX */ 536 537 return 0; 538 539error: 540 if (sc->mem_res) 541 bus_release_resource(dev, SYS_RES_MEMORY, PCI_BASE_MEMORY, sc->mem_res); 542 543 if (sc->enh_res) 544 bus_release_resource(dev, SYS_RES_IOPORT, 1, sc->enh_res); 545 546 if (sc->port_res) 547 bus_release_resource(dev, SYS_RES_IOPORT, 0, sc->port_res); 548 549 return ENXIO; 550}; 551 552static device_method_t s3pci_methods[] = { 553 554 DEVMETHOD(device_probe, s3pci_probe), 555 DEVMETHOD(device_attach, s3pci_attach), 556 {0,0} 557}; 558 559static driver_t s3pci_driver = { 560 "s3pci", 561 s3pci_methods, 562 sizeof(struct s3pci_softc), 563}; 564 565DRIVER_MODULE(s3pci, pci, s3pci_driver, 0, 0); 566