/* 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 //#include "i915_trace.h" //#include "../../../platform/x86/intel_ips.h" #include //#include //#include //#include //#include //#include //#include 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; }