Commit 571a9233 authored by Dave Airlie's avatar Dave Airlie

Merge tag 'drm/tegra/for-5.15-rc1' of ssh://git.freedesktop.org/git/tegra/linux into drm-next

drm/tegra: Changes for v5.15-rc1

The bulk of these changes is a more modern ABI that can be efficiently
used on newer SoCs as well as older ones. The userspace parts for this
are available here:

  - libdrm support: https://gitlab.freedesktop.org/tagr/drm/-/commits/drm-tegra-uabi-v8
  - VAAPI driver: https://github.com/cyndis/vaapi-tegra-driver

In addition, existing userspace from the grate reverse-engineering
project has been updated to use this new ABI:

  - X11 driver: https://github.com/grate-driver/xf86-video-opentegra
  - 3D driver: https://github.com/grate-driver/grate

Other than that, there's also support for display memory bandwidth
management for various generations and a bit of cleanup.
Signed-off-by: default avatarDave Airlie <airlied@redhat.com>

From: Thierry Reding <thierry.reding@gmail.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20210813163616.2822355-1-thierry.reding@gmail.com
parents 0def4b73 fed02893
...@@ -9,6 +9,7 @@ config DRM_TEGRA ...@@ -9,6 +9,7 @@ config DRM_TEGRA
select DRM_MIPI_DSI select DRM_MIPI_DSI
select DRM_PANEL select DRM_PANEL
select TEGRA_HOST1X select TEGRA_HOST1X
select INTERCONNECT
select IOMMU_IOVA select IOMMU_IOVA
select CEC_CORE if CEC_NOTIFIER select CEC_CORE if CEC_NOTIFIER
help help
......
...@@ -3,6 +3,9 @@ ccflags-$(CONFIG_DRM_TEGRA_DEBUG) += -DDEBUG ...@@ -3,6 +3,9 @@ ccflags-$(CONFIG_DRM_TEGRA_DEBUG) += -DDEBUG
tegra-drm-y := \ tegra-drm-y := \
drm.o \ drm.o \
uapi.o \
submit.o \
firewall.o \
gem.o \ gem.o \
fb.o \ fb.o \
dp.o \ dp.o \
......
This diff is collapsed.
...@@ -15,6 +15,8 @@ ...@@ -15,6 +15,8 @@
struct tegra_output; struct tegra_output;
#define TEGRA_DC_LEGACY_PLANES_NUM 7
struct tegra_dc_state { struct tegra_dc_state {
struct drm_crtc_state base; struct drm_crtc_state base;
...@@ -33,11 +35,22 @@ static inline struct tegra_dc_state *to_dc_state(struct drm_crtc_state *state) ...@@ -33,11 +35,22 @@ static inline struct tegra_dc_state *to_dc_state(struct drm_crtc_state *state)
return NULL; return NULL;
} }
static inline const struct tegra_dc_state *
to_const_dc_state(const struct drm_crtc_state *state)
{
return to_dc_state((struct drm_crtc_state *)state);
}
struct tegra_dc_stats { struct tegra_dc_stats {
unsigned long frames; unsigned long frames;
unsigned long vblank; unsigned long vblank;
unsigned long underflow; unsigned long underflow;
unsigned long overflow; unsigned long overflow;
unsigned long frames_total;
unsigned long vblank_total;
unsigned long underflow_total;
unsigned long overflow_total;
}; };
struct tegra_windowgroup_soc { struct tegra_windowgroup_soc {
...@@ -66,7 +79,9 @@ struct tegra_dc_soc_info { ...@@ -66,7 +79,9 @@ struct tegra_dc_soc_info {
unsigned int num_overlay_formats; unsigned int num_overlay_formats;
const u64 *modifiers; const u64 *modifiers;
bool has_win_a_without_filters; bool has_win_a_without_filters;
bool has_win_b_vfilter_mem_client;
bool has_win_c_without_vert_filter; bool has_win_c_without_vert_filter;
bool plane_tiled_memory_bandwidth_x2;
}; };
struct tegra_dc { struct tegra_dc {
...@@ -152,6 +167,8 @@ int tegra_dc_state_setup_clock(struct tegra_dc *dc, ...@@ -152,6 +167,8 @@ int tegra_dc_state_setup_clock(struct tegra_dc *dc,
struct drm_crtc_state *crtc_state, struct drm_crtc_state *crtc_state,
struct clk *clk, unsigned long pclk, struct clk *clk, unsigned long pclk,
unsigned int div); unsigned int div);
void tegra_crtc_atomic_post_commit(struct drm_crtc *crtc,
struct drm_atomic_state *state);
/* from rgb.c */ /* from rgb.c */
int tegra_dc_rgb_probe(struct tegra_dc *dc); int tegra_dc_rgb_probe(struct tegra_dc *dc);
......
...@@ -21,24 +21,21 @@ ...@@ -21,24 +21,21 @@
#include <drm/drm_prime.h> #include <drm/drm_prime.h>
#include <drm/drm_vblank.h> #include <drm/drm_vblank.h>
#include "dc.h"
#include "drm.h" #include "drm.h"
#include "gem.h" #include "gem.h"
#include "uapi.h"
#define DRIVER_NAME "tegra" #define DRIVER_NAME "tegra"
#define DRIVER_DESC "NVIDIA Tegra graphics" #define DRIVER_DESC "NVIDIA Tegra graphics"
#define DRIVER_DATE "20120330" #define DRIVER_DATE "20120330"
#define DRIVER_MAJOR 0 #define DRIVER_MAJOR 1
#define DRIVER_MINOR 0 #define DRIVER_MINOR 0
#define DRIVER_PATCHLEVEL 0 #define DRIVER_PATCHLEVEL 0
#define CARVEOUT_SZ SZ_64M #define CARVEOUT_SZ SZ_64M
#define CDMA_GATHER_FETCHES_MAX_NB 16383 #define CDMA_GATHER_FETCHES_MAX_NB 16383
struct tegra_drm_file {
struct idr contexts;
struct mutex lock;
};
static int tegra_atomic_check(struct drm_device *drm, static int tegra_atomic_check(struct drm_device *drm,
struct drm_atomic_state *state) struct drm_atomic_state *state)
{ {
...@@ -60,6 +57,17 @@ static const struct drm_mode_config_funcs tegra_drm_mode_config_funcs = { ...@@ -60,6 +57,17 @@ static const struct drm_mode_config_funcs tegra_drm_mode_config_funcs = {
.atomic_commit = drm_atomic_helper_commit, .atomic_commit = drm_atomic_helper_commit,
}; };
static void tegra_atomic_post_commit(struct drm_device *drm,
struct drm_atomic_state *old_state)
{
struct drm_crtc_state *old_crtc_state __maybe_unused;
struct drm_crtc *crtc;
unsigned int i;
for_each_old_crtc_in_state(old_state, crtc, old_crtc_state, i)
tegra_crtc_atomic_post_commit(crtc, old_state);
}
static void tegra_atomic_commit_tail(struct drm_atomic_state *old_state) static void tegra_atomic_commit_tail(struct drm_atomic_state *old_state)
{ {
struct drm_device *drm = old_state->dev; struct drm_device *drm = old_state->dev;
...@@ -79,6 +87,8 @@ static void tegra_atomic_commit_tail(struct drm_atomic_state *old_state) ...@@ -79,6 +87,8 @@ static void tegra_atomic_commit_tail(struct drm_atomic_state *old_state)
} else { } else {
drm_atomic_helper_commit_tail_rpm(old_state); drm_atomic_helper_commit_tail_rpm(old_state);
} }
tegra_atomic_post_commit(drm, old_state);
} }
static const struct drm_mode_config_helper_funcs static const struct drm_mode_config_helper_funcs
...@@ -94,7 +104,9 @@ static int tegra_drm_open(struct drm_device *drm, struct drm_file *filp) ...@@ -94,7 +104,9 @@ static int tegra_drm_open(struct drm_device *drm, struct drm_file *filp)
if (!fpriv) if (!fpriv)
return -ENOMEM; return -ENOMEM;
idr_init_base(&fpriv->contexts, 1); idr_init_base(&fpriv->legacy_contexts, 1);
xa_init_flags(&fpriv->contexts, XA_FLAGS_ALLOC1);
xa_init(&fpriv->syncpoints);
mutex_init(&fpriv->lock); mutex_init(&fpriv->lock);
filp->driver_priv = fpriv; filp->driver_priv = fpriv;
...@@ -107,20 +119,6 @@ static void tegra_drm_context_free(struct tegra_drm_context *context) ...@@ -107,20 +119,6 @@ static void tegra_drm_context_free(struct tegra_drm_context *context)
kfree(context); kfree(context);
} }
static struct host1x_bo *
host1x_bo_lookup(struct drm_file *file, u32 handle)
{
struct drm_gem_object *gem;
struct tegra_bo *bo;
gem = drm_gem_object_lookup(file, handle);
if (!gem)
return NULL;
bo = to_tegra_bo(gem);
return &bo->base;
}
static int host1x_reloc_copy_from_user(struct host1x_reloc *dest, static int host1x_reloc_copy_from_user(struct host1x_reloc *dest,
struct drm_tegra_reloc __user *src, struct drm_tegra_reloc __user *src,
struct drm_device *drm, struct drm_device *drm,
...@@ -151,11 +149,11 @@ static int host1x_reloc_copy_from_user(struct host1x_reloc *dest, ...@@ -151,11 +149,11 @@ static int host1x_reloc_copy_from_user(struct host1x_reloc *dest,
dest->flags = HOST1X_RELOC_READ | HOST1X_RELOC_WRITE; dest->flags = HOST1X_RELOC_READ | HOST1X_RELOC_WRITE;
dest->cmdbuf.bo = host1x_bo_lookup(file, cmdbuf); dest->cmdbuf.bo = tegra_gem_lookup(file, cmdbuf);
if (!dest->cmdbuf.bo) if (!dest->cmdbuf.bo)
return -ENOENT; return -ENOENT;
dest->target.bo = host1x_bo_lookup(file, target); dest->target.bo = tegra_gem_lookup(file, target);
if (!dest->target.bo) if (!dest->target.bo)
return -ENOENT; return -ENOENT;
...@@ -193,7 +191,7 @@ int tegra_drm_submit(struct tegra_drm_context *context, ...@@ -193,7 +191,7 @@ int tegra_drm_submit(struct tegra_drm_context *context,
return -EINVAL; return -EINVAL;
job = host1x_job_alloc(context->channel, args->num_cmdbufs, job = host1x_job_alloc(context->channel, args->num_cmdbufs,
args->num_relocs); args->num_relocs, false);
if (!job) if (!job)
return -ENOMEM; return -ENOMEM;
...@@ -201,6 +199,7 @@ int tegra_drm_submit(struct tegra_drm_context *context, ...@@ -201,6 +199,7 @@ int tegra_drm_submit(struct tegra_drm_context *context,
job->client = client; job->client = client;
job->class = client->class; job->class = client->class;
job->serialize = true; job->serialize = true;
job->syncpt_recovery = true;
/* /*
* Track referenced BOs so that they can be unreferenced after the * Track referenced BOs so that they can be unreferenced after the
...@@ -237,7 +236,7 @@ int tegra_drm_submit(struct tegra_drm_context *context, ...@@ -237,7 +236,7 @@ int tegra_drm_submit(struct tegra_drm_context *context,
goto fail; goto fail;
} }
bo = host1x_bo_lookup(file, cmdbuf.handle); bo = tegra_gem_lookup(file, cmdbuf.handle);
if (!bo) { if (!bo) {
err = -ENOENT; err = -ENOENT;
goto fail; goto fail;
...@@ -432,7 +431,7 @@ static int tegra_client_open(struct tegra_drm_file *fpriv, ...@@ -432,7 +431,7 @@ static int tegra_client_open(struct tegra_drm_file *fpriv,
if (err < 0) if (err < 0)
return err; return err;
err = idr_alloc(&fpriv->contexts, context, 1, 0, GFP_KERNEL); err = idr_alloc(&fpriv->legacy_contexts, context, 1, 0, GFP_KERNEL);
if (err < 0) { if (err < 0) {
client->ops->close_channel(context); client->ops->close_channel(context);
return err; return err;
...@@ -487,13 +486,13 @@ static int tegra_close_channel(struct drm_device *drm, void *data, ...@@ -487,13 +486,13 @@ static int tegra_close_channel(struct drm_device *drm, void *data,
mutex_lock(&fpriv->lock); mutex_lock(&fpriv->lock);
context = idr_find(&fpriv->contexts, args->context); context = idr_find(&fpriv->legacy_contexts, args->context);
if (!context) { if (!context) {
err = -EINVAL; err = -EINVAL;
goto unlock; goto unlock;
} }
idr_remove(&fpriv->contexts, context->id); idr_remove(&fpriv->legacy_contexts, context->id);
tegra_drm_context_free(context); tegra_drm_context_free(context);
unlock: unlock:
...@@ -512,7 +511,7 @@ static int tegra_get_syncpt(struct drm_device *drm, void *data, ...@@ -512,7 +511,7 @@ static int tegra_get_syncpt(struct drm_device *drm, void *data,
mutex_lock(&fpriv->lock); mutex_lock(&fpriv->lock);
context = idr_find(&fpriv->contexts, args->context); context = idr_find(&fpriv->legacy_contexts, args->context);
if (!context) { if (!context) {
err = -ENODEV; err = -ENODEV;
goto unlock; goto unlock;
...@@ -541,7 +540,7 @@ static int tegra_submit(struct drm_device *drm, void *data, ...@@ -541,7 +540,7 @@ static int tegra_submit(struct drm_device *drm, void *data,
mutex_lock(&fpriv->lock); mutex_lock(&fpriv->lock);
context = idr_find(&fpriv->contexts, args->context); context = idr_find(&fpriv->legacy_contexts, args->context);
if (!context) { if (!context) {
err = -ENODEV; err = -ENODEV;
goto unlock; goto unlock;
...@@ -566,7 +565,7 @@ static int tegra_get_syncpt_base(struct drm_device *drm, void *data, ...@@ -566,7 +565,7 @@ static int tegra_get_syncpt_base(struct drm_device *drm, void *data,
mutex_lock(&fpriv->lock); mutex_lock(&fpriv->lock);
context = idr_find(&fpriv->contexts, args->context); context = idr_find(&fpriv->legacy_contexts, args->context);
if (!context) { if (!context) {
err = -ENODEV; err = -ENODEV;
goto unlock; goto unlock;
...@@ -735,10 +734,25 @@ static int tegra_gem_get_flags(struct drm_device *drm, void *data, ...@@ -735,10 +734,25 @@ static int tegra_gem_get_flags(struct drm_device *drm, void *data,
static const struct drm_ioctl_desc tegra_drm_ioctls[] = { static const struct drm_ioctl_desc tegra_drm_ioctls[] = {
#ifdef CONFIG_DRM_TEGRA_STAGING #ifdef CONFIG_DRM_TEGRA_STAGING
DRM_IOCTL_DEF_DRV(TEGRA_GEM_CREATE, tegra_gem_create, DRM_IOCTL_DEF_DRV(TEGRA_CHANNEL_OPEN, tegra_drm_ioctl_channel_open,
DRM_RENDER_ALLOW),
DRM_IOCTL_DEF_DRV(TEGRA_CHANNEL_CLOSE, tegra_drm_ioctl_channel_close,
DRM_RENDER_ALLOW),
DRM_IOCTL_DEF_DRV(TEGRA_CHANNEL_MAP, tegra_drm_ioctl_channel_map,
DRM_RENDER_ALLOW),
DRM_IOCTL_DEF_DRV(TEGRA_CHANNEL_UNMAP, tegra_drm_ioctl_channel_unmap,
DRM_RENDER_ALLOW),
DRM_IOCTL_DEF_DRV(TEGRA_CHANNEL_SUBMIT, tegra_drm_ioctl_channel_submit,
DRM_RENDER_ALLOW),
DRM_IOCTL_DEF_DRV(TEGRA_SYNCPOINT_ALLOCATE, tegra_drm_ioctl_syncpoint_allocate,
DRM_RENDER_ALLOW), DRM_RENDER_ALLOW),
DRM_IOCTL_DEF_DRV(TEGRA_GEM_MMAP, tegra_gem_mmap, DRM_IOCTL_DEF_DRV(TEGRA_SYNCPOINT_FREE, tegra_drm_ioctl_syncpoint_free,
DRM_RENDER_ALLOW), DRM_RENDER_ALLOW),
DRM_IOCTL_DEF_DRV(TEGRA_SYNCPOINT_WAIT, tegra_drm_ioctl_syncpoint_wait,
DRM_RENDER_ALLOW),
DRM_IOCTL_DEF_DRV(TEGRA_GEM_CREATE, tegra_gem_create, DRM_RENDER_ALLOW),
DRM_IOCTL_DEF_DRV(TEGRA_GEM_MMAP, tegra_gem_mmap, DRM_RENDER_ALLOW),
DRM_IOCTL_DEF_DRV(TEGRA_SYNCPT_READ, tegra_syncpt_read, DRM_IOCTL_DEF_DRV(TEGRA_SYNCPT_READ, tegra_syncpt_read,
DRM_RENDER_ALLOW), DRM_RENDER_ALLOW),
DRM_IOCTL_DEF_DRV(TEGRA_SYNCPT_INCR, tegra_syncpt_incr, DRM_IOCTL_DEF_DRV(TEGRA_SYNCPT_INCR, tegra_syncpt_incr,
...@@ -792,10 +806,11 @@ static void tegra_drm_postclose(struct drm_device *drm, struct drm_file *file) ...@@ -792,10 +806,11 @@ static void tegra_drm_postclose(struct drm_device *drm, struct drm_file *file)
struct tegra_drm_file *fpriv = file->driver_priv; struct tegra_drm_file *fpriv = file->driver_priv;
mutex_lock(&fpriv->lock); mutex_lock(&fpriv->lock);
idr_for_each(&fpriv->contexts, tegra_drm_context_cleanup, NULL); idr_for_each(&fpriv->legacy_contexts, tegra_drm_context_cleanup, NULL);
tegra_drm_uapi_close_file(fpriv);
mutex_unlock(&fpriv->lock); mutex_unlock(&fpriv->lock);
idr_destroy(&fpriv->contexts); idr_destroy(&fpriv->legacy_contexts);
mutex_destroy(&fpriv->lock); mutex_destroy(&fpriv->lock);
kfree(fpriv); kfree(fpriv);
} }
...@@ -853,7 +868,7 @@ static void tegra_debugfs_init(struct drm_minor *minor) ...@@ -853,7 +868,7 @@ static void tegra_debugfs_init(struct drm_minor *minor)
static const struct drm_driver tegra_drm_driver = { static const struct drm_driver tegra_drm_driver = {
.driver_features = DRIVER_MODESET | DRIVER_GEM | .driver_features = DRIVER_MODESET | DRIVER_GEM |
DRIVER_ATOMIC | DRIVER_RENDER, DRIVER_ATOMIC | DRIVER_RENDER | DRIVER_SYNCOBJ,
.open = tegra_drm_open, .open = tegra_drm_open,
.postclose = tegra_drm_postclose, .postclose = tegra_drm_postclose,
.lastclose = drm_fb_helper_lastclose, .lastclose = drm_fb_helper_lastclose,
...@@ -883,6 +898,14 @@ static const struct drm_driver tegra_drm_driver = { ...@@ -883,6 +898,14 @@ static const struct drm_driver tegra_drm_driver = {
int tegra_drm_register_client(struct tegra_drm *tegra, int tegra_drm_register_client(struct tegra_drm *tegra,
struct tegra_drm_client *client) struct tegra_drm_client *client)
{ {
/*
* When MLOCKs are implemented, change to allocate a shared channel
* only when MLOCKs are disabled.
*/
client->shared_channel = host1x_channel_request(&client->base);
if (!client->shared_channel)
return -EBUSY;
mutex_lock(&tegra->clients_lock); mutex_lock(&tegra->clients_lock);
list_add_tail(&client->list, &tegra->clients); list_add_tail(&client->list, &tegra->clients);
client->drm = tegra; client->drm = tegra;
...@@ -899,6 +922,9 @@ int tegra_drm_unregister_client(struct tegra_drm *tegra, ...@@ -899,6 +922,9 @@ int tegra_drm_unregister_client(struct tegra_drm *tegra,
client->drm = NULL; client->drm = NULL;
mutex_unlock(&tegra->clients_lock); mutex_unlock(&tegra->clients_lock);
if (client->shared_channel)
host1x_channel_put(client->shared_channel);
return 0; return 0;
} }
......
...@@ -64,12 +64,22 @@ struct tegra_drm { ...@@ -64,12 +64,22 @@ struct tegra_drm {
struct tegra_display_hub *hub; struct tegra_display_hub *hub;
}; };
static inline struct host1x *tegra_drm_to_host1x(struct tegra_drm *tegra)
{
return dev_get_drvdata(tegra->drm->dev->parent);
}
struct tegra_drm_client; struct tegra_drm_client;
struct tegra_drm_context { struct tegra_drm_context {
struct tegra_drm_client *client; struct tegra_drm_client *client;
struct host1x_channel *channel; struct host1x_channel *channel;
/* Only used by legacy UAPI. */
unsigned int id; unsigned int id;
/* Only used by new UAPI. */
struct xarray mappings;
}; };
struct tegra_drm_client_ops { struct tegra_drm_client_ops {
...@@ -91,7 +101,9 @@ struct tegra_drm_client { ...@@ -91,7 +101,9 @@ struct tegra_drm_client {
struct host1x_client base; struct host1x_client base;
struct list_head list; struct list_head list;
struct tegra_drm *drm; struct tegra_drm *drm;
struct host1x_channel *shared_channel;
/* Set by driver */
unsigned int version; unsigned int version;
const struct tegra_drm_client_ops *ops; const struct tegra_drm_client_ops *ops;
}; };
......
// SPDX-License-Identifier: GPL-2.0-only
/* Copyright (c) 2010-2020 NVIDIA Corporation */
#include "drm.h"
#include "submit.h"
#include "uapi.h"
struct tegra_drm_firewall {
struct tegra_drm_submit_data *submit;
struct tegra_drm_client *client;
u32 *data;
u32 pos;
u32 end;
u32 class;
};
static int fw_next(struct tegra_drm_firewall *fw, u32 *word)
{
if (fw->pos == fw->end)
return -EINVAL;
*word = fw->data[fw->pos++];
return 0;
}
static bool fw_check_addr_valid(struct tegra_drm_firewall *fw, u32 offset)
{
u32 i;
for (i = 0; i < fw->submit->num_used_mappings; i++) {
struct tegra_drm_mapping *m = fw->submit->used_mappings[i].mapping;
if (offset >= m->iova && offset <= m->iova_end)
return true;
}
return false;
}
static int fw_check_reg(struct tegra_drm_firewall *fw, u32 offset)
{
bool is_addr;
u32 word;
int err;
err = fw_next(fw, &word);
if (err)
return err;
if (!fw->client->ops->is_addr_reg)
return 0;
is_addr = fw->client->ops->is_addr_reg(fw->client->base.dev, fw->class,
offset);
if (!is_addr)
return 0;
if (!fw_check_addr_valid(fw, word))
return -EINVAL;
return 0;
}
static int fw_check_regs_seq(struct tegra_drm_firewall *fw, u32 offset,
u32 count, bool incr)
{
u32 i;
for (i = 0; i < count; i++) {
if (fw_check_reg(fw, offset))
return -EINVAL;
if (incr)
offset++;
}
return 0;
}
static int fw_check_regs_mask(struct tegra_drm_firewall *fw, u32 offset,
u16 mask)
{
unsigned long bmask = mask;
unsigned int bit;
for_each_set_bit(bit, &bmask, 16) {
if (fw_check_reg(fw, offset+bit))
return -EINVAL;
}
return 0;
}
static int fw_check_regs_imm(struct tegra_drm_firewall *fw, u32 offset)
{
bool is_addr;
is_addr = fw->client->ops->is_addr_reg(fw->client->base.dev, fw->class,
offset);
if (is_addr)
return -EINVAL;
return 0;
}
static int fw_check_class(struct tegra_drm_firewall *fw, u32 class)
{
if (!fw->client->ops->is_valid_class) {
if (class == fw->client->base.class)
return 0;
else
return -EINVAL;
}
if (!fw->client->ops->is_valid_class(class))
return -EINVAL;
return 0;
}
enum {
HOST1X_OPCODE_SETCLASS = 0x00,
HOST1X_OPCODE_INCR = 0x01,
HOST1X_OPCODE_NONINCR = 0x02,
HOST1X_OPCODE_MASK = 0x03,
HOST1X_OPCODE_IMM = 0x04,
HOST1X_OPCODE_RESTART = 0x05,
HOST1X_OPCODE_GATHER = 0x06,
HOST1X_OPCODE_SETSTRMID = 0x07,
HOST1X_OPCODE_SETAPPID = 0x08,
HOST1X_OPCODE_SETPYLD = 0x09,
HOST1X_OPCODE_INCR_W = 0x0a,
HOST1X_OPCODE_NONINCR_W = 0x0b,
HOST1X_OPCODE_GATHER_W = 0x0c,
HOST1X_OPCODE_RESTART_W = 0x0d,
HOST1X_OPCODE_EXTEND = 0x0e,
};
int tegra_drm_fw_validate(struct tegra_drm_client *client, u32 *data, u32 start,
u32 words, struct tegra_drm_submit_data *submit,
u32 *job_class)
{
struct tegra_drm_firewall fw = {
.submit = submit,
.client = client,
.data = data,
.pos = start,
.end = start+words,
.class = *job_class,
};
bool payload_valid = false;
u32 payload;
int err;
while (fw.pos != fw.end) {
u32 word, opcode, offset, count, mask, class;
err = fw_next(&fw, &word);
if (err)
return err;
opcode = (word & 0xf0000000) >> 28;
switch (opcode) {
case HOST1X_OPCODE_SETCLASS:
offset = word >> 16 & 0xfff;
mask = word & 0x3f;
class = (word >> 6) & 0x3ff;
err = fw_check_class(&fw, class);
fw.class = class;
*job_class = class;
if (!err)
err = fw_check_regs_mask(&fw, offset, mask);
if (err)
dev_warn(client->base.dev,
"illegal SETCLASS(offset=0x%x, mask=0x%x, class=0x%x) at word %u",
offset, mask, class, fw.pos-1);
break;
case HOST1X_OPCODE_INCR:
offset = (word >> 16) & 0xfff;
count = word & 0xffff;
err = fw_check_regs_seq(&fw, offset, count, true);
if (err)
dev_warn(client->base.dev,
"illegal INCR(offset=0x%x, count=%u) in class 0x%x at word %u",
offset, count, fw.class, fw.pos-1);
break;
case HOST1X_OPCODE_NONINCR:
offset = (word >> 16) & 0xfff;
count = word & 0xffff;
err = fw_check_regs_seq(&fw, offset, count, false);
if (err)
dev_warn(client->base.dev,
"illegal NONINCR(offset=0x%x, count=%u) in class 0x%x at word %u",
offset, count, fw.class, fw.pos-1);
break;
case HOST1X_OPCODE_MASK:
offset = (word >> 16) & 0xfff;
mask = word & 0xffff;
err = fw_check_regs_mask(&fw, offset, mask);
if (err)
dev_warn(client->base.dev,
"illegal MASK(offset=0x%x, mask=0x%x) in class 0x%x at word %u",
offset, mask, fw.class, fw.pos-1);
break;
case HOST1X_OPCODE_IMM:
/* IMM cannot reasonably be used to write a pointer */
offset = (word >> 16) & 0xfff;
err = fw_check_regs_imm(&fw, offset);
if (err)
dev_warn(client->base.dev,
"illegal IMM(offset=0x%x) in class 0x%x at word %u",
offset, fw.class, fw.pos-1);
break;
case HOST1X_OPCODE_SETPYLD:
payload = word & 0xffff;
payload_valid = true;
break;
case HOST1X_OPCODE_INCR_W:
if (!payload_valid)
return -EINVAL;
offset = word & 0x3fffff;
err = fw_check_regs_seq(&fw, offset, payload, true);
if (err)
dev_warn(client->base.dev,
"illegal INCR_W(offset=0x%x) in class 0x%x at word %u",
offset, fw.class, fw.pos-1);
break;
case HOST1X_OPCODE_NONINCR_W:
if (!payload_valid)
return -EINVAL;
offset = word & 0x3fffff;
err = fw_check_regs_seq(&fw, offset, payload, false);
if (err)
dev_warn(client->base.dev,
"illegal NONINCR(offset=0x%x) in class 0x%x at word %u",
offset, fw.class, fw.pos-1);
break;
default:
dev_warn(client->base.dev, "illegal opcode at word %u",
fw.pos-1);
return -EINVAL;
}
if (err)
return err;
}
return 0;
}
...@@ -707,3 +707,16 @@ struct drm_gem_object *tegra_gem_prime_import(struct drm_device *drm, ...@@ -707,3 +707,16 @@ struct drm_gem_object *tegra_gem_prime_import(struct drm_device *drm,
return &bo->gem; return &bo->gem;
} }
struct host1x_bo *tegra_gem_lookup(struct drm_file *file, u32 handle)
{
struct drm_gem_object *gem;
struct tegra_bo *bo;
gem = drm_gem_object_lookup(file, handle);
if (!gem)
return NULL;
bo = to_tegra_bo(gem);
return &bo->base;
}
...@@ -80,4 +80,6 @@ struct dma_buf *tegra_gem_prime_export(struct drm_gem_object *gem, ...@@ -80,4 +80,6 @@ struct dma_buf *tegra_gem_prime_export(struct drm_gem_object *gem,
struct drm_gem_object *tegra_gem_prime_import(struct drm_device *drm, struct drm_gem_object *tegra_gem_prime_import(struct drm_device *drm,
struct dma_buf *buf); struct dma_buf *buf);
struct host1x_bo *tegra_gem_lookup(struct drm_file *file, u32 handle);
#endif #endif
...@@ -4,6 +4,7 @@ ...@@ -4,6 +4,7 @@
*/ */
#include <linux/iommu.h> #include <linux/iommu.h>
#include <linux/interconnect.h>
#include <drm/drm_atomic.h> #include <drm/drm_atomic.h>
#include <drm/drm_atomic_helper.h> #include <drm/drm_atomic_helper.h>
...@@ -64,6 +65,9 @@ tegra_plane_atomic_duplicate_state(struct drm_plane *plane) ...@@ -64,6 +65,9 @@ tegra_plane_atomic_duplicate_state(struct drm_plane *plane)
copy->reflect_x = state->reflect_x; copy->reflect_x = state->reflect_x;
copy->reflect_y = state->reflect_y; copy->reflect_y = state->reflect_y;
copy->opaque = state->opaque; copy->opaque = state->opaque;
copy->total_peak_memory_bandwidth = state->total_peak_memory_bandwidth;
copy->peak_memory_bandwidth = state->peak_memory_bandwidth;
copy->avg_memory_bandwidth = state->avg_memory_bandwidth;
for (i = 0; i < 2; i++) for (i = 0; i < 2; i++)
copy->blending[i] = state->blending[i]; copy->blending[i] = state->blending[i];
...@@ -244,6 +248,78 @@ void tegra_plane_cleanup_fb(struct drm_plane *plane, ...@@ -244,6 +248,78 @@ void tegra_plane_cleanup_fb(struct drm_plane *plane,
tegra_dc_unpin(dc, to_tegra_plane_state(state)); tegra_dc_unpin(dc, to_tegra_plane_state(state));
} }
static int tegra_plane_calculate_memory_bandwidth(struct drm_plane_state *state)
{
struct tegra_plane_state *tegra_state = to_tegra_plane_state(state);
unsigned int i, bpp, dst_w, dst_h, src_w, src_h, mul;
const struct tegra_dc_soc_info *soc;
const struct drm_format_info *fmt;
struct drm_crtc_state *crtc_state;
u64 avg_bandwidth, peak_bandwidth;
if (!state->visible)
return 0;
crtc_state = drm_atomic_get_new_crtc_state(state->state, state->crtc);
if (!crtc_state)
return -EINVAL;
src_w = drm_rect_width(&state->src) >> 16;
src_h = drm_rect_height(&state->src) >> 16;
dst_w = drm_rect_width(&state->dst);
dst_h = drm_rect_height(&state->dst);
fmt = state->fb->format;
soc = to_tegra_dc(state->crtc)->soc;
/*
* Note that real memory bandwidth vary depending on format and
* memory layout, we are not taking that into account because small
* estimation error isn't important since bandwidth is rounded up
* anyway.
*/
for (i = 0, bpp = 0; i < fmt->num_planes; i++) {
unsigned int bpp_plane = fmt->cpp[i] * 8;
/*
* Sub-sampling is relevant for chroma planes only and vertical
* readouts are not cached, hence only horizontal sub-sampling
* matters.
*/
if (i > 0)
bpp_plane /= fmt->hsub;
bpp += bpp_plane;
}
/* average bandwidth in kbytes/sec */
avg_bandwidth = min(src_w, dst_w) * min(src_h, dst_h);
avg_bandwidth *= drm_mode_vrefresh(&crtc_state->adjusted_mode);
avg_bandwidth = DIV_ROUND_UP(avg_bandwidth * bpp, 8) + 999;
do_div(avg_bandwidth, 1000);
/* mode.clock in kHz, peak bandwidth in kbytes/sec */
peak_bandwidth = DIV_ROUND_UP(crtc_state->adjusted_mode.clock * bpp, 8);
/*
* Tegra30/114 Memory Controller can't interleave DC memory requests
* for the tiled windows because DC uses 16-bytes atom, while DDR3
* uses 32-bytes atom. Hence there is x2 memory overfetch for tiled
* framebuffer and DDR3 on these SoCs.
*/
if (soc->plane_tiled_memory_bandwidth_x2 &&
tegra_state->tiling.mode == TEGRA_BO_TILING_MODE_TILED)
mul = 2;
else
mul = 1;
/* ICC bandwidth in kbytes/sec */
tegra_state->peak_memory_bandwidth = kBps_to_icc(peak_bandwidth) * mul;
tegra_state->avg_memory_bandwidth = kBps_to_icc(avg_bandwidth) * mul;
return 0;
}
int tegra_plane_state_add(struct tegra_plane *plane, int tegra_plane_state_add(struct tegra_plane *plane,
struct drm_plane_state *state) struct drm_plane_state *state)
{ {
...@@ -262,6 +338,10 @@ int tegra_plane_state_add(struct tegra_plane *plane, ...@@ -262,6 +338,10 @@ int tegra_plane_state_add(struct tegra_plane *plane,
if (err < 0) if (err < 0)
return err; return err;
err = tegra_plane_calculate_memory_bandwidth(state);
if (err < 0)
return err;
tegra = to_dc_state(crtc_state); tegra = to_dc_state(crtc_state);
tegra->planes |= WIN_A_ACT_REQ << plane->index; tegra->planes |= WIN_A_ACT_REQ << plane->index;
...@@ -646,3 +726,40 @@ int tegra_plane_setup_legacy_state(struct tegra_plane *tegra, ...@@ -646,3 +726,40 @@ int tegra_plane_setup_legacy_state(struct tegra_plane *tegra,
return 0; return 0;
} }
static const char * const tegra_plane_icc_names[TEGRA_DC_LEGACY_PLANES_NUM] = {
"wina", "winb", "winc", NULL, NULL, NULL, "cursor",
};
int tegra_plane_interconnect_init(struct tegra_plane *plane)
{
const char *icc_name = tegra_plane_icc_names[plane->index];
struct device *dev = plane->dc->dev;
struct tegra_dc *dc = plane->dc;
int err;
if (WARN_ON(plane->index >= TEGRA_DC_LEGACY_PLANES_NUM) ||
WARN_ON(!tegra_plane_icc_names[plane->index]))
return -EINVAL;
plane->icc_mem = devm_of_icc_get(dev, icc_name);
err = PTR_ERR_OR_ZERO(plane->icc_mem);
if (err) {
dev_err_probe(dev, err, "failed to get %s interconnect\n",
icc_name);
return err;
}
/* plane B on T20/30 has a dedicated memory client for a 6-tap vertical filter */
if (plane->index == 1 && dc->soc->has_win_b_vfilter_mem_client) {
plane->icc_mem_vfilter = devm_of_icc_get(dev, "winb-vfilter");
err = PTR_ERR_OR_ZERO(plane->icc_mem_vfilter);
if (err) {
dev_err_probe(dev, err, "failed to get %s interconnect\n",
"winb-vfilter");
return err;
}
}
return 0;
}
...@@ -8,6 +8,7 @@ ...@@ -8,6 +8,7 @@
#include <drm/drm_plane.h> #include <drm/drm_plane.h>
struct icc_path;
struct tegra_bo; struct tegra_bo;
struct tegra_dc; struct tegra_dc;
...@@ -16,6 +17,9 @@ struct tegra_plane { ...@@ -16,6 +17,9 @@ struct tegra_plane {
struct tegra_dc *dc; struct tegra_dc *dc;
unsigned int offset; unsigned int offset;
unsigned int index; unsigned int index;
struct icc_path *icc_mem;
struct icc_path *icc_mem_vfilter;
}; };
struct tegra_cursor { struct tegra_cursor {
...@@ -52,6 +56,11 @@ struct tegra_plane_state { ...@@ -52,6 +56,11 @@ struct tegra_plane_state {
/* used for legacy blending support only */ /* used for legacy blending support only */
struct tegra_plane_legacy_blending_state blending[2]; struct tegra_plane_legacy_blending_state blending[2];
bool opaque; bool opaque;
/* bandwidths are in ICC units, i.e. kbytes/sec */
u32 total_peak_memory_bandwidth;
u32 peak_memory_bandwidth;
u32 avg_memory_bandwidth;
}; };
static inline struct tegra_plane_state * static inline struct tegra_plane_state *
...@@ -63,6 +72,12 @@ to_tegra_plane_state(struct drm_plane_state *state) ...@@ -63,6 +72,12 @@ to_tegra_plane_state(struct drm_plane_state *state)
return NULL; return NULL;
} }
static inline const struct tegra_plane_state *
to_const_tegra_plane_state(const struct drm_plane_state *state)
{
return to_tegra_plane_state((struct drm_plane_state *)state);
}
extern const struct drm_plane_funcs tegra_plane_funcs; extern const struct drm_plane_funcs tegra_plane_funcs;
int tegra_plane_prepare_fb(struct drm_plane *plane, int tegra_plane_prepare_fb(struct drm_plane *plane,
...@@ -78,5 +93,6 @@ bool tegra_plane_format_is_indexed(unsigned int format); ...@@ -78,5 +93,6 @@ bool tegra_plane_format_is_indexed(unsigned int format);
bool tegra_plane_format_is_yuv(unsigned int format, bool *planar, unsigned int *bpc); bool tegra_plane_format_is_yuv(unsigned int format, bool *planar, unsigned int *bpc);
int tegra_plane_setup_legacy_state(struct tegra_plane *tegra, int tegra_plane_setup_legacy_state(struct tegra_plane *tegra,
struct tegra_plane_state *state); struct tegra_plane_state *state);
int tegra_plane_interconnect_init(struct tegra_plane *plane);
#endif /* TEGRA_PLANE_H */ #endif /* TEGRA_PLANE_H */
This diff is collapsed.
/* SPDX-License-Identifier: GPL-2.0-only */
/* Copyright (c) 2020 NVIDIA Corporation */
#ifndef _TEGRA_DRM_UAPI_SUBMIT_H
#define _TEGRA_DRM_UAPI_SUBMIT_H
struct tegra_drm_used_mapping {
struct tegra_drm_mapping *mapping;
u32 flags;
};
struct tegra_drm_submit_data {
struct tegra_drm_used_mapping *used_mappings;
u32 num_used_mappings;
};
int tegra_drm_fw_validate(struct tegra_drm_client *client, u32 *data, u32 start,
u32 words, struct tegra_drm_submit_data *submit,
u32 *job_class);
#endif
// SPDX-License-Identifier: GPL-2.0-only
/* Copyright (c) 2020 NVIDIA Corporation */
#include <linux/host1x.h>
#include <linux/iommu.h>
#include <linux/list.h>
#include <drm/drm_drv.h>
#include <drm/drm_file.h>
#include <drm/drm_utils.h>
#include "drm.h"
#include "uapi.h"
static void tegra_drm_mapping_release(struct kref *ref)
{
struct tegra_drm_mapping *mapping =
container_of(ref, struct tegra_drm_mapping, ref);
if (mapping->sgt)
dma_unmap_sgtable(mapping->dev, mapping->sgt, mapping->direction,
DMA_ATTR_SKIP_CPU_SYNC);
host1x_bo_unpin(mapping->dev, mapping->bo, mapping->sgt);
host1x_bo_put(mapping->bo);
kfree(mapping);
}
void tegra_drm_mapping_put(struct tegra_drm_mapping *mapping)
{
kref_put(&mapping->ref, tegra_drm_mapping_release);
}
static void tegra_drm_channel_context_close(struct tegra_drm_context *context)
{
struct tegra_drm_mapping *mapping;
unsigned long id;
xa_for_each(&context->mappings, id, mapping)
tegra_drm_mapping_put(mapping);
xa_destroy(&context->mappings);
host1x_channel_put(context->channel);
kfree(context);
}
void tegra_drm_uapi_close_file(struct tegra_drm_file *file)
{
struct tegra_drm_context *context;
struct host1x_syncpt *sp;
unsigned long id;
xa_for_each(&file->contexts, id, context)
tegra_drm_channel_context_close(context);
xa_for_each(&file->syncpoints, id, sp)
host1x_syncpt_put(sp);
xa_destroy(&file->contexts);
xa_destroy(&file->syncpoints);
}
static struct tegra_drm_client *tegra_drm_find_client(struct tegra_drm *tegra, u32 class)
{
struct tegra_drm_client *client;
list_for_each_entry(client, &tegra->clients, list)
if (client->base.class == class)
return client;
return NULL;
}
int tegra_drm_ioctl_channel_open(struct drm_device *drm, void *data, struct drm_file *file)
{
struct tegra_drm_file *fpriv = file->driver_priv;
struct tegra_drm *tegra = drm->dev_private;
struct drm_tegra_channel_open *args = data;
struct tegra_drm_client *client = NULL;
struct tegra_drm_context *context;
int err;
if (args->flags)
return -EINVAL;
context = kzalloc(sizeof(*context), GFP_KERNEL);
if (!context)
return -ENOMEM;
client = tegra_drm_find_client(tegra, args->host1x_class);
if (!client) {
err = -ENODEV;
goto free;
}
if (client->shared_channel) {
context->channel = host1x_channel_get(client->shared_channel);
} else {
context->channel = host1x_channel_request(&client->base);
if (!context->channel) {
err = -EBUSY;
goto free;
}
}
err = xa_alloc(&fpriv->contexts, &args->context, context, XA_LIMIT(1, U32_MAX),
GFP_KERNEL);
if (err < 0)
goto put_channel;
context->client = client;
xa_init_flags(&context->mappings, XA_FLAGS_ALLOC1);
args->version = client->version;
args->capabilities = 0;
if (device_get_dma_attr(client->base.dev) == DEV_DMA_COHERENT)
args->capabilities |= DRM_TEGRA_CHANNEL_CAP_CACHE_COHERENT;
return 0;
put_channel:
host1x_channel_put(context->channel);
free:
kfree(context);
return err;
}
int tegra_drm_ioctl_channel_close(struct drm_device *drm, void *data, struct drm_file *file)
{
struct tegra_drm_file *fpriv = file->driver_priv;
struct drm_tegra_channel_close *args = data;
struct tegra_drm_context *context;
mutex_lock(&fpriv->lock);
context = xa_load(&fpriv->contexts, args->context);
if (!context) {
mutex_unlock(&fpriv->lock);
return -EINVAL;
}
xa_erase(&fpriv->contexts, args->context);
mutex_unlock(&fpriv->lock);
tegra_drm_channel_context_close(context);
return 0;
}
int tegra_drm_ioctl_channel_map(struct drm_device *drm, void *data, struct drm_file *file)
{
struct tegra_drm_file *fpriv = file->driver_priv;
struct drm_tegra_channel_map *args = data;
struct tegra_drm_mapping *mapping;
struct tegra_drm_context *context;
int err = 0;
if (args->flags & ~DRM_TEGRA_CHANNEL_MAP_READ_WRITE)
return -EINVAL;
mutex_lock(&fpriv->lock);
context = xa_load(&fpriv->contexts, args->context);
if (!context) {
mutex_unlock(&fpriv->lock);
return -EINVAL;
}
mapping = kzalloc(sizeof(*mapping), GFP_KERNEL);
if (!mapping) {
err = -ENOMEM;
goto unlock;
}
kref_init(&mapping->ref);
mapping->dev = context->client->base.dev;
mapping->bo = tegra_gem_lookup(file, args->handle);
if (!mapping->bo) {
err = -EINVAL;
goto unlock;
}
if (context->client->base.group) {
/* IOMMU domain managed directly using IOMMU API */
host1x_bo_pin(mapping->dev, mapping->bo, &mapping->iova);
} else {
switch (args->flags & DRM_TEGRA_CHANNEL_MAP_READ_WRITE) {
case DRM_TEGRA_CHANNEL_MAP_READ_WRITE:
mapping->direction = DMA_BIDIRECTIONAL;
break;
case DRM_TEGRA_CHANNEL_MAP_WRITE:
mapping->direction = DMA_FROM_DEVICE;
break;
case DRM_TEGRA_CHANNEL_MAP_READ:
mapping->direction = DMA_TO_DEVICE;
break;
default:
return -EINVAL;
}
mapping->sgt = host1x_bo_pin(mapping->dev, mapping->bo, NULL);
if (IS_ERR(mapping->sgt)) {
err = PTR_ERR(mapping->sgt);
goto put_gem;
}
err = dma_map_sgtable(mapping->dev, mapping->sgt, mapping->direction,
DMA_ATTR_SKIP_CPU_SYNC);
if (err)
goto unpin;
mapping->iova = sg_dma_address(mapping->sgt->sgl);
}
mapping->iova_end = mapping->iova + host1x_to_tegra_bo(mapping->bo)->size;
err = xa_alloc(&context->mappings, &args->mapping, mapping, XA_LIMIT(1, U32_MAX),
GFP_KERNEL);
if (err < 0)
goto unmap;
mutex_unlock(&fpriv->lock);
return 0;
unmap:
if (mapping->sgt) {
dma_unmap_sgtable(mapping->dev, mapping->sgt, mapping->direction,
DMA_ATTR_SKIP_CPU_SYNC);
}
unpin:
host1x_bo_unpin(mapping->dev, mapping->bo, mapping->sgt);
put_gem:
host1x_bo_put(mapping->bo);
kfree(mapping);
unlock:
mutex_unlock(&fpriv->lock);
return err;
}
int tegra_drm_ioctl_channel_unmap(struct drm_device *drm, void *data, struct drm_file *file)
{
struct tegra_drm_file *fpriv = file->driver_priv;
struct drm_tegra_channel_unmap *args = data;
struct tegra_drm_mapping *mapping;
struct tegra_drm_context *context;
mutex_lock(&fpriv->lock);
context = xa_load(&fpriv->contexts, args->context);
if (!context) {
mutex_unlock(&fpriv->lock);
return -EINVAL;
}
mapping = xa_erase(&context->mappings, args->mapping);
mutex_unlock(&fpriv->lock);
if (!mapping)
return -EINVAL;
tegra_drm_mapping_put(mapping);
return 0;
}
int tegra_drm_ioctl_syncpoint_allocate(struct drm_device *drm, void *data, struct drm_file *file)
{
struct host1x *host1x = tegra_drm_to_host1x(drm->dev_private);
struct tegra_drm_file *fpriv = file->driver_priv;
struct drm_tegra_syncpoint_allocate *args = data;
struct host1x_syncpt *sp;
int err;
if (args->id)
return -EINVAL;
sp = host1x_syncpt_alloc(host1x, HOST1X_SYNCPT_CLIENT_MANAGED, current->comm);
if (!sp)
return -EBUSY;
args->id = host1x_syncpt_id(sp);
err = xa_insert(&fpriv->syncpoints, args->id, sp, GFP_KERNEL);
if (err) {
host1x_syncpt_put(sp);
return err;
}
return 0;
}
int tegra_drm_ioctl_syncpoint_free(struct drm_device *drm, void *data, struct drm_file *file)
{
struct tegra_drm_file *fpriv = file->driver_priv;
struct drm_tegra_syncpoint_allocate *args = data;
struct host1x_syncpt *sp;
mutex_lock(&fpriv->lock);
sp = xa_erase(&fpriv->syncpoints, args->id);
mutex_unlock(&fpriv->lock);
if (!sp)
return -EINVAL;
host1x_syncpt_put(sp);
return 0;
}
int tegra_drm_ioctl_syncpoint_wait(struct drm_device *drm, void *data, struct drm_file *file)
{
struct host1x *host1x = tegra_drm_to_host1x(drm->dev_private);
struct drm_tegra_syncpoint_wait *args = data;
signed long timeout_jiffies;
struct host1x_syncpt *sp;
if (args->padding != 0)
return -EINVAL;
sp = host1x_syncpt_get_by_id_noref(host1x, args->id);
if (!sp)
return -EINVAL;
timeout_jiffies = drm_timeout_abs_to_jiffies(args->timeout_ns);
return host1x_syncpt_wait(sp, args->threshold, timeout_jiffies, &args->value);
}
/* SPDX-License-Identifier: GPL-2.0-only */
/* Copyright (c) 2020 NVIDIA Corporation */
#ifndef _TEGRA_DRM_UAPI_H
#define _TEGRA_DRM_UAPI_H
#include <linux/dma-mapping.h>
#include <linux/idr.h>
#include <linux/kref.h>
#include <linux/xarray.h>
#include <drm/drm.h>
struct drm_file;
struct drm_device;
struct tegra_drm_file {
/* Legacy UAPI state */
struct idr legacy_contexts;
struct mutex lock;
/* New UAPI state */
struct xarray contexts;
struct xarray syncpoints;
};
struct tegra_drm_mapping {
struct kref ref;
struct device *dev;
struct host1x_bo *bo;
struct sg_table *sgt;
enum dma_data_direction direction;
dma_addr_t iova;
dma_addr_t iova_end;
};
int tegra_drm_ioctl_channel_open(struct drm_device *drm, void *data,
struct drm_file *file);
int tegra_drm_ioctl_channel_close(struct drm_device *drm, void *data,
struct drm_file *file);
int tegra_drm_ioctl_channel_map(struct drm_device *drm, void *data,
struct drm_file *file);
int tegra_drm_ioctl_channel_unmap(struct drm_device *drm, void *data,
struct drm_file *file);
int tegra_drm_ioctl_channel_submit(struct drm_device *drm, void *data,
struct drm_file *file);
int tegra_drm_ioctl_syncpoint_allocate(struct drm_device *drm, void *data,
struct drm_file *file);
int tegra_drm_ioctl_syncpoint_free(struct drm_device *drm, void *data,
struct drm_file *file);
int tegra_drm_ioctl_syncpoint_wait(struct drm_device *drm, void *data,
struct drm_file *file);
void tegra_drm_uapi_close_file(struct tegra_drm_file *file);
void tegra_drm_mapping_put(struct tegra_drm_mapping *mapping);
#endif
...@@ -29,7 +29,6 @@ struct vic_config { ...@@ -29,7 +29,6 @@ struct vic_config {
struct vic { struct vic {
struct falcon falcon; struct falcon falcon;
bool booted;
void __iomem *regs; void __iomem *regs;
struct tegra_drm_client client; struct tegra_drm_client client;
...@@ -52,48 +51,6 @@ static void vic_writel(struct vic *vic, u32 value, unsigned int offset) ...@@ -52,48 +51,6 @@ static void vic_writel(struct vic *vic, u32 value, unsigned int offset)
writel(value, vic->regs + offset); writel(value, vic->regs + offset);
} }
static int vic_runtime_resume(struct device *dev)
{
struct vic *vic = dev_get_drvdata(dev);
int err;
err = clk_prepare_enable(vic->clk);
if (err < 0)
return err;
usleep_range(10, 20);
err = reset_control_deassert(vic->rst);
if (err < 0)
goto disable;
usleep_range(10, 20);
return 0;
disable:
clk_disable_unprepare(vic->clk);
return err;
}
static int vic_runtime_suspend(struct device *dev)
{
struct vic *vic = dev_get_drvdata(dev);
int err;
err = reset_control_assert(vic->rst);
if (err < 0)
return err;
usleep_range(2000, 4000);
clk_disable_unprepare(vic->clk);
vic->booted = false;
return 0;
}
static int vic_boot(struct vic *vic) static int vic_boot(struct vic *vic)
{ {
#ifdef CONFIG_IOMMU_API #ifdef CONFIG_IOMMU_API
...@@ -103,9 +60,6 @@ static int vic_boot(struct vic *vic) ...@@ -103,9 +60,6 @@ static int vic_boot(struct vic *vic)
void *hdr; void *hdr;
int err = 0; int err = 0;
if (vic->booted)
return 0;
#ifdef CONFIG_IOMMU_API #ifdef CONFIG_IOMMU_API
if (vic->config->supports_sid && spec) { if (vic->config->supports_sid && spec) {
u32 value; u32 value;
...@@ -168,8 +122,6 @@ static int vic_boot(struct vic *vic) ...@@ -168,8 +122,6 @@ static int vic_boot(struct vic *vic)
return err; return err;
} }
vic->booted = true;
return 0; return 0;
} }
...@@ -323,35 +275,74 @@ static int vic_load_firmware(struct vic *vic) ...@@ -323,35 +275,74 @@ static int vic_load_firmware(struct vic *vic)
return err; return err;
} }
static int vic_open_channel(struct tegra_drm_client *client,
struct tegra_drm_context *context) static int vic_runtime_resume(struct device *dev)
{ {
struct vic *vic = to_vic(client); struct vic *vic = dev_get_drvdata(dev);
int err; int err;
err = pm_runtime_resume_and_get(vic->dev); err = clk_prepare_enable(vic->clk);
if (err < 0) if (err < 0)
return err; return err;
usleep_range(10, 20);
err = reset_control_deassert(vic->rst);
if (err < 0)
goto disable;
usleep_range(10, 20);
err = vic_load_firmware(vic); err = vic_load_firmware(vic);
if (err < 0) if (err < 0)
goto rpm_put; goto assert;
err = vic_boot(vic); err = vic_boot(vic);
if (err < 0) if (err < 0)
goto rpm_put; goto assert;
return 0;
assert:
reset_control_assert(vic->rst);
disable:
clk_disable_unprepare(vic->clk);
return err;
}
static int vic_runtime_suspend(struct device *dev)
{
struct vic *vic = dev_get_drvdata(dev);
int err;
err = reset_control_assert(vic->rst);
if (err < 0)
return err;
usleep_range(2000, 4000);
clk_disable_unprepare(vic->clk);
return 0;
}
static int vic_open_channel(struct tegra_drm_client *client,
struct tegra_drm_context *context)
{
struct vic *vic = to_vic(client);
int err;
err = pm_runtime_resume_and_get(vic->dev);
if (err < 0)
return err;
context->channel = host1x_channel_get(vic->channel); context->channel = host1x_channel_get(vic->channel);
if (!context->channel) { if (!context->channel) {
err = -ENOMEM; pm_runtime_put(vic->dev);
goto rpm_put; return -ENOMEM;
} }
return 0; return 0;
rpm_put:
pm_runtime_put(vic->dev);
return err;
} }
static void vic_close_channel(struct tegra_drm_context *context) static void vic_close_channel(struct tegra_drm_context *context)
...@@ -359,7 +350,6 @@ static void vic_close_channel(struct tegra_drm_context *context) ...@@ -359,7 +350,6 @@ static void vic_close_channel(struct tegra_drm_context *context)
struct vic *vic = to_vic(context->client); struct vic *vic = to_vic(context->client);
host1x_channel_put(context->channel); host1x_channel_put(context->channel);
pm_runtime_put(vic->dev); pm_runtime_put(vic->dev);
} }
......
...@@ -9,6 +9,7 @@ host1x-y = \ ...@@ -9,6 +9,7 @@ host1x-y = \
job.o \ job.o \
debug.o \ debug.o \
mipi.o \ mipi.o \
fence.o \
hw/host1x01.o \ hw/host1x01.o \
hw/host1x02.o \ hw/host1x02.o \
hw/host1x04.o \ hw/host1x04.o \
......
...@@ -312,10 +312,6 @@ static void update_cdma_locked(struct host1x_cdma *cdma) ...@@ -312,10 +312,6 @@ static void update_cdma_locked(struct host1x_cdma *cdma)
bool signal = false; bool signal = false;
struct host1x_job *job, *n; struct host1x_job *job, *n;
/* If CDMA is stopped, queue is cleared and we can return */
if (!cdma->running)
return;
/* /*
* Walk the sync queue, reading the sync point registers as necessary, * Walk the sync queue, reading the sync point registers as necessary,
* to consume as many sync queue entries as possible without blocking * to consume as many sync queue entries as possible without blocking
...@@ -324,7 +320,8 @@ static void update_cdma_locked(struct host1x_cdma *cdma) ...@@ -324,7 +320,8 @@ static void update_cdma_locked(struct host1x_cdma *cdma)
struct host1x_syncpt *sp = job->syncpt; struct host1x_syncpt *sp = job->syncpt;
/* Check whether this syncpt has completed, and bail if not */ /* Check whether this syncpt has completed, and bail if not */
if (!host1x_syncpt_is_expired(sp, job->syncpt_end)) { if (!host1x_syncpt_is_expired(sp, job->syncpt_end) &&
!job->cancelled) {
/* Start timer on next pending syncpt */ /* Start timer on next pending syncpt */
if (job->timeout) if (job->timeout)
cdma_start_timer_locked(cdma, job); cdma_start_timer_locked(cdma, job);
...@@ -413,8 +410,11 @@ void host1x_cdma_update_sync_queue(struct host1x_cdma *cdma, ...@@ -413,8 +410,11 @@ void host1x_cdma_update_sync_queue(struct host1x_cdma *cdma,
else else
restart_addr = cdma->last_pos; restart_addr = cdma->last_pos;
if (!job)
goto resume;
/* do CPU increments for the remaining syncpts */ /* do CPU increments for the remaining syncpts */
if (job) { if (job->syncpt_recovery) {
dev_dbg(dev, "%s: perform CPU incr on pending buffers\n", dev_dbg(dev, "%s: perform CPU incr on pending buffers\n",
__func__); __func__);
...@@ -433,8 +433,44 @@ void host1x_cdma_update_sync_queue(struct host1x_cdma *cdma, ...@@ -433,8 +433,44 @@ void host1x_cdma_update_sync_queue(struct host1x_cdma *cdma,
dev_dbg(dev, "%s: finished sync_queue modification\n", dev_dbg(dev, "%s: finished sync_queue modification\n",
__func__); __func__);
} else {
struct host1x_job *failed_job = job;
host1x_job_dump(dev, job);
host1x_syncpt_set_locked(job->syncpt);
failed_job->cancelled = true;
list_for_each_entry_continue(job, &cdma->sync_queue, list) {
unsigned int i;
if (job->syncpt != failed_job->syncpt)
continue;
for (i = 0; i < job->num_slots; i++) {
unsigned int slot = (job->first_get/8 + i) %
HOST1X_PUSHBUFFER_SLOTS;
u32 *mapped = cdma->push_buffer.mapped;
/*
* Overwrite opcodes with 0 word writes
* to offset 0xbad. This does nothing but
* has a easily detected signature in debug
* traces.
*/
mapped[2*slot+0] = 0x1bad0000;
mapped[2*slot+1] = 0x1bad0000;
}
job->cancelled = true;
}
wmb();
update_cdma_locked(cdma);
} }
resume:
/* roll back DMAGET and start up channel again */ /* roll back DMAGET and start up channel again */
host1x_hw_cdma_resume(host1x, cdma, restart_addr); host1x_hw_cdma_resume(host1x, cdma, restart_addr);
} }
...@@ -490,6 +526,16 @@ int host1x_cdma_begin(struct host1x_cdma *cdma, struct host1x_job *job) ...@@ -490,6 +526,16 @@ int host1x_cdma_begin(struct host1x_cdma *cdma, struct host1x_job *job)
mutex_lock(&cdma->lock); mutex_lock(&cdma->lock);
/*
* Check if syncpoint was locked due to previous job timeout.
* This needs to be done within the cdma lock to avoid a race
* with the timeout handler.
*/
if (job->syncpt->locked) {
mutex_unlock(&cdma->lock);
return -EPERM;
}
if (job->timeout) { if (job->timeout) {
/* init state on first submit with timeout value */ /* init state on first submit with timeout value */
if (!cdma->timeout.initialized) { if (!cdma->timeout.initialized) {
......
// SPDX-License-Identifier: GPL-2.0-only
/*
* Syncpoint dma_fence implementation
*
* Copyright (c) 2020, NVIDIA Corporation.
*/
#include <linux/dma-fence.h>
#include <linux/file.h>
#include <linux/fs.h>
#include <linux/slab.h>
#include <linux/sync_file.h>
#include "fence.h"
#include "intr.h"
#include "syncpt.h"
DEFINE_SPINLOCK(lock);
struct host1x_syncpt_fence {
struct dma_fence base;
atomic_t signaling;
struct host1x_syncpt *sp;
u32 threshold;
struct host1x_waitlist *waiter;
void *waiter_ref;
struct delayed_work timeout_work;
};
static const char *host1x_syncpt_fence_get_driver_name(struct dma_fence *f)
{
return "host1x";
}
static const char *host1x_syncpt_fence_get_timeline_name(struct dma_fence *f)
{
return "syncpoint";
}
static struct host1x_syncpt_fence *to_host1x_fence(struct dma_fence *f)
{
return container_of(f, struct host1x_syncpt_fence, base);
}
static bool host1x_syncpt_fence_enable_signaling(struct dma_fence *f)
{
struct host1x_syncpt_fence *sf = to_host1x_fence(f);
int err;
if (host1x_syncpt_is_expired(sf->sp, sf->threshold))
return false;
dma_fence_get(f);
/*
* The dma_fence framework requires the fence driver to keep a
* reference to any fences for which 'enable_signaling' has been
* called (and that have not been signalled).
*
* We provide a userspace API to create arbitrary syncpoint fences,
* so we cannot normally guarantee that all fences get signalled.
* As such, setup a timeout, so that long-lasting fences will get
* reaped eventually.
*/
schedule_delayed_work(&sf->timeout_work, msecs_to_jiffies(30000));
err = host1x_intr_add_action(sf->sp->host, sf->sp, sf->threshold,
HOST1X_INTR_ACTION_SIGNAL_FENCE, f,
sf->waiter, &sf->waiter_ref);
if (err) {
cancel_delayed_work_sync(&sf->timeout_work);
dma_fence_put(f);
return false;
}
/* intr framework takes ownership of waiter */
sf->waiter = NULL;
/*
* The fence may get signalled at any time after the above call,
* so we need to initialize all state used by signalling
* before it.
*/
return true;
}
static void host1x_syncpt_fence_release(struct dma_fence *f)
{
struct host1x_syncpt_fence *sf = to_host1x_fence(f);
if (sf->waiter)
kfree(sf->waiter);
dma_fence_free(f);
}
const struct dma_fence_ops host1x_syncpt_fence_ops = {
.get_driver_name = host1x_syncpt_fence_get_driver_name,
.get_timeline_name = host1x_syncpt_fence_get_timeline_name,
.enable_signaling = host1x_syncpt_fence_enable_signaling,
.release = host1x_syncpt_fence_release,
};
void host1x_fence_signal(struct host1x_syncpt_fence *f)
{
if (atomic_xchg(&f->signaling, 1))
return;
/*
* Cancel pending timeout work - if it races, it will
* not get 'f->signaling' and return.
*/
cancel_delayed_work_sync(&f->timeout_work);
host1x_intr_put_ref(f->sp->host, f->sp->id, f->waiter_ref, false);
dma_fence_signal(&f->base);
dma_fence_put(&f->base);
}
static void do_fence_timeout(struct work_struct *work)
{
struct delayed_work *dwork = (struct delayed_work *)work;
struct host1x_syncpt_fence *f =
container_of(dwork, struct host1x_syncpt_fence, timeout_work);
if (atomic_xchg(&f->signaling, 1))
return;
/*
* Cancel pending timeout work - if it races, it will
* not get 'f->signaling' and return.
*/
host1x_intr_put_ref(f->sp->host, f->sp->id, f->waiter_ref, true);
dma_fence_set_error(&f->base, -ETIMEDOUT);
dma_fence_signal(&f->base);
dma_fence_put(&f->base);
}
struct dma_fence *host1x_fence_create(struct host1x_syncpt *sp, u32 threshold)
{
struct host1x_syncpt_fence *fence;
fence = kzalloc(sizeof(*fence), GFP_KERNEL);
if (!fence)
return ERR_PTR(-ENOMEM);
fence->waiter = kzalloc(sizeof(*fence->waiter), GFP_KERNEL);
if (!fence->waiter)
return ERR_PTR(-ENOMEM);
fence->sp = sp;
fence->threshold = threshold;
dma_fence_init(&fence->base, &host1x_syncpt_fence_ops, &lock,
dma_fence_context_alloc(1), 0);
INIT_DELAYED_WORK(&fence->timeout_work, do_fence_timeout);
return &fence->base;
}
EXPORT_SYMBOL(host1x_fence_create);
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2020, NVIDIA Corporation.
*/
#ifndef HOST1X_FENCE_H
#define HOST1X_FENCE_H
struct host1x_syncpt_fence;
void host1x_fence_signal(struct host1x_syncpt_fence *fence);
#endif
...@@ -47,39 +47,84 @@ static void trace_write_gather(struct host1x_cdma *cdma, struct host1x_bo *bo, ...@@ -47,39 +47,84 @@ static void trace_write_gather(struct host1x_cdma *cdma, struct host1x_bo *bo,
} }
} }
static void submit_gathers(struct host1x_job *job) static void submit_wait(struct host1x_cdma *cdma, u32 id, u32 threshold,
u32 next_class)
{
#if HOST1X_HW >= 2
host1x_cdma_push_wide(cdma,
host1x_opcode_setclass(
HOST1X_CLASS_HOST1X,
HOST1X_UCLASS_LOAD_SYNCPT_PAYLOAD_32,
/* WAIT_SYNCPT_32 is at SYNCPT_PAYLOAD_32+2 */
BIT(0) | BIT(2)
),
threshold,
id,
host1x_opcode_setclass(next_class, 0, 0)
);
#else
/* TODO add waitchk or use waitbases or other mitigation */
host1x_cdma_push(cdma,
host1x_opcode_setclass(
HOST1X_CLASS_HOST1X,
host1x_uclass_wait_syncpt_r(),
BIT(0)
),
host1x_class_host_wait_syncpt(id, threshold)
);
host1x_cdma_push(cdma,
host1x_opcode_setclass(next_class, 0, 0),
HOST1X_OPCODE_NOP
);
#endif
}
static void submit_gathers(struct host1x_job *job, u32 job_syncpt_base)
{ {
struct host1x_cdma *cdma = &job->channel->cdma; struct host1x_cdma *cdma = &job->channel->cdma;
#if HOST1X_HW < 6 #if HOST1X_HW < 6
struct device *dev = job->channel->dev; struct device *dev = job->channel->dev;
#endif #endif
unsigned int i; unsigned int i;
u32 threshold;
for (i = 0; i < job->num_gathers; i++) { for (i = 0; i < job->num_cmds; i++) {
struct host1x_job_gather *g = &job->gathers[i]; struct host1x_job_cmd *cmd = &job->cmds[i];
dma_addr_t addr = g->base + g->offset;
u32 op2, op3;
op2 = lower_32_bits(addr); if (cmd->is_wait) {
op3 = upper_32_bits(addr); if (cmd->wait.relative)
threshold = job_syncpt_base + cmd->wait.threshold;
else
threshold = cmd->wait.threshold;
trace_write_gather(cdma, g->bo, g->offset, g->words); submit_wait(cdma, cmd->wait.id, threshold, cmd->wait.next_class);
} else {
struct host1x_job_gather *g = &cmd->gather;
dma_addr_t addr = g->base + g->offset;
u32 op2, op3;
op2 = lower_32_bits(addr);
op3 = upper_32_bits(addr);
if (op3 != 0) { trace_write_gather(cdma, g->bo, g->offset, g->words);
if (op3 != 0) {
#if HOST1X_HW >= 6 #if HOST1X_HW >= 6
u32 op1 = host1x_opcode_gather_wide(g->words); u32 op1 = host1x_opcode_gather_wide(g->words);
u32 op4 = HOST1X_OPCODE_NOP; u32 op4 = HOST1X_OPCODE_NOP;
host1x_cdma_push_wide(cdma, op1, op2, op3, op4); host1x_cdma_push_wide(cdma, op1, op2, op3, op4);
#else #else
dev_err(dev, "invalid gather for push buffer %pad\n", dev_err(dev, "invalid gather for push buffer %pad\n",
&addr); &addr);
continue; continue;
#endif #endif
} else { } else {
u32 op1 = host1x_opcode_gather(g->words); u32 op1 = host1x_opcode_gather(g->words);
host1x_cdma_push(cdma, op1, op2); host1x_cdma_push(cdma, op1, op2);
}
} }
} }
} }
...@@ -126,7 +171,7 @@ static int channel_submit(struct host1x_job *job) ...@@ -126,7 +171,7 @@ static int channel_submit(struct host1x_job *job)
struct host1x *host = dev_get_drvdata(ch->dev->parent); struct host1x *host = dev_get_drvdata(ch->dev->parent);
trace_host1x_channel_submit(dev_name(ch->dev), trace_host1x_channel_submit(dev_name(ch->dev),
job->num_gathers, job->num_relocs, job->num_cmds, job->num_relocs,
job->syncpt->id, job->syncpt_incrs); job->syncpt->id, job->syncpt_incrs);
/* before error checks, return current max */ /* before error checks, return current max */
...@@ -181,7 +226,7 @@ static int channel_submit(struct host1x_job *job) ...@@ -181,7 +226,7 @@ static int channel_submit(struct host1x_job *job)
host1x_opcode_setclass(job->class, 0, 0), host1x_opcode_setclass(job->class, 0, 0),
HOST1X_OPCODE_NOP); HOST1X_OPCODE_NOP);
submit_gathers(job); submit_gathers(job, syncval - user_syncpt_incrs);
/* end CDMA submit & stash pinned hMems into sync queue */ /* end CDMA submit & stash pinned hMems into sync queue */
host1x_cdma_end(&ch->cdma, job); host1x_cdma_end(&ch->cdma, job);
...@@ -191,7 +236,7 @@ static int channel_submit(struct host1x_job *job) ...@@ -191,7 +236,7 @@ static int channel_submit(struct host1x_job *job)
/* schedule a submit complete interrupt */ /* schedule a submit complete interrupt */
err = host1x_intr_add_action(host, sp, syncval, err = host1x_intr_add_action(host, sp, syncval,
HOST1X_INTR_ACTION_SUBMIT_COMPLETE, ch, HOST1X_INTR_ACTION_SUBMIT_COMPLETE, ch,
completed_waiter, NULL); completed_waiter, &job->waiter);
completed_waiter = NULL; completed_waiter = NULL;
WARN(err, "Failed to set submit complete interrupt"); WARN(err, "Failed to set submit complete interrupt");
......
...@@ -156,9 +156,9 @@ static unsigned int show_channel_command(struct output *o, u32 val, ...@@ -156,9 +156,9 @@ static unsigned int show_channel_command(struct output *o, u32 val,
} }
} }
static void show_gather(struct output *o, phys_addr_t phys_addr, static void show_gather(struct output *o, dma_addr_t phys_addr,
unsigned int words, struct host1x_cdma *cdma, unsigned int words, struct host1x_cdma *cdma,
phys_addr_t pin_addr, u32 *map_addr) dma_addr_t pin_addr, u32 *map_addr)
{ {
/* Map dmaget cursor to corresponding mem handle */ /* Map dmaget cursor to corresponding mem handle */
u32 offset = phys_addr - pin_addr; u32 offset = phys_addr - pin_addr;
...@@ -176,11 +176,11 @@ static void show_gather(struct output *o, phys_addr_t phys_addr, ...@@ -176,11 +176,11 @@ static void show_gather(struct output *o, phys_addr_t phys_addr,
} }
for (i = 0; i < words; i++) { for (i = 0; i < words; i++) {
u32 addr = phys_addr + i * 4; dma_addr_t addr = phys_addr + i * 4;
u32 val = *(map_addr + offset / 4 + i); u32 val = *(map_addr + offset / 4 + i);
if (!data_count) { if (!data_count) {
host1x_debug_output(o, "%08x: %08x: ", addr, val); host1x_debug_output(o, " %pad: %08x: ", &addr, val);
data_count = show_channel_command(o, val, &payload); data_count = show_channel_command(o, val, &payload);
} else { } else {
host1x_debug_cont(o, "%08x%s", val, host1x_debug_cont(o, "%08x%s", val,
...@@ -195,23 +195,25 @@ static void show_channel_gathers(struct output *o, struct host1x_cdma *cdma) ...@@ -195,23 +195,25 @@ static void show_channel_gathers(struct output *o, struct host1x_cdma *cdma)
struct push_buffer *pb = &cdma->push_buffer; struct push_buffer *pb = &cdma->push_buffer;
struct host1x_job *job; struct host1x_job *job;
host1x_debug_output(o, "PUSHBUF at %pad, %u words\n",
&pb->dma, pb->size / 4);
show_gather(o, pb->dma, pb->size / 4, cdma, pb->dma, pb->mapped);
list_for_each_entry(job, &cdma->sync_queue, list) { list_for_each_entry(job, &cdma->sync_queue, list) {
unsigned int i; unsigned int i;
host1x_debug_output(o, "\n%p: JOB, syncpt_id=%d, syncpt_val=%d, first_get=%08x, timeout=%d num_slots=%d, num_handles=%d\n", host1x_debug_output(o, "JOB, syncpt %u: %u timeout: %u num_slots: %u num_handles: %u\n",
job, job->syncpt->id, job->syncpt_end, job->syncpt->id, job->syncpt_end, job->timeout,
job->first_get, job->timeout,
job->num_slots, job->num_unpins); job->num_slots, job->num_unpins);
for (i = 0; i < job->num_gathers; i++) { show_gather(o, pb->dma + job->first_get, job->num_slots * 2, cdma,
struct host1x_job_gather *g = &job->gathers[i]; pb->dma + job->first_get, pb->mapped + job->first_get);
for (i = 0; i < job->num_cmds; i++) {
struct host1x_job_gather *g;
u32 *mapped; u32 *mapped;
if (job->cmds[i].is_wait)
continue;
g = &job->cmds[i].gather;
if (job->gather_copy_mapped) if (job->gather_copy_mapped)
mapped = (u32 *)job->gather_copy_mapped; mapped = (u32 *)job->gather_copy_mapped;
else else
...@@ -222,7 +224,7 @@ static void show_channel_gathers(struct output *o, struct host1x_cdma *cdma) ...@@ -222,7 +224,7 @@ static void show_channel_gathers(struct output *o, struct host1x_cdma *cdma)
continue; continue;
} }
host1x_debug_output(o, " GATHER at %pad+%#x, %d words\n", host1x_debug_output(o, " GATHER at %pad+%#x, %d words\n",
&g->base, g->offset, g->words); &g->base, g->offset, g->words);
show_gather(o, g->base + g->offset, g->words, cdma, show_gather(o, g->base + g->offset, g->words, cdma,
......
...@@ -16,10 +16,13 @@ static void host1x_debug_show_channel_cdma(struct host1x *host, ...@@ -16,10 +16,13 @@ static void host1x_debug_show_channel_cdma(struct host1x *host,
struct output *o) struct output *o)
{ {
struct host1x_cdma *cdma = &ch->cdma; struct host1x_cdma *cdma = &ch->cdma;
dma_addr_t dmastart, dmaend;
u32 dmaput, dmaget, dmactrl; u32 dmaput, dmaget, dmactrl;
u32 cbstat, cbread; u32 cbstat, cbread;
u32 val, base, baseval; u32 val, base, baseval;
dmastart = host1x_ch_readl(ch, HOST1X_CHANNEL_DMASTART);
dmaend = host1x_ch_readl(ch, HOST1X_CHANNEL_DMAEND);
dmaput = host1x_ch_readl(ch, HOST1X_CHANNEL_DMAPUT); dmaput = host1x_ch_readl(ch, HOST1X_CHANNEL_DMAPUT);
dmaget = host1x_ch_readl(ch, HOST1X_CHANNEL_DMAGET); dmaget = host1x_ch_readl(ch, HOST1X_CHANNEL_DMAGET);
dmactrl = host1x_ch_readl(ch, HOST1X_CHANNEL_DMACTRL); dmactrl = host1x_ch_readl(ch, HOST1X_CHANNEL_DMACTRL);
...@@ -56,9 +59,10 @@ static void host1x_debug_show_channel_cdma(struct host1x *host, ...@@ -56,9 +59,10 @@ static void host1x_debug_show_channel_cdma(struct host1x *host,
HOST1X_SYNC_CBSTAT_CBOFFSET_V(cbstat), HOST1X_SYNC_CBSTAT_CBOFFSET_V(cbstat),
cbread); cbread);
host1x_debug_output(o, "DMAPUT %08x, DMAGET %08x, DMACTL %08x\n", host1x_debug_output(o, "DMASTART %pad, DMAEND %pad\n", &dmastart, &dmaend);
host1x_debug_output(o, "DMAPUT %08x DMAGET %08x DMACTL %08x\n",
dmaput, dmaget, dmactrl); dmaput, dmaget, dmactrl);
host1x_debug_output(o, "CBREAD %08x, CBSTAT %08x\n", cbread, cbstat); host1x_debug_output(o, "CBREAD %08x CBSTAT %08x\n", cbread, cbstat);
show_channel_gathers(o, cdma); show_channel_gathers(o, cdma);
host1x_debug_output(o, "\n"); host1x_debug_output(o, "\n");
......
...@@ -16,10 +16,23 @@ static void host1x_debug_show_channel_cdma(struct host1x *host, ...@@ -16,10 +16,23 @@ static void host1x_debug_show_channel_cdma(struct host1x *host,
struct output *o) struct output *o)
{ {
struct host1x_cdma *cdma = &ch->cdma; struct host1x_cdma *cdma = &ch->cdma;
dma_addr_t dmastart = 0, dmaend = 0;
u32 dmaput, dmaget, dmactrl; u32 dmaput, dmaget, dmactrl;
u32 offset, class; u32 offset, class;
u32 ch_stat; u32 ch_stat;
#if defined(CONFIG_ARCH_DMA_ADDR_T_64BIT) && HOST1X_HW >= 6
dmastart = host1x_ch_readl(ch, HOST1X_CHANNEL_DMASTART_HI);
dmastart <<= 32;
#endif
dmastart |= host1x_ch_readl(ch, HOST1X_CHANNEL_DMASTART);
#if defined(CONFIG_ARCH_DMA_ADDR_T_64BIT) && HOST1X_HW >= 6
dmaend = host1x_ch_readl(ch, HOST1X_CHANNEL_DMAEND_HI);
dmaend <<= 32;
#endif
dmaend |= host1x_ch_readl(ch, HOST1X_CHANNEL_DMAEND);
dmaput = host1x_ch_readl(ch, HOST1X_CHANNEL_DMAPUT); dmaput = host1x_ch_readl(ch, HOST1X_CHANNEL_DMAPUT);
dmaget = host1x_ch_readl(ch, HOST1X_CHANNEL_DMAGET); dmaget = host1x_ch_readl(ch, HOST1X_CHANNEL_DMAGET);
dmactrl = host1x_ch_readl(ch, HOST1X_CHANNEL_DMACTRL); dmactrl = host1x_ch_readl(ch, HOST1X_CHANNEL_DMACTRL);
...@@ -41,7 +54,8 @@ static void host1x_debug_show_channel_cdma(struct host1x *host, ...@@ -41,7 +54,8 @@ static void host1x_debug_show_channel_cdma(struct host1x *host,
host1x_debug_output(o, "active class %02x, offset %04x\n", host1x_debug_output(o, "active class %02x, offset %04x\n",
class, offset); class, offset);
host1x_debug_output(o, "DMAPUT %08x, DMAGET %08x, DMACTL %08x\n", host1x_debug_output(o, "DMASTART %pad, DMAEND %pad\n", &dmastart, &dmaend);
host1x_debug_output(o, "DMAPUT %08x DMAGET %08x DMACTL %08x\n",
dmaput, dmaget, dmactrl); dmaput, dmaget, dmactrl);
host1x_debug_output(o, "CHANNELSTAT %02x\n", ch_stat); host1x_debug_output(o, "CHANNELSTAT %02x\n", ch_stat);
......
...@@ -165,5 +165,17 @@ static inline u32 host1x_uclass_indoff_rwn_read_v(void) ...@@ -165,5 +165,17 @@ static inline u32 host1x_uclass_indoff_rwn_read_v(void)
} }
#define HOST1X_UCLASS_INDOFF_INDROFFSET_F(v) \ #define HOST1X_UCLASS_INDOFF_INDROFFSET_F(v) \
host1x_uclass_indoff_indroffset_f(v) host1x_uclass_indoff_indroffset_f(v)
static inline u32 host1x_uclass_load_syncpt_payload_32_r(void)
{
return 0x4e;
}
#define HOST1X_UCLASS_LOAD_SYNCPT_PAYLOAD_32 \
host1x_uclass_load_syncpt_payload_32_r()
static inline u32 host1x_uclass_wait_syncpt_32_r(void)
{
return 0x50;
}
#define HOST1X_UCLASS_WAIT_SYNCPT_32 \
host1x_uclass_wait_syncpt_32_r()
#endif #endif
...@@ -165,5 +165,17 @@ static inline u32 host1x_uclass_indoff_rwn_read_v(void) ...@@ -165,5 +165,17 @@ static inline u32 host1x_uclass_indoff_rwn_read_v(void)
} }
#define HOST1X_UCLASS_INDOFF_INDROFFSET_F(v) \ #define HOST1X_UCLASS_INDOFF_INDROFFSET_F(v) \
host1x_uclass_indoff_indroffset_f(v) host1x_uclass_indoff_indroffset_f(v)
static inline u32 host1x_uclass_load_syncpt_payload_32_r(void)
{
return 0x4e;
}
#define HOST1X_UCLASS_LOAD_SYNCPT_PAYLOAD_32 \
host1x_uclass_load_syncpt_payload_32_r()
static inline u32 host1x_uclass_wait_syncpt_32_r(void)
{
return 0x50;
}
#define HOST1X_UCLASS_WAIT_SYNCPT_32 \
host1x_uclass_wait_syncpt_32_r()
#endif #endif
...@@ -165,5 +165,17 @@ static inline u32 host1x_uclass_indoff_rwn_read_v(void) ...@@ -165,5 +165,17 @@ static inline u32 host1x_uclass_indoff_rwn_read_v(void)
} }
#define HOST1X_UCLASS_INDOFF_INDROFFSET_F(v) \ #define HOST1X_UCLASS_INDOFF_INDROFFSET_F(v) \
host1x_uclass_indoff_indroffset_f(v) host1x_uclass_indoff_indroffset_f(v)
static inline u32 host1x_uclass_load_syncpt_payload_32_r(void)
{
return 0x4e;
}
#define HOST1X_UCLASS_LOAD_SYNCPT_PAYLOAD_32 \
host1x_uclass_load_syncpt_payload_32_r()
static inline u32 host1x_uclass_wait_syncpt_32_r(void)
{
return 0x50;
}
#define HOST1X_UCLASS_WAIT_SYNCPT_32 \
host1x_uclass_wait_syncpt_32_r()
#endif #endif
...@@ -165,5 +165,17 @@ static inline u32 host1x_uclass_indoff_rwn_read_v(void) ...@@ -165,5 +165,17 @@ static inline u32 host1x_uclass_indoff_rwn_read_v(void)
} }
#define HOST1X_UCLASS_INDOFF_INDROFFSET_F(v) \ #define HOST1X_UCLASS_INDOFF_INDROFFSET_F(v) \
host1x_uclass_indoff_indroffset_f(v) host1x_uclass_indoff_indroffset_f(v)
static inline u32 host1x_uclass_load_syncpt_payload_32_r(void)
{
return 0x4e;
}
#define HOST1X_UCLASS_LOAD_SYNCPT_PAYLOAD_32 \
host1x_uclass_load_syncpt_payload_32_r()
static inline u32 host1x_uclass_wait_syncpt_32_r(void)
{
return 0x50;
}
#define HOST1X_UCLASS_WAIT_SYNCPT_32 \
host1x_uclass_wait_syncpt_32_r()
#endif #endif
...@@ -165,5 +165,17 @@ static inline u32 host1x_uclass_indoff_rwn_read_v(void) ...@@ -165,5 +165,17 @@ static inline u32 host1x_uclass_indoff_rwn_read_v(void)
} }
#define HOST1X_UCLASS_INDOFF_INDROFFSET_F(v) \ #define HOST1X_UCLASS_INDOFF_INDROFFSET_F(v) \
host1x_uclass_indoff_indroffset_f(v) host1x_uclass_indoff_indroffset_f(v)
static inline u32 host1x_uclass_load_syncpt_payload_32_r(void)
{
return 0x4e;
}
#define HOST1X_UCLASS_LOAD_SYNCPT_PAYLOAD_32 \
host1x_uclass_load_syncpt_payload_32_r()
static inline u32 host1x_uclass_wait_syncpt_32_r(void)
{
return 0x50;
}
#define HOST1X_UCLASS_WAIT_SYNCPT_32 \
host1x_uclass_wait_syncpt_32_r()
#endif #endif
...@@ -13,6 +13,7 @@ ...@@ -13,6 +13,7 @@
#include <trace/events/host1x.h> #include <trace/events/host1x.h>
#include "channel.h" #include "channel.h"
#include "dev.h" #include "dev.h"
#include "fence.h"
#include "intr.h" #include "intr.h"
/* Wait list management */ /* Wait list management */
...@@ -121,12 +122,20 @@ static void action_wakeup_interruptible(struct host1x_waitlist *waiter) ...@@ -121,12 +122,20 @@ static void action_wakeup_interruptible(struct host1x_waitlist *waiter)
wake_up_interruptible(wq); wake_up_interruptible(wq);
} }
static void action_signal_fence(struct host1x_waitlist *waiter)
{
struct host1x_syncpt_fence *f = waiter->data;
host1x_fence_signal(f);
}
typedef void (*action_handler)(struct host1x_waitlist *waiter); typedef void (*action_handler)(struct host1x_waitlist *waiter);
static const action_handler action_handlers[HOST1X_INTR_ACTION_COUNT] = { static const action_handler action_handlers[HOST1X_INTR_ACTION_COUNT] = {
action_submit_complete, action_submit_complete,
action_wakeup, action_wakeup,
action_wakeup_interruptible, action_wakeup_interruptible,
action_signal_fence,
}; };
static void run_handlers(struct list_head completed[HOST1X_INTR_ACTION_COUNT]) static void run_handlers(struct list_head completed[HOST1X_INTR_ACTION_COUNT])
......
...@@ -33,6 +33,8 @@ enum host1x_intr_action { ...@@ -33,6 +33,8 @@ enum host1x_intr_action {
*/ */
HOST1X_INTR_ACTION_WAKEUP_INTERRUPTIBLE, HOST1X_INTR_ACTION_WAKEUP_INTERRUPTIBLE,
HOST1X_INTR_ACTION_SIGNAL_FENCE,
HOST1X_INTR_ACTION_COUNT HOST1X_INTR_ACTION_COUNT
}; };
......
...@@ -24,21 +24,25 @@ ...@@ -24,21 +24,25 @@
#define HOST1X_WAIT_SYNCPT_OFFSET 0x8 #define HOST1X_WAIT_SYNCPT_OFFSET 0x8
struct host1x_job *host1x_job_alloc(struct host1x_channel *ch, struct host1x_job *host1x_job_alloc(struct host1x_channel *ch,
u32 num_cmdbufs, u32 num_relocs) u32 num_cmdbufs, u32 num_relocs,
bool skip_firewall)
{ {
struct host1x_job *job = NULL; struct host1x_job *job = NULL;
unsigned int num_unpins = num_relocs; unsigned int num_unpins = num_relocs;
bool enable_firewall;
u64 total; u64 total;
void *mem; void *mem;
if (!IS_ENABLED(CONFIG_TEGRA_HOST1X_FIREWALL)) enable_firewall = IS_ENABLED(CONFIG_TEGRA_HOST1X_FIREWALL) && !skip_firewall;
if (!enable_firewall)
num_unpins += num_cmdbufs; num_unpins += num_cmdbufs;
/* Check that we're not going to overflow */ /* Check that we're not going to overflow */
total = sizeof(struct host1x_job) + total = sizeof(struct host1x_job) +
(u64)num_relocs * sizeof(struct host1x_reloc) + (u64)num_relocs * sizeof(struct host1x_reloc) +
(u64)num_unpins * sizeof(struct host1x_job_unpin_data) + (u64)num_unpins * sizeof(struct host1x_job_unpin_data) +
(u64)num_cmdbufs * sizeof(struct host1x_job_gather) + (u64)num_cmdbufs * sizeof(struct host1x_job_cmd) +
(u64)num_unpins * sizeof(dma_addr_t) + (u64)num_unpins * sizeof(dma_addr_t) +
(u64)num_unpins * sizeof(u32 *); (u64)num_unpins * sizeof(u32 *);
if (total > ULONG_MAX) if (total > ULONG_MAX)
...@@ -48,6 +52,8 @@ struct host1x_job *host1x_job_alloc(struct host1x_channel *ch, ...@@ -48,6 +52,8 @@ struct host1x_job *host1x_job_alloc(struct host1x_channel *ch,
if (!job) if (!job)
return NULL; return NULL;
job->enable_firewall = enable_firewall;
kref_init(&job->ref); kref_init(&job->ref);
job->channel = ch; job->channel = ch;
...@@ -57,8 +63,8 @@ struct host1x_job *host1x_job_alloc(struct host1x_channel *ch, ...@@ -57,8 +63,8 @@ struct host1x_job *host1x_job_alloc(struct host1x_channel *ch,
mem += num_relocs * sizeof(struct host1x_reloc); mem += num_relocs * sizeof(struct host1x_reloc);
job->unpins = num_unpins ? mem : NULL; job->unpins = num_unpins ? mem : NULL;
mem += num_unpins * sizeof(struct host1x_job_unpin_data); mem += num_unpins * sizeof(struct host1x_job_unpin_data);
job->gathers = num_cmdbufs ? mem : NULL; job->cmds = num_cmdbufs ? mem : NULL;
mem += num_cmdbufs * sizeof(struct host1x_job_gather); mem += num_cmdbufs * sizeof(struct host1x_job_cmd);
job->addr_phys = num_unpins ? mem : NULL; job->addr_phys = num_unpins ? mem : NULL;
job->reloc_addr_phys = job->addr_phys; job->reloc_addr_phys = job->addr_phys;
...@@ -79,6 +85,13 @@ static void job_free(struct kref *ref) ...@@ -79,6 +85,13 @@ static void job_free(struct kref *ref)
{ {
struct host1x_job *job = container_of(ref, struct host1x_job, ref); struct host1x_job *job = container_of(ref, struct host1x_job, ref);
if (job->release)
job->release(job);
if (job->waiter)
host1x_intr_put_ref(job->syncpt->host, job->syncpt->id,
job->waiter, false);
if (job->syncpt) if (job->syncpt)
host1x_syncpt_put(job->syncpt); host1x_syncpt_put(job->syncpt);
...@@ -94,22 +107,38 @@ EXPORT_SYMBOL(host1x_job_put); ...@@ -94,22 +107,38 @@ EXPORT_SYMBOL(host1x_job_put);
void host1x_job_add_gather(struct host1x_job *job, struct host1x_bo *bo, void host1x_job_add_gather(struct host1x_job *job, struct host1x_bo *bo,
unsigned int words, unsigned int offset) unsigned int words, unsigned int offset)
{ {
struct host1x_job_gather *gather = &job->gathers[job->num_gathers]; struct host1x_job_gather *gather = &job->cmds[job->num_cmds].gather;
gather->words = words; gather->words = words;
gather->bo = bo; gather->bo = bo;
gather->offset = offset; gather->offset = offset;
job->num_gathers++; job->num_cmds++;
} }
EXPORT_SYMBOL(host1x_job_add_gather); EXPORT_SYMBOL(host1x_job_add_gather);
void host1x_job_add_wait(struct host1x_job *job, u32 id, u32 thresh,
bool relative, u32 next_class)
{
struct host1x_job_cmd *cmd = &job->cmds[job->num_cmds];
cmd->is_wait = true;
cmd->wait.id = id;
cmd->wait.threshold = thresh;
cmd->wait.next_class = next_class;
cmd->wait.relative = relative;
job->num_cmds++;
}
EXPORT_SYMBOL(host1x_job_add_wait);
static unsigned int pin_job(struct host1x *host, struct host1x_job *job) static unsigned int pin_job(struct host1x *host, struct host1x_job *job)
{ {
struct host1x_client *client = job->client; struct host1x_client *client = job->client;
struct device *dev = client->dev; struct device *dev = client->dev;
struct host1x_job_gather *g; struct host1x_job_gather *g;
struct iommu_domain *domain; struct iommu_domain *domain;
struct sg_table *sgt;
unsigned int i; unsigned int i;
int err; int err;
...@@ -119,7 +148,6 @@ static unsigned int pin_job(struct host1x *host, struct host1x_job *job) ...@@ -119,7 +148,6 @@ static unsigned int pin_job(struct host1x *host, struct host1x_job *job)
for (i = 0; i < job->num_relocs; i++) { for (i = 0; i < job->num_relocs; i++) {
struct host1x_reloc *reloc = &job->relocs[i]; struct host1x_reloc *reloc = &job->relocs[i];
dma_addr_t phys_addr, *phys; dma_addr_t phys_addr, *phys;
struct sg_table *sgt;
reloc->target.bo = host1x_bo_get(reloc->target.bo); reloc->target.bo = host1x_bo_get(reloc->target.bo);
if (!reloc->target.bo) { if (!reloc->target.bo) {
...@@ -192,20 +220,23 @@ static unsigned int pin_job(struct host1x *host, struct host1x_job *job) ...@@ -192,20 +220,23 @@ static unsigned int pin_job(struct host1x *host, struct host1x_job *job)
* We will copy gathers BO content later, so there is no need to * We will copy gathers BO content later, so there is no need to
* hold and pin them. * hold and pin them.
*/ */
if (IS_ENABLED(CONFIG_TEGRA_HOST1X_FIREWALL)) if (job->enable_firewall)
return 0; return 0;
for (i = 0; i < job->num_gathers; i++) { for (i = 0; i < job->num_cmds; i++) {
size_t gather_size = 0; size_t gather_size = 0;
struct scatterlist *sg; struct scatterlist *sg;
struct sg_table *sgt;
dma_addr_t phys_addr; dma_addr_t phys_addr;
unsigned long shift; unsigned long shift;
struct iova *alloc; struct iova *alloc;
dma_addr_t *phys; dma_addr_t *phys;
unsigned int j; unsigned int j;
g = &job->gathers[i]; if (job->cmds[i].is_wait)
continue;
g = &job->cmds[i].gather;
g->bo = host1x_bo_get(g->bo); g->bo = host1x_bo_get(g->bo);
if (!g->bo) { if (!g->bo) {
err = -EINVAL; err = -EINVAL;
...@@ -296,7 +327,7 @@ static int do_relocs(struct host1x_job *job, struct host1x_job_gather *g) ...@@ -296,7 +327,7 @@ static int do_relocs(struct host1x_job *job, struct host1x_job_gather *g)
if (cmdbuf != reloc->cmdbuf.bo) if (cmdbuf != reloc->cmdbuf.bo)
continue; continue;
if (IS_ENABLED(CONFIG_TEGRA_HOST1X_FIREWALL)) { if (job->enable_firewall) {
target = (u32 *)job->gather_copy_mapped + target = (u32 *)job->gather_copy_mapped +
reloc->cmdbuf.offset / sizeof(u32) + reloc->cmdbuf.offset / sizeof(u32) +
g->offset / sizeof(u32); g->offset / sizeof(u32);
...@@ -538,8 +569,13 @@ static inline int copy_gathers(struct device *host, struct host1x_job *job, ...@@ -538,8 +569,13 @@ static inline int copy_gathers(struct device *host, struct host1x_job *job,
fw.num_relocs = job->num_relocs; fw.num_relocs = job->num_relocs;
fw.class = job->class; fw.class = job->class;
for (i = 0; i < job->num_gathers; i++) { for (i = 0; i < job->num_cmds; i++) {
struct host1x_job_gather *g = &job->gathers[i]; struct host1x_job_gather *g;
if (job->cmds[i].is_wait)
continue;
g = &job->cmds[i].gather;
size += g->words * sizeof(u32); size += g->words * sizeof(u32);
} }
...@@ -561,10 +597,14 @@ static inline int copy_gathers(struct device *host, struct host1x_job *job, ...@@ -561,10 +597,14 @@ static inline int copy_gathers(struct device *host, struct host1x_job *job,
job->gather_copy_size = size; job->gather_copy_size = size;
for (i = 0; i < job->num_gathers; i++) { for (i = 0; i < job->num_cmds; i++) {
struct host1x_job_gather *g = &job->gathers[i]; struct host1x_job_gather *g;
void *gather; void *gather;
if (job->cmds[i].is_wait)
continue;
g = &job->cmds[i].gather;
/* Copy the gather */ /* Copy the gather */
gather = host1x_bo_mmap(g->bo); gather = host1x_bo_mmap(g->bo);
memcpy(job->gather_copy_mapped + offset, gather + g->offset, memcpy(job->gather_copy_mapped + offset, gather + g->offset,
...@@ -600,28 +640,33 @@ int host1x_job_pin(struct host1x_job *job, struct device *dev) ...@@ -600,28 +640,33 @@ int host1x_job_pin(struct host1x_job *job, struct device *dev)
if (err) if (err)
goto out; goto out;
if (IS_ENABLED(CONFIG_TEGRA_HOST1X_FIREWALL)) { if (job->enable_firewall) {
err = copy_gathers(host->dev, job, dev); err = copy_gathers(host->dev, job, dev);
if (err) if (err)
goto out; goto out;
} }
/* patch gathers */ /* patch gathers */
for (i = 0; i < job->num_gathers; i++) { for (i = 0; i < job->num_cmds; i++) {
struct host1x_job_gather *g = &job->gathers[i]; struct host1x_job_gather *g;
if (job->cmds[i].is_wait)
continue;
g = &job->cmds[i].gather;
/* process each gather mem only once */ /* process each gather mem only once */
if (g->handled) if (g->handled)
continue; continue;
/* copy_gathers() sets gathers base if firewall is enabled */ /* copy_gathers() sets gathers base if firewall is enabled */
if (!IS_ENABLED(CONFIG_TEGRA_HOST1X_FIREWALL)) if (!job->enable_firewall)
g->base = job->gather_addr_phys[i]; g->base = job->gather_addr_phys[i];
for (j = i + 1; j < job->num_gathers; j++) { for (j = i + 1; j < job->num_cmds; j++) {
if (job->gathers[j].bo == g->bo) { if (!job->cmds[j].is_wait &&
job->gathers[j].handled = true; job->cmds[j].gather.bo == g->bo) {
job->gathers[j].base = g->base; job->cmds[j].gather.handled = true;
job->cmds[j].gather.base = g->base;
} }
} }
...@@ -649,8 +694,7 @@ void host1x_job_unpin(struct host1x_job *job) ...@@ -649,8 +694,7 @@ void host1x_job_unpin(struct host1x_job *job)
struct device *dev = unpin->dev ?: host->dev; struct device *dev = unpin->dev ?: host->dev;
struct sg_table *sgt = unpin->sgt; struct sg_table *sgt = unpin->sgt;
if (!IS_ENABLED(CONFIG_TEGRA_HOST1X_FIREWALL) && if (!job->enable_firewall && unpin->size && host->domain) {
unpin->size && host->domain) {
iommu_unmap(host->domain, job->addr_phys[i], iommu_unmap(host->domain, job->addr_phys[i],
unpin->size); unpin->size);
free_iova(&host->iova, free_iova(&host->iova,
......
...@@ -18,6 +18,22 @@ struct host1x_job_gather { ...@@ -18,6 +18,22 @@ struct host1x_job_gather {
bool handled; bool handled;
}; };
struct host1x_job_wait {
u32 id;
u32 threshold;
u32 next_class;
bool relative;
};
struct host1x_job_cmd {
bool is_wait;
union {
struct host1x_job_gather gather;
struct host1x_job_wait wait;
};
};
struct host1x_job_unpin_data { struct host1x_job_unpin_data {
struct host1x_bo *bo; struct host1x_bo *bo;
struct sg_table *sgt; struct sg_table *sgt;
......
...@@ -407,6 +407,8 @@ static void syncpt_release(struct kref *ref) ...@@ -407,6 +407,8 @@ static void syncpt_release(struct kref *ref)
atomic_set(&sp->max_val, host1x_syncpt_read(sp)); atomic_set(&sp->max_val, host1x_syncpt_read(sp));
sp->locked = false;
mutex_lock(&sp->host->syncpt_mutex); mutex_lock(&sp->host->syncpt_mutex);
host1x_syncpt_base_free(sp->base); host1x_syncpt_base_free(sp->base);
......
...@@ -40,6 +40,13 @@ struct host1x_syncpt { ...@@ -40,6 +40,13 @@ struct host1x_syncpt {
/* interrupt data */ /* interrupt data */
struct host1x_syncpt_intr intr; struct host1x_syncpt_intr intr;
/*
* If a submission incrementing this syncpoint fails, lock it so that
* further submission cannot be made until application has handled the
* failure.
*/
bool locked;
}; };
/* Initialize sync point array */ /* Initialize sync point array */
...@@ -115,4 +122,9 @@ static inline int host1x_syncpt_is_valid(struct host1x_syncpt *sp) ...@@ -115,4 +122,9 @@ static inline int host1x_syncpt_is_valid(struct host1x_syncpt *sp)
return sp->id < host1x_syncpt_nb_pts(sp->host); return sp->id < host1x_syncpt_nb_pts(sp->host);
} }
static inline void host1x_syncpt_set_locked(struct host1x_syncpt *sp)
{
sp->locked = true;
}
#endif #endif
...@@ -170,6 +170,8 @@ u32 host1x_syncpt_base_id(struct host1x_syncpt_base *base); ...@@ -170,6 +170,8 @@ u32 host1x_syncpt_base_id(struct host1x_syncpt_base *base);
void host1x_syncpt_release_vblank_reservation(struct host1x_client *client, void host1x_syncpt_release_vblank_reservation(struct host1x_client *client,
u32 syncpt_id); u32 syncpt_id);
struct dma_fence *host1x_fence_create(struct host1x_syncpt *sp, u32 threshold);
/* /*
* host1x channel * host1x channel
*/ */
...@@ -216,8 +218,8 @@ struct host1x_job { ...@@ -216,8 +218,8 @@ struct host1x_job {
struct host1x_client *client; struct host1x_client *client;
/* Gathers and their memory */ /* Gathers and their memory */
struct host1x_job_gather *gathers; struct host1x_job_cmd *cmds;
unsigned int num_gathers; unsigned int num_cmds;
/* Array of handles to be pinned & unpinned */ /* Array of handles to be pinned & unpinned */
struct host1x_reloc *relocs; struct host1x_reloc *relocs;
...@@ -234,9 +236,15 @@ struct host1x_job { ...@@ -234,9 +236,15 @@ struct host1x_job {
u32 syncpt_incrs; u32 syncpt_incrs;
u32 syncpt_end; u32 syncpt_end;
/* Completion waiter ref */
void *waiter;
/* Maximum time to wait for this job */ /* Maximum time to wait for this job */
unsigned int timeout; unsigned int timeout;
/* Job has timed out and should be released */
bool cancelled;
/* Index and number of slots used in the push buffer */ /* Index and number of slots used in the push buffer */
unsigned int first_get; unsigned int first_get;
unsigned int num_slots; unsigned int num_slots;
...@@ -257,12 +265,25 @@ struct host1x_job { ...@@ -257,12 +265,25 @@ struct host1x_job {
/* Add a channel wait for previous ops to complete */ /* Add a channel wait for previous ops to complete */
bool serialize; bool serialize;
/* Fast-forward syncpoint increments on job timeout */
bool syncpt_recovery;
/* Callback called when job is freed */
void (*release)(struct host1x_job *job);
void *user_data;
/* Whether host1x-side firewall should be ran for this job or not */
bool enable_firewall;
}; };
struct host1x_job *host1x_job_alloc(struct host1x_channel *ch, struct host1x_job *host1x_job_alloc(struct host1x_channel *ch,
u32 num_cmdbufs, u32 num_relocs); u32 num_cmdbufs, u32 num_relocs,
bool skip_firewall);
void host1x_job_add_gather(struct host1x_job *job, struct host1x_bo *bo, void host1x_job_add_gather(struct host1x_job *job, struct host1x_bo *bo,
unsigned int words, unsigned int offset); unsigned int words, unsigned int offset);
void host1x_job_add_wait(struct host1x_job *job, u32 id, u32 thresh,
bool relative, u32 next_class);
struct host1x_job *host1x_job_get(struct host1x_job *job); struct host1x_job *host1x_job_get(struct host1x_job *job);
void host1x_job_put(struct host1x_job *job); void host1x_job_put(struct host1x_job *job);
int host1x_job_pin(struct host1x_job *job, struct device *dev); int host1x_job_pin(struct host1x_job *job, struct device *dev);
......
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment