drm fixes for 7.3-rc4

core:
 - fix vblank pending event leak
 
 ttm:
 - swapout fixes
 
 dma-buf:
 - scattergather fixes
 - enable dma-buf debug on debug kernels
 
 dma-fence:
 - fix signaling bit checks
 
 sched:
 - fix virtual runtime race
 
 msm:
 - DT:
   - Corrected indentation
 - Core:
   - Marked fbdev as system memory
 - GPU:
   - Fixed autosuspend cleanup on teardown
   - a750: fix timestamps
   - Increase GMU fw init timeout
   - Misc fixes/cleanups
 - DPU:
   - Fixed clock rounding, unbreaking newest platforms
   - Cleared pending flush state
 - DP:
   - Skip PUSH_IDLE when link was never enabled
   - Fixed bandwidth checks
 - HDMI:
   - Fixed runtime PM cleanup on probe failure
 
 xe:
 - shrinker related fixes
 - xe_mmio_gem fault handler and destroy fixes
 - xe disable i2c irq on unbind
 
 i915:
 - Revert a commit touching registers that don't necessarily exist
 - Check for negative numbers before passing to BIT()
 
 amdgpu:
 - SMU 14.x fix
 - DC IRQ fix
 - Runtime PM fix for P2P
 - RAS fix
 - PCIe reporting fix
 - DCN 6 fix
 - Device removal fix
 - DC MALL fix
 
 amdkfd:
 - GC 12.x fixes
 - Boundary checks
 - Mapping clear fix
 
 nouveau:
 - suspend/resume fixes
 
 gud:
 - out of bounds access fix
 - ignore damage clips in full update
 
 vc4:
 - use-after-free fix
 -----BEGIN PGP SIGNATURE-----
 
 iQIzBAABCgAdFiEEEKbZHaGwW9KfbeusDHTzWXnEhr4FAmqttwkACgkQDHTzWXnE
 hr73Ww//S+8xgr8cpmJxoUrOrTnd4yeX/oj+HSRkEHQPdNayOf+pNnP4y+nChfXi
 ddQ3jTRyG5JwNmXRG0ouKf9koQk/V54R8v9CBtNQYsN2K58xW6riCIaEeOUbe3r1
 1IQgYCEfS32b7/9D2lo1768LbJ0KWBr6qznKrfvC7vJ62wK3F0B9EzOmsKSzLxd3
 gdUfyxEbtgegKOAamCbUyJyuzCErGkMAtkQ0HnmQGYmqqBBRS7Uvl1HN10JMoLSM
 MmR40g+dsp6ZzBDywNrdmIGDv749o1/k/zm5i6c2hJSibYBPR5sWtKICwu1ND44u
 ts2HjJJdyhm5PLiS09i9nVUnMkzV0N2czIDZvt9izp8+k8P4Bh/y1iagda1G0U65
 h7dp9j8WXMCApxXNjplNgjMLSO7g4UJI1rAWjxGY4ecu62XHErrZ6tbjrOVOaa1T
 Xh8IN4EqPr+tRSKnW4AxwwwA1EfxNwoJSxglAvBvIaCjEhRWRlj7Sdo0Wrtg3WKy
 sORar40ySYHR+MeAbabewjRoMPq1Nyqh4D2PrOv48a4MK7a6Fl5ed6EHpyOSHYKF
 7tMbLLLtDMIYX24wd/j8CkvhcTp09iphzXSqmUlIBn3t1gbrXspzCQtRU0UrsamJ
 QSbtL+qUoYSsobmpYOzuTVnO8W4oOnnIYaLz+TpVNDd7o7nzZf0=
 =EC37
 -----END PGP SIGNATURE-----

Merge tag 'drm-fixes-2026-09-19' of https://gitlab.freedesktop.org/drm/kernel

Pull drm fixes from Dave Airlie:
 "Things have picked back up a bit this week, mostly amdgpu, xe and msm
  this time. There are a bunch of scattered changes across the rest of
  drivers and core stuff, nouveau, i915.

  core:
   - fix vblank pending event leak

  ttm:
   - swapout fixes

  dma-buf:
   - scattergather fixes
   - enable dma-buf debug on debug kernels

  dma-fence:
   - fix signaling bit checks

  sched:
   - fix virtual runtime race

  msm:
   - DT:
      - Corrected indentation
   - Core:
      - Marked fbdev as system memory
   - GPU:
      - Fixed autosuspend cleanup on teardown
      - a750: fix timestamps
      - Increase GMU fw init timeout
      - Misc fixes/cleanups
   - DPU:
      - Fixed clock rounding, unbreaking newest platforms
      - Cleared pending flush state
   - DP:
      - Skip PUSH_IDLE when link was never enabled
      - Fixed bandwidth checks
   - HDMI:
      - Fixed runtime PM cleanup on probe failure

  xe:
   - shrinker related fixes
   - xe_mmio_gem fault handler and destroy fixes
   - xe disable i2c irq on unbind

  i915:
   - Revert a commit touching registers that don't necessarily exist
   - Check for negative numbers before passing to BIT()

  amdgpu:
   - SMU 14.x fix
   - DC IRQ fix
   - Runtime PM fix for P2P
   - RAS fix
   - PCIe reporting fix
   - DCN 6 fix
   - Device removal fix
   - DC MALL fix

  amdkfd:
   - GC 12.x fixes
   - Boundary checks
   - Mapping clear fix

  nouveau:
   - suspend/resume fixes

  gud:
   - out of bounds access fix
   - ignore damage clips in full update

  vc4:
   - use-after-free fix

  versilicon:
   - plane format fix

  longsoon:
   - blend mode property fix"

* tag 'drm-fixes-2026-09-19' of https://gitlab.freedesktop.org/drm/kernel: (59 commits)
  drm/amd/display: fix MALL hysteresis timer underflow at high refresh rates
  drm/amdgpu: fix rmmio iounmap skipped on device removal
  drm/amdgpu: Skip KFD mapping clear before initialization
  drm/amd/display: Fix NULL dereference in dcn50/dcn60 init_hw
  drm/amdkfd: Avoid integer underflow in EOP ring size calculation.
  drm/amdkfd: Avoid integer underflow with ffs in EOP ring size calc
  drm/amdgpu: Fix GPU PCIe link capability reporting
  drm/amdgpu: check ras and obj before dereference
  drm/amdgpu: hold a runtime PM reference for P2P dma-buf attachments
  drm/amdkfd: implement restore_mqd callbacks for GFX12/12.1
  drm/amd/display: Atomize IRQ register read/modify/write ops
  drm/amd/pm: report energy accumulator for smu 14.0.3
  drm/loongson: Create blend mode property for cursor plane
  drm/xe/i2c: Disable IRQ on unbind
  Revert "drm/i915/display: Clear SEL_FETCH_PLANE_CTL on plane disable"
  drm/verisilicon: remove ARGB formats from primary plane
  drm/verisilicon: add primary modifier for format tables
  drm/verisilicon: set blend mode for the cursor plane
  drm/sched: Fix virtual runtime race
  drm/i915/display: check configuration index before shifting
  ...
This commit is contained in:
Linus Torvalds 2026-09-18 16:37:37 -07:00
commit 40288c9206
75 changed files with 743 additions and 388 deletions

View File

@ -150,7 +150,7 @@ examples:
reg = <0>;
dpu_intf0_out: endpoint {
remote-endpoint = <&mdss_dp0_in>;
remote-endpoint = <&mdss_dp0_in>;
};
};
@ -352,9 +352,9 @@ examples:
};
port@1 {
reg = <1>;
reg = <1>;
mdss_dp_out: endpoint { };
mdss_dp_out: endpoint { };
};
};

View File

@ -248,9 +248,9 @@ examples:
remote-endpoint = <&usb_dp_qmpphy_dp_in>;
};
};
};
};
dp_opp_table: opp-table {
dp_opp_table: opp-table {
compatible = "operating-points-v2";
opp-162000000 {

View File

@ -81,7 +81,7 @@ examples:
port@1 {
reg = <1>;
dpu_intf1_out: endpoint {
remote-endpoint = <&mdss_dsi0_in>;
remote-endpoint = <&mdss_dsi0_in>;
};
};
};
@ -90,18 +90,18 @@ examples:
compatible = "operating-points-v2";
opp-19200000 {
opp-hz = /bits/ 64 <19200000>;
required-opps = <&rpmhpd_opp_low_svs>;
opp-hz = /bits/ 64 <19200000>;
required-opps = <&rpmhpd_opp_low_svs>;
};
opp-25600000 {
opp-hz = /bits/ 64 <25600000>;
required-opps = <&rpmhpd_opp_svs>;
opp-hz = /bits/ 64 <25600000>;
required-opps = <&rpmhpd_opp_svs>;
};
opp-307200000 {
opp-hz = /bits/ 64 <307200000>;
required-opps = <&rpmhpd_opp_nom>;
opp-hz = /bits/ 64 <307200000>;
required-opps = <&rpmhpd_opp_nom>;
};
};
};

View File

@ -223,7 +223,7 @@ examples:
phys = <&mdss_dsi0_phy>;
ports {
#address-cells = <1>;
#address-cells = <1>;
#size-cells = <0>;
port@0 {

View File

@ -208,47 +208,47 @@ examples:
#sound-dai-cells = <0>;
ports {
#address-cells = <1>;
#size-cells = <0>;
#address-cells = <1>;
#size-cells = <0>;
port@0 {
reg = <0>;
port@0 {
reg = <0>;
mdss_dp0_in: endpoint {
remote-endpoint = <&mdss_intf0_out>;
};
};
mdss_dp0_in: endpoint {
remote-endpoint = <&mdss_intf0_out>;
};
};
port@1 {
reg = <1>;
port@1 {
reg = <1>;
mdss_dp0_out: endpoint {
};
};
mdss_dp0_out: endpoint {
};
};
};
mdss_dp0_opp_table: opp-table {
compatible = "operating-points-v2";
compatible = "operating-points-v2";
opp-160000000 {
opp-hz = /bits/ 64 <160000000>;
required-opps = <&rpmhpd_opp_low_svs>;
};
opp-160000000 {
opp-hz = /bits/ 64 <160000000>;
required-opps = <&rpmhpd_opp_low_svs>;
};
opp-270000000 {
opp-hz = /bits/ 64 <270000000>;
required-opps = <&rpmhpd_opp_svs>;
};
opp-270000000 {
opp-hz = /bits/ 64 <270000000>;
required-opps = <&rpmhpd_opp_svs>;
};
opp-540000000 {
opp-hz = /bits/ 64 <540000000>;
required-opps = <&rpmhpd_opp_svs_l1>;
};
opp-540000000 {
opp-hz = /bits/ 64 <540000000>;
required-opps = <&rpmhpd_opp_svs_l1>;
};
opp-810000000 {
opp-hz = /bits/ 64 <810000000>;
required-opps = <&rpmhpd_opp_nom>;
};
opp-810000000 {
opp-hz = /bits/ 64 <810000000>;
required-opps = <&rpmhpd_opp_nom>;
};
};
};
};

View File

@ -43,7 +43,7 @@ config UDMABUF
config DMABUF_DEBUG
bool "DMA-BUF debug checks"
depends on DMA_SHARED_BUFFER
default y if DEBUG
default y if DEBUG_KERNEL
help
This option enables additional checks for DMA-BUF importers and
exporters. Specifically it validates that importers do not peek at the

View File

@ -5,16 +5,18 @@
*/
#include <linux/dma-buf-mapping.h>
#include <linux/dma-resv.h>
#include <linux/overflow.h>
#include <linux/align.h>
#define MAX_SG_ENT_SZ ALIGN_DOWN(UINT_MAX, PAGE_SIZE)
static struct scatterlist *fill_sg_entry(struct scatterlist *sgl, size_t length,
dma_addr_t addr)
{
unsigned int len, nents;
int i;
size_t len;
nents = DIV_ROUND_UP(length, UINT_MAX);
for (i = 0; i < nents; i++) {
len = min_t(size_t, length, UINT_MAX);
while (length) {
len = min(length, MAX_SG_ENT_SZ);
length -= len;
/*
* DMABUF abuses scatterlist to create a scatterlist
@ -24,8 +26,10 @@ static struct scatterlist *fill_sg_entry(struct scatterlist *sgl, size_t length,
* does not require the CPU list for mapping or unmapping.
*/
sg_set_page(sgl, NULL, 0, 0);
sg_dma_address(sgl) = addr + (dma_addr_t)i * UINT_MAX;
sg_dma_address(sgl) = addr;
sg_dma_len(sgl) = len;
addr += len;
/* Unconditionally advance. On last segment, this becomes NULL */
sgl = sg_next(sgl);
}
@ -40,15 +44,19 @@ static unsigned int calc_sg_nents(struct dma_iova_state *state,
size_t i;
if (!state || !dma_use_iova(state)) {
for (i = 0; i < nr_ranges; i++)
nents += DIV_ROUND_UP(phys_vec[i].len, UINT_MAX);
for (i = 0; i < nr_ranges; i++) {
unsigned int added = DIV_ROUND_UP(phys_vec[i].len, MAX_SG_ENT_SZ);
if (check_add_overflow(nents, added, &nents))
return 0;
}
} else {
/*
* In IOVA case, there is only one SG entry which spans
* for whole IOVA address space, but we need to make sure
* that it fits sg->length, maybe we need more.
*/
nents = DIV_ROUND_UP(size, UINT_MAX);
nents = DIV_ROUND_UP(size, MAX_SG_ENT_SZ);
}
return nents;
@ -95,9 +103,10 @@ struct sg_table *dma_buf_phys_vec_to_sgt(struct dma_buf_attachment *attach,
size_t nr_ranges, size_t size,
enum dma_data_direction dir)
{
unsigned int nents, mapped_len = 0;
struct dma_buf_dma *dma;
struct scatterlist *sgl;
size_t mapped_len = 0;
unsigned int nents;
dma_addr_t addr;
size_t i;
int ret;
@ -133,6 +142,8 @@ struct sg_table *dma_buf_phys_vec_to_sgt(struct dma_buf_attachment *attach,
}
nents = calc_sg_nents(dma->state, phys_vec, nr_ranges, size);
/* sg_alloc_table will cleanly fail and return -EINVAL if nents == 0 */
ret = sg_alloc_table(&dma->sgt, nents, GFP_KERNEL | __GFP_ZERO);
if (ret)
goto err_free_state;

View File

@ -1170,7 +1170,12 @@ const char __rcu *dma_fence_driver_name(struct dma_fence *fence)
/* RCU protection is required for safe access to returned string */
ops = rcu_dereference(fence->ops);
if (ops)
/*
* Make load ordering irrelevant by checking both signaled state and ops
* pointer and ops pointer is only set to NULL on newer implementations.
*/
if (!dma_fence_test_signaled_flag(fence) && ops)
return (const char __rcu *)ops->get_driver_name(fence);
else
return (const char __rcu *)"detached-driver";
@ -1203,7 +1208,12 @@ const char __rcu *dma_fence_timeline_name(struct dma_fence *fence)
/* RCU protection is required for safe access to returned string */
ops = rcu_dereference(fence->ops);
if (ops)
/*
* Make load ordering irrelevant by checking both signaled state and ops
* pointer and ops pointer is only set to NULL on newer implementations.
*/
if (!dma_fence_test_signaled_flag(fence) && ops)
return (const char __rcu *)ops->get_timeline_name(fence);
else
return (const char __rcu *)"signaled-timeline";

View File

@ -621,6 +621,9 @@ enum amdgpu_enforce_isolation_mode {
struct amdgpu_device {
struct device *dev;
struct pci_dev *pdev;
/* The two ends of the physical PCIe link outside the device. */
struct pci_dev *link_dev;
struct pci_dev *link_partner;
struct drm_device ddev;
#ifdef CONFIG_DRM_AMD_ACP

View File

@ -330,7 +330,7 @@ void amdgpu_amdkfd_clear_kfd_mapping(struct amdgpu_device *adev)
struct kfd_dev *kfd = adev->kfd.dev;
unsigned int i;
if (!kfd)
if (!kfd || !kfd->init_complete)
return;
for (i = 0; i < kfd->num_nodes; i++) {

View File

@ -1954,18 +1954,17 @@ static void amdgpu_uid_fini(struct amdgpu_device *adev)
adev->uid_info = NULL;
}
static struct pci_dev *amdgpu_device_find_parent(struct amdgpu_device *adev)
static void amdgpu_device_init_pcie_links(struct amdgpu_device *adev)
{
struct pci_dev *parent = adev->pdev;
adev->link_dev = adev->pdev;
adev->link_partner = pci_upstream_bridge(adev->link_dev);
/* skip upstream/downstream switches internal to dGPU */
while ((parent = pci_upstream_bridge(parent))) {
if (parent->vendor == PCI_VENDOR_ID_ATI)
continue;
break;
/* Skip upstream/downstream switches internal to the dGPU. */
while (adev->link_partner &&
adev->link_partner->vendor == PCI_VENDOR_ID_ATI) {
adev->link_dev = adev->link_partner;
adev->link_partner = pci_upstream_bridge(adev->link_dev);
}
return parent;
}
/**
@ -1981,7 +1980,6 @@ static struct pci_dev *amdgpu_device_find_parent(struct amdgpu_device *adev)
static int amdgpu_device_ip_early_init(struct amdgpu_device *adev)
{
struct amdgpu_ip_block *ip_block;
struct pci_dev *parent;
bool total, skip_bios, early_full_gpu_access = false;
uint32_t bios_flags;
int i, r;
@ -2077,10 +2075,9 @@ static int amdgpu_device_ip_early_init(struct amdgpu_device *adev)
!dev_is_removable(&adev->pdev->dev))
adev->flags |= AMD_IS_PX;
if (!(adev->flags & AMD_IS_APU)) {
parent = amdgpu_device_find_parent(adev);
adev->has_pr3 = parent ? pci_pr3_present(parent) : false;
}
if (!(adev->flags & AMD_IS_APU))
adev->has_pr3 = adev->link_partner &&
pci_pr3_present(adev->link_partner);
adev->pm.pp_feature = amdgpu_pp_feature_mask;
if (amdgpu_sriov_vf(adev) || sched_policy == KFD_SCHED_POLICY_NO_HWS)
@ -3776,6 +3773,7 @@ int amdgpu_device_init(struct amdgpu_device *adev,
adev->shutdown = false;
adev->flags = flags;
amdgpu_device_init_pcie_links(adev);
if (amdgpu_force_asic_type >= 0 && amdgpu_force_asic_type < CHIP_LAST)
adev->asic_type = amdgpu_force_asic_type;
@ -4337,7 +4335,7 @@ void amdgpu_device_fini_hw(struct amdgpu_device *adev)
void amdgpu_device_fini_sw(struct amdgpu_device *adev)
{
int i, idx;
int i;
bool px;
amdgpu_device_ip_fini(adev);
@ -4379,11 +4377,9 @@ void amdgpu_device_fini_sw(struct amdgpu_device *adev)
if ((adev->pdev->class >> 8) == PCI_CLASS_DISPLAY_VGA)
vga_client_unregister(adev->pdev);
if (drm_dev_enter(adev_to_drm(adev), &idx)) {
if (adev->rmmio) {
iounmap(adev->rmmio);
adev->rmmio = NULL;
drm_dev_exit(idx);
}
if (IS_ENABLED(CONFIG_PERF_EVENTS))
@ -5872,11 +5868,9 @@ static void amdgpu_device_partner_bandwidth(struct amdgpu_device *adev,
*width = PCIE_LNK_WIDTH_UNKNOWN;
if (amdgpu_device_pcie_dynamic_switching_supported(adev)) {
struct pci_dev *parent = amdgpu_device_find_parent(adev);
if (parent) {
*speed = pcie_get_speed_cap(parent);
*width = pcie_get_width_cap(parent);
if (adev->link_partner) {
*speed = pcie_get_speed_cap(adev->link_partner);
*width = pcie_get_width_cap(adev->link_partner);
}
} else {
/* use the current speeds rather than max if switching is not supported */
@ -5898,21 +5892,11 @@ static void amdgpu_device_gpu_bandwidth(struct amdgpu_device *adev,
enum pci_bus_speed *speed,
enum pcie_link_width *width)
{
struct pci_dev *parent = adev->pdev;
if (!speed || !width)
return;
/* use the device itself */
*speed = pcie_get_speed_cap(adev->pdev);
*width = pcie_get_width_cap(adev->pdev);
/* use the link outside the device */
parent = amdgpu_device_find_parent(adev);
if (parent) {
*speed = pcie_get_speed_cap(parent);
*width = pcie_get_width_cap(parent);
}
*speed = pcie_get_speed_cap(adev->link_dev);
*width = pcie_get_width_cap(adev->link_dev);
}
/**

View File

@ -43,6 +43,7 @@
#include <linux/dma-buf.h>
#include <linux/dma-fence-array.h>
#include <linux/pci-p2pdma.h>
#include <linux/pm_runtime.h>
static const struct dma_buf_attach_ops amdgpu_dma_buf_attach_ops;
@ -100,15 +101,54 @@ static int amdgpu_dma_buf_attach(struct dma_buf *dmabuf,
pci_p2pdma_distance(adev->pdev, attach->dev, false) < 0)
attach->peer2peer = false;
/*
* Only allow P2P while the exporter is active, and keep it active
* until detach. With runtime PM disabled take a plain reference so
* the put in detach stays balanced.
*/
if (attach->peer2peer) {
struct device *dev = adev_to_drm(adev)->dev;
int ret = pm_runtime_get_if_active(dev);
if (!ret)
attach->peer2peer = false;
else if (ret < 0)
pm_runtime_get_noresume(dev);
}
r = dma_resv_lock(bo->tbo.base.resv, NULL);
if (r)
return r;
goto err_pm_put;
amdgpu_vm_bo_update_shared(bo);
dma_resv_unlock(bo->tbo.base.resv);
return 0;
err_pm_put:
if (attach->peer2peer)
pm_runtime_put_autosuspend(adev_to_drm(adev)->dev);
return r;
}
/**
* amdgpu_dma_buf_detach - &dma_buf_ops.detach implementation
*
* @dmabuf: DMA-buf where we remove the attachment from
* @attach: the attachment to remove
*
* Drop the runtime PM reference taken in amdgpu_dma_buf_attach().
*/
static void amdgpu_dma_buf_detach(struct dma_buf *dmabuf,
struct dma_buf_attachment *attach)
{
struct drm_gem_object *obj = dmabuf->priv;
struct amdgpu_bo *bo = gem_to_amdgpu_bo(obj);
struct amdgpu_device *adev = amdgpu_ttm_adev(bo->tbo.bdev);
if (attach->peer2peer)
pm_runtime_put_autosuspend(adev_to_drm(adev)->dev);
}
/**
@ -350,6 +390,7 @@ static void amdgpu_dma_buf_vunmap(struct dma_buf *dma_buf, struct iosys_map *map
const struct dma_buf_ops amdgpu_dmabuf_ops = {
.attach = amdgpu_dma_buf_attach,
.detach = amdgpu_dma_buf_detach,
.pin = amdgpu_dma_buf_pin,
.unpin = amdgpu_dma_buf_unpin,
.map_dma_buf = amdgpu_dma_buf_map,

View File

@ -535,7 +535,7 @@ static void nbio_v7_9_handle_ras_controller_intr_no_bifring(struct amdgpu_device
RAS_CNTLR_INTERRUPT_CLEAR, 1);
WREG32_SOC15(NBIO, 0, regBIF_BX0_BIF_DOORBELL_INT_CNTL, bif_doorbell_intr_cntl);
if (!ras->disable_ras_err_cnt_harvest) {
if (ras && !ras->disable_ras_err_cnt_harvest && obj) {
/*
* clear error status after ras_controller_intr
* according to hw team and count ue number

View File

@ -770,10 +770,12 @@ static int create_queue_nocpsch(struct device_queue_manager *dqm,
mqd_mgr = dqm->mqd_mgrs[get_mqd_type_from_queue_type(
q->properties.type)];
if (qd && !mqd_mgr->restore_mqd) {
pr_debug("restore_mqd not implemented for this GPU\n");
pr_debug("restore_mqd not implemented for queue type %d\n",
q->properties.type);
retval = -EOPNOTSUPP;
goto deallocate_vmid;
}
if (q->properties.type == KFD_QUEUE_TYPE_COMPUTE) {
retval = allocate_hqd(dqm, q);
if (retval)
@ -2250,7 +2252,8 @@ static int create_queue_cpsch(struct device_queue_manager *dqm, struct queue *q,
mqd_mgr = dqm->mqd_mgrs[get_mqd_type_from_queue_type(
q->properties.type)];
if (qd && !mqd_mgr->restore_mqd) {
pr_debug("restore_mqd not implemented for this GPU\n");
pr_debug("restore_mqd not implemented for queue type %d\n",
q->properties.type);
retval = -EOPNOTSUPP;
goto out_deallocate_doorbell;
}

View File

@ -204,7 +204,7 @@ static void update_mqd(struct mqd_manager *mm, void *mqd,
* is safe, giving a maximum field value of 0xA.
*/
m->cp_hqd_eop_control = q->eop_ring_buffer_size ? min(0xA,
ffs(q->eop_ring_buffer_size / sizeof(unsigned int)) - 1 - 1) : 0;
ffs(q->eop_ring_buffer_size / sizeof(unsigned int) / 4)) : 0;
m->cp_hqd_eop_base_addr_lo =
lower_32_bits(q->eop_ring_buffer_address >> 8);
m->cp_hqd_eop_base_addr_hi =

View File

@ -242,7 +242,7 @@ static void update_mqd(struct mqd_manager *mm, void *mqd,
* is safe, giving a maximum field value of 0xA.
*/
m->cp_hqd_eop_control = q->eop_ring_buffer_size ? min(0xA,
ffs(q->eop_ring_buffer_size / sizeof(unsigned int)) - 1 - 1) : 0;
ffs(q->eop_ring_buffer_size / sizeof(unsigned int) / 4)) : 0;
m->cp_hqd_eop_base_addr_lo =
lower_32_bits(q->eop_ring_buffer_address >> 8);
m->cp_hqd_eop_base_addr_hi =

View File

@ -217,7 +217,7 @@ static void update_mqd(struct mqd_manager *mm, void *mqd,
* is safe, giving a maximum field value of 0xA.
*/
m->cp_hqd_eop_control = q->eop_ring_buffer_size ? min(0xA,
ffs(q->eop_ring_buffer_size / sizeof(unsigned int)) - 1 - 1) : 0;
ffs(q->eop_ring_buffer_size / sizeof(unsigned int) / 4)) : 0;
m->cp_hqd_eop_base_addr_lo =
lower_32_bits(q->eop_ring_buffer_address >> 8);
m->cp_hqd_eop_base_addr_hi =
@ -380,6 +380,63 @@ static int debugfs_show_mqd_sdma(struct seq_file *m, void *data)
#endif
static void restore_mqd(struct mqd_manager *mm, void **mqd,
struct kfd_mem_obj *mqd_mem_obj, uint64_t *gart_addr,
struct queue_properties *qp, const void *mqd_src,
const void *ctl_stack_src, const u32 ctl_stack_size)
{
u64 addr;
struct v12_compute_mqd *m;
m = (struct v12_compute_mqd *)mqd_mem_obj->cpu_ptr;
addr = mqd_mem_obj->gpu_addr;
memset(m, 0, AMDGPU_MQD_SIZE_ALIGN(mm->mqd_size));
memcpy(m, mqd_src, sizeof(*m));
/* Update MQD base address to the newly allocated location */
m->cp_mqd_base_addr_lo = lower_32_bits(addr);
m->cp_mqd_base_addr_hi = upper_32_bits(addr);
m->cp_hqd_pq_doorbell_control &=
~CP_HQD_PQ_DOORBELL_CONTROL__DOORBELL_OFFSET_MASK;
m->cp_hqd_pq_doorbell_control |=
qp->doorbell_off << CP_HQD_PQ_DOORBELL_CONTROL__DOORBELL_OFFSET__SHIFT;
pr_debug("cp_hqd_pq_doorbell_control 0x%x\n", m->cp_hqd_pq_doorbell_control);
*mqd = m;
if (gart_addr)
*gart_addr = addr;
qp->is_active = 0;
}
static void restore_mqd_sdma(struct mqd_manager *mm, void **mqd,
struct kfd_mem_obj *mqd_mem_obj, uint64_t *gart_addr,
struct queue_properties *qp,
const void *mqd_src,
const void *ctl_stack_src,
const u32 ctl_stack_size)
{
u64 addr;
struct v12_sdma_mqd *m;
m = (struct v12_sdma_mqd *)mqd_mem_obj->cpu_ptr;
addr = mqd_mem_obj->gpu_addr;
memset(m, 0, AMDGPU_MQD_SIZE_ALIGN(mm->mqd_size));
memcpy(m, mqd_src, sizeof(*m));
m->sdmax_rlcx_doorbell_offset =
qp->doorbell_off << SDMA0_QUEUE0_DOORBELL_OFFSET__OFFSET__SHIFT;
*mqd = m;
if (gart_addr)
*gart_addr = addr;
qp->is_active = 0;
}
struct mqd_manager *mqd_manager_init_v12(enum KFD_MQD_TYPE type,
struct kfd_node *dev)
{
@ -407,6 +464,7 @@ struct mqd_manager *mqd_manager_init_v12(enum KFD_MQD_TYPE type,
mqd->mqd_size = sizeof(struct v12_compute_mqd);
mqd->get_wave_state = get_wave_state;
mqd->mqd_stride = kfd_mqd_stride;
mqd->restore_mqd = restore_mqd;
#if defined(CONFIG_DEBUG_FS)
mqd->debugfs_show_mqd = debugfs_show_mqd;
#endif
@ -453,6 +511,7 @@ struct mqd_manager *mqd_manager_init_v12(enum KFD_MQD_TYPE type,
mqd->is_occupied = kfd_is_occupied_sdma;
mqd->mqd_size = sizeof(struct v12_sdma_mqd);
mqd->mqd_stride = kfd_mqd_stride;
mqd->restore_mqd = restore_mqd_sdma;
#if defined(CONFIG_DEBUG_FS)
mqd->debugfs_show_mqd = debugfs_show_mqd_sdma;
#endif

View File

@ -295,7 +295,7 @@ static void update_mqd(struct mqd_manager *mm, void *mqd,
* is safe, giving a maximum field value of 0xA.
*/
m->cp_hqd_eop_control = q->eop_ring_buffer_size ? min(0xA,
ffs(q->eop_ring_buffer_size / sizeof(unsigned int)) - 1 - 1) : 0;
ffs(q->eop_ring_buffer_size / sizeof(unsigned int) / 4)) : 0;
m->cp_hqd_eop_base_addr_lo =
lower_32_bits(q->eop_ring_buffer_address >> 8);
m->cp_hqd_eop_base_addr_hi =
@ -641,6 +641,72 @@ static int debugfs_show_mqd_sdma(struct seq_file *m, void *data)
#endif
static void restore_mqd_v12_1(struct mqd_manager *mm, void **mqd,
struct kfd_mem_obj *mqd_mem_obj, uint64_t *gart_addr,
struct queue_properties *qp, const void *mqd_src,
const void *ctl_stack_src, const u32 ctl_stack_size)
{
u64 addr;
struct v12_1_compute_mqd *m;
/*
* GFX12.1 is multi-XCC capable but this restore handles XCC0 only.
* Multi-XCC CRIU restore is currently unreachable because
* kfd_criu_restore_queue() validates against unscaled mqd_size.
*/
if (NUM_XCC(mm->dev->xcc_mask) > 1)
pr_warn_once("GFX12.1 multi-XCC CRIU restore not fully supported\n");
m = (struct v12_1_compute_mqd *)mqd_mem_obj->cpu_ptr;
addr = mqd_mem_obj->gpu_addr;
memset(m, 0, AMDGPU_MQD_SIZE_ALIGN(mm->mqd_size) *
NUM_XCC(mm->dev->xcc_mask));
memcpy(m, mqd_src, sizeof(*m));
/* Update MQD base address to the newly allocated location */
m->cp_mqd_base_addr_lo = lower_32_bits(addr);
m->cp_mqd_base_addr_hi = upper_32_bits(addr);
m->cp_hqd_pq_doorbell_control &=
~CP_HQD_PQ_DOORBELL_CONTROL__DOORBELL_OFFSET_MASK;
m->cp_hqd_pq_doorbell_control |=
qp->doorbell_off << CP_HQD_PQ_DOORBELL_CONTROL__DOORBELL_OFFSET__SHIFT;
pr_debug("cp_hqd_pq_doorbell_control 0x%x\n", m->cp_hqd_pq_doorbell_control);
*mqd = m;
if (gart_addr)
*gart_addr = addr;
qp->is_active = 0;
}
static void restore_mqd_sdma_v12_1(struct mqd_manager *mm, void **mqd,
struct kfd_mem_obj *mqd_mem_obj, uint64_t *gart_addr,
struct queue_properties *qp,
const void *mqd_src,
const void *ctl_stack_src,
const u32 ctl_stack_size)
{
u64 addr;
struct v12_sdma_mqd *m;
m = (struct v12_sdma_mqd *)mqd_mem_obj->cpu_ptr;
addr = mqd_mem_obj->gpu_addr;
memset(m, 0, AMDGPU_MQD_SIZE_ALIGN(mm->mqd_size));
memcpy(m, mqd_src, sizeof(*m));
m->sdmax_rlcx_doorbell_offset =
qp->doorbell_off << SDMA0_SDMA_QUEUE0_DOORBELL_OFFSET__OFFSET__SHIFT;
*mqd = m;
if (gart_addr)
*gart_addr = addr;
qp->is_active = 0;
}
struct mqd_manager *mqd_manager_init_v12_1(enum KFD_MQD_TYPE type,
struct kfd_node *dev)
{
@ -668,6 +734,7 @@ struct mqd_manager *mqd_manager_init_v12_1(enum KFD_MQD_TYPE type,
mqd->mqd_size = sizeof(struct v12_1_compute_mqd);
mqd->get_wave_state = get_wave_state_v12_1;
mqd->mqd_stride = kfd_mqd_stride;
mqd->restore_mqd = restore_mqd_v12_1;
#if defined(CONFIG_DEBUG_FS)
mqd->debugfs_show_mqd = debugfs_show_mqd;
#endif
@ -714,6 +781,7 @@ struct mqd_manager *mqd_manager_init_v12_1(enum KFD_MQD_TYPE type,
mqd->is_occupied = kfd_is_occupied_sdma;
mqd->mqd_size = sizeof(struct v12_sdma_mqd);
mqd->mqd_stride = kfd_mqd_stride;
mqd->restore_mqd = restore_mqd_sdma_v12_1;
#if defined(CONFIG_DEBUG_FS)
mqd->debugfs_show_mqd = debugfs_show_mqd_sdma;
#endif

View File

@ -285,6 +285,10 @@ static void update_mqd(struct mqd_manager *mm, void *mqd,
1 << CP_HQD_IB_CONTROL__IB_EXE_DISABLE__SHIFT;
/*
* The lowest 6 bits of eop_control store the EOP ring size. If
* their value is X, the ring size is 2^(X + 1) dwords, or
* 2^(X + 3) bytes.
*
* HW does not clamp this field correctly. Maximum EOP queue size
* is constrained by per-SE EOP done signal count, which is 8-bit.
* Limit is 0xFF EOP entries (= 0x7F8 dwords). CP will not submit
@ -296,7 +300,7 @@ static void update_mqd(struct mqd_manager *mm, void *mqd,
*
*/
m->cp_hqd_eop_control = q->eop_ring_buffer_size ?
min(0xA, order_base_2(q->eop_ring_buffer_size / 4) - 1) : 0;
min(0xA, order_base_2(q->eop_ring_buffer_size / 8)) : 0;
m->cp_hqd_eop_base_addr_lo =
lower_32_bits(q->eop_ring_buffer_address >> 8);

View File

@ -208,6 +208,9 @@ static void __update_mqd(struct mqd_manager *mm, void *mqd,
mtype << CP_HQD_IB_CONTROL__MTYPE__SHIFT;
/*
* The lowest 6 bits of eop_control store the EOP ring size. If
* their value is X, the ring size is 2^(X + 1) dwords, or
* 2^(X + 3) bytes.
* HW does not clamp this field correctly. Maximum EOP queue size
* is constrained by per-SE EOP done signal count, which is 8-bit.
* Limit is 0xFF EOP entries (= 0x7F8 dwords). CP will not submit
@ -215,7 +218,7 @@ static void __update_mqd(struct mqd_manager *mm, void *mqd,
* is safe, giving a maximum field value of 0xA.
*/
m->cp_hqd_eop_control |= q->eop_ring_buffer_size ? min(0xA,
order_base_2(q->eop_ring_buffer_size / 4) - 1) : 0;
order_base_2(q->eop_ring_buffer_size / 8)) : 0;
m->cp_hqd_eop_base_addr_lo =
lower_32_bits(q->eop_ring_buffer_address >> 8);
m->cp_hqd_eop_base_addr_hi =

View File

@ -1420,7 +1420,7 @@ static void dm_gpureset_toggle_interrupts(struct amdgpu_device *adev,
if (acrtc && state->stream_status[i].plane_count != 0 &&
amdgpu_ip_version(adev, DCE_HWIP, 0) == 0) {
irq_source = IRQ_TYPE_PFLIP + acrtc->otg_inst;
rc = dc_interrupt_set(adev->dm.dc, irq_source, enable) ? 0 : -EBUSY;
rc = amdgpu_dm_irq_set(adev, irq_source, enable) ? 0 : -EBUSY;
if (rc)
drm_warn(adev_to_drm(adev), "Failed to %s pflip interrupts\n",
enable ? "enable" : "disable");
@ -1444,7 +1444,7 @@ static void dm_gpureset_toggle_interrupts(struct amdgpu_device *adev,
/* During gpu-reset we disable and then enable vblank irq, so
* don't use amdgpu_irq_get/put() to avoid refcount change.
*/
if (!dc_interrupt_set(adev->dm.dc, irq_source, enable))
if (!amdgpu_dm_irq_set(adev, irq_source, enable))
drm_warn(adev_to_drm(adev), "Failed to %sable vblank interrupt\n", enable ? "en" : "dis");
} else if (acrtc && state->stream_status[i].plane_count != 0) {

View File

@ -552,6 +552,18 @@ struct amdgpu_display_manager {
struct common_irq_params
vupdate_params[DC_IRQ_SOURCE_VUPDATE6 - DC_IRQ_SOURCE_VUPDATE1 + 1];
/**
* @irq_reg_lock:
*
* Serializes the read-modify-writes of the HW interrupt control
* registers. Several interrupt sources share one register - e.g. the
* enable and clear bits of both VSTARTUP (vblank) and VUPDATE_NO_LOCK
* live in OTG_GLOBAL_SYNC_STATUS. Therefore, enabling one source must
* not race with acking another. Held only across amdgpu_dm_irq_set()
* and amdgpu_dm_irq_ack().
*/
spinlock_t irq_reg_lock;
/**
* @dmub_trace_params:
*

View File

@ -31,6 +31,7 @@
#include "amdgpu_dm_psr.h"
#include "amdgpu_dm_replay.h"
#include "amdgpu_dm_crtc.h"
#include "amdgpu_dm_irq.h"
#include "amdgpu_dm_plane.h"
#include "amdgpu_dm_trace.h"
#include "amdgpu_dm_debugfs.h"
@ -91,7 +92,7 @@ int amdgpu_dm_crtc_set_vupdate_irq(struct drm_crtc *crtc, bool enable)
irq_source = IRQ_TYPE_VUPDATE + acrtc->otg_inst;
rc = dc_interrupt_set(adev->dm.dc, irq_source, enable) ? 0 : -EBUSY;
rc = amdgpu_dm_irq_set(adev, irq_source, enable) ? 0 : -EBUSY;
DRM_DEBUG_VBL("crtc %d - vupdate irq %sabling: r=%d\n",
acrtc->crtc_id, enable ? "en" : "dis", rc);

View File

@ -1439,12 +1439,13 @@ void dm_helpers_free_gpu_mem(
bool dm_helpers_dmub_outbox_interrupt_control(struct dc_context *ctx, bool enable)
{
struct amdgpu_device *adev = ctx->driver_context;
enum dc_irq_source irq_source;
bool ret;
irq_source = DC_IRQ_SOURCE_DMCUB_OUTBOX;
ret = dc_interrupt_set(ctx->dc, irq_source, enable);
ret = amdgpu_dm_irq_set(adev, irq_source, enable);
DRM_DEBUG_DRIVER("Dmub trace irq %sabling: r=%d\n",
enable ? "en" : "dis", ret);

View File

@ -396,6 +396,7 @@ int amdgpu_dm_irq_init(struct amdgpu_device *adev)
DRM_DEBUG_KMS("DM_IRQ\n");
spin_lock_init(&adev->dm.irq_handler_list_table_lock);
spin_lock_init(&adev->dm.irq_reg_lock);
adev->dm.irq_wq = alloc_workqueue("amdgpu_dm_irq",
WQ_UNBOUND | WQ_HIGHPRI, 0);
@ -530,7 +531,7 @@ void amdgpu_dm_irq_suspend(struct amdgpu_device *adev)
*/
for (src = DC_IRQ_SOURCE_HPD1; src <= DC_IRQ_SOURCE_HPD6RX; src++) {
hnd_list_l = &adev->dm.irq_handler_list_low_tab[src];
dc_interrupt_set(adev->dm.dc, src, false);
amdgpu_dm_irq_set(adev, src, false);
DM_IRQ_TABLE_UNLOCK(adev, irq_table_flags);
@ -568,7 +569,7 @@ void amdgpu_dm_irq_resume_early(struct amdgpu_device *adev)
hnd_list_l = &adev->dm.irq_handler_list_low_tab[src];
hnd_list_h = &adev->dm.irq_handler_list_high_tab[src];
if (!list_empty(hnd_list_l) || !list_empty(hnd_list_h))
dc_interrupt_set(adev->dm.dc, src, true);
amdgpu_dm_irq_set(adev, src, true);
}
DM_IRQ_TABLE_UNLOCK(adev, irq_table_flags);
@ -594,7 +595,7 @@ void amdgpu_dm_irq_resume_late(struct amdgpu_device *adev)
hnd_list_l = &adev->dm.irq_handler_list_low_tab[src];
hnd_list_h = &adev->dm.irq_handler_list_high_tab[src];
if (!list_empty(hnd_list_l) || !list_empty(hnd_list_h))
dc_interrupt_set(adev->dm.dc, src, true);
amdgpu_dm_irq_set(adev, src, true);
}
DM_IRQ_TABLE_UNLOCK(adev, irq_table_flags);
@ -690,6 +691,23 @@ STATIC_IFN_KUNIT void amdgpu_dm_irq_immediate_work(struct amdgpu_device *adev,
}
EXPORT_IF_KUNIT(amdgpu_dm_irq_immediate_work);
bool amdgpu_dm_irq_set(struct amdgpu_device *adev, enum dc_irq_source src,
bool enable)
{
guard(spinlock_irqsave)(&adev->dm.irq_reg_lock);
return dc_interrupt_set(adev->dm.dc, src, enable);
}
EXPORT_IF_KUNIT(amdgpu_dm_irq_set);
void amdgpu_dm_irq_ack(struct amdgpu_device *adev, enum dc_irq_source src)
{
guard(spinlock_irqsave)(&adev->dm.irq_reg_lock);
dc_interrupt_ack(adev->dm.dc, src);
}
EXPORT_IF_KUNIT(amdgpu_dm_irq_ack);
/**
* amdgpu_dm_irq_handler - Generic DM IRQ handler
* @adev: amdgpu base driver device containing the DM device
@ -710,7 +728,7 @@ STATIC_IFN_KUNIT int amdgpu_dm_irq_handler(struct amdgpu_device *adev,
entry->src_id,
entry->src_data[0]);
dc_interrupt_ack(adev->dm.dc, src);
amdgpu_dm_irq_ack(adev, src);
/* Call high irq work immediately */
amdgpu_dm_irq_immediate_work(adev, src);
@ -750,7 +768,7 @@ STATIC_IFN_KUNIT int amdgpu_dm_set_hpd_irq_state(struct amdgpu_device *adev,
enum dc_irq_source src = amdgpu_dm_hpd_to_dal_irq_source(type);
bool st = (state == AMDGPU_IRQ_STATE_ENABLE);
dc_interrupt_set(adev->dm.dc, src, st);
amdgpu_dm_irq_set(adev, src, st);
return 0;
}
EXPORT_IF_KUNIT(amdgpu_dm_set_hpd_irq_state);
@ -785,7 +803,7 @@ static inline int dm_irq_state(struct amdgpu_device *adev,
if (dc && dc->caps.ips_support && dc->idle_optimizations_allowed)
dc_allow_idle_optimizations(dc, false);
dc_interrupt_set(adev->dm.dc, irq_source, st);
amdgpu_dm_irq_set(adev, irq_source, st);
return 0;
}
@ -842,7 +860,7 @@ STATIC_IFN_KUNIT int amdgpu_dm_set_dmub_outbox_irq_state(struct amdgpu_device *a
enum dc_irq_source irq_source = DC_IRQ_SOURCE_DMCUB_OUTBOX;
bool st = (state == AMDGPU_IRQ_STATE_ENABLE);
dc_interrupt_set(adev->dm.dc, irq_source, st);
amdgpu_dm_irq_set(adev, irq_source, st);
return 0;
}
EXPORT_IF_KUNIT(amdgpu_dm_set_dmub_outbox_irq_state);
@ -870,7 +888,7 @@ STATIC_IFN_KUNIT int amdgpu_dm_set_dmub_trace_irq_state(struct amdgpu_device *ad
enum dc_irq_source irq_source = DC_IRQ_SOURCE_DMCUB_OUTBOX0;
bool st = (state == AMDGPU_IRQ_STATE_ENABLE);
dc_interrupt_set(adev->dm.dc, irq_source, st);
amdgpu_dm_irq_set(adev, irq_source, st);
return 0;
}
EXPORT_IF_KUNIT(amdgpu_dm_set_dmub_trace_irq_state);
@ -937,9 +955,7 @@ EXPORT_IF_KUNIT(amdgpu_dm_set_irq_funcs);
void amdgpu_dm_outbox_init(struct amdgpu_device *adev)
{
dc_interrupt_set(adev->dm.dc,
DC_IRQ_SOURCE_DMCUB_OUTBOX,
true);
amdgpu_dm_irq_set(adev, DC_IRQ_SOURCE_DMCUB_OUTBOX, true);
}
EXPORT_IF_KUNIT(amdgpu_dm_outbox_init);
@ -962,7 +978,7 @@ void amdgpu_dm_hpd_init(struct amdgpu_device *adev)
/* First, clear all hpd and hpdrx interrupts */
for (i = DC_IRQ_SOURCE_HPD1; i <= DC_IRQ_SOURCE_HPD6RX; i++) {
if (!dc_interrupt_set(adev->dm.dc, i, false))
if (!amdgpu_dm_irq_set(adev, i, false))
drm_err(dev, "Failed to clear hpd(rx) source=%d on init\n",
i);
}
@ -991,7 +1007,7 @@ void amdgpu_dm_hpd_init(struct amdgpu_device *adev)
* of dm. Note that only hpd interrupt types are registered with
* base driver; hpd_rx types aren't. IOW, amdgpu_irq_get/put on
* hpd_rx isn't available. DM currently controls hpd_rx
* explicitly with dc_interrupt_set()
* explicitly with amdgpu_dm_irq_set()
*/
if (dc_link->irq_source_hpd != DC_IRQ_SOURCE_INVALID) {
irq_type = dc_link->irq_source_hpd - DC_IRQ_SOURCE_HPD1;
@ -1000,23 +1016,21 @@ void amdgpu_dm_hpd_init(struct amdgpu_device *adev)
* and what bios reports as the # of connectors with hpd
* sources. Since the # of hpd source types registered
* with base driver == mode_info.num_hpd, we have to
* fallback to dc_interrupt_set for the remaining types.
* fallback to amdgpu_dm_irq_set for the remaining types.
*/
if (irq_type < adev->mode_info.num_hpd) {
if (amdgpu_irq_get(adev, &adev->hpd_irq, irq_type))
drm_err(dev, "DM_IRQ: Failed get HPD for source=%d)!\n",
dc_link->irq_source_hpd);
} else {
dc_interrupt_set(adev->dm.dc,
dc_link->irq_source_hpd,
true);
amdgpu_dm_irq_set(adev, dc_link->irq_source_hpd,
true);
}
}
if (dc_link->irq_source_hpd_rx != DC_IRQ_SOURCE_INVALID) {
dc_interrupt_set(adev->dm.dc,
dc_link->irq_source_hpd_rx,
true);
amdgpu_dm_irq_set(adev, dc_link->irq_source_hpd_rx,
true);
}
}
drm_connector_list_iter_end(&iter);
@ -1061,16 +1075,14 @@ void amdgpu_dm_hpd_fini(struct amdgpu_device *adev)
drm_err(dev, "DM_IRQ: Failed put HPD for source=%d!\n",
dc_link->irq_source_hpd);
} else {
dc_interrupt_set(adev->dm.dc,
dc_link->irq_source_hpd,
false);
amdgpu_dm_irq_set(adev, dc_link->irq_source_hpd,
false);
}
}
if (dc_link->irq_source_hpd_rx != DC_IRQ_SOURCE_INVALID) {
dc_interrupt_set(adev->dm.dc,
dc_link->irq_source_hpd_rx,
false);
amdgpu_dm_irq_set(adev, dc_link->irq_source_hpd_rx,
false);
}
}
drm_connector_list_iter_end(&iter);

View File

@ -89,6 +89,34 @@ void amdgpu_dm_irq_unregister_interrupt(struct amdgpu_device *adev,
enum dc_irq_source irq_source,
void *ih_index);
/**
* amdgpu_dm_irq_set - enable or disable a DC interrupt source.
*
* @adev: AMD DRM device
* @src: DC interrupt source to toggle
* @enable: true to enable the source, false to disable it
*
* DM-wide replacement for dc_interrupt_set(). As locking is DM's
* responsibility, this is a thin wrapper serializes the underlying
* read-modify-write against the other interrupt sources sharing HW control
* registers with @src, so DM must never call dc_interrupt_set() directly.
*
* Returns: true if the source was toggled.
*/
bool amdgpu_dm_irq_set(struct amdgpu_device *adev, enum dc_irq_source src,
bool enable);
/**
* amdgpu_dm_irq_ack - acknowledge a DC interrupt source.
*
* @adev: AMD DRM device
* @src: DC interrupt source to acknowledge
*
* DM-wide replacement for dc_interrupt_ack(), serialized the same way as
* amdgpu_dm_irq_set().
*/
void amdgpu_dm_irq_ack(struct amdgpu_device *adev, enum dc_irq_source src);
void amdgpu_dm_set_irq_funcs(struct amdgpu_device *adev);
void amdgpu_dm_outbox_init(struct amdgpu_device *adev);

View File

@ -436,7 +436,7 @@ static void dm_test_crtc_set_vupdate_irq_no_otg(struct kunit *test)
* dm_test_crtc_set_vupdate_irq_dc_busy - Test vupdate irq when DC rejects request
* @test: The KUnit test context
*
* With an OTG instance assigned but no DC attached, dc_interrupt_set() returns
* With an OTG instance assigned but no DC attached, amdgpu_dm_irq_set() returns
* false and the function must report the request as busy (-EBUSY).
*/
static void dm_test_crtc_set_vupdate_irq_dc_busy(struct kunit *test)
@ -453,12 +453,12 @@ static void dm_test_crtc_set_vupdate_irq_dc_busy(struct kunit *test)
acrtc->base.dev = &adev->ddev;
acrtc->otg_inst = 0;
/* adev->dm.dc is NULL, so dc_interrupt_set() returns false. */
/* adev->dm.dc is NULL, so amdgpu_dm_irq_set() returns false. */
KUNIT_EXPECT_EQ(test,
amdgpu_dm_crtc_set_vupdate_irq(&acrtc->base, true), -EBUSY);
}
/* Per-source funcs let dc_interrupt_set() succeed without register access. */
/* Per-source funcs let amdgpu_dm_irq_set() succeed without register access. */
static bool dm_test_vupdate_irq_src_set(struct irq_service *irq_service,
const struct irq_source_info *info,
bool enable)
@ -477,7 +477,7 @@ static struct irq_source_info_funcs dm_test_vupdate_irq_src_funcs = {
.ack = dm_test_vupdate_irq_src_ack,
};
/* A .set that fails so dc_interrupt_set() reports the source as busy. */
/* A .set that fails so amdgpu_dm_irq_set() reports the source as busy. */
static bool dm_test_vupdate_irq_src_set_busy(struct irq_service *irq_service,
const struct irq_source_info *info,
bool enable)
@ -519,7 +519,9 @@ static void dm_test_crtc_set_vupdate_irq_enable(struct kunit *test)
irqs = kunit_kzalloc(test, sizeof(*irqs), GFP_KERNEL);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, irqs);
/* Populate the per-source info table so dc_interrupt_set() succeeds. */
/*
* Populate the per-source info table so amdgpu_dm_irq_set() succeeds.
*/
info = kunit_kzalloc(test, sizeof(*info) * DAL_IRQ_SOURCES_NUMBER,
GFP_KERNEL);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, info);
@ -1018,7 +1020,9 @@ static void dm_test_crtc_enable_vblank_vupdate_busy(struct kunit *test)
irqs = kunit_kzalloc(test, sizeof(*irqs), GFP_KERNEL);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, irqs);
/* Per-source .set fails so dc_interrupt_set() reports the source busy. */
/*
* Per-source .set fails so amdgpu_dm_irq_set() reports the source busy.
*/
info = kunit_kzalloc(test, sizeof(*info) * DAL_IRQ_SOURCES_NUMBER,
GFP_KERNEL);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, info);

View File

@ -2442,17 +2442,21 @@ static void dm_test_is_dp_sink_present_null_priv(struct kunit *test)
* dm_test_dmub_outbox_interrupt_control_null_dc - Test outbox irq control with NULL dc
* @test: The KUnit test context
*
* dc_interrupt_set() is NULL-safe and returns false when dc is NULL, so the
* amdgpu_dm_irq_set() is NULL-safe and returns false when dc is NULL, so the
* helper returns false without touching real interrupt hardware.
*/
static void dm_test_dmub_outbox_interrupt_control_null_dc(struct kunit *test)
{
struct amdgpu_device *adev;
struct dc_context *ctx;
adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL);
KUNIT_ASSERT_NOT_NULL(test, adev);
ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL);
KUNIT_ASSERT_NOT_NULL(test, ctx);
ctx->driver_context = adev;
/* ctx->dc is NULL → dc_interrupt_set returns false */
/* adev->dm.dc is NULL → amdgpu_dm_irq_set returns false */
KUNIT_EXPECT_FALSE(test, dm_helpers_dmub_outbox_interrupt_control(ctx, true));
KUNIT_EXPECT_FALSE(test, dm_helpers_dmub_outbox_interrupt_control(ctx, false));
}

View File

@ -268,7 +268,7 @@ static bool dm_test_irq_src_ack(struct irq_service *irq_service,
return true;
}
/* Per-source funcs let dc_interrupt_set() succeed without register access. */
/* Per-source funcs let amdgpu_dm_irq_set() succeed without register access. */
static struct irq_source_info_funcs dm_test_irq_src_funcs = {
.set = dm_test_irq_src_set,
.ack = dm_test_irq_src_ack,
@ -290,7 +290,7 @@ static struct dc *dm_test_alloc_dc_with_irq_service(struct kunit *test,
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, irqs);
/*
* Populate the per-source info table so dc_interrupt_set()/_ack()
* Populate the per-source info table so amdgpu_dm_irq_set()/_ack()
* succeed without touching hardware registers.
*/
info = kunit_kzalloc(test, sizeof(*info) * DAL_IRQ_SOURCES_NUMBER,
@ -1227,7 +1227,7 @@ static void dm_test_irq_suspend_empty(struct kunit *test)
KUNIT_ASSERT_EQ(test, amdgpu_dm_irq_init(adev), 0);
/*
* With no registered handlers the HW dc_interrupt_set() calls are
* With no registered handlers the amdgpu_dm_irq_set() calls are
* skipped, so suspend must complete without touching the (absent) DC.
*/
amdgpu_dm_irq_suspend(adev);
@ -1275,11 +1275,11 @@ static void dm_test_irq_resume_late_empty(struct kunit *test)
}
/**
* dm_test_irq_suspend_registered - Test suspend reaches the dc_interrupt_set path
* dm_test_irq_suspend_registered - Test suspend reaches the irq set path
* @test: The KUnit test context
*
* Registers a low-context HPD handler so the handler list is non-empty,
* forcing amdgpu_dm_irq_suspend() to call dc_interrupt_set() (NULL-safe with
* forcing amdgpu_dm_irq_suspend() to call amdgpu_dm_irq_set() (NULL-safe with
* no DC) and flush_work() on the registered handler.
*/
static void dm_test_irq_suspend_registered(struct kunit *test)
@ -1330,11 +1330,11 @@ static void dm_test_irq_suspend_disables_polling(struct kunit *test)
}
/**
* dm_test_irq_resume_early_registered - Test early resume reaches dc_interrupt_set
* dm_test_irq_resume_early_registered - Test early resume reaches irq set
* @test: The KUnit test context
*
* Registers a low-context HPD RX handler so early resume calls
* dc_interrupt_set() for the short-pulse interrupt source.
* amdgpu_dm_irq_set() for the short-pulse interrupt source.
*/
static void dm_test_irq_resume_early_registered(struct kunit *test)
{
@ -1358,10 +1358,10 @@ static void dm_test_irq_resume_early_registered(struct kunit *test)
}
/**
* dm_test_irq_resume_late_registered - Test late resume reaches dc_interrupt_set
* dm_test_irq_resume_late_registered - Test late resume reaches irq set
* @test: The KUnit test context
*
* Registers a low-context HPD handler so late resume calls dc_interrupt_set()
* Registers a low-context HPD handler so late resume calls amdgpu_dm_irq_set()
* for the HPD interrupt source.
*/
static void dm_test_irq_resume_late_registered(struct kunit *test)
@ -1592,7 +1592,7 @@ static void dm_test_set_crtc_irq_state_enable(struct kunit *test)
/*
* otg_inst >= 0 computes the irq source and reaches the NULL-safe
* dc_interrupt_set(); the ips_support branch is skipped (dc == NULL).
* amdgpu_dm_irq_set(); the ips_support branch is skipped (dc == NULL).
*/
acrtc->otg_inst = 3;
adev->mode_info.crtcs[0] = acrtc;
@ -1671,8 +1671,8 @@ static void dm_test_set_vupdate_irq_state_enable(struct kunit *test)
*
* With a non-NULL DC that advertises IPS support and currently allows idle
* optimizations, dm_irq_state() must call dc_allow_idle_optimizations() before
* dc_interrupt_set(). disable_idle_power_optimizations makes that call a safe
* early return, and per-source stub funcs let dc_interrupt_set() succeed.
* amdgpu_dm_irq_set(). disable_idle_power_optimizations makes that call a safe
* early return, and per-source stub funcs let amdgpu_dm_irq_set() succeed.
*/
static void dm_test_set_crtc_irq_state_allows_idle(struct kunit *test)
{
@ -1891,7 +1891,7 @@ static void dm_test_set_hpd_irq_state_null_dc(struct kunit *test)
adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, adev);
/* dc_interrupt_set() is a no-op when dc is NULL, so both states
/* amdgpu_dm_irq_set() is a no-op when dc is NULL, so both states
* return 0 without dereferencing the (absent) DC.
*/
KUNIT_EXPECT_EQ(test, amdgpu_dm_set_hpd_irq_state(adev, NULL, AMDGPU_HPD_1,
@ -1951,7 +1951,7 @@ static void dm_test_outbox_init_null_dc(struct kunit *test)
adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, adev);
/* Single dc_interrupt_set() call must be skipped when dc is NULL. */
/* Single amdgpu_dm_irq_set() call must be skipped when dc is NULL. */
amdgpu_dm_outbox_init(adev);
}
@ -1969,7 +1969,7 @@ static void dm_test_hpd_init_empty_connectors(struct kunit *test)
/*
* With an empty connector list the per-connector loop is skipped and
* the initial clear loop relies on dc_interrupt_set() being a no-op
* the initial clear loop relies on amdgpu_dm_irq_set() being a no-op
* for a NULL dc, so init must complete without touching the DC.
*/
amdgpu_dm_hpd_init(adev);
@ -2004,7 +2004,7 @@ static void dm_test_hpd_init_fini_with_connectors(struct kunit *test)
/*
* num_hpd = 0 forces irq_type >= num_hpd so the loop takes the HW
* fallback (dc_interrupt_set()) instead of amdgpu_irq_get(); with a
* fallback (amdgpu_dm_irq_set()) instead of amdgpu_irq_get(); with a
* NULL dc that fallback is a safe no-op.
*/
adev->mode_info.num_hpd = 0;
@ -2090,7 +2090,7 @@ static void dm_test_hpd_init_fini_irq_ref(struct kunit *test)
/*
* num_hpd >= 1 makes irq_type (0) < num_hpd, so the loop takes the
* amdgpu_irq_get()/amdgpu_irq_put() branch instead of the
* dc_interrupt_set() fallback. The mock device has irq.installed ==
* amdgpu_dm_irq_set() fallback. The mock device has irq.installed ==
* false, so both calls fail early with -ENOENT (logging an error)
* without touching the base-driver irq state.
*/

View File

@ -1064,10 +1064,12 @@ bool dcn30_apply_idle_power_optimizations(struct dc *dc, bool enable)
*/
unsigned int denom = refresh_hz * 6528;
unsigned int stutter_period = dc->current_state->perf_params.stutter_period_us;
uint64_t num = (1000000LL + 2 * stutter_period * refresh_hz) *
(100LL + dc->debug.mall_additional_timer_percent);
uint64_t tmr_ticks;
tmr_delay = (uint32_t)(div_u64(((1000000LL + 2 * stutter_period * refresh_hz) *
(100LL + dc->debug.mall_additional_timer_percent) + denom - 1),
denom) - 64LL);
tmr_ticks = div_u64(num + denom - 1, denom);
tmr_delay = tmr_ticks > 64 ? (uint32_t)(tmr_ticks - 64) : 0;
/* In some cases the stutter period is really big (tiny modes) in these
* cases MALL cant be enabled, So skip these cases to avoid a ASSERT()
@ -1089,9 +1091,8 @@ bool dcn30_apply_idle_power_optimizations(struct dc *dc, bool enable)
}
denom *= 2;
tmr_delay = (uint32_t)(div_u64(((1000000LL + 2 * stutter_period * refresh_hz) *
(100LL + dc->debug.mall_additional_timer_percent) + denom - 1),
denom) - 64LL);
tmr_ticks = div_u64(num + denom - 1, denom);
tmr_delay = tmr_ticks > 64 ? (uint32_t)(tmr_ticks - 64) : 0;
}
/* Copy HW cursor */

View File

@ -67,7 +67,8 @@ static void dcn50_initialize_min_clocks(struct dc *dc)
* audio corruption. Read current DISPCLK from DENTIST and request the same
* freq to ensure that the timing is valid and unchanged.
*/
clocks->dispclk_khz = dc->clk_mgr->funcs->get_dispclk_from_dentist(dc->clk_mgr);
if (dc->clk_mgr->funcs->get_dispclk_from_dentist)
clocks->dispclk_khz = dc->clk_mgr->funcs->get_dispclk_from_dentist(dc->clk_mgr);
}
clocks->ref_dtbclk_khz = dc->clk_mgr->bw_params->clk_table.entries[0].dtbclk_mhz * 1000;
clocks->fclk_p_state_change_support = true;
@ -639,7 +640,8 @@ void dcn50_init_hw(struct dc *dc)
dc->res_pool->hubbub->funcs->allow_self_refresh_control(dc->res_pool->hubbub,
!dc->res_pool->hubbub->ctx->dc->debug.disable_stutter);
dcn50_initialize_min_clocks(dc);
if (dc->clk_mgr && dc->clk_mgr->funcs)
dcn50_initialize_min_clocks(dc);
/* On HW init, allow idle optimizations after pipes have been turned off.
*

View File

@ -643,7 +643,8 @@ void dcn60_init_hw(struct dc *dc)
dc->res_pool->hubbub->funcs->allow_self_refresh_control(dc->res_pool->hubbub,
!dc->res_pool->hubbub->ctx->dc->debug.disable_stutter);
dcn401_initialize_min_clocks(dc);
if (dc->clk_mgr && dc->clk_mgr->funcs)
dcn401_initialize_min_clocks(dc);
/* On HW init, allow idle optimizations after pipes have been turned off.
*
@ -1001,6 +1002,7 @@ static void dcn60_build_hubbub_perfmon_sequence(
/**
* dcn60_update_probe_status - Set the valid flag on a latched probe result.
* @status: result sink whose u was written by the GET BLS step during execute
* @probe: current probe state used to determine measurement type and validity
*/
static void dcn60_update_probe_status(struct dc_probe_status *status)
{
@ -1024,6 +1026,7 @@ static void dcn60_update_probe_status(struct dc_probe_status *status)
/**
* is_probe_measurement_type_for_hubbub - Returns true if the probe type is
* served by the hubbub perfmon block on DCN60.
* @type: the probe measurement type to classify
*/
static bool is_probe_measurement_type_for_hubbub(enum dc_probe_type type)
{

View File

@ -2142,6 +2142,7 @@ static void smu_v14_0_2_init_msg_ctl(struct smu_context *smu)
static ssize_t smu_v14_0_2_get_gpu_metrics(struct smu_context *smu,
void **table)
{
uint32_t mp1_ver = amdgpu_ip_version(smu->adev, MP1_HWIP, 0);
struct gpu_metrics_v1_3 *gpu_metrics =
(struct gpu_metrics_v1_3 *)smu_driver_table_ptr(
smu, SMU_DRIVER_TABLE_GPU_METRICS);
@ -2171,6 +2172,8 @@ static ssize_t smu_v14_0_2_get_gpu_metrics(struct smu_context *smu,
metrics->Vcn1ActivityPercentage);
gpu_metrics->average_socket_power = metrics->AverageSocketPower;
if (mp1_ver == IP_VERSION(14, 0, 3) && smu->smc_fw_version >= 0x00685000)
gpu_metrics->energy_accumulator = metrics->EnergyAccumulator;
if (metrics->AverageGfxActivity <= SMU_14_0_2_BUSY_THRESHOLD)
gpu_metrics->average_gfxclk_frequency = metrics->AverageGfxclkFrequencyPostDs;

View File

@ -7,3 +7,10 @@ kms_hdmi_inject@inject-4k,Fail
kms_lease@lease-uevent,Fail
msm/msm_mapping@memptrs,Fail
msm/msm_mapping@ring,Fail
# Started failing with v7.3-rc2 backmerge
# https://gitlab.freedesktop.org/drm/msm/-/work_items/104
kms_cursor_legacy@single-move,Fail
kms_cursor_legacy@torture-bo,Fail
kms_cursor_legacy@forked-bo,Fail
kms_cursor_legacy@torture-move,Fail

View File

@ -1462,10 +1462,12 @@ static int prepare_signaling(struct drm_device *dev,
struct dma_fence *fence;
struct drm_out_fence_state *f;
ret = -ENOMEM;
f = krealloc(*fence_state, sizeof(**fence_state) *
(*num_fences + 1), GFP_KERNEL);
if (!f)
return -ENOMEM;
goto err_free_event;
memset(&f[*num_fences], 0, sizeof(*f));
@ -1474,12 +1476,12 @@ static int prepare_signaling(struct drm_device *dev,
fence = drm_crtc_create_fence(crtc);
if (!fence)
return -ENOMEM;
goto err_free_event;
ret = setup_out_fence(&f[(*num_fences)++], fence);
if (ret) {
dma_fence_put(fence);
return ret;
goto err_free_event;
}
crtc_state->event->base.fence = fence;
@ -1535,6 +1537,11 @@ static int prepare_signaling(struct drm_device *dev,
}
return 0;
err_free_event:
drm_event_cancel_free(dev, &crtc_state->event->base);
crtc_state->event = NULL;
return ret;
}
static void complete_signaling(struct drm_device *dev,

View File

@ -482,6 +482,9 @@ int gud_plane_atomic_check(struct drm_plane *plane,
if (!new_plane_state->visible)
return 0;
if (gdrm->flags & GUD_DISPLAY_FLAG_FULL_UPDATE)
new_plane_state->ignore_damage_clips = true;
if (old_plane_state->rotation != new_plane_state->rotation)
crtc_state->mode_changed = true;
@ -562,8 +565,8 @@ int gud_plane_atomic_check(struct drm_plane *plane,
goto out;
}
req->properties[num_properties + i].prop = cpu_to_le16(prop);
req->properties[num_properties + i].val = cpu_to_le64(val);
req->properties[num_properties].prop = cpu_to_le16(prop);
req->properties[num_properties].val = cpu_to_le64(val);
num_properties++;
}

View File

@ -530,18 +530,13 @@ static int i9xx_check_cursor(struct intel_crtc_state *crtc_state,
}
static void i9xx_cursor_disable_sel_fetch_arm(struct intel_dsb *dsb,
struct intel_plane *plane)
struct intel_plane *plane,
const struct intel_crtc_state *crtc_state)
{
struct intel_display *display = to_intel_display(plane);
enum pipe pipe = plane->pipe;
/*
* Clear this whenever the hardware has selective fetch, not just when
* the current state uses it. The cursor may have been enabled with
* selective fetch earlier and had its enable bit orphaned when the
* feature was switched off.
*/
if (!HAS_PSR2_SEL_FETCH(display))
if (!crtc_state->enable_psr2_sel_fetch)
return;
intel_de_write_dsb(display, dsb, SEL_FETCH_CUR_CTL(pipe), 0);
@ -591,7 +586,7 @@ static void i9xx_cursor_update_sel_fetch_arm(struct intel_dsb *dsb,
if (crtc_state->enable_psr2_su_region_et)
wa_16021440873(dsb, plane, crtc_state, plane_state);
else
i9xx_cursor_disable_sel_fetch_arm(dsb, plane);
i9xx_cursor_disable_sel_fetch_arm(dsb, plane, crtc_state);
}
}
@ -700,7 +695,7 @@ static void i9xx_cursor_update_arm(struct intel_dsb *dsb,
if (plane_state)
i9xx_cursor_update_sel_fetch_arm(dsb, plane, crtc_state, plane_state);
else
i9xx_cursor_disable_sel_fetch_arm(dsb, plane);
i9xx_cursor_disable_sel_fetch_arm(dsb, plane, crtc_state);
if (plane->cursor.base != base ||
plane->cursor.size != fbc_ctl ||

View File

@ -426,12 +426,15 @@ calc_allowed_config_filter(struct intel_dp_link_caps *link_caps,
const struct intel_dp_link_config *forced_params)
{
struct intel_dp_link_caps_filter allowed_configs = INTEL_DP_LINK_CAPS_FILTER_NONE;
struct intel_display *display = to_intel_display(link_caps->dp);
struct intel_dp_link_caps_order order = bw_desc_config_order();
struct intel_dp_link_caps_iter iter;
struct intel_dp_link_config config;
iter_start(&iter, link_caps, order, enabled_configs);
for_each_dp_link_config(&iter, &config) {
int config_idx;
if (forced_params->rate &&
forced_params->rate != config.rate)
continue;
@ -446,7 +449,11 @@ calc_allowed_config_filter(struct intel_dp_link_caps *link_caps,
if (config.lane_count > max_limits->lane_count)
continue;
allowed_configs.config_mask |= BIT(iter_pos_to_idx(link_caps, order, iter.pos));
config_idx = iter_pos_to_idx(link_caps, order, iter.pos);
if (drm_WARN_ON(display->drm, config_idx < 0))
continue;
allowed_configs.config_mask |= BIT(config_idx);
}
intel_dp_link_caps_iter_end(&iter);

View File

@ -879,18 +879,13 @@ skl_plane_disable_arm(struct intel_dsb *dsb,
}
static void icl_plane_disable_sel_fetch_arm(struct intel_dsb *dsb,
struct intel_plane *plane)
struct intel_plane *plane,
const struct intel_crtc_state *crtc_state)
{
struct intel_display *display = to_intel_display(plane);
enum pipe pipe = plane->pipe;
/*
* Clear this whenever the hardware has selective fetch, not just when
* the current state uses it. The plane may have been enabled with
* selective fetch earlier and had its enable bit orphaned when the
* feature was switched off.
*/
if (!HAS_PSR2_SEL_FETCH(display))
if (!crtc_state->enable_psr2_sel_fetch)
return;
intel_de_write_dsb(display, dsb, SEL_FETCH_PLANE_CTL(pipe, plane->id), 0);
@ -926,7 +921,7 @@ icl_plane_disable_arm(struct intel_dsb *dsb,
skl_write_plane_wm(dsb, plane, crtc_state);
icl_plane_disable_sel_fetch_arm(dsb, plane);
icl_plane_disable_sel_fetch_arm(dsb, plane, crtc_state);
if (plane_has_normalizer(plane))
intel_de_write_dsb(display, dsb,
@ -1646,7 +1641,7 @@ static void icl_plane_update_sel_fetch_arm(struct intel_dsb *dsb,
intel_de_write_dsb(display, dsb, SEL_FETCH_PLANE_CTL(pipe, plane->id),
SEL_FETCH_PLANE_CTL_ENABLE);
else
icl_plane_disable_sel_fetch_arm(dsb, plane);
icl_plane_disable_sel_fetch_arm(dsb, plane, crtc_state);
}
static void

View File

@ -7,6 +7,7 @@
#include <drm/drm_atomic.h>
#include <drm/drm_atomic_helper.h>
#include <drm/drm_blend.h>
#include <drm/drm_framebuffer.h>
#include <drm/drm_gem_atomic_helper.h>
#include <drm/drm_print.h>
@ -765,7 +766,7 @@ int ls7a1000_cursor_plane_init(struct drm_device *ddev,
drm_plane_helper_add(plane, &ls7a1000_cursor_plane_helper_funcs);
return 0;
return drm_plane_create_blend_mode_property(plane, BIT(DRM_MODE_BLEND_COVERAGE));
}
int ls7a2000_cursor_plane_init(struct drm_device *ddev,
@ -790,5 +791,5 @@ int ls7a2000_cursor_plane_init(struct drm_device *ddev,
drm_plane_helper_add(plane, &ls7a2000_cursor_plane_helper_funcs);
return 0;
return drm_plane_create_blend_mode_property(plane, BIT(DRM_MODE_BLEND_COVERAGE));
}

View File

@ -177,7 +177,6 @@ quiet_cmd_headergen = GENHDR $@
cmd_headergen = mkdir -p $(obj)/generated && $(PYTHON3) $(src)/registers/gen_header.py \
$(headergen-opts) --rnn $(src)/registers --xml $< c-defines > $@
# TODO how to do this for a2xx/a5xx which have different .xml arg?
quiet_cmd_headergen_json = GENHDRJSN $@
cmd_headergen_json = mkdir -p $(obj)/generated && $(PYTHON3) $(src)/registers/gen_header.py \
$(headergen-opts) --rnn $(src)/registers --xml $(filter %.xml,$^) perfcntrs --json $< > $@

View File

@ -320,7 +320,7 @@ static int a6xx_gmu_start(struct a6xx_gmu *gmu)
gmu_write(gmu, REG_A6XX_GMU_CM3_SYSRESET, 0);
ret = gmu_poll_timeout(gmu, REG_A6XX_GMU_CM3_FW_INIT_RESULT, val,
(val & mask) == reset_val, 100, 10000);
(val & mask) == reset_val, 100, 100000);
if (ret)
DRM_DEV_ERROR(gmu->dev, "GMU firmware initialization timed out\n");

View File

@ -23,9 +23,15 @@ static u64 a6xx_gmu_get_timestamp(struct msm_gpu *gpu)
u64 count_hi, count_lo, temp;
do {
count_hi = gmu_read(&a6xx_gpu->gmu, REG_A6XX_GMU_ALWAYS_ON_COUNTER_H);
count_lo = gmu_read(&a6xx_gpu->gmu, REG_A6XX_GMU_ALWAYS_ON_COUNTER_L);
temp = gmu_read(&a6xx_gpu->gmu, REG_A6XX_GMU_ALWAYS_ON_COUNTER_H);
if (adreno_is_a750_family(adreno_gpu)) {
count_hi = gmu_read(&a6xx_gpu->gmu, REG_A7XX_GMU_CX_AO_COUNTER_H);
count_lo = gmu_read(&a6xx_gpu->gmu, REG_A7XX_GMU_CX_AO_COUNTER_L);
temp = gmu_read(&a6xx_gpu->gmu, REG_A7XX_GMU_CX_AO_COUNTER_H);
} else {
count_hi = gmu_read(&a6xx_gpu->gmu, REG_A6XX_GMU_ALWAYS_ON_COUNTER_H);
count_lo = gmu_read(&a6xx_gpu->gmu, REG_A6XX_GMU_ALWAYS_ON_COUNTER_L);
temp = gmu_read(&a6xx_gpu->gmu, REG_A6XX_GMU_ALWAYS_ON_COUNTER_H);
}
} while (unlikely(count_hi != temp));
return (count_hi << 32) | count_lo;

View File

@ -25,7 +25,7 @@ MODULE_PARM_DESC(disable_acd, "Forcefully disable GPU ACD");
module_param_unsafe(disable_acd, bool, 0400);
static bool skip_gpu;
MODULE_PARM_DESC(no_gpu, "Disable GPU driver register (0=enable GPU driver register (default), 1=skip GPU driver register");
MODULE_PARM_DESC(skip_gpu, "Disable GPU driver register (0=enable GPU driver register (default), 1=skip GPU driver register");
module_param(skip_gpu, bool, 0400);
extern const struct adreno_gpulist a2xx_gpulist;

View File

@ -52,6 +52,12 @@ static int zap_shader_load_mdt(struct msm_gpu *gpu, const char *fwname,
return -ENODEV;
}
/* We need PAS to be able to load the firmware */
if (!qcom_pas_is_available()) {
DRM_DEV_ERROR(dev, "PAS is not available\n");
return -EPROBE_DEFER;
}
ret = of_reserved_mem_region_to_resource(np, 0, &r);
if (ret) {
zap_available = false;
@ -170,18 +176,11 @@ static int zap_shader_load_mdt(struct msm_gpu *gpu, const char *fwname,
int adreno_zap_shader_load(struct msm_gpu *gpu, u32 pasid)
{
struct adreno_gpu *adreno_gpu = to_adreno_gpu(gpu);
struct platform_device *pdev = gpu->pdev;
/* Short cut if we determine the zap shader isn't available/needed */
if (!zap_available)
return -ENODEV;
/* We need PAS to be able to load the firmware */
if (!qcom_pas_is_available()) {
DRM_DEV_ERROR(&pdev->dev, "PAS is not available\n");
return -EPROBE_DEFER;
}
return zap_shader_load_mdt(gpu, adreno_gpu->info->zapfw, pasid);
}
@ -1262,6 +1261,8 @@ void adreno_gpu_cleanup(struct adreno_gpu *adreno_gpu)
for (i = 0; i < ARRAY_SIZE(adreno_gpu->info->fw); i++)
release_firmware(adreno_gpu->fw[i]);
pm_runtime_dont_use_autosuspend(&gpu->pdev->dev);
if (priv && pm_runtime_enabled(&priv->gpu_pdev->dev))
pm_runtime_disable(&priv->gpu_pdev->dev);

View File

@ -118,6 +118,7 @@ static inline void dpu_hw_ctl_clear_pending_flush(struct dpu_hw_ctl *ctx)
ctx->pending_intf_flush_mask = 0;
ctx->pending_wb_flush_mask = 0;
ctx->pending_cwb_flush_mask = 0;
ctx->pending_periph_flush_mask = 0;
ctx->pending_merge_3d_flush_mask = 0;
ctx->pending_dsc_flush_mask = 0;
ctx->pending_cdm_flush_mask = 0;

View File

@ -756,6 +756,7 @@ enum drm_mode_status msm_dp_display_mode_valid(struct msm_dp *dp,
struct msm_dp_link_info *link_info;
u32 mode_rate_khz = 0, supported_rate_khz = 0, mode_bpp = 0;
int mode_pclk_khz = mode->clock;
int link_pclk_khz;
bool is_yuv_420;
if (!dp || !mode_pclk_khz || !dp->connector) {
@ -775,6 +776,8 @@ enum drm_mode_status msm_dp_display_mode_valid(struct msm_dp *dp,
if (is_yuv_420 && !msm_dp_display->panel->vsc_sdp_supported)
return MODE_NO_420;
link_pclk_khz = is_yuv_420 ? mode_pclk_khz / 2 : mode_pclk_khz;
if (is_yuv_420 || msm_dp_display->wide_bus_supported)
mode_pclk_khz /= 2;
@ -786,9 +789,9 @@ enum drm_mode_status msm_dp_display_mode_valid(struct msm_dp *dp,
mode_bpp = default_bpp;
mode_bpp = msm_dp_panel_get_mode_bpp(msm_dp_display->panel,
mode_bpp, mode_pclk_khz);
mode_bpp, link_pclk_khz);
mode_rate_khz = mode_pclk_khz * mode_bpp;
mode_rate_khz = link_pclk_khz * mode_bpp;
supported_rate_khz = link_info->num_lanes * link_info->rate * 8;
if (mode_rate_khz > supported_rate_khz)
@ -1458,6 +1461,20 @@ void msm_dp_display_atomic_disable(struct msm_dp *dp)
msm_dp_display = container_of(dp, struct msm_dp_display_private, msm_dp_display);
/*
* If .atomic_enable() bailed out - link training failure is the common
* case - the mainlink was never brought up and ->power_on stayed false.
* Driving the PUSH_IDLE pattern into a controller that was never
* enabled times out, and .atomic_post_disable() then drops the
* controller's runtime-PM reference without tearing the PHY back down,
* because msm_dp_display_disable() returns early on !power_on. On
* glymur (Snapdragon X2 Elite) that combination is answered by a
* TrustZone-level SOCCP/ADSP force-stop and a silent SoC reset.
* There is nothing to push idle, so leave it alone.
*/
if (!dp->power_on)
return;
msm_dp_ctrl_push_idle(msm_dp_display->ctrl);
}

View File

@ -129,7 +129,7 @@ struct msm_dsi_host {
struct clk *dsi_pll_pixel_clk;
unsigned long byte_clk_rate;
unsigned long byte_intf_clk_rate;
bool byte_intf_clk_div_2;
unsigned long pixel_clk_rate;
unsigned long esc_clk_rate;
@ -382,8 +382,20 @@ int msm_dsi_runtime_resume(struct device *dev)
int dsi_link_clk_set_rate_6g(struct msm_dsi_host *msm_host)
{
unsigned long byte_intf_clk_rate;
long rounded_byte_clk_rate;
int ret;
rounded_byte_clk_rate = clk_round_rate(msm_host->byte_clk,
msm_host->byte_clk_rate);
if (rounded_byte_clk_rate < 0) {
pr_err("%s: failed to round byte clock rate, %ld\n",
__func__, rounded_byte_clk_rate);
return rounded_byte_clk_rate;
}
msm_host->byte_clk_rate = rounded_byte_clk_rate;
DBG("Set clk rates: pclk=%lu, byteclk=%lu",
msm_host->pixel_clk_rate, msm_host->byte_clk_rate);
@ -401,7 +413,11 @@ int dsi_link_clk_set_rate_6g(struct msm_dsi_host *msm_host)
}
if (msm_host->byte_intf_clk) {
ret = clk_set_rate(msm_host->byte_intf_clk, msm_host->byte_intf_clk_rate);
byte_intf_clk_rate = msm_host->byte_clk_rate;
if (msm_host->byte_intf_clk_div_2)
byte_intf_clk_rate /= 2;
ret = clk_set_rate(msm_host->byte_intf_clk, byte_intf_clk_rate);
if (ret) {
pr_err("%s: Failed to set rate byte intf clk, %d\n",
__func__, ret);
@ -669,24 +685,12 @@ static void dsi_calc_pclk(struct msm_dsi_host *msm_host, bool is_bonded_dsi)
int dsi_calc_clk_rate_6g(struct msm_dsi_host *msm_host, bool is_bonded_dsi)
{
long rounded_byte_clk_rate;
if (!msm_host->mode) {
pr_err("%s: mode not set\n", __func__);
return -EINVAL;
}
dsi_calc_pclk(msm_host, is_bonded_dsi);
rounded_byte_clk_rate = clk_round_rate(msm_host->byte_clk,
msm_host->byte_clk_rate);
if (rounded_byte_clk_rate < 0) {
pr_err("%s: failed to round byte clock rate, %ld\n",
__func__, rounded_byte_clk_rate);
return rounded_byte_clk_rate;
}
msm_host->byte_clk_rate = rounded_byte_clk_rate;
msm_host->esc_clk_rate = clk_get_rate(msm_host->esc_clk);
return 0;
}
@ -2495,9 +2499,7 @@ int msm_dsi_host_power_on(struct mipi_dsi_host *host,
goto unlock_ret;
}
msm_host->byte_intf_clk_rate = msm_host->byte_clk_rate;
if (phy_shared_timings->byte_intf_clk_div_2)
msm_host->byte_intf_clk_rate /= 2;
msm_host->byte_intf_clk_div_2 = phy_shared_timings->byte_intf_clk_div_2;
msm_dsi_sfpb_config(msm_host, true);

View File

@ -168,13 +168,13 @@ static int msm_hdmi_phy_probe(struct platform_device *pdev)
ret = msm_hdmi_phy_resource_enable(phy);
if (ret)
return ret;
goto err_pm_disable;
ret = msm_hdmi_phy_pll_init(pdev, phy->cfg->type);
if (ret) {
DRM_DEV_ERROR(dev, "couldn't init PLL\n");
msm_hdmi_phy_resource_disable(phy);
return ret;
goto err_pm_disable;
}
msm_hdmi_phy_resource_disable(phy);
@ -182,6 +182,10 @@ static int msm_hdmi_phy_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, phy);
return 0;
err_pm_disable:
pm_runtime_disable(dev);
return ret;
}
static void msm_hdmi_phy_remove(struct platform_device *pdev)

View File

@ -55,7 +55,7 @@ MODULE_PARM_DESC(modeset, "Use kernel modesetting [KMS] (1=on (default), 0=disab
module_param(modeset, bool, 0600);
static bool separate_gpu_kms;
MODULE_PARM_DESC(separate_gpu_drm, "Use separate DRM device for the GPU (0=single DRM device for both GPU and display (default), 1=two DRM devices)");
MODULE_PARM_DESC(separate_gpu_kms, "Use separate DRM device for the GPU (0=single DRM device for both GPU and display (default), 1=two DRM devices)");
module_param(separate_gpu_kms, bool, 0400);
DECLARE_FAULT_ATTR(fail_gem_alloc);

View File

@ -155,6 +155,7 @@ int msm_fbdev_driver_fbdev_probe(struct drm_fb_helper *helper,
helper->fb = buffer->fb;
fbi->fbops = &msm_fb_ops;
fbi->flags |= FBINFO_VIRTFB; /* system memory */
drm_fb_helper_fill_info(fbi, helper, sizes);

View File

@ -68,6 +68,9 @@ struct msm_gem_vm {
/** @base: Inherit from drm_gpuvm. */
struct drm_gpuvm base;
/** @rcu: RCU-delayed free so an exported sched fence->sched stays valid. */
struct rcu_head rcu;
/**
* @sched: Scheduler used for asynchronous VM_BIND request.
*

View File

@ -166,7 +166,7 @@ msm_gem_vm_free(struct drm_gpuvm *gpuvm)
dma_fence_put(vm->last_fence);
put_pid(vm->pid);
kfree(vm->log);
kfree(vm);
kfree_rcu(vm, rcu);
}
/**

View File

@ -140,5 +140,5 @@ void msm_ringbuffer_destroy(struct msm_ringbuffer *ring)
msm_gem_kernel_put(ring->bo, ring->gpu->vm);
kfree(ring);
kfree_rcu(ring, rcu);
}

View File

@ -55,6 +55,7 @@ struct msm_ringbuffer {
/*
* The job scheduler for this ring.
*/
struct rcu_head rcu;
struct drm_gpu_scheduler sched;
bool sched_initialized;

View File

@ -141,6 +141,8 @@ xsi:schemaLocation="https://gitlab.freedesktop.org/freedreno/ rules-fd.xsd">
<reg32 offset="0x1f9f0" name="GMU_BOOT_KMD_LM_HANDSHAKE"/>
<reg32 offset="0x1f957" name="GMU_LLM_GLM_SLEEP_CTRL"/>
<reg32 offset="0x1f958" name="GMU_LLM_GLM_SLEEP_STATUS"/>
<reg32 offset="0x1f880" name="GMU_CX_AO_COUNTER_L" variants="A7XX"/>
<reg32 offset="0x1f881" name="GMU_CX_AO_COUNTER_H" variants="A7XX"/>
<reg32 offset="0x1f888" name="GMU_ALWAYS_ON_COUNTER_L" variants="A6XX-A7XX"/>
<reg32 offset="0x1f840" name="GMU_ALWAYS_ON_COUNTER_L" variants="A8XX-"/>
<reg32 offset="0x1f889" name="GMU_ALWAYS_ON_COUNTER_H" variants="A6XX-A7XX"/>

View File

@ -208,7 +208,7 @@ r535_fbsr_resume(struct nvkm_gsp *gsp)
}
static int
r535_fbsr_suspend(struct nvkm_gsp *gsp, bool runtime)
r535_fbsr_suspend(struct nvkm_gsp *gsp)
{
struct nvkm_subdev *subdev = &gsp->subdev;
struct nvkm_device *device = subdev->device;

View File

@ -1749,7 +1749,7 @@ r535_gsp_fini(struct nvkm_gsp *gsp, enum nvkm_suspend_state suspend)
sr->sysmemAddrOfSuspendResumeData = gsp->sr.radix3.lvl0.addr;
sr->sizeOfSuspendResumeData = len;
ret = rm->api->fbsr->suspend(gsp, suspend == NVKM_RUNTIME_SUSPEND);
ret = rm->api->fbsr->suspend(gsp);
if (ret) {
nvkm_gsp_mem_dtor(&gsp->sr.meta);
nvkm_gsp_radix3_dtor(gsp, &gsp->sr.radix3);
@ -1761,8 +1761,12 @@ r535_gsp_fini(struct nvkm_gsp *gsp, enum nvkm_suspend_state suspend)
* TODO: Debug the GSP firmware / RPC handling to find out why
* without this Turing (but none of the other architectures)
* ends up resetting all channels after resume.
* Additionally, runtime suspend on other architectures quickly
* becomes unreliable without this sleep. If you're experiencing
* issues with runtime suspend, try bumping this delay up and
* sending a patch if it fixes your GPU.
*/
msleep(50);
msleep(200);
}
ret = r535_gsp_rpc_unloading_guest_driver(gsp, suspend);

View File

@ -62,7 +62,7 @@ r570_fbsr_resume(struct nvkm_gsp *gsp)
}
static int
r570_fbsr_init(struct nvkm_gsp *gsp, struct sg_table *sgt, u64 size, bool runtime)
r570_fbsr_init(struct nvkm_gsp *gsp, struct sg_table *sgt, u64 size)
{
NV2080_CTRL_INTERNAL_FBSR_INIT_PARAMS *ctrl;
struct nvkm_gsp_object memlist;
@ -81,7 +81,7 @@ r570_fbsr_init(struct nvkm_gsp *gsp, struct sg_table *sgt, u64 size, bool runtim
ctrl->hClient = gsp->internal.client.object.handle;
ctrl->hSysMem = memlist.handle;
ctrl->sysmemAddrOfSuspendResumeData = gsp->sr.meta.addr;
ctrl->bEnteringGcoffState = runtime ? 1 : 0;
ctrl->bEnteringGcoffState = 0;
ret = nvkm_gsp_rm_ctrl_wr(&gsp->internal.device.subdevice, ctrl);
if (ret)
@ -92,7 +92,7 @@ r570_fbsr_init(struct nvkm_gsp *gsp, struct sg_table *sgt, u64 size, bool runtim
}
static int
r570_fbsr_suspend(struct nvkm_gsp *gsp, bool runtime)
r570_fbsr_suspend(struct nvkm_gsp *gsp)
{
struct nvkm_subdev *subdev = &gsp->subdev;
struct nvkm_device *device = subdev->device;
@ -133,7 +133,7 @@ r570_fbsr_suspend(struct nvkm_gsp *gsp, bool runtime)
return ret;
/* Initialise FBSR on RM. */
ret = r570_fbsr_init(gsp, &gsp->sr.fbsr, size, runtime);
ret = r570_fbsr_init(gsp, &gsp->sr.fbsr, size);
if (ret) {
nvkm_gsp_sg_free(device, &gsp->sr.fbsr);
return ret;

View File

@ -207,7 +207,8 @@ r570_gsp_set_rmargs(struct nvkm_gsp *gsp, bool resume)
args->srInitArguments.bInPMTransition = 0;
} else {
args->srInitArguments.oldLevel = NV2080_CTRL_GPU_SET_POWER_STATE_GPU_LEVEL_3;
args->srInitArguments.flags = 0;
args->srInitArguments.flags =
GPU_STATE_FLAGS_PRESERVING | GPU_STATE_FLAGS_PM_TRANSITION;
args->srInitArguments.bInPMTransition = 1;
}

View File

@ -523,6 +523,14 @@ typedef struct
#define NV2080_CTRL_GPU_SET_POWER_STATE_GPU_LEVEL_3 (0x00000003U)
#define GPU_STATE_FLAGS_PRESERVING BIT(0) // GPU state is preserved
#define GPU_STATE_FLAGS_VGA_TRANSITION BIT(1) // To be used with GPU_STATE_FLAGS_PRESERVING.
#define GPU_STATE_FLAGS_PM_TRANSITION BIT(2) // To be used with GPU_STATE_FLAGS_PRESERVING.
#define GPU_STATE_FLAGS_PM_SUSPEND BIT(3)
#define GPU_STATE_FLAGS_PM_HIBERNATE BIT(4)
#define GPU_STATE_FLAGS_GC6_TRANSITION BIT(5) // To be used with GPU_STATE_FLAGS_PRESERVING.
#define GPU_STATE_FLAGS_FAST_UNLOAD BIT(6) // Used during windows restart, skips stateDestroy steps
typedef struct
{
// Magic for verification by secure ucode

View File

@ -79,7 +79,7 @@ struct nvkm_rm_api {
} *device;
const struct nvkm_rm_api_fbsr {
int (*suspend)(struct nvkm_gsp *, bool runtime);
int (*suspend)(struct nvkm_gsp *);
void (*resume)(struct nvkm_gsp *);
} *fbsr;

View File

@ -559,9 +559,10 @@ struct drm_sched_job *drm_sched_entity_pop_job(struct drm_sched_entity *entity)
*/
smp_wmb();
spin_lock(&entity->lock);
spsc_queue_pop(&entity->job_queue);
drm_sched_rq_pop_entity(entity);
spin_unlock(&entity->lock);
/* Jobs and entities might have different lifecycles. Since we're
* removing the job from the entities queue, set the jobs entity pointer
@ -647,6 +648,9 @@ void drm_sched_entity_push_job(struct drm_sched_job *sched_job)
* Make sure to set the submit_ts first, to avoid a race.
*/
sched_job->submit_ts = submit_ts = ktime_get();
spin_lock(&entity->lock);
first = spsc_queue_push(&entity->job_queue, &sched_job->queue_node);
/* first job wakes up scheduler */
@ -657,5 +661,7 @@ void drm_sched_entity_push_job(struct drm_sched_job *sched_job)
if (sched)
drm_sched_wakeup(sched);
}
spin_unlock(&entity->lock);
}
EXPORT_SYMBOL(drm_sched_entity_push_job);

View File

@ -257,19 +257,17 @@ static ktime_t drm_sched_entity_get_job_ts(struct drm_sched_entity *entity)
struct drm_gpu_scheduler *
drm_sched_rq_add_entity(struct drm_sched_entity *entity, ktime_t ts)
{
struct drm_sched_rq *rq = entity->rq;
struct drm_gpu_scheduler *sched;
struct drm_sched_rq *rq;
/* Add the entity to the run queue */
spin_lock(&entity->lock);
if (entity->stopped) {
spin_unlock(&entity->lock);
lockdep_assert_held(&entity->lock);
if (entity->stopped) {
DRM_ERROR("Trying to push to a killed entity\n");
return NULL;
}
rq = entity->rq;
spin_lock(&rq->lock);
sched = rq->sched;
@ -289,7 +287,6 @@ drm_sched_rq_add_entity(struct drm_sched_entity *entity, ktime_t ts)
drm_sched_rq_update_fifo_locked(entity, rq, ts);
spin_unlock(&rq->lock);
spin_unlock(&entity->lock);
return sched;
}
@ -343,16 +340,17 @@ drm_sched_rq_next_rr_ts(struct drm_sched_rq *rq,
*/
void drm_sched_rq_pop_entity(struct drm_sched_entity *entity)
{
struct drm_sched_rq *rq = entity->rq;
struct drm_sched_job *next_job;
struct drm_sched_rq *rq;
lockdep_assert_held(&entity->lock);
spin_lock(&rq->lock);
/*
* Update the entity's location in the min heap according to
* the timestamp of the next job, if any.
*/
spin_lock(&entity->lock);
rq = entity->rq;
spin_lock(&rq->lock);
next_job = drm_sched_entity_queue_peek(entity);
if (next_job) {
ktime_t ts;
@ -375,8 +373,8 @@ void drm_sched_rq_pop_entity(struct drm_sched_entity *entity)
drm_sched_entity_save_vruntime(entity, min_vruntime);
}
}
spin_unlock(&rq->lock);
spin_unlock(&entity->lock);
}
/**

View File

@ -1434,7 +1434,7 @@ ttm_bo_swapout_cb(struct ttm_lru_walk *walk, struct ttm_buffer_object *bo)
if (ttm_tt_is_populated(tt)) {
ret = ttm_tt_swapout(bdev, tt, swapout_walk->gfp_flags);
if (!ret) {
if (ret > 0) {
spin_lock(&bdev->lru_lock);
ttm_resource_del_bulk_move_unevictable(bo->resource, bo);
ttm_resource_move_to_lru_tail(bo->resource);

View File

@ -1188,7 +1188,7 @@ int vc4_kms_load(struct drm_device *dev)
drm_mode_config_reset(dev);
drm_kms_helper_poll_init(dev);
drmm_kms_helper_poll_init(dev);
return 0;
}

View File

@ -11,6 +11,7 @@
#include <drm/drm_atomic.h>
#include <drm/drm_atomic_helper.h>
#include <drm/drm_blend.h>
#include <drm/drm_crtc.h>
#include <drm/drm_fourcc.h>
#include <drm/drm_framebuffer.h>
@ -267,6 +268,7 @@ struct drm_plane *vs_cursor_plane_init(struct drm_device *drm_dev,
return plane;
drm_plane_helper_add(plane, &vs_cursor_plane_helper_funcs);
drm_plane_create_blend_mode_property(plane, BIT(DRM_MODE_BLEND_COVERAGE));
return plane;
}

View File

@ -10,82 +10,50 @@
#include "vs_dc_top_regs.h"
#include "vs_hwdb.h"
static const u32 vs_formats_array_no_yuv444[] = {
static const u32 vs_primary_formats_array_no_yuv444[] = {
DRM_FORMAT_XRGB4444,
DRM_FORMAT_XBGR4444,
DRM_FORMAT_RGBX4444,
DRM_FORMAT_BGRX4444,
DRM_FORMAT_ARGB4444,
DRM_FORMAT_ABGR4444,
DRM_FORMAT_RGBA4444,
DRM_FORMAT_BGRA4444,
DRM_FORMAT_XRGB1555,
DRM_FORMAT_XBGR1555,
DRM_FORMAT_RGBX5551,
DRM_FORMAT_BGRX5551,
DRM_FORMAT_ARGB1555,
DRM_FORMAT_ABGR1555,
DRM_FORMAT_RGBA5551,
DRM_FORMAT_BGRA5551,
DRM_FORMAT_RGB565,
DRM_FORMAT_BGR565,
DRM_FORMAT_XRGB8888,
DRM_FORMAT_XBGR8888,
DRM_FORMAT_RGBX8888,
DRM_FORMAT_BGRX8888,
DRM_FORMAT_ARGB8888,
DRM_FORMAT_ABGR8888,
DRM_FORMAT_RGBA8888,
DRM_FORMAT_BGRA8888,
DRM_FORMAT_ARGB2101010,
DRM_FORMAT_ABGR2101010,
DRM_FORMAT_RGBA1010102,
DRM_FORMAT_BGRA1010102,
/* TODO: non-RGB formats */
};
static const u32 vs_formats_array_with_yuv444[] = {
static const u32 vs_primary_formats_array_with_yuv444[] = {
DRM_FORMAT_XRGB4444,
DRM_FORMAT_XBGR4444,
DRM_FORMAT_RGBX4444,
DRM_FORMAT_BGRX4444,
DRM_FORMAT_ARGB4444,
DRM_FORMAT_ABGR4444,
DRM_FORMAT_RGBA4444,
DRM_FORMAT_BGRA4444,
DRM_FORMAT_XRGB1555,
DRM_FORMAT_XBGR1555,
DRM_FORMAT_RGBX5551,
DRM_FORMAT_BGRX5551,
DRM_FORMAT_ARGB1555,
DRM_FORMAT_ABGR1555,
DRM_FORMAT_RGBA5551,
DRM_FORMAT_BGRA5551,
DRM_FORMAT_RGB565,
DRM_FORMAT_BGR565,
DRM_FORMAT_XRGB8888,
DRM_FORMAT_XBGR8888,
DRM_FORMAT_RGBX8888,
DRM_FORMAT_BGRX8888,
DRM_FORMAT_ARGB8888,
DRM_FORMAT_ABGR8888,
DRM_FORMAT_RGBA8888,
DRM_FORMAT_BGRA8888,
DRM_FORMAT_ARGB2101010,
DRM_FORMAT_ABGR2101010,
DRM_FORMAT_RGBA1010102,
DRM_FORMAT_BGRA1010102,
/* TODO: non-RGB formats */
};
static const struct vs_formats vs_formats_no_yuv444 = {
.array = vs_formats_array_no_yuv444,
.num = ARRAY_SIZE(vs_formats_array_no_yuv444)
.primary_array = vs_primary_formats_array_no_yuv444,
.primary_num = ARRAY_SIZE(vs_primary_formats_array_no_yuv444)
};
static const struct vs_formats vs_formats_with_yuv444 = {
.array = vs_formats_array_with_yuv444,
.num = ARRAY_SIZE(vs_formats_array_with_yuv444)
.primary_array = vs_primary_formats_array_with_yuv444,
.primary_num = ARRAY_SIZE(vs_primary_formats_array_with_yuv444)
};
static struct vs_chip_identity vs_chip_identities[] = {

View File

@ -10,8 +10,8 @@
#include <linux/types.h>
struct vs_formats {
const u32 *array;
unsigned int num;
const u32 *primary_array;
unsigned int primary_num;
};
struct vs_chip_identity {

View File

@ -168,8 +168,8 @@ struct drm_plane *vs_primary_plane_init(struct drm_device *drm_dev, struct vs_dc
plane = drmm_universal_plane_alloc(drm_dev, struct drm_plane, dev, 0,
&vs_primary_plane_funcs,
dc->identity.formats->array,
dc->identity.formats->num,
dc->identity.formats->primary_array,
dc->identity.formats->primary_num,
NULL,
DRM_PLANE_TYPE_PRIMARY,
NULL);

View File

@ -318,8 +318,10 @@ void xe_i2c_pm_resume(struct xe_device *xe, bool d3cold)
static void xe_i2c_remove(void *data)
{
struct xe_i2c *i2c = data;
struct xe_device *xe = tile_to_xe(i2c->mmio->tile);
unsigned int i;
xe_i2c_irq_reset(xe);
xe_amc_exit(i2c);
for (i = 0; i < XE_I2C_MAX_CLIENTS; i++) {
@ -329,6 +331,7 @@ static void xe_i2c_remove(void *data)
bus_unregister_notifier(&i2c_bus_type, &i2c->bus_notifier);
xe_i2c_unregister_adapter(i2c);
xe->i2c = NULL;
}
/**

View File

@ -5,9 +5,9 @@
#include "xe_mmio_gem.h"
#include <linux/dma-resv.h>
#include <drm/drm_drv.h>
#include <drm/drm_gem.h>
#include <drm/drm_managed.h>
#include "xe_device_types.h"
@ -37,12 +37,24 @@ static vm_fault_t xe_mmio_gem_vm_fault(struct vm_fault *);
struct xe_mmio_gem {
struct drm_gem_object base;
phys_addr_t phys_addr;
struct page *dummy_page; /* protected by the GEM's dma_resv */
bool destroyed; /* protected by the GEM's dma_resv */
};
static int xe_mmio_gem_vm_may_split(struct vm_area_struct *area, unsigned long addr)
{
/*
* Forbid splitting. Together with VM_DONTEXPAND, this keeps the VMA
* matching the GEM object exactly.
*/
return -EINVAL;
}
static const struct vm_operations_struct vm_ops = {
.open = drm_gem_vm_open,
.close = drm_gem_vm_close,
.fault = xe_mmio_gem_vm_fault,
.may_split = xe_mmio_gem_vm_may_split,
};
static const struct drm_gem_object_funcs xe_mmio_gem_funcs = {
@ -121,6 +133,8 @@ static void xe_mmio_gem_free(struct drm_gem_object *base)
{
struct xe_mmio_gem *obj = to_xe_mmio_gem(base);
if (obj->dummy_page)
__free_page(obj->dummy_page);
drm_gem_object_release(base);
kfree(obj);
}
@ -128,15 +142,31 @@ static void xe_mmio_gem_free(struct drm_gem_object *base)
/**
* xe_mmio_gem_destroy - Destroy the GEM object that exposes an MMIO region
* @gem: the GEM object to destroy
* @file: DRM file descriptor previously passed to xe_mmio_gem_create()
*
* This function releases resources associated with the GEM object created by
* xe_mmio_gem_create().
*
* See: "Exposing MMIO regions to userspace"
*/
void xe_mmio_gem_destroy(struct xe_mmio_gem *gem)
void xe_mmio_gem_destroy(struct xe_mmio_gem *gem, struct drm_file *file)
{
xe_mmio_gem_free(&gem->base);
struct drm_gem_object *base = &gem->base;
struct drm_device *dev = base->dev;
drm_vma_node_revoke(&base->vma_node, file);
dma_resv_lock(base->resv, NULL);
gem->destroyed = true;
dma_resv_unlock(base->resv);
/*
* Setting 'destroyed' under lock takes care of the subsequent faults.
* Zap the existing PTEs to cut off access to the real MMIO through
* currently mapped pages.
*/
drm_vma_node_unmap(&base->vma_node, dev->anon_inode->i_mapping);
drm_gem_object_put(base);
}
static int xe_mmio_gem_mmap(struct drm_gem_object *base, struct vm_area_struct *vma)
@ -147,8 +177,6 @@ static int xe_mmio_gem_mmap(struct drm_gem_object *base, struct vm_area_struct *
if ((vma->vm_flags & VM_SHARED) == 0)
return -EINVAL;
/* Set vm_pgoff (used as a fake buffer offset by DRM) to 0 */
vma->vm_pgoff = 0;
vma->vm_page_prot = pgprot_noncached(vma_get_page_prot(vma));
vm_flags_set(vma, VM_IO | VM_PFNMAP | VM_DONTEXPAND | VM_DONTDUMP |
VM_DONTCOPY | VM_NORESERVE);
@ -157,51 +185,47 @@ static int xe_mmio_gem_mmap(struct drm_gem_object *base, struct vm_area_struct *
return 0;
}
static void xe_mmio_gem_release_dummy_page(struct drm_device *dev, void *res)
static int alloc_dummy_page_if_needed(struct drm_gem_object *base)
{
__free_page((struct page *)res);
struct xe_mmio_gem *obj = to_xe_mmio_gem(base);
dma_resv_assert_held(base->resv);
if (!obj->dummy_page)
obj->dummy_page = alloc_page(GFP_KERNEL | __GFP_ZERO);
return obj->dummy_page ? 0 : -ENOMEM;
}
static vm_fault_t xe_mmio_gem_vm_fault_dummy_page(struct vm_area_struct *vma)
static vm_fault_t xe_mmio_gem_vm_fault_dummy_page(struct vm_fault *vmf)
{
struct vm_area_struct *vma = vmf->vma;
struct drm_gem_object *base = vma->vm_private_data;
struct drm_device *dev = base->dev;
vm_fault_t ret = VM_FAULT_NOPAGE;
struct page *page;
struct xe_mmio_gem *obj = to_xe_mmio_gem(base);
unsigned long pfn;
unsigned long i;
page = alloc_page(GFP_KERNEL | __GFP_ZERO);
if (!page)
if (alloc_dummy_page_if_needed(base))
return VM_FAULT_OOM;
if (drmm_add_action_or_reset(dev, xe_mmio_gem_release_dummy_page, page))
return VM_FAULT_OOM;
pfn = page_to_pfn(obj->dummy_page);
pfn = page_to_pfn(page);
/* Map the entire VMA to the same dummy page */
for (i = 0; i < base->size; i += PAGE_SIZE) {
unsigned long addr = vma->vm_start + i;
ret = vmf_insert_pfn(vma, addr, pfn);
if (ret & VM_FAULT_ERROR)
break;
}
return ret;
return vmf_insert_pfn_prot(vma, vmf->address, pfn,
vm_get_page_prot(vma->vm_flags));
}
static vm_fault_t xe_mmio_gem_vm_fault(struct vm_fault *vmf)
static vm_fault_t xe_mmio_gem_vm_fault_locked(struct vm_fault *vmf)
{
struct vm_area_struct *vma = vmf->vma;
struct drm_gem_object *base = vma->vm_private_data;
struct xe_mmio_gem *obj = to_xe_mmio_gem(base);
struct drm_device *dev = base->dev;
vm_fault_t ret = VM_FAULT_NOPAGE;
unsigned long i;
unsigned long addr, pfn;
int idx;
dma_resv_assert_held(base->resv);
if (obj->destroyed)
return VM_FAULT_SIGBUS;
if (!drm_dev_enter(dev, &idx)) {
/*
* Provide a dummy page to avoid SIGBUS for events such as hot-unplug.
@ -209,18 +233,30 @@ static vm_fault_t xe_mmio_gem_vm_fault(struct vm_fault *vmf)
* It is assumed the userspace will receive the notification via some
* other channel (e.g. drm uevent).
*/
return xe_mmio_gem_vm_fault_dummy_page(vma);
return xe_mmio_gem_vm_fault_dummy_page(vmf);
}
for (i = 0; i < base->size; i += PAGE_SIZE) {
unsigned long addr = vma->vm_start + i;
unsigned long phys_addr = obj->phys_addr + i;
ret = vmf_insert_pfn(vma, addr, PHYS_PFN(phys_addr));
pfn = PHYS_PFN(obj->phys_addr);
for (addr = vma->vm_start; addr < vma->vm_end; addr += PAGE_SIZE) {
ret = vmf_insert_pfn(vma, addr, pfn);
if (ret & VM_FAULT_ERROR)
break;
pfn++;
}
drm_dev_exit(idx);
return ret;
}
static vm_fault_t xe_mmio_gem_vm_fault(struct vm_fault *vmf)
{
struct vm_area_struct *vma = vmf->vma;
struct drm_gem_object *base = vma->vm_private_data;
vm_fault_t ret;
dma_resv_lock(base->resv, NULL);
ret = xe_mmio_gem_vm_fault_locked(vmf);
dma_resv_unlock(base->resv);
return ret;
}

View File

@ -15,6 +15,6 @@ struct xe_mmio_gem;
struct xe_mmio_gem *xe_mmio_gem_create(struct xe_device *xe, struct drm_file *file,
phys_addr_t phys_addr, size_t size);
u64 xe_mmio_gem_mmap_offset(struct xe_mmio_gem *gem);
void xe_mmio_gem_destroy(struct xe_mmio_gem *gem);
void xe_mmio_gem_destroy(struct xe_mmio_gem *gem, struct drm_file *file);
#endif /* _XE_MMIO_GEM_H_ */

View File

@ -54,13 +54,40 @@ xe_shrinker_mod_pages(struct xe_shrinker *shrinker, long shrinkable, long purgea
write_unlock(&shrinker->lock);
}
static s64 __xe_shrinker_walk(struct xe_device *xe,
static bool __xe_shrinker_runtime_pm_get(struct xe_shrinker *shrinker)
{
struct xe_device *xe = shrinker->xe;
if (xe_pm_runtime_get_if_active(xe))
return true;
if (xe_rpm_reclaim_safe(xe) && !ttm_bo_shrink_avoid_wait()) {
xe_pm_runtime_get(xe);
return true;
}
queue_work(xe->unordered_wq, &shrinker->pm_worker);
return false;
}
static void xe_shrinker_runtime_pm_put(struct xe_shrinker *shrinker, bool runtime_pm)
{
if (runtime_pm)
xe_pm_runtime_put(shrinker->xe);
}
static int __xe_shrinker_walk(struct xe_shrinker *shrinker,
struct ttm_operation_ctx *ctx,
const struct xe_bo_shrink_flags flags,
unsigned long to_scan, unsigned long *scanned)
unsigned long to_scan, unsigned long *scanned,
unsigned long *freed)
{
struct xe_device *xe = shrinker->xe;
unsigned int mem_type;
s64 freed = 0, lret;
bool rpm = false;
int ret = 0;
s64 lret;
for (mem_type = XE_PL_SYSTEM; mem_type <= XE_PL_TT; ++mem_type) {
struct ttm_resource_manager *man = ttm_manager_type(&xe->ttm, mem_type);
@ -74,23 +101,35 @@ static s64 __xe_shrinker_walk(struct xe_device *xe,
if (!man || !man->use_tt)
continue;
if (mem_type != XE_PL_SYSTEM && !rpm &&
xe_device_is_l2_flush_optimized(xe)) {
if (!__xe_shrinker_runtime_pm_get(shrinker))
break;
rpm = true;
}
ttm_bo_lru_for_each_reserved_guarded(&curs, man, &arg, ttm_bo) {
if (!ttm_bo_shrink_suitable(ttm_bo, ctx))
continue;
lret = xe_bo_shrink(ctx, ttm_bo, flags, scanned);
if (lret < 0)
return lret;
if (lret < 0) {
ret = lret;
goto out;
}
freed += lret;
*freed += lret;
if (*scanned >= to_scan)
break;
goto out;
}
/* Trylocks should never error, just fail. */
xe_assert(xe, !IS_ERR(ttm_bo));
}
return freed;
out:
xe_shrinker_runtime_pm_put(shrinker, rpm);
return ret;
}
/*
@ -99,40 +138,36 @@ static s64 __xe_shrinker_walk(struct xe_device *xe,
* add writeback. This avoids stalls and explicit writebacks with light or
* moderate memory pressure.
*/
static s64 xe_shrinker_walk(struct xe_device *xe,
static int xe_shrinker_walk(struct xe_shrinker *shrinker,
struct ttm_operation_ctx *ctx,
const struct xe_bo_shrink_flags flags,
unsigned long to_scan, unsigned long *scanned)
unsigned long to_scan, unsigned long *scanned,
unsigned long *freed)
{
bool no_wait_gpu = true;
struct xe_bo_shrink_flags save_flags = flags;
s64 lret, freed;
int ret;
swap(no_wait_gpu, ctx->no_wait_gpu);
save_flags.writeback = false;
lret = __xe_shrinker_walk(xe, ctx, save_flags, to_scan, scanned);
ret = __xe_shrinker_walk(shrinker, ctx, save_flags, to_scan, scanned,
freed);
swap(no_wait_gpu, ctx->no_wait_gpu);
if (lret < 0 || *scanned >= to_scan)
return lret;
if (ret || *scanned >= to_scan)
return ret;
freed = lret;
if (!ctx->no_wait_gpu) {
lret = __xe_shrinker_walk(xe, ctx, save_flags, to_scan, scanned);
if (lret < 0)
return lret;
freed += lret;
if (*scanned >= to_scan)
return freed;
ret = __xe_shrinker_walk(shrinker, ctx, save_flags, to_scan, scanned,
freed);
if (ret || *scanned >= to_scan)
return ret;
}
if (flags.writeback) {
lret = __xe_shrinker_walk(xe, ctx, flags, to_scan, scanned);
if (lret < 0)
return lret;
freed += lret;
}
if (flags.writeback)
ret = __xe_shrinker_walk(shrinker, ctx, flags, to_scan, scanned,
freed);
return freed;
return ret;
}
static unsigned long
@ -180,22 +215,7 @@ static bool xe_shrinker_runtime_pm_get(struct xe_shrinker *shrinker, bool force,
return false;
}
if (!xe_pm_runtime_get_if_active(xe)) {
if (xe_rpm_reclaim_safe(xe) && !ttm_bo_shrink_avoid_wait()) {
xe_pm_runtime_get(xe);
return true;
}
queue_work(xe->unordered_wq, &shrinker->pm_worker);
return false;
}
return true;
}
static void xe_shrinker_runtime_pm_put(struct xe_shrinker *shrinker, bool runtime_pm)
{
if (runtime_pm)
xe_pm_runtime_put(shrinker->xe);
return __xe_shrinker_runtime_pm_get(shrinker);
}
static unsigned long xe_shrinker_scan(struct shrinker *shrink, struct shrink_control *sc)
@ -214,7 +234,6 @@ static unsigned long xe_shrinker_scan(struct shrinker *shrink, struct shrink_con
bool runtime_pm;
bool purgeable;
bool can_backup = !!(sc->gfp_mask & __GFP_FS);
s64 lret;
nr_to_scan = sc->nr_to_scan;
@ -225,12 +244,9 @@ static unsigned long xe_shrinker_scan(struct shrinker *shrink, struct shrink_con
/* Might need runtime PM. Try to wake early if it looks like it. */
runtime_pm = xe_shrinker_runtime_pm_get(shrinker, false, nr_to_scan, can_backup);
if (purgeable && nr_scanned < nr_to_scan) {
lret = xe_shrinker_walk(shrinker->xe, &ctx, shrink_flags,
nr_to_scan, &nr_scanned);
if (lret >= 0)
freed += lret;
}
if (purgeable && nr_scanned < nr_to_scan)
xe_shrinker_walk(shrinker, &ctx, shrink_flags,
nr_to_scan, &nr_scanned, &freed);
sc->nr_scanned = nr_scanned;
if (nr_scanned >= nr_to_scan || !can_backup)
@ -242,10 +258,8 @@ static unsigned long xe_shrinker_scan(struct shrinker *shrink, struct shrink_con
shrink_flags.purge = false;
lret = xe_shrinker_walk(shrinker->xe, &ctx, shrink_flags,
nr_to_scan, &nr_scanned);
if (lret >= 0)
freed += lret;
xe_shrinker_walk(shrinker, &ctx, shrink_flags,
nr_to_scan, &nr_scanned, &freed);
sc->nr_scanned = nr_scanned;
out:

View File

@ -141,6 +141,9 @@ struct dma_fence_ops {
* compute the name at runtime, without having it to store permanently
* for each fence, or build a cache of some sort.
*
* The returned string is RCU protected and can be freed after the fence
* signaled and a RCU grace period passed.
*
* This callback is mandatory.
*/
const char * (*get_driver_name)(struct dma_fence *fence);
@ -153,6 +156,9 @@ struct dma_fence_ops {
* having it to store permanently for each fence, or build a cache of
* some sort.
*
* The returned string is RCU protected and can be freed after the fence
* signaled and a RCU grace period passed.
*
* This callback is mandatory.
*/
const char * (*get_timeline_name)(struct dma_fence *fence);