197 lines
4.9 KiB
C
197 lines
4.9 KiB
C
/*
|
|
* Tegra Graphics Host Hardware Context Interface
|
|
*
|
|
* Copyright (c) 2013, 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.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include <linux/slab.h>
|
|
#include <linux/scatterlist.h>
|
|
#include <linux/nvhost_ioctl.h>
|
|
#include "user_hwctx.h"
|
|
#include "nvhost_cdma.h"
|
|
#include "nvhost_memmgr.h"
|
|
#include "nvhost_channel.h"
|
|
#include "host1x/host1x.h"
|
|
#include "host1x/host1x01_hardware.h"
|
|
|
|
static void user_hwctx_save_push(struct nvhost_hwctx *nctx,
|
|
struct nvhost_cdma *cdma)
|
|
{
|
|
struct user_hwctx *ctx = to_user_hwctx(nctx);
|
|
nvhost_cdma_push_gather(cdma,
|
|
nctx->memmgr,
|
|
ctx->save_buf,
|
|
ctx->save_offset,
|
|
nvhost_opcode_gather(ctx->save_size),
|
|
nvhost_memmgr_dma_addr(ctx->save_sgt));
|
|
}
|
|
|
|
static void user_hwctx_restore_push(struct nvhost_hwctx *nctx,
|
|
struct nvhost_cdma *cdma)
|
|
{
|
|
struct user_hwctx *ctx = to_user_hwctx(nctx);
|
|
nvhost_cdma_push_gather(cdma,
|
|
nctx->memmgr,
|
|
ctx->restore,
|
|
ctx->restore_offset,
|
|
nvhost_opcode_gather(ctx->restore_size),
|
|
nvhost_memmgr_dma_addr(ctx->restore_sgt));
|
|
}
|
|
|
|
static void user_hwctx_free(struct kref *ref)
|
|
{
|
|
struct nvhost_hwctx *hwctx =
|
|
container_of(ref, struct nvhost_hwctx, ref);
|
|
struct user_hwctx *uhwctx = to_user_hwctx(hwctx);
|
|
|
|
user_ctxhandler_free(hwctx->h);
|
|
|
|
if (uhwctx->save_sgt)
|
|
nvhost_memmgr_unpin(hwctx->memmgr, uhwctx->save_buf,
|
|
&hwctx->channel->dev->dev, uhwctx->save_sgt);
|
|
if (uhwctx->restore_sgt)
|
|
nvhost_memmgr_unpin(hwctx->memmgr, uhwctx->restore,
|
|
&hwctx->channel->dev->dev, uhwctx->restore_sgt);
|
|
|
|
if (uhwctx->save_buf)
|
|
nvhost_memmgr_put(hwctx->memmgr, uhwctx->save_buf);
|
|
|
|
if (uhwctx->restore)
|
|
nvhost_memmgr_put(hwctx->memmgr, uhwctx->restore);
|
|
|
|
nvhost_memmgr_put_mgr(hwctx->memmgr);
|
|
kfree(uhwctx);
|
|
}
|
|
|
|
static void user_hwctx_put(struct nvhost_hwctx *ctx)
|
|
{
|
|
kref_put(&ctx->ref, user_hwctx_free);
|
|
}
|
|
|
|
static void user_hwctx_get(struct nvhost_hwctx *ctx)
|
|
{
|
|
kref_get(&ctx->ref);
|
|
}
|
|
|
|
static struct nvhost_hwctx *user_hwctx_alloc(struct nvhost_hwctx_handler *h,
|
|
struct nvhost_channel *ch)
|
|
{
|
|
struct user_hwctx *hwctx;
|
|
|
|
hwctx = kzalloc(sizeof(*hwctx), GFP_KERNEL);
|
|
|
|
if (!hwctx)
|
|
return NULL;
|
|
|
|
kref_init(&hwctx->hwctx.ref);
|
|
hwctx->hwctx.h = h;
|
|
hwctx->hwctx.channel = ch;
|
|
hwctx->hwctx.valid = false;
|
|
hwctx->hwctx.save_slots = 1;
|
|
|
|
return &hwctx->hwctx;
|
|
}
|
|
|
|
int user_hwctx_set_save(struct user_hwctx *ctx,
|
|
ulong mem, u32 offset, u32 words, struct nvhost_reloc *reloc)
|
|
{
|
|
struct mem_handle *buf;
|
|
struct sg_table *sgt;
|
|
void *page_addr;
|
|
|
|
/* First the restore buffer is set, then the save buffer */
|
|
if (!ctx->restore || !ctx->restore_sgt)
|
|
return -EINVAL;
|
|
|
|
buf = nvhost_memmgr_get(ctx->hwctx.memmgr,
|
|
mem, ctx->hwctx.channel->dev);
|
|
if (IS_ERR(buf))
|
|
return PTR_ERR(buf);
|
|
|
|
sgt = nvhost_memmgr_pin(ctx->hwctx.memmgr, buf,
|
|
&ctx->hwctx.channel->dev->dev, mem_flag_none);
|
|
if (IS_ERR(sgt))
|
|
return PTR_ERR(sgt);
|
|
|
|
ctx->save_offset = offset;
|
|
ctx->save_size = words;
|
|
ctx->save_buf = buf;
|
|
ctx->save_sgt = sgt;
|
|
|
|
/* Patch restore buffer address into save buffer */
|
|
page_addr = nvhost_memmgr_kmap(ctx->save_buf,
|
|
reloc->cmdbuf_offset >> PAGE_SHIFT);
|
|
if (!page_addr)
|
|
return -ENOMEM;
|
|
|
|
__raw_writel(nvhost_memmgr_dma_addr(ctx->restore_sgt) + offset,
|
|
page_addr + (reloc->cmdbuf_offset & ~PAGE_MASK));
|
|
nvhost_memmgr_kunmap(ctx->save_buf,
|
|
reloc->cmdbuf_offset >> PAGE_SHIFT,
|
|
page_addr);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int user_hwctx_set_restore(struct user_hwctx *ctx,
|
|
ulong mem, u32 offset, u32 words)
|
|
{
|
|
struct mem_handle *buf;
|
|
struct sg_table *sgt;
|
|
|
|
buf = nvhost_memmgr_get(ctx->hwctx.memmgr,
|
|
mem, ctx->hwctx.channel->dev);
|
|
if (IS_ERR(buf))
|
|
return PTR_ERR(buf);
|
|
|
|
sgt = nvhost_memmgr_pin(ctx->hwctx.memmgr, buf,
|
|
&ctx->hwctx.channel->dev->dev, mem_flag_none);
|
|
if (IS_ERR(sgt))
|
|
return PTR_ERR(sgt);
|
|
|
|
ctx->restore_offset = offset;
|
|
ctx->restore_size = words;
|
|
ctx->restore = buf;
|
|
ctx->restore_sgt = sgt;
|
|
|
|
return 0;
|
|
}
|
|
|
|
struct nvhost_hwctx_handler *user_ctxhandler_init(u32 syncpt,
|
|
u32 waitbase, struct nvhost_channel *ch)
|
|
{
|
|
struct user_hwctx_handler *p;
|
|
|
|
p = kzalloc(sizeof(*p), GFP_KERNEL);
|
|
if (!p)
|
|
return NULL;
|
|
|
|
p->h.syncpt = syncpt;
|
|
p->h.waitbase = waitbase;
|
|
|
|
p->h.alloc = user_hwctx_alloc;
|
|
p->h.save_push = user_hwctx_save_push;
|
|
p->h.restore_push = user_hwctx_restore_push;
|
|
p->h.get = user_hwctx_get;
|
|
p->h.put = user_hwctx_put;
|
|
|
|
return &p->h;
|
|
}
|
|
|
|
void user_ctxhandler_free(struct nvhost_hwctx_handler *h)
|
|
{
|
|
kfree(to_user_hwctx_handler(h));
|
|
}
|