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