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