/* * This file is part of the coreboot project. * * Copyright (C) 2015 Timothy Pearson , Raptor Engineering * * xgifb_probe taken from the Linux xgifb driver (v3.18.5) and adapted for coreboot * xgifb_modeset cobbled together from other portions of the same driver * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; version 2 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ #include #include #include #include #include #include #include #include #include #include #include "xgi_coreboot.h" #include "vstruct.h" #include "XGIfb.h" #include "XGI_main.h" #include "vb_init.h" #include "vb_util.h" #include "vb_setmode.h" #include "XGI_main.c" static int xgi_vbe_valid; static struct lb_framebuffer xgi_fb; int xgifb_probe(struct pci_dev *pdev, struct xgifb_video_info *xgifb_info) { u8 reg, reg1; u8 CR48, CR38; int ret; struct xgi_hw_device_info *hw_info; unsigned long video_size_max; hw_info = &xgifb_info->hw_info; xgifb_info->chip_id = pdev->device; pci_read_config_byte(pdev, PCI_REVISION_ID, &xgifb_info->revision_id); hw_info->jChipRevision = xgifb_info->revision_id; xgifb_info->subsysvendor = pdev->subsystem_vendor; xgifb_info->subsysdevice = pdev->subsystem_device; video_size_max = pci_resource_len(pdev, 0); xgifb_info->video_base = pci_resource_start(pdev, 0); xgifb_info->mmio_base = pci_resource_start(pdev, 1); xgifb_info->mmio_size = pci_resource_len(pdev, 1); xgifb_info->vga_base = pci_resource_start(pdev, 2) + 0x30; dev_info(&pdev->dev, "Relocate IO address: %Lx [%08lx]\n", (u64) pci_resource_start(pdev, 2), xgifb_info->vga_base); if (XGIfb_crt2type != -1) { xgifb_info->display2 = XGIfb_crt2type; xgifb_info->display2_force = true; } XGIRegInit(&xgifb_info->dev_info, xgifb_info->vga_base); xgifb_reg_set(XGISR, IND_SIS_PASSWORD, SIS_PASSWORD); reg1 = xgifb_reg_get(XGISR, IND_SIS_PASSWORD); if (reg1 != 0xa1) { dev_err(&pdev->dev, "I/O error\n"); ret = -5; goto error_disable; } switch (xgifb_info->chip_id) { case PCI_DEVICE_ID_XGI_20: xgifb_reg_or(XGICR, Index_CR_GPIO_Reg3, GPIOG_EN); CR48 = xgifb_reg_get(XGICR, Index_CR_GPIO_Reg1); if (CR48&GPIOG_READ) xgifb_info->chip = XG21; else xgifb_info->chip = XG20; break; case PCI_DEVICE_ID_XGI_40: xgifb_info->chip = XG40; break; case PCI_DEVICE_ID_XGI_42: xgifb_info->chip = XG42; break; case PCI_DEVICE_ID_XGI_27: xgifb_info->chip = XG27; break; default: ret = -19; goto error_disable; } dev_info(&pdev->dev, "chipid = %x\n", xgifb_info->chip); hw_info->jChipType = xgifb_info->chip; if (XGIfb_get_dram_size(xgifb_info)) { xgifb_info->video_size = min_t(unsigned long, video_size_max, SZ_16M); } else if (xgifb_info->video_size > video_size_max) { xgifb_info->video_size = video_size_max; } if (IS_ENABLED(CONFIG_LINEAR_FRAMEBUFFER)) { /* Enable PCI_LINEAR_ADDRESSING and MMIO_ENABLE */ xgifb_reg_or(XGISR, IND_SIS_PCI_ADDRESS_SET, (SIS_PCI_ADDR_ENABLE | SIS_MEM_MAP_IO_ENABLE)); /* Enable 2D accelerator engine */ xgifb_reg_or(XGISR, IND_SIS_MODULE_ENABLE, SIS_ENABLE_2D); } hw_info->ulVideoMemorySize = xgifb_info->video_size; xgifb_info->video_vbase = hw_info->pjVideoMemoryAddress = (void *)(intptr_t)xgifb_info->video_base; xgifb_info->mmio_vbase = (void *)(intptr_t)xgifb_info->mmio_base; dev_info(&pdev->dev, "Framebuffer at 0x%Lx, mapped to 0x%p, size %dk\n", (u64) xgifb_info->video_base, xgifb_info->video_vbase, xgifb_info->video_size / 1024); dev_info(&pdev->dev, "MMIO at 0x%Lx, mapped to 0x%p, size %ldk\n", (u64) xgifb_info->mmio_base, xgifb_info->mmio_vbase, xgifb_info->mmio_size / 1024); pci_set_drvdata(pdev, xgifb_info); if (!XGIInitNew(pdev)) dev_err(&pdev->dev, "XGIInitNew() failed!\n"); xgifb_info->mtrr = -1; xgifb_info->hasVB = HASVB_NONE; if ((xgifb_info->chip == XG20) || (xgifb_info->chip == XG27)) { xgifb_info->hasVB = HASVB_NONE; } else if (xgifb_info->chip == XG21) { CR38 = xgifb_reg_get(XGICR, 0x38); if ((CR38 & 0xE0) == 0xC0) xgifb_info->display2 = XGIFB_DISP_LCD; else if ((CR38 & 0xE0) == 0x60) xgifb_info->hasVB = HASVB_CHRONTEL; else xgifb_info->hasVB = HASVB_NONE; } else { XGIfb_get_VB_type(xgifb_info); } hw_info->ujVBChipID = VB_CHIP_UNKNOWN; hw_info->ulExternalChip = 0; switch (xgifb_info->hasVB) { case HASVB_301: reg = xgifb_reg_get(XGIPART4, 0x01); if (reg >= 0xE0) { hw_info->ujVBChipID = VB_CHIP_302LV; dev_info(&pdev->dev, "XGI302LV bridge detected (revision 0x%02x)\n", reg); } else if (reg >= 0xD0) { hw_info->ujVBChipID = VB_CHIP_301LV; dev_info(&pdev->dev, "XGI301LV bridge detected (revision 0x%02x)\n", reg); } else { hw_info->ujVBChipID = VB_CHIP_301; dev_info(&pdev->dev, "XGI301 bridge detected\n"); } break; case HASVB_302: reg = xgifb_reg_get(XGIPART4, 0x01); if (reg >= 0xE0) { hw_info->ujVBChipID = VB_CHIP_302LV; dev_info(&pdev->dev, "XGI302LV bridge detected (revision 0x%02x)\n", reg); } else if (reg >= 0xD0) { hw_info->ujVBChipID = VB_CHIP_301LV; dev_info(&pdev->dev, "XGI302LV bridge detected (revision 0x%02x)\n", reg); } else if (reg >= 0xB0) { reg1 = xgifb_reg_get(XGIPART4, 0x23); hw_info->ujVBChipID = VB_CHIP_302B; } else { hw_info->ujVBChipID = VB_CHIP_302; dev_info(&pdev->dev, "XGI302 bridge detected\n"); } break; case HASVB_LVDS: hw_info->ulExternalChip = 0x1; dev_info(&pdev->dev, "LVDS transmitter detected\n"); break; case HASVB_TRUMPION: hw_info->ulExternalChip = 0x2; dev_info(&pdev->dev, "Trumpion Zurac LVDS scaler detected\n"); break; case HASVB_CHRONTEL: hw_info->ulExternalChip = 0x4; dev_info(&pdev->dev, "Chrontel TV encoder detected\n"); break; case HASVB_LVDS_CHRONTEL: hw_info->ulExternalChip = 0x5; dev_info(&pdev->dev, "LVDS transmitter and Chrontel TV encoder detected\n"); break; default: dev_info(&pdev->dev, "No or unknown bridge type detected\n"); break; } if (xgifb_info->hasVB != HASVB_NONE) XGIfb_detect_VB(xgifb_info); else if (xgifb_info->chip != XG21) xgifb_info->display2 = XGIFB_DISP_NONE; if (xgifb_info->display2 == XGIFB_DISP_LCD) { if (!enable_dstn) { reg = xgifb_reg_get(XGICR, IND_XGI_LCD_PANEL); reg &= 0x0f; hw_info->ulCRT2LCDType = XGI310paneltype[reg]; } } xgifb_info->mode_idx = -1; /* FIXME coreboot does not provide sscanf, needed by XGIfb_search_mode */ /* if (mode) XGIfb_search_mode(xgifb_info, mode); else */if (vesa != -1) XGIfb_search_vesamode(xgifb_info, vesa); if (xgifb_info->mode_idx >= 0) xgifb_info->mode_idx = XGIfb_validate_mode(xgifb_info, xgifb_info->mode_idx); if (xgifb_info->mode_idx < 0) { if (xgifb_info->display2 == XGIFB_DISP_LCD && xgifb_info->chip == XG21) xgifb_info->mode_idx = XGIfb_GetXG21DefaultLVDSModeIdx(xgifb_info); else if (IS_ENABLED(CONFIG_LINEAR_FRAMEBUFFER)) xgifb_info->mode_idx = DEFAULT_MODE; else xgifb_info->mode_idx = DEFAULT_TEXT_MODE; } if (xgifb_info->mode_idx < 0) { dev_err(&pdev->dev, "No supported video mode found\n"); ret = -22; goto error_1; } /* set default refresh rate */ xgifb_info->refresh_rate = refresh_rate; if (xgifb_info->refresh_rate == 0) xgifb_info->refresh_rate = 60; if (XGIfb_search_refresh_rate(xgifb_info, xgifb_info->refresh_rate) == 0) { xgifb_info->rate_idx = 1; xgifb_info->refresh_rate = 60; } xgifb_info->video_bpp = XGIbios_mode[xgifb_info->mode_idx].bpp; xgifb_info->video_vwidth = xgifb_info->video_width = XGIbios_mode[xgifb_info->mode_idx].xres; xgifb_info->video_vheight = xgifb_info->video_height = XGIbios_mode[xgifb_info->mode_idx].yres; xgifb_info->org_x = xgifb_info->org_y = 0; xgifb_info->video_linelength = xgifb_info->video_width * (xgifb_info->video_bpp >> 3); switch (xgifb_info->video_bpp) { case 8: xgifb_info->DstColor = 0x0000; xgifb_info->XGI310_AccelDepth = 0x00000000; xgifb_info->video_cmap_len = 256; break; case 16: xgifb_info->DstColor = 0x8000; xgifb_info->XGI310_AccelDepth = 0x00010000; xgifb_info->video_cmap_len = 16; break; case 32: xgifb_info->DstColor = 0xC000; xgifb_info->XGI310_AccelDepth = 0x00020000; xgifb_info->video_cmap_len = 16; break; default: xgifb_info->video_cmap_len = 16; pr_info("Unsupported depth %d\n", xgifb_info->video_bpp); break; } pr_info("Default mode is %dx%dx%d (%dHz)\n", xgifb_info->video_width, xgifb_info->video_height, xgifb_info->video_bpp, xgifb_info->refresh_rate); return 0; error_1: error_disable: return ret; } int xgifb_modeset(struct pci_dev *pdev, struct xgifb_video_info *xgifb_info) { struct xgi_hw_device_info *hw_info; hw_info = &xgifb_info->hw_info; if (IS_ENABLED(CONFIG_LINEAR_FRAMEBUFFER)) { /* Set mode */ XGIfb_pre_setmode(xgifb_info); if (XGISetModeNew(xgifb_info, hw_info, XGIbios_mode[xgifb_info->mode_idx].mode_no) == 0) { pr_err("Setting mode[0x%x] failed\n", XGIbios_mode[xgifb_info->mode_idx].mode_no); return -22; } xgifb_info->video_linelength = xgifb_info->video_width * (xgifb_info->video_bpp >> 3); xgifb_reg_set(XGISR, IND_SIS_PASSWORD, SIS_PASSWORD); xgifb_reg_set(XGICR, 0x13, (xgifb_info->video_linelength & 0x00ff)); xgifb_reg_set(XGISR, 0x0e, (xgifb_info->video_linelength & 0xff00) >> 8); XGIfb_post_setmode(xgifb_info); pr_debug("Set new mode: %dx%dx%d-%d\n", XGIbios_mode[xgifb_info->mode_idx].xres, XGIbios_mode[xgifb_info->mode_idx].yres, XGIbios_mode[xgifb_info->mode_idx].bpp, xgifb_info->refresh_rate); /* Set LinuxBIOS framebuffer information */ xgi_vbe_valid = 1; xgi_fb.physical_address = xgifb_info->video_base; xgi_fb.x_resolution = xgifb_info->video_width; xgi_fb.y_resolution = xgifb_info->video_height; xgi_fb.bytes_per_line = xgifb_info->video_width * xgifb_info->video_bpp; xgi_fb.bits_per_pixel = xgifb_info->video_bpp; xgi_fb.reserved_mask_pos = 0; xgi_fb.reserved_mask_size = 0; switch (xgifb_info->video_bpp) { case 32: case 24: /* packed into 4-byte words */ xgi_fb.reserved_mask_pos = 24; xgi_fb.reserved_mask_size = 8; xgi_fb.red_mask_pos = 16; xgi_fb.red_mask_size = 8; xgi_fb.green_mask_pos = 8; xgi_fb.green_mask_size = 8; xgi_fb.blue_mask_pos = 0; xgi_fb.blue_mask_size = 8; break; case 16: /* packed into 2-byte words */ xgi_fb.red_mask_pos = 11; xgi_fb.red_mask_size = 5; xgi_fb.green_mask_pos = 5; xgi_fb.green_mask_size = 6; xgi_fb.blue_mask_pos = 0; xgi_fb.blue_mask_size = 5; break; default: printk(BIOS_SPEW, "%s: unsupported BPP %d\n", __func__, xgifb_info->video_bpp); xgi_vbe_valid = 0; } } else { /* * FIXME * Text mode is slightly unstable/jittery * (bad/incomplete DDR init?) */ /* Initialize standard VGA text mode */ vga_io_init(); vga_textmode_init(); printk(BIOS_INFO, "XGI VGA text mode initialized\n"); /* if we don't have console, at least print something... */ vga_line_write(0, "XGI VGA text mode initialized"); } return 0; } static int vbe_mode_info_valid(void) { return xgi_vbe_valid; } int fill_lb_framebuffer(struct lb_framebuffer *framebuffer) { if (!vbe_mode_info_valid()) return -1; *framebuffer = xgi_fb; return 0; } struct xgifb_video_info *xgifb_video_info_ptr; struct xgifb_video_info *pci_get_drvdata(struct pci_dev *pdev) { return xgifb_video_info_ptr; } void pci_set_drvdata(struct pci_dev *pdev, struct xgifb_video_info *data) { xgifb_video_info_ptr = data; }