Merge tag 'amd-drm-next-7.3-2026-08-06' of https://gitlab.freedesktop.org/agd5f/linux into drm-next

amd-drm-next-7.3-2026-08-06:

amdgpu:
- JPEG queue reset fixes
- GC 12 fix
- GMC 12.1 fixes
- Lockdep false positive fix
- Userq fixes
- Bounds checking fixes
- Devcoredump fixes
- DCN 2.0.1 fix
- Aperture mapping fix
- DC avmute fix
- DC self refresh fix
- RAS updates
- SMU 15 updates
- SMU PPT updates
- SMU 14 fixes
- Initial DCN 6.0.0 support
- GC 6 soft reset rework
- Display bounds checking fixes
- FRL fixes
- VRR fixes
- More display KUnit tests
- Refactor DC stream validation
- DCN 3.5.1 fixes
- DCN 3.2 fixes
- DC CRC fixes
- MES 12.0 fixes

amdkfd:
- SVM fixes
- MES updates

radeon:
- Performance regression fix

Signed-off-by: Dave Airlie <airlied@redhat.com>

From: Alex Deucher <alexander.deucher@amd.com>
Link: https://patch.msgid.link/20260806213106.994528-1-alexander.deucher@amd.com
This commit is contained in:
Dave Airlie
2026-08-08 06:37:56 +10:00
399 changed files with 132044 additions and 3211 deletions

View File

@@ -42,6 +42,26 @@
#include "amdgpu_ras.h"
#include "amdgpu_hmm.h"
/*
* Maximum IB length (dwords) for rings whose emit_ib packet format
* documents a 20-bit size field.
*/
#define AMDGPU_GFX_SDMA_IB_PACKET_SIZE_MAX_DW 0xFFFFF
#define AMDGPU_MM_IB_PACKET_SIZE_MAX_DW 0x7FFFF0
static u32 amdgpu_cs_ib_packet_size_max_dw(enum amdgpu_ring_type type)
{
switch (type) {
case AMDGPU_RING_TYPE_GFX:
case AMDGPU_RING_TYPE_COMPUTE:
case AMDGPU_RING_TYPE_SDMA:
case AMDGPU_RING_TYPE_VPE:
return AMDGPU_GFX_SDMA_IB_PACKET_SIZE_MAX_DW;
default:
return AMDGPU_MM_IB_PACKET_SIZE_MAX_DW;
}
}
static int amdgpu_cs_parser_init(struct amdgpu_cs_parser *p,
struct amdgpu_device *adev,
struct drm_file *filp,
@@ -340,7 +360,6 @@ static int amdgpu_cs_p2_ib(struct amdgpu_cs_parser *p,
job = p->jobs[r];
ring = amdgpu_job_ring(job);
ib = &job->ibs[job->num_ibs++];
/* submissions to kernel queues are disabled */
if (ring->no_user_submission)
@@ -369,6 +388,12 @@ static int amdgpu_cs_p2_ib(struct amdgpu_cs_parser *p,
return -EINVAL;
}
if (chunk_ib->ib_bytes / 4 >
amdgpu_cs_ib_packet_size_max_dw(ring->funcs->type))
return -EINVAL;
ib = &job->ibs[job->num_ibs++];
if (chunk_ib->flags & AMDGPU_IB_FLAG_PREAMBLE)
job->preamble_status |= AMDGPU_PREAMBLE_IB_PRESENT;

View File

@@ -343,7 +343,7 @@ amdgpu_devcoredump_format(char *buffer, size_t count, struct amdgpu_coredump_inf
struct amdgpu_ip_block *ip_block;
struct amdgpu_ring *ring;
int ver, i, j;
u32 ring_idx, off;
u32 ring_idx;
bool sizing_pass;
sizing_pass = buffer == NULL;
@@ -443,7 +443,6 @@ amdgpu_devcoredump_format(char *buffer, size_t count, struct amdgpu_coredump_inf
for (i = 0; i < coredump->num_rings; i++) {
ring_idx = coredump->rings[i].ring_index;
ring = coredump->adev->rings[ring_idx];
off = coredump->rings[i].offset;
drm_printf(&p, "ring name: %s\n", ring->name);
drm_printf(&p, "Rptr: 0x%llx Wptr: 0x%llx RB mask: %x\n",
@@ -452,12 +451,18 @@ amdgpu_devcoredump_format(char *buffer, size_t count, struct amdgpu_coredump_inf
ring->buf_mask);
drm_printf(&p, "Ring size in dwords: %d\n",
ring->ring_size / 4);
if (!coredump->rings[i].ring_dw) {
drm_printf(&p, "Ring contents unavailable\n");
continue;
}
drm_printf(&p, "Ring contents\n");
drm_printf(&p, "Offset \t Value\n");
for (j = 0; j < ring->ring_size; j += 4)
drm_printf(&p, "0x%x \t 0x%x\n", j,
coredump->rings_dw[off + j / 4]);
coredump->rings[i].ring_dw[j / 4]);
}
}
@@ -498,10 +503,12 @@ amdgpu_devcoredump_read(char *buffer, loff_t offset, size_t count,
static void amdgpu_devcoredump_free(void *data)
{
struct amdgpu_coredump_info *coredump = data;
u32 i;
kvfree(coredump->formatted);
for (i = 0; i < coredump->num_rings; i++)
kvfree(coredump->rings[i].ring_dw);
kvfree(coredump->rings);
kvfree(coredump->rings_dw);
kvfree(data);
}
@@ -543,9 +550,9 @@ void amdgpu_coredump(struct amdgpu_device *adev, bool skip_vram_check,
struct amdgpu_coredump_info *coredump;
size_t size = sizeof(*coredump);
struct drm_sched_job *s_job;
u64 total_ring_size, ring_count;
u64 ring_count;
struct amdgpu_ring *ring;
int i, off, idx;
int i, idx;
/* No need to generate a new coredump if there's one in progress already. */
if (work_busy(&adev->coredump_work))
@@ -554,7 +561,7 @@ void amdgpu_coredump(struct amdgpu_device *adev, bool skip_vram_check,
if (job && job->pasid)
size += sizeof(struct amdgpu_coredump_ib_info) * job->num_ibs;
coredump = kzalloc(size, GFP_NOWAIT);
coredump = kvzalloc(size, GFP_NOWAIT);
if (!coredump)
return;
@@ -585,7 +592,6 @@ void amdgpu_coredump(struct amdgpu_device *adev, bool skip_vram_check,
/* Dump ring content if memory allocation succeeds. */
ring_count = 0;
total_ring_size = 0;
for (i = 0; i < adev->num_rings; i++) {
ring = adev->rings[i];
@@ -594,34 +600,34 @@ void amdgpu_coredump(struct amdgpu_device *adev, bool skip_vram_check,
coredump->ring != ring)
continue;
total_ring_size += ring->ring_size;
ring_count++;
}
coredump->rings_dw = kzalloc(total_ring_size, GFP_NOWAIT);
coredump->rings = kcalloc(ring_count, sizeof(struct amdgpu_coredump_ring), GFP_NOWAIT);
if (coredump->rings && coredump->rings_dw) {
for (i = 0, off = 0, idx = 0; i < adev->num_rings && idx < ring_count; i++) {
if (ring_count)
coredump->rings = kvcalloc(ring_count,
sizeof(struct amdgpu_coredump_ring),
GFP_NOWAIT);
if (coredump->rings) {
for (i = 0, idx = 0; i < adev->num_rings && idx < ring_count; i++) {
struct amdgpu_coredump_ring *cdump_ring;
ring = adev->rings[i];
if (atomic_read(&ring->fence_drv.last_seq) == ring->fence_drv.sync_seq &&
coredump->ring != ring)
continue;
coredump->rings[idx].ring_index = ring->idx;
coredump->rings[idx].rptr = amdgpu_ring_get_rptr(ring);
coredump->rings[idx].wptr = amdgpu_ring_get_wptr(ring);
coredump->rings[idx].offset = off;
cdump_ring = &coredump->rings[idx];
memcpy(&coredump->rings_dw[off], ring->ring, ring->ring_size);
off += ring->ring_size / 4;
cdump_ring->ring_dw = kvzalloc(ring->ring_size, GFP_NOWAIT);
if (cdump_ring->ring_dw)
memcpy(cdump_ring->ring_dw, ring->ring, ring->ring_size);
cdump_ring->ring_index = ring->idx;
cdump_ring->rptr = amdgpu_ring_get_rptr(ring);
cdump_ring->wptr = amdgpu_ring_get_wptr(ring);
idx++;
}
coredump->num_rings = idx;
} else {
kvfree(coredump->rings_dw);
kvfree(coredump->rings);
coredump->rings_dw = NULL;
coredump->rings = NULL;
}
coredump->adev = adev;

View File

@@ -34,8 +34,8 @@
struct amdgpu_coredump_ring {
u64 rptr;
u64 wptr;
u32 *ring_dw;
u32 ring_index;
u32 offset;
};
struct amdgpu_coredump_ib_info {
@@ -53,7 +53,6 @@ struct amdgpu_coredump_info {
struct amdgpu_ring *ring;
struct amdgpu_coredump_ring *rings;
u32 *rings_dw;
u32 num_rings;
/* Readable form of coredevdump, generate once to speed up

View File

@@ -4185,6 +4185,8 @@ static void amdgpu_device_unmap_mmio(struct amdgpu_device *adev)
iounmap(adev->rmmio);
adev->rmmio = NULL;
if (adev->mman.aper_base_kaddr)
iounmap(adev->mman.aper_base_kaddr);
adev->mman.aper_base_kaddr = NULL;
/* Memory manager related */

View File

@@ -334,6 +334,19 @@ static int amdgpu_discovery_get_tmr_info(struct amdgpu_device *adev,
goto out;
}
} else {
if (adev->discovery.offset) {
u32 signature;
/* If VRAM holds a valid discovery signature at the default
* discovery offset, use it as-is.
*/
amdgpu_device_vram_access(adev, adev->discovery.offset,
&signature, sizeof(signature),
false);
if (le32_to_cpu(signature) == BINARY_SIGNATURE)
goto out;
}
tmr_size = RREG32(mmDRIVER_SCRATCH_2);
if (tmr_size) {
/* It's preferred to transition to PSP mailbox reg interface
@@ -2658,6 +2671,7 @@ static int amdgpu_discovery_set_display_ip_blocks(struct amdgpu_device *adev)
case IP_VERSION(4, 1, 0):
case IP_VERSION(4, 2, 0):
case IP_VERSION(4, 2, 1):
case IP_VERSION(6, 0, 0):
/* TODO: Fix IP version. DC code expects version 4.0.1 */
if (adev->ip_versions[DCE_HWIP][0] == IP_VERSION(4, 1, 0))
adev->ip_versions[DCE_HWIP][0] = IP_VERSION(4, 0, 1);

View File

@@ -866,7 +866,8 @@ int amdgpu_gfx_enable_kgq(struct amdgpu_device *adev, int xcc_id)
* @queue_id: queue ID of the faulty ring
*
* This function handles privileged instruction faults by identifying
* the faulty ring (gfx or compute) and triggering a scheduler fault
* the faulty ring (gfx or compute) and triggering a scheduler fault, or by
* recovering the faulting user queue.
*/
void amdgpu_gfx_handle_priv_fault(struct amdgpu_device *adev,
struct amdgpu_iv_entry *entry,
@@ -901,12 +902,25 @@ void amdgpu_gfx_handle_priv_fault(struct amdgpu_device *adev,
}
}
/* No KQ matched: the faulting slot belongs to a user queue. */
if (adev->gfx.disable_uq)
return;
doorbell_offset = entry->src_data[0] & AMDGPU_CTXID0_DOORBELL_ID_MASK;
/* No KQ matched: HW slot is a MES-scheduled user queue. */
if (adev->enable_mes && doorbell_offset)
/*
* A compute user-queue fault IV carries the doorbell offset, so reset
* the queue directly from it. A gfx user-queue fault is raised by the
* ME and carries only the HW slot (no doorbell); record the slot and
* let the worker read the doorbell back from the HQD.
*/
if (doorbell_offset) {
amdgpu_userq_process_reset_irq(adev, entry->pasid,
doorbell_offset);
} else {
set_bit(pipe_id | (queue_id << 2), &adev->gfx.userq_priv_fault_slots);
schedule_work(&adev->gfx.userq_priv_fault_work);
}
}
static void amdgpu_gfx_do_off_ctrl(struct amdgpu_device *adev, bool enable,

View File

@@ -535,6 +535,9 @@ struct amdgpu_gfx {
struct mutex userq_sch_mutex;
u64 userq_sch_req_count[MAX_XCP];
bool userq_sch_inactive[MAX_XCP];
/* atomic bitmap of faulted gfx UQ slots (index = pipe | queue << 2) */
unsigned long userq_priv_fault_slots;
struct work_struct userq_priv_fault_work;
unsigned long enforce_isolation_jiffies[MAX_XCP];
unsigned long enforce_isolation_time[MAX_XCP];

View File

@@ -628,16 +628,6 @@ int amdgpu_gmc_ras_sw_init(struct amdgpu_device *adev)
return r;
return 0;
}
int amdgpu_gmc_ras_late_init(struct amdgpu_device *adev)
{
return 0;
}
void amdgpu_gmc_ras_fini(struct amdgpu_device *adev)
{
}
/*

View File

@@ -438,8 +438,6 @@ int amdgpu_gmc_handle_retry_fault(struct amdgpu_device *adev,
u32 node_id,
bool write_fault);
int amdgpu_gmc_ras_sw_init(struct amdgpu_device *adev);
int amdgpu_gmc_ras_late_init(struct amdgpu_device *adev);
void amdgpu_gmc_ras_fini(struct amdgpu_device *adev);
int amdgpu_gmc_allocate_vm_inv_eng(struct amdgpu_device *adev);
void amdgpu_gmc_flush_gpu_tlb(struct amdgpu_device *adev, uint32_t vmid,
uint32_t vmhub, uint32_t flush_type);

View File

@@ -135,6 +135,21 @@ int amdgpu_lockdep_init(void)
lockdep_set_class(&locks->srbm_mutex, &amdgpu_srbm_lock_key);
lockdep_set_class(&locks->grbm_idx_mutex, &amdgpu_grbm_lock_key);
lockdep_set_class(&locks->mmio_idx_lock, &amdgpu_mmio_lock_key);
/*
* Register fs_reclaim lock class FIRST, before taking any locks.
*
* This acquire/release pair does NOT create a static lockdep edge
* (no locks are held between acquire and release). It only registers
* the fs_reclaim lock class with lockdep.
*
* The actual fs_reclaim -> notifier_lock dependency is established at
* RUNTIME when memory reclaim invokes MMU notifiers:
* fs_reclaim (held by reclaim) -> notifier_lock (acquired in callback)
*/
fs_reclaim_acquire(GFP_KERNEL);
fs_reclaim_release(GFP_KERNEL);
/*
* Take locks in the correct order to train lockdep.
* This establishes the dependency chain.
@@ -154,11 +169,6 @@ int amdgpu_lockdep_init(void)
/* Level 6: Reset control lock */
mutex_lock(&locks->reset_lock);
/*
* Mark potential memory reclaim boundary.
* GPU operations might trigger memory allocation/reclaim.
*/
fs_reclaim_acquire(GFP_KERNEL);
/* Level 7: SRBM register access */
mutex_lock(&locks->srbm_mutex);
@@ -176,7 +186,6 @@ int amdgpu_lockdep_init(void)
spin_unlock_irqrestore(&locks->mmio_idx_lock, flags);
mutex_unlock(&locks->grbm_idx_mutex);
mutex_unlock(&locks->srbm_mutex);
fs_reclaim_release(GFP_KERNEL);
mutex_unlock(&locks->reset_lock);
up_read(&reset_domain->sem);

View File

@@ -308,8 +308,12 @@ void amdgpu_mes_fini(struct amdgpu_device *adev)
amdgpu_mes_doorbell_free(adev);
if (adev->mes.use_rs64mem)
amdgpu_mes_rs64mem_fini(&adev->mes);
ida_destroy(&adev->mes.doorbell_ida);
mutex_destroy(&adev->mes.mutex_hidden);
}
int amdgpu_mes_suspend(struct amdgpu_device *adev, u32 xcc_id)
@@ -1040,13 +1044,13 @@ int amdgpu_mes_rs64mem_setup_bitmaps(struct amdgpu_mes *mes)
* amdgpu_mes_alloc_proc_ctx_index - allocate a process context slot
*
* @mes: MES instance
* @queue: Usermode queue receiving the allocated process context index
* @index: the allocated process context index
*
* Returns 0 on success, -ENOSPC if all slots are used, or
* -EOPNOTSUPP if RS64 local memory is unavailable.
*/
int amdgpu_mes_alloc_proc_ctx_index(struct amdgpu_mes *mes,
struct amdgpu_usermode_queue *queue)
uint32_t *index)
{
unsigned long bit;
@@ -1061,7 +1065,7 @@ int amdgpu_mes_alloc_proc_ctx_index(struct amdgpu_mes *mes,
return -ENOSPC;
}
set_bit(bit, mes->proc_ctx_bitmap);
queue->proc_ctx_array_index = (uint32_t)bit;
*index = (uint32_t)bit;
amdgpu_mes_unlock(mes);
return 0;
@@ -1071,18 +1075,18 @@ int amdgpu_mes_alloc_proc_ctx_index(struct amdgpu_mes *mes,
* amdgpu_mes_free_proc_ctx_index - free a process context slot
*
* @mes: MES instance
* @queue: Usermode queue whose process context index is released
* @index: process context index is released
*/
void amdgpu_mes_free_proc_ctx_index(struct amdgpu_mes *mes,
struct amdgpu_usermode_queue *queue)
uint32_t index)
{
if (!mes->use_rs64mem || !mes->proc_ctx_bitmap)
return;
if (queue->proc_ctx_array_index >= mes->proc_ctx_array_size)
if (index >= mes->proc_ctx_array_size)
return;
amdgpu_mes_lock(mes);
clear_bit(queue->proc_ctx_array_index, mes->proc_ctx_bitmap);
clear_bit(index, mes->proc_ctx_bitmap);
amdgpu_mes_unlock(mes);
}
@@ -1090,13 +1094,13 @@ void amdgpu_mes_free_proc_ctx_index(struct amdgpu_mes *mes,
* amdgpu_mes_alloc_gang_ctx_index - allocate a gang context slot
*
* @mes: MES instance
* @queue: Usermode queue receiving the allocated gang context index
* @index: the allocated gang context index
*
* Returns 0 on success, -ENOSPC if all slots are used, or
* -EOPNOTSUPP if RS64 local memory is unavailable.
*/
int amdgpu_mes_alloc_gang_ctx_index(struct amdgpu_mes *mes,
struct amdgpu_usermode_queue *queue)
uint32_t *index)
{
unsigned long bit;
@@ -1111,7 +1115,7 @@ int amdgpu_mes_alloc_gang_ctx_index(struct amdgpu_mes *mes,
return -ENOSPC;
}
set_bit(bit, mes->gang_ctx_bitmap);
queue->gang_ctx_array_index = bit;
*index = bit;
amdgpu_mes_unlock(mes);
return 0;
@@ -1121,18 +1125,18 @@ int amdgpu_mes_alloc_gang_ctx_index(struct amdgpu_mes *mes,
* amdgpu_mes_free_gang_ctx_index - free a gang context slot
*
* @mes: MES instance
* @queue: Usermode queue whose gang context index is released
* @index: gang context index is released
*/
void amdgpu_mes_free_gang_ctx_index(struct amdgpu_mes *mes,
struct amdgpu_usermode_queue *queue)
uint32_t index)
{
if (!mes->use_rs64mem || !mes->gang_ctx_bitmap)
return;
if (queue->gang_ctx_array_index >= mes->gang_ctx_array_size)
if (index >= mes->gang_ctx_array_size)
return;
amdgpu_mes_lock(mes);
clear_bit(queue->gang_ctx_array_index, mes->gang_ctx_bitmap);
clear_bit(index, mes->gang_ctx_bitmap);
amdgpu_mes_unlock(mes);
}

View File

@@ -636,11 +636,11 @@ int amdgpu_mes_rs64mem_init(struct amdgpu_mes *mes);
void amdgpu_mes_rs64mem_fini(struct amdgpu_mes *mes);
int amdgpu_mes_rs64mem_setup_bitmaps(struct amdgpu_mes *mes);
int amdgpu_mes_alloc_proc_ctx_index(struct amdgpu_mes *mes,
struct amdgpu_usermode_queue *queue);
uint32_t *index);
void amdgpu_mes_free_proc_ctx_index(struct amdgpu_mes *mes,
struct amdgpu_usermode_queue *queue);
uint32_t index);
int amdgpu_mes_alloc_gang_ctx_index(struct amdgpu_mes *mes,
struct amdgpu_usermode_queue *queue);
uint32_t *index);
void amdgpu_mes_free_gang_ctx_index(struct amdgpu_mes *mes,
struct amdgpu_usermode_queue *queue);
uint32_t index);
#endif /* __AMDGPU_MES_H__ */

View File

@@ -2751,7 +2751,8 @@ static void amdgpu_ras_do_recovery(struct work_struct *work)
}
if (amdgpu_ras_get_error_query_mode(adev, &error_query_mode)) {
if (error_query_mode == AMDGPU_RAS_FIRMWARE_ERROR_QUERY) {
if (error_query_mode == AMDGPU_RAS_FIRMWARE_ERROR_QUERY &&
(ras->gpu_reset_flags & AMDGPU_RAS_GPU_RESET_MODE1_RESET)) {
/* wait 500ms to ensure pmfw polling mca bank info done */
msleep(500);
}
@@ -4422,6 +4423,10 @@ bool amdgpu_ras_get_error_query_mode(struct amdgpu_device *adev,
if (amdgpu_sriov_vf(adev)) {
*error_query_mode = AMDGPU_RAS_VIRT_ERROR_COUNT_QUERY;
} else if (amdgpu_uniras_enabled(adev)) {
*error_query_mode = amdgpu_ras_mgr_get_debug_mode(adev) ?
AMDGPU_RAS_DIRECT_ERROR_QUERY :
AMDGPU_RAS_FIRMWARE_ERROR_QUERY;
} else {
*error_query_mode = AMDGPU_RAS_DIRECT_ERROR_QUERY;
}

View File

@@ -2139,23 +2139,17 @@ int amdgpu_ttm_init(struct amdgpu_device *adev)
/* Change the size here instead of the init above so only lpfn is affected */
amdgpu_ttm_disable_buffer_funcs(adev);
#ifdef CONFIG_64BIT
if (adev->gmc.xgmi.connected_to_cpu) {
void *kaddr = devm_memremap(adev->dev, adev->gmc.aper_base,
adev->gmc.visible_vram_size,
MEMREMAP_WB);
if (IS_ERR(kaddr))
return PTR_ERR(kaddr);
adev->mman.aper_base_kaddr = (__force void __iomem *)kaddr;
} else if (adev->gmc.is_app_apu) {
#ifdef CONFIG_X86
if (adev->gmc.xgmi.connected_to_cpu)
adev->mman.aper_base_kaddr = ioremap_cache(adev->gmc.aper_base,
adev->gmc.visible_vram_size);
else if (adev->gmc.is_app_apu)
DRM_DEBUG_DRIVER(
"No need to ioremap when real vram size is 0\n");
} else {
adev->mman.aper_base_kaddr = devm_ioremap_wc(adev->dev,
adev->gmc.aper_base,
adev->gmc.visible_vram_size);
if (!adev->mman.aper_base_kaddr)
return -ENOMEM;
}
else
#endif
adev->mman.aper_base_kaddr = ioremap_wc(adev->gmc.aper_base,
adev->gmc.visible_vram_size);
#endif
amdgpu_ttm_init_vram_resv_regions(adev);
@@ -2306,7 +2300,10 @@ void amdgpu_ttm_fini(struct amdgpu_device *adev)
amdgpu_ttm_unmark_vram_reserved(adev, AMDGPU_RESV_FW_VRAM_USAGE);
amdgpu_ttm_unmark_vram_reserved(adev, AMDGPU_RESV_DRV_VRAM_USAGE);
adev->mman.aper_base_kaddr = NULL;
if (adev->mman.aper_base_kaddr) {
iounmap(adev->mman.aper_base_kaddr);
adev->mman.aper_base_kaddr = NULL;
}
if (!adev->gmc.is_app_apu)
amdgpu_vram_mgr_fini(adev);

View File

@@ -25,6 +25,7 @@
#include <drm/drm_auth.h>
#include <drm/drm_exec.h>
#include <linux/pm_runtime.h>
#include <linux/overflow.h>
#include <drm/drm_drv.h>
#include "amdgpu.h"
@@ -238,24 +239,30 @@ int amdgpu_userq_input_va_validate(struct amdgpu_device *adev,
{
struct amdgpu_bo_va_mapping *va_map;
struct amdgpu_vm *vm = queue->vm;
u64 user_addr;
u64 size;
u64 start_addr;
u64 end_addr;
u64 start_page;
/* Caller must hold vm->root.bo reservation */
dma_resv_assert_held(queue->vm->root.bo->tbo.base.resv);
user_addr = (addr & AMDGPU_GMC_HOLE_MASK) >> AMDGPU_GPU_PAGE_SHIFT;
size = expected_size >> AMDGPU_GPU_PAGE_SHIFT;
if (!expected_size)
return -EINVAL;
va_map = amdgpu_vm_bo_lookup_mapping(vm, user_addr);
start_addr = addr & AMDGPU_GMC_HOLE_MASK;
if (check_add_overflow(start_addr, expected_size - 1, &end_addr))
return -EINVAL;
start_page = start_addr >> AMDGPU_GPU_PAGE_SHIFT;
va_map = amdgpu_vm_bo_lookup_mapping(vm, start_page);
if (!va_map)
return -EINVAL;
/* Only validate the userq whether resident in the VM mapping range */
if (user_addr >= va_map->start &&
va_map->last - user_addr + 1 >= size) {
/* Lookup guarantees start_page is mapped; ensure full span is covered. */
if ((end_addr >> AMDGPU_GPU_PAGE_SHIFT) <= va_map->last) {
va_map->bo_va->userq_va_mapped = true;
*va_out = user_addr;
*va_out = start_page;
return 0;
}
@@ -738,7 +745,12 @@ amdgpu_userq_create(struct drm_file *filp, union drm_amdgpu_userq *args)
if (!adev->userq_halt_for_enforce_isolation ||
((queue->queue_type != AMDGPU_HW_IP_GFX) &&
(queue->queue_type != AMDGPU_HW_IP_COMPUTE))) {
/* Serialize the map against an in-progress GPU reset (MES is
* unresponsive during recovery), matching amdgpu_userq_cleanup().
*/
down_read(&adev->reset_domain->sem);
r = amdgpu_userq_map_helper(queue);
up_read(&adev->reset_domain->sem);
if (r) {
drm_file_err(uq_mgr->file, "Failed to map Queue\n");
trace_amdgpu_userq_create_end(queue, r);
@@ -1250,6 +1262,7 @@ int amdgpu_userq_mgr_init(struct amdgpu_userq_mgr *userq_mgr, struct drm_file *f
xa_init_flags(&userq_mgr->userq_xa, XA_FLAGS_ALLOC);
userq_mgr->adev = adev;
userq_mgr->file = file_priv;
userq_mgr->proc_ctx_allocated = false;
mutex_init(&userq_mgr->proc_ctx_lock);
INIT_DELAYED_WORK(&userq_mgr->resume_work, amdgpu_userq_restore_worker);
@@ -1278,6 +1291,7 @@ void amdgpu_userq_mgr_cancel_resume(struct amdgpu_userq_mgr *userq_mgr)
void amdgpu_userq_mgr_fini(struct amdgpu_userq_mgr *userq_mgr)
{
struct amdgpu_mes *mes = &userq_mgr->adev->mes;
struct amdgpu_usermode_queue *queue;
unsigned long queue_id = 0;
@@ -1304,6 +1318,10 @@ void amdgpu_userq_mgr_fini(struct amdgpu_userq_mgr *userq_mgr)
*/
cancel_work_sync(&userq_mgr->reset_work);
if (userq_mgr->proc_ctx_allocated) {
amdgpu_mes_free_proc_ctx_index(mes, userq_mgr->proc_ctx_array_index);
userq_mgr->proc_ctx_allocated = false;
}
amdgpu_bo_free_kernel(&userq_mgr->proc_ctx_obj.obj,
&userq_mgr->proc_ctx_obj.gpu_addr,
&userq_mgr->proc_ctx_obj.cpu_ptr);

View File

@@ -101,7 +101,6 @@ struct amdgpu_usermode_queue {
u64 va_array[6];
} userq_vas;
uint32_t proc_ctx_array_index;
uint32_t gang_ctx_array_index;
};
@@ -133,6 +132,8 @@ struct amdgpu_userq_mgr {
struct mutex proc_ctx_lock;
struct amdgpu_userq_obj proc_ctx_obj;
bool proc_ctx_allocated;
uint32_t proc_ctx_array_index;
/**
* @reset_work:
*

View File

@@ -362,6 +362,7 @@ static void gfx_v11_0_ring_invalidate_tlbs(struct amdgpu_ring *ring,
bool all_hub, uint8_t dst_sel);
static void gfx_v11_0_set_safe_mode(struct amdgpu_device *adev, int xcc_id);
static void gfx_v11_0_unset_safe_mode(struct amdgpu_device *adev, int xcc_id);
static void gfx_v11_0_userq_priv_fault_work(struct work_struct *work);
static void gfx_v11_0_update_perf_clk(struct amdgpu_device *adev,
bool enable);
@@ -1932,6 +1933,8 @@ static int gfx_v11_0_sw_init(struct amdgpu_ip_block *ip_block)
mutex_init(&adev->gfx.mec.reset_mutex);
INIT_WORK(&adev->gfx.userq_priv_fault_work, gfx_v11_0_userq_priv_fault_work);
return 0;
}
@@ -1969,6 +1972,8 @@ static int gfx_v11_0_sw_fini(struct amdgpu_ip_block *ip_block)
int i;
struct amdgpu_device *adev = ip_block->adev;
cancel_work_sync(&adev->gfx.userq_priv_fault_work);
for (i = 0; i < adev->gfx.num_gfx_rings; i++)
amdgpu_ring_fini(&adev->gfx.gfx_ring[i]);
for (i = 0; i < adev->gfx.num_compute_rings; i++)
@@ -6711,6 +6716,40 @@ static int gfx_v11_0_set_priv_inst_fault_state(struct amdgpu_device *adev,
return 0;
}
/*
* A gfx exception IV carries no doorbell. For each faulted slot, read the
* doorbell back from the HQD (left in place by the fatal fault), look up the
* user queue and kick its per-queue reset.
*/
static void gfx_v11_0_userq_priv_fault_work(struct work_struct *work)
{
struct amdgpu_device *adev =
container_of(work, struct amdgpu_device, gfx.userq_priv_fault_work);
unsigned long slots = xchg(&adev->gfx.userq_priv_fault_slots, 0);
unsigned int id;
for_each_set_bit(id, &slots, BITS_PER_LONG) {
u8 pipe = id & 0x3;
u8 queue = (id >> 2) & 0x7;
struct amdgpu_usermode_queue *q;
u32 db_ctrl, doorbell;
amdgpu_gfx_off_ctrl(adev, false);
mutex_lock(&adev->srbm_mutex);
soc21_grbm_select(adev, 0, pipe, queue, 0);
db_ctrl = RREG32_SOC15(GC, 0, regCP_RB_DOORBELL_CONTROL);
soc21_grbm_select(adev, 0, 0, 0, 0);
mutex_unlock(&adev->srbm_mutex);
amdgpu_gfx_off_ctrl(adev, true);
doorbell = (db_ctrl & CP_RB_DOORBELL_CONTROL__DOORBELL_OFFSET_MASK) >>
CP_RB_DOORBELL_CONTROL__DOORBELL_OFFSET__SHIFT;
q = xa_load(&adev->userq_doorbell_xa, doorbell);
if (q)
amdgpu_userq_start_hang_detect_work(q);
}
}
static void gfx_v11_0_handle_priv_fault(struct amdgpu_device *adev,
struct amdgpu_iv_entry *entry)
{

View File

@@ -286,6 +286,7 @@ static void gfx_v12_0_ring_invalidate_tlbs(struct amdgpu_ring *ring,
bool all_hub, uint8_t dst_sel);
static void gfx_v12_0_set_safe_mode(struct amdgpu_device *adev, int xcc_id);
static void gfx_v12_0_unset_safe_mode(struct amdgpu_device *adev, int xcc_id);
static void gfx_v12_0_userq_priv_fault_work(struct work_struct *work);
static void gfx_v12_0_update_perf_clk(struct amdgpu_device *adev,
bool enable);
@@ -1609,6 +1610,8 @@ static int gfx_v12_0_sw_init(struct amdgpu_ip_block *ip_block)
mutex_init(&adev->gfx.mec.reset_mutex);
INIT_WORK(&adev->gfx.userq_priv_fault_work, gfx_v12_0_userq_priv_fault_work);
return 0;
}
@@ -1646,6 +1649,8 @@ static int gfx_v12_0_sw_fini(struct amdgpu_ip_block *ip_block)
int i;
struct amdgpu_device *adev = ip_block->adev;
cancel_work_sync(&adev->gfx.userq_priv_fault_work);
for (i = 0; i < adev->gfx.num_gfx_rings; i++)
amdgpu_ring_fini(&adev->gfx.gfx_ring[i]);
for (i = 0; i < adev->gfx.num_compute_rings; i++)
@@ -1828,6 +1833,12 @@ static void gfx_v12_0_constants_init(struct amdgpu_device *adev)
gfx_v12_0_get_cu_info(adev, &adev->gfx.cu_info);
gfx_v12_0_get_tcc_info(adev);
adev->gfx.config.pa_sc_tile_steering_override = 0;
/* Set whether texture coordinate truncation is conformant. */
tmp = RREG32_SOC15(GC, 0, regTA_CNTL2);
adev->gfx.config.ta_cntl2_truncate_coord_mode =
REG_GET_FIELD(tmp, TA_CNTL2, TRUNCATE_COORD_MODE);
/* Program DB_RING_CONTROL for multiple GFX pipes
* Default power up value is 1.
* Possible values:
@@ -5042,6 +5053,40 @@ static int gfx_v12_0_set_priv_inst_fault_state(struct amdgpu_device *adev,
return 0;
}
/*
* A gfx exception IV carries no doorbell. For each faulted slot, read the
* doorbell back from the HQD (left in place by the fatal fault), look up the
* user queue and kick its per-queue reset.
*/
static void gfx_v12_0_userq_priv_fault_work(struct work_struct *work)
{
struct amdgpu_device *adev =
container_of(work, struct amdgpu_device, gfx.userq_priv_fault_work);
unsigned long slots = xchg(&adev->gfx.userq_priv_fault_slots, 0);
unsigned int id;
for_each_set_bit(id, &slots, BITS_PER_LONG) {
u8 pipe = id & 0x3;
u8 queue = (id >> 2) & 0x7;
struct amdgpu_usermode_queue *q;
u32 db_ctrl, doorbell;
amdgpu_gfx_off_ctrl(adev, false);
mutex_lock(&adev->srbm_mutex);
soc24_grbm_select(adev, 0, pipe, queue, 0);
db_ctrl = RREG32_SOC15(GC, 0, regCP_RB_DOORBELL_CONTROL);
soc24_grbm_select(adev, 0, 0, 0, 0);
mutex_unlock(&adev->srbm_mutex);
amdgpu_gfx_off_ctrl(adev, true);
doorbell = (db_ctrl & CP_RB_DOORBELL_CONTROL__DOORBELL_OFFSET_MASK) >>
CP_RB_DOORBELL_CONTROL__DOORBELL_OFFSET__SHIFT;
q = xa_load(&adev->userq_doorbell_xa, doorbell);
if (q)
amdgpu_userq_start_hang_detect_work(q);
}
}
static void gfx_v12_0_handle_priv_fault(struct amdgpu_device *adev,
struct amdgpu_iv_entry *entry)
{

View File

@@ -1597,7 +1597,7 @@ static void gfx_v6_0_setup_spi(struct amdgpu_device *adev)
*/
static void gfx_v6_0_setup_tcc(struct amdgpu_device *adev)
{
u32 i, tcc, tcp_addr_config, num_active_tcc = 0;
u32 i, tcc, tcp_addr_config, num_active_tcc = 0, num_max_active_tcc;
u64 chan_steer, patched_chan_steer = 0;
const u32 num_max_tcc = adev->gfx.config.max_texture_channel_caches;
const u32 dis_tcc_mask =
@@ -1611,6 +1611,8 @@ static void gfx_v6_0_setup_tcc(struct amdgpu_device *adev)
if (!dis_tcc_mask)
return;
num_max_active_tcc = num_max_tcc - hweight32(dis_tcc_mask);
/* Each 4-bit nibble contains the index of a TCC used by all TCPs */
chan_steer = RREG32(mmTCP_CHAN_STEER_LO) | ((u64)RREG32(mmTCP_CHAN_STEER_HI) << 32ull);
@@ -1623,9 +1625,12 @@ static void gfx_v6_0_setup_tcc(struct amdgpu_device *adev)
patched_chan_steer |= (u64)tcc << (u64)(4 * num_active_tcc);
++num_active_tcc;
}
if (num_active_tcc == num_max_active_tcc)
break;
}
WARN_ON(num_active_tcc != num_max_tcc - hweight32(dis_tcc_mask));
WARN_ON(num_active_tcc != num_max_active_tcc);
/* Patch number of TCCs used by TCPs */
tcp_addr_config = REG_SET_FIELD(RREG32(mmTCP_ADDR_CONFIG),
@@ -1635,6 +1640,8 @@ static void gfx_v6_0_setup_tcc(struct amdgpu_device *adev)
WREG32(mmTCP_ADDR_CONFIG, tcp_addr_config);
WREG32(mmTCP_CHAN_STEER_HI, upper_32_bits(patched_chan_steer));
WREG32(mmTCP_CHAN_STEER_LO, lower_32_bits(patched_chan_steer));
adev->gfx.config.tcc_disabled_mask = dis_tcc_mask;
}
static void gfx_v6_0_config_init(struct amdgpu_device *adev)
@@ -1882,11 +1889,13 @@ static int gfx_v6_0_ring_test_ring(struct amdgpu_ring *ring)
return r;
}
static void gfx_v6_0_ring_emit_vgt_flush(struct amdgpu_ring *ring)
static void gfx_v6_0_ring_emit_event_write(struct amdgpu_ring *ring,
uint32_t event_type,
uint32_t event_index)
{
amdgpu_ring_write(ring, PACKET3(PACKET3_EVENT_WRITE, 0));
amdgpu_ring_write(ring, EVENT_TYPE(VGT_FLUSH) |
EVENT_INDEX(0));
amdgpu_ring_write(ring, EVENT_TYPE(event_type) |
EVENT_INDEX(event_index));
}
static void gfx_v6_0_ring_emit_fence(struct amdgpu_ring *ring, u64 addr,
@@ -1925,12 +1934,6 @@ static void gfx_v6_0_ring_emit_ib(struct amdgpu_ring *ring,
unsigned vmid = AMDGPU_JOB_GET_VMID(job);
u32 header, control = 0;
/* insert SWITCH_BUFFER packet before first IB in the ring frame */
if (flags & AMDGPU_HAVE_CTX_SWITCH) {
amdgpu_ring_write(ring, PACKET3(PACKET3_SWITCH_BUFFER, 0));
amdgpu_ring_write(ring, 0);
}
if (ib->flags & AMDGPU_IB_FLAG_CE)
header = PACKET3(PACKET3_INDIRECT_BUFFER_CONST, 2);
else
@@ -2133,12 +2136,24 @@ static int gfx_v6_0_cp_gfx_start(struct amdgpu_device *adev)
return 0;
}
/**
* gfx_v6_0_cp_gfx_resume() - Initialize CP rings
*
* @adev: amdgpu_device pointer
*
* In GFX6 GPUs, compute takes the same CP path as graphics.
* CP ME command parser executes packets for each ring buffer:
* RB0 supports graphics, RB1 and RB2 are compute only.
* Initialize all three rings before calling gfx_v6_0_cp_gfx_start()
* to make sure they are all in a sane state before execution starts.
*/
static int gfx_v6_0_cp_gfx_resume(struct amdgpu_device *adev)
{
struct amdgpu_ring *ring;
u32 tmp;
u32 rb_bufsz;
int r;
int i;
u64 rptr_addr;
WREG32(mmCP_SEM_WAIT_TIMER, 0x0);
@@ -2151,8 +2166,12 @@ static int gfx_v6_0_cp_gfx_resume(struct amdgpu_device *adev)
WREG32(mmSCRATCH_ADDR, 0);
/* ring 0 - compute and gfx */
/* Set ring buffer size */
ring = &adev->gfx.gfx_ring[0];
*ring->wptr_cpu_addr = 0;
*ring->rptr_cpu_addr = 0;
amdgpu_ring_clear_ring(ring);
/* Set ring buffer size */
rb_bufsz = order_base_2(ring->ring_size / 8);
tmp = (order_base_2(AMDGPU_GPU_PAGE_SIZE/8) << 8) | rb_bufsz;
@@ -2164,7 +2183,8 @@ static int gfx_v6_0_cp_gfx_resume(struct amdgpu_device *adev)
/* Initialize the ring buffer's read and write pointers */
WREG32(mmCP_RB0_CNTL, tmp | CP_RB0_CNTL__RB_RPTR_WR_ENA_MASK);
ring->wptr = 0;
WREG32(mmCP_RB0_WPTR, ring->wptr);
WREG32(mmCP_RB0_WPTR, lower_32_bits(ring->wptr));
WREG32(mmCP_RB0_RPTR, lower_32_bits(ring->wptr));
/* set the wb address whether it's enabled or not */
rptr_addr = ring->rptr_gpu_addr;
@@ -2178,12 +2198,86 @@ static int gfx_v6_0_cp_gfx_resume(struct amdgpu_device *adev)
WREG32(mmCP_RB0_BASE, ring->gpu_addr >> 8);
/* ring 1 - compute only */
if (adev->gfx.num_compute_rings >= 1) {
ring = &adev->gfx.compute_ring[0];
*ring->wptr_cpu_addr = 0;
*ring->rptr_cpu_addr = 0;
amdgpu_ring_clear_ring(ring);
rb_bufsz = order_base_2(ring->ring_size / 8);
tmp = (order_base_2(AMDGPU_GPU_PAGE_SIZE / 8) << 8) | rb_bufsz;
#ifdef __BIG_ENDIAN
tmp |= BUF_SWAP_32BIT;
#endif
WREG32(mmCP_RB1_CNTL, tmp);
WREG32(mmCP_RB1_CNTL, tmp | CP_RB1_CNTL__RB_RPTR_WR_ENA_MASK);
ring->wptr = 0;
WREG32(mmCP_RB1_WPTR, lower_32_bits(ring->wptr));
WREG32(mmCP_RB1_RPTR, lower_32_bits(ring->wptr));
rptr_addr = ring->rptr_gpu_addr;
WREG32(mmCP_RB1_RPTR_ADDR, lower_32_bits(rptr_addr));
WREG32(mmCP_RB1_RPTR_ADDR_HI, upper_32_bits(rptr_addr) & 0xFF);
mdelay(1);
WREG32(mmCP_RB1_CNTL, tmp);
WREG32(mmCP_RB1_BASE, ring->gpu_addr >> 8);
}
/* ring 2 - compute only */
if (adev->gfx.num_compute_rings >= 2) {
ring = &adev->gfx.compute_ring[1];
*ring->wptr_cpu_addr = 0;
*ring->rptr_cpu_addr = 0;
amdgpu_ring_clear_ring(ring);
rb_bufsz = order_base_2(ring->ring_size / 8);
tmp = (order_base_2(AMDGPU_GPU_PAGE_SIZE / 8) << 8) | rb_bufsz;
#ifdef __BIG_ENDIAN
tmp |= BUF_SWAP_32BIT;
#endif
WREG32(mmCP_RB2_CNTL, tmp);
WREG32(mmCP_RB2_CNTL, tmp | CP_RB2_CNTL__RB_RPTR_WR_ENA_MASK);
ring->wptr = 0;
WREG32(mmCP_RB2_WPTR, lower_32_bits(ring->wptr));
WREG32(mmCP_RB2_RPTR, lower_32_bits(ring->wptr));
rptr_addr = ring->rptr_gpu_addr;
WREG32(mmCP_RB2_RPTR_ADDR, lower_32_bits(rptr_addr));
WREG32(mmCP_RB2_RPTR_ADDR_HI, upper_32_bits(rptr_addr) & 0xFF);
mdelay(1);
WREG32(mmCP_RB2_CNTL, tmp);
WREG32(mmCP_RB2_BASE, ring->gpu_addr >> 8);
}
/* start the rings */
gfx_v6_0_cp_gfx_start(adev);
r = amdgpu_ring_test_helper(ring);
/* Wait for the initial packets to finish, run gfx ring test */
r = amdgpu_ring_test_helper(&adev->gfx.gfx_ring[0]);
if (r)
return r;
for (i = 0; i < adev->gfx.num_compute_rings; i++) {
ring = &adev->gfx.compute_ring[i];
r = amdgpu_ring_alloc(ring, 2);
if (r)
return r;
amdgpu_ring_write(ring, PACKET3_COMPUTE(PACKET3_CLEAR_STATE, 0));
amdgpu_ring_write(ring, 0);
amdgpu_ring_commit(ring);
r = amdgpu_ring_test_helper(ring);
if (r)
return r;
}
return 0;
}
@@ -2228,66 +2322,6 @@ static void gfx_v6_0_ring_set_wptr_compute(struct amdgpu_ring *ring)
}
static int gfx_v6_0_cp_compute_resume(struct amdgpu_device *adev)
{
struct amdgpu_ring *ring;
u32 tmp;
u32 rb_bufsz;
int i, r;
u64 rptr_addr;
/* ring1 - compute only */
/* Set ring buffer size */
ring = &adev->gfx.compute_ring[0];
rb_bufsz = order_base_2(ring->ring_size / 8);
tmp = (order_base_2(AMDGPU_GPU_PAGE_SIZE/8) << 8) | rb_bufsz;
#ifdef __BIG_ENDIAN
tmp |= BUF_SWAP_32BIT;
#endif
WREG32(mmCP_RB1_CNTL, tmp);
WREG32(mmCP_RB1_CNTL, tmp | CP_RB1_CNTL__RB_RPTR_WR_ENA_MASK);
ring->wptr = 0;
WREG32(mmCP_RB1_WPTR, ring->wptr);
rptr_addr = ring->rptr_gpu_addr;
WREG32(mmCP_RB1_RPTR_ADDR, lower_32_bits(rptr_addr));
WREG32(mmCP_RB1_RPTR_ADDR_HI, upper_32_bits(rptr_addr) & 0xFF);
mdelay(1);
WREG32(mmCP_RB1_CNTL, tmp);
WREG32(mmCP_RB1_BASE, ring->gpu_addr >> 8);
ring = &adev->gfx.compute_ring[1];
rb_bufsz = order_base_2(ring->ring_size / 8);
tmp = (order_base_2(AMDGPU_GPU_PAGE_SIZE/8) << 8) | rb_bufsz;
#ifdef __BIG_ENDIAN
tmp |= BUF_SWAP_32BIT;
#endif
WREG32(mmCP_RB2_CNTL, tmp);
WREG32(mmCP_RB2_CNTL, tmp | CP_RB2_CNTL__RB_RPTR_WR_ENA_MASK);
ring->wptr = 0;
WREG32(mmCP_RB2_WPTR, ring->wptr);
rptr_addr = ring->rptr_gpu_addr;
WREG32(mmCP_RB2_RPTR_ADDR, lower_32_bits(rptr_addr));
WREG32(mmCP_RB2_RPTR_ADDR_HI, upper_32_bits(rptr_addr) & 0xFF);
mdelay(1);
WREG32(mmCP_RB2_CNTL, tmp);
WREG32(mmCP_RB2_BASE, ring->gpu_addr >> 8);
for (i = 0; i < 2; i++) {
r = amdgpu_ring_test_helper(&adev->gfx.compute_ring[i]);
if (r)
return r;
}
return 0;
}
static void gfx_v6_0_cp_enable(struct amdgpu_device *adev, bool enable)
{
gfx_v6_0_cp_gfx_enable(adev, enable);
@@ -2337,9 +2371,6 @@ static int gfx_v6_0_cp_resume(struct amdgpu_device *adev)
return r;
r = gfx_v6_0_cp_gfx_resume(adev);
if (r)
return r;
r = gfx_v6_0_cp_compute_resume(adev);
if (r)
return r;
@@ -2350,7 +2381,7 @@ static int gfx_v6_0_cp_resume(struct amdgpu_device *adev)
static void gfx_v6_0_ring_emit_pipeline_sync(struct amdgpu_ring *ring)
{
int usepfp = (ring->funcs->type == AMDGPU_RING_TYPE_GFX);
int usepfp = 1;
uint32_t seq = ring->fence_drv.sync_seq;
uint64_t addr = ring->fence_drv.gpu_addr;
@@ -2363,20 +2394,12 @@ static void gfx_v6_0_ring_emit_pipeline_sync(struct amdgpu_ring *ring)
amdgpu_ring_write(ring, seq);
amdgpu_ring_write(ring, 0xffffffff);
amdgpu_ring_write(ring, 4); /* poll interval */
if (usepfp) {
/* synce CE with ME to prevent CE fetch CEIB before context switch done */
amdgpu_ring_write(ring, PACKET3(PACKET3_SWITCH_BUFFER, 0));
amdgpu_ring_write(ring, 0);
amdgpu_ring_write(ring, PACKET3(PACKET3_SWITCH_BUFFER, 0));
amdgpu_ring_write(ring, 0);
}
}
static void gfx_v6_0_ring_emit_vm_flush(struct amdgpu_ring *ring,
unsigned vmid, uint64_t pd_addr)
{
int usepfp = (ring->funcs->type == AMDGPU_RING_TYPE_GFX);
int usepfp = 1;
amdgpu_gmc_emit_flush_gpu_tlb(ring, vmid, pd_addr);
@@ -2394,19 +2417,31 @@ static void gfx_v6_0_ring_emit_vm_flush(struct amdgpu_ring *ring,
/* sync PFP to ME, otherwise we might get invalid PFP reads */
amdgpu_ring_write(ring, PACKET3(PACKET3_PFP_SYNC_ME, 0));
amdgpu_ring_write(ring, 0x0);
/* synce CE with ME to prevent CE fetch CEIB before context switch done */
amdgpu_ring_write(ring, PACKET3(PACKET3_SWITCH_BUFFER, 0));
amdgpu_ring_write(ring, 0);
amdgpu_ring_write(ring, PACKET3(PACKET3_SWITCH_BUFFER, 0));
amdgpu_ring_write(ring, 0);
}
}
static unsigned int gfx_v6_0_ring_emit_init_cond_exec(struct amdgpu_ring *ring,
uint64_t gpu_addr)
{
unsigned int ret;
/*
* Discard following DWs after this packet when gpu_addr==0
* The packet is only 4 DW on GFX6 (as opposed to GFX7+).
*/
amdgpu_ring_write(ring, PACKET3(PACKET3_COND_EXEC, 2));
amdgpu_ring_write(ring, lower_32_bits(gpu_addr));
amdgpu_ring_write(ring, upper_32_bits(gpu_addr));
ret = ring->wptr & ring->buf_mask;
/* patch dummy value later */
amdgpu_ring_write(ring, 0);
return ret;
}
static void gfx_v6_0_ring_emit_wreg(struct amdgpu_ring *ring,
uint32_t reg, uint32_t val)
{
int usepfp = (ring->funcs->type == AMDGPU_RING_TYPE_GFX);
int usepfp = 1;
amdgpu_ring_write(ring, PACKET3(PACKET3_WRITE_DATA, 3));
amdgpu_ring_write(ring, (WRITE_DATA_ENGINE_SEL(usepfp) |
@@ -2995,12 +3030,30 @@ static uint64_t gfx_v6_0_get_gpu_clock_counter(struct amdgpu_device *adev)
return clock;
}
static void gfx_v6_0_ring_emit_sb(struct amdgpu_ring *ring)
{
amdgpu_ring_write(ring, PACKET3(PACKET3_SWITCH_BUFFER, 0));
amdgpu_ring_write(ring, 0);
}
static void gfx_v6_ring_emit_cntxcntl(struct amdgpu_ring *ring, uint32_t flags)
{
if (flags & AMDGPU_HAVE_CTX_SWITCH)
gfx_v6_0_ring_emit_vgt_flush(ring);
u32 dw2 = 0x80000000; /* set load_enable otherwise this package is just NOPs */
if (flags & AMDGPU_HAVE_CTX_SWITCH) {
gfx_v6_0_ring_emit_event_write(ring, VS_PARTIAL_FLUSH, 4);
gfx_v6_0_ring_emit_event_write(ring, VGT_FLUSH, 0);
/* set load_global_config (load_global_uconfig doesn't exist on GFX6) */
dw2 |= 0x1;
/* set load_cs_sh_regs */
dw2 |= 0x01000000;
/* set load_per_context_state & load_gfx_sh_regs */
dw2 |= 0x10002;
}
amdgpu_ring_write(ring, PACKET3(PACKET3_CONTEXT_CONTROL, 1));
amdgpu_ring_write(ring, 0x80000000);
amdgpu_ring_write(ring, dw2);
amdgpu_ring_write(ring, 0);
}
@@ -3102,6 +3155,11 @@ static int gfx_v6_0_early_init(struct amdgpu_ip_block *ip_block)
return 0;
}
static int gfx_v6_0_late_init(struct amdgpu_ip_block *ip_block)
{
return 0;
}
static int gfx_v6_0_sw_init(struct amdgpu_ip_block *ip_block)
{
struct amdgpu_ring *ring;
@@ -3172,6 +3230,11 @@ static int gfx_v6_0_sw_init(struct amdgpu_ip_block *ip_block)
adev->gfx.compute_supported_reset =
amdgpu_get_soft_full_reset_mask(&adev->gfx.compute_ring[0]);
if (!amdgpu_sriov_vf(adev) && !adev->debug_disable_ip_block_soft_reset) {
adev->gfx.compute_supported_reset |= AMDGPU_RESET_TYPE_IP_BLOCK_SOFT_RESET;
adev->gfx.gfx_supported_reset |= AMDGPU_RESET_TYPE_IP_BLOCK_SOFT_RESET;
}
return r;
}
@@ -3254,6 +3317,99 @@ static int gfx_v6_0_wait_for_idle(struct amdgpu_ip_block *ip_block)
return -ETIMEDOUT;
}
static int gfx_v6_0_soft_reset(struct amdgpu_ip_block *ip_block)
{
struct amdgpu_device *adev = ip_block->adev;
u32 grbm_soft_reset = 0, srbm_soft_reset = 0;
u32 tmp;
int r;
grbm_soft_reset =
REG_SET_FIELD(0, GRBM_SOFT_RESET, SOFT_RESET_CP, 1) |
REG_SET_FIELD(0, GRBM_SOFT_RESET, SOFT_RESET_CB, 1) |
REG_SET_FIELD(0, GRBM_SOFT_RESET, SOFT_RESET_RLC, 1) |
REG_SET_FIELD(0, GRBM_SOFT_RESET, SOFT_RESET_DB, 1) |
REG_SET_FIELD(0, GRBM_SOFT_RESET, SOFT_RESET_GDS, 1) |
REG_SET_FIELD(0, GRBM_SOFT_RESET, SOFT_RESET_PA, 1) |
REG_SET_FIELD(0, GRBM_SOFT_RESET, SOFT_RESET_SC, 1) |
REG_SET_FIELD(0, GRBM_SOFT_RESET, SOFT_RESET_BCI, 1) |
REG_SET_FIELD(0, GRBM_SOFT_RESET, SOFT_RESET_SPI, 1) |
REG_SET_FIELD(0, GRBM_SOFT_RESET, SOFT_RESET_SX, 1) |
REG_SET_FIELD(0, GRBM_SOFT_RESET, SOFT_RESET_TC, 1) |
REG_SET_FIELD(0, GRBM_SOFT_RESET, SOFT_RESET_TA, 1) |
REG_SET_FIELD(0, GRBM_SOFT_RESET, SOFT_RESET_VGT, 1) |
REG_SET_FIELD(0, GRBM_SOFT_RESET, SOFT_RESET_IA, 1);
srbm_soft_reset =
REG_SET_FIELD(0, SRBM_SOFT_RESET, SOFT_RESET_GRBM, 1) |
REG_SET_FIELD(0, SRBM_SOFT_RESET, SOFT_RESET_SEM, 1);
ip_block->version->funcs->set_clockgating_state(ip_block, AMD_CG_STATE_UNGATE);
ip_block->version->funcs->set_powergating_state(ip_block, AMD_PG_STATE_UNGATE);
ip_block->version->funcs->suspend(ip_block);
if (grbm_soft_reset || srbm_soft_reset) {
tmp = RREG32(mmGMCON_DEBUG);
tmp = REG_SET_FIELD(tmp, GMCON_DEBUG, GFX_STALL, 1);
tmp = REG_SET_FIELD(tmp, GMCON_DEBUG, GFX_CLEAR, 1);
WREG32(mmGMCON_DEBUG, tmp);
udelay(100);
}
if (grbm_soft_reset) {
tmp = RREG32(mmGRBM_SOFT_RESET);
tmp |= grbm_soft_reset;
dev_info(adev->dev, "GRBM_SOFT_RESET=0x%08X\n", tmp);
WREG32(mmGRBM_SOFT_RESET, tmp);
tmp = RREG32(mmGRBM_SOFT_RESET);
udelay(100);
tmp &= ~grbm_soft_reset;
WREG32(mmGRBM_SOFT_RESET, tmp);
tmp = RREG32(mmGRBM_SOFT_RESET);
udelay(100);
}
if (srbm_soft_reset) {
tmp = RREG32(mmSRBM_SOFT_RESET);
tmp |= srbm_soft_reset;
dev_info(adev->dev, "SRBM_SOFT_RESET=0x%08X\n", tmp);
WREG32(mmSRBM_SOFT_RESET, tmp);
tmp = RREG32(mmSRBM_SOFT_RESET);
udelay(100);
tmp &= ~srbm_soft_reset;
WREG32(mmSRBM_SOFT_RESET, tmp);
tmp = RREG32(mmSRBM_SOFT_RESET);
udelay(100);
}
if (grbm_soft_reset || srbm_soft_reset) {
tmp = RREG32(mmGMCON_DEBUG);
tmp = REG_SET_FIELD(tmp, GMCON_DEBUG, GFX_STALL, 0);
tmp = REG_SET_FIELD(tmp, GMCON_DEBUG, GFX_CLEAR, 0);
WREG32(mmGMCON_DEBUG, tmp);
}
/* Wait a little for things to settle down */
udelay(100);
r = ip_block->version->funcs->resume(ip_block);
r |= ip_block->version->funcs->late_init(ip_block);
if (r)
return r;
ip_block->version->funcs->set_clockgating_state(ip_block, AMD_CG_STATE_GATE);
ip_block->version->funcs->set_powergating_state(ip_block, AMD_PG_STATE_GATE);
return 0;
}
static void gfx_v6_0_set_gfx_eop_interrupt_state(struct amdgpu_device *adev,
enum amdgpu_interrupt_state state)
{
@@ -3502,6 +3658,7 @@ static void gfx_v6_0_emit_mem_sync(struct amdgpu_ring *ring)
static const struct amd_ip_funcs gfx_v6_0_ip_funcs = {
.name = "gfx_v6_0",
.early_init = gfx_v6_0_early_init,
.late_init = gfx_v6_0_late_init,
.sw_init = gfx_v6_0_sw_init,
.sw_fini = gfx_v6_0_sw_fini,
.hw_init = gfx_v6_0_hw_init,
@@ -3510,6 +3667,7 @@ static const struct amd_ip_funcs gfx_v6_0_ip_funcs = {
.resume = gfx_v6_0_resume,
.is_idle = gfx_v6_0_is_idle,
.wait_for_idle = gfx_v6_0_wait_for_idle,
.soft_reset = gfx_v6_0_soft_reset,
.set_clockgating_state = gfx_v6_0_set_clockgating_state,
.set_powergating_state = gfx_v6_0_set_powergating_state,
};
@@ -3523,13 +3681,16 @@ static const struct amdgpu_ring_funcs gfx_v6_0_ring_funcs_gfx = {
.get_wptr = gfx_v6_0_ring_get_wptr,
.set_wptr = gfx_v6_0_ring_set_wptr_gfx,
.emit_frame_size =
4 + /* gfx_v6_0_ring_emit_init_cond_exec (from amdgpu_ib_schedule) */
4 + /* gfx_v6_0_ring_emit_init_cond_exec (from amdgpu_vm_flush) */
5 + 5 + /* hdp flush / invalidate */
14 + 14 + 14 + /* gfx_v6_0_ring_emit_fence x3 for user fence, vm fence */
7 + 4 + /* gfx_v6_0_ring_emit_pipeline_sync */
SI_FLUSH_GPU_TLB_NUM_WREG * 5 + 7 + 6 + /* gfx_v6_0_ring_emit_vm_flush */
3 + 2 + /* gfx_v6_ring_emit_cntxcntl including vgt flush */
7 + /* gfx_v6_0_ring_emit_pipeline_sync */
SI_FLUSH_GPU_TLB_NUM_WREG * 5 + 7 + 2 + /* gfx_v6_0_ring_emit_vm_flush */
3 * 2 + /* gfx_v6_0_ring_emit_sb x3 (from amdgpu_vm_flush, amdgpu_ib_schedule) */
3 + 2 + 2 + /* gfx_v6_ring_emit_cntxcntl including VGT flush */
5, /* SURFACE_SYNC */
.emit_ib_size = 6, /* gfx_v6_0_ring_emit_ib */
.emit_ib_size = 4, /* gfx_v6_0_ring_emit_ib */
.emit_ib = gfx_v6_0_ring_emit_ib,
.emit_fence = gfx_v6_0_ring_emit_fence,
.emit_pipeline_sync = gfx_v6_0_ring_emit_pipeline_sync,
@@ -3537,7 +3698,9 @@ static const struct amdgpu_ring_funcs gfx_v6_0_ring_funcs_gfx = {
.test_ring = gfx_v6_0_ring_test_ring,
.test_ib = gfx_v6_0_ring_test_ib,
.insert_nop = amdgpu_ring_insert_nop,
.emit_switch_buffer = gfx_v6_0_ring_emit_sb,
.emit_cntxcntl = gfx_v6_ring_emit_cntxcntl,
.init_cond_exec = gfx_v6_0_ring_emit_init_cond_exec,
.emit_wreg = gfx_v6_0_ring_emit_wreg,
.emit_mem_sync = gfx_v6_0_emit_mem_sync,
};
@@ -3550,12 +3713,15 @@ static const struct amdgpu_ring_funcs gfx_v6_0_ring_funcs_compute = {
.get_wptr = gfx_v6_0_ring_get_wptr,
.set_wptr = gfx_v6_0_ring_set_wptr_compute,
.emit_frame_size =
4 + /* gfx_v6_0_ring_emit_init_cond_exec (from amdgpu_ib_schedule) */
4 + /* gfx_v6_0_ring_emit_init_cond_exec (from amdgpu_vm_flush) */
5 + 5 + /* hdp flush / invalidate */
7 + /* gfx_v6_0_ring_emit_pipeline_sync */
SI_FLUSH_GPU_TLB_NUM_WREG * 5 + 7 + /* gfx_v6_0_ring_emit_vm_flush */
SI_FLUSH_GPU_TLB_NUM_WREG * 5 + 7 + 2 + /* gfx_v6_0_ring_emit_vm_flush */
14 + 14 + 14 + /* gfx_v6_0_ring_emit_fence x3 for user fence, vm fence */
3 * 2 + /* gfx_v6_0_ring_emit_sb x3 (from amdgpu_vm_flush, amdgpu_ib_schedule) */
5, /* SURFACE_SYNC */
.emit_ib_size = 6, /* gfx_v6_0_ring_emit_ib */
.emit_ib_size = 4, /* gfx_v6_0_ring_emit_ib */
.emit_ib = gfx_v6_0_ring_emit_ib,
.emit_fence = gfx_v6_0_ring_emit_fence,
.emit_pipeline_sync = gfx_v6_0_ring_emit_pipeline_sync,
@@ -3563,6 +3729,8 @@ static const struct amdgpu_ring_funcs gfx_v6_0_ring_funcs_compute = {
.test_ring = gfx_v6_0_ring_test_ring,
.test_ib = gfx_v6_0_ring_test_ib,
.insert_nop = amdgpu_ring_insert_nop,
.emit_switch_buffer = gfx_v6_0_ring_emit_sb,
.init_cond_exec = gfx_v6_0_ring_emit_init_cond_exec,
.emit_wreg = gfx_v6_0_ring_emit_wreg,
.emit_mem_sync = gfx_v6_0_emit_mem_sync,
};

View File

@@ -640,10 +640,6 @@ static int gmc_v10_0_late_init(struct amdgpu_ip_block *ip_block)
if (r)
return r;
r = amdgpu_gmc_ras_late_init(adev);
if (r)
return r;
return amdgpu_irq_get(adev, &adev->gmc.vm_fault, 0);
}

View File

@@ -646,10 +646,6 @@ static int gmc_v11_0_late_init(struct amdgpu_ip_block *ip_block)
if (r)
return r;
r = amdgpu_gmc_ras_late_init(adev);
if (r)
return r;
return amdgpu_irq_get(adev, &adev->gmc.vm_fault, 0);
}

View File

@@ -682,10 +682,6 @@ static int gmc_v12_0_late_init(struct amdgpu_ip_block *ip_block)
if (r)
return r;
r = amdgpu_gmc_ras_late_init(adev);
if (r)
return r;
return amdgpu_irq_get(adev, &adev->gmc.vm_fault, 0);
}

View File

@@ -267,9 +267,24 @@ static bool gmc_v12_1_get_vmid_pasid_mapping_info(struct amdgpu_device *adev,
* by the amdgpu vm/hsa code.
*/
/**
* gmc_v12_1_use_invalidate_semaphore - judge whether to use semaphore
*
* @adev: amdgpu_device pointer
* @vmhub: vmhub type
*
*/
static bool gmc_v12_1_use_invalidate_semaphore(struct amdgpu_device *adev,
uint32_t vmhub)
{
return ((!AMDGPU_IS_GFXHUB(vmhub)) &&
(!amdgpu_sriov_vf(adev)));
}
static void gmc_v12_1_flush_vm_hub(struct amdgpu_device *adev, uint32_t vmid,
unsigned int vmhub, uint32_t flush_type)
{
bool use_semaphore = gmc_v12_1_use_invalidate_semaphore(adev, vmhub);
struct amdgpu_vmhub *hub = &adev->vmhub[vmhub];
u32 inv_req = hub->vmhub_funcs->get_invalidate_req(vmid, flush_type);
u32 tmp;
@@ -283,6 +298,19 @@ static void gmc_v12_1_flush_vm_hub(struct amdgpu_device *adev, uint32_t vmid,
spin_lock(&adev->gmc.invalidate_lock);
if (use_semaphore) {
for (i = 0; i < adev->usec_timeout; i++) {
/* a read return value of 1 means semaphore acuqire */
tmp = RREG32_RLC_NO_KIQ(hub->vm_inv_eng0_sem + hub->eng_distance * eng, hub_ip);
if (tmp & 0x1)
break;
udelay(1);
}
if (i >= adev->usec_timeout)
DRM_ERROR("Timeout waiting for sem acquire in VM flush!\n");
}
WREG32_RLC_NO_KIQ(hub->vm_inv_eng0_req + hub->eng_distance * eng, inv_req, hub_ip);
/* Wait for ACK with a delay.*/
@@ -296,6 +324,9 @@ static void gmc_v12_1_flush_vm_hub(struct amdgpu_device *adev, uint32_t vmid,
udelay(1);
}
if (use_semaphore)
WREG32_RLC_NO_KIQ(hub->vm_inv_eng0_sem + hub->eng_distance * eng, 0, hub_ip);
/* Issue additional private vm invalidation to MMHUB */
if (!AMDGPU_IS_GFXHUB(vmhub) &&
(hub->vm_l2_bank_select_reserved_cid2) &&
@@ -396,7 +427,7 @@ static void gmc_v12_1_flush_gpu_tlb_pasid(struct amdgpu_device *adev,
if (all_hub) {
/* invalidate mm_hub */
if (test_bit(AMDGPU_MMHUB1(0), adev->vmhubs_mask)) {
if (test_bit(AMDGPU_MMHUB0(0), adev->vmhubs_mask)) {
input.hub_id = AMDGPU_MMHUB0(0);
adev->mes.funcs->invalidate_tlbs_pasid(&adev->mes, &input);
}
@@ -431,10 +462,17 @@ static void gmc_v12_1_flush_gpu_tlb_pasid(struct amdgpu_device *adev,
static uint64_t gmc_v12_1_emit_flush_gpu_tlb(struct amdgpu_ring *ring,
unsigned vmid, uint64_t pd_addr)
{
bool use_semaphore = gmc_v12_1_use_invalidate_semaphore(ring->adev, ring->vm_hub);
struct amdgpu_vmhub *hub = &ring->adev->vmhub[ring->vm_hub];
uint32_t req = hub->vmhub_funcs->get_invalidate_req(vmid, 0);
unsigned eng = ring->vm_inv_eng;
if (use_semaphore)
/* a read return value of 1 means semaphore acuqire */
amdgpu_ring_emit_reg_wait(ring,
hub->vm_inv_eng0_sem +
hub->eng_distance * eng, 0x1, 0x1);
amdgpu_ring_emit_wreg(ring, hub->ctx0_ptb_addr_lo32 +
(hub->ctx_addr_distance * vmid),
lower_32_bits(pd_addr));
@@ -449,6 +487,14 @@ static uint64_t gmc_v12_1_emit_flush_gpu_tlb(struct amdgpu_ring *ring,
hub->eng_distance * eng,
req, 1 << vmid);
if (use_semaphore)
/*
* add semaphore release after invalidation,
* write with 0 means semaphore release
*/
amdgpu_ring_emit_wreg(ring, hub->vm_inv_eng0_sem +
hub->eng_distance * eng, 0);
return pd_addr;
}

View File

@@ -1640,10 +1640,6 @@ static int gmc_v9_0_late_init(struct amdgpu_ip_block *ip_block)
amdgpu_ras_reset_error_count(adev, AMDGPU_RAS_BLOCK__HDP);
}
r = amdgpu_gmc_ras_late_init(adev);
if (r)
return r;
return amdgpu_irq_get(adev, &adev->gmc.vm_fault, 0);
}
@@ -2063,7 +2059,6 @@ static int gmc_v9_0_sw_fini(struct amdgpu_ip_block *ip_block)
if (amdgpu_is_multi_aid(adev))
amdgpu_gmc_sysfs_fini(adev);
amdgpu_gmc_ras_fini(adev);
amdgpu_gem_force_release(adev);
amdgpu_vm_manager_fini(adev);
if (!adev->gmc.real_vram_size) {

View File

@@ -772,15 +772,28 @@ static int jpeg_v4_0_5_ring_reset(struct amdgpu_ring *ring,
unsigned int vmid,
struct amdgpu_fence *timedout_fence)
{
struct amdgpu_device *adev = ring->adev;
u32 pg_flags = adev->pg_flags;
int r;
amdgpu_ring_reset_helper_begin(ring, timedout_fence);
r = jpeg_v4_0_5_stop(ring->adev);
if (r)
return r;
r = jpeg_v4_0_5_start(ring->adev);
/*
* The DPG stop path only clears the JPEG_PG_MODE bit and never resets a
* hung JRBC, so the post-reset ring test times out and the driver falls
* back to a full MODE1 reset. Temporarily force the static power-gating
* path so the stop/start sequence actually power-cycles the JPEG block
* (JMI soft reset + static power off/on), matching the working jpeg_v4_0
* reset.
*/
adev->pg_flags &= ~AMD_PG_SUPPORT_JPEG_DPG;
r = jpeg_v4_0_5_stop(adev);
if (!r)
r = jpeg_v4_0_5_start(adev);
adev->pg_flags = pg_flags;
if (r)
return r;
return amdgpu_ring_reset_helper_end(ring, timedout_fence);
}

View File

@@ -648,15 +648,28 @@ static int jpeg_v5_0_0_ring_reset(struct amdgpu_ring *ring,
unsigned int vmid,
struct amdgpu_fence *timedout_fence)
{
struct amdgpu_device *adev = ring->adev;
u32 pg_flags = adev->pg_flags;
int r;
amdgpu_ring_reset_helper_begin(ring, timedout_fence);
r = jpeg_v5_0_0_stop(ring->adev);
if (r)
return r;
r = jpeg_v5_0_0_start(ring->adev);
/*
* The DPG stop path only clears the JPEG_PG_MODE bit and never resets a
* hung JRBC, so the post-reset ring test times out and the driver falls
* back to a full MODE1 reset. Temporarily force the static power-gating
* path so the stop/start sequence actually power-cycles the JPEG block
* (JMI soft reset + ONO1 power off/on), matching the working jpeg_v4_0
* reset.
*/
adev->pg_flags &= ~AMD_PG_SUPPORT_JPEG_DPG;
r = jpeg_v5_0_0_stop(adev);
if (!r)
r = jpeg_v5_0_0_start(adev);
adev->pg_flags = pg_flags;
if (r)
return r;
return amdgpu_ring_reset_helper_end(ring, timedout_fence);
}

View File

@@ -637,15 +637,28 @@ static int jpeg_v5_3_0_ring_reset(struct amdgpu_ring *ring,
unsigned int vmid,
struct amdgpu_fence *timedout_fence)
{
struct amdgpu_device *adev = ring->adev;
u32 pg_flags = adev->pg_flags;
int r;
amdgpu_ring_reset_helper_begin(ring, timedout_fence);
r = jpeg_v5_3_0_stop(ring->adev);
if (r)
return r;
r = jpeg_v5_3_0_start(ring->adev);
/*
* The DPG stop path only clears the JPEG_PG_MODE bit and never resets a
* hung JRBC, so the post-reset ring test times out and the driver falls
* back to a full MODE1 reset. Temporarily force the static power-gating
* path so the stop/start sequence actually power-cycles the JPEG block
* (JMI soft reset + static power off/on), matching the working jpeg_v4_0
* reset.
*/
adev->pg_flags &= ~AMD_PG_SUPPORT_JPEG_DPG;
r = jpeg_v5_3_0_stop(adev);
if (!r)
r = jpeg_v5_3_0_start(adev);
adev->pg_flags = pg_flags;
if (r)
return r;
return amdgpu_ring_reset_helper_end(ring, timedout_fence);
}

View File

@@ -26,6 +26,7 @@
#include "amdgpu_gfx.h"
#include "mes_userqueue.h"
#include "amdgpu_userq_fence.h"
#include "amdgpu_trace.h"
#define AMDGPU_USERQ_PROC_CTX_SZ PAGE_SIZE
#define AMDGPU_USERQ_GANG_CTX_SZ PAGE_SIZE
@@ -143,10 +144,23 @@ static int mes_userq_map(struct amdgpu_usermode_queue *queue)
queue_input.doorbell_offset = userq_props->doorbell_index;
queue_input.page_table_base_addr = amdgpu_gmc_pd_addr(queue->vm->root.bo);
queue_input.wptr_mc_addr = queue->wptr_obj.gpu_addr;
if (mes->use_rs64mem) {
amdgpu_mes_alloc_proc_ctx_index(mes, queue);
queue_input.process_context_array_index = queue->proc_ctx_array_index;
amdgpu_mes_alloc_gang_ctx_index(mes, queue);
if (!uq_mgr->proc_ctx_allocated) {
r = amdgpu_mes_alloc_proc_ctx_index(mes, &uq_mgr->proc_ctx_array_index);
if (r) {
DRM_ERROR("Failed to allocate userq process index err:%d\n", r);
return r;
}
uq_mgr->proc_ctx_allocated = true;
}
r = amdgpu_mes_alloc_gang_ctx_index(mes, &queue->gang_ctx_array_index);
if (r) {
DRM_ERROR("Failed to allocate userq gang index err:%d\n", r);
return r;
}
queue_input.process_context_array_index = uq_mgr->proc_ctx_array_index;
queue_input.gang_context_array_index = queue->gang_ctx_array_index;
}
amdgpu_mes_lock(&adev->mes);
@@ -179,10 +193,8 @@ static int mes_userq_unmap(struct amdgpu_usermode_queue *queue)
amdgpu_mes_lock(&adev->mes);
r = adev->mes.funcs->remove_hw_queue(&adev->mes, &queue_input);
amdgpu_mes_unlock(&adev->mes);
if (mes->use_rs64mem) {
amdgpu_mes_free_proc_ctx_index(mes, queue);
amdgpu_mes_free_gang_ctx_index(mes, queue);
}
if (mes->use_rs64mem)
amdgpu_mes_free_gang_ctx_index(mes, queue->gang_ctx_array_index);
if (r)
DRM_ERROR("Failed to unmap queue in HW, err (%d)\n", r);
return r;
@@ -205,7 +217,16 @@ int mes_userq_reset(struct amdgpu_usermode_queue *queue)
amdgpu_mes_unlock(&adev->mes);
if (r)
return r;
return mes_userq_unmap(queue);
/* mes_userq_unmap() does not update queue->state; mark it UNMAPPED so the
* destroy path does not issue a second REMOVE_QUEUE for the removed queue.
*/
r = mes_userq_unmap(queue);
if (!r) {
trace_amdgpu_userq_state_changed(queue, AMDGPU_USERQ_STATE_UNMAPPED);
queue->state = AMDGPU_USERQ_STATE_UNMAPPED;
}
return r;
}
int mes_userq_reset_queue(struct amdgpu_device *adev,

View File

@@ -1017,6 +1017,8 @@ static int mes_v11_0_set_hw_resources(struct amdgpu_mes *mes)
mes_set_hw_res_pkt.enable_reg_active_poll = 1;
mes_set_hw_res_pkt.enable_level_process_quantum_check = 1;
mes_set_hw_res_pkt.oversubscription_timer = 50;
if (adev->mes.use_rs64mem)
mes_set_hw_res_pkt.use_rs64mem_for_proc_gang_ctx = 1;
if (amdgpu_mes_log_enable) {
mes_set_hw_res_pkt.enable_mes_event_int_logging = 1;
@@ -1952,6 +1954,8 @@ static int mes_v11_0_hw_init(struct amdgpu_ip_block *ip_block)
if (adev->mes.ring[0].sched.ready)
goto out;
adev->mes.use_rs64mem = true;
if (!adev->enable_mes_kiq) {
if (adev->firmware.load_type == AMDGPU_FW_LOAD_DIRECT) {
r = mes_v11_0_load_microcode(adev,
@@ -1971,11 +1975,12 @@ static int mes_v11_0_hw_init(struct amdgpu_ip_block *ip_block)
/* Allocate GPU buffer for array size query results */
r = amdgpu_mes_rs64mem_init(&adev->mes);
if (r)
if (r) {
dev_warn(adev->dev,
"RS64 local memory init failed (%d),"
"falling back to system memory path\n", r);
adev->mes.use_rs64mem = false;
}
r = mes_v11_0_set_hw_resources(&adev->mes);
if (r)
@@ -1992,6 +1997,7 @@ static int mes_v11_0_hw_init(struct amdgpu_ip_block *ip_block)
"Failed to query ctx array sizes (%d),"
"disabling RS64 local memory\n", r);
/* Continue without optimization - not fatal */
adev->mes.use_rs64mem = false;
}
}
@@ -2033,10 +2039,6 @@ static int mes_v11_0_hw_init(struct amdgpu_ip_block *ip_block)
static int mes_v11_0_hw_fini(struct amdgpu_ip_block *ip_block)
{
struct amdgpu_device *adev = ip_block->adev;
if (adev->mes.use_rs64mem)
amdgpu_mes_rs64mem_fini(&adev->mes);
return 0;
}

View File

@@ -993,10 +993,16 @@ static int mes_v12_0_set_hw_resources(struct amdgpu_mes *mes, int pipe)
/*
* Keep oversubscribe timer for sdma . When we have unmapped doorbell
* handling support, other queue will not use the oversubscribe timer.
* handling mode - 0: disabled; 1: basic version; 2: basic+ version
* handling mode - 0: disabled; 1: basic; 2: basic+; 3: basic++
*
* Use basic+ (2): on an unmapped-queue doorbell ring MES reads the
* per-queue CP_UNMAPPED_QUEUE bitmap and clears the ringing queue's
* work_done so it is rescheduled. basic (1) only sets a coarse
* level-wide ready flag without clearing work_done, so a queue that
* rings after work_done was set is skipped and its work is lost.
*/
mes_set_hw_res_pkt.oversubscription_timer = mes_rev < 0x8b ? 0 : 50;
mes_set_hw_res_pkt.unmapped_doorbell_handling = 1;
mes_set_hw_res_pkt.unmapped_doorbell_handling = 2;
if (amdgpu_mes_log_enable) {
mes_set_hw_res_pkt.enable_mes_event_int_logging = 1;

View File

@@ -226,8 +226,10 @@ static int add_queue_mes(struct device_queue_manager *dqm, struct queue *q,
/* MES unit for quantum is 100ns */
queue_input.process_quantum = KFD_MES_PROCESS_QUANTUM; /* Equivalent to 10ms. */
queue_input.process_context_addr = pdd->proc_ctx_gpu_addr;
queue_input.process_context_array_index = pdd->proc_ctx_array_index;
queue_input.gang_quantum = KFD_MES_GANG_QUANTUM; /* Equivalent to 1ms */
queue_input.gang_context_addr = q->gang_ctx_gpu_addr;
queue_input.gang_context_array_index = q->gang_ctx_array_index;
queue_input.inprocess_gang_priority = q->properties.priority;
queue_input.gang_global_priority_level =
AMDGPU_MES_PRIORITY_LEVEL_NORMAL;
@@ -303,6 +305,7 @@ static int remove_queue_mes_on_reset_option(struct device_queue_manager *dqm, st
queue_input.queue_type = convert_to_amdgpu_ring_type(q->properties.type);
queue_input.remove_queue_after_reset = flush_mes_queue;
queue_input.xcc_id = ffs(dqm->dev->xcc_mask) - 1;
queue_input.gang_context_array_index = q->gang_ctx_array_index;
amdgpu_mes_lock(&adev->mes);
r = adev->mes.funcs->remove_hw_queue(&adev->mes, &queue_input);

View File

@@ -635,6 +635,7 @@ struct queue {
void *gang_ctx_bo;
uint64_t gang_ctx_gpu_addr;
void *gang_ctx_cpu_ptr;
uint32_t gang_ctx_array_index;
struct amdgpu_bo *wptr_bo_gart;
};
@@ -871,6 +872,8 @@ struct kfd_process_device {
uint64_t proc_ctx_gpu_addr;
void *proc_ctx_cpu_ptr;
uint32_t proc_ctx_array_index;
/* Tracks queue reset status */
bool has_reset_queue;
@@ -895,7 +898,7 @@ struct svm_range_list {
DECLARE_BITMAP(bitmap_supported, MAX_GPU_INSTANCE);
struct task_struct *faulting_task;
/* check point ts decides if page fault recovery need be dropped */
uint64_t checkpoint_ts[MAX_GPU_INSTANCE];
atomic64_t checkpoint_ts[MAX_GPU_INSTANCE];
/* Default granularity to use in buffer migration
* and restoration of backing memory while handling

View File

@@ -1215,9 +1215,12 @@ static void kfd_process_destroy_pdds(struct kfd_process *p)
kfd_free_process_doorbells(pdd->dev->kfd, pdd);
if (pdd->dev->kfd->shared_resources.enable_mes &&
pdd->proc_ctx_cpu_ptr)
pdd->proc_ctx_cpu_ptr) {
amdgpu_mes_free_proc_ctx_index(&pdd->dev->adev->mes,
pdd->proc_ctx_array_index);
amdgpu_amdkfd_free_kernel_mem(pdd->dev->adev,
&pdd->proc_ctx_bo);
}
/*
* before destroying pdd, make sure to report availability
* for auto suspend

View File

@@ -210,6 +210,7 @@ static void pqm_clean_queue_resource(struct process_queue_manager *pqm,
}
if (dev->kfd->shared_resources.enable_mes) {
amdgpu_mes_free_gang_ctx_index(&dev->adev->mes, pqn->q->gang_ctx_array_index);
amdgpu_amdkfd_free_kernel_mem(dev->adev, &pqn->q->gang_ctx_bo);
amdgpu_amdkfd_free_kernel_mem(dev->adev, (void **)&pqn->q->wptr_bo_gart);
}
@@ -282,7 +283,15 @@ static int init_user_queue(struct process_queue_manager *pqm,
goto cleanup;
}
memset((*q)->gang_ctx_cpu_ptr, 0, AMDGPU_MES_GANG_CTX_SIZE);
/* Bind one MES gang context slot per queue (gang). */
if (dev->adev->mes.use_rs64mem) {
retval = amdgpu_mes_alloc_gang_ctx_index(&dev->adev->mes,
&(*q)->gang_ctx_array_index);
if (retval) {
pr_err("failed to allocate gang context index slot\n");
goto cleanup;
}
}
/* Starting with GFX11, wptr BOs must be mapped to GART for MES to determine work
* on unmapped queues for usermode queue oversubscription (no aggregated doorbell)
*/
@@ -304,6 +313,7 @@ static int init_user_queue(struct process_queue_manager *pqm,
return 0;
free_gang_ctx_bo:
amdgpu_mes_free_gang_ctx_index(&dev->adev->mes, (*q)->gang_ctx_array_index);
amdgpu_amdkfd_free_kernel_mem(dev->adev, &(*q)->gang_ctx_bo);
cleanup:
uninit_queue(*q);
@@ -386,6 +396,17 @@ int pqm_create_queue(struct process_queue_manager *pqm,
goto err_allocate_pqn;
}
memset(pdd->proc_ctx_cpu_ptr, 0, AMDGPU_MES_PROC_CTX_SIZE);
/* Bind one MES process context slot to the whole process
* (per device); every queue of this process reuses it.
*/
if (dev->adev->mes.use_rs64mem) {
retval = amdgpu_mes_alloc_proc_ctx_index(&dev->adev->mes,
&pdd->proc_ctx_array_index);
if (retval) {
dev_err(dev->adev->dev, "failed to allocate process context index\n");
goto err_allocate_pqn;
}
}
}
pqn = kzalloc_obj(*pqn);

View File

@@ -102,7 +102,7 @@ static int kfd_queue_buffer_svm_get(struct kfd_process_device *pdd, u64 addr, u6
mutex_lock(&p->svms.lock);
/*
* range may split to multiple svm pranges aligned to granularity boundaery.
* range may split to multiple svm pranges aligned to granularity boundary.
*/
while (size) {
uint32_t gpuid, gpuidx;
@@ -112,12 +112,11 @@ static int kfd_queue_buffer_svm_get(struct kfd_process_device *pdd, u64 addr, u6
if (!prange)
break;
if (!prange->mapped_to_gpu)
break;
r = kfd_process_gpuid_from_node(p, pdd->dev, &gpuid, &gpuidx);
if (r < 0)
break;
if (!test_bit(gpuidx, prange->bitmap_mapped))
break;
if (!test_bit(gpuidx, prange->bitmap_access) &&
!test_bit(gpuidx, prange->bitmap_aip))
break;

View File

@@ -748,6 +748,48 @@ svm_range_check_attr(struct kfd_process *p,
return 0;
}
static void svm_range_update_checkpoint_timestamp(struct kfd_process *p)
{
struct svm_range_list *svms;
int i;
svms = &p->svms;
/* calculate time stamps that are used to decide which page faults need be
* dropped or handled before unmap pages from gpu vm
*/
for_each_set_bit(i, svms->bitmap_supported, p->n_pdds) {
struct kfd_process_device *pdd;
struct amdgpu_device *adev;
struct amdgpu_ih_ring *ih;
uint32_t checkpoint_wptr;
pdd = p->pdds[i];
if (!pdd)
continue;
adev = pdd->dev->adev;
/* Check and drain ih1 ring if cam not available */
if (!adev->irq.retry_cam_enabled && adev->irq.ih1.ring_size) {
ih = &adev->irq.ih1;
checkpoint_wptr = amdgpu_ih_get_wptr(adev, ih);
if (ih->rptr != checkpoint_wptr) {
atomic64_set(&svms->checkpoint_ts[i],
amdgpu_ih_decode_iv_ts(adev, ih, checkpoint_wptr, -1));
continue;
}
}
/* check if dev->irq.ih_soft is not empty */
ih = &adev->irq.ih_soft;
checkpoint_wptr = amdgpu_ih_get_wptr(adev, ih);
if (ih->rptr != checkpoint_wptr)
atomic64_set(&svms->checkpoint_ts[i],
amdgpu_ih_decode_iv_ts(adev, ih, checkpoint_wptr, -1));
}
}
static void
svm_range_apply_attrs(struct kfd_process *p, struct svm_range *prange,
uint32_t nattr, struct kfd_ioctl_svm_attribute *attrs,
@@ -775,6 +817,8 @@ svm_range_apply_attrs(struct kfd_process *p, struct svm_range *prange,
if (attrs[i].type == KFD_IOCTL_SVM_ATTR_NO_ACCESS) {
bitmap_clear(prange->bitmap_access, gpuidx, 1);
bitmap_clear(prange->bitmap_aip, gpuidx, 1);
if (test_bit(gpuidx, prange->bitmap_mapped))
bitmap_set(prange->bitmap_needs_unmap, gpuidx, 1);
} else if (attrs[i].type == KFD_IOCTL_SVM_ATTR_ACCESS) {
bitmap_set(prange->bitmap_access, gpuidx, 1);
bitmap_clear(prange->bitmap_aip, gpuidx, 1);
@@ -1065,9 +1109,10 @@ svm_range_split_adjust(struct svm_range *new, struct svm_range *old,
new->prefetch_loc = old->prefetch_loc;
new->actual_loc = old->actual_loc;
new->granularity = old->granularity;
new->mapped_to_gpu = old->mapped_to_gpu;
new->mapping_done = old->mapping_done;
bitmap_copy(new->bitmap_access, old->bitmap_access, MAX_GPU_INSTANCE);
bitmap_copy(new->bitmap_aip, old->bitmap_aip, MAX_GPU_INSTANCE);
bitmap_copy(new->bitmap_mapped, old->bitmap_mapped, MAX_GPU_INSTANCE);
atomic_set(&new->queue_refcount, atomic_read(&old->queue_refcount));
return 0;
@@ -1368,7 +1413,8 @@ svm_range_unmap_from_gpu(struct amdgpu_device *adev, struct amdgpu_vm *vm,
static int
svm_range_unmap_from_gpus(struct svm_range *prange, unsigned long start,
unsigned long last, uint32_t trigger)
unsigned long last, unsigned long *bitmap_unmap,
uint32_t trigger)
{
struct kfd_process_device *pdd;
struct dma_fence *fence = NULL;
@@ -1376,21 +1422,15 @@ svm_range_unmap_from_gpus(struct svm_range *prange, unsigned long start,
uint32_t gpuidx;
int r = 0;
if (!prange->mapped_to_gpu) {
pr_debug("prange 0x%p [0x%lx 0x%lx] not mapped to GPU\n",
prange, prange->start, prange->last);
return 0;
}
if (prange->start == start && prange->last == last) {
pr_debug("unmap svms 0x%p prange 0x%p\n", prange->svms, prange);
prange->mapped_to_gpu = false;
}
p = container_of(prange->svms, struct kfd_process, svms);
for_each_or_bit(gpuidx, prange->bitmap_access, prange->bitmap_aip, MAX_GPU_INSTANCE) {
pr_debug("unmap from gpu idx 0x%x\n", gpuidx);
for_each_set_bit(gpuidx, bitmap_unmap, MAX_GPU_INSTANCE) {
if (prange->start == start && prange->last == last) {
pr_debug("unmap svms 0x%p prange 0x%p from gpu_idx 0x%x\n",
prange->svms, prange, gpuidx);
clear_bit(gpuidx, prange->bitmap_mapped);
}
pdd = kfd_process_device_from_gpuidx(p, gpuidx);
if (!pdd) {
pr_debug("failed to find device idx %d\n", gpuidx);
@@ -1543,6 +1583,8 @@ svm_range_map_to_gpus(struct svm_range *prange, unsigned long offset,
continue;
}
set_bit(gpuidx, prange->bitmap_mapped);
r = svm_range_map_to_gpu(pdd, prange, offset, npages, readonly,
prange->dma_addr[gpuidx],
bo_adev, wait ? &fence : NULL,
@@ -1688,7 +1730,9 @@ static int svm_range_validate_and_map(struct mm_struct *mm,
bitmap_zero(ctx->bitmap, MAX_GPU_INSTANCE);
bitmap_set(ctx->bitmap, gpuidx, 1);
} else if (ctx->process->xnack_enabled) {
bitmap_copy(ctx->bitmap, prange->bitmap_aip, MAX_GPU_INSTANCE);
/* Update mapping on already mapped or access in place GPU */
bitmap_or(ctx->bitmap, prange->bitmap_mapped, prange->bitmap_aip,
MAX_GPU_INSTANCE);
/* If prefetch range to GPU, or GPU retry fault migrate range to
* GPU, which has ACCESS attribute to the range, create mapping
@@ -1708,14 +1752,12 @@ static int svm_range_validate_and_map(struct mm_struct *mm,
}
/*
* If prange is already mapped or with always mapped flag,
* update mapping on GPUs with ACCESS attribute
* If prange with always mapped flag, update mapping on GPUs with
* ACCESS attribute
*/
if (bitmap_empty(ctx->bitmap, MAX_GPU_INSTANCE)) {
if (prange->mapped_to_gpu ||
prange->flags & KFD_IOCTL_SVM_FLAG_GPU_ALWAYS_MAPPED)
bitmap_copy(ctx->bitmap, prange->bitmap_access, MAX_GPU_INSTANCE);
}
if (prange->flags & KFD_IOCTL_SVM_FLAG_GPU_ALWAYS_MAPPED)
bitmap_or(ctx->bitmap, ctx->bitmap, prange->bitmap_access,
MAX_GPU_INSTANCE);
} else {
bitmap_or(ctx->bitmap, prange->bitmap_access,
prange->bitmap_aip, MAX_GPU_INSTANCE);
@@ -1781,6 +1823,7 @@ static int svm_range_validate_and_map(struct mm_struct *mm,
e = min(end, prange->last);
if (e >= s)
r = svm_range_unmap_from_gpus(prange, s, e,
prange->bitmap_mapped,
KFD_SVM_UNMAP_TRIGGER_UNMAP_FROM_CPU);
svm_range_unlock(prange);
/* If unmap returns non-zero, we'll bail on the next for loop
@@ -1843,7 +1886,9 @@ static int svm_range_validate_and_map(struct mm_struct *mm,
}
if (!r && next == end)
prange->mapped_to_gpu = true;
prange->mapping_done = true;
else
prange->mapping_done = false;
svm_range_unlock(prange);
@@ -2013,10 +2058,10 @@ svm_range_evict(struct svm_range *prange, struct mm_struct *mm,
if (!p->xnack_enabled ||
(prange->flags & KFD_IOCTL_SVM_FLAG_GPU_ALWAYS_MAPPED)) {
int evicted_ranges;
bool mapped = prange->mapped_to_gpu;
bool mapped = !bitmap_empty(prange->bitmap_mapped, MAX_GPU_INSTANCE);
list_for_each_entry(pchild, &prange->child_list, child_list) {
if (!pchild->mapped_to_gpu)
if (bitmap_empty(pchild->bitmap_mapped, MAX_GPU_INSTANCE))
continue;
mapped = true;
mutex_lock_nested(&pchild->lock, 1);
@@ -2065,13 +2110,14 @@ svm_range_evict(struct svm_range *prange, struct mm_struct *mm,
s = max(start, pchild->start);
l = min(last, pchild->last);
if (l >= s)
svm_range_unmap_from_gpus(pchild, s, l, trigger);
svm_range_unmap_from_gpus(pchild, s, l, prange->bitmap_mapped,
trigger);
mutex_unlock(&pchild->lock);
}
s = max(start, prange->start);
l = min(last, prange->last);
if (l >= s)
svm_range_unmap_from_gpus(prange, s, l, trigger);
svm_range_unmap_from_gpus(prange, s, l, prange->bitmap_mapped, trigger);
}
return r;
@@ -2101,10 +2147,11 @@ static struct svm_range *svm_range_clone(struct svm_range *old)
new->prefetch_loc = old->prefetch_loc;
new->actual_loc = old->actual_loc;
new->granularity = old->granularity;
new->mapped_to_gpu = old->mapped_to_gpu;
new->mapping_done = old->mapping_done;
new->vram_pages = old->vram_pages;
bitmap_copy(new->bitmap_access, old->bitmap_access, MAX_GPU_INSTANCE);
bitmap_copy(new->bitmap_aip, old->bitmap_aip, MAX_GPU_INSTANCE);
bitmap_copy(new->bitmap_mapped, old->bitmap_mapped, MAX_GPU_INSTANCE);
atomic_set(&new->queue_refcount, atomic_read(&old->queue_refcount));
return new;
@@ -2224,7 +2271,7 @@ svm_range_add(struct kfd_process *p, uint64_t start, uint64_t size,
next_start = min(node->last, last) + 1;
if (svm_range_is_same_attrs(p, prange, nattr, attrs) &&
prange->mapped_to_gpu) {
prange->mapping_done) {
/* nothing to do */
} else if (node->start < start || node->last > last) {
/* node intersects the update range and its attributes
@@ -2545,7 +2592,6 @@ svm_range_unmap_from_cpu(struct mm_struct *mm, struct svm_range *prange,
struct kfd_process *p;
unsigned long s, l;
bool unmap_parent;
uint32_t i;
if (atomic_read(&prange->queue_refcount)) {
int r;
@@ -2565,38 +2611,7 @@ svm_range_unmap_from_cpu(struct mm_struct *mm, struct svm_range *prange,
pr_debug("svms 0x%p prange 0x%p [0x%lx 0x%lx] [0x%lx 0x%lx]\n", svms,
prange, prange->start, prange->last, start, last);
/* calculate time stamps that are used to decide which page faults need be
* dropped or handled before unmap pages from gpu vm
*/
for_each_set_bit(i, svms->bitmap_supported, p->n_pdds) {
struct kfd_process_device *pdd;
struct amdgpu_device *adev;
struct amdgpu_ih_ring *ih;
uint32_t checkpoint_wptr;
pdd = p->pdds[i];
if (!pdd)
continue;
adev = pdd->dev->adev;
/* Check and drain ih1 ring if cam not available */
if (!adev->irq.retry_cam_enabled && adev->irq.ih1.ring_size) {
ih = &adev->irq.ih1;
checkpoint_wptr = amdgpu_ih_get_wptr(adev, ih);
if (ih->rptr != checkpoint_wptr) {
svms->checkpoint_ts[i] =
amdgpu_ih_decode_iv_ts(adev, ih, checkpoint_wptr, -1);
continue;
}
}
/* check if dev->irq.ih_soft is not empty */
ih = &adev->irq.ih_soft;
checkpoint_wptr = amdgpu_ih_get_wptr(adev, ih);
if (ih->rptr != checkpoint_wptr)
svms->checkpoint_ts[i] = amdgpu_ih_decode_iv_ts(adev, ih, checkpoint_wptr, -1);
}
svm_range_update_checkpoint_timestamp(p);
unmap_parent = start <= prange->start && last >= prange->last;
@@ -2605,14 +2620,14 @@ svm_range_unmap_from_cpu(struct mm_struct *mm, struct svm_range *prange,
s = max(start, pchild->start);
l = min(last, pchild->last);
if (l >= s)
svm_range_unmap_from_gpus(pchild, s, l, trigger);
svm_range_unmap_from_gpus(pchild, s, l, prange->bitmap_mapped, trigger);
svm_range_unmap_split(prange, pchild, start, last);
mutex_unlock(&pchild->lock);
}
s = max(start, prange->start);
l = min(last, prange->last);
if (l >= s)
svm_range_unmap_from_gpus(prange, s, l, trigger);
svm_range_unmap_from_gpus(prange, s, l, prange->bitmap_mapped, trigger);
svm_range_unmap_split(prange, prange, start, last);
if (unmap_parent)
@@ -3043,6 +3058,7 @@ svm_range_restore_pages(struct amdgpu_device *adev, unsigned int pasid,
struct svm_range *prange;
struct kfd_process *p;
ktime_t timestamp = ktime_get_boottime();
uint64_t checkpoint_ts;
struct kfd_node *node;
int32_t best_loc;
int32_t gpuid, gpuidx = MAX_GPU_INSTANCE;
@@ -3105,9 +3121,11 @@ svm_range_restore_pages(struct amdgpu_device *adev, unsigned int pasid,
retry_write_locked:
mutex_lock(&svms->lock);
checkpoint_ts = atomic64_read(&svms->checkpoint_ts[gpuidx]);
/* check if this page fault time stamp is before svms->checkpoint_ts */
if (svms->checkpoint_ts[gpuidx] != 0) {
if (amdgpu_ih_ts_after_or_equal(ts, svms->checkpoint_ts[gpuidx])) {
if (checkpoint_ts) {
if (amdgpu_ih_ts_after_or_equal(ts, checkpoint_ts)) {
pr_debug("draining retry fault, drop fault 0x%llx\n", addr);
if (write_locked)
mmap_write_downgrade(mm);
@@ -3117,7 +3135,7 @@ svm_range_restore_pages(struct amdgpu_device *adev, unsigned int pasid,
/* ts is after svms->checkpoint_ts now, reset svms->checkpoint_ts
* to zero to avoid following ts wrap around give wrong comparing
*/
svms->checkpoint_ts[gpuidx] = 0;
atomic64_set(&svms->checkpoint_ts[gpuidx], 0);
}
}
@@ -3711,6 +3729,25 @@ int svm_range_evict_svm_bo(struct amdgpu_bo *bo)
return r;
}
static bool svm_range_needs_unmap(struct kfd_process *p, struct svm_range *prange)
{
if (bitmap_empty(prange->bitmap_needs_unmap, MAX_GPU_INSTANCE))
return false;
pr_debug("prange 0x%p no access set for [0x%lx 0x%lx]\n",
prange, prange->start, prange->last);
svm_range_update_checkpoint_timestamp(p);
svm_range_unmap_from_gpus(prange, prange->start,
prange->last, prange->bitmap_needs_unmap,
KFD_SVM_UNMAP_TRIGGER_UNMAP_FROM_CPU);
bitmap_clear(prange->bitmap_needs_unmap, 0, MAX_GPU_INSTANCE);
return bitmap_empty(prange->bitmap_mapped, MAX_GPU_INSTANCE);
}
static int
svm_range_set_attr(struct kfd_process *p, struct mm_struct *mm,
uint64_t start, uint64_t size, uint32_t nattr,
@@ -3766,10 +3803,10 @@ svm_range_set_attr(struct kfd_process *p, struct mm_struct *mm,
svm_range_add_to_svms(prange);
svm_range_add_notifier_locked(mm, prange);
}
list_for_each_entry(prange, &update_list, update_list) {
list_for_each_entry(prange, &update_list, update_list)
svm_range_apply_attrs(p, prange, nattr, attrs, &update_mapping);
/* TODO: unmap ranges from GPU that lost access */
}
update_mapping |= !p->xnack_enabled && !list_empty(&remap_list);
list_for_each_entry_safe(prange, next, &remove_list, update_list) {
@@ -3790,6 +3827,9 @@ svm_range_set_attr(struct kfd_process *p, struct mm_struct *mm,
list_for_each_entry(prange, &update_list, update_list) {
bool migrated;
if (svm_range_needs_unmap(p, prange))
continue;
mutex_lock(&prange->migrate_mutex);
r = svm_range_trigger_migration(mm, prange, &migrated);
@@ -3798,7 +3838,7 @@ svm_range_set_attr(struct kfd_process *p, struct mm_struct *mm,
if (migrated && (!p->xnack_enabled ||
(prange->flags & KFD_IOCTL_SVM_FLAG_GPU_ALWAYS_MAPPED)) &&
prange->mapped_to_gpu) {
!bitmap_empty(prange->bitmap_mapped, MAX_GPU_INSTANCE)) {
pr_debug("restore_work will update mappings of GPUs\n");
mutex_unlock(&prange->migrate_mutex);
continue;
@@ -3809,7 +3849,8 @@ svm_range_set_attr(struct kfd_process *p, struct mm_struct *mm,
continue;
}
flush_tlb = !migrated && update_mapping && prange->mapped_to_gpu;
flush_tlb = !migrated && update_mapping &&
!bitmap_empty(prange->bitmap_mapped, MAX_GPU_INSTANCE);
r = svm_range_validate_and_map(mm, prange->start, prange->last, prange,
MAX_GPU_INSTANCE, true, true, flush_tlb);
@@ -3823,11 +3864,13 @@ svm_range_set_attr(struct kfd_process *p, struct mm_struct *mm,
}
list_for_each_entry(prange, &remap_list, update_list) {
flush_tlb = !bitmap_empty(prange->bitmap_mapped, MAX_GPU_INSTANCE);
pr_debug("Remapping prange 0x%p [0x%lx 0x%lx]\n",
prange, prange->start, prange->last);
mutex_lock(&prange->migrate_mutex);
r = svm_range_validate_and_map(mm, prange->start, prange->last, prange,
MAX_GPU_INSTANCE, true, true, prange->mapped_to_gpu);
MAX_GPU_INSTANCE, true, true, flush_tlb);
if (r)
pr_debug("failed %d on remap svm range\n", r);
mutex_unlock(&prange->migrate_mutex);

View File

@@ -99,6 +99,9 @@ struct svm_work_list_item {
* @child_list: list header for split ranges which are not added to svms yet
* @bitmap_access: index bitmap of GPUs which can access the range
* @bitmap_aip: index bitmap of GPUs which can access the range in place
* @bitmap_needs_unmap: index bitmap of GPUs which currently set NO_ACCESS
* @bitmap_mapped: index bitmap of GPUs which currently have the range mapped
* @mapping_done: true if range_validate_and_map complete successfully
*
* Data structure for virtual memory range shared by CPU and GPUs, it can be
* allocated from system memory ram or device vram, and migrate from ram to vram
@@ -134,7 +137,9 @@ struct svm_range {
struct list_head child_list;
DECLARE_BITMAP(bitmap_access, MAX_GPU_INSTANCE);
DECLARE_BITMAP(bitmap_aip, MAX_GPU_INSTANCE);
bool mapped_to_gpu;
DECLARE_BITMAP(bitmap_needs_unmap, MAX_GPU_INSTANCE);
DECLARE_BITMAP(bitmap_mapped, MAX_GPU_INSTANCE);
bool mapping_done;
atomic_t queue_refcount;
};

View File

@@ -607,6 +607,11 @@ static int amdgpu_dm_init(struct amdgpu_device *adev)
init_data.flags.unify_link_enc_assignment = true;
init_data.flags.usb4_bw_alloc_support = true;
}
/* DCN201 audio desyncs using DP SS */
if (adev->apu_flags & AMD_APU_IS_CYAN_SKILLFISH2)
init_data.flags.ignore_dpref_ss = true;
retrieve_dmi_info(&adev->dm);
if (adev->dm.edp0_on_dp1_quirk)
init_data.flags.support_edp0_on_dp1 = true;
@@ -1011,6 +1016,7 @@ static int load_dmcu_fw(struct amdgpu_device *adev)
case IP_VERSION(4, 0, 1):
case IP_VERSION(4, 2, 0):
case IP_VERSION(4, 2, 1):
case IP_VERSION(6, 0, 0):
return 0;
default:
break;
@@ -2410,6 +2416,7 @@ static int amdgpu_dm_initialize_drm_device(struct amdgpu_device *adev)
case IP_VERSION(4, 0, 1):
case IP_VERSION(4, 2, 0):
case IP_VERSION(4, 2, 1):
case IP_VERSION(6, 0, 0):
if (amdgpu_dm_register_outbox_irq_handlers(dm->adev)) {
drm_err(adev_to_drm(adev), "DM: Failed to initialize IRQ\n");
goto fail;
@@ -2436,6 +2443,7 @@ static int amdgpu_dm_initialize_drm_device(struct amdgpu_device *adev)
case IP_VERSION(4, 0, 1):
case IP_VERSION(4, 2, 0):
case IP_VERSION(4, 2, 1):
case IP_VERSION(6, 0, 0):
psr_feature_enabled = true;
break;
default:
@@ -2455,6 +2463,7 @@ static int amdgpu_dm_initialize_drm_device(struct amdgpu_device *adev)
case IP_VERSION(3, 6, 0):
case IP_VERSION(4, 2, 0):
case IP_VERSION(4, 2, 1):
case IP_VERSION(6, 0, 0):
replay_feature_enabled = true;
break;
@@ -2617,6 +2626,7 @@ static int amdgpu_dm_initialize_drm_device(struct amdgpu_device *adev)
case IP_VERSION(4, 0, 1):
case IP_VERSION(4, 2, 0):
case IP_VERSION(4, 2, 1):
case IP_VERSION(6, 0, 0):
if (amdgpu_dm_dcn10_register_irq_handlers(dm->adev)) {
drm_err(adev_to_drm(adev), "DM: Failed to initialize IRQ\n");
goto fail;
@@ -2829,6 +2839,7 @@ static int dm_early_init(struct amdgpu_ip_block *ip_block)
case IP_VERSION(4, 0, 1):
case IP_VERSION(4, 2, 0):
case IP_VERSION(4, 2, 1):
case IP_VERSION(6, 0, 0):
adev->mode_info.num_crtc = 4;
adev->mode_info.num_hpd = 4;
adev->mode_info.num_dig = 4;
@@ -4655,21 +4666,19 @@ static void dm_set_writeback(struct amdgpu_display_manager *dm,
wb_info = kzalloc_obj(*wb_info);
if (!wb_info) {
drm_err(adev_to_drm(adev), "Failed to allocate wb_info\n");
return;
goto cleanup;
}
acrtc = to_amdgpu_crtc(wb_conn->encoder.crtc);
if (!acrtc) {
drm_err(adev_to_drm(adev), "no amdgpu_crtc found\n");
kfree(wb_info);
return;
goto cleanup;
}
afb = to_amdgpu_framebuffer(new_con_state->writeback_job->fb);
if (!afb) {
drm_err(adev_to_drm(adev), "No amdgpu_framebuffer found\n");
kfree(wb_info);
return;
goto cleanup;
}
for (i = 0; i < MAX_PIPES; i++) {
@@ -4679,6 +4688,11 @@ static void dm_set_writeback(struct amdgpu_display_manager *dm,
}
}
if (!pipe) {
drm_err(adev_to_drm(adev), "No pipe found for stream\n");
goto cleanup;
}
/* fill in wb_info */
wb_info->wb_enabled = true;
@@ -4752,6 +4766,9 @@ static void dm_set_writeback(struct amdgpu_display_manager *dm,
WARN_ON(drm_crtc_vblank_get(&acrtc->base));
acrtc->wb_frame_done = false;
acrtc->wb_pending = true;
cleanup:
kfree(wb_info);
}
static void amdgpu_dm_update_hdcp(struct drm_atomic_commit *state)
@@ -6330,12 +6347,9 @@ static int amdgpu_dm_atomic_check(struct drm_device *dev,
if (dm_new_crtc_state->cursor_mode == DM_CURSOR_OVERLAY_MODE)
continue;
/* Check if rotation or scaling is enabled on DCN401 */
if ((drm_plane_mask(crtc->cursor) &
new_crtc_state->plane_mask) &&
(amdgpu_ip_version(adev, DCE_HWIP, 0) == IP_VERSION(4, 2, 1) ||
amdgpu_ip_version(adev, DCE_HWIP, 0) == IP_VERSION(4, 2, 0) ||
amdgpu_ip_version(adev, DCE_HWIP, 0) == IP_VERSION(4, 0, 1))) {
/* Check if rotation or scaling is enabled on DCN 4x and above */
if ((drm_plane_mask(crtc->cursor) & new_crtc_state->plane_mask) &&
(amdgpu_ip_version(adev, DCE_HWIP, 0) >= IP_VERSION(4, 0, 1))) {
new_cursor_state = drm_atomic_get_new_plane_state(state, crtc->cursor);
is_rotated = new_cursor_state &&

View File

@@ -857,8 +857,6 @@ struct amdgpu_dm_connector {
struct mutex hpd_lock;
bool fake_enable;
bool force_yuv420_output;
bool force_yuv422_output;
uint8_t force_yuv_pixel_format;
struct dsc_preferred_settings dsc_settings;
struct psr_caps psr_caps;

View File

@@ -28,7 +28,7 @@
struct amdgpu_device;
struct drm_device;
struct drm_atomic_state;
struct drm_atomic_commit;
struct drm_connector;
struct audio_info;
struct dc_sink;

View File

@@ -147,7 +147,7 @@ STATIC_IFN_KUNIT int dm_encoder_helper_atomic_check(struct drm_encoder *encoder,
int max_bpc = conn_state->max_requested_bpc;
is_y420 = drm_mode_is_420_also(&connector->display_info, adjusted_mode) &&
aconnector->force_yuv420_output;
aconnector->force_yuv_pixel_format == PIXEL_ENCODING_YCBCR420;
color_depth = amdgpu_dm_convert_color_depth_from_display_info(connector,
is_y420,
max_bpc);
@@ -855,16 +855,15 @@ STATIC_IFN_KUNIT void fill_stream_properties_from_drm_display_mode(
const struct drm_connector *connector,
const struct drm_connector_state *connector_state,
const struct dc_stream_state *old_stream,
int requested_bpc)
int requested_bpc,
enum dc_pixel_encoding requested_encoding,
bool is_hdmi_ep)
{
bool is_dp_or_hdmi = dc_is_hdmi_signal(stream->signal) || dc_is_dp_signal(stream->signal);
struct dc_crtc_timing *timing_out = &stream->timing;
const struct drm_display_info *info = &connector->display_info;
struct amdgpu_dm_connector *aconnector = NULL;
struct hdmi_vendor_infoframe hv_frame;
struct hdmi_avi_infoframe avi_frame;
bool want_420;
bool want_422;
ssize_t err;
if (connector->connector_type != DRM_MODE_CONNECTOR_WRITEBACK)
@@ -878,40 +877,13 @@ STATIC_IFN_KUNIT void fill_stream_properties_from_drm_display_mode(
timing_out->v_border_top = 0;
timing_out->v_border_bottom = 0;
want_420 = (aconnector && aconnector->force_yuv_pixel_format == PIXEL_ENCODING_YCBCR420) ||
(connector_state->color_format == DRM_CONNECTOR_COLOR_FORMAT_YCBCR420);
want_422 = (aconnector && aconnector->force_yuv_pixel_format == PIXEL_ENCODING_YCBCR422) ||
(connector_state->color_format == DRM_CONNECTOR_COLOR_FORMAT_YCBCR422);
if (drm_mode_is_420_only(info, mode_in) &&
(want_420 || connector_state->color_format == DRM_CONNECTOR_COLOR_FORMAT_AUTO)) {
timing_out->pixel_encoding = PIXEL_ENCODING_YCBCR420;
} else if (drm_mode_is_420_also(info, mode_in) && want_420) {
timing_out->pixel_encoding = PIXEL_ENCODING_YCBCR420;
} else if ((info->color_formats & BIT(DRM_OUTPUT_COLOR_FORMAT_YCBCR422)) &&
want_422 && is_dp_or_hdmi) {
timing_out->pixel_encoding = PIXEL_ENCODING_YCBCR422;
} else if (connector_state->color_format == DRM_CONNECTOR_COLOR_FORMAT_YCBCR444 &&
(info->color_formats & BIT(DRM_OUTPUT_COLOR_FORMAT_YCBCR444)) &&
is_dp_or_hdmi) {
timing_out->pixel_encoding = PIXEL_ENCODING_YCBCR444;
} else if (connector_state->color_format == DRM_CONNECTOR_COLOR_FORMAT_RGB444 ||
connector_state->color_format == DRM_CONNECTOR_COLOR_FORMAT_AUTO) {
timing_out->pixel_encoding = PIXEL_ENCODING_RGB;
} else {
/*
* If a format was explicitly requested but the requested format
* can't be satisfied, set it to an invalid value so that an
* error bubbles up to userspace. This way, userspace knows it
* needs to make a better choice.
*/
if (connector_state->color_format != DRM_CONNECTOR_COLOR_FORMAT_AUTO)
timing_out->pixel_encoding = PIXEL_ENCODING_UNDEFINED;
else if (drm_mode_is_420_only(info, mode_in))
timing_out->pixel_encoding = PIXEL_ENCODING_YCBCR420;
else
timing_out->pixel_encoding = PIXEL_ENCODING_RGB;
}
/*
* The pixel encoding to use is decided entirely by the caller (see
* amdgpu_dm_create_validate_stream_for_sink()), which enumerates only
* the encodings the sink actually advertises. This helper must not
* second-guess that choice; it simply applies it.
*/
timing_out->pixel_encoding = requested_encoding;
timing_out->timing_3d_format = TIMING_3D_FORMAT_NONE;
timing_out->display_color_depth = amdgpu_dm_convert_color_depth_from_display_info(
@@ -977,14 +949,15 @@ STATIC_IFN_KUNIT void fill_stream_properties_from_drm_display_mode(
stream->out_transfer_func.type = TF_TYPE_PREDEFINED;
stream->out_transfer_func.tf = TRANSFER_FUNCTION_SRGB;
if (stream->signal == SIGNAL_TYPE_HDMI_TYPE_A) {
if (!adjust_colour_depth_from_display_info(timing_out, info) &&
drm_mode_is_420_also(info, mode_in) &&
timing_out->pixel_encoding != PIXEL_ENCODING_YCBCR420) {
timing_out->pixel_encoding = PIXEL_ENCODING_YCBCR420;
adjust_colour_depth_from_display_info(timing_out, info);
}
}
/* Clamp the HDMI colour depth to what the sink's max TMDS clock
* allows. The pixel encoding is fixed by the caller and must not be
* changed here: the caller already enumerates YCbCr420 as its own
* candidate, so silently switching to it would hide an enumeration
* bug and produce a stream the caller never asked to validate.
*/
if (is_hdmi_ep)
adjust_colour_depth_from_display_info(timing_out, info);
stream->output_color_space = amdgpu_dm_get_output_color_space(timing_out, connector_state);
stream->content_type = get_output_content_type(connector_state);
@@ -1451,7 +1424,9 @@ create_stream_for_sink(struct drm_connector *connector,
const struct drm_display_mode *drm_mode,
const struct dm_connector_state *dm_state,
const struct dc_stream_state *old_stream,
int requested_bpc)
int requested_bpc,
enum dc_pixel_encoding requested_encoding,
bool is_hdmi_ep)
{
struct drm_device *dev = connector->dev;
struct amdgpu_dm_connector *aconnector = NULL;
@@ -1562,11 +1537,11 @@ create_stream_for_sink(struct drm_connector *connector,
if (!scale || mode_refresh != preferred_refresh)
fill_stream_properties_from_drm_display_mode(
stream, &mode, connector, con_state, NULL,
requested_bpc);
requested_bpc, requested_encoding, is_hdmi_ep);
else
fill_stream_properties_from_drm_display_mode(
stream, &mode, connector, con_state, old_stream,
requested_bpc);
requested_bpc, requested_encoding, is_hdmi_ep);
/* The rest isn't needed for writeback connectors */
if (!aconnector)
@@ -2271,13 +2246,32 @@ amdgpu_dm_create_validate_stream_for_sink(struct drm_connector *connector,
const struct dm_connector_state *dm_state,
const struct dc_stream_state *old_stream)
{
/*
* Ordered lists of the encodings and bit depths we are willing to
* validate, best quality/bandwidth first. The per-sink masks built
* below gate which of these entries are actually attempted.
*/
static const enum dc_pixel_encoding encoding_order[] = {
PIXEL_ENCODING_YCBCR444,
PIXEL_ENCODING_RGB,
PIXEL_ENCODING_YCBCR422,
PIXEL_ENCODING_YCBCR420,
};
static const u8 bpc_order[] = { 16, 12, 10, 8, 6 };
struct amdgpu_dm_connector *aconnector = NULL;
struct amdgpu_device *adev = drm_to_adev(connector->dev);
struct dc_stream_state *stream;
const struct drm_display_info *info = &connector->display_info;
struct dc_stream_state *stream = NULL;
const struct drm_connector_state *drm_state = dm_state ? &dm_state->base : NULL;
int requested_bpc = drm_state ? drm_state->max_requested_bpc : 8;
enum dc_status dc_result = DC_OK;
uint8_t bpc_limit = 6;
enum signal_type signal = SIGNAL_TYPE_NONE;
bool want_420, want_422, is_dp_or_hdmi;
u32 encoding_mask = 0;
u32 bpc_mask = 0;
bool is_hdmi_ep = false;
unsigned int i, j;
if (!dm_state)
return NULL;
@@ -2285,90 +2279,181 @@ amdgpu_dm_create_validate_stream_for_sink(struct drm_connector *connector,
if (connector->connector_type != DRM_MODE_CONNECTOR_WRITEBACK)
aconnector = to_amdgpu_dm_connector(connector);
if (aconnector &&
(aconnector->dc_link->connector_signal == SIGNAL_TYPE_HDMI_TYPE_A ||
aconnector->dc_link->connector_signal == SIGNAL_TYPE_HDMI_FRL ||
aconnector->dc_link->dpcd_caps.dongle_type == DISPLAY_DONGLE_DP_HDMI_CONVERTER))
bpc_limit = 8;
do {
drm_dbg_kms(connector->dev, "Trying with %d bpc\n", requested_bpc);
stream = create_stream_for_sink(connector, drm_mode,
dm_state, old_stream,
requested_bpc);
if (stream == NULL) {
/*
* Writeback connectors have no sink EDID to enumerate against. They
* only ever use RGB at the requested depth, so build and validate a
* single stream directly and return it (dc_validate_stream() is not
* meaningful for the writeback path).
*/
if (!aconnector) {
stream = create_stream_for_sink(connector, drm_mode, dm_state,
old_stream, requested_bpc,
PIXEL_ENCODING_RGB, is_hdmi_ep);
if (!stream)
drm_err(adev_to_drm(adev), "Failed to create stream for sink!\n");
break;
}
return stream;
}
dc_result = dc_validate_stream(adev->dm.dc, stream);
signal = aconnector->dc_link->connector_signal;
if (!aconnector) /* writeback connector */
return stream;
/*
* Determine whether this is a native HDMI sink or a DP->HDMI dongle.
* Since we check for it a few times below, cache the result.
*/
is_hdmi_ep = (signal == SIGNAL_TYPE_HDMI_TYPE_A ||
signal == SIGNAL_TYPE_HDMI_FRL ||
aconnector->dc_link->dpcd_caps.dongle_type ==
DISPLAY_DONGLE_DP_HDMI_CONVERTER);
is_dp_or_hdmi = dc_is_hdmi_signal(signal) || dc_is_dp_signal(signal);
if (dc_result == DC_OK && stream->signal == SIGNAL_TYPE_DISPLAY_PORT_MST)
dc_result = dm_dp_mst_is_port_support_mode(aconnector, stream);
/*
* Build the set of pixel encodings this sink advertises, so we only
* ever validate combinations the display can actually accept. Ordered
* best-first: RGB / YCbCr444 (full bandwidth) -> YCbCr422 -> YCbCr420.
*
* - RGB is the mandatory baseline and always available.
* - YCbCr444 is only meaningful for native HDMI sinks.
* - A 420-only mode collapses the mask to YCbCr420 alone.
* - The debugfs force_yuv_pixel_format override pins the encoding to a
* single dc_pixel_encoding when set (PIXEL_ENCODING_UNDEFINED means
* "no override"). An explicit YCbCr420 force is honoured even on
* modes the sink only lists as RGB/4:4:4 capable
* (drm_mode_is_420_also() clear), as required for HDMI compliance
* testing; dc_validate_stream() still rejects anything the link
* genuinely cannot carry. The YCbCr422/YCbCr444 forces stay gated on
* the sink's advertised caps.
*/
want_420 = (aconnector->force_yuv_pixel_format == PIXEL_ENCODING_YCBCR420) ||
(drm_state && drm_state->color_format == DRM_CONNECTOR_COLOR_FORMAT_YCBCR420);
want_422 = (aconnector->force_yuv_pixel_format == PIXEL_ENCODING_YCBCR422) ||
(drm_state && drm_state->color_format == DRM_CONNECTOR_COLOR_FORMAT_YCBCR422);
if (dc_result == DC_OK)
dc_result = dm_validate_stream_and_context(adev->dm.dc, stream);
if (drm_mode_is_420_only(info, drm_mode) &&
(want_420 || (drm_state && drm_state->color_format == DRM_CONNECTOR_COLOR_FORMAT_AUTO))) {
encoding_mask = BIT(PIXEL_ENCODING_YCBCR420);
} else if (drm_mode_is_420_also(info, drm_mode) && want_420) {
encoding_mask = BIT(PIXEL_ENCODING_YCBCR420);
} else if ((info->color_formats & BIT(DRM_OUTPUT_COLOR_FORMAT_YCBCR422)) &&
want_422 && is_dp_or_hdmi) {
encoding_mask = BIT(PIXEL_ENCODING_YCBCR422);
} else if (((aconnector->force_yuv_pixel_format == PIXEL_ENCODING_YCBCR444) ||
(drm_state && drm_state->color_format == DRM_CONNECTOR_COLOR_FORMAT_YCBCR444)) &&
(info->color_formats & BIT(DRM_OUTPUT_COLOR_FORMAT_YCBCR444)) &&
is_dp_or_hdmi) {
encoding_mask = BIT(PIXEL_ENCODING_YCBCR444);
} else if (drm_state && drm_state->color_format == DRM_CONNECTOR_COLOR_FORMAT_RGB444) {
encoding_mask = BIT(PIXEL_ENCODING_RGB);
} else {
encoding_mask = BIT(PIXEL_ENCODING_RGB);
if (dc_result == DC_OK)
dc_result = dm_validate_stream_color_format(drm_state, stream);
if ((info->color_formats & BIT(DRM_OUTPUT_COLOR_FORMAT_YCBCR444)) &&
is_hdmi_ep)
encoding_mask |= BIT(PIXEL_ENCODING_YCBCR444);
if (dc_result != DC_OK) {
drm_dbg_kms(connector->dev, "Pruned mode %d x %d (clk %d) %s %s -- %s\n",
if (info->color_formats & BIT(DRM_OUTPUT_COLOR_FORMAT_YCBCR422))
encoding_mask |= BIT(PIXEL_ENCODING_YCBCR422);
if (drm_mode_is_420_also(info, drm_mode))
encoding_mask |= BIT(PIXEL_ENCODING_YCBCR420);
}
/*
* Build the set of bit depths to try, high-to-low. Start from the
* atomic-requested cap and clear anything the sink cannot do:
*
* - Never exceed the requested bpc.
* - HDMI/FRL and DP->HDMI dongles have no defined 6 bpc mode.
* - Any depth above the EDID-reported bpc is dropped.
*
* amdgpu_dm_convert_color_depth_from_display_info() applies the final
* per-encoding cap (e.g. YCbCr420 deep-colour limits) when the stream
* is built, so this mask only needs the coarse sink limits.
*/
for (j = 0; j < ARRAY_SIZE(bpc_order); j++) {
u8 bpc = bpc_order[j];
if (requested_bpc > 0 && bpc > requested_bpc)
continue;
if (bpc == 6 && is_hdmi_ep)
continue;
if (info->bpc && bpc > info->bpc)
continue;
bpc_mask |= BIT(bpc);
}
/*
* Enumerate the supported (encoding, bpc) combinations in priority
* order and return the first that validates. Because the masks only
* contain sink-supported entries, this is generic across connector
* types and needs no encoding-specific fallback afterwards.
*
* The selection lives entirely on the stack and is passed by value to
* create_stream_for_sink(); no shared connector state is mutated. This
* matters because this helper runs concurrently from the connector
* probe worker (->mode_valid) and from a compositor's atomic check on
* the same connector.
*/
for (i = 0; i < ARRAY_SIZE(encoding_order); i++) {
enum dc_pixel_encoding enc = encoding_order[i];
if (!(encoding_mask & BIT(enc)))
continue;
for (j = 0; j < ARRAY_SIZE(bpc_order); j++) {
u8 bpc = bpc_order[j];
if (!(bpc_mask & BIT(bpc)))
continue;
drm_dbg_kms(connector->dev,
"Trying %s with %d bpc (encoding_mask=0x%x bpc_mask=0x%x requested_bpc=%d drm max_bpc)\n",
dc_pixel_encoding_to_str(enc), bpc,
encoding_mask, bpc_mask, requested_bpc);
stream = create_stream_for_sink(connector, drm_mode,
dm_state, old_stream,
bpc, enc, is_hdmi_ep);
if (stream == NULL) {
drm_err(adev_to_drm(adev), "Failed to create stream for sink!\n");
return NULL;
}
dc_result = dc_validate_stream(adev->dm.dc, stream);
if (dc_result == DC_OK &&
stream->signal == SIGNAL_TYPE_DISPLAY_PORT_MST)
dc_result = dm_dp_mst_is_port_support_mode(aconnector, stream);
if (dc_result == DC_OK)
dc_result = dm_validate_stream_and_context(adev->dm.dc, stream);
if (dc_result == DC_OK)
dc_result = dm_validate_stream_color_format(drm_state, stream);
if (dc_result == DC_OK)
return stream;
drm_dbg_kms(connector->dev, "Pruned mode %d x %d (refresh rate %d) %s %s -- %s\n",
drm_mode->hdisplay,
drm_mode->vdisplay,
drm_mode->clock,
drm_mode_vrefresh(drm_mode),
dc_pixel_encoding_to_str(stream->timing.pixel_encoding),
dc_color_depth_to_str(stream->timing.display_color_depth),
dc_status_to_str(dc_result));
dc_stream_release(stream);
stream = NULL;
requested_bpc -= 2; /* lower bpc to retry validation */
}
} while (stream == NULL && requested_bpc >= bpc_limit);
switch (dc_result) {
/*
* If we failed to validate DP bandwidth stream with the requested RGB color depth,
* we try to fallback and configure in order:
* YUV422 (8bpc, 6bpc)
* YUV420 (8bpc, 6bpc)
*/
case DC_FAIL_ENC_VALIDATE:
case DC_EXCEED_DONGLE_CAP:
case DC_NO_DP_LINK_BANDWIDTH:
/* recursively entered twice and already tried both YUV422 and YUV420 */
if (aconnector->force_yuv422_output && aconnector->force_yuv420_output)
break;
/* first failure; try YUV422 */
if (!aconnector->force_yuv422_output) {
drm_dbg_kms(connector->dev, "%s:%d Validation failed with %d, retrying w/ YUV422\n",
__func__, __LINE__, dc_result);
aconnector->force_yuv422_output = true;
/* recursively entered and YUV422 failed, try YUV420 */
} else if (!aconnector->force_yuv420_output) {
drm_dbg_kms(connector->dev, "%s:%d Validation failed with %d, retrying w/ YUV420\n",
__func__, __LINE__, dc_result);
aconnector->force_yuv420_output = true;
}
stream = amdgpu_dm_create_validate_stream_for_sink(connector, drm_mode,
dm_state, old_stream);
aconnector->force_yuv422_output = false;
aconnector->force_yuv420_output = false;
break;
case DC_OK:
break;
default:
drm_dbg_kms(connector->dev, "%s:%d Unhandled validation failure %d\n",
__func__, __LINE__, dc_result);
break;
}
return stream;
/*
* Every sink-supported combination was exhausted without validating;
* the mode is genuinely unsupported on this link.
*/
return NULL;
}
EXPORT_IF_KUNIT(amdgpu_dm_create_validate_stream_for_sink);
@@ -3068,7 +3153,7 @@ static void hdmi_frl_status_polling_work(struct work_struct *work)
if (!dc_is_hdmi_signal(dc_link->connector_signal))
continue;
if (dc_link->connector_signal != SIGNAL_TYPE_HDMI_FRL)
if (dc_link->frl_link_settings.frl_link_rate == 0)
continue;
link_update = dc_link_frl_poll_status_flag(dc_link);

View File

@@ -189,13 +189,17 @@ void fill_stream_properties_from_drm_display_mode(
const struct drm_connector *connector,
const struct drm_connector_state *connector_state,
const struct dc_stream_state *old_stream,
int requested_bpc);
int requested_bpc,
enum dc_pixel_encoding requested_encoding,
bool is_hdmi_ep);
struct dc_stream_state *
create_stream_for_sink(struct drm_connector *connector,
const struct drm_display_mode *drm_mode,
const struct dm_connector_state *dm_state,
const struct dc_stream_state *old_stream,
int requested_bpc);
int requested_bpc,
enum dc_pixel_encoding requested_encoding,
bool is_hdmi_ep);
enum drm_connector_status
amdgpu_dm_connector_poll(struct amdgpu_dm_connector *aconnector, bool force);
enum drm_connector_status

View File

@@ -604,10 +604,15 @@ int amdgpu_dm_crtc_configure_crc_source(struct drm_crtc *crtc,
/* Enable or disable CRTC CRC generation */
if (dm_is_crc_source_crtc(source) || source == AMDGPU_DM_PIPE_CRC_SOURCE_NONE) {
if (!dc_stream_configure_crc(stream_state->ctx->dc,
stream_state, NULL, enable, enable, 0, true, crc_poly_mode)) {
ret = -EINVAL;
goto unlock;
int i;
for (i = 0; i < MAX_CRC_WINDOW_NUM; i++) {
if (!dc_stream_configure_crc(stream_state->ctx->dc,
stream_state, NULL, enable, enable,
i, true, crc_poly_mode)) {
ret = -EINVAL;
goto unlock;
}
}
}

View File

@@ -59,6 +59,7 @@ void amdgpu_dm_crtc_handle_vblank(struct amdgpu_crtc *acrtc)
spin_unlock_irqrestore(&dev->event_lock, flags);
}
EXPORT_IF_KUNIT(amdgpu_dm_crtc_handle_vblank);
bool amdgpu_dm_crtc_modeset_required(struct drm_crtc_state *crtc_state,
struct dc_stream_state *new_stream,
@@ -124,7 +125,11 @@ void amdgpu_dm_crtc_set_static_screen_optimze(
struct dc_link *link = stream->link;
bool set_vsync_event = !sso_enable;
if (!allow_sr_entry)
/*
* allow_sr_entry gates only entry. A disable request must still set
* the vsync events to force Replay and PSR1 out and keep them blocked.
*/
if (sso_enable && !allow_sr_entry)
return;
amdgpu_dm_replay_set_event(dm, stream,
@@ -134,6 +139,7 @@ void amdgpu_dm_crtc_set_static_screen_optimze(
amdgpu_dm_psr_set_event(dm, stream,
set_vsync_event, psr_event_vsync, set_vsync_event);
}
EXPORT_IF_KUNIT(amdgpu_dm_crtc_set_static_screen_optimze);
bool amdgpu_dm_is_headless(struct amdgpu_device *adev)
{
@@ -163,7 +169,7 @@ bool amdgpu_dm_is_headless(struct amdgpu_device *adev)
}
EXPORT_IF_KUNIT(amdgpu_dm_is_headless);
static void amdgpu_dm_idle_worker(struct work_struct *work)
STATIC_IFN_KUNIT void amdgpu_dm_idle_worker(struct work_struct *work)
{
struct idle_workqueue *idle_work;
@@ -197,6 +203,7 @@ static void amdgpu_dm_idle_worker(struct work_struct *work)
}
idle_work->dm->idle_workqueue->running = false;
}
EXPORT_IF_KUNIT(amdgpu_dm_idle_worker);
struct idle_workqueue *idle_create_workqueue(struct amdgpu_device *adev)
{
@@ -215,7 +222,7 @@ struct idle_workqueue *idle_create_workqueue(struct amdgpu_device *adev)
}
EXPORT_IF_KUNIT(idle_create_workqueue);
static void amdgpu_dm_crtc_vblank_control_worker(struct work_struct *work)
STATIC_IFN_KUNIT void amdgpu_dm_crtc_vblank_control_worker(struct work_struct *work)
{
struct vblank_control_work *vblank_work =
container_of(work, struct vblank_control_work, work);
@@ -240,6 +247,7 @@ static void amdgpu_dm_crtc_vblank_control_worker(struct work_struct *work)
kfree(vblank_work);
}
EXPORT_IF_KUNIT(amdgpu_dm_crtc_vblank_control_worker);
static inline int amdgpu_dm_crtc_set_vblank(struct drm_crtc *crtc, bool enable)
{
@@ -393,13 +401,15 @@ int amdgpu_dm_crtc_enable_vblank(struct drm_crtc *crtc)
{
return amdgpu_dm_crtc_set_vblank(crtc, true);
}
EXPORT_IF_KUNIT(amdgpu_dm_crtc_enable_vblank);
void amdgpu_dm_crtc_disable_vblank(struct drm_crtc *crtc)
{
amdgpu_dm_crtc_set_vblank(crtc, false);
}
EXPORT_IF_KUNIT(amdgpu_dm_crtc_disable_vblank);
static void amdgpu_dm_crtc_destroy_state(struct drm_crtc *crtc,
STATIC_IFN_KUNIT void amdgpu_dm_crtc_destroy_state(struct drm_crtc *crtc,
struct drm_crtc_state *state)
{
struct dm_crtc_state *cur = to_dm_crtc_state(state);
@@ -414,8 +424,9 @@ static void amdgpu_dm_crtc_destroy_state(struct drm_crtc *crtc,
kfree(state);
}
EXPORT_IF_KUNIT(amdgpu_dm_crtc_destroy_state);
static struct drm_crtc_state *amdgpu_dm_crtc_duplicate_state(struct drm_crtc *crtc)
STATIC_IFN_KUNIT struct drm_crtc_state *amdgpu_dm_crtc_duplicate_state(struct drm_crtc *crtc)
{
struct dm_crtc_state *state, *cur;
@@ -450,6 +461,7 @@ static struct drm_crtc_state *amdgpu_dm_crtc_duplicate_state(struct drm_crtc *cr
return &state->base;
}
EXPORT_IF_KUNIT(amdgpu_dm_crtc_duplicate_state);
static void amdgpu_dm_crtc_destroy(struct drm_crtc *crtc)
{
@@ -463,7 +475,7 @@ static void amdgpu_dm_crtc_destroy(struct drm_crtc *crtc)
kfree(crtc);
}
static void amdgpu_dm_crtc_reset_state(struct drm_crtc *crtc)
STATIC_IFN_KUNIT void amdgpu_dm_crtc_reset_state(struct drm_crtc *crtc)
{
struct dm_crtc_state *state;
@@ -476,6 +488,7 @@ static void amdgpu_dm_crtc_reset_state(struct drm_crtc *crtc)
__drm_atomic_helper_crtc_reset(crtc, &state->base);
}
EXPORT_IF_KUNIT(amdgpu_dm_crtc_reset_state);
#ifdef CONFIG_DEBUG_FS
static int amdgpu_dm_crtc_late_register(struct drm_crtc *crtc)
@@ -578,7 +591,7 @@ static void amdgpu_dm_crtc_helper_disable(struct drm_crtc *crtc)
{
}
static int amdgpu_dm_crtc_count_crtc_active_planes(struct drm_crtc_state *new_crtc_state)
STATIC_IFN_KUNIT int amdgpu_dm_crtc_count_crtc_active_planes(struct drm_crtc_state *new_crtc_state)
{
struct drm_atomic_commit *state = new_crtc_state->state;
struct drm_plane *plane;
@@ -609,8 +622,9 @@ static int amdgpu_dm_crtc_count_crtc_active_planes(struct drm_crtc_state *new_cr
return num_active;
}
EXPORT_IF_KUNIT(amdgpu_dm_crtc_count_crtc_active_planes);
static void amdgpu_dm_crtc_update_crtc_active_planes(struct drm_crtc *crtc,
STATIC_IFN_KUNIT void amdgpu_dm_crtc_update_crtc_active_planes(struct drm_crtc *crtc,
struct drm_crtc_state *new_crtc_state)
{
struct dm_crtc_state *dm_new_crtc_state =
@@ -624,6 +638,7 @@ static void amdgpu_dm_crtc_update_crtc_active_planes(struct drm_crtc *crtc,
dm_new_crtc_state->active_planes =
amdgpu_dm_crtc_count_crtc_active_planes(new_crtc_state);
}
EXPORT_IF_KUNIT(amdgpu_dm_crtc_update_crtc_active_planes);
STATIC_IFN_KUNIT bool amdgpu_dm_crtc_helper_mode_fixup(struct drm_crtc *crtc,
const struct drm_display_mode *mode,

View File

@@ -46,6 +46,15 @@ bool amdgpu_dm_crtc_vrr_active_irq(struct amdgpu_crtc *acrtc);
bool amdgpu_dm_crtc_helper_mode_fixup(struct drm_crtc *crtc,
const struct drm_display_mode *mode,
struct drm_display_mode *adjusted_mode);
void amdgpu_dm_crtc_destroy_state(struct drm_crtc *crtc,
struct drm_crtc_state *state);
struct drm_crtc_state *amdgpu_dm_crtc_duplicate_state(struct drm_crtc *crtc);
void amdgpu_dm_crtc_reset_state(struct drm_crtc *crtc);
int amdgpu_dm_crtc_count_crtc_active_planes(struct drm_crtc_state *new_crtc_state);
void amdgpu_dm_crtc_update_crtc_active_planes(struct drm_crtc *crtc,
struct drm_crtc_state *new_crtc_state);
void amdgpu_dm_crtc_vblank_control_worker(struct work_struct *work);
void amdgpu_dm_idle_worker(struct work_struct *work);
#endif
bool amdgpu_dm_crtc_vrr_active(const struct dm_crtc_state *dm_state);

View File

@@ -290,15 +290,20 @@ int amdgpu_dm_crtc_get_cursor_mode(struct amdgpu_device *adev,
/* Overlay cursor not supported on HW before DCN
* DCN401/420 does not have the cursor-on-scaled-plane or cursor-on-yuv-plane restrictions
* as previous DCN generations, so enable native mode on DCN401/420
*
* Always set native cursor mode when the CRTC is disabled,
* to make sure it doesn't cause atomic commits to fail when
* they are trying to disable the CRTC.
*/
if (amdgpu_ip_version(adev, DCE_HWIP, 0) == IP_VERSION(4, 0, 1) ||
amdgpu_ip_version(adev, DCE_HWIP, 0) == IP_VERSION(4, 2, 0) ||
#if defined(CONFIG_DRM_AMD_DC_DCN6_0)
amdgpu_ip_version(adev, DCE_HWIP, 0) == IP_VERSION(4, 2, 1) ||
amdgpu_ip_version(adev, DCE_HWIP, 0) == IP_VERSION(6, 0, 0)) {
amdgpu_ip_version(adev, DCE_HWIP, 0) == IP_VERSION(6, 0, 0) ||
#else
amdgpu_ip_version(adev, DCE_HWIP, 0) == IP_VERSION(4, 2, 1)) {
amdgpu_ip_version(adev, DCE_HWIP, 0) == IP_VERSION(4, 2, 1) ||
#endif
!dm_crtc_state->base.enable) {
*cursor_mode = DM_CURSOR_NATIVE_MODE;
return 0;
}

View File

@@ -3199,57 +3199,42 @@ static const struct {
};
/*
* Force YUV420 output if available from the given mode
* Force a specific pixel encoding for the given connector, overriding the
* encoding that stream validation would otherwise pick. The value is an
* enum dc_pixel_encoding:
*
* 0 - PIXEL_ENCODING_UNDEFINED (no override, default)
* 1 - PIXEL_ENCODING_RGB
* 2 - PIXEL_ENCODING_YCBCR422
* 3 - PIXEL_ENCODING_YCBCR444
* 4 - PIXEL_ENCODING_YCBCR420
*/
static int force_yuv420_output_set(void *data, u64 val)
static int force_yuv_pixel_format_set(void *data, u64 val)
{
struct amdgpu_dm_connector *connector = data;
connector->force_yuv420_output = (bool)val;
connector->force_yuv_pixel_format = PIXEL_ENCODING_YCBCR420;
if (val >= PIXEL_ENCODING_COUNT)
return -EINVAL;
connector->force_yuv_pixel_format = (uint8_t)val;
return 0;
}
/*
* Check if YUV420 is forced when available from the given mode
* Read back the pixel encoding currently forced on the given connector.
*/
static int force_yuv420_output_get(void *data, u64 *val)
static int force_yuv_pixel_format_get(void *data, u64 *val)
{
struct amdgpu_dm_connector *connector = data;
*val = connector->force_yuv420_output;
*val = connector->force_yuv_pixel_format;
return 0;
}
DEFINE_DEBUGFS_ATTRIBUTE(force_yuv420_output_fops, force_yuv420_output_get,
force_yuv420_output_set, "%llu\n");
static int force_yuv422_output_set(void *data, u64 val)
{
struct amdgpu_dm_connector *connector = data;
connector->force_yuv422_output = (bool)val;
connector->force_yuv_pixel_format = PIXEL_ENCODING_YCBCR422;
return 0;
}
DEFINE_DEBUGFS_ATTRIBUTE(force_yuv422_output_fops, NULL,
force_yuv422_output_set, "%llu\n");
static int force_yuv444_output_set(void *data, u64 val)
{
struct amdgpu_dm_connector *connector = data;
connector->force_yuv_pixel_format = PIXEL_ENCODING_YCBCR444;
return 0;
}
DEFINE_DEBUGFS_ATTRIBUTE(force_yuv444_output_fops, NULL,
force_yuv444_output_set, "%llu\n");
DEFINE_DEBUGFS_ATTRIBUTE(force_yuv_pixel_format_fops, force_yuv_pixel_format_get,
force_yuv_pixel_format_set, "%llu\n");
/*
* Read Replay state
@@ -3699,9 +3684,7 @@ static const struct {
char *name;
const struct file_operations *fops;
} connector_debugfs_entries[] = {
{"force_yuv420_output", &force_yuv420_output_fops},
{"force_yuv422_output", &force_yuv422_output_fops},
{"force_yuv444_output", &force_yuv444_output_fops},
{"force_yuv_pixel_format", &force_yuv_pixel_format_fops},
{"trigger_hotplug", &trigger_hotplug_debugfs_fops},
{"internal_display", &internal_display_fops},
{"odm_combine_segments", &odm_combine_segments_fops}

View File

@@ -63,6 +63,7 @@ MODULE_FIRMWARE(FIRMWARE_DCN_36_DMUB);
MODULE_FIRMWARE(FIRMWARE_DCN_401_DMUB);
MODULE_FIRMWARE(FIRMWARE_DCN_42_DMUB);
MODULE_FIRMWARE(FIRMWARE_DCN_42B_DMUB);
MODULE_FIRMWARE(FIRMWARE_DCN_60_DMUB);
/**
* dm_dmub_aux_setconfig_callback - Callback for AUX or SET_CONFIG command.
@@ -408,6 +409,9 @@ STATIC_IFN_KUNIT void *dm_dmub_get_vbios_bounding_box(struct amdgpu_device *adev
case IP_VERSION(4, 2, 1):
bb_size = sizeof(struct dml2_soc_bb);
break;
case IP_VERSION(6, 0, 0):
bb_size = sizeof(struct dmub_soc_bb_params);
break;
default:
return NULL;
}
@@ -560,6 +564,9 @@ int dm_dmub_sw_init(struct amdgpu_device *adev)
case IP_VERSION(4, 2, 1):
dmub_asic = DMUB_ASIC_DCN42B;
break;
case IP_VERSION(6, 0, 0):
dmub_asic = DMUB_ASIC_DCN60;
break;
default:
/* ASIC doesn't support DMUB. */
return 0;
@@ -752,6 +759,9 @@ int dm_init_microcode(struct amdgpu_device *adev)
case IP_VERSION(4, 2, 1):
fw_name_dmub = FIRMWARE_DCN_42B_DMUB;
break;
case IP_VERSION(6, 0, 0):
fw_name_dmub = FIRMWARE_DCN_60_DMUB;
break;
default:
/* ASIC doesn't support DMUB. */
return 0;

View File

@@ -62,6 +62,7 @@ int dm_init_microcode(struct amdgpu_device *adev);
#define FIRMWARE_DCN_401_DMUB "amdgpu/dcn_4_0_1_dmcub.bin"
#define FIRMWARE_DCN_42_DMUB "amdgpu/dcn_4_2_dmcub.bin"
#define FIRMWARE_DCN_42B_DMUB "amdgpu/dcn_4_2_1_dmcub.bin"
#define FIRMWARE_DCN_60_DMUB "amdgpu/dcn_6_0_0_dmcub.bin"
#define FIRMWARE_RAVEN_DMCU "amdgpu/raven_dmcu.bin"
#define FIRMWARE_NAVI12_DMCU "amdgpu/navi12_dmcu.bin"

View File

@@ -365,7 +365,7 @@ void hdcp_reset_display(struct hdcp_workqueue *hdcp_work, unsigned int link_inde
cancel_delayed_work(&hdcp_w->property_validate_dwork);
for (conn_index = 0; conn_index < AMDGPU_DM_MAX_DISPLAY_INDEX; conn_index++) {
for (conn_index = 0; conn_index < AMDGPU_DM_MAX_DISPLAY_COUNT; conn_index++) {
hdcp_w->encryption_status[conn_index] =
MOD_HDCP_ENCRYPTION_STATUS_HDCP_OFF;
if (hdcp_w->aconnector[conn_index]) {
@@ -418,7 +418,7 @@ void event_property_update(struct work_struct *work)
struct drm_connector *connector;
struct drm_connector_state *conn_state;
for (conn_index = 0; conn_index < AMDGPU_DM_MAX_DISPLAY_INDEX; conn_index++) {
for (conn_index = 0; conn_index < AMDGPU_DM_MAX_DISPLAY_COUNT; conn_index++) {
aconnector = hdcp_work->aconnector[conn_index];
if (!aconnector)
@@ -478,7 +478,7 @@ void event_property_validate(struct work_struct *work)
guard(mutex)(&hdcp_work->mutex);
for (conn_index = 0; conn_index < AMDGPU_DM_MAX_DISPLAY_INDEX;
for (conn_index = 0; conn_index < AMDGPU_DM_MAX_DISPLAY_COUNT;
conn_index++) {
aconnector = hdcp_work->aconnector[conn_index];
@@ -897,10 +897,10 @@ struct hdcp_workqueue *hdcp_create_workqueue(struct amdgpu_device *adev,
memset(hdcp_work[i].aconnector, 0,
sizeof(struct amdgpu_dm_connector *) *
AMDGPU_DM_MAX_DISPLAY_INDEX);
AMDGPU_DM_MAX_DISPLAY_COUNT);
memset(hdcp_work[i].encryption_status, 0,
sizeof(enum mod_hdcp_encryption_status) *
AMDGPU_DM_MAX_DISPLAY_INDEX);
AMDGPU_DM_MAX_DISPLAY_COUNT);
}
cp_psp->funcs.update_stream_config = update_config;

View File

@@ -36,7 +36,7 @@
* Minimal declarations needed by this header.
* Full amdgpu/DM definitions come from amdgpu_dm.h included by each .c file.
*/
#define AMDGPU_DM_MAX_DISPLAY_INDEX 31
#define AMDGPU_DM_MAX_DISPLAY_COUNT 32
struct amdgpu_dm_connector;
struct mod_hdcp;
@@ -57,7 +57,7 @@ struct hdcp_workqueue {
struct delayed_work callback_dwork;
struct delayed_work watchdog_timer_dwork;
struct delayed_work property_validate_dwork;
struct amdgpu_dm_connector *aconnector[AMDGPU_DM_MAX_DISPLAY_INDEX];
struct amdgpu_dm_connector *aconnector[AMDGPU_DM_MAX_DISPLAY_COUNT];
struct mutex mutex;
struct mod_hdcp hdcp;
@@ -65,7 +65,7 @@ struct hdcp_workqueue {
struct mod_hdcp_display display;
struct mod_hdcp_link link;
enum mod_hdcp_encryption_status encryption_status[AMDGPU_DM_MAX_DISPLAY_INDEX];
enum mod_hdcp_encryption_status encryption_status[AMDGPU_DM_MAX_DISPLAY_COUNT];
/* when display is unplugged from mst hub, connctor will be
* destroyed within dm_dp_mst_connector_destroy. connector
* hdcp perperties, like type, undesired, desired, enabled,
@@ -75,9 +75,9 @@ struct hdcp_workqueue {
* will be retrieved from hdcp_work within dm_dp_mst_get_modes
*/
/* un-desired, desired, enabled */
unsigned int content_protection[AMDGPU_DM_MAX_DISPLAY_INDEX];
unsigned int content_protection[AMDGPU_DM_MAX_DISPLAY_COUNT];
/* hdcp1.x, hdcp2.x */
unsigned int hdcp_content_type[AMDGPU_DM_MAX_DISPLAY_INDEX];
unsigned int hdcp_content_type[AMDGPU_DM_MAX_DISPLAY_COUNT];
uint8_t max_link;

View File

@@ -1903,3 +1903,9 @@ void dm_helpers_mccs_vcp_set(struct dc_context *ctx, struct dc_link *link,
}
EXPORT_IF_KUNIT(dm_helpers_mccs_vcp_set);
bool dm_helpers_submit_i2c_over_aux(struct ddc_service *ddc, uint32_t address, uint8_t offset,
uint8_t *cmdBuffer, uint32_t len, bool read)
{
//TODO: Implement this
return false;
}

View File

@@ -485,7 +485,8 @@ static int dm_dp_mst_get_modes(struct drm_connector *connector)
struct drm_device *dev = connector->dev;
struct amdgpu_device *adev = drm_to_adev(dev);
if (adev->dm.hdcp_workqueue) {
if (adev->dm.hdcp_workqueue &&
connector->index < AMDGPU_DM_MAX_DISPLAY_COUNT) {
struct hdcp_workqueue *hdcp_work = adev->dm.hdcp_workqueue;
struct hdcp_workqueue *hdcp_w =
&hdcp_work[aconnector->dc_link->link_index];

View File

@@ -144,7 +144,8 @@ void amdgpu_dm_plane_fill_blending_from_plane_state(const struct drm_plane_state
* other ASICs use an 8-bit field. The DRM plane alpha is
* 16-bit, so scale it down to the width the hardware expects.
*/
if (amdgpu_ip_version(adev, DCE_HWIP, 0) == IP_VERSION(4, 2, 0))
if (amdgpu_ip_version(adev, DCE_HWIP, 0) == IP_VERSION(4, 2, 0)
|| amdgpu_ip_version(adev, DCE_HWIP, 0) == IP_VERSION(6, 0, 0))
*global_alpha_value = plane_state->alpha >> 4;
else
*global_alpha_value = plane_state->alpha >> 8;
@@ -316,6 +317,7 @@ STATIC_IFN_KUNIT int amdgpu_dm_plane_validate_dcc(struct amdgpu_device *adev,
return 0;
if (adev->family != AMDGPU_FAMILY_GC_12_0_0 &&
adev->family != AMDGPU_FAMILY_GC_13_0_1 &&
format >= SURFACE_PIXEL_FORMAT_VIDEO_BEGIN)
return -EINVAL;
@@ -1047,6 +1049,8 @@ STATIC_IFN_KUNIT int amdgpu_dm_plane_get_plane_modifiers(struct amdgpu_device *a
amdgpu_dm_plane_add_gfx11_modifiers(adev, mods, &size, &capacity);
break;
case AMDGPU_FAMILY_GC_12_0_0:
case AMDGPU_FAMILY_GC_13_0_1:
/* GFX13 (DCN6) shares the GFX12 tiling/DCC binary modifier format. */
amdgpu_dm_plane_add_gfx12_modifiers(adev, mods, &size, &capacity);
break;
}
@@ -1187,7 +1191,8 @@ int amdgpu_dm_plane_fill_plane_buffer_attributes(struct amdgpu_device *adev,
upper_32_bits(chroma_addr);
}
if (adev->family == AMDGPU_FAMILY_GC_12_0_0) {
if (adev->family == AMDGPU_FAMILY_GC_12_0_0
|| adev->family == AMDGPU_FAMILY_GC_13_0_1) {
ret = amdgpu_dm_plane_fill_gfx12_attrs_from_modifiers(adev, afb, format,
rotation, plane_size,
tiling_info, dcc,

View File

@@ -35,4 +35,3 @@ obj-$(CONFIG_DRM_AMD_DC_KUNIT_TEST) += amdgpu_dm_crtc_test.o
obj-$(CONFIG_DRM_AMD_DC_KUNIT_TEST) += amdgpu_dm_services_test.o
obj-$(CONFIG_DRM_AMD_DC_KUNIT_TEST) += amdgpu_dm_helpers_test.o
obj-$(CONFIG_DRM_AMD_DC_KUNIT_TEST) += amdgpu_dm_quirks_test.o
obj-$(CONFIG_DRM_AMD_DC_KUNIT_TEST) += amdgpu_dm_plane_test.o

View File

@@ -3416,7 +3416,8 @@ static void dm_test_fill_stream_borders_zeroed(struct kunit *test)
timing->v_border_bottom = 8;
fill_stream_properties_from_drm_display_mode(ctx->stream, ctx->mode,
&ctx->aconnector->base, ctx->conn_state, NULL, 8);
&ctx->aconnector->base, ctx->conn_state, NULL, 8,
PIXEL_ENCODING_RGB, false);
KUNIT_EXPECT_EQ(test, (int)timing->h_border_left, 0);
KUNIT_EXPECT_EQ(test, (int)timing->h_border_right, 0);
@@ -3437,7 +3438,8 @@ static void dm_test_fill_stream_rgb_defaults(struct kunit *test)
struct dc_crtc_timing *timing = &ctx->stream->timing;
fill_stream_properties_from_drm_display_mode(ctx->stream, ctx->mode,
&ctx->aconnector->base, ctx->conn_state, NULL, 8);
&ctx->aconnector->base, ctx->conn_state, NULL, 8,
PIXEL_ENCODING_RGB, false);
KUNIT_EXPECT_EQ(test, (int)timing->pixel_encoding, (int)PIXEL_ENCODING_RGB);
KUNIT_EXPECT_EQ(test, (int)timing->timing_3d_format,
@@ -3463,7 +3465,8 @@ static void dm_test_fill_stream_sync_polarity_positive(struct kunit *test)
ctx->mode->flags = DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC;
fill_stream_properties_from_drm_display_mode(ctx->stream, ctx->mode,
&ctx->aconnector->base, ctx->conn_state, NULL, 8);
&ctx->aconnector->base, ctx->conn_state, NULL, 8,
PIXEL_ENCODING_RGB, false);
KUNIT_EXPECT_EQ(test, (int)timing->flags.HSYNC_POSITIVE_POLARITY, 1);
KUNIT_EXPECT_EQ(test, (int)timing->flags.VSYNC_POSITIVE_POLARITY, 1);
@@ -3482,7 +3485,8 @@ static void dm_test_fill_stream_sync_polarity_negative(struct kunit *test)
ctx->mode->flags = 0;
fill_stream_properties_from_drm_display_mode(ctx->stream, ctx->mode,
&ctx->aconnector->base, ctx->conn_state, NULL, 8);
&ctx->aconnector->base, ctx->conn_state, NULL, 8,
PIXEL_ENCODING_RGB, false);
KUNIT_EXPECT_EQ(test, (int)timing->flags.HSYNC_POSITIVE_POLARITY, 0);
KUNIT_EXPECT_EQ(test, (int)timing->flags.VSYNC_POSITIVE_POLARITY, 0);
@@ -3511,7 +3515,8 @@ static void dm_test_fill_stream_inherits_old_stream(struct kunit *test)
ctx->mode->flags = DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC;
fill_stream_properties_from_drm_display_mode(ctx->stream, ctx->mode,
&ctx->aconnector->base, ctx->conn_state, old_stream, 8);
&ctx->aconnector->base, ctx->conn_state, old_stream, 8,
PIXEL_ENCODING_RGB, false);
KUNIT_EXPECT_EQ(test, (int)timing->vic, 16);
KUNIT_EXPECT_EQ(test, (int)timing->flags.HSYNC_POSITIVE_POLARITY, 1);
@@ -3541,7 +3546,8 @@ static void dm_test_fill_stream_timing_from_crtc(struct kunit *test)
ctx->mode->crtc_clock = 148500;
fill_stream_properties_from_drm_display_mode(ctx->stream, ctx->mode,
&ctx->aconnector->base, ctx->conn_state, NULL, 8);
&ctx->aconnector->base, ctx->conn_state, NULL, 8,
PIXEL_ENCODING_RGB, false);
KUNIT_EXPECT_EQ(test, (int)timing->h_addressable, 1920);
KUNIT_EXPECT_EQ(test, (int)timing->h_total, 2200);
@@ -3568,7 +3574,8 @@ static void dm_test_fill_stream_color_depth_requested_bpc(struct kunit *test)
ctx->aconnector->base.display_info.bpc = 12;
fill_stream_properties_from_drm_display_mode(ctx->stream, ctx->mode,
&ctx->aconnector->base, ctx->conn_state, NULL, 10);
&ctx->aconnector->base, ctx->conn_state, NULL, 10,
PIXEL_ENCODING_RGB, false);
KUNIT_EXPECT_EQ(test, (int)timing->display_color_depth,
(int)COLOR_DEPTH_101010);
@@ -3585,7 +3592,8 @@ static void dm_test_fill_stream_content_type(struct kunit *test)
ctx->conn_state->content_type = DRM_MODE_CONTENT_TYPE_GRAPHICS;
fill_stream_properties_from_drm_display_mode(ctx->stream, ctx->mode,
&ctx->aconnector->base, ctx->conn_state, NULL, 8);
&ctx->aconnector->base, ctx->conn_state, NULL, 8,
PIXEL_ENCODING_RGB, false);
KUNIT_EXPECT_EQ(test, (int)ctx->stream->content_type,
(int)DISPLAY_CONTENT_TYPE_GRAPHICS);
@@ -3603,12 +3611,121 @@ static void dm_test_fill_stream_aspect_ratio(struct kunit *test)
ctx->mode->picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9;
fill_stream_properties_from_drm_display_mode(ctx->stream, ctx->mode,
&ctx->aconnector->base, ctx->conn_state, NULL, 8);
&ctx->aconnector->base, ctx->conn_state, NULL, 8,
PIXEL_ENCODING_RGB, false);
KUNIT_EXPECT_EQ(test, (int)timing->aspect_ratio,
(int)ASPECT_RATIO_16_9);
}
/**
* dm_test_fill_stream_encoding_from_caller_ycbcr420 - Test caller-selected 420
* @test: The KUnit test context
*
* The helper no longer derives the pixel encoding from the display info; it
* applies whatever the caller selected. Passing YCbCr420 must be honoured even
* though the DisplayPort sink advertises no YCbCr color formats.
*/
static void dm_test_fill_stream_encoding_from_caller_ycbcr420(struct kunit *test)
{
struct dm_test_fill_ctx *ctx = dm_test_fill_ctx_alloc(test);
struct dc_crtc_timing *timing = &ctx->stream->timing;
fill_stream_properties_from_drm_display_mode(ctx->stream, ctx->mode,
&ctx->aconnector->base, ctx->conn_state, NULL, 8,
PIXEL_ENCODING_YCBCR420, false);
KUNIT_EXPECT_EQ(test, (int)timing->pixel_encoding,
(int)PIXEL_ENCODING_YCBCR420);
}
/**
* dm_test_fill_stream_encoding_from_caller_ycbcr422 - Test caller-selected 422
* @test: The KUnit test context
*
* A caller-selected YCbCr422 encoding is applied verbatim.
*/
static void dm_test_fill_stream_encoding_from_caller_ycbcr422(struct kunit *test)
{
struct dm_test_fill_ctx *ctx = dm_test_fill_ctx_alloc(test);
struct dc_crtc_timing *timing = &ctx->stream->timing;
fill_stream_properties_from_drm_display_mode(ctx->stream, ctx->mode,
&ctx->aconnector->base, ctx->conn_state, NULL, 8,
PIXEL_ENCODING_YCBCR422, false);
KUNIT_EXPECT_EQ(test, (int)timing->pixel_encoding,
(int)PIXEL_ENCODING_YCBCR422);
}
/**
* dm_test_fill_stream_encoding_from_caller_ycbcr444 - Test caller-selected 444
* @test: The KUnit test context
*
* A caller-selected YCbCr444 encoding is applied verbatim.
*/
static void dm_test_fill_stream_encoding_from_caller_ycbcr444(struct kunit *test)
{
struct dm_test_fill_ctx *ctx = dm_test_fill_ctx_alloc(test);
struct dc_crtc_timing *timing = &ctx->stream->timing;
fill_stream_properties_from_drm_display_mode(ctx->stream, ctx->mode,
&ctx->aconnector->base, ctx->conn_state, NULL, 8,
PIXEL_ENCODING_YCBCR444, false);
KUNIT_EXPECT_EQ(test, (int)timing->pixel_encoding,
(int)PIXEL_ENCODING_YCBCR444);
}
/**
* dm_test_fill_stream_hdmi_ep_clamps_depth - Test HDMI TMDS depth clamp applied
* @test: The KUnit test context
*
* With is_hdmi_ep set the colour depth is clamped to what the sink's max TMDS
* clock allows: a 10bpc request that exceeds the limit is reduced to 8bpc.
*/
static void dm_test_fill_stream_hdmi_ep_clamps_depth(struct kunit *test)
{
struct dm_test_fill_ctx *ctx = dm_test_fill_ctx_alloc(test);
struct dc_crtc_timing *timing = &ctx->stream->timing;
ctx->aconnector->base.display_info.bpc = 10;
/* 10bpc RGB needs 185625 KHz, over the sink's 160 MHz TMDS limit. */
ctx->aconnector->base.display_info.max_tmds_clock = 160000;
ctx->mode->crtc_clock = 148500;
fill_stream_properties_from_drm_display_mode(ctx->stream, ctx->mode,
&ctx->aconnector->base, ctx->conn_state, NULL, 10,
PIXEL_ENCODING_RGB, true);
KUNIT_EXPECT_EQ(test, (int)timing->display_color_depth,
(int)COLOR_DEPTH_888);
}
/**
* dm_test_fill_stream_non_hdmi_ep_keeps_depth - Test no TMDS clamp off HDMI
* @test: The KUnit test context
*
* With is_hdmi_ep clear the TMDS clamp is skipped, so the same over-limit
* 10bpc request is left untouched. The clamp is HDMI-specific.
*/
static void dm_test_fill_stream_non_hdmi_ep_keeps_depth(struct kunit *test)
{
struct dm_test_fill_ctx *ctx = dm_test_fill_ctx_alloc(test);
struct dc_crtc_timing *timing = &ctx->stream->timing;
ctx->aconnector->base.display_info.bpc = 10;
ctx->aconnector->base.display_info.max_tmds_clock = 160000;
ctx->mode->crtc_clock = 148500;
fill_stream_properties_from_drm_display_mode(ctx->stream, ctx->mode,
&ctx->aconnector->base, ctx->conn_state, NULL, 10,
PIXEL_ENCODING_RGB, false);
KUNIT_EXPECT_EQ(test, (int)timing->display_color_depth,
(int)COLOR_DEPTH_101010);
}
/* Tests for create_stream_for_sink() */
/*
@@ -3646,7 +3763,7 @@ static struct dm_test_stream_ctx *dm_test_stream_ctx_alloc(struct kunit *test)
DRIVER_MODESET);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx->drm);
ctx->aconnector = kunit_kzalloc(test, sizeof(*ctx->aconnector), GFP_KERNEL);
ctx->aconnector = drmm_kzalloc(ctx->drm, sizeof(*ctx->aconnector), GFP_KERNEL);
KUNIT_ASSERT_NOT_NULL(test, ctx->aconnector);
KUNIT_ASSERT_EQ(test,
drmm_connector_init(ctx->drm, &ctx->aconnector->base,
@@ -3687,7 +3804,8 @@ static void dm_test_create_stream_fake_sink_success(struct kunit *test)
struct dc_stream_state *stream;
stream = create_stream_for_sink(&ctx->aconnector->base, ctx->mode,
ctx->dm_state, NULL, 8);
ctx->dm_state, NULL, 8,
PIXEL_ENCODING_RGB, false);
KUNIT_ASSERT_NOT_NULL(test, stream);
dc_stream_release(stream);
@@ -3703,7 +3821,8 @@ static void dm_test_create_stream_sets_dm_context(struct kunit *test)
struct dc_stream_state *stream;
stream = create_stream_for_sink(&ctx->aconnector->base, ctx->mode,
ctx->dm_state, NULL, 8);
ctx->dm_state, NULL, 8,
PIXEL_ENCODING_RGB, false);
KUNIT_ASSERT_NOT_NULL(test, stream);
KUNIT_EXPECT_PTR_EQ(test, stream->dm_stream_context, ctx->aconnector);
@@ -3720,7 +3839,8 @@ static void dm_test_create_stream_virtual_signal(struct kunit *test)
struct dc_stream_state *stream;
stream = create_stream_for_sink(&ctx->aconnector->base, ctx->mode,
ctx->dm_state, NULL, 8);
ctx->dm_state, NULL, 8,
PIXEL_ENCODING_RGB, false);
KUNIT_ASSERT_NOT_NULL(test, stream);
KUNIT_EXPECT_EQ(test, (int)stream->signal, (int)SIGNAL_TYPE_VIRTUAL);
@@ -3739,7 +3859,8 @@ static void dm_test_create_stream_scaling_src(struct kunit *test)
struct dc_stream_state *stream;
stream = create_stream_for_sink(&ctx->aconnector->base, ctx->mode,
ctx->dm_state, NULL, 8);
ctx->dm_state, NULL, 8,
PIXEL_ENCODING_RGB, false);
KUNIT_ASSERT_NOT_NULL(test, stream);
KUNIT_EXPECT_EQ(test, (int)stream->src.width, 1920);
@@ -3770,7 +3891,8 @@ static void dm_test_create_stream_existing_sink(struct kunit *test)
ctx->aconnector->dc_sink = sink;
stream = create_stream_for_sink(&ctx->aconnector->base, ctx->mode,
ctx->dm_state, NULL, 8);
ctx->dm_state, NULL, 8,
PIXEL_ENCODING_RGB, false);
KUNIT_ASSERT_NOT_NULL(test, stream);
KUNIT_EXPECT_PTR_EQ(test, stream->sink, sink);
@@ -3904,7 +4026,7 @@ static void dm_test_poll_dac_load_returns_cached(struct kunit *test)
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, drm);
adev = drm_to_adev(drm);
aconnector = kunit_kzalloc(test, sizeof(*aconnector), GFP_KERNEL);
aconnector = drmm_kzalloc(drm, sizeof(*aconnector), GFP_KERNEL);
KUNIT_ASSERT_NOT_NULL(test, aconnector);
KUNIT_ASSERT_EQ(test,
drmm_connector_init(drm, &aconnector->base,
@@ -3951,7 +4073,7 @@ static struct amdgpu_dm_connector *dm_test_reg_connector(struct kunit *test)
DRIVER_MODESET);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, drm);
aconnector = kunit_kzalloc(test, sizeof(*aconnector), GFP_KERNEL);
aconnector = drmm_kzalloc(drm, sizeof(*aconnector), GFP_KERNEL);
KUNIT_ASSERT_NOT_NULL(test, aconnector);
KUNIT_ASSERT_EQ(test,
drmm_connector_init(drm, &aconnector->base,
@@ -4406,7 +4528,7 @@ dm_test_modes_ctx_alloc(struct kunit *test, int connector_type)
ctx->drm = dm_test_alloc_drm(test);
ctx->aconnector = dm_test_add_connector(test, ctx->drm, connector_type);
ctx->aenc = kunit_kzalloc(test, sizeof(*ctx->aenc), GFP_KERNEL);
ctx->aenc = drmm_kzalloc(ctx->drm, sizeof(*ctx->aenc), GFP_KERNEL);
KUNIT_ASSERT_NOT_NULL(test, ctx->aenc);
KUNIT_ASSERT_EQ(test,
drmm_encoder_init(ctx->drm, &ctx->aenc->base, NULL,
@@ -5456,6 +5578,11 @@ static struct kunit_case amdgpu_dm_connector_tests[] = {
KUNIT_CASE(dm_test_fill_stream_color_depth_requested_bpc),
KUNIT_CASE(dm_test_fill_stream_content_type),
KUNIT_CASE(dm_test_fill_stream_aspect_ratio),
KUNIT_CASE(dm_test_fill_stream_encoding_from_caller_ycbcr420),
KUNIT_CASE(dm_test_fill_stream_encoding_from_caller_ycbcr422),
KUNIT_CASE(dm_test_fill_stream_encoding_from_caller_ycbcr444),
KUNIT_CASE(dm_test_fill_stream_hdmi_ep_clamps_depth),
KUNIT_CASE(dm_test_fill_stream_non_hdmi_ep_keeps_depth),
/* create_stream_for_sink */
KUNIT_CASE(dm_test_create_stream_fake_sink_success),
KUNIT_CASE(dm_test_create_stream_sets_dm_context),

View File

@@ -12,6 +12,7 @@
#include "dc.h"
#include "core_types.h"
#include "clk_mgr.h"
#include "amdgpu.h"
#include "amdgpu_mode.h"
#include "amdgpu_dm.h"
@@ -114,6 +115,34 @@ struct dc_stream_state *dm_kunit_alloc_stream(struct kunit *test,
}
EXPORT_SYMBOL(dm_kunit_alloc_stream);
struct dc_state *dm_kunit_alloc_dc_state(struct kunit *test)
{
struct dc_state *state;
state = kunit_kzalloc(test, sizeof(*state), GFP_KERNEL);
KUNIT_ASSERT_NOT_NULL(test, state);
return state;
}
EXPORT_SYMBOL(dm_kunit_alloc_dc_state);
struct clk_mgr *dm_kunit_alloc_clk_mgr(struct kunit *test)
{
struct clk_mgr *clk_mgr;
struct clk_mgr_funcs *funcs;
clk_mgr = kunit_kzalloc(test, sizeof(*clk_mgr), GFP_KERNEL);
KUNIT_ASSERT_NOT_NULL(test, clk_mgr);
funcs = kunit_kzalloc(test, sizeof(*funcs), GFP_KERNEL);
KUNIT_ASSERT_NOT_NULL(test, funcs);
clk_mgr->funcs = funcs;
return clk_mgr;
}
EXPORT_SYMBOL(dm_kunit_alloc_clk_mgr);
void dm_kunit_add_stream_to_state(struct kunit *test, struct dc_state *state,
unsigned int index, struct dc_link *link)
{

View File

@@ -13,6 +13,7 @@
struct amdgpu_device;
struct amdgpu_display_manager;
struct amdgpu_dm_connector;
struct clk_mgr;
struct dc;
struct dc_link;
struct dc_state;
@@ -25,6 +26,8 @@ struct dc_link *dm_kunit_alloc_link(struct kunit *test);
struct dc_link *dm_kunit_alloc_link_with_ctx(struct kunit *test);
struct amdgpu_display_manager *dm_kunit_alloc_dm(struct kunit *test);
struct drm_device *dm_kunit_alloc_drm_with_connector_list(struct kunit *test);
struct dc_state *dm_kunit_alloc_dc_state(struct kunit *test);
struct clk_mgr *dm_kunit_alloc_clk_mgr(struct kunit *test);
struct dc_stream_state *dm_kunit_alloc_stream(struct kunit *test,
struct dc_link *link);
void dm_kunit_add_stream_to_state(struct kunit *test, struct dc_state *state,

View File

@@ -86,6 +86,7 @@ bool dal_bios_parser_init_cmd_tbl_helper2(
case DCN_VERSION_4_01:
case DCN_VERSION_4_2:
case DCN_VERSION_4_2B:
case DCN_VERSION_6_0:
*h = dal_cmd_tbl_helper_dce112_get_table2();
return true;

View File

@@ -195,4 +195,13 @@ AMD_DAL_CLK_MGR_DCN42B = $(addprefix $(AMDDALPATH)/dc/clk_mgr/dcn42b/,$(CLK_MGR_
AMD_DISPLAY_FILES += $(AMD_DAL_CLK_MGR_DCN42B)
###############################################################################
# DCN60
###############################################################################
CLK_MGR_DCN60 = dcn60_clk_mgr.o dcn60_clk_mgr_smu_msg.o
AMD_DAL_CLK_MGR_DCN60 = $(addprefix $(AMDDALPATH)/dc/clk_mgr/dcn60/,$(CLK_MGR_DCN60))
AMD_DISPLAY_FILES += $(AMD_DAL_CLK_MGR_DCN60)
endif

View File

@@ -50,6 +50,7 @@
#include "dcn401/dcn401_clk_mgr.h"
#include "dcn42/dcn42_clk_mgr.h"
#include "dcn42b/dcn42b_clk_mgr.h"
#include "dcn60/dcn60_clk_mgr.h"
int clk_mgr_helper_get_active_display_cnt(
struct dc *dc,
@@ -387,6 +388,17 @@ struct clk_mgr *dc_clk_mgr_create(struct dc_context *ctx, struct pp_smu_funcs *p
return &clk_mgr->base.base;
}
break;
case AMDGPU_FAMILY_GC_13_0_1: {
struct clk_mgr_internal *clk_mgr = dcn60_clk_mgr_construct(ctx, dccg);
if (clk_mgr == NULL) {
BREAK_TO_DEBUGGER();
return NULL;
}
return &clk_mgr->base;
}
#endif /* CONFIG_DRM_AMD_DC_FP */
default:
ASSERT(0); /* Unknown Asic */
@@ -451,6 +463,9 @@ void dc_destroy_clk_mgr(struct clk_mgr *clk_mgr_base)
case AMDGPU_FAMILY_GC_11_5_4:
dcn42_clk_mgr_destroy(clk_mgr);
break;
case AMDGPU_FAMILY_GC_13_0_1:
dcn60_clk_mgr_destroy(clk_mgr);
break;
default:
break;

View File

@@ -220,6 +220,7 @@ void dcn42_update_clocks(struct clk_mgr *clk_mgr_base,
bool update_dispclk = false;
bool dpp_clock_lowered = false;
bool has_active_display;
int actual_dtbclk = 0;
if (dc->work_arounds.skip_clock_update)
return;
@@ -260,8 +261,9 @@ void dcn42_update_clocks(struct clk_mgr *clk_mgr_base,
* For dcn42b (no dtbclk hardware), init_clk_states sets dtbclk_en=false and
* new_clocks->dtbclk_en should always be false, so this block never executes.
*/
if (!clk_mgr_base->clks.dtbclk_en && new_clocks->dtbclk_en) {
int actual_dtbclk = 0;
actual_dtbclk = dcn42_get_clock_freq_from_clkip(clk_mgr_base, clock_type_dtbclk);
if (new_clocks->dtbclk_en && actual_dtbclk < 590000) {
dcn42_update_clocks_update_dtb_dto(clk_mgr, context, new_clocks->ref_dtbclk_khz);
dcn42_smu_set_dtbclk(clk_mgr, true);
@@ -343,7 +345,6 @@ void dcn42_update_clocks(struct clk_mgr *clk_mgr_base,
dcn42_update_clocks_update_dtb_dto(clk_mgr, context, new_clocks->ref_dtbclk_khz);
clk_mgr_base->clks.ref_dtbclk_khz = new_clocks->ref_dtbclk_khz;
}
if (dpp_clock_lowered) {
// increase per DPP DTO before lowering global dppclk
dcn42_update_clocks_update_dpp_dto(clk_mgr, context, safe_to_lower);
@@ -1051,7 +1052,27 @@ void dcn42_get_smu_clocks(struct clk_mgr_internal *clk_mgr_int)
dm_helpers_free_gpu_mem(clk_mgr_base->ctx, DC_MEM_ALLOC_TYPE_GART,
smu_dpm_clks.dpm_clks);
}
void dcn42_request_dtbclk(struct clk_mgr *clk_mgr_base, bool enable)
{
struct clk_mgr_internal *clk_mgr = TO_CLK_MGR_INTERNAL(clk_mgr_base);
/*pmfw might turn off dtblck based on allow_dtbstop*/
clk_mgr_base->clks.dtbclk_en = false;
if (enable) {
int actual_dtbclk = 0;
dcn42_smu_set_dtbclk(clk_mgr, true);
actual_dtbclk = dcn42_get_clock_freq_from_clkip(clk_mgr_base, clock_type_dtbclk);
if (actual_dtbclk > 590000) {
clk_mgr_base->clks.ref_dtbclk_khz = actual_dtbclk;
clk_mgr_base->clks.dtbclk_en = true;
}
} else {
clk_mgr_base->clks.dtbclk_en = false;
dcn42_smu_set_dtbclk(clk_mgr, false);
}
}
static struct clk_mgr_funcs dcn42_funcs = {
.get_dp_ref_clk_frequency = dce12_get_dp_ref_freq_khz,
.get_dtb_ref_clk_frequency = dcn31_get_dtb_ref_freq_khz,
@@ -1065,6 +1086,7 @@ static struct clk_mgr_funcs dcn42_funcs = {
.get_max_clock_khz = dcn42_get_max_clock_khz,
.get_dispclk_from_dentist = dcn42_get_dispclk_from_dentist,
.is_smu_present = dcn42_is_smu_present,
.request_dtbclk = dcn42_request_dtbclk,
.notify_cstate_disable = dcn42_notify_cstate_disable,
};

View File

@@ -84,4 +84,5 @@ void dcn42_get_dpm_table_from_smu(struct clk_mgr_internal *clk_mgr, struct dcn42
void dcn42_get_smu_clocks(struct clk_mgr_internal *clk_mgr_int);
void dcn42_update_clocks_fpga(struct clk_mgr *clk_mgr, struct dc_state *context, bool safe_to_lower);
int dcn42_get_dispclk_from_dentist(struct clk_mgr *clk_mgr_base);
void dcn42_request_dtbclk(struct clk_mgr *clk_mgr_base, bool enable);
#endif //__DCN42_CLK_MGR_H__

View File

@@ -0,0 +1,389 @@
// SPDX-License-Identifier: MIT
//
// Copyright 2026 Advanced Micro Devices, Inc.
#ifndef DALSMC_H
#define DALSMC_H
/**
* @file dalsmc.h
*
* @brief VBIOS and DAL to PMFW Interface
*
* Clients: VBIOS and DAL
* Protocols: dalsmc
*
* @date 2016 - 2026
*/
/**
* @mainpage PMFW-DAL Message Interface
*
* The protocol uses six registers:
*
* - MSG_REG write the message ID (DALSMC_MSG_*) to trigger the transaction
* - ARG_REG_0 input argument Reg0; also carries response data on completion
* - ARG_REG_1 input argument Reg1
* - ARG_REG_2 input argument Reg2
* - ARG_REG_3 input argument Reg3
* - RESP_REG poll until non-zero; value is a DALSMC_Result_* response code
*
* Programming sequence:
* 1. Clear RESP_REG to 0
* 2. Write input arguments to ARG_REG_0..3
* 3. Write message ID to MSG_REG (triggers PMFW interrupt)
* 4. Poll RESP_REG until non-zero value is the result code
* 5. Read response data from ARG_REG_0 (message-specific)
*
* For payloads too large for the four argument registers, the protocol supports
* DRAM table transfers where DAL allocates a DRAM buffer and exchanges bulk data
* with PMFW through system memory.
*
* This documentation contains the subsections:\n\n
* @ref ResponseCodes\n
* @ref Messages\n
* @ref DramTables\n
*/
#define DALSMC_VERSION 0x1
/** @defgroup ResponseCodes PMFW Response Codes
* @{
*/
// SMU Response Codes:
#define DALSMC_Result_OK 0x01
#define DALSMC_Result_Failed 0xFF
#define DALSMC_Result_UnknownCmd 0xFE
#define DALSMC_Result_CmdRejectedPrereq 0xFD
#define DALSMC_Result_CmdRejectedBusy 0xFC
/** @} */
/** @defgroup Messages Message definitions
* @{
*/
/** Generic register overlay — four 32-bit C2PMSG argument registers. */
typedef struct {
uint32_t Reg0;
uint32_t Reg1;
uint32_t Reg2;
uint32_t Reg3;
} DALSMC_args_t;
/**
* DALSMC_MSG_TestMessage - Test interface connectivity.
*
* Echos back the argument value incremented by 1. Use to verify the mailbox
* is functional before sending real messages.
*
* Request: TestValue arbitrary test integer
* Response: Reg0 TestValue + 1
*/
#define DALSMC_MSG_TestMessage 0x01
typedef union {
struct {
uint32_t TestValue;
uint32_t Reserved[3];
};
DALSMC_args_t Args;
} DALSMC_TestMessage_arg_t;
/**
* DALSMC_MSG_GetMsgHeaderVersion - Query the DALSMC header version running on PMFW.
*
* DAL uses the returned version to determine which messages are supported in
* environments that require backwards compatibility.
*
* Request: (none)
* Response: Reg0 DALSMC_VERSION value compiled into PMFW
*/
#define DALSMC_MSG_GetMsgHeaderVersion 0x02
/**
* DALSMC_MSG_TransferTableSmu2Dram - Transfer a PMFW table into DRAM.
* DALSMC_MSG_TransferTableDram2Smu - Transfer a DRAM buffer into PMFW.
*
* Both directions use the same argument layout. The DRAM address must be set
* beforehand (AddrLow / AddrHigh are the GPU MC address bits [31:0] / [63:32]).
*
* Smu2Dram supported tables: TABLE_DAL_INIT (DPM clocks + UTM QoS + memory config)
* Dram2Smu supported tables: TABLE_SOC_UTM (debug override of UTM QoS parameters)
*
* Request: TableId table identifier (TABLE_* defines below)
* AddrLow GPU MC address bits [31:0] of destination/source buffer
* AddrHigh GPU MC address bits [63:32] of destination/source buffer
* Response: (none beyond result code)
*/
#define DALSMC_MSG_TransferTableSmu2Dram 0x03
#define DALSMC_MSG_TransferTableDram2Smu 0x04
typedef union {
struct {
uint32_t TableId;
uint32_t AddrLow;
uint32_t AddrHigh;
uint32_t Reserved;
};
DALSMC_args_t Args;
} DALSMC_TransferTable_arg_t;
/**
* DALSMC_MSG_SetHardMinByFreq - Set a lower bound frequency constraint on a PPCLK.
*
* Response does not indicate that the effective clock has already been raised to
* meet the minimum requirement; poll DALSMC_MSG_ReturnHardMinStatus for the status
* of the request.
*
* Supported clocks: SOCCLK, DISPCLK, DPPCLK, DCFCLK, DTBCLK.
*
* Request: FreqKhz[23:0] target minimum frequency in kHz (0 to ~16.7 GHz)
* Ppclk[31:24] PPCLK_e clock identifier
* Response: (none beyond result code)
*/
#define DALSMC_MSG_SetHardMinByFreq 0x05
typedef union {
struct {
uint32_t FreqKhz : 24;
uint32_t Ppclk : 8;
uint32_t Reserved[3];
};
DALSMC_args_t Args;
} DALSMC_SetHardMinByFreq_arg_t;
/**
* DALSMC_MSG_SetMinDeepSleepDcfclk - Set the minimum DCFCLK frequency in deep sleep.
*
* Request: MinDcfclkMhz minimum DCFCLK frequency in MHz during deep sleep
* Response: (none beyond result code)
*/
#define DALSMC_MSG_SetMinDeepSleepDcfclk 0x06
typedef union {
struct {
uint32_t MinDcfclkMhz;
uint32_t Reserved[3];
};
DALSMC_args_t Args;
} DALSMC_SetMinDeepSleepDcfclk_arg_t;
/**
* DALSMC_MSG_BacoAudioD3PME - Wake the audio block from D3/BACO.
*
* Triggers PMFW to bring the AZ (audio) block out of its D3 power state.
* No arguments or response data; result code indicates success.
*/
#define DALSMC_MSG_BacoAudioD3PME 0x07
/**
* DALSMC_MSG_ReturnHardMinStatus - Query outstanding hard-min request status.
*
* Returns a bitmask reporting which PPCLK hard-min requests have been satisfied
* by the arbiter. Each bit position corresponds to the matching PPCLK_e value.
* A set bit means the arbiter has reached or exceeded the requested minimum.
*
* Request: (none)
* Response: Reg0 bitmask of satisfied PPCLKs (bit N set PPCLK_e N is satisfied)
*/
#define DALSMC_MSG_ReturnHardMinStatus 0x08
/**
* DALSMC_MSG_IndicatePstateStatus - Indicate to PMFW various DMU behaviors required
* to support UCLK P-state, for example whether or not DMU needs to modulate refresh
* rate to perform UCLK switches.
*
* Request: WaitResp[0] DAL requires a synchronous response before proceeding
* DrrEnable[1] DRR (dynamic refresh rate modulation) is active
* AltCh[2] alternate-channel mode is active
* AllowUclk[16] DCN can tolerate UCLK P-state switches
* AllowFclk[17] DCN can tolerate FCLK P-state switches
* Response: (none beyond result code)
*/
#define DALSMC_MSG_IndicatePstateStatus 0x09
typedef union {
struct {
uint32_t WaitResp : 1;
uint32_t DrrEnable : 1;
uint32_t AltCh : 1;
uint32_t Reserved : 13;
uint32_t AllowUclk : 1;
uint32_t AllowFclk : 1;
uint32_t Reserved1 : 14;
uint32_t Reserved2[3];
};
DALSMC_args_t Args;
} DALSMC_IndicatePstateStatus_arg_t;
/**
* DALSMC_MSG_UpdateUTMQoSRequest - Update the active UTM QoS bandwidth/latency request.
*
* Passes the current display bandwidth and latency requirements to PMFW so it
* can select the appropriate SoC operating point (UCLK/FCLK level) from the
* UTM table. Called whenever the display configuration changes.
*
* The QoS requirement must take effect before PMFW sends its response.
*
* Request: LatencySopIndex index into the UTM SOP table that satisfies latency
* NominalBandwidthKBps required nominal (average) bandwidth in KB/s
* UrgentBandwidthKBps required urgent bandwidth in KB/s
* LsdmaBandwidthKBps required LSDMA bandwidth in KB/s
* Response: (none beyond result code)
*/
#define DALSMC_MSG_UpdateUTMQoSRequest 0x0A
typedef union {
struct {
uint32_t LatencySopIndex;
uint32_t NominalBandwidthKBps;
uint32_t UrgentBandwidthKBps;
uint32_t LsdmaBandwidthKBps;
};
DALSMC_args_t Args;
} DALSMC_UpdateUTMQoSRequest_arg_t;
/**
* DALSMC_MSG_SetDisplayIdleOptimizations - Notify PMFW of DCN idle-state conditions.
*
* Indicates which display-side power optimizations are currently safe to apply.
* PMFW uses these flags to gate deeper SoC power states such as S0i2.
*
* Request: DfRequestDisabled[0] DF (data fabric) requests from DCN are disabled
* PhyRefClkOff[1] PHY reference clock has been gated off
* S0i2Rdy[2] DCN is ready for the system to enter S0i2
* Response: (none beyond result code)
*/
#define DALSMC_MSG_SetDisplayIdleOptimizations 0x0B
typedef union {
struct {
uint32_t DfRequestDisabled : 1;
uint32_t PhyRefClkOff : 1;
uint32_t S0i2Rdy : 1;
uint32_t Reserved : 29;
uint32_t Reserved1[3];
};
DALSMC_args_t Args;
} DALSMC_SetDisplayIdleOptimizations_arg_t;
/**
* DALSMC_MSG_SetStutterEfficiency - Report DCN stutter efficiency to PMFW.
*
* Informs PMFW of the current stutter utilisation for base and low-power stutter
* modes so PMFW can adjust memory power policy accordingly.
*
* Base mode lower enter+exit latency (PHY LP1, no UCIE LP).
* Low-power mode higher enter+exit latency (PHY LP2, UCIE LP1).
*
* Request: BaseEfficiencyPct[7:0] stutter efficiency % in base mode
* LowPowerEfficiencyPct[15:8] stutter efficiency % in low-power mode
* Response: (none beyond result code)
*/
#define DALSMC_MSG_SetStutterEfficiency 0x0C
typedef union {
struct {
uint32_t BaseEfficiencyPct : 8;
uint32_t LowPowerEfficiencyPct : 8;
uint32_t Reserved : 16;
uint32_t Reserved1[3];
};
DALSMC_args_t Args;
} DALSMC_SetStutterEfficiency_arg_t;
#define DALSMC_Message_Count 0x0D ///< Total number of messages
/** @} */
/** @defgroup DramTables DRAM Tables
* @brief Bulk data structures exchanged between DAL and PMFW via system DRAM.
*
* Used when the payload exceeds the four 32-bit C2PMSG argument registers
* (DALSMC_args_t). DAL allocates a DRAM buffer, passes its address through
* DALSMC_TransferTable_arg_t, and issues either a
* DALSMC_MSG_TransferTableSmu2Dram or DALSMC_MSG_TransferTableDram2Smu message.
* @{
*/
typedef struct {
uint32_t LoadLevelCount : 4;
uint32_t SopCount : 4;
uint32_t Reserved : 24;
} SocUtmTableHeader_t;
typedef struct {
uint32_t UrgentRampPs;
uint32_t TripPs;
uint32_t MetaTripToMemPs;
uint32_t MaxReqLatencyUrgPs;
uint32_t AvgReqLatencyUrgPs;
uint32_t MaxReqLatencyNonUrgPs;
uint32_t AvgReqLatencyNonUrgPs;
uint32_t DfResponseTimePs;
uint32_t UrgentBandwidthKBps;
uint32_t NominalBandwidthKBps;
uint32_t LsdmaBandwidthKBps;
uint32_t Reserved[1];
} SocUtmSopEntry_t;
typedef struct {
uint32_t SmuVersion;
uint32_t SmuDriverIfVersion;
uint32_t Reserved[2];
} DalInitHeader_t;
#define NUM_CLOCK_LEVELS 8
typedef struct {
uint32_t Clocks[NUM_CLOCK_LEVELS];
uint32_t DcMaxClock;
uint32_t NumClocks;
uint32_t Reserved[2];
} DpmClock_t;
typedef struct {
uint32_t NumUmcChannels;
uint32_t Reserved[3];
} MemoryConfig_t;
/**
* TABLE_SOC_UTM - SoC UTM QoS table.
*
* Provides per-load-level bandwidth and latency bounds used by the display
* engine to meet memory access requirements.
*
* Normal path: embedded in DalInitTable_t, fetched once via
* DALSMC_MSG_TransferTableSmu2Dram(TABLE_DAL_INIT).
*
* Override path (debug only): TABLE_SOC_UTM via
* DALSMC_MSG_TransferTableDram2Smu to override custom QoS parameters into PMFW.
*/
#define TABLE_SOC_UTM 0xC
/* TODO: rename back to MAX_UTM_SOP_COUNT once utm_qos_model_types.h conflict is resolved */
#define DALSMC_MAX_UTM_SOP_COUNT 16
#define MAX_UTM_LOAD_LEVEL_COUNT 16
#define UTM_LOAD_LEVEL_INDEX_IDLE 0
#define UTM_LOAD_LEVEL_INDEX_ACTIVE_ALTERNATE_PSTATE 1
#define UTM_LOAD_LEVEL_INDEX_ACTIVE 2
#define UTM_SOP_ENTRIES_OFFSET(LoadLevel, SopIndex) \
(sizeof(SocUtmTableHeader_t) \
+ ((LoadLevel) * DALSMC_MAX_UTM_SOP_COUNT \
+ (SopIndex)) * sizeof(SocUtmSopEntry_t))
typedef struct {
SocUtmTableHeader_t Header;
SocUtmSopEntry_t Sops[MAX_UTM_LOAD_LEVEL_COUNT][DALSMC_MAX_UTM_SOP_COUNT];
} SocUtmTable_t;
/**
* TABLE_DAL_INIT - Full TABLE_DAL_INIT payload transferred from SMU to DRAM.
*/
#define TABLE_DAL_INIT 0xD
#define MAX_PPCLK_COUNT 12
#define DPM_CLOCK_OFFSET(Ppclk) \
(sizeof(DalInitHeader_t) + (Ppclk) * sizeof(DpmClock_t))
#define UTM_TABLE_OFFSET \
(sizeof(DalInitHeader_t) + MAX_PPCLK_COUNT * sizeof(DpmClock_t))
#define MEMORY_CONFIG_OFFSET \
(sizeof(DalInitHeader_t) + MAX_PPCLK_COUNT * sizeof(DpmClock_t) \
+ sizeof(SocUtmTable_t))
typedef struct {
DalInitHeader_t Header;
DpmClock_t PPClocks[MAX_PPCLK_COUNT];
SocUtmTable_t UtmTable;
MemoryConfig_t MemoryConfig;
} DalInitTable_t;
/** @} */
#endif /* DALSMC_H */

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,150 @@
// SPDX-License-Identifier: MIT
//
// Copyright 2025 Advanced Micro Devices, Inc.
#ifndef __DCN60_CLK_MGR_H_
#define __DCN60_CLK_MGR_H_
#ifdef CONFIG_DRM_AMD_DC_FP
#include "bounding_boxes/utm_qos_model_types.h"
#include "bounding_boxes/utm_qos_model_dchub_v3.h"
#endif
union dcn60_clk_mgr_block_sequence_params {
struct {
/* inputs */
uint32_t ppclk;
uint16_t freq_mhz;
/* outputs */
int *response;
} update_hardmin_params;
struct {
/* inputs */
uint32_t ppclk;
int freq_khz;
/* outputs */
int *response;
} update_hardmin_optimized_params;
struct {
/* inputs */
uint16_t freq_mhz;
} update_deep_sleep_dcfclk_params;
struct {
/* inputs */
bool allow_fclk;
bool allow_uclk;
bool wait_resp;
bool drr_enable;
bool alt_ch_enable;
} indicate_pstate_status_params;
struct {
/* inputs */
struct dc_state *context;
int *ref_dppclk_khz;
bool safe_to_lower;
} update_dppclk_dto_params;
struct {
/* inputs */
struct dc_state *context;
int *ref_dtbclk_khz;
} update_dtbclk_dto_params;
struct {
/* inputs */
struct dc_state *context;
} update_dentist_params;
struct {
/* inputs */
struct dmcu *dmcu;
unsigned int wait;
} update_psr_wait_loop_params;
struct {
/* inputs */
uint8_t base_efficiency;
uint8_t low_power_efficiency;
} update_stutter_efficiency_params;
struct {
unsigned int utm_urgent_bandwidth_lb_KBps;
unsigned int utm_nominal_bandwidth_lb_KBps;
unsigned int utm_lsdma_bandwidth_lb_KBps;
unsigned int utm_latency_ub_index;
} update_utm_qos_request_params;
};
enum dcn60_clk_mgr_block_sequence_func {
CLK_MGR60_READ_CLOCKS_FROM_DENTIST,
CLK_MGR60_UPDATE_HARDMIN_PPCLK,
CLK_MGR60_UPDATE_HARDMIN_PPCLK_OPTIMIZED,
CLK_MGR60_UPDATE_DEEP_SLEEP_DCFCLK,
CLK_MGR60_INDICATE_PSTATE_STATUS,
CLK_MGR60_UPDATE_DPPCLK_DTO,
CLK_MGR60_UPDATE_DTBCLK_DTO,
CLK_MGR60_UPDATE_DENTIST,
CLK_MGR60_UPDATE_PSR_WAIT_LOOP,
CLK_MGR60_UPDATE_STUTTER_EFFICIENCY,
CLK_MGR60_UPDATE_UTM_QOS_REQUEST
};
struct dcn60_clk_mgr_block_sequence {
union dcn60_clk_mgr_block_sequence_params params;
enum dcn60_clk_mgr_block_sequence_func func;
};
struct dcn60_update_action {
/** clk_mgr state needs updating */
bool update;
/** SMU message required */
bool send_message;
};
struct dcn60_enablement_action {
/** feature transitioning to enabled */
bool enable;
/** feature transitioning to disabled */
bool disable;
/** SMU message required for this transition */
bool send_message;
};
/**
* struct dcn60_bandwidth_clocks_update_action - captures what clock and
* p-state changes are needed for a bandwidth update, separating the action
* logic from block sequence construction and state mutation.
*/
struct dcn60_bandwidth_clocks_update_action {
struct dcn60_update_action dcfclk;
struct dcn60_update_action deep_sleep_dcfclk;
struct dcn60_update_action socclk;
struct dcn60_update_action stutter;
struct dcn60_update_action utm_qos;
struct dcn60_enablement_action uclk_pstate;
struct dcn60_enablement_action fclk_pstate;
struct dcn60_enablement_action fams;
struct dcn60_enablement_action alt_ch;
};
struct dcn60_clk_mgr {
struct clk_mgr_internal base;
struct dcn60_clk_mgr_block_sequence block_sequence[DCN401_CLK_MGR_MAX_SEQUENCE_SIZE];
#ifdef CONFIG_DRM_AMD_DC_FP
struct utm_qos_model utm_qos_model;
struct utm_qos_model_dchub_v3 dchub_v3;
#endif
unsigned int num_block_sequence_steps;
};
void dcn60_init_clocks(struct clk_mgr *clk_mgr_base);
struct block_sequence_state;
void dcn60_build_clock_update_for_bls(
struct clk_mgr *clk_mgr_base,
struct dc_state *context,
bool safe_to_lower,
struct block_sequence_state *seq_state);
struct clk_mgr_internal *dcn60_clk_mgr_construct(struct dc_context *ctx,
struct dccg *dccg);
void dcn60_clk_mgr_destroy(struct clk_mgr_internal *clk_mgr);
#endif /* __DCN60_CLK_MGR_H_ */

View File

@@ -0,0 +1,369 @@
// SPDX-License-Identifier: MIT
//
// Copyright 2025 Advanced Micro Devices, Inc.
#include "dcn60_clk_mgr_smu_msg.h"
#include "clk_mgr_internal.h"
#include "reg_helper.h"
#include "dalsmc.h"
#include "dcn401/dcn401_smu14_driver_if.h"
/* MMIO = MP1_BASE__INST0_SEG1 (0x16200 from at2_offset.h)
* + regMP1_SMN_C2PMSG_N (sequential from 0xA2)
* MSG=C2PMSG_98, RESP=C2PMSG_99, ARG0..3=C2PMSG_100..103
*/
#ifndef mmDAL_MSG_REG
#define mmDAL_MSG_REG 0x162A2
#endif
#ifndef mmDAL_RESP_REG
#define mmDAL_RESP_REG 0x162A3
#endif
#ifndef mmDAL_ARG_REG
#define mmDAL_ARG_REG 0x162A4
#endif
#ifndef mmDAL_ARG_REG_0
#define mmDAL_ARG_REG_0 0x162A4
#endif
#ifndef mmDAL_ARG_REG_1
#define mmDAL_ARG_REG_1 0x162A5
#endif
#ifndef mmDAL_ARG_REG_2
#define mmDAL_ARG_REG_2 0x162A6
#endif
#ifndef mmDAL_ARG_REG_3
#define mmDAL_ARG_REG_3 0x162A7
#endif
#define REG(reg_name) \
mm ## reg_name
#include "logger_types.h"
#define smu_print(str, ...) {DC_LOG_SMU(str, ##__VA_ARGS__); }
/**
* dcn60_smu_wait_for_response - Poll DAL_RESP_REG until non-zero.
* @clk_mgr: clock manager instance
* @delay_us: microseconds to wait between each poll iteration
* @max_retries: maximum number of poll iterations before giving up
* @total_delay_us: if non-NULL, accumulates the total poll delay
*
* Cannot use REG_WAIT because the condition is "not equal to zero"
* and the translation in msg_if.h is incompatible with REG_WAIT.
*
* Return: RESP_REG value (non-zero on success, 0 on timeout)
*/
static uint32_t dcn60_smu_wait_for_response(struct clk_mgr_internal *clk_mgr,
unsigned int delay_us, unsigned int max_retries,
unsigned int *total_delay_us)
{
uint32_t reg = 0;
if (total_delay_us)
*total_delay_us = 0;
do {
reg = REG_READ(DAL_RESP_REG);
if (reg)
break;
if (delay_us >= 1000)
msleep(delay_us/1000);
else if (delay_us > 0)
udelay(delay_us);
if (total_delay_us)
*total_delay_us += delay_us;
} while (max_retries--);
return reg;
}
/**
* dcn60_smu_send_msg_with_args - Send a DALSMC message with four argument registers.
* @clk_mgr: clock manager instance
* @msg_id: DALSMC_MSG_* message identifier
* @args: four-register argument payload
* @param_out: if non-NULL, receives ARG_REG_0 on success
* @total_delay_us: if non-NULL, accumulates the total poll delay
*/
static bool dcn60_smu_send_msg_with_args(struct clk_mgr_internal *clk_mgr,
uint32_t msg_id, DALSMC_args_t args, uint32_t *param_out,
unsigned int *total_delay_us)
{
unsigned int delay1_us = 0, delay2_us = 0;
if (total_delay_us)
*total_delay_us = 0;
/* Wait for response register to be ready */
dcn60_smu_wait_for_response(clk_mgr, 10, 200000,
total_delay_us ? &delay1_us : NULL);
smu_print("SMU msg 0x%x enter: arg0=0x%08x arg1=0x%08x arg2=0x%08x arg3=0x%08x\n",
msg_id, args.Reg0, args.Reg1, args.Reg2, args.Reg3);
TRACE_SMU_MSG_ENTER(msg_id, args.Reg0, clk_mgr->base.ctx);
/* Clear response register */
REG_WRITE(DAL_RESP_REG, 0);
/* Write all four argument registers */
REG_WRITE(DAL_ARG_REG_0, args.Reg0);
REG_WRITE(DAL_ARG_REG_1, args.Reg1);
REG_WRITE(DAL_ARG_REG_2, args.Reg2);
REG_WRITE(DAL_ARG_REG_3, args.Reg3);
/* Trigger the message transaction by writing the message ID */
REG_WRITE(DAL_MSG_REG, msg_id);
/* Wait for response */
if (dcn60_smu_wait_for_response(clk_mgr, 10, 200000,
total_delay_us ? &delay2_us : NULL) == DALSMC_Result_OK) {
if (param_out)
*param_out = REG_READ(DAL_ARG_REG_0);
if (total_delay_us)
*total_delay_us = delay1_us + delay2_us;
smu_print("SMU msg 0x%x exit: ok resp=0x%08x\n",
msg_id, param_out ? *param_out : 0);
TRACE_SMU_MSG_EXIT(true, param_out ? *param_out : 0, clk_mgr->base.ctx);
return true;
}
if (total_delay_us)
*total_delay_us = delay1_us + 2000000;
smu_print("SMU msg 0x%x exit: failed\n", msg_id);
TRACE_SMU_MSG_EXIT(false, 0, clk_mgr->base.ctx);
return false;
}
static unsigned int dcn60_smu_get_hard_min_status(struct clk_mgr_internal *clk_mgr,
bool *no_timeout, unsigned int *total_delay_us)
{
DALSMC_args_t args = {};
uint32_t response = 0;
*no_timeout = dcn60_smu_send_msg_with_args(clk_mgr,
DALSMC_MSG_ReturnHardMinStatus, args, &response,
total_delay_us);
smu_print("SMU Get hard min status: no_timeout %d delay %d us clk bits %x\n",
*no_timeout, *total_delay_us, response);
return response;
}
static bool dcn60_smu_wait_hard_min_status(struct clk_mgr_internal *clk_mgr, uint32_t ppclk)
{
const unsigned int max_delay_us = 1000000;
unsigned int hardmin_status_mask = (1 << ppclk);
unsigned int total_delay_us = 0;
bool hardmin_done = false;
while (!hardmin_done && total_delay_us < max_delay_us) {
unsigned int hardmin_status;
unsigned int read_total_delay_us;
bool no_timeout;
if (!hardmin_done && total_delay_us > 0) {
/* hardmin not yet fulfilled, wait 500us and retry*/
udelay(500);
total_delay_us += 500;
smu_print("SMU Wait hard min status for %d us\n", total_delay_us);
}
hardmin_status = dcn60_smu_get_hard_min_status(clk_mgr, &no_timeout, &read_total_delay_us);
total_delay_us += read_total_delay_us;
hardmin_done = hardmin_status & hardmin_status_mask;
}
return hardmin_done;
}
/* TODO: update callers to pass kHz directly for finer granularity
* now that the DALSMC interface supports 24-bit kHz encoding.
*/
unsigned int dcn60_smu_set_hard_min_by_freq(struct clk_mgr_internal *clk_mgr, uint32_t clk, uint16_t freq_mhz)
{
DALSMC_SetHardMinByFreq_arg_t arg = {};
uint32_t response = 0;
bool hard_min_done = false;
smu_print("SMU Set hard min by freq: clk = %d, freq_mhz = %d MHz\n", clk, freq_mhz);
/* New interface encodes frequency in kHz (24-bit) and PPCLK in bits [31:24] */
arg.FreqKhz = (uint32_t)freq_mhz * 1000;
arg.Ppclk = clk;
dcn60_smu_send_msg_with_args(clk_mgr,
DALSMC_MSG_SetHardMinByFreq, arg.Args, &response, NULL);
/* wait until hardmin acknowledged */
hard_min_done = dcn60_smu_wait_hard_min_status(clk_mgr, clk);
smu_print("SMU Frequency set = %d KHz hard_min_done %d\n", response, hard_min_done);
return response;
}
void dcn60_smu_set_stutter_efficiency(struct clk_mgr_internal *clk_mgr, uint8_t base_efficiency, uint8_t low_power_efficiency)
{
DALSMC_SetStutterEfficiency_arg_t arg = {};
smu_print("SMU Set stutter efficiencies: base(LP1) = %d percent, low power(LP2) = %d percent\n",
base_efficiency, low_power_efficiency);
arg.BaseEfficiencyPct = base_efficiency;
arg.LowPowerEfficiencyPct = low_power_efficiency;
dcn60_smu_send_msg_with_args(clk_mgr,
DALSMC_MSG_SetStutterEfficiency, arg.Args, NULL, NULL);
}
void dcn60_smu_set_min_deep_sleep_dcfclk(struct clk_mgr_internal *clk_mgr, uint32_t freq_mhz)
{
DALSMC_SetMinDeepSleepDcfclk_arg_t arg = {};
smu_print("SMU Set min deep sleep dcfclk: freq_mhz = %d MHz\n", freq_mhz);
arg.MinDcfclkMhz = freq_mhz;
dcn60_smu_send_msg_with_args(clk_mgr,
DALSMC_MSG_SetMinDeepSleepDcfclk, arg.Args, NULL, NULL);
}
void dcn60_smu_set_pme_workaround(struct clk_mgr_internal *clk_mgr)
{
DALSMC_args_t args = {};
smu_print("SMU Set PME workaround (BacoAudioD3PME)\n");
dcn60_smu_send_msg_with_args(clk_mgr,
DALSMC_MSG_BacoAudioD3PME, args, NULL, NULL);
}
void dcn60_smu_indicate_pstate_status(struct clk_mgr_internal *clk_mgr,
bool allow_fclk, bool allow_uclk,
bool wait_resp, bool drr_enable, bool alt_ch_enable)
{
DALSMC_IndicatePstateStatus_arg_t arg = {};
smu_print("SMU Indicate pstate status: allow_fclk=%d allow_uclk=%d wait_resp=%d drr_enable=%d alt_ch_enable=%d\n",
allow_fclk, allow_uclk, wait_resp, drr_enable, alt_ch_enable);
arg.AllowFclk = allow_fclk ? 1 : 0;
arg.AllowUclk = allow_uclk ? 1 : 0;
arg.WaitResp = wait_resp ? 1 : 0;
arg.DrrEnable = drr_enable ? 1 : 0;
arg.AltCh = alt_ch_enable ? 1 : 0;
dcn60_smu_send_msg_with_args(clk_mgr,
DALSMC_MSG_IndicatePstateStatus, arg.Args, NULL, NULL);
}
static bool dcn60_smu_transfer_table_smu_2_dram(struct clk_mgr_internal *clk_mgr,
uint32_t table_id, long long dram_addr)
{
DALSMC_TransferTable_arg_t arg = {};
smu_print("SMU TransferTableSmu2Dram: table_id=0x%x addr=0x%08x_%08x\n",
table_id,
(uint32_t)(dram_addr >> 32),
(uint32_t)(dram_addr & 0xFFFFFFFF));
arg.TableId = table_id;
arg.AddrLow = (uint32_t)(dram_addr & 0xFFFFFFFF);
arg.AddrHigh = (uint32_t)(dram_addr >> 32);
return dcn60_smu_send_msg_with_args(clk_mgr,
DALSMC_MSG_TransferTableSmu2Dram, arg.Args, NULL, NULL);
}
static bool dcn60_smu_transfer_table_dram_2_smu(struct clk_mgr_internal *clk_mgr,
uint32_t table_id, long long dram_addr)
{
DALSMC_TransferTable_arg_t arg = {};
smu_print("SMU TransferTableDram2Smu: table_id=0x%x addr=0x%08x_%08x\n",
table_id,
(uint32_t)(dram_addr >> 32),
(uint32_t)(dram_addr & 0xFFFFFFFF));
arg.TableId = table_id;
arg.AddrLow = (uint32_t)(dram_addr & 0xFFFFFFFF);
arg.AddrHigh = (uint32_t)(dram_addr >> 32);
return dcn60_smu_send_msg_with_args(clk_mgr,
DALSMC_MSG_TransferTableDram2Smu, arg.Args, NULL, NULL);
}
bool dcn60_smu_set_soc_utm_table(struct clk_mgr_internal *clk_mgr,
long long dram_addr)
{
return dcn60_smu_transfer_table_dram_2_smu(clk_mgr,
TABLE_SOC_UTM, dram_addr);
}
bool dcn60_smu_get_dal_init_table(struct clk_mgr_internal *clk_mgr,
const DalInitTable_t **init_table)
{
if (!dcn60_smu_transfer_table_smu_2_dram(clk_mgr,
TABLE_DAL_INIT, clk_mgr->dal_init_table_addr))
return false;
*init_table = (const DalInitTable_t *)clk_mgr->dal_init_table;
return true;
}
bool dcn60_smu_update_utm_qos_request(struct clk_mgr_internal *clk_mgr,
uint32_t latency_sop_index,
uint32_t nominal_bandwidth_KBps,
uint32_t urgent_bandwidth_KBps,
uint32_t lsdma_bandwidth_KBps)
{
DALSMC_UpdateUTMQoSRequest_arg_t arg = {};
smu_print("SMU UpdateUTMQoSRequest: sop_idx=%d nominal=%d urgent=%d lsdma=%d KBps\n",
latency_sop_index, nominal_bandwidth_KBps,
urgent_bandwidth_KBps, lsdma_bandwidth_KBps);
arg.LatencySopIndex = latency_sop_index;
arg.NominalBandwidthKBps = nominal_bandwidth_KBps;
arg.UrgentBandwidthKBps = urgent_bandwidth_KBps;
arg.LsdmaBandwidthKBps = lsdma_bandwidth_KBps;
return dcn60_smu_send_msg_with_args(clk_mgr,
DALSMC_MSG_UpdateUTMQoSRequest, arg.Args, NULL, NULL);
}
bool dcn60_smu_get_msg_header_version(struct clk_mgr_internal *clk_mgr,
uint32_t *version)
{
DALSMC_args_t args = {};
return dcn60_smu_send_msg_with_args(clk_mgr,
DALSMC_MSG_GetMsgHeaderVersion, args, version, NULL);
}
void dcn60_smu_set_display_idle_optimization(struct clk_mgr_internal *clk_mgr, bool is_idle)
{
DALSMC_SetDisplayIdleOptimizations_arg_t arg = {};
arg.DfRequestDisabled = is_idle ? 1 : 0;
arg.PhyRefClkOff = is_idle ? 1 : 0;
arg.S0i2Rdy = is_idle ? 1 : 0;
smu_print("SMU SetDisplayIdleOptimizations: DfRequestDisabled=%d PhyRefClkOff=%d S0i2Rdy=%d\n",
arg.DfRequestDisabled, arg.PhyRefClkOff, arg.S0i2Rdy);
dcn60_smu_send_msg_with_args(clk_mgr,
DALSMC_MSG_SetDisplayIdleOptimizations, arg.Args, NULL, NULL);
}

View File

@@ -0,0 +1,35 @@
// SPDX-License-Identifier: MIT
//
// Copyright 2025 Advanced Micro Devices, Inc.
#ifndef __DCN60_CLK_MGR_SMU_MSG_H_
#define __DCN60_CLK_MGR_SMU_MSG_H_
#include "os_types.h"
#include "core_types.h"
#include "dalsmc.h"
struct clk_mgr_internal;
unsigned int dcn60_smu_set_hard_min_by_freq(struct clk_mgr_internal *clk_mgr, uint32_t clk, uint16_t freq_mhz);
void dcn60_smu_set_stutter_efficiency(struct clk_mgr_internal *clk_mgr,
uint8_t base_efficiency, uint8_t low_power_efficiency);
void dcn60_smu_set_min_deep_sleep_dcfclk(struct clk_mgr_internal *clk_mgr, uint32_t freq_mhz);
void dcn60_smu_set_pme_workaround(struct clk_mgr_internal *clk_mgr);
void dcn60_smu_indicate_pstate_status(struct clk_mgr_internal *clk_mgr,
bool allow_fclk, bool allow_uclk,
bool wait_resp, bool drr_enable, bool alt_ch_enable);
bool dcn60_smu_update_utm_qos_request(struct clk_mgr_internal *clk_mgr,
uint32_t latency_sop_index,
uint32_t nominal_bandwidth_KBps,
uint32_t urgent_bandwidth_KBps,
uint32_t lsdma_bandwidth_KBps);
bool dcn60_smu_set_soc_utm_table(struct clk_mgr_internal *clk_mgr,
long long dram_addr);
bool dcn60_smu_get_dal_init_table(struct clk_mgr_internal *clk_mgr,
const DalInitTable_t **init_table);
bool dcn60_smu_get_msg_header_version(struct clk_mgr_internal *clk_mgr,
uint32_t *version);
void dcn60_smu_set_display_idle_optimization(struct clk_mgr_internal *clk_mgr, bool is_idle);
#endif /* __DCN60_CLK_MGR_SMU_MSG_H_ */

View File

@@ -3027,13 +3027,14 @@ static struct dc_update_descriptor det_surface_update(
elevate_update_type(&overall_type, UPDATE_TYPE_FAST, LOCK_DESCRIPTOR_STREAM);
}
if ((u->cm && u->cm->flags.bits.blend_enable) ||
if ((u->cm && (u->cm->flags.bits.blend_enable ||
u->cm->flags.bits.blend_enable != u->surface->cm.flags.bits.blend_enable)) ||
(u->gamma && dce_use_lut(u->plane_info ? u->plane_info->format : u->surface->format))) {
update_bits->gamma_change = 1;
elevate_update_type(&overall_type, UPDATE_TYPE_FAST, LOCK_DESCRIPTOR_STREAM);
}
if (u->cm && (u->cm->flags.bits.lut3d_enable || u->cm->flags.bits.shaper_enable)) {
if (u->cm && (u->cm->flags.bits.lut3d_enable || u->surface->cm.flags.bits.lut3d_enable)) {
update_bits->lut_3d = 1;
elevate_update_type(&overall_type, UPDATE_TYPE_FAST, LOCK_DESCRIPTOR_STREAM);
}
@@ -3075,31 +3076,19 @@ static struct dc_update_descriptor det_surface_update(
}
};
if (u->cm->flags.bits.shaper_enable != u->surface->cm.flags.bits.shaper_enable
|| u->cm->flags.bits.blend_enable != u->surface->cm.flags.bits.blend_enable
|| u->cm->flags.bits.lut3d_enable != u->surface->cm.flags.bits.lut3d_enable
|| u->cm->flags.bits.lut3d_dma_enable != u->surface->cm.flags.bits.lut3d_dma_enable) {
update_bits->mcm_transfer_function_enable_change = 1;
elevate_update_type(&overall_type, UPDATE_TYPE_FULL, LOCK_DESCRIPTOR_GLOBAL);
}
if ((u->cm->flags.all != blend_only_flags.all && u->cm->flags.all != 0) ||
(u->surface->cm.flags.all != blend_only_flags.all && u->surface->cm.flags.all != 0)) {
elevate_update_type(&overall_type, UPDATE_TYPE_FULL, LOCK_DESCRIPTOR_GLOBAL);
}
}
if (update_bits->lut_3d &&
!u->surface->cm.flags.bits.lut3d_dma_enable) {
elevate_update_type(&overall_type, UPDATE_TYPE_FULL, LOCK_DESCRIPTOR_GLOBAL);
}
if (check_config->enable_legacy_fast_update &&
(update_bits->gamma_change ||
update_bits->gamut_remap_change ||
update_bits->input_csc_change ||
update_bits->cm_hist_change ||
update_bits->coeff_reduction_change)) {
update_bits->coeff_reduction_change ||
update_bits->cursor_csc_color_matrix_change)) {
elevate_update_type(&overall_type, UPDATE_TYPE_FULL, LOCK_DESCRIPTOR_GLOBAL);
}
return overall_type;
@@ -3403,6 +3392,10 @@ static void copy_surface_update_to_plane(
srf_update->plane_info->dcc;
surface->layer_index =
srf_update->plane_info->layer_index;
surface->scaling_linearity =
srf_update->plane_info->scaling_linearity;
surface->cositing =
srf_update->plane_info->cositing;
}
if (srf_update->gamma) {
@@ -3441,46 +3434,6 @@ static void copy_surface_update_to_plane(
memcpy(&surface->cm, srf_update->cm, sizeof(surface->cm));
surface->cm.refcount = refcount;
#ifndef TRIM_CM2
/* Populate mcm_luts from cm for legacy consumers (dml2, hwseq) */
surface->mcm_luts.lut1d_func = &surface->cm.blend_func;
surface->mcm_luts.shaper = &surface->cm.shaper_func;
if (srf_update->cm->flags.bits.lut3d_dma_enable) {
surface->mcm_luts.lut3d_data.lut3d_src = DC_CM2_TRANSFER_FUNC_SOURCE_VIDMEM;
surface->mcm_luts.lut3d_data.gpu_mem_params.addr = surface->cm.lut3d_dma.addr;
surface->mcm_luts.lut3d_data.gpu_mem_params.layout =
(surface->cm.lut3d_dma.swizzle == CM_LUT_3D_SWIZZLE_LINEAR_RGB) ?
DC_CM2_GPU_MEM_LAYOUT_3D_SWIZZLE_LINEAR_RGB :
(surface->cm.lut3d_dma.swizzle == CM_LUT_3D_SWIZZLE_LINEAR_BGR) ?
DC_CM2_GPU_MEM_LAYOUT_3D_SWIZZLE_LINEAR_BGR :
DC_CM2_GPU_MEM_LAYOUT_1D_PACKED_LINEAR;
surface->mcm_luts.lut3d_data.gpu_mem_params.format_params.format =
(surface->cm.lut3d_dma.format == CM_LUT_PIXEL_FORMAT_RGBA16161616_UNORM_12MSB) ?
DC_CM2_GPU_MEM_FORMAT_16161616_UNORM_12MSB :
(surface->cm.lut3d_dma.format == CM_LUT_PIXEL_FORMAT_RGBA16161616_UNORM_12LSB) ?
DC_CM2_GPU_MEM_FORMAT_16161616_UNORM_12LSB :
DC_CM2_GPU_MEM_FORMAT_16161616_FLOAT_FP1_5_10;
surface->mcm_luts.lut3d_data.gpu_mem_params.format_params.float_params.bias =
surface->cm.lut3d_dma.bias;
surface->mcm_luts.lut3d_data.gpu_mem_params.format_params.float_params.scale =
surface->cm.lut3d_dma.scale;
surface->mcm_luts.lut3d_data.gpu_mem_params.component_order =
DC_CM2_GPU_MEM_PIXEL_COMPONENT_ORDER_RGBA;
surface->mcm_luts.lut3d_data.gpu_mem_params.size = DC_CM2_GPU_MEM_SIZE_TRANSFORMED;
surface->mcm_luts.lut3d_data.mpc_3dlut_enable = (srf_update->cm->flags.bits.lut3d_enable != 0);
} else {
surface->mcm_luts.lut3d_data.lut3d_src = DC_CM2_TRANSFER_FUNC_SOURCE_SYSMEM;
surface->mcm_luts.lut3d_data.lut3d_func = &surface->cm.lut3d_func;
}
if (srf_update->cm->flags.bits.shaper_enable &&
srf_update->cm->flags.bits.lut3d_enable)
surface->mcm_shaper_3dlut_setting = DC_CM2_SHAPER_3DLUT_SETTING_ENABLE_SHAPER_3DLUT;
else if (srf_update->cm->flags.bits.shaper_enable)
surface->mcm_shaper_3dlut_setting = DC_CM2_SHAPER_3DLUT_SETTING_ENABLE_SHAPER;
else
surface->mcm_shaper_3dlut_setting = DC_CM2_SHAPER_3DLUT_SETTING_BYPASS_ALL;
#endif /* TRIM_CM2 */
}
if (srf_update->hdr_mult.value)
@@ -3491,12 +3444,6 @@ static void copy_surface_update_to_plane(
surface->sdr_white_level_nits =
srf_update->sdr_white_level_nits;
if (srf_update->cm &&
(srf_update->cm->flags.bits.blend_enable ||
srf_update->cm->flags.bits.shaper_enable ||
srf_update->cm->flags.bits.lut3d_enable))
surface->lut_bank_a = !surface->lut_bank_a;
if (srf_update->input_csc_color_matrix)
surface->input_csc_color_matrix =
*srf_update->input_csc_color_matrix;
@@ -3658,6 +3605,9 @@ static void copy_stream_update_to_stream(struct dc *dc,
if (update->sharpening_required)
stream->sharpening_required = *update->sharpening_required;
if (update->blending_linearity)
stream->blending_linearity = *update->blending_linearity;
if (update->drr_trigger_mode) {
stream->drr_trigger_mode = *update->drr_trigger_mode;
}
@@ -5235,7 +5185,7 @@ static void commit_planes_for_stream(struct dc *dc,
srf_updates[i].cm->flags.bits.lut3d_enable &&
srf_updates[i].cm->flags.bits.lut3d_dma_enable &&
dc->hwss.trigger_3dlut_dma_load)
dc->hwss.trigger_3dlut_dma_load(dc, pipe_ctx);
dc->hwss.trigger_3dlut_dma_load(pipe_ctx);
/*program triple buffer after lock based on flip type*/
if (dc->hwss.program_triplebuffer != NULL && dc->debug.enable_tri_buf) {

View File

@@ -41,6 +41,7 @@
#include "dcn10/dcn10_hubbub.h"
#include "dce/dmub_hw_lock_mgr.h"
#include "link_service.h"
#include "custom_float.h"
#define MAX_NUM_MCACHE 8
@@ -729,6 +730,386 @@ void get_fams2_visual_confirm_color(
}
}
static bool get_update_dchubp_dpp_flags_status(struct pipe_ctx *pipe)
{
if (!pipe->plane_state) {
return false;
}
if ((pipe->update_flags.raw ||
dc_pipe_update_bits_is_any_set(&pipe->plane_state->update_bits) ||
pipe->stream->update_flags.raw)) {
return true;
}
return false;
}
// Function to check if any update flags are set
static bool get_pipe_update_bits_status(struct pipe_ctx *pipe, struct dc_plane_state *plane, struct dc_stream_state *stream)
{
(void)stream;
if (plane) {
return (pipe->update_flags.bits.enable ||
pipe->update_flags.bits.plane_changed ||
pipe->update_flags.bits.opp_changed ||
plane->update_bits.pixel_format_change ||
plane->update_bits.horizontal_mirror_change ||
plane->update_bits.rotation_change ||
plane->update_bits.swizzle_change ||
plane->update_bits.dcc_change ||
plane->update_bits.bpp_change ||
plane->update_bits.scaling_change ||
plane->update_bits.plane_size_change);
}
return false;
}
void hwss_build_full_sequence(struct dc *dc,
struct block_sequence block_sequence[MAX_HWSS_BLOCK_SEQUENCE_SIZE],
unsigned int *num_steps,
struct dc_state *context, bool program_phantom_pipe)
{
(void)program_phantom_pipe;
struct dc_plane_state *plane = NULL;
struct dc_stream_state *stream = NULL;
struct pipe_ctx *current_pipe = NULL;
struct pipe_ctx *pipe = NULL;
struct dce_hwseq *hws = dc->hwseq;
unsigned int i;
*num_steps = 0; // Initialize to 0
struct block_sequence_state seq_state = { .steps = block_sequence, .num_steps = num_steps };
for (i = 0; i < dc->res_pool->pipe_count; i++)
dc->hwss.detect_pipe_changes(dc->current_state, context, &dc->current_state->res_ctx.pipe_ctx[i],
&context->res_ctx.pipe_ctx[i]);
/* Program triplebuffer if enabled */
if (dc->hwss.program_triplebuffer != NULL && dc->debug.enable_tri_buf) {
for (i = 0; i < dc->res_pool->pipe_count; i++) {
pipe = &context->res_ctx.pipe_ctx[i];
if (pipe->plane_state) {
/* Turn off triple buffer for full update */
hwss_add_hubp_program_triplebuffer(&seq_state, dc, pipe, pipe->plane_state->triplebuffer_flips);
}
}
}
/* Count hubp usage and force pstate change if needed */
unsigned int prev_hubp_count = 0;
unsigned int hubp_count = 0;
for (i = 0; i < dc->res_pool->pipe_count; i++) {
if (dc->current_state->res_ctx.pipe_ctx[i].plane_state)
prev_hubp_count++;
if (context->res_ctx.pipe_ctx[i].plane_state)
hubp_count++;
}
if (prev_hubp_count == 0 && hubp_count > 0) {
if (dc->res_pool->hubbub->funcs->force_pstate_change_control)
hwss_add_hubbub_force_pstate_change_control(&seq_state, dc->res_pool->hubbub, true, false);
}
/* When disabling phantom pipes, turn on phantom OTG first */
for (i = 0; i < dc->res_pool->pipe_count; i++) {
struct dc_stream_state *pipe_stream = dc->current_state->res_ctx.pipe_ctx[i].stream;
pipe = &dc->current_state->res_ctx.pipe_ctx[i];
if (context->res_ctx.pipe_ctx[i].update_flags.bits.disable && pipe_stream &&
dc_state_get_pipe_subvp_type(dc->current_state, pipe) == SUBVP_PHANTOM) {
struct timing_generator *tg = dc->current_state->res_ctx.pipe_ctx[i].stream_res.tg;
if (tg->funcs->enable_crtc) {
if (dc->hwseq->funcs.blank_pixel_data_sequence)
dc->hwseq->funcs.blank_pixel_data_sequence(dc, pipe, true, &seq_state);
hwss_add_tg_enable_crtc(&seq_state, tg);
}
}
}
/* OTG blank before disabling all front ends */
for (i = 0; i < dc->res_pool->pipe_count; i++) {
if (context->res_ctx.pipe_ctx[i].update_flags.bits.disable
&& !context->res_ctx.pipe_ctx[i].top_pipe
&& !context->res_ctx.pipe_ctx[i].prev_odm_pipe
&& context->res_ctx.pipe_ctx[i].stream) {
if (dc->hwseq->funcs.blank_pixel_data_sequence) {
dc->hwseq->funcs.blank_pixel_data_sequence(dc, &context->res_ctx.pipe_ctx[i],
true, &seq_state);
}
}
}
/* Disconnect mpcc for pipes being disabled or with opp changes */
for (i = 0; i < dc->res_pool->pipe_count; i++) {
if (context->res_ctx.pipe_ctx[i].update_flags.bits.disable
|| context->res_ctx.pipe_ctx[i].update_flags.bits.opp_changed) {
struct hubbub *hubbub = dc->res_pool->hubbub;
/* Phantom pipe DET should be 0, but if a pipe in use is being transitioned to phantom
* then we want to do the programming here (effectively it's being disabled). If we do
* the programming later the DET won't be updated until the OTG for the phantom pipe is
* turned on (i.e. in an MCLK switch) which can come in too late and cause issues with
* DET allocation.
*/
if ((context->res_ctx.pipe_ctx[i].update_flags.bits.disable ||
(context->res_ctx.pipe_ctx[i].plane_state &&
dc_state_get_pipe_subvp_type(context, &context->res_ctx.pipe_ctx[i]) ==
SUBVP_PHANTOM))) {
if (hubbub->funcs->program_det_size) {
hwss_add_hubp_program_det_size(&seq_state, hubbub,
dc->current_state->res_ctx.pipe_ctx[i].plane_res.hubp->inst, 0);
}
if (dc->res_pool->hubbub->funcs->program_det_segments) {
hwss_add_hubp_program_det_segments(&seq_state, hubbub,
dc->current_state->res_ctx.pipe_ctx[i].plane_res.hubp->inst, 0);
}
}
if (hws->funcs.plane_atomic_disconnect_sequence)
hws->funcs.plane_atomic_disconnect_sequence(dc, dc->current_state,
&dc->current_state->res_ctx.pipe_ctx[i], &seq_state);
}
}
/* update ODM for blanked OTG master pipes */
for (i = 0; i < dc->res_pool->pipe_count; i++) {
pipe = &context->res_ctx.pipe_ctx[i];
if (resource_is_pipe_type(pipe, OTG_MASTER) &&
!resource_is_pipe_type(pipe, DPP_PIPE) &&
pipe->update_flags.bits.odm) {
if (hws->funcs.update_odm_sequence)
hws->funcs.update_odm_sequence(dc, context, pipe, &seq_state);
}
}
for (i = 0; i < dc->res_pool->pipe_count; i++) {
pipe = &context->res_ctx.pipe_ctx[i];
if (pipe->update_flags.bits.disable) {
struct cm_hist_control hist_disable = { .channels_enabled = 0 };
hwss_add_dpp_program_cm_hist(&seq_state, pipe->plane_res.dpp,
hist_disable, COLOR_SPACE_UNKNOWN);
}
}
for (i = 0; i < dc->res_pool->pipe_count; i++) {
pipe = &context->res_ctx.pipe_ctx[i];
if (pipe->plane_state && !pipe->top_pipe) {
while (pipe) {
if (pipe->stream &&
dc_state_get_pipe_subvp_type(context, pipe) != SUBVP_PHANTOM) {
if (hws->funcs.program_pipe_sequence)
hws->funcs.program_pipe_sequence(dc, pipe, context, &seq_state);
}
pipe = pipe->bottom_pipe;
}
}
/* Program secondary blending tree and writeback pipes */
pipe = &context->res_ctx.pipe_ctx[i];
if (!pipe->top_pipe && !pipe->prev_odm_pipe
&& pipe->stream && pipe->stream->num_wb_info > 0
&& (pipe->update_flags.raw || (pipe->plane_state && dc_pipe_update_bits_is_any_set(&pipe->plane_state->update_bits))
|| pipe->stream->update_flags.raw)) {
if (hws->funcs.program_all_writeback_pipes_in_tree_sequence)
hws->funcs.program_all_writeback_pipes_in_tree_sequence(dc, pipe->stream,
context, &seq_state);
}
/* Avoid underflow by check of pipe line read when adding 2nd plane. */
if (hws->wa.wait_hubpret_read_start_during_mpo_transition &&
!pipe->top_pipe &&
pipe->stream &&
pipe->plane_res.hubp->funcs->hubp_wait_pipe_read_start &&
dc->current_state->stream_status[0].plane_count == 1 &&
context->stream_status[0].plane_count > 1) {
hwss_add_hubp_wait_pipe_read_start(&seq_state, pipe->plane_res.hubp);
}
}
for (i = 0; i < dc->res_pool->pipe_count; i++) {
current_pipe = &context->res_ctx.pipe_ctx[i];
while (current_pipe) {
plane = current_pipe->plane_state;
stream = current_pipe->stream;
bool is_phantom = (dc_state_get_pipe_subvp_type(context, current_pipe) == SUBVP_PHANTOM);
if (get_update_dchubp_dpp_flags_status(current_pipe)) {
if (!is_phantom && plane && stream) {
if (get_pipe_update_bits_status(current_pipe, plane, stream)) {
struct plane_size size = plane->plane_size;
size.surface_size = current_pipe->plane_res.scl_data.viewport;
hwss_add_hubp_program_surface_config(&seq_state, current_pipe->plane_res.hubp,
plane->format, &plane->tiling_info, size, plane->rotation, &plane->dcc,
plane->horizontal_mirror, 0);
}
if (current_pipe->plane_res.hubp->funcs->hubp_program_mcache_id_and_split_coordinate)
hwss_add_hubp_program_mcache_id(&seq_state, current_pipe->plane_res.hubp, &current_pipe->mcache_regs);
if (current_pipe->plane_res.dpp->funcs->dpp_program_upsp) {
block_sequence[*num_steps].params.program_upsp_params.pipe_ctx = current_pipe;
block_sequence[*num_steps].func = DPP_PROGRAM_UPSP;
(*num_steps)++;
}
}
}
current_pipe = current_pipe->bottom_pipe;
}
}
}
void hwss_build_post_unlock_full_sequence(struct dc *dc,
struct block_sequence block_sequence[MAX_HWSS_BLOCK_SEQUENCE_SIZE],
unsigned int *num_steps,
struct dc_state *context)
{
unsigned int i;
struct dce_hwseq *hwseq = dc->hwseq;
*num_steps = 0; // Initialize to 0
struct block_sequence_state seq_state = { .steps = block_sequence, .num_steps = num_steps };
/* Reset OPP for pipes transitioning from OPP_HEAD to non-OPP_HEAD */
for (i = 0; i < dc->res_pool->pipe_count; i++) {
if (resource_is_pipe_type(&dc->current_state->res_ctx.pipe_ctx[i], OPP_HEAD) &&
!resource_is_pipe_type(&context->res_ctx.pipe_ctx[i], OPP_HEAD)) {
if (dc->hwss.post_unlock_reset_opp_sequence)
dc->hwss.post_unlock_reset_opp_sequence(dc, &dc->current_state->res_ctx.pipe_ctx[i],
&seq_state);
}
}
/* Disable planes that are being disabled */
for (i = 0; i < dc->res_pool->pipe_count; i++) {
if (context->res_ctx.pipe_ctx[i].update_flags.bits.disable) {
if (dc->hwss.disable_plane_sequence)
dc->hwss.disable_plane_sequence(dc, dc->current_state, &dc->current_state->res_ctx.pipe_ctx[i],
&seq_state);
}
}
/* Wait for flip pending on pipes being enabled */
for (i = 0; i < dc->res_pool->pipe_count; i++) {
struct pipe_ctx *pipe = &context->res_ctx.pipe_ctx[i];
if (pipe->plane_state && !pipe->top_pipe && pipe->update_flags.bits.enable &&
dc_state_get_pipe_subvp_type(context, pipe) != SUBVP_PHANTOM) {
unsigned int polling_interval_us;
polling_interval_us = 1;
hwss_add_hubp_wait_flip_pending(&seq_state, pipe->plane_res.hubp, 100000, polling_interval_us);
}
}
/* Wait for double buffer pending when ODM slice count increases */
for (i = 0; i < dc->res_pool->pipe_count; i++) {
struct pipe_ctx *pipe = &context->res_ctx.pipe_ctx[i];
struct pipe_ctx *old_pipe = &dc->current_state->res_ctx.pipe_ctx[i];
if (resource_is_pipe_type(old_pipe, OTG_MASTER) && resource_is_pipe_type(pipe, OTG_MASTER) &&
resource_get_odm_slice_count(old_pipe) < resource_get_odm_slice_count(pipe) &&
dc_state_get_pipe_subvp_type(context, pipe) != SUBVP_PHANTOM) {
unsigned int polling_interval_us;
polling_interval_us = 1;
hwss_add_tg_wait_double_buffer_pending(&seq_state, pipe->stream_res.tg, 100000, polling_interval_us);
}
}
/* Force pstate change control to false */
if (dc->res_pool->hubbub->funcs->force_pstate_change_control)
hwss_add_hubbub_force_pstate_change_control(&seq_state, dc->res_pool->hubbub, false, false);
/* Program phantom pipes */
for (i = 0; i < dc->res_pool->pipe_count; i++) {
struct pipe_ctx *pipe = &context->res_ctx.pipe_ctx[i];
if (pipe->plane_state && !pipe->top_pipe) {
while (pipe) {
if (pipe->stream && dc_state_get_pipe_subvp_type(context, pipe) == SUBVP_PHANTOM) {
/* Apply update flags for phantom pipes using existing HWS functions */
if (dc->hwss.apply_update_flags_for_phantom)
hwss_add_hws_apply_update_flags_for_phantom(&seq_state, pipe);
if (dc->hwss.update_phantom_vp_position)
hwss_add_hws_update_phantom_vp_position(&seq_state, dc, context, pipe);
/* Program the phantom pipe - use program_pipe_sequence if available */
if (dc->hwseq && dc->hwseq->funcs.program_pipe_sequence)
dc->hwseq->funcs.program_pipe_sequence(dc, pipe, context, &seq_state);
}
pipe = pipe->bottom_pipe;
}
}
}
for (i = 0; i < dc->res_pool->pipe_count; i++) {
struct pipe_ctx *pipe = &context->res_ctx.pipe_ctx[i];
while (pipe) {
struct dc_plane_state *plane = pipe->plane_state;
struct dc_stream_state *stream = pipe->stream;
bool is_phantom = (dc_state_get_pipe_subvp_type(context, pipe) == SUBVP_PHANTOM);
if (get_update_dchubp_dpp_flags_status(pipe)) {
if (is_phantom && plane && stream) {
if (get_pipe_update_bits_status(pipe, plane, stream)) {
struct plane_size size = plane->plane_size;
size.surface_size = pipe->plane_res.scl_data.viewport;
hwss_add_hubp_program_surface_config(&seq_state, pipe->plane_res.hubp,
plane->format, &plane->tiling_info, size, plane->rotation, &plane->dcc,
plane->horizontal_mirror, 0);
}
if (pipe->plane_res.hubp->funcs->hubp_program_mcache_id_and_split_coordinate)
hwss_add_hubp_program_mcache_id(&seq_state, pipe->plane_res.hubp, &pipe->mcache_regs);
}
}
pipe = pipe->bottom_pipe;
}
}
/* Update force pstate if hwseq is available */
if (hwseq && hwseq->funcs.update_force_pstate)
hwss_add_update_force_pstate(&seq_state, dc, context);
/* Program MALL pipe configuration */
if (hwseq && hwseq->funcs.program_mall_pipe_config_sequence)
hwseq->funcs.program_mall_pipe_config_sequence(dc, context, &seq_state);
/* Apply DEDCN21 147 WA if needed */
if (hwseq && hwseq->wa.DEGVIDCN21)
hwss_add_hubbub_apply_dedcn21_147_wa(&seq_state, dc->res_pool->hubbub);
/* Handle stutter underflow WA during MPO transitions */
if (hwseq && hwseq->wa.disallow_self_refresh_during_multi_plane_transition &&
dc->current_state->stream_status[0].plane_count == 1 &&
context->stream_status[0].plane_count > 1) {
hwss_add_hubbub_allow_self_refresh_control(&seq_state, dc->res_pool->hubbub, false,
&hwseq->wa_state.disallow_self_refresh_during_multi_plane_transition_applied);
/* Get frame count for WA state tracking - this needs to be done immediately after the above call */
if (dc->res_pool->timing_generators[0]->funcs->get_frame_count) {
hwss_add_tg_get_frame_count(&seq_state, dc->res_pool->timing_generators[0],
&hwseq->wa_state.disallow_self_refresh_during_multi_plane_transition_applied_on_frame);
}
}
}
void hwss_build_fast_sequence(struct dc *dc,
struct dc_dmub_cmd *dc_dmub_cmd,
unsigned int dmub_cmd_count,
@@ -1058,6 +1439,16 @@ void hwss_build_fast_sequence(struct dc *dc,
(*num_steps)++;
}
if (current_mpc_pipe->plane_state->update_bits.lut_3d &&
current_mpc_pipe->plane_state->cm.flags.bits.lut3d_dma_enable &&
current_mpc_pipe->plane_state->cm.flags.bits.shaper_enable &&
current_mpc_pipe->plane_state->cm.flags.bits.lut3d_enable &&
current_mpc_pipe->plane_res.hubp->funcs->hubp_enable_3dlut_fl) {
block_sequence[*num_steps].params.hubp_enable_3dlut_fl_params.hubp =
current_mpc_pipe->plane_res.hubp;
block_sequence[*num_steps].func = HUBP_ENABLE_3DLUT_FL;
(*num_steps)++;
}
if (hws->funcs.set_input_transfer_func && current_mpc_pipe->plane_state->update_bits.gamma_change) {
block_sequence[*num_steps].params.set_input_transfer_func_params.dc = dc;
block_sequence[*num_steps].params.set_input_transfer_func_params.pipe_ctx = current_mpc_pipe;
@@ -1066,6 +1457,24 @@ void hwss_build_fast_sequence(struct dc *dc,
(*num_steps)++;
}
if (current_mpc_pipe->plane_state->update_bits.hdr_mult) {
struct fixed31_32 multiplier = current_mpc_pipe->plane_state->hdr_mult;
uint32_t hw_mult = 0x1f000; // 1.0 default multiplier
struct custom_float_format fmt;
fmt.exponenta_bits = 6;
fmt.mantissa_bits = 12;
fmt.sign = true;
if (!dc_fixpt_eq(multiplier, dc_fixpt_from_int(0)) && // check != 0
convert_to_custom_float_format(multiplier, &fmt, &hw_mult)) {
block_sequence[*num_steps].params.dpp_set_hdr_multiplier_params.dpp = current_mpc_pipe->plane_res.dpp;
block_sequence[*num_steps].params.dpp_set_hdr_multiplier_params.hw_mult = hw_mult;
block_sequence[*num_steps].func = DPP_SET_HDR_MULTIPLIER;
(*num_steps)++;
}
}
if (dc->hwss.program_gamut_remap &&
(current_mpc_pipe->plane_state->update_bits.gamut_remap_change ||
current_mpc_pipe->stream->update_flags.bits.gamut_remap)) {
@@ -1279,6 +1688,9 @@ void hwss_execute_sequence(struct dc *dc,
case DPP_SET_OUTPUT_TRANSFER_FUNC:
hws->funcs.set_output_transfer_func(&params->set_output_transfer_func_params);
break;
case DPP_PROGRAM_UPSP:
hwss_program_upsp(params);
break;
case MPC_UPDATE_VISUAL_CONFIRM:
dc->hwss.update_visual_confirm_color(params->update_visual_confirm_params.dc,
params->update_visual_confirm_params.pipe_ctx,
@@ -1900,7 +2312,8 @@ void hwss_set_output_transfer_func(struct dc *dc, struct pipe_ctx *pipe_ctx)
.xfm = pipe_ctx->plane_res.xfm,
.dpp = pipe_ctx->plane_res.dpp,
.mpc = dc->res_pool->mpc,
.mpcc_id = pipe_ctx->plane_res.hubp->inst,
.mpcc_id = pipe_ctx->plane_res.hubp ?
pipe_ctx->plane_res.hubp->inst : 0,
.is_top_pipe = resource_is_pipe_type(pipe_ctx, OPP_HEAD),
.stream = pipe_ctx->stream,
}
@@ -2448,6 +2861,22 @@ void hwss_program_bias_and_scale(union block_sequence_params *params)
dpp->funcs->dpp_program_bias_and_scale(dpp, &bns_params);
}
void hwss_program_upsp(union block_sequence_params *params)
{
struct pipe_ctx *pipe_ctx = params->program_upsp_params.pipe_ctx;
struct dpp *dpp = pipe_ctx->plane_res.dpp;
struct dscl_prog_data *dscl_prog_data = &pipe_ctx->plane_res.scl_data.dscl_prog_data;
if (!dpp || !dscl_prog_data)
return;
if (dpp && dpp->funcs->dpp_program_upsp) {
// program upsampler
dpp->funcs->dpp_program_upsp(dpp,
dscl_prog_data);
}
}
void hwss_power_on_mpc_mem_pwr(union block_sequence_params *params)
{
struct mpc *mpc = params->power_on_mpc_mem_pwr_params.mpc;

View File

@@ -80,6 +80,7 @@
#include "dcn401/dcn401_resource.h"
#include "dcn42/dcn42_resource.h"
#include "dcn42b/dcn42b_resource.h"
#include "dcn60/dcn60_resource.h"
#if defined(CONFIG_DRM_AMD_DC_FP)
#include "dc_spl_translate.h"
#endif
@@ -261,6 +262,9 @@ enum dce_version resource_parse_asic_id(struct hw_asic_id asic_id)
case AMDGPU_FAMILY_GC_11_5_4:
dc_version = DCN_VERSION_4_2;
break;
case AMDGPU_FAMILY_GC_13_0_1:
dc_version = DCN_VERSION_6_0;
break;
default:
dc_version = DCE_VERSION_UNKNOWN;
break;
@@ -383,6 +387,9 @@ struct resource_pool *dc_create_resource_pool(struct dc *dc,
case DCN_VERSION_4_2B:
res_pool = dcn42b_create_resource_pool(init_data, dc);
break;
case DCN_VERSION_6_0:
res_pool = dcn60_create_resource_pool(init_data, dc);
break;
#endif /* CONFIG_DRM_AMD_DC_FP */
default:
break;
@@ -818,6 +825,36 @@ static enum dc_pixel_format convert_pixel_format_to_dalsurface(
case SURFACE_PIXEL_FORMAT_VIDEO_420_10bpc_YCrCb:
dal_pixel_format = PIXEL_FORMAT_420BPP10;
break;
case SURFACE_PIXEL_FORMAT_VIDEO_422_CrCb_P208:
case SURFACE_PIXEL_FORMAT_VIDEO_422_CbCr_P208:
dal_pixel_format = PIXEL_FORMAT_422BPP8;
break;
case SURFACE_PIXEL_FORMAT_VIDEO_422_CrCb_P210:
case SURFACE_PIXEL_FORMAT_VIDEO_422_CbCr_P210:
dal_pixel_format = PIXEL_FORMAT_422BPP10;
break;
case SURFACE_PIXEL_FORMAT_VIDEO_422_CrCb_P212:
case SURFACE_PIXEL_FORMAT_VIDEO_422_CbCr_P212:
dal_pixel_format = PIXEL_FORMAT_422BPP12;
break;
case SURFACE_PIXEL_FORMAT_VIDEO_422_YCrYCb:
case SURFACE_PIXEL_FORMAT_VIDEO_422_YCbYCr:
case SURFACE_PIXEL_FORMAT_VIDEO_422_CrYCbY:
case SURFACE_PIXEL_FORMAT_VIDEO_422_CbYCrY:
dal_pixel_format = PIXEL_FORMAT_422BPP8;
break;
case SURFACE_PIXEL_FORMAT_VIDEO_422_10bpc_YCrYCb:
case SURFACE_PIXEL_FORMAT_VIDEO_422_10bpc_YCbYCr:
case SURFACE_PIXEL_FORMAT_VIDEO_422_10bpc_CrYCbY:
case SURFACE_PIXEL_FORMAT_VIDEO_422_10bpc_CbYCrY:
dal_pixel_format = PIXEL_FORMAT_422BPP10;
break;
case SURFACE_PIXEL_FORMAT_VIDEO_422_12bpc_YCrYCb:
case SURFACE_PIXEL_FORMAT_VIDEO_422_12bpc_YCbYCr:
case SURFACE_PIXEL_FORMAT_VIDEO_422_12bpc_CrYCbY:
case SURFACE_PIXEL_FORMAT_VIDEO_422_12bpc_CbYCrY:
dal_pixel_format = PIXEL_FORMAT_422BPP12;
break;
case SURFACE_PIXEL_FORMAT_GRPH_ARGB16161616:
case SURFACE_PIXEL_FORMAT_GRPH_ABGR16161616:
default:
@@ -1516,6 +1553,15 @@ void resource_build_test_pattern_params(struct resource_context *res_ctx,
}
}
enum upsp_mode resource_is_upsp_required(enum surface_pixel_format format)
{
if (format >= SURFACE_PIXEL_FORMAT_VIDEO_BEGIN && format <= SURFACE_PIXEL_FORMAT_VIDEO_420_10bpc_YCrCb) //420 Formats
return UPSP_HORIZONTAL_VERTICAL_UPSAMPLING;
if (format > SURFACE_PIXEL_FORMAT_VIDEO_420_10bpc_YCrCb && format < SURFACE_PIXEL_FORMAT_SUBSAMPLE_END) //422 Formats
return UPSP_HORIZONTAL_UPSAMPLING_ONLY;
return UPSP_BYPASS;
}
bool resource_build_scaling_params(struct pipe_ctx *pipe_ctx)
{
const struct dc_plane_state *plane_state = pipe_ctx->plane_state;
@@ -1561,6 +1607,7 @@ bool resource_build_scaling_params(struct pipe_ctx *pipe_ctx)
pipe_ctx->plane_res.scl_data.lb_params.depth = LB_PIXEL_DEPTH_30BPP;
pipe_ctx->plane_res.scl_data.lb_params.alpha_en = plane_state->per_pixel_alpha;
pipe_ctx->plane_res.scl_data.upsp = resource_is_upsp_required(plane_state->format);
// Convert pipe_ctx to respective input params for SPL
translate_SPL_in_params_from_pipe_ctx(pipe_ctx, spl_in);

View File

@@ -1155,3 +1155,15 @@ bool dc_state_is_subvp_in_use(struct dc_state *state)
return false;
}
bool dc_state_is_alt_in_use(const struct dc *dc, const struct dc_state *state)
{
uint32_t i;
const struct pipe_ctx *pipe;
for (i = 0; i < dc->res_pool->pipe_count; i++) {
pipe = &state->res_ctx.pipe_ctx[i];
if (pipe->p_state_type == P_STATE_ALT)
return true;
}
return false;
}

View File

@@ -405,6 +405,7 @@ bool dc_stream_program_cursor_attributes(
{
struct dc *dc;
bool reset_idle_optimizations = false;
bool should_release_dmub_hw_control_lock = false;
if (!stream)
return false;
@@ -413,6 +414,13 @@ bool dc_stream_program_cursor_attributes(
if (dc_stream_set_cursor_attributes(stream, attributes)) {
dc_z10_restore(dc);
if (dc->hwss.dmub_hw_control_lock) {
if (dc_state_is_alt_in_use(dc, dc->current_state) &&
!dc_dmub_srv_is_cursor_offload_enabled(dc)) {
dc->hwss.dmub_hw_control_lock(dc, dc->current_state, true);
should_release_dmub_hw_control_lock = true;
}
}
/* disable idle optimizations while updating cursor */
if (dc->idle_optimizations_allowed) {
dc_allow_idle_optimizations(dc, false);
@@ -425,6 +433,10 @@ bool dc_stream_program_cursor_attributes(
if (reset_idle_optimizations && !dc->debug.disable_dmub_reallow_idle)
dc_allow_idle_optimizations(dc, true);
if (dc->hwss.dmub_hw_control_lock) {
if (should_release_dmub_hw_control_lock)
dc->hwss.dmub_hw_control_lock(dc, dc->current_state, false);
}
return true;
}
@@ -507,6 +519,7 @@ bool dc_stream_program_cursor_position(
struct dc *dc;
bool reset_idle_optimizations = false;
const struct dc_cursor_position *old_position;
bool should_release_dmub_hw_control_lock = false;
if (!stream)
return false;
@@ -517,6 +530,13 @@ bool dc_stream_program_cursor_position(
if (dc_stream_set_cursor_position(stream, position)) {
dc_z10_restore(dc);
if (dc->hwss.dmub_hw_control_lock) {
if (dc_state_is_alt_in_use(dc, dc->current_state) &&
!dc_dmub_srv_is_cursor_offload_enabled(dc)) {
dc->hwss.dmub_hw_control_lock(dc, dc->current_state, true);
should_release_dmub_hw_control_lock = true;
}
}
/* disable idle optimizations if enabling cursor */
if (dc->idle_optimizations_allowed &&
(!old_position->enable || dc->debug.exit_idle_opt_for_cursor_updates) &&
@@ -567,6 +587,10 @@ bool dc_stream_program_cursor_position(
}
}
if (dc->hwss.dmub_hw_control_lock) {
if (should_release_dmub_hw_control_lock)
dc->hwss.dmub_hw_control_lock(dc, dc->current_state, false);
}
return true;
}
@@ -967,6 +991,7 @@ void dc_stream_log(const struct dc *dc, const struct dc_stream_state *stream)
}
}
/* TODO - move to per plane ownership? */
/*
* dc_stream_get_3dlut()
* Requirements:
@@ -1018,7 +1043,6 @@ void dc_stream_release_3dlut_for_stream(
if (rmcm_3dlut) {
rmcm_3dlut->isInUse = false;
rmcm_3dlut->stream = NULL;
rmcm_3dlut->protection_bits = 0;
}
}
@@ -1030,7 +1054,6 @@ void dc_stream_init_rmcm_3dlut(struct dc *dc)
for (unsigned int i = 0; i < num_rmcm; i++) {
dc->res_pool->rmcm_3dlut[i].isInUse = false;
dc->res_pool->rmcm_3dlut[i].stream = NULL;
dc->res_pool->rmcm_3dlut[i].protection_bits = 0;
}
}

View File

@@ -66,7 +66,7 @@ struct dcn_dsc_reg_state;
struct dcn_optc_reg_state;
struct dcn_dccg_reg_state;
#define DC_VER "3.2.391"
#define DC_VER "3.2.392"
/**
* MAX_SURFACES - representative of the upper bound of surfaces that can be piped to a single CRTC
@@ -174,6 +174,13 @@ struct dc_plane_cap {
uint32_t fp16 : 1;
uint32_t p010 : 1;
uint32_t ayuv : 1;
uint32_t yuy2 : 1; // Packed 422 8bpc
uint32_t y210 : 1; // Packed 422 10bpc
uint32_t y212 : 1; // Packed 422 12bpc
uint32_t p208 : 1; // Planar 422 8bpc
uint32_t p210 : 1; // Planar 422 10bpc
uint32_t p212 : 1; // Planar 422 12bpc
/* Not all caps will be used/supported */
} pixel_format_support;
// max upscaling factor x1000
// upscaling factors are always >= 1
@@ -240,6 +247,7 @@ struct rom_curve_caps {
* @ogam_ram: programmable out/blend gamma LUT
* @ocsc: output color space conversion
* @dgam_rom_for_yuv: pre-defined degamma LUT for YUV planes
* @upsp_pre_scaler: Ability to upsample 420/422 before scaling
* @dgam_rom_caps: pre-definied curve caps for degamma 1D LUT
* @ogam_rom_caps: pre-definied curve caps for regamma 1D LUT
*
@@ -256,6 +264,7 @@ struct dpp_color_caps {
uint16_t ogam_ram : 1;
uint16_t ocsc : 1;
uint16_t dgam_rom_for_yuv : 1;
uint16_t upsp_pre_scaler : 1;
struct rom_curve_caps dgam_rom_caps;
struct rom_curve_caps ogam_rom_caps;
};
@@ -617,6 +626,7 @@ struct dc_config {
bool unify_link_enc_assignment;
bool enable_cursor_offload;
bool dp_connector_no_native_i2c;
unsigned int link_index_with_no_ddc;
bool frame_update_cmd_version2;
struct spl_sharpness_range dcn_sharpness_range;
struct spl_sharpness_range dcn_override_sharpness_range;
@@ -785,6 +795,7 @@ struct dc_clocks {
*/
bool fw_based_mclk_switching;
bool fw_based_mclk_switching_shut_down;
bool alt_ch_pstate_switch;
int prev_num_ways;
enum dtm_pstate dtm_level;
int max_supported_dppclk_khz;
@@ -795,6 +806,18 @@ struct dc_clocks {
int idle_fclk_khz;
int subvp_prefetch_dramclk_khz;
int subvp_prefetch_fclk_khz;
/* deprecated: use _KBps variants — will be removed after DML update */
unsigned int utm_urgent_bandwidth_lb_Kbps;
unsigned int utm_nominal_bandwidth_lb_Kbps;
unsigned int utm_urgent_bandwidth_lb_KBps;
unsigned int utm_nominal_bandwidth_lb_KBps;
unsigned int utm_latency_ub_index;
unsigned int utm_lsdma_bandwidth_lb_KBps;
unsigned int utm_nominal_max_latency_ub_ns;
unsigned int utm_nominal_avg_latency_ub_ns;
/* deprecated: use _KBps variant — will be removed after DML update */
unsigned int required_avg_active_bandwidth_Kbps;
unsigned int required_avg_active_bandwidth_KBps;
/* Stutter efficiency is technically not clock values
* but stored here so the values are part of the update_clocks call similar to num_ways
@@ -1436,6 +1459,7 @@ struct dc_transfer_func {
enum dc_transfer_func_predefined tf;
/* FP16 1.0 reference level in nits, default is 80 nits, only for PQ*/
uint32_t sdr_ref_white_level;
struct fixed31_32 hdr_multiplier;
union {
struct pwl_params pwl;
struct dc_transfer_func_distributed_points tf_pts;
@@ -1486,13 +1510,11 @@ struct lut_mem_mapping {
struct dc_rmcm_3dlut {
bool isInUse;
const struct dc_stream_state *stream;
uint8_t protection_bits;
};
struct dc_3dlut {
struct kref refcount;
struct tetrahedral_params lut_3d;
struct fixed31_32 hdr_multiplier;
union dc_3dlut_state state;
};
@@ -1572,7 +1594,6 @@ struct pipe_update_bits {
uint32_t stereo_format_change:1;
uint32_t lut_3d:1;
uint32_t tmz_changed:1;
uint32_t mcm_transfer_function_enable_change:1; /* disable or enable MCM transfer func */
uint32_t full_update:1;
uint32_t sdr_white_level_nits:1;
uint32_t cm_hist_change:1;
@@ -1617,7 +1638,6 @@ static inline void dc_pipe_update_bits_set_full(struct pipe_update_bits *flags)
flags->stereo_format_change = 1;
flags->lut_3d = 1;
flags->tmz_changed = 1;
flags->mcm_transfer_function_enable_change = 1;
flags->full_update = 1;
flags->sdr_white_level_nits = 1;
flags->cm_hist_change = 1;
@@ -1651,7 +1671,6 @@ static inline bool dc_pipe_update_bits_is_any_set(const struct pipe_update_bits
flags->stereo_format_change ||
flags->lut_3d ||
flags->tmz_changed ||
flags->mcm_transfer_function_enable_change ||
flags->full_update ||
flags->sdr_white_level_nits ||
flags->cm_hist_change;
@@ -1686,18 +1705,16 @@ struct dc_plane_state {
enum dc_color_space color_space;
#ifndef TRIM_CM2
// TODO: No longer used, remove
bool lut_bank_a;
struct dc_hdr_static_metadata hdr_static_ctx;
struct dc_3dlut lut3d_func;
struct dc_transfer_func in_shaper_func;
struct dc_transfer_func blend_tf;
enum dc_cm2_shaper_3dlut_setting mcm_shaper_3dlut_setting;
bool mcm_lut1d_enable;
struct dc_cm2_func_luts mcm_luts;
#endif /* TRIM_CM2 */
bool lut_bank_a;
enum mpcc_movable_cm_location mcm_location;
#endif /* TRIM_CM2 */
struct dc_plane_cm cm;
struct dc_transfer_func *gamcor_tf;
@@ -1739,7 +1756,7 @@ struct dc_plane_state {
bool adaptive_sharpness_en;
int adaptive_sharpness_policy;
unsigned int sharpness_level;
enum linear_light_scaling linear_light_scaling;
enum dc_scaling_linearity scaling_linearity;
unsigned int sdr_white_level_nits;
struct cm_hist_control cm_hist_control;
struct spl_sharpness_range sharpness_range;
@@ -1763,6 +1780,7 @@ struct dc_plane_info {
bool input_csc_enabled;
unsigned int layer_index;
enum chroma_cositing cositing;
enum dc_scaling_linearity scaling_linearity;
};
#include "dc_stream.h"
@@ -1970,6 +1988,7 @@ struct dc_scratch_space {
// BW ALLOCATON USB4 ONLY
struct dc_dpia_bw_alloc dpia_bw_alloc_config;
bool skip_implict_edp_power_control;
bool forced_psr_active;
enum backlight_control_type backlight_control_type;
};

View File

@@ -2431,3 +2431,47 @@ void dc_dmub_srv_log_preos_dmcub_info(struct dc_dmub_srv *dc_dmub_srv)
DC_LOG_DEBUG("fb_offset : 0x%016llx", dmub->preos_info.fb_offset);
}
}
bool dc_dmub_srv_ihc_set_dig_hdcp_interrupt_dest(
struct dc_dmub_srv *dc_dmub_srv,
uint8_t dig_id,
bool to_dmu)
{
union dmub_rb_cmd cmd;
if (!dc_dmub_srv || !dc_dmub_srv->dmub)
return false;
memset(&cmd, 0, sizeof(cmd));
cmd.ihc.header.type = DMUB_CMD__IHC;
cmd.ihc.header.sub_type = DMUB_CMD__IHC_SET_DIG_HDCP_INTERRUPT_DEST;
cmd.ihc.header.payload_bytes = sizeof(cmd.ihc.data);
cmd.ihc.data.dig_id = dig_id;
cmd.ihc.data.to_dmu = to_dmu ? 1 : 0;
dc_wake_and_execute_dmub_cmd(dc_dmub_srv->ctx, &cmd, DM_DMUB_WAIT_TYPE_WAIT);
return true;
}
void dc_dmub_srv_get_fams2_debug_meta(struct dc_dmub_srv *dc_dmub_srv)
{
union dmub_rb_cmd cmd;
memset(&cmd, 0, sizeof(cmd));
cmd.ib_fams2_debug_meta.header.type = DMUB_CMD__FW_ASSISTED_MCLK_SWITCH;
cmd.ib_fams2_debug_meta.header.sub_type = DMUB_CMD__FAMS2_IB_DEBUG_META;
cmd.ib_fams2_debug_meta.ib_data.src.quad_part = dc_dmub_srv->dmub->ib_mem_gart.gpu_addr;
cmd.ib_fams2_debug_meta.ib_data.size = dc_dmub_srv->dmub->ib_mem_gart.size > 0xFFFF ?
0xFFFF : (uint16_t)dc_dmub_srv->dmub->ib_mem_gart.size;
/* flush any existing writes to the buffer to prevent conflicts */
dmub_srv_flush_buffer_mem(dc_dmub_srv->dmub, &dc_dmub_srv->dmub->ib_mem_gart);
dm_execute_dmub_cmd_list(dc_dmub_srv->ctx, 1, &cmd, DM_DMUB_WAIT_TYPE_WAIT);
}

View File

@@ -401,4 +401,27 @@ void dc_dmub_srv_release_hw(const struct dc *dc);
* @dc - pointer to DC object
*/
void dc_dmub_srv_log_preos_dmcub_info(struct dc_dmub_srv *dc_dmub_srv);
/**
* dc_dmub_srv_ihc_set_dig_hdcp_interrupt_dest() - Configure IHC interrupt destination.
* @dc_dmub_srv: DMUB service handle
* @dig_id: DIG engine ID (0-3)
* @to_dmu: Route interrupt to DMU (true) or not (false)
*
* Sends command to DMUB firmware to configure IHC interrupt routing
* for HDCP I2C transfer requests.
*
* Return: true on success, false on failure
*/
bool dc_dmub_srv_ihc_set_dig_hdcp_interrupt_dest(
struct dc_dmub_srv *dc_dmub_srv,
uint8_t dig_id,
bool to_dmu);
/**
* dc_dmub_srv_get_fams2_debug_meta() - Queries for FAMS2 debug metadata from DMUB and logs it.
*
* @dc_dmub_srv - pointer to DMUB service object
*/
void dc_dmub_srv_get_fams2_debug_meta(struct dc_dmub_srv *dc_dmub_srv);
#endif /* _DMUB_DC_SRV_H_ */

View File

@@ -530,6 +530,8 @@ char *dce_version_to_string(const int version)
return "DCN 4.2";
case DCN_VERSION_4_2B:
return "DCN 4.2B";
case DCN_VERSION_6_0:
return "DCN 6.0";
default:
return "Unknown";
}

View File

@@ -205,9 +205,32 @@ enum surface_pixel_format {
SURFACE_PIXEL_FORMAT_VIDEO_420_YCrCb,
SURFACE_PIXEL_FORMAT_VIDEO_420_10bpc_YCbCr,
SURFACE_PIXEL_FORMAT_VIDEO_420_10bpc_YCrCb,
/* Planar 422 Formats*/
SURFACE_PIXEL_FORMAT_VIDEO_422_CrCb_P208, // 8 bpc
SURFACE_PIXEL_FORMAT_VIDEO_422_CbCr_P208, // 8 bpc
SURFACE_PIXEL_FORMAT_VIDEO_422_CrCb_P210, // 10 bpc
SURFACE_PIXEL_FORMAT_VIDEO_422_CbCr_P210, // 10 bpc
SURFACE_PIXEL_FORMAT_VIDEO_422_CrCb_P212, // 12 bpc
SURFACE_PIXEL_FORMAT_VIDEO_422_CbCr_P212, // 12 bpc
SURFACE_PIXEL_FORMAT_SUBSAMPLE_END,
SURFACE_PIXEL_FORMAT_VIDEO_ACrYCb2101010 =
SURFACE_PIXEL_FORMAT_SUBSAMPLE_END,
/* packed 422 formats, they should reside here as the necessity for programming chroma parameters are determined wrt SURFACE_PIXEL_FORMAT_SUBSAMPLE_END*/
/* Packed 422 Format, 8bpc*/
SURFACE_PIXEL_FORMAT_VIDEO_422_YCrYCb,
SURFACE_PIXEL_FORMAT_VIDEO_422_YCbYCr,
SURFACE_PIXEL_FORMAT_VIDEO_422_CrYCbY,
SURFACE_PIXEL_FORMAT_VIDEO_422_CbYCrY,
/* Packed 422 Format, 10bpc*/
SURFACE_PIXEL_FORMAT_VIDEO_422_10bpc_YCrYCb,
SURFACE_PIXEL_FORMAT_VIDEO_422_10bpc_YCbYCr,
SURFACE_PIXEL_FORMAT_VIDEO_422_10bpc_CrYCbY,
SURFACE_PIXEL_FORMAT_VIDEO_422_10bpc_CbYCrY,
/* Packed 422 Format, 12bpc*/
SURFACE_PIXEL_FORMAT_VIDEO_422_12bpc_YCrYCb,
SURFACE_PIXEL_FORMAT_VIDEO_422_12bpc_YCbYCr,
SURFACE_PIXEL_FORMAT_VIDEO_422_12bpc_CrYCbY,
SURFACE_PIXEL_FORMAT_VIDEO_422_12bpc_CbYCrY,
SURFACE_PIXEL_FORMAT_VIDEO_CrYCbA1010102,
SURFACE_PIXEL_FORMAT_VIDEO_AYCrCb8888,
SURFACE_PIXEL_FORMAT_INVALID
@@ -230,13 +253,19 @@ enum dc_pixel_format {
/*video*/
PIXEL_FORMAT_420BPP8,
PIXEL_FORMAT_420BPP10,
// Align with SPL formats
PIXEL_FORMAT_422BPP8,
PIXEL_FORMAT_422BPP10,
PIXEL_FORMAT_422BPP12,
PIXEL_FORMAT_444BPP8,
PIXEL_FORMAT_444BPP10,
/*end of pixel format definition*/
PIXEL_FORMAT_INVALID,
PIXEL_FORMAT_GRPH_BEGIN = PIXEL_FORMAT_INDEX8,
PIXEL_FORMAT_GRPH_END = PIXEL_FORMAT_FP16,
PIXEL_FORMAT_VIDEO_BEGIN = PIXEL_FORMAT_420BPP8,
PIXEL_FORMAT_VIDEO_END = PIXEL_FORMAT_420BPP10,
PIXEL_FORMAT_VIDEO_END = PIXEL_FORMAT_444BPP10,
PIXEL_FORMAT_UNKNOWN
};
@@ -651,6 +680,7 @@ struct dc_cursor_attributes {
enum dc_rotation_angle rotation_angle;
union dc_cursor_attribute_flags attribute_flags;
bool force_cursor_to_disp_pref;
};
struct dpp_cursor_attributes {
@@ -1249,5 +1279,26 @@ struct cm_hist {
uint32_t ch2[256];
uint32_t ch3[256];
};
enum pregam_mode {
PREGAM_BYPASS = 0,
PREGAM_DEGAM,
PREGAM_REGAM
};
enum degam_lut {
DEGAM_SRGB = 0,
DEGAM_GAMMA_22,
DEGAM_GAMMA_24,
DEGAM_GAMMA_26,
DEGAM_BT2020,
DEGAM_BT2100PQ,
DEGAM_BT2100HLG
};
enum regam_lut {
REGAM_20 = 0,
REGAM_24
};
#endif /* DC_HW_TYPES_H */

View File

@@ -16,6 +16,9 @@ static struct spl_callbacks dcn32_spl_callbacks = {
static struct spl_callbacks dcn401_spl_callbacks = {
.spl_calc_lb_num_partitions = dscl401_spl_calc_lb_num_partitions,
};
static struct spl_callbacks dcn50_spl_callbacks = {
.spl_calc_lb_num_partitions = dscl401_spl_calc_lb_num_partitions,
};
static void populate_splrect_from_rect(struct spl_rect *spl_rect, const struct rect *rect)
{
spl_rect->x = rect->x;
@@ -71,6 +74,23 @@ static void populate_splformat_from_format(enum spl_pixel_format *spl_pixel_form
else
*spl_pixel_format = SPL_PIXEL_FORMAT_INVALID;
}
static void set_linear_light_scaling_preference(const struct dc_plane_state *plane_state, struct spl_in *spl_in)
{
if (plane_state != NULL) {
switch (plane_state->scaling_linearity) {
case DC_SCALING_LINEARITY_LINEAR:
spl_in->lls_pref = LLS_PREF_YES;
break;
case DC_SCALING_LINEARITY_SOURCE:
spl_in->lls_pref = LLS_PREF_NO;
break;
default:
spl_in->lls_pref = LLS_PREF_DONT_CARE;
break;
}
} else
spl_in->lls_pref = LLS_PREF_DONT_CARE;
}
/// @brief Translate SPL input parameters from pipe context
/// @param pipe_ctx
/// @param spl_in
@@ -94,6 +114,9 @@ void translate_SPL_in_params_from_pipe_ctx(struct pipe_ctx *pipe_ctx, struct spl
case DCN_VERSION_4_2B:
spl_in->callbacks = dcn401_spl_callbacks;
break;
case DCN_VERSION_6_0:
spl_in->callbacks = dcn50_spl_callbacks;
break;
default:
spl_in->callbacks = dcn2_spl_callbacks;
}
@@ -189,7 +212,14 @@ void translate_SPL_in_params_from_pipe_ctx(struct pipe_ctx *pipe_ctx, struct spl
if (pipe_ctx->stream->ctx->dc->debug.force_lls > 0)
spl_in->lls_pref = pipe_ctx->stream->ctx->dc->debug.force_lls;
else
spl_in->lls_pref = plane_state->linear_light_scaling;
if ((plane_state->ctx->dce_version == DCN_VERSION_4_01) ||
(plane_state->ctx->dce_version == DCN_VERSION_4_2) ||
(plane_state->ctx->dce_version == DCN_VERSION_4_2B))
spl_in->lls_pref = LLS_PREF_DONT_CARE;
else
set_linear_light_scaling_preference(plane_state, spl_in);
/* Translate upsampling mode*/
spl_in->upsp_mode = pipe_ctx->plane_res.scl_data.upsp;
/* Translate chroma subsampling offset ( cositing ) */
if (pipe_ctx->stream->ctx->dc->debug.force_cositing)
spl_in->basic_in.cositing = pipe_ctx->stream->ctx->dc->debug.force_cositing - 1;

View File

@@ -125,4 +125,5 @@ bool dc_state_can_clear_stream_cursor_subvp_limit(const struct dc_stream_state *
bool dc_state_is_subvp_in_use(struct dc_state *state);
bool dc_state_is_alt_in_use(const struct dc *dc, const struct dc_state *state);
#endif /* _DC_STATE_PRIV_H_ */

View File

@@ -329,6 +329,9 @@ struct dc_stream_state {
enum dc_drr_trigger_mode drr_trigger_mode;
enum dc_blending_linearity blending_linearity;
struct dc_update_scratch_space *update_scratch;
bool firmware_controlled_hdr_info_packet;
};
@@ -380,6 +383,8 @@ struct dc_stream_update {
bool *scaler_sharpener_update;
bool *sharpening_required;
enum dc_blending_linearity *blending_linearity;
enum dc_drr_trigger_mode *drr_trigger_mode;
};

View File

@@ -389,6 +389,16 @@ struct dc_csc_adjustments {
struct fixed31_32 hue;
};
enum dc_scaling_linearity {
DC_SCALING_LINEARITY_LINEAR,
DC_SCALING_LINEARITY_SOURCE,
};
enum dc_blending_linearity {
DC_BLENDING_LINEARITY_LINEAR,
DC_BLENDING_LINEARITY_SOURCE,
};
/* Scaling format */
enum scaling_transformation {
SCALING_TRANSFORMATION_UNINITIALIZED,
@@ -1035,9 +1045,10 @@ enum backlight_control_type {
BACKLIGHT_CONTROL_AMD_AUX = 2,
};
#if defined(CONFIG_DRM_AMD_SECURE_DISPLAY)
#define MAX_CRC_WINDOW_NUM 2
#if defined(CONFIG_DRM_AMD_SECURE_DISPLAY)
struct otg_phy_mux {
uint8_t phy_output_num;
uint8_t otg_output_num;

View File

@@ -109,4 +109,12 @@ DCCG_DCN42 = dcn42_dccg.o
AMD_DAL_DCCG_DCN42 = $(addprefix $(AMDDALPATH)/dc/dccg/dcn42/,$(DCCG_DCN42))
AMD_DISPLAY_FILES += $(AMD_DAL_DCCG_DCN42)
###############################################################################
# DCN60
###############################################################################
DCCG_DCN60 = dcn60_dccg.o
AMD_DAL_DCCG_DCN60 = $(addprefix $(AMDDALPATH)/dc/dccg/dcn60/,$(DCCG_DCN60))
AMD_DISPLAY_FILES += $(AMD_DAL_DCCG_DCN60)
endif

View File

@@ -377,6 +377,13 @@
type OTG3_DROP_PIXEL;\
type RESYNC_FIFO_LEVEL_ADJUST_EN;
#define DCCG60_REG_FIELD_LIST(type) \
type OTG_ADD_DROP_PIXEL_DONE;\
type DSCCLK0_SRC_SEL;\
type DSCCLK1_SRC_SEL;\
type DSCCLK2_SRC_SEL;\
type DSCCLK3_SRC_SEL;
struct dccg_shift {
DCCG_REG_FIELD_LIST(uint8_t)
DCCG3_REG_FIELD_LIST(uint8_t)
@@ -386,6 +393,7 @@ struct dccg_shift {
DCCG35_REG_FIELD_LIST(uint8_t)
DCCG401_REG_FIELD_LIST(uint8_t)
DCCG42_REG_FIELD_LIST(uint8_t)
DCCG60_REG_FIELD_LIST(uint8_t)
};
struct dccg_mask {
@@ -397,6 +405,7 @@ struct dccg_mask {
DCCG35_REG_FIELD_LIST(uint32_t)
DCCG401_REG_FIELD_LIST(uint32_t)
DCCG42_REG_FIELD_LIST(uint32_t)
DCCG60_REG_FIELD_LIST(uint32_t)
};
#define DCCG_REG_VARIABLE_LIST \
@@ -511,6 +520,7 @@ struct dccg_mask {
struct dccg_registers {
DCCG_REG_VARIABLE_LIST;
uint32_t OTG_ADD_DROP_PIXEL_CNTL;
uint32_t DSCCLK_SRC_SEL;
};
struct dcn_dccg {

View File

@@ -0,0 +1,187 @@
// SPDX-License-Identifier: MIT
//
// Copyright 2025 Advanced Micro Devices, Inc.
#include "reg_helper.h"
#include "core_types.h"
#include "dcn42/dcn42_dccg.h"
#include "dcn60_dccg.h"
#define TO_DCN_DCCG(dccg)\
container_of(dccg, struct dcn_dccg, base)
#define REG(reg) \
(dccg_dcn->regs->reg)
#undef FN
#define FN(reg_name, field_name) \
dccg_dcn->dccg_shift->field_name, dccg_dcn->dccg_mask->field_name
#define CTX \
dccg_dcn->base.ctx
#define DC_LOGGER \
dccg->ctx->logger
static void dccg60_set_pixel_rate_div(
struct dccg *dccg,
uint32_t otg_inst,
enum pixel_rate_div tmds_div,
enum pixel_rate_div unused)
{
(void)unused;
struct dcn_dccg *dccg_dcn = TO_DCN_DCCG(dccg);
uint32_t cur_tmds_div = PIXEL_RATE_DIV_NA;
uint32_t dp_dto_int;
uint32_t reg_val;
// only 2 is valid on dcn401
if (tmds_div != PIXEL_RATE_DIV_BY_2 && tmds_div != PIXEL_RATE_DIV_BY_4) {
return;
}
dccg401_get_pixel_rate_div(dccg, otg_inst, &cur_tmds_div, &dp_dto_int);
if (tmds_div == cur_tmds_div)
return;
// encode enum to register value
reg_val = tmds_div == PIXEL_RATE_DIV_BY_4 ? 1 : 0;
switch (otg_inst) {
case 0:
REG_UPDATE(OTG_PIXEL_RATE_DIV,
OTG0_TMDS_PIXEL_RATE_DIV, reg_val);
break;
case 1:
REG_UPDATE(OTG_PIXEL_RATE_DIV,
OTG1_TMDS_PIXEL_RATE_DIV, reg_val);
break;
case 2:
REG_UPDATE(OTG_PIXEL_RATE_DIV,
OTG2_TMDS_PIXEL_RATE_DIV, reg_val);
break;
case 3:
REG_UPDATE(OTG_PIXEL_RATE_DIV,
OTG3_TMDS_PIXEL_RATE_DIV, reg_val);
break;
default:
BREAK_TO_DEBUGGER();
return;
}
}
static void dccg60_set_dto_dscclk(struct dccg *dccg, uint32_t inst,
uint32_t num_slices_h)
{
struct dcn_dccg *dccg_dcn = TO_DCN_DCCG(dccg);
switch (inst) {
case 0:
REG_UPDATE_2(DSCCLK0_DTO_PARAM, DSCCLK0_DTO_PHASE, 1,
DSCCLK0_DTO_MODULO, 1);
REG_UPDATE(DSCCLK_DTO_CTRL, DSCCLK0_EN, 1);
/*
* Source for dscclk should be set when dto tuned clock is used.
* For 1 slice config, set src to dprefclk.
* For 2 or more slice config, set src to dispclk.
*/
if (num_slices_h == 1)
REG_UPDATE(DSCCLK_SRC_SEL, DSCCLK0_SRC_SEL, 1);
else
REG_UPDATE(DSCCLK_SRC_SEL, DSCCLK0_SRC_SEL, 0);
break;
case 1:
REG_UPDATE_2(DSCCLK1_DTO_PARAM, DSCCLK1_DTO_PHASE, 1,
DSCCLK1_DTO_MODULO, 1);
REG_UPDATE(DSCCLK_DTO_CTRL, DSCCLK1_EN, 1);
if (num_slices_h == 1)
REG_UPDATE(DSCCLK_SRC_SEL, DSCCLK1_SRC_SEL, 1);
else
REG_UPDATE(DSCCLK_SRC_SEL, DSCCLK1_SRC_SEL, 0);
break;
case 2:
REG_UPDATE_2(DSCCLK2_DTO_PARAM, DSCCLK2_DTO_PHASE, 1,
DSCCLK2_DTO_MODULO, 1);
REG_UPDATE(DSCCLK_DTO_CTRL, DSCCLK2_EN, 1);
if (num_slices_h == 1)
REG_UPDATE(DSCCLK_SRC_SEL, DSCCLK2_SRC_SEL, 1);
else
REG_UPDATE(DSCCLK_SRC_SEL, DSCCLK2_SRC_SEL, 0);
break;
case 3:
REG_UPDATE_2(DSCCLK3_DTO_PARAM, DSCCLK3_DTO_PHASE, 1,
DSCCLK3_DTO_MODULO, 1);
REG_UPDATE(DSCCLK_DTO_CTRL, DSCCLK3_EN, 1);
if (num_slices_h == 1)
REG_UPDATE(DSCCLK_SRC_SEL, DSCCLK3_SRC_SEL, 1);
else
REG_UPDATE(DSCCLK_SRC_SEL, DSCCLK3_SRC_SEL, 0);
break;
default:
BREAK_TO_DEBUGGER();
return;
}
}
static const struct dccg_funcs dccg60_funcs = {
.enable_hdmicharclk = dccg401_enable_hdmicharclk,
.disable_hdmicharclk = dccg401_disable_hdmicharclk,
.set_hdmistreamclk = dccg401_set_hdmistreamclk,
.update_dpp_dto = dccg401_update_dpp_dto,
.get_dccg_ref_freq = dccg401_get_dccg_ref_freq,
.dccg_init = dccg401_init,
.set_dpstreamclk = dccg401_set_dpstreamclk,
.enable_symclk32_se = dccg31_enable_symclk32_se,
.disable_symclk32_se = dccg31_disable_symclk32_se,
.enable_symclk32_le = dccg401_enable_symclk32_le,
.disable_symclk32_le = dccg401_disable_symclk32_le,
.set_physymclk = dccg401_set_physymclk,
.set_dtbclk_dto = NULL,
.set_dto_dscclk = dccg60_set_dto_dscclk,
.set_ref_dscclk = dccg401_set_ref_dscclk,
.set_valid_pixel_rate = NULL,
.set_fifo_errdet_ovr_en = dccg2_set_fifo_errdet_ovr_en,
.set_audio_dtbclk_dto = NULL,
.otg_add_pixel = dccg42_otg_add_pixel,
.otg_drop_pixel = dccg42_otg_drop_pixel,
.set_pixel_rate_div = dccg60_set_pixel_rate_div,
.get_pixel_rate_div = dccg401_get_pixel_rate_div,
.set_dp_dto = dccg401_set_dp_dto,
.enable_symclk_se = dccg401_enable_symclk_se,
.disable_symclk_se = dccg401_disable_symclk_se,
.set_dtbclk_p_src = dccg401_set_dtbclk_p_src,
.dccg_read_reg_state = dccg31_read_reg_state,
.allow_clock_gating = dccg2_allow_clock_gating
};
struct dccg *dccg60_create(
struct dc_context *ctx,
const struct dccg_registers *regs,
const struct dccg_shift *dccg_shift,
const struct dccg_mask *dccg_mask)
{
struct dcn_dccg *dccg_dcn = kzalloc(sizeof(*dccg_dcn), GFP_KERNEL);
struct dccg *base;
if (dccg_dcn == NULL) {
BREAK_TO_DEBUGGER();
return NULL;
}
base = &dccg_dcn->base;
base->ctx = ctx;
base->funcs = &dccg60_funcs;
dccg_dcn->regs = regs;
dccg_dcn->dccg_shift = dccg_shift;
dccg_dcn->dccg_mask = dccg_mask;
return &dccg_dcn->base;
}

View File

@@ -0,0 +1,182 @@
// SPDX-License-Identifier: MIT
//
// Copyright 2025 Advanced Micro Devices, Inc.
#ifndef __DCN60_DCCG_H__
#define __DCN60_DCCG_H__
#include "dcn401/dcn401_dccg.h"
#define DCCG_MASK_SH_LIST_DCN60(mask_sh) \
DCCG_SFI(DPPCLK_DTO_CTRL, DTO_DB_EN, DPPCLK, 0, mask_sh),\
DCCG_SFI(DPPCLK_DTO_CTRL, DTO_DB_EN, DPPCLK, 1, mask_sh),\
DCCG_SFI(DPPCLK_DTO_CTRL, DTO_DB_EN, DPPCLK, 2, mask_sh),\
DCCG_SFI(DPPCLK_DTO_CTRL, DTO_DB_EN, DPPCLK, 3, mask_sh),\
DCCG_SF(DPPCLK_CTRL, DPPCLK0_EN, mask_sh),\
DCCG_SF(DPPCLK_CTRL, DPPCLK1_EN, mask_sh),\
DCCG_SF(DPPCLK_CTRL, DPPCLK2_EN, mask_sh),\
DCCG_SF(DPPCLK_CTRL, DPPCLK3_EN, mask_sh),\
DCCG_SF(DPPCLK0_DTO_PARAM, DPPCLK0_DTO_PHASE, mask_sh),\
DCCG_SF(DPPCLK0_DTO_PARAM, DPPCLK0_DTO_MODULO, mask_sh),\
DCCG_SF(HDMICHARCLK0_CLOCK_CNTL, HDMICHARCLK0_EN, mask_sh),\
DCCG_SF(HDMICHARCLK0_CLOCK_CNTL, HDMICHARCLK0_SRC_SEL, mask_sh),\
DCCG_SF(PHYASYMCLK_CLOCK_CNTL, PHYASYMCLK_EN, mask_sh),\
DCCG_SF(PHYASYMCLK_CLOCK_CNTL, PHYASYMCLK_SRC_SEL, mask_sh),\
DCCG_SF(PHYBSYMCLK_CLOCK_CNTL, PHYBSYMCLK_EN, mask_sh),\
DCCG_SF(PHYBSYMCLK_CLOCK_CNTL, PHYBSYMCLK_SRC_SEL, mask_sh),\
DCCG_SF(PHYCSYMCLK_CLOCK_CNTL, PHYCSYMCLK_EN, mask_sh),\
DCCG_SF(PHYCSYMCLK_CLOCK_CNTL, PHYCSYMCLK_SRC_SEL, mask_sh),\
DCCG_SF(PHYDSYMCLK_CLOCK_CNTL, PHYDSYMCLK_EN, mask_sh),\
DCCG_SF(PHYDSYMCLK_CLOCK_CNTL, PHYDSYMCLK_SRC_SEL, mask_sh),\
DCCG_SF(DPSTREAMCLK_CNTL, DPSTREAMCLK0_EN, mask_sh),\
DCCG_SF(DPSTREAMCLK_CNTL, DPSTREAMCLK1_EN, mask_sh),\
DCCG_SF(DPSTREAMCLK_CNTL, DPSTREAMCLK2_EN, mask_sh),\
DCCG_SF(DPSTREAMCLK_CNTL, DPSTREAMCLK3_EN, mask_sh),\
DCCG_SF(DPSTREAMCLK_CNTL, DPSTREAMCLK0_SRC_SEL, mask_sh),\
DCCG_SF(DPSTREAMCLK_CNTL, DPSTREAMCLK1_SRC_SEL, mask_sh),\
DCCG_SF(DPSTREAMCLK_CNTL, DPSTREAMCLK2_SRC_SEL, mask_sh),\
DCCG_SF(DPSTREAMCLK_CNTL, DPSTREAMCLK3_SRC_SEL, mask_sh),\
DCCG_SF(HDMISTREAMCLK_CNTL, HDMISTREAMCLK0_EN, mask_sh),\
DCCG_SF(HDMISTREAMCLK_CNTL, HDMISTREAMCLK0_SRC_SEL, mask_sh),\
DCCG_SF(SYMCLK32_SE_CNTL, SYMCLK32_SE0_SRC_SEL, mask_sh),\
DCCG_SF(SYMCLK32_SE_CNTL, SYMCLK32_SE1_SRC_SEL, mask_sh),\
DCCG_SF(SYMCLK32_SE_CNTL, SYMCLK32_SE2_SRC_SEL, mask_sh),\
DCCG_SF(SYMCLK32_SE_CNTL, SYMCLK32_SE3_SRC_SEL, mask_sh),\
DCCG_SF(SYMCLK32_SE_CNTL, SYMCLK32_SE0_EN, mask_sh),\
DCCG_SF(SYMCLK32_SE_CNTL, SYMCLK32_SE1_EN, mask_sh),\
DCCG_SF(SYMCLK32_SE_CNTL, SYMCLK32_SE2_EN, mask_sh),\
DCCG_SF(SYMCLK32_SE_CNTL, SYMCLK32_SE3_EN, mask_sh),\
DCCG_SF(SYMCLK32_LE_CNTL, SYMCLK32_LE0_SRC_SEL, mask_sh),\
DCCG_SF(SYMCLK32_LE_CNTL, SYMCLK32_LE1_SRC_SEL, mask_sh),\
DCCG_SF(SYMCLK32_LE_CNTL, SYMCLK32_LE0_EN, mask_sh),\
DCCG_SF(SYMCLK32_LE_CNTL, SYMCLK32_LE1_EN, mask_sh),\
DCCG_SFII(OTG, PIXEL_RATE_CNTL, PIPE, DTO_SRC_SEL, 0, mask_sh),\
DCCG_SFII(OTG, PIXEL_RATE_CNTL, PIPE, DTO_SRC_SEL, 1, mask_sh),\
DCCG_SFII(OTG, PIXEL_RATE_CNTL, PIPE, DTO_SRC_SEL, 2, mask_sh),\
DCCG_SFII(OTG, PIXEL_RATE_CNTL, PIPE, DTO_SRC_SEL, 3, mask_sh),\
DCCG_SF(OTG_ADD_DROP_PIXEL_CNTL, OTG0_ADD_PIXEL, mask_sh),\
DCCG_SF(OTG_ADD_DROP_PIXEL_CNTL, OTG1_ADD_PIXEL, mask_sh),\
DCCG_SF(OTG_ADD_DROP_PIXEL_CNTL, OTG2_ADD_PIXEL, mask_sh),\
DCCG_SF(OTG_ADD_DROP_PIXEL_CNTL, OTG3_ADD_PIXEL, mask_sh),\
DCCG_SF(OTG_ADD_DROP_PIXEL_CNTL, OTG0_DROP_PIXEL, mask_sh),\
DCCG_SF(OTG_ADD_DROP_PIXEL_CNTL, OTG1_DROP_PIXEL, mask_sh),\
DCCG_SF(OTG_ADD_DROP_PIXEL_CNTL, OTG2_DROP_PIXEL, mask_sh),\
DCCG_SF(OTG_ADD_DROP_PIXEL_CNTL, OTG3_DROP_PIXEL, mask_sh),\
DCCG_SF(OTG_PIXEL_RATE_DIV, OTG0_TMDS_PIXEL_RATE_DIV, mask_sh),\
DCCG_SF(OTG_PIXEL_RATE_DIV, DPDTO0_INT, mask_sh),\
DCCG_SF(OTG_PIXEL_RATE_DIV, OTG1_TMDS_PIXEL_RATE_DIV, mask_sh),\
DCCG_SF(OTG_PIXEL_RATE_DIV, DPDTO1_INT, mask_sh),\
DCCG_SF(OTG_PIXEL_RATE_DIV, OTG2_TMDS_PIXEL_RATE_DIV, mask_sh),\
DCCG_SF(OTG_PIXEL_RATE_DIV, DPDTO2_INT, mask_sh),\
DCCG_SF(OTG_PIXEL_RATE_DIV, OTG3_TMDS_PIXEL_RATE_DIV, mask_sh),\
DCCG_SF(OTG_PIXEL_RATE_DIV, DPDTO3_INT, mask_sh),\
DCCG_SF(DTBCLK_P_CNTL, DTBCLK_P0_SRC_SEL, mask_sh),\
DCCG_SF(DTBCLK_P_CNTL, DTBCLK_P0_EN, mask_sh),\
DCCG_SF(DTBCLK_P_CNTL, DTBCLK_P1_SRC_SEL, mask_sh),\
DCCG_SF(DTBCLK_P_CNTL, DTBCLK_P1_EN, mask_sh),\
DCCG_SF(DTBCLK_P_CNTL, DTBCLK_P2_SRC_SEL, mask_sh),\
DCCG_SF(DTBCLK_P_CNTL, DTBCLK_P2_EN, mask_sh),\
DCCG_SF(DTBCLK_P_CNTL, DTBCLK_P3_SRC_SEL, mask_sh),\
DCCG_SF(DTBCLK_P_CNTL, DTBCLK_P3_EN, mask_sh),\
DCCG_SF(DCCG_AUDIO_DTO_SOURCE, DCCG_AUDIO_DTO_SEL, mask_sh),\
DCCG_SF(DCCG_AUDIO_DTO_SOURCE, DCCG_AUDIO_DTO0_SOURCE_SEL, mask_sh),\
DCCG_SF(DCCG_GATE_DISABLE_CNTL2, PHYASYMCLK_ROOT_GATE_DISABLE, mask_sh),\
DCCG_SF(DCCG_GATE_DISABLE_CNTL2, PHYBSYMCLK_ROOT_GATE_DISABLE, mask_sh),\
DCCG_SF(DCCG_GATE_DISABLE_CNTL2, PHYCSYMCLK_ROOT_GATE_DISABLE, mask_sh),\
DCCG_SF(DCCG_GATE_DISABLE_CNTL2, PHYDSYMCLK_ROOT_GATE_DISABLE, mask_sh),\
DCCG_SFII(OTG, PIXEL_RATE_CNTL, DP_DTO, ENABLE, 0, mask_sh),\
DCCG_SFII(OTG, PIXEL_RATE_CNTL, DP_DTO, ENABLE, 1, mask_sh),\
DCCG_SFII(OTG, PIXEL_RATE_CNTL, DP_DTO, ENABLE, 2, mask_sh),\
DCCG_SFII(OTG, PIXEL_RATE_CNTL, DP_DTO, ENABLE, 3, mask_sh),\
DCCG_SFII(OTG, PIXEL_RATE_CNTL, PIPE, DTO_SRC_SEL, 0, mask_sh),\
DCCG_SFII(OTG, PIXEL_RATE_CNTL, PIPE, DTO_SRC_SEL, 1, mask_sh),\
DCCG_SFII(OTG, PIXEL_RATE_CNTL, PIPE, DTO_SRC_SEL, 2, mask_sh),\
DCCG_SFII(OTG, PIXEL_RATE_CNTL, PIPE, DTO_SRC_SEL, 3, mask_sh),\
DCCG_SF(DSCCLK_DTO_CTRL, DSCCLK0_EN, mask_sh),\
DCCG_SF(DSCCLK_DTO_CTRL, DSCCLK1_EN, mask_sh),\
DCCG_SF(DSCCLK_DTO_CTRL, DSCCLK2_EN, mask_sh),\
DCCG_SF(DSCCLK_DTO_CTRL, DSCCLK3_EN, mask_sh),\
DCCG_SF(DSCCLK0_DTO_PARAM, DSCCLK0_DTO_PHASE, mask_sh),\
DCCG_SF(DSCCLK0_DTO_PARAM, DSCCLK0_DTO_MODULO, mask_sh),\
DCCG_SF(DSCCLK1_DTO_PARAM, DSCCLK1_DTO_PHASE, mask_sh),\
DCCG_SF(DSCCLK1_DTO_PARAM, DSCCLK1_DTO_MODULO, mask_sh),\
DCCG_SF(DSCCLK2_DTO_PARAM, DSCCLK2_DTO_PHASE, mask_sh),\
DCCG_SF(DSCCLK2_DTO_PARAM, DSCCLK2_DTO_MODULO, mask_sh),\
DCCG_SF(DSCCLK3_DTO_PARAM, DSCCLK3_DTO_PHASE, mask_sh),\
DCCG_SF(DSCCLK3_DTO_PARAM, DSCCLK3_DTO_MODULO, mask_sh),\
DCCG_SF(DCCG_GATE_DISABLE_CNTL2, HDMICHARCLK0_GATE_DISABLE, mask_sh),\
DCCG_SF(DCCG_GATE_DISABLE_CNTL3, HDMISTREAMCLK0_GATE_DISABLE, mask_sh),\
DCCG_SF(DCCG_GATE_DISABLE_CNTL3, SYMCLK32_ROOT_SE0_GATE_DISABLE, mask_sh),\
DCCG_SF(DCCG_GATE_DISABLE_CNTL3, SYMCLK32_ROOT_SE1_GATE_DISABLE, mask_sh),\
DCCG_SF(DCCG_GATE_DISABLE_CNTL3, SYMCLK32_ROOT_SE2_GATE_DISABLE, mask_sh),\
DCCG_SF(DCCG_GATE_DISABLE_CNTL3, SYMCLK32_ROOT_SE3_GATE_DISABLE, mask_sh),\
DCCG_SF(DCCG_GATE_DISABLE_CNTL3, SYMCLK32_ROOT_LE0_GATE_DISABLE, mask_sh),\
DCCG_SF(DCCG_GATE_DISABLE_CNTL3, SYMCLK32_ROOT_LE1_GATE_DISABLE, mask_sh),\
DCCG_SF(DCCG_GATE_DISABLE_CNTL3, SYMCLK32_ROOT_LE2_GATE_DISABLE, mask_sh),\
DCCG_SF(DCCG_GATE_DISABLE_CNTL3, SYMCLK32_ROOT_LE3_GATE_DISABLE, mask_sh),\
DCCG_SF(DCCG_GATE_DISABLE_CNTL3, SYMCLK32_SE0_GATE_DISABLE, mask_sh),\
DCCG_SF(DCCG_GATE_DISABLE_CNTL3, SYMCLK32_SE1_GATE_DISABLE, mask_sh),\
DCCG_SF(DCCG_GATE_DISABLE_CNTL3, SYMCLK32_SE2_GATE_DISABLE, mask_sh),\
DCCG_SF(DCCG_GATE_DISABLE_CNTL3, SYMCLK32_SE3_GATE_DISABLE, mask_sh),\
DCCG_SF(DCCG_GATE_DISABLE_CNTL3, SYMCLK32_LE0_GATE_DISABLE, mask_sh),\
DCCG_SF(DCCG_GATE_DISABLE_CNTL3, SYMCLK32_LE1_GATE_DISABLE, mask_sh),\
DCCG_SF(DCCG_GATE_DISABLE_CNTL3, SYMCLK32_LE2_GATE_DISABLE, mask_sh),\
DCCG_SF(DCCG_GATE_DISABLE_CNTL3, SYMCLK32_LE3_GATE_DISABLE, mask_sh),\
DCCG_SF(DCCG_GATE_DISABLE_CNTL4, HDMICHARCLK0_ROOT_GATE_DISABLE, mask_sh),\
DCCG_SF(DCCG_GATE_DISABLE_CNTL4, PHYA_REFCLK_ROOT_GATE_DISABLE, mask_sh),\
DCCG_SF(DCCG_GATE_DISABLE_CNTL4, PHYB_REFCLK_ROOT_GATE_DISABLE, mask_sh),\
DCCG_SF(DCCG_GATE_DISABLE_CNTL4, PHYC_REFCLK_ROOT_GATE_DISABLE, mask_sh),\
DCCG_SF(DCCG_GATE_DISABLE_CNTL4, PHYD_REFCLK_ROOT_GATE_DISABLE, mask_sh),\
DCCG_SF(DCCG_GATE_DISABLE_CNTL5, DTBCLK_P0_GATE_DISABLE, mask_sh),\
DCCG_SF(DCCG_GATE_DISABLE_CNTL5, DTBCLK_P1_GATE_DISABLE, mask_sh),\
DCCG_SF(DCCG_GATE_DISABLE_CNTL5, DTBCLK_P2_GATE_DISABLE, mask_sh),\
DCCG_SF(DCCG_GATE_DISABLE_CNTL5, DTBCLK_P3_GATE_DISABLE, mask_sh),\
DCCG_SF(DCCG_GATE_DISABLE_CNTL5, SYMCLKA_FE_ROOT_GATE_DISABLE, mask_sh),\
DCCG_SF(DCCG_GATE_DISABLE_CNTL5, SYMCLKB_FE_ROOT_GATE_DISABLE, mask_sh),\
DCCG_SF(DCCG_GATE_DISABLE_CNTL5, SYMCLKC_FE_ROOT_GATE_DISABLE, mask_sh),\
DCCG_SF(DCCG_GATE_DISABLE_CNTL5, SYMCLKD_FE_ROOT_GATE_DISABLE, mask_sh),\
DCCG_SF(DCCG_GATE_DISABLE_CNTL5, SYMCLKA_ROOT_GATE_DISABLE, mask_sh),\
DCCG_SF(DCCG_GATE_DISABLE_CNTL5, SYMCLKB_ROOT_GATE_DISABLE, mask_sh),\
DCCG_SF(DCCG_GATE_DISABLE_CNTL5, SYMCLKC_ROOT_GATE_DISABLE, mask_sh),\
DCCG_SF(DCCG_GATE_DISABLE_CNTL5, SYMCLKD_ROOT_GATE_DISABLE, mask_sh),\
DCCG_SF(DCCG_GATE_DISABLE_CNTL5, DPSTREAMCLK0_ROOT_GATE_DISABLE, mask_sh),\
DCCG_SF(DCCG_GATE_DISABLE_CNTL5, DPSTREAMCLK1_ROOT_GATE_DISABLE, mask_sh),\
DCCG_SF(DCCG_GATE_DISABLE_CNTL5, DPSTREAMCLK2_ROOT_GATE_DISABLE, mask_sh),\
DCCG_SF(DCCG_GATE_DISABLE_CNTL5, DPSTREAMCLK3_ROOT_GATE_DISABLE, mask_sh),\
DCCG_SF(DCCG_GATE_DISABLE_CNTL5, DPSTREAMCLK0_GATE_DISABLE, mask_sh),\
DCCG_SF(DCCG_GATE_DISABLE_CNTL5, DPSTREAMCLK1_GATE_DISABLE, mask_sh),\
DCCG_SF(DCCG_GATE_DISABLE_CNTL5, DPSTREAMCLK2_GATE_DISABLE, mask_sh),\
DCCG_SF(DCCG_GATE_DISABLE_CNTL5, DPSTREAMCLK3_GATE_DISABLE, mask_sh),\
DCCG_SF(DCCG_GATE_DISABLE_CNTL6, DSCCLK0_ROOT_GATE_DISABLE, mask_sh),\
DCCG_SF(DCCG_GATE_DISABLE_CNTL6, DSCCLK1_ROOT_GATE_DISABLE, mask_sh),\
DCCG_SF(DCCG_GATE_DISABLE_CNTL6, DSCCLK2_ROOT_GATE_DISABLE, mask_sh),\
DCCG_SF(DCCG_GATE_DISABLE_CNTL6, DSCCLK3_ROOT_GATE_DISABLE, mask_sh),\
DCCG_SF(DCCG_GATE_DISABLE_CNTL6, DPPCLK0_ROOT_GATE_DISABLE, mask_sh),\
DCCG_SF(DCCG_GATE_DISABLE_CNTL6, DPPCLK1_ROOT_GATE_DISABLE, mask_sh),\
DCCG_SF(DCCG_GATE_DISABLE_CNTL6, DPPCLK2_ROOT_GATE_DISABLE, mask_sh),\
DCCG_SF(DCCG_GATE_DISABLE_CNTL6, DPPCLK3_ROOT_GATE_DISABLE, mask_sh),\
DCCG_SF(DCCG_GATE_DISABLE_CNTL6, HDMISTREAMCLK0_ROOT_GATE_DISABLE, mask_sh),\
DCCG_SF(SYMCLKA_CLOCK_ENABLE, SYMCLKA_CLOCK_ENABLE, mask_sh),\
DCCG_SF(SYMCLKB_CLOCK_ENABLE, SYMCLKB_CLOCK_ENABLE, mask_sh),\
DCCG_SF(SYMCLKC_CLOCK_ENABLE, SYMCLKC_CLOCK_ENABLE, mask_sh),\
DCCG_SF(SYMCLKD_CLOCK_ENABLE, SYMCLKD_CLOCK_ENABLE, mask_sh),\
DCCG_SF(SYMCLKA_CLOCK_ENABLE, SYMCLKA_FE_EN, mask_sh),\
DCCG_SF(SYMCLKB_CLOCK_ENABLE, SYMCLKB_FE_EN, mask_sh),\
DCCG_SF(SYMCLKC_CLOCK_ENABLE, SYMCLKC_FE_EN, mask_sh),\
DCCG_SF(SYMCLKD_CLOCK_ENABLE, SYMCLKD_FE_EN, mask_sh),\
DCCG_SF(SYMCLKA_CLOCK_ENABLE, SYMCLKA_FE_SRC_SEL, mask_sh),\
DCCG_SF(SYMCLKB_CLOCK_ENABLE, SYMCLKB_FE_SRC_SEL, mask_sh),\
DCCG_SF(SYMCLKC_CLOCK_ENABLE, SYMCLKC_FE_SRC_SEL, mask_sh),\
DCCG_SF(SYMCLKD_CLOCK_ENABLE, SYMCLKD_FE_SRC_SEL, mask_sh),\
DCCG_SF(DSCCLK_SRC_SEL, DSCCLK0_SRC_SEL, mask_sh),\
DCCG_SF(DSCCLK_SRC_SEL, DSCCLK1_SRC_SEL, mask_sh),\
DCCG_SF(DSCCLK_SRC_SEL, DSCCLK2_SRC_SEL, mask_sh),\
DCCG_SF(DSCCLK_SRC_SEL, DSCCLK3_SRC_SEL, mask_sh),\
struct dccg *dccg60_create(
struct dc_context *ctx,
const struct dccg_registers *regs,
const struct dccg_shift *dccg_shift,
const struct dccg_mask *dccg_mask);
#endif //__DCN60_DCCG_H__

View File

@@ -1117,6 +1117,10 @@ static bool dcn401_program_pix_clk(
if (clock_source->ctx->dc->caps.is_apu &&
pix_clk_params->requested_pix_clk_100hz &&
dc_is_hdmi_frl_signal(pix_clk_params->signal_type)) {
//make sure dtbclk is enabled
if (clock_source->ctx->dc->clk_mgr->funcs->request_dtbclk)
clock_source->ctx->dc->clk_mgr->funcs->request_dtbclk(
clock_source->ctx->dc->clk_mgr, true);
/*need hdmistreamclk before vpg block register access*/
clock_source->ctx->dc->res_pool->dccg->funcs->set_hdmistreamclk(
clock_source->ctx->dc->res_pool->dccg,
@@ -1493,6 +1497,13 @@ static const struct clock_source_funcs dcn401_clk_src_funcs = {
.get_dp_dto_frequency_100hz = dcn401_get_dp_dto_frequency_100hz
};
static const struct clock_source_funcs dcn50_clk_src_funcs = {
.cs_power_down = dce110_clock_source_power_down,
.program_pix_clk = dcn401_program_pix_clk,
.get_pix_clk_dividers = dcn3_get_pix_clk_dividers,
.get_dp_dto_frequency_100hz = dcn401_get_dp_dto_frequency_100hz
};
/*****************************************/
/* Constructor */
/*****************************************/
@@ -1922,6 +1933,23 @@ bool dcn401_clk_src_construct(
return ret;
}
bool dcn50_clk_src_construct(
struct dce110_clk_src *clk_src,
struct dc_context *ctx,
struct dc_bios *bios,
enum clock_source_id id,
const struct dce110_clk_src_regs *regs,
const struct dce110_clk_src_shift *cs_shift,
const struct dce110_clk_src_mask *cs_mask)
{
bool ret = dce112_clk_src_construct(clk_src, ctx, bios, id, regs, cs_shift, cs_mask);
clk_src->base.funcs = &dcn50_clk_src_funcs;
return ret;
}
bool dcn301_clk_src_construct(
struct dce110_clk_src *clk_src,
struct dc_context *ctx,

View File

@@ -339,6 +339,15 @@ bool dcn401_clk_src_construct(
const struct dce110_clk_src_regs *regs,
const struct dce110_clk_src_shift *cs_shift,
const struct dce110_clk_src_mask *cs_mask);
bool dcn50_clk_src_construct(
struct dce110_clk_src *clk_src,
struct dc_context *ctx,
struct dc_bios *bios,
enum clock_source_id id,
const struct dce110_clk_src_regs *regs,
const struct dce110_clk_src_shift *cs_shift,
const struct dce110_clk_src_mask *cs_mask);
/* this table is use to find *1.001 and /1.001 pixel rates from non-precise pixel rate */
struct pixel_rate_range_table_entry {
unsigned int range_min_khz;

View File

@@ -132,4 +132,12 @@ DIO_DCN42 = dcn42_dio_link_encoder.o dcn42_dio_stream_encoder.o
AMD_DAL_DIO_DCN42 = $(addprefix $(AMDDALPATH)/dc/dio/dcn42/,$(DIO_DCN42))
AMD_DISPLAY_FILES += $(AMD_DAL_DIO_DCN42)
###############################################################################
# DCN60
###############################################################################
DIO_DCN60 = dcn60_dio_link_encoder.o dcn60_dio_stream_encoder.o
AMD_DAL_DIO_DCN60 = $(addprefix $(AMDDALPATH)/dc/dio/dcn60/,$(DIO_DCN60))
AMD_DISPLAY_FILES += $(AMD_DAL_DIO_DCN60)
endif

View File

@@ -75,12 +75,14 @@ struct dcn10_link_enc_aux_registers {
uint32_t AUX_DPHY_RX_CONTROL0;
uint32_t AUX_DPHY_TX_CONTROL;
uint32_t AUX_DPHY_RX_CONTROL1;
uint32_t DC_GPIO_DDC;
};
struct dcn10_link_enc_hpd_registers {
uint32_t DC_HPD_CONTROL;
uint32_t DC_HPD_INT_STATUS;
uint32_t DC_HPD_TOGGLE_FILT_CNTL;
uint32_t HPD_CTRL;
};
struct dcn10_link_enc_registers {
@@ -171,6 +173,8 @@ struct dcn10_link_enc_registers {
uint32_t DIO_LINKF_CNTL;
uint32_t DIO_CLK_CNTL;
uint32_t DIG_BE_CLK_CNTL;
uint32_t HDCP_I2C_CONTROL_0;
uint32_t HDCP_INT_CONTROL;
};
#define LE_SF(reg_name, field_name, post_fix)\
@@ -508,12 +512,23 @@ struct dcn10_link_enc_registers {
type SYMCLKF_G_HDCP_GATE_DIS;\
type SYMCLKG_G_HDCP_GATE_DIS
#define DCN60_LINK_ENCODER_REG_FIELD_LIST(type) \
type HDCP_I2C_DISABLE;\
type HDCP_I2C_DDC_SELECT;\
type HDCP_I2C_XFER_REQ_MASK;\
type AUX_PAD1_MODE;\
type HPD1_Y_POL_INVERT;\
type HPD2_Y_POL_INVERT;\
type HPD3_Y_POL_INVERT;\
type HPD4_Y_POL_INVERT
struct dcn10_link_enc_shift {
DCN_LINK_ENCODER_REG_FIELD_LIST(uint8_t);
DCN20_LINK_ENCODER_REG_FIELD_LIST(uint8_t);
DCN30_LINK_ENCODER_REG_FIELD_LIST(uint8_t);
DCN31_LINK_ENCODER_REG_FIELD_LIST(uint8_t);
DCN35_LINK_ENCODER_REG_FIELD_LIST(uint8_t);
DCN60_LINK_ENCODER_REG_FIELD_LIST(uint8_t);
};
struct dcn10_link_enc_mask {
@@ -522,6 +537,7 @@ struct dcn10_link_enc_mask {
DCN30_LINK_ENCODER_REG_FIELD_LIST(uint32_t);
DCN31_LINK_ENCODER_REG_FIELD_LIST(uint32_t);
DCN35_LINK_ENCODER_REG_FIELD_LIST(uint32_t);
DCN60_LINK_ENCODER_REG_FIELD_LIST(uint32_t);
};
struct dcn10_link_encoder {

View File

@@ -384,6 +384,20 @@ enum signal_type dcn401_get_dig_mode(
}
}
void dcn401_setup_ri_pj_check_in_sw_or_hw_mode(
struct link_encoder *enc,
unsigned char aux_or_ddc_instance,
bool enable_sw_mode)
{
struct dcn10_link_encoder *enc10 = TO_DCN10_LINK_ENC(enc);
REG_UPDATE_2(HDCP_I2C_CONTROL_0,
HDCP_I2C_DISABLE, enable_sw_mode,
HDCP_I2C_DDC_SELECT, aux_or_ddc_instance);
REG_UPDATE(HDCP_INT_CONTROL, HDCP_I2C_XFER_REQ_MASK, enable_sw_mode);
}
static const struct link_encoder_funcs dcn401_link_enc_funcs = {
.read_state = link_enc2_read_state,
.validate_output_with_stream =
@@ -423,6 +437,7 @@ static const struct link_encoder_funcs dcn401_link_enc_funcs = {
.get_txffe = dpcs401_get_txffe,
.set_txffe = dpcs401_set_txffe,
.set_dio_phy_mux = dcn31_link_encoder_set_dio_phy_mux,
.setup_ri_pj_check_in_sw_or_hw_mode = dcn401_setup_ri_pj_check_in_sw_or_hw_mode,
.get_hpd_state = dcn10_get_hpd_state,
.program_hpd_filter = dcn10_program_hpd_filter,
};

View File

@@ -104,6 +104,11 @@
LE_SF(DP_AUX0_AUX_DPHY_RX_CONTROL1, AUX_RX_TIMEOUT_LEN, mask_sh),\
LE_SF(DP_AUX0_AUX_DPHY_RX_CONTROL1, AUX_RX_TIMEOUT_LEN_MUL, mask_sh)
#define LINK_ENCODER_MASK_SH_LIST_DCN60_ON_DCN401(mask_sh) \
LE_SF(DIG0_HDCP_I2C_CONTROL_0, HDCP_I2C_DISABLE, mask_sh),\
LE_SF(DIG0_HDCP_I2C_CONTROL_0, HDCP_I2C_DDC_SELECT, mask_sh),\
LE_SF(DIG0_HDCP_INT_CONTROL, HDCP_I2C_XFER_REQ_MASK, mask_sh)
void dcn401_link_encoder_construct(
struct dcn20_link_encoder *enc20,
const struct encoder_init_data *init_data,
@@ -147,4 +152,9 @@ enum signal_type dcn401_get_dig_mode(
bool dcn401_is_dig_enabled(struct link_encoder *enc);
enum signal_type dcn401_get_dig_mode(struct link_encoder *enc);
void dcn401_setup_ri_pj_check_in_sw_or_hw_mode(
struct link_encoder *enc,
unsigned char aux_or_ddc_instance,
bool enable_sw_mode);
#endif /* __DC_LINK_ENCODER__DCN401_H__ */

View File

@@ -0,0 +1,373 @@
// SPDX-License-Identifier: MIT
//
// Copyright 2025 Advanced Micro Devices, Inc.
#include "reg_helper.h"
#include "core_types.h"
#include "link_encoder.h"
#include "dcn31/dcn31_dio_link_encoder.h"
#include "dcn32/dcn32_dio_link_encoder.h"
#include "dcn401/dcn401_dio_link_encoder.h"
#include "dcn42/dcn42_dio_link_encoder.h"
#include "dcn60_dio_link_encoder.h"
#include "stream_encoder.h"
#include "dc_bios_types.h"
#include "gpio_service_interface.h"
#ifndef MIN
#define MIN(X, Y) ((X) < (Y) ? (X) : (Y))
#endif
#define CTX \
enc10->base.ctx
#define DC_LOGGER \
enc10->base.ctx->logger
#define REG(reg)\
(enc10->link_regs->reg)
#undef FN
#define FN(reg_name, field_name) \
enc10->link_shift->field_name, enc10->link_mask->field_name
#define AUX_REG(reg)\
(enc10->aux_regs->reg)
#define AUX_REG_READ(reg_name) \
dm_read_reg(CTX, AUX_REG(reg_name))
#define AUX_REG_WRITE(reg_name, val) \
dm_write_reg(CTX, AUX_REG(reg_name), val)
#define AUX_REG_UPDATE_N(reg_name, n, ...) \
generic_reg_update_ex(CTX, \
AUX_REG(reg_name), \
n, __VA_ARGS__)
#define AUX_REG_UPDATE_1(reg_name, field, val) \
AUX_REG_UPDATE_N(reg_name, 1, \
FN(reg_name, field), val)
#define HPD_REG(reg)\
(enc10->hpd_regs->reg)
#define HPD_REG_GET(reg_name, field, val) \
generic_reg_get(CTX, HPD_REG(reg_name), \
FN(reg_name, field), val)
#define HPD_REG_SET_N(reg_name, n, initial_val, ...) \
generic_reg_set_ex(CTX, \
HPD_REG(reg_name), \
initial_val, \
n, __VA_ARGS__)
#define HPD_REG_SET_2(reg, init_value, f1, v1, f2, v2) \
HPD_REG_SET_N(reg, 2, init_value, \
FN(reg, f1), v1,\
FN(reg, f2), v2)
#define HPD_REG_SET_1(reg, init_value, f1, v1) \
HPD_REG_SET_N(reg, 1, init_value, \
FN(reg, f1), v1)
#ifndef MIN
#define MIN(X, Y) ((X) < (Y) ? (X) : (Y))
#endif
// DP2/HDMI FRL PRESET_LEVEL EQ options for FFE byte
// Preset level 0-32 bits[0:4]
#define PRESET_LEVEL_BYTE__LEVEL__SHIFT 0x0u
#define PRESET_LEVEL_BYTE__LEVEL__MASK 0x1Fu
// Enable no preshoot bit[5]
#define PRESET_LEVEL_BYTE__NO_PRE__SHIFT 0x5u
#define PRESET_LEVEL_BYTE__NO_PRE__MASK 0x20u
// Enable no demphasis bit[6]
#define PRESET_LEVEL_BYTE__NO_DEMPH__SHIFT 0x6u
#define PRESET_LEVEL_BYTE__NO_DEMPH__MASK 0x40u
// Enable method 2 bit[7]
#define PRESET_LEVEL_BYTE__METHOD_2__SHIFT 0x7u
#define PRESET_LEVEL_BYTE__METHOD_2__MASK 0x80u
void enc60_hw_init(struct link_encoder *enc)
{
struct dcn10_link_encoder *enc10 = TO_DCN10_LINK_ENC(enc);
switch (enc10->base.connector.id) {
case CONNECTOR_ID_DISPLAY_PORT:
case CONNECTOR_ID_EDP:
AUX_REG_WRITE(AUX_DPHY_RX_CONTROL0, 0x103d1110);
AUX_REG_WRITE(AUX_DPHY_TX_CONTROL, 0x21c7a);
dcn10_aux_initialize(enc10);
break;
default:
break;
}
REG_UPDATE(TMDS_CTL_BITS, TMDS_CTL0, 1);
// Polarity update is handled by DMU init.
// AUX_PAD_MODE update done by DMU init
}
static enum bp_result link_transmitter_control(
struct dcn10_link_encoder *enc10,
struct bp_transmitter_control *cntl)
{
enum bp_result result;
struct dc_bios *bp = enc10->base.ctx->dc_bios;
result = bp->funcs->transmitter_control(bp, cntl);
return result;
}
//---------------------------------------------------
// Task: Program EQ setting
// Note:
// EQ setting can be dont during P2 state or P0 state
// If set in P0 state, The values are latched in a single
// cycle of txX_clk but will take maximum of 40 txX_clk symbols
// to be reflected on the output. During this period the
// analog serial lines might have a transitional behavior.
//---------------------------------------------------
void dpcs60_program_eq_setting(
struct link_encoder *enc,
uint8_t FFE_Level,
bool de_emphasis_only,
bool pre_shoot_only,
bool no_ffe,
const struct dc_hdmi_frl_link_settings *link_settings)
{
const uint8_t max_ffe_level =
(link_settings->frl_link_rate > HDMI_FRL_LINK_RATE_12GBPS) ? 0x7 : 0x3;
struct dcn10_link_encoder *enc10 = TO_DCN10_LINK_ENC(enc);
struct bp_transmitter_control cntl = { 0 };
if (enc10->base.ctx->dc->debug.ignore_ffe)
return;
if (FFE_Level <= max_ffe_level)
enc10->base.txffe_state = FFE_Level;
if (enc10->base.ctx->dc->debug.select_ffe)
enc10->base.txffe_state =
(uint8_t)enc10->base.ctx->dc->debug.select_ffe;
if (FFE_Level == 0xEE) {
enc10->base.txffe_state++;
if (enc10->base.txffe_state > max_ffe_level)
enc10->base.txffe_state = 0;
}
if (no_ffe) {
de_emphasis_only = true;
pre_shoot_only = true;
}
/* Pass on the input params to DMCUB for proper calc of eq settings */
cntl.lane_settings = ((de_emphasis_only ? 1u : 0u) << PRESET_LEVEL_BYTE__NO_PRE__SHIFT) |
((pre_shoot_only ? 1u : 0u) << PRESET_LEVEL_BYTE__NO_DEMPH__SHIFT) |
((enc10->base.txffe_state & PRESET_LEVEL_BYTE__LEVEL__MASK)
<< PRESET_LEVEL_BYTE__LEVEL__SHIFT);
cntl.lane_select = 0;
cntl.action = TRANSMITTER_CONTROL_SET_VOLTAGE_AND_PREEMPASIS;
cntl.transmitter = enc10->base.transmitter;
cntl.connector_obj_id = enc10->base.connector;
cntl.lanes_number = link_settings->frl_num_lanes;
cntl.hpd_sel = enc10->base.hpd_source;
/* Use below or dc_link_frl_bandwidth_kbps()? */
switch (link_settings->frl_link_rate) {
case HDMI_FRL_LINK_RATE_3GBPS:
cntl.pixel_clock = 166667 / 10;
break;
case HDMI_FRL_LINK_RATE_6GBPS:
case HDMI_FRL_LINK_RATE_6GBPS_4LANE:
cntl.pixel_clock = 333333 / 10;
break;
case HDMI_FRL_LINK_RATE_8GBPS:
cntl.pixel_clock = 444444 / 10;
break;
case HDMI_FRL_LINK_RATE_10GBPS:
cntl.pixel_clock = 555555 / 10;
break;
case HDMI_FRL_LINK_RATE_12GBPS:
cntl.pixel_clock = 666667 / 10;
break;
case HDMI_FRL_LINK_RATE_16GBPS:
cntl.pixel_clock = 888889 / 10;
break;
case HDMI_FRL_LINK_RATE_20GBPS:
default:
cntl.pixel_clock = 1111111 / 10;
break;
}
/* call VBIOS table to set eq settings - voltage swing and pre-emphasis */
link_transmitter_control(enc10, &cntl);
}
static const struct link_encoder_funcs dcn60_link_enc_funcs = {
.read_state = link_enc2_read_state,
.validate_output_with_stream =
dcn30_link_encoder_validate_output_with_stream,
.hw_init = enc60_hw_init,
.setup = dcn401_link_encoder_setup,
.enable_tmds_output = dcn10_link_encoder_enable_tmds_output,
.enable_dp_output = dcn401_link_encoder_enable_dp_output,
.enable_dp_mst_output = dcn10_link_encoder_enable_dp_mst_output,
.disable_output = dcn10_link_encoder_disable_output,
.dp_set_lane_settings = dcn10_link_encoder_dp_set_lane_settings,
.dp_set_phy_pattern = dcn10_link_encoder_dp_set_phy_pattern,
.update_mst_stream_allocation_table =
dcn10_link_encoder_update_mst_stream_allocation_table,
.psr_program_dp_dphy_fast_training =
dcn10_psr_program_dp_dphy_fast_training,
.psr_program_secondary_packet = dcn10_psr_program_secondary_packet,
.connect_dig_be_to_fe = dcn10_link_encoder_connect_dig_be_to_fe,
.enable_hpd = dcn10_link_encoder_enable_hpd,
.disable_hpd = dcn10_link_encoder_disable_hpd,
.is_dig_enabled = dcn401_is_dig_enabled,
.destroy = dcn10_link_encoder_destroy,
.fec_set_enable = enc2_fec_set_enable,
.fec_set_ready = enc2_fec_set_ready,
.fec_is_active = enc2_fec_is_active,
.get_dig_frontend = dcn10_get_dig_frontend,
.get_dig_mode = dcn401_get_dig_mode,
.is_in_alt_mode = dcn32_link_encoder_is_in_alt_mode,
.get_max_link_cap = dcn32_link_encoder_get_max_link_cap,
.dpcstx_set_order_invert_18_bit = NULL,
.set_phy_source = NULL,
.dpcs_initialize_phy = NULL,
.dpcs_configure_phypll = NULL,
.dpcs_configure_dpcs = NULL,
.dpcs_enable_dpcs = NULL,
.prog_eq_setting = dpcs60_program_eq_setting,
.get_txffe = dpcs401_get_txffe,
.set_txffe = dpcs401_set_txffe,
.set_dio_phy_mux = dcn31_link_encoder_set_dio_phy_mux,
.setup_ri_pj_check_in_sw_or_hw_mode = dcn401_setup_ri_pj_check_in_sw_or_hw_mode,
.get_hpd_state = dcn42_get_hpd_state,
.program_hpd_filter = dcn42_program_hpd_filter,
};
void dcn60_link_encoder_construct(
struct dcn20_link_encoder *enc20,
const struct encoder_init_data *init_data,
const struct encoder_feature_support *enc_features,
const struct dcn10_link_enc_registers *link_regs,
const struct dcn10_link_enc_aux_registers *aux_regs,
const struct dcn10_link_enc_hpd_registers *hpd_regs,
const struct dcn10_link_enc_shift *link_shift,
const struct dcn10_link_enc_mask *link_mask)
{
struct bp_connector_speed_cap_info bp_cap_info = {0};
const struct dc_vbios_funcs *bp_funcs = init_data->ctx->dc_bios->funcs;
enum bp_result result = BP_RESULT_OK;
struct dcn10_link_encoder *enc10 = &enc20->enc10;
enc10->base.funcs = &dcn60_link_enc_funcs;
enc10->base.ctx = init_data->ctx;
enc10->base.id = init_data->encoder;
enc10->base.hpd_source = init_data->hpd_source;
enc10->base.hpd_active_high = init_data->hpd_active_high;
enc10->base.connector = init_data->connector;
enc10->base.preferred_engine = ENGINE_ID_UNKNOWN;
enc10->base.features = *enc_features;
if (enc10->base.connector.id == CONNECTOR_ID_USBC)
enc10->base.features.flags.bits.DP_IS_USB_C = 1;
enc10->base.transmitter = init_data->transmitter;
/* set the flag to indicate whether driver poll the I2C data pin
* while doing the DP sink detect
*/
/* if (dal_adapter_service_is_feature_supported(as,
FEATURE_DP_SINK_DETECT_POLL_DATA_PIN))
enc10->base.features.flags.bits.
DP_SINK_DETECT_POLL_DATA_PIN = true;*/
enc10->base.output_signals =
SIGNAL_TYPE_DVI_SINGLE_LINK |
SIGNAL_TYPE_DVI_DUAL_LINK |
SIGNAL_TYPE_LVDS |
SIGNAL_TYPE_DISPLAY_PORT |
SIGNAL_TYPE_DISPLAY_PORT_MST |
SIGNAL_TYPE_EDP |
SIGNAL_TYPE_HDMI_TYPE_A;
enc10->link_regs = link_regs;
enc10->aux_regs = aux_regs;
enc10->hpd_regs = hpd_regs;
enc10->link_shift = link_shift;
enc10->link_mask = link_mask;
switch (enc10->base.transmitter) {
case TRANSMITTER_UNIPHY_A:
enc10->base.preferred_engine = ENGINE_ID_DIGA;
break;
case TRANSMITTER_UNIPHY_B:
enc10->base.preferred_engine = ENGINE_ID_DIGB;
break;
case TRANSMITTER_UNIPHY_C:
enc10->base.preferred_engine = ENGINE_ID_DIGC;
break;
case TRANSMITTER_UNIPHY_D:
enc10->base.preferred_engine = ENGINE_ID_DIGD;
break;
case TRANSMITTER_UNIPHY_E:
enc10->base.preferred_engine = ENGINE_ID_DIGE;
break;
default:
ASSERT_CRITICAL(false);
enc10->base.preferred_engine = ENGINE_ID_UNKNOWN;
}
/* default to one to mirror Windows behavior */
enc10->base.features.flags.bits.HDMI_6GB_EN = 1;
if (bp_funcs->get_connector_speed_cap_info)
result = bp_funcs->get_connector_speed_cap_info(enc10->base.ctx->dc_bios,
enc10->base.connector, &bp_cap_info);
/* Override features with DCE-specific values */
if (result == BP_RESULT_OK) {
enc10->base.features.flags.bits.IS_HBR2_CAPABLE =
bp_cap_info.DP_HBR2_EN;
enc10->base.features.flags.bits.IS_HBR3_CAPABLE =
bp_cap_info.DP_HBR3_EN;
enc10->base.features.flags.bits.HDMI_6GB_EN = bp_cap_info.HDMI_6GB_EN;
enc10->base.features.flags.bits.IS_DP2_CAPABLE = 1;
enc10->base.features.flags.bits.IS_UHBR10_CAPABLE = bp_cap_info.DP_UHBR10_EN;
enc10->base.features.flags.bits.IS_UHBR13_5_CAPABLE = bp_cap_info.DP_UHBR13_5_EN;
enc10->base.features.flags.bits.IS_UHBR20_CAPABLE = bp_cap_info.DP_UHBR20_EN;
enc10->base.features.flags.bits.IS_HDMI_FRL_CAPABLE =
bp_cap_info.FRL_8G_EN || bp_cap_info.FRL_10G_EN || bp_cap_info.FRL_12G_EN ||
bp_cap_info.FRL_16G_EN || bp_cap_info.FRL_20G_EN || bp_cap_info.FRL_24G_EN;
enc10->base.features.flags.bits.IS_FRL_8G_CAPABLE = bp_cap_info.FRL_8G_EN;
enc10->base.features.flags.bits.IS_FRL_10G_CAPABLE = bp_cap_info.FRL_10G_EN;
enc10->base.features.flags.bits.IS_FRL_12G_CAPABLE = bp_cap_info.FRL_12G_EN;
enc10->base.features.flags.bits.IS_FRL_16G_CAPABLE = bp_cap_info.FRL_16G_EN;
enc10->base.features.flags.bits.IS_FRL_20G_CAPABLE = bp_cap_info.FRL_20G_EN;
enc10->base.features.flags.bits.IS_FRL_24G_CAPABLE = bp_cap_info.FRL_24G_EN;
enc10->base.txffe_state = 0;
} else {
DC_LOG_WARNING("%s: Failed to get encoder_cap_info from VBIOS with error code %d!\n",
__func__,
result);
}
if (enc10->base.ctx->dc->debug.hdmi20_disable) {
enc10->base.features.flags.bits.HDMI_6GB_EN = 0;
}
if (enc10->base.ctx->dc->config.force_hdmi21_frl_enc_enable) {
enc10->base.features.flags.bits.IS_HDMI_FRL_CAPABLE = 1;
enc10->base.features.flags.bits.IS_FRL_8G_CAPABLE = 1;
enc10->base.features.flags.bits.IS_FRL_10G_CAPABLE = 1;
enc10->base.features.flags.bits.IS_FRL_12G_CAPABLE = 1;
enc10->base.features.flags.bits.IS_FRL_16G_CAPABLE = 1;
enc10->base.features.flags.bits.IS_FRL_20G_CAPABLE = 1;
enc10->base.features.flags.bits.IS_FRL_24G_CAPABLE = 0;
}
}

View File

@@ -0,0 +1,43 @@
// SPDX-License-Identifier: MIT
//
// Copyright 2025 Advanced Micro Devices, Inc.
#ifndef __DC_LINK_ENCODER__DCN60_H__
#define __DC_LINK_ENCODER__DCN60_H__
#include "dcn30/dcn30_dio_link_encoder.h"
#define LINK_ENCODER_MASK_SH_LIST_DCN60(mask_sh) \
LE_SF(DIG0_HDCP_I2C_CONTROL_0, HDCP_I2C_DISABLE, mask_sh),\
LE_SF(DIG0_HDCP_I2C_CONTROL_0, HDCP_I2C_DDC_SELECT, mask_sh),\
LE_SF(DIG0_HDCP_INT_CONTROL, HDCP_I2C_XFER_REQ_MASK, mask_sh),\
LE_SF(HPD0_DC_HPD_INT_STATUS, DC_HPD_SENSE, mask_sh),\
LE_SF(HPD0_DC_HPD_TOGGLE_FILT_CNTL, DC_HPD_CONNECT_INT_DELAY, mask_sh),\
LE_SF(HPD0_DC_HPD_TOGGLE_FILT_CNTL, DC_HPD_DISCONNECT_INT_DELAY, mask_sh),\
LE_SF(DC_GPIO_DDC1_MASK, AUX_PAD1_MODE, mask_sh),\
SF(HPD_CTRL, HPD1_Y_POL_INVERT, mask_sh),\
SF(HPD_CTRL, HPD2_Y_POL_INVERT, mask_sh),\
SF(HPD_CTRL, HPD3_Y_POL_INVERT, mask_sh),\
SF(HPD_CTRL, HPD4_Y_POL_INVERT, mask_sh)
void dcn60_link_encoder_construct(
struct dcn20_link_encoder *enc20,
const struct encoder_init_data *init_data,
const struct encoder_feature_support *enc_features,
const struct dcn10_link_enc_registers *link_regs,
const struct dcn10_link_enc_aux_registers *aux_regs,
const struct dcn10_link_enc_hpd_registers *hpd_regs,
const struct dcn10_link_enc_shift *link_shift,
const struct dcn10_link_enc_mask *link_mask);
void dpcs60_program_eq_setting(
struct link_encoder *enc,
uint8_t FFE_Level,
bool de_emphasis_only,
bool pre_shoot_only,
bool no_ffe,
const struct dc_hdmi_frl_link_settings *link_settings);
void enc60_hw_init(struct link_encoder *enc);
#endif /* __DC_LINK_ENCODER__DCN60_H__ */

View File

@@ -0,0 +1,538 @@
// SPDX-License-Identifier: MIT
//
// Copyright 2025 Advanced Micro Devices, Inc.
#include "dc_bios_types.h"
#include "dcn30/dcn30_dio_stream_encoder.h"
#include "dcn32/dcn32_dio_stream_encoder.h"
#include "dcn35/dcn35_dio_stream_encoder.h"
#include "dcn401/dcn401_dio_stream_encoder.h"
#include "dcn60_dio_stream_encoder.h"
#include "reg_helper.h"
#include "hw_shared.h"
#include "link_service.h"
#include "dpcd_defs.h"
#define DC_LOGGER \
enc1->base.ctx->logger
#define REG(reg)\
(enc1->regs->reg)
#undef FN
#define FN(reg_name, field_name) \
(uint8_t)enc1->se_shift->field_name, enc1->se_mask->field_name
#define VBI_LINE_0 0
#define HDMI_CLOCK_CHANNEL_RATE_MORE_340M 340000
#define CTX \
enc1->base.ctx
static void enc60_dp_set_odm_combine(
struct stream_encoder *enc,
bool odm_combine)
{
(void)enc;
(void)odm_combine;
}
/* setup stream encoder in hdmi mode */
static void enc60_stream_encoder_hdmi_set_stream_attribute(
struct stream_encoder *enc,
struct dc_crtc_timing *crtc_timing,
int actual_pix_clk_khz,
bool enable_audio)
{
struct dcn10_stream_encoder *enc1 = DCN10STRENC_FROM_STRENC(enc);
if (!enc->ctx->dc->debug.avoid_vbios_exec_table) {
struct bp_encoder_control cntl = {0};
cntl.action = ENCODER_CONTROL_SETUP;
cntl.engine_id = enc1->base.id;
cntl.signal = SIGNAL_TYPE_HDMI_TYPE_A;
cntl.enable_dp_audio = enable_audio;
cntl.pixel_clock = actual_pix_clk_khz;
cntl.lanes_number = LANE_COUNT_FOUR;
if (enc1->base.bp->funcs->encoder_control(
enc1->base.bp, &cntl) != BP_RESULT_OK)
return;
} else {
//Set pattern for clock channel, default vlue 0x63 does not work
REG_UPDATE(DIG_CLOCK_PATTERN, DIG_CLOCK_PATTERN, 0x1F);
//DIG_BE_TMDS_HDMI_MODE : TMDS-HDMI mode is already set in link_encoder_setup
//DIG_SOURCE_SELECT is already set in dig_connect_to_otg
/* DIG_START is removed from the register spec */
}
/* Configure pixel encoding */
enc401_stream_encoder_set_stream_attribute_helper(enc1, crtc_timing);
/* setup HDMI engine */
REG_UPDATE_4(HDMI_CONTROL,
HDMI_DEEP_COLOR_ENABLE, 0,
HDMI_DATA_SCRAMBLE_EN, 0,
HDMI_NO_EXTRA_NULL_PACKET_FILLED, 1,
HDMI_CLOCK_CHANNEL_RATE, 0);
/* Configure color depth */
switch (crtc_timing->display_color_depth) {
case COLOR_DEPTH_888:
REG_UPDATE(HDMI_CONTROL, HDMI_DEEP_COLOR_DEPTH, 0);
break;
case COLOR_DEPTH_101010:
if (crtc_timing->pixel_encoding == PIXEL_ENCODING_YCBCR422) {
REG_UPDATE_2(HDMI_CONTROL,
HDMI_DEEP_COLOR_DEPTH, 1,
HDMI_DEEP_COLOR_ENABLE, 0);
} else {
REG_UPDATE_2(HDMI_CONTROL,
HDMI_DEEP_COLOR_DEPTH, 1,
HDMI_DEEP_COLOR_ENABLE, 1);
}
break;
case COLOR_DEPTH_121212:
if (crtc_timing->pixel_encoding == PIXEL_ENCODING_YCBCR422) {
REG_UPDATE_2(HDMI_CONTROL,
HDMI_DEEP_COLOR_DEPTH, 2,
HDMI_DEEP_COLOR_ENABLE, 0);
} else {
REG_UPDATE_2(HDMI_CONTROL,
HDMI_DEEP_COLOR_DEPTH, 2,
HDMI_DEEP_COLOR_ENABLE, 1);
}
break;
case COLOR_DEPTH_161616:
REG_UPDATE_2(HDMI_CONTROL,
HDMI_DEEP_COLOR_DEPTH, 3,
HDMI_DEEP_COLOR_ENABLE, 1);
break;
default:
break;
}
if (actual_pix_clk_khz >= HDMI_CLOCK_CHANNEL_RATE_MORE_340M) {
/* enable HDMI data scrambler
* HDMI_CLOCK_CHANNEL_RATE_MORE_340M
* Clock channel frequency is 1/4 of character rate.
*/
REG_UPDATE_2(HDMI_CONTROL,
HDMI_DATA_SCRAMBLE_EN, 1,
HDMI_CLOCK_CHANNEL_RATE, 1);
} else if (crtc_timing->flags.LTE_340MCSC_SCRAMBLE) {
/* TODO: New feature for DCE11, still need to implement */
/* enable HDMI data scrambler
* HDMI_CLOCK_CHANNEL_FREQ_EQUAL_TO_CHAR_RATE
* Clock channel frequency is the same
* as character rate
*/
REG_UPDATE_2(HDMI_CONTROL,
HDMI_DATA_SCRAMBLE_EN, 1,
HDMI_CLOCK_CHANNEL_RATE, 0);
}
/* Enable transmission of General Control packet on every frame */
REG_UPDATE_3(HDMI_VBI_PACKET_CONTROL,
HDMI_GC_CONT, 1,
HDMI_GC_SEND, 1,
HDMI_NULL_SEND, 1);
/* Disable Audio Content Protection packet transmission */
REG_UPDATE(HDMI_VBI_PACKET_CONTROL, HDMI_ACP_SEND, 0);
/* following belongs to audio */
/* Enable Audio InfoFrame packet transmission. */
REG_UPDATE(HDMI_INFOFRAME_CONTROL0, HDMI_AUDIO_INFO_SEND, 1);
/* update double-buffered AUDIO_INFO registers immediately */
// ASSERT(enc->afmt);
// enc->afmt->funcs->audio_info_immediate_update(enc->afmt);
/* Select line number on which to send Audio InfoFrame packets */
REG_UPDATE(HDMI_INFOFRAME_CONTROL0, HDMI_AUDIO_INFO_LINE,
VBI_LINE_0 + 2);
/* set HDMI GC AVMUTE */
REG_UPDATE(HDMI_GC, HDMI_GC_AVMUTE, 0);
}
static void enc60_stream_encoder_update_hdmi_info_packets(
struct stream_encoder *enc,
const struct encoder_info_frame *info_frame)
{
struct dcn10_stream_encoder *enc1 = DCN10STRENC_FROM_STRENC(enc);
/* for bring up, disable dp double TODO */
REG_UPDATE(HDMI_DB_CONTROL, HDMI_DB_DISABLE, 1);
REG_UPDATE(DIG_FE_AUDIO_CNTL, APG_CLOCK_ENABLE, 1);
/*Always add mandatory packets first followed by optional ones*/
enc3_update_hdmi_info_packet(enc1, 0, &info_frame->avi);
enc3_update_hdmi_info_packet(enc1, 5, &info_frame->hfvsif);
enc3_update_hdmi_info_packet(enc1, 2, &info_frame->gamut);
enc3_update_hdmi_info_packet(enc1, 1, &info_frame->vendor);
enc3_update_hdmi_info_packet(enc1, 3, &info_frame->spd);
enc3_update_hdmi_info_packet(enc1, 4, &info_frame->hdrsmd);
enc3_update_hdmi_info_packet(enc1, 6, &info_frame->vtem);
}
static void enc60_se_enable_audio_clock(
struct stream_encoder *enc,
bool enable)
{
struct dcn10_stream_encoder *enc1 = DCN10STRENC_FROM_STRENC(enc);
REG_UPDATE(DIG_FE_AUDIO_CNTL, APG_CLOCK_ENABLE, enable);
}
static void enc60_se_setup_hdmi_audio(
struct stream_encoder *enc,
const struct audio_crtc_info *crtc_info)
{
struct dcn10_stream_encoder *enc1 = DCN10STRENC_FROM_STRENC(enc);
struct audio_clock_info audio_clock_info = {0};
/* HDMI_AUDIO_PACKET_CONTROL */
REG_UPDATE(HDMI_AUDIO_PACKET_CONTROL,
HDMI_AUDIO_DELAY_EN, 1);
/* HDMI_ACR_PACKET_CONTROL */
REG_UPDATE_2(HDMI_ACR_PACKET_CONTROL,
HDMI_ACR_AUTO_SEND, 1,
HDMI_ACR_SOURCE, 0);
/* Program audio clock sample/regeneration parameters */
get_audio_clock_info(crtc_info->color_depth,
crtc_info->requested_pixel_clock_100Hz,
crtc_info->calculated_pixel_clock_100Hz,
&audio_clock_info);
DC_LOG_HW_AUDIO(
"\n%s:Input::requested_pixel_clock_100Hz = %d" \
"calculated_pixel_clock_100Hz = %d \n", __func__, \
crtc_info->requested_pixel_clock_100Hz, \
crtc_info->calculated_pixel_clock_100Hz);
/* HDMI_ACR_32_0__HDMI_ACR_CTS_32_MASK */
REG_UPDATE(HDMI_ACR_32_0, HDMI_ACR_CTS_32, audio_clock_info.cts_32khz);
/* HDMI_ACR_32_1__HDMI_ACR_N_32_MASK */
REG_UPDATE(HDMI_ACR_32_1, HDMI_ACR_N_32, audio_clock_info.n_32khz);
/* HDMI_ACR_44_0__HDMI_ACR_CTS_44_MASK */
REG_UPDATE(HDMI_ACR_44_0, HDMI_ACR_CTS_44, audio_clock_info.cts_44khz);
/* HDMI_ACR_44_1__HDMI_ACR_N_44_MASK */
REG_UPDATE(HDMI_ACR_44_1, HDMI_ACR_N_44, audio_clock_info.n_44khz);
/* HDMI_ACR_48_0__HDMI_ACR_CTS_48_MASK */
REG_UPDATE(HDMI_ACR_48_0, HDMI_ACR_CTS_48, audio_clock_info.cts_48khz);
/* HDMI_ACR_48_1__HDMI_ACR_N_48_MASK */
REG_UPDATE(HDMI_ACR_48_1, HDMI_ACR_N_48, audio_clock_info.n_48khz);
/* Video driver cannot know in advance which sample rate will
* be used by HD Audio driver
* HDMI_ACR_PACKET_CONTROL__HDMI_ACR_N_MULTIPLE field is
* programmed below in interruppt callback
*/
}
static void enc60_se_hdmi_audio_setup(
struct stream_encoder *enc,
unsigned int az_inst,
struct audio_info *info,
struct audio_crtc_info *audio_crtc_info)
{
struct dcn10_stream_encoder *enc1 = DCN10STRENC_FROM_STRENC(enc);
enc60_se_enable_audio_clock(enc, true);
enc60_se_setup_hdmi_audio(enc, audio_crtc_info);
REG_UPDATE(DIG_FE_AUDIO_CNTL,
DIG_FE_INPUT_MUX_AUDIO_STREAM_SOURCE_SEL, az_inst);
ASSERT (enc->apg);
enc->apg->funcs->se_audio_setup(enc->apg, az_inst, info);
}
static void enc60_se_hdmi_audio_disable(
struct stream_encoder *enc)
{
ASSERT (enc->apg);
if (enc->apg && enc->apg->funcs->disable_apg)
enc->apg->funcs->disable_apg(enc->apg);
enc60_se_enable_audio_clock(enc, false);
}
static void enc60_stream_encoder_stop_dp_info_packets(
struct stream_encoder *enc)
{
/* stop generic packets on DP */
struct dcn10_stream_encoder *enc1 = DCN10STRENC_FROM_STRENC(enc);
uint32_t value = 0;
REG_SET_9(DP_SEC_CNTL, 0,
DP_SEC_GSP0_ENABLE, 0,
DP_SEC_GSP1_ENABLE, 0,
DP_SEC_GSP2_ENABLE, 0,
DP_SEC_GSP3_ENABLE, 0,
DP_SEC_GSP4_ENABLE, 0,
DP_SEC_GSP5_ENABLE, 0,
DP_SEC_GSP6_ENABLE, 0,
DP_SEC_GSP7_ENABLE, 0,
DP_SEC_STREAM_ENABLE, 0);
/* this register shared with audio info frame.
* therefore we need to keep master enabled
* if at least one of the fields is not 0 */
value = REG_READ(DP_SEC_CNTL);
if (value)
REG_UPDATE(DP_SEC_CNTL, DP_SEC_STREAM_ENABLE, 1);
}
#define DP_SEC_AUD_N__DP_SEC_AUD_N__DEFAULT 0x8000
#define DP_SEC_TIMESTAMP__DP_SEC_TIMESTAMP_MODE__AUTO_CALC 1
static void enc60_se_setup_dp_audio(
struct stream_encoder *enc)
{
struct dcn10_stream_encoder *enc1 = DCN10STRENC_FROM_STRENC(enc);
/* --- DP Audio packet configurations --- */
/* ATP Configuration */
REG_SET(DP_SEC_AUD_N, 0,
DP_SEC_AUD_N, DP_SEC_AUD_N__DP_SEC_AUD_N__DEFAULT);
/* Async/auto-calc timestamp mode */
REG_SET(DP_SEC_TIMESTAMP, 0, DP_SEC_TIMESTAMP_MODE,
DP_SEC_TIMESTAMP__DP_SEC_TIMESTAMP_MODE__AUTO_CALC);
}
static void enc60_se_dp_audio_enable(
struct stream_encoder *enc)
{
enc60_se_enable_audio_clock(enc, true);
enc60_se_setup_dp_audio(enc);
enc1_se_enable_dp_audio(enc);
/* Enable APG block */
enc->apg->funcs->enable_apg(enc->apg);
}
static void enc60_se_disable_dp_audio(
struct stream_encoder *enc)
{
struct dcn10_stream_encoder *enc1 = DCN10STRENC_FROM_STRENC(enc);
uint32_t value = 0;
/* Disable Audio packets */
REG_UPDATE_5(DP_SEC_CNTL,
DP_SEC_ASP_ENABLE, 0,
DP_SEC_ATP_ENABLE, 0,
DP_SEC_AIP_ENABLE, 0,
DP_SEC_ACM_ENABLE, 0,
DP_SEC_STREAM_ENABLE, 0);
/* This register shared with encoder info frame. Therefore we need to
* keep master enabled if at least on of the fields is not 0
*/
value = REG_READ(DP_SEC_CNTL);
if (value != 0)
REG_UPDATE(DP_SEC_CNTL, DP_SEC_STREAM_ENABLE, 1);
}
static void enc60_se_dp_audio_disable(
struct stream_encoder *enc)
{
enc60_se_disable_dp_audio(enc);
/* Disable APG block */
enc->apg->funcs->disable_apg(enc->apg);
enc60_se_enable_audio_clock(enc, false);
}
static void enc60_audio_mute_control(
struct stream_encoder *enc,
bool mute)
{
ASSERT (enc->apg);
if (mute)
enc->apg->funcs->disable_apg(enc->apg);
else
enc->apg->funcs->enable_apg(enc->apg);
}
static void enc60_se_dp_audio_setup(
struct stream_encoder *enc,
unsigned int az_inst,
struct audio_info *info)
{
struct dcn10_stream_encoder *enc1 = DCN10STRENC_FROM_STRENC(enc);
enc60_se_enable_audio_clock(enc, true);
/*ACR setup*/
REG_UPDATE(DIG_FE_AUDIO_CNTL,
DIG_FE_INPUT_MUX_AUDIO_STREAM_SOURCE_SEL, az_inst);
ASSERT(enc->apg);
enc->apg->funcs->se_audio_setup(enc->apg, az_inst, info);
}
static void enc60_dp_set_dsc_pps_info_packet(struct stream_encoder *enc,
bool enable,
uint8_t *dsc_packed_pps,
bool immediate_update)
{
struct dcn10_stream_encoder *enc1 = DCN10STRENC_FROM_STRENC(enc);
if (enable) {
struct dc_info_packet pps_sdp;
int i;
/* Configure for PPS packet size (128 bytes) */
REG_UPDATE(DP_SEC_CNTL2, DP_SEC_GSP11_PPS, 1);
/* Load PPS into infoframe (SDP) registers */
pps_sdp.valid = true;
pps_sdp.hb0 = 0;
pps_sdp.hb1 = DC_DP_INFOFRAME_TYPE_PPS;
pps_sdp.hb2 = 127;
pps_sdp.hb3 = 0;
for (i = 0; i < 4; i++) {
memcpy(pps_sdp.sb, &dsc_packed_pps[i * 32], 32);
enc1->base.vpg->funcs->update_generic_info_packet(
enc1->base.vpg,
11 + i,
&pps_sdp,
immediate_update);
}
/* SW should make sure VBID[6] update line number is bigger
* than PPS transmit line number
*/
REG_UPDATE(DP_GSP11_CNTL,
DP_SEC_GSP11_LINE_NUM, 2);
REG_UPDATE_2(DP_MSA_VBID_MISC,
DP_VBID6_LINE_REFERENCE, 0,
DP_VBID6_LINE_NUM, 3);
/* Send PPS data at the line number specified above.
* DP spec requires PPS to be sent only when it changes, however since
* decoder has to be able to handle its change on every frame, we're
* sending it always (i.e. on every frame) to reduce the chance it'd be
* missed by decoder. If it turns out required to send PPS only when it
* changes, we can use DP_SEC_GSP11_SEND register.
*/
REG_UPDATE(DP_GSP11_CNTL,
DP_SEC_GSP11_ENABLE, 1);
REG_UPDATE(DP_SEC_CNTL,
DP_SEC_STREAM_ENABLE, 1);
} else {
/* Disable Generic Stream Packet 11 (GSP) transmission */
REG_UPDATE(DP_GSP11_CNTL, DP_SEC_GSP11_ENABLE, 0);
REG_UPDATE(DP_SEC_CNTL2, DP_SEC_GSP11_PPS, 0);
}
}
static void enc60_reset_hdmi_stream_attribute(
struct stream_encoder *enc)
{
struct dcn10_stream_encoder *enc1 = DCN10STRENC_FROM_STRENC(enc);
REG_UPDATE_3(HDMI_CONTROL,
HDMI_DEEP_COLOR_ENABLE, 0,
HDMI_DATA_SCRAMBLE_EN, 0,
HDMI_CLOCK_CHANNEL_RATE, 0);
}
static const struct stream_encoder_funcs dcn60_str_enc_funcs = {
.dp_set_odm_combine =
enc60_dp_set_odm_combine,
.dp_set_stream_attribute =
enc401_stream_encoder_dp_set_stream_attribute,
.hdmi_set_stream_attribute =
enc60_stream_encoder_hdmi_set_stream_attribute,
.dvi_set_stream_attribute =
enc401_stream_encoder_dvi_set_stream_attribute,
.set_throttled_vcp_size =
enc1_stream_encoder_set_throttled_vcp_size,
.update_hdmi_info_packets =
enc60_stream_encoder_update_hdmi_info_packets,
.stop_hdmi_info_packets =
enc3_stream_encoder_stop_hdmi_info_packets,
.update_dp_info_packets_sdp_line_num =
enc3_stream_encoder_update_dp_info_packets_sdp_line_num,
.update_dp_info_packets =
enc3_stream_encoder_update_dp_info_packets,
.stop_dp_info_packets =
enc60_stream_encoder_stop_dp_info_packets,
.dp_blank =
enc1_stream_encoder_dp_blank,
.dp_unblank =
enc401_stream_encoder_dp_unblank,
.audio_mute_control = enc60_audio_mute_control,
.dp_audio_setup = enc60_se_dp_audio_setup,
.dp_audio_enable = enc60_se_dp_audio_enable,
.dp_audio_disable = enc60_se_dp_audio_disable,
.hdmi_audio_setup = enc60_se_hdmi_audio_setup,
.hdmi_audio_disable = enc60_se_hdmi_audio_disable,
.setup_stereo_sync = enc1_setup_stereo_sync,
.set_avmute = enc1_stream_encoder_set_avmute,
.dig_connect_to_otg = enc1_dig_connect_to_otg,
.dig_source_otg = enc1_dig_source_otg,
.dp_get_pixel_format = enc1_stream_encoder_dp_get_pixel_format,
.enc_read_state = enc401_read_state,
.dp_set_dsc_config = NULL,
.dp_set_dsc_pps_info_packet = enc60_dp_set_dsc_pps_info_packet,
.set_dynamic_metadata = enc401_set_dynamic_metadata,
.hdmi_reset_stream_attribute = enc60_reset_hdmi_stream_attribute,
.enable_stream = enc401_stream_encoder_enable,
.set_input_mode = enc401_set_dig_input_mode,
.enable_fifo = enc35_enable_fifo,
.disable_fifo = enc35_disable_fifo,
.map_stream_to_link = enc401_stream_encoder_map_to_link,
};
void dcn60_dio_stream_encoder_construct(
struct dcn10_stream_encoder *enc1,
struct dc_context *ctx,
struct dc_bios *bp,
enum engine_id eng_id,
struct vpg *vpg,
struct apg *apg,
const struct dcn10_stream_enc_registers *regs,
const struct dcn10_stream_encoder_shift *se_shift,
const struct dcn10_stream_encoder_mask *se_mask)
{
enc1->base.funcs = &dcn60_str_enc_funcs;
enc1->base.ctx = ctx;
enc1->base.id = eng_id;
enc1->base.bp = bp;
enc1->base.vpg = vpg;
enc1->base.apg = apg;
enc1->regs = regs;
enc1->se_shift = se_shift;
enc1->se_mask = se_mask;
enc1->base.stream_enc_inst = vpg->inst;
}

View File

@@ -0,0 +1,181 @@
// SPDX-License-Identifier: MIT
//
// Copyright 2025 Advanced Micro Devices, Inc.
#ifndef __DC_DIO_STREAM_ENCODER_DCN60_H__
#define __DC_DIO_STREAM_ENCODER_DCN60_H__
#include "dcn30/dcn30_vpg.h"
#include "dcn30/dcn30_afmt.h"
#include "dcn31/dcn31_apg.h"
#include "stream_encoder.h"
#include "dcn20/dcn20_stream_encoder.h"
#define SE_COMMON_MASK_SH_LIST_DCN60(mask_sh)\
SE_SF(DP0_DP_PIXEL_FORMAT, PIXEL_ENCODING_TYPE, mask_sh),\
SE_SF(DP0_DP_PIXEL_FORMAT, UNCOMPRESSED_PIXEL_FORMAT, mask_sh),\
SE_SF(DP0_DP_PIXEL_FORMAT, UNCOMPRESSED_COMPONENT_DEPTH, mask_sh),\
SE_SF(DP0_DP_PIXEL_FORMAT, COMPRESSED_PIXEL_FORMAT, mask_sh),\
SE_SF(DIG0_HDMI_CONTROL, HDMI_DEEP_COLOR_ENABLE, mask_sh),\
SE_SF(DIG0_HDMI_CONTROL, HDMI_DEEP_COLOR_DEPTH, mask_sh),\
SE_SF(DIG0_HDMI_CONTROL, HDMI_DATA_SCRAMBLE_EN, mask_sh),\
SE_SF(DIG0_HDMI_CONTROL, HDMI_NO_EXTRA_NULL_PACKET_FILLED, mask_sh),\
SE_SF(DIG0_HDMI_VBI_PACKET_CONTROL, HDMI_GC_CONT, mask_sh),\
SE_SF(DIG0_HDMI_VBI_PACKET_CONTROL, HDMI_GC_SEND, mask_sh),\
SE_SF(DIG0_HDMI_VBI_PACKET_CONTROL, HDMI_NULL_SEND, mask_sh),\
SE_SF(DIG0_HDMI_VBI_PACKET_CONTROL, HDMI_ACP_SEND, mask_sh),\
SE_SF(DIG0_HDMI_INFOFRAME_CONTROL0, HDMI_AUDIO_INFO_SEND, mask_sh),\
SE_SF(DIG0_HDMI_INFOFRAME_CONTROL0, HDMI_AUDIO_INFO_LINE, mask_sh),\
SE_SF(DIG0_HDMI_GC, HDMI_GC_AVMUTE, mask_sh),\
SE_SF(DP0_DP_MSE_RATE_CNTL, DP_MSE_RATE_X, mask_sh),\
SE_SF(DP0_DP_MSE_RATE_CNTL, DP_MSE_RATE_Y, mask_sh),\
SE_SF(DP0_DP_MSE_RATE_UPDATE, DP_MSE_RATE_UPDATE_PENDING, mask_sh),\
SE_SF(DP0_DP_SEC_CNTL, DP_SEC_GSP0_ENABLE, mask_sh),\
SE_SF(DP0_DP_SEC_CNTL, DP_SEC_STREAM_ENABLE, mask_sh),\
SE_SF(DP0_DP_SEC_CNTL, DP_SEC_GSP1_ENABLE, mask_sh),\
SE_SF(DP0_DP_SEC_CNTL, DP_SEC_GSP2_ENABLE, mask_sh),\
SE_SF(DP0_DP_SEC_CNTL, DP_SEC_GSP3_ENABLE, mask_sh),\
SE_SF(DP0_DP_SEC_CNTL1, DP_SEC_GSP5_LINE_REFERENCE, mask_sh),\
SE_SF(DP0_DP_SEC_CNTL2, DP_SEC_GSP4_SEND, mask_sh),\
SE_SF(DP0_DP_SEC_CNTL2, DP_SEC_GSP4_SEND_PENDING, mask_sh),\
SE_SF(DP0_DP_SEC_CNTL4, DP_SEC_GSP4_LINE_NUM, mask_sh),\
SE_SF(DP0_DP_SEC_CNTL5, DP_SEC_GSP5_LINE_NUM, mask_sh),\
SE_SF(DP0_DP_SEC_CNTL2, DP_SEC_GSP4_SEND_ANY_LINE, mask_sh),\
SE_SF(DP0_DP_VID_STREAM_CNTL, DP_VID_STREAM_DIS_DEFER, mask_sh),\
SE_SF(DP0_DP_VID_STREAM_CNTL, DP_VID_STREAM_ENABLE, mask_sh),\
SE_SF(DP0_DP_VID_STREAM_CNTL, DP_VID_STREAM_STATUS, mask_sh),\
SE_SF(DP0_DP_STEER_FIFO, DP_STEER_FIFO_RESET, mask_sh),\
SE_SF(DP0_DP_STEER_FIFO, DP_STEER_FIFO_ENABLE, mask_sh),\
SE_SF(DP0_DP_VID_TIMING, DP_VID_M_N_GEN_EN, mask_sh),\
SE_SF(DP0_DP_VID_N, DP_VID_N, mask_sh),\
SE_SF(DP0_DP_VID_M, DP_VID_M, mask_sh),\
SE_SF(DIG0_HDMI_AUDIO_PACKET_CONTROL, HDMI_AUDIO_DELAY_EN, mask_sh),\
SE_SF(DIG0_HDMI_ACR_PACKET_CONTROL, HDMI_ACR_AUTO_SEND, mask_sh),\
SE_SF(DIG0_HDMI_ACR_PACKET_CONTROL, HDMI_ACR_SOURCE, mask_sh),\
SE_SF(DIG0_HDMI_ACR_32_0, HDMI_ACR_CTS_32, mask_sh),\
SE_SF(DIG0_HDMI_ACR_32_1, HDMI_ACR_N_32, mask_sh),\
SE_SF(DIG0_HDMI_ACR_44_0, HDMI_ACR_CTS_44, mask_sh),\
SE_SF(DIG0_HDMI_ACR_44_1, HDMI_ACR_N_44, mask_sh),\
SE_SF(DIG0_HDMI_ACR_48_0, HDMI_ACR_CTS_48, mask_sh),\
SE_SF(DIG0_HDMI_ACR_48_1, HDMI_ACR_N_48, mask_sh),\
SE_SF(DP0_DP_SEC_AUD_N, DP_SEC_AUD_N, mask_sh),\
SE_SF(DP0_DP_SEC_TIMESTAMP, DP_SEC_TIMESTAMP_MODE, mask_sh),\
SE_SF(DP0_DP_SEC_CNTL, DP_SEC_ASP_ENABLE, mask_sh),\
SE_SF(DP0_DP_SEC_CNTL, DP_SEC_ATP_ENABLE, mask_sh),\
SE_SF(DP0_DP_SEC_CNTL, DP_SEC_AIP_ENABLE, mask_sh),\
SE_SF(DP0_DP_SEC_CNTL, DP_SEC_ACM_ENABLE, mask_sh),\
SE_SF(DIG0_HDMI_CONTROL, HDMI_CLOCK_CHANNEL_RATE, mask_sh),\
SE_SF(DIG1_HDMI_CONTROL, TMDS_PIXEL_ENCODING, mask_sh),\
SE_SF(DIG1_HDMI_CONTROL, TMDS_COLOR_FORMAT, mask_sh),\
SE_SF(DIG0_DIG_FE_CNTL, DIG_STEREOSYNC_SELECT, mask_sh),\
SE_SF(DIG0_DIG_FE_CNTL, DIG_STEREOSYNC_GATE_EN, mask_sh),\
SE_SF(DP0_DP_SEC_CNTL, DP_SEC_GSP4_ENABLE, mask_sh),\
SE_SF(DP0_DP_SEC_CNTL, DP_SEC_GSP5_ENABLE, mask_sh),\
SE_SF(DP0_DP_SEC_CNTL, DP_SEC_GSP6_ENABLE, mask_sh),\
SE_SF(DP0_DP_SEC_CNTL, DP_SEC_GSP7_ENABLE, mask_sh),\
SE_SF(DP0_DP_SEC_CNTL2, DP_SEC_GSP7_SEND, mask_sh),\
SE_SF(DP0_DP_SEC_CNTL6, DP_SEC_GSP7_LINE_NUM, mask_sh),\
SE_SF(DP0_DP_SEC_CNTL2, DP_SEC_GSP11_PPS, mask_sh),\
SE_SF(DP0_DP_GSP11_CNTL, DP_SEC_GSP11_ENABLE, mask_sh),\
SE_SF(DP0_DP_GSP11_CNTL, DP_SEC_GSP11_LINE_NUM, mask_sh),\
SE_SF(DP0_DP_DB_CNTL, DP_DB_DISABLE, mask_sh),\
SE_SF(DP0_DP_MSA_COLORIMETRY, DP_MSA_MISC0, mask_sh),\
SE_SF(DP0_DP_MSA_TIMING_PARAM1, DP_MSA_HTOTAL, mask_sh),\
SE_SF(DP0_DP_MSA_TIMING_PARAM1, DP_MSA_VTOTAL, mask_sh),\
SE_SF(DP0_DP_MSA_TIMING_PARAM2, DP_MSA_HSTART, mask_sh),\
SE_SF(DP0_DP_MSA_TIMING_PARAM2, DP_MSA_VSTART, mask_sh),\
SE_SF(DP0_DP_MSA_TIMING_PARAM3, DP_MSA_HSYNCWIDTH, mask_sh),\
SE_SF(DP0_DP_MSA_TIMING_PARAM3, DP_MSA_HSYNCPOLARITY, mask_sh),\
SE_SF(DP0_DP_MSA_TIMING_PARAM3, DP_MSA_VSYNCWIDTH, mask_sh),\
SE_SF(DP0_DP_MSA_TIMING_PARAM3, DP_MSA_VSYNCPOLARITY, mask_sh),\
SE_SF(DP0_DP_MSA_TIMING_PARAM4, DP_MSA_HWIDTH, mask_sh),\
SE_SF(DP0_DP_MSA_TIMING_PARAM4, DP_MSA_VHEIGHT, mask_sh),\
SE_SF(DIG0_HDMI_DB_CONTROL, HDMI_DB_DISABLE, mask_sh),\
SE_SF(DP0_DP_VID_TIMING, DP_VID_N_INTERVAL, mask_sh),\
SE_SF(DIG0_DIG_FE_CNTL, DIG_SOURCE_SELECT, mask_sh), \
SE_SF(DIG0_HDMI_GENERIC_PACKET_CONTROL0, HDMI_GENERIC0_CONT, mask_sh),\
SE_SF(DIG0_HDMI_GENERIC_PACKET_CONTROL0, HDMI_GENERIC0_SEND, mask_sh),\
SE_SF(DIG0_HDMI_GENERIC_PACKET_CONTROL0, HDMI_GENERIC1_CONT, mask_sh),\
SE_SF(DIG0_HDMI_GENERIC_PACKET_CONTROL0, HDMI_GENERIC1_SEND, mask_sh),\
SE_SF(DIG0_HDMI_GENERIC_PACKET_CONTROL0, HDMI_GENERIC2_CONT, mask_sh),\
SE_SF(DIG0_HDMI_GENERIC_PACKET_CONTROL0, HDMI_GENERIC2_SEND, mask_sh),\
SE_SF(DIG0_HDMI_GENERIC_PACKET_CONTROL0, HDMI_GENERIC3_CONT, mask_sh),\
SE_SF(DIG0_HDMI_GENERIC_PACKET_CONTROL0, HDMI_GENERIC3_SEND, mask_sh),\
SE_SF(DIG0_HDMI_GENERIC_PACKET_CONTROL0, HDMI_GENERIC4_CONT, mask_sh),\
SE_SF(DIG0_HDMI_GENERIC_PACKET_CONTROL0, HDMI_GENERIC4_SEND, mask_sh),\
SE_SF(DIG0_HDMI_GENERIC_PACKET_CONTROL0, HDMI_GENERIC5_CONT, mask_sh),\
SE_SF(DIG0_HDMI_GENERIC_PACKET_CONTROL0, HDMI_GENERIC5_SEND, mask_sh),\
SE_SF(DIG0_HDMI_GENERIC_PACKET_CONTROL0, HDMI_GENERIC6_CONT, mask_sh),\
SE_SF(DIG0_HDMI_GENERIC_PACKET_CONTROL0, HDMI_GENERIC6_SEND, mask_sh),\
SE_SF(DIG0_HDMI_GENERIC_PACKET_CONTROL0, HDMI_GENERIC7_CONT, mask_sh),\
SE_SF(DIG0_HDMI_GENERIC_PACKET_CONTROL0, HDMI_GENERIC7_SEND, mask_sh),\
SE_SF(DIG0_HDMI_GENERIC_PACKET_CONTROL6, HDMI_GENERIC8_CONT, mask_sh),\
SE_SF(DIG0_HDMI_GENERIC_PACKET_CONTROL6, HDMI_GENERIC8_SEND, mask_sh),\
SE_SF(DIG0_HDMI_GENERIC_PACKET_CONTROL6, HDMI_GENERIC9_CONT, mask_sh),\
SE_SF(DIG0_HDMI_GENERIC_PACKET_CONTROL6, HDMI_GENERIC9_SEND, mask_sh),\
SE_SF(DIG0_HDMI_GENERIC_PACKET_CONTROL6, HDMI_GENERIC10_CONT, mask_sh),\
SE_SF(DIG0_HDMI_GENERIC_PACKET_CONTROL6, HDMI_GENERIC10_SEND, mask_sh),\
SE_SF(DIG0_HDMI_GENERIC_PACKET_CONTROL6, HDMI_GENERIC11_CONT, mask_sh),\
SE_SF(DIG0_HDMI_GENERIC_PACKET_CONTROL6, HDMI_GENERIC11_SEND, mask_sh),\
SE_SF(DIG0_HDMI_GENERIC_PACKET_CONTROL6, HDMI_GENERIC12_CONT, mask_sh),\
SE_SF(DIG0_HDMI_GENERIC_PACKET_CONTROL6, HDMI_GENERIC12_SEND, mask_sh),\
SE_SF(DIG0_HDMI_GENERIC_PACKET_CONTROL6, HDMI_GENERIC13_CONT, mask_sh),\
SE_SF(DIG0_HDMI_GENERIC_PACKET_CONTROL6, HDMI_GENERIC13_SEND, mask_sh),\
SE_SF(DIG0_HDMI_GENERIC_PACKET_CONTROL6, HDMI_GENERIC14_CONT, mask_sh),\
SE_SF(DIG0_HDMI_GENERIC_PACKET_CONTROL6, HDMI_GENERIC14_SEND, mask_sh),\
SE_SF(DIG0_HDMI_GENERIC_PACKET_CONTROL1, HDMI_GENERIC0_LINE, mask_sh),\
SE_SF(DIG0_HDMI_GENERIC_PACKET_CONTROL1, HDMI_GENERIC1_LINE, mask_sh),\
SE_SF(DIG0_HDMI_GENERIC_PACKET_CONTROL2, HDMI_GENERIC2_LINE, mask_sh),\
SE_SF(DIG0_HDMI_GENERIC_PACKET_CONTROL2, HDMI_GENERIC3_LINE, mask_sh),\
SE_SF(DIG0_HDMI_GENERIC_PACKET_CONTROL3, HDMI_GENERIC4_LINE, mask_sh),\
SE_SF(DIG0_HDMI_GENERIC_PACKET_CONTROL3, HDMI_GENERIC5_LINE, mask_sh),\
SE_SF(DIG0_HDMI_GENERIC_PACKET_CONTROL4, HDMI_GENERIC6_LINE, mask_sh),\
SE_SF(DIG0_HDMI_GENERIC_PACKET_CONTROL4, HDMI_GENERIC7_LINE, mask_sh),\
SE_SF(DIG0_HDMI_GENERIC_PACKET_CONTROL7, HDMI_GENERIC8_LINE, mask_sh),\
SE_SF(DIG0_HDMI_GENERIC_PACKET_CONTROL7, HDMI_GENERIC9_LINE, mask_sh),\
SE_SF(DIG0_HDMI_GENERIC_PACKET_CONTROL8, HDMI_GENERIC10_LINE, mask_sh),\
SE_SF(DIG0_HDMI_GENERIC_PACKET_CONTROL8, HDMI_GENERIC11_LINE, mask_sh),\
SE_SF(DIG0_HDMI_GENERIC_PACKET_CONTROL9, HDMI_GENERIC12_LINE, mask_sh),\
SE_SF(DIG0_HDMI_GENERIC_PACKET_CONTROL9, HDMI_GENERIC13_LINE, mask_sh),\
SE_SF(DIG0_HDMI_GENERIC_PACKET_CONTROL10, HDMI_GENERIC14_LINE, mask_sh),\
SE_SF(DP0_DP_MSA_VBID_MISC, DP_VBID6_LINE_REFERENCE, mask_sh),\
SE_SF(DP0_DP_MSA_VBID_MISC, DP_VBID6_LINE_NUM, mask_sh),\
SE_SF(DME0_DME_CONTROL, METADATA_ENGINE_EN, mask_sh),\
SE_SF(DME0_DME_CONTROL, METADATA_HUBP_REQUESTOR_ID, mask_sh),\
SE_SF(DME0_DME_CONTROL, METADATA_STREAM_TYPE, mask_sh),\
SE_SF(DP0_DP_SEC_METADATA_TRANSMISSION, DP_SEC_METADATA_PACKET_ENABLE, mask_sh),\
SE_SF(DP0_DP_SEC_METADATA_TRANSMISSION, DP_SEC_METADATA_PACKET_LINE_REFERENCE, mask_sh),\
SE_SF(DP0_DP_SEC_METADATA_TRANSMISSION, DP_SEC_METADATA_PACKET_LINE, mask_sh),\
SE_SF(DIG0_HDMI_METADATA_PACKET_CONTROL, HDMI_METADATA_PACKET_ENABLE, mask_sh),\
SE_SF(DIG0_HDMI_METADATA_PACKET_CONTROL, HDMI_METADATA_PACKET_LINE_REFERENCE, mask_sh),\
SE_SF(DIG0_HDMI_METADATA_PACKET_CONTROL, HDMI_METADATA_PACKET_LINE, mask_sh),\
SE_SF(DIG0_HDMI_CONTROL, DOLBY_VISION_EN, mask_sh),\
SE_SF(DIG0_DIG_FE_EN_CNTL, DIG_FE_ENABLE, mask_sh),\
SE_SF(DIG0_DIG_FE_CLK_CNTL, DIG_FE_MODE, mask_sh),\
SE_SF(DIG0_DIG_FE_CLK_CNTL, DIG_FE_CLK_EN, mask_sh),\
SE_SF(DIG0_DIG_FE_CLK_CNTL, DIG_FE_SOFT_RESET, mask_sh),\
SE_SF(DIG0_DIG_FE_CNTL, DIG_STEREOSYNC_GATE_EN, mask_sh),\
SE_SF(DP0_DP_SEC_FRAMING4, DP_SST_SDP_SPLITTING, mask_sh),\
SE_SF(DIG0_DIG_CLOCK_PATTERN, DIG_CLOCK_PATTERN, mask_sh),\
SE_SF(DIG0_DIG_FIFO_CTRL0, DIG_FIFO_OUTPUT_PIXEL_PER_CYCLE, mask_sh),\
SE_SF(DIG0_DIG_FIFO_CTRL0, DIG_FIFO_READ_START_LEVEL, mask_sh),\
SE_SF(DIG0_DIG_FIFO_CTRL0, DIG_FIFO_ENABLE, mask_sh),\
SE_SF(DIG0_DIG_FIFO_CTRL0, DIG_FIFO_RESET, mask_sh),\
SE_SF(DIG0_DIG_FIFO_CTRL0, DIG_FIFO_RESET_DONE, mask_sh),\
SE_SF(DIG0_STREAM_MAPPER_CONTROL, DIG_STREAM_LINK_TARGET, mask_sh),\
SE_SF(DIG0_DIG_FE_AUDIO_CNTL, DIG_FE_INPUT_MUX_AUDIO_STREAM_SOURCE_SEL, mask_sh),\
SE_SF(DIG0_DIG_FE_AUDIO_CNTL, APG_CLOCK_ENABLE, mask_sh),
void dcn60_dio_stream_encoder_construct(
struct dcn10_stream_encoder *enc1,
struct dc_context *ctx,
struct dc_bios *bp,
enum engine_id eng_id,
struct vpg *vpg,
struct apg *apg,
const struct dcn10_stream_enc_registers *regs,
const struct dcn10_stream_encoder_shift *se_shift,
const struct dcn10_stream_encoder_mask *se_mask);
#endif /* __DC_DIO_STREAM_ENCODER_DCN60_H__ */

View File

@@ -203,6 +203,14 @@ void dm_helpers_mccs_vcp_set(
#define EXPORT_IF_KUNIT(symbol)
#endif
bool dm_helpers_submit_i2c_over_aux(
struct ddc_service *ddc,
uint32_t address,
uint8_t offset,
uint8_t *cmdBuffer,
uint32_t len,
bool read);
bool dm_helpers_dp_handle_test_pattern_request(
struct dc_context *ctx,
const struct dc_link *link,

View File

@@ -206,7 +206,7 @@ struct _vcs_dpi_soc_bounding_box_st dcn3_51_soc = {
.sr_enter_plus_exit_time_us = 30.0,
.sr_exit_z8_time_us = 263.0,
.sr_enter_plus_exit_z8_time_us = 363.0,
.fclk_change_latency_us = 24.0,
.fclk_change_latency_us = 39.0,
.usr_retraining_latency_us = 2,
.writeback_latency_us = 12.0,
@@ -215,7 +215,7 @@ struct _vcs_dpi_soc_bounding_box_st dcn3_51_soc = {
.urgent_latency_pixel_data_only_us = 4.0,
.urgent_latency_pixel_mixed_with_vm_data_us = 4.0,
.urgent_latency_vm_data_only_us = 4.0,
.dram_clock_change_latency_us = 34,
.dram_clock_change_latency_us = 39,
.urgent_out_of_order_return_per_channel_pixel_only_bytes = 4096,
.urgent_out_of_order_return_per_channel_pixel_and_vm_bytes = 4096,
.urgent_out_of_order_return_per_channel_vm_only_bytes = 4096,

View File

@@ -52,6 +52,9 @@ subdir-ccflags-y += -I$(FULL_AMD_DISPLAY_PATH)/dc/dml2_0/dml21/src/dml2_standalo
subdir-ccflags-y += -I$(FULL_AMD_DISPLAY_PATH)/dc/dml2_0/dml21/src/inc
subdir-ccflags-y += -I$(FULL_AMD_DISPLAY_PATH)/dc/dml2_0/dml21/inc
subdir-ccflags-y += -I$(FULL_AMD_DISPLAY_PATH)/dc/dml2_0/dml21/
subdir-ccflags-y += -I$(FULL_AMD_DISPLAY_PATH)/dc/dml2_0/dml21/inc/bounding_boxes/
subdir-ccflags-y += -I$(FULL_AMD_DISPLAY_PATH)/dc/dml2_0/dml21/src/dml2_utm_soc_bb/
subdir-ccflags-y += -I$(FULL_AMD_DISPLAY_PATH)/dc/dml2_0/dml21/src/dml2_cga/
# Add FPU flags to all dml2 files by default, remove NO_FPU flags.
# FPU flags step 1: Find all .c files in dal/dc/dml2_0 and it's subfolders
@@ -107,6 +110,28 @@ DML21 += src/dml2_pmo/dml2_pmo_dcn4_fams2.o
DML21 += src/dml2_pmo/dml2_pmo_dcn42.o
DML21 += src/dml2_standalone_libraries/lib_float_math.o
DML21 += src/dml2_standalone_libraries/lib_frl_cap_check.o
DML21 += src/dml2_top/dml2_top_utm.o
DML21 += src/dml2_core/dml2_core_dcn5_calcs_dchub.o
DML21 += src/dml2_core/dml2_core_dcn5_calcs_display_pipe.o
DML21 += src/dml2_core/dml2_core_dcn5_calcs_dsc_latency.o
DML21 += src/dml2_core/dml2_core_dcn5_funcs_initialize.o
DML21 += src/dml2_core/dml2_core_dcn5_funcs_mode_programming.o
DML21 += src/dml2_core/dml2_core_dcn5_funcs_mode_support.o
DML21 += src/dml2_dpmm/dml2_dpmm_dcn5.o
DML21 += src/dml2_utm_soc_bb/dml2_utm_soc_bb_dcn5.o
DML21 += src/dml2_utm_soc_bb/dml2_utm_soc_bb_factory.o
DML21 += src/dml2_cga/dml2_cga_dcn6.o
DML21 += src/dml2_cga/dml2_cga_factory.o
DML21 += src/dml2_core/dml2_core_dcn6_calcs_dchub.o
DML21 += src/dml2_core/dml2_core_dcn6_funcs_initialize.o
DML21 += src/dml2_core/dml2_core_dcn6_funcs_mode_programming.o
DML21 += src/dml2_core/dml2_core_dcn6_funcs_mode_support.o
DML21 += src/dml2_pmo/dml2_pmo_dcn5.o
DML21 += src/dml2_pmo/dml2_pmo_dcn5_stage_optimizers.o
DML21 += src/dml2_pmo/dml2_pmo_dcn6.o
DML21 += src/dml2_pmo/dml2_pmo_dcn6_stage_optimizers.o
DML21 += src/dml2_standalone_libraries/alternate_pstate_shared_lib.o
DML21 += src/dml2_utm_soc_bb/dml2_utm_soc_bb_dcn6.o
DML21 += dml21_translation_helper.o
DML21 += dml21_wrapper.o
DML21 += dml21_wrapper_fpu.o

Some files were not shown because too many files have changed in this diff Show More