forked from KolibriOS/kolibrios
442 lines
13 KiB
C
442 lines
13 KiB
C
|
/* i915_dma.c -- DMA support for the I915 -*- linux-c -*-
|
||
|
*/
|
||
|
/*
|
||
|
* Copyright 2003 Tungsten Graphics, Inc., Cedar Park, Texas.
|
||
|
* All Rights Reserved.
|
||
|
*
|
||
|
* Permission is hereby granted, free of charge, to any person obtaining a
|
||
|
* copy of this software and associated documentation files (the
|
||
|
* "Software"), to deal in the Software without restriction, including
|
||
|
* without limitation the rights to use, copy, modify, merge, publish,
|
||
|
* distribute, sub license, and/or sell copies of the Software, and to
|
||
|
* permit persons to whom the Software is furnished to do so, subject to
|
||
|
* the following conditions:
|
||
|
*
|
||
|
* The above copyright notice and this permission notice (including the
|
||
|
* next paragraph) shall be included in all copies or substantial portions
|
||
|
* of the Software.
|
||
|
*
|
||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||
|
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||
|
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
|
||
|
* IN NO EVENT SHALL TUNGSTEN GRAPHICS AND/OR ITS SUPPLIERS BE LIABLE FOR
|
||
|
* ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||
|
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||
|
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||
|
*
|
||
|
*/
|
||
|
|
||
|
#include "drmP.h"
|
||
|
#include "drm.h"
|
||
|
#include "drm_crtc_helper.h"
|
||
|
#include "drm_fb_helper.h"
|
||
|
#include "intel_drv.h"
|
||
|
//#include "i915_drm.h"
|
||
|
#include "i915_drv.h"
|
||
|
#include <drm/intel-gtt.h>
|
||
|
//#include "i915_trace.h"
|
||
|
//#include "../../../platform/x86/intel_ips.h"
|
||
|
#include <linux/pci.h>
|
||
|
//#include <linux/vgaarb.h>
|
||
|
//#include <linux/acpi.h>
|
||
|
//#include <linux/pnp.h>
|
||
|
//#include <linux/vga_switcheroo.h>
|
||
|
//#include <linux/slab.h>
|
||
|
//#include <acpi/video.h>
|
||
|
|
||
|
void __iomem *pci_iomap(struct pci_dev *dev, int bar, unsigned long maxlen);
|
||
|
|
||
|
static void i915_write_hws_pga(struct drm_device *dev)
|
||
|
{
|
||
|
drm_i915_private_t *dev_priv = dev->dev_private;
|
||
|
u32 addr;
|
||
|
|
||
|
addr = dev_priv->status_page_dmah->busaddr;
|
||
|
if (INTEL_INFO(dev)->gen >= 4)
|
||
|
addr |= (dev_priv->status_page_dmah->busaddr >> 28) & 0xf0;
|
||
|
I915_WRITE(HWS_PGA, addr);
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
* Sets up the hardware status page for devices that need a physical address
|
||
|
* in the register.
|
||
|
*/
|
||
|
static int i915_init_phys_hws(struct drm_device *dev)
|
||
|
{
|
||
|
drm_i915_private_t *dev_priv = dev->dev_private;
|
||
|
|
||
|
/* Program Hardware Status Page */
|
||
|
dev_priv->status_page_dmah =
|
||
|
drm_pci_alloc(dev, PAGE_SIZE, PAGE_SIZE);
|
||
|
|
||
|
if (!dev_priv->status_page_dmah) {
|
||
|
DRM_ERROR("Can not allocate hardware status page\n");
|
||
|
return -ENOMEM;
|
||
|
}
|
||
|
|
||
|
i915_write_hws_pga(dev);
|
||
|
|
||
|
dbgprintf("Enabled hardware status page\n");
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static void i915_pineview_get_mem_freq(struct drm_device *dev)
|
||
|
{
|
||
|
drm_i915_private_t *dev_priv = dev->dev_private;
|
||
|
u32 tmp;
|
||
|
|
||
|
tmp = I915_READ(CLKCFG);
|
||
|
|
||
|
switch (tmp & CLKCFG_FSB_MASK) {
|
||
|
case CLKCFG_FSB_533:
|
||
|
dev_priv->fsb_freq = 533; /* 133*4 */
|
||
|
break;
|
||
|
case CLKCFG_FSB_800:
|
||
|
dev_priv->fsb_freq = 800; /* 200*4 */
|
||
|
break;
|
||
|
case CLKCFG_FSB_667:
|
||
|
dev_priv->fsb_freq = 667; /* 167*4 */
|
||
|
break;
|
||
|
case CLKCFG_FSB_400:
|
||
|
dev_priv->fsb_freq = 400; /* 100*4 */
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
switch (tmp & CLKCFG_MEM_MASK) {
|
||
|
case CLKCFG_MEM_533:
|
||
|
dev_priv->mem_freq = 533;
|
||
|
break;
|
||
|
case CLKCFG_MEM_667:
|
||
|
dev_priv->mem_freq = 667;
|
||
|
break;
|
||
|
case CLKCFG_MEM_800:
|
||
|
dev_priv->mem_freq = 800;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
/* detect pineview DDR3 setting */
|
||
|
tmp = I915_READ(CSHRDDR3CTL);
|
||
|
dev_priv->is_ddr3 = (tmp & CSHRDDR3CTL_DDR3) ? 1 : 0;
|
||
|
}
|
||
|
|
||
|
static void i915_ironlake_get_mem_freq(struct drm_device *dev)
|
||
|
{
|
||
|
drm_i915_private_t *dev_priv = dev->dev_private;
|
||
|
u16 ddrpll, csipll;
|
||
|
|
||
|
ddrpll = I915_READ16(DDRMPLL1);
|
||
|
csipll = I915_READ16(CSIPLL0);
|
||
|
|
||
|
switch (ddrpll & 0xff) {
|
||
|
case 0xc:
|
||
|
dev_priv->mem_freq = 800;
|
||
|
break;
|
||
|
case 0x10:
|
||
|
dev_priv->mem_freq = 1066;
|
||
|
break;
|
||
|
case 0x14:
|
||
|
dev_priv->mem_freq = 1333;
|
||
|
break;
|
||
|
case 0x18:
|
||
|
dev_priv->mem_freq = 1600;
|
||
|
break;
|
||
|
default:
|
||
|
DRM_DEBUG_DRIVER("unknown memory frequency 0x%02x\n",
|
||
|
ddrpll & 0xff);
|
||
|
dev_priv->mem_freq = 0;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
dev_priv->r_t = dev_priv->mem_freq;
|
||
|
|
||
|
switch (csipll & 0x3ff) {
|
||
|
case 0x00c:
|
||
|
dev_priv->fsb_freq = 3200;
|
||
|
break;
|
||
|
case 0x00e:
|
||
|
dev_priv->fsb_freq = 3733;
|
||
|
break;
|
||
|
case 0x010:
|
||
|
dev_priv->fsb_freq = 4266;
|
||
|
break;
|
||
|
case 0x012:
|
||
|
dev_priv->fsb_freq = 4800;
|
||
|
break;
|
||
|
case 0x014:
|
||
|
dev_priv->fsb_freq = 5333;
|
||
|
break;
|
||
|
case 0x016:
|
||
|
dev_priv->fsb_freq = 5866;
|
||
|
break;
|
||
|
case 0x018:
|
||
|
dev_priv->fsb_freq = 6400;
|
||
|
break;
|
||
|
default:
|
||
|
DRM_DEBUG_DRIVER("unknown fsb frequency 0x%04x\n",
|
||
|
csipll & 0x3ff);
|
||
|
dev_priv->fsb_freq = 0;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
if (dev_priv->fsb_freq == 3200) {
|
||
|
dev_priv->c_m = 0;
|
||
|
} else if (dev_priv->fsb_freq > 3200 && dev_priv->fsb_freq <= 4800) {
|
||
|
dev_priv->c_m = 1;
|
||
|
} else {
|
||
|
dev_priv->c_m = 2;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static int i915_get_bridge_dev(struct drm_device *dev)
|
||
|
{
|
||
|
struct drm_i915_private *dev_priv = dev->dev_private;
|
||
|
|
||
|
dev_priv->bridge_dev = pci_get_bus_and_slot(0, PCI_DEVFN(0,0));
|
||
|
if (!dev_priv->bridge_dev) {
|
||
|
DRM_ERROR("bridge device not found\n");
|
||
|
return -1;
|
||
|
}
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
|
||
|
/* Global for IPS driver to get at the current i915 device */
|
||
|
static struct drm_i915_private *i915_mch_dev;
|
||
|
/*
|
||
|
* Lock protecting IPS related data structures
|
||
|
* - i915_mch_dev
|
||
|
* - dev_priv->max_delay
|
||
|
* - dev_priv->min_delay
|
||
|
* - dev_priv->fmax
|
||
|
* - dev_priv->gpu_busy
|
||
|
*/
|
||
|
static DEFINE_SPINLOCK(mchdev_lock);
|
||
|
|
||
|
|
||
|
/**
|
||
|
* i915_driver_load - setup chip and create an initial config
|
||
|
* @dev: DRM device
|
||
|
* @flags: startup flags
|
||
|
*
|
||
|
* The driver load routine has to do several things:
|
||
|
* - drive output discovery via intel_modeset_init()
|
||
|
* - initialize the memory manager
|
||
|
* - allocate initial config memory
|
||
|
* - setup the DRM framebuffer with the allocated memory
|
||
|
*/
|
||
|
int i915_driver_load(struct drm_device *dev, unsigned long flags)
|
||
|
{
|
||
|
struct drm_i915_private *dev_priv;
|
||
|
int ret = 0, mmio_bar;
|
||
|
uint32_t agp_size;
|
||
|
|
||
|
ENTER();
|
||
|
|
||
|
dev_priv = kzalloc(sizeof(drm_i915_private_t), GFP_KERNEL);
|
||
|
if (dev_priv == NULL)
|
||
|
return -ENOMEM;
|
||
|
|
||
|
dev->dev_private = (void *)dev_priv;
|
||
|
dev_priv->dev = dev;
|
||
|
dev_priv->info = (struct intel_device_info *) flags;
|
||
|
|
||
|
if (i915_get_bridge_dev(dev)) {
|
||
|
ret = -EIO;
|
||
|
goto free_priv;
|
||
|
}
|
||
|
|
||
|
/* overlay on gen2 is broken and can't address above 1G */
|
||
|
// if (IS_GEN2(dev))
|
||
|
// dma_set_coherent_mask(&dev->pdev->dev, DMA_BIT_MASK(30));
|
||
|
|
||
|
/* 965GM sometimes incorrectly writes to hardware status page (HWS)
|
||
|
* using 32bit addressing, overwriting memory if HWS is located
|
||
|
* above 4GB.
|
||
|
*
|
||
|
* The documentation also mentions an issue with undefined
|
||
|
* behaviour if any general state is accessed within a page above 4GB,
|
||
|
* which also needs to be handled carefully.
|
||
|
*/
|
||
|
// if (IS_BROADWATER(dev) || IS_CRESTLINE(dev))
|
||
|
// dma_set_coherent_mask(&dev->pdev->dev, DMA_BIT_MASK(32));
|
||
|
|
||
|
mmio_bar = IS_GEN2(dev) ? 1 : 0;
|
||
|
dev_priv->regs = pci_iomap(dev->pdev, mmio_bar, 0);
|
||
|
if (!dev_priv->regs) {
|
||
|
DRM_ERROR("failed to map registers\n");
|
||
|
ret = -EIO;
|
||
|
goto put_bridge;
|
||
|
}
|
||
|
|
||
|
dev_priv->mm.gtt = intel_gtt_get();
|
||
|
if (!dev_priv->mm.gtt) {
|
||
|
DRM_ERROR("Failed to initialize GTT\n");
|
||
|
ret = -ENODEV;
|
||
|
goto out_rmmap;
|
||
|
}
|
||
|
|
||
|
// agp_size = dev_priv->mm.gtt->gtt_mappable_entries << PAGE_SHIFT;
|
||
|
|
||
|
/* agp_bridge->gart_bus_addr = intel_private.gma_bus_addr; */
|
||
|
|
||
|
// dev_priv->mm.gtt_mapping =
|
||
|
// io_mapping_create_wc(dev->agp->base, agp_size);
|
||
|
// if (dev_priv->mm.gtt_mapping == NULL) {
|
||
|
// ret = -EIO;
|
||
|
// goto out_rmmap;
|
||
|
// }
|
||
|
|
||
|
/* Set up a WC MTRR for non-PAT systems. This is more common than
|
||
|
* one would think, because the kernel disables PAT on first
|
||
|
* generation Core chips because WC PAT gets overridden by a UC
|
||
|
* MTRR if present. Even if a UC MTRR isn't present.
|
||
|
*/
|
||
|
// dev_priv->mm.gtt_mtrr = mtrr_add(dev->agp->base,
|
||
|
// agp_size,
|
||
|
// MTRR_TYPE_WRCOMB, 1);
|
||
|
// if (dev_priv->mm.gtt_mtrr < 0) {
|
||
|
// DRM_INFO("MTRR allocation failed. Graphics "
|
||
|
// "performance may suffer.\n");
|
||
|
// }
|
||
|
|
||
|
/* The i915 workqueue is primarily used for batched retirement of
|
||
|
* requests (and thus managing bo) once the task has been completed
|
||
|
* by the GPU. i915_gem_retire_requests() is called directly when we
|
||
|
* need high-priority retirement, such as waiting for an explicit
|
||
|
* bo.
|
||
|
*
|
||
|
* It is also used for periodic low-priority events, such as
|
||
|
* idle-timers and recording error state.
|
||
|
*
|
||
|
* All tasks on the workqueue are expected to acquire the dev mutex
|
||
|
* so there is no point in running more than one instance of the
|
||
|
* workqueue at any time: max_active = 1 and NON_REENTRANT.
|
||
|
*/
|
||
|
|
||
|
// dev_priv->wq = alloc_workqueue("i915",
|
||
|
// WQ_UNBOUND | WQ_NON_REENTRANT,
|
||
|
// 1);
|
||
|
// if (dev_priv->wq == NULL) {
|
||
|
// DRM_ERROR("Failed to create our workqueue.\n");
|
||
|
// ret = -ENOMEM;
|
||
|
// goto out_mtrrfree;
|
||
|
// }
|
||
|
|
||
|
/* enable GEM by default */
|
||
|
dev_priv->has_gem = 1;
|
||
|
|
||
|
|
||
|
// intel_irq_init(dev);
|
||
|
|
||
|
/* Try to make sure MCHBAR is enabled before poking at it */
|
||
|
// intel_setup_mchbar(dev);
|
||
|
intel_setup_gmbus(dev);
|
||
|
|
||
|
// intel_opregion_setup(dev);
|
||
|
|
||
|
/* Make sure the bios did its job and set up vital registers */
|
||
|
// intel_setup_bios(dev);
|
||
|
|
||
|
i915_gem_load(dev);
|
||
|
|
||
|
/* Init HWS */
|
||
|
if (!I915_NEED_GFX_HWS(dev)) {
|
||
|
ret = i915_init_phys_hws(dev);
|
||
|
if (ret)
|
||
|
goto out_gem_unload;
|
||
|
}
|
||
|
|
||
|
if (IS_PINEVIEW(dev))
|
||
|
i915_pineview_get_mem_freq(dev);
|
||
|
else if (IS_GEN5(dev))
|
||
|
i915_ironlake_get_mem_freq(dev);
|
||
|
|
||
|
/* On the 945G/GM, the chipset reports the MSI capability on the
|
||
|
* integrated graphics even though the support isn't actually there
|
||
|
* according to the published specs. It doesn't appear to function
|
||
|
* correctly in testing on 945G.
|
||
|
* This may be a side effect of MSI having been made available for PEG
|
||
|
* and the registers being closely associated.
|
||
|
*
|
||
|
* According to chipset errata, on the 965GM, MSI interrupts may
|
||
|
* be lost or delayed, but we use them anyways to avoid
|
||
|
* stuck interrupts on some machines.
|
||
|
*/
|
||
|
// if (!IS_I945G(dev) && !IS_I945GM(dev))
|
||
|
// pci_enable_msi(dev->pdev);
|
||
|
|
||
|
spin_lock_init(&dev_priv->irq_lock);
|
||
|
spin_lock_init(&dev_priv->error_lock);
|
||
|
spin_lock_init(&dev_priv->rps_lock);
|
||
|
|
||
|
if (IS_MOBILE(dev) || !IS_GEN2(dev))
|
||
|
dev_priv->num_pipe = 2;
|
||
|
else
|
||
|
dev_priv->num_pipe = 1;
|
||
|
|
||
|
// ret = drm_vblank_init(dev, dev_priv->num_pipe);
|
||
|
// if (ret)
|
||
|
// goto out_gem_unload;
|
||
|
|
||
|
/* Start out suspended */
|
||
|
dev_priv->mm.suspended = 1;
|
||
|
|
||
|
intel_detect_pch(dev);
|
||
|
|
||
|
|
||
|
// if (drm_core_check_feature(dev, DRIVER_MODESET)) {
|
||
|
// ret = i915_load_modeset_init(dev);
|
||
|
// if (ret < 0) {
|
||
|
// DRM_ERROR("failed to init modeset\n");
|
||
|
// goto out_gem_unload;
|
||
|
// }
|
||
|
// }
|
||
|
|
||
|
/* Must be done after probing outputs */
|
||
|
// intel_opregion_init(dev);
|
||
|
// acpi_video_register();
|
||
|
|
||
|
// setup_timer(&dev_priv->hangcheck_timer, i915_hangcheck_elapsed,
|
||
|
// (unsigned long) dev);
|
||
|
|
||
|
spin_lock(&mchdev_lock);
|
||
|
i915_mch_dev = dev_priv;
|
||
|
dev_priv->mchdev_lock = &mchdev_lock;
|
||
|
spin_unlock(&mchdev_lock);
|
||
|
|
||
|
// ips_ping_for_i915_load();
|
||
|
|
||
|
LEAVE();
|
||
|
|
||
|
return 0;
|
||
|
|
||
|
out_gem_unload:
|
||
|
// if (dev_priv->mm.inactive_shrinker.shrink)
|
||
|
// unregister_shrinker(&dev_priv->mm.inactive_shrinker);
|
||
|
|
||
|
// if (dev->pdev->msi_enabled)
|
||
|
// pci_disable_msi(dev->pdev);
|
||
|
|
||
|
// intel_teardown_gmbus(dev);
|
||
|
// intel_teardown_mchbar(dev);
|
||
|
// destroy_workqueue(dev_priv->wq);
|
||
|
out_mtrrfree:
|
||
|
// if (dev_priv->mm.gtt_mtrr >= 0) {
|
||
|
// mtrr_del(dev_priv->mm.gtt_mtrr, dev->agp->base,
|
||
|
// dev->agp->agp_info.aper_size * 1024 * 1024);
|
||
|
// dev_priv->mm.gtt_mtrr = -1;
|
||
|
// }
|
||
|
// io_mapping_free(dev_priv->mm.gtt_mapping);
|
||
|
|
||
|
out_rmmap:
|
||
|
pci_iounmap(dev->pdev, dev_priv->regs);
|
||
|
|
||
|
put_bridge:
|
||
|
// pci_dev_put(dev_priv->bridge_dev);
|
||
|
free_priv:
|
||
|
kfree(dev_priv);
|
||
|
return ret;
|
||
|
}
|
||
|
|