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
This commit is contained in:
Dave Airlie
2026-05-26 06:51:30 +10:00
62 changed files with 976 additions and 373 deletions

View File

@@ -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;
}

View File

@@ -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;

View File

@@ -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,

View File

@@ -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,
};

View File

@@ -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_ */

View File

@@ -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 @@ int amdxdna_drm_create_hwctx_ioctl(struct drm_device *dev, void *data, struct dr
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 @@ int amdxdna_hwctx_sync_debug_bo(struct amdxdna_client *client, u32 debug_bo_hdl)
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)
{

View File

@@ -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,

View File

@@ -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);
}
@@ -161,31 +232,13 @@ 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;
}
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);
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;
}
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 @@ int amdxdna_drm_create_bo_ioctl(struct drm_device *dev, void *data, struct drm_f
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;
if (is_import_bo(abo))
return;
mutex_lock(&abo->lock);
drm_gem_shmem_unpin(&abo->base);
if (abo->type == AMDXDNA_BO_DEV) {
struct amdxdna_gem_obj *heap;
unsigned long heap_id;
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_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,23 +1246,46 @@ 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;
}
ret = amdxdna_flush_bo(abo, args->offset, args->size);
amdxdna_gem_unpin(abo);
if (ret) {
drm_WARN(&xdna->ddev, 1, "Can not get flush memory");
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");
amdxdna_gem_unpin(abo);
XDNA_DBG(xdna, "Sync bo %d offset 0x%llx, size 0x%llx\n",
args->handle, args->offset, args->size);

View File

@@ -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)

View File

@@ -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);

View File

@@ -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;

View File

@@ -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,

View File

@@ -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,

View File

@@ -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,

View File

@@ -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,

View File

@@ -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,

View File

@@ -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;

View File

@@ -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);

View File

@@ -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[] = {

View File

@@ -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)) {

View File

@@ -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)

View File

@@ -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 @@ static const struct drm_edid *it66121_bridge_edid_read(struct drm_bridge *bridge
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 @@ static int it66121_audio_hw_params(struct device *dev, void *data,
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);

View File

@@ -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)

View File

@@ -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)

View File

@@ -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)

View File

@@ -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;
}

View File

@@ -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:

View File

@@ -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);

View File

@@ -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
/**

View File

@@ -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) {

View File

@@ -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.
*/

View File

@@ -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);

View File

@@ -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

View File

@@ -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,22 +333,11 @@ 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;
err = xa_alloc(&pvr_dev->ctx_ids, &ctx->ctx_id, ctx, xa_limit_32b, GFP_KERNEL);
if (err)
goto err_destroy_fw_obj;
goto err_destroy_queues;
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;
}
if (err)
goto err_destroy_fw_obj;
spin_lock(&pvr_dev->ctx_list_lock);
list_add_tail(&ctx->file_link, &pvr_file->contexts);
@@ -358,6 +351,15 @@ int pvr_context_create(struct pvr_file *pvr_file, struct drm_pvr_ioctl_create_co
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);

View File

@@ -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);

View File

@@ -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;

View File

@@ -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);

View File

@@ -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

View File

@@ -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;

View File

@@ -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 =

View File

@@ -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 {

View File

@@ -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);
}
/**

View File

@@ -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>

View File

@@ -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

View File

@@ -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)

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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"),

View File

@@ -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) {

View File

@@ -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;
/*

View File

@@ -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);

View File

@@ -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,

View File

@@ -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);

View File

@@ -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) {

View File

@@ -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;
}

View File

@@ -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) {

View File

@@ -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,

View File

@@ -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;

View File

@@ -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)

View File

@@ -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

View File

@@ -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;

View File

@@ -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 */