return ERR_PTR(-ENOMEM);
kref_init(&map->ref);
- map->bo = host1x_bo_get(bo);
+ map->bo = bo;
map->direction = direction;
map->dev = dev;
kfree(map->sgt);
}
- host1x_bo_put(map->bo);
kfree(map);
}
void tegra_bo_free_object(struct drm_gem_object *gem)
{
struct tegra_drm *tegra = gem->dev->dev_private;
- struct host1x_bo_mapping *mapping, *tmp;
struct tegra_bo *bo = to_tegra_bo(gem);
- /* remove all mappings of this buffer object from any caches */
- list_for_each_entry_safe(mapping, tmp, &bo->base.mappings, list) {
- if (mapping->cache)
- host1x_bo_unpin(mapping);
- else
- dev_err(gem->dev->dev, "mapping %p stale for device %s\n", mapping,
- dev_name(mapping->dev));
- }
+ host1x_bo_clear_cached_mappings(&bo->base);
if (tegra->domain) {
tegra_bo_iommu_unmap(tegra, bo);
}
EXPORT_SYMBOL(host1x_client_resume);
+/**
+ * host1x_bo_pin() - Create a DMA mapping for the buffer object
+ * @dev: Device onto which DMA map to
+ * @bo: Buffer object to map
+ * @dir: DMA direction
+ * @cache: Cache in which to store mapping, or NULL
+ *
+ * Creates a DMA mapping pointing to @bo for @dev. The refcount of @bo is incremented
+ * until host1x_bo_unpin is called.
+ *
+ * If @cache is specified, the mapping is also stored in the cache and not released
+ * until @bo is freed (refcount drops to zero). This improves performance when a buffer
+ * is pinned and unpinned frequently as in the case of display use.
+ */
struct host1x_bo_mapping *host1x_bo_pin(struct device *dev, struct host1x_bo *bo,
enum dma_data_direction dir,
struct host1x_bo_cache *cache)
list_for_each_entry(mapping, &cache->mappings, entry) {
if (mapping->bo == bo && mapping->direction == dir) {
kref_get(&mapping->ref);
+ host1x_bo_get(bo);
goto unlock;
}
}
if (IS_ERR(mapping))
goto unlock;
+ host1x_bo_get(bo);
+
spin_lock(&mapping->bo->lock);
list_add_tail(&mapping->list, &bo->mappings);
spin_unlock(&mapping->bo->lock);
list_add_tail(&mapping->entry, &cache->mappings);
- /* bump reference count to track the copy in the cache */
+ /*
+ * Bump the mapping reference count to track the mapping in the cache,
+ * but do not bump the BO's refcount. This allows the BO to still get freed,
+ * triggering the release of the cache mapping through
+ * host1x_bo_clear_cached_mappings.
+ */
kref_get(&mapping->ref);
}
mapping->bo->ops->unpin(mapping);
}
+/**
+ * host1x_bo_unpin() - Release an established DMA mapping of a buffer object
+ * @mapping: Mapping to release
+ *
+ * Unmaps the given @mapping, unless it is cached. Decreases the refcount on
+ * the underlying buffer object.
+ */
void host1x_bo_unpin(struct host1x_bo_mapping *mapping)
{
struct host1x_bo_cache *cache = mapping->cache;
+ struct host1x_bo *bo = mapping->bo;
if (cache)
mutex_lock(&cache->lock);
if (cache)
mutex_unlock(&cache->lock);
+
+ host1x_bo_put(bo);
}
EXPORT_SYMBOL(host1x_bo_unpin);
+
+/**
+ * host1x_bo_clear_cached_mappings() - Remove all cached mappings pointing at a bo
+ * @bo: Buffer object to release mappings of
+ *
+ * Drops references to any mappings pointing to @bo left in any caches. This must
+ * be called by any host1x_bo implementers that may be pinned with caching enabled
+ * before freeing the bo.
+ */
+void host1x_bo_clear_cached_mappings(struct host1x_bo *bo)
+{
+ struct host1x_bo_mapping *mapping, *tmp;
+ struct host1x_bo_cache *cache;
+
+ list_for_each_entry_safe(mapping, tmp, &bo->mappings, list) {
+ cache = mapping->cache;
+ if (WARN_ON(!cache))
+ continue;
+
+ mutex_lock(&mapping->cache->lock);
+ WARN_ON(kref_read(&mapping->ref) != 1);
+ __host1x_bo_unpin(&mapping->ref);
+ mutex_unlock(&mapping->cache->lock);
+ }
+}
+EXPORT_SYMBOL(host1x_bo_clear_cached_mappings);
return container_of(ref, struct host1x_bo_mapping, ref);
}
+/**
+ * struct host1x_bo_ops - operations implemented by a host1x_bo provider
+ *
+ * @pin: create a DMA mapping. Implementation must not touch the bo's refcount.
+ * @unpin: destroy a DMA mapping. Implementation must not touch the bo's refcount.
+ */
struct host1x_bo_ops {
struct host1x_bo *(*get)(struct host1x_bo *bo);
void (*put)(struct host1x_bo *bo);
enum dma_data_direction dir,
struct host1x_bo_cache *cache);
void host1x_bo_unpin(struct host1x_bo_mapping *map);
+void host1x_bo_clear_cached_mappings(struct host1x_bo *bo);
static inline void *host1x_bo_mmap(struct host1x_bo *bo)
{