mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-07-22 01:08:13 -04:00
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:
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
|
||||
@@ -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_ */
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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[] = {
|
||||
|
||||
@@ -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)) {
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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,
|
||||
<8713sx->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)
|
||||
|
||||
@@ -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, <9611->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)
|
||||
|
||||
@@ -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, <9611uxc->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)
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
/**
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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.
|
||||
*/
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 =
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
113
drivers/gpu/drm/imagination/pvr_trace.h
Normal file
113
drivers/gpu/drm/imagination/pvr_trace.h
Normal 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>
|
||||
7
drivers/gpu/drm/imagination/pvr_trace_points.c
Normal file
7
drivers/gpu/drm/imagination/pvr_trace_points.c
Normal 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
|
||||
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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"),
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
|
||||
/*
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 */
|
||||
|
||||
Reference in New Issue
Block a user