2010-11-06 05:23:30 +08:00
|
|
|
/*
|
|
|
|
* Copyright © 2010 Daniel Vetter
|
|
|
|
*
|
|
|
|
* 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, sublicense,
|
|
|
|
* 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 NONINFRINGEMENT. IN NO EVENT SHALL
|
|
|
|
* THE AUTHORS OR COPYRIGHT HOLDERS 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.
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
2012-10-03 01:01:07 +08:00
|
|
|
#include <drm/drmP.h>
|
|
|
|
#include <drm/i915_drm.h>
|
2010-11-06 05:23:30 +08:00
|
|
|
#include "i915_drv.h"
|
|
|
|
#include "i915_trace.h"
|
|
|
|
#include "intel_drv.h"
|
|
|
|
|
2013-06-28 07:30:04 +08:00
|
|
|
#define GEN6_PPGTT_PD_ENTRIES 512
|
|
|
|
#define I915_PPGTT_PT_ENTRIES (PAGE_SIZE / sizeof(gen6_gtt_pte_t))
|
|
|
|
|
2012-11-05 01:21:31 +08:00
|
|
|
/* PPGTT stuff */
|
|
|
|
#define GEN6_GTT_ADDR_ENCODE(addr) ((addr) | (((addr) >> 28) & 0xff0))
|
|
|
|
|
|
|
|
#define GEN6_PDE_VALID (1 << 0)
|
|
|
|
/* gen6+ has bit 11-4 for physical addr bit 39-32 */
|
|
|
|
#define GEN6_PDE_ADDR_ENCODE(addr) GEN6_GTT_ADDR_ENCODE(addr)
|
|
|
|
|
|
|
|
#define GEN6_PTE_VALID (1 << 0)
|
|
|
|
#define GEN6_PTE_UNCACHED (1 << 1)
|
|
|
|
#define HSW_PTE_UNCACHED (0)
|
|
|
|
#define GEN6_PTE_CACHE_LLC (2 << 1)
|
|
|
|
#define GEN6_PTE_CACHE_LLC_MLC (3 << 1)
|
|
|
|
#define GEN6_PTE_ADDR_ENCODE(addr) GEN6_GTT_ADDR_ENCODE(addr)
|
|
|
|
|
2013-06-28 07:30:19 +08:00
|
|
|
static gen6_gtt_pte_t gen6_pte_encode(dma_addr_t addr,
|
2013-04-22 15:53:49 +08:00
|
|
|
enum i915_cache_level level)
|
2012-09-25 07:44:32 +08:00
|
|
|
{
|
2013-04-09 09:43:48 +08:00
|
|
|
gen6_gtt_pte_t pte = GEN6_PTE_VALID;
|
2012-09-25 07:44:32 +08:00
|
|
|
pte |= GEN6_PTE_ADDR_ENCODE(addr);
|
2012-10-20 00:33:22 +08:00
|
|
|
|
|
|
|
switch (level) {
|
|
|
|
case I915_CACHE_LLC_MLC:
|
2013-04-22 15:53:51 +08:00
|
|
|
pte |= GEN6_PTE_CACHE_LLC_MLC;
|
2012-10-20 00:33:22 +08:00
|
|
|
break;
|
|
|
|
case I915_CACHE_LLC:
|
|
|
|
pte |= GEN6_PTE_CACHE_LLC;
|
|
|
|
break;
|
|
|
|
case I915_CACHE_NONE:
|
2013-04-22 15:53:51 +08:00
|
|
|
pte |= GEN6_PTE_UNCACHED;
|
2012-10-20 00:33:22 +08:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
BUG();
|
|
|
|
}
|
|
|
|
|
2012-09-25 07:44:32 +08:00
|
|
|
return pte;
|
|
|
|
}
|
|
|
|
|
2013-04-22 15:53:50 +08:00
|
|
|
#define BYT_PTE_WRITEABLE (1 << 1)
|
|
|
|
#define BYT_PTE_SNOOPED_BY_CPU_CACHES (1 << 2)
|
|
|
|
|
2013-06-28 07:30:19 +08:00
|
|
|
static gen6_gtt_pte_t byt_pte_encode(dma_addr_t addr,
|
2013-04-22 15:53:50 +08:00
|
|
|
enum i915_cache_level level)
|
|
|
|
{
|
|
|
|
gen6_gtt_pte_t pte = GEN6_PTE_VALID;
|
|
|
|
pte |= GEN6_PTE_ADDR_ENCODE(addr);
|
|
|
|
|
|
|
|
/* Mark the page as writeable. Other platforms don't have a
|
|
|
|
* setting for read-only/writable, so this matches that behavior.
|
|
|
|
*/
|
|
|
|
pte |= BYT_PTE_WRITEABLE;
|
|
|
|
|
|
|
|
if (level != I915_CACHE_NONE)
|
|
|
|
pte |= BYT_PTE_SNOOPED_BY_CPU_CACHES;
|
|
|
|
|
|
|
|
return pte;
|
|
|
|
}
|
|
|
|
|
2013-06-28 07:30:19 +08:00
|
|
|
static gen6_gtt_pte_t hsw_pte_encode(dma_addr_t addr,
|
2013-04-22 15:53:51 +08:00
|
|
|
enum i915_cache_level level)
|
|
|
|
{
|
|
|
|
gen6_gtt_pte_t pte = GEN6_PTE_VALID;
|
|
|
|
pte |= GEN6_PTE_ADDR_ENCODE(addr);
|
|
|
|
|
|
|
|
if (level != I915_CACHE_NONE)
|
|
|
|
pte |= GEN6_PTE_CACHE_LLC;
|
|
|
|
|
|
|
|
return pte;
|
|
|
|
}
|
|
|
|
|
2013-04-24 14:15:32 +08:00
|
|
|
static void gen6_write_pdes(struct i915_hw_ppgtt *ppgtt)
|
2013-04-09 09:43:54 +08:00
|
|
|
{
|
2013-04-24 14:15:32 +08:00
|
|
|
struct drm_i915_private *dev_priv = ppgtt->dev->dev_private;
|
2013-04-09 09:43:54 +08:00
|
|
|
gen6_gtt_pte_t __iomem *pd_addr;
|
|
|
|
uint32_t pd_entry;
|
|
|
|
int i;
|
|
|
|
|
2013-04-24 14:15:30 +08:00
|
|
|
WARN_ON(ppgtt->pd_offset & 0x3f);
|
2013-04-09 09:43:54 +08:00
|
|
|
pd_addr = (gen6_gtt_pte_t __iomem*)dev_priv->gtt.gsm +
|
|
|
|
ppgtt->pd_offset / sizeof(gen6_gtt_pte_t);
|
|
|
|
for (i = 0; i < ppgtt->num_pd_entries; i++) {
|
|
|
|
dma_addr_t pt_addr;
|
|
|
|
|
|
|
|
pt_addr = ppgtt->pt_dma_addr[i];
|
|
|
|
pd_entry = GEN6_PDE_ADDR_ENCODE(pt_addr);
|
|
|
|
pd_entry |= GEN6_PDE_VALID;
|
|
|
|
|
|
|
|
writel(pd_entry, pd_addr + i);
|
|
|
|
}
|
|
|
|
readl(pd_addr);
|
2013-04-24 14:15:32 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static int gen6_ppgtt_enable(struct drm_device *dev)
|
|
|
|
{
|
|
|
|
drm_i915_private_t *dev_priv = dev->dev_private;
|
|
|
|
uint32_t pd_offset;
|
|
|
|
struct intel_ring_buffer *ring;
|
|
|
|
struct i915_hw_ppgtt *ppgtt = dev_priv->mm.aliasing_ppgtt;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
BUG_ON(ppgtt->pd_offset & 0x3f);
|
|
|
|
|
|
|
|
gen6_write_pdes(ppgtt);
|
2013-04-09 09:43:54 +08:00
|
|
|
|
|
|
|
pd_offset = ppgtt->pd_offset;
|
|
|
|
pd_offset /= 64; /* in cachelines, */
|
|
|
|
pd_offset <<= 16;
|
|
|
|
|
|
|
|
if (INTEL_INFO(dev)->gen == 6) {
|
|
|
|
uint32_t ecochk, gab_ctl, ecobits;
|
|
|
|
|
|
|
|
ecobits = I915_READ(GAC_ECO_BITS);
|
2013-04-04 20:13:40 +08:00
|
|
|
I915_WRITE(GAC_ECO_BITS, ecobits | ECOBITS_SNB_BIT |
|
|
|
|
ECOBITS_PPGTT_CACHE64B);
|
2013-04-09 09:43:54 +08:00
|
|
|
|
|
|
|
gab_ctl = I915_READ(GAB_CTL);
|
|
|
|
I915_WRITE(GAB_CTL, gab_ctl | GAB_CTL_CONT_AFTER_PAGEFAULT);
|
|
|
|
|
|
|
|
ecochk = I915_READ(GAM_ECOCHK);
|
|
|
|
I915_WRITE(GAM_ECOCHK, ecochk | ECOCHK_SNB_BIT |
|
|
|
|
ECOCHK_PPGTT_CACHE64B);
|
|
|
|
I915_WRITE(GFX_MODE, _MASKED_BIT_ENABLE(GFX_PPGTT_ENABLE));
|
|
|
|
} else if (INTEL_INFO(dev)->gen >= 7) {
|
2013-04-04 20:13:42 +08:00
|
|
|
uint32_t ecochk, ecobits;
|
2013-04-04 20:13:41 +08:00
|
|
|
|
|
|
|
ecobits = I915_READ(GAC_ECO_BITS);
|
|
|
|
I915_WRITE(GAC_ECO_BITS, ecobits | ECOBITS_PPGTT_CACHE64B);
|
|
|
|
|
2013-04-04 20:13:42 +08:00
|
|
|
ecochk = I915_READ(GAM_ECOCHK);
|
|
|
|
if (IS_HASWELL(dev)) {
|
|
|
|
ecochk |= ECOCHK_PPGTT_WB_HSW;
|
|
|
|
} else {
|
|
|
|
ecochk |= ECOCHK_PPGTT_LLC_IVB;
|
|
|
|
ecochk &= ~ECOCHK_PPGTT_GFDT_IVB;
|
|
|
|
}
|
|
|
|
I915_WRITE(GAM_ECOCHK, ecochk);
|
2013-04-09 09:43:54 +08:00
|
|
|
/* GFX_MODE is per-ring on gen7+ */
|
|
|
|
}
|
|
|
|
|
|
|
|
for_each_ring(ring, dev_priv, i) {
|
|
|
|
if (INTEL_INFO(dev)->gen >= 7)
|
|
|
|
I915_WRITE(RING_MODE_GEN7(ring),
|
|
|
|
_MASKED_BIT_ENABLE(GFX_PPGTT_ENABLE));
|
|
|
|
|
|
|
|
I915_WRITE(RING_PP_DIR_DCLV(ring), PP_DIR_DCLV_2G);
|
|
|
|
I915_WRITE(RING_PP_DIR_BASE(ring), pd_offset);
|
|
|
|
}
|
2013-04-09 09:43:56 +08:00
|
|
|
return 0;
|
2013-04-09 09:43:54 +08:00
|
|
|
}
|
|
|
|
|
2012-02-10 00:15:46 +08:00
|
|
|
/* PPGTT support for Sandybdrige/Gen6 and later */
|
2013-01-25 06:44:56 +08:00
|
|
|
static void gen6_ppgtt_clear_range(struct i915_hw_ppgtt *ppgtt,
|
2012-02-10 00:15:46 +08:00
|
|
|
unsigned first_entry,
|
|
|
|
unsigned num_entries)
|
|
|
|
{
|
2013-06-28 07:30:17 +08:00
|
|
|
struct drm_i915_private *dev_priv = ppgtt->dev->dev_private;
|
2013-04-09 09:43:48 +08:00
|
|
|
gen6_gtt_pte_t *pt_vaddr, scratch_pte;
|
2013-03-20 06:48:39 +08:00
|
|
|
unsigned act_pt = first_entry / I915_PPGTT_PT_ENTRIES;
|
2012-02-10 00:15:47 +08:00
|
|
|
unsigned first_pte = first_entry % I915_PPGTT_PT_ENTRIES;
|
|
|
|
unsigned last_pte, i;
|
2012-02-10 00:15:46 +08:00
|
|
|
|
2013-06-28 07:30:19 +08:00
|
|
|
scratch_pte = ppgtt->pte_encode(dev_priv->gtt.scratch.addr,
|
2013-04-22 15:53:49 +08:00
|
|
|
I915_CACHE_LLC);
|
2012-02-10 00:15:46 +08:00
|
|
|
|
2012-02-10 00:15:47 +08:00
|
|
|
while (num_entries) {
|
|
|
|
last_pte = first_pte + num_entries;
|
|
|
|
if (last_pte > I915_PPGTT_PT_ENTRIES)
|
|
|
|
last_pte = I915_PPGTT_PT_ENTRIES;
|
|
|
|
|
2013-03-20 06:48:39 +08:00
|
|
|
pt_vaddr = kmap_atomic(ppgtt->pt_pages[act_pt]);
|
2012-02-10 00:15:46 +08:00
|
|
|
|
2012-02-10 00:15:47 +08:00
|
|
|
for (i = first_pte; i < last_pte; i++)
|
|
|
|
pt_vaddr[i] = scratch_pte;
|
2012-02-10 00:15:46 +08:00
|
|
|
|
|
|
|
kunmap_atomic(pt_vaddr);
|
|
|
|
|
2012-02-10 00:15:47 +08:00
|
|
|
num_entries -= last_pte - first_pte;
|
|
|
|
first_pte = 0;
|
2013-03-20 06:48:39 +08:00
|
|
|
act_pt++;
|
2012-02-10 00:15:47 +08:00
|
|
|
}
|
2012-02-10 00:15:46 +08:00
|
|
|
}
|
|
|
|
|
2013-01-25 06:44:56 +08:00
|
|
|
static void gen6_ppgtt_insert_entries(struct i915_hw_ppgtt *ppgtt,
|
|
|
|
struct sg_table *pages,
|
|
|
|
unsigned first_entry,
|
|
|
|
enum i915_cache_level cache_level)
|
|
|
|
{
|
2013-04-09 09:43:48 +08:00
|
|
|
gen6_gtt_pte_t *pt_vaddr;
|
2013-03-20 06:48:39 +08:00
|
|
|
unsigned act_pt = first_entry / I915_PPGTT_PT_ENTRIES;
|
2013-02-19 01:28:04 +08:00
|
|
|
unsigned act_pte = first_entry % I915_PPGTT_PT_ENTRIES;
|
|
|
|
struct sg_page_iter sg_iter;
|
|
|
|
|
2013-03-20 06:48:39 +08:00
|
|
|
pt_vaddr = kmap_atomic(ppgtt->pt_pages[act_pt]);
|
2013-02-19 01:28:04 +08:00
|
|
|
for_each_sg_page(pages->sgl, &sg_iter, pages->nents, 0) {
|
|
|
|
dma_addr_t page_addr;
|
|
|
|
|
2013-03-26 21:14:18 +08:00
|
|
|
page_addr = sg_page_iter_dma_address(&sg_iter);
|
2013-06-28 07:30:19 +08:00
|
|
|
pt_vaddr[act_pte] = ppgtt->pte_encode(page_addr, cache_level);
|
2013-02-19 01:28:04 +08:00
|
|
|
if (++act_pte == I915_PPGTT_PT_ENTRIES) {
|
|
|
|
kunmap_atomic(pt_vaddr);
|
2013-03-20 06:48:39 +08:00
|
|
|
act_pt++;
|
|
|
|
pt_vaddr = kmap_atomic(ppgtt->pt_pages[act_pt]);
|
2013-02-19 01:28:04 +08:00
|
|
|
act_pte = 0;
|
2013-01-25 06:44:56 +08:00
|
|
|
|
|
|
|
}
|
|
|
|
}
|
2013-02-19 01:28:04 +08:00
|
|
|
kunmap_atomic(pt_vaddr);
|
2013-01-25 06:44:56 +08:00
|
|
|
}
|
|
|
|
|
2013-01-25 05:49:56 +08:00
|
|
|
static void gen6_ppgtt_cleanup(struct i915_hw_ppgtt *ppgtt)
|
2012-02-10 00:15:46 +08:00
|
|
|
{
|
2013-01-25 05:49:56 +08:00
|
|
|
int i;
|
|
|
|
|
|
|
|
if (ppgtt->pt_dma_addr) {
|
|
|
|
for (i = 0; i < ppgtt->num_pd_entries; i++)
|
|
|
|
pci_unmap_page(ppgtt->dev->pdev,
|
|
|
|
ppgtt->pt_dma_addr[i],
|
|
|
|
4096, PCI_DMA_BIDIRECTIONAL);
|
|
|
|
}
|
|
|
|
|
|
|
|
kfree(ppgtt->pt_dma_addr);
|
|
|
|
for (i = 0; i < ppgtt->num_pd_entries; i++)
|
|
|
|
__free_page(ppgtt->pt_pages[i]);
|
|
|
|
kfree(ppgtt->pt_pages);
|
|
|
|
kfree(ppgtt);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int gen6_ppgtt_init(struct i915_hw_ppgtt *ppgtt)
|
|
|
|
{
|
|
|
|
struct drm_device *dev = ppgtt->dev;
|
2012-02-10 00:15:46 +08:00
|
|
|
struct drm_i915_private *dev_priv = dev->dev_private;
|
|
|
|
unsigned first_pd_entry_in_global_pt;
|
|
|
|
int i;
|
|
|
|
int ret = -ENOMEM;
|
|
|
|
|
|
|
|
/* ppgtt PDEs reside in the global gtt pagetable, which has 512*1024
|
|
|
|
* entries. For aliasing ppgtt support we just steal them at the end for
|
|
|
|
* now. */
|
2013-05-21 15:52:16 +08:00
|
|
|
first_pd_entry_in_global_pt = gtt_total_entries(dev_priv->gtt);
|
2012-02-10 00:15:46 +08:00
|
|
|
|
2013-04-22 15:53:51 +08:00
|
|
|
if (IS_HASWELL(dev)) {
|
|
|
|
ppgtt->pte_encode = hsw_pte_encode;
|
|
|
|
} else if (IS_VALLEYVIEW(dev)) {
|
2013-04-22 15:53:50 +08:00
|
|
|
ppgtt->pte_encode = byt_pte_encode;
|
|
|
|
} else {
|
|
|
|
ppgtt->pte_encode = gen6_pte_encode;
|
|
|
|
}
|
2013-06-28 07:30:04 +08:00
|
|
|
ppgtt->num_pd_entries = GEN6_PPGTT_PD_ENTRIES;
|
2013-04-09 09:43:54 +08:00
|
|
|
ppgtt->enable = gen6_ppgtt_enable;
|
2013-01-25 06:44:56 +08:00
|
|
|
ppgtt->clear_range = gen6_ppgtt_clear_range;
|
|
|
|
ppgtt->insert_entries = gen6_ppgtt_insert_entries;
|
2013-01-25 05:49:56 +08:00
|
|
|
ppgtt->cleanup = gen6_ppgtt_cleanup;
|
2012-02-10 00:15:46 +08:00
|
|
|
ppgtt->pt_pages = kzalloc(sizeof(struct page *)*ppgtt->num_pd_entries,
|
|
|
|
GFP_KERNEL);
|
|
|
|
if (!ppgtt->pt_pages)
|
2013-01-25 05:49:56 +08:00
|
|
|
return -ENOMEM;
|
2012-02-10 00:15:46 +08:00
|
|
|
|
|
|
|
for (i = 0; i < ppgtt->num_pd_entries; i++) {
|
|
|
|
ppgtt->pt_pages[i] = alloc_page(GFP_KERNEL);
|
|
|
|
if (!ppgtt->pt_pages[i])
|
|
|
|
goto err_pt_alloc;
|
|
|
|
}
|
|
|
|
|
2013-01-19 04:30:33 +08:00
|
|
|
ppgtt->pt_dma_addr = kzalloc(sizeof(dma_addr_t) *ppgtt->num_pd_entries,
|
|
|
|
GFP_KERNEL);
|
|
|
|
if (!ppgtt->pt_dma_addr)
|
|
|
|
goto err_pt_alloc;
|
2012-02-10 00:15:46 +08:00
|
|
|
|
2013-01-19 04:30:33 +08:00
|
|
|
for (i = 0; i < ppgtt->num_pd_entries; i++) {
|
|
|
|
dma_addr_t pt_addr;
|
2012-04-10 23:29:17 +08:00
|
|
|
|
2013-01-19 04:30:33 +08:00
|
|
|
pt_addr = pci_map_page(dev->pdev, ppgtt->pt_pages[i], 0, 4096,
|
|
|
|
PCI_DMA_BIDIRECTIONAL);
|
2012-02-10 00:15:46 +08:00
|
|
|
|
2013-01-19 04:30:33 +08:00
|
|
|
if (pci_dma_mapping_error(dev->pdev, pt_addr)) {
|
|
|
|
ret = -EIO;
|
|
|
|
goto err_pd_pin;
|
2012-02-10 00:15:46 +08:00
|
|
|
|
2012-04-10 23:29:17 +08:00
|
|
|
}
|
2013-01-19 04:30:33 +08:00
|
|
|
ppgtt->pt_dma_addr[i] = pt_addr;
|
2012-02-10 00:15:46 +08:00
|
|
|
}
|
|
|
|
|
2013-01-25 06:44:56 +08:00
|
|
|
ppgtt->clear_range(ppgtt, 0,
|
|
|
|
ppgtt->num_pd_entries*I915_PPGTT_PT_ENTRIES);
|
2012-02-10 00:15:46 +08:00
|
|
|
|
2013-04-09 09:43:48 +08:00
|
|
|
ppgtt->pd_offset = first_pd_entry_in_global_pt * sizeof(gen6_gtt_pte_t);
|
2012-02-10 00:15:46 +08:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
err_pd_pin:
|
|
|
|
if (ppgtt->pt_dma_addr) {
|
|
|
|
for (i--; i >= 0; i--)
|
|
|
|
pci_unmap_page(dev->pdev, ppgtt->pt_dma_addr[i],
|
|
|
|
4096, PCI_DMA_BIDIRECTIONAL);
|
|
|
|
}
|
|
|
|
err_pt_alloc:
|
|
|
|
kfree(ppgtt->pt_dma_addr);
|
|
|
|
for (i = 0; i < ppgtt->num_pd_entries; i++) {
|
|
|
|
if (ppgtt->pt_pages[i])
|
|
|
|
__free_page(ppgtt->pt_pages[i]);
|
|
|
|
}
|
|
|
|
kfree(ppgtt->pt_pages);
|
2013-01-25 05:49:56 +08:00
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int i915_gem_init_aliasing_ppgtt(struct drm_device *dev)
|
|
|
|
{
|
|
|
|
struct drm_i915_private *dev_priv = dev->dev_private;
|
|
|
|
struct i915_hw_ppgtt *ppgtt;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
ppgtt = kzalloc(sizeof(*ppgtt), GFP_KERNEL);
|
|
|
|
if (!ppgtt)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
ppgtt->dev = dev;
|
|
|
|
|
2013-04-09 09:43:53 +08:00
|
|
|
if (INTEL_INFO(dev)->gen < 8)
|
|
|
|
ret = gen6_ppgtt_init(ppgtt);
|
|
|
|
else
|
|
|
|
BUG();
|
|
|
|
|
2013-01-25 05:49:56 +08:00
|
|
|
if (ret)
|
|
|
|
kfree(ppgtt);
|
|
|
|
else
|
|
|
|
dev_priv->mm.aliasing_ppgtt = ppgtt;
|
2012-02-10 00:15:46 +08:00
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
void i915_gem_cleanup_aliasing_ppgtt(struct drm_device *dev)
|
|
|
|
{
|
|
|
|
struct drm_i915_private *dev_priv = dev->dev_private;
|
|
|
|
struct i915_hw_ppgtt *ppgtt = dev_priv->mm.aliasing_ppgtt;
|
|
|
|
|
|
|
|
if (!ppgtt)
|
|
|
|
return;
|
|
|
|
|
2013-01-25 05:49:56 +08:00
|
|
|
ppgtt->cleanup(ppgtt);
|
2013-04-09 09:43:55 +08:00
|
|
|
dev_priv->mm.aliasing_ppgtt = NULL;
|
2012-02-10 00:15:46 +08:00
|
|
|
}
|
|
|
|
|
2012-02-10 00:15:47 +08:00
|
|
|
void i915_ppgtt_bind_object(struct i915_hw_ppgtt *ppgtt,
|
|
|
|
struct drm_i915_gem_object *obj,
|
|
|
|
enum i915_cache_level cache_level)
|
|
|
|
{
|
2013-01-25 06:44:56 +08:00
|
|
|
ppgtt->insert_entries(ppgtt, obj->pages,
|
2013-07-06 05:41:04 +08:00
|
|
|
i915_gem_obj_ggtt_offset(obj) >> PAGE_SHIFT,
|
2013-01-25 06:44:56 +08:00
|
|
|
cache_level);
|
2012-02-10 00:15:47 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
void i915_ppgtt_unbind_object(struct i915_hw_ppgtt *ppgtt,
|
|
|
|
struct drm_i915_gem_object *obj)
|
|
|
|
{
|
2013-01-25 06:44:56 +08:00
|
|
|
ppgtt->clear_range(ppgtt,
|
2013-07-06 05:41:04 +08:00
|
|
|
i915_gem_obj_ggtt_offset(obj) >> PAGE_SHIFT,
|
2013-01-25 06:44:56 +08:00
|
|
|
obj->base.size >> PAGE_SHIFT);
|
2012-02-10 00:15:47 +08:00
|
|
|
}
|
|
|
|
|
2013-01-19 04:30:31 +08:00
|
|
|
extern int intel_iommu_gfx_mapped;
|
|
|
|
/* Certain Gen5 chipsets require require idling the GPU before
|
|
|
|
* unmapping anything from the GTT when VT-d is enabled.
|
|
|
|
*/
|
|
|
|
static inline bool needs_idle_maps(struct drm_device *dev)
|
|
|
|
{
|
|
|
|
#ifdef CONFIG_INTEL_IOMMU
|
|
|
|
/* Query intel_iommu to see if we need the workaround. Presumably that
|
|
|
|
* was loaded first.
|
|
|
|
*/
|
|
|
|
if (IS_GEN5(dev) && IS_MOBILE(dev) && intel_iommu_gfx_mapped)
|
|
|
|
return true;
|
|
|
|
#endif
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2011-10-18 06:51:55 +08:00
|
|
|
static bool do_idling(struct drm_i915_private *dev_priv)
|
|
|
|
{
|
|
|
|
bool ret = dev_priv->mm.interruptible;
|
|
|
|
|
2013-01-19 04:30:31 +08:00
|
|
|
if (unlikely(dev_priv->gtt.do_idle_maps)) {
|
2011-10-18 06:51:55 +08:00
|
|
|
dev_priv->mm.interruptible = false;
|
2012-04-27 07:02:58 +08:00
|
|
|
if (i915_gpu_idle(dev_priv->dev)) {
|
2011-10-18 06:51:55 +08:00
|
|
|
DRM_ERROR("Couldn't idle GPU\n");
|
|
|
|
/* Wait a bit, in hopes it avoids the hang */
|
|
|
|
udelay(10);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void undo_idling(struct drm_i915_private *dev_priv, bool interruptible)
|
|
|
|
{
|
2013-01-19 04:30:31 +08:00
|
|
|
if (unlikely(dev_priv->gtt.do_idle_maps))
|
2011-10-18 06:51:55 +08:00
|
|
|
dev_priv->mm.interruptible = interruptible;
|
|
|
|
}
|
|
|
|
|
2010-11-06 05:23:30 +08:00
|
|
|
void i915_gem_restore_gtt_mappings(struct drm_device *dev)
|
|
|
|
{
|
|
|
|
struct drm_i915_private *dev_priv = dev->dev_private;
|
2010-11-09 03:18:58 +08:00
|
|
|
struct drm_i915_gem_object *obj;
|
2010-11-06 05:23:30 +08:00
|
|
|
|
2011-01-21 18:54:32 +08:00
|
|
|
/* First fill our portion of the GTT with scratch pages */
|
2013-01-25 06:44:55 +08:00
|
|
|
dev_priv->gtt.gtt_clear_range(dev, dev_priv->gtt.start / PAGE_SIZE,
|
|
|
|
dev_priv->gtt.total / PAGE_SIZE);
|
2011-01-21 18:54:32 +08:00
|
|
|
|
2013-06-01 02:28:48 +08:00
|
|
|
list_for_each_entry(obj, &dev_priv->mm.bound_list, global_list) {
|
2010-12-08 22:28:54 +08:00
|
|
|
i915_gem_clflush_object(obj);
|
2012-02-16 06:50:21 +08:00
|
|
|
i915_gem_gtt_bind_object(obj, obj->cache_level);
|
2010-11-06 05:23:30 +08:00
|
|
|
}
|
|
|
|
|
2012-11-05 01:21:27 +08:00
|
|
|
i915_gem_chipset_flush(dev);
|
2010-11-06 05:23:30 +08:00
|
|
|
}
|
2010-11-06 17:10:47 +08:00
|
|
|
|
2012-02-16 06:50:21 +08:00
|
|
|
int i915_gem_gtt_prepare_object(struct drm_i915_gem_object *obj)
|
2010-11-06 17:10:47 +08:00
|
|
|
{
|
2012-06-01 22:20:22 +08:00
|
|
|
if (obj->has_dma_mapping)
|
2012-02-16 06:50:21 +08:00
|
|
|
return 0;
|
2012-06-01 22:20:22 +08:00
|
|
|
|
|
|
|
if (!dma_map_sg(&obj->base.dev->pdev->dev,
|
|
|
|
obj->pages->sgl, obj->pages->nents,
|
|
|
|
PCI_DMA_BIDIRECTIONAL))
|
|
|
|
return -ENOSPC;
|
|
|
|
|
|
|
|
return 0;
|
2010-11-06 17:10:47 +08:00
|
|
|
}
|
|
|
|
|
2012-11-05 01:21:27 +08:00
|
|
|
/*
|
|
|
|
* Binds an object into the global gtt with the specified cache level. The object
|
|
|
|
* will be accessible to the GPU via commands whose operands reference offsets
|
|
|
|
* within the global GTT as well as accessible by the GPU through the GMADR
|
|
|
|
* mapped BAR (dev_priv->mm.gtt->gtt).
|
|
|
|
*/
|
2013-01-25 06:44:55 +08:00
|
|
|
static void gen6_ggtt_insert_entries(struct drm_device *dev,
|
|
|
|
struct sg_table *st,
|
|
|
|
unsigned int first_entry,
|
|
|
|
enum i915_cache_level level)
|
2012-11-05 01:21:27 +08:00
|
|
|
{
|
|
|
|
struct drm_i915_private *dev_priv = dev->dev_private;
|
2013-04-09 09:43:48 +08:00
|
|
|
gen6_gtt_pte_t __iomem *gtt_entries =
|
|
|
|
(gen6_gtt_pte_t __iomem *)dev_priv->gtt.gsm + first_entry;
|
2013-02-19 01:28:04 +08:00
|
|
|
int i = 0;
|
|
|
|
struct sg_page_iter sg_iter;
|
2012-11-05 01:21:27 +08:00
|
|
|
dma_addr_t addr;
|
|
|
|
|
2013-02-19 01:28:04 +08:00
|
|
|
for_each_sg_page(st->sgl, &sg_iter, st->nents, 0) {
|
2013-03-26 21:14:18 +08:00
|
|
|
addr = sg_page_iter_dma_address(&sg_iter);
|
2013-06-28 07:30:19 +08:00
|
|
|
iowrite32(dev_priv->gtt.pte_encode(addr, level),
|
2013-04-22 15:53:49 +08:00
|
|
|
>t_entries[i]);
|
2013-02-19 01:28:04 +08:00
|
|
|
i++;
|
2012-11-05 01:21:27 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/* XXX: This serves as a posting read to make sure that the PTE has
|
|
|
|
* actually been updated. There is some concern that even though
|
|
|
|
* registers and PTEs are within the same BAR that they are potentially
|
|
|
|
* of NUMA access patterns. Therefore, even with the way we assume
|
|
|
|
* hardware should work, we must keep this posting read for paranoia.
|
|
|
|
*/
|
|
|
|
if (i != 0)
|
2013-01-25 06:44:57 +08:00
|
|
|
WARN_ON(readl(>t_entries[i-1])
|
2013-06-28 07:30:19 +08:00
|
|
|
!= dev_priv->gtt.pte_encode(addr, level));
|
2012-11-05 01:21:30 +08:00
|
|
|
|
|
|
|
/* This next bit makes the above posting read even more important. We
|
|
|
|
* want to flush the TLBs only after we're certain all the PTE updates
|
|
|
|
* have finished.
|
|
|
|
*/
|
|
|
|
I915_WRITE(GFX_FLSH_CNTL_GEN6, GFX_FLSH_CNTL_EN);
|
|
|
|
POSTING_READ(GFX_FLSH_CNTL_GEN6);
|
2012-11-05 01:21:27 +08:00
|
|
|
}
|
|
|
|
|
2013-01-25 06:44:55 +08:00
|
|
|
static void gen6_ggtt_clear_range(struct drm_device *dev,
|
|
|
|
unsigned int first_entry,
|
|
|
|
unsigned int num_entries)
|
|
|
|
{
|
|
|
|
struct drm_i915_private *dev_priv = dev->dev_private;
|
2013-04-09 09:43:48 +08:00
|
|
|
gen6_gtt_pte_t scratch_pte, __iomem *gtt_base =
|
|
|
|
(gen6_gtt_pte_t __iomem *) dev_priv->gtt.gsm + first_entry;
|
2013-01-25 06:45:00 +08:00
|
|
|
const int max_entries = gtt_total_entries(dev_priv->gtt) - first_entry;
|
2013-01-25 06:44:55 +08:00
|
|
|
int i;
|
|
|
|
|
|
|
|
if (WARN(num_entries > max_entries,
|
|
|
|
"First entry = %d; Num entries = %d (max=%d)\n",
|
|
|
|
first_entry, num_entries, max_entries))
|
|
|
|
num_entries = max_entries;
|
|
|
|
|
2013-06-28 07:30:19 +08:00
|
|
|
scratch_pte = dev_priv->gtt.pte_encode(dev_priv->gtt.scratch.addr,
|
2013-04-22 15:53:49 +08:00
|
|
|
I915_CACHE_LLC);
|
2013-01-25 06:44:55 +08:00
|
|
|
for (i = 0; i < num_entries; i++)
|
|
|
|
iowrite32(scratch_pte, >t_base[i]);
|
|
|
|
readl(gtt_base);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void i915_ggtt_insert_entries(struct drm_device *dev,
|
|
|
|
struct sg_table *st,
|
|
|
|
unsigned int pg_start,
|
|
|
|
enum i915_cache_level cache_level)
|
|
|
|
{
|
|
|
|
unsigned int flags = (cache_level == I915_CACHE_NONE) ?
|
|
|
|
AGP_USER_MEMORY : AGP_USER_CACHED_MEMORY;
|
|
|
|
|
|
|
|
intel_gtt_insert_sg_entries(st, pg_start, flags);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
static void i915_ggtt_clear_range(struct drm_device *dev,
|
|
|
|
unsigned int first_entry,
|
|
|
|
unsigned int num_entries)
|
|
|
|
{
|
|
|
|
intel_gtt_clear_range(first_entry, num_entries);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2012-02-16 06:50:21 +08:00
|
|
|
void i915_gem_gtt_bind_object(struct drm_i915_gem_object *obj,
|
|
|
|
enum i915_cache_level cache_level)
|
2011-04-14 13:48:26 +08:00
|
|
|
{
|
|
|
|
struct drm_device *dev = obj->base.dev;
|
2013-01-25 06:44:55 +08:00
|
|
|
struct drm_i915_private *dev_priv = dev->dev_private;
|
|
|
|
|
|
|
|
dev_priv->gtt.gtt_insert_entries(dev, obj->pages,
|
2013-07-06 05:41:04 +08:00
|
|
|
i915_gem_obj_ggtt_offset(obj) >> PAGE_SHIFT,
|
2013-01-25 06:44:55 +08:00
|
|
|
cache_level);
|
2011-04-14 13:48:26 +08:00
|
|
|
|
2012-02-16 06:50:22 +08:00
|
|
|
obj->has_global_gtt_mapping = 1;
|
2011-04-14 13:48:26 +08:00
|
|
|
}
|
|
|
|
|
2010-11-09 03:18:58 +08:00
|
|
|
void i915_gem_gtt_unbind_object(struct drm_i915_gem_object *obj)
|
2012-02-16 06:50:21 +08:00
|
|
|
{
|
2013-01-25 06:44:55 +08:00
|
|
|
struct drm_device *dev = obj->base.dev;
|
|
|
|
struct drm_i915_private *dev_priv = dev->dev_private;
|
|
|
|
|
|
|
|
dev_priv->gtt.gtt_clear_range(obj->base.dev,
|
2013-07-06 05:41:04 +08:00
|
|
|
i915_gem_obj_ggtt_offset(obj) >> PAGE_SHIFT,
|
2013-01-25 06:44:55 +08:00
|
|
|
obj->base.size >> PAGE_SHIFT);
|
2012-02-16 06:50:22 +08:00
|
|
|
|
|
|
|
obj->has_global_gtt_mapping = 0;
|
2012-02-16 06:50:21 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
void i915_gem_gtt_finish_object(struct drm_i915_gem_object *obj)
|
2010-11-06 17:10:47 +08:00
|
|
|
{
|
2011-10-18 06:51:55 +08:00
|
|
|
struct drm_device *dev = obj->base.dev;
|
|
|
|
struct drm_i915_private *dev_priv = dev->dev_private;
|
|
|
|
bool interruptible;
|
|
|
|
|
|
|
|
interruptible = do_idling(dev_priv);
|
|
|
|
|
2012-06-01 22:20:22 +08:00
|
|
|
if (!obj->has_dma_mapping)
|
|
|
|
dma_unmap_sg(&dev->pdev->dev,
|
|
|
|
obj->pages->sgl, obj->pages->nents,
|
|
|
|
PCI_DMA_BIDIRECTIONAL);
|
2011-10-18 06:51:55 +08:00
|
|
|
|
|
|
|
undo_idling(dev_priv, interruptible);
|
2010-11-06 17:10:47 +08:00
|
|
|
}
|
2012-03-26 15:45:40 +08:00
|
|
|
|
2012-07-26 18:49:32 +08:00
|
|
|
static void i915_gtt_color_adjust(struct drm_mm_node *node,
|
|
|
|
unsigned long color,
|
|
|
|
unsigned long *start,
|
|
|
|
unsigned long *end)
|
|
|
|
{
|
|
|
|
if (node->color != color)
|
|
|
|
*start += 4096;
|
|
|
|
|
|
|
|
if (!list_empty(&node->node_list)) {
|
|
|
|
node = list_entry(node->node_list.next,
|
|
|
|
struct drm_mm_node,
|
|
|
|
node_list);
|
|
|
|
if (node->allocated && node->color != color)
|
|
|
|
*end -= 4096;
|
|
|
|
}
|
|
|
|
}
|
2012-12-19 02:31:25 +08:00
|
|
|
void i915_gem_setup_global_gtt(struct drm_device *dev,
|
|
|
|
unsigned long start,
|
|
|
|
unsigned long mappable_end,
|
|
|
|
unsigned long end)
|
2012-03-26 15:45:40 +08:00
|
|
|
{
|
2013-01-26 08:41:04 +08:00
|
|
|
/* Let GEM Manage all of the aperture.
|
|
|
|
*
|
|
|
|
* However, leave one page at the end still bound to the scratch page.
|
|
|
|
* There are a number of places where the hardware apparently prefetches
|
|
|
|
* past the end of the object, and we've seen multiple hangs with the
|
|
|
|
* GPU head pointer stuck in a batchbuffer bound at the last page of the
|
|
|
|
* aperture. One page should be enough to keep any prefetching inside
|
|
|
|
* of the aperture.
|
|
|
|
*/
|
2012-03-26 15:45:40 +08:00
|
|
|
drm_i915_private_t *dev_priv = dev->dev_private;
|
2012-11-15 19:32:19 +08:00
|
|
|
struct drm_mm_node *entry;
|
|
|
|
struct drm_i915_gem_object *obj;
|
|
|
|
unsigned long hole_start, hole_end;
|
2012-03-26 15:45:40 +08:00
|
|
|
|
2013-01-18 04:45:13 +08:00
|
|
|
BUG_ON(mappable_end > end);
|
|
|
|
|
2012-11-15 19:32:19 +08:00
|
|
|
/* Subtract the guard page ... */
|
2012-03-26 15:45:42 +08:00
|
|
|
drm_mm_init(&dev_priv->mm.gtt_space, start, end - start - PAGE_SIZE);
|
2012-07-26 18:49:32 +08:00
|
|
|
if (!HAS_LLC(dev))
|
|
|
|
dev_priv->mm.gtt_space.color_adjust = i915_gtt_color_adjust;
|
2012-03-26 15:45:40 +08:00
|
|
|
|
2012-11-15 19:32:19 +08:00
|
|
|
/* Mark any preallocated objects as occupied */
|
2013-06-01 02:28:48 +08:00
|
|
|
list_for_each_entry(obj, &dev_priv->mm.bound_list, global_list) {
|
2013-07-06 05:41:02 +08:00
|
|
|
int ret;
|
2012-11-15 19:32:19 +08:00
|
|
|
DRM_DEBUG_KMS("reserving preallocated space: %x + %zx\n",
|
|
|
|
obj->gtt_offset, obj->base.size);
|
|
|
|
|
|
|
|
BUG_ON(obj->gtt_space != I915_GTT_RESERVED);
|
2013-07-06 05:41:02 +08:00
|
|
|
obj->gtt_space = kzalloc(sizeof(*obj->gtt_space), GFP_KERNEL);
|
|
|
|
if (!obj->gtt_space) {
|
|
|
|
DRM_ERROR("Failed to preserve object at offset %x\n",
|
|
|
|
obj->gtt_offset);
|
|
|
|
continue;
|
|
|
|
}
|
2013-07-06 05:41:03 +08:00
|
|
|
obj->gtt_space->start = obj->gtt_offset;
|
|
|
|
obj->gtt_space->size = obj->base.size;
|
|
|
|
ret = drm_mm_reserve_node(&dev_priv->mm.gtt_space,
|
|
|
|
obj->gtt_space);
|
2013-07-06 05:41:02 +08:00
|
|
|
if (ret) {
|
|
|
|
DRM_DEBUG_KMS("Reservation failed\n");
|
|
|
|
kfree(obj->gtt_space);
|
|
|
|
obj->gtt_space = NULL;
|
|
|
|
}
|
2012-11-15 19:32:19 +08:00
|
|
|
obj->has_global_gtt_mapping = 1;
|
|
|
|
}
|
|
|
|
|
2013-01-18 04:45:15 +08:00
|
|
|
dev_priv->gtt.start = start;
|
|
|
|
dev_priv->gtt.total = end - start;
|
2012-03-26 15:45:40 +08:00
|
|
|
|
2012-11-15 19:32:19 +08:00
|
|
|
/* Clear any non-preallocated blocks */
|
|
|
|
drm_mm_for_each_hole(entry, &dev_priv->mm.gtt_space,
|
|
|
|
hole_start, hole_end) {
|
|
|
|
DRM_DEBUG_KMS("clearing unused GTT space: [%lx, %lx]\n",
|
|
|
|
hole_start, hole_end);
|
2013-01-25 06:44:55 +08:00
|
|
|
dev_priv->gtt.gtt_clear_range(dev, hole_start / PAGE_SIZE,
|
|
|
|
(hole_end-hole_start) / PAGE_SIZE);
|
2012-11-15 19:32:19 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/* And finally clear the reserved guard page */
|
2013-01-25 06:44:55 +08:00
|
|
|
dev_priv->gtt.gtt_clear_range(dev, end / PAGE_SIZE - 1, 1);
|
2012-11-05 01:21:27 +08:00
|
|
|
}
|
|
|
|
|
2012-12-19 02:31:25 +08:00
|
|
|
static bool
|
|
|
|
intel_enable_ppgtt(struct drm_device *dev)
|
|
|
|
{
|
|
|
|
if (i915_enable_ppgtt >= 0)
|
|
|
|
return i915_enable_ppgtt;
|
|
|
|
|
|
|
|
#ifdef CONFIG_INTEL_IOMMU
|
|
|
|
/* Disable ppgtt on SNB if VT-d is on. */
|
|
|
|
if (INTEL_INFO(dev)->gen == 6 && intel_iommu_gfx_mapped)
|
|
|
|
return false;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void i915_gem_init_global_gtt(struct drm_device *dev)
|
|
|
|
{
|
|
|
|
struct drm_i915_private *dev_priv = dev->dev_private;
|
|
|
|
unsigned long gtt_size, mappable_size;
|
|
|
|
|
2013-01-25 06:45:00 +08:00
|
|
|
gtt_size = dev_priv->gtt.total;
|
2013-01-18 04:45:17 +08:00
|
|
|
mappable_size = dev_priv->gtt.mappable_end;
|
2012-12-19 02:31:25 +08:00
|
|
|
|
|
|
|
if (intel_enable_ppgtt(dev) && HAS_ALIASING_PPGTT(dev)) {
|
2013-01-26 08:41:04 +08:00
|
|
|
int ret;
|
2013-04-09 09:43:52 +08:00
|
|
|
|
|
|
|
if (INTEL_INFO(dev)->gen <= 7) {
|
|
|
|
/* PPGTT pdes are stolen from global gtt ptes, so shrink the
|
|
|
|
* aperture accordingly when using aliasing ppgtt. */
|
2013-06-28 07:30:04 +08:00
|
|
|
gtt_size -= GEN6_PPGTT_PD_ENTRIES * PAGE_SIZE;
|
2013-04-09 09:43:52 +08:00
|
|
|
}
|
2012-12-19 02:31:25 +08:00
|
|
|
|
|
|
|
i915_gem_setup_global_gtt(dev, 0, mappable_size, gtt_size);
|
|
|
|
|
|
|
|
ret = i915_gem_init_aliasing_ppgtt(dev);
|
2013-01-26 08:41:04 +08:00
|
|
|
if (!ret)
|
2012-12-19 02:31:25 +08:00
|
|
|
return;
|
2013-01-26 08:41:04 +08:00
|
|
|
|
|
|
|
DRM_ERROR("Aliased PPGTT setup failed %d\n", ret);
|
|
|
|
drm_mm_takedown(&dev_priv->mm.gtt_space);
|
2013-06-28 07:30:04 +08:00
|
|
|
gtt_size += GEN6_PPGTT_PD_ENTRIES * PAGE_SIZE;
|
2012-12-19 02:31:25 +08:00
|
|
|
}
|
2013-01-26 08:41:04 +08:00
|
|
|
i915_gem_setup_global_gtt(dev, 0, mappable_size, gtt_size);
|
2012-11-05 01:21:27 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static int setup_scratch_page(struct drm_device *dev)
|
|
|
|
{
|
|
|
|
struct drm_i915_private *dev_priv = dev->dev_private;
|
|
|
|
struct page *page;
|
|
|
|
dma_addr_t dma_addr;
|
|
|
|
|
|
|
|
page = alloc_page(GFP_KERNEL | GFP_DMA32 | __GFP_ZERO);
|
|
|
|
if (page == NULL)
|
|
|
|
return -ENOMEM;
|
|
|
|
get_page(page);
|
|
|
|
set_pages_uc(page, 1);
|
|
|
|
|
|
|
|
#ifdef CONFIG_INTEL_IOMMU
|
|
|
|
dma_addr = pci_map_page(dev->pdev, page, 0, PAGE_SIZE,
|
|
|
|
PCI_DMA_BIDIRECTIONAL);
|
|
|
|
if (pci_dma_mapping_error(dev->pdev, dma_addr))
|
|
|
|
return -EINVAL;
|
|
|
|
#else
|
|
|
|
dma_addr = page_to_phys(page);
|
|
|
|
#endif
|
2013-06-28 07:30:18 +08:00
|
|
|
dev_priv->gtt.scratch.page = page;
|
|
|
|
dev_priv->gtt.scratch.addr = dma_addr;
|
2012-11-05 01:21:27 +08:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void teardown_scratch_page(struct drm_device *dev)
|
|
|
|
{
|
|
|
|
struct drm_i915_private *dev_priv = dev->dev_private;
|
2013-06-28 07:30:18 +08:00
|
|
|
set_pages_wb(dev_priv->gtt.scratch.page, 1);
|
|
|
|
pci_unmap_page(dev->pdev, dev_priv->gtt.scratch.addr,
|
2012-11-05 01:21:27 +08:00
|
|
|
PAGE_SIZE, PCI_DMA_BIDIRECTIONAL);
|
2013-06-28 07:30:18 +08:00
|
|
|
put_page(dev_priv->gtt.scratch.page);
|
|
|
|
__free_page(dev_priv->gtt.scratch.page);
|
2012-11-05 01:21:27 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static inline unsigned int gen6_get_total_gtt_size(u16 snb_gmch_ctl)
|
|
|
|
{
|
|
|
|
snb_gmch_ctl >>= SNB_GMCH_GGMS_SHIFT;
|
|
|
|
snb_gmch_ctl &= SNB_GMCH_GGMS_MASK;
|
|
|
|
return snb_gmch_ctl << 20;
|
|
|
|
}
|
|
|
|
|
2013-01-25 05:49:57 +08:00
|
|
|
static inline size_t gen6_get_stolen_size(u16 snb_gmch_ctl)
|
2012-11-05 01:21:27 +08:00
|
|
|
{
|
|
|
|
snb_gmch_ctl >>= SNB_GMCH_GMS_SHIFT;
|
|
|
|
snb_gmch_ctl &= SNB_GMCH_GMS_MASK;
|
|
|
|
return snb_gmch_ctl << 25; /* 32 MB units */
|
|
|
|
}
|
|
|
|
|
2013-01-25 05:49:57 +08:00
|
|
|
static int gen6_gmch_probe(struct drm_device *dev,
|
|
|
|
size_t *gtt_total,
|
2013-02-09 03:32:47 +08:00
|
|
|
size_t *stolen,
|
|
|
|
phys_addr_t *mappable_base,
|
|
|
|
unsigned long *mappable_end)
|
2012-11-05 01:21:27 +08:00
|
|
|
{
|
|
|
|
struct drm_i915_private *dev_priv = dev->dev_private;
|
|
|
|
phys_addr_t gtt_bus_addr;
|
2013-01-25 05:49:57 +08:00
|
|
|
unsigned int gtt_size;
|
2012-11-05 01:21:27 +08:00
|
|
|
u16 snb_gmch_ctl;
|
|
|
|
int ret;
|
|
|
|
|
2013-02-09 03:32:47 +08:00
|
|
|
*mappable_base = pci_resource_start(dev->pdev, 2);
|
|
|
|
*mappable_end = pci_resource_len(dev->pdev, 2);
|
|
|
|
|
2013-01-25 05:49:57 +08:00
|
|
|
/* 64/512MB is the current min/max we actually know of, but this is just
|
|
|
|
* a coarse sanity check.
|
2012-11-05 01:21:27 +08:00
|
|
|
*/
|
2013-02-09 03:32:47 +08:00
|
|
|
if ((*mappable_end < (64<<20) || (*mappable_end > (512<<20)))) {
|
2013-01-25 05:49:57 +08:00
|
|
|
DRM_ERROR("Unknown GMADR size (%lx)\n",
|
|
|
|
dev_priv->gtt.mappable_end);
|
|
|
|
return -ENXIO;
|
2012-11-05 01:21:27 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
if (!pci_set_dma_mask(dev->pdev, DMA_BIT_MASK(40)))
|
|
|
|
pci_set_consistent_dma_mask(dev->pdev, DMA_BIT_MASK(40));
|
|
|
|
pci_read_config_word(dev->pdev, SNB_GMCH_CTRL, &snb_gmch_ctl);
|
2013-01-25 05:49:57 +08:00
|
|
|
gtt_size = gen6_get_total_gtt_size(snb_gmch_ctl);
|
2012-11-05 01:21:27 +08:00
|
|
|
|
Revert "drm/i915: Calculate correct stolen size for GEN7+"
This reverts commit 03752f5b7b77b95d83479885040950fba1250850.
This revert requires a bit of explanation on how I understand things
work. Internally the architects/designers decide how the stolen encoding
works. We put it in a doc. BIOS writers take these docs and implement
it. Driver writers read the doc too, and read the value left by the BIOS
writers, and then we make magic.
The failing here is that in the docs we had[1] contained two different
definitions for this register for Gen7. (We have both a PCI register,
and an MMIO, and each of these were different). At the time [2] of
03752f5, we asked the architects what the correct value should be; but
that doesn't match the reality (BIOS) unfortunately.
So on all machines I can get my hands on, this revert is the right thing
to do. I've also worked with the product group to confirm that they
agree this revert is what we should do. People using HW made my "people"
who both write their own BIOS, and have access to our docs (Apple?).
Investigations are still ongoing about whether we need to add a list
of machines needing special handling, but this patch should be the
right thing for pretty much everyone.
[1] The docs are still wrong on this one. Now instead of two registers with
two definitions, we have one register with BOTH definitions, progress?
[2] The open source PRMs have the "wrong" definitions in chapter Volume
1 part6, section 1.1.12.
This digging was inspired by Paulo.
Cc: Paulo Zanoni <przanoni@gmail.com>
Signed-off-by: Ben Widawsky <ben@bwidawsk.net>
Acked-by: Jesse Barnes <jbarnes@virtuousgeek.org>
[danvet: Augment the patch saying that it's still a bit unclear
whether there are any machines out there with "wrong" firmware and
whether we need to add a list to handle them specially.]
Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch>
2013-05-02 02:00:34 +08:00
|
|
|
*stolen = gen6_get_stolen_size(snb_gmch_ctl);
|
2013-04-09 09:43:48 +08:00
|
|
|
*gtt_total = (gtt_size / sizeof(gen6_gtt_pte_t)) << PAGE_SHIFT;
|
2012-11-05 01:21:27 +08:00
|
|
|
|
2013-04-09 09:43:47 +08:00
|
|
|
/* For Modern GENs the PTEs and register space are split in the BAR */
|
|
|
|
gtt_bus_addr = pci_resource_start(dev->pdev, 0) +
|
|
|
|
(pci_resource_len(dev->pdev, 0) / 2);
|
|
|
|
|
2013-01-25 05:49:57 +08:00
|
|
|
dev_priv->gtt.gsm = ioremap_wc(gtt_bus_addr, gtt_size);
|
2013-01-18 04:45:15 +08:00
|
|
|
if (!dev_priv->gtt.gsm) {
|
2012-11-05 01:21:27 +08:00
|
|
|
DRM_ERROR("Failed to map the gtt page table\n");
|
2013-01-25 05:49:57 +08:00
|
|
|
return -ENOMEM;
|
2012-11-05 01:21:27 +08:00
|
|
|
}
|
|
|
|
|
2013-01-25 05:49:57 +08:00
|
|
|
ret = setup_scratch_page(dev);
|
|
|
|
if (ret)
|
|
|
|
DRM_ERROR("Scratch setup failed\n");
|
2012-11-05 01:21:27 +08:00
|
|
|
|
2013-01-25 06:44:55 +08:00
|
|
|
dev_priv->gtt.gtt_clear_range = gen6_ggtt_clear_range;
|
|
|
|
dev_priv->gtt.gtt_insert_entries = gen6_ggtt_insert_entries;
|
|
|
|
|
2012-11-05 01:21:27 +08:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2013-01-31 11:32:50 +08:00
|
|
|
static void gen6_gmch_remove(struct drm_device *dev)
|
2012-11-05 01:21:27 +08:00
|
|
|
{
|
|
|
|
struct drm_i915_private *dev_priv = dev->dev_private;
|
2013-01-18 04:45:15 +08:00
|
|
|
iounmap(dev_priv->gtt.gsm);
|
2013-01-25 05:49:57 +08:00
|
|
|
teardown_scratch_page(dev_priv->dev);
|
2012-03-26 15:45:40 +08:00
|
|
|
}
|
2013-01-25 05:49:57 +08:00
|
|
|
|
|
|
|
static int i915_gmch_probe(struct drm_device *dev,
|
|
|
|
size_t *gtt_total,
|
2013-02-09 03:32:47 +08:00
|
|
|
size_t *stolen,
|
|
|
|
phys_addr_t *mappable_base,
|
|
|
|
unsigned long *mappable_end)
|
2013-01-25 05:49:57 +08:00
|
|
|
{
|
|
|
|
struct drm_i915_private *dev_priv = dev->dev_private;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
ret = intel_gmch_probe(dev_priv->bridge_dev, dev_priv->dev->pdev, NULL);
|
|
|
|
if (!ret) {
|
|
|
|
DRM_ERROR("failed to set up gmch\n");
|
|
|
|
return -EIO;
|
|
|
|
}
|
|
|
|
|
2013-02-09 03:32:47 +08:00
|
|
|
intel_gtt_get(gtt_total, stolen, mappable_base, mappable_end);
|
2013-01-25 05:49:57 +08:00
|
|
|
|
|
|
|
dev_priv->gtt.do_idle_maps = needs_idle_maps(dev_priv->dev);
|
|
|
|
dev_priv->gtt.gtt_clear_range = i915_ggtt_clear_range;
|
|
|
|
dev_priv->gtt.gtt_insert_entries = i915_ggtt_insert_entries;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void i915_gmch_remove(struct drm_device *dev)
|
|
|
|
{
|
|
|
|
intel_gmch_remove();
|
|
|
|
}
|
|
|
|
|
|
|
|
int i915_gem_gtt_init(struct drm_device *dev)
|
|
|
|
{
|
|
|
|
struct drm_i915_private *dev_priv = dev->dev_private;
|
|
|
|
struct i915_gtt *gtt = &dev_priv->gtt;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
if (INTEL_INFO(dev)->gen <= 5) {
|
2013-06-28 07:30:20 +08:00
|
|
|
gtt->gtt_probe = i915_gmch_probe;
|
|
|
|
gtt->gtt_remove = i915_gmch_remove;
|
2013-01-25 05:49:57 +08:00
|
|
|
} else {
|
2013-06-28 07:30:20 +08:00
|
|
|
gtt->gtt_probe = gen6_gmch_probe;
|
|
|
|
gtt->gtt_remove = gen6_gmch_remove;
|
|
|
|
if (IS_HASWELL(dev))
|
|
|
|
gtt->pte_encode = hsw_pte_encode;
|
|
|
|
else if (IS_VALLEYVIEW(dev))
|
|
|
|
gtt->pte_encode = byt_pte_encode;
|
|
|
|
else
|
|
|
|
gtt->pte_encode = gen6_pte_encode;
|
2013-01-25 05:49:57 +08:00
|
|
|
}
|
|
|
|
|
2013-06-28 07:30:20 +08:00
|
|
|
ret = gtt->gtt_probe(dev, >t->total, >t->stolen_size,
|
|
|
|
>t->mappable_base, >t->mappable_end);
|
2013-01-25 06:45:00 +08:00
|
|
|
if (ret)
|
2013-01-25 05:49:57 +08:00
|
|
|
return ret;
|
|
|
|
|
|
|
|
/* GMADR is the PCI mmio aperture into the global GTT. */
|
2013-06-28 07:30:20 +08:00
|
|
|
DRM_INFO("Memory usable by graphics device = %zdM\n", gtt->total >> 20);
|
|
|
|
DRM_DEBUG_DRIVER("GMADR size = %ldM\n", gtt->mappable_end >> 20);
|
|
|
|
DRM_DEBUG_DRIVER("GTT stolen size = %zdM\n", gtt->stolen_size >> 20);
|
2013-01-25 05:49:57 +08:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|