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