drm/i915: framebuffer compression for pre-GM45
This patch adds framebuffer compression (good for about ~0.5W power savings in the best case) support for pre-GM45 chips. GM45+ have a new, more flexible FBC scheme that will be added in a separate patch. FBC can't always be enabled: the compressed buffer must be physically contiguous and reside in stolen space. So if you have a large display and a small amount of stolen memory, you may not be able to take advantage of FBC. In some cases, a BIOS setting controls how much stolen space is available. Increasing this to 8 or 16M can help. Signed-off-by: Jesse Barnes <jbarnes@virtuousgeek.org> Signed-off-by: Eric Anholt <eric@anholt.net>
This commit is contained in:

committed by
Eric Anholt

parent
06324194ee
commit
8082400327
@@ -921,7 +921,8 @@ static int i915_get_bridge_dev(struct drm_device *dev)
|
||||
* how much was set aside so we can use it for our own purposes.
|
||||
*/
|
||||
static int i915_probe_agp(struct drm_device *dev, uint32_t *aperture_size,
|
||||
uint32_t *preallocated_size)
|
||||
uint32_t *preallocated_size,
|
||||
uint32_t *start)
|
||||
{
|
||||
struct drm_i915_private *dev_priv = dev->dev_private;
|
||||
u16 tmp = 0;
|
||||
@@ -1008,11 +1009,148 @@ static int i915_probe_agp(struct drm_device *dev, uint32_t *aperture_size,
|
||||
return -1;
|
||||
}
|
||||
*preallocated_size = stolen - overhead;
|
||||
*start = overhead;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define PTE_ADDRESS_MASK 0xfffff000
|
||||
#define PTE_ADDRESS_MASK_HIGH 0x000000f0 /* i915+ */
|
||||
#define PTE_MAPPING_TYPE_UNCACHED (0 << 1)
|
||||
#define PTE_MAPPING_TYPE_DCACHE (1 << 1) /* i830 only */
|
||||
#define PTE_MAPPING_TYPE_CACHED (3 << 1)
|
||||
#define PTE_MAPPING_TYPE_MASK (3 << 1)
|
||||
#define PTE_VALID (1 << 0)
|
||||
|
||||
/**
|
||||
* i915_gtt_to_phys - take a GTT address and turn it into a physical one
|
||||
* @dev: drm device
|
||||
* @gtt_addr: address to translate
|
||||
*
|
||||
* Some chip functions require allocations from stolen space but need the
|
||||
* physical address of the memory in question. We use this routine
|
||||
* to get a physical address suitable for register programming from a given
|
||||
* GTT address.
|
||||
*/
|
||||
static unsigned long i915_gtt_to_phys(struct drm_device *dev,
|
||||
unsigned long gtt_addr)
|
||||
{
|
||||
unsigned long *gtt;
|
||||
unsigned long entry, phys;
|
||||
int gtt_bar = IS_I9XX(dev) ? 0 : 1;
|
||||
int gtt_offset, gtt_size;
|
||||
|
||||
if (IS_I965G(dev)) {
|
||||
if (IS_G4X(dev) || IS_IGDNG(dev)) {
|
||||
gtt_offset = 2*1024*1024;
|
||||
gtt_size = 2*1024*1024;
|
||||
} else {
|
||||
gtt_offset = 512*1024;
|
||||
gtt_size = 512*1024;
|
||||
}
|
||||
} else {
|
||||
gtt_bar = 3;
|
||||
gtt_offset = 0;
|
||||
gtt_size = pci_resource_len(dev->pdev, gtt_bar);
|
||||
}
|
||||
|
||||
gtt = ioremap_wc(pci_resource_start(dev->pdev, gtt_bar) + gtt_offset,
|
||||
gtt_size);
|
||||
if (!gtt) {
|
||||
DRM_ERROR("ioremap of GTT failed\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
entry = *(volatile u32 *)(gtt + (gtt_addr / 1024));
|
||||
|
||||
DRM_DEBUG("GTT addr: 0x%08lx, PTE: 0x%08lx\n", gtt_addr, entry);
|
||||
|
||||
/* Mask out these reserved bits on this hardware. */
|
||||
if (!IS_I9XX(dev) || IS_I915G(dev) || IS_I915GM(dev) ||
|
||||
IS_I945G(dev) || IS_I945GM(dev)) {
|
||||
entry &= ~PTE_ADDRESS_MASK_HIGH;
|
||||
}
|
||||
|
||||
/* If it's not a mapping type we know, then bail. */
|
||||
if ((entry & PTE_MAPPING_TYPE_MASK) != PTE_MAPPING_TYPE_UNCACHED &&
|
||||
(entry & PTE_MAPPING_TYPE_MASK) != PTE_MAPPING_TYPE_CACHED) {
|
||||
iounmap(gtt);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!(entry & PTE_VALID)) {
|
||||
DRM_ERROR("bad GTT entry in stolen space\n");
|
||||
iounmap(gtt);
|
||||
return 0;
|
||||
}
|
||||
|
||||
iounmap(gtt);
|
||||
|
||||
phys =(entry & PTE_ADDRESS_MASK) |
|
||||
((uint64_t)(entry & PTE_ADDRESS_MASK_HIGH) << (32 - 4));
|
||||
|
||||
DRM_DEBUG("GTT addr: 0x%08lx, phys addr: 0x%08lx\n", gtt_addr, phys);
|
||||
|
||||
return phys;
|
||||
}
|
||||
|
||||
static void i915_warn_stolen(struct drm_device *dev)
|
||||
{
|
||||
DRM_ERROR("not enough stolen space for compressed buffer, disabling\n");
|
||||
DRM_ERROR("hint: you may be able to increase stolen memory size in the BIOS to avoid this\n");
|
||||
}
|
||||
|
||||
static void i915_setup_compression(struct drm_device *dev, int size)
|
||||
{
|
||||
struct drm_i915_private *dev_priv = dev->dev_private;
|
||||
struct drm_mm_node *compressed_fb, *compressed_llb;
|
||||
unsigned long cfb_base, ll_base;
|
||||
|
||||
/* Leave 1M for line length buffer & misc. */
|
||||
compressed_fb = drm_mm_search_free(&dev_priv->vram, size, 4096, 0);
|
||||
if (!compressed_fb) {
|
||||
i915_warn_stolen(dev);
|
||||
return;
|
||||
}
|
||||
|
||||
compressed_fb = drm_mm_get_block(compressed_fb, size, 4096);
|
||||
if (!compressed_fb) {
|
||||
i915_warn_stolen(dev);
|
||||
return;
|
||||
}
|
||||
|
||||
compressed_llb = drm_mm_search_free(&dev_priv->vram, 4096, 4096, 0);
|
||||
if (!compressed_llb) {
|
||||
i915_warn_stolen(dev);
|
||||
return;
|
||||
}
|
||||
|
||||
compressed_llb = drm_mm_get_block(compressed_fb, 4096, 4096);
|
||||
if (!compressed_llb) {
|
||||
i915_warn_stolen(dev);
|
||||
return;
|
||||
}
|
||||
|
||||
dev_priv->cfb_size = size;
|
||||
|
||||
cfb_base = i915_gtt_to_phys(dev, compressed_fb->start);
|
||||
ll_base = i915_gtt_to_phys(dev, compressed_llb->start);
|
||||
if (!cfb_base || !ll_base) {
|
||||
DRM_ERROR("failed to get stolen phys addr, disabling FBC\n");
|
||||
drm_mm_put_block(compressed_fb);
|
||||
drm_mm_put_block(compressed_llb);
|
||||
}
|
||||
|
||||
i8xx_disable_fbc(dev);
|
||||
|
||||
DRM_DEBUG("FBC base 0x%08lx, ll base 0x%08lx, size %dM\n", cfb_base,
|
||||
ll_base, size >> 20);
|
||||
I915_WRITE(FBC_CFB_BASE, cfb_base);
|
||||
I915_WRITE(FBC_LL_BASE, ll_base);
|
||||
}
|
||||
|
||||
static int i915_load_modeset_init(struct drm_device *dev,
|
||||
unsigned long prealloc_start,
|
||||
unsigned long prealloc_size,
|
||||
unsigned long agp_size)
|
||||
{
|
||||
@@ -1033,6 +1171,7 @@ static int i915_load_modeset_init(struct drm_device *dev,
|
||||
|
||||
/* Basic memrange allocator for stolen space (aka vram) */
|
||||
drm_mm_init(&dev_priv->vram, 0, prealloc_size);
|
||||
DRM_INFO("set up %ldM of stolen space\n", prealloc_size / (1024*1024));
|
||||
|
||||
/* Let GEM Manage from end of prealloc space to end of aperture.
|
||||
*
|
||||
@@ -1049,6 +1188,19 @@ static int i915_load_modeset_init(struct drm_device *dev,
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
/* Try to set up FBC with a reasonable compressed buffer size */
|
||||
if (IS_MOBILE(dev) && (IS_I9XX(dev) || IS_I965G(dev)) &&
|
||||
i915_powersave) {
|
||||
int cfb_size;
|
||||
|
||||
/* Try to get an 8M buffer... */
|
||||
if (prealloc_size > (9*1024*1024))
|
||||
cfb_size = 8*1024*1024;
|
||||
else /* fall back to 7/8 of the stolen space */
|
||||
cfb_size = prealloc_size * 7 / 8;
|
||||
i915_setup_compression(dev, cfb_size);
|
||||
}
|
||||
|
||||
/* Allow hardware batchbuffers unless told otherwise.
|
||||
*/
|
||||
dev_priv->allow_batchbuffer = 1;
|
||||
@@ -1161,7 +1313,7 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags)
|
||||
struct drm_i915_private *dev_priv = dev->dev_private;
|
||||
resource_size_t base, size;
|
||||
int ret = 0, mmio_bar = IS_I9XX(dev) ? 0 : 1;
|
||||
uint32_t agp_size, prealloc_size;
|
||||
uint32_t agp_size, prealloc_size, prealloc_start;
|
||||
|
||||
/* i915 has 4 more counters */
|
||||
dev->counters += 4;
|
||||
@@ -1215,7 +1367,7 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags)
|
||||
"performance may suffer.\n");
|
||||
}
|
||||
|
||||
ret = i915_probe_agp(dev, &agp_size, &prealloc_size);
|
||||
ret = i915_probe_agp(dev, &agp_size, &prealloc_size, &prealloc_start);
|
||||
if (ret)
|
||||
goto out_iomapfree;
|
||||
|
||||
@@ -1282,7 +1434,8 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags)
|
||||
}
|
||||
|
||||
if (drm_core_check_feature(dev, DRIVER_MODESET)) {
|
||||
ret = i915_load_modeset_init(dev, prealloc_size, agp_size);
|
||||
ret = i915_load_modeset_init(dev, prealloc_start,
|
||||
prealloc_size, agp_size);
|
||||
if (ret < 0) {
|
||||
DRM_ERROR("failed to init modeset\n");
|
||||
goto out_workqueue_free;
|
||||
|
Reference in New Issue
Block a user