Initial commit; kernel source import

This commit is contained in:
Nathan
2025-04-06 23:50:55 -05:00
commit 25c6d769f4
45093 changed files with 18199410 additions and 0 deletions

View File

@@ -0,0 +1,11 @@
GCOV_PROFILE := y
ccflags-y += -Idrivers/video/tegra/host
ccflags-y += -Idrivers/devfreq
nvhost-gr3d-objs = \
gr3d.o \
gr3d_t114.o \
scale3d.o \
pod_scaling.o
obj-$(CONFIG_TEGRA_GRHOST) += nvhost-gr3d.o

View File

@@ -0,0 +1,263 @@
/*
* drivers/video/tegra/host/gr3d/gr3d.c
*
* Tegra Graphics Host 3D
*
* Copyright (c) 2012-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/export.h>
#include <linux/module.h>
#include <linux/scatterlist.h>
#include <linux/pm_runtime.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/of_platform.h>
#include <linux/pm.h>
#include <linux/dma-buf.h>
#include <linux/syscalls.h>
#include <linux/platform_data/tegra_pm_domains.h>
#include "t114/t114.h"
#include "host1x/host1x01_hardware.h"
#include "nvhost_hwctx.h"
#include "nvhost_acm.h"
#include "dev.h"
#include "gr3d.h"
#include "gr3d_t114.h"
#include "scale3d.h"
#include "bus_client.h"
#include "nvhost_channel.h"
#include "chip_support.h"
#include "pod_scaling.h"
#include "class_ids.h"
#include "nvhost_job.h"
void nvhost_3dctx_restore_begin(struct host1x_hwctx_handler *p, u32 *ptr)
{
/* set class to host */
ptr[0] = nvhost_opcode_setclass(NV_HOST1X_CLASS_ID,
host1x_uclass_incr_syncpt_base_r(), 1);
/* increment sync point base */
ptr[1] = nvhost_class_host_incr_syncpt_base(p->h.waitbase,
p->restore_incrs);
/* set class to 3D */
ptr[2] = nvhost_opcode_setclass(NV_GRAPHICS_3D_CLASS_ID, 0, 0);
/* program PSEQ_QUAD_ID */
ptr[3] = nvhost_opcode_imm(AR3D_PSEQ_QUAD_ID, 0);
}
void nvhost_3dctx_restore_direct(u32 *ptr, u32 start_reg, u32 count)
{
ptr[0] = nvhost_opcode_incr(start_reg, count);
}
void nvhost_3dctx_restore_indirect(u32 *ptr, u32 offset_reg, u32 offset,
u32 data_reg, u32 count)
{
ptr[0] = nvhost_opcode_imm(offset_reg, offset);
ptr[1] = nvhost_opcode_nonincr(data_reg, count);
}
void nvhost_3dctx_restore_end(struct host1x_hwctx_handler *p, u32 *ptr)
{
/* syncpt increment to track restore gather. */
ptr[0] = nvhost_opcode_imm_incr_syncpt(
host1x_uclass_incr_syncpt_cond_op_done_v(), p->h.syncpt);
}
/*** ctx3d ***/
struct host1x_hwctx *nvhost_3dctx_alloc_common(struct host1x_hwctx_handler *p,
struct nvhost_channel *ch, bool mem_flag)
{
struct host1x_hwctx *ctx;
ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
if (!ctx)
return NULL;
if (mem_flag)
ctx->cpuva = dma_alloc_writecombine(&ch->dev->dev,
p->restore_size * 4,
&ctx->iova,
GFP_KERNEL);
else
ctx->cpuva = dma_alloc_coherent(&ch->dev->dev,
p->restore_size * 4,
&ctx->iova,
GFP_KERNEL);
if (!ctx->cpuva) {
dev_err(&ch->dev->dev, "memory allocation failed\n");
goto fail;
}
kref_init(&ctx->hwctx.ref);
ctx->hwctx.h = &p->h;
ctx->hwctx.channel = ch;
ctx->hwctx.valid = false;
ctx->hwctx.save_incrs = p->save_incrs;
ctx->hwctx.save_slots = p->save_slots;
ctx->restore_size = p->restore_size;
ctx->hwctx.restore_incrs = p->restore_incrs;
ctx->mem_flag = mem_flag;
return ctx;
fail:
kfree(ctx);
return NULL;
}
void nvhost_3dctx_restore_push(struct nvhost_hwctx *nctx,
struct nvhost_cdma *cdma)
{
struct host1x_hwctx *ctx = to_host1x_hwctx(nctx);
_nvhost_cdma_push_gather(cdma,
ctx->cpuva,
ctx->iova,
0,
nvhost_opcode_gather(ctx->restore_size),
ctx->iova);
}
void nvhost_3dctx_get(struct nvhost_hwctx *ctx)
{
kref_get(&ctx->ref);
}
void nvhost_3dctx_free(struct kref *ref)
{
struct nvhost_hwctx *nctx = container_of(ref, struct nvhost_hwctx, ref);
struct host1x_hwctx *ctx = to_host1x_hwctx(nctx);
if (ctx->cpuva) {
if (ctx->mem_flag)
dma_free_writecombine(&nctx->channel->dev->dev,
ctx->restore_size * 4,
ctx->cpuva,
ctx->iova);
else
dma_free_coherent(&nctx->channel->dev->dev,
ctx->restore_size * 4,
ctx->cpuva,
ctx->iova);
ctx->cpuva = NULL;
ctx->iova = 0;
}
kfree(ctx);
}
void nvhost_3dctx_put(struct nvhost_hwctx *ctx)
{
kref_put(&ctx->ref, nvhost_3dctx_free);
}
int nvhost_gr3d_prepare_power_off(struct platform_device *dev)
{
struct nvhost_device_data *pdata = platform_get_drvdata(dev);
return nvhost_channel_save_context(pdata->channel);
}
static struct of_device_id tegra_gr3d_of_match[] = {
#ifdef TEGRA_11X_OR_HIGHER_CONFIG
{ .compatible = "nvidia,tegra114-gr3d",
.data = (struct nvhost_device_data *)&t11_gr3d_info },
#endif
{ },
};
static int gr3d_probe(struct platform_device *dev)
{
int err = 0;
struct nvhost_device_data *pdata = NULL;
if (dev->dev.of_node) {
const struct of_device_id *match;
match = of_match_device(tegra_gr3d_of_match, &dev->dev);
if (match)
pdata = (struct nvhost_device_data *)match->data;
} else
pdata = (struct nvhost_device_data *)dev->dev.platform_data;
WARN_ON(!pdata);
if (!pdata) {
dev_info(&dev->dev, "no platform data\n");
return -ENODATA;
}
pdata->pdev = dev;
mutex_init(&pdata->lock);
platform_set_drvdata(dev, pdata);
err = nvhost_client_device_get_resources(dev);
if (err)
return err;
nvhost_module_init(dev);
#ifdef CONFIG_PM_GENERIC_DOMAINS
pdata->pd.name = "gr3d";
err = nvhost_module_add_domain(&pdata->pd, dev);
#endif
err = nvhost_client_device_init(dev);
return err;
}
static int __exit gr3d_remove(struct platform_device *dev)
{
#ifdef CONFIG_PM_RUNTIME
pm_runtime_put(&dev->dev);
pm_runtime_disable(&dev->dev);
#else
nvhost_module_disable_clk(&dev->dev);
#endif
return 0;
}
static struct platform_driver gr3d_driver = {
.probe = gr3d_probe,
.remove = __exit_p(gr3d_remove),
.driver = {
.owner = THIS_MODULE,
.name = "gr3d",
#ifdef CONFIG_OF
.of_match_table = tegra_gr3d_of_match,
#endif
#ifdef CONFIG_PM
.pm = &nvhost_module_pm_ops,
#endif
},
};
static int __init gr3d_init(void)
{
return platform_driver_register(&gr3d_driver);
}
static void __exit gr3d_exit(void)
{
platform_driver_unregister(&gr3d_driver);
}
module_init(gr3d_init);
module_exit(gr3d_exit);

View File

@@ -0,0 +1,62 @@
/*
* drivers/video/tegra/host/gr3d/gr3d.h
*
* Tegra Graphics Host 3D
*
* Copyright (c) 2011-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/>.
*/
#ifndef __NVHOST_GR3D_GR3D_H
#define __NVHOST_GR3D_GR3D_H
#include "host1x/host1x_hwctx.h"
#include <linux/types.h>
/* Registers of 3D unit */
#define AR3D_PSEQ_QUAD_ID 0x545
#define AR3D_DW_MEMORY_OUTPUT_ADDRESS 0x904
#define AR3D_DW_MEMORY_OUTPUT_DATA 0x905
#define AR3D_FDC_CONTROL_0 0xa00
#define AR3D_FDC_CONTROL_0_RESET_VAL 0xe00
#define AR3D_FDC_CONTROL_0_INVALIDATE 1
#define AR3D_GSHIM_WRITE_MASK 0xb00
#define AR3D_GSHIM_READ_SELECT 0xb01
#define AR3D_GLOBAL_MEMORY_OUTPUT_READS 0xe40
#define AR3D_PIPEALIAS_DW_MEMORY_OUTPUT_DATA 0xc10
#define AR3D_PIPEALIAS_DW_MEMORY_OUTPUT_INCR 0xc20
struct nvhost_hwctx;
struct nvhost_channel;
struct nvhost_cdma;
struct kref;
/* Functions used commonly by all 3D context switch modules */
void nvhost_3dctx_restore_begin(struct host1x_hwctx_handler *h, u32 *ptr);
void nvhost_3dctx_restore_direct(u32 *ptr, u32 start_reg, u32 count);
void nvhost_3dctx_restore_indirect(u32 *ptr, u32 offset_reg,
u32 offset, u32 data_reg, u32 count);
void nvhost_3dctx_restore_end(struct host1x_hwctx_handler *h, u32 *ptr);
void nvhost_3dctx_restore_push(struct nvhost_hwctx *ctx,
struct nvhost_cdma *cdma);
struct host1x_hwctx *nvhost_3dctx_alloc_common(
struct host1x_hwctx_handler *p,
struct nvhost_channel *ch, bool map_restore);
void nvhost_3dctx_get(struct nvhost_hwctx *ctx);
void nvhost_3dctx_free(struct kref *ref);
void nvhost_3dctx_put(struct nvhost_hwctx *ctx);
int nvhost_gr3d_prepare_power_off(struct platform_device *dev);
#endif

View File

@@ -0,0 +1,465 @@
/*
* drivers/video/tegra/host/t20/3dctx_t114.c
*
* Tegra Graphics Host 3d hardware context
*
* Copyright (c) 2011-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 "nvhost_hwctx.h"
#include "nvhost_channel.h"
#include "dev.h"
#include "host1x/host1x02_hardware.h"
#include "gr3d.h"
#include "chip_support.h"
#include "nvhost_scale.h"
#include "nvhost_job.h"
#include "nvhost_acm.h"
#include "class_ids.h"
#include <linux/slab.h>
#include <linux/dma-mapping.h>
#include <linux/scatterlist.h>
#include <linux/tegra-soc.h>
static const struct hwctx_reginfo ctxsave_regs_3d_per_pipe[] = {
HWCTX_REGINFO(0xc30, 1, DIRECT),
HWCTX_REGINFO(0xc40, 1, DIRECT),
HWCTX_REGINFO(0xc50, 1, DIRECT)
};
static const struct hwctx_reginfo ctxsave_regs_3d_global[] = {
/* bug 962360. Reg 0xe44 has to be the first one to be restored*/
HWCTX_REGINFO_RST(0x40e, 1, DIRECT, 0xe44),
HWCTX_REGINFO(0xe00, 35, DIRECT),
HWCTX_REGINFO(0xe25, 2, DIRECT),
HWCTX_REGINFO(0xe28, 2, DIRECT),
HWCTX_REGINFO(0x001, 2, DIRECT),
HWCTX_REGINFO(0x00c, 10, DIRECT),
HWCTX_REGINFO(0x100, 34, DIRECT),
HWCTX_REGINFO(0x124, 2, DIRECT),
HWCTX_REGINFO(0x200, 5, DIRECT),
HWCTX_REGINFO(0x205, 1024, INDIRECT),
HWCTX_REGINFO(0x207, 1120, INDIRECT),
HWCTX_REGINFO(0x209, 1, DIRECT),
HWCTX_REGINFO(0x20b, 1, DIRECT),
HWCTX_REGINFO(0x300, 64, DIRECT),
HWCTX_REGINFO(0x343, 25, DIRECT),
HWCTX_REGINFO(0x363, 2, DIRECT),
HWCTX_REGINFO(0x400, 3, DIRECT),
/* bug 976976 requires reg 0x403 to be restored before reg 0xe45 */
/* bug 972588 requires reg 0x403 to be restored with reg 0x411's
value */
HWCTX_REGINFO_RST(0x411, 1, DIRECT, 0x403),
HWCTX_REGINFO(0x404, 15, DIRECT),
/* bug 955371 requires reg 0x7e0 to be restored with 0x410,s value.
bug 982750 requires reg 0x7e0 to be restored before 0x804.
note: 0x803 is the offset reg for 0x804 */
HWCTX_REGINFO_RST(0x410, 1, DIRECT, 0x7e0),
HWCTX_REGINFO(0x414, 7, DIRECT),
HWCTX_REGINFO(0x434, 1, DIRECT),
HWCTX_REGINFO(0x500, 4, DIRECT),
HWCTX_REGINFO(0x520, 32, DIRECT),
HWCTX_REGINFO(0x540, 64, INDIRECT),
HWCTX_REGINFO(0x545, 1, DIRECT),
HWCTX_REGINFO(0x547, 1, DIRECT),
HWCTX_REGINFO(0x548, 64, INDIRECT),
/* bug 951938 requires that reg 601 should not be the last reg to be
saved */
HWCTX_REGINFO(0x600, 16, INDIRECT_4X),
HWCTX_REGINFO(0x603, 128, INDIRECT),
HWCTX_REGINFO(0x608, 4, DIRECT),
HWCTX_REGINFO(0x60e, 1, DIRECT),
HWCTX_REGINFO(0x700, 64, INDIRECT),
HWCTX_REGINFO(0x710, 50, DIRECT),
HWCTX_REGINFO(0x750, 16, DIRECT),
HWCTX_REGINFO(0x770, 48, DIRECT),
HWCTX_REGINFO(0x7e0, 1, DIRECT),
HWCTX_REGINFO(0x800, 64, INDIRECT),
/* bug 982750 requires 0x804 to be restored after reg 0x7e0 */
HWCTX_REGINFO(0x803, 1024, INDIRECT),
HWCTX_REGINFO(0x805, 64, INDIRECT),
HWCTX_REGINFO(0x807, 1, DIRECT),
HWCTX_REGINFO(0x820, 32, DIRECT),
HWCTX_REGINFO(0x900, 64, INDIRECT),
HWCTX_REGINFO(0x902, 2, DIRECT),
HWCTX_REGINFO(0x907, 1, DIRECT),
HWCTX_REGINFO(0x90a, 1, DIRECT),
HWCTX_REGINFO(0xa02, 10, DIRECT),
HWCTX_REGINFO(0xe2a, 1, DIRECT),
/* bug 976976 requires reg 0xe45 to be restored after reg 0x403 */
/* bug 972588 requires reg 0x403 to be restored with reg 0x411's
value */
HWCTX_REGINFO_RST(0x411, 1, DIRECT, 0xe45),
HWCTX_REGINFO(0xe50, 49, DIRECT),
/* bug 930456 requires reg 0xe2b to be restored with 0x126's value */
HWCTX_REGINFO_RST(0x126, 1, DIRECT, 0xe2b),
};
#define SAVE_BEGIN_V1_SIZE (1 + RESTORE_BEGIN_SIZE)
#define SAVE_DIRECT_V1_SIZE (4 + RESTORE_DIRECT_SIZE)
#define SAVE_INDIRECT_V1_SIZE (6 + RESTORE_INDIRECT_SIZE)
#define SAVE_END_V1_SIZE (8 + RESTORE_END_SIZE)
#define SAVE_INCRS 3
#define RESTORE_BEGIN_SIZE 4
#define RESTORE_DIRECT_SIZE 1
#define RESTORE_INDIRECT_SIZE 2
#define RESTORE_END_SIZE 1
struct save_info {
u32 *ptr;
unsigned int save_count;
unsigned int restore_count;
unsigned int save_incrs;
unsigned int restore_incrs;
};
/*** save ***/
static void save_push_v1(struct nvhost_hwctx *nctx, struct nvhost_cdma *cdma)
{
struct host1x_hwctx *ctx = to_host1x_hwctx(nctx);
struct host1x_hwctx_handler *p = host1x_hwctx_handler(ctx);
/* wait for 3d idle */
nvhost_cdma_push(cdma,
nvhost_opcode_setclass(NV_GRAPHICS_3D_CLASS_ID, 0, 0),
nvhost_opcode_imm_incr_syncpt(
host1x_uclass_incr_syncpt_cond_op_done_v(),
p->h.syncpt));
nvhost_cdma_push(cdma,
nvhost_opcode_setclass(NV_HOST1X_CLASS_ID,
host1x_uclass_wait_syncpt_base_r(), 1),
nvhost_class_host_wait_syncpt_base(p->h.syncpt,
p->h.waitbase, 1));
/* back to 3d */
nvhost_cdma_push(cdma,
nvhost_opcode_setclass(NV_GRAPHICS_3D_CLASS_ID, 0, 0),
NVHOST_OPCODE_NOOP);
/* invalidate the FDC to prevent cache-coherency issues across GPUs
note that we assume FDC_CONTROL_0 is left in the reset state by all
contexts. the invalidate bit will clear itself, so the register
should be unchanged after this */
/* bug 990395 T114 HW no longer can automatically clear the invalidate
bit. Luckily that the ctx switching always happens on the push
buffer boundary, and 3d driver inserts a FDC flush & invalidate &
clear the invalidate bit in the beginning of the each push buffer.
So we do not need to explicitly clear the invalidate bit here. */
nvhost_cdma_push(cdma,
nvhost_opcode_imm(AR3D_FDC_CONTROL_0,
AR3D_FDC_CONTROL_0_RESET_VAL
| AR3D_FDC_CONTROL_0_INVALIDATE),
nvhost_opcode_imm(AR3D_GLOBAL_MEMORY_OUTPUT_READS, 1));
/* bug 972588 requires SW to clear the reg 0x403 and 0xe45 */
nvhost_cdma_push(cdma,
nvhost_opcode_imm(0xe45, 0),
nvhost_opcode_imm(0x403, 0));
nvhost_cdma_push(cdma,
nvhost_opcode_nonincr(AR3D_DW_MEMORY_OUTPUT_ADDRESS, 1),
ctx->iova);
/* gather the save buffer */
_nvhost_cdma_push_gather(cdma,
p->cpuva,
p->iova,
0,
nvhost_opcode_gather(p->save_size),
p->iova);
}
static void save_begin_v1(struct host1x_hwctx_handler *p, u32 *ptr)
{
ptr[0] = nvhost_opcode_nonincr(AR3D_PIPEALIAS_DW_MEMORY_OUTPUT_DATA,
RESTORE_BEGIN_SIZE);
nvhost_3dctx_restore_begin(p, ptr + 1);
ptr += RESTORE_BEGIN_SIZE;
}
static void save_direct_v1(u32 *ptr, u32 start_reg, u32 count,
u32 rst_reg, unsigned int pipe)
{
ptr[0] = nvhost_opcode_setclass(NV_GRAPHICS_3D_CLASS_ID,
(AR3D_PIPEALIAS_DW_MEMORY_OUTPUT_DATA + pipe), 1);
nvhost_3dctx_restore_direct(ptr + 1, rst_reg, count);
ptr += RESTORE_DIRECT_SIZE;
ptr[1] = nvhost_opcode_setclass(NV_HOST1X_CLASS_ID,
host1x_uclass_indoff_r(), 1);
ptr[2] = nvhost_class_host_indoff_reg_read(
host1x_uclass_indoff_indmodid_gr3d_v(),
start_reg, true);
/* TODO could do this in the setclass if count < 6 */
ptr[3] = nvhost_opcode_nonincr(host1x_uclass_inddata_r(), count);
}
static void save_indirect_v1(u32 *ptr, u32 offset_reg, u32 offset,
u32 data_reg, u32 count,
u32 rst_reg, u32 rst_data_reg, unsigned int pipe)
{
ptr[0] = nvhost_opcode_setclass(NV_GRAPHICS_3D_CLASS_ID, 0, 0);
ptr[1] = nvhost_opcode_nonincr(
(AR3D_PIPEALIAS_DW_MEMORY_OUTPUT_DATA + pipe),
RESTORE_INDIRECT_SIZE);
nvhost_3dctx_restore_indirect(ptr + 2, rst_reg, offset, rst_data_reg,
count);
ptr += RESTORE_INDIRECT_SIZE;
ptr[2] = nvhost_opcode_imm(offset_reg, offset);
ptr[3] = nvhost_opcode_setclass(NV_HOST1X_CLASS_ID,
host1x_uclass_indoff_r(), 1);
ptr[4] = nvhost_class_host_indoff_reg_read(
host1x_uclass_indoff_indmodid_gr3d_v(),
data_reg, false);
ptr[5] = nvhost_opcode_nonincr(host1x_uclass_inddata_r(), count);
}
static void save_end_v1(struct host1x_hwctx_handler *p, u32 *ptr)
{
/* write end of restore buffer */
ptr[0] = nvhost_opcode_setclass(NV_GRAPHICS_3D_CLASS_ID,
AR3D_PIPEALIAS_DW_MEMORY_OUTPUT_DATA, 1);
nvhost_3dctx_restore_end(p, ptr + 1);
ptr += RESTORE_END_SIZE;
/* op_done syncpt incr to flush FDC */
ptr[1] = nvhost_opcode_imm_incr_syncpt(
host1x_uclass_incr_syncpt_cond_op_done_v(), p->h.syncpt);
/* host wait for that syncpt incr, and advance the wait base */
ptr[2] = nvhost_opcode_setclass(NV_HOST1X_CLASS_ID,
host1x_uclass_wait_syncpt_base_r(),
nvhost_mask2(
host1x_uclass_wait_syncpt_base_r(),
host1x_uclass_incr_syncpt_base_r()));
ptr[3] = nvhost_class_host_wait_syncpt_base(p->h.syncpt,
p->h.waitbase, p->save_incrs - 1);
ptr[4] = nvhost_class_host_incr_syncpt_base(p->h.waitbase,
p->save_incrs);
/* set class back to 3d */
ptr[5] = nvhost_opcode_setclass(NV_GRAPHICS_3D_CLASS_ID, 0, 0);
/* send reg reads back to host */
ptr[6] = nvhost_opcode_imm(AR3D_GLOBAL_MEMORY_OUTPUT_READS, 0);
/* final syncpt increment to release waiters */
ptr[7] = nvhost_opcode_imm(0, p->h.syncpt);
}
static void setup_save_regs(struct save_info *info,
const struct hwctx_reginfo *regs,
unsigned int nr_regs,
unsigned int pipe)
{
const struct hwctx_reginfo *rend = regs + nr_regs;
u32 *ptr = info->ptr;
unsigned int save_count = info->save_count;
unsigned int restore_count = info->restore_count;
for (; regs != rend; ++regs) {
u32 offset = regs->offset + pipe;
u32 count = regs->count;
u32 rstoff = regs->rst_off + pipe;
u32 indoff = offset + 1;
u32 indrstoff = rstoff + 1;
switch (regs->type) {
case HWCTX_REGINFO_DIRECT:
if (ptr) {
save_direct_v1(ptr, offset,
count, rstoff, pipe);
ptr += SAVE_DIRECT_V1_SIZE;
}
save_count += SAVE_DIRECT_V1_SIZE;
restore_count += RESTORE_DIRECT_SIZE;
break;
case HWCTX_REGINFO_INDIRECT_4X:
++indoff;
++indrstoff;
/* fall through */
case HWCTX_REGINFO_INDIRECT:
if (ptr) {
save_indirect_v1(ptr, offset, 0,
indoff, count,
rstoff, indrstoff,
pipe);
ptr += SAVE_INDIRECT_V1_SIZE;
}
save_count += SAVE_INDIRECT_V1_SIZE;
restore_count += RESTORE_INDIRECT_SIZE;
break;
}
if (ptr) {
/* SAVE cases only: reserve room for incoming data */
u32 k = 0;
/*
* Create a signature pattern for indirect data (which
* will be overwritten by true incoming data) for
* better deducing where we are in a long command
* sequence, when given only a FIFO snapshot for debug
* purposes.
*/
for (k = 0; k < count; k++)
*(ptr + k) = 0xd000d000 | (offset << 16) | k;
ptr += count;
}
save_count += count;
restore_count += count;
}
info->ptr = ptr;
info->save_count = save_count;
info->restore_count = restore_count;
}
static void incr_mem_output_pointer(struct save_info *info,
unsigned int pipe,
unsigned int incr)
{
unsigned int i;
u32 *ptr = info->ptr;
if (ptr) {
*ptr = nvhost_opcode_setclass(NV_GRAPHICS_3D_CLASS_ID, 0, 0);
ptr++;
for (i = 0; i < incr; i++)
*(ptr + i) = nvhost_opcode_imm(
(AR3D_PIPEALIAS_DW_MEMORY_OUTPUT_INCR + pipe),
1);
ptr += incr;
}
info->ptr = ptr;
info->save_count += incr+1;
}
static void setup_save(struct host1x_hwctx_handler *p, u32 *ptr)
{
int pipe, i;
unsigned int old_restore_count, incr_count;
struct gpu_info gpu_info;
struct save_info info = {
ptr,
SAVE_BEGIN_V1_SIZE,
RESTORE_BEGIN_SIZE,
SAVE_INCRS,
1
};
if (info.ptr) {
save_begin_v1(p, info.ptr);
info.ptr += SAVE_BEGIN_V1_SIZE;
}
tegra_gpu_get_info(&gpu_info);
/* save regs for per pixel pipe, this has to be before the global
* one. Advance the rest of the pipes' output pointer to match with
* the pipe 0's.
*/
for (pipe = 1; pipe < gpu_info.num_pixel_pipes; pipe++)
incr_mem_output_pointer(&info, pipe, RESTORE_BEGIN_SIZE);
for (pipe = gpu_info.num_pixel_pipes - 1; pipe >= 0; pipe--) {
old_restore_count = info.restore_count;
setup_save_regs(&info,
ctxsave_regs_3d_per_pipe,
ARRAY_SIZE(ctxsave_regs_3d_per_pipe),
(unsigned int) pipe);
/* Advance the rest of the pipes' output pointer to match with
* the current pipe's one.
*/
incr_count = info.restore_count - old_restore_count;
for (i = 0; i < pipe; i++)
incr_mem_output_pointer(&info, (unsigned int) i,
incr_count);
}
/* save regs for global. Use pipe 0 to do the save */
setup_save_regs(&info,
ctxsave_regs_3d_global,
ARRAY_SIZE(ctxsave_regs_3d_global), 0);
if (info.ptr) {
save_end_v1(p, info.ptr);
info.ptr += SAVE_END_V1_SIZE;
}
wmb();
p->save_size = info.save_count + SAVE_END_V1_SIZE;
p->restore_size = info.restore_count + RESTORE_END_SIZE;
p->save_incrs = info.save_incrs;
p->restore_incrs = info.restore_incrs;
}
/*** ctx3d ***/
static struct nvhost_hwctx *ctx3d_alloc_v1(struct nvhost_hwctx_handler *h,
struct nvhost_channel *ch)
{
struct host1x_hwctx_handler *p = to_host1x_hwctx_handler(h);
struct host1x_hwctx *ctx = nvhost_3dctx_alloc_common(p, ch, false);
if (ctx)
return &ctx->hwctx;
else
return NULL;
}
struct nvhost_hwctx_handler *nvhost_gr3d_t114_ctxhandler_init(
u32 syncpt, u32 waitbase,
struct nvhost_channel *ch)
{
struct host1x_hwctx_handler *p;
p = kmalloc(sizeof(*p), GFP_KERNEL);
if (!p)
return NULL;
p->h.syncpt = syncpt;
p->h.waitbase = waitbase;
setup_save(p, NULL);
p->cpuva = dma_alloc_writecombine(&ch->dev->dev,
p->save_size * 4,
&p->iova,
GFP_KERNEL);
if (!p->cpuva) {
dev_err(&ch->dev->dev, "memory allocation failed\n");
goto fail;
}
setup_save(p, p->cpuva);
p->save_slots = 5;
p->h.alloc = ctx3d_alloc_v1;
p->h.save_push = save_push_v1;
p->h.restore_push = nvhost_3dctx_restore_push;
p->h.get = nvhost_3dctx_get;
p->h.put = nvhost_3dctx_put;
return &p->h;
fail:
kfree(p);
return NULL;
}
int nvhost_gr3d_t114_prepare_power_off(struct platform_device *dev)
{
nvhost_scale_hw_deinit(dev);
return nvhost_gr3d_prepare_power_off(dev);
}
int nvhost_gr3d_t114_finalize_power_on(struct platform_device *dev)
{
/* actmon needs to be reinitialized when we come back from
* power gated state */
return nvhost_scale_hw_init(dev);
}

View File

@@ -0,0 +1,38 @@
/*
* drivers/video/tegra/host/t30/3dctx_t114.h
*
* Tegra Graphics Host Context Switching for Tegra11x SOCs
*
* Copyright (c) 2011-2013, NVIDIA Corporation.
*
* 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/>.
*/
#ifndef __NVHOST_3DCTX_T114_H
#define __NVHOST_3DCTX_T114_H
struct nvhost_hwctx_handler;
struct platform_device;
struct nvhost_channel;
struct nvhost_hwctx;
struct mem_mgr;
struct nvhost_hwctx_handler *nvhost_gr3d_t114_ctxhandler_init(u32 syncpt,
u32 base, struct nvhost_channel *ch);
int nvhost_gr3d_t114_init(struct platform_device *dev);
void nvhost_gr3d_t114_deinit(struct platform_device *dev);
int nvhost_gr3d_t114_prepare_power_off(struct platform_device *dev);
int nvhost_gr3d_t114_finalize_power_on(struct platform_device *dev);
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,30 @@
/*
* drivers/video/tegra/host/gr3d/pod_scaling.h
*
* Tegra Graphics Host Power-On-Demand Scaling
*
* Copyright (c) 2012-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/>.
*/
#ifndef POD_SCALING_H
#define POD_SCALING_H
struct platform_device;
struct dentry;
#define GET_TARGET_FREQ_DONTSCALE 1
/* Suspend is called when powering down module */
void nvhost_scale3d_suspend(struct device *);
#endif

View File

@@ -0,0 +1,302 @@
/*
* drivers/video/tegra/host/gr3d/scale3d.c
*
* Tegra Graphics Host 3D clock scaling
*
* Copyright (c) 2010-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/>.
*/
/*
* 3d clock scaling mechanism
*
* module3d_notify_busy() is called upon submit, module3d_notify_idle() is
* called when all outstanding submits are completed. Both functions notify
* the governor about changed state.
*
* 3d.emc clock is scaled proportionately to 3d clock, with a quadratic-
* bezier-like factor added to pull 3d.emc rate a bit lower.
*/
#include <linux/types.h>
#include <linux/clk.h>
#include <linux/export.h>
#include <linux/slab.h>
#include <linux/ftrace.h>
#include <linux/tegra-soc.h>
#include "chip_support.h"
#include "dev.h"
#include "scale3d.h"
#include "nvhost_acm.h"
#include "nvhost_scale.h"
#define POW2(x) ((x) * (x))
/*
* 20.12 fixed point arithmetic
*/
static const int FXFRAC = 12;
static const int FX_HALF = (1 << 12) / 2;
#define INT_TO_FX(x) ((x) << FXFRAC)
#define FX_TO_INT(x) ((x) >> FXFRAC)
static int FXMUL(int x, int y);
static int FXDIV(int x, int y);
#define MHZ_TO_HZ(x) ((x) * 1000000)
#define HZ_TO_MHZ(x) ((x) / 1000000)
struct nvhost_gr3d_params {
struct nvhost_emc_params emc_params;
int clk_3d;
int clk_3d2;
int clk_3d_emc;
};
/* Convert clk index to struct clk * */
static inline struct clk *clk(struct nvhost_device_profile *profile, int index)
{
struct nvhost_device_data *pdata =
platform_get_drvdata(profile->pdev);
return pdata->clk[index];
}
long nvhost_scale3d_get_emc_rate(struct nvhost_emc_params *emc_params,
long freq)
{
long hz;
freq = INT_TO_FX(HZ_TO_MHZ(freq));
hz = FXMUL(freq, emc_params->emc_slope) + emc_params->emc_offset;
if (!emc_params->linear)
hz -= FXMUL(emc_params->emc_dip_slope,
FXMUL(freq - emc_params->emc_xmid,
freq - emc_params->emc_xmid)) +
emc_params->emc_dip_offset;
hz = MHZ_TO_HZ(FX_TO_INT(hz + FX_HALF)); /* round to nearest */
hz = (hz < 0) ? 0 : hz;
return hz;
}
void nvhost_scale3d_callback(struct nvhost_device_profile *profile,
unsigned long freq)
{
struct nvhost_gr3d_params *gr3d_params = profile->private_data;
struct nvhost_emc_params *emc_params = &gr3d_params->emc_params;
long hz;
long after;
/* Set EMC clockrate */
after = (long) clk_get_rate(clk(profile, gr3d_params->clk_3d));
hz = nvhost_scale3d_get_emc_rate(emc_params, after);
nvhost_module_set_devfreq_rate(profile->pdev, gr3d_params->clk_3d_emc,
hz);
}
/*
* nvhost_scale3d_calibrate_emc()
*
* Compute emc scaling parameters
*
* Remc = S * R3d + O - (Sd * (R3d - Rm)^2 + Od)
*
* Remc - 3d.emc rate
* R3d - 3d.cbus rate
* Rm - 3d.cbus 'middle' rate = (max + min)/2
* S - emc_slope
* O - emc_offset
* Sd - emc_dip_slope
* Od - emc_dip_offset
*
* this superposes a quadratic dip centered around the middle 3d
* frequency over a linear correlation of 3d.emc to 3d clock
* rates.
*
* S, O are chosen so that the maximum 3d rate produces the
* maximum 3d.emc rate exactly, and the minimum 3d rate produces
* at least the minimum 3d.emc rate.
*
* Sd and Od are chosen to produce the largest dip that will
* keep 3d.emc frequencies monotonously decreasing with 3d
* frequencies. To achieve this, the first derivative of Remc
* with respect to R3d should be zero for the minimal 3d rate:
*
* R'emc = S - 2 * Sd * (R3d - Rm)
* R'emc(R3d-min) = 0
* S = 2 * Sd * (R3d-min - Rm)
* = 2 * Sd * (R3d-min - R3d-max) / 2
*
* +------------------------------+
* | Sd = S / (R3d-min - R3d-max) |
* +------------------------------+
*
* dip = Sd * (R3d - Rm)^2 + Od
*
* requiring dip(R3d-min) = 0 and dip(R3d-max) = 0 gives
*
* Sd * (R3d-min - Rm)^2 + Od = 0
* Od = -Sd * ((R3d-min - R3d-max) / 2)^2
* = -Sd * ((R3d-min - R3d-max)^2) / 4
*
* +------------------------------+
* | Od = (emc-max - emc-min) / 4 |
* +------------------------------+
*
*/
void nvhost_scale3d_calibrate_emc(struct nvhost_emc_params *emc_params,
struct clk *clk_3d, struct clk *clk_3d_emc,
bool linear_emc)
{
long correction;
unsigned long max_emc;
unsigned long min_emc;
unsigned long min_rate_3d;
unsigned long max_rate_3d;
max_emc = clk_round_rate(clk_3d_emc, UINT_MAX);
max_emc = INT_TO_FX(HZ_TO_MHZ(max_emc));
min_emc = clk_round_rate(clk_3d_emc, 0);
min_emc = INT_TO_FX(HZ_TO_MHZ(min_emc));
max_rate_3d = clk_round_rate(clk_3d, UINT_MAX);
max_rate_3d = INT_TO_FX(HZ_TO_MHZ(max_rate_3d));
min_rate_3d = clk_round_rate(clk_3d, 0);
min_rate_3d = INT_TO_FX(HZ_TO_MHZ(min_rate_3d));
emc_params->emc_slope =
FXDIV((max_emc - min_emc), (max_rate_3d - min_rate_3d));
emc_params->emc_offset = max_emc -
FXMUL(emc_params->emc_slope, max_rate_3d);
/* Guarantee max 3d rate maps to max emc rate */
emc_params->emc_offset += max_emc -
(FXMUL(emc_params->emc_slope, max_rate_3d) +
emc_params->emc_offset);
emc_params->linear = linear_emc;
if (linear_emc)
return;
emc_params->emc_dip_offset = (max_emc - min_emc) / 4;
emc_params->emc_dip_slope =
-FXDIV(emc_params->emc_slope, max_rate_3d - min_rate_3d);
emc_params->emc_xmid = (max_rate_3d + min_rate_3d) / 2;
correction =
emc_params->emc_dip_offset +
FXMUL(emc_params->emc_dip_slope,
FXMUL(max_rate_3d - emc_params->emc_xmid,
max_rate_3d - emc_params->emc_xmid));
emc_params->emc_dip_offset -= correction;
}
/*
* nvhost_scale3d_init(dev)
*
* Initialise 3d clock scaling for the given device. This function installs
* pod_scaling governor to handle the clock scaling.
*/
void nvhost_scale3d_init(struct platform_device *pdev)
{
struct nvhost_device_data *pdata = platform_get_drvdata(pdev);
struct nvhost_device_profile *profile;
struct nvhost_gr3d_params *gr3d_params;
nvhost_scale_init(pdev);
profile = pdata->power_profile;
if (!profile)
return;
gr3d_params = kzalloc(sizeof(*gr3d_params), GFP_KERNEL);
if (!gr3d_params)
goto err_allocate_gr3d_params;
gr3d_params->clk_3d = 0;
gr3d_params->clk_3d_emc = 1;
profile->private_data = gr3d_params;
nvhost_scale3d_calibrate_emc(&gr3d_params->emc_params,
clk(profile, gr3d_params->clk_3d),
clk(profile, gr3d_params->clk_3d_emc),
pdata->linear_emc);
return;
err_allocate_gr3d_params:
nvhost_scale_deinit(pdev);
}
/*
* nvhost_scale3d_deinit(dev)
*
* Stop 3d scaling for the given device.
*/
void nvhost_scale3d_deinit(struct platform_device *pdev)
{
struct nvhost_device_data *pdata = platform_get_drvdata(pdev);
if (!pdata->power_profile)
return;
kfree(pdata->power_profile->private_data);
pdata->power_profile->private_data = NULL;
nvhost_scale_deinit(pdev);
}
/*
* 20.12 fixed point arithmetic
*
* int FXMUL(int x, int y)
* int FXDIV(int x, int y)
*/
int FXMUL(int x, int y)
{
return ((long long) x * (long long) y) >> FXFRAC;
}
int FXDIV(int x, int y)
{
/* long long div operation not supported, must shift manually. This
* would have been
*
* return (((long long) x) << FXFRAC) / (long long) y;
*/
int pos, t;
if (x == 0)
return 0;
/* find largest allowable right shift to numerator, limit to FXFRAC */
t = x < 0 ? -x : x;
pos = 31 - fls(t); /* fls can't be 32 if x != 0 */
if (pos > FXFRAC)
pos = FXFRAC;
y >>= FXFRAC - pos;
if (y == 0)
return 0x7FFFFFFF; /* overflow, return MAX_FIXED */
return (x << pos) / y;
}

View File

@@ -0,0 +1,53 @@
/*
* drivers/video/tegra/host/gr3d/scale3d.h
*
* Tegra Graphics Host 3D Clock Scaling
*
* Copyright (c) 2010-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/>.
*/
#ifndef NVHOST_T30_SCALE3D_H
#define NVHOST_T30_SCALE3D_H
struct nvhost_device_profile;
struct platform_device;
struct device;
struct dentry;
struct clk;
struct nvhost_emc_params {
long emc_slope;
long emc_offset;
long emc_dip_slope;
long emc_dip_offset;
long emc_xmid;
bool linear;
};
/* Initialization and de-initialization for module */
void nvhost_scale3d_init(struct platform_device *pdev);
void nvhost_scale3d_deinit(struct platform_device *pdev);
/* Callback for generic profile. The callback handles setting
* the frequencies of EMC and the second 3d unit (if available) */
void nvhost_scale3d_callback(struct nvhost_device_profile *profile,
unsigned long freq);
void nvhost_scale3d_calibrate_emc(struct nvhost_emc_params *emc_params,
struct clk *clk_3d, struct clk *clk_3d_emc,
bool linear_emc);
long nvhost_scale3d_get_emc_rate(struct nvhost_emc_params *emc_params,
long freq);
#endif