1 /* $NetBSD: igmafb.c,v 1.2 2020/02/24 12:20:30 rin 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.2 2020/02/24 12:20:30 rin 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 prop_dictionary_t dict; 122 bool is_console; 123 unsigned long defattr; 124 struct wsemuldisplaydev_attach_args waa; 125 126 sc->sc_dev = self; 127 128 aprint_normal("\n"); 129 130 dict = device_properties(self); 131 prop_dictionary_get_bool(dict, "is_console", &is_console); 132 if (iaa->iaa_console) 133 is_console = true; 134 135 sc->sc_chip = iaa->iaa_chip; 136 137 sc->sc_fbaddr = bus_space_vaddr(sc->sc_chip.gmt, sc->sc_chip.gmh); 138 sc->sc_fbsize = 16 * 1024 * 1024; 139 140 igmafb_guess_size(sc, &sc->sc_width, &sc->sc_height); 141 sc->sc_depth = 32; 142 sc->sc_stride = (sc->sc_width*4 + 511)/512*512; 143 144 aprint_normal("%s: %d x %d, %d bit, stride %d\n", device_xname(self), 145 sc->sc_width, sc->sc_height, sc->sc_depth, sc->sc_stride); 146 147 aprint_normal("%s: %d MB video memory at %p\n", device_xname(self), 148 (int)sc->sc_fbsize >> 20, (void *)sc->sc_chip.gmb); 149 150 sc->sc_vga_save = kmem_alloc(256*1024, KM_SLEEP); 151 152 igmafb_get_brightness(sc, &sc->sc_brightness); 153 igmafb_get_brightness_max(sc, &sc->sc_brightness_max); 154 sc->sc_backlight = sc->sc_brightness != 0; 155 156 sc->sc_defaultscreen_descr = (struct wsscreen_descr){ 157 "default", 158 0, 0, 159 NULL, 160 8, 16, 161 WSSCREEN_WSCOLORS | WSSCREEN_HILIT, 162 NULL 163 }; 164 sc->sc_screens[0] = &sc->sc_defaultscreen_descr; 165 sc->sc_screenlist = (struct wsscreen_list){1, sc->sc_screens}; 166 167 vcons_init(&sc->vd, sc, &sc->sc_defaultscreen_descr, 168 &igmafb_accessops); 169 sc->vd.init_screen = igmafb_init_screen; 170 171 /* enable hardware display */ 172 igmafb_set_mode(sc, true); 173 174 ri = &sc->sc_console_screen.scr_ri; 175 176 if (is_console) { 177 vcons_init_screen(&sc->vd, &sc->sc_console_screen, 1, 178 &defattr); 179 180 sc->sc_console_screen.scr_flags |= VCONS_SCREEN_IS_STATIC 181 | VCONS_NO_COPYROWS | VCONS_NO_COPYCOLS; 182 vcons_redraw_screen(&sc->sc_console_screen); 183 184 sc->sc_defaultscreen_descr.textops = &ri->ri_ops; 185 sc->sc_defaultscreen_descr.capabilities = ri->ri_caps; 186 sc->sc_defaultscreen_descr.nrows = ri->ri_rows; 187 sc->sc_defaultscreen_descr.ncols = ri->ri_cols; 188 189 wsdisplay_cnattach(&sc->sc_defaultscreen_descr, ri, 0, 0, 190 defattr); 191 vcons_replay_msgbuf(&sc->sc_console_screen); 192 } else { 193 if (sc->sc_console_screen.scr_ri.ri_rows == 0) { 194 /* do some minimal setup to avoid weirdness later */ 195 vcons_init_screen(&sc->vd, &sc->sc_console_screen, 1, 196 &defattr); 197 } else 198 (*ri->ri_ops.allocattr)(ri, 0, 0, 0, &defattr); 199 } 200 201 waa.console = is_console; 202 waa.scrdata = &sc->sc_screenlist; 203 waa.accessops = &igmafb_accessops; 204 waa.accesscookie = &sc->vd; 205 206 config_found(sc->sc_dev, &waa, wsemuldisplaydevprint); 207 } 208 209 /* 210 * wsdisplay accessops 211 */ 212 213 static int 214 igmafb_ioctl(void *v, void *vs, u_long cmd, void *data, int flags, 215 struct lwp *l) 216 { 217 struct vcons_data *vd = v; 218 struct igmafb_softc *sc = vd->cookie; 219 struct wsdisplay_fbinfo *wdf; 220 struct vcons_screen *ms = vd->active; 221 struct wsdisplayio_fbinfo *fbi; 222 struct wsdisplay_param *param; 223 int val; 224 225 switch (cmd) { 226 case WSDISPLAYIO_GTYPE: 227 *(u_int *)data = WSDISPLAY_TYPE_PCIMISC; 228 return 0; 229 case WSDISPLAYIO_GINFO: 230 if (ms == NULL) 231 return ENODEV; 232 wdf = data; 233 wdf->width = ms->scr_ri.ri_width; 234 wdf->height = ms->scr_ri.ri_height; 235 wdf->depth = ms->scr_ri.ri_depth; 236 wdf->cmsize = 256; /* XXX */ 237 return 0; 238 case WSDISPLAYIO_LINEBYTES: 239 if (ms == NULL) 240 return ENODEV; 241 *(u_int *)data = ms->scr_ri.ri_stride; 242 return 0; 243 case WSDISPLAYIO_GET_FBINFO: 244 fbi = data; 245 return wsdisplayio_get_fbinfo(&ms->scr_ri, fbi); 246 case WSDISPLAYIO_SVIDEO: 247 val = (*(u_int *)data) != WSDISPLAYIO_VIDEO_OFF; 248 sc->sc_backlight = val; 249 if (val) 250 igmafb_set_brightness(sc, sc->sc_brightness); 251 else 252 igmafb_set_brightness(sc, 0); 253 return 0; 254 case WSDISPLAYIO_GETPARAM: 255 param = (struct wsdisplay_param *)data; 256 switch (param->param) { 257 case WSDISPLAYIO_PARAM_BRIGHTNESS: 258 param->min = 0; 259 param->max = 255; 260 if (sc->sc_backlight) 261 igmafb_get_brightness(sc, &val); 262 else 263 val = sc->sc_brightness; 264 val = val * 255 / sc->sc_brightness_max; 265 param->curval = val; 266 return 0; 267 case WSDISPLAYIO_PARAM_BACKLIGHT: 268 param->min = 0; 269 param->max = 1; 270 param->curval = sc->sc_backlight; 271 return 0; 272 } 273 return EPASSTHROUGH; 274 case WSDISPLAYIO_SETPARAM: 275 param = (struct wsdisplay_param *)data; 276 switch (param->param) { 277 case WSDISPLAYIO_PARAM_BRIGHTNESS: 278 val = param->curval; 279 if (val < 0) 280 val = 0; 281 if (val > 255) 282 val = 255; 283 val = val * sc->sc_brightness_max / 255; 284 sc->sc_brightness = val; 285 if (sc->sc_backlight) 286 igmafb_set_brightness(sc, val); 287 return 0; 288 case WSDISPLAYIO_PARAM_BACKLIGHT: 289 val = param->curval; 290 sc->sc_backlight = val; 291 if (val) 292 igmafb_set_brightness(sc, sc->sc_brightness); 293 else 294 igmafb_set_brightness(sc, 0); 295 return 0; 296 } 297 return EPASSTHROUGH; 298 } 299 300 return EPASSTHROUGH; 301 } 302 303 static paddr_t 304 igmafb_mmap(void *v, void *vs, off_t offset, int prot) 305 { 306 struct vcons_data *vd = v; 307 struct igmafb_softc *sc = vd->cookie; 308 309 if ((offset & PAGE_MASK) != 0) 310 return -1; 311 312 if (offset < 0 || offset >= sc->sc_fbsize) 313 return -1; 314 315 return bus_space_mmap(sc->sc_chip.gmt, sc->sc_chip.gmb, offset, prot, 316 BUS_SPACE_MAP_LINEAR); 317 } 318 319 static void 320 igmafb_pollc(void *v, int on) 321 { 322 struct vcons_data *vd = v; 323 struct igmafb_softc *sc = vd->cookie; 324 325 if (sc == NULL) 326 return; 327 if (sc->sc_console_screen.scr_vd == NULL) 328 return; 329 330 if (on) 331 vcons_enable_polling(&sc->vd); 332 else 333 vcons_disable_polling(&sc->vd); 334 } 335 336 static void 337 igmafb_init_screen(void *cookie, struct vcons_screen *scr, 338 int existing, long *defattr) 339 { 340 struct igmafb_softc *sc = cookie; 341 struct rasops_info *ri = &scr->scr_ri; 342 343 memset(ri, 0, sizeof(struct rasops_info)); 344 345 ri->ri_depth = sc->sc_depth; 346 ri->ri_width = sc->sc_width; 347 ri->ri_height = sc->sc_height; 348 ri->ri_stride = sc->sc_stride; 349 ri->ri_flg = RI_CENTER | RI_FULLCLEAR; 350 351 ri->ri_bits = (char *)sc->sc_fbaddr; 352 353 if (existing) { 354 ri->ri_flg |= RI_CLEAR; 355 } 356 357 switch (sc->sc_depth) { 358 case 32: 359 ri->ri_rnum = 8; 360 ri->ri_gnum = 8; 361 ri->ri_bnum = 8; 362 ri->ri_rpos = 16; 363 ri->ri_gpos = 8; 364 ri->ri_bpos = 0; 365 break; 366 } 367 368 rasops_init(ri, 0, 0); 369 ri->ri_caps = WSSCREEN_WSCOLORS; 370 371 rasops_reconfig(ri, sc->sc_height / ri->ri_font->fontheight, 372 sc->sc_width / ri->ri_font->fontwidth); 373 374 ri->ri_hw = scr; 375 } 376 377 static void 378 igmafb_guess_size(struct igmafb_softc *sc, int *widthp, int *heightp) 379 { 380 const struct igma_chip *cd = &sc->sc_chip; 381 const struct igma_chip_ops *co = cd->ops; 382 int pipe = cd->use_pipe; 383 u_int32_t r; 384 385 r = co->read_reg(cd, PIPE_HTOTAL(pipe)); 386 *widthp = PIPE_HTOTAL_GET_ACTIVE(r); 387 r = co->read_reg(cd, PIPE_VTOTAL(pipe)); 388 *heightp = PIPE_VTOTAL_GET_ACTIVE(r); 389 390 aprint_normal("%s: vga active size %d x %d\n", 391 device_xname(sc->sc_dev), 392 *widthp, *heightp); 393 394 if (*widthp < 640 || *heightp < 400) { 395 r = co->read_reg(cd, PF_WINSZ(pipe)); 396 *widthp = PF_WINSZ_GET_WIDTH(r); 397 *heightp = PF_WINSZ_GET_HEIGHT(r); 398 399 aprint_normal("%s: window size %d x %d\n", 400 device_xname(sc->sc_dev), 401 *widthp, *heightp); 402 } 403 404 if (*widthp < 640) *widthp = 640; 405 if (*heightp < 400) *heightp = 400; 406 } 407 408 static void 409 igmafb_set_mode(struct igmafb_softc *sc, bool enable) 410 { 411 const struct igma_chip *cd = &sc->sc_chip; 412 const struct igma_chip_ops *co = cd->ops; 413 int pipe = cd->use_pipe; 414 u_int32_t r; 415 u_int8_t b; 416 int i; 417 418 if (enable) { 419 /* disable VGA machinery */ 420 b = co->read_vga(cd, 0x01); 421 co->write_vga(cd, 0x01, b | 0x20); 422 423 /* disable VGA compatible display */ 424 r = co->read_reg(cd, sc->sc_chip.vga_cntrl); 425 co->write_reg(cd, sc->sc_chip.vga_cntrl, r | VGA_CNTRL_DISABLE); 426 427 /* save VGA memory */ 428 memcpy(sc->sc_vga_save, sc->sc_fbaddr, 256*1024); 429 430 /* configure panel fitter */ 431 co->write_reg(cd, PF_WINPOS(pipe), 432 PF_WINPOS_VAL(0, 0)); 433 co->write_reg(cd, PF_WINSZ(pipe), 434 PF_WINSZ_VAL(sc->sc_width, sc->sc_height)); 435 436 /* pipe size */ 437 co->write_reg(cd, PIPE_SRCSZ(pipe), 438 PIPE_SRCSZ_VAL(sc->sc_width, sc->sc_height)); 439 440 /* enable pipe */ 441 co->write_reg(cd, PIPE_CONF(pipe), 442 PIPE_CONF_ENABLE | PIPE_CONF_8BPP); 443 444 /* configure planes */ 445 r = co->read_reg(cd, PRI_CTRL(pipe)); 446 r &= ~(PRI_CTRL_PIXFMTMSK | PRI_CTRL_TILED); 447 r |= PRI_CTRL_ENABLE | PRI_CTRL_BGR; 448 co->write_reg(cd, PRI_CTRL(pipe), r | cd->pri_cntrl); 449 co->write_reg(cd, PRI_LINOFF(pipe), 0); 450 co->write_reg(cd, PRI_STRIDE(pipe), sc->sc_stride); 451 co->write_reg(cd, PRI_SURF(pipe), 0); 452 co->write_reg(cd, PRI_TILEOFF(pipe), 0); 453 454 if (cd->quirks & IGMA_PLANESTART_QUIRK) 455 igmafb_planestart_quirk(sc); 456 457 if (cd->quirks & IGMA_PFITDISABLE_QUIRK) 458 igmafb_pfitdisable_quirk(sc); 459 } else { 460 /* disable planes */ 461 co->write_reg(cd, PRI_CTRL(pipe), 0 | cd->pri_cntrl); 462 co->write_reg(cd, PRI_LINOFF(pipe), 0); 463 co->write_reg(cd, PRI_STRIDE(pipe), 2560); 464 co->write_reg(cd, PRI_SURF(pipe), 0); 465 co->write_reg(cd, PRI_TILEOFF(pipe), 0); 466 467 /* pipe size */ 468 co->write_reg(cd, PIPE_SRCSZ(pipe), 469 PIPE_SRCSZ_VAL(720,400)); 470 471 /* disable pipe */ 472 co->write_reg(cd, PIPE_CONF(pipe), 0); 473 for (i=0; i<10; ++i) { 474 delay(10); 475 if ((co->read_reg(cd, PIPE_CONF(pipe)) & PIPE_CONF_STATE) == 0) 476 break; 477 } 478 479 /* workaround before enabling VGA */ 480 r = co->read_reg(cd, 0x42000); 481 co->write_reg(cd, 0x42000, (r & 0x1fffffff) | 0xa0000000); 482 r = co->read_reg(cd, 0x42004); 483 co->write_reg(cd, 0x42004, (r & 0xfbffffff) | 0x00000000); 484 485 /* configure panel fitter */ 486 co->write_reg(cd, PF_WINPOS(pipe), 487 PF_WINPOS_VAL(0, 0)); 488 co->write_reg(cd, PF_WINSZ(pipe), 489 PF_WINSZ_VAL(sc->sc_width, sc->sc_height)); 490 491 /* enable VGA compatible display */ 492 r = co->read_reg(cd, sc->sc_chip.vga_cntrl); 493 co->write_reg(cd, sc->sc_chip.vga_cntrl, r & ~VGA_CNTRL_DISABLE); 494 495 /* enable VGA machinery */ 496 b = co->read_vga(cd, 0x01); 497 co->write_vga(cd, 0x01, b & ~0x20); 498 499 /* restore VGA memory */ 500 memcpy(sc->sc_fbaddr, sc->sc_vga_save, 256*1024); 501 502 /* enable pipe again */ 503 co->write_reg(cd, PIPE_CONF(pipe), 504 PIPE_CONF_ENABLE | PIPE_CONF_6BPP | PIPE_CONF_DITHER); 505 } 506 } 507 508 static void 509 igmafb_planestart_quirk(struct igmafb_softc *sc) 510 { 511 const struct igma_chip *cd = &sc->sc_chip; 512 const struct igma_chip_ops *co = cd->ops; 513 int pipe = cd->use_pipe; 514 u_int32_t cntrl, fwbcl; 515 516 /* disable self refresh */ 517 fwbcl = co->read_reg(cd, FW_BLC_SELF); 518 co->write_reg(cd, FW_BLC_SELF, fwbcl & ~FW_BLC_SELF_EN); 519 520 cntrl = co->read_reg(cd, CUR_CNTR(pipe)); 521 co->write_reg(cd, CUR_CNTR(pipe), 1<<5 | 0x07); 522 523 /* "wait for vblank" */ 524 delay(40000); 525 526 co->write_reg(cd, CUR_CNTR(pipe), cntrl); 527 co->write_reg(cd, CUR_BASE(pipe), 528 co->read_reg(cd, CUR_BASE(pipe))); 529 530 co->write_reg(cd, FW_BLC_SELF, fwbcl); 531 } 532 533 static void 534 igmafb_pfitdisable_quirk(struct igmafb_softc *sc) 535 { 536 const struct igma_chip *cd = &sc->sc_chip; 537 const struct igma_chip_ops *co = cd->ops; 538 u_int32_t r; 539 540 /* disable i965 panel fitter */ 541 r = co->read_reg(cd, PF_CTRL_I965); 542 co->write_reg(cd, PF_CTRL_I965, r & ~PF_ENABLE); 543 } 544 545 static void 546 igmafb_get_brightness_max(struct igmafb_softc *sc, int *valp) 547 { 548 const struct igma_chip *cd = &sc->sc_chip; 549 const struct igma_chip_ops *co = cd->ops; 550 u_int32_t r, f; 551 552 r = co->read_reg(cd, cd->backlight_cntrl); 553 f = BACKLIGHT_GET_FREQ(r); 554 if (f == 0) { 555 r = co->read_reg(cd, RAWCLK_FREQ); 556 f = r * 1000000 / (200 * 128); 557 if (f == 0 || f > 32767) 558 f = 125 * 100000 / (200 * 128); 559 } 560 561 *valp = f; 562 } 563 564 static void 565 igmafb_get_brightness(struct igmafb_softc *sc, int *valp) 566 { 567 const struct igma_chip *cd = &sc->sc_chip; 568 const struct igma_chip_ops *co = cd->ops; 569 u_int32_t r, v; 570 571 r = co->read_reg(cd, cd->backlight_cntrl); 572 v = BACKLIGHT_GET_CYCLE(r); 573 *valp = v; 574 } 575 576 static void 577 igmafb_set_brightness(struct igmafb_softc *sc, int val) 578 { 579 const struct igma_chip *cd = &sc->sc_chip; 580 const struct igma_chip_ops *co = cd->ops; 581 u_int32_t r, f, l; 582 583 r = co->read_reg(cd, cd->backlight_cntrl); 584 f = BACKLIGHT_GET_FREQ(r); 585 l = BACKLIGHT_GET_LEGACY(r); 586 587 co->write_reg(cd, cd->backlight_cntrl, 588 BACKLIGHT_VAL(f,l,val)); 589 } 590 591