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