Commit dae437d5 authored by Dave Airlie's avatar Dave Airlie

Merge tag 'drm/tegra/for-6.3-rc1' of https://gitlab.freedesktop.org/drm/tegra into drm-next

drm/tegra: Changes for v6.3-rc1

This set of changes includes a rework of the custom syncpoint interrupt
code to take better advantage of existing DRM/KMS infrastructure.

There's also various bits of cleanup and fixes included.
Signed-off-by: default avatarDave Airlie <airlied@redhat.com>

From: Thierry Reding <thierry.reding@gmail.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20230127170119.495943-1-thierry.reding@gmail.com
parents 54587d99 2abdd44e
...@@ -7040,7 +7040,7 @@ M: Thierry Reding <thierry.reding@gmail.com> ...@@ -7040,7 +7040,7 @@ M: Thierry Reding <thierry.reding@gmail.com>
L: dri-devel@lists.freedesktop.org L: dri-devel@lists.freedesktop.org
L: linux-tegra@vger.kernel.org L: linux-tegra@vger.kernel.org
S: Supported S: Supported
T: git git://anongit.freedesktop.org/tegra/linux.git T: git https://gitlab.freedesktop.org/drm/tegra.git
F: Documentation/devicetree/bindings/display/tegra/nvidia,tegra20-host1x.yaml F: Documentation/devicetree/bindings/display/tegra/nvidia,tegra20-host1x.yaml
F: Documentation/devicetree/bindings/gpu/host1x/ F: Documentation/devicetree/bindings/gpu/host1x/
F: drivers/gpu/drm/tegra/ F: drivers/gpu/drm/tegra/
......
...@@ -598,7 +598,6 @@ static int tegra_dpaux_remove(struct platform_device *pdev) ...@@ -598,7 +598,6 @@ static int tegra_dpaux_remove(struct platform_device *pdev)
return 0; return 0;
} }
#ifdef CONFIG_PM
static int tegra_dpaux_suspend(struct device *dev) static int tegra_dpaux_suspend(struct device *dev)
{ {
struct tegra_dpaux *dpaux = dev_get_drvdata(dev); struct tegra_dpaux *dpaux = dev_get_drvdata(dev);
...@@ -657,10 +656,9 @@ static int tegra_dpaux_resume(struct device *dev) ...@@ -657,10 +656,9 @@ static int tegra_dpaux_resume(struct device *dev)
clk_disable_unprepare(dpaux->clk); clk_disable_unprepare(dpaux->clk);
return err; return err;
} }
#endif
static const struct dev_pm_ops tegra_dpaux_pm_ops = { static const struct dev_pm_ops tegra_dpaux_pm_ops = {
SET_RUNTIME_PM_OPS(tegra_dpaux_suspend, tegra_dpaux_resume, NULL) RUNTIME_PM_OPS(tegra_dpaux_suspend, tegra_dpaux_resume, NULL)
}; };
static const struct tegra_dpaux_soc tegra124_dpaux_soc = { static const struct tegra_dpaux_soc tegra124_dpaux_soc = {
...@@ -694,7 +692,7 @@ struct platform_driver tegra_dpaux_driver = { ...@@ -694,7 +692,7 @@ struct platform_driver tegra_dpaux_driver = {
.driver = { .driver = {
.name = "tegra-dpaux", .name = "tegra-dpaux",
.of_match_table = tegra_dpaux_of_match, .of_match_table = tegra_dpaux_of_match,
.pm = &tegra_dpaux_pm_ops, .pm = pm_ptr(&tegra_dpaux_pm_ops),
}, },
.probe = tegra_dpaux_probe, .probe = tegra_dpaux_probe,
.remove = tegra_dpaux_remove, .remove = tegra_dpaux_remove,
......
...@@ -97,6 +97,9 @@ static int fw_check_regs_imm(struct tegra_drm_firewall *fw, u32 offset) ...@@ -97,6 +97,9 @@ static int fw_check_regs_imm(struct tegra_drm_firewall *fw, u32 offset)
{ {
bool is_addr; bool is_addr;
if (!fw->client->ops->is_addr_reg)
return 0;
is_addr = fw->client->ops->is_addr_reg(fw->client->base.dev, fw->class, is_addr = fw->client->ops->is_addr_reg(fw->client->base.dev, fw->class,
offset); offset);
if (is_addr) if (is_addr)
......
...@@ -67,26 +67,18 @@ static inline void nvdec_writel(struct nvdec *nvdec, u32 value, ...@@ -67,26 +67,18 @@ static inline void nvdec_writel(struct nvdec *nvdec, u32 value,
static int nvdec_boot_falcon(struct nvdec *nvdec) static int nvdec_boot_falcon(struct nvdec *nvdec)
{ {
#ifdef CONFIG_IOMMU_API u32 stream_id;
struct iommu_fwspec *spec = dev_iommu_fwspec_get(nvdec->dev);
#endif
int err; int err;
#ifdef CONFIG_IOMMU_API if (nvdec->config->supports_sid && tegra_dev_iommu_get_stream_id(nvdec->dev, &stream_id)) {
if (nvdec->config->supports_sid && spec) {
u32 value; u32 value;
value = TRANSCFG_ATT(1, TRANSCFG_SID_FALCON) | TRANSCFG_ATT(0, TRANSCFG_SID_HW); value = TRANSCFG_ATT(1, TRANSCFG_SID_FALCON) | TRANSCFG_ATT(0, TRANSCFG_SID_HW);
nvdec_writel(nvdec, value, NVDEC_TFBIF_TRANSCFG); nvdec_writel(nvdec, value, NVDEC_TFBIF_TRANSCFG);
if (spec->num_ids > 0) { nvdec_writel(nvdec, stream_id, VIC_THI_STREAMID0);
value = spec->ids[0] & 0xffff; nvdec_writel(nvdec, stream_id, VIC_THI_STREAMID1);
nvdec_writel(nvdec, value, VIC_THI_STREAMID0);
nvdec_writel(nvdec, value, VIC_THI_STREAMID1);
}
} }
#endif
err = falcon_boot(&nvdec->falcon); err = falcon_boot(&nvdec->falcon);
if (err < 0) if (err < 0)
......
...@@ -609,21 +609,13 @@ int tegra_drm_ioctl_channel_submit(struct drm_device *drm, void *data, ...@@ -609,21 +609,13 @@ int tegra_drm_ioctl_channel_submit(struct drm_device *drm, void *data,
host1x_memory_context_get(job->memory_context); host1x_memory_context_get(job->memory_context);
} }
} else if (context->client->ops->get_streamid_offset) { } else if (context->client->ops->get_streamid_offset) {
#ifdef CONFIG_IOMMU_API
struct iommu_fwspec *spec;
/* /*
* Job submission will need to temporarily change stream ID, * Job submission will need to temporarily change stream ID,
* so need to tell it what to change it back to. * so need to tell it what to change it back to.
*/ */
spec = dev_iommu_fwspec_get(context->client->base.dev); if (!tegra_dev_iommu_get_stream_id(context->client->base.dev,
if (spec && spec->num_ids > 0) &job->engine_fallback_streamid))
job->engine_fallback_streamid = spec->ids[0] & 0xffff; job->engine_fallback_streamid = TEGRA_STREAM_ID_BYPASS;
else
job->engine_fallback_streamid = 0x7f;
#else
job->engine_fallback_streamid = 0x7f;
#endif
} }
/* Boot engine. */ /* Boot engine. */
...@@ -654,7 +646,7 @@ int tegra_drm_ioctl_channel_submit(struct drm_device *drm, void *data, ...@@ -654,7 +646,7 @@ int tegra_drm_ioctl_channel_submit(struct drm_device *drm, void *data,
args->syncpt.value = job->syncpt_end; args->syncpt.value = job->syncpt_end;
if (syncobj) { if (syncobj) {
struct dma_fence *fence = host1x_fence_create(job->syncpt, job->syncpt_end); struct dma_fence *fence = host1x_fence_create(job->syncpt, job->syncpt_end, true);
if (IS_ERR(fence)) { if (IS_ERR(fence)) {
err = PTR_ERR(fence); err = PTR_ERR(fence);
SUBMIT_ERR(context, "failed to create postfence: %d", err); SUBMIT_ERR(context, "failed to create postfence: %d", err);
...@@ -680,8 +672,7 @@ int tegra_drm_ioctl_channel_submit(struct drm_device *drm, void *data, ...@@ -680,8 +672,7 @@ int tegra_drm_ioctl_channel_submit(struct drm_device *drm, void *data,
kfree(job_data->used_mappings); kfree(job_data->used_mappings);
} }
if (job_data) kfree(job_data);
kfree(job_data);
put_bo: put_bo:
gather_bo_put(&bo->base); gather_bo_put(&bo->base);
unlock: unlock:
......
...@@ -56,41 +56,30 @@ static void vic_writel(struct vic *vic, u32 value, unsigned int offset) ...@@ -56,41 +56,30 @@ static void vic_writel(struct vic *vic, u32 value, unsigned int offset)
static int vic_boot(struct vic *vic) static int vic_boot(struct vic *vic)
{ {
#ifdef CONFIG_IOMMU_API u32 fce_ucode_size, fce_bin_data_offset, stream_id;
struct iommu_fwspec *spec = dev_iommu_fwspec_get(vic->dev);
#endif
u32 fce_ucode_size, fce_bin_data_offset;
void *hdr; void *hdr;
int err = 0; int err = 0;
#ifdef CONFIG_IOMMU_API if (vic->config->supports_sid && tegra_dev_iommu_get_stream_id(vic->dev, &stream_id)) {
if (vic->config->supports_sid && spec) {
u32 value; u32 value;
value = TRANSCFG_ATT(1, TRANSCFG_SID_FALCON) | value = TRANSCFG_ATT(1, TRANSCFG_SID_FALCON) |
TRANSCFG_ATT(0, TRANSCFG_SID_HW); TRANSCFG_ATT(0, TRANSCFG_SID_HW);
vic_writel(vic, value, VIC_TFBIF_TRANSCFG); vic_writel(vic, value, VIC_TFBIF_TRANSCFG);
if (spec->num_ids > 0) { /*
value = spec->ids[0] & 0xffff; * STREAMID0 is used for input/output buffers. Initialize it to SID_VIC in case
* context isolation is not enabled, and SID_VIC is used for both firmware and
/* * data buffers.
* STREAMID0 is used for input/output buffers. *
* Initialize it to SID_VIC in case context isolation * If context isolation is enabled, it will be overridden by the SETSTREAMID
* is not enabled, and SID_VIC is used for both firmware * opcode as part of each job.
* and data buffers. */
* vic_writel(vic, stream_id, VIC_THI_STREAMID0);
* If context isolation is enabled, it will be
* overridden by the SETSTREAMID opcode as part of /* STREAMID1 is used for firmware loading. */
* each job. vic_writel(vic, stream_id, VIC_THI_STREAMID1);
*/
vic_writel(vic, value, VIC_THI_STREAMID0);
/* STREAMID1 is used for firmware loading. */
vic_writel(vic, value, VIC_THI_STREAMID1);
}
} }
#endif
/* setup clockgating registers */ /* setup clockgating registers */
vic_writel(vic, CG_IDLE_CG_DLY_CNT(4) | vic_writel(vic, CG_IDLE_CG_DLY_CNT(4) |
......
...@@ -490,6 +490,15 @@ void host1x_cdma_update_sync_queue(struct host1x_cdma *cdma, ...@@ -490,6 +490,15 @@ void host1x_cdma_update_sync_queue(struct host1x_cdma *cdma,
host1x_hw_cdma_resume(host1x, cdma, restart_addr); host1x_hw_cdma_resume(host1x, cdma, restart_addr);
} }
static void cdma_update_work(struct work_struct *work)
{
struct host1x_cdma *cdma = container_of(work, struct host1x_cdma, update_work);
mutex_lock(&cdma->lock);
update_cdma_locked(cdma);
mutex_unlock(&cdma->lock);
}
/* /*
* Create a cdma * Create a cdma
*/ */
...@@ -499,6 +508,7 @@ int host1x_cdma_init(struct host1x_cdma *cdma) ...@@ -499,6 +508,7 @@ int host1x_cdma_init(struct host1x_cdma *cdma)
mutex_init(&cdma->lock); mutex_init(&cdma->lock);
init_completion(&cdma->complete); init_completion(&cdma->complete);
INIT_WORK(&cdma->update_work, cdma_update_work);
INIT_LIST_HEAD(&cdma->sync_queue); INIT_LIST_HEAD(&cdma->sync_queue);
...@@ -679,7 +689,5 @@ void host1x_cdma_end(struct host1x_cdma *cdma, ...@@ -679,7 +689,5 @@ void host1x_cdma_end(struct host1x_cdma *cdma,
*/ */
void host1x_cdma_update(struct host1x_cdma *cdma) void host1x_cdma_update(struct host1x_cdma *cdma)
{ {
mutex_lock(&cdma->lock); schedule_work(&cdma->update_work);
update_cdma_locked(cdma);
mutex_unlock(&cdma->lock);
} }
...@@ -11,6 +11,7 @@ ...@@ -11,6 +11,7 @@
#include <linux/sched.h> #include <linux/sched.h>
#include <linux/completion.h> #include <linux/completion.h>
#include <linux/list.h> #include <linux/list.h>
#include <linux/workqueue.h>
struct host1x_syncpt; struct host1x_syncpt;
struct host1x_userctx_timeout; struct host1x_userctx_timeout;
...@@ -69,6 +70,7 @@ struct host1x_cdma { ...@@ -69,6 +70,7 @@ struct host1x_cdma {
struct buffer_timeout timeout; /* channel's timeout state/wq */ struct buffer_timeout timeout; /* channel's timeout state/wq */
bool running; bool running;
bool torndown; bool torndown;
struct work_struct update_work;
}; };
#define cdma_to_channel(cdma) container_of(cdma, struct host1x_channel, cdma) #define cdma_to_channel(cdma) container_of(cdma, struct host1x_channel, cdma)
......
...@@ -35,8 +35,6 @@ int host1x_memory_context_list_init(struct host1x *host1x) ...@@ -35,8 +35,6 @@ int host1x_memory_context_list_init(struct host1x *host1x)
cdl->len = err / 4; cdl->len = err / 4;
for (i = 0; i < cdl->len; i++) { for (i = 0; i < cdl->len; i++) {
struct iommu_fwspec *fwspec;
ctx = &cdl->devs[i]; ctx = &cdl->devs[i];
ctx->host = host1x; ctx->host = host1x;
...@@ -70,14 +68,12 @@ int host1x_memory_context_list_init(struct host1x *host1x) ...@@ -70,14 +68,12 @@ int host1x_memory_context_list_init(struct host1x *host1x)
goto del_devices; goto del_devices;
} }
fwspec = dev_iommu_fwspec_get(&ctx->dev); if (!tegra_dev_iommu_get_stream_id(&ctx->dev, &ctx->stream_id) ||
if (!fwspec || !device_iommu_mapped(&ctx->dev)) { !device_iommu_mapped(&ctx->dev)) {
dev_err(host1x->dev, "Context device %d has no IOMMU!\n", i); dev_err(host1x->dev, "Context device %d has no IOMMU!\n", i);
device_del(&ctx->dev); device_del(&ctx->dev);
goto del_devices; goto del_devices;
} }
ctx->stream_id = fwspec->ids[0] & 0xffff;
} }
return 0; return 0;
......
...@@ -77,6 +77,7 @@ static int show_channel(struct host1x_channel *ch, void *data, bool show_fifo) ...@@ -77,6 +77,7 @@ static int show_channel(struct host1x_channel *ch, void *data, bool show_fifo)
static void show_syncpts(struct host1x *m, struct output *o, bool show_all) static void show_syncpts(struct host1x *m, struct output *o, bool show_all)
{ {
unsigned long irqflags;
struct list_head *pos; struct list_head *pos;
unsigned int i; unsigned int i;
int err; int err;
...@@ -92,10 +93,10 @@ static void show_syncpts(struct host1x *m, struct output *o, bool show_all) ...@@ -92,10 +93,10 @@ static void show_syncpts(struct host1x *m, struct output *o, bool show_all)
u32 min = host1x_syncpt_load(m->syncpt + i); u32 min = host1x_syncpt_load(m->syncpt + i);
unsigned int waiters = 0; unsigned int waiters = 0;
spin_lock(&m->syncpt[i].intr.lock); spin_lock_irqsave(&m->syncpt[i].fences.lock, irqflags);
list_for_each(pos, &m->syncpt[i].intr.wait_head) list_for_each(pos, &m->syncpt[i].fences.list)
waiters++; waiters++;
spin_unlock(&m->syncpt[i].intr.lock); spin_unlock_irqrestore(&m->syncpt[i].fences.lock, irqflags);
if (!kref_read(&m->syncpt[i].ref)) if (!kref_read(&m->syncpt[i].ref))
continue; continue;
......
...@@ -516,7 +516,7 @@ static int host1x_probe(struct platform_device *pdev) ...@@ -516,7 +516,7 @@ static int host1x_probe(struct platform_device *pdev)
return PTR_ERR(host->regs); return PTR_ERR(host->regs);
} }
syncpt_irq = platform_get_irq(pdev, 0); host->syncpt_irq = platform_get_irq(pdev, 0);
if (syncpt_irq < 0) if (syncpt_irq < 0)
return syncpt_irq; return syncpt_irq;
...@@ -578,7 +578,7 @@ static int host1x_probe(struct platform_device *pdev) ...@@ -578,7 +578,7 @@ static int host1x_probe(struct platform_device *pdev)
goto free_contexts; goto free_contexts;
} }
err = host1x_intr_init(host, syncpt_irq); err = host1x_intr_init(host);
if (err) { if (err) {
dev_err(&pdev->dev, "failed to initialize interrupts\n"); dev_err(&pdev->dev, "failed to initialize interrupts\n");
goto deinit_syncpt; goto deinit_syncpt;
......
...@@ -74,8 +74,7 @@ struct host1x_syncpt_ops { ...@@ -74,8 +74,7 @@ struct host1x_syncpt_ops {
}; };
struct host1x_intr_ops { struct host1x_intr_ops {
int (*init_host_sync)(struct host1x *host, u32 cpm, int (*init_host_sync)(struct host1x *host, u32 cpm);
void (*syncpt_thresh_work)(struct work_struct *work));
void (*set_syncpt_threshold)( void (*set_syncpt_threshold)(
struct host1x *host, unsigned int id, u32 thresh); struct host1x *host, unsigned int id, u32 thresh);
void (*enable_syncpt_intr)(struct host1x *host, unsigned int id); void (*enable_syncpt_intr)(struct host1x *host, unsigned int id);
...@@ -125,6 +124,7 @@ struct host1x { ...@@ -125,6 +124,7 @@ struct host1x {
void __iomem *regs; void __iomem *regs;
void __iomem *hv_regs; /* hypervisor region */ void __iomem *hv_regs; /* hypervisor region */
void __iomem *common_regs; void __iomem *common_regs;
int syncpt_irq;
struct host1x_syncpt *syncpt; struct host1x_syncpt *syncpt;
struct host1x_syncpt_base *bases; struct host1x_syncpt_base *bases;
struct device *dev; struct device *dev;
...@@ -138,7 +138,6 @@ struct host1x { ...@@ -138,7 +138,6 @@ struct host1x {
dma_addr_t iova_end; dma_addr_t iova_end;
struct mutex intr_mutex; struct mutex intr_mutex;
int intr_syncpt_irq;
const struct host1x_syncpt_ops *syncpt_op; const struct host1x_syncpt_ops *syncpt_op;
const struct host1x_intr_ops *intr_op; const struct host1x_intr_ops *intr_op;
...@@ -216,10 +215,9 @@ static inline void host1x_hw_syncpt_enable_protection(struct host1x *host) ...@@ -216,10 +215,9 @@ static inline void host1x_hw_syncpt_enable_protection(struct host1x *host)
return host->syncpt_op->enable_protection(host); return host->syncpt_op->enable_protection(host);
} }
static inline int host1x_hw_intr_init_host_sync(struct host1x *host, u32 cpm, static inline int host1x_hw_intr_init_host_sync(struct host1x *host, u32 cpm)
void (*syncpt_thresh_work)(struct work_struct *))
{ {
return host->intr_op->init_host_sync(host, cpm, syncpt_thresh_work); return host->intr_op->init_host_sync(host, cpm);
} }
static inline void host1x_hw_intr_set_syncpt_threshold(struct host1x *host, static inline void host1x_hw_intr_set_syncpt_threshold(struct host1x *host,
......
...@@ -15,22 +15,6 @@ ...@@ -15,22 +15,6 @@
#include "intr.h" #include "intr.h"
#include "syncpt.h" #include "syncpt.h"
static 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) static const char *host1x_syncpt_fence_get_driver_name(struct dma_fence *f)
{ {
return "host1x"; return "host1x";
...@@ -49,11 +33,11 @@ static struct host1x_syncpt_fence *to_host1x_fence(struct dma_fence *f) ...@@ -49,11 +33,11 @@ static struct host1x_syncpt_fence *to_host1x_fence(struct dma_fence *f)
static bool host1x_syncpt_fence_enable_signaling(struct dma_fence *f) static bool host1x_syncpt_fence_enable_signaling(struct dma_fence *f)
{ {
struct host1x_syncpt_fence *sf = to_host1x_fence(f); struct host1x_syncpt_fence *sf = to_host1x_fence(f);
int err;
if (host1x_syncpt_is_expired(sf->sp, sf->threshold)) if (host1x_syncpt_is_expired(sf->sp, sf->threshold))
return false; return false;
/* Reference for interrupt path. */
dma_fence_get(f); dma_fence_get(f);
/* /*
...@@ -61,24 +45,17 @@ static bool host1x_syncpt_fence_enable_signaling(struct dma_fence *f) ...@@ -61,24 +45,17 @@ static bool host1x_syncpt_fence_enable_signaling(struct dma_fence *f)
* reference to any fences for which 'enable_signaling' has been * reference to any fences for which 'enable_signaling' has been
* called (and that have not been signalled). * called (and that have not been signalled).
* *
* We provide a userspace API to create arbitrary syncpoint fences, * We cannot currently always guarantee that all fences get signalled
* so we cannot normally guarantee that all fences get signalled. * or cancelled. As such, for such situations, set up a timeout, so
* As such, setup a timeout, so that long-lasting fences will get * that long-lasting fences will get reaped eventually.
* reaped eventually.
*/ */
schedule_delayed_work(&sf->timeout_work, msecs_to_jiffies(30000)); if (sf->timeout) {
/* Reference for timeout path. */
err = host1x_intr_add_action(sf->sp->host, sf->sp, sf->threshold, dma_fence_get(f);
HOST1X_INTR_ACTION_SIGNAL_FENCE, f, schedule_delayed_work(&sf->timeout_work, msecs_to_jiffies(30000));
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 */ host1x_intr_add_fence_locked(sf->sp->host, sf);
sf->waiter = NULL;
/* /*
* The fence may get signalled at any time after the above call, * The fence may get signalled at any time after the above call,
...@@ -89,37 +66,32 @@ static bool host1x_syncpt_fence_enable_signaling(struct dma_fence *f) ...@@ -89,37 +66,32 @@ static bool host1x_syncpt_fence_enable_signaling(struct dma_fence *f)
return true; 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);
}
static const struct dma_fence_ops host1x_syncpt_fence_ops = { static const struct dma_fence_ops host1x_syncpt_fence_ops = {
.get_driver_name = host1x_syncpt_fence_get_driver_name, .get_driver_name = host1x_syncpt_fence_get_driver_name,
.get_timeline_name = host1x_syncpt_fence_get_timeline_name, .get_timeline_name = host1x_syncpt_fence_get_timeline_name,
.enable_signaling = host1x_syncpt_fence_enable_signaling, .enable_signaling = host1x_syncpt_fence_enable_signaling,
.release = host1x_syncpt_fence_release,
}; };
void host1x_fence_signal(struct host1x_syncpt_fence *f) void host1x_fence_signal(struct host1x_syncpt_fence *f)
{ {
if (atomic_xchg(&f->signaling, 1)) if (atomic_xchg(&f->signaling, 1)) {
/*
* Already on timeout path, but we removed the fence before
* timeout path could, so drop interrupt path reference.
*/
dma_fence_put(&f->base);
return; return;
}
/* if (f->timeout && cancel_delayed_work(&f->timeout_work)) {
* Cancel pending timeout work - if it races, it will /*
* not get 'f->signaling' and return. * We know that the timeout path will not be entered.
*/ * Safe to drop the timeout path's reference now.
cancel_delayed_work_sync(&f->timeout_work); */
dma_fence_put(&f->base);
host1x_intr_put_ref(f->sp->host, f->sp->id, f->waiter_ref, false); }
dma_fence_signal(&f->base); dma_fence_signal_locked(&f->base);
dma_fence_put(&f->base); dma_fence_put(&f->base);
} }
...@@ -129,21 +101,29 @@ static void do_fence_timeout(struct work_struct *work) ...@@ -129,21 +101,29 @@ static void do_fence_timeout(struct work_struct *work)
struct host1x_syncpt_fence *f = struct host1x_syncpt_fence *f =
container_of(dwork, struct host1x_syncpt_fence, timeout_work); container_of(dwork, struct host1x_syncpt_fence, timeout_work);
if (atomic_xchg(&f->signaling, 1)) if (atomic_xchg(&f->signaling, 1)) {
/* Already on interrupt path, drop timeout path reference if any. */
if (f->timeout)
dma_fence_put(&f->base);
return; return;
}
/* if (host1x_intr_remove_fence(f->sp->host, f)) {
* Cancel pending timeout work - if it races, it will /*
* not get 'f->signaling' and return. * Managed to remove fence from queue, so it's safe to drop
*/ * the interrupt path's reference.
host1x_intr_put_ref(f->sp->host, f->sp->id, f->waiter_ref, true); */
dma_fence_put(&f->base);
}
dma_fence_set_error(&f->base, -ETIMEDOUT); dma_fence_set_error(&f->base, -ETIMEDOUT);
dma_fence_signal(&f->base); dma_fence_signal(&f->base);
dma_fence_put(&f->base); if (f->timeout)
dma_fence_put(&f->base);
} }
struct dma_fence *host1x_fence_create(struct host1x_syncpt *sp, u32 threshold) struct dma_fence *host1x_fence_create(struct host1x_syncpt *sp, u32 threshold,
bool timeout)
{ {
struct host1x_syncpt_fence *fence; struct host1x_syncpt_fence *fence;
...@@ -151,16 +131,11 @@ struct dma_fence *host1x_fence_create(struct host1x_syncpt *sp, u32 threshold) ...@@ -151,16 +131,11 @@ struct dma_fence *host1x_fence_create(struct host1x_syncpt *sp, u32 threshold)
if (!fence) if (!fence)
return ERR_PTR(-ENOMEM); return ERR_PTR(-ENOMEM);
fence->waiter = kzalloc(sizeof(*fence->waiter), GFP_KERNEL);
if (!fence->waiter) {
kfree(fence);
return ERR_PTR(-ENOMEM);
}
fence->sp = sp; fence->sp = sp;
fence->threshold = threshold; fence->threshold = threshold;
fence->timeout = timeout;
dma_fence_init(&fence->base, &host1x_syncpt_fence_ops, &lock, dma_fence_init(&fence->base, &host1x_syncpt_fence_ops, &sp->fences.lock,
dma_fence_context_alloc(1), 0); dma_fence_context_alloc(1), 0);
INIT_DELAYED_WORK(&fence->timeout_work, do_fence_timeout); INIT_DELAYED_WORK(&fence->timeout_work, do_fence_timeout);
...@@ -168,3 +143,12 @@ struct dma_fence *host1x_fence_create(struct host1x_syncpt *sp, u32 threshold) ...@@ -168,3 +143,12 @@ struct dma_fence *host1x_fence_create(struct host1x_syncpt *sp, u32 threshold)
return &fence->base; return &fence->base;
} }
EXPORT_SYMBOL(host1x_fence_create); EXPORT_SYMBOL(host1x_fence_create);
void host1x_fence_cancel(struct dma_fence *f)
{
struct host1x_syncpt_fence *sf = to_host1x_fence(f);
schedule_delayed_work(&sf->timeout_work, 0);
flush_delayed_work(&sf->timeout_work);
}
EXPORT_SYMBOL(host1x_fence_cancel);
...@@ -6,7 +6,24 @@ ...@@ -6,7 +6,24 @@
#ifndef HOST1X_FENCE_H #ifndef HOST1X_FENCE_H
#define HOST1X_FENCE_H #define HOST1X_FENCE_H
struct host1x_syncpt_fence; struct host1x_syncpt_fence {
struct dma_fence base;
atomic_t signaling;
struct host1x_syncpt *sp;
u32 threshold;
bool timeout;
struct delayed_work timeout_work;
struct list_head list;
};
struct host1x_fence_list {
spinlock_t lock;
struct list_head list;
};
void host1x_fence_signal(struct host1x_syncpt_fence *fence); void host1x_fence_signal(struct host1x_syncpt_fence *fence);
......
...@@ -179,14 +179,12 @@ static inline void synchronize_syncpt_base(struct host1x_job *job) ...@@ -179,14 +179,12 @@ static inline void synchronize_syncpt_base(struct host1x_job *job)
static void host1x_channel_set_streamid(struct host1x_channel *channel) static void host1x_channel_set_streamid(struct host1x_channel *channel)
{ {
#if HOST1X_HW >= 6 #if HOST1X_HW >= 6
u32 sid = 0x7f; u32 stream_id;
#ifdef CONFIG_IOMMU_API
struct iommu_fwspec *spec = dev_iommu_fwspec_get(channel->dev->parent); if (!tegra_dev_iommu_get_stream_id(channel->dev->parent, &stream_id))
if (spec) stream_id = TEGRA_STREAM_ID_BYPASS;
sid = spec->ids[0] & 0xffff;
#endif
host1x_ch_writel(channel, sid, HOST1X_CHANNEL_SMMU_STREAMID); host1x_ch_writel(channel, stream_id, HOST1X_CHANNEL_SMMU_STREAMID);
#endif #endif
} }
...@@ -278,6 +276,14 @@ static void channel_program_cdma(struct host1x_job *job) ...@@ -278,6 +276,14 @@ static void channel_program_cdma(struct host1x_job *job)
#endif #endif
} }
static void job_complete_callback(struct dma_fence *fence, struct dma_fence_cb *cb)
{
struct host1x_job *job = container_of(cb, struct host1x_job, fence_cb);
/* Schedules CDMA update. */
host1x_cdma_update(&job->channel->cdma);
}
static int channel_submit(struct host1x_job *job) static int channel_submit(struct host1x_job *job)
{ {
struct host1x_channel *ch = job->channel; struct host1x_channel *ch = job->channel;
...@@ -285,7 +291,6 @@ static int channel_submit(struct host1x_job *job) ...@@ -285,7 +291,6 @@ static int channel_submit(struct host1x_job *job)
u32 prev_max = 0; u32 prev_max = 0;
u32 syncval; u32 syncval;
int err; int err;
struct host1x_waitlist *completed_waiter = NULL;
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),
...@@ -298,14 +303,7 @@ static int channel_submit(struct host1x_job *job) ...@@ -298,14 +303,7 @@ static int channel_submit(struct host1x_job *job)
/* get submit lock */ /* get submit lock */
err = mutex_lock_interruptible(&ch->submitlock); err = mutex_lock_interruptible(&ch->submitlock);
if (err) if (err)
goto error; return err;
completed_waiter = kzalloc(sizeof(*completed_waiter), GFP_KERNEL);
if (!completed_waiter) {
mutex_unlock(&ch->submitlock);
err = -ENOMEM;
goto error;
}
host1x_channel_set_streamid(ch); host1x_channel_set_streamid(ch);
host1x_enable_gather_filter(ch); host1x_enable_gather_filter(ch);
...@@ -315,31 +313,37 @@ static int channel_submit(struct host1x_job *job) ...@@ -315,31 +313,37 @@ static int channel_submit(struct host1x_job *job)
err = host1x_cdma_begin(&ch->cdma, job); err = host1x_cdma_begin(&ch->cdma, job);
if (err) { if (err) {
mutex_unlock(&ch->submitlock); mutex_unlock(&ch->submitlock);
goto error; return err;
} }
channel_program_cdma(job); channel_program_cdma(job);
syncval = host1x_syncpt_read_max(sp); syncval = host1x_syncpt_read_max(sp);
/*
* Create fence before submitting job to HW to avoid job completing
* before the fence is set up.
*/
job->fence = host1x_fence_create(sp, syncval, true);
if (WARN(IS_ERR(job->fence), "Failed to create submit complete fence")) {
job->fence = NULL;
} else {
err = dma_fence_add_callback(job->fence, &job->fence_cb,
job_complete_callback);
}
/* 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);
trace_host1x_channel_submitted(dev_name(ch->dev), prev_max, syncval); trace_host1x_channel_submitted(dev_name(ch->dev), prev_max, syncval);
/* schedule a submit complete interrupt */
err = host1x_intr_add_action(host, sp, syncval,
HOST1X_INTR_ACTION_SUBMIT_COMPLETE, ch,
completed_waiter, &job->waiter);
completed_waiter = NULL;
WARN(err, "Failed to set submit complete interrupt");
mutex_unlock(&ch->submitlock); mutex_unlock(&ch->submitlock);
return 0; if (err == -ENOENT)
host1x_cdma_update(&ch->cdma);
else
WARN(err, "Failed to set submit complete interrupt");
error: return 0;
kfree(completed_waiter);
return err;
} }
static int host1x_channel_init(struct host1x_channel *ch, struct host1x *dev, static int host1x_channel_init(struct host1x_channel *ch, struct host1x *dev,
......
...@@ -53,7 +53,7 @@ static inline u32 host1x_uclass_incr_syncpt_cond_f(u32 v) ...@@ -53,7 +53,7 @@ static inline u32 host1x_uclass_incr_syncpt_cond_f(u32 v)
host1x_uclass_incr_syncpt_cond_f(v) host1x_uclass_incr_syncpt_cond_f(v)
static inline u32 host1x_uclass_incr_syncpt_indx_f(u32 v) static inline u32 host1x_uclass_incr_syncpt_indx_f(u32 v)
{ {
return (v & 0xff) << 0; return (v & 0x3ff) << 0;
} }
#define HOST1X_UCLASS_INCR_SYNCPT_INDX_F(v) \ #define HOST1X_UCLASS_INCR_SYNCPT_INDX_F(v) \
host1x_uclass_incr_syncpt_indx_f(v) host1x_uclass_incr_syncpt_indx_f(v)
......
...@@ -53,7 +53,7 @@ static inline u32 host1x_uclass_incr_syncpt_cond_f(u32 v) ...@@ -53,7 +53,7 @@ static inline u32 host1x_uclass_incr_syncpt_cond_f(u32 v)
host1x_uclass_incr_syncpt_cond_f(v) host1x_uclass_incr_syncpt_cond_f(v)
static inline u32 host1x_uclass_incr_syncpt_indx_f(u32 v) static inline u32 host1x_uclass_incr_syncpt_indx_f(u32 v)
{ {
return (v & 0xff) << 0; return (v & 0x3ff) << 0;
} }
#define HOST1X_UCLASS_INCR_SYNCPT_INDX_F(v) \ #define HOST1X_UCLASS_INCR_SYNCPT_INDX_F(v) \
host1x_uclass_incr_syncpt_indx_f(v) host1x_uclass_incr_syncpt_indx_f(v)
......
...@@ -53,7 +53,7 @@ static inline u32 host1x_uclass_incr_syncpt_cond_f(u32 v) ...@@ -53,7 +53,7 @@ static inline u32 host1x_uclass_incr_syncpt_cond_f(u32 v)
host1x_uclass_incr_syncpt_cond_f(v) host1x_uclass_incr_syncpt_cond_f(v)
static inline u32 host1x_uclass_incr_syncpt_indx_f(u32 v) static inline u32 host1x_uclass_incr_syncpt_indx_f(u32 v)
{ {
return (v & 0xff) << 0; return (v & 0x3ff) << 0;
} }
#define HOST1X_UCLASS_INCR_SYNCPT_INDX_F(v) \ #define HOST1X_UCLASS_INCR_SYNCPT_INDX_F(v) \
host1x_uclass_incr_syncpt_indx_f(v) host1x_uclass_incr_syncpt_indx_f(v)
......
...@@ -13,23 +13,6 @@ ...@@ -13,23 +13,6 @@
#include "../intr.h" #include "../intr.h"
#include "../dev.h" #include "../dev.h"
/*
* Sync point threshold interrupt service function
* Handles sync point threshold triggers, in interrupt context
*/
static void host1x_intr_syncpt_handle(struct host1x_syncpt *syncpt)
{
unsigned int id = syncpt->id;
struct host1x *host = syncpt->host;
host1x_sync_writel(host, BIT(id % 32),
HOST1X_SYNC_SYNCPT_THRESH_INT_DISABLE(id / 32));
host1x_sync_writel(host, BIT(id % 32),
HOST1X_SYNC_SYNCPT_THRESH_CPU0_INT_STATUS(id / 32));
schedule_work(&syncpt->intr.work);
}
static irqreturn_t syncpt_thresh_isr(int irq, void *dev_id) static irqreturn_t syncpt_thresh_isr(int irq, void *dev_id)
{ {
struct host1x *host = dev_id; struct host1x *host = dev_id;
...@@ -39,17 +22,20 @@ static irqreturn_t syncpt_thresh_isr(int irq, void *dev_id) ...@@ -39,17 +22,20 @@ static irqreturn_t syncpt_thresh_isr(int irq, void *dev_id)
for (i = 0; i < DIV_ROUND_UP(host->info->nb_pts, 32); i++) { for (i = 0; i < DIV_ROUND_UP(host->info->nb_pts, 32); i++) {
reg = host1x_sync_readl(host, reg = host1x_sync_readl(host,
HOST1X_SYNC_SYNCPT_THRESH_CPU0_INT_STATUS(i)); HOST1X_SYNC_SYNCPT_THRESH_CPU0_INT_STATUS(i));
for_each_set_bit(id, &reg, 32) {
struct host1x_syncpt *syncpt = host1x_sync_writel(host, reg,
host->syncpt + (i * 32 + id); HOST1X_SYNC_SYNCPT_THRESH_INT_DISABLE(i));
host1x_intr_syncpt_handle(syncpt); host1x_sync_writel(host, reg,
} HOST1X_SYNC_SYNCPT_THRESH_CPU0_INT_STATUS(i));
for_each_set_bit(id, &reg, 32)
host1x_intr_handle_interrupt(host, i * 32 + id);
} }
return IRQ_HANDLED; return IRQ_HANDLED;
} }
static void _host1x_intr_disable_all_syncpt_intrs(struct host1x *host) static void host1x_intr_disable_all_syncpt_intrs(struct host1x *host)
{ {
unsigned int i; unsigned int i;
...@@ -90,45 +76,38 @@ static void intr_hw_init(struct host1x *host, u32 cpm) ...@@ -90,45 +76,38 @@ static void intr_hw_init(struct host1x *host, u32 cpm)
} }
static int static int
_host1x_intr_init_host_sync(struct host1x *host, u32 cpm, host1x_intr_init_host_sync(struct host1x *host, u32 cpm)
void (*syncpt_thresh_work)(struct work_struct *))
{ {
unsigned int i;
int err; int err;
host1x_hw_intr_disable_all_syncpt_intrs(host); host1x_hw_intr_disable_all_syncpt_intrs(host);
for (i = 0; i < host->info->nb_pts; i++) err = devm_request_irq(host->dev, host->syncpt_irq,
INIT_WORK(&host->syncpt[i].intr.work, syncpt_thresh_work);
err = devm_request_irq(host->dev, host->intr_syncpt_irq,
syncpt_thresh_isr, IRQF_SHARED, syncpt_thresh_isr, IRQF_SHARED,
"host1x_syncpt", host); "host1x_syncpt", host);
if (err < 0) { if (err < 0)
WARN_ON(1);
return err; return err;
}
intr_hw_init(host, cpm); intr_hw_init(host, cpm);
return 0; return 0;
} }
static void _host1x_intr_set_syncpt_threshold(struct host1x *host, static void host1x_intr_set_syncpt_threshold(struct host1x *host,
unsigned int id, unsigned int id,
u32 thresh) u32 thresh)
{ {
host1x_sync_writel(host, thresh, HOST1X_SYNC_SYNCPT_INT_THRESH(id)); host1x_sync_writel(host, thresh, HOST1X_SYNC_SYNCPT_INT_THRESH(id));
} }
static void _host1x_intr_enable_syncpt_intr(struct host1x *host, static void host1x_intr_enable_syncpt_intr(struct host1x *host,
unsigned int id) unsigned int id)
{ {
host1x_sync_writel(host, BIT(id % 32), host1x_sync_writel(host, BIT(id % 32),
HOST1X_SYNC_SYNCPT_THRESH_INT_ENABLE_CPU0(id / 32)); HOST1X_SYNC_SYNCPT_THRESH_INT_ENABLE_CPU0(id / 32));
} }
static void _host1x_intr_disable_syncpt_intr(struct host1x *host, static void host1x_intr_disable_syncpt_intr(struct host1x *host,
unsigned int id) unsigned int id)
{ {
host1x_sync_writel(host, BIT(id % 32), host1x_sync_writel(host, BIT(id % 32),
...@@ -137,23 +116,10 @@ static void _host1x_intr_disable_syncpt_intr(struct host1x *host, ...@@ -137,23 +116,10 @@ static void _host1x_intr_disable_syncpt_intr(struct host1x *host,
HOST1X_SYNC_SYNCPT_THRESH_CPU0_INT_STATUS(id / 32)); HOST1X_SYNC_SYNCPT_THRESH_CPU0_INT_STATUS(id / 32));
} }
static int _host1x_free_syncpt_irq(struct host1x *host)
{
unsigned int i;
devm_free_irq(host->dev, host->intr_syncpt_irq, host);
for (i = 0; i < host->info->nb_pts; i++)
cancel_work_sync(&host->syncpt[i].intr.work);
return 0;
}
static const struct host1x_intr_ops host1x_intr_ops = { static const struct host1x_intr_ops host1x_intr_ops = {
.init_host_sync = _host1x_intr_init_host_sync, .init_host_sync = host1x_intr_init_host_sync,
.set_syncpt_threshold = _host1x_intr_set_syncpt_threshold, .set_syncpt_threshold = host1x_intr_set_syncpt_threshold,
.enable_syncpt_intr = _host1x_intr_enable_syncpt_intr, .enable_syncpt_intr = host1x_intr_enable_syncpt_intr,
.disable_syncpt_intr = _host1x_intr_disable_syncpt_intr, .disable_syncpt_intr = host1x_intr_disable_syncpt_intr,
.disable_all_syncpt_intrs = _host1x_intr_disable_all_syncpt_intrs, .disable_all_syncpt_intrs = host1x_intr_disable_all_syncpt_intrs,
.free_syncpt_irq = _host1x_free_syncpt_irq,
}; };
...@@ -106,9 +106,6 @@ static void syncpt_assign_to_channel(struct host1x_syncpt *sp, ...@@ -106,9 +106,6 @@ static void syncpt_assign_to_channel(struct host1x_syncpt *sp,
#if HOST1X_HW >= 6 #if HOST1X_HW >= 6
struct host1x *host = sp->host; struct host1x *host = sp->host;
if (!host->hv_regs)
return;
host1x_sync_writel(host, host1x_sync_writel(host,
HOST1X_SYNC_SYNCPT_CH_APP_CH(ch ? ch->id : 0xff), HOST1X_SYNC_SYNCPT_CH_APP_CH(ch ? ch->id : 0xff),
HOST1X_SYNC_SYNCPT_CH_APP(sp->id)); HOST1X_SYNC_SYNCPT_CH_APP(sp->id));
......
This diff is collapsed.
...@@ -2,87 +2,17 @@ ...@@ -2,87 +2,17 @@
/* /*
* Tegra host1x Interrupt Management * Tegra host1x Interrupt Management
* *
* Copyright (c) 2010-2013, NVIDIA Corporation. * Copyright (c) 2010-2021, NVIDIA Corporation.
*/ */
#ifndef __HOST1X_INTR_H #ifndef __HOST1X_INTR_H
#define __HOST1X_INTR_H #define __HOST1X_INTR_H
#include <linux/interrupt.h>
#include <linux/workqueue.h>
struct host1x_syncpt;
struct host1x; struct host1x;
struct host1x_syncpt_fence;
enum host1x_intr_action {
/*
* Perform cleanup after a submit has completed.
* 'data' points to a channel
*/
HOST1X_INTR_ACTION_SUBMIT_COMPLETE = 0,
/*
* Wake up a task.
* 'data' points to a wait_queue_head_t
*/
HOST1X_INTR_ACTION_WAKEUP,
/*
* Wake up a interruptible task.
* 'data' points to a wait_queue_head_t
*/
HOST1X_INTR_ACTION_WAKEUP_INTERRUPTIBLE,
HOST1X_INTR_ACTION_SIGNAL_FENCE,
HOST1X_INTR_ACTION_COUNT
};
struct host1x_syncpt_intr {
spinlock_t lock;
struct list_head wait_head;
char thresh_irq_name[12];
struct work_struct work;
};
struct host1x_waitlist {
struct list_head list;
struct kref refcount;
u32 thresh;
enum host1x_intr_action action;
atomic_t state;
void *data;
int count;
};
/*
* Schedule an action to be taken when a sync point reaches the given threshold.
*
* @id the sync point
* @thresh the threshold
* @action the action to take
* @data a pointer to extra data depending on action, see above
* @waiter waiter structure - assumes ownership
* @ref must be passed if cancellation is possible, else NULL
*
* This is a non-blocking api.
*/
int host1x_intr_add_action(struct host1x *host, struct host1x_syncpt *syncpt,
u32 thresh, enum host1x_intr_action action,
void *data, struct host1x_waitlist *waiter,
void **ref);
/*
* Unreference an action submitted to host1x_intr_add_action().
* You must call this if you passed non-NULL as ref.
* @ref the ref returned from host1x_intr_add_action()
* @flush wait until any pending handlers have completed before returning.
*/
void host1x_intr_put_ref(struct host1x *host, unsigned int id, void *ref,
bool flush);
/* Initialize host1x sync point interrupt */ /* Initialize host1x sync point interrupt */
int host1x_intr_init(struct host1x *host, unsigned int irq_sync); int host1x_intr_init(struct host1x *host);
/* Deinitialize host1x sync point interrupt */ /* Deinitialize host1x sync point interrupt */
void host1x_intr_deinit(struct host1x *host); void host1x_intr_deinit(struct host1x *host);
...@@ -93,5 +23,10 @@ void host1x_intr_start(struct host1x *host); ...@@ -93,5 +23,10 @@ void host1x_intr_start(struct host1x *host);
/* Disable host1x sync point interrupt */ /* Disable host1x sync point interrupt */
void host1x_intr_stop(struct host1x *host); void host1x_intr_stop(struct host1x *host);
irqreturn_t host1x_syncpt_thresh_fn(void *dev_id); void host1x_intr_handle_interrupt(struct host1x *host, unsigned int id);
void host1x_intr_add_fence_locked(struct host1x *host, struct host1x_syncpt_fence *fence);
bool host1x_intr_remove_fence(struct host1x *host, struct host1x_syncpt_fence *fence);
#endif #endif
...@@ -88,9 +88,15 @@ static void job_free(struct kref *ref) ...@@ -88,9 +88,15 @@ static void job_free(struct kref *ref)
if (job->release) if (job->release)
job->release(job); job->release(job);
if (job->waiter) if (job->fence) {
host1x_intr_put_ref(job->syncpt->host, job->syncpt->id, /*
job->waiter, false); * remove_callback is atomic w.r.t. fence signaling, so
* after the call returns, we know that the callback is not
* in execution, and the fence can be safely freed.
*/
dma_fence_remove_callback(job->fence, &job->fence_cb);
dma_fence_put(job->fence);
}
if (job->syncpt) if (job->syncpt)
host1x_syncpt_put(job->syncpt); host1x_syncpt_put(job->syncpt);
......
...@@ -7,6 +7,7 @@ ...@@ -7,6 +7,7 @@
#include <linux/module.h> #include <linux/module.h>
#include <linux/device.h> #include <linux/device.h>
#include <linux/dma-fence.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <trace/events/host1x.h> #include <trace/events/host1x.h>
...@@ -209,17 +210,6 @@ int host1x_syncpt_incr(struct host1x_syncpt *sp) ...@@ -209,17 +210,6 @@ int host1x_syncpt_incr(struct host1x_syncpt *sp)
} }
EXPORT_SYMBOL(host1x_syncpt_incr); EXPORT_SYMBOL(host1x_syncpt_incr);
/*
* Updated sync point form hardware, and returns true if syncpoint is expired,
* false if we may need to wait
*/
static bool syncpt_load_min_is_expired(struct host1x_syncpt *sp, u32 thresh)
{
host1x_hw_syncpt_load(sp->host, sp);
return host1x_syncpt_is_expired(sp, thresh);
}
/** /**
* host1x_syncpt_wait() - wait for a syncpoint to reach a given value * host1x_syncpt_wait() - wait for a syncpoint to reach a given value
* @sp: host1x syncpoint * @sp: host1x syncpoint
...@@ -230,10 +220,10 @@ static bool syncpt_load_min_is_expired(struct host1x_syncpt *sp, u32 thresh) ...@@ -230,10 +220,10 @@ static bool syncpt_load_min_is_expired(struct host1x_syncpt *sp, u32 thresh)
int host1x_syncpt_wait(struct host1x_syncpt *sp, u32 thresh, long timeout, int host1x_syncpt_wait(struct host1x_syncpt *sp, u32 thresh, long timeout,
u32 *value) u32 *value)
{ {
DECLARE_WAIT_QUEUE_HEAD_ONSTACK(wq); struct dma_fence *fence;
void *ref; long wait_err;
struct host1x_waitlist *waiter;
int err = 0, check_count = 0; host1x_hw_syncpt_load(sp->host, sp);
if (value) if (value)
*value = host1x_syncpt_load(sp); *value = host1x_syncpt_load(sp);
...@@ -241,73 +231,29 @@ int host1x_syncpt_wait(struct host1x_syncpt *sp, u32 thresh, long timeout, ...@@ -241,73 +231,29 @@ int host1x_syncpt_wait(struct host1x_syncpt *sp, u32 thresh, long timeout,
if (host1x_syncpt_is_expired(sp, thresh)) if (host1x_syncpt_is_expired(sp, thresh))
return 0; return 0;
if (!timeout) {
err = -EAGAIN;
goto done;
}
/* allocate a waiter */
waiter = kzalloc(sizeof(*waiter), GFP_KERNEL);
if (!waiter) {
err = -ENOMEM;
goto done;
}
/* schedule a wakeup when the syncpoint value is reached */
err = host1x_intr_add_action(sp->host, sp, thresh,
HOST1X_INTR_ACTION_WAKEUP_INTERRUPTIBLE,
&wq, waiter, &ref);
if (err)
goto done;
err = -EAGAIN;
/* Caller-specified timeout may be impractically low */
if (timeout < 0) if (timeout < 0)
timeout = LONG_MAX; timeout = LONG_MAX;
else if (timeout == 0)
return -EAGAIN;
/* wait for the syncpoint, or timeout, or signal */ fence = host1x_fence_create(sp, thresh, false);
while (timeout) { if (IS_ERR(fence))
long check = min_t(long, SYNCPT_CHECK_PERIOD, timeout); return PTR_ERR(fence);
int remain;
remain = wait_event_interruptible_timeout(wq,
syncpt_load_min_is_expired(sp, thresh),
check);
if (remain > 0 || host1x_syncpt_is_expired(sp, thresh)) {
if (value)
*value = host1x_syncpt_load(sp);
err = 0; wait_err = dma_fence_wait_timeout(fence, true, timeout);
if (wait_err == 0)
host1x_fence_cancel(fence);
dma_fence_put(fence);
break; if (value)
} *value = host1x_syncpt_load(sp);
if (remain < 0) {
err = remain;
break;
}
timeout -= check;
if (timeout && check_count <= MAX_STUCK_CHECK_COUNT) {
dev_warn(sp->host->dev,
"%s: syncpoint id %u (%s) stuck waiting %d, timeout=%ld\n",
current->comm, sp->id, sp->name,
thresh, timeout);
host1x_debug_dump_syncpts(sp->host);
if (check_count == MAX_STUCK_CHECK_COUNT)
host1x_debug_dump(sp->host);
check_count++;
}
}
host1x_intr_put_ref(sp->host, sp->id, ref, true);
done: if (wait_err == 0)
return err; return -EAGAIN;
else if (wait_err < 0)
return wait_err;
else
return 0;
} }
EXPORT_SYMBOL(host1x_syncpt_wait); EXPORT_SYMBOL(host1x_syncpt_wait);
......
...@@ -14,6 +14,7 @@ ...@@ -14,6 +14,7 @@
#include <linux/kref.h> #include <linux/kref.h>
#include <linux/sched.h> #include <linux/sched.h>
#include "fence.h"
#include "intr.h" #include "intr.h"
struct host1x; struct host1x;
...@@ -39,7 +40,7 @@ struct host1x_syncpt { ...@@ -39,7 +40,7 @@ struct host1x_syncpt {
struct host1x_syncpt_base *base; struct host1x_syncpt_base *base;
/* interrupt data */ /* interrupt data */
struct host1x_syncpt_intr intr; struct host1x_fence_list fences;
/* /*
* If a submission incrementing this syncpoint fails, lock it so that * If a submission incrementing this syncpoint fails, lock it so that
......
...@@ -8,6 +8,7 @@ ...@@ -8,6 +8,7 @@
#include <linux/device.h> #include <linux/device.h>
#include <linux/dma-direction.h> #include <linux/dma-direction.h>
#include <linux/dma-fence.h>
#include <linux/spinlock.h> #include <linux/spinlock.h>
#include <linux/types.h> #include <linux/types.h>
...@@ -221,7 +222,9 @@ u32 host1x_syncpt_base_id(struct host1x_syncpt_base *base); ...@@ -221,7 +222,9 @@ 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); struct dma_fence *host1x_fence_create(struct host1x_syncpt *sp, u32 threshold,
bool timeout);
void host1x_fence_cancel(struct dma_fence *fence);
/* /*
* host1x channel * host1x channel
...@@ -288,8 +291,9 @@ struct host1x_job { ...@@ -288,8 +291,9 @@ struct host1x_job {
u32 syncpt_incrs; u32 syncpt_incrs;
u32 syncpt_end; u32 syncpt_end;
/* Completion waiter ref */ /* Completion fence for job tracking */
void *waiter; struct dma_fence *fence;
struct dma_fence_cb fence_cb;
/* Maximum time to wait for this job */ /* Maximum time to wait for this job */
unsigned int timeout; unsigned int timeout;
......
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