1 /* $NetBSD: igmafb.c,v 1.5 2025/10/04 03:58:38 thorpej Exp $ */ 2 3 /* 4 * Copyright (c) 2012 Michael van Elst 5 * 6 * Permission to use, copy, modify, and distribute this software for any 7 * purpose with or without fee is hereby granted, provided that the above 8 * copyright notice and this permission notice appear in all copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 */ 18 19 /* 20 * Intel Graphic Media Accelerator 21 */ 22 23 #include <sys/cdefs.h> 24 __KERNEL_RCSID(0, "$NetBSD: igmafb.c,v 1.5 2025/10/04 03:58:38 thorpej Exp $"); 25 26 #include <sys/param.h> 27 #include <sys/systm.h> 28 #include <sys/device.h> 29 #include <sys/bus.h> 30 #include <sys/kmem.h> 31 32 #include <dev/pci/pcireg.h> 33 #include <dev/pci/pcivar.h> 34 #include <dev/pci/pcidevs.h> 35 #include <dev/pci/pciio.h> 36 37 #include <dev/videomode/videomode.h> 38 39 #include <dev/wscons/wsdisplayvar.h> 40 #include <dev/wscons/wsconsio.h> 41 #include <dev/wsfont/wsfont.h> 42 #include <dev/rasops/rasops.h> 43 #include <dev/wscons/wsdisplay_vconsvar.h> 44 #include <dev/pci/wsdisplay_pci.h> 45 46 #include <dev/pci/igmareg.h> 47 #include <dev/pci/igmavar.h> 48 49 #include "opt_voyagerfb.h" 50 51 struct igmafb_softc { 52 device_t sc_dev; 53 54 int sc_width; 55 int sc_height; 56 int sc_depth; 57 int sc_stride; 58 void *sc_fbaddr; 59 bus_size_t sc_fbsize; 60 struct vcons_screen sc_console_screen; 61 struct wsscreen_descr sc_defaultscreen_descr; 62 const struct wsscreen_descr *sc_screens[1]; 63 struct wsscreen_list sc_screenlist; 64 struct vcons_data vd; 65 66 struct igma_chip sc_chip; 67 void *sc_vga_save; 68 69 int sc_backlight; 70 int sc_brightness; 71 int sc_brightness_max; 72 }; 73 74 static int igmafb_match(device_t, cfdata_t, void *); 75 static void igmafb_attach(device_t, device_t, void *); 76 77 CFATTACH_DECL_NEW(igmafb, sizeof(struct igmafb_softc), 78 igmafb_match, igmafb_attach, NULL, NULL); 79 80 static int igmafb_ioctl(void *, void *, u_long, void *, int, struct lwp *); 81 static paddr_t igmafb_mmap(void *, void *, off_t, int); 82 static void igmafb_pollc(void *v, int); 83 84 static /*const*/ struct wsdisplay_accessops igmafb_accessops = { 85 igmafb_ioctl, 86 igmafb_mmap, 87 NULL, /* alloc_screen */ 88 NULL, /* free_screen */ 89 NULL, /* show_screen */ 90 NULL, /* load_font */ 91 igmafb_pollc, /* pollc */ 92 NULL /* scroll */ 93 }; 94 95 static void igmafb_init_screen(void *, struct vcons_screen *, int, long *); 96 static void igmafb_guess_size(struct igmafb_softc *, int *, int*); 97 static void igmafb_set_mode(struct igmafb_softc *, bool); 98 99 static void igmafb_planestart_quirk(struct igmafb_softc *); 100 static void igmafb_pfitdisable_quirk(struct igmafb_softc *); 101 102 static void igmafb_get_brightness_max(struct igmafb_softc *, int *); 103 static void igmafb_get_brightness(struct igmafb_softc *, int *); 104 static void igmafb_set_brightness(struct igmafb_softc *, int); 105 106 static int 107 igmafb_match(device_t parent, cfdata_t match, void *aux) 108 { 109 struct igma_attach_args *iaa = (struct igma_attach_args *)aux; 110 111 if (strcmp(iaa->iaa_name, "igmafb") == 0) return 100; 112 return 0; 113 } 114 115 static void 116 igmafb_attach(device_t parent, device_t self, void *aux) 117 { 118 struct igmafb_softc *sc = device_private(self); 119 struct igma_attach_args *iaa = (struct igma_attach_args *)aux; 120 struct rasops_info *ri; 121 bool is_console; 122 unsigned long defattr; 123 struct wsemuldisplaydev_attach_args waa; 124 125 sc->sc_dev = self; 126 127 aprint_normal("\n"); 128 129 is_console = iaa->iaa_console || 130 device_getprop_bool(self, "is_console"); 131 132 sc->sc_chip = iaa->iaa_chip; 133 134 sc->sc_fbaddr = bus_space_vaddr(sc->sc_chip.gmt, sc->sc_chip.gmh); 135 sc->sc_fbsize = 16 * 1024 * 1024; 136 137 igmafb_guess_size(sc, &sc->sc_width, &sc->sc_height); 138 sc->sc_depth = 32; 139 sc->sc_stride = (sc->sc_width*4 + 511)/512*512; 140 141 aprint_normal("%s: %d x %d, %d bit, stride %d\n", device_xname(self), 142 sc->sc_width, sc->sc_height, sc->sc_depth, sc->sc_stride); 143 144 aprint_normal("%s: %d MB video memory at %p\n", device_xname(self), 145 (int)sc->sc_fbsize >> 20, (void *)sc->sc_chip.gmb); 146 147 sc->sc_vga_save = kmem_alloc(256*1024, KM_SLEEP); 148 149 igmafb_get_brightness(sc, &sc->sc_brightness); 150 igmafb_get_brightness_max(sc, &sc->sc_brightness_max); 151 sc->sc_backlight = sc->sc_brightness != 0; 152 153 sc->sc_defaultscreen_descr = (struct wsscreen_descr){ 154 "default", 155 0, 0, 156 NULL, 157 8, 16, 158 WSSCREEN_WSCOLORS | WSSCREEN_HILIT, 159 NULL 160 }; 161 sc->sc_screens[0] = &sc->sc_defaultscreen_descr; 162 sc->sc_screenlist = (struct wsscreen_list){1, sc->sc_screens}; 163 164 vcons_init(&sc->vd, sc, &sc->sc_defaultscreen_descr, 165 &igmafb_accessops); 166 sc->vd.init_screen = igmafb_init_screen; 167 168 /* enable hardware display */ 169 igmafb_set_mode(sc, true); 170 171 ri = &sc->sc_console_screen.scr_ri; 172 173 if (is_console) { 174 vcons_init_screen(&sc->vd, &sc->sc_console_screen, 1, 175 &defattr); 176 177 sc->sc_console_screen.scr_flags |= VCONS_SCREEN_IS_STATIC 178 | VCONS_NO_COPYROWS | VCONS_NO_COPYCOLS; 179 vcons_redraw_screen(&sc->sc_console_screen); 180 181 sc->sc_defaultscreen_descr.textops = &ri->ri_ops; 182 sc->sc_defaultscreen_descr.capabilities = ri->ri_caps; 183 sc->sc_defaultscreen_descr.nrows = ri->ri_rows; 184 sc->sc_defaultscreen_descr.ncols = ri->ri_cols; 185 186 wsdisplay_cnattach(&sc->sc_defaultscreen_descr, ri, 0, 0, 187 defattr); 188 vcons_replay_msgbuf(&sc->sc_console_screen); 189 } else { 190 if (sc->sc_console_screen.scr_ri.ri_rows == 0) { 191 /* do some minimal setup to avoid weirdness later */ 192 vcons_init_screen(&sc->vd, &sc->sc_console_screen, 1, 193 &defattr); 194 } else 195 (*ri->ri_ops.allocattr)(ri, 0, 0, 0, &defattr); 196 } 197 198 waa.console = is_console; 199 waa.scrdata = &sc->sc_screenlist; 200 waa.accessops = &igmafb_accessops; 201 waa.accesscookie = &sc->vd; 202 203 config_found(sc->sc_dev, &waa, wsemuldisplaydevprint, CFARGS_NONE); 204 } 205 206 /* 207 * wsdisplay accessops 208 */ 209 210 static int 211 igmafb_ioctl(void *v, void *vs, u_long cmd, void *data, int flags, 212 struct lwp *l) 213 { 214 struct vcons_data *vd = v; 215 struct igmafb_softc *sc = vd->cookie; 216 struct wsdisplay_fbinfo *wdf; 217 struct vcons_screen *ms = vd->active; 218 struct wsdisplayio_fbinfo *fbi; 219 struct wsdisplay_param *param; 220 int val; 221 222 switch (cmd) { 223 case WSDISPLAYIO_GTYPE: 224 *(u_int *)data = WSDISPLAY_TYPE_PCIMISC; 225 return 0; 226 case WSDISPLAYIO_GINFO: 227 if (ms == NULL) 228 return ENODEV; 229 wdf = data; 230 wdf->width = ms->scr_ri.ri_width; 231 wdf->height = ms->scr_ri.ri_height; 232 wdf->depth = ms->scr_ri.ri_depth; 233 wdf->cmsize = 256; /* XXX */ 234 return 0; 235 case WSDISPLAYIO_LINEBYTES: 236 if (ms == NULL) 237 return ENODEV; 238 *(u_int *)data = ms->scr_ri.ri_stride; 239 return 0; 240 case WSDISPLAYIO_GET_FBINFO: 241 fbi = data; 242 return wsdisplayio_get_fbinfo(&ms->scr_ri, fbi); 243 case WSDISPLAYIO_SVIDEO: 244 val = (*(u_int *)data) != WSDISPLAYIO_VIDEO_OFF; 245 sc->sc_backlight = val; 246 if (val) 247 igmafb_set_brightness(sc, sc->sc_brightness); 248 else 249 igmafb_set_brightness(sc, 0); 250 return 0; 251 case WSDISPLAYIO_GETPARAM: 252 param = (struct wsdisplay_param *)data; 253 switch (param->param) { 254 case WSDISPLAYIO_PARAM_BRIGHTNESS: 255 param->min = 0; 256 param->max = 255; 257 if (sc->sc_backlight) 258 igmafb_get_brightness(sc, &val); 259 else 260 val = sc->sc_brightness; 261 val = val * 255 / sc->sc_brightness_max; 262 param->curval = val; 263 return 0; 264 case WSDISPLAYIO_PARAM_BACKLIGHT: 265 param->min = 0; 266 param->max = 1; 267 param->curval = sc->sc_backlight; 268 return 0; 269 } 270 return EPASSTHROUGH; 271 case WSDISPLAYIO_SETPARAM: 272 param = (struct wsdisplay_param *)data; 273 switch (param->param) { 274 case WSDISPLAYIO_PARAM_BRIGHTNESS: 275 val = param->curval; 276 if (val < 0) 277 val = 0; 278 if (val > 255) 279 val = 255; 280 val = val * sc->sc_brightness_max / 255; 281 sc->sc_brightness = val; 282 if (sc->sc_backlight) 283 igmafb_set_brightness(sc, val); 284 return 0; 285 case WSDISPLAYIO_PARAM_BACKLIGHT: 286 val = param->curval; 287 sc->sc_backlight = val; 288 if (val) 289 igmafb_set_brightness(sc, sc->sc_brightness); 290 else 291 igmafb_set_brightness(sc, 0); 292 return 0; 293 } 294 return EPASSTHROUGH; 295 } 296 297 return EPASSTHROUGH; 298 } 299 300 static paddr_t 301 igmafb_mmap(void *v, void *vs, off_t offset, int prot) 302 { 303 struct vcons_data *vd = v; 304 struct igmafb_softc *sc = vd->cookie; 305 306 if ((offset & PAGE_MASK) != 0) 307 return -1; 308 309 if (offset < 0 || offset >= sc->sc_fbsize) 310 return -1; 311 312 return bus_space_mmap(sc->sc_chip.gmt, sc->sc_chip.gmb, offset, prot, 313 BUS_SPACE_MAP_LINEAR); 314 } 315 316 static void 317 igmafb_pollc(void *v, int on) 318 { 319 struct vcons_data *vd = v; 320 struct igmafb_softc *sc = vd->cookie; 321 322 if (sc == NULL) 323 return; 324 if (sc->sc_console_screen.scr_vd == NULL) 325 return; 326 327 if (on) 328 vcons_enable_polling(&sc->vd); 329 else 330 vcons_disable_polling(&sc->vd); 331 } 332 333 static void 334 igmafb_init_screen(void *cookie, struct vcons_screen *scr, 335 int existing, long *defattr) 336 { 337 struct igmafb_softc *sc = cookie; 338 struct rasops_info *ri = &scr->scr_ri; 339 340 memset(ri, 0, sizeof(struct rasops_info)); 341 342 ri->ri_depth = sc->sc_depth; 343 ri->ri_width = sc->sc_width; 344 ri->ri_height = sc->sc_height; 345 ri->ri_stride = sc->sc_stride; 346 ri->ri_flg = RI_CENTER | RI_FULLCLEAR; 347 348 ri->ri_bits = (char *)sc->sc_fbaddr; 349 350 if (existing) { 351 ri->ri_flg |= RI_CLEAR; 352 } 353 354 switch (sc->sc_depth) { 355 case 32: 356 ri->ri_rnum = 8; 357 ri->ri_gnum = 8; 358 ri->ri_bnum = 8; 359 ri->ri_rpos = 16; 360 ri->ri_gpos = 8; 361 ri->ri_bpos = 0; 362 break; 363 } 364 365 rasops_init(ri, 0, 0); 366 ri->ri_caps = WSSCREEN_WSCOLORS; 367 368 rasops_reconfig(ri, sc->sc_height / ri->ri_font->fontheight, 369 sc->sc_width / ri->ri_font->fontwidth); 370 371 ri->ri_hw = scr; 372 } 373 374 static void 375 igmafb_guess_size(struct igmafb_softc *sc, int *widthp, int *heightp) 376 { 377 const struct igma_chip *cd = &sc->sc_chip; 378 const struct igma_chip_ops *co = cd->ops; 379 int pipe = cd->use_pipe; 380 u_int32_t r; 381 382 r = co->read_reg(cd, PIPE_HTOTAL(pipe)); 383 *widthp = PIPE_HTOTAL_GET_ACTIVE(r); 384 r = co->read_reg(cd, PIPE_VTOTAL(pipe)); 385 *heightp = PIPE_VTOTAL_GET_ACTIVE(r); 386 387 aprint_normal("%s: vga active size %d x %d\n", 388 device_xname(sc->sc_dev), 389 *widthp, *heightp); 390 391 if (*widthp < 640 || *heightp < 400) { 392 r = co->read_reg(cd, PF_WINSZ(pipe)); 393 *widthp = PF_WINSZ_GET_WIDTH(r); 394 *heightp = PF_WINSZ_GET_HEIGHT(r); 395 396 aprint_normal("%s: window size %d x %d\n", 397 device_xname(sc->sc_dev), 398 *widthp, *heightp); 399 } 400 401 if (*widthp < 640) *widthp = 640; 402 if (*heightp < 400) *heightp = 400; 403 } 404 405 static void 406 igmafb_set_mode(struct igmafb_softc *sc, bool enable) 407 { 408 const struct igma_chip *cd = &sc->sc_chip; 409 const struct igma_chip_ops *co = cd->ops; 410 int pipe = cd->use_pipe; 411 u_int32_t r; 412 u_int8_t b; 413 int i; 414 415 if (enable) { 416 /* disable VGA machinery */ 417 b = co->read_vga(cd, 0x01); 418 co->write_vga(cd, 0x01, b | 0x20); 419 420 /* disable VGA compatible display */ 421 r = co->read_reg(cd, sc->sc_chip.vga_cntrl); 422 co->write_reg(cd, sc->sc_chip.vga_cntrl, r | VGA_CNTRL_DISABLE); 423 424 /* save VGA memory */ 425 memcpy(sc->sc_vga_save, sc->sc_fbaddr, 256*1024); 426 427 /* configure panel fitter */ 428 co->write_reg(cd, PF_WINPOS(pipe), 429 PF_WINPOS_VAL(0, 0)); 430 co->write_reg(cd, PF_WINSZ(pipe), 431 PF_WINSZ_VAL(sc->sc_width, sc->sc_height)); 432 433 /* pipe size */ 434 co->write_reg(cd, PIPE_SRCSZ(pipe), 435 PIPE_SRCSZ_VAL(sc->sc_width, sc->sc_height)); 436 437 /* enable pipe */ 438 co->write_reg(cd, PIPE_CONF(pipe), 439 PIPE_CONF_ENABLE | PIPE_CONF_8BPP); 440 441 /* configure planes */ 442 r = co->read_reg(cd, PRI_CTRL(pipe)); 443 r &= ~(PRI_CTRL_PIXFMTMSK | PRI_CTRL_TILED); 444 r |= PRI_CTRL_ENABLE | PRI_CTRL_BGR; 445 co->write_reg(cd, PRI_CTRL(pipe), r | cd->pri_cntrl); 446 co->write_reg(cd, PRI_LINOFF(pipe), 0); 447 co->write_reg(cd, PRI_STRIDE(pipe), sc->sc_stride); 448 co->write_reg(cd, PRI_SURF(pipe), 0); 449 co->write_reg(cd, PRI_TILEOFF(pipe), 0); 450 451 if (cd->quirks & IGMA_PLANESTART_QUIRK) 452 igmafb_planestart_quirk(sc); 453 454 if (cd->quirks & IGMA_PFITDISABLE_QUIRK) 455 igmafb_pfitdisable_quirk(sc); 456 } else { 457 /* disable planes */ 458 co->write_reg(cd, PRI_CTRL(pipe), 0 | cd->pri_cntrl); 459 co->write_reg(cd, PRI_LINOFF(pipe), 0); 460 co->write_reg(cd, PRI_STRIDE(pipe), 2560); 461 co->write_reg(cd, PRI_SURF(pipe), 0); 462 co->write_reg(cd, PRI_TILEOFF(pipe), 0); 463 464 /* pipe size */ 465 co->write_reg(cd, PIPE_SRCSZ(pipe), 466 PIPE_SRCSZ_VAL(720,400)); 467 468 /* disable pipe */ 469 co->write_reg(cd, PIPE_CONF(pipe), 0); 470 for (i=0; i<10; ++i) { 471 delay(10); 472 if ((co->read_reg(cd, PIPE_CONF(pipe)) & PIPE_CONF_STATE) == 0) 473 break; 474 } 475 476 /* workaround before enabling VGA */ 477 r = co->read_reg(cd, 0x42000); 478 co->write_reg(cd, 0x42000, (r & 0x1fffffff) | 0xa0000000); 479 r = co->read_reg(cd, 0x42004); 480 co->write_reg(cd, 0x42004, (r & 0xfbffffff) | 0x00000000); 481 482 /* configure panel fitter */ 483 co->write_reg(cd, PF_WINPOS(pipe), 484 PF_WINPOS_VAL(0, 0)); 485 co->write_reg(cd, PF_WINSZ(pipe), 486 PF_WINSZ_VAL(sc->sc_width, sc->sc_height)); 487 488 /* enable VGA compatible display */ 489 r = co->read_reg(cd, sc->sc_chip.vga_cntrl); 490 co->write_reg(cd, sc->sc_chip.vga_cntrl, r & ~VGA_CNTRL_DISABLE); 491 492 /* enable VGA machinery */ 493 b = co->read_vga(cd, 0x01); 494 co->write_vga(cd, 0x01, b & ~0x20); 495 496 /* restore VGA memory */ 497 memcpy(sc->sc_fbaddr, sc->sc_vga_save, 256*1024); 498 499 /* enable pipe again */ 500 co->write_reg(cd, PIPE_CONF(pipe), 501 PIPE_CONF_ENABLE | PIPE_CONF_6BPP | PIPE_CONF_DITHER); 502 } 503 } 504 505 static void 506 igmafb_planestart_quirk(struct igmafb_softc *sc) 507 { 508 const struct igma_chip *cd = &sc->sc_chip; 509 const struct igma_chip_ops *co = cd->ops; 510 int pipe = cd->use_pipe; 511 u_int32_t cntrl, fwbcl; 512 513 /* disable self refresh */ 514 fwbcl = co->read_reg(cd, FW_BLC_SELF); 515 co->write_reg(cd, FW_BLC_SELF, fwbcl & ~FW_BLC_SELF_EN); 516 517 cntrl = co->read_reg(cd, CUR_CNTR(pipe)); 518 co->write_reg(cd, CUR_CNTR(pipe), 1<<5 | 0x07); 519 520 /* "wait for vblank" */ 521 delay(40000); 522 523 co->write_reg(cd, CUR_CNTR(pipe), cntrl); 524 co->write_reg(cd, CUR_BASE(pipe), 525 co->read_reg(cd, CUR_BASE(pipe))); 526 527 co->write_reg(cd, FW_BLC_SELF, fwbcl); 528 } 529 530 static void 531 igmafb_pfitdisable_quirk(struct igmafb_softc *sc) 532 { 533 const struct igma_chip *cd = &sc->sc_chip; 534 const struct igma_chip_ops *co = cd->ops; 535 u_int32_t r; 536 537 /* disable i965 panel fitter */ 538 r = co->read_reg(cd, PF_CTRL_I965); 539 co->write_reg(cd, PF_CTRL_I965, r & ~PF_ENABLE); 540 } 541 542 static void 543 igmafb_get_brightness_max(struct igmafb_softc *sc, int *valp) 544 { 545 const struct igma_chip *cd = &sc->sc_chip; 546 const struct igma_chip_ops *co = cd->ops; 547 u_int32_t r, f; 548 549 r = co->read_reg(cd, cd->backlight_cntrl); 550 f = BACKLIGHT_GET_FREQ(r); 551 if (f == 0) { 552 r = co->read_reg(cd, RAWCLK_FREQ); 553 f = r * 1000000 / (200 * 128); 554 if (f == 0 || f > 32767) 555 f = 125 * 100000 / (200 * 128); 556 } 557 558 *valp = f; 559 } 560 561 static void 562 igmafb_get_brightness(struct igmafb_softc *sc, int *valp) 563 { 564 const struct igma_chip *cd = &sc->sc_chip; 565 const struct igma_chip_ops *co = cd->ops; 566 u_int32_t r, v; 567 568 r = co->read_reg(cd, cd->backlight_cntrl); 569 v = BACKLIGHT_GET_CYCLE(r); 570 *valp = v; 571 } 572 573 static void 574 igmafb_set_brightness(struct igmafb_softc *sc, int val) 575 { 576 const struct igma_chip *cd = &sc->sc_chip; 577 const struct igma_chip_ops *co = cd->ops; 578 u_int32_t r, f, l; 579 580 r = co->read_reg(cd, cd->backlight_cntrl); 581 f = BACKLIGHT_GET_FREQ(r); 582 l = BACKLIGHT_GET_LEGACY(r); 583 584 co->write_reg(cd, cd->backlight_cntrl, 585 BACKLIGHT_VAL(f,l,val)); 586 } 587 588