521 lines
15 KiB
C

/*
* drivers/video/tegra/host/t124/t124.c
*
* Tegra Graphics Init for T124 Architecture Chips
*
* 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 <linux/slab.h>
#include <linux/tegra-powergate.h>
#include <linux/platform_data/tegra_mc.h>
#include "dev.h"
#include "nvhost_job.h"
#include "class_ids.h"
#include "t124.h"
#include "host1x/host1x.h"
#include "hardware_t124.h"
#include "syncpt_t124.h"
#include "gk20a/gk20a.h"
#include "gk20a/gk20a_scale.h"
#include "vic03/vic03.h"
#include "msenc/msenc.h"
#include "tsec/tsec.h"
#include "isp/isp.h"
#include "gr3d/pod_scaling.h"
#include "gr3d/scale3d.h"
#include "nvhost_memmgr.h"
#include "chip_support.h"
#include "nvhost_scale.h"
static int t124_num_alloc_channels;
#define HOST_EMC_FLOOR 300000000
#define VI_CLOCKGATE_DELAY 60
#define VI_POWERGATE_DELAY 500
#define ISP_CLOCKGATE_DELAY 60
#define ISP_POWERGATE_DELAY 500
#define TSEC_POWERGATE_DELAY 500
#define GK20A_DEV_NAME_SIZE 5
#define BIT64(nr) (1ULL << (nr))
#define NVSYNCPTS_CLIENT_MANAGED_T124 ( \
BIT64(NVSYNCPT_DISP0_A) | BIT64(NVSYNCPT_DISP1_A) | \
BIT64(NVSYNCPT_DISP0_B) | BIT64(NVSYNCPT_DISP1_B) | \
BIT64(NVSYNCPT_DISP0_C) | BIT64(NVSYNCPT_DISP1_C) | \
BIT(NVSYNCPT_DISP0_D) | \
BIT(NVSYNCPT_DISP0_H) | BIT(NVSYNCPT_DISP1_H) | \
BIT64(NVSYNCPT_DSI) | \
BIT64(NVSYNCPT_VBLANK0) | BIT64(NVSYNCPT_VBLANK1) | \
BIT64(NVSYNCPT_AVP_0))
/* Host1x driver matches module IDs while setting a
* particular clock, This ID is used for EMC module.
*/
#define TEGRA_HOST1X_EMC_MODULE_ID 75
static const char *s_syncpt_names[NV_HOST1X_SYNCPT_NB_PTS] = {
[NVSYNCPT_ISP_0_0] = "ispa_memory",
[NVSYNCPT_ISP_0_1] = "ispa_stats",
[NVSYNCPT_ISP_0_2] = "ispa_stream",
[NVSYNCPT_ISP_0_3] = "ispa_loadv",
[NVSYNCPT_ISP_1_0] = "ispb_memory",
[NVSYNCPT_ISP_1_1] = "ispb_stats",
[NVSYNCPT_ISP_1_2] = "ispb_stream",
[NVSYNCPT_ISP_1_3] = "ispb_loadv",
[NVSYNCPT_VI_0_0] = "vi0_ispa",
[NVSYNCPT_VI_0_1] = "vi0_ispb",
[NVSYNCPT_VI_0_2] = "vi0_stream",
[NVSYNCPT_VI_0_3] = "vi0_memory",
[NVSYNCPT_VI_0_4] = "vi0_flash",
[NVSYNCPT_VI_1_0] = "vi1_ispa",
[NVSYNCPT_VI_1_1] = "vi1_ispb",
[NVSYNCPT_VI_1_2] = "vi1_stream",
[NVSYNCPT_VI_1_3] = "vi1_memory",
[NVSYNCPT_VI_1_4] = "vi1_flash",
[NVSYNCPT_3D] = "3d",
[NVSYNCPT_MPE] = "mpe",
[NVSYNCPT_MPE_EBM_EOF] = "mpe_ebm_eof",
[NVSYNCPT_MPE_WR_SAFE] = "mpe_wr_safe",
[NVSYNCPT_VIC] = "vic",
[NVSYNCPT_TSEC] = "tsec",
[NVSYNCPT_DISP0_A] = "disp0",
[NVSYNCPT_DISP1_A] = "disp1",
[NVSYNCPT_AVP_0] = "avp",
[NVSYNCPT_DISP0_B] = "disp0b",
[NVSYNCPT_DISP1_B] = "disp1b",
[NVSYNCPT_DISP0_C] = "disp0c",
[NVSYNCPT_DISP1_C] = "disp1c",
[NVSYNCPT_DISP0_D] = "disp0d",
[NVSYNCPT_DISP0_H] = "disp0h",
[NVSYNCPT_DISP1_H] = "disp1h",
[NVSYNCPT_VBLANK0] = "vblank0",
[NVSYNCPT_VBLANK1] = "vblank1",
[NVSYNCPT_DSI] = "dsi",
};
static struct host1x_device_info host1x04_info = {
.nb_channels = T124_NVHOST_NUMCHANNELS,
.nb_pts = NV_HOST1X_SYNCPT_NB_PTS,
.nb_mlocks = NV_HOST1X_NB_MLOCKS,
.nb_bases = NV_HOST1X_SYNCPT_NB_BASES,
.syncpt_names = s_syncpt_names,
.client_managed = NVSYNCPTS_CLIENT_MANAGED_T124,
};
struct nvhost_device_data t124_host1x_info = {
.clocks = {{"host1x", 81600000}, {"actmon", UINT_MAX} },
NVHOST_MODULE_NO_POWERGATE_IDS,
.private_data = &host1x04_info,
.finalize_poweron = nvhost_host1x_finalize_poweron,
.prepare_poweroff = nvhost_host1x_prepare_poweroff,
};
struct nvhost_device_data t124_isp_info = {
/* FIXME: control clocks from user space instead of hard-coding here */
.syncpts = NV_ISP_0_SYNCPTS,
.moduleid = NVHOST_MODULE_ISP,
.modulemutexes = {NVMODMUTEX_ISP_0},
.exclusive = true,
.keepalive = true,
.powergate_ids = {TEGRA_POWERGATE_VENC, -1},
.can_powergate = true,
.clockgate_delay = ISP_CLOCKGATE_DELAY,
.powergate_delay = ISP_POWERGATE_DELAY,
.clocks = {
{"isp", UINT_MAX, 0, TEGRA_MC_CLIENT_ISP},
{"emc", 0, TEGRA_HOST1X_EMC_MODULE_ID} },
.finalize_poweron = nvhost_isp_t124_finalize_poweron,
};
struct nvhost_device_data t124_ispb_info = {
/* FIXME: control clocks from user space instead of hard-coding here */
.syncpts = NV_ISP_1_SYNCPTS,
.moduleid = (1 << 16) | NVHOST_MODULE_ISP,
.modulemutexes = {NVMODMUTEX_ISP_1},
.exclusive = true,
.keepalive = true,
.powergate_ids = {TEGRA_POWERGATE_VENC, -1},
.can_powergate = true,
.clockgate_delay = ISP_CLOCKGATE_DELAY,
.powergate_delay = ISP_POWERGATE_DELAY,
.clocks = {
{"isp", UINT_MAX, 0, TEGRA_MC_CLIENT_ISPB},
{"emc", 0, TEGRA_HOST1X_EMC_MODULE_ID} },
.finalize_poweron = nvhost_isp_t124_finalize_poweron,
};
struct nvhost_device_data t124_msenc_info = {
.version = NVHOST_ENCODE_MSENC_VER(3, 1),
.syncpts = {NVSYNCPT_MSENC, NVSYNCPT_MSENC_SLICE},
.waitbases = {NVWAITBASE_MSENC},
.class = NV_VIDEO_ENCODE_MSENC_CLASS_ID,
.clocks = {{"cbus", UINT_MAX, 0, TEGRA_MC_CLIENT_MSENC},
{"emc", HOST_EMC_FLOOR} },
NVHOST_DEFAULT_CLOCKGATE_DELAY,
.moduleid = NVHOST_MODULE_MSENC,
.powergate_ids = { TEGRA_POWERGATE_MPE, -1 },
.powergate_delay = 100,
.can_powergate = true,
.init = nvhost_msenc_init,
.deinit = nvhost_msenc_deinit,
.finalize_poweron = nvhost_msenc_finalize_poweron,
.scaling_init = nvhost_scale_init,
.scaling_deinit = nvhost_scale_deinit,
.actmon_regs = HOST1X_CHANNEL_ACTMON1_REG_BASE,
.actmon_enabled = true,
};
struct nvhost_device_data t124_tsec_info = {
.version = NVHOST_ENCODE_TSEC_VER(1, 0),
.syncpts = {NVSYNCPT_TSEC},
.waitbases = {NVWAITBASE_TSEC},
.class = NV_TSEC_CLASS_ID,
.exclusive = true,
.clocks = {{"tsec", UINT_MAX, 0, TEGRA_MC_CLIENT_TSEC},
{"emc", HOST_EMC_FLOOR} },
NVHOST_MODULE_NO_POWERGATE_IDS,
NVHOST_DEFAULT_CLOCKGATE_DELAY,
.can_powergate = true,
.powergate_delay = TSEC_POWERGATE_DELAY,
.keepalive = true,
.moduleid = NVHOST_MODULE_TSEC,
.init = nvhost_tsec_init,
.deinit = nvhost_tsec_deinit,
.finalize_poweron = nvhost_tsec_finalize_poweron,
.prepare_poweroff = nvhost_tsec_prepare_poweroff,
};
#ifdef CONFIG_ARCH_TEGRA_VIC
struct nvhost_device_data t124_vic_info = {
.syncpts = {NVSYNCPT_VIC},
.modulemutexes = {NVMODMUTEX_VIC},
.clocks = {{"cbus", UINT_MAX, 0, TEGRA_MC_CLIENT_VIC},
{"emc", UINT_MAX} },
.version = NVHOST_ENCODE_VIC_VER(3, 0),
NVHOST_MODULE_NO_POWERGATE_IDS,
NVHOST_DEFAULT_CLOCKGATE_DELAY,
.moduleid = NVHOST_MODULE_VIC,
.alloc_hwctx_handler = nvhost_vic03_alloc_hwctx_handler,
.can_powergate = true,
.powergate_delay = 500,
.powergate_ids = { TEGRA_POWERGATE_VIC, -1 },
.init = nvhost_vic03_init,
.deinit = nvhost_vic03_deinit,
.alloc_hwctx_handler = nvhost_vic03_alloc_hwctx_handler,
.finalize_poweron = nvhost_vic03_finalize_poweron,
.prepare_poweroff = nvhost_vic03_prepare_poweroff,
.scaling_init = nvhost_scale3d_init,
.scaling_deinit = nvhost_scale3d_deinit,
.busy = nvhost_scale_notify_busy,
.idle = nvhost_scale_notify_idle,
.suspend_ndev = nvhost_scale3d_suspend,
.scaling_post_cb = &nvhost_scale3d_callback,
.devfreq_governor = "nvhost_podgov",
.actmon_regs = HOST1X_CHANNEL_ACTMON2_REG_BASE,
.actmon_enabled = true,
.linear_emc = true,
};
#endif
#if defined(CONFIG_TEGRA_GK20A)
struct nvhost_device_data tegra_gk20a_info = {
.syncpts = {NVSYNCPT_GK20A_BASE},
.syncpt_base = NVSYNCPT_GK20A_BASE,
.class = NV_GRAPHICS_GPU_CLASS_ID,
.clocks = {{"gpu", UINT_MAX},
{"pwr", 204000000},
{"emc", UINT_MAX},
{} },
.powergate_ids = { TEGRA_POWERGATE_3D, -1 },
NVHOST_DEFAULT_CLOCKGATE_DELAY,
.powergate_delay = 500,
.can_powergate = true,
.alloc_hwctx_handler = nvhost_gk20a_alloc_hwctx_handler,
.ctrl_ops = &tegra_gk20a_ctrl_ops,
#ifdef CONFIG_TEGRA_GK20A_DEBUG_SESSION
.dbg_ops = &tegra_gk20a_dbg_gpu_ops,
.prof_ops = &tegra_gk20a_prof_gpu_ops,
#endif
.as_ops = &tegra_gk20a_as_ops,
.moduleid = NVHOST_MODULE_GPU,
.init = nvhost_gk20a_init,
.deinit = nvhost_gk20a_deinit,
.alloc_hwctx_handler = nvhost_gk20a_alloc_hwctx_handler,
.prepare_poweroff = nvhost_gk20a_prepare_poweroff,
.finalize_poweron = nvhost_gk20a_finalize_poweron,
#ifdef CONFIG_TEGRA_GK20A_DEVFREQ
.busy = nvhost_gk20a_scale_notify_busy,
.idle = nvhost_gk20a_scale_notify_idle,
.scaling_init = nvhost_gk20a_scale_init,
.scaling_deinit = nvhost_gk20a_scale_deinit,
.suspend_ndev = nvhost_scale3d_suspend,
.devfreq_governor = "nvhost_podgov",
.scaling_post_cb = nvhost_gk20a_scale_callback,
#endif
};
#endif
#include "host1x/host1x_channel.c"
#if defined(CONFIG_TEGRA_GK20A)
static int t124_channel_alloc_obj(struct nvhost_hwctx *hwctx,
struct nvhost_alloc_obj_ctx_args *args)
{
nvhost_dbg_fn("");
return gk20a_alloc_obj_ctx(hwctx->priv, args);
}
static int t124_channel_free_obj(struct nvhost_hwctx *hwctx,
struct nvhost_free_obj_ctx_args *args)
{
nvhost_dbg_fn("");
return gk20a_free_obj_ctx(hwctx->priv, args);
}
static int t124_channel_alloc_gpfifo(struct nvhost_hwctx *hwctx,
struct nvhost_alloc_gpfifo_args *args)
{
nvhost_dbg_fn("");
return gk20a_alloc_channel_gpfifo(hwctx->priv, args);
}
static int t124_channel_set_error_notifier(struct nvhost_hwctx *hwctx,
struct nvhost_set_error_notifier *args) {
return gk20a_init_error_notifier(hwctx, args->mem, args->offset);
}
static int t124_channel_submit_gpfifo(struct nvhost_hwctx *hwctx,
struct nvhost_gpfifo *gpfifo, u32 num_entries,
struct nvhost_fence *fence, u32 flags)
{
struct channel_gk20a *ch = hwctx->priv;
struct nvhost_channel *nvhost_ch = hwctx->channel;
void *completed_waiter = NULL;
int err, ret;
nvhost_dbg_fn("");
if (hwctx->has_timedout || !ch)
return -ETIMEDOUT;
completed_waiter = nvhost_intr_alloc_waiter();
if (!completed_waiter)
return -ENOMEM;
nvhost_module_busy(nvhost_ch->dev);
ret = gk20a_submit_channel_gpfifo(hwctx->priv, gpfifo, num_entries,
fence, flags);
if (!ret) {
err = nvhost_intr_add_action(
&nvhost_get_host(nvhost_ch->dev)->intr,
fence->syncpt_id, fence->value,
NVHOST_INTR_ACTION_GPFIFO_SUBMIT_COMPLETE,
ch,
completed_waiter,
NULL);
WARN(err, "Failed to set submit complete interrupt");
} else {
pr_err("submit error %d\n", ret);
kfree(completed_waiter);
}
return ret;
}
static int t124_channel_wait(struct nvhost_hwctx *hwctx,
struct nvhost_wait_args *args)
{
nvhost_dbg_fn("");
return gk20a_channel_wait(hwctx->priv, args);
}
static int t124_channel_set_priority(struct nvhost_hwctx *hwctx,
struct nvhost_set_priority_args *args)
{
nvhost_dbg_fn("");
return gk20a_channel_set_priority(hwctx->priv, args->priority);
}
#if defined(CONFIG_TEGRA_GPU_CYCLE_STATS)
static int t124_channel_cycle_stats(struct nvhost_hwctx *hwctx,
struct nvhost_cycle_stats_args *args)
{
nvhost_dbg_fn("");
return gk20a_channel_cycle_stats(hwctx->priv, args);
}
#endif
static int t124_channel_zcull_bind(struct nvhost_hwctx *hwctx,
struct nvhost_zcull_bind_args *args)
{
nvhost_dbg_fn("");
return gk20a_channel_zcull_bind(hwctx->priv, args);
}
#endif /* CONFIG_TEGRA_GK20A */
static void t124_free_nvhost_channel(struct nvhost_channel *ch)
{
nvhost_dbg_fn("");
nvhost_free_channel_internal(ch, &t124_num_alloc_channels);
}
static struct nvhost_channel *t124_alloc_nvhost_channel(
struct platform_device *dev)
{
struct nvhost_device_data *pdata = nvhost_get_devdata(dev);
struct nvhost_channel *ch;
nvhost_dbg_fn("");
ch = nvhost_alloc_channel_internal(pdata->index,
nvhost_get_host(dev)->info.nb_channels,
&t124_num_alloc_channels);
if (ch) {
#if defined(CONFIG_TEGRA_GK20A)
if (strstr(dev->name, "gk20a")) {
ch->ops.init = host1x_channel_ops.init;
ch->ops.alloc_obj = t124_channel_alloc_obj;
ch->ops.free_obj = t124_channel_free_obj;
ch->ops.alloc_gpfifo = t124_channel_alloc_gpfifo;
ch->ops.submit_gpfifo = t124_channel_submit_gpfifo;
ch->ops.set_priority = t124_channel_set_priority;
ch->ops.wait = t124_channel_wait;
ch->ops.set_error_notifier =
t124_channel_set_error_notifier;
#if defined(CONFIG_TEGRA_GPU_CYCLE_STATS)
ch->ops.cycle_stats = t124_channel_cycle_stats;
#endif
ch->ops.zcull.bind = t124_channel_zcull_bind;
} else
#endif
ch->ops = host1x_channel_ops;
}
return ch;
}
static struct nvhost_device_data *t124_pdatas[] = {
&t124_isp_info,
&t124_ispb_info,
&t124_msenc_info,
&t124_tsec_info,
#if defined(CONFIG_TEGRA_GK20A)
&tegra_gk20a_info,
#endif
#if defined(CONFIG_ARCH_TEGRA_VIC)
&t124_vic_info,
#endif
};
int nvhost_init_t124_channel_support(struct nvhost_master *host,
struct nvhost_chip_support *op)
{
int i, num_channels;
/* Set indices dynamically as we can have
* missing/non-static devices above (e.g.: vic, gk20a).
*/
for (num_channels = i = 0; i < ARRAY_SIZE(t124_pdatas); i++) {
struct nvhost_device_data *pdata =
(struct nvhost_device_data *)t124_pdatas[i];
pdata->index = num_channels++;
if (pdata->slave) {
struct nvhost_device_data *slave_pdata =
(struct nvhost_device_data *)
pdata->slave->dev.platform_data;
slave_pdata->index = num_channels++;
}
}
nvhost_dbg_fn("max channels=%d num channels=%zd",
NV_HOST1X_CHANNELS, num_channels);
if (num_channels > T124_NVHOST_NUMCHANNELS) {
WARN(-ENODEV, "too many channel devices");
return -ENODEV;
}
op->nvhost_dev.alloc_nvhost_channel = t124_alloc_nvhost_channel;
op->nvhost_dev.free_nvhost_channel = t124_free_nvhost_channel;
return 0;
}
static void t124_remove_support(struct nvhost_chip_support *op)
{
kfree(op->priv);
op->priv = 0;
}
#include "host1x/host1x_cdma.c"
#include "host1x/host1x_syncpt.c"
#include "host1x/host1x_intr.c"
#include "host1x/host1x_actmon_t124.c"
int nvhost_init_t124_support(struct nvhost_master *host,
struct nvhost_chip_support *op)
{
int i = 0;
int err;
struct t124 *t124 = 0;
for (i = NVSYNCPT_GK20A_BASE; i <= NVSYNCPT_GK20A_LAST; i++)
s_syncpt_names[i] = "gk20a";
/* don't worry about cleaning up on failure... "remove" does it. */
err = nvhost_init_t124_channel_support(host, op);
if (err)
return err;
op->cdma = host1x_cdma_ops;
op->push_buffer = host1x_pushbuffer_ops;
err = nvhost_init_t124_debug_support(op);
if (err)
return err;
host->sync_aperture = host->aperture + HOST1X_CHANNEL_SYNC_REG_BASE;
op->syncpt = host1x_syncpt_ops;
op->intr = host1x_intr_ops;
op->actmon = host1x_actmon_ops;
err = nvhost_memmgr_init(op);
if (err)
return err;
t124 = kzalloc(sizeof(struct t124), GFP_KERNEL);
if (!t124) {
err = -ENOMEM;
goto err;
}
t124->host = host;
op->priv = t124;
op->remove_support = t124_remove_support;
return 0;
err:
kfree(t124);
op->priv = 0;
op->remove_support = 0;
return err;
}