#include <linux/delay.h>
#include <linux/pci.h>
#include <linux/vgaarb.h>
#include <drm/drm_device.h>
#include <drm/drm_print.h>
#include <drm/intel/i915_drm.h>
#include <video/vga.h>
#include "intel_de.h"
#include "intel_display.h"
#include "intel_display_types.h"
#include "intel_vga.h"
#include "intel_vga_regs.h"
static unsigned int intel_gmch_ctrl_reg(struct intel_display *display)
{
return DISPLAY_VER(display) >= 6 ? SNB_GMCH_CTRL : I830_GMCH_CTRL;
}
static bool intel_vga_decode_is_enabled(struct intel_display *display)
{
struct pci_dev *pdev = to_pci_dev(display->drm->dev);
u16 gmch_ctrl = 0;
if (pci_bus_read_config_word(pdev->bus, PCI_DEVFN(0, 0),
intel_gmch_ctrl_reg(display), &gmch_ctrl))
return false;
return !(gmch_ctrl & INTEL_GMCH_VGA_DISABLE);
}
static intel_reg_t intel_vga_cntrl_reg(struct intel_display *display)
{
if (display->platform.valleyview || display->platform.cherryview)
return VLV_VGACNTRL;
else if (DISPLAY_VER(display) >= 5)
return CPU_VGACNTRL;
else
return VGACNTRL;
}
static bool has_vga_pipe_sel(struct intel_display *display)
{
if (display->platform.i845g ||
display->platform.i865g)
return false;
if (display->platform.valleyview ||
display->platform.cherryview)
return true;
return DISPLAY_VER(display) < 7;
}
static bool has_vga_mmio_access(struct intel_display *display)
{
return DISPLAY_VER(display) < 5 && !display->platform.g4x;
}
static bool intel_pci_has_vga_io_decode(struct pci_dev *pdev)
{
u16 cmd = 0;
pci_read_config_word(pdev, PCI_COMMAND, &cmd);
if ((cmd & PCI_COMMAND_IO) == 0)
return false;
pdev = pdev->bus->self;
while (pdev) {
u16 ctl = 0;
pci_read_config_word(pdev, PCI_BRIDGE_CONTROL, &ctl);
if ((ctl & PCI_BRIDGE_CTL_VGA) == 0)
return false;
pdev = pdev->bus->self;
}
return true;
}
static bool intel_pci_set_io_decode(struct pci_dev *pdev, bool enable)
{
u16 old = 0, cmd;
pci_read_config_word(pdev, PCI_COMMAND, &old);
cmd = old & ~PCI_COMMAND_IO;
if (enable)
cmd |= PCI_COMMAND_IO;
pci_write_config_word(pdev, PCI_COMMAND, cmd);
return old & PCI_COMMAND_IO;
}
static bool intel_pci_bridge_set_vga(struct pci_dev *pdev, bool enable)
{
u16 old = 0, ctl;
pci_read_config_word(pdev->bus->self, PCI_BRIDGE_CONTROL, &old);
ctl = old & ~PCI_BRIDGE_CTL_VGA;
if (enable)
ctl |= PCI_BRIDGE_CTL_VGA;
pci_write_config_word(pdev->bus->self, PCI_BRIDGE_CONTROL, ctl);
return old & PCI_BRIDGE_CTL_VGA;
}
static int intel_vga_get(struct intel_display *display, bool mmio,
bool *old_io_decode)
{
struct pci_dev *pdev = to_pci_dev(display->drm->dev);
int err;
if (mmio) {
*old_io_decode = false;
return 0;
}
if (display->platform.dgfx) {
err = vga_get_uninterruptible(pdev, VGA_RSRC_LEGACY_IO);
if (err)
return err;
}
*old_io_decode = intel_pci_set_io_decode(pdev, true);
return 0;
}
static void intel_vga_put(struct intel_display *display, bool io_decode, bool mmio)
{
struct pci_dev *pdev = to_pci_dev(display->drm->dev);
if (mmio)
return;
intel_pci_set_io_decode(pdev, io_decode);
if (display->platform.dgfx)
vga_put(pdev, VGA_RSRC_LEGACY_IO);
}
u8 intel_vga_read(struct intel_display *display, u16 reg, bool mmio)
{
if (mmio)
return intel_de_read8(display, _MMIO(reg));
else
return inb(reg);
}
static void intel_vga_write(struct intel_display *display, u16 reg, u8 val, bool mmio)
{
if (mmio)
intel_de_write8(display, _MMIO(reg), val);
else
outb(val, reg);
}
void intel_vga_disable(struct intel_display *display)
{
struct pci_dev *pdev = to_pci_dev(display->drm->dev);
intel_reg_t vga_reg = intel_vga_cntrl_reg(display);
bool mmio = has_vga_mmio_access(display);
bool io_decode;
u8 msr, sr1;
u32 tmp;
int err;
if (!intel_vga_decode_is_enabled(display)) {
drm_dbg_kms(display->drm, "VGA decode is disabled\n");
goto reset_vgacntr;
}
tmp = intel_de_read(display, vga_reg);
if ((tmp & VGA_DISP_DISABLE) == 0) {
enum pipe pipe;
if (display->platform.cherryview)
pipe = REG_FIELD_GET(VGA_PIPE_SEL_MASK_CHV, tmp);
else if (has_vga_pipe_sel(display))
pipe = REG_FIELD_GET(VGA_PIPE_SEL_MASK, tmp);
else
pipe = PIPE_A;
drm_dbg_kms(display->drm, "Disabling VGA plane on pipe %c\n",
pipe_name(pipe));
} else {
drm_dbg_kms(display->drm, "VGA plane is disabled\n");
if (display->platform.dgfx)
goto reset_vgacntr;
}
err = intel_vga_get(display, mmio, &io_decode);
if (err)
goto reset_vgacntr;
drm_WARN_ON(display->drm, !mmio && !intel_pci_has_vga_io_decode(pdev));
intel_vga_write(display, VGA_SEQ_I, 0x01, mmio);
sr1 = intel_vga_read(display, VGA_SEQ_D, mmio);
sr1 |= VGA_SR01_SCREEN_OFF;
intel_vga_write(display, VGA_SEQ_D, sr1, mmio);
msr = intel_vga_read(display, VGA_MIS_R, mmio);
msr &= ~VGA_MIS_ENB_MEM_ACCESS;
msr &= ~VGA_MIS_COLOR;
intel_vga_write(display, VGA_MIS_W, msr, mmio);
intel_vga_put(display, io_decode, mmio);
if (!display->platform.dgfx)
vga_set_legacy_decoding(pdev, VGA_RSRC_LEGACY_IO);
udelay(300);
reset_vgacntr:
intel_de_write(display, vga_reg, VGA_DISP_DISABLE);
intel_de_posting_read(display, vga_reg);
}
static unsigned int intel_vga_set_decode(struct pci_dev *pdev, bool enable_decode)
{
struct intel_display *display = to_intel_display(pdev);
unsigned int decodes = VGA_RSRC_NORMAL_IO | VGA_RSRC_NORMAL_MEM;
drm_dbg_kms(display->drm, "%s VGA decode due to VGA arbitration\n",
str_enable_disable(enable_decode));
if (display->platform.dgfx) {
if (!enable_decode)
intel_pci_bridge_set_vga(pdev, false);
else
decodes |= VGA_RSRC_LEGACY_IO | VGA_RSRC_LEGACY_MEM;
} else {
if (!enable_decode)
intel_pci_set_io_decode(pdev, false);
else
decodes |= VGA_RSRC_LEGACY_IO;
}
return decodes;
}
void intel_vga_register(struct intel_display *display)
{
struct pci_dev *pdev = to_pci_dev(display->drm->dev);
int ret;
ret = vga_client_register(pdev, intel_vga_set_decode);
drm_WARN_ON(display->drm, ret && ret != -ENODEV);
}
void intel_vga_unregister(struct intel_display *display)
{
struct pci_dev *pdev = to_pci_dev(display->drm->dev);
vga_client_unregister(pdev);
}