summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDave Airlie <airlied@redhat.com>2026-05-25 23:51:30 +0300
committerDave Airlie <airlied@redhat.com>2026-05-25 23:51:31 +0300
commit40149d887c2d2825dc039261e2c7339dc02e7b58 (patch)
tree63b5a0ce750c0bac69972260551e13b48f07093a
parent4b18a9d44e38c9aa63ef8bff8658ca142e89c343 (diff)
parentbfb76105b864af5553263a2156eecd7f08d7810b (diff)
downloadlinux-40149d887c2d2825dc039261e2c7339dc02e7b58.tar.xz
Merge tag 'drm-misc-next-2026-05-21' of https://gitlab.freedesktop.org/drm/misc/kernel into drm-next
drm-misc-next for v7.2-rc1: UAPI Changes: - Add VIRTIO_GPU_F_BLOB_ALIGNMENT flag. Cross-subsystem Changes: - Add common TMDS character rate constants to video/hdmi and use those in bridge drivers. Core Changes: - Fix leak in drm_syncobj_find_fence. - Fix OOB reads related to DP-MST. - Create drm_get_bridge_by_endpoint and convert drivers to use it in preparation of hotplug. Driver Changes: - Assorted bugfixes and cleanups to accel/ethosu, imagination, virtio, rockchip. - Expandable device heap support to amdxdna, bridge/chipone-icn6211. - Add Surface Pro 12 panels. - Convert ite-it6211 to use drm hdmi audio helpers. Signed-off-by: Dave Airlie <airlied@redhat.com> From: Maarten Lankhorst <maarten.lankhorst@linux.intel.com> Link: https://patch.msgid.link/f4034e3c-8290-49e1-9410-dc1f449265f4@linux.intel.com
-rw-r--r--drivers/accel/amdxdna/aie2_ctx.c45
-rw-r--r--drivers/accel/amdxdna/aie2_message.c52
-rw-r--r--drivers/accel/amdxdna/aie2_msg_priv.h1
-rw-r--r--drivers/accel/amdxdna/aie2_pci.c1
-rw-r--r--drivers/accel/amdxdna/aie2_pci.h8
-rw-r--r--drivers/accel/amdxdna/amdxdna_ctx.c82
-rw-r--r--drivers/accel/amdxdna/amdxdna_ctx.h2
-rw-r--r--drivers/accel/amdxdna/amdxdna_gem.c326
-rw-r--r--drivers/accel/amdxdna/amdxdna_gem.h16
-rw-r--r--drivers/accel/amdxdna/amdxdna_pci_drv.c12
-rw-r--r--drivers/accel/amdxdna/amdxdna_pci_drv.h8
-rw-r--r--drivers/accel/amdxdna/npu1_regs.c1
-rw-r--r--drivers/accel/amdxdna/npu4_regs.c2
-rw-r--r--drivers/accel/amdxdna/npu5_regs.c1
-rw-r--r--drivers/accel/amdxdna/npu6_regs.c1
-rw-r--r--drivers/accel/ethosu/ethosu_job.c18
-rw-r--r--drivers/gpu/drm/bridge/adv7511/adv7511.h1
-rw-r--r--drivers/gpu/drm/bridge/adv7511/adv7511_drv.c15
-rw-r--r--drivers/gpu/drm/bridge/chipone-icn6211.c11
-rw-r--r--drivers/gpu/drm/bridge/chrontel-ch7033.c28
-rw-r--r--drivers/gpu/drm/bridge/inno-hdmi.c4
-rw-r--r--drivers/gpu/drm/bridge/ite-it66121.c138
-rw-r--r--drivers/gpu/drm/bridge/lontium-lt8713sx.c10
-rw-r--r--drivers/gpu/drm/bridge/lontium-lt9611.c9
-rw-r--r--drivers/gpu/drm/bridge/lontium-lt9611uxc.c9
-rw-r--r--drivers/gpu/drm/bridge/synopsys/dw-hdmi-qp.c6
-rw-r--r--drivers/gpu/drm/bridge/synopsys/dw-hdmi.c10
-rw-r--r--drivers/gpu/drm/display/drm_dp_mst_topology.c23
-rw-r--r--drivers/gpu/drm/drm_bridge.c45
-rw-r--r--drivers/gpu/drm/drm_of.c26
-rw-r--r--drivers/gpu/drm/drm_syncobj.c10
-rw-r--r--drivers/gpu/drm/hisilicon/kirin/dw_drm_dsi.c9
-rw-r--r--drivers/gpu/drm/imagination/Makefile3
-rw-r--r--drivers/gpu/drm/imagination/pvr_context.c30
-rw-r--r--drivers/gpu/drm/imagination/pvr_device.c2
-rw-r--r--drivers/gpu/drm/imagination/pvr_drv.c3
-rw-r--r--drivers/gpu/drm/imagination/pvr_fw.c4
-rw-r--r--drivers/gpu/drm/imagination/pvr_fw.h7
-rw-r--r--drivers/gpu/drm/imagination/pvr_job.c3
-rw-r--r--drivers/gpu/drm/imagination/pvr_mmu.c4
-rw-r--r--drivers/gpu/drm/imagination/pvr_power.c10
-rw-r--r--drivers/gpu/drm/imagination/pvr_queue.c12
-rw-r--r--drivers/gpu/drm/imagination/pvr_trace.h113
-rw-r--r--drivers/gpu/drm/imagination/pvr_trace_points.c7
-rw-r--r--drivers/gpu/drm/msm/hdmi/hdmi.c70
-rw-r--r--drivers/gpu/drm/msm/hdmi/hdmi_phy_8996.c6
-rw-r--r--drivers/gpu/drm/msm/hdmi/hdmi_phy_8998.c6
-rw-r--r--drivers/gpu/drm/panel/panel-edp.c1
-rw-r--r--drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c25
-rw-r--r--drivers/gpu/drm/sti/sti_hdmi_tx3g4c28phy.c6
-rw-r--r--drivers/gpu/drm/sun4i/sun4i_hdmi_enc.c4
-rw-r--r--drivers/gpu/drm/virtio/virtgpu_drv.c1
-rw-r--r--drivers/gpu/drm/virtio/virtgpu_drv.h3
-rw-r--r--drivers/gpu/drm/virtio/virtgpu_gem.c17
-rw-r--r--drivers/gpu/drm/virtio/virtgpu_ioctl.c10
-rw-r--r--drivers/gpu/drm/virtio/virtgpu_kms.c14
-rw-r--r--drivers/gpu/drm/virtio/virtgpu_plane.c10
-rw-r--r--drivers/gpu/drm/xlnx/zynqmp_dp.c19
-rw-r--r--include/drm/drm_bridge.h7
-rw-r--r--include/linux/hdmi.h6
-rw-r--r--include/uapi/drm/virtgpu_drm.h1
-rw-r--r--include/uapi/linux/virtio_gpu.h9
62 files changed, 973 insertions, 370 deletions
diff --git a/drivers/accel/amdxdna/aie2_ctx.c b/drivers/accel/amdxdna/aie2_ctx.c
index 7d6094aefb6f..658a5fb1fda6 100644
--- a/drivers/accel/amdxdna/aie2_ctx.c
+++ b/drivers/accel/amdxdna/aie2_ctx.c
@@ -91,6 +91,7 @@ static void aie2_hwctx_stop(struct amdxdna_dev *xdna, struct amdxdna_hwctx *hwct
static int aie2_hwctx_restart(struct amdxdna_dev *xdna, struct amdxdna_hwctx *hwctx)
{
struct amdxdna_gem_obj *heap = hwctx->priv->heap;
+ unsigned long heap_id;
int ret;
ret = aie2_create_context(xdna->dev_handle, hwctx);
@@ -107,6 +108,17 @@ static int aie2_hwctx_restart(struct amdxdna_dev *xdna, struct amdxdna_hwctx *hw
goto out;
}
+ xa_for_each_range(&hwctx->client->dev_heap_xa, heap_id, heap, 1,
+ hwctx->last_attached_heap) {
+ ret = aie2_add_host_buf(xdna->dev_handle, hwctx->fw_ctx_id,
+ amdxdna_obj_dma_addr(heap),
+ heap->mem.size);
+ if (ret) {
+ XDNA_ERR(xdna, "Add heap %ld failed ret %d", heap_id, ret);
+ goto out;
+ }
+ }
+
ret = aie2_config_cu(hwctx, NULL);
if (ret) {
XDNA_ERR(xdna, "Config cu failed, ret %d", ret);
@@ -650,7 +662,7 @@ int aie2_hwctx_init(struct amdxdna_hwctx *hwctx)
hwctx->priv = priv;
mutex_lock(&client->mm_lock);
- heap = client->dev_heap;
+ heap = xa_load(&client->dev_heap_xa, 0);
if (!heap) {
XDNA_ERR(xdna, "The client dev heap object not exist");
mutex_unlock(&client->mm_lock);
@@ -732,6 +744,12 @@ int aie2_hwctx_init(struct amdxdna_hwctx *hwctx)
goto release_resource;
}
+ ret = amdxdna_update_heap(client, hwctx);
+ if (ret) {
+ XDNA_ERR(xdna, "Update heap failed, ret %d", ret);
+ goto release_resource;
+ }
+
ret = aie2_ctx_syncobj_create(hwctx);
if (ret) {
XDNA_ERR(xdna, "Create syncobj failed, ret %d", ret);
@@ -1161,3 +1179,28 @@ void aie2_hmm_invalidate(struct amdxdna_gem_obj *abo,
else if (ret == -ERESTARTSYS)
XDNA_DBG(xdna, "Wait for bo interrupted by signal");
}
+
+int aie2_hwctx_heap_expand(struct amdxdna_hwctx *hwctx,
+ struct amdxdna_gem_obj *heap)
+{
+ struct amdxdna_client *client = hwctx->client;
+ struct amdxdna_dev *xdna = client->xdna;
+ u64 addr;
+ int ret;
+
+ ret = amdxdna_pm_resume_get_locked(xdna);
+ if (ret)
+ return ret;
+
+ addr = amdxdna_obj_dma_addr(heap);
+ ret = aie2_add_host_buf(xdna->dev_handle, hwctx->fw_ctx_id,
+ addr, heap->mem.size);
+ if (ret) {
+ XDNA_ERR(xdna, "Add heap failed hwctx %s 0x%lx ret %d",
+ hwctx->name, heap->mem.size, ret);
+ }
+
+ amdxdna_pm_suspend_put(xdna);
+
+ return ret;
+}
diff --git a/drivers/accel/amdxdna/aie2_message.c b/drivers/accel/amdxdna/aie2_message.c
index 1504295a21e4..c4b364801cc0 100644
--- a/drivers/accel/amdxdna/aie2_message.c
+++ b/drivers/accel/amdxdna/aie2_message.c
@@ -301,25 +301,59 @@ int aie2_destroy_context(struct amdxdna_dev_hdl *ndev, struct amdxdna_hwctx *hwc
return ret;
}
-int aie2_map_host_buf(struct amdxdna_dev_hdl *ndev, u32 context_id, u64 addr, u64 size)
+static int aie2_send_host_buf_msgs(struct amdxdna_dev_hdl *ndev, u32 context_id,
+ u64 addr, u64 size, u32 initial_opcode)
{
DECLARE_AIE_MSG(map_host_buffer, MSG_OP_MAP_HOST_BUFFER);
struct amdxdna_dev *xdna = ndev->aie.xdna;
+ size_t chunk_size;
int ret;
- req.context_id = context_id;
- req.buf_addr = addr;
- req.buf_size = size;
- ret = aie_send_mgmt_msg_wait(&ndev->aie, &msg);
- if (ret)
- return ret;
+ chunk_size = xdna->dev_info->dev_mem_size;
+ if (!size || !IS_ALIGNED(size, chunk_size)) {
+ XDNA_ERR(xdna, "Invalid size 0x%llx for chunk 0x%lx",
+ size, chunk_size);
+ return -EINVAL;
+ }
- XDNA_DBG(xdna, "fw ctx %d map host buf addr 0x%llx size 0x%llx",
- context_id, addr, size);
+ msg.opcode = initial_opcode;
+ do {
+ req.context_id = context_id;
+ req.buf_addr = addr;
+ req.buf_size = chunk_size;
+ ret = aie_send_mgmt_msg_wait(&ndev->aie, &msg);
+ if (ret) {
+ XDNA_ERR(xdna, "fw ctx %d addr 0x%llx size 0x%lx",
+ context_id, addr, chunk_size);
+ return ret;
+ }
+
+ XDNA_DBG(xdna, "fw ctx %d host buf op 0x%x addr 0x%llx size 0x%lx",
+ context_id, msg.opcode, addr, chunk_size);
+
+ addr += chunk_size;
+ size -= chunk_size;
+ msg.opcode = MSG_OP_ADD_HOST_BUFFER;
+ } while (size);
return 0;
}
+int aie2_map_host_buf(struct amdxdna_dev_hdl *ndev, u32 context_id, u64 addr, u64 size)
+{
+ return aie2_send_host_buf_msgs(ndev, context_id, addr, size,
+ MSG_OP_MAP_HOST_BUFFER);
+}
+
+int aie2_add_host_buf(struct amdxdna_dev_hdl *ndev, u32 context_id, u64 addr, u64 size)
+{
+ if (!AIE_FEATURE_ON(&ndev->aie, AIE2_ADD_HOST_BUFFER))
+ return -EOPNOTSUPP;
+
+ return aie2_send_host_buf_msgs(ndev, context_id, addr, size,
+ MSG_OP_ADD_HOST_BUFFER);
+}
+
static int amdxdna_hwctx_col_map(struct amdxdna_hwctx *hwctx, void *arg)
{
u32 *bitmap = arg;
diff --git a/drivers/accel/amdxdna/aie2_msg_priv.h b/drivers/accel/amdxdna/aie2_msg_priv.h
index a41c9797e265..fd65a4236d49 100644
--- a/drivers/accel/amdxdna/aie2_msg_priv.h
+++ b/drivers/accel/amdxdna/aie2_msg_priv.h
@@ -33,6 +33,7 @@ enum aie2_msg_opcode {
MSG_OP_REGISTER_ASYNC_EVENT_MSG = 0x10C,
MSG_OP_UPDATE_PROPERTY = 0x113,
MSG_OP_GET_APP_HEALTH = 0x114,
+ MSG_OP_ADD_HOST_BUFFER = 0x115,
MSG_OP_GET_DEV_REVISION = 0x117,
MSG_OP_MAX_DRV_OPCODE,
MSG_OP_GET_PROTOCOL_VERSION = 0x301,
diff --git a/drivers/accel/amdxdna/aie2_pci.c b/drivers/accel/amdxdna/aie2_pci.c
index 6c8a0f70b73d..c4d345d4c76b 100644
--- a/drivers/accel/amdxdna/aie2_pci.c
+++ b/drivers/accel/amdxdna/aie2_pci.c
@@ -1241,4 +1241,5 @@ const struct amdxdna_dev_ops aie2_ops = {
.hmm_invalidate = aie2_hmm_invalidate,
.get_array = aie2_get_array,
.get_dev_revision = aie2_get_dev_rev,
+ .hwctx_heap_expand = aie2_hwctx_heap_expand,
};
diff --git a/drivers/accel/amdxdna/aie2_pci.h b/drivers/accel/amdxdna/aie2_pci.h
index 84bdb3f8b8f9..77648cc548b6 100644
--- a/drivers/accel/amdxdna/aie2_pci.h
+++ b/drivers/accel/amdxdna/aie2_pci.h
@@ -15,8 +15,9 @@
#include "amdxdna_mailbox.h"
/* Firmware determines device memory base address and size */
-#define AIE2_DEVM_BASE 0x4000000
-#define AIE2_DEVM_SIZE SZ_64M
+#define AIE2_DEVM_BASE 0x4000000
+#define AIE2_DEVM_SIZE SZ_64M
+#define AIE2_DEVM_MAX_SIZE SZ_512M
#define NDEV2PDEV(ndev) (to_pci_dev((ndev)->aie.xdna->ddev.dev))
@@ -198,6 +199,7 @@ enum aie2_fw_feature {
AIE2_PREEMPT,
AIE2_TEMPORAL_ONLY,
AIE2_APP_HEALTH,
+ AIE2_ADD_HOST_BUFFER,
AIE2_UPDATE_PROPERTY,
AIE2_GET_DEV_REVISION,
AIE2_FEATURE_MAX
@@ -271,6 +273,7 @@ int aie2_get_dev_revision(struct amdxdna_dev_hdl *ndev, enum aie2_dev_revision *
int aie2_create_context(struct amdxdna_dev_hdl *ndev, struct amdxdna_hwctx *hwctx);
int aie2_destroy_context(struct amdxdna_dev_hdl *ndev, struct amdxdna_hwctx *hwctx);
int aie2_map_host_buf(struct amdxdna_dev_hdl *ndev, u32 context_id, u64 addr, u64 size);
+int aie2_add_host_buf(struct amdxdna_dev_hdl *ndev, u32 context_id, u64 addr, u64 size);
int aie2_query_status(struct amdxdna_dev_hdl *ndev, char __user *buf, u32 size, u32 *cols_filled);
int aie2_query_telemetry(struct amdxdna_dev_hdl *ndev,
char __user *buf, u32 size,
@@ -302,5 +305,6 @@ void aie2_hwctx_suspend(struct amdxdna_client *client);
int aie2_hwctx_resume(struct amdxdna_client *client);
int aie2_cmd_submit(struct amdxdna_hwctx *hwctx, struct amdxdna_sched_job *job, u64 *seq);
void aie2_hmm_invalidate(struct amdxdna_gem_obj *abo, unsigned long cur_seq);
+int aie2_hwctx_heap_expand(struct amdxdna_hwctx *hwctx, struct amdxdna_gem_obj *heap);
#endif /* _AIE2_PCI_H_ */
diff --git a/drivers/accel/amdxdna/amdxdna_ctx.c b/drivers/accel/amdxdna/amdxdna_ctx.c
index b79229a63af3..855da8c79a1c 100644
--- a/drivers/accel/amdxdna/amdxdna_ctx.c
+++ b/drivers/accel/amdxdna/amdxdna_ctx.c
@@ -61,16 +61,35 @@ static struct dma_fence *amdxdna_fence_create(struct amdxdna_hwctx *hwctx)
return &fence->base;
}
+static void amdxdna_hwctx_release_expanded_heap(struct amdxdna_hwctx *hwctx)
+{
+ struct amdxdna_client *client = hwctx->client;
+ struct amdxdna_gem_obj *heap;
+ unsigned long heap_id;
+
+ mutex_lock(&client->mm_lock);
+ if (hwctx->last_attached_heap) {
+ xa_for_each_range(&client->dev_heap_xa, heap_id, heap, 1,
+ hwctx->last_attached_heap) {
+ amdxdna_gem_unpin(heap);
+ drm_gem_object_put(to_gobj(heap));
+ }
+ }
+ mutex_unlock(&client->mm_lock);
+}
+
static void amdxdna_hwctx_destroy_rcu(struct amdxdna_hwctx *hwctx,
struct srcu_struct *ss)
{
- struct amdxdna_dev *xdna = hwctx->client->xdna;
+ struct amdxdna_client *client = hwctx->client;
+ struct amdxdna_dev *xdna = client->xdna;
synchronize_srcu(ss);
/* At this point, user is not able to submit new commands */
xdna->dev_info->ops->hwctx_fini(hwctx);
+ amdxdna_hwctx_release_expanded_heap(hwctx);
kfree(hwctx->name);
kfree(hwctx);
}
@@ -238,7 +257,7 @@ int amdxdna_drm_create_hwctx_ioctl(struct drm_device *dev, void *data, struct dr
ret = xdna->dev_info->ops->hwctx_init(hwctx);
if (ret) {
XDNA_ERR(xdna, "Init hwctx failed, ret %d", ret);
- goto dev_exit;
+ goto release_expanded_heap;
}
hwctx->name = kasprintf(GFP_KERNEL, "hwctx.%d.%d", client->pid, hwctx->fw_ctx_id);
@@ -269,7 +288,8 @@ free_name:
kfree(hwctx->name);
fini_hwctx:
xdna->dev_info->ops->hwctx_fini(hwctx);
-dev_exit:
+release_expanded_heap:
+ amdxdna_hwctx_release_expanded_heap(hwctx);
drm_dev_exit(idx);
free_hwctx:
kfree(hwctx);
@@ -407,6 +427,62 @@ put_obj:
return ret;
}
+static int amdxdna_hwctx_expand_heap(struct amdxdna_hwctx *hwctx)
+{
+ struct amdxdna_client *client = hwctx->client;
+ struct amdxdna_dev *xdna = client->xdna;
+ struct amdxdna_gem_obj *heap;
+ unsigned long heap_id, nid;
+ int ret = 0;
+
+ nid = hwctx->last_attached_heap + 1;
+ if (nid == client->dev_heap_nid)
+ goto out;
+
+ xa_for_each_range(&client->dev_heap_xa, heap_id, heap,
+ nid, client->dev_heap_nid) {
+ drm_gem_object_get(to_gobj(heap));
+ ret = amdxdna_gem_pin(heap);
+ if (ret) {
+ drm_gem_object_put(to_gobj(heap));
+ break;
+ }
+
+ mutex_unlock(&client->mm_lock);
+ ret = xdna->dev_info->ops->hwctx_heap_expand(hwctx, heap);
+ mutex_lock(&client->mm_lock);
+ if (ret) {
+ amdxdna_gem_unpin(heap);
+ drm_gem_object_put(to_gobj(heap));
+ break;
+ }
+
+ hwctx->last_attached_heap = heap_id;
+ }
+
+out:
+ return ret;
+}
+
+int amdxdna_update_heap(struct amdxdna_client *client, struct amdxdna_hwctx *hwctx)
+{
+ unsigned long hwctx_id;
+ int ret;
+
+ guard(mutex)(&client->mm_lock);
+
+ if (hwctx)
+ return amdxdna_hwctx_expand_heap(hwctx);
+
+ amdxdna_for_each_hwctx(client, hwctx_id, hwctx) {
+ ret = amdxdna_hwctx_expand_heap(hwctx);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
static void
amdxdna_arg_bos_put(struct amdxdna_sched_job *job)
{
diff --git a/drivers/accel/amdxdna/amdxdna_ctx.h b/drivers/accel/amdxdna/amdxdna_ctx.h
index 6e3c6371a088..aaae16430466 100644
--- a/drivers/accel/amdxdna/amdxdna_ctx.h
+++ b/drivers/accel/amdxdna/amdxdna_ctx.h
@@ -109,6 +109,7 @@ struct amdxdna_hwctx {
u32 umq_bo_hdl;
u32 doorbell_offset;
u32 num_unused_col;
+ u32 last_attached_heap;
struct amdxdna_qos_info qos;
struct amdxdna_hwctx_param_config_cu *cus;
@@ -205,6 +206,7 @@ void amdxdna_hwctx_remove_all(struct amdxdna_client *client);
int amdxdna_hwctx_walk(struct amdxdna_client *client, void *arg,
int (*walk)(struct amdxdna_hwctx *hwctx, void *arg));
int amdxdna_hwctx_sync_debug_bo(struct amdxdna_client *client, u32 debug_bo_hdl);
+int amdxdna_update_heap(struct amdxdna_client *client, struct amdxdna_hwctx *hwctx);
int amdxdna_cmd_submit(struct amdxdna_client *client,
struct amdxdna_drv_cmd *drv_cmd, u32 cmd_bo_hdls,
diff --git a/drivers/accel/amdxdna/amdxdna_gem.c b/drivers/accel/amdxdna/amdxdna_gem.c
index 319d2064fafa..2dfdc56ba91d 100644
--- a/drivers/accel/amdxdna/amdxdna_gem.c
+++ b/drivers/accel/amdxdna/amdxdna_gem.c
@@ -24,6 +24,77 @@
MODULE_IMPORT_NS("DMA_BUF");
+/*
+ * The dev BO could be across multiple heap BO chunks. The heap chunks should
+ * be mapped to userspace and the userspace virtual address should be
+ * contiguous.
+ */
+static int
+amdxdna_init_dev_bo(struct amdxdna_gem_obj *dev_bo)
+{
+ struct amdxdna_client *client = dev_bo->client;
+ struct amdxdna_dev *xdna = client->xdna;
+ struct amdxdna_gem_obj *heap;
+ u64 heap_addr, exp_heap_uva;
+ u32 heap_id;
+
+ if (xa_empty(&client->dev_heap_xa)) {
+ XDNA_DBG(xdna, "Empty heap xa");
+ return -EAGAIN;
+ }
+
+ for (heap_id = 0; heap_id < client->dev_heap_nid; heap_id++) {
+ heap = xa_load(&client->dev_heap_xa, heap_id);
+ if (!heap) {
+ XDNA_ERR(xdna, "Failed to load heap %d", heap_id);
+ return -EINVAL;
+ }
+ heap_addr = amdxdna_gem_dev_addr(heap);
+ if (heap_addr > dev_bo->mm_node.start)
+ break;
+ }
+
+ heap_id--;
+ heap = xa_load(&client->dev_heap_xa, heap_id);
+ exp_heap_uva = amdxdna_gem_uva(heap);
+ heap_addr = amdxdna_gem_dev_addr(heap);
+ dev_bo->heap_start_id = heap_id;
+ dev_bo->mem.uva = dev_bo->mm_node.start - heap_addr + exp_heap_uva;
+
+ for (; heap_id < client->dev_heap_nid; heap_id++) {
+ heap = xa_load(&client->dev_heap_xa, heap_id);
+ if (!heap) {
+ XDNA_ERR(xdna, "Failed to load heap %d", heap_id);
+ return -EINVAL;
+ }
+ heap_addr = amdxdna_gem_uva(heap);
+ if (heap_addr == AMDXDNA_INVALID_ADDR) {
+ XDNA_ERR(xdna, "Heap %d is not mapped", heap_id);
+ return -EAGAIN;
+ }
+
+ if (heap_addr != exp_heap_uva) {
+ XDNA_ERR(xdna, "Heap %d uva is not contiguous", heap_id);
+ return -EINVAL;
+ }
+
+ if (heap->dev_addr + heap->mem.size >=
+ dev_bo->mm_node.start + dev_bo->mem.size)
+ break;
+
+ exp_heap_uva += heap->mem.size;
+ }
+
+ if (heap_id == client->dev_heap_nid) {
+ XDNA_DBG(xdna, "Can not find heap end");
+ return -EAGAIN;
+ }
+
+ dev_bo->heap_end_id = heap_id;
+
+ return 0;
+}
+
static int
amdxdna_gem_heap_alloc(struct amdxdna_gem_obj *abo)
{
@@ -31,32 +102,22 @@ amdxdna_gem_heap_alloc(struct amdxdna_gem_obj *abo)
struct amdxdna_dev *xdna = client->xdna;
struct amdxdna_mem *mem = &abo->mem;
struct amdxdna_gem_obj *heap;
+ unsigned long heap_id;
u32 align;
int ret;
mutex_lock(&client->mm_lock);
- heap = client->dev_heap;
- if (!heap) {
- ret = -EINVAL;
- goto unlock_out;
- }
-
- if (amdxdna_gem_uva(heap) == AMDXDNA_INVALID_ADDR) {
- XDNA_ERR(xdna, "Invalid dev heap userptr");
- ret = -EINVAL;
- goto unlock_out;
- }
-
- if (mem->size == 0 || mem->size > heap->mem.size) {
- XDNA_ERR(xdna, "Invalid dev bo size 0x%lx, limit 0x%lx",
- mem->size, heap->mem.size);
+ if (!mem->size || mem->size > xdna->dev_info->dev_heap_max_size) {
+ XDNA_ERR(xdna, "Invalid dev bo size 0x%lx, max heap 0x%lx",
+ mem->size, xdna->dev_info->dev_heap_max_size);
ret = -EINVAL;
goto unlock_out;
}
align = 1 << max(PAGE_SHIFT, xdna->dev_info->dev_mem_buf_shift);
- ret = drm_mm_insert_node_generic(&heap->mm, &abo->mm_node,
+ ret = drm_mm_insert_node_generic(&client->dev_heap_mm,
+ &abo->mm_node,
mem->size, align,
0, DRM_MM_INSERT_BEST);
if (ret) {
@@ -64,9 +125,16 @@ amdxdna_gem_heap_alloc(struct amdxdna_gem_obj *abo)
goto unlock_out;
}
- client->heap_usage += mem->size;
+ ret = amdxdna_init_dev_bo(abo);
+ if (ret) {
+ drm_mm_remove_node(&abo->mm_node);
+ goto unlock_out;
+ }
- drm_gem_object_get(to_gobj(heap));
+ client->heap_usage += mem->size;
+ xa_for_each_range(&client->dev_heap_xa, heap_id, heap,
+ abo->heap_start_id, abo->heap_end_id)
+ drm_gem_object_get(to_gobj(heap));
unlock_out:
mutex_unlock(&client->mm_lock);
@@ -79,13 +147,16 @@ amdxdna_gem_heap_free(struct amdxdna_gem_obj *abo)
{
struct amdxdna_client *client = abo->client;
struct amdxdna_gem_obj *heap;
+ unsigned long heap_id;
mutex_lock(&client->mm_lock);
drm_mm_remove_node(&abo->mm_node);
client->heap_usage -= abo->mem.size;
- heap = client->dev_heap;
- drm_gem_object_put(to_gobj(heap));
+
+ xa_for_each_range(&client->dev_heap_xa, heap_id, heap,
+ abo->heap_start_id, abo->heap_end_id)
+ drm_gem_object_put(to_gobj(heap));
mutex_unlock(&client->mm_lock);
}
@@ -162,30 +233,12 @@ static void amdxdna_gem_vunmap(struct amdxdna_gem_obj *abo)
}
/*
- * Obtain the user virtual address for accessing the BO.
- * It can be used for device to access the BO when PASID is enabled.
- */
-u64 amdxdna_gem_uva(struct amdxdna_gem_obj *abo)
-{
- if (abo->type == AMDXDNA_BO_DEV) {
- struct amdxdna_gem_obj *heap = abo->client->dev_heap;
- u64 off = amdxdna_dev_bo_offset(abo);
-
- if (amdxdna_gem_uva(heap) != AMDXDNA_INVALID_ADDR)
- return amdxdna_gem_uva(heap) + off;
- return AMDXDNA_INVALID_ADDR;
- }
-
- return abo->mem.uva;
-}
-
-/*
* Obtain the address for device to access the BO.
*/
u64 amdxdna_gem_dev_addr(struct amdxdna_gem_obj *abo)
{
if (abo->type == AMDXDNA_BO_DEV_HEAP)
- return abo->client->xdna->dev_info->dev_mem_base;
+ return abo->dev_addr;
if (abo->type == AMDXDNA_BO_DEV)
return abo->mm_node.start;
return amdxdna_obj_dma_addr(abo);
@@ -566,9 +619,6 @@ static void amdxdna_gem_obj_free(struct drm_gem_object *gobj)
if (abo->pinned)
amdxdna_gem_unpin(abo);
- if (abo->type == AMDXDNA_BO_DEV_HEAP)
- drm_mm_takedown(&abo->mm);
-
amdxdna_dma_unmap_bo(xdna, abo);
amdxdna_gem_vunmap(abo);
mutex_destroy(&abo->lock);
@@ -654,11 +704,23 @@ static void amdxdna_gem_obj_vunmap(struct drm_gem_object *obj, struct iosys_map
static int amdxdna_gem_dev_obj_vmap(struct drm_gem_object *obj, struct iosys_map *map)
{
struct amdxdna_gem_obj *abo = to_xdna_obj(obj);
- void *base = amdxdna_gem_vmap(abo->client->dev_heap);
- u64 offset = amdxdna_dev_bo_offset(abo);
+ struct amdxdna_gem_obj *heap;
+ void *base;
+ u64 offset;
+
+ /* vmap dev bo which is across more than 1 heap is not allowed */
+ if (abo->heap_start_id != abo->heap_end_id)
+ return -ENOMEM;
+
+ heap = xa_load(&abo->client->dev_heap_xa, abo->heap_start_id);
+ if (!heap)
+ return -ENOMEM;
+ base = amdxdna_gem_vmap(heap);
if (!base)
return -ENOMEM;
+
+ offset = amdxdna_gem_dev_addr(abo) - amdxdna_gem_dev_addr(heap);
iosys_map_set_vaddr(map, base + offset);
return 0;
}
@@ -873,15 +935,32 @@ amdxdna_drm_create_dev_heap_bo(struct drm_device *dev,
/* Set up heap for this client. */
mutex_lock(&client->mm_lock);
- if (client->dev_heap) {
- XDNA_DBG(client->xdna, "dev heap is already created");
- ret = -EBUSY;
+ if (!xdna->dev_info->ops->hwctx_heap_expand &&
+ client->dev_heap_nid > 0) {
+ XDNA_ERR(xdna, "Heap expansion is not supported");
+ ret = -EOPNOTSUPP;
+ goto mm_unlock;
+ }
+
+ if (client->total_heap_size + abo->mem.size >
+ xdna->dev_info->dev_heap_max_size) {
+ XDNA_ERR(xdna, "Heap size 0x%lx + 0x%lx exceeds max 0x%lx",
+ client->total_heap_size, abo->mem.size,
+ xdna->dev_info->dev_heap_max_size);
+ ret = -ENOSPC;
+ goto mm_unlock;
+ }
+
+ ret = xa_insert(&client->dev_heap_xa, client->dev_heap_nid, abo, GFP_KERNEL);
+ if (ret) {
+ XDNA_ERR(xdna, "Add heap failed %d", ret);
goto mm_unlock;
}
- client->dev_heap = abo;
- drm_gem_object_get(to_gobj(abo));
- drm_mm_init(&abo->mm, xdna->dev_info->dev_mem_base, abo->mem.size);
+ abo->dev_addr = xdna->dev_info->dev_mem_base + client->total_heap_size;
+ client->total_heap_size += abo->mem.size;
+ client->dev_heap_nid++;
+ drm_gem_object_get(to_gobj(abo));
mutex_unlock(&client->mm_lock);
@@ -924,10 +1003,10 @@ amdxdna_drm_create_dev_bo(struct drm_device *dev,
ret = amdxdna_gem_heap_alloc(abo);
if (ret) {
- XDNA_ERR(xdna, "Failed to alloc dev bo memory, ret %d", ret);
amdxdna_gem_destroy_obj(abo);
return ERR_PTR(ret);
}
+
drm_gem_private_object_init(dev, gobj, aligned_sz);
return abo;
@@ -935,6 +1014,7 @@ amdxdna_drm_create_dev_bo(struct drm_device *dev,
int amdxdna_drm_create_bo_ioctl(struct drm_device *dev, void *data, struct drm_file *filp)
{
+ struct amdxdna_client *client = filp->driver_priv;
struct amdxdna_dev *xdna = to_xdna_dev(dev);
struct amdxdna_drm_create_bo *args = data;
struct amdxdna_gem_obj *abo;
@@ -955,6 +1035,13 @@ int amdxdna_drm_create_bo_ioctl(struct drm_device *dev, void *data, struct drm_f
break;
case AMDXDNA_BO_DEV:
abo = amdxdna_drm_create_dev_bo(dev, args, filp);
+ if (!IS_ERR(abo)) {
+ mutex_lock(&xdna->dev_lock);
+ ret = amdxdna_update_heap(client, NULL);
+ mutex_unlock(&xdna->dev_lock);
+ if (ret)
+ goto put_obj;
+ }
break;
default:
return -EINVAL;
@@ -978,14 +1065,11 @@ put_obj:
return ret;
}
-int amdxdna_gem_pin_nolock(struct amdxdna_gem_obj *abo)
+static int amdxdna_bo_pin(struct amdxdna_gem_obj *abo)
{
struct amdxdna_dev *xdna = to_xdna_dev(to_gobj(abo)->dev);
int ret;
- if (abo->type == AMDXDNA_BO_DEV)
- abo = abo->client->dev_heap;
-
if (is_import_bo(abo))
return 0;
@@ -995,6 +1079,45 @@ int amdxdna_gem_pin_nolock(struct amdxdna_gem_obj *abo)
return ret;
}
+static void amdxdna_bo_unpin(struct amdxdna_gem_obj *abo)
+{
+ struct amdxdna_dev *xdna = to_xdna_dev(to_gobj(abo)->dev);
+
+ if (is_import_bo(abo))
+ return;
+
+ drm_gem_shmem_unpin(&abo->base);
+
+ XDNA_DBG(xdna, "BO type %d", abo->type);
+}
+
+int amdxdna_gem_pin_nolock(struct amdxdna_gem_obj *abo)
+{
+ struct amdxdna_client *client = abo->client;
+ struct amdxdna_gem_obj *heap;
+ unsigned long heap_id, last = ULONG_MAX;
+ int ret = 0;
+
+ if (abo->type != AMDXDNA_BO_DEV)
+ return amdxdna_bo_pin(abo);
+
+ xa_for_each_range(&client->dev_heap_xa, heap_id, heap,
+ abo->heap_start_id, abo->heap_end_id) {
+ ret = amdxdna_bo_pin(heap);
+ if (ret)
+ break;
+ last = heap_id;
+ }
+
+ if (ret && last <= abo->heap_end_id) {
+ xa_for_each_range(&client->dev_heap_xa, heap_id, heap,
+ abo->heap_start_id, last)
+ amdxdna_bo_unpin(heap);
+ }
+
+ return ret;
+}
+
int amdxdna_gem_pin(struct amdxdna_gem_obj *abo)
{
int ret;
@@ -1008,14 +1131,18 @@ int amdxdna_gem_pin(struct amdxdna_gem_obj *abo)
void amdxdna_gem_unpin(struct amdxdna_gem_obj *abo)
{
- if (abo->type == AMDXDNA_BO_DEV)
- abo = abo->client->dev_heap;
+ mutex_lock(&abo->lock);
+ if (abo->type == AMDXDNA_BO_DEV) {
+ struct amdxdna_gem_obj *heap;
+ unsigned long heap_id;
- if (is_import_bo(abo))
- return;
+ xa_for_each_range(&abo->client->dev_heap_xa, heap_id, heap,
+ abo->heap_start_id, abo->heap_end_id)
+ amdxdna_bo_unpin(heap);
+ } else {
+ amdxdna_bo_unpin(abo);
+ }
- mutex_lock(&abo->lock);
- drm_gem_shmem_unpin(&abo->base);
mutex_unlock(&abo->lock);
}
@@ -1072,6 +1199,29 @@ int amdxdna_drm_get_bo_info_ioctl(struct drm_device *dev, void *data, struct drm
return ret;
}
+static int amdxdna_flush_bo(struct amdxdna_gem_obj *abo, u64 offset, u64 size)
+{
+ u64 end;
+
+ if (offset >= abo->mem.size)
+ return -EINVAL;
+
+ if (check_add_overflow(offset, size, &end))
+ 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))
+ 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);
+ else
+ return -EINVAL;
+
+ return 0;
+}
+
/*
* The sync bo ioctl is to make sure the CPU cache is in sync with memory.
* This is required because NPU is not cache coherent device. CPU cache
@@ -1082,11 +1232,12 @@ int amdxdna_drm_get_bo_info_ioctl(struct drm_device *dev, void *data, struct drm
int amdxdna_drm_sync_bo_ioctl(struct drm_device *dev,
void *data, struct drm_file *filp)
{
+ struct amdxdna_client *client = filp->driver_priv;
struct amdxdna_dev *xdna = to_xdna_dev(dev);
struct amdxdna_drm_sync_bo *args = data;
struct amdxdna_gem_obj *abo;
struct drm_gem_object *gobj;
- int ret;
+ int ret = 0;
gobj = drm_gem_object_lookup(filp, args->handle);
if (!gobj) {
@@ -1095,22 +1246,45 @@ int amdxdna_drm_sync_bo_ioctl(struct drm_device *dev,
}
abo = to_xdna_obj(gobj);
- ret = amdxdna_gem_pin(abo);
- if (ret) {
- XDNA_ERR(xdna, "Pin BO %d failed, ret %d", args->handle, ret);
- goto put_obj;
- }
+ if (abo->type == AMDXDNA_BO_DEV) {
+ struct amdxdna_gem_obj *heap;
+ unsigned long heap_id;
+ u64 bo_start = amdxdna_gem_dev_addr(abo);
+ u64 flush_start = bo_start + args->offset;
+ u64 flush_end = flush_start + args->size;
+
+ xa_for_each_range(&client->dev_heap_xa, heap_id, heap,
+ abo->heap_start_id, abo->heap_end_id) {
+ u64 heap_start = amdxdna_gem_dev_addr(heap);
+ u64 heap_end = heap_start + heap->mem.size;
+ u64 start = max(flush_start, heap_start);
+ u64 end = min(flush_end, heap_end);
+
+ if (start >= end)
+ continue;
+
+ ret = amdxdna_flush_bo(heap, start - heap_start, end - start);
+ if (ret) {
+ XDNA_ERR(xdna, "Failed to flush heap %ld ret %d",
+ heap_id, ret);
+ goto put_obj;
+ }
+ }
+ } else {
+ ret = amdxdna_gem_pin(abo);
+ if (ret) {
+ XDNA_ERR(xdna, "Pin BO %d failed, ret %d", args->handle, ret);
+ goto put_obj;
+ }
- if (is_import_bo(abo))
- drm_clflush_sg(abo->base.sgt);
- else if (amdxdna_gem_vmap(abo))
- drm_clflush_virt_range(amdxdna_gem_vmap(abo) + args->offset, args->size);
- else if (abo->base.pages)
- drm_clflush_pages(abo->base.pages, gobj->size >> PAGE_SHIFT);
- else
- drm_WARN(&xdna->ddev, 1, "Can not get flush memory");
+ ret = amdxdna_flush_bo(abo, args->offset, args->size);
+ amdxdna_gem_unpin(abo);
- amdxdna_gem_unpin(abo);
+ if (ret) {
+ drm_WARN(&xdna->ddev, 1, "Can not get flush memory");
+ goto put_obj;
+ }
+ }
XDNA_DBG(xdna, "Sync bo %d offset 0x%llx, size 0x%llx\n",
args->handle, args->offset, args->size);
diff --git a/drivers/accel/amdxdna/amdxdna_gem.h b/drivers/accel/amdxdna/amdxdna_gem.h
index 4fc48a1189d2..6a6df51969e0 100644
--- a/drivers/accel/amdxdna/amdxdna_gem.h
+++ b/drivers/accel/amdxdna/amdxdna_gem.h
@@ -46,8 +46,10 @@ struct amdxdna_gem_obj {
int open_ref;
/* Below members are initialized when needed */
- struct drm_mm mm; /* For AMDXDNA_BO_DEV_HEAP */
struct drm_mm_node mm_node; /* For AMDXDNA_BO_DEV */
+ u32 heap_start_id;
+ u32 heap_end_id;
+ u64 dev_addr; /* For heap bo */
u32 assigned_hwctx;
struct dma_buf *dma_buf;
struct dma_buf_attachment *attach;
@@ -71,13 +73,21 @@ static inline void amdxdna_gem_put_obj(struct amdxdna_gem_obj *abo)
drm_gem_object_put(to_gobj(abo));
}
+/*
+ * Obtain the user virtual address for accessing the BO.
+ * It can be used for device to access the BO when PASID is enabled.
+ */
+static inline u64 amdxdna_gem_uva(struct amdxdna_gem_obj *abo)
+{
+ return abo->mem.uva;
+}
+
void *amdxdna_gem_vmap(struct amdxdna_gem_obj *abo);
-u64 amdxdna_gem_uva(struct amdxdna_gem_obj *abo);
u64 amdxdna_gem_dev_addr(struct amdxdna_gem_obj *abo);
static inline u64 amdxdna_dev_bo_offset(struct amdxdna_gem_obj *abo)
{
- return amdxdna_gem_dev_addr(abo) - amdxdna_gem_dev_addr(abo->client->dev_heap);
+ return amdxdna_gem_dev_addr(abo) - abo->client->xdna->dev_info->dev_mem_base;
}
static inline u64 amdxdna_obj_dma_addr(struct amdxdna_gem_obj *abo)
diff --git a/drivers/accel/amdxdna/amdxdna_pci_drv.c b/drivers/accel/amdxdna/amdxdna_pci_drv.c
index a6e9be7960c2..c677293c1ae7 100644
--- a/drivers/accel/amdxdna/amdxdna_pci_drv.c
+++ b/drivers/accel/amdxdna/amdxdna_pci_drv.c
@@ -126,6 +126,9 @@ static int amdxdna_drm_open(struct drm_device *ddev, struct drm_file *filp)
mmgrab(client->mm);
init_srcu_struct(&client->hwctx_srcu);
xa_init_flags(&client->hwctx_xa, XA_FLAGS_ALLOC);
+ xa_init_flags(&client->dev_heap_xa, XA_FLAGS_ALLOC);
+ drm_mm_init(&client->dev_heap_mm, xdna->dev_info->dev_mem_base,
+ xdna->dev_info->dev_heap_max_size);
mutex_init(&client->mm_lock);
mutex_lock(&xdna->dev_lock);
@@ -141,13 +144,18 @@ static int amdxdna_drm_open(struct drm_device *ddev, struct drm_file *filp)
static void amdxdna_client_cleanup(struct amdxdna_client *client)
{
+ struct amdxdna_gem_obj *heap;
+ unsigned long heap_id;
+
list_del(&client->node);
amdxdna_hwctx_remove_all(client);
xa_destroy(&client->hwctx_xa);
cleanup_srcu_struct(&client->hwctx_srcu);
- if (client->dev_heap)
- drm_gem_object_put(to_gobj(client->dev_heap));
+ xa_for_each(&client->dev_heap_xa, heap_id, heap)
+ drm_gem_object_put(to_gobj(heap));
+ xa_destroy(&client->dev_heap_xa);
+ drm_mm_takedown(&client->dev_heap_mm);
mutex_destroy(&client->mm_lock);
mmdrop(client->mm);
diff --git a/drivers/accel/amdxdna/amdxdna_pci_drv.h b/drivers/accel/amdxdna/amdxdna_pci_drv.h
index 471b72299aee..34271c14d359 100644
--- a/drivers/accel/amdxdna/amdxdna_pci_drv.h
+++ b/drivers/accel/amdxdna/amdxdna_pci_drv.h
@@ -7,6 +7,7 @@
#define _AMDXDNA_PCI_DRV_H_
#include <drm/amdxdna_accel.h>
+#include <drm/drm_mm.h>
#include <drm/drm_print.h>
#include <linux/iommu.h>
#include <linux/iova.h>
@@ -61,6 +62,7 @@ struct amdxdna_dev_ops {
void (*hwctx_fini)(struct amdxdna_hwctx *hwctx);
int (*hwctx_config)(struct amdxdna_hwctx *hwctx, u32 type, u64 value, void *buf, u32 size);
int (*hwctx_sync_debug_bo)(struct amdxdna_hwctx *hwctx, u32 debug_bo_hdl);
+ int (*hwctx_heap_expand)(struct amdxdna_hwctx *hwctx, struct amdxdna_gem_obj *heap);
void (*hmm_invalidate)(struct amdxdna_gem_obj *abo, unsigned long cur_seq);
int (*cmd_submit)(struct amdxdna_hwctx *hwctx, struct amdxdna_sched_job *job, u64 *seq);
int (*cmd_wait)(struct amdxdna_hwctx *hwctx, u64 seq, u32 timeout);
@@ -95,6 +97,7 @@ struct amdxdna_dev_info {
size_t dev_mem_size;
const char *default_vbnv;
const struct amdxdna_rev_vbnv *rev_vbnv_tbl;
+ size_t dev_heap_max_size;
const struct amdxdna_dev_priv *dev_priv;
const struct amdxdna_fw_feature_tbl *fw_feature_tbl;
const struct amdxdna_dev_ops *ops;
@@ -153,7 +156,10 @@ struct amdxdna_client {
struct drm_file *filp;
struct mutex mm_lock; /* protect memory related */
- struct amdxdna_gem_obj *dev_heap;
+ struct xarray dev_heap_xa;
+ struct drm_mm dev_heap_mm;
+ u32 dev_heap_nid;
+ size_t total_heap_size;
struct iommu_sva *sva;
int pasid;
diff --git a/drivers/accel/amdxdna/npu1_regs.c b/drivers/accel/amdxdna/npu1_regs.c
index 4e48c030a69f..ca779674017a 100644
--- a/drivers/accel/amdxdna/npu1_regs.c
+++ b/drivers/accel/amdxdna/npu1_regs.c
@@ -139,6 +139,7 @@ const struct amdxdna_dev_info dev_npu1_info = {
.dev_mem_base = AIE2_DEVM_BASE,
.dev_mem_size = AIE2_DEVM_SIZE,
.default_vbnv = "RyzenAI-npu1",
+ .dev_heap_max_size = AIE2_DEVM_SIZE,
.device_type = AMDXDNA_DEV_TYPE_KMQ,
.dev_priv = &npu1_dev_priv,
.fw_feature_tbl = npu1_fw_feature_table,
diff --git a/drivers/accel/amdxdna/npu4_regs.c b/drivers/accel/amdxdna/npu4_regs.c
index eddc31803a50..15a161384625 100644
--- a/drivers/accel/amdxdna/npu4_regs.c
+++ b/drivers/accel/amdxdna/npu4_regs.c
@@ -98,6 +98,7 @@ const struct amdxdna_fw_feature_tbl npu4_fw_feature_table[] = {
{ .features = BIT_U64(AIE2_NPU_COMMAND), .major = 6, .min_minor = 15 },
{ .features = BIT_U64(AIE2_UPDATE_PROPERTY), .major = 6, .min_minor = 15 },
{ .features = BIT_U64(AIE2_APP_HEALTH), .major = 6, .min_minor = 18 },
+ { .features = BIT_U64(AIE2_ADD_HOST_BUFFER), .major = 6, .min_minor = 18 },
{ .features = BIT_U64(AIE2_GET_DEV_REVISION), .major = 6, .min_minor = 24 },
{ .features = AIE2_ALL_FEATURES, .major = 7 },
{ 0 }
@@ -200,6 +201,7 @@ const struct amdxdna_dev_info dev_npu4_info = {
.dev_mem_base = AIE2_DEVM_BASE,
.dev_mem_size = AIE2_DEVM_SIZE,
.default_vbnv = "RyzenAI-npu4",
+ .dev_heap_max_size = AIE2_DEVM_MAX_SIZE,
.device_type = AMDXDNA_DEV_TYPE_KMQ,
.rev_vbnv_tbl = npu4_rev_vbnv_tbl,
.dev_priv = &npu4_dev_priv,
diff --git a/drivers/accel/amdxdna/npu5_regs.c b/drivers/accel/amdxdna/npu5_regs.c
index a9102978e4a8..306b359d0cd3 100644
--- a/drivers/accel/amdxdna/npu5_regs.c
+++ b/drivers/accel/amdxdna/npu5_regs.c
@@ -107,6 +107,7 @@ const struct amdxdna_dev_info dev_npu5_info = {
.dev_mem_base = AIE2_DEVM_BASE,
.dev_mem_size = AIE2_DEVM_SIZE,
.default_vbnv = "RyzenAI-npu5",
+ .dev_heap_max_size = AIE2_DEVM_MAX_SIZE,
.device_type = AMDXDNA_DEV_TYPE_KMQ,
.rev_vbnv_tbl = npu4_rev_vbnv_tbl,
.dev_priv = &npu5_dev_priv,
diff --git a/drivers/accel/amdxdna/npu6_regs.c b/drivers/accel/amdxdna/npu6_regs.c
index e0db3a09740b..e68637d2a228 100644
--- a/drivers/accel/amdxdna/npu6_regs.c
+++ b/drivers/accel/amdxdna/npu6_regs.c
@@ -108,6 +108,7 @@ const struct amdxdna_dev_info dev_npu6_info = {
.dev_mem_base = AIE2_DEVM_BASE,
.dev_mem_size = AIE2_DEVM_SIZE,
.default_vbnv = "RyzenAI-npu6",
+ .dev_heap_max_size = AIE2_DEVM_MAX_SIZE,
.device_type = AMDXDNA_DEV_TYPE_KMQ,
.rev_vbnv_tbl = npu4_rev_vbnv_tbl,
.dev_priv = &npu6_dev_priv,
diff --git a/drivers/accel/ethosu/ethosu_job.c b/drivers/accel/ethosu/ethosu_job.c
index 418463c03bfb..b76924645aaa 100644
--- a/drivers/accel/ethosu/ethosu_job.c
+++ b/drivers/accel/ethosu/ethosu_job.c
@@ -416,9 +416,21 @@ static int ethosu_ioctl_submit_job(struct drm_device *dev, struct drm_file *file
struct drm_gem_object *gem;
/* Can only omit a BO handle if the region is not used or used for SRAM */
- if (!job->region_bo_handles[i] &&
- (!cmd_info->region_size[i] || (i == ETHOSU_SRAM_REGION && job->sram_size)))
- continue;
+ if (!job->region_bo_handles[i]) {
+ if (!cmd_info->region_size[i])
+ continue;
+ if (i == ETHOSU_SRAM_REGION) {
+ if (cmd_info->region_size[i] <= edev->npu_info.sram_size)
+ continue;
+
+ dev_err(dev->dev,
+ "cmd stream region %d size greater than SRAM size (%llu > %u)\n",
+ i, cmd_info->region_size[i],
+ edev->npu_info.sram_size);
+ ret = -EINVAL;
+ goto out_cleanup_job;
+ }
+ }
if (job->region_bo_handles[i] && !cmd_info->region_size[i]) {
dev_err(dev->dev,
diff --git a/drivers/gpu/drm/bridge/adv7511/adv7511.h b/drivers/gpu/drm/bridge/adv7511/adv7511.h
index 8be7266fd4f4..12c95198d9a4 100644
--- a/drivers/gpu/drm/bridge/adv7511/adv7511.h
+++ b/drivers/gpu/drm/bridge/adv7511/adv7511.h
@@ -354,7 +354,6 @@ struct adv7511 {
enum drm_connector_status status;
bool powered;
- struct drm_bridge *next_bridge;
struct drm_display_mode curr_mode;
unsigned int f_tmds;
diff --git a/drivers/gpu/drm/bridge/adv7511/adv7511_drv.c b/drivers/gpu/drm/bridge/adv7511/adv7511_drv.c
index 821a3008a1e7..02f8f7e78a16 100644
--- a/drivers/gpu/drm/bridge/adv7511/adv7511_drv.c
+++ b/drivers/gpu/drm/bridge/adv7511/adv7511_drv.c
@@ -849,8 +849,8 @@ static int adv7511_bridge_attach(struct drm_bridge *bridge,
struct adv7511 *adv = bridge_to_adv7511(bridge);
int ret = 0;
- if (adv->next_bridge) {
- ret = drm_bridge_attach(encoder, adv->next_bridge, bridge,
+ if (adv->bridge.next_bridge) {
+ ret = drm_bridge_attach(encoder, adv->bridge.next_bridge, bridge,
flags | DRM_BRIDGE_ATTACH_NO_CONNECTOR);
if (ret)
return ret;
@@ -1249,10 +1249,13 @@ static int adv7511_probe(struct i2c_client *i2c)
memset(&link_config, 0, sizeof(link_config));
- ret = drm_of_find_panel_or_bridge(dev->of_node, 1, -1, NULL,
- &adv7511->next_bridge);
- if (ret && ret != -ENODEV)
- return ret;
+ adv7511->bridge.next_bridge = of_drm_get_bridge_by_endpoint(dev->of_node, 1, -1);
+ if (IS_ERR(adv7511->bridge.next_bridge)) {
+ if (PTR_ERR(adv7511->bridge.next_bridge) == -ENODEV)
+ adv7511->bridge.next_bridge = NULL;
+ else
+ return PTR_ERR(adv7511->bridge.next_bridge);
+ }
if (adv7511->info->link_config)
ret = adv7511_parse_dt(dev->of_node, &link_config);
diff --git a/drivers/gpu/drm/bridge/chipone-icn6211.c b/drivers/gpu/drm/bridge/chipone-icn6211.c
index 08775381b075..e5957917ad88 100644
--- a/drivers/gpu/drm/bridge/chipone-icn6211.c
+++ b/drivers/gpu/drm/bridge/chipone-icn6211.c
@@ -730,13 +730,11 @@ static int chipone_dsi_probe(struct mipi_dsi_device *dsi)
mipi_dsi_set_drvdata(dsi, icn);
- drm_bridge_add(&icn->bridge);
-
- ret = chipone_dsi_attach(icn);
+ ret = devm_drm_bridge_add(dev, &icn->bridge);
if (ret)
- drm_bridge_remove(&icn->bridge);
+ return ret;
- return ret;
+ return chipone_dsi_attach(icn);
}
static int chipone_i2c_probe(struct i2c_client *client)
@@ -765,10 +763,7 @@ static int chipone_i2c_probe(struct i2c_client *client)
static void chipone_dsi_remove(struct mipi_dsi_device *dsi)
{
- struct chipone *icn = mipi_dsi_get_drvdata(dsi);
-
mipi_dsi_detach(dsi);
- drm_bridge_remove(&icn->bridge);
}
static const struct of_device_id chipone_of_match[] = {
diff --git a/drivers/gpu/drm/bridge/chrontel-ch7033.c b/drivers/gpu/drm/bridge/chrontel-ch7033.c
index 54d49d4882c8..a237c65ebd69 100644
--- a/drivers/gpu/drm/bridge/chrontel-ch7033.c
+++ b/drivers/gpu/drm/bridge/chrontel-ch7033.c
@@ -199,7 +199,6 @@ enum {
struct ch7033_priv {
struct regmap *regmap;
- struct drm_bridge *next_bridge;
struct drm_bridge bridge;
struct drm_connector connector;
};
@@ -215,7 +214,7 @@ static enum drm_connector_status ch7033_connector_detect(
{
struct ch7033_priv *priv = conn_to_ch7033_priv(connector);
- return drm_bridge_detect(priv->next_bridge, connector);
+ return drm_bridge_detect(priv->bridge.next_bridge, connector);
}
static const struct drm_connector_funcs ch7033_connector_funcs = {
@@ -233,7 +232,7 @@ static int ch7033_connector_get_modes(struct drm_connector *connector)
const struct drm_edid *drm_edid;
int ret;
- drm_edid = drm_bridge_edid_read(priv->next_bridge, connector);
+ drm_edid = drm_bridge_edid_read(priv->bridge.next_bridge, connector);
drm_edid_connector_update(connector, drm_edid);
if (drm_edid) {
ret = drm_edid_connector_add_modes(connector);
@@ -275,7 +274,7 @@ static int ch7033_bridge_attach(struct drm_bridge *bridge,
struct drm_connector *connector = &priv->connector;
int ret;
- ret = drm_bridge_attach(encoder, priv->next_bridge, bridge,
+ ret = drm_bridge_attach(encoder, priv->bridge.next_bridge, bridge,
DRM_BRIDGE_ATTACH_NO_CONNECTOR);
if (ret)
return ret;
@@ -283,15 +282,15 @@ static int ch7033_bridge_attach(struct drm_bridge *bridge,
if (flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR)
return 0;
- if (priv->next_bridge->ops & DRM_BRIDGE_OP_DETECT) {
+ if (priv->bridge.next_bridge->ops & DRM_BRIDGE_OP_DETECT) {
connector->polled = DRM_CONNECTOR_POLL_HPD;
} else {
connector->polled = DRM_CONNECTOR_POLL_CONNECT |
DRM_CONNECTOR_POLL_DISCONNECT;
}
- if (priv->next_bridge->ops & DRM_BRIDGE_OP_HPD) {
- drm_bridge_hpd_enable(priv->next_bridge, ch7033_hpd_event,
+ if (priv->bridge.next_bridge->ops & DRM_BRIDGE_OP_HPD) {
+ drm_bridge_hpd_enable(priv->bridge.next_bridge, ch7033_hpd_event,
priv);
}
@@ -299,8 +298,8 @@ static int ch7033_bridge_attach(struct drm_bridge *bridge,
&ch7033_connector_helper_funcs);
ret = drm_connector_init_with_ddc(bridge->dev, &priv->connector,
&ch7033_connector_funcs,
- priv->next_bridge->type,
- priv->next_bridge->ddc);
+ priv->bridge.next_bridge->type,
+ priv->bridge.next_bridge->ddc);
if (ret) {
DRM_ERROR("Failed to initialize connector\n");
return ret;
@@ -313,8 +312,8 @@ static void ch7033_bridge_detach(struct drm_bridge *bridge)
{
struct ch7033_priv *priv = bridge_to_ch7033_priv(bridge);
- if (priv->next_bridge->ops & DRM_BRIDGE_OP_HPD)
- drm_bridge_hpd_disable(priv->next_bridge);
+ if (priv->bridge.next_bridge->ops & DRM_BRIDGE_OP_HPD)
+ drm_bridge_hpd_disable(priv->bridge.next_bridge);
drm_connector_cleanup(&priv->connector);
}
@@ -543,10 +542,9 @@ static int ch7033_probe(struct i2c_client *client)
dev_set_drvdata(dev, priv);
- ret = drm_of_find_panel_or_bridge(dev->of_node, 1, -1, NULL,
- &priv->next_bridge);
- if (ret)
- return ret;
+ priv->bridge.next_bridge = of_drm_get_bridge_by_endpoint(dev->of_node, 1, -1);
+ if (IS_ERR(priv->bridge.next_bridge))
+ return PTR_ERR(priv->bridge.next_bridge);
priv->regmap = devm_regmap_init_i2c(client, &ch7033_regmap_config);
if (IS_ERR(priv->regmap)) {
diff --git a/drivers/gpu/drm/bridge/inno-hdmi.c b/drivers/gpu/drm/bridge/inno-hdmi.c
index 1091af29ad8f..5fa533a4eb34 100644
--- a/drivers/gpu/drm/bridge/inno-hdmi.c
+++ b/drivers/gpu/drm/bridge/inno-hdmi.c
@@ -31,8 +31,6 @@
#include <drm/display/drm_hdmi_helper.h>
#include <drm/display/drm_hdmi_state_helper.h>
-#define INNO_HDMI_MIN_TMDS_CLOCK 25000000U
-
#define DDC_SEGMENT_ADDR 0x30
#define HDMI_SCL_RATE (100 * 1000)
@@ -820,7 +818,7 @@ static enum drm_mode_status inno_hdmi_bridge_mode_valid(struct drm_bridge *bridg
mpixelclk = mode->clock * 1000;
- if (mpixelclk < INNO_HDMI_MIN_TMDS_CLOCK)
+ if (mpixelclk < HDMI_TMDS_CHAR_RATE_MIN_HZ)
return MODE_CLOCK_LOW;
if (inno_hdmi_find_phy_config(hdmi, mpixelclk) < 0)
diff --git a/drivers/gpu/drm/bridge/ite-it66121.c b/drivers/gpu/drm/bridge/ite-it66121.c
index 19a027d75b61..19e188fe6e3b 100644
--- a/drivers/gpu/drm/bridge/ite-it66121.c
+++ b/drivers/gpu/drm/bridge/ite-it66121.c
@@ -306,7 +306,6 @@ struct it66121_ctx {
struct mutex lock; /* Protects fields below and device registers */
struct hdmi_avi_infoframe hdmi_avi_infoframe;
struct {
- struct platform_device *pdev;
u8 ch_enable;
u8 fs;
u8 swl;
@@ -902,24 +901,6 @@ out_unlock:
return drm_edid;
}
-static const struct drm_bridge_funcs it66121_bridge_funcs = {
- .atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state,
- .atomic_destroy_state = drm_atomic_helper_bridge_destroy_state,
- .atomic_reset = drm_atomic_helper_bridge_reset,
- .attach = it66121_bridge_attach,
- .atomic_get_output_bus_fmts = it66121_bridge_atomic_get_output_bus_fmts,
- .atomic_get_input_bus_fmts = it66121_bridge_atomic_get_input_bus_fmts,
- .atomic_enable = it66121_bridge_enable,
- .atomic_disable = it66121_bridge_disable,
- .atomic_check = it66121_bridge_check,
- .mode_set = it66121_bridge_mode_set,
- .mode_valid = it66121_bridge_mode_valid,
- .detect = it66121_bridge_detect,
- .edid_read = it66121_bridge_edid_read,
- .hpd_enable = it66121_bridge_hpd_enable,
- .hpd_disable = it66121_bridge_hpd_disable,
-};
-
static irqreturn_t it66121_irq_threaded_handler(int irq, void *dev_id)
{
int ret;
@@ -1225,14 +1206,16 @@ static int it661221_audio_ch_enable(struct it66121_ctx *ctx, bool enable)
return ret;
}
-static int it66121_audio_hw_params(struct device *dev, void *data,
- struct hdmi_codec_daifmt *daifmt,
- struct hdmi_codec_params *params)
+static int it66121_hdmi_audio_prepare(struct drm_bridge *bridge,
+ struct drm_connector *connector,
+ struct hdmi_codec_daifmt *daifmt,
+ struct hdmi_codec_params *params)
{
u8 fs;
u8 swl;
int ret;
- struct it66121_ctx *ctx = dev_get_drvdata(dev);
+ struct it66121_ctx *ctx = container_of(bridge, struct it66121_ctx, bridge);
+ struct device *dev = ctx->dev;
static u8 iec60958_chstat[5];
unsigned int channels = params->channels;
unsigned int sample_rate = params->sample_rate;
@@ -1379,41 +1362,44 @@ out:
return ret;
}
-static int it66121_audio_startup(struct device *dev, void *data)
+static int it66121_hdmi_audio_startup(struct drm_bridge *bridge,
+ struct drm_connector *connector)
{
int ret;
- struct it66121_ctx *ctx = dev_get_drvdata(dev);
+ struct it66121_ctx *ctx = container_of(bridge, struct it66121_ctx, bridge);
mutex_lock(&ctx->lock);
ret = it661221_audio_output_enable(ctx, true);
if (ret)
- dev_err(dev, "Failed to enable audio output: %d\n", ret);
+ dev_err(ctx->dev, "Failed to enable audio output: %d\n", ret);
mutex_unlock(&ctx->lock);
return ret;
}
-static void it66121_audio_shutdown(struct device *dev, void *data)
+static void it66121_hdmi_audio_shutdown(struct drm_bridge *bridge,
+ struct drm_connector *connector)
{
int ret;
- struct it66121_ctx *ctx = dev_get_drvdata(dev);
+ struct it66121_ctx *ctx = container_of(bridge, struct it66121_ctx, bridge);
mutex_lock(&ctx->lock);
ret = it661221_audio_output_enable(ctx, false);
if (ret)
- dev_err(dev, "Failed to disable audio output: %d\n", ret);
+ dev_err(ctx->dev, "Failed to disable audio output: %d\n", ret);
mutex_unlock(&ctx->lock);
}
-static int it66121_audio_mute(struct device *dev, void *data,
- bool enable, int direction)
+static int it66121_hdmi_audio_mute_stream(struct drm_bridge *bridge,
+ struct drm_connector *connector,
+ bool enable, int direction)
{
int ret;
- struct it66121_ctx *ctx = dev_get_drvdata(dev);
+ struct it66121_ctx *ctx = container_of(bridge, struct it66121_ctx, bridge);
- dev_dbg(dev, "%s: enable=%s, direction=%d\n",
+ dev_dbg(ctx->dev, "%s: enable=%s, direction=%d\n",
__func__, enable ? "true" : "false", direction);
mutex_lock(&ctx->lock);
@@ -1436,64 +1422,28 @@ static int it66121_audio_mute(struct device *dev, void *data,
return ret;
}
-static int it66121_audio_get_eld(struct device *dev, void *data,
- u8 *buf, size_t len)
-{
- struct it66121_ctx *ctx = dev_get_drvdata(dev);
-
- mutex_lock(&ctx->lock);
- if (!ctx->connector) {
- /* Pass en empty ELD if connector not available */
- dev_dbg(dev, "No connector present, passing empty EDID data");
- memset(buf, 0, len);
- } else {
- mutex_lock(&ctx->connector->eld_mutex);
- memcpy(buf, ctx->connector->eld,
- min(sizeof(ctx->connector->eld), len));
- mutex_unlock(&ctx->connector->eld_mutex);
- }
- mutex_unlock(&ctx->lock);
-
- return 0;
-}
-
-static const struct hdmi_codec_ops it66121_audio_codec_ops = {
- .hw_params = it66121_audio_hw_params,
- .audio_startup = it66121_audio_startup,
- .audio_shutdown = it66121_audio_shutdown,
- .mute_stream = it66121_audio_mute,
- .get_eld = it66121_audio_get_eld,
+static const struct drm_bridge_funcs it66121_bridge_funcs = {
+ .atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state,
+ .atomic_destroy_state = drm_atomic_helper_bridge_destroy_state,
+ .atomic_reset = drm_atomic_helper_bridge_reset,
+ .attach = it66121_bridge_attach,
+ .atomic_get_output_bus_fmts = it66121_bridge_atomic_get_output_bus_fmts,
+ .atomic_get_input_bus_fmts = it66121_bridge_atomic_get_input_bus_fmts,
+ .atomic_enable = it66121_bridge_enable,
+ .atomic_disable = it66121_bridge_disable,
+ .atomic_check = it66121_bridge_check,
+ .mode_set = it66121_bridge_mode_set,
+ .mode_valid = it66121_bridge_mode_valid,
+ .detect = it66121_bridge_detect,
+ .edid_read = it66121_bridge_edid_read,
+ .hpd_enable = it66121_bridge_hpd_enable,
+ .hpd_disable = it66121_bridge_hpd_disable,
+ .hdmi_audio_startup = it66121_hdmi_audio_startup,
+ .hdmi_audio_prepare = it66121_hdmi_audio_prepare,
+ .hdmi_audio_shutdown = it66121_hdmi_audio_shutdown,
+ .hdmi_audio_mute_stream = it66121_hdmi_audio_mute_stream,
};
-static int it66121_audio_codec_init(struct it66121_ctx *ctx, struct device *dev)
-{
- struct hdmi_codec_pdata codec_data = {
- .ops = &it66121_audio_codec_ops,
- .i2s = 1, /* Only i2s support for now */
- .spdif = 0,
- .max_i2s_channels = 8,
- .no_capture_mute = 1,
- };
-
- if (!of_property_present(dev->of_node, "#sound-dai-cells")) {
- dev_info(dev, "No \"#sound-dai-cells\", no audio\n");
- return 0;
- }
-
- ctx->audio.pdev = platform_device_register_data(dev,
- HDMI_CODEC_DRV_NAME,
- PLATFORM_DEVID_AUTO,
- &codec_data,
- sizeof(codec_data));
-
- if (IS_ERR(ctx->audio.pdev)) {
- dev_err(dev, "Failed to initialize HDMI audio codec: %d\n",
- PTR_ERR_OR_ZERO(ctx->audio.pdev));
- }
-
- return PTR_ERR_OR_ZERO(ctx->audio.pdev);
-}
-
static const char * const it66121_supplies[] = {
"vcn33", "vcn18", "vrf12"
};
@@ -1602,7 +1552,15 @@ static int it66121_probe(struct i2c_client *client)
}
}
- it66121_audio_codec_init(ctx, dev);
+ if (of_property_present(dev->of_node, "#sound-dai-cells")) {
+ ctx->bridge.ops |= DRM_BRIDGE_OP_HDMI_AUDIO;
+ ctx->bridge.hdmi_audio_dev = dev;
+ ctx->bridge.hdmi_audio_max_i2s_playback_channels = 8;
+ /* of-graph not supported, phandle match only */
+ ctx->bridge.hdmi_audio_dai_port = -1;
+ } else {
+ dev_info(dev, "No \"#sound-dai-cells\", no audio\n");
+ }
drm_bridge_add(&ctx->bridge);
diff --git a/drivers/gpu/drm/bridge/lontium-lt8713sx.c b/drivers/gpu/drm/bridge/lontium-lt8713sx.c
index 18fac6a46db4..cee485adf5e5 100644
--- a/drivers/gpu/drm/bridge/lontium-lt8713sx.c
+++ b/drivers/gpu/drm/bridge/lontium-lt8713sx.c
@@ -32,7 +32,6 @@ DECLARE_CRC8_TABLE(lt8713sx_crc_table);
struct lt8713sx {
struct device *dev;
struct drm_bridge bridge;
- struct drm_bridge *next_bridge;
struct regmap *regmap;
/* Protects all accesses to registers by stopping the on-chip MCU */
@@ -458,7 +457,7 @@ static int lt8713sx_bridge_attach(struct drm_bridge *bridge,
struct lt8713sx *lt8713sx = container_of(bridge, struct lt8713sx, bridge);
return drm_bridge_attach(encoder,
- lt8713sx->next_bridge,
+ lt8713sx->bridge.next_bridge,
bridge, flags);
}
@@ -537,10 +536,9 @@ static int lt8713sx_probe(struct i2c_client *client)
if (IS_ERR(lt8713sx->regmap))
return dev_err_probe(dev, PTR_ERR(lt8713sx->regmap), "regmap i2c init failed\n");
- ret = drm_of_find_panel_or_bridge(lt8713sx->dev->of_node, 1, -1, NULL,
- &lt8713sx->next_bridge);
- if (ret < 0)
- return ret;
+ lt8713sx->bridge.next_bridge = of_drm_get_bridge_by_endpoint(lt8713sx->dev->of_node, 1, -1);
+ if (IS_ERR(lt8713sx->bridge.next_bridge))
+ return PTR_ERR(lt8713sx->bridge.next_bridge);
ret = lt8713sx_gpio_init(lt8713sx);
if (ret < 0)
diff --git a/drivers/gpu/drm/bridge/lontium-lt9611.c b/drivers/gpu/drm/bridge/lontium-lt9611.c
index 0f49b13193b9..21305296e111 100644
--- a/drivers/gpu/drm/bridge/lontium-lt9611.c
+++ b/drivers/gpu/drm/bridge/lontium-lt9611.c
@@ -37,7 +37,6 @@
struct lt9611 {
struct device *dev;
struct drm_bridge bridge;
- struct drm_bridge *next_bridge;
struct regmap *regmap;
@@ -761,7 +760,7 @@ static int lt9611_bridge_attach(struct drm_bridge *bridge,
{
struct lt9611 *lt9611 = bridge_to_lt9611(bridge);
- return drm_bridge_attach(encoder, lt9611->next_bridge,
+ return drm_bridge_attach(encoder, lt9611->bridge.next_bridge,
bridge, flags);
}
@@ -1058,7 +1057,11 @@ static int lt9611_parse_dt(struct device *dev,
lt9611->ac_mode = of_property_read_bool(dev->of_node, "lt,ac-mode");
- return drm_of_find_panel_or_bridge(dev->of_node, 2, -1, NULL, &lt9611->next_bridge);
+ lt9611->bridge.next_bridge = of_drm_get_bridge_by_endpoint(dev->of_node, 2, -1);
+ if (IS_ERR(lt9611->bridge.next_bridge))
+ return PTR_ERR(lt9611->bridge.next_bridge);
+
+ return 0;
}
static int lt9611_gpio_init(struct lt9611 *lt9611)
diff --git a/drivers/gpu/drm/bridge/lontium-lt9611uxc.c b/drivers/gpu/drm/bridge/lontium-lt9611uxc.c
index 11aab07d88df..9427cc2358ae 100644
--- a/drivers/gpu/drm/bridge/lontium-lt9611uxc.c
+++ b/drivers/gpu/drm/bridge/lontium-lt9611uxc.c
@@ -35,7 +35,6 @@
struct lt9611uxc {
struct device *dev;
struct drm_bridge bridge;
- struct drm_bridge *next_bridge;
struct regmap *regmap;
/* Protects all accesses to registers by stopping the on-chip MCU */
@@ -284,7 +283,7 @@ static int lt9611uxc_bridge_attach(struct drm_bridge *bridge,
{
struct lt9611uxc *lt9611uxc = bridge_to_lt9611uxc(bridge);
- return drm_bridge_attach(encoder, lt9611uxc->next_bridge,
+ return drm_bridge_attach(encoder, lt9611uxc->bridge.next_bridge,
bridge, flags);
}
@@ -487,7 +486,11 @@ static int lt9611uxc_parse_dt(struct device *dev,
lt9611uxc->dsi1_node = of_graph_get_remote_node(dev->of_node, 1, -1);
- return drm_of_find_panel_or_bridge(dev->of_node, 2, -1, NULL, &lt9611uxc->next_bridge);
+ lt9611uxc->bridge.next_bridge = of_drm_get_bridge_by_endpoint(dev->of_node, 2, -1);
+ if (IS_ERR(lt9611uxc->bridge.next_bridge))
+ return PTR_ERR(lt9611uxc->bridge.next_bridge);
+
+ return 0;
}
static int lt9611uxc_gpio_init(struct lt9611uxc *lt9611uxc)
diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi-qp.c b/drivers/gpu/drm/bridge/synopsys/dw-hdmi-qp.c
index 0dbb12743609..1c214a8e6dc2 100644
--- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi-qp.c
+++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi-qp.c
@@ -38,8 +38,6 @@
#define DDC_CI_ADDR 0x37
#define DDC_SEGMENT_ADDR 0x30
-#define HDMI14_MAX_TMDSCLK 340000000
-
#define SCRAMB_POLL_DELAY_MS 3000
/*
@@ -835,9 +833,9 @@ dw_hdmi_qp_bridge_tmds_char_rate_valid(const struct drm_bridge *bridge,
/*
* TODO: when hdmi->no_hpd is 1 we must not support modes that
* require scrambling, including every mode with a clock above
- * HDMI14_MAX_TMDSCLK.
+ * HDMI_1_3_TMDS_CHAR_RATE_MAX_HZ.
*/
- if (rate > HDMI14_MAX_TMDSCLK) {
+ if (rate > HDMI_1_3_TMDS_CHAR_RATE_MAX_HZ) {
dev_dbg(hdmi->dev, "Unsupported TMDS char rate: %lld\n", rate);
return MODE_CLOCK_HIGH;
}
diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
index 41b3a9cfa2f5..17d5caedb32e 100644
--- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
+++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
@@ -51,8 +51,6 @@
/* DW-HDMI Controller >= 0x200a are at least compliant with SCDC version 1 */
#define SCDC_MIN_SOURCE_VERSION 0x1
-#define HDMI14_MAX_TMDSCLK 340000000
-
static const u16 csc_coeff_default[3][4] = {
{ 0x2000, 0x0000, 0x0000, 0x0000 },
{ 0x0000, 0x2000, 0x0000, 0x0000 },
@@ -1426,7 +1424,7 @@ void dw_hdmi_set_high_tmds_clock_ratio(struct dw_hdmi *hdmi,
/* Control for TMDS Bit Period/TMDS Clock-Period Ratio */
if (dw_hdmi_support_scdc(hdmi, display)) {
- if (mtmdsclock > HDMI14_MAX_TMDSCLK)
+ if (mtmdsclock > HDMI_1_3_TMDS_CHAR_RATE_MAX_HZ)
drm_scdc_set_high_tmds_clock_ratio(hdmi->curr_conn, 1);
else
drm_scdc_set_high_tmds_clock_ratio(hdmi->curr_conn, 0);
@@ -1671,7 +1669,7 @@ static int hdmi_phy_configure(struct dw_hdmi *hdmi,
}
/* Wait for resuming transmission of TMDS clock and data */
- if (mtmdsclock > HDMI14_MAX_TMDSCLK)
+ if (mtmdsclock > HDMI_1_3_TMDS_CHAR_RATE_MAX_HZ)
msleep(100);
return dw_hdmi_phy_power_on(hdmi);
@@ -2032,7 +2030,7 @@ static void hdmi_av_composer(struct dw_hdmi *hdmi,
/* Set up HDMI_FC_INVIDCONF */
inv_val = (hdmi->hdmi_data.hdcp_enable ||
(dw_hdmi_support_scdc(hdmi, display) &&
- (vmode->mtmdsclock > HDMI14_MAX_TMDSCLK ||
+ (vmode->mtmdsclock > HDMI_1_3_TMDS_CHAR_RATE_MAX_HZ ||
hdmi_info->scdc.scrambling.low_rates)) ?
HDMI_FC_INVIDCONF_HDCP_KEEPOUT_ACTIVE :
HDMI_FC_INVIDCONF_HDCP_KEEPOUT_INACTIVE);
@@ -2100,7 +2098,7 @@ static void hdmi_av_composer(struct dw_hdmi *hdmi,
/* Scrambling Control */
if (dw_hdmi_support_scdc(hdmi, display)) {
- if (vmode->mtmdsclock > HDMI14_MAX_TMDSCLK ||
+ if (vmode->mtmdsclock > HDMI_1_3_TMDS_CHAR_RATE_MAX_HZ ||
hdmi_info->scdc.scrambling.low_rates) {
/*
* HDMI2.0 Specifies the following procedure:
diff --git a/drivers/gpu/drm/display/drm_dp_mst_topology.c b/drivers/gpu/drm/display/drm_dp_mst_topology.c
index fe72245fb9ca..4de36fda0544 100644
--- a/drivers/gpu/drm/display/drm_dp_mst_topology.c
+++ b/drivers/gpu/drm/display/drm_dp_mst_topology.c
@@ -880,7 +880,7 @@ static bool drm_dp_sideband_parse_remote_dpcd_read(struct drm_dp_sideband_msg_rx
goto fail_len;
repmsg->u.remote_dpcd_read_ack.num_bytes = raw->msg[idx];
idx++;
- if (idx > raw->curlen)
+ if (idx + repmsg->u.remote_dpcd_read_ack.num_bytes > raw->curlen)
goto fail_len;
memcpy(repmsg->u.remote_dpcd_read_ack.bytes, &raw->msg[idx], repmsg->u.remote_dpcd_read_ack.num_bytes);
@@ -916,7 +916,9 @@ static bool drm_dp_sideband_parse_remote_i2c_read_ack(struct drm_dp_sideband_msg
goto fail_len;
repmsg->u.remote_i2c_read_ack.num_bytes = raw->msg[idx];
idx++;
- /* TODO check */
+ if (idx + repmsg->u.remote_i2c_read_ack.num_bytes > raw->curlen)
+ goto fail_len;
+
memcpy(repmsg->u.remote_i2c_read_ack.bytes, &raw->msg[idx], repmsg->u.remote_i2c_read_ack.num_bytes);
return true;
fail_len:
@@ -932,16 +934,13 @@ static bool drm_dp_sideband_parse_enum_path_resources_ack(struct drm_dp_sideband
repmsg->u.path_resources.port_number = (raw->msg[idx] >> 4) & 0xf;
repmsg->u.path_resources.fec_capable = raw->msg[idx] & 0x1;
idx++;
- if (idx > raw->curlen)
+ if (idx + 2 > raw->curlen)
goto fail_len;
repmsg->u.path_resources.full_payload_bw_number = (raw->msg[idx] << 8) | (raw->msg[idx+1]);
idx += 2;
- if (idx > raw->curlen)
+ if (idx + 2 > raw->curlen)
goto fail_len;
repmsg->u.path_resources.avail_payload_bw_number = (raw->msg[idx] << 8) | (raw->msg[idx+1]);
- idx += 2;
- if (idx > raw->curlen)
- goto fail_len;
return true;
fail_len:
DRM_DEBUG_KMS("enum resource parse length fail %d %d\n", idx, raw->curlen);
@@ -959,12 +958,9 @@ static bool drm_dp_sideband_parse_allocate_payload_ack(struct drm_dp_sideband_ms
goto fail_len;
repmsg->u.allocate_payload.vcpi = raw->msg[idx];
idx++;
- if (idx > raw->curlen)
+ if (idx + 2 > raw->curlen)
goto fail_len;
repmsg->u.allocate_payload.allocated_pbn = (raw->msg[idx] << 8) | (raw->msg[idx+1]);
- idx += 2;
- if (idx > raw->curlen)
- goto fail_len;
return true;
fail_len:
DRM_DEBUG_KMS("allocate payload parse length fail %d %d\n", idx, raw->curlen);
@@ -978,12 +974,9 @@ static bool drm_dp_sideband_parse_query_payload_ack(struct drm_dp_sideband_msg_r
repmsg->u.query_payload.port_number = (raw->msg[idx] >> 4) & 0xf;
idx++;
- if (idx > raw->curlen)
+ if (idx + 2 > raw->curlen)
goto fail_len;
repmsg->u.query_payload.allocated_pbn = (raw->msg[idx] << 8) | (raw->msg[idx + 1]);
- idx += 2;
- if (idx > raw->curlen)
- goto fail_len;
return true;
fail_len:
DRM_DEBUG_KMS("query payload parse length fail %d %d\n", idx, raw->curlen);
diff --git a/drivers/gpu/drm/drm_bridge.c b/drivers/gpu/drm/drm_bridge.c
index ce180f0b26b2..687b36eea0c7 100644
--- a/drivers/gpu/drm/drm_bridge.c
+++ b/drivers/gpu/drm/drm_bridge.c
@@ -300,7 +300,7 @@ EXPORT_SYMBOL(drm_bridge_get);
/**
* drm_bridge_put - Release a bridge reference
- * @bridge: DRM bridge; if NULL this function does nothing
+ * @bridge: DRM bridge; if NULL or an ERR_PTR this function does nothing
*
* This function decrements the bridge's reference count and frees the
* object if the reference count drops to zero.
@@ -310,7 +310,7 @@ EXPORT_SYMBOL(drm_bridge_get);
*/
void drm_bridge_put(struct drm_bridge *bridge)
{
- if (bridge)
+ if (!IS_ERR_OR_NULL(bridge))
kref_put(&bridge->refcount, __drm_bridge_free);
}
EXPORT_SYMBOL(drm_bridge_put);
@@ -1582,6 +1582,47 @@ struct drm_bridge *of_drm_find_bridge(struct device_node *np)
return bridge;
}
EXPORT_SYMBOL(of_drm_find_bridge);
+
+/**
+ * of_drm_get_bridge_by_endpoint - return DRM bridge connected to a port/endpoint
+ * @np: device tree node containing output ports
+ * @port: port in the device tree node, or -1 for the first port found
+ * @endpoint: endpoint in the device tree node, or -1 for the first endpoint found
+ *
+ * Given a DT node's port and endpoint number, find the connected node and
+ * return the associated drm_bridge device.
+ *
+ * The refcount of the returned bridge is incremented. Use drm_bridge_put()
+ * when done with it.
+ *
+ * Returns a pointer to the connected drm_bridge, or a negative error on failure
+ */
+struct drm_bridge *of_drm_get_bridge_by_endpoint(const struct device_node *np,
+ int port, int endpoint)
+{
+ struct drm_bridge *bridge;
+
+ /*
+ * of_graph_get_remote_node() produces a noisy error message if port
+ * node isn't found and the absence of the port is a legit case here,
+ * so at first we silently check whether graph is present in the
+ * device-tree node.
+ */
+ if (!of_graph_is_present(np))
+ return ERR_PTR(-ENODEV);
+
+ struct device_node *remote __free(device_node) =
+ of_graph_get_remote_node(np, port, endpoint);
+ if (!remote)
+ return ERR_PTR(-ENODEV);
+
+ bridge = of_drm_find_and_get_bridge(remote);
+ if (!bridge)
+ return ERR_PTR(-EPROBE_DEFER);
+
+ return bridge;
+}
+EXPORT_SYMBOL_GPL(of_drm_get_bridge_by_endpoint);
#endif
/**
diff --git a/drivers/gpu/drm/drm_of.c b/drivers/gpu/drm/drm_of.c
index ef6b09316963..d03ada82eac9 100644
--- a/drivers/gpu/drm/drm_of.c
+++ b/drivers/gpu/drm/drm_of.c
@@ -225,15 +225,15 @@ EXPORT_SYMBOL_GPL(drm_of_encoder_active_endpoint);
* @np: device tree node containing encoder output ports
* @port: port in the device tree node
* @endpoint: endpoint in the device tree node
- * @panel: pointer to hold returned drm_panel
+ * @panel: pointer to hold returned drm_panel, must not be NULL
* @bridge: pointer to hold returned drm_bridge
*
* Given a DT node's port and endpoint number, find the connected node and
- * return either the associated struct drm_panel or drm_bridge device. Either
- * @panel or @bridge must not be NULL.
+ * return either the associated struct drm_panel or drm_bridge device.
*
* This function is deprecated and should not be used in new drivers. Use
- * devm_drm_of_get_bridge() instead.
+ * of_drm_get_bridge_by_endpoint() instead when not looking for a panel, or
+ * devm_drm_of_get_bridge() otherwise.
*
* Returns zero if successful, or one of the standard error codes if it fails.
*/
@@ -245,10 +245,10 @@ int drm_of_find_panel_or_bridge(const struct device_node *np,
int ret = -EPROBE_DEFER;
struct device_node *remote;
- if (!panel && !bridge)
+ if (WARN_ON(!panel))
return -EINVAL;
- if (panel)
- *panel = NULL;
+
+ *panel = NULL;
/*
* of_graph_get_remote_node() produces a noisy error message if port
@@ -263,13 +263,11 @@ int drm_of_find_panel_or_bridge(const struct device_node *np,
if (!remote)
return -ENODEV;
- if (panel) {
- *panel = of_drm_find_panel(remote);
- if (!IS_ERR(*panel))
- ret = 0;
- else
- *panel = NULL;
- }
+ *panel = of_drm_find_panel(remote);
+ if (!IS_ERR(*panel))
+ ret = 0;
+ else
+ *panel = NULL;
if (bridge) {
if (ret) {
diff --git a/drivers/gpu/drm/drm_syncobj.c b/drivers/gpu/drm/drm_syncobj.c
index 8d9fd1917c6e..c9dbf64c0c9f 100644
--- a/drivers/gpu/drm/drm_syncobj.c
+++ b/drivers/gpu/drm/drm_syncobj.c
@@ -442,13 +442,15 @@ int drm_syncobj_find_fence(struct drm_file *file_private,
u64 timeout = nsecs_to_jiffies64(DRM_SYNCOBJ_WAIT_FOR_SUBMIT_TIMEOUT);
int ret;
- if (flags & ~DRM_SYNCOBJ_WAIT_FLAGS_WAIT_FOR_SUBMIT)
- return -EINVAL;
-
if (!syncobj)
return -ENOENT;
- /* Waiting for userspace with locks help is illegal cause that can
+ if (flags & ~DRM_SYNCOBJ_WAIT_FLAGS_WAIT_FOR_SUBMIT) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ /* Waiting for userspace with locks held is illegal cause that can
* trivial deadlock with page faults for example. Make lockdep complain
* about it early on.
*/
diff --git a/drivers/gpu/drm/hisilicon/kirin/dw_drm_dsi.c b/drivers/gpu/drm/hisilicon/kirin/dw_drm_dsi.c
index e80debdc4176..ab3cd309505a 100644
--- a/drivers/gpu/drm/hisilicon/kirin/dw_drm_dsi.c
+++ b/drivers/gpu/drm/hisilicon/kirin/dw_drm_dsi.c
@@ -778,17 +778,16 @@ static int dsi_host_init(struct device *dev, struct dw_dsi *dsi)
static int dsi_bridge_init(struct drm_device *dev, struct dw_dsi *dsi)
{
struct drm_encoder *encoder = &dsi->encoder;
- struct drm_bridge *bridge;
+ struct drm_bridge *bridge __free(drm_bridge_put) = NULL;
struct device_node *np = dsi->dev->of_node;
- int ret;
/*
* Get the endpoint node. In our case, dsi has one output port1
* to which the external HDMI bridge is connected.
*/
- ret = drm_of_find_panel_or_bridge(np, 1, 0, NULL, &bridge);
- if (ret)
- return ret;
+ bridge = of_drm_get_bridge_by_endpoint(np, 1, 0);
+ if (IS_ERR(bridge))
+ return PTR_ERR(bridge);
/* associate the bridge to dsi encoder */
return drm_bridge_attach(encoder, bridge, NULL, 0);
diff --git a/drivers/gpu/drm/imagination/Makefile b/drivers/gpu/drm/imagination/Makefile
index 1222a14262e4..a5c5e386f16a 100644
--- a/drivers/gpu/drm/imagination/Makefile
+++ b/drivers/gpu/drm/imagination/Makefile
@@ -32,6 +32,9 @@ powervr-y := \
powervr-$(CONFIG_DEBUG_FS) += \
pvr_debugfs.o
+powervr-$(CONFIG_TRACEPOINTS) += \
+ pvr_trace_points.o
+
obj-$(CONFIG_DRM_POWERVR) += powervr.o
obj-$(CONFIG_DRM_POWERVR_KUNIT_TEST) += pvr_test.o
diff --git a/drivers/gpu/drm/imagination/pvr_context.c b/drivers/gpu/drm/imagination/pvr_context.c
index 8de70c30b9de..eba4694400b5 100644
--- a/drivers/gpu/drm/imagination/pvr_context.c
+++ b/drivers/gpu/drm/imagination/pvr_context.c
@@ -318,10 +318,14 @@ int pvr_context_create(struct pvr_file *pvr_file, struct drm_pvr_ioctl_create_co
goto err_put_vm;
}
- err = pvr_context_create_queues(ctx, args, ctx->data);
+ err = xa_alloc(&pvr_dev->ctx_ids, &ctx->ctx_id, ctx, xa_limit_32b, GFP_KERNEL);
if (err)
goto err_free_ctx_data;
+ err = pvr_context_create_queues(ctx, args, ctx->data);
+ if (err)
+ goto err_free_ctx_id;
+
err = init_fw_objs(ctx, args, ctx->data);
if (err)
goto err_destroy_queues;
@@ -329,23 +333,12 @@ int pvr_context_create(struct pvr_file *pvr_file, struct drm_pvr_ioctl_create_co
err = pvr_fw_object_create(pvr_dev, ctx_size, PVR_BO_FW_FLAGS_DEVICE_UNCACHED,
ctx_fw_data_init, ctx, &ctx->fw_obj);
if (err)
- goto err_free_ctx_data;
+ goto err_destroy_queues;
- err = xa_alloc(&pvr_dev->ctx_ids, &ctx->ctx_id, ctx, xa_limit_32b, GFP_KERNEL);
+ err = xa_alloc(&pvr_file->ctx_handles, &args->handle, ctx, xa_limit_32b, GFP_KERNEL);
if (err)
goto err_destroy_fw_obj;
- err = xa_alloc(&pvr_file->ctx_handles, &args->handle, ctx, xa_limit_32b, GFP_KERNEL);
- if (err) {
- /*
- * It's possible that another thread could have taken a reference on the context at
- * this point as it is in the ctx_ids xarray. Therefore instead of directly
- * destroying the context, drop a reference instead.
- */
- pvr_context_put(ctx);
- return err;
- }
-
spin_lock(&pvr_dev->ctx_list_lock);
list_add_tail(&ctx->file_link, &pvr_file->contexts);
spin_unlock(&pvr_dev->ctx_list_lock);
@@ -358,6 +351,15 @@ err_destroy_fw_obj:
err_destroy_queues:
pvr_context_destroy_queues(ctx);
+err_free_ctx_id:
+ /*
+ * Ctx_id is not exposed to userspace and not visible yet within
+ * the kernel/FW, plus a matching context handle (exposed to userspace)
+ * hasn't been allocated yet, so it is safe to remove ctx_id
+ * from the ctx_ids xarray.
+ */
+ xa_erase(&pvr_dev->ctx_ids, ctx->ctx_id);
+
err_free_ctx_data:
kfree(ctx->data);
diff --git a/drivers/gpu/drm/imagination/pvr_device.c b/drivers/gpu/drm/imagination/pvr_device.c
index dbb6f5a8ded1..2691ef9af0ca 100644
--- a/drivers/gpu/drm/imagination/pvr_device.c
+++ b/drivers/gpu/drm/imagination/pvr_device.c
@@ -213,7 +213,7 @@ static irqreturn_t pvr_device_irq_thread_handler(int irq, void *data)
while (pvr_fw_irq_pending(pvr_dev)) {
pvr_fw_irq_clear(pvr_dev);
- if (pvr_dev->fw_dev.booted) {
+ if (READ_ONCE(pvr_dev->fw_dev.initialised)) {
pvr_fwccb_process(pvr_dev);
pvr_kccb_wake_up_waiters(pvr_dev);
pvr_device_process_active_queues(pvr_dev);
diff --git a/drivers/gpu/drm/imagination/pvr_drv.c b/drivers/gpu/drm/imagination/pvr_drv.c
index 268900464ab6..b20c462bcba0 100644
--- a/drivers/gpu/drm/imagination/pvr_drv.c
+++ b/drivers/gpu/drm/imagination/pvr_drv.c
@@ -14,6 +14,7 @@
#include "pvr_rogue_defs.h"
#include "pvr_rogue_fwif_client.h"
#include "pvr_rogue_fwif_shared.h"
+#include "pvr_trace.h"
#include "pvr_vm.h"
#include <uapi/drm/pvr_drm.h>
@@ -1150,6 +1151,8 @@ pvr_ioctl_submit_jobs(struct drm_device *drm_dev, void *raw_args,
int idx;
int err;
+ trace_pvr_job_submit_ioctl(pvr_dev, args->jobs.count);
+
if (!drm_dev_enter(drm_dev, &idx))
return -EIO;
diff --git a/drivers/gpu/drm/imagination/pvr_fw.c b/drivers/gpu/drm/imagination/pvr_fw.c
index 288516dc2560..850a3ec8e775 100644
--- a/drivers/gpu/drm/imagination/pvr_fw.c
+++ b/drivers/gpu/drm/imagination/pvr_fw.c
@@ -1004,7 +1004,7 @@ pvr_fw_init(struct pvr_device *pvr_dev)
goto err_fw_stop;
}
- fw_dev->booted = true;
+ WRITE_ONCE(fw_dev->initialised, true);
return 0;
@@ -1044,7 +1044,7 @@ pvr_fw_fini(struct pvr_device *pvr_dev)
{
struct pvr_fw_device *fw_dev = &pvr_dev->fw_dev;
- fw_dev->booted = false;
+ WRITE_ONCE(fw_dev->initialised, false);
pvr_fw_destroy_structures(pvr_dev);
pvr_fw_object_unmap_and_destroy(pvr_dev->kccb.rtn_obj);
diff --git a/drivers/gpu/drm/imagination/pvr_fw.h b/drivers/gpu/drm/imagination/pvr_fw.h
index 1404dd492d7c..3390c84e4fd3 100644
--- a/drivers/gpu/drm/imagination/pvr_fw.h
+++ b/drivers/gpu/drm/imagination/pvr_fw.h
@@ -295,8 +295,11 @@ struct pvr_fw_device {
/** @mem: Structure containing objects representing firmware memory allocations. */
struct pvr_fw_mem mem;
- /** @booted: %true if the firmware has been booted, %false otherwise. */
- bool booted;
+ /**
+ * @initialised: %true if the firmware has been successfully initialised,
+ * %false otherwise.
+ */
+ bool initialised;
/**
* @processor_type: FW processor type for this device. Must be one of
diff --git a/drivers/gpu/drm/imagination/pvr_job.c b/drivers/gpu/drm/imagination/pvr_job.c
index dd9f5df01e08..b8a58d817009 100644
--- a/drivers/gpu/drm/imagination/pvr_job.c
+++ b/drivers/gpu/drm/imagination/pvr_job.c
@@ -14,6 +14,7 @@
#include "pvr_stream.h"
#include "pvr_stream_defs.h"
#include "pvr_sync.h"
+#include "pvr_trace.h"
#include <drm/drm_exec.h>
#include <drm/drm_gem.h>
@@ -510,6 +511,8 @@ static int pvr_job_data_init(struct pvr_device *pvr_dev,
}
job_data_out[i].sync_op_count = job_args[i].sync_ops.count;
+
+ trace_pvr_job_create(pvr_dev, job_data_out[i].job, job_data_out[i].sync_op_count);
}
return 0;
diff --git a/drivers/gpu/drm/imagination/pvr_mmu.c b/drivers/gpu/drm/imagination/pvr_mmu.c
index 2e4da5b2c499..3cac482e1034 100644
--- a/drivers/gpu/drm/imagination/pvr_mmu.c
+++ b/drivers/gpu/drm/imagination/pvr_mmu.c
@@ -133,8 +133,8 @@ int pvr_mmu_flush_exec(struct pvr_device *pvr_dev, bool wait)
if (!drm_dev_enter(from_pvr_device(pvr_dev), &idx))
return -EIO;
- /* Can't flush MMU if the firmware hasn't booted yet. */
- if (!pvr_dev->fw_dev.booted)
+ /* Can't flush MMU if the firmware hasn't been initialised yet. */
+ if (!READ_ONCE(pvr_dev->fw_dev.initialised))
goto err_drm_dev_exit;
cmd_mmu_cache_data->cache_flags =
diff --git a/drivers/gpu/drm/imagination/pvr_power.c b/drivers/gpu/drm/imagination/pvr_power.c
index 88fcce433d12..a71d5b35601e 100644
--- a/drivers/gpu/drm/imagination/pvr_power.c
+++ b/drivers/gpu/drm/imagination/pvr_power.c
@@ -216,7 +216,7 @@ pvr_watchdog_worker(struct work_struct *work)
if (pm_runtime_get_if_in_use(from_pvr_device(pvr_dev)->dev) <= 0)
goto out_requeue;
- if (!pvr_dev->fw_dev.booted)
+ if (!READ_ONCE(pvr_dev->fw_dev.initialised))
goto out_pm_runtime_put;
stalled = pvr_watchdog_kccb_stalled(pvr_dev);
@@ -378,7 +378,7 @@ pvr_power_device_suspend(struct device *dev)
if (!drm_dev_enter(drm_dev, &idx))
return -EIO;
- if (pvr_dev->fw_dev.booted) {
+ if (READ_ONCE(pvr_dev->fw_dev.initialised)) {
err = pvr_power_fw_disable(pvr_dev, false, true);
if (err)
goto err_drm_dev_exit;
@@ -408,7 +408,7 @@ pvr_power_device_resume(struct device *dev)
if (err)
goto err_drm_dev_exit;
- if (pvr_dev->fw_dev.booted) {
+ if (READ_ONCE(pvr_dev->fw_dev.initialised)) {
err = pvr_power_fw_enable(pvr_dev, true);
if (err)
goto err_power_off;
@@ -548,7 +548,7 @@ pvr_power_reset(struct pvr_device *pvr_dev, bool hard_reset)
err = pvr_power_fw_disable(pvr_dev, hard_reset, false);
if (!err) {
if (hard_reset) {
- pvr_dev->fw_dev.booted = false;
+ WRITE_ONCE(pvr_dev->fw_dev.initialised, false);
WARN_ON(pvr_power_device_suspend(from_pvr_device(pvr_dev)->dev));
err = pvr_fw_hard_reset(pvr_dev);
@@ -556,7 +556,7 @@ pvr_power_reset(struct pvr_device *pvr_dev, bool hard_reset)
goto err_device_lost;
err = pvr_power_device_resume(from_pvr_device(pvr_dev)->dev);
- pvr_dev->fw_dev.booted = true;
+ WRITE_ONCE(pvr_dev->fw_dev.initialised, true);
if (err)
goto err_device_lost;
} else {
diff --git a/drivers/gpu/drm/imagination/pvr_queue.c b/drivers/gpu/drm/imagination/pvr_queue.c
index d10a13173f0f..7ed60e1c1a86 100644
--- a/drivers/gpu/drm/imagination/pvr_queue.c
+++ b/drivers/gpu/drm/imagination/pvr_queue.c
@@ -10,6 +10,7 @@
#include "pvr_drv.h"
#include "pvr_job.h"
#include "pvr_queue.h"
+#include "pvr_trace.h"
#include "pvr_vm.h"
#include "pvr_rogue_fwif_client.h"
@@ -725,6 +726,8 @@ static void pvr_queue_submit_job_to_cccb(struct pvr_job *job)
cmd->partial_render_geom_frag_fence.value = job->done_fence->seqno - 1;
}
+ trace_pvr_job_submit_fw(job);
+
/* Submit job to FW */
pvr_cccb_write_command_with_header(cccb, job->fw_ccb_cmd_type, job->cmd_len, job->cmd,
job->id, job->id);
@@ -851,7 +854,9 @@ static void pvr_queue_start(struct pvr_queue *queue)
* the scheduler, and re-assign parent fences in the middle.
*
* Return:
- * * DRM_GPU_SCHED_STAT_RESET.
+ * *%DRM_GPU_SCHED_STAT_NO_HANG if the job fence has already been
+ * signaled, or
+ * *%DRM_GPU_SCHED_STAT_RESET otherwise.
*/
static enum drm_gpu_sched_stat
pvr_queue_timedout_job(struct drm_sched_job *s_job)
@@ -862,6 +867,9 @@ pvr_queue_timedout_job(struct drm_sched_job *s_job)
struct pvr_job *job;
u32 job_count = 0;
+ if (dma_fence_is_signaled(s_job->s_fence->parent))
+ return DRM_GPU_SCHED_STAT_NO_HANG;
+
dev_err(sched->dev, "Job timeout\n");
/* Before we stop the scheduler, make sure the queue is out of any list, so
@@ -1193,6 +1201,8 @@ void pvr_queue_job_cleanup(struct pvr_job *job)
if (job->base.s_fence)
drm_sched_job_cleanup(&job->base);
+
+ trace_pvr_job_done(job);
}
/**
diff --git a/drivers/gpu/drm/imagination/pvr_trace.h b/drivers/gpu/drm/imagination/pvr_trace.h
new file mode 100644
index 000000000000..ffbbde3b2bcc
--- /dev/null
+++ b/drivers/gpu/drm/imagination/pvr_trace.h
@@ -0,0 +1,113 @@
+/* SPDX-License-Identifier: GPL-2.0 OR MIT */
+/* Copyright (c) 2026 Imagination Technologies Ltd. */
+
+#undef TRACE_SYSTEM
+#define TRACE_SYSTEM pvr
+
+#if !defined(PVR_TRACE_H) || defined(TRACE_HEADER_MULTI_READ)
+#define PVR_TRACE_H
+
+#include "pvr_context.h"
+#include "pvr_device.h"
+#include "pvr_fw.h"
+#include "pvr_hwrt.h"
+#include "pvr_job.h"
+#include "pvr_sync.h"
+
+#include <linux/stringify.h>
+#include <linux/types.h>
+#include <linux/tracepoint.h>
+
+/*
+ * NOTE:
+ * When adding trace points, or extra data to existing ones - you must capture
+ * all the data in the TP_fast_assign section that you wish to use in the
+ * TP_printk section. This is because the printk is performed on demand from the
+ * captured data when you `cat /sys/kernel/tracing/trace` and by the time this
+ * happens any pointers you captured will likely no longer point to valid data.
+ */
+
+/* Job submit */
+
+TRACE_EVENT(pvr_job_submit_ioctl,
+ TP_PROTO(struct pvr_device *pvr_dev, u32 count),
+ TP_ARGS(pvr_dev, count),
+ TP_STRUCT__entry(__field(struct pvr_device *, pvr_dev)
+ __field(u32, count)),
+ TP_fast_assign(__entry->pvr_dev = pvr_dev;
+ __entry->count = count;),
+ TP_printk("pvr_dev=%p count=%u",
+ __entry->pvr_dev,
+ __entry->count)
+);
+
+#define PVR_JOB_TYPE_TO_STR(val) \
+ __print_symbolic(val, \
+ { DRM_PVR_JOB_TYPE_GEOMETRY, "geometry" }, \
+ { DRM_PVR_JOB_TYPE_FRAGMENT, "fragment" }, \
+ { DRM_PVR_JOB_TYPE_COMPUTE, "compute" }, \
+ { DRM_PVR_JOB_TYPE_TRANSFER_FRAG, "transfer" })
+
+TRACE_EVENT(pvr_job_create,
+ TP_PROTO(struct pvr_device *pvr_dev, struct pvr_job *job,
+ u32 sync_op_count),
+ TP_ARGS(pvr_dev, job, sync_op_count),
+ TP_STRUCT__entry(__field(struct pvr_device *, pvr_dev)
+ __field(struct pvr_context *, ctx)
+ __field(struct pvr_fw_object *, fw_obj)
+ __field(u32, fw_addr)
+ __field(u32, hwrt_addr)
+ __field(struct pvr_job *, job)
+ __field(enum drm_pvr_job_type, job_type)
+ __field(u32, sync_op_count)),
+ TP_fast_assign(__entry->pvr_dev = pvr_dev;
+ __entry->ctx = job->ctx;
+ __entry->fw_obj = job->ctx->fw_obj;
+ pvr_fw_object_get_fw_addr(job->ctx->fw_obj, &__entry->fw_addr);
+ __entry->hwrt_addr = job->hwrt ?
+ job->hwrt->fw_obj->fw_addr_offset :
+ 0;
+ __entry->job = job;
+ __entry->job_type = job->type;
+ __entry->sync_op_count = sync_op_count;),
+ TP_printk("pvr_dev=%p ctx=%p fw_obj=%p fw_addr=0x%x hwrt_addr=0x%x job=%p job_type=%s sync_op_count=%u",
+ __entry->pvr_dev,
+ __entry->ctx,
+ __entry->fw_obj,
+ __entry->fw_addr,
+ __entry->hwrt_addr,
+ __entry->job,
+ PVR_JOB_TYPE_TO_STR(__entry->job_type),
+ __entry->sync_op_count)
+);
+
+#undef PVR_JOB_TYPE_TO_STR
+
+TRACE_EVENT(pvr_job_submit_fw,
+ TP_PROTO(struct pvr_job *job),
+ TP_ARGS(job),
+ TP_STRUCT__entry(__field(struct pvr_job *, job)
+ __field(u32, done_seqno)),
+ TP_fast_assign(__entry->job = job;
+ __entry->done_seqno = job->done_fence->seqno;),
+ TP_printk("job=%p done_seqno=%u",
+ __entry->job,
+ __entry->done_seqno)
+);
+
+TRACE_EVENT(pvr_job_done,
+ TP_PROTO(struct pvr_job *job),
+ TP_ARGS(job),
+ TP_STRUCT__entry(__field(struct pvr_job *, job)),
+ TP_fast_assign(__entry->job = job;),
+ TP_printk("job=%p", __entry->job)
+);
+
+#endif /* PVR_TRACE_H */
+
+/* This part must be outside protection */
+#undef TRACE_INCLUDE_PATH
+#undef TRACE_INCLUDE_FILE
+#define TRACE_INCLUDE_PATH ../../drivers/gpu/drm/imagination
+#define TRACE_INCLUDE_FILE pvr_trace
+#include <trace/define_trace.h>
diff --git a/drivers/gpu/drm/imagination/pvr_trace_points.c b/drivers/gpu/drm/imagination/pvr_trace_points.c
new file mode 100644
index 000000000000..71b53f267af0
--- /dev/null
+++ b/drivers/gpu/drm/imagination/pvr_trace_points.c
@@ -0,0 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0 OR MIT
+/* Copyright (c) 2026 Imagination Technologies Ltd. */
+
+#ifndef __CHECKER__
+#define CREATE_TRACE_POINTS
+#include "pvr_trace.h"
+#endif
diff --git a/drivers/gpu/drm/msm/hdmi/hdmi.c b/drivers/gpu/drm/msm/hdmi/hdmi.c
index d9491aac1a89..474006084633 100644
--- a/drivers/gpu/drm/msm/hdmi/hdmi.c
+++ b/drivers/gpu/drm/msm/hdmi/hdmi.c
@@ -285,19 +285,27 @@ static int msm_hdmi_dev_probe(struct platform_device *pdev)
spin_lock_init(&hdmi->reg_lock);
mutex_init(&hdmi->state_mutex);
- ret = drm_of_find_panel_or_bridge(dev_of_node(dev), 1, 0, NULL, &hdmi->next_bridge);
- if (ret && ret != -ENODEV)
- return ret;
+ hdmi->next_bridge = of_drm_get_bridge_by_endpoint(dev_of_node(dev), 1, 0);
+ if (IS_ERR(hdmi->next_bridge)) {
+ if (PTR_ERR(hdmi->next_bridge) != -ENODEV)
+ return PTR_ERR(hdmi->next_bridge);
+
+ hdmi->next_bridge = NULL;
+ }
hdmi->mmio = msm_ioremap(pdev, "core_physical");
- if (IS_ERR(hdmi->mmio))
- return PTR_ERR(hdmi->mmio);
+ if (IS_ERR(hdmi->mmio)) {
+ ret = PTR_ERR(hdmi->mmio);
+ goto err_put_bridge;
+ }
/* HDCP needs physical address of hdmi register */
res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
"core_physical");
- if (!res)
- return -EINVAL;
+ if (!res) {
+ ret = -EINVAL;
+ goto err_put_bridge;
+ }
hdmi->mmio_phy_addr = res->start;
hdmi->qfprom_mmio = msm_ioremap(pdev, "qfprom_physical");
@@ -307,45 +315,58 @@ static int msm_hdmi_dev_probe(struct platform_device *pdev)
}
hdmi->irq = platform_get_irq(pdev, 0);
- if (hdmi->irq < 0)
- return hdmi->irq;
+ if (hdmi->irq < 0) {
+ ret = hdmi->irq;
+ goto err_put_bridge;
+ }
hdmi->pwr_regs = devm_kcalloc(dev, config->pwr_reg_cnt,
sizeof(hdmi->pwr_regs[0]),
GFP_KERNEL);
- if (!hdmi->pwr_regs)
- return -ENOMEM;
+ if (!hdmi->pwr_regs) {
+ ret = -ENOMEM;
+ goto err_put_bridge;
+ }
for (i = 0; i < config->pwr_reg_cnt; i++)
hdmi->pwr_regs[i].supply = config->pwr_reg_names[i];
ret = devm_regulator_bulk_get(dev, config->pwr_reg_cnt, hdmi->pwr_regs);
- if (ret)
- return dev_err_probe(dev, ret, "failed to get pwr regulators\n");
+ if (ret) {
+ dev_err_probe(dev, ret, "failed to get pwr regulators\n");
+ goto err_put_bridge;
+ }
hdmi->pwr_clks = devm_kcalloc(dev, config->pwr_clk_cnt,
sizeof(hdmi->pwr_clks[0]),
GFP_KERNEL);
- if (!hdmi->pwr_clks)
- return -ENOMEM;
+ if (!hdmi->pwr_clks) {
+ ret = -ENOMEM;
+ goto err_put_bridge;
+ }
for (i = 0; i < config->pwr_clk_cnt; i++)
hdmi->pwr_clks[i].id = config->pwr_clk_names[i];
ret = devm_clk_bulk_get(dev, config->pwr_clk_cnt, hdmi->pwr_clks);
if (ret)
- return ret;
+ goto err_put_bridge;
+
hdmi->extp_clk = devm_clk_get_optional(dev, "extp");
- if (IS_ERR(hdmi->extp_clk))
- return dev_err_probe(dev, PTR_ERR(hdmi->extp_clk),
- "failed to get extp clock\n");
+ if (IS_ERR(hdmi->extp_clk)) {
+ ret = dev_err_probe(dev, PTR_ERR(hdmi->extp_clk),
+ "failed to get extp clock\n");
+ goto err_put_bridge;
+ }
hdmi->hpd_gpiod = devm_gpiod_get_optional(dev, "hpd", GPIOD_IN);
/* This will catch e.g. -EPROBE_DEFER */
- if (IS_ERR(hdmi->hpd_gpiod))
- return dev_err_probe(dev, PTR_ERR(hdmi->hpd_gpiod),
- "failed to get hpd gpio\n");
+ if (IS_ERR(hdmi->hpd_gpiod)) {
+ ret = dev_err_probe(dev, PTR_ERR(hdmi->hpd_gpiod),
+ "failed to get hpd gpio\n");
+ goto err_put_bridge;
+ }
if (!hdmi->hpd_gpiod)
DBG("failed to get HPD gpio");
@@ -355,7 +376,7 @@ static int msm_hdmi_dev_probe(struct platform_device *pdev)
ret = msm_hdmi_get_phy(hdmi);
if (ret)
- return ret;
+ goto err_put_bridge;
ret = devm_pm_runtime_enable(dev);
if (ret)
@@ -371,6 +392,8 @@ static int msm_hdmi_dev_probe(struct platform_device *pdev)
err_put_phy:
msm_hdmi_put_phy(hdmi);
+err_put_bridge:
+ drm_bridge_put(hdmi->next_bridge);
return ret;
}
@@ -381,6 +404,7 @@ static void msm_hdmi_dev_remove(struct platform_device *pdev)
component_del(&pdev->dev, &msm_hdmi_ops);
msm_hdmi_put_phy(hdmi);
+ drm_bridge_put(hdmi->next_bridge);
}
static int msm_hdmi_runtime_suspend(struct device *dev)
diff --git a/drivers/gpu/drm/msm/hdmi/hdmi_phy_8996.c b/drivers/gpu/drm/msm/hdmi/hdmi_phy_8996.c
index 36e928b0fd5a..71da20322b3d 100644
--- a/drivers/gpu/drm/msm/hdmi/hdmi_phy_8996.c
+++ b/drivers/gpu/drm/msm/hdmi/hdmi_phy_8996.c
@@ -11,9 +11,6 @@
#define HDMI_VCO_MAX_FREQ 12000000000UL
#define HDMI_VCO_MIN_FREQ 8000000000UL
-#define HDMI_PCLK_MAX_FREQ 600000000
-#define HDMI_PCLK_MIN_FREQ 25000000
-
#define HDMI_HIGH_FREQ_BIT_CLK_THRESHOLD 3400000000UL
#define HDMI_DIG_FREQ_BIT_CLK_THRESHOLD 1500000000UL
#define HDMI_MID_FREQ_BIT_CLK_THRESHOLD 750000000UL
@@ -632,7 +629,8 @@ static int hdmi_8996_pll_prepare(struct clk_hw *hw)
static int hdmi_8996_pll_determine_rate(struct clk_hw *hw,
struct clk_rate_request *req)
{
- req->rate = clamp_t(unsigned long, req->rate, HDMI_PCLK_MIN_FREQ, HDMI_PCLK_MAX_FREQ);
+ req->rate = clamp_t(unsigned long, req->rate, HDMI_TMDS_CHAR_RATE_MIN_HZ,
+ HDMI_2_0_TMDS_CHAR_RATE_MAX_HZ);
return 0;
}
diff --git a/drivers/gpu/drm/msm/hdmi/hdmi_phy_8998.c b/drivers/gpu/drm/msm/hdmi/hdmi_phy_8998.c
index a86ff3706369..05c3ffad858d 100644
--- a/drivers/gpu/drm/msm/hdmi/hdmi_phy_8998.c
+++ b/drivers/gpu/drm/msm/hdmi/hdmi_phy_8998.c
@@ -12,9 +12,6 @@
#define HDMI_VCO_MAX_FREQ 12000000000UL
#define HDMI_VCO_MIN_FREQ 8000000000UL
-#define HDMI_PCLK_MAX_FREQ 600000000
-#define HDMI_PCLK_MIN_FREQ 25000000
-
#define HDMI_HIGH_FREQ_BIT_CLK_THRESHOLD 3400000000UL
#define HDMI_DIG_FREQ_BIT_CLK_THRESHOLD 1500000000UL
#define HDMI_MID_FREQ_BIT_CLK_THRESHOLD 750000000UL
@@ -649,7 +646,8 @@ static int hdmi_8998_pll_prepare(struct clk_hw *hw)
static int hdmi_8998_pll_determine_rate(struct clk_hw *hw,
struct clk_rate_request *req)
{
- req->rate = clamp_t(unsigned long, req->rate, HDMI_PCLK_MIN_FREQ, HDMI_PCLK_MAX_FREQ);
+ req->rate = clamp_t(unsigned long, req->rate, HDMI_TMDS_CHAR_RATE_MIN_HZ,
+ HDMI_2_0_TMDS_CHAR_RATE_MAX_HZ);
return 0;
}
diff --git a/drivers/gpu/drm/panel/panel-edp.c b/drivers/gpu/drm/panel/panel-edp.c
index 241915e9f495..cb7d6c43466e 100644
--- a/drivers/gpu/drm/panel/panel-edp.c
+++ b/drivers/gpu/drm/panel/panel-edp.c
@@ -2030,6 +2030,7 @@ static const struct edp_panel_entry edp_panels[] = {
EDP_PANEL_ENTRY('B', 'O', 'E', 0x0c6f, &delay_200_500_e50, "NV140FHM-N40"),
EDP_PANEL_ENTRY('B', 'O', 'E', 0x0c93, &delay_200_500_e200, "Unknown"),
EDP_PANEL_ENTRY('B', 'O', 'E', 0x0cb6, &delay_200_500_e200, "NT116WHM-N44"),
+ EDP_PANEL_ENTRY('B', 'O', 'E', 0x0cc9, &delay_200_500_e50, "NE120DRM-N28"),
EDP_PANEL_ENTRY('B', 'O', 'E', 0x0cf2, &delay_200_500_e200, "NV156FHM-N4S"),
EDP_PANEL_ENTRY('B', 'O', 'E', 0x0cf6, &delay_200_500_e200_d100, "NV140WUM-N64"),
EDP_PANEL_ENTRY('B', 'O', 'E', 0x0cfa, &delay_200_500_e50, "NV116WHM-A4D"),
diff --git a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
index 0dc1eb5d2ae3..7abb42e486c0 100644
--- a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
+++ b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
@@ -537,21 +537,22 @@ static int dw_hdmi_rockchip_bind(struct device *dev, struct device *master,
void *data)
{
struct platform_device *pdev = to_platform_device(dev);
+ struct device_node *np = dev_of_node(dev);
struct dw_hdmi_plat_data *plat_data;
const struct of_device_id *match;
struct drm_device *drm = data;
struct drm_encoder *encoder;
struct rockchip_hdmi *hdmi;
- int ret;
+ int ret, index;
- if (!pdev->dev.of_node)
+ if (!np)
return -ENODEV;
hdmi = devm_kzalloc(&pdev->dev, sizeof(*hdmi), GFP_KERNEL);
if (!hdmi)
return -ENOMEM;
- match = of_match_node(dw_hdmi_rockchip_dt_ids, pdev->dev.of_node);
+ match = of_match_node(dw_hdmi_rockchip_dt_ids, np);
plat_data = devm_kmemdup(&pdev->dev, match->data,
sizeof(*plat_data), GFP_KERNEL);
if (!plat_data)
@@ -564,9 +565,9 @@ static int dw_hdmi_rockchip_bind(struct device *dev, struct device *master,
plat_data->priv_data = hdmi;
encoder = &hdmi->encoder.encoder;
- encoder->possible_crtcs = drm_of_find_possible_crtcs(drm, dev->of_node);
+ encoder->possible_crtcs = drm_of_find_possible_crtcs(drm, np);
rockchip_drm_encoder_set_crtc_endpoint_id(&hdmi->encoder,
- dev->of_node, 0, 0);
+ np, 0, 0);
/*
* If we failed to find the CRTC(s) which this encoder is
@@ -588,13 +589,17 @@ static int dw_hdmi_rockchip_bind(struct device *dev, struct device *master,
return dev_err_probe(hdmi->dev, ret, "failed to get phy\n");
}
- if (hdmi->phy) {
+ index = of_property_match_string(np, "phy-names", "hdmi");
+ if (index >= 0) {
struct of_phandle_args clkspec;
- clkspec.np = hdmi->phy->dev.of_node;
- hdmi->hdmiphy_clk = of_clk_get_from_provider(&clkspec);
- if (IS_ERR(hdmi->hdmiphy_clk))
- hdmi->hdmiphy_clk = NULL;
+ if (!of_parse_phandle_with_args(np, "phys", "#phy-cells", index,
+ &clkspec)) {
+ hdmi->hdmiphy_clk = of_clk_get_from_provider(&clkspec);
+ of_node_put(clkspec.np);
+ if (IS_ERR(hdmi->hdmiphy_clk))
+ hdmi->hdmiphy_clk = NULL;
+ }
}
if (hdmi->chip_data == &rk3568_chip_data) {
diff --git a/drivers/gpu/drm/sti/sti_hdmi_tx3g4c28phy.c b/drivers/gpu/drm/sti/sti_hdmi_tx3g4c28phy.c
index d25ecd4f4b67..4fe0c54a096f 100644
--- a/drivers/gpu/drm/sti/sti_hdmi_tx3g4c28phy.c
+++ b/drivers/gpu/drm/sti/sti_hdmi_tx3g4c28phy.c
@@ -4,6 +4,8 @@
* Author: Vincent Abriou <vincent.abriou@st.com> for STMicroelectronics.
*/
+#include <linux/hdmi.h>
+
#include <drm/drm_print.h>
#include "sti_hdmi_tx3g4c28phy.h"
@@ -102,7 +104,7 @@ static bool sti_hdmi_tx3g4c28phy_start(struct sti_hdmi *hdmi)
tmdsck = ckpxpll;
pllctrl |= 40 << PLL_CFG_NDIV_SHIFT;
- if (tmdsck > 340000000) {
+ if (tmdsck > HDMI_1_3_TMDS_CHAR_RATE_MAX_HZ) {
DRM_ERROR("output TMDS clock (%d) out of range\n", tmdsck);
goto err;
}
@@ -135,7 +137,7 @@ static bool sti_hdmi_tx3g4c28phy_start(struct sti_hdmi *hdmi)
HDMI_SRZ_CFG_EN_BIASRES_DETECTION |
HDMI_SRZ_CFG_EN_SINK_TERM_DETECTION);
- if (tmdsck > 165000000)
+ if (tmdsck > HDMI_1_0_TMDS_CHAR_RATE_MAX_HZ)
val |= HDMI_SRZ_CFG_EN_SRC_TERMINATION;
/*
diff --git a/drivers/gpu/drm/sun4i/sun4i_hdmi_enc.c b/drivers/gpu/drm/sun4i/sun4i_hdmi_enc.c
index 07e2afcb4f95..74c7c3720ba8 100644
--- a/drivers/gpu/drm/sun4i/sun4i_hdmi_enc.c
+++ b/drivers/gpu/drm/sun4i/sun4i_hdmi_enc.c
@@ -189,8 +189,8 @@ sun4i_hdmi_connector_clock_valid(const struct drm_connector *connector,
if (mode->flags & DRM_MODE_FLAG_DBLCLK)
return MODE_BAD;
- /* 165 MHz is the typical max pixelclock frequency for HDMI <= 1.2 */
- if (clock > 165000000)
+ /* HDMI 1.0 max TMDS character rate */
+ if (clock > HDMI_1_0_TMDS_CHAR_RATE_MAX_HZ)
return MODE_CLOCK_HIGH;
rounded_rate = clk_round_rate(hdmi->tmds_clk, clock);
diff --git a/drivers/gpu/drm/virtio/virtgpu_drv.c b/drivers/gpu/drm/virtio/virtgpu_drv.c
index a5ce96fb8a1d..812ee3f5e4aa 100644
--- a/drivers/gpu/drm/virtio/virtgpu_drv.c
+++ b/drivers/gpu/drm/virtio/virtgpu_drv.c
@@ -163,6 +163,7 @@ static unsigned int features[] = {
VIRTIO_GPU_F_RESOURCE_UUID,
VIRTIO_GPU_F_RESOURCE_BLOB,
VIRTIO_GPU_F_CONTEXT_INIT,
+ VIRTIO_GPU_F_BLOB_ALIGNMENT,
};
static struct virtio_driver virtio_gpu_driver = {
.feature_table = features,
diff --git a/drivers/gpu/drm/virtio/virtgpu_drv.h b/drivers/gpu/drm/virtio/virtgpu_drv.h
index 6f49213e23f8..7449907754a4 100644
--- a/drivers/gpu/drm/virtio/virtgpu_drv.h
+++ b/drivers/gpu/drm/virtio/virtgpu_drv.h
@@ -258,6 +258,7 @@ struct virtio_gpu_device {
bool has_resource_blob;
bool has_host_visible;
bool has_context_init;
+ bool has_blob_alignment;
struct virtio_shm_region host_visible_region;
struct drm_mm host_visible_mm;
@@ -271,6 +272,7 @@ struct virtio_gpu_device {
uint32_t num_capsets;
uint64_t capset_id_mask;
struct list_head cap_cache;
+ uint32_t blob_alignment;
/* protects uuid state when exporting */
spinlock_t resource_export_lock;
@@ -318,6 +320,7 @@ virtio_gpu_array_from_handles(struct drm_file *drm_file, u32 *handles, u32 nents
void virtio_gpu_array_add_obj(struct virtio_gpu_object_array *objs,
struct drm_gem_object *obj);
int virtio_gpu_array_lock_resv(struct virtio_gpu_object_array *objs);
+int virtio_gpu_lock_one_resv_uninterruptible(struct virtio_gpu_object_array *objs);
void virtio_gpu_array_unlock_resv(struct virtio_gpu_object_array *objs);
void virtio_gpu_array_add_fence(struct virtio_gpu_object_array *objs,
struct dma_fence *fence);
diff --git a/drivers/gpu/drm/virtio/virtgpu_gem.c b/drivers/gpu/drm/virtio/virtgpu_gem.c
index f22dc5c21cd4..435d37d36034 100644
--- a/drivers/gpu/drm/virtio/virtgpu_gem.c
+++ b/drivers/gpu/drm/virtio/virtgpu_gem.c
@@ -238,6 +238,23 @@ int virtio_gpu_array_lock_resv(struct virtio_gpu_object_array *objs)
return ret;
}
+int virtio_gpu_lock_one_resv_uninterruptible(struct virtio_gpu_object_array *objs)
+{
+ int ret;
+
+ if (objs->nents != 1)
+ return -EINVAL;
+
+ dma_resv_lock(objs->objs[0]->resv, NULL);
+
+ ret = dma_resv_reserve_fences(objs->objs[0]->resv, 1);
+ if (ret) {
+ virtio_gpu_array_unlock_resv(objs);
+ return ret;
+ }
+ return 0;
+}
+
void virtio_gpu_array_unlock_resv(struct virtio_gpu_object_array *objs)
{
if (objs->nents == 1) {
diff --git a/drivers/gpu/drm/virtio/virtgpu_ioctl.c b/drivers/gpu/drm/virtio/virtgpu_ioctl.c
index 01daa72b1310..3d8e4ccdb7c1 100644
--- a/drivers/gpu/drm/virtio/virtgpu_ioctl.c
+++ b/drivers/gpu/drm/virtio/virtgpu_ioctl.c
@@ -117,6 +117,11 @@ static int virtio_gpu_getparam_ioctl(struct drm_device *dev, void *data,
case VIRTGPU_PARAM_EXPLICIT_DEBUG_NAME:
value = vgdev->has_context_init ? 1 : 0;
break;
+ case VIRTGPU_PARAM_BLOB_ALIGNMENT:
+ if (!vgdev->has_blob_alignment)
+ return -ENOENT;
+ value = vgdev->blob_alignment;
+ break;
default:
return -EINVAL;
}
@@ -490,6 +495,11 @@ static int verify_blob(struct virtio_gpu_device *vgdev,
params->blob = true;
params->blob_flags = rc_blob->blob_flags;
params->blob_hints = rc_blob->blob_hints;
+
+ if (vgdev->has_blob_alignment &&
+ !IS_ALIGNED(params->size, vgdev->blob_alignment))
+ return -EINVAL;
+
return 0;
}
diff --git a/drivers/gpu/drm/virtio/virtgpu_kms.c b/drivers/gpu/drm/virtio/virtgpu_kms.c
index 80ba69b4860b..cfde9f573df6 100644
--- a/drivers/gpu/drm/virtio/virtgpu_kms.c
+++ b/drivers/gpu/drm/virtio/virtgpu_kms.c
@@ -124,7 +124,7 @@ int virtio_gpu_init(struct virtio_device *vdev, struct drm_device *dev)
struct virtio_gpu_device *vgdev;
/* this will expand later */
struct virtqueue *vqs[2];
- u32 num_scanouts, num_capsets;
+ u32 num_scanouts, num_capsets, blob_alignment;
int ret = 0;
if (!virtio_has_feature(vdev, VIRTIO_F_VERSION_1))
@@ -198,14 +198,22 @@ int virtio_gpu_init(struct virtio_device *vdev, struct drm_device *dev)
if (virtio_has_feature(vgdev->vdev, VIRTIO_GPU_F_CONTEXT_INIT))
vgdev->has_context_init = true;
+ if (virtio_has_feature(vgdev->vdev, VIRTIO_GPU_F_BLOB_ALIGNMENT)) {
+ vgdev->has_blob_alignment = true;
+ virtio_cread_le(vgdev->vdev, struct virtio_gpu_config,
+ blob_alignment, &blob_alignment);
+ vgdev->blob_alignment = blob_alignment;
+ }
+
DRM_INFO("features: %cvirgl %cedid %cresource_blob %chost_visible",
vgdev->has_virgl_3d ? '+' : '-',
vgdev->has_edid ? '+' : '-',
vgdev->has_resource_blob ? '+' : '-',
vgdev->has_host_visible ? '+' : '-');
- DRM_INFO("features: %ccontext_init\n",
- vgdev->has_context_init ? '+' : '-');
+ DRM_INFO("features: %ccontext_init %cblob_alignment\n",
+ vgdev->has_context_init ? '+' : '-',
+ vgdev->has_blob_alignment ? '+' : '-');
ret = virtio_find_vqs(vgdev->vdev, 2, vqs, vqs_info, NULL);
if (ret) {
diff --git a/drivers/gpu/drm/virtio/virtgpu_plane.c b/drivers/gpu/drm/virtio/virtgpu_plane.c
index 73aa70bd4231..1d1b27ece62a 100644
--- a/drivers/gpu/drm/virtio/virtgpu_plane.c
+++ b/drivers/gpu/drm/virtio/virtgpu_plane.c
@@ -215,7 +215,10 @@ static void virtio_gpu_resource_flush(struct drm_plane *plane,
if (!objs)
return;
virtio_gpu_array_add_obj(objs, vgfb->base.obj[0]);
- virtio_gpu_array_lock_resv(objs);
+ if (virtio_gpu_lock_one_resv_uninterruptible(objs)) {
+ virtio_gpu_array_put_free(objs);
+ return;
+ }
virtio_gpu_cmd_resource_flush(vgdev, bo->hw_res_handle, x, y,
width, height, objs,
vgplane_st->fence);
@@ -459,7 +462,10 @@ static void virtio_gpu_cursor_plane_update(struct drm_plane *plane,
if (!objs)
return;
virtio_gpu_array_add_obj(objs, vgfb->base.obj[0]);
- virtio_gpu_array_lock_resv(objs);
+ if (virtio_gpu_lock_one_resv_uninterruptible(objs)) {
+ virtio_gpu_array_put_free(objs);
+ return;
+ }
virtio_gpu_cmd_transfer_to_host_2d
(vgdev, 0,
plane->state->crtc_w,
diff --git a/drivers/gpu/drm/xlnx/zynqmp_dp.c b/drivers/gpu/drm/xlnx/zynqmp_dp.c
index 05bd31fe675b..7fb11b0a44f0 100644
--- a/drivers/gpu/drm/xlnx/zynqmp_dp.c
+++ b/drivers/gpu/drm/xlnx/zynqmp_dp.c
@@ -353,7 +353,6 @@ struct zynqmp_dp_train_set_priv {
* @lock: Mutex protecting this struct and register access (but not AUX)
* @irq: irq
* @bridge: DRM bridge for the DP encoder
- * @next_bridge: The downstream bridge
* @test: Configuration for test mode
* @config: IP core configuration from DTS
* @aux: aux channel
@@ -385,7 +384,6 @@ struct zynqmp_dp {
struct completion aux_done;
struct mutex lock;
- struct drm_bridge *next_bridge;
struct device *dev;
struct zynqmp_dpsub *dpsub;
void __iomem *iomem;
@@ -1494,8 +1492,8 @@ static int zynqmp_dp_bridge_attach(struct drm_bridge *bridge,
return ret;
}
- if (dp->next_bridge) {
- ret = drm_bridge_attach(encoder, dp->next_bridge,
+ if (dp->bridge.next_bridge) {
+ ret = drm_bridge_attach(encoder, dp->bridge.next_bridge,
bridge, flags);
if (ret < 0)
goto error;
@@ -2461,10 +2459,15 @@ int zynqmp_dp_probe(struct zynqmp_dpsub *dpsub)
* Acquire the next bridge in the chain. Ignore errors caused by port@5
* not being connected for backward-compatibility with older DTs.
*/
- ret = drm_of_find_panel_or_bridge(dp->dev->of_node, 5, 0, NULL,
- &dp->next_bridge);
- if (ret < 0 && ret != -ENODEV)
- goto err_reset;
+ dp->bridge.next_bridge = of_drm_get_bridge_by_endpoint(dp->dev->of_node, 5, 0);
+ if (IS_ERR(dp->bridge.next_bridge)) {
+ if (PTR_ERR(dp->bridge.next_bridge) != -ENODEV) {
+ ret = PTR_ERR(dp->bridge.next_bridge);
+ goto err_reset;
+ }
+
+ dp->bridge.next_bridge = NULL;
+ }
/* Initialize the hardware. */
dp->config.misc0 &= ~ZYNQMP_DP_MAIN_STREAM_MISC0_SYNC_LOCK;
diff --git a/include/drm/drm_bridge.h b/include/drm/drm_bridge.h
index fdac9a526f38..4ba3a5deef9a 100644
--- a/include/drm/drm_bridge.h
+++ b/include/drm/drm_bridge.h
@@ -1327,6 +1327,8 @@ int drm_bridge_attach(struct drm_encoder *encoder, struct drm_bridge *bridge,
#ifdef CONFIG_OF
struct drm_bridge *of_drm_find_and_get_bridge(struct device_node *np);
struct drm_bridge *of_drm_find_bridge(struct device_node *np);
+struct drm_bridge *of_drm_get_bridge_by_endpoint(const struct device_node *np,
+ int port, int endpoint);
#else
static inline struct drm_bridge *of_drm_find_and_get_bridge(struct device_node *np)
{
@@ -1336,6 +1338,11 @@ static inline struct drm_bridge *of_drm_find_bridge(struct device_node *np)
{
return NULL;
}
+static inline struct drm_bridge *of_drm_get_bridge_by_endpoint(const struct device_node *np,
+ int port, int endpoint)
+{
+ return ERR_PTR(-ENODEV);
+}
#endif
static inline bool drm_bridge_is_last(struct drm_bridge *bridge)
diff --git a/include/linux/hdmi.h b/include/linux/hdmi.h
index 96bda41d9148..8dab78e1f61b 100644
--- a/include/linux/hdmi.h
+++ b/include/linux/hdmi.h
@@ -50,6 +50,12 @@ enum hdmi_infoframe_type {
HDMI_INFOFRAME_TYPE_DRM = 0x87,
};
+/* HDMI spec maximum TMDS character rates, in Hz */
+#define HDMI_TMDS_CHAR_RATE_MIN_HZ 25000000
+#define HDMI_1_0_TMDS_CHAR_RATE_MAX_HZ 165000000
+#define HDMI_1_3_TMDS_CHAR_RATE_MAX_HZ 340000000
+#define HDMI_2_0_TMDS_CHAR_RATE_MAX_HZ 600000000
+
#define HDMI_IEEE_OUI 0x000c03
#define HDMI_FORUM_IEEE_OUI 0xc45dd8
#define HDMI_INFOFRAME_HEADER_SIZE 4
diff --git a/include/uapi/drm/virtgpu_drm.h b/include/uapi/drm/virtgpu_drm.h
index ba09a4ee3e77..95587e12aed5 100644
--- a/include/uapi/drm/virtgpu_drm.h
+++ b/include/uapi/drm/virtgpu_drm.h
@@ -98,6 +98,7 @@ struct drm_virtgpu_execbuffer {
#define VIRTGPU_PARAM_CONTEXT_INIT 6 /* DRM_VIRTGPU_CONTEXT_INIT */
#define VIRTGPU_PARAM_SUPPORTED_CAPSET_IDs 7 /* Bitmask of supported capability set ids */
#define VIRTGPU_PARAM_EXPLICIT_DEBUG_NAME 8 /* Ability to set debug name from userspace */
+#define VIRTGPU_PARAM_BLOB_ALIGNMENT 9 /* Device alignment requirements for blobs */
struct drm_virtgpu_getparam {
__u64 param;
diff --git a/include/uapi/linux/virtio_gpu.h b/include/uapi/linux/virtio_gpu.h
index be109777d10d..4f530d90058c 100644
--- a/include/uapi/linux/virtio_gpu.h
+++ b/include/uapi/linux/virtio_gpu.h
@@ -64,6 +64,14 @@
* context_init and multiple timelines
*/
#define VIRTIO_GPU_F_CONTEXT_INIT 4
+/*
+ * The device provides a valid blob_alignment
+ * field in its configuration and both
+ * VIRTIO_GPU_CMD_RESOURCE_CREATE_BLOB and
+ * VIRTIO_GPU_CMD_RESOURCE_MAP_BLOB requests
+ * must be aligned to that value.
+ */
+#define VIRTIO_GPU_F_BLOB_ALIGNMENT 5
enum virtio_gpu_ctrl_type {
VIRTIO_GPU_UNDEFINED = 0,
@@ -365,6 +373,7 @@ struct virtio_gpu_config {
__le32 events_clear;
__le32 num_scanouts;
__le32 num_capsets;
+ __le32 blob_alignment;
};
/* simple formats for fbcon/X use */