summaryrefslogtreecommitdiffstats
path: root/drivers/staging/gma500/psb_gtt.c
diff options
context:
space:
mode:
authorAlan Cox2011-04-13 11:37:03 +0200
committerGreg Kroah-Hartman2011-04-26 02:12:58 +0200
commit8d9c134c6e53e27f37ae3167e25190885586e538 (patch)
tree0b20985584942c83ee419c257fa3bdbdf1a7ac56 /drivers/staging/gma500/psb_gtt.c
parentgma500: backlight warning (diff)
downloadkernel-qcow2-linux-8d9c134c6e53e27f37ae3167e25190885586e538.tar.gz
kernel-qcow2-linux-8d9c134c6e53e27f37ae3167e25190885586e538.tar.xz
kernel-qcow2-linux-8d9c134c6e53e27f37ae3167e25190885586e538.zip
gma500: Add a gtt allocator
At the moment we don't do any page backing for the GTT so only the stolen area pages will actually work. That is fine for our initial framebuffer and a bit of testing but will need resolution (including alternate mmap methods and the like for s/g areas) eventually. Rather than use some of the overcomplex stuff in the DRM we use the existing Linux resource allocators to hand out framebuffers and the like. This also has the nice result that /proc/iomem shows the allocations. Signed-off-by: Alan Cox <alan@linux.intel.com> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'drivers/staging/gma500/psb_gtt.c')
-rw-r--r--drivers/staging/gma500/psb_gtt.c155
1 files changed, 154 insertions, 1 deletions
diff --git a/drivers/staging/gma500/psb_gtt.c b/drivers/staging/gma500/psb_gtt.c
index 5f2acef014f8..a97e7beefc17 100644
--- a/drivers/staging/gma500/psb_gtt.c
+++ b/drivers/staging/gma500/psb_gtt.c
@@ -94,8 +94,10 @@ int psb_gtt_init(struct psb_gtt *pg, int resume)
PSB_WVDC32(pg->pge_ctl | _PSB_PGETBL_ENABLED, PSB_PGETBL_CTL);
(void) PSB_RVDC32(PSB_PGETBL_CTL);
- pg->initialized = 1;
+ /* The root resource we allocate address space from */
+ dev_priv->gtt_mem = &dev->pdev->resource[PSB_GATT_RESOURCE];
+ pg->initialized = 1;
pg->gtt_phys_start = pg->pge_ctl & PAGE_MASK;
pg->gatt_start = pci_resource_start(dev->pdev, PSB_GATT_RESOURCE);
@@ -884,3 +886,154 @@ int psb_gtt_unmap_meminfo(struct drm_device *dev, void * hKernelMemInfo)
return 0;
}
+/*
+ * GTT resource allocator
+ */
+
+/**
+ * psb_gtt_alloc_handle - allocate a handle to a GTT map
+ * @dev: our DRM device
+ * @gt: Our GTT range
+ *
+ * Assign a handle to a gtt range object. For the moment we use a very
+ * simplistic interface.
+ */
+int psb_gtt_alloc_handle(struct drm_device *dev, struct gtt_range *gt)
+{
+ struct drm_psb_private *dev_priv = dev->dev_private;
+ int h;
+
+ mutex_lock(&dev_priv->gtt_mutex);
+ for (h = 0; h < GTT_MAX; h++) {
+ if (dev_priv->gtt_handles[h] == NULL) {
+ dev_priv->gtt_handles[h] = gt;
+ gt->handle = h;
+ kref_get(&gt->kref);
+ mutex_unlock(&dev_priv->gtt_mutex);
+ return h;
+ }
+ }
+ mutex_unlock(&dev_priv->gtt_mutex);
+ return -ENOSPC;
+}
+
+/**
+ * psb_gtt_release_handle - release a handle to a GTT map
+ * @dev: our DRM device
+ * @gt: Our GTT range
+ *
+ * Remove the handle from a gtt range object
+ */
+int psb_gtt_release_handle(struct drm_device *dev, struct gtt_range *gt)
+{
+ struct drm_psb_private *dev_priv = dev->dev_private;
+
+ if (gt->handle < 0 || gt->handle >= GTT_MAX) {
+ gt->handle = -1;
+ WARN_ON(1);
+ return -EINVAL;
+ }
+ mutex_lock(&dev_priv->gtt_mutex);
+ dev_priv->gtt_handles[gt->handle] = NULL;
+ gt->handle = -1;
+ mutex_unlock(&dev_priv->gtt_mutex);
+ psb_gtt_kref_put(gt);
+ return 0;
+}
+
+/**
+ * psb_gtt_lookup_handle - look up a GTT handle
+ * @dev: our DRM device
+ * @handle: our handle
+ *
+ * Look up a gtt handle and return the gtt or NULL. The object returned
+ * has a reference held so the caller must drop this when finished.
+ */
+struct gtt_range *psb_gtt_lookup_handle(struct drm_device *dev, int handle)
+{
+ struct drm_psb_private *dev_priv = dev->dev_private;
+ struct gtt_range *gt;
+
+ if (handle < 0 || handle > GTT_MAX)
+ return ERR_PTR(-EINVAL);
+
+ mutex_lock(&dev_priv->gtt_mutex);
+ gt = dev_priv->gtt_handles[handle];
+ kref_get(&gt->kref);
+ mutex_unlock(&dev_priv->gtt_mutex);
+
+ if (gt == NULL)
+ return ERR_PTR(-ENOENT);
+ return gt;
+}
+
+/**
+ * psb_gtt_alloc_range - allocate GTT address space
+ * @dev: Our DRM device
+ * @len: length (bytes) of address space required
+ * @name: resource name
+ *
+ * Ask the kernel core to find us a suitable range of addresses
+ * to use for a GTT mapping.
+ *
+ * Returns a gtt_range structure describing the object, or NULL on
+ * error. On successful return the resource is both allocated and marked
+ * as in use.
+ */
+struct gtt_range *psb_gtt_alloc_range(struct drm_device *dev, int len,
+ const char *name)
+{
+ struct drm_psb_private *dev_priv = dev->dev_private;
+ struct gtt_range *gt;
+ struct resource *r = dev_priv->gtt_mem;
+ int ret;
+
+ gt = kzalloc(sizeof(struct gtt_range), GFP_KERNEL);
+ if (gt == NULL)
+ return NULL;
+ gt->handle = -1;
+ gt->resource.name = name;
+ kref_init(&gt->kref);
+
+ ret = allocate_resource(dev_priv->gtt_mem, &gt->resource,
+ len, 0, -1, /*r->start, r->end - 1, */
+ PAGE_SIZE, NULL, NULL);
+ if (ret == 0) {
+ gt->offset = gt->resource.start - r->start;
+ return gt;
+ }
+ kfree(gt);
+ return NULL;
+}
+
+static void psb_gtt_destroy(struct kref *kref)
+{
+ struct gtt_range *gt = container_of(kref, struct gtt_range, kref);
+ release_resource(&gt->resource);
+ kfree(gt);
+}
+
+/**
+ * psb_gtt_kref_put - drop reference to a GTT object
+ * @gt: the GT being dropped
+ *
+ * Drop a reference to a psb gtt
+ */
+void psb_gtt_kref_put(struct gtt_range *gt)
+{
+ kref_put(&gt->kref, psb_gtt_destroy);
+}
+
+/**
+ * psb_gtt_free_range - release GTT address space
+ * @dev: our DRM device
+ * @gt: a mapping created with psb_gtt_alloc_range
+ *
+ * Release a resource that was allocated with psb_gtt_alloc_range
+ */
+void psb_gtt_free_range(struct drm_device *dev, struct gtt_range *gt)
+{
+ if (gt->handle != -1)
+ psb_gtt_release_handle(dev, gt);
+ psb_gtt_kref_put(gt);
+}