drm fixes for 7.3-rc2

core:
 - Fix drm_crtc_commit leak when PAGE_FLIP_EVENT is used,
 
 dma-buf:
 - Publish the dma-buf only after copy_to_user succeeds
 - fix some kernel-doc warnings
 
 atomic-state-helpers:
 - set pixel_blend_mode to prop default on reset
 
 sysfb:
 - Fix integer overflow
 - fix constant comparison bug
 
 pagemap:
 - Prevent double migration of device pages
 - Reset migration page count on eviction retry
 - dma-unmap pages before handling migration errors
 - use after free fixes
 
 prime:
 - fix prime exports tracing
 
 amdgpu:
  - Fix for drm_amdgpu_info_device with mixed 64 bit kernel and 32 bit userspace
 - plane blend mode fixes
 - SR-IOV fix
 - GFX8 fix
 - MES queue reset fix
 - GPUVM fixes
 - DCN 6 warning fix
 - DCN 3.5/3.6 fix
 - DML fix
 - Backlight fix
 - Colorop fix
 - DC get_estimated_bw() fix
 - devcoredump fix
 - Userq fixes
 - APU PSP fix
 - Cursor fix
 
 amdkfd:
 - MES queue eviction fix
 - MQD debugfs fix
 
 xe:
 - oa uapi error handling fix
 - drm info message to report FLAT_CSS base misalignment.
 
 i915:
 - Drop an accidentally duplicated panel fitter call in DP MST
 - Fix DDI clock programming for Cx0 and LT PHY
 - Fix PTL CDCLK handling at probe, causing a glitch
 - Fix dg2_power_well_count() return type
 - Fix a NULL pointer deref at forced probe
 - Fix selective fetch disable
 
 amdxdna:
 - out-of-bounds access fix
 - reject commands chains with no commands
 - handle chained mapping BO failures
 - refuse to flush an imported BO
 
 ethosu:
 - handle mmio mapping failures
 - handle storage modes only on hardware that supports it
 - fix job completion fence cleanup
 
 fastrpc:
 - Publish the dma-buf only after copy_to_user succeeds
 
 gud:
 - Improve TV modes and rotation handling
 
 nouveau:
 - use-after-free fixes
 - add missing scanline position support
 - HDMI and DP fixes
 - null pointer dereference fix
 - dmem accounting fixes for large folios
 - use write-combined maps for coherent
 
 qaic:
 - out-of-bounds access fix
 
 tegra:
 - Add blend mode properties
 
 virtio:
 - exit path and error handling fixes
 -----BEGIN PGP SIGNATURE-----
 
 iQIzBAABCgAdFiEEEKbZHaGwW9KfbeusDHTzWXnEhr4FAmqbH88ACgkQDHTzWXnE
 hr7stA//SAJOADL8CuoBzSyAX7zoqhVErYk798+4r5tPFJ5CzjkZVxGRGur7XWbm
 atezEEKaMTEz2BDVb1JDNRI1X1Yq9GtfWM860hUXmYHCegeO49B9lDnS1v0HZweD
 PQvPhCqvJpOOF6D8sjYFjVNvi0OrY0JVRlzsnMNmTIlw0xbg01lqn/oWgRA7qdvq
 Zo1k1yEPlK3jV2YuX674n7NoioFqeiSWBo9PzIX+yghagg2LrrS1Plx4RFA/B6bm
 Czt/x7WPE7lvoZqyDGBwlAY/dta3bagCFkGoDwb2Q1B3MjYXKcKBS0aGL9PyOgu/
 /9dNqvR4aDu9CXvNwb3kNbqjJL7DdFBCzwm78PNc43TizkR4WXCBXCNxJOf93e6h
 Bwx0GamXQJeGI6xNvQpEssUxezuS3wdoNZ0Rbk3nxMXlvf7OB/sgwkNYVCNKkk/V
 dSMOr1XB9pBGmtuWFPOf1kq/is4P4Ns/m8Rutfp5SBJNU9Air5ECblRNNkqFuOtS
 582QAM+7xp6zIbepWALu8TTNQMsNKlDwiNc3JOH3Ks3wZ0wExXMTIaSJ7NHOxYj/
 B9gWEN+g1LreFHDaCzR1xRetO1bIHNGMNhuqChXj8K8vuDoVPP8FAvKzKEy/iyIl
 CRQXLPSV8LjqKL5d5YW3QFvwUSwJwGr3fXCWd1Voo26dL4DOEx0=
 =vHdE
 -----END PGP SIGNATURE-----

Merge tag 'drm-fixes-2026-09-05' of https://gitlab.freedesktop.org/drm/kernel

Pull drm fixes from Dave Airlie:
 "Lots of scattered fixes: nouveau has a bunch of display fixes for
  blackwell GPUs that should mean we light up monitors properly and fix
  some desktop rendering problems, amdgpu and intel display changes as
  usual.

  There also changes to the core pagemap, then the usual amouny of AI
  inspired validation fixes.

  core:
   - Fix drm_crtc_commit leak when PAGE_FLIP_EVENT is used

  dma-buf:
   - Publish the dma-buf only after copy_to_user succeeds
   - fix some kernel-doc warnings

  atomic-state-helpers:
   - set pixel_blend_mode to prop default on reset

  sysfb:
   - Fix integer overflow
   - fix constant comparison bug

  pagemap:
   - Prevent double migration of device pages
   - Reset migration page count on eviction retry
   - dma-unmap pages before handling migration errors
   - use after free fixes

  prime:
   - fix prime exports tracing

  amdgpu:
   - Fix for drm_amdgpu_info_device with mixed 64 bit kernel and 32 bit
     userspace
   - plane blend mode fixes
   - SR-IOV fix
   - GFX8 fix
   - MES queue reset fix
   - GPUVM fixes
   - DCN 6 warning fix
   - DCN 3.5/3.6 fix
   - DML fix
   - Backlight fix
   - Colorop fix
   - DC get_estimated_bw() fix
   - devcoredump fix
   - Userq fixes
   - APU PSP fix
   - Cursor fix

  amdkfd:
   - MES queue eviction fix
   - MQD debugfs fix

  xe:
   - oa uapi error handling fix
   - drm info message to report FLAT_CSS base misalignment

  i915:
   - Drop an accidentally duplicated panel fitter call in DP MST
   - Fix DDI clock programming for Cx0 and LT PHY
   - Fix PTL CDCLK handling at probe, causing a glitch
   - Fix dg2_power_well_count() return type
   - Fix a NULL pointer deref at forced probe
   - Fix selective fetch disable

  amdxdna:
   - out-of-bounds access fix
   - reject commands chains with no commands
   - handle chained mapping BO failures
   - refuse to flush an imported BO

  ethosu:
   - handle mmio mapping failures
   - handle storage modes only on hardware that supports it
   - fix job completion fence cleanup

  fastrpc:
   - Publish the dma-buf only after copy_to_user succeeds

  gud:
   - Improve TV modes and rotation handling

  nouveau:
   - use-after-free fixes
   - add missing scanline position support
   - HDMI and DP fixes
   - null pointer dereference fix
   - dmem accounting fixes for large folios
   - use write-combined maps for coherent

  qaic:
   - out-of-bounds access fix

  tegra:
   - Add blend mode properties

  virtio:
   - exit path and error handling fixes

* tag 'drm-fixes-2026-09-05' of https://gitlab.freedesktop.org/drm/kernel: (83 commits)
  drm/xe/vram: report FLAT_CCS base misalignment
  MAINTAINERS, mailmap: use Aditya Garg's linux.dev account
  drm/amd/display: use plane color_mgmt_changed to track colorop changes
  drm/amdgpu/userq: fix struct drm_amdgpu_info_device padding for 32bit compile
  drm/amd/display: Fix cursor disable with horizontally split planes
  drm/amdgpu/userq: dont overwrite the error of subsequent map call
  drm/amdgpu: Skip accessing psp rum time db for APUs
  drm/amdgpu: update the fw version for gfx12 userqueues
  drm/amdgpu: update the fw version for gfx11 userqueues
  drm/amdgpu: fix byte/dword unit mismatch in coredump IB dump
  drm/amdkfd: fix scope of mqd_mgr dereference in pqm_debugfs_mqds
  drm/amd/display: fix division by zero in get_estimated_bw()
  drm/amd/display: use halving distribution for all encode-to-linear curves
  drm/amd/display: Fix backlight control for luminance-capable OLED
  drm/amd/display: Remove const Qualifier From Non-Pointer Fields
  drm/amd/display: Set gpuvm min page size to 4K on dcn35/36
  drm/amd/display: Fix DCN5/6 DML2 compilation warnings
  drm/amdgpu: fix Idle BOs list in VM debugfs status info
  drm/amdgpu: use AMDGPU_GPU_PAGE_SHIFT instead of PAGE_SHIFT
  drm/amdgpu: Update queue reset support version
  ...
This commit is contained in:
Linus Torvalds 2026-09-04 13:42:16 -07:00
commit 654ae5d73c
85 changed files with 1210 additions and 317 deletions

View File

@ -19,7 +19,8 @@ Abhinav Kumar <quic_abhinavk@quicinc.com> <abhinavk@codeaurora.org>
Ahmad Masri <quic_amasri@quicinc.com> <amasri@codeaurora.org>
Adam Oldham <oldhamca@gmail.com>
Adam Radford <aradford@gmail.com>
Aditya Garg <gargaditya08@proton.me> <gargaditya08@live.com>
Aditya Garg <aditya.garg@linux.dev> <gargaditya08@live.com>
Aditya Garg <aditya.garg@linux.dev> <gargaditya08@proton.me>
Adriana Reus <adi.reus@gmail.com> <adriana.reus@intel.com>
Adrian Bunk <bunk@stusta.de>
Ajay Kaher <ajay.kaher@broadcom.com> <akaher@vmware.com>

View File

@ -8039,7 +8039,7 @@ F: drivers/gpu/drm/sun4i/sun8i*
DRM DRIVER FOR APPLE TOUCH BARS
M: Aun-Ali Zaidi <admin@kodeit.net>
M: Aditya Garg <gargaditya08@proton.me>
M: Aditya Garg <aditya.garg@linux.dev>
L: dri-devel@lists.freedesktop.org
S: Maintained
T: git https://gitlab.freedesktop.org/drm/misc/kernel.git

View File

@ -994,7 +994,7 @@ int aie2_cmdlist_multi_execbuf(struct amdxdna_hwctx *hwctx,
}
ccnt = payload->command_count;
if (payload_len < struct_size(payload, data, ccnt)) {
if (!ccnt || payload_len < struct_size(payload, data, ccnt)) {
XDNA_DBG(xdna, "Invalid command count %d", ccnt);
return -EINVAL;
}

View File

@ -183,8 +183,10 @@ int amdxdna_cmd_set_error(struct amdxdna_gem_obj *abo,
if (!abo)
return -EINVAL;
cmd = amdxdna_gem_vmap(abo);
if (!cmd)
if (!cmd) {
amdxdna_gem_put_obj(abo);
return -ENOMEM;
}
}
memset(cmd->data, 0xff, abo->mem.size - sizeof(*cmd));

View File

@ -55,7 +55,7 @@ struct amdxdna_cmd_chain {
u32 submit_index;
u32 error_index;
u32 reserved[3];
u64 data[] __counted_by(command_count);
u64 data[];
};
/*

View File

@ -1246,6 +1246,9 @@ static int amdxdna_flush_bo(struct amdxdna_gem_obj *abo, u64 offset, u64 size)
{
u64 end;
if (is_import_bo(abo))
return -EOPNOTSUPP;
if (offset >= abo->mem.size)
return -EINVAL;
@ -1253,9 +1256,10 @@ static int amdxdna_flush_bo(struct amdxdna_gem_obj *abo, u64 offset, u64 size)
return -EINVAL;
size = min(abo->mem.size, end) - offset;
if (is_import_bo(abo))
drm_clflush_sg(abo->base.sgt);
else if (amdxdna_gem_vmap(abo))
if (!size)
return 0;
if (amdxdna_gem_vmap(abo))
drm_clflush_virt_range(amdxdna_gem_vmap(abo) + offset, size);
else if (abo->base.pages)
drm_clflush_pages(abo->base.pages, abo->mem.size >> PAGE_SHIFT);

View File

@ -356,6 +356,8 @@ static int ethosu_probe(struct platform_device *pdev)
dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(40));
ethosudev->regs = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(ethosudev->regs))
return PTR_ERR(ethosudev->regs);
ethosudev->pmu_regs = ethosudev->regs;
ethosudev->num_clks = devm_clk_bulk_get_all(&pdev->dev, &ethosudev->clks);

View File

@ -204,7 +204,7 @@ static u64 feat_matrix_length(struct ethosu_device *edev,
struct feat_matrix *fm,
u32 x, u32 y, u32 c, bool ofm)
{
u32 element_size, storage = fm->precision >> 14;
u32 element_size, storage = ethosu_is_u65(edev) ? 0 : fm->precision >> 14;
int tile = 0;
u64 addr;

View File

@ -154,6 +154,13 @@ static void ethosu_job_err_cleanup(struct ethosu_job *job)
drm_gem_object_put(job->cmd_bo);
if (job->done_fence) {
if (dma_fence_was_initialized(job->done_fence))
dma_fence_put(job->done_fence);
else
dma_fence_free(job->done_fence);
}
kfree(job);
}
@ -164,7 +171,6 @@ static void ethosu_job_cleanup(struct kref *ref)
pm_runtime_put_autosuspend(job->dev->base.dev);
dma_fence_put(job->done_fence);
dma_fence_put(job->inference_done_fence);
ethosu_job_err_cleanup(job);
@ -415,7 +421,7 @@ static int ethosu_ioctl_submit_job(struct drm_device *dev, struct drm_file *file
ejob->done_fence = kzalloc_obj(*ejob->done_fence);
if (!ejob->done_fence) {
ret = -ENOMEM;
goto out_cleanup_job;
goto out_put_job;
}
ret = drm_sched_job_init(&ejob->base,

View File

@ -963,11 +963,13 @@ static int decode_status(struct qaic_device *qdev, void *trans, struct manage_ms
static int decode_message(struct qaic_device *qdev, struct manage_msg *user_msg,
struct wire_msg *msg, struct ioctl_resources *resources,
struct qaic_user *usr)
struct qaic_user *usr, bool orphaned_deactivate)
{
u32 msg_hdr_count = le32_to_cpu(msg->hdr.count);
u32 msg_hdr_len = le32_to_cpu(msg->hdr.len);
struct wire_trans_hdr *trans_hdr;
u32 msg_len = 0;
int trans_type;
int ret;
int i;
@ -975,10 +977,12 @@ static int decode_message(struct qaic_device *qdev, struct manage_msg *user_msg,
msg_hdr_len > QAIC_MANAGE_MAX_MSG_LENGTH)
return -EINVAL;
user_msg->len = 0;
user_msg->count = le32_to_cpu(msg->hdr.count);
if (user_msg) {
user_msg->len = 0;
user_msg->count = msg_hdr_count;
}
for (i = 0; i < user_msg->count; ++i) {
for (i = 0; i < msg_hdr_count; ++i) {
u32 hdr_len;
if (msg_len > msg_hdr_len - sizeof(*trans_hdr))
@ -990,7 +994,20 @@ static int decode_message(struct qaic_device *qdev, struct manage_msg *user_msg,
size_add(msg_len, hdr_len) > msg_hdr_len)
return -EINVAL;
switch (le32_to_cpu(trans_hdr->type)) {
trans_type = le32_to_cpu(trans_hdr->type);
/*
* orphaned_deactivate is the case where a deactivate response
* is received from the device after the user owning the DBC,
* and the message requesting deactivation, has gone away.
* In this case, only process QAIC_TRANS_DEACTIVATE_FROM_DEV
* transaction and skip the others.
*/
if (orphaned_deactivate && trans_type != QAIC_TRANS_DEACTIVATE_FROM_DEV) {
msg_len += hdr_len;
continue;
}
switch (trans_type) {
case QAIC_TRANS_PASSTHROUGH_FROM_DEV:
ret = decode_passthrough(qdev, trans_hdr, user_msg, &msg_len);
break;
@ -1281,7 +1298,7 @@ static int qaic_manage(struct qaic_device *qdev, struct qaic_user *usr, struct m
goto dma_cont_failed;
}
ret = decode_message(qdev, user_msg, rsp, &resources, usr);
ret = decode_message(qdev, user_msg, rsp, &resources, usr, false);
dma_cont_failed:
free_dbc_buf(qdev, &resources);
@ -1446,22 +1463,7 @@ static void resp_worker(struct work_struct *work)
* response to the QAIC_TRANS_TERMINATE_TO_DEV transaction,
* otherwise, the user can issue an soc_reset to the device.
*/
u32 msg_count = le32_to_cpu(msg->hdr.count);
u32 msg_len = le32_to_cpu(msg->hdr.len);
u32 len = 0;
int j;
for (j = 0; j < msg_count && len < msg_len; ++j) {
struct wire_trans_hdr *trans_hdr;
trans_hdr = (struct wire_trans_hdr *)(msg->data + len);
if (le32_to_cpu(trans_hdr->type) == QAIC_TRANS_DEACTIVATE_FROM_DEV) {
if (decode_deactivate(qdev, trans_hdr, &len, NULL))
len += le32_to_cpu(trans_hdr->len);
} else {
len += le32_to_cpu(trans_hdr->len);
}
}
decode_message(qdev, NULL, msg, NULL, NULL, true);
/* request must have timed out, drop packet */
kfree(msg);
}

View File

@ -803,6 +803,26 @@ int dma_buf_fd(struct dma_buf *dmabuf, int flags)
}
EXPORT_SYMBOL_NS_GPL(dma_buf_fd, "DMA_BUF");
/**
* dma_buf_fd_install - install a reserved fd for a dma-buf
* @dmabuf: [in] pointer to dma_buf
* @fd: [in] fd reserved with get_unused_fd_flags()
*
* Publishes a previously reserved fd into the caller's fd table.
* Must only be called after all fallible work (e.g. copy_to_user)
* has succeeded, as it cannot be undone safely once called.
*
* The caller is responsible for having emitted the trace event
* (via dma_buf_fd() or get_unused_fd_flags() + this function)
* before calling this.
*/
void dma_buf_fd_install(struct dma_buf *dmabuf, int fd)
{
DMA_BUF_TRACE(trace_dma_buf_fd, dmabuf, fd);
fd_install(fd, dmabuf->file);
}
EXPORT_SYMBOL_NS_GPL(dma_buf_fd_install, "DMA_BUF");
/**
* dma_buf_get - returns the struct dma_buf related to an fd
* @fd: [in] fd associated with the struct dma_buf to be returned

View File

@ -56,33 +56,6 @@ MODULE_PARM_DESC(mem_accounting,
"Enable cgroup-based memory accounting for dma-buf heap allocations (default=false).");
EXPORT_SYMBOL_NS_GPL(mem_accounting, "DMA_BUF_HEAP");
static int dma_heap_buffer_alloc(struct dma_heap *heap, size_t len,
u32 fd_flags,
u64 heap_flags)
{
struct dma_buf *dmabuf;
int fd;
/*
* Allocations from all heaps have to begin
* and end on page boundaries.
*/
len = PAGE_ALIGN(len);
if (!len)
return -EINVAL;
dmabuf = heap->ops->allocate(heap, len, fd_flags, heap_flags);
if (IS_ERR(dmabuf))
return PTR_ERR(dmabuf);
fd = dma_buf_fd(dmabuf, fd_flags);
if (fd < 0) {
dma_buf_put(dmabuf);
/* just return, as put will call release and that will free */
}
return fd;
}
static int dma_heap_open(struct inode *inode, struct file *file)
{
struct dma_heap *heap;
@ -100,30 +73,42 @@ static int dma_heap_open(struct inode *inode, struct file *file)
return 0;
}
static long dma_heap_ioctl_allocate(struct file *file, void *data)
static struct dma_buf *dma_heap_ioctl_allocate(struct file *file, void *data)
{
struct dma_heap_allocation_data *heap_allocation = data;
struct dma_heap *heap = file->private_data;
struct dma_buf *dmabuf;
int fd;
size_t len;
if (heap_allocation->fd)
return -EINVAL;
return ERR_PTR(-EINVAL);
if (heap_allocation->fd_flags & ~DMA_HEAP_VALID_FD_FLAGS)
return -EINVAL;
return ERR_PTR(-EINVAL);
if (heap_allocation->heap_flags & ~DMA_HEAP_VALID_HEAP_FLAGS)
return -EINVAL;
return ERR_PTR(-EINVAL);
fd = dma_heap_buffer_alloc(heap, heap_allocation->len,
heap_allocation->fd_flags,
heap_allocation->heap_flags);
if (fd < 0)
return fd;
len = PAGE_ALIGN(heap_allocation->len);
if (!len)
return ERR_PTR(-EINVAL);
dmabuf = heap->ops->allocate(heap, len, heap_allocation->fd_flags,
heap_allocation->heap_flags);
if (IS_ERR(dmabuf))
return dmabuf;
fd = get_unused_fd_flags(heap_allocation->fd_flags);
if (fd < 0) {
dma_buf_put(dmabuf);
return ERR_PTR(fd);
}
heap_allocation->fd = fd;
return 0;
return dmabuf;
}
static unsigned int dma_heap_ioctl_cmds[] = {
@ -139,6 +124,8 @@ static long dma_heap_ioctl(struct file *file, unsigned int ucmd,
unsigned int in_size, out_size, drv_size, ksize;
int nr = _IOC_NR(ucmd);
int ret = 0;
int fd;
struct dma_buf *dmabuf;
if (nr >= ARRAY_SIZE(dma_heap_ioctl_cmds))
return -EINVAL;
@ -175,15 +162,28 @@ static long dma_heap_ioctl(struct file *file, unsigned int ucmd,
switch (kcmd) {
case DMA_HEAP_IOCTL_ALLOC:
ret = dma_heap_ioctl_allocate(file, kdata);
dmabuf = dma_heap_ioctl_allocate(file, kdata);
if (IS_ERR(dmabuf)) {
ret = PTR_ERR(dmabuf);
break;
}
fd = ((struct dma_heap_allocation_data *)kdata)->fd;
if (copy_to_user((void __user *)arg, kdata, out_size) != 0) {
put_unused_fd(fd);
dma_buf_put(dmabuf);
ret = -EFAULT;
} else {
dma_buf_fd_install(dmabuf, fd);
}
break;
default:
ret = -ENOTTY;
goto err;
}
if (copy_to_user((void __user *)arg, kdata, out_size) != 0)
ret = -EFAULT;
err:
if (kdata != stack_kdata)
kfree(kdata);

View File

@ -299,10 +299,10 @@ amdgpu_devcoredump_print_ibs(struct drm_printer *p,
amdgpu_res_first(abo->tbo.resource, offset,
coredump->ibs[i].ib_size_dw * 4, &cursor);
while (cursor.remaining) {
amdgpu_device_mm_access(adev, cursor.start / 4,
&ib_content[off], cursor.size / 4,
amdgpu_device_mm_access(adev, cursor.start,
&ib_content[off], cursor.size,
false);
off += cursor.size;
off += cursor.size / 4;
amdgpu_res_next(&cursor, cursor.size);
}
emit_content = true;

View File

@ -771,6 +771,9 @@ static int amdgpu_device_read_fb_via_bar0(struct amdgpu_device *adev,
if (!buf || !size)
return -EINVAL;
if (!amdgpu_sriov_vf(adev))
return -EINVAL;
flags = pci_resource_flags(adev->pdev, 0);
if ((flags & IORESOURCE_UNSET) || !(flags & IORESOURCE_MEM))
return -EINVAL;

View File

@ -879,7 +879,7 @@ bool amdgpu_mes_queue_reset_by_mes_supported(struct amdgpu_device *adev)
return (ip_maj == 11 && mes_sched >= 0x8c) ||
((ip_maj == 12 && ip_min == 0) && mes_sched >= 0x8d) ||
((ip_maj == 12 && ip_min == 1) && mes_sched >= 0x73);
((ip_maj == 12 && ip_min == 1) && mes_sched >= 0x7b);
}
/* Fix me -- node_id is used to identify the correct MES instances in the future */

View File

@ -396,6 +396,12 @@ static bool psp_get_runtime_db_entry(struct amdgpu_device *adev,
bool ret = false;
int i;
/*
* Runtime DB is for dGPUs only.
*/
if (adev->flags & AMD_IS_APU)
return false;
if (amdgpu_ip_version(adev, MP0_HWIP, 0) == IP_VERSION(13, 0, 6) ||
amdgpu_ip_version(adev, MP0_HWIP, 0) == IP_VERSION(13, 0, 12) ||
amdgpu_ip_version(adev, MP0_HWIP, 0) == IP_VERSION(13, 0, 14) ||

View File

@ -1544,7 +1544,7 @@ int amdgpu_userq_post_reset(struct amdgpu_device *adev, bool vram_lost)
struct amdgpu_usermode_queue *queue;
const struct amdgpu_userq_funcs *userq_funcs;
unsigned long queue_id;
int r = 0;
int ret = 0, r;
xa_for_each(&adev->userq_doorbell_xa, queue_id, queue) {
if (queue->state == AMDGPU_USERQ_STATE_HUNG && !vram_lost) {
@ -1555,6 +1555,7 @@ int amdgpu_userq_post_reset(struct amdgpu_device *adev, bool vram_lost)
r = userq_funcs->map(queue);
if (r) {
dev_err(adev->dev, "Failed to remap queue %ld\n", queue_id);
ret = r;
continue;
}
trace_amdgpu_userq_state_changed(queue, AMDGPU_USERQ_STATE_MAPPED);
@ -1562,5 +1563,5 @@ int amdgpu_userq_post_reset(struct amdgpu_device *adev, bool vram_lost)
}
}
return r;
return ret;
}

View File

@ -2090,7 +2090,7 @@ int amdgpu_vm_bo_clear_mappings(struct amdgpu_device *adev,
after->start = eaddr + 1;
after->last = tmp->last;
after->offset = tmp->offset;
after->offset += (after->start - tmp->start) << PAGE_SHIFT;
after->offset += (after->start - tmp->start) << AMDGPU_GPU_PAGE_SHIFT;
after->flags = tmp->flags;
after->bo_va = tmp->bo_va;
list_add(&after->list, &tmp->bo_va->invalids);
@ -3122,7 +3122,7 @@ static void amdgpu_debugfs_vm_bo_status_info(struct seq_file *m,
id = 0;
seq_puts(m, "\tIdle BOs:\n");
list_for_each_entry(base, &lists->needs_update, vm_status) {
list_for_each_entry(base, &lists->idle, vm_status) {
if (!base->bo)
continue;

View File

@ -1651,10 +1651,10 @@ static int gfx_v11_0_sw_init(struct amdgpu_ip_block *ip_block)
case IP_VERSION(11, 0, 2):
case IP_VERSION(11, 0, 3):
if (!adev->gfx.disable_uq &&
adev->gfx.me_fw_version >= 2420 &&
adev->gfx.pfp_fw_version >= 2580 &&
adev->gfx.mec_fw_version >= 2650 &&
adev->mes.fw_version[0] >= 120) {
adev->gfx.me_fw_version >= 3090 &&
adev->gfx.pfp_fw_version >= 3190 &&
adev->gfx.mec_fw_version >= 3450 &&
adev->mes.fw_version[0] >= 147) {
adev->userq_funcs[AMDGPU_HW_IP_GFX] = &userq_mes_funcs;
adev->userq_funcs[AMDGPU_HW_IP_COMPUTE] = &userq_mes_funcs;
}

View File

@ -1436,10 +1436,10 @@ static int gfx_v12_0_sw_init(struct amdgpu_ip_block *ip_block)
case IP_VERSION(12, 0, 0):
case IP_VERSION(12, 0, 1):
if (!adev->gfx.disable_uq &&
adev->gfx.me_fw_version >= 2780 &&
adev->gfx.pfp_fw_version >= 2840 &&
adev->gfx.mec_fw_version >= 3050 &&
adev->mes.fw_version[0] >= 123) {
adev->gfx.me_fw_version >= 3090 &&
adev->gfx.pfp_fw_version >= 3190 &&
adev->gfx.mec_fw_version >= 3450 &&
adev->mes.fw_version[0] >= 147) {
adev->userq_funcs[AMDGPU_HW_IP_GFX] = &userq_mes_funcs;
adev->userq_funcs[AMDGPU_HW_IP_COMPUTE] = &userq_mes_funcs;
}

View File

@ -4546,9 +4546,11 @@ static int gfx_v8_0_mqd_init(struct amdgpu_ring *ring)
/* set static priority for a queue/ring */
gfx_v8_0_mqd_set_priority(ring, mqd);
tmp = RREG32(mmCP_HQD_QUANTUM);
tmp = REG_SET_FIELD(tmp, CP_HQD_QUANTUM, QUANTUM_EN, 1);
tmp = REG_SET_FIELD(tmp, CP_HQD_QUANTUM, QUANTUM_SCALE, 1);
tmp = REG_SET_FIELD(tmp, CP_HQD_QUANTUM, QUANTUM_DURATION, 10);
if (ring != &adev->gfx.kiq[0].ring) {
tmp = REG_SET_FIELD(tmp, CP_HQD_QUANTUM, QUANTUM_EN, 1);
tmp = REG_SET_FIELD(tmp, CP_HQD_QUANTUM, QUANTUM_SCALE, 1);
tmp = REG_SET_FIELD(tmp, CP_HQD_QUANTUM, QUANTUM_DURATION, 10);
}
mqd->cp_hqd_quantum = tmp;
/* map_queues packet doesn't need activate the queue,

View File

@ -1455,6 +1455,14 @@ static int evict_process_queues_cpsch(struct device_queue_manager *dqm,
dqm_evict_mqd_bo(dqm, q);
}
/*
* Heavy-weight TLB flush after MES removes queues to ensure
* in-flight memory accesses complete before memory is freed/migrated.
* HWS does this automatically, MES does not.
*/
if (dqm->dev->kfd->shared_resources.enable_mes)
kfd_flush_tlb(pdd);
if (!dqm->dev->kfd->shared_resources.enable_mes) {
pdd->last_evict_timestamp = get_jiffies_64();
retval = execute_queues_cpsch(dqm,
@ -3746,8 +3754,11 @@ int suspend_queues(struct kfd_process *p,
if (!per_device_suspended) {
dqm_unlock(dqm);
mutex_unlock(&p->event_mutex);
if (total_suspended)
if (total_suspended) {
amdgpu_amdkfd_debug_mem_fence(dqm->dev->adev);
/* Heavy-weight TLB flush after MES suspends queues */
kfd_flush_tlb(pdd);
}
continue;
}

View File

@ -1169,13 +1169,13 @@ int pqm_debugfs_mqds(struct seq_file *m, void *data)
mqd_mgr = q->device->dqm->mqd_mgrs[mqd_type];
size = mqd_mgr->mqd_stride(mqd_mgr,
&q->properties);
}
for (xcc = 0; xcc < num_xccs; xcc++) {
mqd = q->mqd + size * xcc;
r = mqd_mgr->debugfs_show_mqd(m, mqd);
if (r != 0)
break;
for (xcc = 0; xcc < num_xccs; xcc++) {
mqd = q->mqd + size * xcc;
r = mqd_mgr->debugfs_show_mqd(m, mqd);
if (r != 0)
break;
}
}
}

View File

@ -3879,7 +3879,7 @@ static void amdgpu_dm_commit_planes(struct drm_atomic_commit *state,
continue;
bundle->surface_updates[planes_count].surface = dc_plane;
if (new_pcrtc_state->color_mgmt_changed) {
if (new_pcrtc_state->color_mgmt_changed || new_plane_state->color_mgmt_changed) {
bundle->surface_updates[planes_count].gamma = &dc_plane->gamma_correction;
bundle->surface_updates[planes_count].in_transfer_func = &dc_plane->in_transfer_func;
bundle->surface_updates[planes_count].gamut_remap_matrix = &dc_plane->gamut_remap_matrix;
@ -5698,6 +5698,10 @@ static bool should_reset_plane(struct drm_atomic_commit *state,
if (new_crtc_state->color_mgmt_changed)
return true;
/* Plane color pipeline or its colorop changes. */
if (new_plane_state->color_mgmt_changed)
return true;
/*
* On zpos change, planes need to be reordered by removing and re-adding
* them one by one to the dc state, in order of descending zpos.

View File

@ -534,8 +534,12 @@ void amdgpu_dm_update_connector_ext_caps(struct amdgpu_dm_connector *aconnector)
else if (!IS_ERR_OR_NULL(panel_backlight_quirk) &&
panel_backlight_quirk->force_pwm)
caps->aux_support = false;
if (caps->aux_support)
aconnector->dc_link->backlight_control_type = BACKLIGHT_CONTROL_AMD_AUX;
if (caps->aux_support) {
if (aconnector->dc_link->dpcd_caps.panel_luminance_control)
aconnector->dc_link->backlight_control_type = BACKLIGHT_CONTROL_VESA_AUX;
else
aconnector->dc_link->backlight_control_type = BACKLIGHT_CONTROL_AMD_AUX;
}
luminance_range = &conn_base->display_info.luminance_range;

View File

@ -2208,16 +2208,41 @@ int amdgpu_dm_plane_init(struct amdgpu_display_manager *dm,
if (res)
return res;
if (plane->type == DRM_PLANE_TYPE_OVERLAY &&
plane_cap && plane_cap->per_pixel_alpha) {
/* Blend mode support varies on DCE generations according to HW caps
* and number of planes per CRTC. However, as current driver
* implementation only creates one primary and one cursor plane per
* CRTC for DCE (overlay is only created if
* DC_PLANE_TYPE_DCN_UNIVERSAL), the primary plane blend mode is
* ignored across DCE versions. Keep PREMULTI to avoid uAPI
* regressions: it was the default/mandatory mode for many years and,
* with no overlay plane, primary composes on top of a black
* background, where PREMULTI and PIXEL_NONE are equivalent.
*/
if (plane_cap && plane_cap->type != DC_PLANE_TYPE_DCN_UNIVERSAL) {
unsigned int blend_caps = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
BIT(DRM_MODE_BLEND_PREMULTI);
drm_plane_create_blend_mode_property(plane, blend_caps);
} else if ((plane->type == DRM_PLANE_TYPE_OVERLAY ||
plane->type == DRM_PLANE_TYPE_PRIMARY) &&
plane_cap && plane_cap->per_pixel_alpha) {
unsigned int blend_caps = BIT(DRM_MODE_BLEND_PIXEL_NONE) |
BIT(DRM_MODE_BLEND_PREMULTI) |
BIT(DRM_MODE_BLEND_COVERAGE);
drm_plane_create_alpha_property(plane);
drm_plane_create_blend_mode_property(plane, blend_caps);
if (plane->type == DRM_PLANE_TYPE_OVERLAY)
drm_plane_create_alpha_property(plane);
}
/* Cursor color format is set to CURSOR_MODE_COLOR_PRE_MULTIPLIED_ALPHA
* by default, so only advertise DRM_MODE_BLEND_PREMULTI blend mode for
* this type of plane.
*/
if (plane->type == DRM_PLANE_TYPE_CURSOR)
drm_plane_create_blend_mode_property(plane, BIT(DRM_MODE_BLEND_PREMULTI));
if (plane->type == DRM_PLANE_TYPE_PRIMARY) {
/*
* Allow OVERLAY planes to be used as underlays by assigning an

View File

@ -1797,7 +1797,11 @@ bool resource_can_pipe_disable_cursor(struct pipe_ctx *pipe_ctx)
* pipe-split, merge together per same height.
*/
for (split_pipe = pipe_ctx->top_pipe; split_pipe;
split_pipe = split_pipe->top_pipe)
split_pipe = split_pipe->top_pipe) {
if (split_pipe == test_pipe)
continue;
if (split_pipe->plane_state->layer_index == test_pipe->plane_state->layer_index) {
struct rect r2_half;
@ -1809,6 +1813,7 @@ bool resource_can_pipe_disable_cursor(struct pipe_ctx *pipe_ctx)
r2_bottom = min(r2_bottom, r2_half.y + r2_half.height);
break;
}
}
if (r1.x >= r2.x && r1.y >= r2.y && r1_right <= r2_right && r1_bottom <= r2_bottom)
return true;

View File

@ -320,6 +320,8 @@ static struct fixed31_32 interp_tf_pts(const struct fixed31_32 *output_tf_channe
return value;
}
#define NUM_DEGAMMA_REGIONS 9
bool cm3_helper_translate_curve_to_degamma_hw_format(
const struct dc_transfer_func *output_tf,
struct pwl_params *lut_params)
@ -343,31 +345,15 @@ bool cm3_helper_translate_curve_to_degamma_hw_format(
memset(lut_params, 0, sizeof(struct pwl_params));
memset(seg_distr, 0, sizeof(seg_distr));
if (output_tf->tf == TRANSFER_FUNCTION_PQ ||
output_tf->tf == TRANSFER_FUNCTION_SRGB) {
/* 9 segments
* segments are from 2^-9 to 0
*/
const uint8_t SEG_COUNT = 9;
seg_distr[0] = 0; // Since we only have one point in darkest region
for (k = 1; k < SEG_COUNT; k++)
seg_distr[k] = k - 1; // 2^(k-1) points per region; halves as k decreases
/* 9 segments
* segments are from 2^-9 to 2^0
*/
seg_distr[0] = 0; // Since we only have one point in darkest region
for (k = 1; k < NUM_DEGAMMA_REGIONS; k++)
seg_distr[k] = k - 1; // 2^(k-1) points per region; halves as k decreases
region_start = -SEG_COUNT;
region_end = 0;
} else {
/* 12 segments
* segments are from 2^-12 to 2^0
* There are less than 256 points, for optimization
*/
const uint8_t SEG_COUNT = 12;
for (i = 0; i < SEG_COUNT; i++)
seg_distr[i] = 4;
region_start = -SEG_COUNT;
region_end = 0;
}
region_start = -NUM_DEGAMMA_REGIONS;
region_end = 0;
for (i = region_end - region_start; i < MAX_REGIONS_NUMBER ; i++)
seg_distr[i] = -1;

View File

@ -1128,8 +1128,8 @@ static bool dcn5_mode_programming(struct dml2_core_calcs_mode_programming_ex *in
CalculateWatermarks_params->USRRetrainingSupport = &mode_lib->mp.USRRetrainingSupport;
CalculateWatermarks_params->temp_read_or_ppt_support = mode_lib->mp.temp_read_or_ppt_support;
CalculateWatermarks_params->global_temp_read_or_ppt_supported = &mode_lib->mp.global_temp_read_or_ppt_supported;
CalculateWatermarks_params->VActiveLatencyHidingMargin = 0;
CalculateWatermarks_params->VActiveLatencyHidingUs = 0;
CalculateWatermarks_params->VActiveLatencyHidingMargin = NULL;
CalculateWatermarks_params->VActiveLatencyHidingUs = NULL;
dcn5_calculate_watermarks_and_dram_speed_change_support(&mode_lib->scratch, CalculateWatermarks_params);

View File

@ -1819,8 +1819,8 @@ struct dml2_core_calcs_CalculateWatermarksMALLUseAndDRAMSpeedChangeSupport_param
bool UnboundedRequestEnabled;
unsigned int CompressedBufferSizeInkByte;
bool max_outstanding_when_urgent_expected;
const unsigned int max_outstanding_requests;
const unsigned int max_request_size_bytes;
unsigned int max_outstanding_requests;
unsigned int max_request_size_bytes;
const unsigned int *meta_row_height_l;
const unsigned int *meta_row_height_c;
const enum dml2_pstate_method *uclk_pstate_switch_modes;

View File

@ -1414,7 +1414,7 @@ static bool find_shift_for_valid_cache_id_assignment(const int *mcache_boundarie
success = true;
for (pipe_index = 0; pipe_index < pipe_count; pipe_index++) {
if (!calculate_first_second_splitting(mcache_boundaries, num_boundaries, *shift,
pipe_vp_startx[pipe_index], pipe_vp_endx[pipe_index], 0, 0)) {
pipe_vp_startx[pipe_index], pipe_vp_endx[pipe_index], NULL, NULL)) {
success = false;
break;
}

View File

@ -301,6 +301,7 @@ void dml2_init_socbb_params(struct dml2_context *dml2, const struct dc *in_dc, s
out->smn_latency_us = 2;
out->dispclk_dppclk_vco_speed_mhz = 3600;
out->pct_ideal_dram_bw_after_urgent_pixel_only = 65.0;
out->gpuvm_min_page_size_kbytes = 4;
break;

View File

@ -103,6 +103,11 @@ static int get_estimated_bw(struct dc_link *link)
{
uint8_t bw_estimated_bw = 0;
if (link->dpia_bw_alloc_config.bw_granularity == 0) {
DC_LOG_ERROR("%s: BW granularity is zero!\n", __func__);
return 0;
}
core_link_read_dpcd(
link,
ESTIMATED_BW,

View File

@ -278,7 +278,14 @@ void __drm_atomic_helper_plane_state_init(struct drm_plane_state *plane_state,
plane_state->rotation = DRM_MODE_ROTATE_0;
plane_state->alpha = DRM_BLEND_ALPHA_OPAQUE;
plane_state->pixel_blend_mode = DRM_MODE_BLEND_PREMULTI;
if (plane->blend_mode_property) {
if (!drm_object_property_get_default_value(&plane->base,
plane->blend_mode_property,
&val))
plane_state->pixel_blend_mode = val;
}
if (plane->color_encoding_property) {
if (!drm_object_property_get_default_value(&plane->base,

View File

@ -1449,9 +1449,6 @@ static int prepare_signaling(struct drm_device *dev,
if (arg->flags & DRM_MODE_PAGE_FLIP_EVENT) {
struct drm_pending_vblank_event *e = crtc_state->event;
if (!file_priv)
continue;
ret = drm_event_reserve_init(dev, file_priv, &e->base,
&e->event.base);
if (ret) {
@ -1567,6 +1564,8 @@ static void complete_signaling(struct drm_device *dev,
* to prevent a double free in drm_atomic_commit_clear.
*/
if (event && (event->base.fence || event->base.file_priv)) {
if (crtc_state->commit && crtc_state->commit->abort_completion)
drm_crtc_commit_put(crtc_state->commit);
drm_event_cancel_free(dev, &event->base);
crtc_state->event = NULL;
}

View File

@ -383,6 +383,58 @@ drm_pagemap_migrate_map_system_pages(struct device *dev,
return 0;
}
/**
* drm_pagemap_migrate_populate_src_pages() - Populate the source page array
* @pages: Array of source pages to populate
* @src_mpfn: Source array of migrate PFNs
* @dst_mpfn: Destination array of migrate PFNs
* @npages: Number of pages in the arrays
*
* Populate @pages with the device pages the copy callback is to read from.
*
* Entries are normally only populated at the head of each source folio, with
* the copy callback deriving the rest of the folio from the order recorded in
* the corresponding drm_pagemap_addr. That does not work where
* drm_pagemap_migrate_populate_ram_pfn() had to demote a higher-order source
* folio to order-0 destination folios: the drm_pagemap_addr entries are then
* per-page, and the copy callback needs a source page for each of them.
* Populate every entry for those ranges.
*
* Note that the source folio itself is only split later, by
* migrate_vma_pages() / migrate_device_pages(), so its order cannot be used to
* detect the demotion - the destination has to be inspected instead.
*/
static void drm_pagemap_migrate_populate_src_pages(struct page **pages,
unsigned long *src_mpfn,
unsigned long *dst_mpfn,
unsigned long npages)
{
unsigned long i;
for (i = 0; i < npages;) {
struct page *page = migrate_pfn_to_page(src_mpfn[i]);
unsigned int order = 0;
unsigned long j, nr;
if (!page) {
i++;
continue;
}
order = folio_order(page_folio(page));
nr = NR_PAGES(order);
if (order && !(dst_mpfn[i] & MIGRATE_PFN_COMPOUND)) {
for (j = 0; j < nr && i + j < npages; j++)
pages[i + j] = folio_page(page_folio(page), j);
} else {
pages[i] = page;
}
i += nr;
}
}
/**
* drm_pagemap_migrate_unmap_pages() - Unmap pages previously mapped for GPU SVM migration
* @dev: The device for which the pages were mapped
@ -875,6 +927,7 @@ static int drm_pagemap_migrate_populate_ram_pfn(struct vm_area_struct *vas,
struct page *page = NULL, *src_page;
struct folio *folio;
unsigned int order = 0;
gfp_t gfp = GFP_HIGHUSER;
if (!(src_mpfn[i] & MIGRATE_PFN_MIGRATE))
goto next;
@ -891,11 +944,51 @@ static int drm_pagemap_migrate_populate_ram_pfn(struct vm_area_struct *vas,
order = folio_order(page_folio(src_page));
/* TODO: Support fallback to single pages if THP allocation fails */
/*
* A large source folio is always collected whole, at its head
* page, PMD aligned and flagged MIGRATE_PFN_COMPOUND: anything
* else is split before it reaches us, either by
* migrate_vma_collect_pmd() or, for the eviction path, by
* migrate_device_pfns(). Both the order-0 fallback below and
* drm_pagemap_migrate_populate_src_pages() rely on that, as
* they index the folio from @i.
*/
WARN_ON_ONCE(order &&
(src_page != folio_page(page_folio(src_page), 0) ||
!(src_mpfn[i] & MIGRATE_PFN_COMPOUND)));
if (order)
gfp |= __GFP_NOWARN;
if (vas)
folio = vma_alloc_folio(GFP_HIGHUSER, order, vas, addr);
folio = vma_alloc_folio(gfp, order, vas, addr);
else
folio = folio_alloc(GFP_HIGHUSER, order);
folio = folio_alloc(gfp, order);
if (!folio && order) {
/*
* Higher-order allocation failed, fall back to
* order-0 allocations for the entire range covered
* by the original higher-order allocation, without
* setting MIGRATE_PFN_COMPOUND, until we move past
* that range.
*/
unsigned long nr = NR_PAGES(order);
unsigned long j;
gfp &= ~__GFP_NOWARN;
for (j = 0; j < nr && i < npages; j++, i++, addr += PAGE_SIZE) {
folio = vas ?
vma_alloc_folio(gfp, 0, vas, addr) :
folio_alloc(gfp, 0);
if (!folio)
goto free_pages;
page = folio_page(folio, 0);
mpfn[i] = migrate_pfn(page_to_pfn(page));
}
continue;
}
if (!folio)
goto free_pages;
@ -940,11 +1033,11 @@ static int drm_pagemap_migrate_populate_ram_pfn(struct vm_area_struct *vas,
if (!page)
goto next_put;
order = folio_order(page_folio(page));
put_page(page);
mpfn[i] = 0;
order = folio_order(page_folio(page));
next_put:
i += NR_PAGES(order);
}
@ -1102,12 +1195,117 @@ void drm_pagemap_put(struct drm_pagemap *dpagemap)
}
EXPORT_SYMBOL(drm_pagemap_put);
/**
* drm_pagemap_page_get_flags() - Read flags from a device-private folio
* @page: Pointer to a page of the device-private folio
*
* Return: The DRM_PAGEMAP_ZDD_FLAG_* bits encoded in zone_device_data.
*/
static unsigned long drm_pagemap_page_get_flags(struct page *page)
{
struct folio *folio = page_folio(page);
return (unsigned long)folio_zone_device_data(folio) &
DRM_PAGEMAP_ZDD_FLAG_MASK;
}
/**
* drm_pagemap_page_set_flags() - Set flags on a device-private folio
* @page: Pointer to a page of the device-private folio
* @flags: DRM_PAGEMAP_ZDD_FLAG_* bits to set
*
* Preserve any flags already encoded alongside the ZDD pointer.
*/
static void drm_pagemap_page_set_flags(struct page *page,
unsigned long flags)
{
struct folio *folio = page_folio(page);
unsigned long old;
if (WARN_ON_ONCE(flags & ~DRM_PAGEMAP_ZDD_FLAG_MASK))
return;
old = (unsigned long)folio_zone_device_data(folio);
folio_set_zone_device_data(folio, (void *)(old | flags));
}
/**
* drm_pagemap_retire_migrated_pages() - Record migrated device-private folios
* @src_pfns: source array after migrate_vma_pages() or migrate_device_pages()
* @npages: number of entries in @src_pfns
*
* Flag device-private folios successfully migrated to RAM before finalize
* unlocks the sources. The migrated state is stored in the physical folio, so
* it survives later folio splits and subsequent migrations can skip it.
*/
static void drm_pagemap_retire_migrated_pages(unsigned long *src_pfns,
unsigned long npages)
{
unsigned long i = 0;
while (i < npages) {
struct page *page = migrate_pfn_to_page(src_pfns[i]);
unsigned long nr = 1;
if (!page) {
i++;
continue;
}
if (src_pfns[i] & MIGRATE_PFN_COMPOUND)
nr = folio_nr_pages(page_folio(page));
if ((src_pfns[i] & MIGRATE_PFN_MIGRATE) &&
is_device_private_page(page))
drm_pagemap_page_set_flags(page,
DRM_PAGEMAP_ZDD_FLAG_MIGRATED);
i += nr;
}
}
/**
* drm_pagemap_skip_retired_pages() - Skip retired device-private folios
* @src_pfns: MIGRATE_PFN-encoded source array
* @npages: number of entries in @src_pfns
*
* Skip source folios already migrated to RAM, identified by the migrated flag
* stored in the physical folio's zone_device_data.
*/
static void drm_pagemap_skip_retired_pages(unsigned long *src_pfns,
unsigned long npages)
{
unsigned long i = 0;
while (i < npages) {
struct page *page = migrate_pfn_to_page(src_pfns[i]);
unsigned long nr = 1;
if (!page) {
i++;
continue;
}
if (src_pfns[i] & MIGRATE_PFN_COMPOUND)
nr = folio_nr_pages(page_folio(page));
if ((src_pfns[i] & MIGRATE_PFN_MIGRATE) &&
is_device_private_page(page) &&
(drm_pagemap_page_get_flags(page) &
DRM_PAGEMAP_ZDD_FLAG_MIGRATED))
src_pfns[i] &= ~MIGRATE_PFN_MIGRATE;
i += nr;
}
}
/**
* drm_pagemap_evict_to_ram() - Evict GPU SVM range to RAM
* @devmem_allocation: Pointer to the device memory allocation
*
* Similar to __drm_pagemap_migrate_to_ram but does not require mmap lock and
* migration done via migrate_device_* functions.
* Similar to __drm_pagemap_migrate_to_ram(), but uses the
* migrate_device_* helpers and does not require the mmap lock.
* Device-private PFNs already migrated to RAM by either path are skipped.
*
* Return: 0 on success, negative error code on failure.
*/
@ -1115,17 +1313,18 @@ int drm_pagemap_evict_to_ram(struct drm_pagemap_devmem *devmem_allocation)
{
const struct drm_pagemap_devmem_ops *ops = devmem_allocation->ops;
struct drm_pagemap_iova_state state = {};
unsigned long npages, mpages = 0;
unsigned long npages, mpages;
struct page **pages;
unsigned long *src, *dst;
struct drm_pagemap_addr *pagemap_addr;
void *buf;
int i, err = 0;
int err = 0;
unsigned int retry_count = 2;
npages = devmem_allocation->size >> PAGE_SHIFT;
retry:
mpages = 0;
if (!mmget_not_zero(devmem_allocation->mm))
return -EFAULT;
@ -1148,6 +1347,8 @@ int drm_pagemap_evict_to_ram(struct drm_pagemap_devmem *devmem_allocation)
if (err)
goto err_free;
drm_pagemap_skip_retired_pages(src, npages);
err = drm_pagemap_migrate_populate_ram_pfn(NULL, NULL, npages, &mpages,
src, dst, 0);
if (err || !mpages)
@ -1160,27 +1361,20 @@ int drm_pagemap_evict_to_ram(struct drm_pagemap_devmem *devmem_allocation)
if (err)
goto err_finalize;
for (i = 0; i < npages;) {
unsigned int order = 0;
pages[i] = migrate_pfn_to_page(src[i]);
if (pages[i])
order = folio_order(page_folio(pages[i]));
i += NR_PAGES(order);
}
drm_pagemap_migrate_populate_src_pages(pages, src, dst, npages);
err = ops->copy_to_ram(pages, pagemap_addr, npages, NULL);
if (err)
goto err_finalize;
err_finalize:
drm_pagemap_migrate_unmap_pages(devmem_allocation->dev, pagemap_addr, dst, npages,
DMA_FROM_DEVICE, &state);
if (err)
drm_pagemap_migration_unlock_put_pages(npages, dst);
migrate_device_pages(src, dst, npages);
drm_pagemap_retire_migrated_pages(src, npages);
migrate_device_finalize(src, dst, npages);
drm_pagemap_migrate_unmap_pages(devmem_allocation->dev, pagemap_addr, dst, npages,
DMA_FROM_DEVICE, &state);
err_free:
kvfree(buf);
@ -1235,7 +1429,7 @@ static int __drm_pagemap_migrate_to_ram(struct vm_area_struct *vas,
struct drm_pagemap_addr *pagemap_addr;
unsigned long start, end;
void *buf;
int i, err = 0;
int err = 0;
zdd = drm_pagemap_page_zone_device_data(page);
if (time_before64(get_jiffies_64(), zdd->devmem_allocation->timeslice_expiration))
@ -1275,13 +1469,15 @@ static int __drm_pagemap_migrate_to_ram(struct vm_area_struct *vas,
if (!migrate.cpages)
goto err_free;
drm_pagemap_skip_retired_pages(migrate.src, npages);
ops = zdd->devmem_allocation->ops;
dev = zdd->devmem_allocation->dev;
err = drm_pagemap_migrate_populate_ram_pfn(vas, page, npages, &mpages,
migrate.src, migrate.dst,
start);
if (err)
if (err || !mpages)
goto err_finalize;
err = drm_pagemap_migrate_map_system_pages(dev, pagemap_addr,
@ -1290,29 +1486,23 @@ static int __drm_pagemap_migrate_to_ram(struct vm_area_struct *vas,
if (err)
goto err_finalize;
for (i = 0; i < npages;) {
unsigned int order = 0;
pages[i] = migrate_pfn_to_page(migrate.src[i]);
if (pages[i])
order = folio_order(page_folio(pages[i]));
i += NR_PAGES(order);
}
drm_pagemap_migrate_populate_src_pages(pages, migrate.src, migrate.dst,
npages);
err = ops->copy_to_ram(pages, pagemap_addr, npages, NULL);
if (err)
goto err_finalize;
err_finalize:
if (err)
drm_pagemap_migration_unlock_put_pages(npages, migrate.dst);
migrate_vma_pages(&migrate);
migrate_vma_finalize(&migrate);
if (dev)
drm_pagemap_migrate_unmap_pages(dev, pagemap_addr, migrate.dst,
npages, DMA_FROM_DEVICE,
&state);
if (err)
drm_pagemap_migration_unlock_put_pages(npages, migrate.dst);
migrate_vma_pages(&migrate);
drm_pagemap_retire_migrated_pages(migrate.src, npages);
migrate_vma_finalize(&migrate);
err_free:
kvfree(buf);
err_out:
@ -1360,13 +1550,19 @@ static vm_fault_t drm_pagemap_migrate_to_ram(struct vm_fault *vmf)
static void drm_pagemap_folio_split(struct folio *orig_folio, struct folio *new_folio)
{
struct drm_pagemap_zdd *zdd;
unsigned long orig_data, new_data;
if (!new_folio)
return;
new_folio->pgmap = orig_folio->pgmap;
zdd = folio_zone_device_data(orig_folio);
folio_set_zone_device_data(new_folio, drm_pagemap_zdd_get(zdd));
orig_data = (unsigned long)folio_zone_device_data(orig_folio);
zdd = (struct drm_pagemap_zdd *)(orig_data & ~DRM_PAGEMAP_ZDD_FLAG_MASK);
new_data = (unsigned long)drm_pagemap_zdd_get(zdd);
new_data |= orig_data & DRM_PAGEMAP_ZDD_FLAG_MASK;
folio_set_zone_device_data(new_folio, (void *)new_data);
}
static const struct dev_pagemap_ops drm_pagemap_pagemap_ops = {

View File

@ -524,7 +524,7 @@ int drm_gem_prime_handle_to_fd(struct drm_device *dev,
return PTR_ERR(dmabuf);
}
fd_install(fd, dmabuf->file);
dma_buf_fd_install(dmabuf, fd);
*prime_fd = fd;
return 0;
}

View File

@ -396,8 +396,16 @@ static int gud_connector_add_tv_mode(struct gud_device *gdrm, struct drm_connect
}
num_modes = ret / GUD_CONNECTOR_TV_MODE_NAME_LEN;
for (i = 0; i < num_modes; i++)
modes[i] = &buf[i * GUD_CONNECTOR_TV_MODE_NAME_LEN];
for (i = 0; i < num_modes; i++) {
char *mode = &buf[i * GUD_CONNECTOR_TV_MODE_NAME_LEN];
if (!memchr(mode, '\0', GUD_CONNECTOR_TV_MODE_NAME_LEN)) {
ret = -EIO;
goto free;
}
modes[i] = mode;
}
ret = drm_mode_create_tv_properties_legacy(connector->dev, num_modes, modes);
free:

View File

@ -289,6 +289,8 @@ static int gud_plane_add_properties(struct gud_device *gdrm)
* but mask out any additions on future devices.
*/
val &= GUD_ROTATION_MASK;
if (!(val & GUD_ROTATION_0))
continue;
ret = drm_plane_create_rotation_property(&gdrm->plane,
DRM_MODE_ROTATE_0, val);
break;

View File

@ -2381,8 +2381,10 @@ static void bxt_sanitize_cdclk(struct intel_display *display)
* dividers both syncing to an active pipe, or asynchronously
* (PIPE_NONE).
*/
cdctl &= ~bxt_cdclk_cd2x_pipe_mask(display);
cdctl |= bxt_cdclk_cd2x_pipe(display, INVALID_PIPE);
if (DISPLAY_VER(display) < 30) {
cdctl &= ~bxt_cdclk_cd2x_pipe_mask(display);
cdctl |= bxt_cdclk_cd2x_pipe(display, INVALID_PIPE);
}
if (cdctl != expected) {
if (DISPLAY_VER(display) < 20) {
@ -2713,8 +2715,8 @@ static void intel_set_cdclk(struct intel_display *display,
}
}
static bool dg2_power_well_count(struct intel_display *display,
const struct intel_cdclk_state *cdclk_state)
static int dg2_power_well_count(struct intel_display *display,
const struct intel_cdclk_state *cdclk_state)
{
return display->platform.dg2 ? hweight8(cdclk_state->active_pipes) : 0;
}

View File

@ -530,13 +530,18 @@ static int i9xx_check_cursor(struct intel_crtc_state *crtc_state,
}
static void i9xx_cursor_disable_sel_fetch_arm(struct intel_dsb *dsb,
struct intel_plane *plane,
const struct intel_crtc_state *crtc_state)
struct intel_plane *plane)
{
struct intel_display *display = to_intel_display(plane);
enum pipe pipe = plane->pipe;
if (!crtc_state->enable_psr2_sel_fetch)
/*
* Clear this whenever the hardware has selective fetch, not just when
* the current state uses it. The cursor may have been enabled with
* selective fetch earlier and had its enable bit orphaned when the
* feature was switched off.
*/
if (!HAS_PSR2_SEL_FETCH(display))
return;
intel_de_write_dsb(display, dsb, SEL_FETCH_CUR_CTL(pipe), 0);
@ -586,7 +591,7 @@ static void i9xx_cursor_update_sel_fetch_arm(struct intel_dsb *dsb,
if (crtc_state->enable_psr2_su_region_et)
wa_16021440873(dsb, plane, crtc_state, plane_state);
else
i9xx_cursor_disable_sel_fetch_arm(dsb, plane, crtc_state);
i9xx_cursor_disable_sel_fetch_arm(dsb, plane);
}
}
@ -695,7 +700,7 @@ static void i9xx_cursor_update_arm(struct intel_dsb *dsb,
if (plane_state)
i9xx_cursor_update_sel_fetch_arm(dsb, plane, crtc_state, plane_state);
else
i9xx_cursor_disable_sel_fetch_arm(dsb, plane, crtc_state);
i9xx_cursor_disable_sel_fetch_arm(dsb, plane);
if (plane->cursor.base != base ||
plane->cursor.size != fbc_ctl ||

View File

@ -3233,7 +3233,8 @@ static void intel_cx0pll_enable(struct intel_encoder *encoder,
* 8. Program DDI_CLK_VALFREQ to match intended DDI
* clock frequency.
*/
intel_de_write(display, DDI_CLK_VALFREQ(encoder->port), port_clock);
intel_de_write(display, DDI_CLK_VALFREQ(encoder->port),
intel_ddi_link_symbol_clock(encoder, port_clock));
/*
* 9. Set PORT_CLOCK_CTL register PCLK PLL Request
@ -3406,7 +3407,7 @@ void intel_mtl_tbt_pll_enable_clock(struct intel_encoder *encoder, int port_cloc
* clock frequency.
*/
intel_de_write(display, DDI_CLK_VALFREQ(encoder->port),
port_clock);
intel_ddi_link_symbol_clock(encoder, port_clock));
}
void intel_mtl_pll_enable(struct intel_encoder *encoder,

View File

@ -1529,6 +1529,17 @@ int intel_ddi_level(struct intel_encoder *encoder,
return level;
}
int intel_ddi_link_symbol_clock(struct intel_encoder *encoder, int clock)
{
if (intel_encoder_is_dp(encoder))
return intel_dp_link_symbol_clock(clock);
if (intel_hdmi_is_frl(clock))
return DIV_ROUND_CLOSEST(clock * 10, 18);
return clock;
}
static void
hsw_set_signal_levels(struct intel_encoder *encoder,
const struct intel_crtc_state *crtc_state)

View File

@ -81,6 +81,7 @@ void intel_ddi_sanitize_encoder_pll_mapping(struct intel_encoder *encoder);
int intel_ddi_level(struct intel_encoder *encoder,
const struct intel_crtc_state *crtc_state,
int lane);
int intel_ddi_link_symbol_clock(struct intel_encoder *encoder, int clock);
void intel_ddi_update_active_dpll(struct intel_atomic_state *state,
struct intel_encoder *encoder,
struct intel_crtc *crtc);

View File

@ -761,10 +761,6 @@ static int mst_stream_compute_config(struct intel_atomic_state *state,
pipe_config->sink_format = INTEL_OUTPUT_FORMAT_RGB;
pipe_config->output_format = INTEL_OUTPUT_FORMAT_RGB;
ret = intel_pfit_compute_config(pipe_config, conn_state);
if (ret)
return ret;
ret = intel_pfit_compute_config(pipe_config, conn_state);
if (ret)
return ret;

View File

@ -1976,7 +1976,8 @@ void intel_lt_phy_pll_enable(struct intel_encoder *encoder,
* Change. We handle this step in bxt_set_cdclk().
*/
/* 10. Program DDI_CLK_VALFREQ to match intended DDI clock frequency. */
intel_de_write(display, DDI_CLK_VALFREQ(encoder->port), port_clock);
intel_de_write(display, DDI_CLK_VALFREQ(encoder->port),
intel_ddi_link_symbol_clock(encoder, port_clock));
/* 11. Program PORT_CLOCK_CTL[PCLK PLL Request LN0] = 1. */
intel_de_rmw(display, XELPDP_PORT_CLOCK_CTL(display, port),
@ -2023,7 +2024,8 @@ void intel_lt_phy_pll_enable(struct intel_encoder *encoder,
lane_phy_pulse_status,
lane_phy_pulse_status);
} else {
intel_de_write(display, DDI_CLK_VALFREQ(encoder->port), port_clock);
intel_de_write(display, DDI_CLK_VALFREQ(encoder->port),
intel_ddi_link_symbol_clock(encoder, port_clock));
}
/*

View File

@ -879,13 +879,18 @@ skl_plane_disable_arm(struct intel_dsb *dsb,
}
static void icl_plane_disable_sel_fetch_arm(struct intel_dsb *dsb,
struct intel_plane *plane,
const struct intel_crtc_state *crtc_state)
struct intel_plane *plane)
{
struct intel_display *display = to_intel_display(plane);
enum pipe pipe = plane->pipe;
if (!crtc_state->enable_psr2_sel_fetch)
/*
* Clear this whenever the hardware has selective fetch, not just when
* the current state uses it. The plane may have been enabled with
* selective fetch earlier and had its enable bit orphaned when the
* feature was switched off.
*/
if (!HAS_PSR2_SEL_FETCH(display))
return;
intel_de_write_dsb(display, dsb, SEL_FETCH_PLANE_CTL(pipe, plane->id), 0);
@ -921,7 +926,7 @@ icl_plane_disable_arm(struct intel_dsb *dsb,
skl_write_plane_wm(dsb, plane, crtc_state);
icl_plane_disable_sel_fetch_arm(dsb, plane, crtc_state);
icl_plane_disable_sel_fetch_arm(dsb, plane);
if (plane_has_normalizer(plane))
intel_de_write_dsb(display, dsb,
@ -1641,7 +1646,7 @@ static void icl_plane_update_sel_fetch_arm(struct intel_dsb *dsb,
intel_de_write_dsb(display, dsb, SEL_FETCH_PLANE_CTL(pipe, plane->id),
SEL_FETCH_PLANE_CTL_ENABLE);
else
icl_plane_disable_sel_fetch_arm(dsb, plane, crtc_state);
icl_plane_disable_sel_fetch_arm(dsb, plane);
}
static void

View File

@ -958,6 +958,9 @@ static int i915_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
(struct intel_device_info *) ent->driver_data;
int err;
if (!intel_info)
return -ENODEV;
if (intel_info->require_force_probe && !id_forced(pdev->device)) {
dev_info(&pdev->dev,
"Your graphics device %04x is not properly supported by i915 in this\n"

View File

@ -87,4 +87,5 @@ int gp102_disp_new(struct nvkm_device *, enum nvkm_subdev_type, int inst, struct
int gv100_disp_new(struct nvkm_device *, enum nvkm_subdev_type, int inst, struct nvkm_disp **);
int tu102_disp_new(struct nvkm_device *, enum nvkm_subdev_type, int inst, struct nvkm_disp **);
int ga102_disp_new(struct nvkm_device *, enum nvkm_subdev_type, int inst, struct nvkm_disp **);
int gb202_disp_new(struct nvkm_device *, enum nvkm_subdev_type, int inst, struct nvkm_disp **);
#endif

View File

@ -90,6 +90,14 @@ nouveau_channel_del(struct nouveau_channel **pchan)
{
struct nouveau_channel *chan = *pchan;
if (chan) {
/*
* Drop the kill-event subscription first. Its handler
* dereferences chan->fence, which the fence context teardown
* below frees, so leaving it armed across the teardown leaves
* a window for a use-after-free.
*/
nvif_event_dtor(&chan->kill);
if (chan->fence)
nouveau_fence(chan->cli->drm)->context_del(chan);
@ -100,7 +108,6 @@ nouveau_channel_del(struct nouveau_channel **pchan)
nvif_object_dtor(&chan->nvsw);
nvif_object_dtor(&chan->gart);
nvif_object_dtor(&chan->vram);
nvif_event_dtor(&chan->kill);
nvif_object_dtor(&chan->user);
nvif_mem_dtor(&chan->mem_userd);
nouveau_vma_del(&chan->sema.vma);

View File

@ -267,7 +267,7 @@ static vm_fault_t nouveau_dmem_migrate_to_ram(struct vm_fault *vmf)
nouveau_fence_new(&fence, dmem->migrate.chan);
migrate_vma_pages(&args);
nouveau_dmem_fence_done(&fence);
dma_unmap_page(drm->dev->dev, dma_info.dma_addr, PAGE_SIZE,
dma_unmap_page(drm->dev->dev, dma_info.dma_addr, dma_info.size,
DMA_BIDIRECTIONAL);
done:
migrate_vma_finalize(&args);
@ -279,11 +279,25 @@ static vm_fault_t nouveau_dmem_migrate_to_ram(struct vm_fault *vmf)
static void nouveau_dmem_folio_split(struct folio *head, struct folio *tail)
{
struct nouveau_dmem_chunk *chunk;
struct nouveau_dmem *dmem;
if (tail == NULL)
return;
tail->pgmap = head->pgmap;
tail->mapping = head->mapping;
folio_set_zone_device_data(tail, folio_zone_device_data(head));
/*
* The split hands out a new independently-freeable folio that will
* later be released via nouveau_dmem_folio_free(); account for it so
* chunk->callocated stays balanced.
*/
chunk = nouveau_page_to_chunk(&head->page);
dmem = chunk->drm->dmem;
spin_lock(&dmem->lock);
chunk->callocated++;
spin_unlock(&dmem->lock);
}
static const struct dev_pagemap_ops nouveau_dmem_pagemap_ops = {
@ -772,7 +786,7 @@ static unsigned long nouveau_dmem_migrate_copy_one(struct nouveau_drm *drm,
return mpfn;
out_dma_unmap:
dma_unmap_page(dev, dma_info->dma_addr, PAGE_SIZE, DMA_BIDIRECTIONAL);
dma_unmap_page(dev, dma_info->dma_addr, dma_info->size, DMA_BIDIRECTIONAL);
out_free_page:
nouveau_dmem_page_free_locked(drm, dpage);
out:

View File

@ -72,9 +72,7 @@ nouveau_sgdma_create_ttm(struct ttm_buffer_object *bo, uint32_t page_flags)
struct nouveau_sgdma_be *nvbe;
enum ttm_caching caching;
if (nvbo->force_coherent)
caching = ttm_uncached;
else if (drm->agp.bridge)
if (nvbo->force_coherent || drm->agp.bridge)
caching = ttm_write_combined;
else
caching = ttm_cached;

View File

@ -1319,6 +1319,7 @@ nouveau_uvmm_bind_job_submit(struct nouveau_job *job,
op->va.range);
if (!op->reg || op->reg->dirty) {
ret = -ENOENT;
op->reg = NULL;
goto unwind_continue;
}
@ -1327,6 +1328,7 @@ nouveau_uvmm_bind_job_submit(struct nouveau_job *job,
op->va.range);
if (IS_ERR(op->ops)) {
ret = PTR_ERR(op->ops);
op->reg = NULL;
goto unwind_continue;
}
@ -1473,6 +1475,7 @@ nouveau_uvmm_bind_job_submit(struct nouveau_job *job,
op->va.range);
break;
case OP_UNMAP_SPARSE:
op->reg->dirty = false;
__nouveau_uvma_region_insert(uvmm, op->reg);
nouveau_uvmm_sm_unmap_prepare_unwind(uvmm, &op->new,
op->ops);
@ -1489,7 +1492,8 @@ nouveau_uvmm_bind_job_submit(struct nouveau_job *job,
break;
}
drm_gpuva_ops_free(&uvmm->base, op->ops);
if (!IS_ERR_OR_NULL(op->ops))
drm_gpuva_ops_free(&uvmm->base, op->ops);
op->ops = NULL;
op->reg = NULL;
}

View File

@ -2846,7 +2846,7 @@ nv1b2_chipset = {
.pci = { 0x00000001, gh100_pci_new },
.timer = { 0x00000001, gk20a_timer_new },
.vfn = { 0x00000001, ga100_vfn_new },
.disp = { 0x00000001, ga102_disp_new },
.disp = { 0x00000001, gb202_disp_new },
.fifo = { 0x00000001, ga102_fifo_new },
};
@ -2862,7 +2862,7 @@ nv1b3_chipset = {
.pci = { 0x00000001, gh100_pci_new },
.timer = { 0x00000001, gk20a_timer_new },
.vfn = { 0x00000001, ga100_vfn_new },
.disp = { 0x00000001, ga102_disp_new },
.disp = { 0x00000001, gb202_disp_new },
.fifo = { 0x00000001, ga102_fifo_new },
};
@ -2878,7 +2878,7 @@ nv1b5_chipset = {
.pci = { 0x00000001, gh100_pci_new },
.timer = { 0x00000001, gk20a_timer_new },
.vfn = { 0x00000001, ga100_vfn_new },
.disp = { 0x00000001, ga102_disp_new },
.disp = { 0x00000001, gb202_disp_new },
.fifo = { 0x00000001, ga102_fifo_new },
};
@ -2894,7 +2894,7 @@ nv1b6_chipset = {
.pci = { 0x00000001, gh100_pci_new },
.timer = { 0x00000001, gk20a_timer_new },
.vfn = { 0x00000001, ga100_vfn_new },
.disp = { 0x00000001, ga102_disp_new },
.disp = { 0x00000001, gb202_disp_new },
.fifo = { 0x00000001, ga102_fifo_new },
};
@ -2910,7 +2910,7 @@ nv1b7_chipset = {
.pci = { 0x00000001, gh100_pci_new },
.timer = { 0x00000001, gk20a_timer_new },
.vfn = { 0x00000001, ga100_vfn_new },
.disp = { 0x00000001, ga102_disp_new },
.disp = { 0x00000001, gb202_disp_new },
.fifo = { 0x00000001, ga102_fifo_new },
};

View File

@ -27,6 +27,7 @@ nvkm-y += nvkm/engine/disp/gp102.o
nvkm-y += nvkm/engine/disp/gv100.o
nvkm-y += nvkm/engine/disp/tu102.o
nvkm-y += nvkm/engine/disp/ga102.o
nvkm-y += nvkm/engine/disp/gb202.o
nvkm-y += nvkm/engine/disp/udisp.o
nvkm-y += nvkm/engine/disp/uconn.o

View File

@ -144,12 +144,23 @@ ga102_disp = {
},
};
static const struct nvkm_disp_func
ga102_gsp_disp = {
.uevent = &gv100_disp_chan_uevent,
.ramht_size = 0x2000,
.gsp.intr = tu102_disp_intr,
.gsp.head = &tu102_gsp_head,
.gsp.hdmi_gcp = tu102_sor_hdmi_gcp,
.gsp.hdmi_infoframe_avi = gv100_sor_hdmi_infoframe_avi,
.gsp.hdmi_infoframe_vsi = gv100_sor_hdmi_infoframe_vsi,
};
int
ga102_disp_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst,
struct nvkm_disp **pdisp)
{
if (nvkm_gsp_rm(device->gsp))
return r535_disp_new(&ga102_disp, device, type, inst, pdisp);
return r535_disp_new(&ga102_gsp_disp, device, type, inst, pdisp);
return nvkm_disp_new_(&ga102_disp, device, type, inst, pdisp);
}

View File

@ -0,0 +1,191 @@
// SPDX-License-Identifier: MIT
/*
* Copyright 2026 Valve Corp.
*/
#include "priv.h"
#include "head.h"
#include "ior.h"
#include <subdev/timer.h>
/* GB20x (NVD5.0) reorganised the SF HDMI packet units. The AVI unit is
* unchanged from GV100, but the legacy VSI unit is gone. Vendor infoframes
* are sent through the shared generic infoframe units instead. Register
* layout per NVIDIA's clc971.h/clca71.h, programming sequence per
* nvhdmipkt_C971.c:programAdvancedInfoframeC971().
*/
static void
gb202_sor_hdmi_infoframe_vsi(struct nvkm_ior *ior, int head, void *data, u32 size)
{
struct nvkm_device *device = ior->disp->engine.subdev.device;
const u32 hoff = head * 0x400;
/* Generic infoframe unit 1, the slot NVIDIA's driver uses for the VSI. */
const u32 ctrl = 0x6f0138 + hoff;
u8 buf[36] = {};
int i;
/* Disable the unit and wait for it to go idle. */
nvkm_mask(device, ctrl, 0x00000001, 0x00000000);
if (nvkm_msec(device, 2000,
if (!(nvkm_rd32(device, ctrl) & 0x00400000))
break;
) < 0)
return;
if (!size)
return;
/* Clear SENT status, and point the data port at unit 1's slot. */
nvkm_mask(device, ctrl, 0x00800000, 0x00800000);
nvkm_wr32(device, 0x6f03f0 + hoff, 0x00000001);
/* The data port takes the raw packet, except that a zero is inserted
* in HB3 after the three header bytes. A slot is 9 dwords (HB0-3 plus
* up to 32 payload bytes). An HDMI infoframe carries at most PB0-27,
* so the tail stays zero, and we always write the whole slot.
*/
size = min_t(u32, size, 31);
memcpy(buf, data, min_t(u32, size, 3));
if (size > 3)
memcpy(&buf[4], (u8 *)data + 3, size - 3);
for (i = 0; i < 36; i += 4) {
nvkm_wr32(device, 0x6f03f4 + hoff, buf[i + 0] | buf[i + 1] << 8 |
buf[i + 2] << 16 |
(u32)buf[i + 3] << 24);
}
/* No flip ID or scanline matching. */
nvkm_wr32(device, 0x6f013c + hoff, 0x00000000);
/* ENABLE | RUN_MODE=ALWAYS | LOC=VBLANK | OFFSET=1 | SIZE=0. */
nvkm_wr32(device, ctrl, 0x00000041);
/* Audio priority low (the init value). */
nvkm_wr32(device, 0x6f03f8 + hoff, 0x00000002);
}
/* General Control Packet AVMute bracket. The GCP unit moved to slot 1 on
* NVD5.0. Only SB0 (the AVMute bit) is ours to write so we must not do a
* full write here: SB1 carries the deep-color CD/PP fields, and SB1_CTRL
* (bit 24, new with clc871.h) controls where their generation happens (HW
* or driver) on these chips, with the default being HW.
*/
static void
gb202_sor_hdmi_gcp(struct nvkm_ior *sor, int head, bool enable)
{
struct nvkm_device *device = sor->disp->engine.subdev.device;
const u32 hdmi = head * 0x400;
nvkm_mask(device, 0x6f0040 + hdmi, 0x00000001, 0x00000000);
nvkm_mask(device, 0x6f004c + hdmi, 0x000000ff, !enable ? 0x00000001 :
0x00000010);
nvkm_mask(device, 0x6f0040 + hdmi, 0x00000001, 0x00000001);
}
/* Same core-channel state mirror as gv100_head_state() (assembly at 0x680000,
* armed at +0x8000, per-head method offsets unchanged), but NVD5.0 spaces
* heads 0x800 apart (see NVCA7D_HEAD_SET_*(a) in clca7d.h).
*/
static void
gb202_head_state(struct nvkm_head *head, struct nvkm_head_state *state)
{
struct nvkm_device *device = head->disp->engine.subdev.device;
const u32 hoff = (state == &head->arm) * 0x8000 + head->id * 0x800;
u32 data;
data = nvkm_rd32(device, 0x682064 + hoff);
state->vtotal = (data & 0xffff0000) >> 16;
state->htotal = (data & 0x0000ffff);
data = nvkm_rd32(device, 0x682068 + hoff);
state->vsynce = (data & 0xffff0000) >> 16;
state->hsynce = (data & 0x0000ffff);
data = nvkm_rd32(device, 0x68206c + hoff);
state->vblanke = (data & 0xffff0000) >> 16;
state->hblanke = (data & 0x0000ffff);
data = nvkm_rd32(device, 0x682070 + hoff);
state->vblanks = (data & 0xffff0000) >> 16;
state->hblanks = (data & 0x0000ffff);
/* Bit 31 is ADJ1000DIV1001, not a HERTZ bit. We don't have enough bits
* to add the full clock in hz on Blackwell (35 bits), but state->hz
* is unused and obsolete under GSP so this is fine.
*/
state->hz = nvkm_rd32(device, 0x68200c + hoff) & 0x7fffffff;
data = nvkm_rd32(device, 0x682004 + hoff);
switch ((data & 0x000000f0) >> 4) {
case 5: state->or.depth = 30; break;
case 4: state->or.depth = 24; break;
case 1: state->or.depth = 18; break;
default:
state->or.depth = 18;
WARN_ON(1);
break;
}
}
/* NVD5.0 (GB20x and later) moved the RM head-timing interrupt enable to
* the low-latency vector's EN1 block. The event latch is unchanged.
*/
static void
gb202_head_vblank_put(struct nvkm_head *head)
{
struct nvkm_device *device = head->disp->engine.subdev.device;
nvkm_mask(device, 0x611ef0 + (head->id * 4), 0x00000002, 0x00000000);
}
static void
gb202_head_vblank_get(struct nvkm_head *head)
{
struct nvkm_device *device = head->disp->engine.subdev.device;
nvkm_wr32(device, 0x611800 + (head->id * 4), 0x00000002);
nvkm_mask(device, 0x611ef0 + (head->id * 4), 0x00000002, 0x00000002);
}
static irqreturn_t
gb202_disp_intr(struct nvkm_inth *inth)
{
struct nvkm_disp *disp = container_of(inth, typeof(*disp), engine.subdev.inth);
irqreturn_t ret = tu102_disp_intr(inth);
/* The FE interrupt vectors are message-based on NVD5.0. Re-arm the
* low-latency vector so it fires again for any event that latched
* while we were servicing.
*/
nvkm_wr32(disp->engine.subdev.device, 0x611f34, 0x00000001);
return ret;
}
static const struct nvkm_head_func
gb202_gsp_head = {
.state = gb202_head_state,
.rgpos = gv100_head_rgpos,
.vblank_get = gb202_head_vblank_get,
.vblank_put = gb202_head_vblank_put,
};
/* GB20x is GSP-only. This table supplies the register programming the
* GSP-RM display path needs from the chip.
*/
static const struct nvkm_disp_func
gb202_gsp_disp = {
.uevent = &gv100_disp_chan_uevent,
.ramht_size = 0x2000,
/* Head timing arrives on the dedicated low-latency vector. */
.gsp.intr = gb202_disp_intr,
.gsp.intr_low_latency = true,
.gsp.head = &gb202_gsp_head,
.gsp.hdmi_gcp = gb202_sor_hdmi_gcp,
/* The legacy AVI unit is unchanged on GB20x. */
.gsp.hdmi_infoframe_avi = gv100_sor_hdmi_infoframe_avi,
.gsp.hdmi_infoframe_vsi = gb202_sor_hdmi_infoframe_vsi,
};
int
gb202_disp_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst,
struct nvkm_disp **pdisp)
{
return r535_disp_new(&gb202_gsp_disp, device, type, inst, pdisp);
}

View File

@ -56,6 +56,8 @@ int gv100_head_new(struct nvkm_disp *, int id);
void gv100_head_state(struct nvkm_head *head, struct nvkm_head_state *state);
void gv100_head_rgpos(struct nvkm_head *head, u16 *hline, u16 *vline);
extern const struct nvkm_head_func tu102_gsp_head;
#define HEAD_MSG(h,l,f,a...) do { \
struct nvkm_head *_h = (h); \
nvkm_##l(&_h->disp->engine.subdev, "head-%d: "f"\n", _h->id, ##a); \

View File

@ -194,6 +194,7 @@ void gv100_sor_dp_audio_sym(struct nvkm_ior *, int, u16, u32);
void gv100_sor_dp_watermark(struct nvkm_ior *, int, u8);
extern const struct nvkm_ior_func_hda gv100_sor_hda;
void tu102_sor_hdmi_gcp(struct nvkm_ior *, int, bool);
void tu102_sor_dp_vcpi(struct nvkm_ior *, int, u8, u8, u16, u16);
int nv50_pior_cnt(struct nvkm_disp *, unsigned long *);

View File

@ -5,6 +5,8 @@
#include <engine/disp.h>
#include <core/enum.h>
struct nvkm_head;
struct nvkm_head_func;
struct nvkm_ior;
struct nvkm_outp;
struct dcb_output;
@ -34,6 +36,20 @@ struct nvkm_disp_func {
int (*new)(struct nvkm_disp *, int id);
} wndw, head, dac, sor, pior;
/* Register programming that the GSP-RM display path (rm/r535) needs from
* the chip, everything else on that path goes through RM. The hooks are
* called unconditionally and the head table is handed to nvkm_head_new_().
*/
struct {
irqreturn_t (*intr)(struct nvkm_inth *);
/* Head-timing interrupts arrive on a second DISP vector. */
bool intr_low_latency;
const struct nvkm_head_func *head;
void (*hdmi_gcp)(struct nvkm_ior *, int head, bool enable);
void (*hdmi_infoframe_avi)(struct nvkm_ior *, int head, void *data, u32 size);
void (*hdmi_infoframe_vsi)(struct nvkm_ior *, int head, void *data, u32 size);
} gsp;
u16 ramht_size;
struct nvkm_sclass root;
@ -72,6 +88,7 @@ int gv100_disp_wndw_cnt(struct nvkm_disp *, unsigned long *);
int gv100_disp_caps_new(const struct nvkm_oclass *, void *, u32, struct nvkm_object **);
int tu102_disp_init(struct nvkm_disp *);
irqreturn_t tu102_disp_intr(struct nvkm_inth *);
void nv50_disp_dptmds_war_2(struct nvkm_disp *, struct dcb_output *);
void nv50_disp_dptmds_war_3(struct nvkm_disp *, struct dcb_output *);

View File

@ -30,6 +30,21 @@
#include <nvif/class.h>
/* General Control Packet: bracket an audio enable/disable with AVMute
* through the legacy GCP SF unit. Used by the GSP-RM path, which sends the
* equivalent packet via RM as well but keeps the direct write in sync.
*/
void
tu102_sor_hdmi_gcp(struct nvkm_ior *sor, int head, bool enable)
{
struct nvkm_device *device = sor->disp->engine.subdev.device;
const u32 hdmi = head * 0x400;
nvkm_mask(device, 0x6f00c0 + hdmi, 0x00000001, 0x00000000);
nvkm_wr32(device, 0x6f00cc + hdmi, !enable ? 0x00000001 : 0x00000010);
nvkm_mask(device, 0x6f00c0 + hdmi, 0x00000001, 0x00000001);
}
void
tu102_sor_dp_vcpi(struct nvkm_ior *sor, int head, u8 slot, u8 slot_nr, u16 pbn, u16 aligned)
{
@ -104,6 +119,64 @@ tu102_sor_new(struct nvkm_disp *disp, int id)
return nvkm_ior_new_(&tu102_sor, disp, SOR, id, hda & BIT(id));
}
/* The GSP-RM display path leaves head-timing (vblank) interrupts and their
* enables to us. These program the RM head-timing line (bit 1 of the
* per-head enable, not the bit nvkm's own gv100 path uses).
*/
static void
tu102_head_vblank_put(struct nvkm_head *head)
{
struct nvkm_device *device = head->disp->engine.subdev.device;
nvkm_mask(device, 0x611d80 + (head->id * 4), 0x00000002, 0x00000000);
}
static void
tu102_head_vblank_get(struct nvkm_head *head)
{
struct nvkm_device *device = head->disp->engine.subdev.device;
nvkm_wr32(device, 0x611800 + (head->id * 4), 0x00000002);
nvkm_mask(device, 0x611d80 + (head->id * 4), 0x00000002, 0x00000002);
}
const struct nvkm_head_func
tu102_gsp_head = {
.state = gv100_head_state,
.rgpos = gv100_head_rgpos,
.vblank_get = tu102_head_vblank_get,
.vblank_put = tu102_head_vblank_put,
};
static void
tu102_disp_intr_head_timing(struct nvkm_disp *disp, int head)
{
struct nvkm_subdev *subdev = &disp->engine.subdev;
struct nvkm_device *device = subdev->device;
u32 stat = nvkm_rd32(device, 0x611c00 + (head * 0x04));
if (stat & 0x00000002) {
nvkm_disp_vblank(disp, head);
nvkm_wr32(device, 0x611800 + (head * 0x04), 0x00000002);
}
}
irqreturn_t
tu102_disp_intr(struct nvkm_inth *inth)
{
struct nvkm_disp *disp = container_of(inth, typeof(*disp), engine.subdev.inth);
struct nvkm_subdev *subdev = &disp->engine.subdev;
struct nvkm_device *device = subdev->device;
unsigned long mask = nvkm_rd32(device, 0x611ec0) & 0x000000ff;
int head;
for_each_set_bit(head, &mask, 8)
tu102_disp_intr_head_timing(disp, head);
return IRQ_HANDLED;
}
int
tu102_disp_init(struct nvkm_disp *disp)
{
@ -230,12 +303,23 @@ tu102_disp = {
},
};
static const struct nvkm_disp_func
tu102_gsp_disp = {
.uevent = &gv100_disp_chan_uevent,
.ramht_size = 0x2000,
.gsp.intr = tu102_disp_intr,
.gsp.head = &tu102_gsp_head,
.gsp.hdmi_gcp = tu102_sor_hdmi_gcp,
.gsp.hdmi_infoframe_avi = gv100_sor_hdmi_infoframe_avi,
.gsp.hdmi_infoframe_vsi = gv100_sor_hdmi_infoframe_vsi,
};
int
tu102_disp_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst,
struct nvkm_disp **pdisp)
{
if (nvkm_gsp_rm(device->gsp))
return r535_disp_new(&tu102_disp, device, type, inst, pdisp);
return r535_disp_new(&tu102_gsp_disp, device, type, inst, pdisp);
return nvkm_disp_new_(&tu102_disp, device, type, inst, pdisp);
}

View File

@ -400,16 +400,16 @@ r535_sor_dp_audio(struct nvkm_ior *sor, int head, bool enable)
r535_sor_dp_audio_mute(sor, false);
}
static void
r535_sor_dp_vcpi(struct nvkm_ior *sor, int head, u8 slot, u8 slot_nr, u16 pbn, u16 aligned_pbn)
static int
r535_dp_vcpi(struct nvkm_ior *sor, int head, u8 slot, u8 slot_nr, u16 pbn, u16 aligned_pbn)
{
struct nvkm_disp *disp = sor->disp;
struct NV0073_CTRL_CMD_DP_CONFIG_STREAM_PARAMS *ctrl;
ctrl = nvkm_gsp_rm_ctrl_get(&disp->rm.objcom,
NV0073_CTRL_CMD_DP_CONFIG_STREAM, sizeof(*ctrl));
if (WARN_ON(IS_ERR(ctrl)))
return;
if (IS_ERR(ctrl))
return PTR_ERR(ctrl);
ctrl->subDeviceInstance = 0;
ctrl->head = head;
@ -429,12 +429,20 @@ r535_sor_dp_vcpi(struct nvkm_ior *sor, int head, u8 slot, u8 slot_nr, u16 pbn, u
ctrl->MST.sendACT = 0;
ctrl->MST.singleHeadMSTPipeline = 0;
ctrl->MST.bEnableAudioOverRightPanel = 0;
WARN_ON(nvkm_gsp_rm_ctrl_wr(&disp->rm.objcom, ctrl));
return nvkm_gsp_rm_ctrl_wr(&disp->rm.objcom, ctrl);
}
static void
r535_sor_dp_vcpi(struct nvkm_ior *sor, int head, u8 slot, u8 slot_nr, u16 pbn, u16 aligned_pbn)
{
const struct nvkm_rm_api *rmapi = sor->disp->engine.subdev.device->gsp->rm->api;
WARN_ON(rmapi->disp->dp.vcpi(sor, head, slot, slot_nr, pbn, aligned_pbn));
}
static int
r535_sor_dp_sst(struct nvkm_ior *sor, int head, bool ef,
u32 watermark, u32 hblanksym, u32 vblanksym)
r535_dp_sst(struct nvkm_ior *sor, int head, bool ef,
u32 watermark, u32 hblanksym, u32 vblanksym)
{
struct nvkm_disp *disp = sor->disp;
struct NV0073_CTRL_CMD_DP_CONFIG_STREAM_PARAMS *ctrl;
@ -461,6 +469,15 @@ r535_sor_dp_sst(struct nvkm_ior *sor, int head, bool ef,
return nvkm_gsp_rm_ctrl_wr(&disp->rm.objcom, ctrl);
}
static int
r535_sor_dp_sst(struct nvkm_ior *sor, int head, bool ef,
u32 watermark, u32 hblanksym, u32 vblanksym)
{
const struct nvkm_rm_api *rmapi = sor->disp->engine.subdev.device->gsp->rm->api;
return rmapi->disp->dp.sst(sor, head, ef, watermark, hblanksym, vblanksym);
}
static const struct nvkm_ior_func_dp
r535_sor_dp = {
.sst = r535_sor_dp_sst,
@ -545,16 +562,21 @@ r535_sor_hdmi_ctrl_audio(struct nvkm_outp *outp, bool enable)
static void
r535_sor_hdmi_audio(struct nvkm_ior *sor, int head, bool enable)
{
struct nvkm_device *device = sor->disp->engine.subdev.device;
const u32 hdmi = head * 0x400;
r535_sor_hdmi_ctrl_audio(sor->asy.outp, enable);
r535_sor_hdmi_ctrl_audio_mute(sor->asy.outp, !enable);
sor->disp->func->gsp.hdmi_gcp(sor, head, enable);
}
/* General Control (GCP). */
nvkm_mask(device, 0x6f00c0 + hdmi, 0x00000001, 0x00000000);
nvkm_wr32(device, 0x6f00cc + hdmi, !enable ? 0x00000001 : 0x00000010);
nvkm_mask(device, 0x6f00c0 + hdmi, 0x00000001, 0x00000001);
static void
r535_sor_hdmi_infoframe_avi(struct nvkm_ior *sor, int head, void *data, u32 size)
{
sor->disp->func->gsp.hdmi_infoframe_avi(sor, head, data, size);
}
static void
r535_sor_hdmi_infoframe_vsi(struct nvkm_ior *sor, int head, void *data, u32 size)
{
sor->disp->func->gsp.hdmi_infoframe_vsi(sor, head, data, size);
}
static void
@ -582,8 +604,8 @@ r535_sor_hdmi = {
.ctrl = r535_sor_hdmi_ctrl,
.scdc = r535_sor_hdmi_scdc,
/*TODO: SF_USER -> KMS. */
.infoframe_avi = gv100_sor_hdmi_infoframe_avi,
.infoframe_vsi = gv100_sor_hdmi_infoframe_vsi,
.infoframe_avi = r535_sor_hdmi_infoframe_avi,
.infoframe_vsi = r535_sor_hdmi_infoframe_vsi,
.audio = r535_sor_hdmi_audio,
};
@ -608,31 +630,6 @@ r535_sor_cnt(struct nvkm_disp *disp, unsigned long *pmask)
return 4;
}
static void
r535_head_vblank_put(struct nvkm_head *head)
{
struct nvkm_device *device = head->disp->engine.subdev.device;
nvkm_mask(device, 0x611d80 + (head->id * 4), 0x00000002, 0x00000000);
}
static void
r535_head_vblank_get(struct nvkm_head *head)
{
struct nvkm_device *device = head->disp->engine.subdev.device;
nvkm_wr32(device, 0x611800 + (head->id * 4), 0x00000002);
nvkm_mask(device, 0x611d80 + (head->id * 4), 0x00000002, 0x00000002);
}
static const struct nvkm_head_func
r535_head = {
.state = gv100_head_state,
.rgpos = gv100_head_rgpos,
.vblank_get = r535_head_vblank_get,
.vblank_put = r535_head_vblank_put,
};
static struct nvkm_conn *
r535_conn_new(struct nvkm_disp *disp, u32 id)
{
@ -1404,35 +1401,6 @@ static const struct nvkm_event_func
r535_disp_event = {
};
static void
r535_disp_intr_head_timing(struct nvkm_disp *disp, int head)
{
struct nvkm_subdev *subdev = &disp->engine.subdev;
struct nvkm_device *device = subdev->device;
u32 stat = nvkm_rd32(device, 0x611c00 + (head * 0x04));
if (stat & 0x00000002) {
nvkm_disp_vblank(disp, head);
nvkm_wr32(device, 0x611800 + (head * 0x04), 0x00000002);
}
}
static irqreturn_t
r535_disp_intr(struct nvkm_inth *inth)
{
struct nvkm_disp *disp = container_of(inth, typeof(*disp), engine.subdev.inth);
struct nvkm_subdev *subdev = &disp->engine.subdev;
struct nvkm_device *device = subdev->device;
unsigned long mask = nvkm_rd32(device, 0x611ec0) & 0x000000ff;
int head;
for_each_set_bit(head, &mask, 8)
r535_disp_intr_head_timing(disp, head);
return IRQ_HANDLED;
}
static void
r535_disp_fini(struct nvkm_disp *disp, bool suspend)
{
@ -1659,7 +1627,7 @@ r535_disp_oneinit(struct nvkm_disp *disp)
nvkm_gsp_rm_ctrl_done(&disp->rm.objcom, ctrl);
for_each_set_bit(i, &disp->head.mask, disp->head.nr) {
ret = nvkm_head_new_(&r535_head, disp, i);
ret = nvkm_head_new_(disp->func->gsp.head, disp, i);
if (ret)
return ret;
}
@ -1703,12 +1671,20 @@ r535_disp_oneinit(struct nvkm_disp *disp)
if (ret)
return ret;
ret = nvkm_gsp_intr_stall(gsp, disp->engine.subdev.type, disp->engine.subdev.inst);
/* Chips that raise head-timing interrupts on a separate low-latency
* vector report it as a second DISP interrupt table entry, exposed
* as instance 1 by the RM engine-index translation (see
* r570_gsp_xlat_mc_engine_idx()). Their high-latency vector
* (instance 0) is left unhandled as no event nouveau enables is
* routed to it, and without a handler it stays masked.
*/
ret = nvkm_gsp_intr_stall(gsp, disp->engine.subdev.type,
disp->func->gsp.intr_low_latency ? 1 : disp->engine.subdev.inst);
if (ret < 0)
return ret;
ret = nvkm_inth_add(&device->vfn->intr, ret, NVKM_INTR_PRIO_NORMAL, &disp->engine.subdev,
r535_disp_intr, &disp->engine.subdev.inth);
disp->func->gsp.intr, &disp->engine.subdev.inth);
if (ret)
return ret;
@ -1741,6 +1717,7 @@ r535_disp_new(const struct nvkm_disp_func *hw, struct nvkm_device *device,
rm->uevent = hw->uevent;
rm->sor.cnt = r535_sor_cnt;
rm->sor.new = r535_sor_new;
rm->gsp = hw->gsp;
rm->ramht_size = hw->ramht_size;
rm->root.oclass = gpu->disp.class.root;
@ -1782,6 +1759,8 @@ r535_disp = {
.dp = {
.get_caps = r535_dp_get_caps,
.set_indexed_link_rates = r535_dp_set_indexed_link_rates,
.sst = r535_dp_sst,
.vcpi = r535_dp_vcpi,
},
.chan = {
.set_pushbuf = r535_disp_chan_set_pushbuf,

View File

@ -5,6 +5,7 @@
#include <rm/rm.h>
#include <engine/disp.h>
#include <engine/disp/ior.h>
#include <engine/disp/outp.h>
#include "nvhw/drf.h"
@ -74,6 +75,67 @@ r570_disp_chan_set_pushbuf(struct nvkm_disp *disp, s32 oclass, int inst, struct
return nvkm_gsp_rm_ctrl_wr(&gsp->internal.device.subdevice, ctrl);
}
static int
r570_dp_vcpi(struct nvkm_ior *sor, int head, u8 slot, u8 slot_nr, u16 pbn, u16 aligned_pbn)
{
struct nvkm_disp *disp = sor->disp;
NV0073_CTRL_CMD_DP_CONFIG_STREAM_PARAMS *ctrl;
ctrl = nvkm_gsp_rm_ctrl_get(&disp->rm.objcom,
NV0073_CTRL_CMD_DP_CONFIG_STREAM, sizeof(*ctrl));
if (IS_ERR(ctrl))
return PTR_ERR(ctrl);
ctrl->subDeviceInstance = 0;
ctrl->head = head;
ctrl->sorIndex = sor->id;
ctrl->dpLink = sor->asy.link == 2;
ctrl->bEnableOverride = 1;
ctrl->bMST = 1;
ctrl->hBlankSym = 0;
ctrl->vBlankSym = 0;
ctrl->colorFormat = 0;
ctrl->bEnableTwoHeadOneOr = 0;
ctrl->singleHeadMultistreamMode = 0;
ctrl->MST.slotStart = slot;
ctrl->MST.slotEnd = slot + slot_nr - 1;
ctrl->MST.PBN = pbn;
ctrl->MST.Timeslice = aligned_pbn;
ctrl->MST.sendACT = 0;
ctrl->MST.singleHeadMSTPipeline = 0;
ctrl->MST.bEnableAudioOverRightPanel = 0;
return nvkm_gsp_rm_ctrl_wr(&disp->rm.objcom, ctrl);
}
static int
r570_dp_sst(struct nvkm_ior *sor, int head, bool ef,
u32 watermark, u32 hblanksym, u32 vblanksym)
{
struct nvkm_disp *disp = sor->disp;
NV0073_CTRL_CMD_DP_CONFIG_STREAM_PARAMS *ctrl;
ctrl = nvkm_gsp_rm_ctrl_get(&disp->rm.objcom,
NV0073_CTRL_CMD_DP_CONFIG_STREAM, sizeof(*ctrl));
if (IS_ERR(ctrl))
return PTR_ERR(ctrl);
ctrl->subDeviceInstance = 0;
ctrl->head = head;
ctrl->sorIndex = sor->id;
ctrl->dpLink = sor->asy.link == 2;
ctrl->bEnableOverride = 1;
ctrl->bMST = 0;
ctrl->hBlankSym = hblanksym;
ctrl->vBlankSym = vblanksym;
ctrl->colorFormat = 0;
ctrl->bEnableTwoHeadOneOr = 0;
ctrl->SST.bEnhancedFraming = ef;
ctrl->SST.tuSize = 64;
ctrl->SST.waterMark = watermark;
ctrl->SST.bEnableAudioOverRightPanel = 0;
return nvkm_gsp_rm_ctrl_wr(&disp->rm.objcom, ctrl);
}
static int
r570_dp_set_indexed_link_rates(struct nvkm_outp *outp)
{
@ -255,6 +317,8 @@ r570_disp = {
.dp = {
.get_caps = r570_dp_get_caps,
.set_indexed_link_rates = r570_dp_set_indexed_link_rates,
.sst = r570_dp_sst,
.vcpi = r570_dp_vcpi,
},
.chan = {
.set_pushbuf = r570_disp_chan_set_pushbuf,

View File

@ -44,6 +44,15 @@ r570_gsp_xlat_mc_engine_idx(u32 mc_engine_idx, enum nvkm_subdev_type *ptype, int
*ptype = NVKM_ENGINE_DISP;
*pinst = 0;
return true;
case MC_ENGINE_IDX_DISP_LOW:
/* GB20x+ report a separate low-latency display vector, used
* for head-timing interrupts. Expose it as a second DISP
* interrupt instance. r535_disp_oneinit() attaches the
* handler to it when the chip's gsp.intr_low_latency is set.
*/
*ptype = NVKM_ENGINE_DISP;
*pinst = 1;
return true;
case MC_ENGINE_IDX_CE0 ... MC_ENGINE_IDX_CE19:
*ptype = NVKM_ENGINE_CE;
*pinst = mc_engine_idx - MC_ENGINE_IDX_CE0;

View File

@ -256,6 +256,8 @@ typedef struct NV0073_CTRL_DP_CTRL_PARAMS {
NvU32 eightLaneDpcdBaseAddr;
} NV0073_CTRL_DP_CTRL_PARAMS;
#define NV0073_CTRL_CMD_DP_CONFIG_STREAM (0x731362U) /* finn: Evaluated from "(FINN_NV04_DISPLAY_COMMON_DP_INTERFACE_ID << 8) | NV0073_CTRL_CMD_DP_CONFIG_STREAM_PARAMS_MESSAGE_ID" */
typedef struct NV0073_CTRL_CMD_DP_CONFIG_STREAM_PARAMS {
NvU32 subDeviceInstance;
NvU32 head;

View File

@ -6,6 +6,7 @@
#ifndef __NVKM_RM_H__
#define __NVKM_RM_H__
#include "handles.h"
struct nvkm_ior;
struct nvkm_outp;
struct r535_gr;
@ -93,6 +94,10 @@ struct nvkm_rm_api {
struct {
int (*get_caps)(struct nvkm_disp *, int *link_bw, bool *mst, bool *wm);
int (*set_indexed_link_rates)(struct nvkm_outp *);
int (*sst)(struct nvkm_ior *, int head, bool ef,
u32 watermark, u32 hblanksym, u32 vblanksym);
int (*vcpi)(struct nvkm_ior *, int head,
u8 slot, u8 slot_nr, u16 pbn, u16 aligned_pbn);
} dp;
struct {

View File

@ -195,6 +195,9 @@ check_io_mapping(struct nv50_instmem *imem)
{
struct nvkm_device *device = imem->base.subdev.device;
if (imem->iomap.size)
return true;
return io_mapping_init_wc(&imem->iomap,
device->func->resource_addr(device, NVKM_BAR2_INST),
device->func->resource_size(device, NVKM_BAR2_INST)) != NULL;

View File

@ -2,6 +2,7 @@
#include <linux/aperture.h>
#include <linux/of_address.h>
#include <linux/overflow.h>
#include <linux/pci.h>
#include <linux/platform_device.h>
#include <linux/pm.h>
@ -238,7 +239,7 @@ static bool is_avivo(u32 vendor, u32 device)
/* This will match most R5xx */
return (vendor == PCI_VENDOR_ID_ATI) &&
((device >= PCI_VENDOR_ID_ATI_R520 && device < 0x7800) ||
(PCI_VENDOR_ID_ATI_R600 >= 0x9400));
(device >= PCI_VENDOR_ID_ATI_R600));
}
static enum ofdrm_model display_get_model_of(struct drm_device *dev, struct device_node *of_node)
@ -913,7 +914,10 @@ static struct ofdrm_device *ofdrm_device_create(struct drm_driver *drv,
return ERR_PTR(-EINVAL);
}
fb_size = linebytes * height;
if (check_mul_overflow(linebytes, height, &fb_size)) {
drm_err(dev, "framebuffer size exceeds maximum\n");
return ERR_PTR(-EINVAL);
}
/*
* Try to figure out the address of the framebuffer. Unfortunately, Open

View File

@ -904,6 +904,7 @@ static struct drm_plane *tegra_primary_plane_create(struct drm_device *drm,
struct tegra_dc *dc)
{
unsigned long possible_crtcs = tegra_plane_get_possible_crtcs(drm);
unsigned int blend_caps = BIT(DRM_MODE_BLEND_COVERAGE);
enum drm_plane_type type = DRM_PLANE_TYPE_PRIMARY;
struct tegra_plane *plane;
unsigned int num_formats;
@ -939,6 +940,7 @@ static struct drm_plane *tegra_primary_plane_create(struct drm_device *drm,
}
drm_plane_helper_add(&plane->base, &tegra_plane_helper_funcs);
drm_plane_create_blend_mode_property(&plane->base, blend_caps);
drm_plane_create_zpos_property(&plane->base, plane->index, 0, 255);
err = drm_plane_create_rotation_property(&plane->base,
@ -1209,6 +1211,7 @@ static struct drm_plane *tegra_dc_cursor_plane_create(struct drm_device *drm,
struct tegra_dc *dc)
{
unsigned long possible_crtcs = tegra_plane_get_possible_crtcs(drm);
unsigned int blend_caps = BIT(DRM_MODE_BLEND_COVERAGE);
struct tegra_plane *plane;
unsigned int num_formats;
const u32 *formats;
@ -1252,6 +1255,7 @@ static struct drm_plane *tegra_dc_cursor_plane_create(struct drm_device *drm,
}
drm_plane_helper_add(&plane->base, &tegra_cursor_plane_helper_funcs);
drm_plane_create_blend_mode_property(&plane->base, blend_caps);
drm_plane_create_zpos_immutable_property(&plane->base, 255);
return &plane->base;
@ -1356,6 +1360,7 @@ static struct drm_plane *tegra_dc_overlay_plane_create(struct drm_device *drm,
bool cursor)
{
unsigned long possible_crtcs = tegra_plane_get_possible_crtcs(drm);
unsigned int blend_caps = BIT(DRM_MODE_BLEND_COVERAGE);
struct tegra_plane *plane;
unsigned int num_formats;
enum drm_plane_type type;
@ -1394,6 +1399,7 @@ static struct drm_plane *tegra_dc_overlay_plane_create(struct drm_device *drm,
}
drm_plane_helper_add(&plane->base, &tegra_plane_helper_funcs);
drm_plane_create_blend_mode_property(&plane->base, blend_caps);
drm_plane_create_zpos_property(&plane->base, plane->index, 0, 255);
err = drm_plane_create_rotation_property(&plane->base,

View File

@ -759,6 +759,7 @@ struct drm_plane *tegra_shared_plane_create(struct drm_device *drm,
unsigned int index,
enum drm_plane_type type)
{
unsigned int blend_caps = BIT(DRM_MODE_BLEND_COVERAGE);
struct tegra_drm *tegra = drm->dev_private;
struct tegra_display_hub *hub = tegra->hub;
struct tegra_shared_plane *plane;
@ -797,6 +798,7 @@ struct drm_plane *tegra_shared_plane_create(struct drm_device *drm,
}
drm_plane_helper_add(p, &tegra_shared_plane_helper_funcs);
drm_plane_create_blend_mode_property(p, blend_caps);
drm_plane_create_zpos_property(p, 0, 0, 255);
return p;

View File

@ -582,6 +582,9 @@ static int cirrus_pci_probe(struct pci_dev *pdev,
struct cirrus_device *cirrus;
int ret;
if (pci_resource_len(pdev, 0) < CIRRUS_VRAM_SIZE)
return -ENODEV;
ret = aperture_remove_conflicting_pci_devices(pdev, cirrus_driver.name);
if (ret)
return ret;

View File

@ -344,7 +344,7 @@ virtio_gpu_user_framebuffer_create(struct drm_device *dev,
if (ret) {
kfree(virtio_gpu_fb);
drm_gem_object_put(obj);
return NULL;
return ERR_PTR(ret);
}
return &virtio_gpu_fb->base;
@ -378,8 +378,11 @@ int virtio_gpu_modeset_init(struct virtio_gpu_device *vgdev)
vgdev->ddev->mode_config.fb_modifiers_not_supported = true;
for (i = 0 ; i < vgdev->num_scanouts; ++i)
vgdev_output_init(vgdev, i);
for (i = 0; i < vgdev->num_scanouts; ++i) {
ret = vgdev_output_init(vgdev, i);
if (ret)
return ret;
}
ret = drm_vblank_init(vgdev->ddev, vgdev->num_scanouts);
if (ret)

View File

@ -43,6 +43,8 @@
#include <drm/drm_probe_helper.h>
#include <drm/virtgpu_drm.h>
#include <xen/xen.h>
#define DRIVER_NAME "virtio_gpu"
#define DRIVER_DESC "virtio GPU"
@ -60,6 +62,24 @@
/* See virtio_gpu_ctx_create. One additional character for NULL terminator. */
#define DEBUG_NAME_MAX_LEN 65
/*
* Whether the host must be told about resource backing pages by DMA address
* rather than guest-physical address.
*
* This mirrors vring_use_map_api() in drivers/virtio/virtio_ring.c, including
* its xen_domain() case.
*/
static inline bool virtio_gpu_use_dma_api(const struct virtio_device *vdev)
{
if (!virtio_has_dma_quirk(vdev))
return true;
if (xen_domain())
return true;
return false;
}
struct virtio_gpu_object_params {
unsigned long size;
bool dumb;
@ -343,6 +363,7 @@ void virtio_gpu_array_put_free_work(struct work_struct *work);
/* virtgpu_vq.c */
int virtio_gpu_alloc_vbufs(struct virtio_gpu_device *vgdev);
void virtio_gpu_free_vbufs(struct virtio_gpu_device *vgdev);
void virtio_gpu_reclaim_vbufs(struct virtio_gpu_device *vgdev);
void virtio_gpu_cmd_create_resource(struct virtio_gpu_device *vgdev,
struct virtio_gpu_object *bo,
struct virtio_gpu_object_params *params,

View File

@ -352,6 +352,7 @@ void virtio_gpu_deinit(struct drm_device *dev)
flush_work(&vgdev->cursorq.dequeue_work);
flush_work(&vgdev->config_changed_work);
virtio_reset_device(vgdev->vdev);
virtio_gpu_reclaim_vbufs(vgdev);
vgdev->vdev->config->del_vqs(vgdev->vdev);
mutex_destroy(&vgdev->obj_restore_lock);
}

View File

@ -173,7 +173,7 @@ static int virtio_gpu_object_shmem_init(struct virtio_gpu_device *vgdev,
struct virtio_gpu_mem_entry **ents,
unsigned int *nents)
{
bool use_dma_api = !virtio_has_dma_quirk(vgdev->vdev);
bool use_dma_api = virtio_gpu_use_dma_api(vgdev->vdev);
struct scatterlist *sg;
struct sg_table *pages;
int si;

View File

@ -208,6 +208,21 @@ static void free_vbuf(struct virtio_gpu_device *vgdev,
kmem_cache_free(vgdev->vbufs, vbuf);
}
void virtio_gpu_reclaim_vbufs(struct virtio_gpu_device *vgdev)
{
struct virtio_gpu_vbuffer *vbuf;
while ((vbuf = virtqueue_detach_unused_buf(vgdev->ctrlq.vq))) {
if (vbuf->objs)
virtio_gpu_array_put_free(vbuf->objs);
if (vbuf->resp_cb_data)
virtio_gpu_cleanup_object(vbuf->resp_cb_data);
free_vbuf(vgdev, vbuf);
}
while ((vbuf = virtqueue_detach_unused_buf(vgdev->cursorq.vq)))
free_vbuf(vgdev, vbuf);
}
static void reclaim_vbufs(struct virtqueue *vq, struct list_head *reclaim_list)
{
struct virtio_gpu_vbuffer *vbuf;
@ -764,7 +779,7 @@ int virtio_gpu_panic_cmd_transfer_to_host_2d(struct virtio_gpu_device *vgdev,
struct virtio_gpu_object *bo = gem_to_virtio_gpu_obj(objs->objs[0]);
struct virtio_gpu_transfer_to_host_2d *cmd_p;
struct virtio_gpu_vbuffer *vbuf;
bool use_dma_api = !virtio_has_dma_quirk(vgdev->vdev);
bool use_dma_api = virtio_gpu_use_dma_api(vgdev->vdev);
if (virtio_gpu_is_shmem(bo) && use_dma_api)
dma_sync_sgtable_for_device(vgdev->vdev->dev.parent,
@ -795,7 +810,7 @@ void virtio_gpu_cmd_transfer_to_host_2d(struct virtio_gpu_device *vgdev,
struct virtio_gpu_object *bo = gem_to_virtio_gpu_obj(objs->objs[0]);
struct virtio_gpu_transfer_to_host_2d *cmd_p;
struct virtio_gpu_vbuffer *vbuf;
bool use_dma_api = !virtio_has_dma_quirk(vgdev->vdev);
bool use_dma_api = virtio_gpu_use_dma_api(vgdev->vdev);
if (virtio_gpu_is_shmem(bo) && use_dma_api)
dma_sync_sgtable_for_device(vgdev->vdev->dev.parent,
@ -1228,7 +1243,7 @@ void virtio_gpu_cmd_transfer_to_host_3d(struct virtio_gpu_device *vgdev,
struct virtio_gpu_object *bo = gem_to_virtio_gpu_obj(objs->objs[0]);
struct virtio_gpu_transfer_host_3d *cmd_p;
struct virtio_gpu_vbuffer *vbuf;
bool use_dma_api = !virtio_has_dma_quirk(vgdev->vdev);
bool use_dma_api = virtio_gpu_use_dma_api(vgdev->vdev);
if (virtio_gpu_is_shmem(bo) && use_dma_api)
dma_sync_sgtable_for_device(vgdev->vdev->dev.parent,

View File

@ -2435,9 +2435,9 @@ int xe_oa_add_config_ioctl(struct drm_device *dev, u64 data, struct drm_file *fi
oa_config->id = idr_alloc(&oa->metrics_idr, oa_config, 1, 0, GFP_KERNEL);
if (oa_config->id < 0) {
drm_dbg(&oa->xe->drm, "Failed to create sysfs entry for OA config\n");
drm_dbg(&oa->xe->drm, "Failed to allocate id for OA config\n");
err = oa_config->id;
goto sysfs_err;
goto id_alloc_err;
}
id = oa_config->id;
@ -2448,6 +2448,8 @@ int xe_oa_add_config_ioctl(struct drm_device *dev, u64 data, struct drm_file *fi
return id;
id_alloc_err:
sysfs_remove_group(oa->metrics_kobj, &oa_config->sysfs_metric);
sysfs_err:
mutex_unlock(&oa->metrics_lock);
reg_err:

View File

@ -90,6 +90,9 @@ static int get_flat_ccs_offset(struct xe_gt *gt, u64 tile_size, u64 *poffset)
offset |= offset_lo << 6; /* HW view bits 31:6 */
offset *= num_enabled; /* convert to SW view */
drm_info(&xe->drm, "FLAT_CCS base:%llx, aligned:%s\n", offset,
str_yes_no(IS_ALIGNED(offset, SZ_128K)));
/*
* Everything below this offset is handed to the VRAM
* allocator, so it has to be the *first* address the

View File

@ -1822,24 +1822,20 @@ static int fastrpc_dmabuf_alloc(struct fastrpc_user *fl, char __user *argp)
return err;
}
bp.fd = dma_buf_fd(buf->dmabuf, O_ACCMODE);
bp.fd = get_unused_fd_flags(O_ACCMODE);
if (bp.fd < 0) {
dma_buf_put(buf->dmabuf);
return -EINVAL;
return bp.fd;
}
if (copy_to_user(argp, &bp, sizeof(bp))) {
/*
* The usercopy failed, but we can't do much about it, as
* dma_buf_fd() already called fd_install() and made the
* file descriptor accessible for the current process. It
* might already be closed and dmabuf no longer valid when
* we reach this point. Therefore "leak" the fd and rely on
* the process exit path to do any required cleanup.
*/
put_unused_fd(bp.fd);
dma_buf_put(buf->dmabuf);
return -EFAULT;
}
dma_buf_fd_install(buf->dmabuf, bp.fd);
return 0;
}

View File

@ -2,6 +2,7 @@
#ifndef _DRM_PAGEMAP_H_
#define _DRM_PAGEMAP_H_
#include <linux/bits.h>
#include <linux/dma-direction.h>
#include <linux/hmm.h>
#include <linux/memremap.h>
@ -339,6 +340,9 @@ struct drm_pagemap_migrate_details {
#if IS_ENABLED(CONFIG_ZONE_DEVICE)
#define DRM_PAGEMAP_ZDD_FLAG_MIGRATED BIT(0)
#define DRM_PAGEMAP_ZDD_FLAG_MASK DRM_PAGEMAP_ZDD_FLAG_MIGRATED
int drm_pagemap_migrate_to_devmem(struct drm_pagemap_devmem *devmem_allocation,
struct mm_struct *mm,
unsigned long start, unsigned long end,
@ -373,7 +377,9 @@ static inline struct drm_pagemap_zdd *drm_pagemap_page_zone_device_data(struct p
{
struct folio *folio = page_folio(page);
return folio_zone_device_data(folio);
return (struct drm_pagemap_zdd *)
((unsigned long)folio_zone_device_data(folio) &
~DRM_PAGEMAP_ZDD_FLAG_MASK);
}
#else

View File

@ -567,6 +567,7 @@ void dma_buf_unpin(struct dma_buf_attachment *attach);
struct dma_buf *dma_buf_export(const struct dma_buf_export_info *exp_info);
int dma_buf_fd(struct dma_buf *dmabuf, int flags);
void dma_buf_fd_install(struct dma_buf *dmabuf, int fd);
struct dma_buf *dma_buf_get(int fd);
void dma_buf_put(struct dma_buf *dmabuf);

View File

@ -28,7 +28,6 @@ struct dma_fence_array_cb {
/**
* struct dma_fence_array - fence to represent an array of fences
* @base: fence base class
* @lock: spinlock for fence handling
* @num_fences: number of fences in the array
* @num_pending: fences in the array still pending
* @fences: array of the fences

View File

@ -20,7 +20,6 @@
* @prev: previous fence of the chain
* @prev_seqno: original previous seqno before garbage collection
* @fence: encapsulated fence
* @lock: spinlock for fence handling
*/
struct dma_fence_chain {
struct dma_fence base;
@ -81,9 +80,8 @@ dma_fence_chain_contained(struct dma_fence *fence)
}
/**
* dma_fence_chain_alloc
*
* Returns a new struct dma_fence_chain object or NULL on failure.
* dma_fence_chain_alloc - Returns a new &struct dma_fence_chain object or
* %NULL on failure.
*
* This specialized allocator has to be a macro for its allocations to be
* accounted separately (to have a separate alloc_tag). The typecast is
@ -93,7 +91,8 @@ dma_fence_chain_contained(struct dma_fence *fence)
kmalloc_obj(struct dma_fence_chain)
/**
* dma_fence_chain_free
* dma_fence_chain_free - Frees an allocated but not used
* &struct dma_fence_chain object.
* @chain: chain node to free
*
* Frees up an allocated but not used struct dma_fence_chain object. This

View File

@ -1512,6 +1512,7 @@ struct drm_amdgpu_info_device {
__u64 high_va_max;
/* gfx10 pa_sc_tile_steering_override */
__u32 pa_sc_tile_steering_override;
__u32 pad;
/* disabled TCCs */
__u64 tcc_disabled_mask;
__u64 min_engine_clock;
@ -1536,7 +1537,6 @@ struct drm_amdgpu_info_device {
__u32 csa_alignment;
/* Userq IP mask (1 << AMDGPU_HW_IP_*) */
__u32 userq_ip_mask;
__u32 pad;
};
struct drm_amdgpu_info_hw_ip {

View File

@ -390,6 +390,116 @@ static void test_alloc_errors(char *heap_name)
close(heap_fd);
}
/*
* count_open_fds - return the number of open file descriptors.
*
* The fd opened by opendir() itself is counted, but since it is opened
* and closed within each call, it cancels out when comparing two counts.
* Returns -1 on error.
*/
static int count_open_fds(void)
{
DIR *d = opendir("/proc/self/fd");
struct dirent *de;
int count = 0;
if (!d)
return -1;
while ((de = readdir(d)))
if (de->d_name[0] != '.')
count++;
closedir(d);
return count;
}
/*
* test_alloc_no_fd_leak_on_efault - verify no fd is leaked when
* copy_to_user() fails during DMA_HEAP_IOCTL_ALLOC.
*
* The bug: dma_buf_fd() called fd_install() before copy_to_user().
* If copy_to_user() then failed (e.g. via mprotect), the fd was
* silently installed in the fd table but never returned to userspace.
*
* The fix: reserve the fd with get_unused_fd_flags() first, attempt
* copy_to_user(), and only call fd_install() on success.
*
* We trigger the failure by placing the ioctl argument in a private
* anonymous page and flipping it to PROT_READ before the ioctl.
* Inside the kernel, copy_from_user() reads from the page (reads are
* allowed under PROT_READ, so it succeeds), but copy_to_user() that
* writes the fd number back faults, returning -EFAULT. We then
* count open file descriptors before and after; with the bug an extra
* fd is left in the table.
*/
static void test_alloc_no_fd_leak_on_efault(char *heap_name)
{
int heap_fd = -1;
int fd_before, fd_after;
int ret;
long page_size;
struct dma_heap_allocation_data *req;
ksft_print_msg("Testing fd leak when copy_to_user() fails:\n");
heap_fd = dmabuf_heap_open(heap_name);
page_size = sysconf(_SC_PAGESIZE);
/*
* Place the ioctl argument in its own private anonymous page so
* we can flip its protection independently.
*/
req = mmap(NULL, page_size, PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
if (req == MAP_FAILED) {
ksft_test_result_fail("mmap failed: %s\n", strerror(errno));
goto out;
}
memset(req, 0, sizeof(*req));
req->len = page_size;
req->fd_flags = O_RDWR | O_CLOEXEC;
fd_before = count_open_fds();
if (fd_before < 0) {
ksft_test_result_fail("count_open_fds: %s\n", strerror(errno));
munmap(req, page_size);
goto out;
}
/*
* Make the page read-only so copy_to_user() will fault. The
* ioctl must fail with -1; if it returns success the test setup
* is broken (mprotect is synchronous, so there is no race).
*/
mprotect(req, page_size, PROT_READ);
ret = ioctl(heap_fd, DMA_HEAP_IOCTL_ALLOC, req);
/* Re-allow writes so munmap can clean up */
mprotect(req, page_size, PROT_READ | PROT_WRITE);
munmap(req, page_size);
if (ret != -1) {
ksft_test_result_fail("ioctl returned %d, expected -1 EFAULT\n",
ret);
goto out;
}
fd_after = count_open_fds();
if (fd_after < 0) {
ksft_test_result_fail("count_open_fds: %s\n", strerror(errno));
goto out;
}
ksft_test_result(fd_before == fd_after,
"fd leak on EFAULT: before=%d after=%d\n",
fd_before, fd_after);
out:
close(heap_fd);
}
static int numer_of_heaps(void)
{
DIR *d = opendir(DEVPATH);
@ -420,7 +530,7 @@ int main(void)
return KSFT_SKIP;
}
ksft_set_plan(11 * numer_of_heaps());
ksft_set_plan(12 * numer_of_heaps());
while ((dir = readdir(d))) {
if (!strncmp(dir->d_name, ".", 2))
@ -435,6 +545,7 @@ int main(void)
test_alloc_zeroed(dir->d_name, ONE_MEG);
test_alloc_compat(dir->d_name);
test_alloc_errors(dir->d_name);
test_alloc_no_fd_leak_on_efault(dir->d_name);
}
closedir(d);