summaryrefslogtreecommitdiffstats
path: root/drivers/gpu/drm/gma500/gem.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/gpu/drm/gma500/gem.c')
-rw-r--r--drivers/gpu/drm/gma500/gem.c161
1 files changed, 150 insertions, 11 deletions
diff --git a/drivers/gpu/drm/gma500/gem.c b/drivers/gpu/drm/gma500/gem.c
index 8d65af80bb08..dffe37490206 100644
--- a/drivers/gpu/drm/gma500/gem.c
+++ b/drivers/gpu/drm/gma500/gem.c
@@ -21,6 +21,10 @@
#include "gem.h"
#include "psb_drv.h"
+/*
+ * PSB GEM object
+ */
+
int psb_gem_pin(struct psb_gem_object *pobj)
{
struct drm_gem_object *obj = &pobj->base;
@@ -31,7 +35,9 @@ int psb_gem_pin(struct psb_gem_object *pobj)
unsigned int npages;
int ret;
- mutex_lock(&dev_priv->gtt_mutex);
+ ret = dma_resv_lock(obj->resv, NULL);
+ if (drm_WARN_ONCE(dev, ret, "dma_resv_lock() failed, ret=%d\n", ret))
+ return ret;
if (pobj->in_gart || pobj->stolen)
goto out; /* already mapped */
@@ -39,7 +45,7 @@ int psb_gem_pin(struct psb_gem_object *pobj)
pages = drm_gem_get_pages(obj);
if (IS_ERR(pages)) {
ret = PTR_ERR(pages);
- goto err_mutex_unlock;
+ goto err_dma_resv_unlock;
}
npages = obj->size / PAGE_SIZE;
@@ -51,17 +57,16 @@ int psb_gem_pin(struct psb_gem_object *pobj)
(gpu_base + pobj->offset), npages, 0, 0,
PSB_MMU_CACHED_MEMORY);
- pobj->npage = npages;
pobj->pages = pages;
out:
++pobj->in_gart;
- mutex_unlock(&dev_priv->gtt_mutex);
+ dma_resv_unlock(obj->resv);
return 0;
-err_mutex_unlock:
- mutex_unlock(&dev_priv->gtt_mutex);
+err_dma_resv_unlock:
+ dma_resv_unlock(obj->resv);
return ret;
}
@@ -71,8 +76,12 @@ void psb_gem_unpin(struct psb_gem_object *pobj)
struct drm_device *dev = obj->dev;
struct drm_psb_private *dev_priv = to_drm_psb_private(dev);
u32 gpu_base = dev_priv->gtt.gatt_start;
+ unsigned long npages;
+ int ret;
- mutex_lock(&dev_priv->gtt_mutex);
+ ret = dma_resv_lock(obj->resv, NULL);
+ if (drm_WARN_ONCE(dev, ret, "dma_resv_lock() failed, ret=%d\n", ret))
+ return;
WARN_ON(!pobj->in_gart);
@@ -81,19 +90,20 @@ void psb_gem_unpin(struct psb_gem_object *pobj)
if (pobj->in_gart || pobj->stolen)
goto out;
+ npages = obj->size / PAGE_SIZE;
+
psb_mmu_remove_pages(psb_mmu_get_default_pd(dev_priv->mmu),
- (gpu_base + pobj->offset), pobj->npage, 0, 0);
+ (gpu_base + pobj->offset), npages, 0, 0);
psb_gtt_remove_pages(dev_priv, &pobj->resource);
/* Reset caching flags */
- set_pages_array_wb(pobj->pages, pobj->npage);
+ set_pages_array_wb(pobj->pages, npages);
drm_gem_put_pages(obj, pobj->pages, true, false);
pobj->pages = NULL;
- pobj->npage = 0;
out:
- mutex_unlock(&dev_priv->gtt_mutex);
+ dma_resv_unlock(obj->resv);
}
static vm_fault_t psb_gem_fault(struct vm_fault *vmf);
@@ -290,3 +300,132 @@ fail:
return ret;
}
+
+/*
+ * Memory management
+ */
+
+/* Insert vram stolen pages into the GTT. */
+static void psb_gem_mm_populate_stolen(struct drm_psb_private *pdev)
+{
+ struct drm_device *dev = &pdev->dev;
+ unsigned int pfn_base;
+ unsigned int i, num_pages;
+ uint32_t pte;
+
+ pfn_base = pdev->stolen_base >> PAGE_SHIFT;
+ num_pages = pdev->vram_stolen_size >> PAGE_SHIFT;
+
+ drm_dbg(dev, "Set up %u stolen pages starting at 0x%08x, GTT offset %dK\n",
+ num_pages, pfn_base << PAGE_SHIFT, 0);
+
+ for (i = 0; i < num_pages; ++i) {
+ pte = psb_gtt_mask_pte(pfn_base + i, PSB_MMU_CACHED_MEMORY);
+ iowrite32(pte, pdev->gtt_map + i);
+ }
+
+ (void)ioread32(pdev->gtt_map + i - 1);
+}
+
+int psb_gem_mm_init(struct drm_device *dev)
+{
+ struct drm_psb_private *dev_priv = to_drm_psb_private(dev);
+ struct pci_dev *pdev = to_pci_dev(dev->dev);
+ unsigned long stolen_size, vram_stolen_size;
+ struct psb_gtt *pg;
+ int ret;
+
+ mutex_init(&dev_priv->mmap_mutex);
+
+ pg = &dev_priv->gtt;
+
+ pci_read_config_dword(pdev, PSB_BSM, &dev_priv->stolen_base);
+ vram_stolen_size = pg->gtt_phys_start - dev_priv->stolen_base - PAGE_SIZE;
+
+ stolen_size = vram_stolen_size;
+
+ dev_dbg(dev->dev, "Stolen memory base 0x%x, size %luK\n",
+ dev_priv->stolen_base, vram_stolen_size / 1024);
+
+ pg->stolen_size = stolen_size;
+ dev_priv->vram_stolen_size = vram_stolen_size;
+
+ dev_priv->vram_addr = ioremap_wc(dev_priv->stolen_base, stolen_size);
+ if (!dev_priv->vram_addr) {
+ dev_err(dev->dev, "Failure to map stolen base.\n");
+ ret = -ENOMEM;
+ goto err_mutex_destroy;
+ }
+
+ psb_gem_mm_populate_stolen(dev_priv);
+
+ return 0;
+
+err_mutex_destroy:
+ mutex_destroy(&dev_priv->mmap_mutex);
+ return ret;
+}
+
+void psb_gem_mm_fini(struct drm_device *dev)
+{
+ struct drm_psb_private *dev_priv = to_drm_psb_private(dev);
+
+ iounmap(dev_priv->vram_addr);
+
+ mutex_destroy(&dev_priv->mmap_mutex);
+}
+
+/* Re-insert all pinned GEM objects into GTT. */
+static void psb_gem_mm_populate_resources(struct drm_psb_private *pdev)
+{
+ unsigned int restored = 0, total = 0, size = 0;
+ struct resource *r = pdev->gtt_mem->child;
+ struct drm_device *dev = &pdev->dev;
+ struct psb_gem_object *pobj;
+
+ while (r) {
+ /*
+ * TODO: GTT restoration needs a refactoring, so that we don't have to touch
+ * struct psb_gem_object here. The type represents a GEM object and is
+ * not related to the GTT itself.
+ */
+ pobj = container_of(r, struct psb_gem_object, resource);
+ if (pobj->pages) {
+ psb_gtt_insert_pages(pdev, &pobj->resource, pobj->pages);
+ size += resource_size(&pobj->resource);
+ ++restored;
+ }
+ r = r->sibling;
+ ++total;
+ }
+
+ drm_dbg(dev, "Restored %u of %u gtt ranges (%u KB)", restored, total, (size / 1024));
+}
+
+int psb_gem_mm_resume(struct drm_device *dev)
+{
+ struct drm_psb_private *dev_priv = to_drm_psb_private(dev);
+ struct pci_dev *pdev = to_pci_dev(dev->dev);
+ unsigned long stolen_size, vram_stolen_size;
+ struct psb_gtt *pg;
+
+ pg = &dev_priv->gtt;
+
+ pci_read_config_dword(pdev, PSB_BSM, &dev_priv->stolen_base);
+ vram_stolen_size = pg->gtt_phys_start - dev_priv->stolen_base - PAGE_SIZE;
+
+ stolen_size = vram_stolen_size;
+
+ dev_dbg(dev->dev, "Stolen memory base 0x%x, size %luK\n", dev_priv->stolen_base,
+ vram_stolen_size / 1024);
+
+ if (stolen_size != pg->stolen_size) {
+ dev_err(dev->dev, "GTT resume error.\n");
+ return -EINVAL;
+ }
+
+ psb_gem_mm_populate_stolen(dev_priv);
+ psb_gem_mm_populate_resources(dev_priv);
+
+ return 0;
+}