/* * 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 . */ #include #include #include #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; }