summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDave Airlie <airlied@redhat.com>2026-06-03 14:31:52 +1000
committerDave Airlie <airlied@redhat.com>2026-06-03 15:47:53 +1000
commit61cd9291cab2fe34b6260d092d633f891c32bf9c (patch)
treee32e9f92d8693207d389ac23a559d8abc303cbaf
parentedbea3eff5b45e32d5c398583637b8dfef136832 (diff)
parent61de054a772a1feda6364931ab1baf9038abf1c8 (diff)
downloadlinux-61cd9291cab2fe34b6260d092d633f891c32bf9c.tar.gz
linux-61cd9291cab2fe34b6260d092d633f891c32bf9c.zip
Merge tag 'drm-misc-next-2026-05-28' of https://gitlab.freedesktop.org/drm/misc/kernel into drm-next
drm-misc-next for v7.2-rc1: UAPI Changes: - amdxdna: Revert read-only user-pointer BO mappings. - panthor: Add eviction and reclaim info to fdinfo. Cross-subsystem Changes: - Convert DMA-buf system and cma heap allocators to module. Core Changes: - Cleanup driver misuses of drm/exec. Driver Changes: - Add LG LP129WT232166, AM-1280800W8TZQW-T00H, NEC NL6448BC33-70C, Riverdi RVT70HSLNWCA0 and RVT101HVLNWC00 panels. - Add support for RZ/T2H SoC to renesas. - Add cursor plane support to verisilicon. - Support DVI outputs in ite-it66121 bridge. - Assorted bugfixes, docbook updates and improvements to ivpu, tegra, host1x, nouveau. - Add DSC quirk for ASUS DC301 USB-C dock. - Use drm client buffer for tegra framebuffer. - Add support for GA100 to nouveau. Signed-off-by: Dave Airlie <airlied@redhat.com> From: Maarten Lankhorst <maarten.lankhorst@linux.intel.com> Link: https://patch.msgid.link/ef65f43c-becf-473c-a5cc-203fdfecd491@linux.intel.com
-rw-r--r--Documentation/devicetree/bindings/display/panel/panel-lvds.yaml4
-rw-r--r--Documentation/devicetree/bindings/display/panel/panel-simple.yaml4
-rw-r--r--Documentation/devicetree/bindings/display/renesas,rzg2l-du.yaml21
-rw-r--r--arch/arm64/configs/defconfig3
-rw-r--r--drivers/accel/amdxdna/amdxdna_ubuf.c29
-rw-r--r--drivers/accel/ivpu/ivpu_drv.h3
-rw-r--r--drivers/accel/ivpu/ivpu_fw.c23
-rw-r--r--drivers/accel/ivpu/ivpu_ipc.c6
-rw-r--r--drivers/accel/ivpu/ivpu_jsm_msg.c3
-rw-r--r--drivers/accel/ivpu/vpu_boot_api.h6
-rw-r--r--drivers/dma-buf/dma-heap.c1
-rw-r--r--drivers/dma-buf/heaps/Kconfig4
-rw-r--r--drivers/dma-buf/heaps/cma_heap.c3
-rw-r--r--drivers/dma-buf/heaps/system_heap.c5
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gpuvm.c4
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c15
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_eviction_fence.c3
-rw-r--r--drivers/gpu/drm/bridge/Kconfig2
-rw-r--r--drivers/gpu/drm/bridge/ite-it66121.c284
-rw-r--r--drivers/gpu/drm/display/drm_dp_helper.c2
-rw-r--r--drivers/gpu/drm/drm_exec.c9
-rw-r--r--drivers/gpu/drm/drm_gpuvm.c3
-rw-r--r--drivers/gpu/drm/drm_panel.c1
-rw-r--r--drivers/gpu/drm/nouveau/include/nvkm/subdev/gsp.h8
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/device/base.c10
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/bios/shadowramin.c3
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/bios/shadowrom.c110
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/gsp/ga100.c1
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/r535/alloc.c4
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/r535/bar.c2
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/r535/ctrl.c2
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/r535/rpc.c10
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/gsp/tu102.c69
-rw-r--r--drivers/gpu/drm/panel/panel-edp.c1
-rw-r--r--drivers/gpu/drm/panel/panel-simple.c57
-rw-r--r--drivers/gpu/drm/panthor/panthor_gem.c16
-rw-r--r--drivers/gpu/drm/panthor/panthor_gem.h10
-rw-r--r--drivers/gpu/drm/renesas/rz-du/rzg2l_du_crtc.c9
-rw-r--r--drivers/gpu/drm/renesas/rz-du/rzg2l_du_drv.c20
-rw-r--r--drivers/gpu/drm/renesas/rz-du/rzg2l_du_drv.h14
-rw-r--r--drivers/gpu/drm/renesas/rz-du/rzg2l_du_encoder.c9
-rw-r--r--drivers/gpu/drm/tegra/dc.c120
-rw-r--r--drivers/gpu/drm/tegra/dc.h13
-rw-r--r--drivers/gpu/drm/tegra/dp.c6
-rw-r--r--drivers/gpu/drm/tegra/dp.h12
-rw-r--r--drivers/gpu/drm/tegra/drm.h5
-rw-r--r--drivers/gpu/drm/tegra/fb.c1
-rw-r--r--drivers/gpu/drm/tegra/fbdev.c88
-rw-r--r--drivers/gpu/drm/tegra/gem.c22
-rw-r--r--drivers/gpu/drm/tegra/gr2d.c19
-rw-r--r--drivers/gpu/drm/tegra/gr3d.c19
-rw-r--r--drivers/gpu/drm/tegra/hdmi.c10
-rw-r--r--drivers/gpu/drm/tegra/sor.c37
-rw-r--r--drivers/gpu/drm/tegra/submit.c3
-rw-r--r--drivers/gpu/drm/verisilicon/Makefile3
-rw-r--r--drivers/gpu/drm/verisilicon/vs_crtc.c11
-rw-r--r--drivers/gpu/drm/verisilicon/vs_cursor_plane.c272
-rw-r--r--drivers/gpu/drm/verisilicon/vs_cursor_plane_regs.h44
-rw-r--r--drivers/gpu/drm/verisilicon/vs_hwdb.c4
-rw-r--r--drivers/gpu/drm/verisilicon/vs_hwdb.h5
-rw-r--r--drivers/gpu/drm/verisilicon/vs_plane.h1
-rw-r--r--drivers/gpu/drm/xe/xe_validation.c4
-rw-r--r--drivers/gpu/drm/xe/xe_validation.h2
-rw-r--r--drivers/gpu/drm/xe/xe_vm.c3
-rw-r--r--drivers/gpu/host1x/bus.c66
-rw-r--r--drivers/gpu/host1x/context.c1
-rw-r--r--drivers/gpu/host1x/context.h3
-rw-r--r--drivers/gpu/host1x/context_bus.c1
-rw-r--r--drivers/gpu/host1x/intr.c8
-rw-r--r--drivers/gpu/host1x/job.c10
-rw-r--r--drivers/gpu/host1x/mipi.c6
-rw-r--r--drivers/gpu/host1x/syncpt.c23
-rw-r--r--include/drm/drm_exec.h71
-rw-r--r--include/linux/host1x.h7
-rw-r--r--include/trace/events/host1x.h50
-rw-r--r--include/uapi/drm/tegra_drm.h16
76 files changed, 1380 insertions, 379 deletions
diff --git a/Documentation/devicetree/bindings/display/panel/panel-lvds.yaml b/Documentation/devicetree/bindings/display/panel/panel-lvds.yaml
index b31c67babaa8..b89f86bc0683 100644
--- a/Documentation/devicetree/bindings/display/panel/panel-lvds.yaml
+++ b/Documentation/devicetree/bindings/display/panel/panel-lvds.yaml
@@ -58,6 +58,10 @@ properties:
- hydis,hv070wx2-1e0
# Jenson Display BL-JT60050-01A 7" WSVGA (1024x600) color TFT LCD LVDS panel
- jenson,bl-jt60050-01a
+ # Riverdi RVT101HVLNWC00 10.1" WXGA (1280x800) TFT LCD LVDS panel
+ - riverdi,rvt101hvlnwc00
+ # Riverdi RVT70HSLNWCA0 7.0" WSVGA (1024x600) TFT LCD LVDS panel
+ - riverdi,rvt70hslnwca0
# Samsung LTN070NL01 7.0" WSVGA (1024x600) TFT LCD LVDS panel
- samsung,ltn070nl01
# Samsung LTN101AL03 10.1" WXGA (800x1280) TFT LCD LVDS panel
diff --git a/Documentation/devicetree/bindings/display/panel/panel-simple.yaml b/Documentation/devicetree/bindings/display/panel/panel-simple.yaml
index 44ec7388d851..21453f0d7ce2 100644
--- a/Documentation/devicetree/bindings/display/panel/panel-simple.yaml
+++ b/Documentation/devicetree/bindings/display/panel/panel-simple.yaml
@@ -31,6 +31,8 @@ properties:
# Ampire AM-1280800N3TZQW-T00H 10.1" WQVGA TFT LCD panel
- ampire,am-1280800n3tzqw-t00h
+ # Ampire AM-1280800W8TZQW-T00H 10.1" WXGA TFT LCD panel
+ - ampire,am-1280800w8tzqw-t00h
# Ampire AM-480272H3TMQW-T01H 4.3" WQVGA TFT LCD panel
- ampire,am-480272h3tmqw-t01h
# Ampire AM-800480L1TMQW-T00H 5" WVGA TFT LCD panel
@@ -234,6 +236,8 @@ properties:
- nec,nl12880bc20-05
# NEC LCD Technologies,Ltd. WQVGA TFT LCD panel
- nec,nl4827hc19-05b
+ # NEC LCD Technologies,Ltd. VGA TFT LCD panel
+ - nec,nl6448bc33-70c
# Netron-DY E231732 7.0" WSVGA TFT LCD panel
- netron-dy,e231732
# Newhaven Display International 480 x 272 TFT LCD panel
diff --git a/Documentation/devicetree/bindings/display/renesas,rzg2l-du.yaml b/Documentation/devicetree/bindings/display/renesas,rzg2l-du.yaml
index 2cc66dcef870..7c84a9ecc7a7 100644
--- a/Documentation/devicetree/bindings/display/renesas,rzg2l-du.yaml
+++ b/Documentation/devicetree/bindings/display/renesas,rzg2l-du.yaml
@@ -21,6 +21,7 @@ properties:
- renesas,r9a07g043u-du # RZ/G2UL
- renesas,r9a07g044-du # RZ/G2{L,LC}
- renesas,r9a09g057-du # RZ/V2H(P)
+ - renesas,r9a09g077-du # RZ/T2H
- items:
- enum:
- renesas,r9a07g054-du # RZ/V2L
@@ -28,6 +29,9 @@ properties:
- items:
- const: renesas,r9a09g056-du # RZ/V2N
- const: renesas,r9a09g057-du # RZ/V2H(P) fallback
+ - items:
+ - const: renesas,r9a09g087-du # RZ/N2H
+ - const: renesas,r9a09g077-du # RZ/T2H fallback
reg:
maxItems: 1
@@ -83,7 +87,6 @@ required:
- interrupts
- clocks
- clock-names
- - resets
- power-domains
- ports
- renesas,vsps
@@ -95,13 +98,16 @@ allOf:
properties:
compatible:
contains:
- const: renesas,r9a07g043u-du
+ enum:
+ - renesas,r9a07g043u-du
+ - renesas,r9a09g077-du
then:
properties:
ports:
properties:
port@0:
description: DPI
+ port@1: false
required:
- port@0
@@ -137,6 +143,17 @@ allOf:
required:
- port@0
+ - if:
+ properties:
+ compatible:
+ contains:
+ const: renesas,r9a09g077-du
+ then:
+ properties:
+ resets: false
+ else:
+ required:
+ - resets
examples:
# RZ/G2L DU
diff --git a/arch/arm64/configs/defconfig b/arch/arm64/configs/defconfig
index d905a0777f93..f33193b50e6b 100644
--- a/arch/arm64/configs/defconfig
+++ b/arch/arm64/configs/defconfig
@@ -1411,6 +1411,9 @@ CONFIG_RZ_DMAC=y
CONFIG_TI_K3_UDMA=y
CONFIG_TI_K3_UDMA_GLUE_LAYER=y
CONFIG_STM32_DMA3=m
+CONFIG_DMABUF_HEAPS=y
+CONFIG_DMABUF_HEAPS_SYSTEM=m
+CONFIG_DMABUF_HEAPS_CMA=m
CONFIG_VFIO=y
CONFIG_VFIO_PCI=y
CONFIG_VIRTIO_PCI=y
diff --git a/drivers/accel/amdxdna/amdxdna_ubuf.c b/drivers/accel/amdxdna/amdxdna_ubuf.c
index bb60fb80467e..0e0cd69cd1fb 100644
--- a/drivers/accel/amdxdna/amdxdna_ubuf.c
+++ b/drivers/accel/amdxdna/amdxdna_ubuf.c
@@ -75,26 +75,6 @@ static const struct dma_buf_ops amdxdna_ubuf_dmabuf_ops = {
.release = amdxdna_ubuf_release,
};
-static int readonly_va_entry(struct amdxdna_drm_va_entry *va_ent)
-{
- struct mm_struct *mm = current->mm;
- struct vm_area_struct *vma;
- int ret;
-
- mmap_read_lock(mm);
-
- vma = find_vma(mm, va_ent->vaddr);
- if (!vma ||
- vma->vm_start > va_ent->vaddr ||
- vma->vm_end - va_ent->vaddr < va_ent->len)
- ret = -ENOENT;
- else
- ret = vma->vm_flags & VM_WRITE ? 0 : 1;
-
- mmap_read_unlock(mm);
- return ret;
-}
-
struct dma_buf *amdxdna_get_ubuf(struct drm_device *dev,
u32 num_entries, void __user *va_entries)
{
@@ -104,7 +84,6 @@ struct dma_buf *amdxdna_get_ubuf(struct drm_device *dev,
struct amdxdna_ubuf_priv *ubuf;
u32 npages, start = 0;
struct dma_buf *dbuf;
- bool readonly = true;
int i, ret;
DEFINE_DMA_BUF_EXPORT_INFO(exp_info);
@@ -143,10 +122,6 @@ struct dma_buf *amdxdna_get_ubuf(struct drm_device *dev,
ret = -EINVAL;
goto free_ent;
}
-
- /* Pin pages as writable as long as not all entries are read-only. */
- if (readonly && readonly_va_entry(&va_ent[i]) != 1)
- readonly = false;
}
ubuf->nr_pages = exp_info.size >> PAGE_SHIFT;
@@ -169,7 +144,7 @@ struct dma_buf *amdxdna_get_ubuf(struct drm_device *dev,
npages = va_ent[i].len >> PAGE_SHIFT;
ret = pin_user_pages_fast(va_ent[i].vaddr, npages,
- (readonly ? 0 : FOLL_WRITE) | FOLL_LONGTERM,
+ FOLL_WRITE | FOLL_LONGTERM,
&ubuf->pages[start]);
if (ret >= 0) {
start += ret;
@@ -186,7 +161,7 @@ struct dma_buf *amdxdna_get_ubuf(struct drm_device *dev,
exp_info.ops = &amdxdna_ubuf_dmabuf_ops;
exp_info.priv = ubuf;
- exp_info.flags = (readonly ? O_RDONLY : O_RDWR) | O_CLOEXEC;
+ exp_info.flags = O_RDWR | O_CLOEXEC;
dbuf = dma_buf_export(&exp_info);
if (IS_ERR(dbuf)) {
diff --git a/drivers/accel/ivpu/ivpu_drv.h b/drivers/accel/ivpu/ivpu_drv.h
index c77dde310e81..9eefbbb7ba11 100644
--- a/drivers/accel/ivpu/ivpu_drv.h
+++ b/drivers/accel/ivpu/ivpu_drv.h
@@ -111,7 +111,6 @@ struct ivpu_wa_table {
bool clear_runtime_mem;
bool interrupt_clear_with_0;
bool disable_clock_relinquish;
- bool disable_d0i3_msg;
bool wp0_during_power_up;
bool disable_d0i2;
};
@@ -220,8 +219,6 @@ extern bool ivpu_force_snoop;
#define IVPU_TEST_MODE_FW_TEST BIT(0)
#define IVPU_TEST_MODE_NULL_HW BIT(1)
#define IVPU_TEST_MODE_NULL_SUBMISSION BIT(2)
-#define IVPU_TEST_MODE_D0I3_MSG_DISABLE BIT(4)
-#define IVPU_TEST_MODE_D0I3_MSG_ENABLE BIT(5)
#define IVPU_TEST_MODE_MIP_DISABLE BIT(6)
#define IVPU_TEST_MODE_DISABLE_TIMEOUTS BIT(8)
#define IVPU_TEST_MODE_TURBO_ENABLE BIT(9)
diff --git a/drivers/accel/ivpu/ivpu_fw.c b/drivers/accel/ivpu/ivpu_fw.c
index 107f8ad31050..7db199e04f7c 100644
--- a/drivers/accel/ivpu/ivpu_fw.c
+++ b/drivers/accel/ivpu/ivpu_fw.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
- * Copyright (C) 2020-2025 Intel Corporation
+ * Copyright (C) 2020-2026 Intel Corporation
*/
#include <linux/firmware.h>
@@ -347,22 +347,6 @@ static void ivpu_fw_release(struct ivpu_device *vdev)
release_firmware(vdev->fw->file);
}
-/* Initialize workarounds that depend on FW version */
-static void
-ivpu_fw_init_wa(struct ivpu_device *vdev)
-{
- const struct vpu_firmware_header *fw_hdr = (const void *)vdev->fw->file->data;
-
- if (IVPU_FW_CHECK_API_VER_LT(vdev, fw_hdr, BOOT, 3, 17) ||
- (ivpu_test_mode & IVPU_TEST_MODE_D0I3_MSG_DISABLE))
- vdev->wa.disable_d0i3_msg = true;
-
- /* Force enable the feature for testing purposes */
- if (ivpu_test_mode & IVPU_TEST_MODE_D0I3_MSG_ENABLE)
- vdev->wa.disable_d0i3_msg = false;
-
- IVPU_PRINT_WA(disable_d0i3_msg);
-}
static int ivpu_fw_mem_init(struct ivpu_device *vdev)
{
@@ -480,8 +464,6 @@ int ivpu_fw_init(struct ivpu_device *vdev)
if (ret)
goto err_fw_release;
- ivpu_fw_init_wa(vdev);
-
ret = ivpu_fw_mem_init(vdev);
if (ret)
goto err_fw_release;
@@ -711,8 +693,7 @@ void ivpu_fw_boot_params_setup(struct ivpu_device *vdev, struct vpu_boot_params
if (vdev->fw->sched_mode == VPU_SCHEDULING_MODE_HW)
boot_params->vpu_focus_present_timer_ms = IVPU_FOCUS_PRESENT_TIMER_MS;
boot_params->dvfs_mode = vdev->fw->dvfs_mode;
- if (!IVPU_WA(disable_d0i3_msg))
- boot_params->d0i3_delayed_entry = 1;
+ boot_params->d0i3_delayed_entry = 1;
boot_params->d0i3_residency_time_us = 0;
boot_params->d0i3_entry_vpu_ts = 0;
if (IVPU_WA(disable_d0i2))
diff --git a/drivers/accel/ivpu/ivpu_ipc.c b/drivers/accel/ivpu/ivpu_ipc.c
index f47df092bb0d..9f1ba0c57442 100644
--- a/drivers/accel/ivpu/ivpu_ipc.c
+++ b/drivers/accel/ivpu/ivpu_ipc.c
@@ -97,7 +97,11 @@ ivpu_ipc_tx_prepare(struct ivpu_device *vdev, struct ivpu_ipc_consumer *cons,
memset(tx_buf, 0, sizeof(*tx_buf));
tx_buf->ipc.data_addr = jsm_vpu_addr;
- /* TODO: Set data_size to actual JSM message size, not union of all messages */
+ /*
+ * Firmware expects full JSM message size regardless of the payload size.
+ * Unused fields must be zeroed to allow future extensions of existing
+ * commands without breaking compatibility.
+ */
tx_buf->ipc.data_size = sizeof(*req);
tx_buf->ipc.channel = cons->channel;
tx_buf->ipc.src_node = 0;
diff --git a/drivers/accel/ivpu/ivpu_jsm_msg.c b/drivers/accel/ivpu/ivpu_jsm_msg.c
index 17b42a76aef9..6361fe50596e 100644
--- a/drivers/accel/ivpu/ivpu_jsm_msg.c
+++ b/drivers/accel/ivpu/ivpu_jsm_msg.c
@@ -275,9 +275,6 @@ int ivpu_jsm_pwr_d0i3_enter(struct ivpu_device *vdev)
struct vpu_jsm_msg resp;
int ret;
- if (IVPU_WA(disable_d0i3_msg))
- return 0;
-
req.payload.pwr_d0i3_enter.send_response = 1;
ret = ivpu_ipc_send_receive_internal(vdev, &req, VPU_JSM_MSG_PWR_D0I3_ENTER_DONE, &resp,
diff --git a/drivers/accel/ivpu/vpu_boot_api.h b/drivers/accel/ivpu/vpu_boot_api.h
index a41170bbc6b7..06c092d5e675 100644
--- a/drivers/accel/ivpu/vpu_boot_api.h
+++ b/drivers/accel/ivpu/vpu_boot_api.h
@@ -41,7 +41,7 @@
/**
* API header changed (field names, documentation, formatting) but API itself has not been changed
*/
-#define VPU_BOOT_API_VER_PATCH 4
+#define VPU_BOOT_API_VER_PATCH 5
/**
* Index in the API version table
@@ -320,9 +320,11 @@ struct vpu_boot_params {
u64 dvfs_param;
/**
* D0i3 delayed entry
- * Bit0: Disable CPU state save on D0i2 entry flow.
+ * Bit 0: Disable CPU state save on D0i2 entry flow.
* 0: Every D0i2 entry saves state. Save state IPC message ignored.
* 1: IPC message required to save state on D0i3 entry flow.
+ * NOTE: This parameter is deprecated starting NPU50xx+. Bit 0 is now hardcoded to 1,
+ * meaning CPU state save always requires IPC message on D0i3 entry flow.
*/
u32 d0i3_delayed_entry;
/** Time spent by VPU in D0i3 state */
diff --git a/drivers/dma-buf/dma-heap.c b/drivers/dma-buf/dma-heap.c
index ac5f8685a649..a76bf3f8b071 100644
--- a/drivers/dma-buf/dma-heap.c
+++ b/drivers/dma-buf/dma-heap.c
@@ -53,6 +53,7 @@ bool __read_mostly mem_accounting;
module_param(mem_accounting, bool, 0444);
MODULE_PARM_DESC(mem_accounting,
"Enable cgroup-based memory accounting for dma-buf heap allocations (default=false).");
+EXPORT_SYMBOL_NS_GPL(mem_accounting, "DMA_BUF_HEAP");
static int dma_heap_buffer_alloc(struct dma_heap *heap, size_t len,
u32 fd_flags,
diff --git a/drivers/dma-buf/heaps/Kconfig b/drivers/dma-buf/heaps/Kconfig
index a5eef06c4226..e273fb18feca 100644
--- a/drivers/dma-buf/heaps/Kconfig
+++ b/drivers/dma-buf/heaps/Kconfig
@@ -1,12 +1,12 @@
config DMABUF_HEAPS_SYSTEM
- bool "DMA-BUF System Heap"
+ tristate "DMA-BUF System Heap"
depends on DMABUF_HEAPS
help
Choose this option to enable the system dmabuf heap. The system heap
is backed by pages from the buddy allocator. If in doubt, say Y.
config DMABUF_HEAPS_CMA
- bool "DMA-BUF CMA Heap"
+ tristate "DMA-BUF CMA Heap"
depends on DMABUF_HEAPS && DMA_CMA
help
Choose this option to enable dma-buf CMA heap. This heap is backed
diff --git a/drivers/dma-buf/heaps/cma_heap.c b/drivers/dma-buf/heaps/cma_heap.c
index a359aac45579..3fb4b946c91a 100644
--- a/drivers/dma-buf/heaps/cma_heap.c
+++ b/drivers/dma-buf/heaps/cma_heap.c
@@ -420,3 +420,6 @@ static int __init add_cma_heaps(void)
}
module_init(add_cma_heaps);
MODULE_DESCRIPTION("DMA-BUF CMA Heap");
+MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS("DMA_BUF");
+MODULE_IMPORT_NS("DMA_BUF_HEAP");
diff --git a/drivers/dma-buf/heaps/system_heap.c b/drivers/dma-buf/heaps/system_heap.c
index 03c2b87cb111..c92bdec356fc 100644
--- a/drivers/dma-buf/heaps/system_heap.c
+++ b/drivers/dma-buf/heaps/system_heap.c
@@ -537,3 +537,8 @@ static int __init system_heap_create(void)
return 0;
}
module_init(system_heap_create);
+
+MODULE_DESCRIPTION("DMA-BUF System Heap");
+MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS("DMA_BUF");
+MODULE_IMPORT_NS("DMA_BUF_HEAP");
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gpuvm.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gpuvm.c
index 72a5a29e63f6..d54794e5b18b 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gpuvm.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gpuvm.c
@@ -2999,7 +2999,7 @@ int amdgpu_amdkfd_gpuvm_restore_process_bos(void *info, struct dma_fence __rcu *
/* Validate PDs, PTs and evicted DMABuf imports last. Otherwise BO
* validations above would invalidate DMABuf imports again.
*/
- ret = process_validate_vms(process_info, &exec.ticket);
+ ret = process_validate_vms(process_info, drm_exec_ticket(&exec));
if (ret) {
pr_debug("Validating VMs failed, ret: %d\n", ret);
goto validate_map_fail;
@@ -3040,7 +3040,7 @@ int amdgpu_amdkfd_gpuvm_restore_process_bos(void *info, struct dma_fence __rcu *
goto validate_map_fail;
}
- ret = amdgpu_vm_handle_moved(adev, peer_vm, &exec.ticket);
+ ret = amdgpu_vm_handle_moved(adev, peer_vm, drm_exec_ticket(&exec));
if (ret) {
dev_dbg(adev->dev,
"Memory eviction: handle moved failed, pid %8d. Try again.\n",
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c
index 32af8cce3df8..6ada57abce9d 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c
@@ -850,7 +850,6 @@ static int amdgpu_cs_parser_bos(struct amdgpu_cs_parser *p,
struct amdgpu_vm *vm = &fpriv->vm;
struct amdgpu_bo_list_entry *e;
struct drm_gem_object *obj;
- unsigned long index;
unsigned int i;
int r;
@@ -961,7 +960,7 @@ static int amdgpu_cs_parser_bos(struct amdgpu_cs_parser *p,
goto out_free_user_pages;
}
- drm_exec_for_each_locked_object(&p->exec, index, obj) {
+ drm_exec_for_each_locked_object(&p->exec, obj) {
r = amdgpu_cs_bo_validate(p, gem_to_amdgpu_bo(obj));
if (unlikely(r))
goto out_free_user_pages;
@@ -1158,7 +1157,7 @@ static int amdgpu_cs_vm_handling(struct amdgpu_cs_parser *p)
return r;
}
- r = amdgpu_vm_handle_moved(adev, vm, &p->exec.ticket);
+ r = amdgpu_vm_handle_moved(adev, vm, drm_exec_ticket(&p->exec));
if (r)
return r;
@@ -1201,7 +1200,6 @@ static int amdgpu_cs_sync_rings(struct amdgpu_cs_parser *p)
struct drm_gpu_scheduler *sched;
struct drm_gem_object *obj;
struct dma_fence *fence;
- unsigned long index;
unsigned int i;
int r;
@@ -1212,7 +1210,7 @@ static int amdgpu_cs_sync_rings(struct amdgpu_cs_parser *p)
return r;
}
- drm_exec_for_each_locked_object(&p->exec, index, obj) {
+ drm_exec_for_each_locked_object(&p->exec, obj) {
struct amdgpu_bo *bo = gem_to_amdgpu_bo(obj);
struct dma_resv *resv = bo->tbo.base.resv;
@@ -1281,7 +1279,6 @@ static int amdgpu_cs_submit(struct amdgpu_cs_parser *p,
struct amdgpu_job *leader = p->gang_leader;
struct amdgpu_bo_list_entry *e;
struct drm_gem_object *gobj;
- unsigned long index;
unsigned int i;
uint64_t seq;
int r;
@@ -1331,7 +1328,7 @@ static int amdgpu_cs_submit(struct amdgpu_cs_parser *p,
}
p->fence = dma_fence_get(&leader->base.s_fence->finished);
- drm_exec_for_each_locked_object(&p->exec, index, gobj) {
+ drm_exec_for_each_locked_object(&p->exec, gobj) {
ttm_bo_move_to_lru_tail_unlocked(&gem_to_amdgpu_bo(gobj)->tbo);
@@ -1362,7 +1359,7 @@ static int amdgpu_cs_submit(struct amdgpu_cs_parser *p,
cs->out.handle = seq;
leader->uf_sequence = seq;
- amdgpu_vm_bo_trace_cs(&fpriv->vm, &p->exec.ticket);
+ amdgpu_vm_bo_trace_cs(&fpriv->vm, drm_exec_ticket(&p->exec));
for (i = 0; i < p->gang_size; ++i) {
amdgpu_job_free_resources(p->jobs[i]);
trace_amdgpu_cs_ioctl(p->jobs[i]);
@@ -1797,7 +1794,7 @@ int amdgpu_cs_find_mapping(struct amdgpu_cs_parser *parser,
*map = mapping;
/* Double check that the BO is reserved by this CS */
- if (dma_resv_locking_ctx((*bo)->tbo.base.resv) != &parser->exec.ticket)
+ if (dma_resv_locking_ctx((*bo)->tbo.base.resv) != drm_exec_ticket(&parser->exec))
return -EINVAL;
/* Make sure VRAM is allocated contigiously */
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_eviction_fence.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_eviction_fence.c
index 4c5e38dea4c2..f6b7522c3c82 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_eviction_fence.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_eviction_fence.c
@@ -121,7 +121,6 @@ int amdgpu_evf_mgr_rearm(struct amdgpu_eviction_fence_mgr *evf_mgr,
{
struct amdgpu_eviction_fence *ev_fence;
struct drm_gem_object *obj;
- unsigned long index;
/* Create and initialize a new eviction fence */
ev_fence = kzalloc_obj(*ev_fence);
@@ -140,7 +139,7 @@ int amdgpu_evf_mgr_rearm(struct amdgpu_eviction_fence_mgr *evf_mgr,
evf_mgr->ev_fence = &ev_fence->base;
/* And add it to all existing BOs */
- drm_exec_for_each_locked_object(exec, index, obj) {
+ drm_exec_for_each_locked_object(exec, obj) {
struct amdgpu_bo *bo = gem_to_amdgpu_bo(obj);
amdgpu_evf_mgr_attach_fence(evf_mgr, bo);
diff --git a/drivers/gpu/drm/bridge/Kconfig b/drivers/gpu/drm/bridge/Kconfig
index f81b566c82a1..4a57d49b4c6d 100644
--- a/drivers/gpu/drm/bridge/Kconfig
+++ b/drivers/gpu/drm/bridge/Kconfig
@@ -205,6 +205,8 @@ config DRM_LONTIUM_LT8713SX
config DRM_ITE_IT66121
tristate "ITE IT66121 HDMI bridge"
depends on OF
+ select DRM_DISPLAY_HDMI_STATE_HELPER
+ select DRM_DISPLAY_HELPER
select DRM_KMS_HELPER
select REGMAP_I2C
help
diff --git a/drivers/gpu/drm/bridge/ite-it66121.c b/drivers/gpu/drm/bridge/ite-it66121.c
index fd14639c0f9a..a2f48db369e3 100644
--- a/drivers/gpu/drm/bridge/ite-it66121.c
+++ b/drivers/gpu/drm/bridge/ite-it66121.c
@@ -20,6 +20,8 @@
#include <linux/pinctrl/consumer.h>
#include <linux/regulator/consumer.h>
+#include <drm/display/drm_hdmi_helper.h>
+#include <drm/display/drm_hdmi_state_helper.h>
#include <drm/drm_atomic_helper.h>
#include <drm/drm_bridge.h>
#include <drm/drm_edid.h>
@@ -161,6 +163,14 @@
#define IT66121_PKT_GEN_CTRL_ON BIT(0)
#define IT66121_PKT_GEN_CTRL_RPT BIT(1)
+#define IT66121_PKT_NULL_CTRL_REG 0xC9
+#define IT66121_PKT_NULL_CTRL_ON BIT(0)
+#define IT66121_PKT_NULL_CTRL_RPT BIT(1)
+
+/* Null packet data registers (used for HDMI Vendor Specific InfoFrame) */
+#define IT66121_PKT_NULL_HB(n) (0x138 + (n))
+#define IT66121_PKT_NULL_PB(n) (0x13B + (n))
+
#define IT66121_AVIINFO_DB1_REG 0x158
#define IT66121_AVIINFO_DB2_REG 0x159
#define IT66121_AVIINFO_DB3_REG 0x15A
@@ -180,8 +190,16 @@
#define IT66121_AVI_INFO_PKT_ON BIT(0)
#define IT66121_AVI_INFO_PKT_RPT BIT(1)
+#define IT66121_AUD_INFO_PKT_REG 0xCE
+#define IT66121_AUD_INFO_PKT_ON BIT(0)
+#define IT66121_AUD_INFO_PKT_RPT BIT(1)
+
+#define IT66121_AUD_INFO_DB1_REG 0x168
+#define IT66121_AUD_INFO_CSUM_REG 0x16D
+
#define IT66121_HDMI_MODE_REG 0xC0
#define IT66121_HDMI_MODE_HDMI BIT(0)
+#define IT66121_HDMI_MODE_DVI 0
#define IT66121_SYS_STATUS_REG 0x0E
#define IT66121_SYS_STATUS_ACTIVE_IRQ BIT(7)
@@ -298,13 +316,11 @@ struct it66121_chip_info {
struct it66121_ctx {
struct regmap *regmap;
struct drm_bridge bridge;
- struct drm_connector *connector;
struct device *dev;
struct gpio_desc *gpio_reset;
struct i2c_client *client;
u32 bus_width;
struct mutex lock; /* Protects fields below and device registers */
- struct hdmi_avi_infoframe hdmi_avi_infoframe;
struct {
u8 ch_enable;
u8 fs;
@@ -652,6 +668,60 @@ static int it66121_bridge_attach(struct drm_bridge *bridge,
return 0;
}
+static void it66121_set_mode(struct it66121_ctx *ctx,
+ struct drm_connector *connector,
+ struct drm_atomic_commit *state)
+{
+ const struct drm_connector_state *conn_state;
+ const struct drm_crtc_state *crtc_state;
+ const struct drm_display_mode *mode;
+ struct drm_crtc *crtc;
+
+ conn_state = drm_atomic_get_new_connector_state(state, connector);
+ if (WARN_ON(!conn_state))
+ return;
+
+ crtc = conn_state->crtc;
+ if (WARN_ON(!crtc))
+ return;
+
+ crtc_state = drm_atomic_get_new_crtc_state(state, crtc);
+ if (WARN_ON(!crtc_state))
+ return;
+
+ mode = &crtc_state->adjusted_mode;
+
+ mutex_lock(&ctx->lock);
+
+ /* Set TX mode to HDMI or DVI */
+ if (regmap_write(ctx->regmap, IT66121_HDMI_MODE_REG,
+ connector->display_info.is_hdmi ?
+ IT66121_HDMI_MODE_HDMI : IT66121_HDMI_MODE_DVI))
+ goto unlock;
+
+ if ((ctx->id == ID_IT66121 || ctx->id == ID_IT66122) &&
+ regmap_write_bits(ctx->regmap, IT66121_CLK_BANK_REG,
+ IT66121_CLK_BANK_PWROFF_TXCLK,
+ IT66121_CLK_BANK_PWROFF_TXCLK)) {
+ goto unlock;
+ }
+
+ if (it66121_configure_input(ctx))
+ goto unlock;
+
+ if (it66121_configure_afe(ctx, mode))
+ goto unlock;
+
+ if ((ctx->id == ID_IT66121 || ctx->id == ID_IT66122) &&
+ regmap_write_bits(ctx->regmap, IT66121_CLK_BANK_REG,
+ IT66121_CLK_BANK_PWROFF_TXCLK, 0)) {
+ goto unlock;
+ }
+
+unlock:
+ mutex_unlock(&ctx->lock);
+}
+
static int it66121_set_mute(struct it66121_ctx *ctx, bool mute)
{
int ret;
@@ -724,9 +794,15 @@ static void it66121_bridge_enable(struct drm_bridge *bridge,
struct drm_atomic_commit *state)
{
struct it66121_ctx *ctx = container_of(bridge, struct it66121_ctx, bridge);
+ struct drm_connector *connector;
+
+ connector = drm_atomic_get_new_connector_for_encoder(state, bridge->encoder);
+ if (WARN_ON(!connector))
+ return;
- ctx->connector = drm_atomic_get_new_connector_for_encoder(state, bridge->encoder);
+ drm_atomic_helper_connector_hdmi_update_infoframes(connector, state);
+ it66121_set_mode(ctx, connector, state);
it66121_set_mute(ctx, false);
}
@@ -736,8 +812,6 @@ static void it66121_bridge_disable(struct drm_bridge *bridge,
struct it66121_ctx *ctx = container_of(bridge, struct it66121_ctx, bridge);
it66121_set_mute(ctx, true);
-
- ctx->connector = NULL;
}
static int it66121_bridge_check(struct drm_bridge *bridge,
@@ -758,118 +832,156 @@ static int it66121_bridge_check(struct drm_bridge *bridge,
return 0;
}
-static
-void it66121_bridge_mode_set(struct drm_bridge *bridge,
- const struct drm_display_mode *mode,
- const struct drm_display_mode *adjusted_mode)
+static enum drm_connector_status
+it66121_bridge_detect(struct drm_bridge *bridge, struct drm_connector *connector)
{
- u8 buf[HDMI_INFOFRAME_SIZE(AVI)];
struct it66121_ctx *ctx = container_of(bridge, struct it66121_ctx, bridge);
- int ret;
-
- mutex_lock(&ctx->lock);
- ret = drm_hdmi_avi_infoframe_from_display_mode(&ctx->hdmi_avi_infoframe, ctx->connector,
- adjusted_mode);
- if (ret) {
- DRM_ERROR("Failed to setup AVI infoframe: %d\n", ret);
- goto unlock;
- }
+ return it66121_is_hpd_detect(ctx) ? connector_status_connected
+ : connector_status_disconnected;
+}
- ret = hdmi_avi_infoframe_pack(&ctx->hdmi_avi_infoframe, buf, sizeof(buf));
- if (ret < 0) {
- DRM_ERROR("Failed to pack infoframe: %d\n", ret);
- goto unlock;
- }
+static void it66121_bridge_hpd_enable(struct drm_bridge *bridge)
+{
+ struct it66121_ctx *ctx = container_of(bridge, struct it66121_ctx, bridge);
+ int ret;
- /* Write new AVI infoframe packet */
- ret = regmap_bulk_write(ctx->regmap, IT66121_AVIINFO_DB1_REG,
- &buf[HDMI_INFOFRAME_HEADER_SIZE],
- HDMI_AVI_INFOFRAME_SIZE);
+ ret = regmap_write_bits(ctx->regmap, IT66121_INT_MASK1_REG, IT66121_INT_MASK1_HPD, 0);
if (ret)
- goto unlock;
+ dev_err(ctx->dev, "failed to enable HPD IRQ\n");
+}
- if (regmap_write(ctx->regmap, IT66121_AVIINFO_CSUM_REG, buf[3]))
- goto unlock;
+static void it66121_bridge_hpd_disable(struct drm_bridge *bridge)
+{
+ struct it66121_ctx *ctx = container_of(bridge, struct it66121_ctx, bridge);
+ int ret;
- /* Enable AVI infoframe */
- if (regmap_write(ctx->regmap, IT66121_AVI_INFO_PKT_REG,
- IT66121_AVI_INFO_PKT_ON | IT66121_AVI_INFO_PKT_RPT))
- goto unlock;
+ ret = regmap_write_bits(ctx->regmap, IT66121_INT_MASK1_REG,
+ IT66121_INT_MASK1_HPD, IT66121_INT_MASK1_HPD);
+ if (ret)
+ dev_err(ctx->dev, "failed to disable HPD IRQ\n");
+}
- /* Set TX mode to HDMI */
- if (regmap_write(ctx->regmap, IT66121_HDMI_MODE_REG, IT66121_HDMI_MODE_HDMI))
- goto unlock;
+static enum drm_mode_status
+it66121_bridge_hdmi_tmds_char_rate_valid(const struct drm_bridge *bridge,
+ const struct drm_display_mode *mode,
+ unsigned long long tmds_rate)
+{
+ const struct it66121_ctx *ctx =
+ container_of(bridge, const struct it66121_ctx, bridge);
+ unsigned long long max_rate;
- if ((ctx->id == ID_IT66121 || ctx->id == ID_IT66122) &&
- regmap_write_bits(ctx->regmap, IT66121_CLK_BANK_REG,
- IT66121_CLK_BANK_PWROFF_TXCLK,
- IT66121_CLK_BANK_PWROFF_TXCLK)) {
- goto unlock;
- }
+ max_rate = (ctx->bus_width == 12) ? 74250000ULL : 148500000ULL;
- if (it66121_configure_input(ctx))
- goto unlock;
+ if (tmds_rate > max_rate)
+ return MODE_CLOCK_HIGH;
- if (it66121_configure_afe(ctx, adjusted_mode))
- goto unlock;
+ if (tmds_rate < HDMI_TMDS_CHAR_RATE_MIN_HZ)
+ return MODE_CLOCK_LOW;
- if ((ctx->id == ID_IT66121 || ctx->id == ID_IT66122) &&
- regmap_write_bits(ctx->regmap, IT66121_CLK_BANK_REG,
- IT66121_CLK_BANK_PWROFF_TXCLK, 0)) {
- goto unlock;
- }
+ return MODE_OK;
+}
-unlock:
- mutex_unlock(&ctx->lock);
+static int it66121_bridge_hdmi_clear_avi_infoframe(struct drm_bridge *bridge)
+{
+ struct it66121_ctx *ctx = container_of(bridge, struct it66121_ctx, bridge);
+
+ /* Clear both IT66121_AVI_INFO_PKT_ON and IT66121_AVI_INFO_PKT_RPT */
+ return regmap_write(ctx->regmap, IT66121_AVI_INFO_PKT_REG, 0);
}
-static enum drm_mode_status it66121_bridge_mode_valid(struct drm_bridge *bridge,
- const struct drm_display_info *info,
- const struct drm_display_mode *mode)
+static int it66121_bridge_hdmi_write_avi_infoframe(struct drm_bridge *bridge,
+ const u8 *buffer, size_t len)
{
struct it66121_ctx *ctx = container_of(bridge, struct it66121_ctx, bridge);
- unsigned long max_clock;
+ int ret;
- max_clock = (ctx->bus_width == 12) ? 74250 : 148500;
+ mutex_lock(&ctx->lock);
- if (mode->clock > max_clock)
- return MODE_CLOCK_HIGH;
+ /* Write new AVI infoframe packet */
+ ret = regmap_bulk_write(ctx->regmap, IT66121_AVIINFO_DB1_REG,
+ &buffer[HDMI_INFOFRAME_HEADER_SIZE],
+ HDMI_AVI_INFOFRAME_SIZE);
+ if (ret)
+ goto unlock;
- if (mode->clock < 25000)
- return MODE_CLOCK_LOW;
+ ret = regmap_write(ctx->regmap, IT66121_AVIINFO_CSUM_REG, buffer[3]);
+ if (ret)
+ goto unlock;
- return MODE_OK;
+ /* Enable AVI infoframe */
+ ret = regmap_write(ctx->regmap, IT66121_AVI_INFO_PKT_REG,
+ IT66121_AVI_INFO_PKT_ON | IT66121_AVI_INFO_PKT_RPT);
+
+unlock:
+ mutex_unlock(&ctx->lock);
+ return ret;
}
-static enum drm_connector_status
-it66121_bridge_detect(struct drm_bridge *bridge, struct drm_connector *connector)
+static int it66121_bridge_hdmi_clear_hdmi_infoframe(struct drm_bridge *bridge)
{
struct it66121_ctx *ctx = container_of(bridge, struct it66121_ctx, bridge);
- return it66121_is_hpd_detect(ctx) ? connector_status_connected
- : connector_status_disconnected;
+ /* Clear both IT66121_PKT_NULL_CTRL_ON and IT66121_PKT_NULL_CTRL_RPT */
+ return regmap_write(ctx->regmap, IT66121_PKT_NULL_CTRL_REG, 0);
}
-static void it66121_bridge_hpd_enable(struct drm_bridge *bridge)
+static int it66121_bridge_hdmi_write_hdmi_infoframe(struct drm_bridge *bridge,
+ const u8 *buffer, size_t len)
{
struct it66121_ctx *ctx = container_of(bridge, struct it66121_ctx, bridge);
int ret;
- ret = regmap_write_bits(ctx->regmap, IT66121_INT_MASK1_REG, IT66121_INT_MASK1_HPD, 0);
+ mutex_lock(&ctx->lock);
+
+ /* Write new HDMI Vendor Specific Infoframe packet */
+ ret = regmap_bulk_write(ctx->regmap, IT66121_PKT_NULL_HB(0), buffer, len);
if (ret)
- dev_err(ctx->dev, "failed to enable HPD IRQ\n");
+ goto unlock;
+
+ /* Enable HDMI Vendor Specific Infoframe */
+ ret = regmap_write(ctx->regmap, IT66121_PKT_NULL_CTRL_REG,
+ IT66121_PKT_NULL_CTRL_ON | IT66121_PKT_NULL_CTRL_RPT);
+
+unlock:
+ mutex_unlock(&ctx->lock);
+ return ret;
}
-static void it66121_bridge_hpd_disable(struct drm_bridge *bridge)
+static int it66121_bridge_hdmi_clear_audio_infoframe(struct drm_bridge *bridge)
+{
+ struct it66121_ctx *ctx = container_of(bridge, struct it66121_ctx, bridge);
+
+ /* Clear both IT66121_AUD_INFO_PKT_ON and IT66121_AUD_INFO_PKT_RPT */
+ return regmap_write(ctx->regmap, IT66121_AUD_INFO_PKT_REG, 0);
+}
+
+static int it66121_bridge_hdmi_write_audio_infoframe(struct drm_bridge *bridge,
+ const u8 *buffer, size_t len)
{
struct it66121_ctx *ctx = container_of(bridge, struct it66121_ctx, bridge);
int ret;
- ret = regmap_write_bits(ctx->regmap, IT66121_INT_MASK1_REG,
- IT66121_INT_MASK1_HPD, IT66121_INT_MASK1_HPD);
+ mutex_lock(&ctx->lock);
+
+ /* Write new Audio infoframe packet */
+ ret = regmap_bulk_write(ctx->regmap, IT66121_AUD_INFO_DB1_REG,
+ &buffer[HDMI_INFOFRAME_HEADER_SIZE],
+ min_t(size_t, len - HDMI_INFOFRAME_HEADER_SIZE, 5));
if (ret)
- dev_err(ctx->dev, "failed to disable HPD IRQ\n");
+ goto unlock;
+
+ ret = regmap_write(ctx->regmap, IT66121_AUD_INFO_CSUM_REG, buffer[3]);
+ if (ret)
+ goto unlock;
+
+ /* Enable Audio infoframe */
+ ret = regmap_write(ctx->regmap, IT66121_AUD_INFO_PKT_REG,
+ IT66121_AUD_INFO_PKT_ON | IT66121_AUD_INFO_PKT_RPT);
+
+unlock:
+ mutex_unlock(&ctx->lock);
+ return ret;
}
static const struct drm_edid *it66121_bridge_edid_read(struct drm_bridge *bridge,
@@ -1359,6 +1471,10 @@ static int it66121_hdmi_audio_prepare(struct drm_bridge *bridge,
out:
mutex_unlock(&ctx->lock);
+ if (!ret)
+ ret = drm_atomic_helper_connector_hdmi_update_audio_infoframe(connector,
+ &params->cea);
+
return ret;
}
@@ -1384,6 +1500,8 @@ static void it66121_hdmi_audio_shutdown(struct drm_bridge *bridge,
int ret;
struct it66121_ctx *ctx = container_of(bridge, struct it66121_ctx, bridge);
+ drm_atomic_helper_connector_hdmi_clear_audio_infoframe(connector);
+
mutex_lock(&ctx->lock);
ret = it661221_audio_output_enable(ctx, false);
if (ret)
@@ -1432,12 +1550,17 @@ static const struct drm_bridge_funcs it66121_bridge_funcs = {
.atomic_enable = it66121_bridge_enable,
.atomic_disable = it66121_bridge_disable,
.atomic_check = it66121_bridge_check,
- .mode_set = it66121_bridge_mode_set,
- .mode_valid = it66121_bridge_mode_valid,
.detect = it66121_bridge_detect,
.edid_read = it66121_bridge_edid_read,
.hpd_enable = it66121_bridge_hpd_enable,
.hpd_disable = it66121_bridge_hpd_disable,
+ .hdmi_tmds_char_rate_valid = it66121_bridge_hdmi_tmds_char_rate_valid,
+ .hdmi_clear_avi_infoframe = it66121_bridge_hdmi_clear_avi_infoframe,
+ .hdmi_write_avi_infoframe = it66121_bridge_hdmi_write_avi_infoframe,
+ .hdmi_clear_hdmi_infoframe = it66121_bridge_hdmi_clear_hdmi_infoframe,
+ .hdmi_write_hdmi_infoframe = it66121_bridge_hdmi_write_hdmi_infoframe,
+ .hdmi_clear_audio_infoframe = it66121_bridge_hdmi_clear_audio_infoframe,
+ .hdmi_write_audio_infoframe = it66121_bridge_hdmi_write_audio_infoframe,
.hdmi_audio_startup = it66121_hdmi_audio_startup,
.hdmi_audio_prepare = it66121_hdmi_audio_prepare,
.hdmi_audio_shutdown = it66121_hdmi_audio_shutdown,
@@ -1543,7 +1666,10 @@ static int it66121_probe(struct i2c_client *client)
ctx->bridge.of_node = dev->of_node;
ctx->bridge.type = DRM_MODE_CONNECTOR_HDMIA;
- ctx->bridge.ops = DRM_BRIDGE_OP_DETECT | DRM_BRIDGE_OP_EDID;
+ ctx->bridge.ops = DRM_BRIDGE_OP_DETECT | DRM_BRIDGE_OP_EDID |
+ DRM_BRIDGE_OP_HDMI;
+ ctx->bridge.vendor = "ITE";
+ ctx->bridge.product = "IT66121";
if (client->irq > 0) {
ctx->bridge.ops |= DRM_BRIDGE_OP_HPD;
diff --git a/drivers/gpu/drm/display/drm_dp_helper.c b/drivers/gpu/drm/display/drm_dp_helper.c
index a6067471dd56..9c31e14cc413 100644
--- a/drivers/gpu/drm/display/drm_dp_helper.c
+++ b/drivers/gpu/drm/display/drm_dp_helper.c
@@ -2546,6 +2546,8 @@ static const struct dpcd_quirk dpcd_quirk_list[] = {
{ OUI(0x00, 0x00, 0x00), DEVICE_ID('C', 'H', '7', '5', '1', '1'), false, BIT(DP_DPCD_QUIRK_NO_SINK_COUNT) },
/* Synaptics DP1.4 MST hubs can support DSC without virtual DPCD */
{ OUI(0x90, 0xCC, 0x24), DEVICE_ID_ANY, true, BIT(DP_DPCD_QUIRK_DSC_WITHOUT_VIRTUAL_DPCD) },
+ /* Realtek DP1.4 MST hubs can support DSC without virtual DPCD */
+ { OUI(0x00, 0xe0, 0x4c), DEVICE_ID('D', 'p', '1', '.', '4', 0), true, BIT(DP_DPCD_QUIRK_DSC_WITHOUT_VIRTUAL_DPCD) },
/* Synaptics DP1.4 MST hubs require DSC for some modes on which it applies HBLANK expansion. */
{ OUI(0x90, 0xCC, 0x24), DEVICE_ID_ANY, true, BIT(DP_DPCD_QUIRK_HBLANK_EXPANSION_REQUIRES_DSC) },
/* MediaTek panels (at least in U3224KBA) require DSC for modes with a short HBLANK on UHBR links. */
diff --git a/drivers/gpu/drm/drm_exec.c b/drivers/gpu/drm/drm_exec.c
index 8d0601400182..7988f5e7d56a 100644
--- a/drivers/gpu/drm/drm_exec.c
+++ b/drivers/gpu/drm/drm_exec.c
@@ -24,7 +24,6 @@
*
* struct drm_gem_object *obj;
* struct drm_exec exec;
- * unsigned long index;
* int ret;
*
* drm_exec_init(&exec, DRM_EXEC_INTERRUPTIBLE_WAIT);
@@ -40,7 +39,7 @@
* goto error;
* }
*
- * drm_exec_for_each_locked_object(&exec, index, obj) {
+ * drm_exec_for_each_locked_object(&exec, obj) {
* dma_resv_add_fence(obj->resv, fence, DMA_RESV_USAGE_READ);
* ...
* }
@@ -49,16 +48,12 @@
* See struct dma_exec for more details.
*/
-/* Dummy value used to initially enter the retry loop */
-#define DRM_EXEC_DUMMY ((void *)~0)
-
/* Unlock all objects and drop references */
static void drm_exec_unlock_all(struct drm_exec *exec)
{
struct drm_gem_object *obj;
- unsigned long index;
- drm_exec_for_each_locked_object_reverse(exec, index, obj) {
+ drm_exec_for_each_locked_object_reverse(exec, obj) {
dma_resv_unlock(obj->resv);
drm_gem_object_put(obj);
}
diff --git a/drivers/gpu/drm/drm_gpuvm.c b/drivers/gpu/drm/drm_gpuvm.c
index f56719e9f435..078cbc0235a4 100644
--- a/drivers/gpu/drm/drm_gpuvm.c
+++ b/drivers/gpu/drm/drm_gpuvm.c
@@ -1553,9 +1553,8 @@ drm_gpuvm_resv_add_fence(struct drm_gpuvm *gpuvm,
enum dma_resv_usage extobj_usage)
{
struct drm_gem_object *obj;
- unsigned long index;
- drm_exec_for_each_locked_object(exec, index, obj) {
+ drm_exec_for_each_locked_object(exec, obj) {
dma_resv_assert_held(obj->resv);
dma_resv_add_fence(obj->resv, fence,
drm_gpuvm_is_extobj(gpuvm, obj) ?
diff --git a/drivers/gpu/drm/drm_panel.c b/drivers/gpu/drm/drm_panel.c
index 04f4a31ed27a..2c5649e433df 100644
--- a/drivers/gpu/drm/drm_panel.c
+++ b/drivers/gpu/drm/drm_panel.c
@@ -108,6 +108,7 @@ static void drm_panel_add_release(void *data)
/**
* devm_drm_panel_add - add a panel to the global registry using devres
+ * @dev: device to which the panel is attached
* @panel: panel to add
*
* Add a panel to the global registry so that it can be looked
diff --git a/drivers/gpu/drm/nouveau/include/nvkm/subdev/gsp.h b/drivers/gpu/drm/nouveau/include/nvkm/subdev/gsp.h
index 64fed208e4cf..d771134fa410 100644
--- a/drivers/gpu/drm/nouveau/include/nvkm/subdev/gsp.h
+++ b/drivers/gpu/drm/nouveau/include/nvkm/subdev/gsp.h
@@ -293,7 +293,7 @@ nvkm_gsp_rpc_rd(struct nvkm_gsp *gsp, u32 fn, u32 argc)
{
void *argv = nvkm_gsp_rpc_get(gsp, fn, argc);
- if (IS_ERR_OR_NULL(argv))
+ if (IS_ERR(argv))
return argv;
return nvkm_gsp_rpc_push(gsp, argv, NVKM_GSP_RPC_REPLY_RECV, argc);
@@ -373,7 +373,7 @@ nvkm_gsp_rm_alloc_get(struct nvkm_gsp_object *parent, u32 handle, u32 oclass, u3
object->handle = handle;
argv = gsp->rm->api->alloc->get(object, oclass, argc);
- if (IS_ERR_OR_NULL(argv)) {
+ if (IS_ERR(argv)) {
object->client = NULL;
return argv;
}
@@ -415,8 +415,8 @@ nvkm_gsp_rm_alloc(struct nvkm_gsp_object *parent, u32 handle, u32 oclass, u32 ar
{
void *argv = nvkm_gsp_rm_alloc_get(parent, handle, oclass, argc, object);
- if (IS_ERR_OR_NULL(argv))
- return argv ? PTR_ERR(argv) : -EIO;
+ if (IS_ERR(argv))
+ return PTR_ERR(argv);
return nvkm_gsp_rm_alloc_wr(object, argv);
}
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/device/base.c b/drivers/gpu/drm/nouveau/nvkm/engine/device/base.c
index b101e14f841e..ea62dc97f118 100644
--- a/drivers/gpu/drm/nouveau/nvkm/engine/device/base.c
+++ b/drivers/gpu/drm/nouveau/nvkm/engine/device/base.c
@@ -2530,6 +2530,7 @@ nv170_chipset = {
.vfn = { 0x00000001, ga100_vfn_new },
.ce = { 0x000003ff, ga100_ce_new },
.fifo = { 0x00000001, ga100_fifo_new },
+ .sec2 = { 0x00000001, tu102_sec2_new },
};
static const struct nvkm_device_chip
@@ -3341,6 +3342,7 @@ nvkm_device_ctor(const struct nvkm_device_func *func,
case 0x166: device->chip = &nv166_chipset; break;
case 0x167: device->chip = &nv167_chipset; break;
case 0x168: device->chip = &nv168_chipset; break;
+ case 0x170: device->chip = &nv170_chipset; break;
case 0x172: device->chip = &nv172_chipset; break;
case 0x173: device->chip = &nv173_chipset; break;
case 0x174: device->chip = &nv174_chipset; break;
@@ -3360,14 +3362,6 @@ nvkm_device_ctor(const struct nvkm_device_func *func,
case 0x1b6: device->chip = &nv1b6_chipset; break;
case 0x1b7: device->chip = &nv1b7_chipset; break;
default:
- if (nvkm_boolopt(device->cfgopt, "NvEnableUnsupportedChipsets", false)) {
- switch (device->chipset) {
- case 0x170: device->chip = &nv170_chipset; break;
- default:
- break;
- }
- }
-
if (!device->chip) {
nvdev_error(device, "unknown chipset (%08x)\n", boot0);
ret = -ENODEV;
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/bios/shadowramin.c b/drivers/gpu/drm/nouveau/nvkm/subdev/bios/shadowramin.c
index d5411d176e3a..0d9e6cdd6119 100644
--- a/drivers/gpu/drm/nouveau/nvkm/subdev/bios/shadowramin.c
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/bios/shadowramin.c
@@ -65,13 +65,14 @@ pramin_init(struct nvkm_bios *bios, const char *name)
/* we can't get the bios image pointer without PDISP */
if (device->card_type >= GA100)
- addr = device->chipset == 0x170; /*XXX: find the fuse reg for this */
+ addr = nvkm_rd32(device, 0x820c04);
else
if (device->card_type >= GM100)
addr = nvkm_rd32(device, 0x021c04);
else
if (device->card_type >= NV_C0)
addr = nvkm_rd32(device, 0x022500);
+
if (addr & 0x00000001) {
nvkm_debug(subdev, "... display disabled\n");
return ERR_PTR(-ENODEV);
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/bios/shadowrom.c b/drivers/gpu/drm/nouveau/nvkm/subdev/bios/shadowrom.c
index 39144ceb117b..9e171b1bad73 100644
--- a/drivers/gpu/drm/nouveau/nvkm/subdev/bios/shadowrom.c
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/bios/shadowrom.c
@@ -24,34 +24,126 @@
#include <subdev/pci.h>
+#define NV_PBUS_IFR_FMT_FIXED0_SIGNATURE_VALUE 0x4947564E /* "NVGI" */
+#define NV_ROM_DIRECTORY_IDENTIFIER 0x44524652 /* "RFRD" */
+
+struct priv {
+ struct nvkm_device *device;
+ u32 pci_rom_offset;
+};
+
static u32
nvbios_prom_read(void *data, u32 offset, u32 length, struct nvkm_bios *bios)
{
- struct nvkm_device *device = data;
+ struct priv *priv = data;
+ struct nvkm_device *device = priv->device;
u32 i;
- if (offset + length <= 0x00100000) {
- for (i = offset; i < offset + length; i += 4)
- *(u32 *)&bios->data[i] = nvkm_rd32(device, 0x300000 + i);
- return length;
- }
- return 0;
+
+ /* Make sure we don't try to read past the end of data[] */
+ if (offset + length > bios->size)
+ return 0;
+
+ /* Make sure the read falls within the 1MB PROM window */
+ if (offset + priv->pci_rom_offset + length > 0x00100000)
+ return 0;
+
+ for (i = offset; i < offset + length; i += 4)
+ *(u32 *)&bios->data[i] = nvkm_rd32(device, 0x300000 + priv->pci_rom_offset + i);
+ return length;
}
static void
nvbios_prom_fini(void *data)
{
- struct nvkm_device *device = data;
+ struct priv *priv = data;
+ struct nvkm_device *device = priv->device;
+
nvkm_pci_rom_shadow(device->pci, true);
+
+ kfree(data);
}
static void *
nvbios_prom_init(struct nvkm_bios *bios, const char *name)
{
struct nvkm_device *device = bios->subdev.device;
+ struct priv *priv;
+ u32 fixed0;
+
+ /* There is no PROM on NV4x iGPUs */
if (device->card_type == NV_40 && device->chipset >= 0x4c)
return ERR_PTR(-ENODEV);
+
+ priv = kzalloc_obj(*priv);
+ if (!priv)
+ return ERR_PTR(-ENOMEM);
+
+ /* Disable the PCI ROM shadow so that we can read PROM. */
nvkm_pci_rom_shadow(device->pci, false);
- return device;
+
+ /*
+ * Check for an IFR header. If present, parse it to find the actual PCI ROM header.
+ *
+ * The IFR header is documented in Documentation/gpu/nova/core/vbios.rst
+ */
+ fixed0 = nvkm_rd32(device, 0x300000);
+ if (fixed0 == NV_PBUS_IFR_FMT_FIXED0_SIGNATURE_VALUE) {
+ u32 fixed1 = nvkm_rd32(device, 0x300004);
+ u8 version = (fixed1 >> 8) & 0xff;
+ u32 fixed2, data_size, offset, signature;
+
+ switch (version) {
+ case 1:
+ case 2:
+ data_size = (fixed1 >> 16) & 0x7fff;
+ priv->pci_rom_offset = nvkm_rd32(device, 0x300000 + data_size + 4);
+ break;
+ case 3:
+ fixed2 = nvkm_rd32(device, 0x300008);
+ data_size = fixed2 & 0x000fffff;
+
+ /* ROM directory offset */
+ offset = nvkm_rd32(device, 0x300000 + data_size) + 4096;
+
+ signature = nvkm_rd32(device, 0x300000 + offset);
+ if (signature != NV_ROM_DIRECTORY_IDENTIFIER) {
+ nvkm_error(&bios->subdev, "could not find IFR ROM directory\n");
+ goto fail;
+ }
+
+ priv->pci_rom_offset = nvkm_rd32(device, 0x300000 + offset + 8);
+
+ break;
+ default:
+ nvkm_error(&bios->subdev, "unsupported IFR header version %u\n",
+ version);
+ goto fail;
+ }
+
+ /* Double-check that the offset is valid */
+ if (priv->pci_rom_offset >= 0x00100000) {
+ nvkm_error(&bios->subdev,
+ "PCI ROM offset of 0x%x is too large\n", priv->pci_rom_offset);
+ goto fail;
+ }
+
+ /* If there is an IFR header, there must also be a PCI ROM header. */
+ signature = nvkm_rd32(device, 0x300000 + priv->pci_rom_offset) & 0xffff;
+ if (signature != 0xaa55) {
+ nvkm_error(&bios->subdev,
+ "could not find PCI ROM signature at offset 0x%x\n",
+ priv->pci_rom_offset);
+ goto fail;
+ }
+ }
+
+ priv->device = device;
+ return priv;
+
+fail:
+ nvkm_pci_rom_shadow(device->pci, true);
+ kfree(priv);
+ return ERR_PTR(-ENODEV);
}
const struct nvbios_source
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/gsp/ga100.c b/drivers/gpu/drm/nouveau/nvkm/subdev/gsp/ga100.c
index 27a13aeccd3c..d5126fe39d27 100644
--- a/drivers/gpu/drm/nouveau/nvkm/subdev/gsp/ga100.c
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/gsp/ga100.c
@@ -63,7 +63,6 @@ static struct nvkm_gsp_fwif
ga100_gsps[] = {
{ 1, tu102_gsp_load, &ga100_gsp, &r570_rm_tu102, "570.144" },
{ 0, tu102_gsp_load, &ga100_gsp, &r535_rm_tu102, "535.113.01" },
- { -1, gv100_gsp_nofw, &gv100_gsp },
{}
};
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/r535/alloc.c b/drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/r535/alloc.c
index 46e3a29f2ad7..27f275d2e151 100644
--- a/drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/r535/alloc.c
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/r535/alloc.c
@@ -35,7 +35,7 @@ r535_gsp_rpc_rm_free(struct nvkm_gsp_object *object)
client->object.handle, object->handle);
rpc = nvkm_gsp_rpc_get(gsp, NV_VGPU_MSG_FUNCTION_FREE, sizeof(*rpc));
- if (WARN_ON(IS_ERR_OR_NULL(rpc)))
+ if (WARN_ON(IS_ERR(rpc)))
return -EIO;
rpc->params.hRoot = client->object.handle;
@@ -60,7 +60,7 @@ r535_gsp_rpc_rm_alloc_push(struct nvkm_gsp_object *object, void *params)
void *ret = NULL;
rpc = nvkm_gsp_rpc_push(gsp, rpc, NVKM_GSP_RPC_REPLY_RECV, sizeof(*rpc));
- if (IS_ERR_OR_NULL(rpc))
+ if (IS_ERR(rpc))
return rpc;
if (rpc->status) {
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/r535/bar.c b/drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/r535/bar.c
index fae08ac3b18c..9cd68f8622d3 100644
--- a/drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/r535/bar.c
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/r535/bar.c
@@ -55,7 +55,7 @@ r535_bar_bar2_update_pde(struct nvkm_gsp *gsp, u8 page_shift, u64 pdbe)
rpc_update_bar_pde_v15_00 *rpc;
rpc = nvkm_gsp_rpc_get(gsp, NV_VGPU_MSG_FUNCTION_UPDATE_BAR_PDE, sizeof(*rpc));
- if (WARN_ON(IS_ERR_OR_NULL(rpc)))
+ if (WARN_ON(IS_ERR(rpc)))
return -EIO;
rpc->info.barType = NV_RPC_UPDATE_PDE_BAR_2;
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/r535/ctrl.c b/drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/r535/ctrl.c
index 70b9ee911c5e..aa0ebd3dfb17 100644
--- a/drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/r535/ctrl.c
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/r535/ctrl.c
@@ -42,7 +42,7 @@ r535_gsp_rpc_rm_ctrl_push(struct nvkm_gsp_object *object, void **params, u32 rep
int ret = 0;
rpc = nvkm_gsp_rpc_push(gsp, rpc, NVKM_GSP_RPC_REPLY_RECV, repc);
- if (IS_ERR_OR_NULL(rpc)) {
+ if (IS_ERR(rpc)) {
*params = NULL;
return PTR_ERR(rpc);
}
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/r535/rpc.c b/drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/r535/rpc.c
index 3ca3de8f4340..0c9657cb2dd7 100644
--- a/drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/r535/rpc.c
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/r535/rpc.c
@@ -324,7 +324,7 @@ r535_gsp_msgq_recv(struct nvkm_gsp *gsp, u32 gsp_rpc_len, int *retries)
u32 size;
rpc = r535_gsp_msgq_peek(gsp, sizeof(*rpc), info.retries);
- if (IS_ERR_OR_NULL(rpc)) {
+ if (IS_ERR(rpc)) {
kvfree(buf);
return rpc;
}
@@ -333,7 +333,7 @@ r535_gsp_msgq_recv(struct nvkm_gsp *gsp, u32 gsp_rpc_len, int *retries)
info.continuation = true;
rpc = r535_gsp_msgq_recv_one_elem(gsp, &info);
- if (IS_ERR_OR_NULL(rpc)) {
+ if (IS_ERR(rpc)) {
kvfree(buf);
return rpc;
}
@@ -459,11 +459,11 @@ r535_gsp_msg_recv(struct nvkm_gsp *gsp, int fn, u32 gsp_rpc_len)
retry:
rpc = r535_gsp_msgq_peek(gsp, sizeof(*rpc), &retries);
- if (IS_ERR_OR_NULL(rpc))
+ if (IS_ERR(rpc))
return rpc;
rpc = r535_gsp_msgq_recv(gsp, gsp_rpc_len, &retries);
- if (IS_ERR_OR_NULL(rpc))
+ if (IS_ERR(rpc))
return rpc;
if (rpc->rpc_result) {
@@ -561,7 +561,7 @@ r535_gsp_rpc_handle_reply(struct nvkm_gsp *gsp, u32 fn,
break;
case NVKM_GSP_RPC_REPLY_RECV:
reply = r535_gsp_msg_recv(gsp, fn, gsp_rpc_len);
- if (!IS_ERR_OR_NULL(reply))
+ if (!IS_ERR(reply))
repv = reply->data;
else
repv = reply;
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/gsp/tu102.c b/drivers/gpu/drm/nouveau/nvkm/subdev/gsp/tu102.c
index 19cb269e7a26..11a37963bd4e 100644
--- a/drivers/gpu/drm/nouveau/nvkm/subdev/gsp/tu102.c
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/gsp/tu102.c
@@ -246,7 +246,7 @@ tu102_gsp_wpr_meta_init(struct nvkm_gsp *gsp)
meta->bootBinOffset = gsp->fb.wpr2.boot.addr;
meta->frtsOffset = gsp->fb.wpr2.frts.addr;
meta->frtsSize = gsp->fb.wpr2.frts.size;
- meta->gspFwWprEnd = ALIGN_DOWN(gsp->fb.bios.vga_workspace.addr, 0x20000);
+ meta->gspFwWprEnd = ALIGN_DOWN(gsp->fb.bios.addr, 0x20000);
meta->fbSize = gsp->fb.size;
meta->vgaWorkspaceOffset = gsp->fb.bios.vga_workspace.addr;
meta->vgaWorkspaceSize = gsp->fb.bios.vga_workspace.size;
@@ -291,6 +291,42 @@ tu102_gsp_vga_workspace_addr(struct nvkm_gsp *gsp, u64 fb_size)
return addr;
}
+/*
+ * Read the MMU_LOCK range programmed by VBIOS.
+ *
+ * The row remapper reserves a small amount of DRAM at the end of FB for
+ * spare rows used to repair memory errors. The register used to report
+ * FB size rounds to 1GB boundaries, and VBIOS rounds up rather than
+ * down -- reporting a larger size than is actually usable. To
+ * compensate, VBIOS programs MMU_LOCK to fence off the unusable region
+ * at the top. We must read this and keep WPR below it.
+ *
+ * Returns the low address of the locked region, or 0 if not set.
+ */
+static u64
+ga100_gsp_mmu_lock_lo(struct nvkm_gsp *gsp)
+{
+ struct nvkm_device *device = gsp->subdev.device;
+ u32 lo, hi;
+ u64 addr_lo, addr_hi;
+
+ /* NV_PFB_PRI_MMU_LOCK_CFG_PRIV_LEVEL_MASK */
+ if (!(nvkm_rd32(device, 0x1fa7c8) & 0x00000001))
+ return 0;
+
+ lo = nvkm_rd32(device, 0x1fa82c); /* NV_PFB_PRI_MMU_LOCK_ADDR_LO */
+ hi = nvkm_rd32(device, 0x1fa830); /* NV_PFB_PRI_MMU_LOCK_ADDR_HI */
+
+ addr_lo = (u64)(lo >> 4) << 12;
+ addr_hi = (u64)(hi >> 4) << 12;
+
+ if (addr_hi < addr_lo)
+ return 0;
+
+ nvkm_debug(&gsp->subdev, "MMU_LOCK range: 0x%llx - 0x%llx\n", addr_lo, addr_hi);
+ return addr_lo;
+}
+
int
tu102_gsp_oneinit(struct nvkm_gsp *gsp)
{
@@ -304,6 +340,19 @@ tu102_gsp_oneinit(struct nvkm_gsp *gsp)
gsp->fb.bios.addr = gsp->fb.bios.vga_workspace.addr;
gsp->fb.bios.size = gsp->fb.bios.vga_workspace.size;
+ /*
+ * On GA100, VBIOS may lock the top of FB via MMU_LOCK to reserve
+ * space for the row remapper. Mirror OpenRM's kgspCalculateFbLayout
+ * by clamping bios.addr to min(mmuLockLo, vgaWorkspace) so that the
+ * entire WPR2 layout and gspFwWprEnd stay within usable FB.
+ */
+ if (device->chipset == 0x170) {
+ u64 lock_lo = ga100_gsp_mmu_lock_lo(gsp);
+
+ if (lock_lo)
+ gsp->fb.bios.addr = min(gsp->fb.bios.addr, lock_lo);
+ }
+
ret = gsp->func->booter.ctor(gsp, "booter-load", gsp->fws.booter.load,
&device->sec2->falcon, &gsp->booter.load);
if (ret)
@@ -318,8 +367,13 @@ tu102_gsp_oneinit(struct nvkm_gsp *gsp)
if (ret)
return ret;
- /* Calculate FB layout. */
- gsp->fb.wpr2.frts.size = 0x100000;
+ /*
+ * Calculate FB layout. FRTS is a memory region created by running the FWSEC-FRTS
+ * command, which writes power management data into WPR2. On GA100, the booter
+ * firmware handles WPR2 setup directly and FRTS data is not needed, so no FRTS
+ * region is reserved.
+ */
+ gsp->fb.wpr2.frts.size = device->chipset == 0x170 ? 0 : 0x100000;
gsp->fb.wpr2.frts.addr = ALIGN_DOWN(gsp->fb.bios.addr, 0x20000) - gsp->fb.wpr2.frts.size;
gsp->fb.wpr2.boot.size = gsp->boot.fw.size;
@@ -343,9 +397,12 @@ tu102_gsp_oneinit(struct nvkm_gsp *gsp)
if (ret)
return ret;
- ret = nvkm_gsp_fwsec_frts(gsp);
- if (WARN_ON(ret))
- return ret;
+ /* Only boot FWSEC-FRTS if it actually exists */
+ if (gsp->fb.wpr2.frts.size) {
+ ret = nvkm_gsp_fwsec_frts(gsp);
+ if (WARN_ON(ret))
+ return ret;
+ }
/* Reset GSP into RISC-V mode. */
ret = gsp->func->reset(gsp);
diff --git a/drivers/gpu/drm/panel/panel-edp.c b/drivers/gpu/drm/panel/panel-edp.c
index cb7d6c43466e..aa27d6cd932e 100644
--- a/drivers/gpu/drm/panel/panel-edp.c
+++ b/drivers/gpu/drm/panel/panel-edp.c
@@ -2134,6 +2134,7 @@ static const struct edp_panel_entry edp_panels[] = {
EDP_PANEL_ENTRY('L', 'G', 'D', 0x0567, &delay_200_500_e200_d200, "Unknown"),
EDP_PANEL_ENTRY('L', 'G', 'D', 0x05af, &delay_200_500_e200_d200, "Unknown"),
EDP_PANEL_ENTRY('L', 'G', 'D', 0x05f1, &delay_200_500_e200_d200, "Unknown"),
+ EDP_PANEL_ENTRY('L', 'G', 'D', 0x06b2, &delay_200_500_e200_d200, "LP129WT232166"),
EDP_PANEL_ENTRY('L', 'G', 'D', 0x0778, &delay_200_500_e200_d200, "134WT1"),
EDP_PANEL_ENTRY('L', 'G', 'D', 0x07fe, &delay_200_500_e200_d200, "LP116WHA-SPB1"),
diff --git a/drivers/gpu/drm/panel/panel-simple.c b/drivers/gpu/drm/panel/panel-simple.c
index 2fbb9dff0a34..c09bf3db5e78 100644
--- a/drivers/gpu/drm/panel/panel-simple.c
+++ b/drivers/gpu/drm/panel/panel-simple.c
@@ -820,6 +820,31 @@ static const struct panel_desc ampire_am_1280800n3tzqw_t00h = {
.connector_type = DRM_MODE_CONNECTOR_LVDS,
};
+static const struct drm_display_mode ampire_am_1280800w8tzqw_t00h_mode = {
+ .clock = 72400,
+ .hdisplay = 1280,
+ .hsync_start = 1280 + 40,
+ .hsync_end = 1280 + 40 + 80,
+ .htotal = 1280 + 40 + 80 + 40,
+ .vdisplay = 800,
+ .vsync_start = 800 + 10,
+ .vsync_end = 800 + 10 + 18,
+ .vtotal = 800 + 10 + 18 + 10,
+};
+
+static const struct panel_desc ampire_am_1280800w8tzqw_t00h = {
+ .modes = &ampire_am_1280800w8tzqw_t00h_mode,
+ .num_modes = 1,
+ .bpc = 8,
+ .size = {
+ .width = 217,
+ .height = 136,
+ },
+ .bus_flags = DRM_BUS_FLAG_DE_HIGH,
+ .bus_format = MEDIA_BUS_FMT_RGB888_1X7X4_SPWG,
+ .connector_type = DRM_MODE_CONNECTOR_LVDS,
+};
+
static const struct drm_display_mode ampire_am_480272h3tmqw_t01h_mode = {
.clock = 9000,
.hdisplay = 480,
@@ -3692,6 +3717,32 @@ static const struct panel_desc nec_nl4827hc19_05b = {
.bus_flags = DRM_BUS_FLAG_PIXDATA_DRIVE_POSEDGE,
};
+static const struct drm_display_mode nec_nl6448bc33_70c_mode = {
+ .clock = 25175,
+ .hdisplay = 640,
+ .hsync_start = 640 + 16,
+ .hsync_end = 640 + 16 + 48,
+ .htotal = 640 + 16 + 48 + 96,
+ .vdisplay = 480,
+ .vsync_start = 480 + 2,
+ .vsync_end = 480 + 2 + 31,
+ .vtotal = 480 + 2 + 31 + 31,
+ .flags = DRM_MODE_FLAG_PVSYNC | DRM_MODE_FLAG_PHSYNC,
+};
+
+static const struct panel_desc nec_nl6448bc33_70c = {
+ .modes = &nec_nl6448bc33_70c_mode,
+ .num_modes = 1,
+ .bpc = 6,
+ .size = {
+ .width = 211,
+ .height = 158,
+ },
+ .bus_format = MEDIA_BUS_FMT_RGB666_1X7X3_SPWG,
+ .bus_flags = DRM_BUS_FLAG_DE_HIGH,
+ .connector_type = DRM_MODE_CONNECTOR_LVDS,
+};
+
static const struct drm_display_mode netron_dy_e231732_mode = {
.clock = 66000,
.hdisplay = 1024,
@@ -5589,6 +5640,9 @@ static const struct of_device_id platform_of_match[] = {
.compatible = "ampire,am-1280800n3tzqw-t00h",
.data = &ampire_am_1280800n3tzqw_t00h,
}, {
+ .compatible = "ampire,am-1280800w8tzqw-t00h",
+ .data = &ampire_am_1280800w8tzqw_t00h,
+ }, {
.compatible = "ampire,am-480272h3tmqw-t01h",
.data = &ampire_am_480272h3tmqw_t01h,
}, {
@@ -5919,6 +5973,9 @@ static const struct of_device_id platform_of_match[] = {
.compatible = "nec,nl4827hc19-05b",
.data = &nec_nl4827hc19_05b,
}, {
+ .compatible = "nec,nl6448bc33-70c",
+ .data = &nec_nl6448bc33_70c,
+ }, {
.compatible = "netron-dy,e231732",
.data = &netron_dy_e231732,
}, {
diff --git a/drivers/gpu/drm/panthor/panthor_gem.c b/drivers/gpu/drm/panthor/panthor_gem.c
index abe0c5bb1bca..a1e2eb1ca7bb 100644
--- a/drivers/gpu/drm/panthor/panthor_gem.c
+++ b/drivers/gpu/drm/panthor/panthor_gem.c
@@ -687,6 +687,8 @@ static void panthor_gem_evict_locked(struct panthor_gem_object *bo)
if (drm_WARN_ON_ONCE(bo->base.dev, !bo->backing.pages))
return;
+ atomic_add_unless(&bo->reclaimed_count, 1, INT_MAX);
+
panthor_gem_dev_map_cleanup_locked(bo);
panthor_gem_backing_cleanup_locked(bo);
panthor_gem_update_reclaim_state_locked(bo, NULL);
@@ -1590,6 +1592,7 @@ static void panthor_gem_debugfs_print_flag_names(struct seq_file *m)
static const char * const gem_state_flags_names[] = {
[PANTHOR_DEBUGFS_GEM_STATE_IMPORTED_BIT] = "imported",
[PANTHOR_DEBUGFS_GEM_STATE_EXPORTED_BIT] = "exported",
+ [PANTHOR_DEBUGFS_GEM_STATE_EVICTED_BIT] = "evicted",
};
static const char * const gem_usage_flags_names[] = {
@@ -1620,6 +1623,7 @@ static void panthor_gem_debugfs_bo_print(struct panthor_gem_object *bo,
{
enum panthor_gem_reclaim_state reclaim_state = bo->reclaim_state;
unsigned int refcount = kref_read(&bo->base.refcount);
+ int reclaimed_count = atomic_read(&bo->reclaimed_count);
char creator_info[32] = {};
size_t resident_size;
u32 gem_usage_flags = bo->debugfs.flags;
@@ -1633,16 +1637,20 @@ static void panthor_gem_debugfs_bo_print(struct panthor_gem_object *bo,
snprintf(creator_info, sizeof(creator_info),
"%s/%d", bo->debugfs.creator.process_name, bo->debugfs.creator.tgid);
- seq_printf(m, "%-32s%-16d%-16d%-16zd%-16zd0x%-16lx",
+ seq_printf(m, "%-32s%-16d%-11d%-11d%-16zd%-16zd0x%-16lx",
creator_info,
bo->base.name,
refcount,
+ reclaimed_count,
bo->base.size,
resident_size,
drm_vma_node_start(&bo->base.vma_node));
if (drm_gem_is_imported(&bo->base))
gem_state_flags |= PANTHOR_DEBUGFS_GEM_STATE_FLAG_IMPORTED;
+ else if (!resident_size && reclaimed_count)
+ gem_state_flags |= PANTHOR_DEBUGFS_GEM_STATE_FLAG_EVICTED;
+
if (bo->base.dma_buf)
gem_state_flags |= PANTHOR_DEBUGFS_GEM_STATE_FLAG_EXPORTED;
@@ -1666,8 +1674,8 @@ static void panthor_gem_debugfs_print_bos(struct panthor_device *ptdev,
panthor_gem_debugfs_print_flag_names(m);
- seq_puts(m, "created-by global-name refcount size resident-size file-offset state usage label\n");
- seq_puts(m, "----------------------------------------------------------------------------------------------------------------------------------------------\n");
+ seq_puts(m, "created-by global-name refcount evictions size resident-size file-offset state usage label\n");
+ seq_puts(m, "----------------------------------------------------------------------------------------------------------------------------------------------------\n");
scoped_guard(mutex, &ptdev->gems.lock) {
list_for_each_entry(bo, &ptdev->gems.node, debugfs.node) {
@@ -1675,7 +1683,7 @@ static void panthor_gem_debugfs_print_bos(struct panthor_device *ptdev,
}
}
- seq_puts(m, "==============================================================================================================================================\n");
+ seq_puts(m, "====================================================================================================================================================\n");
seq_printf(m, "Total size: %zd, Total resident: %zd, Total reclaimable: %zd\n",
totals.size, totals.resident, totals.reclaimable);
}
diff --git a/drivers/gpu/drm/panthor/panthor_gem.h b/drivers/gpu/drm/panthor/panthor_gem.h
index ae0491d0b121..56d63137b4eb 100644
--- a/drivers/gpu/drm/panthor/panthor_gem.h
+++ b/drivers/gpu/drm/panthor/panthor_gem.h
@@ -19,12 +19,16 @@ struct panthor_vm;
enum panthor_debugfs_gem_state_flags {
PANTHOR_DEBUGFS_GEM_STATE_IMPORTED_BIT = 0,
PANTHOR_DEBUGFS_GEM_STATE_EXPORTED_BIT = 1,
+ PANTHOR_DEBUGFS_GEM_STATE_EVICTED_BIT = 2,
/** @PANTHOR_DEBUGFS_GEM_STATE_FLAG_IMPORTED: GEM BO is PRIME imported. */
PANTHOR_DEBUGFS_GEM_STATE_FLAG_IMPORTED = BIT(PANTHOR_DEBUGFS_GEM_STATE_IMPORTED_BIT),
/** @PANTHOR_DEBUGFS_GEM_STATE_FLAG_EXPORTED: GEM BO is PRIME exported. */
PANTHOR_DEBUGFS_GEM_STATE_FLAG_EXPORTED = BIT(PANTHOR_DEBUGFS_GEM_STATE_EXPORTED_BIT),
+
+ /** @PANTHOR_DEBUGFS_GEM_STATE_FLAG_EVICTED: GEM BO is evicted to swap. */
+ PANTHOR_DEBUGFS_GEM_STATE_FLAG_EVICTED = BIT(PANTHOR_DEBUGFS_GEM_STATE_EVICTED_BIT),
};
enum panthor_debugfs_gem_usage_flags {
@@ -173,6 +177,12 @@ struct panthor_gem_object {
enum panthor_gem_reclaim_state reclaim_state;
/**
+ * @reclaimed_count: How many times object has been evicted to swap.
+ * The count saturates at %INT_MAX and will never wrap around to 0.
+ */
+ atomic_t reclaimed_count;
+
+ /**
* @exclusive_vm_root_gem: Root GEM of the exclusive VM this GEM object
* is attached to.
*
diff --git a/drivers/gpu/drm/renesas/rz-du/rzg2l_du_crtc.c b/drivers/gpu/drm/renesas/rz-du/rzg2l_du_crtc.c
index 26b95153ce88..d0f01aa642a7 100644
--- a/drivers/gpu/drm/renesas/rz-du/rzg2l_du_crtc.c
+++ b/drivers/gpu/drm/renesas/rz-du/rzg2l_du_crtc.c
@@ -28,6 +28,7 @@
#include "rzg2l_du_vsp.h"
#define DU_MCR0 0x00
+#define DU_MCR0_DPI_EN BIT(0)
#define DU_MCR0_DI_EN BIT(8)
#define DU_DITR0 0x10
@@ -217,8 +218,12 @@ static void rzg2l_du_crtc_put(struct rzg2l_du_crtc *rcrtc)
static void rzg2l_du_start_stop(struct rzg2l_du_crtc *rcrtc, bool start)
{
struct rzg2l_du_device *rcdu = rcrtc->dev;
+ u32 val = DU_MCR0_DI_EN;
- writel(start ? DU_MCR0_DI_EN : 0, rcdu->mmio + DU_MCR0);
+ if (start && rzg2l_du_has(rcdu, RZG2L_DU_FEATURE_DPIO_OE))
+ val |= DU_MCR0_DPI_EN;
+
+ writel(start ? val : 0, rcdu->mmio + DU_MCR0);
}
static void rzg2l_du_crtc_start(struct rzg2l_du_crtc *rcrtc)
@@ -380,7 +385,7 @@ int rzg2l_du_crtc_create(struct rzg2l_du_device *rcdu)
struct drm_plane *primary;
int ret;
- rcrtc->rstc = devm_reset_control_get_shared(rcdu->dev, NULL);
+ rcrtc->rstc = devm_reset_control_get_optional_shared(rcdu->dev, NULL);
if (IS_ERR(rcrtc->rstc)) {
dev_err(rcdu->dev, "can't get cpg reset\n");
return PTR_ERR(rcrtc->rstc);
diff --git a/drivers/gpu/drm/renesas/rz-du/rzg2l_du_drv.c b/drivers/gpu/drm/renesas/rz-du/rzg2l_du_drv.c
index 0fef33a5a089..3d13f61d3c97 100644
--- a/drivers/gpu/drm/renesas/rz-du/rzg2l_du_drv.c
+++ b/drivers/gpu/drm/renesas/rz-du/rzg2l_du_drv.c
@@ -35,6 +35,8 @@ static const struct rzg2l_du_device_info rzg2l_du_r9a07g043u_info = {
.port = 0,
},
},
+ .mode_clock_min = 20875,
+ .mode_clock_max = 83500,
};
static const struct rzg2l_du_device_info rzg2l_du_r9a07g044_info = {
@@ -48,7 +50,9 @@ static const struct rzg2l_du_device_info rzg2l_du_r9a07g044_info = {
.possible_outputs = BIT(0),
.port = 1,
}
- }
+ },
+ .mode_clock_min = 20875,
+ .mode_clock_max = 83500,
};
static const struct rzg2l_du_device_info rzg2l_du_r9a09g057_info = {
@@ -61,10 +65,24 @@ static const struct rzg2l_du_device_info rzg2l_du_r9a09g057_info = {
},
};
+static const struct rzg2l_du_device_info rzg2l_du_r9a09g077_info = {
+ .channels_mask = BIT(0),
+ .routes = {
+ [RZG2L_DU_OUTPUT_DPAD0] = {
+ .possible_outputs = BIT(0),
+ .port = 0,
+ },
+ },
+ .features = RZG2L_DU_FEATURE_DPIO_OE,
+ .mode_clock_min = 5000,
+ .mode_clock_max = 100000,
+};
+
static const struct of_device_id rzg2l_du_of_table[] = {
{ .compatible = "renesas,r9a07g043u-du", .data = &rzg2l_du_r9a07g043u_info },
{ .compatible = "renesas,r9a07g044-du", .data = &rzg2l_du_r9a07g044_info },
{ .compatible = "renesas,r9a09g057-du", .data = &rzg2l_du_r9a09g057_info },
+ { .compatible = "renesas,r9a09g077-du", .data = &rzg2l_du_r9a09g077_info },
{ /* sentinel */ }
};
diff --git a/drivers/gpu/drm/renesas/rz-du/rzg2l_du_drv.h b/drivers/gpu/drm/renesas/rz-du/rzg2l_du_drv.h
index 58806c2a8f2b..baf076d69cda 100644
--- a/drivers/gpu/drm/renesas/rz-du/rzg2l_du_drv.h
+++ b/drivers/gpu/drm/renesas/rz-du/rzg2l_du_drv.h
@@ -20,6 +20,8 @@
struct device;
struct drm_property;
+#define RZG2L_DU_FEATURE_DPIO_OE BIT(0) /* Has DPIO output enable control */
+
enum rzg2l_du_output {
RZG2L_DU_OUTPUT_DSI0,
RZG2L_DU_OUTPUT_DPAD0,
@@ -44,10 +46,16 @@ struct rzg2l_du_output_routing {
* struct rzg2l_du_device_info - DU model-specific information
* @channels_mask: bit mask of available DU channels
* @routes: array of CRTC to output routes, indexed by output (RZG2L_DU_OUTPUT_*)
+ * @mode_clock_min: minimum pixel clock in kHz
+ * @mode_clock_max: maximum pixel clock in kHz
+ * @features: device features (RZG2L_DU_FEATURE_*)
*/
struct rzg2l_du_device_info {
unsigned int channels_mask;
struct rzg2l_du_output_routing routes[RZG2L_DU_OUTPUT_MAX];
+ u32 mode_clock_min;
+ u32 mode_clock_max;
+ unsigned int features;
};
#define RZG2L_DU_MAX_CRTCS 1
@@ -73,6 +81,12 @@ static inline struct rzg2l_du_device *to_rzg2l_du_device(struct drm_device *dev)
return container_of(dev, struct rzg2l_du_device, ddev);
}
+static inline bool rzg2l_du_has(struct rzg2l_du_device *rcdu,
+ unsigned int feature)
+{
+ return rcdu->info->features & feature;
+}
+
const char *rzg2l_du_output_name(enum rzg2l_du_output output);
#endif /* __RZG2L_DU_DRV_H__ */
diff --git a/drivers/gpu/drm/renesas/rz-du/rzg2l_du_encoder.c b/drivers/gpu/drm/renesas/rz-du/rzg2l_du_encoder.c
index 0e567b57a408..f50d166b764f 100644
--- a/drivers/gpu/drm/renesas/rz-du/rzg2l_du_encoder.c
+++ b/drivers/gpu/drm/renesas/rz-du/rzg2l_du_encoder.c
@@ -50,8 +50,15 @@ rzg2l_du_encoder_mode_valid(struct drm_encoder *encoder,
const struct drm_display_mode *mode)
{
struct rzg2l_du_encoder *renc = to_rzg2l_encoder(encoder);
+ struct rzg2l_du_device *rcdu = to_rzg2l_du_device(renc->base.dev);
+ const struct rzg2l_du_device_info *info = rcdu->info;
- if (renc->output == RZG2L_DU_OUTPUT_DPAD0 && mode->clock > 83500)
+ if (renc->output != RZG2L_DU_OUTPUT_DPAD0)
+ return MODE_OK;
+
+ if (mode->clock < info->mode_clock_min)
+ return MODE_CLOCK_LOW;
+ if (mode->clock > info->mode_clock_max)
return MODE_CLOCK_HIGH;
return MODE_OK;
diff --git a/drivers/gpu/drm/tegra/dc.c b/drivers/gpu/drm/tegra/dc.c
index de683644d658..3842ca34a16e 100644
--- a/drivers/gpu/drm/tegra/dc.c
+++ b/drivers/gpu/drm/tegra/dc.c
@@ -36,6 +36,103 @@
#include "hub.h"
#include "plane.h"
+static const u16 default_srgb_lut[] = {
+ 0x6000, 0x60CE, 0x619D, 0x626C, 0x632D, 0x63D4, 0x6469, 0x64F0, 0x656B, 0x65DF, 0x664A,
+ 0x66B0, 0x6711, 0x676D, 0x67C4, 0x6819, 0x686A, 0x68B8, 0x6904, 0x694D, 0x6994, 0x69D8,
+ 0x6A1B, 0x6A5D, 0x6A9C, 0x6ADA, 0x6B17, 0x6B52, 0x6B8C, 0x6BC5, 0x6BFD, 0x6C33, 0x6C69,
+ 0x6C9E, 0x6CD1, 0x6D04, 0x6D36, 0x6D67, 0x6D98, 0x6DC7, 0x6DF6, 0x6E25, 0x6E52, 0x6E7F,
+ 0x6EAC, 0x6ED7, 0x6F03, 0x6F2D, 0x6F58, 0x6F81, 0x6FAA, 0x6FD3, 0x6FFB, 0x7023, 0x704B,
+ 0x7071, 0x7098, 0x70BE, 0x70E4, 0x7109, 0x712E, 0x7153, 0x7177, 0x719B, 0x71BF, 0x71E2,
+ 0x7205, 0x7227, 0x724A, 0x726C, 0x728E, 0x72AF, 0x72D0, 0x72F1, 0x7312, 0x7333, 0x7353,
+ 0x7373, 0x7392, 0x73B2, 0x73D1, 0x73F0, 0x740F, 0x742D, 0x744C, 0x746A, 0x7488, 0x74A6,
+ 0x74C3, 0x74E0, 0x74FE, 0x751B, 0x7537, 0x7554, 0x7570, 0x758D, 0x75A9, 0x75C4, 0x75E0,
+ 0x75FC, 0x7617, 0x7632, 0x764D, 0x7668, 0x7683, 0x769E, 0x76B8, 0x76D3, 0x76ED, 0x7707,
+ 0x7721, 0x773B, 0x7754, 0x776E, 0x7787, 0x77A0, 0x77B9, 0x77D2, 0x77EB, 0x7804, 0x781D,
+ 0x7835, 0x784E, 0x7866, 0x787E, 0x7896, 0x78AE, 0x78C6, 0x78DD, 0x78F5, 0x790D, 0x7924,
+ 0x793B, 0x7952, 0x796A, 0x7981, 0x7997, 0x79AE, 0x79C5, 0x79DB, 0x79F2, 0x7A08, 0x7A1F,
+ 0x7A35, 0x7A4B, 0x7A61, 0x7A77, 0x7A8D, 0x7AA3, 0x7AB8, 0x7ACE, 0x7AE3, 0x7AF9, 0x7B0E,
+ 0x7B24, 0x7B39, 0x7B4E, 0x7B63, 0x7B78, 0x7B8D, 0x7BA2, 0x7BB6, 0x7BCB, 0x7BE0, 0x7BF4,
+ 0x7C08, 0x7C1D, 0x7C31, 0x7C45, 0x7C59, 0x7C6E, 0x7C82, 0x7C96, 0x7CA9, 0x7CBD, 0x7CD1,
+ 0x7CE5, 0x7CF8, 0x7D0C, 0x7D1F, 0x7D33, 0x7D46, 0x7D59, 0x7D6D, 0x7D80, 0x7D93, 0x7DA6,
+ 0x7DB9, 0x7DCC, 0x7DDF, 0x7DF2, 0x7E04, 0x7E17, 0x7E2A, 0x7E3C, 0x7E4F, 0x7E61, 0x7E74,
+ 0x7E86, 0x7E98, 0x7EAB, 0x7EBD, 0x7ECF, 0x7EE1, 0x7EF3, 0x7F05, 0x7F17, 0x7F29, 0x7F3B,
+ 0x7F4D, 0x7F5E, 0x7F70, 0x7F82, 0x7F93, 0x7FA5, 0x7FB6, 0x7FC8, 0x7FD9, 0x7FEB, 0x7FFC,
+ 0x800D, 0x801E, 0x8030, 0x8041, 0x8052, 0x8063, 0x8074, 0x8085, 0x8096, 0x80A7, 0x80B7,
+ 0x80C8, 0x80D9, 0x80EA, 0x80FA, 0x810B, 0x811C, 0x812C, 0x813D, 0x814D, 0x815D, 0x816E,
+ 0x817E, 0x818E, 0x819F, 0x81AF, 0x81BF, 0x81CF, 0x81DF, 0x81EF, 0x81FF, 0x820F, 0x821F,
+ 0x822F, 0x823F, 0x824F, 0x825F, 0x826F, 0x827E, 0x828E, 0x829E, 0x82AD, 0x82BD, 0x82CC,
+ 0x82DC, 0x82EB, 0x82FB, 0x830A, 0x831A, 0x8329, 0x8338, 0x8348, 0x8357, 0x8366, 0x8375,
+ 0x8385, 0x8394, 0x83A3, 0x83B2, 0x83C1, 0x83D0, 0x83DF, 0x83EE, 0x83FD, 0x840C, 0x841A,
+ 0x8429, 0x8438, 0x8447, 0x8455, 0x8464, 0x8473, 0x8481, 0x8490, 0x849F, 0x84AD, 0x84BC,
+ 0x84CA, 0x84D9, 0x84E7, 0x84F5, 0x8504, 0x8512, 0x8521, 0x852F, 0x853D, 0x854B, 0x855A,
+ 0x8568, 0x8576, 0x8584, 0x8592, 0x85A0, 0x85AE, 0x85BC, 0x85CA, 0x85D8, 0x85E6, 0x85F4,
+ 0x8602, 0x8610, 0x861E, 0x862C, 0x8639, 0x8647, 0x8655, 0x8663, 0x8670, 0x867E, 0x868C,
+ 0x8699, 0x86A7, 0x86B5, 0x86C2, 0x86D0, 0x86DD, 0x86EB, 0x86F8, 0x8705, 0x8713, 0x8720,
+ 0x872E, 0x873B, 0x8748, 0x8756, 0x8763, 0x8770, 0x877D, 0x878B, 0x8798, 0x87A5, 0x87B2,
+ 0x87BF, 0x87CC, 0x87D9, 0x87E6, 0x87F3, 0x8801, 0x880E, 0x881A, 0x8827, 0x8834, 0x8841,
+ 0x884E, 0x885B, 0x8868, 0x8875, 0x8882, 0x888E, 0x889B, 0x88A8, 0x88B5, 0x88C1, 0x88CE,
+ 0x88DB, 0x88E7, 0x88F4, 0x8900, 0x890D, 0x891A, 0x8926, 0x8933, 0x893F, 0x894C, 0x8958,
+ 0x8965, 0x8971, 0x897D, 0x898A, 0x8996, 0x89A3, 0x89AF, 0x89BB, 0x89C8, 0x89D4, 0x89E0,
+ 0x89EC, 0x89F9, 0x8A05, 0x8A11, 0x8A1D, 0x8A29, 0x8A36, 0x8A42, 0x8A4E, 0x8A5A, 0x8A66,
+ 0x8A72, 0x8A7E, 0x8A8A, 0x8A96, 0x8AA2, 0x8AAE, 0x8ABA, 0x8AC6, 0x8AD2, 0x8ADE, 0x8AEA,
+ 0x8AF5, 0x8B01, 0x8B0D, 0x8B19, 0x8B25, 0x8B31, 0x8B3C, 0x8B48, 0x8B54, 0x8B60, 0x8B6B,
+ 0x8B77, 0x8B83, 0x8B8E, 0x8B9A, 0x8BA6, 0x8BB1, 0x8BBD, 0x8BC8, 0x8BD4, 0x8BDF, 0x8BEB,
+ 0x8BF6, 0x8C02, 0x8C0D, 0x8C19, 0x8C24, 0x8C30, 0x8C3B, 0x8C47, 0x8C52, 0x8C5D, 0x8C69,
+ 0x8C74, 0x8C80, 0x8C8B, 0x8C96, 0x8CA1, 0x8CAD, 0x8CB8, 0x8CC3, 0x8CCF, 0x8CDA, 0x8CE5,
+ 0x8CF0, 0x8CFB, 0x8D06, 0x8D12, 0x8D1D, 0x8D28, 0x8D33, 0x8D3E, 0x8D49, 0x8D54, 0x8D5F,
+ 0x8D6A, 0x8D75, 0x8D80, 0x8D8B, 0x8D96, 0x8DA1, 0x8DAC, 0x8DB7, 0x8DC2, 0x8DCD, 0x8DD8,
+ 0x8DE3, 0x8DEE, 0x8DF9, 0x8E04, 0x8E0E, 0x8E19, 0x8E24, 0x8E2F, 0x8E3A, 0x8E44, 0x8E4F,
+ 0x8E5A, 0x8E65, 0x8E6F, 0x8E7A, 0x8E85, 0x8E90, 0x8E9A, 0x8EA5, 0x8EB0, 0x8EBA, 0x8EC5,
+ 0x8ECF, 0x8EDA, 0x8EE5, 0x8EEF, 0x8EFA, 0x8F04, 0x8F0F, 0x8F19, 0x8F24, 0x8F2E, 0x8F39,
+ 0x8F43, 0x8F4E, 0x8F58, 0x8F63, 0x8F6D, 0x8F78, 0x8F82, 0x8F8C, 0x8F97, 0x8FA1, 0x8FAC,
+ 0x8FB6, 0x8FC0, 0x8FCB, 0x8FD5, 0x8FDF, 0x8FEA, 0x8FF4, 0x8FFE, 0x9008, 0x9013, 0x901D,
+ 0x9027, 0x9031, 0x903C, 0x9046, 0x9050, 0x905A, 0x9064, 0x906E, 0x9079, 0x9083, 0x908D,
+ 0x9097, 0x90A1, 0x90AB, 0x90B5, 0x90BF, 0x90C9, 0x90D3, 0x90DD, 0x90E7, 0x90F1, 0x90FB,
+ 0x9105, 0x910F, 0x9119, 0x9123, 0x912D, 0x9137, 0x9141, 0x914B, 0x9155, 0x915F, 0x9169,
+ 0x9173, 0x917D, 0x9186, 0x9190, 0x919A, 0x91A4, 0x91AE, 0x91B8, 0x91C1, 0x91CB, 0x91D5,
+ 0x91DF, 0x91E9, 0x91F2, 0x91FC, 0x9206, 0x9210, 0x9219, 0x9223, 0x922D, 0x9236, 0x9240,
+ 0x924A, 0x9253, 0x925D, 0x9267, 0x9270, 0x927A, 0x9283, 0x928D, 0x9297, 0x92A0, 0x92AA,
+ 0x92B3, 0x92BD, 0x92C6, 0x92D0, 0x92DA, 0x92E3, 0x92ED, 0x92F6, 0x9300, 0x9309, 0x9313,
+ 0x931C, 0x9325, 0x932F, 0x9338, 0x9342, 0x934B, 0x9355, 0x935E, 0x9367, 0x9371, 0x937A,
+ 0x9384, 0x938D, 0x9396, 0x93A0, 0x93A9, 0x93B2, 0x93BC, 0x93C5, 0x93CE, 0x93D7, 0x93E1,
+ 0x93EA, 0x93F3, 0x93FC, 0x9406, 0x940F, 0x9418, 0x9421, 0x942B, 0x9434, 0x943D, 0x9446,
+ 0x944F, 0x9459, 0x9462, 0x946B, 0x9474, 0x947D, 0x9486, 0x948F, 0x9499, 0x94A2, 0x94AB,
+ 0x94B4, 0x94BD, 0x94C6, 0x94CF, 0x94D8, 0x94E1, 0x94EA, 0x94F3, 0x94FC, 0x9505, 0x950E,
+ 0x9517, 0x9520, 0x9529, 0x9532, 0x953B, 0x9544, 0x954D, 0x9556, 0x955F, 0x9568, 0x9571,
+ 0x957A, 0x9583, 0x958C, 0x9595, 0x959D, 0x95A6, 0x95AF, 0x95B8, 0x95C1, 0x95CA, 0x95D3,
+ 0x95DB, 0x95E4, 0x95ED, 0x95F6, 0x95FF, 0x9608, 0x9610, 0x9619, 0x9622, 0x962B, 0x9633,
+ 0x963C, 0x9645, 0x964E, 0x9656, 0x965F, 0x9668, 0x9671, 0x9679, 0x9682, 0x968B, 0x9693,
+ 0x969C, 0x96A5, 0x96AD, 0x96B6, 0x96BF, 0x96C7, 0x96D0, 0x96D9, 0x96E1, 0x96EA, 0x96F2,
+ 0x96FB, 0x9704, 0x970C, 0x9715, 0x971D, 0x9726, 0x972E, 0x9737, 0x9740, 0x9748, 0x9751,
+ 0x9759, 0x9762, 0x976A, 0x9773, 0x977B, 0x9784, 0x978C, 0x9795, 0x979D, 0x97A6, 0x97AE,
+ 0x97B6, 0x97BF, 0x97C7, 0x97D0, 0x97D8, 0x97E1, 0x97E9, 0x97F1, 0x97FA, 0x9802, 0x980B,
+ 0x9813, 0x981B, 0x9824, 0x982C, 0x9834, 0x983D, 0x9845, 0x984D, 0x9856, 0x985E, 0x9866,
+ 0x986F, 0x9877, 0x987F, 0x9888, 0x9890, 0x9898, 0x98A0, 0x98A9, 0x98B1, 0x98B9, 0x98C1,
+ 0x98CA, 0x98D2, 0x98DA, 0x98E2, 0x98EB, 0x98F3, 0x98FB, 0x9903, 0x990B, 0x9914, 0x991C,
+ 0x9924, 0x992C, 0x9934, 0x993C, 0x9945, 0x994D, 0x9955, 0x995D, 0x9965, 0x996D, 0x9975,
+ 0x997D, 0x9986, 0x998E, 0x9996, 0x999E, 0x99A6, 0x99AE, 0x99B6, 0x99BE, 0x99C6, 0x99CE,
+ 0x99D6, 0x99DE, 0x99E6, 0x99EE, 0x99F6, 0x99FE, 0x9A06, 0x9A0E, 0x9A16, 0x9A1E, 0x9A26,
+ 0x9A2E, 0x9A36, 0x9A3E, 0x9A46, 0x9A4E, 0x9A56, 0x9A5E, 0x9A66, 0x9A6E, 0x9A76, 0x9A7E,
+ 0x9A86, 0x9A8E, 0x9A96, 0x9A9D, 0x9AA5, 0x9AAD, 0x9AB5, 0x9ABD, 0x9AC5, 0x9ACD, 0x9AD5,
+ 0x9ADC, 0x9AE4, 0x9AEC, 0x9AF4, 0x9AFC, 0x9B04, 0x9B0C, 0x9B13, 0x9B1B, 0x9B23, 0x9B2B,
+ 0x9B33, 0x9B3A, 0x9B42, 0x9B4A, 0x9B52, 0x9B59, 0x9B61, 0x9B69, 0x9B71, 0x9B79, 0x9B80,
+ 0x9B88, 0x9B90, 0x9B97, 0x9B9F, 0x9BA7, 0x9BAF, 0x9BB6, 0x9BBE, 0x9BC6, 0x9BCD, 0x9BD5,
+ 0x9BDD, 0x9BE5, 0x9BEC, 0x9BF4, 0x9BFC, 0x9C03, 0x9C0B, 0x9C12, 0x9C1A, 0x9C22, 0x9C29,
+ 0x9C31, 0x9C39, 0x9C40, 0x9C48, 0x9C50, 0x9C57, 0x9C5F, 0x9C66, 0x9C6E, 0x9C75, 0x9C7D,
+ 0x9C85, 0x9C8C, 0x9C94, 0x9C9B, 0x9CA3, 0x9CAA, 0x9CB2, 0x9CBA, 0x9CC1, 0x9CC9, 0x9CD0,
+ 0x9CD8, 0x9CDF, 0x9CE7, 0x9CEE, 0x9CF6, 0x9CFD, 0x9D05, 0x9D0C, 0x9D14, 0x9D1B, 0x9D23,
+ 0x9D2A, 0x9D32, 0x9D39, 0x9D40, 0x9D48, 0x9D4F, 0x9D57, 0x9D5E, 0x9D66, 0x9D6D, 0x9D75,
+ 0x9D7C, 0x9D83, 0x9D8B, 0x9D92, 0x9D9A, 0x9DA1, 0x9DA8, 0x9DB0, 0x9DB7, 0x9DBE, 0x9DC6,
+ 0x9DCD, 0x9DD5, 0x9DDC, 0x9DE3, 0x9DEB, 0x9DF2, 0x9DF9, 0x9E01, 0x9E08, 0x9E0F, 0x9E17,
+ 0x9E1E, 0x9E25, 0x9E2D, 0x9E34, 0x9E3B, 0x9E43, 0x9E4A, 0x9E51, 0x9E58, 0x9E60, 0x9E67,
+ 0x9E6E, 0x9E75, 0x9E7D, 0x9E84, 0x9E8B, 0x9E92, 0x9E9A, 0x9EA1, 0x9EA8, 0x9EAF, 0x9EB7,
+ 0x9EBE, 0x9EC5, 0x9ECC, 0x9ED4, 0x9EDB, 0x9EE2, 0x9EE9, 0x9EF0, 0x9EF7, 0x9EFF, 0x9F06,
+ 0x9F0D, 0x9F14, 0x9F1B, 0x9F23, 0x9F2A, 0x9F31, 0x9F38, 0x9F3F, 0x9F46, 0x9F4D, 0x9F55,
+ 0x9F5C, 0x9F63, 0x9F6A, 0x9F71, 0x9F78, 0x9F7F, 0x9F86, 0x9F8D, 0x9F95, 0x9F9C, 0x9FA3,
+ 0x9FAA, 0x9FB1, 0x9FB8, 0x9FBF, 0x9FC6, 0x9FCD, 0x9FD4, 0x9FDB, 0x9FE2, 0x9FE9, 0x9FF0,
+ 0x9FF7, 0x9FFF,
+};
+
static void tegra_crtc_atomic_destroy_state(struct drm_crtc *crtc,
struct drm_crtc_state *state);
@@ -101,8 +198,10 @@ bool tegra_dc_has_output(struct tegra_dc *dc, struct device *dev)
int err;
of_for_each_phandle(&it, err, np, "nvidia,outputs", NULL, 0)
- if (it.node == dev->of_node)
+ if (it.node == dev->of_node) {
+ of_node_put(it.node);
return true;
+ }
return false;
}
@@ -3251,6 +3350,25 @@ static int tegra_dc_probe(struct platform_device *pdev)
if (dc->irq < 0)
return -ENXIO;
+ if (dc->soc->has_nvdisplay) {
+ unsigned int i;
+ u64 r;
+
+ dc->cmu_output_lut =
+ dmam_alloc_coherent(dc->dev, ARRAY_SIZE(default_srgb_lut) * sizeof(u64),
+ &dc->cmu_output_lut_phys, GFP_KERNEL);
+
+ if (!dc->cmu_output_lut) {
+ dev_err(dc->dev, "failed to allocate lut for cmu\n");
+ return -ENOMEM;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(default_srgb_lut); i++) {
+ r = default_srgb_lut[i];
+ dc->cmu_output_lut[i] = (r << 32) | (r << 16) | r;
+ }
+ }
+
err = tegra_dc_rgb_probe(dc);
if (err < 0 && err != -ENODEV)
return dev_err_probe(&pdev->dev, err,
diff --git a/drivers/gpu/drm/tegra/dc.h b/drivers/gpu/drm/tegra/dc.h
index 577c2e8e3ab3..951a61673cd7 100644
--- a/drivers/gpu/drm/tegra/dc.h
+++ b/drivers/gpu/drm/tegra/dc.h
@@ -103,6 +103,9 @@ struct tegra_dc {
const struct tegra_dc_soc_info *soc;
bool has_opp_table;
+
+ u64 *cmu_output_lut;
+ dma_addr_t cmu_output_lut_phys;
};
static inline struct tegra_dc *
@@ -447,6 +450,7 @@ int tegra_dc_rgb_exit(struct tegra_dc *dc);
#define BASE_COLOR_SIZE_888 ( 8 << 0)
#define BASE_COLOR_SIZE_101010 ( 10 << 0)
#define BASE_COLOR_SIZE_121212 ( 12 << 0)
+#define CMU_ENABLE_ENABLE (1 << 20)
#define DC_DISP_SHIFT_CLOCK_OPTIONS 0x431
#define SC1_H_QUALIFIER_NONE (1 << 16)
@@ -732,6 +736,15 @@ int tegra_dc_rgb_exit(struct tegra_dc *dc);
#define PROTOCOL_MASK (0xf << 8)
#define PROTOCOL_SINGLE_TMDS_A (0x1 << 8)
+#define DC_DISP_CORE_HEAD_SET_CONTROL_OUTPUT_LUT 0x431
+#define OUTPUT_LUT_MODE_MASK (3 << 5)
+#define OUTPUT_LUT_MODE_INTERPOLATE (1 << 5)
+#define OUTPUT_LUT_SIZE_MASK (3 << 1)
+#define OUTPUT_LUT_SIZE_SIZE_1025 (2 << 1)
+
+#define DC_DISP_COREPVT_HEAD_SET_OUTPUT_LUT_BASE 0x432
+#define DC_DISP_COREPVT_HEAD_SET_OUTPUT_LUT_BASE_HI 0x433
+
#define DC_DISP_PCALC_HEAD_SET_CROPPED_POINT_IN_CURSOR 0x442
#define DC_DISP_PCALC_HEAD_SET_CROPPED_SIZE_IN_CURSOR 0x446
diff --git a/drivers/gpu/drm/tegra/dp.c b/drivers/gpu/drm/tegra/dp.c
index 990e744b0923..3cb194f5ce36 100644
--- a/drivers/gpu/drm/tegra/dp.c
+++ b/drivers/gpu/drm/tegra/dp.c
@@ -4,6 +4,8 @@
* Copyright (C) 2015 Rob Clark
*/
+#include <linux/string_choices.h>
+
#include <drm/display/drm_dp_helper.h>
#include <drm/drm_crtc.h>
#include <drm/drm_print.h>
@@ -668,7 +670,7 @@ static int drm_dp_link_train_full(struct drm_dp_link *link)
retry:
DRM_DEBUG_KMS("full-training link: %u lane%s at %u MHz\n",
- link->lanes, (link->lanes > 1) ? "s" : "",
+ link->lanes, str_plural(link->lanes),
link->rate / 100);
err = drm_dp_link_configure(link->aux, link);
@@ -724,7 +726,7 @@ static int drm_dp_link_train_fast(struct drm_dp_link *link)
int err;
DRM_DEBUG_KMS("fast-training link: %u lane%s at %u MHz\n",
- link->lanes, (link->lanes > 1) ? "s" : "",
+ link->lanes, str_plural(link->lanes),
link->rate / 100);
err = drm_dp_link_configure(link->aux, link);
diff --git a/drivers/gpu/drm/tegra/dp.h b/drivers/gpu/drm/tegra/dp.h
index 695060cafac0..774fb13c0861 100644
--- a/drivers/gpu/drm/tegra/dp.h
+++ b/drivers/gpu/drm/tegra/dp.h
@@ -26,7 +26,7 @@ struct drm_dp_link_caps {
bool enhanced_framing;
/**
- * tps3_supported:
+ * @tps3_supported:
*
* training pattern sequence 3 supported for equalization
*/
@@ -62,12 +62,12 @@ void drm_dp_link_caps_copy(struct drm_dp_link_caps *dest,
*/
struct drm_dp_link_ops {
/**
- * @apply_training:
+ * @apply_training: apply the link training
*/
int (*apply_training)(struct drm_dp_link *link);
/**
- * @configure:
+ * @configure: configure the DP link
*/
int (*configure)(struct drm_dp_link *link);
};
@@ -113,6 +113,8 @@ struct drm_dp_link_train {
* @max_lanes: maximum number of lanes supported on the link
* @caps: capabilities supported on the link (see &drm_dp_link_caps)
* @aux_rd_interval: AUX read interval to use for training (in microseconds)
+ * @aux_rd_interval.cr: clock recovery read interval
+ * @aux_rd_interval.ce: channel equalization read interval
* @edp: eDP revision (0x11: eDP 1.1, 0x12: eDP 1.2, ...)
* @rate: currently configured link rate
* @lanes: currently configured number of lanes
@@ -126,10 +128,6 @@ struct drm_dp_link {
struct drm_dp_link_caps caps;
- /**
- * @cr: clock recovery read interval
- * @ce: channel equalization read interval
- */
struct {
unsigned int cr;
unsigned int ce;
diff --git a/drivers/gpu/drm/tegra/drm.h b/drivers/gpu/drm/tegra/drm.h
index ae68b03d8483..bc720ae8d95e 100644
--- a/drivers/gpu/drm/tegra/drm.h
+++ b/drivers/gpu/drm/tegra/drm.h
@@ -184,11 +184,6 @@ struct tegra_bo *tegra_fb_get_plane(struct drm_framebuffer *framebuffer,
bool tegra_fb_is_bottom_up(struct drm_framebuffer *framebuffer);
int tegra_fb_get_tiling(struct drm_framebuffer *framebuffer,
struct tegra_bo_tiling *tiling);
-struct drm_framebuffer *tegra_fb_alloc(struct drm_device *drm,
- const struct drm_format_info *info,
- const struct drm_mode_fb_cmd2 *mode_cmd,
- struct tegra_bo **planes,
- unsigned int num_planes);
struct drm_framebuffer *tegra_fb_create(struct drm_device *drm,
struct drm_file *file,
const struct drm_format_info *info,
diff --git a/drivers/gpu/drm/tegra/fb.c b/drivers/gpu/drm/tegra/fb.c
index 1e4803d355dd..0a7cb9e462ff 100644
--- a/drivers/gpu/drm/tegra/fb.c
+++ b/drivers/gpu/drm/tegra/fb.c
@@ -102,6 +102,7 @@ static const struct drm_framebuffer_funcs tegra_fb_funcs = {
.create_handle = drm_gem_fb_create_handle,
};
+static
struct drm_framebuffer *tegra_fb_alloc(struct drm_device *drm,
const struct drm_format_info *info,
const struct drm_mode_fb_cmd2 *mode_cmd,
diff --git a/drivers/gpu/drm/tegra/fbdev.c b/drivers/gpu/drm/tegra/fbdev.c
index 8f40882aa76e..003e80cf7b9a 100644
--- a/drivers/gpu/drm/tegra/fbdev.c
+++ b/drivers/gpu/drm/tegra/fbdev.c
@@ -40,8 +40,7 @@ static int tegra_fb_mmap(struct fb_info *info, struct vm_area_struct *vma)
static void tegra_fbdev_fb_destroy(struct fb_info *info)
{
struct drm_fb_helper *helper = info->par;
- struct drm_framebuffer *fb = helper->fb;
- struct tegra_bo *bo = tegra_fb_get_plane(fb, 0);
+ struct tegra_bo *bo = tegra_fb_get_plane(helper->fb, 0);
drm_fb_helper_fini(helper);
@@ -50,8 +49,8 @@ static void tegra_fbdev_fb_destroy(struct fb_info *info)
vunmap(bo->vaddr);
bo->vaddr = NULL;
}
- drm_framebuffer_remove(fb);
+ drm_client_buffer_delete(helper->buffer);
drm_client_release(&helper->client);
}
@@ -70,74 +69,79 @@ static const struct drm_fb_helper_funcs tegra_fbdev_helper_funcs = {
int tegra_fbdev_driver_fbdev_probe(struct drm_fb_helper *helper,
struct drm_fb_helper_surface_size *sizes)
{
- struct tegra_drm *tegra = helper->dev->dev_private;
- struct drm_device *drm = helper->dev;
- struct drm_mode_fb_cmd2 cmd = { 0 };
+ struct drm_client_dev *client = &helper->client;
+ struct drm_device *drm = client->dev;
+ struct drm_file *file = client->file;
+ struct tegra_drm *tegra = drm->dev_private;
struct fb_info *info = helper->info;
- unsigned int bytes_per_pixel;
- struct drm_framebuffer *fb;
- unsigned long offset;
+ u32 fourcc, pitch;
+ u64 size;
+ const struct drm_format_info *format;
struct tegra_bo *bo;
- size_t size;
+ struct drm_gem_object *gem;
+ u32 handle;
+ struct drm_client_buffer *buffer;
int err;
- bytes_per_pixel = DIV_ROUND_UP(sizes->surface_bpp, 8);
-
- cmd.width = sizes->surface_width;
- cmd.height = sizes->surface_height;
- cmd.pitches[0] = round_up(sizes->surface_width * bytes_per_pixel,
- tegra->pitch_align);
-
- cmd.pixel_format = drm_mode_legacy_fb_format(sizes->surface_bpp,
- sizes->surface_depth);
-
- size = cmd.pitches[0] * cmd.height;
+ fourcc = drm_mode_legacy_fb_format(sizes->surface_bpp, sizes->surface_depth);
+ format = drm_get_format_info(drm, fourcc, DRM_FORMAT_MOD_LINEAR);
+ pitch = round_up(drm_format_info_min_pitch(format, 0, sizes->surface_width),
+ tegra->pitch_align);
+ size = ALIGN(pitch * sizes->surface_height, PAGE_SIZE);
bo = tegra_bo_create(drm, size, 0);
if (IS_ERR(bo))
return PTR_ERR(bo);
+ gem = &bo->gem;
+
+ err = drm_gem_handle_create(file, gem, &handle);
+ if (err)
+ goto err_drm_gem_object_put;
- fb = tegra_fb_alloc(drm,
- drm_get_format_info(drm, cmd.pixel_format, cmd.modifier[0]),
- &cmd, &bo, 1);
- if (IS_ERR(fb)) {
- err = PTR_ERR(fb);
- dev_err(drm->dev, "failed to allocate DRM framebuffer: %d\n",
- err);
- drm_gem_object_put(&bo->gem);
- return PTR_ERR(fb);
+ buffer = drm_client_buffer_create(client, sizes->surface_width, sizes->surface_height,
+ fourcc, handle, pitch);
+ if (IS_ERR(buffer)) {
+ err = PTR_ERR(buffer);
+ goto err_drm_gem_handle_delete;
}
helper->funcs = &tegra_fbdev_helper_funcs;
- helper->fb = fb;
- helper->info = info;
+ helper->buffer = buffer;
+ helper->fb = buffer->fb;
info->fbops = &tegra_fb_ops;
drm_fb_helper_fill_info(info, helper, sizes);
- offset = info->var.xoffset * bytes_per_pixel +
- info->var.yoffset * fb->pitches[0];
-
if (bo->pages) {
bo->vaddr = vmap(bo->pages, bo->num_pages, VM_MAP,
pgprot_writecombine(PAGE_KERNEL));
if (!bo->vaddr) {
dev_err(drm->dev, "failed to vmap() framebuffer\n");
err = -ENOMEM;
- goto destroy;
+ goto err_drm_client_buffer_delete;
}
}
info->flags |= FBINFO_VIRTFB;
- info->screen_buffer = bo->vaddr + offset;
- info->screen_size = size;
- info->fix.smem_start = (unsigned long)(bo->iova + offset);
- info->fix.smem_len = size;
+ info->screen_buffer = bo->vaddr;
+ info->screen_size = gem->size;
+ info->fix.smem_start = (unsigned long)(bo->iova);
+ info->fix.smem_len = gem->size;
+
+ /* The handle is only needed for creating the framebuffer. */
+ drm_gem_handle_delete(file, handle);
+
+ /* The framebuffer still holds a reference on the GEM object. */
+ drm_gem_object_put(gem);
return 0;
-destroy:
- drm_framebuffer_remove(fb);
+err_drm_client_buffer_delete:
+ drm_client_buffer_delete(buffer);
+err_drm_gem_handle_delete:
+ drm_gem_handle_delete(file, handle);
+err_drm_gem_object_put:
+ drm_gem_object_put(gem);
return err;
}
diff --git a/drivers/gpu/drm/tegra/gem.c b/drivers/gpu/drm/tegra/gem.c
index d2bae88ad545..436394e04812 100644
--- a/drivers/gpu/drm/tegra/gem.c
+++ b/drivers/gpu/drm/tegra/gem.c
@@ -69,7 +69,7 @@ static struct host1x_bo_mapping *tegra_bo_pin(struct device *dev, struct host1x_
return ERR_PTR(-ENOMEM);
kref_init(&map->ref);
- map->bo = host1x_bo_get(bo);
+ map->bo = bo;
map->direction = direction;
map->dev = dev;
@@ -170,7 +170,6 @@ static void tegra_bo_unpin(struct host1x_bo_mapping *map)
kfree(map->sgt);
}
- host1x_bo_put(map->bo);
kfree(map);
}
@@ -235,6 +234,7 @@ static const struct host1x_bo_ops tegra_bo_ops = {
static int tegra_bo_iommu_map(struct tegra_drm *tegra, struct tegra_bo *bo)
{
int prot = IOMMU_READ | IOMMU_WRITE;
+ ssize_t size;
int err;
if (bo->mm)
@@ -256,13 +256,15 @@ static int tegra_bo_iommu_map(struct tegra_drm *tegra, struct tegra_bo *bo)
bo->iova = bo->mm->start;
- bo->size = iommu_map_sgtable(tegra->domain, bo->iova, bo->sgt, prot);
- if (!bo->size) {
+ size = iommu_map_sgtable(tegra->domain, bo->iova, bo->sgt, prot);
+ if (size < 0) {
dev_err(tegra->drm->dev, "failed to map buffer\n");
- err = -ENOMEM;
+ err = size;
goto remove;
}
+ bo->size = size;
+
mutex_unlock(&tegra->mm_lock);
return 0;
@@ -509,17 +511,9 @@ free:
void tegra_bo_free_object(struct drm_gem_object *gem)
{
struct tegra_drm *tegra = gem->dev->dev_private;
- struct host1x_bo_mapping *mapping, *tmp;
struct tegra_bo *bo = to_tegra_bo(gem);
- /* remove all mappings of this buffer object from any caches */
- list_for_each_entry_safe(mapping, tmp, &bo->base.mappings, list) {
- if (mapping->cache)
- host1x_bo_unpin(mapping);
- else
- dev_err(gem->dev->dev, "mapping %p stale for device %s\n", mapping,
- dev_name(mapping->dev));
- }
+ host1x_bo_clear_cached_mappings(&bo->base);
if (tegra->domain) {
tegra_bo_iommu_unmap(tegra, bo);
diff --git a/drivers/gpu/drm/tegra/gr2d.c b/drivers/gpu/drm/tegra/gr2d.c
index 21f4dd0fa6af..892e3450b281 100644
--- a/drivers/gpu/drm/tegra/gr2d.c
+++ b/drivers/gpu/drm/tegra/gr2d.c
@@ -100,9 +100,6 @@ static int gr2d_exit(struct host1x_client *client)
if (err < 0)
return err;
- pm_runtime_dont_use_autosuspend(client->dev);
- pm_runtime_force_suspend(client->dev);
-
host1x_client_iommu_detach(client);
host1x_syncpt_put(client->syncpts[0]);
host1x_channel_put(gr2d->channel);
@@ -276,15 +273,21 @@ static int gr2d_probe(struct platform_device *pdev)
if (err)
return err;
+ /* initialize address register map */
+ for (i = 0; i < ARRAY_SIZE(gr2d_addr_regs); i++)
+ set_bit(gr2d_addr_regs[i], gr2d->addr_regs);
+
+ pm_runtime_enable(dev);
+
err = host1x_client_register(&gr2d->client.base);
if (err < 0) {
+ pm_runtime_disable(dev);
dev_err(dev, "failed to register host1x client: %d\n", err);
return err;
}
- /* initialize address register map */
- for (i = 0; i < ARRAY_SIZE(gr2d_addr_regs); i++)
- set_bit(gr2d_addr_regs[i], gr2d->addr_regs);
+ pm_runtime_use_autosuspend(dev);
+ pm_runtime_set_autosuspend_delay(dev, 500);
return 0;
}
@@ -367,10 +370,6 @@ static int __maybe_unused gr2d_runtime_resume(struct device *dev)
goto disable_clk;
}
- pm_runtime_enable(dev);
- pm_runtime_use_autosuspend(dev);
- pm_runtime_set_autosuspend_delay(dev, 500);
-
return 0;
disable_clk:
diff --git a/drivers/gpu/drm/tegra/gr3d.c b/drivers/gpu/drm/tegra/gr3d.c
index 42e9656ab80c..388e47943d5e 100644
--- a/drivers/gpu/drm/tegra/gr3d.c
+++ b/drivers/gpu/drm/tegra/gr3d.c
@@ -109,9 +109,6 @@ static int gr3d_exit(struct host1x_client *client)
if (err < 0)
return err;
- pm_runtime_dont_use_autosuspend(client->dev);
- pm_runtime_force_suspend(client->dev);
-
host1x_client_iommu_detach(client);
host1x_syncpt_put(client->syncpts[0]);
host1x_channel_put(gr3d->channel);
@@ -506,16 +503,22 @@ static int gr3d_probe(struct platform_device *pdev)
if (err)
return err;
+ /* initialize address register map */
+ for (i = 0; i < ARRAY_SIZE(gr3d_addr_regs); i++)
+ set_bit(gr3d_addr_regs[i], gr3d->addr_regs);
+
+ pm_runtime_enable(&pdev->dev);
+
err = host1x_client_register(&gr3d->client.base);
if (err < 0) {
+ pm_runtime_disable(&pdev->dev);
dev_err(&pdev->dev, "failed to register host1x client: %d\n",
err);
return err;
}
- /* initialize address register map */
- for (i = 0; i < ARRAY_SIZE(gr3d_addr_regs); i++)
- set_bit(gr3d_addr_regs[i], gr3d->addr_regs);
+ pm_runtime_use_autosuspend(&pdev->dev);
+ pm_runtime_set_autosuspend_delay(&pdev->dev, 500);
return 0;
}
@@ -578,10 +581,6 @@ static int __maybe_unused gr3d_runtime_resume(struct device *dev)
goto disable_clk;
}
- pm_runtime_enable(dev);
- pm_runtime_use_autosuspend(dev);
- pm_runtime_set_autosuspend_delay(dev, 500);
-
return 0;
disable_clk:
diff --git a/drivers/gpu/drm/tegra/hdmi.c b/drivers/gpu/drm/tegra/hdmi.c
index 10f02f35c866..0d2c0ab0a63e 100644
--- a/drivers/gpu/drm/tegra/hdmi.c
+++ b/drivers/gpu/drm/tegra/hdmi.c
@@ -25,12 +25,12 @@
#include <drm/drm_crtc.h>
#include <drm/drm_debugfs.h>
#include <drm/drm_edid.h>
+#include <drm/drm_encoder.h>
#include <drm/drm_eld.h>
#include <drm/drm_file.h>
#include <drm/drm_fourcc.h>
#include <drm/drm_print.h>
#include <drm/drm_probe_helper.h>
-#include <drm/drm_simple_kms_helper.h>
#include "hda.h"
#include "hdmi.h"
@@ -372,6 +372,10 @@ static const struct tmds_config tegra124_tmds_config[] = {
},
};
+static const struct drm_encoder_funcs tegra_hdmi_encoder_funcs = {
+ .destroy = drm_encoder_cleanup,
+};
+
static void tegra_hdmi_audio_lock(struct tegra_hdmi *hdmi)
{
mutex_lock(&hdmi->audio_lock);
@@ -1555,8 +1559,8 @@ static int tegra_hdmi_init(struct host1x_client *client)
hdmi->output.dev = client->dev;
- drm_simple_encoder_init(drm, &hdmi->output.encoder,
- DRM_MODE_ENCODER_TMDS);
+ drm_encoder_init(drm, &hdmi->output.encoder, &tegra_hdmi_encoder_funcs,
+ DRM_MODE_ENCODER_TMDS, NULL);
drm_encoder_helper_add(&hdmi->output.encoder,
&tegra_hdmi_encoder_helper_funcs);
diff --git a/drivers/gpu/drm/tegra/sor.c b/drivers/gpu/drm/tegra/sor.c
index de8b2dfc4984..43409439e9f4 100644
--- a/drivers/gpu/drm/tegra/sor.c
+++ b/drivers/gpu/drm/tegra/sor.c
@@ -13,6 +13,7 @@
#include <linux/pm_runtime.h>
#include <linux/regulator/consumer.h>
#include <linux/reset.h>
+#include <linux/string_choices.h>
#include <soc/tegra/pmc.h>
@@ -22,10 +23,10 @@
#include <drm/drm_debugfs.h>
#include <drm/drm_edid.h>
#include <drm/drm_eld.h>
+#include <drm/drm_encoder.h>
#include <drm/drm_file.h>
#include <drm/drm_panel.h>
#include <drm/drm_print.h>
-#include <drm/drm_simple_kms_helper.h>
#include "dc.h"
#include "dp.h"
@@ -940,7 +941,7 @@ static int tegra_sor_dp_link_configure(struct drm_dp_link *link)
err = tegra_sor_power_up_lanes(sor, lanes);
if (err < 0) {
dev_err(sor->dev, "failed to power up %u lane%s: %d\n",
- lanes, (lanes != 1) ? "s" : "", err);
+ lanes, str_plural(lanes), err);
return err;
}
@@ -2557,6 +2558,17 @@ static void tegra_sor_hdmi_enable(struct drm_encoder *encoder)
value = tegra_dc_readl(dc, DC_DISP_DISP_COLOR_CONTROL);
value &= ~DITHER_CONTROL_MASK;
value &= ~BASE_COLOR_SIZE_MASK;
+ if (dc->soc->has_nvdisplay) {
+ tegra_dc_writel(dc, lower_32_bits(dc->cmu_output_lut_phys),
+ DC_DISP_COREPVT_HEAD_SET_OUTPUT_LUT_BASE);
+ tegra_dc_writel(dc, upper_32_bits(dc->cmu_output_lut_phys),
+ DC_DISP_COREPVT_HEAD_SET_OUTPUT_LUT_BASE_HI);
+
+ tegra_dc_writel(dc, OUTPUT_LUT_MODE_INTERPOLATE | OUTPUT_LUT_SIZE_SIZE_1025,
+ DC_DISP_CORE_HEAD_SET_CONTROL_OUTPUT_LUT);
+
+ value |= CMU_ENABLE_ENABLE;
+ }
switch (state->bpc) {
case 6:
@@ -2921,6 +2933,20 @@ static void tegra_sor_dp_enable(struct drm_encoder *encoder)
if (err < 0)
dev_err(sor->dev, "failed to attach SOR: %d\n", err);
+ if (dc->soc->has_nvdisplay) {
+ value = tegra_dc_readl(dc, DC_DISP_DISP_COLOR_CONTROL);
+ tegra_dc_writel(dc, lower_32_bits(dc->cmu_output_lut_phys),
+ DC_DISP_COREPVT_HEAD_SET_OUTPUT_LUT_BASE);
+ tegra_dc_writel(dc, upper_32_bits(dc->cmu_output_lut_phys),
+ DC_DISP_COREPVT_HEAD_SET_OUTPUT_LUT_BASE_HI);
+
+ tegra_dc_writel(dc, OUTPUT_LUT_MODE_INTERPOLATE | OUTPUT_LUT_SIZE_SIZE_1025,
+ DC_DISP_CORE_HEAD_SET_CONTROL_OUTPUT_LUT);
+
+ value |= CMU_ENABLE_ENABLE;
+ tegra_dc_writel(dc, value, DC_DISP_DISP_COLOR_CONTROL);
+ }
+
value = tegra_dc_readl(dc, DC_DISP_DISP_WIN_OPTIONS);
value |= SOR_ENABLE(sor->index);
tegra_dc_writel(dc, value, DC_DISP_DISP_WIN_OPTIONS);
@@ -3038,6 +3064,10 @@ static const struct tegra_sor_ops tegra_sor_dp_ops = {
.probe = tegra_sor_dp_probe,
};
+static const struct drm_encoder_funcs tegra_sor_encoder_funcs_cleanup = {
+ .destroy = drm_encoder_cleanup,
+};
+
static int tegra_sor_init(struct host1x_client *client)
{
struct drm_device *drm = dev_get_drvdata(client->host);
@@ -3081,7 +3111,8 @@ static int tegra_sor_init(struct host1x_client *client)
&tegra_sor_connector_helper_funcs);
sor->output.connector.dpms = DRM_MODE_DPMS_OFF;
- drm_simple_encoder_init(drm, &sor->output.encoder, encoder);
+ drm_encoder_init(drm, &sor->output.encoder,
+ &tegra_sor_encoder_funcs_cleanup, encoder, NULL);
drm_encoder_helper_add(&sor->output.encoder, helpers);
drm_connector_attach_encoder(&sor->output.connector,
diff --git a/drivers/gpu/drm/tegra/submit.c b/drivers/gpu/drm/tegra/submit.c
index 3009b8b9e619..e5841857c937 100644
--- a/drivers/gpu/drm/tegra/submit.c
+++ b/drivers/gpu/drm/tegra/submit.c
@@ -76,7 +76,7 @@ gather_bo_pin(struct device *dev, struct host1x_bo *bo, enum dma_data_direction
return ERR_PTR(-ENOMEM);
kref_init(&map->ref);
- map->bo = host1x_bo_get(bo);
+ map->bo = bo;
map->direction = direction;
map->dev = dev;
@@ -117,7 +117,6 @@ static void gather_bo_unpin(struct host1x_bo_mapping *map)
dma_unmap_sgtable(map->dev, map->sgt, map->direction, 0);
sg_free_table(map->sgt);
kfree(map->sgt);
- host1x_bo_put(map->bo);
kfree(map);
}
diff --git a/drivers/gpu/drm/verisilicon/Makefile b/drivers/gpu/drm/verisilicon/Makefile
index fd8d805fbcde..426f4bcaa834 100644
--- a/drivers/gpu/drm/verisilicon/Makefile
+++ b/drivers/gpu/drm/verisilicon/Makefile
@@ -1,5 +1,6 @@
# SPDX-License-Identifier: GPL-2.0-only
-verisilicon-dc-objs := vs_bridge.o vs_crtc.o vs_dc.o vs_drm.o vs_hwdb.o vs_plane.o vs_primary_plane.o
+verisilicon-dc-objs := vs_bridge.o vs_crtc.o vs_dc.o vs_drm.o vs_hwdb.o \
+ vs_plane.o vs_primary_plane.o vs_cursor_plane.o
obj-$(CONFIG_DRM_VERISILICON_DC) += verisilicon-dc.o
diff --git a/drivers/gpu/drm/verisilicon/vs_crtc.c b/drivers/gpu/drm/verisilicon/vs_crtc.c
index 9080344398ca..0b8a35d09cd2 100644
--- a/drivers/gpu/drm/verisilicon/vs_crtc.c
+++ b/drivers/gpu/drm/verisilicon/vs_crtc.c
@@ -159,7 +159,7 @@ struct vs_crtc *vs_crtc_init(struct drm_device *drm_dev, struct vs_dc *dc,
unsigned int output)
{
struct vs_crtc *vcrtc;
- struct drm_plane *primary;
+ struct drm_plane *primary, *cursor;
int ret;
vcrtc = drmm_kzalloc(drm_dev, sizeof(*vcrtc), GFP_KERNEL);
@@ -175,9 +175,16 @@ struct vs_crtc *vs_crtc_init(struct drm_device *drm_dev, struct vs_dc *dc,
return ERR_PTR(PTR_ERR(primary));
}
+ /* Create our cursor plane */
+ cursor = vs_cursor_plane_init(drm_dev, dc);
+ if (IS_ERR(cursor)) {
+ drm_err(drm_dev, "Couldn't create the cursor plane\n");
+ return ERR_CAST(cursor);
+ }
+
ret = drmm_crtc_init_with_planes(drm_dev, &vcrtc->base,
primary,
- NULL,
+ cursor,
&vs_crtc_funcs,
NULL);
if (ret) {
diff --git a/drivers/gpu/drm/verisilicon/vs_cursor_plane.c b/drivers/gpu/drm/verisilicon/vs_cursor_plane.c
new file mode 100644
index 000000000000..fa4f601dd0c8
--- /dev/null
+++ b/drivers/gpu/drm/verisilicon/vs_cursor_plane.c
@@ -0,0 +1,272 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2026 Institute of Software, Chinese Academy of Sciences (ISCAS)
+ *
+ * Authors:
+ * Icenowy Zheng <zhengxingda@iscas.ac.cn>
+ */
+
+#include <linux/log2.h>
+#include <linux/regmap.h>
+
+#include <drm/drm_atomic.h>
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_crtc.h>
+#include <drm/drm_fourcc.h>
+#include <drm/drm_framebuffer.h>
+#include <drm/drm_gem_atomic_helper.h>
+#include <drm/drm_modeset_helper_vtables.h>
+#include <drm/drm_plane.h>
+#include <drm/drm_print.h>
+
+#include "vs_crtc.h"
+#include "vs_plane.h"
+#include "vs_dc.h"
+#include "vs_hwdb.h"
+#include "vs_cursor_plane_regs.h"
+
+#define VSDC_MIN_CURSOR_SIZE 32
+#define VSDC_MAX_CURSOR_SIZE 256
+
+#define VSDC_CURSOR_LOCATION_MAX_POSITIVE BIT_MASK(15)
+#define VSDC_CURSOR_LOCATION_MAX_NEGATIVE BIT_MASK(5)
+
+static bool vs_cursor_plane_check_coord(int32_t coord)
+{
+ if (coord >= 0)
+ return coord <= VSDC_CURSOR_LOCATION_MAX_POSITIVE;
+ else
+ return (-coord) <= VSDC_CURSOR_LOCATION_MAX_NEGATIVE;
+}
+
+static int vs_cursor_plane_atomic_check(struct drm_plane *plane,
+ struct drm_atomic_commit *state)
+{
+ struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state,
+ plane);
+ struct drm_crtc *crtc = new_plane_state->crtc;
+ struct drm_framebuffer *fb = new_plane_state->fb;
+ struct drm_crtc_state *crtc_state = NULL;
+ struct vs_crtc *vcrtc;
+ struct vs_dc *dc;
+ int ret;
+
+ if (crtc)
+ crtc_state = drm_atomic_get_new_crtc_state(state, crtc);
+
+ ret = drm_atomic_helper_check_plane_state(new_plane_state,
+ crtc_state,
+ DRM_PLANE_NO_SCALING,
+ DRM_PLANE_NO_SCALING,
+ true, true);
+ if (ret)
+ return ret;
+
+ if (!new_plane_state->visible)
+ return 0; /* Skip validity check */
+
+ vcrtc = drm_crtc_to_vs_crtc(crtc);
+ dc = vcrtc->dc;
+
+ /* Only certain PoT square sizes is supported. */
+ if (!is_power_of_2(new_plane_state->crtc_w) ||
+ new_plane_state->crtc_w < VSDC_MIN_CURSOR_SIZE ||
+ new_plane_state->crtc_w > dc->identity.max_cursor_size)
+ return -EINVAL;
+
+ if (new_plane_state->crtc_w != new_plane_state->crtc_h)
+ return -EINVAL;
+
+ /* Check if the cursor is inside the register fields' range */
+ if (!vs_cursor_plane_check_coord(new_plane_state->crtc_x) ||
+ !vs_cursor_plane_check_coord(new_plane_state->crtc_y))
+ return -EINVAL;
+
+ /* Extra line padding isn't supported */
+ if (fb->pitches[0] !=
+ drm_format_info_min_pitch(fb->format, 0, new_plane_state->crtc_w))
+ return -EINVAL;
+
+ return 0;
+}
+
+static void vs_cursor_plane_commit(struct vs_dc *dc, unsigned int output)
+{
+ regmap_set_bits(dc->regs, VSDC_CURSOR_CONFIG(output),
+ VSDC_CURSOR_CONFIG_COMMIT |
+ VSDC_CURSOR_CONFIG_IMG_UPDATE);
+}
+
+static void vs_cursor_plane_atomic_enable(struct drm_plane *plane,
+ struct drm_atomic_commit *atomic_state)
+{
+ struct drm_plane_state *state = drm_atomic_get_new_plane_state(atomic_state,
+ plane);
+ struct drm_crtc *crtc = state->crtc;
+ struct vs_crtc *vcrtc = drm_crtc_to_vs_crtc(crtc);
+ unsigned int output = vcrtc->id;
+ struct vs_dc *dc = vcrtc->dc;
+
+ regmap_update_bits(dc->regs, VSDC_CURSOR_CONFIG(output),
+ VSDC_CURSOR_CONFIG_FMT_MASK,
+ VSDC_CURSOR_CONFIG_FMT_ARGB8888);
+
+ vs_cursor_plane_commit(dc, output);
+}
+
+static void vs_cursor_plane_atomic_disable(struct drm_plane *plane,
+ struct drm_atomic_commit *atomic_state)
+{
+ struct drm_plane_state *state = drm_atomic_get_old_plane_state(atomic_state,
+ plane);
+ struct drm_crtc *crtc = state->crtc;
+ struct vs_crtc *vcrtc = drm_crtc_to_vs_crtc(crtc);
+ unsigned int output = vcrtc->id;
+ struct vs_dc *dc = vcrtc->dc;
+
+ regmap_update_bits(dc->regs, VSDC_CURSOR_CONFIG(output),
+ VSDC_CURSOR_CONFIG_FMT_MASK,
+ VSDC_CURSOR_CONFIG_FMT_OFF);
+
+ vs_cursor_plane_commit(dc, output);
+}
+
+static void vs_cursor_plane_atomic_update(struct drm_plane *plane,
+ struct drm_atomic_commit *atomic_state)
+{
+ struct drm_plane_state *state = drm_atomic_get_new_plane_state(atomic_state,
+ plane);
+ struct drm_framebuffer *fb = state->fb;
+ struct drm_crtc *crtc = state->crtc;
+ struct vs_dc *dc;
+ struct vs_crtc *vcrtc;
+ unsigned int output;
+ dma_addr_t dma_addr;
+
+ if (!state->visible) {
+ vs_cursor_plane_atomic_disable(plane, atomic_state);
+ return;
+ }
+
+ vcrtc = drm_crtc_to_vs_crtc(crtc);
+ output = vcrtc->id;
+ dc = vcrtc->dc;
+
+ /* Other sizes should be rejected by atomic_check */
+ switch (state->crtc_w) {
+ case 32:
+ regmap_update_bits(dc->regs, VSDC_CURSOR_CONFIG(output),
+ VSDC_CURSOR_CONFIG_SIZE_MASK,
+ VSDC_CURSOR_CONFIG_SIZE_32);
+ break;
+ case 64:
+ regmap_update_bits(dc->regs, VSDC_CURSOR_CONFIG(output),
+ VSDC_CURSOR_CONFIG_SIZE_MASK,
+ VSDC_CURSOR_CONFIG_SIZE_64);
+ break;
+ case 128:
+ regmap_update_bits(dc->regs, VSDC_CURSOR_CONFIG(output),
+ VSDC_CURSOR_CONFIG_SIZE_MASK,
+ VSDC_CURSOR_CONFIG_SIZE_128);
+ break;
+ case 256:
+ regmap_update_bits(dc->regs, VSDC_CURSOR_CONFIG(output),
+ VSDC_CURSOR_CONFIG_SIZE_MASK,
+ VSDC_CURSOR_CONFIG_SIZE_256);
+ break;
+ }
+
+ dma_addr = vs_fb_get_dma_addr(fb, &state->src);
+
+ regmap_write(dc->regs, VSDC_CURSOR_ADDRESS(output),
+ lower_32_bits(dma_addr));
+
+ /*
+ * The X_OFF and Y_OFF fields define which point does the LOCATION
+ * register represent in the cursor image, and LOCATION register
+ * values are unsigned. To for positive left-top coordinates the
+ * offset is set to 0 and the location is set to the coordinate, for
+ * negative coordinates the location is set to 0 and the offset
+ * is set to the opposite number of the coordinate to offset the
+ * cursor image partly off-screen.
+ */
+ if (state->crtc_x >= 0) {
+ regmap_update_bits(dc->regs, VSDC_CURSOR_CONFIG(output),
+ VSDC_CURSOR_CONFIG_X_OFF_MASK, 0);
+ regmap_update_bits(dc->regs, VSDC_CURSOR_LOCATION(output),
+ VSDC_CURSOR_LOCATION_X_MASK,
+ VSDC_CURSOR_LOCATION_X(state->crtc_x));
+ } else {
+ regmap_update_bits(dc->regs, VSDC_CURSOR_CONFIG(output),
+ VSDC_CURSOR_CONFIG_X_OFF_MASK,
+ -state->crtc_x);
+ regmap_update_bits(dc->regs, VSDC_CURSOR_LOCATION(output),
+ VSDC_CURSOR_LOCATION_X_MASK, 0);
+ }
+
+ if (state->crtc_y >= 0) {
+ regmap_update_bits(dc->regs, VSDC_CURSOR_CONFIG(output),
+ VSDC_CURSOR_CONFIG_Y_OFF_MASK, 0);
+ regmap_update_bits(dc->regs, VSDC_CURSOR_LOCATION(output),
+ VSDC_CURSOR_LOCATION_Y_MASK,
+ VSDC_CURSOR_LOCATION_Y(state->crtc_y));
+ } else {
+ regmap_update_bits(dc->regs, VSDC_CURSOR_CONFIG(output),
+ VSDC_CURSOR_CONFIG_Y_OFF_MASK,
+ -state->crtc_y);
+ regmap_update_bits(dc->regs, VSDC_CURSOR_LOCATION(output),
+ VSDC_CURSOR_LOCATION_Y_MASK, 0);
+ }
+
+ vs_cursor_plane_commit(dc, output);
+}
+
+static const struct drm_plane_helper_funcs vs_cursor_plane_helper_funcs = {
+ .atomic_check = vs_cursor_plane_atomic_check,
+ .atomic_update = vs_cursor_plane_atomic_update,
+ .atomic_enable = vs_cursor_plane_atomic_enable,
+ .atomic_disable = vs_cursor_plane_atomic_disable,
+};
+
+static const struct drm_plane_funcs vs_cursor_plane_funcs = {
+ .atomic_destroy_state = drm_atomic_helper_plane_destroy_state,
+ .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state,
+ .disable_plane = drm_atomic_helper_disable_plane,
+ .reset = drm_atomic_helper_plane_reset,
+ .update_plane = drm_atomic_helper_update_plane,
+};
+
+static const u32 vs_cursor_plane_formats[] = {
+ DRM_FORMAT_ARGB8888,
+};
+
+static const u64 vs_cursor_plane_modifiers[] = {
+ DRM_FORMAT_MOD_LINEAR,
+ DRM_FORMAT_MOD_INVALID, /* sentinel */
+};
+
+struct drm_plane *vs_cursor_plane_init(struct drm_device *drm_dev,
+ struct vs_dc *dc)
+{
+ int32_t max_cursor_size = dc->identity.max_cursor_size;
+ struct drm_plane *plane;
+
+ if (drm_WARN_ON_ONCE(drm_dev, max_cursor_size < VSDC_MIN_CURSOR_SIZE ||
+ max_cursor_size > VSDC_MAX_CURSOR_SIZE))
+ return ERR_PTR(-EINVAL);
+
+ plane = drmm_universal_plane_alloc(drm_dev, struct drm_plane, dev, 0,
+ &vs_cursor_plane_funcs,
+ vs_cursor_plane_formats,
+ ARRAY_SIZE(vs_cursor_plane_formats),
+ vs_cursor_plane_modifiers,
+ DRM_PLANE_TYPE_CURSOR,
+ NULL);
+
+ if (IS_ERR(plane))
+ return plane;
+
+ drm_plane_helper_add(plane, &vs_cursor_plane_helper_funcs);
+
+ return plane;
+}
diff --git a/drivers/gpu/drm/verisilicon/vs_cursor_plane_regs.h b/drivers/gpu/drm/verisilicon/vs_cursor_plane_regs.h
new file mode 100644
index 000000000000..99693f2c95b9
--- /dev/null
+++ b/drivers/gpu/drm/verisilicon/vs_cursor_plane_regs.h
@@ -0,0 +1,44 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Copyright (C) 2025 Icenowy Zheng <uwu@icenowy.me>
+ *
+ * Based on vs_dc_hw.h, which is:
+ * Copyright (C) 2023 VeriSilicon Holdings Co., Ltd.
+ */
+
+#ifndef _VS_CURSOR_PLANE_REGS_H_
+#define _VS_CURSOR_PLANE_REGS_H_
+
+#include <linux/bits.h>
+
+#define VSDC_CURSOR_CONFIG(n) (0x1468 + 0x1080 * (n))
+#define VSDC_CURSOR_CONFIG_FMT_MASK GENMASK(1, 0)
+#define VSDC_CURSOR_CONFIG_FMT_ARGB8888 (0x2 << 0)
+#define VSDC_CURSOR_CONFIG_FMT_OFF (0x0 << 0)
+#define VSDC_CURSOR_CONFIG_IMG_UPDATE BIT(2)
+#define VSDC_CURSOR_CONFIG_COMMIT BIT(3)
+#define VSDC_CURSOR_CONFIG_SIZE_MASK GENMASK(7, 5)
+#define VSDC_CURSOR_CONFIG_SIZE_32 (0x0 << 5)
+#define VSDC_CURSOR_CONFIG_SIZE_64 (0x1 << 5)
+#define VSDC_CURSOR_CONFIG_SIZE_128 (0x2 << 5)
+#define VSDC_CURSOR_CONFIG_SIZE_256 (0x3 << 5)
+#define VSDC_CURSOR_CONFIG_Y_OFF_MASK GENMASK(12, 8)
+#define VSDC_CURSOR_CONFIG_Y_OFF(v) ((v) << 8)
+#define VSDC_CURSOR_CONFIG_X_OFF_MASK GENMASK(20, 16)
+#define VSDC_CURSOR_CONFIG_X_OFF(v) ((v) << 16)
+
+#define VSDC_CURSOR_ADDRESS(n) (0x146C + 0x1080 * (n))
+
+#define VSDC_CURSOR_LOCATION(n) (0x1470 + 0x1080 * (n))
+#define VSDC_CURSOR_LOCATION_X_MASK GENMASK(14, 0)
+#define VSDC_CURSOR_LOCATION_X(v) ((v) << 0)
+#define VSDC_CURSOR_LOCATION_Y_MASK GENMASK(30, 16)
+#define VSDC_CURSOR_LOCATION_Y(v) ((v) << 16)
+
+#define VSDC_CURSOR_BACKGROUND(n) (0x1474 + 0x1080 * (n))
+#define VSDC_CURSOR_BACKGRUOND_DEFAULT 0x00FFFFFF
+
+#define VSDC_CURSOR_FOREGROUND(n) (0x1478 + 0x1080 * (n))
+#define VSDC_CURSOR_FOREGRUOND_DEFAULT 0x00AAAAAA
+
+#endif /* _VS_CURSOR_PLANE_REGS_H_ */
diff --git a/drivers/gpu/drm/verisilicon/vs_hwdb.c b/drivers/gpu/drm/verisilicon/vs_hwdb.c
index 09336af0900a..2a0f7c59afa3 100644
--- a/drivers/gpu/drm/verisilicon/vs_hwdb.c
+++ b/drivers/gpu/drm/verisilicon/vs_hwdb.c
@@ -95,6 +95,7 @@ static struct vs_chip_identity vs_chip_identities[] = {
.customer_id = ~0U,
.display_count = 2,
+ .max_cursor_size = 64,
.formats = &vs_formats_no_yuv444,
},
{
@@ -103,6 +104,7 @@ static struct vs_chip_identity vs_chip_identities[] = {
.customer_id = 0x30B,
.display_count = 2,
+ .max_cursor_size = 64,
.formats = &vs_formats_no_yuv444,
},
{
@@ -111,6 +113,7 @@ static struct vs_chip_identity vs_chip_identities[] = {
.customer_id = 0x310,
.display_count = 2,
+ .max_cursor_size = 64,
.formats = &vs_formats_with_yuv444,
},
{
@@ -119,6 +122,7 @@ static struct vs_chip_identity vs_chip_identities[] = {
.customer_id = 0x311,
.display_count = 2,
+ .max_cursor_size = 64,
.formats = &vs_formats_no_yuv444,
},
};
diff --git a/drivers/gpu/drm/verisilicon/vs_hwdb.h b/drivers/gpu/drm/verisilicon/vs_hwdb.h
index 92192e4fa086..2065ecb73043 100644
--- a/drivers/gpu/drm/verisilicon/vs_hwdb.h
+++ b/drivers/gpu/drm/verisilicon/vs_hwdb.h
@@ -20,6 +20,11 @@ struct vs_chip_identity {
u32 customer_id;
u32 display_count;
+ /*
+ * The hardware only supports square cursor planes, so this field
+ * is both the maximum width and height in pixels.
+ */
+ int32_t max_cursor_size;
const struct vs_formats *formats;
};
diff --git a/drivers/gpu/drm/verisilicon/vs_plane.h b/drivers/gpu/drm/verisilicon/vs_plane.h
index f18c36ef4cd9..32ed1d70f3b8 100644
--- a/drivers/gpu/drm/verisilicon/vs_plane.h
+++ b/drivers/gpu/drm/verisilicon/vs_plane.h
@@ -84,5 +84,6 @@ void vs_plane_destroy_state(struct drm_plane *plane,
void vs_plane_reset(struct drm_plane *plane);
struct drm_plane *vs_primary_plane_init(struct drm_device *dev, struct vs_dc *dc);
+struct drm_plane *vs_cursor_plane_init(struct drm_device *dev, struct vs_dc *dc);
#endif /* _VS_PLANE_H_ */
diff --git a/drivers/gpu/drm/xe/xe_validation.c b/drivers/gpu/drm/xe/xe_validation.c
index a611438eaafe..8dff4d0ec895 100644
--- a/drivers/gpu/drm/xe/xe_validation.c
+++ b/drivers/gpu/drm/xe/xe_validation.c
@@ -156,7 +156,7 @@ int xe_validation_ctx_init(struct xe_validation_ctx *ctx, struct xe_validation_d
#ifdef CONFIG_DEBUG_WW_MUTEX_SLOWPATH
/*
- * This abuses both drm_exec and ww_mutex internals and should be
+ * This abuses ww_mutex internals and should be
* replaced by checking for -EDEADLK when we can make TTM
* stop converting -EDEADLK to -ENOMEM.
* An alternative is to not have exhaustive eviction with
@@ -164,7 +164,7 @@ int xe_validation_ctx_init(struct xe_validation_ctx *ctx, struct xe_validation_d
*/
static bool xe_validation_contention_injected(struct drm_exec *exec)
{
- return !!exec->ticket.contending_lock;
+ return !!drm_exec_ticket(exec)->contending_lock;
}
#else
diff --git a/drivers/gpu/drm/xe/xe_validation.h b/drivers/gpu/drm/xe/xe_validation.h
index a30e732c4d51..4cd955ce6cd2 100644
--- a/drivers/gpu/drm/xe/xe_validation.h
+++ b/drivers/gpu/drm/xe/xe_validation.h
@@ -146,7 +146,7 @@ bool xe_validation_should_retry(struct xe_validation_ctx *ctx, int *ret);
#define xe_validation_retry_on_oom(_ctx, _ret) \
do { \
if (xe_validation_should_retry(_ctx, _ret)) \
- goto *__drm_exec_retry_ptr; \
+ drm_exec_retry((_ctx)->exec); \
} while (0)
/**
diff --git a/drivers/gpu/drm/xe/xe_vm.c b/drivers/gpu/drm/xe/xe_vm.c
index b01f31ed4417..080c2fff0e95 100644
--- a/drivers/gpu/drm/xe/xe_vm.c
+++ b/drivers/gpu/drm/xe/xe_vm.c
@@ -373,7 +373,6 @@ int xe_vm_validate_rebind(struct xe_vm *vm, struct drm_exec *exec,
unsigned int num_fences)
{
struct drm_gem_object *obj;
- unsigned long index;
int ret;
do {
@@ -386,7 +385,7 @@ int xe_vm_validate_rebind(struct xe_vm *vm, struct drm_exec *exec,
return ret;
} while (!list_empty(&vm->gpuvm.evict.list));
- drm_exec_for_each_locked_object(exec, index, obj) {
+ drm_exec_for_each_locked_object(exec, obj) {
ret = dma_resv_reserve_fences(obj->resv, num_fences);
if (ret)
return ret;
diff --git a/drivers/gpu/host1x/bus.c b/drivers/gpu/host1x/bus.c
index f814eb4941c0..e3884096c2fe 100644
--- a/drivers/gpu/host1x/bus.c
+++ b/drivers/gpu/host1x/bus.c
@@ -221,7 +221,7 @@ int host1x_device_init(struct host1x_device *device)
teardown:
list_for_each_entry_continue_reverse(client, &device->clients, list)
- if (client->ops->exit)
+ if (client->ops && client->ops->exit)
client->ops->exit(client);
/* reset client to end of list for late teardown */
@@ -229,7 +229,7 @@ teardown:
teardown_late:
list_for_each_entry_continue_reverse(client, &device->clients, list)
- if (client->ops->late_exit)
+ if (client->ops && client->ops->late_exit)
client->ops->late_exit(client);
mutex_unlock(&device->clients_lock);
@@ -485,7 +485,7 @@ static int host1x_device_add(struct host1x *host1x,
err = host1x_device_parse_dt(device, driver);
if (err < 0) {
- kfree(device);
+ put_device(&device->dev);
return err;
}
@@ -887,6 +887,20 @@ unlock:
}
EXPORT_SYMBOL(host1x_client_resume);
+/**
+ * host1x_bo_pin() - Create a DMA mapping for the buffer object
+ * @dev: Device onto which DMA map to
+ * @bo: Buffer object to map
+ * @dir: DMA direction
+ * @cache: Cache in which to store mapping, or NULL
+ *
+ * Creates a DMA mapping pointing to @bo for @dev. The refcount of @bo is incremented
+ * until host1x_bo_unpin is called.
+ *
+ * If @cache is specified, the mapping is also stored in the cache and not released
+ * until @bo is freed (refcount drops to zero). This improves performance when a buffer
+ * is pinned and unpinned frequently as in the case of display use.
+ */
struct host1x_bo_mapping *host1x_bo_pin(struct device *dev, struct host1x_bo *bo,
enum dma_data_direction dir,
struct host1x_bo_cache *cache)
@@ -899,6 +913,7 @@ struct host1x_bo_mapping *host1x_bo_pin(struct device *dev, struct host1x_bo *bo
list_for_each_entry(mapping, &cache->mappings, entry) {
if (mapping->bo == bo && mapping->direction == dir) {
kref_get(&mapping->ref);
+ host1x_bo_get(bo);
goto unlock;
}
}
@@ -908,6 +923,8 @@ struct host1x_bo_mapping *host1x_bo_pin(struct device *dev, struct host1x_bo *bo
if (IS_ERR(mapping))
goto unlock;
+ host1x_bo_get(bo);
+
spin_lock(&mapping->bo->lock);
list_add_tail(&mapping->list, &bo->mappings);
spin_unlock(&mapping->bo->lock);
@@ -918,7 +935,12 @@ struct host1x_bo_mapping *host1x_bo_pin(struct device *dev, struct host1x_bo *bo
list_add_tail(&mapping->entry, &cache->mappings);
- /* bump reference count to track the copy in the cache */
+ /*
+ * Bump the mapping reference count to track the mapping in the cache,
+ * but do not bump the BO's refcount. This allows the BO to still get freed,
+ * triggering the release of the cache mapping through
+ * host1x_bo_clear_cached_mappings.
+ */
kref_get(&mapping->ref);
}
@@ -948,9 +970,17 @@ static void __host1x_bo_unpin(struct kref *ref)
mapping->bo->ops->unpin(mapping);
}
+/**
+ * host1x_bo_unpin() - Release an established DMA mapping of a buffer object
+ * @mapping: Mapping to release
+ *
+ * Unmaps the given @mapping, unless it is cached. Decreases the refcount on
+ * the underlying buffer object.
+ */
void host1x_bo_unpin(struct host1x_bo_mapping *mapping)
{
struct host1x_bo_cache *cache = mapping->cache;
+ struct host1x_bo *bo = mapping->bo;
if (cache)
mutex_lock(&cache->lock);
@@ -959,5 +989,33 @@ void host1x_bo_unpin(struct host1x_bo_mapping *mapping)
if (cache)
mutex_unlock(&cache->lock);
+
+ host1x_bo_put(bo);
}
EXPORT_SYMBOL(host1x_bo_unpin);
+
+/**
+ * host1x_bo_clear_cached_mappings() - Remove all cached mappings pointing at a bo
+ * @bo: Buffer object to release mappings of
+ *
+ * Drops references to any mappings pointing to @bo left in any caches. This must
+ * be called by any host1x_bo implementers that may be pinned with caching enabled
+ * before freeing the bo.
+ */
+void host1x_bo_clear_cached_mappings(struct host1x_bo *bo)
+{
+ struct host1x_bo_mapping *mapping, *tmp;
+ struct host1x_bo_cache *cache;
+
+ list_for_each_entry_safe(mapping, tmp, &bo->mappings, list) {
+ cache = mapping->cache;
+ if (WARN_ON(!cache))
+ continue;
+
+ mutex_lock(&mapping->cache->lock);
+ WARN_ON(kref_read(&mapping->ref) != 1);
+ __host1x_bo_unpin(&mapping->ref);
+ mutex_unlock(&mapping->cache->lock);
+ }
+}
+EXPORT_SYMBOL(host1x_bo_clear_cached_mappings);
diff --git a/drivers/gpu/host1x/context.c b/drivers/gpu/host1x/context.c
index d50d41c20561..512f3d189ebf 100644
--- a/drivers/gpu/host1x/context.c
+++ b/drivers/gpu/host1x/context.c
@@ -4,6 +4,7 @@
*/
#include <linux/device.h>
+#include <linux/host1x_context_bus.h>
#include <linux/kref.h>
#include <linux/of.h>
#include <linux/of_device.h>
diff --git a/drivers/gpu/host1x/context.h b/drivers/gpu/host1x/context.h
index 3e03bc1d3bac..5ab5be1a6c63 100644
--- a/drivers/gpu/host1x/context.h
+++ b/drivers/gpu/host1x/context.h
@@ -12,8 +12,7 @@
#include <linux/refcount.h>
struct host1x;
-
-extern struct bus_type host1x_context_device_bus_type;
+struct host1x_memory_context;
struct host1x_memory_context_list {
struct mutex lock;
diff --git a/drivers/gpu/host1x/context_bus.c b/drivers/gpu/host1x/context_bus.c
index 7cd0e1a5edd1..398d56e94c73 100644
--- a/drivers/gpu/host1x/context_bus.c
+++ b/drivers/gpu/host1x/context_bus.c
@@ -4,6 +4,7 @@
*/
#include <linux/device.h>
+#include <linux/host1x_context_bus.h>
#include <linux/of.h>
const struct bus_type host1x_context_device_bus_type = {
diff --git a/drivers/gpu/host1x/intr.c b/drivers/gpu/host1x/intr.c
index f77a678949e9..723297250768 100644
--- a/drivers/gpu/host1x/intr.c
+++ b/drivers/gpu/host1x/intr.c
@@ -92,8 +92,12 @@ void host1x_intr_handle_interrupt(struct host1x *host, unsigned int id)
host1x_fence_signal(fence);
}
- /* Re-enable interrupt if necessary */
- host1x_intr_update_hw_state(host, sp);
+ /*
+ * Re-enable interrupt if necessary. The ISR already disabled the interrupt,
+ * so if no fences remain, no update is needed.
+ */
+ if (!list_empty(&sp->fences.list))
+ host1x_intr_update_hw_state(host, sp);
spin_unlock(&sp->fences.lock);
}
diff --git a/drivers/gpu/host1x/job.c b/drivers/gpu/host1x/job.c
index 3ed49e1fd933..70bda32f1ff4 100644
--- a/drivers/gpu/host1x/job.c
+++ b/drivers/gpu/host1x/job.c
@@ -235,6 +235,8 @@ static unsigned int pin_job(struct host1x *host, struct host1x_job *job)
}
if (host->domain) {
+ ssize_t map_err;
+
for_each_sgtable_sg(map->sgt, sg, j)
gather_size += sg->length;
@@ -248,11 +250,11 @@ static unsigned int pin_job(struct host1x *host, struct host1x_job *job)
goto put;
}
- err = iommu_map_sgtable(host->domain, iova_dma_addr(&host->iova, alloc),
- map->sgt, IOMMU_READ);
- if (err == 0) {
+ map_err = iommu_map_sgtable(host->domain, iova_dma_addr(&host->iova, alloc),
+ map->sgt, IOMMU_READ);
+ if (map_err < 0) {
__free_iova(&host->iova, alloc);
- err = -EINVAL;
+ err = map_err;
goto put;
}
diff --git a/drivers/gpu/host1x/mipi.c b/drivers/gpu/host1x/mipi.c
index 01513b775d89..988681423981 100644
--- a/drivers/gpu/host1x/mipi.c
+++ b/drivers/gpu/host1x/mipi.c
@@ -114,8 +114,10 @@ struct tegra_mipi_device *tegra_mipi_request(struct device *device,
if (err < 0)
return ERR_PTR(err);
- if (provider.np != args.np)
- return ERR_PTR(-ENODEV);
+ if (provider.np != args.np) {
+ err = -ENODEV;
+ goto out;
+ }
mipidev = kzalloc_obj(*mipidev);
if (!mipidev) {
diff --git a/drivers/gpu/host1x/syncpt.c b/drivers/gpu/host1x/syncpt.c
index acc7d82e0585..807c74fc6a0a 100644
--- a/drivers/gpu/host1x/syncpt.c
+++ b/drivers/gpu/host1x/syncpt.c
@@ -222,11 +222,12 @@ int host1x_syncpt_wait(struct host1x_syncpt *sp, u32 thresh, long timeout,
{
struct dma_fence *fence;
long wait_err;
+ u32 curr;
- host1x_hw_syncpt_load(sp->host, sp);
+ curr = host1x_syncpt_load(sp);
if (value)
- *value = host1x_syncpt_load(sp);
+ *value = curr;
if (host1x_syncpt_is_expired(sp, thresh))
return 0;
@@ -245,21 +246,25 @@ int host1x_syncpt_wait(struct host1x_syncpt *sp, u32 thresh, long timeout,
host1x_fence_cancel(fence);
dma_fence_put(fence);
- if (value)
- *value = host1x_syncpt_load(sp);
-
/*
* Don't rely on dma_fence_wait_timeout return value,
* since it returns zero both on timeout and if the
* wait completed with 0 jiffies left.
*/
- host1x_hw_syncpt_load(sp->host, sp);
- if (wait_err == 0 && !host1x_syncpt_is_expired(sp, thresh))
+ if (wait_err == 0 && !host1x_syncpt_is_expired(sp, thresh)) {
+ if (value)
+ *value = host1x_syncpt_load(sp);
+
return -EAGAIN;
- else if (wait_err < 0)
+ } else if (wait_err < 0) {
return wait_err;
- else
+ } else {
+ /* Success, read the value cached by ISR */
+ if (value)
+ *value = host1x_syncpt_read_min(sp);
+
return 0;
+ }
}
EXPORT_SYMBOL(host1x_syncpt_wait);
diff --git a/include/drm/drm_exec.h b/include/drm/drm_exec.h
index aa786b828a0a..8725ba92ff91 100644
--- a/include/drm/drm_exec.h
+++ b/include/drm/drm_exec.h
@@ -9,6 +9,12 @@
#define DRM_EXEC_INTERRUPTIBLE_WAIT BIT(0)
#define DRM_EXEC_IGNORE_DUPLICATES BIT(1)
+/*
+ * Dummy value used to initially enter the retry loop.
+ * internal use only.
+ */
+#define DRM_EXEC_DUMMY ((void *)~0)
+
struct drm_gem_object;
/**
@@ -65,31 +71,46 @@ drm_exec_obj(struct drm_exec *exec, unsigned long index)
return index < exec->num_objects ? exec->objects[index] : NULL;
}
+/* Helper for drm_exec_for_each_locked_object(). Internal use only. */
+#define __drm_exec_for_each_locked_object(exec, obj, __index) \
+ for (unsigned long __index = 0; ((obj) = drm_exec_obj(exec, __index)); ++__index)
/**
* drm_exec_for_each_locked_object - iterate over all the locked objects
* @exec: drm_exec object
- * @index: unsigned long index for the iteration
* @obj: the current GEM object
*
* Iterate over all the locked GEM objects inside the drm_exec object.
*/
-#define drm_exec_for_each_locked_object(exec, index, obj) \
- for ((index) = 0; ((obj) = drm_exec_obj(exec, index)); ++(index))
+#define drm_exec_for_each_locked_object(exec, obj) \
+ __drm_exec_for_each_locked_object(exec, obj, __UNIQUE_ID(drm_exec))
+/* Helper for drm_exec_for_each_locked_object_reverse(). Internal use only. */
+#define __drm_exec_for_each_locked_object_reverse(exec, obj, __index) \
+ for (unsigned long __index = (exec)->num_objects - 1; \
+ ((obj) = drm_exec_obj(exec, __index)); --__index)
/**
* drm_exec_for_each_locked_object_reverse - iterate over all the locked
* objects in reverse locking order
* @exec: drm_exec object
- * @index: unsigned long index for the iteration
* @obj: the current GEM object
*
* Iterate over all the locked GEM objects inside the drm_exec object in
- * reverse locking order. Note that @index may go below zero and wrap,
+ * reverse locking order. Note that the internal index may wrap around,
* but that will be caught by drm_exec_obj(), returning a NULL object.
*/
-#define drm_exec_for_each_locked_object_reverse(exec, index, obj) \
- for ((index) = (exec)->num_objects - 1; \
- ((obj) = drm_exec_obj(exec, index)); --(index))
+#define drm_exec_for_each_locked_object_reverse(exec, obj) \
+ __drm_exec_for_each_locked_object_reverse(exec, obj, __UNIQUE_ID(drm_exec))
+
+/*
+ * Helper to drm_exec_until_all_locked(). Don't use directly.
+ *
+ * Since labels can't be defined local to the loop's body we use a jump pointer
+ * to make sure that the retry is only used from within the loop's body.
+ */
+#define __drm_exec_until_all_locked(exec, _label) \
+_label: \
+ for (void *const __maybe_unused __drm_exec_retry_ptr = &&_label; \
+ drm_exec_cleanup(exec);)
/**
* drm_exec_until_all_locked - loop until all GEM objects are locked
@@ -98,17 +119,9 @@ drm_exec_obj(struct drm_exec *exec, unsigned long index)
* Core functionality of the drm_exec object. Loops until all GEM objects are
* locked and no more contention exists. At the beginning of the loop it is
* guaranteed that no GEM object is locked.
- *
- * Since labels can't be defined local to the loops body we use a jump pointer
- * to make sure that the retry is only used from within the loops body.
*/
#define drm_exec_until_all_locked(exec) \
-__PASTE(__drm_exec_, __LINE__): \
- for (void *__drm_exec_retry_ptr; ({ \
- __drm_exec_retry_ptr = &&__PASTE(__drm_exec_, __LINE__);\
- (void)__drm_exec_retry_ptr; \
- drm_exec_cleanup(exec); \
- });)
+ __drm_exec_until_all_locked(exec, __UNIQUE_ID(drm_exec))
/**
* drm_exec_retry_on_contention - restart the loop to grap all locks
@@ -135,6 +148,30 @@ static inline bool drm_exec_is_contended(struct drm_exec *exec)
return !!exec->contended;
}
+/**
+ * drm_exec_retry() - Unconditionally restart the loop to grab all locks.
+ * @exec: drm_exec object
+ *
+ * Unconditionally retry the loop to lock all objects. For consistency,
+ * the exec object needs to be newly initialized.
+ */
+#define drm_exec_retry(_exec) \
+ do { \
+ WARN_ON((_exec)->contended != DRM_EXEC_DUMMY); \
+ goto *__drm_exec_retry_ptr; \
+ } while (0)
+
+/**
+ * drm_exec_ticket - return the ww_acquire_ctx for this exec context
+ * @exec: drm_exec object
+ *
+ * Return: Pointer to the ww_acquire_ctx embedded in @exec.
+ */
+static inline struct ww_acquire_ctx *drm_exec_ticket(struct drm_exec *exec)
+{
+ return &exec->ticket;
+}
+
void drm_exec_init(struct drm_exec *exec, u32 flags, unsigned nr);
void drm_exec_fini(struct drm_exec *exec);
bool drm_exec_cleanup(struct drm_exec *exec);
diff --git a/include/linux/host1x.h b/include/linux/host1x.h
index 1f5f55917d1c..a7a675783136 100644
--- a/include/linux/host1x.h
+++ b/include/linux/host1x.h
@@ -143,6 +143,12 @@ static inline struct host1x_bo_mapping *to_host1x_bo_mapping(struct kref *ref)
return container_of(ref, struct host1x_bo_mapping, ref);
}
+/**
+ * struct host1x_bo_ops - operations implemented by a host1x_bo provider
+ *
+ * @pin: create a DMA mapping. Implementation must not touch the bo's refcount.
+ * @unpin: destroy a DMA mapping. Implementation must not touch the bo's refcount.
+ */
struct host1x_bo_ops {
struct host1x_bo *(*get)(struct host1x_bo *bo);
void (*put)(struct host1x_bo *bo);
@@ -181,6 +187,7 @@ struct host1x_bo_mapping *host1x_bo_pin(struct device *dev, struct host1x_bo *bo
enum dma_data_direction dir,
struct host1x_bo_cache *cache);
void host1x_bo_unpin(struct host1x_bo_mapping *map);
+void host1x_bo_clear_cached_mappings(struct host1x_bo *bo);
static inline void *host1x_bo_mmap(struct host1x_bo *bo)
{
diff --git a/include/trace/events/host1x.h b/include/trace/events/host1x.h
index 1ba84b738e46..1b6aeb7b177b 100644
--- a/include/trace/events/host1x.h
+++ b/include/trace/events/host1x.h
@@ -21,9 +21,11 @@ struct host1x_bo;
DECLARE_EVENT_CLASS(host1x,
TP_PROTO(const char *name),
TP_ARGS(name),
- TP_STRUCT__entry(__field(const char *, name)),
- TP_fast_assign(__entry->name = name;),
- TP_printk("name=%s", __entry->name)
+ TP_STRUCT__entry(__string(name, name)),
+ TP_fast_assign(
+ __assign_str(name);
+ ),
+ TP_printk("name=%s", __get_str(name))
);
DEFINE_EVENT(host1x, host1x_channel_open,
@@ -52,19 +54,19 @@ TRACE_EVENT(host1x_cdma_push,
TP_ARGS(name, op1, op2),
TP_STRUCT__entry(
- __field(const char *, name)
+ __string(name, name)
__field(u32, op1)
__field(u32, op2)
),
TP_fast_assign(
- __entry->name = name;
+ __assign_str(name);
__entry->op1 = op1;
__entry->op2 = op2;
),
TP_printk("name=%s, op1=%08x, op2=%08x",
- __entry->name, __entry->op1, __entry->op2)
+ __get_str(name), __entry->op1, __entry->op2)
);
TRACE_EVENT(host1x_cdma_push_wide,
@@ -73,7 +75,7 @@ TRACE_EVENT(host1x_cdma_push_wide,
TP_ARGS(name, op1, op2, op3, op4),
TP_STRUCT__entry(
- __field(const char *, name)
+ __string(name, name)
__field(u32, op1)
__field(u32, op2)
__field(u32, op3)
@@ -81,7 +83,7 @@ TRACE_EVENT(host1x_cdma_push_wide,
),
TP_fast_assign(
- __entry->name = name;
+ __assign_str(name);
__entry->op1 = op1;
__entry->op2 = op2;
__entry->op3 = op3;
@@ -89,7 +91,7 @@ TRACE_EVENT(host1x_cdma_push_wide,
),
TP_printk("name=%s, op1=%08x, op2=%08x, op3=%08x op4=%08x",
- __entry->name, __entry->op1, __entry->op2, __entry->op3,
+ __get_str(name), __entry->op1, __entry->op2, __entry->op3,
__entry->op4)
);
@@ -100,7 +102,7 @@ TRACE_EVENT(host1x_cdma_push_gather,
TP_ARGS(name, bo, words, offset, cmdbuf),
TP_STRUCT__entry(
- __field(const char *, name)
+ __string(name, name)
__field(struct host1x_bo *, bo)
__field(u32, words)
__field(u32, offset)
@@ -114,14 +116,14 @@ TRACE_EVENT(host1x_cdma_push_gather,
words * sizeof(u32));
}
__entry->cmdbuf = cmdbuf;
- __entry->name = name;
+ __assign_str(name);
__entry->bo = bo;
__entry->words = words;
__entry->offset = offset;
),
TP_printk("name=%s, bo=%p, words=%u, offset=%d, contents=[%s]",
- __entry->name, __entry->bo,
+ __get_str(name), __entry->bo,
__entry->words, __entry->offset,
__print_hex(__get_dynamic_array(cmdbuf),
__entry->cmdbuf ? __entry->words * 4 : 0))
@@ -134,7 +136,7 @@ TRACE_EVENT(host1x_channel_submit,
TP_ARGS(name, cmdbufs, relocs, syncpt_id, syncpt_incrs),
TP_STRUCT__entry(
- __field(const char *, name)
+ __string(name, name)
__field(u32, cmdbufs)
__field(u32, relocs)
__field(u32, syncpt_id)
@@ -142,7 +144,7 @@ TRACE_EVENT(host1x_channel_submit,
),
TP_fast_assign(
- __entry->name = name;
+ __assign_str(name);
__entry->cmdbufs = cmdbufs;
__entry->relocs = relocs;
__entry->syncpt_id = syncpt_id;
@@ -151,7 +153,7 @@ TRACE_EVENT(host1x_channel_submit,
TP_printk("name=%s, cmdbufs=%u, relocs=%u, syncpt_id=%u, "
"syncpt_incrs=%u",
- __entry->name, __entry->cmdbufs, __entry->relocs,
+ __get_str(name), __entry->cmdbufs, __entry->relocs,
__entry->syncpt_id, __entry->syncpt_incrs)
);
@@ -161,19 +163,19 @@ TRACE_EVENT(host1x_channel_submitted,
TP_ARGS(name, syncpt_base, syncpt_max),
TP_STRUCT__entry(
- __field(const char *, name)
+ __string(name, name)
__field(u32, syncpt_base)
__field(u32, syncpt_max)
),
TP_fast_assign(
- __entry->name = name;
+ __assign_str(name);
__entry->syncpt_base = syncpt_base;
__entry->syncpt_max = syncpt_max;
),
TP_printk("name=%s, syncpt_base=%d, syncpt_max=%d",
- __entry->name, __entry->syncpt_base, __entry->syncpt_max)
+ __get_str(name), __entry->syncpt_base, __entry->syncpt_max)
);
TRACE_EVENT(host1x_channel_submit_complete,
@@ -182,19 +184,19 @@ TRACE_EVENT(host1x_channel_submit_complete,
TP_ARGS(name, count, thresh),
TP_STRUCT__entry(
- __field(const char *, name)
+ __string(name, name)
__field(int, count)
__field(u32, thresh)
),
TP_fast_assign(
- __entry->name = name;
+ __assign_str(name);
__entry->count = count;
__entry->thresh = thresh;
),
TP_printk("name=%s, count=%d, thresh=%d",
- __entry->name, __entry->count, __entry->thresh)
+ __get_str(name), __entry->count, __entry->thresh)
);
TRACE_EVENT(host1x_wait_cdma,
@@ -203,16 +205,16 @@ TRACE_EVENT(host1x_wait_cdma,
TP_ARGS(name, eventid),
TP_STRUCT__entry(
- __field(const char *, name)
+ __string(name, name)
__field(u32, eventid)
),
TP_fast_assign(
- __entry->name = name;
+ __assign_str(name);
__entry->eventid = eventid;
),
- TP_printk("name=%s, event=%d", __entry->name, __entry->eventid)
+ TP_printk("name=%s, event=%d", __get_str(name), __entry->eventid)
);
TRACE_EVENT(host1x_syncpt_load_min,
diff --git a/include/uapi/drm/tegra_drm.h b/include/uapi/drm/tegra_drm.h
index 94cfc306d50a..8f21f3a44832 100644
--- a/include/uapi/drm/tegra_drm.h
+++ b/include/uapi/drm/tegra_drm.h
@@ -304,6 +304,7 @@ struct drm_tegra_cmdbuf {
* struct drm_tegra_reloc - GEM object relocation structure
*/
struct drm_tegra_reloc {
+ /** @cmdbuf: cmd information */
struct {
/**
* @cmdbuf.handle:
@@ -321,6 +322,7 @@ struct drm_tegra_reloc {
*/
__u32 offset;
} cmdbuf;
+ /** @target: relocate target information */
struct {
/**
* @target.handle:
@@ -778,6 +780,9 @@ struct drm_tegra_channel_unmap {
/* Submission */
/**
+ * define DRM_TEGRA_SUBMIT_RELOC_SECTOR_LAYOUT - \
+ * Select sector layout swizzling for in-memory buffers.
+ *
* Specify that bit 39 of the patched-in address should be set to switch
* swizzling between Tegra and non-Tegra sector layout on systems that store
* surfaces in system memory in non-Tegra sector layout.
@@ -830,16 +835,27 @@ struct drm_tegra_submit_buf {
};
/**
+ * define DRM_TEGRA_SUBMIT_CMD_GATHER_UPTR - \
+ * Execute Host1x opcodes from user pointer.
+ *
* Execute `words` words of Host1x opcodes specified in the `gather_data_ptr`
* buffer. Each GATHER_UPTR command uses successive words from the buffer.
*/
#define DRM_TEGRA_SUBMIT_CMD_GATHER_UPTR 0
+
/**
+ * define DRM_TEGRA_SUBMIT_CMD_WAIT_SYNCPT - \
+ * Wait for syncpoint (absolute).
+ *
* Wait for a syncpoint to reach a value before continuing with further
* commands.
*/
#define DRM_TEGRA_SUBMIT_CMD_WAIT_SYNCPT 1
+
/**
+ * define DRM_TEGRA_SUBMIT_CMD_WAIT_SYNCPT_RELATIVE - \
+ * Wait for syncpoint (relative).
+ *
* Wait for a syncpoint to reach a value before continuing with further
* commands. The threshold is calculated relative to the start of the job.
*/