Merge branch 'drm-next-4.3' of git://people.freedesktop.org/~agd5f/linux into drm-next

amdgpu and radeon changes for 4.3. Highlights:
- Fiji support for amdgpu.
- CGS support for amdgpu.  This is a new driver
  internal cross-component API.
- Initial GPU scheduler for amdgpu.  Still disabled
  by default.
- Lots of bug fixes and optimizations

* 'drm-next-4.3' of git://people.freedesktop.org/~agd5f/linux: (130 commits)
  drm/amdgpu: wait on page directory changes. v2
  drm/amdgpu: Select BACKLIGHT_LCD_SUPPORT
  drm/radeon: Select BACKLIGHT_LCD_SUPPORT
  drm/amdgpu: cleanup sheduler rq handling v2
  drm/amdgpu: move prepare work out of scheduler to cs_ioctl
  drm/amdgpu: fix unnecessary wake up
  drm/amdgpu: fix duplicated mapping invoke bug
  drm/amdgpu: drop bo_list_clone when no scheduler
  drm/amdgpu: disable GPU reset by default
  drm/amdgpu: fix type mismatch error
  drm/amdgpu: add reference for **fence
  drm/amdgpu: fix waiting for all fences before flipping
  drm/amdgpu: fix UVD return code checking
  drm/amdgpu: remove scheduler fence list v2
  drm/amdgpu: remove amd_sched_wait_emit v2
  drm/amdgpu: remove unecessary scheduler fence callbacks
  drm/amdgpu: fix scheduler fence implementation
  drm/amdgpu: don't grab dev->struct_mutex in pm functions
  drm/amdgpu: Don't take dev->struct_mutex in bo_force_delete
  drm/radeon: Don't take dev->struct_mutex in pm functions
  ...
This commit is contained in:
Dave Airlie 2015-08-20 09:40:49 +10:00
commit e2a8986f3e
74 changed files with 14135 additions and 1156 deletions

View File

@ -128,6 +128,7 @@ config DRM_RADEON
select POWER_SUPPLY
select HWMON
select BACKLIGHT_CLASS_DEVICE
select BACKLIGHT_LCD_SUPPORT
select INTERVAL_TREE
help
Choose this option if you have an ATI Radeon graphics card. There
@ -151,6 +152,7 @@ config DRM_AMDGPU
select POWER_SUPPLY
select HWMON
select BACKLIGHT_CLASS_DEVICE
select BACKLIGHT_LCD_SUPPORT
select INTERVAL_TREE
help
Choose this option if you have a recent AMD Radeon graphics card.

View File

@ -3,7 +3,9 @@
# Direct Rendering Infrastructure (DRI) in XFree86 4.1.0 and higher.
ccflags-y := -Iinclude/drm -Idrivers/gpu/drm/amd/include/asic_reg \
-Idrivers/gpu/drm/amd/include
-Idrivers/gpu/drm/amd/include \
-Idrivers/gpu/drm/amd/amdgpu \
-Idrivers/gpu/drm/amd/scheduler
amdgpu-y := amdgpu_drv.o
@ -21,7 +23,8 @@ amdgpu-y += amdgpu_device.o amdgpu_kms.o \
# add asic specific block
amdgpu-$(CONFIG_DRM_AMDGPU_CIK)+= cik.o gmc_v7_0.o cik_ih.o kv_smc.o kv_dpm.o \
ci_smc.o ci_dpm.o dce_v8_0.o gfx_v7_0.o cik_sdma.o uvd_v4_2.o vce_v2_0.o
ci_smc.o ci_dpm.o dce_v8_0.o gfx_v7_0.o cik_sdma.o uvd_v4_2.o vce_v2_0.o \
amdgpu_amdkfd_gfx_v7.o
amdgpu-y += \
vi.o
@ -43,6 +46,7 @@ amdgpu-y += \
amdgpu_dpm.o \
cz_smc.o cz_dpm.o \
tonga_smc.o tonga_dpm.o \
fiji_smc.o fiji_dpm.o \
iceland_smc.o iceland_dpm.o
# add DCE block
@ -74,9 +78,17 @@ amdgpu-y += \
# add amdkfd interfaces
amdgpu-y += \
amdgpu_amdkfd.o \
amdgpu_amdkfd_gfx_v7.o \
amdgpu_amdkfd_gfx_v8.o
# add cgs
amdgpu-y += amdgpu_cgs.o
# GPU scheduler
amdgpu-y += \
../scheduler/gpu_scheduler.o \
../scheduler/sched_fence.o \
amdgpu_sched.o
amdgpu-$(CONFIG_COMPAT) += amdgpu_ioc32.o
amdgpu-$(CONFIG_VGA_SWITCHEROO) += amdgpu_atpx_handler.o
amdgpu-$(CONFIG_ACPI) += amdgpu_acpi.o

View File

@ -42,17 +42,19 @@
#include <ttm/ttm_module.h>
#include <ttm/ttm_execbuf_util.h>
#include <drm/drmP.h>
#include <drm/drm_gem.h>
#include <drm/amdgpu_drm.h>
#include "amd_shared.h"
#include "amdgpu_family.h"
#include "amdgpu_mode.h"
#include "amdgpu_ih.h"
#include "amdgpu_irq.h"
#include "amdgpu_ucode.h"
#include "amdgpu_gds.h"
#include "gpu_scheduler.h"
/*
* Modules parameters.
*/
@ -77,7 +79,11 @@ extern int amdgpu_bapm;
extern int amdgpu_deep_color;
extern int amdgpu_vm_size;
extern int amdgpu_vm_block_size;
extern int amdgpu_enable_scheduler;
extern int amdgpu_sched_jobs;
extern int amdgpu_sched_hw_submission;
#define AMDGPU_WAIT_IDLE_TIMEOUT_IN_MS 3000
#define AMDGPU_MAX_USEC_TIMEOUT 100000 /* 100 ms */
#define AMDGPU_FENCE_JIFFIES_TIMEOUT (HZ / 2)
/* AMDGPU_IB_POOL_SIZE must be a power of 2 */
@ -178,6 +184,7 @@ struct amdgpu_ring;
struct amdgpu_semaphore;
struct amdgpu_cs_parser;
struct amdgpu_irq_src;
struct amdgpu_fpriv;
enum amdgpu_cp_irq {
AMDGPU_CP_IRQ_GFX_EOP = 0,
@ -381,10 +388,10 @@ struct amdgpu_fence_driver {
uint64_t sync_seq[AMDGPU_MAX_RINGS];
atomic64_t last_seq;
bool initialized;
bool delayed_irq;
struct amdgpu_irq_src *irq_src;
unsigned irq_type;
struct delayed_work lockup_work;
wait_queue_head_t fence_queue;
};
/* some special values for the owner field */
@ -423,20 +430,18 @@ void amdgpu_fence_driver_init_ring(struct amdgpu_ring *ring);
int amdgpu_fence_driver_start_ring(struct amdgpu_ring *ring,
struct amdgpu_irq_src *irq_src,
unsigned irq_type);
void amdgpu_fence_driver_suspend(struct amdgpu_device *adev);
void amdgpu_fence_driver_resume(struct amdgpu_device *adev);
int amdgpu_fence_emit(struct amdgpu_ring *ring, void *owner,
struct amdgpu_fence **fence);
int amdgpu_fence_recreate(struct amdgpu_ring *ring, void *owner,
uint64_t seq, struct amdgpu_fence **fence);
void amdgpu_fence_process(struct amdgpu_ring *ring);
int amdgpu_fence_wait_next(struct amdgpu_ring *ring);
int amdgpu_fence_wait_empty(struct amdgpu_ring *ring);
unsigned amdgpu_fence_count_emitted(struct amdgpu_ring *ring);
bool amdgpu_fence_signaled(struct amdgpu_fence *fence);
int amdgpu_fence_wait(struct amdgpu_fence *fence, bool interruptible);
int amdgpu_fence_wait_any(struct amdgpu_device *adev,
signed long amdgpu_fence_wait_any(struct amdgpu_device *adev,
struct amdgpu_fence **fences,
bool intr);
bool intr, long t);
struct amdgpu_fence *amdgpu_fence_ref(struct amdgpu_fence *fence);
void amdgpu_fence_unref(struct amdgpu_fence **fence);
@ -481,7 +486,7 @@ static inline bool amdgpu_fence_is_earlier(struct amdgpu_fence *a,
return a->seq < b->seq;
}
int amdgpu_user_fence_emit(struct amdgpu_ring *ring, struct amdgpu_user_fence *user,
int amdgpu_user_fence_emit(struct amdgpu_ring *ring, struct amdgpu_user_fence *user,
void *owner, struct amdgpu_fence **fence);
/*
@ -532,14 +537,16 @@ struct amdgpu_bo_va_mapping {
struct amdgpu_bo_va {
/* protected by bo being reserved */
struct list_head bo_list;
uint64_t addr;
struct amdgpu_fence *last_pt_update;
struct fence *last_pt_update;
unsigned ref_count;
/* protected by vm mutex */
struct list_head mappings;
/* protected by vm mutex and spinlock */
struct list_head vm_status;
/* mappings for this bo_va */
struct list_head invalids;
struct list_head valids;
/* constant after initialization */
struct amdgpu_vm *vm;
struct amdgpu_bo *bo;
@ -697,8 +704,8 @@ struct amdgpu_sync {
};
void amdgpu_sync_create(struct amdgpu_sync *sync);
void amdgpu_sync_fence(struct amdgpu_sync *sync,
struct amdgpu_fence *fence);
int amdgpu_sync_fence(struct amdgpu_device *adev, struct amdgpu_sync *sync,
struct fence *f);
int amdgpu_sync_resv(struct amdgpu_device *adev,
struct amdgpu_sync *sync,
struct reservation_object *resv,
@ -821,7 +828,9 @@ struct amdgpu_flip_work {
uint64_t base;
struct drm_pending_vblank_event *event;
struct amdgpu_bo *old_rbo;
struct fence *fence;
struct fence *excl;
unsigned shared_count;
struct fence **shared;
};
@ -844,6 +853,8 @@ struct amdgpu_ib {
uint32_t gws_base, gws_size;
uint32_t oa_base, oa_size;
uint32_t flags;
/* resulting sequence number */
uint64_t sequence;
};
enum amdgpu_ring_type {
@ -854,11 +865,23 @@ enum amdgpu_ring_type {
AMDGPU_RING_TYPE_VCE
};
extern struct amd_sched_backend_ops amdgpu_sched_ops;
int amdgpu_sched_ib_submit_kernel_helper(struct amdgpu_device *adev,
struct amdgpu_ring *ring,
struct amdgpu_ib *ibs,
unsigned num_ibs,
int (*free_job)(struct amdgpu_cs_parser *),
void *owner,
struct fence **fence);
struct amdgpu_ring {
struct amdgpu_device *adev;
const struct amdgpu_ring_funcs *funcs;
struct amdgpu_fence_driver fence_drv;
struct amd_gpu_scheduler *scheduler;
spinlock_t fence_lock;
struct mutex *ring_lock;
struct amdgpu_bo *ring_obj;
volatile uint32_t *ring;
@ -892,6 +915,7 @@ struct amdgpu_ring {
struct amdgpu_ctx *current_ctx;
enum amdgpu_ring_type type;
char name[16];
bool is_pte_ring;
};
/*
@ -943,18 +967,22 @@ struct amdgpu_vm {
struct rb_root va;
/* protecting invalidated and freed */
/* protecting invalidated */
spinlock_t status_lock;
/* BOs moved, but not yet updated in the PT */
struct list_head invalidated;
/* BOs freed, but not yet updated in the PT */
/* BOs cleared in the PT because of a move */
struct list_head cleared;
/* BO mappings freed, but not yet updated in the PT */
struct list_head freed;
/* contains the page directory */
struct amdgpu_bo *page_directory;
unsigned max_pde_used;
struct fence *page_directory_fence;
/* array of page tables, one for each page directory entry */
struct amdgpu_vm_pt *page_tables;
@ -983,27 +1011,47 @@ struct amdgpu_vm_manager {
* context related structures
*/
struct amdgpu_ctx_state {
uint64_t flags;
uint32_t hangs;
#define AMDGPU_CTX_MAX_CS_PENDING 16
struct amdgpu_ctx_ring {
uint64_t sequence;
struct fence *fences[AMDGPU_CTX_MAX_CS_PENDING];
struct amd_sched_entity entity;
};
struct amdgpu_ctx {
/* call kref_get()before CS start and kref_put() after CS fence signaled */
struct kref refcount;
struct amdgpu_fpriv *fpriv;
struct amdgpu_ctx_state state;
uint32_t id;
unsigned reset_counter;
struct kref refcount;
struct amdgpu_device *adev;
unsigned reset_counter;
spinlock_t ring_lock;
struct amdgpu_ctx_ring rings[AMDGPU_MAX_RINGS];
};
struct amdgpu_ctx_mgr {
struct amdgpu_device *adev;
struct idr ctx_handles;
/* lock for IDR system */
struct mutex lock;
struct amdgpu_device *adev;
struct mutex lock;
/* protected by lock */
struct idr ctx_handles;
};
int amdgpu_ctx_init(struct amdgpu_device *adev, bool kernel,
struct amdgpu_ctx *ctx);
void amdgpu_ctx_fini(struct amdgpu_ctx *ctx);
struct amdgpu_ctx *amdgpu_ctx_get(struct amdgpu_fpriv *fpriv, uint32_t id);
int amdgpu_ctx_put(struct amdgpu_ctx *ctx);
uint64_t amdgpu_ctx_add_fence(struct amdgpu_ctx *ctx, struct amdgpu_ring *ring,
struct fence *fence, uint64_t queued_seq);
struct fence *amdgpu_ctx_get_fence(struct amdgpu_ctx *ctx,
struct amdgpu_ring *ring, uint64_t seq);
int amdgpu_ctx_ioctl(struct drm_device *dev, void *data,
struct drm_file *filp);
void amdgpu_ctx_mgr_init(struct amdgpu_ctx_mgr *mgr);
void amdgpu_ctx_mgr_fini(struct amdgpu_ctx_mgr *mgr);
/*
* file private structure
*/
@ -1012,7 +1060,7 @@ struct amdgpu_fpriv {
struct amdgpu_vm vm;
struct mutex bo_list_lock;
struct idr bo_list_handles;
struct amdgpu_ctx_mgr ctx_mgr;
struct amdgpu_ctx_mgr ctx_mgr;
};
/*
@ -1029,6 +1077,8 @@ struct amdgpu_bo_list {
struct amdgpu_bo_list_entry *array;
};
struct amdgpu_bo_list *
amdgpu_bo_list_clone(struct amdgpu_bo_list *list);
struct amdgpu_bo_list *
amdgpu_bo_list_get(struct amdgpu_fpriv *fpriv, int id);
void amdgpu_bo_list_put(struct amdgpu_bo_list *list);
@ -1205,6 +1255,14 @@ struct amdgpu_cs_parser {
/* user fence */
struct amdgpu_user_fence uf;
struct amdgpu_ring *ring;
struct mutex job_lock;
struct work_struct job_work;
int (*prepare_job)(struct amdgpu_cs_parser *sched_job);
int (*run_job)(struct amdgpu_cs_parser *sched_job);
int (*free_job)(struct amdgpu_cs_parser *sched_job);
struct amd_sched_fence *s_fence;
};
static inline u32 amdgpu_get_ib_value(struct amdgpu_cs_parser *p, uint32_t ib_idx, int idx)
@ -1849,17 +1907,12 @@ struct amdgpu_atcs {
struct amdgpu_atcs_functions functions;
};
int amdgpu_ctx_alloc(struct amdgpu_device *adev,struct amdgpu_fpriv *fpriv,
uint32_t *id,uint32_t flags);
int amdgpu_ctx_free(struct amdgpu_device *adev, struct amdgpu_fpriv *fpriv,
uint32_t id);
/*
* CGS
*/
void *amdgpu_cgs_create_device(struct amdgpu_device *adev);
void amdgpu_cgs_destroy_device(void *cgs_device);
void amdgpu_ctx_fini(struct amdgpu_fpriv *fpriv);
struct amdgpu_ctx *amdgpu_ctx_get(struct amdgpu_fpriv *fpriv, uint32_t id);
int amdgpu_ctx_put(struct amdgpu_ctx *ctx);
extern int amdgpu_ctx_ioctl(struct drm_device *dev, void *data,
struct drm_file *filp);
/*
* Core structure, functions and helpers.
@ -1883,7 +1936,7 @@ struct amdgpu_device {
struct rw_semaphore exclusive_lock;
/* ASIC */
enum amdgpu_asic_type asic_type;
enum amd_asic_type asic_type;
uint32_t family;
uint32_t rev_id;
uint32_t external_rev_id;
@ -1976,7 +2029,6 @@ struct amdgpu_device {
struct amdgpu_irq_src hpd_irq;
/* rings */
wait_queue_head_t fence_queue;
unsigned fence_context;
struct mutex ring_lock;
unsigned num_rings;
@ -2028,6 +2080,9 @@ struct amdgpu_device {
/* amdkfd interface */
struct kfd_dev *kfd;
/* kernel conext for IB submission */
struct amdgpu_ctx kernel_ctx;
};
bool amdgpu_device_is_px(struct drm_device *dev);
@ -2215,6 +2270,12 @@ void amdgpu_pci_config_reset(struct amdgpu_device *adev);
bool amdgpu_card_posted(struct amdgpu_device *adev);
void amdgpu_update_display_priority(struct amdgpu_device *adev);
bool amdgpu_boot_test_post_card(struct amdgpu_device *adev);
struct amdgpu_cs_parser *amdgpu_cs_parser_create(struct amdgpu_device *adev,
struct drm_file *filp,
struct amdgpu_ctx *ctx,
struct amdgpu_ib *ibs,
uint32_t num_ibs);
int amdgpu_cs_parser_init(struct amdgpu_cs_parser *p, void *data);
int amdgpu_cs_get_ring(struct amdgpu_device *adev, u32 ip_type,
u32 ip_instance, u32 ring,
@ -2278,8 +2339,8 @@ void amdgpu_vm_fini(struct amdgpu_device *adev, struct amdgpu_vm *vm);
struct amdgpu_bo_list_entry *amdgpu_vm_get_bos(struct amdgpu_device *adev,
struct amdgpu_vm *vm,
struct list_head *head);
struct amdgpu_fence *amdgpu_vm_grab_id(struct amdgpu_ring *ring,
struct amdgpu_vm *vm);
int amdgpu_vm_grab_id(struct amdgpu_vm *vm, struct amdgpu_ring *ring,
struct amdgpu_sync *sync);
void amdgpu_vm_flush(struct amdgpu_ring *ring,
struct amdgpu_vm *vm,
struct amdgpu_fence *updates);

View File

@ -21,7 +21,7 @@
*/
#include "amdgpu_amdkfd.h"
#include "amdgpu_family.h"
#include "amd_shared.h"
#include <drm/drmP.h>
#include "amdgpu.h"
#include <linux/module.h>
@ -50,9 +50,11 @@ bool amdgpu_amdkfd_load_interface(struct amdgpu_device *rdev)
#endif
switch (rdev->asic_type) {
#ifdef CONFIG_DRM_AMDGPU_CIK
case CHIP_KAVERI:
kfd2kgd = amdgpu_amdkfd_gfx_7_get_functions();
break;
#endif
case CHIP_CARRIZO:
kfd2kgd = amdgpu_amdkfd_gfx_8_0_get_functions();
break;

View File

@ -897,7 +897,7 @@ bool amdgpu_atombios_get_asic_ss_info(struct amdgpu_device *adev,
if ((id == ASIC_INTERNAL_ENGINE_SS) ||
(id == ASIC_INTERNAL_MEMORY_SS))
ss->rate /= 100;
if (adev->flags & AMDGPU_IS_APU)
if (adev->flags & AMD_IS_APU)
amdgpu_atombios_get_igp_ss_overrides(adev, ss, id);
return true;
}
@ -1058,7 +1058,7 @@ void amdgpu_atombios_set_memory_clock(struct amdgpu_device *adev,
SET_MEMORY_CLOCK_PS_ALLOCATION args;
int index = GetIndexIntoMasterTable(COMMAND, SetMemoryClock);
if (adev->flags & AMDGPU_IS_APU)
if (adev->flags & AMD_IS_APU)
return;
args.ulTargetMemoryClock = cpu_to_le32(mem_clock); /* 10 khz */

View File

@ -42,7 +42,7 @@ static int amdgpu_benchmark_do_move(struct amdgpu_device *adev, unsigned size,
r = amdgpu_copy_buffer(ring, saddr, daddr, size, NULL, &fence);
if (r)
goto exit_do_move;
r = amdgpu_fence_wait(fence, false);
r = fence_wait(&fence->base, false);
if (r)
goto exit_do_move;
amdgpu_fence_unref(&fence);

View File

@ -48,7 +48,7 @@ static bool igp_read_bios_from_vram(struct amdgpu_device *adev)
resource_size_t vram_base;
resource_size_t size = 256 * 1024; /* ??? */
if (!(adev->flags & AMDGPU_IS_APU))
if (!(adev->flags & AMD_IS_APU))
if (!amdgpu_card_posted(adev))
return false;
@ -184,7 +184,7 @@ static bool amdgpu_atrm_get_bios(struct amdgpu_device *adev)
bool found = false;
/* ATRM is for the discrete card only */
if (adev->flags & AMDGPU_IS_APU)
if (adev->flags & AMD_IS_APU)
return false;
while ((pdev = pci_get_class(PCI_CLASS_DISPLAY_VGA << 8, pdev)) != NULL) {
@ -246,7 +246,7 @@ static inline bool amdgpu_atrm_get_bios(struct amdgpu_device *adev)
static bool amdgpu_read_disabled_bios(struct amdgpu_device *adev)
{
if (adev->flags & AMDGPU_IS_APU)
if (adev->flags & AMD_IS_APU)
return igp_read_bios_from_vram(adev);
else
return amdgpu_asic_read_disabled_bios(adev);

View File

@ -62,6 +62,39 @@ static int amdgpu_bo_list_create(struct amdgpu_fpriv *fpriv,
return 0;
}
struct amdgpu_bo_list *
amdgpu_bo_list_clone(struct amdgpu_bo_list *list)
{
struct amdgpu_bo_list *result;
unsigned i;
result = kmalloc(sizeof(struct amdgpu_bo_list), GFP_KERNEL);
if (!result)
return NULL;
result->array = drm_calloc_large(list->num_entries,
sizeof(struct amdgpu_bo_list_entry));
if (!result->array) {
kfree(result);
return NULL;
}
mutex_init(&result->lock);
result->gds_obj = list->gds_obj;
result->gws_obj = list->gws_obj;
result->oa_obj = list->oa_obj;
result->has_userptr = list->has_userptr;
result->num_entries = list->num_entries;
memcpy(result->array, list->array, list->num_entries *
sizeof(struct amdgpu_bo_list_entry));
for (i = 0; i < result->num_entries; ++i)
amdgpu_bo_ref(result->array[i].robj);
return result;
}
static void amdgpu_bo_list_destroy(struct amdgpu_fpriv *fpriv, int id)
{
struct amdgpu_bo_list *list;

View File

@ -0,0 +1,838 @@
/*
* Copyright 2015 Advanced Micro Devices, Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*
*
*/
#include <linux/list.h>
#include <linux/slab.h>
#include <linux/pci.h>
#include <drm/drmP.h>
#include <linux/firmware.h>
#include <drm/amdgpu_drm.h>
#include "amdgpu.h"
#include "cgs_linux.h"
#include "atom.h"
#include "amdgpu_ucode.h"
struct amdgpu_cgs_device {
struct cgs_device base;
struct amdgpu_device *adev;
};
#define CGS_FUNC_ADEV \
struct amdgpu_device *adev = \
((struct amdgpu_cgs_device *)cgs_device)->adev
static int amdgpu_cgs_gpu_mem_info(void *cgs_device, enum cgs_gpu_mem_type type,
uint64_t *mc_start, uint64_t *mc_size,
uint64_t *mem_size)
{
CGS_FUNC_ADEV;
switch(type) {
case CGS_GPU_MEM_TYPE__VISIBLE_CONTIG_FB:
case CGS_GPU_MEM_TYPE__VISIBLE_FB:
*mc_start = 0;
*mc_size = adev->mc.visible_vram_size;
*mem_size = adev->mc.visible_vram_size - adev->vram_pin_size;
break;
case CGS_GPU_MEM_TYPE__INVISIBLE_CONTIG_FB:
case CGS_GPU_MEM_TYPE__INVISIBLE_FB:
*mc_start = adev->mc.visible_vram_size;
*mc_size = adev->mc.real_vram_size - adev->mc.visible_vram_size;
*mem_size = *mc_size;
break;
case CGS_GPU_MEM_TYPE__GART_CACHEABLE:
case CGS_GPU_MEM_TYPE__GART_WRITECOMBINE:
*mc_start = adev->mc.gtt_start;
*mc_size = adev->mc.gtt_size;
*mem_size = adev->mc.gtt_size - adev->gart_pin_size;
break;
default:
return -EINVAL;
}
return 0;
}
static int amdgpu_cgs_gmap_kmem(void *cgs_device, void *kmem,
uint64_t size,
uint64_t min_offset, uint64_t max_offset,
cgs_handle_t *kmem_handle, uint64_t *mcaddr)
{
CGS_FUNC_ADEV;
int ret;
struct amdgpu_bo *bo;
struct page *kmem_page = vmalloc_to_page(kmem);
int npages = ALIGN(size, PAGE_SIZE) >> PAGE_SHIFT;
struct sg_table *sg = drm_prime_pages_to_sg(&kmem_page, npages);
ret = amdgpu_bo_create(adev, size, PAGE_SIZE, false,
AMDGPU_GEM_DOMAIN_GTT, 0, sg, &bo);
if (ret)
return ret;
ret = amdgpu_bo_reserve(bo, false);
if (unlikely(ret != 0))
return ret;
/* pin buffer into GTT */
ret = amdgpu_bo_pin_restricted(bo, AMDGPU_GEM_DOMAIN_GTT,
min_offset, max_offset, mcaddr);
amdgpu_bo_unreserve(bo);
*kmem_handle = (cgs_handle_t)bo;
return ret;
}
static int amdgpu_cgs_gunmap_kmem(void *cgs_device, cgs_handle_t kmem_handle)
{
struct amdgpu_bo *obj = (struct amdgpu_bo *)kmem_handle;
if (obj) {
int r = amdgpu_bo_reserve(obj, false);
if (likely(r == 0)) {
amdgpu_bo_unpin(obj);
amdgpu_bo_unreserve(obj);
}
amdgpu_bo_unref(&obj);
}
return 0;
}
static int amdgpu_cgs_alloc_gpu_mem(void *cgs_device,
enum cgs_gpu_mem_type type,
uint64_t size, uint64_t align,
uint64_t min_offset, uint64_t max_offset,
cgs_handle_t *handle)
{
CGS_FUNC_ADEV;
uint16_t flags = 0;
int ret = 0;
uint32_t domain = 0;
struct amdgpu_bo *obj;
struct ttm_placement placement;
struct ttm_place place;
if (min_offset > max_offset) {
BUG_ON(1);
return -EINVAL;
}
/* fail if the alignment is not a power of 2 */
if (((align != 1) && (align & (align - 1)))
|| size == 0 || align == 0)
return -EINVAL;
switch(type) {
case CGS_GPU_MEM_TYPE__VISIBLE_CONTIG_FB:
case CGS_GPU_MEM_TYPE__VISIBLE_FB:
flags = AMDGPU_GEM_CREATE_CPU_ACCESS_REQUIRED;
domain = AMDGPU_GEM_DOMAIN_VRAM;
if (max_offset > adev->mc.real_vram_size)
return -EINVAL;
place.fpfn = min_offset >> PAGE_SHIFT;
place.lpfn = max_offset >> PAGE_SHIFT;
place.flags = TTM_PL_FLAG_WC | TTM_PL_FLAG_UNCACHED |
TTM_PL_FLAG_VRAM;
break;
case CGS_GPU_MEM_TYPE__INVISIBLE_CONTIG_FB:
case CGS_GPU_MEM_TYPE__INVISIBLE_FB:
flags = AMDGPU_GEM_CREATE_NO_CPU_ACCESS;
domain = AMDGPU_GEM_DOMAIN_VRAM;
if (adev->mc.visible_vram_size < adev->mc.real_vram_size) {
place.fpfn =
max(min_offset, adev->mc.visible_vram_size) >> PAGE_SHIFT;
place.lpfn =
min(max_offset, adev->mc.real_vram_size) >> PAGE_SHIFT;
place.flags = TTM_PL_FLAG_WC | TTM_PL_FLAG_UNCACHED |
TTM_PL_FLAG_VRAM;
}
break;
case CGS_GPU_MEM_TYPE__GART_CACHEABLE:
domain = AMDGPU_GEM_DOMAIN_GTT;
place.fpfn = min_offset >> PAGE_SHIFT;
place.lpfn = max_offset >> PAGE_SHIFT;
place.flags = TTM_PL_FLAG_CACHED | TTM_PL_FLAG_TT;
break;
case CGS_GPU_MEM_TYPE__GART_WRITECOMBINE:
flags = AMDGPU_GEM_CREATE_CPU_GTT_USWC;
domain = AMDGPU_GEM_DOMAIN_GTT;
place.fpfn = min_offset >> PAGE_SHIFT;
place.lpfn = max_offset >> PAGE_SHIFT;
place.flags = TTM_PL_FLAG_WC | TTM_PL_FLAG_TT |
TTM_PL_FLAG_UNCACHED;
break;
default:
return -EINVAL;
}
*handle = 0;
placement.placement = &place;
placement.num_placement = 1;
placement.busy_placement = &place;
placement.num_busy_placement = 1;
ret = amdgpu_bo_create_restricted(adev, size, PAGE_SIZE,
true, domain, flags,
NULL, &placement, &obj);
if (ret) {
DRM_ERROR("(%d) bo create failed\n", ret);
return ret;
}
*handle = (cgs_handle_t)obj;
return ret;
}
static int amdgpu_cgs_import_gpu_mem(void *cgs_device, int dmabuf_fd,
cgs_handle_t *handle)
{
CGS_FUNC_ADEV;
int r;
uint32_t dma_handle;
struct drm_gem_object *obj;
struct amdgpu_bo *bo;
struct drm_device *dev = adev->ddev;
struct drm_file *file_priv = NULL, *priv;
mutex_lock(&dev->struct_mutex);
list_for_each_entry(priv, &dev->filelist, lhead) {
rcu_read_lock();
if (priv->pid == get_pid(task_pid(current)))
file_priv = priv;
rcu_read_unlock();
if (file_priv)
break;
}
mutex_unlock(&dev->struct_mutex);
r = dev->driver->prime_fd_to_handle(dev,
file_priv, dmabuf_fd,
&dma_handle);
spin_lock(&file_priv->table_lock);
/* Check if we currently have a reference on the object */
obj = idr_find(&file_priv->object_idr, dma_handle);
if (obj == NULL) {
spin_unlock(&file_priv->table_lock);
return -EINVAL;
}
spin_unlock(&file_priv->table_lock);
bo = gem_to_amdgpu_bo(obj);
*handle = (cgs_handle_t)bo;
return 0;
}
static int amdgpu_cgs_free_gpu_mem(void *cgs_device, cgs_handle_t handle)
{
struct amdgpu_bo *obj = (struct amdgpu_bo *)handle;
if (obj) {
int r = amdgpu_bo_reserve(obj, false);
if (likely(r == 0)) {
amdgpu_bo_kunmap(obj);
amdgpu_bo_unpin(obj);
amdgpu_bo_unreserve(obj);
}
amdgpu_bo_unref(&obj);
}
return 0;
}
static int amdgpu_cgs_gmap_gpu_mem(void *cgs_device, cgs_handle_t handle,
uint64_t *mcaddr)
{
int r;
u64 min_offset, max_offset;
struct amdgpu_bo *obj = (struct amdgpu_bo *)handle;
WARN_ON_ONCE(obj->placement.num_placement > 1);
min_offset = obj->placements[0].fpfn << PAGE_SHIFT;
max_offset = obj->placements[0].lpfn << PAGE_SHIFT;
r = amdgpu_bo_reserve(obj, false);
if (unlikely(r != 0))
return r;
r = amdgpu_bo_pin_restricted(obj, AMDGPU_GEM_DOMAIN_GTT,
min_offset, max_offset, mcaddr);
amdgpu_bo_unreserve(obj);
return r;
}
static int amdgpu_cgs_gunmap_gpu_mem(void *cgs_device, cgs_handle_t handle)
{
int r;
struct amdgpu_bo *obj = (struct amdgpu_bo *)handle;
r = amdgpu_bo_reserve(obj, false);
if (unlikely(r != 0))
return r;
r = amdgpu_bo_unpin(obj);
amdgpu_bo_unreserve(obj);
return r;
}
static int amdgpu_cgs_kmap_gpu_mem(void *cgs_device, cgs_handle_t handle,
void **map)
{
int r;
struct amdgpu_bo *obj = (struct amdgpu_bo *)handle;
r = amdgpu_bo_reserve(obj, false);
if (unlikely(r != 0))
return r;
r = amdgpu_bo_kmap(obj, map);
amdgpu_bo_unreserve(obj);
return r;
}
static int amdgpu_cgs_kunmap_gpu_mem(void *cgs_device, cgs_handle_t handle)
{
int r;
struct amdgpu_bo *obj = (struct amdgpu_bo *)handle;
r = amdgpu_bo_reserve(obj, false);
if (unlikely(r != 0))
return r;
amdgpu_bo_kunmap(obj);
amdgpu_bo_unreserve(obj);
return r;
}
static uint32_t amdgpu_cgs_read_register(void *cgs_device, unsigned offset)
{
CGS_FUNC_ADEV;
return RREG32(offset);
}
static void amdgpu_cgs_write_register(void *cgs_device, unsigned offset,
uint32_t value)
{
CGS_FUNC_ADEV;
WREG32(offset, value);
}
static uint32_t amdgpu_cgs_read_ind_register(void *cgs_device,
enum cgs_ind_reg space,
unsigned index)
{
CGS_FUNC_ADEV;
switch (space) {
case CGS_IND_REG__MMIO:
return RREG32_IDX(index);
case CGS_IND_REG__PCIE:
return RREG32_PCIE(index);
case CGS_IND_REG__SMC:
return RREG32_SMC(index);
case CGS_IND_REG__UVD_CTX:
return RREG32_UVD_CTX(index);
case CGS_IND_REG__DIDT:
return RREG32_DIDT(index);
case CGS_IND_REG__AUDIO_ENDPT:
DRM_ERROR("audio endpt register access not implemented.\n");
return 0;
}
WARN(1, "Invalid indirect register space");
return 0;
}
static void amdgpu_cgs_write_ind_register(void *cgs_device,
enum cgs_ind_reg space,
unsigned index, uint32_t value)
{
CGS_FUNC_ADEV;
switch (space) {
case CGS_IND_REG__MMIO:
return WREG32_IDX(index, value);
case CGS_IND_REG__PCIE:
return WREG32_PCIE(index, value);
case CGS_IND_REG__SMC:
return WREG32_SMC(index, value);
case CGS_IND_REG__UVD_CTX:
return WREG32_UVD_CTX(index, value);
case CGS_IND_REG__DIDT:
return WREG32_DIDT(index, value);
case CGS_IND_REG__AUDIO_ENDPT:
DRM_ERROR("audio endpt register access not implemented.\n");
return;
}
WARN(1, "Invalid indirect register space");
}
static uint8_t amdgpu_cgs_read_pci_config_byte(void *cgs_device, unsigned addr)
{
CGS_FUNC_ADEV;
uint8_t val;
int ret = pci_read_config_byte(adev->pdev, addr, &val);
if (WARN(ret, "pci_read_config_byte error"))
return 0;
return val;
}
static uint16_t amdgpu_cgs_read_pci_config_word(void *cgs_device, unsigned addr)
{
CGS_FUNC_ADEV;
uint16_t val;
int ret = pci_read_config_word(adev->pdev, addr, &val);
if (WARN(ret, "pci_read_config_word error"))
return 0;
return val;
}
static uint32_t amdgpu_cgs_read_pci_config_dword(void *cgs_device,
unsigned addr)
{
CGS_FUNC_ADEV;
uint32_t val;
int ret = pci_read_config_dword(adev->pdev, addr, &val);
if (WARN(ret, "pci_read_config_dword error"))
return 0;
return val;
}
static void amdgpu_cgs_write_pci_config_byte(void *cgs_device, unsigned addr,
uint8_t value)
{
CGS_FUNC_ADEV;
int ret = pci_write_config_byte(adev->pdev, addr, value);
WARN(ret, "pci_write_config_byte error");
}
static void amdgpu_cgs_write_pci_config_word(void *cgs_device, unsigned addr,
uint16_t value)
{
CGS_FUNC_ADEV;
int ret = pci_write_config_word(adev->pdev, addr, value);
WARN(ret, "pci_write_config_word error");
}
static void amdgpu_cgs_write_pci_config_dword(void *cgs_device, unsigned addr,
uint32_t value)
{
CGS_FUNC_ADEV;
int ret = pci_write_config_dword(adev->pdev, addr, value);
WARN(ret, "pci_write_config_dword error");
}
static const void *amdgpu_cgs_atom_get_data_table(void *cgs_device,
unsigned table, uint16_t *size,
uint8_t *frev, uint8_t *crev)
{
CGS_FUNC_ADEV;
uint16_t data_start;
if (amdgpu_atom_parse_data_header(
adev->mode_info.atom_context, table, size,
frev, crev, &data_start))
return (uint8_t*)adev->mode_info.atom_context->bios +
data_start;
return NULL;
}
static int amdgpu_cgs_atom_get_cmd_table_revs(void *cgs_device, unsigned table,
uint8_t *frev, uint8_t *crev)
{
CGS_FUNC_ADEV;
if (amdgpu_atom_parse_cmd_header(
adev->mode_info.atom_context, table,
frev, crev))
return 0;
return -EINVAL;
}
static int amdgpu_cgs_atom_exec_cmd_table(void *cgs_device, unsigned table,
void *args)
{
CGS_FUNC_ADEV;
return amdgpu_atom_execute_table(
adev->mode_info.atom_context, table, args);
}
static int amdgpu_cgs_create_pm_request(void *cgs_device, cgs_handle_t *request)
{
/* TODO */
return 0;
}
static int amdgpu_cgs_destroy_pm_request(void *cgs_device, cgs_handle_t request)
{
/* TODO */
return 0;
}
static int amdgpu_cgs_set_pm_request(void *cgs_device, cgs_handle_t request,
int active)
{
/* TODO */
return 0;
}
static int amdgpu_cgs_pm_request_clock(void *cgs_device, cgs_handle_t request,
enum cgs_clock clock, unsigned freq)
{
/* TODO */
return 0;
}
static int amdgpu_cgs_pm_request_engine(void *cgs_device, cgs_handle_t request,
enum cgs_engine engine, int powered)
{
/* TODO */
return 0;
}
static int amdgpu_cgs_pm_query_clock_limits(void *cgs_device,
enum cgs_clock clock,
struct cgs_clock_limits *limits)
{
/* TODO */
return 0;
}
static int amdgpu_cgs_set_camera_voltages(void *cgs_device, uint32_t mask,
const uint32_t *voltages)
{
DRM_ERROR("not implemented");
return -EPERM;
}
struct cgs_irq_params {
unsigned src_id;
cgs_irq_source_set_func_t set;
cgs_irq_handler_func_t handler;
void *private_data;
};
static int cgs_set_irq_state(struct amdgpu_device *adev,
struct amdgpu_irq_src *src,
unsigned type,
enum amdgpu_interrupt_state state)
{
struct cgs_irq_params *irq_params =
(struct cgs_irq_params *)src->data;
if (!irq_params)
return -EINVAL;
if (!irq_params->set)
return -EINVAL;
return irq_params->set(irq_params->private_data,
irq_params->src_id,
type,
(int)state);
}
static int cgs_process_irq(struct amdgpu_device *adev,
struct amdgpu_irq_src *source,
struct amdgpu_iv_entry *entry)
{
struct cgs_irq_params *irq_params =
(struct cgs_irq_params *)source->data;
if (!irq_params)
return -EINVAL;
if (!irq_params->handler)
return -EINVAL;
return irq_params->handler(irq_params->private_data,
irq_params->src_id,
entry->iv_entry);
}
static const struct amdgpu_irq_src_funcs cgs_irq_funcs = {
.set = cgs_set_irq_state,
.process = cgs_process_irq,
};
static int amdgpu_cgs_add_irq_source(void *cgs_device, unsigned src_id,
unsigned num_types,
cgs_irq_source_set_func_t set,
cgs_irq_handler_func_t handler,
void *private_data)
{
CGS_FUNC_ADEV;
int ret = 0;
struct cgs_irq_params *irq_params;
struct amdgpu_irq_src *source =
kzalloc(sizeof(struct amdgpu_irq_src), GFP_KERNEL);
if (!source)
return -ENOMEM;
irq_params =
kzalloc(sizeof(struct cgs_irq_params), GFP_KERNEL);
if (!irq_params) {
kfree(source);
return -ENOMEM;
}
source->num_types = num_types;
source->funcs = &cgs_irq_funcs;
irq_params->src_id = src_id;
irq_params->set = set;
irq_params->handler = handler;
irq_params->private_data = private_data;
source->data = (void *)irq_params;
ret = amdgpu_irq_add_id(adev, src_id, source);
if (ret) {
kfree(irq_params);
kfree(source);
}
return ret;
}
static int amdgpu_cgs_irq_get(void *cgs_device, unsigned src_id, unsigned type)
{
CGS_FUNC_ADEV;
return amdgpu_irq_get(adev, adev->irq.sources[src_id], type);
}
static int amdgpu_cgs_irq_put(void *cgs_device, unsigned src_id, unsigned type)
{
CGS_FUNC_ADEV;
return amdgpu_irq_put(adev, adev->irq.sources[src_id], type);
}
int amdgpu_cgs_set_clockgating_state(void *cgs_device,
enum amd_ip_block_type block_type,
enum amd_clockgating_state state)
{
CGS_FUNC_ADEV;
int i, r = -1;
for (i = 0; i < adev->num_ip_blocks; i++) {
if (!adev->ip_block_status[i].valid)
continue;
if (adev->ip_blocks[i].type == block_type) {
r = adev->ip_blocks[i].funcs->set_clockgating_state(
(void *)adev,
state);
break;
}
}
return r;
}
int amdgpu_cgs_set_powergating_state(void *cgs_device,
enum amd_ip_block_type block_type,
enum amd_powergating_state state)
{
CGS_FUNC_ADEV;
int i, r = -1;
for (i = 0; i < adev->num_ip_blocks; i++) {
if (!adev->ip_block_status[i].valid)
continue;
if (adev->ip_blocks[i].type == block_type) {
r = adev->ip_blocks[i].funcs->set_powergating_state(
(void *)adev,
state);
break;
}
}
return r;
}
static uint32_t fw_type_convert(void *cgs_device, uint32_t fw_type)
{
CGS_FUNC_ADEV;
enum AMDGPU_UCODE_ID result = AMDGPU_UCODE_ID_MAXIMUM;
switch (fw_type) {
case CGS_UCODE_ID_SDMA0:
result = AMDGPU_UCODE_ID_SDMA0;
break;
case CGS_UCODE_ID_SDMA1:
result = AMDGPU_UCODE_ID_SDMA1;
break;
case CGS_UCODE_ID_CP_CE:
result = AMDGPU_UCODE_ID_CP_CE;
break;
case CGS_UCODE_ID_CP_PFP:
result = AMDGPU_UCODE_ID_CP_PFP;
break;
case CGS_UCODE_ID_CP_ME:
result = AMDGPU_UCODE_ID_CP_ME;
break;
case CGS_UCODE_ID_CP_MEC:
case CGS_UCODE_ID_CP_MEC_JT1:
result = AMDGPU_UCODE_ID_CP_MEC1;
break;
case CGS_UCODE_ID_CP_MEC_JT2:
if (adev->asic_type == CHIP_TONGA)
result = AMDGPU_UCODE_ID_CP_MEC2;
else if (adev->asic_type == CHIP_CARRIZO)
result = AMDGPU_UCODE_ID_CP_MEC1;
break;
case CGS_UCODE_ID_RLC_G:
result = AMDGPU_UCODE_ID_RLC_G;
break;
default:
DRM_ERROR("Firmware type not supported\n");
}
return result;
}
static int amdgpu_cgs_get_firmware_info(void *cgs_device,
enum cgs_ucode_id type,
struct cgs_firmware_info *info)
{
CGS_FUNC_ADEV;
if (CGS_UCODE_ID_SMU != type) {
uint64_t gpu_addr;
uint32_t data_size;
const struct gfx_firmware_header_v1_0 *header;
enum AMDGPU_UCODE_ID id;
struct amdgpu_firmware_info *ucode;
id = fw_type_convert(cgs_device, type);
ucode = &adev->firmware.ucode[id];
if (ucode->fw == NULL)
return -EINVAL;
gpu_addr = ucode->mc_addr;
header = (const struct gfx_firmware_header_v1_0 *)ucode->fw->data;
data_size = le32_to_cpu(header->header.ucode_size_bytes);
if ((type == CGS_UCODE_ID_CP_MEC_JT1) ||
(type == CGS_UCODE_ID_CP_MEC_JT2)) {
gpu_addr += le32_to_cpu(header->jt_offset) << 2;
data_size = le32_to_cpu(header->jt_size) << 2;
}
info->mc_addr = gpu_addr;
info->image_size = data_size;
info->version = (uint16_t)le32_to_cpu(header->header.ucode_version);
info->feature_version = (uint16_t)le32_to_cpu(header->ucode_feature_version);
} else {
char fw_name[30] = {0};
int err = 0;
uint32_t ucode_size;
uint32_t ucode_start_address;
const uint8_t *src;
const struct smc_firmware_header_v1_0 *hdr;
switch (adev->asic_type) {
case CHIP_TONGA:
strcpy(fw_name, "amdgpu/tonga_smc.bin");
break;
default:
DRM_ERROR("SMC firmware not supported\n");
return -EINVAL;
}
err = request_firmware(&adev->pm.fw, fw_name, adev->dev);
if (err) {
DRM_ERROR("Failed to request firmware\n");
return err;
}
err = amdgpu_ucode_validate(adev->pm.fw);
if (err) {
DRM_ERROR("Failed to load firmware \"%s\"", fw_name);
release_firmware(adev->pm.fw);
adev->pm.fw = NULL;
return err;
}
hdr = (const struct smc_firmware_header_v1_0 *) adev->pm.fw->data;
adev->pm.fw_version = le32_to_cpu(hdr->header.ucode_version);
ucode_size = le32_to_cpu(hdr->header.ucode_size_bytes);
ucode_start_address = le32_to_cpu(hdr->ucode_start_addr);
src = (const uint8_t *)(adev->pm.fw->data +
le32_to_cpu(hdr->header.ucode_array_offset_bytes));
info->version = adev->pm.fw_version;
info->image_size = ucode_size;
info->kptr = (void *)src;
}
return 0;
}
static const struct cgs_ops amdgpu_cgs_ops = {
amdgpu_cgs_gpu_mem_info,
amdgpu_cgs_gmap_kmem,
amdgpu_cgs_gunmap_kmem,
amdgpu_cgs_alloc_gpu_mem,
amdgpu_cgs_free_gpu_mem,
amdgpu_cgs_gmap_gpu_mem,
amdgpu_cgs_gunmap_gpu_mem,
amdgpu_cgs_kmap_gpu_mem,
amdgpu_cgs_kunmap_gpu_mem,
amdgpu_cgs_read_register,
amdgpu_cgs_write_register,
amdgpu_cgs_read_ind_register,
amdgpu_cgs_write_ind_register,
amdgpu_cgs_read_pci_config_byte,
amdgpu_cgs_read_pci_config_word,
amdgpu_cgs_read_pci_config_dword,
amdgpu_cgs_write_pci_config_byte,
amdgpu_cgs_write_pci_config_word,
amdgpu_cgs_write_pci_config_dword,
amdgpu_cgs_atom_get_data_table,
amdgpu_cgs_atom_get_cmd_table_revs,
amdgpu_cgs_atom_exec_cmd_table,
amdgpu_cgs_create_pm_request,
amdgpu_cgs_destroy_pm_request,
amdgpu_cgs_set_pm_request,
amdgpu_cgs_pm_request_clock,
amdgpu_cgs_pm_request_engine,
amdgpu_cgs_pm_query_clock_limits,
amdgpu_cgs_set_camera_voltages,
amdgpu_cgs_get_firmware_info,
amdgpu_cgs_set_powergating_state,
amdgpu_cgs_set_clockgating_state
};
static const struct cgs_os_ops amdgpu_cgs_os_ops = {
amdgpu_cgs_import_gpu_mem,
amdgpu_cgs_add_irq_source,
amdgpu_cgs_irq_get,
amdgpu_cgs_irq_put
};
void *amdgpu_cgs_create_device(struct amdgpu_device *adev)
{
struct amdgpu_cgs_device *cgs_device =
kmalloc(sizeof(*cgs_device), GFP_KERNEL);
if (!cgs_device) {
DRM_ERROR("Couldn't allocate CGS device structure\n");
return NULL;
}
cgs_device->base.ops = &amdgpu_cgs_ops;
cgs_device->base.os_ops = &amdgpu_cgs_os_ops;
cgs_device->adev = adev;
return cgs_device;
}
void amdgpu_cgs_destroy_device(void *cgs_device)
{
kfree(cgs_device);
}

View File

@ -126,12 +126,54 @@ int amdgpu_cs_get_ring(struct amdgpu_device *adev, u32 ip_type,
return 0;
}
static void amdgpu_job_work_func(struct work_struct *work)
{
struct amdgpu_cs_parser *sched_job =
container_of(work, struct amdgpu_cs_parser,
job_work);
mutex_lock(&sched_job->job_lock);
if (sched_job->free_job)
sched_job->free_job(sched_job);
mutex_unlock(&sched_job->job_lock);
/* after processing job, free memory */
fence_put(&sched_job->s_fence->base);
kfree(sched_job);
}
struct amdgpu_cs_parser *amdgpu_cs_parser_create(struct amdgpu_device *adev,
struct drm_file *filp,
struct amdgpu_ctx *ctx,
struct amdgpu_ib *ibs,
uint32_t num_ibs)
{
struct amdgpu_cs_parser *parser;
int i;
parser = kzalloc(sizeof(struct amdgpu_cs_parser), GFP_KERNEL);
if (!parser)
return NULL;
parser->adev = adev;
parser->filp = filp;
parser->ctx = ctx;
parser->ibs = ibs;
parser->num_ibs = num_ibs;
if (amdgpu_enable_scheduler) {
mutex_init(&parser->job_lock);
INIT_WORK(&parser->job_work, amdgpu_job_work_func);
}
for (i = 0; i < num_ibs; i++)
ibs[i].ctx = ctx;
return parser;
}
int amdgpu_cs_parser_init(struct amdgpu_cs_parser *p, void *data)
{
union drm_amdgpu_cs *cs = data;
uint64_t *chunk_array_user;
uint64_t *chunk_array = NULL;
struct amdgpu_fpriv *fpriv = p->filp->driver_priv;
struct amdgpu_bo_list *bo_list = NULL;
unsigned size, i;
int r = 0;
@ -143,17 +185,30 @@ int amdgpu_cs_parser_init(struct amdgpu_cs_parser *p, void *data)
r = -EINVAL;
goto out;
}
p->bo_list = amdgpu_bo_list_get(fpriv, cs->in.bo_list_handle);
bo_list = amdgpu_bo_list_get(fpriv, cs->in.bo_list_handle);
if (!amdgpu_enable_scheduler)
p->bo_list = bo_list;
else {
if (bo_list && !bo_list->has_userptr) {
p->bo_list = amdgpu_bo_list_clone(bo_list);
amdgpu_bo_list_put(bo_list);
if (!p->bo_list)
return -ENOMEM;
} else if (bo_list && bo_list->has_userptr)
p->bo_list = bo_list;
else
p->bo_list = NULL;
}
/* get chunks */
INIT_LIST_HEAD(&p->validated);
chunk_array = kcalloc(cs->in.num_chunks, sizeof(uint64_t), GFP_KERNEL);
chunk_array = kmalloc_array(cs->in.num_chunks, sizeof(uint64_t), GFP_KERNEL);
if (chunk_array == NULL) {
r = -ENOMEM;
goto out;
}
chunk_array_user = (uint64_t *)(unsigned long)(cs->in.chunks);
chunk_array_user = (uint64_t __user *)(cs->in.chunks);
if (copy_from_user(chunk_array, chunk_array_user,
sizeof(uint64_t)*cs->in.num_chunks)) {
r = -EFAULT;
@ -161,7 +216,7 @@ int amdgpu_cs_parser_init(struct amdgpu_cs_parser *p, void *data)
}
p->nchunks = cs->in.num_chunks;
p->chunks = kcalloc(p->nchunks, sizeof(struct amdgpu_cs_chunk),
p->chunks = kmalloc_array(p->nchunks, sizeof(struct amdgpu_cs_chunk),
GFP_KERNEL);
if (p->chunks == NULL) {
r = -ENOMEM;
@ -173,7 +228,7 @@ int amdgpu_cs_parser_init(struct amdgpu_cs_parser *p, void *data)
struct drm_amdgpu_cs_chunk user_chunk;
uint32_t __user *cdata;
chunk_ptr = (void __user *)(unsigned long)chunk_array[i];
chunk_ptr = (void __user *)chunk_array[i];
if (copy_from_user(&user_chunk, chunk_ptr,
sizeof(struct drm_amdgpu_cs_chunk))) {
r = -EFAULT;
@ -183,7 +238,7 @@ int amdgpu_cs_parser_init(struct amdgpu_cs_parser *p, void *data)
p->chunks[i].length_dw = user_chunk.length_dw;
size = p->chunks[i].length_dw;
cdata = (void __user *)(unsigned long)user_chunk.chunk_data;
cdata = (void __user *)user_chunk.chunk_data;
p->chunks[i].user_ptr = cdata;
p->chunks[i].kdata = drm_malloc_ab(size, sizeof(uint32_t));
@ -235,11 +290,10 @@ int amdgpu_cs_parser_init(struct amdgpu_cs_parser *p, void *data)
}
}
p->ibs = kcalloc(p->num_ibs, sizeof(struct amdgpu_ib), GFP_KERNEL);
if (!p->ibs) {
p->ibs = kmalloc_array(p->num_ibs, sizeof(struct amdgpu_ib), GFP_KERNEL);
if (!p->ibs)
r = -ENOMEM;
goto out;
}
out:
kfree(chunk_array);
@ -415,18 +469,8 @@ static int cmp_size_smaller_first(void *priv, struct list_head *a,
return (int)la->robj->tbo.num_pages - (int)lb->robj->tbo.num_pages;
}
/**
* cs_parser_fini() - clean parser states
* @parser: parser structure holding parsing context.
* @error: error number
*
* If error is set than unvalidate buffer, otherwise just free memory
* used by parsing context.
**/
static void amdgpu_cs_parser_fini(struct amdgpu_cs_parser *parser, int error, bool backoff)
static void amdgpu_cs_parser_fini_early(struct amdgpu_cs_parser *parser, int error, bool backoff)
{
unsigned i;
if (!error) {
/* Sort the buffer list from the smallest to largest buffer,
* which affects the order of buffers in the LRU list.
@ -447,11 +491,19 @@ static void amdgpu_cs_parser_fini(struct amdgpu_cs_parser *parser, int error, bo
ttm_eu_backoff_reservation(&parser->ticket,
&parser->validated);
}
}
static void amdgpu_cs_parser_fini_late(struct amdgpu_cs_parser *parser)
{
unsigned i;
if (parser->ctx)
amdgpu_ctx_put(parser->ctx);
if (parser->bo_list)
amdgpu_bo_list_put(parser->bo_list);
if (parser->bo_list) {
if (amdgpu_enable_scheduler && !parser->bo_list->has_userptr)
amdgpu_bo_list_free(parser->bo_list);
else
amdgpu_bo_list_put(parser->bo_list);
}
drm_free_large(parser->vm_bos);
for (i = 0; i < parser->nchunks; i++)
drm_free_large(parser->chunks[i].kdata);
@ -462,6 +514,29 @@ static void amdgpu_cs_parser_fini(struct amdgpu_cs_parser *parser, int error, bo
kfree(parser->ibs);
if (parser->uf.bo)
drm_gem_object_unreference_unlocked(&parser->uf.bo->gem_base);
if (!amdgpu_enable_scheduler)
kfree(parser);
}
/**
* cs_parser_fini() - clean parser states
* @parser: parser structure holding parsing context.
* @error: error number
*
* If error is set than unvalidate buffer, otherwise just free memory
* used by parsing context.
**/
static void amdgpu_cs_parser_fini(struct amdgpu_cs_parser *parser, int error, bool backoff)
{
amdgpu_cs_parser_fini_early(parser, error, backoff);
amdgpu_cs_parser_fini_late(parser);
}
static int amdgpu_cs_parser_free_job(struct amdgpu_cs_parser *sched_job)
{
amdgpu_cs_parser_fini_late(sched_job);
return 0;
}
static int amdgpu_bo_vm_update_pte(struct amdgpu_cs_parser *p,
@ -476,12 +551,18 @@ static int amdgpu_bo_vm_update_pte(struct amdgpu_cs_parser *p,
if (r)
return r;
r = amdgpu_sync_fence(adev, &p->ibs[0].sync, vm->page_directory_fence);
if (r)
return r;
r = amdgpu_vm_clear_freed(adev, vm);
if (r)
return r;
if (p->bo_list) {
for (i = 0; i < p->bo_list->num_entries; i++) {
struct fence *f;
/* ignore duplicates */
bo = p->bo_list->array[i].robj;
if (!bo)
@ -495,7 +576,10 @@ static int amdgpu_bo_vm_update_pte(struct amdgpu_cs_parser *p,
if (r)
return r;
amdgpu_sync_fence(&p->ibs[0].sync, bo_va->last_pt_update);
f = bo_va->last_pt_update;
r = amdgpu_sync_fence(adev, &p->ibs[0].sync, f);
if (r)
return r;
}
}
@ -529,9 +613,9 @@ static int amdgpu_cs_ib_vm_chunk(struct amdgpu_device *adev,
goto out;
}
amdgpu_cs_sync_rings(parser);
r = amdgpu_ib_schedule(adev, parser->num_ibs, parser->ibs,
parser->filp);
if (!amdgpu_enable_scheduler)
r = amdgpu_ib_schedule(adev, parser->num_ibs, parser->ibs,
parser->filp);
out:
mutex_unlock(&vm->mutex);
@ -650,7 +734,6 @@ static int amdgpu_cs_ib_fill(struct amdgpu_device *adev,
ib->oa_size = amdgpu_bo_size(oa);
}
}
/* wrap the last IB with user fence */
if (parser->uf.bo) {
struct amdgpu_ib *ib = &parser->ibs[parser->num_ibs - 1];
@ -693,9 +776,9 @@ static int amdgpu_cs_dependencies(struct amdgpu_device *adev,
sizeof(struct drm_amdgpu_cs_chunk_dep);
for (j = 0; j < num_deps; ++j) {
struct amdgpu_fence *fence;
struct amdgpu_ring *ring;
struct amdgpu_ctx *ctx;
struct fence *fence;
r = amdgpu_cs_get_ring(adev, deps[j].ip_type,
deps[j].ip_instance,
@ -707,50 +790,34 @@ static int amdgpu_cs_dependencies(struct amdgpu_device *adev,
if (ctx == NULL)
return -EINVAL;
r = amdgpu_fence_recreate(ring, p->filp,
deps[j].handle,
&fence);
if (r) {
fence = amdgpu_ctx_get_fence(ctx, ring,
deps[j].handle);
if (IS_ERR(fence)) {
r = PTR_ERR(fence);
amdgpu_ctx_put(ctx);
return r;
}
amdgpu_sync_fence(&ib->sync, fence);
amdgpu_fence_unref(&fence);
amdgpu_ctx_put(ctx);
} else if (fence) {
r = amdgpu_sync_fence(adev, &ib->sync, fence);
fence_put(fence);
amdgpu_ctx_put(ctx);
if (r)
return r;
}
}
}
return 0;
}
int amdgpu_cs_ioctl(struct drm_device *dev, void *data, struct drm_file *filp)
static int amdgpu_cs_parser_prepare_job(struct amdgpu_cs_parser *sched_job)
{
struct amdgpu_device *adev = dev->dev_private;
union drm_amdgpu_cs *cs = data;
struct amdgpu_cs_parser parser;
int r, i;
struct amdgpu_cs_parser *parser = sched_job;
struct amdgpu_device *adev = sched_job->adev;
bool reserved_buffers = false;
down_read(&adev->exclusive_lock);
if (!adev->accel_working) {
up_read(&adev->exclusive_lock);
return -EBUSY;
}
/* initialize parser */
memset(&parser, 0, sizeof(struct amdgpu_cs_parser));
parser.filp = filp;
parser.adev = adev;
r = amdgpu_cs_parser_init(&parser, data);
if (r) {
DRM_ERROR("Failed to initialize parser !\n");
amdgpu_cs_parser_fini(&parser, r, false);
up_read(&adev->exclusive_lock);
r = amdgpu_cs_handle_lockup(adev, r);
return r;
}
r = amdgpu_cs_parser_relocs(&parser);
r = amdgpu_cs_parser_relocs(parser);
if (r) {
if (r != -ERESTARTSYS) {
if (r == -ENOMEM)
@ -762,30 +829,114 @@ int amdgpu_cs_ioctl(struct drm_device *dev, void *data, struct drm_file *filp)
if (!r) {
reserved_buffers = true;
r = amdgpu_cs_ib_fill(adev, &parser);
r = amdgpu_cs_ib_fill(adev, parser);
}
if (!r) {
r = amdgpu_cs_dependencies(adev, parser);
if (r)
DRM_ERROR("Failed in the dependencies handling %d!\n", r);
}
if (r) {
amdgpu_cs_parser_fini(parser, r, reserved_buffers);
return r;
}
if (!r)
r = amdgpu_cs_dependencies(adev, &parser);
for (i = 0; i < parser->num_ibs; i++)
trace_amdgpu_cs(parser, i);
r = amdgpu_cs_ib_vm_chunk(adev, parser);
return r;
}
static struct amdgpu_ring *amdgpu_cs_parser_get_ring(
struct amdgpu_device *adev,
struct amdgpu_cs_parser *parser)
{
int i, r;
struct amdgpu_cs_chunk *chunk;
struct drm_amdgpu_cs_chunk_ib *chunk_ib;
struct amdgpu_ring *ring;
for (i = 0; i < parser->nchunks; i++) {
chunk = &parser->chunks[i];
chunk_ib = (struct drm_amdgpu_cs_chunk_ib *)chunk->kdata;
if (chunk->chunk_id != AMDGPU_CHUNK_ID_IB)
continue;
r = amdgpu_cs_get_ring(adev, chunk_ib->ip_type,
chunk_ib->ip_instance, chunk_ib->ring,
&ring);
if (r)
return NULL;
break;
}
return ring;
}
int amdgpu_cs_ioctl(struct drm_device *dev, void *data, struct drm_file *filp)
{
struct amdgpu_device *adev = dev->dev_private;
union drm_amdgpu_cs *cs = data;
struct amdgpu_cs_parser *parser;
int r;
down_read(&adev->exclusive_lock);
if (!adev->accel_working) {
up_read(&adev->exclusive_lock);
return -EBUSY;
}
parser = amdgpu_cs_parser_create(adev, filp, NULL, NULL, 0);
if (!parser)
return -ENOMEM;
r = amdgpu_cs_parser_init(parser, data);
if (r) {
amdgpu_cs_parser_fini(&parser, r, reserved_buffers);
DRM_ERROR("Failed to initialize parser !\n");
amdgpu_cs_parser_fini(parser, r, false);
up_read(&adev->exclusive_lock);
r = amdgpu_cs_handle_lockup(adev, r);
return r;
}
for (i = 0; i < parser.num_ibs; i++)
trace_amdgpu_cs(&parser, i);
if (amdgpu_enable_scheduler && parser->num_ibs) {
struct amdgpu_ring * ring =
amdgpu_cs_parser_get_ring(adev, parser);
r = amdgpu_cs_parser_prepare_job(parser);
if (r)
goto out;
parser->ring = ring;
parser->free_job = amdgpu_cs_parser_free_job;
mutex_lock(&parser->job_lock);
r = amd_sched_push_job(ring->scheduler,
&parser->ctx->rings[ring->idx].entity,
parser,
&parser->s_fence);
if (r) {
mutex_unlock(&parser->job_lock);
goto out;
}
parser->ibs[parser->num_ibs - 1].sequence =
amdgpu_ctx_add_fence(parser->ctx, ring,
&parser->s_fence->base,
parser->s_fence->v_seq);
cs->out.handle = parser->s_fence->v_seq;
list_sort(NULL, &parser->validated, cmp_size_smaller_first);
ttm_eu_fence_buffer_objects(&parser->ticket,
&parser->validated,
&parser->s_fence->base);
r = amdgpu_cs_ib_vm_chunk(adev, &parser);
if (r) {
goto out;
mutex_unlock(&parser->job_lock);
up_read(&adev->exclusive_lock);
return 0;
}
r = amdgpu_cs_parser_prepare_job(parser);
if (r)
goto out;
cs->out.handle = parser.ibs[parser.num_ibs - 1].fence->seq;
cs->out.handle = parser->ibs[parser->num_ibs - 1].sequence;
out:
amdgpu_cs_parser_fini(&parser, r, true);
amdgpu_cs_parser_fini(parser, r, true);
up_read(&adev->exclusive_lock);
r = amdgpu_cs_handle_lockup(adev, r);
return r;
@ -806,30 +957,29 @@ int amdgpu_cs_wait_ioctl(struct drm_device *dev, void *data,
union drm_amdgpu_wait_cs *wait = data;
struct amdgpu_device *adev = dev->dev_private;
unsigned long timeout = amdgpu_gem_timeout(wait->in.timeout);
struct amdgpu_fence *fence = NULL;
struct amdgpu_ring *ring = NULL;
struct amdgpu_ctx *ctx;
struct fence *fence;
long r;
r = amdgpu_cs_get_ring(adev, wait->in.ip_type, wait->in.ip_instance,
wait->in.ring, &ring);
if (r)
return r;
ctx = amdgpu_ctx_get(filp->driver_priv, wait->in.ctx_id);
if (ctx == NULL)
return -EINVAL;
r = amdgpu_cs_get_ring(adev, wait->in.ip_type, wait->in.ip_instance,
wait->in.ring, &ring);
if (r) {
amdgpu_ctx_put(ctx);
return r;
}
fence = amdgpu_ctx_get_fence(ctx, ring, wait->in.handle);
if (IS_ERR(fence))
r = PTR_ERR(fence);
else if (fence) {
r = fence_wait_timeout(fence, true, timeout);
fence_put(fence);
} else
r = 1;
r = amdgpu_fence_recreate(ring, filp, wait->in.handle, &fence);
if (r) {
amdgpu_ctx_put(ctx);
return r;
}
r = fence_wait_timeout(&fence->base, true, timeout);
amdgpu_fence_unref(&fence);
amdgpu_ctx_put(ctx);
if (r < 0)
return r;
@ -864,7 +1014,16 @@ amdgpu_cs_find_mapping(struct amdgpu_cs_parser *parser,
if (!reloc->bo_va)
continue;
list_for_each_entry(mapping, &reloc->bo_va->mappings, list) {
list_for_each_entry(mapping, &reloc->bo_va->valids, list) {
if (mapping->it.start > addr ||
addr > mapping->it.last)
continue;
*bo = reloc->bo_va->bo;
return mapping;
}
list_for_each_entry(mapping, &reloc->bo_va->invalids, list) {
if (mapping->it.start > addr ||
addr > mapping->it.last)
continue;

View File

@ -25,54 +25,107 @@
#include <drm/drmP.h>
#include "amdgpu.h"
static void amdgpu_ctx_do_release(struct kref *ref)
int amdgpu_ctx_init(struct amdgpu_device *adev, bool kernel,
struct amdgpu_ctx *ctx)
{
struct amdgpu_ctx *ctx;
struct amdgpu_ctx_mgr *mgr;
unsigned i, j;
int r;
ctx = container_of(ref, struct amdgpu_ctx, refcount);
mgr = &ctx->fpriv->ctx_mgr;
memset(ctx, 0, sizeof(*ctx));
ctx->adev = adev;
kref_init(&ctx->refcount);
spin_lock_init(&ctx->ring_lock);
for (i = 0; i < AMDGPU_MAX_RINGS; ++i)
ctx->rings[i].sequence = 1;
idr_remove(&mgr->ctx_handles, ctx->id);
kfree(ctx);
if (amdgpu_enable_scheduler) {
/* create context entity for each ring */
for (i = 0; i < adev->num_rings; i++) {
struct amd_sched_rq *rq;
if (kernel)
rq = &adev->rings[i]->scheduler->kernel_rq;
else
rq = &adev->rings[i]->scheduler->sched_rq;
r = amd_sched_entity_init(adev->rings[i]->scheduler,
&ctx->rings[i].entity,
rq, amdgpu_sched_jobs);
if (r)
break;
}
if (i < adev->num_rings) {
for (j = 0; j < i; j++)
amd_sched_entity_fini(adev->rings[j]->scheduler,
&ctx->rings[j].entity);
kfree(ctx);
return r;
}
}
return 0;
}
int amdgpu_ctx_alloc(struct amdgpu_device *adev, struct amdgpu_fpriv *fpriv, uint32_t *id, uint32_t flags)
void amdgpu_ctx_fini(struct amdgpu_ctx *ctx)
{
struct amdgpu_device *adev = ctx->adev;
unsigned i, j;
for (i = 0; i < AMDGPU_MAX_RINGS; ++i)
for (j = 0; j < AMDGPU_CTX_MAX_CS_PENDING; ++j)
fence_put(ctx->rings[i].fences[j]);
if (amdgpu_enable_scheduler) {
for (i = 0; i < adev->num_rings; i++)
amd_sched_entity_fini(adev->rings[i]->scheduler,
&ctx->rings[i].entity);
}
}
static int amdgpu_ctx_alloc(struct amdgpu_device *adev,
struct amdgpu_fpriv *fpriv,
uint32_t *id)
{
int r;
struct amdgpu_ctx *ctx;
struct amdgpu_ctx_mgr *mgr = &fpriv->ctx_mgr;
struct amdgpu_ctx *ctx;
int r;
ctx = kmalloc(sizeof(*ctx), GFP_KERNEL);
if (!ctx)
return -ENOMEM;
mutex_lock(&mgr->lock);
r = idr_alloc(&mgr->ctx_handles, ctx, 0, 0, GFP_KERNEL);
r = idr_alloc(&mgr->ctx_handles, ctx, 1, 0, GFP_KERNEL);
if (r < 0) {
mutex_unlock(&mgr->lock);
kfree(ctx);
return r;
}
*id = (uint32_t)r;
memset(ctx, 0, sizeof(*ctx));
ctx->id = *id;
ctx->fpriv = fpriv;
kref_init(&ctx->refcount);
r = amdgpu_ctx_init(adev, false, ctx);
mutex_unlock(&mgr->lock);
return 0;
return r;
}
int amdgpu_ctx_free(struct amdgpu_device *adev, struct amdgpu_fpriv *fpriv, uint32_t id)
static void amdgpu_ctx_do_release(struct kref *ref)
{
struct amdgpu_ctx *ctx;
ctx = container_of(ref, struct amdgpu_ctx, refcount);
amdgpu_ctx_fini(ctx);
kfree(ctx);
}
static int amdgpu_ctx_free(struct amdgpu_fpriv *fpriv, uint32_t id)
{
struct amdgpu_ctx_mgr *mgr = &fpriv->ctx_mgr;
struct amdgpu_ctx *ctx;
mutex_lock(&mgr->lock);
ctx = idr_find(&mgr->ctx_handles, id);
if (ctx) {
idr_remove(&mgr->ctx_handles, id);
kref_put(&ctx->refcount, amdgpu_ctx_do_release);
mutex_unlock(&mgr->lock);
return 0;
@ -86,9 +139,13 @@ static int amdgpu_ctx_query(struct amdgpu_device *adev,
union drm_amdgpu_ctx_out *out)
{
struct amdgpu_ctx *ctx;
struct amdgpu_ctx_mgr *mgr = &fpriv->ctx_mgr;
struct amdgpu_ctx_mgr *mgr;
unsigned reset_counter;
if (!fpriv)
return -EINVAL;
mgr = &fpriv->ctx_mgr;
mutex_lock(&mgr->lock);
ctx = idr_find(&mgr->ctx_handles, id);
if (!ctx) {
@ -97,8 +154,8 @@ static int amdgpu_ctx_query(struct amdgpu_device *adev,
}
/* TODO: these two are always zero */
out->state.flags = ctx->state.flags;
out->state.hangs = ctx->state.hangs;
out->state.flags = 0x0;
out->state.hangs = 0x0;
/* determine if a GPU reset has occured since the last call */
reset_counter = atomic_read(&adev->gpu_reset_counter);
@ -113,28 +170,11 @@ static int amdgpu_ctx_query(struct amdgpu_device *adev,
return 0;
}
void amdgpu_ctx_fini(struct amdgpu_fpriv *fpriv)
{
struct idr *idp;
struct amdgpu_ctx *ctx;
uint32_t id;
struct amdgpu_ctx_mgr *mgr = &fpriv->ctx_mgr;
idp = &mgr->ctx_handles;
idr_for_each_entry(idp,ctx,id) {
if (kref_put(&ctx->refcount, amdgpu_ctx_do_release) != 1)
DRM_ERROR("ctx (id=%ul) is still alive\n",ctx->id);
}
mutex_destroy(&mgr->lock);
}
int amdgpu_ctx_ioctl(struct drm_device *dev, void *data,
struct drm_file *filp)
{
int r;
uint32_t id;
uint32_t flags;
union drm_amdgpu_ctx *args = data;
struct amdgpu_device *adev = dev->dev_private;
@ -142,15 +182,14 @@ int amdgpu_ctx_ioctl(struct drm_device *dev, void *data,
r = 0;
id = args->in.ctx_id;
flags = args->in.flags;
switch (args->in.op) {
case AMDGPU_CTX_OP_ALLOC_CTX:
r = amdgpu_ctx_alloc(adev, fpriv, &id, flags);
r = amdgpu_ctx_alloc(adev, fpriv, &id);
args->out.alloc.ctx_id = id;
break;
case AMDGPU_CTX_OP_FREE_CTX:
r = amdgpu_ctx_free(adev, fpriv, id);
r = amdgpu_ctx_free(fpriv, id);
break;
case AMDGPU_CTX_OP_QUERY_STATE:
r = amdgpu_ctx_query(adev, fpriv, id, &args->out);
@ -165,7 +204,12 @@ int amdgpu_ctx_ioctl(struct drm_device *dev, void *data,
struct amdgpu_ctx *amdgpu_ctx_get(struct amdgpu_fpriv *fpriv, uint32_t id)
{
struct amdgpu_ctx *ctx;
struct amdgpu_ctx_mgr *mgr = &fpriv->ctx_mgr;
struct amdgpu_ctx_mgr *mgr;
if (!fpriv)
return NULL;
mgr = &fpriv->ctx_mgr;
mutex_lock(&mgr->lock);
ctx = idr_find(&mgr->ctx_handles, id);
@ -177,17 +221,96 @@ struct amdgpu_ctx *amdgpu_ctx_get(struct amdgpu_fpriv *fpriv, uint32_t id)
int amdgpu_ctx_put(struct amdgpu_ctx *ctx)
{
struct amdgpu_fpriv *fpriv;
struct amdgpu_ctx_mgr *mgr;
if (ctx == NULL)
return -EINVAL;
fpriv = ctx->fpriv;
mgr = &fpriv->ctx_mgr;
mutex_lock(&mgr->lock);
kref_put(&ctx->refcount, amdgpu_ctx_do_release);
mutex_unlock(&mgr->lock);
return 0;
}
uint64_t amdgpu_ctx_add_fence(struct amdgpu_ctx *ctx, struct amdgpu_ring *ring,
struct fence *fence, uint64_t queued_seq)
{
struct amdgpu_ctx_ring *cring = & ctx->rings[ring->idx];
uint64_t seq = 0;
unsigned idx = 0;
struct fence *other = NULL;
if (amdgpu_enable_scheduler)
seq = queued_seq;
else
seq = cring->sequence;
idx = seq % AMDGPU_CTX_MAX_CS_PENDING;
other = cring->fences[idx];
if (other) {
signed long r;
r = fence_wait_timeout(other, false, MAX_SCHEDULE_TIMEOUT);
if (r < 0)
DRM_ERROR("Error (%ld) waiting for fence!\n", r);
}
fence_get(fence);
spin_lock(&ctx->ring_lock);
cring->fences[idx] = fence;
if (!amdgpu_enable_scheduler)
cring->sequence++;
spin_unlock(&ctx->ring_lock);
fence_put(other);
return seq;
}
struct fence *amdgpu_ctx_get_fence(struct amdgpu_ctx *ctx,
struct amdgpu_ring *ring, uint64_t seq)
{
struct amdgpu_ctx_ring *cring = & ctx->rings[ring->idx];
struct fence *fence;
uint64_t queued_seq;
spin_lock(&ctx->ring_lock);
if (amdgpu_enable_scheduler)
queued_seq = amd_sched_next_queued_seq(&cring->entity);
else
queued_seq = cring->sequence;
if (seq >= queued_seq) {
spin_unlock(&ctx->ring_lock);
return ERR_PTR(-EINVAL);
}
if (seq + AMDGPU_CTX_MAX_CS_PENDING < queued_seq) {
spin_unlock(&ctx->ring_lock);
return NULL;
}
fence = fence_get(cring->fences[seq % AMDGPU_CTX_MAX_CS_PENDING]);
spin_unlock(&ctx->ring_lock);
return fence;
}
void amdgpu_ctx_mgr_init(struct amdgpu_ctx_mgr *mgr)
{
mutex_init(&mgr->lock);
idr_init(&mgr->ctx_handles);
}
void amdgpu_ctx_mgr_fini(struct amdgpu_ctx_mgr *mgr)
{
struct amdgpu_ctx *ctx;
struct idr *idp;
uint32_t id;
idp = &mgr->ctx_handles;
idr_for_each_entry(idp, ctx, id) {
if (kref_put(&ctx->refcount, amdgpu_ctx_do_release) != 1)
DRM_ERROR("ctx %p is still alive\n", ctx);
}
idr_destroy(&mgr->ctx_handles);
mutex_destroy(&mgr->lock);
}

View File

@ -55,6 +55,7 @@ static const char *amdgpu_asic_name[] = {
"MULLINS",
"TOPAZ",
"TONGA",
"FIJI",
"CARRIZO",
"LAST",
};
@ -63,7 +64,7 @@ bool amdgpu_device_is_px(struct drm_device *dev)
{
struct amdgpu_device *adev = dev->dev_private;
if (adev->flags & AMDGPU_IS_PX)
if (adev->flags & AMD_IS_PX)
return true;
return false;
}
@ -1160,6 +1161,7 @@ static int amdgpu_early_init(struct amdgpu_device *adev)
switch (adev->asic_type) {
case CHIP_TOPAZ:
case CHIP_TONGA:
case CHIP_FIJI:
case CHIP_CARRIZO:
if (adev->asic_type == CHIP_CARRIZO)
adev->family = AMDGPU_FAMILY_CZ;
@ -1377,7 +1379,7 @@ int amdgpu_device_init(struct amdgpu_device *adev,
adev->ddev = ddev;
adev->pdev = pdev;
adev->flags = flags;
adev->asic_type = flags & AMDGPU_ASIC_MASK;
adev->asic_type = flags & AMD_ASIC_MASK;
adev->is_atom_bios = false;
adev->usec_timeout = AMDGPU_MAX_USEC_TIMEOUT;
adev->mc.gtt_size = 512 * 1024 * 1024;
@ -1523,6 +1525,11 @@ int amdgpu_device_init(struct amdgpu_device *adev,
return r;
}
r = amdgpu_ctx_init(adev, true, &adev->kernel_ctx);
if (r) {
dev_err(adev->dev, "failed to create kernel context (%d).\n", r);
return r;
}
r = amdgpu_ib_ring_tests(adev);
if (r)
DRM_ERROR("ib ring test failed (%d).\n", r);
@ -1584,6 +1591,7 @@ void amdgpu_device_fini(struct amdgpu_device *adev)
adev->shutdown = true;
/* evict vram memory */
amdgpu_bo_evict_vram(adev);
amdgpu_ctx_fini(&adev->kernel_ctx);
amdgpu_ib_pool_fini(adev);
amdgpu_fence_driver_fini(adev);
amdgpu_fbdev_fini(adev);
@ -1627,8 +1635,7 @@ int amdgpu_suspend_kms(struct drm_device *dev, bool suspend, bool fbcon)
struct amdgpu_device *adev;
struct drm_crtc *crtc;
struct drm_connector *connector;
int i, r;
bool force_completion = false;
int r;
if (dev == NULL || dev->dev_private == NULL) {
return -ENODEV;
@ -1667,21 +1674,7 @@ int amdgpu_suspend_kms(struct drm_device *dev, bool suspend, bool fbcon)
/* evict vram memory */
amdgpu_bo_evict_vram(adev);
/* wait for gpu to finish processing current batch */
for (i = 0; i < AMDGPU_MAX_RINGS; i++) {
struct amdgpu_ring *ring = adev->rings[i];
if (!ring)
continue;
r = amdgpu_fence_wait_empty(ring);
if (r) {
/* delay GPU reset to resume */
force_completion = true;
}
}
if (force_completion) {
amdgpu_fence_driver_force_completion(adev);
}
amdgpu_fence_driver_suspend(adev);
r = amdgpu_suspend(adev);
@ -1739,6 +1732,8 @@ int amdgpu_resume_kms(struct drm_device *dev, bool resume, bool fbcon)
r = amdgpu_resume(adev);
amdgpu_fence_driver_resume(adev);
r = amdgpu_ib_ring_tests(adev);
if (r)
DRM_ERROR("ib ring test failed (%d).\n", r);

View File

@ -35,6 +35,36 @@
#include <drm/drm_crtc_helper.h>
#include <drm/drm_edid.h>
static void amdgpu_flip_wait_fence(struct amdgpu_device *adev,
struct fence **f)
{
struct amdgpu_fence *fence;
long r;
if (*f == NULL)
return;
fence = to_amdgpu_fence(*f);
if (fence) {
r = fence_wait(&fence->base, false);
if (r == -EDEADLK) {
up_read(&adev->exclusive_lock);
r = amdgpu_gpu_reset(adev);
down_read(&adev->exclusive_lock);
}
} else
r = fence_wait(*f, false);
if (r)
DRM_ERROR("failed to wait on page flip fence (%ld)!\n", r);
/* We continue with the page flip even if we failed to wait on
* the fence, otherwise the DRM core and userspace will be
* confused about which BO the CRTC is scanning out
*/
fence_put(*f);
*f = NULL;
}
static void amdgpu_flip_work_func(struct work_struct *__work)
{
@ -44,34 +74,13 @@ static void amdgpu_flip_work_func(struct work_struct *__work)
struct amdgpu_crtc *amdgpuCrtc = adev->mode_info.crtcs[work->crtc_id];
struct drm_crtc *crtc = &amdgpuCrtc->base;
struct amdgpu_fence *fence;
unsigned long flags;
int r;
unsigned i;
down_read(&adev->exclusive_lock);
if (work->fence) {
fence = to_amdgpu_fence(work->fence);
if (fence) {
r = amdgpu_fence_wait(fence, false);
if (r == -EDEADLK) {
up_read(&adev->exclusive_lock);
r = amdgpu_gpu_reset(adev);
down_read(&adev->exclusive_lock);
}
} else
r = fence_wait(work->fence, false);
if (r)
DRM_ERROR("failed to wait on page flip fence (%d)!\n", r);
/* We continue with the page flip even if we failed to wait on
* the fence, otherwise the DRM core and userspace will be
* confused about which BO the CRTC is scanning out
*/
fence_put(work->fence);
work->fence = NULL;
}
amdgpu_flip_wait_fence(adev, &work->excl);
for (i = 0; i < work->shared_count; ++i)
amdgpu_flip_wait_fence(adev, &work->shared[i]);
/* We borrow the event spin lock for protecting flip_status */
spin_lock_irqsave(&crtc->dev->event_lock, flags);
@ -108,6 +117,7 @@ static void amdgpu_unpin_work_func(struct work_struct *__work)
DRM_ERROR("failed to reserve buffer after flip\n");
drm_gem_object_unreference_unlocked(&work->old_rbo->gem_base);
kfree(work->shared);
kfree(work);
}
@ -127,7 +137,7 @@ int amdgpu_crtc_page_flip(struct drm_crtc *crtc,
unsigned long flags;
u64 tiling_flags;
u64 base;
int r;
int i, r;
work = kzalloc(sizeof *work, GFP_KERNEL);
if (work == NULL)
@ -167,7 +177,19 @@ int amdgpu_crtc_page_flip(struct drm_crtc *crtc,
goto cleanup;
}
work->fence = fence_get(reservation_object_get_excl(new_rbo->tbo.resv));
r = reservation_object_get_fences_rcu(new_rbo->tbo.resv, &work->excl,
&work->shared_count,
&work->shared);
if (unlikely(r != 0)) {
amdgpu_bo_unreserve(new_rbo);
DRM_ERROR("failed to get fences for buffer\n");
goto cleanup;
}
fence_get(work->excl);
for (i = 0; i < work->shared_count; ++i)
fence_get(work->shared[i]);
amdgpu_bo_get_tiling_flags(new_rbo, &tiling_flags);
amdgpu_bo_unreserve(new_rbo);
@ -212,7 +234,10 @@ pflip_cleanup:
cleanup:
drm_gem_object_unreference_unlocked(&work->old_rbo->gem_base);
fence_put(work->fence);
fence_put(work->excl);
for (i = 0; i < work->shared_count; ++i)
fence_put(work->shared[i]);
kfree(work->shared);
kfree(work);
return r;

View File

@ -63,7 +63,7 @@ int amdgpu_disp_priority = 0;
int amdgpu_hw_i2c = 0;
int amdgpu_pcie_gen2 = -1;
int amdgpu_msi = -1;
int amdgpu_lockup_timeout = 10000;
int amdgpu_lockup_timeout = 0;
int amdgpu_dpm = -1;
int amdgpu_smc_load_fw = 1;
int amdgpu_aspm = -1;
@ -75,6 +75,9 @@ int amdgpu_deep_color = 0;
int amdgpu_vm_size = 8;
int amdgpu_vm_block_size = -1;
int amdgpu_exp_hw_support = 0;
int amdgpu_enable_scheduler = 0;
int amdgpu_sched_jobs = 16;
int amdgpu_sched_hw_submission = 2;
MODULE_PARM_DESC(vramlimit, "Restrict VRAM for testing, in megabytes");
module_param_named(vramlimit, amdgpu_vram_limit, int, 0600);
@ -103,7 +106,7 @@ module_param_named(pcie_gen2, amdgpu_pcie_gen2, int, 0444);
MODULE_PARM_DESC(msi, "MSI support (1 = enable, 0 = disable, -1 = auto)");
module_param_named(msi, amdgpu_msi, int, 0444);
MODULE_PARM_DESC(lockup_timeout, "GPU lockup timeout in ms (defaul 10000 = 10 seconds, 0 = disable)");
MODULE_PARM_DESC(lockup_timeout, "GPU lockup timeout in ms (default 0 = disable)");
module_param_named(lockup_timeout, amdgpu_lockup_timeout, int, 0444);
MODULE_PARM_DESC(dpm, "DPM support (1 = enable, 0 = disable, -1 = auto)");
@ -139,36 +142,45 @@ module_param_named(vm_block_size, amdgpu_vm_block_size, int, 0444);
MODULE_PARM_DESC(exp_hw_support, "experimental hw support (1 = enable, 0 = disable (default))");
module_param_named(exp_hw_support, amdgpu_exp_hw_support, int, 0444);
MODULE_PARM_DESC(enable_scheduler, "enable SW GPU scheduler (1 = enable, 0 = disable ((default))");
module_param_named(enable_scheduler, amdgpu_enable_scheduler, int, 0444);
MODULE_PARM_DESC(sched_jobs, "the max number of jobs supported in the sw queue (default 16)");
module_param_named(sched_jobs, amdgpu_sched_jobs, int, 0444);
MODULE_PARM_DESC(sched_hw_submission, "the max number of HW submissions (default 2)");
module_param_named(sched_hw_submission, amdgpu_sched_hw_submission, int, 0444);
static struct pci_device_id pciidlist[] = {
#ifdef CONFIG_DRM_AMDGPU_CIK
/* Kaveri */
{0x1002, 0x1304, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KAVERI|AMDGPU_IS_MOBILITY|AMDGPU_IS_APU},
{0x1002, 0x1305, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KAVERI|AMDGPU_IS_APU},
{0x1002, 0x1306, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KAVERI|AMDGPU_IS_MOBILITY|AMDGPU_IS_APU},
{0x1002, 0x1307, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KAVERI|AMDGPU_IS_APU},
{0x1002, 0x1309, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KAVERI|AMDGPU_IS_MOBILITY|AMDGPU_IS_APU},
{0x1002, 0x130A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KAVERI|AMDGPU_IS_MOBILITY|AMDGPU_IS_APU},
{0x1002, 0x130B, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KAVERI|AMDGPU_IS_MOBILITY|AMDGPU_IS_APU},
{0x1002, 0x130C, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KAVERI|AMDGPU_IS_MOBILITY|AMDGPU_IS_APU},
{0x1002, 0x130D, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KAVERI|AMDGPU_IS_MOBILITY|AMDGPU_IS_APU},
{0x1002, 0x130E, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KAVERI|AMDGPU_IS_MOBILITY|AMDGPU_IS_APU},
{0x1002, 0x130F, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KAVERI|AMDGPU_IS_APU},
{0x1002, 0x1310, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KAVERI|AMDGPU_IS_APU},
{0x1002, 0x1311, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KAVERI|AMDGPU_IS_APU},
{0x1002, 0x1312, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KAVERI|AMDGPU_IS_APU},
{0x1002, 0x1313, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KAVERI|AMDGPU_IS_APU},
{0x1002, 0x1315, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KAVERI|AMDGPU_IS_APU},
{0x1002, 0x1316, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KAVERI|AMDGPU_IS_APU},
{0x1002, 0x1317, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KAVERI|AMDGPU_IS_MOBILITY|AMDGPU_IS_APU},
{0x1002, 0x1318, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KAVERI|AMDGPU_IS_MOBILITY|AMDGPU_IS_APU},
{0x1002, 0x131B, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KAVERI|AMDGPU_IS_APU},
{0x1002, 0x131C, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KAVERI|AMDGPU_IS_APU},
{0x1002, 0x131D, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KAVERI|AMDGPU_IS_APU},
{0x1002, 0x1304, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KAVERI|AMD_IS_MOBILITY|AMD_IS_APU},
{0x1002, 0x1305, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KAVERI|AMD_IS_APU},
{0x1002, 0x1306, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KAVERI|AMD_IS_MOBILITY|AMD_IS_APU},
{0x1002, 0x1307, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KAVERI|AMD_IS_APU},
{0x1002, 0x1309, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KAVERI|AMD_IS_MOBILITY|AMD_IS_APU},
{0x1002, 0x130A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KAVERI|AMD_IS_MOBILITY|AMD_IS_APU},
{0x1002, 0x130B, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KAVERI|AMD_IS_MOBILITY|AMD_IS_APU},
{0x1002, 0x130C, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KAVERI|AMD_IS_MOBILITY|AMD_IS_APU},
{0x1002, 0x130D, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KAVERI|AMD_IS_MOBILITY|AMD_IS_APU},
{0x1002, 0x130E, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KAVERI|AMD_IS_MOBILITY|AMD_IS_APU},
{0x1002, 0x130F, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KAVERI|AMD_IS_APU},
{0x1002, 0x1310, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KAVERI|AMD_IS_APU},
{0x1002, 0x1311, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KAVERI|AMD_IS_APU},
{0x1002, 0x1312, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KAVERI|AMD_IS_APU},
{0x1002, 0x1313, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KAVERI|AMD_IS_APU},
{0x1002, 0x1315, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KAVERI|AMD_IS_APU},
{0x1002, 0x1316, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KAVERI|AMD_IS_APU},
{0x1002, 0x1317, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KAVERI|AMD_IS_MOBILITY|AMD_IS_APU},
{0x1002, 0x1318, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KAVERI|AMD_IS_MOBILITY|AMD_IS_APU},
{0x1002, 0x131B, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KAVERI|AMD_IS_APU},
{0x1002, 0x131C, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KAVERI|AMD_IS_APU},
{0x1002, 0x131D, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KAVERI|AMD_IS_APU},
/* Bonaire */
{0x1002, 0x6640, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_BONAIRE|AMDGPU_IS_MOBILITY},
{0x1002, 0x6641, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_BONAIRE|AMDGPU_IS_MOBILITY},
{0x1002, 0x6646, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_BONAIRE|AMDGPU_IS_MOBILITY},
{0x1002, 0x6647, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_BONAIRE|AMDGPU_IS_MOBILITY},
{0x1002, 0x6640, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_BONAIRE|AMD_IS_MOBILITY},
{0x1002, 0x6641, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_BONAIRE|AMD_IS_MOBILITY},
{0x1002, 0x6646, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_BONAIRE|AMD_IS_MOBILITY},
{0x1002, 0x6647, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_BONAIRE|AMD_IS_MOBILITY},
{0x1002, 0x6649, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_BONAIRE},
{0x1002, 0x6650, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_BONAIRE},
{0x1002, 0x6651, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_BONAIRE},
@ -190,39 +202,39 @@ static struct pci_device_id pciidlist[] = {
{0x1002, 0x67BA, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_HAWAII},
{0x1002, 0x67BE, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_HAWAII},
/* Kabini */
{0x1002, 0x9830, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KABINI|AMDGPU_IS_MOBILITY|AMDGPU_IS_APU},
{0x1002, 0x9831, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KABINI|AMDGPU_IS_APU},
{0x1002, 0x9832, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KABINI|AMDGPU_IS_MOBILITY|AMDGPU_IS_APU},
{0x1002, 0x9833, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KABINI|AMDGPU_IS_APU},
{0x1002, 0x9834, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KABINI|AMDGPU_IS_MOBILITY|AMDGPU_IS_APU},
{0x1002, 0x9835, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KABINI|AMDGPU_IS_APU},
{0x1002, 0x9836, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KABINI|AMDGPU_IS_MOBILITY|AMDGPU_IS_APU},
{0x1002, 0x9837, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KABINI|AMDGPU_IS_APU},
{0x1002, 0x9838, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KABINI|AMDGPU_IS_MOBILITY|AMDGPU_IS_APU},
{0x1002, 0x9839, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KABINI|AMDGPU_IS_MOBILITY|AMDGPU_IS_APU},
{0x1002, 0x983a, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KABINI|AMDGPU_IS_APU},
{0x1002, 0x983b, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KABINI|AMDGPU_IS_MOBILITY|AMDGPU_IS_APU},
{0x1002, 0x983c, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KABINI|AMDGPU_IS_APU},
{0x1002, 0x983d, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KABINI|AMDGPU_IS_APU},
{0x1002, 0x983e, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KABINI|AMDGPU_IS_APU},
{0x1002, 0x983f, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KABINI|AMDGPU_IS_APU},
{0x1002, 0x9830, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KABINI|AMD_IS_MOBILITY|AMD_IS_APU},
{0x1002, 0x9831, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KABINI|AMD_IS_APU},
{0x1002, 0x9832, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KABINI|AMD_IS_MOBILITY|AMD_IS_APU},
{0x1002, 0x9833, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KABINI|AMD_IS_APU},
{0x1002, 0x9834, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KABINI|AMD_IS_MOBILITY|AMD_IS_APU},
{0x1002, 0x9835, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KABINI|AMD_IS_APU},
{0x1002, 0x9836, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KABINI|AMD_IS_MOBILITY|AMD_IS_APU},
{0x1002, 0x9837, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KABINI|AMD_IS_APU},
{0x1002, 0x9838, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KABINI|AMD_IS_MOBILITY|AMD_IS_APU},
{0x1002, 0x9839, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KABINI|AMD_IS_MOBILITY|AMD_IS_APU},
{0x1002, 0x983a, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KABINI|AMD_IS_APU},
{0x1002, 0x983b, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KABINI|AMD_IS_MOBILITY|AMD_IS_APU},
{0x1002, 0x983c, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KABINI|AMD_IS_APU},
{0x1002, 0x983d, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KABINI|AMD_IS_APU},
{0x1002, 0x983e, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KABINI|AMD_IS_APU},
{0x1002, 0x983f, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_KABINI|AMD_IS_APU},
/* mullins */
{0x1002, 0x9850, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_MULLINS|AMDGPU_IS_MOBILITY|AMDGPU_IS_APU},
{0x1002, 0x9851, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_MULLINS|AMDGPU_IS_MOBILITY|AMDGPU_IS_APU},
{0x1002, 0x9852, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_MULLINS|AMDGPU_IS_MOBILITY|AMDGPU_IS_APU},
{0x1002, 0x9853, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_MULLINS|AMDGPU_IS_MOBILITY|AMDGPU_IS_APU},
{0x1002, 0x9854, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_MULLINS|AMDGPU_IS_MOBILITY|AMDGPU_IS_APU},
{0x1002, 0x9855, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_MULLINS|AMDGPU_IS_MOBILITY|AMDGPU_IS_APU},
{0x1002, 0x9856, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_MULLINS|AMDGPU_IS_MOBILITY|AMDGPU_IS_APU},
{0x1002, 0x9857, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_MULLINS|AMDGPU_IS_MOBILITY|AMDGPU_IS_APU},
{0x1002, 0x9858, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_MULLINS|AMDGPU_IS_MOBILITY|AMDGPU_IS_APU},
{0x1002, 0x9859, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_MULLINS|AMDGPU_IS_MOBILITY|AMDGPU_IS_APU},
{0x1002, 0x985A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_MULLINS|AMDGPU_IS_MOBILITY|AMDGPU_IS_APU},
{0x1002, 0x985B, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_MULLINS|AMDGPU_IS_MOBILITY|AMDGPU_IS_APU},
{0x1002, 0x985C, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_MULLINS|AMDGPU_IS_MOBILITY|AMDGPU_IS_APU},
{0x1002, 0x985D, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_MULLINS|AMDGPU_IS_MOBILITY|AMDGPU_IS_APU},
{0x1002, 0x985E, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_MULLINS|AMDGPU_IS_MOBILITY|AMDGPU_IS_APU},
{0x1002, 0x985F, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_MULLINS|AMDGPU_IS_MOBILITY|AMDGPU_IS_APU},
{0x1002, 0x9850, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_MULLINS|AMD_IS_MOBILITY|AMD_IS_APU},
{0x1002, 0x9851, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_MULLINS|AMD_IS_MOBILITY|AMD_IS_APU},
{0x1002, 0x9852, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_MULLINS|AMD_IS_MOBILITY|AMD_IS_APU},
{0x1002, 0x9853, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_MULLINS|AMD_IS_MOBILITY|AMD_IS_APU},
{0x1002, 0x9854, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_MULLINS|AMD_IS_MOBILITY|AMD_IS_APU},
{0x1002, 0x9855, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_MULLINS|AMD_IS_MOBILITY|AMD_IS_APU},
{0x1002, 0x9856, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_MULLINS|AMD_IS_MOBILITY|AMD_IS_APU},
{0x1002, 0x9857, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_MULLINS|AMD_IS_MOBILITY|AMD_IS_APU},
{0x1002, 0x9858, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_MULLINS|AMD_IS_MOBILITY|AMD_IS_APU},
{0x1002, 0x9859, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_MULLINS|AMD_IS_MOBILITY|AMD_IS_APU},
{0x1002, 0x985A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_MULLINS|AMD_IS_MOBILITY|AMD_IS_APU},
{0x1002, 0x985B, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_MULLINS|AMD_IS_MOBILITY|AMD_IS_APU},
{0x1002, 0x985C, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_MULLINS|AMD_IS_MOBILITY|AMD_IS_APU},
{0x1002, 0x985D, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_MULLINS|AMD_IS_MOBILITY|AMD_IS_APU},
{0x1002, 0x985E, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_MULLINS|AMD_IS_MOBILITY|AMD_IS_APU},
{0x1002, 0x985F, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_MULLINS|AMD_IS_MOBILITY|AMD_IS_APU},
#endif
/* topaz */
{0x1002, 0x6900, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_TOPAZ},
@ -240,12 +252,14 @@ static struct pci_device_id pciidlist[] = {
{0x1002, 0x6930, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_TONGA},
{0x1002, 0x6938, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_TONGA},
{0x1002, 0x6939, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_TONGA},
/* fiji */
{0x1002, 0x7300, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_FIJI},
/* carrizo */
{0x1002, 0x9870, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CARRIZO|AMDGPU_IS_APU},
{0x1002, 0x9874, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CARRIZO|AMDGPU_IS_APU},
{0x1002, 0x9875, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CARRIZO|AMDGPU_IS_APU},
{0x1002, 0x9876, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CARRIZO|AMDGPU_IS_APU},
{0x1002, 0x9877, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CARRIZO|AMDGPU_IS_APU},
{0x1002, 0x9870, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CARRIZO|AMD_IS_APU},
{0x1002, 0x9874, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CARRIZO|AMD_IS_APU},
{0x1002, 0x9875, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CARRIZO|AMD_IS_APU},
{0x1002, 0x9876, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CARRIZO|AMD_IS_APU},
{0x1002, 0x9877, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_CARRIZO|AMD_IS_APU},
{0, 0, 0}
};
@ -281,7 +295,7 @@ static int amdgpu_pci_probe(struct pci_dev *pdev,
unsigned long flags = ent->driver_data;
int ret;
if ((flags & AMDGPU_EXP_HW_SUPPORT) && !amdgpu_exp_hw_support) {
if ((flags & AMD_EXP_HW_SUPPORT) && !amdgpu_exp_hw_support) {
DRM_INFO("This hardware requires experimental hardware support.\n"
"See modparam exp_hw_support\n");
return -ENODEV;

View File

@ -31,7 +31,7 @@
#include <linux/firmware.h>
#include <linux/platform_device.h>
#include "amdgpu_family.h"
#include "amd_shared.h"
/* General customization:
*/

View File

@ -126,7 +126,8 @@ int amdgpu_fence_emit(struct amdgpu_ring *ring, void *owner,
(*fence)->ring = ring;
(*fence)->owner = owner;
fence_init(&(*fence)->base, &amdgpu_fence_ops,
&adev->fence_queue.lock, adev->fence_context + ring->idx,
&ring->fence_drv.fence_queue.lock,
adev->fence_context + ring->idx,
(*fence)->seq);
amdgpu_ring_emit_fence(ring, ring->fence_drv.gpu_addr,
(*fence)->seq,
@ -135,38 +136,6 @@ int amdgpu_fence_emit(struct amdgpu_ring *ring, void *owner,
return 0;
}
/**
* amdgpu_fence_recreate - recreate a fence from an user fence
*
* @ring: ring the fence is associated with
* @owner: creator of the fence
* @seq: user fence sequence number
* @fence: resulting amdgpu fence object
*
* Recreates a fence command from the user fence sequence number (all asics).
* Returns 0 on success, -ENOMEM on failure.
*/
int amdgpu_fence_recreate(struct amdgpu_ring *ring, void *owner,
uint64_t seq, struct amdgpu_fence **fence)
{
struct amdgpu_device *adev = ring->adev;
if (seq > ring->fence_drv.sync_seq[ring->idx])
return -EINVAL;
*fence = kmalloc(sizeof(struct amdgpu_fence), GFP_KERNEL);
if ((*fence) == NULL)
return -ENOMEM;
(*fence)->seq = seq;
(*fence)->ring = ring;
(*fence)->owner = owner;
fence_init(&(*fence)->base, &amdgpu_fence_ops,
&adev->fence_queue.lock, adev->fence_context + ring->idx,
(*fence)->seq);
return 0;
}
/**
* amdgpu_fence_check_signaled - callback from fence_queue
*
@ -196,9 +165,7 @@ static int amdgpu_fence_check_signaled(wait_queue_t *wait, unsigned mode, int fl
else
FENCE_TRACE(&fence->base, "was already signaled\n");
amdgpu_irq_put(adev, fence->ring->fence_drv.irq_src,
fence->ring->fence_drv.irq_type);
__remove_wait_queue(&adev->fence_queue, &fence->fence_wake);
__remove_wait_queue(&fence->ring->fence_drv.fence_queue, &fence->fence_wake);
fence_put(&fence->base);
} else
FENCE_TRACE(&fence->base, "pending\n");
@ -299,14 +266,9 @@ static void amdgpu_fence_check_lockup(struct work_struct *work)
return;
}
if (fence_drv->delayed_irq && ring->adev->ddev->irq_enabled) {
fence_drv->delayed_irq = false;
amdgpu_irq_update(ring->adev, fence_drv->irq_src,
fence_drv->irq_type);
if (amdgpu_fence_activity(ring)) {
wake_up_all(&ring->fence_drv.fence_queue);
}
if (amdgpu_fence_activity(ring))
wake_up_all(&ring->adev->fence_queue);
else if (amdgpu_ring_is_lockup(ring)) {
/* good news we believe it's a lockup */
dev_warn(ring->adev->dev, "GPU lockup (current fence id "
@ -316,7 +278,7 @@ static void amdgpu_fence_check_lockup(struct work_struct *work)
/* remember that we need an reset */
ring->adev->needs_reset = true;
wake_up_all(&ring->adev->fence_queue);
wake_up_all(&ring->fence_drv.fence_queue);
}
up_read(&ring->adev->exclusive_lock);
}
@ -332,62 +294,8 @@ static void amdgpu_fence_check_lockup(struct work_struct *work)
*/
void amdgpu_fence_process(struct amdgpu_ring *ring)
{
uint64_t seq, last_seq, last_emitted;
unsigned count_loop = 0;
bool wake = false;
/* Note there is a scenario here for an infinite loop but it's
* very unlikely to happen. For it to happen, the current polling
* process need to be interrupted by another process and another
* process needs to update the last_seq btw the atomic read and
* xchg of the current process.
*
* More over for this to go in infinite loop there need to be
* continuously new fence signaled ie amdgpu_fence_read needs
* to return a different value each time for both the currently
* polling process and the other process that xchg the last_seq
* btw atomic read and xchg of the current process. And the
* value the other process set as last seq must be higher than
* the seq value we just read. Which means that current process
* need to be interrupted after amdgpu_fence_read and before
* atomic xchg.
*
* To be even more safe we count the number of time we loop and
* we bail after 10 loop just accepting the fact that we might
* have temporarly set the last_seq not to the true real last
* seq but to an older one.
*/
last_seq = atomic64_read(&ring->fence_drv.last_seq);
do {
last_emitted = ring->fence_drv.sync_seq[ring->idx];
seq = amdgpu_fence_read(ring);
seq |= last_seq & 0xffffffff00000000LL;
if (seq < last_seq) {
seq &= 0xffffffff;
seq |= last_emitted & 0xffffffff00000000LL;
}
if (seq <= last_seq || seq > last_emitted) {
break;
}
/* If we loop over we don't want to return without
* checking if a fence is signaled as it means that the
* seq we just read is different from the previous on.
*/
wake = true;
last_seq = seq;
if ((count_loop++) > 10) {
/* We looped over too many time leave with the
* fact that we might have set an older fence
* seq then the current real last seq as signaled
* by the hw.
*/
break;
}
} while (atomic64_xchg(&ring->fence_drv.last_seq, seq) > seq);
if (wake)
wake_up_all(&ring->adev->fence_queue);
if (amdgpu_fence_activity(ring))
wake_up_all(&ring->fence_drv.fence_queue);
}
/**
@ -447,284 +355,49 @@ static bool amdgpu_fence_enable_signaling(struct fence *f)
{
struct amdgpu_fence *fence = to_amdgpu_fence(f);
struct amdgpu_ring *ring = fence->ring;
struct amdgpu_device *adev = ring->adev;
if (atomic64_read(&ring->fence_drv.last_seq) >= fence->seq)
return false;
if (down_read_trylock(&adev->exclusive_lock)) {
amdgpu_irq_get(adev, ring->fence_drv.irq_src,
ring->fence_drv.irq_type);
if (amdgpu_fence_activity(ring))
wake_up_all_locked(&adev->fence_queue);
/* did fence get signaled after we enabled the sw irq? */
if (atomic64_read(&ring->fence_drv.last_seq) >= fence->seq) {
amdgpu_irq_put(adev, ring->fence_drv.irq_src,
ring->fence_drv.irq_type);
up_read(&adev->exclusive_lock);
return false;
}
up_read(&adev->exclusive_lock);
} else {
/* we're probably in a lockup, lets not fiddle too much */
if (amdgpu_irq_get_delayed(adev, ring->fence_drv.irq_src,
ring->fence_drv.irq_type))
ring->fence_drv.delayed_irq = true;
amdgpu_fence_schedule_check(ring);
}
fence->fence_wake.flags = 0;
fence->fence_wake.private = NULL;
fence->fence_wake.func = amdgpu_fence_check_signaled;
__add_wait_queue(&adev->fence_queue, &fence->fence_wake);
__add_wait_queue(&ring->fence_drv.fence_queue, &fence->fence_wake);
fence_get(f);
FENCE_TRACE(&fence->base, "armed on ring %i!\n", ring->idx);
return true;
}
/**
* amdgpu_fence_signaled - check if a fence has signaled
/*
* amdgpu_ring_wait_seq_timeout - wait for seq of the specific ring to signal
* @ring: ring to wait on for the seq number
* @seq: seq number wait for
*
* @fence: amdgpu fence object
*
* Check if the requested fence has signaled (all asics).
* Returns true if the fence has signaled or false if it has not.
* return value:
* 0: seq signaled, and gpu not hang
* -EDEADL: GPU hang detected
* -EINVAL: some paramter is not valid
*/
bool amdgpu_fence_signaled(struct amdgpu_fence *fence)
static int amdgpu_fence_ring_wait_seq(struct amdgpu_ring *ring, uint64_t seq)
{
if (!fence)
return true;
struct amdgpu_device *adev = ring->adev;
bool signaled = false;
if (amdgpu_fence_seq_signaled(fence->ring, fence->seq)) {
if (!fence_signal(&fence->base))
FENCE_TRACE(&fence->base, "signaled from amdgpu_fence_signaled\n");
return true;
}
BUG_ON(!ring);
if (seq > ring->fence_drv.sync_seq[ring->idx])
return -EINVAL;
return false;
}
if (atomic64_read(&ring->fence_drv.last_seq) >= seq)
return 0;
/**
* amdgpu_fence_any_seq_signaled - check if any sequence number is signaled
*
* @adev: amdgpu device pointer
* @seq: sequence numbers
*
* Check if the last signaled fence sequnce number is >= the requested
* sequence number (all asics).
* Returns true if any has signaled (current value is >= requested value)
* or false if it has not. Helper function for amdgpu_fence_wait_seq.
*/
static bool amdgpu_fence_any_seq_signaled(struct amdgpu_device *adev, u64 *seq)
{
unsigned i;
wait_event(ring->fence_drv.fence_queue, (
(signaled = amdgpu_fence_seq_signaled(ring, seq))
|| adev->needs_reset));
for (i = 0; i < AMDGPU_MAX_RINGS; ++i) {
if (!adev->rings[i] || !seq[i])
continue;
if (amdgpu_fence_seq_signaled(adev->rings[i], seq[i]))
return true;
}
return false;
}
/**
* amdgpu_fence_wait_seq_timeout - wait for a specific sequence numbers
*
* @adev: amdgpu device pointer
* @target_seq: sequence number(s) we want to wait for
* @intr: use interruptable sleep
* @timeout: maximum time to wait, or MAX_SCHEDULE_TIMEOUT for infinite wait
*
* Wait for the requested sequence number(s) to be written by any ring
* (all asics). Sequnce number array is indexed by ring id.
* @intr selects whether to use interruptable (true) or non-interruptable
* (false) sleep when waiting for the sequence number. Helper function
* for amdgpu_fence_wait_*().
* Returns remaining time if the sequence number has passed, 0 when
* the wait timeout, or an error for all other cases.
* -EDEADLK is returned when a GPU lockup has been detected.
*/
static long amdgpu_fence_wait_seq_timeout(struct amdgpu_device *adev,
u64 *target_seq, bool intr,
long timeout)
{
uint64_t last_seq[AMDGPU_MAX_RINGS];
bool signaled;
int i;
long r;
if (timeout == 0) {
return amdgpu_fence_any_seq_signaled(adev, target_seq);
}
while (!amdgpu_fence_any_seq_signaled(adev, target_seq)) {
/* Save current sequence values, used to check for GPU lockups */
for (i = 0; i < AMDGPU_MAX_RINGS; ++i) {
struct amdgpu_ring *ring = adev->rings[i];
if (!ring || !target_seq[i])
continue;
last_seq[i] = atomic64_read(&ring->fence_drv.last_seq);
trace_amdgpu_fence_wait_begin(adev->ddev, i, target_seq[i]);
amdgpu_irq_get(adev, ring->fence_drv.irq_src,
ring->fence_drv.irq_type);
}
if (intr) {
r = wait_event_interruptible_timeout(adev->fence_queue, (
(signaled = amdgpu_fence_any_seq_signaled(adev, target_seq))
|| adev->needs_reset), AMDGPU_FENCE_JIFFIES_TIMEOUT);
} else {
r = wait_event_timeout(adev->fence_queue, (
(signaled = amdgpu_fence_any_seq_signaled(adev, target_seq))
|| adev->needs_reset), AMDGPU_FENCE_JIFFIES_TIMEOUT);
}
for (i = 0; i < AMDGPU_MAX_RINGS; ++i) {
struct amdgpu_ring *ring = adev->rings[i];
if (!ring || !target_seq[i])
continue;
amdgpu_irq_put(adev, ring->fence_drv.irq_src,
ring->fence_drv.irq_type);
trace_amdgpu_fence_wait_end(adev->ddev, i, target_seq[i]);
}
if (unlikely(r < 0))
return r;
if (unlikely(!signaled)) {
if (adev->needs_reset)
return -EDEADLK;
/* we were interrupted for some reason and fence
* isn't signaled yet, resume waiting */
if (r)
continue;
for (i = 0; i < AMDGPU_MAX_RINGS; ++i) {
struct amdgpu_ring *ring = adev->rings[i];
if (!ring || !target_seq[i])
continue;
if (last_seq[i] != atomic64_read(&ring->fence_drv.last_seq))
break;
}
if (i != AMDGPU_MAX_RINGS)
continue;
for (i = 0; i < AMDGPU_MAX_RINGS; ++i) {
if (!adev->rings[i] || !target_seq[i])
continue;
if (amdgpu_ring_is_lockup(adev->rings[i]))
break;
}
if (i < AMDGPU_MAX_RINGS) {
/* good news we believe it's a lockup */
dev_warn(adev->dev, "GPU lockup (waiting for "
"0x%016llx last fence id 0x%016llx on"
" ring %d)\n",
target_seq[i], last_seq[i], i);
/* remember that we need an reset */
adev->needs_reset = true;
wake_up_all(&adev->fence_queue);
return -EDEADLK;
}
if (timeout < MAX_SCHEDULE_TIMEOUT) {
timeout -= AMDGPU_FENCE_JIFFIES_TIMEOUT;
if (timeout <= 0) {
return 0;
}
}
}
}
return timeout;
}
/**
* amdgpu_fence_wait - wait for a fence to signal
*
* @fence: amdgpu fence object
* @intr: use interruptable sleep
*
* Wait for the requested fence to signal (all asics).
* @intr selects whether to use interruptable (true) or non-interruptable
* (false) sleep when waiting for the fence.
* Returns 0 if the fence has passed, error for all other cases.
*/
int amdgpu_fence_wait(struct amdgpu_fence *fence, bool intr)
{
uint64_t seq[AMDGPU_MAX_RINGS] = {};
long r;
seq[fence->ring->idx] = fence->seq;
r = amdgpu_fence_wait_seq_timeout(fence->ring->adev, seq, intr, MAX_SCHEDULE_TIMEOUT);
if (r < 0) {
return r;
}
r = fence_signal(&fence->base);
if (!r)
FENCE_TRACE(&fence->base, "signaled from fence_wait\n");
return 0;
}
/**
* amdgpu_fence_wait_any - wait for a fence to signal on any ring
*
* @adev: amdgpu device pointer
* @fences: amdgpu fence object(s)
* @intr: use interruptable sleep
*
* Wait for any requested fence to signal (all asics). Fence
* array is indexed by ring id. @intr selects whether to use
* interruptable (true) or non-interruptable (false) sleep when
* waiting for the fences. Used by the suballocator.
* Returns 0 if any fence has passed, error for all other cases.
*/
int amdgpu_fence_wait_any(struct amdgpu_device *adev,
struct amdgpu_fence **fences,
bool intr)
{
uint64_t seq[AMDGPU_MAX_RINGS];
unsigned i, num_rings = 0;
long r;
for (i = 0; i < AMDGPU_MAX_RINGS; ++i) {
seq[i] = 0;
if (!fences[i]) {
continue;
}
seq[i] = fences[i]->seq;
++num_rings;
}
/* nothing to wait for ? */
if (num_rings == 0)
return -ENOENT;
r = amdgpu_fence_wait_seq_timeout(adev, seq, intr, MAX_SCHEDULE_TIMEOUT);
if (r < 0) {
return r;
}
return 0;
if (signaled)
return 0;
else
return -EDEADLK;
}
/**
@ -739,19 +412,12 @@ int amdgpu_fence_wait_any(struct amdgpu_device *adev,
*/
int amdgpu_fence_wait_next(struct amdgpu_ring *ring)
{
uint64_t seq[AMDGPU_MAX_RINGS] = {};
long r;
uint64_t seq = atomic64_read(&ring->fence_drv.last_seq) + 1ULL;
seq[ring->idx] = atomic64_read(&ring->fence_drv.last_seq) + 1ULL;
if (seq[ring->idx] >= ring->fence_drv.sync_seq[ring->idx]) {
/* nothing to wait for, last_seq is
already the last emited fence */
if (seq >= ring->fence_drv.sync_seq[ring->idx])
return -ENOENT;
}
r = amdgpu_fence_wait_seq_timeout(ring->adev, seq, false, MAX_SCHEDULE_TIMEOUT);
if (r < 0)
return r;
return 0;
return amdgpu_fence_ring_wait_seq(ring, seq);
}
/**
@ -766,23 +432,12 @@ int amdgpu_fence_wait_next(struct amdgpu_ring *ring)
*/
int amdgpu_fence_wait_empty(struct amdgpu_ring *ring)
{
struct amdgpu_device *adev = ring->adev;
uint64_t seq[AMDGPU_MAX_RINGS] = {};
long r;
uint64_t seq = ring->fence_drv.sync_seq[ring->idx];
seq[ring->idx] = ring->fence_drv.sync_seq[ring->idx];
if (!seq[ring->idx])
if (!seq)
return 0;
r = amdgpu_fence_wait_seq_timeout(adev, seq, false, MAX_SCHEDULE_TIMEOUT);
if (r < 0) {
if (r == -EDEADLK)
return -EDEADLK;
dev_err(adev->dev, "error waiting for ring[%d] to become idle (%ld)\n",
ring->idx, r);
}
return 0;
return amdgpu_fence_ring_wait_seq(ring, seq);
}
/**
@ -933,9 +588,12 @@ int amdgpu_fence_driver_start_ring(struct amdgpu_ring *ring,
ring->fence_drv.gpu_addr = adev->uvd.gpu_addr + index;
}
amdgpu_fence_write(ring, atomic64_read(&ring->fence_drv.last_seq));
ring->fence_drv.initialized = true;
amdgpu_irq_get(adev, irq_src, irq_type);
ring->fence_drv.irq_src = irq_src;
ring->fence_drv.irq_type = irq_type;
ring->fence_drv.initialized = true;
dev_info(adev->dev, "fence driver on ring %d use gpu addr 0x%016llx, "
"cpu addr 0x%p\n", ring->idx,
ring->fence_drv.gpu_addr, ring->fence_drv.cpu_addr);
@ -966,6 +624,16 @@ void amdgpu_fence_driver_init_ring(struct amdgpu_ring *ring)
INIT_DELAYED_WORK(&ring->fence_drv.lockup_work,
amdgpu_fence_check_lockup);
ring->fence_drv.ring = ring;
if (amdgpu_enable_scheduler) {
ring->scheduler = amd_sched_create((void *)ring->adev,
&amdgpu_sched_ops,
ring->idx, 5, 0,
amdgpu_sched_hw_submission);
if (!ring->scheduler)
DRM_ERROR("Failed to create scheduler on ring %d.\n",
ring->idx);
}
}
/**
@ -982,7 +650,6 @@ void amdgpu_fence_driver_init_ring(struct amdgpu_ring *ring)
*/
int amdgpu_fence_driver_init(struct amdgpu_device *adev)
{
init_waitqueue_head(&adev->fence_queue);
if (amdgpu_debugfs_fence_init(adev))
dev_err(adev->dev, "fence debugfs file creation failed\n");
@ -1011,12 +678,77 @@ void amdgpu_fence_driver_fini(struct amdgpu_device *adev)
/* no need to trigger GPU reset as we are unloading */
amdgpu_fence_driver_force_completion(adev);
}
wake_up_all(&adev->fence_queue);
wake_up_all(&ring->fence_drv.fence_queue);
amdgpu_irq_put(adev, ring->fence_drv.irq_src,
ring->fence_drv.irq_type);
if (ring->scheduler)
amd_sched_destroy(ring->scheduler);
ring->fence_drv.initialized = false;
}
mutex_unlock(&adev->ring_lock);
}
/**
* amdgpu_fence_driver_suspend - suspend the fence driver
* for all possible rings.
*
* @adev: amdgpu device pointer
*
* Suspend the fence driver for all possible rings (all asics).
*/
void amdgpu_fence_driver_suspend(struct amdgpu_device *adev)
{
int i, r;
mutex_lock(&adev->ring_lock);
for (i = 0; i < AMDGPU_MAX_RINGS; i++) {
struct amdgpu_ring *ring = adev->rings[i];
if (!ring || !ring->fence_drv.initialized)
continue;
/* wait for gpu to finish processing current batch */
r = amdgpu_fence_wait_empty(ring);
if (r) {
/* delay GPU reset to resume */
amdgpu_fence_driver_force_completion(adev);
}
/* disable the interrupt */
amdgpu_irq_put(adev, ring->fence_drv.irq_src,
ring->fence_drv.irq_type);
}
mutex_unlock(&adev->ring_lock);
}
/**
* amdgpu_fence_driver_resume - resume the fence driver
* for all possible rings.
*
* @adev: amdgpu device pointer
*
* Resume the fence driver for all possible rings (all asics).
* Not all asics have all rings, so each asic will only
* start the fence driver on the rings it has using
* amdgpu_fence_driver_start_ring().
* Returns 0 for success.
*/
void amdgpu_fence_driver_resume(struct amdgpu_device *adev)
{
int i;
mutex_lock(&adev->ring_lock);
for (i = 0; i < AMDGPU_MAX_RINGS; i++) {
struct amdgpu_ring *ring = adev->rings[i];
if (!ring || !ring->fence_drv.initialized)
continue;
/* enable the interrupt */
amdgpu_irq_get(adev, ring->fence_drv.irq_src,
ring->fence_drv.irq_type);
}
mutex_unlock(&adev->ring_lock);
}
/**
* amdgpu_fence_driver_force_completion - force all fence waiter to complete
*
@ -1104,6 +836,22 @@ static inline bool amdgpu_test_signaled(struct amdgpu_fence *fence)
return test_bit(FENCE_FLAG_SIGNALED_BIT, &fence->base.flags);
}
static inline bool amdgpu_test_signaled_any(struct amdgpu_fence **fences)
{
int idx;
struct amdgpu_fence *fence;
idx = 0;
for (idx = 0; idx < AMDGPU_MAX_RINGS; ++idx) {
fence = fences[idx];
if (fence) {
if (test_bit(FENCE_FLAG_SIGNALED_BIT, &fence->base.flags))
return true;
}
}
return false;
}
struct amdgpu_wait_cb {
struct fence_cb base;
struct task_struct *task;
@ -1119,14 +867,35 @@ static void amdgpu_fence_wait_cb(struct fence *fence, struct fence_cb *cb)
static signed long amdgpu_fence_default_wait(struct fence *f, bool intr,
signed long t)
{
struct amdgpu_fence *array[AMDGPU_MAX_RINGS];
struct amdgpu_fence *fence = to_amdgpu_fence(f);
struct amdgpu_device *adev = fence->ring->adev;
struct amdgpu_wait_cb cb;
cb.task = current;
memset(&array[0], 0, sizeof(array));
array[0] = fence;
if (fence_add_callback(f, &cb.base, amdgpu_fence_wait_cb))
return t;
return amdgpu_fence_wait_any(adev, array, intr, t);
}
/* wait until any fence in array signaled */
signed long amdgpu_fence_wait_any(struct amdgpu_device *adev,
struct amdgpu_fence **array, bool intr, signed long t)
{
long idx = 0;
struct amdgpu_wait_cb cb[AMDGPU_MAX_RINGS];
struct amdgpu_fence *fence;
BUG_ON(!array);
for (idx = 0; idx < AMDGPU_MAX_RINGS; ++idx) {
fence = array[idx];
if (fence) {
cb[idx].task = current;
if (fence_add_callback(&fence->base,
&cb[idx].base, amdgpu_fence_wait_cb))
return t; /* return if fence is already signaled */
}
}
while (t > 0) {
if (intr)
@ -1135,10 +904,10 @@ static signed long amdgpu_fence_default_wait(struct fence *f, bool intr,
set_current_state(TASK_UNINTERRUPTIBLE);
/*
* amdgpu_test_signaled must be called after
* amdgpu_test_signaled_any must be called after
* set_current_state to prevent a race with wake_up_process
*/
if (amdgpu_test_signaled(fence))
if (amdgpu_test_signaled_any(array))
break;
if (adev->needs_reset) {
@ -1153,7 +922,13 @@ static signed long amdgpu_fence_default_wait(struct fence *f, bool intr,
}
__set_current_state(TASK_RUNNING);
fence_remove_callback(f, &cb.base);
idx = 0;
for (idx = 0; idx < AMDGPU_MAX_RINGS; ++idx) {
fence = array[idx];
if (fence)
fence_remove_callback(&fence->base, &cb[idx].base);
}
return t;
}

View File

@ -88,6 +88,7 @@ int amdgpu_ib_get(struct amdgpu_ring *ring, struct amdgpu_vm *vm,
ib->fence = NULL;
ib->user = NULL;
ib->vm = vm;
ib->ctx = NULL;
ib->gds_base = 0;
ib->gds_size = 0;
ib->gws_base = 0;
@ -142,6 +143,7 @@ int amdgpu_ib_schedule(struct amdgpu_device *adev, unsigned num_ibs,
struct amdgpu_ring *ring;
struct amdgpu_ctx *ctx, *old_ctx;
struct amdgpu_vm *vm;
uint64_t sequence;
unsigned i;
int r = 0;
@ -165,9 +167,11 @@ int amdgpu_ib_schedule(struct amdgpu_device *adev, unsigned num_ibs,
if (vm) {
/* grab a vm id if necessary */
struct amdgpu_fence *vm_id_fence = NULL;
vm_id_fence = amdgpu_vm_grab_id(ibs->ring, ibs->vm);
amdgpu_sync_fence(&ibs->sync, vm_id_fence);
r = amdgpu_vm_grab_id(ibs->vm, ibs->ring, &ibs->sync);
if (r) {
amdgpu_ring_unlock_undo(ring);
return r;
}
}
r = amdgpu_sync_rings(&ibs->sync, ring);
@ -212,11 +216,18 @@ int amdgpu_ib_schedule(struct amdgpu_device *adev, unsigned num_ibs,
return r;
}
sequence = amdgpu_enable_scheduler ? ib->sequence : 0;
if (!amdgpu_enable_scheduler && ib->ctx)
ib->sequence = amdgpu_ctx_add_fence(ib->ctx, ring,
&ib->fence->base,
sequence);
/* wrap the last IB with fence */
if (ib->user) {
uint64_t addr = amdgpu_bo_gpu_offset(ib->user->bo);
addr += ib->user->offset;
amdgpu_ring_emit_fence(ring, addr, ib->fence->seq,
amdgpu_ring_emit_fence(ring, addr, ib->sequence,
AMDGPU_FENCE_FLAG_64BIT);
}

View File

@ -206,6 +206,8 @@ restart_ih:
amdgpu_amdkfd_interrupt(adev,
(const void *) &adev->irq.ih.ring[ring_index]);
entry.iv_entry = (const uint32_t *)
&adev->irq.ih.ring[ring_index];
amdgpu_ih_decode_iv(adev, &entry);
adev->irq.ih.rptr &= adev->irq.ih.ptr_mask;

View File

@ -52,6 +52,7 @@ struct amdgpu_iv_entry {
unsigned ring_id;
unsigned vm_id;
unsigned pas_id;
const uint32_t *iv_entry;
};
int amdgpu_ih_ring_init(struct amdgpu_device *adev, unsigned ring_size,

View File

@ -272,6 +272,11 @@ void amdgpu_irq_fini(struct amdgpu_device *adev)
kfree(src->enabled_types);
src->enabled_types = NULL;
if (src->data) {
kfree(src->data);
kfree(src);
adev->irq.sources[i] = NULL;
}
}
}

View File

@ -40,6 +40,7 @@ struct amdgpu_irq_src {
unsigned num_types;
atomic_t *enabled_types;
const struct amdgpu_irq_src_funcs *funcs;
void *data;
};
/* provided by interrupt generating IP blocks */

View File

@ -96,8 +96,8 @@ int amdgpu_driver_load_kms(struct drm_device *dev, unsigned long flags)
if ((amdgpu_runtime_pm != 0) &&
amdgpu_has_atpx() &&
((flags & AMDGPU_IS_APU) == 0))
flags |= AMDGPU_IS_PX;
((flags & AMD_IS_APU) == 0))
flags |= AMD_IS_PX;
/* amdgpu_device_init should report only fatal error
* like memory allocation failure or iomapping failure,
@ -451,11 +451,11 @@ static int amdgpu_info_ioctl(struct drm_device *dev, void *data, struct drm_file
dev_info.num_hw_gfx_contexts = adev->gfx.config.max_hw_contexts;
dev_info._pad = 0;
dev_info.ids_flags = 0;
if (adev->flags & AMDGPU_IS_APU)
if (adev->flags & AMD_IS_APU)
dev_info.ids_flags |= AMDGPU_IDS_FLAGS_FUSION;
dev_info.virtual_address_offset = AMDGPU_VA_RESERVED_SIZE;
dev_info.virtual_address_max = (uint64_t)adev->vm_manager.max_pfn * AMDGPU_GPU_PAGE_SIZE;
dev_info.virtual_address_alignment = max(PAGE_SIZE, 0x10000UL);
dev_info.virtual_address_alignment = max((int)PAGE_SIZE, AMDGPU_GPU_PAGE_SIZE);
dev_info.pte_fragment_size = (1 << AMDGPU_LOG2_PAGES_PER_FRAG) *
AMDGPU_GPU_PAGE_SIZE;
dev_info.gart_page_size = AMDGPU_GPU_PAGE_SIZE;
@ -527,10 +527,7 @@ int amdgpu_driver_open_kms(struct drm_device *dev, struct drm_file *file_priv)
mutex_init(&fpriv->bo_list_lock);
idr_init(&fpriv->bo_list_handles);
/* init context manager */
mutex_init(&fpriv->ctx_mgr.lock);
idr_init(&fpriv->ctx_mgr.ctx_handles);
fpriv->ctx_mgr.adev = adev;
amdgpu_ctx_mgr_init(&fpriv->ctx_mgr);
file_priv->driver_priv = fpriv;
@ -571,8 +568,7 @@ void amdgpu_driver_postclose_kms(struct drm_device *dev,
idr_destroy(&fpriv->bo_list_handles);
mutex_destroy(&fpriv->bo_list_lock);
/* release context */
amdgpu_ctx_fini(fpriv);
amdgpu_ctx_mgr_fini(&fpriv->ctx_mgr);
kfree(fpriv);
file_priv->driver_priv = NULL;

View File

@ -223,18 +223,6 @@ int amdgpu_bo_create_restricted(struct amdgpu_device *adev,
size_t acc_size;
int r;
/* VI has a hw bug where VM PTEs have to be allocated in groups of 8.
* do this as a temporary workaround
*/
if (!(domain & (AMDGPU_GEM_DOMAIN_GDS | AMDGPU_GEM_DOMAIN_GWS | AMDGPU_GEM_DOMAIN_OA))) {
if (adev->asic_type >= CHIP_TOPAZ) {
if (byte_align & 0x7fff)
byte_align = ALIGN(byte_align, 0x8000);
if (size & 0x7fff)
size = ALIGN(size, 0x8000);
}
}
page_align = roundup(byte_align, PAGE_SIZE) >> PAGE_SHIFT;
size = ALIGN(size, PAGE_SIZE);
@ -462,7 +450,7 @@ int amdgpu_bo_unpin(struct amdgpu_bo *bo)
int amdgpu_bo_evict_vram(struct amdgpu_device *adev)
{
/* late 2.6.33 fix IGP hibernate - we need pm ops to do this correct */
if (0 && (adev->flags & AMDGPU_IS_APU)) {
if (0 && (adev->flags & AMD_IS_APU)) {
/* Useless to evict on IGP chips */
return 0;
}
@ -478,7 +466,6 @@ void amdgpu_bo_force_delete(struct amdgpu_device *adev)
}
dev_err(adev->dev, "Userspace still has active objects !\n");
list_for_each_entry_safe(bo, n, &adev->gem.objects, list) {
mutex_lock(&adev->ddev->struct_mutex);
dev_err(adev->dev, "%p %p %lu %lu force free\n",
&bo->gem_base, bo, (unsigned long)bo->gem_base.size,
*((unsigned long *)&bo->gem_base.refcount));
@ -486,8 +473,7 @@ void amdgpu_bo_force_delete(struct amdgpu_device *adev)
list_del_init(&bo->list);
mutex_unlock(&bo->adev->gem.mutex);
/* this should unref the ttm bo */
drm_gem_object_unreference(&bo->gem_base);
mutex_unlock(&adev->ddev->struct_mutex);
drm_gem_object_unreference_unlocked(&bo->gem_base);
}
}
@ -658,13 +644,13 @@ int amdgpu_bo_fault_reserve_notify(struct ttm_buffer_object *bo)
* @shared: true if fence should be added shared
*
*/
void amdgpu_bo_fence(struct amdgpu_bo *bo, struct amdgpu_fence *fence,
void amdgpu_bo_fence(struct amdgpu_bo *bo, struct fence *fence,
bool shared)
{
struct reservation_object *resv = bo->tbo.resv;
if (shared)
reservation_object_add_shared_fence(resv, &fence->base);
reservation_object_add_shared_fence(resv, fence);
else
reservation_object_add_excl_fence(resv, &fence->base);
reservation_object_add_excl_fence(resv, fence);
}

View File

@ -161,7 +161,7 @@ int amdgpu_bo_get_metadata(struct amdgpu_bo *bo, void *buffer,
void amdgpu_bo_move_notify(struct ttm_buffer_object *bo,
struct ttm_mem_reg *new_mem);
int amdgpu_bo_fault_reserve_notify(struct ttm_buffer_object *bo);
void amdgpu_bo_fence(struct amdgpu_bo *bo, struct amdgpu_fence *fence,
void amdgpu_bo_fence(struct amdgpu_bo *bo, struct fence *fence,
bool shared);
/*

View File

@ -82,7 +82,7 @@ static ssize_t amdgpu_set_dpm_state(struct device *dev,
mutex_unlock(&adev->pm.mutex);
/* Can't set dpm state when the card is off */
if (!(adev->flags & AMDGPU_IS_PX) ||
if (!(adev->flags & AMD_IS_PX) ||
(ddev->switch_power_state == DRM_SWITCH_POWER_ON))
amdgpu_pm_compute_clocks(adev);
fail:
@ -538,7 +538,7 @@ static void amdgpu_dpm_change_power_state_locked(struct amdgpu_device *adev)
/* vce just modifies an existing state so force a change */
if (ps->vce_active != adev->pm.dpm.vce_active)
goto force;
if (adev->flags & AMDGPU_IS_APU) {
if (adev->flags & AMD_IS_APU) {
/* for APUs if the num crtcs changed but state is the same,
* all we need to do is update the display configuration.
*/
@ -580,7 +580,6 @@ force:
amdgpu_dpm_print_power_state(adev, adev->pm.dpm.requested_ps);
}
mutex_lock(&adev->ddev->struct_mutex);
mutex_lock(&adev->ring_lock);
/* update whether vce is active */
@ -628,7 +627,6 @@ force:
done:
mutex_unlock(&adev->ring_lock);
mutex_unlock(&adev->ddev->struct_mutex);
}
void amdgpu_dpm_enable_uvd(struct amdgpu_device *adev, bool enable)

View File

@ -342,6 +342,8 @@ int amdgpu_ring_init(struct amdgpu_device *adev, struct amdgpu_ring *ring,
amdgpu_fence_driver_init_ring(ring);
}
init_waitqueue_head(&ring->fence_drv.fence_queue);
r = amdgpu_wb_get(adev, &ring->rptr_offs);
if (r) {
dev_err(adev->dev, "(%d) ring rptr_offs wb alloc failed\n", r);
@ -367,7 +369,7 @@ int amdgpu_ring_init(struct amdgpu_device *adev, struct amdgpu_ring *ring,
}
ring->next_rptr_gpu_addr = adev->wb.gpu_addr + (ring->next_rptr_offs * 4);
ring->next_rptr_cpu_addr = &adev->wb.wb[ring->next_rptr_offs];
spin_lock_init(&ring->fence_lock);
r = amdgpu_fence_driver_start_ring(ring, irq_src, irq_type);
if (r) {
dev_err(adev->dev, "failed initializing fences (%d).\n", r);

View File

@ -160,7 +160,8 @@ static void amdgpu_sa_bo_try_free(struct amdgpu_sa_manager *sa_manager)
sa_bo = list_entry(sa_manager->hole->next, struct amdgpu_sa_bo, olist);
list_for_each_entry_safe_from(sa_bo, tmp, &sa_manager->olist, olist) {
if (sa_bo->fence == NULL || !amdgpu_fence_signaled(sa_bo->fence)) {
if (sa_bo->fence == NULL ||
!fence_is_signaled(&sa_bo->fence->base)) {
return;
}
amdgpu_sa_bo_remove_locked(sa_bo);
@ -274,7 +275,7 @@ static bool amdgpu_sa_bo_next_hole(struct amdgpu_sa_manager *sa_manager,
sa_bo = list_first_entry(&sa_manager->flist[i],
struct amdgpu_sa_bo, flist);
if (!amdgpu_fence_signaled(sa_bo->fence)) {
if (!fence_is_signaled(&sa_bo->fence->base)) {
fences[i] = sa_bo->fence;
continue;
}
@ -317,6 +318,7 @@ int amdgpu_sa_bo_new(struct amdgpu_device *adev,
struct amdgpu_fence *fences[AMDGPU_MAX_RINGS];
unsigned tries[AMDGPU_MAX_RINGS];
int i, r;
signed long t;
BUG_ON(align > sa_manager->align);
BUG_ON(size > sa_manager->size);
@ -350,7 +352,8 @@ int amdgpu_sa_bo_new(struct amdgpu_device *adev,
} while (amdgpu_sa_bo_next_hole(sa_manager, fences, tries));
spin_unlock(&sa_manager->wq.lock);
r = amdgpu_fence_wait_any(adev, fences, false);
t = amdgpu_fence_wait_any(adev, fences, false, MAX_SCHEDULE_TIMEOUT);
r = (t > 0) ? 0 : t;
spin_lock(&sa_manager->wq.lock);
/* if we have nothing to wait for block */
if (r == -ENOENT) {
@ -379,7 +382,7 @@ void amdgpu_sa_bo_free(struct amdgpu_device *adev, struct amdgpu_sa_bo **sa_bo,
sa_manager = (*sa_bo)->manager;
spin_lock(&sa_manager->wq.lock);
if (fence && !amdgpu_fence_signaled(fence)) {
if (fence && !fence_is_signaled(&fence->base)) {
(*sa_bo)->fence = amdgpu_fence_ref(fence);
list_add_tail(&(*sa_bo)->flist,
&sa_manager->flist[fence->ring->idx]);

View File

@ -0,0 +1,145 @@
/*
* Copyright 2015 Advanced Micro Devices, Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*
*
*/
#include <linux/kthread.h>
#include <linux/wait.h>
#include <linux/sched.h>
#include <drm/drmP.h>
#include "amdgpu.h"
static int amdgpu_sched_prepare_job(struct amd_gpu_scheduler *sched,
struct amd_sched_entity *entity,
struct amd_sched_job *job)
{
int r = 0;
struct amdgpu_cs_parser *sched_job;
if (!job || !job->data) {
DRM_ERROR("job is null\n");
return -EINVAL;
}
sched_job = (struct amdgpu_cs_parser *)job->data;
if (sched_job->prepare_job) {
r = sched_job->prepare_job(sched_job);
if (r) {
DRM_ERROR("Prepare job error\n");
schedule_work(&sched_job->job_work);
}
}
return r;
}
static struct fence *amdgpu_sched_run_job(struct amd_gpu_scheduler *sched,
struct amd_sched_entity *entity,
struct amd_sched_job *job)
{
int r = 0;
struct amdgpu_cs_parser *sched_job;
struct amdgpu_fence *fence;
if (!job || !job->data) {
DRM_ERROR("job is null\n");
return NULL;
}
sched_job = (struct amdgpu_cs_parser *)job->data;
mutex_lock(&sched_job->job_lock);
r = amdgpu_ib_schedule(sched_job->adev,
sched_job->num_ibs,
sched_job->ibs,
sched_job->filp);
if (r)
goto err;
fence = amdgpu_fence_ref(sched_job->ibs[sched_job->num_ibs - 1].fence);
if (sched_job->run_job) {
r = sched_job->run_job(sched_job);
if (r)
goto err;
}
mutex_unlock(&sched_job->job_lock);
return &fence->base;
err:
DRM_ERROR("Run job error\n");
mutex_unlock(&sched_job->job_lock);
schedule_work(&sched_job->job_work);
return NULL;
}
static void amdgpu_sched_process_job(struct amd_gpu_scheduler *sched,
struct amd_sched_job *job)
{
struct amdgpu_cs_parser *sched_job;
if (!job || !job->data) {
DRM_ERROR("job is null\n");
return;
}
sched_job = (struct amdgpu_cs_parser *)job->data;
schedule_work(&sched_job->job_work);
}
struct amd_sched_backend_ops amdgpu_sched_ops = {
.prepare_job = amdgpu_sched_prepare_job,
.run_job = amdgpu_sched_run_job,
.process_job = amdgpu_sched_process_job
};
int amdgpu_sched_ib_submit_kernel_helper(struct amdgpu_device *adev,
struct amdgpu_ring *ring,
struct amdgpu_ib *ibs,
unsigned num_ibs,
int (*free_job)(struct amdgpu_cs_parser *),
void *owner,
struct fence **f)
{
int r = 0;
if (amdgpu_enable_scheduler) {
struct amdgpu_cs_parser *sched_job =
amdgpu_cs_parser_create(adev, owner, &adev->kernel_ctx,
ibs, num_ibs);
if(!sched_job) {
return -ENOMEM;
}
sched_job->free_job = free_job;
mutex_lock(&sched_job->job_lock);
r = amd_sched_push_job(ring->scheduler,
&adev->kernel_ctx.rings[ring->idx].entity,
sched_job, &sched_job->s_fence);
if (r) {
mutex_unlock(&sched_job->job_lock);
kfree(sched_job);
return r;
}
ibs[num_ibs - 1].sequence = sched_job->s_fence->v_seq;
*f = fence_get(&sched_job->s_fence->base);
mutex_unlock(&sched_job->job_lock);
} else {
r = amdgpu_ib_schedule(adev, num_ibs, ibs, owner);
if (r)
return r;
*f = fence_get(&ibs[num_ibs - 1].fence->base);
}
return 0;
}

View File

@ -53,20 +53,24 @@ void amdgpu_sync_create(struct amdgpu_sync *sync)
}
/**
* amdgpu_sync_fence - use the semaphore to sync to a fence
* amdgpu_sync_fence - remember to sync to this fence
*
* @sync: sync object to add fence to
* @fence: fence to sync to
*
* Sync to the fence using the semaphore objects
*/
void amdgpu_sync_fence(struct amdgpu_sync *sync,
struct amdgpu_fence *fence)
int amdgpu_sync_fence(struct amdgpu_device *adev, struct amdgpu_sync *sync,
struct fence *f)
{
struct amdgpu_fence *fence;
struct amdgpu_fence *other;
if (!fence)
return;
if (!f)
return 0;
fence = to_amdgpu_fence(f);
if (!fence || fence->ring->adev != adev)
return fence_wait(f, true);
other = sync->sync_to[fence->ring->idx];
sync->sync_to[fence->ring->idx] = amdgpu_fence_ref(
@ -79,6 +83,8 @@ void amdgpu_sync_fence(struct amdgpu_sync *sync,
amdgpu_fence_later(fence, other));
amdgpu_fence_unref(&other);
}
return 0;
}
/**
@ -106,11 +112,7 @@ int amdgpu_sync_resv(struct amdgpu_device *adev,
/* always sync to the exclusive fence */
f = reservation_object_get_excl(resv);
fence = f ? to_amdgpu_fence(f) : NULL;
if (fence && fence->ring->adev == adev)
amdgpu_sync_fence(sync, fence);
else if (f)
r = fence_wait(f, true);
r = amdgpu_sync_fence(adev, sync, f);
flist = reservation_object_get_list(resv);
if (!flist || r)
@ -121,14 +123,26 @@ int amdgpu_sync_resv(struct amdgpu_device *adev,
reservation_object_held(resv));
fence = f ? to_amdgpu_fence(f) : NULL;
if (fence && fence->ring->adev == adev) {
if (fence->owner != owner ||
fence->owner == AMDGPU_FENCE_OWNER_UNDEFINED)
amdgpu_sync_fence(sync, fence);
} else if (f) {
r = fence_wait(f, true);
if (r)
break;
/* VM updates are only interesting
* for other VM updates and moves.
*/
if ((owner != AMDGPU_FENCE_OWNER_MOVE) &&
(fence->owner != AMDGPU_FENCE_OWNER_MOVE) &&
((owner == AMDGPU_FENCE_OWNER_VM) !=
(fence->owner == AMDGPU_FENCE_OWNER_VM)))
continue;
/* Ignore fence from the same owner as
* long as it isn't undefined.
*/
if (owner != AMDGPU_FENCE_OWNER_UNDEFINED &&
fence->owner == owner)
continue;
}
r = amdgpu_sync_fence(adev, sync, f);
if (r)
break;
}
return r;
}
@ -164,9 +178,9 @@ int amdgpu_sync_rings(struct amdgpu_sync *sync,
return -EINVAL;
}
if (count >= AMDGPU_NUM_SYNCS) {
if (amdgpu_enable_scheduler || (count >= AMDGPU_NUM_SYNCS)) {
/* not enough room, wait manually */
r = amdgpu_fence_wait(fence, false);
r = fence_wait(&fence->base, false);
if (r)
return r;
continue;
@ -186,7 +200,7 @@ int amdgpu_sync_rings(struct amdgpu_sync *sync,
if (!amdgpu_semaphore_emit_signal(other, semaphore)) {
/* signaling wasn't successful wait manually */
amdgpu_ring_undo(other);
r = amdgpu_fence_wait(fence, false);
r = fence_wait(&fence->base, false);
if (r)
return r;
continue;
@ -196,7 +210,7 @@ int amdgpu_sync_rings(struct amdgpu_sync *sync,
if (!amdgpu_semaphore_emit_wait(ring, semaphore)) {
/* waiting wasn't successful wait manually */
amdgpu_ring_undo(other);
r = amdgpu_fence_wait(fence, false);
r = fence_wait(&fence->base, false);
if (r)
return r;
continue;

View File

@ -116,7 +116,7 @@ static void amdgpu_do_test_moves(struct amdgpu_device *adev)
goto out_lclean_unpin;
}
r = amdgpu_fence_wait(fence, false);
r = fence_wait(&fence->base, false);
if (r) {
DRM_ERROR("Failed to wait for GTT->VRAM fence %d\n", i);
goto out_lclean_unpin;
@ -161,7 +161,7 @@ static void amdgpu_do_test_moves(struct amdgpu_device *adev)
goto out_lclean_unpin;
}
r = amdgpu_fence_wait(fence, false);
r = fence_wait(&fence->base, false);
if (r) {
DRM_ERROR("Failed to wait for VRAM->GTT fence %d\n", i);
goto out_lclean_unpin;
@ -238,7 +238,7 @@ void amdgpu_test_moves(struct amdgpu_device *adev)
static int amdgpu_test_create_and_emit_fence(struct amdgpu_device *adev,
struct amdgpu_ring *ring,
struct amdgpu_fence **fence)
struct fence **fence)
{
uint32_t handle = ring->idx ^ 0xdeafbeef;
int r;
@ -269,15 +269,16 @@ static int amdgpu_test_create_and_emit_fence(struct amdgpu_device *adev,
DRM_ERROR("Failed to get dummy destroy msg\n");
return r;
}
} else {
struct amdgpu_fence *a_fence = NULL;
r = amdgpu_ring_lock(ring, 64);
if (r) {
DRM_ERROR("Failed to lock ring A %d\n", ring->idx);
return r;
}
amdgpu_fence_emit(ring, AMDGPU_FENCE_OWNER_UNDEFINED, fence);
amdgpu_fence_emit(ring, AMDGPU_FENCE_OWNER_UNDEFINED, &a_fence);
amdgpu_ring_unlock_commit(ring);
*fence = &a_fence->base;
}
return 0;
}
@ -286,7 +287,7 @@ void amdgpu_test_ring_sync(struct amdgpu_device *adev,
struct amdgpu_ring *ringA,
struct amdgpu_ring *ringB)
{
struct amdgpu_fence *fence1 = NULL, *fence2 = NULL;
struct fence *fence1 = NULL, *fence2 = NULL;
struct amdgpu_semaphore *semaphore = NULL;
int r;
@ -322,7 +323,7 @@ void amdgpu_test_ring_sync(struct amdgpu_device *adev,
mdelay(1000);
if (amdgpu_fence_signaled(fence1)) {
if (fence_is_signaled(fence1)) {
DRM_ERROR("Fence 1 signaled without waiting for semaphore.\n");
goto out_cleanup;
}
@ -335,7 +336,7 @@ void amdgpu_test_ring_sync(struct amdgpu_device *adev,
amdgpu_semaphore_emit_signal(ringB, semaphore);
amdgpu_ring_unlock_commit(ringB);
r = amdgpu_fence_wait(fence1, false);
r = fence_wait(fence1, false);
if (r) {
DRM_ERROR("Failed to wait for sync fence 1\n");
goto out_cleanup;
@ -343,7 +344,7 @@ void amdgpu_test_ring_sync(struct amdgpu_device *adev,
mdelay(1000);
if (amdgpu_fence_signaled(fence2)) {
if (fence_is_signaled(fence2)) {
DRM_ERROR("Fence 2 signaled without waiting for semaphore.\n");
goto out_cleanup;
}
@ -356,7 +357,7 @@ void amdgpu_test_ring_sync(struct amdgpu_device *adev,
amdgpu_semaphore_emit_signal(ringB, semaphore);
amdgpu_ring_unlock_commit(ringB);
r = amdgpu_fence_wait(fence2, false);
r = fence_wait(fence2, false);
if (r) {
DRM_ERROR("Failed to wait for sync fence 1\n");
goto out_cleanup;
@ -366,10 +367,10 @@ out_cleanup:
amdgpu_semaphore_free(adev, &semaphore, NULL);
if (fence1)
amdgpu_fence_unref(&fence1);
fence_put(fence1);
if (fence2)
amdgpu_fence_unref(&fence2);
fence_put(fence2);
if (r)
printk(KERN_WARNING "Error while testing ring sync (%d).\n", r);
@ -380,7 +381,7 @@ static void amdgpu_test_ring_sync2(struct amdgpu_device *adev,
struct amdgpu_ring *ringB,
struct amdgpu_ring *ringC)
{
struct amdgpu_fence *fenceA = NULL, *fenceB = NULL;
struct fence *fenceA = NULL, *fenceB = NULL;
struct amdgpu_semaphore *semaphore = NULL;
bool sigA, sigB;
int i, r;
@ -416,11 +417,11 @@ static void amdgpu_test_ring_sync2(struct amdgpu_device *adev,
mdelay(1000);
if (amdgpu_fence_signaled(fenceA)) {
if (fence_is_signaled(fenceA)) {
DRM_ERROR("Fence A signaled without waiting for semaphore.\n");
goto out_cleanup;
}
if (amdgpu_fence_signaled(fenceB)) {
if (fence_is_signaled(fenceB)) {
DRM_ERROR("Fence B signaled without waiting for semaphore.\n");
goto out_cleanup;
}
@ -435,8 +436,8 @@ static void amdgpu_test_ring_sync2(struct amdgpu_device *adev,
for (i = 0; i < 30; ++i) {
mdelay(100);
sigA = amdgpu_fence_signaled(fenceA);
sigB = amdgpu_fence_signaled(fenceB);
sigA = fence_is_signaled(fenceA);
sigB = fence_is_signaled(fenceB);
if (sigA || sigB)
break;
}
@ -461,12 +462,12 @@ static void amdgpu_test_ring_sync2(struct amdgpu_device *adev,
mdelay(1000);
r = amdgpu_fence_wait(fenceA, false);
r = fence_wait(fenceA, false);
if (r) {
DRM_ERROR("Failed to wait for sync fence A\n");
goto out_cleanup;
}
r = amdgpu_fence_wait(fenceB, false);
r = fence_wait(fenceB, false);
if (r) {
DRM_ERROR("Failed to wait for sync fence B\n");
goto out_cleanup;
@ -476,10 +477,10 @@ out_cleanup:
amdgpu_semaphore_free(adev, &semaphore, NULL);
if (fenceA)
amdgpu_fence_unref(&fenceA);
fence_put(fenceA);
if (fenceB)
amdgpu_fence_unref(&fenceB);
fence_put(fenceB);
if (r)
printk(KERN_WARNING "Error while testing ring sync (%d).\n", r);

View File

@ -52,6 +52,7 @@
#endif
#define FIRMWARE_TONGA "amdgpu/tonga_uvd.bin"
#define FIRMWARE_CARRIZO "amdgpu/carrizo_uvd.bin"
#define FIRMWARE_FIJI "amdgpu/fiji_uvd.bin"
/**
* amdgpu_uvd_cs_ctx - Command submission parser context
@ -81,6 +82,7 @@ MODULE_FIRMWARE(FIRMWARE_MULLINS);
#endif
MODULE_FIRMWARE(FIRMWARE_TONGA);
MODULE_FIRMWARE(FIRMWARE_CARRIZO);
MODULE_FIRMWARE(FIRMWARE_FIJI);
static void amdgpu_uvd_note_usage(struct amdgpu_device *adev);
static void amdgpu_uvd_idle_work_handler(struct work_struct *work);
@ -116,6 +118,9 @@ int amdgpu_uvd_sw_init(struct amdgpu_device *adev)
case CHIP_TONGA:
fw_name = FIRMWARE_TONGA;
break;
case CHIP_FIJI:
fw_name = FIRMWARE_FIJI;
break;
case CHIP_CARRIZO:
fw_name = FIRMWARE_CARRIZO;
break;
@ -283,7 +288,7 @@ void amdgpu_uvd_free_handles(struct amdgpu_device *adev, struct drm_file *filp)
for (i = 0; i < AMDGPU_MAX_UVD_HANDLES; ++i) {
uint32_t handle = atomic_read(&adev->uvd.handles[i]);
if (handle != 0 && adev->uvd.filp[i] == filp) {
struct amdgpu_fence *fence;
struct fence *fence;
amdgpu_uvd_note_usage(adev);
@ -293,8 +298,8 @@ void amdgpu_uvd_free_handles(struct amdgpu_device *adev, struct drm_file *filp)
continue;
}
amdgpu_fence_wait(fence, false);
amdgpu_fence_unref(&fence);
fence_wait(fence, false);
fence_put(fence);
adev->uvd.filp[i] = NULL;
atomic_set(&adev->uvd.handles[i], 0);
@ -374,7 +379,8 @@ static int amdgpu_uvd_cs_msg_decode(uint32_t *msg, unsigned buf_sizes[])
unsigned height_in_mb = ALIGN(height / 16, 2);
unsigned fs_in_mb = width_in_mb * height_in_mb;
unsigned image_size, tmp, min_dpb_size, num_dpb_buffer, min_ctx_size;
unsigned image_size, tmp, min_dpb_size, num_dpb_buffer;
unsigned min_ctx_size = 0;
image_size = width * height;
image_size += image_size / 2;
@ -507,28 +513,25 @@ static int amdgpu_uvd_cs_msg(struct amdgpu_uvd_cs_ctx *ctx,
{
struct amdgpu_device *adev = ctx->parser->adev;
int32_t *msg, msg_type, handle;
struct fence *f;
void *ptr;
int i, r;
long r;
int i;
if (offset & 0x3F) {
DRM_ERROR("UVD messages must be 64 byte aligned!\n");
return -EINVAL;
}
f = reservation_object_get_excl(bo->tbo.resv);
if (f) {
r = amdgpu_fence_wait((struct amdgpu_fence *)f, false);
if (r) {
DRM_ERROR("Failed waiting for UVD message (%d)!\n", r);
return r;
}
r = reservation_object_wait_timeout_rcu(bo->tbo.resv, true, false,
MAX_SCHEDULE_TIMEOUT);
if (r < 0) {
DRM_ERROR("Failed waiting for UVD message (%ld)!\n", r);
return r;
}
r = amdgpu_bo_kmap(bo, &ptr);
if (r) {
DRM_ERROR("Failed mapping the UVD message (%d)!\n", r);
DRM_ERROR("Failed mapping the UVD message (%ld)!\n", r);
return r;
}
@ -803,14 +806,24 @@ int amdgpu_uvd_ring_parse_cs(struct amdgpu_cs_parser *parser, uint32_t ib_idx)
return 0;
}
static int amdgpu_uvd_free_job(
struct amdgpu_cs_parser *sched_job)
{
amdgpu_ib_free(sched_job->adev, sched_job->ibs);
kfree(sched_job->ibs);
return 0;
}
static int amdgpu_uvd_send_msg(struct amdgpu_ring *ring,
struct amdgpu_bo *bo,
struct amdgpu_fence **fence)
struct fence **fence)
{
struct ttm_validate_buffer tv;
struct ww_acquire_ctx ticket;
struct list_head head;
struct amdgpu_ib ib;
struct amdgpu_ib *ib = NULL;
struct fence *f = NULL;
struct amdgpu_device *adev = ring->adev;
uint64_t addr;
int i, r;
@ -832,34 +845,49 @@ static int amdgpu_uvd_send_msg(struct amdgpu_ring *ring,
r = ttm_bo_validate(&bo->tbo, &bo->placement, true, false);
if (r)
goto err;
r = amdgpu_ib_get(ring, NULL, 64, &ib);
if (r)
ib = kzalloc(sizeof(struct amdgpu_ib), GFP_KERNEL);
if (!ib) {
r = -ENOMEM;
goto err;
}
r = amdgpu_ib_get(ring, NULL, 64, ib);
if (r)
goto err1;
addr = amdgpu_bo_gpu_offset(bo);
ib.ptr[0] = PACKET0(mmUVD_GPCOM_VCPU_DATA0, 0);
ib.ptr[1] = addr;
ib.ptr[2] = PACKET0(mmUVD_GPCOM_VCPU_DATA1, 0);
ib.ptr[3] = addr >> 32;
ib.ptr[4] = PACKET0(mmUVD_GPCOM_VCPU_CMD, 0);
ib.ptr[5] = 0;
ib->ptr[0] = PACKET0(mmUVD_GPCOM_VCPU_DATA0, 0);
ib->ptr[1] = addr;
ib->ptr[2] = PACKET0(mmUVD_GPCOM_VCPU_DATA1, 0);
ib->ptr[3] = addr >> 32;
ib->ptr[4] = PACKET0(mmUVD_GPCOM_VCPU_CMD, 0);
ib->ptr[5] = 0;
for (i = 6; i < 16; ++i)
ib.ptr[i] = PACKET2(0);
ib.length_dw = 16;
ib->ptr[i] = PACKET2(0);
ib->length_dw = 16;
r = amdgpu_ib_schedule(ring->adev, 1, &ib, AMDGPU_FENCE_OWNER_UNDEFINED);
r = amdgpu_sched_ib_submit_kernel_helper(adev, ring, ib, 1,
&amdgpu_uvd_free_job,
AMDGPU_FENCE_OWNER_UNDEFINED,
&f);
if (r)
goto err;
ttm_eu_fence_buffer_objects(&ticket, &head, &ib.fence->base);
goto err2;
ttm_eu_fence_buffer_objects(&ticket, &head, f);
if (fence)
*fence = amdgpu_fence_ref(ib.fence);
amdgpu_ib_free(ring->adev, &ib);
*fence = fence_get(f);
amdgpu_bo_unref(&bo);
return 0;
fence_put(f);
if (amdgpu_enable_scheduler)
return 0;
amdgpu_ib_free(ring->adev, ib);
kfree(ib);
return 0;
err2:
amdgpu_ib_free(ring->adev, ib);
err1:
kfree(ib);
err:
ttm_eu_backoff_reservation(&ticket, &head);
return r;
@ -869,7 +897,7 @@ err:
crash the vcpu so just try to emmit a dummy create/destroy msg to
avoid this */
int amdgpu_uvd_get_create_msg(struct amdgpu_ring *ring, uint32_t handle,
struct amdgpu_fence **fence)
struct fence **fence)
{
struct amdgpu_device *adev = ring->adev;
struct amdgpu_bo *bo;
@ -916,7 +944,7 @@ int amdgpu_uvd_get_create_msg(struct amdgpu_ring *ring, uint32_t handle,
}
int amdgpu_uvd_get_destroy_msg(struct amdgpu_ring *ring, uint32_t handle,
struct amdgpu_fence **fence)
struct fence **fence)
{
struct amdgpu_device *adev = ring->adev;
struct amdgpu_bo *bo;

View File

@ -29,9 +29,9 @@ int amdgpu_uvd_sw_fini(struct amdgpu_device *adev);
int amdgpu_uvd_suspend(struct amdgpu_device *adev);
int amdgpu_uvd_resume(struct amdgpu_device *adev);
int amdgpu_uvd_get_create_msg(struct amdgpu_ring *ring, uint32_t handle,
struct amdgpu_fence **fence);
struct fence **fence);
int amdgpu_uvd_get_destroy_msg(struct amdgpu_ring *ring, uint32_t handle,
struct amdgpu_fence **fence);
struct fence **fence);
void amdgpu_uvd_free_handles(struct amdgpu_device *adev,
struct drm_file *filp);
int amdgpu_uvd_ring_parse_cs(struct amdgpu_cs_parser *parser, uint32_t ib_idx);

View File

@ -48,6 +48,7 @@
#endif
#define FIRMWARE_TONGA "amdgpu/tonga_vce.bin"
#define FIRMWARE_CARRIZO "amdgpu/carrizo_vce.bin"
#define FIRMWARE_FIJI "amdgpu/fiji_vce.bin"
#ifdef CONFIG_DRM_AMDGPU_CIK
MODULE_FIRMWARE(FIRMWARE_BONAIRE);
@ -58,6 +59,7 @@ MODULE_FIRMWARE(FIRMWARE_MULLINS);
#endif
MODULE_FIRMWARE(FIRMWARE_TONGA);
MODULE_FIRMWARE(FIRMWARE_CARRIZO);
MODULE_FIRMWARE(FIRMWARE_FIJI);
static void amdgpu_vce_idle_work_handler(struct work_struct *work);
@ -101,6 +103,9 @@ int amdgpu_vce_sw_init(struct amdgpu_device *adev, unsigned long size)
case CHIP_CARRIZO:
fw_name = FIRMWARE_CARRIZO;
break;
case CHIP_FIJI:
fw_name = FIRMWARE_FIJI;
break;
default:
return -EINVAL;
@ -334,6 +339,14 @@ void amdgpu_vce_free_handles(struct amdgpu_device *adev, struct drm_file *filp)
}
}
static int amdgpu_vce_free_job(
struct amdgpu_cs_parser *sched_job)
{
amdgpu_ib_free(sched_job->adev, sched_job->ibs);
kfree(sched_job->ibs);
return 0;
}
/**
* amdgpu_vce_get_create_msg - generate a VCE create msg
*
@ -345,59 +358,69 @@ void amdgpu_vce_free_handles(struct amdgpu_device *adev, struct drm_file *filp)
* Open up a stream for HW test
*/
int amdgpu_vce_get_create_msg(struct amdgpu_ring *ring, uint32_t handle,
struct amdgpu_fence **fence)
struct fence **fence)
{
const unsigned ib_size_dw = 1024;
struct amdgpu_ib ib;
struct amdgpu_ib *ib = NULL;
struct fence *f = NULL;
struct amdgpu_device *adev = ring->adev;
uint64_t dummy;
int i, r;
r = amdgpu_ib_get(ring, NULL, ib_size_dw * 4, &ib);
ib = kzalloc(sizeof(struct amdgpu_ib), GFP_KERNEL);
if (!ib)
return -ENOMEM;
r = amdgpu_ib_get(ring, NULL, ib_size_dw * 4, ib);
if (r) {
DRM_ERROR("amdgpu: failed to get ib (%d).\n", r);
kfree(ib);
return r;
}
dummy = ib.gpu_addr + 1024;
dummy = ib->gpu_addr + 1024;
/* stitch together an VCE create msg */
ib.length_dw = 0;
ib.ptr[ib.length_dw++] = 0x0000000c; /* len */
ib.ptr[ib.length_dw++] = 0x00000001; /* session cmd */
ib.ptr[ib.length_dw++] = handle;
ib->length_dw = 0;
ib->ptr[ib->length_dw++] = 0x0000000c; /* len */
ib->ptr[ib->length_dw++] = 0x00000001; /* session cmd */
ib->ptr[ib->length_dw++] = handle;
ib.ptr[ib.length_dw++] = 0x00000030; /* len */
ib.ptr[ib.length_dw++] = 0x01000001; /* create cmd */
ib.ptr[ib.length_dw++] = 0x00000000;
ib.ptr[ib.length_dw++] = 0x00000042;
ib.ptr[ib.length_dw++] = 0x0000000a;
ib.ptr[ib.length_dw++] = 0x00000001;
ib.ptr[ib.length_dw++] = 0x00000080;
ib.ptr[ib.length_dw++] = 0x00000060;
ib.ptr[ib.length_dw++] = 0x00000100;
ib.ptr[ib.length_dw++] = 0x00000100;
ib.ptr[ib.length_dw++] = 0x0000000c;
ib.ptr[ib.length_dw++] = 0x00000000;
ib->ptr[ib->length_dw++] = 0x00000030; /* len */
ib->ptr[ib->length_dw++] = 0x01000001; /* create cmd */
ib->ptr[ib->length_dw++] = 0x00000000;
ib->ptr[ib->length_dw++] = 0x00000042;
ib->ptr[ib->length_dw++] = 0x0000000a;
ib->ptr[ib->length_dw++] = 0x00000001;
ib->ptr[ib->length_dw++] = 0x00000080;
ib->ptr[ib->length_dw++] = 0x00000060;
ib->ptr[ib->length_dw++] = 0x00000100;
ib->ptr[ib->length_dw++] = 0x00000100;
ib->ptr[ib->length_dw++] = 0x0000000c;
ib->ptr[ib->length_dw++] = 0x00000000;
ib.ptr[ib.length_dw++] = 0x00000014; /* len */
ib.ptr[ib.length_dw++] = 0x05000005; /* feedback buffer */
ib.ptr[ib.length_dw++] = upper_32_bits(dummy);
ib.ptr[ib.length_dw++] = dummy;
ib.ptr[ib.length_dw++] = 0x00000001;
ib->ptr[ib->length_dw++] = 0x00000014; /* len */
ib->ptr[ib->length_dw++] = 0x05000005; /* feedback buffer */
ib->ptr[ib->length_dw++] = upper_32_bits(dummy);
ib->ptr[ib->length_dw++] = dummy;
ib->ptr[ib->length_dw++] = 0x00000001;
for (i = ib.length_dw; i < ib_size_dw; ++i)
ib.ptr[i] = 0x0;
r = amdgpu_ib_schedule(ring->adev, 1, &ib, AMDGPU_FENCE_OWNER_UNDEFINED);
if (r) {
DRM_ERROR("amdgpu: failed to schedule ib (%d).\n", r);
}
for (i = ib->length_dw; i < ib_size_dw; ++i)
ib->ptr[i] = 0x0;
r = amdgpu_sched_ib_submit_kernel_helper(adev, ring, ib, 1,
&amdgpu_vce_free_job,
AMDGPU_FENCE_OWNER_UNDEFINED,
&f);
if (r)
goto err;
if (fence)
*fence = amdgpu_fence_ref(ib.fence);
amdgpu_ib_free(ring->adev, &ib);
*fence = fence_get(f);
fence_put(f);
if (amdgpu_enable_scheduler)
return 0;
err:
amdgpu_ib_free(adev, ib);
kfree(ib);
return r;
}
@ -412,49 +435,59 @@ int amdgpu_vce_get_create_msg(struct amdgpu_ring *ring, uint32_t handle,
* Close up a stream for HW test or if userspace failed to do so
*/
int amdgpu_vce_get_destroy_msg(struct amdgpu_ring *ring, uint32_t handle,
struct amdgpu_fence **fence)
struct fence **fence)
{
const unsigned ib_size_dw = 1024;
struct amdgpu_ib ib;
struct amdgpu_ib *ib = NULL;
struct fence *f = NULL;
struct amdgpu_device *adev = ring->adev;
uint64_t dummy;
int i, r;
r = amdgpu_ib_get(ring, NULL, ib_size_dw * 4, &ib);
ib = kzalloc(sizeof(struct amdgpu_ib), GFP_KERNEL);
if (!ib)
return -ENOMEM;
r = amdgpu_ib_get(ring, NULL, ib_size_dw * 4, ib);
if (r) {
kfree(ib);
DRM_ERROR("amdgpu: failed to get ib (%d).\n", r);
return r;
}
dummy = ib.gpu_addr + 1024;
dummy = ib->gpu_addr + 1024;
/* stitch together an VCE destroy msg */
ib.length_dw = 0;
ib.ptr[ib.length_dw++] = 0x0000000c; /* len */
ib.ptr[ib.length_dw++] = 0x00000001; /* session cmd */
ib.ptr[ib.length_dw++] = handle;
ib->length_dw = 0;
ib->ptr[ib->length_dw++] = 0x0000000c; /* len */
ib->ptr[ib->length_dw++] = 0x00000001; /* session cmd */
ib->ptr[ib->length_dw++] = handle;
ib.ptr[ib.length_dw++] = 0x00000014; /* len */
ib.ptr[ib.length_dw++] = 0x05000005; /* feedback buffer */
ib.ptr[ib.length_dw++] = upper_32_bits(dummy);
ib.ptr[ib.length_dw++] = dummy;
ib.ptr[ib.length_dw++] = 0x00000001;
ib->ptr[ib->length_dw++] = 0x00000014; /* len */
ib->ptr[ib->length_dw++] = 0x05000005; /* feedback buffer */
ib->ptr[ib->length_dw++] = upper_32_bits(dummy);
ib->ptr[ib->length_dw++] = dummy;
ib->ptr[ib->length_dw++] = 0x00000001;
ib.ptr[ib.length_dw++] = 0x00000008; /* len */
ib.ptr[ib.length_dw++] = 0x02000001; /* destroy cmd */
for (i = ib.length_dw; i < ib_size_dw; ++i)
ib.ptr[i] = 0x0;
r = amdgpu_ib_schedule(ring->adev, 1, &ib, AMDGPU_FENCE_OWNER_UNDEFINED);
if (r) {
DRM_ERROR("amdgpu: failed to schedule ib (%d).\n", r);
}
ib->ptr[ib->length_dw++] = 0x00000008; /* len */
ib->ptr[ib->length_dw++] = 0x02000001; /* destroy cmd */
for (i = ib->length_dw; i < ib_size_dw; ++i)
ib->ptr[i] = 0x0;
r = amdgpu_sched_ib_submit_kernel_helper(adev, ring, ib, 1,
&amdgpu_vce_free_job,
AMDGPU_FENCE_OWNER_UNDEFINED,
&f);
if (r)
goto err;
if (fence)
*fence = amdgpu_fence_ref(ib.fence);
amdgpu_ib_free(ring->adev, &ib);
*fence = fence_get(f);
fence_put(f);
if (amdgpu_enable_scheduler)
return 0;
err:
amdgpu_ib_free(adev, ib);
kfree(ib);
return r;
}
@ -800,7 +833,7 @@ int amdgpu_vce_ring_test_ring(struct amdgpu_ring *ring)
*/
int amdgpu_vce_ring_test_ib(struct amdgpu_ring *ring)
{
struct amdgpu_fence *fence = NULL;
struct fence *fence = NULL;
int r;
r = amdgpu_vce_get_create_msg(ring, 1, NULL);
@ -815,13 +848,13 @@ int amdgpu_vce_ring_test_ib(struct amdgpu_ring *ring)
goto error;
}
r = amdgpu_fence_wait(fence, false);
r = fence_wait(fence, false);
if (r) {
DRM_ERROR("amdgpu: fence wait failed (%d).\n", r);
} else {
DRM_INFO("ib test on ring %d succeeded\n", ring->idx);
}
error:
amdgpu_fence_unref(&fence);
fence_put(fence);
return r;
}

View File

@ -29,9 +29,9 @@ int amdgpu_vce_sw_fini(struct amdgpu_device *adev);
int amdgpu_vce_suspend(struct amdgpu_device *adev);
int amdgpu_vce_resume(struct amdgpu_device *adev);
int amdgpu_vce_get_create_msg(struct amdgpu_ring *ring, uint32_t handle,
struct amdgpu_fence **fence);
struct fence **fence);
int amdgpu_vce_get_destroy_msg(struct amdgpu_ring *ring, uint32_t handle,
struct amdgpu_fence **fence);
struct fence **fence);
void amdgpu_vce_free_handles(struct amdgpu_device *adev, struct drm_file *filp);
int amdgpu_vce_ring_parse_cs(struct amdgpu_cs_parser *p, uint32_t ib_idx);
bool amdgpu_vce_ring_emit_semaphore(struct amdgpu_ring *ring,

View File

@ -127,16 +127,16 @@ struct amdgpu_bo_list_entry *amdgpu_vm_get_bos(struct amdgpu_device *adev,
/**
* amdgpu_vm_grab_id - allocate the next free VMID
*
* @ring: ring we want to submit job to
* @vm: vm to allocate id for
* @ring: ring we want to submit job to
* @sync: sync object where we add dependencies
*
* Allocate an id for the vm (cayman+).
* Returns the fence we need to sync to (if any).
* Allocate an id for the vm, adding fences to the sync obj as necessary.
*
* Global and local mutex must be locked!
* Global mutex must be locked!
*/
struct amdgpu_fence *amdgpu_vm_grab_id(struct amdgpu_ring *ring,
struct amdgpu_vm *vm)
int amdgpu_vm_grab_id(struct amdgpu_vm *vm, struct amdgpu_ring *ring,
struct amdgpu_sync *sync)
{
struct amdgpu_fence *best[AMDGPU_MAX_RINGS] = {};
struct amdgpu_vm_id *vm_id = &vm->ids[ring->idx];
@ -148,7 +148,7 @@ struct amdgpu_fence *amdgpu_vm_grab_id(struct amdgpu_ring *ring,
/* check if the id is still valid */
if (vm_id->id && vm_id->last_id_use &&
vm_id->last_id_use == adev->vm_manager.active[vm_id->id])
return NULL;
return 0;
/* we definately need to flush */
vm_id->pd_gpu_addr = ~0ll;
@ -161,7 +161,7 @@ struct amdgpu_fence *amdgpu_vm_grab_id(struct amdgpu_ring *ring,
/* found a free one */
vm_id->id = i;
trace_amdgpu_vm_grab_id(i, ring->idx);
return NULL;
return 0;
}
if (amdgpu_fence_is_earlier(fence, best[fence->ring->idx])) {
@ -172,15 +172,19 @@ struct amdgpu_fence *amdgpu_vm_grab_id(struct amdgpu_ring *ring,
for (i = 0; i < 2; ++i) {
if (choices[i]) {
struct amdgpu_fence *fence;
fence = adev->vm_manager.active[choices[i]];
vm_id->id = choices[i];
trace_amdgpu_vm_grab_id(choices[i], ring->idx);
return adev->vm_manager.active[choices[i]];
return amdgpu_sync_fence(ring->adev, sync, &fence->base);
}
}
/* should never happen */
BUG();
return NULL;
return -EINVAL;
}
/**
@ -200,13 +204,15 @@ void amdgpu_vm_flush(struct amdgpu_ring *ring,
{
uint64_t pd_addr = amdgpu_bo_gpu_offset(vm->page_directory);
struct amdgpu_vm_id *vm_id = &vm->ids[ring->idx];
struct amdgpu_fence *flushed_updates = vm_id->flushed_updates;
if (pd_addr != vm_id->pd_gpu_addr || !vm_id->flushed_updates ||
amdgpu_fence_is_earlier(vm_id->flushed_updates, updates)) {
if (pd_addr != vm_id->pd_gpu_addr || !flushed_updates ||
(updates && amdgpu_fence_is_earlier(flushed_updates, updates))) {
trace_amdgpu_vm_flush(pd_addr, ring->idx, vm_id->id);
amdgpu_fence_unref(&vm_id->flushed_updates);
vm_id->flushed_updates = amdgpu_fence_ref(updates);
vm_id->flushed_updates = amdgpu_fence_ref(
amdgpu_fence_later(flushed_updates, updates));
amdgpu_fence_unref(&flushed_updates);
vm_id->pd_gpu_addr = pd_addr;
amdgpu_ring_emit_vm_flush(ring, vm_id->id, vm_id->pd_gpu_addr);
}
@ -300,6 +306,16 @@ static void amdgpu_vm_update_pages(struct amdgpu_device *adev,
}
}
static int amdgpu_vm_free_job(
struct amdgpu_cs_parser *sched_job)
{
int i;
for (i = 0; i < sched_job->num_ibs; i++)
amdgpu_ib_free(sched_job->adev, &sched_job->ibs[i]);
kfree(sched_job->ibs);
return 0;
}
/**
* amdgpu_vm_clear_bo - initially clear the page dir/table
*
@ -310,7 +326,8 @@ static int amdgpu_vm_clear_bo(struct amdgpu_device *adev,
struct amdgpu_bo *bo)
{
struct amdgpu_ring *ring = adev->vm_manager.vm_pte_funcs_ring;
struct amdgpu_ib ib;
struct fence *fence = NULL;
struct amdgpu_ib *ib;
unsigned entries;
uint64_t addr;
int r;
@ -330,24 +347,33 @@ static int amdgpu_vm_clear_bo(struct amdgpu_device *adev,
addr = amdgpu_bo_gpu_offset(bo);
entries = amdgpu_bo_size(bo) / 8;
r = amdgpu_ib_get(ring, NULL, entries * 2 + 64, &ib);
if (r)
ib = kzalloc(sizeof(struct amdgpu_ib), GFP_KERNEL);
if (!ib)
goto error_unreserve;
ib.length_dw = 0;
amdgpu_vm_update_pages(adev, &ib, addr, 0, entries, 0, 0, 0);
amdgpu_vm_pad_ib(adev, &ib);
WARN_ON(ib.length_dw > 64);
r = amdgpu_ib_schedule(adev, 1, &ib, AMDGPU_FENCE_OWNER_VM);
r = amdgpu_ib_get(ring, NULL, entries * 2 + 64, ib);
if (r)
goto error_free;
amdgpu_bo_fence(bo, ib.fence, true);
ib->length_dw = 0;
amdgpu_vm_update_pages(adev, ib, addr, 0, entries, 0, 0, 0);
amdgpu_vm_pad_ib(adev, ib);
WARN_ON(ib->length_dw > 64);
r = amdgpu_sched_ib_submit_kernel_helper(adev, ring, ib, 1,
&amdgpu_vm_free_job,
AMDGPU_FENCE_OWNER_VM,
&fence);
if (!r)
amdgpu_bo_fence(bo, fence, true);
fence_put(fence);
if (amdgpu_enable_scheduler) {
amdgpu_bo_unreserve(bo);
return 0;
}
error_free:
amdgpu_ib_free(adev, &ib);
amdgpu_ib_free(adev, ib);
kfree(ib);
error_unreserve:
amdgpu_bo_unreserve(bo);
@ -400,7 +426,9 @@ int amdgpu_vm_update_page_directory(struct amdgpu_device *adev,
uint32_t incr = AMDGPU_VM_PTE_COUNT * 8;
uint64_t last_pde = ~0, last_pt = ~0;
unsigned count = 0, pt_idx, ndw;
struct amdgpu_ib ib;
struct amdgpu_ib *ib;
struct fence *fence = NULL;
int r;
/* padding, etc. */
@ -413,10 +441,14 @@ int amdgpu_vm_update_page_directory(struct amdgpu_device *adev,
if (ndw > 0xfffff)
return -ENOMEM;
r = amdgpu_ib_get(ring, NULL, ndw * 4, &ib);
ib = kzalloc(sizeof(struct amdgpu_ib), GFP_KERNEL);
if (!ib)
return -ENOMEM;
r = amdgpu_ib_get(ring, NULL, ndw * 4, ib);
if (r)
return r;
ib.length_dw = 0;
ib->length_dw = 0;
/* walk over the address space and update the page directory */
for (pt_idx = 0; pt_idx <= vm->max_pde_used; ++pt_idx) {
@ -436,7 +468,7 @@ int amdgpu_vm_update_page_directory(struct amdgpu_device *adev,
((last_pt + incr * count) != pt)) {
if (count) {
amdgpu_vm_update_pages(adev, &ib, last_pde,
amdgpu_vm_update_pages(adev, ib, last_pde,
last_pt, count, incr,
AMDGPU_PTE_VALID, 0);
}
@ -450,23 +482,37 @@ int amdgpu_vm_update_page_directory(struct amdgpu_device *adev,
}
if (count)
amdgpu_vm_update_pages(adev, &ib, last_pde, last_pt, count,
amdgpu_vm_update_pages(adev, ib, last_pde, last_pt, count,
incr, AMDGPU_PTE_VALID, 0);
if (ib.length_dw != 0) {
amdgpu_vm_pad_ib(adev, &ib);
amdgpu_sync_resv(adev, &ib.sync, pd->tbo.resv, AMDGPU_FENCE_OWNER_VM);
WARN_ON(ib.length_dw > ndw);
r = amdgpu_ib_schedule(adev, 1, &ib, AMDGPU_FENCE_OWNER_VM);
if (r) {
amdgpu_ib_free(adev, &ib);
return r;
}
amdgpu_bo_fence(pd, ib.fence, true);
if (ib->length_dw != 0) {
amdgpu_vm_pad_ib(adev, ib);
amdgpu_sync_resv(adev, &ib->sync, pd->tbo.resv, AMDGPU_FENCE_OWNER_VM);
WARN_ON(ib->length_dw > ndw);
r = amdgpu_sched_ib_submit_kernel_helper(adev, ring, ib, 1,
&amdgpu_vm_free_job,
AMDGPU_FENCE_OWNER_VM,
&fence);
if (r)
goto error_free;
amdgpu_bo_fence(pd, fence, true);
fence_put(vm->page_directory_fence);
vm->page_directory_fence = fence_get(fence);
fence_put(fence);
}
if (!amdgpu_enable_scheduler || ib->length_dw == 0) {
amdgpu_ib_free(adev, ib);
kfree(ib);
}
amdgpu_ib_free(adev, &ib);
return 0;
error_free:
amdgpu_ib_free(adev, ib);
kfree(ib);
return r;
}
/**
@ -640,7 +686,7 @@ static int amdgpu_vm_update_ptes(struct amdgpu_device *adev,
*/
static void amdgpu_vm_fence_pts(struct amdgpu_vm *vm,
uint64_t start, uint64_t end,
struct amdgpu_fence *fence)
struct fence *fence)
{
unsigned i;
@ -670,12 +716,13 @@ static int amdgpu_vm_bo_update_mapping(struct amdgpu_device *adev,
struct amdgpu_vm *vm,
struct amdgpu_bo_va_mapping *mapping,
uint64_t addr, uint32_t gtt_flags,
struct amdgpu_fence **fence)
struct fence **fence)
{
struct amdgpu_ring *ring = adev->vm_manager.vm_pte_funcs_ring;
unsigned nptes, ncmds, ndw;
uint32_t flags = gtt_flags;
struct amdgpu_ib ib;
struct amdgpu_ib *ib;
struct fence *f = NULL;
int r;
/* normally,bo_va->flags only contians READABLE and WIRTEABLE bit go here
@ -722,46 +769,65 @@ static int amdgpu_vm_bo_update_mapping(struct amdgpu_device *adev,
if (ndw > 0xfffff)
return -ENOMEM;
r = amdgpu_ib_get(ring, NULL, ndw * 4, &ib);
if (r)
ib = kzalloc(sizeof(struct amdgpu_ib), GFP_KERNEL);
if (!ib)
return -ENOMEM;
r = amdgpu_ib_get(ring, NULL, ndw * 4, ib);
if (r) {
kfree(ib);
return r;
ib.length_dw = 0;
}
ib->length_dw = 0;
if (!(flags & AMDGPU_PTE_VALID)) {
unsigned i;
for (i = 0; i < AMDGPU_MAX_RINGS; ++i) {
struct amdgpu_fence *f = vm->ids[i].last_id_use;
amdgpu_sync_fence(&ib.sync, f);
r = amdgpu_sync_fence(adev, &ib->sync, &f->base);
if (r)
return r;
}
}
r = amdgpu_vm_update_ptes(adev, vm, &ib, mapping->it.start,
r = amdgpu_vm_update_ptes(adev, vm, ib, mapping->it.start,
mapping->it.last + 1, addr + mapping->offset,
flags, gtt_flags);
if (r) {
amdgpu_ib_free(adev, &ib);
amdgpu_ib_free(adev, ib);
kfree(ib);
return r;
}
amdgpu_vm_pad_ib(adev, &ib);
WARN_ON(ib.length_dw > ndw);
amdgpu_vm_pad_ib(adev, ib);
WARN_ON(ib->length_dw > ndw);
r = amdgpu_sched_ib_submit_kernel_helper(adev, ring, ib, 1,
&amdgpu_vm_free_job,
AMDGPU_FENCE_OWNER_VM,
&f);
if (r)
goto error_free;
r = amdgpu_ib_schedule(adev, 1, &ib, AMDGPU_FENCE_OWNER_VM);
if (r) {
amdgpu_ib_free(adev, &ib);
return r;
}
amdgpu_vm_fence_pts(vm, mapping->it.start,
mapping->it.last + 1, ib.fence);
mapping->it.last + 1, f);
if (fence) {
amdgpu_fence_unref(fence);
*fence = amdgpu_fence_ref(ib.fence);
fence_put(*fence);
*fence = fence_get(f);
}
fence_put(f);
if (!amdgpu_enable_scheduler) {
amdgpu_ib_free(adev, ib);
kfree(ib);
}
amdgpu_ib_free(adev, &ib);
return 0;
error_free:
amdgpu_ib_free(adev, ib);
kfree(ib);
return r;
}
/**
@ -794,21 +860,25 @@ int amdgpu_vm_bo_update(struct amdgpu_device *adev,
addr = 0;
}
if (addr == bo_va->addr)
return 0;
flags = amdgpu_ttm_tt_pte_flags(adev, bo_va->bo->tbo.ttm, mem);
list_for_each_entry(mapping, &bo_va->mappings, list) {
spin_lock(&vm->status_lock);
if (!list_empty(&bo_va->vm_status))
list_splice_init(&bo_va->valids, &bo_va->invalids);
spin_unlock(&vm->status_lock);
list_for_each_entry(mapping, &bo_va->invalids, list) {
r = amdgpu_vm_bo_update_mapping(adev, vm, mapping, addr,
flags, &bo_va->last_pt_update);
if (r)
return r;
}
bo_va->addr = addr;
spin_lock(&vm->status_lock);
list_splice_init(&bo_va->invalids, &bo_va->valids);
list_del_init(&bo_va->vm_status);
if (!mem)
list_add(&bo_va->vm_status, &vm->cleared);
spin_unlock(&vm->status_lock);
return 0;
@ -861,7 +931,7 @@ int amdgpu_vm_clear_invalids(struct amdgpu_device *adev,
struct amdgpu_vm *vm, struct amdgpu_sync *sync)
{
struct amdgpu_bo_va *bo_va = NULL;
int r;
int r = 0;
spin_lock(&vm->status_lock);
while (!list_empty(&vm->invalidated)) {
@ -878,8 +948,9 @@ int amdgpu_vm_clear_invalids(struct amdgpu_device *adev,
spin_unlock(&vm->status_lock);
if (bo_va)
amdgpu_sync_fence(sync, bo_va->last_pt_update);
return 0;
r = amdgpu_sync_fence(adev, sync, bo_va->last_pt_update);
return r;
}
/**
@ -907,10 +978,10 @@ struct amdgpu_bo_va *amdgpu_vm_bo_add(struct amdgpu_device *adev,
}
bo_va->vm = vm;
bo_va->bo = bo;
bo_va->addr = 0;
bo_va->ref_count = 1;
INIT_LIST_HEAD(&bo_va->bo_list);
INIT_LIST_HEAD(&bo_va->mappings);
INIT_LIST_HEAD(&bo_va->valids);
INIT_LIST_HEAD(&bo_va->invalids);
INIT_LIST_HEAD(&bo_va->vm_status);
mutex_lock(&vm->mutex);
@ -999,12 +1070,10 @@ int amdgpu_vm_bo_map(struct amdgpu_device *adev,
mapping->offset = offset;
mapping->flags = flags;
list_add(&mapping->list, &bo_va->mappings);
list_add(&mapping->list, &bo_va->invalids);
interval_tree_insert(&mapping->it, &vm->va);
trace_amdgpu_vm_bo_map(bo_va, mapping);
bo_va->addr = 0;
/* Make sure the page tables are allocated */
saddr >>= amdgpu_vm_block_size;
eaddr >>= amdgpu_vm_block_size;
@ -1085,17 +1154,27 @@ int amdgpu_vm_bo_unmap(struct amdgpu_device *adev,
{
struct amdgpu_bo_va_mapping *mapping;
struct amdgpu_vm *vm = bo_va->vm;
bool valid = true;
saddr /= AMDGPU_GPU_PAGE_SIZE;
list_for_each_entry(mapping, &bo_va->mappings, list) {
list_for_each_entry(mapping, &bo_va->valids, list) {
if (mapping->it.start == saddr)
break;
}
if (&mapping->list == &bo_va->mappings) {
amdgpu_bo_unreserve(bo_va->bo);
return -ENOENT;
if (&mapping->list == &bo_va->valids) {
valid = false;
list_for_each_entry(mapping, &bo_va->invalids, list) {
if (mapping->it.start == saddr)
break;
}
if (&mapping->list == &bo_va->invalids) {
amdgpu_bo_unreserve(bo_va->bo);
return -ENOENT;
}
}
mutex_lock(&vm->mutex);
@ -1103,12 +1182,10 @@ int amdgpu_vm_bo_unmap(struct amdgpu_device *adev,
interval_tree_remove(&mapping->it, &vm->va);
trace_amdgpu_vm_bo_unmap(bo_va, mapping);
if (bo_va->addr) {
/* clear the old address */
if (valid)
list_add(&mapping->list, &vm->freed);
} else {
else
kfree(mapping);
}
mutex_unlock(&vm->mutex);
amdgpu_bo_unreserve(bo_va->bo);
@ -1139,16 +1216,19 @@ void amdgpu_vm_bo_rmv(struct amdgpu_device *adev,
list_del(&bo_va->vm_status);
spin_unlock(&vm->status_lock);
list_for_each_entry_safe(mapping, next, &bo_va->mappings, list) {
list_for_each_entry_safe(mapping, next, &bo_va->valids, list) {
list_del(&mapping->list);
interval_tree_remove(&mapping->it, &vm->va);
trace_amdgpu_vm_bo_unmap(bo_va, mapping);
if (bo_va->addr)
list_add(&mapping->list, &vm->freed);
else
kfree(mapping);
list_add(&mapping->list, &vm->freed);
}
amdgpu_fence_unref(&bo_va->last_pt_update);
list_for_each_entry_safe(mapping, next, &bo_va->invalids, list) {
list_del(&mapping->list);
interval_tree_remove(&mapping->it, &vm->va);
kfree(mapping);
}
fence_put(bo_va->last_pt_update);
kfree(bo_va);
mutex_unlock(&vm->mutex);
@ -1169,12 +1249,10 @@ void amdgpu_vm_bo_invalidate(struct amdgpu_device *adev,
struct amdgpu_bo_va *bo_va;
list_for_each_entry(bo_va, &bo->va, bo_list) {
if (bo_va->addr) {
spin_lock(&bo_va->vm->status_lock);
list_del(&bo_va->vm_status);
spin_lock(&bo_va->vm->status_lock);
if (list_empty(&bo_va->vm_status))
list_add(&bo_va->vm_status, &bo_va->vm->invalidated);
spin_unlock(&bo_va->vm->status_lock);
}
spin_unlock(&bo_va->vm->status_lock);
}
}
@ -1202,6 +1280,7 @@ int amdgpu_vm_init(struct amdgpu_device *adev, struct amdgpu_vm *vm)
vm->va = RB_ROOT;
spin_lock_init(&vm->status_lock);
INIT_LIST_HEAD(&vm->invalidated);
INIT_LIST_HEAD(&vm->cleared);
INIT_LIST_HEAD(&vm->freed);
pd_size = amdgpu_vm_directory_size(adev);
@ -1215,6 +1294,8 @@ int amdgpu_vm_init(struct amdgpu_device *adev, struct amdgpu_vm *vm)
return -ENOMEM;
}
vm->page_directory_fence = NULL;
r = amdgpu_bo_create(adev, pd_size, align, true,
AMDGPU_GEM_DOMAIN_VRAM, 0,
NULL, &vm->page_directory);
@ -1263,6 +1344,7 @@ void amdgpu_vm_fini(struct amdgpu_device *adev, struct amdgpu_vm *vm)
kfree(vm->page_tables);
amdgpu_bo_unref(&vm->page_directory);
fence_put(vm->page_directory_fence);
for (i = 0; i < AMDGPU_MAX_RINGS; ++i) {
amdgpu_fence_unref(&vm->ids[i].flushed_updates);

View File

@ -812,7 +812,7 @@ amdgpu_atombios_encoder_setup_dig_transmitter(struct drm_encoder *encoder, int a
else
args.v1.ucConfig |= ATOM_TRANSMITTER_CONFIG_DIG1_ENCODER;
if ((adev->flags & AMDGPU_IS_APU) &&
if ((adev->flags & AMD_IS_APU) &&
(amdgpu_encoder->encoder_id == ENCODER_OBJECT_ID_INTERNAL_UNIPHY)) {
if (is_dp ||
!amdgpu_dig_monitor_is_duallink(encoder, amdgpu_encoder->pixel_clock)) {

View File

@ -838,7 +838,7 @@ static u32 cik_get_xclk(struct amdgpu_device *adev)
{
u32 reference_clock = adev->clock.spll.reference_freq;
if (adev->flags & AMDGPU_IS_APU) {
if (adev->flags & AMD_IS_APU) {
if (RREG32_SMC(ixGENERAL_PWRMGT) & GENERAL_PWRMGT__GPU_COUNTER_CLK_MASK)
return reference_clock / 2;
} else {
@ -1235,7 +1235,7 @@ static void cik_gpu_soft_reset(struct amdgpu_device *adev, u32 reset_mask)
if (reset_mask & AMDGPU_RESET_VMC)
srbm_soft_reset |= SRBM_SOFT_RESET__SOFT_RESET_VMC_MASK;
if (!(adev->flags & AMDGPU_IS_APU)) {
if (!(adev->flags & AMD_IS_APU)) {
if (reset_mask & AMDGPU_RESET_MC)
srbm_soft_reset |= SRBM_SOFT_RESET__SOFT_RESET_MC_MASK;
}
@ -1411,7 +1411,7 @@ static void cik_gpu_pci_config_reset(struct amdgpu_device *adev)
dev_warn(adev->dev, "Wait for MC idle timed out !\n");
}
if (adev->flags & AMDGPU_IS_APU)
if (adev->flags & AMD_IS_APU)
kv_save_regs_for_reset(adev, &kv_save);
/* disable BM */
@ -1429,7 +1429,7 @@ static void cik_gpu_pci_config_reset(struct amdgpu_device *adev)
}
/* does asic init need to be run first??? */
if (adev->flags & AMDGPU_IS_APU)
if (adev->flags & AMD_IS_APU)
kv_restore_regs_for_reset(adev, &kv_save);
}
@ -1570,7 +1570,7 @@ static void cik_pcie_gen3_enable(struct amdgpu_device *adev)
if (amdgpu_pcie_gen2 == 0)
return;
if (adev->flags & AMDGPU_IS_APU)
if (adev->flags & AMD_IS_APU)
return;
ret = drm_pcie_get_speed_cap_mask(adev->ddev, &mask);
@ -1730,7 +1730,7 @@ static void cik_program_aspm(struct amdgpu_device *adev)
return;
/* XXX double check APUs */
if (adev->flags & AMDGPU_IS_APU)
if (adev->flags & AMD_IS_APU)
return;
orig = data = RREG32_PCIE(ixPCIE_LC_N_FTS_CNTL);

View File

@ -614,6 +614,7 @@ static int cik_sdma_ring_test_ib(struct amdgpu_ring *ring)
{
struct amdgpu_device *adev = ring->adev;
struct amdgpu_ib ib;
struct fence *f = NULL;
unsigned i;
unsigned index;
int r;
@ -629,12 +630,10 @@ static int cik_sdma_ring_test_ib(struct amdgpu_ring *ring)
gpu_addr = adev->wb.gpu_addr + (index * 4);
tmp = 0xCAFEDEAD;
adev->wb.wb[index] = cpu_to_le32(tmp);
r = amdgpu_ib_get(ring, NULL, 256, &ib);
if (r) {
amdgpu_wb_free(adev, index);
DRM_ERROR("amdgpu: failed to get ib (%d).\n", r);
return r;
goto err0;
}
ib.ptr[0] = SDMA_PACKET(SDMA_OPCODE_WRITE, SDMA_WRITE_SUB_OPCODE_LINEAR, 0);
@ -643,20 +642,16 @@ static int cik_sdma_ring_test_ib(struct amdgpu_ring *ring)
ib.ptr[3] = 1;
ib.ptr[4] = 0xDEADBEEF;
ib.length_dw = 5;
r = amdgpu_sched_ib_submit_kernel_helper(adev, ring, &ib, 1, NULL,
AMDGPU_FENCE_OWNER_UNDEFINED,
&f);
if (r)
goto err1;
r = amdgpu_ib_schedule(adev, 1, &ib, AMDGPU_FENCE_OWNER_UNDEFINED);
r = fence_wait(f, false);
if (r) {
amdgpu_ib_free(adev, &ib);
amdgpu_wb_free(adev, index);
DRM_ERROR("amdgpu: failed to schedule ib (%d).\n", r);
return r;
}
r = amdgpu_fence_wait(ib.fence, false);
if (r) {
amdgpu_ib_free(adev, &ib);
amdgpu_wb_free(adev, index);
DRM_ERROR("amdgpu: fence wait failed (%d).\n", r);
return r;
goto err1;
}
for (i = 0; i < adev->usec_timeout; i++) {
tmp = le32_to_cpu(adev->wb.wb[index]);
@ -666,12 +661,17 @@ static int cik_sdma_ring_test_ib(struct amdgpu_ring *ring)
}
if (i < adev->usec_timeout) {
DRM_INFO("ib test on ring %d succeeded in %u usecs\n",
ib.fence->ring->idx, i);
ring->idx, i);
goto err1;
} else {
DRM_ERROR("amdgpu: ib test failed (0x%08X)\n", tmp);
r = -EINVAL;
}
err1:
fence_put(f);
amdgpu_ib_free(adev, &ib);
err0:
amdgpu_wb_free(adev, index);
return r;
}
@ -1404,5 +1404,6 @@ static void cik_sdma_set_vm_pte_funcs(struct amdgpu_device *adev)
if (adev->vm_manager.vm_pte_funcs == NULL) {
adev->vm_manager.vm_pte_funcs = &cik_sdma_vm_pte_funcs;
adev->vm_manager.vm_pte_funcs_ring = &adev->sdma[0].ring;
adev->vm_manager.vm_pte_funcs_ring->is_pte_ring = true;
}
}

View File

@ -126,9 +126,31 @@ static const u32 tonga_mgcg_cgcg_init[] =
mmXDMA_MEM_POWER_CNTL, 0x00000101, 0x00000000,
};
static const u32 golden_settings_fiji_a10[] =
{
mmDCI_CLK_CNTL, 0x00000080, 0x00000000,
mmFBC_DEBUG_COMP, 0x000000f0, 0x00000070,
mmFBC_MISC, 0x1f311fff, 0x12300000,
mmHDMI_CONTROL, 0x31000111, 0x00000011,
};
static const u32 fiji_mgcg_cgcg_init[] =
{
mmXDMA_CLOCK_GATING_CNTL, 0xffffffff, 0x00000100,
mmXDMA_MEM_POWER_CNTL, 0x00000101, 0x00000000,
};
static void dce_v10_0_init_golden_registers(struct amdgpu_device *adev)
{
switch (adev->asic_type) {
case CHIP_FIJI:
amdgpu_program_register_sequence(adev,
fiji_mgcg_cgcg_init,
(const u32)ARRAY_SIZE(fiji_mgcg_cgcg_init));
amdgpu_program_register_sequence(adev,
golden_settings_fiji_a10,
(const u32)ARRAY_SIZE(golden_settings_fiji_a10));
break;
case CHIP_TONGA:
amdgpu_program_register_sequence(adev,
tonga_mgcg_cgcg_init,
@ -803,11 +825,11 @@ static u32 dce_v10_0_line_buffer_adjust(struct amdgpu_device *adev,
buffer_alloc = 2;
} else if (mode->crtc_hdisplay < 4096) {
mem_cfg = 0;
buffer_alloc = (adev->flags & AMDGPU_IS_APU) ? 2 : 4;
buffer_alloc = (adev->flags & AMD_IS_APU) ? 2 : 4;
} else {
DRM_DEBUG_KMS("Mode too big for LB!\n");
mem_cfg = 0;
buffer_alloc = (adev->flags & AMDGPU_IS_APU) ? 2 : 4;
buffer_alloc = (adev->flags & AMD_IS_APU) ? 2 : 4;
}
} else {
mem_cfg = 1;
@ -2888,6 +2910,7 @@ static int dce_v10_0_early_init(void *handle)
dce_v10_0_set_irq_funcs(adev);
switch (adev->asic_type) {
case CHIP_FIJI:
case CHIP_TONGA:
adev->mode_info.num_crtc = 6; /* XXX 7??? */
adev->mode_info.num_hpd = 6;

View File

@ -801,11 +801,11 @@ static u32 dce_v11_0_line_buffer_adjust(struct amdgpu_device *adev,
buffer_alloc = 2;
} else if (mode->crtc_hdisplay < 4096) {
mem_cfg = 0;
buffer_alloc = (adev->flags & AMDGPU_IS_APU) ? 2 : 4;
buffer_alloc = (adev->flags & AMD_IS_APU) ? 2 : 4;
} else {
DRM_DEBUG_KMS("Mode too big for LB!\n");
mem_cfg = 0;
buffer_alloc = (adev->flags & AMDGPU_IS_APU) ? 2 : 4;
buffer_alloc = (adev->flags & AMD_IS_APU) ? 2 : 4;
}
} else {
mem_cfg = 1;

View File

@ -770,11 +770,11 @@ static u32 dce_v8_0_line_buffer_adjust(struct amdgpu_device *adev,
buffer_alloc = 2;
} else if (mode->crtc_hdisplay < 4096) {
tmp = 0;
buffer_alloc = (adev->flags & AMDGPU_IS_APU) ? 2 : 4;
buffer_alloc = (adev->flags & AMD_IS_APU) ? 2 : 4;
} else {
DRM_DEBUG_KMS("Mode too big for LB!\n");
tmp = 0;
buffer_alloc = (adev->flags & AMDGPU_IS_APU) ? 2 : 4;
buffer_alloc = (adev->flags & AMD_IS_APU) ? 2 : 4;
}
} else {
tmp = 1;

View File

@ -0,0 +1,181 @@
/*
* Copyright 2014 Advanced Micro Devices, Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*
*/
#include <linux/firmware.h>
#include "drmP.h"
#include "amdgpu.h"
#include "fiji_smumgr.h"
MODULE_FIRMWARE("amdgpu/fiji_smc.bin");
static void fiji_dpm_set_funcs(struct amdgpu_device *adev);
static int fiji_dpm_early_init(void *handle)
{
struct amdgpu_device *adev = (struct amdgpu_device *)handle;
fiji_dpm_set_funcs(adev);
return 0;
}
static int fiji_dpm_init_microcode(struct amdgpu_device *adev)
{
char fw_name[30] = "amdgpu/fiji_smc.bin";
int err;
err = request_firmware(&adev->pm.fw, fw_name, adev->dev);
if (err)
goto out;
err = amdgpu_ucode_validate(adev->pm.fw);
out:
if (err) {
DRM_ERROR("Failed to load firmware \"%s\"", fw_name);
release_firmware(adev->pm.fw);
adev->pm.fw = NULL;
}
return err;
}
static int fiji_dpm_sw_init(void *handle)
{
int ret;
struct amdgpu_device *adev = (struct amdgpu_device *)handle;
ret = fiji_dpm_init_microcode(adev);
if (ret)
return ret;
return 0;
}
static int fiji_dpm_sw_fini(void *handle)
{
return 0;
}
static int fiji_dpm_hw_init(void *handle)
{
int ret;
struct amdgpu_device *adev = (struct amdgpu_device *)handle;
mutex_lock(&adev->pm.mutex);
ret = fiji_smu_init(adev);
if (ret) {
DRM_ERROR("SMU initialization failed\n");
goto fail;
}
ret = fiji_smu_start(adev);
if (ret) {
DRM_ERROR("SMU start failed\n");
goto fail;
}
mutex_unlock(&adev->pm.mutex);
return 0;
fail:
adev->firmware.smu_load = false;
mutex_unlock(&adev->pm.mutex);
return -EINVAL;
}
static int fiji_dpm_hw_fini(void *handle)
{
struct amdgpu_device *adev = (struct amdgpu_device *)handle;
mutex_lock(&adev->pm.mutex);
fiji_smu_fini(adev);
mutex_unlock(&adev->pm.mutex);
return 0;
}
static int fiji_dpm_suspend(void *handle)
{
struct amdgpu_device *adev = (struct amdgpu_device *)handle;
fiji_dpm_hw_fini(adev);
return 0;
}
static int fiji_dpm_resume(void *handle)
{
struct amdgpu_device *adev = (struct amdgpu_device *)handle;
fiji_dpm_hw_init(adev);
return 0;
}
static int fiji_dpm_set_clockgating_state(void *handle,
enum amd_clockgating_state state)
{
return 0;
}
static int fiji_dpm_set_powergating_state(void *handle,
enum amd_powergating_state state)
{
return 0;
}
const struct amd_ip_funcs fiji_dpm_ip_funcs = {
.early_init = fiji_dpm_early_init,
.late_init = NULL,
.sw_init = fiji_dpm_sw_init,
.sw_fini = fiji_dpm_sw_fini,
.hw_init = fiji_dpm_hw_init,
.hw_fini = fiji_dpm_hw_fini,
.suspend = fiji_dpm_suspend,
.resume = fiji_dpm_resume,
.is_idle = NULL,
.wait_for_idle = NULL,
.soft_reset = NULL,
.print_status = NULL,
.set_clockgating_state = fiji_dpm_set_clockgating_state,
.set_powergating_state = fiji_dpm_set_powergating_state,
};
static const struct amdgpu_dpm_funcs fiji_dpm_funcs = {
.get_temperature = NULL,
.pre_set_power_state = NULL,
.set_power_state = NULL,
.post_set_power_state = NULL,
.display_configuration_changed = NULL,
.get_sclk = NULL,
.get_mclk = NULL,
.print_power_state = NULL,
.debugfs_print_current_performance_level = NULL,
.force_performance_level = NULL,
.vblank_too_short = NULL,
.powergate_uvd = NULL,
};
static void fiji_dpm_set_funcs(struct amdgpu_device *adev)
{
if (NULL == adev->pm.funcs)
adev->pm.funcs = &fiji_dpm_funcs;
}

View File

@ -0,0 +1,182 @@
/*
* Copyright 2014 Advanced Micro Devices, Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*
*/
#ifndef FIJI_PP_SMC_H
#define FIJI_PP_SMC_H
#pragma pack(push, 1)
#define PPSMC_SWSTATE_FLAG_DC 0x01
#define PPSMC_SWSTATE_FLAG_UVD 0x02
#define PPSMC_SWSTATE_FLAG_VCE 0x04
#define PPSMC_THERMAL_PROTECT_TYPE_INTERNAL 0x00
#define PPSMC_THERMAL_PROTECT_TYPE_EXTERNAL 0x01
#define PPSMC_THERMAL_PROTECT_TYPE_NONE 0xff
#define PPSMC_SYSTEMFLAG_GPIO_DC 0x01
#define PPSMC_SYSTEMFLAG_STEPVDDC 0x02
#define PPSMC_SYSTEMFLAG_GDDR5 0x04
#define PPSMC_SYSTEMFLAG_DISABLE_BABYSTEP 0x08
#define PPSMC_SYSTEMFLAG_REGULATOR_HOT 0x10
#define PPSMC_SYSTEMFLAG_REGULATOR_HOT_ANALOG 0x20
#define PPSMC_EXTRAFLAGS_AC2DC_ACTION_MASK 0x07
#define PPSMC_EXTRAFLAGS_AC2DC_DONT_WAIT_FOR_VBLANK 0x08
#define PPSMC_EXTRAFLAGS_AC2DC_ACTION_GOTODPMLOWSTATE 0x00
#define PPSMC_EXTRAFLAGS_AC2DC_ACTION_GOTOINITIALSTATE 0x01
#define PPSMC_DPM2FLAGS_TDPCLMP 0x01
#define PPSMC_DPM2FLAGS_PWRSHFT 0x02
#define PPSMC_DPM2FLAGS_OCP 0x04
#define PPSMC_DISPLAY_WATERMARK_LOW 0
#define PPSMC_DISPLAY_WATERMARK_HIGH 1
#define PPSMC_STATEFLAG_AUTO_PULSE_SKIP 0x01
#define PPSMC_STATEFLAG_POWERBOOST 0x02
#define PPSMC_STATEFLAG_PSKIP_ON_TDP_FAULT 0x04
#define PPSMC_STATEFLAG_POWERSHIFT 0x08
#define PPSMC_STATEFLAG_SLOW_READ_MARGIN 0x10
#define PPSMC_STATEFLAG_DEEPSLEEP_THROTTLE 0x20
#define PPSMC_STATEFLAG_DEEPSLEEP_BYPASS 0x40
#define FDO_MODE_HARDWARE 0
#define FDO_MODE_PIECE_WISE_LINEAR 1
enum FAN_CONTROL {
FAN_CONTROL_FUZZY,
FAN_CONTROL_TABLE
};
//Gemini Modes
#define PPSMC_GeminiModeNone 0 //Single GPU board
#define PPSMC_GeminiModeMaster 1 //Master GPU on a Gemini board
#define PPSMC_GeminiModeSlave 2 //Slave GPU on a Gemini board
#define PPSMC_Result_OK ((uint16_t)0x01)
#define PPSMC_Result_NoMore ((uint16_t)0x02)
#define PPSMC_Result_NotNow ((uint16_t)0x03)
#define PPSMC_Result_Failed ((uint16_t)0xFF)
#define PPSMC_Result_UnknownCmd ((uint16_t)0xFE)
#define PPSMC_Result_UnknownVT ((uint16_t)0xFD)
typedef uint16_t PPSMC_Result;
#define PPSMC_isERROR(x) ((uint16_t)0x80 & (x))
#define PPSMC_MSG_Halt ((uint16_t)0x10)
#define PPSMC_MSG_Resume ((uint16_t)0x11)
#define PPSMC_MSG_EnableDPMLevel ((uint16_t)0x12)
#define PPSMC_MSG_ZeroLevelsDisabled ((uint16_t)0x13)
#define PPSMC_MSG_OneLevelsDisabled ((uint16_t)0x14)
#define PPSMC_MSG_TwoLevelsDisabled ((uint16_t)0x15)
#define PPSMC_MSG_EnableThermalInterrupt ((uint16_t)0x16)
#define PPSMC_MSG_RunningOnAC ((uint16_t)0x17)
#define PPSMC_MSG_LevelUp ((uint16_t)0x18)
#define PPSMC_MSG_LevelDown ((uint16_t)0x19)
#define PPSMC_MSG_ResetDPMCounters ((uint16_t)0x1a)
#define PPSMC_MSG_SwitchToSwState ((uint16_t)0x20)
#define PPSMC_MSG_SwitchToSwStateLast ((uint16_t)0x3f)
#define PPSMC_MSG_SwitchToInitialState ((uint16_t)0x40)
#define PPSMC_MSG_NoForcedLevel ((uint16_t)0x41)
#define PPSMC_MSG_ForceHigh ((uint16_t)0x42)
#define PPSMC_MSG_ForceMediumOrHigh ((uint16_t)0x43)
#define PPSMC_MSG_SwitchToMinimumPower ((uint16_t)0x51)
#define PPSMC_MSG_ResumeFromMinimumPower ((uint16_t)0x52)
#define PPSMC_MSG_EnableCac ((uint16_t)0x53)
#define PPSMC_MSG_DisableCac ((uint16_t)0x54)
#define PPSMC_DPMStateHistoryStart ((uint16_t)0x55)
#define PPSMC_DPMStateHistoryStop ((uint16_t)0x56)
#define PPSMC_CACHistoryStart ((uint16_t)0x57)
#define PPSMC_CACHistoryStop ((uint16_t)0x58)
#define PPSMC_TDPClampingActive ((uint16_t)0x59)
#define PPSMC_TDPClampingInactive ((uint16_t)0x5A)
#define PPSMC_StartFanControl ((uint16_t)0x5B)
#define PPSMC_StopFanControl ((uint16_t)0x5C)
#define PPSMC_NoDisplay ((uint16_t)0x5D)
#define PPSMC_HasDisplay ((uint16_t)0x5E)
#define PPSMC_MSG_UVDPowerOFF ((uint16_t)0x60)
#define PPSMC_MSG_UVDPowerON ((uint16_t)0x61)
#define PPSMC_MSG_EnableULV ((uint16_t)0x62)
#define PPSMC_MSG_DisableULV ((uint16_t)0x63)
#define PPSMC_MSG_EnterULV ((uint16_t)0x64)
#define PPSMC_MSG_ExitULV ((uint16_t)0x65)
#define PPSMC_PowerShiftActive ((uint16_t)0x6A)
#define PPSMC_PowerShiftInactive ((uint16_t)0x6B)
#define PPSMC_OCPActive ((uint16_t)0x6C)
#define PPSMC_OCPInactive ((uint16_t)0x6D)
#define PPSMC_CACLongTermAvgEnable ((uint16_t)0x6E)
#define PPSMC_CACLongTermAvgDisable ((uint16_t)0x6F)
#define PPSMC_MSG_InferredStateSweep_Start ((uint16_t)0x70)
#define PPSMC_MSG_InferredStateSweep_Stop ((uint16_t)0x71)
#define PPSMC_MSG_SwitchToLowestInfState ((uint16_t)0x72)
#define PPSMC_MSG_SwitchToNonInfState ((uint16_t)0x73)
#define PPSMC_MSG_AllStateSweep_Start ((uint16_t)0x74)
#define PPSMC_MSG_AllStateSweep_Stop ((uint16_t)0x75)
#define PPSMC_MSG_SwitchNextLowerInfState ((uint16_t)0x76)
#define PPSMC_MSG_SwitchNextHigherInfState ((uint16_t)0x77)
#define PPSMC_MSG_MclkRetrainingTest ((uint16_t)0x78)
#define PPSMC_MSG_ForceTDPClamping ((uint16_t)0x79)
#define PPSMC_MSG_CollectCAC_PowerCorreln ((uint16_t)0x7A)
#define PPSMC_MSG_CollectCAC_WeightCalib ((uint16_t)0x7B)
#define PPSMC_MSG_CollectCAC_SQonly ((uint16_t)0x7C)
#define PPSMC_MSG_CollectCAC_TemperaturePwr ((uint16_t)0x7D)
#define PPSMC_MSG_ExtremitiesTest_Start ((uint16_t)0x7E)
#define PPSMC_MSG_ExtremitiesTest_Stop ((uint16_t)0x7F)
#define PPSMC_FlushDataCache ((uint16_t)0x80)
#define PPSMC_FlushInstrCache ((uint16_t)0x81)
#define PPSMC_MSG_SetEnabledLevels ((uint16_t)0x82)
#define PPSMC_MSG_SetForcedLevels ((uint16_t)0x83)
#define PPSMC_MSG_ResetToDefaults ((uint16_t)0x84)
#define PPSMC_MSG_SetForcedLevelsAndJump ((uint16_t)0x85)
#define PPSMC_MSG_SetCACHistoryMode ((uint16_t)0x86)
#define PPSMC_MSG_EnableDTE ((uint16_t)0x87)
#define PPSMC_MSG_DisableDTE ((uint16_t)0x88)
#define PPSMC_MSG_SmcSpaceSetAddress ((uint16_t)0x89)
#define PPSMC_MSG_SmcSpaceWriteDWordInc ((uint16_t)0x8A)
#define PPSMC_MSG_SmcSpaceWriteWordInc ((uint16_t)0x8B)
#define PPSMC_MSG_SmcSpaceWriteByteInc ((uint16_t)0x8C)
#define PPSMC_MSG_BREAK ((uint16_t)0xF8)
#define PPSMC_MSG_Test ((uint16_t)0x100)
#define PPSMC_MSG_DRV_DRAM_ADDR_HI ((uint16_t)0x250)
#define PPSMC_MSG_DRV_DRAM_ADDR_LO ((uint16_t)0x251)
#define PPSMC_MSG_SMU_DRAM_ADDR_HI ((uint16_t)0x252)
#define PPSMC_MSG_SMU_DRAM_ADDR_LO ((uint16_t)0x253)
#define PPSMC_MSG_LoadUcodes ((uint16_t)0x254)
typedef uint16_t PPSMC_Msg;
#define PPSMC_EVENT_STATUS_THERMAL 0x00000001
#define PPSMC_EVENT_STATUS_REGULATORHOT 0x00000002
#define PPSMC_EVENT_STATUS_DC 0x00000004
#define PPSMC_EVENT_STATUS_GPIO17 0x00000008
#pragma pack(pop)
#endif

View File

@ -0,0 +1,853 @@
/*
* Copyright 2014 Advanced Micro Devices, Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*
*/
#include <linux/firmware.h>
#include "drmP.h"
#include "amdgpu.h"
#include "fiji_ppsmc.h"
#include "fiji_smumgr.h"
#include "smu_ucode_xfer_vi.h"
#include "amdgpu_ucode.h"
#include "smu/smu_7_1_3_d.h"
#include "smu/smu_7_1_3_sh_mask.h"
#define FIJI_SMC_SIZE 0x20000
static int fiji_set_smc_sram_address(struct amdgpu_device *adev, uint32_t smc_address, uint32_t limit)
{
uint32_t val;
if (smc_address & 3)
return -EINVAL;
if ((smc_address + 3) > limit)
return -EINVAL;
WREG32(mmSMC_IND_INDEX_0, smc_address);
val = RREG32(mmSMC_IND_ACCESS_CNTL);
val = REG_SET_FIELD(val, SMC_IND_ACCESS_CNTL, AUTO_INCREMENT_IND_0, 0);
WREG32(mmSMC_IND_ACCESS_CNTL, val);
return 0;
}
static int fiji_copy_bytes_to_smc(struct amdgpu_device *adev, uint32_t smc_start_address, const uint8_t *src, uint32_t byte_count, uint32_t limit)
{
uint32_t addr;
uint32_t data, orig_data;
int result = 0;
uint32_t extra_shift;
unsigned long flags;
if (smc_start_address & 3)
return -EINVAL;
if ((smc_start_address + byte_count) > limit)
return -EINVAL;
addr = smc_start_address;
spin_lock_irqsave(&adev->smc_idx_lock, flags);
while (byte_count >= 4) {
/* Bytes are written into the SMC addres space with the MSB first */
data = (src[0] << 24) + (src[1] << 16) + (src[2] << 8) + src[3];
result = fiji_set_smc_sram_address(adev, addr, limit);
if (result)
goto out;
WREG32(mmSMC_IND_DATA_0, data);
src += 4;
byte_count -= 4;
addr += 4;
}
if (0 != byte_count) {
/* Now write odd bytes left, do a read modify write cycle */
data = 0;
result = fiji_set_smc_sram_address(adev, addr, limit);
if (result)
goto out;
orig_data = RREG32(mmSMC_IND_DATA_0);
extra_shift = 8 * (4 - byte_count);
while (byte_count > 0) {
data = (data << 8) + *src++;
byte_count--;
}
data <<= extra_shift;
data |= (orig_data & ~((~0UL) << extra_shift));
result = fiji_set_smc_sram_address(adev, addr, limit);
if (result)
goto out;
WREG32(mmSMC_IND_DATA_0, data);
}
out:
spin_unlock_irqrestore(&adev->smc_idx_lock, flags);
return result;
}
static int fiji_program_jump_on_start(struct amdgpu_device *adev)
{
static unsigned char data[] = {0xE0, 0x00, 0x80, 0x40};
fiji_copy_bytes_to_smc(adev, 0x0, data, 4, sizeof(data)+1);
return 0;
}
static bool fiji_is_smc_ram_running(struct amdgpu_device *adev)
{
uint32_t val = RREG32_SMC(ixSMC_SYSCON_CLOCK_CNTL_0);
val = REG_GET_FIELD(val, SMC_SYSCON_CLOCK_CNTL_0, ck_disable);
return ((0 == val) && (0x20100 <= RREG32_SMC(ixSMC_PC_C)));
}
static int wait_smu_response(struct amdgpu_device *adev)
{
int i;
uint32_t val;
for (i = 0; i < adev->usec_timeout; i++) {
val = RREG32(mmSMC_RESP_0);
if (REG_GET_FIELD(val, SMC_RESP_0, SMC_RESP))
break;
udelay(1);
}
if (i == adev->usec_timeout)
return -EINVAL;
return 0;
}
static int fiji_send_msg_to_smc_offset(struct amdgpu_device *adev)
{
if (wait_smu_response(adev)) {
DRM_ERROR("Failed to send previous message\n");
return -EINVAL;
}
WREG32(mmSMC_MSG_ARG_0, 0x20000);
WREG32(mmSMC_MESSAGE_0, PPSMC_MSG_Test);
if (wait_smu_response(adev)) {
DRM_ERROR("Failed to send message\n");
return -EINVAL;
}
return 0;
}
static int fiji_send_msg_to_smc(struct amdgpu_device *adev, PPSMC_Msg msg)
{
if (!fiji_is_smc_ram_running(adev))
{
return -EINVAL;;
}
if (wait_smu_response(adev)) {
DRM_ERROR("Failed to send previous message\n");
return -EINVAL;
}
WREG32(mmSMC_MESSAGE_0, msg);
if (wait_smu_response(adev)) {
DRM_ERROR("Failed to send message\n");
return -EINVAL;
}
return 0;
}
static int fiji_send_msg_to_smc_without_waiting(struct amdgpu_device *adev,
PPSMC_Msg msg)
{
if (wait_smu_response(adev)) {
DRM_ERROR("Failed to send previous message\n");
return -EINVAL;
}
WREG32(mmSMC_MESSAGE_0, msg);
return 0;
}
static int fiji_send_msg_to_smc_with_parameter(struct amdgpu_device *adev,
PPSMC_Msg msg,
uint32_t parameter)
{
if (!fiji_is_smc_ram_running(adev))
return -EINVAL;
if (wait_smu_response(adev)) {
DRM_ERROR("Failed to send previous message\n");
return -EINVAL;
}
WREG32(mmSMC_MSG_ARG_0, parameter);
return fiji_send_msg_to_smc(adev, msg);
}
static int fiji_send_msg_to_smc_with_parameter_without_waiting(
struct amdgpu_device *adev,
PPSMC_Msg msg, uint32_t parameter)
{
if (wait_smu_response(adev)) {
DRM_ERROR("Failed to send previous message\n");
return -EINVAL;
}
WREG32(mmSMC_MSG_ARG_0, parameter);
return fiji_send_msg_to_smc_without_waiting(adev, msg);
}
#if 0 /* not used yet */
static int fiji_wait_for_smc_inactive(struct amdgpu_device *adev)
{
int i;
uint32_t val;
if (!fiji_is_smc_ram_running(adev))
return -EINVAL;
for (i = 0; i < adev->usec_timeout; i++) {
val = RREG32_SMC(ixSMC_SYSCON_CLOCK_CNTL_0);
if (REG_GET_FIELD(val, SMC_SYSCON_CLOCK_CNTL_0, cken) == 0)
break;
udelay(1);
}
if (i == adev->usec_timeout)
return -EINVAL;
return 0;
}
#endif
static int fiji_smu_upload_firmware_image(struct amdgpu_device *adev)
{
const struct smc_firmware_header_v1_0 *hdr;
uint32_t ucode_size;
uint32_t ucode_start_address;
const uint8_t *src;
uint32_t val;
uint32_t byte_count;
uint32_t *data;
unsigned long flags;
if (!adev->pm.fw)
return -EINVAL;
hdr = (const struct smc_firmware_header_v1_0 *)adev->pm.fw->data;
amdgpu_ucode_print_smc_hdr(&hdr->header);
adev->pm.fw_version = le32_to_cpu(hdr->header.ucode_version);
ucode_size = le32_to_cpu(hdr->header.ucode_size_bytes);
ucode_start_address = le32_to_cpu(hdr->ucode_start_addr);
src = (const uint8_t *)
(adev->pm.fw->data + le32_to_cpu(hdr->header.ucode_array_offset_bytes));
if (ucode_size & 3) {
DRM_ERROR("SMC ucode is not 4 bytes aligned\n");
return -EINVAL;
}
if (ucode_size > FIJI_SMC_SIZE) {
DRM_ERROR("SMC address is beyond the SMC RAM area\n");
return -EINVAL;
}
spin_lock_irqsave(&adev->smc_idx_lock, flags);
WREG32(mmSMC_IND_INDEX_0, ucode_start_address);
val = RREG32(mmSMC_IND_ACCESS_CNTL);
val = REG_SET_FIELD(val, SMC_IND_ACCESS_CNTL, AUTO_INCREMENT_IND_0, 1);
WREG32(mmSMC_IND_ACCESS_CNTL, val);
byte_count = ucode_size;
data = (uint32_t *)src;
for (; byte_count >= 4; data++, byte_count -= 4)
WREG32(mmSMC_IND_DATA_0, data[0]);
val = RREG32(mmSMC_IND_ACCESS_CNTL);
val = REG_SET_FIELD(val, SMC_IND_ACCESS_CNTL, AUTO_INCREMENT_IND_0, 0);
WREG32(mmSMC_IND_ACCESS_CNTL, val);
spin_unlock_irqrestore(&adev->smc_idx_lock, flags);
return 0;
}
#if 0 /* not used yet */
static int fiji_read_smc_sram_dword(struct amdgpu_device *adev,
uint32_t smc_address,
uint32_t *value,
uint32_t limit)
{
int result;
unsigned long flags;
spin_lock_irqsave(&adev->smc_idx_lock, flags);
result = fiji_set_smc_sram_address(adev, smc_address, limit);
if (result == 0)
*value = RREG32(mmSMC_IND_DATA_0);
spin_unlock_irqrestore(&adev->smc_idx_lock, flags);
return result;
}
static int fiji_write_smc_sram_dword(struct amdgpu_device *adev,
uint32_t smc_address,
uint32_t value,
uint32_t limit)
{
int result;
unsigned long flags;
spin_lock_irqsave(&adev->smc_idx_lock, flags);
result = fiji_set_smc_sram_address(adev, smc_address, limit);
if (result == 0)
WREG32(mmSMC_IND_DATA_0, value);
spin_unlock_irqrestore(&adev->smc_idx_lock, flags);
return result;
}
static int fiji_smu_stop_smc(struct amdgpu_device *adev)
{
uint32_t val = RREG32_SMC(ixSMC_SYSCON_RESET_CNTL);
val = REG_SET_FIELD(val, SMC_SYSCON_RESET_CNTL, rst_reg, 1);
WREG32_SMC(ixSMC_SYSCON_RESET_CNTL, val);
val = RREG32_SMC(ixSMC_SYSCON_CLOCK_CNTL_0);
val = REG_SET_FIELD(val, SMC_SYSCON_CLOCK_CNTL_0, ck_disable, 1);
WREG32_SMC(ixSMC_SYSCON_CLOCK_CNTL_0, val);
return 0;
}
#endif
static enum AMDGPU_UCODE_ID fiji_convert_fw_type(uint32_t fw_type)
{
switch (fw_type) {
case UCODE_ID_SDMA0:
return AMDGPU_UCODE_ID_SDMA0;
case UCODE_ID_SDMA1:
return AMDGPU_UCODE_ID_SDMA1;
case UCODE_ID_CP_CE:
return AMDGPU_UCODE_ID_CP_CE;
case UCODE_ID_CP_PFP:
return AMDGPU_UCODE_ID_CP_PFP;
case UCODE_ID_CP_ME:
return AMDGPU_UCODE_ID_CP_ME;
case UCODE_ID_CP_MEC:
case UCODE_ID_CP_MEC_JT1:
case UCODE_ID_CP_MEC_JT2:
return AMDGPU_UCODE_ID_CP_MEC1;
case UCODE_ID_RLC_G:
return AMDGPU_UCODE_ID_RLC_G;
default:
DRM_ERROR("ucode type is out of range!\n");
return AMDGPU_UCODE_ID_MAXIMUM;
}
}
static int fiji_smu_populate_single_firmware_entry(struct amdgpu_device *adev,
uint32_t fw_type,
struct SMU_Entry *entry)
{
enum AMDGPU_UCODE_ID id = fiji_convert_fw_type(fw_type);
struct amdgpu_firmware_info *ucode = &adev->firmware.ucode[id];
const struct gfx_firmware_header_v1_0 *header = NULL;
uint64_t gpu_addr;
uint32_t data_size;
if (ucode->fw == NULL)
return -EINVAL;
gpu_addr = ucode->mc_addr;
header = (const struct gfx_firmware_header_v1_0 *)ucode->fw->data;
data_size = le32_to_cpu(header->header.ucode_size_bytes);
if ((fw_type == UCODE_ID_CP_MEC_JT1) ||
(fw_type == UCODE_ID_CP_MEC_JT2)) {
gpu_addr += le32_to_cpu(header->jt_offset) << 2;
data_size = le32_to_cpu(header->jt_size) << 2;
}
entry->version = (uint16_t)le32_to_cpu(header->header.ucode_version);
entry->id = (uint16_t)fw_type;
entry->image_addr_high = upper_32_bits(gpu_addr);
entry->image_addr_low = lower_32_bits(gpu_addr);
entry->meta_data_addr_high = 0;
entry->meta_data_addr_low = 0;
entry->data_size_byte = data_size;
entry->num_register_entries = 0;
if (fw_type == UCODE_ID_RLC_G)
entry->flags = 1;
else
entry->flags = 0;
return 0;
}
static int fiji_smu_request_load_fw(struct amdgpu_device *adev)
{
struct fiji_smu_private_data *private = (struct fiji_smu_private_data *)adev->smu.priv;
struct SMU_DRAMData_TOC *toc;
uint32_t fw_to_load;
WREG32_SMC(ixSOFT_REGISTERS_TABLE_28, 0);
fiji_send_msg_to_smc_with_parameter(adev, PPSMC_MSG_SMU_DRAM_ADDR_HI, private->smu_buffer_addr_high);
fiji_send_msg_to_smc_with_parameter(adev, PPSMC_MSG_SMU_DRAM_ADDR_LO, private->smu_buffer_addr_low);
toc = (struct SMU_DRAMData_TOC *)private->header;
toc->num_entries = 0;
toc->structure_version = 1;
if (!adev->firmware.smu_load)
return 0;
if (fiji_smu_populate_single_firmware_entry(adev, UCODE_ID_RLC_G,
&toc->entry[toc->num_entries++])) {
DRM_ERROR("Failed to get firmware entry for RLC\n");
return -EINVAL;
}
if (fiji_smu_populate_single_firmware_entry(adev, UCODE_ID_CP_CE,
&toc->entry[toc->num_entries++])) {
DRM_ERROR("Failed to get firmware entry for CE\n");
return -EINVAL;
}
if (fiji_smu_populate_single_firmware_entry(adev, UCODE_ID_CP_PFP,
&toc->entry[toc->num_entries++])) {
DRM_ERROR("Failed to get firmware entry for PFP\n");
return -EINVAL;
}
if (fiji_smu_populate_single_firmware_entry(adev, UCODE_ID_CP_ME,
&toc->entry[toc->num_entries++])) {
DRM_ERROR("Failed to get firmware entry for ME\n");
return -EINVAL;
}
if (fiji_smu_populate_single_firmware_entry(adev, UCODE_ID_CP_MEC,
&toc->entry[toc->num_entries++])) {
DRM_ERROR("Failed to get firmware entry for MEC\n");
return -EINVAL;
}
if (fiji_smu_populate_single_firmware_entry(adev, UCODE_ID_CP_MEC_JT1,
&toc->entry[toc->num_entries++])) {
DRM_ERROR("Failed to get firmware entry for MEC_JT1\n");
return -EINVAL;
}
if (fiji_smu_populate_single_firmware_entry(adev, UCODE_ID_CP_MEC_JT2,
&toc->entry[toc->num_entries++])) {
DRM_ERROR("Failed to get firmware entry for MEC_JT2\n");
return -EINVAL;
}
if (fiji_smu_populate_single_firmware_entry(adev, UCODE_ID_SDMA0,
&toc->entry[toc->num_entries++])) {
DRM_ERROR("Failed to get firmware entry for SDMA0\n");
return -EINVAL;
}
if (fiji_smu_populate_single_firmware_entry(adev, UCODE_ID_SDMA1,
&toc->entry[toc->num_entries++])) {
DRM_ERROR("Failed to get firmware entry for SDMA1\n");
return -EINVAL;
}
fiji_send_msg_to_smc_with_parameter(adev, PPSMC_MSG_DRV_DRAM_ADDR_HI, private->header_addr_high);
fiji_send_msg_to_smc_with_parameter(adev, PPSMC_MSG_DRV_DRAM_ADDR_LO, private->header_addr_low);
fw_to_load = UCODE_ID_RLC_G_MASK |
UCODE_ID_SDMA0_MASK |
UCODE_ID_SDMA1_MASK |
UCODE_ID_CP_CE_MASK |
UCODE_ID_CP_ME_MASK |
UCODE_ID_CP_PFP_MASK |
UCODE_ID_CP_MEC_MASK;
if (fiji_send_msg_to_smc_with_parameter_without_waiting(adev, PPSMC_MSG_LoadUcodes, fw_to_load)) {
DRM_ERROR("Fail to request SMU load ucode\n");
return -EINVAL;
}
return 0;
}
static uint32_t fiji_smu_get_mask_for_fw_type(uint32_t fw_type)
{
switch (fw_type) {
case AMDGPU_UCODE_ID_SDMA0:
return UCODE_ID_SDMA0_MASK;
case AMDGPU_UCODE_ID_SDMA1:
return UCODE_ID_SDMA1_MASK;
case AMDGPU_UCODE_ID_CP_CE:
return UCODE_ID_CP_CE_MASK;
case AMDGPU_UCODE_ID_CP_PFP:
return UCODE_ID_CP_PFP_MASK;
case AMDGPU_UCODE_ID_CP_ME:
return UCODE_ID_CP_ME_MASK;
case AMDGPU_UCODE_ID_CP_MEC1:
return UCODE_ID_CP_MEC_MASK;
case AMDGPU_UCODE_ID_CP_MEC2:
return UCODE_ID_CP_MEC_MASK;
case AMDGPU_UCODE_ID_RLC_G:
return UCODE_ID_RLC_G_MASK;
default:
DRM_ERROR("ucode type is out of range!\n");
return 0;
}
}
static int fiji_smu_check_fw_load_finish(struct amdgpu_device *adev,
uint32_t fw_type)
{
uint32_t fw_mask = fiji_smu_get_mask_for_fw_type(fw_type);
int i;
for (i = 0; i < adev->usec_timeout; i++) {
if (fw_mask == (RREG32_SMC(ixSOFT_REGISTERS_TABLE_28) & fw_mask))
break;
udelay(1);
}
if (i == adev->usec_timeout) {
DRM_ERROR("check firmware loading failed\n");
return -EINVAL;
}
return 0;
}
static int fiji_smu_start_in_protection_mode(struct amdgpu_device *adev)
{
int result;
uint32_t val;
int i;
/* Assert reset */
val = RREG32_SMC(ixSMC_SYSCON_RESET_CNTL);
val = REG_SET_FIELD(val, SMC_SYSCON_RESET_CNTL, rst_reg, 1);
WREG32_SMC(ixSMC_SYSCON_RESET_CNTL, val);
result = fiji_smu_upload_firmware_image(adev);
if (result)
return result;
/* Clear status */
WREG32_SMC(ixSMU_STATUS, 0);
/* Enable clock */
val = RREG32_SMC(ixSMC_SYSCON_CLOCK_CNTL_0);
val = REG_SET_FIELD(val, SMC_SYSCON_CLOCK_CNTL_0, ck_disable, 0);
WREG32_SMC(ixSMC_SYSCON_CLOCK_CNTL_0, val);
/* De-assert reset */
val = RREG32_SMC(ixSMC_SYSCON_RESET_CNTL);
val = REG_SET_FIELD(val, SMC_SYSCON_RESET_CNTL, rst_reg, 0);
WREG32_SMC(ixSMC_SYSCON_RESET_CNTL, val);
/* Set SMU Auto Start */
val = RREG32_SMC(ixSMU_INPUT_DATA);
val = REG_SET_FIELD(val, SMU_INPUT_DATA, AUTO_START, 1);
WREG32_SMC(ixSMU_INPUT_DATA, val);
/* Clear firmware interrupt enable flag */
WREG32_SMC(ixFIRMWARE_FLAGS, 0);
for (i = 0; i < adev->usec_timeout; i++) {
val = RREG32_SMC(ixRCU_UC_EVENTS);
if (REG_GET_FIELD(val, RCU_UC_EVENTS, INTERRUPTS_ENABLED))
break;
udelay(1);
}
if (i == adev->usec_timeout) {
DRM_ERROR("Interrupt is not enabled by firmware\n");
return -EINVAL;
}
/* Call Test SMU message with 0x20000 offset
* to trigger SMU start
*/
fiji_send_msg_to_smc_offset(adev);
DRM_INFO("[FM]try triger smu start\n");
/* Wait for done bit to be set */
for (i = 0; i < adev->usec_timeout; i++) {
val = RREG32_SMC(ixSMU_STATUS);
if (REG_GET_FIELD(val, SMU_STATUS, SMU_DONE))
break;
udelay(1);
}
if (i == adev->usec_timeout) {
DRM_ERROR("Timeout for SMU start\n");
return -EINVAL;
}
/* Check pass/failed indicator */
val = RREG32_SMC(ixSMU_STATUS);
if (!REG_GET_FIELD(val, SMU_STATUS, SMU_PASS)) {
DRM_ERROR("SMU Firmware start failed\n");
return -EINVAL;
}
DRM_INFO("[FM]smu started\n");
/* Wait for firmware to initialize */
for (i = 0; i < adev->usec_timeout; i++) {
val = RREG32_SMC(ixFIRMWARE_FLAGS);
if(REG_GET_FIELD(val, FIRMWARE_FLAGS, INTERRUPTS_ENABLED))
break;
udelay(1);
}
if (i == adev->usec_timeout) {
DRM_ERROR("SMU firmware initialization failed\n");
return -EINVAL;
}
DRM_INFO("[FM]smu initialized\n");
return 0;
}
static int fiji_smu_start_in_non_protection_mode(struct amdgpu_device *adev)
{
int i, result;
uint32_t val;
/* wait for smc boot up */
for (i = 0; i < adev->usec_timeout; i++) {
val = RREG32_SMC(ixRCU_UC_EVENTS);
val = REG_GET_FIELD(val, RCU_UC_EVENTS, boot_seq_done);
if (val)
break;
udelay(1);
}
if (i == adev->usec_timeout) {
DRM_ERROR("SMC boot sequence is not completed\n");
return -EINVAL;
}
/* Clear firmware interrupt enable flag */
WREG32_SMC(ixFIRMWARE_FLAGS, 0);
/* Assert reset */
val = RREG32_SMC(ixSMC_SYSCON_RESET_CNTL);
val = REG_SET_FIELD(val, SMC_SYSCON_RESET_CNTL, rst_reg, 1);
WREG32_SMC(ixSMC_SYSCON_RESET_CNTL, val);
result = fiji_smu_upload_firmware_image(adev);
if (result)
return result;
/* Set smc instruct start point at 0x0 */
fiji_program_jump_on_start(adev);
/* Enable clock */
val = RREG32_SMC(ixSMC_SYSCON_CLOCK_CNTL_0);
val = REG_SET_FIELD(val, SMC_SYSCON_CLOCK_CNTL_0, ck_disable, 0);
WREG32_SMC(ixSMC_SYSCON_CLOCK_CNTL_0, val);
/* De-assert reset */
val = RREG32_SMC(ixSMC_SYSCON_RESET_CNTL);
val = REG_SET_FIELD(val, SMC_SYSCON_RESET_CNTL, rst_reg, 0);
WREG32_SMC(ixSMC_SYSCON_RESET_CNTL, val);
/* Wait for firmware to initialize */
for (i = 0; i < adev->usec_timeout; i++) {
val = RREG32_SMC(ixFIRMWARE_FLAGS);
if (REG_GET_FIELD(val, FIRMWARE_FLAGS, INTERRUPTS_ENABLED))
break;
udelay(1);
}
if (i == adev->usec_timeout) {
DRM_ERROR("Timeout for SMC firmware initialization\n");
return -EINVAL;
}
return 0;
}
int fiji_smu_start(struct amdgpu_device *adev)
{
int result;
uint32_t val;
if (!fiji_is_smc_ram_running(adev)) {
val = RREG32_SMC(ixSMU_FIRMWARE);
if (!REG_GET_FIELD(val, SMU_FIRMWARE, SMU_MODE)) {
DRM_INFO("[FM]start smu in nonprotection mode\n");
result = fiji_smu_start_in_non_protection_mode(adev);
if (result)
return result;
} else {
DRM_INFO("[FM]start smu in protection mode\n");
result = fiji_smu_start_in_protection_mode(adev);
if (result)
return result;
}
}
return fiji_smu_request_load_fw(adev);
}
static const struct amdgpu_smumgr_funcs fiji_smumgr_funcs = {
.check_fw_load_finish = fiji_smu_check_fw_load_finish,
.request_smu_load_fw = NULL,
.request_smu_specific_fw = NULL,
};
int fiji_smu_init(struct amdgpu_device *adev)
{
struct fiji_smu_private_data *private;
uint32_t image_size = ((sizeof(struct SMU_DRAMData_TOC) / 4096) + 1) * 4096;
uint32_t smu_internal_buffer_size = 200*4096;
struct amdgpu_bo **toc_buf = &adev->smu.toc_buf;
struct amdgpu_bo **smu_buf = &adev->smu.smu_buf;
uint64_t mc_addr;
void *toc_buf_ptr;
void *smu_buf_ptr;
int ret;
private = kzalloc(sizeof(struct fiji_smu_private_data), GFP_KERNEL);
if (NULL == private)
return -ENOMEM;
/* allocate firmware buffers */
if (adev->firmware.smu_load)
amdgpu_ucode_init_bo(adev);
adev->smu.priv = private;
adev->smu.fw_flags = 0;
/* Allocate FW image data structure and header buffer */
ret = amdgpu_bo_create(adev, image_size, PAGE_SIZE,
true, AMDGPU_GEM_DOMAIN_VRAM, 0, NULL, toc_buf);
if (ret) {
DRM_ERROR("Failed to allocate memory for TOC buffer\n");
return -ENOMEM;
}
/* Allocate buffer for SMU internal buffer */
ret = amdgpu_bo_create(adev, smu_internal_buffer_size, PAGE_SIZE,
true, AMDGPU_GEM_DOMAIN_VRAM, 0, NULL, smu_buf);
if (ret) {
DRM_ERROR("Failed to allocate memory for SMU internal buffer\n");
return -ENOMEM;
}
/* Retrieve GPU address for header buffer and internal buffer */
ret = amdgpu_bo_reserve(adev->smu.toc_buf, false);
if (ret) {
amdgpu_bo_unref(&adev->smu.toc_buf);
DRM_ERROR("Failed to reserve the TOC buffer\n");
return -EINVAL;
}
ret = amdgpu_bo_pin(adev->smu.toc_buf, AMDGPU_GEM_DOMAIN_VRAM, &mc_addr);
if (ret) {
amdgpu_bo_unreserve(adev->smu.toc_buf);
amdgpu_bo_unref(&adev->smu.toc_buf);
DRM_ERROR("Failed to pin the TOC buffer\n");
return -EINVAL;
}
ret = amdgpu_bo_kmap(*toc_buf, &toc_buf_ptr);
if (ret) {
amdgpu_bo_unreserve(adev->smu.toc_buf);
amdgpu_bo_unref(&adev->smu.toc_buf);
DRM_ERROR("Failed to map the TOC buffer\n");
return -EINVAL;
}
amdgpu_bo_unreserve(adev->smu.toc_buf);
private->header_addr_low = lower_32_bits(mc_addr);
private->header_addr_high = upper_32_bits(mc_addr);
private->header = toc_buf_ptr;
ret = amdgpu_bo_reserve(adev->smu.smu_buf, false);
if (ret) {
amdgpu_bo_unref(&adev->smu.smu_buf);
amdgpu_bo_unref(&adev->smu.toc_buf);
DRM_ERROR("Failed to reserve the SMU internal buffer\n");
return -EINVAL;
}
ret = amdgpu_bo_pin(adev->smu.smu_buf, AMDGPU_GEM_DOMAIN_VRAM, &mc_addr);
if (ret) {
amdgpu_bo_unreserve(adev->smu.smu_buf);
amdgpu_bo_unref(&adev->smu.smu_buf);
amdgpu_bo_unref(&adev->smu.toc_buf);
DRM_ERROR("Failed to pin the SMU internal buffer\n");
return -EINVAL;
}
ret = amdgpu_bo_kmap(*smu_buf, &smu_buf_ptr);
if (ret) {
amdgpu_bo_unreserve(adev->smu.smu_buf);
amdgpu_bo_unref(&adev->smu.smu_buf);
amdgpu_bo_unref(&adev->smu.toc_buf);
DRM_ERROR("Failed to map the SMU internal buffer\n");
return -EINVAL;
}
amdgpu_bo_unreserve(adev->smu.smu_buf);
private->smu_buffer_addr_low = lower_32_bits(mc_addr);
private->smu_buffer_addr_high = upper_32_bits(mc_addr);
adev->smu.smumgr_funcs = &fiji_smumgr_funcs;
return 0;
}
int fiji_smu_fini(struct amdgpu_device *adev)
{
amdgpu_bo_unref(&adev->smu.toc_buf);
amdgpu_bo_unref(&adev->smu.smu_buf);
kfree(adev->smu.priv);
adev->smu.priv = NULL;
if (adev->firmware.fw_buf)
amdgpu_ucode_fini_bo(adev);
return 0;
}

View File

@ -1,7 +1,5 @@
/*
* Copyright 2008 Advanced Micro Devices, Inc.
* Copyright 2008 Red Hat Inc.
* Copyright 2009 Jerome Glisse.
* Copyright 2014 Advanced Micro Devices, Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
@ -21,42 +19,24 @@
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*
* Authors: Dave Airlie
* Alex Deucher
* Jerome Glisse
*/
/* this file defines the CHIP_ and family flags used in the pciids,
* its is common between kms and non-kms because duplicating it and
* changing one place is fail.
*/
#ifndef AMDGPU_FAMILY_H
#define AMDGPU_FAMILY_H
/*
* Supported ASIC types
*/
enum amdgpu_asic_type {
CHIP_BONAIRE = 0,
CHIP_KAVERI,
CHIP_KABINI,
CHIP_HAWAII,
CHIP_MULLINS,
CHIP_TOPAZ,
CHIP_TONGA,
CHIP_CARRIZO,
CHIP_LAST,
};
#ifndef FIJI_SMUMGR_H
#define FIJI_SMUMGR_H
/*
* Chip flags
*/
enum amdgpu_chip_flags {
AMDGPU_ASIC_MASK = 0x0000ffffUL,
AMDGPU_FLAGS_MASK = 0xffff0000UL,
AMDGPU_IS_MOBILITY = 0x00010000UL,
AMDGPU_IS_APU = 0x00020000UL,
AMDGPU_IS_PX = 0x00040000UL,
AMDGPU_EXP_HW_SUPPORT = 0x00080000UL,
#include "fiji_ppsmc.h"
int fiji_smu_init(struct amdgpu_device *adev);
int fiji_smu_fini(struct amdgpu_device *adev);
int fiji_smu_start(struct amdgpu_device *adev);
struct fiji_smu_private_data
{
uint8_t *header;
uint32_t smu_buffer_addr_high;
uint32_t smu_buffer_addr_low;
uint32_t header_addr_high;
uint32_t header_addr_low;
};
#endif

View File

@ -2173,7 +2173,7 @@ static void gfx_v7_0_gpu_init(struct amdgpu_device *adev)
adev->gfx.config.num_tile_pipes = adev->gfx.config.max_tile_pipes;
adev->gfx.config.mem_max_burst_length_bytes = 256;
if (adev->flags & AMDGPU_IS_APU) {
if (adev->flags & AMD_IS_APU) {
/* Get memory bank mapping mode. */
tmp = RREG32(mmMC_FUS_DRAM0_BANK_ADDR_MAPPING);
dimm00_addr_map = REG_GET_FIELD(tmp, MC_FUS_DRAM0_BANK_ADDR_MAPPING, DIMM0ADDRMAP);
@ -2648,6 +2648,7 @@ static int gfx_v7_0_ring_test_ib(struct amdgpu_ring *ring)
{
struct amdgpu_device *adev = ring->adev;
struct amdgpu_ib ib;
struct fence *f = NULL;
uint32_t scratch;
uint32_t tmp = 0;
unsigned i;
@ -2662,26 +2663,23 @@ static int gfx_v7_0_ring_test_ib(struct amdgpu_ring *ring)
r = amdgpu_ib_get(ring, NULL, 256, &ib);
if (r) {
DRM_ERROR("amdgpu: failed to get ib (%d).\n", r);
amdgpu_gfx_scratch_free(adev, scratch);
return r;
goto err1;
}
ib.ptr[0] = PACKET3(PACKET3_SET_UCONFIG_REG, 1);
ib.ptr[1] = ((scratch - PACKET3_SET_UCONFIG_REG_START));
ib.ptr[2] = 0xDEADBEEF;
ib.length_dw = 3;
r = amdgpu_ib_schedule(adev, 1, &ib, AMDGPU_FENCE_OWNER_UNDEFINED);
if (r) {
amdgpu_gfx_scratch_free(adev, scratch);
amdgpu_ib_free(adev, &ib);
DRM_ERROR("amdgpu: failed to schedule ib (%d).\n", r);
return r;
}
r = amdgpu_fence_wait(ib.fence, false);
r = amdgpu_sched_ib_submit_kernel_helper(adev, ring, &ib, 1, NULL,
AMDGPU_FENCE_OWNER_UNDEFINED,
&f);
if (r)
goto err2;
r = fence_wait(f, false);
if (r) {
DRM_ERROR("amdgpu: fence wait failed (%d).\n", r);
amdgpu_gfx_scratch_free(adev, scratch);
amdgpu_ib_free(adev, &ib);
return r;
goto err2;
}
for (i = 0; i < adev->usec_timeout; i++) {
tmp = RREG32(scratch);
@ -2691,14 +2689,19 @@ static int gfx_v7_0_ring_test_ib(struct amdgpu_ring *ring)
}
if (i < adev->usec_timeout) {
DRM_INFO("ib test on ring %d succeeded in %u usecs\n",
ib.fence->ring->idx, i);
ring->idx, i);
goto err2;
} else {
DRM_ERROR("amdgpu: ib test failed (scratch(0x%04X)=0x%08X)\n",
scratch, tmp);
r = -EINVAL;
}
amdgpu_gfx_scratch_free(adev, scratch);
err2:
fence_put(f);
amdgpu_ib_free(adev, &ib);
err1:
amdgpu_gfx_scratch_free(adev, scratch);
return r;
}
@ -3758,7 +3761,7 @@ static int gfx_v7_0_rlc_init(struct amdgpu_device *adev)
int r;
/* allocate rlc buffers */
if (adev->flags & AMDGPU_IS_APU) {
if (adev->flags & AMD_IS_APU) {
if (adev->asic_type == CHIP_KAVERI) {
adev->gfx.rlc.reg_list = spectre_rlc_save_restore_register_list;
adev->gfx.rlc.reg_list_size =

View File

@ -87,6 +87,13 @@ MODULE_FIRMWARE("amdgpu/topaz_mec.bin");
MODULE_FIRMWARE("amdgpu/topaz_mec2.bin");
MODULE_FIRMWARE("amdgpu/topaz_rlc.bin");
MODULE_FIRMWARE("amdgpu/fiji_ce.bin");
MODULE_FIRMWARE("amdgpu/fiji_pfp.bin");
MODULE_FIRMWARE("amdgpu/fiji_me.bin");
MODULE_FIRMWARE("amdgpu/fiji_mec.bin");
MODULE_FIRMWARE("amdgpu/fiji_mec2.bin");
MODULE_FIRMWARE("amdgpu/fiji_rlc.bin");
static const struct amdgpu_gds_reg_offset amdgpu_gds_reg_offset[] =
{
{mmGDS_VMID0_BASE, mmGDS_VMID0_SIZE, mmGDS_GWS_VMID0, mmGDS_OA_VMID0},
@ -217,6 +224,71 @@ static const u32 tonga_mgcg_cgcg_init[] =
mmCP_MEM_SLP_CNTL, 0x00000001, 0x00000001,
};
static const u32 fiji_golden_common_all[] =
{
mmGRBM_GFX_INDEX, 0xffffffff, 0xe0000000,
mmPA_SC_RASTER_CONFIG, 0xffffffff, 0x3a00161a,
mmPA_SC_RASTER_CONFIG_1, 0xffffffff, 0x0000002e,
mmGB_ADDR_CONFIG, 0xffffffff, 0x12011003,
mmSPI_RESOURCE_RESERVE_CU_0, 0xffffffff, 0x00000800,
mmSPI_RESOURCE_RESERVE_CU_1, 0xffffffff, 0x00000800,
mmSPI_RESOURCE_RESERVE_EN_CU_0, 0xffffffff, 0x00007FBF,
mmSPI_RESOURCE_RESERVE_EN_CU_1, 0xffffffff, 0x00007FAF
};
static const u32 golden_settings_fiji_a10[] =
{
mmCB_HW_CONTROL_3, 0x000001ff, 0x00000040,
mmDB_DEBUG2, 0xf00fffff, 0x00000400,
mmPA_SC_ENHANCE, 0xffffffff, 0x20000001,
mmPA_SC_FIFO_DEPTH_CNTL, 0x000003ff, 0x00000100,
mmPA_SC_LINE_STIPPLE_STATE, 0x0000ff0f, 0x00000000,
mmTA_CNTL_AUX, 0x000f000f, 0x000b0000,
mmTCC_CTRL, 0x00100000, 0xf30fff7f,
mmTCP_ADDR_CONFIG, 0x000003ff, 0x000000ff,
mmTCP_CHAN_STEER_HI, 0xffffffff, 0x7d6cf5e4,
mmTCP_CHAN_STEER_LO, 0xffffffff, 0x3928b1a0,
};
static const u32 fiji_mgcg_cgcg_init[] =
{
mmRLC_CGTT_MGCG_OVERRIDE, 0xffffffff, 0xffffffc0,
mmGRBM_GFX_INDEX, 0xffffffff, 0xe0000000,
mmCB_CGTT_SCLK_CTRL, 0xffffffff, 0x00000100,
mmCGTT_BCI_CLK_CTRL, 0xffffffff, 0x00000100,
mmCGTT_CP_CLK_CTRL, 0xffffffff, 0x00000100,
mmCGTT_CPC_CLK_CTRL, 0xffffffff, 0x00000100,
mmCGTT_CPF_CLK_CTRL, 0xffffffff, 0x40000100,
mmCGTT_GDS_CLK_CTRL, 0xffffffff, 0x00000100,
mmCGTT_IA_CLK_CTRL, 0xffffffff, 0x06000100,
mmCGTT_PA_CLK_CTRL, 0xffffffff, 0x00000100,
mmCGTT_WD_CLK_CTRL, 0xffffffff, 0x06000100,
mmCGTT_PC_CLK_CTRL, 0xffffffff, 0x00000100,
mmCGTT_RLC_CLK_CTRL, 0xffffffff, 0x00000100,
mmCGTT_SC_CLK_CTRL, 0xffffffff, 0x00000100,
mmCGTT_SPI_CLK_CTRL, 0xffffffff, 0x00000100,
mmCGTT_SQ_CLK_CTRL, 0xffffffff, 0x00000100,
mmCGTT_SQG_CLK_CTRL, 0xffffffff, 0x00000100,
mmCGTT_SX_CLK_CTRL0, 0xffffffff, 0x00000100,
mmCGTT_SX_CLK_CTRL1, 0xffffffff, 0x00000100,
mmCGTT_SX_CLK_CTRL2, 0xffffffff, 0x00000100,
mmCGTT_SX_CLK_CTRL3, 0xffffffff, 0x00000100,
mmCGTT_SX_CLK_CTRL4, 0xffffffff, 0x00000100,
mmCGTT_TCI_CLK_CTRL, 0xffffffff, 0x00000100,
mmCGTT_TCP_CLK_CTRL, 0xffffffff, 0x00000100,
mmCGTT_VGT_CLK_CTRL, 0xffffffff, 0x06000100,
mmDB_CGTT_CLK_CTRL_0, 0xffffffff, 0x00000100,
mmTA_CGTT_CTRL, 0xffffffff, 0x00000100,
mmTCA_CGTT_SCLK_CTRL, 0xffffffff, 0x00000100,
mmTCC_CGTT_SCLK_CTRL, 0xffffffff, 0x00000100,
mmTD_CGTT_CTRL, 0xffffffff, 0x00000100,
mmGRBM_GFX_INDEX, 0xffffffff, 0xe0000000,
mmCGTS_SM_CTRL_REG, 0xffffffff, 0x96e00200,
mmCP_RB_WPTR_POLL_CNTL, 0xffffffff, 0x00900100,
mmRLC_CGCG_CGLS_CTRL, 0xffffffff, 0x0020003c,
mmCP_MEM_SLP_CNTL, 0x00000001, 0x00000001,
};
static const u32 golden_settings_iceland_a11[] =
{
mmCB_HW_CONTROL_3, 0x00000040, 0x00000040,
@ -439,6 +511,18 @@ static void gfx_v8_0_init_golden_registers(struct amdgpu_device *adev)
iceland_golden_common_all,
(const u32)ARRAY_SIZE(iceland_golden_common_all));
break;
case CHIP_FIJI:
amdgpu_program_register_sequence(adev,
fiji_mgcg_cgcg_init,
(const u32)ARRAY_SIZE(fiji_mgcg_cgcg_init));
amdgpu_program_register_sequence(adev,
golden_settings_fiji_a10,
(const u32)ARRAY_SIZE(golden_settings_fiji_a10));
amdgpu_program_register_sequence(adev,
fiji_golden_common_all,
(const u32)ARRAY_SIZE(fiji_golden_common_all));
break;
case CHIP_TONGA:
amdgpu_program_register_sequence(adev,
tonga_mgcg_cgcg_init,
@ -526,6 +610,7 @@ static int gfx_v8_0_ring_test_ib(struct amdgpu_ring *ring)
{
struct amdgpu_device *adev = ring->adev;
struct amdgpu_ib ib;
struct fence *f = NULL;
uint32_t scratch;
uint32_t tmp = 0;
unsigned i;
@ -540,26 +625,23 @@ static int gfx_v8_0_ring_test_ib(struct amdgpu_ring *ring)
r = amdgpu_ib_get(ring, NULL, 256, &ib);
if (r) {
DRM_ERROR("amdgpu: failed to get ib (%d).\n", r);
amdgpu_gfx_scratch_free(adev, scratch);
return r;
goto err1;
}
ib.ptr[0] = PACKET3(PACKET3_SET_UCONFIG_REG, 1);
ib.ptr[1] = ((scratch - PACKET3_SET_UCONFIG_REG_START));
ib.ptr[2] = 0xDEADBEEF;
ib.length_dw = 3;
r = amdgpu_ib_schedule(adev, 1, &ib, AMDGPU_FENCE_OWNER_UNDEFINED);
if (r) {
amdgpu_gfx_scratch_free(adev, scratch);
amdgpu_ib_free(adev, &ib);
DRM_ERROR("amdgpu: failed to schedule ib (%d).\n", r);
return r;
}
r = amdgpu_fence_wait(ib.fence, false);
r = amdgpu_sched_ib_submit_kernel_helper(adev, ring, &ib, 1, NULL,
AMDGPU_FENCE_OWNER_UNDEFINED,
&f);
if (r)
goto err2;
r = fence_wait(f, false);
if (r) {
DRM_ERROR("amdgpu: fence wait failed (%d).\n", r);
amdgpu_gfx_scratch_free(adev, scratch);
amdgpu_ib_free(adev, &ib);
return r;
goto err2;
}
for (i = 0; i < adev->usec_timeout; i++) {
tmp = RREG32(scratch);
@ -569,14 +651,18 @@ static int gfx_v8_0_ring_test_ib(struct amdgpu_ring *ring)
}
if (i < adev->usec_timeout) {
DRM_INFO("ib test on ring %d succeeded in %u usecs\n",
ib.fence->ring->idx, i);
ring->idx, i);
goto err2;
} else {
DRM_ERROR("amdgpu: ib test failed (scratch(0x%04X)=0x%08X)\n",
scratch, tmp);
r = -EINVAL;
}
amdgpu_gfx_scratch_free(adev, scratch);
err2:
fence_put(f);
amdgpu_ib_free(adev, &ib);
err1:
amdgpu_gfx_scratch_free(adev, scratch);
return r;
}
@ -601,6 +687,9 @@ static int gfx_v8_0_init_microcode(struct amdgpu_device *adev)
case CHIP_CARRIZO:
chip_name = "carrizo";
break;
case CHIP_FIJI:
chip_name = "fiji";
break;
default:
BUG();
}
@ -1236,6 +1325,7 @@ static void gfx_v8_0_tiling_mode_table_init(struct amdgpu_device *adev)
adev->gfx.config.macrotile_mode_array[reg_offset] = gb_tile_moden;
WREG32(mmGB_MACROTILE_MODE0 + reg_offset, gb_tile_moden);
}
case CHIP_FIJI:
case CHIP_TONGA:
for (reg_offset = 0; reg_offset < num_tile_mode_states; reg_offset++) {
switch (reg_offset) {
@ -1984,6 +2074,23 @@ static void gfx_v8_0_gpu_init(struct amdgpu_device *adev)
adev->gfx.config.sc_earlyz_tile_fifo_size = 0x130;
gb_addr_config = TOPAZ_GB_ADDR_CONFIG_GOLDEN;
break;
case CHIP_FIJI:
adev->gfx.config.max_shader_engines = 4;
adev->gfx.config.max_tile_pipes = 16;
adev->gfx.config.max_cu_per_sh = 16;
adev->gfx.config.max_sh_per_se = 1;
adev->gfx.config.max_backends_per_se = 4;
adev->gfx.config.max_texture_channel_caches = 8;
adev->gfx.config.max_gprs = 256;
adev->gfx.config.max_gs_threads = 32;
adev->gfx.config.max_hw_contexts = 8;
adev->gfx.config.sc_prim_fifo_size_frontend = 0x20;
adev->gfx.config.sc_prim_fifo_size_backend = 0x100;
adev->gfx.config.sc_hiz_tile_fifo_size = 0x30;
adev->gfx.config.sc_earlyz_tile_fifo_size = 0x130;
gb_addr_config = TONGA_GB_ADDR_CONFIG_GOLDEN;
break;
case CHIP_TONGA:
adev->gfx.config.max_shader_engines = 4;
adev->gfx.config.max_tile_pipes = 8;
@ -2078,7 +2185,7 @@ static void gfx_v8_0_gpu_init(struct amdgpu_device *adev)
adev->gfx.config.num_tile_pipes = adev->gfx.config.max_tile_pipes;
adev->gfx.config.mem_max_burst_length_bytes = 256;
if (adev->flags & AMDGPU_IS_APU) {
if (adev->flags & AMD_IS_APU) {
/* Get memory bank mapping mode. */
tmp = RREG32(mmMC_FUS_DRAM0_BANK_ADDR_MAPPING);
dimm00_addr_map = REG_GET_FIELD(tmp, MC_FUS_DRAM0_BANK_ADDR_MAPPING, DIMM0ADDRMAP);
@ -2490,6 +2597,7 @@ static int gfx_v8_0_cp_gfx_start(struct amdgpu_device *adev)
amdgpu_ring_write(ring, mmPA_SC_RASTER_CONFIG - PACKET3_SET_CONTEXT_REG_START);
switch (adev->asic_type) {
case CHIP_TONGA:
case CHIP_FIJI:
amdgpu_ring_write(ring, 0x16000012);
amdgpu_ring_write(ring, 0x0000002A);
break;
@ -3875,7 +3983,8 @@ static bool gfx_v8_0_ring_emit_semaphore(struct amdgpu_ring *ring,
unsigned sel = emit_wait ? PACKET3_SEM_SEL_WAIT : PACKET3_SEM_SEL_SIGNAL;
if (ring->adev->asic_type == CHIP_TOPAZ ||
ring->adev->asic_type == CHIP_TONGA)
ring->adev->asic_type == CHIP_TONGA ||
ring->adev->asic_type == CHIP_FIJI)
/* we got a hw semaphore bug in VI TONGA, return false to switch back to sw fence wait */
return false;
else {

View File

@ -636,7 +636,7 @@ static int gmc_v7_0_vm_init(struct amdgpu_device *adev)
adev->vm_manager.nvm = AMDGPU_NUM_OF_VMIDS;
/* base offset of vram pages */
if (adev->flags & AMDGPU_IS_APU) {
if (adev->flags & AMD_IS_APU) {
u64 tmp = RREG32(mmMC_VM_FB_OFFSET);
tmp <<= 22;
adev->vm_manager.vram_base_offset = tmp;
@ -841,7 +841,7 @@ static int gmc_v7_0_early_init(void *handle)
gmc_v7_0_set_gart_funcs(adev);
gmc_v7_0_set_irq_funcs(adev);
if (adev->flags & AMDGPU_IS_APU) {
if (adev->flags & AMD_IS_APU) {
adev->mc.vram_type = AMDGPU_VRAM_TYPE_UNKNOWN;
} else {
u32 tmp = RREG32(mmMC_SEQ_MISC0);
@ -957,7 +957,7 @@ static int gmc_v7_0_hw_init(void *handle)
gmc_v7_0_mc_program(adev);
if (!(adev->flags & AMDGPU_IS_APU)) {
if (!(adev->flags & AMD_IS_APU)) {
r = gmc_v7_0_mc_load_microcode(adev);
if (r) {
DRM_ERROR("Failed to load MC firmware!\n");
@ -1172,7 +1172,7 @@ static int gmc_v7_0_soft_reset(void *handle)
if (tmp & (SRBM_STATUS__MCB_BUSY_MASK | SRBM_STATUS__MCB_NON_DISPLAY_BUSY_MASK |
SRBM_STATUS__MCC_BUSY_MASK | SRBM_STATUS__MCD_BUSY_MASK)) {
if (!(adev->flags & AMDGPU_IS_APU))
if (!(adev->flags & AMD_IS_APU))
srbm_soft_reset = REG_SET_FIELD(srbm_soft_reset,
SRBM_SOFT_RESET, SOFT_RESET_MC, 1);
}
@ -1282,7 +1282,7 @@ static int gmc_v7_0_set_clockgating_state(void *handle,
if (state == AMD_CG_STATE_GATE)
gate = true;
if (!(adev->flags & AMDGPU_IS_APU)) {
if (!(adev->flags & AMD_IS_APU)) {
gmc_v7_0_enable_mc_mgcg(adev, gate);
gmc_v7_0_enable_mc_ls(adev, gate);
}

View File

@ -44,6 +44,7 @@ static void gmc_v8_0_set_irq_funcs(struct amdgpu_device *adev);
MODULE_FIRMWARE("amdgpu/topaz_mc.bin");
MODULE_FIRMWARE("amdgpu/tonga_mc.bin");
MODULE_FIRMWARE("amdgpu/fiji_mc.bin");
static const u32 golden_settings_tonga_a11[] =
{
@ -61,6 +62,19 @@ static const u32 tonga_mgcg_cgcg_init[] =
mmMC_MEM_POWER_LS, 0xffffffff, 0x00000104
};
static const u32 golden_settings_fiji_a10[] =
{
mmVM_PRT_APERTURE0_LOW_ADDR, 0x0fffffff, 0x0fffffff,
mmVM_PRT_APERTURE1_LOW_ADDR, 0x0fffffff, 0x0fffffff,
mmVM_PRT_APERTURE2_LOW_ADDR, 0x0fffffff, 0x0fffffff,
mmVM_PRT_APERTURE3_LOW_ADDR, 0x0fffffff, 0x0fffffff,
};
static const u32 fiji_mgcg_cgcg_init[] =
{
mmMC_MEM_POWER_LS, 0xffffffff, 0x00000104
};
static const u32 golden_settings_iceland_a11[] =
{
mmVM_PRT_APERTURE0_LOW_ADDR, 0x0fffffff, 0x0fffffff,
@ -90,6 +104,14 @@ static void gmc_v8_0_init_golden_registers(struct amdgpu_device *adev)
golden_settings_iceland_a11,
(const u32)ARRAY_SIZE(golden_settings_iceland_a11));
break;
case CHIP_FIJI:
amdgpu_program_register_sequence(adev,
fiji_mgcg_cgcg_init,
(const u32)ARRAY_SIZE(fiji_mgcg_cgcg_init));
amdgpu_program_register_sequence(adev,
golden_settings_fiji_a10,
(const u32)ARRAY_SIZE(golden_settings_fiji_a10));
break;
case CHIP_TONGA:
amdgpu_program_register_sequence(adev,
tonga_mgcg_cgcg_init,
@ -202,6 +224,9 @@ static int gmc_v8_0_init_microcode(struct amdgpu_device *adev)
case CHIP_TONGA:
chip_name = "tonga";
break;
case CHIP_FIJI:
chip_name = "fiji";
break;
case CHIP_CARRIZO:
return 0;
default: BUG();
@ -737,7 +762,7 @@ static int gmc_v8_0_vm_init(struct amdgpu_device *adev)
adev->vm_manager.nvm = AMDGPU_NUM_OF_VMIDS;
/* base offset of vram pages */
if (adev->flags & AMDGPU_IS_APU) {
if (adev->flags & AMD_IS_APU) {
u64 tmp = RREG32(mmMC_VM_FB_OFFSET);
tmp <<= 22;
adev->vm_manager.vram_base_offset = tmp;
@ -816,7 +841,7 @@ static int gmc_v8_0_early_init(void *handle)
gmc_v8_0_set_gart_funcs(adev);
gmc_v8_0_set_irq_funcs(adev);
if (adev->flags & AMDGPU_IS_APU) {
if (adev->flags & AMD_IS_APU) {
adev->mc.vram_type = AMDGPU_VRAM_TYPE_UNKNOWN;
} else {
u32 tmp = RREG32(mmMC_SEQ_MISC0);
@ -934,7 +959,7 @@ static int gmc_v8_0_hw_init(void *handle)
gmc_v8_0_mc_program(adev);
if (!(adev->flags & AMDGPU_IS_APU)) {
if (!(adev->flags & AMD_IS_APU)) {
r = gmc_v8_0_mc_load_microcode(adev);
if (r) {
DRM_ERROR("Failed to load MC firmware!\n");
@ -1147,7 +1172,7 @@ static int gmc_v8_0_soft_reset(void *handle)
if (tmp & (SRBM_STATUS__MCB_BUSY_MASK | SRBM_STATUS__MCB_NON_DISPLAY_BUSY_MASK |
SRBM_STATUS__MCC_BUSY_MASK | SRBM_STATUS__MCD_BUSY_MASK)) {
if (!(adev->flags & AMDGPU_IS_APU))
if (!(adev->flags & AMD_IS_APU))
srbm_soft_reset = REG_SET_FIELD(srbm_soft_reset,
SRBM_SOFT_RESET, SOFT_RESET_MC, 1);
}

View File

@ -673,6 +673,7 @@ static int sdma_v2_4_ring_test_ib(struct amdgpu_ring *ring)
{
struct amdgpu_device *adev = ring->adev;
struct amdgpu_ib ib;
struct fence *f = NULL;
unsigned i;
unsigned index;
int r;
@ -688,12 +689,10 @@ static int sdma_v2_4_ring_test_ib(struct amdgpu_ring *ring)
gpu_addr = adev->wb.gpu_addr + (index * 4);
tmp = 0xCAFEDEAD;
adev->wb.wb[index] = cpu_to_le32(tmp);
r = amdgpu_ib_get(ring, NULL, 256, &ib);
if (r) {
amdgpu_wb_free(adev, index);
DRM_ERROR("amdgpu: failed to get ib (%d).\n", r);
return r;
goto err0;
}
ib.ptr[0] = SDMA_PKT_HEADER_OP(SDMA_OP_WRITE) |
@ -707,19 +706,16 @@ static int sdma_v2_4_ring_test_ib(struct amdgpu_ring *ring)
ib.ptr[7] = SDMA_PKT_HEADER_OP(SDMA_OP_NOP);
ib.length_dw = 8;
r = amdgpu_ib_schedule(adev, 1, &ib, AMDGPU_FENCE_OWNER_UNDEFINED);
r = amdgpu_sched_ib_submit_kernel_helper(adev, ring, &ib, 1, NULL,
AMDGPU_FENCE_OWNER_UNDEFINED,
&f);
if (r)
goto err1;
r = fence_wait(f, false);
if (r) {
amdgpu_ib_free(adev, &ib);
amdgpu_wb_free(adev, index);
DRM_ERROR("amdgpu: failed to schedule ib (%d).\n", r);
return r;
}
r = amdgpu_fence_wait(ib.fence, false);
if (r) {
amdgpu_ib_free(adev, &ib);
amdgpu_wb_free(adev, index);
DRM_ERROR("amdgpu: fence wait failed (%d).\n", r);
return r;
goto err1;
}
for (i = 0; i < adev->usec_timeout; i++) {
tmp = le32_to_cpu(adev->wb.wb[index]);
@ -729,12 +725,17 @@ static int sdma_v2_4_ring_test_ib(struct amdgpu_ring *ring)
}
if (i < adev->usec_timeout) {
DRM_INFO("ib test on ring %d succeeded in %u usecs\n",
ib.fence->ring->idx, i);
ring->idx, i);
goto err1;
} else {
DRM_ERROR("amdgpu: ib test failed (0x%08X)\n", tmp);
r = -EINVAL;
}
err1:
fence_put(f);
amdgpu_ib_free(adev, &ib);
err0:
amdgpu_wb_free(adev, index);
return r;
}
@ -1415,5 +1416,6 @@ static void sdma_v2_4_set_vm_pte_funcs(struct amdgpu_device *adev)
if (adev->vm_manager.vm_pte_funcs == NULL) {
adev->vm_manager.vm_pte_funcs = &sdma_v2_4_vm_pte_funcs;
adev->vm_manager.vm_pte_funcs_ring = &adev->sdma[0].ring;
adev->vm_manager.vm_pte_funcs_ring->is_pte_ring = true;
}
}

View File

@ -53,6 +53,8 @@ MODULE_FIRMWARE("amdgpu/tonga_sdma.bin");
MODULE_FIRMWARE("amdgpu/tonga_sdma1.bin");
MODULE_FIRMWARE("amdgpu/carrizo_sdma.bin");
MODULE_FIRMWARE("amdgpu/carrizo_sdma1.bin");
MODULE_FIRMWARE("amdgpu/fiji_sdma.bin");
MODULE_FIRMWARE("amdgpu/fiji_sdma1.bin");
static const u32 sdma_offsets[SDMA_MAX_INSTANCE] =
{
@ -80,6 +82,24 @@ static const u32 tonga_mgcg_cgcg_init[] =
mmSDMA1_CLK_CTRL, 0xff000ff0, 0x00000100
};
static const u32 golden_settings_fiji_a10[] =
{
mmSDMA0_CHICKEN_BITS, 0xfc910007, 0x00810007,
mmSDMA0_GFX_IB_CNTL, 0x800f0111, 0x00000100,
mmSDMA0_RLC0_IB_CNTL, 0x800f0111, 0x00000100,
mmSDMA0_RLC1_IB_CNTL, 0x800f0111, 0x00000100,
mmSDMA1_CHICKEN_BITS, 0xfc910007, 0x00810007,
mmSDMA1_GFX_IB_CNTL, 0x800f0111, 0x00000100,
mmSDMA1_RLC0_IB_CNTL, 0x800f0111, 0x00000100,
mmSDMA1_RLC1_IB_CNTL, 0x800f0111, 0x00000100,
};
static const u32 fiji_mgcg_cgcg_init[] =
{
mmSDMA0_CLK_CTRL, 0xff000ff0, 0x00000100,
mmSDMA1_CLK_CTRL, 0xff000ff0, 0x00000100
};
static const u32 cz_golden_settings_a11[] =
{
mmSDMA0_CHICKEN_BITS, 0xfc910007, 0x00810007,
@ -122,6 +142,14 @@ static const u32 cz_mgcg_cgcg_init[] =
static void sdma_v3_0_init_golden_registers(struct amdgpu_device *adev)
{
switch (adev->asic_type) {
case CHIP_FIJI:
amdgpu_program_register_sequence(adev,
fiji_mgcg_cgcg_init,
(const u32)ARRAY_SIZE(fiji_mgcg_cgcg_init));
amdgpu_program_register_sequence(adev,
golden_settings_fiji_a10,
(const u32)ARRAY_SIZE(golden_settings_fiji_a10));
break;
case CHIP_TONGA:
amdgpu_program_register_sequence(adev,
tonga_mgcg_cgcg_init,
@ -167,6 +195,9 @@ static int sdma_v3_0_init_microcode(struct amdgpu_device *adev)
case CHIP_TONGA:
chip_name = "tonga";
break;
case CHIP_FIJI:
chip_name = "fiji";
break;
case CHIP_CARRIZO:
chip_name = "carrizo";
break;
@ -763,6 +794,7 @@ static int sdma_v3_0_ring_test_ib(struct amdgpu_ring *ring)
{
struct amdgpu_device *adev = ring->adev;
struct amdgpu_ib ib;
struct fence *f = NULL;
unsigned i;
unsigned index;
int r;
@ -778,12 +810,10 @@ static int sdma_v3_0_ring_test_ib(struct amdgpu_ring *ring)
gpu_addr = adev->wb.gpu_addr + (index * 4);
tmp = 0xCAFEDEAD;
adev->wb.wb[index] = cpu_to_le32(tmp);
r = amdgpu_ib_get(ring, NULL, 256, &ib);
if (r) {
amdgpu_wb_free(adev, index);
DRM_ERROR("amdgpu: failed to get ib (%d).\n", r);
return r;
goto err0;
}
ib.ptr[0] = SDMA_PKT_HEADER_OP(SDMA_OP_WRITE) |
@ -797,19 +827,16 @@ static int sdma_v3_0_ring_test_ib(struct amdgpu_ring *ring)
ib.ptr[7] = SDMA_PKT_NOP_HEADER_OP(SDMA_OP_NOP);
ib.length_dw = 8;
r = amdgpu_ib_schedule(adev, 1, &ib, AMDGPU_FENCE_OWNER_UNDEFINED);
r = amdgpu_sched_ib_submit_kernel_helper(adev, ring, &ib, 1, NULL,
AMDGPU_FENCE_OWNER_UNDEFINED,
&f);
if (r)
goto err1;
r = fence_wait(f, false);
if (r) {
amdgpu_ib_free(adev, &ib);
amdgpu_wb_free(adev, index);
DRM_ERROR("amdgpu: failed to schedule ib (%d).\n", r);
return r;
}
r = amdgpu_fence_wait(ib.fence, false);
if (r) {
amdgpu_ib_free(adev, &ib);
amdgpu_wb_free(adev, index);
DRM_ERROR("amdgpu: fence wait failed (%d).\n", r);
return r;
goto err1;
}
for (i = 0; i < adev->usec_timeout; i++) {
tmp = le32_to_cpu(adev->wb.wb[index]);
@ -819,12 +846,16 @@ static int sdma_v3_0_ring_test_ib(struct amdgpu_ring *ring)
}
if (i < adev->usec_timeout) {
DRM_INFO("ib test on ring %d succeeded in %u usecs\n",
ib.fence->ring->idx, i);
ring->idx, i);
goto err1;
} else {
DRM_ERROR("amdgpu: ib test failed (0x%08X)\n", tmp);
r = -EINVAL;
}
err1:
fence_put(f);
amdgpu_ib_free(adev, &ib);
err0:
amdgpu_wb_free(adev, index);
return r;
}
@ -1509,5 +1540,6 @@ static void sdma_v3_0_set_vm_pte_funcs(struct amdgpu_device *adev)
if (adev->vm_manager.vm_pte_funcs == NULL) {
adev->vm_manager.vm_pte_funcs = &sdma_v3_0_vm_pte_funcs;
adev->vm_manager.vm_pte_funcs_ring = &adev->sdma[0].ring;
adev->vm_manager.vm_pte_funcs_ring->is_pte_ring = true;
}
}

View File

@ -534,7 +534,7 @@ static void uvd_v4_2_ring_emit_ib(struct amdgpu_ring *ring,
static int uvd_v4_2_ring_test_ib(struct amdgpu_ring *ring)
{
struct amdgpu_device *adev = ring->adev;
struct amdgpu_fence *fence = NULL;
struct fence *fence = NULL;
int r;
r = amdgpu_asic_set_uvd_clocks(adev, 53300, 40000);
@ -555,14 +555,14 @@ static int uvd_v4_2_ring_test_ib(struct amdgpu_ring *ring)
goto error;
}
r = amdgpu_fence_wait(fence, false);
r = fence_wait(fence, false);
if (r) {
DRM_ERROR("amdgpu: fence wait failed (%d).\n", r);
goto error;
}
DRM_INFO("ib test on ring %d succeeded\n", ring->idx);
error:
amdgpu_fence_unref(&fence);
fence_put(fence);
amdgpu_asic_set_uvd_clocks(adev, 0, 0);
return r;
}

View File

@ -580,7 +580,7 @@ static void uvd_v5_0_ring_emit_ib(struct amdgpu_ring *ring,
static int uvd_v5_0_ring_test_ib(struct amdgpu_ring *ring)
{
struct amdgpu_device *adev = ring->adev;
struct amdgpu_fence *fence = NULL;
struct fence *fence = NULL;
int r;
r = amdgpu_asic_set_uvd_clocks(adev, 53300, 40000);
@ -601,14 +601,14 @@ static int uvd_v5_0_ring_test_ib(struct amdgpu_ring *ring)
goto error;
}
r = amdgpu_fence_wait(fence, false);
r = fence_wait(fence, false);
if (r) {
DRM_ERROR("amdgpu: fence wait failed (%d).\n", r);
goto error;
}
DRM_INFO("ib test on ring %d succeeded\n", ring->idx);
error:
amdgpu_fence_unref(&fence);
fence_put(fence);
amdgpu_asic_set_uvd_clocks(adev, 0, 0);
return r;
}

View File

@ -575,7 +575,7 @@ static void uvd_v6_0_ring_emit_ib(struct amdgpu_ring *ring,
*/
static int uvd_v6_0_ring_test_ib(struct amdgpu_ring *ring)
{
struct amdgpu_fence *fence = NULL;
struct fence *fence = NULL;
int r;
r = amdgpu_uvd_get_create_msg(ring, 1, NULL);
@ -590,14 +590,14 @@ static int uvd_v6_0_ring_test_ib(struct amdgpu_ring *ring)
goto error;
}
r = amdgpu_fence_wait(fence, false);
r = fence_wait(fence, false);
if (r) {
DRM_ERROR("amdgpu: fence wait failed (%d).\n", r);
goto error;
}
DRM_INFO("ib test on ring %d succeeded\n", ring->idx);
error:
amdgpu_fence_unref(&fence);
fence_put(fence);
return r;
}

View File

@ -205,7 +205,14 @@ static unsigned vce_v3_0_get_harvest_config(struct amdgpu_device *adev)
u32 tmp;
unsigned ret;
if (adev->flags & AMDGPU_IS_APU)
/* Fiji is single pipe */
if (adev->asic_type == CHIP_FIJI) {
ret = AMDGPU_VCE_HARVEST_VCE1;
return ret;
}
/* Tonga and CZ are dual or single pipe */
if (adev->flags & AMD_IS_APU)
tmp = (RREG32_SMC(ixVCE_HARVEST_FUSE_MACRO__ADDRESS) &
VCE_HARVEST_FUSE_MACRO__MASK) >>
VCE_HARVEST_FUSE_MACRO__SHIFT;

View File

@ -203,6 +203,17 @@ static const u32 tonga_mgcg_cgcg_init[] =
mmHDP_XDP_CGTT_BLK_CTRL, 0xc0000fff, 0x00000104,
};
static const u32 fiji_mgcg_cgcg_init[] =
{
mmCGTT_DRM_CLK_CTRL0, 0xffffffff, 0x00600100,
mmPCIE_INDEX, 0xffffffff, 0x0140001c,
mmPCIE_DATA, 0x000f0000, 0x00000000,
mmSMC_IND_INDEX_4, 0xffffffff, 0xC060000C,
mmSMC_IND_DATA_4, 0xc0000fff, 0x00000100,
mmCGTT_DRM_CLK_CTRL0, 0xff000fff, 0x00000100,
mmHDP_XDP_CGTT_BLK_CTRL, 0xc0000fff, 0x00000104,
};
static const u32 iceland_mgcg_cgcg_init[] =
{
mmPCIE_INDEX, 0xffffffff, ixPCIE_CNTL2,
@ -232,6 +243,11 @@ static void vi_init_golden_registers(struct amdgpu_device *adev)
iceland_mgcg_cgcg_init,
(const u32)ARRAY_SIZE(iceland_mgcg_cgcg_init));
break;
case CHIP_FIJI:
amdgpu_program_register_sequence(adev,
fiji_mgcg_cgcg_init,
(const u32)ARRAY_SIZE(fiji_mgcg_cgcg_init));
break;
case CHIP_TONGA:
amdgpu_program_register_sequence(adev,
tonga_mgcg_cgcg_init,
@ -261,7 +277,7 @@ static u32 vi_get_xclk(struct amdgpu_device *adev)
u32 reference_clock = adev->clock.spll.reference_freq;
u32 tmp;
if (adev->flags & AMDGPU_IS_APU)
if (adev->flags & AMD_IS_APU)
return reference_clock;
tmp = RREG32_SMC(ixCG_CLKPIN_CNTL_2);
@ -362,6 +378,26 @@ static struct amdgpu_allowed_register_entry cz_allowed_read_registers[] = {
static struct amdgpu_allowed_register_entry vi_allowed_read_registers[] = {
{mmGRBM_STATUS, false},
{mmGRBM_STATUS2, false},
{mmGRBM_STATUS_SE0, false},
{mmGRBM_STATUS_SE1, false},
{mmGRBM_STATUS_SE2, false},
{mmGRBM_STATUS_SE3, false},
{mmSRBM_STATUS, false},
{mmSRBM_STATUS2, false},
{mmSRBM_STATUS3, false},
{mmSDMA0_STATUS_REG + SDMA0_REGISTER_OFFSET, false},
{mmSDMA0_STATUS_REG + SDMA1_REGISTER_OFFSET, false},
{mmCP_STAT, false},
{mmCP_STALLED_STAT1, false},
{mmCP_STALLED_STAT2, false},
{mmCP_STALLED_STAT3, false},
{mmCP_CPF_BUSY_STAT, false},
{mmCP_CPF_STALLED_STAT1, false},
{mmCP_CPF_STATUS, false},
{mmCP_CPC_BUSY_STAT, false},
{mmCP_CPC_STALLED_STAT1, false},
{mmCP_CPC_STATUS, false},
{mmGB_ADDR_CONFIG, false},
{mmMC_ARB_RAMCFG, false},
{mmGB_TILE_MODE0, false},
@ -449,6 +485,7 @@ static int vi_read_register(struct amdgpu_device *adev, u32 se_num,
asic_register_table = tonga_allowed_read_registers;
size = ARRAY_SIZE(tonga_allowed_read_registers);
break;
case CHIP_FIJI:
case CHIP_TONGA:
case CHIP_CARRIZO:
asic_register_table = cz_allowed_read_registers;
@ -751,7 +788,7 @@ static void vi_gpu_soft_reset(struct amdgpu_device *adev, u32 reset_mask)
srbm_soft_reset =
REG_SET_FIELD(srbm_soft_reset, SRBM_SOFT_RESET, SOFT_RESET_VCE1, 1);
if (!(adev->flags & AMDGPU_IS_APU)) {
if (!(adev->flags & AMD_IS_APU)) {
if (reset_mask & AMDGPU_RESET_MC)
srbm_soft_reset =
REG_SET_FIELD(srbm_soft_reset, SRBM_SOFT_RESET, SOFT_RESET_MC, 1);
@ -971,7 +1008,7 @@ static void vi_pcie_gen3_enable(struct amdgpu_device *adev)
if (amdgpu_pcie_gen2 == 0)
return;
if (adev->flags & AMDGPU_IS_APU)
if (adev->flags & AMD_IS_APU)
return;
ret = drm_pcie_get_speed_cap_mask(adev->ddev, &mask);
@ -999,7 +1036,7 @@ static void vi_enable_doorbell_aperture(struct amdgpu_device *adev,
u32 tmp;
/* not necessary on CZ */
if (adev->flags & AMDGPU_IS_APU)
if (adev->flags & AMD_IS_APU)
return;
tmp = RREG32(mmBIF_DOORBELL_APER_EN);
@ -1127,6 +1164,74 @@ static const struct amdgpu_ip_block_version tonga_ip_blocks[] =
},
};
static const struct amdgpu_ip_block_version fiji_ip_blocks[] =
{
/* ORDER MATTERS! */
{
.type = AMD_IP_BLOCK_TYPE_COMMON,
.major = 2,
.minor = 0,
.rev = 0,
.funcs = &vi_common_ip_funcs,
},
{
.type = AMD_IP_BLOCK_TYPE_GMC,
.major = 8,
.minor = 5,
.rev = 0,
.funcs = &gmc_v8_0_ip_funcs,
},
{
.type = AMD_IP_BLOCK_TYPE_IH,
.major = 3,
.minor = 0,
.rev = 0,
.funcs = &tonga_ih_ip_funcs,
},
{
.type = AMD_IP_BLOCK_TYPE_SMC,
.major = 7,
.minor = 1,
.rev = 0,
.funcs = &fiji_dpm_ip_funcs,
},
{
.type = AMD_IP_BLOCK_TYPE_DCE,
.major = 10,
.minor = 1,
.rev = 0,
.funcs = &dce_v10_0_ip_funcs,
},
{
.type = AMD_IP_BLOCK_TYPE_GFX,
.major = 8,
.minor = 0,
.rev = 0,
.funcs = &gfx_v8_0_ip_funcs,
},
{
.type = AMD_IP_BLOCK_TYPE_SDMA,
.major = 3,
.minor = 0,
.rev = 0,
.funcs = &sdma_v3_0_ip_funcs,
},
{
.type = AMD_IP_BLOCK_TYPE_UVD,
.major = 6,
.minor = 0,
.rev = 0,
.funcs = &uvd_v6_0_ip_funcs,
},
{
.type = AMD_IP_BLOCK_TYPE_VCE,
.major = 3,
.minor = 0,
.rev = 0,
.funcs = &vce_v3_0_ip_funcs,
},
};
static const struct amdgpu_ip_block_version cz_ip_blocks[] =
{
/* ORDER MATTERS! */
@ -1202,6 +1307,10 @@ int vi_set_ip_blocks(struct amdgpu_device *adev)
adev->ip_blocks = topaz_ip_blocks;
adev->num_ip_blocks = ARRAY_SIZE(topaz_ip_blocks);
break;
case CHIP_FIJI:
adev->ip_blocks = fiji_ip_blocks;
adev->num_ip_blocks = ARRAY_SIZE(fiji_ip_blocks);
break;
case CHIP_TONGA:
adev->ip_blocks = tonga_ip_blocks;
adev->num_ip_blocks = ARRAY_SIZE(tonga_ip_blocks);
@ -1248,7 +1357,7 @@ static int vi_common_early_init(void *handle)
bool smc_enabled = false;
struct amdgpu_device *adev = (struct amdgpu_device *)handle;
if (adev->flags & AMDGPU_IS_APU) {
if (adev->flags & AMD_IS_APU) {
adev->smc_rreg = &cz_smc_rreg;
adev->smc_wreg = &cz_smc_wreg;
} else {
@ -1279,6 +1388,7 @@ static int vi_common_early_init(void *handle)
if (amdgpu_smc_load_fw && smc_enabled)
adev->firmware.smu_load = true;
break;
case CHIP_FIJI:
case CHIP_TONGA:
adev->has_uvd = true;
adev->cg_flags = 0;

View File

@ -30,7 +30,7 @@ int cz_smu_start(struct amdgpu_device *adev);
int cz_smu_fini(struct amdgpu_device *adev);
extern const struct amd_ip_funcs tonga_dpm_ip_funcs;
extern const struct amd_ip_funcs fiji_dpm_ip_funcs;
extern const struct amd_ip_funcs iceland_dpm_ip_funcs;
#endif

View File

@ -23,6 +23,45 @@
#ifndef __AMD_SHARED_H__
#define __AMD_SHARED_H__
#define AMD_MAX_USEC_TIMEOUT 100000 /* 100 ms */
/*
* Supported GPU families (aligned with amdgpu_drm.h)
*/
#define AMD_FAMILY_UNKNOWN 0
#define AMD_FAMILY_CI 120 /* Bonaire, Hawaii */
#define AMD_FAMILY_KV 125 /* Kaveri, Kabini, Mullins */
#define AMD_FAMILY_VI 130 /* Iceland, Tonga */
#define AMD_FAMILY_CZ 135 /* Carrizo */
/*
* Supported ASIC types
*/
enum amd_asic_type {
CHIP_BONAIRE = 0,
CHIP_KAVERI,
CHIP_KABINI,
CHIP_HAWAII,
CHIP_MULLINS,
CHIP_TOPAZ,
CHIP_TONGA,
CHIP_FIJI,
CHIP_CARRIZO,
CHIP_LAST,
};
/*
* Chip flags
*/
enum amd_chip_flags {
AMD_ASIC_MASK = 0x0000ffffUL,
AMD_FLAGS_MASK = 0xffff0000UL,
AMD_IS_MOBILITY = 0x00010000UL,
AMD_IS_APU = 0x00020000UL,
AMD_IS_PX = 0x00040000UL,
AMD_EXP_HW_SUPPORT = 0x00080000UL,
};
enum amd_ip_block_type {
AMD_IP_BLOCK_TYPE_COMMON,
AMD_IP_BLOCK_TYPE_GMC,

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,624 @@
/*
* Copyright 2015 Advanced Micro Devices, Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*
*
*/
#ifndef _CGS_COMMON_H
#define _CGS_COMMON_H
#include "amd_shared.h"
/**
* enum cgs_gpu_mem_type - GPU memory types
*/
enum cgs_gpu_mem_type {
CGS_GPU_MEM_TYPE__VISIBLE_FB,
CGS_GPU_MEM_TYPE__INVISIBLE_FB,
CGS_GPU_MEM_TYPE__VISIBLE_CONTIG_FB,
CGS_GPU_MEM_TYPE__INVISIBLE_CONTIG_FB,
CGS_GPU_MEM_TYPE__GART_CACHEABLE,
CGS_GPU_MEM_TYPE__GART_WRITECOMBINE
};
/**
* enum cgs_ind_reg - Indirect register spaces
*/
enum cgs_ind_reg {
CGS_IND_REG__MMIO,
CGS_IND_REG__PCIE,
CGS_IND_REG__SMC,
CGS_IND_REG__UVD_CTX,
CGS_IND_REG__DIDT,
CGS_IND_REG__AUDIO_ENDPT
};
/**
* enum cgs_clock - Clocks controlled by the SMU
*/
enum cgs_clock {
CGS_CLOCK__SCLK,
CGS_CLOCK__MCLK,
CGS_CLOCK__VCLK,
CGS_CLOCK__DCLK,
CGS_CLOCK__ECLK,
CGS_CLOCK__ACLK,
CGS_CLOCK__ICLK,
/* ... */
};
/**
* enum cgs_engine - Engines that can be statically power-gated
*/
enum cgs_engine {
CGS_ENGINE__UVD,
CGS_ENGINE__VCE,
CGS_ENGINE__VP8,
CGS_ENGINE__ACP_DMA,
CGS_ENGINE__ACP_DSP0,
CGS_ENGINE__ACP_DSP1,
CGS_ENGINE__ISP,
/* ... */
};
/**
* enum cgs_voltage_planes - Voltage planes for external camera HW
*/
enum cgs_voltage_planes {
CGS_VOLTAGE_PLANE__SENSOR0,
CGS_VOLTAGE_PLANE__SENSOR1,
/* ... */
};
/*
* enum cgs_ucode_id - Firmware types for different IPs
*/
enum cgs_ucode_id {
CGS_UCODE_ID_SMU = 0,
CGS_UCODE_ID_SDMA0,
CGS_UCODE_ID_SDMA1,
CGS_UCODE_ID_CP_CE,
CGS_UCODE_ID_CP_PFP,
CGS_UCODE_ID_CP_ME,
CGS_UCODE_ID_CP_MEC,
CGS_UCODE_ID_CP_MEC_JT1,
CGS_UCODE_ID_CP_MEC_JT2,
CGS_UCODE_ID_GMCON_RENG,
CGS_UCODE_ID_RLC_G,
CGS_UCODE_ID_MAXIMUM,
};
/**
* struct cgs_clock_limits - Clock limits
*
* Clocks are specified in 10KHz units.
*/
struct cgs_clock_limits {
unsigned min; /**< Minimum supported frequency */
unsigned max; /**< Maxumim supported frequency */
unsigned sustainable; /**< Thermally sustainable frequency */
};
/**
* struct cgs_firmware_info - Firmware information
*/
struct cgs_firmware_info {
uint16_t version;
uint16_t feature_version;
uint32_t image_size;
uint64_t mc_addr;
void *kptr;
};
typedef unsigned long cgs_handle_t;
/**
* cgs_gpu_mem_info() - Return information about memory heaps
* @cgs_device: opaque device handle
* @type: memory type
* @mc_start: Start MC address of the heap (output)
* @mc_size: MC address space size (output)
* @mem_size: maximum amount of memory available for allocation (output)
*
* This function returns information about memory heaps. The type
* parameter is used to select the memory heap. The mc_start and
* mc_size for GART heaps may be bigger than the memory available for
* allocation.
*
* mc_start and mc_size are undefined for non-contiguous FB memory
* types, since buffers allocated with these types may or may not be
* GART mapped.
*
* Return: 0 on success, -errno otherwise
*/
typedef int (*cgs_gpu_mem_info_t)(void *cgs_device, enum cgs_gpu_mem_type type,
uint64_t *mc_start, uint64_t *mc_size,
uint64_t *mem_size);
/**
* cgs_gmap_kmem() - map kernel memory to GART aperture
* @cgs_device: opaque device handle
* @kmem: pointer to kernel memory
* @size: size to map
* @min_offset: minimum offset from start of GART aperture
* @max_offset: maximum offset from start of GART aperture
* @kmem_handle: kernel memory handle (output)
* @mcaddr: MC address (output)
*
* Return: 0 on success, -errno otherwise
*/
typedef int (*cgs_gmap_kmem_t)(void *cgs_device, void *kmem, uint64_t size,
uint64_t min_offset, uint64_t max_offset,
cgs_handle_t *kmem_handle, uint64_t *mcaddr);
/**
* cgs_gunmap_kmem() - unmap kernel memory
* @cgs_device: opaque device handle
* @kmem_handle: kernel memory handle returned by gmap_kmem
*
* Return: 0 on success, -errno otherwise
*/
typedef int (*cgs_gunmap_kmem_t)(void *cgs_device, cgs_handle_t kmem_handle);
/**
* cgs_alloc_gpu_mem() - Allocate GPU memory
* @cgs_device: opaque device handle
* @type: memory type
* @size: size in bytes
* @align: alignment in bytes
* @min_offset: minimum offset from start of heap
* @max_offset: maximum offset from start of heap
* @handle: memory handle (output)
*
* The memory types CGS_GPU_MEM_TYPE_*_CONTIG_FB force contiguous
* memory allocation. This guarantees that the MC address returned by
* cgs_gmap_gpu_mem is not mapped through the GART. The non-contiguous
* FB memory types may be GART mapped depending on memory
* fragmentation and memory allocator policies.
*
* If min/max_offset are non-0, the allocation will be forced to
* reside between these offsets in its respective memory heap. The
* base address that the offset relates to, depends on the memory
* type.
*
* - CGS_GPU_MEM_TYPE__*_CONTIG_FB: FB MC base address
* - CGS_GPU_MEM_TYPE__GART_*: GART aperture base address
* - others: undefined, don't use with max_offset
*
* Return: 0 on success, -errno otherwise
*/
typedef int (*cgs_alloc_gpu_mem_t)(void *cgs_device, enum cgs_gpu_mem_type type,
uint64_t size, uint64_t align,
uint64_t min_offset, uint64_t max_offset,
cgs_handle_t *handle);
/**
* cgs_free_gpu_mem() - Free GPU memory
* @cgs_device: opaque device handle
* @handle: memory handle returned by alloc or import
*
* Return: 0 on success, -errno otherwise
*/
typedef int (*cgs_free_gpu_mem_t)(void *cgs_device, cgs_handle_t handle);
/**
* cgs_gmap_gpu_mem() - GPU-map GPU memory
* @cgs_device: opaque device handle
* @handle: memory handle returned by alloc or import
* @mcaddr: MC address (output)
*
* Ensures that a buffer is GPU accessible and returns its MC address.
*
* Return: 0 on success, -errno otherwise
*/
typedef int (*cgs_gmap_gpu_mem_t)(void *cgs_device, cgs_handle_t handle,
uint64_t *mcaddr);
/**
* cgs_gunmap_gpu_mem() - GPU-unmap GPU memory
* @cgs_device: opaque device handle
* @handle: memory handle returned by alloc or import
*
* Allows the buffer to be migrated while it's not used by the GPU.
*
* Return: 0 on success, -errno otherwise
*/
typedef int (*cgs_gunmap_gpu_mem_t)(void *cgs_device, cgs_handle_t handle);
/**
* cgs_kmap_gpu_mem() - Kernel-map GPU memory
*
* @cgs_device: opaque device handle
* @handle: memory handle returned by alloc or import
* @map: Kernel virtual address the memory was mapped to (output)
*
* Return: 0 on success, -errno otherwise
*/
typedef int (*cgs_kmap_gpu_mem_t)(void *cgs_device, cgs_handle_t handle,
void **map);
/**
* cgs_kunmap_gpu_mem() - Kernel-unmap GPU memory
* @cgs_device: opaque device handle
* @handle: memory handle returned by alloc or import
*
* Return: 0 on success, -errno otherwise
*/
typedef int (*cgs_kunmap_gpu_mem_t)(void *cgs_device, cgs_handle_t handle);
/**
* cgs_read_register() - Read an MMIO register
* @cgs_device: opaque device handle
* @offset: register offset
*
* Return: register value
*/
typedef uint32_t (*cgs_read_register_t)(void *cgs_device, unsigned offset);
/**
* cgs_write_register() - Write an MMIO register
* @cgs_device: opaque device handle
* @offset: register offset
* @value: register value
*/
typedef void (*cgs_write_register_t)(void *cgs_device, unsigned offset,
uint32_t value);
/**
* cgs_read_ind_register() - Read an indirect register
* @cgs_device: opaque device handle
* @offset: register offset
*
* Return: register value
*/
typedef uint32_t (*cgs_read_ind_register_t)(void *cgs_device, enum cgs_ind_reg space,
unsigned index);
/**
* cgs_write_ind_register() - Write an indirect register
* @cgs_device: opaque device handle
* @offset: register offset
* @value: register value
*/
typedef void (*cgs_write_ind_register_t)(void *cgs_device, enum cgs_ind_reg space,
unsigned index, uint32_t value);
/**
* cgs_read_pci_config_byte() - Read byte from PCI configuration space
* @cgs_device: opaque device handle
* @addr: address
*
* Return: Value read
*/
typedef uint8_t (*cgs_read_pci_config_byte_t)(void *cgs_device, unsigned addr);
/**
* cgs_read_pci_config_word() - Read word from PCI configuration space
* @cgs_device: opaque device handle
* @addr: address, must be word-aligned
*
* Return: Value read
*/
typedef uint16_t (*cgs_read_pci_config_word_t)(void *cgs_device, unsigned addr);
/**
* cgs_read_pci_config_dword() - Read dword from PCI configuration space
* @cgs_device: opaque device handle
* @addr: address, must be dword-aligned
*
* Return: Value read
*/
typedef uint32_t (*cgs_read_pci_config_dword_t)(void *cgs_device,
unsigned addr);
/**
* cgs_write_pci_config_byte() - Write byte to PCI configuration space
* @cgs_device: opaque device handle
* @addr: address
* @value: value to write
*/
typedef void (*cgs_write_pci_config_byte_t)(void *cgs_device, unsigned addr,
uint8_t value);
/**
* cgs_write_pci_config_word() - Write byte to PCI configuration space
* @cgs_device: opaque device handle
* @addr: address, must be word-aligned
* @value: value to write
*/
typedef void (*cgs_write_pci_config_word_t)(void *cgs_device, unsigned addr,
uint16_t value);
/**
* cgs_write_pci_config_dword() - Write byte to PCI configuration space
* @cgs_device: opaque device handle
* @addr: address, must be dword-aligned
* @value: value to write
*/
typedef void (*cgs_write_pci_config_dword_t)(void *cgs_device, unsigned addr,
uint32_t value);
/**
* cgs_atom_get_data_table() - Get a pointer to an ATOM BIOS data table
* @cgs_device: opaque device handle
* @table: data table index
* @size: size of the table (output, may be NULL)
* @frev: table format revision (output, may be NULL)
* @crev: table content revision (output, may be NULL)
*
* Return: Pointer to start of the table, or NULL on failure
*/
typedef const void *(*cgs_atom_get_data_table_t)(
void *cgs_device, unsigned table,
uint16_t *size, uint8_t *frev, uint8_t *crev);
/**
* cgs_atom_get_cmd_table_revs() - Get ATOM BIOS command table revisions
* @cgs_device: opaque device handle
* @table: data table index
* @frev: table format revision (output, may be NULL)
* @crev: table content revision (output, may be NULL)
*
* Return: 0 on success, -errno otherwise
*/
typedef int (*cgs_atom_get_cmd_table_revs_t)(void *cgs_device, unsigned table,
uint8_t *frev, uint8_t *crev);
/**
* cgs_atom_exec_cmd_table() - Execute an ATOM BIOS command table
* @cgs_device: opaque device handle
* @table: command table index
* @args: arguments
*
* Return: 0 on success, -errno otherwise
*/
typedef int (*cgs_atom_exec_cmd_table_t)(void *cgs_device,
unsigned table, void *args);
/**
* cgs_create_pm_request() - Create a power management request
* @cgs_device: opaque device handle
* @request: handle of created PM request (output)
*
* Return: 0 on success, -errno otherwise
*/
typedef int (*cgs_create_pm_request_t)(void *cgs_device, cgs_handle_t *request);
/**
* cgs_destroy_pm_request() - Destroy a power management request
* @cgs_device: opaque device handle
* @request: handle of created PM request
*
* Return: 0 on success, -errno otherwise
*/
typedef int (*cgs_destroy_pm_request_t)(void *cgs_device, cgs_handle_t request);
/**
* cgs_set_pm_request() - Activate or deactiveate a PM request
* @cgs_device: opaque device handle
* @request: PM request handle
* @active: 0 = deactivate, non-0 = activate
*
* While a PM request is active, its minimum clock requests are taken
* into account as the requested engines are powered up. When the
* request is inactive, the engines may be powered down and clocks may
* be lower, depending on other PM requests by other driver
* components.
*
* Return: 0 on success, -errno otherwise
*/
typedef int (*cgs_set_pm_request_t)(void *cgs_device, cgs_handle_t request,
int active);
/**
* cgs_pm_request_clock() - Request a minimum frequency for a specific clock
* @cgs_device: opaque device handle
* @request: PM request handle
* @clock: which clock?
* @freq: requested min. frequency in 10KHz units (0 to clear request)
*
* Return: 0 on success, -errno otherwise
*/
typedef int (*cgs_pm_request_clock_t)(void *cgs_device, cgs_handle_t request,
enum cgs_clock clock, unsigned freq);
/**
* cgs_pm_request_engine() - Request an engine to be powered up
* @cgs_device: opaque device handle
* @request: PM request handle
* @engine: which engine?
* @powered: 0 = powered down, non-0 = powered up
*
* Return: 0 on success, -errno otherwise
*/
typedef int (*cgs_pm_request_engine_t)(void *cgs_device, cgs_handle_t request,
enum cgs_engine engine, int powered);
/**
* cgs_pm_query_clock_limits() - Query clock frequency limits
* @cgs_device: opaque device handle
* @clock: which clock?
* @limits: clock limits
*
* Return: 0 on success, -errno otherwise
*/
typedef int (*cgs_pm_query_clock_limits_t)(void *cgs_device,
enum cgs_clock clock,
struct cgs_clock_limits *limits);
/**
* cgs_set_camera_voltages() - Apply specific voltages to PMIC voltage planes
* @cgs_device: opaque device handle
* @mask: bitmask of voltages to change (1<<CGS_VOLTAGE_PLANE__xyz|...)
* @voltages: pointer to array of voltage values in 1mV units
*
* Return: 0 on success, -errno otherwise
*/
typedef int (*cgs_set_camera_voltages_t)(void *cgs_device, uint32_t mask,
const uint32_t *voltages);
/**
* cgs_get_firmware_info - Get the firmware information from core driver
* @cgs_device: opaque device handle
* @type: the firmware type
* @info: returend firmware information
*
* Return: 0 on success, -errno otherwise
*/
typedef int (*cgs_get_firmware_info)(void *cgs_device,
enum cgs_ucode_id type,
struct cgs_firmware_info *info);
typedef int(*cgs_set_powergating_state)(void *cgs_device,
enum amd_ip_block_type block_type,
enum amd_powergating_state state);
typedef int(*cgs_set_clockgating_state)(void *cgs_device,
enum amd_ip_block_type block_type,
enum amd_clockgating_state state);
struct cgs_ops {
/* memory management calls (similar to KFD interface) */
cgs_gpu_mem_info_t gpu_mem_info;
cgs_gmap_kmem_t gmap_kmem;
cgs_gunmap_kmem_t gunmap_kmem;
cgs_alloc_gpu_mem_t alloc_gpu_mem;
cgs_free_gpu_mem_t free_gpu_mem;
cgs_gmap_gpu_mem_t gmap_gpu_mem;
cgs_gunmap_gpu_mem_t gunmap_gpu_mem;
cgs_kmap_gpu_mem_t kmap_gpu_mem;
cgs_kunmap_gpu_mem_t kunmap_gpu_mem;
/* MMIO access */
cgs_read_register_t read_register;
cgs_write_register_t write_register;
cgs_read_ind_register_t read_ind_register;
cgs_write_ind_register_t write_ind_register;
/* PCI configuration space access */
cgs_read_pci_config_byte_t read_pci_config_byte;
cgs_read_pci_config_word_t read_pci_config_word;
cgs_read_pci_config_dword_t read_pci_config_dword;
cgs_write_pci_config_byte_t write_pci_config_byte;
cgs_write_pci_config_word_t write_pci_config_word;
cgs_write_pci_config_dword_t write_pci_config_dword;
/* ATOM BIOS */
cgs_atom_get_data_table_t atom_get_data_table;
cgs_atom_get_cmd_table_revs_t atom_get_cmd_table_revs;
cgs_atom_exec_cmd_table_t atom_exec_cmd_table;
/* Power management */
cgs_create_pm_request_t create_pm_request;
cgs_destroy_pm_request_t destroy_pm_request;
cgs_set_pm_request_t set_pm_request;
cgs_pm_request_clock_t pm_request_clock;
cgs_pm_request_engine_t pm_request_engine;
cgs_pm_query_clock_limits_t pm_query_clock_limits;
cgs_set_camera_voltages_t set_camera_voltages;
/* Firmware Info */
cgs_get_firmware_info get_firmware_info;
/* cg pg interface*/
cgs_set_powergating_state set_powergating_state;
cgs_set_clockgating_state set_clockgating_state;
/* ACPI (TODO) */
};
struct cgs_os_ops; /* To be define in OS-specific CGS header */
struct cgs_device
{
const struct cgs_ops *ops;
const struct cgs_os_ops *os_ops;
/* to be embedded at the start of driver private structure */
};
/* Convenience macros that make CGS indirect function calls look like
* normal function calls */
#define CGS_CALL(func,dev,...) \
(((struct cgs_device *)dev)->ops->func(dev, ##__VA_ARGS__))
#define CGS_OS_CALL(func,dev,...) \
(((struct cgs_device *)dev)->os_ops->func(dev, ##__VA_ARGS__))
#define cgs_gpu_mem_info(dev,type,mc_start,mc_size,mem_size) \
CGS_CALL(gpu_mem_info,dev,type,mc_start,mc_size,mem_size)
#define cgs_gmap_kmem(dev,kmem,size,min_off,max_off,kmem_handle,mcaddr) \
CGS_CALL(gmap_kmem,dev,kmem,size,min_off,max_off,kmem_handle,mcaddr)
#define cgs_gunmap_kmem(dev,kmem_handle) \
CGS_CALL(gunmap_kmem,dev,keme_handle)
#define cgs_alloc_gpu_mem(dev,type,size,align,min_off,max_off,handle) \
CGS_CALL(alloc_gpu_mem,dev,type,size,align,min_off,max_off,handle)
#define cgs_free_gpu_mem(dev,handle) \
CGS_CALL(free_gpu_mem,dev,handle)
#define cgs_gmap_gpu_mem(dev,handle,mcaddr) \
CGS_CALL(gmap_gpu_mem,dev,handle,mcaddr)
#define cgs_gunmap_gpu_mem(dev,handle) \
CGS_CALL(gunmap_gpu_mem,dev,handle)
#define cgs_kmap_gpu_mem(dev,handle,map) \
CGS_CALL(kmap_gpu_mem,dev,handle,map)
#define cgs_kunmap_gpu_mem(dev,handle) \
CGS_CALL(kunmap_gpu_mem,dev,handle)
#define cgs_read_register(dev,offset) \
CGS_CALL(read_register,dev,offset)
#define cgs_write_register(dev,offset,value) \
CGS_CALL(write_register,dev,offset,value)
#define cgs_read_ind_register(dev,space,index) \
CGS_CALL(read_ind_register,dev,space,index)
#define cgs_write_ind_register(dev,space,index,value) \
CGS_CALL(write_ind_register,dev,space,index,value)
#define cgs_read_pci_config_byte(dev,addr) \
CGS_CALL(read_pci_config_byte,dev,addr)
#define cgs_read_pci_config_word(dev,addr) \
CGS_CALL(read_pci_config_word,dev,addr)
#define cgs_read_pci_config_dword(dev,addr) \
CGS_CALL(read_pci_config_dword,dev,addr)
#define cgs_write_pci_config_byte(dev,addr,value) \
CGS_CALL(write_pci_config_byte,dev,addr,value)
#define cgs_write_pci_config_word(dev,addr,value) \
CGS_CALL(write_pci_config_word,dev,addr,value)
#define cgs_write_pci_config_dword(dev,addr,value) \
CGS_CALL(write_pci_config_dword,dev,addr,value)
#define cgs_atom_get_data_table(dev,table,size,frev,crev) \
CGS_CALL(atom_get_data_table,dev,table,size,frev,crev)
#define cgs_atom_get_cmd_table_revs(dev,table,frev,crev) \
CGS_CALL(atom_get_cmd_table_revs,dev,table,frev,crev)
#define cgs_atom_exec_cmd_table(dev,table,args) \
CGS_CALL(atom_exec_cmd_table,dev,table,args)
#define cgs_create_pm_request(dev,request) \
CGS_CALL(create_pm_request,dev,request)
#define cgs_destroy_pm_request(dev,request) \
CGS_CALL(destroy_pm_request,dev,request)
#define cgs_set_pm_request(dev,request,active) \
CGS_CALL(set_pm_request,dev,request,active)
#define cgs_pm_request_clock(dev,request,clock,freq) \
CGS_CALL(pm_request_clock,dev,request,clock,freq)
#define cgs_pm_request_engine(dev,request,engine,powered) \
CGS_CALL(pm_request_engine,dev,request,engine,powered)
#define cgs_pm_query_clock_limits(dev,clock,limits) \
CGS_CALL(pm_query_clock_limits,dev,clock,limits)
#define cgs_set_camera_voltages(dev,mask,voltages) \
CGS_CALL(set_camera_voltages,dev,mask,voltages)
#define cgs_get_firmware_info(dev, type, info) \
CGS_CALL(get_firmware_info, dev, type, info)
#define cgs_set_powergating_state(dev, block_type, state) \
CGS_CALL(set_powergating_state, dev, block_type, state)
#define cgs_set_clockgating_state(dev, block_type, state) \
CGS_CALL(set_clockgating_state, dev, block_type, state)
#endif /* _CGS_COMMON_H */

View File

@ -0,0 +1,135 @@
/*
* Copyright 2015 Advanced Micro Devices, Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*
*
*/
#ifndef _CGS_LINUX_H
#define _CGS_LINUX_H
#include "cgs_common.h"
/**
* cgs_import_gpu_mem() - Import dmabuf handle
* @cgs_device: opaque device handle
* @dmabuf_fd: DMABuf file descriptor
* @handle: memory handle (output)
*
* Must be called in the process context that dmabuf_fd belongs to.
*
* Return: 0 on success, -errno otherwise
*/
typedef int (*cgs_import_gpu_mem_t)(void *cgs_device, int dmabuf_fd,
cgs_handle_t *handle);
/**
* cgs_irq_source_set_func() - Callback for enabling/disabling interrupt sources
* @private_data: private data provided to cgs_add_irq_source
* @src_id: interrupt source ID
* @type: interrupt type
* @enabled: 0 = disable source, non-0 = enable source
*
* Return: 0 on success, -errno otherwise
*/
typedef int (*cgs_irq_source_set_func_t)(void *private_data,
unsigned src_id, unsigned type,
int enabled);
/**
* cgs_irq_handler_func() - Interrupt handler callback
* @private_data: private data provided to cgs_add_irq_source
* @src_id: interrupt source ID
* @iv_entry: pointer to raw ih ring entry
*
* This callback runs in interrupt context.
*
* Return: 0 on success, -errno otherwise
*/
typedef int (*cgs_irq_handler_func_t)(void *private_data,
unsigned src_id, const uint32_t *iv_entry);
/**
* cgs_add_irq_source() - Add an IRQ source
* @cgs_device: opaque device handle
* @src_id: interrupt source ID
* @num_types: number of interrupt types that can be independently enabled
* @set: callback function to enable/disable an interrupt type
* @handler: interrupt handler callback
* @private_data: private data to pass to callback functions
*
* The same IRQ source can be added only once. Adding an IRQ source
* indicates ownership of that IRQ source and all its IRQ types.
*
* Return: 0 on success, -errno otherwise
*/
typedef int (*cgs_add_irq_source_t)(void *cgs_device, unsigned src_id,
unsigned num_types,
cgs_irq_source_set_func_t set,
cgs_irq_handler_func_t handler,
void *private_data);
/**
* cgs_irq_get() - Request enabling an IRQ source and type
* @cgs_device: opaque device handle
* @src_id: interrupt source ID
* @type: interrupt type
*
* cgs_irq_get and cgs_irq_put calls must be balanced. They count
* "references" to IRQ sources.
*
* Return: 0 on success, -errno otherwise
*/
typedef int (*cgs_irq_get_t)(void *cgs_device, unsigned src_id, unsigned type);
/**
* cgs_irq_put() - Indicate IRQ source is no longer needed
* @cgs_device: opaque device handle
* @src_id: interrupt source ID
* @type: interrupt type
*
* cgs_irq_get and cgs_irq_put calls must be balanced. They count
* "references" to IRQ sources. Even after cgs_irq_put is called, the
* IRQ handler may still be called if there are more refecences to
* the IRQ source.
*
* Return: 0 on success, -errno otherwise
*/
typedef int (*cgs_irq_put_t)(void *cgs_device, unsigned src_id, unsigned type);
struct cgs_os_ops {
cgs_import_gpu_mem_t import_gpu_mem;
/* IRQ handling */
cgs_add_irq_source_t add_irq_source;
cgs_irq_get_t irq_get;
cgs_irq_put_t irq_put;
};
#define cgs_import_gpu_mem(dev,dmabuf_fd,handle) \
CGS_OS_CALL(import_gpu_mem,dev,dmabuf_fd,handle)
#define cgs_add_irq_source(dev,src_id,num_types,set,handler,private_data) \
CGS_OS_CALL(add_irq_source,dev,src_id,num_types,set,handler, \
private_data)
#define cgs_irq_get(dev,src_id,type) \
CGS_OS_CALL(irq_get,dev,src_id,type)
#define cgs_irq_put(dev,src_id,type) \
CGS_OS_CALL(irq_put,dev,src_id,type)
#endif /* _CGS_LINUX_H */

View File

@ -146,6 +146,9 @@ typedef struct _ATOM_PPLIB_EXTENDEDHEADER
#define ATOM_PP_PLATFORM_CAP_VRHOT_GPIO_CONFIGURABLE 0x00200000 // Does the driver supports VR HOT GPIO Configurable.
#define ATOM_PP_PLATFORM_CAP_TEMP_INVERSION 0x00400000 // Does the driver supports Temp Inversion feature.
#define ATOM_PP_PLATFORM_CAP_EVV 0x00800000
#define ATOM_PP_PLATFORM_COMBINE_PCC_WITH_THERMAL_SIGNAL 0x01000000
#define ATOM_PP_PLATFORM_LOAD_POST_PRODUCTION_FIRMWARE 0x02000000
#define ATOM_PP_PLATFORM_CAP_DISABLE_USING_ACTUAL_TEMPERATURE_FOR_POWER_CALC 0x04000000
typedef struct _ATOM_PPLIB_POWERPLAYTABLE
{
@ -673,7 +676,8 @@ typedef struct _ATOM_PPLIB_POWERTUNE_Table_V1
UCHAR revid;
ATOM_PowerTune_Table power_tune_table;
USHORT usMaximumPowerDeliveryLimit;
USHORT usReserve[7];
USHORT usTjMax;
USHORT usReserve[6];
} ATOM_PPLIB_POWERTUNE_Table_V1;
#define ATOM_PPM_A_A 1

View File

@ -0,0 +1,462 @@
/*
* Copyright 2015 Advanced Micro Devices, Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*
*
*/
#include <linux/kthread.h>
#include <linux/wait.h>
#include <linux/sched.h>
#include <drm/drmP.h>
#include "gpu_scheduler.h"
/* Initialize a given run queue struct */
static void amd_sched_rq_init(struct amd_sched_rq *rq)
{
INIT_LIST_HEAD(&rq->entities);
mutex_init(&rq->lock);
rq->current_entity = NULL;
}
static void amd_sched_rq_add_entity(struct amd_sched_rq *rq,
struct amd_sched_entity *entity)
{
mutex_lock(&rq->lock);
list_add_tail(&entity->list, &rq->entities);
mutex_unlock(&rq->lock);
}
static void amd_sched_rq_remove_entity(struct amd_sched_rq *rq,
struct amd_sched_entity *entity)
{
mutex_lock(&rq->lock);
list_del_init(&entity->list);
if (rq->current_entity == entity)
rq->current_entity = NULL;
mutex_unlock(&rq->lock);
}
/**
* Select next entity from a specified run queue with round robin policy.
* It could return the same entity as current one if current is the only
* available one in the queue. Return NULL if nothing available.
*/
static struct amd_sched_entity *
amd_sched_rq_select_entity(struct amd_sched_rq *rq)
{
struct amd_sched_entity *entity = rq->current_entity;
if (entity) {
list_for_each_entry_continue(entity, &rq->entities, list) {
if (!kfifo_is_empty(&entity->job_queue)) {
rq->current_entity = entity;
return rq->current_entity;
}
}
}
list_for_each_entry(entity, &rq->entities, list) {
if (!kfifo_is_empty(&entity->job_queue)) {
rq->current_entity = entity;
return rq->current_entity;
}
if (entity == rq->current_entity)
break;
}
return NULL;
}
/**
* Note: This function should only been called inside scheduler main
* function for thread safety, there is no other protection here.
* return ture if scheduler has something ready to run.
*
* For active_hw_rq, there is only one producer(scheduler thread) and
* one consumer(ISR). It should be safe to use this function in scheduler
* main thread to decide whether to continue emit more IBs.
*/
static bool is_scheduler_ready(struct amd_gpu_scheduler *sched)
{
unsigned long flags;
bool full;
spin_lock_irqsave(&sched->queue_lock, flags);
full = atomic64_read(&sched->hw_rq_count) <
sched->hw_submission_limit ? true : false;
spin_unlock_irqrestore(&sched->queue_lock, flags);
return full;
}
/**
* Select next entity from the kernel run queue, if not available,
* return null.
*/
static struct amd_sched_entity *
kernel_rq_select_context(struct amd_gpu_scheduler *sched)
{
struct amd_sched_entity *sched_entity;
struct amd_sched_rq *rq = &sched->kernel_rq;
mutex_lock(&rq->lock);
sched_entity = amd_sched_rq_select_entity(rq);
mutex_unlock(&rq->lock);
return sched_entity;
}
/**
* Select next entity containing real IB submissions
*/
static struct amd_sched_entity *
select_context(struct amd_gpu_scheduler *sched)
{
struct amd_sched_entity *wake_entity = NULL;
struct amd_sched_entity *tmp;
struct amd_sched_rq *rq;
if (!is_scheduler_ready(sched))
return NULL;
/* Kernel run queue has higher priority than normal run queue*/
tmp = kernel_rq_select_context(sched);
if (tmp != NULL)
goto exit;
rq = &sched->sched_rq;
mutex_lock(&rq->lock);
tmp = amd_sched_rq_select_entity(rq);
mutex_unlock(&rq->lock);
exit:
if (sched->current_entity && (sched->current_entity != tmp))
wake_entity = sched->current_entity;
sched->current_entity = tmp;
if (wake_entity && wake_entity->need_wakeup)
wake_up(&wake_entity->wait_queue);
return tmp;
}
/**
* Init a context entity used by scheduler when submit to HW ring.
*
* @sched The pointer to the scheduler
* @entity The pointer to a valid amd_sched_entity
* @rq The run queue this entity belongs
* @kernel If this is an entity for the kernel
* @jobs The max number of jobs in the job queue
*
* return 0 if succeed. negative error code on failure
*/
int amd_sched_entity_init(struct amd_gpu_scheduler *sched,
struct amd_sched_entity *entity,
struct amd_sched_rq *rq,
uint32_t jobs)
{
uint64_t seq_ring = 0;
char name[20];
if (!(sched && entity && rq))
return -EINVAL;
memset(entity, 0, sizeof(struct amd_sched_entity));
seq_ring = ((uint64_t)sched->ring_id) << 60;
spin_lock_init(&entity->lock);
entity->belongto_rq = rq;
entity->scheduler = sched;
init_waitqueue_head(&entity->wait_queue);
init_waitqueue_head(&entity->wait_emit);
entity->fence_context = fence_context_alloc(1);
snprintf(name, sizeof(name), "c_entity[%llu]", entity->fence_context);
memcpy(entity->name, name, 20);
entity->need_wakeup = false;
if(kfifo_alloc(&entity->job_queue,
jobs * sizeof(void *),
GFP_KERNEL))
return -EINVAL;
spin_lock_init(&entity->queue_lock);
atomic64_set(&entity->last_queued_v_seq, seq_ring);
atomic64_set(&entity->last_signaled_v_seq, seq_ring);
/* Add the entity to the run queue */
amd_sched_rq_add_entity(rq, entity);
return 0;
}
/**
* Query if entity is initialized
*
* @sched Pointer to scheduler instance
* @entity The pointer to a valid scheduler entity
*
* return true if entity is initialized, false otherwise
*/
static bool is_context_entity_initialized(struct amd_gpu_scheduler *sched,
struct amd_sched_entity *entity)
{
return entity->scheduler == sched &&
entity->belongto_rq != NULL;
}
static bool is_context_entity_idle(struct amd_gpu_scheduler *sched,
struct amd_sched_entity *entity)
{
/**
* Idle means no pending IBs, and the entity is not
* currently being used.
*/
barrier();
if ((sched->current_entity != entity) &&
kfifo_is_empty(&entity->job_queue))
return true;
return false;
}
/**
* Destroy a context entity
*
* @sched Pointer to scheduler instance
* @entity The pointer to a valid scheduler entity
*
* return 0 if succeed. negative error code on failure
*/
int amd_sched_entity_fini(struct amd_gpu_scheduler *sched,
struct amd_sched_entity *entity)
{
int r = 0;
struct amd_sched_rq *rq = entity->belongto_rq;
if (!is_context_entity_initialized(sched, entity))
return 0;
entity->need_wakeup = true;
/**
* The client will not queue more IBs during this fini, consume existing
* queued IBs
*/
r = wait_event_timeout(
entity->wait_queue,
is_context_entity_idle(sched, entity),
msecs_to_jiffies(AMD_GPU_WAIT_IDLE_TIMEOUT_IN_MS)
) ? 0 : -1;
if (r) {
if (entity->is_pending)
DRM_INFO("Entity %p is in waiting state during fini,\
all pending ibs will be canceled.\n",
entity);
}
amd_sched_rq_remove_entity(rq, entity);
kfifo_free(&entity->job_queue);
return r;
}
/**
* Submit a normal job to the job queue
*
* @sched The pointer to the scheduler
* @c_entity The pointer to amd_sched_entity
* @job The pointer to job required to submit
* return 0 if succeed. -1 if failed.
* -2 indicate queue is full for this client, client should wait untill
* scheduler consum some queued command.
* -1 other fail.
*/
int amd_sched_push_job(struct amd_gpu_scheduler *sched,
struct amd_sched_entity *c_entity,
void *data,
struct amd_sched_fence **fence)
{
struct amd_sched_job *job;
if (!fence)
return -EINVAL;
job = kzalloc(sizeof(struct amd_sched_job), GFP_KERNEL);
if (!job)
return -ENOMEM;
job->sched = sched;
job->s_entity = c_entity;
job->data = data;
*fence = amd_sched_fence_create(c_entity);
if ((*fence) == NULL) {
kfree(job);
return -EINVAL;
}
fence_get(&(*fence)->base);
job->s_fence = *fence;
while (kfifo_in_spinlocked(&c_entity->job_queue, &job, sizeof(void *),
&c_entity->queue_lock) != sizeof(void *)) {
/**
* Current context used up all its IB slots
* wait here, or need to check whether GPU is hung
*/
schedule();
}
/* first job wake up scheduler */
if ((kfifo_len(&c_entity->job_queue) / sizeof(void *)) == 1)
wake_up_interruptible(&sched->wait_queue);
return 0;
}
static void amd_sched_process_job(struct fence *f, struct fence_cb *cb)
{
struct amd_sched_job *sched_job =
container_of(cb, struct amd_sched_job, cb);
struct amd_gpu_scheduler *sched;
unsigned long flags;
sched = sched_job->sched;
atomic64_set(&sched_job->s_entity->last_signaled_v_seq,
sched_job->s_fence->v_seq);
amd_sched_fence_signal(sched_job->s_fence);
spin_lock_irqsave(&sched->queue_lock, flags);
list_del(&sched_job->list);
atomic64_dec(&sched->hw_rq_count);
spin_unlock_irqrestore(&sched->queue_lock, flags);
sched->ops->process_job(sched, sched_job);
fence_put(&sched_job->s_fence->base);
kfree(sched_job);
wake_up_interruptible(&sched->wait_queue);
}
static int amd_sched_main(void *param)
{
int r;
struct amd_sched_job *job;
struct sched_param sparam = {.sched_priority = 1};
struct amd_sched_entity *c_entity = NULL;
struct amd_gpu_scheduler *sched = (struct amd_gpu_scheduler *)param;
sched_setscheduler(current, SCHED_FIFO, &sparam);
while (!kthread_should_stop()) {
struct fence *fence;
wait_event_interruptible(sched->wait_queue,
is_scheduler_ready(sched) &&
(c_entity = select_context(sched)));
r = kfifo_out(&c_entity->job_queue, &job, sizeof(void *));
if (r != sizeof(void *))
continue;
r = sched->ops->prepare_job(sched, c_entity, job);
if (!r) {
unsigned long flags;
spin_lock_irqsave(&sched->queue_lock, flags);
list_add_tail(&job->list, &sched->active_hw_rq);
atomic64_inc(&sched->hw_rq_count);
spin_unlock_irqrestore(&sched->queue_lock, flags);
}
mutex_lock(&sched->sched_lock);
fence = sched->ops->run_job(sched, c_entity, job);
if (fence) {
r = fence_add_callback(fence, &job->cb,
amd_sched_process_job);
if (r == -ENOENT)
amd_sched_process_job(fence, &job->cb);
else if (r)
DRM_ERROR("fence add callback failed (%d)\n", r);
fence_put(fence);
}
mutex_unlock(&sched->sched_lock);
}
return 0;
}
/**
* Create a gpu scheduler
*
* @device The device context for this scheduler
* @ops The backend operations for this scheduler.
* @id The scheduler is per ring, here is ring id.
* @granularity The minumum ms unit the scheduler will scheduled.
* @preemption Indicate whether this ring support preemption, 0 is no.
*
* return the pointer to scheduler for success, otherwise return NULL
*/
struct amd_gpu_scheduler *amd_sched_create(void *device,
struct amd_sched_backend_ops *ops,
unsigned ring,
unsigned granularity,
unsigned preemption,
unsigned hw_submission)
{
struct amd_gpu_scheduler *sched;
char name[20];
sched = kzalloc(sizeof(struct amd_gpu_scheduler), GFP_KERNEL);
if (!sched)
return NULL;
sched->device = device;
sched->ops = ops;
sched->granularity = granularity;
sched->ring_id = ring;
sched->preemption = preemption;
sched->hw_submission_limit = hw_submission;
snprintf(name, sizeof(name), "gpu_sched[%d]", ring);
mutex_init(&sched->sched_lock);
spin_lock_init(&sched->queue_lock);
amd_sched_rq_init(&sched->sched_rq);
amd_sched_rq_init(&sched->kernel_rq);
init_waitqueue_head(&sched->wait_queue);
INIT_LIST_HEAD(&sched->active_hw_rq);
atomic64_set(&sched->hw_rq_count, 0);
/* Each scheduler will run on a seperate kernel thread */
sched->thread = kthread_create(amd_sched_main, sched, name);
if (sched->thread) {
wake_up_process(sched->thread);
return sched;
}
DRM_ERROR("Failed to create scheduler for id %d.\n", ring);
kfree(sched);
return NULL;
}
/**
* Destroy a gpu scheduler
*
* @sched The pointer to the scheduler
*
* return 0 if succeed. -1 if failed.
*/
int amd_sched_destroy(struct amd_gpu_scheduler *sched)
{
kthread_stop(sched->thread);
kfree(sched);
return 0;
}
/**
* Get next queued sequence number
*
* @entity The context entity
*
* return the next queued sequence number
*/
uint64_t amd_sched_next_queued_seq(struct amd_sched_entity *c_entity)
{
return atomic64_read(&c_entity->last_queued_v_seq) + 1;
}

View File

@ -0,0 +1,162 @@
/*
* Copyright 2015 Advanced Micro Devices, Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*
*/
#ifndef _GPU_SCHEDULER_H_
#define _GPU_SCHEDULER_H_
#include <linux/kfifo.h>
#include <linux/fence.h>
#define AMD_GPU_WAIT_IDLE_TIMEOUT_IN_MS 3000
struct amd_gpu_scheduler;
struct amd_sched_rq;
/**
* A scheduler entity is a wrapper around a job queue or a group
* of other entities. Entities take turns emitting jobs from their
* job queues to corresponding hardware ring based on scheduling
* policy.
*/
struct amd_sched_entity {
struct list_head list;
struct amd_sched_rq *belongto_rq;
spinlock_t lock;
/* the virtual_seq is unique per context per ring */
atomic64_t last_queued_v_seq;
atomic64_t last_signaled_v_seq;
/* the job_queue maintains the jobs submitted by clients */
struct kfifo job_queue;
spinlock_t queue_lock;
struct amd_gpu_scheduler *scheduler;
wait_queue_head_t wait_queue;
wait_queue_head_t wait_emit;
bool is_pending;
uint64_t fence_context;
char name[20];
bool need_wakeup;
};
/**
* Run queue is a set of entities scheduling command submissions for
* one specific ring. It implements the scheduling policy that selects
* the next entity to emit commands from.
*/
struct amd_sched_rq {
struct mutex lock;
struct list_head entities;
struct amd_sched_entity *current_entity;
};
struct amd_sched_fence {
struct fence base;
struct fence_cb cb;
struct amd_sched_entity *entity;
uint64_t v_seq;
spinlock_t lock;
};
struct amd_sched_job {
struct list_head list;
struct fence_cb cb;
struct amd_gpu_scheduler *sched;
struct amd_sched_entity *s_entity;
void *data;
struct amd_sched_fence *s_fence;
};
extern const struct fence_ops amd_sched_fence_ops;
static inline struct amd_sched_fence *to_amd_sched_fence(struct fence *f)
{
struct amd_sched_fence *__f = container_of(f, struct amd_sched_fence, base);
if (__f->base.ops == &amd_sched_fence_ops)
return __f;
return NULL;
}
/**
* Define the backend operations called by the scheduler,
* these functions should be implemented in driver side
*/
struct amd_sched_backend_ops {
int (*prepare_job)(struct amd_gpu_scheduler *sched,
struct amd_sched_entity *c_entity,
struct amd_sched_job *job);
struct fence *(*run_job)(struct amd_gpu_scheduler *sched,
struct amd_sched_entity *c_entity,
struct amd_sched_job *job);
void (*process_job)(struct amd_gpu_scheduler *sched,
struct amd_sched_job *job);
};
/**
* One scheduler is implemented for each hardware ring
*/
struct amd_gpu_scheduler {
void *device;
struct task_struct *thread;
struct amd_sched_rq sched_rq;
struct amd_sched_rq kernel_rq;
struct list_head active_hw_rq;
atomic64_t hw_rq_count;
struct amd_sched_backend_ops *ops;
uint32_t ring_id;
uint32_t granularity; /* in ms unit */
uint32_t preemption;
wait_queue_head_t wait_queue;
struct amd_sched_entity *current_entity;
struct mutex sched_lock;
spinlock_t queue_lock;
uint32_t hw_submission_limit;
};
struct amd_gpu_scheduler *amd_sched_create(void *device,
struct amd_sched_backend_ops *ops,
uint32_t ring,
uint32_t granularity,
uint32_t preemption,
uint32_t hw_submission);
int amd_sched_destroy(struct amd_gpu_scheduler *sched);
int amd_sched_push_job(struct amd_gpu_scheduler *sched,
struct amd_sched_entity *c_entity,
void *data,
struct amd_sched_fence **fence);
int amd_sched_entity_init(struct amd_gpu_scheduler *sched,
struct amd_sched_entity *entity,
struct amd_sched_rq *rq,
uint32_t jobs);
int amd_sched_entity_fini(struct amd_gpu_scheduler *sched,
struct amd_sched_entity *entity);
uint64_t amd_sched_next_queued_seq(struct amd_sched_entity *c_entity);
struct amd_sched_fence *amd_sched_fence_create(
struct amd_sched_entity *s_entity);
void amd_sched_fence_signal(struct amd_sched_fence *fence);
#endif

View File

@ -0,0 +1,78 @@
/*
* Copyright 2015 Advanced Micro Devices, Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*
*
*/
#include <linux/kthread.h>
#include <linux/wait.h>
#include <linux/sched.h>
#include <drm/drmP.h>
#include "gpu_scheduler.h"
struct amd_sched_fence *amd_sched_fence_create(struct amd_sched_entity *s_entity)
{
struct amd_sched_fence *fence = NULL;
fence = kzalloc(sizeof(struct amd_sched_fence), GFP_KERNEL);
if (fence == NULL)
return NULL;
fence->v_seq = atomic64_inc_return(&s_entity->last_queued_v_seq);
fence->entity = s_entity;
spin_lock_init(&fence->lock);
fence_init(&fence->base, &amd_sched_fence_ops,
&fence->lock,
s_entity->fence_context,
fence->v_seq);
return fence;
}
void amd_sched_fence_signal(struct amd_sched_fence *fence)
{
int ret = fence_signal(&fence->base);
if (!ret)
FENCE_TRACE(&fence->base, "signaled from irq context\n");
else
FENCE_TRACE(&fence->base, "was already signaled\n");
}
static const char *amd_sched_fence_get_driver_name(struct fence *fence)
{
return "amd_sched";
}
static const char *amd_sched_fence_get_timeline_name(struct fence *f)
{
struct amd_sched_fence *fence = to_amd_sched_fence(f);
return (const char *)fence->entity->name;
}
static bool amd_sched_fence_enable_signaling(struct fence *f)
{
return true;
}
const struct fence_ops amd_sched_fence_ops = {
.get_driver_name = amd_sched_fence_get_driver_name,
.get_timeline_name = amd_sched_fence_get_timeline_name,
.enable_signaling = amd_sched_fence_enable_signaling,
.signaled = NULL,
.wait = fence_default_wait,
.release = NULL,
};

View File

@ -76,16 +76,35 @@ static void dce6_afmt_get_connected_pins(struct radeon_device *rdev)
struct r600_audio_pin *dce6_audio_get_pin(struct radeon_device *rdev)
{
int i;
struct drm_encoder *encoder;
struct radeon_encoder *radeon_encoder;
struct radeon_encoder_atom_dig *dig;
struct r600_audio_pin *pin = NULL;
int i, pin_count;
dce6_afmt_get_connected_pins(rdev);
for (i = 0; i < rdev->audio.num_pins; i++) {
if (rdev->audio.pin[i].connected)
return &rdev->audio.pin[i];
if (rdev->audio.pin[i].connected) {
pin = &rdev->audio.pin[i];
pin_count = 0;
list_for_each_entry(encoder, &rdev->ddev->mode_config.encoder_list, head) {
if (radeon_encoder_is_digital(encoder)) {
radeon_encoder = to_radeon_encoder(encoder);
dig = radeon_encoder->enc_priv;
if (dig->pin == pin)
pin_count++;
}
}
if (pin_count == 0)
return pin;
}
}
DRM_ERROR("No connected audio pins found!\n");
return NULL;
if (!pin)
DRM_ERROR("No connected audio pins found!\n");
return pin;
}
void dce6_afmt_select_pin(struct drm_encoder *encoder)

View File

@ -419,7 +419,6 @@ void radeon_bo_force_delete(struct radeon_device *rdev)
}
dev_err(rdev->dev, "Userspace still has active objects !\n");
list_for_each_entry_safe(bo, n, &rdev->gem.objects, list) {
mutex_lock(&rdev->ddev->struct_mutex);
dev_err(rdev->dev, "%p %p %lu %lu force free\n",
&bo->gem_base, bo, (unsigned long)bo->gem_base.size,
*((unsigned long *)&bo->gem_base.refcount));
@ -427,8 +426,7 @@ void radeon_bo_force_delete(struct radeon_device *rdev)
list_del_init(&bo->list);
mutex_unlock(&bo->rdev->gem.mutex);
/* this should unref the ttm bo */
drm_gem_object_unreference(&bo->gem_base);
mutex_unlock(&rdev->ddev->struct_mutex);
drm_gem_object_unreference_unlocked(&bo->gem_base);
}
}

View File

@ -253,7 +253,6 @@ static void radeon_pm_set_clocks(struct radeon_device *rdev)
(rdev->pm.requested_power_state_index == rdev->pm.current_power_state_index))
return;
mutex_lock(&rdev->ddev->struct_mutex);
down_write(&rdev->pm.mclk_lock);
mutex_lock(&rdev->ring_lock);
@ -268,7 +267,6 @@ static void radeon_pm_set_clocks(struct radeon_device *rdev)
/* needs a GPU reset dont reset here */
mutex_unlock(&rdev->ring_lock);
up_write(&rdev->pm.mclk_lock);
mutex_unlock(&rdev->ddev->struct_mutex);
return;
}
}
@ -304,7 +302,6 @@ static void radeon_pm_set_clocks(struct radeon_device *rdev)
mutex_unlock(&rdev->ring_lock);
up_write(&rdev->pm.mclk_lock);
mutex_unlock(&rdev->ddev->struct_mutex);
}
static void radeon_pm_print_states(struct radeon_device *rdev)
@ -1062,7 +1059,6 @@ force:
radeon_dpm_print_power_state(rdev, rdev->pm.dpm.requested_ps);
}
mutex_lock(&rdev->ddev->struct_mutex);
down_write(&rdev->pm.mclk_lock);
mutex_lock(&rdev->ring_lock);
@ -1113,7 +1109,6 @@ force:
done:
mutex_unlock(&rdev->ring_lock);
up_write(&rdev->pm.mclk_lock);
mutex_unlock(&rdev->ddev->struct_mutex);
}
void radeon_dpm_enable_uvd(struct radeon_device *rdev, bool enable)