summaryrefslogtreecommitdiff
path: root/drivers/gpu/drm/vc4
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2023-02-23 05:28:03 +0300
committerLinus Torvalds <torvalds@linux-foundation.org>2023-02-23 05:28:03 +0300
commita5c95ca18a98d742d0a4a04063c32556b5b66378 (patch)
treefdd897b23a1c45b3d03bd1e75e5df42057f339d1 /drivers/gpu/drm/vc4
parent307e14c039063f0c9bd7a18a7add8f940580dcc9 (diff)
parenta48bba98380cb0b43dcd01d276c7efc282e3c33f (diff)
downloadlinux-a5c95ca18a98d742d0a4a04063c32556b5b66378.tar.xz
Merge tag 'drm-next-2023-02-23' of git://anongit.freedesktop.org/drm/drm
Pull drm updates from Dave Airlie: "There are a bunch of changes all over in the usual places. Highlights: - habanalabs moves from misc to accel - first accel driver for Intel VPU (Versatile Processing Unit) inference engine - dropped all the ancient legacy DRI1 drivers. I think it's been at least 10 years since anyone has heard about these. - Intel DG2 updates and prelim Meteorlake enablement - etnaviv adds support for Versilicon NPU device (a GPU like engine with inference accelerators) Detailed summary: Removals: - remove legacy dri1 drivers: i810, mga, r128, savage, sis, tdfx, via New driver: - intel VPU accelerator driver - habanalabs comes via drm tree now drm/core: - use drm_dbg_ helpers in several places - Document defaults for CRTC backgrounds - Document use of drm_minor edid: - improve mode parsing and refactoring connector: - support analog TV mode property media: - add some common formats udmabuf: - add vmap/vunmap methods fourcc: - add XRGB1555 and RGB565 formats - document open source user waiver firmware: - fix color-format selection for system framebuffer format-helper: - Add conversion from XRGB8888 to various sysfb formats - Make XRGB8888 the only driver-emulated legacy format - Add conversion from XRGB8888 to XBGR8888 and ABGR8888 fb-helper: - fix preferred depth and bpp values across drivers - Avoid blank consoles from selecting an incorrect color format probe-helper: - Enable/disable HPD on connectors scheduler: - Fix lockup in drm_sched_entity_kill() - Deprecate drm_sched_resubmit_jobs() bridge: - remove unused functions - implement i2c probe_new in various drivers - ite-it6505: Locking fixes, Cache EDID data - ite-it66121: Support IT6610 chip - lontium-tl9611: Fix HDMI on DragonBoard 845c - parade-ps8640: Use atomic bridge functions - Support i.MX93 LDB plus DT bindings debugfs: - add per device helpers and convert drivers displayport: - mst fixes - add DP adaptive sync DPCD definitions fbdev: - always pick 32bpp as default - remove some unused code simpledrm: - support system memory framebuffers panel: - add orientation quirks for Lenovo Yoga Tab 3 X90F and DynaBook K50 - Use ktime_get_boottime() to measure power-down delay - Fix auto-suspend delay - Visionox VTDR6130 AMOLED DSI - Support Himax HX8394 - Convert many drivers to common generic DSI write-sequence helper - AUO A030JTN01 ttm: - drop bo wait wrapper - fix MIPS build habanalabs: - moved driver to accel subsystem - gaudi2 decoder error improvement - more trace events - Gaudi2 abrupt reset by firmware support - add uAPI to flush memory transactions - add uAPI to pass through userspace reqs to fw - remove dma-buf export by handle amdgpu: - add new INFO queries for peak and min sclk/mclk for profile modes - Add PCIe info to the INFO IOCTL - secure display support for multiple displays - DML optimizations - DCN 3.2 updates - PSR updates - DP 2.1 updates - SR-IOV RAS updates - VCN RAS support - SMU 13.x updates - Switch 1 element arrays to flexible arrays - Add RAS support for DF 4.3 - Stack size improvements - S0ix rework - Allow 0 as a vram limit on APUs - Handle profiling modes for SMU13.x - Fix possible segfault in failure case - Rework FW requests to happen in early_init for all IPs so that we don't lose the sbios console if FW is missing - Fix power reporting on certain firmwares for CZN/RN - Allow S0ix without BIOS support - Enable freesync over PCon - Re-enable the AGP aperture on GMC 11.x amdkfd: - Error handling fixes - PASID fixes - Fix for cleared VRAM BOs - Fix cleanup if GPUVM creation fails - Memory accounting fix - Use resource_size rather than open codeing it - GC11 mGPU fix radeon: - Switch 1 element arrays to flexible arrays - Fix memory leak on shutdown - move to new logging i915: - Meteorlake display/OA/GSC fw/workarounds enabling - DP MST DSC support - Gamma/degamma readout support for the state checker - Enable SDP split support for DP 2.0 - Add probe blocking support to i915.force_probe parameter - Enable Xe HP 4tile support - Avoid display direct calls to uncore - Fix HuC delayed load memory leaks - Add DG2 workarounds Wa_18018764978 and Wa_18019271663 - Improve suspend / resume times with VT-d scanout workaround active - Fix DG2 visual corruption on small BAR systems by not forgetting to copy CCS aux state - Fix TLB invalidation for Gen12.50 video and compute engines - Enable HF-EEODB by switching HDMI, DP and LVDS to use struct drm_edid - Start using unversioned DMC firmware paths for new platforms - ELD refactor: Stop using hardware buffer, precompute ELD - lots of display code refactoring nouveau: - drop legacy ioctl support - replace 0-sized array msm: - dpu/dsi/mdss: Support for SM8350, SM8450 SM8550 and SC8280XP platform - Added bindings for SM8150 - dpu: Partial support for DSC on SM8150 and SM8250 - dpu: Fixed color transformation matrix being lost on suspend/resume - dp: Support SDM845 and SC8280XP platforms - dp: Support for limiting DP link rate via DT property - dsi: Validate display modes according to the DSI OPP table - dsi: DSI PHY support for the SM6375 platform - Add MSM_SUBMIT_BO_NO_IMPLICI - a2xx: Support to load legacy firmware - a6xx: GPU devcore dump updates for a650/a660 - GPU devfreq tuning and fixes - Turn 8960 HDMI PHY into clock provider, - Make 8960 HDMI PHY use PXO clock from DT etnaviv: - experimental versilicon NPU support - report GPU load via fdinfo format - MMU fault message improvements tegra: - rework syncpoint interrupt mediatek: - DSI timing fix - fix config deps ast: - various fixes exynos: - restore bridge chain order fixes gud: - convert to shadow plane buffers - perform flushing synchronously during atomic update - Use new debugfs helpers arm/hdlcd: - Use new debugfs helper ili9486: - Support 16-bit pixel data imx: - Split off IPUv3 driver mipi-dbi: - convert to DRM shadow-plane helpers - rsp driver changes - Support separate I/O-voltage supply mxsfb: - Depend on ARCH_MXS or ARCH_MXC sun4i: - convert to new TV mode property vc4: - convert to new TV mode property - kunit tests - Support RGB565 and RGB666 formats - convert dsi driver to bridge - Various HVS an CRTC fixes v3d: - Do not opencode drm_gem_object_lookup() virtio: - improve tracing vkms: - support small cursors in IGT tests - Fix SEGFAULT from incorrect GEM-buffer mapping rcar-du: - fixes and improvements" * tag 'drm-next-2023-02-23' of git://anongit.freedesktop.org/drm/drm: (1455 commits) msm/fbdev: fix unused variable warning with clang. drm/fb-helper: Remove drm_fb_helper_unprepare() from drm_fb_helper_fini() dma-buf: make kobj_type structure constant drm/shmem-helper: Fix locking for drm_gem_shmem_get_pages_sgt() drm/amd/display: disable SubVP + DRR to prevent underflow drm/amd/display: Fail atomic_check early on normalize_zpos error drm/amd/pm: avoid unaligned access warnings drm/amd/display: avoid unaligned access warnings drm/amd/display: Remove duplicate/repeating expressions drm/amd/display: Remove duplicate/repeating expression drm/amd/display: Make variables declaration inside ifdef guard drm/amd/display: Fix excess arguments on kernel-doc drm/amd/display: Add previously missing includes drm/amd/amdgpu: Add function prototypes to headers drm/amd/display: Add function prototypes to headers drm/amd/display: Turn global functions into static drm/amd/display: remove unused _calculate_degamma_curve function drm/amd/display: remove unused func declaration from resource headers drm/amd/display: unset initial value for tf since it's never used drm/amd/display: camel case cleanup in color_gamma file ...
Diffstat (limited to 'drivers/gpu/drm/vc4')
-rw-r--r--drivers/gpu/drm/vc4/Kconfig16
-rw-r--r--drivers/gpu/drm/vc4/Makefile7
-rw-r--r--drivers/gpu/drm/vc4/tests/.kunitconfig13
-rw-r--r--drivers/gpu/drm/vc4/tests/vc4_mock.c200
-rw-r--r--drivers/gpu/drm/vc4/tests/vc4_mock.h63
-rw-r--r--drivers/gpu/drm/vc4/tests/vc4_mock_crtc.c41
-rw-r--r--drivers/gpu/drm/vc4/tests/vc4_mock_output.c138
-rw-r--r--drivers/gpu/drm/vc4/tests/vc4_mock_plane.c47
-rw-r--r--drivers/gpu/drm/vc4/tests/vc4_test_pv_muxing.c1039
-rw-r--r--drivers/gpu/drm/vc4/vc4_bo.c10
-rw-r--r--drivers/gpu/drm/vc4/vc4_crtc.c215
-rw-r--r--drivers/gpu/drm/vc4/vc4_debugfs.c36
-rw-r--r--drivers/gpu/drm/vc4/vc4_dpi.c34
-rw-r--r--drivers/gpu/drm/vc4/vc4_drv.c5
-rw-r--r--drivers/gpu/drm/vc4/vc4_drv.h148
-rw-r--r--drivers/gpu/drm/vc4/vc4_dsi.c189
-rw-r--r--drivers/gpu/drm/vc4/vc4_hdmi.c28
-rw-r--r--drivers/gpu/drm/vc4/vc4_hdmi_regs.h4
-rw-r--r--drivers/gpu/drm/vc4/vc4_hvs.c272
-rw-r--r--drivers/gpu/drm/vc4/vc4_kms.c139
-rw-r--r--drivers/gpu/drm/vc4/vc4_plane.c139
-rw-r--r--drivers/gpu/drm/vc4/vc4_regs.h20
-rw-r--r--drivers/gpu/drm/vc4/vc4_txp.c62
-rw-r--r--drivers/gpu/drm/vc4/vc4_v3d.c14
-rw-r--r--drivers/gpu/drm/vc4/vc4_vec.c365
25 files changed, 2739 insertions, 505 deletions
diff --git a/drivers/gpu/drm/vc4/Kconfig b/drivers/gpu/drm/vc4/Kconfig
index 246305d17a52..91dcf8d174d6 100644
--- a/drivers/gpu/drm/vc4/Kconfig
+++ b/drivers/gpu/drm/vc4/Kconfig
@@ -34,3 +34,19 @@ config DRM_VC4_HDMI_CEC
help
Choose this option if you have a Broadcom VC4 GPU
and want to use CEC.
+
+config DRM_VC4_KUNIT_TEST
+ tristate "KUnit tests for VC4" if !KUNIT_ALL_TESTS
+ depends on DRM_VC4 && KUNIT
+ select DRM_KUNIT_TEST_HELPERS
+ default KUNIT_ALL_TESTS
+ help
+ This builds unit tests for the VC4 DRM/KMS driver. This option is
+ not useful for distributions or general kernels, but only for kernel
+ developers working on the VC4 driver.
+
+ For more information on KUnit and unit tests in general,
+ please refer to the KUnit documentation in
+ Documentation/dev-tools/kunit/.
+
+ If in doubt, say "N".
diff --git a/drivers/gpu/drm/vc4/Makefile b/drivers/gpu/drm/vc4/Makefile
index d0163e18e9ca..c41f89a15a55 100644
--- a/drivers/gpu/drm/vc4/Makefile
+++ b/drivers/gpu/drm/vc4/Makefile
@@ -25,6 +25,13 @@ vc4-y := \
vc4_validate.o \
vc4_validate_shaders.o
+vc4-$(CONFIG_DRM_VC4_KUNIT_TEST) += \
+ tests/vc4_mock.o \
+ tests/vc4_mock_crtc.o \
+ tests/vc4_mock_output.o \
+ tests/vc4_mock_plane.o \
+ tests/vc4_test_pv_muxing.o
+
vc4-$(CONFIG_DEBUG_FS) += vc4_debugfs.o
obj-$(CONFIG_DRM_VC4) += vc4.o
diff --git a/drivers/gpu/drm/vc4/tests/.kunitconfig b/drivers/gpu/drm/vc4/tests/.kunitconfig
new file mode 100644
index 000000000000..b503e9036c7f
--- /dev/null
+++ b/drivers/gpu/drm/vc4/tests/.kunitconfig
@@ -0,0 +1,13 @@
+CONFIG_ARCH_BCM=y
+CONFIG_ARCH_BCM2835=y
+CONFIG_BCM2835_MBOX=y
+CONFIG_KUNIT=y
+CONFIG_DRM=y
+CONFIG_DRM_VC4=y
+CONFIG_DRM_VC4_KUNIT_TEST=y
+CONFIG_MAILBOX=y
+CONFIG_RASPBERRYPI_FIRMWARE=y
+CONFIG_SND=y
+CONFIG_SND_SOC=y
+CONFIG_SOUND=y
+CONFIG_COMMON_CLK=y
diff --git a/drivers/gpu/drm/vc4/tests/vc4_mock.c b/drivers/gpu/drm/vc4/tests/vc4_mock.c
new file mode 100644
index 000000000000..a4bed26af32f
--- /dev/null
+++ b/drivers/gpu/drm/vc4/tests/vc4_mock.c
@@ -0,0 +1,200 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <drm/drm_drv.h>
+#include <drm/drm_kunit_helpers.h>
+
+#include <kunit/test.h>
+
+#include "vc4_mock.h"
+
+struct vc4_mock_output_desc {
+ enum vc4_encoder_type vc4_encoder_type;
+ unsigned int encoder_type;
+ unsigned int connector_type;
+};
+
+#define VC4_MOCK_OUTPUT_DESC(_vc4_type, _etype, _ctype) \
+ { \
+ .vc4_encoder_type = _vc4_type, \
+ .encoder_type = _etype, \
+ .connector_type = _ctype, \
+ }
+
+struct vc4_mock_pipe_desc {
+ const struct vc4_crtc_data *data;
+ const struct vc4_mock_output_desc *outputs;
+ unsigned int noutputs;
+};
+
+#define VC4_MOCK_CRTC_DESC(_data, ...) \
+ { \
+ .data = _data, \
+ .outputs = (struct vc4_mock_output_desc[]) { __VA_ARGS__ }, \
+ .noutputs = sizeof((struct vc4_mock_output_desc[]) { __VA_ARGS__ }) / \
+ sizeof(struct vc4_mock_output_desc), \
+ }
+
+#define VC4_MOCK_PIXELVALVE_DESC(_data, ...) \
+ VC4_MOCK_CRTC_DESC(&(_data)->base, __VA_ARGS__)
+
+struct vc4_mock_desc {
+ const struct vc4_mock_pipe_desc *pipes;
+ unsigned int npipes;
+};
+
+#define VC4_MOCK_DESC(...) \
+ { \
+ .pipes = (struct vc4_mock_pipe_desc[]) { __VA_ARGS__ }, \
+ .npipes = sizeof((struct vc4_mock_pipe_desc[]) { __VA_ARGS__ }) / \
+ sizeof(struct vc4_mock_pipe_desc), \
+ }
+
+static const struct vc4_mock_desc vc4_mock =
+ VC4_MOCK_DESC(
+ VC4_MOCK_CRTC_DESC(&vc4_txp_crtc_data,
+ VC4_MOCK_OUTPUT_DESC(VC4_ENCODER_TYPE_TXP,
+ DRM_MODE_ENCODER_VIRTUAL,
+ DRM_MODE_CONNECTOR_WRITEBACK)),
+ VC4_MOCK_PIXELVALVE_DESC(&bcm2835_pv0_data,
+ VC4_MOCK_OUTPUT_DESC(VC4_ENCODER_TYPE_DSI0,
+ DRM_MODE_ENCODER_DSI,
+ DRM_MODE_CONNECTOR_DSI),
+ VC4_MOCK_OUTPUT_DESC(VC4_ENCODER_TYPE_DPI,
+ DRM_MODE_ENCODER_DPI,
+ DRM_MODE_CONNECTOR_DPI)),
+ VC4_MOCK_PIXELVALVE_DESC(&bcm2835_pv1_data,
+ VC4_MOCK_OUTPUT_DESC(VC4_ENCODER_TYPE_DSI1,
+ DRM_MODE_ENCODER_DSI,
+ DRM_MODE_CONNECTOR_DSI)),
+ VC4_MOCK_PIXELVALVE_DESC(&bcm2835_pv2_data,
+ VC4_MOCK_OUTPUT_DESC(VC4_ENCODER_TYPE_HDMI0,
+ DRM_MODE_ENCODER_TMDS,
+ DRM_MODE_CONNECTOR_HDMIA),
+ VC4_MOCK_OUTPUT_DESC(VC4_ENCODER_TYPE_VEC,
+ DRM_MODE_ENCODER_TVDAC,
+ DRM_MODE_CONNECTOR_Composite)),
+);
+
+static const struct vc4_mock_desc vc5_mock =
+ VC4_MOCK_DESC(
+ VC4_MOCK_CRTC_DESC(&vc4_txp_crtc_data,
+ VC4_MOCK_OUTPUT_DESC(VC4_ENCODER_TYPE_TXP,
+ DRM_MODE_ENCODER_VIRTUAL,
+ DRM_MODE_CONNECTOR_WRITEBACK)),
+ VC4_MOCK_PIXELVALVE_DESC(&bcm2711_pv0_data,
+ VC4_MOCK_OUTPUT_DESC(VC4_ENCODER_TYPE_DSI0,
+ DRM_MODE_ENCODER_DSI,
+ DRM_MODE_CONNECTOR_DSI),
+ VC4_MOCK_OUTPUT_DESC(VC4_ENCODER_TYPE_DPI,
+ DRM_MODE_ENCODER_DPI,
+ DRM_MODE_CONNECTOR_DPI)),
+ VC4_MOCK_PIXELVALVE_DESC(&bcm2711_pv1_data,
+ VC4_MOCK_OUTPUT_DESC(VC4_ENCODER_TYPE_DSI1,
+ DRM_MODE_ENCODER_DSI,
+ DRM_MODE_CONNECTOR_DSI)),
+ VC4_MOCK_PIXELVALVE_DESC(&bcm2711_pv2_data,
+ VC4_MOCK_OUTPUT_DESC(VC4_ENCODER_TYPE_HDMI0,
+ DRM_MODE_ENCODER_TMDS,
+ DRM_MODE_CONNECTOR_HDMIA)),
+ VC4_MOCK_PIXELVALVE_DESC(&bcm2711_pv3_data,
+ VC4_MOCK_OUTPUT_DESC(VC4_ENCODER_TYPE_VEC,
+ DRM_MODE_ENCODER_TVDAC,
+ DRM_MODE_CONNECTOR_Composite)),
+ VC4_MOCK_PIXELVALVE_DESC(&bcm2711_pv4_data,
+ VC4_MOCK_OUTPUT_DESC(VC4_ENCODER_TYPE_HDMI1,
+ DRM_MODE_ENCODER_TMDS,
+ DRM_MODE_CONNECTOR_HDMIA)),
+);
+
+static int __build_one_pipe(struct kunit *test, struct drm_device *drm,
+ const struct vc4_mock_pipe_desc *pipe)
+{
+ struct vc4_dummy_plane *dummy_plane;
+ struct drm_plane *plane;
+ struct vc4_dummy_crtc *dummy_crtc;
+ struct drm_crtc *crtc;
+ unsigned int i;
+
+ dummy_plane = vc4_dummy_plane(test, drm, DRM_PLANE_TYPE_PRIMARY);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dummy_plane);
+
+ plane = &dummy_plane->plane.base;
+ dummy_crtc = vc4_mock_pv(test, drm, plane, pipe->data);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dummy_crtc);
+
+ crtc = &dummy_crtc->crtc.base;
+ for (i = 0; i < pipe->noutputs; i++) {
+ const struct vc4_mock_output_desc *mock_output = &pipe->outputs[i];
+ struct vc4_dummy_output *dummy_output;
+
+ dummy_output = vc4_dummy_output(test, drm, crtc,
+ mock_output->vc4_encoder_type,
+ mock_output->encoder_type,
+ mock_output->connector_type);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dummy_output);
+ }
+
+ return 0;
+}
+
+static int __build_mock(struct kunit *test, struct drm_device *drm,
+ const struct vc4_mock_desc *mock)
+{
+ unsigned int i;
+
+ for (i = 0; i < mock->npipes; i++) {
+ const struct vc4_mock_pipe_desc *pipe = &mock->pipes[i];
+ int ret;
+
+ ret = __build_one_pipe(test, drm, pipe);
+ KUNIT_ASSERT_EQ(test, ret, 0);
+ }
+
+ return 0;
+}
+
+static struct vc4_dev *__mock_device(struct kunit *test, bool is_vc5)
+{
+ struct drm_device *drm;
+ const struct drm_driver *drv = is_vc5 ? &vc5_drm_driver : &vc4_drm_driver;
+ const struct vc4_mock_desc *desc = is_vc5 ? &vc5_mock : &vc4_mock;
+ struct vc4_dev *vc4;
+ struct device *dev;
+ int ret;
+
+ dev = drm_kunit_helper_alloc_device(test);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dev);
+
+ vc4 = drm_kunit_helper_alloc_drm_device_with_driver(test, dev,
+ struct vc4_dev, base,
+ drv);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, vc4);
+
+ vc4->dev = dev;
+ vc4->is_vc5 = is_vc5;
+
+ vc4->hvs = __vc4_hvs_alloc(vc4, NULL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, vc4->hvs);
+
+ drm = &vc4->base;
+ ret = __build_mock(test, drm, desc);
+ KUNIT_ASSERT_EQ(test, ret, 0);
+
+ ret = vc4_kms_load(drm);
+ KUNIT_ASSERT_EQ(test, ret, 0);
+
+ ret = drm_dev_register(drm, 0);
+ KUNIT_ASSERT_EQ(test, ret, 0);
+
+ return vc4;
+}
+
+struct vc4_dev *vc4_mock_device(struct kunit *test)
+{
+ return __mock_device(test, false);
+}
+
+struct vc4_dev *vc5_mock_device(struct kunit *test)
+{
+ return __mock_device(test, true);
+}
diff --git a/drivers/gpu/drm/vc4/tests/vc4_mock.h b/drivers/gpu/drm/vc4/tests/vc4_mock.h
new file mode 100644
index 000000000000..db8e9a141ef8
--- /dev/null
+++ b/drivers/gpu/drm/vc4/tests/vc4_mock.h
@@ -0,0 +1,63 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+#ifndef VC4_MOCK_H_
+#define VC4_MOCK_H_
+
+#include "../vc4_drv.h"
+
+static inline
+struct drm_crtc *vc4_find_crtc_for_encoder(struct kunit *test,
+ struct drm_device *drm,
+ struct drm_encoder *encoder)
+{
+ struct drm_crtc *crtc;
+
+ KUNIT_ASSERT_EQ(test, hweight32(encoder->possible_crtcs), 1);
+
+ drm_for_each_crtc(crtc, drm)
+ if (encoder->possible_crtcs & drm_crtc_mask(crtc))
+ return crtc;
+
+ return NULL;
+}
+
+struct vc4_dummy_plane {
+ struct vc4_plane plane;
+};
+
+struct vc4_dummy_plane *vc4_dummy_plane(struct kunit *test,
+ struct drm_device *drm,
+ enum drm_plane_type type);
+
+struct vc4_dummy_crtc {
+ struct vc4_crtc crtc;
+};
+
+struct vc4_dummy_crtc *vc4_mock_pv(struct kunit *test,
+ struct drm_device *drm,
+ struct drm_plane *plane,
+ const struct vc4_crtc_data *data);
+
+struct vc4_dummy_output {
+ struct vc4_encoder encoder;
+ struct drm_connector connector;
+};
+
+struct vc4_dummy_output *vc4_dummy_output(struct kunit *test,
+ struct drm_device *drm,
+ struct drm_crtc *crtc,
+ enum vc4_encoder_type vc4_encoder_type,
+ unsigned int kms_encoder_type,
+ unsigned int connector_type);
+
+struct vc4_dev *vc4_mock_device(struct kunit *test);
+struct vc4_dev *vc5_mock_device(struct kunit *test);
+
+int vc4_mock_atomic_add_output(struct kunit *test,
+ struct drm_atomic_state *state,
+ enum vc4_encoder_type type);
+int vc4_mock_atomic_del_output(struct kunit *test,
+ struct drm_atomic_state *state,
+ enum vc4_encoder_type type);
+
+#endif // VC4_MOCK_H_
diff --git a/drivers/gpu/drm/vc4/tests/vc4_mock_crtc.c b/drivers/gpu/drm/vc4/tests/vc4_mock_crtc.c
new file mode 100644
index 000000000000..5d12d7beef0e
--- /dev/null
+++ b/drivers/gpu/drm/vc4/tests/vc4_mock_crtc.c
@@ -0,0 +1,41 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <drm/drm_atomic_state_helper.h>
+#include <drm/drm_modeset_helper_vtables.h>
+
+#include <kunit/test.h>
+
+#include "vc4_mock.h"
+
+static const struct drm_crtc_helper_funcs vc4_dummy_crtc_helper_funcs = {
+ .atomic_check = vc4_crtc_atomic_check,
+};
+
+static const struct drm_crtc_funcs vc4_dummy_crtc_funcs = {
+ .atomic_destroy_state = vc4_crtc_destroy_state,
+ .atomic_duplicate_state = vc4_crtc_duplicate_state,
+ .reset = vc4_crtc_reset,
+};
+
+struct vc4_dummy_crtc *vc4_mock_pv(struct kunit *test,
+ struct drm_device *drm,
+ struct drm_plane *plane,
+ const struct vc4_crtc_data *data)
+{
+ struct vc4_dummy_crtc *dummy_crtc;
+ struct vc4_crtc *vc4_crtc;
+ int ret;
+
+ dummy_crtc = kunit_kzalloc(test, sizeof(*dummy_crtc), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, dummy_crtc);
+
+ vc4_crtc = &dummy_crtc->crtc;
+ ret = __vc4_crtc_init(drm, NULL,
+ vc4_crtc, data, plane,
+ &vc4_dummy_crtc_funcs,
+ &vc4_dummy_crtc_helper_funcs,
+ false);
+ KUNIT_ASSERT_EQ(test, ret, 0);
+
+ return dummy_crtc;
+}
diff --git a/drivers/gpu/drm/vc4/tests/vc4_mock_output.c b/drivers/gpu/drm/vc4/tests/vc4_mock_output.c
new file mode 100644
index 000000000000..8d33be828d9a
--- /dev/null
+++ b/drivers/gpu/drm/vc4/tests/vc4_mock_output.c
@@ -0,0 +1,138 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <drm/drm_atomic_state_helper.h>
+#include <drm/drm_atomic_uapi.h>
+#include <drm/drm_connector.h>
+#include <drm/drm_crtc.h>
+#include <drm/drm_encoder.h>
+#include <drm/drm_modeset_helper_vtables.h>
+
+#include <kunit/test.h>
+
+#include "vc4_mock.h"
+
+static const struct drm_connector_helper_funcs vc4_dummy_connector_helper_funcs = {
+};
+
+static const struct drm_connector_funcs vc4_dummy_connector_funcs = {
+ .atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
+ .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
+ .reset = drm_atomic_helper_connector_reset,
+};
+
+struct vc4_dummy_output *vc4_dummy_output(struct kunit *test,
+ struct drm_device *drm,
+ struct drm_crtc *crtc,
+ enum vc4_encoder_type vc4_encoder_type,
+ unsigned int kms_encoder_type,
+ unsigned int connector_type)
+{
+ struct vc4_dummy_output *dummy_output;
+ struct drm_connector *conn;
+ struct drm_encoder *enc;
+ int ret;
+
+ dummy_output = kunit_kzalloc(test, sizeof(*dummy_output), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dummy_output);
+ dummy_output->encoder.type = vc4_encoder_type;
+
+ enc = &dummy_output->encoder.base;
+ ret = drmm_encoder_init(drm, enc,
+ NULL,
+ kms_encoder_type,
+ NULL);
+ KUNIT_ASSERT_EQ(test, ret, 0);
+ enc->possible_crtcs = drm_crtc_mask(crtc);
+
+ conn = &dummy_output->connector;
+ ret = drmm_connector_init(drm, conn,
+ &vc4_dummy_connector_funcs,
+ connector_type,
+ NULL);
+ KUNIT_ASSERT_EQ(test, ret, 0);
+
+ drm_connector_helper_add(conn, &vc4_dummy_connector_helper_funcs);
+ drm_connector_attach_encoder(conn, enc);
+
+ return dummy_output;
+}
+
+static const struct drm_display_mode default_mode = {
+ DRM_SIMPLE_MODE(640, 480, 64, 48)
+};
+
+int vc4_mock_atomic_add_output(struct kunit *test,
+ struct drm_atomic_state *state,
+ enum vc4_encoder_type type)
+{
+ struct drm_device *drm = state->dev;
+ struct drm_connector_state *conn_state;
+ struct drm_crtc_state *crtc_state;
+ struct vc4_dummy_output *output;
+ struct drm_connector *conn;
+ struct drm_encoder *encoder;
+ struct drm_crtc *crtc;
+ int ret;
+
+ encoder = vc4_find_encoder_by_type(drm, type);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, encoder);
+
+ crtc = vc4_find_crtc_for_encoder(test, drm, encoder);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, crtc);
+
+ output = container_of(encoder, struct vc4_dummy_output, encoder.base);
+ conn = &output->connector;
+ conn_state = drm_atomic_get_connector_state(state, conn);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, conn_state);
+
+ ret = drm_atomic_set_crtc_for_connector(conn_state, crtc);
+ KUNIT_EXPECT_EQ(test, ret, 0);
+
+ crtc_state = drm_atomic_get_crtc_state(state, crtc);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, crtc_state);
+
+ ret = drm_atomic_set_mode_for_crtc(crtc_state, &default_mode);
+ KUNIT_EXPECT_EQ(test, ret, 0);
+
+ crtc_state->active = true;
+
+ return 0;
+}
+
+int vc4_mock_atomic_del_output(struct kunit *test,
+ struct drm_atomic_state *state,
+ enum vc4_encoder_type type)
+{
+ struct drm_device *drm = state->dev;
+ struct drm_connector_state *conn_state;
+ struct drm_crtc_state *crtc_state;
+ struct vc4_dummy_output *output;
+ struct drm_connector *conn;
+ struct drm_encoder *encoder;
+ struct drm_crtc *crtc;
+ int ret;
+
+ encoder = vc4_find_encoder_by_type(drm, type);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, encoder);
+
+ crtc = vc4_find_crtc_for_encoder(test, drm, encoder);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, crtc);
+
+ crtc_state = drm_atomic_get_crtc_state(state, crtc);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, crtc_state);
+
+ crtc_state->active = false;
+
+ ret = drm_atomic_set_mode_for_crtc(crtc_state, NULL);
+ KUNIT_ASSERT_EQ(test, ret, 0);
+
+ output = container_of(encoder, struct vc4_dummy_output, encoder.base);
+ conn = &output->connector;
+ conn_state = drm_atomic_get_connector_state(state, conn);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, conn_state);
+
+ ret = drm_atomic_set_crtc_for_connector(conn_state, NULL);
+ KUNIT_ASSERT_EQ(test, ret, 0);
+
+ return 0;
+}
diff --git a/drivers/gpu/drm/vc4/tests/vc4_mock_plane.c b/drivers/gpu/drm/vc4/tests/vc4_mock_plane.c
new file mode 100644
index 000000000000..62b18f5f41db
--- /dev/null
+++ b/drivers/gpu/drm/vc4/tests/vc4_mock_plane.c
@@ -0,0 +1,47 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <drm/drm_atomic_state_helper.h>
+#include <drm/drm_fourcc.h>
+#include <drm/drm_modeset_helper_vtables.h>
+#include <drm/drm_plane.h>
+
+#include <kunit/test.h>
+
+#include "vc4_mock.h"
+
+static const struct drm_plane_helper_funcs vc4_dummy_plane_helper_funcs = {
+};
+
+static const struct drm_plane_funcs vc4_dummy_plane_funcs = {
+ .atomic_destroy_state = drm_atomic_helper_plane_destroy_state,
+ .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state,
+ .reset = drm_atomic_helper_plane_reset,
+};
+
+static const uint32_t vc4_dummy_plane_formats[] = {
+ DRM_FORMAT_XRGB8888,
+};
+
+struct vc4_dummy_plane *vc4_dummy_plane(struct kunit *test,
+ struct drm_device *drm,
+ enum drm_plane_type type)
+{
+ struct vc4_dummy_plane *dummy_plane;
+ struct drm_plane *plane;
+
+ dummy_plane = drmm_universal_plane_alloc(drm,
+ struct vc4_dummy_plane, plane.base,
+ 0,
+ &vc4_dummy_plane_funcs,
+ vc4_dummy_plane_formats,
+ ARRAY_SIZE(vc4_dummy_plane_formats),
+ NULL,
+ DRM_PLANE_TYPE_PRIMARY,
+ NULL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dummy_plane);
+
+ plane = &dummy_plane->plane.base;
+ drm_plane_helper_add(plane, &vc4_dummy_plane_helper_funcs);
+
+ return dummy_plane;
+}
diff --git a/drivers/gpu/drm/vc4/tests/vc4_test_pv_muxing.c b/drivers/gpu/drm/vc4/tests/vc4_test_pv_muxing.c
new file mode 100644
index 000000000000..ae0bd0f81698
--- /dev/null
+++ b/drivers/gpu/drm/vc4/tests/vc4_test_pv_muxing.c
@@ -0,0 +1,1039 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <drm/drm_atomic.h>
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_atomic_state_helper.h>
+#include <drm/drm_atomic_uapi.h>
+#include <drm/drm_crtc.h>
+#include <drm/drm_drv.h>
+#include <drm/drm_fourcc.h>
+#include <drm/drm_kunit_helpers.h>
+#include <drm/drm_mode.h>
+#include <drm/drm_modeset_helper_vtables.h>
+#include <drm/drm_plane.h>
+
+#include <kunit/test.h>
+
+#include "../vc4_drv.h"
+
+#include "vc4_mock.h"
+
+struct pv_muxing_priv {
+ struct vc4_dev *vc4;
+ struct drm_modeset_acquire_ctx ctx;
+ struct drm_atomic_state *state;
+};
+
+static bool check_fifo_conflict(struct kunit *test,
+ const struct drm_atomic_state *state)
+{
+ struct vc4_hvs_state *hvs_state;
+ unsigned int used_fifos = 0;
+ unsigned int i;
+
+ hvs_state = vc4_hvs_get_new_global_state(state);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, hvs_state);
+
+ for (i = 0; i < HVS_NUM_CHANNELS; i++) {
+ if (!hvs_state->fifo_state[i].in_use)
+ continue;
+
+ KUNIT_EXPECT_FALSE(test, used_fifos & BIT(i));
+ used_fifos |= BIT(i);
+ }
+
+ return true;
+}
+
+struct encoder_constraint {
+ enum vc4_encoder_type type;
+ unsigned int *channels;
+ size_t nchannels;
+};
+
+#define ENCODER_CONSTRAINT(_type, ...) \
+ { \
+ .type = _type, \
+ .channels = (unsigned int[]) { __VA_ARGS__ }, \
+ .nchannels = sizeof((unsigned int[]) { __VA_ARGS__ }) / \
+ sizeof(unsigned int), \
+ }
+
+static bool __check_encoder_constraints(const struct encoder_constraint *constraints,
+ size_t nconstraints,
+ enum vc4_encoder_type type,
+ unsigned int channel)
+{
+ unsigned int i;
+
+ for (i = 0; i < nconstraints; i++) {
+ const struct encoder_constraint *constraint = &constraints[i];
+ unsigned int j;
+
+ if (constraint->type != type)
+ continue;
+
+ for (j = 0; j < constraint->nchannels; j++) {
+ unsigned int _channel = constraint->channels[j];
+
+ if (channel != _channel)
+ continue;
+
+ return true;
+ }
+ }
+
+ return false;
+}
+
+static const struct encoder_constraint vc4_encoder_constraints[] = {
+ ENCODER_CONSTRAINT(VC4_ENCODER_TYPE_DPI, 0),
+ ENCODER_CONSTRAINT(VC4_ENCODER_TYPE_DSI0, 0),
+ ENCODER_CONSTRAINT(VC4_ENCODER_TYPE_HDMI0, 1),
+ ENCODER_CONSTRAINT(VC4_ENCODER_TYPE_VEC, 1),
+ ENCODER_CONSTRAINT(VC4_ENCODER_TYPE_TXP, 2),
+ ENCODER_CONSTRAINT(VC4_ENCODER_TYPE_DSI1, 2),
+};
+
+static const struct encoder_constraint vc5_encoder_constraints[] = {
+ ENCODER_CONSTRAINT(VC4_ENCODER_TYPE_DPI, 0),
+ ENCODER_CONSTRAINT(VC4_ENCODER_TYPE_DSI0, 0),
+ ENCODER_CONSTRAINT(VC4_ENCODER_TYPE_VEC, 1),
+ ENCODER_CONSTRAINT(VC4_ENCODER_TYPE_TXP, 0, 2),
+ ENCODER_CONSTRAINT(VC4_ENCODER_TYPE_DSI1, 0, 1, 2),
+ ENCODER_CONSTRAINT(VC4_ENCODER_TYPE_HDMI0, 0, 1, 2),
+ ENCODER_CONSTRAINT(VC4_ENCODER_TYPE_HDMI1, 0, 1, 2),
+};
+
+static bool check_vc4_encoder_constraints(enum vc4_encoder_type type, unsigned int channel)
+{
+ return __check_encoder_constraints(vc4_encoder_constraints,
+ ARRAY_SIZE(vc4_encoder_constraints),
+ type, channel);
+}
+
+static bool check_vc5_encoder_constraints(enum vc4_encoder_type type, unsigned int channel)
+{
+ return __check_encoder_constraints(vc5_encoder_constraints,
+ ARRAY_SIZE(vc5_encoder_constraints),
+ type, channel);
+}
+
+static struct vc4_crtc_state *
+get_vc4_crtc_state_for_encoder(struct kunit *test,
+ const struct drm_atomic_state *state,
+ enum vc4_encoder_type type)
+{
+ struct drm_device *drm = state->dev;
+ struct drm_crtc_state *new_crtc_state;
+ struct drm_encoder *encoder;
+ struct drm_crtc *crtc;
+
+ encoder = vc4_find_encoder_by_type(drm, type);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, encoder);
+
+ crtc = vc4_find_crtc_for_encoder(test, drm, encoder);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, crtc);
+
+ new_crtc_state = drm_atomic_get_new_crtc_state(state, crtc);
+ if (!new_crtc_state)
+ return NULL;
+
+ return to_vc4_crtc_state(new_crtc_state);
+}
+
+static bool check_channel_for_encoder(struct kunit *test,
+ const struct drm_atomic_state *state,
+ enum vc4_encoder_type type,
+ bool (*check_fn)(enum vc4_encoder_type type, unsigned int channel))
+{
+ struct vc4_crtc_state *new_vc4_crtc_state;
+ struct vc4_hvs_state *new_hvs_state;
+ unsigned int channel;
+
+ new_hvs_state = vc4_hvs_get_new_global_state(state);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, new_hvs_state);
+
+ new_vc4_crtc_state = get_vc4_crtc_state_for_encoder(test, state, type);
+ KUNIT_ASSERT_NOT_NULL(test, new_vc4_crtc_state);
+
+ channel = new_vc4_crtc_state->assigned_channel;
+ KUNIT_EXPECT_NE(test, channel, VC4_HVS_CHANNEL_DISABLED);
+
+ KUNIT_EXPECT_TRUE(test, new_hvs_state->fifo_state[channel].in_use);
+
+ KUNIT_EXPECT_TRUE(test, check_fn(type, channel));
+
+ return true;
+}
+
+struct pv_muxing_param {
+ const char *name;
+ struct vc4_dev *(*mock_fn)(struct kunit *test);
+ bool (*check_fn)(enum vc4_encoder_type type, unsigned int channel);
+ enum vc4_encoder_type *encoders;
+ size_t nencoders;
+};
+
+static void vc4_test_pv_muxing_desc(const struct pv_muxing_param *t, char *desc)
+{
+ strscpy(desc, t->name, KUNIT_PARAM_DESC_SIZE);
+}
+
+#define PV_MUXING_TEST(_name, _mock_fn, _check_fn, ...) \
+ { \
+ .name = _name, \
+ .mock_fn = &_mock_fn, \
+ .check_fn = &_check_fn, \
+ .encoders = (enum vc4_encoder_type[]) { __VA_ARGS__ }, \
+ .nencoders = sizeof((enum vc4_encoder_type[]) { __VA_ARGS__ }) / \
+ sizeof(enum vc4_encoder_type), \
+ }
+
+#define VC4_PV_MUXING_TEST(_name, ...) \
+ PV_MUXING_TEST(_name, vc4_mock_device, check_vc4_encoder_constraints, __VA_ARGS__)
+
+#define VC5_PV_MUXING_TEST(_name, ...) \
+ PV_MUXING_TEST(_name, vc5_mock_device, check_vc5_encoder_constraints, __VA_ARGS__)
+
+static const struct pv_muxing_param vc4_test_pv_muxing_params[] = {
+ VC4_PV_MUXING_TEST("1 output: DSI0",
+ VC4_ENCODER_TYPE_DSI0),
+ VC4_PV_MUXING_TEST("1 output: DPI",
+ VC4_ENCODER_TYPE_DPI),
+ VC4_PV_MUXING_TEST("1 output: HDMI0",
+ VC4_ENCODER_TYPE_HDMI0),
+ VC4_PV_MUXING_TEST("1 output: VEC",
+ VC4_ENCODER_TYPE_VEC),
+ VC4_PV_MUXING_TEST("1 output: DSI1",
+ VC4_ENCODER_TYPE_DSI1),
+ VC4_PV_MUXING_TEST("1 output: TXP",
+ VC4_ENCODER_TYPE_TXP),
+ VC4_PV_MUXING_TEST("2 outputs: DSI0, HDMI0",
+ VC4_ENCODER_TYPE_DSI0,
+ VC4_ENCODER_TYPE_HDMI0),
+ VC4_PV_MUXING_TEST("2 outputs: DSI0, VEC",
+ VC4_ENCODER_TYPE_DSI0,
+ VC4_ENCODER_TYPE_VEC),
+ VC4_PV_MUXING_TEST("2 outputs: DSI0, DSI1",
+ VC4_ENCODER_TYPE_DSI0,
+ VC4_ENCODER_TYPE_DSI1),
+ VC4_PV_MUXING_TEST("2 outputs: DSI0, TXP",
+ VC4_ENCODER_TYPE_DSI0,
+ VC4_ENCODER_TYPE_TXP),
+ VC4_PV_MUXING_TEST("2 outputs: DPI, HDMI0",
+ VC4_ENCODER_TYPE_DPI,
+ VC4_ENCODER_TYPE_HDMI0),
+ VC4_PV_MUXING_TEST("2 outputs: DPI, VEC",
+ VC4_ENCODER_TYPE_DPI,
+ VC4_ENCODER_TYPE_VEC),
+ VC4_PV_MUXING_TEST("2 outputs: DPI, DSI1",
+ VC4_ENCODER_TYPE_DPI,
+ VC4_ENCODER_TYPE_DSI1),
+ VC4_PV_MUXING_TEST("2 outputs: DPI, TXP",
+ VC4_ENCODER_TYPE_DPI,
+ VC4_ENCODER_TYPE_TXP),
+ VC4_PV_MUXING_TEST("2 outputs: HDMI0, DSI1",
+ VC4_ENCODER_TYPE_HDMI0,
+ VC4_ENCODER_TYPE_DSI1),
+ VC4_PV_MUXING_TEST("2 outputs: HDMI0, TXP",
+ VC4_ENCODER_TYPE_HDMI0,
+ VC4_ENCODER_TYPE_TXP),
+ VC4_PV_MUXING_TEST("2 outputs: VEC, DSI1",
+ VC4_ENCODER_TYPE_VEC,
+ VC4_ENCODER_TYPE_DSI1),
+ VC4_PV_MUXING_TEST("2 outputs: VEC, TXP",
+ VC4_ENCODER_TYPE_VEC,
+ VC4_ENCODER_TYPE_TXP),
+ VC4_PV_MUXING_TEST("3 outputs: DSI0, HDMI0, DSI1",
+ VC4_ENCODER_TYPE_DSI0,
+ VC4_ENCODER_TYPE_HDMI0,
+ VC4_ENCODER_TYPE_DSI1),
+ VC4_PV_MUXING_TEST("3 outputs: DSI0, HDMI0, TXP",
+ VC4_ENCODER_TYPE_DSI0,
+ VC4_ENCODER_TYPE_HDMI0,
+ VC4_ENCODER_TYPE_TXP),
+ VC4_PV_MUXING_TEST("3 outputs: DSI0, VEC, DSI1",
+ VC4_ENCODER_TYPE_DSI0,
+ VC4_ENCODER_TYPE_VEC,
+ VC4_ENCODER_TYPE_DSI1),
+ VC4_PV_MUXING_TEST("3 outputs: DSI0, VEC, TXP",
+ VC4_ENCODER_TYPE_DSI0,
+ VC4_ENCODER_TYPE_VEC,
+ VC4_ENCODER_TYPE_TXP),
+ VC4_PV_MUXING_TEST("3 outputs: DPI, HDMI0, DSI1",
+ VC4_ENCODER_TYPE_DPI,
+ VC4_ENCODER_TYPE_HDMI0,
+ VC4_ENCODER_TYPE_DSI1),
+ VC4_PV_MUXING_TEST("3 outputs: DPI, HDMI0, TXP",
+ VC4_ENCODER_TYPE_DPI,
+ VC4_ENCODER_TYPE_HDMI0,
+ VC4_ENCODER_TYPE_TXP),
+ VC4_PV_MUXING_TEST("3 outputs: DPI, VEC, DSI1",
+ VC4_ENCODER_TYPE_DPI,
+ VC4_ENCODER_TYPE_VEC,
+ VC4_ENCODER_TYPE_DSI1),
+ VC4_PV_MUXING_TEST("3 outputs: DPI, VEC, TXP",
+ VC4_ENCODER_TYPE_DPI,
+ VC4_ENCODER_TYPE_VEC,
+ VC4_ENCODER_TYPE_TXP),
+};
+
+KUNIT_ARRAY_PARAM(vc4_test_pv_muxing,
+ vc4_test_pv_muxing_params,
+ vc4_test_pv_muxing_desc);
+
+static const struct pv_muxing_param vc4_test_pv_muxing_invalid_params[] = {
+ VC4_PV_MUXING_TEST("DPI/DSI0 Conflict",
+ VC4_ENCODER_TYPE_DPI,
+ VC4_ENCODER_TYPE_DSI0),
+ VC4_PV_MUXING_TEST("TXP/DSI1 Conflict",
+ VC4_ENCODER_TYPE_TXP,
+ VC4_ENCODER_TYPE_DSI1),
+ VC4_PV_MUXING_TEST("HDMI0/VEC Conflict",
+ VC4_ENCODER_TYPE_HDMI0,
+ VC4_ENCODER_TYPE_VEC),
+ VC4_PV_MUXING_TEST("More than 3 outputs: DSI0, HDMI0, DSI1, TXP",
+ VC4_ENCODER_TYPE_DSI0,
+ VC4_ENCODER_TYPE_HDMI0,
+ VC4_ENCODER_TYPE_DSI1,
+ VC4_ENCODER_TYPE_TXP),
+ VC4_PV_MUXING_TEST("More than 3 outputs: DSI0, VEC, DSI1, TXP",
+ VC4_ENCODER_TYPE_DSI0,
+ VC4_ENCODER_TYPE_VEC,
+ VC4_ENCODER_TYPE_DSI1,
+ VC4_ENCODER_TYPE_TXP),
+ VC4_PV_MUXING_TEST("More than 3 outputs: DPI, HDMI0, DSI1, TXP",
+ VC4_ENCODER_TYPE_DPI,
+ VC4_ENCODER_TYPE_HDMI0,
+ VC4_ENCODER_TYPE_DSI1,
+ VC4_ENCODER_TYPE_TXP),
+ VC4_PV_MUXING_TEST("More than 3 outputs: DPI, VEC, DSI1, TXP",
+ VC4_ENCODER_TYPE_DPI,
+ VC4_ENCODER_TYPE_VEC,
+ VC4_ENCODER_TYPE_DSI1,
+ VC4_ENCODER_TYPE_TXP),
+};
+
+KUNIT_ARRAY_PARAM(vc4_test_pv_muxing_invalid,
+ vc4_test_pv_muxing_invalid_params,
+ vc4_test_pv_muxing_desc);
+
+static const struct pv_muxing_param vc5_test_pv_muxing_params[] = {
+ VC5_PV_MUXING_TEST("1 output: DPI",
+ VC4_ENCODER_TYPE_DPI),
+ VC5_PV_MUXING_TEST("1 output: DSI0",
+ VC4_ENCODER_TYPE_DSI0),
+ VC5_PV_MUXING_TEST("1 output: DSI1",
+ VC4_ENCODER_TYPE_DSI1),
+ VC5_PV_MUXING_TEST("1 output: HDMI0",
+ VC4_ENCODER_TYPE_HDMI0),
+ VC5_PV_MUXING_TEST("1 output: HDMI1",
+ VC4_ENCODER_TYPE_HDMI1),
+ VC5_PV_MUXING_TEST("1 output: VEC",
+ VC4_ENCODER_TYPE_VEC),
+ VC5_PV_MUXING_TEST("2 outputs: DPI, DSI1",
+ VC4_ENCODER_TYPE_DPI,
+ VC4_ENCODER_TYPE_DSI1),
+ VC5_PV_MUXING_TEST("2 outputs: DPI, HDMI0",
+ VC4_ENCODER_TYPE_DPI,
+ VC4_ENCODER_TYPE_HDMI0),
+ VC5_PV_MUXING_TEST("2 outputs: DPI, HDMI1",
+ VC4_ENCODER_TYPE_DPI,
+ VC4_ENCODER_TYPE_HDMI1),
+ VC5_PV_MUXING_TEST("2 outputs: DPI, TXP",
+ VC4_ENCODER_TYPE_DPI,
+ VC4_ENCODER_TYPE_TXP),
+ VC5_PV_MUXING_TEST("2 outputs: DPI, VEC",
+ VC4_ENCODER_TYPE_DPI,
+ VC4_ENCODER_TYPE_VEC),
+ VC5_PV_MUXING_TEST("2 outputs: DPI, DSI1",
+ VC4_ENCODER_TYPE_DPI,
+ VC4_ENCODER_TYPE_DSI1),
+ VC5_PV_MUXING_TEST("2 outputs: DSI0, DSI1",
+ VC4_ENCODER_TYPE_DSI0,
+ VC4_ENCODER_TYPE_DSI1),
+ VC5_PV_MUXING_TEST("2 outputs: DSI0, HDMI0",
+ VC4_ENCODER_TYPE_DSI0,
+ VC4_ENCODER_TYPE_HDMI0),
+ VC5_PV_MUXING_TEST("2 outputs: DSI0, HDMI1",
+ VC4_ENCODER_TYPE_DSI0,
+ VC4_ENCODER_TYPE_HDMI1),
+ VC5_PV_MUXING_TEST("2 outputs: DSI0, TXP",
+ VC4_ENCODER_TYPE_DSI0,
+ VC4_ENCODER_TYPE_TXP),
+ VC5_PV_MUXING_TEST("2 outputs: DSI0, VEC",
+ VC4_ENCODER_TYPE_DSI0,
+ VC4_ENCODER_TYPE_VEC),
+ VC5_PV_MUXING_TEST("2 outputs: DSI0, DSI1",
+ VC4_ENCODER_TYPE_DSI0,
+ VC4_ENCODER_TYPE_DSI1),
+ VC5_PV_MUXING_TEST("2 outputs: DSI1, VEC",
+ VC4_ENCODER_TYPE_DSI1,
+ VC4_ENCODER_TYPE_VEC),
+ VC5_PV_MUXING_TEST("2 outputs: DSI1, TXP",
+ VC4_ENCODER_TYPE_DSI1,
+ VC4_ENCODER_TYPE_TXP),
+ VC5_PV_MUXING_TEST("2 outputs: DSI1, HDMI0",
+ VC4_ENCODER_TYPE_DSI1,
+ VC4_ENCODER_TYPE_HDMI0),
+ VC5_PV_MUXING_TEST("2 outputs: DSI1, HDMI1",
+ VC4_ENCODER_TYPE_DSI1,
+ VC4_ENCODER_TYPE_HDMI1),
+ VC5_PV_MUXING_TEST("2 outputs: HDMI0, VEC",
+ VC4_ENCODER_TYPE_HDMI0,
+ VC4_ENCODER_TYPE_VEC),
+ VC5_PV_MUXING_TEST("2 outputs: HDMI0, TXP",
+ VC4_ENCODER_TYPE_HDMI0,
+ VC4_ENCODER_TYPE_TXP),
+ VC5_PV_MUXING_TEST("2 outputs: HDMI0, HDMI1",
+ VC4_ENCODER_TYPE_HDMI0,
+ VC4_ENCODER_TYPE_HDMI1),
+ VC5_PV_MUXING_TEST("2 outputs: HDMI1, VEC",
+ VC4_ENCODER_TYPE_HDMI1,
+ VC4_ENCODER_TYPE_VEC),
+ VC5_PV_MUXING_TEST("2 outputs: HDMI1, TXP",
+ VC4_ENCODER_TYPE_HDMI1,
+ VC4_ENCODER_TYPE_TXP),
+ VC5_PV_MUXING_TEST("2 outputs: TXP, VEC",
+ VC4_ENCODER_TYPE_TXP,
+ VC4_ENCODER_TYPE_VEC),
+ VC5_PV_MUXING_TEST("3 outputs: DPI, VEC, TXP",
+ VC4_ENCODER_TYPE_DPI,
+ VC4_ENCODER_TYPE_VEC,
+ VC4_ENCODER_TYPE_TXP),
+ VC5_PV_MUXING_TEST("3 outputs: DPI, VEC, DSI1",
+ VC4_ENCODER_TYPE_DPI,
+ VC4_ENCODER_TYPE_VEC,
+ VC4_ENCODER_TYPE_DSI1),
+ VC5_PV_MUXING_TEST("3 outputs: DPI, VEC, HDMI0",
+ VC4_ENCODER_TYPE_DPI,
+ VC4_ENCODER_TYPE_VEC,
+ VC4_ENCODER_TYPE_HDMI0),
+ VC5_PV_MUXING_TEST("3 outputs: DPI, VEC, HDMI1",
+ VC4_ENCODER_TYPE_DPI,
+ VC4_ENCODER_TYPE_VEC,
+ VC4_ENCODER_TYPE_HDMI1),
+ VC5_PV_MUXING_TEST("3 outputs: DPI, TXP, DSI1",
+ VC4_ENCODER_TYPE_DPI,
+ VC4_ENCODER_TYPE_TXP,
+ VC4_ENCODER_TYPE_DSI1),
+ VC5_PV_MUXING_TEST("3 outputs: DPI, TXP, HDMI0",
+ VC4_ENCODER_TYPE_DPI,
+ VC4_ENCODER_TYPE_TXP,
+ VC4_ENCODER_TYPE_HDMI0),
+ VC5_PV_MUXING_TEST("3 outputs: DPI, TXP, HDMI1",
+ VC4_ENCODER_TYPE_DPI,
+ VC4_ENCODER_TYPE_TXP,
+ VC4_ENCODER_TYPE_HDMI1),
+ VC5_PV_MUXING_TEST("3 outputs: DPI, DSI1, HDMI0",
+ VC4_ENCODER_TYPE_DPI,
+ VC4_ENCODER_TYPE_DSI1,
+ VC4_ENCODER_TYPE_HDMI0),
+ VC5_PV_MUXING_TEST("3 outputs: DPI, DSI1, HDMI1",
+ VC4_ENCODER_TYPE_DPI,
+ VC4_ENCODER_TYPE_DSI1,
+ VC4_ENCODER_TYPE_HDMI1),
+ VC5_PV_MUXING_TEST("3 outputs: DPI, HDMI0, HDMI1",
+ VC4_ENCODER_TYPE_DPI,
+ VC4_ENCODER_TYPE_HDMI0,
+ VC4_ENCODER_TYPE_HDMI1),
+ VC5_PV_MUXING_TEST("3 outputs: DSI0, VEC, TXP",
+ VC4_ENCODER_TYPE_DSI0,
+ VC4_ENCODER_TYPE_VEC,
+ VC4_ENCODER_TYPE_TXP),
+ VC5_PV_MUXING_TEST("3 outputs: DSI0, VEC, DSI1",
+ VC4_ENCODER_TYPE_DSI0,
+ VC4_ENCODER_TYPE_VEC,
+ VC4_ENCODER_TYPE_DSI1),
+ VC5_PV_MUXING_TEST("3 outputs: DSI0, VEC, HDMI0",
+ VC4_ENCODER_TYPE_DSI0,
+ VC4_ENCODER_TYPE_VEC,
+ VC4_ENCODER_TYPE_HDMI0),
+ VC5_PV_MUXING_TEST("3 outputs: DSI0, VEC, HDMI1",
+ VC4_ENCODER_TYPE_DSI0,
+ VC4_ENCODER_TYPE_VEC,
+ VC4_ENCODER_TYPE_HDMI1),
+ VC5_PV_MUXING_TEST("3 outputs: DSI0, TXP, DSI1",
+ VC4_ENCODER_TYPE_DSI0,
+ VC4_ENCODER_TYPE_TXP,
+ VC4_ENCODER_TYPE_DSI1),
+ VC5_PV_MUXING_TEST("3 outputs: DSI0, TXP, HDMI0",
+ VC4_ENCODER_TYPE_DSI0,
+ VC4_ENCODER_TYPE_TXP,
+ VC4_ENCODER_TYPE_HDMI0),
+ VC5_PV_MUXING_TEST("3 outputs: DSI0, TXP, HDMI1",
+ VC4_ENCODER_TYPE_DSI0,
+ VC4_ENCODER_TYPE_TXP,
+ VC4_ENCODER_TYPE_HDMI1),
+ VC5_PV_MUXING_TEST("3 outputs: DSI0, DSI1, HDMI0",
+ VC4_ENCODER_TYPE_DSI0,
+ VC4_ENCODER_TYPE_DSI1,
+ VC4_ENCODER_TYPE_HDMI0),
+ VC5_PV_MUXING_TEST("3 outputs: DSI0, DSI1, HDMI1",
+ VC4_ENCODER_TYPE_DSI0,
+ VC4_ENCODER_TYPE_DSI1,
+ VC4_ENCODER_TYPE_HDMI1),
+ VC5_PV_MUXING_TEST("3 outputs: DSI0, HDMI0, HDMI1",
+ VC4_ENCODER_TYPE_DSI0,
+ VC4_ENCODER_TYPE_HDMI0,
+ VC4_ENCODER_TYPE_HDMI1),
+};
+
+KUNIT_ARRAY_PARAM(vc5_test_pv_muxing,
+ vc5_test_pv_muxing_params,
+ vc4_test_pv_muxing_desc);
+
+static const struct pv_muxing_param vc5_test_pv_muxing_invalid_params[] = {
+ VC5_PV_MUXING_TEST("DPI/DSI0 Conflict",
+ VC4_ENCODER_TYPE_DPI,
+ VC4_ENCODER_TYPE_DSI0),
+ VC5_PV_MUXING_TEST("More than 3 outputs: DPI, VEC, TXP, DSI1",
+ VC4_ENCODER_TYPE_DPI,
+ VC4_ENCODER_TYPE_VEC,
+ VC4_ENCODER_TYPE_TXP,
+ VC4_ENCODER_TYPE_DSI1),
+ VC5_PV_MUXING_TEST("More than 3 outputs: DPI, VEC, TXP, HDMI0",
+ VC4_ENCODER_TYPE_DPI,
+ VC4_ENCODER_TYPE_VEC,
+ VC4_ENCODER_TYPE_TXP,
+ VC4_ENCODER_TYPE_HDMI0),
+ VC5_PV_MUXING_TEST("More than 3 outputs: DPI, VEC, TXP, HDMI1",
+ VC4_ENCODER_TYPE_DPI,
+ VC4_ENCODER_TYPE_VEC,
+ VC4_ENCODER_TYPE_TXP,
+ VC4_ENCODER_TYPE_HDMI1),
+ VC5_PV_MUXING_TEST("More than 3 outputs: DPI, VEC, DSI1, HDMI0",
+ VC4_ENCODER_TYPE_DPI,
+ VC4_ENCODER_TYPE_VEC,
+ VC4_ENCODER_TYPE_DSI1,
+ VC4_ENCODER_TYPE_HDMI0),
+ VC5_PV_MUXING_TEST("More than 3 outputs: DPI, VEC, DSI1, HDMI1",
+ VC4_ENCODER_TYPE_DPI,
+ VC4_ENCODER_TYPE_VEC,
+ VC4_ENCODER_TYPE_DSI1,
+ VC4_ENCODER_TYPE_HDMI1),
+ VC5_PV_MUXING_TEST("More than 3 outputs: DPI, VEC, HDMI0, HDMI1",
+ VC4_ENCODER_TYPE_DPI,
+ VC4_ENCODER_TYPE_VEC,
+ VC4_ENCODER_TYPE_HDMI0,
+ VC4_ENCODER_TYPE_HDMI1),
+ VC5_PV_MUXING_TEST("More than 3 outputs: DPI, TXP, DSI1, HDMI0",
+ VC4_ENCODER_TYPE_DPI,
+ VC4_ENCODER_TYPE_TXP,
+ VC4_ENCODER_TYPE_DSI1,
+ VC4_ENCODER_TYPE_HDMI0),
+ VC5_PV_MUXING_TEST("More than 3 outputs: DPI, TXP, DSI1, HDMI1",
+ VC4_ENCODER_TYPE_DPI,
+ VC4_ENCODER_TYPE_TXP,
+ VC4_ENCODER_TYPE_DSI1,
+ VC4_ENCODER_TYPE_HDMI1),
+ VC5_PV_MUXING_TEST("More than 3 outputs: DPI, TXP, HDMI0, HDMI1",
+ VC4_ENCODER_TYPE_DPI,
+ VC4_ENCODER_TYPE_TXP,
+ VC4_ENCODER_TYPE_HDMI0,
+ VC4_ENCODER_TYPE_HDMI1),
+ VC5_PV_MUXING_TEST("More than 3 outputs: DPI, DSI1, HDMI0, HDMI1",
+ VC4_ENCODER_TYPE_DPI,
+ VC4_ENCODER_TYPE_DSI1,
+ VC4_ENCODER_TYPE_HDMI0,
+ VC4_ENCODER_TYPE_HDMI1),
+ VC5_PV_MUXING_TEST("More than 3 outputs: DPI, VEC, TXP, DSI1, HDMI0",
+ VC4_ENCODER_TYPE_DPI,
+ VC4_ENCODER_TYPE_VEC,
+ VC4_ENCODER_TYPE_TXP,
+ VC4_ENCODER_TYPE_DSI1,
+ VC4_ENCODER_TYPE_HDMI0),
+ VC5_PV_MUXING_TEST("More than 3 outputs: DPI, VEC, TXP, DSI1, HDMI1",
+ VC4_ENCODER_TYPE_DPI,
+ VC4_ENCODER_TYPE_VEC,
+ VC4_ENCODER_TYPE_TXP,
+ VC4_ENCODER_TYPE_DSI1,
+ VC4_ENCODER_TYPE_HDMI1),
+ VC5_PV_MUXING_TEST("More than 3 outputs: DPI, VEC, TXP, HDMI0, HDMI1",
+ VC4_ENCODER_TYPE_DPI,
+ VC4_ENCODER_TYPE_VEC,
+ VC4_ENCODER_TYPE_TXP,
+ VC4_ENCODER_TYPE_HDMI0,
+ VC4_ENCODER_TYPE_HDMI1),
+ VC5_PV_MUXING_TEST("More than 3 outputs: DPI, VEC, DSI1, HDMI0, HDMI1",
+ VC4_ENCODER_TYPE_DPI,
+ VC4_ENCODER_TYPE_VEC,
+ VC4_ENCODER_TYPE_DSI1,
+ VC4_ENCODER_TYPE_HDMI0,
+ VC4_ENCODER_TYPE_HDMI1),
+ VC5_PV_MUXING_TEST("More than 3 outputs: DPI, TXP, DSI1, HDMI0, HDMI1",
+ VC4_ENCODER_TYPE_DPI,
+ VC4_ENCODER_TYPE_TXP,
+ VC4_ENCODER_TYPE_DSI1,
+ VC4_ENCODER_TYPE_HDMI0,
+ VC4_ENCODER_TYPE_HDMI1),
+ VC5_PV_MUXING_TEST("More than 3 outputs: DSI0, VEC, TXP, DSI1",
+ VC4_ENCODER_TYPE_DSI0,
+ VC4_ENCODER_TYPE_VEC,
+ VC4_ENCODER_TYPE_TXP,
+ VC4_ENCODER_TYPE_DSI1),
+ VC5_PV_MUXING_TEST("More than 3 outputs: DSI0, VEC, TXP, HDMI0",
+ VC4_ENCODER_TYPE_DSI0,
+ VC4_ENCODER_TYPE_VEC,
+ VC4_ENCODER_TYPE_TXP,
+ VC4_ENCODER_TYPE_HDMI0),
+ VC5_PV_MUXING_TEST("More than 3 outputs: DSI0, VEC, TXP, HDMI1",
+ VC4_ENCODER_TYPE_DSI0,
+ VC4_ENCODER_TYPE_VEC,
+ VC4_ENCODER_TYPE_TXP,
+ VC4_ENCODER_TYPE_HDMI1),
+ VC5_PV_MUXING_TEST("More than 3 outputs: DSI0, VEC, DSI1, HDMI0",
+ VC4_ENCODER_TYPE_DSI0,
+ VC4_ENCODER_TYPE_VEC,
+ VC4_ENCODER_TYPE_DSI1,
+ VC4_ENCODER_TYPE_HDMI0),
+ VC5_PV_MUXING_TEST("More than 3 outputs: DSI0, VEC, DSI1, HDMI1",
+ VC4_ENCODER_TYPE_DSI0,
+ VC4_ENCODER_TYPE_VEC,
+ VC4_ENCODER_TYPE_DSI1,
+ VC4_ENCODER_TYPE_HDMI1),
+ VC5_PV_MUXING_TEST("More than 3 outputs: DSI0, VEC, HDMI0, HDMI1",
+ VC4_ENCODER_TYPE_DSI0,
+ VC4_ENCODER_TYPE_VEC,
+ VC4_ENCODER_TYPE_HDMI0,
+ VC4_ENCODER_TYPE_HDMI1),
+ VC5_PV_MUXING_TEST("More than 3 outputs: DSI0, TXP, DSI1, HDMI0",
+ VC4_ENCODER_TYPE_DSI0,
+ VC4_ENCODER_TYPE_TXP,
+ VC4_ENCODER_TYPE_DSI1,
+ VC4_ENCODER_TYPE_HDMI0),
+ VC5_PV_MUXING_TEST("More than 3 outputs: DSI0, TXP, DSI1, HDMI1",
+ VC4_ENCODER_TYPE_DSI0,
+ VC4_ENCODER_TYPE_TXP,
+ VC4_ENCODER_TYPE_DSI1,
+ VC4_ENCODER_TYPE_HDMI1),
+ VC5_PV_MUXING_TEST("More than 3 outputs: DSI0, TXP, HDMI0, HDMI1",
+ VC4_ENCODER_TYPE_DSI0,
+ VC4_ENCODER_TYPE_TXP,
+ VC4_ENCODER_TYPE_HDMI0,
+ VC4_ENCODER_TYPE_HDMI1),
+ VC5_PV_MUXING_TEST("More than 3 outputs: DSI0, DSI1, HDMI0, HDMI1",
+ VC4_ENCODER_TYPE_DSI0,
+ VC4_ENCODER_TYPE_DSI1,
+ VC4_ENCODER_TYPE_HDMI0,
+ VC4_ENCODER_TYPE_HDMI1),
+ VC5_PV_MUXING_TEST("More than 3 outputs: DSI0, VEC, TXP, DSI1, HDMI0",
+ VC4_ENCODER_TYPE_DSI0,
+ VC4_ENCODER_TYPE_VEC,
+ VC4_ENCODER_TYPE_TXP,
+ VC4_ENCODER_TYPE_DSI1,
+ VC4_ENCODER_TYPE_HDMI0),
+ VC5_PV_MUXING_TEST("More than 3 outputs: DSI0, VEC, TXP, DSI1, HDMI1",
+ VC4_ENCODER_TYPE_DSI0,
+ VC4_ENCODER_TYPE_VEC,
+ VC4_ENCODER_TYPE_TXP,
+ VC4_ENCODER_TYPE_DSI1,
+ VC4_ENCODER_TYPE_HDMI1),
+ VC5_PV_MUXING_TEST("More than 3 outputs: DSI0, VEC, TXP, HDMI0, HDMI1",
+ VC4_ENCODER_TYPE_DSI0,
+ VC4_ENCODER_TYPE_VEC,
+ VC4_ENCODER_TYPE_TXP,
+ VC4_ENCODER_TYPE_HDMI0,
+ VC4_ENCODER_TYPE_HDMI1),
+ VC5_PV_MUXING_TEST("More than 3 outputs: DSI0, VEC, DSI1, HDMI0, HDMI1",
+ VC4_ENCODER_TYPE_DSI0,
+ VC4_ENCODER_TYPE_VEC,
+ VC4_ENCODER_TYPE_DSI1,
+ VC4_ENCODER_TYPE_HDMI0,
+ VC4_ENCODER_TYPE_HDMI1),
+ VC5_PV_MUXING_TEST("More than 3 outputs: DSI0, TXP, DSI1, HDMI0, HDMI1",
+ VC4_ENCODER_TYPE_DSI0,
+ VC4_ENCODER_TYPE_TXP,
+ VC4_ENCODER_TYPE_DSI1,
+ VC4_ENCODER_TYPE_HDMI0,
+ VC4_ENCODER_TYPE_HDMI1),
+ VC5_PV_MUXING_TEST("More than 3 outputs: VEC, TXP, DSI1, HDMI0, HDMI1",
+ VC4_ENCODER_TYPE_VEC,
+ VC4_ENCODER_TYPE_TXP,
+ VC4_ENCODER_TYPE_DSI1,
+ VC4_ENCODER_TYPE_HDMI0,
+ VC4_ENCODER_TYPE_HDMI1),
+ VC5_PV_MUXING_TEST("More than 3 outputs: DPI, VEC, TXP, DSI1, HDMI0, HDMI1",
+ VC4_ENCODER_TYPE_DPI,
+ VC4_ENCODER_TYPE_VEC,
+ VC4_ENCODER_TYPE_TXP,
+ VC4_ENCODER_TYPE_DSI1,
+ VC4_ENCODER_TYPE_HDMI0,
+ VC4_ENCODER_TYPE_HDMI1),
+ VC5_PV_MUXING_TEST("More than 3 outputs: DSI0, VEC, TXP, DSI1, HDMI0, HDMI1",
+ VC4_ENCODER_TYPE_DSI0,
+ VC4_ENCODER_TYPE_VEC,
+ VC4_ENCODER_TYPE_TXP,
+ VC4_ENCODER_TYPE_DSI1,
+ VC4_ENCODER_TYPE_HDMI0,
+ VC4_ENCODER_TYPE_HDMI1),
+};
+
+KUNIT_ARRAY_PARAM(vc5_test_pv_muxing_invalid,
+ vc5_test_pv_muxing_invalid_params,
+ vc4_test_pv_muxing_desc);
+
+static void drm_vc4_test_pv_muxing(struct kunit *test)
+{
+ const struct pv_muxing_param *params = test->param_value;
+ const struct pv_muxing_priv *priv = test->priv;
+ struct drm_atomic_state *state = priv->state;
+ unsigned int i;
+ int ret;
+
+ for (i = 0; i < params->nencoders; i++) {
+ enum vc4_encoder_type enc_type = params->encoders[i];
+
+ ret = vc4_mock_atomic_add_output(test, state, enc_type);
+ KUNIT_ASSERT_EQ(test, ret, 0);
+ }
+
+ ret = drm_atomic_check_only(state);
+ KUNIT_EXPECT_EQ(test, ret, 0);
+
+ KUNIT_EXPECT_TRUE(test,
+ check_fifo_conflict(test, state));
+
+ for (i = 0; i < params->nencoders; i++) {
+ enum vc4_encoder_type enc_type = params->encoders[i];
+
+ KUNIT_EXPECT_TRUE(test, check_channel_for_encoder(test, state, enc_type,
+ params->check_fn));
+ }
+}
+
+static void drm_vc4_test_pv_muxing_invalid(struct kunit *test)
+{
+ const struct pv_muxing_param *params = test->param_value;
+ const struct pv_muxing_priv *priv = test->priv;
+ struct drm_atomic_state *state = priv->state;
+ unsigned int i;
+ int ret;
+
+ for (i = 0; i < params->nencoders; i++) {
+ enum vc4_encoder_type enc_type = params->encoders[i];
+
+ ret = vc4_mock_atomic_add_output(test, state, enc_type);
+ KUNIT_ASSERT_EQ(test, ret, 0);
+ }
+
+ ret = drm_atomic_check_only(state);
+ KUNIT_EXPECT_LT(test, ret, 0);
+}
+
+static int vc4_pv_muxing_test_init(struct kunit *test)
+{
+ const struct pv_muxing_param *params = test->param_value;
+ struct drm_atomic_state *state;
+ struct pv_muxing_priv *priv;
+ struct drm_device *drm;
+ struct vc4_dev *vc4;
+
+ priv = kunit_kzalloc(test, sizeof(*priv), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, priv);
+ test->priv = priv;
+
+ vc4 = params->mock_fn(test);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, vc4);
+ priv->vc4 = vc4;
+
+ drm_modeset_acquire_init(&priv->ctx, 0);
+
+ drm = &vc4->base;
+ state = drm_atomic_state_alloc(drm);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, state);
+
+ state->acquire_ctx = &priv->ctx;
+
+ priv->state = state;
+
+ return 0;
+}
+
+static void vc4_pv_muxing_test_exit(struct kunit *test)
+{
+ struct pv_muxing_priv *priv = test->priv;
+ struct vc4_dev *vc4 = priv->vc4;
+ struct drm_device *drm = &vc4->base;
+ struct drm_atomic_state *state = priv->state;
+
+ drm_atomic_state_put(state);
+ drm_modeset_drop_locks(&priv->ctx);
+ drm_modeset_acquire_fini(&priv->ctx);
+ drm_dev_unregister(drm);
+ drm_kunit_helper_free_device(test, vc4->dev);
+}
+
+static struct kunit_case vc4_pv_muxing_tests[] = {
+ KUNIT_CASE_PARAM(drm_vc4_test_pv_muxing,
+ vc4_test_pv_muxing_gen_params),
+ KUNIT_CASE_PARAM(drm_vc4_test_pv_muxing_invalid,
+ vc4_test_pv_muxing_invalid_gen_params),
+ {}
+};
+
+static struct kunit_suite vc4_pv_muxing_test_suite = {
+ .name = "vc4-pv-muxing-combinations",
+ .init = vc4_pv_muxing_test_init,
+ .exit = vc4_pv_muxing_test_exit,
+ .test_cases = vc4_pv_muxing_tests,
+};
+
+static struct kunit_case vc5_pv_muxing_tests[] = {
+ KUNIT_CASE_PARAM(drm_vc4_test_pv_muxing,
+ vc5_test_pv_muxing_gen_params),
+ KUNIT_CASE_PARAM(drm_vc4_test_pv_muxing_invalid,
+ vc5_test_pv_muxing_invalid_gen_params),
+ {}
+};
+
+static struct kunit_suite vc5_pv_muxing_test_suite = {
+ .name = "vc5-pv-muxing-combinations",
+ .init = vc4_pv_muxing_test_init,
+ .exit = vc4_pv_muxing_test_exit,
+ .test_cases = vc5_pv_muxing_tests,
+};
+
+/* See
+ * https://lore.kernel.org/all/3e113525-aa89-b1e2-56b7-ca55bd41d057@samsung.com/
+ * and
+ * https://lore.kernel.org/dri-devel/20200917121623.42023-1-maxime@cerno.tech/
+ */
+static void drm_test_vc5_pv_muxing_bugs_subsequent_crtc_enable(struct kunit *test)
+{
+ struct drm_modeset_acquire_ctx ctx;
+ struct drm_atomic_state *state;
+ struct vc4_crtc_state *new_vc4_crtc_state;
+ struct vc4_hvs_state *new_hvs_state;
+ unsigned int hdmi0_channel;
+ unsigned int hdmi1_channel;
+ struct drm_device *drm;
+ struct vc4_dev *vc4;
+ int ret;
+
+ vc4 = vc5_mock_device(test);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, vc4);
+
+ drm_modeset_acquire_init(&ctx, 0);
+
+ drm = &vc4->base;
+ state = drm_atomic_state_alloc(drm);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, state);
+
+ state->acquire_ctx = &ctx;
+
+ ret = vc4_mock_atomic_add_output(test, state, VC4_ENCODER_TYPE_HDMI0);
+ KUNIT_ASSERT_EQ(test, ret, 0);
+
+ ret = drm_atomic_check_only(state);
+ KUNIT_ASSERT_EQ(test, ret, 0);
+
+ new_hvs_state = vc4_hvs_get_new_global_state(state);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, new_hvs_state);
+
+ new_vc4_crtc_state = get_vc4_crtc_state_for_encoder(test, state,
+ VC4_ENCODER_TYPE_HDMI0);
+ KUNIT_ASSERT_NOT_NULL(test, new_vc4_crtc_state);
+
+ hdmi0_channel = new_vc4_crtc_state->assigned_channel;
+ KUNIT_ASSERT_NE(test, hdmi0_channel, VC4_HVS_CHANNEL_DISABLED);
+ KUNIT_ASSERT_TRUE(test, new_hvs_state->fifo_state[hdmi0_channel].in_use);
+
+ ret = drm_atomic_helper_swap_state(state, false);
+ KUNIT_ASSERT_EQ(test, ret, 0);
+
+ drm_atomic_state_put(state);
+
+ state = drm_atomic_state_alloc(drm);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, state);
+
+ state->acquire_ctx = &ctx;
+
+ ret = vc4_mock_atomic_add_output(test, state, VC4_ENCODER_TYPE_HDMI1);
+ KUNIT_ASSERT_EQ(test, ret, 0);
+
+ ret = drm_atomic_check_only(state);
+ KUNIT_ASSERT_EQ(test, ret, 0);
+
+ new_hvs_state = vc4_hvs_get_new_global_state(state);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, new_hvs_state);
+
+ new_vc4_crtc_state = get_vc4_crtc_state_for_encoder(test, state,
+ VC4_ENCODER_TYPE_HDMI1);
+ KUNIT_ASSERT_NOT_NULL(test, new_vc4_crtc_state);
+
+ hdmi1_channel = new_vc4_crtc_state->assigned_channel;
+ KUNIT_ASSERT_NE(test, hdmi1_channel, VC4_HVS_CHANNEL_DISABLED);
+ KUNIT_ASSERT_TRUE(test, new_hvs_state->fifo_state[hdmi1_channel].in_use);
+
+ KUNIT_EXPECT_NE(test, hdmi0_channel, hdmi1_channel);
+
+ drm_atomic_state_put(state);
+ drm_modeset_drop_locks(&ctx);
+ drm_modeset_acquire_fini(&ctx);
+ drm_dev_unregister(drm);
+ drm_kunit_helper_free_device(test, vc4->dev);
+}
+
+static void drm_test_vc5_pv_muxing_bugs_stable_fifo(struct kunit *test)
+{
+ struct drm_modeset_acquire_ctx ctx;
+ struct drm_atomic_state *state;
+ struct vc4_crtc_state *new_vc4_crtc_state;
+ struct vc4_hvs_state *new_hvs_state;
+ unsigned int old_hdmi0_channel;
+ unsigned int old_hdmi1_channel;
+ struct drm_device *drm;
+ struct vc4_dev *vc4;
+ int ret;
+
+ vc4 = vc5_mock_device(test);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, vc4);
+
+ drm_modeset_acquire_init(&ctx, 0);
+
+ drm = &vc4->base;
+ state = drm_atomic_state_alloc(drm);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, state);
+
+ state->acquire_ctx = &ctx;
+
+ ret = vc4_mock_atomic_add_output(test, state, VC4_ENCODER_TYPE_HDMI0);
+ KUNIT_ASSERT_EQ(test, ret, 0);
+
+ ret = vc4_mock_atomic_add_output(test, state, VC4_ENCODER_TYPE_HDMI1);
+ KUNIT_ASSERT_EQ(test, ret, 0);
+
+ ret = drm_atomic_check_only(state);
+ KUNIT_ASSERT_EQ(test, ret, 0);
+
+ new_hvs_state = vc4_hvs_get_new_global_state(state);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, new_hvs_state);
+
+ new_vc4_crtc_state = get_vc4_crtc_state_for_encoder(test, state,
+ VC4_ENCODER_TYPE_HDMI0);
+ KUNIT_ASSERT_NOT_NULL(test, new_vc4_crtc_state);
+
+ old_hdmi0_channel = new_vc4_crtc_state->assigned_channel;
+ KUNIT_ASSERT_NE(test, old_hdmi0_channel, VC4_HVS_CHANNEL_DISABLED);
+ KUNIT_ASSERT_TRUE(test, new_hvs_state->fifo_state[old_hdmi0_channel].in_use);
+
+ new_vc4_crtc_state = get_vc4_crtc_state_for_encoder(test, state,
+ VC4_ENCODER_TYPE_HDMI1);
+ KUNIT_ASSERT_NOT_NULL(test, new_vc4_crtc_state);
+
+ old_hdmi1_channel = new_vc4_crtc_state->assigned_channel;
+ KUNIT_ASSERT_NE(test, old_hdmi1_channel, VC4_HVS_CHANNEL_DISABLED);
+ KUNIT_ASSERT_TRUE(test, new_hvs_state->fifo_state[old_hdmi1_channel].in_use);
+
+ ret = drm_atomic_helper_swap_state(state, false);
+ KUNIT_ASSERT_EQ(test, ret, 0);
+
+ drm_atomic_state_put(state);
+
+ state = drm_atomic_state_alloc(drm);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, state);
+
+ state->acquire_ctx = &ctx;
+
+ ret = vc4_mock_atomic_del_output(test, state, VC4_ENCODER_TYPE_HDMI0);
+ KUNIT_ASSERT_EQ(test, ret, 0);
+
+ ret = drm_atomic_check_only(state);
+ KUNIT_ASSERT_EQ(test, ret, 0);
+
+ new_hvs_state = vc4_hvs_get_new_global_state(state);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, new_hvs_state);
+
+ new_vc4_crtc_state = get_vc4_crtc_state_for_encoder(test, state,
+ VC4_ENCODER_TYPE_HDMI1);
+
+ if (new_vc4_crtc_state) {
+ unsigned int hdmi1_channel;
+
+ hdmi1_channel = new_vc4_crtc_state->assigned_channel;
+ KUNIT_ASSERT_NE(test, hdmi1_channel, VC4_HVS_CHANNEL_DISABLED);
+ KUNIT_ASSERT_TRUE(test, new_hvs_state->fifo_state[hdmi1_channel].in_use);
+
+ KUNIT_EXPECT_EQ(test, old_hdmi1_channel, hdmi1_channel);
+ }
+
+ drm_atomic_state_put(state);
+ drm_modeset_drop_locks(&ctx);
+ drm_modeset_acquire_fini(&ctx);
+ drm_dev_unregister(drm);
+ drm_kunit_helper_free_device(test, vc4->dev);
+}
+
+static void
+drm_test_vc5_pv_muxing_bugs_subsequent_crtc_enable_too_many_crtc_state(struct kunit *test)
+{
+ struct drm_modeset_acquire_ctx ctx;
+ struct drm_atomic_state *state;
+ struct vc4_crtc_state *new_vc4_crtc_state;
+ struct drm_device *drm;
+ struct vc4_dev *vc4;
+ int ret;
+
+ vc4 = vc5_mock_device(test);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, vc4);
+
+ drm_modeset_acquire_init(&ctx, 0);
+
+ drm = &vc4->base;
+ state = drm_atomic_state_alloc(drm);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, state);
+
+ state->acquire_ctx = &ctx;
+
+ ret = vc4_mock_atomic_add_output(test, state, VC4_ENCODER_TYPE_HDMI0);
+ KUNIT_ASSERT_EQ(test, ret, 0);
+
+ ret = drm_atomic_check_only(state);
+ KUNIT_ASSERT_EQ(test, ret, 0);
+
+ ret = drm_atomic_helper_swap_state(state, false);
+ KUNIT_ASSERT_EQ(test, ret, 0);
+
+ drm_atomic_state_put(state);
+
+ state = drm_atomic_state_alloc(drm);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, state);
+
+ state->acquire_ctx = &ctx;
+
+ ret = vc4_mock_atomic_add_output(test, state, VC4_ENCODER_TYPE_HDMI1);
+ KUNIT_ASSERT_EQ(test, ret, 0);
+
+ ret = drm_atomic_check_only(state);
+ KUNIT_ASSERT_EQ(test, ret, 0);
+
+ new_vc4_crtc_state = get_vc4_crtc_state_for_encoder(test, state,
+ VC4_ENCODER_TYPE_HDMI0);
+ KUNIT_EXPECT_NULL(test, new_vc4_crtc_state);
+
+ drm_atomic_state_put(state);
+ drm_modeset_drop_locks(&ctx);
+ drm_modeset_acquire_fini(&ctx);
+ drm_dev_unregister(drm);
+ drm_kunit_helper_free_device(test, vc4->dev);
+}
+
+static struct kunit_case vc5_pv_muxing_bugs_tests[] = {
+ KUNIT_CASE(drm_test_vc5_pv_muxing_bugs_subsequent_crtc_enable),
+ KUNIT_CASE(drm_test_vc5_pv_muxing_bugs_subsequent_crtc_enable_too_many_crtc_state),
+ KUNIT_CASE(drm_test_vc5_pv_muxing_bugs_stable_fifo),
+ {}
+};
+
+static struct kunit_suite vc5_pv_muxing_bugs_test_suite = {
+ .name = "vc5-pv-muxing-bugs",
+ .test_cases = vc5_pv_muxing_bugs_tests,
+};
+
+kunit_test_suites(
+ &vc4_pv_muxing_test_suite,
+ &vc5_pv_muxing_test_suite,
+ &vc5_pv_muxing_bugs_test_suite
+);
diff --git a/drivers/gpu/drm/vc4/vc4_bo.c b/drivers/gpu/drm/vc4/vc4_bo.c
index c5947ed8cc81..86d629e45307 100644
--- a/drivers/gpu/drm/vc4/vc4_bo.c
+++ b/drivers/gpu/drm/vc4/vc4_bo.c
@@ -69,8 +69,8 @@ static void vc4_bo_stats_print(struct drm_printer *p, struct vc4_dev *vc4)
static int vc4_bo_stats_debugfs(struct seq_file *m, void *unused)
{
- struct drm_info_node *node = (struct drm_info_node *)m->private;
- struct drm_device *dev = node->minor->dev;
+ struct drm_debugfs_entry *entry = m->private;
+ struct drm_device *dev = entry->dev;
struct vc4_dev *vc4 = to_vc4_dev(dev);
struct drm_printer p = drm_seq_file_printer(m);
@@ -991,15 +991,11 @@ int vc4_bo_debugfs_init(struct drm_minor *minor)
{
struct drm_device *drm = minor->dev;
struct vc4_dev *vc4 = to_vc4_dev(drm);
- int ret;
if (!vc4->v3d)
return -ENODEV;
- ret = vc4_debugfs_add_file(minor, "bo_stats",
- vc4_bo_stats_debugfs, NULL);
- if (ret)
- return ret;
+ drm_debugfs_add_file(drm, "bo_stats", vc4_bo_stats_debugfs, NULL);
return 0;
}
diff --git a/drivers/gpu/drm/vc4/vc4_crtc.c b/drivers/gpu/drm/vc4/vc4_crtc.c
index 7258975331ca..bef9d45ef1df 100644
--- a/drivers/gpu/drm/vc4/vc4_crtc.c
+++ b/drivers/gpu/drm/vc4/vc4_crtc.c
@@ -50,8 +50,17 @@
#define HVS_FIFO_LATENCY_PIX 6
-#define CRTC_WRITE(offset, val) writel(val, vc4_crtc->regs + (offset))
-#define CRTC_READ(offset) readl(vc4_crtc->regs + (offset))
+#define CRTC_WRITE(offset, val) \
+ do { \
+ kunit_fail_current_test("Accessing a register in a unit test!\n"); \
+ writel(val, vc4_crtc->regs + (offset)); \
+ } while (0)
+
+#define CRTC_READ(offset) \
+ ({ \
+ kunit_fail_current_test("Accessing a register in a unit test!\n"); \
+ readl(vc4_crtc->regs + (offset)); \
+ })
static const struct debugfs_reg32 crtc_regs[] = {
VC4_REG32(PV_CONTROL),
@@ -326,8 +335,14 @@ static void vc4_crtc_config_pv(struct drm_crtc *crtc, struct drm_encoder *encode
bool is_dsi = (vc4_encoder->type == VC4_ENCODER_TYPE_DSI0 ||
vc4_encoder->type == VC4_ENCODER_TYPE_DSI1);
bool is_dsi1 = vc4_encoder->type == VC4_ENCODER_TYPE_DSI1;
+ bool is_vec = vc4_encoder->type == VC4_ENCODER_TYPE_VEC;
u32 format = is_dsi1 ? PV_CONTROL_FORMAT_DSIV_24 : PV_CONTROL_FORMAT_24;
u8 ppc = pv_data->pixels_per_clock;
+
+ u16 vert_bp = mode->crtc_vtotal - mode->crtc_vsync_end;
+ u16 vert_sync = mode->crtc_vsync_end - mode->crtc_vsync_start;
+ u16 vert_fp = mode->crtc_vsync_start - mode->crtc_vdisplay;
+
bool debug_dump_regs = false;
int idx;
@@ -355,49 +370,60 @@ static void vc4_crtc_config_pv(struct drm_crtc *crtc, struct drm_encoder *encode
VC4_SET_FIELD(mode->hdisplay * pixel_rep / ppc,
PV_HORZB_HACTIVE));
- CRTC_WRITE(PV_VERTA,
- VC4_SET_FIELD(mode->crtc_vtotal - mode->crtc_vsync_end +
- interlace,
- PV_VERTA_VBP) |
- VC4_SET_FIELD(mode->crtc_vsync_end - mode->crtc_vsync_start,
- PV_VERTA_VSYNC));
- CRTC_WRITE(PV_VERTB,
- VC4_SET_FIELD(mode->crtc_vsync_start - mode->crtc_vdisplay,
- PV_VERTB_VFP) |
- VC4_SET_FIELD(mode->crtc_vdisplay, PV_VERTB_VACTIVE));
-
if (interlace) {
+ bool odd_field_first = false;
+ u32 field_delay = mode->htotal * pixel_rep / (2 * ppc);
+ u16 vert_bp_even = vert_bp;
+ u16 vert_fp_even = vert_fp;
+
+ if (is_vec) {
+ /* VEC (composite output) */
+ ++field_delay;
+ if (mode->htotal == 858) {
+ /* 525-line mode (NTSC or PAL-M) */
+ odd_field_first = true;
+ }
+ }
+
+ if (odd_field_first)
+ ++vert_fp_even;
+ else
+ ++vert_bp;
+
CRTC_WRITE(PV_VERTA_EVEN,
- VC4_SET_FIELD(mode->crtc_vtotal -
- mode->crtc_vsync_end,
- PV_VERTA_VBP) |
- VC4_SET_FIELD(mode->crtc_vsync_end -
- mode->crtc_vsync_start,
- PV_VERTA_VSYNC));
+ VC4_SET_FIELD(vert_bp_even, PV_VERTA_VBP) |
+ VC4_SET_FIELD(vert_sync, PV_VERTA_VSYNC));
CRTC_WRITE(PV_VERTB_EVEN,
- VC4_SET_FIELD(mode->crtc_vsync_start -
- mode->crtc_vdisplay,
- PV_VERTB_VFP) |
+ VC4_SET_FIELD(vert_fp_even, PV_VERTB_VFP) |
VC4_SET_FIELD(mode->crtc_vdisplay, PV_VERTB_VACTIVE));
- /* We set up first field even mode for HDMI. VEC's
- * NTSC mode would want first field odd instead, once
- * we support it (to do so, set ODD_FIRST and put the
- * delay in VSYNCD_EVEN instead).
+ /* We set up first field even mode for HDMI and VEC's PAL.
+ * For NTSC, we need first field odd.
*/
CRTC_WRITE(PV_V_CONTROL,
PV_VCONTROL_CONTINUOUS |
(is_dsi ? PV_VCONTROL_DSI : 0) |
PV_VCONTROL_INTERLACE |
- VC4_SET_FIELD(mode->htotal * pixel_rep / (2 * ppc),
- PV_VCONTROL_ODD_DELAY));
- CRTC_WRITE(PV_VSYNCD_EVEN, 0);
+ (odd_field_first
+ ? PV_VCONTROL_ODD_FIRST
+ : VC4_SET_FIELD(field_delay,
+ PV_VCONTROL_ODD_DELAY)));
+ CRTC_WRITE(PV_VSYNCD_EVEN,
+ (odd_field_first ? field_delay : 0));
} else {
CRTC_WRITE(PV_V_CONTROL,
PV_VCONTROL_CONTINUOUS |
(is_dsi ? PV_VCONTROL_DSI : 0));
+ CRTC_WRITE(PV_VSYNCD_EVEN, 0);
}
+ CRTC_WRITE(PV_VERTA,
+ VC4_SET_FIELD(vert_bp, PV_VERTA_VBP) |
+ VC4_SET_FIELD(vert_sync, PV_VERTA_VSYNC));
+ CRTC_WRITE(PV_VERTB,
+ VC4_SET_FIELD(vert_fp, PV_VERTB_VFP) |
+ VC4_SET_FIELD(mode->crtc_vdisplay, PV_VERTB_VACTIVE));
+
if (is_dsi)
CRTC_WRITE(PV_HACT_ACT, mode->hdisplay * pixel_rep);
@@ -486,21 +512,6 @@ static int vc4_crtc_disable(struct drm_crtc *crtc,
return 0;
}
-static struct drm_encoder *vc4_crtc_get_encoder_by_type(struct drm_crtc *crtc,
- enum vc4_encoder_type type)
-{
- struct drm_encoder *encoder;
-
- drm_for_each_encoder(encoder, crtc->dev) {
- struct vc4_encoder *vc4_encoder = to_vc4_encoder(encoder);
-
- if (vc4_encoder->type == type)
- return encoder;
- }
-
- return NULL;
-}
-
int vc4_crtc_disable_at_boot(struct drm_crtc *crtc)
{
struct drm_device *drm = crtc->dev;
@@ -536,7 +547,7 @@ int vc4_crtc_disable_at_boot(struct drm_crtc *crtc)
pv_data = vc4_crtc_to_vc4_pv_data(vc4_crtc);
encoder_type = pv_data->encoder_types[encoder_sel];
- encoder = vc4_crtc_get_encoder_by_type(crtc, encoder_type);
+ encoder = vc4_find_encoder_by_type(drm, encoder_type);
if (WARN_ON(!encoder))
return 0;
@@ -690,8 +701,8 @@ void vc4_crtc_get_margins(struct drm_crtc_state *state,
}
}
-static int vc4_crtc_atomic_check(struct drm_crtc *crtc,
- struct drm_atomic_state *state)
+int vc4_crtc_atomic_check(struct drm_crtc *crtc,
+ struct drm_atomic_state *state)
{
struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state,
crtc);
@@ -1096,12 +1107,9 @@ int vc4_crtc_late_register(struct drm_crtc *crtc)
struct drm_device *drm = crtc->dev;
struct vc4_crtc *vc4_crtc = to_vc4_crtc(crtc);
const struct vc4_crtc_data *crtc_data = vc4_crtc_to_vc4_crtc_data(vc4_crtc);
- int ret;
- ret = vc4_debugfs_add_regset32(drm->primary, crtc_data->debugfs_name,
- &vc4_crtc->regset);
- if (ret)
- return ret;
+ vc4_debugfs_add_regset32(drm, crtc_data->debugfs_name,
+ &vc4_crtc->regset);
return 0;
}
@@ -1131,8 +1139,9 @@ static const struct drm_crtc_helper_funcs vc4_crtc_helper_funcs = {
.get_scanout_position = vc4_crtc_get_scanout_position,
};
-static const struct vc4_pv_data bcm2835_pv0_data = {
+const struct vc4_pv_data bcm2835_pv0_data = {
.base = {
+ .name = "pixelvalve-0",
.debugfs_name = "crtc0_regs",
.hvs_available_channels = BIT(0),
.hvs_output = 0,
@@ -1145,8 +1154,9 @@ static const struct vc4_pv_data bcm2835_pv0_data = {
},
};
-static const struct vc4_pv_data bcm2835_pv1_data = {
+const struct vc4_pv_data bcm2835_pv1_data = {
.base = {
+ .name = "pixelvalve-1",
.debugfs_name = "crtc1_regs",
.hvs_available_channels = BIT(2),
.hvs_output = 2,
@@ -1159,8 +1169,9 @@ static const struct vc4_pv_data bcm2835_pv1_data = {
},
};
-static const struct vc4_pv_data bcm2835_pv2_data = {
+const struct vc4_pv_data bcm2835_pv2_data = {
.base = {
+ .name = "pixelvalve-2",
.debugfs_name = "crtc2_regs",
.hvs_available_channels = BIT(1),
.hvs_output = 1,
@@ -1173,8 +1184,9 @@ static const struct vc4_pv_data bcm2835_pv2_data = {
},
};
-static const struct vc4_pv_data bcm2711_pv0_data = {
+const struct vc4_pv_data bcm2711_pv0_data = {
.base = {
+ .name = "pixelvalve-0",
.debugfs_name = "crtc0_regs",
.hvs_available_channels = BIT(0),
.hvs_output = 0,
@@ -1187,8 +1199,9 @@ static const struct vc4_pv_data bcm2711_pv0_data = {
},
};
-static const struct vc4_pv_data bcm2711_pv1_data = {
+const struct vc4_pv_data bcm2711_pv1_data = {
.base = {
+ .name = "pixelvalve-1",
.debugfs_name = "crtc1_regs",
.hvs_available_channels = BIT(0) | BIT(1) | BIT(2),
.hvs_output = 3,
@@ -1201,8 +1214,9 @@ static const struct vc4_pv_data bcm2711_pv1_data = {
},
};
-static const struct vc4_pv_data bcm2711_pv2_data = {
+const struct vc4_pv_data bcm2711_pv2_data = {
.base = {
+ .name = "pixelvalve-2",
.debugfs_name = "crtc2_regs",
.hvs_available_channels = BIT(0) | BIT(1) | BIT(2),
.hvs_output = 4,
@@ -1214,8 +1228,9 @@ static const struct vc4_pv_data bcm2711_pv2_data = {
},
};
-static const struct vc4_pv_data bcm2711_pv3_data = {
+const struct vc4_pv_data bcm2711_pv3_data = {
.base = {
+ .name = "pixelvalve-3",
.debugfs_name = "crtc3_regs",
.hvs_available_channels = BIT(1),
.hvs_output = 1,
@@ -1227,8 +1242,9 @@ static const struct vc4_pv_data bcm2711_pv3_data = {
},
};
-static const struct vc4_pv_data bcm2711_pv4_data = {
+const struct vc4_pv_data bcm2711_pv4_data = {
.base = {
+ .name = "pixelvalve-4",
.debugfs_name = "crtc4_regs",
.hvs_available_channels = BIT(0) | BIT(1) | BIT(2),
.hvs_output = 5,
@@ -1278,31 +1294,44 @@ static void vc4_set_crtc_possible_masks(struct drm_device *drm,
}
}
-int vc4_crtc_init(struct drm_device *drm, struct vc4_crtc *vc4_crtc,
- const struct drm_crtc_funcs *crtc_funcs,
- const struct drm_crtc_helper_funcs *crtc_helper_funcs)
+/**
+ * __vc4_crtc_init - Initializes a CRTC
+ * @drm: DRM Device
+ * @pdev: CRTC Platform Device
+ * @vc4_crtc: CRTC Object to Initialize
+ * @data: Configuration data associated with this CRTC
+ * @primary_plane: Primary plane for CRTC
+ * @crtc_funcs: Callbacks for the new CRTC
+ * @crtc_helper_funcs: Helper Callbacks for the new CRTC
+ * @feeds_txp: Is this CRTC connected to the TXP?
+ *
+ * Initializes our private CRTC structure. This function is mostly
+ * relevant for KUnit testing, all other users should use
+ * vc4_crtc_init() instead.
+ *
+ * Returns:
+ * 0 on success, a negative error code on failure.
+ */
+int __vc4_crtc_init(struct drm_device *drm,
+ struct platform_device *pdev,
+ struct vc4_crtc *vc4_crtc,
+ const struct vc4_crtc_data *data,
+ struct drm_plane *primary_plane,
+ const struct drm_crtc_funcs *crtc_funcs,
+ const struct drm_crtc_helper_funcs *crtc_helper_funcs,
+ bool feeds_txp)
{
struct vc4_dev *vc4 = to_vc4_dev(drm);
struct drm_crtc *crtc = &vc4_crtc->base;
- struct drm_plane *primary_plane;
unsigned int i;
int ret;
- /* For now, we create just the primary and the legacy cursor
- * planes. We should be able to stack more planes on easily,
- * but to do that we would need to compute the bandwidth
- * requirement of the plane configuration, and reject ones
- * that will take too much.
- */
- primary_plane = vc4_plane_init(drm, DRM_PLANE_TYPE_PRIMARY, 0);
- if (IS_ERR(primary_plane)) {
- dev_err(drm->dev, "failed to construct primary plane\n");
- return PTR_ERR(primary_plane);
- }
-
+ vc4_crtc->data = data;
+ vc4_crtc->pdev = pdev;
+ vc4_crtc->feeds_txp = feeds_txp;
spin_lock_init(&vc4_crtc->irq_lock);
ret = drmm_crtc_init_with_planes(drm, crtc, primary_plane, NULL,
- crtc_funcs, NULL);
+ crtc_funcs, data->name);
if (ret)
return ret;
@@ -1328,6 +1357,31 @@ int vc4_crtc_init(struct drm_device *drm, struct vc4_crtc *vc4_crtc,
return 0;
}
+int vc4_crtc_init(struct drm_device *drm, struct platform_device *pdev,
+ struct vc4_crtc *vc4_crtc,
+ const struct vc4_crtc_data *data,
+ const struct drm_crtc_funcs *crtc_funcs,
+ const struct drm_crtc_helper_funcs *crtc_helper_funcs,
+ bool feeds_txp)
+{
+ struct drm_plane *primary_plane;
+
+ /* For now, we create just the primary and the legacy cursor
+ * planes. We should be able to stack more planes on easily,
+ * but to do that we would need to compute the bandwidth
+ * requirement of the plane configuration, and reject ones
+ * that will take too much.
+ */
+ primary_plane = vc4_plane_init(drm, DRM_PLANE_TYPE_PRIMARY, 0);
+ if (IS_ERR(primary_plane)) {
+ dev_err(drm->dev, "failed to construct primary plane\n");
+ return PTR_ERR(primary_plane);
+ }
+
+ return __vc4_crtc_init(drm, pdev, vc4_crtc, data, primary_plane,
+ crtc_funcs, crtc_helper_funcs, feeds_txp);
+}
+
static int vc4_crtc_bind(struct device *dev, struct device *master, void *data)
{
struct platform_device *pdev = to_platform_device(dev);
@@ -1345,8 +1399,6 @@ static int vc4_crtc_bind(struct device *dev, struct device *master, void *data)
pv_data = of_device_get_match_data(dev);
if (!pv_data)
return -ENODEV;
- vc4_crtc->data = &pv_data->base;
- vc4_crtc->pdev = pdev;
vc4_crtc->regs = vc4_ioremap_regs(pdev, 0);
if (IS_ERR(vc4_crtc->regs))
@@ -1356,8 +1408,9 @@ static int vc4_crtc_bind(struct device *dev, struct device *master, void *data)
vc4_crtc->regset.regs = crtc_regs;
vc4_crtc->regset.nregs = ARRAY_SIZE(crtc_regs);
- ret = vc4_crtc_init(drm, vc4_crtc,
- &vc4_crtc_funcs, &vc4_crtc_helper_funcs);
+ ret = vc4_crtc_init(drm, pdev, vc4_crtc, &pv_data->base,
+ &vc4_crtc_funcs, &vc4_crtc_helper_funcs,
+ false);
if (ret)
return ret;
vc4_set_crtc_possible_masks(drm, crtc);
diff --git a/drivers/gpu/drm/vc4/vc4_debugfs.c b/drivers/gpu/drm/vc4/vc4_debugfs.c
index 19cda4f91a82..fac624a663ea 100644
--- a/drivers/gpu/drm/vc4/vc4_debugfs.c
+++ b/drivers/gpu/drm/vc4/vc4_debugfs.c
@@ -34,9 +34,9 @@ vc4_debugfs_init(struct drm_minor *minor)
static int vc4_debugfs_regset32(struct seq_file *m, void *unused)
{
- struct drm_info_node *node = (struct drm_info_node *)m->private;
- struct drm_device *drm = node->minor->dev;
- struct debugfs_regset32 *regset = node->info_ent->data;
+ struct drm_debugfs_entry *entry = m->private;
+ struct drm_device *drm = entry->dev;
+ struct debugfs_regset32 *regset = entry->file.data;
struct drm_printer p = drm_seq_file_printer(m);
int idx;
@@ -50,31 +50,9 @@ static int vc4_debugfs_regset32(struct seq_file *m, void *unused)
return 0;
}
-int vc4_debugfs_add_file(struct drm_minor *minor,
- const char *name,
- int (*show)(struct seq_file*, void*),
- void *data)
+void vc4_debugfs_add_regset32(struct drm_device *drm,
+ const char *name,
+ struct debugfs_regset32 *regset)
{
- struct drm_device *dev = minor->dev;
- struct dentry *root = minor->debugfs_root;
- struct drm_info_list *file;
-
- file = drmm_kzalloc(dev, sizeof(*file), GFP_KERNEL);
- if (!file)
- return -ENOMEM;
-
- file->name = name;
- file->show = show;
- file->data = data;
-
- drm_debugfs_create_files(file, 1, root, minor);
-
- return 0;
-}
-
-int vc4_debugfs_add_regset32(struct drm_minor *minor,
- const char *name,
- struct debugfs_regset32 *regset)
-{
- return vc4_debugfs_add_file(minor, name, vc4_debugfs_regset32, regset);
+ drm_debugfs_add_file(drm, name, vc4_debugfs_regset32, regset);
}
diff --git a/drivers/gpu/drm/vc4/vc4_dpi.c b/drivers/gpu/drm/vc4/vc4_dpi.c
index 1f8f44b7b5a5..f518d6e59ed6 100644
--- a/drivers/gpu/drm/vc4/vc4_dpi.c
+++ b/drivers/gpu/drm/vc4/vc4_dpi.c
@@ -103,8 +103,17 @@ to_vc4_dpi(struct drm_encoder *encoder)
return container_of(encoder, struct vc4_dpi, encoder.base);
}
-#define DPI_READ(offset) readl(dpi->regs + (offset))
-#define DPI_WRITE(offset, val) writel(val, dpi->regs + (offset))
+#define DPI_READ(offset) \
+ ({ \
+ kunit_fail_current_test("Accessing a register in a unit test!\n"); \
+ readl(dpi->regs + (offset)); \
+ })
+
+#define DPI_WRITE(offset, val) \
+ do { \
+ kunit_fail_current_test("Accessing a register in a unit test!\n"); \
+ writel(val, dpi->regs + (offset)); \
+ } while (0)
static const struct debugfs_reg32 dpi_regs[] = {
VC4_REG32(DPI_C),
@@ -150,8 +159,8 @@ static void vc4_dpi_encoder_enable(struct drm_encoder *encoder)
}
drm_connector_list_iter_end(&conn_iter);
- /* Default to 24bit if no connector or format found. */
- dpi_c |= VC4_SET_FIELD(DPI_FORMAT_24BIT_888_RGB, DPI_FORMAT);
+ /* Default to 18bit if no connector or format found. */
+ dpi_c |= VC4_SET_FIELD(DPI_FORMAT_18BIT_666_RGB_1, DPI_FORMAT);
if (connector) {
if (connector->display_info.num_bus_formats) {
@@ -170,16 +179,26 @@ static void vc4_dpi_encoder_enable(struct drm_encoder *encoder)
dpi_c |= VC4_SET_FIELD(DPI_ORDER_BGR,
DPI_ORDER);
break;
+ case MEDIA_BUS_FMT_BGR666_1X24_CPADHI:
+ dpi_c |= VC4_SET_FIELD(DPI_ORDER_BGR, DPI_ORDER);
+ fallthrough;
case MEDIA_BUS_FMT_RGB666_1X24_CPADHI:
dpi_c |= VC4_SET_FIELD(DPI_FORMAT_18BIT_666_RGB_2,
DPI_FORMAT);
break;
+ case MEDIA_BUS_FMT_BGR666_1X18:
+ dpi_c |= VC4_SET_FIELD(DPI_ORDER_BGR, DPI_ORDER);
+ fallthrough;
case MEDIA_BUS_FMT_RGB666_1X18:
dpi_c |= VC4_SET_FIELD(DPI_FORMAT_18BIT_666_RGB_1,
DPI_FORMAT);
break;
case MEDIA_BUS_FMT_RGB565_1X16:
- dpi_c |= VC4_SET_FIELD(DPI_FORMAT_16BIT_565_RGB_3,
+ dpi_c |= VC4_SET_FIELD(DPI_FORMAT_16BIT_565_RGB_1,
+ DPI_FORMAT);
+ break;
+ case MEDIA_BUS_FMT_RGB565_1X24_CPADHI:
+ dpi_c |= VC4_SET_FIELD(DPI_FORMAT_16BIT_565_RGB_2,
DPI_FORMAT);
break;
default:
@@ -248,11 +267,8 @@ static int vc4_dpi_late_register(struct drm_encoder *encoder)
{
struct drm_device *drm = encoder->dev;
struct vc4_dpi *dpi = to_vc4_dpi(encoder);
- int ret;
- ret = vc4_debugfs_add_regset32(drm->primary, "dpi_regs", &dpi->regset);
- if (ret)
- return ret;
+ vc4_debugfs_add_regset32(drm, "dpi_regs", &dpi->regset);
return 0;
}
diff --git a/drivers/gpu/drm/vc4/vc4_drv.c b/drivers/gpu/drm/vc4/vc4_drv.c
index 5990d8f8c363..0ccaee57fe9a 100644
--- a/drivers/gpu/drm/vc4/vc4_drv.c
+++ b/drivers/gpu/drm/vc4/vc4_drv.c
@@ -196,7 +196,7 @@ static const struct drm_ioctl_desc vc4_drm_ioctls[] = {
DRM_IOCTL_DEF_DRV(VC4_PERFMON_GET_VALUES, vc4_perfmon_get_values_ioctl, DRM_RENDER_ALLOW),
};
-static const struct drm_driver vc4_drm_driver = {
+const struct drm_driver vc4_drm_driver = {
.driver_features = (DRIVER_MODESET |
DRIVER_ATOMIC |
DRIVER_GEM |
@@ -225,7 +225,7 @@ static const struct drm_driver vc4_drm_driver = {
.patchlevel = DRIVER_PATCHLEVEL,
};
-static const struct drm_driver vc5_drm_driver = {
+const struct drm_driver vc5_drm_driver = {
.driver_features = (DRIVER_MODESET |
DRIVER_ATOMIC |
DRIVER_GEM),
@@ -320,7 +320,6 @@ static int vc4_drm_bind(struct device *dev)
drm = &vc4->base;
platform_set_drvdata(pdev, drm);
- INIT_LIST_HEAD(&vc4->debugfs_list);
if (!is_vc5) {
ret = drmm_mutex_init(drm, &vc4->bin_bo_lock);
diff --git a/drivers/gpu/drm/vc4/vc4_drv.h b/drivers/gpu/drm/vc4/vc4_drv.h
index 515228682e8e..95069bb16821 100644
--- a/drivers/gpu/drm/vc4/vc4_drv.h
+++ b/drivers/gpu/drm/vc4/vc4_drv.h
@@ -19,11 +19,16 @@
#include <drm/drm_mm.h>
#include <drm/drm_modeset_lock.h>
+#include <kunit/test-bug.h>
+
#include "uapi/drm/vc4_drm.h"
struct drm_device;
struct drm_gem_object;
+extern const struct drm_driver vc4_drm_driver;
+extern const struct drm_driver vc5_drm_driver;
+
/* Don't forget to update vc4_bo.c: bo_type_names[] when adding to
* this.
*/
@@ -221,11 +226,6 @@ struct vc4_dev {
struct drm_private_obj hvs_channels;
struct drm_private_obj load_tracker;
- /* List of vc4_debugfs_info_entry for adding to debugfs once
- * the minor is available (after drm_dev_register()).
- */
- struct list_head debugfs_list;
-
/* Mutex for binner bo allocation. */
struct mutex bin_bo_lock;
/* Reference count for our binner bo. */
@@ -233,7 +233,7 @@ struct vc4_dev {
};
static inline struct vc4_dev *
-to_vc4_dev(struct drm_device *dev)
+to_vc4_dev(const struct drm_device *dev)
{
return container_of(dev, struct vc4_dev, base);
}
@@ -286,7 +286,7 @@ struct vc4_bo {
};
static inline struct vc4_bo *
-to_vc4_bo(struct drm_gem_object *bo)
+to_vc4_bo(const struct drm_gem_object *bo)
{
return container_of(to_drm_gem_dma_obj(bo), struct vc4_bo, base);
}
@@ -299,7 +299,7 @@ struct vc4_fence {
};
static inline struct vc4_fence *
-to_vc4_fence(struct dma_fence *fence)
+to_vc4_fence(const struct dma_fence *fence)
{
return container_of(fence, struct vc4_fence, base);
}
@@ -355,12 +355,35 @@ struct vc4_hvs {
bool vc5_hdmi_enable_4096by2160;
};
+#define HVS_NUM_CHANNELS 3
+
+struct vc4_hvs_state {
+ struct drm_private_state base;
+ unsigned long core_clock_rate;
+
+ struct {
+ unsigned in_use: 1;
+ unsigned long fifo_load;
+ struct drm_crtc_commit *pending_commit;
+ } fifo_state[HVS_NUM_CHANNELS];
+};
+
+static inline struct vc4_hvs_state *
+to_vc4_hvs_state(const struct drm_private_state *priv)
+{
+ return container_of(priv, struct vc4_hvs_state, base);
+}
+
+struct vc4_hvs_state *vc4_hvs_get_global_state(struct drm_atomic_state *state);
+struct vc4_hvs_state *vc4_hvs_get_old_global_state(const struct drm_atomic_state *state);
+struct vc4_hvs_state *vc4_hvs_get_new_global_state(const struct drm_atomic_state *state);
+
struct vc4_plane {
struct drm_plane base;
};
static inline struct vc4_plane *
-to_vc4_plane(struct drm_plane *plane)
+to_vc4_plane(const struct drm_plane *plane)
{
return container_of(plane, struct vc4_plane, base);
}
@@ -436,7 +459,7 @@ struct vc4_plane_state {
};
static inline struct vc4_plane_state *
-to_vc4_plane_state(struct drm_plane_state *state)
+to_vc4_plane_state(const struct drm_plane_state *state)
{
return container_of(state, struct vc4_plane_state, base);
}
@@ -450,6 +473,7 @@ enum vc4_encoder_type {
VC4_ENCODER_TYPE_DSI1,
VC4_ENCODER_TYPE_SMI,
VC4_ENCODER_TYPE_DPI,
+ VC4_ENCODER_TYPE_TXP,
};
struct vc4_encoder {
@@ -466,12 +490,30 @@ struct vc4_encoder {
};
static inline struct vc4_encoder *
-to_vc4_encoder(struct drm_encoder *encoder)
+to_vc4_encoder(const struct drm_encoder *encoder)
{
return container_of(encoder, struct vc4_encoder, base);
}
+static inline
+struct drm_encoder *vc4_find_encoder_by_type(struct drm_device *drm,
+ enum vc4_encoder_type type)
+{
+ struct drm_encoder *encoder;
+
+ drm_for_each_encoder(encoder, drm) {
+ struct vc4_encoder *vc4_encoder = to_vc4_encoder(encoder);
+
+ if (vc4_encoder->type == type)
+ return encoder;
+ }
+
+ return NULL;
+}
+
struct vc4_crtc_data {
+ const char *name;
+
const char *debugfs_name;
/* Bitmask of channels (FIFOs) of the HVS that the output can source from */
@@ -481,6 +523,8 @@ struct vc4_crtc_data {
int hvs_output;
};
+extern const struct vc4_crtc_data vc4_txp_crtc_data;
+
struct vc4_pv_data {
struct vc4_crtc_data base;
@@ -493,6 +537,15 @@ struct vc4_pv_data {
enum vc4_encoder_type encoder_types[4];
};
+extern const struct vc4_pv_data bcm2835_pv0_data;
+extern const struct vc4_pv_data bcm2835_pv1_data;
+extern const struct vc4_pv_data bcm2835_pv2_data;
+extern const struct vc4_pv_data bcm2711_pv0_data;
+extern const struct vc4_pv_data bcm2711_pv1_data;
+extern const struct vc4_pv_data bcm2711_pv2_data;
+extern const struct vc4_pv_data bcm2711_pv3_data;
+extern const struct vc4_pv_data bcm2711_pv4_data;
+
struct vc4_crtc {
struct drm_crtc base;
struct platform_device *pdev;
@@ -539,7 +592,7 @@ struct vc4_crtc {
};
static inline struct vc4_crtc *
-to_vc4_crtc(struct drm_crtc *crtc)
+to_vc4_crtc(const struct drm_crtc *crtc)
{
return container_of(crtc, struct vc4_crtc, base);
}
@@ -584,15 +637,34 @@ struct vc4_crtc_state {
#define VC4_HVS_CHANNEL_DISABLED ((unsigned int)-1)
static inline struct vc4_crtc_state *
-to_vc4_crtc_state(struct drm_crtc_state *crtc_state)
+to_vc4_crtc_state(const struct drm_crtc_state *crtc_state)
{
return container_of(crtc_state, struct vc4_crtc_state, base);
}
-#define V3D_READ(offset) readl(vc4->v3d->regs + offset)
-#define V3D_WRITE(offset, val) writel(val, vc4->v3d->regs + offset)
-#define HVS_READ(offset) readl(hvs->regs + offset)
-#define HVS_WRITE(offset, val) writel(val, hvs->regs + offset)
+#define V3D_READ(offset) \
+ ({ \
+ kunit_fail_current_test("Accessing a register in a unit test!\n"); \
+ readl(vc4->v3d->regs + (offset)); \
+ })
+
+#define V3D_WRITE(offset, val) \
+ do { \
+ kunit_fail_current_test("Accessing a register in a unit test!\n"); \
+ writel(val, vc4->v3d->regs + (offset)); \
+ } while (0)
+
+#define HVS_READ(offset) \
+ ({ \
+ kunit_fail_current_test("Accessing a register in a unit test!\n"); \
+ readl(hvs->regs + (offset)); \
+ })
+
+#define HVS_WRITE(offset, val) \
+ do { \
+ kunit_fail_current_test("Accessing a register in a unit test!\n"); \
+ writel(val, hvs->regs + (offset)); \
+ } while (0)
#define VC4_REG32(reg) { .name = #reg, .offset = reg }
@@ -862,14 +934,24 @@ int vc4_bo_debugfs_init(struct drm_minor *minor);
/* vc4_crtc.c */
extern struct platform_driver vc4_crtc_driver;
int vc4_crtc_disable_at_boot(struct drm_crtc *crtc);
-int vc4_crtc_init(struct drm_device *drm, struct vc4_crtc *vc4_crtc,
+int __vc4_crtc_init(struct drm_device *drm, struct platform_device *pdev,
+ struct vc4_crtc *vc4_crtc, const struct vc4_crtc_data *data,
+ struct drm_plane *primary_plane,
+ const struct drm_crtc_funcs *crtc_funcs,
+ const struct drm_crtc_helper_funcs *crtc_helper_funcs,
+ bool feeds_txp);
+int vc4_crtc_init(struct drm_device *drm, struct platform_device *pdev,
+ struct vc4_crtc *vc4_crtc, const struct vc4_crtc_data *data,
const struct drm_crtc_funcs *crtc_funcs,
- const struct drm_crtc_helper_funcs *crtc_helper_funcs);
+ const struct drm_crtc_helper_funcs *crtc_helper_funcs,
+ bool feeds_txp);
int vc4_page_flip(struct drm_crtc *crtc,
struct drm_framebuffer *fb,
struct drm_pending_vblank_event *event,
uint32_t flags,
struct drm_modeset_acquire_ctx *ctx);
+int vc4_crtc_atomic_check(struct drm_crtc *crtc,
+ struct drm_atomic_state *state);
struct drm_crtc_state *vc4_crtc_duplicate_state(struct drm_crtc *crtc);
void vc4_crtc_destroy_state(struct drm_crtc *crtc,
struct drm_crtc_state *state);
@@ -884,28 +966,15 @@ void vc4_crtc_get_margins(struct drm_crtc_state *state,
/* vc4_debugfs.c */
void vc4_debugfs_init(struct drm_minor *minor);
#ifdef CONFIG_DEBUG_FS
-int vc4_debugfs_add_file(struct drm_minor *minor,
- const char *filename,
- int (*show)(struct seq_file*, void*),
- void *data);
-int vc4_debugfs_add_regset32(struct drm_minor *minor,
- const char *filename,
- struct debugfs_regset32 *regset);
+void vc4_debugfs_add_regset32(struct drm_device *drm,
+ const char *filename,
+ struct debugfs_regset32 *regset);
#else
-static inline int vc4_debugfs_add_file(struct drm_minor *minor,
- const char *filename,
- int (*show)(struct seq_file*, void*),
- void *data)
-{
- return 0;
-}
-static inline int vc4_debugfs_add_regset32(struct drm_minor *minor,
- const char *filename,
- struct debugfs_regset32 *regset)
-{
- return 0;
-}
+static inline void vc4_debugfs_add_regset32(struct drm_device *drm,
+ const char *filename,
+ struct debugfs_regset32 *regset)
+{}
#endif
/* vc4_drv.c */
@@ -959,6 +1028,7 @@ void vc4_irq_reset(struct drm_device *dev);
/* vc4_hvs.c */
extern struct platform_driver vc4_hvs_driver;
+struct vc4_hvs *__vc4_hvs_alloc(struct vc4_dev *vc4, struct platform_device *pdev);
void vc4_hvs_stop_channel(struct vc4_hvs *hvs, unsigned int output);
int vc4_hvs_get_fifo_from_output(struct vc4_hvs *hvs, unsigned int output);
u8 vc4_hvs_get_fifo_frame_count(struct vc4_hvs *hvs, unsigned int fifo);
diff --git a/drivers/gpu/drm/vc4/vc4_dsi.c b/drivers/gpu/drm/vc4/vc4_dsi.c
index 878e05d79e81..a5c075f802e4 100644
--- a/drivers/gpu/drm/vc4/vc4_dsi.c
+++ b/drivers/gpu/drm/vc4/vc4_dsi.c
@@ -24,7 +24,6 @@
#include <linux/component.h>
#include <linux/dma-mapping.h>
#include <linux/dmaengine.h>
-#include <linux/i2c.h>
#include <linux/io.h>
#include <linux/of_address.h>
#include <linux/of_platform.h>
@@ -556,8 +555,8 @@ struct vc4_dsi {
struct platform_device *pdev;
- struct drm_bridge *bridge;
- struct list_head bridge_chain;
+ struct drm_bridge *out_bridge;
+ struct drm_bridge bridge;
void __iomem *regs;
@@ -609,6 +608,12 @@ to_vc4_dsi(struct drm_encoder *encoder)
return container_of(encoder, struct vc4_dsi, encoder.base);
}
+static inline struct vc4_dsi *
+bridge_to_vc4_dsi(struct drm_bridge *bridge)
+{
+ return container_of(bridge, struct vc4_dsi, bridge);
+}
+
static inline void
dsi_dma_workaround_write(struct vc4_dsi *dsi, u32 offset, u32 val)
{
@@ -617,6 +622,8 @@ dsi_dma_workaround_write(struct vc4_dsi *dsi, u32 offset, u32 val)
dma_cookie_t cookie;
int ret;
+ kunit_fail_current_test("Accessing a register in a unit test!\n");
+
/* DSI0 should be able to write normally. */
if (!chan) {
writel(val, dsi->regs + offset);
@@ -645,7 +652,12 @@ dsi_dma_workaround_write(struct vc4_dsi *dsi, u32 offset, u32 val)
DRM_ERROR("Failed to wait for DMA: %d\n", ret);
}
-#define DSI_READ(offset) readl(dsi->regs + (offset))
+#define DSI_READ(offset) \
+ ({ \
+ kunit_fail_current_test("Accessing a register in a unit test!\n"); \
+ readl(dsi->regs + (offset)); \
+ })
+
#define DSI_WRITE(offset, val) dsi_dma_workaround_write(dsi, offset, val)
#define DSI_PORT_READ(offset) \
DSI_READ(dsi->variant->port ? DSI1_##offset : DSI0_##offset)
@@ -790,26 +802,22 @@ dsi_esc_timing(u32 ns)
return DIV_ROUND_UP(ns, ESC_TIME_NS);
}
-static void vc4_dsi_encoder_disable(struct drm_encoder *encoder)
+static void vc4_dsi_bridge_disable(struct drm_bridge *bridge,
+ struct drm_bridge_state *state)
{
- struct vc4_dsi *dsi = to_vc4_dsi(encoder);
- struct device *dev = &dsi->pdev->dev;
- struct drm_bridge *iter;
-
- list_for_each_entry_reverse(iter, &dsi->bridge_chain, chain_node) {
- if (iter->funcs->disable)
- iter->funcs->disable(iter);
+ struct vc4_dsi *dsi = bridge_to_vc4_dsi(bridge);
+ u32 disp0_ctrl;
- if (iter == dsi->bridge)
- break;
- }
-
- vc4_dsi_ulps(dsi, true);
+ disp0_ctrl = DSI_PORT_READ(DISP0_CTRL);
+ disp0_ctrl &= ~DSI_DISP0_ENABLE;
+ DSI_PORT_WRITE(DISP0_CTRL, disp0_ctrl);
+}
- list_for_each_entry_from(iter, &dsi->bridge_chain, chain_node) {
- if (iter->funcs->post_disable)
- iter->funcs->post_disable(iter);
- }
+static void vc4_dsi_bridge_post_disable(struct drm_bridge *bridge,
+ struct drm_bridge_state *state)
+{
+ struct vc4_dsi *dsi = bridge_to_vc4_dsi(bridge);
+ struct device *dev = &dsi->pdev->dev;
clk_disable_unprepare(dsi->pll_phy_clock);
clk_disable_unprepare(dsi->escape_clock);
@@ -831,11 +839,11 @@ static void vc4_dsi_encoder_disable(struct drm_encoder *encoder)
* higher-than-expected clock rate to the panel, but that's what the
* firmware does too.
*/
-static bool vc4_dsi_encoder_mode_fixup(struct drm_encoder *encoder,
- const struct drm_display_mode *mode,
- struct drm_display_mode *adjusted_mode)
+static bool vc4_dsi_bridge_mode_fixup(struct drm_bridge *bridge,
+ const struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode)
{
- struct vc4_dsi *dsi = to_vc4_dsi(encoder);
+ struct vc4_dsi *dsi = bridge_to_vc4_dsi(bridge);
struct clk *phy_parent = clk_get_parent(dsi->pll_phy_clock);
unsigned long parent_rate = clk_get_rate(phy_parent);
unsigned long pixel_clock_hz = mode->clock * 1000;
@@ -867,18 +875,22 @@ static bool vc4_dsi_encoder_mode_fixup(struct drm_encoder *encoder,
return true;
}
-static void vc4_dsi_encoder_enable(struct drm_encoder *encoder)
+static void vc4_dsi_bridge_pre_enable(struct drm_bridge *bridge,
+ struct drm_bridge_state *old_state)
{
- struct drm_display_mode *mode = &encoder->crtc->state->adjusted_mode;
- struct vc4_dsi *dsi = to_vc4_dsi(encoder);
+ struct drm_atomic_state *state = old_state->base.state;
+ struct vc4_dsi *dsi = bridge_to_vc4_dsi(bridge);
+ const struct drm_crtc_state *crtc_state;
struct device *dev = &dsi->pdev->dev;
+ const struct drm_display_mode *mode;
+ struct drm_connector *connector;
bool debug_dump_regs = false;
- struct drm_bridge *iter;
unsigned long hs_clock;
+ struct drm_crtc *crtc;
u32 ui_ns;
/* Minimum LP state duration in escape clock cycles. */
u32 lpx = dsi_esc_timing(60);
- unsigned long pixel_clock_hz = mode->clock * 1000;
+ unsigned long pixel_clock_hz;
unsigned long dsip_clock;
unsigned long phy_clock;
int ret;
@@ -895,6 +907,18 @@ static void vc4_dsi_encoder_enable(struct drm_encoder *encoder)
drm_print_regset32(&p, &dsi->regset);
}
+ /*
+ * Retrieve the CRTC adjusted mode. This requires a little dance to go
+ * from the bridge to the encoder, to the connector and to the CRTC.
+ */
+ connector = drm_atomic_get_new_connector_for_encoder(state,
+ bridge->encoder);
+ crtc = drm_atomic_get_new_connector_state(state, connector)->crtc;
+ crtc_state = drm_atomic_get_new_crtc_state(state, crtc);
+ mode = &crtc_state->adjusted_mode;
+
+ pixel_clock_hz = mode->clock * 1000;
+
/* Round up the clk_set_rate() request slightly, since
* PLLD_DSI1 is an integer divider and its rate selection will
* never round up.
@@ -1106,11 +1130,6 @@ static void vc4_dsi_encoder_enable(struct drm_encoder *encoder)
vc4_dsi_ulps(dsi, false);
- list_for_each_entry_reverse(iter, &dsi->bridge_chain, chain_node) {
- if (iter->funcs->pre_enable)
- iter->funcs->pre_enable(iter);
- }
-
if (dsi->mode_flags & MIPI_DSI_MODE_VIDEO) {
DSI_PORT_WRITE(DISP0_CTRL,
VC4_SET_FIELD(dsi->divider,
@@ -1118,18 +1137,23 @@ static void vc4_dsi_encoder_enable(struct drm_encoder *encoder)
VC4_SET_FIELD(dsi->format, DSI_DISP0_PFORMAT) |
VC4_SET_FIELD(DSI_DISP0_LP_STOP_PERFRAME,
DSI_DISP0_LP_STOP_CTRL) |
- DSI_DISP0_ST_END |
- DSI_DISP0_ENABLE);
+ DSI_DISP0_ST_END);
} else {
DSI_PORT_WRITE(DISP0_CTRL,
- DSI_DISP0_COMMAND_MODE |
- DSI_DISP0_ENABLE);
+ DSI_DISP0_COMMAND_MODE);
}
+}
- list_for_each_entry(iter, &dsi->bridge_chain, chain_node) {
- if (iter->funcs->enable)
- iter->funcs->enable(iter);
- }
+static void vc4_dsi_bridge_enable(struct drm_bridge *bridge,
+ struct drm_bridge_state *old_state)
+{
+ struct vc4_dsi *dsi = bridge_to_vc4_dsi(bridge);
+ bool debug_dump_regs = false;
+ u32 disp0_ctrl;
+
+ disp0_ctrl = DSI_PORT_READ(DISP0_CTRL);
+ disp0_ctrl |= DSI_DISP0_ENABLE;
+ DSI_PORT_WRITE(DISP0_CTRL, disp0_ctrl);
if (debug_dump_regs) {
struct drm_printer p = drm_info_printer(&dsi->pdev->dev);
@@ -1138,6 +1162,16 @@ static void vc4_dsi_encoder_enable(struct drm_encoder *encoder)
}
}
+static int vc4_dsi_bridge_attach(struct drm_bridge *bridge,
+ enum drm_bridge_attach_flags flags)
+{
+ struct vc4_dsi *dsi = bridge_to_vc4_dsi(bridge);
+
+ /* Attach the panel or bridge to the dsi bridge */
+ return drm_bridge_attach(bridge->encoder, dsi->out_bridge,
+ &dsi->bridge, flags);
+}
+
static ssize_t vc4_dsi_host_transfer(struct mipi_dsi_host *host,
const struct mipi_dsi_msg *msg)
{
@@ -1314,6 +1348,7 @@ static int vc4_dsi_host_attach(struct mipi_dsi_host *host,
struct mipi_dsi_device *device)
{
struct vc4_dsi *dsi = host_to_dsi(host);
+ int ret;
dsi->lanes = device->lanes;
dsi->channel = device->channel;
@@ -1348,7 +1383,15 @@ static int vc4_dsi_host_attach(struct mipi_dsi_host *host,
return 0;
}
- return component_add(&dsi->pdev->dev, &vc4_dsi_ops);
+ drm_bridge_add(&dsi->bridge);
+
+ ret = component_add(&dsi->pdev->dev, &vc4_dsi_ops);
+ if (ret) {
+ drm_bridge_remove(&dsi->bridge);
+ return ret;
+ }
+
+ return 0;
}
static int vc4_dsi_host_detach(struct mipi_dsi_host *host,
@@ -1357,6 +1400,7 @@ static int vc4_dsi_host_detach(struct mipi_dsi_host *host,
struct vc4_dsi *dsi = host_to_dsi(host);
component_del(&dsi->pdev->dev, &vc4_dsi_ops);
+ drm_bridge_remove(&dsi->bridge);
return 0;
}
@@ -1366,22 +1410,24 @@ static const struct mipi_dsi_host_ops vc4_dsi_host_ops = {
.transfer = vc4_dsi_host_transfer,
};
-static const struct drm_encoder_helper_funcs vc4_dsi_encoder_helper_funcs = {
- .disable = vc4_dsi_encoder_disable,
- .enable = vc4_dsi_encoder_enable,
- .mode_fixup = vc4_dsi_encoder_mode_fixup,
+static const struct drm_bridge_funcs vc4_dsi_bridge_funcs = {
+ .atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state,
+ .atomic_destroy_state = drm_atomic_helper_bridge_destroy_state,
+ .atomic_reset = drm_atomic_helper_bridge_reset,
+ .atomic_pre_enable = vc4_dsi_bridge_pre_enable,
+ .atomic_enable = vc4_dsi_bridge_enable,
+ .atomic_disable = vc4_dsi_bridge_disable,
+ .atomic_post_disable = vc4_dsi_bridge_post_disable,
+ .attach = vc4_dsi_bridge_attach,
+ .mode_fixup = vc4_dsi_bridge_mode_fixup,
};
static int vc4_dsi_late_register(struct drm_encoder *encoder)
{
struct drm_device *drm = encoder->dev;
struct vc4_dsi *dsi = to_vc4_dsi(encoder);
- int ret;
- ret = vc4_debugfs_add_regset32(drm->primary, dsi->variant->debugfs_name,
- &dsi->regset);
- if (ret)
- return ret;
+ vc4_debugfs_add_regset32(drm, dsi->variant->debugfs_name, &dsi->regset);
return 0;
}
@@ -1617,7 +1663,6 @@ static int vc4_dsi_bind(struct device *dev, struct device *master, void *data)
dsi->variant = of_device_get_match_data(dev);
- INIT_LIST_HEAD(&dsi->bridge_chain);
dsi->encoder.type = dsi->variant->port ?
VC4_ENCODER_TYPE_DSI1 : VC4_ENCODER_TYPE_DSI0;
@@ -1723,9 +1768,9 @@ static int vc4_dsi_bind(struct device *dev, struct device *master, void *data)
return ret;
}
- dsi->bridge = drmm_of_get_bridge(drm, dev->of_node, 0, 0);
- if (IS_ERR(dsi->bridge))
- return PTR_ERR(dsi->bridge);
+ dsi->out_bridge = drmm_of_get_bridge(drm, dev->of_node, 0, 0);
+ if (IS_ERR(dsi->out_bridge))
+ return PTR_ERR(dsi->out_bridge);
/* The esc clock rate is supposed to always be 100Mhz. */
ret = clk_set_rate(dsi->escape_clock, 100 * 1000000);
@@ -1745,41 +1790,19 @@ static int vc4_dsi_bind(struct device *dev, struct device *master, void *data)
if (ret)
return ret;
- drm_encoder_helper_add(encoder, &vc4_dsi_encoder_helper_funcs);
-
ret = devm_pm_runtime_enable(dev);
if (ret)
return ret;
- ret = drm_bridge_attach(encoder, dsi->bridge, NULL, 0);
+ ret = drm_bridge_attach(encoder, &dsi->bridge, NULL, 0);
if (ret)
return ret;
- /* Disable the atomic helper calls into the bridge. We
- * manually call the bridge pre_enable / enable / etc. calls
- * from our driver, since we need to sequence them within the
- * encoder's enable/disable paths.
- */
- list_splice_init(&encoder->bridge_chain, &dsi->bridge_chain);
return 0;
}
-static void vc4_dsi_unbind(struct device *dev, struct device *master,
- void *data)
-{
- struct vc4_dsi *dsi = dev_get_drvdata(dev);
- struct drm_encoder *encoder = &dsi->encoder.base;
-
- /*
- * Restore the bridge_chain so the bridge detach procedure can happen
- * normally.
- */
- list_splice_init(&dsi->bridge_chain, &encoder->bridge_chain);
-}
-
static const struct component_ops vc4_dsi_ops = {
.bind = vc4_dsi_bind,
- .unbind = vc4_dsi_unbind,
};
static int vc4_dsi_dev_probe(struct platform_device *pdev)
@@ -1793,7 +1816,13 @@ static int vc4_dsi_dev_probe(struct platform_device *pdev)
dev_set_drvdata(dev, dsi);
kref_init(&dsi->kref);
+
dsi->pdev = pdev;
+ dsi->bridge.funcs = &vc4_dsi_bridge_funcs;
+#ifdef CONFIG_OF
+ dsi->bridge.of_node = dev->of_node;
+#endif
+ dsi->bridge.type = DRM_MODE_CONNECTOR_DSI;
dsi->dsi_host.ops = &vc4_dsi_host_ops;
dsi->dsi_host.dev = dev;
mipi_dsi_host_register(&dsi->dsi_host);
diff --git a/drivers/gpu/drm/vc4/vc4_hdmi.c b/drivers/gpu/drm/vc4/vc4_hdmi.c
index 7546103f1499..ea22c9bf223a 100644
--- a/drivers/gpu/drm/vc4/vc4_hdmi.c
+++ b/drivers/gpu/drm/vc4/vc4_hdmi.c
@@ -164,8 +164,8 @@ static bool vc4_hdmi_is_full_range_rgb(struct vc4_hdmi *vc4_hdmi,
static int vc4_hdmi_debugfs_regs(struct seq_file *m, void *unused)
{
- struct drm_info_node *node = (struct drm_info_node *)m->private;
- struct vc4_hdmi *vc4_hdmi = node->info_ent->data;
+ struct drm_debugfs_entry *entry = m->private;
+ struct vc4_hdmi *vc4_hdmi = entry->file.data;
struct drm_device *drm = vc4_hdmi->connector.dev;
struct drm_printer p = drm_seq_file_printer(m);
int idx;
@@ -406,6 +406,7 @@ static void vc4_hdmi_handle_hotplug(struct vc4_hdmi *vc4_hdmi,
{
struct drm_connector *connector = &vc4_hdmi->connector;
struct edid *edid;
+ int ret;
/*
* NOTE: This function should really be called with
@@ -434,7 +435,15 @@ static void vc4_hdmi_handle_hotplug(struct vc4_hdmi *vc4_hdmi,
cec_s_phys_addr_from_edid(vc4_hdmi->cec_adap, edid);
kfree(edid);
- vc4_hdmi_reset_link(connector, ctx);
+ for (;;) {
+ ret = vc4_hdmi_reset_link(connector, ctx);
+ if (ret == -EDEADLK) {
+ drm_modeset_backoff(ctx);
+ continue;
+ }
+
+ break;
+ }
}
static int vc4_hdmi_connector_detect_ctx(struct drm_connector *connector,
@@ -1302,11 +1311,12 @@ static void vc5_hdmi_set_timings(struct vc4_hdmi *vc4_hdmi,
VC4_SET_FIELD(mode->crtc_vdisplay, VC5_HDMI_VERTA_VAL));
u32 vertb = (VC4_SET_FIELD(mode->htotal >> (2 - pixel_rep),
VC5_HDMI_VERTB_VSPO) |
- VC4_SET_FIELD(mode->crtc_vtotal - mode->crtc_vsync_end,
+ VC4_SET_FIELD(mode->crtc_vtotal - mode->crtc_vsync_end +
+ interlaced,
VC4_HDMI_VERTB_VBP));
u32 vertb_even = (VC4_SET_FIELD(0, VC5_HDMI_VERTB_VSPO) |
VC4_SET_FIELD(mode->crtc_vtotal -
- mode->crtc_vsync_end - interlaced,
+ mode->crtc_vsync_end,
VC4_HDMI_VERTB_VBP));
unsigned long flags;
unsigned char gcp;
@@ -1995,13 +2005,9 @@ static int vc4_hdmi_late_register(struct drm_encoder *encoder)
struct drm_device *drm = encoder->dev;
struct vc4_hdmi *vc4_hdmi = encoder_to_vc4_hdmi(encoder);
const struct vc4_hdmi_variant *variant = vc4_hdmi->variant;
- int ret;
- ret = vc4_debugfs_add_file(drm->primary, variant->debugfs_name,
- vc4_hdmi_debugfs_regs,
- vc4_hdmi);
- if (ret)
- return ret;
+ drm_debugfs_add_file(drm, variant->debugfs_name,
+ vc4_hdmi_debugfs_regs, vc4_hdmi);
return 0;
}
diff --git a/drivers/gpu/drm/vc4/vc4_hdmi_regs.h b/drivers/gpu/drm/vc4/vc4_hdmi_regs.h
index 48db438550b1..b04b2fc8d831 100644
--- a/drivers/gpu/drm/vc4/vc4_hdmi_regs.h
+++ b/drivers/gpu/drm/vc4/vc4_hdmi_regs.h
@@ -456,6 +456,8 @@ static inline u32 vc4_hdmi_read(struct vc4_hdmi *hdmi,
WARN_ON(pm_runtime_status_suspended(&hdmi->pdev->dev));
+ kunit_fail_current_test("Accessing an HDMI register in a unit test!\n");
+
if (reg >= variant->num_registers) {
dev_warn(&hdmi->pdev->dev,
"Invalid register ID %u\n", reg);
@@ -486,6 +488,8 @@ static inline void vc4_hdmi_write(struct vc4_hdmi *hdmi,
WARN_ON(pm_runtime_status_suspended(&hdmi->pdev->dev));
+ kunit_fail_current_test("Accessing an HDMI register in a unit test!\n");
+
if (reg >= variant->num_registers) {
dev_warn(&hdmi->pdev->dev,
"Invalid register ID %u\n", reg);
diff --git a/drivers/gpu/drm/vc4/vc4_hvs.c b/drivers/gpu/drm/vc4/vc4_hvs.c
index c4453a5ae163..4da66ef96783 100644
--- a/drivers/gpu/drm/vc4/vc4_hvs.c
+++ b/drivers/gpu/drm/vc4/vc4_hvs.c
@@ -93,8 +93,8 @@ void vc4_hvs_dump_state(struct vc4_hvs *hvs)
static int vc4_hvs_debugfs_underrun(struct seq_file *m, void *data)
{
- struct drm_info_node *node = m->private;
- struct drm_device *dev = node->minor->dev;
+ struct drm_debugfs_entry *entry = m->private;
+ struct drm_device *dev = entry->dev;
struct vc4_dev *vc4 = to_vc4_dev(dev);
struct drm_printer p = drm_seq_file_printer(m);
@@ -105,8 +105,8 @@ static int vc4_hvs_debugfs_underrun(struct seq_file *m, void *data)
static int vc4_hvs_debugfs_dlist(struct seq_file *m, void *data)
{
- struct drm_info_node *node = m->private;
- struct drm_device *dev = node->minor->dev;
+ struct drm_debugfs_entry *entry = m->private;
+ struct drm_device *dev = entry->dev;
struct vc4_dev *vc4 = to_vc4_dev(dev);
struct vc4_hvs *hvs = vc4->hvs;
struct drm_printer p = drm_seq_file_printer(m);
@@ -370,28 +370,30 @@ static int vc4_hvs_init_channel(struct vc4_hvs *hvs, struct drm_crtc *crtc,
* mode.
*/
dispctrl = SCALER_DISPCTRLX_ENABLE;
+ dispbkgndx = HVS_READ(SCALER_DISPBKGNDX(chan));
- if (!vc4->is_vc5)
+ if (!vc4->is_vc5) {
dispctrl |= VC4_SET_FIELD(mode->hdisplay,
SCALER_DISPCTRLX_WIDTH) |
VC4_SET_FIELD(mode->vdisplay,
SCALER_DISPCTRLX_HEIGHT) |
(oneshot ? SCALER_DISPCTRLX_ONESHOT : 0);
- else
+ dispbkgndx |= SCALER_DISPBKGND_AUTOHS;
+ } else {
dispctrl |= VC4_SET_FIELD(mode->hdisplay,
SCALER5_DISPCTRLX_WIDTH) |
VC4_SET_FIELD(mode->vdisplay,
SCALER5_DISPCTRLX_HEIGHT) |
(oneshot ? SCALER5_DISPCTRLX_ONESHOT : 0);
+ dispbkgndx &= ~SCALER5_DISPBKGND_BCK2BCK;
+ }
HVS_WRITE(SCALER_DISPCTRLX(chan), dispctrl);
- dispbkgndx = HVS_READ(SCALER_DISPBKGNDX(chan));
dispbkgndx &= ~SCALER_DISPBKGND_GAMMA;
dispbkgndx &= ~SCALER_DISPBKGND_INTERLACE;
HVS_WRITE(SCALER_DISPBKGNDX(chan), dispbkgndx |
- SCALER_DISPBKGND_AUTOHS |
((!vc4->is_vc5) ? SCALER_DISPBKGND_GAMMA : 0) |
(interlace ? SCALER_DISPBKGND_INTERLACE : 0));
@@ -568,6 +570,8 @@ void vc4_hvs_atomic_flush(struct drm_crtc *crtc,
bool enable_bg_fill = false;
u32 __iomem *dlist_start = vc4->hvs->dlist + vc4_state->mm.start;
u32 __iomem *dlist_next = dlist_start;
+ unsigned int zpos = 0;
+ bool found = false;
int idx;
if (!drm_dev_enter(dev, &idx)) {
@@ -575,29 +579,43 @@ void vc4_hvs_atomic_flush(struct drm_crtc *crtc,
return;
}
+ if (vc4_state->assigned_channel == VC4_HVS_CHANNEL_DISABLED)
+ return;
+
if (debug_dump_regs) {
DRM_INFO("CRTC %d HVS before:\n", drm_crtc_index(crtc));
vc4_hvs_dump_state(hvs);
}
/* Copy all the active planes' dlist contents to the hardware dlist. */
- drm_atomic_crtc_for_each_plane(plane, crtc) {
- /* Is this the first active plane? */
- if (dlist_next == dlist_start) {
- /* We need to enable background fill when a plane
- * could be alpha blending from the background, i.e.
- * where no other plane is underneath. It suffices to
- * consider the first active plane here since we set
- * needs_bg_fill such that either the first plane
- * already needs it or all planes on top blend from
- * the first or a lower plane.
- */
- vc4_plane_state = to_vc4_plane_state(plane->state);
- enable_bg_fill = vc4_plane_state->needs_bg_fill;
+ do {
+ found = false;
+
+ drm_atomic_crtc_for_each_plane(plane, crtc) {
+ if (plane->state->normalized_zpos != zpos)
+ continue;
+
+ /* Is this the first active plane? */
+ if (dlist_next == dlist_start) {
+ /* We need to enable background fill when a plane
+ * could be alpha blending from the background, i.e.
+ * where no other plane is underneath. It suffices to
+ * consider the first active plane here since we set
+ * needs_bg_fill such that either the first plane
+ * already needs it or all planes on top blend from
+ * the first or a lower plane.
+ */
+ vc4_plane_state = to_vc4_plane_state(plane->state);
+ enable_bg_fill = vc4_plane_state->needs_bg_fill;
+ }
+
+ dlist_next += vc4_plane_write_dlist(plane, dlist_next);
+
+ found = true;
}
- dlist_next += vc4_plane_write_dlist(plane, dlist_next);
- }
+ zpos++;
+ } while (found);
writel(SCALER_CTL0_END, dlist_next);
dlist_next++;
@@ -658,7 +676,8 @@ void vc4_hvs_mask_underrun(struct vc4_hvs *hvs, int channel)
return;
dispctrl = HVS_READ(SCALER_DISPCTRL);
- dispctrl &= ~SCALER_DISPCTRL_DSPEISLUR(channel);
+ dispctrl &= ~(hvs->vc4->is_vc5 ? SCALER5_DISPCTRL_DSPEISLUR(channel) :
+ SCALER_DISPCTRL_DSPEISLUR(channel));
HVS_WRITE(SCALER_DISPCTRL, dispctrl);
@@ -675,7 +694,8 @@ void vc4_hvs_unmask_underrun(struct vc4_hvs *hvs, int channel)
return;
dispctrl = HVS_READ(SCALER_DISPCTRL);
- dispctrl |= SCALER_DISPCTRL_DSPEISLUR(channel);
+ dispctrl |= (hvs->vc4->is_vc5 ? SCALER5_DISPCTRL_DSPEISLUR(channel) :
+ SCALER_DISPCTRL_DSPEISLUR(channel));
HVS_WRITE(SCALER_DISPSTAT,
SCALER_DISPSTAT_EUFLOW(channel));
@@ -701,6 +721,7 @@ static irqreturn_t vc4_hvs_irq_handler(int irq, void *data)
int channel;
u32 control;
u32 status;
+ u32 dspeislur;
/*
* NOTE: We don't need to protect the register access using
@@ -717,9 +738,11 @@ static irqreturn_t vc4_hvs_irq_handler(int irq, void *data)
control = HVS_READ(SCALER_DISPCTRL);
for (channel = 0; channel < SCALER_CHANNELS_COUNT; channel++) {
+ dspeislur = vc4->is_vc5 ? SCALER5_DISPCTRL_DSPEISLUR(channel) :
+ SCALER_DISPCTRL_DSPEISLUR(channel);
/* Interrupt masking is not always honored, so check it here. */
if (status & SCALER_DISPSTAT_EUFLOW(channel) &&
- control & SCALER_DISPCTRL_DSPEISLUR(channel)) {
+ control & dspeislur) {
vc4_hvs_mask_underrun(hvs, channel);
vc4_hvs_report_underrun(dev);
@@ -740,7 +763,6 @@ int vc4_hvs_debugfs_init(struct drm_minor *minor)
struct drm_device *drm = minor->dev;
struct vc4_dev *vc4 = to_vc4_dev(drm);
struct vc4_hvs *hvs = vc4->hvs;
- int ret;
if (!vc4->hvs)
return -ENODEV;
@@ -750,24 +772,55 @@ int vc4_hvs_debugfs_init(struct drm_minor *minor)
minor->debugfs_root,
&vc4->load_tracker_enabled);
- ret = vc4_debugfs_add_file(minor, "hvs_dlists",
- vc4_hvs_debugfs_dlist, NULL);
- if (ret)
- return ret;
+ drm_debugfs_add_file(drm, "hvs_dlists", vc4_hvs_debugfs_dlist, NULL);
- ret = vc4_debugfs_add_file(minor, "hvs_underrun",
- vc4_hvs_debugfs_underrun, NULL);
- if (ret)
- return ret;
+ drm_debugfs_add_file(drm, "hvs_underrun", vc4_hvs_debugfs_underrun, NULL);
- ret = vc4_debugfs_add_regset32(minor, "hvs_regs",
- &hvs->regset);
- if (ret)
- return ret;
+ vc4_debugfs_add_regset32(drm, "hvs_regs", &hvs->regset);
return 0;
}
+struct vc4_hvs *__vc4_hvs_alloc(struct vc4_dev *vc4, struct platform_device *pdev)
+{
+ struct drm_device *drm = &vc4->base;
+ struct vc4_hvs *hvs;
+
+ hvs = drmm_kzalloc(drm, sizeof(*hvs), GFP_KERNEL);
+ if (!hvs)
+ return ERR_PTR(-ENOMEM);
+
+ hvs->vc4 = vc4;
+ hvs->pdev = pdev;
+
+ spin_lock_init(&hvs->mm_lock);
+
+ /* Set up the HVS display list memory manager. We never
+ * overwrite the setup from the bootloader (just 128b out of
+ * our 16K), since we don't want to scramble the screen when
+ * transitioning from the firmware's boot setup to runtime.
+ */
+ drm_mm_init(&hvs->dlist_mm,
+ HVS_BOOTLOADER_DLIST_END,
+ (SCALER_DLIST_SIZE >> 2) - HVS_BOOTLOADER_DLIST_END);
+
+ /* Set up the HVS LBM memory manager. We could have some more
+ * complicated data structure that allowed reuse of LBM areas
+ * between planes when they don't overlap on the screen, but
+ * for now we just allocate globally.
+ */
+ if (!vc4->is_vc5)
+ /* 48k words of 2x12-bit pixels */
+ drm_mm_init(&hvs->lbm_mm, 0, 48 * 1024);
+ else
+ /* 60k words of 4x12-bit pixels */
+ drm_mm_init(&hvs->lbm_mm, 0, 60 * 1024);
+
+ vc4->hvs = hvs;
+
+ return hvs;
+}
+
static int vc4_hvs_bind(struct device *dev, struct device *master, void *data)
{
struct platform_device *pdev = to_platform_device(dev);
@@ -776,13 +829,11 @@ static int vc4_hvs_bind(struct device *dev, struct device *master, void *data)
struct vc4_hvs *hvs = NULL;
int ret;
u32 dispctrl;
- u32 reg;
+ u32 reg, top;
- hvs = drmm_kzalloc(drm, sizeof(*hvs), GFP_KERNEL);
- if (!hvs)
- return -ENOMEM;
- hvs->vc4 = vc4;
- hvs->pdev = pdev;
+ hvs = __vc4_hvs_alloc(vc4, NULL);
+ if (IS_ERR(hvs))
+ return PTR_ERR(hvs);
hvs->regs = vc4_ioremap_regs(pdev, 0);
if (IS_ERR(hvs->regs))
@@ -835,29 +886,6 @@ static int vc4_hvs_bind(struct device *dev, struct device *master, void *data)
else
hvs->dlist = hvs->regs + SCALER5_DLIST_START;
- spin_lock_init(&hvs->mm_lock);
-
- /* Set up the HVS display list memory manager. We never
- * overwrite the setup from the bootloader (just 128b out of
- * our 16K), since we don't want to scramble the screen when
- * transitioning from the firmware's boot setup to runtime.
- */
- drm_mm_init(&hvs->dlist_mm,
- HVS_BOOTLOADER_DLIST_END,
- (SCALER_DLIST_SIZE >> 2) - HVS_BOOTLOADER_DLIST_END);
-
- /* Set up the HVS LBM memory manager. We could have some more
- * complicated data structure that allowed reuse of LBM areas
- * between planes when they don't overlap on the screen, but
- * for now we just allocate globally.
- */
- if (!vc4->is_vc5)
- /* 48k words of 2x12-bit pixels */
- drm_mm_init(&hvs->lbm_mm, 0, 48 * 1024);
- else
- /* 60k words of 4x12-bit pixels */
- drm_mm_init(&hvs->lbm_mm, 0, 60 * 1024);
-
/* Upload filter kernels. We only have the one for now, so we
* keep it around for the lifetime of the driver.
*/
@@ -867,8 +895,6 @@ static int vc4_hvs_bind(struct device *dev, struct device *master, void *data)
if (ret)
return ret;
- vc4->hvs = hvs;
-
reg = HVS_READ(SCALER_DISPECTRL);
reg &= ~SCALER_DISPECTRL_DSP2_MUX_MASK;
HVS_WRITE(SCALER_DISPECTRL,
@@ -896,22 +922,102 @@ static int vc4_hvs_bind(struct device *dev, struct device *master, void *data)
SCALER_DISPCTRL_DISPEIRQ(1) |
SCALER_DISPCTRL_DISPEIRQ(2);
- dispctrl &= ~(SCALER_DISPCTRL_DMAEIRQ |
- SCALER_DISPCTRL_SLVWREIRQ |
- SCALER_DISPCTRL_SLVRDEIRQ |
- SCALER_DISPCTRL_DSPEIEOF(0) |
- SCALER_DISPCTRL_DSPEIEOF(1) |
- SCALER_DISPCTRL_DSPEIEOF(2) |
- SCALER_DISPCTRL_DSPEIEOLN(0) |
- SCALER_DISPCTRL_DSPEIEOLN(1) |
- SCALER_DISPCTRL_DSPEIEOLN(2) |
- SCALER_DISPCTRL_DSPEISLUR(0) |
- SCALER_DISPCTRL_DSPEISLUR(1) |
- SCALER_DISPCTRL_DSPEISLUR(2) |
- SCALER_DISPCTRL_SCLEIRQ);
+ if (!vc4->is_vc5)
+ dispctrl &= ~(SCALER_DISPCTRL_DMAEIRQ |
+ SCALER_DISPCTRL_SLVWREIRQ |
+ SCALER_DISPCTRL_SLVRDEIRQ |
+ SCALER_DISPCTRL_DSPEIEOF(0) |
+ SCALER_DISPCTRL_DSPEIEOF(1) |
+ SCALER_DISPCTRL_DSPEIEOF(2) |
+ SCALER_DISPCTRL_DSPEIEOLN(0) |
+ SCALER_DISPCTRL_DSPEIEOLN(1) |
+ SCALER_DISPCTRL_DSPEIEOLN(2) |
+ SCALER_DISPCTRL_DSPEISLUR(0) |
+ SCALER_DISPCTRL_DSPEISLUR(1) |
+ SCALER_DISPCTRL_DSPEISLUR(2) |
+ SCALER_DISPCTRL_SCLEIRQ);
+ else
+ dispctrl &= ~(SCALER_DISPCTRL_DMAEIRQ |
+ SCALER5_DISPCTRL_SLVEIRQ |
+ SCALER5_DISPCTRL_DSPEIEOF(0) |
+ SCALER5_DISPCTRL_DSPEIEOF(1) |
+ SCALER5_DISPCTRL_DSPEIEOF(2) |
+ SCALER5_DISPCTRL_DSPEIEOLN(0) |
+ SCALER5_DISPCTRL_DSPEIEOLN(1) |
+ SCALER5_DISPCTRL_DSPEIEOLN(2) |
+ SCALER5_DISPCTRL_DSPEISLUR(0) |
+ SCALER5_DISPCTRL_DSPEISLUR(1) |
+ SCALER5_DISPCTRL_DSPEISLUR(2) |
+ SCALER_DISPCTRL_SCLEIRQ);
+
+
+ /* Set AXI panic mode.
+ * VC4 panics when < 2 lines in FIFO.
+ * VC5 panics when less than 1 line in the FIFO.
+ */
+ dispctrl &= ~(SCALER_DISPCTRL_PANIC0_MASK |
+ SCALER_DISPCTRL_PANIC1_MASK |
+ SCALER_DISPCTRL_PANIC2_MASK);
+ dispctrl |= VC4_SET_FIELD(2, SCALER_DISPCTRL_PANIC0);
+ dispctrl |= VC4_SET_FIELD(2, SCALER_DISPCTRL_PANIC1);
+ dispctrl |= VC4_SET_FIELD(2, SCALER_DISPCTRL_PANIC2);
HVS_WRITE(SCALER_DISPCTRL, dispctrl);
+ /* Recompute Composite Output Buffer (COB) allocations for the displays
+ */
+ if (!vc4->is_vc5) {
+ /* The COB is 20736 pixels, or just over 10 lines at 2048 wide.
+ * The bottom 2048 pixels are full 32bpp RGBA (intended for the
+ * TXP composing RGBA to memory), whilst the remainder are only
+ * 24bpp RGB.
+ *
+ * Assign 3 lines to channels 1 & 2, and just over 4 lines to
+ * channel 0.
+ */
+ #define VC4_COB_SIZE 20736
+ #define VC4_COB_LINE_WIDTH 2048
+ #define VC4_COB_NUM_LINES 3
+ reg = 0;
+ top = VC4_COB_LINE_WIDTH * VC4_COB_NUM_LINES;
+ reg |= (top - 1) << 16;
+ HVS_WRITE(SCALER_DISPBASE2, reg);
+ reg = top;
+ top += VC4_COB_LINE_WIDTH * VC4_COB_NUM_LINES;
+ reg |= (top - 1) << 16;
+ HVS_WRITE(SCALER_DISPBASE1, reg);
+ reg = top;
+ top = VC4_COB_SIZE;
+ reg |= (top - 1) << 16;
+ HVS_WRITE(SCALER_DISPBASE0, reg);
+ } else {
+ /* The COB is 44416 pixels, or 10.8 lines at 4096 wide.
+ * The bottom 4096 pixels are full RGBA (intended for the TXP
+ * composing RGBA to memory), whilst the remainder are only
+ * RGB. Addressing is always pixel wide.
+ *
+ * Assign 3 lines of 4096 to channels 1 & 2, and just over 4
+ * lines. to channel 0.
+ */
+ #define VC5_COB_SIZE 44416
+ #define VC5_COB_LINE_WIDTH 4096
+ #define VC5_COB_NUM_LINES 3
+ reg = 0;
+ top = VC5_COB_LINE_WIDTH * VC5_COB_NUM_LINES;
+ reg |= top << 16;
+ HVS_WRITE(SCALER_DISPBASE2, reg);
+ top += 16;
+ reg = top;
+ top += VC5_COB_LINE_WIDTH * VC5_COB_NUM_LINES;
+ reg |= top << 16;
+ HVS_WRITE(SCALER_DISPBASE1, reg);
+ top += 16;
+ reg = top;
+ top = VC5_COB_SIZE;
+ reg |= top << 16;
+ HVS_WRITE(SCALER_DISPBASE0, reg);
+ }
+
ret = devm_request_irq(dev, platform_get_irq(pdev, 0),
vc4_hvs_irq_handler, 0, "vc4 hvs", drm);
if (ret)
diff --git a/drivers/gpu/drm/vc4/vc4_kms.c b/drivers/gpu/drm/vc4/vc4_kms.c
index 8fbeecdf2ec4..a7e3d47c50f4 100644
--- a/drivers/gpu/drm/vc4/vc4_kms.c
+++ b/drivers/gpu/drm/vc4/vc4_kms.c
@@ -12,6 +12,7 @@
*/
#include <linux/clk.h>
+#include <linux/sort.h>
#include <drm/drm_atomic.h>
#include <drm/drm_atomic_helper.h>
@@ -24,8 +25,6 @@
#include "vc4_drv.h"
#include "vc4_regs.h"
-#define HVS_NUM_CHANNELS 3
-
struct vc4_ctm_state {
struct drm_private_state base;
struct drm_color_ctm *ctm;
@@ -38,23 +37,6 @@ to_vc4_ctm_state(const struct drm_private_state *priv)
return container_of(priv, struct vc4_ctm_state, base);
}
-struct vc4_hvs_state {
- struct drm_private_state base;
- unsigned long core_clock_rate;
-
- struct {
- unsigned in_use: 1;
- unsigned long fifo_load;
- struct drm_crtc_commit *pending_commit;
- } fifo_state[HVS_NUM_CHANNELS];
-};
-
-static struct vc4_hvs_state *
-to_vc4_hvs_state(const struct drm_private_state *priv)
-{
- return container_of(priv, struct vc4_hvs_state, base);
-}
-
struct vc4_load_tracker_state {
struct drm_private_state base;
u64 hvs_load;
@@ -190,8 +172,8 @@ vc4_ctm_commit(struct vc4_dev *vc4, struct drm_atomic_state *state)
VC4_SET_FIELD(ctm_state->fifo, SCALER_OLEDOFFS_DISPFIFO));
}
-static struct vc4_hvs_state *
-vc4_hvs_get_new_global_state(struct drm_atomic_state *state)
+struct vc4_hvs_state *
+vc4_hvs_get_new_global_state(const struct drm_atomic_state *state)
{
struct vc4_dev *vc4 = to_vc4_dev(state->dev);
struct drm_private_state *priv_state;
@@ -203,8 +185,8 @@ vc4_hvs_get_new_global_state(struct drm_atomic_state *state)
return to_vc4_hvs_state(priv_state);
}
-static struct vc4_hvs_state *
-vc4_hvs_get_old_global_state(struct drm_atomic_state *state)
+struct vc4_hvs_state *
+vc4_hvs_get_old_global_state(const struct drm_atomic_state *state)
{
struct vc4_dev *vc4 = to_vc4_dev(state->dev);
struct drm_private_state *priv_state;
@@ -216,7 +198,7 @@ vc4_hvs_get_old_global_state(struct drm_atomic_state *state)
return to_vc4_hvs_state(priv_state);
}
-static struct vc4_hvs_state *
+struct vc4_hvs_state *
vc4_hvs_get_global_state(struct drm_atomic_state *state)
{
struct vc4_dev *vc4 = to_vc4_dev(state->dev);
@@ -776,6 +758,20 @@ static int vc4_hvs_channels_obj_init(struct vc4_dev *vc4)
return drmm_add_action_or_reset(&vc4->base, vc4_hvs_channels_obj_fini, NULL);
}
+static int cmp_vc4_crtc_hvs_output(const void *a, const void *b)
+{
+ const struct vc4_crtc *crtc_a =
+ to_vc4_crtc(*(const struct drm_crtc **)a);
+ const struct vc4_crtc_data *data_a =
+ vc4_crtc_to_vc4_crtc_data(crtc_a);
+ const struct vc4_crtc *crtc_b =
+ to_vc4_crtc(*(const struct drm_crtc **)b);
+ const struct vc4_crtc_data *data_b =
+ vc4_crtc_to_vc4_crtc_data(crtc_b);
+
+ return data_a->hvs_output - data_b->hvs_output;
+}
+
/*
* The BCM2711 HVS has up to 7 outputs connected to the pixelvalves and
* the TXP (and therefore all the CRTCs found on that platform).
@@ -810,10 +806,11 @@ static int vc4_pv_muxing_atomic_check(struct drm_device *dev,
struct drm_atomic_state *state)
{
struct vc4_hvs_state *hvs_new_state;
- struct drm_crtc_state *old_crtc_state, *new_crtc_state;
+ struct drm_crtc **sorted_crtcs;
struct drm_crtc *crtc;
unsigned int unassigned_channels = 0;
unsigned int i;
+ int ret;
hvs_new_state = vc4_hvs_get_global_state(state);
if (IS_ERR(hvs_new_state))
@@ -823,15 +820,59 @@ static int vc4_pv_muxing_atomic_check(struct drm_device *dev,
if (!hvs_new_state->fifo_state[i].in_use)
unassigned_channels |= BIT(i);
- for_each_oldnew_crtc_in_state(state, crtc, old_crtc_state, new_crtc_state, i) {
- struct vc4_crtc_state *old_vc4_crtc_state =
- to_vc4_crtc_state(old_crtc_state);
- struct vc4_crtc_state *new_vc4_crtc_state =
- to_vc4_crtc_state(new_crtc_state);
- struct vc4_crtc *vc4_crtc = to_vc4_crtc(crtc);
+ /*
+ * The problem we have to solve here is that we have up to 7
+ * encoders, connected to up to 6 CRTCs.
+ *
+ * Those CRTCs, depending on the instance, can be routed to 1, 2
+ * or 3 HVS FIFOs, and we need to set the muxing between FIFOs and
+ * outputs in the HVS accordingly.
+ *
+ * It would be pretty hard to come up with an algorithm that
+ * would generically solve this. However, the current routing
+ * trees we support allow us to simplify a bit the problem.
+ *
+ * Indeed, with the current supported layouts, if we try to
+ * assign in the ascending crtc index order the FIFOs, we can't
+ * fall into the situation where an earlier CRTC that had
+ * multiple routes is assigned one that was the only option for
+ * a later CRTC.
+ *
+ * If the layout changes and doesn't give us that in the future,
+ * we will need to have something smarter, but it works so far.
+ */
+ sorted_crtcs = kmalloc_array(dev->num_crtcs, sizeof(*sorted_crtcs), GFP_KERNEL);
+ if (!sorted_crtcs)
+ return -ENOMEM;
+
+ i = 0;
+ drm_for_each_crtc(crtc, dev)
+ sorted_crtcs[i++] = crtc;
+
+ sort(sorted_crtcs, i, sizeof(*sorted_crtcs), cmp_vc4_crtc_hvs_output, NULL);
+
+ for (i = 0; i < dev->num_crtcs; i++) {
+ struct vc4_crtc_state *old_vc4_crtc_state, *new_vc4_crtc_state;
+ struct drm_crtc_state *old_crtc_state, *new_crtc_state;
+ struct vc4_crtc *vc4_crtc;
unsigned int matching_channels;
unsigned int channel;
+ crtc = sorted_crtcs[i];
+ if (!crtc)
+ continue;
+ vc4_crtc = to_vc4_crtc(crtc);
+
+ old_crtc_state = drm_atomic_get_old_crtc_state(state, crtc);
+ if (!old_crtc_state)
+ continue;
+ old_vc4_crtc_state = to_vc4_crtc_state(old_crtc_state);
+
+ new_crtc_state = drm_atomic_get_new_crtc_state(state, crtc);
+ if (!new_crtc_state)
+ continue;
+ new_vc4_crtc_state = to_vc4_crtc_state(new_crtc_state);
+
drm_dbg(dev, "%s: Trying to find a channel.\n", crtc->name);
/* Nothing to do here, let's skip it */
@@ -860,33 +901,11 @@ static int vc4_pv_muxing_atomic_check(struct drm_device *dev,
continue;
}
- /*
- * The problem we have to solve here is that we have
- * up to 7 encoders, connected to up to 6 CRTCs.
- *
- * Those CRTCs, depending on the instance, can be
- * routed to 1, 2 or 3 HVS FIFOs, and we need to set
- * the change the muxing between FIFOs and outputs in
- * the HVS accordingly.
- *
- * It would be pretty hard to come up with an
- * algorithm that would generically solve
- * this. However, the current routing trees we support
- * allow us to simplify a bit the problem.
- *
- * Indeed, with the current supported layouts, if we
- * try to assign in the ascending crtc index order the
- * FIFOs, we can't fall into the situation where an
- * earlier CRTC that had multiple routes is assigned
- * one that was the only option for a later CRTC.
- *
- * If the layout changes and doesn't give us that in
- * the future, we will need to have something smarter,
- * but it works so far.
- */
matching_channels = unassigned_channels & vc4_crtc->data->hvs_available_channels;
- if (!matching_channels)
- return -EINVAL;
+ if (!matching_channels) {
+ ret = -EINVAL;
+ goto err_free_crtc_array;
+ }
channel = ffs(matching_channels) - 1;
@@ -896,7 +915,12 @@ static int vc4_pv_muxing_atomic_check(struct drm_device *dev,
hvs_new_state->fifo_state[channel].in_use = true;
}
+ kfree(sorted_crtcs);
return 0;
+
+err_free_crtc_array:
+ kfree(sorted_crtcs);
+ return ret;
}
static int
@@ -1050,6 +1074,7 @@ int vc4_kms_load(struct drm_device *dev)
dev->mode_config.helper_private = &vc4_mode_config_helpers;
dev->mode_config.preferred_depth = 24;
dev->mode_config.async_page_flip = true;
+ dev->mode_config.normalize_zpos = true;
ret = vc4_ctm_obj_init(vc4);
if (ret)
diff --git a/drivers/gpu/drm/vc4/vc4_plane.c b/drivers/gpu/drm/vc4/vc4_plane.c
index bd5acc4a8687..97c84a3f5a46 100644
--- a/drivers/gpu/drm/vc4/vc4_plane.c
+++ b/drivers/gpu/drm/vc4/vc4_plane.c
@@ -65,78 +65,176 @@ static const struct hvs_format {
.drm = DRM_FORMAT_RGB565,
.hvs = HVS_PIXEL_FORMAT_RGB565,
.pixel_order = HVS_PIXEL_ORDER_XRGB,
+ .pixel_order_hvs5 = HVS_PIXEL_ORDER_XRGB,
},
{
.drm = DRM_FORMAT_BGR565,
.hvs = HVS_PIXEL_FORMAT_RGB565,
.pixel_order = HVS_PIXEL_ORDER_XBGR,
+ .pixel_order_hvs5 = HVS_PIXEL_ORDER_XBGR,
},
{
.drm = DRM_FORMAT_ARGB1555,
.hvs = HVS_PIXEL_FORMAT_RGBA5551,
.pixel_order = HVS_PIXEL_ORDER_ABGR,
+ .pixel_order_hvs5 = HVS_PIXEL_ORDER_ARGB,
},
{
.drm = DRM_FORMAT_XRGB1555,
.hvs = HVS_PIXEL_FORMAT_RGBA5551,
.pixel_order = HVS_PIXEL_ORDER_ABGR,
+ .pixel_order_hvs5 = HVS_PIXEL_ORDER_ARGB,
},
{
.drm = DRM_FORMAT_RGB888,
.hvs = HVS_PIXEL_FORMAT_RGB888,
.pixel_order = HVS_PIXEL_ORDER_XRGB,
+ .pixel_order_hvs5 = HVS_PIXEL_ORDER_XRGB,
},
{
.drm = DRM_FORMAT_BGR888,
.hvs = HVS_PIXEL_FORMAT_RGB888,
.pixel_order = HVS_PIXEL_ORDER_XBGR,
+ .pixel_order_hvs5 = HVS_PIXEL_ORDER_XBGR,
},
{
.drm = DRM_FORMAT_YUV422,
.hvs = HVS_PIXEL_FORMAT_YCBCR_YUV422_3PLANE,
.pixel_order = HVS_PIXEL_ORDER_XYCBCR,
+ .pixel_order_hvs5 = HVS_PIXEL_ORDER_XYCBCR,
},
{
.drm = DRM_FORMAT_YVU422,
.hvs = HVS_PIXEL_FORMAT_YCBCR_YUV422_3PLANE,
.pixel_order = HVS_PIXEL_ORDER_XYCRCB,
+ .pixel_order_hvs5 = HVS_PIXEL_ORDER_XYCRCB,
},
{
.drm = DRM_FORMAT_YUV420,
.hvs = HVS_PIXEL_FORMAT_YCBCR_YUV420_3PLANE,
.pixel_order = HVS_PIXEL_ORDER_XYCBCR,
+ .pixel_order_hvs5 = HVS_PIXEL_ORDER_XYCBCR,
},
{
.drm = DRM_FORMAT_YVU420,
.hvs = HVS_PIXEL_FORMAT_YCBCR_YUV420_3PLANE,
.pixel_order = HVS_PIXEL_ORDER_XYCRCB,
+ .pixel_order_hvs5 = HVS_PIXEL_ORDER_XYCRCB,
},
{
.drm = DRM_FORMAT_NV12,
.hvs = HVS_PIXEL_FORMAT_YCBCR_YUV420_2PLANE,
.pixel_order = HVS_PIXEL_ORDER_XYCBCR,
+ .pixel_order_hvs5 = HVS_PIXEL_ORDER_XYCBCR,
},
{
.drm = DRM_FORMAT_NV21,
.hvs = HVS_PIXEL_FORMAT_YCBCR_YUV420_2PLANE,
.pixel_order = HVS_PIXEL_ORDER_XYCRCB,
+ .pixel_order_hvs5 = HVS_PIXEL_ORDER_XYCRCB,
},
{
.drm = DRM_FORMAT_NV16,
.hvs = HVS_PIXEL_FORMAT_YCBCR_YUV422_2PLANE,
.pixel_order = HVS_PIXEL_ORDER_XYCBCR,
+ .pixel_order_hvs5 = HVS_PIXEL_ORDER_XYCBCR,
},
{
.drm = DRM_FORMAT_NV61,
.hvs = HVS_PIXEL_FORMAT_YCBCR_YUV422_2PLANE,
.pixel_order = HVS_PIXEL_ORDER_XYCRCB,
+ .pixel_order_hvs5 = HVS_PIXEL_ORDER_XYCRCB,
},
{
.drm = DRM_FORMAT_P030,
.hvs = HVS_PIXEL_FORMAT_YCBCR_10BIT,
- .pixel_order = HVS_PIXEL_ORDER_XYCBCR,
+ .pixel_order_hvs5 = HVS_PIXEL_ORDER_XYCBCR,
+ .hvs5_only = true,
+ },
+ {
+ .drm = DRM_FORMAT_XRGB2101010,
+ .hvs = HVS_PIXEL_FORMAT_RGBA1010102,
+ .pixel_order_hvs5 = HVS_PIXEL_ORDER_ARGB,
+ .hvs5_only = true,
+ },
+ {
+ .drm = DRM_FORMAT_ARGB2101010,
+ .hvs = HVS_PIXEL_FORMAT_RGBA1010102,
+ .pixel_order_hvs5 = HVS_PIXEL_ORDER_ARGB,
+ .hvs5_only = true,
+ },
+ {
+ .drm = DRM_FORMAT_ABGR2101010,
+ .hvs = HVS_PIXEL_FORMAT_RGBA1010102,
+ .pixel_order_hvs5 = HVS_PIXEL_ORDER_ABGR,
.hvs5_only = true,
},
+ {
+ .drm = DRM_FORMAT_XBGR2101010,
+ .hvs = HVS_PIXEL_FORMAT_RGBA1010102,
+ .pixel_order_hvs5 = HVS_PIXEL_ORDER_ABGR,
+ .hvs5_only = true,
+ },
+ {
+ .drm = DRM_FORMAT_RGB332,
+ .hvs = HVS_PIXEL_FORMAT_RGB332,
+ .pixel_order = HVS_PIXEL_ORDER_ARGB,
+ .pixel_order_hvs5 = HVS_PIXEL_ORDER_ARGB,
+ },
+ {
+ .drm = DRM_FORMAT_BGR233,
+ .hvs = HVS_PIXEL_FORMAT_RGB332,
+ .pixel_order = HVS_PIXEL_ORDER_ABGR,
+ .pixel_order_hvs5 = HVS_PIXEL_ORDER_ABGR,
+ },
+ {
+ .drm = DRM_FORMAT_XRGB4444,
+ .hvs = HVS_PIXEL_FORMAT_RGBA4444,
+ .pixel_order = HVS_PIXEL_ORDER_ABGR,
+ .pixel_order_hvs5 = HVS_PIXEL_ORDER_ARGB,
+ },
+ {
+ .drm = DRM_FORMAT_ARGB4444,
+ .hvs = HVS_PIXEL_FORMAT_RGBA4444,
+ .pixel_order = HVS_PIXEL_ORDER_ABGR,
+ .pixel_order_hvs5 = HVS_PIXEL_ORDER_ARGB,
+ },
+ {
+ .drm = DRM_FORMAT_XBGR4444,
+ .hvs = HVS_PIXEL_FORMAT_RGBA4444,
+ .pixel_order = HVS_PIXEL_ORDER_ARGB,
+ .pixel_order_hvs5 = HVS_PIXEL_ORDER_ABGR,
+ },
+ {
+ .drm = DRM_FORMAT_ABGR4444,
+ .hvs = HVS_PIXEL_FORMAT_RGBA4444,
+ .pixel_order = HVS_PIXEL_ORDER_ARGB,
+ .pixel_order_hvs5 = HVS_PIXEL_ORDER_ABGR,
+ },
+ {
+ .drm = DRM_FORMAT_BGRX4444,
+ .hvs = HVS_PIXEL_FORMAT_RGBA4444,
+ .pixel_order = HVS_PIXEL_ORDER_RGBA,
+ .pixel_order_hvs5 = HVS_PIXEL_ORDER_BGRA,
+ },
+ {
+ .drm = DRM_FORMAT_BGRA4444,
+ .hvs = HVS_PIXEL_FORMAT_RGBA4444,
+ .pixel_order = HVS_PIXEL_ORDER_RGBA,
+ .pixel_order_hvs5 = HVS_PIXEL_ORDER_BGRA,
+ },
+ {
+ .drm = DRM_FORMAT_RGBX4444,
+ .hvs = HVS_PIXEL_FORMAT_RGBA4444,
+ .pixel_order = HVS_PIXEL_ORDER_BGRA,
+ .pixel_order_hvs5 = HVS_PIXEL_ORDER_RGBA,
+ },
+ {
+ .drm = DRM_FORMAT_RGBA4444,
+ .hvs = HVS_PIXEL_FORMAT_RGBA4444,
+ .pixel_order = HVS_PIXEL_ORDER_BGRA,
+ .pixel_order_hvs5 = HVS_PIXEL_ORDER_RGBA,
+ },
};
static const struct hvs_format *vc4_get_hvs_format(u32 drm_format)
@@ -1003,15 +1101,10 @@ static int vc4_plane_mode_set(struct drm_plane *plane,
vc4_dlist_write(vc4_state, 0xc0c0c0c0);
} else {
- u32 hvs_pixel_order = format->pixel_order;
-
- if (format->pixel_order_hvs5)
- hvs_pixel_order = format->pixel_order_hvs5;
-
/* Control word */
vc4_dlist_write(vc4_state,
SCALER_CTL0_VALID |
- (hvs_pixel_order << SCALER_CTL0_ORDER_SHIFT) |
+ (format->pixel_order_hvs5 << SCALER_CTL0_ORDER_SHIFT) |
(hvs_format << SCALER_CTL0_PIXEL_FORMAT_SHIFT) |
VC4_SET_FIELD(tiling, SCALER_CTL0_TILING) |
(vc4_state->is_unity ?
@@ -1490,6 +1583,16 @@ static bool vc4_format_mod_supported(struct drm_plane *plane,
case DRM_FORMAT_BGRX1010102:
case DRM_FORMAT_RGBA1010102:
case DRM_FORMAT_BGRA1010102:
+ case DRM_FORMAT_XRGB4444:
+ case DRM_FORMAT_ARGB4444:
+ case DRM_FORMAT_XBGR4444:
+ case DRM_FORMAT_ABGR4444:
+ case DRM_FORMAT_RGBX4444:
+ case DRM_FORMAT_RGBA4444:
+ case DRM_FORMAT_BGRX4444:
+ case DRM_FORMAT_BGRA4444:
+ case DRM_FORMAT_RGB332:
+ case DRM_FORMAT_BGR233:
case DRM_FORMAT_YUV422:
case DRM_FORMAT_YVU422:
case DRM_FORMAT_YUV420:
@@ -1570,9 +1673,14 @@ struct drm_plane *vc4_plane_init(struct drm_device *dev,
DRM_COLOR_YCBCR_BT709,
DRM_COLOR_YCBCR_LIMITED_RANGE);
+ if (type == DRM_PLANE_TYPE_PRIMARY)
+ drm_plane_create_zpos_immutable_property(plane, 0);
+
return plane;
}
+#define VC4_NUM_OVERLAY_PLANES 16
+
int vc4_plane_create_additional_planes(struct drm_device *drm)
{
struct drm_plane *cursor_plane;
@@ -1588,24 +1696,35 @@ int vc4_plane_create_additional_planes(struct drm_device *drm)
* modest number of planes to expose, that should hopefully
* still cover any sane usecase.
*/
- for (i = 0; i < 16; i++) {
+ for (i = 0; i < VC4_NUM_OVERLAY_PLANES; i++) {
struct drm_plane *plane =
vc4_plane_init(drm, DRM_PLANE_TYPE_OVERLAY,
GENMASK(drm->mode_config.num_crtc - 1, 0));
if (IS_ERR(plane))
continue;
+
+ /* Create zpos property. Max of all the overlays + 1 primary +
+ * 1 cursor plane on a crtc.
+ */
+ drm_plane_create_zpos_property(plane, i + 1, 1,
+ VC4_NUM_OVERLAY_PLANES + 1);
}
drm_for_each_crtc(crtc, drm) {
/* Set up the legacy cursor after overlay initialization,
- * since we overlay planes on the CRTC in the order they were
- * initialized.
+ * since the zpos fallback is that planes are rendered by plane
+ * ID order, and that then puts the cursor on top.
*/
cursor_plane = vc4_plane_init(drm, DRM_PLANE_TYPE_CURSOR,
drm_crtc_mask(crtc));
if (!IS_ERR(cursor_plane)) {
crtc->cursor = cursor_plane;
+
+ drm_plane_create_zpos_property(cursor_plane,
+ VC4_NUM_OVERLAY_PLANES + 1,
+ 1,
+ VC4_NUM_OVERLAY_PLANES + 1);
}
}
diff --git a/drivers/gpu/drm/vc4/vc4_regs.h b/drivers/gpu/drm/vc4/vc4_regs.h
index f0290fad991d..f3763bd600f6 100644
--- a/drivers/gpu/drm/vc4/vc4_regs.h
+++ b/drivers/gpu/drm/vc4/vc4_regs.h
@@ -220,6 +220,12 @@
#define SCALER_DISPCTRL 0x00000000
/* Global register for clock gating the HVS */
# define SCALER_DISPCTRL_ENABLE BIT(31)
+# define SCALER_DISPCTRL_PANIC0_MASK VC4_MASK(25, 24)
+# define SCALER_DISPCTRL_PANIC0_SHIFT 24
+# define SCALER_DISPCTRL_PANIC1_MASK VC4_MASK(27, 26)
+# define SCALER_DISPCTRL_PANIC1_SHIFT 26
+# define SCALER_DISPCTRL_PANIC2_MASK VC4_MASK(29, 28)
+# define SCALER_DISPCTRL_PANIC2_SHIFT 28
# define SCALER_DISPCTRL_DSP3_MUX_MASK VC4_MASK(19, 18)
# define SCALER_DISPCTRL_DSP3_MUX_SHIFT 18
@@ -228,15 +234,21 @@
* always enabled.
*/
# define SCALER_DISPCTRL_DSPEISLUR(x) BIT(13 + (x))
+# define SCALER5_DISPCTRL_DSPEISLUR(x) BIT(9 + ((x) * 4))
/* Enables Display 0 end-of-line-N contribution to
* SCALER_DISPSTAT_IRQDISP0
*/
# define SCALER_DISPCTRL_DSPEIEOLN(x) BIT(8 + ((x) * 2))
+# define SCALER5_DISPCTRL_DSPEIEOLN(x) BIT(8 + ((x) * 4))
/* Enables Display 0 EOF contribution to SCALER_DISPSTAT_IRQDISP0 */
# define SCALER_DISPCTRL_DSPEIEOF(x) BIT(7 + ((x) * 2))
+# define SCALER5_DISPCTRL_DSPEIEOF(x) BIT(7 + ((x) * 4))
-# define SCALER_DISPCTRL_SLVRDEIRQ BIT(6)
-# define SCALER_DISPCTRL_SLVWREIRQ BIT(5)
+# define SCALER5_DISPCTRL_DSPEIVST(x) BIT(6 + ((x) * 4))
+
+# define SCALER_DISPCTRL_SLVRDEIRQ BIT(6) /* HVS4 only */
+# define SCALER_DISPCTRL_SLVWREIRQ BIT(5) /* HVS4 only */
+# define SCALER5_DISPCTRL_SLVEIRQ BIT(5)
# define SCALER_DISPCTRL_DMAEIRQ BIT(4)
/* Enables interrupt generation on the enabled EOF/EOLN/EISLUR
* bits and short frames..
@@ -360,6 +372,7 @@
#define SCALER_DISPBKGND0 0x00000044
# define SCALER_DISPBKGND_AUTOHS BIT(31)
+# define SCALER5_DISPBKGND_BCK2BCK BIT(31)
# define SCALER_DISPBKGND_INTERLACE BIT(30)
# define SCALER_DISPBKGND_GAMMA BIT(29)
# define SCALER_DISPBKGND_TESTMODE_MASK VC4_MASK(28, 25)
@@ -835,16 +848,19 @@ enum hvs_pixel_format {
/* Note: the LSB is the rightmost character shown. Only valid for
* HVS_PIXEL_FORMAT_RGB8888, not RGB888.
*/
+/* For modes 332, 4444, 555, 5551, 6666, 8888, 10:10:10:2 */
#define HVS_PIXEL_ORDER_RGBA 0
#define HVS_PIXEL_ORDER_BGRA 1
#define HVS_PIXEL_ORDER_ARGB 2
#define HVS_PIXEL_ORDER_ABGR 3
+/* For modes 666 and 888 (4 & 5) */
#define HVS_PIXEL_ORDER_XBRG 0
#define HVS_PIXEL_ORDER_XRBG 1
#define HVS_PIXEL_ORDER_XRGB 2
#define HVS_PIXEL_ORDER_XBGR 3
+/* For YCbCr modes (8-12, and 17) */
#define HVS_PIXEL_ORDER_XYCBCR 0
#define HVS_PIXEL_ORDER_XYCRCB 1
#define HVS_PIXEL_ORDER_YXCBCR 2
diff --git a/drivers/gpu/drm/vc4/vc4_txp.c b/drivers/gpu/drm/vc4/vc4_txp.c
index bd181b5a7b52..ef5cab2a3aa9 100644
--- a/drivers/gpu/drm/vc4/vc4_txp.c
+++ b/drivers/gpu/drm/vc4/vc4_txp.c
@@ -145,14 +145,24 @@
/* Number of lines received and committed to memory. */
#define TXP_PROGRESS 0x10
-#define TXP_READ(offset) readl(txp->regs + (offset))
-#define TXP_WRITE(offset, val) writel(val, txp->regs + (offset))
+#define TXP_READ(offset) \
+ ({ \
+ kunit_fail_current_test("Accessing a register in a unit test!\n"); \
+ readl(txp->regs + (offset)); \
+ })
+
+#define TXP_WRITE(offset, val) \
+ do { \
+ kunit_fail_current_test("Accessing a register in a unit test!\n"); \
+ writel(val, txp->regs + (offset)); \
+ } while (0)
struct vc4_txp {
struct vc4_crtc base;
struct platform_device *pdev;
+ struct vc4_encoder encoder;
struct drm_writeback_connector connector;
void __iomem *regs;
@@ -160,7 +170,7 @@ struct vc4_txp {
static inline struct vc4_txp *encoder_to_vc4_txp(struct drm_encoder *encoder)
{
- return container_of(encoder, struct vc4_txp, connector.encoder);
+ return container_of(encoder, struct vc4_txp, encoder.base);
}
static inline struct vc4_txp *connector_to_vc4_txp(struct drm_connector *conn)
@@ -478,7 +488,8 @@ static irqreturn_t vc4_txp_interrupt(int irq, void *data)
return IRQ_HANDLED;
}
-static const struct vc4_crtc_data vc4_txp_crtc_data = {
+const struct vc4_crtc_data vc4_txp_crtc_data = {
+ .name = "txp",
.debugfs_name = "txp_regs",
.hvs_available_channels = BIT(2),
.hvs_output = 2,
@@ -488,10 +499,10 @@ static int vc4_txp_bind(struct device *dev, struct device *master, void *data)
{
struct platform_device *pdev = to_platform_device(dev);
struct drm_device *drm = dev_get_drvdata(master);
+ struct vc4_encoder *vc4_encoder;
+ struct drm_encoder *encoder;
struct vc4_crtc *vc4_crtc;
struct vc4_txp *txp;
- struct drm_crtc *crtc;
- struct drm_encoder *encoder;
int ret, irq;
irq = platform_get_irq(pdev, 0);
@@ -501,39 +512,42 @@ static int vc4_txp_bind(struct device *dev, struct device *master, void *data)
txp = drmm_kzalloc(drm, sizeof(*txp), GFP_KERNEL);
if (!txp)
return -ENOMEM;
- vc4_crtc = &txp->base;
- crtc = &vc4_crtc->base;
-
- vc4_crtc->pdev = pdev;
- vc4_crtc->data = &vc4_txp_crtc_data;
- vc4_crtc->feeds_txp = true;
txp->pdev = pdev;
-
txp->regs = vc4_ioremap_regs(pdev, 0);
if (IS_ERR(txp->regs))
return PTR_ERR(txp->regs);
+
+ vc4_crtc = &txp->base;
vc4_crtc->regset.base = txp->regs;
vc4_crtc->regset.regs = txp_regs;
vc4_crtc->regset.nregs = ARRAY_SIZE(txp_regs);
- drm_connector_helper_add(&txp->connector.base,
- &vc4_txp_connector_helper_funcs);
- ret = drm_writeback_connector_init(drm, &txp->connector,
- &vc4_txp_connector_funcs,
- &vc4_txp_encoder_helper_funcs,
- drm_fmts, ARRAY_SIZE(drm_fmts),
- 0);
+ ret = vc4_crtc_init(drm, pdev, vc4_crtc, &vc4_txp_crtc_data,
+ &vc4_txp_crtc_funcs, &vc4_txp_crtc_helper_funcs, true);
if (ret)
return ret;
- ret = vc4_crtc_init(drm, vc4_crtc,
- &vc4_txp_crtc_funcs, &vc4_txp_crtc_helper_funcs);
+ vc4_encoder = &txp->encoder;
+ txp->encoder.type = VC4_ENCODER_TYPE_TXP;
+
+ encoder = &vc4_encoder->base;
+ encoder->possible_crtcs = drm_crtc_mask(&vc4_crtc->base);
+
+ drm_encoder_helper_add(encoder, &vc4_txp_encoder_helper_funcs);
+
+ ret = drmm_encoder_init(drm, encoder, NULL, DRM_MODE_ENCODER_VIRTUAL, NULL);
if (ret)
return ret;
- encoder = &txp->connector.encoder;
- encoder->possible_crtcs = drm_crtc_mask(crtc);
+ drm_connector_helper_add(&txp->connector.base,
+ &vc4_txp_connector_helper_funcs);
+ ret = drm_writeback_connector_init_with_encoder(drm, &txp->connector,
+ encoder,
+ &vc4_txp_connector_funcs,
+ drm_fmts, ARRAY_SIZE(drm_fmts));
+ if (ret)
+ return ret;
ret = devm_request_irq(dev, irq, vc4_txp_interrupt, 0,
dev_name(dev), txp);
diff --git a/drivers/gpu/drm/vc4/vc4_v3d.c b/drivers/gpu/drm/vc4/vc4_v3d.c
index 56abb0d6bc39..29a664c8bf44 100644
--- a/drivers/gpu/drm/vc4/vc4_v3d.c
+++ b/drivers/gpu/drm/vc4/vc4_v3d.c
@@ -96,8 +96,8 @@ static const struct debugfs_reg32 v3d_regs[] = {
static int vc4_v3d_debugfs_ident(struct seq_file *m, void *unused)
{
- struct drm_info_node *node = (struct drm_info_node *)m->private;
- struct drm_device *dev = node->minor->dev;
+ struct drm_debugfs_entry *entry = m->private;
+ struct drm_device *dev = entry->dev;
struct vc4_dev *vc4 = to_vc4_dev(dev);
int ret = vc4_v3d_pm_get(vc4);
@@ -404,19 +404,13 @@ int vc4_v3d_debugfs_init(struct drm_minor *minor)
struct drm_device *drm = minor->dev;
struct vc4_dev *vc4 = to_vc4_dev(drm);
struct vc4_v3d *v3d = vc4->v3d;
- int ret;
if (!vc4->v3d)
return -ENODEV;
- ret = vc4_debugfs_add_file(minor, "v3d_ident",
- vc4_v3d_debugfs_ident, NULL);
- if (ret)
- return ret;
+ drm_debugfs_add_file(drm, "v3d_ident", vc4_v3d_debugfs_ident, NULL);
- ret = vc4_debugfs_add_regset32(minor, "v3d_regs", &v3d->regset);
- if (ret)
- return ret;
+ vc4_debugfs_add_regset32(drm, "v3d_regs", &v3d->regset);
return 0;
}
diff --git a/drivers/gpu/drm/vc4/vc4_vec.c b/drivers/gpu/drm/vc4/vc4_vec.c
index 92c07e31d632..a3782d05cd66 100644
--- a/drivers/gpu/drm/vc4/vc4_vec.c
+++ b/drivers/gpu/drm/vc4/vc4_vec.c
@@ -46,6 +46,7 @@
#define VEC_CONFIG0_YDEL(x) ((x) << 26)
#define VEC_CONFIG0_CDEL_MASK GENMASK(25, 24)
#define VEC_CONFIG0_CDEL(x) ((x) << 24)
+#define VEC_CONFIG0_SECAM_STD BIT(21)
#define VEC_CONFIG0_PBPR_FIL BIT(18)
#define VEC_CONFIG0_CHROMA_GAIN_MASK GENMASK(17, 16)
#define VEC_CONFIG0_CHROMA_GAIN_UNITY (0 << 16)
@@ -76,6 +77,27 @@
#define VEC_SOFT_RESET 0x10c
#define VEC_CLMP0_START 0x144
#define VEC_CLMP0_END 0x148
+
+/*
+ * These set the color subcarrier frequency
+ * if VEC_CONFIG1_CUSTOM_FREQ is enabled.
+ *
+ * VEC_FREQ1_0 contains the most significant 16-bit half-word,
+ * VEC_FREQ3_2 contains the least significant 16-bit half-word.
+ * 0x80000000 seems to be equivalent to the pixel clock
+ * (which itself is the VEC clock divided by 8).
+ *
+ * Reference values (with the default pixel clock of 13.5 MHz):
+ *
+ * NTSC (3579545.[45] Hz) - 0x21F07C1F
+ * PAL (4433618.75 Hz) - 0x2A098ACB
+ * PAL-M (3575611.[888111] Hz) - 0x21E6EFE3
+ * PAL-N (3582056.25 Hz) - 0x21F69446
+ *
+ * NOTE: For SECAM, it is used as the Dr center frequency,
+ * regardless of whether VEC_CONFIG1_CUSTOM_FREQ is enabled or not;
+ * that is specified as 4406250 Hz, which corresponds to 0x29C71C72.
+ */
#define VEC_FREQ3_2 0x180
#define VEC_FREQ1_0 0x184
@@ -118,6 +140,14 @@
#define VEC_INTERRUPT_CONTROL 0x190
#define VEC_INTERRUPT_STATUS 0x194
+
+/*
+ * Db center frequency for SECAM; the clock for this is the same as for
+ * VEC_FREQ3_2/VEC_FREQ1_0, which is used for Dr center frequency.
+ *
+ * This is specified as 4250000 Hz, which corresponds to 0x284BDA13.
+ * That is also the default value, so no need to set it explicitly.
+ */
#define VEC_FCW_SECAM_B 0x198
#define VEC_SECAM_GAIN_VAL 0x19c
@@ -172,11 +202,22 @@ struct vc4_vec {
struct clk *clock;
+ struct drm_property *legacy_tv_mode_property;
+
struct debugfs_regset32 regset;
};
-#define VEC_READ(offset) readl(vec->regs + (offset))
-#define VEC_WRITE(offset, val) writel(val, vec->regs + (offset))
+#define VEC_READ(offset) \
+ ({ \
+ kunit_fail_current_test("Accessing a register in a unit test!\n"); \
+ readl(vec->regs + (offset)); \
+ })
+
+#define VEC_WRITE(offset, val) \
+ do { \
+ kunit_fail_current_test("Accessing a register in a unit test!\n"); \
+ writel(val, vec->regs + (offset)); \
+ } while (0)
static inline struct vc4_vec *
encoder_to_vc4_vec(struct drm_encoder *encoder)
@@ -184,15 +225,26 @@ encoder_to_vc4_vec(struct drm_encoder *encoder)
return container_of(encoder, struct vc4_vec, encoder.base);
}
+static inline struct vc4_vec *
+connector_to_vc4_vec(struct drm_connector *connector)
+{
+ return container_of(connector, struct vc4_vec, connector);
+}
+
enum vc4_vec_tv_mode_id {
VC4_VEC_TV_MODE_NTSC,
VC4_VEC_TV_MODE_NTSC_J,
VC4_VEC_TV_MODE_PAL,
VC4_VEC_TV_MODE_PAL_M,
+ VC4_VEC_TV_MODE_NTSC_443,
+ VC4_VEC_TV_MODE_PAL_60,
+ VC4_VEC_TV_MODE_PAL_N,
+ VC4_VEC_TV_MODE_SECAM,
};
struct vc4_vec_tv_mode {
- const struct drm_display_mode *mode;
+ unsigned int mode;
+ u16 expected_htotal;
u32 config0;
u32 config1;
u32 custom_freq;
@@ -225,41 +277,86 @@ static const struct debugfs_reg32 vec_regs[] = {
VC4_REG32(VEC_DAC_MISC),
};
-static const struct drm_display_mode ntsc_mode = {
- DRM_MODE("720x480", DRM_MODE_TYPE_DRIVER, 13500,
- 720, 720 + 14, 720 + 14 + 64, 720 + 14 + 64 + 60, 0,
- 480, 480 + 7, 480 + 7 + 6, 525, 0,
- DRM_MODE_FLAG_INTERLACE)
-};
-
-static const struct drm_display_mode pal_mode = {
- DRM_MODE("720x576", DRM_MODE_TYPE_DRIVER, 13500,
- 720, 720 + 20, 720 + 20 + 64, 720 + 20 + 64 + 60, 0,
- 576, 576 + 4, 576 + 4 + 6, 625, 0,
- DRM_MODE_FLAG_INTERLACE)
-};
-
static const struct vc4_vec_tv_mode vc4_vec_tv_modes[] = {
- [VC4_VEC_TV_MODE_NTSC] = {
- .mode = &ntsc_mode,
+ {
+ .mode = DRM_MODE_TV_MODE_NTSC,
+ .expected_htotal = 858,
.config0 = VEC_CONFIG0_NTSC_STD | VEC_CONFIG0_PDEN,
.config1 = VEC_CONFIG1_C_CVBS_CVBS,
},
- [VC4_VEC_TV_MODE_NTSC_J] = {
- .mode = &ntsc_mode,
+ {
+ .mode = DRM_MODE_TV_MODE_NTSC_443,
+ .expected_htotal = 858,
+ .config0 = VEC_CONFIG0_NTSC_STD,
+ .config1 = VEC_CONFIG1_C_CVBS_CVBS | VEC_CONFIG1_CUSTOM_FREQ,
+ .custom_freq = 0x2a098acb,
+ },
+ {
+ .mode = DRM_MODE_TV_MODE_NTSC_J,
+ .expected_htotal = 858,
.config0 = VEC_CONFIG0_NTSC_STD,
.config1 = VEC_CONFIG1_C_CVBS_CVBS,
},
- [VC4_VEC_TV_MODE_PAL] = {
- .mode = &pal_mode,
+ {
+ .mode = DRM_MODE_TV_MODE_PAL,
+ .expected_htotal = 864,
.config0 = VEC_CONFIG0_PAL_BDGHI_STD,
.config1 = VEC_CONFIG1_C_CVBS_CVBS,
},
- [VC4_VEC_TV_MODE_PAL_M] = {
- .mode = &ntsc_mode,
+ {
+ /* PAL-60 */
+ .mode = DRM_MODE_TV_MODE_PAL,
+ .expected_htotal = 858,
+ .config0 = VEC_CONFIG0_PAL_M_STD,
+ .config1 = VEC_CONFIG1_C_CVBS_CVBS | VEC_CONFIG1_CUSTOM_FREQ,
+ .custom_freq = 0x2a098acb,
+ },
+ {
+ .mode = DRM_MODE_TV_MODE_PAL_M,
+ .expected_htotal = 858,
.config0 = VEC_CONFIG0_PAL_M_STD,
.config1 = VEC_CONFIG1_C_CVBS_CVBS,
},
+ {
+ .mode = DRM_MODE_TV_MODE_PAL_N,
+ .expected_htotal = 864,
+ .config0 = VEC_CONFIG0_PAL_N_STD,
+ .config1 = VEC_CONFIG1_C_CVBS_CVBS,
+ },
+ {
+ .mode = DRM_MODE_TV_MODE_SECAM,
+ .expected_htotal = 864,
+ .config0 = VEC_CONFIG0_SECAM_STD,
+ .config1 = VEC_CONFIG1_C_CVBS_CVBS,
+ .custom_freq = 0x29c71c72,
+ },
+};
+
+static inline const struct vc4_vec_tv_mode *
+vc4_vec_tv_mode_lookup(unsigned int mode, u16 htotal)
+{
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(vc4_vec_tv_modes); i++) {
+ const struct vc4_vec_tv_mode *tv_mode = &vc4_vec_tv_modes[i];
+
+ if (tv_mode->mode == mode &&
+ tv_mode->expected_htotal == htotal)
+ return tv_mode;
+ }
+
+ return NULL;
+}
+
+static const struct drm_prop_enum_list legacy_tv_mode_names[] = {
+ { VC4_VEC_TV_MODE_NTSC, "NTSC", },
+ { VC4_VEC_TV_MODE_NTSC_443, "NTSC-443", },
+ { VC4_VEC_TV_MODE_NTSC_J, "NTSC-J", },
+ { VC4_VEC_TV_MODE_PAL, "PAL", },
+ { VC4_VEC_TV_MODE_PAL_60, "PAL-60", },
+ { VC4_VEC_TV_MODE_PAL_M, "PAL-M", },
+ { VC4_VEC_TV_MODE_PAL_N, "PAL-N", },
+ { VC4_VEC_TV_MODE_SECAM, "SECAM", },
};
static enum drm_connector_status
@@ -268,38 +365,126 @@ vc4_vec_connector_detect(struct drm_connector *connector, bool force)
return connector_status_unknown;
}
-static int vc4_vec_connector_get_modes(struct drm_connector *connector)
+static void vc4_vec_connector_reset(struct drm_connector *connector)
{
- struct drm_connector_state *state = connector->state;
- struct drm_display_mode *mode;
+ drm_atomic_helper_connector_reset(connector);
+ drm_atomic_helper_connector_tv_reset(connector);
+}
- mode = drm_mode_duplicate(connector->dev,
- vc4_vec_tv_modes[state->tv.mode].mode);
- if (!mode) {
- DRM_ERROR("Failed to create a new display mode\n");
- return -ENOMEM;
+static int
+vc4_vec_connector_set_property(struct drm_connector *connector,
+ struct drm_connector_state *state,
+ struct drm_property *property,
+ uint64_t val)
+{
+ struct vc4_vec *vec = connector_to_vc4_vec(connector);
+
+ if (property != vec->legacy_tv_mode_property)
+ return -EINVAL;
+
+ switch (val) {
+ case VC4_VEC_TV_MODE_NTSC:
+ state->tv.mode = DRM_MODE_TV_MODE_NTSC;
+ break;
+
+ case VC4_VEC_TV_MODE_NTSC_443:
+ state->tv.mode = DRM_MODE_TV_MODE_NTSC_443;
+ break;
+
+ case VC4_VEC_TV_MODE_NTSC_J:
+ state->tv.mode = DRM_MODE_TV_MODE_NTSC_J;
+ break;
+
+ case VC4_VEC_TV_MODE_PAL:
+ case VC4_VEC_TV_MODE_PAL_60:
+ state->tv.mode = DRM_MODE_TV_MODE_PAL;
+ break;
+
+ case VC4_VEC_TV_MODE_PAL_M:
+ state->tv.mode = DRM_MODE_TV_MODE_PAL_M;
+ break;
+
+ case VC4_VEC_TV_MODE_PAL_N:
+ state->tv.mode = DRM_MODE_TV_MODE_PAL_N;
+ break;
+
+ case VC4_VEC_TV_MODE_SECAM:
+ state->tv.mode = DRM_MODE_TV_MODE_SECAM;
+ break;
+
+ default:
+ return -EINVAL;
}
- drm_mode_probed_add(connector, mode);
+ return 0;
+}
+
+static int
+vc4_vec_connector_get_property(struct drm_connector *connector,
+ const struct drm_connector_state *state,
+ struct drm_property *property,
+ uint64_t *val)
+{
+ struct vc4_vec *vec = connector_to_vc4_vec(connector);
+
+ if (property != vec->legacy_tv_mode_property)
+ return -EINVAL;
+
+ switch (state->tv.mode) {
+ case DRM_MODE_TV_MODE_NTSC:
+ *val = VC4_VEC_TV_MODE_NTSC;
+ break;
+
+ case DRM_MODE_TV_MODE_NTSC_443:
+ *val = VC4_VEC_TV_MODE_NTSC_443;
+ break;
+
+ case DRM_MODE_TV_MODE_NTSC_J:
+ *val = VC4_VEC_TV_MODE_NTSC_J;
+ break;
- return 1;
+ case DRM_MODE_TV_MODE_PAL:
+ *val = VC4_VEC_TV_MODE_PAL;
+ break;
+
+ case DRM_MODE_TV_MODE_PAL_M:
+ *val = VC4_VEC_TV_MODE_PAL_M;
+ break;
+
+ case DRM_MODE_TV_MODE_PAL_N:
+ *val = VC4_VEC_TV_MODE_PAL_N;
+ break;
+
+ case DRM_MODE_TV_MODE_SECAM:
+ *val = VC4_VEC_TV_MODE_SECAM;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
}
static const struct drm_connector_funcs vc4_vec_connector_funcs = {
.detect = vc4_vec_connector_detect,
.fill_modes = drm_helper_probe_single_connector_modes,
- .reset = drm_atomic_helper_connector_reset,
+ .reset = vc4_vec_connector_reset,
.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
+ .atomic_get_property = vc4_vec_connector_get_property,
+ .atomic_set_property = vc4_vec_connector_set_property,
};
static const struct drm_connector_helper_funcs vc4_vec_connector_helper_funcs = {
- .get_modes = vc4_vec_connector_get_modes,
+ .atomic_check = drm_atomic_helper_connector_tv_check,
+ .get_modes = drm_connector_helper_tv_get_modes,
};
static int vc4_vec_connector_init(struct drm_device *dev, struct vc4_vec *vec)
{
struct drm_connector *connector = &vec->connector;
+ struct drm_property *prop;
int ret;
connector->interlace_allowed = true;
@@ -313,7 +498,16 @@ static int vc4_vec_connector_init(struct drm_device *dev, struct vc4_vec *vec)
drm_object_attach_property(&connector->base,
dev->mode_config.tv_mode_property,
- VC4_VEC_TV_MODE_NTSC);
+ DRM_MODE_TV_MODE_NTSC);
+
+ prop = drm_property_create_enum(dev, 0, "mode",
+ legacy_tv_mode_names,
+ ARRAY_SIZE(legacy_tv_mode_names));
+ if (!prop)
+ return -ENOMEM;
+ vec->legacy_tv_mode_property = prop;
+
+ drm_object_attach_property(&connector->base, prop, VC4_VEC_TV_MODE_NTSC);
drm_connector_attach_encoder(connector, &vec->encoder.base);
@@ -360,14 +554,20 @@ static void vc4_vec_encoder_enable(struct drm_encoder *encoder,
struct drm_connector *connector = &vec->connector;
struct drm_connector_state *conn_state =
drm_atomic_get_new_connector_state(state, connector);
- const struct vc4_vec_tv_mode *tv_mode =
- &vc4_vec_tv_modes[conn_state->tv.mode];
+ struct drm_display_mode *adjusted_mode =
+ &encoder->crtc->state->adjusted_mode;
+ const struct vc4_vec_tv_mode *tv_mode;
int idx, ret;
if (!drm_dev_enter(drm, &idx))
return;
- ret = pm_runtime_get_sync(&vec->pdev->dev);
+ tv_mode = vc4_vec_tv_mode_lookup(conn_state->tv.mode,
+ adjusted_mode->htotal);
+ if (!tv_mode)
+ goto err_dev_exit;
+
+ ret = pm_runtime_resume_and_get(&vec->pdev->dev);
if (ret < 0) {
DRM_ERROR("Failed to retain power domain: %d\n", ret);
goto err_dev_exit;
@@ -413,7 +613,9 @@ static void vc4_vec_encoder_enable(struct drm_encoder *encoder,
VEC_WRITE(VEC_CLMP0_START, 0xac);
VEC_WRITE(VEC_CLMP0_END, 0xec);
VEC_WRITE(VEC_CONFIG2,
- VEC_CONFIG2_UV_DIG_DIS | VEC_CONFIG2_RGB_DIG_DIS);
+ VEC_CONFIG2_UV_DIG_DIS |
+ VEC_CONFIG2_RGB_DIG_DIS |
+ ((adjusted_mode->flags & DRM_MODE_FLAG_INTERLACE) ? 0 : VEC_CONFIG2_PROG_SCAN));
VEC_WRITE(VEC_CONFIG3, VEC_CONFIG3_HORIZ_LEN_STD);
VEC_WRITE(VEC_DAC_CONFIG, vec->variant->dac_config);
@@ -447,13 +649,61 @@ static int vc4_vec_encoder_atomic_check(struct drm_encoder *encoder,
struct drm_crtc_state *crtc_state,
struct drm_connector_state *conn_state)
{
- const struct vc4_vec_tv_mode *vec_mode;
+ const struct drm_display_mode *mode = &crtc_state->adjusted_mode;
+ const struct vc4_vec_tv_mode *tv_mode;
+
+ tv_mode = vc4_vec_tv_mode_lookup(conn_state->tv.mode, mode->htotal);
+ if (!tv_mode)
+ return -EINVAL;
+
+ if (mode->crtc_hdisplay % 4)
+ return -EINVAL;
+
+ if (!(mode->crtc_hsync_end - mode->crtc_hsync_start))
+ return -EINVAL;
+
+ switch (mode->htotal) {
+ /* NTSC */
+ case 858:
+ if (mode->crtc_vtotal > 262)
+ return -EINVAL;
+
+ if (mode->crtc_vdisplay < 1 || mode->crtc_vdisplay > 253)
+ return -EINVAL;
+
+ if (!(mode->crtc_vsync_start - mode->crtc_vdisplay))
+ return -EINVAL;
+
+ if ((mode->crtc_vsync_end - mode->crtc_vsync_start) != 3)
+ return -EINVAL;
+
+ if ((mode->crtc_vtotal - mode->crtc_vsync_end) < 4)
+ return -EINVAL;
- vec_mode = &vc4_vec_tv_modes[conn_state->tv.mode];
+ break;
- if (conn_state->crtc &&
- !drm_mode_equal(vec_mode->mode, &crtc_state->adjusted_mode))
+ /* PAL/SECAM */
+ case 864:
+ if (mode->crtc_vtotal > 312)
+ return -EINVAL;
+
+ if (mode->crtc_vdisplay < 1 || mode->crtc_vdisplay > 305)
+ return -EINVAL;
+
+ if (!(mode->crtc_vsync_start - mode->crtc_vdisplay))
+ return -EINVAL;
+
+ if ((mode->crtc_vsync_end - mode->crtc_vsync_start) != 3)
+ return -EINVAL;
+
+ if ((mode->crtc_vtotal - mode->crtc_vsync_end) < 2)
+ return -EINVAL;
+
+ break;
+
+ default:
return -EINVAL;
+ }
return 0;
}
@@ -468,12 +718,8 @@ static int vc4_vec_late_register(struct drm_encoder *encoder)
{
struct drm_device *drm = encoder->dev;
struct vc4_vec *vec = encoder_to_vc4_vec(encoder);
- int ret;
- ret = vc4_debugfs_add_regset32(drm->primary, "vec_regs",
- &vec->regset);
- if (ret)
- return ret;
+ vc4_debugfs_add_regset32(drm, "vec_regs", &vec->regset);
return 0;
}
@@ -500,13 +746,6 @@ static const struct of_device_id vc4_vec_dt_match[] = {
{ /* sentinel */ },
};
-static const char * const tv_mode_names[] = {
- [VC4_VEC_TV_MODE_NTSC] = "NTSC",
- [VC4_VEC_TV_MODE_NTSC_J] = "NTSC-J",
- [VC4_VEC_TV_MODE_PAL] = "PAL",
- [VC4_VEC_TV_MODE_PAL_M] = "PAL-M",
-};
-
static int vc4_vec_bind(struct device *dev, struct device *master, void *data)
{
struct platform_device *pdev = to_platform_device(dev);
@@ -514,8 +753,14 @@ static int vc4_vec_bind(struct device *dev, struct device *master, void *data)
struct vc4_vec *vec;
int ret;
- ret = drm_mode_create_tv_properties(drm, ARRAY_SIZE(tv_mode_names),
- tv_mode_names);
+ ret = drm_mode_create_tv_properties(drm,
+ BIT(DRM_MODE_TV_MODE_NTSC) |
+ BIT(DRM_MODE_TV_MODE_NTSC_443) |
+ BIT(DRM_MODE_TV_MODE_NTSC_J) |
+ BIT(DRM_MODE_TV_MODE_PAL) |
+ BIT(DRM_MODE_TV_MODE_PAL_M) |
+ BIT(DRM_MODE_TV_MODE_PAL_N) |
+ BIT(DRM_MODE_TV_MODE_SECAM));
if (ret)
return ret;