linux/drivers/gpu/drm/nouveau/nvkm/engine/fifo/chan.c
Maxime Ripard 7b1166dee8 drm for 6.16-rc1
new drivers:
 - bring in the asahi uapi header standalone
 - nova-drm: stub driver
 
 rust dependencies (for nova-core):
 - auxiliary
   - bus abstractions
   - driver registration
   - sample driver
 - devres changes from driver-core
 - revocable changes
 
 core:
 - add Apple fourcc modifiers
 - add virtio capset definitions
 - extend EXPORT_SYNC_FILE for timeline syncobjs
 - convert to devm_platform_ioremap_resource
 - refactor shmem helper page pinning
 - DP powerup/down link helpers
 - remove disgusting turds
 - extended %p4cc in vsprintf.c to support fourcc prints
 - change vsprintf %p4cn to %p4chR, remove %p4cn
 - Add drm_file_err function
 - IN_FORMATS_ASYNC property
 - move sitronix from tiny to their own subdir
 
 rust:
 - add drm core infrastructure rust abstractions
   (device/driver, ioctl, file, gem)
 
 dma-buf:
 - adjust sg handling to not cache map on attach
 - allow setting dma-device for import
 - Add a helper to sort and deduplicate dma_fence arrays
 
 docs:
 - updated drm scheduler docs
 - fbdev todo update
 - fb rendering
 - actual brightness
 
 ttm:
 - fix delayed destroy resv object
 
 bridge:
 - add kunit tests
 - convert tc358775 to atomic
 - convert drivers to devm_drm_bridge_alloc
 - convert rk3066_hdmi to bridge driver
 
 scheduler:
 - add kunit tests
 
 panel:
 - refcount panels to improve lifetime handling
 - Powertip PH128800T004-ZZA01
 - NLT NL13676BC25-03F, Tianma TM070JDHG34-00
 - Himax HX8279/HX8279-D DDIC
 - Visionox G2647FB105
 - Sitronix ST7571
 - ZOTAC rotation quirk
 
 vkms:
 - allow attaching more displays
 
 i915:
 - xe3lpd display updates
 - vrr refactor
 - intel_display struct conversions
 - xe2hpd memory type identification
 - add link rate/count to i915_display_info
 - cleanup VGA plane handling
 - refactor HDCP GSC
 - fix SLPC wait boosting reference counting
 - add 20ms delay to engine reset
 - fix fence release on early probe errors
 
 xe:
 - SRIOV updates
 - BMG PCI ID update
 - support separate firmware for each GT
 - SVM fix, prelim SVM multi-device work
 - export fan speed
 - temp disable d3cold on BMG
 - backup VRAM in PM notifier instead of suspend/freeze
 - update xe_ttm_access_memory to use GPU for non-visible access
 - fix guc_info debugfs for VFs
 - use copy_from_user instead of __copy_from_user
 - append PCIe gen5 limitations to xe_firmware document
 
 amdgpu:
 - DSC cleanup
 - DC Scaling updates
 - Fused I2C-over-AUX updates
 - DMUB updates
 - Use drm_file_err in amdgpu
 - Enforce isolation updates
 - Use new dma_fence helpers
 - USERQ fixes
 - Documentation updates
 - SR-IOV updates
 - RAS updates
 - PSP 12 cleanups
 - GC 9.5 updates
 - SMU 13.x updates
 - VCN / JPEG SR-IOV updates
 
 amdkfd:
 - Update error messages for SDMA
 - Userptr updates
 - XNACK fixes
 
 radeon:
 - CIK doorbell cleanup
 
 nouveau:
 - add support for NVIDIA r570 GSP firmware
 - enable Hopper/Blackwell support
 
 nova-core:
 - fix task list
 - register definition infrastructure
 - move firmware into own rust module
 - register auxiliary device for nova-drm
 
 nova-drm:
 - initial driver skeleton
 
 msm:
 - GPU:
   - ACD (adaptive clock distribution) for X1-85
   - drop fictional address_space_size
   - improve GMU HFI response time out robustness
   - fix crash when throttling during boot
 - DPU:
   - use single CTL path for flushing on DPU 5.x+
   - improve SSPP allocation code for better sharing
   - Enabled SmartDMA on SM8150, SC8180X, SC8280XP, SM8550
   - Added SAR2130P support
   - Disabled DSC support on MSM8937, MSM8917, MSM8953, SDM660
 - DP:
   - switch to new audio helpers
   - better LTTPR handling
 - DSI:
   - Added support for SA8775P
   - Added SAR2130P support
 - HDMI:
   - Switched to use new helpers for ACR data
   - Fixed old standing issue of HPD not working in some cases
 
 amdxdna:
 - add dma-buf support
 - allow empty command submits
 
 renesas:
 - add dma-buf support
 - add zpos, alpha, blend support
 
 panthor:
 - fail properly for NO_MMAP bos
 - add SET_LABEL ioctl
 - debugfs BO dumping support
 
 imagination:
 - update DT bindings
 - support TI AM68 GPU
 
 hibmc:
 - improve interrupt handling and HPD support
 
 virtio:
 - add panic handler support
 
 rockchip:
 - add RK3588 support
 - add DP AUX bus panel support
 
 ivpu:
 - add heartbeat based hangcheck
 
 mediatek:
 - prepares support for MT8195/99 HDMIv2/DDCv2
 
 anx7625:
 - improve HPD
 
 tegra:
 - speed up firmware loading
 -----BEGIN PGP SIGNATURE-----
 
 iQIzBAABCgAdFiEEEKbZHaGwW9KfbeusDHTzWXnEhr4FAmg2aVAACgkQDHTzWXnE
 hr6DjhAApr2fZjugU3EmpsARdcIWgEd+X65R97ef7RlUGqBKm2joSwZGOhH0oBsG
 9WyO92Qzu6XMe8OibKqY4D2hir9UPz5v+uEWe3q9CzZGbNyAwyVRjVkaKpnI9upv
 1dmHFI7HgPu6qbz6RfPIfgALBLXvVXMaQ4+ZgN/cLtZFa+OLAV5ByqWsRPPXZFb0
 F/pQGQ4ursglfA+LH3SVPfnTN53lu93IlM5/Os9OQQGj+44w94zQ6DCm7CY1AugH
 n+RM/0Yv7WaoF1ByeOtq4FcrmLRrd+ozsvITbRZqhOx7zS/mhP8LRzAwgKWOYzSh
 puKunyQiSdHR7FSqSi8uyY3YumcLWNa/17LMKoTf+KqweJbKGE7RVBuFBn6WUdPb
 AYHZrSB4USAeyahdrrsU+q7ltu5urs5ckpbXsRurMiaUz/BLim1PIm3N5FDLPY7B
 PD1n1FcMUv3CmJT5Y+aNIQgmf1/dETESRTSAgSoOo3gNp6jdRCYqSuWIBsppibWT
 26+tyz0/FGhE50QviHzg0Sv+jd/g93fN6snNlV8wNFMviq3bC69Toa+y3qJ5e7UC
 /42R7nCWdkCZJfr6E67rOaahe9TDV/LXLqPErwptOkdK8sMchaIgF+deybgTtTi/
 zGRBfjLvb5ocYBmPbeGX4mtXNRpyZ3o9I0QUyGUO4zMwFXmFwn0=
 =jpVr
 -----END PGP SIGNATURE-----
gpgsig -----BEGIN PGP SIGNATURE-----
 
 iJUEABMJAB0WIQTkHFbLp4ejekA/qfgnX84Zoj2+dgUCaD7zuQAKCRAnX84Zoj2+
 dv21AX4qAXMoS1eQQOzx5/MN0LhibwHO8lq0HgyhKKCMZTUvFP91hvuB6qKGzxEU
 +RJmN5cBgPGNuXwr9zLe5A/Lv1LWgfSj1DaAlauYvduFh1xyLOLuo0H3xfTsKrcl
 Onjxi5QVsg==
 =bMa5
 -----END PGP SIGNATURE-----

Merge drm-next-2025-05-28 into drm-misc-next

Christian needs a recent drm-next branch to merge fence patches.

Signed-off-by: Maxime Ripard <mripard@kernel.org>
2025-06-03 15:07:39 +02:00

482 lines
12 KiB
C

/*
* Copyright 2012 Red Hat Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*
* Authors: Ben Skeggs
*/
#include "chan.h"
#include "chid.h"
#include "cgrp.h"
#include "runl.h"
#include "priv.h"
#include <core/ramht.h>
#include <subdev/mmu.h>
#include <engine/dma.h>
#include <nvif/if0020.h>
const struct nvkm_event_func
nvkm_chan_event = {
};
void
nvkm_chan_cctx_bind(struct nvkm_chan *chan, struct nvkm_engn *engn, struct nvkm_cctx *cctx)
{
struct nvkm_cgrp *cgrp = chan->cgrp;
struct nvkm_runl *runl = cgrp->runl;
struct nvkm_engine *engine = engn->engine;
if (!engn->func->bind)
return;
CHAN_TRACE(chan, "%sbind cctx %d[%s]", cctx ? "" : "un", engn->id, engine->subdev.name);
/* Prevent any channel in channel group from being rescheduled, kick them
* off host and any engine(s) they're loaded on.
*/
if (cgrp->hw)
nvkm_runl_block(runl);
else
nvkm_chan_block(chan);
nvkm_chan_preempt(chan, true);
/* Update context pointer. */
engn->func->bind(engn, cctx, chan);
/* Resume normal operation. */
if (cgrp->hw)
nvkm_runl_allow(runl);
else
nvkm_chan_allow(chan);
}
void
nvkm_chan_cctx_put(struct nvkm_chan *chan, struct nvkm_cctx **pcctx)
{
struct nvkm_cctx *cctx = *pcctx;
if (cctx) {
struct nvkm_engn *engn = cctx->vctx->ectx->engn;
if (refcount_dec_and_mutex_lock(&cctx->refs, &chan->cgrp->mutex)) {
CHAN_TRACE(chan, "dtor cctx %d[%s]", engn->id, engn->engine->subdev.name);
nvkm_cgrp_vctx_put(chan->cgrp, &cctx->vctx);
list_del(&cctx->head);
kfree(cctx);
mutex_unlock(&chan->cgrp->mutex);
}
*pcctx = NULL;
}
}
int
nvkm_chan_cctx_get(struct nvkm_chan *chan, struct nvkm_engn *engn, struct nvkm_cctx **pcctx,
struct nvkm_client *client)
{
struct nvkm_cgrp *cgrp = chan->cgrp;
struct nvkm_vctx *vctx;
struct nvkm_cctx *cctx;
int ret;
/* Look for an existing channel context for this engine+VEID. */
mutex_lock(&cgrp->mutex);
cctx = nvkm_list_find(cctx, &chan->cctxs, head,
cctx->vctx->ectx->engn == engn && cctx->vctx->vmm == chan->vmm);
if (cctx) {
refcount_inc(&cctx->refs);
*pcctx = cctx;
mutex_unlock(&cgrp->mutex);
return 0;
}
/* Nope - create a fresh one. But, sub-context first. */
ret = nvkm_cgrp_vctx_get(cgrp, engn, chan, &vctx, client);
if (ret) {
CHAN_ERROR(chan, "vctx %d[%s]: %d", engn->id, engn->engine->subdev.name, ret);
goto done;
}
/* Now, create the channel context - to track engine binding. */
CHAN_TRACE(chan, "ctor cctx %d[%s]", engn->id, engn->engine->subdev.name);
if (!(cctx = *pcctx = kzalloc(sizeof(*cctx), GFP_KERNEL))) {
nvkm_cgrp_vctx_put(cgrp, &vctx);
ret = -ENOMEM;
goto done;
}
cctx->vctx = vctx;
refcount_set(&cctx->refs, 1);
refcount_set(&cctx->uses, 0);
list_add_tail(&cctx->head, &chan->cctxs);
done:
mutex_unlock(&cgrp->mutex);
return ret;
}
int
nvkm_chan_preempt_locked(struct nvkm_chan *chan, bool wait)
{
struct nvkm_runl *runl = chan->cgrp->runl;
CHAN_TRACE(chan, "preempt");
chan->func->preempt(chan);
if (!wait)
return 0;
return nvkm_runl_preempt_wait(runl);
}
int
nvkm_chan_preempt(struct nvkm_chan *chan, bool wait)
{
int ret;
if (!chan->func->preempt)
return 0;
mutex_lock(&chan->cgrp->runl->mutex);
ret = nvkm_chan_preempt_locked(chan, wait);
mutex_unlock(&chan->cgrp->runl->mutex);
return ret;
}
void
nvkm_chan_remove_locked(struct nvkm_chan *chan)
{
struct nvkm_cgrp *cgrp = chan->cgrp;
struct nvkm_runl *runl = cgrp->runl;
if (list_empty(&chan->head))
return;
CHAN_TRACE(chan, "remove");
if (!--cgrp->chan_nr) {
runl->cgrp_nr--;
list_del(&cgrp->head);
}
runl->chan_nr--;
list_del_init(&chan->head);
atomic_set(&runl->changed, 1);
}
void
nvkm_chan_remove(struct nvkm_chan *chan, bool preempt)
{
struct nvkm_runl *runl = chan->cgrp->runl;
mutex_lock(&runl->mutex);
if (preempt && chan->func->preempt)
nvkm_chan_preempt_locked(chan, true);
nvkm_chan_remove_locked(chan);
nvkm_runl_update_locked(runl, true);
mutex_unlock(&runl->mutex);
}
void
nvkm_chan_insert(struct nvkm_chan *chan)
{
struct nvkm_cgrp *cgrp = chan->cgrp;
struct nvkm_runl *runl = cgrp->runl;
mutex_lock(&runl->mutex);
if (WARN_ON(!list_empty(&chan->head))) {
mutex_unlock(&runl->mutex);
return;
}
CHAN_TRACE(chan, "insert");
list_add_tail(&chan->head, &cgrp->chans);
runl->chan_nr++;
if (!cgrp->chan_nr++) {
list_add_tail(&cgrp->head, &cgrp->runl->cgrps);
runl->cgrp_nr++;
}
atomic_set(&runl->changed, 1);
nvkm_runl_update_locked(runl, true);
mutex_unlock(&runl->mutex);
}
static void
nvkm_chan_block_locked(struct nvkm_chan *chan)
{
CHAN_TRACE(chan, "block %d", atomic_read(&chan->blocked));
if (atomic_inc_return(&chan->blocked) == 1)
chan->func->stop(chan);
}
void
nvkm_chan_error(struct nvkm_chan *chan, bool preempt)
{
unsigned long flags;
spin_lock_irqsave(&chan->lock, flags);
if (atomic_inc_return(&chan->errored) == 1) {
CHAN_ERROR(chan, "errored - disabling channel");
nvkm_chan_block_locked(chan);
if (preempt)
chan->func->preempt(chan);
nvkm_event_ntfy(&chan->cgrp->runl->chid->event, chan->id, NVKM_CHAN_EVENT_ERRORED);
}
spin_unlock_irqrestore(&chan->lock, flags);
}
void
nvkm_chan_block(struct nvkm_chan *chan)
{
spin_lock_irq(&chan->lock);
nvkm_chan_block_locked(chan);
spin_unlock_irq(&chan->lock);
}
void
nvkm_chan_allow(struct nvkm_chan *chan)
{
spin_lock_irq(&chan->lock);
CHAN_TRACE(chan, "allow %d", atomic_read(&chan->blocked));
if (atomic_dec_and_test(&chan->blocked))
chan->func->start(chan);
spin_unlock_irq(&chan->lock);
}
void
nvkm_chan_del(struct nvkm_chan **pchan)
{
struct nvkm_chan *chan = *pchan;
if (!chan)
return;
if (chan->func->ramfc->clear)
chan->func->ramfc->clear(chan);
nvkm_ramht_del(&chan->ramht);
nvkm_gpuobj_del(&chan->pgd);
nvkm_gpuobj_del(&chan->eng);
nvkm_gpuobj_del(&chan->cache);
nvkm_gpuobj_del(&chan->ramfc);
if (chan->cgrp) {
nvkm_chid_put(chan->cgrp->runl->chid, chan->id, &chan->cgrp->lock);
nvkm_cgrp_unref(&chan->cgrp);
}
nvkm_memory_unref(&chan->userd.mem);
if (chan->vmm) {
nvkm_vmm_part(chan->vmm, chan->inst->memory);
nvkm_vmm_unref(&chan->vmm);
}
nvkm_gpuobj_del(&chan->push);
nvkm_gpuobj_del(&chan->inst);
kfree(chan);
}
void
nvkm_chan_put(struct nvkm_chan **pchan, unsigned long irqflags)
{
struct nvkm_chan *chan = *pchan;
if (!chan)
return;
*pchan = NULL;
spin_unlock_irqrestore(&chan->cgrp->lock, irqflags);
}
struct nvkm_chan *
nvkm_chan_get_inst(struct nvkm_engine *engine, u64 inst, unsigned long *pirqflags)
{
struct nvkm_fifo *fifo = engine->subdev.device->fifo;
struct nvkm_runl *runl;
struct nvkm_engn *engn;
struct nvkm_chan *chan;
nvkm_runl_foreach(runl, fifo) {
nvkm_runl_foreach_engn(engn, runl) {
if (engine == &fifo->engine || engn->engine == engine) {
chan = nvkm_runl_chan_get_inst(runl, inst, pirqflags);
if (chan || engn->engine == engine)
return chan;
}
}
}
return NULL;
}
struct nvkm_chan *
nvkm_chan_get_chid(struct nvkm_engine *engine, int id, unsigned long *pirqflags)
{
struct nvkm_fifo *fifo = engine->subdev.device->fifo;
struct nvkm_runl *runl;
struct nvkm_engn *engn;
nvkm_runl_foreach(runl, fifo) {
nvkm_runl_foreach_engn(engn, runl) {
if (fifo->chid || engn->engine == engine)
return nvkm_runl_chan_get_chid(runl, id, pirqflags);
}
}
return NULL;
}
int
nvkm_chan_new_(const struct nvkm_chan_func *func, struct nvkm_runl *runl, int runq,
struct nvkm_cgrp *cgrp, const char *name, bool priv, u32 devm, struct nvkm_vmm *vmm,
struct nvkm_dmaobj *dmaobj, u64 offset, u64 length,
struct nvkm_memory *userd, u64 ouserd, struct nvkm_chan **pchan)
{
struct nvkm_fifo *fifo = runl->fifo;
struct nvkm_device *device = fifo->engine.subdev.device;
struct nvkm_chan *chan;
int ret;
/* Validate arguments against class requirements. */
if ((runq && runq >= runl->func->runqs) ||
(!func->inst->vmm != !vmm) ||
(!func->userd->bar == !userd) ||
(!func->ramfc->ctxdma != !dmaobj) ||
((func->ramfc->devm < devm) && devm != BIT(0)) ||
(!func->ramfc->priv && priv)) {
RUNL_DEBUG(runl, "args runq:%d:%d vmm:%d:%p userd:%d:%p "
"push:%d:%p devm:%08x:%08x priv:%d:%d",
runl->func->runqs, runq, func->inst->vmm, vmm,
func->userd->bar, userd, func->ramfc->ctxdma, dmaobj,
func->ramfc->devm, devm, func->ramfc->priv, priv);
return -EINVAL;
}
if (!(chan = *pchan = kzalloc(sizeof(*chan), GFP_KERNEL)))
return -ENOMEM;
chan->func = func;
strscpy(chan->name, name, sizeof(chan->name));
chan->runq = runq;
chan->id = -1;
spin_lock_init(&chan->lock);
atomic_set(&chan->blocked, 1);
atomic_set(&chan->errored, 0);
INIT_LIST_HEAD(&chan->cctxs);
INIT_LIST_HEAD(&chan->head);
/* Join channel group.
*
* GK110 and newer support channel groups (aka TSGs), where individual channels
* share a timeslice, and, engine context(s).
*
* As such, engine contexts are tracked in nvkm_cgrp and we need them even when
* channels aren't in an API channel group, and on HW that doesn't support TSGs.
*/
if (!cgrp) {
ret = nvkm_cgrp_new(runl, chan->name, vmm, fifo->func->cgrp.force, &chan->cgrp);
if (ret) {
RUNL_DEBUG(runl, "cgrp %d", ret);
return ret;
}
cgrp = chan->cgrp;
} else {
if (cgrp->runl != runl || cgrp->vmm != vmm) {
RUNL_DEBUG(runl, "cgrp %d %d", cgrp->runl != runl, cgrp->vmm != vmm);
return -EINVAL;
}
chan->cgrp = nvkm_cgrp_ref(cgrp);
}
/* Allocate instance block. */
ret = nvkm_gpuobj_new(device, func->inst->size, 0x1000, func->inst->zero, NULL,
&chan->inst);
if (ret) {
RUNL_DEBUG(runl, "inst %d", ret);
return ret;
}
/* Initialise virtual address-space. */
if (func->inst->vmm) {
if (WARN_ON(vmm->mmu != device->mmu))
return -EINVAL;
ret = nvkm_vmm_join(vmm, chan->inst->memory);
if (ret) {
RUNL_DEBUG(runl, "vmm %d", ret);
return ret;
}
chan->vmm = nvkm_vmm_ref(vmm);
}
/* Allocate HW ctxdma for push buffer. */
if (func->ramfc->ctxdma) {
ret = nvkm_object_bind(&dmaobj->object, chan->inst, -16, &chan->push);
if (ret) {
RUNL_DEBUG(runl, "bind %d", ret);
return ret;
}
}
/* Allocate channel ID. */
chan->id = nvkm_chid_get(runl->chid, chan);
if (chan->id >= 0) {
if (!func->userd->bar) {
if (ouserd + chan->func->userd->size >=
nvkm_memory_size(userd)) {
RUNL_DEBUG(runl, "ouserd %llx", ouserd);
return -EINVAL;
}
ret = nvkm_memory_kmap(userd, &chan->userd.mem);
if (ret) {
RUNL_DEBUG(runl, "userd %d", ret);
return ret;
}
chan->userd.base = ouserd;
} else {
chan->userd.mem = nvkm_memory_ref(fifo->userd.mem);
chan->userd.base = chan->id * chan->func->userd->size;
}
}
if (chan->id < 0) {
RUNL_ERROR(runl, "!chids");
return -ENOSPC;
}
if (cgrp->id < 0)
cgrp->id = chan->id;
/* Initialise USERD. */
if (chan->func->userd->clear)
chan->func->userd->clear(chan);
/* Initialise RAMFC. */
ret = chan->func->ramfc->write(chan, offset, length, devm, priv);
if (ret) {
RUNL_DEBUG(runl, "ramfc %d", ret);
return ret;
}
return 0;
}