diff options
author | Daniel Vetter <daniel.vetter@ffwll.ch> | 2023-01-04 14:59:24 +0100 |
---|---|---|
committer | Daniel Vetter <daniel.vetter@ffwll.ch> | 2023-01-04 14:59:25 +0100 |
commit | 03a0a1040895711e12c15ab28d4d1812928e171d (patch) | |
tree | ee39720358f6025b5cc171141ba045394a5d41b8 | |
parent | f422fa7cd85832d7d1505d7fae7ae519185354cc (diff) | |
parent | 2591939e881cf728b6ac45971eeec2f58051c101 (diff) | |
download | linux-03a0a1040895711e12c15ab28d4d1812928e171d.tar.gz linux-03a0a1040895711e12c15ab28d4d1812928e171d.tar.bz2 linux-03a0a1040895711e12c15ab28d4d1812928e171d.zip |
Merge tag 'drm-misc-next-2023-01-03' of git://anongit.freedesktop.org/drm/drm-misc into drm-next
drm-misc-next for v6.3:
UAPI Changes:
* connector: Support analog-TV mode property
* media: Add MEDIA_BUS_FMT_RGB565_1X24_CPADHI,
MEDIA_BUS_FMT_RGB666_1X18 and MEDIA_BUS_FMT_RGB666_1X24_CPADHI
Cross-subsystem Changes:
* dma-buf: Documentation fixes
* i2c: Introduce i2c_client_get_device_id() helper
Core Changes:
* Improve support for analog TV output
* bridge: Remove unused drm_bridge_chain functions
* debugfs: Add per-device helpers and convert various DRM drivers
* dp-mst: Various fixes
* fbdev emulation: Always pick 32 bpp as default
* KUnit: Add tests for managed helpers; Various cleanups
* panel-orientation: Add quirks for Lenovo Yoga Tab 3 X90F and DynaBook K50
* TTM: Open-code ttm_bo_wait() and remove the helper
Driver Changes:
* Fix preferred depth and bpp values throughout DRM drivers
* Remove #CONFIG_PM guards throughout DRM drivers
* ast: Various fixes
* bridge: Implement i2c's probe_new in various drivers; Fixes; ite-it6505:
Locking fixes, Cache EDID data; ite-it66121: Support IT6610 chip,
Cleanups; lontium-tl9611: Fix HDMI on DragonBoard 845c; parade-ps8640:
Use atomic bridge functions
* gud: Convert to DRM shadow-plane helpers; Perform flushing synchronously
during atomic update
* ili9486: Support 16-bit pixel data
* imx: Split off IPUv3 driver; Various fixes
* mipi-dbi: Convert to DRM shadow-plane helpers plus rsp driver changes;i
Support separate I/O-voltage supply
* mxsfb: Depend on ARCH_MXS or ARCH_MXC
* omapdrm: Various fixes
* panel: Use ktime_get_boottime() to measure power-down delay in various
drivers; Fix auto-suspend delay in various drivers; orisetech-ota5601a:
Add support
* sprd: Cleanups
* sun4i: Convert to new TV-mode property
* tidss: Various fixes
* v3d: Various fixes
* vc4: Convert to new TV-mode property; Support Kunit tests; Cleanups;
dpi: Support RGB565 and RGB666 formats; dsi: Convert DSI driver to
bridge
* virtio: Improve tracing
* vkms: Support small cursors in IGT tests; Various fixes
Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch>
From: Thomas Zimmermann <tzimmermann@suse.de>
Link: https://patchwork.freedesktop.org/patch/msgid/Y7QIwlfElAYWxRcR@linux-uq9g
253 files changed, 6902 insertions, 2512 deletions
diff --git a/Documentation/devicetree/bindings/display/bridge/ite,it6505.yaml b/Documentation/devicetree/bindings/display/bridge/ite,it6505.yaml index b697c42399ea..c9a882ee6d98 100644 --- a/Documentation/devicetree/bindings/display/bridge/ite,it6505.yaml +++ b/Documentation/devicetree/bindings/display/bridge/ite,it6505.yaml @@ -52,9 +52,49 @@ properties: maxItems: 1 description: extcon specifier for the Power Delivery - port: - $ref: /schemas/graph.yaml#/properties/port - description: A port node pointing to DPI host port node + ports: + $ref: /schemas/graph.yaml#/properties/ports + + properties: + port@0: + $ref: /schemas/graph.yaml#/$defs/port-base + unevaluatedProperties: false + description: A port node pointing to DPI host port node + + properties: + endpoint: + $ref: /schemas/graph.yaml#/$defs/endpoint-base + unevaluatedProperties: false + + properties: + link-frequencies: + minItems: 1 + maxItems: 1 + description: Allowed max link frequencies in Hz + + port@1: + $ref: /schemas/graph.yaml#/$defs/port-base + unevaluatedProperties: false + description: Video port for DP output + + properties: + endpoint: + $ref: /schemas/graph.yaml#/$defs/endpoint-base + unevaluatedProperties: false + + properties: + data-lanes: + minItems: 1 + uniqueItems: true + items: + - enum: [ 0, 1 ] + - const: 1 + - const: 2 + - const: 3 + + required: + - port@0 + - port@1 required: - compatible @@ -63,6 +103,7 @@ required: - interrupts - reset-gpios - extcon + - ports additionalProperties: false @@ -85,9 +126,24 @@ examples: reset-gpios = <&pio 179 1>; extcon = <&usbc_extcon>; - port { - it6505_in: endpoint { - remote-endpoint = <&dpi_out>; + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + it6505_in: endpoint { + remote-endpoint = <&dpi_out>; + link-frequencies = /bits/ 64 <150000000>; + }; + }; + + port@1 { + reg = <1>; + it6505_out: endpoint { + remote-endpoint = <&dp_in>; + data-lanes = <0 1>; + }; }; }; }; diff --git a/Documentation/devicetree/bindings/display/bridge/ite,it66121.yaml b/Documentation/devicetree/bindings/display/bridge/ite,it66121.yaml index d3454da1247a..a7eb2603691f 100644 --- a/Documentation/devicetree/bindings/display/bridge/ite,it66121.yaml +++ b/Documentation/devicetree/bindings/display/bridge/ite,it66121.yaml @@ -17,7 +17,9 @@ description: | properties: compatible: - const: ite,it66121 + enum: + - ite,it66121 + - ite,it6610 reg: maxItems: 1 diff --git a/Documentation/devicetree/bindings/display/panel/focaltech,gpt3.yaml b/Documentation/devicetree/bindings/display/panel/focaltech,gpt3.yaml new file mode 100644 index 000000000000..d54e96b2a9e1 --- /dev/null +++ b/Documentation/devicetree/bindings/display/panel/focaltech,gpt3.yaml @@ -0,0 +1,56 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/display/panel/focaltech,gpt3.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Focaltech GPT3 3.0" (640x480 pixels) IPS LCD panel + +maintainers: + - Christophe Branchereau <cbranchereau@gmail.com> + +allOf: + - $ref: panel-common.yaml# + - $ref: /schemas/spi/spi-peripheral-props.yaml# + +properties: + compatible: + const: focaltech,gpt3 + + reg: + maxItems: 1 + +required: + - compatible + - reg + - power-supply + - reset-gpios + +unevaluatedProperties: false + +examples: + - | + #include <dt-bindings/gpio/gpio.h> + + spi { + #address-cells = <1>; + #size-cells = <0>; + + panel@0 { + compatible = "focaltech,gpt3"; + reg = <0>; + + spi-max-frequency = <3125000>; + + reset-gpios = <&gpe 2 GPIO_ACTIVE_LOW>; + + backlight = <&backlight>; + power-supply = <&vcc>; + + port { + panel_input: endpoint { + remote-endpoint = <&panel_output>; + }; + }; + }; + }; diff --git a/Documentation/devicetree/bindings/display/panel/panel-mipi-dbi-spi.yaml b/Documentation/devicetree/bindings/display/panel/panel-mipi-dbi-spi.yaml index c2df8d28aaf5..9b701df5e9d2 100644 --- a/Documentation/devicetree/bindings/display/panel/panel-mipi-dbi-spi.yaml +++ b/Documentation/devicetree/bindings/display/panel/panel-mipi-dbi-spi.yaml @@ -22,8 +22,9 @@ description: | The standard defines the following interface signals for type C: - Power: - Vdd: Power supply for display module + Called power-supply in this binding. - Vddi: Logic level supply for interface signals - Combined into one in this binding called: power-supply + Called io-supply in this binding. - Interface: - CSx: Chip select - SCL: Serial clock @@ -80,6 +81,11 @@ properties: Controller data/command selection (D/CX) in 4-line SPI mode. If not set, the controller is in 3-line SPI mode. + io-supply: + description: | + Logic level supply for interface signals (Vddi). + No need to set if this is the same as power-supply. + required: - compatible - reg diff --git a/Documentation/fb/modedb.rst b/Documentation/fb/modedb.rst index e53375033146..bb2889c6ea27 100644 --- a/Documentation/fb/modedb.rst +++ b/Documentation/fb/modedb.rst @@ -29,7 +29,10 @@ Things between square brackets are optional. Valid names are:: - NSTC: 480i output, with the CCIR System-M TV mode and NTSC color encoding + - NTSC-J: 480i output, with the CCIR System-M TV mode, the NTSC color + encoding, and a black level equal to the blanking level. - PAL: 576i output, with the CCIR System-B TV mode and PAL color encoding + - PAL-M: 480i output, with the CCIR System-M TV mode and PAL color encoding If 'M' is specified in the mode_option argument (after <yres> and before <bpp> and <refresh>, if specified) the timings will be calculated using @@ -70,6 +73,8 @@ Valid options are:: - reflect_y (boolean): Perform an axial symmetry on the Y axis - rotate (integer): Rotate the initial framebuffer by x degrees. Valid values are 0, 90, 180 and 270. + - tv_mode: Analog TV mode. One of "NTSC", "NTSC-443", "NTSC-J", "PAL", + "PAL-M", "PAL-N", or "SECAM". - panel_orientation, one of "normal", "upside_down", "left_side_up", or "right_side_up". For KMS drivers only, this sets the "panel orientation" property on the kms connector as hint for kms users. diff --git a/Documentation/gpu/drm-kms-helpers.rst b/Documentation/gpu/drm-kms-helpers.rst index a4860ffd6e86..b8ab05e42dbb 100644 --- a/Documentation/gpu/drm-kms-helpers.rst +++ b/Documentation/gpu/drm-kms-helpers.rst @@ -188,6 +188,13 @@ Bridge Helper Reference .. kernel-doc:: drivers/gpu/drm/drm_bridge.c :export: +MIPI-DSI bridge operation +------------------------- + +.. kernel-doc:: drivers/gpu/drm/drm_bridge.c + :doc: dsi bridge operations + + Bridge Connector Helper Reference --------------------------------- diff --git a/Documentation/gpu/drm-kms.rst b/Documentation/gpu/drm-kms.rst index b4377a545425..c92d425cb2dd 100644 --- a/Documentation/gpu/drm-kms.rst +++ b/Documentation/gpu/drm-kms.rst @@ -520,6 +520,12 @@ HDMI Specific Connector Properties .. kernel-doc:: drivers/gpu/drm/drm_connector.c :doc: HDMI connector properties +Analog TV Specific Connector Properties +--------------------------------------- + +.. kernel-doc:: drivers/gpu/drm/drm_connector.c + :doc: Analog TV Connector Properties + Standard CRTC Properties ------------------------ diff --git a/Documentation/gpu/todo.rst b/Documentation/gpu/todo.rst index b2c6aaf1edf2..1f8a5ebe188e 100644 --- a/Documentation/gpu/todo.rst +++ b/Documentation/gpu/todo.rst @@ -508,17 +508,18 @@ Clean up the debugfs support There's a bunch of issues with it: -- The drm_info_list ->show() function doesn't even bother to cast to the drm - structure for you. This is lazy. +- Convert drivers to support the drm_debugfs_add_files() function instead of + the drm_debugfs_create_files() function. + +- Improve late-register debugfs by rolling out the same debugfs pre-register + infrastructure for connector and crtc too. That way, the drivers won't need to + split their setup code into init and register anymore. - We probably want to have some support for debugfs files on crtc/connectors and maybe other kms objects directly in core. There's even drm_print support in the funcs for these objects to dump kms state, so it's all there. And then the ->show() functions should obviously give you a pointer to the right object. -- The drm_info_list stuff is centered on drm_minor instead of drm_device. For - anything we want to print drm_device (or maybe drm_file) is the right thing. - - The drm_driver->debugfs_init hooks we have is just an artifact of the old midlayered load sequence. DRM debugfs should work more like sysfs, where you can create properties/files for an object anytime you want, and the core @@ -527,8 +528,6 @@ There's a bunch of issues with it: this (together with the drm_minor->drm_device move) would allow us to remove debugfs_init. -Previous RFC that hasn't landed yet: https://lore.kernel.org/dri-devel/20200513114130.28641-2-wambui.karugax@gmail.com/ - Contact: Daniel Vetter Level: Intermediate diff --git a/Documentation/gpu/vc4.rst b/Documentation/gpu/vc4.rst index 5df1d98b9544..5e5e92e40919 100644 --- a/Documentation/gpu/vc4.rst +++ b/Documentation/gpu/vc4.rst @@ -54,6 +54,25 @@ VEC (Composite TV out) encoder .. kernel-doc:: drivers/gpu/drm/vc4/vc4_vec.c :doc: VC4 SDTV module +KUnit Tests +=========== + +The VC4 Driver uses KUnit to perform driver-specific unit and +integration tests. + +These tests are using a mock driver and can be ran using the +command below, on either arm or arm64 architectures, + +.. code-block:: bash + + $ ./tools/testing/kunit/kunit.py run \ + --kunitconfig=drivers/gpu/drm/vc4/tests/.kunitconfig \ + --cross_compile aarch64-linux-gnu- --arch arm64 + +Parts of the driver that are currently covered by tests are: + * The HVS to PixelValve dynamic FIFO assignment, for the BCM2835-7 + and BCM2711. + Memory Management and 3D Command Submission =========================================== diff --git a/Documentation/userspace-api/media/v4l/subdev-formats.rst b/Documentation/userspace-api/media/v4l/subdev-formats.rst index 16ef3b41932e..a3a35eeed708 100644 --- a/Documentation/userspace-api/media/v4l/subdev-formats.rst +++ b/Documentation/userspace-api/media/v4l/subdev-formats.rst @@ -949,6 +949,43 @@ The following tables list existing packed RGB formats. - b\ :sub:`2` - b\ :sub:`1` - b\ :sub:`0` + * .. _MEDIA-BUS-FMT-BGR666-1X18: + + - MEDIA_BUS_FMT_BGR666_1X18 + - 0x1023 + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - b\ :sub:`5` + - b\ :sub:`4` + - b\ :sub:`3` + - b\ :sub:`2` + - b\ :sub:`1` + - b\ :sub:`0` + - g\ :sub:`5` + - g\ :sub:`4` + - g\ :sub:`3` + - g\ :sub:`2` + - g\ :sub:`1` + - g\ :sub:`0` + - r\ :sub:`5` + - r\ :sub:`4` + - r\ :sub:`3` + - r\ :sub:`2` + - r\ :sub:`1` + - r\ :sub:`0` * .. _MEDIA-BUS-FMT-RBG888-1X24: - MEDIA_BUS_FMT_RBG888_1X24 @@ -1023,6 +1060,80 @@ The following tables list existing packed RGB formats. - b\ :sub:`2` - b\ :sub:`1` - b\ :sub:`0` + * .. _MEDIA-BUS-FMT-BGR666-1X24_CPADHI: + + - MEDIA_BUS_FMT_BGR666_1X24_CPADHI + - 0x1024 + - + - + - + - + - + - + - + - + - + - 0 + - 0 + - b\ :sub:`5` + - b\ :sub:`4` + - b\ :sub:`3` + - b\ :sub:`2` + - b\ :sub:`1` + - b\ :sub:`0` + - 0 + - 0 + - g\ :sub:`5` + - g\ :sub:`4` + - g\ :sub:`3` + - g\ :sub:`2` + - g\ :sub:`1` + - g\ :sub:`0` + - 0 + - 0 + - r\ :sub:`5` + - r\ :sub:`4` + - r\ :sub:`3` + - r\ :sub:`2` + - r\ :sub:`1` + - r\ :sub:`0` + * .. _MEDIA-BUS-FMT-RGB565-1X24_CPADHI: + + - MEDIA_BUS_FMT_RGB565_1X24_CPADHI + - 0x1022 + - + - + - + - + - + - + - + - + - + - 0 + - 0 + - 0 + - r\ :sub:`4` + - r\ :sub:`3` + - r\ :sub:`2` + - r\ :sub:`1` + - r\ :sub:`0` + - 0 + - 0 + - g\ :sub:`5` + - g\ :sub:`4` + - g\ :sub:`3` + - g\ :sub:`2` + - g\ :sub:`1` + - g\ :sub:`0` + - 0 + - 0 + - 0 + - b\ :sub:`4` + - b\ :sub:`3` + - b\ :sub:`2` + - b\ :sub:`1` + - b\ :sub:`0` * .. _MEDIA-BUS-FMT-BGR888-1X24: - MEDIA_BUS_FMT_BGR888_1X24 diff --git a/MAINTAINERS b/MAINTAINERS index 7f86d02cb427..a47d963af3b8 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -6984,7 +6984,7 @@ M: Philipp Zabel <p.zabel@pengutronix.de> L: dri-devel@lists.freedesktop.org S: Maintained F: Documentation/devicetree/bindings/display/imx/ -F: drivers/gpu/drm/imx/ +F: drivers/gpu/drm/imx/ipuv3/ F: drivers/gpu/ipu-v3/ DRM DRIVERS FOR FREESCALE IMX BRIDGE diff --git a/drivers/dma-buf/dma-buf.c b/drivers/dma-buf/dma-buf.c index b6c36914e7c6..24e656a3ee74 100644 --- a/drivers/dma-buf/dma-buf.c +++ b/drivers/dma-buf/dma-buf.c @@ -1263,7 +1263,7 @@ EXPORT_SYMBOL_NS_GPL(dma_buf_unmap_attachment_unlocked, DMA_BUF); * * @dmabuf: [in] buffer which is moving * - * Informs all attachmenst that they need to destroy and recreated all their + * Informs all attachments that they need to destroy and recreate all their * mappings. */ void dma_buf_move_notify(struct dma_buf *dmabuf) @@ -1281,11 +1281,11 @@ EXPORT_SYMBOL_NS_GPL(dma_buf_move_notify, DMA_BUF); /** * DOC: cpu access * - * There are mutliple reasons for supporting CPU access to a dma buffer object: + * There are multiple reasons for supporting CPU access to a dma buffer object: * * - Fallback operations in the kernel, for example when a device is connected * over USB and the kernel needs to shuffle the data around first before - * sending it away. Cache coherency is handled by braketing any transactions + * sending it away. Cache coherency is handled by bracketing any transactions * with calls to dma_buf_begin_cpu_access() and dma_buf_end_cpu_access() * access. * @@ -1312,7 +1312,7 @@ EXPORT_SYMBOL_NS_GPL(dma_buf_move_notify, DMA_BUF); * replace ION buffers mmap support was needed. * * There is no special interfaces, userspace simply calls mmap on the dma-buf - * fd. But like for CPU access there's a need to braket the actual access, + * fd. But like for CPU access there's a need to bracket the actual access, * which is handled by the ioctl (DMA_BUF_IOCTL_SYNC). Note that * DMA_BUF_IOCTL_SYNC can fail with -EAGAIN or -EINTR, in which case it must * be restarted. @@ -1386,10 +1386,10 @@ static int __dma_buf_begin_cpu_access(struct dma_buf *dmabuf, * preparations. Coherency is only guaranteed in the specified range for the * specified access direction. * @dmabuf: [in] buffer to prepare cpu access for. - * @direction: [in] length of range for cpu access. + * @direction: [in] direction of access. * * After the cpu access is complete the caller should call - * dma_buf_end_cpu_access(). Only when cpu access is braketed by both calls is + * dma_buf_end_cpu_access(). Only when cpu access is bracketed by both calls is * it guaranteed to be coherent with other DMA access. * * This function will also wait for any DMA transactions tracked through @@ -1429,7 +1429,7 @@ EXPORT_SYMBOL_NS_GPL(dma_buf_begin_cpu_access, DMA_BUF); * actions. Coherency is only guaranteed in the specified range for the * specified access direction. * @dmabuf: [in] buffer to complete cpu access for. - * @direction: [in] length of range for cpu access. + * @direction: [in] direction of access. * * This terminates CPU access started with dma_buf_begin_cpu_access(). * diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig index 315cbdf61979..9f019cd053e1 100644 --- a/drivers/gpu/drm/Kconfig +++ b/drivers/gpu/drm/Kconfig @@ -63,6 +63,12 @@ config DRM_USE_DYNAMIC_DEBUG bytes per callsite, the .data costs can be substantial, and are therefore configurable. +config DRM_KUNIT_TEST_HELPERS + tristate + depends on DRM && KUNIT + help + KUnit Helpers for KMS drivers. + config DRM_KUNIT_TEST tristate "KUnit tests for DRM" if !KUNIT_ALL_TESTS depends on DRM && KUNIT @@ -73,6 +79,7 @@ config DRM_KUNIT_TEST select DRM_KMS_HELPER select DRM_BUDDY select DRM_EXPORT_FOR_TESTS if m + select DRM_KUNIT_TEST_HELPERS default KUNIT_ALL_TESTS help This builds unit tests for DRM. This option is not useful for diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile index cc637343d87b..496fa5a6147a 100644 --- a/drivers/gpu/drm/Makefile +++ b/drivers/gpu/drm/Makefile @@ -126,7 +126,7 @@ obj-$(CONFIG_DRM_KMS_HELPER) += drm_kms_helper.o # Drivers and the rest # -obj-$(CONFIG_DRM_KUNIT_TEST) += tests/ +obj-y += tests/ obj-$(CONFIG_DRM_MIPI_DBI) += drm_mipi_dbi.o obj-$(CONFIG_DRM_MIPI_DSI) += drm_mipi_dsi.o diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu.h b/drivers/gpu/drm/amd/amdgpu/amdgpu.h index 6b74df446694..2644cd991210 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu.h @@ -52,8 +52,7 @@ #include <linux/pci.h> #include <linux/aer.h> -#include <drm/ttm/ttm_bo_api.h> -#include <drm/ttm/ttm_bo_driver.h> +#include <drm/ttm/ttm_bo.h> #include <drm/ttm/ttm_placement.h> #include <drm/ttm/ttm_execbuf_util.h> diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gpuvm.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gpuvm.c index b15091d8310d..92131573cbb5 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gpuvm.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gpuvm.c @@ -25,6 +25,7 @@ #include <linux/pagemap.h> #include <linux/sched/mm.h> #include <linux/sched/task.h> +#include <drm/ttm/ttm_tt.h> #include "amdgpu_object.h" #include "amdgpu_gem.h" diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_bo_list.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_bo_list.h index e4d78491bcc7..ededdc01ca28 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_bo_list.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_bo_list.h @@ -28,6 +28,8 @@ struct hmm_range; +struct drm_file; + struct amdgpu_device; struct amdgpu_bo; struct amdgpu_bo_va; diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c index 8516c814bc9b..8b7a09b392ac 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c @@ -32,6 +32,8 @@ #include <drm/amdgpu_drm.h> #include <drm/drm_syncobj.h> +#include <drm/ttm/ttm_tt.h> + #include "amdgpu_cs.h" #include "amdgpu.h" #include "amdgpu_trace.h" diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.h index 113f39510a72..fb3e3d56d427 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.h @@ -23,6 +23,8 @@ #ifndef __AMDGPU_CS_H__ #define __AMDGPU_CS_H__ +#include <linux/ww_mutex.h> + #include "amdgpu_job.h" #include "amdgpu_bo_list.h" #include "amdgpu_ring.h" diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_debugfs.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_debugfs.c index 0f16d3c09309..f60753f97ac5 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_debugfs.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_debugfs.c @@ -1717,7 +1717,7 @@ no_preempt: static int amdgpu_debugfs_ib_preempt(void *data, u64 val) { - int r, resched, length; + int r, length; struct amdgpu_ring *ring; struct dma_fence **fences = NULL; struct amdgpu_device *adev = (struct amdgpu_device *)data; @@ -1747,8 +1747,6 @@ static int amdgpu_debugfs_ib_preempt(void *data, u64 val) /* stop the scheduler */ kthread_park(ring->sched.thread); - resched = ttm_bo_lock_delayed_workqueue(&adev->mman.bdev); - /* preempt the IB */ r = amdgpu_ring_preempt_ib(ring); if (r) { @@ -1785,8 +1783,6 @@ failure: up_read(&adev->reset_domain->sem); - ttm_bo_unlock_delayed_workqueue(&adev->mman.bdev, resched); - pro_end: kfree(fences); diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c index afe6af9c0138..076ae400d099 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c @@ -3989,10 +3989,8 @@ void amdgpu_device_fini_hw(struct amdgpu_device *adev) } amdgpu_fence_driver_hw_fini(adev); - if (adev->mman.initialized) { - flush_delayed_work(&adev->mman.bdev.wq); - ttm_bo_lock_delayed_workqueue(&adev->mman.bdev); - } + if (adev->mman.initialized) + drain_workqueue(adev->mman.bdev.wq); if (adev->pm_sysfs_en) amdgpu_pm_sysfs_fini(adev); diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_dma_buf.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_dma_buf.c index 271e30e34d93..0c001bb8fc2b 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_dma_buf.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_dma_buf.c @@ -37,6 +37,7 @@ #include "amdgpu_dma_buf.h" #include "amdgpu_xgmi.h" #include <drm/amdgpu_drm.h> +#include <drm/ttm/ttm_tt.h> #include <linux/dma-buf.h> #include <linux/dma-fence-array.h> #include <linux/pci-p2pdma.h> diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_fdinfo.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_fdinfo.h index 41a4c7056729..e86834bfea1d 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_fdinfo.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_fdinfo.h @@ -30,7 +30,6 @@ #include <linux/rbtree.h> #include <drm/gpu_scheduler.h> #include <drm/drm_file.h> -#include <drm/ttm/ttm_bo_driver.h> #include <linux/sched/mm.h> #include "amdgpu_sync.h" diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_gem.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_gem.c index bb7350ea1d75..00edc7002ee8 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_gem.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_gem.c @@ -34,6 +34,7 @@ #include <drm/amdgpu_drm.h> #include <drm/drm_drv.h> #include <drm/drm_gem_ttm_helper.h> +#include <drm/ttm/ttm_tt.h> #include "amdgpu.h" #include "amdgpu_display.h" diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_gmc.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_gmc.c index 02a4c93673ce..12871b71b07b 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_gmc.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_gmc.c @@ -35,6 +35,7 @@ #include "amdgpu_xgmi.h" #include <drm/drm_drv.h> +#include <drm/ttm/ttm_tt.h> /** * amdgpu_gmc_pdb0_alloc - allocate vram for pdb0 diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c index 55e0284b2bdd..28a7d2ea6661 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c @@ -44,10 +44,10 @@ #include <linux/module.h> #include <drm/drm_drv.h> -#include <drm/ttm/ttm_bo_api.h> -#include <drm/ttm/ttm_bo_driver.h> +#include <drm/ttm/ttm_bo.h> #include <drm/ttm/ttm_placement.h> #include <drm/ttm/ttm_range_manager.h> +#include <drm/ttm/ttm_tt.h> #include <drm/amdgpu_drm.h> #include <drm/drm_drv.h> diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c index dc379dc22c77..b9441ab457ea 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c @@ -33,6 +33,7 @@ #include <drm/amdgpu_drm.h> #include <drm/drm_drv.h> +#include <drm/ttm/ttm_tt.h> #include "amdgpu.h" #include "amdgpu_trace.h" #include "amdgpu_amdkfd.h" diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.h index 094bb4807303..856a64bc7a89 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.h @@ -29,7 +29,7 @@ #include <linux/rbtree.h> #include <drm/gpu_scheduler.h> #include <drm/drm_file.h> -#include <drm/ttm/ttm_bo_driver.h> +#include <drm/ttm/ttm_bo.h> #include <linux/sched/mm.h> #include "amdgpu_sync.h" diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_svm.c b/drivers/gpu/drm/amd/amdkfd/kfd_svm.c index 814f99888ab1..37219198d518 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_svm.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_svm.c @@ -23,6 +23,7 @@ #include <linux/types.h> #include <linux/sched/task.h> +#include <drm/ttm/ttm_tt.h> #include "amdgpu_sync.h" #include "amdgpu_object.h" #include "amdgpu_vm.h" diff --git a/drivers/gpu/drm/ast/ast_mode.c b/drivers/gpu/drm/ast/ast_mode.c index c7443317c747..e82e9a8d85e5 100644 --- a/drivers/gpu/drm/ast/ast_mode.c +++ b/drivers/gpu/drm/ast/ast_mode.c @@ -636,7 +636,7 @@ static void ast_handle_damage(struct ast_plane *ast_plane, struct iosys_map *src struct drm_framebuffer *fb, const struct drm_rect *clip) { - struct iosys_map dst = IOSYS_MAP_INIT_VADDR(ast_plane->vaddr); + struct iosys_map dst = IOSYS_MAP_INIT_VADDR_IOMEM(ast_plane->vaddr); iosys_map_incr(&dst, drm_fb_clip_offset(fb->pitches[0], fb->format, clip)); drm_fb_memcpy(&dst, fb->pitches, src, fb, clip); diff --git a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c index a2bb5b916235..4e806b06d35d 100644 --- a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c +++ b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c @@ -784,7 +784,6 @@ static int atmel_hlcdc_dc_drm_remove(struct platform_device *pdev) return 0; } -#ifdef CONFIG_PM_SLEEP static int atmel_hlcdc_dc_drm_suspend(struct device *dev) { struct drm_device *drm_dev = dev_get_drvdata(dev); @@ -815,10 +814,10 @@ static int atmel_hlcdc_dc_drm_resume(struct device *dev) return drm_atomic_helper_resume(drm_dev, dc->suspend.state); } -#endif -static SIMPLE_DEV_PM_OPS(atmel_hlcdc_dc_drm_pm_ops, - atmel_hlcdc_dc_drm_suspend, atmel_hlcdc_dc_drm_resume); +static DEFINE_SIMPLE_DEV_PM_OPS(atmel_hlcdc_dc_drm_pm_ops, + atmel_hlcdc_dc_drm_suspend, + atmel_hlcdc_dc_drm_resume); static const struct of_device_id atmel_hlcdc_dc_of_match[] = { { .compatible = "atmel,hlcdc-display-controller" }, @@ -830,7 +829,7 @@ static struct platform_driver atmel_hlcdc_dc_platform_driver = { .remove = atmel_hlcdc_dc_drm_remove, .driver = { .name = "atmel-hlcdc-display-controller", - .pm = &atmel_hlcdc_dc_drm_pm_ops, + .pm = pm_sleep_ptr(&atmel_hlcdc_dc_drm_pm_ops), .of_match_table = atmel_hlcdc_dc_of_match, }, }; diff --git a/drivers/gpu/drm/bridge/adv7511/adv7511_drv.c b/drivers/gpu/drm/bridge/adv7511/adv7511_drv.c index e7a6e456ed0d..ddceafa7b637 100644 --- a/drivers/gpu/drm/bridge/adv7511/adv7511_drv.c +++ b/drivers/gpu/drm/bridge/adv7511/adv7511_drv.c @@ -1185,8 +1185,9 @@ static int adv7511_parse_dt(struct device_node *np, return 0; } -static int adv7511_probe(struct i2c_client *i2c, const struct i2c_device_id *id) +static int adv7511_probe(struct i2c_client *i2c) { + const struct i2c_device_id *id = i2c_client_get_device_id(i2c); struct adv7511_link_config link_config; struct adv7511 *adv7511; struct device *dev = &i2c->dev; @@ -1392,7 +1393,7 @@ static struct i2c_driver adv7511_driver = { .of_match_table = adv7511_of_ids, }, .id_table = adv7511_i2c_ids, - .probe = adv7511_probe, + .probe_new = adv7511_probe, .remove = adv7511_remove, }; diff --git a/drivers/gpu/drm/bridge/analogix/analogix-anx6345.c b/drivers/gpu/drm/bridge/analogix/analogix-anx6345.c index 660a54857929..339e0f05b260 100644 --- a/drivers/gpu/drm/bridge/analogix/analogix-anx6345.c +++ b/drivers/gpu/drm/bridge/analogix/analogix-anx6345.c @@ -692,8 +692,7 @@ static bool anx6345_get_chip_id(struct anx6345 *anx6345) return false; } -static int anx6345_i2c_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int anx6345_i2c_probe(struct i2c_client *client) { struct anx6345 *anx6345; struct device *dev; @@ -817,7 +816,7 @@ static struct i2c_driver anx6345_driver = { .name = "anx6345", .of_match_table = of_match_ptr(anx6345_match_table), }, - .probe = anx6345_i2c_probe, + .probe_new = anx6345_i2c_probe, .remove = anx6345_i2c_remove, .id_table = anx6345_id, }; diff --git a/drivers/gpu/drm/bridge/analogix/analogix-anx78xx.c b/drivers/gpu/drm/bridge/analogix/analogix-anx78xx.c index 5997049fde5b..a3a38bbe2786 100644 --- a/drivers/gpu/drm/bridge/analogix/analogix-anx78xx.c +++ b/drivers/gpu/drm/bridge/analogix/analogix-anx78xx.c @@ -1214,8 +1214,7 @@ static const u16 anx78xx_chipid_list[] = { 0x7818, }; -static int anx78xx_i2c_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int anx78xx_i2c_probe(struct i2c_client *client) { struct anx78xx *anx78xx; struct anx78xx_platform_data *pdata; @@ -1390,7 +1389,7 @@ static struct i2c_driver anx78xx_driver = { .name = "anx7814", .of_match_table = of_match_ptr(anx78xx_match_table), }, - .probe = anx78xx_i2c_probe, + .probe_new = anx78xx_i2c_probe, .remove = anx78xx_i2c_remove, .id_table = anx78xx_id, }; diff --git a/drivers/gpu/drm/bridge/analogix/anx7625.c b/drivers/gpu/drm/bridge/analogix/anx7625.c index b0ff1ecb80a5..b375887e655d 100644 --- a/drivers/gpu/drm/bridge/analogix/anx7625.c +++ b/drivers/gpu/drm/bridge/analogix/anx7625.c @@ -1403,7 +1403,6 @@ static void anx7625_stop_dp_work(struct anx7625_data *ctx) { ctx->hpd_status = 0; ctx->hpd_high_cnt = 0; - ctx->display_timing_valid = 0; } static void anx7625_start_dp_work(struct anx7625_data *ctx) @@ -2562,8 +2561,7 @@ static void anx7625_runtime_disable(void *data) pm_runtime_disable(data); } -static int anx7625_i2c_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int anx7625_i2c_probe(struct i2c_client *client) { struct anx7625_data *platform; struct anx7625_platform_data *pdata; @@ -2756,7 +2754,7 @@ static struct i2c_driver anx7625_driver = { .of_match_table = anx_match_table, .pm = &anx7625_pm_ops, }, - .probe = anx7625_i2c_probe, + .probe_new = anx7625_i2c_probe, .remove = anx7625_i2c_remove, .id_table = anx7625_id, diff --git a/drivers/gpu/drm/bridge/chipone-icn6211.c b/drivers/gpu/drm/bridge/chipone-icn6211.c index bf920c3503aa..0e37840cd7a8 100644 --- a/drivers/gpu/drm/bridge/chipone-icn6211.c +++ b/drivers/gpu/drm/bridge/chipone-icn6211.c @@ -740,8 +740,7 @@ static int chipone_dsi_probe(struct mipi_dsi_device *dsi) return ret; } -static int chipone_i2c_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int chipone_i2c_probe(struct i2c_client *client) { struct device *dev = &client->dev; struct chipone *icn; @@ -796,7 +795,7 @@ static struct i2c_device_id chipone_i2c_id[] = { MODULE_DEVICE_TABLE(i2c, chipone_i2c_id); static struct i2c_driver chipone_i2c_driver = { - .probe = chipone_i2c_probe, + .probe_new = chipone_i2c_probe, .id_table = chipone_i2c_id, .driver = { .name = "chipone-icn6211-i2c", diff --git a/drivers/gpu/drm/bridge/chrontel-ch7033.c b/drivers/gpu/drm/bridge/chrontel-ch7033.c index b94f39a86846..339b759e4c81 100644 --- a/drivers/gpu/drm/bridge/chrontel-ch7033.c +++ b/drivers/gpu/drm/bridge/chrontel-ch7033.c @@ -528,8 +528,7 @@ static const struct regmap_config ch7033_regmap_config = { .max_register = 0x7f, }; -static int ch7033_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int ch7033_probe(struct i2c_client *client) { struct device *dev = &client->dev; struct ch7033_priv *priv; @@ -604,7 +603,7 @@ static const struct i2c_device_id ch7033_ids[] = { MODULE_DEVICE_TABLE(i2c, ch7033_ids); static struct i2c_driver ch7033_driver = { - .probe = ch7033_probe, + .probe_new = ch7033_probe, .remove = ch7033_remove, .driver = { .name = "ch7033", diff --git a/drivers/gpu/drm/bridge/ite-it6505.c b/drivers/gpu/drm/bridge/ite-it6505.c index 21a9b8422bda..9cda2df21b88 100644 --- a/drivers/gpu/drm/bridge/ite-it6505.c +++ b/drivers/gpu/drm/bridge/ite-it6505.c @@ -412,7 +412,6 @@ struct it6505 { * Mutex protects extcon and interrupt functions from interfering * each other. */ - struct mutex irq_lock; struct mutex extcon_lock; struct mutex mode_lock; /* used to bridge_detect */ struct mutex aux_lock; /* used to aux data transfers */ @@ -438,6 +437,8 @@ struct it6505 { bool powered; bool hpd_state; u32 afe_setting; + u32 max_dpi_pixel_clock; + u32 max_lane_count; enum hdcp_state hdcp_status; struct delayed_work hdcp_work; struct work_struct hdcp_wait_ksv_list; @@ -457,6 +458,8 @@ struct it6505 { /* it6505 driver hold option */ bool enable_drv_hold; + + struct edid *cached_edid; }; struct it6505_step_train_para { @@ -1463,7 +1466,8 @@ static void it6505_parse_link_capabilities(struct it6505 *it6505) it6505->lane_count = link->num_lanes; DRM_DEV_DEBUG_DRIVER(dev, "Sink support %d lanes training", it6505->lane_count); - it6505->lane_count = min_t(int, it6505->lane_count, MAX_LANE_COUNT); + it6505->lane_count = min_t(int, it6505->lane_count, + it6505->max_lane_count); it6505->branch_device = drm_dp_is_branch(it6505->dpcd); DRM_DEV_DEBUG_DRIVER(dev, "Sink %sbranch device", @@ -2244,6 +2248,12 @@ static void it6505_plugged_status_to_codec(struct it6505 *it6505) status == connector_status_connected); } +static void it6505_remove_edid(struct it6505 *it6505) +{ + kfree(it6505->cached_edid); + it6505->cached_edid = NULL; +} + static int it6505_process_hpd_irq(struct it6505 *it6505) { struct device *dev = &it6505->client->dev; @@ -2270,6 +2280,7 @@ static int it6505_process_hpd_irq(struct it6505 *it6505) it6505_reset_logic(it6505); it6505_int_mask_enable(it6505); it6505_init(it6505); + it6505_remove_edid(it6505); return 0; } @@ -2353,6 +2364,7 @@ static void it6505_irq_hpd(struct it6505 *it6505) it6505_video_reset(it6505); } else { memset(it6505->dpcd, 0, sizeof(it6505->dpcd)); + it6505_remove_edid(it6505); if (it6505->hdcp_desired) it6505_stop_hdcp(it6505); @@ -2494,10 +2506,8 @@ static irqreturn_t it6505_int_threaded_handler(int unused, void *data) }; int int_status[3], i; - mutex_lock(&it6505->irq_lock); - - if (it6505->enable_drv_hold || !it6505->powered) - goto unlock; + if (it6505->enable_drv_hold || pm_runtime_get_if_in_use(dev) <= 0) + return IRQ_HANDLED; int_status[0] = it6505_read(it6505, INT_STATUS_01); int_status[1] = it6505_read(it6505, INT_STATUS_02); @@ -2515,16 +2525,14 @@ static irqreturn_t it6505_int_threaded_handler(int unused, void *data) if (it6505_test_bit(irq_vec[0].bit, (unsigned int *)int_status)) irq_vec[0].handler(it6505); - if (!it6505->hpd_state) - goto unlock; - - for (i = 1; i < ARRAY_SIZE(irq_vec); i++) { - if (it6505_test_bit(irq_vec[i].bit, (unsigned int *)int_status)) - irq_vec[i].handler(it6505); + if (it6505->hpd_state) { + for (i = 1; i < ARRAY_SIZE(irq_vec); i++) { + if (it6505_test_bit(irq_vec[i].bit, (unsigned int *)int_status)) + irq_vec[i].handler(it6505); + } } -unlock: - mutex_unlock(&it6505->irq_lock); + pm_runtime_put_sync(dev); return IRQ_HANDLED; } @@ -2902,7 +2910,7 @@ it6505_bridge_mode_valid(struct drm_bridge *bridge, if (mode->flags & DRM_MODE_FLAG_INTERLACE) return MODE_NO_INTERLACE; - if (mode->clock > DPI_PIXEL_CLK_MAX) + if (mode->clock > it6505->max_dpi_pixel_clock) return MODE_CLOCK_HIGH; it6505->video_info.clock = mode->clock; @@ -3016,16 +3024,18 @@ static struct edid *it6505_bridge_get_edid(struct drm_bridge *bridge, { struct it6505 *it6505 = bridge_to_it6505(bridge); struct device *dev = &it6505->client->dev; - struct edid *edid; - edid = drm_do_get_edid(connector, it6505_get_edid_block, it6505); + if (!it6505->cached_edid) { + it6505->cached_edid = drm_do_get_edid(connector, it6505_get_edid_block, + it6505); - if (!edid) { - DRM_DEV_DEBUG_DRIVER(dev, "failed to get edid!"); - return NULL; + if (!it6505->cached_edid) { + DRM_DEV_DEBUG_DRIVER(dev, "failed to get edid!"); + return NULL; + } } - return edid; + return drm_edid_duplicate(it6505->cached_edid); } static const struct drm_bridge_funcs it6505_bridge_funcs = { @@ -3089,10 +3099,32 @@ static int it6505_init_pdata(struct it6505 *it6505) return 0; } +static int it6505_get_data_lanes_count(const struct device_node *endpoint, + const unsigned int min, + const unsigned int max) +{ + int ret; + + ret = of_property_count_u32_elems(endpoint, "data-lanes"); + if (ret < 0) + return ret; + + if (ret < min || ret > max) + return -EINVAL; + + return ret; +} + static void it6505_parse_dt(struct it6505 *it6505) { struct device *dev = &it6505->client->dev; + struct device_node *np = dev->of_node, *ep = NULL; + int len; + u64 link_frequencies; + u32 data_lanes[4]; u32 *afe_setting = &it6505->afe_setting; + u32 *max_lane_count = &it6505->max_lane_count; + u32 *max_dpi_pixel_clock = &it6505->max_dpi_pixel_clock; it6505->lane_swap_disabled = device_property_read_bool(dev, "no-laneswap"); @@ -3108,7 +3140,56 @@ static void it6505_parse_dt(struct it6505 *it6505) } else { *afe_setting = 0; } - DRM_DEV_DEBUG_DRIVER(dev, "using afe_setting: %d", *afe_setting); + + ep = of_graph_get_endpoint_by_regs(np, 1, 0); + of_node_put(ep); + + if (ep) { + len = it6505_get_data_lanes_count(ep, 1, 4); + + if (len > 0 && len != 3) { + of_property_read_u32_array(ep, "data-lanes", + data_lanes, len); + *max_lane_count = len; + } else { + *max_lane_count = MAX_LANE_COUNT; + dev_err(dev, "error data-lanes, use default"); + } + } else { + *max_lane_count = MAX_LANE_COUNT; + dev_err(dev, "error endpoint, use default"); + } + + ep = of_graph_get_endpoint_by_regs(np, 0, 0); + of_node_put(ep); + + if (ep) { + len = of_property_read_variable_u64_array(ep, + "link-frequencies", + &link_frequencies, 0, + 1); + if (len >= 0) { + do_div(link_frequencies, 1000); + if (link_frequencies > 297000) { + dev_err(dev, + "max pixel clock error, use default"); + *max_dpi_pixel_clock = DPI_PIXEL_CLK_MAX; + } else { + *max_dpi_pixel_clock = link_frequencies; + } + } else { + dev_err(dev, "error link frequencies, use default"); + *max_dpi_pixel_clock = DPI_PIXEL_CLK_MAX; + } + } else { + dev_err(dev, "error endpoint, use default"); + *max_dpi_pixel_clock = DPI_PIXEL_CLK_MAX; + } + + DRM_DEV_DEBUG_DRIVER(dev, "using afe_setting: %u, max_lane_count: %u", + it6505->afe_setting, it6505->max_lane_count); + DRM_DEV_DEBUG_DRIVER(dev, "using max_dpi_pixel_clock: %u kHz", + it6505->max_dpi_pixel_clock); } static ssize_t receive_timing_debugfs_show(struct file *file, char __user *buf, @@ -3265,8 +3346,7 @@ static void it6505_shutdown(struct i2c_client *client) it6505_lane_off(it6505); } -static int it6505_i2c_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int it6505_i2c_probe(struct i2c_client *client) { struct it6505 *it6505; struct device *dev = &client->dev; @@ -3277,7 +3357,6 @@ static int it6505_i2c_probe(struct i2c_client *client, if (!it6505) return -ENOMEM; - mutex_init(&it6505->irq_lock); mutex_init(&it6505->extcon_lock); mutex_init(&it6505->mode_lock); mutex_init(&it6505->aux_lock); @@ -3367,6 +3446,7 @@ static void it6505_i2c_remove(struct i2c_client *client) drm_dp_aux_unregister(&it6505->aux); it6505_debugfs_remove(it6505); it6505_poweroff(it6505); + it6505_remove_edid(it6505); } static const struct i2c_device_id it6505_id[] = { @@ -3387,7 +3467,7 @@ static struct i2c_driver it6505_i2c_driver = { .of_match_table = it6505_of_match, .pm = &it6505_bridge_pm_ops, }, - .probe = it6505_i2c_probe, + .probe_new = it6505_i2c_probe, .remove = it6505_i2c_remove, .shutdown = it6505_shutdown, .id_table = it6505_id, diff --git a/drivers/gpu/drm/bridge/ite-it66121.c b/drivers/gpu/drm/bridge/ite-it66121.c index 4f6f1deba28c..b34860871627 100644 --- a/drivers/gpu/drm/bridge/ite-it66121.c +++ b/drivers/gpu/drm/bridge/ite-it66121.c @@ -35,10 +35,6 @@ #define IT66121_DEVICE_ID0_REG 0x02 #define IT66121_DEVICE_ID1_REG 0x03 -#define IT66121_VENDOR_ID0 0x54 -#define IT66121_VENDOR_ID1 0x49 -#define IT66121_DEVICE_ID0 0x12 -#define IT66121_DEVICE_ID1 0x06 #define IT66121_REVISION_MASK GENMASK(7, 4) #define IT66121_DEVICE_ID1_MASK GENMASK(3, 0) @@ -72,6 +68,7 @@ #define IT66121_AFE_XP_ENO BIT(4) #define IT66121_AFE_XP_RESETB BIT(3) #define IT66121_AFE_XP_PWDI BIT(2) +#define IT6610_AFE_XP_BYPASS BIT(0) #define IT66121_AFE_IP_REG 0x64 #define IT66121_AFE_IP_GAINBIT BIT(7) @@ -286,13 +283,18 @@ #define IT66121_AUD_SWL_16BIT 0x2 #define IT66121_AUD_SWL_NOT_INDICATED 0x0 -#define IT66121_VENDOR_ID0 0x54 -#define IT66121_VENDOR_ID1 0x49 -#define IT66121_DEVICE_ID0 0x12 -#define IT66121_DEVICE_ID1 0x06 -#define IT66121_DEVICE_MASK 0x0F #define IT66121_AFE_CLK_HIGH 80000 /* Khz */ +enum chip_id { + ID_IT6610, + ID_IT66121, +}; + +struct it66121_chip_info { + enum chip_id id; + u16 vid, pid; +}; + struct it66121_ctx { struct regmap *regmap; struct drm_bridge bridge; @@ -301,7 +303,6 @@ struct it66121_ctx { struct device *dev; struct gpio_desc *gpio_reset; struct i2c_client *client; - struct regulator_bulk_data supplies[3]; u32 bus_width; struct mutex lock; /* Protects fields below and device registers */ struct hdmi_avi_infoframe hdmi_avi_infoframe; @@ -312,6 +313,7 @@ struct it66121_ctx { u8 swl; bool auto_cts; } audio; + const struct it66121_chip_info *info; }; static const struct regmap_range_cfg it66121_regmap_banks[] = { @@ -342,16 +344,6 @@ static void it66121_hw_reset(struct it66121_ctx *ctx) gpiod_set_value(ctx->gpio_reset, 0); } -static inline int ite66121_power_on(struct it66121_ctx *ctx) -{ - return regulator_bulk_enable(ARRAY_SIZE(ctx->supplies), ctx->supplies); -} - -static inline int ite66121_power_off(struct it66121_ctx *ctx) -{ - return regulator_bulk_disable(ARRAY_SIZE(ctx->supplies), ctx->supplies); -} - static inline int it66121_preamble_ddc(struct it66121_ctx *ctx) { return regmap_write(ctx->regmap, IT66121_MASTER_SEL_REG, IT66121_MASTER_SEL_HOST); @@ -406,16 +398,22 @@ static int it66121_configure_afe(struct it66121_ctx *ctx, ret = regmap_write_bits(ctx->regmap, IT66121_AFE_IP_REG, IT66121_AFE_IP_GAINBIT | - IT66121_AFE_IP_ER0 | - IT66121_AFE_IP_EC1, + IT66121_AFE_IP_ER0, IT66121_AFE_IP_GAINBIT); if (ret) return ret; - ret = regmap_write_bits(ctx->regmap, IT66121_AFE_XP_EC1_REG, - IT66121_AFE_XP_EC1_LOWCLK, 0x80); - if (ret) - return ret; + if (ctx->info->id == ID_IT66121) { + ret = regmap_write_bits(ctx->regmap, IT66121_AFE_IP_REG, + IT66121_AFE_IP_EC1, 0); + if (ret) + return ret; + + ret = regmap_write_bits(ctx->regmap, IT66121_AFE_XP_EC1_REG, + IT66121_AFE_XP_EC1_LOWCLK, 0x80); + if (ret) + return ret; + } } else { ret = regmap_write_bits(ctx->regmap, IT66121_AFE_XP_REG, IT66121_AFE_XP_GAINBIT | @@ -426,17 +424,24 @@ static int it66121_configure_afe(struct it66121_ctx *ctx, ret = regmap_write_bits(ctx->regmap, IT66121_AFE_IP_REG, IT66121_AFE_IP_GAINBIT | - IT66121_AFE_IP_ER0 | - IT66121_AFE_IP_EC1, IT66121_AFE_IP_ER0 | - IT66121_AFE_IP_EC1); + IT66121_AFE_IP_ER0, + IT66121_AFE_IP_ER0); if (ret) return ret; - ret = regmap_write_bits(ctx->regmap, IT66121_AFE_XP_EC1_REG, - IT66121_AFE_XP_EC1_LOWCLK, - IT66121_AFE_XP_EC1_LOWCLK); - if (ret) - return ret; + if (ctx->info->id == ID_IT66121) { + ret = regmap_write_bits(ctx->regmap, IT66121_AFE_IP_REG, + IT66121_AFE_IP_EC1, + IT66121_AFE_IP_EC1); + if (ret) + return ret; + + ret = regmap_write_bits(ctx->regmap, IT66121_AFE_XP_EC1_REG, + IT66121_AFE_XP_EC1_LOWCLK, + IT66121_AFE_XP_EC1_LOWCLK); + if (ret) + return ret; + } } /* Clear reset flags */ @@ -445,38 +450,36 @@ static int it66121_configure_afe(struct it66121_ctx *ctx, if (ret) return ret; + if (ctx->info->id == ID_IT6610) { + ret = regmap_write_bits(ctx->regmap, IT66121_AFE_XP_REG, + IT6610_AFE_XP_BYPASS, + IT6610_AFE_XP_BYPASS); + if (ret) + return ret; + } + return it66121_fire_afe(ctx); } static inline int it66121_wait_ddc_ready(struct it66121_ctx *ctx) { int ret, val; - u32 busy = IT66121_DDC_STATUS_NOACK | IT66121_DDC_STATUS_WAIT_BUS | - IT66121_DDC_STATUS_ARBI_LOSE; + u32 error = IT66121_DDC_STATUS_NOACK | IT66121_DDC_STATUS_WAIT_BUS | + IT66121_DDC_STATUS_ARBI_LOSE; + u32 done = IT66121_DDC_STATUS_TX_DONE; - ret = regmap_read_poll_timeout(ctx->regmap, IT66121_DDC_STATUS_REG, val, true, - IT66121_EDID_SLEEP_US, IT66121_EDID_TIMEOUT_US); + ret = regmap_read_poll_timeout(ctx->regmap, IT66121_DDC_STATUS_REG, val, + val & (error | done), IT66121_EDID_SLEEP_US, + IT66121_EDID_TIMEOUT_US); if (ret) return ret; - if (val & busy) + if (val & error) return -EAGAIN; return 0; } -static int it66121_clear_ddc_fifo(struct it66121_ctx *ctx) -{ - int ret; - - ret = it66121_preamble_ddc(ctx); - if (ret) - return ret; - - return regmap_write(ctx->regmap, IT66121_DDC_COMMAND_REG, - IT66121_DDC_COMMAND_FIFO_CLR); -} - static int it66121_abort_ddc_ops(struct it66121_ctx *ctx) { int ret; @@ -516,7 +519,6 @@ static int it66121_get_edid_block(void *context, u8 *buf, unsigned int block, size_t len) { struct it66121_ctx *ctx = context; - unsigned int val; int remain = len; int offset = 0; int ret, cnt; @@ -524,26 +526,9 @@ static int it66121_get_edid_block(void *context, u8 *buf, offset = (block % 2) * len; block = block / 2; - ret = regmap_read(ctx->regmap, IT66121_INT_STATUS1_REG, &val); - if (ret) - return ret; - - if (val & IT66121_INT_STATUS1_DDC_BUSHANG) { - ret = it66121_abort_ddc_ops(ctx); - if (ret) - return ret; - } - - ret = it66121_clear_ddc_fifo(ctx); - if (ret) - return ret; - while (remain > 0) { cnt = (remain > IT66121_EDID_FIFO_SIZE) ? IT66121_EDID_FIFO_SIZE : remain; - ret = it66121_preamble_ddc(ctx); - if (ret) - return ret; ret = regmap_write(ctx->regmap, IT66121_DDC_COMMAND_REG, IT66121_DDC_COMMAND_FIFO_CLR); @@ -554,25 +539,6 @@ static int it66121_get_edid_block(void *context, u8 *buf, if (ret) return ret; - ret = regmap_read(ctx->regmap, IT66121_INT_STATUS1_REG, &val); - if (ret) - return ret; - - if (val & IT66121_INT_STATUS1_DDC_BUSHANG) { - ret = it66121_abort_ddc_ops(ctx); - if (ret) - return ret; - } - - ret = it66121_preamble_ddc(ctx); - if (ret) - return ret; - - ret = regmap_write(ctx->regmap, IT66121_DDC_HEADER_REG, - IT66121_DDC_HEADER_EDID); - if (ret) - return ret; - ret = regmap_write(ctx->regmap, IT66121_DDC_OFFSET_REG, offset); if (ret) return ret; @@ -593,20 +559,18 @@ static int it66121_get_edid_block(void *context, u8 *buf, offset += cnt; remain -= cnt; - /* Per programming manual, sleep here before emptying the FIFO */ - msleep(20); - ret = it66121_wait_ddc_ready(ctx); + if (ret) { + it66121_abort_ddc_ops(ctx); + return ret; + } + + ret = regmap_noinc_read(ctx->regmap, IT66121_DDC_RD_FIFO_REG, + buf, cnt); if (ret) return ret; - do { - ret = regmap_read(ctx->regmap, IT66121_DDC_RD_FIFO_REG, &val); - if (ret) - return ret; - *(buf++) = val; - cnt--; - } while (cnt > 0); + buf += cnt; } return 0; @@ -635,10 +599,12 @@ static int it66121_bridge_attach(struct drm_bridge *bridge, if (ret) return ret; - ret = regmap_write_bits(ctx->regmap, IT66121_CLK_BANK_REG, - IT66121_CLK_BANK_PWROFF_RCLK, 0); - if (ret) - return ret; + if (ctx->info->id == ID_IT66121) { + ret = regmap_write_bits(ctx->regmap, IT66121_CLK_BANK_REG, + IT66121_CLK_BANK_PWROFF_RCLK, 0); + if (ret) + return ret; + } ret = regmap_write_bits(ctx->regmap, IT66121_INT_REG, IT66121_INT_TX_CLK_OFF, 0); @@ -684,11 +650,7 @@ static int it66121_bridge_attach(struct drm_bridge *bridge, /* Per programming manual, sleep here for bridge to settle */ msleep(50); - /* Start interrupts */ - return regmap_write_bits(ctx->regmap, IT66121_INT_MASK1_REG, - IT66121_INT_MASK1_DDC_NOACK | - IT66121_INT_MASK1_DDC_FIFOERR | - IT66121_INT_MASK1_DDC_BUSHANG, 0); + return 0; } static int it66121_set_mute(struct it66121_ctx *ctx, bool mute) @@ -780,29 +742,32 @@ static void it66121_bridge_disable(struct drm_bridge *bridge, ctx->connector = NULL; } +static int it66121_bridge_check(struct drm_bridge *bridge, + struct drm_bridge_state *bridge_state, + struct drm_crtc_state *crtc_state, + struct drm_connector_state *conn_state) +{ + struct it66121_ctx *ctx = container_of(bridge, struct it66121_ctx, bridge); + + if (ctx->info->id == ID_IT6610) { + /* The IT6610 only supports these settings */ + bridge_state->input_bus_cfg.flags |= DRM_BUS_FLAG_DE_HIGH | + DRM_BUS_FLAG_PIXDATA_DRIVE_NEGEDGE; + bridge_state->input_bus_cfg.flags &= + ~DRM_BUS_FLAG_PIXDATA_DRIVE_POSEDGE; + } + + return 0; +} + static void it66121_bridge_mode_set(struct drm_bridge *bridge, const struct drm_display_mode *mode, const struct drm_display_mode *adjusted_mode) { - int ret, i; u8 buf[HDMI_INFOFRAME_SIZE(AVI)]; struct it66121_ctx *ctx = container_of(bridge, struct it66121_ctx, bridge); - const u16 aviinfo_reg[HDMI_AVI_INFOFRAME_SIZE] = { - IT66121_AVIINFO_DB1_REG, - IT66121_AVIINFO_DB2_REG, - IT66121_AVIINFO_DB3_REG, - IT66121_AVIINFO_DB4_REG, - IT66121_AVIINFO_DB5_REG, - IT66121_AVIINFO_DB6_REG, - IT66121_AVIINFO_DB7_REG, - IT66121_AVIINFO_DB8_REG, - IT66121_AVIINFO_DB9_REG, - IT66121_AVIINFO_DB10_REG, - IT66121_AVIINFO_DB11_REG, - IT66121_AVIINFO_DB12_REG, - IT66121_AVIINFO_DB13_REG - }; + int ret; mutex_lock(&ctx->lock); @@ -822,10 +787,12 @@ void it66121_bridge_mode_set(struct drm_bridge *bridge, } /* Write new AVI infoframe packet */ - for (i = 0; i < HDMI_AVI_INFOFRAME_SIZE; i++) { - if (regmap_write(ctx->regmap, aviinfo_reg[i], buf[i + HDMI_INFOFRAME_HEADER_SIZE])) - goto unlock; - } + ret = regmap_bulk_write(ctx->regmap, IT66121_AVIINFO_DB1_REG, + &buf[HDMI_INFOFRAME_HEADER_SIZE], + HDMI_AVI_INFOFRAME_SIZE); + if (ret) + goto unlock; + if (regmap_write(ctx->regmap, IT66121_AVIINFO_CSUM_REG, buf[3])) goto unlock; @@ -838,9 +805,12 @@ void it66121_bridge_mode_set(struct drm_bridge *bridge, if (regmap_write(ctx->regmap, IT66121_HDMI_MODE_REG, IT66121_HDMI_MODE_HDMI)) goto unlock; - if (regmap_write_bits(ctx->regmap, IT66121_CLK_BANK_REG, - IT66121_CLK_BANK_PWROFF_TXCLK, IT66121_CLK_BANK_PWROFF_TXCLK)) + if (ctx->info->id == ID_IT66121 && + regmap_write_bits(ctx->regmap, IT66121_CLK_BANK_REG, + IT66121_CLK_BANK_PWROFF_TXCLK, + IT66121_CLK_BANK_PWROFF_TXCLK)) { goto unlock; + } if (it66121_configure_input(ctx)) goto unlock; @@ -848,7 +818,11 @@ void it66121_bridge_mode_set(struct drm_bridge *bridge, if (it66121_configure_afe(ctx, adjusted_mode)) goto unlock; - regmap_write_bits(ctx->regmap, IT66121_CLK_BANK_REG, IT66121_CLK_BANK_PWROFF_TXCLK, 0); + if (ctx->info->id == ID_IT66121 && + regmap_write_bits(ctx->regmap, IT66121_CLK_BANK_REG, + IT66121_CLK_BANK_PWROFF_TXCLK, 0)) { + goto unlock; + } unlock: mutex_unlock(&ctx->lock); @@ -906,9 +880,25 @@ static struct edid *it66121_bridge_get_edid(struct drm_bridge *bridge, { struct it66121_ctx *ctx = container_of(bridge, struct it66121_ctx, bridge); struct edid *edid; + int ret; mutex_lock(&ctx->lock); + ret = it66121_preamble_ddc(ctx); + if (ret) { + edid = ERR_PTR(ret); + goto out_unlock; + } + + ret = regmap_write(ctx->regmap, IT66121_DDC_HEADER_REG, + IT66121_DDC_HEADER_EDID); + if (ret) { + edid = ERR_PTR(ret); + goto out_unlock; + } + edid = drm_do_get_edid(connector, it66121_get_edid_block, ctx); + +out_unlock: mutex_unlock(&ctx->lock); return edid; @@ -923,6 +913,7 @@ static const struct drm_bridge_funcs it66121_bridge_funcs = { .atomic_get_input_bus_fmts = it66121_bridge_atomic_get_input_bus_fmts, .atomic_enable = it66121_bridge_enable, .atomic_disable = it66121_bridge_disable, + .atomic_check = it66121_bridge_check, .mode_set = it66121_bridge_mode_set, .mode_valid = it66121_bridge_mode_valid, .detect = it66121_bridge_detect, @@ -952,21 +943,14 @@ static irqreturn_t it66121_irq_threaded_handler(int irq, void *dev_id) ret = regmap_read(ctx->regmap, IT66121_INT_STATUS1_REG, &val); if (ret) { dev_err(dev, "Cannot read STATUS1_REG %d\n", ret); - } else { - if (val & IT66121_INT_STATUS1_DDC_FIFOERR) - it66121_clear_ddc_fifo(ctx); - if (val & (IT66121_INT_STATUS1_DDC_BUSHANG | - IT66121_INT_STATUS1_DDC_NOACK)) - it66121_abort_ddc_ops(ctx); - if (val & IT66121_INT_STATUS1_HPD_STATUS) { - regmap_write_bits(ctx->regmap, IT66121_INT_CLR1_REG, - IT66121_INT_CLR1_HPD, IT66121_INT_CLR1_HPD); + } else if (val & IT66121_INT_STATUS1_HPD_STATUS) { + regmap_write_bits(ctx->regmap, IT66121_INT_CLR1_REG, + IT66121_INT_CLR1_HPD, IT66121_INT_CLR1_HPD); - status = it66121_is_hpd_detect(ctx) ? connector_status_connected - : connector_status_disconnected; + status = it66121_is_hpd_detect(ctx) ? connector_status_connected + : connector_status_disconnected; - event = true; - } + event = true; } regmap_write_bits(ctx->regmap, IT66121_SYS_STATUS_REG, @@ -1512,9 +1496,13 @@ static int it66121_audio_codec_init(struct it66121_ctx *ctx, struct device *dev) return PTR_ERR_OR_ZERO(ctx->audio.pdev); } -static int it66121_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static const char * const it66121_supplies[] = { + "vcn33", "vcn18", "vrf12" +}; + +static int it66121_probe(struct i2c_client *client) { + const struct i2c_device_id *id = i2c_client_get_device_id(client); u32 revision_id, vendor_ids[2] = { 0 }, device_ids[2] = { 0 }; struct device_node *ep; int ret; @@ -1536,6 +1524,7 @@ static int it66121_probe(struct i2c_client *client, ctx->dev = dev; ctx->client = client; + ctx->info = (const struct it66121_chip_info *) id->driver_data; of_property_read_u32(ep, "bus-width", &ctx->bus_width); of_node_put(ep); @@ -1565,26 +1554,18 @@ static int it66121_probe(struct i2c_client *client, i2c_set_clientdata(client, ctx); mutex_init(&ctx->lock); - ctx->supplies[0].supply = "vcn33"; - ctx->supplies[1].supply = "vcn18"; - ctx->supplies[2].supply = "vrf12"; - ret = devm_regulator_bulk_get(ctx->dev, 3, ctx->supplies); + ret = devm_regulator_bulk_get_enable(dev, ARRAY_SIZE(it66121_supplies), + it66121_supplies); if (ret) { - dev_err(ctx->dev, "regulator_bulk failed\n"); + dev_err(dev, "Failed to enable power supplies\n"); return ret; } - ret = ite66121_power_on(ctx); - if (ret) - return ret; - it66121_hw_reset(ctx); ctx->regmap = devm_regmap_init_i2c(client, &it66121_regmap_config); - if (IS_ERR(ctx->regmap)) { - ite66121_power_off(ctx); + if (IS_ERR(ctx->regmap)) return PTR_ERR(ctx->regmap); - } regmap_read(ctx->regmap, IT66121_VENDOR_ID0_REG, &vendor_ids[0]); regmap_read(ctx->regmap, IT66121_VENDOR_ID1_REG, &vendor_ids[1]); @@ -1595,9 +1576,8 @@ static int it66121_probe(struct i2c_client *client, revision_id = FIELD_GET(IT66121_REVISION_MASK, device_ids[1]); device_ids[1] &= IT66121_DEVICE_ID1_MASK; - if (vendor_ids[0] != IT66121_VENDOR_ID0 || vendor_ids[1] != IT66121_VENDOR_ID1 || - device_ids[0] != IT66121_DEVICE_ID0 || device_ids[1] != IT66121_DEVICE_ID1) { - ite66121_power_off(ctx); + if ((vendor_ids[1] << 8 | vendor_ids[0]) != ctx->info->vid || + (device_ids[1] << 8 | device_ids[0]) != ctx->info->pid) { return -ENODEV; } @@ -1610,7 +1590,6 @@ static int it66121_probe(struct i2c_client *client, IRQF_ONESHOT, dev_name(dev), ctx); if (ret < 0) { dev_err(dev, "Failed to request irq %d:%d\n", client->irq, ret); - ite66121_power_off(ctx); return ret; } @@ -1627,19 +1606,32 @@ static void it66121_remove(struct i2c_client *client) { struct it66121_ctx *ctx = i2c_get_clientdata(client); - ite66121_power_off(ctx); drm_bridge_remove(&ctx->bridge); mutex_destroy(&ctx->lock); } static const struct of_device_id it66121_dt_match[] = { { .compatible = "ite,it66121" }, + { .compatible = "ite,it6610" }, { } }; MODULE_DEVICE_TABLE(of, it66121_dt_match); +static const struct it66121_chip_info it66121_chip_info = { + .id = ID_IT66121, + .vid = 0x4954, + .pid = 0x0612, +}; + +static const struct it66121_chip_info it6610_chip_info = { + .id = ID_IT6610, + .vid = 0xca00, + .pid = 0x0611, +}; + static const struct i2c_device_id it66121_id[] = { - { "it66121", 0 }, + { "it66121", (kernel_ulong_t) &it66121_chip_info }, + { "it6610", (kernel_ulong_t) &it6610_chip_info }, { } }; MODULE_DEVICE_TABLE(i2c, it66121_id); @@ -1649,7 +1641,7 @@ static struct i2c_driver it66121_driver = { .name = "it66121", .of_match_table = it66121_dt_match, }, - .probe = it66121_probe, + .probe_new = it66121_probe, .remove = it66121_remove, .id_table = it66121_id, }; diff --git a/drivers/gpu/drm/bridge/lontium-lt8912b.c b/drivers/gpu/drm/bridge/lontium-lt8912b.c index a98efef0ba0e..2019a8167d69 100644 --- a/drivers/gpu/drm/bridge/lontium-lt8912b.c +++ b/drivers/gpu/drm/bridge/lontium-lt8912b.c @@ -517,14 +517,27 @@ static int lt8912_attach_dsi(struct lt8912 *lt) return 0; } +static void lt8912_bridge_hpd_cb(void *data, enum drm_connector_status status) +{ + struct lt8912 *lt = data; + + if (lt->bridge.dev) + drm_helper_hpd_irq_event(lt->bridge.dev); +} + static int lt8912_bridge_connector_init(struct drm_bridge *bridge) { int ret; struct lt8912 *lt = bridge_to_lt8912(bridge); struct drm_connector *connector = <->connector; - connector->polled = DRM_CONNECTOR_POLL_CONNECT | - DRM_CONNECTOR_POLL_DISCONNECT; + if (lt->hdmi_port->ops & DRM_BRIDGE_OP_HPD) { + drm_bridge_hpd_enable(lt->hdmi_port, lt8912_bridge_hpd_cb, lt); + connector->polled = DRM_CONNECTOR_POLL_HPD; + } else { + connector->polled = DRM_CONNECTOR_POLL_CONNECT | + DRM_CONNECTOR_POLL_DISCONNECT; + } ret = drm_connector_init(bridge->dev, connector, <8912_connector_funcs, @@ -578,6 +591,10 @@ static void lt8912_bridge_detach(struct drm_bridge *bridge) if (lt->is_attached) { lt8912_hard_power_off(lt); + + if (lt->hdmi_port->ops & DRM_BRIDGE_OP_HPD) + drm_bridge_hpd_disable(lt->hdmi_port); + drm_connector_unregister(<->connector); drm_connector_cleanup(<->connector); } @@ -685,8 +702,7 @@ static int lt8912_put_dt(struct lt8912 *lt) return 0; } -static int lt8912_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int lt8912_probe(struct i2c_client *client) { static struct lt8912 *lt; int ret = 0; @@ -758,7 +774,7 @@ static struct i2c_driver lt8912_i2c_driver = { .name = "lt8912", .of_match_table = lt8912_dt_match, }, - .probe = lt8912_probe, + .probe_new = lt8912_probe, .remove = lt8912_remove, .id_table = lt8912_id, }; diff --git a/drivers/gpu/drm/bridge/lontium-lt9211.c b/drivers/gpu/drm/bridge/lontium-lt9211.c index 933ca028d612..3e19fff6547a 100644 --- a/drivers/gpu/drm/bridge/lontium-lt9211.c +++ b/drivers/gpu/drm/bridge/lontium-lt9211.c @@ -720,8 +720,7 @@ static int lt9211_host_attach(struct lt9211 *ctx) return 0; } -static int lt9211_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int lt9211_probe(struct i2c_client *client) { struct device *dev = &client->dev; struct lt9211 *ctx; @@ -786,7 +785,7 @@ static const struct of_device_id lt9211_match_table[] = { MODULE_DEVICE_TABLE(of, lt9211_match_table); static struct i2c_driver lt9211_driver = { - .probe = lt9211_probe, + .probe_new = lt9211_probe, .remove = lt9211_remove, .id_table = lt9211_id, .driver = { diff --git a/drivers/gpu/drm/bridge/lontium-lt9611.c b/drivers/gpu/drm/bridge/lontium-lt9611.c index 7c0a99173b39..3ce4e495aee5 100644 --- a/drivers/gpu/drm/bridge/lontium-lt9611.c +++ b/drivers/gpu/drm/bridge/lontium-lt9611.c @@ -259,6 +259,7 @@ static int lt9611_pll_setup(struct lt9611 *lt9611, const struct drm_display_mode { 0x8126, 0x55 }, { 0x8127, 0x66 }, { 0x8128, 0x88 }, + { 0x812a, 0x20 }, }; regmap_multi_reg_write(lt9611->regmap, reg_cfg, ARRAY_SIZE(reg_cfg)); @@ -1108,8 +1109,7 @@ static void lt9611_audio_exit(struct lt9611 *lt9611) } } -static int lt9611_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int lt9611_probe(struct i2c_client *client) { struct lt9611 *lt9611; struct device *dev = &client->dev; @@ -1248,7 +1248,7 @@ static struct i2c_driver lt9611_driver = { .name = "lt9611", .of_match_table = lt9611_match_table, }, - .probe = lt9611_probe, + .probe_new = lt9611_probe, .remove = lt9611_remove, .id_table = lt9611_id, }; diff --git a/drivers/gpu/drm/bridge/lontium-lt9611uxc.c b/drivers/gpu/drm/bridge/lontium-lt9611uxc.c index fa1ee6264d92..583daacf3705 100644 --- a/drivers/gpu/drm/bridge/lontium-lt9611uxc.c +++ b/drivers/gpu/drm/bridge/lontium-lt9611uxc.c @@ -844,8 +844,7 @@ static const struct attribute_group *lt9611uxc_attr_groups[] = { NULL, }; -static int lt9611uxc_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int lt9611uxc_probe(struct i2c_client *client) { struct lt9611uxc *lt9611uxc; struct device *dev = &client->dev; @@ -1012,7 +1011,7 @@ static struct i2c_driver lt9611uxc_driver = { .of_match_table = lt9611uxc_match_table, .dev_groups = lt9611uxc_attr_groups, }, - .probe = lt9611uxc_probe, + .probe_new = lt9611uxc_probe, .remove = lt9611uxc_remove, .id_table = lt9611uxc_id, }; diff --git a/drivers/gpu/drm/bridge/megachips-stdpxxxx-ge-b850v3-fw.c b/drivers/gpu/drm/bridge/megachips-stdpxxxx-ge-b850v3-fw.c index 97359f807bfc..4fc494d9084b 100644 --- a/drivers/gpu/drm/bridge/megachips-stdpxxxx-ge-b850v3-fw.c +++ b/drivers/gpu/drm/bridge/megachips-stdpxxxx-ge-b850v3-fw.c @@ -336,8 +336,7 @@ static int ge_b850v3_register(void) "ge-b850v3-lvds-dp", ge_b850v3_lvds_ptr); } -static int stdp4028_ge_b850v3_fw_probe(struct i2c_client *stdp4028_i2c, - const struct i2c_device_id *id) +static int stdp4028_ge_b850v3_fw_probe(struct i2c_client *stdp4028_i2c) { struct device *dev = &stdp4028_i2c->dev; int ret; @@ -376,7 +375,7 @@ MODULE_DEVICE_TABLE(of, stdp4028_ge_b850v3_fw_match); static struct i2c_driver stdp4028_ge_b850v3_fw_driver = { .id_table = stdp4028_ge_b850v3_fw_i2c_table, - .probe = stdp4028_ge_b850v3_fw_probe, + .probe_new = stdp4028_ge_b850v3_fw_probe, .remove = stdp4028_ge_b850v3_fw_remove, .driver = { .name = "stdp4028-ge-b850v3-fw", @@ -384,8 +383,7 @@ static struct i2c_driver stdp4028_ge_b850v3_fw_driver = { }, }; -static int stdp2690_ge_b850v3_fw_probe(struct i2c_client *stdp2690_i2c, - const struct i2c_device_id *id) +static int stdp2690_ge_b850v3_fw_probe(struct i2c_client *stdp2690_i2c) { struct device *dev = &stdp2690_i2c->dev; int ret; @@ -424,7 +422,7 @@ MODULE_DEVICE_TABLE(of, stdp2690_ge_b850v3_fw_match); static struct i2c_driver stdp2690_ge_b850v3_fw_driver = { .id_table = stdp2690_ge_b850v3_fw_i2c_table, - .probe = stdp2690_ge_b850v3_fw_probe, + .probe_new = stdp2690_ge_b850v3_fw_probe, .remove = stdp2690_ge_b850v3_fw_remove, .driver = { .name = "stdp2690-ge-b850v3-fw", @@ -440,7 +438,11 @@ static int __init stdpxxxx_ge_b850v3_init(void) if (ret) return ret; - return i2c_add_driver(&stdp2690_ge_b850v3_fw_driver); + ret = i2c_add_driver(&stdp2690_ge_b850v3_fw_driver); + if (ret) + i2c_del_driver(&stdp4028_ge_b850v3_fw_driver); + + return ret; } module_init(stdpxxxx_ge_b850v3_init); diff --git a/drivers/gpu/drm/bridge/nxp-ptn3460.c b/drivers/gpu/drm/bridge/nxp-ptn3460.c index 0851101a8c72..cd292a2f894c 100644 --- a/drivers/gpu/drm/bridge/nxp-ptn3460.c +++ b/drivers/gpu/drm/bridge/nxp-ptn3460.c @@ -257,8 +257,7 @@ static const struct drm_bridge_funcs ptn3460_bridge_funcs = { .get_edid = ptn3460_get_edid, }; -static int ptn3460_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int ptn3460_probe(struct i2c_client *client) { struct device *dev = &client->dev; struct ptn3460_bridge *ptn_bridge; @@ -336,7 +335,7 @@ MODULE_DEVICE_TABLE(of, ptn3460_match); static struct i2c_driver ptn3460_driver = { .id_table = ptn3460_i2c_table, - .probe = ptn3460_probe, + .probe_new = ptn3460_probe, .remove = ptn3460_remove, .driver = { .name = "nxp,ptn3460", diff --git a/drivers/gpu/drm/bridge/panel.c b/drivers/gpu/drm/bridge/panel.c index 216af76d0042..1708098fba6d 100644 --- a/drivers/gpu/drm/bridge/panel.c +++ b/drivers/gpu/drm/bridge/panel.c @@ -364,6 +364,8 @@ struct drm_bridge *devm_drm_panel_bridge_add_typed(struct device *dev, devres_free(ptr); } + bridge->pre_enable_prev_first = panel->prepare_prev_first; + return bridge; } EXPORT_SYMBOL(devm_drm_panel_bridge_add_typed); @@ -402,6 +404,8 @@ struct drm_bridge *drmm_panel_bridge_add(struct drm_device *drm, if (ret) return ERR_PTR(ret); + bridge->pre_enable_prev_first = panel->prepare_prev_first; + return bridge; } EXPORT_SYMBOL(drmm_panel_bridge_add); diff --git a/drivers/gpu/drm/bridge/parade-ps8622.c b/drivers/gpu/drm/bridge/parade-ps8622.c index 309de802863d..530ee6a19e7e 100644 --- a/drivers/gpu/drm/bridge/parade-ps8622.c +++ b/drivers/gpu/drm/bridge/parade-ps8622.c @@ -442,9 +442,9 @@ static const struct of_device_id ps8622_devices[] = { }; MODULE_DEVICE_TABLE(of, ps8622_devices); -static int ps8622_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int ps8622_probe(struct i2c_client *client) { + const struct i2c_device_id *id = i2c_client_get_device_id(client); struct device *dev = &client->dev; struct ps8622_bridge *ps8622; struct drm_bridge *panel_bridge; @@ -538,7 +538,7 @@ MODULE_DEVICE_TABLE(i2c, ps8622_i2c_table); static struct i2c_driver ps8622_driver = { .id_table = ps8622_i2c_table, - .probe = ps8622_probe, + .probe_new = ps8622_probe, .remove = ps8622_remove, .driver = { .name = "ps8622", diff --git a/drivers/gpu/drm/bridge/parade-ps8640.c b/drivers/gpu/drm/bridge/parade-ps8640.c index 6a614e54b383..4b361d7d5e44 100644 --- a/drivers/gpu/drm/bridge/parade-ps8640.c +++ b/drivers/gpu/drm/bridge/parade-ps8640.c @@ -15,6 +15,7 @@ #include <drm/display/drm_dp_aux_bus.h> #include <drm/display/drm_dp_helper.h> +#include <drm/drm_atomic_state_helper.h> #include <drm/drm_bridge.h> #include <drm/drm_edid.h> #include <drm/drm_mipi_dsi.h> @@ -442,7 +443,8 @@ static const struct dev_pm_ops ps8640_pm_ops = { pm_runtime_force_resume) }; -static void ps8640_pre_enable(struct drm_bridge *bridge) +static void ps8640_atomic_pre_enable(struct drm_bridge *bridge, + struct drm_bridge_state *old_bridge_state) { struct ps8640 *ps_bridge = bridge_to_ps8640(bridge); struct regmap *map = ps_bridge->regmap[PAGE2_TOP_CNTL]; @@ -476,7 +478,8 @@ static void ps8640_pre_enable(struct drm_bridge *bridge) ps_bridge->pre_enabled = true; } -static void ps8640_post_disable(struct drm_bridge *bridge) +static void ps8640_atomic_post_disable(struct drm_bridge *bridge, + struct drm_bridge_state *old_bridge_state) { struct ps8640 *ps_bridge = bridge_to_ps8640(bridge); @@ -554,7 +557,7 @@ static struct edid *ps8640_bridge_get_edid(struct drm_bridge *bridge, * EDID, for this chip, we need to do a full poweron, otherwise it will * fail. */ - drm_bridge_chain_pre_enable(bridge); + drm_atomic_bridge_chain_pre_enable(bridge, connector->state->state); edid = drm_get_edid(connector, ps_bridge->page[PAGE0_DP_CNTL]->adapter); @@ -564,7 +567,7 @@ static struct edid *ps8640_bridge_get_edid(struct drm_bridge *bridge, * before, return the chip to its original power state. */ if (poweroff) - drm_bridge_chain_post_disable(bridge); + drm_atomic_bridge_chain_post_disable(bridge, connector->state->state); return edid; } @@ -579,8 +582,11 @@ static const struct drm_bridge_funcs ps8640_bridge_funcs = { .attach = ps8640_bridge_attach, .detach = ps8640_bridge_detach, .get_edid = ps8640_bridge_get_edid, - .post_disable = ps8640_post_disable, - .pre_enable = ps8640_pre_enable, + .atomic_post_disable = ps8640_atomic_post_disable, + .atomic_pre_enable = ps8640_atomic_pre_enable, + .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, }; static int ps8640_bridge_get_dsi_resources(struct device *dev, struct ps8640 *ps_bridge) @@ -734,13 +740,13 @@ static int ps8640_probe(struct i2c_client *client) pm_runtime_enable(dev); /* * Powering on ps8640 takes ~300ms. To avoid wasting time on power - * cycling ps8640 too often, set autosuspend_delay to 1000ms to ensure + * cycling ps8640 too often, set autosuspend_delay to 2000ms to ensure * the bridge wouldn't suspend in between each _aux_transfer_msg() call * during EDID read (~20ms in my experiment) and in between the last * _aux_transfer_msg() call during EDID read and the _pre_enable() call * (~100ms in my experiment). */ - pm_runtime_set_autosuspend_delay(dev, 1000); + pm_runtime_set_autosuspend_delay(dev, 2000); pm_runtime_use_autosuspend(dev); pm_suspend_ignore_children(dev, true); ret = devm_add_action_or_reset(dev, ps8640_runtime_disable, dev); diff --git a/drivers/gpu/drm/bridge/sii902x.c b/drivers/gpu/drm/bridge/sii902x.c index 878fb7d3732b..d212ff7f7a87 100644 --- a/drivers/gpu/drm/bridge/sii902x.c +++ b/drivers/gpu/drm/bridge/sii902x.c @@ -171,7 +171,6 @@ struct sii902x { struct drm_connector connector; struct gpio_desc *reset_gpio; struct i2c_mux_core *i2cmux; - struct regulator_bulk_data supplies[2]; bool sink_is_hdmi; /* * Mutex protects audio and video functions from interfering @@ -1066,12 +1065,12 @@ static int sii902x_init(struct sii902x *sii902x) return i2c_mux_add_adapter(sii902x->i2cmux, 0, 0, 0); } -static int sii902x_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int sii902x_probe(struct i2c_client *client) { struct device *dev = &client->dev; struct device_node *endpoint; struct sii902x *sii902x; + static const char * const supplies[] = {"iovcc", "cvcc12"}; int ret; ret = i2c_check_functionality(client->adapter, @@ -1122,27 +1121,11 @@ static int sii902x_probe(struct i2c_client *client, mutex_init(&sii902x->mutex); - sii902x->supplies[0].supply = "iovcc"; - sii902x->supplies[1].supply = "cvcc12"; - ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(sii902x->supplies), - sii902x->supplies); + ret = devm_regulator_bulk_get_enable(dev, ARRAY_SIZE(supplies), supplies); if (ret < 0) - return ret; - - ret = regulator_bulk_enable(ARRAY_SIZE(sii902x->supplies), - sii902x->supplies); - if (ret < 0) { - dev_err_probe(dev, ret, "Failed to enable supplies"); - return ret; - } + return dev_err_probe(dev, ret, "Failed to enable supplies"); - ret = sii902x_init(sii902x); - if (ret < 0) { - regulator_bulk_disable(ARRAY_SIZE(sii902x->supplies), - sii902x->supplies); - } - - return ret; + return sii902x_init(sii902x); } static void sii902x_remove(struct i2c_client *client) @@ -1152,8 +1135,6 @@ static void sii902x_remove(struct i2c_client *client) i2c_mux_del_adapters(sii902x->i2cmux); drm_bridge_remove(&sii902x->bridge); - regulator_bulk_disable(ARRAY_SIZE(sii902x->supplies), - sii902x->supplies); } static const struct of_device_id sii902x_dt_ids[] = { @@ -1169,7 +1150,7 @@ static const struct i2c_device_id sii902x_i2c_ids[] = { MODULE_DEVICE_TABLE(i2c, sii902x_i2c_ids); static struct i2c_driver sii902x_driver = { - .probe = sii902x_probe, + .probe_new = sii902x_probe, .remove = sii902x_remove, .driver = { .name = "sii902x", diff --git a/drivers/gpu/drm/bridge/sii9234.c b/drivers/gpu/drm/bridge/sii9234.c index 5b3061d4b5c3..099b510ff285 100644 --- a/drivers/gpu/drm/bridge/sii9234.c +++ b/drivers/gpu/drm/bridge/sii9234.c @@ -886,8 +886,7 @@ static const struct drm_bridge_funcs sii9234_bridge_funcs = { .mode_valid = sii9234_mode_valid, }; -static int sii9234_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int sii9234_probe(struct i2c_client *client) { struct i2c_adapter *adapter = client->adapter; struct sii9234 *ctx; @@ -961,7 +960,7 @@ static struct i2c_driver sii9234_driver = { .name = "sii9234", .of_match_table = sii9234_dt_match, }, - .probe = sii9234_probe, + .probe_new = sii9234_probe, .remove = sii9234_remove, .id_table = sii9234_id, }; diff --git a/drivers/gpu/drm/bridge/sil-sii8620.c b/drivers/gpu/drm/bridge/sil-sii8620.c index 511982a1cedb..b96d03cd878d 100644 --- a/drivers/gpu/drm/bridge/sil-sii8620.c +++ b/drivers/gpu/drm/bridge/sil-sii8620.c @@ -2284,8 +2284,7 @@ static const struct drm_bridge_funcs sii8620_bridge_funcs = { .mode_valid = sii8620_mode_valid, }; -static int sii8620_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int sii8620_probe(struct i2c_client *client) { struct device *dev = &client->dev; struct sii8620 *ctx; @@ -2379,7 +2378,7 @@ static struct i2c_driver sii8620_driver = { .name = "sii8620", .of_match_table = of_match_ptr(sii8620_dt_match), }, - .probe = sii8620_probe, + .probe_new = sii8620_probe, .remove = sii8620_remove, .id_table = sii8620_id, }; diff --git a/drivers/gpu/drm/bridge/tc358767.c b/drivers/gpu/drm/bridge/tc358767.c index 2a58eb271f70..a4725efe812d 100644 --- a/drivers/gpu/drm/bridge/tc358767.c +++ b/drivers/gpu/drm/bridge/tc358767.c @@ -2029,7 +2029,7 @@ static void tc_clk_disable(void *data) clk_disable_unprepare(refclk); } -static int tc_probe(struct i2c_client *client, const struct i2c_device_id *id) +static int tc_probe(struct i2c_client *client) { struct device *dev = &client->dev; struct tc_data *tc; @@ -2209,7 +2209,7 @@ static struct i2c_driver tc358767_driver = { .of_match_table = tc358767_of_ids, }, .id_table = tc358767_i2c_ids, - .probe = tc_probe, + .probe_new = tc_probe, .remove = tc_remove, }; module_i2c_driver(tc358767_driver); diff --git a/drivers/gpu/drm/bridge/tc358768.c b/drivers/gpu/drm/bridge/tc358768.c index 4c4b77ce8aba..839b8832b9b5 100644 --- a/drivers/gpu/drm/bridge/tc358768.c +++ b/drivers/gpu/drm/bridge/tc358768.c @@ -1018,8 +1018,7 @@ static int tc358768_get_regulators(struct tc358768_priv *priv) return ret; } -static int tc358768_i2c_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int tc358768_i2c_probe(struct i2c_client *client) { struct tc358768_priv *priv; struct device *dev = &client->dev; @@ -1085,7 +1084,7 @@ static struct i2c_driver tc358768_driver = { .of_match_table = tc358768_of_ids, }, .id_table = tc358768_i2c_ids, - .probe = tc358768_i2c_probe, + .probe_new = tc358768_i2c_probe, .remove = tc358768_i2c_remove, }; module_i2c_driver(tc358768_driver); diff --git a/drivers/gpu/drm/bridge/tc358775.c b/drivers/gpu/drm/bridge/tc358775.c index 3ceb0e9f9bdc..91b5e1207c47 100644 --- a/drivers/gpu/drm/bridge/tc358775.c +++ b/drivers/gpu/drm/bridge/tc358775.c @@ -637,7 +637,7 @@ static int tc_attach_host(struct tc_data *tc) return 0; } -static int tc_probe(struct i2c_client *client, const struct i2c_device_id *id) +static int tc_probe(struct i2c_client *client) { struct device *dev = &client->dev; struct tc_data *tc; @@ -729,7 +729,7 @@ static struct i2c_driver tc358775_driver = { .of_match_table = tc358775_of_ids, }, .id_table = tc358775_i2c_ids, - .probe = tc_probe, + .probe_new = tc_probe, .remove = tc_remove, }; module_i2c_driver(tc358775_driver); diff --git a/drivers/gpu/drm/bridge/ti-sn65dsi83.c b/drivers/gpu/drm/bridge/ti-sn65dsi83.c index 7ba9467fff12..91ecfbe45bf9 100644 --- a/drivers/gpu/drm/bridge/ti-sn65dsi83.c +++ b/drivers/gpu/drm/bridge/ti-sn65dsi83.c @@ -346,7 +346,7 @@ static void sn65dsi83_atomic_enable(struct drm_bridge *bridge, /* Deassert reset */ gpiod_set_value_cansleep(ctx->enable_gpio, 1); - usleep_range(1000, 1100); + usleep_range(10000, 11000); /* Get the LVDS format from the bridge state. */ bridge_state = drm_atomic_get_new_bridge_state(state, bridge); @@ -653,9 +653,9 @@ static int sn65dsi83_host_attach(struct sn65dsi83 *ctx) return 0; } -static int sn65dsi83_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int sn65dsi83_probe(struct i2c_client *client) { + const struct i2c_device_id *id = i2c_client_get_device_id(client); struct device *dev = &client->dev; enum sn65dsi83_model model; struct sn65dsi83 *ctx; @@ -730,7 +730,7 @@ static const struct of_device_id sn65dsi83_match_table[] = { MODULE_DEVICE_TABLE(of, sn65dsi83_match_table); static struct i2c_driver sn65dsi83_driver = { - .probe = sn65dsi83_probe, + .probe_new = sn65dsi83_probe, .remove = sn65dsi83_remove, .id_table = sn65dsi83_id, .driver = { diff --git a/drivers/gpu/drm/bridge/ti-sn65dsi86.c b/drivers/gpu/drm/bridge/ti-sn65dsi86.c index 05f8756d1aaf..1e26fa63845a 100644 --- a/drivers/gpu/drm/bridge/ti-sn65dsi86.c +++ b/drivers/gpu/drm/bridge/ti-sn65dsi86.c @@ -1852,8 +1852,7 @@ static int ti_sn65dsi86_parse_regulators(struct ti_sn65dsi86 *pdata) pdata->supplies); } -static int ti_sn65dsi86_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int ti_sn65dsi86_probe(struct i2c_client *client) { struct device *dev = &client->dev; struct ti_sn65dsi86 *pdata; @@ -1952,7 +1951,7 @@ static struct i2c_driver ti_sn65dsi86_driver = { .of_match_table = ti_sn65dsi86_match_table, .pm = &ti_sn65dsi86_pm_ops, }, - .probe = ti_sn65dsi86_probe, + .probe_new = ti_sn65dsi86_probe, .id_table = ti_sn65dsi86_id, }; diff --git a/drivers/gpu/drm/bridge/ti-tfp410.c b/drivers/gpu/drm/bridge/ti-tfp410.c index b9635abbad16..6db69df0e18b 100644 --- a/drivers/gpu/drm/bridge/ti-tfp410.c +++ b/drivers/gpu/drm/bridge/ti-tfp410.c @@ -379,8 +379,7 @@ static struct platform_driver tfp410_platform_driver = { #if IS_ENABLED(CONFIG_I2C) /* There is currently no i2c functionality. */ -static int tfp410_i2c_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int tfp410_i2c_probe(struct i2c_client *client) { int reg; @@ -411,7 +410,7 @@ static struct i2c_driver tfp410_i2c_driver = { .of_match_table = of_match_ptr(tfp410_match), }, .id_table = tfp410_i2c_ids, - .probe = tfp410_i2c_probe, + .probe_new = tfp410_i2c_probe, .remove = tfp410_i2c_remove, }; #endif /* IS_ENABLED(CONFIG_I2C) */ diff --git a/drivers/gpu/drm/display/drm_dp_mst_topology.c b/drivers/gpu/drm/display/drm_dp_mst_topology.c index 51a46689cda7..5861b0a6247b 100644 --- a/drivers/gpu/drm/display/drm_dp_mst_topology.c +++ b/drivers/gpu/drm/display/drm_dp_mst_topology.c @@ -3309,8 +3309,13 @@ int drm_dp_add_payload_part1(struct drm_dp_mst_topology_mgr *mgr, int ret; port = drm_dp_mst_topology_get_port_validated(mgr, payload->port); - if (!port) + if (!port) { + drm_dbg_kms(mgr->dev, + "VCPI %d for port %p not in topology, not creating a payload\n", + payload->vcpi, payload->port); + payload->vc_start_slot = -1; return 0; + } if (mgr->payload_count == 0) mgr->next_start_slot = mst_state->start_slot; @@ -3641,6 +3646,9 @@ int drm_dp_mst_topology_mgr_set_mst(struct drm_dp_mst_topology_mgr *mgr, bool ms drm_dp_dpcd_writeb(mgr->aux, DP_MSTM_CTRL, 0); ret = 0; mgr->payload_id_table_cleared = false; + + memset(&mgr->down_rep_recv, 0, sizeof(mgr->down_rep_recv)); + memset(&mgr->up_req_recv, 0, sizeof(mgr->up_req_recv)); } out_unlock: @@ -3853,7 +3861,7 @@ static int drm_dp_mst_handle_down_rep(struct drm_dp_mst_topology_mgr *mgr) struct drm_dp_sideband_msg_rx *msg = &mgr->down_rep_recv; if (!drm_dp_get_one_sb_msg(mgr, false, &mstb)) - goto out; + goto out_clear_reply; /* Multi-packet message transmission, don't clear the reply */ if (!msg->have_eomt) diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c index f197f59f6d99..5457c02ca1ab 100644 --- a/drivers/gpu/drm/drm_atomic.c +++ b/drivers/gpu/drm/drm_atomic.c @@ -880,7 +880,7 @@ EXPORT_SYMBOL(drm_atomic_get_private_obj_state); * or NULL if the private_obj is not part of the global atomic state. */ struct drm_private_state * -drm_atomic_get_old_private_obj_state(struct drm_atomic_state *state, +drm_atomic_get_old_private_obj_state(const struct drm_atomic_state *state, struct drm_private_obj *obj) { int i; @@ -902,7 +902,7 @@ EXPORT_SYMBOL(drm_atomic_get_old_private_obj_state); * or NULL if the private_obj is not part of the global atomic state. */ struct drm_private_state * -drm_atomic_get_new_private_obj_state(struct drm_atomic_state *state, +drm_atomic_get_new_private_obj_state(const struct drm_atomic_state *state, struct drm_private_obj *obj) { int i; @@ -934,7 +934,7 @@ EXPORT_SYMBOL(drm_atomic_get_new_private_obj_state); * not connected. */ struct drm_connector * -drm_atomic_get_old_connector_for_encoder(struct drm_atomic_state *state, +drm_atomic_get_old_connector_for_encoder(const struct drm_atomic_state *state, struct drm_encoder *encoder) { struct drm_connector_state *conn_state; @@ -968,7 +968,7 @@ EXPORT_SYMBOL(drm_atomic_get_old_connector_for_encoder); * not connected. */ struct drm_connector * -drm_atomic_get_new_connector_for_encoder(struct drm_atomic_state *state, +drm_atomic_get_new_connector_for_encoder(const struct drm_atomic_state *state, struct drm_encoder *encoder) { struct drm_connector_state *conn_state; @@ -1117,7 +1117,7 @@ EXPORT_SYMBOL(drm_atomic_get_bridge_state); * the bridge is not part of the global atomic state. */ struct drm_bridge_state * -drm_atomic_get_old_bridge_state(struct drm_atomic_state *state, +drm_atomic_get_old_bridge_state(const struct drm_atomic_state *state, struct drm_bridge *bridge) { struct drm_private_state *obj_state; @@ -1139,7 +1139,7 @@ EXPORT_SYMBOL(drm_atomic_get_old_bridge_state); * the bridge is not part of the global atomic state. */ struct drm_bridge_state * -drm_atomic_get_new_bridge_state(struct drm_atomic_state *state, +drm_atomic_get_new_bridge_state(const struct drm_atomic_state *state, struct drm_bridge *bridge) { struct drm_private_state *obj_state; @@ -1756,8 +1756,8 @@ EXPORT_SYMBOL(drm_state_dump); #ifdef CONFIG_DEBUG_FS static int drm_state_info(struct seq_file *m, void *data) { - 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 drm_printer p = drm_seq_file_printer(m); __drm_state_dump(dev, &p, true); @@ -1766,14 +1766,13 @@ static int drm_state_info(struct seq_file *m, void *data) } /* any use in debugfs files to dump individual planes/crtc/etc? */ -static const struct drm_info_list drm_atomic_debugfs_list[] = { +static const struct drm_debugfs_info drm_atomic_debugfs_list[] = { {"state", drm_state_info, 0}, }; void drm_atomic_debugfs_init(struct drm_minor *minor) { - drm_debugfs_create_files(drm_atomic_debugfs_list, - ARRAY_SIZE(drm_atomic_debugfs_list), - minor->debugfs_root, minor); + drm_debugfs_add_files(minor->dev, drm_atomic_debugfs_list, + ARRAY_SIZE(drm_atomic_debugfs_list)); } #endif diff --git a/drivers/gpu/drm/drm_atomic_state_helper.c b/drivers/gpu/drm/drm_atomic_state_helper.c index dfb57217253b..22251c5f6a8a 100644 --- a/drivers/gpu/drm/drm_atomic_state_helper.c +++ b/drivers/gpu/drm/drm_atomic_state_helper.c @@ -482,6 +482,130 @@ void drm_atomic_helper_connector_tv_margins_reset(struct drm_connector *connecto EXPORT_SYMBOL(drm_atomic_helper_connector_tv_margins_reset); /** + * drm_atomic_helper_connector_tv_reset - Resets Analog TV connector properties + * @connector: DRM connector + * + * Resets the analog TV properties attached to a connector + */ +void drm_atomic_helper_connector_tv_reset(struct drm_connector *connector) +{ + struct drm_device *dev = connector->dev; + struct drm_cmdline_mode *cmdline = &connector->cmdline_mode; + struct drm_connector_state *state = connector->state; + struct drm_property *prop; + uint64_t val; + + prop = dev->mode_config.tv_mode_property; + if (prop) + if (!drm_object_property_get_default_value(&connector->base, + prop, &val)) + state->tv.mode = val; + + if (cmdline->tv_mode_specified) + state->tv.mode = cmdline->tv_mode; + + prop = dev->mode_config.tv_select_subconnector_property; + if (prop) + if (!drm_object_property_get_default_value(&connector->base, + prop, &val)) + state->tv.select_subconnector = val; + + prop = dev->mode_config.tv_subconnector_property; + if (prop) + if (!drm_object_property_get_default_value(&connector->base, + prop, &val)) + state->tv.subconnector = val; + + prop = dev->mode_config.tv_brightness_property; + if (prop) + if (!drm_object_property_get_default_value(&connector->base, + prop, &val)) + state->tv.brightness = val; + + prop = dev->mode_config.tv_contrast_property; + if (prop) + if (!drm_object_property_get_default_value(&connector->base, + prop, &val)) + state->tv.contrast = val; + + prop = dev->mode_config.tv_flicker_reduction_property; + if (prop) + if (!drm_object_property_get_default_value(&connector->base, + prop, &val)) + state->tv.flicker_reduction = val; + + prop = dev->mode_config.tv_overscan_property; + if (prop) + if (!drm_object_property_get_default_value(&connector->base, + prop, &val)) + state->tv.overscan = val; + + prop = dev->mode_config.tv_saturation_property; + if (prop) + if (!drm_object_property_get_default_value(&connector->base, + prop, &val)) + state->tv.saturation = val; + + prop = dev->mode_config.tv_hue_property; + if (prop) + if (!drm_object_property_get_default_value(&connector->base, + prop, &val)) + state->tv.hue = val; + + drm_atomic_helper_connector_tv_margins_reset(connector); +} +EXPORT_SYMBOL(drm_atomic_helper_connector_tv_reset); + +/** + * @drm_atomic_helper_connector_tv_check: Validate an analog TV connector state + * @connector: DRM Connector + * @state: the DRM State object + * + * Checks the state object to see if the requested state is valid for an + * analog TV connector. + * + * Returns: + * Zero for success, a negative error code on error. + */ +int drm_atomic_helper_connector_tv_check(struct drm_connector *connector, + struct drm_atomic_state *state) +{ + struct drm_connector_state *old_conn_state = + drm_atomic_get_old_connector_state(state, connector); + struct drm_connector_state *new_conn_state = + drm_atomic_get_new_connector_state(state, connector); + struct drm_crtc_state *crtc_state; + struct drm_crtc *crtc; + + crtc = new_conn_state->crtc; + if (!crtc) + return 0; + + crtc_state = drm_atomic_get_new_crtc_state(state, crtc); + if (!crtc_state) + return -EINVAL; + + if (old_conn_state->tv.mode != new_conn_state->tv.mode) + crtc_state->mode_changed = true; + + if (old_conn_state->tv.margins.left != new_conn_state->tv.margins.left || + old_conn_state->tv.margins.right != new_conn_state->tv.margins.right || + old_conn_state->tv.margins.top != new_conn_state->tv.margins.top || + old_conn_state->tv.margins.bottom != new_conn_state->tv.margins.bottom || + old_conn_state->tv.mode != new_conn_state->tv.mode || + old_conn_state->tv.brightness != new_conn_state->tv.brightness || + old_conn_state->tv.contrast != new_conn_state->tv.contrast || + old_conn_state->tv.flicker_reduction != new_conn_state->tv.flicker_reduction || + old_conn_state->tv.overscan != new_conn_state->tv.overscan || + old_conn_state->tv.saturation != new_conn_state->tv.saturation || + old_conn_state->tv.hue != new_conn_state->tv.hue) + crtc_state->connectors_changed = true; + + return 0; +} +EXPORT_SYMBOL(drm_atomic_helper_connector_tv_check); + +/** * __drm_atomic_helper_connector_duplicate_state - copy atomic connector state * @connector: connector object * @state: atomic connector state diff --git a/drivers/gpu/drm/drm_atomic_uapi.c b/drivers/gpu/drm/drm_atomic_uapi.c index c06d0639d552..d867e7f9f2cd 100644 --- a/drivers/gpu/drm/drm_atomic_uapi.c +++ b/drivers/gpu/drm/drm_atomic_uapi.c @@ -698,6 +698,8 @@ static int drm_atomic_connector_set_property(struct drm_connector *connector, state->tv.margins.top = val; } else if (property == config->tv_bottom_margin_property) { state->tv.margins.bottom = val; + } else if (property == config->legacy_tv_mode_property) { + state->tv.legacy_mode = val; } else if (property == config->tv_mode_property) { state->tv.mode = val; } else if (property == config->tv_brightness_property) { @@ -808,6 +810,8 @@ drm_atomic_connector_get_property(struct drm_connector *connector, *val = state->tv.margins.top; } else if (property == config->tv_bottom_margin_property) { *val = state->tv.margins.bottom; + } else if (property == config->legacy_tv_mode_property) { + *val = state->tv.legacy_mode; } else if (property == config->tv_mode_property) { *val = state->tv.mode; } else if (property == config->tv_brightness_property) { diff --git a/drivers/gpu/drm/drm_bridge.c b/drivers/gpu/drm/drm_bridge.c index 1545c50fd1c8..c3d69af02e79 100644 --- a/drivers/gpu/drm/drm_bridge.c +++ b/drivers/gpu/drm/drm_bridge.c @@ -153,6 +153,45 @@ * situation when probing. */ +/** + * DOC: dsi bridge operations + * + * DSI host interfaces are expected to be implemented as bridges rather than + * encoders, however there are a few aspects of their operation that need to + * be defined in order to provide a consistent interface. + * + * A DSI host should keep the PHY powered down until the pre_enable operation is + * called. All lanes are in an undefined idle state up to this point, and it + * must not be assumed that it is LP-11. + * pre_enable should initialise the PHY, set the data lanes to LP-11, and the + * clock lane to either LP-11 or HS depending on the mode_flag + * %MIPI_DSI_CLOCK_NON_CONTINUOUS. + * + * Ordinarily the downstream bridge DSI peripheral pre_enable will have been + * called before the DSI host. If the DSI peripheral requires LP-11 and/or + * the clock lane to be in HS mode prior to pre_enable, then it can set the + * &pre_enable_prev_first flag to request the pre_enable (and + * post_disable) order to be altered to enable the DSI host first. + * + * Either the CRTC being enabled, or the DSI host enable operation should switch + * the host to actively transmitting video on the data lanes. + * + * The reverse also applies. The DSI host disable operation or stopping the CRTC + * should stop transmitting video, and the data lanes should return to the LP-11 + * state. The DSI host &post_disable operation should disable the PHY. + * If the &pre_enable_prev_first flag is set, then the DSI peripheral's + * bridge &post_disable will be called before the DSI host's post_disable. + * + * Whilst it is valid to call &host_transfer prior to pre_enable or after + * post_disable, the exact state of the lanes is undefined at this point. The + * DSI host should initialise the interface, transmit the data, and then disable + * the interface again. + * + * Ultra Low Power State (ULPS) is not explicitly supported by DRM. If + * implemented, it therefore needs to be handled entirely within the DSI Host + * driver. + */ + static DEFINE_MUTEX(bridge_lock); static LIST_HEAD(bridge_list); @@ -510,61 +549,6 @@ drm_bridge_chain_mode_valid(struct drm_bridge *bridge, EXPORT_SYMBOL(drm_bridge_chain_mode_valid); /** - * drm_bridge_chain_disable - disables all bridges in the encoder chain - * @bridge: bridge control structure - * - * Calls &drm_bridge_funcs.disable op for all the bridges in the encoder - * chain, starting from the last bridge to the first. These are called before - * calling the encoder's prepare op. - * - * Note: the bridge passed should be the one closest to the encoder - */ -void drm_bridge_chain_disable(struct drm_bridge *bridge) -{ - struct drm_encoder *encoder; - struct drm_bridge *iter; - - if (!bridge) - return; - - encoder = bridge->encoder; - list_for_each_entry_reverse(iter, &encoder->bridge_chain, chain_node) { - if (iter->funcs->disable) - iter->funcs->disable(iter); - - if (iter == bridge) - break; - } -} -EXPORT_SYMBOL(drm_bridge_chain_disable); - -/** - * drm_bridge_chain_post_disable - cleans up after disabling all bridges in the - * encoder chain - * @bridge: bridge control structure - * - * Calls &drm_bridge_funcs.post_disable op for all the bridges in the - * encoder chain, starting from the first bridge to the last. These are called - * after completing the encoder's prepare op. - * - * Note: the bridge passed should be the one closest to the encoder - */ -void drm_bridge_chain_post_disable(struct drm_bridge *bridge) -{ - struct drm_encoder *encoder; - - if (!bridge) - return; - - encoder = bridge->encoder; - list_for_each_entry_from(bridge, &encoder->bridge_chain, chain_node) { - if (bridge->funcs->post_disable) - bridge->funcs->post_disable(bridge); - } -} -EXPORT_SYMBOL(drm_bridge_chain_post_disable); - -/** * drm_bridge_chain_mode_set - set proposed mode for all bridges in the * encoder chain * @bridge: bridge control structure @@ -594,61 +578,6 @@ void drm_bridge_chain_mode_set(struct drm_bridge *bridge, EXPORT_SYMBOL(drm_bridge_chain_mode_set); /** - * drm_bridge_chain_pre_enable - prepares for enabling all bridges in the - * encoder chain - * @bridge: bridge control structure - * - * Calls &drm_bridge_funcs.pre_enable op for all the bridges in the encoder - * chain, starting from the last bridge to the first. These are called - * before calling the encoder's commit op. - * - * Note: the bridge passed should be the one closest to the encoder - */ -void drm_bridge_chain_pre_enable(struct drm_bridge *bridge) -{ - struct drm_encoder *encoder; - struct drm_bridge *iter; - - if (!bridge) - return; - - encoder = bridge->encoder; - list_for_each_entry_reverse(iter, &encoder->bridge_chain, chain_node) { - if (iter->funcs->pre_enable) - iter->funcs->pre_enable(iter); - - if (iter == bridge) - break; - } -} -EXPORT_SYMBOL(drm_bridge_chain_pre_enable); - -/** - * drm_bridge_chain_enable - enables all bridges in the encoder chain - * @bridge: bridge control structure - * - * Calls &drm_bridge_funcs.enable op for all the bridges in the encoder - * chain, starting from the first bridge to the last. These are called - * after completing the encoder's commit op. - * - * Note that the bridge passed should be the one closest to the encoder - */ -void drm_bridge_chain_enable(struct drm_bridge *bridge) -{ - struct drm_encoder *encoder; - - if (!bridge) - return; - - encoder = bridge->encoder; - list_for_each_entry_from(bridge, &encoder->bridge_chain, chain_node) { - if (bridge->funcs->enable) - bridge->funcs->enable(bridge); - } -} -EXPORT_SYMBOL(drm_bridge_chain_enable); - -/** * drm_atomic_bridge_chain_disable - disables all bridges in the encoder chain * @bridge: bridge control structure * @old_state: old atomic state @@ -691,6 +620,25 @@ void drm_atomic_bridge_chain_disable(struct drm_bridge *bridge, } EXPORT_SYMBOL(drm_atomic_bridge_chain_disable); +static void drm_atomic_bridge_call_post_disable(struct drm_bridge *bridge, + struct drm_atomic_state *old_state) +{ + if (old_state && bridge->funcs->atomic_post_disable) { + struct drm_bridge_state *old_bridge_state; + + old_bridge_state = + drm_atomic_get_old_bridge_state(old_state, + bridge); + if (WARN_ON(!old_bridge_state)) + return; + + bridge->funcs->atomic_post_disable(bridge, + old_bridge_state); + } else if (bridge->funcs->post_disable) { + bridge->funcs->post_disable(bridge); + } +} + /** * drm_atomic_bridge_chain_post_disable - cleans up after disabling all bridges * in the encoder chain @@ -702,36 +650,86 @@ EXPORT_SYMBOL(drm_atomic_bridge_chain_disable); * starting from the first bridge to the last. These are called after completing * &drm_encoder_helper_funcs.atomic_disable * + * If a bridge sets @pre_enable_prev_first, then the @post_disable for that + * bridge will be called before the previous one to reverse the @pre_enable + * calling direction. + * * Note: the bridge passed should be the one closest to the encoder */ void drm_atomic_bridge_chain_post_disable(struct drm_bridge *bridge, struct drm_atomic_state *old_state) { struct drm_encoder *encoder; + struct drm_bridge *next, *limit; if (!bridge) return; encoder = bridge->encoder; + list_for_each_entry_from(bridge, &encoder->bridge_chain, chain_node) { - if (bridge->funcs->atomic_post_disable) { - struct drm_bridge_state *old_bridge_state; + limit = NULL; + + if (!list_is_last(&bridge->chain_node, &encoder->bridge_chain)) { + next = list_next_entry(bridge, chain_node); + + if (next->pre_enable_prev_first) { + /* next bridge had requested that prev + * was enabled first, so disabled last + */ + limit = next; + + /* Find the next bridge that has NOT requested + * prev to be enabled first / disabled last + */ + list_for_each_entry_from(next, &encoder->bridge_chain, + chain_node) { + if (next->pre_enable_prev_first) { + next = list_prev_entry(next, chain_node); + limit = next; + break; + } + } + + /* Call these bridges in reverse order */ + list_for_each_entry_from_reverse(next, &encoder->bridge_chain, + chain_node) { + if (next == bridge) + break; + + drm_atomic_bridge_call_post_disable(next, + old_state); + } + } + } - old_bridge_state = - drm_atomic_get_old_bridge_state(old_state, - bridge); - if (WARN_ON(!old_bridge_state)) - return; + drm_atomic_bridge_call_post_disable(bridge, old_state); - bridge->funcs->atomic_post_disable(bridge, - old_bridge_state); - } else if (bridge->funcs->post_disable) { - bridge->funcs->post_disable(bridge); - } + if (limit) + /* Jump all bridges that we have already post_disabled */ + bridge = limit; } } EXPORT_SYMBOL(drm_atomic_bridge_chain_post_disable); +static void drm_atomic_bridge_call_pre_enable(struct drm_bridge *bridge, + struct drm_atomic_state *old_state) +{ + if (old_state && bridge->funcs->atomic_pre_enable) { + struct drm_bridge_state *old_bridge_state; + + old_bridge_state = + drm_atomic_get_old_bridge_state(old_state, + bridge); + if (WARN_ON(!old_bridge_state)) + return; + + bridge->funcs->atomic_pre_enable(bridge, old_bridge_state); + } else if (bridge->funcs->pre_enable) { + bridge->funcs->pre_enable(bridge); + } +} + /** * drm_atomic_bridge_chain_pre_enable - prepares for enabling all bridges in * the encoder chain @@ -743,32 +741,60 @@ EXPORT_SYMBOL(drm_atomic_bridge_chain_post_disable); * starting from the last bridge to the first. These are called before calling * &drm_encoder_helper_funcs.atomic_enable * + * If a bridge sets @pre_enable_prev_first, then the pre_enable for the + * prev bridge will be called before pre_enable of this bridge. + * * Note: the bridge passed should be the one closest to the encoder */ void drm_atomic_bridge_chain_pre_enable(struct drm_bridge *bridge, struct drm_atomic_state *old_state) { struct drm_encoder *encoder; - struct drm_bridge *iter; + struct drm_bridge *iter, *next, *limit; if (!bridge) return; encoder = bridge->encoder; + list_for_each_entry_reverse(iter, &encoder->bridge_chain, chain_node) { - if (iter->funcs->atomic_pre_enable) { - struct drm_bridge_state *old_bridge_state; + if (iter->pre_enable_prev_first) { + next = iter; + limit = bridge; + list_for_each_entry_from_reverse(next, + &encoder->bridge_chain, + chain_node) { + if (next == bridge) + break; + + if (!next->pre_enable_prev_first) { + /* Found first bridge that does NOT + * request prev to be enabled first + */ + limit = list_prev_entry(next, chain_node); + break; + } + } + + list_for_each_entry_from(next, &encoder->bridge_chain, chain_node) { + /* Call requested prev bridge pre_enable + * in order. + */ + if (next == iter) + /* At the first bridge to request prev + * bridges called first. + */ + break; + + drm_atomic_bridge_call_pre_enable(next, old_state); + } + } - old_bridge_state = - drm_atomic_get_old_bridge_state(old_state, - iter); - if (WARN_ON(!old_bridge_state)) - return; + drm_atomic_bridge_call_pre_enable(iter, old_state); - iter->funcs->atomic_pre_enable(iter, old_bridge_state); - } else if (iter->funcs->pre_enable) { - iter->funcs->pre_enable(iter); - } + if (iter->pre_enable_prev_first) + /* Jump all bridges that we have already pre_enabled */ + iter = limit; if (iter == bridge) break; diff --git a/drivers/gpu/drm/drm_client.c b/drivers/gpu/drm/drm_client.c index fd67efe37c63..262ec64d4397 100644 --- a/drivers/gpu/drm/drm_client.c +++ b/drivers/gpu/drm/drm_client.c @@ -480,8 +480,8 @@ EXPORT_SYMBOL(drm_client_framebuffer_flush); #ifdef CONFIG_DEBUG_FS static int drm_client_debugfs_internal_clients(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 drm_printer p = drm_seq_file_printer(m); struct drm_client_dev *client; @@ -493,14 +493,13 @@ static int drm_client_debugfs_internal_clients(struct seq_file *m, void *data) return 0; } -static const struct drm_info_list drm_client_debugfs_list[] = { +static const struct drm_debugfs_info drm_client_debugfs_list[] = { { "internal_clients", drm_client_debugfs_internal_clients, 0 }, }; void drm_client_debugfs_init(struct drm_minor *minor) { - drm_debugfs_create_files(drm_client_debugfs_list, - ARRAY_SIZE(drm_client_debugfs_list), - minor->debugfs_root, minor); + drm_debugfs_add_files(minor->dev, drm_client_debugfs_list, + ARRAY_SIZE(drm_client_debugfs_list)); } #endif diff --git a/drivers/gpu/drm/drm_client_modeset.c b/drivers/gpu/drm/drm_client_modeset.c index d553e793e673..1b12a3c201a3 100644 --- a/drivers/gpu/drm/drm_client_modeset.c +++ b/drivers/gpu/drm/drm_client_modeset.c @@ -188,10 +188,6 @@ static struct drm_display_mode *drm_connector_pick_cmdline_mode(struct drm_conne prefer_non_interlace = !cmdline_mode->interlace; again: list_for_each_entry(mode, &connector->modes, head) { - /* Check (optional) mode name first */ - if (!strcmp(mode->name, cmdline_mode->name)) - return mode; - /* check width/height */ if (mode->hdisplay != cmdline_mode->xres || mode->vdisplay != cmdline_mode->yres) diff --git a/drivers/gpu/drm/drm_connector.c b/drivers/gpu/drm/drm_connector.c index 547356e00341..8d92777e57dd 100644 --- a/drivers/gpu/drm/drm_connector.c +++ b/drivers/gpu/drm/drm_connector.c @@ -984,6 +984,41 @@ static const struct drm_prop_enum_list drm_dvi_i_subconnector_enum_list[] = { DRM_ENUM_NAME_FN(drm_get_dvi_i_subconnector_name, drm_dvi_i_subconnector_enum_list) +static const struct drm_prop_enum_list drm_tv_mode_enum_list[] = { + { DRM_MODE_TV_MODE_NTSC, "NTSC" }, + { DRM_MODE_TV_MODE_NTSC_443, "NTSC-443" }, + { DRM_MODE_TV_MODE_NTSC_J, "NTSC-J" }, + { DRM_MODE_TV_MODE_PAL, "PAL" }, + { DRM_MODE_TV_MODE_PAL_M, "PAL-M" }, + { DRM_MODE_TV_MODE_PAL_N, "PAL-N" }, + { DRM_MODE_TV_MODE_SECAM, "SECAM" }, +}; +DRM_ENUM_NAME_FN(drm_get_tv_mode_name, drm_tv_mode_enum_list) + +/** + * drm_get_tv_mode_from_name - Translates a TV mode name into its enum value + * @name: TV Mode name we want to convert + * @len: Length of @name + * + * Translates @name into an enum drm_connector_tv_mode. + * + * Returns: the enum value on success, a negative errno otherwise. + */ +int drm_get_tv_mode_from_name(const char *name, size_t len) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(drm_tv_mode_enum_list); i++) { + const struct drm_prop_enum_list *item = &drm_tv_mode_enum_list[i]; + + if (strlen(item->name) == len && !strncmp(item->name, name, len)) + return item->type; + } + + return -EINVAL; +} +EXPORT_SYMBOL(drm_get_tv_mode_from_name); + static const struct drm_prop_enum_list drm_tv_select_enum_list[] = { { DRM_MODE_SUBCONNECTOR_Automatic, "Automatic" }, /* DVI-I and TV-out */ { DRM_MODE_SUBCONNECTOR_Composite, "Composite" }, /* TV-out */ @@ -1552,6 +1587,71 @@ EXPORT_SYMBOL(drm_connector_attach_dp_subconnector_property); * infoframe values is done through drm_hdmi_avi_infoframe_content_type(). */ +/* + * TODO: Document the properties: + * - left margin + * - right margin + * - top margin + * - bottom margin + * - brightness + * - contrast + * - flicker reduction + * - hue + * - mode + * - overscan + * - saturation + * - select subconnector + * - subconnector + */ +/** + * DOC: Analog TV Connector Properties + * + * TV Mode: + * Indicates the TV Mode used on an analog TV connector. The value + * of this property can be one of the following: + * + * NTSC: + * TV Mode is CCIR System M (aka 525-lines) together with + * the NTSC Color Encoding. + * + * NTSC-443: + * + * TV Mode is CCIR System M (aka 525-lines) together with + * the NTSC Color Encoding, but with a color subcarrier + * frequency of 4.43MHz + * + * NTSC-J: + * + * TV Mode is CCIR System M (aka 525-lines) together with + * the NTSC Color Encoding, but with a black level equal to + * the blanking level. + * + * PAL: + * + * TV Mode is CCIR System B (aka 625-lines) together with + * the PAL Color Encoding. + * + * PAL-M: + * + * TV Mode is CCIR System M (aka 525-lines) together with + * the PAL Color Encoding. + * + * PAL-N: + * + * TV Mode is CCIR System N together with the PAL Color + * Encoding, a color subcarrier frequency of 3.58MHz, the + * SECAM color space, and narrower channels than other PAL + * variants. + * + * SECAM: + * + * TV Mode is CCIR System B (aka 625-lines) together with + * the SECAM Color Encoding. + * + * Drivers can set up this property by calling + * drm_mode_create_tv_properties(). + */ + /** * drm_connector_attach_content_type_property - attach content-type property * @connector: connector to attach content type property on. @@ -1604,7 +1704,7 @@ EXPORT_SYMBOL(drm_connector_attach_tv_margin_properties); * Called by a driver's HDMI connector initialization routine, this function * creates the TV margin properties for a given device. No need to call this * function for an SDTV connector, it's already called from - * drm_mode_create_tv_properties(). + * drm_mode_create_tv_properties_legacy(). * * Returns: * 0 on success or a negative error code on failure. @@ -1639,7 +1739,7 @@ int drm_mode_create_tv_margin_properties(struct drm_device *dev) EXPORT_SYMBOL(drm_mode_create_tv_margin_properties); /** - * drm_mode_create_tv_properties - create TV specific connector properties + * drm_mode_create_tv_properties_legacy - create TV specific connector properties * @dev: DRM device * @num_modes: number of different TV formats (modes) supported * @modes: array of pointers to strings containing name of each format @@ -1649,12 +1749,16 @@ EXPORT_SYMBOL(drm_mode_create_tv_margin_properties); * responsible for allocating a list of format names and passing them to * this routine. * + * NOTE: This functions registers the deprecated "mode" connector + * property to select the analog TV mode (ie, NTSC, PAL, etc.). New + * drivers must use drm_mode_create_tv_properties() instead. + * * Returns: * 0 on success or a negative error code on failure. */ -int drm_mode_create_tv_properties(struct drm_device *dev, - unsigned int num_modes, - const char * const modes[]) +int drm_mode_create_tv_properties_legacy(struct drm_device *dev, + unsigned int num_modes, + const char * const modes[]) { struct drm_property *tv_selector; struct drm_property *tv_subconnector; @@ -1690,15 +1794,17 @@ int drm_mode_create_tv_properties(struct drm_device *dev, if (drm_mode_create_tv_margin_properties(dev)) goto nomem; - dev->mode_config.tv_mode_property = - drm_property_create(dev, DRM_MODE_PROP_ENUM, - "mode", num_modes); - if (!dev->mode_config.tv_mode_property) - goto nomem; + if (num_modes) { + dev->mode_config.legacy_tv_mode_property = + drm_property_create(dev, DRM_MODE_PROP_ENUM, + "mode", num_modes); + if (!dev->mode_config.legacy_tv_mode_property) + goto nomem; - for (i = 0; i < num_modes; i++) - drm_property_add_enum(dev->mode_config.tv_mode_property, - i, modes[i]); + for (i = 0; i < num_modes; i++) + drm_property_add_enum(dev->mode_config.legacy_tv_mode_property, + i, modes[i]); + } dev->mode_config.tv_brightness_property = drm_property_create_range(dev, 0, "brightness", 0, 100); @@ -1734,6 +1840,47 @@ int drm_mode_create_tv_properties(struct drm_device *dev, nomem: return -ENOMEM; } +EXPORT_SYMBOL(drm_mode_create_tv_properties_legacy); + +/** + * drm_mode_create_tv_properties - create TV specific connector properties + * @dev: DRM device + * @supported_tv_modes: Bitmask of TV modes supported (See DRM_MODE_TV_MODE_*) + + * Called by a driver's TV initialization routine, this function creates + * the TV specific connector properties for a given device. + * + * Returns: + * 0 on success or a negative error code on failure. + */ +int drm_mode_create_tv_properties(struct drm_device *dev, + unsigned int supported_tv_modes) +{ + struct drm_prop_enum_list tv_mode_list[DRM_MODE_TV_MODE_MAX]; + struct drm_property *tv_mode; + unsigned int i, len = 0; + + if (dev->mode_config.tv_mode_property) + return 0; + + for (i = 0; i < DRM_MODE_TV_MODE_MAX; i++) { + if (!(supported_tv_modes & BIT(i))) + continue; + + tv_mode_list[len].type = i; + tv_mode_list[len].name = drm_get_tv_mode_name(i); + len++; + } + + tv_mode = drm_property_create_enum(dev, 0, "TV mode", + tv_mode_list, len); + if (!tv_mode) + return -ENOMEM; + + dev->mode_config.tv_mode_property = tv_mode; + + return drm_mode_create_tv_properties_legacy(dev, 0, NULL); +} EXPORT_SYMBOL(drm_mode_create_tv_properties); /** diff --git a/drivers/gpu/drm/drm_debugfs.c b/drivers/gpu/drm/drm_debugfs.c index ee445f4605ba..5ea237839439 100644 --- a/drivers/gpu/drm/drm_debugfs.c +++ b/drivers/gpu/drm/drm_debugfs.c @@ -38,6 +38,7 @@ #include <drm/drm_edid.h> #include <drm/drm_file.h> #include <drm/drm_gem.h> +#include <drm/drm_managed.h> #include "drm_crtc_internal.h" #include "drm_internal.h" @@ -50,9 +51,8 @@ static int drm_name_info(struct seq_file *m, void *data) { - struct drm_info_node *node = (struct drm_info_node *) m->private; - struct drm_minor *minor = node->minor; - struct drm_device *dev = minor->dev; + struct drm_debugfs_entry *entry = m->private; + struct drm_device *dev = entry->dev; struct drm_master *master; mutex_lock(&dev->master_mutex); @@ -72,8 +72,8 @@ static int drm_name_info(struct seq_file *m, void *data) static int drm_clients_info(struct seq_file *m, void *data) { - 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 drm_file *priv; kuid_t uid; @@ -124,8 +124,8 @@ static int drm_gem_one_name_info(int id, void *ptr, void *data) static int drm_gem_name_info(struct seq_file *m, void *data) { - 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; seq_printf(m, " name size handles refcount\n"); @@ -136,7 +136,7 @@ static int drm_gem_name_info(struct seq_file *m, void *data) return 0; } -static const struct drm_info_list drm_debugfs_list[] = { +static const struct drm_debugfs_info drm_debugfs_list[] = { {"name", drm_name_info, 0}, {"clients", drm_clients_info, 0}, {"gem_names", drm_gem_name_info, DRIVER_GEM}, @@ -151,6 +151,21 @@ static int drm_debugfs_open(struct inode *inode, struct file *file) return single_open(file, node->info_ent->show, node); } +static int drm_debugfs_entry_open(struct inode *inode, struct file *file) +{ + struct drm_debugfs_entry *entry = inode->i_private; + struct drm_debugfs_info *node = &entry->file; + + return single_open(file, node->show, entry); +} + +static const struct file_operations drm_debugfs_entry_fops = { + .owner = THIS_MODULE, + .open = drm_debugfs_entry_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; static const struct file_operations drm_debugfs_fops = { .owner = THIS_MODULE, @@ -207,6 +222,7 @@ int drm_debugfs_init(struct drm_minor *minor, int minor_id, struct dentry *root) { struct drm_device *dev = minor->dev; + struct drm_debugfs_entry *entry, *tmp; char name[64]; INIT_LIST_HEAD(&minor->debugfs_list); @@ -214,8 +230,7 @@ int drm_debugfs_init(struct drm_minor *minor, int minor_id, sprintf(name, "%d", minor_id); minor->debugfs_root = debugfs_create_dir(name, root); - drm_debugfs_create_files(drm_debugfs_list, DRM_DEBUGFS_ENTRIES, - minor->debugfs_root, minor); + drm_debugfs_add_files(minor->dev, drm_debugfs_list, DRM_DEBUGFS_ENTRIES); if (drm_drv_uses_atomic_modeset(dev)) { drm_atomic_debugfs_init(minor); @@ -230,9 +245,29 @@ int drm_debugfs_init(struct drm_minor *minor, int minor_id, if (dev->driver->debugfs_init) dev->driver->debugfs_init(minor); + list_for_each_entry_safe(entry, tmp, &dev->debugfs_list, list) { + debugfs_create_file(entry->file.name, S_IFREG | S_IRUGO, + minor->debugfs_root, entry, &drm_debugfs_entry_fops); + list_del(&entry->list); + } + return 0; } +void drm_debugfs_late_register(struct drm_device *dev) +{ + struct drm_minor *minor = dev->primary; + struct drm_debugfs_entry *entry, *tmp; + + if (!minor) + return; + + list_for_each_entry_safe(entry, tmp, &dev->debugfs_list, list) { + debugfs_create_file(entry->file.name, S_IFREG | S_IRUGO, + minor->debugfs_root, entry, &drm_debugfs_entry_fops); + list_del(&entry->list); + } +} int drm_debugfs_remove_files(const struct drm_info_list *files, int count, struct drm_minor *minor) @@ -281,6 +316,53 @@ void drm_debugfs_cleanup(struct drm_minor *minor) minor->debugfs_root = NULL; } +/** + * drm_debugfs_add_file - Add a given file to the DRM device debugfs file list + * @dev: drm device for the ioctl + * @name: debugfs file name + * @show: show callback + * @data: driver-private data, should not be device-specific + * + * Add a given file entry to the DRM device debugfs file list to be created on + * drm_debugfs_init. + */ +void drm_debugfs_add_file(struct drm_device *dev, const char *name, + int (*show)(struct seq_file*, void*), void *data) +{ + struct drm_debugfs_entry *entry = drmm_kzalloc(dev, sizeof(*entry), GFP_KERNEL); + + if (!entry) + return; + + entry->file.name = name; + entry->file.show = show; + entry->file.data = data; + entry->dev = dev; + + mutex_lock(&dev->debugfs_mutex); + list_add(&entry->list, &dev->debugfs_list); + mutex_unlock(&dev->debugfs_mutex); +} +EXPORT_SYMBOL(drm_debugfs_add_file); + +/** + * drm_debugfs_add_files - Add an array of files to the DRM device debugfs file list + * @dev: drm device for the ioctl + * @files: The array of files to create + * @count: The number of files given + * + * Add a given set of debugfs files represented by an array of + * &struct drm_debugfs_info in the DRM device debugfs file list. + */ +void drm_debugfs_add_files(struct drm_device *dev, const struct drm_debugfs_info *files, int count) +{ + int i; + + for (i = 0; i < count; i++) + drm_debugfs_add_file(dev, files[i].name, files[i].show, files[i].data); +} +EXPORT_SYMBOL(drm_debugfs_add_files); + static int connector_show(struct seq_file *m, void *data) { struct drm_connector *connector = m->private; diff --git a/drivers/gpu/drm/drm_drv.c b/drivers/gpu/drm/drm_drv.c index 73b845a75d52..c6eb8972451a 100644 --- a/drivers/gpu/drm/drm_drv.c +++ b/drivers/gpu/drm/drm_drv.c @@ -598,6 +598,7 @@ static void drm_dev_init_release(struct drm_device *dev, void *res) mutex_destroy(&dev->clientlist_mutex); mutex_destroy(&dev->filelist_mutex); mutex_destroy(&dev->struct_mutex); + mutex_destroy(&dev->debugfs_mutex); drm_legacy_destroy_members(dev); } @@ -638,12 +639,14 @@ static int drm_dev_init(struct drm_device *dev, INIT_LIST_HEAD(&dev->filelist_internal); INIT_LIST_HEAD(&dev->clientlist); INIT_LIST_HEAD(&dev->vblank_event_list); + INIT_LIST_HEAD(&dev->debugfs_list); spin_lock_init(&dev->event_lock); mutex_init(&dev->struct_mutex); mutex_init(&dev->filelist_mutex); mutex_init(&dev->clientlist_mutex); mutex_init(&dev->master_mutex); + mutex_init(&dev->debugfs_mutex); ret = drmm_add_action_or_reset(dev, drm_dev_init_release, NULL); if (ret) @@ -929,8 +932,8 @@ int drm_dev_register(struct drm_device *dev, unsigned long flags) dev->registered = true; - if (dev->driver->load) { - ret = dev->driver->load(dev, flags); + if (driver->load) { + ret = driver->load(dev, flags); if (ret) goto err_minors; } diff --git a/drivers/gpu/drm/drm_fbdev_generic.c b/drivers/gpu/drm/drm_fbdev_generic.c index ab8695669279..0a4c160e0e58 100644 --- a/drivers/gpu/drm/drm_fbdev_generic.c +++ b/drivers/gpu/drm/drm_fbdev_generic.c @@ -431,7 +431,6 @@ static const struct drm_client_funcs drm_fbdev_client_funcs = { * drm_fbdev_generic_setup() - Setup generic fbdev emulation * @dev: DRM device * @preferred_bpp: Preferred bits per pixel for the device. - * @dev->mode_config.preferred_depth is used if this is zero. * * This function sets up generic fbdev emulation for drivers that supports * dumb buffers with a virtual address and that can be mmap'ed. @@ -475,13 +474,17 @@ void drm_fbdev_generic_setup(struct drm_device *dev, } /* - * FIXME: This mixes up depth with bpp, which results in a glorious - * mess, resulting in some drivers picking wrong fbdev defaults and - * others wrong preferred_depth defaults. + * Pick a preferred bpp of 32 if no value has been given. This + * will select XRGB8888 for the framebuffer formats. All drivers + * have to support XRGB8888 for backwards compatibility with legacy + * userspace, so it's the safe choice here. + * + * TODO: Replace struct drm_mode_config.preferred_depth and this + * bpp value with a preferred format that is given as struct + * drm_format_info. Then derive all other values from the + * format. */ if (!preferred_bpp) - preferred_bpp = dev->mode_config.preferred_depth; - if (!preferred_bpp) preferred_bpp = 32; fb_helper->preferred_bpp = preferred_bpp; diff --git a/drivers/gpu/drm/drm_framebuffer.c b/drivers/gpu/drm/drm_framebuffer.c index 2dd97473ca10..aff3746dedfb 100644 --- a/drivers/gpu/drm/drm_framebuffer.c +++ b/drivers/gpu/drm/drm_framebuffer.c @@ -1203,8 +1203,8 @@ void drm_framebuffer_print_info(struct drm_printer *p, unsigned int indent, #ifdef CONFIG_DEBUG_FS static int drm_framebuffer_info(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 drm_printer p = drm_seq_file_printer(m); struct drm_framebuffer *fb; @@ -1218,14 +1218,13 @@ static int drm_framebuffer_info(struct seq_file *m, void *data) return 0; } -static const struct drm_info_list drm_framebuffer_debugfs_list[] = { +static const struct drm_debugfs_info drm_framebuffer_debugfs_list[] = { { "framebuffer", drm_framebuffer_info, 0 }, }; void drm_framebuffer_debugfs_init(struct drm_minor *minor) { - drm_debugfs_create_files(drm_framebuffer_debugfs_list, - ARRAY_SIZE(drm_framebuffer_debugfs_list), - minor->debugfs_root, minor); + drm_debugfs_add_files(minor->dev, drm_framebuffer_debugfs_list, + ARRAY_SIZE(drm_framebuffer_debugfs_list)); } #endif diff --git a/drivers/gpu/drm/drm_gem_atomic_helper.c b/drivers/gpu/drm/drm_gem_atomic_helper.c index e42800718f51..5d4b9cd077f7 100644 --- a/drivers/gpu/drm/drm_gem_atomic_helper.c +++ b/drivers/gpu/drm/drm_gem_atomic_helper.c @@ -26,11 +26,8 @@ * call drm_gem_plane_helper_prepare_fb() from their implementation of * struct &drm_plane_helper.prepare_fb . It sets the plane's fence from * the framebuffer so that the DRM core can synchronize access automatically. - * * drm_gem_plane_helper_prepare_fb() can also be used directly as - * implementation of prepare_fb. For drivers based on - * struct drm_simple_display_pipe, drm_gem_simple_display_pipe_prepare_fb() - * provides equivalent functionality. + * implementation of prepare_fb. * * .. code-block:: c * @@ -41,11 +38,6 @@ * . prepare_fb = drm_gem_plane_helper_prepare_fb, * }; * - * struct drm_simple_display_pipe_funcs driver_pipe_funcs = { - * ..., - * . prepare_fb = drm_gem_simple_display_pipe_prepare_fb, - * }; - * * A driver using a shadow buffer copies the content of the shadow buffers * into the HW's framebuffer memory during an atomic update. This requires * a mapping of the shadow buffer into kernel address space. The mappings @@ -205,27 +197,6 @@ error: } EXPORT_SYMBOL_GPL(drm_gem_plane_helper_prepare_fb); -/** - * drm_gem_simple_display_pipe_prepare_fb - prepare_fb helper for &drm_simple_display_pipe - * @pipe: Simple display pipe - * @plane_state: Plane state - * - * This function uses drm_gem_plane_helper_prepare_fb() to extract the fences - * from &drm_gem_object.resv and attaches them to the plane state for the atomic - * helper to wait on. This is necessary to correctly implement implicit - * synchronization for any buffers shared as a struct &dma_buf. Drivers can use - * this as their &drm_simple_display_pipe_funcs.prepare_fb callback. - * - * See drm_gem_plane_helper_prepare_fb() for a discussion of implicit and - * explicit fencing in atomic modeset updates. - */ -int drm_gem_simple_display_pipe_prepare_fb(struct drm_simple_display_pipe *pipe, - struct drm_plane_state *plane_state) -{ - return drm_gem_plane_helper_prepare_fb(&pipe->plane, plane_state); -} -EXPORT_SYMBOL(drm_gem_simple_display_pipe_prepare_fb); - /* * Shadow-buffered Planes */ diff --git a/drivers/gpu/drm/drm_gem_ttm_helper.c b/drivers/gpu/drm/drm_gem_ttm_helper.c index d5962a34c01d..3734aa2d1c5b 100644 --- a/drivers/gpu/drm/drm_gem_ttm_helper.c +++ b/drivers/gpu/drm/drm_gem_ttm_helper.c @@ -3,6 +3,8 @@ #include <linux/module.h> #include <drm/drm_gem_ttm_helper.h> +#include <drm/ttm/ttm_placement.h> +#include <drm/ttm/ttm_tt.h> /** * DOC: overview diff --git a/drivers/gpu/drm/drm_gem_vram_helper.c b/drivers/gpu/drm/drm_gem_vram_helper.c index b6c7e3803bb3..d40b3edb52d0 100644 --- a/drivers/gpu/drm/drm_gem_vram_helper.c +++ b/drivers/gpu/drm/drm_gem_vram_helper.c @@ -19,6 +19,7 @@ #include <drm/drm_simple_kms_helper.h> #include <drm/ttm/ttm_range_manager.h> +#include <drm/ttm/ttm_tt.h> static const struct drm_gem_object_funcs drm_gem_vram_object_funcs; @@ -956,8 +957,8 @@ static struct ttm_device_funcs bo_driver = { static int drm_vram_mm_debugfs(struct seq_file *m, void *data) { - struct drm_info_node *node = (struct drm_info_node *) m->private; - struct drm_vram_mm *vmm = node->minor->dev->vram_mm; + struct drm_debugfs_entry *entry = m->private; + struct drm_vram_mm *vmm = entry->dev->vram_mm; struct ttm_resource_manager *man = ttm_manager_type(&vmm->bdev, TTM_PL_VRAM); struct drm_printer p = drm_seq_file_printer(m); @@ -965,7 +966,7 @@ static int drm_vram_mm_debugfs(struct seq_file *m, void *data) return 0; } -static const struct drm_info_list drm_vram_mm_debugfs_list[] = { +static const struct drm_debugfs_info drm_vram_mm_debugfs_list[] = { { "vram-mm", drm_vram_mm_debugfs, 0, NULL }, }; @@ -977,9 +978,8 @@ static const struct drm_info_list drm_vram_mm_debugfs_list[] = { */ void drm_vram_mm_debugfs_init(struct drm_minor *minor) { - drm_debugfs_create_files(drm_vram_mm_debugfs_list, - ARRAY_SIZE(drm_vram_mm_debugfs_list), - minor->debugfs_root, minor); + drm_debugfs_add_files(minor->dev, drm_vram_mm_debugfs_list, + ARRAY_SIZE(drm_vram_mm_debugfs_list)); } EXPORT_SYMBOL(drm_vram_mm_debugfs_init); diff --git a/drivers/gpu/drm/drm_internal.h b/drivers/gpu/drm/drm_internal.h index 5ea5e260118c..ed2103ee272c 100644 --- a/drivers/gpu/drm/drm_internal.h +++ b/drivers/gpu/drm/drm_internal.h @@ -186,6 +186,7 @@ int drm_gem_dumb_destroy(struct drm_file *file, struct drm_device *dev, int drm_debugfs_init(struct drm_minor *minor, int minor_id, struct dentry *root); void drm_debugfs_cleanup(struct drm_minor *minor); +void drm_debugfs_late_register(struct drm_device *dev); void drm_debugfs_connector_add(struct drm_connector *connector); void drm_debugfs_connector_remove(struct drm_connector *connector); void drm_debugfs_crtc_add(struct drm_crtc *crtc); @@ -202,6 +203,10 @@ static inline void drm_debugfs_cleanup(struct drm_minor *minor) { } +static inline void drm_debugfs_late_register(struct drm_device *dev) +{ +} + static inline void drm_debugfs_connector_add(struct drm_connector *connector) { } diff --git a/drivers/gpu/drm/drm_mipi_dbi.c b/drivers/gpu/drm/drm_mipi_dbi.c index a6ac56580876..c871d9f096b8 100644 --- a/drivers/gpu/drm/drm_mipi_dbi.c +++ b/drivers/gpu/drm/drm_mipi_dbi.c @@ -21,6 +21,7 @@ #include <drm/drm_fourcc.h> #include <drm/drm_framebuffer.h> #include <drm/drm_gem.h> +#include <drm/drm_gem_atomic_helper.h> #include <drm/drm_gem_framebuffer_helper.h> #include <drm/drm_mipi_dbi.h> #include <drm/drm_modes.h> @@ -192,6 +193,7 @@ EXPORT_SYMBOL(mipi_dbi_command_stackbuf); /** * mipi_dbi_buf_copy - Copy a framebuffer, transforming it if necessary * @dst: The destination buffer + * @src: The source buffer * @fb: The source framebuffer * @clip: Clipping rectangle of the area to be copied * @swap: When true, swap MSB/LSB of 16-bit values @@ -199,12 +201,10 @@ EXPORT_SYMBOL(mipi_dbi_command_stackbuf); * Returns: * Zero on success, negative error code on failure. */ -int mipi_dbi_buf_copy(void *dst, struct drm_framebuffer *fb, +int mipi_dbi_buf_copy(void *dst, struct iosys_map *src, struct drm_framebuffer *fb, struct drm_rect *clip, bool swap) { struct drm_gem_object *gem = drm_gem_fb_get_obj(fb, 0); - struct iosys_map map[DRM_FORMAT_MAX_PLANES]; - struct iosys_map data[DRM_FORMAT_MAX_PLANES]; struct iosys_map dst_map = IOSYS_MAP_INIT_VADDR(dst); int ret; @@ -212,19 +212,15 @@ int mipi_dbi_buf_copy(void *dst, struct drm_framebuffer *fb, if (ret) return ret; - ret = drm_gem_fb_vmap(fb, map, data); - if (ret) - goto out_drm_gem_fb_end_cpu_access; - switch (fb->format->format) { case DRM_FORMAT_RGB565: if (swap) - drm_fb_swab(&dst_map, NULL, data, fb, clip, !gem->import_attach); + drm_fb_swab(&dst_map, NULL, src, fb, clip, !gem->import_attach); else - drm_fb_memcpy(&dst_map, NULL, data, fb, clip); + drm_fb_memcpy(&dst_map, NULL, src, fb, clip); break; case DRM_FORMAT_XRGB8888: - drm_fb_xrgb8888_to_rgb565(&dst_map, NULL, data, fb, clip, swap); + drm_fb_xrgb8888_to_rgb565(&dst_map, NULL, src, fb, clip, swap); break; default: drm_err_once(fb->dev, "Format is not supported: %p4cc\n", @@ -232,8 +228,6 @@ int mipi_dbi_buf_copy(void *dst, struct drm_framebuffer *fb, ret = -EINVAL; } - drm_gem_fb_vunmap(fb, map); -out_drm_gem_fb_end_cpu_access: drm_gem_fb_end_cpu_access(fb, DMA_FROM_DEVICE); return ret; @@ -257,29 +251,18 @@ static void mipi_dbi_set_window_address(struct mipi_dbi_dev *dbidev, ys & 0xff, (ye >> 8) & 0xff, ye & 0xff); } -static void mipi_dbi_fb_dirty(struct drm_framebuffer *fb, struct drm_rect *rect) +static void mipi_dbi_fb_dirty(struct iosys_map *src, struct drm_framebuffer *fb, + struct drm_rect *rect) { - struct iosys_map map[DRM_FORMAT_MAX_PLANES]; - struct iosys_map data[DRM_FORMAT_MAX_PLANES]; struct mipi_dbi_dev *dbidev = drm_to_mipi_dbi_dev(fb->dev); unsigned int height = rect->y2 - rect->y1; unsigned int width = rect->x2 - rect->x1; struct mipi_dbi *dbi = &dbidev->dbi; bool swap = dbi->swap_bytes; - int idx, ret = 0; + int ret = 0; bool full; void *tr; - if (WARN_ON(!fb)) - return; - - if (!drm_dev_enter(fb->dev, &idx)) - return; - - ret = drm_gem_fb_vmap(fb, map, data); - if (ret) - goto err_drm_dev_exit; - full = width == fb->width && height == fb->height; DRM_DEBUG_KMS("Flushing [FB:%d] " DRM_RECT_FMT "\n", fb->base.id, DRM_RECT_ARG(rect)); @@ -287,11 +270,11 @@ static void mipi_dbi_fb_dirty(struct drm_framebuffer *fb, struct drm_rect *rect) if (!dbi->dc || !full || swap || fb->format->format == DRM_FORMAT_XRGB8888) { tr = dbidev->tx_buf; - ret = mipi_dbi_buf_copy(dbidev->tx_buf, fb, rect, swap); + ret = mipi_dbi_buf_copy(tr, src, fb, rect, swap); if (ret) goto err_msg; } else { - tr = data[0].vaddr; /* TODO: Use mapping abstraction properly */ + tr = src->vaddr; /* TODO: Use mapping abstraction properly */ } mipi_dbi_set_window_address(dbidev, rect->x1, rect->x2 - 1, rect->y1, @@ -302,11 +285,6 @@ static void mipi_dbi_fb_dirty(struct drm_framebuffer *fb, struct drm_rect *rect) err_msg: if (ret) drm_err_once(fb->dev, "Failed to update display %d\n", ret); - - drm_gem_fb_vunmap(fb, map); - -err_drm_dev_exit: - drm_dev_exit(idx); } /** @@ -339,13 +317,24 @@ void mipi_dbi_pipe_update(struct drm_simple_display_pipe *pipe, struct drm_plane_state *old_state) { struct drm_plane_state *state = pipe->plane.state; + struct drm_shadow_plane_state *shadow_plane_state = to_drm_shadow_plane_state(state); + struct drm_framebuffer *fb = state->fb; struct drm_rect rect; + int idx; if (!pipe->crtc.state->active) return; + if (WARN_ON(!fb)) + return; + + if (!drm_dev_enter(fb->dev, &idx)) + return; + if (drm_atomic_helper_damage_merged(old_state, state, &rect)) - mipi_dbi_fb_dirty(state->fb, &rect); + mipi_dbi_fb_dirty(&shadow_plane_state->data[0], fb, &rect); + + drm_dev_exit(idx); } EXPORT_SYMBOL(mipi_dbi_pipe_update); @@ -366,6 +355,7 @@ void mipi_dbi_enable_flush(struct mipi_dbi_dev *dbidev, struct drm_crtc_state *crtc_state, struct drm_plane_state *plane_state) { + struct drm_shadow_plane_state *shadow_plane_state = to_drm_shadow_plane_state(plane_state); struct drm_framebuffer *fb = plane_state->fb; struct drm_rect rect = { .x1 = 0, @@ -378,7 +368,7 @@ void mipi_dbi_enable_flush(struct mipi_dbi_dev *dbidev, if (!drm_dev_enter(&dbidev->drm, &idx)) return; - mipi_dbi_fb_dirty(fb, &rect); + mipi_dbi_fb_dirty(&shadow_plane_state->data[0], fb, &rect); backlight_enable(dbidev->backlight); drm_dev_exit(idx); @@ -427,9 +417,95 @@ void mipi_dbi_pipe_disable(struct drm_simple_display_pipe *pipe) if (dbidev->regulator) regulator_disable(dbidev->regulator); + if (dbidev->io_regulator) + regulator_disable(dbidev->io_regulator); } EXPORT_SYMBOL(mipi_dbi_pipe_disable); +/** + * mipi_dbi_pipe_begin_fb_access - MIPI DBI pipe begin-access helper + * @pipe: Display pipe + * @plane_state: Plane state + * + * This function implements struct &drm_simple_display_funcs.begin_fb_access. + * + * See drm_gem_begin_shadow_fb_access() for details and mipi_dbi_pipe_cleanup_fb() + * for cleanup. + * + * Returns: + * 0 on success, or a negative errno code otherwise. + */ +int mipi_dbi_pipe_begin_fb_access(struct drm_simple_display_pipe *pipe, + struct drm_plane_state *plane_state) +{ + return drm_gem_begin_shadow_fb_access(&pipe->plane, plane_state); +} +EXPORT_SYMBOL(mipi_dbi_pipe_begin_fb_access); + +/** + * mipi_dbi_pipe_end_fb_access - MIPI DBI pipe end-access helper + * @pipe: Display pipe + * @plane_state: Plane state + * + * This function implements struct &drm_simple_display_funcs.end_fb_access. + * + * See mipi_dbi_pipe_begin_fb_access(). + */ +void mipi_dbi_pipe_end_fb_access(struct drm_simple_display_pipe *pipe, + struct drm_plane_state *plane_state) +{ + drm_gem_end_shadow_fb_access(&pipe->plane, plane_state); +} +EXPORT_SYMBOL(mipi_dbi_pipe_end_fb_access); + +/** + * mipi_dbi_pipe_reset_plane - MIPI DBI plane-reset helper + * @pipe: Display pipe + * + * This function implements struct &drm_simple_display_funcs.reset_plane + * for MIPI DBI planes. + */ +void mipi_dbi_pipe_reset_plane(struct drm_simple_display_pipe *pipe) +{ + drm_gem_reset_shadow_plane(&pipe->plane); +} +EXPORT_SYMBOL(mipi_dbi_pipe_reset_plane); + +/** + * mipi_dbi_pipe_duplicate_plane_state - duplicates MIPI DBI plane state + * @pipe: Display pipe + * + * This function implements struct &drm_simple_display_funcs.duplicate_plane_state + * for MIPI DBI planes. + * + * See drm_gem_duplicate_shadow_plane_state() for additional details. + * + * Returns: + * A pointer to a new plane state on success, or NULL otherwise. + */ +struct drm_plane_state *mipi_dbi_pipe_duplicate_plane_state(struct drm_simple_display_pipe *pipe) +{ + return drm_gem_duplicate_shadow_plane_state(&pipe->plane); +} +EXPORT_SYMBOL(mipi_dbi_pipe_duplicate_plane_state); + +/** + * mipi_dbi_pipe_destroy_plane_state - cleans up MIPI DBI plane state + * @pipe: Display pipe + * @plane_state: Plane state + * + * This function implements struct drm_simple_display_funcs.destroy_plane_state + * for MIPI DBI planes. + * + * See drm_gem_destroy_shadow_plane_state() for additional details. + */ +void mipi_dbi_pipe_destroy_plane_state(struct drm_simple_display_pipe *pipe, + struct drm_plane_state *plane_state) +{ + drm_gem_destroy_shadow_plane_state(&pipe->plane, plane_state); +} +EXPORT_SYMBOL(mipi_dbi_pipe_destroy_plane_state); + static int mipi_dbi_connector_get_modes(struct drm_connector *connector) { struct mipi_dbi_dev *dbidev = drm_to_mipi_dbi_dev(connector->dev); @@ -652,6 +728,16 @@ static int mipi_dbi_poweron_reset_conditional(struct mipi_dbi_dev *dbidev, bool } } + if (dbidev->io_regulator) { + ret = regulator_enable(dbidev->io_regulator); + if (ret) { + DRM_DEV_ERROR(dev, "Failed to enable I/O regulator (%d)\n", ret); + if (dbidev->regulator) + regulator_disable(dbidev->regulator); + return ret; + } + } + if (cond && mipi_dbi_display_is_on(dbi)) return 1; @@ -661,6 +747,8 @@ static int mipi_dbi_poweron_reset_conditional(struct mipi_dbi_dev *dbidev, bool DRM_DEV_ERROR(dev, "Failed to send reset command (%d)\n", ret); if (dbidev->regulator) regulator_disable(dbidev->regulator); + if (dbidev->io_regulator) + regulator_disable(dbidev->io_regulator); return ret; } diff --git a/drivers/gpu/drm/drm_mode_config.c b/drivers/gpu/drm/drm_mode_config.c index 8525ef851540..87eb591fe9b5 100644 --- a/drivers/gpu/drm/drm_mode_config.c +++ b/drivers/gpu/drm/drm_mode_config.c @@ -54,6 +54,8 @@ int drm_modeset_register_all(struct drm_device *dev) if (ret) goto err_connector; + drm_debugfs_late_register(dev); + return 0; err_connector: diff --git a/drivers/gpu/drm/drm_modes.c b/drivers/gpu/drm/drm_modes.c index 3c8034a8c27b..be030f4a5311 100644 --- a/drivers/gpu/drm/drm_modes.c +++ b/drivers/gpu/drm/drm_modes.c @@ -116,6 +116,482 @@ void drm_mode_probed_add(struct drm_connector *connector, } EXPORT_SYMBOL(drm_mode_probed_add); +enum drm_mode_analog { + DRM_MODE_ANALOG_NTSC, /* 525 lines, 60Hz */ + DRM_MODE_ANALOG_PAL, /* 625 lines, 50Hz */ +}; + +/* + * The timings come from: + * - https://web.archive.org/web/20220406232708/http://www.kolumbus.fi/pami1/video/pal_ntsc.html + * - https://web.archive.org/web/20220406124914/http://martin.hinner.info/vga/pal.html + * - https://web.archive.org/web/20220609202433/http://www.batsocks.co.uk/readme/video_timing.htm + */ +#define NTSC_LINE_DURATION_NS 63556U +#define NTSC_LINES_NUMBER 525 + +#define NTSC_HBLK_DURATION_TYP_NS 10900U +#define NTSC_HBLK_DURATION_MIN_NS (NTSC_HBLK_DURATION_TYP_NS - 200) +#define NTSC_HBLK_DURATION_MAX_NS (NTSC_HBLK_DURATION_TYP_NS + 200) + +#define NTSC_HACT_DURATION_TYP_NS (NTSC_LINE_DURATION_NS - NTSC_HBLK_DURATION_TYP_NS) +#define NTSC_HACT_DURATION_MIN_NS (NTSC_LINE_DURATION_NS - NTSC_HBLK_DURATION_MAX_NS) +#define NTSC_HACT_DURATION_MAX_NS (NTSC_LINE_DURATION_NS - NTSC_HBLK_DURATION_MIN_NS) + +#define NTSC_HFP_DURATION_TYP_NS 1500 +#define NTSC_HFP_DURATION_MIN_NS 1270 +#define NTSC_HFP_DURATION_MAX_NS 2220 + +#define NTSC_HSLEN_DURATION_TYP_NS 4700 +#define NTSC_HSLEN_DURATION_MIN_NS (NTSC_HSLEN_DURATION_TYP_NS - 100) +#define NTSC_HSLEN_DURATION_MAX_NS (NTSC_HSLEN_DURATION_TYP_NS + 100) + +#define NTSC_HBP_DURATION_TYP_NS 4700 + +/* + * I couldn't find the actual tolerance for the back porch, so let's + * just reuse the sync length ones. + */ +#define NTSC_HBP_DURATION_MIN_NS (NTSC_HBP_DURATION_TYP_NS - 100) +#define NTSC_HBP_DURATION_MAX_NS (NTSC_HBP_DURATION_TYP_NS + 100) + +#define PAL_LINE_DURATION_NS 64000U +#define PAL_LINES_NUMBER 625 + +#define PAL_HACT_DURATION_TYP_NS 51950U +#define PAL_HACT_DURATION_MIN_NS (PAL_HACT_DURATION_TYP_NS - 100) +#define PAL_HACT_DURATION_MAX_NS (PAL_HACT_DURATION_TYP_NS + 400) + +#define PAL_HBLK_DURATION_TYP_NS (PAL_LINE_DURATION_NS - PAL_HACT_DURATION_TYP_NS) +#define PAL_HBLK_DURATION_MIN_NS (PAL_LINE_DURATION_NS - PAL_HACT_DURATION_MAX_NS) +#define PAL_HBLK_DURATION_MAX_NS (PAL_LINE_DURATION_NS - PAL_HACT_DURATION_MIN_NS) + +#define PAL_HFP_DURATION_TYP_NS 1650 +#define PAL_HFP_DURATION_MIN_NS (PAL_HFP_DURATION_TYP_NS - 100) +#define PAL_HFP_DURATION_MAX_NS (PAL_HFP_DURATION_TYP_NS + 400) + +#define PAL_HSLEN_DURATION_TYP_NS 4700 +#define PAL_HSLEN_DURATION_MIN_NS (PAL_HSLEN_DURATION_TYP_NS - 200) +#define PAL_HSLEN_DURATION_MAX_NS (PAL_HSLEN_DURATION_TYP_NS + 200) + +#define PAL_HBP_DURATION_TYP_NS 5700 +#define PAL_HBP_DURATION_MIN_NS (PAL_HBP_DURATION_TYP_NS - 200) +#define PAL_HBP_DURATION_MAX_NS (PAL_HBP_DURATION_TYP_NS + 200) + +struct analog_param_field { + unsigned int even, odd; +}; + +#define PARAM_FIELD(_odd, _even) \ + { .even = _even, .odd = _odd } + +struct analog_param_range { + unsigned int min, typ, max; +}; + +#define PARAM_RANGE(_min, _typ, _max) \ + { .min = _min, .typ = _typ, .max = _max } + +struct analog_parameters { + unsigned int num_lines; + unsigned int line_duration_ns; + + struct analog_param_range hact_ns; + struct analog_param_range hfp_ns; + struct analog_param_range hslen_ns; + struct analog_param_range hbp_ns; + struct analog_param_range hblk_ns; + + unsigned int bt601_hfp; + + struct analog_param_field vfp_lines; + struct analog_param_field vslen_lines; + struct analog_param_field vbp_lines; +}; + +#define TV_MODE_PARAMETER(_mode, _lines, _line_dur, _hact, _hfp, \ + _hslen, _hbp, _hblk, _bt601_hfp, _vfp, \ + _vslen, _vbp) \ + [_mode] = { \ + .num_lines = _lines, \ + .line_duration_ns = _line_dur, \ + .hact_ns = _hact, \ + .hfp_ns = _hfp, \ + .hslen_ns = _hslen, \ + .hbp_ns = _hbp, \ + .hblk_ns = _hblk, \ + .bt601_hfp = _bt601_hfp, \ + .vfp_lines = _vfp, \ + .vslen_lines = _vslen, \ + .vbp_lines = _vbp, \ + } + +static const struct analog_parameters tv_modes_parameters[] = { + TV_MODE_PARAMETER(DRM_MODE_ANALOG_NTSC, + NTSC_LINES_NUMBER, + NTSC_LINE_DURATION_NS, + PARAM_RANGE(NTSC_HACT_DURATION_MIN_NS, + NTSC_HACT_DURATION_TYP_NS, + NTSC_HACT_DURATION_MAX_NS), + PARAM_RANGE(NTSC_HFP_DURATION_MIN_NS, + NTSC_HFP_DURATION_TYP_NS, + NTSC_HFP_DURATION_MAX_NS), + PARAM_RANGE(NTSC_HSLEN_DURATION_MIN_NS, + NTSC_HSLEN_DURATION_TYP_NS, + NTSC_HSLEN_DURATION_MAX_NS), + PARAM_RANGE(NTSC_HBP_DURATION_MIN_NS, + NTSC_HBP_DURATION_TYP_NS, + NTSC_HBP_DURATION_MAX_NS), + PARAM_RANGE(NTSC_HBLK_DURATION_MIN_NS, + NTSC_HBLK_DURATION_TYP_NS, + NTSC_HBLK_DURATION_MAX_NS), + 16, + PARAM_FIELD(3, 3), + PARAM_FIELD(3, 3), + PARAM_FIELD(16, 17)), + TV_MODE_PARAMETER(DRM_MODE_ANALOG_PAL, + PAL_LINES_NUMBER, + PAL_LINE_DURATION_NS, + PARAM_RANGE(PAL_HACT_DURATION_MIN_NS, + PAL_HACT_DURATION_TYP_NS, + PAL_HACT_DURATION_MAX_NS), + PARAM_RANGE(PAL_HFP_DURATION_MIN_NS, + PAL_HFP_DURATION_TYP_NS, + PAL_HFP_DURATION_MAX_NS), + PARAM_RANGE(PAL_HSLEN_DURATION_MIN_NS, + PAL_HSLEN_DURATION_TYP_NS, + PAL_HSLEN_DURATION_MAX_NS), + PARAM_RANGE(PAL_HBP_DURATION_MIN_NS, + PAL_HBP_DURATION_TYP_NS, + PAL_HBP_DURATION_MAX_NS), + PARAM_RANGE(PAL_HBLK_DURATION_MIN_NS, + PAL_HBLK_DURATION_TYP_NS, + PAL_HBLK_DURATION_MAX_NS), + 12, + + /* + * The front porch is actually 6 short sync + * pulses for the even field, and 5 for the + * odd field. Each sync takes half a life so + * the odd field front porch is shorter by + * half a line. + * + * In progressive, we're supposed to use 6 + * pulses, so we're fine there + */ + PARAM_FIELD(3, 2), + + /* + * The vsync length is 5 long sync pulses, + * each field taking half a line. We're + * shorter for both fields by half a line. + * + * In progressive, we're supposed to use 5 + * pulses, so we're off by half + * a line. + * + * In interlace, we're now off by half a line + * for the even field and one line for the odd + * field. + */ + PARAM_FIELD(3, 3), + + /* + * The back porch starts with post-equalizing + * pulses, consisting in 5 short sync pulses + * for the even field, 4 for the odd field. In + * progressive, it's 5 short syncs. + * + * In progressive, we thus have 2.5 lines, + * plus the 0.5 line we were missing + * previously, so we should use 3 lines. + * + * In interlace, the even field is in the + * exact same case than progressive. For the + * odd field, we should be using 2 lines but + * we're one line short, so we'll make up for + * it here by using 3. + * + * The entire blanking area is supposed to + * take 25 lines, so we also need to account + * for the rest of the blanking area that + * can't be in either the front porch or sync + * period. + */ + PARAM_FIELD(19, 20)), +}; + +static int fill_analog_mode(struct drm_device *dev, + struct drm_display_mode *mode, + const struct analog_parameters *params, + unsigned long pixel_clock_hz, + unsigned int hactive, + unsigned int vactive, + bool interlace) +{ + unsigned long pixel_duration_ns = NSEC_PER_SEC / pixel_clock_hz; + unsigned int htotal, vtotal; + unsigned int max_hact, hact_duration_ns; + unsigned int hblk, hblk_duration_ns; + unsigned int hfp, hfp_duration_ns; + unsigned int hslen, hslen_duration_ns; + unsigned int hbp, hbp_duration_ns; + unsigned int porches, porches_duration_ns; + unsigned int vfp, vfp_min; + unsigned int vbp, vbp_min; + unsigned int vslen; + bool bt601 = false; + int porches_rem; + u64 result; + + drm_dbg_kms(dev, + "Generating a %ux%u%c, %u-line mode with a %lu kHz clock\n", + hactive, vactive, + interlace ? 'i' : 'p', + params->num_lines, + pixel_clock_hz / 1000); + + max_hact = params->hact_ns.max / pixel_duration_ns; + if (pixel_clock_hz == 13500000 && hactive > max_hact && hactive <= 720) { + drm_dbg_kms(dev, "Trying to generate a BT.601 mode. Disabling checks.\n"); + bt601 = true; + } + + /* + * Our pixel duration is going to be round down by the division, + * so rounding up is probably going to introduce even more + * deviation. + */ + result = (u64)params->line_duration_ns * pixel_clock_hz; + do_div(result, NSEC_PER_SEC); + htotal = result; + + drm_dbg_kms(dev, "Total Horizontal Number of Pixels: %u\n", htotal); + + hact_duration_ns = hactive * pixel_duration_ns; + if (!bt601 && + (hact_duration_ns < params->hact_ns.min || + hact_duration_ns > params->hact_ns.max)) { + DRM_ERROR("Invalid horizontal active area duration: %uns (min: %u, max %u)\n", + hact_duration_ns, params->hact_ns.min, params->hact_ns.max); + return -EINVAL; + } + + hblk = htotal - hactive; + drm_dbg_kms(dev, "Horizontal Blanking Period: %u\n", hblk); + + hblk_duration_ns = hblk * pixel_duration_ns; + if (!bt601 && + (hblk_duration_ns < params->hblk_ns.min || + hblk_duration_ns > params->hblk_ns.max)) { + DRM_ERROR("Invalid horizontal blanking duration: %uns (min: %u, max %u)\n", + hblk_duration_ns, params->hblk_ns.min, params->hblk_ns.max); + return -EINVAL; + } + + hslen = DIV_ROUND_UP(params->hslen_ns.typ, pixel_duration_ns); + drm_dbg_kms(dev, "Horizontal Sync Period: %u\n", hslen); + + hslen_duration_ns = hslen * pixel_duration_ns; + if (!bt601 && + (hslen_duration_ns < params->hslen_ns.min || + hslen_duration_ns > params->hslen_ns.max)) { + DRM_ERROR("Invalid horizontal sync duration: %uns (min: %u, max %u)\n", + hslen_duration_ns, params->hslen_ns.min, params->hslen_ns.max); + return -EINVAL; + } + + porches = hblk - hslen; + drm_dbg_kms(dev, "Remaining horizontal pixels for both porches: %u\n", porches); + + porches_duration_ns = porches * pixel_duration_ns; + if (!bt601 && + (porches_duration_ns > (params->hfp_ns.max + params->hbp_ns.max) || + porches_duration_ns < (params->hfp_ns.min + params->hbp_ns.min))) { + DRM_ERROR("Invalid horizontal porches duration: %uns\n", porches_duration_ns); + return -EINVAL; + } + + if (bt601) { + hfp = params->bt601_hfp; + } else { + unsigned int hfp_min = DIV_ROUND_UP(params->hfp_ns.min, + pixel_duration_ns); + unsigned int hbp_min = DIV_ROUND_UP(params->hbp_ns.min, + pixel_duration_ns); + int porches_rem = porches - hfp_min - hbp_min; + + hfp = hfp_min + DIV_ROUND_UP(porches_rem, 2); + } + + drm_dbg_kms(dev, "Horizontal Front Porch: %u\n", hfp); + + hfp_duration_ns = hfp * pixel_duration_ns; + if (!bt601 && + (hfp_duration_ns < params->hfp_ns.min || + hfp_duration_ns > params->hfp_ns.max)) { + DRM_ERROR("Invalid horizontal front porch duration: %uns (min: %u, max %u)\n", + hfp_duration_ns, params->hfp_ns.min, params->hfp_ns.max); + return -EINVAL; + } + + hbp = porches - hfp; + drm_dbg_kms(dev, "Horizontal Back Porch: %u\n", hbp); + + hbp_duration_ns = hbp * pixel_duration_ns; + if (!bt601 && + (hbp_duration_ns < params->hbp_ns.min || + hbp_duration_ns > params->hbp_ns.max)) { + DRM_ERROR("Invalid horizontal back porch duration: %uns (min: %u, max %u)\n", + hbp_duration_ns, params->hbp_ns.min, params->hbp_ns.max); + return -EINVAL; + } + + if (htotal != (hactive + hfp + hslen + hbp)) + return -EINVAL; + + mode->clock = pixel_clock_hz / 1000; + mode->hdisplay = hactive; + mode->hsync_start = mode->hdisplay + hfp; + mode->hsync_end = mode->hsync_start + hslen; + mode->htotal = mode->hsync_end + hbp; + + if (interlace) { + vfp_min = params->vfp_lines.even + params->vfp_lines.odd; + vbp_min = params->vbp_lines.even + params->vbp_lines.odd; + vslen = params->vslen_lines.even + params->vslen_lines.odd; + } else { + /* + * By convention, NTSC (aka 525/60) systems start with + * the even field, but PAL (aka 625/50) systems start + * with the odd one. + * + * PAL systems also have asymmetric timings between the + * even and odd field, while NTSC is symmetric. + * + * Moreover, if we want to create a progressive mode for + * PAL, we need to use the odd field timings. + * + * Since odd == even for NTSC, we can just use the odd + * one all the time to simplify the code a bit. + */ + vfp_min = params->vfp_lines.odd; + vbp_min = params->vbp_lines.odd; + vslen = params->vslen_lines.odd; + } + + drm_dbg_kms(dev, "Vertical Sync Period: %u\n", vslen); + + porches = params->num_lines - vactive - vslen; + drm_dbg_kms(dev, "Remaining vertical pixels for both porches: %u\n", porches); + + porches_rem = porches - vfp_min - vbp_min; + vfp = vfp_min + (porches_rem / 2); + drm_dbg_kms(dev, "Vertical Front Porch: %u\n", vfp); + + vbp = porches - vfp; + drm_dbg_kms(dev, "Vertical Back Porch: %u\n", vbp); + + vtotal = vactive + vfp + vslen + vbp; + if (params->num_lines != vtotal) { + DRM_ERROR("Invalid vertical total: %upx (expected %upx)\n", + vtotal, params->num_lines); + return -EINVAL; + } + + mode->vdisplay = vactive; + mode->vsync_start = mode->vdisplay + vfp; + mode->vsync_end = mode->vsync_start + vslen; + mode->vtotal = mode->vsync_end + vbp; + + if (mode->vtotal != params->num_lines) + return -EINVAL; + + mode->type = DRM_MODE_TYPE_DRIVER; + mode->flags = DRM_MODE_FLAG_NVSYNC | DRM_MODE_FLAG_NHSYNC; + if (interlace) + mode->flags |= DRM_MODE_FLAG_INTERLACE; + + drm_mode_set_name(mode); + + drm_dbg_kms(dev, "Generated mode " DRM_MODE_FMT "\n", DRM_MODE_ARG(mode)); + + return 0; +} + +/** + * drm_analog_tv_mode - create a display mode for an analog TV + * @dev: drm device + * @tv_mode: TV Mode standard to create a mode for. See DRM_MODE_TV_MODE_*. + * @pixel_clock_hz: Pixel Clock Frequency, in Hertz + * @hdisplay: hdisplay size + * @vdisplay: vdisplay size + * @interlace: whether to compute an interlaced mode + * + * This function creates a struct drm_display_mode instance suited for + * an analog TV output, for one of the usual analog TV mode. + * + * Note that @hdisplay is larger than the usual constraints for the PAL + * and NTSC timings, and we'll choose to ignore most timings constraints + * to reach those resolutions. + * + * Returns: + * + * A pointer to the mode, allocated with drm_mode_create(). Returns NULL + * on error. + */ +struct drm_display_mode *drm_analog_tv_mode(struct drm_device *dev, + enum drm_connector_tv_mode tv_mode, + unsigned long pixel_clock_hz, + unsigned int hdisplay, + unsigned int vdisplay, + bool interlace) +{ + struct drm_display_mode *mode; + enum drm_mode_analog analog; + int ret; + + switch (tv_mode) { + case DRM_MODE_TV_MODE_NTSC: + fallthrough; + case DRM_MODE_TV_MODE_NTSC_443: + fallthrough; + case DRM_MODE_TV_MODE_NTSC_J: + fallthrough; + case DRM_MODE_TV_MODE_PAL_M: + analog = DRM_MODE_ANALOG_NTSC; + break; + + case DRM_MODE_TV_MODE_PAL: + fallthrough; + case DRM_MODE_TV_MODE_PAL_N: + fallthrough; + case DRM_MODE_TV_MODE_SECAM: + analog = DRM_MODE_ANALOG_PAL; + break; + + default: + return NULL; + } + + mode = drm_mode_create(dev); + if (!mode) + return NULL; + + ret = fill_analog_mode(dev, mode, + &tv_modes_parameters[analog], + pixel_clock_hz, hdisplay, vdisplay, interlace); + if (ret) + goto err_free_mode; + + return mode; + +err_free_mode: + drm_mode_destroy(dev, mode); + return NULL; +} +EXPORT_SYMBOL(drm_analog_tv_mode); + /** * drm_cvt_mode -create a modeline based on the CVT algorithm * @dev: drm device @@ -1659,6 +2135,30 @@ static int drm_mode_parse_panel_orientation(const char *delim, return 0; } +static int drm_mode_parse_tv_mode(const char *delim, + struct drm_cmdline_mode *mode) +{ + const char *value; + int ret; + + if (*delim != '=') + return -EINVAL; + + value = delim + 1; + delim = strchr(value, ','); + if (!delim) + delim = value + strlen(value); + + ret = drm_get_tv_mode_from_name(value, delim - value); + if (ret < 0) + return ret; + + mode->tv_mode_specified = true; + mode->tv_mode = ret; + + return 0; +} + static int drm_mode_parse_cmdline_options(const char *str, bool freestanding, const struct drm_connector *connector, @@ -1728,6 +2228,9 @@ static int drm_mode_parse_cmdline_options(const char *str, } else if (!strncmp(option, "panel_orientation", delim - option)) { if (drm_mode_parse_panel_orientation(delim, mode)) return -EINVAL; + } else if (!strncmp(option, "tv_mode", delim - option)) { + if (drm_mode_parse_tv_mode(delim, mode)) + return -EINVAL; } else { return -EINVAL; } @@ -1756,20 +2259,24 @@ struct drm_named_mode { unsigned int xres; unsigned int yres; unsigned int flags; + unsigned int tv_mode; }; -#define NAMED_MODE(_name, _pclk, _x, _y, _flags) \ +#define NAMED_MODE(_name, _pclk, _x, _y, _flags, _mode) \ { \ .name = _name, \ .pixel_clock_khz = _pclk, \ .xres = _x, \ .yres = _y, \ .flags = _flags, \ + .tv_mode = _mode, \ } static const struct drm_named_mode drm_named_modes[] = { - NAMED_MODE("NTSC", 13500, 720, 480, DRM_MODE_FLAG_INTERLACE), - NAMED_MODE("PAL", 13500, 720, 576, DRM_MODE_FLAG_INTERLACE), + NAMED_MODE("NTSC", 13500, 720, 480, DRM_MODE_FLAG_INTERLACE, DRM_MODE_TV_MODE_NTSC), + NAMED_MODE("NTSC-J", 13500, 720, 480, DRM_MODE_FLAG_INTERLACE, DRM_MODE_TV_MODE_NTSC_J), + NAMED_MODE("PAL", 13500, 720, 576, DRM_MODE_FLAG_INTERLACE, DRM_MODE_TV_MODE_PAL), + NAMED_MODE("PAL-M", 13500, 720, 480, DRM_MODE_FLAG_INTERLACE, DRM_MODE_TV_MODE_PAL_M), }; static int drm_mode_parse_cmdline_named_mode(const char *name, @@ -1809,11 +2316,13 @@ static int drm_mode_parse_cmdline_named_mode(const char *name, if (ret != name_end) continue; - strcpy(cmdline_mode->name, mode->name); + strscpy(cmdline_mode->name, mode->name, sizeof(cmdline_mode->name)); cmdline_mode->pixel_clock = mode->pixel_clock_khz; cmdline_mode->xres = mode->xres; cmdline_mode->yres = mode->yres; cmdline_mode->interlace = !!(mode->flags & DRM_MODE_FLAG_INTERLACE); + cmdline_mode->tv_mode = mode->tv_mode; + cmdline_mode->tv_mode_specified = true; cmdline_mode->specified = true; return 1; @@ -1992,6 +2501,31 @@ bool drm_mode_parse_command_line_for_connector(const char *mode_option, } EXPORT_SYMBOL(drm_mode_parse_command_line_for_connector); +static struct drm_display_mode *drm_named_mode(struct drm_device *dev, + struct drm_cmdline_mode *cmd) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(drm_named_modes); i++) { + const struct drm_named_mode *named_mode = &drm_named_modes[i]; + + if (strcmp(cmd->name, named_mode->name)) + continue; + + if (!cmd->tv_mode_specified) + continue; + + return drm_analog_tv_mode(dev, + named_mode->tv_mode, + named_mode->pixel_clock_khz * 1000, + named_mode->xres, + named_mode->yres, + named_mode->flags & DRM_MODE_FLAG_INTERLACE); + } + + return NULL; +} + /** * drm_mode_create_from_cmdline_mode - convert a command line modeline into a DRM display mode * @dev: DRM device to create the new mode for @@ -2009,7 +2543,9 @@ drm_mode_create_from_cmdline_mode(struct drm_device *dev, if (cmd->xres == 0 || cmd->yres == 0) return NULL; - if (cmd->cvt) + if (strlen(cmd->name)) + mode = drm_named_mode(dev, cmd); + else if (cmd->cvt) mode = drm_cvt_mode(dev, cmd->xres, cmd->yres, cmd->refresh_specified ? cmd->refresh : 60, diff --git a/drivers/gpu/drm/drm_panel_orientation_quirks.c b/drivers/gpu/drm/drm_panel_orientation_quirks.c index 52d8800a8ab8..ca531dbb749d 100644 --- a/drivers/gpu/drm/drm_panel_orientation_quirks.c +++ b/drivers/gpu/drm/drm_panel_orientation_quirks.c @@ -30,12 +30,6 @@ struct drm_dmi_panel_orientation_data { int orientation; }; -static const struct drm_dmi_panel_orientation_data asus_t100ha = { - .width = 800, - .height = 1280, - .orientation = DRM_MODE_PANEL_ORIENTATION_LEFT_UP, -}; - static const struct drm_dmi_panel_orientation_data gpd_micropc = { .width = 720, .height = 1280, @@ -97,6 +91,12 @@ static const struct drm_dmi_panel_orientation_data lcd720x1280_rightside_up = { .orientation = DRM_MODE_PANEL_ORIENTATION_RIGHT_UP, }; +static const struct drm_dmi_panel_orientation_data lcd800x1280_leftside_up = { + .width = 800, + .height = 1280, + .orientation = DRM_MODE_PANEL_ORIENTATION_LEFT_UP, +}; + static const struct drm_dmi_panel_orientation_data lcd800x1280_rightside_up = { .width = 800, .height = 1280, @@ -127,6 +127,12 @@ static const struct drm_dmi_panel_orientation_data lcd1600x2560_leftside_up = { .orientation = DRM_MODE_PANEL_ORIENTATION_LEFT_UP, }; +static const struct drm_dmi_panel_orientation_data lcd1600x2560_rightside_up = { + .width = 1600, + .height = 2560, + .orientation = DRM_MODE_PANEL_ORIENTATION_RIGHT_UP, +}; + static const struct dmi_system_id orientation_data[] = { { /* Acer One 10 (S1003) */ .matches = { @@ -151,7 +157,7 @@ static const struct dmi_system_id orientation_data[] = { DMI_EXACT_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "T100HAN"), }, - .driver_data = (void *)&asus_t100ha, + .driver_data = (void *)&lcd800x1280_leftside_up, }, { /* Asus T101HA */ .matches = { DMI_EXACT_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), @@ -196,6 +202,12 @@ static const struct dmi_system_id orientation_data[] = { DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Hi10 pro tablet"), }, .driver_data = (void *)&lcd1200x1920_rightside_up, + }, { /* Dynabook K50 */ + .matches = { + DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Dynabook Inc."), + DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "dynabook K50/FR"), + }, + .driver_data = (void *)&lcd800x1280_leftside_up, }, { /* GPD MicroPC (generic strings, also match on bios date) */ .matches = { DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Default string"), @@ -325,6 +337,13 @@ static const struct dmi_system_id orientation_data[] = { DMI_MATCH(DMI_BIOS_VERSION, "BLADE_21"), }, .driver_data = (void *)&lcd1200x1920_rightside_up, + }, { /* Lenovo Yoga Tab 3 X90F */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Intel Corporation"), + DMI_MATCH(DMI_PRODUCT_NAME, "CHERRYVIEW D1 PLATFORM"), + DMI_MATCH(DMI_PRODUCT_VERSION, "Blade3-10A-001"), + }, + .driver_data = (void *)&lcd1600x2560_rightside_up, }, { /* Nanote UMPC-01 */ .matches = { DMI_MATCH(DMI_SYS_VENDOR, "RWC CO.,LTD"), diff --git a/drivers/gpu/drm/drm_probe_helper.c b/drivers/gpu/drm/drm_probe_helper.c index bcd9611dabfd..1ea053cef557 100644 --- a/drivers/gpu/drm/drm_probe_helper.c +++ b/drivers/gpu/drm/drm_probe_helper.c @@ -1146,3 +1146,85 @@ int drm_connector_helper_get_modes(struct drm_connector *connector) return count; } EXPORT_SYMBOL(drm_connector_helper_get_modes); + +/** + * drm_connector_helper_tv_get_modes - Fills the modes availables to a TV connector + * @connector: The connector + * + * Fills the available modes for a TV connector based on the supported + * TV modes, and the default mode expressed by the kernel command line. + * + * This can be used as the default TV connector helper .get_modes() hook + * if the driver does not need any special processing. + * + * Returns: + * The number of modes added to the connector. + */ +int drm_connector_helper_tv_get_modes(struct drm_connector *connector) +{ + struct drm_device *dev = connector->dev; + struct drm_property *tv_mode_property = + dev->mode_config.tv_mode_property; + struct drm_cmdline_mode *cmdline = &connector->cmdline_mode; + unsigned int ntsc_modes = 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_M); + unsigned int pal_modes = BIT(DRM_MODE_TV_MODE_PAL) | + BIT(DRM_MODE_TV_MODE_PAL_N) | + BIT(DRM_MODE_TV_MODE_SECAM); + unsigned int tv_modes[2] = { UINT_MAX, UINT_MAX }; + unsigned int i, supported_tv_modes = 0; + + if (!tv_mode_property) + return 0; + + for (i = 0; i < tv_mode_property->num_values; i++) + supported_tv_modes |= BIT(tv_mode_property->values[i]); + + if ((supported_tv_modes & ntsc_modes) && + (supported_tv_modes & pal_modes)) { + uint64_t default_mode; + + if (drm_object_property_get_default_value(&connector->base, + tv_mode_property, + &default_mode)) + return 0; + + if (cmdline->tv_mode_specified) + default_mode = cmdline->tv_mode; + + if (BIT(default_mode) & ntsc_modes) { + tv_modes[0] = DRM_MODE_TV_MODE_NTSC; + tv_modes[1] = DRM_MODE_TV_MODE_PAL; + } else { + tv_modes[0] = DRM_MODE_TV_MODE_PAL; + tv_modes[1] = DRM_MODE_TV_MODE_NTSC; + } + } else if (supported_tv_modes & ntsc_modes) { + tv_modes[0] = DRM_MODE_TV_MODE_NTSC; + } else if (supported_tv_modes & pal_modes) { + tv_modes[0] = DRM_MODE_TV_MODE_PAL; + } else { + return 0; + } + + for (i = 0; i < ARRAY_SIZE(tv_modes); i++) { + struct drm_display_mode *mode; + + if (tv_modes[i] == DRM_MODE_TV_MODE_NTSC) + mode = drm_mode_analog_ntsc_480i(dev); + else if (tv_modes[i] == DRM_MODE_TV_MODE_PAL) + mode = drm_mode_analog_pal_576i(dev); + else + break; + if (!mode) + return i; + if (!i) + mode->type |= DRM_MODE_TYPE_PREFERRED; + drm_mode_probed_add(connector, mode); + } + + return i; +} +EXPORT_SYMBOL(drm_connector_helper_tv_get_modes); diff --git a/drivers/gpu/drm/drm_simple_kms_helper.c b/drivers/gpu/drm/drm_simple_kms_helper.c index 3ef420ec4534..270523ae36d4 100644 --- a/drivers/gpu/drm/drm_simple_kms_helper.c +++ b/drivers/gpu/drm/drm_simple_kms_helper.c @@ -267,7 +267,7 @@ static int drm_simple_kms_plane_prepare_fb(struct drm_plane *plane, WARN_ON_ONCE(pipe->funcs && pipe->funcs->cleanup_fb); - return drm_gem_simple_display_pipe_prepare_fb(pipe, state); + return drm_gem_plane_helper_prepare_fb(plane, state); } return pipe->funcs->prepare_fb(pipe, state); diff --git a/drivers/gpu/drm/exynos/exynos5433_drm_decon.c b/drivers/gpu/drm/exynos/exynos5433_drm_decon.c index 8155d7e650f1..2867b39fa35e 100644 --- a/drivers/gpu/drm/exynos/exynos5433_drm_decon.c +++ b/drivers/gpu/drm/exynos/exynos5433_drm_decon.c @@ -710,7 +710,6 @@ static irqreturn_t decon_irq_handler(int irq, void *dev_id) return IRQ_HANDLED; } -#ifdef CONFIG_PM static int exynos5433_decon_suspend(struct device *dev) { struct decon_context *ctx = dev_get_drvdata(dev); @@ -741,14 +740,10 @@ err: return ret; } -#endif -static const struct dev_pm_ops exynos5433_decon_pm_ops = { - SET_RUNTIME_PM_OPS(exynos5433_decon_suspend, exynos5433_decon_resume, - NULL) - SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, - pm_runtime_force_resume) -}; +static DEFINE_RUNTIME_DEV_PM_OPS(exynos5433_decon_pm_ops, + exynos5433_decon_suspend, + exynos5433_decon_resume, NULL); static const struct of_device_id exynos5433_decon_driver_dt_match[] = { { @@ -881,7 +876,7 @@ struct platform_driver exynos5433_decon_driver = { .remove = exynos5433_decon_remove, .driver = { .name = "exynos5433-decon", - .pm = &exynos5433_decon_pm_ops, + .pm = pm_ptr(&exynos5433_decon_pm_ops), .of_match_table = exynos5433_decon_driver_dt_match, }, }; diff --git a/drivers/gpu/drm/exynos/exynos7_drm_decon.c b/drivers/gpu/drm/exynos/exynos7_drm_decon.c index 7080cf7952ec..3126f735dedc 100644 --- a/drivers/gpu/drm/exynos/exynos7_drm_decon.c +++ b/drivers/gpu/drm/exynos/exynos7_drm_decon.c @@ -779,7 +779,6 @@ static int decon_remove(struct platform_device *pdev) return 0; } -#ifdef CONFIG_PM static int exynos7_decon_suspend(struct device *dev) { struct decon_context *ctx = dev_get_drvdata(dev); @@ -836,21 +835,16 @@ err_aclk_enable: err_pclk_enable: return ret; } -#endif -static const struct dev_pm_ops exynos7_decon_pm_ops = { - SET_RUNTIME_PM_OPS(exynos7_decon_suspend, exynos7_decon_resume, - NULL) - SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, - pm_runtime_force_resume) -}; +static DEFINE_RUNTIME_DEV_PM_OPS(exynos7_decon_pm_ops, exynos7_decon_suspend, + exynos7_decon_resume, NULL); struct platform_driver decon_driver = { .probe = decon_probe, .remove = decon_remove, .driver = { .name = "exynos-decon", - .pm = &exynos7_decon_pm_ops, + .pm = pm_ptr(&exynos7_decon_pm_ops), .of_match_table = decon_driver_dt_match, }, }; diff --git a/drivers/gpu/drm/exynos/exynos_dp.c b/drivers/gpu/drm/exynos/exynos_dp.c index 4e3d3d5f6866..3404ec1367fb 100644 --- a/drivers/gpu/drm/exynos/exynos_dp.c +++ b/drivers/gpu/drm/exynos/exynos_dp.c @@ -260,7 +260,6 @@ static int exynos_dp_remove(struct platform_device *pdev) return 0; } -#ifdef CONFIG_PM static int exynos_dp_suspend(struct device *dev) { struct exynos_dp_device *dp = dev_get_drvdata(dev); @@ -274,13 +273,9 @@ static int exynos_dp_resume(struct device *dev) return analogix_dp_resume(dp->adp); } -#endif -static const struct dev_pm_ops exynos_dp_pm_ops = { - SET_RUNTIME_PM_OPS(exynos_dp_suspend, exynos_dp_resume, NULL) - SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, - pm_runtime_force_resume) -}; +static DEFINE_RUNTIME_DEV_PM_OPS(exynos_dp_pm_ops, exynos_dp_suspend, + exynos_dp_resume, NULL); static const struct of_device_id exynos_dp_match[] = { { .compatible = "samsung,exynos5-dp" }, @@ -294,7 +289,7 @@ struct platform_driver dp_driver = { .driver = { .name = "exynos-dp", .owner = THIS_MODULE, - .pm = &exynos_dp_pm_ops, + .pm = pm_ptr(&exynos_dp_pm_ops), .of_match_table = exynos_dp_match, }, }; diff --git a/drivers/gpu/drm/exynos/exynos_drm_fimc.c b/drivers/gpu/drm/exynos/exynos_drm_fimc.c index 0ee32e4b1e43..8de2714599fc 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_fimc.c +++ b/drivers/gpu/drm/exynos/exynos_drm_fimc.c @@ -1381,7 +1381,6 @@ static int fimc_remove(struct platform_device *pdev) return 0; } -#ifdef CONFIG_PM static int fimc_runtime_suspend(struct device *dev) { struct fimc_context *ctx = get_fimc_context(dev); @@ -1398,13 +1397,9 @@ static int fimc_runtime_resume(struct device *dev) DRM_DEV_DEBUG_KMS(dev, "id[%d]\n", ctx->id); return clk_prepare_enable(ctx->clocks[FIMC_CLK_GATE]); } -#endif -static const struct dev_pm_ops fimc_pm_ops = { - SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, - pm_runtime_force_resume) - SET_RUNTIME_PM_OPS(fimc_runtime_suspend, fimc_runtime_resume, NULL) -}; +static DEFINE_RUNTIME_DEV_PM_OPS(fimc_pm_ops, fimc_runtime_suspend, + fimc_runtime_resume, NULL); static const struct of_device_id fimc_of_match[] = { { .compatible = "samsung,exynos4210-fimc" }, @@ -1420,6 +1415,6 @@ struct platform_driver fimc_driver = { .of_match_table = fimc_of_match, .name = "exynos-drm-fimc", .owner = THIS_MODULE, - .pm = &fimc_pm_ops, + .pm = pm_ptr(&fimc_pm_ops), }, }; diff --git a/drivers/gpu/drm/exynos/exynos_drm_fimd.c b/drivers/gpu/drm/exynos/exynos_drm_fimd.c index ae6636e6658e..7f4a0be03dd1 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_fimd.c +++ b/drivers/gpu/drm/exynos/exynos_drm_fimd.c @@ -1287,7 +1287,6 @@ static int fimd_remove(struct platform_device *pdev) return 0; } -#ifdef CONFIG_PM static int exynos_fimd_suspend(struct device *dev) { struct fimd_context *ctx = dev_get_drvdata(dev); @@ -1321,13 +1320,9 @@ static int exynos_fimd_resume(struct device *dev) return 0; } -#endif -static const struct dev_pm_ops exynos_fimd_pm_ops = { - SET_RUNTIME_PM_OPS(exynos_fimd_suspend, exynos_fimd_resume, NULL) - SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, - pm_runtime_force_resume) -}; +static DEFINE_RUNTIME_DEV_PM_OPS(exynos_fimd_pm_ops, exynos_fimd_suspend, + exynos_fimd_resume, NULL); struct platform_driver fimd_driver = { .probe = fimd_probe, @@ -1335,7 +1330,7 @@ struct platform_driver fimd_driver = { .driver = { .name = "exynos4-fb", .owner = THIS_MODULE, - .pm = &exynos_fimd_pm_ops, + .pm = pm_ptr(&exynos_fimd_pm_ops), .of_match_table = fimd_driver_dt_match, }, }; diff --git a/drivers/gpu/drm/exynos/exynos_drm_g2d.c b/drivers/gpu/drm/exynos/exynos_drm_g2d.c index e19c2ceb3759..ec784e58da5c 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_g2d.c +++ b/drivers/gpu/drm/exynos/exynos_drm_g2d.c @@ -1549,7 +1549,6 @@ static int g2d_remove(struct platform_device *pdev) return 0; } -#ifdef CONFIG_PM_SLEEP static int g2d_suspend(struct device *dev) { struct g2d_data *g2d = dev_get_drvdata(dev); @@ -1574,9 +1573,7 @@ static int g2d_resume(struct device *dev) return 0; } -#endif -#ifdef CONFIG_PM static int g2d_runtime_suspend(struct device *dev) { struct g2d_data *g2d = dev_get_drvdata(dev); @@ -1597,11 +1594,10 @@ static int g2d_runtime_resume(struct device *dev) return ret; } -#endif static const struct dev_pm_ops g2d_pm_ops = { - SET_SYSTEM_SLEEP_PM_OPS(g2d_suspend, g2d_resume) - SET_RUNTIME_PM_OPS(g2d_runtime_suspend, g2d_runtime_resume, NULL) + SYSTEM_SLEEP_PM_OPS(g2d_suspend, g2d_resume) + RUNTIME_PM_OPS(g2d_runtime_suspend, g2d_runtime_resume, NULL) }; static const struct of_device_id exynos_g2d_match[] = { @@ -1617,7 +1613,7 @@ struct platform_driver g2d_driver = { .driver = { .name = "exynos-drm-g2d", .owner = THIS_MODULE, - .pm = &g2d_pm_ops, + .pm = pm_ptr(&g2d_pm_ops), .of_match_table = exynos_g2d_match, }, }; diff --git a/drivers/gpu/drm/exynos/exynos_drm_mic.c b/drivers/gpu/drm/exynos/exynos_drm_mic.c index 09ce28ee08d9..17bab5b1663f 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_mic.c +++ b/drivers/gpu/drm/exynos/exynos_drm_mic.c @@ -340,7 +340,6 @@ static const struct component_ops exynos_mic_component_ops = { .unbind = exynos_mic_unbind, }; -#ifdef CONFIG_PM static int exynos_mic_suspend(struct device *dev) { struct exynos_mic *mic = dev_get_drvdata(dev); @@ -369,13 +368,9 @@ static int exynos_mic_resume(struct device *dev) } return 0; } -#endif -static const struct dev_pm_ops exynos_mic_pm_ops = { - SET_RUNTIME_PM_OPS(exynos_mic_suspend, exynos_mic_resume, NULL) - SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, - pm_runtime_force_resume) -}; +static DEFINE_RUNTIME_DEV_PM_OPS(exynos_mic_pm_ops, exynos_mic_suspend, + exynos_mic_resume, NULL); static int exynos_mic_probe(struct platform_device *pdev) { @@ -470,7 +465,7 @@ struct platform_driver mic_driver = { .remove = exynos_mic_remove, .driver = { .name = "exynos-mic", - .pm = &exynos_mic_pm_ops, + .pm = pm_ptr(&exynos_mic_pm_ops), .owner = THIS_MODULE, .of_match_table = exynos_mic_of_match, }, diff --git a/drivers/gpu/drm/exynos/exynos_drm_rotator.c b/drivers/gpu/drm/exynos/exynos_drm_rotator.c index dec7df35baa9..8706f377c349 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_rotator.c +++ b/drivers/gpu/drm/exynos/exynos_drm_rotator.c @@ -340,7 +340,6 @@ static int rotator_remove(struct platform_device *pdev) return 0; } -#ifdef CONFIG_PM static int rotator_runtime_suspend(struct device *dev) { struct rot_context *rot = dev_get_drvdata(dev); @@ -355,7 +354,6 @@ static int rotator_runtime_resume(struct device *dev) return clk_prepare_enable(rot->clock); } -#endif static const struct drm_exynos_ipp_limit rotator_s5pv210_rbg888_limits[] = { { IPP_SIZE_LIMIT(BUFFER, .h = { 8, SZ_16K }, .v = { 8, SZ_16K }) }, @@ -450,12 +448,8 @@ static const struct of_device_id exynos_rotator_match[] = { }; MODULE_DEVICE_TABLE(of, exynos_rotator_match); -static const struct dev_pm_ops rotator_pm_ops = { - SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, - pm_runtime_force_resume) - SET_RUNTIME_PM_OPS(rotator_runtime_suspend, rotator_runtime_resume, - NULL) -}; +static DEFINE_RUNTIME_DEV_PM_OPS(rotator_pm_ops, rotator_runtime_suspend, + rotator_runtime_resume, NULL); struct platform_driver rotator_driver = { .probe = rotator_probe, @@ -463,7 +457,7 @@ struct platform_driver rotator_driver = { .driver = { .name = "exynos-rotator", .owner = THIS_MODULE, - .pm = &rotator_pm_ops, + .pm = pm_ptr(&rotator_pm_ops), .of_match_table = exynos_rotator_match, }, }; diff --git a/drivers/gpu/drm/exynos/exynos_drm_scaler.c b/drivers/gpu/drm/exynos/exynos_drm_scaler.c index 3c049fb658a3..20608e9780ce 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_scaler.c +++ b/drivers/gpu/drm/exynos/exynos_drm_scaler.c @@ -550,8 +550,6 @@ static int scaler_remove(struct platform_device *pdev) return 0; } -#ifdef CONFIG_PM - static int clk_disable_unprepare_wrapper(struct clk *clk) { clk_disable_unprepare(clk); @@ -584,13 +582,9 @@ static int scaler_runtime_resume(struct device *dev) return scaler_clk_ctrl(scaler, true); } -#endif -static const struct dev_pm_ops scaler_pm_ops = { - SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, - pm_runtime_force_resume) - SET_RUNTIME_PM_OPS(scaler_runtime_suspend, scaler_runtime_resume, NULL) -}; +static DEFINE_RUNTIME_DEV_PM_OPS(scaler_pm_ops, scaler_runtime_suspend, + scaler_runtime_resume, NULL); static const struct drm_exynos_ipp_limit scaler_5420_two_pixel_hv_limits[] = { { IPP_SIZE_LIMIT(BUFFER, .h = { 16, SZ_8K }, .v = { 16, SZ_8K }) }, @@ -731,7 +725,7 @@ struct platform_driver scaler_driver = { .driver = { .name = "exynos-scaler", .owner = THIS_MODULE, - .pm = &scaler_pm_ops, + .pm = pm_ptr(&scaler_pm_ops), .of_match_table = exynos_scaler_match, }, }; diff --git a/drivers/gpu/drm/gud/gud_connector.c b/drivers/gpu/drm/gud/gud_connector.c index fa636206f232..034e78360d4f 100644 --- a/drivers/gpu/drm/gud/gud_connector.c +++ b/drivers/gpu/drm/gud/gud_connector.c @@ -303,7 +303,7 @@ static int gud_connector_atomic_check(struct drm_connector *connector, old_state->tv.margins.right != new_state->tv.margins.right || old_state->tv.margins.top != new_state->tv.margins.top || old_state->tv.margins.bottom != new_state->tv.margins.bottom || - old_state->tv.mode != new_state->tv.mode || + old_state->tv.legacy_mode != new_state->tv.legacy_mode || old_state->tv.brightness != new_state->tv.brightness || old_state->tv.contrast != new_state->tv.contrast || old_state->tv.flicker_reduction != new_state->tv.flicker_reduction || @@ -400,7 +400,7 @@ static int gud_connector_add_tv_mode(struct gud_device *gdrm, struct drm_connect for (i = 0; i < num_modes; i++) modes[i] = &buf[i * GUD_CONNECTOR_TV_MODE_NAME_LEN]; - ret = drm_mode_create_tv_properties(connector->dev, num_modes, modes); + ret = drm_mode_create_tv_properties_legacy(connector->dev, num_modes, modes); free: kfree(buf); if (ret < 0) @@ -424,7 +424,7 @@ gud_connector_property_lookup(struct drm_connector *connector, u16 prop) case GUD_PROPERTY_TV_BOTTOM_MARGIN: return config->tv_bottom_margin_property; case GUD_PROPERTY_TV_MODE: - return config->tv_mode_property; + return config->legacy_tv_mode_property; case GUD_PROPERTY_TV_BRIGHTNESS: return config->tv_brightness_property; case GUD_PROPERTY_TV_CONTRAST: @@ -454,7 +454,7 @@ static unsigned int *gud_connector_tv_state_val(u16 prop, struct drm_tv_connecto case GUD_PROPERTY_TV_BOTTOM_MARGIN: return &state->margins.bottom; case GUD_PROPERTY_TV_MODE: - return &state->mode; + return &state->legacy_mode; case GUD_PROPERTY_TV_BRIGHTNESS: return &state->brightness; case GUD_PROPERTY_TV_CONTRAST: @@ -539,7 +539,7 @@ static int gud_connector_add_properties(struct gud_device *gdrm, struct gud_conn fallthrough; case GUD_PROPERTY_TV_HUE: /* This is a no-op if already added. */ - ret = drm_mode_create_tv_properties(drm, 0, NULL); + ret = drm_mode_create_tv_properties_legacy(drm, 0, NULL); if (ret) goto out; break; diff --git a/drivers/gpu/drm/gud/gud_drv.c b/drivers/gpu/drm/gud/gud_drv.c index d57dab104358..5aac7cda0505 100644 --- a/drivers/gpu/drm/gud/gud_drv.c +++ b/drivers/gpu/drm/gud/gud_drv.c @@ -365,6 +365,7 @@ static void gud_debugfs_init(struct drm_minor *minor) static const struct drm_simple_display_pipe_funcs gud_pipe_funcs = { .check = gud_pipe_check, .update = gud_pipe_update, + DRM_GEM_SIMPLE_DISPLAY_PIPE_SHADOW_PLANE_FUNCS }; static const struct drm_mode_config_funcs gud_mode_config_funcs = { diff --git a/drivers/gpu/drm/gud/gud_internal.h b/drivers/gpu/drm/gud/gud_internal.h index e351a1f1420d..0d148a6f27aa 100644 --- a/drivers/gpu/drm/gud/gud_internal.h +++ b/drivers/gpu/drm/gud/gud_internal.h @@ -43,6 +43,7 @@ struct gud_device { struct drm_framebuffer *fb; struct drm_rect damage; bool prev_flush_failed; + void *shadow_buf; }; static inline struct gud_device *to_gud_device(struct drm_device *drm) diff --git a/drivers/gpu/drm/gud/gud_pipe.c b/drivers/gpu/drm/gud/gud_pipe.c index 7c6dc2bcd14a..dc16a92625d4 100644 --- a/drivers/gpu/drm/gud/gud_pipe.c +++ b/drivers/gpu/drm/gud/gud_pipe.c @@ -5,6 +5,7 @@ #include <linux/lz4.h> #include <linux/usb.h> +#include <linux/vmalloc.h> #include <linux/workqueue.h> #include <drm/drm_atomic.h> @@ -15,6 +16,7 @@ #include <drm/drm_fourcc.h> #include <drm/drm_framebuffer.h> #include <drm/drm_gem.h> +#include <drm/drm_gem_atomic_helper.h> #include <drm/drm_gem_framebuffer_helper.h> #include <drm/drm_print.h> #include <drm/drm_rect.h> @@ -24,17 +26,13 @@ #include "gud_internal.h" /* - * Some userspace rendering loops runs all displays in the same loop. + * Some userspace rendering loops run all displays in the same loop. * This means that a fast display will have to wait for a slow one. - * For this reason gud does flushing asynchronous by default. - * The down side is that in e.g. a single display setup userspace thinks - * the display is insanely fast since the driver reports back immediately - * that the flush/pageflip is done. This wastes CPU and power. - * Such users might want to set this module parameter to false. + * Such users might want to enable this module parameter. */ -static bool gud_async_flush = true; +static bool gud_async_flush; module_param_named(async_flush, gud_async_flush, bool, 0644); -MODULE_PARM_DESC(async_flush, "Enable asynchronous flushing [default=true]"); +MODULE_PARM_DESC(async_flush, "Enable asynchronous flushing [default=0]"); /* * FIXME: The driver is probably broken on Big Endian machines. @@ -152,32 +150,21 @@ static size_t gud_xrgb8888_to_color(u8 *dst, const struct drm_format_info *forma } static int gud_prep_flush(struct gud_device *gdrm, struct drm_framebuffer *fb, + const struct iosys_map *src, bool cached_reads, const struct drm_format_info *format, struct drm_rect *rect, struct gud_set_buffer_req *req) { - struct dma_buf_attachment *import_attach = fb->obj[0]->import_attach; u8 compression = gdrm->compression; - struct iosys_map map[DRM_FORMAT_MAX_PLANES]; - struct iosys_map map_data[DRM_FORMAT_MAX_PLANES]; struct iosys_map dst; void *vaddr, *buf; size_t pitch, len; - int ret = 0; pitch = drm_format_info_min_pitch(format, 0, drm_rect_width(rect)); len = pitch * drm_rect_height(rect); if (len > gdrm->bulk_len) return -E2BIG; - ret = drm_gem_fb_vmap(fb, map, map_data); - if (ret) - return ret; - - vaddr = map_data[0].vaddr; - - ret = drm_gem_fb_begin_cpu_access(fb, DMA_FROM_DEVICE); - if (ret) - goto vunmap; + vaddr = src[0].vaddr; retry: if (compression) buf = gdrm->compress_buf; @@ -192,29 +179,27 @@ retry: if (format != fb->format) { if (format->format == GUD_DRM_FORMAT_R1) { len = gud_xrgb8888_to_r124(buf, format, vaddr, fb, rect); - if (!len) { - ret = -ENOMEM; - goto end_cpu_access; - } + if (!len) + return -ENOMEM; } else if (format->format == DRM_FORMAT_R8) { - drm_fb_xrgb8888_to_gray8(&dst, NULL, map_data, fb, rect); + drm_fb_xrgb8888_to_gray8(&dst, NULL, src, fb, rect); } else if (format->format == DRM_FORMAT_RGB332) { - drm_fb_xrgb8888_to_rgb332(&dst, NULL, map_data, fb, rect); + drm_fb_xrgb8888_to_rgb332(&dst, NULL, src, fb, rect); } else if (format->format == DRM_FORMAT_RGB565) { - drm_fb_xrgb8888_to_rgb565(&dst, NULL, map_data, fb, rect, + drm_fb_xrgb8888_to_rgb565(&dst, NULL, src, fb, rect, gud_is_big_endian()); } else if (format->format == DRM_FORMAT_RGB888) { - drm_fb_xrgb8888_to_rgb888(&dst, NULL, map_data, fb, rect); + drm_fb_xrgb8888_to_rgb888(&dst, NULL, src, fb, rect); } else { len = gud_xrgb8888_to_color(buf, format, vaddr, fb, rect); } } else if (gud_is_big_endian() && format->cpp[0] > 1) { - drm_fb_swab(&dst, NULL, map_data, fb, rect, !import_attach); - } else if (compression && !import_attach && pitch == fb->pitches[0]) { + drm_fb_swab(&dst, NULL, src, fb, rect, cached_reads); + } else if (compression && cached_reads && pitch == fb->pitches[0]) { /* can compress directly from the framebuffer */ buf = vaddr + rect->y1 * pitch; } else { - drm_fb_memcpy(&dst, NULL, map_data, fb, rect); + drm_fb_memcpy(&dst, NULL, src, fb, rect); } memset(req, 0, sizeof(*req)); @@ -237,12 +222,7 @@ retry: req->compressed_length = cpu_to_le32(complen); } -end_cpu_access: - drm_gem_fb_end_cpu_access(fb, DMA_FROM_DEVICE); -vunmap: - drm_gem_fb_vunmap(fb, map); - - return ret; + return 0; } struct gud_usb_bulk_context { @@ -285,6 +265,7 @@ static int gud_usb_bulk(struct gud_device *gdrm, size_t len) } static int gud_flush_rect(struct gud_device *gdrm, struct drm_framebuffer *fb, + const struct iosys_map *src, bool cached_reads, const struct drm_format_info *format, struct drm_rect *rect) { struct gud_set_buffer_req req; @@ -293,7 +274,7 @@ static int gud_flush_rect(struct gud_device *gdrm, struct drm_framebuffer *fb, drm_dbg(&gdrm->drm, "Flushing [FB:%d] " DRM_RECT_FMT "\n", fb->base.id, DRM_RECT_ARG(rect)); - ret = gud_prep_flush(gdrm, fb, format, rect, &req); + ret = gud_prep_flush(gdrm, fb, src, cached_reads, format, rect, &req); if (ret) return ret; @@ -333,46 +314,51 @@ void gud_clear_damage(struct gud_device *gdrm) gdrm->damage.y2 = 0; } -static void gud_add_damage(struct gud_device *gdrm, struct drm_rect *damage) +static void gud_flush_damage(struct gud_device *gdrm, struct drm_framebuffer *fb, + const struct iosys_map *src, bool cached_reads, + struct drm_rect *damage) { - gdrm->damage.x1 = min(gdrm->damage.x1, damage->x1); - gdrm->damage.y1 = min(gdrm->damage.y1, damage->y1); - gdrm->damage.x2 = max(gdrm->damage.x2, damage->x2); - gdrm->damage.y2 = max(gdrm->damage.y2, damage->y2); -} + const struct drm_format_info *format; + unsigned int i, lines; + size_t pitch; + int ret; -static void gud_retry_failed_flush(struct gud_device *gdrm, struct drm_framebuffer *fb, - struct drm_rect *damage) -{ - /* - * pipe_update waits for the worker when the display mode is going to change. - * This ensures that the width and height is still the same making it safe to - * add back the damage. - */ + format = fb->format; + if (format->format == DRM_FORMAT_XRGB8888 && gdrm->xrgb8888_emulation_format) + format = gdrm->xrgb8888_emulation_format; - mutex_lock(&gdrm->damage_lock); - if (!gdrm->fb) { - drm_framebuffer_get(fb); - gdrm->fb = fb; - } - gud_add_damage(gdrm, damage); - mutex_unlock(&gdrm->damage_lock); + /* Split update if it's too big */ + pitch = drm_format_info_min_pitch(format, 0, drm_rect_width(damage)); + lines = drm_rect_height(damage); + + if (gdrm->bulk_len < lines * pitch) + lines = gdrm->bulk_len / pitch; + + for (i = 0; i < DIV_ROUND_UP(drm_rect_height(damage), lines); i++) { + struct drm_rect rect = *damage; - /* Retry only once to avoid a possible storm in case of continues errors. */ - if (!gdrm->prev_flush_failed) - queue_work(system_long_wq, &gdrm->work); - gdrm->prev_flush_failed = true; + rect.y1 += i * lines; + rect.y2 = min_t(u32, rect.y1 + lines, damage->y2); + + ret = gud_flush_rect(gdrm, fb, src, cached_reads, format, &rect); + if (ret) { + if (ret != -ENODEV && ret != -ECONNRESET && + ret != -ESHUTDOWN && ret != -EPROTO) + dev_err_ratelimited(fb->dev->dev, + "Failed to flush framebuffer: error=%d\n", ret); + gdrm->prev_flush_failed = true; + break; + } + } } void gud_flush_work(struct work_struct *work) { struct gud_device *gdrm = container_of(work, struct gud_device, work); - const struct drm_format_info *format; + struct iosys_map shadow_map; struct drm_framebuffer *fb; struct drm_rect damage; - unsigned int i, lines; - int idx, ret = 0; - size_t pitch; + int idx; if (!drm_dev_enter(&gdrm->drm, &idx)) return; @@ -380,6 +366,7 @@ void gud_flush_work(struct work_struct *work) mutex_lock(&gdrm->damage_lock); fb = gdrm->fb; gdrm->fb = NULL; + iosys_map_set_vaddr(&shadow_map, gdrm->shadow_buf); damage = gdrm->damage; gud_clear_damage(gdrm); mutex_unlock(&gdrm->damage_lock); @@ -387,59 +374,43 @@ void gud_flush_work(struct work_struct *work) if (!fb) goto out; - format = fb->format; - if (format->format == DRM_FORMAT_XRGB8888 && gdrm->xrgb8888_emulation_format) - format = gdrm->xrgb8888_emulation_format; - - /* Split update if it's too big */ - pitch = drm_format_info_min_pitch(format, 0, drm_rect_width(&damage)); - lines = drm_rect_height(&damage); - - if (gdrm->bulk_len < lines * pitch) - lines = gdrm->bulk_len / pitch; - - for (i = 0; i < DIV_ROUND_UP(drm_rect_height(&damage), lines); i++) { - struct drm_rect rect = damage; - - rect.y1 += i * lines; - rect.y2 = min_t(u32, rect.y1 + lines, damage.y2); - - ret = gud_flush_rect(gdrm, fb, format, &rect); - if (ret) { - if (ret != -ENODEV && ret != -ECONNRESET && - ret != -ESHUTDOWN && ret != -EPROTO) { - bool prev_flush_failed = gdrm->prev_flush_failed; - - gud_retry_failed_flush(gdrm, fb, &damage); - if (!prev_flush_failed) - dev_err_ratelimited(fb->dev->dev, - "Failed to flush framebuffer: error=%d\n", ret); - } - break; - } - - gdrm->prev_flush_failed = false; - } + gud_flush_damage(gdrm, fb, &shadow_map, true, &damage); drm_framebuffer_put(fb); out: drm_dev_exit(idx); } -static void gud_fb_queue_damage(struct gud_device *gdrm, struct drm_framebuffer *fb, - struct drm_rect *damage) +static int gud_fb_queue_damage(struct gud_device *gdrm, struct drm_framebuffer *fb, + const struct iosys_map *src, struct drm_rect *damage) { struct drm_framebuffer *old_fb = NULL; + struct iosys_map shadow_map; mutex_lock(&gdrm->damage_lock); + if (!gdrm->shadow_buf) { + gdrm->shadow_buf = vzalloc(fb->pitches[0] * fb->height); + if (!gdrm->shadow_buf) { + mutex_unlock(&gdrm->damage_lock); + return -ENOMEM; + } + } + + iosys_map_set_vaddr(&shadow_map, gdrm->shadow_buf); + iosys_map_incr(&shadow_map, drm_fb_clip_offset(fb->pitches[0], fb->format, damage)); + drm_fb_memcpy(&shadow_map, fb->pitches, src, fb, damage); + if (fb != gdrm->fb) { old_fb = gdrm->fb; drm_framebuffer_get(fb); gdrm->fb = fb; } - gud_add_damage(gdrm, damage); + gdrm->damage.x1 = min(gdrm->damage.x1, damage->x1); + gdrm->damage.y1 = min(gdrm->damage.y1, damage->y1); + gdrm->damage.x2 = max(gdrm->damage.x2, damage->x2); + gdrm->damage.y2 = max(gdrm->damage.y2, damage->y2); mutex_unlock(&gdrm->damage_lock); @@ -447,6 +418,26 @@ static void gud_fb_queue_damage(struct gud_device *gdrm, struct drm_framebuffer if (old_fb) drm_framebuffer_put(old_fb); + + return 0; +} + +static void gud_fb_handle_damage(struct gud_device *gdrm, struct drm_framebuffer *fb, + const struct iosys_map *src, struct drm_rect *damage) +{ + int ret; + + if (gdrm->flags & GUD_DISPLAY_FLAG_FULL_UPDATE) + drm_rect_init(damage, 0, 0, fb->width, fb->height); + + if (gud_async_flush) { + ret = gud_fb_queue_damage(gdrm, fb, src, damage); + if (ret != -ENOMEM) + return; + } + + /* Imported buffers are assumed to be WriteCombined with uncached reads */ + gud_flush_damage(gdrm, fb, src, !fb->obj[0]->import_attach, damage); } int gud_pipe_check(struct drm_simple_display_pipe *pipe, @@ -571,10 +562,11 @@ void gud_pipe_update(struct drm_simple_display_pipe *pipe, struct drm_device *drm = pipe->crtc.dev; struct gud_device *gdrm = to_gud_device(drm); struct drm_plane_state *state = pipe->plane.state; + struct drm_shadow_plane_state *shadow_plane_state = to_drm_shadow_plane_state(state); struct drm_framebuffer *fb = state->fb; struct drm_crtc *crtc = &pipe->crtc; struct drm_rect damage; - int idx; + int ret, idx; if (crtc->state->mode_changed || !crtc->state->enable) { cancel_work_sync(&gdrm->work); @@ -584,6 +576,8 @@ void gud_pipe_update(struct drm_simple_display_pipe *pipe, gdrm->fb = NULL; } gud_clear_damage(gdrm); + vfree(gdrm->shadow_buf); + gdrm->shadow_buf = NULL; mutex_unlock(&gdrm->damage_lock); } @@ -599,14 +593,19 @@ void gud_pipe_update(struct drm_simple_display_pipe *pipe, if (crtc->state->active_changed) gud_usb_set_u8(gdrm, GUD_REQ_SET_DISPLAY_ENABLE, crtc->state->active); - if (drm_atomic_helper_damage_merged(old_state, state, &damage)) { - if (gdrm->flags & GUD_DISPLAY_FLAG_FULL_UPDATE) - drm_rect_init(&damage, 0, 0, fb->width, fb->height); - gud_fb_queue_damage(gdrm, fb, &damage); - if (!gud_async_flush) - flush_work(&gdrm->work); - } + if (!fb) + goto ctrl_disable; + + ret = drm_gem_fb_begin_cpu_access(fb, DMA_FROM_DEVICE); + if (ret) + goto ctrl_disable; + + if (drm_atomic_helper_damage_merged(old_state, state, &damage)) + gud_fb_handle_damage(gdrm, fb, &shadow_plane_state->data[0], &damage); + + drm_gem_fb_end_cpu_access(fb, DMA_FROM_DEVICE); +ctrl_disable: if (!crtc->state->enable) gud_usb_set_u8(gdrm, GUD_REQ_SET_CONTROLLER_ENABLE, 0); diff --git a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.c b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.c index 22053c613644..0c4aa4d9b0a7 100644 --- a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.c +++ b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.c @@ -106,7 +106,7 @@ static int hibmc_kms_init(struct hibmc_drm_private *priv) dev->mode_config.max_width = 1920; dev->mode_config.max_height = 1200; - dev->mode_config.preferred_depth = 32; + dev->mode_config.preferred_depth = 24; dev->mode_config.prefer_shadow = 1; dev->mode_config.funcs = (void *)&hibmc_mode_funcs; @@ -340,7 +340,7 @@ static int hibmc_pci_probe(struct pci_dev *pdev, goto err_unload; } - drm_fbdev_generic_setup(dev, dev->mode_config.preferred_depth); + drm_fbdev_generic_setup(dev, 32); return 0; diff --git a/drivers/gpu/drm/i2c/ch7006_drv.c b/drivers/gpu/drm/i2c/ch7006_drv.c index 578b738859b9..b9788218b2ec 100644 --- a/drivers/gpu/drm/i2c/ch7006_drv.c +++ b/drivers/gpu/drm/i2c/ch7006_drv.c @@ -250,7 +250,7 @@ static int ch7006_encoder_create_resources(struct drm_encoder *encoder, struct drm_device *dev = encoder->dev; struct drm_mode_config *conf = &dev->mode_config; - drm_mode_create_tv_properties(dev, NUM_TV_NORMS, ch7006_tv_norm_names); + drm_mode_create_tv_properties_legacy(dev, NUM_TV_NORMS, ch7006_tv_norm_names); priv->scale_property = drm_property_create_range(dev, 0, "scale", 0, 2); if (!priv->scale_property) @@ -264,8 +264,8 @@ static int ch7006_encoder_create_resources(struct drm_encoder *encoder, priv->hmargin); drm_object_attach_property(&connector->base, conf->tv_bottom_margin_property, priv->vmargin); - drm_object_attach_property(&connector->base, conf->tv_mode_property, - priv->norm); + drm_object_attach_property(&connector->base, conf->legacy_tv_mode_property, + priv->norm); drm_object_attach_property(&connector->base, conf->tv_brightness_property, priv->brightness); drm_object_attach_property(&connector->base, conf->tv_contrast_property, @@ -315,7 +315,7 @@ static int ch7006_encoder_set_property(struct drm_encoder *encoder, ch7006_load_reg(client, state, CH7006_POV); ch7006_load_reg(client, state, CH7006_VPOS); - } else if (property == conf->tv_mode_property) { + } else if (property == conf->legacy_tv_mode_property) { if (connector->dpms != DRM_MODE_DPMS_OFF) return -EINVAL; diff --git a/drivers/gpu/drm/i915/display/intel_tv.c b/drivers/gpu/drm/i915/display/intel_tv.c index 4d2101ca1692..b986bf075889 100644 --- a/drivers/gpu/drm/i915/display/intel_tv.c +++ b/drivers/gpu/drm/i915/display/intel_tv.c @@ -1905,10 +1905,10 @@ static void intel_tv_add_properties(struct drm_connector *connector) tv_format_names[i] = tv_modes[i].name; } - drm_mode_create_tv_properties(&i915->drm, i, tv_format_names); + drm_mode_create_tv_properties_legacy(&i915->drm, i, tv_format_names); drm_object_attach_property(&connector->base, - i915->drm.mode_config.tv_mode_property, + i915->drm.mode_config.legacy_tv_mode_property, conn_state->tv.mode); drm_object_attach_property(&connector->base, i915->drm.mode_config.tv_left_margin_property, diff --git a/drivers/gpu/drm/i915/gem/i915_gem_object_types.h b/drivers/gpu/drm/i915/gem/i915_gem_object_types.h index ab4c2f90a564..19c9bdd8f905 100644 --- a/drivers/gpu/drm/i915/gem/i915_gem_object_types.h +++ b/drivers/gpu/drm/i915/gem/i915_gem_object_types.h @@ -10,7 +10,7 @@ #include <linux/mmu_notifier.h> #include <drm/drm_gem.h> -#include <drm/ttm/ttm_bo_api.h> +#include <drm/ttm/ttm_bo.h> #include <uapi/drm/i915_drm.h> #include "i915_active.h" diff --git a/drivers/gpu/drm/i915/gem/i915_gem_ttm.c b/drivers/gpu/drm/i915/gem/i915_gem_ttm.c index 1e50fb0d6bfc..d409a77449a3 100644 --- a/drivers/gpu/drm/i915/gem/i915_gem_ttm.c +++ b/drivers/gpu/drm/i915/gem/i915_gem_ttm.c @@ -5,8 +5,8 @@ #include <linux/shmem_fs.h> -#include <drm/ttm/ttm_bo_driver.h> #include <drm/ttm/ttm_placement.h> +#include <drm/ttm/ttm_tt.h> #include <drm/drm_buddy.h> #include "i915_drv.h" @@ -599,13 +599,16 @@ i915_ttm_resource_get_st(struct drm_i915_gem_object *obj, static int i915_ttm_truncate(struct drm_i915_gem_object *obj) { struct ttm_buffer_object *bo = i915_gem_to_ttm(obj); - int err; + long err; WARN_ON_ONCE(obj->mm.madv == I915_MADV_WILLNEED); - err = ttm_bo_wait(bo, true, false); - if (err) + err = dma_resv_wait_timeout(bo->base.resv, DMA_RESV_USAGE_BOOKKEEP, + true, 15 * HZ); + if (err < 0) return err; + if (err == 0) + return -EBUSY; err = i915_ttm_move_notify(bo); if (err) diff --git a/drivers/gpu/drm/i915/gem/i915_gem_ttm_move.c b/drivers/gpu/drm/i915/gem/i915_gem_ttm_move.c index f59f812dc6d2..2ebaaf4d663c 100644 --- a/drivers/gpu/drm/i915/gem/i915_gem_ttm_move.c +++ b/drivers/gpu/drm/i915/gem/i915_gem_ttm_move.c @@ -3,7 +3,7 @@ * Copyright © 2021 Intel Corporation */ -#include <drm/ttm/ttm_bo_driver.h> +#include <drm/ttm/ttm_tt.h> #include "i915_deps.h" #include "i915_drv.h" diff --git a/drivers/gpu/drm/i915/gt/intel_gt_sysfs_pm.c b/drivers/gpu/drm/i915/gt/intel_gt_sysfs_pm.c index cf71305ad586..09b9365ededd 100644 --- a/drivers/gpu/drm/i915/gt/intel_gt_sysfs_pm.c +++ b/drivers/gpu/drm/i915/gt/intel_gt_sysfs_pm.c @@ -164,7 +164,6 @@ sysfs_gt_attribute_r_func(struct kobject *kobj, struct attribute *attr, NULL); \ INTEL_GT_ATTR_RO(_name) -#ifdef CONFIG_PM static u32 get_residency(struct intel_gt *gt, enum intel_rc6_res_type id) { intel_wakeref_t wakeref; @@ -300,7 +299,7 @@ static void intel_sysfs_rc6_init(struct intel_gt *gt, struct kobject *kobj) { int ret; - if (!HAS_RC6(gt->i915)) + if (!IS_ENABLED(CONFIG_PM) || !HAS_RC6(gt->i915)) return; ret = __intel_gt_sysfs_create_group(kobj, rc6_attr_group); @@ -329,11 +328,6 @@ static void intel_sysfs_rc6_init(struct intel_gt *gt, struct kobject *kobj) gt->info.id, ERR_PTR(ret)); } } -#else -static void intel_sysfs_rc6_init(struct intel_gt *gt, struct kobject *kobj) -{ -} -#endif /* CONFIG_PM */ static u32 __act_freq_mhz_show(struct intel_gt *gt) { diff --git a/drivers/gpu/drm/i915/i915_deps.c b/drivers/gpu/drm/i915/i915_deps.c index 297b8e4e42ee..91c61864285a 100644 --- a/drivers/gpu/drm/i915/i915_deps.c +++ b/drivers/gpu/drm/i915/i915_deps.c @@ -6,7 +6,7 @@ #include <linux/dma-fence.h> #include <linux/slab.h> -#include <drm/ttm/ttm_bo_api.h> +#include <drm/ttm/ttm_bo.h> #include "i915_deps.h" diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index 8468ca9885fd..c38306f156d6 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -1099,7 +1099,7 @@ void i915_gem_drain_freed_objects(struct drm_i915_private *i915) { while (atomic_read(&i915->mm.free_count)) { flush_work(&i915->mm.free_work); - flush_delayed_work(&i915->bdev.wq); + drain_workqueue(i915->bdev.wq); rcu_barrier(); } } diff --git a/drivers/gpu/drm/i915/i915_ttm_buddy_manager.c b/drivers/gpu/drm/i915/i915_ttm_buddy_manager.c index 7e611476c7a4..a72698a2dbc8 100644 --- a/drivers/gpu/drm/i915/i915_ttm_buddy_manager.c +++ b/drivers/gpu/drm/i915/i915_ttm_buddy_manager.c @@ -5,8 +5,8 @@ #include <linux/slab.h> -#include <drm/ttm/ttm_bo_driver.h> #include <drm/ttm/ttm_placement.h> +#include <drm/ttm/ttm_bo.h> #include <drm/drm_buddy.h> diff --git a/drivers/gpu/drm/i915/intel_region_ttm.c b/drivers/gpu/drm/i915/intel_region_ttm.c index cf89d0c2a2d9..4dc0702081b8 100644 --- a/drivers/gpu/drm/i915/intel_region_ttm.c +++ b/drivers/gpu/drm/i915/intel_region_ttm.c @@ -2,7 +2,6 @@ /* * Copyright © 2021 Intel Corporation */ -#include <drm/ttm/ttm_bo_driver.h> #include <drm/ttm/ttm_device.h> #include <drm/ttm/ttm_range_manager.h> @@ -132,7 +131,7 @@ int intel_region_ttm_fini(struct intel_memory_region *mem) break; msleep(20); - flush_delayed_work(&mem->i915->bdev.wq); + drain_workqueue(mem->i915->bdev.wq); } /* If we leaked objects, Don't free the region causing use after free */ diff --git a/drivers/gpu/drm/imx/Kconfig b/drivers/gpu/drm/imx/Kconfig index fd5b2471fdf0..e5749927fd6c 100644 --- a/drivers/gpu/drm/imx/Kconfig +++ b/drivers/gpu/drm/imx/Kconfig @@ -1,43 +1,4 @@ # SPDX-License-Identifier: GPL-2.0-only -config DRM_IMX - tristate "DRM Support for Freescale i.MX" - select DRM_KMS_HELPER - select VIDEOMODE_HELPERS - select DRM_GEM_DMA_HELPER - depends on DRM && (ARCH_MXC || ARCH_MULTIPLATFORM || COMPILE_TEST) - depends on IMX_IPUV3_CORE - help - enable i.MX graphics support - -config DRM_IMX_PARALLEL_DISPLAY - tristate "Support for parallel displays" - select DRM_PANEL - depends on DRM_IMX - select VIDEOMODE_HELPERS - -config DRM_IMX_TVE - tristate "Support for TV and VGA displays" - depends on DRM_IMX - depends on COMMON_CLK - select REGMAP_MMIO - help - Choose this to enable the internal Television Encoder (TVe) - found on i.MX53 processors. - -config DRM_IMX_LDB - tristate "Support for LVDS displays" - depends on DRM_IMX && MFD_SYSCON - depends on COMMON_CLK - select DRM_PANEL - help - Choose this to enable the internal LVDS Display Bridge (LDB) - found on i.MX53 and i.MX6 processors. - -config DRM_IMX_HDMI - tristate "Freescale i.MX DRM HDMI" - select DRM_DW_HDMI - depends on DRM_IMX && OF - help - Choose this if you want to use HDMI on i.MX6. source "drivers/gpu/drm/imx/dcss/Kconfig" +source "drivers/gpu/drm/imx/ipuv3/Kconfig" diff --git a/drivers/gpu/drm/imx/Makefile b/drivers/gpu/drm/imx/Makefile index b644deffe948..909622864716 100644 --- a/drivers/gpu/drm/imx/Makefile +++ b/drivers/gpu/drm/imx/Makefile @@ -1,12 +1,4 @@ # SPDX-License-Identifier: GPL-2.0 -imxdrm-objs := imx-drm-core.o ipuv3-crtc.o ipuv3-plane.o - -obj-$(CONFIG_DRM_IMX) += imxdrm.o - -obj-$(CONFIG_DRM_IMX_PARALLEL_DISPLAY) += parallel-display.o -obj-$(CONFIG_DRM_IMX_TVE) += imx-tve.o -obj-$(CONFIG_DRM_IMX_LDB) += imx-ldb.o - -obj-$(CONFIG_DRM_IMX_HDMI) += dw_hdmi-imx.o obj-$(CONFIG_DRM_IMX_DCSS) += dcss/ +obj-$(CONFIG_DRM_IMX) += ipuv3/ diff --git a/drivers/gpu/drm/imx/dcss/dcss-dev.c b/drivers/gpu/drm/imx/dcss/dcss-dev.c index 3f5750cc2673..66d9233ffb98 100644 --- a/drivers/gpu/drm/imx/dcss/dcss-dev.c +++ b/drivers/gpu/drm/imx/dcss/dcss-dev.c @@ -249,8 +249,7 @@ void dcss_dev_destroy(struct dcss_dev *dcss) kfree(dcss); } -#ifdef CONFIG_PM_SLEEP -int dcss_dev_suspend(struct device *dev) +static int dcss_dev_suspend(struct device *dev) { struct dcss_dev *dcss = dcss_drv_dev_to_dcss(dev); struct drm_device *ddev = dcss_drv_dev_to_drm(dev); @@ -273,7 +272,7 @@ int dcss_dev_suspend(struct device *dev) return 0; } -int dcss_dev_resume(struct device *dev) +static int dcss_dev_resume(struct device *dev) { struct dcss_dev *dcss = dcss_drv_dev_to_dcss(dev); struct drm_device *ddev = dcss_drv_dev_to_drm(dev); @@ -296,10 +295,8 @@ int dcss_dev_resume(struct device *dev) return 0; } -#endif /* CONFIG_PM_SLEEP */ -#ifdef CONFIG_PM -int dcss_dev_runtime_suspend(struct device *dev) +static int dcss_dev_runtime_suspend(struct device *dev) { struct dcss_dev *dcss = dcss_drv_dev_to_dcss(dev); int ret; @@ -313,7 +310,7 @@ int dcss_dev_runtime_suspend(struct device *dev) return 0; } -int dcss_dev_runtime_resume(struct device *dev) +static int dcss_dev_runtime_resume(struct device *dev) { struct dcss_dev *dcss = dcss_drv_dev_to_dcss(dev); @@ -325,4 +322,8 @@ int dcss_dev_runtime_resume(struct device *dev) return 0; } -#endif /* CONFIG_PM */ + +EXPORT_GPL_DEV_PM_OPS(dcss_dev_pm_ops) = { + RUNTIME_PM_OPS(dcss_dev_runtime_suspend, dcss_dev_runtime_resume, NULL) + SYSTEM_SLEEP_PM_OPS(dcss_dev_suspend, dcss_dev_resume) +}; diff --git a/drivers/gpu/drm/imx/dcss/dcss-dev.h b/drivers/gpu/drm/imx/dcss/dcss-dev.h index 1e582270c6ea..f27b87c09599 100644 --- a/drivers/gpu/drm/imx/dcss/dcss-dev.h +++ b/drivers/gpu/drm/imx/dcss/dcss-dev.h @@ -9,6 +9,7 @@ #include <drm/drm_fourcc.h> #include <drm/drm_plane.h> #include <linux/io.h> +#include <linux/pm.h> #include <video/videomode.h> #define SET 0x04 @@ -95,13 +96,11 @@ struct dcss_dev *dcss_drv_dev_to_dcss(struct device *dev); struct drm_device *dcss_drv_dev_to_drm(struct device *dev); struct dcss_dev *dcss_dev_create(struct device *dev, bool hdmi_output); void dcss_dev_destroy(struct dcss_dev *dcss); -int dcss_dev_runtime_suspend(struct device *dev); -int dcss_dev_runtime_resume(struct device *dev); -int dcss_dev_suspend(struct device *dev); -int dcss_dev_resume(struct device *dev); void dcss_enable_dtg_and_ss(struct dcss_dev *dcss); void dcss_disable_dtg_and_ss(struct dcss_dev *dcss); +extern const struct dev_pm_ops dcss_dev_pm_ops; + /* BLKCTL */ int dcss_blkctl_init(struct dcss_dev *dcss, unsigned long blkctl_base); void dcss_blkctl_cfg(struct dcss_blkctl *blkctl); diff --git a/drivers/gpu/drm/imx/dcss/dcss-drv.c b/drivers/gpu/drm/imx/dcss/dcss-drv.c index 1c70f70247f6..431510bd811b 100644 --- a/drivers/gpu/drm/imx/dcss/dcss-drv.c +++ b/drivers/gpu/drm/imx/dcss/dcss-drv.c @@ -117,19 +117,13 @@ static const struct of_device_id dcss_of_match[] = { MODULE_DEVICE_TABLE(of, dcss_of_match); -static const struct dev_pm_ops dcss_dev_pm = { - SET_SYSTEM_SLEEP_PM_OPS(dcss_dev_suspend, dcss_dev_resume) - SET_RUNTIME_PM_OPS(dcss_dev_runtime_suspend, - dcss_dev_runtime_resume, NULL) -}; - static struct platform_driver dcss_platform_driver = { .probe = dcss_drv_platform_probe, .remove = dcss_drv_platform_remove, .driver = { .name = "imx-dcss", .of_match_table = dcss_of_match, - .pm = &dcss_dev_pm, + .pm = pm_ptr(&dcss_dev_pm_ops), }, }; diff --git a/drivers/gpu/drm/imx/ipuv3/Kconfig b/drivers/gpu/drm/imx/ipuv3/Kconfig new file mode 100644 index 000000000000..bb278a369575 --- /dev/null +++ b/drivers/gpu/drm/imx/ipuv3/Kconfig @@ -0,0 +1,41 @@ +# SPDX-License-Identifier: GPL-2.0-only +config DRM_IMX + tristate "DRM Support for Freescale i.MX" + select DRM_KMS_HELPER + select VIDEOMODE_HELPERS + select DRM_GEM_DMA_HELPER + depends on DRM && (ARCH_MXC || ARCH_MULTIPLATFORM || COMPILE_TEST) + depends on IMX_IPUV3_CORE + help + enable i.MX graphics support + +config DRM_IMX_PARALLEL_DISPLAY + tristate "Support for parallel displays" + select DRM_PANEL + depends on DRM_IMX + select VIDEOMODE_HELPERS + +config DRM_IMX_TVE + tristate "Support for TV and VGA displays" + depends on DRM_IMX + depends on COMMON_CLK + select REGMAP_MMIO + help + Choose this to enable the internal Television Encoder (TVe) + found on i.MX53 processors. + +config DRM_IMX_LDB + tristate "Support for LVDS displays" + depends on DRM_IMX && MFD_SYSCON + depends on COMMON_CLK + select DRM_PANEL + help + Choose this to enable the internal LVDS Display Bridge (LDB) + found on i.MX53 and i.MX6 processors. + +config DRM_IMX_HDMI + tristate "Freescale i.MX DRM HDMI" + select DRM_DW_HDMI + depends on DRM_IMX && OF + help + Choose this if you want to use HDMI on i.MX6. diff --git a/drivers/gpu/drm/imx/ipuv3/Makefile b/drivers/gpu/drm/imx/ipuv3/Makefile new file mode 100644 index 000000000000..21cdcc2faabc --- /dev/null +++ b/drivers/gpu/drm/imx/ipuv3/Makefile @@ -0,0 +1,11 @@ +# SPDX-License-Identifier: GPL-2.0 + +imxdrm-objs := imx-drm-core.o ipuv3-crtc.o ipuv3-plane.o + +obj-$(CONFIG_DRM_IMX) += imxdrm.o + +obj-$(CONFIG_DRM_IMX_PARALLEL_DISPLAY) += parallel-display.o +obj-$(CONFIG_DRM_IMX_TVE) += imx-tve.o +obj-$(CONFIG_DRM_IMX_LDB) += imx-ldb.o + +obj-$(CONFIG_DRM_IMX_HDMI) += dw_hdmi-imx.o diff --git a/drivers/gpu/drm/imx/dw_hdmi-imx.c b/drivers/gpu/drm/imx/ipuv3/dw_hdmi-imx.c index a2277a0d6d06..a2277a0d6d06 100644 --- a/drivers/gpu/drm/imx/dw_hdmi-imx.c +++ b/drivers/gpu/drm/imx/ipuv3/dw_hdmi-imx.c diff --git a/drivers/gpu/drm/imx/imx-drm-core.c b/drivers/gpu/drm/imx/ipuv3/imx-drm-core.c index e060fa6cbcb9..e060fa6cbcb9 100644 --- a/drivers/gpu/drm/imx/imx-drm-core.c +++ b/drivers/gpu/drm/imx/ipuv3/imx-drm-core.c diff --git a/drivers/gpu/drm/imx/imx-drm.h b/drivers/gpu/drm/imx/ipuv3/imx-drm.h index e721bebda2bd..e721bebda2bd 100644 --- a/drivers/gpu/drm/imx/imx-drm.h +++ b/drivers/gpu/drm/imx/ipuv3/imx-drm.h diff --git a/drivers/gpu/drm/imx/imx-ldb.c b/drivers/gpu/drm/imx/ipuv3/imx-ldb.c index c45fc8f4744d..c45fc8f4744d 100644 --- a/drivers/gpu/drm/imx/imx-ldb.c +++ b/drivers/gpu/drm/imx/ipuv3/imx-ldb.c diff --git a/drivers/gpu/drm/imx/imx-tve.c b/drivers/gpu/drm/imx/ipuv3/imx-tve.c index d6832f506322..d6832f506322 100644 --- a/drivers/gpu/drm/imx/imx-tve.c +++ b/drivers/gpu/drm/imx/ipuv3/imx-tve.c diff --git a/drivers/gpu/drm/imx/ipuv3-crtc.c b/drivers/gpu/drm/imx/ipuv3/ipuv3-crtc.c index 5f26090b0c98..5f26090b0c98 100644 --- a/drivers/gpu/drm/imx/ipuv3-crtc.c +++ b/drivers/gpu/drm/imx/ipuv3/ipuv3-crtc.c diff --git a/drivers/gpu/drm/imx/ipuv3-plane.c b/drivers/gpu/drm/imx/ipuv3/ipuv3-plane.c index dba4f7d81d69..80142d9a4a55 100644 --- a/drivers/gpu/drm/imx/ipuv3-plane.c +++ b/drivers/gpu/drm/imx/ipuv3/ipuv3-plane.c @@ -614,6 +614,11 @@ static void ipu_plane_atomic_update(struct drm_plane *plane, break; } + if (ipu_plane->dp_flow == IPU_DP_FLOW_SYNC_BG) + width = ipu_src_rect_width(new_state); + else + width = drm_rect_width(&new_state->src) >> 16; + eba = drm_plane_state_to_eba(new_state, 0); /* @@ -622,8 +627,7 @@ static void ipu_plane_atomic_update(struct drm_plane *plane, */ if (ipu_state->use_pre) { axi_id = ipu_chan_assign_axi_id(ipu_plane->dma); - ipu_prg_channel_configure(ipu_plane->ipu_ch, axi_id, - ipu_src_rect_width(new_state), + ipu_prg_channel_configure(ipu_plane->ipu_ch, axi_id, width, drm_rect_height(&new_state->src) >> 16, fb->pitches[0], fb->format->format, fb->modifier, &eba); @@ -678,9 +682,8 @@ static void ipu_plane_atomic_update(struct drm_plane *plane, break; } - ipu_dmfc_config_wait4eot(ipu_plane->dmfc, ALIGN(drm_rect_width(dst), 8)); + ipu_dmfc_config_wait4eot(ipu_plane->dmfc, width); - width = ipu_src_rect_width(new_state); height = drm_rect_height(&new_state->src) >> 16; info = drm_format_info(fb->format->format); ipu_calculate_bursts(width, info->cpp[0], fb->pitches[0], @@ -744,8 +747,7 @@ static void ipu_plane_atomic_update(struct drm_plane *plane, ipu_cpmem_set_burstsize(ipu_plane->ipu_ch, 16); ipu_cpmem_zero(ipu_plane->alpha_ch); - ipu_cpmem_set_resolution(ipu_plane->alpha_ch, - ipu_src_rect_width(new_state), + ipu_cpmem_set_resolution(ipu_plane->alpha_ch, width, drm_rect_height(&new_state->src) >> 16); ipu_cpmem_set_format_passthrough(ipu_plane->alpha_ch, 8); ipu_cpmem_set_high_priority(ipu_plane->alpha_ch); diff --git a/drivers/gpu/drm/imx/ipuv3-plane.h b/drivers/gpu/drm/imx/ipuv3/ipuv3-plane.h index 6d544e6ce63f..6d544e6ce63f 100644 --- a/drivers/gpu/drm/imx/ipuv3-plane.h +++ b/drivers/gpu/drm/imx/ipuv3/ipuv3-plane.h diff --git a/drivers/gpu/drm/imx/parallel-display.c b/drivers/gpu/drm/imx/ipuv3/parallel-display.c index 0fa0b590830b..0fa0b590830b 100644 --- a/drivers/gpu/drm/imx/parallel-display.c +++ b/drivers/gpu/drm/imx/ipuv3/parallel-display.c diff --git a/drivers/gpu/drm/logicvc/logicvc_drm.c b/drivers/gpu/drm/logicvc/logicvc_drm.c index 9de24d9f0c96..2fb23697740a 100644 --- a/drivers/gpu/drm/logicvc/logicvc_drm.c +++ b/drivers/gpu/drm/logicvc/logicvc_drm.c @@ -301,6 +301,7 @@ static int logicvc_drm_probe(struct platform_device *pdev) struct regmap *regmap = NULL; struct resource res; void __iomem *base; + unsigned int preferred_bpp; int irq; int ret; @@ -438,7 +439,17 @@ static int logicvc_drm_probe(struct platform_device *pdev) goto error_mode; } - drm_fbdev_generic_setup(drm_dev, drm_dev->mode_config.preferred_depth); + switch (drm_dev->mode_config.preferred_depth) { + case 16: + preferred_bpp = 16; + break; + case 24: + case 32: + default: + preferred_bpp = 32; + break; + } + drm_fbdev_generic_setup(drm_dev, preferred_bpp); return 0; diff --git a/drivers/gpu/drm/mediatek/mtk_dp.c b/drivers/gpu/drm/mediatek/mtk_dp.c index 9d085c05c49c..b4feaabdb6a7 100644 --- a/drivers/gpu/drm/mediatek/mtk_dp.c +++ b/drivers/gpu/drm/mediatek/mtk_dp.c @@ -1981,7 +1981,7 @@ static struct edid *mtk_dp_get_edid(struct drm_bridge *bridge, struct cea_sad *sads; if (!enabled) { - drm_bridge_chain_pre_enable(bridge); + drm_atomic_bridge_chain_pre_enable(bridge, connector->state->state); /* power on aux */ mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_PWR_STATE, @@ -2019,7 +2019,7 @@ static struct edid *mtk_dp_get_edid(struct drm_bridge *bridge, DP_PWR_STATE_BANDGAP_TPLL, DP_PWR_STATE_MASK); - drm_bridge_chain_post_disable(bridge); + drm_atomic_bridge_chain_post_disable(bridge, connector->state->state); } return new_edid; diff --git a/drivers/gpu/drm/meson/meson_dw_hdmi.c b/drivers/gpu/drm/meson/meson_dw_hdmi.c index 5cd2b2ebbbd3..7642f740272b 100644 --- a/drivers/gpu/drm/meson/meson_dw_hdmi.c +++ b/drivers/gpu/drm/meson/meson_dw_hdmi.c @@ -140,7 +140,6 @@ struct meson_dw_hdmi { struct reset_control *hdmitx_apb; struct reset_control *hdmitx_ctrl; struct reset_control *hdmitx_phy; - struct regulator *hdmi_supply; u32 irq_stat; struct dw_hdmi *hdmi; struct drm_bridge *bridge; @@ -665,11 +664,6 @@ static void meson_dw_hdmi_init(struct meson_dw_hdmi *meson_dw_hdmi) } -static void meson_disable_regulator(void *data) -{ - regulator_disable(data); -} - static void meson_disable_clk(void *data) { clk_disable_unprepare(data); @@ -723,20 +717,9 @@ static int meson_dw_hdmi_bind(struct device *dev, struct device *master, meson_dw_hdmi->data = match; dw_plat_data = &meson_dw_hdmi->dw_plat_data; - meson_dw_hdmi->hdmi_supply = devm_regulator_get_optional(dev, "hdmi"); - if (IS_ERR(meson_dw_hdmi->hdmi_supply)) { - if (PTR_ERR(meson_dw_hdmi->hdmi_supply) == -EPROBE_DEFER) - return -EPROBE_DEFER; - meson_dw_hdmi->hdmi_supply = NULL; - } else { - ret = regulator_enable(meson_dw_hdmi->hdmi_supply); - if (ret) - return ret; - ret = devm_add_action_or_reset(dev, meson_disable_regulator, - meson_dw_hdmi->hdmi_supply); - if (ret) - return ret; - } + ret = devm_regulator_get_enable_optional(dev, "hdmi"); + if (ret != -ENODEV) + return ret; meson_dw_hdmi->hdmitx_apb = devm_reset_control_get_exclusive(dev, "hdmitx_apb"); diff --git a/drivers/gpu/drm/mxsfb/Kconfig b/drivers/gpu/drm/mxsfb/Kconfig index 116f8168bda4..518b53345354 100644 --- a/drivers/gpu/drm/mxsfb/Kconfig +++ b/drivers/gpu/drm/mxsfb/Kconfig @@ -8,6 +8,7 @@ config DRM_MXSFB tristate "i.MX (e)LCDIF LCD controller" depends on DRM && OF depends on COMMON_CLK + depends on ARCH_MXS || ARCH_MXC || COMPILE_TEST select DRM_MXS select DRM_KMS_HELPER select DRM_GEM_DMA_HELPER @@ -24,6 +25,7 @@ config DRM_IMX_LCDIF tristate "i.MX LCDIFv3 LCD controller" depends on DRM && OF depends on COMMON_CLK + depends on ARCH_MXC || COMPILE_TEST select DRM_MXS select DRM_KMS_HELPER select DRM_GEM_DMA_HELPER diff --git a/drivers/gpu/drm/nouveau/dispnv04/tvnv17.c b/drivers/gpu/drm/nouveau/dispnv04/tvnv17.c index be28e7bd7490..e5480dab55e3 100644 --- a/drivers/gpu/drm/nouveau/dispnv04/tvnv17.c +++ b/drivers/gpu/drm/nouveau/dispnv04/tvnv17.c @@ -653,7 +653,7 @@ static int nv17_tv_create_resources(struct drm_encoder *encoder, tv_enc->tv_norm = i; } - drm_mode_create_tv_properties(dev, num_tv_norms, nv17_tv_norm_names); + drm_mode_create_tv_properties_legacy(dev, num_tv_norms, nv17_tv_norm_names); drm_object_attach_property(&connector->base, conf->tv_select_subconnector_property, @@ -662,7 +662,7 @@ static int nv17_tv_create_resources(struct drm_encoder *encoder, conf->tv_subconnector_property, tv_enc->subconnector); drm_object_attach_property(&connector->base, - conf->tv_mode_property, + conf->legacy_tv_mode_property, tv_enc->tv_norm); drm_object_attach_property(&connector->base, conf->tv_flicker_reduction_property, @@ -722,7 +722,7 @@ static int nv17_tv_set_property(struct drm_encoder *encoder, if (encoder->crtc) nv17_tv_update_rescaler(encoder); - } else if (property == conf->tv_mode_property) { + } else if (property == conf->legacy_tv_mode_property) { if (connector->dpms != DRM_MODE_DPMS_OFF) return -EINVAL; diff --git a/drivers/gpu/drm/nouveau/nouveau_bo.c b/drivers/gpu/drm/nouveau/nouveau_bo.c index a11871e3119c..335fa91ca4ad 100644 --- a/drivers/gpu/drm/nouveau/nouveau_bo.c +++ b/drivers/gpu/drm/nouveau/nouveau_bo.c @@ -28,6 +28,7 @@ */ #include <linux/dma-mapping.h> +#include <drm/ttm/ttm_tt.h> #include "nouveau_drv.h" #include "nouveau_chan.h" diff --git a/drivers/gpu/drm/nouveau/nouveau_bo.h b/drivers/gpu/drm/nouveau/nouveau_bo.h index c2d3f9c48eba..774dd93ca76b 100644 --- a/drivers/gpu/drm/nouveau/nouveau_bo.h +++ b/drivers/gpu/drm/nouveau/nouveau_bo.h @@ -1,8 +1,9 @@ /* SPDX-License-Identifier: MIT */ #ifndef __NOUVEAU_BO_H__ #define __NOUVEAU_BO_H__ -#include <drm/ttm/ttm_bo_driver.h> #include <drm/drm_gem.h> +#include <drm/ttm/ttm_bo.h> +#include <drm/ttm/ttm_placement.h> struct nouveau_channel; struct nouveau_cli; diff --git a/drivers/gpu/drm/nouveau/nouveau_drv.h b/drivers/gpu/drm/nouveau/nouveau_drv.h index d6dd07bfa64a..b5de312a523f 100644 --- a/drivers/gpu/drm/nouveau/nouveau_drv.h +++ b/drivers/gpu/drm/nouveau/nouveau_drv.h @@ -51,8 +51,7 @@ #include <drm/drm_drv.h> #include <drm/drm_file.h> -#include <drm/ttm/ttm_bo_api.h> -#include <drm/ttm/ttm_bo_driver.h> +#include <drm/ttm/ttm_bo.h> #include <drm/ttm/ttm_placement.h> #include <drm/drm_audio_component.h> diff --git a/drivers/gpu/drm/nouveau/nouveau_mem.c b/drivers/gpu/drm/nouveau/nouveau_mem.c index 1fde3a5d7c32..25f31d5169e5 100644 --- a/drivers/gpu/drm/nouveau/nouveau_mem.c +++ b/drivers/gpu/drm/nouveau/nouveau_mem.c @@ -19,11 +19,12 @@ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ +#include <drm/ttm/ttm_tt.h> + #include "nouveau_mem.h" #include "nouveau_drv.h" #include "nouveau_bo.h" -#include <drm/ttm/ttm_bo_driver.h> #include <nvif/class.h> #include <nvif/if000a.h> diff --git a/drivers/gpu/drm/nouveau/nouveau_mem.h b/drivers/gpu/drm/nouveau/nouveau_mem.h index 1ee6cdb9ad9b..76c86d8bb01e 100644 --- a/drivers/gpu/drm/nouveau/nouveau_mem.h +++ b/drivers/gpu/drm/nouveau/nouveau_mem.h @@ -1,6 +1,6 @@ #ifndef __NOUVEAU_MEM_H__ #define __NOUVEAU_MEM_H__ -#include <drm/ttm/ttm_bo_api.h> +#include <drm/ttm/ttm_bo.h> struct ttm_tt; #include <nvif/mem.h> diff --git a/drivers/gpu/drm/nouveau/nouveau_prime.c b/drivers/gpu/drm/nouveau/nouveau_prime.c index 9608121e49b7..f42c2b1b0363 100644 --- a/drivers/gpu/drm/nouveau/nouveau_prime.c +++ b/drivers/gpu/drm/nouveau/nouveau_prime.c @@ -23,6 +23,7 @@ */ #include <linux/dma-buf.h> +#include <drm/ttm/ttm_tt.h> #include "nouveau_drv.h" #include "nouveau_gem.h" diff --git a/drivers/gpu/drm/nouveau/nouveau_sgdma.c b/drivers/gpu/drm/nouveau/nouveau_sgdma.c index 85c03c83259b..b14895f75b3c 100644 --- a/drivers/gpu/drm/nouveau/nouveau_sgdma.c +++ b/drivers/gpu/drm/nouveau/nouveau_sgdma.c @@ -1,6 +1,7 @@ // SPDX-License-Identifier: MIT #include <linux/pagemap.h> #include <linux/slab.h> +#include <drm/ttm/ttm_tt.h> #include "nouveau_drv.h" #include "nouveau_mem.h" diff --git a/drivers/gpu/drm/omapdrm/dss/dispc.c b/drivers/gpu/drm/omapdrm/dss/dispc.c index 0ee344ebcd1c..aacad5045e95 100644 --- a/drivers/gpu/drm/omapdrm/dss/dispc.c +++ b/drivers/gpu/drm/omapdrm/dss/dispc.c @@ -855,11 +855,6 @@ struct csc_coef_yuv2rgb { bool full_range; }; -struct csc_coef_rgb2yuv { - int yr, yg, yb, cbr, cbg, cbb, crr, crg, crb; - bool full_range; -}; - static void dispc_ovl_write_color_conv_coef(struct dispc_device *dispc, enum omap_plane_id plane, const struct csc_coef_yuv2rgb *ct) diff --git a/drivers/gpu/drm/omapdrm/dss/dsi.c b/drivers/gpu/drm/omapdrm/dss/dsi.c index a6845856cbce..4c1084eb0175 100644 --- a/drivers/gpu/drm/omapdrm/dss/dsi.c +++ b/drivers/gpu/drm/omapdrm/dss/dsi.c @@ -1039,22 +1039,26 @@ static int dsi_dump_dsi_irqs(struct seq_file *s, void *p) { struct dsi_data *dsi = s->private; unsigned long flags; - struct dsi_irq_stats stats; + struct dsi_irq_stats *stats; + + stats = kmalloc(sizeof(*stats), GFP_KERNEL); + if (!stats) + return -ENOMEM; spin_lock_irqsave(&dsi->irq_stats_lock, flags); - stats = dsi->irq_stats; + *stats = dsi->irq_stats; memset(&dsi->irq_stats, 0, sizeof(dsi->irq_stats)); dsi->irq_stats.last_reset = jiffies; spin_unlock_irqrestore(&dsi->irq_stats_lock, flags); seq_printf(s, "period %u ms\n", - jiffies_to_msecs(jiffies - stats.last_reset)); + jiffies_to_msecs(jiffies - stats->last_reset)); - seq_printf(s, "irqs %d\n", stats.irq_count); + seq_printf(s, "irqs %d\n", stats->irq_count); #define PIS(x) \ - seq_printf(s, "%-20s %10d\n", #x, stats.dsi_irqs[ffs(DSI_IRQ_##x)-1]); + seq_printf(s, "%-20s %10d\n", #x, stats->dsi_irqs[ffs(DSI_IRQ_##x)-1]); seq_printf(s, "-- DSI%d interrupts --\n", dsi->module_id + 1); PIS(VC0); @@ -1078,10 +1082,10 @@ static int dsi_dump_dsi_irqs(struct seq_file *s, void *p) #define PIS(x) \ seq_printf(s, "%-20s %10d %10d %10d %10d\n", #x, \ - stats.vc_irqs[0][ffs(DSI_VC_IRQ_##x)-1], \ - stats.vc_irqs[1][ffs(DSI_VC_IRQ_##x)-1], \ - stats.vc_irqs[2][ffs(DSI_VC_IRQ_##x)-1], \ - stats.vc_irqs[3][ffs(DSI_VC_IRQ_##x)-1]); + stats->vc_irqs[0][ffs(DSI_VC_IRQ_##x)-1], \ + stats->vc_irqs[1][ffs(DSI_VC_IRQ_##x)-1], \ + stats->vc_irqs[2][ffs(DSI_VC_IRQ_##x)-1], \ + stats->vc_irqs[3][ffs(DSI_VC_IRQ_##x)-1]); seq_printf(s, "-- VC interrupts --\n"); PIS(CS); @@ -1097,7 +1101,7 @@ static int dsi_dump_dsi_irqs(struct seq_file *s, void *p) #define PIS(x) \ seq_printf(s, "%-20s %10d\n", #x, \ - stats.cio_irqs[ffs(DSI_CIO_IRQ_##x)-1]); + stats->cio_irqs[ffs(DSI_CIO_IRQ_##x)-1]); seq_printf(s, "-- CIO interrupts --\n"); PIS(ERRSYNCESC1); @@ -1122,6 +1126,8 @@ static int dsi_dump_dsi_irqs(struct seq_file *s, void *p) PIS(ULPSACTIVENOT_ALL1); #undef PIS + kfree(stats); + return 0; } #endif diff --git a/drivers/gpu/drm/panel/Kconfig b/drivers/gpu/drm/panel/Kconfig index 737edcdf9eef..114758f64d26 100644 --- a/drivers/gpu/drm/panel/Kconfig +++ b/drivers/gpu/drm/panel/Kconfig @@ -400,6 +400,15 @@ config DRM_PANEL_OLIMEX_LCD_OLINUXINO Say Y here if you want to enable support for Olimex Ltd. LCD-OLinuXino panel. +config DRM_PANEL_ORISETECH_OTA5601A + tristate "Orise Technology ota5601a RGB/SPI panel" + depends on SPI + depends on BACKLIGHT_CLASS_DEVICE + select REGMAP_SPI + help + Say Y here if you want to enable support for the panels built + around the Orise Technology OTA9601A display controller. + config DRM_PANEL_ORISETECH_OTM8009A tristate "Orise Technology otm8009a 480x800 dsi 2dl panel" depends on OF diff --git a/drivers/gpu/drm/panel/Makefile b/drivers/gpu/drm/panel/Makefile index f8f9d9f6a307..ddcf8df889c8 100644 --- a/drivers/gpu/drm/panel/Makefile +++ b/drivers/gpu/drm/panel/Makefile @@ -37,6 +37,7 @@ obj-$(CONFIG_DRM_PANEL_NOVATEK_NT36672A) += panel-novatek-nt36672a.o obj-$(CONFIG_DRM_PANEL_NOVATEK_NT39016) += panel-novatek-nt39016.o obj-$(CONFIG_DRM_PANEL_MANTIX_MLAF057WE51) += panel-mantix-mlaf057we51.o obj-$(CONFIG_DRM_PANEL_OLIMEX_LCD_OLINUXINO) += panel-olimex-lcd-olinuxino.o +obj-$(CONFIG_DRM_PANEL_ORISETECH_OTA5601A) += panel-orisetech-ota5601a.o obj-$(CONFIG_DRM_PANEL_ORISETECH_OTM8009A) += panel-orisetech-otm8009a.o obj-$(CONFIG_DRM_PANEL_OSD_OSD101T2587_53TS) += panel-osd-osd101t2587-53ts.o obj-$(CONFIG_DRM_PANEL_PANASONIC_VVX10F034N00) += panel-panasonic-vvx10f034n00.o diff --git a/drivers/gpu/drm/panel/panel-edp.c b/drivers/gpu/drm/panel/panel-edp.c index 5cb8dc2ebe18..a0a7ab35e08c 100644 --- a/drivers/gpu/drm/panel/panel-edp.c +++ b/drivers/gpu/drm/panel/panel-edp.c @@ -351,7 +351,7 @@ static void panel_edp_wait(ktime_t start_ktime, unsigned int min_ms) return; min_ktime = ktime_add(start_ktime, ms_to_ktime(min_ms)); - now_ktime = ktime_get(); + now_ktime = ktime_get_boottime(); if (ktime_before(now_ktime, min_ktime)) msleep(ktime_to_ms(ktime_sub(min_ktime, now_ktime)) + 1); @@ -378,7 +378,7 @@ static int panel_edp_suspend(struct device *dev) gpiod_set_value_cansleep(p->enable_gpio, 0); regulator_disable(p->supply); - p->unprepared_time = ktime_get(); + p->unprepared_time = ktime_get_boottime(); return 0; } @@ -464,14 +464,14 @@ static int panel_edp_prepare_once(struct panel_edp *p) } } - p->prepared_time = ktime_get(); + p->prepared_time = ktime_get_boottime(); return 0; error: gpiod_set_value_cansleep(p->enable_gpio, 0); regulator_disable(p->supply); - p->unprepared_time = ktime_get(); + p->unprepared_time = ktime_get_boottime(); return err; } diff --git a/drivers/gpu/drm/panel/panel-ilitek-ili9341.c b/drivers/gpu/drm/panel/panel-ilitek-ili9341.c index 384a724f2822..3fdf884b3257 100644 --- a/drivers/gpu/drm/panel/panel-ilitek-ili9341.c +++ b/drivers/gpu/drm/panel/panel-ilitek-ili9341.c @@ -577,11 +577,7 @@ out_exit: } static const struct drm_simple_display_pipe_funcs ili9341_dbi_funcs = { - .mode_valid = mipi_dbi_pipe_mode_valid, - .enable = ili9341_dbi_enable, - .disable = mipi_dbi_pipe_disable, - .update = mipi_dbi_pipe_update, - .prepare_fb = drm_gem_simple_display_pipe_prepare_fb, + DRM_MIPI_DBI_SIMPLE_DISPLAY_PIPE_FUNCS(ili9341_dbi_enable), }; static const struct drm_display_mode ili9341_dbi_mode = { diff --git a/drivers/gpu/drm/panel/panel-orisetech-ota5601a.c b/drivers/gpu/drm/panel/panel-orisetech-ota5601a.c new file mode 100644 index 000000000000..e46be5014d42 --- /dev/null +++ b/drivers/gpu/drm/panel/panel-orisetech-ota5601a.c @@ -0,0 +1,364 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Orisetech OTA5601A TFT LCD panel driver + * + * Copyright (C) 2021, Christophe Branchereau <cbranchereau@gmail.com> + */ + +#include <linux/bits.h> +#include <linux/delay.h> +#include <linux/device.h> +#include <linux/gpio/consumer.h> +#include <linux/media-bus-format.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/regmap.h> +#include <linux/regulator/consumer.h> +#include <linux/spi/spi.h> + +#include <drm/drm_modes.h> +#include <drm/drm_panel.h> + +#define OTA5601A_CTL 0x01 +#define OTA5601A_CTL_OFF 0x00 +#define OTA5601A_CTL_ON BIT(0) + +struct ota5601a_panel_info { + const struct drm_display_mode *display_modes; + unsigned int num_modes; + u16 width_mm, height_mm; + u32 bus_format, bus_flags; +}; + +struct ota5601a { + struct drm_panel drm_panel; + struct regmap *map; + struct regulator *supply; + const struct ota5601a_panel_info *panel_info; + + struct gpio_desc *reset_gpio; +}; + +static inline struct ota5601a *to_ota5601a(struct drm_panel *panel) +{ + return container_of(panel, struct ota5601a, drm_panel); +} + +static const struct reg_sequence ota5601a_panel_regs[] = { + { 0xfd, 0x00 }, /* Page Shift */ + { 0x02, 0x00 }, /* Reset */ + + { 0x18, 0x00 }, /* Interface Sel: RGB 24 Bits */ + { 0x34, 0x20 }, /* Undocumented */ + + { 0x0c, 0x01 }, /* Contrast set by CMD1 == within page 0x00 */ + { 0x0d, 0x48 }, /* R Brightness */ + { 0x0e, 0x48 }, /* G Brightness */ + { 0x0f, 0x48 }, /* B Brightness */ + { 0x07, 0x40 }, /* R Contrast */ + { 0x08, 0x33 }, /* G Contrast */ + { 0x09, 0x3a }, /* B Contrast */ + + { 0x16, 0x01 }, /* NTSC Sel */ + { 0x19, 0x8d }, /* VBLK */ + { 0x1a, 0x28 }, /* HBLK */ + { 0x1c, 0x00 }, /* Scan Shift Dir. */ + + { 0xfd, 0xc5 }, /* Page Shift */ + { 0x82, 0x0c }, /* PWR_CTRL Pump */ + { 0xa2, 0xb4 }, /* PWR_CTRL VGH/VGL */ + + { 0xfd, 0xc4 }, /* Page Shift - What follows is listed as "RGB 24bit Timing Set" */ + { 0x82, 0x45 }, + + { 0xfd, 0xc1 }, + { 0x91, 0x02 }, + + { 0xfd, 0xc0 }, + { 0xa1, 0x01 }, + { 0xa2, 0x1f }, + { 0xa3, 0x0b }, + { 0xa4, 0x38 }, + { 0xa5, 0x00 }, + { 0xa6, 0x0a }, + { 0xa7, 0x38 }, + { 0xa8, 0x00 }, + { 0xa9, 0x0a }, + { 0xaa, 0x37 }, + + { 0xfd, 0xce }, + { 0x81, 0x18 }, + { 0x82, 0x43 }, + { 0x83, 0x43 }, + { 0x91, 0x06 }, + { 0x93, 0x38 }, + { 0x94, 0x02 }, + { 0x95, 0x06 }, + { 0x97, 0x38 }, + { 0x98, 0x02 }, + { 0x99, 0x06 }, + { 0x9b, 0x38 }, + { 0x9c, 0x02 }, + + { 0xfd, 0x00 }, /* Page Shift */ +}; + +static const struct regmap_config ota5601a_regmap_config = { + .reg_bits = 8, + .val_bits = 8, +}; + +static int ota5601a_prepare(struct drm_panel *drm_panel) +{ + struct ota5601a *panel = to_ota5601a(drm_panel); + int err; + + err = regulator_enable(panel->supply); + if (err) { + dev_err(drm_panel->dev, "Failed to enable power supply: %d\n", err); + return err; + } + + /* Reset to be held low for 10us min according to the doc, 10ms before sending commands */ + gpiod_set_value_cansleep(panel->reset_gpio, 1); + usleep_range(10, 30); + gpiod_set_value_cansleep(panel->reset_gpio, 0); + usleep_range(10000, 20000); + + /* Init all registers. */ + err = regmap_multi_reg_write(panel->map, ota5601a_panel_regs, + ARRAY_SIZE(ota5601a_panel_regs)); + if (err) { + dev_err(drm_panel->dev, "Failed to init registers: %d\n", err); + goto err_disable_regulator; + } + + msleep(120); + + return 0; + +err_disable_regulator: + regulator_disable(panel->supply); + return err; +} + +static int ota5601a_unprepare(struct drm_panel *drm_panel) +{ + struct ota5601a *panel = to_ota5601a(drm_panel); + + gpiod_set_value_cansleep(panel->reset_gpio, 1); + + regulator_disable(panel->supply); + + return 0; +} + +static int ota5601a_enable(struct drm_panel *drm_panel) +{ + struct ota5601a *panel = to_ota5601a(drm_panel); + int err; + + err = regmap_write(panel->map, OTA5601A_CTL, OTA5601A_CTL_ON); + + if (err) { + dev_err(drm_panel->dev, "Unable to enable panel: %d\n", err); + return err; + } + + if (drm_panel->backlight) { + /* Wait for the picture to be ready before enabling backlight */ + msleep(120); + } + + return 0; +} + +static int ota5601a_disable(struct drm_panel *drm_panel) +{ + struct ota5601a *panel = to_ota5601a(drm_panel); + int err; + + err = regmap_write(panel->map, OTA5601A_CTL, OTA5601A_CTL_OFF); + + if (err) { + dev_err(drm_panel->dev, "Unable to disable panel: %d\n", err); + return err; + } + + return 0; +} + +static int ota5601a_get_modes(struct drm_panel *drm_panel, + struct drm_connector *connector) +{ + struct ota5601a *panel = to_ota5601a(drm_panel); + const struct ota5601a_panel_info *panel_info = panel->panel_info; + struct drm_display_mode *mode; + unsigned int i; + + for (i = 0; i < panel_info->num_modes; i++) { + mode = drm_mode_duplicate(connector->dev, + &panel_info->display_modes[i]); + if (!mode) + return -ENOMEM; + + drm_mode_set_name(mode); + + mode->type = DRM_MODE_TYPE_DRIVER; + if (panel_info->num_modes == 1) + mode->type |= DRM_MODE_TYPE_PREFERRED; + + drm_mode_probed_add(connector, mode); + } + + connector->display_info.bpc = 8; + connector->display_info.width_mm = panel_info->width_mm; + connector->display_info.height_mm = panel_info->height_mm; + + drm_display_info_set_bus_formats(&connector->display_info, + &panel_info->bus_format, 1); + connector->display_info.bus_flags = panel_info->bus_flags; + + return panel_info->num_modes; +} + +static const struct drm_panel_funcs ota5601a_funcs = { + .prepare = ota5601a_prepare, + .unprepare = ota5601a_unprepare, + .enable = ota5601a_enable, + .disable = ota5601a_disable, + .get_modes = ota5601a_get_modes, +}; + +static int ota5601a_probe(struct spi_device *spi) +{ + const struct spi_device_id *id = spi_get_device_id(spi); + struct device *dev = &spi->dev; + struct ota5601a *panel; + int err; + + panel = devm_kzalloc(dev, sizeof(*panel), GFP_KERNEL); + if (!panel) + return -ENOMEM; + + spi_set_drvdata(spi, panel); + + panel->panel_info = (const struct ota5601a_panel_info *)id->driver_data; + if (!panel->panel_info) + return -EINVAL; + + panel->supply = devm_regulator_get(dev, "power"); + if (IS_ERR(panel->supply)) { + dev_err(dev, "Failed to get power supply\n"); + return PTR_ERR(panel->supply); + } + + panel->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH); + if (IS_ERR(panel->reset_gpio)) { + dev_err(dev, "Failed to get reset GPIO\n"); + return PTR_ERR(panel->reset_gpio); + } + + spi->bits_per_word = 8; + spi->mode = SPI_MODE_3 | SPI_3WIRE; + err = spi_setup(spi); + if (err) { + dev_err(dev, "Failed to setup SPI\n"); + return err; + } + + panel->map = devm_regmap_init_spi(spi, &ota5601a_regmap_config); + if (IS_ERR(panel->map)) { + dev_err(dev, "Failed to init regmap\n"); + return PTR_ERR(panel->map); + } + + drm_panel_init(&panel->drm_panel, dev, &ota5601a_funcs, + DRM_MODE_CONNECTOR_DPI); + + err = drm_panel_of_backlight(&panel->drm_panel); + if (err) { + if (err != -EPROBE_DEFER) + dev_err(dev, "Failed to get backlight handle\n"); + return err; + } + + drm_panel_add(&panel->drm_panel); + + return 0; +} + +static void ota5601a_remove(struct spi_device *spi) +{ + struct ota5601a *panel = spi_get_drvdata(spi); + + drm_panel_remove(&panel->drm_panel); + + ota5601a_disable(&panel->drm_panel); + ota5601a_unprepare(&panel->drm_panel); +} + +static const struct drm_display_mode gpt3_display_modes[] = { + { /* 60 Hz */ + .clock = 27000, + .hdisplay = 640, + .hsync_start = 640 + 220, + .hsync_end = 640 + 220 + 20, + .htotal = 640 + 220 + 20 + 20, + .vdisplay = 480, + .vsync_start = 480 + 7, + .vsync_end = 480 + 7 + 6, + .vtotal = 480 + 7 + 6 + 7, + .flags = DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC, + }, + + { /* 50 Hz */ + .clock = 24000, + .hdisplay = 640, + .hsync_start = 640 + 280, + .hsync_end = 640 + 280 + 20, + .htotal = 640 + 280 + 20 + 20, + .vdisplay = 480, + .vsync_start = 480 + 7, + .vsync_end = 480 + 7 + 6, + .vtotal = 480 + 7 + 6 + 7, + .flags = DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC, + }, +}; + +static const struct ota5601a_panel_info gpt3_info = { + .display_modes = gpt3_display_modes, + .num_modes = ARRAY_SIZE(gpt3_display_modes), + .width_mm = 71, + .height_mm = 51, + .bus_format = MEDIA_BUS_FMT_RGB888_1X24, + .bus_flags = DRM_BUS_FLAG_PIXDATA_SAMPLE_POSEDGE, +}; + +static const struct spi_device_id gpt3_id[] = { + { "gpt3", (kernel_ulong_t)&gpt3_info }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(spi, gpt3_id); + +static const struct of_device_id ota5601a_of_match[] = { + { .compatible = "focaltech,gpt3" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, ota5601a_of_match); + +static struct spi_driver ota5601a_driver = { + .driver = { + .name = "ota5601a", + .of_match_table = ota5601a_of_match, + }, + .id_table = gpt3_id, + .probe = ota5601a_probe, + .remove = ota5601a_remove, +}; + +module_spi_driver(ota5601a_driver); + +MODULE_AUTHOR("Christophe Branchereau <cbranchereau@gmail.com>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/gpu/drm/panel/panel-samsung-atna33xc20.c b/drivers/gpu/drm/panel/panel-samsung-atna33xc20.c index 5a8b978c6415..5703f4712d96 100644 --- a/drivers/gpu/drm/panel/panel-samsung-atna33xc20.c +++ b/drivers/gpu/drm/panel/panel-samsung-atna33xc20.c @@ -53,7 +53,7 @@ static void atana33xc20_wait(ktime_t start_ktime, unsigned int min_ms) ktime_t now_ktime, min_ktime; min_ktime = ktime_add(start_ktime, ms_to_ktime(min_ms)); - now_ktime = ktime_get(); + now_ktime = ktime_get_boottime(); if (ktime_before(now_ktime, min_ktime)) msleep(ktime_to_ms(ktime_sub(min_ktime, now_ktime)) + 1); @@ -75,7 +75,7 @@ static int atana33xc20_suspend(struct device *dev) ret = regulator_disable(p->supply); if (ret) return ret; - p->powered_off_time = ktime_get(); + p->powered_off_time = ktime_get_boottime(); p->el3_was_on = false; return 0; @@ -93,7 +93,7 @@ static int atana33xc20_resume(struct device *dev) ret = regulator_enable(p->supply); if (ret) return ret; - p->powered_on_time = ktime_get(); + p->powered_on_time = ktime_get_boottime(); if (p->no_hpd) { msleep(HPD_MAX_MS); @@ -142,7 +142,7 @@ static int atana33xc20_disable(struct drm_panel *panel) return 0; gpiod_set_value_cansleep(p->el_on3_gpio, 0); - p->el_on3_off_time = ktime_get(); + p->el_on3_off_time = ktime_get_boottime(); p->enabled = false; /* @@ -310,7 +310,7 @@ static int atana33xc20_probe(struct dp_aux_ep_device *aux_ep) ret = devm_add_action_or_reset(dev, atana33xc20_runtime_disable, dev); if (ret) return ret; - pm_runtime_set_autosuspend_delay(dev, 1000); + pm_runtime_set_autosuspend_delay(dev, 2000); pm_runtime_use_autosuspend(dev); ret = devm_add_action_or_reset(dev, atana33xc20_dont_use_autosuspend, dev); if (ret) diff --git a/drivers/gpu/drm/panel/panel-simple.c b/drivers/gpu/drm/panel/panel-simple.c index 8a3b685c2fcc..065f378bba9d 100644 --- a/drivers/gpu/drm/panel/panel-simple.c +++ b/drivers/gpu/drm/panel/panel-simple.c @@ -280,7 +280,7 @@ static void panel_simple_wait(ktime_t start_ktime, unsigned int min_ms) return; min_ktime = ktime_add(start_ktime, ms_to_ktime(min_ms)); - now_ktime = ktime_get(); + now_ktime = ktime_get_boottime(); if (ktime_before(now_ktime, min_ktime)) msleep(ktime_to_ms(ktime_sub(min_ktime, now_ktime)) + 1); @@ -307,7 +307,7 @@ static int panel_simple_suspend(struct device *dev) gpiod_set_value_cansleep(p->enable_gpio, 0); regulator_disable(p->supply); - p->unprepared_time = ktime_get(); + p->unprepared_time = ktime_get_boottime(); kfree(p->edid); p->edid = NULL; @@ -351,7 +351,7 @@ static int panel_simple_resume(struct device *dev) if (p->desc->delay.prepare) msleep(p->desc->delay.prepare); - p->prepared_time = ktime_get(); + p->prepared_time = ktime_get_boottime(); return 0; } diff --git a/drivers/gpu/drm/panfrost/panfrost_device.c b/drivers/gpu/drm/panfrost/panfrost_device.c index ee612303f076..fa1a086a862b 100644 --- a/drivers/gpu/drm/panfrost/panfrost_device.c +++ b/drivers/gpu/drm/panfrost/panfrost_device.c @@ -6,6 +6,7 @@ #include <linux/reset.h> #include <linux/platform_device.h> #include <linux/pm_domain.h> +#include <linux/pm_runtime.h> #include <linux/regulator/consumer.h> #include "panfrost_device.h" @@ -400,8 +401,7 @@ void panfrost_device_reset(struct panfrost_device *pfdev) panfrost_job_enable_interrupts(pfdev); } -#ifdef CONFIG_PM -int panfrost_device_resume(struct device *dev) +static int panfrost_device_resume(struct device *dev) { struct panfrost_device *pfdev = dev_get_drvdata(dev); @@ -411,7 +411,7 @@ int panfrost_device_resume(struct device *dev) return 0; } -int panfrost_device_suspend(struct device *dev) +static int panfrost_device_suspend(struct device *dev) { struct panfrost_device *pfdev = dev_get_drvdata(dev); @@ -423,4 +423,6 @@ int panfrost_device_suspend(struct device *dev) return 0; } -#endif + +EXPORT_GPL_RUNTIME_DEV_PM_OPS(panfrost_pm_ops, panfrost_device_suspend, + panfrost_device_resume, NULL); diff --git a/drivers/gpu/drm/panfrost/panfrost_device.h b/drivers/gpu/drm/panfrost/panfrost_device.h index 8b25278f34c8..d9ba68cffb77 100644 --- a/drivers/gpu/drm/panfrost/panfrost_device.h +++ b/drivers/gpu/drm/panfrost/panfrost_device.h @@ -7,6 +7,7 @@ #include <linux/atomic.h> #include <linux/io-pgtable.h> +#include <linux/pm.h> #include <linux/regulator/consumer.h> #include <linux/spinlock.h> #include <drm/drm_device.h> @@ -172,8 +173,7 @@ int panfrost_device_init(struct panfrost_device *pfdev); void panfrost_device_fini(struct panfrost_device *pfdev); void panfrost_device_reset(struct panfrost_device *pfdev); -int panfrost_device_resume(struct device *dev); -int panfrost_device_suspend(struct device *dev); +extern const struct dev_pm_ops panfrost_pm_ops; enum drm_panfrost_exception_type { DRM_PANFROST_EXCEPTION_OK = 0x00, diff --git a/drivers/gpu/drm/panfrost/panfrost_drv.c b/drivers/gpu/drm/panfrost/panfrost_drv.c index 2fa5afe21288..fa619fe72086 100644 --- a/drivers/gpu/drm/panfrost/panfrost_drv.c +++ b/drivers/gpu/drm/panfrost/panfrost_drv.c @@ -676,17 +676,12 @@ static const struct of_device_id dt_match[] = { }; MODULE_DEVICE_TABLE(of, dt_match); -static const struct dev_pm_ops panfrost_pm_ops = { - SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, pm_runtime_force_resume) - SET_RUNTIME_PM_OPS(panfrost_device_suspend, panfrost_device_resume, NULL) -}; - static struct platform_driver panfrost_driver = { .probe = panfrost_probe, .remove = panfrost_remove, .driver = { .name = "panfrost", - .pm = &panfrost_pm_ops, + .pm = pm_ptr(&panfrost_pm_ops), .of_match_table = dt_match, }, }; diff --git a/drivers/gpu/drm/qxl/qxl_cmd.c b/drivers/gpu/drm/qxl/qxl_cmd.c index 63aa96a69752..281edab518cd 100644 --- a/drivers/gpu/drm/qxl/qxl_cmd.c +++ b/drivers/gpu/drm/qxl/qxl_cmd.c @@ -579,7 +579,7 @@ void qxl_surface_evict(struct qxl_device *qdev, struct qxl_bo *surf, bool do_upd static int qxl_reap_surf(struct qxl_device *qdev, struct qxl_bo *surf, bool stall) { - int ret; + long ret; ret = qxl_bo_reserve(surf); if (ret) @@ -588,7 +588,19 @@ static int qxl_reap_surf(struct qxl_device *qdev, struct qxl_bo *surf, bool stal if (stall) mutex_unlock(&qdev->surf_evict_mutex); - ret = ttm_bo_wait(&surf->tbo, true, !stall); + if (stall) { + ret = dma_resv_wait_timeout(surf->tbo.base.resv, + DMA_RESV_USAGE_BOOKKEEP, true, + 15 * HZ); + if (ret > 0) + ret = 0; + else if (ret == 0) + ret = -EBUSY; + } else { + ret = dma_resv_test_signaled(surf->tbo.base.resv, + DMA_RESV_USAGE_BOOKKEEP); + ret = ret ? -EBUSY : 0; + } if (stall) mutex_lock(&qdev->surf_evict_mutex); diff --git a/drivers/gpu/drm/qxl/qxl_drv.h b/drivers/gpu/drm/qxl/qxl_drv.h index 76f060810f63..ea993d7162e8 100644 --- a/drivers/gpu/drm/qxl/qxl_drv.h +++ b/drivers/gpu/drm/qxl/qxl_drv.h @@ -42,8 +42,7 @@ #include <drm/drm_ioctl.h> #include <drm/drm_gem.h> #include <drm/qxl_drm.h> -#include <drm/ttm/ttm_bo_api.h> -#include <drm/ttm/ttm_bo_driver.h> +#include <drm/ttm/ttm_bo.h> #include <drm/ttm/ttm_execbuf_util.h> #include <drm/ttm/ttm_placement.h> diff --git a/drivers/gpu/drm/qxl/qxl_ttm.c b/drivers/gpu/drm/qxl/qxl_ttm.c index ee95001e6b5e..a92a5b0d4c25 100644 --- a/drivers/gpu/drm/qxl/qxl_ttm.c +++ b/drivers/gpu/drm/qxl/qxl_ttm.c @@ -29,10 +29,10 @@ #include <drm/drm_file.h> #include <drm/drm_debugfs.h> #include <drm/qxl_drm.h> -#include <drm/ttm/ttm_bo_api.h> -#include <drm/ttm/ttm_bo_driver.h> +#include <drm/ttm/ttm_bo.h> #include <drm/ttm/ttm_placement.h> #include <drm/ttm/ttm_range_manager.h> +#include <drm/ttm/ttm_tt.h> #include "qxl_drv.h" #include "qxl_object.h" diff --git a/drivers/gpu/drm/radeon/radeon.h b/drivers/gpu/drm/radeon/radeon.h index 2e7161acd443..57e20780a458 100644 --- a/drivers/gpu/drm/radeon/radeon.h +++ b/drivers/gpu/drm/radeon/radeon.h @@ -73,8 +73,7 @@ #include <linux/mmu_notifier.h> #endif -#include <drm/ttm/ttm_bo_api.h> -#include <drm/ttm/ttm_bo_driver.h> +#include <drm/ttm/ttm_bo.h> #include <drm/ttm/ttm_placement.h> #include <drm/ttm/ttm_execbuf_util.h> diff --git a/drivers/gpu/drm/radeon/radeon_device.c b/drivers/gpu/drm/radeon/radeon_device.c index 6344454a7721..9a556f505685 100644 --- a/drivers/gpu/drm/radeon/radeon_device.c +++ b/drivers/gpu/drm/radeon/radeon_device.c @@ -1772,7 +1772,6 @@ int radeon_gpu_reset(struct radeon_device *rdev) bool saved = false; int i, r; - int resched; down_write(&rdev->exclusive_lock); @@ -1784,8 +1783,6 @@ int radeon_gpu_reset(struct radeon_device *rdev) atomic_inc(&rdev->gpu_reset_counter); radeon_save_bios_scratch_regs(rdev); - /* block TTM */ - resched = ttm_bo_lock_delayed_workqueue(&rdev->mman.bdev); radeon_suspend(rdev); radeon_hpd_fini(rdev); @@ -1844,8 +1841,6 @@ int radeon_gpu_reset(struct radeon_device *rdev) /* reset hpd state */ radeon_hpd_init(rdev); - ttm_bo_unlock_delayed_workqueue(&rdev->mman.bdev, resched); - rdev->in_reset = true; rdev->needs_reset = false; diff --git a/drivers/gpu/drm/radeon/radeon_pm.c b/drivers/gpu/drm/radeon/radeon_pm.c index 04c693ca419a..cbc554928bcc 100644 --- a/drivers/gpu/drm/radeon/radeon_pm.c +++ b/drivers/gpu/drm/radeon/radeon_pm.c @@ -1853,11 +1853,10 @@ static bool radeon_pm_debug_check_in_vbl(struct radeon_device *rdev, bool finish static void radeon_dynpm_idle_work_handler(struct work_struct *work) { struct radeon_device *rdev; - int resched; + rdev = container_of(work, struct radeon_device, pm.dynpm_idle_work.work); - resched = ttm_bo_lock_delayed_workqueue(&rdev->mman.bdev); mutex_lock(&rdev->pm.mutex); if (rdev->pm.dynpm_state == DYNPM_STATE_ACTIVE) { int not_processed = 0; @@ -1908,7 +1907,6 @@ static void radeon_dynpm_idle_work_handler(struct work_struct *work) msecs_to_jiffies(RADEON_IDLE_LOOP_MS)); } mutex_unlock(&rdev->pm.mutex); - ttm_bo_unlock_delayed_workqueue(&rdev->mman.bdev, resched); } /* diff --git a/drivers/gpu/drm/radeon/radeon_prime.c b/drivers/gpu/drm/radeon/radeon_prime.c index 42a87948e28c..b3cfc99f4d7e 100644 --- a/drivers/gpu/drm/radeon/radeon_prime.c +++ b/drivers/gpu/drm/radeon/radeon_prime.c @@ -29,6 +29,8 @@ #include <drm/drm_prime.h> #include <drm/radeon_drm.h> +#include <drm/ttm/ttm_tt.h> + #include "radeon.h" #include "radeon_prime.h" diff --git a/drivers/gpu/drm/radeon/radeon_ttm.c b/drivers/gpu/drm/radeon/radeon_ttm.c index 30402b5ce4c5..1e8e287e113c 100644 --- a/drivers/gpu/drm/radeon/radeon_ttm.c +++ b/drivers/gpu/drm/radeon/radeon_ttm.c @@ -42,10 +42,10 @@ #include <drm/drm_file.h> #include <drm/drm_prime.h> #include <drm/radeon_drm.h> -#include <drm/ttm/ttm_bo_api.h> -#include <drm/ttm/ttm_bo_driver.h> +#include <drm/ttm/ttm_bo.h> #include <drm/ttm/ttm_placement.h> #include <drm/ttm/ttm_range_manager.h> +#include <drm/ttm/ttm_tt.h> #include "radeon_reg.h" #include "radeon.h" diff --git a/drivers/gpu/drm/rcar-du/rcar_du_drv.c b/drivers/gpu/drm/rcar-du/rcar_du_drv.c index d003e8d9e7a2..eeec1e02446f 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_drv.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_drv.c @@ -599,7 +599,6 @@ static const struct drm_driver rcar_du_driver = { * Power management */ -#ifdef CONFIG_PM_SLEEP static int rcar_du_pm_suspend(struct device *dev) { struct rcar_du_device *rcdu = dev_get_drvdata(dev); @@ -613,11 +612,9 @@ static int rcar_du_pm_resume(struct device *dev) return drm_mode_config_helper_resume(&rcdu->ddev); } -#endif -static const struct dev_pm_ops rcar_du_pm_ops = { - SET_SYSTEM_SLEEP_PM_OPS(rcar_du_pm_suspend, rcar_du_pm_resume) -}; +static DEFINE_SIMPLE_DEV_PM_OPS(rcar_du_pm_ops, + rcar_du_pm_suspend, rcar_du_pm_resume); /* ----------------------------------------------------------------------------- * Platform driver @@ -712,7 +709,7 @@ static struct platform_driver rcar_du_platform_driver = { .shutdown = rcar_du_shutdown, .driver = { .name = "rcar-du", - .pm = &rcar_du_pm_ops, + .pm = pm_sleep_ptr(&rcar_du_pm_ops), .of_match_table = rcar_du_of_table, }, }; diff --git a/drivers/gpu/drm/shmobile/shmob_drm_drv.c b/drivers/gpu/drm/shmobile/shmob_drm_drv.c index 3d511fa38913..337040fa6438 100644 --- a/drivers/gpu/drm/shmobile/shmob_drm_drv.c +++ b/drivers/gpu/drm/shmobile/shmob_drm_drv.c @@ -143,7 +143,6 @@ static const struct drm_driver shmob_drm_driver = { * Power management */ -#ifdef CONFIG_PM_SLEEP static int shmob_drm_pm_suspend(struct device *dev) { struct shmob_drm_device *sdev = dev_get_drvdata(dev); @@ -165,11 +164,9 @@ static int shmob_drm_pm_resume(struct device *dev) drm_kms_helper_poll_enable(sdev->ddev); return 0; } -#endif -static const struct dev_pm_ops shmob_drm_pm_ops = { - SET_SYSTEM_SLEEP_PM_OPS(shmob_drm_pm_suspend, shmob_drm_pm_resume) -}; +static DEFINE_SIMPLE_DEV_PM_OPS(shmob_drm_pm_ops, + shmob_drm_pm_suspend, shmob_drm_pm_resume); /* ----------------------------------------------------------------------------- * Platform driver @@ -292,7 +289,7 @@ static struct platform_driver shmob_drm_platform_driver = { .remove = shmob_drm_remove, .driver = { .name = "shmob-drm", - .pm = &shmob_drm_pm_ops, + .pm = pm_sleep_ptr(&shmob_drm_pm_ops), }, }; diff --git a/drivers/gpu/drm/solomon/ssd130x.c b/drivers/gpu/drm/solomon/ssd130x.c index 53464afc2b9a..c3bf3a18302e 100644 --- a/drivers/gpu/drm/solomon/ssd130x.c +++ b/drivers/gpu/drm/solomon/ssd130x.c @@ -876,7 +876,7 @@ static int ssd130x_init_modeset(struct ssd130x_device *ssd130x) drm->mode_config.max_width = max_width; drm->mode_config.min_height = mode->vdisplay; drm->mode_config.max_height = max_height; - drm->mode_config.preferred_depth = 32; + drm->mode_config.preferred_depth = 24; drm->mode_config.funcs = &ssd130x_mode_config_funcs; /* Primary plane */ @@ -1006,7 +1006,7 @@ struct ssd130x_device *ssd130x_probe(struct device *dev, struct regmap *regmap) if (ret) return ERR_PTR(dev_err_probe(dev, ret, "DRM device register failed\n")); - drm_fbdev_generic_setup(drm, 0); + drm_fbdev_generic_setup(drm, 32); return ssd130x; } diff --git a/drivers/gpu/drm/sprd/sprd_dpu.c b/drivers/gpu/drm/sprd/sprd_dpu.c index 88f4259680f1..db0bcea1d9f4 100644 --- a/drivers/gpu/drm/sprd/sprd_dpu.c +++ b/drivers/gpu/drm/sprd/sprd_dpu.c @@ -803,10 +803,8 @@ static int sprd_dpu_context_init(struct sprd_dpu *dpu, } ctx->irq = platform_get_irq(pdev, 0); - if (ctx->irq < 0) { - dev_err(dev, "failed to get dpu irq\n"); + if (ctx->irq < 0) return ctx->irq; - } /* disable and clear interrupts before register dpu IRQ. */ writel(0x00, ctx->base + REG_DPU_INT_EN); diff --git a/drivers/gpu/drm/sun4i/sun4i_tv.c b/drivers/gpu/drm/sun4i/sun4i_tv.c index c65f0a89b6b0..9625a00a48ba 100644 --- a/drivers/gpu/drm/sun4i/sun4i_tv.c +++ b/drivers/gpu/drm/sun4i/sun4i_tv.c @@ -141,23 +141,14 @@ struct resync_parameters { struct tv_mode { char *name; + unsigned int tv_mode; + u32 mode; u32 chroma_freq; u16 back_porch; u16 front_porch; - u16 line_number; u16 vblank_level; - u32 hdisplay; - u16 hfront_porch; - u16 hsync_len; - u16 hback_porch; - - u32 vdisplay; - u16 vfront_porch; - u16 vsync_len; - u16 vback_porch; - bool yc_en; bool dac3_en; bool dac_bit25_en; @@ -213,7 +204,7 @@ static const struct resync_parameters pal_resync_parameters = { static const struct tv_mode tv_modes[] = { { - .name = "NTSC", + .tv_mode = DRM_MODE_TV_MODE_NTSC, .mode = SUN4I_TVE_CFG0_RES_480i, .chroma_freq = 0x21f07c1f, .yc_en = true, @@ -222,17 +213,6 @@ static const struct tv_mode tv_modes[] = { .back_porch = 118, .front_porch = 32, - .line_number = 525, - - .hdisplay = 720, - .hfront_porch = 18, - .hsync_len = 2, - .hback_porch = 118, - - .vdisplay = 480, - .vfront_porch = 26, - .vsync_len = 2, - .vback_porch = 17, .vblank_level = 240, @@ -242,23 +222,12 @@ static const struct tv_mode tv_modes[] = { .resync_params = &ntsc_resync_parameters, }, { - .name = "PAL", + .tv_mode = DRM_MODE_TV_MODE_PAL, .mode = SUN4I_TVE_CFG0_RES_576i, .chroma_freq = 0x2a098acb, .back_porch = 138, .front_porch = 24, - .line_number = 625, - - .hdisplay = 720, - .hfront_porch = 3, - .hsync_len = 2, - .hback_porch = 139, - - .vdisplay = 576, - .vfront_porch = 28, - .vsync_len = 2, - .vback_porch = 19, .vblank_level = 252, @@ -276,63 +245,21 @@ drm_encoder_to_sun4i_tv(struct drm_encoder *encoder) encoder); } -/* - * FIXME: If only the drm_display_mode private field was usable, this - * could go away... - * - * So far, it doesn't seem to be preserved when the mode is passed by - * to mode_set for some reason. - */ -static const struct tv_mode *sun4i_tv_find_tv_by_mode(const struct drm_display_mode *mode) +static const struct tv_mode * +sun4i_tv_find_tv_by_mode(unsigned int mode) { int i; - /* First try to identify the mode by name */ for (i = 0; i < ARRAY_SIZE(tv_modes); i++) { const struct tv_mode *tv_mode = &tv_modes[i]; - DRM_DEBUG_DRIVER("Comparing mode %s vs %s", - mode->name, tv_mode->name); - - if (!strcmp(mode->name, tv_mode->name)) - return tv_mode; - } - - /* Then by number of lines */ - for (i = 0; i < ARRAY_SIZE(tv_modes); i++) { - const struct tv_mode *tv_mode = &tv_modes[i]; - - DRM_DEBUG_DRIVER("Comparing mode %s vs %s (X: %d vs %d)", - mode->name, tv_mode->name, - mode->vdisplay, tv_mode->vdisplay); - - if (mode->vdisplay == tv_mode->vdisplay) + if (tv_mode->tv_mode == mode) return tv_mode; } return NULL; } -static void sun4i_tv_mode_to_drm_mode(const struct tv_mode *tv_mode, - struct drm_display_mode *mode) -{ - DRM_DEBUG_DRIVER("Creating mode %s\n", mode->name); - - mode->type = DRM_MODE_TYPE_DRIVER; - mode->clock = 13500; - mode->flags = DRM_MODE_FLAG_INTERLACE; - - mode->hdisplay = tv_mode->hdisplay; - mode->hsync_start = mode->hdisplay + tv_mode->hfront_porch; - mode->hsync_end = mode->hsync_start + tv_mode->hsync_len; - mode->htotal = mode->hsync_end + tv_mode->hback_porch; - - mode->vdisplay = tv_mode->vdisplay; - mode->vsync_start = mode->vdisplay + tv_mode->vfront_porch; - mode->vsync_end = mode->vsync_start + tv_mode->vsync_len; - mode->vtotal = mode->vsync_end + tv_mode->vback_porch; -} - static void sun4i_tv_disable(struct drm_encoder *encoder, struct drm_atomic_state *state) { @@ -356,7 +283,11 @@ static void sun4i_tv_enable(struct drm_encoder *encoder, struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state, encoder->crtc); struct drm_display_mode *mode = &crtc_state->mode; - const struct tv_mode *tv_mode = sun4i_tv_find_tv_by_mode(mode); + struct drm_connector *connector = &tv->connector; + struct drm_connector_state *conn_state = + drm_atomic_get_new_connector_state(state, connector); + const struct tv_mode *tv_mode = + sun4i_tv_find_tv_by_mode(conn_state->tv.mode); DRM_DEBUG_DRIVER("Enabling the TV Output\n"); @@ -404,7 +335,7 @@ static void sun4i_tv_enable(struct drm_encoder *encoder, /* Set the lines setup */ regmap_write(tv->regs, SUN4I_TVE_LINE_REG, SUN4I_TVE_LINE_FIRST(22) | - SUN4I_TVE_LINE_NUMBER(tv_mode->line_number)); + SUN4I_TVE_LINE_NUMBER(mode->vtotal)); regmap_write(tv->regs, SUN4I_TVE_LEVEL_REG, SUN4I_TVE_LEVEL_BLANK(tv_mode->video_levels->blank) | @@ -465,37 +396,21 @@ static const struct drm_encoder_helper_funcs sun4i_tv_helper_funcs = { .atomic_enable = sun4i_tv_enable, }; -static int sun4i_tv_comp_get_modes(struct drm_connector *connector) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(tv_modes); i++) { - struct drm_display_mode *mode; - const struct tv_mode *tv_mode = &tv_modes[i]; - - mode = drm_mode_create(connector->dev); - if (!mode) { - DRM_ERROR("Failed to create a new display mode\n"); - return 0; - } - - strcpy(mode->name, tv_mode->name); - - sun4i_tv_mode_to_drm_mode(tv_mode, mode); - drm_mode_probed_add(connector, mode); - } - - return i; -} - static const struct drm_connector_helper_funcs sun4i_tv_comp_connector_helper_funcs = { - .get_modes = sun4i_tv_comp_get_modes, + .atomic_check = drm_atomic_helper_connector_tv_check, + .get_modes = drm_connector_helper_tv_get_modes, }; +static void sun4i_tv_connector_reset(struct drm_connector *connector) +{ + drm_atomic_helper_connector_reset(connector); + drm_atomic_helper_connector_tv_reset(connector); +} + static const struct drm_connector_funcs sun4i_tv_comp_connector_funcs = { .fill_modes = drm_helper_probe_single_connector_modes, .destroy = drm_connector_cleanup, - .reset = drm_atomic_helper_connector_reset, + .reset = sun4i_tv_connector_reset, .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, }; @@ -587,8 +502,20 @@ static int sun4i_tv_bind(struct device *dev, struct device *master, drm_connector_attach_encoder(&tv->connector, &tv->encoder); + ret = drm_mode_create_tv_properties(drm, + BIT(DRM_MODE_TV_MODE_NTSC) | + BIT(DRM_MODE_TV_MODE_PAL)); + if (ret) + goto err_cleanup_connector; + + drm_object_attach_property(&tv->connector.base, + drm->mode_config.tv_mode_property, + DRM_MODE_TV_MODE_NTSC); + return 0; +err_cleanup_connector: + drm_connector_cleanup(&tv->connector); err_cleanup_encoder: drm_encoder_cleanup(&tv->encoder); err_disable_clk: diff --git a/drivers/gpu/drm/tests/Makefile b/drivers/gpu/drm/tests/Makefile index b29ef1085cad..aaf357e29c65 100644 --- a/drivers/gpu/drm/tests/Makefile +++ b/drivers/gpu/drm/tests/Makefile @@ -1,14 +1,20 @@ # SPDX-License-Identifier: GPL-2.0 +obj-$(CONFIG_DRM_KUNIT_TEST_HELPERS) += \ + drm_kunit_helpers.o + obj-$(CONFIG_DRM_KUNIT_TEST) += \ drm_buddy_test.o \ drm_cmdline_parser_test.o \ + drm_connector_test.o \ drm_damage_helper_test.o \ drm_dp_mst_helper_test.o \ drm_format_helper_test.o \ drm_format_test.o \ drm_framebuffer_test.o \ - drm_kunit_helpers.o \ + drm_managed_test.o \ drm_mm_test.o \ + drm_modes_test.o \ drm_plane_helper_test.o \ + drm_probe_helper_test.o \ drm_rect_test.o diff --git a/drivers/gpu/drm/tests/drm_client_modeset_test.c b/drivers/gpu/drm/tests/drm_client_modeset_test.c index 362a5fbd82f5..416a279b6dae 100644 --- a/drivers/gpu/drm/tests/drm_client_modeset_test.c +++ b/drivers/gpu/drm/tests/drm_client_modeset_test.c @@ -8,20 +8,39 @@ #include <drm/drm_connector.h> #include <drm/drm_edid.h> #include <drm/drm_drv.h> +#include <drm/drm_kunit_helpers.h> #include <drm/drm_modes.h> #include <drm/drm_modeset_helper_vtables.h> #include <drm/drm_probe_helper.h> -#include "drm_kunit_helpers.h" - struct drm_client_modeset_test_priv { struct drm_device *drm; + struct device *dev; struct drm_connector connector; }; static int drm_client_modeset_connector_get_modes(struct drm_connector *connector) { - return drm_add_modes_noedid(connector, 1920, 1200); + struct drm_display_mode *mode; + int count; + + count = drm_add_modes_noedid(connector, 1920, 1200); + + mode = drm_mode_analog_ntsc_480i(connector->dev); + if (!mode) + return count; + + drm_mode_probed_add(connector, mode); + count += 1; + + mode = drm_mode_analog_pal_576i(connector->dev); + if (!mode) + return count; + + drm_mode_probed_add(connector, mode); + count += 1; + + return count; } static const struct drm_connector_helper_funcs drm_client_modeset_connector_helper_funcs = { @@ -41,7 +60,12 @@ static int drm_client_modeset_test_init(struct kunit *test) test->priv = priv; - priv->drm = drm_kunit_device_init(test, DRIVER_MODESET, "drm-client-modeset-test"); + priv->dev = drm_kunit_helper_alloc_device(test); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, priv->dev); + + priv->drm = __drm_kunit_helper_alloc_drm_device(test, priv->dev, + sizeof(*priv->drm), 0, + DRIVER_MODESET); KUNIT_ASSERT_NOT_ERR_OR_NULL(test, priv->drm); ret = drmm_connector_init(priv->drm, &priv->connector, @@ -52,9 +76,19 @@ static int drm_client_modeset_test_init(struct kunit *test) drm_connector_helper_add(&priv->connector, &drm_client_modeset_connector_helper_funcs); + priv->connector.interlace_allowed = true; + priv->connector.doublescan_allowed = true; + return 0; } +static void drm_client_modeset_test_exit(struct kunit *test) +{ + struct drm_client_modeset_test_priv *priv = test->priv; + + drm_kunit_helper_free_device(test, priv->dev); +} + static void drm_test_pick_cmdline_res_1920_1080_60(struct kunit *test) { struct drm_client_modeset_test_priv *priv = test->priv; @@ -84,15 +118,83 @@ static void drm_test_pick_cmdline_res_1920_1080_60(struct kunit *test) KUNIT_EXPECT_TRUE(test, drm_mode_equal(expected_mode, mode)); } +struct drm_connector_pick_cmdline_mode_test { + const char *cmdline; + struct drm_display_mode *(*func)(struct drm_device *drm); +}; + +#define TEST_CMDLINE(_cmdline, _fn) \ + { \ + .cmdline = _cmdline, \ + .func = _fn, \ + } + +static void drm_test_pick_cmdline_named(struct kunit *test) +{ + const struct drm_connector_pick_cmdline_mode_test *params = test->param_value; + struct drm_client_modeset_test_priv *priv = test->priv; + struct drm_device *drm = priv->drm; + struct drm_connector *connector = &priv->connector; + struct drm_cmdline_mode *cmdline_mode = &connector->cmdline_mode; + const struct drm_display_mode *expected_mode, *mode; + const char *cmdline = params->cmdline; + int ret; + + KUNIT_ASSERT_TRUE(test, + drm_mode_parse_command_line_for_connector(cmdline, + connector, + cmdline_mode)); + + mutex_lock(&drm->mode_config.mutex); + ret = drm_helper_probe_single_connector_modes(connector, 1920, 1080); + mutex_unlock(&drm->mode_config.mutex); + KUNIT_ASSERT_GT(test, ret, 0); + + mode = drm_connector_pick_cmdline_mode(connector); + KUNIT_ASSERT_NOT_NULL(test, mode); + + expected_mode = params->func(drm); + KUNIT_ASSERT_NOT_NULL(test, expected_mode); + + KUNIT_EXPECT_TRUE(test, drm_mode_equal(expected_mode, mode)); +} + +static const +struct drm_connector_pick_cmdline_mode_test drm_connector_pick_cmdline_mode_tests[] = { + TEST_CMDLINE("NTSC", drm_mode_analog_ntsc_480i), + TEST_CMDLINE("NTSC-J", drm_mode_analog_ntsc_480i), + TEST_CMDLINE("PAL", drm_mode_analog_pal_576i), + TEST_CMDLINE("PAL-M", drm_mode_analog_ntsc_480i), +}; + +static void +drm_connector_pick_cmdline_mode_desc(const struct drm_connector_pick_cmdline_mode_test *t, + char *desc) +{ + sprintf(desc, "%s", t->cmdline); +} + +KUNIT_ARRAY_PARAM(drm_connector_pick_cmdline_mode, + drm_connector_pick_cmdline_mode_tests, + drm_connector_pick_cmdline_mode_desc); + static struct kunit_case drm_test_pick_cmdline_tests[] = { KUNIT_CASE(drm_test_pick_cmdline_res_1920_1080_60), + KUNIT_CASE_PARAM(drm_test_pick_cmdline_named, + drm_connector_pick_cmdline_mode_gen_params), {} }; static struct kunit_suite drm_test_pick_cmdline_test_suite = { .name = "drm_test_pick_cmdline", .init = drm_client_modeset_test_init, + .exit = drm_client_modeset_test_exit, .test_cases = drm_test_pick_cmdline_tests }; kunit_test_suite(drm_test_pick_cmdline_test_suite); + +/* + * This file is included directly by drm_client_modeset.c so we can't + * use any MODULE_* macro here. + */ diff --git a/drivers/gpu/drm/tests/drm_cmdline_parser_test.c b/drivers/gpu/drm/tests/drm_cmdline_parser_test.c index 34790e7a3760..88f7f518ffb3 100644 --- a/drivers/gpu/drm/tests/drm_cmdline_parser_test.c +++ b/drivers/gpu/drm/tests/drm_cmdline_parser_test.c @@ -927,6 +927,14 @@ static const struct drm_cmdline_invalid_test drm_cmdline_invalid_tests[] = { .name = "invalid_option", .cmdline = "720x480,test=42", }, + { + .name = "invalid_tv_option", + .cmdline = "720x480i,tv_mode=invalid", + }, + { + .name = "truncated_tv_option", + .cmdline = "720x480i,tv_mode=NTS", + }, }; static void drm_cmdline_invalid_desc(const struct drm_cmdline_invalid_test *t, @@ -937,6 +945,65 @@ static void drm_cmdline_invalid_desc(const struct drm_cmdline_invalid_test *t, KUNIT_ARRAY_PARAM(drm_cmdline_invalid, drm_cmdline_invalid_tests, drm_cmdline_invalid_desc); +struct drm_cmdline_tv_option_test { + const char *name; + const char *cmdline; + struct drm_display_mode *(*mode_fn)(struct drm_device *dev); + enum drm_connector_tv_mode tv_mode; +}; + +static void drm_test_cmdline_tv_options(struct kunit *test) +{ + const struct drm_cmdline_tv_option_test *params = test->param_value; + const struct drm_display_mode *expected_mode = params->mode_fn(NULL); + struct drm_cmdline_mode mode = { }; + + KUNIT_EXPECT_TRUE(test, drm_mode_parse_command_line_for_connector(params->cmdline, + &no_connector, &mode)); + KUNIT_EXPECT_TRUE(test, mode.specified); + KUNIT_EXPECT_EQ(test, mode.xres, expected_mode->hdisplay); + KUNIT_EXPECT_EQ(test, mode.yres, expected_mode->vdisplay); + KUNIT_EXPECT_EQ(test, mode.tv_mode, params->tv_mode); + + KUNIT_EXPECT_FALSE(test, mode.refresh_specified); + + KUNIT_EXPECT_FALSE(test, mode.bpp_specified); + + KUNIT_EXPECT_FALSE(test, mode.rb); + KUNIT_EXPECT_FALSE(test, mode.cvt); + KUNIT_EXPECT_EQ(test, mode.interlace, !!(expected_mode->flags & DRM_MODE_FLAG_INTERLACE)); + KUNIT_EXPECT_FALSE(test, mode.margins); + KUNIT_EXPECT_EQ(test, mode.force, DRM_FORCE_UNSPECIFIED); +} + +#define TV_OPT_TEST(_opt, _cmdline, _mode_fn) \ + { \ + .name = #_opt, \ + .cmdline = _cmdline, \ + .mode_fn = _mode_fn, \ + .tv_mode = DRM_MODE_TV_MODE_ ## _opt, \ + } + +static const struct drm_cmdline_tv_option_test drm_cmdline_tv_option_tests[] = { + TV_OPT_TEST(NTSC, "720x480i,tv_mode=NTSC", drm_mode_analog_ntsc_480i), + TV_OPT_TEST(NTSC_443, "720x480i,tv_mode=NTSC-443", drm_mode_analog_ntsc_480i), + TV_OPT_TEST(NTSC_J, "720x480i,tv_mode=NTSC-J", drm_mode_analog_ntsc_480i), + TV_OPT_TEST(PAL, "720x576i,tv_mode=PAL", drm_mode_analog_pal_576i), + TV_OPT_TEST(PAL_M, "720x480i,tv_mode=PAL-M", drm_mode_analog_ntsc_480i), + TV_OPT_TEST(PAL_N, "720x576i,tv_mode=PAL-N", drm_mode_analog_pal_576i), + TV_OPT_TEST(SECAM, "720x576i,tv_mode=SECAM", drm_mode_analog_pal_576i), +}; + +static void drm_cmdline_tv_option_desc(const struct drm_cmdline_tv_option_test *t, + char *desc) +{ + sprintf(desc, "%s", t->name); +} + +KUNIT_ARRAY_PARAM(drm_cmdline_tv_option, + drm_cmdline_tv_option_tests, + drm_cmdline_tv_option_desc); + static struct kunit_case drm_cmdline_parser_tests[] = { KUNIT_CASE(drm_test_cmdline_force_d_only), KUNIT_CASE(drm_test_cmdline_force_D_only_dvi), @@ -977,6 +1044,7 @@ static struct kunit_case drm_cmdline_parser_tests[] = { KUNIT_CASE(drm_test_cmdline_freestanding_force_e_and_options), KUNIT_CASE(drm_test_cmdline_panel_orientation), KUNIT_CASE_PARAM(drm_test_cmdline_invalid, drm_cmdline_invalid_gen_params), + KUNIT_CASE_PARAM(drm_test_cmdline_tv_options, drm_cmdline_tv_option_gen_params), {} }; diff --git a/drivers/gpu/drm/tests/drm_connector_test.c b/drivers/gpu/drm/tests/drm_connector_test.c new file mode 100644 index 000000000000..c66aa2dc8d9d --- /dev/null +++ b/drivers/gpu/drm/tests/drm_connector_test.c @@ -0,0 +1,76 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Kunit test for drm_modes functions + */ + +#include <drm/drm_connector.h> + +#include <kunit/test.h> + +struct drm_get_tv_mode_from_name_test { + const char *name; + enum drm_connector_tv_mode expected_mode; +}; + +#define TV_MODE_NAME(_name, _mode) \ + { \ + .name = _name, \ + .expected_mode = _mode, \ + } + +static void drm_test_get_tv_mode_from_name_valid(struct kunit *test) +{ + const struct drm_get_tv_mode_from_name_test *params = test->param_value; + + KUNIT_EXPECT_EQ(test, + drm_get_tv_mode_from_name(params->name, strlen(params->name)), + params->expected_mode); +} + +static const +struct drm_get_tv_mode_from_name_test drm_get_tv_mode_from_name_valid_tests[] = { + TV_MODE_NAME("NTSC", DRM_MODE_TV_MODE_NTSC), + TV_MODE_NAME("NTSC-443", DRM_MODE_TV_MODE_NTSC_443), + TV_MODE_NAME("NTSC-J", DRM_MODE_TV_MODE_NTSC_J), + TV_MODE_NAME("PAL", DRM_MODE_TV_MODE_PAL), + TV_MODE_NAME("PAL-M", DRM_MODE_TV_MODE_PAL_M), + TV_MODE_NAME("PAL-N", DRM_MODE_TV_MODE_PAL_N), + TV_MODE_NAME("SECAM", DRM_MODE_TV_MODE_SECAM), +}; + +static void +drm_get_tv_mode_from_name_valid_desc(const struct drm_get_tv_mode_from_name_test *t, + char *desc) +{ + sprintf(desc, "%s", t->name); +} + +KUNIT_ARRAY_PARAM(drm_get_tv_mode_from_name_valid, + drm_get_tv_mode_from_name_valid_tests, + drm_get_tv_mode_from_name_valid_desc); + +static void drm_test_get_tv_mode_from_name_truncated(struct kunit *test) +{ + const char *name = "NTS"; + int ret; + + ret = drm_get_tv_mode_from_name(name, strlen(name)); + KUNIT_EXPECT_LT(test, ret, 0); +}; + +static struct kunit_case drm_get_tv_mode_from_name_tests[] = { + KUNIT_CASE_PARAM(drm_test_get_tv_mode_from_name_valid, + drm_get_tv_mode_from_name_valid_gen_params), + KUNIT_CASE(drm_test_get_tv_mode_from_name_truncated), + { } +}; + +static struct kunit_suite drm_get_tv_mode_from_name_test_suite = { + .name = "drm_get_tv_mode_from_name", + .test_cases = drm_get_tv_mode_from_name_tests, +}; + +kunit_test_suite(drm_get_tv_mode_from_name_test_suite); + +MODULE_AUTHOR("Maxime Ripard <maxime@cerno.tech>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/gpu/drm/tests/drm_kunit_helpers.c b/drivers/gpu/drm/tests/drm_kunit_helpers.c index f1662091f250..e98b4150f556 100644 --- a/drivers/gpu/drm/tests/drm_kunit_helpers.c +++ b/drivers/gpu/drm/tests/drm_kunit_helpers.c @@ -1,63 +1,101 @@ // SPDX-License-Identifier: GPL-2.0 #include <drm/drm_drv.h> +#include <drm/drm_kunit_helpers.h> #include <drm/drm_managed.h> #include <kunit/resource.h> #include <linux/device.h> +#include <linux/platform_device.h> -#include "drm_kunit_helpers.h" - -struct kunit_dev { - struct drm_device base; -}; +#define KUNIT_DEVICE_NAME "drm-kunit-mock-device" static const struct drm_mode_config_funcs drm_mode_config_funcs = { }; -static int dev_init(struct kunit_resource *res, void *ptr) +static int fake_probe(struct platform_device *pdev) { - char *name = ptr; - struct device *dev; - - dev = root_device_register(name); - if (IS_ERR(dev)) - return PTR_ERR(dev); + return 0; +} - res->data = dev; +static int fake_remove(struct platform_device *pdev) +{ return 0; } -static void dev_free(struct kunit_resource *res) +static struct platform_driver fake_platform_driver = { + .probe = fake_probe, + .remove = fake_remove, + .driver = { + .name = KUNIT_DEVICE_NAME, + }, +}; + +/** + * drm_kunit_helper_alloc_device - Allocate a mock device for a KUnit test + * @test: The test context object + * + * This allocates a fake struct &device to create a mock for a KUnit + * test. The device will also be bound to a fake driver. It will thus be + * able to leverage the usual infrastructure and most notably the + * device-managed resources just like a "real" device. + * + * Callers need to make sure drm_kunit_helper_free_device() on the + * device when done. + * + * Returns: + * A pointer to the new device, or an ERR_PTR() otherwise. + */ +struct device *drm_kunit_helper_alloc_device(struct kunit *test) { - struct device *dev = res->data; + struct platform_device *pdev; + int ret; - root_device_unregister(dev); + ret = platform_driver_register(&fake_platform_driver); + KUNIT_ASSERT_EQ(test, ret, 0); + + pdev = platform_device_alloc(KUNIT_DEVICE_NAME, PLATFORM_DEVID_NONE); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, pdev); + + ret = platform_device_add(pdev); + KUNIT_ASSERT_EQ(test, ret, 0); + + return &pdev->dev; +} +EXPORT_SYMBOL_GPL(drm_kunit_helper_alloc_device); + +/** + * drm_kunit_helper_free_device - Frees a mock device + * @test: The test context object + * @dev: The device to free + * + * Frees a device allocated with drm_kunit_helper_alloc_device(). + */ +void drm_kunit_helper_free_device(struct kunit *test, struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + + platform_device_unregister(pdev); + platform_driver_unregister(&fake_platform_driver); } +EXPORT_SYMBOL_GPL(drm_kunit_helper_free_device); -struct drm_device *drm_kunit_device_init(struct kunit *test, u32 features, char *name) +struct drm_device * +__drm_kunit_helper_alloc_drm_device_with_driver(struct kunit *test, + struct device *dev, + size_t size, size_t offset, + const struct drm_driver *driver) { - struct kunit_dev *kdev; struct drm_device *drm; - struct drm_driver *driver; - struct device *dev; + void *container; int ret; - dev = kunit_alloc_resource(test, dev_init, dev_free, GFP_KERNEL, name); - if (!dev) - return ERR_PTR(-ENOMEM); - - driver = kunit_kzalloc(test, sizeof(*driver), GFP_KERNEL); - if (!driver) - return ERR_PTR(-ENOMEM); - - driver->driver_features = features; - kdev = devm_drm_dev_alloc(dev, driver, struct kunit_dev, base); - if (IS_ERR(kdev)) - return ERR_CAST(kdev); + container = __devm_drm_dev_alloc(dev, driver, size, offset); + if (IS_ERR(container)) + return ERR_CAST(container); - drm = &kdev->base; + drm = container + offset; drm->mode_config.funcs = &drm_mode_config_funcs; ret = drmm_mode_config_init(drm); @@ -66,6 +104,7 @@ struct drm_device *drm_kunit_device_init(struct kunit *test, u32 features, char return drm; } +EXPORT_SYMBOL_GPL(__drm_kunit_helper_alloc_drm_device_with_driver); MODULE_AUTHOR("Maxime Ripard <maxime@cerno.tech>"); MODULE_LICENSE("GPL"); diff --git a/drivers/gpu/drm/tests/drm_kunit_helpers.h b/drivers/gpu/drm/tests/drm_kunit_helpers.h deleted file mode 100644 index 20ab6eec4c89..000000000000 --- a/drivers/gpu/drm/tests/drm_kunit_helpers.h +++ /dev/null @@ -1,11 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 - -#ifndef DRM_KUNIT_HELPERS_H_ -#define DRM_KUNIT_HELPERS_H_ - -struct drm_device; -struct kunit; - -struct drm_device *drm_kunit_device_init(struct kunit *test, u32 features, char *name); - -#endif // DRM_KUNIT_HELPERS_H_ diff --git a/drivers/gpu/drm/tests/drm_managed_test.c b/drivers/gpu/drm/tests/drm_managed_test.c new file mode 100644 index 000000000000..1652dca11d30 --- /dev/null +++ b/drivers/gpu/drm/tests/drm_managed_test.c @@ -0,0 +1,71 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include <drm/drm_drv.h> +#include <drm/drm_kunit_helpers.h> +#include <drm/drm_managed.h> + +#include <kunit/resource.h> + +#include <linux/device.h> + +/* Ought to be enough for anybody */ +#define TEST_TIMEOUT_MS 100 + +struct managed_test_priv { + bool action_done; + wait_queue_head_t action_wq; +}; + +static void drm_action(struct drm_device *drm, void *ptr) +{ + struct managed_test_priv *priv = ptr; + + priv->action_done = true; + wake_up_interruptible(&priv->action_wq); +} + +static void drm_test_managed_run_action(struct kunit *test) +{ + struct managed_test_priv *priv; + struct drm_device *drm; + struct device *dev; + int ret; + + priv = kunit_kzalloc(test, sizeof(*priv), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, priv); + init_waitqueue_head(&priv->action_wq); + + dev = drm_kunit_helper_alloc_device(test); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dev); + + drm = __drm_kunit_helper_alloc_drm_device(test, dev, sizeof(*drm), 0, DRIVER_MODESET); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, drm); + + ret = drmm_add_action_or_reset(drm, drm_action, priv); + KUNIT_EXPECT_EQ(test, ret, 0); + + ret = drm_dev_register(drm, 0); + KUNIT_ASSERT_EQ(test, ret, 0); + + drm_dev_unregister(drm); + drm_kunit_helper_free_device(test, dev); + + ret = wait_event_interruptible_timeout(priv->action_wq, priv->action_done, + msecs_to_jiffies(TEST_TIMEOUT_MS)); + KUNIT_EXPECT_GT(test, ret, 0); +} + +static struct kunit_case drm_managed_tests[] = { + KUNIT_CASE(drm_test_managed_run_action), + {} +}; + +static struct kunit_suite drm_managed_test_suite = { + .name = "drm-test-managed", + .test_cases = drm_managed_tests +}; + +kunit_test_suite(drm_managed_test_suite); + +MODULE_AUTHOR("Maxime Ripard <maxime@cerno.tech>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/gpu/drm/tests/drm_modes_test.c b/drivers/gpu/drm/tests/drm_modes_test.c new file mode 100644 index 000000000000..bc4aa2ce78be --- /dev/null +++ b/drivers/gpu/drm/tests/drm_modes_test.c @@ -0,0 +1,158 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Kunit test for drm_modes functions + */ + +#include <drm/drm_drv.h> +#include <drm/drm_kunit_helpers.h> +#include <drm/drm_modes.h> + +#include <kunit/test.h> + +#include <linux/units.h> + +struct drm_test_modes_priv { + struct drm_device *drm; + struct device *dev; +}; + +static int drm_test_modes_init(struct kunit *test) +{ + struct drm_test_modes_priv *priv; + + priv = kunit_kzalloc(test, sizeof(*priv), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, priv); + + priv->dev = drm_kunit_helper_alloc_device(test); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, priv->dev); + + priv->drm = __drm_kunit_helper_alloc_drm_device(test, priv->dev, + sizeof(*priv->drm), 0, + DRIVER_MODESET); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, priv->drm); + + test->priv = priv; + + return 0; +} + +static void drm_test_modes_exit(struct kunit *test) +{ + struct drm_test_modes_priv *priv = test->priv; + + drm_kunit_helper_free_device(test, priv->dev); +} + +static void drm_test_modes_analog_tv_ntsc_480i(struct kunit *test) +{ + struct drm_test_modes_priv *priv = test->priv; + struct drm_display_mode *mode; + + mode = drm_analog_tv_mode(priv->drm, + DRM_MODE_TV_MODE_NTSC, + 13500 * HZ_PER_KHZ, 720, 480, + true); + KUNIT_ASSERT_NOT_NULL(test, mode); + + KUNIT_EXPECT_EQ(test, drm_mode_vrefresh(mode), 60); + KUNIT_EXPECT_EQ(test, mode->hdisplay, 720); + + /* BT.601 defines hsync_start at 736 for 480i */ + KUNIT_EXPECT_EQ(test, mode->hsync_start, 736); + + /* + * The NTSC standard expects a line to take 63.556us. With a + * pixel clock of 13.5 MHz, a pixel takes around 74ns, so we + * need to have 63556ns / 74ns = 858. + * + * This is also mandated by BT.601. + */ + KUNIT_EXPECT_EQ(test, mode->htotal, 858); + + KUNIT_EXPECT_EQ(test, mode->vdisplay, 480); + KUNIT_EXPECT_EQ(test, mode->vtotal, 525); +} + +static void drm_test_modes_analog_tv_ntsc_480i_inlined(struct kunit *test) +{ + struct drm_test_modes_priv *priv = test->priv; + struct drm_display_mode *expected, *mode; + + expected = drm_analog_tv_mode(priv->drm, + DRM_MODE_TV_MODE_NTSC, + 13500 * HZ_PER_KHZ, 720, 480, + true); + KUNIT_ASSERT_NOT_NULL(test, expected); + + mode = drm_mode_analog_ntsc_480i(priv->drm); + KUNIT_ASSERT_NOT_NULL(test, mode); + + KUNIT_EXPECT_TRUE(test, drm_mode_equal(expected, mode)); +} + +static void drm_test_modes_analog_tv_pal_576i(struct kunit *test) +{ + struct drm_test_modes_priv *priv = test->priv; + struct drm_display_mode *mode; + + mode = drm_analog_tv_mode(priv->drm, + DRM_MODE_TV_MODE_PAL, + 13500 * HZ_PER_KHZ, 720, 576, + true); + KUNIT_ASSERT_NOT_NULL(test, mode); + + KUNIT_EXPECT_EQ(test, drm_mode_vrefresh(mode), 50); + KUNIT_EXPECT_EQ(test, mode->hdisplay, 720); + + /* BT.601 defines hsync_start at 732 for 576i */ + KUNIT_EXPECT_EQ(test, mode->hsync_start, 732); + + /* + * The PAL standard expects a line to take 64us. With a pixel + * clock of 13.5 MHz, a pixel takes around 74ns, so we need to + * have 64000ns / 74ns = 864. + * + * This is also mandated by BT.601. + */ + KUNIT_EXPECT_EQ(test, mode->htotal, 864); + + KUNIT_EXPECT_EQ(test, mode->vdisplay, 576); + KUNIT_EXPECT_EQ(test, mode->vtotal, 625); +} + +static void drm_test_modes_analog_tv_pal_576i_inlined(struct kunit *test) +{ + struct drm_test_modes_priv *priv = test->priv; + struct drm_display_mode *expected, *mode; + + expected = drm_analog_tv_mode(priv->drm, + DRM_MODE_TV_MODE_PAL, + 13500 * HZ_PER_KHZ, 720, 576, + true); + KUNIT_ASSERT_NOT_NULL(test, expected); + + mode = drm_mode_analog_pal_576i(priv->drm); + KUNIT_ASSERT_NOT_NULL(test, mode); + + KUNIT_EXPECT_TRUE(test, drm_mode_equal(expected, mode)); +} + +static struct kunit_case drm_modes_analog_tv_tests[] = { + KUNIT_CASE(drm_test_modes_analog_tv_ntsc_480i), + KUNIT_CASE(drm_test_modes_analog_tv_ntsc_480i_inlined), + KUNIT_CASE(drm_test_modes_analog_tv_pal_576i), + KUNIT_CASE(drm_test_modes_analog_tv_pal_576i_inlined), + { } +}; + +static struct kunit_suite drm_modes_analog_tv_test_suite = { + .name = "drm_modes_analog_tv", + .init = drm_test_modes_init, + .exit = drm_test_modes_exit, + .test_cases = drm_modes_analog_tv_tests, +}; + +kunit_test_suite(drm_modes_analog_tv_test_suite); + +MODULE_AUTHOR("Maxime Ripard <maxime@cerno.tech>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/gpu/drm/tests/drm_probe_helper_test.c b/drivers/gpu/drm/tests/drm_probe_helper_test.c new file mode 100644 index 000000000000..0ee65828623e --- /dev/null +++ b/drivers/gpu/drm/tests/drm_probe_helper_test.c @@ -0,0 +1,218 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Kunit test for drm_probe_helper functions + */ + +#include <drm/drm_atomic_state_helper.h> +#include <drm/drm_connector.h> +#include <drm/drm_device.h> +#include <drm/drm_drv.h> +#include <drm/drm_kunit_helpers.h> +#include <drm/drm_mode.h> +#include <drm/drm_modes.h> +#include <drm/drm_modeset_helper_vtables.h> +#include <drm/drm_probe_helper.h> + +#include <kunit/test.h> + +struct drm_probe_helper_test_priv { + struct drm_device *drm; + struct device *dev; + struct drm_connector connector; +}; + +static const struct drm_connector_helper_funcs drm_probe_helper_connector_helper_funcs = { +}; + +static const struct drm_connector_funcs drm_probe_helper_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, +}; + +static int drm_probe_helper_test_init(struct kunit *test) +{ + struct drm_probe_helper_test_priv *priv; + struct drm_connector *connector; + int ret; + + priv = kunit_kzalloc(test, sizeof(*priv), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, priv); + test->priv = priv; + + priv->dev = drm_kunit_helper_alloc_device(test); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, priv->dev); + + priv->drm = __drm_kunit_helper_alloc_drm_device(test, priv->dev, + sizeof(*priv->drm), 0, + DRIVER_MODESET | DRIVER_ATOMIC); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, priv->drm); + + connector = &priv->connector; + ret = drmm_connector_init(priv->drm, connector, + &drm_probe_helper_connector_funcs, + DRM_MODE_CONNECTOR_Unknown, + NULL); + KUNIT_ASSERT_EQ(test, ret, 0); + + drm_connector_helper_add(connector, &drm_probe_helper_connector_helper_funcs); + + return 0; +} + +static void drm_probe_helper_test_exit(struct kunit *test) +{ + struct drm_probe_helper_test_priv *priv = test->priv; + + drm_kunit_helper_free_device(test, priv->dev); +} + +typedef struct drm_display_mode *(*expected_mode_func_t)(struct drm_device *); + +struct drm_connector_helper_tv_get_modes_test { + const char *name; + unsigned int supported_tv_modes; + enum drm_connector_tv_mode default_mode; + bool cmdline; + enum drm_connector_tv_mode cmdline_mode; + expected_mode_func_t *expected_modes; + unsigned int num_expected_modes; +}; + +#define _TV_MODE_TEST(_name, _supported, _default, _cmdline, _cmdline_mode, ...) \ + { \ + .name = _name, \ + .supported_tv_modes = _supported, \ + .default_mode = _default, \ + .cmdline = _cmdline, \ + .cmdline_mode = _cmdline_mode, \ + .expected_modes = (expected_mode_func_t[]) { __VA_ARGS__ }, \ + .num_expected_modes = sizeof((expected_mode_func_t[]) { __VA_ARGS__ }) / \ + (sizeof(expected_mode_func_t)), \ + } + +#define TV_MODE_TEST(_name, _supported, _default, ...) \ + _TV_MODE_TEST(_name, _supported, _default, false, 0, __VA_ARGS__) + +#define TV_MODE_TEST_CMDLINE(_name, _supported, _default, _cmdline, ...) \ + _TV_MODE_TEST(_name, _supported, _default, true, _cmdline, __VA_ARGS__) + +static void +drm_test_connector_helper_tv_get_modes_check(struct kunit *test) +{ + const struct drm_connector_helper_tv_get_modes_test *params = test->param_value; + struct drm_probe_helper_test_priv *priv = test->priv; + struct drm_connector *connector = &priv->connector; + struct drm_cmdline_mode *cmdline = &connector->cmdline_mode; + struct drm_display_mode *mode; + const struct drm_display_mode *expected; + size_t len; + int ret; + + if (params->cmdline) { + cmdline->tv_mode_specified = true; + cmdline->tv_mode = params->cmdline_mode; + } + + ret = drm_mode_create_tv_properties(priv->drm, params->supported_tv_modes); + KUNIT_ASSERT_EQ(test, ret, 0); + + drm_object_attach_property(&connector->base, + priv->drm->mode_config.tv_mode_property, + params->default_mode); + + mutex_lock(&priv->drm->mode_config.mutex); + + ret = drm_connector_helper_tv_get_modes(connector); + KUNIT_EXPECT_EQ(test, ret, params->num_expected_modes); + + len = 0; + list_for_each_entry(mode, &connector->probed_modes, head) + len++; + KUNIT_EXPECT_EQ(test, len, params->num_expected_modes); + + if (params->num_expected_modes >= 1) { + mode = list_first_entry_or_null(&connector->probed_modes, + struct drm_display_mode, head); + KUNIT_ASSERT_NOT_NULL(test, mode); + + expected = params->expected_modes[0](priv->drm); + KUNIT_ASSERT_NOT_NULL(test, expected); + + KUNIT_EXPECT_TRUE(test, drm_mode_equal(mode, expected)); + KUNIT_EXPECT_TRUE(test, mode->type & DRM_MODE_TYPE_PREFERRED); + } + + if (params->num_expected_modes >= 2) { + mode = list_next_entry(mode, head); + KUNIT_ASSERT_NOT_NULL(test, mode); + + expected = params->expected_modes[1](priv->drm); + KUNIT_ASSERT_NOT_NULL(test, expected); + + KUNIT_EXPECT_TRUE(test, drm_mode_equal(mode, expected)); + KUNIT_EXPECT_FALSE(test, mode->type & DRM_MODE_TYPE_PREFERRED); + } + + mutex_unlock(&priv->drm->mode_config.mutex); +} + +static const +struct drm_connector_helper_tv_get_modes_test drm_connector_helper_tv_get_modes_tests[] = { + { .name = "None" }, + TV_MODE_TEST("PAL", + BIT(DRM_MODE_TV_MODE_PAL), + DRM_MODE_TV_MODE_PAL, + drm_mode_analog_pal_576i), + TV_MODE_TEST("NTSC", + BIT(DRM_MODE_TV_MODE_NTSC), + DRM_MODE_TV_MODE_NTSC, + drm_mode_analog_ntsc_480i), + TV_MODE_TEST("Both, NTSC Default", + BIT(DRM_MODE_TV_MODE_NTSC) | BIT(DRM_MODE_TV_MODE_PAL), + DRM_MODE_TV_MODE_NTSC, + drm_mode_analog_ntsc_480i, drm_mode_analog_pal_576i), + TV_MODE_TEST("Both, PAL Default", + BIT(DRM_MODE_TV_MODE_NTSC) | BIT(DRM_MODE_TV_MODE_PAL), + DRM_MODE_TV_MODE_PAL, + drm_mode_analog_pal_576i, drm_mode_analog_ntsc_480i), + TV_MODE_TEST_CMDLINE("Both, NTSC Default, with PAL on command-line", + BIT(DRM_MODE_TV_MODE_NTSC) | BIT(DRM_MODE_TV_MODE_PAL), + DRM_MODE_TV_MODE_NTSC, + DRM_MODE_TV_MODE_PAL, + drm_mode_analog_pal_576i, drm_mode_analog_ntsc_480i), + TV_MODE_TEST_CMDLINE("Both, PAL Default, with NTSC on command-line", + BIT(DRM_MODE_TV_MODE_NTSC) | BIT(DRM_MODE_TV_MODE_PAL), + DRM_MODE_TV_MODE_PAL, + DRM_MODE_TV_MODE_NTSC, + drm_mode_analog_ntsc_480i, drm_mode_analog_pal_576i), +}; + +static void +drm_connector_helper_tv_get_modes_desc(const struct drm_connector_helper_tv_get_modes_test *t, + char *desc) +{ + sprintf(desc, "%s", t->name); +} + +KUNIT_ARRAY_PARAM(drm_connector_helper_tv_get_modes, + drm_connector_helper_tv_get_modes_tests, + drm_connector_helper_tv_get_modes_desc); + +static struct kunit_case drm_test_connector_helper_tv_get_modes_tests[] = { + KUNIT_CASE_PARAM(drm_test_connector_helper_tv_get_modes_check, + drm_connector_helper_tv_get_modes_gen_params), + { } +}; + +static struct kunit_suite drm_test_connector_helper_tv_get_modes_suite = { + .name = "drm_connector_helper_tv_get_modes", + .init = drm_probe_helper_test_init, + .exit = drm_probe_helper_test_exit, + .test_cases = drm_test_connector_helper_tv_get_modes_tests, +}; + +kunit_test_suite(drm_test_connector_helper_tv_get_modes_suite); + +MODULE_AUTHOR("Maxime Ripard <maxime@cerno.tech>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/gpu/drm/tidss/tidss_dispc.c b/drivers/gpu/drm/tidss/tidss_dispc.c index ad93acc9abd2..165365b515e1 100644 --- a/drivers/gpu/drm/tidss/tidss_dispc.c +++ b/drivers/gpu/drm/tidss/tidss_dispc.c @@ -1858,8 +1858,8 @@ static const struct { { DRM_FORMAT_XBGR4444, 0x21, }, { DRM_FORMAT_RGBX4444, 0x22, }, - { DRM_FORMAT_ARGB1555, 0x25, }, - { DRM_FORMAT_ABGR1555, 0x26, }, + { DRM_FORMAT_XRGB1555, 0x25, }, + { DRM_FORMAT_XBGR1555, 0x26, }, { DRM_FORMAT_XRGB8888, 0x27, }, { DRM_FORMAT_XBGR8888, 0x28, }, @@ -2686,6 +2686,8 @@ int dispc_init(struct tidss_device *tidss) dev_warn(dev, "cannot set DMA masks to 48-bit\n"); } + dma_set_max_seg_size(dev, UINT_MAX); + dispc = devm_kzalloc(dev, sizeof(*dispc), GFP_KERNEL); if (!dispc) return -ENOMEM; diff --git a/drivers/gpu/drm/tilcdc/tilcdc_drv.c b/drivers/gpu/drm/tilcdc/tilcdc_drv.c index 80615ecdae0b..4ca426007dc8 100644 --- a/drivers/gpu/drm/tilcdc/tilcdc_drv.c +++ b/drivers/gpu/drm/tilcdc/tilcdc_drv.c @@ -496,7 +496,6 @@ static const struct drm_driver tilcdc_driver = { * Power management: */ -#ifdef CONFIG_PM_SLEEP static int tilcdc_pm_suspend(struct device *dev) { struct drm_device *ddev = dev_get_drvdata(dev); @@ -518,11 +517,9 @@ static int tilcdc_pm_resume(struct device *dev) pinctrl_pm_select_default_state(dev); return drm_mode_config_helper_resume(ddev); } -#endif -static const struct dev_pm_ops tilcdc_pm_ops = { - SET_SYSTEM_SLEEP_PM_OPS(tilcdc_pm_suspend, tilcdc_pm_resume) -}; +static DEFINE_SIMPLE_DEV_PM_OPS(tilcdc_pm_ops, + tilcdc_pm_suspend, tilcdc_pm_resume); /* * Platform driver: @@ -597,7 +594,7 @@ static struct platform_driver tilcdc_platform_driver = { .remove = tilcdc_pdev_remove, .driver = { .name = "tilcdc", - .pm = &tilcdc_pm_ops, + .pm = pm_sleep_ptr(&tilcdc_pm_ops), .of_match_table = tilcdc_of_match, }, }; diff --git a/drivers/gpu/drm/tiny/cirrus.c b/drivers/gpu/drm/tiny/cirrus.c index 678c2ef1cae7..cf35b6090503 100644 --- a/drivers/gpu/drm/tiny/cirrus.c +++ b/drivers/gpu/drm/tiny/cirrus.c @@ -604,7 +604,7 @@ static int cirrus_pci_probe(struct pci_dev *pdev, if (ret) return ret; - drm_fbdev_generic_setup(dev, dev->mode_config.preferred_depth); + drm_fbdev_generic_setup(dev, 16); return 0; } diff --git a/drivers/gpu/drm/tiny/gm12u320.c b/drivers/gpu/drm/tiny/gm12u320.c index 130fd07a967d..c5bb683e440c 100644 --- a/drivers/gpu/drm/tiny/gm12u320.c +++ b/drivers/gpu/drm/tiny/gm12u320.c @@ -4,6 +4,7 @@ */ #include <linux/module.h> +#include <linux/pm.h> #include <linux/usb.h> #include <drm/drm_atomic_helper.h> @@ -718,15 +719,15 @@ static void gm12u320_usb_disconnect(struct usb_interface *interface) drm_atomic_helper_shutdown(dev); } -static __maybe_unused int gm12u320_suspend(struct usb_interface *interface, - pm_message_t message) +static int gm12u320_suspend(struct usb_interface *interface, + pm_message_t message) { struct drm_device *dev = usb_get_intfdata(interface); return drm_mode_config_helper_suspend(dev); } -static __maybe_unused int gm12u320_resume(struct usb_interface *interface) +static int gm12u320_resume(struct usb_interface *interface) { struct drm_device *dev = usb_get_intfdata(interface); struct gm12u320_device *gm12u320 = to_gm12u320(dev); @@ -747,11 +748,9 @@ static struct usb_driver gm12u320_usb_driver = { .probe = gm12u320_usb_probe, .disconnect = gm12u320_usb_disconnect, .id_table = id_table, -#ifdef CONFIG_PM - .suspend = gm12u320_suspend, - .resume = gm12u320_resume, - .reset_resume = gm12u320_resume, -#endif + .suspend = pm_ptr(gm12u320_suspend), + .resume = pm_ptr(gm12u320_resume), + .reset_resume = pm_ptr(gm12u320_resume), }; module_usb_driver(gm12u320_usb_driver); diff --git a/drivers/gpu/drm/tiny/hx8357d.c b/drivers/gpu/drm/tiny/hx8357d.c index 9f634f720817..cdc4486e059b 100644 --- a/drivers/gpu/drm/tiny/hx8357d.c +++ b/drivers/gpu/drm/tiny/hx8357d.c @@ -181,10 +181,7 @@ out_exit: } static const struct drm_simple_display_pipe_funcs hx8357d_pipe_funcs = { - .mode_valid = mipi_dbi_pipe_mode_valid, - .enable = yx240qv29_enable, - .disable = mipi_dbi_pipe_disable, - .update = mipi_dbi_pipe_update, + DRM_MIPI_DBI_SIMPLE_DISPLAY_PIPE_FUNCS(yx240qv29_enable), }; static const struct drm_display_mode yx350hv15_mode = { diff --git a/drivers/gpu/drm/tiny/ili9163.c b/drivers/gpu/drm/tiny/ili9163.c index ca0451f79962..bc4384d410fc 100644 --- a/drivers/gpu/drm/tiny/ili9163.c +++ b/drivers/gpu/drm/tiny/ili9163.c @@ -100,11 +100,7 @@ out_exit: } static const struct drm_simple_display_pipe_funcs ili9163_pipe_funcs = { - .mode_valid = mipi_dbi_pipe_mode_valid, - .enable = yx240qv29_enable, - .disable = mipi_dbi_pipe_disable, - .update = mipi_dbi_pipe_update, - .prepare_fb = drm_gem_simple_display_pipe_prepare_fb, + DRM_MIPI_DBI_SIMPLE_DISPLAY_PIPE_FUNCS(yx240qv29_enable), }; static const struct drm_display_mode yx240qv29_mode = { diff --git a/drivers/gpu/drm/tiny/ili9225.c b/drivers/gpu/drm/tiny/ili9225.c index 815bab285823..077c6ff5a2e1 100644 --- a/drivers/gpu/drm/tiny/ili9225.c +++ b/drivers/gpu/drm/tiny/ili9225.c @@ -25,6 +25,7 @@ #include <drm/drm_framebuffer.h> #include <drm/drm_gem_atomic_helper.h> #include <drm/drm_gem_dma_helper.h> +#include <drm/drm_gem_framebuffer_helper.h> #include <drm/drm_managed.h> #include <drm/drm_mipi_dbi.h> #include <drm/drm_rect.h> @@ -76,9 +77,9 @@ static inline int ili9225_command(struct mipi_dbi *dbi, u8 cmd, u16 data) return mipi_dbi_command_buf(dbi, cmd, par, 2); } -static void ili9225_fb_dirty(struct drm_framebuffer *fb, struct drm_rect *rect) +static void ili9225_fb_dirty(struct iosys_map *src, struct drm_framebuffer *fb, + struct drm_rect *rect) { - struct drm_gem_dma_object *dma_obj = drm_fb_dma_get_gem_obj(fb, 0); struct mipi_dbi_dev *dbidev = drm_to_mipi_dbi_dev(fb->dev); unsigned int height = rect->y2 - rect->y1; unsigned int width = rect->x2 - rect->x1; @@ -86,13 +87,10 @@ static void ili9225_fb_dirty(struct drm_framebuffer *fb, struct drm_rect *rect) bool swap = dbi->swap_bytes; u16 x_start, y_start; u16 x1, x2, y1, y2; - int idx, ret = 0; + int ret = 0; bool full; void *tr; - if (!drm_dev_enter(fb->dev, &idx)) - return; - full = width == fb->width && height == fb->height; DRM_DEBUG_KMS("Flushing [FB:%d] " DRM_RECT_FMT "\n", fb->base.id, DRM_RECT_ARG(rect)); @@ -100,11 +98,11 @@ static void ili9225_fb_dirty(struct drm_framebuffer *fb, struct drm_rect *rect) if (!dbi->dc || !full || swap || fb->format->format == DRM_FORMAT_XRGB8888) { tr = dbidev->tx_buf; - ret = mipi_dbi_buf_copy(dbidev->tx_buf, fb, rect, swap); + ret = mipi_dbi_buf_copy(tr, src, fb, rect, swap); if (ret) goto err_msg; } else { - tr = dma_obj->vaddr; + tr = src->vaddr; /* TODO: Use mapping abstraction properly */ } switch (dbidev->rotation) { @@ -155,21 +153,27 @@ static void ili9225_fb_dirty(struct drm_framebuffer *fb, struct drm_rect *rect) err_msg: if (ret) dev_err_once(fb->dev->dev, "Failed to update display %d\n", ret); - - drm_dev_exit(idx); } static void ili9225_pipe_update(struct drm_simple_display_pipe *pipe, struct drm_plane_state *old_state) { struct drm_plane_state *state = pipe->plane.state; + struct drm_shadow_plane_state *shadow_plane_state = to_drm_shadow_plane_state(state); + struct drm_framebuffer *fb = state->fb; struct drm_rect rect; + int idx; if (!pipe->crtc.state->active) return; + if (!drm_dev_enter(fb->dev, &idx)) + return; + if (drm_atomic_helper_damage_merged(old_state, state, &rect)) - ili9225_fb_dirty(state->fb, &rect); + ili9225_fb_dirty(&shadow_plane_state->data[0], fb, &rect); + + drm_dev_exit(idx); } static void ili9225_pipe_enable(struct drm_simple_display_pipe *pipe, @@ -177,6 +181,7 @@ static void ili9225_pipe_enable(struct drm_simple_display_pipe *pipe, struct drm_plane_state *plane_state) { struct mipi_dbi_dev *dbidev = drm_to_mipi_dbi_dev(pipe->crtc.dev); + struct drm_shadow_plane_state *shadow_plane_state = to_drm_shadow_plane_state(plane_state); struct drm_framebuffer *fb = plane_state->fb; struct device *dev = pipe->crtc.dev->dev; struct mipi_dbi *dbi = &dbidev->dbi; @@ -276,7 +281,8 @@ static void ili9225_pipe_enable(struct drm_simple_display_pipe *pipe, ili9225_command(dbi, ILI9225_DISPLAY_CONTROL_1, 0x1017); - ili9225_fb_dirty(fb, &rect); + ili9225_fb_dirty(&shadow_plane_state->data[0], fb, &rect); + out_exit: drm_dev_exit(idx); } @@ -326,9 +332,15 @@ static int ili9225_dbi_command(struct mipi_dbi *dbi, u8 *cmd, u8 *par, } static const struct drm_simple_display_pipe_funcs ili9225_pipe_funcs = { + .mode_valid = mipi_dbi_pipe_mode_valid, .enable = ili9225_pipe_enable, .disable = ili9225_pipe_disable, .update = ili9225_pipe_update, + .begin_fb_access = mipi_dbi_pipe_begin_fb_access, + .end_fb_access = mipi_dbi_pipe_end_fb_access, + .reset_plane = mipi_dbi_pipe_reset_plane, + .duplicate_plane_state = mipi_dbi_pipe_duplicate_plane_state, + .destroy_plane_state = mipi_dbi_pipe_destroy_plane_state, }; static const struct drm_display_mode ili9225_mode = { diff --git a/drivers/gpu/drm/tiny/ili9341.c b/drivers/gpu/drm/tiny/ili9341.c index 420f6005a956..47b61c3bf145 100644 --- a/drivers/gpu/drm/tiny/ili9341.c +++ b/drivers/gpu/drm/tiny/ili9341.c @@ -137,10 +137,7 @@ out_exit: } static const struct drm_simple_display_pipe_funcs ili9341_pipe_funcs = { - .mode_valid = mipi_dbi_pipe_mode_valid, - .enable = yx240qv29_enable, - .disable = mipi_dbi_pipe_disable, - .update = mipi_dbi_pipe_update, + DRM_MIPI_DBI_SIMPLE_DISPLAY_PIPE_FUNCS(yx240qv29_enable), }; static const struct drm_display_mode yx240qv29_mode = { diff --git a/drivers/gpu/drm/tiny/ili9486.c b/drivers/gpu/drm/tiny/ili9486.c index 1bb847466b10..02265c898816 100644 --- a/drivers/gpu/drm/tiny/ili9486.c +++ b/drivers/gpu/drm/tiny/ili9486.c @@ -43,6 +43,7 @@ static int waveshare_command(struct mipi_dbi *mipi, u8 *cmd, u8 *par, size_t num) { struct spi_device *spi = mipi->spi; + unsigned int bpw = 8; void *data = par; u32 speed_hz; int i, ret; @@ -56,8 +57,6 @@ static int waveshare_command(struct mipi_dbi *mipi, u8 *cmd, u8 *par, * The displays are Raspberry Pi HATs and connected to the 8-bit only * SPI controller, so 16-bit command and parameters need byte swapping * before being transferred as 8-bit on the big endian SPI bus. - * Pixel data bytes have already been swapped before this function is - * called. */ buf[0] = cpu_to_be16(*cmd); gpiod_set_value_cansleep(mipi->dc, 0); @@ -71,12 +70,18 @@ static int waveshare_command(struct mipi_dbi *mipi, u8 *cmd, u8 *par, for (i = 0; i < num; i++) buf[i] = cpu_to_be16(par[i]); num *= 2; - speed_hz = mipi_dbi_spi_cmd_max_speed(spi, num); data = buf; } + /* + * Check whether pixel data bytes needs to be swapped or not + */ + if (*cmd == MIPI_DCS_WRITE_MEMORY_START && !mipi->swap_bytes) + bpw = 16; + gpiod_set_value_cansleep(mipi->dc, 1); - ret = mipi_dbi_spi_transfer(spi, speed_hz, 8, data, num); + speed_hz = mipi_dbi_spi_cmd_max_speed(spi, num); + ret = mipi_dbi_spi_transfer(spi, speed_hz, bpw, data, num); free: kfree(buf); @@ -150,10 +155,7 @@ static void waveshare_enable(struct drm_simple_display_pipe *pipe, } static const struct drm_simple_display_pipe_funcs waveshare_pipe_funcs = { - .mode_valid = mipi_dbi_pipe_mode_valid, - .enable = waveshare_enable, - .disable = mipi_dbi_pipe_disable, - .update = mipi_dbi_pipe_update, + DRM_MIPI_DBI_SIMPLE_DISPLAY_PIPE_FUNCS(waveshare_enable), }; static const struct drm_display_mode waveshare_mode = { @@ -183,6 +185,8 @@ MODULE_DEVICE_TABLE(of, ili9486_of_match); static const struct spi_device_id ili9486_id[] = { { "ili9486", 0 }, + { "rpi-lcd-35", 0 }, + { "piscreen", 0 }, { } }; MODULE_DEVICE_TABLE(spi, ili9486_id); diff --git a/drivers/gpu/drm/tiny/mi0283qt.c b/drivers/gpu/drm/tiny/mi0283qt.c index 47df2b5a3048..01ff43c8ac3f 100644 --- a/drivers/gpu/drm/tiny/mi0283qt.c +++ b/drivers/gpu/drm/tiny/mi0283qt.c @@ -141,10 +141,7 @@ out_exit: } static const struct drm_simple_display_pipe_funcs mi0283qt_pipe_funcs = { - .mode_valid = mipi_dbi_pipe_mode_valid, - .enable = mi0283qt_enable, - .disable = mipi_dbi_pipe_disable, - .update = mipi_dbi_pipe_update, + DRM_MIPI_DBI_SIMPLE_DISPLAY_PIPE_FUNCS(mi0283qt_enable), }; static const struct drm_display_mode mi0283qt_mode = { diff --git a/drivers/gpu/drm/tiny/ofdrm.c b/drivers/gpu/drm/tiny/ofdrm.c index dc9e4d71b12a..33eefeba437c 100644 --- a/drivers/gpu/drm/tiny/ofdrm.c +++ b/drivers/gpu/drm/tiny/ofdrm.c @@ -1284,14 +1284,7 @@ static struct ofdrm_device *ofdrm_device_create(struct drm_driver *drv, dev->mode_config.min_height = height; dev->mode_config.max_height = max_height; dev->mode_config.funcs = &ofdrm_mode_config_funcs; - switch (depth) { - case 32: - dev->mode_config.preferred_depth = 24; - break; - default: - dev->mode_config.preferred_depth = depth; - break; - } + dev->mode_config.preferred_depth = format->depth; dev->mode_config.quirk_addfb_prefer_host_byte_order = true; /* Primary plane */ @@ -1390,10 +1383,6 @@ static int ofdrm_probe(struct platform_device *pdev) if (ret) return ret; - /* - * FIXME: 24-bit color depth does not work reliably with a 32-bpp - * value. Force the bpp value of the scanout buffer's format. - */ drm_fbdev_generic_setup(dev, drm_format_info_bpp(odev->format, 0)); return 0; diff --git a/drivers/gpu/drm/tiny/panel-mipi-dbi.c b/drivers/gpu/drm/tiny/panel-mipi-dbi.c index 03a7d569cd56..eb9f13f18a02 100644 --- a/drivers/gpu/drm/tiny/panel-mipi-dbi.c +++ b/drivers/gpu/drm/tiny/panel-mipi-dbi.c @@ -212,10 +212,7 @@ out_exit: } static const struct drm_simple_display_pipe_funcs panel_mipi_dbi_pipe_funcs = { - .mode_valid = mipi_dbi_pipe_mode_valid, - .enable = panel_mipi_dbi_enable, - .disable = mipi_dbi_pipe_disable, - .update = mipi_dbi_pipe_update, + DRM_MIPI_DBI_SIMPLE_DISPLAY_PIPE_FUNCS(panel_mipi_dbi_enable), }; DEFINE_DRM_GEM_DMA_FOPS(panel_mipi_dbi_fops); @@ -297,6 +294,11 @@ static int panel_mipi_dbi_spi_probe(struct spi_device *spi) return dev_err_probe(dev, PTR_ERR(dbidev->regulator), "Failed to get regulator 'power'\n"); + dbidev->io_regulator = devm_regulator_get(dev, "io"); + if (IS_ERR(dbidev->io_regulator)) + return dev_err_probe(dev, PTR_ERR(dbidev->io_regulator), + "Failed to get regulator 'io'\n"); + dbidev->backlight = devm_of_find_backlight(dev); if (IS_ERR(dbidev->backlight)) return dev_err_probe(dev, PTR_ERR(dbidev->backlight), "Failed to get backlight\n"); diff --git a/drivers/gpu/drm/tiny/simpledrm.c b/drivers/gpu/drm/tiny/simpledrm.c index 162eb44dcba8..30e928d627e8 100644 --- a/drivers/gpu/drm/tiny/simpledrm.c +++ b/drivers/gpu/drm/tiny/simpledrm.c @@ -739,7 +739,7 @@ static struct simpledrm_device *simpledrm_device_create(struct drm_driver *drv, dev->mode_config.max_width = max_width; dev->mode_config.min_height = height; dev->mode_config.max_height = max_height; - dev->mode_config.preferred_depth = format->cpp[0] * 8; + dev->mode_config.preferred_depth = format->depth; dev->mode_config.funcs = &simpledrm_mode_config_funcs; /* Primary plane */ @@ -834,7 +834,7 @@ static int simpledrm_probe(struct platform_device *pdev) if (ret) return ret; - drm_fbdev_generic_setup(dev, 0); + drm_fbdev_generic_setup(dev, drm_format_info_bpp(sdev->format, 0)); return 0; } diff --git a/drivers/gpu/drm/tiny/st7586.c b/drivers/gpu/drm/tiny/st7586.c index ce57fa9917e5..3cf4eec16a81 100644 --- a/drivers/gpu/drm/tiny/st7586.c +++ b/drivers/gpu/drm/tiny/st7586.c @@ -92,32 +92,28 @@ static void st7586_xrgb8888_to_gray332(u8 *dst, void *vaddr, kfree(buf); } -static int st7586_buf_copy(void *dst, struct drm_framebuffer *fb, +static int st7586_buf_copy(void *dst, struct iosys_map *src, struct drm_framebuffer *fb, struct drm_rect *clip) { - struct drm_gem_dma_object *dma_obj = drm_fb_dma_get_gem_obj(fb, 0); - void *src = dma_obj->vaddr; - int ret = 0; + int ret; ret = drm_gem_fb_begin_cpu_access(fb, DMA_FROM_DEVICE); if (ret) return ret; - st7586_xrgb8888_to_gray332(dst, src, fb, clip); + st7586_xrgb8888_to_gray332(dst, src->vaddr, fb, clip); drm_gem_fb_end_cpu_access(fb, DMA_FROM_DEVICE); return 0; } -static void st7586_fb_dirty(struct drm_framebuffer *fb, struct drm_rect *rect) +static void st7586_fb_dirty(struct iosys_map *src, struct drm_framebuffer *fb, + struct drm_rect *rect) { struct mipi_dbi_dev *dbidev = drm_to_mipi_dbi_dev(fb->dev); struct mipi_dbi *dbi = &dbidev->dbi; - int start, end, idx, ret = 0; - - if (!drm_dev_enter(fb->dev, &idx)) - return; + int start, end, ret = 0; /* 3 pixels per byte, so grow clip to nearest multiple of 3 */ rect->x1 = rounddown(rect->x1, 3); @@ -125,7 +121,7 @@ static void st7586_fb_dirty(struct drm_framebuffer *fb, struct drm_rect *rect) DRM_DEBUG_KMS("Flushing [FB:%d] " DRM_RECT_FMT "\n", fb->base.id, DRM_RECT_ARG(rect)); - ret = st7586_buf_copy(dbidev->tx_buf, fb, rect); + ret = st7586_buf_copy(dbidev->tx_buf, src, fb, rect); if (ret) goto err_msg; @@ -146,21 +142,27 @@ static void st7586_fb_dirty(struct drm_framebuffer *fb, struct drm_rect *rect) err_msg: if (ret) dev_err_once(fb->dev->dev, "Failed to update display %d\n", ret); - - drm_dev_exit(idx); } static void st7586_pipe_update(struct drm_simple_display_pipe *pipe, struct drm_plane_state *old_state) { struct drm_plane_state *state = pipe->plane.state; + struct drm_shadow_plane_state *shadow_plane_state = to_drm_shadow_plane_state(state); + struct drm_framebuffer *fb = state->fb; struct drm_rect rect; + int idx; if (!pipe->crtc.state->active) return; + if (!drm_dev_enter(fb->dev, &idx)) + return; + if (drm_atomic_helper_damage_merged(old_state, state, &rect)) - st7586_fb_dirty(state->fb, &rect); + st7586_fb_dirty(&shadow_plane_state->data[0], fb, &rect); + + drm_dev_exit(idx); } static void st7586_pipe_enable(struct drm_simple_display_pipe *pipe, @@ -168,6 +170,7 @@ static void st7586_pipe_enable(struct drm_simple_display_pipe *pipe, struct drm_plane_state *plane_state) { struct mipi_dbi_dev *dbidev = drm_to_mipi_dbi_dev(pipe->crtc.dev); + struct drm_shadow_plane_state *shadow_plane_state = to_drm_shadow_plane_state(plane_state); struct drm_framebuffer *fb = plane_state->fb; struct mipi_dbi *dbi = &dbidev->dbi; struct drm_rect rect = { @@ -235,7 +238,7 @@ static void st7586_pipe_enable(struct drm_simple_display_pipe *pipe, msleep(100); - st7586_fb_dirty(fb, &rect); + st7586_fb_dirty(&shadow_plane_state->data[0], fb, &rect); mipi_dbi_command(dbi, MIPI_DCS_SET_DISPLAY_ON); out_exit: @@ -263,9 +266,15 @@ static const u32 st7586_formats[] = { }; static const struct drm_simple_display_pipe_funcs st7586_pipe_funcs = { + .mode_valid = mipi_dbi_pipe_mode_valid, .enable = st7586_pipe_enable, .disable = st7586_pipe_disable, .update = st7586_pipe_update, + .begin_fb_access = mipi_dbi_pipe_begin_fb_access, + .end_fb_access = mipi_dbi_pipe_end_fb_access, + .reset_plane = mipi_dbi_pipe_reset_plane, + .duplicate_plane_state = mipi_dbi_pipe_duplicate_plane_state, + .destroy_plane_state = mipi_dbi_pipe_destroy_plane_state, }; static const struct drm_display_mode st7586_mode = { diff --git a/drivers/gpu/drm/tiny/st7735r.c b/drivers/gpu/drm/tiny/st7735r.c index 15d9cf283c66..477eb36fbb70 100644 --- a/drivers/gpu/drm/tiny/st7735r.c +++ b/drivers/gpu/drm/tiny/st7735r.c @@ -133,10 +133,7 @@ out_exit: } static const struct drm_simple_display_pipe_funcs st7735r_pipe_funcs = { - .mode_valid = mipi_dbi_pipe_mode_valid, - .enable = st7735r_pipe_enable, - .disable = mipi_dbi_pipe_disable, - .update = mipi_dbi_pipe_update, + DRM_MIPI_DBI_SIMPLE_DISPLAY_PIPE_FUNCS(st7735r_pipe_enable), }; static const struct st7735r_cfg jd_t18003_t01_cfg = { diff --git a/drivers/gpu/drm/ttm/ttm_bo.c b/drivers/gpu/drm/ttm/ttm_bo.c index c3f4b33136e5..cd266a067773 100644 --- a/drivers/gpu/drm/ttm/ttm_bo.c +++ b/drivers/gpu/drm/ttm/ttm_bo.c @@ -31,8 +31,10 @@ #define pr_fmt(fmt) "[TTM] " fmt -#include <drm/ttm/ttm_bo_driver.h> +#include <drm/ttm/ttm_bo.h> #include <drm/ttm/ttm_placement.h> +#include <drm/ttm/ttm_tt.h> + #include <linux/jiffies.h> #include <linux/slab.h> #include <linux/sched.h> @@ -280,14 +282,13 @@ static int ttm_bo_cleanup_refs(struct ttm_buffer_object *bo, ret = 0; } - if (ret || unlikely(list_empty(&bo->ddestroy))) { + if (ret) { if (unlock_resv) dma_resv_unlock(bo->base.resv); spin_unlock(&bo->bdev->lru_lock); return ret; } - list_del_init(&bo->ddestroy); spin_unlock(&bo->bdev->lru_lock); ttm_bo_cleanup_memtype_use(bo); @@ -300,47 +301,21 @@ static int ttm_bo_cleanup_refs(struct ttm_buffer_object *bo, } /* - * Traverse the delayed list, and call ttm_bo_cleanup_refs on all - * encountered buffers. + * Block for the dma_resv object to become idle, lock the buffer and clean up + * the resource and tt object. */ -bool ttm_bo_delayed_delete(struct ttm_device *bdev, bool remove_all) +static void ttm_bo_delayed_delete(struct work_struct *work) { - struct list_head removed; - bool empty; - - INIT_LIST_HEAD(&removed); - - spin_lock(&bdev->lru_lock); - while (!list_empty(&bdev->ddestroy)) { - struct ttm_buffer_object *bo; - - bo = list_first_entry(&bdev->ddestroy, struct ttm_buffer_object, - ddestroy); - list_move_tail(&bo->ddestroy, &removed); - if (!ttm_bo_get_unless_zero(bo)) - continue; - - if (remove_all || bo->base.resv != &bo->base._resv) { - spin_unlock(&bdev->lru_lock); - dma_resv_lock(bo->base.resv, NULL); - - spin_lock(&bdev->lru_lock); - ttm_bo_cleanup_refs(bo, false, !remove_all, true); + struct ttm_buffer_object *bo; - } else if (dma_resv_trylock(bo->base.resv)) { - ttm_bo_cleanup_refs(bo, false, !remove_all, true); - } else { - spin_unlock(&bdev->lru_lock); - } - - ttm_bo_put(bo); - spin_lock(&bdev->lru_lock); - } - list_splice_tail(&removed, &bdev->ddestroy); - empty = list_empty(&bdev->ddestroy); - spin_unlock(&bdev->lru_lock); + bo = container_of(work, typeof(*bo), delayed_delete); - return empty; + dma_resv_wait_timeout(bo->base.resv, DMA_RESV_USAGE_BOOKKEEP, false, + MAX_SCHEDULE_TIMEOUT); + dma_resv_lock(bo->base.resv, NULL); + ttm_bo_cleanup_memtype_use(bo); + dma_resv_unlock(bo->base.resv); + ttm_bo_put(bo); } static void ttm_bo_release(struct kref *kref) @@ -369,69 +344,58 @@ static void ttm_bo_release(struct kref *kref) drm_vma_offset_remove(bdev->vma_manager, &bo->base.vma_node); ttm_mem_io_free(bdev, bo->resource); - } - if (!dma_resv_test_signaled(bo->base.resv, DMA_RESV_USAGE_BOOKKEEP) || - !dma_resv_trylock(bo->base.resv)) { - /* The BO is not idle, resurrect it for delayed destroy */ - ttm_bo_flush_all_fences(bo); - bo->deleted = true; + if (!dma_resv_test_signaled(bo->base.resv, + DMA_RESV_USAGE_BOOKKEEP) || + !dma_resv_trylock(bo->base.resv)) { + /* The BO is not idle, resurrect it for delayed destroy */ + ttm_bo_flush_all_fences(bo); + bo->deleted = true; - spin_lock(&bo->bdev->lru_lock); - - /* - * Make pinned bos immediately available to - * shrinkers, now that they are queued for - * destruction. - * - * FIXME: QXL is triggering this. Can be removed when the - * driver is fixed. - */ - if (bo->pin_count) { - bo->pin_count = 0; - ttm_resource_move_to_lru_tail(bo->resource); - } + spin_lock(&bo->bdev->lru_lock); - kref_init(&bo->kref); - list_add_tail(&bo->ddestroy, &bdev->ddestroy); - spin_unlock(&bo->bdev->lru_lock); + /* + * Make pinned bos immediately available to + * shrinkers, now that they are queued for + * destruction. + * + * FIXME: QXL is triggering this. Can be removed when the + * driver is fixed. + */ + if (bo->pin_count) { + bo->pin_count = 0; + ttm_resource_move_to_lru_tail(bo->resource); + } - schedule_delayed_work(&bdev->wq, - ((HZ / 100) < 1) ? 1 : HZ / 100); - return; - } + kref_init(&bo->kref); + spin_unlock(&bo->bdev->lru_lock); - spin_lock(&bo->bdev->lru_lock); - list_del(&bo->ddestroy); - spin_unlock(&bo->bdev->lru_lock); + INIT_WORK(&bo->delayed_delete, ttm_bo_delayed_delete); + queue_work(bdev->wq, &bo->delayed_delete); + return; + } - ttm_bo_cleanup_memtype_use(bo); - dma_resv_unlock(bo->base.resv); + ttm_bo_cleanup_memtype_use(bo); + dma_resv_unlock(bo->base.resv); + } atomic_dec(&ttm_glob.bo_count); bo->destroy(bo); } +/** + * ttm_bo_put + * + * @bo: The buffer object. + * + * Unreference a buffer object. + */ void ttm_bo_put(struct ttm_buffer_object *bo) { kref_put(&bo->kref, ttm_bo_release); } EXPORT_SYMBOL(ttm_bo_put); -int ttm_bo_lock_delayed_workqueue(struct ttm_device *bdev) -{ - return cancel_delayed_work_sync(&bdev->wq); -} -EXPORT_SYMBOL(ttm_bo_lock_delayed_workqueue); - -void ttm_bo_unlock_delayed_workqueue(struct ttm_device *bdev, int resched) -{ - if (resched) - schedule_delayed_work(&bdev->wq, - ((HZ / 100) < 1) ? 1 : HZ / 100); -} -EXPORT_SYMBOL(ttm_bo_unlock_delayed_workqueue); - static int ttm_bo_bounce_temp_buffer(struct ttm_buffer_object *bo, struct ttm_resource **mem, struct ttm_operation_ctx *ctx, @@ -475,7 +439,7 @@ static int ttm_bo_evict(struct ttm_buffer_object *bo, bdev->funcs->evict_flags(bo, &placement); if (!placement.num_placement && !placement.num_busy_placement) { - ret = ttm_bo_wait(bo, true, false); + ret = ttm_bo_wait_ctx(bo, ctx); if (ret) return ret; @@ -512,6 +476,14 @@ out: return ret; } +/** + * ttm_bo_eviction_valuable + * + * @bo: The buffer object to evict + * @place: the placement we need to make room for + * + * Check if it is valuable to evict the BO to make room for the given placement. + */ bool ttm_bo_eviction_valuable(struct ttm_buffer_object *bo, const struct ttm_place *place) { @@ -771,13 +743,23 @@ static int ttm_bo_mem_force_space(struct ttm_buffer_object *bo, return ttm_bo_add_move_fence(bo, man, *mem, ctx->no_wait_gpu); } -/* - * Creates space for memory region @mem according to its type. +/** + * ttm_bo_mem_space * - * This function first searches for free space in compatible memory types in - * the priority order defined by the driver. If free space isn't found, then - * ttm_bo_mem_force_space is attempted in priority order to evict and find - * space. + * @bo: Pointer to a struct ttm_buffer_object. the data of which + * we want to allocate space for. + * @proposed_placement: Proposed new placement for the buffer object. + * @mem: A struct ttm_resource. + * @ctx: if and how to sleep, lock buffers and alloc memory + * + * Allocate memory space for the buffer object pointed to by @bo, using + * the placement flags in @placement, potentially evicting other idle buffer objects. + * This function may sleep while waiting for space to become available. + * Returns: + * -EBUSY: No space available (only if no_wait == 1). + * -ENOMEM: Could not allocate memory for the buffer object, either due to + * fragmentation or concurrent allocators. + * -ERESTARTSYS: An interruptible sleep was interrupted by a signal. */ int ttm_bo_mem_space(struct ttm_buffer_object *bo, struct ttm_placement *placement, @@ -883,6 +865,21 @@ out: return ret; } +/** + * ttm_bo_validate + * + * @bo: The buffer object. + * @placement: Proposed placement for the buffer object. + * @ctx: validation parameters. + * + * Changes placement and caching policy of the buffer object + * according proposed placement. + * Returns + * -EINVAL on invalid proposed placement. + * -ENOMEM on out-of-memory condition. + * -EBUSY if no_wait is true and buffer busy. + * -ERESTARTSYS if interrupted by a signal. + */ int ttm_bo_validate(struct ttm_buffer_object *bo, struct ttm_placement *placement, struct ttm_operation_ctx *ctx) @@ -960,7 +957,6 @@ int ttm_bo_init_reserved(struct ttm_device *bdev, struct ttm_buffer_object *bo, int ret; kref_init(&bo->kref); - INIT_LIST_HEAD(&bo->ddestroy); bo->bdev = bdev; bo->type = type; bo->page_alignment = alignment; @@ -1076,6 +1072,11 @@ EXPORT_SYMBOL(ttm_bo_init_validate); * buffer object vm functions. */ +/** + * ttm_bo_unmap_virtual + * + * @bo: tear down the virtual mappings for this BO + */ void ttm_bo_unmap_virtual(struct ttm_buffer_object *bo) { struct ttm_device *bdev = bo->bdev; @@ -1085,6 +1086,20 @@ void ttm_bo_unmap_virtual(struct ttm_buffer_object *bo) } EXPORT_SYMBOL(ttm_bo_unmap_virtual); +/** + * ttm_bo_wait - wait for buffer idle. + * + * @bo: The buffer object. + * @interruptible: Use interruptible wait. + * @no_wait: Return immediately if buffer is busy. + * + * This function must be called with the bo::mutex held, and makes + * sure any previous rendering to the buffer is completed. + * Note: It might be necessary to block validations before the + * wait by reserving the buffer. + * Returns -EBUSY if no_wait is true and the buffer is busy. + * Returns -ERESTARTSYS if interrupted by a signal. + */ int ttm_bo_wait(struct ttm_buffer_object *bo, bool interruptible, bool no_wait) { @@ -1109,6 +1124,12 @@ int ttm_bo_wait(struct ttm_buffer_object *bo, } EXPORT_SYMBOL(ttm_bo_wait); +int ttm_bo_wait_ctx(struct ttm_buffer_object *bo, struct ttm_operation_ctx *ctx) +{ + return ttm_bo_wait(bo, ctx->interruptible, ctx->no_wait_gpu); +} +EXPORT_SYMBOL(ttm_bo_wait_ctx); + int ttm_bo_swapout(struct ttm_buffer_object *bo, struct ttm_operation_ctx *ctx, gfp_t gfp_flags) { @@ -1169,7 +1190,7 @@ int ttm_bo_swapout(struct ttm_buffer_object *bo, struct ttm_operation_ctx *ctx, /* * Make sure BO is idle. */ - ret = ttm_bo_wait(bo, false, false); + ret = ttm_bo_wait_ctx(bo, ctx); if (unlikely(ret != 0)) goto out; diff --git a/drivers/gpu/drm/ttm/ttm_bo_util.c b/drivers/gpu/drm/ttm/ttm_bo_util.c index ba3aa0a0fc43..fee7c20775c0 100644 --- a/drivers/gpu/drm/ttm/ttm_bo_util.c +++ b/drivers/gpu/drm/ttm/ttm_bo_util.c @@ -29,18 +29,11 @@ * Authors: Thomas Hellstrom <thellstrom-at-vmware-dot-com> */ -#include <drm/ttm/ttm_bo_driver.h> +#include <drm/ttm/ttm_bo.h> #include <drm/ttm/ttm_placement.h> +#include <drm/ttm/ttm_tt.h> + #include <drm/drm_cache.h> -#include <drm/drm_vma_manager.h> -#include <linux/iosys-map.h> -#include <linux/io.h> -#include <linux/highmem.h> -#include <linux/wait.h> -#include <linux/slab.h> -#include <linux/vmalloc.h> -#include <linux/module.h> -#include <linux/dma-resv.h> struct ttm_transfer_obj { struct ttm_buffer_object base; @@ -128,6 +121,23 @@ void ttm_move_memcpy(bool clear, } EXPORT_SYMBOL(ttm_move_memcpy); +/** + * ttm_bo_move_memcpy + * + * @bo: A pointer to a struct ttm_buffer_object. + * @interruptible: Sleep interruptible if waiting. + * @no_wait_gpu: Return immediately if the GPU is busy. + * @new_mem: struct ttm_resource indicating where to move. + * + * Fallback move function for a mappable buffer object in mappable memory. + * The function will, if successful, + * free any old aperture space, and set (@new_mem)->mm_node to NULL, + * and update the (@bo)->mem placement flags. If unsuccessful, the old + * data remains untouched, and it's up to the caller to free the + * memory space indicated by @new_mem. + * Returns: + * !0: Failure. + */ int ttm_bo_move_memcpy(struct ttm_buffer_object *bo, struct ttm_operation_ctx *ctx, struct ttm_resource *dst_mem) @@ -230,7 +240,6 @@ static int ttm_buffer_object_transfer(struct ttm_buffer_object *bo, */ atomic_inc(&ttm_glob.bo_count); - INIT_LIST_HEAD(&fbo->base.ddestroy); drm_vma_node_reset(&fbo->base.base.vma_node); kref_init(&fbo->base.kref); @@ -267,6 +276,16 @@ static int ttm_buffer_object_transfer(struct ttm_buffer_object *bo, return 0; } +/** + * ttm_io_prot + * + * bo: ttm buffer object + * res: ttm resource object + * @tmp: Page protection flag for a normal, cached mapping. + * + * Utility function that returns the pgprot_t that should be used for + * setting up a PTE with the caching model indicated by @c_state. + */ pgprot_t ttm_io_prot(struct ttm_buffer_object *bo, struct ttm_resource *res, pgprot_t tmp) { @@ -348,6 +367,22 @@ static int ttm_bo_kmap_ttm(struct ttm_buffer_object *bo, return (!map->virtual) ? -ENOMEM : 0; } +/** + * ttm_bo_kmap + * + * @bo: The buffer object. + * @start_page: The first page to map. + * @num_pages: Number of pages to map. + * @map: pointer to a struct ttm_bo_kmap_obj representing the map. + * + * Sets up a kernel virtual mapping, using ioremap, vmap or kmap to the + * data in the buffer object. The ttm_kmap_obj_virtual function can then be + * used to obtain a virtual address to the data. + * + * Returns + * -ENOMEM: Out of memory. + * -EINVAL: Invalid range. + */ int ttm_bo_kmap(struct ttm_buffer_object *bo, unsigned long start_page, unsigned long num_pages, struct ttm_bo_kmap_obj *map) @@ -375,6 +410,13 @@ int ttm_bo_kmap(struct ttm_buffer_object *bo, } EXPORT_SYMBOL(ttm_bo_kmap); +/** + * ttm_bo_kunmap + * + * @map: Object describing the map to unmap. + * + * Unmaps a kernel map set up by ttm_bo_kmap. + */ void ttm_bo_kunmap(struct ttm_bo_kmap_obj *map) { if (!map->virtual) @@ -400,6 +442,20 @@ void ttm_bo_kunmap(struct ttm_bo_kmap_obj *map) } EXPORT_SYMBOL(ttm_bo_kunmap); +/** + * ttm_bo_vmap + * + * @bo: The buffer object. + * @map: pointer to a struct iosys_map representing the map. + * + * Sets up a kernel virtual mapping, using ioremap or vmap to the + * data in the buffer object. The parameter @map returns the virtual + * address as struct iosys_map. Unmap the buffer with ttm_bo_vunmap(). + * + * Returns + * -ENOMEM: Out of memory. + * -EINVAL: Invalid range. + */ int ttm_bo_vmap(struct ttm_buffer_object *bo, struct iosys_map *map) { struct ttm_resource *mem = bo->resource; @@ -461,6 +517,14 @@ int ttm_bo_vmap(struct ttm_buffer_object *bo, struct iosys_map *map) } EXPORT_SYMBOL(ttm_bo_vmap); +/** + * ttm_bo_vunmap + * + * @bo: The buffer object. + * @map: Object describing the map to unmap. + * + * Unmaps a kernel map set up by ttm_bo_vmap(). + */ void ttm_bo_vunmap(struct ttm_buffer_object *bo, struct iosys_map *map) { struct ttm_resource *mem = bo->resource; @@ -554,6 +618,22 @@ static void ttm_bo_move_pipeline_evict(struct ttm_buffer_object *bo, ttm_resource_free(bo, &bo->resource); } +/** + * ttm_bo_move_accel_cleanup. + * + * @bo: A pointer to a struct ttm_buffer_object. + * @fence: A fence object that signals when moving is complete. + * @evict: This is an evict move. Don't return until the buffer is idle. + * @pipeline: evictions are to be pipelined. + * @new_mem: struct ttm_resource indicating where to move. + * + * Accelerated move function to be called when an accelerated move + * has been scheduled. The function will create a new temporary buffer object + * representing the old placement, and put the sync object on both buffer + * objects. After that the newly created buffer object is unref'd to be + * destroyed when the move is complete. This will help pipeline + * buffer moves. + */ int ttm_bo_move_accel_cleanup(struct ttm_buffer_object *bo, struct dma_fence *fence, bool evict, @@ -582,6 +662,15 @@ int ttm_bo_move_accel_cleanup(struct ttm_buffer_object *bo, } EXPORT_SYMBOL(ttm_bo_move_accel_cleanup); +/** + * ttm_bo_move_sync_cleanup. + * + * @bo: A pointer to a struct ttm_buffer_object. + * @new_mem: struct ttm_resource indicating where to move. + * + * Special case of ttm_bo_move_accel_cleanup where the bo is guaranteed + * by the caller to be idle. Typically used after memcpy buffer moves. + */ void ttm_bo_move_sync_cleanup(struct ttm_buffer_object *bo, struct ttm_resource *new_mem) { diff --git a/drivers/gpu/drm/ttm/ttm_bo_vm.c b/drivers/gpu/drm/ttm/ttm_bo_vm.c index 5a3e4b891377..3ecda6db24b8 100644 --- a/drivers/gpu/drm/ttm/ttm_bo_vm.c +++ b/drivers/gpu/drm/ttm/ttm_bo_vm.c @@ -31,17 +31,12 @@ #define pr_fmt(fmt) "[TTM] " fmt -#include <drm/ttm/ttm_bo_driver.h> +#include <drm/ttm/ttm_bo.h> #include <drm/ttm/ttm_placement.h> -#include <drm/drm_vma_manager.h> +#include <drm/ttm/ttm_tt.h> + #include <drm/drm_drv.h> #include <drm/drm_managed.h> -#include <linux/mm.h> -#include <linux/pfn_t.h> -#include <linux/rbtree.h> -#include <linux/module.h> -#include <linux/uaccess.h> -#include <linux/mem_encrypt.h> static vm_fault_t ttm_bo_vm_fault_idle(struct ttm_buffer_object *bo, struct vm_fault *vmf) @@ -446,6 +441,14 @@ static const struct vm_operations_struct ttm_bo_vm_ops = { .access = ttm_bo_vm_access, }; +/** + * ttm_bo_mmap_obj - mmap memory backed by a ttm buffer object. + * + * @vma: vma as input from the fbdev mmap method. + * @bo: The bo backing the address space. + * + * Maps a buffer object. + */ int ttm_bo_mmap_obj(struct vm_area_struct *vma, struct ttm_buffer_object *bo) { /* Enforce no COW since would have really strange behavior with it. */ diff --git a/drivers/gpu/drm/ttm/ttm_device.c b/drivers/gpu/drm/ttm/ttm_device.c index e7147e304637..c7a1862f322a 100644 --- a/drivers/gpu/drm/ttm/ttm_device.c +++ b/drivers/gpu/drm/ttm/ttm_device.c @@ -29,10 +29,10 @@ #include <linux/mm.h> +#include <drm/ttm/ttm_bo.h> #include <drm/ttm/ttm_device.h> #include <drm/ttm/ttm_tt.h> #include <drm/ttm/ttm_placement.h> -#include <drm/ttm/ttm_bo_api.h> #include "ttm_module.h" @@ -175,16 +175,6 @@ int ttm_device_swapout(struct ttm_device *bdev, struct ttm_operation_ctx *ctx, } EXPORT_SYMBOL(ttm_device_swapout); -static void ttm_device_delayed_workqueue(struct work_struct *work) -{ - struct ttm_device *bdev = - container_of(work, struct ttm_device, wq.work); - - if (!ttm_bo_delayed_delete(bdev, false)) - schedule_delayed_work(&bdev->wq, - ((HZ / 100) < 1) ? 1 : HZ / 100); -} - /** * ttm_device_init * @@ -215,15 +205,19 @@ int ttm_device_init(struct ttm_device *bdev, struct ttm_device_funcs *funcs, if (ret) return ret; + bdev->wq = alloc_workqueue("ttm", WQ_MEM_RECLAIM | WQ_HIGHPRI, 16); + if (!bdev->wq) { + ttm_global_release(); + return -ENOMEM; + } + bdev->funcs = funcs; ttm_sys_man_init(bdev); ttm_pool_init(&bdev->pool, dev, use_dma_alloc, use_dma32); bdev->vma_manager = vma_manager; - INIT_DELAYED_WORK(&bdev->wq, ttm_device_delayed_workqueue); spin_lock_init(&bdev->lru_lock); - INIT_LIST_HEAD(&bdev->ddestroy); INIT_LIST_HEAD(&bdev->pinned); bdev->dev_mapping = mapping; mutex_lock(&ttm_global_mutex); @@ -247,10 +241,8 @@ void ttm_device_fini(struct ttm_device *bdev) list_del(&bdev->device_list); mutex_unlock(&ttm_global_mutex); - cancel_delayed_work_sync(&bdev->wq); - - if (ttm_bo_delayed_delete(bdev, true)) - pr_debug("Delayed destroy list was clean\n"); + drain_workqueue(bdev->wq); + destroy_workqueue(bdev->wq); spin_lock(&bdev->lru_lock); for (i = 0; i < TTM_MAX_BO_PRIORITY; ++i) diff --git a/drivers/gpu/drm/ttm/ttm_execbuf_util.c b/drivers/gpu/drm/ttm/ttm_execbuf_util.c index dbee34a058df..f1c60fa80c2d 100644 --- a/drivers/gpu/drm/ttm/ttm_execbuf_util.c +++ b/drivers/gpu/drm/ttm/ttm_execbuf_util.c @@ -27,11 +27,7 @@ **************************************************************************/ #include <drm/ttm/ttm_execbuf_util.h> -#include <drm/ttm/ttm_bo_driver.h> -#include <drm/ttm/ttm_placement.h> -#include <linux/wait.h> -#include <linux/sched.h> -#include <linux/module.h> +#include <drm/ttm/ttm_bo.h> static void ttm_eu_backoff_reservation_reverse(struct list_head *list, struct ttm_validate_buffer *entry) diff --git a/drivers/gpu/drm/ttm/ttm_pool.c b/drivers/gpu/drm/ttm/ttm_pool.c index 9f6764bf3b15..aa116a7bbae3 100644 --- a/drivers/gpu/drm/ttm/ttm_pool.c +++ b/drivers/gpu/drm/ttm/ttm_pool.c @@ -33,6 +33,7 @@ #include <linux/module.h> #include <linux/dma-mapping.h> +#include <linux/debugfs.h> #include <linux/highmem.h> #include <linux/sched/mm.h> @@ -41,8 +42,8 @@ #endif #include <drm/ttm/ttm_pool.h> -#include <drm/ttm/ttm_bo_driver.h> #include <drm/ttm/ttm_tt.h> +#include <drm/ttm/ttm_bo.h> #include "ttm_module.h" diff --git a/drivers/gpu/drm/ttm/ttm_range_manager.c b/drivers/gpu/drm/ttm/ttm_range_manager.c index 0a8bc0b7f380..ae11d07eb63a 100644 --- a/drivers/gpu/drm/ttm/ttm_range_manager.c +++ b/drivers/gpu/drm/ttm/ttm_range_manager.c @@ -32,7 +32,7 @@ #include <drm/ttm/ttm_device.h> #include <drm/ttm/ttm_placement.h> #include <drm/ttm/ttm_range_manager.h> -#include <drm/ttm/ttm_bo_api.h> +#include <drm/ttm/ttm_bo.h> #include <drm/drm_mm.h> #include <linux/slab.h> #include <linux/spinlock.h> diff --git a/drivers/gpu/drm/ttm/ttm_resource.c b/drivers/gpu/drm/ttm/ttm_resource.c index 328391bb1d87..b8a826a24fb2 100644 --- a/drivers/gpu/drm/ttm/ttm_resource.c +++ b/drivers/gpu/drm/ttm/ttm_resource.c @@ -26,8 +26,9 @@ #include <linux/io-mapping.h> #include <linux/scatterlist.h> +#include <drm/ttm/ttm_bo.h> +#include <drm/ttm/ttm_placement.h> #include <drm/ttm/ttm_resource.h> -#include <drm/ttm/ttm_bo_driver.h> /** * ttm_lru_bulk_move_init - initialize a bulk move structure diff --git a/drivers/gpu/drm/ttm/ttm_tt.c b/drivers/gpu/drm/ttm/ttm_tt.c index d505603930a7..ab725d9d14a6 100644 --- a/drivers/gpu/drm/ttm/ttm_tt.c +++ b/drivers/gpu/drm/ttm/ttm_tt.c @@ -36,7 +36,8 @@ #include <linux/file.h> #include <linux/module.h> #include <drm/drm_cache.h> -#include <drm/ttm/ttm_bo_driver.h> +#include <drm/ttm/ttm_bo.h> +#include <drm/ttm/ttm_tt.h> #include "ttm_module.h" diff --git a/drivers/gpu/drm/v3d/v3d_debugfs.c b/drivers/gpu/drm/v3d/v3d_debugfs.c index efbde124c296..330669f51fa7 100644 --- a/drivers/gpu/drm/v3d/v3d_debugfs.c +++ b/drivers/gpu/drm/v3d/v3d_debugfs.c @@ -79,8 +79,8 @@ static const struct v3d_reg_def v3d_csd_reg_defs[] = { static int v3d_v3d_debugfs_regs(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 v3d_dev *v3d = to_v3d_dev(dev); int i, core; @@ -126,8 +126,8 @@ static int v3d_v3d_debugfs_regs(struct seq_file *m, void *unused) static int v3d_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 v3d_dev *v3d = to_v3d_dev(dev); u32 ident0, ident1, ident2, ident3, cores; int core; @@ -188,8 +188,8 @@ static int v3d_v3d_debugfs_ident(struct seq_file *m, void *unused) static int v3d_debugfs_bo_stats(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 v3d_dev *v3d = to_v3d_dev(dev); mutex_lock(&v3d->bo_lock); @@ -204,8 +204,8 @@ static int v3d_debugfs_bo_stats(struct seq_file *m, void *unused) static int v3d_measure_clock(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 v3d_dev *v3d = to_v3d_dev(dev); uint32_t cycles; int core = 0; @@ -236,7 +236,7 @@ static int v3d_measure_clock(struct seq_file *m, void *unused) return 0; } -static const struct drm_info_list v3d_debugfs_list[] = { +static const struct drm_debugfs_info v3d_debugfs_list[] = { {"v3d_ident", v3d_v3d_debugfs_ident, 0}, {"v3d_regs", v3d_v3d_debugfs_regs, 0}, {"measure_clock", v3d_measure_clock, 0}, @@ -246,7 +246,5 @@ static const struct drm_info_list v3d_debugfs_list[] = { void v3d_debugfs_init(struct drm_minor *minor) { - drm_debugfs_create_files(v3d_debugfs_list, - ARRAY_SIZE(v3d_debugfs_list), - minor->debugfs_root, minor); + drm_debugfs_add_files(minor->dev, v3d_debugfs_list, ARRAY_SIZE(v3d_debugfs_list)); } diff --git a/drivers/gpu/drm/v3d/v3d_gem.c b/drivers/gpu/drm/v3d/v3d_gem.c index 96af1cb5202a..6e152ef26358 100644 --- a/drivers/gpu/drm/v3d/v3d_gem.c +++ b/drivers/gpu/drm/v3d/v3d_gem.c @@ -299,10 +299,6 @@ v3d_lookup_bos(struct drm_device *dev, u64 bo_handles, u32 bo_count) { - u32 *handles; - int ret = 0; - int i; - job->bo_count = bo_count; if (!job->bo_count) { @@ -313,48 +309,9 @@ v3d_lookup_bos(struct drm_device *dev, return -EINVAL; } - job->bo = kvmalloc_array(job->bo_count, - sizeof(struct drm_gem_dma_object *), - GFP_KERNEL | __GFP_ZERO); - if (!job->bo) { - DRM_DEBUG("Failed to allocate validated BO pointers\n"); - return -ENOMEM; - } - - handles = kvmalloc_array(job->bo_count, sizeof(u32), GFP_KERNEL); - if (!handles) { - ret = -ENOMEM; - DRM_DEBUG("Failed to allocate incoming GEM handles\n"); - goto fail; - } - - if (copy_from_user(handles, - (void __user *)(uintptr_t)bo_handles, - job->bo_count * sizeof(u32))) { - ret = -EFAULT; - DRM_DEBUG("Failed to copy in GEM handles\n"); - goto fail; - } - - spin_lock(&file_priv->table_lock); - for (i = 0; i < job->bo_count; i++) { - struct drm_gem_object *bo = idr_find(&file_priv->object_idr, - handles[i]); - if (!bo) { - DRM_DEBUG("Failed to look up GEM BO %d: %d\n", - i, handles[i]); - ret = -ENOENT; - spin_unlock(&file_priv->table_lock); - goto fail; - } - drm_gem_object_get(bo); - job->bo[i] = bo; - } - spin_unlock(&file_priv->table_lock); - -fail: - kvfree(handles); - return ret; + return drm_gem_objects_lookup(file_priv, + (void __user *)(uintptr_t)bo_handles, + job->bo_count, &job->bo); } static void @@ -363,11 +320,11 @@ v3d_job_free(struct kref *ref) struct v3d_job *job = container_of(ref, struct v3d_job, refcount); int i; - for (i = 0; i < job->bo_count; i++) { - if (job->bo[i]) + if (job->bo) { + for (i = 0; i < job->bo_count; i++) drm_gem_object_put(job->bo[i]); + kvfree(job->bo); } - kvfree(job->bo); dma_fence_put(job->irq_fence); dma_fence_put(job->done_fence); diff --git a/drivers/gpu/drm/vboxvideo/vbox_drv.c b/drivers/gpu/drm/vboxvideo/vbox_drv.c index b450f449a3ab..12fee99dbfe8 100644 --- a/drivers/gpu/drm/vboxvideo/vbox_drv.c +++ b/drivers/gpu/drm/vboxvideo/vbox_drv.c @@ -102,7 +102,6 @@ static void vbox_pci_remove(struct pci_dev *pdev) vbox_hw_fini(vbox); } -#ifdef CONFIG_PM_SLEEP static int vbox_pm_suspend(struct device *dev) { struct vbox_private *vbox = dev_get_drvdata(dev); @@ -160,16 +159,13 @@ static const struct dev_pm_ops vbox_pm_ops = { .poweroff = vbox_pm_poweroff, .restore = vbox_pm_resume, }; -#endif static struct pci_driver vbox_pci_driver = { .name = DRIVER_NAME, .id_table = pciidlist, .probe = vbox_pci_probe, .remove = vbox_pci_remove, -#ifdef CONFIG_PM_SLEEP - .driver.pm = &vbox_pm_ops, -#endif + .driver.pm = pm_sleep_ptr(&vbox_pm_ops), }; DEFINE_DRM_GEM_FOPS(vbox_fops); diff --git a/drivers/gpu/drm/vc4/Kconfig b/drivers/gpu/drm/vc4/Kconfig index 246305d17a52..f423941c028d 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 + bool "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 43d9b3a6a352..c2b7573bd92b 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); @@ -993,15 +993,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 0108613e79d5..35a6b5907278 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), @@ -486,21 +495,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 +530,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 +684,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 +1090,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 +1122,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 +1137,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 +1152,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 +1167,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 +1182,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 +1197,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 +1211,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 +1225,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 +1277,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 +1340,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 +1382,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 +1391,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..e05be9a34156 100644 --- a/drivers/gpu/drm/vc4/vc4_dsi.c +++ b/drivers/gpu/drm/vc4/vc4_dsi.c @@ -556,8 +556,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 +609,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 +623,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 +653,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 +803,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 +840,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 +876,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 +908,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 +1131,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 +1138,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 +1163,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 +1349,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 +1384,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 +1401,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 +1411,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 +1664,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 +1769,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 +1791,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 +1817,11 @@ 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; + dsi->bridge.of_node = dev->of_node; + 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 12a00d644b61..dfb7f41b28df 100644 --- a/drivers/gpu/drm/vc4/vc4_hdmi.c +++ b/drivers/gpu/drm/vc4/vc4_hdmi.c @@ -160,8 +160,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; @@ -402,6 +402,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 @@ -430,7 +431,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, @@ -1995,13 +2004,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..024a2cdff5b2 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); @@ -740,7 +740,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 +749,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); @@ -778,11 +808,9 @@ static int vc4_hvs_bind(struct device *dev, struct device *master, void *data) u32 dispctrl; u32 reg; - 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 +863,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 +872,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, diff --git a/drivers/gpu/drm/vc4/vc4_kms.c b/drivers/gpu/drm/vc4/vc4_kms.c index 8fbeecdf2ec4..53d9f30460cf 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 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..d8bf00b442e9 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; @@ -447,13 +647,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 +716,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 +744,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 +751,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; diff --git a/drivers/gpu/drm/virtio/virtgpu_drv.h b/drivers/gpu/drm/virtio/virtgpu_drv.h index b7a64c7dcc2c..af6ffb696086 100644 --- a/drivers/gpu/drm/virtio/virtgpu_drv.h +++ b/drivers/gpu/drm/virtio/virtgpu_drv.h @@ -165,6 +165,8 @@ struct virtio_gpu_vbuffer { struct virtio_gpu_object_array *objs; struct list_head list; + + uint32_t seqno; }; struct virtio_gpu_output { @@ -194,6 +196,7 @@ struct virtio_gpu_queue { spinlock_t qlock; wait_queue_head_t ack_queue; struct work_struct dequeue_work; + uint32_t seqno; }; struct virtio_gpu_drv_capset { diff --git a/drivers/gpu/drm/virtio/virtgpu_trace.h b/drivers/gpu/drm/virtio/virtgpu_trace.h index 711ecc2bd241..031bc77689d5 100644 --- a/drivers/gpu/drm/virtio/virtgpu_trace.h +++ b/drivers/gpu/drm/virtio/virtgpu_trace.h @@ -9,40 +9,44 @@ #define TRACE_INCLUDE_FILE virtgpu_trace DECLARE_EVENT_CLASS(virtio_gpu_cmd, - TP_PROTO(struct virtqueue *vq, struct virtio_gpu_ctrl_hdr *hdr), - TP_ARGS(vq, hdr), + TP_PROTO(struct virtqueue *vq, struct virtio_gpu_ctrl_hdr *hdr, u32 seqno), + TP_ARGS(vq, hdr, seqno), TP_STRUCT__entry( __field(int, dev) __field(unsigned int, vq) - __field(const char *, name) + __string(name, vq->name) __field(u32, type) __field(u32, flags) __field(u64, fence_id) __field(u32, ctx_id) + __field(u32, num_free) + __field(u32, seqno) ), TP_fast_assign( __entry->dev = vq->vdev->index; __entry->vq = vq->index; - __entry->name = vq->name; + __assign_str(name, vq->name); __entry->type = le32_to_cpu(hdr->type); __entry->flags = le32_to_cpu(hdr->flags); __entry->fence_id = le64_to_cpu(hdr->fence_id); __entry->ctx_id = le32_to_cpu(hdr->ctx_id); + __entry->num_free = vq->num_free; + __entry->seqno = seqno; ), - TP_printk("vdev=%d vq=%u name=%s type=0x%x flags=0x%x fence_id=%llu ctx_id=%u", - __entry->dev, __entry->vq, __entry->name, + TP_printk("vdev=%d vq=%u name=%s type=0x%x flags=0x%x fence_id=%llu ctx_id=%u num_free=%u seqno=%u", + __entry->dev, __entry->vq, __get_str(name), __entry->type, __entry->flags, __entry->fence_id, - __entry->ctx_id) + __entry->ctx_id, __entry->num_free, __entry->seqno) ); DEFINE_EVENT(virtio_gpu_cmd, virtio_gpu_cmd_queue, - TP_PROTO(struct virtqueue *vq, struct virtio_gpu_ctrl_hdr *hdr), - TP_ARGS(vq, hdr) + TP_PROTO(struct virtqueue *vq, struct virtio_gpu_ctrl_hdr *hdr, u32 seqno), + TP_ARGS(vq, hdr, seqno) ); DEFINE_EVENT(virtio_gpu_cmd, virtio_gpu_cmd_response, - TP_PROTO(struct virtqueue *vq, struct virtio_gpu_ctrl_hdr *hdr), - TP_ARGS(vq, hdr) + TP_PROTO(struct virtqueue *vq, struct virtio_gpu_ctrl_hdr *hdr, u32 seqno), + TP_ARGS(vq, hdr, seqno) ); #endif diff --git a/drivers/gpu/drm/virtio/virtgpu_vq.c b/drivers/gpu/drm/virtio/virtgpu_vq.c index 9ff8660b50ad..a04a9b20896d 100644 --- a/drivers/gpu/drm/virtio/virtgpu_vq.c +++ b/drivers/gpu/drm/virtio/virtgpu_vq.c @@ -215,7 +215,7 @@ void virtio_gpu_dequeue_ctrl_func(struct work_struct *work) list_for_each_entry(entry, &reclaim_list, list) { resp = (struct virtio_gpu_ctrl_hdr *)entry->resp_buf; - trace_virtio_gpu_cmd_response(vgdev->ctrlq.vq, resp); + trace_virtio_gpu_cmd_response(vgdev->ctrlq.vq, resp, entry->seqno); if (resp->type != cpu_to_le32(VIRTIO_GPU_RESP_OK_NODATA)) { if (le32_to_cpu(resp->type) >= VIRTIO_GPU_RESP_ERR_UNSPEC) { @@ -261,6 +261,10 @@ void virtio_gpu_dequeue_cursor_func(struct work_struct *work) spin_unlock(&vgdev->cursorq.qlock); list_for_each_entry_safe(entry, tmp, &reclaim_list, list) { + struct virtio_gpu_ctrl_hdr *resp = + (struct virtio_gpu_ctrl_hdr *)entry->resp_buf; + + trace_virtio_gpu_cmd_response(vgdev->cursorq.vq, resp, entry->seqno); list_del(&entry->list); free_vbuf(vgdev, entry); } @@ -353,7 +357,8 @@ again: ret = virtqueue_add_sgs(vq, sgs, outcnt, incnt, vbuf, GFP_ATOMIC); WARN_ON(ret); - trace_virtio_gpu_cmd_queue(vq, virtio_gpu_vbuf_ctrl_hdr(vbuf)); + vbuf->seqno = ++vgdev->ctrlq.seqno; + trace_virtio_gpu_cmd_queue(vq, virtio_gpu_vbuf_ctrl_hdr(vbuf), vbuf->seqno); atomic_inc(&vgdev->pending_commands); @@ -465,8 +470,10 @@ retry: spin_lock(&vgdev->cursorq.qlock); goto retry; } else { + vbuf->seqno = ++vgdev->cursorq.seqno; trace_virtio_gpu_cmd_queue(vq, - virtio_gpu_vbuf_ctrl_hdr(vbuf)); + virtio_gpu_vbuf_ctrl_hdr(vbuf), + vbuf->seqno); notify = virtqueue_kick_prepare(vq); } diff --git a/drivers/gpu/drm/vkms/vkms_drv.c b/drivers/gpu/drm/vkms/vkms_drv.c index 293dbca50c31..6d3a2d57d992 100644 --- a/drivers/gpu/drm/vkms/vkms_drv.c +++ b/drivers/gpu/drm/vkms/vkms_drv.c @@ -57,7 +57,8 @@ static void vkms_release(struct drm_device *dev) { struct vkms_device *vkms = drm_device_to_vkms_device(dev); - destroy_workqueue(vkms->output.composer_workq); + if (vkms->output.composer_workq) + destroy_workqueue(vkms->output.composer_workq); } static void vkms_atomic_commit_tail(struct drm_atomic_state *old_state) @@ -91,8 +92,8 @@ static void vkms_atomic_commit_tail(struct drm_atomic_state *old_state) static int vkms_config_show(struct seq_file *m, void *data) { - 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 vkms_device *vkmsdev = drm_device_to_vkms_device(dev); seq_printf(m, "writeback=%d\n", vkmsdev->config->writeback); @@ -102,24 +103,16 @@ static int vkms_config_show(struct seq_file *m, void *data) return 0; } -static const struct drm_info_list vkms_config_debugfs_list[] = { +static const struct drm_debugfs_info vkms_config_debugfs_list[] = { { "vkms_config", vkms_config_show, 0 }, }; -static void vkms_config_debugfs_init(struct drm_minor *minor) -{ - drm_debugfs_create_files(vkms_config_debugfs_list, ARRAY_SIZE(vkms_config_debugfs_list), - minor->debugfs_root, minor); -} - static const struct drm_driver vkms_driver = { .driver_features = DRIVER_MODESET | DRIVER_ATOMIC | DRIVER_GEM, .release = vkms_release, .fops = &vkms_driver_fops, DRM_GEM_SHMEM_DRIVER_OPS, - .debugfs_init = vkms_config_debugfs_init, - .name = DRIVER_NAME, .desc = DRIVER_DESC, .date = DRIVER_DATE, @@ -201,6 +194,9 @@ static int vkms_create(struct vkms_config *config) if (ret) goto out_devres; + drm_debugfs_add_files(&vkms_device->drm, vkms_config_debugfs_list, + ARRAY_SIZE(vkms_config_debugfs_list)); + ret = drm_dev_register(&vkms_device->drm, 0); if (ret) goto out_devres; @@ -218,6 +214,7 @@ out_unregister: static int __init vkms_init(void) { + int ret; struct vkms_config *config; config = kmalloc(sizeof(*config), GFP_KERNEL); @@ -230,7 +227,11 @@ static int __init vkms_init(void) config->writeback = enable_writeback; config->overlay = enable_overlay; - return vkms_create(config); + ret = vkms_create(config); + if (ret) + kfree(config); + + return ret; } static void vkms_destroy(struct vkms_config *config) diff --git a/drivers/gpu/drm/vkms/vkms_drv.h b/drivers/gpu/drm/vkms/vkms_drv.h index 0a67b8073f7e..4a248567efb2 100644 --- a/drivers/gpu/drm/vkms/vkms_drv.h +++ b/drivers/gpu/drm/vkms/vkms_drv.h @@ -12,8 +12,8 @@ #include <drm/drm_encoder.h> #include <drm/drm_writeback.h> -#define XRES_MIN 20 -#define YRES_MIN 20 +#define XRES_MIN 10 +#define YRES_MIN 10 #define XRES_DEF 1024 #define YRES_DEF 768 diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_cmdbuf.c b/drivers/gpu/drm/vmwgfx/vmwgfx_cmdbuf.c index 3c06df2a5474..2b843ff4b437 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_cmdbuf.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_cmdbuf.c @@ -28,7 +28,7 @@ #include <linux/dmapool.h> #include <linux/pci.h> -#include <drm/ttm/ttm_bo_api.h> +#include <drm/ttm/ttm_bo.h> #include "vmwgfx_drv.h" diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c index bd02cb0e6837..9ad28346aff7 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c @@ -40,7 +40,6 @@ #include <drm/drm_ioctl.h> #include <drm/drm_module.h> #include <drm/drm_sysfs.h> -#include <drm/ttm/ttm_bo_driver.h> #include <drm/ttm/ttm_range_manager.h> #include <drm/ttm/ttm_placement.h> #include <generated/utsrelease.h> diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h index b062b020b378..4b612fc9758c 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h @@ -37,8 +37,10 @@ #include <drm/drm_file.h> #include <drm/drm_rect.h> -#include <drm/ttm/ttm_bo_driver.h> #include <drm/ttm/ttm_execbuf_util.h> +#include <drm/ttm/ttm_tt.h> +#include <drm/ttm/ttm_placement.h> +#include <drm/ttm/ttm_bo.h> #include "ttm_object.h" diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c b/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c index a5379f6fb5ab..43cec8e37e4d 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c @@ -29,7 +29,7 @@ #include "vmwgfx_drv.h" #include "vmwgfx_reg.h" -#include <drm/ttm/ttm_bo_api.h> +#include <drm/ttm/ttm_bo.h> #include <drm/ttm/ttm_placement.h> #include "vmwgfx_so.h" #include "vmwgfx_binding.h" diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_gmr.c b/drivers/gpu/drm/vmwgfx/vmwgfx_gmr.c index c482e5298e11..20158a92acc7 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_gmr.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_gmr.c @@ -25,7 +25,6 @@ * **************************************************************************/ -#include <drm/ttm/ttm_bo_driver.h> #include "vmwgfx_drv.h" diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_gmrid_manager.c b/drivers/gpu/drm/vmwgfx/vmwgfx_gmrid_manager.c index abd5e3323ebf..ceb4d3d3b965 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_gmrid_manager.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_gmrid_manager.c @@ -29,7 +29,6 @@ */ #include "vmwgfx_drv.h" -#include <drm/ttm/ttm_bo_driver.h> #include <drm/ttm/ttm_placement.h> #include <linux/idr.h> #include <linux/spinlock.h> diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_system_manager.c b/drivers/gpu/drm/vmwgfx/vmwgfx_system_manager.c index d3007bf1b8f5..ee7964cbdaca 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_system_manager.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_system_manager.c @@ -26,7 +26,6 @@ #include "vmwgfx_drv.h" -#include <drm/ttm/ttm_bo_driver.h> #include <drm/ttm/ttm_device.h> #include <drm/ttm/ttm_placement.h> #include <drm/ttm/ttm_resource.h> diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_ttm_buffer.c b/drivers/gpu/drm/vmwgfx/vmwgfx_ttm_buffer.c index 4e3938e62c08..856a352a72a6 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_ttm_buffer.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_ttm_buffer.c @@ -26,7 +26,6 @@ **************************************************************************/ #include "vmwgfx_drv.h" -#include <drm/ttm/ttm_bo_driver.h> #include <drm/ttm/ttm_placement.h> static const struct ttm_place vram_placement_flags = { diff --git a/drivers/gpu/ipu-v3/ipu-common.c b/drivers/gpu/ipu-v3/ipu-common.c index 118318513e2d..c35eac1116f5 100644 --- a/drivers/gpu/ipu-v3/ipu-common.c +++ b/drivers/gpu/ipu-v3/ipu-common.c @@ -1165,6 +1165,7 @@ static int ipu_add_client_devices(struct ipu_soc *ipu, unsigned long ipu_base) pdev = platform_device_alloc(reg->name, id++); if (!pdev) { ret = -ENOMEM; + of_node_put(of_node); goto err_register; } diff --git a/include/drm/drm_atomic.h b/include/drm/drm_atomic.h index 10b1990bc1f6..92586ab55ef5 100644 --- a/include/drm/drm_atomic.h +++ b/include/drm/drm_atomic.h @@ -515,17 +515,17 @@ struct drm_private_state * __must_check drm_atomic_get_private_obj_state(struct drm_atomic_state *state, struct drm_private_obj *obj); struct drm_private_state * -drm_atomic_get_old_private_obj_state(struct drm_atomic_state *state, +drm_atomic_get_old_private_obj_state(const struct drm_atomic_state *state, struct drm_private_obj *obj); struct drm_private_state * -drm_atomic_get_new_private_obj_state(struct drm_atomic_state *state, +drm_atomic_get_new_private_obj_state(const struct drm_atomic_state *state, struct drm_private_obj *obj); struct drm_connector * -drm_atomic_get_old_connector_for_encoder(struct drm_atomic_state *state, +drm_atomic_get_old_connector_for_encoder(const struct drm_atomic_state *state, struct drm_encoder *encoder); struct drm_connector * -drm_atomic_get_new_connector_for_encoder(struct drm_atomic_state *state, +drm_atomic_get_new_connector_for_encoder(const struct drm_atomic_state *state, struct drm_encoder *encoder); /** @@ -540,7 +540,7 @@ drm_atomic_get_new_connector_for_encoder(struct drm_atomic_state *state, * @drm_atomic_get_new_crtc_state should be used instead. */ static inline struct drm_crtc_state * -drm_atomic_get_existing_crtc_state(struct drm_atomic_state *state, +drm_atomic_get_existing_crtc_state(const struct drm_atomic_state *state, struct drm_crtc *crtc) { return state->crtcs[drm_crtc_index(crtc)].state; @@ -555,7 +555,7 @@ drm_atomic_get_existing_crtc_state(struct drm_atomic_state *state, * NULL if the CRTC is not part of the global atomic state. */ static inline struct drm_crtc_state * -drm_atomic_get_old_crtc_state(struct drm_atomic_state *state, +drm_atomic_get_old_crtc_state(const struct drm_atomic_state *state, struct drm_crtc *crtc) { return state->crtcs[drm_crtc_index(crtc)].old_state; @@ -569,7 +569,7 @@ drm_atomic_get_old_crtc_state(struct drm_atomic_state *state, * NULL if the CRTC is not part of the global atomic state. */ static inline struct drm_crtc_state * -drm_atomic_get_new_crtc_state(struct drm_atomic_state *state, +drm_atomic_get_new_crtc_state(const struct drm_atomic_state *state, struct drm_crtc *crtc) { return state->crtcs[drm_crtc_index(crtc)].new_state; @@ -587,7 +587,7 @@ drm_atomic_get_new_crtc_state(struct drm_atomic_state *state, * @drm_atomic_get_new_plane_state should be used instead. */ static inline struct drm_plane_state * -drm_atomic_get_existing_plane_state(struct drm_atomic_state *state, +drm_atomic_get_existing_plane_state(const struct drm_atomic_state *state, struct drm_plane *plane) { return state->planes[drm_plane_index(plane)].state; @@ -602,7 +602,7 @@ drm_atomic_get_existing_plane_state(struct drm_atomic_state *state, * NULL if the plane is not part of the global atomic state. */ static inline struct drm_plane_state * -drm_atomic_get_old_plane_state(struct drm_atomic_state *state, +drm_atomic_get_old_plane_state(const struct drm_atomic_state *state, struct drm_plane *plane) { return state->planes[drm_plane_index(plane)].old_state; @@ -617,7 +617,7 @@ drm_atomic_get_old_plane_state(struct drm_atomic_state *state, * NULL if the plane is not part of the global atomic state. */ static inline struct drm_plane_state * -drm_atomic_get_new_plane_state(struct drm_atomic_state *state, +drm_atomic_get_new_plane_state(const struct drm_atomic_state *state, struct drm_plane *plane) { return state->planes[drm_plane_index(plane)].new_state; @@ -635,7 +635,7 @@ drm_atomic_get_new_plane_state(struct drm_atomic_state *state, * @drm_atomic_get_new_connector_state should be used instead. */ static inline struct drm_connector_state * -drm_atomic_get_existing_connector_state(struct drm_atomic_state *state, +drm_atomic_get_existing_connector_state(const struct drm_atomic_state *state, struct drm_connector *connector) { int index = drm_connector_index(connector); @@ -655,7 +655,7 @@ drm_atomic_get_existing_connector_state(struct drm_atomic_state *state, * or NULL if the connector is not part of the global atomic state. */ static inline struct drm_connector_state * -drm_atomic_get_old_connector_state(struct drm_atomic_state *state, +drm_atomic_get_old_connector_state(const struct drm_atomic_state *state, struct drm_connector *connector) { int index = drm_connector_index(connector); @@ -675,7 +675,7 @@ drm_atomic_get_old_connector_state(struct drm_atomic_state *state, * or NULL if the connector is not part of the global atomic state. */ static inline struct drm_connector_state * -drm_atomic_get_new_connector_state(struct drm_atomic_state *state, +drm_atomic_get_new_connector_state(const struct drm_atomic_state *state, struct drm_connector *connector) { int index = drm_connector_index(connector); @@ -713,7 +713,7 @@ drm_atomic_get_new_connector_state(struct drm_atomic_state *state, * Read-only pointer to the current plane state. */ static inline const struct drm_plane_state * -__drm_atomic_get_current_plane_state(struct drm_atomic_state *state, +__drm_atomic_get_current_plane_state(const struct drm_atomic_state *state, struct drm_plane *plane) { if (state->planes[drm_plane_index(plane)].state) @@ -1134,10 +1134,10 @@ struct drm_bridge_state * drm_atomic_get_bridge_state(struct drm_atomic_state *state, struct drm_bridge *bridge); struct drm_bridge_state * -drm_atomic_get_old_bridge_state(struct drm_atomic_state *state, +drm_atomic_get_old_bridge_state(const struct drm_atomic_state *state, struct drm_bridge *bridge); struct drm_bridge_state * -drm_atomic_get_new_bridge_state(struct drm_atomic_state *state, +drm_atomic_get_new_bridge_state(const struct drm_atomic_state *state, struct drm_bridge *bridge); #endif /* DRM_ATOMIC_H_ */ diff --git a/include/drm/drm_atomic_state_helper.h b/include/drm/drm_atomic_state_helper.h index 192766656b88..b9740edb2658 100644 --- a/include/drm/drm_atomic_state_helper.h +++ b/include/drm/drm_atomic_state_helper.h @@ -26,6 +26,7 @@ #include <linux/types.h> +struct drm_atomic_state; struct drm_bridge; struct drm_bridge_state; struct drm_crtc; @@ -70,6 +71,9 @@ void __drm_atomic_helper_connector_state_reset(struct drm_connector_state *conn_ void __drm_atomic_helper_connector_reset(struct drm_connector *connector, struct drm_connector_state *conn_state); void drm_atomic_helper_connector_reset(struct drm_connector *connector); +void drm_atomic_helper_connector_tv_reset(struct drm_connector *connector); +int drm_atomic_helper_connector_tv_check(struct drm_connector *connector, + struct drm_atomic_state *state); void drm_atomic_helper_connector_tv_margins_reset(struct drm_connector *connector); void __drm_atomic_helper_connector_duplicate_state(struct drm_connector *connector, diff --git a/include/drm/drm_audio_component.h b/include/drm/drm_audio_component.h index 0d36bfd1a4cd..5a4cd1fa8e2a 100644 --- a/include/drm/drm_audio_component.h +++ b/include/drm/drm_audio_component.h @@ -4,6 +4,9 @@ #ifndef _DRM_AUDIO_COMPONENT_H_ #define _DRM_AUDIO_COMPONENT_H_ +#include <linux/completion.h> +#include <linux/types.h> + struct drm_audio_component; struct device; diff --git a/include/drm/drm_bridge.h b/include/drm/drm_bridge.h index 6b65b0dfb4fb..42f86327b40a 100644 --- a/include/drm/drm_bridge.h +++ b/include/drm/drm_bridge.h @@ -297,12 +297,6 @@ struct drm_bridge_funcs { * not enable the display link feeding the next bridge in the chain (if * there is one) when this callback is called. * - * Note that this function will only be invoked in the context of an - * atomic commit. It will not be invoked from - * &drm_bridge_chain_pre_enable. It would be prudent to also provide an - * implementation of @pre_enable if you are expecting driver calls into - * &drm_bridge_chain_pre_enable. - * * The @atomic_pre_enable callback is optional. */ void (*atomic_pre_enable)(struct drm_bridge *bridge, @@ -323,11 +317,6 @@ struct drm_bridge_funcs { * callback must enable the display link feeding the next bridge in the * chain if there is one. * - * Note that this function will only be invoked in the context of an - * atomic commit. It will not be invoked from &drm_bridge_chain_enable. - * It would be prudent to also provide an implementation of @enable if - * you are expecting driver calls into &drm_bridge_chain_enable. - * * The @atomic_enable callback is optional. */ void (*atomic_enable)(struct drm_bridge *bridge, @@ -345,12 +334,6 @@ struct drm_bridge_funcs { * The bridge can assume that the display pipe (i.e. clocks and timing * signals) feeding it is still running when this callback is called. * - * Note that this function will only be invoked in the context of an - * atomic commit. It will not be invoked from - * &drm_bridge_chain_disable. It would be prudent to also provide an - * implementation of @disable if you are expecting driver calls into - * &drm_bridge_chain_disable. - * * The @atomic_disable callback is optional. */ void (*atomic_disable)(struct drm_bridge *bridge, @@ -370,13 +353,6 @@ struct drm_bridge_funcs { * signals) feeding it is no longer running when this callback is * called. * - * Note that this function will only be invoked in the context of an - * atomic commit. It will not be invoked from - * &drm_bridge_chain_post_disable. - * It would be prudent to also provide an implementation of - * @post_disable if you are expecting driver calls into - * &drm_bridge_chain_post_disable. - * * The @atomic_post_disable callback is optional. */ void (*atomic_post_disable)(struct drm_bridge *bridge, @@ -769,6 +745,14 @@ struct drm_bridge { */ bool interlace_allowed; /** + * @pre_enable_prev_first: The bridge requires that the prev + * bridge @pre_enable function is called before its @pre_enable, + * and conversely for post_disable. This is most frequently a + * requirement for DSI devices which need the host to be initialised + * before the peripheral. + */ + bool pre_enable_prev_first; + /** * @ddc: Associated I2C adapter for DDC access, if any. */ struct i2c_adapter *ddc; @@ -876,13 +860,9 @@ enum drm_mode_status drm_bridge_chain_mode_valid(struct drm_bridge *bridge, const struct drm_display_info *info, const struct drm_display_mode *mode); -void drm_bridge_chain_disable(struct drm_bridge *bridge); -void drm_bridge_chain_post_disable(struct drm_bridge *bridge); void drm_bridge_chain_mode_set(struct drm_bridge *bridge, const struct drm_display_mode *mode, const struct drm_display_mode *adjusted_mode); -void drm_bridge_chain_pre_enable(struct drm_bridge *bridge); -void drm_bridge_chain_enable(struct drm_bridge *bridge); int drm_atomic_bridge_chain_check(struct drm_bridge *bridge, struct drm_crtc_state *crtc_state, diff --git a/include/drm/drm_connector.h b/include/drm/drm_connector.h index 565cf9d3c550..9037f1317aee 100644 --- a/include/drm/drm_connector.h +++ b/include/drm/drm_connector.h @@ -144,6 +144,65 @@ enum subpixel_order { }; /** + * enum drm_connector_tv_mode - Analog TV output mode + * + * This enum is used to indicate the TV output mode used on an analog TV + * connector. + * + * WARNING: The values of this enum is uABI since they're exposed in the + * "TV mode" connector property. + */ +enum drm_connector_tv_mode { + /** + * @DRM_MODE_TV_MODE_NTSC: CCIR System M (aka 525-lines) + * together with the NTSC Color Encoding. + */ + DRM_MODE_TV_MODE_NTSC, + + /** + * @DRM_MODE_TV_MODE_NTSC_443: Variant of + * @DRM_MODE_TV_MODE_NTSC. Uses a color subcarrier frequency + * of 4.43 MHz. + */ + DRM_MODE_TV_MODE_NTSC_443, + + /** + * @DRM_MODE_TV_MODE_NTSC_J: Variant of @DRM_MODE_TV_MODE_NTSC + * used in Japan. Uses a black level equals to the blanking + * level. + */ + DRM_MODE_TV_MODE_NTSC_J, + + /** + * @DRM_MODE_TV_MODE_PAL: CCIR System B together with the PAL + * color system. + */ + DRM_MODE_TV_MODE_PAL, + + /** + * @DRM_MODE_TV_MODE_PAL_M: CCIR System M (aka 525-lines) + * together with the PAL color encoding + */ + DRM_MODE_TV_MODE_PAL_M, + + /** + * @DRM_MODE_TV_MODE_PAL_N: CCIR System N together with the PAL + * color encoding. It uses 625 lines, but has a color subcarrier + * frequency of 3.58MHz, the SECAM color space, and narrower + * channels compared to most of the other PAL variants. + */ + DRM_MODE_TV_MODE_PAL_N, + + /** + * @DRM_MODE_TV_MODE_SECAM: CCIR System B together with the + * SECAM color system. + */ + DRM_MODE_TV_MODE_SECAM, + + DRM_MODE_TV_MODE_MAX, +}; + +/** * struct drm_scrambling: sink's scrambling support. */ struct drm_scrambling { @@ -701,6 +760,7 @@ struct drm_connector_tv_margins { * @select_subconnector: selected subconnector * @subconnector: detected subconnector * @margins: TV margins + * @legacy_mode: Legacy TV mode, driver specific value * @mode: TV mode * @brightness: brightness in percent * @contrast: contrast in percent @@ -713,6 +773,7 @@ struct drm_tv_connector_state { enum drm_mode_subconnector select_subconnector; enum drm_mode_subconnector subconnector; struct drm_connector_tv_margins margins; + unsigned int legacy_mode; unsigned int mode; unsigned int brightness; unsigned int contrast; @@ -1313,6 +1374,18 @@ struct drm_cmdline_mode { * @tv_margins: TV margins to apply to the mode. */ struct drm_connector_tv_margins tv_margins; + + /** + * @tv_mode: TV mode standard. See DRM_MODE_TV_MODE_*. + */ + enum drm_connector_tv_mode tv_mode; + + /** + * @tv_mode_specified: + * + * Did the mode have a preferred TV mode? + */ + bool tv_mode_specified; }; /** @@ -1810,19 +1883,24 @@ const char *drm_get_subpixel_order_name(enum subpixel_order order); const char *drm_get_dpms_name(int val); const char *drm_get_dvi_i_subconnector_name(int val); const char *drm_get_dvi_i_select_name(int val); +const char *drm_get_tv_mode_name(int val); const char *drm_get_tv_subconnector_name(int val); const char *drm_get_tv_select_name(int val); const char *drm_get_dp_subconnector_name(int val); const char *drm_get_content_protection_name(int val); const char *drm_get_hdcp_content_type_name(int val); +int drm_get_tv_mode_from_name(const char *name, size_t len); + int drm_mode_create_dvi_i_properties(struct drm_device *dev); void drm_connector_attach_dp_subconnector_property(struct drm_connector *connector); int drm_mode_create_tv_margin_properties(struct drm_device *dev); +int drm_mode_create_tv_properties_legacy(struct drm_device *dev, + unsigned int num_modes, + const char * const modes[]); int drm_mode_create_tv_properties(struct drm_device *dev, - unsigned int num_modes, - const char * const modes[]); + unsigned int supported_tv_modes); void drm_connector_attach_tv_margin_properties(struct drm_connector *conn); int drm_mode_create_scaling_mode_property(struct drm_device *dev); int drm_connector_attach_content_type_property(struct drm_connector *dev); diff --git a/include/drm/drm_debugfs.h b/include/drm/drm_debugfs.h index 2188dc83957f..53b7297260a5 100644 --- a/include/drm/drm_debugfs.h +++ b/include/drm/drm_debugfs.h @@ -79,12 +79,43 @@ struct drm_info_node { struct dentry *dent; }; +/** + * struct drm_debugfs_info - debugfs info list entry + * + * This structure represents a debugfs file to be created by the drm + * core. + */ +struct drm_debugfs_info { + const char *name; + int (*show)(struct seq_file*, void*); + u32 driver_features; + void *data; +}; + +/** + * struct drm_debugfs_entry - Per-device debugfs node structure + * + * This structure represents a debugfs file, as an instantiation of a &struct + * drm_debugfs_info on a &struct drm_device. + */ +struct drm_debugfs_entry { + struct drm_device *dev; + struct drm_debugfs_info file; + struct list_head list; +}; + #if defined(CONFIG_DEBUG_FS) void drm_debugfs_create_files(const struct drm_info_list *files, int count, struct dentry *root, struct drm_minor *minor); int drm_debugfs_remove_files(const struct drm_info_list *files, int count, struct drm_minor *minor); + +void drm_debugfs_add_file(struct drm_device *dev, const char *name, + int (*show)(struct seq_file*, void*), void *data); + +void drm_debugfs_add_files(struct drm_device *dev, + const struct drm_debugfs_info *files, int count); #else static inline void drm_debugfs_create_files(const struct drm_info_list *files, int count, struct dentry *root, @@ -96,6 +127,16 @@ static inline int drm_debugfs_remove_files(const struct drm_info_list *files, { return 0; } + +static inline void drm_debugfs_add_file(struct drm_device *dev, const char *name, + int (*show)(struct seq_file*, void*), + void *data) +{} + +static inline void drm_debugfs_add_files(struct drm_device *dev, + const struct drm_debugfs_info *files, + int count) +{} #endif #endif /* _DRM_DEBUGFS_H_ */ diff --git a/include/drm/drm_device.h b/include/drm/drm_device.h index 933ce2048e20..a68c6a312b46 100644 --- a/include/drm/drm_device.h +++ b/include/drm/drm_device.h @@ -298,6 +298,21 @@ struct drm_device { */ struct drm_fb_helper *fb_helper; + /** + * @debugfs_mutex: + * + * Protects &debugfs_list access. + */ + struct mutex debugfs_mutex; + + /** + * @debugfs_list: + * + * List of debugfs files to be created by the DRM device. The files + * must be added during drm_dev_register(). + */ + struct list_head debugfs_list; + /* Everything below here is for legacy driver, never use! */ /* private: */ #if IS_ENABLED(CONFIG_DRM_LEGACY) diff --git a/include/drm/drm_gem_atomic_helper.h b/include/drm/drm_gem_atomic_helper.h index 6970ccb787e2..40b8b039518e 100644 --- a/include/drm/drm_gem_atomic_helper.h +++ b/include/drm/drm_gem_atomic_helper.h @@ -15,8 +15,6 @@ struct drm_simple_display_pipe; */ int drm_gem_plane_helper_prepare_fb(struct drm_plane *plane, struct drm_plane_state *state); -int drm_gem_simple_display_pipe_prepare_fb(struct drm_simple_display_pipe *pipe, - struct drm_plane_state *plane_state); /* * Helpers for planes with shadow buffers diff --git a/include/drm/drm_gem_ttm_helper.h b/include/drm/drm_gem_ttm_helper.h index 4c003b4f173e..7b53d673ae7e 100644 --- a/include/drm/drm_gem_ttm_helper.h +++ b/include/drm/drm_gem_ttm_helper.h @@ -7,8 +7,7 @@ #include <drm/drm_device.h> #include <drm/drm_gem.h> -#include <drm/ttm/ttm_bo_api.h> -#include <drm/ttm/ttm_bo_driver.h> +#include <drm/ttm/ttm_bo.h> struct iosys_map; diff --git a/include/drm/drm_gem_vram_helper.h b/include/drm/drm_gem_vram_helper.h index c083a1d71cf4..d3e8920c0b64 100644 --- a/include/drm/drm_gem_vram_helper.h +++ b/include/drm/drm_gem_vram_helper.h @@ -8,8 +8,8 @@ #include <drm/drm_gem_ttm_helper.h> #include <drm/drm_ioctl.h> #include <drm/drm_modes.h> -#include <drm/ttm/ttm_bo_api.h> -#include <drm/ttm/ttm_bo_driver.h> +#include <drm/ttm/ttm_bo.h> +#include <drm/ttm/ttm_placement.h> #include <linux/container_of.h> #include <linux/iosys-map.h> diff --git a/include/drm/drm_kunit_helpers.h b/include/drm/drm_kunit_helpers.h new file mode 100644 index 000000000000..ed013fdcc1ff --- /dev/null +++ b/include/drm/drm_kunit_helpers.h @@ -0,0 +1,91 @@ +// SPDX-License-Identifier: GPL-2.0 + +#ifndef DRM_KUNIT_HELPERS_H_ +#define DRM_KUNIT_HELPERS_H_ + +#include <kunit/test.h> + +struct drm_device; +struct kunit; + +struct device *drm_kunit_helper_alloc_device(struct kunit *test); +void drm_kunit_helper_free_device(struct kunit *test, struct device *dev); + +struct drm_device * +__drm_kunit_helper_alloc_drm_device_with_driver(struct kunit *test, + struct device *dev, + size_t size, size_t offset, + const struct drm_driver *driver); + +/** + * drm_kunit_helper_alloc_drm_device_with_driver - Allocates a mock DRM device for KUnit tests + * @_test: The test context object + * @_dev: The parent device object + * @_type: the type of the struct which contains struct &drm_device + * @_member: the name of the &drm_device within @_type. + * @_drv: Mocked DRM device driver features + * + * This function creates a struct &drm_device from @_dev and @_drv. + * + * @_dev should be allocated using drm_kunit_helper_alloc_device(). + * + * The driver is tied to the @_test context and will get cleaned at the + * end of the test. The drm_device is allocated through + * devm_drm_dev_alloc() and will thus be freed through a device-managed + * resource. + * + * Returns: + * A pointer to the new drm_device, or an ERR_PTR() otherwise. + */ +#define drm_kunit_helper_alloc_drm_device_with_driver(_test, _dev, _type, _member, _drv) \ + ((_type *)__drm_kunit_helper_alloc_drm_device_with_driver(_test, _dev, \ + sizeof(_type), \ + offsetof(_type, _member), \ + _drv)) + +static inline struct drm_device * +__drm_kunit_helper_alloc_drm_device(struct kunit *test, + struct device *dev, + size_t size, size_t offset, + u32 features) +{ + struct drm_driver *driver; + + driver = kunit_kzalloc(test, sizeof(*driver), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, driver); + + driver->driver_features = features; + + return __drm_kunit_helper_alloc_drm_device_with_driver(test, dev, + size, offset, + driver); +} + +/** + * drm_kunit_helper_alloc_drm_device - Allocates a mock DRM device for KUnit tests + * @_test: The test context object + * @_dev: The parent device object + * @_type: the type of the struct which contains struct &drm_device + * @_member: the name of the &drm_device within @_type. + * @_features: Mocked DRM device driver features + * + * This function creates a struct &drm_driver and will create a struct + * &drm_device from @_dev and that driver. + * + * @_dev should be allocated using drm_kunit_helper_alloc_device(). + * + * The driver is tied to the @_test context and will get cleaned at the + * end of the test. The drm_device is allocated through + * devm_drm_dev_alloc() and will thus be freed through a device-managed + * resource. + * + * Returns: + * A pointer to the new drm_device, or an ERR_PTR() otherwise. + */ +#define drm_kunit_helper_alloc_drm_device(_test, _dev, _type, _member, _feat) \ + ((_type *)__drm_kunit_helper_alloc_drm_device(_test, _dev, \ + sizeof(_type), \ + offsetof(_type, _member), \ + _feat)) + +#endif // DRM_KUNIT_HELPERS_H_ diff --git a/include/drm/drm_mipi_dbi.h b/include/drm/drm_mipi_dbi.h index 14eaecb1825c..816f196b3d4c 100644 --- a/include/drm/drm_mipi_dbi.h +++ b/include/drm/drm_mipi_dbi.h @@ -13,9 +13,10 @@ #include <drm/drm_simple_kms_helper.h> struct drm_rect; -struct spi_device; struct gpio_desc; +struct iosys_map; struct regulator; +struct spi_device; /** * struct mipi_dbi - MIPI DBI interface @@ -122,11 +123,16 @@ struct mipi_dbi_dev { struct backlight_device *backlight; /** - * @regulator: power regulator (optional) + * @regulator: power regulator (Vdd) (optional) */ struct regulator *regulator; /** + * @io_regulator: I/O power regulator (Vddi) (optional) + */ + struct regulator *io_regulator; + + /** * @dbi: MIPI DBI interface */ struct mipi_dbi dbi; @@ -163,6 +169,15 @@ void mipi_dbi_enable_flush(struct mipi_dbi_dev *dbidev, struct drm_crtc_state *crtc_state, struct drm_plane_state *plan_state); void mipi_dbi_pipe_disable(struct drm_simple_display_pipe *pipe); +int mipi_dbi_pipe_begin_fb_access(struct drm_simple_display_pipe *pipe, + struct drm_plane_state *plane_state); +void mipi_dbi_pipe_end_fb_access(struct drm_simple_display_pipe *pipe, + struct drm_plane_state *plane_state); +void mipi_dbi_pipe_reset_plane(struct drm_simple_display_pipe *pipe); +struct drm_plane_state *mipi_dbi_pipe_duplicate_plane_state(struct drm_simple_display_pipe *pipe); +void mipi_dbi_pipe_destroy_plane_state(struct drm_simple_display_pipe *pipe, + struct drm_plane_state *plane_state); + void mipi_dbi_hw_reset(struct mipi_dbi *dbi); bool mipi_dbi_display_is_on(struct mipi_dbi *dbi); int mipi_dbi_poweron_reset(struct mipi_dbi_dev *dbidev); @@ -176,8 +191,9 @@ int mipi_dbi_command_read(struct mipi_dbi *dbi, u8 cmd, u8 *val); int mipi_dbi_command_buf(struct mipi_dbi *dbi, u8 cmd, u8 *data, size_t len); int mipi_dbi_command_stackbuf(struct mipi_dbi *dbi, u8 cmd, const u8 *data, size_t len); -int mipi_dbi_buf_copy(void *dst, struct drm_framebuffer *fb, +int mipi_dbi_buf_copy(void *dst, struct iosys_map *src, struct drm_framebuffer *fb, struct drm_rect *clip, bool swap); + /** * mipi_dbi_command - MIPI DCS command with optional parameter(s) * @dbi: MIPI DBI structure @@ -207,4 +223,25 @@ void mipi_dbi_debugfs_init(struct drm_minor *minor); static inline void mipi_dbi_debugfs_init(struct drm_minor *minor) {} #endif +/** + * DRM_MIPI_DBI_SIMPLE_DISPLAY_PIPE_FUNCS - Initializes struct drm_simple_display_pipe_funcs + * for MIPI-DBI devices + * @enable_: Enable-callback implementation + * + * This macro initializes struct drm_simple_display_pipe_funcs with default + * values for MIPI-DBI-based devices. The only callback that depends on the + * hardware is @enable, for which the driver has to provide an implementation. + * MIPI-based drivers are encouraged to use this macro for initialization. + */ +#define DRM_MIPI_DBI_SIMPLE_DISPLAY_PIPE_FUNCS(enable_) \ + .mode_valid = mipi_dbi_pipe_mode_valid, \ + .enable = (enable_), \ + .disable = mipi_dbi_pipe_disable, \ + .update = mipi_dbi_pipe_update, \ + .begin_fb_access = mipi_dbi_pipe_begin_fb_access, \ + .end_fb_access = mipi_dbi_pipe_end_fb_access, \ + .reset_plane = mipi_dbi_pipe_reset_plane, \ + .duplicate_plane_state = mipi_dbi_pipe_duplicate_plane_state, \ + .destroy_plane_state = mipi_dbi_pipe_destroy_plane_state + #endif /* __LINUX_MIPI_DBI_H */ diff --git a/include/drm/drm_mode_config.h b/include/drm/drm_mode_config.h index 5362702fffe1..e5b053001d22 100644 --- a/include/drm/drm_mode_config.h +++ b/include/drm/drm_mode_config.h @@ -712,11 +712,21 @@ struct drm_mode_config { * between different TV connector types. */ struct drm_property *tv_select_subconnector_property; + /** - * @tv_mode_property: Optional TV property to select + * @legacy_tv_mode_property: Optional TV property to select * the output TV mode. + * + * Superseded by @tv_mode_property + */ + struct drm_property *legacy_tv_mode_property; + + /** + * @tv_mode_property: Optional TV property to select the TV + * standard output on the connector. */ struct drm_property *tv_mode_property; + /** * @tv_left_margin_property: Optional TV property to set the left * margin (expressed in pixels). diff --git a/include/drm/drm_modes.h b/include/drm/drm_modes.h index b0c680e6f670..c613f0abe9dc 100644 --- a/include/drm/drm_modes.h +++ b/include/drm/drm_modes.h @@ -468,6 +468,23 @@ bool drm_mode_is_420_also(const struct drm_display_info *display, bool drm_mode_is_420(const struct drm_display_info *display, const struct drm_display_mode *mode); +struct drm_display_mode *drm_analog_tv_mode(struct drm_device *dev, + enum drm_connector_tv_mode mode, + unsigned long pixel_clock_hz, + unsigned int hdisplay, + unsigned int vdisplay, + bool interlace); + +static inline struct drm_display_mode *drm_mode_analog_ntsc_480i(struct drm_device *dev) +{ + return drm_analog_tv_mode(dev, DRM_MODE_TV_MODE_NTSC, 13500000, 720, 480, true); +} + +static inline struct drm_display_mode *drm_mode_analog_pal_576i(struct drm_device *dev) +{ + return drm_analog_tv_mode(dev, DRM_MODE_TV_MODE_PAL, 13500000, 720, 576, true); +} + struct drm_display_mode *drm_cvt_mode(struct drm_device *dev, int hdisplay, int vdisplay, int vrefresh, bool reduced, bool interlaced, diff --git a/include/drm/drm_panel.h b/include/drm/drm_panel.h index 994bfcdd84c5..432fab2347eb 100644 --- a/include/drm/drm_panel.h +++ b/include/drm/drm_panel.h @@ -188,6 +188,16 @@ struct drm_panel { * Panel entry in registry. */ struct list_head list; + + /** + * @prepare_prev_first: + * + * The previous controller should be prepared first, before the prepare + * for the panel is called. This is largely required for DSI panels + * where the DSI host controller should be initialised to LP-11 before + * the panel is powered up. + */ + bool prepare_prev_first; }; void drm_panel_init(struct drm_panel *panel, struct device *dev, diff --git a/include/drm/drm_plane.h b/include/drm/drm_plane.h index 447e664e49d5..51291983ea44 100644 --- a/include/drm/drm_plane.h +++ b/include/drm/drm_plane.h @@ -77,8 +77,8 @@ struct drm_plane_state { * write this field directly for a driver's implicit fence. * * Drivers should store any implicit fence in this from their - * &drm_plane_helper_funcs.prepare_fb callback. See drm_gem_plane_helper_prepare_fb() - * and drm_gem_simple_display_pipe_prepare_fb() for suitable helpers. + * &drm_plane_helper_funcs.prepare_fb callback. See + * drm_gem_plane_helper_prepare_fb() for a suitable helper. */ struct dma_fence *fence; diff --git a/include/drm/drm_probe_helper.h b/include/drm/drm_probe_helper.h index 5880daa14624..4977e0ab72db 100644 --- a/include/drm/drm_probe_helper.h +++ b/include/drm/drm_probe_helper.h @@ -35,5 +35,6 @@ int drm_connector_helper_get_modes_from_ddc(struct drm_connector *connector); int drm_connector_helper_get_modes_fixed(struct drm_connector *connector, const struct drm_display_mode *fixed_mode); int drm_connector_helper_get_modes(struct drm_connector *connector); +int drm_connector_helper_tv_get_modes(struct drm_connector *connector); #endif diff --git a/include/drm/drm_simple_kms_helper.h b/include/drm/drm_simple_kms_helper.h index 2298fe3af4cd..b2486d073763 100644 --- a/include/drm/drm_simple_kms_helper.h +++ b/include/drm/drm_simple_kms_helper.h @@ -117,9 +117,9 @@ struct drm_simple_display_pipe_funcs { * more details. * * For GEM drivers who neither have a @prepare_fb nor @cleanup_fb hook - * set drm_gem_simple_display_pipe_prepare_fb() is called automatically + * set, drm_gem_plane_helper_prepare_fb() is called automatically * to implement this. Other drivers which need additional plane - * processing can call drm_gem_simple_display_pipe_prepare_fb() from + * processing can call drm_gem_plane_helper_prepare_fb() from * their @prepare_fb hook. */ int (*prepare_fb)(struct drm_simple_display_pipe *pipe, diff --git a/include/drm/ttm/ttm_bo_api.h b/include/drm/ttm/ttm_bo.h index 44a538ee5e2a..d87232472435 100644 --- a/include/drm/ttm/ttm_bo_api.h +++ b/include/drm/ttm/ttm_bo.h @@ -32,28 +32,24 @@ #define _TTM_BO_API_H_ #include <drm/drm_gem.h> -#include <drm/drm_vma_manager.h> + #include <linux/kref.h> #include <linux/list.h> -#include <linux/wait.h> -#include <linux/mutex.h> -#include <linux/mm.h> -#include <linux/bitmap.h> -#include <linux/dma-resv.h> - -#include "ttm_resource.h" -struct ttm_global; +#include "ttm_device.h" -struct ttm_device; +/* Default number of pre-faulted pages in the TTM fault handler */ +#define TTM_BO_VM_NUM_PREFAULT 16 struct iosys_map; -struct drm_mm_node; - +struct ttm_global; +struct ttm_device; struct ttm_placement; - struct ttm_place; +struct ttm_resource; +struct ttm_resource_manager; +struct ttm_tt; /** * enum ttm_bo_type @@ -68,15 +64,12 @@ struct ttm_place; * @ttm_bo_type_sg: Buffer made from dmabuf sg table shared with another * driver. */ - enum ttm_bo_type { ttm_bo_type_device, ttm_bo_type_kernel, ttm_bo_type_sg }; -struct ttm_tt; - /** * struct ttm_buffer_object * @@ -85,18 +78,11 @@ struct ttm_tt; * @type: The bo type. * @page_alignment: Page alignment. * @destroy: Destruction function. If NULL, kfree is used. - * @num_pages: Actual number of pages. * @kref: Reference count of this buffer object. When this refcount reaches * zero, the object is destroyed or put on the delayed delete list. - * @mem: structure describing current placement. + * @resource: structure describing current placement. * @ttm: TTM structure holding system pages. - * @evicted: Whether the object was evicted without user-space knowing. * @deleted: True if the object is only a zombie and already deleted. - * @ddestroy: List head for the delayed destroy list. - * @swap: List head for swap LRU list. - * @offset: The current GPU offset, which can have different meanings - * depending on the memory type. For SYSTEM type memory, it should be 0. - * @cur_placement: Hint of current placement. * * Base class for TTM buffer object, that deals with data placement and CPU * mappings. GPU mappings are really up to the driver, but for simpler GPUs @@ -109,52 +95,43 @@ struct ttm_tt; * The destroy member, the API visibility of this object makes it possible * to derive driver specific types. */ - struct ttm_buffer_object { struct drm_gem_object base; - /** + /* * Members constant at init. */ - struct ttm_device *bdev; enum ttm_bo_type type; uint32_t page_alignment; void (*destroy) (struct ttm_buffer_object *); - /** + /* * Members not needing protection. */ struct kref kref; - /** + /* * Members protected by the bo::resv::reserved lock. */ - struct ttm_resource *resource; struct ttm_tt *ttm; bool deleted; struct ttm_lru_bulk_move *bulk_move; + unsigned priority; + unsigned pin_count; /** - * Members protected by the bdev::lru_lock. - */ - - struct list_head ddestroy; - - /** - * Members protected by a bo reservation. + * @delayed_delete: Work item used when we can't delete the BO + * immediately */ - - unsigned priority; - unsigned pin_count; + struct work_struct delayed_delete; /** * Special members that are protected by the reserve lock * and the bo::lock when written to. Can be read with * either of these locks held. */ - struct sg_table *sg; }; @@ -170,7 +147,6 @@ struct ttm_buffer_object { * mapping can either be an ioremap, a vmap, a kmap or part of a * premapped region. */ - #define TTM_BO_MAP_IOMEM_MASK 0x80 struct ttm_bo_kmap_obj { void *virtual; @@ -238,95 +214,120 @@ ttm_bo_get_unless_zero(struct ttm_buffer_object *bo) } /** - * ttm_bo_wait - wait for buffer idle. + * ttm_bo_reserve: + * + * @bo: A pointer to a struct ttm_buffer_object. + * @interruptible: Sleep interruptible if waiting. + * @no_wait: Don't sleep while trying to reserve, rather return -EBUSY. + * @ticket: ticket used to acquire the ww_mutex. * - * @bo: The buffer object. - * @interruptible: Use interruptible wait. - * @no_wait: Return immediately if buffer is busy. + * Locks a buffer object for validation. (Or prevents other processes from + * locking it for validation), while taking a number of measures to prevent + * deadlocks. * - * This function must be called with the bo::mutex held, and makes - * sure any previous rendering to the buffer is completed. - * Note: It might be necessary to block validations before the - * wait by reserving the buffer. - * Returns -EBUSY if no_wait is true and the buffer is busy. - * Returns -ERESTARTSYS if interrupted by a signal. + * Returns: + * -EDEADLK: The reservation may cause a deadlock. + * Release all buffer reservations, wait for @bo to become unreserved and + * try again. + * -ERESTARTSYS: A wait for the buffer to become unreserved was interrupted by + * a signal. Release all buffer reservations and return to user-space. + * -EBUSY: The function needed to sleep, but @no_wait was true + * -EALREADY: Bo already reserved using @ticket. This error code will only + * be returned if @use_ticket is set to true. */ -int ttm_bo_wait(struct ttm_buffer_object *bo, bool interruptible, bool no_wait); - -static inline int ttm_bo_wait_ctx(struct ttm_buffer_object *bo, struct ttm_operation_ctx *ctx) +static inline int ttm_bo_reserve(struct ttm_buffer_object *bo, + bool interruptible, bool no_wait, + struct ww_acquire_ctx *ticket) { - return ttm_bo_wait(bo, ctx->interruptible, ctx->no_wait_gpu); -} + int ret = 0; -/** - * ttm_bo_validate - * - * @bo: The buffer object. - * @placement: Proposed placement for the buffer object. - * @ctx: validation parameters. - * - * Changes placement and caching policy of the buffer object - * according proposed placement. - * Returns - * -EINVAL on invalid proposed placement. - * -ENOMEM on out-of-memory condition. - * -EBUSY if no_wait is true and buffer busy. - * -ERESTARTSYS if interrupted by a signal. - */ -int ttm_bo_validate(struct ttm_buffer_object *bo, - struct ttm_placement *placement, - struct ttm_operation_ctx *ctx); + if (no_wait) { + bool success; + + if (WARN_ON(ticket)) + return -EBUSY; + + success = dma_resv_trylock(bo->base.resv); + return success ? 0 : -EBUSY; + } + + if (interruptible) + ret = dma_resv_lock_interruptible(bo->base.resv, ticket); + else + ret = dma_resv_lock(bo->base.resv, ticket); + if (ret == -EINTR) + return -ERESTARTSYS; + return ret; +} /** - * ttm_bo_put - * - * @bo: The buffer object. - * - * Unreference a buffer object. + * ttm_bo_reserve_slowpath: + * @bo: A pointer to a struct ttm_buffer_object. + * @interruptible: Sleep interruptible if waiting. + * @sequence: Set (@bo)->sequence to this value after lock + * + * This is called after ttm_bo_reserve returns -EAGAIN and we backed off + * from all our other reservations. Because there are no other reservations + * held by us, this function cannot deadlock any more. */ -void ttm_bo_put(struct ttm_buffer_object *bo); +static inline int ttm_bo_reserve_slowpath(struct ttm_buffer_object *bo, + bool interruptible, + struct ww_acquire_ctx *ticket) +{ + if (interruptible) { + int ret = dma_resv_lock_slow_interruptible(bo->base.resv, + ticket); + if (ret == -EINTR) + ret = -ERESTARTSYS; + return ret; + } + dma_resv_lock_slow(bo->base.resv, ticket); + return 0; +} void ttm_bo_move_to_lru_tail(struct ttm_buffer_object *bo); -void ttm_bo_set_bulk_move(struct ttm_buffer_object *bo, - struct ttm_lru_bulk_move *bulk); -/** - * ttm_bo_lock_delayed_workqueue - * - * Prevent the delayed workqueue from running. - * Returns - * True if the workqueue was queued at the time - */ -int ttm_bo_lock_delayed_workqueue(struct ttm_device *bdev); +static inline void +ttm_bo_move_to_lru_tail_unlocked(struct ttm_buffer_object *bo) +{ + spin_lock(&bo->bdev->lru_lock); + ttm_bo_move_to_lru_tail(bo); + spin_unlock(&bo->bdev->lru_lock); +} + +static inline void ttm_bo_assign_mem(struct ttm_buffer_object *bo, + struct ttm_resource *new_mem) +{ + WARN_ON(bo->resource); + bo->resource = new_mem; +} /** - * ttm_bo_unlock_delayed_workqueue + * ttm_bo_move_null = assign memory for a buffer object. + * @bo: The bo to assign the memory to + * @new_mem: The memory to be assigned. * - * Allows the delayed workqueue to run. + * Assign the memory from new_mem to the memory of the buffer object bo. */ -void ttm_bo_unlock_delayed_workqueue(struct ttm_device *bdev, int resched); +static inline void ttm_bo_move_null(struct ttm_buffer_object *bo, + struct ttm_resource *new_mem) +{ + ttm_resource_free(bo, &bo->resource); + ttm_bo_assign_mem(bo, new_mem); +} /** - * ttm_bo_eviction_valuable + * ttm_bo_unreserve * - * @bo: The buffer object to evict - * @place: the placement we need to make room for + * @bo: A pointer to a struct ttm_buffer_object. * - * Check if it is valuable to evict the BO to make room for the given placement. + * Unreserve a previous reservation of @bo. */ -bool ttm_bo_eviction_valuable(struct ttm_buffer_object *bo, - const struct ttm_place *place); - -int ttm_bo_init_reserved(struct ttm_device *bdev, struct ttm_buffer_object *bo, - enum ttm_bo_type type, struct ttm_placement *placement, - uint32_t alignment, struct ttm_operation_ctx *ctx, - struct sg_table *sg, struct dma_resv *resv, - void (*destroy) (struct ttm_buffer_object *)); -int ttm_bo_init_validate(struct ttm_device *bdev, struct ttm_buffer_object *bo, - enum ttm_bo_type type, struct ttm_placement *placement, - uint32_t alignment, bool interruptible, - struct sg_table *sg, struct dma_resv *resv, - void (*destroy) (struct ttm_buffer_object *)); +static inline void ttm_bo_unreserve(struct ttm_buffer_object *bo) +{ + ttm_bo_move_to_lru_tail_unlocked(bo); + dma_resv_unlock(bo->base.resv); +} /** * ttm_kmap_obj_virtual @@ -346,126 +347,84 @@ static inline void *ttm_kmap_obj_virtual(struct ttm_bo_kmap_obj *map, return map->virtual; } -/** - * ttm_bo_kmap - * - * @bo: The buffer object. - * @start_page: The first page to map. - * @num_pages: Number of pages to map. - * @map: pointer to a struct ttm_bo_kmap_obj representing the map. - * - * Sets up a kernel virtual mapping, using ioremap, vmap or kmap to the - * data in the buffer object. The ttm_kmap_obj_virtual function can then be - * used to obtain a virtual address to the data. - * - * Returns - * -ENOMEM: Out of memory. - * -EINVAL: Invalid range. - */ +int ttm_bo_wait(struct ttm_buffer_object *bo, bool interruptible, bool no_wait); +int ttm_bo_wait_ctx(struct ttm_buffer_object *bo, + struct ttm_operation_ctx *ctx); +int ttm_bo_validate(struct ttm_buffer_object *bo, + struct ttm_placement *placement, + struct ttm_operation_ctx *ctx); +void ttm_bo_put(struct ttm_buffer_object *bo); +void ttm_bo_set_bulk_move(struct ttm_buffer_object *bo, + struct ttm_lru_bulk_move *bulk); +int ttm_bo_lock_delayed_workqueue(struct ttm_device *bdev); +void ttm_bo_unlock_delayed_workqueue(struct ttm_device *bdev, int resched); +bool ttm_bo_eviction_valuable(struct ttm_buffer_object *bo, + const struct ttm_place *place); +int ttm_bo_init_reserved(struct ttm_device *bdev, struct ttm_buffer_object *bo, + enum ttm_bo_type type, struct ttm_placement *placement, + uint32_t alignment, struct ttm_operation_ctx *ctx, + struct sg_table *sg, struct dma_resv *resv, + void (*destroy)(struct ttm_buffer_object *)); +int ttm_bo_init_validate(struct ttm_device *bdev, struct ttm_buffer_object *bo, + enum ttm_bo_type type, struct ttm_placement *placement, + uint32_t alignment, bool interruptible, + struct sg_table *sg, struct dma_resv *resv, + void (*destroy)(struct ttm_buffer_object *)); int ttm_bo_kmap(struct ttm_buffer_object *bo, unsigned long start_page, unsigned long num_pages, struct ttm_bo_kmap_obj *map); - -/** - * ttm_bo_kunmap - * - * @map: Object describing the map to unmap. - * - * Unmaps a kernel map set up by ttm_bo_kmap. - */ void ttm_bo_kunmap(struct ttm_bo_kmap_obj *map); - -/** - * ttm_bo_vmap - * - * @bo: The buffer object. - * @map: pointer to a struct iosys_map representing the map. - * - * Sets up a kernel virtual mapping, using ioremap or vmap to the - * data in the buffer object. The parameter @map returns the virtual - * address as struct iosys_map. Unmap the buffer with ttm_bo_vunmap(). - * - * Returns - * -ENOMEM: Out of memory. - * -EINVAL: Invalid range. - */ int ttm_bo_vmap(struct ttm_buffer_object *bo, struct iosys_map *map); - -/** - * ttm_bo_vunmap - * - * @bo: The buffer object. - * @map: Object describing the map to unmap. - * - * Unmaps a kernel map set up by ttm_bo_vmap(). - */ void ttm_bo_vunmap(struct ttm_buffer_object *bo, struct iosys_map *map); - -/** - * ttm_bo_mmap_obj - mmap memory backed by a ttm buffer object. - * - * @vma: vma as input from the fbdev mmap method. - * @bo: The bo backing the address space. - * - * Maps a buffer object. - */ int ttm_bo_mmap_obj(struct vm_area_struct *vma, struct ttm_buffer_object *bo); - -/** - * ttm_bo_io - * - * @bdev: Pointer to the struct ttm_device. - * @filp: Pointer to the struct file attempting to read / write. - * @wbuf: User-space pointer to address of buffer to write. NULL on read. - * @rbuf: User-space pointer to address of buffer to read into. - * Null on write. - * @count: Number of bytes to read / write. - * @f_pos: Pointer to current file position. - * @write: 1 for read, 0 for write. - * - * This function implements read / write into ttm buffer objects, and is - * intended to - * be called from the fops::read and fops::write method. - * Returns: - * See man (2) write, man(2) read. In particular, - * the function may return -ERESTARTSYS if - * interrupted by a signal. - */ -ssize_t ttm_bo_io(struct ttm_device *bdev, struct file *filp, - const char __user *wbuf, char __user *rbuf, - size_t count, loff_t *f_pos, bool write); - int ttm_bo_swapout(struct ttm_buffer_object *bo, struct ttm_operation_ctx *ctx, gfp_t gfp_flags); - void ttm_bo_pin(struct ttm_buffer_object *bo); void ttm_bo_unpin(struct ttm_buffer_object *bo); - int ttm_mem_evict_first(struct ttm_device *bdev, struct ttm_resource_manager *man, const struct ttm_place *place, struct ttm_operation_ctx *ctx, struct ww_acquire_ctx *ticket); - -/* Default number of pre-faulted pages in the TTM fault handler */ -#define TTM_BO_VM_NUM_PREFAULT 16 - vm_fault_t ttm_bo_vm_reserve(struct ttm_buffer_object *bo, struct vm_fault *vmf); - vm_fault_t ttm_bo_vm_fault_reserved(struct vm_fault *vmf, pgprot_t prot, pgoff_t num_prefault); - vm_fault_t ttm_bo_vm_fault(struct vm_fault *vmf); - void ttm_bo_vm_open(struct vm_area_struct *vma); - void ttm_bo_vm_close(struct vm_area_struct *vma); - int ttm_bo_vm_access(struct vm_area_struct *vma, unsigned long addr, void *buf, int len, int write); -bool ttm_bo_delayed_delete(struct ttm_device *bdev, bool remove_all); - vm_fault_t ttm_bo_vm_dummy_page(struct vm_fault *vmf, pgprot_t prot); +int ttm_bo_mem_space(struct ttm_buffer_object *bo, + struct ttm_placement *placement, + struct ttm_resource **mem, + struct ttm_operation_ctx *ctx); + +void ttm_bo_unmap_virtual(struct ttm_buffer_object *bo); +/* + * ttm_bo_util.c + */ +int ttm_mem_io_reserve(struct ttm_device *bdev, + struct ttm_resource *mem); +void ttm_mem_io_free(struct ttm_device *bdev, + struct ttm_resource *mem); +void ttm_move_memcpy(bool clear, u32 num_pages, + struct ttm_kmap_iter *dst_iter, + struct ttm_kmap_iter *src_iter); +int ttm_bo_move_memcpy(struct ttm_buffer_object *bo, + struct ttm_operation_ctx *ctx, + struct ttm_resource *new_mem); +int ttm_bo_move_accel_cleanup(struct ttm_buffer_object *bo, + struct dma_fence *fence, bool evict, + bool pipeline, + struct ttm_resource *new_mem); +void ttm_bo_move_sync_cleanup(struct ttm_buffer_object *bo, + struct ttm_resource *new_mem); +int ttm_bo_pipeline_gutting(struct ttm_buffer_object *bo); +pgprot_t ttm_io_prot(struct ttm_buffer_object *bo, struct ttm_resource *res, + pgprot_t tmp); +void ttm_bo_tt_destroy(struct ttm_buffer_object *bo); + #endif diff --git a/include/drm/ttm/ttm_bo_driver.h b/include/drm/ttm/ttm_bo_driver.h deleted file mode 100644 index 1afa891f488a..000000000000 --- a/include/drm/ttm/ttm_bo_driver.h +++ /dev/null @@ -1,303 +0,0 @@ -/************************************************************************** - * - * Copyright (c) 2006-2009 Vmware, Inc., Palo Alto, CA., USA - * All Rights Reserved. - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sub license, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice (including the - * next paragraph) shall be included in all copies or substantial portions - * of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL - * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, - * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR - * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE - * USE OR OTHER DEALINGS IN THE SOFTWARE. - * - **************************************************************************/ -/* - * Authors: Thomas Hellstrom <thellstrom-at-vmware-dot-com> - */ -#ifndef _TTM_BO_DRIVER_H_ -#define _TTM_BO_DRIVER_H_ - -#include <drm/drm_mm.h> -#include <drm/drm_vma_manager.h> -#include <linux/workqueue.h> -#include <linux/fs.h> -#include <linux/spinlock.h> -#include <linux/dma-resv.h> - -#include <drm/ttm/ttm_device.h> - -#include "ttm_bo_api.h" -#include "ttm_kmap_iter.h" -#include "ttm_placement.h" -#include "ttm_tt.h" -#include "ttm_pool.h" - -/* - * ttm_bo.c - */ - -/** - * ttm_bo_mem_space - * - * @bo: Pointer to a struct ttm_buffer_object. the data of which - * we want to allocate space for. - * @proposed_placement: Proposed new placement for the buffer object. - * @mem: A struct ttm_resource. - * @interruptible: Sleep interruptible when sliping. - * @no_wait_gpu: Return immediately if the GPU is busy. - * - * Allocate memory space for the buffer object pointed to by @bo, using - * the placement flags in @mem, potentially evicting other idle buffer objects. - * This function may sleep while waiting for space to become available. - * Returns: - * -EBUSY: No space available (only if no_wait == 1). - * -ENOMEM: Could not allocate memory for the buffer object, either due to - * fragmentation or concurrent allocators. - * -ERESTARTSYS: An interruptible sleep was interrupted by a signal. - */ -int ttm_bo_mem_space(struct ttm_buffer_object *bo, - struct ttm_placement *placement, - struct ttm_resource **mem, - struct ttm_operation_ctx *ctx); - -/** - * ttm_bo_unmap_virtual - * - * @bo: tear down the virtual mappings for this BO - */ -void ttm_bo_unmap_virtual(struct ttm_buffer_object *bo); - -/** - * ttm_bo_reserve: - * - * @bo: A pointer to a struct ttm_buffer_object. - * @interruptible: Sleep interruptible if waiting. - * @no_wait: Don't sleep while trying to reserve, rather return -EBUSY. - * @ticket: ticket used to acquire the ww_mutex. - * - * Locks a buffer object for validation. (Or prevents other processes from - * locking it for validation), while taking a number of measures to prevent - * deadlocks. - * - * Returns: - * -EDEADLK: The reservation may cause a deadlock. - * Release all buffer reservations, wait for @bo to become unreserved and - * try again. - * -ERESTARTSYS: A wait for the buffer to become unreserved was interrupted by - * a signal. Release all buffer reservations and return to user-space. - * -EBUSY: The function needed to sleep, but @no_wait was true - * -EALREADY: Bo already reserved using @ticket. This error code will only - * be returned if @use_ticket is set to true. - */ -static inline int ttm_bo_reserve(struct ttm_buffer_object *bo, - bool interruptible, bool no_wait, - struct ww_acquire_ctx *ticket) -{ - int ret; - - if (no_wait) { - bool success; - if (WARN_ON(ticket)) - return -EBUSY; - - success = dma_resv_trylock(bo->base.resv); - return success ? 0 : -EBUSY; - } - - if (interruptible) - ret = dma_resv_lock_interruptible(bo->base.resv, ticket); - else - ret = dma_resv_lock(bo->base.resv, ticket); - if (ret == -EINTR) - return -ERESTARTSYS; - return ret; -} - -/** - * ttm_bo_reserve_slowpath: - * @bo: A pointer to a struct ttm_buffer_object. - * @interruptible: Sleep interruptible if waiting. - * @sequence: Set (@bo)->sequence to this value after lock - * - * This is called after ttm_bo_reserve returns -EAGAIN and we backed off - * from all our other reservations. Because there are no other reservations - * held by us, this function cannot deadlock any more. - */ -static inline int ttm_bo_reserve_slowpath(struct ttm_buffer_object *bo, - bool interruptible, - struct ww_acquire_ctx *ticket) -{ - if (interruptible) { - int ret = dma_resv_lock_slow_interruptible(bo->base.resv, - ticket); - if (ret == -EINTR) - ret = -ERESTARTSYS; - return ret; - } - dma_resv_lock_slow(bo->base.resv, ticket); - return 0; -} - -static inline void -ttm_bo_move_to_lru_tail_unlocked(struct ttm_buffer_object *bo) -{ - spin_lock(&bo->bdev->lru_lock); - ttm_bo_move_to_lru_tail(bo); - spin_unlock(&bo->bdev->lru_lock); -} - -static inline void ttm_bo_assign_mem(struct ttm_buffer_object *bo, - struct ttm_resource *new_mem) -{ - WARN_ON(bo->resource); - bo->resource = new_mem; -} - -/** - * ttm_bo_move_null = assign memory for a buffer object. - * @bo: The bo to assign the memory to - * @new_mem: The memory to be assigned. - * - * Assign the memory from new_mem to the memory of the buffer object bo. - */ -static inline void ttm_bo_move_null(struct ttm_buffer_object *bo, - struct ttm_resource *new_mem) -{ - ttm_resource_free(bo, &bo->resource); - ttm_bo_assign_mem(bo, new_mem); -} - -/** - * ttm_bo_unreserve - * - * @bo: A pointer to a struct ttm_buffer_object. - * - * Unreserve a previous reservation of @bo. - */ -static inline void ttm_bo_unreserve(struct ttm_buffer_object *bo) -{ - ttm_bo_move_to_lru_tail_unlocked(bo); - dma_resv_unlock(bo->base.resv); -} - -/* - * ttm_bo_util.c - */ -int ttm_mem_io_reserve(struct ttm_device *bdev, - struct ttm_resource *mem); -void ttm_mem_io_free(struct ttm_device *bdev, - struct ttm_resource *mem); - -/** - * ttm_bo_move_memcpy - * - * @bo: A pointer to a struct ttm_buffer_object. - * @interruptible: Sleep interruptible if waiting. - * @no_wait_gpu: Return immediately if the GPU is busy. - * @new_mem: struct ttm_resource indicating where to move. - * - * Fallback move function for a mappable buffer object in mappable memory. - * The function will, if successful, - * free any old aperture space, and set (@new_mem)->mm_node to NULL, - * and update the (@bo)->mem placement flags. If unsuccessful, the old - * data remains untouched, and it's up to the caller to free the - * memory space indicated by @new_mem. - * Returns: - * !0: Failure. - */ - -int ttm_bo_move_memcpy(struct ttm_buffer_object *bo, - struct ttm_operation_ctx *ctx, - struct ttm_resource *new_mem); - -/** - * ttm_bo_move_accel_cleanup. - * - * @bo: A pointer to a struct ttm_buffer_object. - * @fence: A fence object that signals when moving is complete. - * @evict: This is an evict move. Don't return until the buffer is idle. - * @pipeline: evictions are to be pipelined. - * @new_mem: struct ttm_resource indicating where to move. - * - * Accelerated move function to be called when an accelerated move - * has been scheduled. The function will create a new temporary buffer object - * representing the old placement, and put the sync object on both buffer - * objects. After that the newly created buffer object is unref'd to be - * destroyed when the move is complete. This will help pipeline - * buffer moves. - */ -int ttm_bo_move_accel_cleanup(struct ttm_buffer_object *bo, - struct dma_fence *fence, bool evict, - bool pipeline, - struct ttm_resource *new_mem); - -/** - * ttm_bo_move_sync_cleanup. - * - * @bo: A pointer to a struct ttm_buffer_object. - * @new_mem: struct ttm_resource indicating where to move. - * - * Special case of ttm_bo_move_accel_cleanup where the bo is guaranteed - * by the caller to be idle. Typically used after memcpy buffer moves. - */ -void ttm_bo_move_sync_cleanup(struct ttm_buffer_object *bo, - struct ttm_resource *new_mem); - -/** - * ttm_bo_pipeline_gutting. - * - * @bo: A pointer to a struct ttm_buffer_object. - * - * Pipelined gutting a BO of its backing store. - */ -int ttm_bo_pipeline_gutting(struct ttm_buffer_object *bo); - -/** - * ttm_io_prot - * - * bo: ttm buffer object - * res: ttm resource object - * @tmp: Page protection flag for a normal, cached mapping. - * - * Utility function that returns the pgprot_t that should be used for - * setting up a PTE with the caching model indicated by @c_state. - */ -pgprot_t ttm_io_prot(struct ttm_buffer_object *bo, struct ttm_resource *res, - pgprot_t tmp); - -/** - * ttm_bo_tt_bind - * - * Bind the object tt to a memory resource. - */ -int ttm_bo_tt_bind(struct ttm_buffer_object *bo, struct ttm_resource *mem); - -/** - * ttm_bo_tt_destroy. - */ -void ttm_bo_tt_destroy(struct ttm_buffer_object *bo); - -void ttm_move_memcpy(bool clear, - u32 num_pages, - struct ttm_kmap_iter *dst_iter, - struct ttm_kmap_iter *src_iter); - -struct ttm_kmap_iter * -ttm_kmap_iter_iomap_init(struct ttm_kmap_iter_iomap *iter_io, - struct io_mapping *iomap, - struct sg_table *st, - resource_size_t start); -#endif diff --git a/include/drm/ttm/ttm_device.h b/include/drm/ttm/ttm_device.h index 95b3c04b1ab9..4f3e81eac6f3 100644 --- a/include/drm/ttm/ttm_device.h +++ b/include/drm/ttm/ttm_device.h @@ -252,11 +252,6 @@ struct ttm_device { spinlock_t lru_lock; /** - * @ddestroy: Destroyed but not yet cleaned up buffer objects. - */ - struct list_head ddestroy; - - /** * @pinned: Buffer objects which are pinned and so not on any LRU list. */ struct list_head pinned; @@ -270,7 +265,7 @@ struct ttm_device { /** * @wq: Work queue structure for the delayed delete workqueue. */ - struct delayed_work wq; + struct workqueue_struct *wq; }; int ttm_global_swapout(struct ttm_operation_ctx *ctx, gfp_t gfp_flags); diff --git a/include/drm/ttm/ttm_execbuf_util.h b/include/drm/ttm/ttm_execbuf_util.h index a99d7fdf2964..03aca29d3ce4 100644 --- a/include/drm/ttm/ttm_execbuf_util.h +++ b/include/drm/ttm/ttm_execbuf_util.h @@ -33,7 +33,9 @@ #include <linux/list.h> -#include "ttm_bo_api.h" +struct ww_acquire_ctx; +struct dma_fence; +struct ttm_buffer_object; /** * struct ttm_validate_buffer diff --git a/include/linux/dma-buf.h b/include/linux/dma-buf.h index 6fa8d4e29719..3f31baa3293f 100644 --- a/include/linux/dma-buf.h +++ b/include/linux/dma-buf.h @@ -356,7 +356,7 @@ struct dma_buf { */ const char *name; - /** @name_lock: Spinlock to protect name acces for read access. */ + /** @name_lock: Spinlock to protect name access for read access. */ spinlock_t name_lock; /** @@ -393,7 +393,7 @@ struct dma_buf { * anything the userspace API considers write access. * * - Drivers may just always add a write fence, since that only - * causes unecessarily synchronization, but no correctness issues. + * causes unnecessary synchronization, but no correctness issues. * * - Some drivers only expose a synchronous userspace API with no * pipelining across drivers. These do not set any fences for their diff --git a/include/uapi/linux/media-bus-format.h b/include/uapi/linux/media-bus-format.h index ca9a24c874da..a03c543cb072 100644 --- a/include/uapi/linux/media-bus-format.h +++ b/include/uapi/linux/media-bus-format.h @@ -34,7 +34,7 @@ #define MEDIA_BUS_FMT_FIXED 0x0001 -/* RGB - next is 0x1022 */ +/* RGB - next is 0x1025 */ #define MEDIA_BUS_FMT_RGB444_1X12 0x1016 #define MEDIA_BUS_FMT_RGB444_2X8_PADHI_BE 0x1001 #define MEDIA_BUS_FMT_RGB444_2X8_PADHI_LE 0x1002 @@ -46,8 +46,11 @@ #define MEDIA_BUS_FMT_RGB565_2X8_BE 0x1007 #define MEDIA_BUS_FMT_RGB565_2X8_LE 0x1008 #define MEDIA_BUS_FMT_RGB666_1X18 0x1009 +#define MEDIA_BUS_FMT_BGR666_1X18 0x1023 #define MEDIA_BUS_FMT_RBG888_1X24 0x100e #define MEDIA_BUS_FMT_RGB666_1X24_CPADHI 0x1015 +#define MEDIA_BUS_FMT_BGR666_1X24_CPADHI 0x1024 +#define MEDIA_BUS_FMT_RGB565_1X24_CPADHI 0x1022 #define MEDIA_BUS_FMT_RGB666_1X7X3_SPWG 0x1010 #define MEDIA_BUS_FMT_BGR888_1X24 0x1013 #define MEDIA_BUS_FMT_BGR888_3X8 0x101b |