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