summaryrefslogblamecommitdiffstats
path: root/drivers/gpu/nvgpu/gk20a/semaphore_gk20a.c
blob: cf8554635d6543bacfc1ef63b1225091bddb2ea0 (plain) (tree)
1
2
3
4
5
6




                                                   
                                                                     







































                                                                            



                                                                           





































                                                                             

                                                                    

                                                            





                                                  








                                                            





                                                          

                                                            



                               

                         
                                   
                                                             







                                                               











                                                                         




                                                               









                                                          









                                                                                

                                                               




















                                                                             
                                                












                                                   
/*
 * drivers/video/tegra/host/gk20a/semaphore_gk20a.c
 *
 * GK20A Semaphores
 *
 * Copyright (c) 2014-2015, NVIDIA CORPORATION.  All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms and conditions of the GNU General Public License,
 * version 2, as published by the Free Software Foundation.
 *
 * This program is distributed in the hope it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
 * more details.
 */

#include "semaphore_gk20a.h"
#include <linux/dma-mapping.h>
#include <linux/slab.h>
#include "gk20a.h"
#include "mm_gk20a.h"

static const int SEMAPHORE_SIZE = 16;

struct gk20a_semaphore_pool *gk20a_semaphore_pool_alloc(struct device *d,
		const char *unique_name, size_t capacity)
{
	struct gk20a_semaphore_pool *p;
	p = kzalloc(sizeof(*p), GFP_KERNEL);
	if (!p)
		return NULL;

	kref_init(&p->ref);
	INIT_LIST_HEAD(&p->maps);
	mutex_init(&p->maps_mutex);
	p->dev = d;

	/* Alloc one 4k page of semaphore per channel. */
	p->size = roundup(capacity * SEMAPHORE_SIZE, PAGE_SIZE);
	p->cpu_va = dma_alloc_coherent(d, p->size, &p->iova, GFP_KERNEL);
	if (!p->cpu_va)
		goto clean_up;
	if (gk20a_get_sgtable(d, &p->sgt, p->cpu_va, p->iova, p->size))
		goto clean_up;

	/* Sacrifice one semaphore in the name of returning error codes. */
	if (gk20a_allocator_init(&p->alloc, unique_name,
				 SEMAPHORE_SIZE, p->size - SEMAPHORE_SIZE,
				 SEMAPHORE_SIZE))
		goto clean_up;

	gk20a_dbg_info("cpuva=%p iova=%llx phys=%llx", p->cpu_va,
		(u64)sg_dma_address(p->sgt->sgl), (u64)sg_phys(p->sgt->sgl));
	return p;
clean_up:
	if (p->cpu_va)
		dma_free_coherent(d, p->size, p->cpu_va, p->iova);
	if (p->sgt)
		gk20a_free_sgtable(&p->sgt);
	kfree(p);
	return NULL;
}

static void gk20a_semaphore_pool_free(struct kref *ref)
{
	struct gk20a_semaphore_pool *p =
		container_of(ref, struct gk20a_semaphore_pool, ref);
	mutex_lock(&p->maps_mutex);
	WARN_ON(!list_empty(&p->maps));
	mutex_unlock(&p->maps_mutex);
	gk20a_free_sgtable(&p->sgt);
	dma_free_coherent(p->dev, p->size, p->cpu_va, p->iova);
	gk20a_allocator_destroy(&p->alloc);
	kfree(p);
}

static void gk20a_semaphore_pool_get(struct gk20a_semaphore_pool *p)
{
	kref_get(&p->ref);
}

void gk20a_semaphore_pool_put(struct gk20a_semaphore_pool *p)
{
	kref_put(&p->ref, gk20a_semaphore_pool_free);
}

static struct gk20a_semaphore_pool_map *
gk20a_semaphore_pool_find_map_locked(struct gk20a_semaphore_pool *p,
				     struct vm_gk20a *vm)
{
	struct gk20a_semaphore_pool_map *map, *found = NULL;
	list_for_each_entry(map, &p->maps, list) {
		if (map->vm == vm) {
			found = map;
			break;
		}
	}
	return found;
}

int gk20a_semaphore_pool_map(struct gk20a_semaphore_pool *p,
			     struct vm_gk20a *vm,
			     enum gk20a_mem_rw_flag rw_flag)
{
	struct gk20a_semaphore_pool_map *map;

	map = kzalloc(sizeof(*map), GFP_KERNEL);
	if (!map)
		return -ENOMEM;
	map->vm = vm;
	map->rw_flag = rw_flag;
	map->gpu_va = gk20a_gmmu_map(vm, &p->sgt, p->size,
				     0/*uncached*/, rw_flag,
				     false);
	if (!map->gpu_va) {
		kfree(map);
		return -ENOMEM;
	}
	gk20a_vm_get(vm);

	mutex_lock(&p->maps_mutex);
	WARN_ON(gk20a_semaphore_pool_find_map_locked(p, vm));
	list_add(&map->list, &p->maps);
	mutex_unlock(&p->maps_mutex);
	return 0;
}

void gk20a_semaphore_pool_unmap(struct gk20a_semaphore_pool *p,
		struct vm_gk20a *vm)
{
	struct gk20a_semaphore_pool_map *map;
	WARN_ON(!vm);

	mutex_lock(&p->maps_mutex);
	map = gk20a_semaphore_pool_find_map_locked(p, vm);
	if (map) {
		gk20a_gmmu_unmap(vm, map->gpu_va, p->size, map->rw_flag);
		gk20a_vm_put(vm);
		list_del(&map->list);
		kfree(map);
	}
	mutex_unlock(&p->maps_mutex);
}

u64 gk20a_semaphore_pool_gpu_va(struct gk20a_semaphore_pool *p,
		struct vm_gk20a *vm)
{
	struct gk20a_semaphore_pool_map *map;
	u64 gpu_va = 0;

	mutex_lock(&p->maps_mutex);
	map = gk20a_semaphore_pool_find_map_locked(p, vm);
	if (map)
		gpu_va = map->gpu_va;
	mutex_unlock(&p->maps_mutex);

	return gpu_va;
}

struct gk20a_semaphore *gk20a_semaphore_alloc(struct gk20a_semaphore_pool *pool)
{
	struct gk20a_semaphore *s;

	s = kzalloc(sizeof(*s), GFP_KERNEL);
	if (!s)
		return NULL;

	s->offset = gk20a_balloc(&pool->alloc, SEMAPHORE_SIZE);
	if (!s->offset) {
		gk20a_err(pool->dev, "failed to allocate semaphore");
		kfree(s);
		return NULL;
	}

	gk20a_semaphore_pool_get(pool);
	s->pool = pool;

	kref_init(&s->ref);
	s->value = (volatile u32 *)((uintptr_t)pool->cpu_va + s->offset);
	*s->value = 0; /* Initially acquired. */
	gk20a_dbg_info("created semaphore offset=%d, value_cpu=%p, value=%d",
			s->offset, s->value, *s->value);
	return s;
}

static void gk20a_semaphore_free(struct kref *ref)
{
	struct gk20a_semaphore *s =
		container_of(ref, struct gk20a_semaphore, ref);

	gk20a_bfree(&s->pool->alloc, s->offset);
	gk20a_semaphore_pool_put(s->pool);
	kfree(s);
}

void gk20a_semaphore_put(struct gk20a_semaphore *s)
{
	kref_put(&s->ref, gk20a_semaphore_free);
}

void gk20a_semaphore_get(struct gk20a_semaphore *s)
{
	kref_get(&s->ref);
}