mirror of
git://nv-tegra.nvidia.com/linux-nvgpu.git
synced 2025-12-22 17:36:20 +03:00
The nvgpu driver now supports using the Tegra graphics virtualization interfaces to support gk20a in a virtualized environment. Bug 1509608 Change-Id: I6ede15ee7bf0b0ad8a13e8eb5f557c3516ead676 Signed-off-by: Aingara Paramakuru <aparamakuru@nvidia.com> Reviewed-on: http://git-master/r/440122 Reviewed-by: Terje Bergstrom <tbergstrom@nvidia.com> Tested-by: Terje Bergstrom <tbergstrom@nvidia.com>
688 lines
17 KiB
C
688 lines
17 KiB
C
/*
|
|
* Virtualized GPU Graphics
|
|
*
|
|
* Copyright (c) 2014 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 "vgpu/vgpu.h"
|
|
#include "gk20a/hw_gr_gk20a.h"
|
|
|
|
static int vgpu_gr_commit_inst(struct channel_gk20a *c, u64 gpu_va)
|
|
{
|
|
struct gk20a_platform *platform = gk20a_get_platform(c->g->dev);
|
|
struct tegra_vgpu_cmd_msg msg;
|
|
struct tegra_vgpu_gr_ctx_params *p = &msg.params.gr_ctx;
|
|
int err;
|
|
|
|
gk20a_dbg_fn("");
|
|
|
|
msg.cmd = TEGRA_VGPU_CMD_CHANNEL_COMMIT_GR_CTX;
|
|
msg.handle = platform->virt_handle;
|
|
p->handle = c->virt_ctx;
|
|
err = vgpu_comm_sendrecv(&msg, sizeof(msg), sizeof(msg));
|
|
|
|
return (err || msg.ret) ? -1 : 0;
|
|
}
|
|
|
|
static int vgpu_gr_commit_global_ctx_buffers(struct gk20a *g,
|
|
struct channel_gk20a *c, bool patch)
|
|
{
|
|
struct gk20a_platform *platform = gk20a_get_platform(g->dev);
|
|
struct tegra_vgpu_cmd_msg msg;
|
|
struct tegra_vgpu_gr_ctx_params *p = &msg.params.gr_ctx;
|
|
int err;
|
|
|
|
gk20a_dbg_fn("");
|
|
|
|
msg.cmd = TEGRA_VGPU_CMD_CHANNEL_COMMIT_GR_GLOBAL_CTX;
|
|
msg.handle = platform->virt_handle;
|
|
p->handle = c->virt_ctx;
|
|
err = vgpu_comm_sendrecv(&msg, sizeof(msg), sizeof(msg));
|
|
|
|
return (err || msg.ret) ? -1 : 0;
|
|
}
|
|
|
|
/* load saved fresh copy of gloden image into channel gr_ctx */
|
|
static int vgpu_gr_load_golden_ctx_image(struct gk20a *g,
|
|
struct channel_gk20a *c)
|
|
{
|
|
struct gk20a_platform *platform = gk20a_get_platform(g->dev);
|
|
struct tegra_vgpu_cmd_msg msg;
|
|
struct tegra_vgpu_gr_ctx_params *p = &msg.params.gr_ctx;
|
|
int err;
|
|
|
|
gk20a_dbg_fn("");
|
|
|
|
msg.cmd = TEGRA_VGPU_CMD_CHANNEL_LOAD_GR_GOLDEN_CTX;
|
|
msg.handle = platform->virt_handle;
|
|
p->handle = c->virt_ctx;
|
|
err = vgpu_comm_sendrecv(&msg, sizeof(msg), sizeof(msg));
|
|
|
|
return (err || msg.ret) ? -1 : 0;
|
|
}
|
|
|
|
static int vgpu_gr_init_ctx_state(struct gk20a *g, struct gr_gk20a *gr)
|
|
{
|
|
struct gk20a_platform *platform = gk20a_get_platform(g->dev);
|
|
|
|
gk20a_dbg_fn("");
|
|
|
|
vgpu_get_attribute(platform->virt_handle,
|
|
TEGRA_VGPU_ATTRIB_GOLDEN_CTX_SIZE,
|
|
&g->gr.ctx_vars.golden_image_size);
|
|
vgpu_get_attribute(platform->virt_handle,
|
|
TEGRA_VGPU_ATTRIB_ZCULL_CTX_SIZE,
|
|
&g->gr.ctx_vars.zcull_ctxsw_image_size);
|
|
if (!g->gr.ctx_vars.golden_image_size ||
|
|
!g->gr.ctx_vars.zcull_ctxsw_image_size)
|
|
return -ENXIO;
|
|
|
|
gr->ctx_vars.buffer_size = g->gr.ctx_vars.golden_image_size;
|
|
g->gr.ctx_vars.priv_access_map_size = 512 * 1024;
|
|
return 0;
|
|
}
|
|
|
|
static int vgpu_gr_alloc_global_ctx_buffers(struct gk20a *g)
|
|
{
|
|
struct gr_gk20a *gr = &g->gr;
|
|
int attr_buffer_size;
|
|
|
|
u32 cb_buffer_size = gr->bundle_cb_default_size *
|
|
gr_scc_bundle_cb_size_div_256b_byte_granularity_v();
|
|
|
|
u32 pagepool_buffer_size = gr_scc_pagepool_total_pages_hwmax_value_v() *
|
|
gr_scc_pagepool_total_pages_byte_granularity_v();
|
|
|
|
gk20a_dbg_fn("");
|
|
|
|
attr_buffer_size = g->ops.gr.calc_global_ctx_buffer_size(g);
|
|
|
|
gk20a_dbg_info("cb_buffer_size : %d", cb_buffer_size);
|
|
gr->global_ctx_buffer[CIRCULAR].size = cb_buffer_size;
|
|
|
|
gk20a_dbg_info("pagepool_buffer_size : %d", pagepool_buffer_size);
|
|
gr->global_ctx_buffer[PAGEPOOL].size = pagepool_buffer_size;
|
|
|
|
gk20a_dbg_info("attr_buffer_size : %d", attr_buffer_size);
|
|
gr->global_ctx_buffer[ATTRIBUTE].size = attr_buffer_size;
|
|
|
|
gk20a_dbg_info("priv access map size : %d",
|
|
gr->ctx_vars.priv_access_map_size);
|
|
gr->global_ctx_buffer[PRIV_ACCESS_MAP].size =
|
|
gr->ctx_vars.priv_access_map_size;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int vgpu_gr_map_global_ctx_buffers(struct gk20a *g,
|
|
struct channel_gk20a *c)
|
|
{
|
|
struct gk20a_platform *platform = gk20a_get_platform(g->dev);
|
|
struct tegra_vgpu_cmd_msg msg;
|
|
struct tegra_vgpu_gr_ctx_params *p = &msg.params.gr_ctx;
|
|
struct vm_gk20a *ch_vm = c->vm;
|
|
u64 *g_bfr_va = c->ch_ctx.global_ctx_buffer_va;
|
|
u64 *g_bfr_size = c->ch_ctx.global_ctx_buffer_size;
|
|
struct gr_gk20a *gr = &g->gr;
|
|
u64 gpu_va;
|
|
u32 i;
|
|
int err;
|
|
|
|
gk20a_dbg_fn("");
|
|
|
|
/* FIXME: add VPR support */
|
|
|
|
/* Circular Buffer */
|
|
gpu_va = gk20a_vm_alloc_va(ch_vm,
|
|
gr->global_ctx_buffer[CIRCULAR].size, 0);
|
|
|
|
if (!gpu_va)
|
|
goto clean_up;
|
|
g_bfr_va[CIRCULAR_VA] = gpu_va;
|
|
g_bfr_size[CIRCULAR_VA] = gr->global_ctx_buffer[CIRCULAR].size;
|
|
|
|
/* Attribute Buffer */
|
|
gpu_va = gk20a_vm_alloc_va(ch_vm,
|
|
gr->global_ctx_buffer[ATTRIBUTE].size, 0);
|
|
|
|
if (!gpu_va)
|
|
goto clean_up;
|
|
g_bfr_va[ATTRIBUTE_VA] = gpu_va;
|
|
g_bfr_size[ATTRIBUTE_VA] = gr->global_ctx_buffer[ATTRIBUTE].size;
|
|
|
|
/* Page Pool */
|
|
gpu_va = gk20a_vm_alloc_va(ch_vm,
|
|
gr->global_ctx_buffer[PAGEPOOL].size, 0);
|
|
if (!gpu_va)
|
|
goto clean_up;
|
|
g_bfr_va[PAGEPOOL_VA] = gpu_va;
|
|
g_bfr_size[PAGEPOOL_VA] = gr->global_ctx_buffer[PAGEPOOL].size;
|
|
|
|
/* Priv register Access Map */
|
|
gpu_va = gk20a_vm_alloc_va(ch_vm,
|
|
gr->global_ctx_buffer[PRIV_ACCESS_MAP].size, 0);
|
|
if (!gpu_va)
|
|
goto clean_up;
|
|
g_bfr_va[PRIV_ACCESS_MAP_VA] = gpu_va;
|
|
g_bfr_size[PRIV_ACCESS_MAP_VA] =
|
|
gr->global_ctx_buffer[PRIV_ACCESS_MAP].size;
|
|
|
|
msg.cmd = TEGRA_VGPU_CMD_CHANNEL_MAP_GR_GLOBAL_CTX;
|
|
msg.handle = platform->virt_handle;
|
|
p->handle = c->virt_ctx;
|
|
p->cb_va = g_bfr_va[CIRCULAR_VA];
|
|
p->attr_va = g_bfr_va[ATTRIBUTE_VA];
|
|
p->page_pool_va = g_bfr_va[PAGEPOOL_VA];
|
|
p->priv_access_map_va = g_bfr_va[PRIV_ACCESS_MAP_VA];
|
|
err = vgpu_comm_sendrecv(&msg, sizeof(msg), sizeof(msg));
|
|
if (err || msg.ret)
|
|
goto clean_up;
|
|
|
|
c->ch_ctx.global_ctx_buffer_mapped = true;
|
|
return 0;
|
|
|
|
clean_up:
|
|
for (i = 0; i < NR_GLOBAL_CTX_BUF_VA; i++) {
|
|
if (g_bfr_va[i]) {
|
|
gk20a_vm_free_va(ch_vm, g_bfr_va[i],
|
|
g_bfr_size[i], 0);
|
|
g_bfr_va[i] = 0;
|
|
}
|
|
}
|
|
return -ENOMEM;
|
|
}
|
|
|
|
static void vgpu_gr_unmap_global_ctx_buffers(struct channel_gk20a *c)
|
|
{
|
|
struct gk20a_platform *platform = gk20a_get_platform(c->g->dev);
|
|
struct vm_gk20a *ch_vm = c->vm;
|
|
u64 *g_bfr_va = c->ch_ctx.global_ctx_buffer_va;
|
|
u64 *g_bfr_size = c->ch_ctx.global_ctx_buffer_size;
|
|
u32 i;
|
|
|
|
gk20a_dbg_fn("");
|
|
|
|
if (c->ch_ctx.global_ctx_buffer_mapped) {
|
|
struct tegra_vgpu_cmd_msg msg;
|
|
struct tegra_vgpu_gr_ctx_params *p = &msg.params.gr_ctx;
|
|
int err;
|
|
|
|
msg.cmd = TEGRA_VGPU_CMD_CHANNEL_UNMAP_GR_GLOBAL_CTX;
|
|
msg.handle = platform->virt_handle;
|
|
p->handle = c->virt_ctx;
|
|
err = vgpu_comm_sendrecv(&msg, sizeof(msg), sizeof(msg));
|
|
WARN_ON(err || msg.ret);
|
|
}
|
|
|
|
for (i = 0; i < NR_GLOBAL_CTX_BUF_VA; i++) {
|
|
if (g_bfr_va[i]) {
|
|
gk20a_vm_free_va(ch_vm, g_bfr_va[i], g_bfr_size[i], 0);
|
|
g_bfr_va[i] = 0;
|
|
g_bfr_size[i] = 0;
|
|
}
|
|
}
|
|
c->ch_ctx.global_ctx_buffer_mapped = false;
|
|
}
|
|
|
|
static int vgpu_gr_alloc_channel_gr_ctx(struct gk20a *g,
|
|
struct channel_gk20a *c)
|
|
{
|
|
struct gk20a_platform *platform = gk20a_get_platform(g->dev);
|
|
struct tegra_vgpu_cmd_msg msg;
|
|
struct tegra_vgpu_gr_ctx_params *p = &msg.params.gr_ctx;
|
|
struct gr_gk20a *gr = &g->gr;
|
|
struct gr_ctx_desc *gr_ctx;
|
|
struct vm_gk20a *ch_vm = c->vm;
|
|
int err;
|
|
|
|
gk20a_dbg_fn("");
|
|
|
|
if (gr->ctx_vars.buffer_size == 0)
|
|
return 0;
|
|
|
|
/* alloc channel gr ctx buffer */
|
|
gr->ctx_vars.buffer_size = gr->ctx_vars.golden_image_size;
|
|
gr->ctx_vars.buffer_total_size = gr->ctx_vars.golden_image_size;
|
|
|
|
gr_ctx = kzalloc(sizeof(*gr_ctx), GFP_KERNEL);
|
|
if (!gr_ctx)
|
|
return -ENOMEM;
|
|
|
|
gr_ctx->size = gr->ctx_vars.buffer_total_size;
|
|
gr_ctx->gpu_va = gk20a_vm_alloc_va(ch_vm, gr_ctx->size, 0);
|
|
|
|
if (!gr_ctx->gpu_va) {
|
|
kfree(gr_ctx);
|
|
return -ENOMEM;
|
|
}
|
|
|
|
msg.cmd = TEGRA_VGPU_CMD_CHANNEL_ALLOC_GR_CTX;
|
|
msg.handle = platform->virt_handle;
|
|
p->handle = c->virt_ctx;
|
|
p->gr_ctx_va = gr_ctx->gpu_va;
|
|
p->class_num = c->obj_class;
|
|
err = vgpu_comm_sendrecv(&msg, sizeof(msg), sizeof(msg));
|
|
|
|
if (err || msg.ret) {
|
|
gk20a_vm_free_va(ch_vm, gr_ctx->gpu_va, gr_ctx->size, 0);
|
|
err = -ENOMEM;
|
|
} else
|
|
c->ch_ctx.gr_ctx = gr_ctx;
|
|
|
|
return err;
|
|
}
|
|
|
|
static void vgpu_gr_free_channel_gr_ctx(struct channel_gk20a *c)
|
|
{
|
|
struct gk20a_platform *platform = gk20a_get_platform(c->g->dev);
|
|
struct channel_ctx_gk20a *ch_ctx = &c->ch_ctx;
|
|
struct vm_gk20a *ch_vm = c->vm;
|
|
|
|
gk20a_dbg_fn("");
|
|
|
|
if (ch_ctx->gr_ctx && ch_ctx->gr_ctx->gpu_va) {
|
|
struct tegra_vgpu_cmd_msg msg;
|
|
struct tegra_vgpu_gr_ctx_params *p = &msg.params.gr_ctx;
|
|
int err;
|
|
|
|
msg.cmd = TEGRA_VGPU_CMD_CHANNEL_FREE_GR_CTX;
|
|
msg.handle = platform->virt_handle;
|
|
p->handle = c->virt_ctx;
|
|
err = vgpu_comm_sendrecv(&msg, sizeof(msg), sizeof(msg));
|
|
WARN_ON(err || msg.ret);
|
|
|
|
gk20a_vm_free_va(ch_vm, ch_ctx->gr_ctx->gpu_va,
|
|
ch_ctx->gr_ctx->size, 0);
|
|
ch_ctx->gr_ctx->gpu_va = 0;
|
|
kfree(ch_ctx->gr_ctx);
|
|
}
|
|
}
|
|
|
|
static int vgpu_gr_alloc_channel_patch_ctx(struct gk20a *g,
|
|
struct channel_gk20a *c)
|
|
{
|
|
struct gk20a_platform *platform = gk20a_get_platform(g->dev);
|
|
struct patch_desc *patch_ctx = &c->ch_ctx.patch_ctx;
|
|
struct vm_gk20a *ch_vm = c->vm;
|
|
struct tegra_vgpu_cmd_msg msg;
|
|
struct tegra_vgpu_gr_ctx_params *p = &msg.params.gr_ctx;
|
|
int err;
|
|
|
|
gk20a_dbg_fn("");
|
|
|
|
patch_ctx->size = 128 * sizeof(u32);
|
|
patch_ctx->gpu_va = gk20a_vm_alloc_va(ch_vm, patch_ctx->size, 0);
|
|
if (!patch_ctx->gpu_va)
|
|
return -ENOMEM;
|
|
|
|
msg.cmd = TEGRA_VGPU_CMD_CHANNEL_ALLOC_GR_PATCH_CTX;
|
|
msg.handle = platform->virt_handle;
|
|
p->handle = c->virt_ctx;
|
|
p->patch_ctx_va = patch_ctx->gpu_va;
|
|
err = vgpu_comm_sendrecv(&msg, sizeof(msg), sizeof(msg));
|
|
if (err || msg.ret) {
|
|
gk20a_vm_free_va(ch_vm, patch_ctx->gpu_va, patch_ctx->size, 0);
|
|
err = -ENOMEM;
|
|
}
|
|
|
|
return err;
|
|
}
|
|
|
|
static void vgpu_gr_free_channel_patch_ctx(struct channel_gk20a *c)
|
|
{
|
|
struct gk20a_platform *platform = gk20a_get_platform(c->g->dev);
|
|
struct patch_desc *patch_ctx = &c->ch_ctx.patch_ctx;
|
|
struct vm_gk20a *ch_vm = c->vm;
|
|
|
|
gk20a_dbg_fn("");
|
|
|
|
if (patch_ctx->gpu_va) {
|
|
struct tegra_vgpu_cmd_msg msg;
|
|
struct tegra_vgpu_gr_ctx_params *p = &msg.params.gr_ctx;
|
|
int err;
|
|
|
|
msg.cmd = TEGRA_VGPU_CMD_CHANNEL_FREE_GR_PATCH_CTX;
|
|
msg.handle = platform->virt_handle;
|
|
p->handle = c->virt_ctx;
|
|
err = vgpu_comm_sendrecv(&msg, sizeof(msg), sizeof(msg));
|
|
WARN_ON(err || msg.ret);
|
|
|
|
gk20a_vm_free_va(ch_vm, patch_ctx->gpu_va, patch_ctx->size, 0);
|
|
patch_ctx->gpu_va = 0;
|
|
}
|
|
}
|
|
|
|
static void vgpu_gr_free_channel_ctx(struct channel_gk20a *c)
|
|
{
|
|
gk20a_dbg_fn("");
|
|
|
|
vgpu_gr_unmap_global_ctx_buffers(c);
|
|
vgpu_gr_free_channel_patch_ctx(c);
|
|
if (!gk20a_is_channel_marked_as_tsg(c))
|
|
vgpu_gr_free_channel_gr_ctx(c);
|
|
|
|
/* zcull_ctx, pm_ctx */
|
|
|
|
memset(&c->ch_ctx, 0, sizeof(struct channel_ctx_gk20a));
|
|
|
|
c->num_objects = 0;
|
|
c->first_init = false;
|
|
}
|
|
|
|
static int vgpu_gr_alloc_obj_ctx(struct channel_gk20a *c,
|
|
struct nvhost_alloc_obj_ctx_args *args)
|
|
{
|
|
struct gk20a *g = c->g;
|
|
struct fifo_gk20a *f = &g->fifo;
|
|
struct channel_ctx_gk20a *ch_ctx = &c->ch_ctx;
|
|
struct tsg_gk20a *tsg = NULL;
|
|
int err = 0;
|
|
|
|
gk20a_dbg_fn("");
|
|
|
|
/* an address space needs to have been bound at this point.*/
|
|
if (!gk20a_channel_as_bound(c)) {
|
|
gk20a_err(dev_from_gk20a(g),
|
|
"not bound to address space at time"
|
|
" of grctx allocation");
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (!g->ops.gr.is_valid_class(g, args->class_num)) {
|
|
gk20a_err(dev_from_gk20a(g),
|
|
"invalid obj class 0x%x", args->class_num);
|
|
err = -EINVAL;
|
|
goto out;
|
|
}
|
|
c->obj_class = args->class_num;
|
|
|
|
/* FIXME: add TSG support */
|
|
if (gk20a_is_channel_marked_as_tsg(c))
|
|
tsg = &f->tsg[c->tsgid];
|
|
|
|
/* allocate gr ctx buffer */
|
|
if (!ch_ctx->gr_ctx) {
|
|
err = vgpu_gr_alloc_channel_gr_ctx(g, c);
|
|
if (err) {
|
|
gk20a_err(dev_from_gk20a(g),
|
|
"fail to allocate gr ctx buffer");
|
|
goto out;
|
|
}
|
|
} else {
|
|
/*TBD: needs to be more subtle about which is
|
|
* being allocated as some are allowed to be
|
|
* allocated along same channel */
|
|
gk20a_err(dev_from_gk20a(g),
|
|
"too many classes alloc'd on same channel");
|
|
err = -EINVAL;
|
|
goto out;
|
|
}
|
|
|
|
/* commit gr ctx buffer */
|
|
err = vgpu_gr_commit_inst(c, ch_ctx->gr_ctx->gpu_va);
|
|
if (err) {
|
|
gk20a_err(dev_from_gk20a(g),
|
|
"fail to commit gr ctx buffer");
|
|
goto out;
|
|
}
|
|
|
|
/* allocate patch buffer */
|
|
if (ch_ctx->patch_ctx.pages == NULL) {
|
|
err = vgpu_gr_alloc_channel_patch_ctx(g, c);
|
|
if (err) {
|
|
gk20a_err(dev_from_gk20a(g),
|
|
"fail to allocate patch buffer");
|
|
goto out;
|
|
}
|
|
}
|
|
|
|
/* map global buffer to channel gpu_va and commit */
|
|
if (!ch_ctx->global_ctx_buffer_mapped) {
|
|
err = vgpu_gr_map_global_ctx_buffers(g, c);
|
|
if (err) {
|
|
gk20a_err(dev_from_gk20a(g),
|
|
"fail to map global ctx buffer");
|
|
goto out;
|
|
}
|
|
gr_gk20a_elpg_protected_call(g,
|
|
vgpu_gr_commit_global_ctx_buffers(g, c, true));
|
|
}
|
|
|
|
/* load golden image */
|
|
if (!c->first_init) {
|
|
err = gr_gk20a_elpg_protected_call(g,
|
|
vgpu_gr_load_golden_ctx_image(g, c));
|
|
if (err) {
|
|
gk20a_err(dev_from_gk20a(g),
|
|
"fail to load golden ctx image");
|
|
goto out;
|
|
}
|
|
c->first_init = true;
|
|
}
|
|
|
|
c->num_objects++;
|
|
|
|
gk20a_dbg_fn("done");
|
|
return 0;
|
|
out:
|
|
/* 1. gr_ctx, patch_ctx and global ctx buffer mapping
|
|
can be reused so no need to release them.
|
|
2. golden image load is a one time thing so if
|
|
they pass, no need to undo. */
|
|
gk20a_err(dev_from_gk20a(g), "fail");
|
|
return err;
|
|
}
|
|
|
|
static int vgpu_gr_free_obj_ctx(struct channel_gk20a *c,
|
|
struct nvhost_free_obj_ctx_args *args)
|
|
{
|
|
unsigned long timeout = gk20a_get_gr_idle_timeout(c->g);
|
|
|
|
gk20a_dbg_fn("");
|
|
|
|
if (c->num_objects == 0)
|
|
return 0;
|
|
|
|
c->num_objects--;
|
|
|
|
if (c->num_objects == 0) {
|
|
c->first_init = false;
|
|
gk20a_disable_channel(c,
|
|
!c->has_timedout,
|
|
timeout);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int vgpu_gr_init_gr_config(struct gk20a *g, struct gr_gk20a *gr)
|
|
{
|
|
struct gk20a_platform *platform = gk20a_get_platform(g->dev);
|
|
|
|
gk20a_dbg_fn("");
|
|
|
|
if (vgpu_get_attribute(platform->virt_handle,
|
|
TEGRA_VGPU_ATTRIB_GPC_COUNT, &gr->gpc_count))
|
|
return -ENOMEM;
|
|
|
|
if (vgpu_get_attribute(platform->virt_handle,
|
|
TEGRA_VGPU_ATTRIB_MAX_TPC_PER_GPC_COUNT,
|
|
&gr->max_tpc_per_gpc_count))
|
|
return -ENOMEM;
|
|
|
|
if (vgpu_get_attribute(platform->virt_handle,
|
|
TEGRA_VGPU_ATTRIB_MAX_TPC_COUNT,
|
|
&gr->max_tpc_count))
|
|
return -ENOMEM;
|
|
|
|
g->ops.gr.bundle_cb_defaults(g);
|
|
g->ops.gr.cb_size_default(g);
|
|
g->ops.gr.calc_global_ctx_buffer_size(g);
|
|
return 0;
|
|
}
|
|
|
|
static int vgpu_gr_bind_ctxsw_zcull(struct gk20a *g, struct gr_gk20a *gr,
|
|
struct channel_gk20a *c, u64 zcull_va,
|
|
u32 mode)
|
|
{
|
|
struct gk20a_platform *platform = gk20a_get_platform(g->dev);
|
|
struct tegra_vgpu_cmd_msg msg;
|
|
struct tegra_vgpu_zcull_bind_params *p = &msg.params.zcull_bind;
|
|
int err;
|
|
|
|
gk20a_dbg_fn("");
|
|
|
|
msg.cmd = TEGRA_VGPU_CMD_CHANNEL_BIND_ZCULL;
|
|
msg.handle = platform->virt_handle;
|
|
p->handle = c->virt_ctx;
|
|
p->zcull_va = zcull_va;
|
|
p->mode = mode;
|
|
err = vgpu_comm_sendrecv(&msg, sizeof(msg), sizeof(msg));
|
|
|
|
return (err || msg.ret) ? -ENOMEM : 0;
|
|
}
|
|
|
|
static int vgpu_gr_get_zcull_info(struct gk20a *g, struct gr_gk20a *gr,
|
|
struct gr_zcull_info *zcull_params)
|
|
{
|
|
struct gk20a_platform *platform = gk20a_get_platform(g->dev);
|
|
struct tegra_vgpu_cmd_msg msg;
|
|
struct tegra_vgpu_zcull_info_params *p = &msg.params.zcull_info;
|
|
int err;
|
|
|
|
gk20a_dbg_fn("");
|
|
|
|
msg.cmd = TEGRA_VGPU_CMD_GET_ZCULL_INFO;
|
|
msg.handle = platform->virt_handle;
|
|
err = vgpu_comm_sendrecv(&msg, sizeof(msg), sizeof(msg));
|
|
if (err || msg.ret)
|
|
return -ENOMEM;
|
|
|
|
zcull_params->width_align_pixels = p->width_align_pixels;
|
|
zcull_params->height_align_pixels = p->height_align_pixels;
|
|
zcull_params->pixel_squares_by_aliquots = p->pixel_squares_by_aliquots;
|
|
zcull_params->aliquot_total = p->aliquot_total;
|
|
zcull_params->region_byte_multiplier = p->region_byte_multiplier;
|
|
zcull_params->region_header_size = p->region_header_size;
|
|
zcull_params->subregion_header_size = p->subregion_header_size;
|
|
zcull_params->subregion_width_align_pixels =
|
|
p->subregion_width_align_pixels;
|
|
zcull_params->subregion_height_align_pixels =
|
|
p->subregion_height_align_pixels;
|
|
zcull_params->subregion_count = p->subregion_count;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void vgpu_remove_gr_support(struct gr_gk20a *gr)
|
|
{
|
|
gk20a_dbg_fn("");
|
|
|
|
gk20a_allocator_destroy(&gr->comp_tags);
|
|
}
|
|
|
|
static int vgpu_gr_init_gr_setup_sw(struct gk20a *g)
|
|
{
|
|
struct gr_gk20a *gr = &g->gr;
|
|
int err;
|
|
|
|
gk20a_dbg_fn("");
|
|
|
|
if (gr->sw_ready) {
|
|
gk20a_dbg_fn("skip init");
|
|
return 0;
|
|
}
|
|
|
|
gr->g = g;
|
|
|
|
err = vgpu_gr_init_gr_config(g, gr);
|
|
if (err)
|
|
goto clean_up;
|
|
|
|
err = vgpu_gr_init_ctx_state(g, gr);
|
|
if (err)
|
|
goto clean_up;
|
|
|
|
err = g->ops.ltc.init_comptags(g, gr);
|
|
if (err)
|
|
goto clean_up;
|
|
|
|
err = vgpu_gr_alloc_global_ctx_buffers(g);
|
|
if (err)
|
|
goto clean_up;
|
|
|
|
mutex_init(&gr->ctx_mutex);
|
|
|
|
gr->remove_support = vgpu_remove_gr_support;
|
|
gr->sw_ready = true;
|
|
|
|
gk20a_dbg_fn("done");
|
|
return 0;
|
|
|
|
clean_up:
|
|
gk20a_err(dev_from_gk20a(g), "fail");
|
|
vgpu_remove_gr_support(gr);
|
|
return err;
|
|
}
|
|
|
|
int vgpu_init_gr_support(struct gk20a *g)
|
|
{
|
|
gk20a_dbg_fn("");
|
|
|
|
return vgpu_gr_init_gr_setup_sw(g);
|
|
}
|
|
|
|
struct gr_isr_data {
|
|
u32 addr;
|
|
u32 data_lo;
|
|
u32 data_hi;
|
|
u32 curr_ctx;
|
|
u32 chid;
|
|
u32 offset;
|
|
u32 sub_chan;
|
|
u32 class_num;
|
|
};
|
|
|
|
static int vgpu_gr_handle_notify_pending(struct gk20a *g,
|
|
struct gr_isr_data *isr_data)
|
|
{
|
|
struct fifo_gk20a *f = &g->fifo;
|
|
struct channel_gk20a *ch = &f->channel[isr_data->chid];
|
|
|
|
gk20a_dbg_fn("");
|
|
wake_up(&ch->notifier_wq);
|
|
return 0;
|
|
}
|
|
|
|
int vgpu_gr_isr(struct gk20a *g, struct tegra_vgpu_gr_intr_info *info)
|
|
{
|
|
struct gr_isr_data isr_data;
|
|
|
|
gk20a_dbg_fn("");
|
|
|
|
isr_data.chid = info->chid;
|
|
|
|
if (info->type == TEGRA_VGPU_GR_INTR_NOTIFY)
|
|
vgpu_gr_handle_notify_pending(g, &isr_data);
|
|
|
|
return 0;
|
|
}
|
|
|
|
void vgpu_init_gr_ops(struct gpu_ops *gops)
|
|
{
|
|
gops->gr.free_channel_ctx = vgpu_gr_free_channel_ctx;
|
|
gops->gr.alloc_obj_ctx = vgpu_gr_alloc_obj_ctx;
|
|
gops->gr.free_obj_ctx = vgpu_gr_free_obj_ctx;
|
|
gops->gr.bind_ctxsw_zcull = vgpu_gr_bind_ctxsw_zcull;
|
|
gops->gr.get_zcull_info = vgpu_gr_get_zcull_info;
|
|
}
|