1 /* 2 * This file and its contents are supplied under the terms of the 3 * Common Development and Distribution License ("CDDL"), version 1.0. 4 * You may only use this file in accordance with the terms of version 5 * 1.0 of the CDDL. 6 * 7 * A full copy of the text of the CDDL should have accompanied this 8 * source. A copy of the CDDL is also available via the Internet at 9 * http://www.illumos.org/license/CDDL. 10 */ 11 12 /* 13 * Copyright 2016 Toomas Soome <tsoome@me.com> 14 * Copyright 2020 RackTop Systems, Inc. 15 */ 16 17 /* 18 * Generic framebuffer interface. Implementing common interfaces 19 * for bitmapped frame buffer and vgatext. 20 */ 21 #include <sys/types.h> 22 #include <sys/ddi.h> 23 #include <sys/sunddi.h> 24 #include <sys/file.h> 25 #include <sys/visual_io.h> 26 #include <sys/vgareg.h> 27 #include <sys/vgasubr.h> 28 #include <sys/pci.h> 29 #include <sys/boot_console.h> 30 #include <sys/kd.h> 31 #include <sys/fbio.h> 32 #include <sys/gfx_private.h> 33 #include "gfxp_fb.h" 34 35 #define MYNAME "gfxp_fb" 36 37 /* need to keep vgatext symbols for compatibility */ 38 #pragma weak gfxp_vgatext_softc_alloc = gfxp_fb_softc_alloc 39 #pragma weak gfxp_vgatext_softc_free = gfxp_fb_softc_free 40 #pragma weak gfxp_vgatext_attach = gfxp_fb_attach 41 #pragma weak gfxp_vgatext_detach = gfxp_fb_detach 42 #pragma weak gfxp_vgatext_open = gfxp_fb_open 43 #pragma weak gfxp_vgatext_close = gfxp_fb_close 44 #pragma weak gfxp_vgatext_ioctl = gfxp_fb_ioctl 45 #pragma weak gfxp_vgatext_devmap = gfxp_fb_devmap 46 47 /* 48 * NOTE: this function is duplicated here and in consplat/vgatext while 49 * we work on a set of commitable interfaces to sunpci.c. 50 * 51 * Use the class code to determine if the device is a PCI-to-PCI bridge. 52 * Returns: B_TRUE if the device is a bridge. 53 * B_FALSE if the device is not a bridge or the property cannot be 54 * retrieved. 55 */ 56 static boolean_t 57 is_pci_bridge(dev_info_t *dip) 58 { 59 uint32_t class_code; 60 61 class_code = (uint32_t)ddi_prop_get_int(DDI_DEV_T_ANY, dip, 62 DDI_PROP_DONTPASS, "class-code", 0xffffffff); 63 64 if (class_code == 0xffffffff || class_code == DDI_PROP_NOT_FOUND) 65 return (B_FALSE); 66 67 class_code &= 0x00ffff00; 68 if (class_code == ((PCI_CLASS_BRIDGE << 16) | (PCI_BRIDGE_PCI << 8))) 69 return (B_TRUE); 70 71 return (B_FALSE); 72 } 73 74 #define STREQ(a, b) (strcmp((a), (b)) == 0) 75 76 static void 77 gfxp_check_for_console(dev_info_t *devi, struct gfxp_fb_softc *softc, 78 int pci_pcie_bus) 79 { 80 ddi_acc_handle_t pci_conf; 81 dev_info_t *pdevi; 82 uint16_t data16; 83 84 /* 85 * fb_info is filled in by data gathered by the bootloader. 86 * In particular we are interested in "paddr" which is the physical 87 * address of the framebuffer. If that is not zero, then we have 88 * a valid framebuffer and we can use this device as a console. 89 */ 90 if (fb_info.paddr != 0) { 91 softc->flags |= GFXP_FLAG_CONSOLE; 92 return; 93 } 94 95 /* 96 * Based on Section 11.3, "PCI Display Subsystem Initialization", 97 * of the 1.1 PCI-to-PCI Bridge Architecture Specification 98 * determine if this is the boot console device. First, see 99 * if the SBIOS has turned on PCI I/O for this device. Then if 100 * this is PCI/PCI-E, verify the parent bridge has VGAEnable set. 101 */ 102 103 if (pci_config_setup(devi, &pci_conf) != DDI_SUCCESS) { 104 cmn_err(CE_WARN, MYNAME ": can't get PCI conf handle"); 105 return; 106 } 107 108 data16 = pci_config_get16(pci_conf, PCI_CONF_COMM); 109 if (data16 & PCI_COMM_IO) 110 softc->flags |= GFXP_FLAG_CONSOLE; 111 112 pci_config_teardown(&pci_conf); 113 114 /* If IO not enabled or ISA/EISA, just return */ 115 if (!(softc->flags & GFXP_FLAG_CONSOLE) || !pci_pcie_bus) 116 return; 117 118 /* 119 * Check for VGA Enable in the Bridge Control register for all 120 * PCI/PCIEX parents. If not set all the way up the chain, 121 * this cannot be the boot console. 122 */ 123 124 pdevi = devi; 125 while (pdevi = ddi_get_parent(pdevi)) { 126 int error; 127 ddi_acc_handle_t ppci_conf; 128 char *parent_type = NULL; 129 130 error = ddi_prop_lookup_string(DDI_DEV_T_ANY, pdevi, 131 DDI_PROP_DONTPASS, "device_type", &parent_type); 132 if (error != DDI_SUCCESS) { 133 return; 134 } 135 136 /* Verify still on the PCI/PCIEX parent tree */ 137 if (!STREQ(parent_type, "pci") && 138 !STREQ(parent_type, "pciex")) { 139 ddi_prop_free(parent_type); 140 return; 141 } 142 143 ddi_prop_free(parent_type); 144 parent_type = NULL; 145 146 /* VGAEnable is set only for PCI-to-PCI bridges. */ 147 if (is_pci_bridge(pdevi) == B_FALSE) 148 continue; 149 150 if (pci_config_setup(pdevi, &ppci_conf) != DDI_SUCCESS) 151 continue; 152 153 data16 = pci_config_get16(ppci_conf, PCI_BCNF_BCNTRL); 154 pci_config_teardown(&ppci_conf); 155 156 if (!(data16 & PCI_BCNF_BCNTRL_VGA_ENABLE)) { 157 softc->flags &= ~GFXP_FLAG_CONSOLE; 158 return; 159 } 160 } 161 } 162 163 gfxp_fb_softc_ptr_t 164 gfxp_fb_softc_alloc(void) 165 { 166 return (kmem_zalloc(sizeof (struct gfxp_fb_softc), KM_SLEEP)); 167 } 168 169 void 170 gfxp_fb_softc_free(gfxp_fb_softc_ptr_t ptr) 171 { 172 kmem_free(ptr, sizeof (struct gfxp_fb_softc)); 173 } 174 175 void 176 gfxp_fb_resume(struct gfxp_fb_softc *softc) 177 { 178 if (softc->gfxp_ops->resume != NULL) 179 softc->gfxp_ops->resume(softc); 180 } 181 182 int 183 gfxp_fb_suspend(struct gfxp_fb_softc *softc) 184 { 185 if (softc->gfxp_ops->suspend != NULL) 186 return (softc->gfxp_ops->suspend(softc)); 187 return (DDI_FAILURE); 188 } 189 190 int 191 gfxp_fb_attach(dev_info_t *devi, ddi_attach_cmd_t cmd, gfxp_fb_softc_ptr_t ptr) 192 { 193 struct gfxp_fb_softc *softc = (struct gfxp_fb_softc *)ptr; 194 int error; 195 char *parent_type = NULL; 196 int pci_pcie_bus = 0; 197 int value; 198 199 if (softc == NULL) 200 return (DDI_FAILURE); 201 202 switch (cmd) { 203 case DDI_ATTACH: 204 break; 205 206 case DDI_RESUME: 207 gfxp_fb_resume(softc); 208 return (DDI_SUCCESS); 209 210 default: 211 return (DDI_FAILURE); 212 } 213 214 /* DDI_ATTACH */ 215 softc->devi = devi; /* Copy and init DEVI */ 216 softc->polledio.arg = (struct vis_polledio_arg *)softc; 217 softc->mode = -1; /* the actual value will be set by tem */ 218 mutex_init(&(softc->lock), NULL, MUTEX_DRIVER, NULL); 219 220 error = ddi_prop_lookup_string(DDI_DEV_T_ANY, ddi_get_parent(devi), 221 DDI_PROP_DONTPASS, "device_type", &parent_type); 222 if (error != DDI_SUCCESS) { 223 cmn_err(CE_WARN, MYNAME ": can't determine parent type."); 224 goto fail; 225 } 226 227 if (STREQ(parent_type, "pci") || STREQ(parent_type, "pciex")) { 228 pci_pcie_bus = 1; 229 } 230 ddi_prop_free(parent_type); 231 gfxp_check_for_console(devi, softc, pci_pcie_bus); 232 233 value = GFXP_IS_CONSOLE(softc) ? 1 : 0; 234 if (ddi_prop_update_int(DDI_DEV_T_NONE, devi, 235 "primary-controller", value) != DDI_SUCCESS) { 236 cmn_err(CE_WARN, 237 "Cannot %s primary-controller " 238 "property for driver", value ? "set" : "clear"); 239 } 240 241 switch (fb_info.fb_type) { 242 case FB_TYPE_UNINITIALIZED: 243 /* 244 * While booting from MB1, we do not have FB. 245 * Fall through. 246 */ 247 case FB_TYPE_EGA_TEXT: 248 softc->fb_type = GFXP_VGATEXT; 249 error = gfxp_vga_attach(devi, softc); 250 break; 251 252 case FB_TYPE_INDEXED: /* FB types */ 253 case FB_TYPE_RGB: 254 softc->fb_type = GFXP_BITMAP; 255 error = gfxp_bm_attach(devi, softc); 256 break; 257 258 default: 259 error = DDI_FAILURE; 260 } 261 262 if (error == DDI_SUCCESS) 263 return (error); 264 265 (void) ddi_prop_remove(DDI_DEV_T_ANY, devi, "primary-controller"); 266 fail: 267 (void) gfxp_fb_detach(devi, DDI_DETACH, (void *)softc); 268 return (error); 269 } 270 271 int 272 gfxp_fb_detach(dev_info_t *devi, ddi_detach_cmd_t cmd, gfxp_fb_softc_ptr_t ptr) 273 { 274 struct gfxp_fb_softc *softc = (struct gfxp_fb_softc *)ptr; 275 int error; 276 277 if (softc == NULL) 278 return (DDI_FAILURE); 279 280 switch (cmd) { 281 case DDI_SUSPEND: 282 return (gfxp_fb_suspend(softc)); 283 284 case DDI_DETACH: 285 (void) ddi_prop_remove(DDI_DEV_T_ANY, devi, 286 "primary-controller"); 287 error = DDI_SUCCESS; 288 switch (softc->fb_type) { 289 case GFXP_BITMAP: 290 error = gfxp_bm_detach(devi, softc); 291 break; 292 case GFXP_VGATEXT: 293 error = gfxp_vga_detach(devi, softc); 294 break; 295 } 296 mutex_destroy(&(softc->lock)); 297 return (error); 298 299 default: 300 cmn_err(CE_WARN, "gfxp_fb_detach: unknown cmd 0x%x\n", 301 cmd); 302 return (DDI_FAILURE); 303 } 304 } 305 306 /*ARGSUSED*/ 307 int 308 gfxp_fb_open(dev_t *devp, int flag, int otyp, cred_t *cred, 309 gfxp_fb_softc_ptr_t ptr) 310 { 311 struct gfxp_fb_softc *softc = (struct gfxp_fb_softc *)ptr; 312 313 if (softc == NULL || otyp == OTYP_BLK) 314 return (ENXIO); 315 316 return (0); 317 } 318 319 /*ARGSUSED*/ 320 int 321 gfxp_fb_close(dev_t devp, int flag, int otyp, cred_t *cred, 322 gfxp_fb_softc_ptr_t ptr) 323 { 324 return (0); 325 } 326 327 static int 328 do_gfx_ioctl(int cmd, intptr_t data, int mode, struct gfxp_fb_softc *softc) 329 { 330 static char kernel_only[] = 331 "gfxp_fb_ioctl: %s is a kernel only ioctl"; 332 int err; 333 int kd_mode; 334 335 switch (cmd) { 336 case KDSETMODE: 337 kd_mode = (int)data; 338 if ((kd_mode == softc->mode) || (!GFXP_IS_CONSOLE(softc))) 339 break; 340 return (softc->gfxp_ops->kdsetmode(softc, kd_mode)); 341 342 case KDGETMODE: 343 kd_mode = softc->mode; 344 if (ddi_copyout(&kd_mode, (void *)data, sizeof (int), mode)) 345 return (EFAULT); 346 break; 347 348 case VIS_GETIDENTIFIER: 349 if (ddi_copyout(softc->gfxp_ops->ident, (void *)data, 350 sizeof (struct vis_identifier), mode)) 351 return (EFAULT); 352 break; 353 354 case VIS_DEVINIT: 355 356 if (!(mode & FKIOCTL)) { 357 cmn_err(CE_CONT, kernel_only, "VIS_DEVINIT"); 358 return (ENXIO); 359 } 360 361 err = softc->gfxp_ops->devinit(softc, 362 (struct vis_devinit *)data); 363 if (err != 0) { 364 cmn_err(CE_WARN, 365 "gfxp_fb_ioctl: could not initialize console"); 366 return (err); 367 } 368 break; 369 370 case VIS_CONSCLEAR: /* clear screen */ 371 { 372 struct vis_consclear pma; 373 374 if (ddi_copyin((void *)data, &pma, 375 sizeof (struct vis_consclear), mode)) 376 return (EFAULT); 377 378 return (softc->gfxp_ops->cons_clear(softc, &pma)); 379 } 380 381 case VIS_CONSCOPY: /* move */ 382 { 383 struct vis_conscopy pma; 384 385 if (ddi_copyin((void *)data, &pma, 386 sizeof (struct vis_conscopy), mode)) 387 return (EFAULT); 388 389 softc->gfxp_ops->cons_copy(softc, &pma); 390 break; 391 } 392 393 case VIS_CONSDISPLAY: /* display */ 394 { 395 struct vis_consdisplay display_request; 396 397 if (ddi_copyin((void *)data, &display_request, 398 sizeof (display_request), mode)) 399 return (EFAULT); 400 401 softc->gfxp_ops->cons_display(softc, &display_request); 402 break; 403 } 404 405 case VIS_CONSCURSOR: 406 { 407 struct vis_conscursor cursor_request; 408 409 if (ddi_copyin((void *)data, &cursor_request, 410 sizeof (cursor_request), mode)) 411 return (EFAULT); 412 413 softc->gfxp_ops->cons_cursor(softc, &cursor_request); 414 415 if (cursor_request.action == VIS_GET_CURSOR && 416 ddi_copyout(&cursor_request, (void *)data, 417 sizeof (cursor_request), mode)) 418 return (EFAULT); 419 break; 420 } 421 422 case VIS_GETCMAP: 423 case VIS_PUTCMAP: 424 case FBIOPUTCMAP: 425 case FBIOGETCMAP: 426 /* 427 * At the moment, text mode is not considered to have 428 * a color map. 429 */ 430 return (EINVAL); 431 432 case FBIOGATTR: 433 if (copyout(softc->fbgattr, (void *)data, 434 sizeof (struct fbgattr))) 435 return (EFAULT); 436 break; 437 438 case FBIOGTYPE: 439 if (copyout(&softc->fbgattr->fbtype, (void *)data, 440 sizeof (struct fbtype))) 441 return (EFAULT); 442 break; 443 444 default: 445 cmn_err(CE_CONT, "!unimplemented cmd: 0x%x\n", cmd); 446 return (ENXIO); 447 } 448 return (0); 449 } 450 451 /*ARGSUSED*/ 452 int 453 gfxp_fb_ioctl(dev_t dev, int cmd, intptr_t data, int mode, 454 cred_t *cred, int *rval, gfxp_fb_softc_ptr_t ptr) 455 { 456 struct gfxp_fb_softc *softc = (struct gfxp_fb_softc *)ptr; 457 int error = DDI_FAILURE; 458 459 if (softc == NULL) 460 return (error); 461 mutex_enter(&(softc->lock)); 462 error = do_gfx_ioctl(cmd, data, mode, softc); 463 mutex_exit(&(softc->lock)); 464 return (error); 465 } 466 467 int 468 gfxp_fb_devmap(dev_t dev, devmap_cookie_t dhp, offset_t off, 469 size_t len, size_t *maplen, uint_t model, void *ptr) 470 { 471 struct gfxp_fb_softc *softc = (struct gfxp_fb_softc *)ptr; 472 473 if (softc == NULL) 474 return (DDI_FAILURE); 475 476 return (softc->gfxp_ops->devmap(dev, dhp, off, len, maplen, 477 model, ptr)); 478 } 479