diff options
| author | Mark Brown <broonie@kernel.org> | 2026-07-03 15:44:41 +0100 |
|---|---|---|
| committer | Mark Brown <broonie@kernel.org> | 2026-07-03 15:44:41 +0100 |
| commit | 5a42c9f18870162ae14f0d8e193671017421a982 (patch) | |
| tree | 5c84a4cc4c90157c7e9c833c037d4e825f21f1bb | |
| parent | cd4f513931c31ca3ed852f370bb573fcbd0721c5 (diff) | |
| parent | 903f8773ee96c5dc5fb9aec65227f39fd3e7a1dc (diff) | |
| download | linux-next-5a42c9f18870162ae14f0d8e193671017421a982.tar.gz linux-next-5a42c9f18870162ae14f0d8e193671017421a982.zip | |
Merge branch 'for-linux-next' of https://gitlab.freedesktop.org/drm/misc/kernel.git
261 files changed, 9840 insertions, 3189 deletions
@@ -560,6 +560,7 @@ Martyna Szapar-Mudlaw <martyna.szapar-mudlaw@linux.intel.com> <martyna.szapar-mu Mathieu Othacehe <othacehe@gnu.org> <m.othacehe@gmail.com> Mat Martineau <martineau@kernel.org> <mathew.j.martineau@linux.intel.com> Mat Martineau <martineau@kernel.org> <mathewm@codeaurora.org> +Matt Coster <opensource@mtcoster.net> <matt.coster@imgtec.com> Matthew Wilcox <willy@infradead.org> <matthew.r.wilcox@intel.com> Matthew Wilcox <willy@infradead.org> <matthew@wil.cx> Matthew Wilcox <willy@infradead.org> <mawilcox@linuxonhyperv.com> diff --git a/Documentation/admin-guide/cgroup-v2.rst b/Documentation/admin-guide/cgroup-v2.rst index 993446ab66d0..f0e6bce3f162 100644 --- a/Documentation/admin-guide/cgroup-v2.rst +++ b/Documentation/admin-guide/cgroup-v2.rst @@ -2861,6 +2861,12 @@ DMEM Interface Files The semantics are the same as for the memory cgroup controller, and are calculated in the same way. + dmem.peak + A read-only nested-keyed file that exists on non-root cgroups. + + The max device memory usage recorded for the cgroup and its + descendants since the creation of the cgroup for each region. + dmem.capacity A read-only file that describes maximum region capacity. It only exists on the root cgroup. Not all memory can be diff --git a/Documentation/devicetree/bindings/display/panel/anbernic,td4310.yaml b/Documentation/devicetree/bindings/display/panel/anbernic,td4310.yaml new file mode 100644 index 000000000000..f820dc2f732b --- /dev/null +++ b/Documentation/devicetree/bindings/display/panel/anbernic,td4310.yaml @@ -0,0 +1,66 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/display/panel/anbernic,td4310.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Anbernic TD4310 Based Panels + +maintainers: + - Chris Morgan <macromorgan@hotmail.com> + +description: + Anbernic TD4310 Based Panels, such as the RG-Vita-Pro panel + (a 1080x1920 5.5 inch panel). + +allOf: + - $ref: panel-common.yaml# + +properties: + compatible: + items: + - enum: + - anbernic,panel-vita-pro + - const: anbernic,td4310 + + reg: + maxItems: 1 + + vdd-supply: + description: Panel power supply + +required: + - compatible + - port + - reg + - reset-gpios + - vdd-supply + +unevaluatedProperties: false + +examples: + - | + #include <dt-bindings/gpio/gpio.h> + + dsi { + #address-cells = <1>; + #size-cells = <0>; + + panel@0 { + compatible = "anbernic,panel-vita-pro", "anbernic,td4310"; + reg = <0>; + backlight = <&backlight>; + enable-gpios = <&gpio0 9 GPIO_ACTIVE_HIGH>; + reset-gpios = <&gpio0 13 GPIO_ACTIVE_LOW>; + rotation = <270>; + vdd-supply = <&vdd_lcd>; + + port { + endpoint { + remote-endpoint = <&dsi_out>; + }; + }; + }; + }; + +... diff --git a/Documentation/devicetree/bindings/display/panel/chipone,icna3512.yaml b/Documentation/devicetree/bindings/display/panel/chipone,icna3512.yaml new file mode 100644 index 000000000000..90e69f30cd91 --- /dev/null +++ b/Documentation/devicetree/bindings/display/panel/chipone,icna3512.yaml @@ -0,0 +1,79 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/display/panel/chipone,icna3512.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Chipone ICNA3512 and ICNA3520 display drivers + +maintainers: + - Neil Armstrong <neil.armstrong@linaro.org> + +description: + The Chipone ICNA3512 and ICNA3520 are DDICs connected + using a MIPI-DSI video interface. + +allOf: + - $ref: panel-common.yaml# + +properties: + compatible: + oneOf: + - items: + - enum: + - ayaneo,pocketds-panel-top + - ayntec,odin2portal-panel + - const: chipone,icna3512 + + - items: + - enum: + - ayntec,odin3-panel + - ayntec,thor-panel-top + - const: chipone,icna3520 + + reg: + maxItems: 1 + description: DSI virtual channel + + vdd-supply: true + vddio-supply: true + vci-supply: true + disp-supply: true + blvdd-supply: true + + port: true + reset-gpios: true + rotation: true + +required: + - compatible + - reg + - vdd-supply + - vddio-supply + - vci-supply + - disp-supply + - blvdd-supply + - reset-gpios + +additionalProperties: false + +examples: + - | + #include <dt-bindings/gpio/gpio.h> + + dsi { + #address-cells = <1>; + #size-cells = <0>; + panel@0 { + compatible = "ayntec,odin2portal-panel", "chipone,icna3512"; + reg = <0>; + vdd-supply = <&vreg_l11b_1p2>; + vddio-supply = <&vreg_l12b_1p8>; + vci-supply = <&vreg_l13b_3p0>; + disp-supply = <&vdd_disp_2v8>; + blvdd-supply = <&vdd_bl_5v0>; + reset-gpios = <&tlmm 133 GPIO_ACTIVE_LOW>; + }; + }; + +... diff --git a/Documentation/devicetree/bindings/display/panel/himax,hx83121a.yaml b/Documentation/devicetree/bindings/display/panel/himax,hx83121a.yaml index e067a2f6d0b2..aeca3c9a599c 100644 --- a/Documentation/devicetree/bindings/display/panel/himax,hx83121a.yaml +++ b/Documentation/devicetree/bindings/display/panel/himax,hx83121a.yaml @@ -40,6 +40,9 @@ properties: vddi-supply: description: power supply for IC + bl-supply: + description: power supply for backlight, in case it's managed via DSC + backlight: true ports: true diff --git a/Documentation/devicetree/bindings/display/panel/ilitek,ili7807s.yaml b/Documentation/devicetree/bindings/display/panel/ilitek,ili7807s.yaml new file mode 100644 index 000000000000..ba8c5bbf8ffc --- /dev/null +++ b/Documentation/devicetree/bindings/display/panel/ilitek,ili7807s.yaml @@ -0,0 +1,71 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/display/panel/ilitek,ili7807s.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Ilitek ILI7807S-based DSI panels + +maintainers: + - Arpit Saini <arpit.saini@oss.qualcomm.com> + +allOf: + - $ref: panel-common.yaml# + +properties: + compatible: + items: + - enum: + - dlc,dlc0697 + - const: ilitek,ili7807s + + reg: + maxItems: 1 + description: DSI virtual channel + + vddi-supply: + description: I/O voltage supply (1.8V) + + avdd-supply: + description: Positive LCD bias supply (AVDD), typically +5.5V + (range 4.5V to 6.3V) + + avee-supply: + description: Negative LCD bias supply (AVEE), typically -5.5V + (range -6.3V to -4.5V) + +required: + - compatible + - reg + - reset-gpios + - vddi-supply + - avdd-supply + - avee-supply + - port + +unevaluatedProperties: false + +examples: + - | + #include <dt-bindings/gpio/gpio.h> + + dsi { + #address-cells = <1>; + #size-cells = <0>; + + panel@0 { + compatible = "dlc,dlc0697", "ilitek,ili7807s"; + reg = <0>; + + reset-gpios = <&tlmm 3 GPIO_ACTIVE_LOW>; + vddi-supply = <&pm4125_l15>; + avdd-supply = <&avdd>; + avee-supply = <&avee>; + + port { + panel_in: endpoint { + remote-endpoint = <&dsi0_out>; + }; + }; + }; + }; diff --git a/Documentation/devicetree/bindings/display/panel/ilitek,ili9488.yaml b/Documentation/devicetree/bindings/display/panel/ilitek,ili9488.yaml new file mode 100644 index 000000000000..ea7449273022 --- /dev/null +++ b/Documentation/devicetree/bindings/display/panel/ilitek,ili9488.yaml @@ -0,0 +1,63 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/display/panel/ilitek,ili9488.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Ilitek ILI9488 based MIPI-DSI panels + +maintainers: + - Igor Reznichenko <igor@reznichenko.net> + +allOf: + - $ref: panel-common.yaml# + +properties: + compatible: + items: + - enum: + - focuslcds,e35gh-i-mw800cb + - const: ilitek,ili9488 + + reg: + maxItems: 1 + + vci-supply: true + iovcc-supply: true + +required: + - compatible + - reg + - vci-supply + - iovcc-supply + - reset-gpios + - backlight + - port + +unevaluatedProperties: false + +examples: + - | + #include <dt-bindings/gpio/gpio.h> + + dsi { + #address-cells = <1>; + #size-cells = <0>; + + panel@0 { + compatible = "focuslcds,e35gh-i-mw800cb", "ilitek,ili9488"; + reg = <0>; + vci-supply = <®_vci_panel>; + iovcc-supply = <®_iovcc_panel>; + reset-gpios = <&gpio3 6 GPIO_ACTIVE_LOW>; + backlight = <&pwm_bl>; + + port { + panel_in: endpoint { + remote-endpoint = <&dsi_out>; + }; + }; + }; + }; + +... diff --git a/Documentation/devicetree/bindings/display/panel/renesas,r63419.yaml b/Documentation/devicetree/bindings/display/panel/renesas,r63419.yaml new file mode 100644 index 000000000000..adfdd2c300a3 --- /dev/null +++ b/Documentation/devicetree/bindings/display/panel/renesas,r63419.yaml @@ -0,0 +1,98 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/display/panel/renesas,r63419.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Renesas R63419 based dual-DSI video mode Display Panel + +maintainers: + - Neil Armstrong <neil.armstrong@linaro.org> + +description: + The Renesas R63419 is a generic DDIC used to control dual-DSI LCD panels. + +allOf: + - $ref: panel-common-dual.yaml# + +properties: + compatible: + items: + - enum: + - ayaneo,wt0600-2k + - ayaneo,wt0630-2k + - const: renesas,r63419 + + reg: + maxItems: 1 + + vdd-supply: true + vddio-supply: true + vsp-supply: true + vsn-supply: true + vci-supply: true + + backlight: true + reset-gpios: true + rotation: true + ports: true + +required: + - compatible + - reg + - vdd-supply + - vddio-supply + - vsp-supply + - vsn-supply + - vci-supply + - backlight + - reset-gpios + - ports + +additionalProperties: false + +examples: + - | + #include <dt-bindings/gpio/gpio.h> + + dsi { + #address-cells = <1>; + #size-cells = <0>; + + panel@0 { + compatible = "ayaneo,wt0600-2k", "renesas,r63419"; + reg = <0>; + + reset-gpios = <&gpio 176 GPIO_ACTIVE_LOW>; + + vdd-supply = <&vdd_3v0_lcd>; + vddio-supply = <&vdd_1v8_io>; + vsn-supply = <&vdd_5v0_neg>; + vsp-supply = <&vdd_5v0_pos>; + vci-supply = <&vdd_3v0_vci>; + + backlight = <&backlight>; + + rotation = <90>; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + panel_in0: endpoint { + remote-endpoint = <&dsi0_out>; + }; + }; + + port@1 { + reg = <1>; + panel_in1: endpoint { + remote-endpoint = <&dsi1_out>; + }; + }; + }; + }; + }; +... diff --git a/Documentation/devicetree/bindings/display/panel/samsung,atna33xc20.yaml b/Documentation/devicetree/bindings/display/panel/samsung,atna33xc20.yaml index 1bbe0da3997c..27536eeaed85 100644 --- a/Documentation/devicetree/bindings/display/panel/samsung,atna33xc20.yaml +++ b/Documentation/devicetree/bindings/display/panel/samsung,atna33xc20.yaml @@ -25,6 +25,8 @@ properties: - samsung,atna40ct06 # Samsung 14" WQXGA+ (2880x1800 pixels) eDP AMOLED panel - samsung,atna40cu11 + # Samsung 14" WQXGA+ (2880x1800 pixels) eDP AMOLED panel + - samsung,atna40hq08 # Samsung 14" WQXGA+ (2880×1800 pixels) eDP AMOLED panel - samsung,atna40yk20 # Samsung 14.5" WQXGA+ (2880x1800 pixels) eDP AMOLED panel diff --git a/Documentation/devicetree/bindings/display/rockchip/rockchip,analogix-dp.yaml b/Documentation/devicetree/bindings/display/rockchip/rockchip,analogix-dp.yaml index d99b23b88cc5..6548f157fd96 100644 --- a/Documentation/devicetree/bindings/display/rockchip/rockchip,analogix-dp.yaml +++ b/Documentation/devicetree/bindings/display/rockchip/rockchip,analogix-dp.yaml @@ -15,18 +15,24 @@ properties: enum: - rockchip,rk3288-dp - rockchip,rk3399-edp + - rockchip,rk3576-edp - rockchip,rk3588-edp clocks: minItems: 2 - maxItems: 3 + items: + - description: Reference clock + - description: APB bus clock + - description: GRF or AHB bus clock clock-names: minItems: 2 items: - const: dp - const: pclk - - const: grf + - enum: + - grf + - hclk power-domains: maxItems: 1 @@ -65,9 +71,46 @@ allOf: compatible: contains: enum: + - rockchip,rk3288-dp + then: + properties: + clocks: + maxItems: 2 + clock-names: + maxItems: 2 + + - if: + properties: + compatible: + contains: + enum: + - rockchip,rk3399-edp + then: + properties: + clocks: + minItems: 3 + clock-names: + items: + - const: dp + - const: pclk + - const: grf + + - if: + properties: + compatible: + contains: + enum: + - rockchip,rk3576-edp - rockchip,rk3588-edp then: properties: + clocks: + minItems: 3 + clock-names: + items: + - const: dp + - const: pclk + - const: hclk resets: minItems: 2 reset-names: diff --git a/Documentation/devicetree/bindings/gpu/img,powervr-rogue.yaml b/Documentation/devicetree/bindings/gpu/img,powervr-rogue.yaml index a1f54dbae3f3..a6bccb600a57 100644 --- a/Documentation/devicetree/bindings/gpu/img,powervr-rogue.yaml +++ b/Documentation/devicetree/bindings/gpu/img,powervr-rogue.yaml @@ -8,7 +8,8 @@ $schema: http://devicetree.org/meta-schemas/core.yaml# title: Imagination Technologies PowerVR and IMG Rogue GPUs maintainers: - - Frank Binns <frank.binns@imgtec.com> + - Alessio Belle <alessio.belle@imgtec.com> + - Luigi Santivetti <luigi.santivetti@imgtec.com> properties: compatible: diff --git a/Documentation/devicetree/bindings/gpu/img,powervr-sgx.yaml b/Documentation/devicetree/bindings/gpu/img,powervr-sgx.yaml index f5898b04381c..58b799b8e96e 100644 --- a/Documentation/devicetree/bindings/gpu/img,powervr-sgx.yaml +++ b/Documentation/devicetree/bindings/gpu/img,powervr-sgx.yaml @@ -9,7 +9,8 @@ $schema: http://devicetree.org/meta-schemas/core.yaml# title: Imagination Technologies PowerVR SGX GPUs maintainers: - - Frank Binns <frank.binns@imgtec.com> + - Alessio Belle <alessio.belle@imgtec.com> + - Luigi Santivetti <luigi.santivetti@imgtec.com> properties: compatible: diff --git a/Documentation/devicetree/bindings/vendor-prefixes.yaml b/Documentation/devicetree/bindings/vendor-prefixes.yaml index a8e751f387d1..707182b2c00f 100644 --- a/Documentation/devicetree/bindings/vendor-prefixes.yaml +++ b/Documentation/devicetree/bindings/vendor-prefixes.yaml @@ -225,6 +225,8 @@ patternProperties: description: Axis Communications AB "^ayaneo,.*": description: Anyun Intelligent Technology (Hong Kong) Co., Ltd + "^ayntec,.*": + description: AYN Technologies Co., Ltd. "^azoteq,.*": description: Azoteq (Pty) Ltd "^azw,.*": @@ -606,6 +608,8 @@ patternProperties: description: Flipkart Inc. "^focaltech,.*": description: FocalTech Systems Co.,Ltd + "^focuslcds,.*": + description: Focus Display Solutions, Inc. "^forlinx,.*": description: Baoding Forlinx Embedded Technology Co., Ltd. "^foursemi,.*": @@ -1413,6 +1417,8 @@ patternProperties: description: Embest RIoT "^riscv,.*": description: RISC-V Foundation + "^riverdi,.*": + description: Riverdi Sp. z o.o "^rockchip,.*": description: Rockchip Electronics Co., Ltd. "^rocktech,.*": diff --git a/Documentation/gpu/automated_testing.rst b/Documentation/gpu/automated_testing.rst index 62aa3ede02a5..8a7328aef10e 100644 --- a/Documentation/gpu/automated_testing.rst +++ b/Documentation/gpu/automated_testing.rst @@ -99,7 +99,8 @@ How to enable automated testing on your tree ============================================ 1. Create a Linux tree in https://gitlab.freedesktop.org/ if you don't have one -yet +yet, by forking https://gitlab.freedesktop.org/drm/kernel (this allows GitLab +to internally track that these are the same git objects). 2. In your kernel repo's configuration (eg. https://gitlab.freedesktop.org/janedoe/linux/-/settings/ci_cd), change the diff --git a/Documentation/gpu/drm-kms-helpers.rst b/Documentation/gpu/drm-kms-helpers.rst index 80453dda33b8..94cfc26acecc 100644 --- a/Documentation/gpu/drm-kms-helpers.rst +++ b/Documentation/gpu/drm-kms-helpers.rst @@ -171,6 +171,12 @@ Bridge Operations .. kernel-doc:: drivers/gpu/drm/drm_bridge.c :doc: bridge operations +Bridge Chain Format Selection +----------------------------- + +.. kernel-doc:: drivers/gpu/drm/drm_bridge.c + :doc: bridge chain format selection + Bridge Connector Helper ----------------------- diff --git a/Documentation/gpu/drm-kms.rst b/Documentation/gpu/drm-kms.rst index fa69a5450f96..0dd440a14946 100644 --- a/Documentation/gpu/drm-kms.rst +++ b/Documentation/gpu/drm-kms.rst @@ -287,6 +287,12 @@ structure, ordering of committing state changes to hardware is sequenced using Read on in this chapter, and also in :ref:`drm_atomic_helper` for more detailed coverage of specific topics. +Atomic State Lifetime +--------------------- + +.. kernel-doc:: drivers/gpu/drm/drm_atomic.c + :doc: state lifetime + Handling Driver Private State ----------------------------- @@ -604,6 +610,12 @@ Color Management Properties .. kernel-doc:: drivers/gpu/drm/drm_color_mgmt.c :doc: overview +Color Format Property +--------------------- + +.. kernel-doc:: drivers/gpu/drm/drm_connector.c + :doc: Color format + Tile Group Property ------------------- diff --git a/Documentation/gpu/todo.rst b/Documentation/gpu/todo.rst index cdddf8db35f5..14cf37590fc7 100644 --- a/Documentation/gpu/todo.rst +++ b/Documentation/gpu/todo.rst @@ -55,7 +55,7 @@ There are still drivers that use drm_simple_display_pipe. The task here is to convert them to use regular atomic helpers. Search for a driver that calls drm_simple_display_pipe_init() and inline all helpers from drm_simple_kms_helper.c into the driver, such that no simple-KMS interfaces are required. Please also -rename all inlined fucntions according to driver conventions. +rename all inlined functions according to driver conventions. Contact: Thomas Zimmermann, respective driver maintainer @@ -896,10 +896,11 @@ complection of submission. One minor feature still missing is a generic DRM IOCTL to query the error status of binary and timeline drm_syncobj. -This should probably be improved by implementing the necessary kernel interface -and adding support for that in the userspace stack. +This was already improved by implementing the necessary kernel interface +(patches are on the dri-devel mailing list) but adding support for that in the +userspace stack is still missing. -Contact: Christian König +Contact: Christian König, Michel Dänzer Level: Starter @@ -948,6 +949,47 @@ Contact: Philipp Stanner <phasta@kernel.org> Level: Intermediate +Replace the lockless queue with a locked list +--------------------------------------------- + +drm_sched is the only user in the entire kernel of a special lockless queue, the +spsc_queue. This queue utilizes: + +- preempt_disable() +- atomic instructions +- memory barriers +- ACCESS_ONCE() + +whereas a conventional spinlock utilizes: + +- preempt_disable() +- 1 atomic instruction for taking / releasing the lock +- memory barriers + +Moreover, drm_sched_entity_push_job(), the only user of spsc_queue_push(), has +to take a lock in some situations anyways and calls to it are often serialized +with a driver lock. + +It is, thus, highly questionable whether the lockless queue grants any advantage +at all. Considering that its internals are not well documented and its correctness +is not formally proven, it seems desirable to replace the queue with a mere list +or hlist that is protected by a spinlock. + +Tasks: + +- Replace the spsc_queue in drm/sched (and those who might access the scheduler's + internal queue) with a spinlock + (h)list. +- Ideally, check with some micro benchmarks and real world tests (preferably + with amdgpu) for relevant performance regressions. +- Remove the spsc_queue from the kernel altogether. + +Contact: + +- Philipp Stanner <phasta@kernel.org> +- Christian König <christian.koenig@amd.com> + +Level: Beginner + Outside DRM =========== diff --git a/MAINTAINERS b/MAINTAINERS index 8e2551fd5cba..5f1cfb757d3b 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -8122,6 +8122,12 @@ T: git https://gitlab.freedesktop.org/drm/misc/kernel.git F: Documentation/devicetree/bindings/display/ilitek,ili9486.yaml F: drivers/gpu/drm/tiny/ili9486.c +DRM DRIVER FOR ILITEK ILI9488 PANELS +M: Igor Reznichenko <igor@reznichenko.net> +S: Maintained +F: Documentation/devicetree/bindings/display/panel/ilitek,ili9488.yaml +F: drivers/gpu/drm/panel/panel-ilitek-ili9488.c + DRM DRIVER FOR ILITEK ILI9805 PANELS M: Michael Trimarchi <michael@amarulasolutions.com> S: Maintained @@ -12656,12 +12662,15 @@ S: Orphan F: drivers/media/rc/img-ir/ IMGTEC POWERVR DRM DRIVER -M: Frank Binns <frank.binns@imgtec.com> -M: Matt Coster <matt.coster@imgtec.com> +M: Alessio Belle <alessio.belle@imgtec.com> +M: Luigi Santivetti <luigi.santivetti@imgtec.com> +L: imagination@lists.freedesktop.org S: Supported +Q: https://patchwork.freedesktop.org/project/imagination/list/ +B: https://gitlab.freedesktop.org/imagination/linux/-/issues +C: irc://irc.oftc.net/powervr T: git https://gitlab.freedesktop.org/drm/misc/kernel.git -F: Documentation/devicetree/bindings/gpu/img,powervr-rogue.yaml -F: Documentation/devicetree/bindings/gpu/img,powervr-sgx.yaml +F: Documentation/devicetree/bindings/gpu/img,powervr-*.yaml F: Documentation/gpu/imagination/ F: drivers/gpu/drm/ci/xfails/powervr* F: drivers/gpu/drm/imagination/ diff --git a/drivers/accel/amdxdna/amdxdna_gem.c b/drivers/accel/amdxdna/amdxdna_gem.c index 45abd6a804cc..8d9c4a3ddc4f 100644 --- a/drivers/accel/amdxdna/amdxdna_gem.c +++ b/drivers/accel/amdxdna/amdxdna_gem.c @@ -732,9 +732,15 @@ static int amdxdna_gem_dev_obj_vmap(struct drm_gem_object *obj, struct iosys_map return 0; } +static struct dma_buf *amdxdna_gem_dev_obj_export(struct drm_gem_object *gobj, int flags) +{ + return ERR_PTR(-EOPNOTSUPP); +} + static const struct drm_gem_object_funcs amdxdna_gem_dev_obj_funcs = { .free = amdxdna_gem_dev_obj_free, .vmap = amdxdna_gem_dev_obj_vmap, + .export = amdxdna_gem_dev_obj_export, }; static const struct drm_gem_object_funcs amdxdna_gem_shmem_funcs = { diff --git a/drivers/accel/ethosu/Makefile b/drivers/accel/ethosu/Makefile index 17db5a600416..598a388b7179 100644 --- a/drivers/accel/ethosu/Makefile +++ b/drivers/accel/ethosu/Makefile @@ -1,4 +1,4 @@ # SPDX-License-Identifier: GPL-2.0-only obj-$(CONFIG_DRM_ACCEL_ARM_ETHOSU) := ethosu.o -ethosu-y += ethosu_drv.o ethosu_gem.o ethosu_job.o +ethosu-y += ethosu_drv.o ethosu_gem.o ethosu_job.o ethosu_perfmon.o diff --git a/drivers/accel/ethosu/ethosu_device.h b/drivers/accel/ethosu/ethosu_device.h index b189fa783d6a..d4458eac8447 100644 --- a/drivers/accel/ethosu/ethosu_device.h +++ b/drivers/accel/ethosu/ethosu_device.h @@ -6,6 +6,7 @@ #include <linux/bitfield.h> #include <linux/bits.h> +#include <linux/mutex.h> #include <linux/types.h> #include <drm/drm_device.h> @@ -43,6 +44,15 @@ struct gen_pool; #define NPU_REG_BASEP_HI(x) (0x0084 + (x) * 8) #define NPU_BASEP_REGION_MAX 8 +#define NPU_REG_PMCR 0x0180 +#define NPU_REG_PMCNTENSET 0x0184 +#define NPU_REG_PMCNTENCLR 0x0188 +#define NPU_REG_PMCCNTR_LO 0x01A0 +#define NPU_REG_PMCCNTR_HI 0x01A4 +#define NPU_REG_PMCCNTR_CFG 0x01A8 +#define NPU_REG_PMU_EVCNTR(x) (0x0300 + (x) * 4) +#define NPU_REG_PMU_EVTYPER(x) (0x0380 + (x) * 4) + #define ID_ARCH_MAJOR_MASK GENMASK(31, 28) #define ID_ARCH_MINOR_MASK GENMASK(27, 20) #define ID_ARCH_PATCH_MASK GENMASK(19, 16) @@ -67,6 +77,15 @@ struct gen_pool; #define PROT_ACTIVE_CSL BIT(1) +#define PMCR_NUM_EVENT_CNT_MASK GENMASK(15, 11) +#define PMCR_CYCLE_CNT_RST BIT(2) +#define PMCR_EVENT_CNT_RST BIT(1) +#define PMCR_CNT_EN BIT(0) + +#define PMU_EV_TYPE_NONE 0 +#define PMU_EV_TYPE_CYCLES 0x11 +#define PMU_EV_TYPE_IDLE 0x20 + enum ethosu_cmds { NPU_OP_CONV = 0x2, NPU_OP_DEPTHWISE = 0x3, @@ -152,6 +171,8 @@ enum ethosu_cmds { #define ETHOSU_SRAM_REGION 2 /* Matching Vela compiler */ +struct ethosu_perfmon; + /** * struct ethosu_device - Ethosu device */ @@ -161,6 +182,7 @@ struct ethosu_device { /** @iomem: CPU mapping of the registers. */ void __iomem *regs; + void __iomem *pmu_regs; void __iomem *sram; struct gen_pool *srampool; @@ -173,8 +195,6 @@ struct ethosu_device { struct drm_ethosu_npu_info npu_info; struct ethosu_job *in_flight_job; - /* For in_flight_job and ethosu_job_hw_submit() */ - struct mutex job_lock; /* For dma_fence */ spinlock_t fence_lock; @@ -184,6 +204,17 @@ struct ethosu_device { struct mutex sched_lock; u64 fence_context; u64 emit_seqno; + + /* Tracks the performance monitor state. */ + struct { + /* Protects @active. */ + struct mutex lock; + + /* Perfmon currently programmed in HW (or NULL if none). */ + struct ethosu_perfmon *active; + } perfmon_state; + + struct ethosu_perfmon *global_perfmon; }; #define to_ethosu_device(drm_dev) \ diff --git a/drivers/accel/ethosu/ethosu_drv.c b/drivers/accel/ethosu/ethosu_drv.c index ed9c748a54ad..d121fb0d7732 100644 --- a/drivers/accel/ethosu/ethosu_drv.c +++ b/drivers/accel/ethosu/ethosu_drv.c @@ -15,6 +15,7 @@ #include <drm/drm_utils.h> #include <drm/drm_gem.h> #include <drm/drm_accel.h> +#include <drm/drm_managed.h> #include <drm/ethosu_accel.h> #include "ethosu_drv.h" @@ -154,6 +155,7 @@ static int ethosu_open(struct drm_device *ddev, struct drm_file *file) if (ret) goto err_put_mod; + ethosu_perfmon_open_file(priv); file->driver_priv = no_free_ptr(priv); return 0; @@ -165,6 +167,7 @@ err_put_mod: static void ethosu_postclose(struct drm_device *ddev, struct drm_file *file) { ethosu_job_close(file->driver_priv); + ethosu_perfmon_close_file(file->driver_priv); kfree(file->driver_priv); module_put(THIS_MODULE); } @@ -179,6 +182,10 @@ static const struct drm_ioctl_desc ethosu_drm_driver_ioctls[] = { ETHOSU_IOCTL(BO_MMAP_OFFSET, bo_mmap_offset, 0), ETHOSU_IOCTL(CMDSTREAM_BO_CREATE, cmdstream_bo_create, 0), ETHOSU_IOCTL(SUBMIT, submit, 0), + ETHOSU_IOCTL(PERFMON_CREATE, perfmon_create, 0), + ETHOSU_IOCTL(PERFMON_DESTROY, perfmon_destroy, 0), + ETHOSU_IOCTL(PERFMON_GET_VALUES, perfmon_get_values, 0), + ETHOSU_IOCTL(PERFMON_SET_GLOBAL, perfmon_set_global, 0), }; DEFINE_DRM_ACCEL_FOPS(ethosu_drm_driver_fops); @@ -314,8 +321,14 @@ static int ethosu_init(struct ethosu_device *ethosudev) ethosu_sram_init(ethosudev); + if (!ethosu_is_u65(ethosudev)) + ethosudev->pmu_regs += 0x1000; + + ethosudev->npu_info.pmu_counters = FIELD_GET(PMCR_NUM_EVENT_CNT_MASK, + readl_relaxed(ethosudev->pmu_regs + NPU_REG_PMCR)); + dev_info(ethosudev->base.dev, - "Ethos-U NPU, arch v%ld.%ld.%ld, rev r%ldp%ld, cmd stream ver%ld, %d MACs, %dKB SRAM\n", + "Ethos-U NPU, arch v%ld.%ld.%ld, rev r%ldp%ld, cmd stream ver%ld, %d MACs, %dKB SRAM, %d PMU cntrs\n", FIELD_GET(ID_ARCH_MAJOR_MASK, id), FIELD_GET(ID_ARCH_MINOR_MASK, id), FIELD_GET(ID_ARCH_PATCH_MASK, id), @@ -323,7 +336,8 @@ static int ethosu_init(struct ethosu_device *ethosudev) FIELD_GET(ID_VER_MINOR_MASK, id), FIELD_GET(CONFIG_CMD_STREAM_VER_MASK, config), 1 << FIELD_GET(CONFIG_MACS_PER_CC_MASK, config), - ethosudev->npu_info.sram_size / 1024); + ethosudev->npu_info.sram_size / 1024, + ethosudev->npu_info.pmu_counters); return 0; } @@ -342,11 +356,16 @@ static int ethosu_probe(struct platform_device *pdev) dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(40)); ethosudev->regs = devm_platform_ioremap_resource(pdev, 0); + ethosudev->pmu_regs = ethosudev->regs; ethosudev->num_clks = devm_clk_bulk_get_all(&pdev->dev, ðosudev->clks); if (ethosudev->num_clks < 0) return ethosudev->num_clks; + ret = drmm_mutex_init(ðosudev->base, ðosudev->perfmon_state.lock); + if (ret) + return ret; + ret = ethosu_job_init(ethosudev); if (ret) return ret; diff --git a/drivers/accel/ethosu/ethosu_drv.h b/drivers/accel/ethosu/ethosu_drv.h index 9e21dfe94184..2193bc51d425 100644 --- a/drivers/accel/ethosu/ethosu_drv.h +++ b/drivers/accel/ethosu/ethosu_drv.h @@ -1,15 +1,74 @@ /* SPDX-License-Identifier: GPL-2.0-only OR MIT */ -/* Copyright 2025 Arm, Ltd. */ +/* Copyright 2025-2026 Arm, Ltd. */ #ifndef __ETHOSU_DRV_H__ #define __ETHOSU_DRV_H__ +#include <linux/mutex.h> +#include <linux/xarray.h> #include <drm/gpu_scheduler.h> struct ethosu_device; +struct drm_device; +struct drm_file; struct ethosu_file_priv { struct ethosu_device *edev; struct drm_sched_entity sched_entity; + struct xarray perfmons; }; +/* Performance monitor object. The perfmon lifetime is controlled by userspace + * using perfmon related ioctls. A perfmon can be attached to a DRM_ETHOSU_SUBMIT + * request, and when this is the case, HW perf counters will be activated just + * before the job is submitted to the NPU and disabled when the job is + * done. This way, only events related to a specific job will be counted. + */ +struct ethosu_perfmon { + /* Tracks the number of users of the perfmon, when this counter reaches + * zero the perfmon is destroyed. + */ + refcount_t refcnt; + + /* Number of counters activated in this perfmon instance + * (should be less than or equal to DRM_ETHOSU_MAX_PERF_COUNTERS). + */ + u8 ncounters; + + /* Events counted by the HW perf counters. */ + u16 counters[DRM_ETHOSU_MAX_PERF_EVENT_COUNTERS]; + + /* + * Storage for counter values. Counters are incremented by the HW + * perf counter values every time the perfmon is attached to an + * NPU job. This way, perfmon users don't have to retrieve the + * results after each job if they want to track events covering + * several submissions. Note that counter values can't be reset, + * but you can fake a reset by destroying the perfmon and + * creating a new one. + */ + u64 values[] __counted_by(ncounters); +}; + +/* ethosu_perfmon.c */ +void ethosu_perfmon_get(struct ethosu_perfmon *perfmon); +void ethosu_perfmon_put(struct ethosu_perfmon *perfmon); +void ethosu_perfmon_start(struct ethosu_device *ethosu, + struct ethosu_perfmon *perfmon); +void ethosu_perfmon_stop(struct ethosu_device *ethosu, + struct ethosu_perfmon *perfmon, bool capture); +void ethosu_perfmon_stop_locked(struct ethosu_device *ethosu, struct ethosu_perfmon *perfmon, + bool capture); +struct ethosu_perfmon *ethosu_perfmon_find(struct ethosu_file_priv *ethosu_priv, + int id); +void ethosu_perfmon_open_file(struct ethosu_file_priv *ethosu_priv); +void ethosu_perfmon_close_file(struct ethosu_file_priv *ethosu_priv); +int ethosu_ioctl_perfmon_create(struct drm_device *dev, void *data, + struct drm_file *file_priv); +int ethosu_ioctl_perfmon_destroy(struct drm_device *dev, void *data, + struct drm_file *file_priv); +int ethosu_ioctl_perfmon_get_values(struct drm_device *dev, void *data, + struct drm_file *file_priv); +int ethosu_ioctl_perfmon_set_global(struct drm_device *dev, void *data, + struct drm_file *file_priv); + #endif diff --git a/drivers/accel/ethosu/ethosu_job.c b/drivers/accel/ethosu/ethosu_job.c index b76924645aaa..74f3493fb11a 100644 --- a/drivers/accel/ethosu/ethosu_job.c +++ b/drivers/accel/ethosu/ethosu_job.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only OR MIT /* Copyright 2024-2025 Tomeu Vizoso <tomeu@tomeuvizoso.net> */ -/* Copyright 2025 Arm, Ltd. */ +/* Copyright 2025-2026 Arm, Ltd. */ #include <linux/bitfield.h> #include <linux/genalloc.h> @@ -147,6 +147,8 @@ static void ethosu_job_err_cleanup(struct ethosu_job *job) { unsigned int i; + ethosu_perfmon_put(job->perfmon); + for (i = 0; i < job->region_cnt; i++) drm_gem_object_put(job->region_bo[i]); @@ -181,6 +183,26 @@ static void ethosu_job_free(struct drm_sched_job *sched_job) ethosu_job_put(job); } +static void +ethosu_switch_perfmon(struct ethosu_device *ethosu, struct ethosu_job *job) +{ + struct ethosu_perfmon *perfmon; + + guard(mutex)(ðosu->perfmon_state.lock); + + perfmon = ethosu->global_perfmon; + if (!perfmon) + perfmon = job->perfmon; + + if (perfmon == ethosu->perfmon_state.active) + return; + + ethosu_perfmon_stop_locked(ethosu, ethosu->perfmon_state.active, true); + + if (perfmon) + ethosu_perfmon_start(ethosu, perfmon); +} + static struct dma_fence *ethosu_job_run(struct drm_sched_job *sched_job) { struct ethosu_job *job = to_ethosu_job(sched_job); @@ -194,10 +216,10 @@ static struct dma_fence *ethosu_job_run(struct drm_sched_job *sched_job) dev->fence_context, ++dev->emit_seqno); dma_fence_get(fence); - scoped_guard(mutex, &dev->job_lock) { - dev->in_flight_job = job; - ethosu_job_hw_submit(dev, job); - } + ethosu_switch_perfmon(dev, job); + + WRITE_ONCE(dev->in_flight_job, job); + ethosu_job_hw_submit(dev, job); return fence; } @@ -205,6 +227,7 @@ static struct dma_fence *ethosu_job_run(struct drm_sched_job *sched_job) static void ethosu_job_handle_irq(struct ethosu_device *dev) { u32 status = readl_relaxed(dev->regs + NPU_REG_STATUS); + struct ethosu_job *job; if (status & (STATUS_BUS_STATUS | STATUS_CMD_PARSE_ERR)) { dev_err(dev->base.dev, "Error IRQ - %x\n", status); @@ -212,11 +235,10 @@ static void ethosu_job_handle_irq(struct ethosu_device *dev) return; } - scoped_guard(mutex, &dev->job_lock) { - if (dev->in_flight_job) { - dma_fence_signal(dev->in_flight_job->done_fence); - dev->in_flight_job = NULL; - } + job = READ_ONCE(dev->in_flight_job); + if (job) { + WRITE_ONCE(dev->in_flight_job, NULL); + dma_fence_signal(job->done_fence); } } @@ -272,8 +294,7 @@ static enum drm_gpu_sched_stat ethosu_job_timedout(struct drm_sched_job *bad) drm_sched_stop(&dev->sched, bad); - scoped_guard(mutex, &dev->job_lock) - dev->in_flight_job = NULL; + WRITE_ONCE(dev->in_flight_job, NULL); /* Proceed with reset now. */ pm_runtime_force_suspend(dev->base.dev); @@ -304,9 +325,6 @@ int ethosu_job_init(struct ethosu_device *edev) int ret; spin_lock_init(&edev->fence_lock); - ret = devm_mutex_init(dev, &edev->job_lock); - if (ret) - return ret; ret = devm_mutex_init(dev, &edev->sched_lock); if (ret) return ret; @@ -365,7 +383,8 @@ void ethosu_job_close(struct ethosu_file_priv *ethosu_priv) } static int ethosu_ioctl_submit_job(struct drm_device *dev, struct drm_file *file, - struct drm_ethosu_job *job) + struct drm_ethosu_job *job, + int perfmon_id) { struct ethosu_device *edev = to_ethosu_device(dev); struct ethosu_file_priv *file_priv = file->driver_priv; @@ -389,6 +408,9 @@ static int ethosu_ioctl_submit_job(struct drm_device *dev, struct drm_file *file ejob->dev = edev; ejob->sram_size = job->sram_size; + if (perfmon_id) + ejob->perfmon = ethosu_perfmon_find(file_priv, perfmon_id); + ejob->done_fence = kzalloc_obj(*ejob->done_fence); if (!ejob->done_fence) { ret = -ENOMEM; @@ -491,11 +513,6 @@ int ethosu_ioctl_submit(struct drm_device *dev, void *data, struct drm_file *fil int ret = 0; unsigned int i = 0; - if (args->pad) { - drm_dbg(dev, "Reserved field in drm_ethosu_submit struct should be 0.\n"); - return -EINVAL; - } - struct drm_ethosu_job __free(kvfree) *jobs = kvmalloc_objs(*jobs, args->job_count); if (!jobs) @@ -509,7 +526,7 @@ int ethosu_ioctl_submit(struct drm_device *dev, void *data, struct drm_file *fil } for (i = 0; i < args->job_count; i++) { - ret = ethosu_ioctl_submit_job(dev, file, &jobs[i]); + ret = ethosu_ioctl_submit_job(dev, file, &jobs[i], args->perfmon_id); if (ret) return ret; } diff --git a/drivers/accel/ethosu/ethosu_job.h b/drivers/accel/ethosu/ethosu_job.h index ff1cf448d094..8988edd00eed 100644 --- a/drivers/accel/ethosu/ethosu_job.h +++ b/drivers/accel/ethosu/ethosu_job.h @@ -21,6 +21,8 @@ struct ethosu_job { u8 region_cnt; u32 sram_size; + struct ethosu_perfmon *perfmon; + /* Fence to be signaled by drm-sched once its done with the job */ struct dma_fence *inference_done_fence; diff --git a/drivers/accel/ethosu/ethosu_perfmon.c b/drivers/accel/ethosu/ethosu_perfmon.c new file mode 100644 index 000000000000..26f625374f9d --- /dev/null +++ b/drivers/accel/ethosu/ethosu_perfmon.c @@ -0,0 +1,301 @@ +// SPDX-License-Identifier: GPL-2.0-only OR MIT +/* Copyright 2026 Arm, Ltd. */ +/* Based on v3d_perfmon.c, Copyright (C) 2021 Raspberry Pi */ + +#include <linux/device.h> +#include <linux/errno.h> +#include <linux/pm_runtime.h> +#include <linux/types.h> +#include <linux/slab.h> +#include <linux/uaccess.h> + +#include <drm/drm_file.h> +#include <drm/drm_ioctl.h> + +#include <uapi/drm/ethosu_accel.h> + +#include "ethosu_drv.h" +#include "ethosu_device.h" + +void ethosu_perfmon_get(struct ethosu_perfmon *perfmon) +{ + if (perfmon) + refcount_inc(&perfmon->refcnt); +} + +void ethosu_perfmon_put(struct ethosu_perfmon *perfmon) +{ + if (perfmon && refcount_dec_and_test(&perfmon->refcnt)) + kfree(perfmon); +} + +void ethosu_perfmon_start(struct ethosu_device *ethosu, struct ethosu_perfmon *perfmon) +{ + unsigned int i; + u8 ncounters; + u32 mask; + + lockdep_assert_held(ðosu->perfmon_state.lock); + + if (WARN_ON_ONCE(!perfmon || ethosu->perfmon_state.active)) + return; + + writel_relaxed(PMCR_CNT_EN, ethosu->pmu_regs + NPU_REG_PMCR); + writel_relaxed(PMU_EV_TYPE_CYCLES, ethosu->pmu_regs + NPU_REG_PMCCNTR_CFG); + + mask = 0x80000000; + ncounters = perfmon->ncounters - 1; + if (ncounters) + mask |= GENMASK(ncounters - 1, 0); + + for (i = 0; i < ncounters; i++) + writel_relaxed(perfmon->counters[i], ethosu->pmu_regs + NPU_REG_PMU_EVTYPER(i)); + + writel_relaxed(mask, ethosu->pmu_regs + NPU_REG_PMCNTENSET); + writel_relaxed(PMCR_CNT_EN | PMCR_EVENT_CNT_RST | PMCR_CYCLE_CNT_RST, + ethosu->pmu_regs + NPU_REG_PMCR); + ethosu->perfmon_state.active = perfmon; +} + +void ethosu_perfmon_stop_locked(struct ethosu_device *ethosu, struct ethosu_perfmon *perfmon, + bool capture) +{ + unsigned int i; + u8 ncounters; + u32 mask; + + lockdep_assert_held(ðosu->perfmon_state.lock); + + if (!perfmon || perfmon != ethosu->perfmon_state.active) + return; + + ncounters = perfmon->ncounters - 1; + + if (!pm_runtime_get_if_active(ethosu->base.dev)) { + ethosu->perfmon_state.active = NULL; + return; + } + + if (capture) { + for (i = 0; i < ncounters; i++) + perfmon->values[i] += readl_relaxed(ethosu->pmu_regs + NPU_REG_PMU_EVCNTR(i)); + + perfmon->values[ncounters] += + readl_relaxed(ethosu->pmu_regs + NPU_REG_PMCCNTR_LO) | + (u64)readl_relaxed(ethosu->pmu_regs + NPU_REG_PMCCNTR_HI) << 32; + } + + mask = 0x80000000; + if (ncounters) + mask |= GENMASK(ncounters - 1, 0); + writel_relaxed(mask, ethosu->pmu_regs + NPU_REG_PMCNTENCLR); + + writel_relaxed(0, ethosu->pmu_regs + NPU_REG_PMCR); + ethosu->perfmon_state.active = NULL; + + pm_runtime_put(ethosu->base.dev); +} + +void ethosu_perfmon_stop(struct ethosu_device *ethosu, struct ethosu_perfmon *perfmon, + bool capture) +{ + if (!perfmon) + return; + + guard(mutex)(ðosu->perfmon_state.lock); + ethosu_perfmon_stop_locked(ethosu, perfmon, capture); +} + +struct ethosu_perfmon *ethosu_perfmon_find(struct ethosu_file_priv *ethosu_priv, int id) +{ + struct ethosu_perfmon *perfmon; + + xa_lock(ðosu_priv->perfmons); + perfmon = xa_load(ðosu_priv->perfmons, id); + ethosu_perfmon_get(perfmon); + xa_unlock(ðosu_priv->perfmons); + + return perfmon; +} + +void ethosu_perfmon_open_file(struct ethosu_file_priv *ethosu_priv) +{ + xa_init_flags(ðosu_priv->perfmons, XA_FLAGS_ALLOC1); +} + +static void ethosu_perfmon_delete(struct ethosu_file_priv *ethosu_priv, + struct ethosu_perfmon *perfmon) +{ + struct ethosu_device *ethosu = ethosu_priv->edev; + + /* If the active perfmon is being destroyed, stop it first */ + scoped_guard(mutex, ðosu->perfmon_state.lock) { + /* If the global perfmon is being destroyed, set it to NULL */ + if (ethosu->global_perfmon == perfmon) { + ethosu->global_perfmon = NULL; + ethosu_perfmon_put(perfmon); + } + + ethosu_perfmon_stop_locked(ethosu, perfmon, false); + } + + ethosu_perfmon_put(perfmon); +} + +void ethosu_perfmon_close_file(struct ethosu_file_priv *ethosu_priv) +{ + struct ethosu_perfmon *perfmon; + unsigned long id; + + xa_for_each(ðosu_priv->perfmons, id, perfmon) + ethosu_perfmon_delete(ethosu_priv, perfmon); + + xa_destroy(ðosu_priv->perfmons); +} + +int ethosu_ioctl_perfmon_create(struct drm_device *dev, void *data, + struct drm_file *file_priv) +{ + struct ethosu_file_priv *ethosu_priv = file_priv->driver_priv; + struct drm_ethosu_perfmon_create *req = data; + struct ethosu_device *ethosu = to_ethosu_device(dev); + struct ethosu_perfmon *perfmon; + unsigned int i, event_max; + int ret; + u32 id; + + /* Number of monitored counters cannot exceed HW limits. */ + if (req->ncounters > ethosu->npu_info.pmu_counters) + return -EINVAL; + + /* Make sure all counters are valid. */ + event_max = ethosu_is_u65(ethosu) ? 433 : 671; + for (i = 0; i < req->ncounters; i++) { + if (req->counters[i] > event_max) + return -EINVAL; + } + + /* Add 1 more counter for cycle counter */ + req->ncounters++; + + perfmon = kzalloc_flex(*perfmon, values, req->ncounters); + if (!perfmon) + return -ENOMEM; + + for (i = 0; i < req->ncounters - 1; i++) + perfmon->counters[i] = req->counters[i]; + + perfmon->ncounters = req->ncounters; + + refcount_set(&perfmon->refcnt, 1); + + ret = xa_alloc(ðosu_priv->perfmons, &id, perfmon, xa_limit_32b, + GFP_KERNEL); + + if (ret < 0) { + kfree(perfmon); + return ret; + } + + req->id = id; + + return 0; +} + +int ethosu_ioctl_perfmon_destroy(struct drm_device *dev, void *data, + struct drm_file *file_priv) +{ + struct ethosu_file_priv *ethosu_priv = file_priv->driver_priv; + struct drm_ethosu_perfmon_destroy *req = data; + struct ethosu_perfmon *perfmon; + + perfmon = xa_erase(ðosu_priv->perfmons, req->id); + if (!perfmon) + return -EINVAL; + + ethosu_perfmon_delete(ethosu_priv, perfmon); + + return 0; +} + +int ethosu_ioctl_perfmon_get_values(struct drm_device *dev, void *data, + struct drm_file *file_priv) +{ + struct ethosu_device *ethosu = to_ethosu_device(dev); + struct ethosu_file_priv *ethosu_priv = file_priv->driver_priv; + struct drm_ethosu_perfmon_get_values *req = data; + struct ethosu_perfmon *perfmon; + int ret = 0; + + if (req->pad != 0) + return -EINVAL; + + perfmon = ethosu_perfmon_find(ethosu_priv, req->id); + if (!perfmon) + return -EINVAL; + + ret = pm_runtime_resume_and_get(dev->dev); + if (ret) { + ethosu_perfmon_put(perfmon); + return ret; + } + ethosu_perfmon_stop(ethosu, perfmon, true); + + pm_runtime_put_autosuspend(dev->dev); + + if (copy_to_user(u64_to_user_ptr(req->values_ptr), perfmon->values, + perfmon->ncounters * sizeof(u64))) + ret = -EFAULT; + + ethosu_perfmon_put(perfmon); + + return ret; +} + +int ethosu_ioctl_perfmon_set_global(struct drm_device *dev, void *data, + struct drm_file *file_priv) +{ + struct ethosu_file_priv *ethosu_priv = file_priv->driver_priv; + struct drm_ethosu_perfmon_set_global *req = data; + struct ethosu_device *ethosu = to_ethosu_device(dev); + struct ethosu_perfmon *perfmon; + + if (req->flags & ~DRM_ETHOSU_PERFMON_CLEAR_GLOBAL) + return -EINVAL; + + perfmon = ethosu_perfmon_find(ethosu_priv, req->id); + if (!perfmon) + return -EINVAL; + + /* If the request is to clear the global performance monitor */ + if (req->flags & DRM_ETHOSU_PERFMON_CLEAR_GLOBAL) { + struct ethosu_perfmon *old; + + scoped_guard(mutex, ðosu->perfmon_state.lock) { + old = ethosu->global_perfmon; + if (!old) { + ethosu_perfmon_put(perfmon); + return -EINVAL; + } + + ethosu->global_perfmon = NULL; + ethosu_perfmon_stop_locked(ethosu, old, true); + } + + ethosu_perfmon_put(old); + ethosu_perfmon_put(perfmon); + + return 0; + } + + scoped_guard(mutex, ðosu->perfmon_state.lock) { + if (ethosu->perfmon_state.active || ethosu->global_perfmon) { + ethosu_perfmon_put(perfmon); + return -EBUSY; + } + + ethosu->global_perfmon = perfmon; + } + + return 0; +} diff --git a/drivers/accel/ivpu/ivpu_drv.c b/drivers/accel/ivpu/ivpu_drv.c index 35e506074d5f..95120957f42a 100644 --- a/drivers/accel/ivpu/ivpu_drv.c +++ b/drivers/accel/ivpu/ivpu_drv.c @@ -307,6 +307,11 @@ static int ivpu_open(struct drm_device *dev, struct drm_file *file) return -ENODEV; limits = ivpu_user_limits_get(vdev); + if (IS_ERR(limits) && PTR_ERR(limits) == -EMFILE) { + /* Context limit may be held by jobs pending deferred cleanup */ + flush_work(&vdev->job_destroy_work); + limits = ivpu_user_limits_get(vdev); + } if (IS_ERR(limits)) { ret = PTR_ERR(limits); goto err_dev_exit; @@ -510,9 +515,9 @@ void ivpu_prepare_for_reset(struct ivpu_device *vdev) { ivpu_hw_irq_disable(vdev); disable_irq(vdev->irq); - flush_work(&vdev->irq_ipc_work); flush_work(&vdev->irq_dct_work); flush_work(&vdev->context_abort_work); + flush_work(&vdev->job_destroy_work); ivpu_ipc_disable(vdev); ivpu_mmu_disable(vdev); } @@ -584,6 +589,11 @@ static const struct drm_driver driver = { .major = 1, }; +static void ivpu_destroy_workqueue(void *wq) +{ + destroy_workqueue(wq); +} + static int ivpu_irq_init(struct ivpu_device *vdev) { struct pci_dev *pdev = to_pci_dev(vdev->drm.dev); @@ -595,16 +605,26 @@ static int ivpu_irq_init(struct ivpu_device *vdev) return ret; } - INIT_WORK(&vdev->irq_ipc_work, ivpu_ipc_irq_work_fn); INIT_WORK(&vdev->irq_dct_work, ivpu_pm_irq_dct_work_fn); INIT_WORK(&vdev->context_abort_work, ivpu_context_abort_work_fn); + init_llist_head(&vdev->job_destroy_list); + INIT_WORK(&vdev->job_destroy_work, ivpu_job_destroy_work_fn); + + vdev->job_destroy_wq = alloc_workqueue("ivpu_job_destroy", WQ_UNBOUND | WQ_MEM_RECLAIM, 0); + if (!vdev->job_destroy_wq) + return -ENOMEM; + + ret = devm_add_action_or_reset(vdev->drm.dev, ivpu_destroy_workqueue, vdev->job_destroy_wq); + if (ret) + return ret; ivpu_irq_handlers_init(vdev); vdev->irq = pci_irq_vector(pdev, 0); - ret = devm_request_irq(vdev->drm.dev, vdev->irq, ivpu_hw_irq_handler, - IRQF_NO_AUTOEN, DRIVER_NAME, vdev); + ret = devm_request_threaded_irq(vdev->drm.dev, vdev->irq, ivpu_hw_irq_handler, + ivpu_ipc_irq_thread_handler, IRQF_NO_AUTOEN, + DRIVER_NAME, vdev); if (ret) ivpu_err(vdev, "Failed to request an IRQ %d\n", ret); diff --git a/drivers/accel/ivpu/ivpu_drv.h b/drivers/accel/ivpu/ivpu_drv.h index 9eefbbb7ba11..86d7c9966cac 100644 --- a/drivers/accel/ivpu/ivpu_drv.h +++ b/drivers/accel/ivpu/ivpu_drv.h @@ -13,6 +13,7 @@ #include <drm/drm_print.h> #include <linux/hashtable.h> +#include <linux/llist.h> #include <linux/pci.h> #include <linux/xarray.h> #include <uapi/drm/ivpu_accel.h> @@ -157,9 +158,11 @@ struct ivpu_device { struct xa_limit db_limit; u32 db_next; - struct work_struct irq_ipc_work; struct work_struct irq_dct_work; struct work_struct context_abort_work; + struct llist_head job_destroy_list; + struct work_struct job_destroy_work; + struct workqueue_struct *job_destroy_wq; struct mutex bo_list_lock; /* Protects bo_list */ struct list_head bo_list; diff --git a/drivers/accel/ivpu/ivpu_hw.c b/drivers/accel/ivpu/ivpu_hw.c index d4a9bcda4100..647dc045c231 100644 --- a/drivers/accel/ivpu/ivpu_hw.c +++ b/drivers/accel/ivpu/ivpu_hw.c @@ -399,6 +399,10 @@ irqreturn_t ivpu_hw_irq_handler(int irq, void *ptr) return IRQ_NONE; pm_runtime_mark_last_busy(vdev->drm.dev); + + if (ip_handled) + return IRQ_WAKE_THREAD; + return IRQ_HANDLED; } diff --git a/drivers/accel/ivpu/ivpu_ipc.c b/drivers/accel/ivpu/ivpu_ipc.c index 978bc3d8704f..62607ec8ca8f 100644 --- a/drivers/accel/ivpu/ivpu_ipc.c +++ b/drivers/accel/ivpu/ivpu_ipc.c @@ -146,7 +146,7 @@ ivpu_ipc_rx_msg_add(struct ivpu_device *vdev, struct ivpu_ipc_consumer *cons, lockdep_assert_held(&ipc->cons_lock); - rx_msg = kzalloc_obj(*rx_msg, GFP_ATOMIC); + rx_msg = kmem_cache_zalloc(ipc->rx_msg_cache, GFP_ATOMIC); if (!rx_msg) { ivpu_ipc_rx_mark_free(vdev, ipc_hdr, jsm_msg); return; @@ -174,7 +174,7 @@ ivpu_ipc_rx_msg_del(struct ivpu_device *vdev, struct ivpu_ipc_rx_msg *rx_msg) list_del(&rx_msg->link); ivpu_ipc_rx_mark_free(vdev, rx_msg->ipc_hdr, rx_msg->jsm_msg); atomic_dec(&vdev->ipc->rx_msg_count); - kfree(rx_msg); + kmem_cache_free(vdev->ipc->rx_msg_cache, rx_msg); } void ivpu_ipc_consumer_add(struct ivpu_device *vdev, struct ivpu_ipc_consumer *cons, @@ -462,13 +462,11 @@ void ivpu_ipc_irq_handler(struct ivpu_device *vdev) ivpu_ipc_rx_mark_free(vdev, ipc_hdr, jsm_msg); } } - - queue_work(system_percpu_wq, &vdev->irq_ipc_work); } -void ivpu_ipc_irq_work_fn(struct work_struct *work) +irqreturn_t ivpu_ipc_irq_thread_handler(int irq, void *ptr) { - struct ivpu_device *vdev = container_of(work, struct ivpu_device, irq_ipc_work); + struct ivpu_device *vdev = ptr; struct ivpu_ipc_info *ipc = vdev->ipc; struct ivpu_ipc_rx_msg *rx_msg, *r; struct list_head cb_msg_list; @@ -483,6 +481,8 @@ void ivpu_ipc_irq_work_fn(struct work_struct *work) rx_msg->callback(vdev, rx_msg->ipc_hdr, rx_msg->jsm_msg); ivpu_ipc_rx_msg_del(vdev, rx_msg); } + + return IRQ_HANDLED; } int ivpu_ipc_init(struct ivpu_device *vdev) @@ -490,10 +490,18 @@ int ivpu_ipc_init(struct ivpu_device *vdev) struct ivpu_ipc_info *ipc = vdev->ipc; int ret; + ipc->rx_msg_cache = kmem_cache_create("ivpu_ipc_rx_msg", sizeof(struct ivpu_ipc_rx_msg), 0, + SLAB_HWCACHE_ALIGN, NULL); + if (!ipc->rx_msg_cache) { + ivpu_err(vdev, "Failed to create rx_msg_cache\n"); + return -ENOMEM; + } + ipc->mem_tx = ivpu_bo_create_global(vdev, SZ_16K, DRM_IVPU_BO_WC | DRM_IVPU_BO_MAPPABLE); if (!ipc->mem_tx) { ivpu_err(vdev, "Failed to allocate mem_tx\n"); - return -ENOMEM; + ret = -ENOMEM; + goto err_destroy_cache; } ipc->mem_rx = ivpu_bo_create_global(vdev, SZ_16K, DRM_IVPU_BO_WC | DRM_IVPU_BO_MAPPABLE); @@ -532,6 +540,8 @@ err_free_rx: ivpu_bo_free(ipc->mem_rx); err_free_tx: ivpu_bo_free(ipc->mem_tx); +err_destroy_cache: + kmem_cache_destroy(ipc->rx_msg_cache); return ret; } @@ -544,6 +554,7 @@ void ivpu_ipc_fini(struct ivpu_device *vdev) drm_WARN_ON(&vdev->drm, atomic_read(&ipc->rx_msg_count) > 0); ivpu_ipc_mem_fini(vdev); + kmem_cache_destroy(ipc->rx_msg_cache); } void ivpu_ipc_enable(struct ivpu_device *vdev) diff --git a/drivers/accel/ivpu/ivpu_ipc.h b/drivers/accel/ivpu/ivpu_ipc.h index b524a1985b9d..fb3e5816ca50 100644 --- a/drivers/accel/ivpu/ivpu_ipc.h +++ b/drivers/accel/ivpu/ivpu_ipc.h @@ -70,6 +70,7 @@ struct ivpu_ipc_info { struct gen_pool *mm_tx; struct ivpu_bo *mem_tx; struct ivpu_bo *mem_rx; + struct kmem_cache *rx_msg_cache; atomic_t rx_msg_count; @@ -90,7 +91,7 @@ void ivpu_ipc_disable(struct ivpu_device *vdev); void ivpu_ipc_reset(struct ivpu_device *vdev); void ivpu_ipc_irq_handler(struct ivpu_device *vdev); -void ivpu_ipc_irq_work_fn(struct work_struct *work); +irqreturn_t ivpu_ipc_irq_thread_handler(int irq, void *ptr); void ivpu_ipc_consumer_add(struct ivpu_device *vdev, struct ivpu_ipc_consumer *cons, u32 channel, ivpu_ipc_rx_callback_t callback); diff --git a/drivers/accel/ivpu/ivpu_job.c b/drivers/accel/ivpu/ivpu_job.c index b24f31a8b567..ebb2c865b09a 100644 --- a/drivers/accel/ivpu/ivpu_job.c +++ b/drivers/accel/ivpu/ivpu_job.c @@ -535,6 +535,20 @@ static void ivpu_job_destroy(struct ivpu_job *job) kfree(job); } +void ivpu_job_destroy_work_fn(struct work_struct *work) +{ + struct ivpu_device *vdev = container_of(work, struct ivpu_device, job_destroy_work); + struct ivpu_job *job, *tmp; + struct llist_node *list; + + list = llist_del_all(&vdev->job_destroy_list); + + llist_for_each_entry_safe(job, tmp, list, destroy_node) { + ivpu_job_destroy(job); + ivpu_rpm_put(vdev); + } +} + static struct ivpu_job * ivpu_job_create(struct ivpu_file_priv *file_priv, u32 engine_idx, u32 bo_count) { @@ -619,7 +633,7 @@ bool ivpu_job_handle_engine_error(struct ivpu_device *vdev, u32 job_id, u32 job_ return false; } -static int ivpu_job_signal_and_destroy(struct ivpu_device *vdev, u32 job_id, u32 job_status) +static struct ivpu_job *ivpu_job_signal(struct ivpu_device *vdev, u32 job_id, u32 job_status) { struct ivpu_job *job; @@ -627,7 +641,7 @@ static int ivpu_job_signal_and_destroy(struct ivpu_device *vdev, u32 job_id, u32 job = xa_load(&vdev->submitted_jobs_xa, job_id); if (!job) - return -ENOENT; + return NULL; ivpu_job_remove_from_submitted_jobs(vdev, job_id); @@ -646,14 +660,37 @@ static int ivpu_job_signal_and_destroy(struct ivpu_device *vdev, u32 job_id, u32 job->job_id, job->file_priv->ctx.id, job->cmdq_id, job->engine_idx, job->job_status); - ivpu_job_destroy(job); ivpu_stop_job_timeout_detection(vdev); - ivpu_rpm_put(vdev); - if (!xa_empty(&vdev->submitted_jobs_xa)) ivpu_start_job_timeout_detection(vdev); + return job; +} + +static int ivpu_job_signal_and_destroy(struct ivpu_device *vdev, u32 job_id, u32 job_status) +{ + struct ivpu_job *job = ivpu_job_signal(vdev, job_id, job_status); + + if (!job) + return -ENOENT; + + ivpu_job_destroy(job); + ivpu_rpm_put(vdev); + + return 0; +} + +static int ivpu_job_signal_and_defer_destroy(struct ivpu_device *vdev, u32 job_id, u32 job_status) +{ + struct ivpu_job *job = ivpu_job_signal(vdev, job_id, job_status); + + if (!job) + return -ENOENT; + + llist_add(&job->destroy_node, &vdev->job_destroy_list); + queue_work(vdev->job_destroy_wq, &vdev->job_destroy_work); + return 0; } @@ -689,6 +726,7 @@ static int ivpu_job_submit(struct ivpu_job *job, u8 priority, u32 cmdq_id) struct ivpu_file_priv *file_priv = job->file_priv; struct ivpu_device *vdev = job->vdev; struct ivpu_cmdq *cmdq; + bool flushed = false; bool is_first_job; int ret; @@ -696,6 +734,7 @@ static int ivpu_job_submit(struct ivpu_job *job, u8 priority, u32 cmdq_id) if (ret < 0) return ret; +retry: mutex_lock(&vdev->submitted_jobs_lock); mutex_lock(&file_priv->lock); @@ -709,6 +748,14 @@ static int ivpu_job_submit(struct ivpu_job *job, u8 priority, u32 cmdq_id) } ret = ivpu_cmdq_register(file_priv, cmdq); + if (ret == -EBUSY && !flushed) { + /* Doorbell may be held by jobs pending deferred cleanup */ + mutex_unlock(&file_priv->lock); + mutex_unlock(&vdev->submitted_jobs_lock); + flush_work(&vdev->job_destroy_work); + flushed = true; + goto retry; + } if (ret) { ivpu_err(vdev, "Failed to register command queue: %d\n", ret); goto err_unlock; @@ -1101,7 +1148,7 @@ ivpu_job_done_callback(struct ivpu_device *vdev, struct ivpu_ipc_hdr *ipc_hdr, mutex_lock(&vdev->submitted_jobs_lock); if (!ivpu_job_handle_engine_error(vdev, payload->job_id, payload->job_status)) /* No engine error, complete the job normally */ - ivpu_job_signal_and_destroy(vdev, payload->job_id, payload->job_status); + ivpu_job_signal_and_defer_destroy(vdev, payload->job_id, payload->job_status); mutex_unlock(&vdev->submitted_jobs_lock); } diff --git a/drivers/accel/ivpu/ivpu_job.h b/drivers/accel/ivpu/ivpu_job.h index 3ab61e6a5616..d8dbce82447a 100644 --- a/drivers/accel/ivpu/ivpu_job.h +++ b/drivers/accel/ivpu/ivpu_job.h @@ -6,8 +6,10 @@ #ifndef __IVPU_JOB_H__ #define __IVPU_JOB_H__ -#include <linux/kref.h> #include <linux/idr.h> +#include <linux/kref.h> +#include <linux/llist.h> +#include <linux/workqueue.h> #include "ivpu_gem.h" @@ -47,6 +49,7 @@ struct ivpu_cmdq { * @vdev: Pointer to the VPU device * @file_priv: The client context that submitted this job * @done_fence: Fence signaled when job completes + * @destroy_node: List node for deferred resource cleanup after job completion * @cmd_buf_vpu_addr: VPU address of the command buffer for this job * @cmdq_id: Command queue ID used for submission * @job_id: Unique job ID for tracking and status reporting @@ -61,6 +64,7 @@ struct ivpu_job { struct ivpu_device *vdev; struct ivpu_file_priv *file_priv; struct dma_fence *done_fence; + struct llist_node destroy_node; u64 cmd_buf_vpu_addr; u32 cmdq_id; u32 job_id; @@ -87,6 +91,7 @@ void ivpu_job_done_consumer_init(struct ivpu_device *vdev); void ivpu_job_done_consumer_fini(struct ivpu_device *vdev); bool ivpu_job_handle_engine_error(struct ivpu_device *vdev, u32 job_id, u32 job_status); void ivpu_context_abort_work_fn(struct work_struct *work); +void ivpu_job_destroy_work_fn(struct work_struct *work); void ivpu_jobs_abort_all(struct ivpu_device *vdev); diff --git a/drivers/dma-buf/dma-fence-unwrap.c b/drivers/dma-buf/dma-fence-unwrap.c index 364cbf79ad73..120fe0c2a75a 100644 --- a/drivers/dma-buf/dma-fence-unwrap.c +++ b/drivers/dma-buf/dma-fence-unwrap.c @@ -93,9 +93,9 @@ static int fence_cmp(const void *_a, const void *_b) * * Return: Number of unique fences remaining in the array. */ -int dma_fence_dedup_array(struct dma_fence **fences, int num_fences) +size_t dma_fence_dedup_array(struct dma_fence **fences, size_t num_fences) { - int i, j; + size_t i, j; if (!num_fences) return 0; @@ -118,14 +118,14 @@ int dma_fence_dedup_array(struct dma_fence **fences, int num_fences) EXPORT_SYMBOL_GPL(dma_fence_dedup_array); /* Implementation for the dma_fence_merge() marco, don't use directly */ -struct dma_fence *__dma_fence_unwrap_merge(unsigned int num_fences, +struct dma_fence *__dma_fence_unwrap_merge(size_t num_fences, struct dma_fence **fences, struct dma_fence_unwrap *iter) { struct dma_fence *tmp, *unsignaled = NULL, **array; struct dma_fence_array *result; ktime_t timestamp; - int i, count; + size_t i, count; count = 0; timestamp = ns_to_ktime(0); diff --git a/drivers/dma-buf/dma-fence.c b/drivers/dma-buf/dma-fence.c index 87797bea91cb..05090fb0fd5a 100644 --- a/drivers/dma-buf/dma-fence.c +++ b/drivers/dma-buf/dma-fence.c @@ -534,7 +534,7 @@ dma_fence_wait_timeout(struct dma_fence *fence, bool intr, signed long timeout) __dma_fence_might_wait(); - dma_fence_enable_sw_signaling(fence); + dma_fence_enable_signaling(fence); rcu_read_lock(); ops = rcu_dereference(fence->ops); @@ -656,14 +656,14 @@ static bool __dma_fence_enable_signaling(struct dma_fence *fence) } /** - * dma_fence_enable_sw_signaling - enable signaling on fence + * dma_fence_enable_signaling - enable signaling on fence * @fence: the fence to enable * * This will request for sw signaling to be enabled, to make the fence * complete as soon as possible. This calls &dma_fence_ops.enable_signaling * internally. */ -void dma_fence_enable_sw_signaling(struct dma_fence *fence) +void dma_fence_enable_signaling(struct dma_fence *fence) { unsigned long flags; @@ -671,7 +671,7 @@ void dma_fence_enable_sw_signaling(struct dma_fence *fence) __dma_fence_enable_signaling(fence); dma_fence_unlock_irqrestore(fence, flags); } -EXPORT_SYMBOL(dma_fence_enable_sw_signaling); +EXPORT_SYMBOL(dma_fence_enable_signaling); /** * dma_fence_add_callback - add a callback to be called when the fence @@ -1102,9 +1102,12 @@ __dma_fence_init(struct dma_fence *fence, const struct dma_fence_ops *ops, * context and seqno are used for easy comparison between fences, allowing * to check which fence is later by simply using dma_fence_later(). * - * It is strongly discouraged to provide an external lock because this couples - * lock and fence life time. This is only allowed for legacy use cases when - * multiple fences need to be prevented from signaling out of order. + * External locks are a relic of legacy use cases that needed a shared lock + * to serialize signaling when no out-of-order signaling was possible through + * &dma_fence_ops.signaled. Drivers have abandoned this concept since the + * introduction of the callback, but the external lock is still around. New + * users MUST NOT use external locks, as they force the issuer to outlive all + * fences that reference the lock. */ void dma_fence_init(struct dma_fence *fence, const struct dma_fence_ops *ops, @@ -1129,9 +1132,8 @@ EXPORT_SYMBOL(dma_fence_init); * Context and seqno are used for easy comparison between fences, allowing * to check which fence is later by simply using dma_fence_later(). * - * It is strongly discouraged to provide an external lock because this couples - * lock and fence life time. This is only allowed for legacy use cases when - * multiple fences need to be prevented from signaling out of order. + * New users MUST NOT use external locks. Check the documentation in + * dma_fence_init() to understand the motives behind the legacy use cases. */ void dma_fence_init64(struct dma_fence *fence, const struct dma_fence_ops *ops, diff --git a/drivers/dma-buf/dma-heap.c b/drivers/dma-buf/dma-heap.c index a76bf3f8b071..3937dd41bb0f 100644 --- a/drivers/dma-buf/dma-heap.c +++ b/drivers/dma-buf/dma-heap.c @@ -13,6 +13,7 @@ #include <linux/err.h> #include <linux/export.h> #include <linux/list.h> +#include <linux/minmax.h> #include <linux/nospec.h> #include <linux/syscalls.h> #include <linux/uaccess.h> @@ -154,7 +155,7 @@ static long dma_heap_ioctl(struct file *file, unsigned int ucmd, in_size = 0; if ((ucmd & kcmd & IOC_OUT) == 0) out_size = 0; - ksize = max(max(in_size, out_size), drv_size); + ksize = max3(in_size, out_size, drv_size); /* If necessary, allocate buffer for ioctl argument */ if (ksize > sizeof(stack_kdata)) { diff --git a/drivers/dma-buf/st-dma-fence-chain.c b/drivers/dma-buf/st-dma-fence-chain.c index a3023d3fedc9..e0d9b69bfa76 100644 --- a/drivers/dma-buf/st-dma-fence-chain.c +++ b/drivers/dma-buf/st-dma-fence-chain.c @@ -82,7 +82,7 @@ static void test_sanitycheck(struct kunit *test) chain = mock_chain(NULL, f, 1); if (chain) - dma_fence_enable_sw_signaling(chain); + dma_fence_enable_signaling(chain); else KUNIT_FAIL(test, "Failed to create chain"); @@ -139,7 +139,7 @@ static int fence_chains_init(struct fence_chains *fc, unsigned int count, fc->tail = fc->chains[i]; - dma_fence_enable_sw_signaling(fc->chains[i]); + dma_fence_enable_signaling(fc->chains[i]); } fc->chain_length = i; diff --git a/drivers/dma-buf/st-dma-fence-unwrap.c b/drivers/dma-buf/st-dma-fence-unwrap.c index 4e7ee25372ba..4d9d313b460c 100644 --- a/drivers/dma-buf/st-dma-fence-unwrap.c +++ b/drivers/dma-buf/st-dma-fence-unwrap.c @@ -103,7 +103,7 @@ static void test_sanitycheck(struct kunit *test) f = mock_fence(); KUNIT_ASSERT_NOT_NULL(test, f); - dma_fence_enable_sw_signaling(f); + dma_fence_enable_signaling(f); array = mock_array(1, f); KUNIT_ASSERT_NOT_NULL(test, array); @@ -122,7 +122,7 @@ static void test_unwrap_array(struct kunit *test) f1 = mock_fence(); KUNIT_ASSERT_NOT_NULL(test, f1); - dma_fence_enable_sw_signaling(f1); + dma_fence_enable_signaling(f1); f2 = mock_fence(); if (!f2) { @@ -131,7 +131,7 @@ static void test_unwrap_array(struct kunit *test) return; } - dma_fence_enable_sw_signaling(f2); + dma_fence_enable_signaling(f2); array = mock_array(2, f1, f2); KUNIT_ASSERT_NOT_NULL(test, array); @@ -160,7 +160,7 @@ static void test_unwrap_chain(struct kunit *test) f1 = mock_fence(); KUNIT_ASSERT_NOT_NULL(test, f1); - dma_fence_enable_sw_signaling(f1); + dma_fence_enable_signaling(f1); f2 = mock_fence(); if (!f2) { @@ -169,7 +169,7 @@ static void test_unwrap_chain(struct kunit *test) return; } - dma_fence_enable_sw_signaling(f2); + dma_fence_enable_signaling(f2); chain = mock_chain(f1, f2); KUNIT_ASSERT_NOT_NULL(test, chain); @@ -198,7 +198,7 @@ static void test_unwrap_chain_array(struct kunit *test) f1 = mock_fence(); KUNIT_ASSERT_NOT_NULL(test, f1); - dma_fence_enable_sw_signaling(f1); + dma_fence_enable_signaling(f1); f2 = mock_fence(); if (!f2) { @@ -207,7 +207,7 @@ static void test_unwrap_chain_array(struct kunit *test) return; } - dma_fence_enable_sw_signaling(f2); + dma_fence_enable_signaling(f2); array = mock_array(2, f1, f2); KUNIT_ASSERT_NOT_NULL(test, array); @@ -239,7 +239,7 @@ static void test_unwrap_merge(struct kunit *test) f1 = mock_fence(); KUNIT_ASSERT_NOT_NULL(test, f1); - dma_fence_enable_sw_signaling(f1); + dma_fence_enable_signaling(f1); f2 = mock_fence(); if (!f2) { @@ -247,7 +247,7 @@ static void test_unwrap_merge(struct kunit *test) goto error_put_f1; } - dma_fence_enable_sw_signaling(f2); + dma_fence_enable_signaling(f2); f3 = dma_fence_unwrap_merge(f1, f2); if (!f3) { @@ -285,7 +285,7 @@ static void test_unwrap_merge_duplicate(struct kunit *test) f1 = mock_fence(); KUNIT_ASSERT_NOT_NULL(test, f1); - dma_fence_enable_sw_signaling(f1); + dma_fence_enable_signaling(f1); f2 = dma_fence_unwrap_merge(f1, f1); if (!f2) { @@ -322,7 +322,7 @@ static void test_unwrap_merge_seqno(struct kunit *test) f1 = __mock_fence(ctx[1], 1); KUNIT_ASSERT_NOT_NULL(test, f1); - dma_fence_enable_sw_signaling(f1); + dma_fence_enable_signaling(f1); f2 = __mock_fence(ctx[1], 2); if (!f2) { @@ -330,7 +330,7 @@ static void test_unwrap_merge_seqno(struct kunit *test) goto error_put_f1; } - dma_fence_enable_sw_signaling(f2); + dma_fence_enable_signaling(f2); f3 = __mock_fence(ctx[0], 1); if (!f3) { @@ -338,7 +338,7 @@ static void test_unwrap_merge_seqno(struct kunit *test) goto error_put_f2; } - dma_fence_enable_sw_signaling(f3); + dma_fence_enable_signaling(f3); f4 = dma_fence_unwrap_merge(f1, f2, f3); if (!f4) { @@ -378,7 +378,7 @@ static void test_unwrap_merge_order(struct kunit *test) f1 = mock_fence(); KUNIT_ASSERT_NOT_NULL(test, f1); - dma_fence_enable_sw_signaling(f1); + dma_fence_enable_signaling(f1); f2 = mock_fence(); if (!f2) { @@ -387,7 +387,7 @@ static void test_unwrap_merge_order(struct kunit *test) return; } - dma_fence_enable_sw_signaling(f2); + dma_fence_enable_signaling(f2); a1 = mock_array(2, f1, f2); KUNIT_ASSERT_NOT_NULL(test, a1); @@ -442,7 +442,7 @@ static void test_unwrap_merge_complex(struct kunit *test) f1 = mock_fence(); KUNIT_ASSERT_NOT_NULL(test, f1); - dma_fence_enable_sw_signaling(f1); + dma_fence_enable_signaling(f1); f2 = mock_fence(); if (!f2) { @@ -450,7 +450,7 @@ static void test_unwrap_merge_complex(struct kunit *test) goto error_put_f1; } - dma_fence_enable_sw_signaling(f2); + dma_fence_enable_signaling(f2); f3 = dma_fence_unwrap_merge(f1, f2); if (!f3) { @@ -510,7 +510,7 @@ static void test_unwrap_merge_complex_seqno(struct kunit *test) f1 = __mock_fence(ctx[0], 2); KUNIT_ASSERT_NOT_NULL(test, f1); - dma_fence_enable_sw_signaling(f1); + dma_fence_enable_signaling(f1); f2 = __mock_fence(ctx[1], 1); if (!f2) { @@ -518,7 +518,7 @@ static void test_unwrap_merge_complex_seqno(struct kunit *test) goto error_put_f1; } - dma_fence_enable_sw_signaling(f2); + dma_fence_enable_signaling(f2); f3 = __mock_fence(ctx[0], 1); if (!f3) { @@ -526,7 +526,7 @@ static void test_unwrap_merge_complex_seqno(struct kunit *test) goto error_put_f2; } - dma_fence_enable_sw_signaling(f3); + dma_fence_enable_signaling(f3); f4 = __mock_fence(ctx[1], 2); if (!f4) { @@ -534,7 +534,7 @@ static void test_unwrap_merge_complex_seqno(struct kunit *test) goto error_put_f3; } - dma_fence_enable_sw_signaling(f4); + dma_fence_enable_signaling(f4); f5 = mock_array(2, dma_fence_get(f1), dma_fence_get(f2)); if (!f5) { diff --git a/drivers/dma-buf/st-dma-fence.c b/drivers/dma-buf/st-dma-fence.c index 499272229696..856d0d302a5d 100644 --- a/drivers/dma-buf/st-dma-fence.c +++ b/drivers/dma-buf/st-dma-fence.c @@ -42,7 +42,7 @@ static void test_sanitycheck(struct kunit *test) f = mock_fence(); KUNIT_ASSERT_NOT_NULL(test, f); - dma_fence_enable_sw_signaling(f); + dma_fence_enable_signaling(f); dma_fence_signal(f); dma_fence_put(f); @@ -55,7 +55,7 @@ static void test_signaling(struct kunit *test) f = mock_fence(); KUNIT_ASSERT_NOT_NULL(test, f); - dma_fence_enable_sw_signaling(f); + dma_fence_enable_signaling(f); if (dma_fence_is_signaled(f)) { KUNIT_FAIL(test, "Fence unexpectedly signaled on creation"); @@ -127,7 +127,7 @@ static void test_late_add_callback(struct kunit *test) f = mock_fence(); KUNIT_ASSERT_NOT_NULL(test, f); - dma_fence_enable_sw_signaling(f); + dma_fence_enable_signaling(f); dma_fence_signal(f); @@ -209,7 +209,7 @@ static void test_status(struct kunit *test) f = mock_fence(); KUNIT_ASSERT_NOT_NULL(test, f); - dma_fence_enable_sw_signaling(f); + dma_fence_enable_signaling(f); if (dma_fence_get_status(f)) { KUNIT_FAIL(test, "Fence unexpectedly has signaled status on creation"); @@ -233,7 +233,7 @@ static void test_error(struct kunit *test) f = mock_fence(); KUNIT_ASSERT_NOT_NULL(test, f); - dma_fence_enable_sw_signaling(f); + dma_fence_enable_signaling(f); dma_fence_set_error(f, -EIO); @@ -260,7 +260,7 @@ static void test_wait(struct kunit *test) f = mock_fence(); KUNIT_ASSERT_NOT_NULL(test, f); - dma_fence_enable_sw_signaling(f); + dma_fence_enable_signaling(f); if (dma_fence_wait_timeout(f, false, 0) != 0) { KUNIT_FAIL(test, "Wait reported complete before being signaled"); @@ -300,7 +300,7 @@ static void test_wait_timeout(struct kunit *test) wt.f = mock_fence(); KUNIT_ASSERT_NOT_NULL(test, wt.f); - dma_fence_enable_sw_signaling(wt.f); + dma_fence_enable_signaling(wt.f); if (dma_fence_wait_timeout(wt.f, false, 1) != 0) { KUNIT_FAIL(test, "Wait reported complete before being signaled"); @@ -379,7 +379,7 @@ static int thread_signal_callback(void *arg) break; } - dma_fence_enable_sw_signaling(f1); + dma_fence_enable_signaling(f1); rcu_assign_pointer(t->fences[t->id], f1); smp_wmb(); diff --git a/drivers/dma-buf/st-dma-resv.c b/drivers/dma-buf/st-dma-resv.c index 95a4becdb892..0b96136bbd54 100644 --- a/drivers/dma-buf/st-dma-resv.c +++ b/drivers/dma-buf/st-dma-resv.c @@ -48,7 +48,7 @@ static void test_sanitycheck(struct kunit *test) f = alloc_fence(); KUNIT_ASSERT_NOT_NULL(test, f); - dma_fence_enable_sw_signaling(f); + dma_fence_enable_signaling(f); dma_fence_signal(f); dma_fence_put(f); @@ -73,7 +73,7 @@ static void test_signaling(struct kunit *test) f = alloc_fence(); KUNIT_ASSERT_NOT_NULL(test, f); - dma_fence_enable_sw_signaling(f); + dma_fence_enable_signaling(f); dma_resv_init(&resv); r = dma_resv_lock(&resv, NULL); @@ -117,7 +117,7 @@ static void test_for_each(struct kunit *test) f = alloc_fence(); KUNIT_ASSERT_NOT_NULL(test, f); - dma_fence_enable_sw_signaling(f); + dma_fence_enable_signaling(f); dma_resv_init(&resv); r = dma_resv_lock(&resv, NULL); @@ -176,7 +176,7 @@ static void test_for_each_unlocked(struct kunit *test) f = alloc_fence(); KUNIT_ASSERT_NOT_NULL(test, f); - dma_fence_enable_sw_signaling(f); + dma_fence_enable_signaling(f); dma_resv_init(&resv); r = dma_resv_lock(&resv, NULL); @@ -246,7 +246,7 @@ static void test_get_fences(struct kunit *test) f = alloc_fence(); KUNIT_ASSERT_NOT_NULL(test, f); - dma_fence_enable_sw_signaling(f); + dma_fence_enable_signaling(f); dma_resv_init(&resv); r = dma_resv_lock(&resv, NULL); diff --git a/drivers/firmware/Kconfig b/drivers/firmware/Kconfig index 12dc70254842..544438a03f7b 100644 --- a/drivers/firmware/Kconfig +++ b/drivers/firmware/Kconfig @@ -187,32 +187,20 @@ config SYSFB select SCREEN_INFO config SYSFB_SIMPLEFB - bool "Mark VGA/VBE/EFI FB as generic system framebuffer" + bool "Mark VGA/VBE/EFI FB as generic system framebuffer (deprecated)" depends on X86 || EFI select SYSFB help - Firmwares often provide initial graphics framebuffers so the BIOS, + Firmware often provides initial graphics framebuffers so the BIOS, bootloader or kernel can show basic video-output during boot for - user-guidance and debugging. Historically, x86 used the VESA BIOS - Extensions and EFI-framebuffers for this, which are mostly limited - to x86 BIOS or EFI systems. - This option, if enabled, marks VGA/VBE/EFI framebuffers as generic - framebuffers so the new generic system-framebuffer drivers can be - used instead. If the framebuffer is not compatible with the generic - modes, it is advertised as fallback platform framebuffer so legacy - drivers like efifb, vesafb and uvesafb can pick it up. - If this option is not selected, all system framebuffers are always - marked as fallback platform framebuffers as usual. - - Note: Legacy fbdev drivers, including vesafb, efifb, uvesafb, will - not be able to pick up generic system framebuffers if this option - is selected. You are highly encouraged to enable simplefb as - replacement if you select this option. simplefb can correctly deal - with generic system framebuffers. But you should still keep vesafb - and others enabled as fallback if a system framebuffer is - incompatible with simplefb. - - If unsure, say Y. + user-guidance and debugging. + + This option, if enabled, marks VBE/EFI framebuffers as system + framebuffers so the generic simpledrm driver can be used. + + This option is deprecated and will be removed in the near future. If + unsure, say N and select efidrm, vesadrm instead. The dedicated DRM + drivers provide the same functionality plus additional features. config TH1520_AON_PROTOCOL tristate "Always-On firmware protocol" diff --git a/drivers/gpu/buddy.c b/drivers/gpu/buddy.c index eb1457376307..dc81fe0301ce 100644 --- a/drivers/gpu/buddy.c +++ b/drivers/gpu/buddy.c @@ -193,6 +193,9 @@ static void mark_allocated(struct gpu_buddy *mm, block->header &= ~GPU_BUDDY_HEADER_STATE; block->header |= GPU_BUDDY_ALLOCATED; + mm->free_scoreboard[gpu_buddy_block_order(block)]--; + mm->used_scoreboard[gpu_buddy_block_order(block)]++; + rbtree_remove(mm, block); } @@ -201,9 +204,14 @@ static void mark_free(struct gpu_buddy *mm, { enum gpu_buddy_free_tree tree; + if (gpu_buddy_block_is_allocated(block)) + mm->used_scoreboard[gpu_buddy_block_order(block)]--; + block->header &= ~GPU_BUDDY_HEADER_STATE; block->header |= GPU_BUDDY_FREE; + mm->free_scoreboard[gpu_buddy_block_order(block)]++; + tree = get_block_tree(block); rbtree_insert(mm, block, tree); } @@ -214,6 +222,8 @@ static void mark_split(struct gpu_buddy *mm, block->header &= ~GPU_BUDDY_HEADER_STATE; block->header |= GPU_BUDDY_SPLIT; + mm->free_scoreboard[gpu_buddy_block_order(block)]--; + rbtree_remove(mm, block); } @@ -271,9 +281,13 @@ static unsigned int __gpu_buddy_free(struct gpu_buddy *mm, } rbtree_remove(mm, buddy); + mm->free_scoreboard[gpu_buddy_block_order(buddy)]--; if (force_merge && gpu_buddy_block_is_clear(buddy)) mm->clear_avail -= gpu_buddy_block_size(mm, buddy); + if (gpu_buddy_block_is_allocated(block)) + mm->used_scoreboard[gpu_buddy_block_order(block)]--; + gpu_block_free(mm, block); gpu_block_free(mm, buddy); @@ -335,6 +349,7 @@ static int __force_merge(struct gpu_buddy *mm, iter = rb_prev(iter); rbtree_remove(mm, block); + mm->free_scoreboard[gpu_buddy_block_order(block)]--; if (gpu_buddy_block_is_clear(block)) mm->clear_avail -= gpu_buddy_block_size(mm, block); @@ -384,11 +399,23 @@ int gpu_buddy_init(struct gpu_buddy *mm, u64 size, u64 chunk_size) BUG_ON(mm->max_order > GPU_BUDDY_MAX_ORDER); + mm->free_scoreboard = kcalloc(mm->max_order + 1, + sizeof(*mm->free_scoreboard), + GFP_KERNEL); + if (!mm->free_scoreboard) + return -ENOMEM; + + mm->used_scoreboard = kcalloc(mm->max_order + 1, + sizeof(*mm->used_scoreboard), + GFP_KERNEL); + if (!mm->used_scoreboard) + goto out_free_free_scoreboard; + mm->free_trees = kmalloc_array(GPU_BUDDY_MAX_FREE_TREES, sizeof(*mm->free_trees), GFP_KERNEL); if (!mm->free_trees) - return -ENOMEM; + goto out_free_used_scoreboard; for_each_free_tree(i) { mm->free_trees[i] = kmalloc_array(mm->max_order + 1, @@ -450,6 +477,10 @@ out_free_tree: while (i--) kfree(mm->free_trees[i]); kfree(mm->free_trees); +out_free_used_scoreboard: + kfree(mm->used_scoreboard); +out_free_free_scoreboard: + kfree(mm->free_scoreboard); return -ENOMEM; } EXPORT_SYMBOL(gpu_buddy_init); @@ -484,10 +515,15 @@ void gpu_buddy_fini(struct gpu_buddy *mm) gpu_buddy_assert(mm->avail == mm->size); + for (i = 0; i <= mm->max_order; ++i) + gpu_buddy_assert(!mm->used_scoreboard[i]); + for_each_free_tree(i) kfree(mm->free_trees[i]); kfree(mm->free_trees); kfree(mm->roots); + kfree(mm->free_scoreboard); + kfree(mm->used_scoreboard); } EXPORT_SYMBOL(gpu_buddy_fini); @@ -650,6 +686,20 @@ static bool block_incompatible(struct gpu_buddy_block *block, unsigned int flags return needs_clear != gpu_buddy_block_is_clear(block); } +static void __gpu_buddy_undo_splits(struct gpu_buddy *mm, + struct gpu_buddy_block *block) +{ + struct gpu_buddy_block *buddy = __get_buddy(block); + + if (buddy && + (gpu_buddy_block_is_free(block) && + gpu_buddy_block_is_free(buddy))) { + rbtree_remove(mm, block); + mm->free_scoreboard[gpu_buddy_block_order(block)]--; + __gpu_buddy_free(mm, block, false); + } +} + static struct gpu_buddy_block * __alloc_range_bias(struct gpu_buddy *mm, u64 start, u64 end, @@ -659,7 +709,6 @@ __alloc_range_bias(struct gpu_buddy *mm, { u64 req_size = mm->chunk_size << order; struct gpu_buddy_block *block; - struct gpu_buddy_block *buddy; LIST_HEAD(dfs); int err; int i; @@ -734,11 +783,7 @@ err_undo: * bigger is better, so make sure we merge everything back before we * free the allocated blocks. */ - buddy = __get_buddy(block); - if (buddy && - (gpu_buddy_block_is_free(block) && - gpu_buddy_block_is_free(buddy))) - __gpu_buddy_free(mm, block, false); + __gpu_buddy_undo_splits(mm, block); return ERR_PTR(err); } @@ -847,8 +892,7 @@ alloc_from_freetree(struct gpu_buddy *mm, return block; err_undo: - if (tmp != order) - __gpu_buddy_free(mm, block, false); + __gpu_buddy_undo_splits(mm, block); return ERR_PTR(err); } @@ -912,7 +956,6 @@ gpu_buddy_offset_aligned_allocation(struct gpu_buddy *mm, { struct gpu_buddy_block *block = NULL; unsigned int order, tmp, alignment; - struct gpu_buddy_block *buddy; enum gpu_buddy_free_tree tree; unsigned long pages; int err; @@ -965,11 +1008,7 @@ err_undo: * bigger is better, so make sure we merge everything back before we * free the allocated blocks. */ - buddy = __get_buddy(block); - if (buddy && - (gpu_buddy_block_is_free(block) && - gpu_buddy_block_is_free(buddy))) - __gpu_buddy_free(mm, block, false); + __gpu_buddy_undo_splits(mm, block); return ERR_PTR(err); } @@ -980,7 +1019,6 @@ static int __alloc_range(struct gpu_buddy *mm, u64 *total_allocated_on_err) { struct gpu_buddy_block *block; - struct gpu_buddy_block *buddy; u64 total_allocated = 0; LIST_HEAD(allocated); u64 end; @@ -1051,11 +1089,7 @@ err_undo: * bigger is better, so make sure we merge everything back before we * free the allocated blocks. */ - buddy = __get_buddy(block); - if (buddy && - (gpu_buddy_block_is_free(block) && - gpu_buddy_block_is_free(buddy))) - __gpu_buddy_free(mm, block, false); + __gpu_buddy_undo_splits(mm, block); err_free: if (err == -ENOSPC && total_allocated_on_err) { @@ -1490,27 +1524,18 @@ void gpu_buddy_print(struct gpu_buddy *mm) mm->chunk_size >> 10, mm->size >> 20, mm->avail >> 20, mm->clear_avail >> 20); for (order = mm->max_order; order >= 0; order--) { - struct gpu_buddy_block *block, *tmp; - struct rb_root *root; - u64 count = 0, free; - unsigned int tree; - - for_each_free_tree(tree) { - root = &mm->free_trees[tree][order]; - - rbtree_postorder_for_each_entry_safe(block, tmp, root, rb) { - BUG_ON(!gpu_buddy_block_is_free(block)); - count++; - } - } - - free = count * (mm->chunk_size << order); - if (free < SZ_1M) - pr_info("order-%2d free: %8llu KiB, blocks: %llu\n", - order, free >> 10, count); + u64 free_count = mm->free_scoreboard[order]; + u64 used_count = mm->used_scoreboard[order]; + u64 block_size = mm->chunk_size << order; + u64 free = free_count * block_size; + u64 used = used_count * block_size; + + if (block_size < SZ_1M) + pr_info("order-%2d free: %8llu KiB, used: %8llu KiB, free_blocks: %llu, used_blocks: %llu\n", + order, free >> 10, used >> 10, free_count, used_count); else - pr_info("order-%2d free: %8llu MiB, blocks: %llu\n", - order, free >> 20, count); + pr_info("order-%2d free: %8llu MiB, used: %8llu MiB, free_blocks: %llu, used_blocks: %llu\n", + order, free >> 20, used >> 20, free_count, used_count); } } EXPORT_SYMBOL(gpu_buddy_print); diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c index 18145d78334f..97ae3e32e4a4 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c @@ -4018,11 +4018,13 @@ static void dm_set_panel_type(struct amdgpu_dm_connector *aconnector) link->panel_type = PANEL_TYPE_OLED; } - /* - * TODO: get panel type from DID2 that has device technology field - * to specify if it's OLED or not. But we need to wait for DID2 - * support in DC and EDID parser to be able to use it here. - */ + /* If VSDB and DPCD didn't determine panel type, check DID */ + if (link->panel_type == PANEL_TYPE_NONE) { + if (display_info->panel_type == DRM_MODE_PANEL_TYPE_LCD) + link->panel_type = PANEL_TYPE_LCD; + else if (display_info->panel_type == DRM_MODE_PANEL_TYPE_OLED) + link->panel_type = PANEL_TYPE_OLED; + } if (link->panel_type == PANEL_TYPE_NONE) { struct drm_amd_vsdb_info *vsdb = &display_info->amd_vsdb; @@ -4040,6 +4042,10 @@ static void dm_set_panel_type(struct amdgpu_dm_connector *aconnector) drm_object_property_set_value(&connector->base, adev_to_drm(adev)->mode_config.panel_type_property, DRM_MODE_PANEL_TYPE_OLED); + else if (link->panel_type == PANEL_TYPE_LCD) + drm_object_property_set_value(&connector->base, + adev_to_drm(adev)->mode_config.panel_type_property, + DRM_MODE_PANEL_TYPE_LCD); else drm_object_property_set_value(&connector->base, adev_to_drm(adev)->mode_config.panel_type_property, @@ -6964,11 +6970,14 @@ static void fill_stream_properties_from_drm_display_mode( const struct dc_stream_state *old_stream, int requested_bpc) { + bool is_dp_or_hdmi = dc_is_hdmi_signal(stream->signal) || dc_is_dp_signal(stream->signal); struct dc_crtc_timing *timing_out = &stream->timing; const struct drm_display_info *info = &connector->display_info; struct amdgpu_dm_connector *aconnector = NULL; struct hdmi_vendor_infoframe hv_frame; struct hdmi_avi_infoframe avi_frame; + bool want_420; + bool want_422; ssize_t err; if (connector->connector_type != DRM_MODE_CONNECTOR_WRITEBACK) @@ -6981,31 +6990,41 @@ static void fill_stream_properties_from_drm_display_mode( timing_out->h_border_right = 0; timing_out->v_border_top = 0; timing_out->v_border_bottom = 0; - /* TODO: un-hardcode */ - if (drm_mode_is_420_only(info, mode_in) - && (stream->signal == SIGNAL_TYPE_HDMI_TYPE_A || - stream->signal == SIGNAL_TYPE_HDMI_FRL) - && aconnector - && aconnector->force_yuv_pixel_format == PIXEL_ENCODING_YCBCR420) + + want_420 = (aconnector && aconnector->force_yuv_pixel_format == PIXEL_ENCODING_YCBCR420) || + (connector_state->color_format == DRM_CONNECTOR_COLOR_FORMAT_YCBCR420); + want_422 = (aconnector && aconnector->force_yuv_pixel_format == PIXEL_ENCODING_YCBCR422) || + (connector_state->color_format == DRM_CONNECTOR_COLOR_FORMAT_YCBCR422); + + if (drm_mode_is_420_only(info, mode_in) && + (want_420 || connector_state->color_format == DRM_CONNECTOR_COLOR_FORMAT_AUTO)) { timing_out->pixel_encoding = PIXEL_ENCODING_YCBCR420; - else if (drm_mode_is_420_also(info, mode_in) - && aconnector - && (aconnector->force_yuv_pixel_format == PIXEL_ENCODING_YCBCR420 - || aconnector->force_yuv420_output)) + } else if (drm_mode_is_420_also(info, mode_in) && want_420) { timing_out->pixel_encoding = PIXEL_ENCODING_YCBCR420; - else if ((connector->display_info.color_formats & BIT(DRM_OUTPUT_COLOR_FORMAT_YCBCR422)) - && aconnector - && (aconnector->force_yuv_pixel_format == PIXEL_ENCODING_YCBCR422 - || aconnector->force_yuv422_output)) + } else if ((info->color_formats & BIT(DRM_OUTPUT_COLOR_FORMAT_YCBCR422)) && + want_422 && is_dp_or_hdmi) { timing_out->pixel_encoding = PIXEL_ENCODING_YCBCR422; - else if ((connector->display_info.color_formats & BIT(DRM_OUTPUT_COLOR_FORMAT_YCBCR444)) - && (stream->signal == SIGNAL_TYPE_HDMI_TYPE_A || - stream->signal == SIGNAL_TYPE_HDMI_FRL) - && aconnector - && aconnector->force_yuv_pixel_format == PIXEL_ENCODING_YCBCR444) + } else if (connector_state->color_format == DRM_CONNECTOR_COLOR_FORMAT_YCBCR444 && + (info->color_formats & BIT(DRM_OUTPUT_COLOR_FORMAT_YCBCR444)) && + is_dp_or_hdmi) { timing_out->pixel_encoding = PIXEL_ENCODING_YCBCR444; - else + } else if (connector_state->color_format == DRM_CONNECTOR_COLOR_FORMAT_RGB444 || + connector_state->color_format == DRM_CONNECTOR_COLOR_FORMAT_AUTO) { timing_out->pixel_encoding = PIXEL_ENCODING_RGB; + } else { + /* + * If a format was explicitly requested but the requested format + * can't be satisfied, set it to an invalid value so that an + * error bubbles up to userspace. This way, userspace knows it + * needs to make a better choice. + */ + if (connector_state->color_format != DRM_CONNECTOR_COLOR_FORMAT_AUTO) + timing_out->pixel_encoding = PIXEL_ENCODING_UNDEFINED; + else if (drm_mode_is_420_only(info, mode_in)) + timing_out->pixel_encoding = PIXEL_ENCODING_YCBCR420; + else + timing_out->pixel_encoding = PIXEL_ENCODING_RGB; + } timing_out->timing_3d_format = TIMING_3D_FORMAT_NONE; timing_out->display_color_depth = convert_color_depth_from_display_info( @@ -8419,6 +8438,38 @@ cleanup: return dc_result; } +static enum dc_status +dm_validate_stream_color_format(const struct drm_connector_state *drm_state, + const struct dc_stream_state *stream) +{ + enum dc_pixel_encoding encoding; + + if (!drm_state->color_format) + return DC_OK; + + switch (drm_state->color_format) { + case DRM_CONNECTOR_COLOR_FORMAT_AUTO: + case DRM_CONNECTOR_COLOR_FORMAT_RGB444: + encoding = PIXEL_ENCODING_RGB; + break; + case DRM_CONNECTOR_COLOR_FORMAT_YCBCR444: + encoding = PIXEL_ENCODING_YCBCR444; + break; + case DRM_CONNECTOR_COLOR_FORMAT_YCBCR422: + encoding = PIXEL_ENCODING_YCBCR422; + break; + case DRM_CONNECTOR_COLOR_FORMAT_YCBCR420: + encoding = PIXEL_ENCODING_YCBCR420; + break; + default: + encoding = PIXEL_ENCODING_UNDEFINED; + break; + } + + return encoding == stream->timing.pixel_encoding ? + DC_OK : DC_UNSUPPORTED_VALUE; +} + struct dc_stream_state * create_validate_stream_for_sink(struct drm_connector *connector, const struct drm_display_mode *drm_mode, @@ -8466,6 +8517,9 @@ create_validate_stream_for_sink(struct drm_connector *connector, if (dc_result == DC_OK) dc_result = dm_validate_stream_and_context(adev->dm.dc, stream); + if (dc_result == DC_OK) + dc_result = dm_validate_stream_color_format(drm_state, stream); + if (dc_result != DC_OK) { drm_dbg_kms(connector->dev, "Pruned mode %d x %d (clk %d) %s %s -- %s\n", drm_mode->hdisplay, @@ -9295,6 +9349,12 @@ static const u32 supported_colorspaces = BIT(DRM_MODE_COLORIMETRY_BT2020_RGB) | BIT(DRM_MODE_COLORIMETRY_BT2020_YCC); +static const u32 supported_colorformats = + BIT(DRM_OUTPUT_COLOR_FORMAT_RGB444) | + BIT(DRM_OUTPUT_COLOR_FORMAT_YCBCR444) | + BIT(DRM_OUTPUT_COLOR_FORMAT_YCBCR422) | + BIT(DRM_OUTPUT_COLOR_FORMAT_YCBCR420); + void amdgpu_dm_connector_init_helper(struct amdgpu_display_manager *dm, struct amdgpu_dm_connector *aconnector, int connector_type, @@ -9411,8 +9471,11 @@ void amdgpu_dm_connector_init_helper(struct amdgpu_display_manager *dm, connector_type == DRM_MODE_CONNECTOR_eDP) { drm_connector_attach_hdr_output_metadata_property(&aconnector->base); - if (!aconnector->mst_root) + if (!aconnector->mst_root) { drm_connector_attach_vrr_capable_property(&aconnector->base); + drm_connector_attach_color_format_property(&aconnector->base, + supported_colorformats); + } if (adev->dm.hdcp_workqueue) drm_connector_attach_content_protection_property(&aconnector->base, true); diff --git a/drivers/gpu/drm/bridge/adv7511/adv7511_drv.c b/drivers/gpu/drm/bridge/adv7511/adv7511_drv.c index 02f8f7e78a16..db99e252aeeb 100644 --- a/drivers/gpu/drm/bridge/adv7511/adv7511_drv.c +++ b/drivers/gpu/drm/bridge/adv7511/adv7511_drv.c @@ -1004,7 +1004,7 @@ static const struct drm_bridge_funcs adv7511_bridge_funcs = { .atomic_disable = adv7511_bridge_atomic_disable, .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_create_state = drm_atomic_helper_bridge_create_state, .hdmi_tmds_char_rate_valid = adv7511_bridge_hdmi_tmds_char_rate_valid, .hdmi_clear_audio_infoframe = adv7511_bridge_hdmi_clear_audio_infoframe, diff --git a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c index 8cf6b73bceac..7a85774aaac1 100644 --- a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c +++ b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c @@ -619,7 +619,7 @@ static int analogix_dp_config_video(struct analogix_dp_device *dp) for (;;) { timeout_loop++; - if (analogix_dp_is_slave_video_stream_clock_on(dp) == 0) + if (analogix_dp_is_slave_video_stream_clock_on(dp)) break; if (timeout_loop > DP_TIMEOUT_LOOP_COUNT) { dev_err(dp->dev, "Timeout of slave video streamclk ok\n"); @@ -647,7 +647,7 @@ static int analogix_dp_config_video(struct analogix_dp_device *dp) for (;;) { timeout_loop++; - if (analogix_dp_is_video_stream_on(dp) == 0) { + if (analogix_dp_is_video_stream_on(dp)) { done_count++; if (done_count > 10) break; @@ -870,7 +870,7 @@ static int analogix_dp_bridge_atomic_check(struct drm_bridge *bridge, struct drm_display_info *di = &conn_state->connector->display_info; u32 mask = BIT(DRM_OUTPUT_COLOR_FORMAT_YCBCR444) | BIT(DRM_OUTPUT_COLOR_FORMAT_YCBCR422); - if (is_rockchip(dp->plat_data->dev_type)) { + if (analogix_dp_is_rockchip(dp->plat_data->dev_type)) { if ((di->color_formats & mask)) { DRM_DEBUG_KMS("Swapping display color format from YUV to RGB\n"); di->color_formats &= ~mask; @@ -1223,7 +1223,7 @@ static void analogix_dp_bridge_atomic_post_disable(struct drm_bridge *bridge, static const struct drm_bridge_funcs analogix_dp_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_create_state = drm_atomic_helper_bridge_create_state, .atomic_pre_enable = analogix_dp_bridge_atomic_pre_enable, .atomic_enable = analogix_dp_bridge_atomic_enable, .atomic_disable = analogix_dp_bridge_atomic_disable, @@ -1249,6 +1249,7 @@ static int analogix_dp_dt_parse_pdata(struct analogix_dp_device *dp) video_info->max_link_rate = 0x0A; video_info->max_lane_count = 0x04; break; + case RK3576_EDP: case RK3588_EDP: video_info->max_link_rate = 0x14; video_info->max_lane_count = 0x04; diff --git a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.h b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.h index 17347448c6b0..94348c4e3623 100644 --- a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.h +++ b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.h @@ -211,7 +211,7 @@ void analogix_dp_reset_macro(struct analogix_dp_device *dp); void analogix_dp_init_video(struct analogix_dp_device *dp); void analogix_dp_set_video_color_format(struct analogix_dp_device *dp); -int analogix_dp_is_slave_video_stream_clock_on(struct analogix_dp_device *dp); +bool analogix_dp_is_slave_video_stream_clock_on(struct analogix_dp_device *dp); void analogix_dp_set_video_cr_mn(struct analogix_dp_device *dp, enum clock_recovery_m_value_type type, u32 m_value, @@ -220,7 +220,7 @@ void analogix_dp_set_video_timing_mode(struct analogix_dp_device *dp, u32 type); void analogix_dp_enable_video_master(struct analogix_dp_device *dp, bool enable); void analogix_dp_start_video(struct analogix_dp_device *dp); -int analogix_dp_is_video_stream_on(struct analogix_dp_device *dp); +bool analogix_dp_is_video_stream_on(struct analogix_dp_device *dp); void analogix_dp_config_video_slave_mode(struct analogix_dp_device *dp); void analogix_dp_enable_scrambling(struct analogix_dp_device *dp); void analogix_dp_disable_scrambling(struct analogix_dp_device *dp); diff --git a/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.c b/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.c index 38fd8d5014d2..ea8401293a23 100644 --- a/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.c +++ b/drivers/gpu/drm/bridge/analogix/analogix_dp_reg.c @@ -72,7 +72,7 @@ void analogix_dp_init_analog_param(struct analogix_dp_device *dp) reg = SEL_24M | TX_DVDD_BIT_1_0625V; writel(reg, dp->reg_base + ANALOGIX_DP_ANALOG_CTL_2); - if (dp->plat_data && is_rockchip(dp->plat_data->dev_type)) { + if (dp->plat_data && analogix_dp_is_rockchip(dp->plat_data->dev_type)) { reg = REF_CLK_24M; if (dp->plat_data->dev_type == RK3288_DP) reg ^= REF_CLK_MASK; @@ -123,7 +123,7 @@ void analogix_dp_reset(struct analogix_dp_device *dp) analogix_dp_stop_video(dp); analogix_dp_enable_video_mute(dp, 0); - if (dp->plat_data && is_rockchip(dp->plat_data->dev_type)) + if (dp->plat_data && analogix_dp_is_rockchip(dp->plat_data->dev_type)) reg = RK_VID_CAP_FUNC_EN_N | RK_VID_FIFO_FUNC_EN_N | SW_FUNC_EN_N; else @@ -233,7 +233,7 @@ void analogix_dp_set_pll_power_down(struct analogix_dp_device *dp, bool enable) u32 mask = DP_PLL_PD; u32 pd_addr = ANALOGIX_DP_PLL_CTL; - if (dp->plat_data && is_rockchip(dp->plat_data->dev_type)) { + if (dp->plat_data && analogix_dp_is_rockchip(dp->plat_data->dev_type)) { pd_addr = ANALOGIX_DP_PD; mask = RK_PLL_PD; } @@ -254,12 +254,12 @@ void analogix_dp_set_analog_power_down(struct analogix_dp_device *dp, u32 phy_pd_addr = ANALOGIX_DP_PHY_PD; u32 mask; - if (dp->plat_data && is_rockchip(dp->plat_data->dev_type)) + if (dp->plat_data && analogix_dp_is_rockchip(dp->plat_data->dev_type)) phy_pd_addr = ANALOGIX_DP_PD; switch (block) { case AUX_BLOCK: - if (dp->plat_data && is_rockchip(dp->plat_data->dev_type)) + if (dp->plat_data && analogix_dp_is_rockchip(dp->plat_data->dev_type)) mask = RK_AUX_PD; else mask = AUX_PD; @@ -317,7 +317,7 @@ void analogix_dp_set_analog_power_down(struct analogix_dp_device *dp, * to power off everything instead of DP_PHY_PD in * Rockchip */ - if (dp->plat_data && is_rockchip(dp->plat_data->dev_type)) + if (dp->plat_data && analogix_dp_is_rockchip(dp->plat_data->dev_type)) mask = DP_INC_BG; else mask = DP_PHY_PD; @@ -329,7 +329,7 @@ void analogix_dp_set_analog_power_down(struct analogix_dp_device *dp, reg &= ~mask; writel(reg, dp->reg_base + phy_pd_addr); - if (dp->plat_data && is_rockchip(dp->plat_data->dev_type)) + if (dp->plat_data && analogix_dp_is_rockchip(dp->plat_data->dev_type)) usleep_range(10, 15); break; case POWER_ALL: @@ -465,7 +465,7 @@ void analogix_dp_init_aux(struct analogix_dp_device *dp) analogix_dp_reset_aux(dp); /* AUX_BIT_PERIOD_EXPECTED_DELAY doesn't apply to Rockchip IP */ - if (dp->plat_data && is_rockchip(dp->plat_data->dev_type)) + if (dp->plat_data && analogix_dp_is_rockchip(dp->plat_data->dev_type)) reg = 0; else reg = AUX_BIT_PERIOD_EXPECTED_DELAY(3); @@ -713,7 +713,7 @@ void analogix_dp_set_video_color_format(struct analogix_dp_device *dp) writel(reg, dp->reg_base + ANALOGIX_DP_VIDEO_CTL_3); } -int analogix_dp_is_slave_video_stream_clock_on(struct analogix_dp_device *dp) +bool analogix_dp_is_slave_video_stream_clock_on(struct analogix_dp_device *dp) { u32 reg; @@ -724,7 +724,7 @@ int analogix_dp_is_slave_video_stream_clock_on(struct analogix_dp_device *dp) if (!(reg & DET_STA)) { dev_dbg(dp->dev, "Input stream clock not detected.\n"); - return -EINVAL; + return false; } reg = readl(dp->reg_base + ANALOGIX_DP_SYS_CTL_2); @@ -735,10 +735,10 @@ int analogix_dp_is_slave_video_stream_clock_on(struct analogix_dp_device *dp) if (reg & CHA_STA) { dev_dbg(dp->dev, "Input stream clk is changing\n"); - return -EINVAL; + return false; } - return 0; + return true; } void analogix_dp_set_video_cr_mn(struct analogix_dp_device *dp, @@ -816,7 +816,7 @@ void analogix_dp_start_video(struct analogix_dp_device *dp) writel(reg, dp->reg_base + ANALOGIX_DP_VIDEO_CTL_1); } -int analogix_dp_is_video_stream_on(struct analogix_dp_device *dp) +bool analogix_dp_is_video_stream_on(struct analogix_dp_device *dp) { u32 reg; @@ -826,10 +826,10 @@ int analogix_dp_is_video_stream_on(struct analogix_dp_device *dp) reg = readl(dp->reg_base + ANALOGIX_DP_SYS_CTL_3); if (!(reg & STRM_VALID)) { dev_dbg(dp->dev, "Input video stream is not detected.\n"); - return -EINVAL; + return false; } - return 0; + return true; } void analogix_dp_config_video_slave_mode(struct analogix_dp_device *dp) @@ -837,7 +837,7 @@ void analogix_dp_config_video_slave_mode(struct analogix_dp_device *dp) u32 reg; reg = readl(dp->reg_base + ANALOGIX_DP_FUNC_EN_1); - if (dp->plat_data && is_rockchip(dp->plat_data->dev_type)) { + if (dp->plat_data && analogix_dp_is_rockchip(dp->plat_data->dev_type)) { reg &= ~(RK_VID_CAP_FUNC_EN_N | RK_VID_FIFO_FUNC_EN_N); } else { reg &= ~(MASTER_VID_FUNC_EN_N | SLAVE_VID_FUNC_EN_N); diff --git a/drivers/gpu/drm/bridge/analogix/anx7625.c b/drivers/gpu/drm/bridge/analogix/anx7625.c index fffcd6154c71..ebd34657908e 100644 --- a/drivers/gpu/drm/bridge/analogix/anx7625.c +++ b/drivers/gpu/drm/bridge/analogix/anx7625.c @@ -2679,7 +2679,7 @@ static const struct drm_bridge_funcs anx7625_bridge_funcs = { .atomic_disable = anx7625_bridge_atomic_disable, .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_create_state = drm_atomic_helper_bridge_create_state, .detect = anx7625_bridge_detect, .edid_read = anx7625_bridge_edid_read, .hpd_enable = anx7625_bridge_hpd_enable, diff --git a/drivers/gpu/drm/bridge/cadence/cdns-dsi-core.c b/drivers/gpu/drm/bridge/cadence/cdns-dsi-core.c index cf90d4468b5c..c3e466ded84a 100644 --- a/drivers/gpu/drm/bridge/cadence/cdns-dsi-core.c +++ b/drivers/gpu/drm/bridge/cadence/cdns-dsi-core.c @@ -1009,16 +1009,15 @@ cdns_dsi_bridge_atomic_destroy_state(struct drm_bridge *bridge, } static struct drm_bridge_state * -cdns_dsi_bridge_atomic_reset(struct drm_bridge *bridge) +cdns_dsi_bridge_atomic_create_state(struct drm_bridge *bridge) { struct cdns_dsi_bridge_state *dsi_state; dsi_state = kzalloc_obj(*dsi_state); if (!dsi_state) - return NULL; + return ERR_PTR(-ENOMEM); - memset(dsi_state, 0, sizeof(*dsi_state)); - dsi_state->base.bridge = bridge; + __drm_atomic_helper_bridge_state_init(&dsi_state->base, bridge); return &dsi_state->base; } @@ -1029,7 +1028,7 @@ static const struct drm_bridge_funcs cdns_dsi_bridge_funcs = { .atomic_pre_enable = cdns_dsi_bridge_atomic_pre_enable, .atomic_post_disable = cdns_dsi_bridge_atomic_post_disable, .atomic_check = cdns_dsi_bridge_atomic_check, - .atomic_reset = cdns_dsi_bridge_atomic_reset, + .atomic_create_state = cdns_dsi_bridge_atomic_create_state, .atomic_duplicate_state = cdns_dsi_bridge_atomic_duplicate_state, .atomic_destroy_state = cdns_dsi_bridge_atomic_destroy_state, .atomic_get_input_bus_fmts = cdns_dsi_bridge_get_input_bus_fmts, diff --git a/drivers/gpu/drm/bridge/cadence/cdns-mhdp8546-core.c b/drivers/gpu/drm/bridge/cadence/cdns-mhdp8546-core.c index 36c07b71fe04..504a3186ebb3 100644 --- a/drivers/gpu/drm/bridge/cadence/cdns-mhdp8546-core.c +++ b/drivers/gpu/drm/bridge/cadence/cdns-mhdp8546-core.c @@ -1921,15 +1921,15 @@ cdns_mhdp_bridge_atomic_destroy_state(struct drm_bridge *bridge, } static struct drm_bridge_state * -cdns_mhdp_bridge_atomic_reset(struct drm_bridge *bridge) +cdns_mhdp_bridge_atomic_create_state(struct drm_bridge *bridge) { struct cdns_mhdp_bridge_state *cdns_mhdp_state; cdns_mhdp_state = kzalloc_obj(*cdns_mhdp_state); if (!cdns_mhdp_state) - return NULL; + return ERR_PTR(-ENOMEM); - __drm_atomic_helper_bridge_reset(bridge, &cdns_mhdp_state->base); + __drm_atomic_helper_bridge_state_init(&cdns_mhdp_state->base, bridge); return &cdns_mhdp_state->base; } @@ -2051,7 +2051,7 @@ static const struct drm_bridge_funcs cdns_mhdp_bridge_funcs = { .detach = cdns_mhdp_detach, .atomic_duplicate_state = cdns_mhdp_bridge_atomic_duplicate_state, .atomic_destroy_state = cdns_mhdp_bridge_atomic_destroy_state, - .atomic_reset = cdns_mhdp_bridge_atomic_reset, + .atomic_create_state = cdns_mhdp_bridge_atomic_create_state, .atomic_get_input_bus_fmts = cdns_mhdp_get_input_bus_fmts, .detect = cdns_mhdp_bridge_detect, .edid_read = cdns_mhdp_bridge_edid_read, diff --git a/drivers/gpu/drm/bridge/chipone-icn6211.c b/drivers/gpu/drm/bridge/chipone-icn6211.c index d8344e011818..eb628a0ce6ec 100644 --- a/drivers/gpu/drm/bridge/chipone-icn6211.c +++ b/drivers/gpu/drm/bridge/chipone-icn6211.c @@ -618,7 +618,7 @@ chipone_atomic_get_input_bus_fmts(struct drm_bridge *bridge, static const struct drm_bridge_funcs chipone_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_create_state = drm_atomic_helper_bridge_create_state, .atomic_pre_enable = chipone_atomic_pre_enable, .atomic_enable = chipone_atomic_enable, .atomic_post_disable = chipone_atomic_post_disable, diff --git a/drivers/gpu/drm/bridge/display-connector.c b/drivers/gpu/drm/bridge/display-connector.c index 16c0631adeb1..d1e74dd2e051 100644 --- a/drivers/gpu/drm/bridge/display-connector.c +++ b/drivers/gpu/drm/bridge/display-connector.c @@ -12,6 +12,7 @@ #include <linux/of.h> #include <linux/platform_device.h> #include <linux/regulator/consumer.h> +#include <linux/workqueue.h> #include <drm/drm_atomic_helper.h> #include <drm/drm_bridge.h> @@ -25,6 +26,8 @@ struct display_connector { struct regulator *supply; struct gpio_desc *ddc_en; + + struct work_struct hpd_work; }; static inline struct display_connector * @@ -87,6 +90,34 @@ display_connector_bridge_detect(struct drm_bridge *bridge, struct drm_connector return display_connector_detect(bridge); } +static void display_connector_hpd_enable(struct drm_bridge *bridge) +{ + struct display_connector *conn = to_display_connector(bridge); + + enable_irq(conn->hpd_irq); + + if (conn->bridge.type == DRM_MODE_CONNECTOR_DisplayPort) + schedule_work(&conn->hpd_work); +} + +static void display_connector_hpd_disable(struct drm_bridge *bridge) +{ + struct display_connector *conn = to_display_connector(bridge); + + if (conn->bridge.type == DRM_MODE_CONNECTOR_DisplayPort) + cancel_work_sync(&conn->hpd_work); + + disable_irq(conn->hpd_irq); +} + +static void display_connector_hpd_work(struct work_struct *work) +{ + struct display_connector *conn = container_of(work, struct display_connector, hpd_work); + struct drm_bridge *bridge = &conn->bridge; + + drm_bridge_hpd_notify(bridge, display_connector_detect(bridge)); +} + static const struct drm_edid *display_connector_edid_read(struct drm_bridge *bridge, struct drm_connector *connector) { @@ -178,12 +209,14 @@ static u32 *display_connector_get_input_bus_fmts(struct drm_bridge *bridge, static const struct drm_bridge_funcs display_connector_bridge_funcs = { .attach = display_connector_attach, .detect = display_connector_bridge_detect, + .hpd_enable = display_connector_hpd_enable, + .hpd_disable = display_connector_hpd_disable, .edid_read = display_connector_edid_read, .atomic_get_output_bus_fmts = display_connector_get_output_bus_fmts, .atomic_get_input_bus_fmts = display_connector_get_input_bus_fmts, .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_create_state = drm_atomic_helper_bridge_create_state, }; static irqreturn_t display_connector_hpd_irq(int irq, void *arg) @@ -307,6 +340,7 @@ static int display_connector_probe(struct platform_device *pdev) NULL, display_connector_hpd_irq, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | + IRQF_NO_AUTOEN | IRQF_ONESHOT, "HPD", conn); if (ret) { @@ -378,6 +412,8 @@ static int display_connector_probe(struct platform_device *pdev) conn->bridge.ops |= DRM_BRIDGE_OP_DETECT; if (conn->hpd_irq >= 0) conn->bridge.ops |= DRM_BRIDGE_OP_HPD; + if (conn->hpd_irq >= 0 && type == DRM_MODE_CONNECTOR_DisplayPort) + INIT_WORK(&conn->hpd_work, display_connector_hpd_work); dev_dbg(&pdev->dev, "Found %s display connector '%s' %s DDC bus and %s HPD GPIO (ops 0x%x)\n", diff --git a/drivers/gpu/drm/bridge/fsl-ldb.c b/drivers/gpu/drm/bridge/fsl-ldb.c index 9bfaa3f93370..cc1f88e7873e 100644 --- a/drivers/gpu/drm/bridge/fsl-ldb.c +++ b/drivers/gpu/drm/bridge/fsl-ldb.c @@ -289,7 +289,7 @@ static const struct drm_bridge_funcs funcs = { .atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state, .atomic_destroy_state = drm_atomic_helper_bridge_destroy_state, .atomic_get_input_bus_fmts = fsl_ldb_atomic_get_input_bus_fmts, - .atomic_reset = drm_atomic_helper_bridge_reset, + .atomic_create_state = drm_atomic_helper_bridge_create_state, .mode_valid = fsl_ldb_mode_valid, }; diff --git a/drivers/gpu/drm/bridge/imx/imx8mp-hdmi-pvi.c b/drivers/gpu/drm/bridge/imx/imx8mp-hdmi-pvi.c index 7d5fda7173e5..a31d4fc76f35 100644 --- a/drivers/gpu/drm/bridge/imx/imx8mp-hdmi-pvi.c +++ b/drivers/gpu/drm/bridge/imx/imx8mp-hdmi-pvi.c @@ -131,7 +131,7 @@ static const struct drm_bridge_funcs imx_hdmi_pvi_bridge_funcs = { .atomic_get_input_bus_fmts = imx8mp_hdmi_pvi_bridge_get_input_bus_fmts, .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_create_state = drm_atomic_helper_bridge_create_state, }; static int imx8mp_hdmi_pvi_probe(struct platform_device *pdev) diff --git a/drivers/gpu/drm/bridge/imx/imx8qm-ldb.c b/drivers/gpu/drm/bridge/imx/imx8qm-ldb.c index a6ca4f5c6cc6..5884af991547 100644 --- a/drivers/gpu/drm/bridge/imx/imx8qm-ldb.c +++ b/drivers/gpu/drm/bridge/imx/imx8qm-ldb.c @@ -389,7 +389,7 @@ imx8qm_ldb_bridge_mode_valid(struct drm_bridge *bridge, static const struct drm_bridge_funcs imx8qm_ldb_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_create_state = drm_atomic_helper_bridge_create_state, .mode_valid = imx8qm_ldb_bridge_mode_valid, .attach = ldb_bridge_attach_helper, .atomic_check = imx8qm_ldb_bridge_atomic_check, diff --git a/drivers/gpu/drm/bridge/imx/imx8qxp-ldb.c b/drivers/gpu/drm/bridge/imx/imx8qxp-ldb.c index a7906314ade1..b95c640564e4 100644 --- a/drivers/gpu/drm/bridge/imx/imx8qxp-ldb.c +++ b/drivers/gpu/drm/bridge/imx/imx8qxp-ldb.c @@ -404,7 +404,7 @@ static const struct drm_bridge_funcs imx8qxp_ldb_bridge_funcs = { .destroy = imx8qxp_ldb_bridge_destroy, .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_create_state = drm_atomic_helper_bridge_create_state, .mode_valid = imx8qxp_ldb_bridge_mode_valid, .attach = ldb_bridge_attach_helper, .atomic_check = imx8qxp_ldb_bridge_atomic_check, diff --git a/drivers/gpu/drm/bridge/imx/imx8qxp-pixel-combiner.c b/drivers/gpu/drm/bridge/imx/imx8qxp-pixel-combiner.c index e0ee51a9ca7f..a5a1bf42325b 100644 --- a/drivers/gpu/drm/bridge/imx/imx8qxp-pixel-combiner.c +++ b/drivers/gpu/drm/bridge/imx/imx8qxp-pixel-combiner.c @@ -251,7 +251,7 @@ imx8qxp_pc_bridge_atomic_get_output_bus_fmts(struct drm_bridge *bridge, static const struct drm_bridge_funcs imx8qxp_pc_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_create_state = drm_atomic_helper_bridge_create_state, .mode_valid = imx8qxp_pc_bridge_mode_valid, .attach = imx8qxp_pc_bridge_attach, .mode_set = imx8qxp_pc_bridge_mode_set, diff --git a/drivers/gpu/drm/bridge/imx/imx8qxp-pixel-link.c b/drivers/gpu/drm/bridge/imx/imx8qxp-pixel-link.c index ee6b6dbbe952..56ce6aa76181 100644 --- a/drivers/gpu/drm/bridge/imx/imx8qxp-pixel-link.c +++ b/drivers/gpu/drm/bridge/imx/imx8qxp-pixel-link.c @@ -229,7 +229,7 @@ imx8qxp_pixel_link_bridge_atomic_get_output_bus_fmts(struct drm_bridge *bridge, static const struct drm_bridge_funcs imx8qxp_pixel_link_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_create_state = drm_atomic_helper_bridge_create_state, .attach = imx8qxp_pixel_link_bridge_attach, .mode_set = imx8qxp_pixel_link_bridge_mode_set, .atomic_enable = imx8qxp_pixel_link_bridge_atomic_enable, diff --git a/drivers/gpu/drm/bridge/imx/imx8qxp-pxl2dpi.c b/drivers/gpu/drm/bridge/imx/imx8qxp-pxl2dpi.c index 87305d3e0c39..dce1a5b36024 100644 --- a/drivers/gpu/drm/bridge/imx/imx8qxp-pxl2dpi.c +++ b/drivers/gpu/drm/bridge/imx/imx8qxp-pxl2dpi.c @@ -210,7 +210,7 @@ imx8qxp_pxl2dpi_bridge_atomic_get_output_bus_fmts(struct drm_bridge *bridge, static const struct drm_bridge_funcs imx8qxp_pxl2dpi_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_create_state = drm_atomic_helper_bridge_create_state, .attach = imx8qxp_pxl2dpi_bridge_attach, .destroy = imx8qxp_pxl2dpi_bridge_destroy, .atomic_check = imx8qxp_pxl2dpi_bridge_atomic_check, diff --git a/drivers/gpu/drm/bridge/inno-hdmi.c b/drivers/gpu/drm/bridge/inno-hdmi.c index 9a62bf59a403..dd35f5a875d3 100644 --- a/drivers/gpu/drm/bridge/inno-hdmi.c +++ b/drivers/gpu/drm/bridge/inno-hdmi.c @@ -878,7 +878,7 @@ static void inno_hdmi_bridge_atomic_disable(struct drm_bridge *bridge, static const struct drm_bridge_funcs inno_hdmi_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_create_state = drm_atomic_helper_bridge_create_state, .atomic_enable = inno_hdmi_bridge_atomic_enable, .atomic_disable = inno_hdmi_bridge_atomic_disable, .detect = inno_hdmi_bridge_detect, diff --git a/drivers/gpu/drm/bridge/ite-it6263.c b/drivers/gpu/drm/bridge/ite-it6263.c index c7d588be12cb..4c0b670806cc 100644 --- a/drivers/gpu/drm/bridge/ite-it6263.c +++ b/drivers/gpu/drm/bridge/ite-it6263.c @@ -829,7 +829,7 @@ static int it6263_hdmi_write_hdmi_infoframe(struct drm_bridge *bridge, static const struct drm_bridge_funcs it6263_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_create_state = drm_atomic_helper_bridge_create_state, .attach = it6263_bridge_attach, .mode_valid = it6263_bridge_mode_valid, .atomic_disable = it6263_bridge_atomic_disable, diff --git a/drivers/gpu/drm/bridge/ite-it6505.c b/drivers/gpu/drm/bridge/ite-it6505.c index 6b7cb132a437..8ecb43611dba 100644 --- a/drivers/gpu/drm/bridge/ite-it6505.c +++ b/drivers/gpu/drm/bridge/ite-it6505.c @@ -3239,7 +3239,7 @@ static const struct drm_edid *it6505_bridge_edid_read(struct drm_bridge *bridge, static const struct drm_bridge_funcs it6505_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_create_state = drm_atomic_helper_bridge_create_state, .attach = it6505_bridge_attach, .detach = it6505_bridge_detach, .mode_valid = it6505_bridge_mode_valid, diff --git a/drivers/gpu/drm/bridge/ite-it66121.c b/drivers/gpu/drm/bridge/ite-it66121.c index a2f48db369e3..51fc68ffdf8f 100644 --- a/drivers/gpu/drm/bridge/ite-it66121.c +++ b/drivers/gpu/drm/bridge/ite-it66121.c @@ -1543,7 +1543,7 @@ static int it66121_hdmi_audio_mute_stream(struct drm_bridge *bridge, static const struct drm_bridge_funcs it66121_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_create_state = drm_atomic_helper_bridge_create_state, .attach = it66121_bridge_attach, .atomic_get_output_bus_fmts = it66121_bridge_atomic_get_output_bus_fmts, .atomic_get_input_bus_fmts = it66121_bridge_atomic_get_input_bus_fmts, diff --git a/drivers/gpu/drm/bridge/lontium-lt9211.c b/drivers/gpu/drm/bridge/lontium-lt9211.c index 870175a84839..f39d83a5ae37 100644 --- a/drivers/gpu/drm/bridge/lontium-lt9211.c +++ b/drivers/gpu/drm/bridge/lontium-lt9211.c @@ -619,7 +619,7 @@ static const struct drm_bridge_funcs lt9211_funcs = { .atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state, .atomic_destroy_state = drm_atomic_helper_bridge_destroy_state, .atomic_get_input_bus_fmts = lt9211_atomic_get_input_bus_fmts, - .atomic_reset = drm_atomic_helper_bridge_reset, + .atomic_create_state = drm_atomic_helper_bridge_create_state, }; static int lt9211_parse_dt(struct lt9211 *ctx) diff --git a/drivers/gpu/drm/bridge/lontium-lt9611.c b/drivers/gpu/drm/bridge/lontium-lt9611.c index 21305296e111..b6a368f71285 100644 --- a/drivers/gpu/drm/bridge/lontium-lt9611.c +++ b/drivers/gpu/drm/bridge/lontium-lt9611.c @@ -1026,7 +1026,7 @@ static const struct drm_bridge_funcs lt9611_bridge_funcs = { .atomic_post_disable = lt9611_bridge_atomic_post_disable, .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_create_state = drm_atomic_helper_bridge_create_state, .atomic_get_input_bus_fmts = lt9611_atomic_get_input_bus_fmts, .hdmi_tmds_char_rate_valid = lt9611_hdmi_tmds_char_rate_valid, diff --git a/drivers/gpu/drm/bridge/lvds-codec.c b/drivers/gpu/drm/bridge/lvds-codec.c index e6a7147e141b..d1135dc3b99f 100644 --- a/drivers/gpu/drm/bridge/lvds-codec.c +++ b/drivers/gpu/drm/bridge/lvds-codec.c @@ -104,7 +104,7 @@ static const struct drm_bridge_funcs funcs = { .disable = lvds_codec_disable, .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_create_state = drm_atomic_helper_bridge_create_state, .atomic_get_input_bus_fmts = lvds_codec_atomic_get_input_bus_fmts, }; diff --git a/drivers/gpu/drm/bridge/nwl-dsi.c b/drivers/gpu/drm/bridge/nwl-dsi.c index 3c9ae93c4f67..09992529f3d8 100644 --- a/drivers/gpu/drm/bridge/nwl-dsi.c +++ b/drivers/gpu/drm/bridge/nwl-dsi.c @@ -960,7 +960,7 @@ static u32 *nwl_bridge_atomic_get_input_bus_fmts(struct drm_bridge *bridge, static const struct drm_bridge_funcs nwl_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_create_state = drm_atomic_helper_bridge_create_state, .atomic_check = nwl_dsi_bridge_atomic_check, .atomic_enable = nwl_dsi_bridge_atomic_enable, .atomic_disable = nwl_dsi_bridge_atomic_disable, diff --git a/drivers/gpu/drm/bridge/of-display-mode-bridge.c b/drivers/gpu/drm/bridge/of-display-mode-bridge.c index cb15713f3a79..e66dae168fd0 100644 --- a/drivers/gpu/drm/bridge/of-display-mode-bridge.c +++ b/drivers/gpu/drm/bridge/of-display-mode-bridge.c @@ -51,7 +51,7 @@ static int of_display_mode_bridge_get_modes(struct drm_bridge *bridge, return 0; } -struct drm_bridge_funcs of_display_mode_bridge_funcs = { +static const struct drm_bridge_funcs of_display_mode_bridge_funcs = { .attach = of_display_mode_bridge_attach, .get_modes = of_display_mode_bridge_get_modes, }; diff --git a/drivers/gpu/drm/bridge/panel.c b/drivers/gpu/drm/bridge/panel.c index 4978ec98a082..62af499f1f5c 100644 --- a/drivers/gpu/drm/bridge/panel.c +++ b/drivers/gpu/drm/bridge/panel.c @@ -213,7 +213,7 @@ static const struct drm_bridge_funcs panel_bridge_bridge_funcs = { .atomic_disable = panel_bridge_atomic_disable, .atomic_post_disable = panel_bridge_atomic_post_disable, .get_modes = panel_bridge_get_modes, - .atomic_reset = drm_atomic_helper_bridge_reset, + .atomic_create_state = drm_atomic_helper_bridge_create_state, .atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state, .atomic_destroy_state = drm_atomic_helper_bridge_destroy_state, .atomic_get_input_bus_fmts = drm_atomic_helper_bridge_propagate_bus_fmt, diff --git a/drivers/gpu/drm/bridge/parade-ps8640.c b/drivers/gpu/drm/bridge/parade-ps8640.c index b93514023baa..96332721cb69 100644 --- a/drivers/gpu/drm/bridge/parade-ps8640.c +++ b/drivers/gpu/drm/bridge/parade-ps8640.c @@ -556,7 +556,7 @@ static const struct drm_bridge_funcs ps8640_bridge_funcs = { .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, + .atomic_create_state = drm_atomic_helper_bridge_create_state, }; static int ps8640_bridge_get_dsi_resources(struct device *dev, struct ps8640 *ps_bridge) diff --git a/drivers/gpu/drm/bridge/samsung-dsim.c b/drivers/gpu/drm/bridge/samsung-dsim.c index 9ee0515074c7..5788066d479e 100644 --- a/drivers/gpu/drm/bridge/samsung-dsim.c +++ b/drivers/gpu/drm/bridge/samsung-dsim.c @@ -1835,7 +1835,7 @@ static int samsung_dsim_attach(struct drm_bridge *bridge, static const struct drm_bridge_funcs samsung_dsim_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_create_state = drm_atomic_helper_bridge_create_state, .atomic_get_input_bus_fmts = samsung_dsim_atomic_get_input_bus_fmts, .atomic_check = samsung_dsim_atomic_check, .atomic_pre_enable = samsung_dsim_atomic_pre_enable, diff --git a/drivers/gpu/drm/bridge/sii902x.c b/drivers/gpu/drm/bridge/sii902x.c index ea543be4ae3e..f575b2dbe32b 100644 --- a/drivers/gpu/drm/bridge/sii902x.c +++ b/drivers/gpu/drm/bridge/sii902x.c @@ -544,7 +544,7 @@ static const struct drm_bridge_funcs sii902x_bridge_funcs = { .atomic_enable = sii902x_bridge_atomic_enable, .detect = sii902x_bridge_detect, .edid_read = sii902x_bridge_edid_read, - .atomic_reset = drm_atomic_helper_bridge_reset, + .atomic_create_state = drm_atomic_helper_bridge_create_state, .atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state, .atomic_destroy_state = drm_atomic_helper_bridge_destroy_state, .atomic_get_input_bus_fmts = sii902x_bridge_atomic_get_input_bus_fmts, @@ -1158,9 +1158,7 @@ static int sii902x_probe(struct i2c_client *client) static const char * const supplies[] = {"iovcc", "cvcc12"}; int ret; - ret = i2c_check_functionality(client->adapter, - I2C_FUNC_SMBUS_BYTE_DATA); - if (!ret) { + if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) { dev_err(dev, "I2C adapter not suitable\n"); return -EIO; } diff --git a/drivers/gpu/drm/bridge/ssd2825.c b/drivers/gpu/drm/bridge/ssd2825.c index 54ca6bd6883a..00a4ed4a0700 100644 --- a/drivers/gpu/drm/bridge/ssd2825.c +++ b/drivers/gpu/drm/bridge/ssd2825.c @@ -679,7 +679,7 @@ static const struct drm_bridge_funcs ssd2825_bridge_funcs = { .atomic_enable = ssd2825_bridge_atomic_enable, .atomic_disable = ssd2825_bridge_atomic_disable, - .atomic_reset = drm_atomic_helper_bridge_reset, + .atomic_create_state = drm_atomic_helper_bridge_create_state, .atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state, .atomic_destroy_state = drm_atomic_helper_bridge_destroy_state, }; diff --git a/drivers/gpu/drm/bridge/synopsys/dw-dp.c b/drivers/gpu/drm/bridge/synopsys/dw-dp.c index 21541be094c4..3445c82e6f50 100644 --- a/drivers/gpu/drm/bridge/synopsys/dw-dp.c +++ b/drivers/gpu/drm/bridge/synopsys/dw-dp.c @@ -1816,7 +1816,7 @@ static struct drm_bridge_state *dw_dp_bridge_atomic_duplicate_state(struct drm_b static const struct drm_bridge_funcs dw_dp_bridge_funcs = { .atomic_duplicate_state = dw_dp_bridge_atomic_duplicate_state, .atomic_destroy_state = drm_atomic_helper_bridge_destroy_state, - .atomic_reset = drm_atomic_helper_bridge_reset, + .atomic_create_state = drm_atomic_helper_bridge_create_state, .atomic_get_input_bus_fmts = drm_atomic_helper_bridge_propagate_bus_fmt, .atomic_get_output_bus_fmts = dw_dp_bridge_atomic_get_output_bus_fmts, .atomic_check = dw_dp_bridge_atomic_check, @@ -2093,6 +2093,12 @@ unregister_aux: } EXPORT_SYMBOL_GPL(dw_dp_bind); +void dw_dp_unbind(struct dw_dp *dp) +{ + drm_dp_aux_unregister(&dp->aux); +} +EXPORT_SYMBOL_GPL(dw_dp_unbind); + MODULE_AUTHOR("Andy Yan <andyshrk@163.com>"); MODULE_DESCRIPTION("DW DP Core Library"); MODULE_LICENSE("GPL"); diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi-qp.c b/drivers/gpu/drm/bridge/synopsys/dw-hdmi-qp.c index 1c214a8e6dc2..cfd1b18c06e6 100644 --- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi-qp.c +++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi-qp.c @@ -1190,9 +1190,10 @@ static int dw_hdmi_qp_cec_transmit(struct drm_bridge *bridge, u8 attempts, #endif /* CONFIG_DRM_DW_HDMI_QP_CEC */ static const struct drm_bridge_funcs dw_hdmi_qp_bridge_funcs = { + .atomic_get_output_bus_fmts = drm_atomic_helper_bridge_get_hdmi_output_bus_fmts, .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_create_state = drm_atomic_helper_bridge_create_state, .atomic_enable = dw_hdmi_qp_bridge_atomic_enable, .atomic_disable = dw_hdmi_qp_bridge_atomic_disable, .detect = dw_hdmi_qp_bridge_detect, diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c index 17d5caedb32e..a408dbd414bf 100644 --- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c +++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c @@ -3027,7 +3027,7 @@ static const struct drm_edid *dw_hdmi_bridge_edid_read(struct drm_bridge *bridge static const struct drm_bridge_funcs dw_hdmi_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_create_state = drm_atomic_helper_bridge_create_state, .attach = dw_hdmi_bridge_attach, .detach = dw_hdmi_bridge_detach, .atomic_check = dw_hdmi_bridge_atomic_check, diff --git a/drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c b/drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c index af176e136f85..5dd475fcaacf 100644 --- a/drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c +++ b/drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c @@ -1095,7 +1095,7 @@ static const struct drm_bridge_funcs dw_mipi_dsi_bridge_funcs = { .atomic_destroy_state = drm_atomic_helper_bridge_destroy_state, .atomic_get_input_bus_fmts = dw_mipi_dsi_bridge_atomic_get_input_bus_fmts, .atomic_check = dw_mipi_dsi_bridge_atomic_check, - .atomic_reset = drm_atomic_helper_bridge_reset, + .atomic_create_state = drm_atomic_helper_bridge_create_state, .atomic_pre_enable = dw_mipi_dsi_bridge_atomic_pre_enable, .atomic_enable = dw_mipi_dsi_bridge_atomic_enable, .atomic_post_disable = dw_mipi_dsi_bridge_post_atomic_disable, diff --git a/drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi2.c b/drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi2.c index b2a022d266d9..5f06cb03b4c0 100644 --- a/drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi2.c +++ b/drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi2.c @@ -893,7 +893,7 @@ static const struct drm_bridge_funcs dw_mipi_dsi2_bridge_funcs = { .atomic_destroy_state = drm_atomic_helper_bridge_destroy_state, .atomic_get_input_bus_fmts = dw_mipi_dsi2_bridge_atomic_get_input_bus_fmts, .atomic_check = dw_mipi_dsi2_bridge_atomic_check, - .atomic_reset = drm_atomic_helper_bridge_reset, + .atomic_create_state = drm_atomic_helper_bridge_create_state, .atomic_pre_enable = dw_mipi_dsi2_bridge_atomic_pre_enable, .atomic_enable = dw_mipi_dsi2_bridge_atomic_enable, .atomic_post_disable = dw_mipi_dsi2_bridge_post_atomic_disable, diff --git a/drivers/gpu/drm/bridge/tc358762.c b/drivers/gpu/drm/bridge/tc358762.c index 6aa93b3274dd..6124d3123f57 100644 --- a/drivers/gpu/drm/bridge/tc358762.c +++ b/drivers/gpu/drm/bridge/tc358762.c @@ -225,7 +225,7 @@ static const struct drm_bridge_funcs tc358762_bridge_funcs = { .atomic_enable = tc358762_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, + .atomic_create_state = drm_atomic_helper_bridge_create_state, .attach = tc358762_attach, .mode_set = tc358762_bridge_mode_set, }; diff --git a/drivers/gpu/drm/bridge/tc358767.c b/drivers/gpu/drm/bridge/tc358767.c index 7188935fdb82..966c3eae437e 100644 --- a/drivers/gpu/drm/bridge/tc358767.c +++ b/drivers/gpu/drm/bridge/tc358767.c @@ -1933,7 +1933,7 @@ static const struct drm_bridge_funcs tc_dpi_bridge_funcs = { .atomic_disable = tc_dpi_bridge_atomic_disable, .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_create_state = drm_atomic_helper_bridge_create_state, .atomic_get_input_bus_fmts = tc_dpi_atomic_get_input_bus_fmts, }; @@ -1949,7 +1949,7 @@ static const struct drm_bridge_funcs tc_edp_bridge_funcs = { .edid_read = tc_edid_read, .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_create_state = drm_atomic_helper_bridge_create_state, .atomic_get_input_bus_fmts = drm_atomic_helper_bridge_propagate_bus_fmt, .atomic_get_output_bus_fmts = tc_edp_atomic_get_output_bus_fmts, }; diff --git a/drivers/gpu/drm/bridge/tc358768.c b/drivers/gpu/drm/bridge/tc358768.c index 0f2820b37302..0d85120fcc7a 100644 --- a/drivers/gpu/drm/bridge/tc358768.c +++ b/drivers/gpu/drm/bridge/tc358768.c @@ -1290,7 +1290,7 @@ static const struct drm_bridge_funcs tc358768_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_create_state = drm_atomic_helper_bridge_create_state, .atomic_get_input_bus_fmts = tc358768_atomic_get_input_bus_fmts, }; diff --git a/drivers/gpu/drm/bridge/tc358775.c b/drivers/gpu/drm/bridge/tc358775.c index e067b671cece..728938f9dcb4 100644 --- a/drivers/gpu/drm/bridge/tc358775.c +++ b/drivers/gpu/drm/bridge/tc358775.c @@ -598,7 +598,7 @@ static const struct drm_bridge_funcs tc_bridge_funcs = { .atomic_enable = tc_bridge_atomic_enable, .mode_valid = tc_mode_valid, .atomic_post_disable = tc_bridge_atomic_post_disable, - .atomic_reset = drm_atomic_helper_bridge_reset, + .atomic_create_state = drm_atomic_helper_bridge_create_state, .atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state, .atomic_destroy_state = drm_atomic_helper_bridge_destroy_state, }; diff --git a/drivers/gpu/drm/bridge/ti-dlpc3433.c b/drivers/gpu/drm/bridge/ti-dlpc3433.c index 9c35eade0a78..c16eb3a05cfa 100644 --- a/drivers/gpu/drm/bridge/ti-dlpc3433.c +++ b/drivers/gpu/drm/bridge/ti-dlpc3433.c @@ -254,7 +254,7 @@ static const struct drm_bridge_funcs dlpc_bridge_funcs = { .atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state, .atomic_destroy_state = drm_atomic_helper_bridge_destroy_state, .atomic_get_input_bus_fmts = dlpc_atomic_get_input_bus_fmts, - .atomic_reset = drm_atomic_helper_bridge_reset, + .atomic_create_state = drm_atomic_helper_bridge_create_state, .atomic_pre_enable = dlpc_atomic_pre_enable, .atomic_enable = dlpc_atomic_enable, .atomic_post_disable = dlpc_atomic_post_disable, diff --git a/drivers/gpu/drm/bridge/ti-sn65dsi83.c b/drivers/gpu/drm/bridge/ti-sn65dsi83.c index 42b451432bbb..e6dbe51d0dba 100644 --- a/drivers/gpu/drm/bridge/ti-sn65dsi83.c +++ b/drivers/gpu/drm/bridge/ti-sn65dsi83.c @@ -801,7 +801,7 @@ static const struct drm_bridge_funcs sn65dsi83_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_create_state = drm_atomic_helper_bridge_create_state, .atomic_get_input_bus_fmts = sn65dsi83_atomic_get_input_bus_fmts, }; @@ -977,7 +977,6 @@ static int sn65dsi83_host_attach(struct sn65dsi83 *ctx) dsi->lanes = dsi_lanes; dsi->format = MIPI_DSI_FMT_RGB888; dsi->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_BURST | - MIPI_DSI_MODE_VIDEO_NO_HFP | MIPI_DSI_MODE_VIDEO_NO_HBP | MIPI_DSI_MODE_VIDEO_NO_HSA | MIPI_DSI_MODE_NO_EOT_PACKET; ret = devm_mipi_dsi_attach(dev, dsi); diff --git a/drivers/gpu/drm/bridge/ti-sn65dsi86.c b/drivers/gpu/drm/bridge/ti-sn65dsi86.c index 0884ec459cc5..1da242f17413 100644 --- a/drivers/gpu/drm/bridge/ti-sn65dsi86.c +++ b/drivers/gpu/drm/bridge/ti-sn65dsi86.c @@ -1302,7 +1302,7 @@ static const struct drm_bridge_funcs ti_sn_bridge_funcs = { .atomic_enable = ti_sn_bridge_atomic_enable, .atomic_disable = ti_sn_bridge_atomic_disable, .atomic_post_disable = ti_sn_bridge_atomic_post_disable, - .atomic_reset = drm_atomic_helper_bridge_reset, + .atomic_create_state = drm_atomic_helper_bridge_create_state, .atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state, .atomic_destroy_state = drm_atomic_helper_bridge_destroy_state, .debugfs_init = ti_sn65dsi86_debugfs_init, diff --git a/drivers/gpu/drm/bridge/ti-tdp158.c b/drivers/gpu/drm/bridge/ti-tdp158.c index 3a19967f213f..01fc643bd78f 100644 --- a/drivers/gpu/drm/bridge/ti-tdp158.c +++ b/drivers/gpu/drm/bridge/ti-tdp158.c @@ -60,7 +60,7 @@ static const struct drm_bridge_funcs tdp158_bridge_funcs = { .atomic_disable = tdp158_disable, .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_create_state = drm_atomic_helper_bridge_create_state, }; static int tdp158_probe(struct i2c_client *client) diff --git a/drivers/gpu/drm/bridge/ti-tfp410.c b/drivers/gpu/drm/bridge/ti-tfp410.c index bf4ab4eaf269..6f06f7b4e992 100644 --- a/drivers/gpu/drm/bridge/ti-tfp410.c +++ b/drivers/gpu/drm/bridge/ti-tfp410.c @@ -245,7 +245,7 @@ static const struct drm_bridge_funcs tfp410_bridge_funcs = { .enable = tfp410_enable, .disable = tfp410_disable, .mode_valid = tfp410_mode_valid, - .atomic_reset = drm_atomic_helper_bridge_reset, + .atomic_create_state = drm_atomic_helper_bridge_create_state, .atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state, .atomic_destroy_state = drm_atomic_helper_bridge_destroy_state, .atomic_get_input_bus_fmts = tfp410_get_input_bus_fmts, diff --git a/drivers/gpu/drm/clients/drm_log.c b/drivers/gpu/drm/clients/drm_log.c index 8d21b785bead..e3e02c84a4cf 100644 --- a/drivers/gpu/drm/clients/drm_log.c +++ b/drivers/gpu/drm/clients/drm_log.c @@ -122,10 +122,12 @@ static void drm_log_draw_line(struct drm_log_scanout *scanout, const char *s, iosys_map_incr(&map, r.y1 * fb->pitches[0]); for (i = 0; i < len && i < scanout->columns; i++) { u32 color = (i < prefix_len) ? scanout->prefix_color : scanout->front_color; - src = drm_draw_get_char_bitmap(font, s[i], font_pitch); - drm_log_blit(&map, fb->pitches[0], src, font_pitch, - scanout->scaled_font_h, scanout->scaled_font_w, - px_width, color); + src = font_data_glyph_buf(font->data, font->width, font->height, + (unsigned char)s[i]); + if (src) + drm_log_blit(&map, fb->pitches[0], src, font_pitch, + scanout->scaled_font_h, scanout->scaled_font_w, + px_width, color); iosys_map_incr(&map, scanout->scaled_font_w * px_width); } diff --git a/drivers/gpu/drm/display/drm_bridge_connector.c b/drivers/gpu/drm/display/drm_bridge_connector.c index 649969fca141..92f8a2d7aab4 100644 --- a/drivers/gpu/drm/display/drm_bridge_connector.c +++ b/drivers/gpu/drm/display/drm_bridge_connector.c @@ -265,26 +265,57 @@ static void drm_bridge_connector_debugfs_init(struct drm_connector *connector, } } -static void drm_bridge_connector_reset(struct drm_connector *connector) +static struct drm_connector_state * +drm_bridge_connector_create_state(struct drm_connector *connector) { struct drm_bridge_connector *bridge_connector = to_drm_bridge_connector(connector); + struct drm_connector_state *conn_state; + + conn_state = drm_atomic_helper_connector_create_state(connector); + if (IS_ERR(conn_state)) + return conn_state; - drm_atomic_helper_connector_reset(connector); if (bridge_connector->bridge_hdmi) - __drm_atomic_helper_connector_hdmi_reset(connector, - connector->state); + __drm_atomic_helper_connector_hdmi_state_init(connector, + conn_state); + + return conn_state; +} + +static enum drm_connector_color_format +drm_bridge_connector_color_format(const struct drm_connector_state *conn_state) +{ + struct drm_bridge_connector *bridge_connector = + to_drm_bridge_connector(conn_state->connector); + + if (bridge_connector->bridge_hdmi) { + switch (conn_state->hdmi.output_format) { + default: + case DRM_OUTPUT_COLOR_FORMAT_RGB444: + return DRM_CONNECTOR_COLOR_FORMAT_RGB444; + case DRM_OUTPUT_COLOR_FORMAT_YCBCR444: + return DRM_CONNECTOR_COLOR_FORMAT_YCBCR444; + case DRM_OUTPUT_COLOR_FORMAT_YCBCR422: + return DRM_CONNECTOR_COLOR_FORMAT_YCBCR422; + case DRM_OUTPUT_COLOR_FORMAT_YCBCR420: + return DRM_CONNECTOR_COLOR_FORMAT_YCBCR420; + } + } + + return conn_state->color_format; } static const struct drm_connector_funcs drm_bridge_connector_funcs = { - .reset = drm_bridge_connector_reset, .detect = drm_bridge_connector_detect, .force = drm_bridge_connector_force, .fill_modes = drm_helper_probe_single_connector_modes, + .atomic_create_state = drm_bridge_connector_create_state, .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, .debugfs_init = drm_bridge_connector_debugfs_init, .oob_hotplug_event = drm_bridge_connector_oob_hotplug_event, + .color_format = drm_bridge_connector_color_format, }; /* ----------------------------------------------------------------------------- diff --git a/drivers/gpu/drm/display/drm_hdmi_state_helper.c b/drivers/gpu/drm/display/drm_hdmi_state_helper.c index 4867edbf2622..ce17eeefc2da 100644 --- a/drivers/gpu/drm/display/drm_hdmi_state_helper.c +++ b/drivers/gpu/drm/display/drm_hdmi_state_helper.c @@ -306,17 +306,18 @@ */ /** - * __drm_atomic_helper_connector_hdmi_reset() - Initializes all HDMI @drm_connector_state resources + * __drm_atomic_helper_connector_hdmi_state_init() - Initialize all HDMI @drm_connector_state resources * @connector: DRM connector - * @new_conn_state: connector state to reset + * @new_conn_state: connector state to initialize * * Initializes all HDMI resources from a @drm_connector_state without * actually allocating it. This is useful for HDMI drivers, in - * combination with __drm_atomic_helper_connector_reset() or - * drm_atomic_helper_connector_reset(). + * combination with __drm_atomic_helper_connector_state_init(), + * drm_atomic_helper_connector_reset(), or + * drm_atomic_helper_connector_create_state(). */ -void __drm_atomic_helper_connector_hdmi_reset(struct drm_connector *connector, - struct drm_connector_state *new_conn_state) +void __drm_atomic_helper_connector_hdmi_state_init(struct drm_connector *connector, + struct drm_connector_state *new_conn_state) { unsigned int max_bpc = connector->max_bpc; @@ -324,7 +325,7 @@ void __drm_atomic_helper_connector_hdmi_reset(struct drm_connector *connector, new_conn_state->max_requested_bpc = max_bpc; new_conn_state->hdmi.broadcast_rgb = DRM_HDMI_BROADCAST_RGB_AUTO; } -EXPORT_SYMBOL(__drm_atomic_helper_connector_hdmi_reset); +EXPORT_SYMBOL(__drm_atomic_helper_connector_hdmi_state_init); static enum hdmi_colorspace output_color_format_to_hdmi_colorspace(const struct drm_connector *connector, @@ -541,10 +542,11 @@ sink_supports_format_bpc(const struct drm_connector *connector, drm_dbg_kms(dev, "YUV444 format supported in that configuration.\n"); return true; - } - drm_dbg_kms(dev, "Unsupported pixel format.\n"); - return false; + default: + drm_dbg_kms(dev, "Unsupported pixel format.\n"); + return false; + } } static enum drm_mode_status @@ -669,8 +671,39 @@ hdmi_compute_config(const struct drm_connector *connector, unsigned int max_bpc = clamp_t(unsigned int, conn_state->max_bpc, 8, connector->max_bpc); + enum drm_output_color_format fmt; int ret; + if (conn_state->color_format != DRM_CONNECTOR_COLOR_FORMAT_AUTO) { + switch (conn_state->color_format) { + case DRM_CONNECTOR_COLOR_FORMAT_AUTO: + drm_warn(connector->dev, "AUTO format in non-AUTO path.\n"); + fallthrough; + case DRM_CONNECTOR_COLOR_FORMAT_RGB444: + fmt = DRM_OUTPUT_COLOR_FORMAT_RGB444; + break; + case DRM_CONNECTOR_COLOR_FORMAT_YCBCR444: + fmt = DRM_OUTPUT_COLOR_FORMAT_YCBCR444; + break; + case DRM_CONNECTOR_COLOR_FORMAT_YCBCR422: + fmt = DRM_OUTPUT_COLOR_FORMAT_YCBCR422; + break; + case DRM_CONNECTOR_COLOR_FORMAT_YCBCR420: + fmt = DRM_OUTPUT_COLOR_FORMAT_YCBCR420; + break; + default: + drm_dbg_kms(connector->dev, "HDMI does not support color format '%d'.\n", + conn_state->color_format); + return -EINVAL; + } + + return hdmi_compute_format_bpc(connector, conn_state, mode, max_bpc, fmt); + } + + /* + * For %DRM_CONNECTOR_COLOR_FORMAT_AUTO, try RGB first, and fall back + * to the less bandwidth-intensive YCBCR420 if RGB fails. + */ ret = hdmi_compute_format_bpc(connector, conn_state, mode, max_bpc, DRM_OUTPUT_COLOR_FORMAT_RGB444); if (ret) { @@ -910,8 +943,21 @@ drm_hdmi_connector_mode_valid(struct drm_connector *connector, const struct drm_display_mode *mode) { unsigned long long clock; + enum drm_output_color_format fmt; + + if (drm_mode_is_420_only(&connector->display_info, mode)) { + if (connector->ycbcr_420_allowed) + fmt = DRM_OUTPUT_COLOR_FORMAT_YCBCR420; + else + return MODE_NO_420; + } else if (drm_mode_is_420_also(&connector->display_info, mode) && + connector->ycbcr_420_allowed) { + fmt = DRM_OUTPUT_COLOR_FORMAT_YCBCR420; + } else { + fmt = DRM_OUTPUT_COLOR_FORMAT_RGB444; + } - clock = drm_hdmi_compute_mode_clock(mode, 8, DRM_OUTPUT_COLOR_FORMAT_RGB444); + clock = drm_hdmi_compute_mode_clock(mode, 8, fmt); if (!clock) return MODE_ERROR; diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c index 080aec5a9774..735ab7badc2e 100644 --- a/drivers/gpu/drm/drm_atomic.c +++ b/drivers/gpu/drm/drm_atomic.c @@ -47,6 +47,86 @@ #include "drm_crtc_internal.h" #include "drm_internal.h" +/** + * DOC: state lifetime + * + * &drm_atomic_commit represents an update to modeset pipeline state. + * It's a transient object that holds a state update as a collection of + * pointers to individual objects' states. &drm_atomic_commit has a much + * shorter lifetime than the objects' states, since it's only allocated + * while preparing, checking or committing the update, while object + * states are allocated when preparing the update and kept alive as long + * as they are active in the device. + * + * Their respective lifetimes are: + * + * - at driver initialization time, the driver calls + * drm_mode_config_create_initial_state() to allocate an initial, + * pristine, state for each object and stores it in the objects state + * pointer. Historically, this was one of drm_mode_config_reset() job, + * so one might still encounter it in a driver. + * + * - When resuming from suspend, drm_mode_config_reset() resets the + * software and hardware state to a known default and stores it in the + * object's state pointer. Not all objects are affected by + * drm_mode_config_reset() though. + * + * - whenever a new update is needed: + * + * + drm_atomic_commit_alloc() allocates a new &drm_atomic_commit + * instance. + * + * + The code triggering the commit (ioctl, client modeset, + * drm_atomic_helper_reset_crtc(), etc.) copies the current active + * state of all entities affected by the update into this new + * &drm_atomic_commit using drm_atomic_get_plane_state(), + * drm_atomic_get_crtc_state(), drm_atomic_get_connector_state(), or + * drm_atomic_get_private_obj_state(). This new state can then be + * modified. + * + * At that point, &drm_atomic_commit stores three state pointers for + * any affected entity: the "old" and "new" states, and + * state_to_destroy. The old state is the state currently active in + * the hardware, which is either the one initialized by reset() or a + * newer one if a commit has been made. The new state is the state + * we just allocated and we might eventually commit to the hardware. + * The state_to_destroy points to the state we'll eventually have to + * free when the drm_atomic_commit will be destroyed, and points to + * the new state for now since the old state is still the active + * state. + * + * + After the calling code populated the commit with the entities + * states, it updates the new states with the new values we need to + * commit. The new commit instance is now ready. + * + * + Then we have two branches depending on the calling code intent: + * + * - If the calling code only wants to check that the commit would + * work (for example because of the DRM_MODE_ATOMIC_TEST_ONLY + * flag). It calls drm_atomic_check_only(), which in turn checks + * all these states by invoking atomic_check on all affected + * pipeline stages. + * + * - If the calling code actually wants to trigger a commit, it + * calls drm_atomic_commit(). The first stage is the check + * mentioned above, and if the check is successful, it performs + * the commit. Part of the commit is a call to + * drm_atomic_helper_swap_state() which turns the new states into + * the active states. After swapping states, each object's state + * pointer now refers to the formerly new state. The + * state_to_destroy now refers to the formerly old state. + * + * + Once done, and when the last reference to our &drm_atomic_commit + * is given up through drm_atomic_commit_put(), it calls + * __drm_atomic_commit_free(). In turn, __drm_atomic_commit_free() + * calls drm_atomic_commit_clear() that will free all + * state_to_destroy (ie. old states), and it finally frees + * &drm_atomic_commit instance. + * + * + Now, we don't have any active &drm_atomic_commit anymore, and + * only the entity active states remain allocated. + */ + void __drm_crtc_commit_free(struct kref *kref) { struct drm_crtc_commit *commit = diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c index 51f39edc31ed..285aac3554df 100644 --- a/drivers/gpu/drm/drm_atomic_helper.c +++ b/drivers/gpu/drm/drm_atomic_helper.c @@ -28,6 +28,7 @@ #include <linux/export.h> #include <linux/dma-fence.h> #include <linux/ktime.h> +#include <linux/media-bus-format.h> #include <drm/drm_atomic.h> #include <drm/drm_atomic_helper.h> @@ -737,6 +738,11 @@ drm_atomic_helper_check_modeset(struct drm_device *dev, if (old_connector_state->max_requested_bpc != new_connector_state->max_requested_bpc) new_crtc_state->connectors_changed = true; + + if (old_connector_state->color_format != + new_connector_state->color_format) + new_crtc_state->connectors_changed = true; + } if (funcs->atomic_check) @@ -4102,3 +4108,83 @@ drm_atomic_helper_bridge_propagate_bus_fmt(struct drm_bridge *bridge, return input_fmts; } EXPORT_SYMBOL(drm_atomic_helper_bridge_propagate_bus_fmt); + +/** + * drm_atomic_helper_bridge_get_hdmi_output_bus_fmts - helper implementing + * atomic_get_output_bus_fmts for HDMI + * @bridge: pointer to &struct drm_bridge + * @bridge_state: pointer to the current bridge state + * @crtc_state: pointer to the current CRTC state + * @conn_state: pointer to the current connector state + * @num_output_fmts: pointer to where the number of entries in the returned array + * will be stored. Set to 0 if unsuccessful. + * + * Common implementation for the &drm_bridge_funcs.atomic_get_output_bus_fmts + * operation that's applicable to HDMI connectors. + * + * Returns: a newly allocated array of u32 values of length \*@num_output_fmts, + * representing all the MEDIA_BUS_FMTS\_ for the current connector state's + * chosen HDMI output bits per compoennt, or %NULL if it fails to allocate one. + */ +u32 * +drm_atomic_helper_bridge_get_hdmi_output_bus_fmts(struct drm_bridge *bridge, + struct drm_bridge_state *bridge_state, + struct drm_crtc_state *crtc_state, + struct drm_connector_state *conn_state, + unsigned int *num_output_fmts) +{ + unsigned int num_fmts = 0; + u32 *out_fmts; + + /* + * bridge->supported_formats is a bit field of BIT(enum drm_output_color_format) + * values. The smallest hweight that is smaller than or equal to + * %DRM_OUTPUT_COLOR_FORMAT_COUNT will do for counting set bits here. + */ + BUILD_BUG_ON(const_true(DRM_OUTPUT_COLOR_FORMAT_COUNT > 8)); + out_fmts = kmalloc_array(hweight8(bridge->supported_formats), + sizeof(u32), GFP_KERNEL); + if (!out_fmts) { + *num_output_fmts = 0; + return NULL; + } + + switch (conn_state->hdmi.output_bpc) { + case 12: + if (bridge->supported_formats & BIT(DRM_OUTPUT_COLOR_FORMAT_RGB444)) + out_fmts[num_fmts++] = MEDIA_BUS_FMT_RGB121212_1X36; + if (bridge->supported_formats & BIT(DRM_OUTPUT_COLOR_FORMAT_YCBCR444)) + out_fmts[num_fmts++] = MEDIA_BUS_FMT_YUV12_1X36; + if (bridge->supported_formats & BIT(DRM_OUTPUT_COLOR_FORMAT_YCBCR422)) + out_fmts[num_fmts++] = MEDIA_BUS_FMT_UYVY12_1X24; + if (bridge->supported_formats & BIT(DRM_OUTPUT_COLOR_FORMAT_YCBCR420)) + out_fmts[num_fmts++] = MEDIA_BUS_FMT_UYYVYY12_0_5X36; + break; + case 10: + if (bridge->supported_formats & BIT(DRM_OUTPUT_COLOR_FORMAT_RGB444)) + out_fmts[num_fmts++] = MEDIA_BUS_FMT_RGB101010_1X30; + if (bridge->supported_formats & BIT(DRM_OUTPUT_COLOR_FORMAT_YCBCR444)) + out_fmts[num_fmts++] = MEDIA_BUS_FMT_YUV10_1X30; + if (bridge->supported_formats & BIT(DRM_OUTPUT_COLOR_FORMAT_YCBCR422)) + out_fmts[num_fmts++] = MEDIA_BUS_FMT_UYVY10_1X20; + if (bridge->supported_formats & BIT(DRM_OUTPUT_COLOR_FORMAT_YCBCR420)) + out_fmts[num_fmts++] = MEDIA_BUS_FMT_UYYVYY10_0_5X30; + break; + default: + if (bridge->supported_formats & BIT(DRM_OUTPUT_COLOR_FORMAT_RGB444)) + out_fmts[num_fmts++] = MEDIA_BUS_FMT_RGB888_1X24; + if (bridge->supported_formats & BIT(DRM_OUTPUT_COLOR_FORMAT_YCBCR444)) + out_fmts[num_fmts++] = MEDIA_BUS_FMT_YUV8_1X24; + if (bridge->supported_formats & BIT(DRM_OUTPUT_COLOR_FORMAT_YCBCR422)) + out_fmts[num_fmts++] = MEDIA_BUS_FMT_UYVY8_1X16; + if (bridge->supported_formats & BIT(DRM_OUTPUT_COLOR_FORMAT_YCBCR420)) + out_fmts[num_fmts++] = MEDIA_BUS_FMT_UYYVYY8_0_5X24; + break; + } + + *num_output_fmts = num_fmts; + + return out_fmts; +} +EXPORT_SYMBOL(drm_atomic_helper_bridge_get_hdmi_output_bus_fmts); + diff --git a/drivers/gpu/drm/drm_atomic_state_helper.c b/drivers/gpu/drm/drm_atomic_state_helper.c index cc70508d4fdb..d90d1d7c9cf9 100644 --- a/drivers/gpu/drm/drm_atomic_state_helper.c +++ b/drivers/gpu/drm/drm_atomic_state_helper.c @@ -63,7 +63,7 @@ */ /** - * __drm_atomic_helper_crtc_state_reset - reset the CRTC state + * __drm_atomic_helper_crtc_state_init - Initialize the CRTC state * @crtc_state: atomic CRTC state, must not be NULL * @crtc: CRTC object, must not be NULL * @@ -71,13 +71,13 @@ * values. This is useful for drivers that subclass the CRTC state. */ void -__drm_atomic_helper_crtc_state_reset(struct drm_crtc_state *crtc_state, - struct drm_crtc *crtc) +__drm_atomic_helper_crtc_state_init(struct drm_crtc_state *crtc_state, + struct drm_crtc *crtc) { crtc_state->crtc = crtc; crtc_state->background_color = DRM_ARGB64_PREP(0xffff, 0, 0, 0); } -EXPORT_SYMBOL(__drm_atomic_helper_crtc_state_reset); +EXPORT_SYMBOL(__drm_atomic_helper_crtc_state_init); /** * __drm_atomic_helper_crtc_reset - reset state on CRTC @@ -96,7 +96,7 @@ __drm_atomic_helper_crtc_reset(struct drm_crtc *crtc, struct drm_crtc_state *crtc_state) { if (crtc_state) - __drm_atomic_helper_crtc_state_reset(crtc_state, crtc); + __drm_atomic_helper_crtc_state_init(crtc_state, crtc); if (drm_dev_has_vblank(crtc->dev)) drm_crtc_vblank_reset(crtc); @@ -125,6 +125,31 @@ void drm_atomic_helper_crtc_reset(struct drm_crtc *crtc) EXPORT_SYMBOL(drm_atomic_helper_crtc_reset); /** + * drm_atomic_helper_crtc_create_state - default &drm_crtc_funcs.atomic_create_state hook for crtcs + * @crtc: crtc object + * + * Allocates and initializes pristine @drm_crtc_state. + * + * This is useful for drivers that don't subclass @drm_crtc_state. + * + * RETURNS: + * Pointer to new crtc state, or ERR_PTR on failure. + */ +struct drm_crtc_state *drm_atomic_helper_crtc_create_state(struct drm_crtc *crtc) +{ + struct drm_crtc_state *state; + + state = kzalloc_obj(*state); + if (!state) + return ERR_PTR(-ENOMEM); + + __drm_atomic_helper_crtc_state_init(state, crtc); + + return state; +} +EXPORT_SYMBOL(drm_atomic_helper_crtc_create_state); + +/** * __drm_atomic_helper_crtc_duplicate_state - copy atomic CRTC state * @crtc: CRTC object * @state: atomic CRTC state @@ -237,15 +262,15 @@ void drm_atomic_helper_crtc_destroy_state(struct drm_crtc *crtc, EXPORT_SYMBOL(drm_atomic_helper_crtc_destroy_state); /** - * __drm_atomic_helper_plane_state_reset - resets plane state to default values + * __drm_atomic_helper_plane_state_init - Initialize the plane state * @plane_state: atomic plane state, must not be NULL * @plane: plane object, must not be NULL * * Initializes the newly allocated @plane_state with default - * values. This is useful for drivers that subclass the CRTC state. + * values. This is useful for drivers that subclass the plane state. */ -void __drm_atomic_helper_plane_state_reset(struct drm_plane_state *plane_state, - struct drm_plane *plane) +void __drm_atomic_helper_plane_state_init(struct drm_plane_state *plane_state, + struct drm_plane *plane) { u64 val; @@ -297,7 +322,7 @@ void __drm_atomic_helper_plane_state_reset(struct drm_plane_state *plane_state, plane_state->hotspot_y = val; } } -EXPORT_SYMBOL(__drm_atomic_helper_plane_state_reset); +EXPORT_SYMBOL(__drm_atomic_helper_plane_state_init); /** * __drm_atomic_helper_plane_reset - reset state on plane @@ -305,7 +330,7 @@ EXPORT_SYMBOL(__drm_atomic_helper_plane_state_reset); * @plane_state: plane state to assign * * Initializes the newly allocated @plane_state and assigns it to - * the &drm_crtc->state pointer of @plane, usually required when + * the &drm_plane->state pointer of @plane, usually required when * initializing the drivers or when called from the &drm_plane_funcs.reset * hook. * @@ -315,7 +340,7 @@ void __drm_atomic_helper_plane_reset(struct drm_plane *plane, struct drm_plane_state *plane_state) { if (plane_state) - __drm_atomic_helper_plane_state_reset(plane_state, plane); + __drm_atomic_helper_plane_state_init(plane_state, plane); plane->state = plane_state; } @@ -341,6 +366,31 @@ void drm_atomic_helper_plane_reset(struct drm_plane *plane) EXPORT_SYMBOL(drm_atomic_helper_plane_reset); /** + * drm_atomic_helper_plane_create_state - default &drm_plane_funcs.atomic_create_state hook for planes + * @plane: plane object + * + * Allocates and initializes pristine @drm_plane_state. + * + * This is useful for drivers that don't subclass @drm_plane_state. + * + * RETURNS: + * Pointer to new plane state, or ERR_PTR on failure. + */ +struct drm_plane_state *drm_atomic_helper_plane_create_state(struct drm_plane *plane) +{ + struct drm_plane_state *state; + + state = kzalloc_obj(*state); + if (!state) + return ERR_PTR(-ENOMEM); + + __drm_atomic_helper_plane_state_init(state, plane); + + return state; +} +EXPORT_SYMBOL(drm_atomic_helper_plane_create_state); + +/** * __drm_atomic_helper_plane_duplicate_state - copy atomic plane state * @plane: plane object * @state: atomic plane state @@ -426,20 +476,20 @@ void drm_atomic_helper_plane_destroy_state(struct drm_plane *plane, EXPORT_SYMBOL(drm_atomic_helper_plane_destroy_state); /** - * __drm_atomic_helper_connector_state_reset - reset the connector state + * __drm_atomic_helper_connector_state_init - Initialize the connector state * @conn_state: atomic connector state, must not be NULL - * @connector: connectotr object, must not be NULL + * @connector: connector object, must not be NULL * * Initializes the newly allocated @conn_state with default * values. This is useful for drivers that subclass the connector state. */ void -__drm_atomic_helper_connector_state_reset(struct drm_connector_state *conn_state, - struct drm_connector *connector) +__drm_atomic_helper_connector_state_init(struct drm_connector_state *conn_state, + struct drm_connector *connector) { conn_state->connector = connector; } -EXPORT_SYMBOL(__drm_atomic_helper_connector_state_reset); +EXPORT_SYMBOL(__drm_atomic_helper_connector_state_init); /** * __drm_atomic_helper_connector_reset - reset state on connector @@ -458,7 +508,7 @@ __drm_atomic_helper_connector_reset(struct drm_connector *connector, struct drm_connector_state *conn_state) { if (conn_state) - __drm_atomic_helper_connector_state_reset(conn_state, connector); + __drm_atomic_helper_connector_state_init(conn_state, connector); connector->state = conn_state; } @@ -485,6 +535,32 @@ void drm_atomic_helper_connector_reset(struct drm_connector *connector) EXPORT_SYMBOL(drm_atomic_helper_connector_reset); /** + * drm_atomic_helper_connector_create_state - default &drm_connector_funcs.atomic_create_state hook for connectors + * @connector: connector object + * + * Allocates and initializes pristine @drm_connector_state. + * + * This is useful for drivers that don't subclass @drm_connector_state. + * + * RETURNS: + * Pointer to new connector state, or ERR_PTR on failure. + */ +struct drm_connector_state * +drm_atomic_helper_connector_create_state(struct drm_connector *connector) +{ + struct drm_connector_state *state; + + state = kzalloc_obj(*state); + if (!state) + return ERR_PTR(-ENOMEM); + + __drm_atomic_helper_connector_state_init(state, connector); + + return state; +} +EXPORT_SYMBOL(drm_atomic_helper_connector_create_state); + +/** * drm_atomic_helper_connector_tv_margins_reset - Resets TV connector properties * @connector: DRM connector * @@ -731,8 +807,6 @@ void __drm_atomic_helper_private_obj_create_state(struct drm_private_obj *obj, { if (state) state->obj = obj; - - obj->state = state; } EXPORT_SYMBOL(__drm_atomic_helper_private_obj_create_state); @@ -799,7 +873,7 @@ EXPORT_SYMBOL(drm_atomic_helper_bridge_duplicate_state); * @state: bridge state to destroy * * Destroys a bridge state previously created by - * &drm_atomic_helper_bridge_reset() or + * &drm_atomic_helper_bridge_create_state() or * &drm_atomic_helper_bridge_duplicate_state(). This helper is meant to be * used as a bridge &drm_bridge_funcs.atomic_destroy_state hook for bridges * that don't subclass the bridge state. @@ -812,35 +886,39 @@ void drm_atomic_helper_bridge_destroy_state(struct drm_bridge *bridge, EXPORT_SYMBOL(drm_atomic_helper_bridge_destroy_state); /** - * __drm_atomic_helper_bridge_reset() - Initialize a bridge state to its + * __drm_atomic_helper_bridge_state_init() - Initialize a bridge state to its * default - * @bridge: the bridge this state refers to * @state: bridge state to initialize + * @bridge: the bridge this state refers to + * + * @state is assumed to be zeroed. * * Initializes the bridge state to default values. This is meant to be called - * by the bridge &drm_bridge_funcs.atomic_reset hook for bridges that subclass - * the bridge state. + * by the bridge &drm_bridge_funcs.atomic_create_state hook for bridges that + * subclass the bridge state. */ -void __drm_atomic_helper_bridge_reset(struct drm_bridge *bridge, - struct drm_bridge_state *state) +void __drm_atomic_helper_bridge_state_init(struct drm_bridge_state *state, + struct drm_bridge *bridge) { - memset(state, 0, sizeof(*state)); __drm_atomic_helper_private_obj_create_state(&bridge->base, &state->base); state->bridge = bridge; } -EXPORT_SYMBOL(__drm_atomic_helper_bridge_reset); +EXPORT_SYMBOL(__drm_atomic_helper_bridge_state_init); /** - * drm_atomic_helper_bridge_reset() - Allocate and initialize a bridge state - * to its default - * @bridge: the bridge this state refers to + * drm_atomic_helper_bridge_create_state - default + * &drm_bridge_funcs.atomic_create_state hook for bridges + * @bridge: bridge object + * + * Allocates and initializes pristine @drm_bridge_state. + * + * This is useful for drivers that don't subclass @drm_bridge_state. * - * Allocates the bridge state and initializes it to default values. This helper - * is meant to be used as a bridge &drm_bridge_funcs.atomic_reset hook for - * bridges that don't subclass the bridge state. + * RETURNS: + * Pointer to new bridge state, or ERR_PTR on failure. */ struct drm_bridge_state * -drm_atomic_helper_bridge_reset(struct drm_bridge *bridge) +drm_atomic_helper_bridge_create_state(struct drm_bridge *bridge) { struct drm_bridge_state *bridge_state; @@ -848,7 +926,7 @@ drm_atomic_helper_bridge_reset(struct drm_bridge *bridge) if (!bridge_state) return ERR_PTR(-ENOMEM); - __drm_atomic_helper_bridge_reset(bridge, bridge_state); + __drm_atomic_helper_bridge_state_init(bridge_state, bridge); return bridge_state; } -EXPORT_SYMBOL(drm_atomic_helper_bridge_reset); +EXPORT_SYMBOL(drm_atomic_helper_bridge_create_state); diff --git a/drivers/gpu/drm/drm_atomic_uapi.c b/drivers/gpu/drm/drm_atomic_uapi.c index e997917819e8..1050dddadb17 100644 --- a/drivers/gpu/drm/drm_atomic_uapi.c +++ b/drivers/gpu/drm/drm_atomic_uapi.c @@ -960,6 +960,8 @@ static int drm_atomic_connector_set_property(struct drm_connector *connector, state->privacy_screen_sw_state = val; } else if (property == connector->broadcast_rgb_property) { state->hdmi.broadcast_rgb = val; + } else if (property == connector->color_format_property) { + state->color_format = val; } else if (connector->funcs->atomic_set_property) { return connector->funcs->atomic_set_property(connector, state, property, val); @@ -1045,6 +1047,8 @@ drm_atomic_connector_get_property(struct drm_connector *connector, *val = state->privacy_screen_sw_state; } else if (property == connector->broadcast_rgb_property) { *val = state->hdmi.broadcast_rgb; + } else if (property == connector->color_format_property) { + *val = state->color_format; } else if (connector->funcs->atomic_get_property) { return connector->funcs->atomic_get_property(connector, state, property, val); diff --git a/drivers/gpu/drm/drm_bridge.c b/drivers/gpu/drm/drm_bridge.c index 687b36eea0c7..6ead9b75ae3d 100644 --- a/drivers/gpu/drm/drm_bridge.c +++ b/drivers/gpu/drm/drm_bridge.c @@ -198,6 +198,46 @@ * driver. */ +/** + * DOC: bridge chain format selection + * + * A bridge chain, from display output processor to connector, may contain + * bridges capable of converting between bus formats on their inputs, and + * output formats on their outputs. For example, a bridge may be able to convert + * from RGB to YCbCr 4:4:4, and pass through YCbCr 4:2:0 as-is, but not convert + * from RGB to YCbCr 4:2:0. This means not all input formats map to all output + * formats. + * + * Further adding to this, a desired output color format, as specified with the + * "color format" DRM property, might not correspond 1:1 to what the display + * driver should set at its output. The bridge chain it feeds into may only be + * able to reach the desired output format, if a conversion from a different + * starting format is performed. + * + * To deal with this complexity, the recursive bridge chain bus format selection + * logic starts with the last bridge in the chain, usually the connector, and + * then recursively walks the chain of bridges backwards to the first bridge, + * trying to find a path. + * + * For a display driver to work in such a scenario, it should read the first + * bridge's bridge state to figure out which bus format the chain resolved to. + * If the first bridge's input format resolved to %MEDIA_BUS_FMT_FIXED, then its + * output format should be used. + * + * Special handling is done for HDMI as it relates to format selection. Instead + * of directly using the "color format" DRM property for bridge chains that end + * in HDMI bridges, the bridge chain format selection logic will trust the logic + * that set the HDMI output format. For the common HDMI state helper + * functionality, this means that %DRM_CONNECTOR_COLOR_FORMAT_AUTO will allow + * fallbacks to YCBCr 4:2:0 if the bandwidth requirements would otherwise be too + * high but the mode and connector allow it. + * + * For bridge chains that do not end in an HDMI bridge, + * %DRM_CONNECTOR_COLOR_FORMAT_AUTO will be satisfied with the first output + * format on the last bridge for which it can find a path back to the first + * bridge. + */ + /* Protect bridge_list and bridge_lingering_list */ static DEFINE_MUTEX(bridge_lock); static LIST_HEAD(bridge_list); @@ -417,6 +457,7 @@ void drm_bridge_add(struct drm_bridge *bridge) if (!list_empty(&bridge->list)) list_del_init(&bridge->list); + mutex_init(&bridge->hpd_state_mutex); mutex_init(&bridge->hpd_mutex); if (bridge->ops & DRM_BRIDGE_OP_HDMI) @@ -469,6 +510,7 @@ void drm_bridge_remove(struct drm_bridge *bridge) mutex_unlock(&bridge_lock); mutex_destroy(&bridge->hpd_mutex); + mutex_destroy(&bridge->hpd_state_mutex); drm_bridge_put(bridge); } @@ -500,7 +542,7 @@ drm_bridge_atomic_create_priv_state(struct drm_private_obj *obj) struct drm_bridge *bridge = drm_priv_to_bridge(obj); struct drm_bridge_state *state; - state = bridge->funcs->atomic_reset(bridge); + state = bridge->funcs->atomic_create_state(bridge); if (IS_ERR(state)) return ERR_CAST(state); @@ -515,7 +557,7 @@ static const struct drm_private_state_funcs drm_bridge_priv_state_funcs = { static bool drm_bridge_is_atomic(struct drm_bridge *bridge) { - return bridge->funcs->atomic_reset != NULL; + return bridge->funcs->atomic_create_state != NULL; } /** @@ -1149,6 +1191,47 @@ static int select_bus_fmt_recursive(struct drm_bridge *first_bridge, return ret; } +static bool __pure bus_format_is_color_fmt(u32 bus_fmt, enum drm_connector_color_format fmt) +{ + if (fmt == DRM_CONNECTOR_COLOR_FORMAT_AUTO) + return true; + + switch (bus_fmt) { + case MEDIA_BUS_FMT_FIXED: + return true; + case MEDIA_BUS_FMT_RGB888_1X24: + case MEDIA_BUS_FMT_RGB101010_1X30: + case MEDIA_BUS_FMT_RGB121212_1X36: + case MEDIA_BUS_FMT_RGB161616_1X48: + return fmt == DRM_CONNECTOR_COLOR_FORMAT_RGB444; + case MEDIA_BUS_FMT_YUV8_1X24: + case MEDIA_BUS_FMT_YUV10_1X30: + case MEDIA_BUS_FMT_YUV12_1X36: + case MEDIA_BUS_FMT_YUV16_1X48: + return fmt == DRM_CONNECTOR_COLOR_FORMAT_YCBCR444; + case MEDIA_BUS_FMT_UYVY8_1X16: + case MEDIA_BUS_FMT_VYUY8_1X16: + case MEDIA_BUS_FMT_YUYV8_1X16: + case MEDIA_BUS_FMT_YVYU8_1X16: + case MEDIA_BUS_FMT_UYVY10_1X20: + case MEDIA_BUS_FMT_YUYV10_1X20: + case MEDIA_BUS_FMT_VYUY10_1X20: + case MEDIA_BUS_FMT_YVYU10_1X20: + case MEDIA_BUS_FMT_UYVY12_1X24: + case MEDIA_BUS_FMT_VYUY12_1X24: + case MEDIA_BUS_FMT_YUYV12_1X24: + case MEDIA_BUS_FMT_YVYU12_1X24: + return fmt == DRM_CONNECTOR_COLOR_FORMAT_YCBCR422; + case MEDIA_BUS_FMT_UYYVYY8_0_5X24: + case MEDIA_BUS_FMT_UYYVYY10_0_5X30: + case MEDIA_BUS_FMT_UYYVYY12_0_5X36: + case MEDIA_BUS_FMT_UYYVYY16_0_5X48: + return fmt == DRM_CONNECTOR_COLOR_FORMAT_YCBCR420; + default: + return false; + } +} + /* * This function is called by &drm_atomic_bridge_chain_check() just before * calling &drm_bridge_funcs.atomic_check() on all elements of the chain. @@ -1192,6 +1275,7 @@ drm_atomic_bridge_chain_select_bus_fmts(struct drm_bridge *bridge, struct drm_encoder *encoder = bridge->encoder; struct drm_bridge_state *last_bridge_state; unsigned int i, num_out_bus_fmts = 0; + enum drm_connector_color_format fmt; u32 *out_bus_fmts; int ret = 0; @@ -1233,11 +1317,31 @@ drm_atomic_bridge_chain_select_bus_fmts(struct drm_bridge *bridge, out_bus_fmts[0] = MEDIA_BUS_FMT_FIXED; } + /* + * Instead of directly accessing conn_state.color_format, call into a + * connector function that allows connector implementations (e.g. for + * bridge connectors including HDMI bridges, where the HDMI helpers will + * have already chosen an appropriate output format) to override the + * selected format. + */ + fmt = drm_connector_get_color_format(conn_state); + for (i = 0; i < num_out_bus_fmts; i++) { + if (!bus_format_is_color_fmt(out_bus_fmts[i], fmt)) { + drm_dbg_kms(last_bridge->dev, + "Skipping bus format 0x%04x as it doesn't match format %d\n", + out_bus_fmts[i], fmt); + ret = -ENOTSUPP; + continue; + } ret = select_bus_fmt_recursive(bridge, last_bridge, crtc_state, conn_state, out_bus_fmts[i]); - if (ret != -ENOTSUPP) + if (ret != -ENOTSUPP) { + drm_dbg_kms(last_bridge->dev, + "Found bridge chain ending with bus format 0x%04x\n", + out_bus_fmts[i]); break; + } } kfree(out_bus_fmts); @@ -1451,19 +1555,25 @@ void drm_bridge_hpd_enable(struct drm_bridge *bridge, if (!(bridge->ops & DRM_BRIDGE_OP_HPD)) return; + mutex_lock(&bridge->hpd_state_mutex); + mutex_lock(&bridge->hpd_mutex); - if (WARN(bridge->hpd_cb, "Hot plug detection already enabled\n")) + if (WARN(bridge->hpd_cb, "Hot plug detection already enabled\n")) { + mutex_unlock(&bridge->hpd_mutex); goto unlock; + } bridge->hpd_cb = cb; bridge->hpd_data = data; + mutex_unlock(&bridge->hpd_mutex); + if (bridge->funcs->hpd_enable) bridge->funcs->hpd_enable(bridge); unlock: - mutex_unlock(&bridge->hpd_mutex); + mutex_unlock(&bridge->hpd_state_mutex); } EXPORT_SYMBOL_GPL(drm_bridge_hpd_enable); @@ -1484,13 +1594,15 @@ void drm_bridge_hpd_disable(struct drm_bridge *bridge) if (!(bridge->ops & DRM_BRIDGE_OP_HPD)) return; - mutex_lock(&bridge->hpd_mutex); + mutex_lock(&bridge->hpd_state_mutex); if (bridge->funcs->hpd_disable) bridge->funcs->hpd_disable(bridge); + mutex_lock(&bridge->hpd_mutex); bridge->hpd_cb = NULL; bridge->hpd_data = NULL; mutex_unlock(&bridge->hpd_mutex); + mutex_unlock(&bridge->hpd_state_mutex); } EXPORT_SYMBOL_GPL(drm_bridge_hpd_disable); diff --git a/drivers/gpu/drm/drm_buddy.c b/drivers/gpu/drm/drm_buddy.c index faa025498de4..1536e59c6fe7 100644 --- a/drivers/gpu/drm/drm_buddy.c +++ b/drivers/gpu/drm/drm_buddy.c @@ -47,29 +47,23 @@ void drm_buddy_print(struct gpu_buddy *mm, struct drm_printer *p) mm->chunk_size >> 10, mm->size >> 20, mm->avail >> 20, mm->clear_avail >> 20); for (order = mm->max_order; order >= 0; order--) { - struct gpu_buddy_block *block, *tmp; - struct rb_root *root; - u64 count = 0, free; - unsigned int tree; - - for_each_free_tree(tree) { - root = &mm->free_trees[tree][order]; - - rbtree_postorder_for_each_entry_safe(block, tmp, root, rb) { - BUG_ON(!gpu_buddy_block_is_free(block)); - count++; - } - } + u64 free_count = mm->free_scoreboard[order]; + u64 used_count = mm->used_scoreboard[order]; + u64 block_size = mm->chunk_size << order; + u64 free = free_count * block_size; + u64 used = used_count * block_size; drm_printf(p, "order-%2d ", order); - free = count * (mm->chunk_size << order); - if (free < SZ_1M) - drm_printf(p, "free: %8llu KiB", free >> 10); + if (block_size < SZ_1M) + drm_printf(p, "free: %8llu KiB, used: %8llu KiB", + free >> 10, used >> 10); else - drm_printf(p, "free: %8llu MiB", free >> 20); + drm_printf(p, "free: %8llu MiB, used: %8llu MiB", + free >> 20, used >> 20); - drm_printf(p, ", blocks: %llu\n", count); + drm_printf(p, ", free_blocks: %llu, used_blocks: %llu\n", + free_count, used_count); } } EXPORT_SYMBOL(drm_buddy_print); diff --git a/drivers/gpu/drm/drm_colorop.c b/drivers/gpu/drm/drm_colorop.c index 65fec53f70fa..682fcc651525 100644 --- a/drivers/gpu/drm/drm_colorop.c +++ b/drivers/gpu/drm/drm_colorop.c @@ -498,15 +498,15 @@ void drm_colorop_atomic_destroy_state(struct drm_colorop *colorop, } /** - * __drm_colorop_state_reset - resets colorop state to default values + * __drm_colorop_state_init - Initializes colorop state to default values * @colorop_state: atomic colorop state, must not be NULL * @colorop: colorop object, must not be NULL * * Initializes the newly allocated @colorop_state with default - * values. This is useful for drivers that subclass the CRTC state. + * values. This is useful for drivers that subclass the colorop state. */ -static void __drm_colorop_state_reset(struct drm_colorop_state *colorop_state, - struct drm_colorop *colorop) +static void __drm_colorop_state_init(struct drm_colorop_state *colorop_state, + struct drm_colorop *colorop) { u64 val; @@ -536,14 +536,37 @@ static void __drm_colorop_state_reset(struct drm_colorop_state *colorop_state, } /** + * drm_atomic_helper_colorop_create_state - Allocates and initializes colorop atomic state + * @colorop: drm colorop + * + * Initializes a pristine @drm_colorop_state. + * + * RETURNS: + * Pointer to new colorop state, or ERR_PTR on failure. + */ +struct drm_colorop_state * +drm_atomic_helper_colorop_create_state(struct drm_colorop *colorop) +{ + struct drm_colorop_state *state; + + state = kzalloc_obj(*state); + if (!state) + return ERR_PTR(-ENOMEM); + + __drm_colorop_state_init(state, colorop); + + return state; +} + +/** * __drm_colorop_reset - reset state on colorop * @colorop: drm colorop * @colorop_state: colorop state to assign * - * Initializes the newly allocated @colorop_state and assigns it to - * the &drm_crtc->state pointer of @colorop, usually required when - * initializing the drivers or when called from the &drm_colorop_funcs.reset - * hook. + * Initializes the newly allocated @colorop_state and assigns it to the + * &drm_colorop->state pointer of @colorop, usually required when + * initializing the drivers or when called from the + * &drm_colorop_funcs.reset hook. * * This is useful for drivers that subclass the colorop state. */ @@ -551,7 +574,7 @@ static void __drm_colorop_reset(struct drm_colorop *colorop, struct drm_colorop_state *colorop_state) { if (colorop_state) - __drm_colorop_state_reset(colorop_state, colorop); + __drm_colorop_state_init(colorop_state, colorop); colorop->state = colorop_state; } diff --git a/drivers/gpu/drm/drm_connector.c b/drivers/gpu/drm/drm_connector.c index 11646453aaac..8b4baed060f3 100644 --- a/drivers/gpu/drm/drm_connector.c +++ b/drivers/gpu/drm/drm_connector.c @@ -618,8 +618,17 @@ int drmm_connector_hdmi_init(struct drm_device *dev, * drm_connector_attach_max_bpc_property() requires the * connector to have a state. */ - if (connector->funcs->reset) + if (connector->funcs->atomic_create_state) { + struct drm_connector_state *state; + + state = connector->funcs->atomic_create_state(connector); + if (IS_ERR(state)) + return PTR_ERR(state); + + connector->state = state; + } else if (connector->funcs->reset) { connector->funcs->reset(connector); + } drm_connector_attach_max_bpc_property(connector, 8, max_bpc); connector->max_bpc = max_bpc; @@ -627,6 +636,10 @@ int drmm_connector_hdmi_init(struct drm_device *dev, if (max_bpc > 8) drm_connector_attach_hdr_output_metadata_property(connector); + ret = drm_connector_attach_color_format_property(connector, supported_formats); + if (ret) + return ret; + connector->hdmi.funcs = hdmi_funcs; return 0; @@ -1176,6 +1189,7 @@ static const struct drm_prop_enum_list drm_link_status_enum_list[] = { static const struct drm_prop_enum_list drm_panel_type_enum_list[] = { { DRM_MODE_PANEL_TYPE_UNKNOWN, "unknown" }, { DRM_MODE_PANEL_TYPE_OLED, "OLED" }, + { DRM_MODE_PANEL_TYPE_LCD, "LCD" }, }; /** @@ -1388,6 +1402,18 @@ static const u32 hdmi_colorspaces = BIT(DRM_MODE_COLORIMETRY_DCI_P3_RGB_D65) | BIT(DRM_MODE_COLORIMETRY_DCI_P3_RGB_THEATER); +static const u32 hdmi_colorformats = + BIT(DRM_OUTPUT_COLOR_FORMAT_RGB444) | + BIT(DRM_OUTPUT_COLOR_FORMAT_YCBCR444) | + BIT(DRM_OUTPUT_COLOR_FORMAT_YCBCR422) | + BIT(DRM_OUTPUT_COLOR_FORMAT_YCBCR420); + +static const u32 dp_colorformats = + BIT(DRM_OUTPUT_COLOR_FORMAT_RGB444) | + BIT(DRM_OUTPUT_COLOR_FORMAT_YCBCR444) | + BIT(DRM_OUTPUT_COLOR_FORMAT_YCBCR422) | + BIT(DRM_OUTPUT_COLOR_FORMAT_YCBCR420); + /* * As per DP 1.4a spec, 2.2.5.7.5 VSC SDP Payload for Pixel Encoding/Colorimetry * Format Table 2-120 @@ -1508,7 +1534,7 @@ EXPORT_SYMBOL(drm_hdmi_connector_get_output_format_name); * never read back the value of "DPMS" because it can be incorrect. * panel_type: * Immutable enum property to indicate the type of connected panel. - * Possible values are "unknown" (default) and "OLED". + * Possible values are "unknown" (default), "OLED", and "LCD". * PATH: * Connector path property to identify how this sink is physically * connected. Used by DP MST. This should be set by calling @@ -2557,7 +2583,8 @@ EXPORT_SYMBOL(drm_mode_create_aspect_ratio_property); * conversion matrix and convert to the appropriate quantization * range. * The variants BT2020_RGB and BT2020_YCC are equivalent and the - * driver chooses between RGB and YCbCr on its own. + * driver chooses between RGB and YCbCr based on the color format + * property. * * SMPTE_170M_YCC: * BT709_YCC: @@ -2936,6 +2963,149 @@ int drm_connector_attach_colorspace_property(struct drm_connector *connector) EXPORT_SYMBOL(drm_connector_attach_colorspace_property); /** + * DOC: Color format + * + * The connector "color format" property allows userspace to request a specific + * color model on the output of the connector. Not all values listed by the + * property are guaranteed to work for every sink; rather, it is an optimistic + * listing of color formats that the source could output depending on + * circumstances. + * + * Whether it actually can output a certain color format is determined during + * the atomic check phase. Consequently, a userspace application that sets the + * color format to a value other than "AUTO" should check whether its atomic + * commit succeeded. + * + * Possible values for "color format": + * + * "AUTO": + * The driver or display protocol helpers should pick a suitable color + * format. All implementations of a specific display protocol will behave + * the same way with "AUTO", but different display protocols do not + * necessarily have the same "AUTO" semantics. + * + * For HDMI connectors, "AUTO" picks RGB, but falls back to YUV 4:2:0 if + * the bandwidth required for full-scale RGB is not available, or the mode + * is YUV 4:2:0-only, as long as the mode, source, and sink all support + * YUV 4:2:0. + * "RGB": + * RGB output format. The quantization range (limited/full) depends on the + * value of the "Broadcast RGB" property if it is present on the connector. + * "YUV 4:4:4": + * YUV 4:4:4 (a.k.a. YCbCr 4:4:4) output format. Chroma is not subsampled. + * The quantization range defaults to limited. + * "YUV 4:2:2": + * YUV 4:2:2 (a.k.a. YCbCr 4:2:2) output format. Chroma has half the + * horizontal resolution of Luma. The quantization range defaults to + * limited. + * "YUV 4:2:0": + * YUV 4:2:0 (a.k.a. YCbCr 4:2:0) output format. Chroma has half the + * horizontal and vertical resolution of Luma. The quantization range + * defaults to limited. + * + * A sink may only support some color formats in specific modes and at specific + * bit depths. The atomic modesetting API should be used to set a working + * configuration in one go, as an unsupported combination of parameters is + * rejected. + */ + +/** + * drm_connector_attach_color_format_property - create and attach color format property + * @connector: connector to create the color format property on + * @supported_color_formats: bitmask of bit-shifted &enum drm_output_color_format + * values the connector supports + * + * Called by a driver to create a color format property. The property is + * attached to the connector automatically on success. + * + * @supported_color_formats should only include color formats the connector + * type can actually support. + * + * Returns: + * 0 on success, negative errno on error + */ +int drm_connector_attach_color_format_property(struct drm_connector *connector, + unsigned long supported_color_formats) +{ + struct drm_device *dev = connector->dev; + struct drm_prop_enum_list enum_list[DRM_CONNECTOR_COLOR_FORMAT_COUNT]; + unsigned int i = 0; + unsigned long fmt; + + if (connector->color_format_property) + return 0; + + if (!supported_color_formats) { + drm_err(dev, "No supported color formats provided on [CONNECTOR:%d:%s]\n", + connector->base.id, connector->name); + return -EINVAL; + } + + if (supported_color_formats & ~GENMASK(DRM_OUTPUT_COLOR_FORMAT_COUNT - 1, 0)) { + drm_err(dev, "Unknown color formats provided on [CONNECTOR:%d:%s]\n", + connector->base.id, connector->name); + return -EINVAL; + } + + switch (connector->connector_type) { + case DRM_MODE_CONNECTOR_HDMIA: + case DRM_MODE_CONNECTOR_HDMIB: + if (supported_color_formats & ~hdmi_colorformats) { + drm_err(dev, "Color formats not allowed for HDMI on [CONNECTOR:%d:%s]\n", + connector->base.id, connector->name); + return -EINVAL; + } + break; + case DRM_MODE_CONNECTOR_DisplayPort: + case DRM_MODE_CONNECTOR_eDP: + if (supported_color_formats & ~dp_colorformats) { + drm_err(dev, "Color formats not allowed for DP on [CONNECTOR:%d:%s]\n", + connector->base.id, connector->name); + return -EINVAL; + } + break; + } + + enum_list[0].name = "AUTO"; + enum_list[0].type = DRM_CONNECTOR_COLOR_FORMAT_AUTO; + + for_each_set_bit(fmt, &supported_color_formats, DRM_OUTPUT_COLOR_FORMAT_COUNT) { + switch (fmt) { + case DRM_OUTPUT_COLOR_FORMAT_RGB444: + enum_list[++i].type = DRM_CONNECTOR_COLOR_FORMAT_RGB444; + break; + case DRM_OUTPUT_COLOR_FORMAT_YCBCR444: + enum_list[++i].type = DRM_CONNECTOR_COLOR_FORMAT_YCBCR444; + break; + case DRM_OUTPUT_COLOR_FORMAT_YCBCR422: + enum_list[++i].type = DRM_CONNECTOR_COLOR_FORMAT_YCBCR422; + break; + case DRM_OUTPUT_COLOR_FORMAT_YCBCR420: + enum_list[++i].type = DRM_CONNECTOR_COLOR_FORMAT_YCBCR420; + break; + default: + drm_warn(dev, "Unknown supported format %ld on [CONNECTOR:%d:%s]\n", + fmt, connector->base.id, connector->name); + continue; + } + enum_list[i].name = drm_hdmi_connector_get_output_format_name(fmt); + } + + connector->color_format_property = + drm_property_create_enum(dev, DRM_MODE_PROP_ENUM, "color format", + enum_list, i + 1); + + if (!connector->color_format_property) + return -ENOMEM; + + drm_object_attach_property(&connector->base, connector->color_format_property, + DRM_CONNECTOR_COLOR_FORMAT_AUTO); + + return 0; +} +EXPORT_SYMBOL(drm_connector_attach_color_format_property); + +/** * drm_connector_atomic_hdr_metadata_equal - checks if the hdr metadata changed * @old_state: old connector state to compare * @new_state: new connector state to compare @@ -3530,6 +3700,22 @@ void drm_connector_oob_hotplug_event(struct fwnode_handle *connector_fwnode, } EXPORT_SYMBOL(drm_connector_oob_hotplug_event); +/** + * drm_connector_get_color_format - Return connector color format of @conn_state + * @conn_state: pointer to the &struct drm_connector_state to go check + * + */ +enum drm_connector_color_format +drm_connector_get_color_format(const struct drm_connector_state *conn_state) +{ + struct drm_connector *connector = conn_state->connector; + + if (connector->funcs->color_format) + return connector->funcs->color_format(conn_state); + + return conn_state->color_format; +} +EXPORT_SYMBOL(drm_connector_get_color_format); /** * DOC: Tile group diff --git a/drivers/gpu/drm/drm_displayid_internal.h b/drivers/gpu/drm/drm_displayid_internal.h index 4590d6a3d821..ef7e6c9bd4db 100644 --- a/drivers/gpu/drm/drm_displayid_internal.h +++ b/drivers/gpu/drm/drm_displayid_internal.h @@ -142,6 +142,30 @@ struct displayid_formula_timing_block { struct displayid_formula_timings_9 timings[]; } __packed; +#define DISPLAYID_DEVICE_TECH_UNSPECIFIED 0 +#define DISPLAYID_DEVICE_TECH_LCD 1 +#define DISPLAYID_DEVICE_TECH_OLED 2 + +#define DISPLAYID_DISPLAY_PARAMS_DEVICE_TECH GENMASK(6, 4) + +struct displayid_display_params_block { + struct displayid_block base; + __le16 horiz_image_size; + __le16 vert_image_size; + __le16 horiz_pixel_count; + __le16 vert_pixel_count; + u8 features; + u8 primary_color1[3]; + u8 primary_color2[3]; + u8 primary_color3[3]; + u8 white_point[3]; + __le16 max_luminance_full; + __le16 max_luminance_10; + __le16 min_luminance; + u8 color_depth_and_tech; /* [2:0] depth, [6:4] device tech, [7] theme */ + u8 gamma_eotf; +} __packed; + #define DISPLAYID_VESA_MSO_OVERLAP GENMASK(3, 0) #define DISPLAYID_VESA_MSO_MODE GENMASK(6, 5) diff --git a/drivers/gpu/drm/drm_draw_internal.h b/drivers/gpu/drm/drm_draw_internal.h index 261967145635..44ddcee4744c 100644 --- a/drivers/gpu/drm/drm_draw_internal.h +++ b/drivers/gpu/drm/drm_draw_internal.h @@ -7,7 +7,6 @@ #ifndef __DRM_DRAW_INTERNAL_H__ #define __DRM_DRAW_INTERNAL_H__ -#include <linux/font.h> #include <linux/types.h> struct iosys_map; @@ -18,12 +17,6 @@ static inline bool drm_draw_is_pixel_fg(const u8 *sbuf8, unsigned int spitch, in return (sbuf8[(y * spitch) + x / 8] & (0x80 >> (x % 8))) != 0; } -static inline const u8 *drm_draw_get_char_bitmap(const struct font_desc *font, - char c, size_t font_pitch) -{ - return font->data + (c * font->height) * font_pitch; -} - bool drm_draw_can_convert_from_xrgb8888(u32 format); u32 drm_draw_color_from_xrgb8888(u32 color, u32 format); diff --git a/drivers/gpu/drm/drm_drv.c b/drivers/gpu/drm/drm_drv.c index 675675480da4..1ff0bf7cba6a 100644 --- a/drivers/gpu/drm/drm_drv.c +++ b/drivers/gpu/drm/drm_drv.c @@ -342,7 +342,9 @@ void drm_minor_release(struct drm_minor *minor) * * platform_set_drvdata(pdev, drm); * - * drm_mode_config_reset(drm); + * ret = drm_mode_config_create_initial_state(drm); + * if (ret) + * return ret; * * ret = drm_dev_register(drm); * if (ret) diff --git a/drivers/gpu/drm/drm_dumb_buffers.c b/drivers/gpu/drm/drm_dumb_buffers.c index 2156dbe601c9..8e9ff17538e7 100644 --- a/drivers/gpu/drm/drm_dumb_buffers.c +++ b/drivers/gpu/drm/drm_dumb_buffers.c @@ -32,7 +32,6 @@ #include <drm/drm_print.h> #include "drm_crtc_internal.h" -#include "drm_internal.h" /** * DOC: overview diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c index df3c25bac761..07970e5b5f65 100644 --- a/drivers/gpu/drm/drm_edid.c +++ b/drivers/gpu/drm/drm_edid.c @@ -6713,32 +6713,82 @@ static void drm_reset_display_info(struct drm_connector *connector) info->source_physical_address = CEC_PHYS_ADDR_INVALID; memset(&info->amd_vsdb, 0, sizeof(info->amd_vsdb)); + + info->panel_type = DRM_MODE_PANEL_TYPE_UNKNOWN; +} + +static void drm_displayid_process_base_section_header(struct drm_connector *connector, + const struct displayid_iter *iter) +{ + struct drm_display_info *info = &connector->display_info; + + drm_dbg_kms(connector->dev, + "[CONNECTOR:%d:%s] DisplayID extension version 0x%02x, primary use 0x%02x\n", + connector->base.id, connector->name, + displayid_version(iter), + displayid_primary_use(iter)); + if (displayid_version(iter) == DISPLAY_ID_STRUCTURE_VER_20 && + (displayid_primary_use(iter) == PRIMARY_USE_HEAD_MOUNTED_VR || + displayid_primary_use(iter) == PRIMARY_USE_HEAD_MOUNTED_AR)) + info->non_desktop = true; +} + +static void +drm_displayid_parse_display_params(struct drm_connector *connector, + const struct displayid_block *block) +{ + struct drm_display_info *info = &connector->display_info; + const struct displayid_display_params_block *params = + (const struct displayid_display_params_block *)block; + u8 tech; + + if (block->num_bytes < sizeof(*params) - sizeof(params->base)) { + drm_dbg_kms(connector->dev, + "[CONNECTOR:%d:%s] DisplayID Display Parameters block too short (%u < %zu)\n", + connector->base.id, connector->name, + block->num_bytes, + sizeof(*params) - sizeof(params->base)); + return; + } + + tech = FIELD_GET(DISPLAYID_DISPLAY_PARAMS_DEVICE_TECH, + params->color_depth_and_tech); + + drm_dbg_kms(connector->dev, + "[CONNECTOR:%d:%s] DisplayID Display Parameters: device technology %s\n", + connector->base.id, connector->name, + tech == DISPLAYID_DEVICE_TECH_LCD ? "LCD" : + tech == DISPLAYID_DEVICE_TECH_OLED ? "OLED" : "unspecified"); + + switch (tech) { + case DISPLAYID_DEVICE_TECH_LCD: + info->panel_type = DRM_MODE_PANEL_TYPE_LCD; + break; + case DISPLAYID_DEVICE_TECH_OLED: + info->panel_type = DRM_MODE_PANEL_TYPE_OLED; + break; + default: + break; + } } static void update_displayid_info(struct drm_connector *connector, const struct drm_edid *drm_edid) { - struct drm_display_info *info = &connector->display_info; const struct displayid_block *block; struct displayid_iter iter; + bool base_section_header_processed = false; displayid_iter_edid_begin(drm_edid, &iter); displayid_iter_for_each(block, &iter) { - drm_dbg_kms(connector->dev, - "[CONNECTOR:%d:%s] DisplayID extension version 0x%02x, primary use 0x%02x\n", - connector->base.id, connector->name, - displayid_version(&iter), - displayid_primary_use(&iter)); - if (displayid_version(&iter) == DISPLAY_ID_STRUCTURE_VER_20 && - (displayid_primary_use(&iter) == PRIMARY_USE_HEAD_MOUNTED_VR || - displayid_primary_use(&iter) == PRIMARY_USE_HEAD_MOUNTED_AR)) - info->non_desktop = true; + if (!base_section_header_processed) { + drm_displayid_process_base_section_header(connector, &iter); + base_section_header_processed = true; + } - /* - * We're only interested in the base section here, no need to - * iterate further. - */ - break; + if (displayid_version(&iter) == DISPLAY_ID_STRUCTURE_VER_20 && + block->tag == DATA_BLOCK_2_DISPLAY_PARAMETERS) + drm_displayid_parse_display_params(connector, block); } displayid_iter_end(&iter); } diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c index 7b11a582f8ec..3316bb00a662 100644 --- a/drivers/gpu/drm/drm_fb_helper.c +++ b/drivers/gpu/drm/drm_fb_helper.c @@ -1744,21 +1744,17 @@ EXPORT_SYMBOL(drm_fb_helper_initial_config); */ int drm_fb_helper_hotplug_event(struct drm_fb_helper *fb_helper) { - int err = 0; - if (!drm_fbdev_emulation || !fb_helper) return 0; mutex_lock(&fb_helper->lock); - if (fb_helper->deferred_setup) { - err = __drm_fb_helper_initial_config_and_unlock(fb_helper); - return err; - } + if (fb_helper->deferred_setup) + return __drm_fb_helper_initial_config_and_unlock(fb_helper); if (!fb_helper->fb || !drm_master_internal_acquire(fb_helper->dev)) { fb_helper->delayed_hotplug = true; mutex_unlock(&fb_helper->lock); - return err; + return 0; } drm_master_internal_release(fb_helper->dev); @@ -1802,4 +1798,3 @@ bool drm_fb_helper_gem_is_fb(const struct drm_fb_helper *fb_helper, return gem == obj; } EXPORT_SYMBOL_GPL(drm_fb_helper_gem_is_fb); - diff --git a/drivers/gpu/drm/drm_gem.c b/drivers/gpu/drm/drm_gem.c index e3ed684ddcf2..018df97d590d 100644 --- a/drivers/gpu/drm/drm_gem.c +++ b/drivers/gpu/drm/drm_gem.c @@ -232,8 +232,7 @@ void drm_gem_private_object_init(struct drm_device *dev, if (!obj->resv) obj->resv = &obj->_resv; - if (drm_core_check_feature(dev, DRIVER_GEM_GPUVA)) - drm_gem_gpuva_init(obj); + drm_gem_gpuva_init(obj); drm_vma_node_reset(&obj->vma_node); INIT_LIST_HEAD(&obj->lru_node); diff --git a/drivers/gpu/drm/drm_gem_shmem_helper.c b/drivers/gpu/drm/drm_gem_shmem_helper.c index c989459eb215..22ec52e2ffb8 100644 --- a/drivers/gpu/drm/drm_gem_shmem_helper.c +++ b/drivers/gpu/drm/drm_gem_shmem_helper.c @@ -473,10 +473,23 @@ void drm_gem_shmem_vunmap_locked(struct drm_gem_shmem_object *shmem, } EXPORT_SYMBOL_GPL(drm_gem_shmem_vunmap_locked); -static int -drm_gem_shmem_create_with_handle(struct drm_file *file_priv, - struct drm_device *dev, size_t size, - uint32_t *handle) +/** + * drm_gem_shmem_create_with_handle - Allocate an object with the given size and + * returns a GEM handle + * @file_priv: DRM file structure to create the dumb buffer for + * @dev: DRM device + * @size: Size of the object to allocate + * @handle: Returns the GEM handle on success + * + * Allocates an shmem GEM buffer using drm_gem_shmem_create() and returns + * a GEM handle to it. + * + * Returns: + * Zero on success, or an error code otherwise. + */ +int drm_gem_shmem_create_with_handle(struct drm_file *file_priv, + struct drm_device *dev, size_t size, + uint32_t *handle) { struct drm_gem_shmem_object *shmem; int ret; @@ -495,6 +508,7 @@ drm_gem_shmem_create_with_handle(struct drm_file *file_priv, return ret; } +EXPORT_SYMBOL_GPL(drm_gem_shmem_create_with_handle); /* Update madvise status, returns true if not purged, else * false or -errno. diff --git a/drivers/gpu/drm/drm_mode_config.c b/drivers/gpu/drm/drm_mode_config.c index 66f7dc37b597..f432f485a914 100644 --- a/drivers/gpu/drm/drm_mode_config.c +++ b/drivers/gpu/drm/drm_mode_config.c @@ -23,6 +23,7 @@ #include <linux/export.h> #include <linux/uaccess.h> +#include <drm/drm_atomic.h> #include <drm/drm_drv.h> #include <drm/drm_encoder.h> #include <drm/drm_file.h> @@ -182,6 +183,87 @@ int drm_mode_getresources(struct drm_device *dev, void *data, return ret; } +static int drm_mode_config_plane_create_state(struct drm_plane *plane) +{ + struct drm_plane_state *plane_state; + + if (!plane->funcs->atomic_create_state) + return 0; + + plane_state = plane->funcs->atomic_create_state(plane); + if (IS_ERR(plane_state)) + return PTR_ERR(plane_state); + + plane->state = plane_state; + + return 0; +} + +static int drm_mode_config_plane_reset_with_create_state(struct drm_plane *plane) +{ + if (plane->state) { + plane->funcs->atomic_destroy_state(plane, plane->state); + plane->state = NULL; + } + + return drm_mode_config_plane_create_state(plane); +} + +static int drm_mode_config_crtc_create_state(struct drm_crtc *crtc) +{ + struct drm_crtc_state *crtc_state; + + if (!crtc->funcs->atomic_create_state) + return 0; + + crtc_state = crtc->funcs->atomic_create_state(crtc); + if (IS_ERR(crtc_state)) + return PTR_ERR(crtc_state); + + if (drm_dev_has_vblank(crtc->dev)) + drm_crtc_vblank_reset(crtc); + + crtc->state = crtc_state; + + return 0; +} + +static int drm_mode_config_crtc_reset_with_create_state(struct drm_crtc *crtc) +{ + if (crtc->state) { + crtc->funcs->atomic_destroy_state(crtc, crtc->state); + crtc->state = NULL; + } + + return drm_mode_config_crtc_create_state(crtc); +} + +static int drm_mode_config_connector_create_state(struct drm_connector *connector) +{ + struct drm_connector_state *conn_state; + + if (!connector->funcs->atomic_create_state) + return 0; + + conn_state = connector->funcs->atomic_create_state(connector); + if (IS_ERR(conn_state)) + return PTR_ERR(conn_state); + + connector->state = conn_state; + + return 0; +} + +static int drm_mode_config_connector_reset_with_create_state(struct drm_connector *connector) +{ + if (connector->state) { + connector->funcs->atomic_destroy_state(connector, connector->state); + connector->state = NULL; + } + + return drm_mode_config_connector_create_state(connector); +} + /** * drm_mode_config_reset - call ->reset callbacks * @dev: drm device @@ -189,6 +271,10 @@ int drm_mode_getresources(struct drm_device *dev, void *data, * This functions calls all the crtc's, encoder's and connector's ->reset * callback. Drivers can use this in e.g. their driver load or resume code to * reset hardware and software state. + * + * Note that &drm_private_obj structures are expected to be stable across + * suspend/resume cycles, and drm_mode_config_reset() does not affect these + * structures. */ void drm_mode_config_reset(struct drm_device *dev) { @@ -202,26 +288,123 @@ void drm_mode_config_reset(struct drm_device *dev) drm_for_each_colorop(colorop, dev) drm_colorop_reset(colorop); - drm_for_each_plane(plane, dev) + drm_for_each_plane(plane, dev) { if (plane->funcs->reset) plane->funcs->reset(plane); + else if (plane->funcs->atomic_create_state) + drm_mode_config_plane_reset_with_create_state(plane); + } - drm_for_each_crtc(crtc, dev) + drm_for_each_crtc(crtc, dev) { if (crtc->funcs->reset) crtc->funcs->reset(crtc); + else if (crtc->funcs->atomic_create_state) + drm_mode_config_crtc_reset_with_create_state(crtc); + } drm_for_each_encoder(encoder, dev) if (encoder->funcs && encoder->funcs->reset) encoder->funcs->reset(encoder); drm_connector_list_iter_begin(dev, &conn_iter); - drm_for_each_connector_iter(connector, &conn_iter) + drm_for_each_connector_iter(connector, &conn_iter) { if (connector->funcs->reset) connector->funcs->reset(connector); + else if (connector->funcs->atomic_create_state) + drm_mode_config_connector_reset_with_create_state(connector); + } drm_connector_list_iter_end(&conn_iter); } EXPORT_SYMBOL(drm_mode_config_reset); +/** + * drm_mode_config_create_initial_state - Allocates the initial state + * @dev: drm device + * + * This functions creates the initial state for all the objects. Drivers + * can use this in e.g. probe to initialize their software state. + * + * It has two main differences with drm_mode_config_reset(): the reset() + * hooks aren't called and thus the hardware will be left untouched, but + * also the &drm_private_obj structures will be initialized as opposed + * to drm_mode_config_reset() that skips them. + * + * Returns: 0 on success, negative error value on failure. + */ +int drm_mode_config_create_initial_state(struct drm_device *dev) +{ + struct drm_crtc *crtc; + struct drm_colorop *colorop; + struct drm_plane *plane; + struct drm_connector *connector; + struct drm_connector_list_iter conn_iter; + struct drm_private_obj *privobj; + int ret; + + drm_for_each_privobj(privobj, dev) { + struct drm_private_state *privobj_state; + + if (privobj->state) + continue; + + if (!privobj->funcs->atomic_create_state) + continue; + + privobj_state = privobj->funcs->atomic_create_state(privobj); + if (IS_ERR(privobj_state)) + return PTR_ERR(privobj_state); + + privobj->state = privobj_state; + } + + drm_for_each_colorop(colorop, dev) { + struct drm_colorop_state *colorop_state; + + if (colorop->state) + continue; + + colorop_state = drm_atomic_helper_colorop_create_state(colorop); + if (IS_ERR(colorop_state)) + return PTR_ERR(colorop_state); + + colorop->state = colorop_state; + } + + drm_for_each_plane(plane, dev) { + if (plane->state) + continue; + + ret = drm_mode_config_plane_create_state(plane); + if (ret) + return ret; + } + + drm_for_each_crtc(crtc, dev) { + if (crtc->state) + continue; + + ret = drm_mode_config_crtc_create_state(crtc); + if (ret) + return ret; + } + + drm_connector_list_iter_begin(dev, &conn_iter); + drm_for_each_connector_iter(connector, &conn_iter) { + if (connector->state) + continue; + + ret = drm_mode_config_connector_create_state(connector); + if (ret) { + drm_connector_list_iter_end(&conn_iter); + return ret; + } + } + drm_connector_list_iter_end(&conn_iter); + + return 0; +} +EXPORT_SYMBOL(drm_mode_config_create_initial_state); + /* * Global properties */ diff --git a/drivers/gpu/drm/drm_panic.c b/drivers/gpu/drm/drm_panic.c index d6d3b8d85dea..e576c4791861 100644 --- a/drivers/gpu/drm/drm_panic.c +++ b/drivers/gpu/drm/drm_panic.c @@ -443,9 +443,11 @@ static void draw_txt_rectangle(struct drm_scanout_buffer *sb, rec.x1 += (drm_rect_width(clip) - (line_len * font->width)) / 2; for (j = 0; j < line_len; j++) { - src = drm_draw_get_char_bitmap(font, msg[i].txt[j], font_pitch); + src = font_data_glyph_buf(font->data, font->width, font->height, + (unsigned char)msg[i].txt[j]); rec.x2 = rec.x1 + font->width; - drm_panic_blit(sb, &rec, src, font_pitch, 1, color); + if (src) + drm_panic_blit(sb, &rec, src, font_pitch, 1, color); rec.x1 += font->width; } } diff --git a/drivers/gpu/drm/drm_print.c b/drivers/gpu/drm/drm_print.c index ded9461df5f2..86cef1a37678 100644 --- a/drivers/gpu/drm/drm_print.c +++ b/drivers/gpu/drm/drm_print.c @@ -50,8 +50,10 @@ MODULE_PARM_DESC(debug, "Enable debug output, where each bit enables a debug cat "\t\tBit 3 (0x08) will enable PRIME messages (prime code)\n" "\t\tBit 4 (0x10) will enable ATOMIC messages (atomic code)\n" "\t\tBit 5 (0x20) will enable VBL messages (vblank code)\n" +"\t\tBit 6 (0x40) will enable STATE messages (atomic state code)\n" "\t\tBit 7 (0x80) will enable LEASE messages (leasing code)\n" -"\t\tBit 8 (0x100) will enable DP messages (displayport code)"); +"\t\tBit 8 (0x100) will enable DP messages (displayport code)\n" +"\t\tBit 9 (0x200) will enable DRMRES messages (managed resources code)"); #if !defined(CONFIG_DRM_USE_DYNAMIC_DEBUG) module_param_named(debug, __drm_debug, ulong, 0600); diff --git a/drivers/gpu/drm/drm_writeback.c b/drivers/gpu/drm/drm_writeback.c index 4da5d6094721..b4a002e6d043 100644 --- a/drivers/gpu/drm/drm_writeback.c +++ b/drivers/gpu/drm/drm_writeback.c @@ -58,6 +58,12 @@ * framebuffer to be written by the writeback connector. This property is * similar to the FB_ID property on planes, but will always read as zero * and is not preserved across commits. + * If the width and height of the framebuffer do not match those of the + * attached CRTC, the driver must either fail or scale (not crop) the + * content to exactly fit the framebuffer. + * If the driver is unable to exactly fill the framebuffer for any reason, + * such as hardware scaler constraints or an odd width for a sub-sampled + * format, the writeback must fail instead of partially filling the buffer. * Userspace must set this property to an output buffer every time it * wishes the buffer to get filled. * diff --git a/drivers/gpu/drm/gma500/psb_drv.c b/drivers/gpu/drm/gma500/psb_drv.c index 947f7c4ff4e9..d17cb5b4a4bf 100644 --- a/drivers/gpu/drm/gma500/psb_drv.c +++ b/drivers/gpu/drm/gma500/psb_drv.c @@ -55,36 +55,36 @@ static int psb_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent); */ static const struct pci_device_id pciidlist[] = { /* Poulsbo */ - { 0x8086, 0x8108, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &psb_chip_ops }, - { 0x8086, 0x8109, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &psb_chip_ops }, + { PCI_DEVICE(0x8086, 0x8108), .driver_data = (long)&psb_chip_ops }, + { PCI_DEVICE(0x8086, 0x8109), .driver_data = (long)&psb_chip_ops }, /* Oak Trail */ - { 0x8086, 0x4100, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &oaktrail_chip_ops }, - { 0x8086, 0x4101, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &oaktrail_chip_ops }, - { 0x8086, 0x4102, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &oaktrail_chip_ops }, - { 0x8086, 0x4103, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &oaktrail_chip_ops }, - { 0x8086, 0x4104, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &oaktrail_chip_ops }, - { 0x8086, 0x4105, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &oaktrail_chip_ops }, - { 0x8086, 0x4106, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &oaktrail_chip_ops }, - { 0x8086, 0x4107, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &oaktrail_chip_ops }, - { 0x8086, 0x4108, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &oaktrail_chip_ops }, + { PCI_DEVICE(0x8086, 0x4100), .driver_data = (long)&oaktrail_chip_ops }, + { PCI_DEVICE(0x8086, 0x4101), .driver_data = (long)&oaktrail_chip_ops }, + { PCI_DEVICE(0x8086, 0x4102), .driver_data = (long)&oaktrail_chip_ops }, + { PCI_DEVICE(0x8086, 0x4103), .driver_data = (long)&oaktrail_chip_ops }, + { PCI_DEVICE(0x8086, 0x4104), .driver_data = (long)&oaktrail_chip_ops }, + { PCI_DEVICE(0x8086, 0x4105), .driver_data = (long)&oaktrail_chip_ops }, + { PCI_DEVICE(0x8086, 0x4106), .driver_data = (long)&oaktrail_chip_ops }, + { PCI_DEVICE(0x8086, 0x4107), .driver_data = (long)&oaktrail_chip_ops }, + { PCI_DEVICE(0x8086, 0x4108), .driver_data = (long)&oaktrail_chip_ops }, /* Cedar Trail */ - { 0x8086, 0x0be0, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &cdv_chip_ops }, - { 0x8086, 0x0be1, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &cdv_chip_ops }, - { 0x8086, 0x0be2, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &cdv_chip_ops }, - { 0x8086, 0x0be3, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &cdv_chip_ops }, - { 0x8086, 0x0be4, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &cdv_chip_ops }, - { 0x8086, 0x0be5, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &cdv_chip_ops }, - { 0x8086, 0x0be6, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &cdv_chip_ops }, - { 0x8086, 0x0be7, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &cdv_chip_ops }, - { 0x8086, 0x0be8, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &cdv_chip_ops }, - { 0x8086, 0x0be9, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &cdv_chip_ops }, - { 0x8086, 0x0bea, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &cdv_chip_ops }, - { 0x8086, 0x0beb, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &cdv_chip_ops }, - { 0x8086, 0x0bec, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &cdv_chip_ops }, - { 0x8086, 0x0bed, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &cdv_chip_ops }, - { 0x8086, 0x0bee, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &cdv_chip_ops }, - { 0x8086, 0x0bef, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &cdv_chip_ops }, - { 0, } + { PCI_DEVICE(0x8086, 0x0be0), .driver_data = (long)&cdv_chip_ops }, + { PCI_DEVICE(0x8086, 0x0be1), .driver_data = (long)&cdv_chip_ops }, + { PCI_DEVICE(0x8086, 0x0be2), .driver_data = (long)&cdv_chip_ops }, + { PCI_DEVICE(0x8086, 0x0be3), .driver_data = (long)&cdv_chip_ops }, + { PCI_DEVICE(0x8086, 0x0be4), .driver_data = (long)&cdv_chip_ops }, + { PCI_DEVICE(0x8086, 0x0be5), .driver_data = (long)&cdv_chip_ops }, + { PCI_DEVICE(0x8086, 0x0be6), .driver_data = (long)&cdv_chip_ops }, + { PCI_DEVICE(0x8086, 0x0be7), .driver_data = (long)&cdv_chip_ops }, + { PCI_DEVICE(0x8086, 0x0be8), .driver_data = (long)&cdv_chip_ops }, + { PCI_DEVICE(0x8086, 0x0be9), .driver_data = (long)&cdv_chip_ops }, + { PCI_DEVICE(0x8086, 0x0bea), .driver_data = (long)&cdv_chip_ops }, + { PCI_DEVICE(0x8086, 0x0beb), .driver_data = (long)&cdv_chip_ops }, + { PCI_DEVICE(0x8086, 0x0bec), .driver_data = (long)&cdv_chip_ops }, + { PCI_DEVICE(0x8086, 0x0bed), .driver_data = (long)&cdv_chip_ops }, + { PCI_DEVICE(0x8086, 0x0bee), .driver_data = (long)&cdv_chip_ops }, + { PCI_DEVICE(0x8086, 0x0bef), .driver_data = (long)&cdv_chip_ops }, + { } }; MODULE_DEVICE_TABLE(pci, pciidlist); diff --git a/drivers/gpu/drm/hisilicon/hibmc/Kconfig b/drivers/gpu/drm/hisilicon/hibmc/Kconfig index d1f3f5793f34..adf4516bf8f6 100644 --- a/drivers/gpu/drm/hisilicon/hibmc/Kconfig +++ b/drivers/gpu/drm/hisilicon/hibmc/Kconfig @@ -5,10 +5,8 @@ config DRM_HISI_HIBMC select DRM_CLIENT_SELECTION select DRM_DISPLAY_HELPER select DRM_DISPLAY_DP_HELPER + select DRM_GEM_SHMEM_HELPER select DRM_KMS_HELPER - select DRM_VRAM_HELPER - select DRM_TTM - select DRM_TTM_HELPER select I2C select I2C_ALGOBIT help diff --git a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_de.c b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_de.c index 3066dc9ebc64..b4ab53db1c08 100644 --- a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_de.c +++ b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_de.c @@ -15,8 +15,10 @@ #include <drm/drm_atomic.h> #include <drm/drm_atomic_helper.h> +#include <drm/drm_damage_helper.h> #include <drm/drm_fourcc.h> -#include <drm/drm_gem_vram_helper.h> +#include <drm/drm_gem_atomic_helper.h> +#include <drm/drm_gem_framebuffer_helper.h> #include <drm/drm_vblank.h> #include "hibmc_drm_drv.h" @@ -72,74 +74,69 @@ static int hibmc_get_best_clock_idx(const struct drm_display_mode *mode) static int hibmc_plane_atomic_check(struct drm_plane *plane, struct drm_atomic_commit *state) { - struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state, - plane); - struct drm_framebuffer *fb = new_plane_state->fb; - struct drm_crtc *crtc = new_plane_state->crtc; - struct drm_crtc_state *crtc_state; - u32 src_w = new_plane_state->src_w >> 16; - u32 src_h = new_plane_state->src_h >> 16; - - if (!crtc || !fb) - return 0; - - crtc_state = drm_atomic_get_crtc_state(state, crtc); - if (IS_ERR(crtc_state)) - return PTR_ERR(crtc_state); - - if (src_w != new_plane_state->crtc_w || src_h != new_plane_state->crtc_h) { - drm_dbg_atomic(plane->dev, "scale not support\n"); - return -EINVAL; - } + struct drm_plane_state *new_plane_state = + drm_atomic_get_new_plane_state(state, plane); + struct drm_crtc_state *new_crtc_state = NULL; + int ret; - if (new_plane_state->crtc_x < 0 || new_plane_state->crtc_y < 0) { - drm_dbg_atomic(plane->dev, "crtc_x/y of drm_plane state is invalid\n"); - return -EINVAL; - } + if (new_plane_state->crtc) + new_crtc_state = drm_atomic_get_new_crtc_state(state, new_plane_state->crtc); - if (!crtc_state->enable) + ret = drm_atomic_helper_check_plane_state(new_plane_state, new_crtc_state, + DRM_PLANE_NO_SCALING, + DRM_PLANE_NO_SCALING, + false, true); + if (ret) + return ret; + else if (!new_plane_state->visible) return 0; - if (new_plane_state->crtc_x + new_plane_state->crtc_w > - crtc_state->adjusted_mode.hdisplay || - new_plane_state->crtc_y + new_plane_state->crtc_h > - crtc_state->adjusted_mode.vdisplay) { - drm_dbg_atomic(plane->dev, "visible portion of plane is invalid\n"); - return -EINVAL; - } - if (new_plane_state->fb->pitches[0] % 128 != 0) { drm_dbg_atomic(plane->dev, "wrong stride with 128-byte aligned\n"); return -EINVAL; } + return 0; } static void hibmc_plane_atomic_update(struct drm_plane *plane, struct drm_atomic_commit *state) { - struct drm_plane_state *new_state = drm_atomic_get_new_plane_state(state, - plane); + struct hibmc_drm_private *priv = to_hibmc_drm_private(plane->dev); + struct drm_plane_state *new_state = drm_atomic_get_new_plane_state(state, plane); + struct drm_shadow_plane_state *shadow_plane_state = to_drm_shadow_plane_state(new_state); + struct drm_framebuffer *fb = new_state->fb; + struct drm_plane_state *old_state = drm_atomic_get_old_plane_state(state, plane); + u32 gpu_addr = 0; u32 reg; - s64 gpu_addr = 0; u32 line_l; - struct hibmc_drm_private *priv = to_hibmc_drm_private(plane->dev); - struct drm_gem_vram_object *gbo; - if (!new_state->fb) + if (!fb) return; - gbo = drm_gem_vram_of_gem(new_state->fb->obj[0]); + if (drm_gem_fb_begin_cpu_access(fb, DMA_FROM_DEVICE) == 0) { + struct drm_rect damage; + struct drm_atomic_helper_damage_iter iter; - gpu_addr = drm_gem_vram_offset(gbo); - if (WARN_ON_ONCE(gpu_addr < 0)) - return; /* Bug: we didn't pin the BO to VRAM in prepare_fb. */ + drm_atomic_helper_damage_iter_init(&iter, old_state, new_state); + drm_atomic_for_each_plane_damage(&iter, &damage) { + struct iosys_map dst[DRM_FORMAT_MAX_PLANES] = { + IOSYS_MAP_INIT_VADDR_IOMEM(priv->vram + gpu_addr), + }; + + iosys_map_incr(&dst[0], + drm_fb_clip_offset(fb->pitches[0], fb->format, &damage)); + drm_fb_memcpy(dst, fb->pitches, shadow_plane_state->data, fb, &damage); + } + + drm_gem_fb_end_cpu_access(fb, DMA_FROM_DEVICE); + } writel(gpu_addr, priv->mmio + HIBMC_CRT_FB_ADDRESS); - reg = new_state->fb->width * (new_state->fb->format->cpp[0]); + reg = drm_format_info_min_pitch(fb->format, 0, fb->width); - line_l = new_state->fb->pitches[0]; + line_l = fb->pitches[0]; writel(HIBMC_FIELD(HIBMC_CRT_FB_WIDTH_WIDTH, reg) | HIBMC_FIELD(HIBMC_CRT_FB_WIDTH_OFFS, line_l), priv->mmio + HIBMC_CRT_FB_WIDTH); @@ -147,29 +144,31 @@ static void hibmc_plane_atomic_update(struct drm_plane *plane, /* SET PIXEL FORMAT */ reg = readl(priv->mmio + HIBMC_CRT_DISP_CTL); reg &= ~HIBMC_CRT_DISP_CTL_FORMAT_MASK; - reg |= HIBMC_FIELD(HIBMC_CRT_DISP_CTL_FORMAT, - new_state->fb->format->cpp[0] * 8 / 16); + switch (fb->format->format) { + case DRM_FORMAT_XRGB8888: + reg |= HIBMC_FIELD(HIBMC_CRT_DISP_CTL_FORMAT, 2); + break; + case DRM_FORMAT_RGB565: + reg |= HIBMC_FIELD(HIBMC_CRT_DISP_CTL_FORMAT, 1); + break; + } writel(reg, priv->mmio + HIBMC_CRT_DISP_CTL); } static const u32 channel_formats1[] = { - DRM_FORMAT_RGB565, DRM_FORMAT_BGR565, DRM_FORMAT_RGB888, - DRM_FORMAT_BGR888, DRM_FORMAT_XRGB8888, DRM_FORMAT_XBGR8888, - DRM_FORMAT_RGBA8888, DRM_FORMAT_BGRA8888, DRM_FORMAT_ARGB8888, - DRM_FORMAT_ABGR8888 + DRM_FORMAT_XRGB8888, + DRM_FORMAT_RGB565, }; static const struct drm_plane_funcs hibmc_plane_funcs = { .update_plane = drm_atomic_helper_update_plane, .disable_plane = drm_atomic_helper_disable_plane, .destroy = drm_plane_cleanup, - .reset = drm_atomic_helper_plane_reset, - .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state, - .atomic_destroy_state = drm_atomic_helper_plane_destroy_state, + DRM_GEM_SHADOW_PLANE_FUNCS, }; static const struct drm_plane_helper_funcs hibmc_plane_helper_funcs = { - DRM_GEM_VRAM_PLANE_HELPER_FUNCS, + DRM_GEM_SHADOW_PLANE_HELPER_FUNCS, .atomic_check = hibmc_plane_atomic_check, .atomic_update = hibmc_plane_atomic_update, }; @@ -529,6 +528,7 @@ int hibmc_de_init(struct hibmc_drm_private *priv) } drm_plane_helper_add(plane, &hibmc_plane_helper_funcs); + drm_plane_enable_fb_damage_clips(plane); ret = drm_crtc_init_with_planes(dev, crtc, plane, NULL, &hibmc_crtc_funcs, NULL); diff --git a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.c b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.c index 99b36de1fe13..4d85c89f3f88 100644 --- a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.c +++ b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.c @@ -14,13 +14,15 @@ #include <linux/aperture.h> #include <linux/module.h> #include <linux/pci.h> +#include <linux/sizes.h> #include <drm/clients/drm_client_setup.h> #include <drm/drm_atomic_helper.h> #include <drm/drm_drv.h> -#include <drm/drm_fbdev_ttm.h> +#include <drm/drm_dumb_buffers.h> +#include <drm/drm_fbdev_shmem.h> #include <drm/drm_gem_framebuffer_helper.h> -#include <drm/drm_gem_vram_helper.h> +#include <drm/drm_gem_shmem_helper.h> #include <drm/drm_managed.h> #include <drm/drm_module.h> #include <drm/drm_vblank.h> @@ -71,7 +73,13 @@ static irqreturn_t hibmc_dp_interrupt(int irq, void *arg) static int hibmc_dumb_create(struct drm_file *file, struct drm_device *dev, struct drm_mode_create_dumb *args) { - return drm_gem_vram_fill_create_dumb(file, dev, 0, 128, args); + int ret; + + ret = drm_mode_size_dumb(dev, args, SZ_128, 0); + if (ret) + return ret; + + return drm_gem_shmem_create_with_handle(file, dev, args->size, &args->handle); } static const struct drm_driver hibmc_driver = { @@ -81,10 +89,9 @@ static const struct drm_driver hibmc_driver = { .desc = "hibmc drm driver", .major = 1, .minor = 0, - .debugfs_init = drm_vram_mm_debugfs_init, - .dumb_create = hibmc_dumb_create, - .dumb_map_offset = drm_gem_ttm_dumb_map_offset, - DRM_FBDEV_TTM_DRIVER_OPS, + .gem_prime_import = drm_gem_shmem_prime_import_no_map, + .dumb_create = hibmc_dumb_create, + DRM_FBDEV_SHMEM_DRIVER_OPS, }; static int __maybe_unused hibmc_pm_suspend(struct device *dev) @@ -106,11 +113,47 @@ static const struct dev_pm_ops hibmc_pm_ops = { hibmc_pm_resume) }; +static enum drm_mode_status hibmc_mode_config_mode_valid(struct drm_device *dev, + const struct drm_display_mode *mode) +{ + const struct drm_format_info *info = + drm_get_format_info(dev, DRM_FORMAT_XRGB8888, DRM_FORMAT_MOD_LINEAR); + struct hibmc_drm_private *priv = to_hibmc_drm_private(dev); + unsigned long max_fb_size = priv->vram_size; + u64 pitch; + + if (drm_WARN_ON_ONCE(dev, !info)) + return MODE_ERROR; /* driver bug */ + + pitch = drm_format_info_min_pitch(info, 0, mode->hdisplay); + if (!pitch) + return MODE_BAD_WIDTH; + else if (pitch > max_fb_size / mode->vdisplay) + return MODE_MEM; + + return MODE_OK; +} + +static struct drm_framebuffer *hibmc_mode_config_fb_create(struct drm_device *dev, + struct drm_file *file_priv, + const struct drm_format_info *info, + const struct drm_mode_fb_cmd2 *mode_cmd) +{ + int i; + + for (i = 0; i < info->num_planes; ++i) { + if (mode_cmd->pitches[i] % SZ_128) + return ERR_PTR(-EINVAL); + } + + return drm_gem_fb_create_with_dirty(dev, file_priv, info, mode_cmd); +} + static const struct drm_mode_config_funcs hibmc_mode_funcs = { - .mode_valid = drm_vram_helper_mode_valid, + .mode_valid = hibmc_mode_config_mode_valid, .atomic_check = drm_atomic_helper_check, .atomic_commit = drm_atomic_helper_commit, - .fb_create = drm_gem_fb_create, + .fb_create = hibmc_mode_config_fb_create, }; static int hibmc_kms_init(struct hibmc_drm_private *priv) @@ -130,7 +173,6 @@ static int hibmc_kms_init(struct hibmc_drm_private *priv) dev->mode_config.max_height = 1200; dev->mode_config.preferred_depth = 24; - dev->mode_config.prefer_shadow = 1; dev->mode_config.funcs = (void *)&hibmc_mode_funcs; @@ -335,18 +377,22 @@ static int hibmc_load(struct drm_device *dev) { struct pci_dev *pdev = to_pci_dev(dev->dev); struct hibmc_drm_private *priv = to_hibmc_drm_private(dev); + resource_size_t vram_base, vram_size; int ret; ret = hibmc_hw_init(priv); if (ret) return ret; - ret = drmm_vram_helper_init(dev, pci_resource_start(pdev, 0), - pci_resource_len(pdev, 0)); - if (ret) { - drm_err(dev, "Error initializing VRAM MM; %d\n", ret); - return ret; - } + vram_base = pci_resource_start(pdev, 0); + vram_size = pci_resource_len(pdev, 0); + + priv->vram = devm_ioremap_wc(dev->dev, vram_base, vram_size); + if (!priv->vram) + return -ENOMEM; + + priv->vram_base = vram_base; + priv->vram_size = vram_size; ret = hibmc_kms_init(priv); if (ret) diff --git a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.h b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.h index cd3a3fca1fe6..dce8572bf63e 100644 --- a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.h +++ b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.h @@ -38,6 +38,11 @@ struct hibmc_drm_private { /* hw */ void __iomem *mmio; + /* vram */ + void __iomem *vram; + resource_size_t vram_base; + resource_size_t vram_size; + /* drm */ struct drm_device dev; struct drm_plane primary_plane; diff --git a/drivers/gpu/drm/hyperv/hyperv_drm.h b/drivers/gpu/drm/hyperv/hyperv_drm.h index 9e776112c03e..78136ec2c2f4 100644 --- a/drivers/gpu/drm/hyperv/hyperv_drm.h +++ b/drivers/gpu/drm/hyperv/hyperv_drm.h @@ -8,7 +8,7 @@ #define VMBUS_MAX_PACKET_SIZE 0x4000 -struct hyperv_drm_device { +struct hv_drm_device { /* drm */ struct drm_device dev; struct drm_plane plane; @@ -39,17 +39,17 @@ struct hyperv_drm_device { struct hv_device *hdev; }; -#define to_hv(_dev) container_of(_dev, struct hyperv_drm_device, dev) +#define to_hv_drm(_dev) container_of(_dev, struct hv_drm_device, dev) /* hyperv_drm_modeset */ -int hyperv_mode_config_init(struct hyperv_drm_device *hv); +int hv_drm_mode_config_init(struct hv_drm_device *hv); /* hyperv_drm_proto */ -int hyperv_update_vram_location(struct hv_device *hdev, phys_addr_t vram_pp); -int hyperv_update_situation(struct hv_device *hdev, u8 active, u32 bpp, +int hv_drm_update_vram_location(struct hv_device *hdev, phys_addr_t vram_pp); +int hv_drm_update_situation(struct hv_device *hdev, u8 active, u32 bpp, u32 w, u32 h, u32 pitch); -int hyperv_hide_hw_ptr(struct hv_device *hdev); -int hyperv_update_dirt(struct hv_device *hdev, struct drm_rect *rect); -int hyperv_connect_vsp(struct hv_device *hdev); +int hv_drm_hide_hw_ptr(struct hv_device *hdev); +int hv_drm_update_dirt(struct hv_device *hdev, struct drm_rect *rect); +int hv_drm_connect_vsp(struct hv_device *hdev); #endif diff --git a/drivers/gpu/drm/hyperv/hyperv_drm_drv.c b/drivers/gpu/drm/hyperv/hyperv_drm_drv.c index b6bf6412ae34..20f35c48c0b8 100644 --- a/drivers/gpu/drm/hyperv/hyperv_drm_drv.c +++ b/drivers/gpu/drm/hyperv/hyperv_drm_drv.c @@ -24,9 +24,9 @@ #define DRIVER_MAJOR 1 #define DRIVER_MINOR 0 -DEFINE_DRM_GEM_FOPS(hv_fops); +DEFINE_DRM_GEM_FOPS(hv_drm_fops); -static struct drm_driver hyperv_driver = { +static struct drm_driver hv_drm_driver = { .driver_features = DRIVER_MODESET | DRIVER_GEM | DRIVER_ATOMIC, .name = DRIVER_NAME, @@ -34,22 +34,22 @@ static struct drm_driver hyperv_driver = { .major = DRIVER_MAJOR, .minor = DRIVER_MINOR, - .fops = &hv_fops, + .fops = &hv_drm_fops, DRM_GEM_SHMEM_DRIVER_OPS, DRM_FBDEV_SHMEM_DRIVER_OPS, }; -static int hyperv_pci_probe(struct pci_dev *pdev, +static int hv_drm_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) { return 0; } -static void hyperv_pci_remove(struct pci_dev *pdev) +static void hv_drm_pci_remove(struct pci_dev *pdev) { } -static const struct pci_device_id hyperv_pci_tbl[] = { +static const struct pci_device_id hv_drm_pci_tbl[] = { { .vendor = PCI_VENDOR_ID_MICROSOFT, .device = PCI_DEVICE_ID_HYPERV_VIDEO, @@ -60,14 +60,14 @@ static const struct pci_device_id hyperv_pci_tbl[] = { /* * PCI stub to support gen1 VM. */ -static struct pci_driver hyperv_pci_driver = { +static struct pci_driver hv_drm_pci_driver = { .name = KBUILD_MODNAME, - .id_table = hyperv_pci_tbl, - .probe = hyperv_pci_probe, - .remove = hyperv_pci_remove, + .id_table = hv_drm_pci_tbl, + .probe = hv_drm_pci_probe, + .remove = hv_drm_pci_remove, }; -static int hyperv_setup_vram(struct hyperv_drm_device *hv, +static int hv_drm_setup_vram(struct hv_drm_device *hv, struct hv_device *hdev) { struct drm_device *dev = &hv->dev; @@ -102,15 +102,15 @@ error: return ret; } -static int hyperv_vmbus_probe(struct hv_device *hdev, +static int hv_drm_vmbus_probe(struct hv_device *hdev, const struct hv_vmbus_device_id *dev_id) { - struct hyperv_drm_device *hv; + struct hv_drm_device *hv; struct drm_device *dev; int ret; - hv = devm_drm_dev_alloc(&hdev->device, &hyperv_driver, - struct hyperv_drm_device, dev); + hv = devm_drm_dev_alloc(&hdev->device, &hv_drm_driver, + struct hv_drm_device, dev); if (IS_ERR(hv)) return PTR_ERR(hv); @@ -119,15 +119,15 @@ static int hyperv_vmbus_probe(struct hv_device *hdev, hv_set_drvdata(hdev, hv); hv->hdev = hdev; - ret = hyperv_connect_vsp(hdev); + ret = hv_drm_connect_vsp(hdev); if (ret) { drm_err(dev, "Failed to connect to vmbus.\n"); goto err_hv_set_drv_data; } - aperture_remove_all_conflicting_devices(hyperv_driver.name); + aperture_remove_all_conflicting_devices(hv_drm_driver.name); - ret = hyperv_setup_vram(hv, hdev); + ret = hv_drm_setup_vram(hv, hdev); if (ret) goto err_vmbus_close; @@ -136,11 +136,11 @@ static int hyperv_vmbus_probe(struct hv_device *hdev, * vram location is not fatal. Device will update dirty area till * preferred resolution only. */ - ret = hyperv_update_vram_location(hdev, hv->fb_base); + ret = hv_drm_update_vram_location(hdev, hv->fb_base); if (ret) drm_warn(dev, "Failed to update vram location.\n"); - ret = hyperv_mode_config_init(hv); + ret = hv_drm_mode_config_init(hv); if (ret) goto err_free_mmio; @@ -168,10 +168,10 @@ err_hv_set_drv_data: return ret; } -static void hyperv_vmbus_remove(struct hv_device *hdev) +static void hv_drm_vmbus_remove(struct hv_device *hdev) { struct drm_device *dev = hv_get_drvdata(hdev); - struct hyperv_drm_device *hv = to_hv(dev); + struct hv_drm_device *hv = to_hv_drm(dev); vmbus_set_skip_unload(false); drm_dev_unplug(dev); @@ -183,12 +183,12 @@ static void hyperv_vmbus_remove(struct hv_device *hdev) vmbus_free_mmio(hv->mem->start, hv->fb_size); } -static void hyperv_vmbus_shutdown(struct hv_device *hdev) +static void hv_drm_vmbus_shutdown(struct hv_device *hdev) { drm_atomic_helper_shutdown(hv_get_drvdata(hdev)); } -static int hyperv_vmbus_suspend(struct hv_device *hdev) +static int hv_drm_vmbus_suspend(struct hv_device *hdev) { struct drm_device *dev = hv_get_drvdata(hdev); int ret; @@ -202,67 +202,67 @@ static int hyperv_vmbus_suspend(struct hv_device *hdev) return 0; } -static int hyperv_vmbus_resume(struct hv_device *hdev) +static int hv_drm_vmbus_resume(struct hv_device *hdev) { struct drm_device *dev = hv_get_drvdata(hdev); - struct hyperv_drm_device *hv = to_hv(dev); + struct hv_drm_device *hv = to_hv_drm(dev); int ret; - ret = hyperv_connect_vsp(hdev); + ret = hv_drm_connect_vsp(hdev); if (ret) return ret; - ret = hyperv_update_vram_location(hdev, hv->fb_base); + ret = hv_drm_update_vram_location(hdev, hv->fb_base); if (ret) return ret; return drm_mode_config_helper_resume(dev); } -static const struct hv_vmbus_device_id hyperv_vmbus_tbl[] = { +static const struct hv_vmbus_device_id hv_drm_vmbus_tbl[] = { /* Synthetic Video Device GUID */ {HV_SYNTHVID_GUID}, {} }; -static struct hv_driver hyperv_hv_driver = { +static struct hv_driver hv_drm_hv_driver = { .name = KBUILD_MODNAME, - .id_table = hyperv_vmbus_tbl, - .probe = hyperv_vmbus_probe, - .remove = hyperv_vmbus_remove, - .shutdown = hyperv_vmbus_shutdown, - .suspend = hyperv_vmbus_suspend, - .resume = hyperv_vmbus_resume, + .id_table = hv_drm_vmbus_tbl, + .probe = hv_drm_vmbus_probe, + .remove = hv_drm_vmbus_remove, + .shutdown = hv_drm_vmbus_shutdown, + .suspend = hv_drm_vmbus_suspend, + .resume = hv_drm_vmbus_resume, .driver = { .probe_type = PROBE_PREFER_ASYNCHRONOUS, }, }; -static int __init hyperv_init(void) +static int __init hv_drm_init(void) { int ret; if (drm_firmware_drivers_only()) return -ENODEV; - ret = pci_register_driver(&hyperv_pci_driver); + ret = pci_register_driver(&hv_drm_pci_driver); if (ret != 0) return ret; - return vmbus_driver_register(&hyperv_hv_driver); + return vmbus_driver_register(&hv_drm_hv_driver); } -static void __exit hyperv_exit(void) +static void __exit hv_drm_exit(void) { - vmbus_driver_unregister(&hyperv_hv_driver); - pci_unregister_driver(&hyperv_pci_driver); + vmbus_driver_unregister(&hv_drm_hv_driver); + pci_unregister_driver(&hv_drm_pci_driver); } -module_init(hyperv_init); -module_exit(hyperv_exit); +module_init(hv_drm_init); +module_exit(hv_drm_exit); -MODULE_DEVICE_TABLE(pci, hyperv_pci_tbl); -MODULE_DEVICE_TABLE(vmbus, hyperv_vmbus_tbl); +MODULE_DEVICE_TABLE(pci, hv_drm_pci_tbl); +MODULE_DEVICE_TABLE(vmbus, hv_drm_vmbus_tbl); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Deepak Rawat <drawat.floss@gmail.com>"); MODULE_DESCRIPTION("DRM driver for Hyper-V synthetic video device"); diff --git a/drivers/gpu/drm/hyperv/hyperv_drm_modeset.c b/drivers/gpu/drm/hyperv/hyperv_drm_modeset.c index 793dbbf61893..1855749c1e41 100644 --- a/drivers/gpu/drm/hyperv/hyperv_drm_modeset.c +++ b/drivers/gpu/drm/hyperv/hyperv_drm_modeset.c @@ -25,11 +25,11 @@ #include "hyperv_drm.h" -static int hyperv_blit_to_vram_rect(struct drm_framebuffer *fb, +static int hv_drm_blit_to_vram_rect(struct drm_framebuffer *fb, const struct iosys_map *vmap, struct drm_rect *rect) { - struct hyperv_drm_device *hv = to_hv(fb->dev); + struct hv_drm_device *hv = to_hv_drm(fb->dev); struct iosys_map dst = IOSYS_MAP_INIT_VADDR_IOMEM(hv->vram); int idx; @@ -44,9 +44,9 @@ static int hyperv_blit_to_vram_rect(struct drm_framebuffer *fb, return 0; } -static int hyperv_connector_get_modes(struct drm_connector *connector) +static int hv_drm_connector_get_modes(struct drm_connector *connector) { - struct hyperv_drm_device *hv = to_hv(connector->dev); + struct hv_drm_device *hv = to_hv_drm(connector->dev); int count; count = drm_add_modes_noedid(connector, @@ -58,11 +58,11 @@ static int hyperv_connector_get_modes(struct drm_connector *connector) return count; } -static const struct drm_connector_helper_funcs hyperv_connector_helper_funcs = { - .get_modes = hyperv_connector_get_modes, +static const struct drm_connector_helper_funcs hv_drm_connector_helper_funcs = { + .get_modes = hv_drm_connector_get_modes, }; -static const struct drm_connector_funcs hyperv_connector_funcs = { +static const struct drm_connector_funcs hv_drm_connector_funcs = { .fill_modes = drm_helper_probe_single_connector_modes, .destroy = drm_connector_cleanup, .reset = drm_atomic_helper_connector_reset, @@ -70,15 +70,15 @@ static const struct drm_connector_funcs hyperv_connector_funcs = { .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, }; -static inline int hyperv_conn_init(struct hyperv_drm_device *hv) +static inline int hv_drm_conn_init(struct hv_drm_device *hv) { - drm_connector_helper_add(&hv->connector, &hyperv_connector_helper_funcs); + drm_connector_helper_add(&hv->connector, &hv_drm_connector_helper_funcs); return drm_connector_init(&hv->dev, &hv->connector, - &hyperv_connector_funcs, + &hv_drm_connector_funcs, DRM_MODE_CONNECTOR_VIRTUAL); } -static int hyperv_check_size(struct hyperv_drm_device *hv, int w, int h, +static int hv_drm_check_size(struct hv_drm_device *hv, int w, int h, struct drm_framebuffer *fb) { u32 pitch = w * (hv->screen_depth / 8); @@ -92,25 +92,25 @@ static int hyperv_check_size(struct hyperv_drm_device *hv, int w, int h, return 0; } -static const uint32_t hyperv_formats[] = { +static const uint32_t hv_drm_formats[] = { DRM_FORMAT_XRGB8888, }; -static const uint64_t hyperv_modifiers[] = { +static const uint64_t hv_drm_modifiers[] = { DRM_FORMAT_MOD_LINEAR, DRM_FORMAT_MOD_INVALID }; -static void hyperv_crtc_helper_atomic_enable(struct drm_crtc *crtc, +static void hv_drm_crtc_helper_atomic_enable(struct drm_crtc *crtc, struct drm_atomic_commit *state) { - struct hyperv_drm_device *hv = to_hv(crtc->dev); + struct hv_drm_device *hv = to_hv_drm(crtc->dev); struct drm_plane *plane = &hv->plane; struct drm_plane_state *plane_state = plane->state; struct drm_crtc_state *crtc_state = crtc->state; - hyperv_hide_hw_ptr(hv->hdev); - hyperv_update_situation(hv->hdev, 1, hv->screen_depth, + hv_drm_hide_hw_ptr(hv->hdev); + hv_drm_update_situation(hv->hdev, 1, hv->screen_depth, crtc_state->mode.hdisplay, crtc_state->mode.vdisplay, plane_state->fb->pitches[0]); @@ -118,14 +118,14 @@ static void hyperv_crtc_helper_atomic_enable(struct drm_crtc *crtc, drm_crtc_vblank_on(crtc); } -static const struct drm_crtc_helper_funcs hyperv_crtc_helper_funcs = { +static const struct drm_crtc_helper_funcs hv_drm_crtc_helper_funcs = { .atomic_check = drm_crtc_helper_atomic_check, .atomic_flush = drm_crtc_vblank_atomic_flush, - .atomic_enable = hyperv_crtc_helper_atomic_enable, + .atomic_enable = hv_drm_crtc_helper_atomic_enable, .atomic_disable = drm_crtc_vblank_atomic_disable, }; -static const struct drm_crtc_funcs hyperv_crtc_funcs = { +static const struct drm_crtc_funcs hv_drm_crtc_funcs = { .reset = drm_atomic_helper_crtc_reset, .destroy = drm_crtc_cleanup, .set_config = drm_atomic_helper_set_config, @@ -135,11 +135,11 @@ static const struct drm_crtc_funcs hyperv_crtc_funcs = { DRM_CRTC_VBLANK_TIMER_FUNCS, }; -static int hyperv_plane_atomic_check(struct drm_plane *plane, +static int hv_drm_plane_atomic_check(struct drm_plane *plane, struct drm_atomic_commit *state) { struct drm_plane_state *plane_state = drm_atomic_get_new_plane_state(state, plane); - struct hyperv_drm_device *hv = to_hv(plane->dev); + struct hv_drm_device *hv = to_hv_drm(plane->dev); struct drm_framebuffer *fb = plane_state->fb; struct drm_crtc *crtc = plane_state->crtc; struct drm_crtc_state *crtc_state = NULL; @@ -167,10 +167,10 @@ static int hyperv_plane_atomic_check(struct drm_plane *plane, return 0; } -static void hyperv_plane_atomic_update(struct drm_plane *plane, +static void hv_drm_plane_atomic_update(struct drm_plane *plane, struct drm_atomic_commit *state) { - struct hyperv_drm_device *hv = to_hv(plane->dev); + struct hv_drm_device *hv = to_hv_drm(plane->dev); struct drm_plane_state *old_state = drm_atomic_get_old_plane_state(state, plane); struct drm_plane_state *new_state = drm_atomic_get_new_plane_state(state, plane); struct drm_shadow_plane_state *shadow_plane_state = to_drm_shadow_plane_state(new_state); @@ -185,15 +185,15 @@ static void hyperv_plane_atomic_update(struct drm_plane *plane, if (!drm_rect_intersect(&dst_clip, &damage)) continue; - hyperv_blit_to_vram_rect(new_state->fb, &shadow_plane_state->data[0], &damage); - hyperv_update_dirt(hv->hdev, &damage); + hv_drm_blit_to_vram_rect(new_state->fb, &shadow_plane_state->data[0], &damage); + hv_drm_update_dirt(hv->hdev, &damage); } } -static int hyperv_plane_get_scanout_buffer(struct drm_plane *plane, +static int hv_drm_plane_get_scanout_buffer(struct drm_plane *plane, struct drm_scanout_buffer *sb) { - struct hyperv_drm_device *hv = to_hv(plane->dev); + struct hv_drm_device *hv = to_hv_drm(plane->dev); struct iosys_map map = IOSYS_MAP_INIT_VADDR_IOMEM(hv->vram); if (plane->state && plane->state->fb) { @@ -207,9 +207,9 @@ static int hyperv_plane_get_scanout_buffer(struct drm_plane *plane, return -ENODEV; } -static void hyperv_plane_panic_flush(struct drm_plane *plane) +static void hv_drm_plane_panic_flush(struct drm_plane *plane) { - struct hyperv_drm_device *hv = to_hv(plane->dev); + struct hv_drm_device *hv = to_hv_drm(plane->dev); struct drm_rect rect; if (plane->state && plane->state->fb) { @@ -218,32 +218,32 @@ static void hyperv_plane_panic_flush(struct drm_plane *plane) rect.x2 = plane->state->fb->width; rect.y2 = plane->state->fb->height; - hyperv_update_dirt(hv->hdev, &rect); + hv_drm_update_dirt(hv->hdev, &rect); } vmbus_initiate_unload(true); } -static const struct drm_plane_helper_funcs hyperv_plane_helper_funcs = { +static const struct drm_plane_helper_funcs hv_drm_plane_helper_funcs = { DRM_GEM_SHADOW_PLANE_HELPER_FUNCS, - .atomic_check = hyperv_plane_atomic_check, - .atomic_update = hyperv_plane_atomic_update, - .get_scanout_buffer = hyperv_plane_get_scanout_buffer, - .panic_flush = hyperv_plane_panic_flush, + .atomic_check = hv_drm_plane_atomic_check, + .atomic_update = hv_drm_plane_atomic_update, + .get_scanout_buffer = hv_drm_plane_get_scanout_buffer, + .panic_flush = hv_drm_plane_panic_flush, }; -static const struct drm_plane_funcs hyperv_plane_funcs = { +static const struct drm_plane_funcs hv_drm_plane_funcs = { .update_plane = drm_atomic_helper_update_plane, .disable_plane = drm_atomic_helper_disable_plane, .destroy = drm_plane_cleanup, DRM_GEM_SHADOW_PLANE_FUNCS, }; -static const struct drm_encoder_funcs hyperv_drm_simple_encoder_funcs_cleanup = { +static const struct drm_encoder_funcs hv_drm_simple_encoder_funcs_cleanup = { .destroy = drm_encoder_cleanup, }; -static inline int hyperv_pipe_init(struct hyperv_drm_device *hv) +static inline int hv_drm_pipe_init(struct hv_drm_device *hv) { struct drm_device *dev = &hv->dev; struct drm_encoder *encoder = &hv->encoder; @@ -253,29 +253,29 @@ static inline int hyperv_pipe_init(struct hyperv_drm_device *hv) int ret; ret = drm_universal_plane_init(dev, plane, 0, - &hyperv_plane_funcs, - hyperv_formats, ARRAY_SIZE(hyperv_formats), - hyperv_modifiers, + &hv_drm_plane_funcs, + hv_drm_formats, ARRAY_SIZE(hv_drm_formats), + hv_drm_modifiers, DRM_PLANE_TYPE_PRIMARY, NULL); if (ret) return ret; - drm_plane_helper_add(plane, &hyperv_plane_helper_funcs); + drm_plane_helper_add(plane, &hv_drm_plane_helper_funcs); drm_plane_enable_fb_damage_clips(plane); ret = drm_crtc_init_with_planes(dev, crtc, plane, NULL, - &hyperv_crtc_funcs, NULL); + &hv_drm_crtc_funcs, NULL); if (ret) return ret; - drm_crtc_helper_add(crtc, &hyperv_crtc_helper_funcs); + drm_crtc_helper_add(crtc, &hv_drm_crtc_helper_funcs); encoder->possible_crtcs = drm_crtc_mask(crtc); ret = drm_encoder_init(dev, encoder, - &hyperv_drm_simple_encoder_funcs_cleanup, + &hv_drm_simple_encoder_funcs_cleanup, DRM_MODE_ENCODER_NONE, NULL); if (ret) return ret; - ret = hyperv_conn_init(hv); + ret = hv_drm_conn_init(hv); if (ret) { drm_err(dev, "Failed to initialized connector.\n"); return ret; @@ -285,25 +285,25 @@ static inline int hyperv_pipe_init(struct hyperv_drm_device *hv) } static enum drm_mode_status -hyperv_mode_valid(struct drm_device *dev, +hv_drm_mode_valid(struct drm_device *dev, const struct drm_display_mode *mode) { - struct hyperv_drm_device *hv = to_hv(dev); + struct hv_drm_device *hv = to_hv_drm(dev); - if (hyperv_check_size(hv, mode->hdisplay, mode->vdisplay, NULL)) + if (hv_drm_check_size(hv, mode->hdisplay, mode->vdisplay, NULL)) return MODE_BAD; return MODE_OK; } -static const struct drm_mode_config_funcs hyperv_mode_config_funcs = { +static const struct drm_mode_config_funcs hv_drm_mode_config_funcs = { .fb_create = drm_gem_fb_create_with_dirty, - .mode_valid = hyperv_mode_valid, + .mode_valid = hv_drm_mode_valid, .atomic_check = drm_atomic_helper_check, .atomic_commit = drm_atomic_helper_commit, }; -int hyperv_mode_config_init(struct hyperv_drm_device *hv) +int hv_drm_mode_config_init(struct hv_drm_device *hv) { struct drm_device *dev = &hv->dev; int ret; @@ -322,9 +322,9 @@ int hyperv_mode_config_init(struct hyperv_drm_device *hv) dev->mode_config.preferred_depth = hv->screen_depth; dev->mode_config.prefer_shadow = 0; - dev->mode_config.funcs = &hyperv_mode_config_funcs; + dev->mode_config.funcs = &hv_drm_mode_config_funcs; - ret = hyperv_pipe_init(hv); + ret = hv_drm_pipe_init(hv); if (ret) { drm_err(dev, "Failed to initialized pipe.\n"); return ret; diff --git a/drivers/gpu/drm/hyperv/hyperv_drm_proto.c b/drivers/gpu/drm/hyperv/hyperv_drm_proto.c index 6e09b0218df4..f0ef627b4898 100644 --- a/drivers/gpu/drm/hyperv/hyperv_drm_proto.c +++ b/drivers/gpu/drm/hyperv/hyperv_drm_proto.c @@ -181,7 +181,7 @@ struct synthvid_msg { }; } __packed; -static inline bool hyperv_version_ge(u32 ver1, u32 ver2) +static inline bool hv_drm_version_ge(u32 ver1, u32 ver2) { if (SYNTHVID_VER_GET_MAJOR(ver1) > SYNTHVID_VER_GET_MAJOR(ver2) || (SYNTHVID_VER_GET_MAJOR(ver1) == SYNTHVID_VER_GET_MAJOR(ver2) && @@ -191,10 +191,10 @@ static inline bool hyperv_version_ge(u32 ver1, u32 ver2) return false; } -static inline int hyperv_sendpacket(struct hv_device *hdev, struct synthvid_msg *msg) +static inline int hv_drm_sendpacket(struct hv_device *hdev, struct synthvid_msg *msg) { static atomic64_t request_id = ATOMIC64_INIT(0); - struct hyperv_drm_device *hv = hv_get_drvdata(hdev); + struct hv_drm_device *hv = hv_get_drvdata(hdev); int ret; msg->pipe_hdr.type = PIPE_MSG_DATA; @@ -211,9 +211,9 @@ static inline int hyperv_sendpacket(struct hv_device *hdev, struct synthvid_msg return ret; } -static int hyperv_negotiate_version(struct hv_device *hdev, u32 ver) +static int hv_drm_negotiate_version(struct hv_device *hdev, u32 ver) { - struct hyperv_drm_device *hv = hv_get_drvdata(hdev); + struct hv_drm_device *hv = hv_get_drvdata(hdev); struct synthvid_msg *msg = (struct synthvid_msg *)hv->init_buf; struct drm_device *dev = &hv->dev; unsigned long t; @@ -223,7 +223,7 @@ static int hyperv_negotiate_version(struct hv_device *hdev, u32 ver) msg->vid_hdr.size = sizeof(struct synthvid_msg_hdr) + sizeof(struct synthvid_version_req); msg->ver_req.version = ver; - hyperv_sendpacket(hdev, msg); + hv_drm_sendpacket(hdev, msg); t = wait_for_completion_timeout(&hv->wait, VMBUS_VSP_TIMEOUT); if (!t) { @@ -243,9 +243,9 @@ static int hyperv_negotiate_version(struct hv_device *hdev, u32 ver) return 0; } -int hyperv_update_vram_location(struct hv_device *hdev, phys_addr_t vram_pp) +int hv_drm_update_vram_location(struct hv_device *hdev, phys_addr_t vram_pp) { - struct hyperv_drm_device *hv = hv_get_drvdata(hdev); + struct hv_drm_device *hv = hv_get_drvdata(hdev); struct synthvid_msg *msg = (struct synthvid_msg *)hv->init_buf; struct drm_device *dev = &hv->dev; unsigned long t; @@ -257,7 +257,7 @@ int hyperv_update_vram_location(struct hv_device *hdev, phys_addr_t vram_pp) msg->vram.user_ctx = vram_pp; msg->vram.vram_gpa = vram_pp; msg->vram.is_vram_gpa_specified = 1; - hyperv_sendpacket(hdev, msg); + hv_drm_sendpacket(hdev, msg); t = wait_for_completion_timeout(&hv->wait, VMBUS_VSP_TIMEOUT); if (!t) { @@ -272,7 +272,7 @@ int hyperv_update_vram_location(struct hv_device *hdev, phys_addr_t vram_pp) return 0; } -int hyperv_update_situation(struct hv_device *hdev, u8 active, u32 bpp, +int hv_drm_update_situation(struct hv_device *hdev, u8 active, u32 bpp, u32 w, u32 h, u32 pitch) { struct synthvid_msg msg; @@ -292,7 +292,7 @@ int hyperv_update_situation(struct hv_device *hdev, u8 active, u32 bpp, msg.situ.video_output[0].height_pixels = h; msg.situ.video_output[0].pitch_bytes = pitch; - hyperv_sendpacket(hdev, &msg); + hv_drm_sendpacket(hdev, &msg); return 0; } @@ -306,11 +306,11 @@ int hyperv_update_situation(struct hv_device *hdev, u8 active, u32 bpp, * the msg.ptr_shape.data. Note: setting msg.ptr_pos.is_visible to 0 doesn't * work in tests. * - * The hyperv_hide_hw_ptr() is also called in the handler of the + * The hv_drm_hide_hw_ptr() is also called in the handler of the * SYNTHVID_FEATURE_CHANGE event, otherwise the host still draws an extra * unwanted mouse pointer after the VM Connection window is closed and reopened. */ -int hyperv_hide_hw_ptr(struct hv_device *hdev) +int hv_drm_hide_hw_ptr(struct hv_device *hdev) { struct synthvid_msg msg; @@ -322,7 +322,7 @@ int hyperv_hide_hw_ptr(struct hv_device *hdev) msg.ptr_pos.video_output = 0; msg.ptr_pos.image_x = 0; msg.ptr_pos.image_y = 0; - hyperv_sendpacket(hdev, &msg); + hv_drm_sendpacket(hdev, &msg); memset(&msg, 0, sizeof(struct synthvid_msg)); msg.vid_hdr.type = SYNTHVID_POINTER_SHAPE; @@ -338,14 +338,14 @@ int hyperv_hide_hw_ptr(struct hv_device *hdev) msg.ptr_shape.data[1] = 1; msg.ptr_shape.data[2] = 1; msg.ptr_shape.data[3] = 1; - hyperv_sendpacket(hdev, &msg); + hv_drm_sendpacket(hdev, &msg); return 0; } -int hyperv_update_dirt(struct hv_device *hdev, struct drm_rect *rect) +int hv_drm_update_dirt(struct hv_device *hdev, struct drm_rect *rect) { - struct hyperv_drm_device *hv = hv_get_drvdata(hdev); + struct hv_drm_device *hv = hv_get_drvdata(hdev); struct synthvid_msg msg; if (!hv->dirt_needed) @@ -363,14 +363,14 @@ int hyperv_update_dirt(struct hv_device *hdev, struct drm_rect *rect) msg.dirt.rect[0].x2 = rect->x2; msg.dirt.rect[0].y2 = rect->y2; - hyperv_sendpacket(hdev, &msg); + hv_drm_sendpacket(hdev, &msg); return 0; } -static int hyperv_get_supported_resolution(struct hv_device *hdev) +static int hv_drm_get_supported_resolution(struct hv_device *hdev) { - struct hyperv_drm_device *hv = hv_get_drvdata(hdev); + struct hv_drm_device *hv = hv_get_drvdata(hdev); struct synthvid_msg *msg = (struct synthvid_msg *)hv->init_buf; struct drm_device *dev = &hv->dev; unsigned long t; @@ -383,7 +383,7 @@ static int hyperv_get_supported_resolution(struct hv_device *hdev) sizeof(struct synthvid_supported_resolution_req); msg->resolution_req.maximum_resolution_count = SYNTHVID_MAX_RESOLUTION_COUNT; - hyperv_sendpacket(hdev, msg); + hv_drm_sendpacket(hdev, msg); t = wait_for_completion_timeout(&hv->wait, VMBUS_VSP_TIMEOUT); if (!t) { @@ -420,9 +420,9 @@ static int hyperv_get_supported_resolution(struct hv_device *hdev) return 0; } -static void hyperv_receive_sub(struct hv_device *hdev, u32 bytes_recvd) +static void hv_drm_receive_sub(struct hv_device *hdev, u32 bytes_recvd) { - struct hyperv_drm_device *hv = hv_get_drvdata(hdev); + struct hv_drm_device *hv = hv_get_drvdata(hdev); struct synthvid_msg *msg; size_t hdr_size; size_t need; @@ -486,7 +486,7 @@ static void hyperv_receive_sub(struct hv_device *hdev, u32 bytes_recvd) } hv->dirt_needed = msg->feature_chg.is_dirt_needed; if (hv->dirt_needed) - hyperv_hide_hw_ptr(hv->hdev); + hv_drm_hide_hw_ptr(hv->hdev); return; default: return; @@ -508,10 +508,10 @@ static void hyperv_receive_sub(struct hv_device *hdev, u32 bytes_recvd) complete(&hv->wait); } -static void hyperv_receive(void *ctx) +static void hv_drm_receive(void *ctx) { struct hv_device *hdev = ctx; - struct hyperv_drm_device *hv = hv_get_drvdata(hdev); + struct hv_drm_device *hv = hv_get_drvdata(hdev); struct synthvid_msg *recv_buf; u32 bytes_recvd; u64 req_id; @@ -539,19 +539,19 @@ static void hyperv_receive(void *ctx) ret, bytes_recvd); } else if (bytes_recvd > 0 && recv_buf->pipe_hdr.type == PIPE_MSG_DATA) { - hyperv_receive_sub(hdev, bytes_recvd); + hv_drm_receive_sub(hdev, bytes_recvd); } } while (bytes_recvd > 0 && ret == 0); } -int hyperv_connect_vsp(struct hv_device *hdev) +int hv_drm_connect_vsp(struct hv_device *hdev) { - struct hyperv_drm_device *hv = hv_get_drvdata(hdev); + struct hv_drm_device *hv = hv_get_drvdata(hdev); struct drm_device *dev = &hv->dev; int ret; ret = vmbus_open(hdev->channel, VMBUS_RING_BUFSIZE, VMBUS_RING_BUFSIZE, - NULL, 0, hyperv_receive, hdev); + NULL, 0, hv_drm_receive, hdev); if (ret) { drm_err(dev, "Unable to open vmbus channel\n"); return ret; @@ -561,16 +561,16 @@ int hyperv_connect_vsp(struct hv_device *hdev) switch (vmbus_proto_version) { case VERSION_WIN10: case VERSION_WIN10_V5: - ret = hyperv_negotiate_version(hdev, SYNTHVID_VERSION_WIN10); + ret = hv_drm_negotiate_version(hdev, SYNTHVID_VERSION_WIN10); if (!ret) break; fallthrough; case VERSION_WIN8: case VERSION_WIN8_1: - ret = hyperv_negotiate_version(hdev, SYNTHVID_VERSION_WIN8); + ret = hv_drm_negotiate_version(hdev, SYNTHVID_VERSION_WIN8); break; default: - ret = hyperv_negotiate_version(hdev, SYNTHVID_VERSION_WIN10); + ret = hv_drm_negotiate_version(hdev, SYNTHVID_VERSION_WIN10); break; } @@ -581,8 +581,8 @@ int hyperv_connect_vsp(struct hv_device *hdev) hv->screen_depth = SYNTHVID_DEPTH_WIN8; - if (hyperv_version_ge(hv->synthvid_version, SYNTHVID_VERSION_WIN10)) { - ret = hyperv_get_supported_resolution(hdev); + if (hv_drm_version_ge(hv->synthvid_version, SYNTHVID_VERSION_WIN10)) { + ret = hv_drm_get_supported_resolution(hdev); if (ret) drm_err(dev, "Failed to get supported resolution from host, use default\n"); } diff --git a/drivers/gpu/drm/i915/display/intel_crtc.c b/drivers/gpu/drm/i915/display/intel_crtc.c index 7ce2b52297d1..10ed9bdfee76 100644 --- a/drivers/gpu/drm/i915/display/intel_crtc.c +++ b/drivers/gpu/drm/i915/display/intel_crtc.c @@ -182,7 +182,7 @@ void intel_crtc_state_reset(struct intel_crtc_state *crtc_state, { memset(crtc_state, 0, sizeof(*crtc_state)); - __drm_atomic_helper_crtc_state_reset(&crtc_state->uapi, &crtc->base); + __drm_atomic_helper_crtc_state_init(&crtc_state->uapi, &crtc->base); crtc_state->cpu_transcoder = INVALID_TRANSCODER; crtc_state->master_transcoder = INVALID_TRANSCODER; diff --git a/drivers/gpu/drm/i915/display/intel_dp.c b/drivers/gpu/drm/i915/display/intel_dp.c index 85d3aa3b9894..c81fc00a334c 100644 --- a/drivers/gpu/drm/i915/display/intel_dp.c +++ b/drivers/gpu/drm/i915/display/intel_dp.c @@ -2203,17 +2203,56 @@ static int dsc_compute_link_config(struct intel_dp *intel_dp, return -EINVAL; } +static u16 intel_dp_dsc_max_delta_bppx16(const struct intel_connector *connector, + enum intel_output_format output_format) +{ + const u8 *dsc_dpcd = connector->dp.dsc_dpcd; + u8 max_bpp_delta_v1 = dsc_dpcd[DP_DSC_MAX_BPP_DELTA_VERSION_1 - DP_DSC_SUPPORT]; + int max_bpp; + + if (!(dsc_dpcd[DP_DSC_MAX_BITS_PER_PIXEL_HI - DP_DSC_SUPPORT] & + DP_DSC_MAX_BPP_DELTA_AVAILABILITY)) + return 0; + + switch (output_format) { + case INTEL_OUTPUT_FORMAT_RGB: + case INTEL_OUTPUT_FORMAT_YCBCR444: + max_bpp = max_bpp_delta_v1 & DP_DSC_RGB_YCbCr444_MAX_BPP_DELTA_MASK; + if (max_bpp >= 1 && max_bpp <= 21) + max_bpp = max_bpp + DP_DSC_BPP_DELTA_444 - 1; + else + max_bpp = 0; + break; + case INTEL_OUTPUT_FORMAT_YCBCR420: + max_bpp = (max_bpp_delta_v1 & DP_DSC_NATIVE_YCbCr420_MAX_BPP_DELTA_MASK) >> + DP_DSC_BPP_DELTA_SHIFT_420; + if (max_bpp >= 1 && max_bpp <= 7) + max_bpp = max_bpp + DP_DSC_BPP_DELTA_420 - 1; + break; + default: + MISSING_CASE(output_format); + return 0; + } + + return max_bpp << 4; +} + static u16 intel_dp_dsc_max_sink_compressed_bppx16(const struct intel_connector *connector, enum intel_output_format output_format, int bpc) { - u16 max_bppx16 = drm_edp_dsc_sink_output_bpp(connector->dp.dsc_dpcd); + u16 max_bppx16 = intel_dp_dsc_max_delta_bppx16(connector, output_format); + + if (max_bppx16) + return max_bppx16; + + max_bppx16 = drm_edp_dsc_sink_output_bpp(connector->dp.dsc_dpcd); if (max_bppx16) return max_bppx16; /* - * If support not given in DPCD 67h, 68h use the Maximum Allowed bit rate + * If support not given in DPCD 67h, 68h, 6Eh, 6Fh use the Maximum Allowed bit rate * values as given in spec Table 2-157 DP v2.0 */ switch (output_format) { diff --git a/drivers/gpu/drm/i915/display/intel_plane.c b/drivers/gpu/drm/i915/display/intel_plane.c index e087554488b4..7017ed6cb9d1 100644 --- a/drivers/gpu/drm/i915/display/intel_plane.c +++ b/drivers/gpu/drm/i915/display/intel_plane.c @@ -67,7 +67,7 @@ static void intel_plane_state_reset(struct intel_plane_state *plane_state, { memset(plane_state, 0, sizeof(*plane_state)); - __drm_atomic_helper_plane_state_reset(&plane_state->uapi, &plane->base); + __drm_atomic_helper_plane_state_init(&plane_state->uapi, &plane->base); plane_state->scaler_id = -1; plane_state->fence_id = -1; diff --git a/drivers/gpu/drm/i915/i915_active.c b/drivers/gpu/drm/i915/i915_active.c index aa77def0bc0d..b2442f05825e 100644 --- a/drivers/gpu/drm/i915/i915_active.c +++ b/drivers/gpu/drm/i915/i915_active.c @@ -548,7 +548,7 @@ static void enable_signaling(struct i915_active_fence *active) if (!fence) return; - dma_fence_enable_sw_signaling(fence); + dma_fence_enable_signaling(fence); dma_fence_put(fence); } diff --git a/drivers/gpu/drm/imagination/pvr_drv.c b/drivers/gpu/drm/imagination/pvr_drv.c index 58087e8517d6..8897b3bdeb4a 100644 --- a/drivers/gpu/drm/imagination/pvr_drv.c +++ b/drivers/gpu/drm/imagination/pvr_drv.c @@ -1378,7 +1378,7 @@ pvr_drm_driver_postclose(__always_unused struct drm_device *drm_dev, DEFINE_DRM_GEM_FOPS(pvr_drm_driver_fops); static struct drm_driver pvr_drm_driver = { - .driver_features = DRIVER_GEM | DRIVER_GEM_GPUVA | DRIVER_RENDER | + .driver_features = DRIVER_GEM | DRIVER_RENDER | DRIVER_SYNCOBJ | DRIVER_SYNCOBJ_TIMELINE, .open = pvr_drm_driver_open, .postclose = pvr_drm_driver_postclose, diff --git a/drivers/gpu/drm/imx/ipuv3/parallel-display.c b/drivers/gpu/drm/imx/ipuv3/parallel-display.c index 0f06db95f00f..eb75827394f8 100644 --- a/drivers/gpu/drm/imx/ipuv3/parallel-display.c +++ b/drivers/gpu/drm/imx/ipuv3/parallel-display.c @@ -180,7 +180,7 @@ static int imx_pd_bridge_attach(struct drm_bridge *bridge, static const struct drm_bridge_funcs imx_pd_bridge_funcs = { .attach = imx_pd_bridge_attach, - .atomic_reset = drm_atomic_helper_bridge_reset, + .atomic_create_state = drm_atomic_helper_bridge_create_state, .atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state, .atomic_destroy_state = drm_atomic_helper_bridge_destroy_state, .atomic_check = imx_pd_bridge_atomic_check, diff --git a/drivers/gpu/drm/ingenic/ingenic-drm-drv.c b/drivers/gpu/drm/ingenic/ingenic-drm-drv.c index 42c86f195c66..7e569af22391 100644 --- a/drivers/gpu/drm/ingenic/ingenic-drm-drv.c +++ b/drivers/gpu/drm/ingenic/ingenic-drm-drv.c @@ -1031,7 +1031,7 @@ static const struct drm_bridge_funcs ingenic_drm_bridge_funcs = { .atomic_enable = ingenic_drm_bridge_atomic_enable, .atomic_disable = ingenic_drm_bridge_atomic_disable, .atomic_check = ingenic_drm_bridge_atomic_check, - .atomic_reset = drm_atomic_helper_bridge_reset, + .atomic_create_state = drm_atomic_helper_bridge_create_state, .atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state, .atomic_destroy_state = drm_atomic_helper_bridge_destroy_state, .atomic_get_input_bus_fmts = ingenic_drm_bridge_atomic_get_input_bus_fmts, diff --git a/drivers/gpu/drm/lima/lima_device.c b/drivers/gpu/drm/lima/lima_device.c index 0bf7105c8748..7c873e62c16d 100644 --- a/drivers/gpu/drm/lima/lima_device.c +++ b/drivers/gpu/drm/lima/lima_device.c @@ -368,12 +368,6 @@ int lima_device_init(struct lima_device *ldev) if (err) goto err_out0; - ldev->empty_vm = lima_vm_create(ldev); - if (!ldev->empty_vm) { - err = -ENOMEM; - goto err_out1; - } - ldev->va_start = 0; if (ldev->id == lima_gpu_mali450) { ldev->va_end = LIMA_VA_RESERVE_START; @@ -387,6 +381,12 @@ int lima_device_init(struct lima_device *ldev) } else ldev->va_end = LIMA_VA_RESERVE_END; + ldev->empty_vm = lima_vm_create(ldev); + if (!ldev->empty_vm) { + err = -ENOMEM; + goto err_out1; + } + ldev->iomem = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(ldev->iomem)) { dev_err(ldev->dev, "fail to ioremap iomem\n"); diff --git a/drivers/gpu/drm/loongson/lsdc_drv.c b/drivers/gpu/drm/loongson/lsdc_drv.c index 34405073c4d4..3fd828d23271 100644 --- a/drivers/gpu/drm/loongson/lsdc_drv.c +++ b/drivers/gpu/drm/loongson/lsdc_drv.c @@ -444,8 +444,8 @@ static const struct dev_pm_ops lsdc_pm_ops = { }; static const struct pci_device_id lsdc_pciid_list[] = { - {PCI_VDEVICE(LOONGSON, 0x7a06), CHIP_LS7A1000}, - {PCI_VDEVICE(LOONGSON, 0x7a36), CHIP_LS7A2000}, + { PCI_VDEVICE(LOONGSON, 0x7a06), .driver_data = CHIP_LS7A1000 }, + { PCI_VDEVICE(LOONGSON, 0x7a36), .driver_data = CHIP_LS7A2000 }, { } }; diff --git a/drivers/gpu/drm/mediatek/mtk_dp.c b/drivers/gpu/drm/mediatek/mtk_dp.c index eefbc7e0f9c8..f656b85b8421 100644 --- a/drivers/gpu/drm/mediatek/mtk_dp.c +++ b/drivers/gpu/drm/mediatek/mtk_dp.c @@ -2578,7 +2578,7 @@ static const struct drm_bridge_funcs mtk_dp_bridge_funcs = { .atomic_destroy_state = drm_atomic_helper_bridge_destroy_state, .atomic_get_output_bus_fmts = mtk_dp_bridge_atomic_get_output_bus_fmts, .atomic_get_input_bus_fmts = mtk_dp_bridge_atomic_get_input_bus_fmts, - .atomic_reset = drm_atomic_helper_bridge_reset, + .atomic_create_state = drm_atomic_helper_bridge_create_state, .attach = mtk_dp_bridge_attach, .detach = mtk_dp_bridge_detach, .atomic_enable = mtk_dp_bridge_atomic_enable, diff --git a/drivers/gpu/drm/mediatek/mtk_dpi.c b/drivers/gpu/drm/mediatek/mtk_dpi.c index bb4b7e6f1e29..959c994eef24 100644 --- a/drivers/gpu/drm/mediatek/mtk_dpi.c +++ b/drivers/gpu/drm/mediatek/mtk_dpi.c @@ -989,7 +989,7 @@ static const struct drm_bridge_funcs mtk_dpi_bridge_funcs = { .atomic_get_input_bus_fmts = mtk_dpi_bridge_atomic_get_input_bus_fmts, .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_create_state = drm_atomic_helper_bridge_create_state, .debugfs_init = mtk_dpi_debugfs_init, }; diff --git a/drivers/gpu/drm/mediatek/mtk_dsi.c b/drivers/gpu/drm/mediatek/mtk_dsi.c index eb325e68aa59..3f3f56eed3f9 100644 --- a/drivers/gpu/drm/mediatek/mtk_dsi.c +++ b/drivers/gpu/drm/mediatek/mtk_dsi.c @@ -894,7 +894,7 @@ static const struct drm_bridge_funcs mtk_dsi_bridge_funcs = { .atomic_enable = mtk_dsi_bridge_atomic_enable, .atomic_pre_enable = mtk_dsi_bridge_atomic_pre_enable, .atomic_post_disable = mtk_dsi_bridge_atomic_post_disable, - .atomic_reset = drm_atomic_helper_bridge_reset, + .atomic_create_state = drm_atomic_helper_bridge_create_state, .mode_valid = mtk_dsi_bridge_mode_valid, .mode_set = mtk_dsi_bridge_mode_set, }; diff --git a/drivers/gpu/drm/mediatek/mtk_hdmi.c b/drivers/gpu/drm/mediatek/mtk_hdmi.c index 38a7c7953874..738090a47176 100644 --- a/drivers/gpu/drm/mediatek/mtk_hdmi.c +++ b/drivers/gpu/drm/mediatek/mtk_hdmi.c @@ -1072,7 +1072,7 @@ static const struct drm_bridge_funcs mtk_hdmi_bridge_funcs = { .mode_valid = mtk_hdmi_bridge_mode_valid, .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_create_state = drm_atomic_helper_bridge_create_state, .attach = mtk_hdmi_bridge_attach, .mode_fixup = mtk_hdmi_bridge_mode_fixup, .atomic_disable = mtk_hdmi_bridge_atomic_disable, diff --git a/drivers/gpu/drm/mediatek/mtk_hdmi_v2.c b/drivers/gpu/drm/mediatek/mtk_hdmi_v2.c index 7bbf463056c9..d9316d30fd5f 100644 --- a/drivers/gpu/drm/mediatek/mtk_hdmi_v2.c +++ b/drivers/gpu/drm/mediatek/mtk_hdmi_v2.c @@ -1326,7 +1326,7 @@ static const struct drm_bridge_funcs mtk_v2_hdmi_bridge_funcs = { .atomic_post_disable = mtk_hdmi_v2_bridge_post_disable, .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_create_state = drm_atomic_helper_bridge_create_state, .detect = mtk_hdmi_v2_bridge_detect, .edid_read = mtk_hdmi_v2_bridge_edid_read, .hpd_enable = mtk_hdmi_v2_hpd_enable, diff --git a/drivers/gpu/drm/meson/meson_encoder_cvbs.c b/drivers/gpu/drm/meson/meson_encoder_cvbs.c index 8b26a0031cde..22cacb1660c4 100644 --- a/drivers/gpu/drm/meson/meson_encoder_cvbs.c +++ b/drivers/gpu/drm/meson/meson_encoder_cvbs.c @@ -215,7 +215,7 @@ static const struct drm_bridge_funcs meson_encoder_cvbs_bridge_funcs = { .atomic_check = meson_encoder_cvbs_atomic_check, .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_create_state = drm_atomic_helper_bridge_create_state, }; int meson_encoder_cvbs_probe(struct meson_drm *priv) diff --git a/drivers/gpu/drm/meson/meson_encoder_dsi.c b/drivers/gpu/drm/meson/meson_encoder_dsi.c index c1f4685073bb..3e422b612f74 100644 --- a/drivers/gpu/drm/meson/meson_encoder_dsi.c +++ b/drivers/gpu/drm/meson/meson_encoder_dsi.c @@ -96,7 +96,7 @@ static const struct drm_bridge_funcs meson_encoder_dsi_bridge_funcs = { .atomic_disable = meson_encoder_dsi_atomic_disable, .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_create_state = drm_atomic_helper_bridge_create_state, }; int meson_encoder_dsi_probe(struct meson_drm *priv) diff --git a/drivers/gpu/drm/meson/meson_encoder_hdmi.c b/drivers/gpu/drm/meson/meson_encoder_hdmi.c index 55c0601df3c6..0c7a72cb514a 100644 --- a/drivers/gpu/drm/meson/meson_encoder_hdmi.c +++ b/drivers/gpu/drm/meson/meson_encoder_hdmi.c @@ -366,7 +366,7 @@ static const struct drm_bridge_funcs meson_encoder_hdmi_bridge_funcs = { .atomic_check = meson_encoder_hdmi_atomic_check, .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_create_state = drm_atomic_helper_bridge_create_state, }; int meson_encoder_hdmi_probe(struct meson_drm *priv) diff --git a/drivers/gpu/drm/mgag200/mgag200_drv.c b/drivers/gpu/drm/mgag200/mgag200_drv.c index d7b5dfdb28aa..dfae5812284d 100644 --- a/drivers/gpu/drm/mgag200/mgag200_drv.c +++ b/drivers/gpu/drm/mgag200/mgag200_drv.c @@ -204,18 +204,18 @@ int mgag200_device_init(struct mga_device *mdev, */ static const struct pci_device_id mgag200_pciidlist[] = { - { PCI_VENDOR_ID_MATROX, 0x520, PCI_ANY_ID, PCI_ANY_ID, 0, 0, G200_PCI }, - { PCI_VENDOR_ID_MATROX, 0x521, PCI_ANY_ID, PCI_ANY_ID, 0, 0, G200_AGP }, - { PCI_VENDOR_ID_MATROX, 0x522, PCI_ANY_ID, PCI_ANY_ID, 0, 0, G200_SE_A }, - { PCI_VENDOR_ID_MATROX, 0x524, PCI_ANY_ID, PCI_ANY_ID, 0, 0, G200_SE_B }, - { PCI_VENDOR_ID_MATROX, 0x530, PCI_ANY_ID, PCI_ANY_ID, 0, 0, G200_EV }, - { PCI_VENDOR_ID_MATROX, 0x532, PCI_ANY_ID, PCI_ANY_ID, 0, 0, G200_WB }, - { PCI_VENDOR_ID_MATROX, 0x533, PCI_ANY_ID, PCI_ANY_ID, 0, 0, G200_EH }, - { PCI_VENDOR_ID_MATROX, 0x534, PCI_ANY_ID, PCI_ANY_ID, 0, 0, G200_ER }, - { PCI_VENDOR_ID_MATROX, 0x536, PCI_ANY_ID, PCI_ANY_ID, 0, 0, G200_EW3 }, - { PCI_VENDOR_ID_MATROX, 0x538, PCI_ANY_ID, PCI_ANY_ID, 0, 0, G200_EH3 }, - { PCI_VENDOR_ID_MATROX, 0x53a, PCI_ANY_ID, PCI_ANY_ID, 0, 0, G200_EH5 }, - {0,} + { PCI_VDEVICE(MATROX, 0x0520), .driver_data = G200_PCI }, + { PCI_VDEVICE(MATROX, 0x0521), .driver_data = G200_AGP }, + { PCI_VDEVICE(MATROX, 0x0522), .driver_data = G200_SE_A }, + { PCI_VDEVICE(MATROX, 0x0524), .driver_data = G200_SE_B }, + { PCI_VDEVICE(MATROX, 0x0530), .driver_data = G200_EV }, + { PCI_VDEVICE(MATROX, 0x0532), .driver_data = G200_WB }, + { PCI_VDEVICE(MATROX, 0x0533), .driver_data = G200_EH }, + { PCI_VDEVICE(MATROX, 0x0534), .driver_data = G200_ER }, + { PCI_VDEVICE(MATROX, 0x0536), .driver_data = G200_EW3 }, + { PCI_VDEVICE(MATROX, 0x0538), .driver_data = G200_EH3 }, + { PCI_VDEVICE(MATROX, 0x053a), .driver_data = G200_EH5 }, + { } }; MODULE_DEVICE_TABLE(pci, mgag200_pciidlist); diff --git a/drivers/gpu/drm/msm/dp/dp_drm.c b/drivers/gpu/drm/msm/dp/dp_drm.c index b659d22f5f28..06881bfd6181 100644 --- a/drivers/gpu/drm/msm/dp/dp_drm.c +++ b/drivers/gpu/drm/msm/dp/dp_drm.c @@ -52,7 +52,7 @@ static void msm_dp_bridge_debugfs_init(struct drm_bridge *bridge, struct dentry static const struct drm_bridge_funcs msm_dp_bridge_ops = { .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_create_state = drm_atomic_helper_bridge_create_state, .atomic_enable = msm_dp_bridge_atomic_enable, .atomic_disable = msm_dp_bridge_atomic_disable, .atomic_post_disable = msm_dp_bridge_atomic_post_disable, @@ -235,7 +235,7 @@ static const struct drm_bridge_funcs msm_edp_bridge_ops = { .atomic_post_disable = msm_edp_bridge_atomic_post_disable, .mode_set = msm_dp_bridge_mode_set, .mode_valid = msm_edp_bridge_mode_valid, - .atomic_reset = drm_atomic_helper_bridge_reset, + .atomic_create_state = drm_atomic_helper_bridge_create_state, .atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state, .atomic_destroy_state = drm_atomic_helper_bridge_destroy_state, .atomic_check = msm_edp_bridge_atomic_check, diff --git a/drivers/gpu/drm/msm/dsi/dsi_host.c b/drivers/gpu/drm/msm/dsi/dsi_host.c index 982abaaac00d..de1ba11987fc 100644 --- a/drivers/gpu/drm/msm/dsi/dsi_host.c +++ b/drivers/gpu/drm/msm/dsi/dsi_host.c @@ -166,6 +166,7 @@ struct msm_dsi_host { struct drm_display_mode *mode; struct drm_dsc_config *dsc; + unsigned int dsc_slice_per_pkt; /* connected device info */ unsigned int channel; @@ -938,17 +939,10 @@ static void dsi_update_dsc_timing(struct msm_dsi_host *msm_host, bool is_cmd_mod slice_per_intf = dsc->slice_count; total_bytes_per_intf = dsc->slice_chunk_size * slice_per_intf; - bytes_per_pkt = dsc->slice_chunk_size; /* * slice_per_pkt; */ + bytes_per_pkt = dsc->slice_chunk_size * msm_host->dsc_slice_per_pkt; eol_byte_num = total_bytes_per_intf % 3; - - /* - * Typically, pkt_per_line = slice_per_intf * slice_per_pkt. - * - * Since the current driver only supports slice_per_pkt = 1, - * pkt_per_line will be equal to slice per intf for now. - */ - pkt_per_line = slice_per_intf; + pkt_per_line = slice_per_intf / msm_host->dsc_slice_per_pkt; if (is_cmd_mode) /* packet data type */ reg = DSI_COMMAND_COMPRESSION_MODE_CTRL_STREAM0_DATATYPE(MIPI_DSI_DCS_LONG_WRITE); @@ -1104,12 +1098,8 @@ static void dsi_timing_setup(struct msm_dsi_host *msm_host, bool is_bonded_dsi) else /* * When DSC is enabled, WC = slice_chunk_size * slice_per_pkt + 1. - * Currently, the driver only supports default value of slice_per_pkt = 1 - * - * TODO: Expand mipi_dsi_device struct to hold slice_per_pkt info - * and adjust DSC math to account for slice_per_pkt. */ - wc = msm_host->dsc->slice_chunk_size + 1; + wc = msm_host->dsc->slice_chunk_size * msm_host->dsc_slice_per_pkt + 1; dsi_write(msm_host, REG_DSI_CMD_MDP_STREAM0_CTRL, DSI_CMD_MDP_STREAM0_CTRL_WORD_COUNT(wc) | @@ -1718,8 +1708,13 @@ static int dsi_host_attach(struct mipi_dsi_host *host, msm_host->lanes = dsi->lanes; msm_host->format = dsi->format; msm_host->mode_flags = dsi->mode_flags; - if (dsi->dsc) + if (dsi->dsc) { msm_host->dsc = dsi->dsc; + if (dsi->mode_flags & MIPI_DSI_MODE_DSC_ALL_SLICES_IN_PKT) + msm_host->dsc_slice_per_pkt = dsi->dsc->slice_count; + else + msm_host->dsc_slice_per_pkt = 1; + } if (msm_host->format == MIPI_DSI_FMT_RGB101010) { if (!msm_dsi_host_version_geq(msm_host, MSM_DSI_VER_MAJOR_6G, diff --git a/drivers/gpu/drm/msm/hdmi/hdmi_bridge.c b/drivers/gpu/drm/msm/hdmi/hdmi_bridge.c index c4d5e0faf3b3..7abb9243dba5 100644 --- a/drivers/gpu/drm/msm/hdmi/hdmi_bridge.c +++ b/drivers/gpu/drm/msm/hdmi/hdmi_bridge.c @@ -455,7 +455,7 @@ static enum drm_mode_status msm_hdmi_bridge_tmds_char_rate_valid(const struct dr static const struct drm_bridge_funcs msm_hdmi_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_create_state = drm_atomic_helper_bridge_create_state, .atomic_pre_enable = msm_hdmi_bridge_atomic_pre_enable, .atomic_post_disable = msm_hdmi_bridge_atomic_post_disable, .edid_read = msm_hdmi_bridge_edid_read, diff --git a/drivers/gpu/drm/msm/msm_drv.c b/drivers/gpu/drm/msm/msm_drv.c index 32d5ebea2596..ac86b427c0e5 100644 --- a/drivers/gpu/drm/msm/msm_drv.c +++ b/drivers/gpu/drm/msm/msm_drv.c @@ -824,7 +824,6 @@ static const struct file_operations fops = { #define DRIVER_FEATURES_GPU ( \ DRIVER_GEM | \ - DRIVER_GEM_GPUVA | \ DRIVER_RENDER | \ DRIVER_SYNCOBJ | \ DRIVER_SYNCOBJ_TIMELINE | \ @@ -832,7 +831,6 @@ static const struct file_operations fops = { #define DRIVER_FEATURES_KMS ( \ DRIVER_GEM | \ - DRIVER_GEM_GPUVA | \ DRIVER_ATOMIC | \ DRIVER_MODESET | \ 0 ) diff --git a/drivers/gpu/drm/mxsfb/lcdif_drv.c b/drivers/gpu/drm/mxsfb/lcdif_drv.c index f5bb59cd5028..e2173c4d6fc2 100644 --- a/drivers/gpu/drm/mxsfb/lcdif_drv.c +++ b/drivers/gpu/drm/mxsfb/lcdif_drv.c @@ -186,7 +186,7 @@ static int lcdif_load(struct drm_device *drm) ret = lcdif_attach_bridge(lcdif); if (ret) - return dev_err_probe(drm->dev, ret, "Cannot connect bridge\n"); + return ret; drm->mode_config.min_width = LCDIF_MIN_XRES; drm->mode_config.min_height = LCDIF_MIN_YRES; diff --git a/drivers/gpu/drm/mxsfb/lcdif_kms.c b/drivers/gpu/drm/mxsfb/lcdif_kms.c index ade76c3f4e4e..79e0f483ca20 100644 --- a/drivers/gpu/drm/mxsfb/lcdif_kms.c +++ b/drivers/gpu/drm/mxsfb/lcdif_kms.c @@ -338,7 +338,8 @@ static void lcdif_set_mode(struct lcdif_drm_private *lcdif, u32 bus_flags) * Downstream set it to 256B burst size to improve the memory * efficiency so set it here too. */ - ctrl = CTRLDESCL0_3_P_SIZE(2) | CTRLDESCL0_3_T_SIZE(2) | + ctrl = CTRLDESCL0_3_STATE_CLEAR_VSYNC | + CTRLDESCL0_3_P_SIZE(2) | CTRLDESCL0_3_T_SIZE(2) | CTRLDESCL0_3_PITCH(lcdif->crtc.primary->state->fb->pitches[0]); writel(ctrl, lcdif->base + LCDC_V8_CTRLDESCL0_3); } @@ -374,14 +375,23 @@ static void lcdif_disable_controller(struct lcdif_drm_private *lcdif) int ret; reg = readl(lcdif->base + LCDC_V8_CTRLDESCL0_5); + /* Disable the layer for DMA. */ reg &= ~CTRLDESCL0_5_EN; + /* + * It is necessary to wait for the full frame to finish streaming + * through the DMA engine before we can safely disable it by removing + * the DISP_PARA_DISP_ON bit. Disabling it in-flight can leave the + * hardware confused and unable to resume streaming for the next frame. + */ + reg |= CTRLDESCL0_5_SHADOW_LOAD_EN; writel(reg, lcdif->base + LCDC_V8_CTRLDESCL0_5); + /* Wait for the frame to finish or timeout after 50 ms. */ ret = readl_poll_timeout(lcdif->base + LCDC_V8_CTRLDESCL0_5, - reg, !(reg & CTRLDESCL0_5_EN), - 0, 36000); /* Wait ~2 frame times max */ + reg, !(reg & CTRLDESCL0_5_SHADOW_LOAD_EN), + 200, 50000); if (ret) - drm_err(lcdif->drm, "Failed to disable controller!\n"); + drm_err(lcdif->drm, "Timed out waiting for final vblank!\n"); reg = readl(lcdif->base + LCDC_V8_DISP_PARA); reg &= ~DISP_PARA_DISP_ON; diff --git a/drivers/gpu/drm/mxsfb/lcdif_regs.h b/drivers/gpu/drm/mxsfb/lcdif_regs.h index c55dfb236c1d..17882c593d27 100644 --- a/drivers/gpu/drm/mxsfb/lcdif_regs.h +++ b/drivers/gpu/drm/mxsfb/lcdif_regs.h @@ -190,6 +190,7 @@ #define CTRLDESCL0_1_WIDTH(n) ((n) & 0xffff) #define CTRLDESCL0_1_WIDTH_MASK GENMASK(15, 0) +#define CTRLDESCL0_3_STATE_CLEAR_VSYNC BIT(23) #define CTRLDESCL0_3_P_SIZE(n) (((n) << 20) & CTRLDESCL0_3_P_SIZE_MASK) #define CTRLDESCL0_3_P_SIZE_MASK GENMASK(22, 20) #define CTRLDESCL0_3_T_SIZE(n) (((n) << 16) & CTRLDESCL0_3_T_SIZE_MASK) diff --git a/drivers/gpu/drm/nouveau/nouveau_drm.c b/drivers/gpu/drm/nouveau/nouveau_drm.c index e16f59b00f6f..7ea95ab960a0 100644 --- a/drivers/gpu/drm/nouveau/nouveau_drm.c +++ b/drivers/gpu/drm/nouveau/nouveau_drm.c @@ -153,22 +153,6 @@ nouveau_name(struct drm_device *dev) return nouveau_platform_name(to_platform_device(dev->dev)); } -static inline bool -nouveau_cli_work_ready(struct dma_fence *fence) -{ - unsigned long flags; - bool ret = true; - - dma_fence_lock_irqsave(fence, flags); - if (!dma_fence_is_signaled_locked(fence)) - ret = false; - dma_fence_unlock_irqrestore(fence, flags); - - if (ret == true) - dma_fence_put(fence); - return ret; -} - static void nouveau_cli_work(struct work_struct *w) { @@ -176,9 +160,25 @@ nouveau_cli_work(struct work_struct *w) struct nouveau_cli_work *work, *wtmp; mutex_lock(&cli->lock); list_for_each_entry_safe(work, wtmp, &cli->worker, head) { - if (!work->fence || nouveau_cli_work_ready(work->fence)) { + struct dma_fence *fence = work->fence; + + if (!fence || dma_fence_is_signaled(fence)) { + if (fence) { + unsigned long flags; + + /* + * Because fence can still be in the process of + * processing the callback list, and the + * callback references the work we are about to + * free, we need to sync with the callback + * processing before freeing the work. + */ + dma_fence_lock_irqsave(fence, flags); + dma_fence_unlock_irqrestore(fence, flags); + } list_del(&work->head); work->func(work); + dma_fence_put(fence); } } mutex_unlock(&cli->lock); @@ -1364,7 +1364,6 @@ static struct drm_driver driver_stub = { .driver_features = DRIVER_GEM | DRIVER_SYNCOBJ | DRIVER_SYNCOBJ_TIMELINE | - DRIVER_GEM_GPUVA | DRIVER_MODESET | DRIVER_RENDER, .open = nouveau_drm_open, diff --git a/drivers/gpu/drm/nouveau/nouveau_gem.c b/drivers/gpu/drm/nouveau/nouveau_gem.c index 20dba02d6175..c5a24dff4b69 100644 --- a/drivers/gpu/drm/nouveau/nouveau_gem.c +++ b/drivers/gpu/drm/nouveau/nouveau_gem.c @@ -313,11 +313,20 @@ nouveau_gem_info(struct drm_file *file_priv, struct drm_gem_object *gem, rep->offset = nvbo->offset; if (vmm->vmm.object.oclass >= NVIF_CLASS_VMM_NV50 && !nouveau_cli_uvmm(cli)) { + int ret; + + ret = ttm_bo_reserve(&nvbo->bo, false, false, NULL); + if (ret) + return ret; + vma = nouveau_vma_find(nvbo, vmm); - if (!vma) + if (!vma) { + ttm_bo_unreserve(&nvbo->bo); return -EINVAL; + } rep->offset = vma->addr; + ttm_bo_unreserve(&nvbo->bo); } else rep->offset = 0; diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/gv100.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/gv100.c index dbd984da7501..0608266188d3 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/gv100.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/gv100.c @@ -253,7 +253,7 @@ gv100_head_vblank_get(struct nvkm_head *head) nvkm_mask(device, 0x611d80 + (head->id * 4), 0x00000004, 0x00000004); } -static void +void gv100_head_rgpos(struct nvkm_head *head, u16 *hline, u16 *vline) { struct nvkm_device *device = head->disp->engine.subdev.device; @@ -263,7 +263,7 @@ gv100_head_rgpos(struct nvkm_head *head, u16 *hline, u16 *vline) *hline = nvkm_rd32(device, 0x616334 + hoff) & 0x0000ffff; } -static void +void gv100_head_state(struct nvkm_head *head, struct nvkm_head_state *state) { struct nvkm_device *device = head->disp->engine.subdev.device; diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/head.h b/drivers/gpu/drm/nouveau/nvkm/engine/disp/head.h index 856252bf559a..b642729c254f 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/head.h +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/head.h @@ -53,6 +53,8 @@ void gf119_head_rgclk(struct nvkm_head *, int); int gv100_head_cnt(struct nvkm_disp *, unsigned long *); int gv100_head_new(struct nvkm_disp *, int id); +void gv100_head_state(struct nvkm_head *head, struct nvkm_head_state *state); +void gv100_head_rgpos(struct nvkm_head *head, u16 *hline, u16 *vline); #define HEAD_MSG(h,l,f,a...) do { \ struct nvkm_head *_h = (h); \ diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/r535/disp.c b/drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/r535/disp.c index 1155f079b0c3..e77733a5d9c3 100644 --- a/drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/r535/disp.c +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/gsp/rm/r535/disp.c @@ -625,14 +625,10 @@ r535_head_vblank_get(struct nvkm_head *head) nvkm_mask(device, 0x611d80 + (head->id * 4), 0x00000002, 0x00000002); } -static void -r535_head_state(struct nvkm_head *head, struct nvkm_head_state *state) -{ -} - static const struct nvkm_head_func r535_head = { - .state = r535_head_state, + .state = gv100_head_state, + .rgpos = gv100_head_rgpos, .vblank_get = r535_head_vblank_get, .vblank_put = r535_head_vblank_put, }; diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/vmm.c b/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/vmm.c index 107bdb642f22..190c082b12c8 100644 --- a/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/vmm.c +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/vmm.c @@ -231,29 +231,26 @@ nvkm_vmm_unref_sptes(struct nvkm_vmm_iter *it, struct nvkm_vmm_pt *pgt, * covered by a number of LPTEs, the LPTEs once again take * control over their address range. * - * Determine how many LPTEs need to transition state. + * Transition each LPTE individually as each may have a + * different target state (sparse, invalid, or valid). */ - pgt->pte[ptei].s.spte_valid = false; - for (ptes = 1, ptei++; ptei < lpti; ptes++, ptei++) { + for (ptei++; ptei < lpti; ptei++) { if (pgt->pte[ptei].s.sptes) break; - pgt->pte[ptei].s.spte_valid = false; } - if (pgt->pte[pteb].s.sparse) { - TRA(it, "LPTE %05x: U -> S %d PTEs", pteb, ptes); - pair->func->sparse(vmm, pgt->pt[0], pteb, ptes); - } else if (!pgt->pte[pteb].s.lpte_valid) { - if (pair->func->invalid) { - /* If the MMU supports it, restore the LPTE to the - * INVALID state to tell the MMU there is no point - * trying to fetch the corresponding SPTEs. - */ - TRA(it, "LPTE %05x: U -> I %d PTEs", pteb, ptes); - pair->func->invalid(vmm, pgt->pt[0], pteb, ptes); + while (pteb < ptei) { + pgt->pte[pteb].s.spte_valid = false; + if (pgt->pte[pteb].s.sparse) { + TRA(it, "LPTE %05x: U -> S", pteb); + pair->func->sparse(vmm, pgt->pt[0], pteb, 1); + } else if (!pgt->pte[pteb].s.lpte_valid) { + if (pair->func->invalid) { + TRA(it, "LPTE %05x: U -> I", pteb); + pair->func->invalid(vmm, pgt->pt[0], pteb, 1); + } } - } else { - TRA(it, "LPTE %05x: V %d PTEs", pteb, ptes); + pteb++; } } } diff --git a/drivers/gpu/drm/omapdrm/dss/hdmi4.c b/drivers/gpu/drm/omapdrm/dss/hdmi4.c index e306247ed8a0..61cfd003569c 100644 --- a/drivers/gpu/drm/omapdrm/dss/hdmi4.c +++ b/drivers/gpu/drm/omapdrm/dss/hdmi4.c @@ -497,7 +497,7 @@ static const struct drm_bridge_funcs hdmi4_bridge_funcs = { .mode_set = hdmi4_bridge_mode_set, .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_create_state = drm_atomic_helper_bridge_create_state, .atomic_enable = hdmi4_bridge_enable, .atomic_disable = hdmi4_bridge_disable, .hpd_notify = hdmi4_bridge_hpd_notify, diff --git a/drivers/gpu/drm/omapdrm/dss/hdmi5.c b/drivers/gpu/drm/omapdrm/dss/hdmi5.c index ab2d4eb6787f..1ee3bbe20583 100644 --- a/drivers/gpu/drm/omapdrm/dss/hdmi5.c +++ b/drivers/gpu/drm/omapdrm/dss/hdmi5.c @@ -472,7 +472,7 @@ static const struct drm_bridge_funcs hdmi5_bridge_funcs = { .mode_set = hdmi5_bridge_mode_set, .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_create_state = drm_atomic_helper_bridge_create_state, .atomic_enable = hdmi5_bridge_enable, .atomic_disable = hdmi5_bridge_disable, .edid_read = hdmi5_bridge_edid_read, diff --git a/drivers/gpu/drm/panel/Kconfig b/drivers/gpu/drm/panel/Kconfig index 7450b27622a2..cfbfb371bc67 100644 --- a/drivers/gpu/drm/panel/Kconfig +++ b/drivers/gpu/drm/panel/Kconfig @@ -17,6 +17,16 @@ config DRM_PANEL_ABT_Y030XX067A Y030XX067A 320x480 3.0" panel as found in the YLM RG-280M, RG-300 and RG-99 handheld gaming consoles. +config DRM_PANEL_ANBERNIC_TD4310 + tristate "Anbernic TD4310 LCD panel" + depends on GPIOLIB && OF + depends on DRM_MIPI_DSI + depends on BACKLIGHT_CLASS_DEVICE + help + Say Y here to enable support for Anbernic designed panels with the + TD4310 panel controller such as the ones used on the Anbernic RG + Vita Pro. + config DRM_PANEL_ARM_VERSATILE tristate "ARM Versatile panel driver" depends on OF @@ -105,6 +115,17 @@ config DRM_PANEL_BOE_TV101WUM_LL2 Say Y here if you want to support for BOE TV101WUM-LL2 WUXGA PANEL DSI Video Mode panel +config DRM_PANEL_CHIPONE_ICNA35XX + tristate "Chipone ICNA35XX panel driver" + depends on OF + depends on DRM_MIPI_DSI + depends on BACKLIGHT_CLASS_DEVICE + select DRM_DISPLAY_HELPER + help + Say Y here if you want to enable support for the panels built + around the Chipone ICNA3512 and ICNA3520 display controllers, + such as some Tianma panels used in AYN Odin2 Portal and Thor. + config DRM_PANEL_CHIPWEALTH_CH13726A tristate "CHIPWEALTH CH13726A-based DSI panel" depends on OF @@ -264,6 +285,18 @@ config DRM_PANEL_HYDIS_HV101HD1 If M is selected the module will be called panel-hydis-hv101hd1 +config DRM_PANEL_ILITEK_ILI7807S + tristate "Ilitek ILI7807S-based panels" + depends on OF + depends on DRM_MIPI_DSI + depends on BACKLIGHT_CLASS_DEVICE + help + Say Y if you want to enable support for panels based on the + Ilitek ILI7807S display controller, such as the DLC DLC0697 + 1080x1920 MIPI DSI panel. + + If M is selected the module will be called panel-ilitek-ili7807s. + config DRM_PANEL_ILITEK_IL9322 tristate "Ilitek ILI9322 320x240 QVGA panels" depends on OF && SPI @@ -284,6 +317,15 @@ config DRM_PANEL_ILITEK_ILI9341 QVGA (240x320) RGB panels. support serial & parallel rgb interface. +config DRM_PANEL_ILITEK_ILI9488 + tristate "Ilitek ILI9488-based panels" + depends on OF + depends on DRM_MIPI_DSI + depends on BACKLIGHT_CLASS_DEVICE + help + Say Y if you want to enable support for panels based on the + Ilitek ILI9488 controller. + config DRM_PANEL_ILITEK_ILI9805 tristate "Ilitek ILI9805-based panels" depends on OF @@ -796,6 +838,18 @@ config DRM_PANEL_RENESAS_R61307 This panel controller can be found in LG Optimus Vu P895 smartphone in combination with LCD panel. +config DRM_PANEL_RENESAS_R63419 + tristate "Renesas R63419 dual-DSI video mode panels" + depends on OF && GPIOLIB + depends on DRM_MIPI_DSI + depends on BACKLIGHT_CLASS_DEVICE + help + Say Y here if you want to enable support for Ayaneo WT0600 and WT0630 + 1440x2560 60Hz dual-DSI video mode display panels with Renesas + R63419 IC. + + These panels are used in Ayaneo handheld gaming devices. + config DRM_PANEL_RENESAS_R69328 tristate "Renesas R69328 720x1280 DSI video mode panel" depends on OF @@ -1322,6 +1376,8 @@ config DRM_PANEL_VISIONOX_VTDR6130 depends on OF depends on DRM_MIPI_DSI depends on BACKLIGHT_CLASS_DEVICE + select DRM_DISPLAY_DSC_HELPER + select DRM_DISPLAY_HELPER help Say Y here if you want to enable support for Visionox VTDR6130 1080x2400 AMOLED DSI panel. diff --git a/drivers/gpu/drm/panel/Makefile b/drivers/gpu/drm/panel/Makefile index c2c5cf817116..0f29f22f589e 100644 --- a/drivers/gpu/drm/panel/Makefile +++ b/drivers/gpu/drm/panel/Makefile @@ -1,5 +1,6 @@ # SPDX-License-Identifier: GPL-2.0 obj-$(CONFIG_DRM_PANEL_ABT_Y030XX067A) += panel-abt-y030xx067a.o +obj-$(CONFIG_DRM_PANEL_ANBERNIC_TD4310) += panel-anbernic-td4310.o obj-$(CONFIG_DRM_PANEL_ARM_VERSATILE) += panel-arm-versatile.o obj-$(CONFIG_DRM_PANEL_ASUS_Z00T_TM5P5_NT35596) += panel-asus-z00t-tm5p5-n35596.o obj-$(CONFIG_DRM_PANEL_AUO_A030JTN01) += panel-auo-a030jtn01.o @@ -9,6 +10,7 @@ obj-$(CONFIG_DRM_PANEL_BOE_TD4320) += panel-boe-td4320.o obj-$(CONFIG_DRM_PANEL_BOE_TH101MB31UIG002_28A) += panel-boe-th101mb31ig002-28a.o obj-$(CONFIG_DRM_PANEL_BOE_TV101WUM_LL2) += panel-boe-tv101wum-ll2.o obj-$(CONFIG_DRM_PANEL_BOE_TV101WUM_NL6) += panel-boe-tv101wum-nl6.o +obj-$(CONFIG_DRM_PANEL_CHIPONE_ICNA35XX) += panel-chipone-icna35xx.o obj-$(CONFIG_DRM_PANEL_CHIPWEALTH_CH13726A) += panel-chipwealth-ch13726a.o obj-$(CONFIG_DRM_PANEL_DSI_CM) += panel-dsi-cm.o obj-$(CONFIG_DRM_PANEL_LVDS) += panel-lvds.o @@ -26,8 +28,10 @@ obj-$(CONFIG_DRM_PANEL_HIMAX_HX83112B) += panel-himax-hx83112b.o obj-$(CONFIG_DRM_PANEL_HIMAX_HX83121A) += panel-himax-hx83121a.o obj-$(CONFIG_DRM_PANEL_HIMAX_HX8394) += panel-himax-hx8394.o obj-$(CONFIG_DRM_PANEL_HYDIS_HV101HD1) += panel-hydis-hv101hd1.o +obj-$(CONFIG_DRM_PANEL_ILITEK_ILI7807S) += panel-ilitek-ili7807s.o obj-$(CONFIG_DRM_PANEL_ILITEK_IL9322) += panel-ilitek-ili9322.o obj-$(CONFIG_DRM_PANEL_ILITEK_ILI9341) += panel-ilitek-ili9341.o +obj-$(CONFIG_DRM_PANEL_ILITEK_ILI9488) += panel-ilitek-ili9488.o obj-$(CONFIG_DRM_PANEL_ILITEK_ILI9805) += panel-ilitek-ili9805.o obj-$(CONFIG_DRM_PANEL_ILITEK_ILI9806E_CORE) += panel-ilitek-ili9806e-core.o obj-$(CONFIG_DRM_PANEL_ILITEK_ILI9806E_DSI) += panel-ilitek-ili9806e-dsi.o @@ -78,6 +82,7 @@ obj-$(CONFIG_DRM_PANEL_RAYDIUM_RM68200) += panel-raydium-rm68200.o obj-$(CONFIG_DRM_PANEL_RAYDIUM_RM692E5) += panel-raydium-rm692e5.o obj-$(CONFIG_DRM_PANEL_RAYDIUM_RM69380) += panel-raydium-rm69380.o obj-$(CONFIG_DRM_PANEL_RENESAS_R61307) += panel-renesas-r61307.o +obj-$(CONFIG_DRM_PANEL_RENESAS_R63419) += panel-renesas-r63419.o obj-$(CONFIG_DRM_PANEL_RENESAS_R69328) += panel-renesas-r69328.o obj-$(CONFIG_DRM_PANEL_RONBO_RB070D30) += panel-ronbo-rb070d30.o obj-$(CONFIG_DRM_PANEL_SAMSUNG_AMS581VF01) += panel-samsung-ams581vf01.o diff --git a/drivers/gpu/drm/panel/panel-anbernic-td4310.c b/drivers/gpu/drm/panel/panel-anbernic-td4310.c new file mode 100644 index 000000000000..9a1b4525423c --- /dev/null +++ b/drivers/gpu/drm/panel/panel-anbernic-td4310.c @@ -0,0 +1,257 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Driver for Anbernic panels with TD4310 panel controller. + * + * Copyright (C) 2026 Chris Morgan <macromorgan@hotmail.com> + * + */ + +#include <linux/gpio/consumer.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/regulator/consumer.h> + +#include <drm/drm_mipi_dsi.h> +#include <drm/drm_panel.h> +#include <drm/drm_probe_helper.h> + +#include <video/mipi_display.h> + +struct anbernic_panel_td4310_info { + const struct drm_display_mode *display_mode; + u16 width_mm; + u16 height_mm; + u32 bus_flags; + unsigned long mode_flags; + u32 format; + u32 lanes; + u16 prepare_delay; + u16 reset_delay; + u16 init_delay; + u16 enable_delay; + u16 disable_delay; + u16 unprepare_delay; +}; + +struct anbernic_panel_td4310 { + struct device *dev; + struct mipi_dsi_device *dsi; + struct drm_panel panel; + const struct anbernic_panel_td4310_info *panel_info; + struct gpio_desc *reset_gpio; + struct gpio_desc *enable_gpio; + struct regulator *vdd; + enum drm_panel_orientation orientation; +}; + +static inline struct anbernic_panel_td4310 *panel_to_anbernic_panel_td4310(struct drm_panel *panel) +{ + return container_of(panel, struct anbernic_panel_td4310, panel); +} + +static int panel_anbernic_td4310_prepare(struct drm_panel *panel) +{ + struct anbernic_panel_td4310 *ctx = panel_to_anbernic_panel_td4310(panel); + struct mipi_dsi_device *dsi = ctx->dsi; + struct mipi_dsi_multi_context dsi_ctx = { .dsi = dsi }; + int ret; + + ret = regulator_enable(ctx->vdd); + if (ret) + return ret; + + ret = gpiod_set_value_cansleep(ctx->enable_gpio, 1); + if (ret) + goto err_enable; + + if (ctx->panel_info->enable_delay) + mipi_dsi_msleep(&dsi_ctx, ctx->panel_info->enable_delay); + + ret = gpiod_set_value_cansleep(ctx->reset_gpio, 1); + if (ret) + goto err_reset; + + mipi_dsi_msleep(&dsi_ctx, 10); + + ret = gpiod_set_value_cansleep(ctx->reset_gpio, 0); + if (ret) + goto err_reset; + + if (ctx->panel_info->reset_delay) + mipi_dsi_msleep(&dsi_ctx, ctx->panel_info->reset_delay); + + mipi_dsi_dcs_exit_sleep_mode_multi(&dsi_ctx); + mipi_dsi_msleep(&dsi_ctx, ctx->panel_info->prepare_delay); + mipi_dsi_dcs_set_display_on_multi(&dsi_ctx); + mipi_dsi_msleep(&dsi_ctx, ctx->panel_info->prepare_delay); + + if (dsi_ctx.accum_err) { + ret = dsi_ctx.accum_err; + goto err_reset; + } + + return 0; + +err_reset: + gpiod_set_value_cansleep(ctx->enable_gpio, 0); +err_enable: + regulator_disable(ctx->vdd); + return ret; +} + +static int panel_anbernic_td4310_unprepare(struct drm_panel *panel) +{ + struct anbernic_panel_td4310 *ctx = panel_to_anbernic_panel_td4310(panel); + struct mipi_dsi_device *dsi = ctx->dsi; + struct mipi_dsi_multi_context dsi_ctx = { .dsi = dsi }; + + mipi_dsi_dcs_set_display_off_multi(&dsi_ctx); + mipi_dsi_msleep(&dsi_ctx, ctx->panel_info->unprepare_delay); + mipi_dsi_dcs_enter_sleep_mode_multi(&dsi_ctx); + mipi_dsi_msleep(&dsi_ctx, ctx->panel_info->disable_delay); + + gpiod_set_value_cansleep(ctx->enable_gpio, 0); + + gpiod_set_value_cansleep(ctx->reset_gpio, 1); + + regulator_disable(ctx->vdd); + + return 0; +} + +static int panel_anbernic_td4310_get_mode(struct drm_panel *panel, + struct drm_connector *connector) +{ + struct anbernic_panel_td4310 *ctx = panel_to_anbernic_panel_td4310(panel); + const struct anbernic_panel_td4310_info *panel_info = ctx->panel_info; + + connector->display_info.bpc = 8; + connector->display_info.width_mm = panel_info->width_mm; + connector->display_info.height_mm = panel_info->height_mm; + connector->display_info.bus_flags = panel_info->bus_flags; + + return drm_connector_helper_get_modes_fixed(connector, panel_info->display_mode); +} + +static enum drm_panel_orientation panel_anbernic_td4310_get_orientation(struct drm_panel *panel) +{ + struct anbernic_panel_td4310 *ctx = panel_to_anbernic_panel_td4310(panel); + + return ctx->orientation; +} + +static const struct drm_panel_funcs panel_anbernic_td4310_funcs = { + .prepare = panel_anbernic_td4310_prepare, + .unprepare = panel_anbernic_td4310_unprepare, + .get_modes = panel_anbernic_td4310_get_mode, + .get_orientation = panel_anbernic_td4310_get_orientation, +}; + +static int panel_anbernic_td4310_probe(struct mipi_dsi_device *dsi) +{ + struct device *dev = &dsi->dev; + struct anbernic_panel_td4310 *ctx; + int ret; + + ctx = devm_drm_panel_alloc(dev, struct anbernic_panel_td4310, panel, + &panel_anbernic_td4310_funcs, + DRM_MODE_CONNECTOR_DSI); + if (IS_ERR(ctx)) + return PTR_ERR(ctx); + + ctx->dev = dev; + + ctx->panel_info = of_device_get_match_data(dev); + if (!ctx->panel_info) + return -EINVAL; + + ret = of_drm_get_panel_orientation(dev->of_node, &ctx->orientation); + if (ret < 0) + return dev_err_probe(dev, ret, "Failed to get panel orientation\n"); + + ctx->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW); + if (IS_ERR(ctx->reset_gpio)) + return dev_err_probe(dev, PTR_ERR(ctx->reset_gpio), + "Cannot get reset gpio\n"); + + ctx->enable_gpio = devm_gpiod_get_optional(dev, "enable", GPIOD_OUT_LOW); + if (IS_ERR(ctx->enable_gpio)) + return dev_err_probe(dev, PTR_ERR(ctx->enable_gpio), + "Cannot get enable gpio\n"); + + ctx->vdd = devm_regulator_get(dev, "vdd"); + if (IS_ERR(ctx->vdd)) + return dev_err_probe(dev, PTR_ERR(ctx->vdd), + "Failed to request vdd regulator\n"); + + ctx->dsi = dsi; + mipi_dsi_set_drvdata(dsi, ctx); + + dsi->lanes = ctx->panel_info->lanes; + dsi->format = ctx->panel_info->format; + dsi->mode_flags = ctx->panel_info->mode_flags; + + ret = drm_panel_of_backlight(&ctx->panel); + if (ret) + return ret; + + devm_drm_panel_add(dev, &ctx->panel); + + ret = devm_mipi_dsi_attach(dev, dsi); + if (ret < 0) + return dev_err_probe(dev, ret, "Failed to attach to DSI host\n"); + + return 0; +} + +static const struct drm_display_mode anbernic_vitapro_mode = { + .clock = 140020, + .hdisplay = 1080, + .hsync_start = 1080 + 50, + .hsync_end = 1080 + 50 + 4, + .htotal = 1080 + 50 + 4 + 50, + .vdisplay = 1920, + .vsync_start = 1920 + 15, + .vsync_end = 1920 + 15 + 4, + .vtotal = 1920 + 15 + 4 + 32, + .flags = DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC, +}; + +static const struct anbernic_panel_td4310_info anbernic_vitapro_info = { + .display_mode = &anbernic_vitapro_mode, + .width_mm = 69, + .height_mm = 121, + .bus_flags = DRM_BUS_FLAG_DE_LOW | DRM_BUS_FLAG_PIXDATA_DRIVE_NEGEDGE, + .mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_BURST | + MIPI_DSI_MODE_LPM | MIPI_DSI_MODE_NO_EOT_PACKET | + MIPI_DSI_CLOCK_NON_CONTINUOUS, + .format = MIPI_DSI_FMT_RGB888, + .lanes = 4, + .prepare_delay = 50, + .reset_delay = 220, + .enable_delay = 120, + .disable_delay = 50, + .unprepare_delay = 20, +}; + +static const struct of_device_id panel_anbernic_td4310_of_match[] = { + { + .compatible = "anbernic,panel-vita-pro", + .data = &anbernic_vitapro_info, + }, + { } +}; +MODULE_DEVICE_TABLE(of, panel_anbernic_td4310_of_match); + +static struct mipi_dsi_driver anbernic_panel_td4310_driver = { + .driver = { + .name = "panel-anbernic-td4310", + .of_match_table = panel_anbernic_td4310_of_match, + }, + .probe = panel_anbernic_td4310_probe, +}; +module_mipi_dsi_driver(anbernic_panel_td4310_driver); + +MODULE_AUTHOR("Chris Morgan <macromorgan@hotmail.com>"); +MODULE_DESCRIPTION("DRM driver for Anbernic TD4310 MIPI DSI panels"); +MODULE_LICENSE("GPL"); diff --git a/drivers/gpu/drm/panel/panel-chipone-icna35xx.c b/drivers/gpu/drm/panel/panel-chipone-icna35xx.c new file mode 100644 index 000000000000..86d096455caa --- /dev/null +++ b/drivers/gpu/drm/panel/panel-chipone-icna35xx.c @@ -0,0 +1,422 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Chipone ICNA35XX Driver IC panels driver + * + * Copyright (c) 2025 Teguh Sobirin <teguh@sobir.in> + */ + +#include <linux/backlight.h> +#include <linux/delay.h> +#include <linux/gpio/consumer.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_graph.h> +#include <linux/regulator/consumer.h> + +#include <video/mipi_display.h> + +#include <drm/display/drm_dsc.h> +#include <drm/display/drm_dsc_helper.h> +#include <drm/drm_connector.h> +#include <drm/drm_crtc.h> +#include <drm/drm_mipi_dsi.h> +#include <drm/drm_modes.h> +#include <drm/drm_panel.h> +#include <drm/drm_probe_helper.h> + +struct panel_info { + struct drm_panel panel; + struct drm_connector *connector; + struct mipi_dsi_device *dsi; + struct panel_desc *desc; + enum drm_panel_orientation orientation; + + struct gpio_desc *reset_gpio; + struct regulator_bulk_data *supplies; +}; + +struct panel_desc { + unsigned int width_mm; + unsigned int height_mm; + + unsigned int bpc; + unsigned int lanes; + unsigned long mode_flags; + enum mipi_dsi_pixel_format format; + + const struct drm_display_mode *modes; + unsigned int num_modes; + int (*init_sequence)(struct panel_info *pinfo); + + struct drm_dsc_config dsc; +}; + +static const struct regulator_bulk_data panel_supplies[] = { + { .supply = "vdd" }, + { .supply = "vddio" }, + { .supply = "vci" }, + { .supply = "disp" }, + { .supply = "blvdd" }, +}; + +static inline struct panel_info *to_panel_info(struct drm_panel *panel) +{ + return container_of(panel, struct panel_info, panel); +} + +static int icna3512_init_sequence(struct panel_info *pinfo) +{ + struct mipi_dsi_multi_context dsi_ctx = { .dsi = pinfo->dsi }; + struct drm_dsc_picture_parameter_set pps; + + pinfo->dsi->mode_flags |= MIPI_DSI_MODE_LPM; + + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0x9C, 0xA5, 0xA5); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xFD, 0x5A, 0x5A); + + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0x53, 0xE0); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0x35, 0x00); + + mipi_dsi_dcs_exit_sleep_mode_multi(&dsi_ctx); + + mipi_dsi_msleep(&dsi_ctx, 120); + + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0x9F, 0x0F); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xCE, 0x22); + + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0x9F, 0x01); + + /* 165 hz */ + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0x48, 0x20); + + drm_dsc_pps_payload_pack(&pps, &pinfo->desc->dsc); + mipi_dsi_picture_parameter_set_multi(&dsi_ctx, &pps); + + mipi_dsi_msleep(&dsi_ctx, 20); + + mipi_dsi_dcs_set_display_on_multi(&dsi_ctx); + + return dsi_ctx.accum_err; +} + +static int icna3520_init_sequence(struct panel_info *pinfo) +{ + struct mipi_dsi_multi_context dsi_ctx = { .dsi = pinfo->dsi }; + struct drm_dsc_picture_parameter_set pps; + + pinfo->dsi->mode_flags |= MIPI_DSI_MODE_LPM; + + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0x9C, 0xA5, 0xA5); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xFD, 0x5A, 0x5A); + + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0x53, 0xE0); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0x35, 0x00); + + mipi_dsi_dcs_exit_sleep_mode_multi(&dsi_ctx); + + mipi_dsi_msleep(&dsi_ctx, 120); + + /* 120 hz */ + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0x48, 0x00); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0x9F, 0x00); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xB3, + 0x00, 0xD8, 0x00, 0x1C, 0x00, 0x4C); + + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0x9F, 0x01); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xB2, 0x00); + + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0x9F, 0x0D); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xB2, 0x27); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xB6, 0x03); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xBB, 0x01); + mipi_dsi_generic_write_seq_multi(&dsi_ctx, 0xB2, 0x24); + + drm_dsc_pps_payload_pack(&pps, &pinfo->desc->dsc); + mipi_dsi_picture_parameter_set_multi(&dsi_ctx, &pps); + + mipi_dsi_msleep(&dsi_ctx, 20); + + mipi_dsi_dcs_set_display_on_multi(&dsi_ctx); + + return dsi_ctx.accum_err; +} + +static const struct drm_display_mode odin2portal_modes[] = { + { + /* 165Hz */ + .clock = (1080 + 98 + 1 + 23) * (1920 + 20 + 1 + 15) * 165 / 1000, + .hdisplay = 1080, + .hsync_start = 1080 + 98, + .hsync_end = 1080 + 98 + 1, + .htotal = 1080 + 98 + 1 + 23, + .vdisplay = 1920, + .vsync_start = 1920 + 20, + .vsync_end = 1920 + 20 + 1, + .vtotal = 1920 + 20 + 1 + 15, + } +}; + +static const struct drm_display_mode thor_top_modes[] = { + { + /* 120Hz */ + .clock = (1080 + 24 + 1 + 24) * (1920 + 28 + 1 + 28) * 120 / 1000, + .hdisplay = 1080, + .hsync_start = 1080 + 24, + .hsync_end = 1080 + 24 + 1, + .htotal = 1080 + 24 + 1 + 24, + .vdisplay = 1920, + .vsync_start = 1920 + 28, + .vsync_end = 1920 + 28 + 1, + .vtotal = 1920 + 28 + 1 + 28, + } +}; + +static struct panel_desc odin2portal_desc = { + .modes = odin2portal_modes, + .num_modes = ARRAY_SIZE(odin2portal_modes), + .width_mm = 160, + .height_mm = 89, + .bpc = 8, + .lanes = 4, + .format = MIPI_DSI_FMT_RGB888, + .mode_flags = MIPI_DSI_MODE_NO_EOT_PACKET | MIPI_DSI_CLOCK_NON_CONTINUOUS | + MIPI_DSI_MODE_LPM, + .init_sequence = icna3512_init_sequence, + .dsc = { + .dsc_version_major = 0x1, + .dsc_version_minor = 0x1, + .slice_height = 20, + .slice_width = 540, + .slice_count = 2, + .bits_per_component = 8, + .bits_per_pixel = 8 << 4, + .block_pred_enable = true, + }, +}; + +static struct panel_desc thor_top_desc = { + .modes = thor_top_modes, + .num_modes = ARRAY_SIZE(thor_top_modes), + .width_mm = 136, + .height_mm = 68, + .bpc = 8, + .lanes = 4, + .format = MIPI_DSI_FMT_RGB888, + .mode_flags = MIPI_DSI_MODE_NO_EOT_PACKET | MIPI_DSI_CLOCK_NON_CONTINUOUS | + MIPI_DSI_MODE_LPM, + .init_sequence = icna3520_init_sequence, + .dsc = { + .dsc_version_major = 0x1, + .dsc_version_minor = 0x1, + .slice_height = 12, + .slice_width = 540, + .slice_count = 2, + .bits_per_component = 8, + .bits_per_pixel = 8 << 4, + .block_pred_enable = true, + }, +}; + +static void icna35xx_reset(struct panel_info *pinfo) +{ + gpiod_set_value_cansleep(pinfo->reset_gpio, 0); + usleep_range(20000, 21000); + gpiod_set_value_cansleep(pinfo->reset_gpio, 1); + usleep_range(20000, 21000); + gpiod_set_value_cansleep(pinfo->reset_gpio, 0); + usleep_range(20000, 21000); +} + +static int icna35xx_prepare(struct drm_panel *panel) +{ + struct panel_info *pinfo = to_panel_info(panel); + int ret; + + ret = regulator_bulk_enable(ARRAY_SIZE(panel_supplies), pinfo->supplies); + if (ret < 0) { + dev_err(panel->dev, "failed to enable regulators: %d\n", ret); + return ret; + } + + icna35xx_reset(pinfo); + + ret = pinfo->desc->init_sequence(pinfo); + if (ret < 0) { + regulator_bulk_disable(ARRAY_SIZE(panel_supplies), pinfo->supplies); + dev_err(panel->dev, "failed to initialize panel: %d\n", ret); + return ret; + } + + return 0; +} + +static int icna35xx_disable(struct drm_panel *panel) +{ + struct panel_info *pinfo = to_panel_info(panel); + struct mipi_dsi_multi_context dsi_ctx = { .dsi = pinfo->dsi }; + + pinfo->dsi->mode_flags &= ~MIPI_DSI_MODE_LPM; + + mipi_dsi_dcs_set_display_off_multi(&dsi_ctx); + mipi_dsi_msleep(&dsi_ctx, 50); + mipi_dsi_dcs_enter_sleep_mode_multi(&dsi_ctx); + mipi_dsi_msleep(&dsi_ctx, 120); + + return dsi_ctx.accum_err; +} + +static int icna35xx_unprepare(struct drm_panel *panel) +{ + struct panel_info *pinfo = to_panel_info(panel); + + gpiod_set_value_cansleep(pinfo->reset_gpio, 1); + regulator_bulk_disable(ARRAY_SIZE(panel_supplies), pinfo->supplies); + + return 0; +} + +static int icna35xx_get_modes(struct drm_panel *panel, + struct drm_connector *connector) +{ + struct panel_info *pinfo = to_panel_info(panel); + + return drm_connector_helper_get_modes_fixed(connector, pinfo->desc->modes); +} + +static enum drm_panel_orientation icna35xx_get_orientation(struct drm_panel *panel) +{ + struct panel_info *pinfo = to_panel_info(panel); + + return pinfo->orientation; +} + +static const struct drm_panel_funcs icna35xx_panel_funcs = { + .disable = icna35xx_disable, + .prepare = icna35xx_prepare, + .unprepare = icna35xx_unprepare, + .get_modes = icna35xx_get_modes, + .get_orientation = icna35xx_get_orientation, +}; + +static int icna35xx_bl_update_status(struct backlight_device *bl) +{ + struct mipi_dsi_device *dsi = bl_get_data(bl); + u16 brightness = backlight_get_brightness(bl); + int ret; + + dsi->mode_flags &= ~MIPI_DSI_MODE_LPM; + + ret = mipi_dsi_dcs_set_display_brightness_large(dsi, brightness); + + dsi->mode_flags |= MIPI_DSI_MODE_LPM; + + return ret; +} + +static int icna35xx_bl_get_brightness(struct backlight_device *bl) +{ + struct mipi_dsi_device *dsi = bl_get_data(bl); + u16 brightness; + int ret; + + dsi->mode_flags &= ~MIPI_DSI_MODE_LPM; + + ret = mipi_dsi_dcs_get_display_brightness_large(dsi, &brightness); + + dsi->mode_flags |= MIPI_DSI_MODE_LPM; + + return ret < 0 ? ret : brightness; +} + +static const struct backlight_ops icna35xx_bl_ops = { + .update_status = icna35xx_bl_update_status, + .get_brightness = icna35xx_bl_get_brightness, +}; + +static struct backlight_device *icna35xx_create_backlight(struct mipi_dsi_device *dsi) +{ + struct device *dev = &dsi->dev; + const struct backlight_properties props = { + .type = BACKLIGHT_RAW, + .brightness = 4096, + .max_brightness = 4096, + }; + + return devm_backlight_device_register(dev, dev_name(dev), dev, dsi, + &icna35xx_bl_ops, &props); +} + +static int icna35xx_probe(struct mipi_dsi_device *dsi) +{ + struct device *dev = &dsi->dev; + struct panel_info *pinfo; + int ret; + + pinfo = devm_drm_panel_alloc(dev, __typeof(*pinfo), panel, + &icna35xx_panel_funcs, + DRM_MODE_CONNECTOR_DSI); + if (IS_ERR(pinfo)) + return PTR_ERR(pinfo); + + ret = devm_regulator_bulk_get_const(dev, ARRAY_SIZE(panel_supplies), + panel_supplies, &pinfo->supplies); + if (ret < 0) + return dev_err_probe(dev, ret, "Failed to get regulators\n"); + + pinfo->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW); + if (IS_ERR(pinfo->reset_gpio)) + return dev_err_probe(dev, PTR_ERR(pinfo->reset_gpio), "failed to get reset gpio\n"); + + pinfo->desc = (struct panel_desc *)of_device_get_match_data(dev); + if (!pinfo->desc) + return -ENODEV; + + pinfo->dsi = dsi; + mipi_dsi_set_drvdata(dsi, pinfo); + + ret = of_drm_get_panel_orientation(dev->of_node, &pinfo->orientation); + if (ret < 0) { + dev_err(dev, "%pOF: failed to get orientation %d\n", dev->of_node, ret); + return ret; + } + + pinfo->panel.prepare_prev_first = true; + + pinfo->panel.backlight = icna35xx_create_backlight(dsi); + if (IS_ERR(pinfo->panel.backlight)) + return dev_err_probe(dev, PTR_ERR(pinfo->panel.backlight), + "Failed to create backlight\n"); + + ret = devm_drm_panel_add(dev, &pinfo->panel); + if (ret) + return ret; + + pinfo->dsi->lanes = pinfo->desc->lanes; + pinfo->dsi->format = pinfo->desc->format; + pinfo->dsi->mode_flags = pinfo->desc->mode_flags; + pinfo->dsi->dsc = &pinfo->desc->dsc; + + return devm_mipi_dsi_attach(dev, dsi); +} + +static const struct of_device_id icna35xx_of_match[] = { + { .compatible = "ayaneo,pocketds-panel-top", .data = &odin2portal_desc }, + { .compatible = "ayntec,odin2portal-panel", .data = &odin2portal_desc }, + { .compatible = "ayntec,odin3-panel", .data = &thor_top_desc }, + { .compatible = "ayntec,thor-panel-top", .data = &thor_top_desc }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, icna35xx_of_match); + +static struct mipi_dsi_driver icna35xx_driver = { + .probe = icna35xx_probe, + .driver = { + .name = "panel-chipone-icna35xx", + .of_match_table = icna35xx_of_match, + }, +}; +module_mipi_dsi_driver(icna35xx_driver); + +MODULE_AUTHOR("Teguh Sobirin <teguh@sobir.in>"); +MODULE_DESCRIPTION("DRM driver for Chipone ICNA35XX based MIPI DSI panels"); +MODULE_LICENSE("GPL"); diff --git a/drivers/gpu/drm/panel/panel-edp.c b/drivers/gpu/drm/panel/panel-edp.c index aa27d6cd932e..4aafdc7bef7e 100644 --- a/drivers/gpu/drm/panel/panel-edp.c +++ b/drivers/gpu/drm/panel/panel-edp.c @@ -1855,6 +1855,14 @@ static const struct panel_delay delay_200_500_e50_po2e200 = { .powered_on_to_enable = 200, }; +static const struct panel_delay delay_200_500_e50_po2e200_d100 = { + .hpd_absent = 200, + .unprepare = 500, + .enable = 50, + .powered_on_to_enable = 200, + .disable = 100, +}; + static const struct panel_delay delay_200_150_e50 = { .hpd_absent = 200, .unprepare = 150, @@ -1934,6 +1942,7 @@ static const struct edp_panel_entry edp_panels[] = { EDP_PANEL_ENTRY('A', 'U', 'O', 0x235c, &delay_200_500_e50, "B116XTN02.3"), EDP_PANEL_ENTRY('A', 'U', 'O', 0x239b, &delay_200_500_e50, "B116XAN06.1"), EDP_PANEL_ENTRY('A', 'U', 'O', 0x255c, &delay_200_500_e50, "B116XTN02.5"), + EDP_PANEL_ENTRY('A', 'U', 'O', 0x29c0, &delay_200_500_e50, "B116XAT04.3"), EDP_PANEL_ENTRY('A', 'U', 'O', 0x30ed, &delay_200_500_e50, "G156HAN03.0"), EDP_PANEL_ENTRY('A', 'U', 'O', 0x3c9f, &delay_200_500_e50, "B140HAK03.5"), EDP_PANEL_ENTRY('A', 'U', 'O', 0x403d, &delay_200_500_e50, "B140HAN04.0"), @@ -2004,6 +2013,7 @@ static const struct edp_panel_entry edp_panels[] = { EDP_PANEL_ENTRY('B', 'O', 'E', 0x0964, &delay_200_500_e50, "NV133WUM-N61"), EDP_PANEL_ENTRY('B', 'O', 'E', 0x096e, &delay_200_500_e50_po2e200, "NV116WHM-T07 V8.0"), EDP_PANEL_ENTRY('B', 'O', 'E', 0x0979, &delay_200_500_e50, "NV116WHM-N49 V8.0"), + EDP_PANEL_ENTRY('B', 'O', 'E', 0x0985, &delay_200_500_e50, "NE160QDM-NY1"), EDP_PANEL_ENTRY('B', 'O', 'E', 0x098d, &boe_nv110wtm_n61.delay, "NV110WTM-N61"), EDP_PANEL_ENTRY('B', 'O', 'E', 0x0993, &delay_200_500_e80, "NV116WHM-T14 V8.0"), EDP_PANEL_ENTRY('B', 'O', 'E', 0x09ad, &delay_200_500_e80, "NV116WHM-N47"), @@ -2036,8 +2046,13 @@ static const struct edp_panel_entry edp_panels[] = { EDP_PANEL_ENTRY('B', 'O', 'E', 0x0cfa, &delay_200_500_e50, "NV116WHM-A4D"), EDP_PANEL_ENTRY('B', 'O', 'E', 0x0d45, &delay_200_500_e80, "NV116WHM-N4B"), EDP_PANEL_ENTRY('B', 'O', 'E', 0x0d73, &delay_200_500_e80, "NE140WUM-N6S"), + EDP_PANEL_ENTRY('B', 'O', 'E', 0x0d98, &delay_200_500_e50_po2e200_d100, "NV140FHM-N5B"), EDP_PANEL_ENTRY('B', 'O', 'E', 0x0db3, &delay_200_500_e80, "NV153WUM-N42"), EDP_PANEL_ENTRY('B', 'O', 'E', 0x0ddf, &delay_200_500_e80, "NV116WHM-T01"), + EDP_PANEL_ENTRY('B', 'O', 'E', 0x386e, &delay_200_500_e80, "NV116WH2-M30"), + EDP_PANEL_ENTRY('B', 'O', 'E', 0x3879, &delay_200_500_e80, "NT116WHM-N21"), + EDP_PANEL_ENTRY('B', 'O', 'E', 0x388b, &delay_200_500_e80, "NV116FH1-M31"), + EDP_PANEL_ENTRY('B', 'O', 'E', 0x388c, &delay_200_500_e80, "NV116FH1-M30"), EDP_PANEL_ENTRY('C', 'M', 'N', 0x1130, &delay_200_500_e50, "N116BGE-EB2"), EDP_PANEL_ENTRY('C', 'M', 'N', 0x1132, &delay_200_500_e80_d50, "N116BGE-EA2"), @@ -2060,6 +2075,7 @@ static const struct edp_panel_entry edp_panels[] = { EDP_PANEL_ENTRY('C', 'M', 'N', 0x1161, &delay_200_500_e80, "N116BCP-EA2"), EDP_PANEL_ENTRY('C', 'M', 'N', 0x1163, &delay_200_500_e80_d50, "N116BCJ-EAK"), EDP_PANEL_ENTRY('C', 'M', 'N', 0x1169, &delay_200_500_e80_d50, "N116BCN-EA1"), + EDP_PANEL_ENTRY('C', 'M', 'N', 0x116d, &delay_200_500_e80_d50, "N116BCP-EA2"), EDP_PANEL_ENTRY('C', 'M', 'N', 0x117a, &delay_200_500_e80_d50, "N116BCL-EAK"), EDP_PANEL_ENTRY('C', 'M', 'N', 0x1247, &delay_200_500_e80_d50, "N120ACA-EA1"), EDP_PANEL_ENTRY('C', 'M', 'N', 0x124c, &delay_200_500_e80_d50, "N122JCA-ENK"), @@ -2086,6 +2102,7 @@ static const struct edp_panel_entry edp_panels[] = { EDP_PANEL_ENTRY('C', 'S', 'W', 0x1103, &delay_200_500_e80_d50, "MNB601LS1-3"), EDP_PANEL_ENTRY('C', 'S', 'W', 0x1104, &delay_200_500_e50_d100, "MNB601LS1-4"), EDP_PANEL_ENTRY('C', 'S', 'W', 0x110a, &delay_200_500_e50, "PNB601LS1-2"), + EDP_PANEL_ENTRY('C', 'S', 'W', 0x110d, &delay_200_500_e50, "MNB601LS1-8"), EDP_PANEL_ENTRY('C', 'S', 'W', 0x143f, &delay_200_500_e50, "MNE007QS3-6"), EDP_PANEL_ENTRY('C', 'S', 'W', 0x1448, &delay_200_500_e50, "MNE007QS3-7"), EDP_PANEL_ENTRY('C', 'S', 'W', 0x144b, &delay_200_500_e80, "MNE001BS1-4"), @@ -2099,6 +2116,7 @@ static const struct edp_panel_entry edp_panels[] = { EDP_PANEL_ENTRY('E', 'T', 'C', 0x0000, &delay_50_500_e200_d200_po2e335, "LP079QX1-SP0V"), + EDP_PANEL_ENTRY('H', 'K', 'C', 0x1203, &delay_200_500_e80, "MB116AS01'3"), EDP_PANEL_ENTRY('H', 'K', 'C', 0x2d51, &delay_200_500_e200, "Unknown"), EDP_PANEL_ENTRY('H', 'K', 'C', 0x2d5b, &delay_200_500_e200, "MB116AN01"), EDP_PANEL_ENTRY('H', 'K', 'C', 0x2d5c, &delay_200_500_e200, "MB116AN01-2"), @@ -2151,6 +2169,7 @@ static const struct edp_panel_entry edp_panels[] = { EDP_PANEL_ENTRY('T', 'M', 'A', 0x0811, &delay_200_500_e80_d50, "TM140VDXP01-04"), EDP_PANEL_ENTRY('T', 'M', 'A', 0x2094, &delay_200_500_e50_d100, "TL140VDMS03-01"), + EDP_PANEL_ENTRY('T', 'M', 'A', 0x2139, &delay_200_500_e50_d100, "TM156VDXP25"), { /* sentinal */ } }; diff --git a/drivers/gpu/drm/panel/panel-himax-hx83121a.c b/drivers/gpu/drm/panel/panel-himax-hx83121a.c index 8b11fce4c7c5..3b6a562c4d11 100644 --- a/drivers/gpu/drm/panel/panel-himax-hx83121a.c +++ b/drivers/gpu/drm/panel/panel-himax-hx83121a.c @@ -33,7 +33,9 @@ struct himax { struct drm_dsc_config dsc; struct gpio_desc *reset_gpio; struct regulator_bulk_data *supplies; + struct regulator *bl_supply; struct backlight_device *backlight; + bool backlight_enabled; }; struct panel_desc { @@ -192,10 +194,31 @@ static const struct drm_panel_funcs himax_panel_funcs = { static int himax_bl_update_status(struct backlight_device *bl) { - struct mipi_dsi_device *dsi = bl_get_data(bl); + struct himax *ctx = bl_get_data(bl); u16 brightness = backlight_get_brightness(bl); + int ret = 0; + + if (!brightness) { + if (ctx->backlight_enabled) + ret = regulator_disable(ctx->bl_supply); + if (ret) + return ret; + + ctx->backlight_enabled = false; + + return 0; + } + /* TODO: brightness to raw map table */ - return mipi_dsi_dcs_set_display_brightness_large(dsi, brightness); + if (!ctx->backlight_enabled) + ret = regulator_enable(ctx->bl_supply); + if (ret) + return ret; + + ctx->backlight_enabled = true; + + return mipi_dsi_dcs_set_display_brightness_large(to_primary_dsi(ctx), + brightness); } static const struct backlight_ops himax_bl_ops = { @@ -204,9 +227,9 @@ static const struct backlight_ops himax_bl_ops = { }; static struct backlight_device * -himax_create_backlight(struct mipi_dsi_device *dsi) +himax_create_backlight(struct himax *ctx) { - struct device *dev = &dsi->dev; + struct device *dev = &to_primary_dsi(ctx)->dev; const struct backlight_properties props = { .type = BACKLIGHT_RAW, .brightness = 512, @@ -214,7 +237,7 @@ himax_create_backlight(struct mipi_dsi_device *dsi) .scale = BACKLIGHT_SCALE_NON_LINEAR, }; - return devm_backlight_device_register(dev, dev_name(dev), dev, dsi, + return devm_backlight_device_register(dev, dev_name(dev), dev, ctx, &himax_bl_ops, &props); } @@ -645,7 +668,11 @@ static int himax_probe(struct mipi_dsi_device *dsi) ctx->panel.prepare_prev_first = true; if (desc->has_dcs_backlight) { - ctx->backlight = himax_create_backlight(to_primary_dsi(ctx)); + ctx->bl_supply = devm_regulator_get_optional(dev, "bl"); + if (IS_ERR(ctx->bl_supply)) + return dev_err_probe(dev, PTR_ERR(ctx->bl_supply), + "Failed to get backlight supply\n"); + ctx->backlight = himax_create_backlight(ctx); if (IS_ERR(ctx->backlight)) return dev_err_probe(dev, PTR_ERR(ctx->backlight), "Failed to create backlight\n"); diff --git a/drivers/gpu/drm/panel/panel-ilitek-ili7807s.c b/drivers/gpu/drm/panel/panel-ilitek-ili7807s.c new file mode 100644 index 000000000000..12b491b0bca4 --- /dev/null +++ b/drivers/gpu/drm/panel/panel-ilitek-ili7807s.c @@ -0,0 +1,285 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#include <linux/backlight.h> +#include <linux/delay.h> +#include <linux/gpio/consumer.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/regulator/consumer.h> + +#include <video/mipi_display.h> + +#include <drm/drm_mipi_dsi.h> +#include <drm/drm_modes.h> +#include <drm/drm_panel.h> +#include <drm/drm_probe_helper.h> + +struct panel_desc { + const struct drm_display_mode *mode; + unsigned int lanes; + enum mipi_dsi_pixel_format format; + unsigned long mode_flags; + void (*init)(struct mipi_dsi_multi_context *dsi_ctx); +}; + +struct ili7807s { + struct drm_panel panel; + struct mipi_dsi_device *dsi; + const struct panel_desc *desc; + + struct regulator_bulk_data *supplies; + struct gpio_desc *reset_gpio; +}; + +static const struct regulator_bulk_data ili7807s_supplies[] = { + { .supply = "vddi" }, + { .supply = "avdd" }, + { .supply = "avee" }, +}; + +static inline struct ili7807s *to_ili7807s(struct drm_panel *panel) +{ + return container_of(panel, struct ili7807s, panel); +} + +static void ili7807s_reset(struct ili7807s *ctx) +{ + gpiod_set_value_cansleep(ctx->reset_gpio, 1); + usleep_range(10000, 11000); + gpiod_set_value_cansleep(ctx->reset_gpio, 0); + usleep_range(10000, 11000); +} + +static void dlc0697_init_sequence(struct mipi_dsi_multi_context *dsi_ctx) +{ + mipi_dsi_dcs_soft_reset_multi(dsi_ctx); + mipi_dsi_msleep(dsi_ctx, 120); + + mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xff, 0x78, 0x07, 0x00); + mipi_dsi_dcs_set_tear_on_multi(dsi_ctx, MIPI_DSI_DCS_TEAR_MODE_VBLANK); + mipi_dsi_dcs_write_seq_multi(dsi_ctx, MIPI_DCS_SET_CABC_MIN_BRIGHTNESS, 0x09, 0x99); + mipi_dsi_dcs_write_seq_multi(dsi_ctx, MIPI_DCS_WRITE_CONTROL_DISPLAY, 0x24); + mipi_dsi_dcs_write_seq_multi(dsi_ctx, MIPI_DCS_WRITE_POWER_SAVE, 0x01); + mipi_dsi_dcs_set_display_brightness_multi(dsi_ctx, 0x1fff); + + mipi_dsi_dcs_exit_sleep_mode_multi(dsi_ctx); + mipi_dsi_msleep(dsi_ctx, 120); + + mipi_dsi_dcs_set_display_on_multi(dsi_ctx); + mipi_dsi_msleep(dsi_ctx, 20); +} + +static int ili7807s_on(struct ili7807s *ctx) +{ + struct mipi_dsi_multi_context dsi_ctx = { .dsi = ctx->dsi }; + + ctx->desc->init(&dsi_ctx); + + return dsi_ctx.accum_err; +} + +static int ili7807s_off(struct ili7807s *ctx) +{ + struct mipi_dsi_multi_context dsi_ctx = { .dsi = ctx->dsi }; + + mipi_dsi_dcs_set_display_off_multi(&dsi_ctx); + mipi_dsi_msleep(&dsi_ctx, 20); + + mipi_dsi_dcs_enter_sleep_mode_multi(&dsi_ctx); + mipi_dsi_msleep(&dsi_ctx, 120); + + return dsi_ctx.accum_err; +} + +static int ili7807s_prepare(struct drm_panel *panel) +{ + struct ili7807s *ctx = to_ili7807s(panel); + int ret; + + ret = regulator_bulk_enable(ARRAY_SIZE(ili7807s_supplies), ctx->supplies); + if (ret < 0) { + dev_err(ctx->panel.dev, "failed to enable regulators: %d\n", ret); + return ret; + } + + msleep(20); + + ili7807s_reset(ctx); + + ret = ili7807s_on(ctx); + if (ret < 0) { + dev_err(ctx->panel.dev, "failed to initialise panel: %d\n", ret); + goto err; + } + + return 0; + +err: + gpiod_set_value_cansleep(ctx->reset_gpio, 1); + + regulator_bulk_disable(ARRAY_SIZE(ili7807s_supplies), ctx->supplies); + return ret; +} + +static int ili7807s_unprepare(struct drm_panel *panel) +{ + struct ili7807s *ctx = to_ili7807s(panel); + int ret; + + ret = ili7807s_off(ctx); + if (ret < 0) + dev_err(ctx->panel.dev, "failed to disable panel: %d\n", ret); + + gpiod_set_value_cansleep(ctx->reset_gpio, 1); + + regulator_bulk_disable(ARRAY_SIZE(ili7807s_supplies), ctx->supplies); + + return 0; +} + +static int ili7807s_get_modes(struct drm_panel *panel, + struct drm_connector *connector) +{ + struct ili7807s *ctx = to_ili7807s(panel); + + return drm_connector_helper_get_modes_fixed(connector, ctx->desc->mode); +} + +static const struct drm_panel_funcs ili7807s_panel_funcs = { + .prepare = ili7807s_prepare, + .unprepare = ili7807s_unprepare, + .get_modes = ili7807s_get_modes, +}; + +static int ili7807s_bl_update_status(struct backlight_device *bl) +{ + struct mipi_dsi_device *dsi = bl_get_data(bl); + u16 brightness = backlight_get_brightness(bl); + int ret; + + dsi->mode_flags &= ~MIPI_DSI_MODE_LPM; + + ret = mipi_dsi_dcs_set_display_brightness_large(dsi, brightness); + + dsi->mode_flags |= MIPI_DSI_MODE_LPM; + + return ret; +} + +static const struct backlight_ops ili7807s_bl_ops = { + .update_status = ili7807s_bl_update_status, +}; + +static struct backlight_device *ili7807s_create_backlight(struct mipi_dsi_device *dsi) +{ + struct device *dev = &dsi->dev; + const struct backlight_properties props = { + .type = BACKLIGHT_RAW, + .brightness = 0x3fff, + .max_brightness = 0x3fff, + }; + + return devm_backlight_device_register(dev, dev_name(dev), dev, dsi, + &ili7807s_bl_ops, &props); +} + +static const struct drm_display_mode dlc0697_mode = { + .clock = (1080 + 18 + 2 + 16) * (1920 + 26 + 4 + 20) * 60 / 1000, + + .hdisplay = 1080, + .hsync_start = 1080 + 18, + .hsync_end = 1080 + 18 + 2, + .htotal = 1080 + 18 + 2 + 16, + + .vdisplay = 1920, + .vsync_start = 1920 + 26, + .vsync_end = 1920 + 26 + 4, + .vtotal = 1920 + 26 + 4 + 20, + + .width_mm = 87, + .height_mm = 154, + .type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED, +}; + +static const struct panel_desc dlc0697_desc = { + .mode = &dlc0697_mode, + .lanes = 4, + .format = MIPI_DSI_FMT_RGB888, + .mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_BURST, + .init = dlc0697_init_sequence, +}; + +static int ili7807s_probe(struct mipi_dsi_device *dsi) +{ + struct device *dev = &dsi->dev; + const struct panel_desc *desc; + struct ili7807s *ctx; + int ret; + + ctx = devm_drm_panel_alloc(dev, struct ili7807s, panel, + &ili7807s_panel_funcs, + DRM_MODE_CONNECTOR_DSI); + if (IS_ERR(ctx)) + return PTR_ERR(ctx); + + desc = of_device_get_match_data(dev); + ctx->desc = desc; + + ret = devm_regulator_bulk_get_const(dev, ARRAY_SIZE(ili7807s_supplies), + ili7807s_supplies, &ctx->supplies); + if (ret < 0) + return dev_err_probe(dev, ret, "failed to get regulators\n"); + + ctx->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH); + if (IS_ERR(ctx->reset_gpio)) + return dev_err_probe(dev, PTR_ERR(ctx->reset_gpio), + "failed to get reset gpio\n"); + + ctx->dsi = dsi; + mipi_dsi_set_drvdata(dsi, ctx); + + dsi->lanes = desc->lanes; + dsi->format = desc->format; + dsi->mode_flags = desc->mode_flags | MIPI_DSI_MODE_LPM; + + ctx->panel.prepare_prev_first = true; + + ret = drm_panel_of_backlight(&ctx->panel); + if (ret) + return ret; + + if (!ctx->panel.backlight) { + ctx->panel.backlight = ili7807s_create_backlight(dsi); + if (IS_ERR(ctx->panel.backlight)) + return dev_err_probe(dev, PTR_ERR(ctx->panel.backlight), + "failed to create backlight\n"); + } + + ret = devm_drm_panel_add(dev, &ctx->panel); + if (ret < 0) + return dev_err_probe(dev, ret, "failed to add panel\n"); + + return devm_mipi_dsi_attach(dev, dsi); +} + +static const struct of_device_id ili7807s_of_match[] = { + { .compatible = "dlc,dlc0697", .data = &dlc0697_desc }, + { } +}; +MODULE_DEVICE_TABLE(of, ili7807s_of_match); + +static struct mipi_dsi_driver ili7807s_dsi_driver = { + .probe = ili7807s_probe, + .driver = { + .name = "panel-ilitek-ili7807s", + .of_match_table = ili7807s_of_match, + }, +}; +module_mipi_dsi_driver(ili7807s_dsi_driver); + +MODULE_AUTHOR("Arpit Saini <arpit.saini@oss.qualcomm.com>"); +MODULE_DESCRIPTION("Panel driver for Ilitek ILI7807S LCD DSI panel"); +MODULE_LICENSE("GPL"); diff --git a/drivers/gpu/drm/panel/panel-ilitek-ili9488.c b/drivers/gpu/drm/panel/panel-ilitek-ili9488.c new file mode 100644 index 000000000000..7302766034fc --- /dev/null +++ b/drivers/gpu/drm/panel/panel-ilitek-ili9488.c @@ -0,0 +1,289 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include <linux/delay.h> +#include <linux/device.h> +#include <linux/err.h> +#include <linux/errno.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/of.h> + +#include <linux/gpio/consumer.h> +#include <linux/regulator/consumer.h> + +#include <drm/drm_mipi_dsi.h> +#include <drm/drm_modes.h> +#include <drm/drm_panel.h> +#include <drm/drm_probe_helper.h> + +#include <video/mipi_display.h> + +struct ili9488_desc { + const struct drm_display_mode *display_mode; + unsigned long mode_flags; + enum mipi_dsi_pixel_format format; + unsigned int lanes; + unsigned int bpc; + void (*init_sequence)(struct mipi_dsi_multi_context *ctx); +}; + +struct ili9488 { + struct drm_panel panel; + struct mipi_dsi_device *dsi; + struct gpio_desc *reset; + struct regulator_bulk_data supplies[2]; + const struct ili9488_desc *desc; + enum drm_panel_orientation orientation; +}; + +static const char * const regulator_names[] = { + "vci", + "iovcc", +}; + +static void e35gh_i_mw800cb_init(struct mipi_dsi_multi_context *ctx) +{ + /* Gamma control 1,2 */ + mipi_dsi_dcs_write_seq_multi(ctx, 0xE0, 0x00, 0x10, 0x14, 0x01, 0x0E, 0x04, 0x33, + 0x56, 0x48, 0x03, 0x0C, 0x0B, 0x2B, 0x34, 0x0F); + mipi_dsi_dcs_write_seq_multi(ctx, 0xE1, 0x00, 0x12, 0x18, 0x05, 0x12, 0x06, 0x40, + 0x34, 0x57, 0x06, 0x10, 0x0C, 0x3B, 0x3F, 0x0F); + /* Power control 1,2 */ + mipi_dsi_dcs_write_seq_multi(ctx, 0xC0, 0x0F, 0x0C); + mipi_dsi_dcs_write_seq_multi(ctx, 0xC1, 0x41); + /* VCOM Control */ + mipi_dsi_dcs_write_seq_multi(ctx, 0xC5, 0x00, 0x25, 0x80); + mipi_dsi_dcs_write_seq_multi(ctx, 0x36, 0x48); + /* Interface pixel format 18bpp */ + mipi_dsi_dcs_write_seq_multi(ctx, 0x3A, 0x66); + mipi_dsi_dcs_write_seq_multi(ctx, 0xB0, 0x00); + mipi_dsi_dcs_write_seq_multi(ctx, 0xB1, 0xA0); + mipi_dsi_dcs_write_seq_multi(ctx, 0xB4, 0x02); + mipi_dsi_dcs_write_seq_multi(ctx, 0xB6, 0x02, 0x02, 0x3B); + mipi_dsi_dcs_write_seq_multi(ctx, 0xE9, 0x00); + mipi_dsi_dcs_write_seq_multi(ctx, 0xF7, 0xA9, 0x51, 0x2C, 0x82); + mipi_dsi_dcs_write_seq_multi(ctx, 0x21); +} + +static const struct drm_display_mode e35gh_i_mw800cb_display_mode = { + .clock = 14400, + + .hdisplay = 320, + .hsync_start = 320 + 60, + .hsync_end = 320 + 60 + 20, + .htotal = 320 + 60 + 20 + 42, + + .vdisplay = 480, + .vsync_start = 480 + 20, + .vsync_end = 480 + 20 + 10, + .vtotal = 480 + 20 + 10 + 33, + + .width_mm = 48, + .height_mm = 73, + + .flags = DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC, + .type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED, +}; + +static inline struct ili9488 *panel_to_ili9488(struct drm_panel *panel) +{ + return container_of(panel, struct ili9488, panel); +} + +static int ili9488_power_on(struct ili9488 *ili) +{ + struct mipi_dsi_device *dsi = ili->dsi; + int ret; + + ret = regulator_bulk_enable(ARRAY_SIZE(ili->supplies), ili->supplies); + if (ret < 0) { + dev_err(&dsi->dev, "regulator bulk enable failed: %d\n", ret); + return ret; + } + + gpiod_set_value_cansleep(ili->reset, 0); + usleep_range(1000, 5000); + gpiod_set_value_cansleep(ili->reset, 1); + usleep_range(1000, 5000); + gpiod_set_value_cansleep(ili->reset, 0); + usleep_range(5000, 10000); + + return 0; +} + +static int ili9488_power_off(struct ili9488 *ili) +{ + struct mipi_dsi_device *dsi = ili->dsi; + int ret; + + gpiod_set_value_cansleep(ili->reset, 1); + + ret = regulator_bulk_disable(ARRAY_SIZE(ili->supplies), ili->supplies); + if (ret) + dev_err(&dsi->dev, "regulator bulk disable failed: %d\n", ret); + + return ret; +} + +static int ili9488_activate(struct ili9488 *ili) +{ + struct mipi_dsi_multi_context ctx = { .dsi = ili->dsi }; + + if (ili->desc->init_sequence) + ili->desc->init_sequence(&ctx); + + mipi_dsi_dcs_exit_sleep_mode_multi(&ctx); + mipi_dsi_msleep(&ctx, 120); + mipi_dsi_dcs_set_display_on_multi(&ctx); + + return ctx.accum_err; +} + +static int ili9488_prepare(struct drm_panel *panel) +{ + struct ili9488 *ili = panel_to_ili9488(panel); + int ret; + + ret = ili9488_power_on(ili); + if (ret) + return ret; + + ret = ili9488_activate(ili); + if (ret) { + ili9488_power_off(ili); + return ret; + } + + return 0; +} + +static int ili9488_deactivate(struct ili9488 *ili) +{ + struct mipi_dsi_multi_context ctx = { .dsi = ili->dsi }; + + mipi_dsi_dcs_set_display_off_multi(&ctx); + mipi_dsi_dcs_enter_sleep_mode_multi(&ctx); + mipi_dsi_msleep(&ctx, 120); + + return ctx.accum_err; +} + +static int ili9488_unprepare(struct drm_panel *panel) +{ + struct ili9488 *ili = panel_to_ili9488(panel); + struct mipi_dsi_device *dsi = ili->dsi; + int ret; + + ili9488_deactivate(ili); + ret = ili9488_power_off(ili); + if (ret < 0) + dev_err(&dsi->dev, "power off failed: %d\n", ret); + + return ret; +} + +static int ili9488_get_modes(struct drm_panel *panel, struct drm_connector *connector) +{ + struct ili9488 *ili = panel_to_ili9488(panel); + const struct drm_display_mode *mode = ili->desc->display_mode; + + connector->display_info.bpc = ili->desc->bpc; + + return drm_connector_helper_get_modes_fixed(connector, mode); +} + +static enum drm_panel_orientation ili9488_get_orientation(struct drm_panel *panel) +{ + struct ili9488 *ili = panel_to_ili9488(panel); + + return ili->orientation; +} + +static const struct drm_panel_funcs ili9488_funcs = { + .prepare = ili9488_prepare, + .unprepare = ili9488_unprepare, + .get_modes = ili9488_get_modes, + .get_orientation = ili9488_get_orientation, +}; + +static int ili9488_dsi_probe(struct mipi_dsi_device *dsi) +{ + struct device *dev = &dsi->dev; + struct ili9488 *ili; + int i, ret; + + ili = devm_drm_panel_alloc(dev, struct ili9488, panel, &ili9488_funcs, + DRM_MODE_CONNECTOR_DSI); + if (IS_ERR(ili)) + return PTR_ERR(ili); + + ili->desc = device_get_match_data(dev); + ili->dsi = dsi; + + dsi->mode_flags = ili->desc->mode_flags; + dsi->format = ili->desc->format; + dsi->lanes = ili->desc->lanes; + + ili->reset = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW); + if (IS_ERR(ili->reset)) + return dev_err_probe(dev, PTR_ERR(ili->reset), + "failed to get reset-gpios\n"); + + for (i = 0; i < ARRAY_SIZE(ili->supplies); i++) + ili->supplies[i].supply = regulator_names[i]; + + ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(ili->supplies), + ili->supplies); + if (ret < 0) + return dev_err_probe(dev, ret, "failed to get regulators\n"); + + ret = of_drm_get_panel_orientation(dev->of_node, &ili->orientation); + if (ret) + return dev_err_probe(dev, ret, "failed to get orientation\n"); + + ret = drm_panel_of_backlight(&ili->panel); + if (ret) + return dev_err_probe(dev, ret, "failed to get backlight\n"); + + ili->panel.prepare_prev_first = true; + + ret = devm_drm_panel_add(dev, &ili->panel); + if (ret) + return ret; + + ret = devm_mipi_dsi_attach(dev, dsi); + if (ret < 0) + return dev_err_probe(dev, ret, "failed to attach to DSI host\n"); + + return 0; +} + +static const struct ili9488_desc e35gh_i_mw800cb_desc = { + .init_sequence = e35gh_i_mw800cb_init, + .display_mode = &e35gh_i_mw800cb_display_mode, + .mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_SYNC_PULSE | + MIPI_DSI_MODE_LPM | MIPI_DSI_CLOCK_NON_CONTINUOUS, + .format = MIPI_DSI_FMT_RGB666_PACKED, + .lanes = 1, + .bpc = 6, +}; + +static const struct of_device_id ili9488_of_match[] = { + { .compatible = "focuslcds,e35gh-i-mw800cb", .data = &e35gh_i_mw800cb_desc }, + { } +}; + +MODULE_DEVICE_TABLE(of, ili9488_of_match); + +static struct mipi_dsi_driver ili9488_dsi_driver = { + .probe = ili9488_dsi_probe, + .driver = { + .name = "ili9488-dsi", + .of_match_table = ili9488_of_match, + }, +}; +module_mipi_dsi_driver(ili9488_dsi_driver); + +MODULE_AUTHOR("Igor Reznichenko <igor@reznichenko.net>"); +MODULE_DESCRIPTION("Ilitek ILI9488 Controller Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/gpu/drm/panel/panel-novatek-nt36672a.c b/drivers/gpu/drm/panel/panel-novatek-nt36672a.c index 7e8b5e059575..464d9ce47d87 100644 --- a/drivers/gpu/drm/panel/panel-novatek-nt36672a.c +++ b/drivers/gpu/drm/panel/panel-novatek-nt36672a.c @@ -28,10 +28,6 @@ #include <video/mipi_display.h> -struct nt36672a_panel_cmd { - const char data[2]; -}; - static const char * const nt36672a_regulator_names[] = { "vddio", "vddpos", @@ -55,13 +51,9 @@ struct nt36672a_panel_desc { enum mipi_dsi_pixel_format format; unsigned int lanes; - unsigned int num_on_cmds_1; - const struct nt36672a_panel_cmd *on_cmds_1; - unsigned int num_on_cmds_2; - const struct nt36672a_panel_cmd *on_cmds_2; - - unsigned int num_off_cmds; - const struct nt36672a_panel_cmd *off_cmds; + void (*send_init_cmds_1)(struct mipi_dsi_multi_context *dsi_ctx); + void (*send_init_cmds_2)(struct mipi_dsi_multi_context *dsi_ctx); + void (*send_deinit_cmds)(struct mipi_dsi_multi_context *dsi_ctx); }; struct nt36672a_panel { @@ -79,19 +71,6 @@ static inline struct nt36672a_panel *to_nt36672a_panel(struct drm_panel *panel) return container_of(panel, struct nt36672a_panel, base); } -static void nt36672a_send_cmds(struct mipi_dsi_multi_context *dsi_ctx, - const struct nt36672a_panel_cmd *cmds, int num) -{ - unsigned int i; - - for (i = 0; i < num; i++) { - const struct nt36672a_panel_cmd *cmd = &cmds[i]; - - /* cmd->data[0] is the DCS command, cmd->data[1] is the parameter */ - mipi_dsi_dcs_write_buffer_multi(dsi_ctx, cmd->data, sizeof(cmd->data)); - } -} - static void nt36672a_panel_power_off(struct drm_panel *panel) { struct nt36672a_panel *pinfo = to_nt36672a_panel(panel); @@ -110,8 +89,8 @@ static int nt36672a_panel_unprepare(struct drm_panel *panel) struct mipi_dsi_multi_context dsi_ctx = { .dsi = pinfo->link }; /* send off cmds */ - nt36672a_send_cmds(&dsi_ctx, pinfo->desc->off_cmds, - pinfo->desc->num_off_cmds); + if (pinfo->desc->send_deinit_cmds) + pinfo->desc->send_deinit_cmds(&dsi_ctx); /* Reset error to continue with display off even if send_cmds failed */ dsi_ctx.accum_err = 0; @@ -162,8 +141,8 @@ static int nt36672a_panel_prepare(struct drm_panel *panel) dsi_ctx.accum_err = nt36672a_panel_power_on(pinfo); /* send first part of init cmds */ - nt36672a_send_cmds(&dsi_ctx, pinfo->desc->on_cmds_1, - pinfo->desc->num_on_cmds_1); + if (pinfo->desc->send_init_cmds_1) + pinfo->desc->send_init_cmds_1(&dsi_ctx); mipi_dsi_dcs_exit_sleep_mode_multi(&dsi_ctx); @@ -173,8 +152,8 @@ static int nt36672a_panel_prepare(struct drm_panel *panel) mipi_dsi_dcs_set_display_on_multi(&dsi_ctx); /* Send rest of the init cmds */ - nt36672a_send_cmds(&dsi_ctx, pinfo->desc->on_cmds_2, - pinfo->desc->num_on_cmds_2); + if (pinfo->desc->send_init_cmds_2) + pinfo->desc->send_init_cmds_2(&dsi_ctx); mipi_dsi_msleep(&dsi_ctx, 120); @@ -213,310 +192,184 @@ static const struct drm_panel_funcs panel_funcs = { .get_modes = nt36672a_panel_get_modes, }; -static const struct nt36672a_panel_cmd tianma_fhd_video_on_cmds_1[] = { +static void tianma_fhd_video_send_init_cmds_1(struct mipi_dsi_multi_context *dsi_ctx) +{ + u8 reg; + /* skin enhancement mode */ - { .data = {0xFF, 0x22} }, - { .data = {0x00, 0x40} }, - { .data = {0x01, 0xC0} }, - { .data = {0x02, 0x40} }, - { .data = {0x03, 0x40} }, - { .data = {0x04, 0x40} }, - { .data = {0x05, 0x40} }, - { .data = {0x06, 0x40} }, - { .data = {0x07, 0x40} }, - { .data = {0x08, 0x40} }, - { .data = {0x09, 0x40} }, - { .data = {0x0A, 0x40} }, - { .data = {0x0B, 0x40} }, - { .data = {0x0C, 0x40} }, - { .data = {0x0D, 0x40} }, - { .data = {0x0E, 0x40} }, - { .data = {0x0F, 0x40} }, - { .data = {0x10, 0x40} }, - { .data = {0x11, 0x50} }, - { .data = {0x12, 0x60} }, - { .data = {0x13, 0x70} }, - { .data = {0x14, 0x58} }, - { .data = {0x15, 0x68} }, - { .data = {0x16, 0x78} }, - { .data = {0x17, 0x77} }, - { .data = {0x18, 0x39} }, - { .data = {0x19, 0x2D} }, - { .data = {0x1A, 0x2E} }, - { .data = {0x1B, 0x32} }, - { .data = {0x1C, 0x37} }, - { .data = {0x1D, 0x3A} }, - { .data = {0x1E, 0x40} }, - { .data = {0x1F, 0x40} }, - { .data = {0x20, 0x40} }, - { .data = {0x21, 0x40} }, - { .data = {0x22, 0x40} }, - { .data = {0x23, 0x40} }, - { .data = {0x24, 0x40} }, - { .data = {0x25, 0x40} }, - { .data = {0x26, 0x40} }, - { .data = {0x27, 0x40} }, - { .data = {0x28, 0x40} }, - { .data = {0x2D, 0x00} }, - { .data = {0x2F, 0x40} }, - { .data = {0x30, 0x40} }, - { .data = {0x31, 0x40} }, - { .data = {0x32, 0x40} }, - { .data = {0x33, 0x40} }, - { .data = {0x34, 0x40} }, - { .data = {0x35, 0x40} }, - { .data = {0x36, 0x40} }, - { .data = {0x37, 0x40} }, - { .data = {0x38, 0x40} }, - { .data = {0x39, 0x40} }, - { .data = {0x3A, 0x40} }, - { .data = {0x3B, 0x40} }, - { .data = {0x3D, 0x40} }, - { .data = {0x3F, 0x40} }, - { .data = {0x40, 0x40} }, - { .data = {0x41, 0x40} }, - { .data = {0x42, 0x40} }, - { .data = {0x43, 0x40} }, - { .data = {0x44, 0x40} }, - { .data = {0x45, 0x40} }, - { .data = {0x46, 0x40} }, - { .data = {0x47, 0x40} }, - { .data = {0x48, 0x40} }, - { .data = {0x49, 0x40} }, - { .data = {0x4A, 0x40} }, - { .data = {0x4B, 0x40} }, - { .data = {0x4C, 0x40} }, - { .data = {0x4D, 0x40} }, - { .data = {0x4E, 0x40} }, - { .data = {0x4F, 0x40} }, - { .data = {0x50, 0x40} }, - { .data = {0x51, 0x40} }, - { .data = {0x52, 0x40} }, - { .data = {0x53, 0x01} }, - { .data = {0x54, 0x01} }, - { .data = {0x55, 0xFE} }, - { .data = {0x56, 0x77} }, - { .data = {0x58, 0xCD} }, - { .data = {0x59, 0xD0} }, - { .data = {0x5A, 0xD0} }, - { .data = {0x5B, 0x50} }, - { .data = {0x5C, 0x50} }, - { .data = {0x5D, 0x50} }, - { .data = {0x5E, 0x50} }, - { .data = {0x5F, 0x50} }, - { .data = {0x60, 0x50} }, - { .data = {0x61, 0x50} }, - { .data = {0x62, 0x50} }, - { .data = {0x63, 0x50} }, - { .data = {0x64, 0x50} }, - { .data = {0x65, 0x50} }, - { .data = {0x66, 0x50} }, - { .data = {0x67, 0x50} }, - { .data = {0x68, 0x50} }, - { .data = {0x69, 0x50} }, - { .data = {0x6A, 0x50} }, - { .data = {0x6B, 0x50} }, - { .data = {0x6C, 0x50} }, - { .data = {0x6D, 0x50} }, - { .data = {0x6E, 0x50} }, - { .data = {0x6F, 0x50} }, - { .data = {0x70, 0x07} }, - { .data = {0x71, 0x00} }, - { .data = {0x72, 0x00} }, - { .data = {0x73, 0x00} }, - { .data = {0x74, 0x06} }, - { .data = {0x75, 0x0C} }, - { .data = {0x76, 0x03} }, - { .data = {0x77, 0x09} }, - { .data = {0x78, 0x0F} }, - { .data = {0x79, 0x68} }, - { .data = {0x7A, 0x88} }, - { .data = {0x7C, 0x80} }, - { .data = {0x7D, 0x80} }, - { .data = {0x7E, 0x80} }, - { .data = {0x7F, 0x00} }, - { .data = {0x80, 0x00} }, - { .data = {0x81, 0x00} }, - { .data = {0x83, 0x01} }, - { .data = {0x84, 0x00} }, - { .data = {0x85, 0x80} }, - { .data = {0x86, 0x80} }, - { .data = {0x87, 0x80} }, - { .data = {0x88, 0x40} }, - { .data = {0x89, 0x91} }, - { .data = {0x8A, 0x98} }, - { .data = {0x8B, 0x80} }, - { .data = {0x8C, 0x80} }, - { .data = {0x8D, 0x80} }, - { .data = {0x8E, 0x80} }, - { .data = {0x8F, 0x80} }, - { .data = {0x90, 0x80} }, - { .data = {0x91, 0x80} }, - { .data = {0x92, 0x80} }, - { .data = {0x93, 0x80} }, - { .data = {0x94, 0x80} }, - { .data = {0x95, 0x80} }, - { .data = {0x96, 0x80} }, - { .data = {0x97, 0x80} }, - { .data = {0x98, 0x80} }, - { .data = {0x99, 0x80} }, - { .data = {0x9A, 0x80} }, - { .data = {0x9B, 0x80} }, - { .data = {0x9C, 0x80} }, - { .data = {0x9D, 0x80} }, - { .data = {0x9E, 0x80} }, - { .data = {0x9F, 0x80} }, - { .data = {0xA0, 0x8A} }, - { .data = {0xA2, 0x80} }, - { .data = {0xA6, 0x80} }, - { .data = {0xA7, 0x80} }, - { .data = {0xA9, 0x80} }, - { .data = {0xAA, 0x80} }, - { .data = {0xAB, 0x80} }, - { .data = {0xAC, 0x80} }, - { .data = {0xAD, 0x80} }, - { .data = {0xAE, 0x80} }, - { .data = {0xAF, 0x80} }, - { .data = {0xB7, 0x76} }, - { .data = {0xB8, 0x76} }, - { .data = {0xB9, 0x05} }, - { .data = {0xBA, 0x0D} }, - { .data = {0xBB, 0x14} }, - { .data = {0xBC, 0x0F} }, - { .data = {0xBD, 0x18} }, - { .data = {0xBE, 0x1F} }, - { .data = {0xBF, 0x05} }, - { .data = {0xC0, 0x0D} }, - { .data = {0xC1, 0x14} }, - { .data = {0xC2, 0x03} }, - { .data = {0xC3, 0x07} }, - { .data = {0xC4, 0x0A} }, - { .data = {0xC5, 0xA0} }, - { .data = {0xC6, 0x55} }, - { .data = {0xC7, 0xFF} }, - { .data = {0xC8, 0x39} }, - { .data = {0xC9, 0x44} }, - { .data = {0xCA, 0x12} }, - { .data = {0xCD, 0x80} }, - { .data = {0xDB, 0x80} }, - { .data = {0xDC, 0x80} }, - { .data = {0xDD, 0x80} }, - { .data = {0xE0, 0x80} }, - { .data = {0xE1, 0x80} }, - { .data = {0xE2, 0x80} }, - { .data = {0xE3, 0x80} }, - { .data = {0xE4, 0x80} }, - { .data = {0xE5, 0x40} }, - { .data = {0xE6, 0x40} }, - { .data = {0xE7, 0x40} }, - { .data = {0xE8, 0x40} }, - { .data = {0xE9, 0x40} }, - { .data = {0xEA, 0x40} }, - { .data = {0xEB, 0x40} }, - { .data = {0xEC, 0x40} }, - { .data = {0xED, 0x40} }, - { .data = {0xEE, 0x40} }, - { .data = {0xEF, 0x40} }, - { .data = {0xF0, 0x40} }, - { .data = {0xF1, 0x40} }, - { .data = {0xF2, 0x40} }, - { .data = {0xF3, 0x40} }, - { .data = {0xF4, 0x40} }, - { .data = {0xF5, 0x40} }, - { .data = {0xF6, 0x40} }, - { .data = {0xFB, 0x1} }, - { .data = {0xFF, 0x23} }, - { .data = {0xFB, 0x01} }, + mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xff, 0x22); + mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0x00, 0x40); + mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0x01, 0xc0); + for (reg = 0x02; reg <= 0x10; reg++) + mipi_dsi_dcs_write_var_seq_multi(dsi_ctx, reg, 0x40); + mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0x11, 0x50); + mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0x12, 0x60); + mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0x13, 0x70); + mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0x14, 0x58); + mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0x15, 0x68); + mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0x16, 0x78); + mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0x17, 0x77); + mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0x18, 0x39); + mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0x19, 0x2d); + mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0x1a, 0x2e); + mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0x1b, 0x32); + mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0x1c, 0x37); + mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0x1d, 0x3a); + for (reg = 0x1e; reg <= 0x28; reg++) + mipi_dsi_dcs_write_var_seq_multi(dsi_ctx, reg, 0x40); + mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0x2d, 0x00); + for (reg = 0x2f; reg <= 0x3b; reg++) + mipi_dsi_dcs_write_var_seq_multi(dsi_ctx, reg, 0x40); + mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0x3d, 0x40); + for (reg = 0x3f; reg <= 0x52; reg++) + mipi_dsi_dcs_write_var_seq_multi(dsi_ctx, reg, 0x40); + mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0x53, 0x01); + mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0x54, 0x01); + mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0x55, 0xfe); + mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0x56, 0x77); + mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0x58, 0xcd); + mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0x59, 0xd0); + mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0x5a, 0xd0); + for (reg = 0x5b; reg <= 0x6f; reg++) + mipi_dsi_dcs_write_var_seq_multi(dsi_ctx, reg, 0x50); + mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0x70, 0x07); + mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0x71, 0x00); + mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0x72, 0x00); + mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0x73, 0x00); + mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0x74, 0x06); + mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0x75, 0x0c); + mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0x76, 0x03); + mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0x77, 0x09); + mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0x78, 0x0f); + mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0x79, 0x68); + mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0x7a, 0x88); + mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0x7c, 0x80); + mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0x7d, 0x80); + mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0x7e, 0x80); + mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0x7f, 0x00); + mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0x80, 0x00); + mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0x81, 0x00); + mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0x83, 0x01); + mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0x84, 0x00); + mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0x85, 0x80); + mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0x86, 0x80); + mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0x87, 0x80); + mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0x88, 0x40); + mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0x89, 0x91); + mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0x8a, 0x98); + for (reg = 0x8b; reg <= 0x9f; reg++) + mipi_dsi_dcs_write_var_seq_multi(dsi_ctx, reg, 0x80); + mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xa0, 0x8a); + mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xa2, 0x80); + mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xa6, 0x80); + mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xa7, 0x80); + for (reg = 0xa9; reg <= 0xaf; reg++) + mipi_dsi_dcs_write_var_seq_multi(dsi_ctx, reg, 0x80); + mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xb7, 0x76); + mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xb8, 0x76); + mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xb9, 0x05); + mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xba, 0x0d); + mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xbb, 0x14); + mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xbc, 0x0f); + mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xbd, 0x18); + mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xbe, 0x1f); + mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xbf, 0x05); + mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xc0, 0x0d); + mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xc1, 0x14); + mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xc2, 0x03); + mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xc3, 0x07); + mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xc4, 0x0a); + mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xc5, 0xa0); + mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xc6, 0x55); + mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xc7, 0xff); + mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xc8, 0x39); + mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xc9, 0x44); + mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xca, 0x12); + mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xcd, 0x80); + mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xdb, 0x80); + mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xdc, 0x80); + mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xdd, 0x80); + for (reg = 0xe0; reg <= 0xe4; reg++) + mipi_dsi_dcs_write_var_seq_multi(dsi_ctx, reg, 0x80); + for (reg = 0xe5; reg <= 0xf6; reg++) + mipi_dsi_dcs_write_var_seq_multi(dsi_ctx, reg, 0x40); + mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xfb, 0x01); + mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xff, 0x23); + mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xfb, 0x01); /* dimming enable */ - { .data = {0x01, 0x84} }, - { .data = {0x05, 0x2D} }, - { .data = {0x06, 0x00} }, - /* resolution 1080*2246 */ - { .data = {0x11, 0x01} }, - { .data = {0x12, 0x7B} }, - { .data = {0x15, 0x6F} }, - { .data = {0x16, 0x0B} }, - /* UI mode */ - { .data = {0x29, 0x0A} }, - { .data = {0x30, 0xFF} }, - { .data = {0x31, 0xFF} }, - { .data = {0x32, 0xFF} }, - { .data = {0x33, 0xFF} }, - { .data = {0x34, 0xFF} }, - { .data = {0x35, 0xFF} }, - { .data = {0x36, 0xFF} }, - { .data = {0x37, 0xFF} }, - { .data = {0x38, 0xFC} }, - { .data = {0x39, 0xF8} }, - { .data = {0x3A, 0xF4} }, - { .data = {0x3B, 0xF1} }, - { .data = {0x3D, 0xEE} }, - { .data = {0x3F, 0xEB} }, - { .data = {0x40, 0xE8} }, - { .data = {0x41, 0xE5} }, - /* STILL mode */ - { .data = {0x2A, 0x13} }, - { .data = {0x45, 0xFF} }, - { .data = {0x46, 0xFF} }, - { .data = {0x47, 0xFF} }, - { .data = {0x48, 0xFF} }, - { .data = {0x49, 0xFF} }, - { .data = {0x4A, 0xFF} }, - { .data = {0x4B, 0xFF} }, - { .data = {0x4C, 0xFF} }, - { .data = {0x4D, 0xED} }, - { .data = {0x4E, 0xD5} }, - { .data = {0x4F, 0xBF} }, - { .data = {0x50, 0xA6} }, - { .data = {0x51, 0x96} }, - { .data = {0x52, 0x86} }, - { .data = {0x53, 0x76} }, - { .data = {0x54, 0x66} }, - /* MOVING mode */ - { .data = {0x2B, 0x0E} }, - { .data = {0x58, 0xFF} }, - { .data = {0x59, 0xFF} }, - { .data = {0x5A, 0xFF} }, - { .data = {0x5B, 0xFF} }, - { .data = {0x5C, 0xFF} }, - { .data = {0x5D, 0xFF} }, - { .data = {0x5E, 0xFF} }, - { .data = {0x5F, 0xFF} }, - { .data = {0x60, 0xF6} }, - { .data = {0x61, 0xEA} }, - { .data = {0x62, 0xE1} }, - { .data = {0x63, 0xD8} }, - { .data = {0x64, 0xCE} }, - { .data = {0x65, 0xC3} }, - { .data = {0x66, 0xBA} }, - { .data = {0x67, 0xB3} }, - { .data = {0xFF, 0x25} }, - { .data = {0xFB, 0x01} }, - { .data = {0x05, 0x04} }, - { .data = {0xFF, 0x26} }, - { .data = {0xFB, 0x01} }, - { .data = {0x1C, 0xAF} }, - { .data = {0xFF, 0x10} }, - { .data = {0xFB, 0x01} }, - { .data = {0x51, 0xFF} }, - { .data = {0x53, 0x24} }, - { .data = {0x55, 0x00} }, -}; + mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0x01, 0x84); + mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0x05, 0x2d); + mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0x06, 0x00); + /* resolution 1080*2246 */ + mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0x11, 0x01); + mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0x12, 0x7b); + mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0x15, 0x6f); + mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0x16, 0x0b); + /* UI mode */ + mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0x29, 0x0a); + for (reg = 0x30; reg <= 0x37; reg++) + mipi_dsi_dcs_write_var_seq_multi(dsi_ctx, reg, 0xff); + mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0x38, 0xfc); + mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0x39, 0xf8); + mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0x3a, 0xf4); + mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0x3b, 0xf1); + mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0x3d, 0xee); + mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0x3f, 0xeb); + mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0x40, 0xe8); + mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0x41, 0xe5); + /* STILL mode */ + mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0x2a, 0x13); + for (reg = 0x45; reg <= 0x4c; reg++) + mipi_dsi_dcs_write_var_seq_multi(dsi_ctx, reg, 0xff); + mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0x4d, 0xed); + mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0x4e, 0xd5); + mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0x4f, 0xbf); + mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0x50, 0xa6); + mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0x51, 0x96); + mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0x52, 0x86); + mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0x53, 0x76); + mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0x54, 0x66); + /* MOVING mode */ + mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0x2b, 0x0e); + for (reg = 0x58; reg <= 0x5f; reg++) + mipi_dsi_dcs_write_var_seq_multi(dsi_ctx, reg, 0xff); + mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0x60, 0xf6); + mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0x61, 0xea); + mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0x62, 0xe1); + mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0x63, 0xd8); + mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0x64, 0xce); + mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0x65, 0xc3); + mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0x66, 0xba); + mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0x67, 0xb3); + mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xff, 0x25); + mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xfb, 0x01); + mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0x05, 0x04); + mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xff, 0x26); + mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xfb, 0x01); + mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0x1c, 0xaf); + mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xff, 0x10); + mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xfb, 0x01); + mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0x51, 0xff); + mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0x53, 0x24); + mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0x55, 0x00); +} -static const struct nt36672a_panel_cmd tianma_fhd_video_on_cmds_2[] = { - { .data = {0xFF, 0x24} }, - { .data = {0xFB, 0x01} }, - { .data = {0xC3, 0x01} }, - { .data = {0xC4, 0x54} }, - { .data = {0xFF, 0x10} }, -}; +static void tianma_fhd_video_send_init_cmds_2(struct mipi_dsi_multi_context *dsi_ctx) +{ + mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xff, 0x24); + mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xfb, 0x01); + mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xc3, 0x01); + mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xc4, 0x54); + mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xff, 0x10); +} -static const struct nt36672a_panel_cmd tianma_fhd_video_off_cmds[] = { - { .data = {0xFF, 0x24} }, - { .data = {0xFB, 0x01} }, - { .data = {0xC3, 0x01} }, - { .data = {0xFF, 0x10} }, -}; +static void tianma_fhd_video_send_deinit_cmds(struct mipi_dsi_multi_context *dsi_ctx) +{ + mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xff, 0x24); + mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xfb, 0x01); + mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xc3, 0x01); + mipi_dsi_dcs_write_seq_multi(dsi_ctx, 0xff, 0x10); +} static const struct drm_display_mode tianma_fhd_video_panel_default_mode = { .clock = 161331, @@ -546,12 +399,9 @@ static const struct nt36672a_panel_desc tianma_fhd_video_panel_desc = { | MIPI_DSI_MODE_VIDEO_BURST, .format = MIPI_DSI_FMT_RGB888, .lanes = 4, - .on_cmds_1 = tianma_fhd_video_on_cmds_1, - .num_on_cmds_1 = ARRAY_SIZE(tianma_fhd_video_on_cmds_1), - .on_cmds_2 = tianma_fhd_video_on_cmds_2, - .num_on_cmds_2 = ARRAY_SIZE(tianma_fhd_video_on_cmds_2), - .off_cmds = tianma_fhd_video_off_cmds, - .num_off_cmds = ARRAY_SIZE(tianma_fhd_video_off_cmds), + .send_init_cmds_1 = tianma_fhd_video_send_init_cmds_1, + .send_init_cmds_2 = tianma_fhd_video_send_init_cmds_2, + .send_deinit_cmds = tianma_fhd_video_send_deinit_cmds, }; static int nt36672a_panel_add(struct nt36672a_panel *pinfo) diff --git a/drivers/gpu/drm/panel/panel-renesas-r63419.c b/drivers/gpu/drm/panel/panel-renesas-r63419.c new file mode 100644 index 000000000000..59ac323f5276 --- /dev/null +++ b/drivers/gpu/drm/panel/panel-renesas-r63419.c @@ -0,0 +1,350 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * DRM driver for Renesas R63419 based dual-DSI video mode panels + * + * Copyright (c) 2025, Kancy Joe <kancy2333@outlook.com> + * Copyright (C) 2026 Linaro Limited + * Author: Neil Armstrong <neil.armstrong@linaro.org> + */ + +#include <linux/backlight.h> +#include <linux/delay.h> +#include <linux/gpio/consumer.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_graph.h> +#include <linux/regulator/consumer.h> + +#include <video/mipi_display.h> + +#include <drm/drm_connector.h> +#include <drm/drm_mipi_dsi.h> +#include <drm/drm_modes.h> +#include <drm/drm_panel.h> +#include <drm/drm_probe_helper.h> + +struct renesas_r63419_panel { + struct drm_panel panel; + struct mipi_dsi_device *dsi[2]; + const struct panel_desc *desc; + + struct gpio_desc *reset_gpio; + struct regulator_bulk_data *vdd_supplies; + struct regulator_bulk_data *vcc_supplies; + enum drm_panel_orientation orientation; +}; + +/* VDDIO/VDD Supplies */ +static const struct regulator_bulk_data renesas_r63419_vdd_supplies[] = { + { .supply = "vddio" }, + { .supply = "vdd" }, +}; + +/* VSP/VSN/VCI Supplies */ +static const struct regulator_bulk_data renesas_r63419_vcc_supplies[] = { + { .supply = "vsp" }, + { .supply = "vsn" }, + { .supply = "vci" }, +}; + +struct panel_desc { + const struct drm_display_mode *mode; + unsigned int lanes; + unsigned long mode_flags; + enum mipi_dsi_pixel_format format; + const struct mipi_dsi_device_info dsi_info; +}; + +static const struct drm_display_mode wt0600_mode = { + /* Dual dsi */ + .clock = 2 * (720 + 100 + 8 + 40) * (2560 + 15 + 2 + 8) * 60 / 1000, + .hdisplay = 2 * 720, + .hsync_start = 2 * (720 + 100), + .hsync_end = 2 * (720 + 100 + 8), + .htotal = 2 * (720 + 100 + 8 + 40), + .vdisplay = 2560, + .vsync_start = 2560 + 15, + .vsync_end = 2560 + 15 + 2, + .vtotal = 2560 + 15 + 2 + 8, + .type = DRM_MODE_TYPE_DRIVER, + .width_mm = 74, + .height_mm = 131, +}; + +static const struct drm_display_mode wt0630_mode = { + /* Dual dsi */ + .clock = 2 * (720 + 100 + 8 + 40) * (2560 + 15 + 2 + 8) * 60 / 1000, + .hdisplay = 2 * 720, + .hsync_start = 2 * (720 + 100), + .hsync_end = 2 * (720 + 100 + 8), + .htotal = 2 * (720 + 100 + 8 + 40), + .vdisplay = 2560, + .vsync_start = 2560 + 15, + .vsync_end = 2560 + 15 + 2, + .vtotal = 2560 + 15 + 2 + 8, + .type = DRM_MODE_TYPE_DRIVER, + .width_mm = 78, + .height_mm = 140, +}; + +static struct panel_desc wt0600_desc = { + .lanes = 4, + .mode = &wt0600_mode, + .mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_BURST | + MIPI_DSI_CLOCK_NON_CONTINUOUS | MIPI_DSI_MODE_LPM, + .format = MIPI_DSI_FMT_RGB888, +}; + +static struct panel_desc wt0630_desc = { + .lanes = 4, + .mode = &wt0630_mode, /* wt0600 only has different screen size */ + .mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_BURST | + MIPI_DSI_CLOCK_NON_CONTINUOUS | MIPI_DSI_MODE_LPM, + .format = MIPI_DSI_FMT_RGB888, +}; + +static inline struct renesas_r63419_panel * +to_renesas_r63419_panel(struct drm_panel *panel) +{ + return container_of(panel, struct renesas_r63419_panel, panel); +} + +static int renesas_r63419_on(struct renesas_r63419_panel *ctx) +{ + struct mipi_dsi_multi_context dsi_ctx = { 0 }; + + /* + * Panel registers are loaded from DDIC Non Volatile Memory + * + * The DDIC expects this sequence to get out of sleep and enable display + */ + + mipi_dsi_dual(mipi_dsi_dcs_set_display_on_multi, + &dsi_ctx, ctx->dsi[0], ctx->dsi[1]); + mipi_dsi_msleep(&dsi_ctx, 50); + + mipi_dsi_dual(mipi_dsi_dcs_exit_sleep_mode_multi, + &dsi_ctx, ctx->dsi[0], ctx->dsi[1]); + mipi_dsi_msleep(&dsi_ctx, 150); + + return dsi_ctx.accum_err; +} + +static int renesas_r63419_disable(struct drm_panel *panel) +{ + struct renesas_r63419_panel *ctx = to_renesas_r63419_panel(panel); + struct mipi_dsi_multi_context dsi_ctx = { 0 }; + + mipi_dsi_dual(mipi_dsi_dcs_set_display_off_multi, + &dsi_ctx, ctx->dsi[0], ctx->dsi[1]); + mipi_dsi_msleep(&dsi_ctx, 50); + + mipi_dsi_dual(mipi_dsi_dcs_enter_sleep_mode_multi, + &dsi_ctx, ctx->dsi[0], ctx->dsi[1]); + mipi_dsi_msleep(&dsi_ctx, 120); + + return 0; +} + +static int renesas_r63419_prepare(struct drm_panel *panel) +{ + struct renesas_r63419_panel *ctx = to_renesas_r63419_panel(panel); + int ret; + + ret = regulator_bulk_enable(ARRAY_SIZE(renesas_r63419_vdd_supplies), + ctx->vdd_supplies); + if (ret < 0) + return ret; + + usleep_range(1000, 2000); + + ret = regulator_bulk_enable(ARRAY_SIZE(renesas_r63419_vcc_supplies), + ctx->vcc_supplies); + if (ret < 0) { + regulator_bulk_disable(ARRAY_SIZE(renesas_r63419_vdd_supplies), + ctx->vdd_supplies); + return ret; + } + + usleep_range(1000, 2000); + + gpiod_set_value_cansleep(ctx->reset_gpio, 0); + + usleep_range(3000, 4000); + + ret = renesas_r63419_on(ctx); + if (ret < 0) { + dev_err(panel->dev, "Failed to initialize panel: %d\n", ret); + + /* Power off sequence from the r63419 datasheet */ + regulator_bulk_disable(ARRAY_SIZE(renesas_r63419_vdd_supplies), + ctx->vdd_supplies); + + gpiod_set_value_cansleep(ctx->reset_gpio, 1); + + regulator_bulk_disable(ARRAY_SIZE(renesas_r63419_vcc_supplies), + ctx->vcc_supplies); + + return ret; + } + + return 0; +} + +static int renesas_r63419_unprepare(struct drm_panel *panel) +{ + struct renesas_r63419_panel *ctx = to_renesas_r63419_panel(panel); + + /* Power off sequence from the r63419 datasheet */ + regulator_bulk_disable(ARRAY_SIZE(renesas_r63419_vdd_supplies), ctx->vdd_supplies); + + gpiod_set_value_cansleep(ctx->reset_gpio, 1); + + regulator_bulk_disable(ARRAY_SIZE(renesas_r63419_vcc_supplies), ctx->vcc_supplies); + + return 0; +} + +static int renesas_r63419_get_modes(struct drm_panel *panel, + struct drm_connector *connector) +{ + struct renesas_r63419_panel *ctx = to_renesas_r63419_panel(panel); + const struct drm_display_mode *mode = ctx->desc->mode; + + return drm_connector_helper_get_modes_fixed(connector, mode); +} + +static enum drm_panel_orientation +renesas_r63419_get_orientation(struct drm_panel *panel) +{ + struct renesas_r63419_panel *ctx = to_renesas_r63419_panel(panel); + + return ctx->orientation; +} + +static const struct drm_panel_funcs renesas_r63419_panel_funcs = { + .disable = renesas_r63419_disable, + .prepare = renesas_r63419_prepare, + .unprepare = renesas_r63419_unprepare, + .get_modes = renesas_r63419_get_modes, + .get_orientation = renesas_r63419_get_orientation, +}; + +static int renesas_r63419_probe(struct mipi_dsi_device *dsi) +{ + struct mipi_dsi_device_info info = { 0 }; + struct device *dev = &dsi->dev; + struct renesas_r63419_panel *ctx; + struct device_node *dsi1_node; + struct mipi_dsi_host *dsi1_host; + int ret, i; + + ctx = devm_drm_panel_alloc(dev, struct renesas_r63419_panel, panel, + &renesas_r63419_panel_funcs, DRM_MODE_CONNECTOR_DSI); + if (IS_ERR(ctx)) + return PTR_ERR(ctx); + + ctx->desc = of_device_get_match_data(dev); + if (!ctx->desc) + return dev_err_probe(dev, -ENODEV, + "Failed to get panel description\n"); + + ret = devm_regulator_bulk_get_const(&dsi->dev, + ARRAY_SIZE(renesas_r63419_vdd_supplies), + renesas_r63419_vdd_supplies, &ctx->vdd_supplies); + if (ret < 0) + return ret; + + ret = devm_regulator_bulk_get_const(&dsi->dev, + ARRAY_SIZE(renesas_r63419_vcc_supplies), + renesas_r63419_vcc_supplies, &ctx->vcc_supplies); + if (ret < 0) + return ret; + + ctx->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH); + if (IS_ERR(ctx->reset_gpio)) + return dev_err_probe(dev, PTR_ERR(ctx->reset_gpio), + "Failed to get reset gpio\n"); + + /* Get second DSI host */ + dsi1_node = of_graph_get_remote_node(dsi->dev.of_node, 1, -1); + if (!dsi1_node) + return dev_err_probe(dev, -ENODEV, + "Failed to get remote node for second DSI\n"); + + dsi1_host = of_find_mipi_dsi_host_by_node(dsi1_node); + of_node_put(dsi1_node); + if (!dsi1_host) + return dev_err_probe(dev, -EPROBE_DEFER, + "Failed to find second DSI host\n"); + + /* Copy current DSI info, do not provide OF node since no driver needs to be attached */ + strscpy(info.type, dsi->name); + info.channel = dsi->channel; + + /* Register the second DSI device */ + ctx->dsi[1] = devm_mipi_dsi_device_register_full(dev, dsi1_host, &info); + if (IS_ERR(ctx->dsi[1])) + return dev_err_probe(dev, PTR_ERR(ctx->dsi[1]), + "Failed to register second DSI device\n"); + + ctx->dsi[0] = dsi; + mipi_dsi_set_drvdata(dsi, ctx); + + /* Get panel orientation */ + ret = of_drm_get_panel_orientation(dev->of_node, &ctx->orientation); + if (ret < 0 && ret != -ENODEV) + return dev_err_probe(dev, ret, + "Failed to get panel orientation\n"); + + ctx->panel.prepare_prev_first = true; + + ret = drm_panel_of_backlight(&ctx->panel); + if (ret) + return dev_err_probe(dev, ret, "Failed to get backlight\n"); + + ret = devm_drm_panel_add(dev, &ctx->panel); + if (ret) + return dev_err_probe(dev, ret, "Failed to add panel\n"); + + /* Configure and attach both DSI devices */ + for (i = 0; i < ARRAY_SIZE(ctx->dsi); i++) { + ctx->dsi[i]->lanes = ctx->desc->lanes; + ctx->dsi[i]->format = ctx->desc->format; + ctx->dsi[i]->mode_flags = ctx->desc->mode_flags; + + ret = devm_mipi_dsi_attach(dev, ctx->dsi[i]); + if (ret < 0) + return dev_err_probe(dev, ret, + "Failed to attach DSI device %d\n", i); + } + + return 0; +} + +static const struct of_device_id renesas_r63419_of_match[] = { + { + .compatible = "ayaneo,wt0600-2k", + .data = &wt0600_desc, + }, + { + .compatible = "ayaneo,wt0630-2k", + .data = &wt0630_desc, + }, + {} +}; +MODULE_DEVICE_TABLE(of, renesas_r63419_of_match); + +static struct mipi_dsi_driver renesas_r63419_driver = { + .probe = renesas_r63419_probe, + .driver = { + .name = "panel-renesas-r63419", + .of_match_table = renesas_r63419_of_match, + }, +}; +module_mipi_dsi_driver(renesas_r63419_driver); + +MODULE_AUTHOR("Kancy Joe <kancy2333@outlook.com>"); +MODULE_AUTHOR("Neil Armstrong <neil.armstrong@linaro.org>"); +MODULE_DESCRIPTION("DRM driver for Renesas R63419 based dual-DSI video mode panels"); +MODULE_LICENSE("GPL"); diff --git a/drivers/gpu/drm/panel/panel-visionox-vtdr6130.c b/drivers/gpu/drm/panel/panel-visionox-vtdr6130.c index 97a79411e1ec..6733e0e1d061 100644 --- a/drivers/gpu/drm/panel/panel-visionox-vtdr6130.c +++ b/drivers/gpu/drm/panel/panel-visionox-vtdr6130.c @@ -9,6 +9,7 @@ #include <linux/of.h> #include <drm/display/drm_dsc.h> +#include <drm/display/drm_dsc_helper.h> #include <drm/drm_mipi_dsi.h> #include <drm/drm_modes.h> #include <drm/drm_panel.h> @@ -17,6 +18,7 @@ struct visionox_vtdr6130 { struct drm_panel panel; + struct drm_dsc_config dsc; struct mipi_dsi_device *dsi; struct gpio_desc *reset_gpio; struct regulator_bulk_data *supplies; @@ -47,9 +49,12 @@ static int visionox_vtdr6130_on(struct visionox_vtdr6130 *ctx) { struct mipi_dsi_device *dsi = ctx->dsi; struct mipi_dsi_multi_context dsi_ctx = { .dsi = dsi }; + struct drm_dsc_picture_parameter_set pps; dsi->mode_flags |= MIPI_DSI_MODE_LPM; + mipi_dsi_dcs_write_seq_multi(&dsi_ctx, 0x03, 0x01); + mipi_dsi_dcs_set_tear_on_multi(&dsi_ctx, MIPI_DSI_DCS_TEAR_MODE_VBLANK); mipi_dsi_dcs_write_seq_multi(&dsi_ctx, @@ -123,6 +128,9 @@ static int visionox_vtdr6130_on(struct visionox_vtdr6130 *ctx) mipi_dsi_dcs_set_display_on_multi(&dsi_ctx); mipi_dsi_msleep(&dsi_ctx, 20); + drm_dsc_pps_payload_pack(&pps, dsi->dsc); + mipi_dsi_picture_parameter_set_multi(&dsi_ctx, &pps); + return dsi_ctx.accum_err; } @@ -269,6 +277,16 @@ static int visionox_vtdr6130_probe(struct mipi_dsi_device *dsi) ctx->dsi = dsi; mipi_dsi_set_drvdata(dsi, ctx); + ctx->dsc.dsc_version_major = 0x1; + ctx->dsc.dsc_version_minor = 0x2; + ctx->dsc.slice_height = 40; + ctx->dsc.slice_width = 540; + ctx->dsc.slice_count = 2; + ctx->dsc.bits_per_component = 8; + ctx->dsc.bits_per_pixel = 8 << 4; + ctx->dsc.block_pred_enable = true; + + dsi->dsc = &ctx->dsc; dsi->lanes = 4; dsi->format = MIPI_DSI_FMT_RGB888; dsi->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_NO_EOT_PACKET | @@ -280,28 +298,11 @@ static int visionox_vtdr6130_probe(struct mipi_dsi_device *dsi) return dev_err_probe(dev, PTR_ERR(ctx->panel.backlight), "Failed to create backlight\n"); - drm_panel_add(&ctx->panel); - - ret = mipi_dsi_attach(dsi); - if (ret < 0) { - dev_err(dev, "Failed to attach to DSI host: %d\n", ret); - drm_panel_remove(&ctx->panel); + ret = devm_drm_panel_add(dev, &ctx->panel); + if (ret) return ret; - } - - return 0; -} - -static void visionox_vtdr6130_remove(struct mipi_dsi_device *dsi) -{ - struct visionox_vtdr6130 *ctx = mipi_dsi_get_drvdata(dsi); - int ret; - - ret = mipi_dsi_detach(dsi); - if (ret < 0) - dev_err(&dsi->dev, "Failed to detach from DSI host: %d\n", ret); - drm_panel_remove(&ctx->panel); + return devm_mipi_dsi_attach(dev, dsi); } static const struct of_device_id visionox_vtdr6130_of_match[] = { @@ -312,7 +313,6 @@ MODULE_DEVICE_TABLE(of, visionox_vtdr6130_of_match); static struct mipi_dsi_driver visionox_vtdr6130_driver = { .probe = visionox_vtdr6130_probe, - .remove = visionox_vtdr6130_remove, .driver = { .name = "panel-visionox-vtdr6130", .of_match_table = visionox_vtdr6130_of_match, diff --git a/drivers/gpu/drm/panthor/panthor_device.h b/drivers/gpu/drm/panthor/panthor_device.h index 98828e81db0b..0fda64fbe5f2 100644 --- a/drivers/gpu/drm/panthor/panthor_device.h +++ b/drivers/gpu/drm/panthor/panthor_device.h @@ -161,6 +161,9 @@ struct panthor_device { /** @csif_info: Command stream interface information. */ struct drm_panthor_csif_info csif_info; + /** @mmu_info: MMU info */ + struct drm_panthor_mmu_info mmu_info; + /** @hw: GPU-specific data. */ struct panthor_hw *hw; diff --git a/drivers/gpu/drm/panthor/panthor_drv.c b/drivers/gpu/drm/panthor/panthor_drv.c index e8dc4096c1d2..106da676fd2e 100644 --- a/drivers/gpu/drm/panthor/panthor_drv.c +++ b/drivers/gpu/drm/panthor/panthor_drv.c @@ -175,6 +175,7 @@ panthor_get_uobj_array(const struct drm_panthor_obj_array *in, u32 min_stride, _Generic(_obj_name, \ PANTHOR_UOBJ_DECL(struct drm_panthor_gpu_info, tiler_present), \ PANTHOR_UOBJ_DECL(struct drm_panthor_csif_info, pad), \ + PANTHOR_UOBJ_DECL(struct drm_panthor_mmu_info, page_size_bitmap), \ PANTHOR_UOBJ_DECL(struct drm_panthor_timestamp_info, current_timestamp), \ PANTHOR_UOBJ_DECL(struct drm_panthor_group_priorities_info, pad), \ PANTHOR_UOBJ_DECL(struct drm_panthor_sync_op, timeline_value), \ @@ -954,6 +955,10 @@ static int panthor_ioctl_dev_query(struct drm_device *ddev, void *data, struct d args->size = sizeof(priorities_info); return 0; + case DRM_PANTHOR_DEV_QUERY_MMU_INFO: + args->size = sizeof(ptdev->mmu_info); + return 0; + default: return -EINVAL; } @@ -984,6 +989,9 @@ static int panthor_ioctl_dev_query(struct drm_device *ddev, void *data, struct d panthor_query_group_priorities_info(file, &priorities_info); return PANTHOR_UOBJ_SET(args->pointer, args->size, priorities_info); + case DRM_PANTHOR_DEV_QUERY_MMU_INFO: + return PANTHOR_UOBJ_SET(args->pointer, args->size, ptdev->mmu_info); + default: return -EINVAL; } @@ -1779,10 +1787,12 @@ static void panthor_debugfs_init(struct drm_minor *minor) * - adds DRM_IOCTL_PANTHOR_BO_QUERY_INFO ioctl * - adds drm_panthor_gpu_info::selected_coherency * - 1.8 - extends DEV_QUERY_TIMESTAMP_INFO with flags + * - 1.9 - adds DRM_PANTHOR_DEV_QUERY_MMU_INFO query + * - adds DRM_PANTHOR_VM_BIND_OP_MAP_SPARSE flag */ static const struct drm_driver panthor_drm_driver = { .driver_features = DRIVER_RENDER | DRIVER_GEM | DRIVER_SYNCOBJ | - DRIVER_SYNCOBJ_TIMELINE | DRIVER_GEM_GPUVA, + DRIVER_SYNCOBJ_TIMELINE, .open = panthor_open, .postclose = panthor_postclose, .show_fdinfo = panthor_show_fdinfo, @@ -1792,7 +1802,7 @@ static const struct drm_driver panthor_drm_driver = { .name = "panthor", .desc = "Panthor DRM driver", .major = 1, - .minor = 8, + .minor = 9, .gem_prime_import_sg_table = panthor_gem_prime_import_sg_table, .gem_prime_import = panthor_gem_prime_import, diff --git a/drivers/gpu/drm/panthor/panthor_gem.c b/drivers/gpu/drm/panthor/panthor_gem.c index a1e2eb1ca7bb..9855df738194 100644 --- a/drivers/gpu/drm/panthor/panthor_gem.c +++ b/drivers/gpu/drm/panthor/panthor_gem.c @@ -1347,6 +1347,24 @@ err_free_kbo: return ERR_PTR(ret); } +/** + * panthor_dummy_bo_create() - Create a Panthor BO meant to back sparse bindings. + * @ptdev: Device. + * + * Return: A valid pointer in case of success, an ERR_PTR() otherwise. + */ +struct panthor_gem_object * +panthor_dummy_bo_create(struct panthor_device *ptdev) +{ + /* Since even when the DRM device's mount point has enabled THP we have no guarantee + * that drm_gem_get_pages() will return a single 2MiB PMD, and also we cannot be sure + * that the 2MiB won't be reclaimed and re-allocated later on as 4KiB chunks, it doesn't + * make sense to pre-populate this object's page array, nor to fall back on a BO size + * of 4KiB. Sticking to a dummy object size of 2MiB lets us keep things simple for now. + */ + return panthor_gem_create(&ptdev->base, SZ_2M, DRM_PANTHOR_BO_NO_MMAP, NULL, 0); +} + static bool can_swap(void) { return get_nr_swap_pages() > 0; diff --git a/drivers/gpu/drm/panthor/panthor_gem.h b/drivers/gpu/drm/panthor/panthor_gem.h index 56d63137b4eb..5ae37d0d3646 100644 --- a/drivers/gpu/drm/panthor/panthor_gem.h +++ b/drivers/gpu/drm/panthor/panthor_gem.h @@ -325,6 +325,8 @@ panthor_kernel_bo_create(struct panthor_device *ptdev, struct panthor_vm *vm, void panthor_kernel_bo_destroy(struct panthor_kernel_bo *bo); +struct panthor_gem_object *panthor_dummy_bo_create(struct panthor_device *ptdev); + #ifdef CONFIG_DEBUG_FS void panthor_gem_debugfs_init(struct drm_minor *minor); #endif diff --git a/drivers/gpu/drm/panthor/panthor_mmu.c b/drivers/gpu/drm/panthor/panthor_mmu.c index e592a8ebb478..f45ef5824ff2 100644 --- a/drivers/gpu/drm/panthor/panthor_mmu.c +++ b/drivers/gpu/drm/panthor/panthor_mmu.c @@ -116,6 +116,17 @@ struct panthor_mmu { struct panthor_vm_pool { /** @xa: Array used for VM handle tracking. */ struct xarray xa; + + /** + * @dummy: Dummy object used for sparse mappings + * + * Sparse bindings map virtual address ranges onto a dummy + * BO in a modulo fashion. Even though sparse writes are meant + * to be discarded and reads undefined, writes are still reflected + * in the dummy buffer. That means we must keep a dummy object per + * file context, to avoid data leaks between them. + */ + struct panthor_gem_object *dummy; }; /** @@ -199,14 +210,6 @@ struct panthor_vm_op_ctx { /** @map.bo_offset: Offset in the buffer object. */ u64 bo_offset; - /** - * @map.sgt: sg-table pointing to pages backing the GEM object. - * - * This is gathered at job creation time, such that we don't have - * to allocate in ::run_job(). - */ - struct sg_table *sgt; - /** @map.bo: the BO being mapped. */ struct panthor_gem_object *bo; } map; @@ -403,6 +406,15 @@ struct panthor_vm { */ struct list_head lru_node; } reclaim; + + /** + * @dummy: Dummy object used for sparse mappings. + * + * VM's must keep a reference to the file context-wide dummy BO because + * they can outlive the file context, which includes the VM pool holding + * the original dummy BO reference. + */ + struct panthor_gem_object *dummy; }; /** @@ -1035,6 +1047,30 @@ panthor_vm_map_pages(struct panthor_vm *vm, u64 iova, int prot, return 0; } +static int +panthor_vm_map_sparse(struct panthor_vm *vm, u64 iova, int prot, + struct sg_table *sgt, u64 size) +{ + u64 mapped = 0; + int ret; + + while (mapped < size) { + u64 addr = iova + mapped; + u32 chunk_size = min(size - mapped, SZ_2M - (addr & (SZ_2M - 1))); + + ret = panthor_vm_map_pages(vm, addr, prot, sgt, + addr % SZ_2M, chunk_size); + if (ret) { + panthor_vm_unmap_pages(vm, iova, mapped); + return ret; + } + + mapped += chunk_size; + } + + return 0; +} + static int flags_to_prot(u32 flags) { int prot = 0; @@ -1277,15 +1313,15 @@ static int panthor_vm_op_ctx_prealloc_pts(struct panthor_vm_op_ctx *op_ctx) (DRM_PANTHOR_VM_BIND_OP_MAP_READONLY | \ DRM_PANTHOR_VM_BIND_OP_MAP_NOEXEC | \ DRM_PANTHOR_VM_BIND_OP_MAP_UNCACHED | \ + DRM_PANTHOR_VM_BIND_OP_MAP_SPARSE | \ DRM_PANTHOR_VM_BIND_OP_TYPE_MASK) static int panthor_vm_prepare_map_op_ctx(struct panthor_vm_op_ctx *op_ctx, struct panthor_vm *vm, struct panthor_gem_object *bo, - u64 offset, - u64 size, u64 va, - u32 flags) + const struct drm_panthor_vm_bind_op *op) { + bool is_sparse = op->flags & DRM_PANTHOR_VM_BIND_OP_MAP_SPARSE; struct drm_gpuvm_bo *preallocated_vm_bo; struct sg_table *sgt = NULL; int ret; @@ -1293,12 +1329,25 @@ static int panthor_vm_prepare_map_op_ctx(struct panthor_vm_op_ctx *op_ctx, if (!bo) return -EINVAL; - if ((flags & ~PANTHOR_VM_BIND_OP_MAP_FLAGS) || - (flags & DRM_PANTHOR_VM_BIND_OP_TYPE_MASK) != DRM_PANTHOR_VM_BIND_OP_TYPE_MAP) + if ((op->flags & ~PANTHOR_VM_BIND_OP_MAP_FLAGS) || + (op->flags & DRM_PANTHOR_VM_BIND_OP_TYPE_MASK) != DRM_PANTHOR_VM_BIND_OP_TYPE_MAP) return -EINVAL; - /* Make sure the VA and size are in-bounds. */ - if (size > bo->base.size || offset > bo->base.size - size) + /* uAPI mandates sparsely bound regions must not be executable. */ + if (is_sparse && !(op->flags & DRM_PANTHOR_VM_BIND_OP_MAP_NOEXEC)) + return -EINVAL; + + /* For non-sparse, make sure the VA and size are in-bounds. + * For sparse, this is not applicable, because the dummy BO is + * repeatedly mapped over a potentially wider VA range. + */ + if (!is_sparse && (op->size > bo->base.size || op->bo_offset > bo->base.size - op->size)) + return -EINVAL; + + /* For sparse, we don't expect any user BO, the BO we get passed + * is the dummy BO attached to the VM pool. + */ + if (is_sparse && (op->bo_handle || op->bo_offset)) return -EINVAL; /* If the BO has an exclusive VM attached, it can't be mapped to other VMs. */ @@ -1306,7 +1355,7 @@ static int panthor_vm_prepare_map_op_ctx(struct panthor_vm_op_ctx *op_ctx, bo->exclusive_vm_root_gem != panthor_vm_root_gem(vm)) return -EINVAL; - panthor_vm_init_op_ctx(op_ctx, size, va, flags); + panthor_vm_init_op_ctx(op_ctx, op->size, op->va, op->flags); ret = panthor_vm_op_ctx_prealloc_vmas(op_ctx); if (ret) @@ -1335,7 +1384,7 @@ static int panthor_vm_prepare_map_op_ctx(struct panthor_vm_op_ctx *op_ctx, } op_ctx->map.vm_bo = drm_gpuvm_bo_obtain_prealloc(preallocated_vm_bo); - op_ctx->map.bo_offset = offset; + op_ctx->map.bo_offset = op->bo_offset; ret = panthor_vm_op_ctx_prealloc_pts(op_ctx); if (ret) @@ -1446,7 +1495,9 @@ panthor_vm_get_bo_for_va(struct panthor_vm *vm, u64 va, u64 *bo_offset) if (vma && vma->base.gem.obj) { drm_gem_object_get(vma->base.gem.obj); bo = to_panthor_bo(vma->base.gem.obj); - *bo_offset = vma->base.gem.offset + (va - vma->base.va.addr); + *bo_offset = !(vma->flags & DRM_PANTHOR_VM_BIND_OP_MAP_SPARSE) ? + vma->base.gem.offset + (va - vma->base.va.addr) : + va & (SZ_2M - 1); } mutex_unlock(&vm->op_lock); @@ -1551,6 +1602,9 @@ int panthor_vm_pool_create_vm(struct panthor_device *ptdev, if (IS_ERR(vm)) return PTR_ERR(vm); + drm_gem_object_get(&pool->dummy->base); + vm->dummy = pool->dummy; + ret = xa_alloc(&pool->xa, &id, vm, XA_LIMIT(1, PANTHOR_MAX_VMS_PER_FILE), GFP_KERNEL); @@ -1650,6 +1704,8 @@ void panthor_vm_pool_destroy(struct panthor_file *pfile) xa_for_each(&pfile->vms->xa, i, vm) panthor_vm_destroy(vm); + if (pfile->vms->dummy) + drm_gem_object_put(&pfile->vms->dummy->base); xa_destroy(&pfile->vms->xa); kfree(pfile->vms); } @@ -1662,12 +1718,28 @@ void panthor_vm_pool_destroy(struct panthor_file *pfile) */ int panthor_vm_pool_create(struct panthor_file *pfile) { + struct panthor_gem_object *dummy; + int ret; + pfile->vms = kzalloc_obj(*pfile->vms); if (!pfile->vms) return -ENOMEM; xa_init_flags(&pfile->vms->xa, XA_FLAGS_ALLOC1); + + dummy = panthor_dummy_bo_create(pfile->ptdev); + if (IS_ERR(dummy)) { + ret = PTR_ERR(dummy); + goto err_destroy_vm_pool; + } + + pfile->vms->dummy = dummy; + return 0; + +err_destroy_vm_pool: + panthor_vm_pool_destroy(pfile); + return ret; } /* dummy TLB ops, the real TLB flush happens in panthor_vm_flush_range() */ @@ -2004,6 +2076,9 @@ static void panthor_vm_free(struct drm_gpuvm *gpuvm) free_io_pgtable_ops(vm->pgtbl_ops); + if (vm->dummy) + drm_gem_object_put(&vm->dummy->base); + drm_mm_takedown(&vm->mm); kfree(vm); } @@ -2163,7 +2238,30 @@ static void panthor_vma_init(struct panthor_vma *vma, u32 flags) #define PANTHOR_VM_MAP_FLAGS \ (DRM_PANTHOR_VM_BIND_OP_MAP_READONLY | \ DRM_PANTHOR_VM_BIND_OP_MAP_NOEXEC | \ - DRM_PANTHOR_VM_BIND_OP_MAP_UNCACHED) + DRM_PANTHOR_VM_BIND_OP_MAP_UNCACHED | \ + DRM_PANTHOR_VM_BIND_OP_MAP_SPARSE) + +static void +panthor_fix_sparse_map_offset(struct drm_gpuva_op_map *op, u32 flags) +{ + if (op && (flags & DRM_PANTHOR_VM_BIND_OP_MAP_SPARSE)) + op->gem.offset = op->va.addr & (SZ_2M - 1); +} + +static int +panthor_vm_exec_map_op(struct panthor_vm *vm, u32 flags, + const struct drm_gpuva_op_map *op) +{ + struct panthor_gem_object *bo = to_panthor_bo(op->gem.obj); + int prot = flags_to_prot(flags); + + if (flags & DRM_PANTHOR_VM_BIND_OP_MAP_SPARSE) + return panthor_vm_map_sparse(vm, op->va.addr, prot, + bo->dmap.sgt, op->va.range); + + return panthor_vm_map_pages(vm, op->va.addr, prot, bo->dmap.sgt, + op->gem.offset, op->va.range); +} static int panthor_gpuva_sm_step_map(struct drm_gpuva_op *op, void *priv) { @@ -2176,10 +2274,9 @@ static int panthor_gpuva_sm_step_map(struct drm_gpuva_op *op, void *priv) return -EINVAL; panthor_vma_init(vma, op_ctx->flags & PANTHOR_VM_MAP_FLAGS); + panthor_fix_sparse_map_offset(&op->map, vma->flags); - ret = panthor_vm_map_pages(vm, op->map.va.addr, flags_to_prot(vma->flags), - op_ctx->map.bo->dmap.sgt, op->map.gem.offset, - op->map.va.range); + ret = panthor_vm_exec_map_op(vm, vma->flags, &op->map); if (ret) { panthor_vm_op_ctx_return_vma(op_ctx, vma); return ret; @@ -2211,6 +2308,8 @@ static void unmap_hugepage_align(const struct drm_gpuva_op_remap *op, u64 *unmap_start, u64 *unmap_range) { + struct panthor_vma *unmap_vma = container_of(op->unmap->va, struct panthor_vma, base); + bool is_sparse = unmap_vma->flags & DRM_PANTHOR_VM_BIND_OP_MAP_SPARSE; u64 aligned_unmap_start, aligned_unmap_end, unmap_end; unmap_end = *unmap_start + *unmap_range; @@ -2218,11 +2317,15 @@ unmap_hugepage_align(const struct drm_gpuva_op_remap *op, aligned_unmap_end = ALIGN(unmap_end, SZ_2M); /* If we're dealing with a huge page, make sure the unmap region is - * aligned on the start of the page. + * aligned on the start of the page. If the unmapped VMA stands for + * a sparse mapping, always assume the backing storage is a THP, since + * the overhead of unmapping 2MiB worth of 4KiB pages and remapping + * some of them is offset by the logic of working out whether it's + * the opposite case right below. This also holds true for op->next. */ if (op->prev && aligned_unmap_start < *unmap_start && op->prev->va.addr <= aligned_unmap_start && - iova_mapped_as_huge_page(op->prev, *unmap_start)) { + (is_sparse || iova_mapped_as_huge_page(op->prev, *unmap_start))) { *unmap_range += *unmap_start - aligned_unmap_start; *unmap_start = aligned_unmap_start; } @@ -2232,7 +2335,7 @@ unmap_hugepage_align(const struct drm_gpuva_op_remap *op, */ if (op->next && aligned_unmap_end > unmap_end && op->next->va.addr + op->next->va.range >= aligned_unmap_end && - iova_mapped_as_huge_page(op->next, unmap_end - 1)) { + (is_sparse || iova_mapped_as_huge_page(op->next, unmap_end - 1))) { *unmap_range += aligned_unmap_end - unmap_end; } } @@ -2249,33 +2352,43 @@ static int panthor_gpuva_sm_step_remap(struct drm_gpuva_op *op, drm_gpuva_op_remap_to_unmap_range(&op->remap, &unmap_start, &unmap_range); - /* - * ARM IOMMU page table management code disallows partial unmaps of huge pages, - * so when a partial unmap is requested, we must first unmap the entire huge - * page and then remap the difference between the huge page minus the requested - * unmap region. Calculating the right start address and range for the expanded - * unmap operation is the responsibility of the following function. + /* op->remap.prev's BO offset is always the same as the unmap va's, but + * that of op->remap.next must be adjusted so as to remain < SZ_2M */ - unmap_hugepage_align(&op->remap, &unmap_start, &unmap_range); + panthor_fix_sparse_map_offset(op->remap.next, unmap_vma->flags); - /* If the range changed, we might have to lock a wider region to guarantee - * atomicity. panthor_vm_lock_region() bails out early if the new region - * is already part of the locked region, so no need to do this check here. - */ if (!unmap_vma->evicted) { + /* + * ARM IOMMU page table management code disallows partial unmaps of huge pages, + * so when a partial unmap is requested, we must first unmap the entire huge + * page and then remap the difference between the huge page minus the requested + * unmap region. Calculating the right start address and range for the expanded + * unmap operation is the responsibility of the following function. + */ + unmap_hugepage_align(&op->remap, &unmap_start, &unmap_range); + + /* If the range changed, we might have to lock a wider region to guarantee + * atomicity. panthor_vm_lock_region() bails out early if the new region + * is already part of the locked region, so no need to do this check here. + */ panthor_vm_lock_region(vm, unmap_start, unmap_range); panthor_vm_unmap_pages(vm, unmap_start, unmap_range); } if (op->remap.prev) { - struct panthor_gem_object *bo = to_panthor_bo(op->remap.prev->gem.obj); u64 offset = op->remap.prev->gem.offset + unmap_start - op->remap.prev->va.addr; u64 size = op->remap.prev->va.addr + op->remap.prev->va.range - unmap_start; - if (!unmap_vma->evicted) { - ret = panthor_vm_map_pages(vm, unmap_start, - flags_to_prot(unmap_vma->flags), - bo->dmap.sgt, offset, size); + if (!unmap_vma->evicted && size > 0) { + struct drm_gpuva_op_map map_op = { + .va.addr = unmap_start, + .va.range = size, + .gem.obj = op->remap.prev->gem.obj, + .gem.offset = offset, + }; + panthor_fix_sparse_map_offset(&map_op, unmap_vma->flags); + + ret = panthor_vm_exec_map_op(vm, unmap_vma->flags, &map_op); if (ret) return ret; } @@ -2286,14 +2399,19 @@ static int panthor_gpuva_sm_step_remap(struct drm_gpuva_op *op, } if (op->remap.next) { - struct panthor_gem_object *bo = to_panthor_bo(op->remap.next->gem.obj); u64 addr = op->remap.next->va.addr; u64 size = unmap_start + unmap_range - op->remap.next->va.addr; - if (!unmap_vma->evicted) { - ret = panthor_vm_map_pages(vm, addr, flags_to_prot(unmap_vma->flags), - bo->dmap.sgt, op->remap.next->gem.offset, - size); + if (!unmap_vma->evicted && size > 0) { + struct drm_gpuva_op_map map_op = { + .va.addr = addr, + .va.range = size, + .gem.obj = op->remap.next->gem.obj, + .gem.offset = op->remap.next->gem.offset, + }; + panthor_fix_sparse_map_offset(&map_op, unmap_vma->flags); + + ret = panthor_vm_exec_map_op(vm, unmap_vma->flags, &map_op); if (ret) return ret; } @@ -2490,11 +2608,17 @@ static int remap_evicted_vma(struct drm_gpuvm_bo *vm_bo, ret = panthor_vm_lock_region(vm, evicted_vma->base.va.addr, evicted_vma->base.va.range); if (!ret) { - ret = panthor_vm_map_pages(vm, evicted_vma->base.va.addr, - flags_to_prot(evicted_vma->flags), - bo->dmap.sgt, - evicted_vma->base.gem.offset, - evicted_vma->base.va.range); + struct drm_gpuva_op_map map_op = { + .va.addr = evicted_vma->base.va.addr, + .va.range = evicted_vma->base.va.range, + .gem.obj = &bo->base, + .gem.offset = evicted_vma->base.gem.offset, + }; + if (evicted_vma->flags & DRM_PANTHOR_VM_BIND_OP_MAP_SPARSE) + drm_WARN_ON_ONCE(&vm->ptdev->base, map_op.gem.offset != + (map_op.va.addr & (SZ_2M - 1))); + + ret = panthor_vm_exec_map_op(vm, evicted_vma->flags, &map_op); if (!ret) evicted_vma->evicted = false; @@ -2781,7 +2905,7 @@ panthor_vm_create(struct panthor_device *ptdev, bool for_mcu, refcount_set(&vm->as.active_cnt, 0); pgtbl_cfg = (struct io_pgtable_cfg) { - .pgsize_bitmap = SZ_4K | SZ_2M, + .pgsize_bitmap = ptdev->mmu_info.page_size_bitmap, .ias = va_bits, .oas = pa_bits, .coherent_walk = ptdev->coherent, @@ -2858,13 +2982,16 @@ panthor_vm_bind_prepare_op_ctx(struct drm_file *file, switch (op->flags & DRM_PANTHOR_VM_BIND_OP_TYPE_MASK) { case DRM_PANTHOR_VM_BIND_OP_TYPE_MAP: - gem = drm_gem_object_lookup(file, op->bo_handle); + if (!(op->flags & DRM_PANTHOR_VM_BIND_OP_MAP_SPARSE)) { + gem = drm_gem_object_lookup(file, op->bo_handle); + } else { + gem = &vm->dummy->base; + drm_gem_object_get(&vm->dummy->base); + } + ret = panthor_vm_prepare_map_op_ctx(op_ctx, vm, gem ? to_panthor_bo(gem) : NULL, - op->bo_offset, - op->size, - op->va, - op->flags); + op); drm_gem_object_put(gem); return ret; @@ -3060,10 +3187,19 @@ int panthor_vm_bind_exec_sync_op(struct drm_file *file, int panthor_vm_map_bo_range(struct panthor_vm *vm, struct panthor_gem_object *bo, u64 offset, u64 size, u64 va, u32 flags) { + struct drm_panthor_vm_bind_op op = { + .bo_offset = offset, + .size = size, + .va = va, + .flags = flags, + }; struct panthor_vm_op_ctx op_ctx; int ret; - ret = panthor_vm_prepare_map_op_ctx(&op_ctx, vm, bo, offset, size, va, flags); + if (drm_WARN_ON(&vm->ptdev->base, flags & DRM_PANTHOR_VM_BIND_OP_MAP_SPARSE)) + return -EINVAL; + + ret = panthor_vm_prepare_map_op_ctx(&op_ctx, vm, bo, &op); if (ret) return ret; @@ -3227,6 +3363,11 @@ static void panthor_mmu_release_wq(struct drm_device *ddev, void *res) destroy_workqueue(res); } +static void panthor_mmu_info_init(struct panthor_device *ptdev) +{ + ptdev->mmu_info.page_size_bitmap = SZ_4K | SZ_2M; +} + /** * panthor_mmu_init() - Initialize the MMU logic. * @ptdev: Device. @@ -3239,6 +3380,8 @@ int panthor_mmu_init(struct panthor_device *ptdev) struct panthor_mmu *mmu; int ret, irq; + panthor_mmu_info_init(ptdev); + mmu = drmm_kzalloc(&ptdev->base, sizeof(*mmu), GFP_KERNEL); if (!mmu) return -ENOMEM; diff --git a/drivers/gpu/drm/qxl/qxl_drv.c b/drivers/gpu/drm/qxl/qxl_drv.c index 1e6a2392d7c6..97df9d3d6957 100644 --- a/drivers/gpu/drm/qxl/qxl_drv.c +++ b/drivers/gpu/drm/qxl/qxl_drv.c @@ -50,11 +50,16 @@ #include "qxl_object.h" static const struct pci_device_id pciidlist[] = { - { 0x1b36, 0x100, PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_DISPLAY_VGA << 8, - 0xffff00, 0 }, - { 0x1b36, 0x100, PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_DISPLAY_OTHER << 8, - 0xffff00, 0 }, - { 0, 0, 0 }, + { + PCI_DEVICE(0x1b36, 0x0100), + .class = PCI_CLASS_DISPLAY_VGA << 8, + .class_mask = 0xffff00 + }, { + PCI_DEVICE(0x1b36, 0x0100), + .class = PCI_CLASS_DISPLAY_OTHER << 8, + .class_mask = 0xffff00 + }, + { }, }; MODULE_DEVICE_TABLE(pci, pciidlist); diff --git a/drivers/gpu/drm/renesas/rcar-du/rcar_du_drv.c b/drivers/gpu/drm/renesas/rcar-du/rcar_du_drv.c index 031d07f4508e..d9926c24984e 100644 --- a/drivers/gpu/drm/renesas/rcar-du/rcar_du_drv.c +++ b/drivers/gpu/drm/renesas/rcar-du/rcar_du_drv.c @@ -137,7 +137,7 @@ static const struct rcar_du_device_info rcar_du_r8a774a1_info = { }, .num_lvds = 1, .num_rpf = 5, - .dpll_mask = BIT(1), + .dpll_mask = BIT(1), }; static const struct rcar_du_device_info rcar_du_r8a774b1_info = { @@ -168,7 +168,7 @@ static const struct rcar_du_device_info rcar_du_r8a774b1_info = { }, .num_lvds = 1, .num_rpf = 5, - .dpll_mask = BIT(1), + .dpll_mask = BIT(1), }; static const struct rcar_du_device_info rcar_du_r8a774c0_info = { @@ -196,7 +196,7 @@ static const struct rcar_du_device_info rcar_du_r8a774c0_info = { }, .num_lvds = 2, .num_rpf = 4, - .lvds_clk_mask = BIT(1) | BIT(0), + .lvds_clk_mask = BIT(1) | BIT(0), }; static const struct rcar_du_device_info rcar_du_r8a774e1_info = { @@ -227,7 +227,7 @@ static const struct rcar_du_device_info rcar_du_r8a774e1_info = { }, .num_lvds = 1, .num_rpf = 5, - .dpll_mask = BIT(1), + .dpll_mask = BIT(1), }; static const struct rcar_du_device_info rcar_du_r8a7779_info = { @@ -385,7 +385,7 @@ static const struct rcar_du_device_info rcar_du_r8a7795_info = { }, .num_lvds = 1, .num_rpf = 5, - .dpll_mask = BIT(2) | BIT(1), + .dpll_mask = BIT(2) | BIT(1), }; static const struct rcar_du_device_info rcar_du_r8a7796_info = { @@ -416,7 +416,7 @@ static const struct rcar_du_device_info rcar_du_r8a7796_info = { }, .num_lvds = 1, .num_rpf = 5, - .dpll_mask = BIT(1), + .dpll_mask = BIT(1), }; static const struct rcar_du_device_info rcar_du_r8a77965_info = { @@ -447,7 +447,7 @@ static const struct rcar_du_device_info rcar_du_r8a77965_info = { }, .num_lvds = 1, .num_rpf = 5, - .dpll_mask = BIT(1), + .dpll_mask = BIT(1), }; static const struct rcar_du_device_info rcar_du_r8a77970_info = { @@ -502,7 +502,7 @@ static const struct rcar_du_device_info rcar_du_r8a7799x_info = { }, .num_lvds = 2, .num_rpf = 5, - .lvds_clk_mask = BIT(1) | BIT(0), + .lvds_clk_mask = BIT(1) | BIT(0), }; static const struct rcar_du_device_info rcar_du_r8a779a0_info = { @@ -523,7 +523,7 @@ static const struct rcar_du_device_info rcar_du_r8a779a0_info = { }, }, .num_rpf = 5, - .dsi_clk_mask = BIT(1) | BIT(0), + .dsi_clk_mask = BIT(1) | BIT(0), }; static const struct rcar_du_device_info rcar_du_r8a779g0_info = { @@ -544,7 +544,7 @@ static const struct rcar_du_device_info rcar_du_r8a779g0_info = { }, }, .num_rpf = 5, - .dsi_clk_mask = BIT(1) | BIT(0), + .dsi_clk_mask = BIT(1) | BIT(0), }; static const struct rcar_du_device_info rcar_du_r8a779h0_info = { diff --git a/drivers/gpu/drm/renesas/rcar-du/rcar_lvds.c b/drivers/gpu/drm/renesas/rcar-du/rcar_lvds.c index 154410745a74..e433ce61d431 100644 --- a/drivers/gpu/drm/renesas/rcar-du/rcar_lvds.c +++ b/drivers/gpu/drm/renesas/rcar-du/rcar_lvds.c @@ -658,7 +658,7 @@ static const struct drm_bridge_funcs rcar_lvds_bridge_ops = { .destroy = rcar_lvds_destroy, .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_create_state = drm_atomic_helper_bridge_create_state, .atomic_enable = rcar_lvds_atomic_enable, .atomic_disable = rcar_lvds_atomic_disable, .mode_fixup = rcar_lvds_mode_fixup, diff --git a/drivers/gpu/drm/renesas/rcar-du/rcar_mipi_dsi.c b/drivers/gpu/drm/renesas/rcar-du/rcar_mipi_dsi.c index aaafee1c060b..6e46d6d99f3c 100644 --- a/drivers/gpu/drm/renesas/rcar-du/rcar_mipi_dsi.c +++ b/drivers/gpu/drm/renesas/rcar-du/rcar_mipi_dsi.c @@ -924,7 +924,7 @@ static const struct drm_bridge_funcs rcar_mipi_dsi_bridge_ops = { .attach = rcar_mipi_dsi_attach, .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_create_state = drm_atomic_helper_bridge_create_state, .atomic_enable = rcar_mipi_dsi_atomic_enable, .atomic_disable = rcar_mipi_dsi_atomic_disable, .mode_valid = rcar_mipi_dsi_bridge_mode_valid, diff --git a/drivers/gpu/drm/renesas/rz-du/rzg2l_mipi_dsi.c b/drivers/gpu/drm/renesas/rz-du/rzg2l_mipi_dsi.c index a2c864860075..0590ade96b91 100644 --- a/drivers/gpu/drm/renesas/rz-du/rzg2l_mipi_dsi.c +++ b/drivers/gpu/drm/renesas/rz-du/rzg2l_mipi_dsi.c @@ -1109,7 +1109,7 @@ static const struct drm_bridge_funcs rzg2l_mipi_dsi_bridge_ops = { .attach = rzg2l_mipi_dsi_attach, .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_create_state = drm_atomic_helper_bridge_create_state, .atomic_pre_enable = rzg2l_mipi_dsi_atomic_pre_enable, .atomic_enable = rzg2l_mipi_dsi_atomic_enable, .atomic_disable = rzg2l_mipi_dsi_atomic_disable, diff --git a/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c b/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c index 06072efd7fca..776954c7d052 100644 --- a/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c +++ b/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c @@ -8,6 +8,7 @@ * Jeff Chen <jeff.chen@rock-chips.com> */ +#include <linux/cleanup.h> #include <linux/component.h> #include <linux/mfd/syscon.h> #include <linux/of.h> @@ -206,7 +207,6 @@ static void rockchip_dp_drm_encoder_enable(struct drm_encoder *encoder, struct drm_crtc *crtc; struct drm_crtc_state *old_crtc_state; struct of_endpoint endpoint; - struct device_node *remote_port, *remote_port_parent; char name[32]; u32 port_id; int ret; @@ -230,18 +230,22 @@ static void rockchip_dp_drm_encoder_enable(struct drm_encoder *encoder, if (ret < 0) return; - remote_port_parent = of_graph_get_remote_port_parent(endpoint.local_node); + struct device_node *remote_port_parent __free(device_node) = + of_graph_get_remote_port_parent(endpoint.local_node); if (remote_port_parent) { - if (of_get_child_by_name(remote_port_parent, "ports")) { - remote_port = of_graph_get_remote_port(endpoint.local_node); + struct device_node *ports __free(device_node) = + of_get_child_by_name(remote_port_parent, "ports"); + + if (ports) { + struct device_node *remote_port __free(device_node) = + of_graph_get_remote_port(endpoint.local_node); + of_property_read_u32(remote_port, "reg", &port_id); - of_node_put(remote_port); sprintf(name, "%s vp%d", remote_port_parent->full_name, port_id); } else { sprintf(name, "%s %s", remote_port_parent->full_name, endpoint.id ? "vopl" : "vopb"); } - of_node_put(remote_port_parent); DRM_DEV_DEBUG(dp->dev, "vop %s output to dp\n", (ret) ? "LIT" : "BIG"); } @@ -311,6 +315,7 @@ static int rockchip_dp_of_probe(struct rockchip_dp_device *dp) { struct device *dev = dp->dev; struct device_node *np = dev->of_node; + struct clk *clk; dp->grf = syscon_regmap_lookup_by_phandle(np, "rockchip,grf"); if (IS_ERR(dp->grf)) @@ -327,6 +332,11 @@ static int rockchip_dp_of_probe(struct rockchip_dp_device *dp) return dev_err_probe(dev, PTR_ERR(dp->pclk), "failed to get pclk property\n"); + clk = devm_clk_get_optional_enabled(dev, "hclk"); + if (IS_ERR(clk)) + return dev_err_probe(dev, PTR_ERR(clk), + "failed to get hclk property\n"); + dp->rst = devm_reset_control_get(dev, "dp"); if (IS_ERR(dp->rst)) return dev_err_probe(dev, PTR_ERR(dp->rst), @@ -516,6 +526,14 @@ static const struct rockchip_dp_chip_data rk3288_dp[] = { { /* sentinel */ } }; +static const struct rockchip_dp_chip_data rk3576_edp[] = { + { + .chip_type = RK3576_EDP, + .reg = 0x27dc0000, + }, + { /* sentinel */ } +}; + static const struct rockchip_dp_chip_data rk3588_edp[] = { { .edp_mode = GRF_REG_FIELD(0x0000, 0, 0), @@ -533,6 +551,7 @@ static const struct rockchip_dp_chip_data rk3588_edp[] = { static const struct of_device_id rockchip_dp_dt_ids[] = { {.compatible = "rockchip,rk3288-dp", .data = &rk3288_dp }, {.compatible = "rockchip,rk3399-edp", .data = &rk3399_edp }, + {.compatible = "rockchip,rk3576-edp", .data = &rk3576_edp }, {.compatible = "rockchip,rk3588-edp", .data = &rk3588_edp }, {} }; diff --git a/drivers/gpu/drm/rockchip/cdn-dp-core.c b/drivers/gpu/drm/rockchip/cdn-dp-core.c index 190cce9530c8..e159e1627f3c 100644 --- a/drivers/gpu/drm/rockchip/cdn-dp-core.c +++ b/drivers/gpu/drm/rockchip/cdn-dp-core.c @@ -825,7 +825,7 @@ out: static const struct drm_bridge_funcs cdn_dp_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_create_state = drm_atomic_helper_bridge_create_state, .detect = cdn_dp_bridge_detect, .edid_read = cdn_dp_bridge_edid_read, .atomic_enable = cdn_dp_bridge_atomic_enable, diff --git a/drivers/gpu/drm/rockchip/dw_dp-rockchip.c b/drivers/gpu/drm/rockchip/dw_dp-rockchip.c index 32bc73a1d5e4..0de822360c8d 100644 --- a/drivers/gpu/drm/rockchip/dw_dp-rockchip.c +++ b/drivers/gpu/drm/rockchip/dw_dp-rockchip.c @@ -107,15 +107,26 @@ static int dw_dp_rockchip_bind(struct device *dev, struct device *master, void * return PTR_ERR(dp->base); connector = drm_bridge_connector_init(drm_dev, encoder); - if (IS_ERR(connector)) + if (IS_ERR(connector)) { + dw_dp_unbind(dp->base); return dev_err_probe(dev, PTR_ERR(connector), - "Failed to init bridge connector"); + "Failed to init bridge connector\n"); + } return 0; } +static void dw_dp_rockchip_unbind(struct device *dev, struct device *master, + void *data) +{ + struct rockchip_dw_dp *dp = dev_get_drvdata(dev); + + dw_dp_unbind(dp->base); +} + static const struct component_ops dw_dp_rockchip_component_ops = { .bind = dw_dp_rockchip_bind, + .unbind = dw_dp_rockchip_unbind, }; static int dw_dp_probe(struct platform_device *pdev) diff --git a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c index 7abb42e486c0..cffc0af5190f 100644 --- a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c +++ b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c @@ -5,6 +5,7 @@ #include <linux/clk.h> #include <linux/hw_bitfield.h> +#include <linux/media-bus-format.h> #include <linux/mfd/syscon.h> #include <linux/module.h> #include <linux/platform_device.h> @@ -14,6 +15,7 @@ #include <drm/bridge/dw_hdmi.h> #include <drm/drm_edid.h> +#include <drm/drm_managed.h> #include <drm/drm_of.h> #include <drm/drm_probe_helper.h> #include <drm/drm_simple_kms_helper.h> @@ -30,8 +32,8 @@ #define RK3288_GRF_SOC_CON6 0x025C #define RK3288_HDMI_LCDC_SEL BIT(4) -#define RK3328_GRF_SOC_CON2 0x0408 +#define RK3328_GRF_SOC_CON2 0x0408 #define RK3328_HDMI_SDAIN_MSK BIT(11) #define RK3328_HDMI_SCLIN_MSK BIT(10) #define RK3328_HDMI_HPD_IOE BIT(2) @@ -78,6 +80,7 @@ struct rockchip_hdmi { struct clk *hdmiphy_clk; struct clk *ref_clk; struct clk *grf_clk; + struct drm_bridge *bridge; struct dw_hdmi *hdmi; struct phy *phy; }; @@ -188,48 +191,13 @@ static const struct dw_hdmi_curr_ctrl rockchip_cur_ctr[] = { static const struct dw_hdmi_phy_config rockchip_phy_config[] = { /*pixelclk symbol term vlev*/ - { 74250000, 0x8009, 0x0004, 0x0272}, - { 165000000, 0x802b, 0x0004, 0x0209}, - { 297000000, 0x8039, 0x0005, 0x028d}, - { 594000000, 0x8039, 0x0000, 0x019d}, - { ~0UL, 0x0000, 0x0000, 0x0000} + { 74250000, 0x8009, 0x0004, 0x0272 }, + { 165000000, 0x802b, 0x0004, 0x0209 }, + { 297000000, 0x8039, 0x0005, 0x028d }, + { 594000000, 0x8039, 0x0000, 0x019d }, + { ~0UL, 0x0000, 0x0000, 0x0000 }, }; -static int rockchip_hdmi_parse_dt(struct rockchip_hdmi *hdmi) -{ - struct device_node *np = hdmi->dev->of_node; - int ret; - - hdmi->regmap = syscon_regmap_lookup_by_phandle(np, "rockchip,grf"); - if (IS_ERR(hdmi->regmap)) { - dev_err(hdmi->dev, "Unable to get rockchip,grf\n"); - return PTR_ERR(hdmi->regmap); - } - - hdmi->ref_clk = devm_clk_get_optional_enabled(hdmi->dev, "ref"); - if (!hdmi->ref_clk) - hdmi->ref_clk = devm_clk_get_optional_enabled(hdmi->dev, "vpll"); - - if (IS_ERR(hdmi->ref_clk)) { - ret = PTR_ERR(hdmi->ref_clk); - return dev_err_probe(hdmi->dev, ret, "failed to get reference clock\n"); - } - - hdmi->grf_clk = devm_clk_get_optional(hdmi->dev, "grf"); - if (IS_ERR(hdmi->grf_clk)) { - ret = PTR_ERR(hdmi->grf_clk); - return dev_err_probe(hdmi->dev, ret, "failed to get grf clock\n"); - } - - ret = devm_regulator_get_enable(hdmi->dev, "avdd-0v9"); - if (ret) - return ret; - - ret = devm_regulator_get_enable(hdmi->dev, "avdd-1v8"); - - return ret; -} - static enum drm_mode_status dw_hdmi_rockchip_mode_valid(struct dw_hdmi *dw_hdmi, void *data, const struct drm_display_info *info, @@ -259,23 +227,22 @@ dw_hdmi_rockchip_mode_valid(struct dw_hdmi *dw_hdmi, void *data, return MODE_OK; } -static void dw_hdmi_rockchip_encoder_disable(struct drm_encoder *encoder) +static void +dw_hdmi_rockchip_encoder_atomic_mode_set(struct drm_encoder *encoder, + struct drm_crtc_state *crtc_state, + struct drm_connector_state *conn_state) { -} + struct rockchip_hdmi *hdmi = to_rockchip_hdmi(encoder); + struct drm_display_mode *adj_mode = &crtc_state->adjusted_mode; -static bool -dw_hdmi_rockchip_encoder_mode_fixup(struct drm_encoder *encoder, - const struct drm_display_mode *mode, - struct drm_display_mode *adj_mode) -{ - return true; -} + if (hdmi->phy && conn_state->hdmi.tmds_char_rate) { + union phy_configure_opts opts = {}; -static void dw_hdmi_rockchip_encoder_mode_set(struct drm_encoder *encoder, - struct drm_display_mode *mode, - struct drm_display_mode *adj_mode) -{ - struct rockchip_hdmi *hdmi = to_rockchip_hdmi(encoder); + opts.hdmi.bpc = conn_state->hdmi.output_bpc; + opts.hdmi.tmds_char_rate = conn_state->hdmi.tmds_char_rate; + + phy_configure(hdmi->phy, &opts); + } clk_set_rate(hdmi->ref_clk, adj_mode->clock * 1000); } @@ -309,24 +276,71 @@ static void dw_hdmi_rockchip_encoder_enable(struct drm_encoder *encoder) dev_dbg(hdmi->dev, "vop %s output to hdmi\n", ret ? "LIT" : "BIG"); } +static u32 dw_hdmi_rockchip_get_bus_format(struct drm_encoder *encoder, + struct drm_connector_state *conn_state) +{ + struct drm_bridge *bridge __free(drm_bridge_put) = NULL; + struct drm_bridge_state *bridge_state; + + bridge = drm_bridge_chain_get_first_bridge(encoder); + if (!bridge) + return 0; + + bridge_state = drm_atomic_get_bridge_state(conn_state->state, bridge); + if (!bridge_state) + return 0; + + if (bridge_state->input_bus_cfg.format != MEDIA_BUS_FMT_FIXED) + return bridge_state->input_bus_cfg.format; + + return bridge_state->output_bus_cfg.format; +} + static int dw_hdmi_rockchip_encoder_atomic_check(struct drm_encoder *encoder, struct drm_crtc_state *crtc_state, struct drm_connector_state *conn_state) { struct rockchip_crtc_state *s = to_rockchip_crtc_state(crtc_state); + struct rockchip_hdmi *hdmi = to_rockchip_hdmi(encoder); + union phy_configure_opts opts = {}; + u32 bus_format; + + bus_format = dw_hdmi_rockchip_get_bus_format(encoder, conn_state); + + switch (bus_format) { + case MEDIA_BUS_FMT_FIXED: + bus_format = MEDIA_BUS_FMT_RGB888_1X24; + fallthrough; + case MEDIA_BUS_FMT_RGB888_1X24: + case MEDIA_BUS_FMT_RGB101010_1X30: + case MEDIA_BUS_FMT_YUV8_1X24: + case MEDIA_BUS_FMT_YUV10_1X30: + s->output_mode = ROCKCHIP_OUT_MODE_AAAA; + break; + case MEDIA_BUS_FMT_UYYVYY8_0_5X24: + case MEDIA_BUS_FMT_UYYVYY10_0_5X30: + s->output_mode = ROCKCHIP_OUT_MODE_YUV420; + break; + default: + return -EINVAL; + } - s->output_mode = ROCKCHIP_OUT_MODE_AAAA; s->output_type = DRM_MODE_CONNECTOR_HDMIA; + s->bus_format = bus_format; - return 0; + if (!hdmi->phy || !conn_state->hdmi.tmds_char_rate) + return 0; + + opts.hdmi.bpc = conn_state->hdmi.output_bpc; + opts.hdmi.tmds_char_rate = conn_state->hdmi.tmds_char_rate; + + return phy_validate(hdmi->phy, PHY_MODE_HDMI, PHY_HDMI_MODE_TMDS, &opts); } static const struct drm_encoder_helper_funcs dw_hdmi_rockchip_encoder_helper_funcs = { - .mode_fixup = dw_hdmi_rockchip_encoder_mode_fixup, - .mode_set = dw_hdmi_rockchip_encoder_mode_set, - .enable = dw_hdmi_rockchip_encoder_enable, - .disable = dw_hdmi_rockchip_encoder_disable, + .atomic_mode_set = dw_hdmi_rockchip_encoder_atomic_mode_set, + .enable = dw_hdmi_rockchip_encoder_enable, .atomic_check = dw_hdmi_rockchip_encoder_atomic_check, }; @@ -508,6 +522,7 @@ static const struct dw_hdmi_plat_data rk3568_hdmi_drv_data = { .phy_config = rockchip_phy_config, .phy_data = &rk3568_chip_data, .use_drm_infoframe = true, + .output_port = 1, }; static const struct of_device_id dw_hdmi_rockchip_dt_ids[] = { @@ -538,8 +553,8 @@ static int dw_hdmi_rockchip_bind(struct device *dev, struct device *master, { struct platform_device *pdev = to_platform_device(dev); struct device_node *np = dev_of_node(dev); + const struct dw_hdmi_plat_data *drv_data; struct dw_hdmi_plat_data *plat_data; - const struct of_device_id *match; struct drm_device *drm = data; struct drm_encoder *encoder; struct rockchip_hdmi *hdmi; @@ -548,17 +563,20 @@ static int dw_hdmi_rockchip_bind(struct device *dev, struct device *master, if (!np) return -ENODEV; - hdmi = devm_kzalloc(&pdev->dev, sizeof(*hdmi), GFP_KERNEL); + drv_data = of_device_get_match_data(dev); + if (!drv_data) + return -ENODEV; + + hdmi = drmm_kzalloc(drm, sizeof(*hdmi), GFP_KERNEL); if (!hdmi) return -ENOMEM; - match = of_match_node(dw_hdmi_rockchip_dt_ids, np); - plat_data = devm_kmemdup(&pdev->dev, match->data, - sizeof(*plat_data), GFP_KERNEL); + plat_data = drmm_kzalloc(drm, sizeof(*drv_data), GFP_KERNEL); if (!plat_data) return -ENOMEM; + memcpy(plat_data, drv_data, sizeof(*drv_data)); - hdmi->dev = &pdev->dev; + hdmi->dev = dev; hdmi->plat_data = plat_data; hdmi->chip_data = plat_data->phy_data; plat_data->phy_data = hdmi; @@ -576,18 +594,39 @@ static int dw_hdmi_rockchip_bind(struct device *dev, struct device *master, * the required CRTC is added later. */ if (encoder->possible_crtcs == 0) - return -EPROBE_DEFER; + return dev_err_probe(dev, -EPROBE_DEFER, + "failed to find possible crtcs\n"); - ret = rockchip_hdmi_parse_dt(hdmi); - if (ret) { - return dev_err_probe(hdmi->dev, ret, "Unable to parse OF data\n"); - } + hdmi->regmap = syscon_regmap_lookup_by_phandle(np, "rockchip,grf"); + if (IS_ERR(hdmi->regmap)) + return dev_err_probe(dev, PTR_ERR(hdmi->regmap), + "failed to get rockchip,grf\n"); + + hdmi->ref_clk = devm_clk_get_optional_enabled(dev, "ref"); + if (!hdmi->ref_clk) + hdmi->ref_clk = devm_clk_get_optional_enabled(dev, "vpll"); + + if (IS_ERR(hdmi->ref_clk)) + return dev_err_probe(dev, PTR_ERR(hdmi->ref_clk), + "failed to get reference clock\n"); + + hdmi->grf_clk = devm_clk_get_optional(dev, "grf"); + if (IS_ERR(hdmi->grf_clk)) + return dev_err_probe(dev, PTR_ERR(hdmi->grf_clk), + "failed to get grf clock\n"); + + ret = devm_regulator_get_enable(dev, "avdd-0v9"); + if (ret) + return dev_err_probe(dev, ret, "failed to enable avdd-0v9\n"); + + ret = devm_regulator_get_enable(dev, "avdd-1v8"); + if (ret) + return dev_err_probe(dev, ret, "failed to enable avdd-1v8\n"); hdmi->phy = devm_phy_optional_get(dev, "hdmi"); - if (IS_ERR(hdmi->phy)) { - ret = PTR_ERR(hdmi->phy); - return dev_err_probe(hdmi->dev, ret, "failed to get phy\n"); - } + if (IS_ERR(hdmi->phy)) + return dev_err_probe(dev, PTR_ERR(hdmi->phy), + "failed to get phy\n"); index = of_property_match_string(np, "phy-names", "hdmi"); if (index >= 0) { @@ -608,28 +647,27 @@ static int dw_hdmi_rockchip_bind(struct device *dev, struct device *master, FIELD_PREP_WM16(RK3568_HDMI_SCLIN_MSK, 1)); } + ret = drmm_encoder_init(drm, encoder, NULL, DRM_MODE_ENCODER_TMDS, NULL); + if (ret) + return dev_err_probe(dev, ret, "failed to init encoder\n"); + drm_encoder_helper_add(encoder, &dw_hdmi_rockchip_encoder_helper_funcs); - drm_simple_encoder_init(drm, encoder, DRM_MODE_ENCODER_TMDS); platform_set_drvdata(pdev, hdmi); hdmi->hdmi = dw_hdmi_bind(pdev, encoder, plat_data); - - /* - * If dw_hdmi_bind() fails we'll never call dw_hdmi_unbind(), - * which would have called the encoder cleanup. Do it manually. - */ - if (IS_ERR(hdmi->hdmi)) { - ret = PTR_ERR(hdmi->hdmi); - goto err_bind; + if (IS_ERR(hdmi->hdmi)) + return dev_err_probe(dev, PTR_ERR(hdmi->hdmi), + "failed to probe dw-hdmi bridge\n"); + + hdmi->bridge = of_drm_find_and_get_bridge(np); + if (!hdmi->bridge) { + dw_hdmi_unbind(hdmi->hdmi); + return dev_err_probe(dev, -ENODEV, + "failed to find dw-hdmi bridge\n"); } return 0; - -err_bind: - drm_encoder_cleanup(encoder); - - return ret; } static void dw_hdmi_rockchip_unbind(struct device *dev, struct device *master, @@ -637,8 +675,8 @@ static void dw_hdmi_rockchip_unbind(struct device *dev, struct device *master, { struct rockchip_hdmi *hdmi = dev_get_drvdata(dev); + drm_bridge_put(hdmi->bridge); dw_hdmi_unbind(hdmi->hdmi); - drm_encoder_cleanup(&hdmi->encoder.encoder); } static const struct component_ops dw_hdmi_rockchip_ops = { @@ -656,17 +694,18 @@ static void dw_hdmi_rockchip_remove(struct platform_device *pdev) component_del(&pdev->dev, &dw_hdmi_rockchip_ops); } -static int __maybe_unused dw_hdmi_rockchip_resume(struct device *dev) +static int __maybe_unused dw_hdmi_rockchip_resume_early(struct device *dev) { struct rockchip_hdmi *hdmi = dev_get_drvdata(dev); - dw_hdmi_resume(hdmi->hdmi); + if (hdmi) + dw_hdmi_resume(hdmi->hdmi); return 0; } static const struct dev_pm_ops dw_hdmi_rockchip_pm = { - SET_SYSTEM_SLEEP_PM_OPS(NULL, dw_hdmi_rockchip_resume) + SET_LATE_SYSTEM_SLEEP_PM_OPS(NULL, dw_hdmi_rockchip_resume_early) }; struct platform_driver dw_hdmi_rockchip_pltfm_driver = { diff --git a/drivers/gpu/drm/rockchip/dw_hdmi_qp-rockchip.c b/drivers/gpu/drm/rockchip/dw_hdmi_qp-rockchip.c index f35484715c2d..815f9ea7bcbe 100644 --- a/drivers/gpu/drm/rockchip/dw_hdmi_qp-rockchip.c +++ b/drivers/gpu/drm/rockchip/dw_hdmi_qp-rockchip.c @@ -11,6 +11,7 @@ #include <linux/gpio/consumer.h> #include <linux/hw_bitfield.h> #include <linux/mfd/syscon.h> +#include <linux/media-bus-format.h> #include <linux/module.h> #include <linux/platform_device.h> #include <linux/phy/phy.h> @@ -44,10 +45,6 @@ #define RK3576_8BPC 0x0 #define RK3576_10BPC 0x6 #define RK3576_COLOR_FORMAT_MASK GENMASK(7, 4) -#define RK3576_RGB 0x9 -#define RK3576_YUV422 0x1 -#define RK3576_YUV444 0x2 -#define RK3576_YUV420 0x3 #define RK3576_CECIN_MASK BIT(3) #define RK3576_VO0_GRF_SOC_CON14 0x0038 @@ -75,8 +72,6 @@ #define RK3588_8BPC 0x0 #define RK3588_10BPC 0x6 #define RK3588_COLOR_FORMAT_MASK GENMASK(3, 0) -#define RK3588_RGB 0x0 -#define RK3588_YUV420 0x3 #define RK3588_SCLIN_MASK BIT(9) #define RK3588_SDAIN_MASK BIT(10) #define RK3588_MODE_MASK BIT(11) @@ -88,6 +83,11 @@ #define HOTPLUG_DEBOUNCE_MS 150 #define MAX_HDMI_PORT_NUM 2 +#define RK_COLOR_FMT_RGB 0x0 +#define RK_COLOR_FMT_YUV422 0x1 +#define RK_COLOR_FMT_YUV444 0x2 +#define RK_COLOR_FMT_YUV420 0x3 + struct rockchip_hdmi_qp { struct device *dev; struct regmap *regmap; @@ -116,6 +116,33 @@ static struct rockchip_hdmi_qp *to_rockchip_hdmi_qp(struct drm_encoder *encoder) return container_of(rkencoder, struct rockchip_hdmi_qp, encoder); } +/** + * dw_hdmi_qp_rockchip_bus_fmt_to_reg - converts a bus format to a GRF reg value + * @bus_fmt: One of the MEDIA_BUS_FMT_s allowed by this driver's atomic_check + * + * Returns: an unshifted value to be written to the COLOR_FORMAT GRF register + * on success, or %-EINVAL if the bus format is not supported. + */ +static int __pure dw_hdmi_qp_rockchip_bus_fmt_to_reg(u32 bus_fmt) +{ + switch (bus_fmt) { + case MEDIA_BUS_FMT_RGB888_1X24: + case MEDIA_BUS_FMT_RGB101010_1X30: + return RK_COLOR_FMT_RGB; + case MEDIA_BUS_FMT_UYVY8_1X16: + case MEDIA_BUS_FMT_UYVY10_1X20: + return RK_COLOR_FMT_YUV422; + case MEDIA_BUS_FMT_YUV8_1X24: + case MEDIA_BUS_FMT_YUV10_1X30: + return RK_COLOR_FMT_YUV444; + case MEDIA_BUS_FMT_UYYVYY8_0_5X24: + case MEDIA_BUS_FMT_UYYVYY10_0_5X30: + return RK_COLOR_FMT_YUV420; + } + + return -EINVAL; +} + static void dw_hdmi_qp_rockchip_encoder_enable(struct drm_encoder *encoder) { struct rockchip_hdmi_qp *hdmi = to_rockchip_hdmi_qp(encoder); @@ -131,29 +158,83 @@ static void dw_hdmi_qp_rockchip_encoder_enable(struct drm_encoder *encoder) hdmi->ctrl_ops->enc_init(hdmi, to_rockchip_crtc_state(crtc->state)); } +/** + * dw_hdmi_qp_rockchip_get_vop_format - get the bus format VOP should output + * @encoder: pointer to a &struct drm_encoder + * @conn_state: pointer to the current atomic &struct drm_connector_state + * + * Determines which bus format the Rockchip video processor should output as + * to feed into the bridge chain. + * + * Returns a MEDIA_BUS_FMT_* on success, or negative errno on error. + */ +static int dw_hdmi_qp_rockchip_get_vop_format(struct drm_encoder *encoder, + struct drm_connector_state *conn_state) +{ + struct drm_bridge *bridge __free(drm_bridge_put) = NULL; + struct drm_bridge_state *bstate; + + bridge = drm_bridge_chain_get_first_bridge(encoder); + if (!bridge) + return -ENODEV; + + bstate = drm_atomic_get_bridge_state(conn_state->state, bridge); + if (IS_ERR(bstate)) + return PTR_ERR(bstate); + + if (bstate->input_bus_cfg.format != MEDIA_BUS_FMT_FIXED) + return bstate->input_bus_cfg.format; + + return bstate->output_bus_cfg.format; +} + static int dw_hdmi_qp_rockchip_encoder_atomic_check(struct drm_encoder *encoder, struct drm_crtc_state *crtc_state, struct drm_connector_state *conn_state) { - struct rockchip_hdmi_qp *hdmi = to_rockchip_hdmi_qp(encoder); struct rockchip_crtc_state *s = to_rockchip_crtc_state(crtc_state); + struct rockchip_hdmi_qp *hdmi = to_rockchip_hdmi_qp(encoder); union phy_configure_opts phy_cfg = {}; + int ingest_fmt; int ret; + ingest_fmt = dw_hdmi_qp_rockchip_get_vop_format(encoder, conn_state); + if (ingest_fmt < 0) + return -EINVAL; + if (hdmi->tmds_char_rate == conn_state->hdmi.tmds_char_rate && - s->output_bpc == conn_state->hdmi.output_bpc) + s->output_bpc == conn_state->hdmi.output_bpc && + s->bus_format == ingest_fmt) return 0; + switch (ingest_fmt) { + case MEDIA_BUS_FMT_RGB888_1X24: + case MEDIA_BUS_FMT_RGB101010_1X30: + case MEDIA_BUS_FMT_YUV8_1X24: + case MEDIA_BUS_FMT_YUV10_1X30: + s->output_mode = ROCKCHIP_OUT_MODE_AAAA; + break; + case MEDIA_BUS_FMT_UYVY8_1X16: + s->output_mode = ROCKCHIP_OUT_MODE_YUV422; + break; + case MEDIA_BUS_FMT_UYYVYY8_0_5X24: + case MEDIA_BUS_FMT_UYYVYY10_0_5X30: + s->output_mode = ROCKCHIP_OUT_MODE_YUV420; + break; + default: + return -EINVAL; + } + phy_cfg.hdmi.tmds_char_rate = conn_state->hdmi.tmds_char_rate; phy_cfg.hdmi.bpc = conn_state->hdmi.output_bpc; ret = phy_configure(hdmi->phy, &phy_cfg); if (!ret) { hdmi->tmds_char_rate = conn_state->hdmi.tmds_char_rate; - s->output_mode = ROCKCHIP_OUT_MODE_AAAA; s->output_type = DRM_MODE_CONNECTOR_HDMIA; s->output_bpc = conn_state->hdmi.output_bpc; + s->bus_format = ingest_fmt; } else { dev_err(hdmi->dev, "Failed to configure phy: %d\n", ret); } @@ -383,6 +464,7 @@ static void dw_hdmi_qp_rk3588_io_init(struct rockchip_hdmi_qp *hdmi) static void dw_hdmi_qp_rk3576_enc_init(struct rockchip_hdmi_qp *hdmi, struct rockchip_crtc_state *state) { + int color = dw_hdmi_qp_rockchip_bus_fmt_to_reg(state->bus_format); u32 val; if (state->output_bpc == 10) @@ -390,12 +472,16 @@ static void dw_hdmi_qp_rk3576_enc_init(struct rockchip_hdmi_qp *hdmi, else val = FIELD_PREP_WM16(RK3576_COLOR_DEPTH_MASK, RK3576_8BPC); + if (likely(color >= 0)) + val |= FIELD_PREP_WM16(RK3576_COLOR_FORMAT_MASK, color); + regmap_write(hdmi->vo_regmap, RK3576_VO0_GRF_SOC_CON8, val); } static void dw_hdmi_qp_rk3588_enc_init(struct rockchip_hdmi_qp *hdmi, struct rockchip_crtc_state *state) { + int color = dw_hdmi_qp_rockchip_bus_fmt_to_reg(state->bus_format); u32 val; if (state->output_bpc == 10) @@ -403,6 +489,9 @@ static void dw_hdmi_qp_rk3588_enc_init(struct rockchip_hdmi_qp *hdmi, else val = FIELD_PREP_WM16(RK3588_COLOR_DEPTH_MASK, RK3588_8BPC); + if (likely(color >= 0)) + val |= FIELD_PREP_WM16(RK3588_COLOR_FORMAT_MASK, color); + regmap_write(hdmi->vo_regmap, hdmi->port_id ? RK3588_GRF_VO1_CON6 : RK3588_GRF_VO1_CON3, val); @@ -513,6 +602,10 @@ static int dw_hdmi_qp_rockchip_bind(struct device *dev, struct device *master, plat_data.phy_data = hdmi; plat_data.max_bpc = 10; + plat_data.supported_formats = BIT(DRM_OUTPUT_COLOR_FORMAT_RGB444) | + BIT(DRM_OUTPUT_COLOR_FORMAT_YCBCR444) | + BIT(DRM_OUTPUT_COLOR_FORMAT_YCBCR422); + encoder = &hdmi->encoder.encoder; encoder->possible_crtcs = drm_of_find_possible_crtcs(drm, dev->of_node); diff --git a/drivers/gpu/drm/rockchip/rk3066_hdmi.c b/drivers/gpu/drm/rockchip/rk3066_hdmi.c index 1996f8656612..9b8981fa19ae 100644 --- a/drivers/gpu/drm/rockchip/rk3066_hdmi.c +++ b/drivers/gpu/drm/rockchip/rk3066_hdmi.c @@ -497,7 +497,7 @@ rk3066_hdmi_bridge_mode_valid(struct drm_bridge *bridge, static const struct drm_bridge_funcs rk3066_hdmi_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_create_state = drm_atomic_helper_bridge_create_state, .atomic_enable = rk3066_hdmi_bridge_atomic_enable, .atomic_disable = rk3066_hdmi_bridge_atomic_disable, .detect = rk3066_hdmi_bridge_detect, diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_drv.h b/drivers/gpu/drm/rockchip/rockchip_drm_drv.h index 2e86ad00979c..4705dc6b8bd7 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_drv.h +++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.h @@ -30,10 +30,14 @@ #define ROCKCHIP_OUT_MODE_P565 2 #define ROCKCHIP_OUT_MODE_BT656 5 #define ROCKCHIP_OUT_MODE_S888 8 +#define ROCKCHIP_OUT_MODE_YUV422 9 #define ROCKCHIP_OUT_MODE_S888_DUMMY 12 #define ROCKCHIP_OUT_MODE_YUV420 14 /* for use special outface */ #define ROCKCHIP_OUT_MODE_AAAA 15 +/* SoC specific output modes */ +#define ROCKCHIP_OUT_MODE_YUV422_RK3576_DP 12 +#define ROCKCHIP_OUT_MODE_YUV422_RK3576_HDMI 13 /* output flags */ #define ROCKCHIP_OUTPUT_DSI_DUAL BIT(0) diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c index a160077a507f..4cce3e336f5b 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c +++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c @@ -337,7 +337,8 @@ static bool vop2_output_uv_swap(u32 bus_format, u32 output_mode) static bool vop2_output_rg_swap(struct vop2 *vop2, u32 bus_format) { - if (vop2->version == VOP_VERSION_RK3588) { + if (vop2->version == VOP_VERSION_RK3588 || + vop2->version == VOP_VERSION_RK3576) { if (bus_format == MEDIA_BUS_FMT_YUV8_1X24 || bus_format == MEDIA_BUS_FMT_YUV10_1X30) return true; @@ -351,6 +352,8 @@ static bool is_yuv_output(u32 bus_format) switch (bus_format) { case MEDIA_BUS_FMT_YUV8_1X24: case MEDIA_BUS_FMT_YUV10_1X30: + case MEDIA_BUS_FMT_YUYV10_1X20: + case MEDIA_BUS_FMT_UYVY10_1X20: case MEDIA_BUS_FMT_UYYVYY8_0_5X24: case MEDIA_BUS_FMT_UYYVYY10_0_5X30: case MEDIA_BUS_FMT_YUYV8_2X8: @@ -658,7 +661,7 @@ static int vop2_convert_csc_mode(int csc_mode) case V4L2_COLORSPACE_JPEG: return CSC_BT601F; case V4L2_COLORSPACE_BT2020: - return CSC_BT2020; + return CSC_BT2020L; default: return CSC_BT709L; } @@ -728,6 +731,89 @@ static void vop2_setup_csc_mode(struct vop2_video_port *vp, vop2_win_write(win, VOP2_WIN_CSC_MODE, csc_mode); } +/* + * RGB-to-YCbCr conversion based on color_to_ycbcr() and rgb2ycbcr() from + * drivers/media/common/v4l2-tpg/v4l2-tpg-core.c. + * + * Limited-range Y offset & chroma midpoint are expressed in 16-bit space. + */ +#define RGB2YUV_LIMITED_Y_OFFSET (16 << 8) +#define RGB2YUV_CHROMA_OFFSET (128 << 8) +#define COEFF(v, r) ((s32)(0.5 + (v) * (r) * 256.0)) + +struct rgb2yuv_matrix { + s32 y_r, y_g, y_b; + s32 cb_r, cb_g, cb_b; + s32 cr_r, cr_g, cr_b; + s32 y_offset; +}; + +/* BT.601 Limited range */ +static const struct rgb2yuv_matrix rgb2yuv_bt601l = { + .y_r = COEFF(0.299, 219), .y_g = COEFF(0.587, 219), .y_b = COEFF(0.114, 219), + .cb_r = COEFF(-0.1687, 224), .cb_g = COEFF(-0.3313, 224), .cb_b = COEFF(0.5, 224), + .cr_r = COEFF(0.5, 224), .cr_g = COEFF(-0.4187, 224), .cr_b = COEFF(-0.0813, 224), + .y_offset = RGB2YUV_LIMITED_Y_OFFSET, +}; + +/* BT.601 Full range */ +static const struct rgb2yuv_matrix rgb2yuv_bt601f = { + .y_r = COEFF(0.299, 255), .y_g = COEFF(0.587, 255), .y_b = COEFF(0.114, 255), + .cb_r = COEFF(-0.1687, 255), .cb_g = COEFF(-0.3313, 255), .cb_b = COEFF(0.5, 255), + .cr_r = COEFF(0.5, 255), .cr_g = COEFF(-0.4187, 255), .cr_b = COEFF(-0.0813, 255), + .y_offset = 0, +}; + +/* BT.709 Limited range */ +static const struct rgb2yuv_matrix rgb2yuv_bt709l = { + .y_r = COEFF(0.2126, 219), .y_g = COEFF(0.7152, 219), .y_b = COEFF(0.0722, 219), + .cb_r = COEFF(-0.1146, 224), .cb_g = COEFF(-0.3854, 224), .cb_b = COEFF(0.5, 224), + .cr_r = COEFF(0.5, 224), .cr_g = COEFF(-0.4542, 224), .cr_b = COEFF(-0.0458, 224), + .y_offset = RGB2YUV_LIMITED_Y_OFFSET, +}; + +/* BT.2020 Limited range */ +static const struct rgb2yuv_matrix rgb2yuv_bt2020l = { + .y_r = COEFF(0.2627, 219), .y_g = COEFF(0.6780, 219), .y_b = COEFF(0.0593, 219), + .cb_r = COEFF(-0.1396, 224), .cb_g = COEFF(-0.3604, 224), .cb_b = COEFF(0.5, 224), + .cr_r = COEFF(0.5, 224), .cr_g = COEFF(-0.4598, 224), .cr_b = COEFF(-0.0402, 224), + .y_offset = RGB2YUV_LIMITED_Y_OFFSET, +}; + +static const struct rgb2yuv_matrix * +vop2_rgb2yuv_get_matrix(enum vop_csc_format csc) +{ + switch (csc) { + case CSC_BT601L: + return &rgb2yuv_bt601l; + case CSC_BT601F: + return &rgb2yuv_bt601f; + case CSC_BT2020L: + return &rgb2yuv_bt2020l; + case CSC_BT709L: + default: + return &rgb2yuv_bt709l; + } +} + +/* Convert an RGB (16bpc) to YUV444 (16bpc). */ +static void vop2_rgb16_to_yuv16(int v4l2_cs, u16 r, u16 g, u16 b, + u16 *y, u16 *cb, u16 *cr) +{ + enum vop_csc_format csc = vop2_convert_csc_mode(v4l2_cs); + const struct rgb2yuv_matrix *m = vop2_rgb2yuv_get_matrix(csc); + s64 rs = r, gs = g, bs = b; + s64 ys, cbs, crs; + + ys = m->y_r * rs + m->y_g * gs + m->y_b * bs; + cbs = m->cb_r * rs + m->cb_g * gs + m->cb_b * bs; + crs = m->cr_r * rs + m->cr_g * gs + m->cr_b * bs; + + *y = (ys >> 16) + m->y_offset; + *cb = (cbs >> 16) + RGB2YUV_CHROMA_OFFSET; + *cr = (crs >> 16) + RGB2YUV_CHROMA_OFFSET; +} + static void vop2_crtc_enable_irq(struct vop2_video_port *vp, u32 irq) { struct vop2 *vop2 = vp->vop2; @@ -1554,12 +1640,58 @@ static void vop2_dither_setup(struct drm_crtc *crtc, u32 *dsp_ctrl) DITHER_DOWN_ALLEGRO); } -static void vop2_post_config(struct drm_crtc *crtc) +static void vop2_bgcolor_setup(struct drm_crtc *crtc, bool force, + struct drm_crtc_state *new_crtc_state, + struct drm_crtc_state *old_crtc_state) +{ + struct rockchip_crtc_state *new_vcstate = to_rockchip_crtc_state(new_crtc_state); + struct rockchip_crtc_state *old_vcstate = to_rockchip_crtc_state(old_crtc_state); + struct vop2_video_port *vp = to_vop2_video_port(crtc); + u64 bgcolor = new_crtc_state->background_color; + u16 y, cb, cr; + u32 val; + + if (!force && old_crtc_state->background_color == bgcolor && + old_vcstate->color_space == new_vcstate->color_space) + return; + + /* + * Background color is programmed with 10 bits of precision, using YUV + * format when operating in YUV overlay mode, and RGB otherwise. + */ + if (new_vcstate->yuv_overlay) { + vop2_rgb16_to_yuv16(new_vcstate->color_space, + DRM_ARGB64_GETR(bgcolor), + DRM_ARGB64_GETG(bgcolor), + DRM_ARGB64_GETB(bgcolor), + &y, &cb, &cr); + + val = FIELD_PREP(RK3568_VP_DSP_BG__DSP_BG_RED, cr >> 6); + FIELD_MODIFY(RK3568_VP_DSP_BG__DSP_BG_GREEN, &val, y >> 6); + FIELD_MODIFY(RK3568_VP_DSP_BG__DSP_BG_BLUE, &val, cb >> 6); + } else { + /* + * Since performance is more important than accuracy here, make + * use of the DRM_ARGB64_GET*_BPCS() helpers. + */ + val = FIELD_PREP(RK3568_VP_DSP_BG__DSP_BG_RED, + DRM_ARGB64_GETR_BPCS(bgcolor, 10)); + FIELD_MODIFY(RK3568_VP_DSP_BG__DSP_BG_GREEN, &val, + DRM_ARGB64_GETG_BPCS(bgcolor, 10)); + FIELD_MODIFY(RK3568_VP_DSP_BG__DSP_BG_BLUE, &val, + DRM_ARGB64_GETB_BPCS(bgcolor, 10)); + } + + vop2_vp_write(vp, RK3568_VP_DSP_BG, val); +} + +static void vop2_post_config(struct drm_crtc *crtc, bool force, + struct drm_crtc_state *new_crtc_state, + struct drm_crtc_state *old_crtc_state) { struct vop2_video_port *vp = to_vop2_video_port(crtc); struct vop2 *vop2 = vp->vop2; - struct drm_display_mode *mode = &crtc->state->adjusted_mode; - u64 bgcolor = crtc->state->background_color; + struct drm_display_mode *mode = &new_crtc_state->adjusted_mode; u16 vtotal = mode->crtc_vtotal; u16 hdisplay = mode->crtc_hdisplay; u16 hact_st = mode->crtc_htotal - mode->crtc_hsync_start; @@ -1605,15 +1737,7 @@ static void vop2_post_config(struct drm_crtc *crtc) vop2_vp_write(vp, RK3568_VP_POST_DSP_VACT_INFO_F1, val); } - /* - * Background color is programmed with 10 bits of precision. - * Since performance is more important than accuracy here, - * make use of the DRM_ARGB64_GET*_BPCS() helpers. - */ - val = FIELD_PREP(RK3568_VP_DSP_BG__DSP_BG_RED, DRM_ARGB64_GETR_BPCS(bgcolor, 10)); - FIELD_MODIFY(RK3568_VP_DSP_BG__DSP_BG_GREEN, &val, DRM_ARGB64_GETG_BPCS(bgcolor, 10)); - FIELD_MODIFY(RK3568_VP_DSP_BG__DSP_BG_BLUE, &val, DRM_ARGB64_GETB_BPCS(bgcolor, 10)); - vop2_vp_write(vp, RK3568_VP_DSP_BG, val); + vop2_bgcolor_setup(crtc, force, new_crtc_state, old_crtc_state); } static int us_to_vertical_line(struct drm_display_mode *mode, int us) @@ -1628,8 +1752,9 @@ static void vop2_crtc_atomic_enable(struct drm_crtc *crtc, struct vop2 *vop2 = vp->vop2; const struct vop2_data *vop2_data = vop2->data; const struct vop2_video_port_data *vp_data = &vop2_data->vp[vp->id]; + struct drm_crtc_state *old_crtc_state = drm_atomic_get_old_crtc_state(state, crtc); struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state, crtc); - struct rockchip_crtc_state *vcstate = to_rockchip_crtc_state(crtc->state); + struct rockchip_crtc_state *vcstate = to_rockchip_crtc_state(crtc_state); struct drm_display_mode *mode = &crtc->state->adjusted_mode; unsigned long clock = mode->crtc_clock * 1000; u16 hsync_len = mode->crtc_hsync_end - mode->crtc_hsync_start; @@ -1699,6 +1824,22 @@ static void vop2_crtc_atomic_enable(struct drm_crtc *crtc, if (vcstate->output_mode == ROCKCHIP_OUT_MODE_AAAA && !(vp_data->feature & VOP2_VP_FEATURE_OUTPUT_10BIT)) out_mode = ROCKCHIP_OUT_MODE_P888; + else if (vcstate->output_mode == ROCKCHIP_OUT_MODE_YUV422 && + vop2->version == VOP_VERSION_RK3576) + switch (vcstate->output_type) { + case DRM_MODE_CONNECTOR_DisplayPort: + case DRM_MODE_CONNECTOR_eDP: + out_mode = ROCKCHIP_OUT_MODE_YUV422_RK3576_DP; + break; + case DRM_MODE_CONNECTOR_HDMIA: + out_mode = ROCKCHIP_OUT_MODE_YUV422_RK3576_HDMI; + break; + default: + drm_err(vop2->drm, "Unknown DRM_MODE_CONNECTOR %d\n", + vcstate->output_type); + vop2_unlock(vop2); + return; + } else out_mode = vcstate->output_mode; @@ -1799,7 +1940,7 @@ static void vop2_crtc_atomic_enable(struct drm_crtc *crtc, clk_set_rate(vp->dclk, clock); - vop2_post_config(crtc); + vop2_post_config(crtc, true, crtc_state, old_crtc_state); vop2_cfg_done(vp); @@ -1874,6 +2015,7 @@ static void vop2_crtc_atomic_flush(struct drm_crtc *crtc, struct drm_atomic_commit *state) { struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state, crtc); + struct drm_crtc_state *old_crtc_state = drm_atomic_get_old_crtc_state(state, crtc); struct vop2_video_port *vp = to_vop2_video_port(crtc); struct vop2 *vop2 = vp->vop2; @@ -1881,7 +2023,7 @@ static void vop2_crtc_atomic_flush(struct drm_crtc *crtc, if (!drm_atomic_crtc_needs_modeset(crtc_state) && crtc_state->color_mgmt_changed) vop2_crtc_atomic_try_set_gamma_locked(vop2, vp, crtc, crtc_state); - vop2_post_config(crtc); + vop2_post_config(crtc, false, crtc_state, old_crtc_state); vop2_cfg_done(vp); diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop2.h b/drivers/gpu/drm/rockchip/rockchip_drm_vop2.h index 37722652844a..ffcb39c130aa 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_vop2.h +++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop2.h @@ -373,7 +373,7 @@ enum vop_csc_format { CSC_BT601L, CSC_BT709L, CSC_BT601F, - CSC_BT2020, + CSC_BT2020L, }; enum src_factor_mode { diff --git a/drivers/gpu/drm/rockchip/rockchip_lvds.c b/drivers/gpu/drm/rockchip/rockchip_lvds.c index 7a0c4fa29f2f..31dc206bedeb 100644 --- a/drivers/gpu/drm/rockchip/rockchip_lvds.c +++ b/drivers/gpu/drm/rockchip/rockchip_lvds.c @@ -119,7 +119,7 @@ static const struct drm_bridge_funcs rockchip_lvds_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_create_state = drm_atomic_helper_bridge_create_state, .get_modes = rockchip_lvds_bridge_get_modes, }; diff --git a/drivers/gpu/drm/rockchip/rockchip_vop2_reg.c b/drivers/gpu/drm/rockchip/rockchip_vop2_reg.c index 17eda592b183..c44648e1bc0d 100644 --- a/drivers/gpu/drm/rockchip/rockchip_vop2_reg.c +++ b/drivers/gpu/drm/rockchip/rockchip_vop2_reg.c @@ -2114,7 +2114,7 @@ static u32 rk3568_vop2_read_layer_cfg(struct vop2 *vop2) return vop2_readl(vop2, RK3568_OVL_LAYER_SEL); } -static void rk3568_vop2_wait_for_layer_cfg_done(struct vop2 *vop2, u32 cfg) +static void rk3568_vop2_wait_for_layer_cfg_done(struct vop2 *vop2) { u32 atv_layer_cfg; int ret; @@ -2123,21 +2123,19 @@ static void rk3568_vop2_wait_for_layer_cfg_done(struct vop2 *vop2, u32 cfg) * Spin until the previous layer configuration is done. */ ret = readx_poll_timeout_atomic(rk3568_vop2_read_layer_cfg, vop2, atv_layer_cfg, - atv_layer_cfg == cfg, 10, 50 * 1000); + atv_layer_cfg == vop2->old_layer_sel, 10, 50 * 1000); if (ret) drm_err_ratelimited(vop2->drm, "wait layer cfg done timeout: 0x%x--0x%x\n", - atv_layer_cfg, cfg); + atv_layer_cfg, vop2->old_layer_sel); } static void rk3568_vop2_setup_layer_mixer(struct vop2_video_port *vp) { struct vop2 *vop2 = vp->vop2; struct drm_plane *plane; - u32 layer_sel = 0; + u32 layer_sel; u32 port_sel; - u32 old_layer_sel = 0; - u32 atv_layer_sel = 0; - u32 old_port_sel = 0; + u32 atv_layer_sel; u8 layer_id; u8 old_layer_id; u8 layer_sel_id; @@ -2160,8 +2158,7 @@ static void rk3568_vop2_setup_layer_mixer(struct vop2_video_port *vp) else ovl_ctrl &= ~RK3568_OVL_CTRL__YUV_MODE(vp->id); - old_port_sel = vop2->old_port_sel; - port_sel = old_port_sel; + port_sel = vop2->old_port_sel; port_sel &= RK3568_OVL_PORT_SEL__SEL_PORT; if (vp0->nlayers) @@ -2187,8 +2184,7 @@ static void rk3568_vop2_setup_layer_mixer(struct vop2_video_port *vp) port_sel |= FIELD_PREP(RK3588_OVL_PORT_SET__PORT3_MUX, 7); atv_layer_sel = vop2_readl(vop2, RK3568_OVL_LAYER_SEL); - old_layer_sel = vop2->old_layer_sel; - layer_sel = old_layer_sel; + layer_sel = vop2->old_layer_sel; ofs = 0; for (i = 0; i < vp->id; i++) @@ -2272,8 +2268,6 @@ static void rk3568_vop2_setup_layer_mixer(struct vop2_video_port *vp) old_win->data->layer_sel_id[vp->id]); } - vop2->old_layer_sel = layer_sel; - vop2->old_port_sel = port_sel; /* * As the RK3568_OVL_LAYER_SEL and RK3568_OVL_PORT_SEL are shared by all Video Ports, * and the configuration take effect by one Video Port's vsync. @@ -2288,17 +2282,8 @@ static void rk3568_vop2_setup_layer_mixer(struct vop2_video_port *vp) * lead to the configuration of the previous VP being take effect along with the VSYNC * of the new VP. */ - if (layer_sel != old_layer_sel || port_sel != old_port_sel) - ovl_ctrl |= FIELD_PREP(RK3568_OVL_CTRL__LAYERSEL_REGDONE_SEL, vp->id); - vop2_writel(vop2, RK3568_OVL_CTRL, ovl_ctrl); - if (port_sel != old_port_sel) { - vop2_writel(vop2, RK3568_OVL_PORT_SEL, port_sel); - vop2_cfg_done(vp); - rk3568_vop2_wait_for_port_mux_done(vop2); - } - - if (layer_sel != old_layer_sel && atv_layer_sel != old_layer_sel) { + if (layer_sel != vop2->old_layer_sel && atv_layer_sel != vop2->old_layer_sel) { cfg_done = vop2_readl(vop2, RK3568_REG_CFG_DONE); cfg_done &= (BIT(vop2->data->nr_vps) - 1); cfg_done &= ~BIT(vp->id); @@ -2306,10 +2291,23 @@ static void rk3568_vop2_setup_layer_mixer(struct vop2_video_port *vp) * Changes of other VPs' overlays have not taken effect */ if (cfg_done) - rk3568_vop2_wait_for_layer_cfg_done(vop2, vop2->old_layer_sel); + rk3568_vop2_wait_for_layer_cfg_done(vop2); + } + + if (layer_sel != vop2->old_layer_sel || port_sel != vop2->old_port_sel) + ovl_ctrl |= FIELD_PREP(RK3568_OVL_CTRL__LAYERSEL_REGDONE_SEL, vp->id); + vop2_writel(vop2, RK3568_OVL_CTRL, ovl_ctrl); + + if (port_sel != vop2->old_port_sel) { + vop2->old_port_sel = port_sel; + vop2_writel(vop2, RK3568_OVL_PORT_SEL, port_sel); + vop2_cfg_done(vp); + rk3568_vop2_wait_for_port_mux_done(vop2); } + vop2->old_layer_sel = layer_sel; vop2_writel(vop2, RK3568_OVL_LAYER_SEL, layer_sel); + mutex_unlock(&vop2->ovl_lock); } diff --git a/drivers/gpu/drm/scheduler/sched_entity.c b/drivers/gpu/drm/scheduler/sched_entity.c index 4ebb513255ed..c51101ec70c1 100644 --- a/drivers/gpu/drm/scheduler/sched_entity.c +++ b/drivers/gpu/drm/scheduler/sched_entity.c @@ -129,7 +129,6 @@ int drm_sched_entity_init(struct drm_sched_entity *entity, return -ENOMEM; INIT_LIST_HEAD(&entity->list); - entity->rq = NULL; entity->guilty = guilty; entity->priority = priority; entity->last_user = current->group_leader; @@ -280,9 +279,6 @@ void drm_sched_entity_kill(struct drm_sched_entity *entity) struct drm_sched_job *job; struct dma_fence *prev; - if (!entity->rq) - return; - spin_lock(&entity->lock); entity->stopped = true; drm_sched_rq_remove_entity(entity->rq, entity); @@ -329,14 +325,11 @@ EXPORT_SYMBOL(drm_sched_entity_kill); */ long drm_sched_entity_flush(struct drm_sched_entity *entity, long timeout) { - struct drm_gpu_scheduler *sched; + struct drm_gpu_scheduler *sched = + container_of(entity->rq, typeof(*sched), rq); struct task_struct *last_user; long ret = timeout; - if (!entity->rq) - return 0; - - sched = container_of(entity->rq, typeof(*sched), rq); /* * The client will not queue more jobs during this fini - consume * existing queued ones, or discard them on SIGKILL. diff --git a/drivers/gpu/drm/scheduler/sched_main.c b/drivers/gpu/drm/scheduler/sched_main.c index 818d3d4434b5..d2ca01b31ee4 100644 --- a/drivers/gpu/drm/scheduler/sched_main.c +++ b/drivers/gpu/drm/scheduler/sched_main.c @@ -588,15 +588,6 @@ int drm_sched_job_init(struct drm_sched_job *job, u32 credits, void *owner, uint64_t drm_client_id) { - if (!entity->rq) { - /* This will most likely be followed by missing frames - * or worse--a blank screen--leave a trail in the - * logs, so this can be debugged easier. - */ - dev_err(job->sched->dev, "%s: entity has no rq!\n", __func__); - return -ENOENT; - } - if (unlikely(!credits)) { pr_err("*ERROR* %s: credits cannot be 0!\n", __func__); return -EINVAL; diff --git a/drivers/gpu/drm/scheduler/tests/tests_basic.c b/drivers/gpu/drm/scheduler/tests/tests_basic.c index a5a5a35a87b0..da5c4952780f 100644 --- a/drivers/gpu/drm/scheduler/tests/tests_basic.c +++ b/drivers/gpu/drm/scheduler/tests/tests_basic.c @@ -1,7 +1,11 @@ // SPDX-License-Identifier: GPL-2.0 /* Copyright (c) 2025 Valve Corporation */ +#include <linux/completion.h> #include <linux/delay.h> +#include <linux/minmax.h> +#include <linux/time.h> +#include <linux/workqueue.h> #include "sched_tests.h" @@ -235,6 +239,360 @@ static void drm_sched_basic_cancel(struct kunit *test) KUNIT_ASSERT_EQ(test, job->hw_fence.error, -ECANCELED); } +struct sched_concurrent_context { + struct drm_mock_scheduler *sched; + struct workqueue_struct *sub_wq; + struct kunit *test; + struct completion wait_go; +}; + +KUNIT_DEFINE_ACTION_WRAPPER(drm_mock_sched_fini_wrap, drm_mock_sched_fini, + struct drm_mock_scheduler *); + +KUNIT_DEFINE_ACTION_WRAPPER(drm_mock_sched_entity_free_wrap, drm_mock_sched_entity_free, + struct drm_mock_sched_entity *); + +static void complete_destroy_workqueue(void *context) +{ + struct sched_concurrent_context *ctx = context; + + complete_all(&ctx->wait_go); + + destroy_workqueue(ctx->sub_wq); +} + +static int drm_sched_concurrent_init(struct kunit *test) +{ + struct sched_concurrent_context *ctx; + int ret; + + ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, ctx); + + init_completion(&ctx->wait_go); + + ctx->sched = drm_mock_sched_new(test, MAX_SCHEDULE_TIMEOUT); + + ret = kunit_add_action_or_reset(test, drm_mock_sched_fini_wrap, ctx->sched); + KUNIT_ASSERT_EQ(test, ret, 0); + + /* Use an unbounded workqueue to maximize job submission concurrency */ + ctx->sub_wq = alloc_workqueue("drm-sched-submitters-wq", WQ_UNBOUND, + WQ_UNBOUND_MAX_ACTIVE); + KUNIT_ASSERT_NOT_NULL(test, ctx->sub_wq); + + ret = kunit_add_action_or_reset(test, complete_destroy_workqueue, ctx); + KUNIT_ASSERT_EQ(test, ret, 0); + + ctx->test = test; + test->priv = ctx; + + return 0; +} + +struct drm_sched_parallel_params { + const char *description; + unsigned int num_jobs; + unsigned int num_workers; +}; + +static const struct drm_sched_parallel_params drm_sched_parallel_cases[] = { + { + .description = "Parallel submission of multiple jobs per worker", + .num_jobs = 8, + .num_workers = 16, + }, +}; + +static void +drm_sched_parallel_desc(const struct drm_sched_parallel_params *params, char *desc) +{ + strscpy(desc, params->description, KUNIT_PARAM_DESC_SIZE); +} + +KUNIT_ARRAY_PARAM(drm_sched_parallel, drm_sched_parallel_cases, drm_sched_parallel_desc); + +struct parallel_worker { + struct work_struct work; + struct sched_concurrent_context *ctx; + struct drm_mock_sched_entity *entity; + struct drm_mock_sched_job **jobs; + unsigned int id; +}; + +static void drm_sched_parallel_worker(struct work_struct *work) +{ + const struct drm_sched_parallel_params *params; + struct sched_concurrent_context *test_ctx; + struct parallel_worker *worker; + unsigned int i; + + worker = container_of(work, struct parallel_worker, work); + test_ctx = worker->ctx; + params = test_ctx->test->param_value; + + wait_for_completion(&test_ctx->wait_go); + + kunit_info(test_ctx->test, "Parallel worker %u submitting %u jobs started\n", + worker->id, params->num_jobs); + + for (i = 0; i < params->num_jobs; i++) + drm_mock_sched_job_submit(worker->jobs[i]); +} + +/* + * Spawns workers that submit a sequence of jobs to the mock scheduler. + * Once all jobs are submitted, the timeline is manually advanced. + */ +static void drm_sched_parallel_submit_test(struct kunit *test) +{ + struct sched_concurrent_context *ctx = test->priv; + const struct drm_sched_parallel_params *params = test->param_value; + struct parallel_worker *workers, *worker; + struct drm_mock_sched_job *job; + unsigned int i, j, completed_jobs, total_jobs; + bool done; + int ret; + + KUNIT_ASSERT_GT(test, params->num_workers, 0); + KUNIT_ASSERT_GT(test, params->num_jobs, 0); + + workers = kunit_kcalloc(test, params->num_workers, sizeof(*workers), + GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, workers); + + /* + * Init workers only after all jobs and entities have been successfully + * allocated. In this way, the cleanup logic for when an assertion fail + * can be simplified. + */ + for (i = 0; i < params->num_workers; i++) { + worker = &workers[i]; + worker->id = i; + worker->ctx = ctx; + worker->entity = drm_mock_sched_entity_new(test, + DRM_SCHED_PRIORITY_NORMAL, + ctx->sched); + + worker->jobs = kunit_kcalloc(test, params->num_jobs, + sizeof(*worker->jobs), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, worker->jobs); + + for (j = 0; j < params->num_jobs; j++) { + job = drm_mock_sched_job_new(test, worker->entity); + worker->jobs[j] = job; + } + + ret = kunit_add_action_or_reset(test, drm_mock_sched_entity_free_wrap, + worker->entity); + KUNIT_ASSERT_EQ(test, ret, 0); + } + + for (i = 0; i < params->num_workers; i++) { + worker = &workers[i]; + INIT_WORK(&worker->work, drm_sched_parallel_worker); + queue_work(ctx->sub_wq, &worker->work); + } + + complete_all(&ctx->wait_go); + flush_workqueue(ctx->sub_wq); + + for (i = 0; i < params->num_workers; i++) { + worker = &workers[i]; + for (j = 0; j < params->num_jobs; j++) { + job = worker->jobs[j]; + done = drm_mock_sched_job_wait_scheduled(job, HZ); + KUNIT_EXPECT_TRUE(test, done); + } + } + + total_jobs = params->num_workers * params->num_jobs; + completed_jobs = drm_mock_sched_advance(ctx->sched, total_jobs); + KUNIT_EXPECT_EQ(test, completed_jobs, total_jobs); + + for (i = 0; i < params->num_workers; i++) { + worker = &workers[i]; + for (j = 0; j < params->num_jobs; j++) { + job = worker->jobs[j]; + done = drm_mock_sched_job_wait_finished(job, HZ); + KUNIT_EXPECT_TRUE(test, done); + } + } +} + +struct drm_sched_interleaved_params { + const char *description; + unsigned int test_duration_ms; + unsigned int job_base_duration_us; + unsigned int num_workers; + unsigned int num_in_flight_jobs; +}; + +static const struct drm_sched_interleaved_params drm_sched_interleaved_cases[] = { + { + .description = "Interleaved submission of multiple jobs per worker", + .test_duration_ms = 1000, + .job_base_duration_us = 100, + .num_workers = 16, + .num_in_flight_jobs = 8, + }, +}; + +static void +drm_sched_interleaved_desc(const struct drm_sched_interleaved_params *params, char *desc) +{ + strscpy(desc, params->description, KUNIT_PARAM_DESC_SIZE); +} + +KUNIT_ARRAY_PARAM(drm_sched_interleaved, drm_sched_interleaved_cases, + drm_sched_interleaved_desc); + +struct interleaved_worker { + struct work_struct work; + struct sched_concurrent_context *ctx; + struct drm_mock_sched_entity *entity; + struct drm_mock_sched_job **jobs; + unsigned int id; + unsigned int job_count; + unsigned int job_duration_us; +}; + +static void drm_sched_interleaved_worker(struct work_struct *work) +{ + struct sched_concurrent_context *test_ctx; + const struct drm_sched_interleaved_params *params; + struct interleaved_worker *worker; + unsigned int i, j, max_in_flight_job; + unsigned long timeout; + bool done; + + worker = container_of(work, struct interleaved_worker, work); + test_ctx = worker->ctx; + params = test_ctx->test->param_value; + + wait_for_completion(&test_ctx->wait_go); + + kunit_info(test_ctx->test, "Worker %u submitting %u jobs of %u us started\n", + worker->id, worker->job_count, worker->job_duration_us); + + timeout = msecs_to_jiffies(params->test_duration_ms * 2); + + /* Fill the submission window */ + max_in_flight_job = min(worker->job_count, params->num_in_flight_jobs); + for (i = 0; i < max_in_flight_job; i++) + drm_mock_sched_job_submit(worker->jobs[i]); + + /* Keep the window full by submitting a new job at once until done */ + for (i = 0; i < worker->job_count; i++) { + done = drm_mock_sched_job_wait_finished(worker->jobs[i], timeout); + if (!done) + kunit_info(test_ctx->test, "Job %u of worker %u timed out\n", + i, worker->id); + + j = i + max_in_flight_job; + if (j < worker->job_count) + drm_mock_sched_job_submit(worker->jobs[j]); + } +} + +/* + * Spawns workers that submit a sequence of jobs to the mock scheduler. Job + * durations are chosen as multiples of a base duration value specified as + * a test parameter. Since the scheduler serializes jobs from all workers, + * the total test duration budget is divided into equal shares among workers. + * These shares are then used to compute the number of jobs that each worker + * can submit. + */ +static void drm_sched_interleaved_submit_test(struct kunit *test) +{ + const struct drm_sched_interleaved_params *params = test->param_value; + struct sched_concurrent_context *ctx = test->priv; + struct interleaved_worker *workers, *worker; + struct drm_mock_sched_job *job; + unsigned int worker_share_us; + unsigned int i, j; + bool done; + int ret; + + KUNIT_ASSERT_GT(test, params->num_workers, 0); + KUNIT_ASSERT_GT(test, params->job_base_duration_us, 0); + + workers = kunit_kcalloc(test, params->num_workers, sizeof(*workers), + GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, workers); + + /* Divide the available test time into equal shares among the workers */ + worker_share_us = (params->test_duration_ms * USEC_PER_MSEC) / + params->num_workers; + + /* + * Init workers only after all jobs and entities have been successfully + * allocated. In this way, the cleanup logic for when an assertion fails + * can be simplified. + */ + for (i = 0; i < params->num_workers; i++) { + worker = &workers[i]; + worker->id = i; + worker->ctx = ctx; + + worker->job_duration_us = params->job_base_duration_us * (i + 1); + worker->job_count = worker_share_us / worker->job_duration_us; + worker->job_count = max(1U, worker->job_count); + + worker->entity = drm_mock_sched_entity_new(test, + DRM_SCHED_PRIORITY_NORMAL, + ctx->sched); + + worker->jobs = kunit_kcalloc(test, worker->job_count, + sizeof(*worker->jobs), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, worker->jobs); + + for (j = 0; j < worker->job_count; j++) { + job = drm_mock_sched_job_new(test, worker->entity); + drm_mock_sched_job_set_duration_us(job, worker->job_duration_us); + + worker->jobs[j] = job; + } + + ret = kunit_add_action_or_reset(test, drm_mock_sched_entity_free_wrap, + worker->entity); + KUNIT_ASSERT_EQ(test, ret, 0); + } + + for (i = 0; i < params->num_workers; i++) { + worker = &workers[i]; + INIT_WORK(&worker->work, drm_sched_interleaved_worker); + queue_work(ctx->sub_wq, &worker->work); + } + + complete_all(&ctx->wait_go); + flush_workqueue(ctx->sub_wq); + + for (i = 0; i < params->num_workers; i++) { + worker = &workers[i]; + for (j = 0; j < worker->job_count; j++) { + job = worker->jobs[j]; + done = drm_mock_sched_job_is_finished(job); + KUNIT_EXPECT_TRUE(test, done); + } + } +} + +static struct kunit_case drm_sched_concurrent_tests[] = { + KUNIT_CASE_PARAM(drm_sched_parallel_submit_test, drm_sched_parallel_gen_params), + KUNIT_CASE_PARAM(drm_sched_interleaved_submit_test, drm_sched_interleaved_gen_params), + {} +}; + +static struct kunit_suite drm_sched_concurrent = { + .name = "drm_sched_concurrent_tests", + .init = drm_sched_concurrent_init, + .test_cases = drm_sched_concurrent_tests, + .attr = { + .speed = KUNIT_SPEED_SLOW, + }, +}; + static struct kunit_case drm_sched_cancel_tests[] = { KUNIT_CASE(drm_sched_basic_cancel), {} @@ -556,6 +914,7 @@ static struct kunit_suite drm_sched_credits = { }; kunit_test_suites(&drm_sched_basic, + &drm_sched_concurrent, &drm_sched_timeout, &drm_sched_cancel, &drm_sched_priority, diff --git a/drivers/gpu/drm/scheduler/tests/tests_scheduler.c b/drivers/gpu/drm/scheduler/tests/tests_scheduler.c index 90d31888cf92..285546a2218f 100644 --- a/drivers/gpu/drm/scheduler/tests/tests_scheduler.c +++ b/drivers/gpu/drm/scheduler/tests/tests_scheduler.c @@ -599,7 +599,7 @@ static void drm_sched_scheduler_two_clients_test(struct kunit *test) * - qd: Number of outstanding jobs in the client/entity */ - pr_info(" [pct] - Job sumission progress\n" + pr_info(" [pct] - Job submission progress\n" " [cps] - Cycles per second\n" " [qd] - Number of outstanding jobs in the client/entity\n"); pr_info("%s:\n\t pct1 cps1 qd1; pct2 cps2 qd2\n", diff --git a/drivers/gpu/drm/solomon/ssd130x.c b/drivers/gpu/drm/solomon/ssd130x.c index cae92a3ae8a4..04da4f2f7d08 100644 --- a/drivers/gpu/drm/solomon/ssd130x.c +++ b/drivers/gpu/drm/solomon/ssd130x.c @@ -276,6 +276,49 @@ out_end: return ret; } +/* + * Write a command byte sequence from a buffer. + * + * Like ssd130x_write_cmd() but takes a pre-built byte array instead of + * variadic arguments, handy when the command is already in an array or + * when the caller wants to use sizeof() for the length. + */ +static int ssd130x_write_cmds(struct ssd130x_device *ssd130x, const u8 *cmd, + size_t len) +{ + unsigned int i; + int ret; + + for (i = 0; i < len; i++) { + ret = regmap_write(ssd130x->regmap, SSD13XX_COMMAND, cmd[i]); + if (ret) + return ret; + } + + return 0; +} + +/* + * Run a packed command sequence. The format is a flat byte array where each + * entry starts with a length byte followed by that many command bytes. A + * zero length byte terminates the sequence. + * + * Example: { 2, 0x81, 0x80, 1, 0xAF, 0 } + * sends command {0x81, 0x80}, then command {0xAF}, then stops. + */ +static int ssd130x_run_cmd_seq(struct ssd130x_device *ssd130x, const u8 *seq) +{ + while (*seq) { + u8 len = *seq++; + int ret = ssd130x_write_cmds(ssd130x, seq, len); + + if (ret) + return ret; + seq += len; + } + + return 0; +} /* Set address range for horizontal/vertical addressing modes */ static int ssd130x_set_col_range(struct ssd130x_device *ssd130x, u8 col_start, u8 cols) @@ -408,39 +451,26 @@ static int ssd130x_init(struct ssd130x_device *ssd130x) bool scan_mode; int ret; - /* Set initial contrast */ - ret = ssd130x_write_cmd(ssd130x, 2, SSD13XX_CONTRAST, ssd130x->contrast); - if (ret < 0) - return ret; - /* Set segment re-map */ seg_remap = (SSD13XX_SET_SEG_REMAP | SSD13XX_SET_SEG_REMAP_SET(ssd130x->seg_remap)); - ret = ssd130x_write_cmd(ssd130x, 1, seg_remap); - if (ret < 0) - return ret; - /* Set COM direction */ com_invdir = (SSD130X_SET_COM_SCAN_DIR | SSD130X_SET_COM_SCAN_DIR_SET(ssd130x->com_invdir)); - ret = ssd130x_write_cmd(ssd130x, 1, com_invdir); - if (ret < 0) - return ret; - - /* Set multiplex ratio value */ - ret = ssd130x_write_cmd(ssd130x, 2, SSD13XX_SET_MULTIPLEX_RATIO, ssd130x->height - 1); - if (ret < 0) - return ret; - - /* set display offset value */ - ret = ssd130x_write_cmd(ssd130x, 2, SSD130X_SET_DISPLAY_OFFSET, ssd130x->com_offset); - if (ret < 0) - return ret; - /* Set clock frequency */ dclk = (SSD130X_SET_CLOCK_DIV_SET(ssd130x->dclk_div - 1) | SSD130X_SET_CLOCK_FREQ_SET(ssd130x->dclk_frq)); - ret = ssd130x_write_cmd(ssd130x, 2, SSD130X_SET_CLOCK_FREQ, dclk); + + const u8 *cmds = (const u8[]) { + 2, SSD13XX_CONTRAST, ssd130x->contrast, + 1, seg_remap, + 1, com_invdir, 0, + 2, SSD13XX_SET_MULTIPLEX_RATIO, ssd130x->height - 1, + 2, SSD130X_SET_DISPLAY_OFFSET, ssd130x->com_offset, + 2, SSD130X_SET_CLOCK_FREQ, dclk, + 0, + }; + ret = ssd130x_run_cmd_seq(ssd130x, cmds); if (ret < 0) return ret; @@ -462,9 +492,6 @@ static int ssd130x_init(struct ssd130x_device *ssd130x) /* Set precharge period in number of ticks from the internal clock */ precharge = (SSD130X_SET_PRECHARGE_PERIOD1_SET(ssd130x->prechargep1) | SSD130X_SET_PRECHARGE_PERIOD2_SET(ssd130x->prechargep2)); - ret = ssd130x_write_cmd(ssd130x, 2, SSD130X_SET_PRECHARGE_PERIOD, precharge); - if (ret < 0) - return ret; /* Set COM pins configuration */ compins = BIT(1); @@ -476,22 +503,20 @@ static int ssd130x_init(struct ssd130x_device *ssd130x) scan_mode = !ssd130x->com_seq; compins |= (SSD130X_SET_COM_PINS_CONFIG1_SET(scan_mode) | SSD130X_SET_COM_PINS_CONFIG2_SET(ssd130x->com_lrremap)); - ret = ssd130x_write_cmd(ssd130x, 2, SSD130X_SET_COM_PINS_CONFIG, compins); - if (ret < 0) - return ret; - - /* Set VCOMH */ - ret = ssd130x_write_cmd(ssd130x, 2, SSD130X_SET_VCOMH, ssd130x->vcomh); - if (ret < 0) - return ret; /* Turn on the DC-DC Charge Pump */ chargepump = BIT(4); - if (ssd130x->device_info->need_chargepump) chargepump |= BIT(2); - ret = ssd130x_write_cmd(ssd130x, 2, SSD130X_CHARGE_PUMP, chargepump); + cmds = (const u8[]) { + 2, SSD130X_SET_PRECHARGE_PERIOD, precharge, + 2, SSD130X_SET_COM_PINS_CONFIG, compins, + 2, SSD130X_SET_VCOMH, ssd130x->vcomh, + 2, SSD130X_CHARGE_PUMP, chargepump, + 0 + }; + ret = ssd130x_run_cmd_seq(ssd130x, cmds); if (ret < 0) return ret; @@ -528,203 +553,68 @@ static int ssd130x_init(struct ssd130x_device *ssd130x) static int ssd132x_init(struct ssd130x_device *ssd130x) { - int ret; - - /* Set initial contrast */ - ret = ssd130x_write_cmd(ssd130x, 2, SSD13XX_CONTRAST, 0x80); - if (ret < 0) - return ret; - - /* Set column start and end */ - ret = ssd130x_write_cmd(ssd130x, 3, SSD132X_SET_COL_RANGE, 0x00, - ssd130x->width / SSD132X_SEGMENT_WIDTH - 1); - if (ret < 0) - return ret; - - /* Set row start and end */ - ret = ssd130x_write_cmd(ssd130x, 3, SSD132X_SET_ROW_RANGE, 0x00, ssd130x->height - 1); - if (ret < 0) - return ret; - /* - * Horizontal Address Increment - * Re-map for Column Address, Nibble and COM - * COM Split Odd Even - */ - ret = ssd130x_write_cmd(ssd130x, 2, SSD13XX_SET_SEG_REMAP, 0x53); - if (ret < 0) - return ret; - - /* Set display start and offset */ - ret = ssd130x_write_cmd(ssd130x, 2, SSD132X_SET_DISPLAY_START, 0x00); - if (ret < 0) - return ret; - - ret = ssd130x_write_cmd(ssd130x, 2, SSD132X_SET_DISPLAY_OFFSET, 0x00); - if (ret < 0) - return ret; - - /* Set display mode normal */ - ret = ssd130x_write_cmd(ssd130x, 1, SSD132X_SET_DISPLAY_NORMAL); - if (ret < 0) - return ret; - - /* Set multiplex ratio value */ - ret = ssd130x_write_cmd(ssd130x, 2, SSD13XX_SET_MULTIPLEX_RATIO, ssd130x->height - 1); - if (ret < 0) - return ret; - - /* Set phase length */ - ret = ssd130x_write_cmd(ssd130x, 2, SSD132X_SET_PHASE_LENGTH, 0x55); - if (ret < 0) - return ret; - - /* Select default linear gray scale table */ - ret = ssd130x_write_cmd(ssd130x, 1, SSD132X_SELECT_DEFAULT_TABLE); - if (ret < 0) - return ret; - - /* Set clock frequency */ - ret = ssd130x_write_cmd(ssd130x, 2, SSD132X_SET_CLOCK_FREQ, 0x01); - if (ret < 0) - return ret; - - /* Enable internal VDD regulator */ - ret = ssd130x_write_cmd(ssd130x, 2, SSD132X_SET_FUNCTION_SELECT_A, 0x1); - if (ret < 0) - return ret; - - /* Set pre-charge period */ - ret = ssd130x_write_cmd(ssd130x, 2, SSD132X_SET_PRECHARGE_PERIOD, 0x01); - if (ret < 0) - return ret; - - /* Set pre-charge voltage */ - ret = ssd130x_write_cmd(ssd130x, 2, SSD132X_SET_PRECHARGE_VOLTAGE, 0x08); - if (ret < 0) - return ret; - - /* Set VCOMH voltage */ - ret = ssd130x_write_cmd(ssd130x, 2, SSD130X_SET_VCOMH_VOLTAGE, 0x07); - if (ret < 0) - return ret; - - /* Enable second pre-charge and internal VSL */ - ret = ssd130x_write_cmd(ssd130x, 2, SSD132X_SET_FUNCTION_SELECT_B, 0x62); - if (ret < 0) - return ret; - - return 0; + const u8 cmds[] = { + 2, SSD13XX_CONTRAST, 0x80, + 3, SSD132X_SET_COL_RANGE, 0x00, ssd130x->width / SSD132X_SEGMENT_WIDTH - 1, + 3, SSD132X_SET_ROW_RANGE, 0x00, ssd130x->height - 1, + /* + * Horizontal Address Increment + * Re-map for Column Address, Nibble and COM + * COM Split Odd Even + */ + 2, SSD13XX_SET_SEG_REMAP, 0x53, + 2, SSD132X_SET_DISPLAY_START, 0x00, + 2, SSD132X_SET_DISPLAY_OFFSET, 0x00, + 1, SSD132X_SET_DISPLAY_NORMAL, + 2, SSD13XX_SET_MULTIPLEX_RATIO, ssd130x->height - 1, + 2, SSD132X_SET_PHASE_LENGTH, 0x55, + 1, SSD132X_SELECT_DEFAULT_TABLE, + 2, SSD132X_SET_CLOCK_FREQ, 0x01, + 2, SSD132X_SET_FUNCTION_SELECT_A, 0x1, + 2, SSD132X_SET_PRECHARGE_PERIOD, 0x01, + 2, SSD132X_SET_PRECHARGE_VOLTAGE, 0x08, + 2, SSD130X_SET_VCOMH_VOLTAGE, 0x07, + /* Enable second pre-charge and internal VSL */ + 2, SSD132X_SET_FUNCTION_SELECT_B, 0x62, + 0, + }; + + return ssd130x_run_cmd_seq(ssd130x, cmds); } static int ssd133x_init(struct ssd130x_device *ssd130x) { - int ret; - - /* Set color A contrast */ - ret = ssd130x_write_cmd(ssd130x, 2, SSD133X_CONTRAST_A, 0x91); - if (ret < 0) - return ret; - - /* Set color B contrast */ - ret = ssd130x_write_cmd(ssd130x, 2, SSD133X_CONTRAST_B, 0x50); - if (ret < 0) - return ret; - - /* Set color C contrast */ - ret = ssd130x_write_cmd(ssd130x, 2, SSD133X_CONTRAST_C, 0x7d); - if (ret < 0) - return ret; - - /* Set master current */ - ret = ssd130x_write_cmd(ssd130x, 2, SSD133X_SET_MASTER_CURRENT, 0x06); - if (ret < 0) - return ret; - - /* Set column start and end */ - ret = ssd130x_write_cmd(ssd130x, 3, SSD133X_SET_COL_RANGE, 0x00, ssd130x->width - 1); - if (ret < 0) - return ret; - - /* Set row start and end */ - ret = ssd130x_write_cmd(ssd130x, 3, SSD133X_SET_ROW_RANGE, 0x00, ssd130x->height - 1); - if (ret < 0) - return ret; - - /* - * Horizontal Address Increment - * Normal order SA,SB,SC (e.g. RGB) - * COM Split Odd Even - * 256 color format - */ - ret = ssd130x_write_cmd(ssd130x, 2, SSD13XX_SET_SEG_REMAP, 0x20); - if (ret < 0) - return ret; - - /* Set display start and offset */ - ret = ssd130x_write_cmd(ssd130x, 2, SSD133X_SET_DISPLAY_START, 0x00); - if (ret < 0) - return ret; - - ret = ssd130x_write_cmd(ssd130x, 2, SSD133X_SET_DISPLAY_OFFSET, 0x00); - if (ret < 0) - return ret; - - /* Set display mode normal */ - ret = ssd130x_write_cmd(ssd130x, 1, SSD133X_SET_DISPLAY_NORMAL); - if (ret < 0) - return ret; - - /* Set multiplex ratio value */ - ret = ssd130x_write_cmd(ssd130x, 2, SSD13XX_SET_MULTIPLEX_RATIO, ssd130x->height - 1); - if (ret < 0) - return ret; - - /* Set master configuration */ - ret = ssd130x_write_cmd(ssd130x, 2, SSD133X_SET_MASTER_CONFIG, 0x8e); - if (ret < 0) - return ret; - - /* Set power mode */ - ret = ssd130x_write_cmd(ssd130x, 2, SSD133X_POWER_SAVE_MODE, 0x0b); - if (ret < 0) - return ret; - - /* Set Phase 1 and 2 period */ - ret = ssd130x_write_cmd(ssd130x, 2, SSD133X_PHASES_PERIOD, 0x31); - if (ret < 0) - return ret; - - /* Set clock divider */ - ret = ssd130x_write_cmd(ssd130x, 2, SSD133X_SET_CLOCK_FREQ, 0xf0); - if (ret < 0) - return ret; - - /* Set pre-charge A */ - ret = ssd130x_write_cmd(ssd130x, 2, SSD132X_SET_PRECHARGE_A, 0x64); - if (ret < 0) - return ret; - - /* Set pre-charge B */ - ret = ssd130x_write_cmd(ssd130x, 2, SSD132X_SET_PRECHARGE_B, 0x78); - if (ret < 0) - return ret; - - /* Set pre-charge C */ - ret = ssd130x_write_cmd(ssd130x, 2, SSD132X_SET_PRECHARGE_C, 0x64); - if (ret < 0) - return ret; - - /* Set pre-charge level */ - ret = ssd130x_write_cmd(ssd130x, 2, SSD133X_SET_PRECHARGE_VOLTAGE, 0x3a); - if (ret < 0) - return ret; - - /* Set VCOMH voltage */ - ret = ssd130x_write_cmd(ssd130x, 2, SSD133X_SET_VCOMH_VOLTAGE, 0x3e); - if (ret < 0) - return ret; - - return 0; + const u8 cmds[] = { + 2, SSD133X_CONTRAST_A, 0x91, + 2, SSD133X_CONTRAST_B, 0x50, + 2, SSD133X_CONTRAST_C, 0x7d, + 2, SSD133X_SET_MASTER_CURRENT, 0x06, + 3, SSD133X_SET_COL_RANGE, 0x00, ssd130x->width - 1, + 3, SSD133X_SET_ROW_RANGE, 0x00, ssd130x->height - 1, + /* + * Horizontal Address Increment + * Normal order SA,SB,SC (e.g. RGB) + * COM Split Odd Even + * 256 color format + */ + 2, SSD13XX_SET_SEG_REMAP, 0x20, + 2, SSD133X_SET_DISPLAY_START, 0x00, + 2, SSD133X_SET_DISPLAY_OFFSET, 0x00, + 1, SSD133X_SET_DISPLAY_NORMAL, + 2, SSD13XX_SET_MULTIPLEX_RATIO, ssd130x->height - 1, + 2, SSD133X_SET_MASTER_CONFIG, 0x8e, + 2, SSD133X_POWER_SAVE_MODE, 0x0b, + 2, SSD133X_PHASES_PERIOD, 0x31, + 2, SSD133X_SET_CLOCK_FREQ, 0xf0, + 2, SSD132X_SET_PRECHARGE_A, 0x64, + 2, SSD132X_SET_PRECHARGE_B, 0x78, + 2, SSD132X_SET_PRECHARGE_C, 0x64, + 2, SSD133X_SET_PRECHARGE_VOLTAGE, 0x3a, + 2, SSD133X_SET_VCOMH_VOLTAGE, 0x3e, + 0, + }; + + return ssd130x_run_cmd_seq(ssd130x, cmds); } static int ssd130x_update_rect(struct ssd130x_device *ssd130x, diff --git a/drivers/gpu/drm/stm/lvds.c b/drivers/gpu/drm/stm/lvds.c index 50a878688e47..90a44e722057 100644 --- a/drivers/gpu/drm/stm/lvds.c +++ b/drivers/gpu/drm/stm/lvds.c @@ -1038,7 +1038,7 @@ static const struct drm_bridge_funcs lvds_bridge_funcs = { .atomic_disable = lvds_atomic_disable, .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_create_state = drm_atomic_helper_bridge_create_state, }; static int lvds_probe(struct platform_device *pdev) diff --git a/drivers/gpu/drm/sun4i/sun4i_hdmi_enc.c b/drivers/gpu/drm/sun4i/sun4i_hdmi_enc.c index 74c7c3720ba8..8f64464621c9 100644 --- a/drivers/gpu/drm/sun4i/sun4i_hdmi_enc.c +++ b/drivers/gpu/drm/sun4i/sun4i_hdmi_enc.c @@ -285,7 +285,7 @@ sun4i_hdmi_connector_detect(struct drm_connector *connector, bool force) static void sun4i_hdmi_connector_reset(struct drm_connector *connector) { drm_atomic_helper_connector_reset(connector); - __drm_atomic_helper_connector_hdmi_reset(connector, connector->state); + __drm_atomic_helper_connector_hdmi_state_init(connector, connector->state); } static const struct drm_connector_funcs sun4i_hdmi_connector_funcs = { diff --git a/drivers/gpu/drm/sysfb/Kconfig b/drivers/gpu/drm/sysfb/Kconfig index 2559ead6cf1f..f7e48178885e 100644 --- a/drivers/gpu/drm/sysfb/Kconfig +++ b/drivers/gpu/drm/sysfb/Kconfig @@ -67,11 +67,8 @@ config DRM_SIMPLEDRM This driver assumes that the display hardware has been initialized by the firmware or bootloader before the kernel boots. Scanout - buffer, size, and display format must be provided via device tree, - UEFI, VESA, etc. - - On x86 BIOS or UEFI systems, you should also select SYSFB_SIMPLEFB - to use UEFI and VESA framebuffers. + buffer, size, and display format must be provided via device tree's + simple-framebuffer node. config DRM_VESADRM tristate "VESA framebuffer driver" diff --git a/drivers/gpu/drm/sysfb/simpledrm.c b/drivers/gpu/drm/sysfb/simpledrm.c index fc168920f2c6..9e0711e0095a 100644 --- a/drivers/gpu/drm/sysfb/simpledrm.c +++ b/drivers/gpu/drm/sysfb/simpledrm.c @@ -6,6 +6,7 @@ #include <linux/of_address.h> #include <linux/of_clk.h> #include <linux/of_reserved_mem.h> +#include <linux/overflow.h> #include <linux/platform_data/simplefb.h> #include <linux/platform_device.h> #include <linux/pm.h> @@ -41,20 +42,6 @@ * Helpers for simplefb */ -static int -simplefb_get_validated_int(struct drm_device *dev, const char *name, - uint32_t value) -{ - return drm_sysfb_get_validated_int(dev, name, value, INT_MAX); -} - -static int -simplefb_get_validated_int0(struct drm_device *dev, const char *name, - uint32_t value) -{ - return drm_sysfb_get_validated_int0(dev, name, value, INT_MAX); -} - static const struct drm_format_info * simplefb_get_validated_format(struct drm_device *dev, const char *format_name) { @@ -88,21 +75,21 @@ static int simplefb_get_width_pd(struct drm_device *dev, const struct simplefb_platform_data *pd) { - return simplefb_get_validated_int0(dev, "width", pd->width); + return drm_sysfb_get_validated_int0(dev, "width", pd->width, U16_MAX); } static int simplefb_get_height_pd(struct drm_device *dev, const struct simplefb_platform_data *pd) { - return simplefb_get_validated_int0(dev, "height", pd->height); + return drm_sysfb_get_validated_int0(dev, "height", pd->height, U16_MAX); } static int simplefb_get_stride_pd(struct drm_device *dev, const struct simplefb_platform_data *pd) { - return simplefb_get_validated_int(dev, "stride", pd->stride); + return drm_sysfb_get_validated_int(dev, "stride", pd->stride, INT_MAX); } static const struct drm_format_info * @@ -144,7 +131,7 @@ simplefb_get_width_of(struct drm_device *dev, struct device_node *of_node) if (ret) return ret; - return simplefb_get_validated_int0(dev, "width", width); + return drm_sysfb_get_validated_int0(dev, "width", width, U16_MAX); } static int @@ -155,7 +142,7 @@ simplefb_get_height_of(struct drm_device *dev, struct device_node *of_node) if (ret) return ret; - return simplefb_get_validated_int0(dev, "height", height); + return drm_sysfb_get_validated_int0(dev, "height", height, U16_MAX); } static int @@ -166,7 +153,7 @@ simplefb_get_stride_of(struct drm_device *dev, struct device_node *of_node) if (ret) return ret; - return simplefb_get_validated_int(dev, "stride", stride); + return drm_sysfb_get_validated_int(dev, "stride", stride, INT_MAX); } static const struct drm_format_info * @@ -200,6 +187,39 @@ simplefb_get_memory_of(struct drm_device *dev, struct device_node *of_node) return res; } +static int __simplefb_get_panel_size_mm_of(struct drm_device *dev, + struct device_node *of_panel_node, + const char *name) +{ + int ret; + u32 value; + + ret = of_property_read_u32(of_panel_node, name, &value); + if (ret) { + drm_dbg(dev, "simplefb: cannot parse panel %s: error %d\n", + name, ret); + return ret; + } else if (value > U16_MAX) { + drm_dbg(dev, "simplefb: panel %s of %u exceeds maximum value\n", + name, value); + return -EINVAL; + } + + return value; +} + +static int simplefb_get_panel_width_mm_of(struct drm_device *dev, + struct device_node *of_panel_node) +{ + return __simplefb_get_panel_size_mm_of(dev, of_panel_node, "width-mm"); +} + +static int simplefb_get_panel_height_mm_of(struct drm_device *dev, + struct device_node *of_panel_node) +{ + return __simplefb_get_panel_size_mm_of(dev, of_panel_node, "height-mm"); +} + /* * Simple Framebuffer device */ @@ -601,9 +621,10 @@ static struct simpledrm_device *simpledrm_device_create(struct drm_driver *drv, struct drm_sysfb_device *sysfb; struct drm_device *dev; int width, height, stride; - int width_mm = 0, height_mm = 0; + u16 width_mm = 0, height_mm = 0; struct device_node *panel_node; const struct drm_format_info *format; + u64 size; struct resource *res, *mem = NULL; struct drm_plane *primary_plane; struct drm_crtc *crtc; @@ -665,8 +686,18 @@ static struct simpledrm_device *simpledrm_device_create(struct drm_driver *drv, return ERR_CAST(mem); panel_node = of_parse_phandle(of_node, "panel", 0); if (panel_node) { - simplefb_read_u32_of(dev, panel_node, "width-mm", &width_mm); - simplefb_read_u32_of(dev, panel_node, "height-mm", &height_mm); + /* + * Ignore errors from parsing the physical panel + * size. Using the pre-initialized sizes of 0 will + * make drm_sysfb_mode() calculate a default physical + * size based on a resolution of 96 dpi. + */ + ret = simplefb_get_panel_width_mm_of(dev, panel_node); + if (ret > 0) + width_mm = ret; + ret = simplefb_get_panel_height_mm_of(dev, panel_node); + if (ret > 0) + height_mm = ret; of_node_put(panel_node); } } else { @@ -674,9 +705,23 @@ static struct simpledrm_device *simpledrm_device_create(struct drm_driver *drv, return ERR_PTR(-ENODEV); } if (!stride) { - stride = drm_format_info_min_pitch(format, 0, width); - if (drm_WARN_ON(dev, !stride)) + u64 pitch = drm_format_info_min_pitch(format, 0, width); + + if (drm_WARN_ON(dev, !pitch)) { + return ERR_PTR(-EINVAL); /* driver bug */ + } else if (pitch > INT_MAX) { + drm_warn(dev, "stride of %llu exceeds maximum\n", pitch); return ERR_PTR(-EINVAL); + } + stride = pitch; + } + if (check_mul_overflow(height, stride, &size)) { + drm_err(dev, "framebuffer size exceeds maximum\n"); + return ERR_PTR(-EINVAL); + } + if (ALIGN(size, PAGE_SIZE) < PAGE_SIZE) { + drm_err(dev, "page-aligned framebuffer exceeds maximum\n"); + return ERR_PTR(-EINVAL); } sysfb->fb_mode = drm_sysfb_mode(width, height, width_mm, height_mm); @@ -703,6 +748,13 @@ static struct simpledrm_device *simpledrm_device_create(struct drm_driver *drv, drm_dbg(dev, "using system memory framebuffer at %pr\n", mem); + if (size > resource_size(mem)) { + drm_err(dev, + "framebuffer size of %llu exceeds memory range %pr\n", + size, mem); + return ERR_PTR(-EINVAL); + } + screen_base = devm_memremap(dev->dev, mem->start, resource_size(mem), MEMREMAP_WC); if (IS_ERR(screen_base)) return screen_base; @@ -736,6 +788,13 @@ static struct simpledrm_device *simpledrm_device_create(struct drm_driver *drv, mem = res; } + if (size > resource_size(mem)) { + drm_err(dev, + "framebuffer size of %llu exceeds memory range %pr\n", + size, mem); + return ERR_PTR(-EINVAL); + } + screen_base = devm_ioremap_wc(&pdev->dev, mem->start, resource_size(mem)); if (!screen_base) return ERR_PTR(-ENOMEM); diff --git a/drivers/gpu/drm/tegra/drm.c b/drivers/gpu/drm/tegra/drm.c index dd6c564ff408..24e8ef261836 100644 --- a/drivers/gpu/drm/tegra/drm.c +++ b/drivers/gpu/drm/tegra/drm.c @@ -1358,10 +1358,12 @@ static SIMPLE_DEV_PM_OPS(host1x_drm_pm_ops, host1x_drm_suspend, static const struct of_device_id host1x_drm_subdevs[] = { { .compatible = "nvidia,tegra20-dc", }, + { .compatible = "nvidia,tegra20-dsi", }, { .compatible = "nvidia,tegra20-hdmi", }, { .compatible = "nvidia,tegra20-gr2d", }, { .compatible = "nvidia,tegra20-gr3d", }, { .compatible = "nvidia,tegra30-dc", }, + { .compatible = "nvidia,tegra30-dsi", }, { .compatible = "nvidia,tegra30-hdmi", }, { .compatible = "nvidia,tegra30-gr2d", }, { .compatible = "nvidia,tegra30-gr3d", }, diff --git a/drivers/gpu/drm/tegra/dsi.c b/drivers/gpu/drm/tegra/dsi.c index 7f25c50621c9..e7fdd8c7ac12 100644 --- a/drivers/gpu/drm/tegra/dsi.c +++ b/drivers/gpu/drm/tegra/dsi.c @@ -54,6 +54,11 @@ to_dsi_state(struct drm_connector_state *state) return container_of(state, struct tegra_dsi_state, base); } +struct tegra_dsi_config { + bool has_multiple_pad_controls; + bool has_mux_parent_clk; +}; + struct tegra_dsi { struct host1x_client client; struct tegra_output output; @@ -83,6 +88,8 @@ struct tegra_dsi { /* for ganged-mode support */ struct tegra_dsi *master; struct tegra_dsi *slave; + + const struct tegra_dsi_config *config; }; static inline struct tegra_dsi * @@ -665,39 +672,46 @@ static int tegra_dsi_pad_enable(struct tegra_dsi *dsi) { u32 value; - value = DSI_PAD_CONTROL_VS1_PULLDN(0) | DSI_PAD_CONTROL_VS1_PDIO(0); - tegra_dsi_writel(dsi, value, DSI_PAD_CONTROL_0); + if (dsi->config->has_multiple_pad_controls) { + /* + * XXX Is this still needed? The module reset is deasserted right + * before this function is called. + */ + tegra_dsi_writel(dsi, 0, DSI_PAD_CONTROL_0); + tegra_dsi_writel(dsi, 0, DSI_PAD_CONTROL_1); + tegra_dsi_writel(dsi, 0, DSI_PAD_CONTROL_2); + tegra_dsi_writel(dsi, 0, DSI_PAD_CONTROL_3); + tegra_dsi_writel(dsi, 0, DSI_PAD_CONTROL_4); + + value = DSI_PAD_CONTROL_VS1_PULLDN(0) | DSI_PAD_CONTROL_VS1_PDIO(0); + tegra_dsi_writel(dsi, value, DSI_PAD_CONTROL_0); + + value = DSI_PAD_SLEW_UP(0x7) | DSI_PAD_SLEW_DN(0x7) | + DSI_PAD_LP_UP(0x1) | DSI_PAD_LP_DN(0x1) | + DSI_PAD_OUT_CLK(0x0); + tegra_dsi_writel(dsi, value, DSI_PAD_CONTROL_2); + + value = DSI_PAD_PREEMP_PD_CLK(0x3) | DSI_PAD_PREEMP_PU_CLK(0x3) | + DSI_PAD_PREEMP_PD(0x03) | DSI_PAD_PREEMP_PU(0x3); + tegra_dsi_writel(dsi, value, DSI_PAD_CONTROL_3); + } else { + value = DSI_PAD_CONTROL_LPUPADJ(0x1) | DSI_PAD_CONTROL_LPDNADJ(0x1) | + DSI_PAD_CONTROL_PREEMP_EN(0x1) | DSI_PAD_CONTROL_SLEWDNADJ(0x6) | + DSI_PAD_CONTROL_SLEWUPADJ(0x6) | DSI_PAD_CONTROL_PDIO(0) | + DSI_PAD_CONTROL_PDIO_CLK(0) | DSI_PAD_CONTROL_PULLDN_ENAB(0); + tegra_dsi_writel(dsi, value, DSI_PAD_CONTROL_0); + } return 0; } static int tegra_dsi_pad_calibrate(struct tegra_dsi *dsi) { - u32 value; int err; - /* - * XXX Is this still needed? The module reset is deasserted right - * before this function is called. - */ - tegra_dsi_writel(dsi, 0, DSI_PAD_CONTROL_0); - tegra_dsi_writel(dsi, 0, DSI_PAD_CONTROL_1); - tegra_dsi_writel(dsi, 0, DSI_PAD_CONTROL_2); - tegra_dsi_writel(dsi, 0, DSI_PAD_CONTROL_3); - tegra_dsi_writel(dsi, 0, DSI_PAD_CONTROL_4); - /* start calibration */ tegra_dsi_pad_enable(dsi); - value = DSI_PAD_SLEW_UP(0x7) | DSI_PAD_SLEW_DN(0x7) | - DSI_PAD_LP_UP(0x1) | DSI_PAD_LP_DN(0x1) | - DSI_PAD_OUT_CLK(0x0); - tegra_dsi_writel(dsi, value, DSI_PAD_CONTROL_2); - - value = DSI_PAD_PREEMP_PD_CLK(0x3) | DSI_PAD_PREEMP_PU_CLK(0x3) | - DSI_PAD_PREEMP_PD(0x03) | DSI_PAD_PREEMP_PU(0x3); - tegra_dsi_writel(dsi, value, DSI_PAD_CONTROL_3); - err = tegra_mipi_start_calibration(dsi->mipi); if (err < 0) return err; @@ -922,6 +936,15 @@ static void tegra_dsi_encoder_enable(struct drm_encoder *encoder) return; } + /* If the bootloader enabled DSI it needs to be disabled + * in order for the panel initialization commands to be + * properly sent. + */ + value = tegra_dsi_readl(dsi, DSI_POWER_CONTROL); + + if (value & DSI_POWER_CONTROL_ENABLE) + tegra_dsi_disable(dsi); + state = tegra_dsi_get_state(dsi); tegra_dsi_set_timeout(dsi, state->bclk, state->vrefresh); @@ -1174,6 +1197,12 @@ static int tegra_dsi_setup_clocks(struct tegra_dsi *dsi) struct clk *parent; int err; + /* + * Tegra124+ uses a clock gate, not a mux, so this step + * should be redundant for configuration; yet, DSI refuses + * to work without it. + */ + parent = clk_get_parent(dsi->clk); if (!parent) return -EINVAL; @@ -1562,6 +1591,10 @@ static int tegra_dsi_probe(struct platform_device *pdev) if (!dsi) return -ENOMEM; + dsi->config = device_get_match_data(&pdev->dev); + if (!dsi->config) + return -ENODEV; + dsi->output.dev = dsi->dev = &pdev->dev; dsi->video_fifo_depth = 1920; dsi->host_fifo_depth = 64; @@ -1600,7 +1633,7 @@ static int tegra_dsi_probe(struct platform_device *pdev) goto remove; } - dsi->clk_lp = devm_clk_get(&pdev->dev, "lp"); + dsi->clk_lp = devm_clk_get_optional(&pdev->dev, "lp"); if (IS_ERR(dsi->clk_lp)) { err = dev_err_probe(&pdev->dev, PTR_ERR(dsi->clk_lp), "cannot get low-power clock\n"); @@ -1621,10 +1654,12 @@ static int tegra_dsi_probe(struct platform_device *pdev) goto remove; } - err = tegra_dsi_setup_clocks(dsi); - if (err < 0) { - dev_err(&pdev->dev, "cannot setup clocks\n"); - goto remove; + if (dsi->config->has_mux_parent_clk) { + err = tegra_dsi_setup_clocks(dsi); + if (err < 0) { + dev_err(&pdev->dev, "cannot setup clocks\n"); + goto remove; + } } dsi->regs = devm_platform_ioremap_resource(pdev, 0); @@ -1688,11 +1723,40 @@ static void tegra_dsi_remove(struct platform_device *pdev) tegra_mipi_free(dsi->mipi); } +static const struct tegra_dsi_config tegra20_dsi_config = { + .has_multiple_pad_controls = false, + .has_mux_parent_clk = false, +}; + +/* + * Tegra30 allows DSIA/DSIB to be muxed to either PLL_D or PLL_D2; this is + * simply not modeled in the clock driver yet. If this functionality is + * required, the has_mux_parent_clk flag can be set to true once the clock + * driver is patched. + */ +static const struct tegra_dsi_config tegra30_dsi_config = { + .has_multiple_pad_controls = false, + .has_mux_parent_clk = false, +}; + +static const struct tegra_dsi_config tegra114_dsi_config = { + .has_multiple_pad_controls = true, + .has_mux_parent_clk = true, +}; + +/* TODO: figure out why has_mux_parent_clk = true is necessary on Tegra124+ */ +static const struct tegra_dsi_config tegra124_dsi_config = { + .has_multiple_pad_controls = true, + .has_mux_parent_clk = true, +}; + static const struct of_device_id tegra_dsi_of_match[] = { - { .compatible = "nvidia,tegra210-dsi", }, - { .compatible = "nvidia,tegra132-dsi", }, - { .compatible = "nvidia,tegra124-dsi", }, - { .compatible = "nvidia,tegra114-dsi", }, + { .compatible = "nvidia,tegra210-dsi", .data = &tegra124_dsi_config }, + { .compatible = "nvidia,tegra132-dsi", .data = &tegra124_dsi_config }, + { .compatible = "nvidia,tegra124-dsi", .data = &tegra124_dsi_config }, + { .compatible = "nvidia,tegra114-dsi", .data = &tegra114_dsi_config }, + { .compatible = "nvidia,tegra30-dsi", .data = &tegra30_dsi_config }, + { .compatible = "nvidia,tegra20-dsi", .data = &tegra20_dsi_config }, { }, }; MODULE_DEVICE_TABLE(of, tegra_dsi_of_match); diff --git a/drivers/gpu/drm/tegra/dsi.h b/drivers/gpu/drm/tegra/dsi.h index f39594e65e97..d834ac0c47ab 100644 --- a/drivers/gpu/drm/tegra/dsi.h +++ b/drivers/gpu/drm/tegra/dsi.h @@ -95,6 +95,16 @@ #define DSI_TALLY_LRX(x) (((x) & 0xff) << 8) #define DSI_TALLY_HTX(x) (((x) & 0xff) << 0) #define DSI_PAD_CONTROL_0 0x4b +/* Tegra20/Tegra30 */ +#define DSI_PAD_CONTROL_PULLDN_ENAB(x) (((x) & 0x1) << 28) +#define DSI_PAD_CONTROL_SLEWUPADJ(x) (((x) & 0x7) << 24) +#define DSI_PAD_CONTROL_SLEWDNADJ(x) (((x) & 0x7) << 20) +#define DSI_PAD_CONTROL_PREEMP_EN(x) (((x) & 0x1) << 19) +#define DSI_PAD_CONTROL_PDIO_CLK(x) (((x) & 0x1) << 18) +#define DSI_PAD_CONTROL_PDIO(x) (((x) & 0x3) << 16) +#define DSI_PAD_CONTROL_LPUPADJ(x) (((x) & 0x3) << 14) +#define DSI_PAD_CONTROL_LPDNADJ(x) (((x) & 0x3) << 12) +/* Tegra114+ */ #define DSI_PAD_CONTROL_VS1_PDIO(x) (((x) & 0xf) << 0) #define DSI_PAD_CONTROL_VS1_PDIO_CLK (1 << 8) #define DSI_PAD_CONTROL_VS1_PULLDN(x) (((x) & 0xf) << 16) diff --git a/drivers/gpu/drm/tests/Makefile b/drivers/gpu/drm/tests/Makefile index 3b83e6616fb2..e7193b49cf91 100644 --- a/drivers/gpu/drm/tests/Makefile +++ b/drivers/gpu/drm/tests/Makefile @@ -24,6 +24,7 @@ obj-$(CONFIG_DRM_KUNIT_TEST) += \ drm_probe_helper_test.o \ drm_rect_test.o \ drm_sysfb_modeset_test.o \ - drm_fixp_test.o + drm_fixp_test.o \ + drm_kunit_edid.o CFLAGS_drm_mm_test.o := $(DISABLE_STRUCTLEAK_PLUGIN) diff --git a/drivers/gpu/drm/tests/drm_bridge_test.c b/drivers/gpu/drm/tests/drm_bridge_test.c index 64b665580a88..10a32620e962 100644 --- a/drivers/gpu/drm/tests/drm_bridge_test.c +++ b/drivers/gpu/drm/tests/drm_bridge_test.c @@ -2,15 +2,24 @@ /* * Kunit test for drm_bridge functions */ +#include <linux/cleanup.h> +#include <linux/media-bus-format.h> + +#include <drm/drm_atomic_helper.h> #include <drm/drm_atomic_state_helper.h> +#include <drm/drm_atomic_uapi.h> #include <drm/drm_bridge.h> #include <drm/drm_bridge_connector.h> #include <drm/drm_bridge_helper.h> +#include <drm/drm_edid.h> #include <drm/drm_kunit_helpers.h> +#include <drm/drm_managed.h> #include <kunit/device.h> #include <kunit/test.h> +#include "drm_kunit_edid.h" + /* * Mimick the typical "private" struct defined by a bridge driver, which * embeds a bridge plus other fields. @@ -37,6 +46,21 @@ struct drm_bridge_init_priv { bool destroyed; }; +struct drm_bridge_chain_priv { + struct drm_device drm; + struct drm_encoder encoder; + struct drm_plane *plane; + struct drm_crtc *crtc; + struct drm_connector *connector; + unsigned int num_bridges; + + /** + * @test_bridges: array of pointers to &struct drm_bridge_priv entries + * of which the first @num_bridges entries are valid. + */ + struct drm_bridge_priv **test_bridges; +}; + static struct drm_bridge_priv *bridge_to_priv(struct drm_bridge *bridge) { return container_of(bridge, struct drm_bridge_priv, bridge); @@ -92,9 +116,254 @@ static const struct drm_bridge_funcs drm_test_bridge_atomic_funcs = { .atomic_disable = drm_test_bridge_atomic_disable, .atomic_destroy_state = drm_atomic_helper_bridge_destroy_state, .atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state, - .atomic_reset = drm_atomic_helper_bridge_reset, + .atomic_create_state = drm_atomic_helper_bridge_create_state, +}; + +static int dummy_clear_infoframe(struct drm_bridge *bridge) +{ + return 0; +} + +static int dummy_write_infoframe(struct drm_bridge *bridge, const u8 *buffer, + size_t len) +{ + return 0; +} + +static const struct drm_bridge_funcs drm_test_bridge_bus_fmts_funcs = { + .atomic_get_output_bus_fmts = drm_atomic_helper_bridge_get_hdmi_output_bus_fmts, + .atomic_destroy_state = drm_atomic_helper_bridge_destroy_state, + .atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state, + .atomic_create_state = drm_atomic_helper_bridge_create_state, + .hdmi_write_avi_infoframe = dummy_write_infoframe, + .hdmi_write_hdmi_infoframe = dummy_write_infoframe, + .hdmi_clear_avi_infoframe = dummy_clear_infoframe, + .hdmi_clear_hdmi_infoframe = dummy_clear_infoframe, +}; + +/** + * struct fmt_tuple - a tuple of input/output MEDIA_BUS_FMT_* + */ +struct fmt_tuple { + u32 in_fmt; + u32 out_fmt; +}; + +/* + * Format mapping that only accepts RGB888, and outputs only RGB888 + */ +static const struct fmt_tuple rgb8_passthrough[] = { + { MEDIA_BUS_FMT_RGB888_1X24, MEDIA_BUS_FMT_RGB888_1X24 }, +}; + +/* + * Format mapping that only accepts YUV444, and outputs only YUV444 + */ +static const struct fmt_tuple yuv8_passthrough[] = { + { MEDIA_BUS_FMT_YUV8_1X24, MEDIA_BUS_FMT_YUV8_1X24 }, }; +/* + * Format mapping where 8bpc RGB -> 8bpc YUV444, or ID(RGB) or ID(YUV444) + */ +static const struct fmt_tuple rgb8_to_yuv8_or_id[] = { + { MEDIA_BUS_FMT_RGB888_1X24, MEDIA_BUS_FMT_RGB888_1X24 }, + { MEDIA_BUS_FMT_YUV8_1X24, MEDIA_BUS_FMT_YUV8_1X24 }, + { MEDIA_BUS_FMT_RGB888_1X24, MEDIA_BUS_FMT_YUV8_1X24 }, +}; + +static const struct fmt_tuple rgb8_to_id_yuv8_or_yuv8_to_yuv422_yuv420[] = { + { MEDIA_BUS_FMT_RGB888_1X24, MEDIA_BUS_FMT_RGB888_1X24 }, + { MEDIA_BUS_FMT_RGB888_1X24, MEDIA_BUS_FMT_YUV8_1X24 }, + { MEDIA_BUS_FMT_YUV8_1X24, MEDIA_BUS_FMT_UYVY8_1X16 }, + { MEDIA_BUS_FMT_YUV8_1X24, MEDIA_BUS_FMT_UYYVYY8_0_5X24 }, +}; + +/* + * Format mapping where 8bpc YUV444 -> 8bpc RGB, or ID(YUV444) + */ +static const struct fmt_tuple yuv8_to_rgb8_or_id[] = { + { MEDIA_BUS_FMT_YUV8_1X24, MEDIA_BUS_FMT_YUV8_1X24 }, + { MEDIA_BUS_FMT_YUV8_1X24, MEDIA_BUS_FMT_RGB888_1X24 }, +}; + +/* + * A format mapping that acts like a video processor that generates an RGB signal + */ +static const struct fmt_tuple rgb_producer[] = { + { MEDIA_BUS_FMT_FIXED, MEDIA_BUS_FMT_RGB888_1X24 }, + { MEDIA_BUS_FMT_FIXED, MEDIA_BUS_FMT_RGB101010_1X30 }, + { MEDIA_BUS_FMT_FIXED, MEDIA_BUS_FMT_RGB121212_1X36 }, +}; + +/* + * A format mapping that acts like a video processor that generates an 8-bit RGB, + * YUV444 or YUV420 signal + */ +static const struct fmt_tuple rgb_yuv444_yuv420_producer[] = { + { MEDIA_BUS_FMT_FIXED, MEDIA_BUS_FMT_RGB888_1X24 }, + { MEDIA_BUS_FMT_FIXED, MEDIA_BUS_FMT_YUV8_1X24 }, + { MEDIA_BUS_FMT_FIXED, MEDIA_BUS_FMT_UYYVYY8_0_5X24 }, +}; + +static const struct fmt_tuple rgb8_yuv444_yuv422_passthrough[] = { + { MEDIA_BUS_FMT_RGB888_1X24, MEDIA_BUS_FMT_RGB888_1X24 }, + { MEDIA_BUS_FMT_YUV8_1X24, MEDIA_BUS_FMT_YUV8_1X24 }, + { MEDIA_BUS_FMT_UYVY8_1X16, MEDIA_BUS_FMT_UYVY8_1X16 }, +}; + +static const struct fmt_tuple yuv444_yuv422_rgb8_passthrough[] = { + { MEDIA_BUS_FMT_YUV8_1X24, MEDIA_BUS_FMT_YUV8_1X24 }, + { MEDIA_BUS_FMT_UYVY8_1X16, MEDIA_BUS_FMT_UYVY8_1X16 }, + { MEDIA_BUS_FMT_RGB888_1X24, MEDIA_BUS_FMT_RGB888_1X24 }, +}; + +static bool fmt_in_list(const u32 fmt, const u32 *out_fmts, const size_t num_fmts) +{ + size_t i; + + for (i = 0; i < num_fmts; i++) + if (out_fmts[i] == fmt) + return true; + + return false; +} + +/** + * get_tuples_out_fmts - Get unique output formats of a &struct fmt_tuple list + * @fmt_tuples: array of &struct fmt_tuple + * @num_fmt_tuples: number of entries in @fmt_tuples + * @out_fmts: target array to store the unique output bus formats + * + * Returns the number of unique output formats, i.e. the number of entries in + * @out_fmts that were populated with sensible values. + */ +static size_t get_tuples_out_fmts(const struct fmt_tuple *fmt_tuples, + const size_t num_fmt_tuples, u32 *out_fmts) +{ + size_t num_unique = 0; + size_t i; + + for (i = 0; i < num_fmt_tuples; i++) + if (!fmt_in_list(fmt_tuples[i].out_fmt, out_fmts, num_unique)) + out_fmts[num_unique++] = fmt_tuples[i].out_fmt; + + return num_unique; +} + +#define DEFINE_FMT_FUNCS_FROM_TUPLES(name) \ +static u32 *drm_test_bridge_ ## name ## _out_fmts(struct drm_bridge *bridge, \ + struct drm_bridge_state *bridge_state, \ + struct drm_crtc_state *crtc_state, \ + struct drm_connector_state *conn_state, \ + unsigned int *num_output_fmts) \ +{ \ + u32 *out_fmts = kcalloc(ARRAY_SIZE((name)), sizeof(u32), GFP_KERNEL); \ + \ + if (out_fmts) \ + *num_output_fmts = get_tuples_out_fmts((name), ARRAY_SIZE((name)), out_fmts); \ + else \ + *num_output_fmts = 0; \ + \ + return out_fmts; \ +} \ + \ +static u32 *drm_test_bridge_ ## name ## _in_fmts(struct drm_bridge *bridge, \ + struct drm_bridge_state *bridge_state, \ + struct drm_crtc_state *crtc_state, \ + struct drm_connector_state *conn_state, \ + u32 output_fmt, \ + unsigned int *num_input_fmts) \ +{ \ + u32 *in_fmts = kcalloc(ARRAY_SIZE((name)), sizeof(u32), GFP_KERNEL); \ + unsigned int num_fmts = 0; \ + size_t i; \ + \ + if (!in_fmts) { \ + *num_input_fmts = 0; \ + return NULL; \ + } \ + \ + for (i = 0; i < ARRAY_SIZE((name)); i++) \ + if ((name)[i].out_fmt == output_fmt) \ + in_fmts[num_fmts++] = (name)[i].in_fmt; \ + \ + *num_input_fmts = num_fmts; \ + \ + return in_fmts; \ +} + +#define DRM_BRIDGE_ATOMIC_WITH_BUS_FMT_HDMI_FUNC(ident, input_fmts_func, output_fmts_func, \ + hdmi_write_infoframe_func, \ + hdmi_clear_infoframe_func) \ +static const struct drm_bridge_funcs (ident) = { \ + .atomic_enable = drm_test_bridge_atomic_enable, \ + .atomic_disable = drm_test_bridge_atomic_disable, \ + .atomic_destroy_state = drm_atomic_helper_bridge_destroy_state, \ + .atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state, \ + .atomic_create_state = drm_atomic_helper_bridge_create_state, \ + .atomic_get_input_bus_fmts = (input_fmts_func), \ + .atomic_get_output_bus_fmts = (output_fmts_func), \ + .hdmi_write_avi_infoframe = (hdmi_write_infoframe_func), \ + .hdmi_clear_avi_infoframe = (hdmi_clear_infoframe_func), \ + .hdmi_write_hdmi_infoframe = (hdmi_write_infoframe_func), \ + .hdmi_clear_hdmi_infoframe = (hdmi_clear_infoframe_func), \ +} + +#define DRM_BRIDGE_ATOMIC_WITH_BUS_FMT_FUNC(ident, input_fmts_func, output_fmts_func) \ + DRM_BRIDGE_ATOMIC_WITH_BUS_FMT_HDMI_FUNC(ident, input_fmts_func, output_fmts_func, \ + NULL, NULL) + +#define DRM_BRIDGE_ATOMIC_WITH_BUS_FMT(_name) \ + DRM_BRIDGE_ATOMIC_WITH_BUS_FMT_FUNC(_name ## _funcs, \ + drm_test_bridge_ ## _name ## _in_fmts, \ + drm_test_bridge_ ## _name ## _out_fmts) + +static int drm_test_bridge_write_infoframe_stub(struct drm_bridge *bridge, + const u8 *buffer, size_t len) +{ + return 0; +} + +static int drm_test_bridge_clear_infoframe_stub(struct drm_bridge *bridge) +{ + return 0; +} + +#define DRM_BRIDGE_ATOMIC_WITH_BUS_FMT_HDMI(_name) \ + DRM_BRIDGE_ATOMIC_WITH_BUS_FMT_HDMI_FUNC(_name ## _hdmi ## _funcs, \ + drm_test_bridge_ ## _name ## _in_fmts, \ + drm_test_bridge_ ## _name ## _out_fmts, \ + drm_test_bridge_write_infoframe_stub, \ + drm_test_bridge_clear_infoframe_stub) +DEFINE_FMT_FUNCS_FROM_TUPLES(rgb8_passthrough) +DRM_BRIDGE_ATOMIC_WITH_BUS_FMT(rgb8_passthrough); + +DEFINE_FMT_FUNCS_FROM_TUPLES(yuv8_passthrough) +DRM_BRIDGE_ATOMIC_WITH_BUS_FMT(yuv8_passthrough); + +DEFINE_FMT_FUNCS_FROM_TUPLES(rgb8_to_yuv8_or_id) +DRM_BRIDGE_ATOMIC_WITH_BUS_FMT(rgb8_to_yuv8_or_id); + +DEFINE_FMT_FUNCS_FROM_TUPLES(yuv8_to_rgb8_or_id) +DRM_BRIDGE_ATOMIC_WITH_BUS_FMT(yuv8_to_rgb8_or_id); + +DEFINE_FMT_FUNCS_FROM_TUPLES(rgb_producer) +DRM_BRIDGE_ATOMIC_WITH_BUS_FMT(rgb_producer); + +DEFINE_FMT_FUNCS_FROM_TUPLES(rgb_yuv444_yuv420_producer) +DRM_BRIDGE_ATOMIC_WITH_BUS_FMT(rgb_yuv444_yuv420_producer); + +DEFINE_FMT_FUNCS_FROM_TUPLES(rgb8_to_id_yuv8_or_yuv8_to_yuv422_yuv420) +DRM_BRIDGE_ATOMIC_WITH_BUS_FMT(rgb8_to_id_yuv8_or_yuv8_to_yuv422_yuv420); + +DEFINE_FMT_FUNCS_FROM_TUPLES(rgb8_yuv444_yuv422_passthrough) +DRM_BRIDGE_ATOMIC_WITH_BUS_FMT(rgb8_yuv444_yuv422_passthrough); + +DEFINE_FMT_FUNCS_FROM_TUPLES(yuv444_yuv422_rgb8_passthrough) +DRM_BRIDGE_ATOMIC_WITH_BUS_FMT(yuv444_yuv422_rgb8_passthrough); +DRM_BRIDGE_ATOMIC_WITH_BUS_FMT_HDMI(yuv444_yuv422_rgb8_passthrough); + KUNIT_DEFINE_ACTION_WRAPPER(drm_bridge_remove_wrapper, drm_bridge_remove, struct drm_bridge *); @@ -178,6 +447,196 @@ drm_test_bridge_init(struct kunit *test, const struct drm_bridge_funcs *funcs) return priv; } +static struct drm_bridge_chain_priv * +drm_test_bridge_chain_init(struct kunit *test, unsigned int num_bridges, + const struct drm_bridge_funcs **funcs) +{ + struct drm_bridge_chain_priv *priv; + const struct drm_edid *edid; + struct drm_bridge *prev; + struct drm_encoder *enc; + struct drm_bridge *bridge; + struct drm_device *drm; + bool has_hdmi = false; + struct device *dev; + unsigned int i; + int ret; + + dev = drm_kunit_helper_alloc_device(test); + if (IS_ERR(dev)) + return ERR_CAST(dev); + + priv = drm_kunit_helper_alloc_drm_device(test, dev, struct drm_bridge_chain_priv, + drm, DRIVER_MODESET | DRIVER_ATOMIC); + if (IS_ERR(priv)) + return ERR_CAST(priv); + + drm = &priv->drm; + + priv->test_bridges = drmm_kmalloc_array(drm, num_bridges, sizeof(*priv->test_bridges), + GFP_KERNEL); + if (!priv->test_bridges) + return ERR_PTR(-ENOMEM); + + priv->num_bridges = num_bridges; + + for (i = 0; i < num_bridges; i++) { + priv->test_bridges[i] = devm_drm_bridge_alloc(dev, struct drm_bridge_priv, + bridge, funcs[i]); + if (IS_ERR(priv->test_bridges[i])) + return ERR_CAST(priv->test_bridges[i]); + + priv->test_bridges[i]->data = priv; + } + + priv->plane = drm_kunit_helper_create_primary_plane(test, drm, NULL, NULL, + NULL, 0, NULL); + if (IS_ERR(priv->plane)) + return ERR_CAST(priv->plane); + + priv->crtc = drm_kunit_helper_create_crtc(test, drm, priv->plane, NULL, + NULL, NULL); + if (IS_ERR(priv->crtc)) + return ERR_CAST(priv->crtc); + + enc = &priv->encoder; + ret = drmm_encoder_init(drm, enc, NULL, DRM_MODE_ENCODER_TMDS, NULL); + if (ret) + return ERR_PTR(ret); + + enc->possible_crtcs = drm_crtc_mask(priv->crtc); + + prev = NULL; + for (i = 0; i < num_bridges; i++) { + bridge = &priv->test_bridges[i]->bridge; + bridge->type = DRM_MODE_CONNECTOR_VIRTUAL; + + if (bridge->funcs->hdmi_write_hdmi_infoframe) { + has_hdmi = true; + bridge->ops |= DRM_BRIDGE_OP_HDMI; + bridge->type = DRM_MODE_CONNECTOR_HDMIA; + bridge->vendor = "LNX"; + bridge->product = "KUnit"; + bridge->supported_formats = (BIT(DRM_OUTPUT_COLOR_FORMAT_RGB444) | + BIT(DRM_OUTPUT_COLOR_FORMAT_YCBCR444) | + BIT(DRM_OUTPUT_COLOR_FORMAT_YCBCR422) | + BIT(DRM_OUTPUT_COLOR_FORMAT_YCBCR420)); + } + + ret = drm_kunit_bridge_add(test, bridge); + if (ret) + return ERR_PTR(ret); + + ret = drm_bridge_attach(enc, bridge, prev, 0); + if (ret) + return ERR_PTR(ret); + + prev = bridge; + } + + priv->connector = drm_bridge_connector_init(drm, enc); + if (IS_ERR(priv->connector)) + return ERR_CAST(priv->connector); + + drm_connector_attach_encoder(priv->connector, enc); + + drm_mode_config_reset(drm); + + if (!has_hdmi) + return priv; + + scoped_guard(mutex, &drm->mode_config.mutex) { + edid = drm_edid_alloc(test_edid_hdmi_1080p_rgb_yuv_4k_yuv420_dc_max_200mhz, + ARRAY_SIZE(test_edid_hdmi_1080p_rgb_yuv_4k_yuv420_dc_max_200mhz)); + if (!edid) + return ERR_PTR(-EINVAL); + + drm_edid_connector_update(priv->connector, edid); + KUNIT_ASSERT_GT(test, drm_edid_connector_add_modes(priv->connector), 0); + + ret = priv->connector->funcs->fill_modes(priv->connector, 4096, 4096); + } + + return priv; +} + +static struct drm_bridge_init_priv * +drm_test_bridge_hdmi_init(struct kunit *test, const struct drm_bridge_funcs *funcs, + unsigned int supported_formats, int max_bpc) +{ + struct drm_bridge_init_priv *priv; + struct drm_encoder *enc; + struct drm_bridge *bridge; + struct drm_device *drm; + struct device *dev; + int ret; + + dev = drm_kunit_helper_alloc_device(test); + if (IS_ERR(dev)) + return ERR_CAST(dev); + + priv = drm_kunit_helper_alloc_drm_device(test, dev, + struct drm_bridge_init_priv, drm, + DRIVER_MODESET | DRIVER_ATOMIC); + if (IS_ERR(priv)) + return ERR_CAST(priv); + + priv->test_bridge = devm_drm_bridge_alloc(dev, struct drm_bridge_priv, bridge, funcs); + if (IS_ERR(priv->test_bridge)) + return ERR_CAST(priv->test_bridge); + + priv->test_bridge->data = priv; + + drm = &priv->drm; + priv->plane = drm_kunit_helper_create_primary_plane(test, drm, + NULL, + NULL, + NULL, 0, + NULL); + if (IS_ERR(priv->plane)) + return ERR_CAST(priv->plane); + + priv->crtc = drm_kunit_helper_create_crtc(test, drm, + priv->plane, NULL, + NULL, + NULL); + if (IS_ERR(priv->crtc)) + return ERR_CAST(priv->crtc); + + enc = &priv->encoder; + ret = drmm_encoder_init(drm, enc, NULL, DRM_MODE_ENCODER_TMDS, NULL); + if (ret) + return ERR_PTR(ret); + + enc->possible_crtcs = drm_crtc_mask(priv->crtc); + + bridge = &priv->test_bridge->bridge; + bridge->type = DRM_MODE_CONNECTOR_HDMIA; + bridge->supported_formats = supported_formats; + bridge->max_bpc = max_bpc; + bridge->ops |= DRM_BRIDGE_OP_HDMI; + bridge->vendor = "LNX"; + bridge->product = "KUnit"; + + ret = drm_kunit_bridge_add(test, bridge); + if (ret) + return ERR_PTR(ret); + + ret = drm_bridge_attach(enc, bridge, NULL, 0); + if (ret) + return ERR_PTR(ret); + + priv->connector = drm_bridge_connector_init(drm, enc); + if (IS_ERR(priv->connector)) + return ERR_CAST(priv->connector); + + drm_connector_attach_encoder(priv->connector, enc); + + drm_mode_config_reset(drm); + + return priv; +} + /* * Test that drm_bridge_get_current_state() returns the last committed * state for an atomic bridge. @@ -425,10 +884,94 @@ retry_reset: KUNIT_EXPECT_EQ(test, bridge_priv->disable_count, 1); } +/* + * Test that a bridge using the drm_atomic_helper_bridge_get_hdmi_output_bus_fmts() + * function for &drm_bridge_funcs.atomic_get_output_bus_fmts behaves as expected + * for an HDMI connector bridge. Does so by creating an HDMI bridge connector + * with RGB444, YCBCR444, and YCBCR420 (but not YCBCR422) as supported formats, + * sets the output depth to 8 bits per component, and then validates the returned + * list of bus formats. + */ +static void drm_test_drm_bridge_helper_hdmi_output_bus_fmts(struct kunit *test) +{ + struct drm_connector_state *conn_state; + struct drm_bridge_state *bridge_state; + struct drm_modeset_acquire_ctx ctx; + struct drm_bridge_init_priv *priv; + struct drm_crtc_state *crtc_state; + struct drm_atomic_commit *state; + struct drm_display_mode *mode; + unsigned int num_output_fmts; + struct drm_bridge *bridge; + u32 *out_bus_fmts; + int ret; + + priv = drm_test_bridge_hdmi_init(test, &drm_test_bridge_bus_fmts_funcs, + BIT(DRM_OUTPUT_COLOR_FORMAT_RGB444) | + BIT(DRM_OUTPUT_COLOR_FORMAT_YCBCR444) | + BIT(DRM_OUTPUT_COLOR_FORMAT_YCBCR420), + 12); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, priv); + + bridge = &priv->test_bridge->bridge; + + drm_modeset_acquire_init(&ctx, 0); + + state = drm_kunit_helper_atomic_state_alloc(test, &priv->drm, &ctx); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, state); + +retry_commit: + conn_state = drm_atomic_get_connector_state(state, priv->connector); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, conn_state); + + conn_state->hdmi.output_bpc = 8; + + mode = drm_kunit_display_mode_from_cea_vic(test, &priv->drm, 16); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, mode); + + ret = drm_atomic_set_crtc_for_connector(conn_state, priv->crtc); + if (ret == -EDEADLK) { + drm_modeset_backoff(&ctx); + goto retry_commit; + } + KUNIT_ASSERT_EQ(test, ret, 0); + + crtc_state = drm_atomic_get_crtc_state(state, priv->crtc); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, crtc_state); + + ret = drm_atomic_set_mode_for_crtc(crtc_state, mode); + if (ret == -EDEADLK) { + drm_modeset_backoff(&ctx); + goto retry_commit; + } + KUNIT_ASSERT_EQ(test, ret, 0); + + crtc_state->enable = true; + crtc_state->active = true; + + bridge_state = drm_atomic_get_bridge_state(state, bridge); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, bridge_state); + + out_bus_fmts = bridge->funcs->atomic_get_output_bus_fmts( + bridge, bridge_state, crtc_state, conn_state, &num_output_fmts); + KUNIT_EXPECT_NOT_NULL(test, out_bus_fmts); + KUNIT_EXPECT_EQ(test, num_output_fmts, 3); + + KUNIT_EXPECT_EQ(test, out_bus_fmts[0], MEDIA_BUS_FMT_RGB888_1X24); + KUNIT_EXPECT_EQ(test, out_bus_fmts[1], MEDIA_BUS_FMT_YUV8_1X24); + KUNIT_EXPECT_EQ(test, out_bus_fmts[2], MEDIA_BUS_FMT_UYYVYY8_0_5X24); + + drm_modeset_drop_locks(&ctx); + drm_modeset_acquire_fini(&ctx); + + kfree(out_bus_fmts); +} + static struct kunit_case drm_bridge_helper_reset_crtc_tests[] = { KUNIT_CASE(drm_test_drm_bridge_helper_reset_crtc_atomic), KUNIT_CASE(drm_test_drm_bridge_helper_reset_crtc_atomic_disabled), KUNIT_CASE(drm_test_drm_bridge_helper_reset_crtc_legacy), + KUNIT_CASE(drm_test_drm_bridge_helper_hdmi_output_bus_fmts), { } }; @@ -506,14 +1049,442 @@ static struct kunit_suite drm_bridge_alloc_test_suite = { .test_cases = drm_bridge_alloc_tests, }; +/** + * drm_test_bridge_chain_verify_fmt - Verify bridge chain format selection + * @test: pointer to KUnit test object + * @priv: pointer to a &struct drm_bridge_chain_priv for this chain + * @expected: constant array of &struct fmt_tuple describing the expected + * input and output bus formats + * @num_expected: number of entries in @expected + * + * Runs the KUNIT_EXPECT clauses to verify the bridge chain format selection + * resulted in the expected formats. If %0 is given as a format in a + * &struct fmt_tuple, then it is understood to mean "any". + * + * Must be called with the modeset lock held. + */ +static void drm_test_bridge_chain_verify_fmt(struct kunit *test, + struct drm_bridge_chain_priv *priv, + const struct fmt_tuple *const expected, + const unsigned int num_expected) +{ + struct drm_bridge_state *bstate; + unsigned int i = 0; + + drm_for_each_bridge_in_chain_scoped(&priv->encoder, bridge) { + KUNIT_ASSERT_LT(test, i, num_expected); + + bstate = drm_bridge_get_current_state(bridge); + if (expected[i].in_fmt) + KUNIT_EXPECT_EQ(test, bstate->input_bus_cfg.format, + expected[i].in_fmt); + if (expected[i].out_fmt) + KUNIT_EXPECT_EQ(test, bstate->output_bus_cfg.format, + expected[i].out_fmt); + + i++; + } + + KUNIT_ASSERT_EQ_MSG(test, i, num_expected, + "Fewer bridges (%u) than expected (%u)\n", i, num_expected); +} + +/* + * Test that constructs a bridge chain in which an RGB888 producer is chained to + * two bridges that will convert from RGB to YUV and from YUV to RGB respectively. + * + * The test requests an output color_format of RGB using the color_format property, + * so to satisfy this request, the bridge chain must take a detour over YUV. + */ +static void drm_test_bridge_rgb_yuv_rgb(struct kunit *test) +{ + static const struct drm_bridge_funcs *funcs[] = { + &rgb_producer_funcs, + &rgb8_to_yuv8_or_id_funcs, + &yuv8_to_rgb8_or_id_funcs, + }; + static const struct fmt_tuple expected[] = { + { MEDIA_BUS_FMT_FIXED, MEDIA_BUS_FMT_RGB888_1X24 }, + { MEDIA_BUS_FMT_RGB888_1X24, MEDIA_BUS_FMT_YUV8_1X24 }, + { MEDIA_BUS_FMT_YUV8_1X24, MEDIA_BUS_FMT_RGB888_1X24 }, + }; + struct drm_connector_state *conn_state; + struct drm_modeset_acquire_ctx ctx; + struct drm_bridge_chain_priv *priv; + struct drm_crtc_state *crtc_state; + struct drm_atomic_commit *state; + struct drm_display_mode *mode; + int ret; + + priv = drm_test_bridge_chain_init(test, ARRAY_SIZE(funcs), funcs); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, priv); + + drm_modeset_acquire_init(&ctx, 0); + + state = drm_kunit_helper_atomic_state_alloc(test, &priv->drm, &ctx); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, state); + +retry_commit: + conn_state = drm_atomic_get_connector_state(state, priv->connector); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, conn_state); + + mode = drm_kunit_display_mode_from_cea_vic(test, &priv->drm, 16); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, mode); + + conn_state->color_format = DRM_CONNECTOR_COLOR_FORMAT_RGB444; + + ret = drm_atomic_set_crtc_for_connector(conn_state, priv->crtc); + if (ret == -EDEADLK) { + drm_modeset_backoff(&ctx); + goto retry_commit; + } + KUNIT_ASSERT_EQ(test, ret, 0); + + crtc_state = drm_atomic_get_crtc_state(state, priv->crtc); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, crtc_state); + + ret = drm_atomic_set_mode_for_crtc(crtc_state, mode); + if (ret == -EDEADLK) { + drm_modeset_backoff(&ctx); + goto retry_commit; + } + KUNIT_ASSERT_EQ(test, ret, 0); + + crtc_state->enable = true; + crtc_state->active = true; + + ret = drm_atomic_commit(state); + if (ret == -EDEADLK) { + drm_modeset_backoff(&ctx); + goto retry_commit; + } + KUNIT_ASSERT_EQ(test, ret, 0); + + drm_test_bridge_chain_verify_fmt(test, priv, expected, ARRAY_SIZE(expected)); + + drm_modeset_drop_locks(&ctx); + drm_modeset_acquire_fini(&ctx); +} + +/* + * Test in which a bridge capable of producing RGB, YUV444 and YUV420 has to + * produce RGB and convert with a downstream bridge in the chain to reach the + * requested YUV444 color format, as no direct path exists between its YUV444 + * and the last bridge. + * + * The rationale behind this test is to devise a scenario in which naively + * assuming any format the video processor can output, and the connector + * requests, is the right format to pick, does not work. + */ +static void drm_test_bridge_must_convert_to_yuv444(struct kunit *test) +{ + static const struct drm_bridge_funcs *funcs[] = { + &rgb_yuv444_yuv420_producer_funcs, + &rgb8_passthrough_funcs, + &rgb8_to_id_yuv8_or_yuv8_to_yuv422_yuv420_funcs, + &rgb8_yuv444_yuv422_passthrough_funcs, + }; + static const struct fmt_tuple expected[] = { + { MEDIA_BUS_FMT_FIXED, MEDIA_BUS_FMT_RGB888_1X24 }, + { MEDIA_BUS_FMT_RGB888_1X24, MEDIA_BUS_FMT_RGB888_1X24 }, + { MEDIA_BUS_FMT_RGB888_1X24, MEDIA_BUS_FMT_YUV8_1X24 }, + { MEDIA_BUS_FMT_YUV8_1X24, MEDIA_BUS_FMT_YUV8_1X24 }, + }; + struct drm_connector_state *conn_state; + struct drm_modeset_acquire_ctx ctx; + struct drm_bridge_chain_priv *priv; + struct drm_crtc_state *crtc_state; + struct drm_atomic_commit *state; + struct drm_display_mode *mode; + int ret; + + priv = drm_test_bridge_chain_init(test, ARRAY_SIZE(funcs), funcs); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, priv); + + drm_modeset_acquire_init(&ctx, 0); + + state = drm_kunit_helper_atomic_state_alloc(test, &priv->drm, &ctx); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, state); + +retry_commit: + conn_state = drm_atomic_get_connector_state(state, priv->connector); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, conn_state); + + mode = drm_kunit_display_mode_from_cea_vic(test, &priv->drm, 16); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, mode); + + conn_state->color_format = DRM_CONNECTOR_COLOR_FORMAT_YCBCR444; + + ret = drm_atomic_set_crtc_for_connector(conn_state, priv->crtc); + if (ret == -EDEADLK) { + drm_modeset_backoff(&ctx); + goto retry_commit; + } + KUNIT_ASSERT_EQ(test, ret, 0); + + crtc_state = drm_atomic_get_crtc_state(state, priv->crtc); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, crtc_state); + + ret = drm_atomic_set_mode_for_crtc(crtc_state, mode); + if (ret == -EDEADLK) { + drm_modeset_backoff(&ctx); + goto retry_commit; + } + KUNIT_ASSERT_EQ(test, ret, 0); + + crtc_state->enable = true; + crtc_state->active = true; + + ret = drm_atomic_commit(state); + if (ret == -EDEADLK) { + drm_modeset_backoff(&ctx); + goto retry_commit; + } + KUNIT_ASSERT_EQ(test, ret, 0); + + drm_test_bridge_chain_verify_fmt(test, priv, expected, ARRAY_SIZE(expected)); + + drm_modeset_drop_locks(&ctx); + drm_modeset_acquire_fini(&ctx); +} + +/* + * Test which checks that no matter the order of bus formats returned by an + * HDMI bridge, RGB is preferred on DRM_CONNECTOR_COLOR_FORMAT_AUTO if it's + * available. + */ +static void drm_test_bridge_hdmi_auto_rgb(struct kunit *test) +{ + static const struct drm_bridge_funcs *funcs[] = { + &rgb_yuv444_yuv420_producer_funcs, + &yuv444_yuv422_rgb8_passthrough_hdmi_funcs, + }; + static const struct fmt_tuple expected[] = { + { MEDIA_BUS_FMT_FIXED, MEDIA_BUS_FMT_RGB888_1X24 }, + { MEDIA_BUS_FMT_RGB888_1X24, MEDIA_BUS_FMT_RGB888_1X24 }, + }; + struct drm_connector_state *conn_state; + struct drm_modeset_acquire_ctx ctx; + struct drm_bridge_chain_priv *priv; + struct drm_crtc_state *crtc_state; + struct drm_atomic_commit *state; + struct drm_display_mode *mode; + int ret; + + priv = drm_test_bridge_chain_init(test, ARRAY_SIZE(funcs), funcs); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, priv); + + drm_modeset_acquire_init(&ctx, 0); + + state = drm_kunit_helper_atomic_state_alloc(test, &priv->drm, &ctx); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, state); + +retry_commit: + conn_state = drm_atomic_get_connector_state(state, priv->connector); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, conn_state); + + mode = drm_kunit_display_mode_from_cea_vic(test, &priv->drm, 16); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, mode); + + KUNIT_ASSERT_EQ(test, conn_state->color_format, DRM_CONNECTOR_COLOR_FORMAT_AUTO); + + ret = drm_atomic_set_crtc_for_connector(conn_state, priv->crtc); + if (ret == -EDEADLK) { + drm_modeset_backoff(&ctx); + goto retry_commit; + } + KUNIT_ASSERT_EQ(test, ret, 0); + + crtc_state = drm_atomic_get_crtc_state(state, priv->crtc); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, crtc_state); + + ret = drm_atomic_set_mode_for_crtc(crtc_state, mode); + if (ret == -EDEADLK) { + drm_modeset_backoff(&ctx); + goto retry_commit; + } + KUNIT_ASSERT_EQ(test, ret, 0); + + crtc_state->enable = true; + crtc_state->active = true; + + ret = drm_atomic_commit(state); + if (ret == -EDEADLK) { + drm_modeset_backoff(&ctx); + goto retry_commit; + } + KUNIT_ASSERT_EQ(test, ret, 0); + + KUNIT_EXPECT_EQ(test, conn_state->hdmi.output_format, DRM_OUTPUT_COLOR_FORMAT_RGB444); + + drm_test_bridge_chain_verify_fmt(test, priv, expected, ARRAY_SIZE(expected)); + + drm_modeset_drop_locks(&ctx); + drm_modeset_acquire_fini(&ctx); +} + +/* + * Test which checks that DRM_CONNECTOR_COLOR_FORMAT_AUTO on non-HDMI connectors + * will result in the first bus format on the output to be picked. + */ +static void drm_test_bridge_auto_first(struct kunit *test) +{ + static const struct drm_bridge_funcs *funcs[] = { + &rgb_yuv444_yuv420_producer_funcs, + &yuv444_yuv422_rgb8_passthrough_funcs, + }; + static const struct fmt_tuple expected[] = { + { MEDIA_BUS_FMT_FIXED, MEDIA_BUS_FMT_YUV8_1X24 }, + { MEDIA_BUS_FMT_YUV8_1X24, MEDIA_BUS_FMT_YUV8_1X24 }, + }; + struct drm_connector_state *conn_state; + struct drm_modeset_acquire_ctx ctx; + struct drm_bridge_chain_priv *priv; + struct drm_crtc_state *crtc_state; + struct drm_atomic_commit *state; + struct drm_display_mode *mode; + int ret; + + priv = drm_test_bridge_chain_init(test, ARRAY_SIZE(funcs), funcs); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, priv); + + drm_modeset_acquire_init(&ctx, 0); + + state = drm_kunit_helper_atomic_state_alloc(test, &priv->drm, &ctx); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, state); + +retry_commit: + conn_state = drm_atomic_get_connector_state(state, priv->connector); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, conn_state); + + mode = drm_kunit_display_mode_from_cea_vic(test, &priv->drm, 16); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, mode); + + KUNIT_ASSERT_EQ(test, conn_state->color_format, DRM_CONNECTOR_COLOR_FORMAT_AUTO); + + ret = drm_atomic_set_crtc_for_connector(conn_state, priv->crtc); + if (ret == -EDEADLK) { + drm_modeset_backoff(&ctx); + goto retry_commit; + } + KUNIT_ASSERT_EQ(test, ret, 0); + + crtc_state = drm_atomic_get_crtc_state(state, priv->crtc); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, crtc_state); + + ret = drm_atomic_set_mode_for_crtc(crtc_state, mode); + if (ret == -EDEADLK) { + drm_modeset_backoff(&ctx); + goto retry_commit; + } + KUNIT_ASSERT_EQ(test, ret, 0); + + crtc_state->enable = true; + crtc_state->active = true; + + ret = drm_atomic_commit(state); + if (ret == -EDEADLK) { + drm_modeset_backoff(&ctx); + goto retry_commit; + } + KUNIT_ASSERT_EQ(test, ret, 0); + + drm_test_bridge_chain_verify_fmt(test, priv, expected, ARRAY_SIZE(expected)); + + drm_modeset_drop_locks(&ctx); + drm_modeset_acquire_fini(&ctx); +} + +/* + * Test which checks that in a configuration of bridge chains where an RGB + * producer is hooked to a YUV444-only pass-through, the atomic commit fails as + * the bridge format selection cannot find a valid sequence of bus formats. + */ +static void drm_test_bridge_rgb_yuv_no_path(struct kunit *test) +{ + static const struct drm_bridge_funcs *funcs[] = { + &rgb_producer_funcs, + &yuv8_passthrough_funcs, + &rgb8_yuv444_yuv422_passthrough_funcs, + }; + struct drm_connector_state *conn_state; + struct drm_modeset_acquire_ctx ctx; + struct drm_bridge_chain_priv *priv; + struct drm_crtc_state *crtc_state; + struct drm_atomic_commit *state; + struct drm_display_mode *mode; + int ret; + + priv = drm_test_bridge_chain_init(test, ARRAY_SIZE(funcs), funcs); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, priv); + + drm_modeset_acquire_init(&ctx, 0); + + state = drm_kunit_helper_atomic_state_alloc(test, &priv->drm, &ctx); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, state); + +retry_commit: + conn_state = drm_atomic_get_connector_state(state, priv->connector); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, conn_state); + + mode = drm_kunit_display_mode_from_cea_vic(test, &priv->drm, 16); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, mode); + + ret = drm_atomic_set_crtc_for_connector(conn_state, priv->crtc); + if (ret == -EDEADLK) { + drm_modeset_backoff(&ctx); + goto retry_commit; + } + KUNIT_ASSERT_EQ(test, ret, 0); + + crtc_state = drm_atomic_get_crtc_state(state, priv->crtc); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, crtc_state); + + ret = drm_atomic_set_mode_for_crtc(crtc_state, mode); + if (ret == -EDEADLK) { + drm_modeset_backoff(&ctx); + goto retry_commit; + } + KUNIT_ASSERT_EQ(test, ret, 0); + + crtc_state->enable = true; + crtc_state->active = true; + + ret = drm_atomic_commit(state); + if (ret == -EDEADLK) { + drm_modeset_backoff(&ctx); + goto retry_commit; + } + KUNIT_EXPECT_EQ(test, ret, -ENOTSUPP); + + drm_modeset_drop_locks(&ctx); + drm_modeset_acquire_fini(&ctx); +} + +static struct kunit_case drm_bridge_bus_fmt_tests[] = { + KUNIT_CASE(drm_test_bridge_rgb_yuv_rgb), + KUNIT_CASE(drm_test_bridge_must_convert_to_yuv444), + KUNIT_CASE(drm_test_bridge_hdmi_auto_rgb), + KUNIT_CASE(drm_test_bridge_auto_first), + KUNIT_CASE(drm_test_bridge_rgb_yuv_no_path), + { } +}; + +static struct kunit_suite drm_bridge_bus_fmt_test_suite = { + .name = "drm_bridge_bus_fmt", + .test_cases = drm_bridge_bus_fmt_tests, +}; + kunit_test_suites( &drm_bridge_get_current_state_test_suite, &drm_bridge_helper_reset_crtc_test_suite, &drm_bridge_alloc_test_suite, + &drm_bridge_bus_fmt_test_suite, ); MODULE_AUTHOR("Maxime Ripard <mripard@kernel.org>"); MODULE_AUTHOR("Luca Ceresoli <luca.ceresoli@bootlin.com>"); +MODULE_AUTHOR("Nicolas Frattaroli <nicolas.frattaroli@collabora.com>"); MODULE_DESCRIPTION("Kunit test for drm_bridge functions"); MODULE_LICENSE("GPL"); diff --git a/drivers/gpu/drm/tests/drm_hdmi_state_helper_test.c b/drivers/gpu/drm/tests/drm_hdmi_state_helper_test.c index c9819c3fc635..353a261d42da 100644 --- a/drivers/gpu/drm/tests/drm_hdmi_state_helper_test.c +++ b/drivers/gpu/drm/tests/drm_hdmi_state_helper_test.c @@ -60,6 +60,40 @@ static struct drm_display_mode *find_preferred_mode(struct drm_connector *connec return preferred; } +static struct drm_display_mode *find_420_only_mode(struct drm_connector *connector) +{ + struct drm_device *drm = connector->dev; + struct drm_display_mode *mode; + + mutex_lock(&drm->mode_config.mutex); + list_for_each_entry(mode, &connector->modes, head) { + if (drm_mode_is_420_only(&connector->display_info, mode)) { + mutex_unlock(&drm->mode_config.mutex); + return mode; + } + } + mutex_unlock(&drm->mode_config.mutex); + + return NULL; +} + +static struct drm_display_mode *find_420_also_mode(struct drm_connector *connector) +{ + struct drm_device *drm = connector->dev; + struct drm_display_mode *mode; + + mutex_lock(&drm->mode_config.mutex); + list_for_each_entry(mode, &connector->modes, head) { + if (drm_mode_is_420_also(&connector->display_info, mode)) { + mutex_unlock(&drm->mode_config.mutex); + return mode; + } + } + mutex_unlock(&drm->mode_config.mutex); + + return NULL; +} + static int set_connector_edid(struct kunit *test, struct drm_connector *connector, const void *edid, size_t edid_len) { @@ -168,7 +202,7 @@ static const struct drm_connector_helper_funcs dummy_connector_helper_funcs = { static void dummy_hdmi_connector_reset(struct drm_connector *connector) { drm_atomic_helper_connector_reset(connector); - __drm_atomic_helper_connector_hdmi_reset(connector, connector->state); + __drm_atomic_helper_connector_hdmi_state_init(connector, connector->state); } static const struct drm_connector_funcs dummy_connector_funcs = { @@ -1547,6 +1581,7 @@ retry_conn_enable: * RGB/10bpc * - The chosen mode has a TMDS character rate lower than the display * supports in YUV422/12bpc. + * - The HDMI connector state's color format property is unset (i.e. AUTO) * * Then we will prefer to keep the RGB format with a lower bpc over * picking YUV422. @@ -1609,6 +1644,7 @@ retry_conn_enable: conn_state = conn->state; KUNIT_ASSERT_NOT_NULL(test, conn_state); + KUNIT_ASSERT_EQ(test, conn_state->color_format, DRM_CONNECTOR_COLOR_FORMAT_AUTO); KUNIT_EXPECT_EQ(test, conn_state->hdmi.output_bpc, 10); KUNIT_EXPECT_EQ(test, conn_state->hdmi.output_format, DRM_OUTPUT_COLOR_FORMAT_RGB444); @@ -1626,6 +1662,7 @@ retry_conn_enable: * RGB/8bpc * - The chosen mode has a TMDS character rate lower than the display * supports in YUV420/12bpc. + * - The HDMI connector state's color format property is unset (i.e. AUTO) * * Then we will prefer to keep the RGB format with a lower bpc over * picking YUV420. @@ -1687,6 +1724,7 @@ retry_conn_enable: conn_state = conn->state; KUNIT_ASSERT_NOT_NULL(test, conn_state); + KUNIT_ASSERT_EQ(test, conn_state->color_format, DRM_CONNECTOR_COLOR_FORMAT_AUTO); KUNIT_EXPECT_EQ(test, conn_state->hdmi.output_bpc, 8); KUNIT_EXPECT_EQ(test, conn_state->hdmi.output_format, DRM_OUTPUT_COLOR_FORMAT_RGB444); @@ -2198,6 +2236,217 @@ retry_conn_enable: drm_modeset_acquire_fini(&ctx); } +struct color_format_test_param { + enum drm_connector_color_format fmt; + enum drm_output_color_format expected; + int expected_ret; + const char *desc; +}; + +/* Test that if: + * - an HDMI connector supports RGB, YUV444, YUV422, and YUV420 + * - the display supports RGB, YUV444, YUV422, and YUV420 + * - the "color format" property is set + * then, for the preferred mode, for a given "color format" option: + * - DRM_CONNECTOR_COLOR_FORMAT_AUTO results in an output format of RGB + * - DRM_CONNECTOR_COLOR_FORMAT_YCBCR422 results in an output format of YUV422 + * - DRM_CONNECTOR_COLOR_FORMAT_YCBCR420 results in an output format of YUV420 + * - DRM_CONNECTOR_COLOR_FORMAT_YCBCR444 results in an output format of YUV444 + * - DRM_CONNECTOR_COLOR_FORMAT_RGB results in an HDMI output format of RGB + */ +static void drm_test_check_hdmi_color_format(struct kunit *test) +{ + const struct color_format_test_param *param = test->param_value; + struct drm_atomic_helper_connector_hdmi_priv *priv; + struct drm_connector_state *conn_state; + struct drm_modeset_acquire_ctx ctx; + struct drm_crtc_state *crtc_state; + struct drm_atomic_commit *state; + struct drm_display_info *info; + struct drm_display_mode *preferred; + int ret; + + priv = drm_kunit_helper_connector_hdmi_init_with_edid_funcs(test, + BIT(DRM_OUTPUT_COLOR_FORMAT_RGB444) | + BIT(DRM_OUTPUT_COLOR_FORMAT_YCBCR422) | + BIT(DRM_OUTPUT_COLOR_FORMAT_YCBCR420) | + BIT(DRM_OUTPUT_COLOR_FORMAT_YCBCR444), + 12, + &dummy_connector_hdmi_funcs, + test_edid_hdmi_4k_rgb_yuv420_dc_max_340mhz); + KUNIT_ASSERT_NOT_NULL(test, priv); + + drm_modeset_acquire_init(&ctx, 0); + + KUNIT_ASSERT_TRUE(test, priv->connector.ycbcr_420_allowed); + + info = &priv->connector.display_info; + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, info); + preferred = find_preferred_mode(&priv->connector); + KUNIT_ASSERT_TRUE(test, drm_mode_is_420(info, preferred)); + + state = drm_kunit_helper_atomic_state_alloc(test, &priv->drm, &ctx); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, state); + + conn_state = drm_atomic_get_connector_state(state, &priv->connector); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, conn_state); + + conn_state->color_format = param->fmt; + + ret = drm_atomic_set_crtc_for_connector(conn_state, priv->crtc); + KUNIT_ASSERT_EQ(test, ret, 0); + + crtc_state = drm_atomic_get_crtc_state(state, priv->crtc); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, crtc_state); + + ret = drm_atomic_set_mode_for_crtc(crtc_state, preferred); + KUNIT_ASSERT_EQ(test, ret, 0); + + crtc_state->enable = true; + crtc_state->active = true; + + ret = drm_atomic_check_only(state); + KUNIT_EXPECT_EQ(test, ret, param->expected_ret); + KUNIT_EXPECT_EQ(test, conn_state->hdmi.output_format, param->expected); + + drm_modeset_drop_locks(&ctx); + drm_modeset_acquire_fini(&ctx); +} + +static const struct color_format_test_param hdmi_color_format_params[] = { + { + .fmt = DRM_CONNECTOR_COLOR_FORMAT_AUTO, + .expected = DRM_OUTPUT_COLOR_FORMAT_RGB444, + .expected_ret = 0, + .desc = "AUTO -> RGB" + }, + { + .fmt = DRM_CONNECTOR_COLOR_FORMAT_YCBCR422, + .expected = DRM_OUTPUT_COLOR_FORMAT_YCBCR422, + .expected_ret = 0, + .desc = "YCBCR422 -> YUV422" + }, + { + .fmt = DRM_CONNECTOR_COLOR_FORMAT_YCBCR420, + .expected = DRM_OUTPUT_COLOR_FORMAT_YCBCR420, + .expected_ret = 0, + .desc = "YCBCR420 -> YUV420" + }, + { + .fmt = DRM_CONNECTOR_COLOR_FORMAT_YCBCR444, + .expected = DRM_OUTPUT_COLOR_FORMAT_YCBCR444, + .expected_ret = 0, + .desc = "YCBCR444 -> YUV444" + }, + { + .fmt = DRM_CONNECTOR_COLOR_FORMAT_RGB444, + .expected = DRM_OUTPUT_COLOR_FORMAT_RGB444, + .expected_ret = 0, + .desc = "RGB -> RGB" + }, +}; + +KUNIT_ARRAY_PARAM_DESC(check_hdmi_color_format, hdmi_color_format_params, desc); + +/* Test that if: + * - the HDMI connector supports RGB, YUV422, YUV420, and YUV444 + * - the display has a YUV420-only mode + * - the "color format" property is explicitly set (i.e. !AUTO) + * then: + * - color format DRM_CONNECTOR_COLOR_FORMAT_RGB444 will fail + * drm_atomic_check_only for the YUV420-only mode with -EINVAL + * - color format DRM_CONNECTOR_COLOR_FORMAT_YCBCR444 will fail + * drm_atomic_check_only for the YUV420-only mode with -EINVAL + * - color format DRM_CONNECTOR_COLOR_FORMAT_YCBCR422 will fail + * drm_atomic_check_only for the YUV420-only mode with -EINVAL + * - color format DRM_CONNECTOR_COLOR_FORMAT_YCBCR420 passes + * drm_atomic_check_only for the YUV420-only mode + */ +static void drm_test_check_hdmi_color_format_420_only(struct kunit *test) +{ + const struct color_format_test_param *param = test->param_value; + struct drm_atomic_helper_connector_hdmi_priv *priv; + struct drm_connector_state *conn_state; + struct drm_modeset_acquire_ctx ctx; + struct drm_crtc_state *crtc_state; + struct drm_atomic_commit *state; + struct drm_display_mode *dank; + int ret; + + priv = drm_kunit_helper_connector_hdmi_init_with_edid_funcs(test, + BIT(DRM_OUTPUT_COLOR_FORMAT_RGB444) | + BIT(DRM_OUTPUT_COLOR_FORMAT_YCBCR422) | + BIT(DRM_OUTPUT_COLOR_FORMAT_YCBCR420) | + BIT(DRM_OUTPUT_COLOR_FORMAT_YCBCR444), + 12, + &dummy_connector_hdmi_funcs, + test_edid_hdmi_1080p_rgb_yuv_4k_yuv420_dc_max_200mhz); + KUNIT_ASSERT_NOT_NULL(test, priv); + + drm_modeset_acquire_init(&ctx, 0); + + dank = find_420_only_mode(&priv->connector); + KUNIT_ASSERT_NOT_NULL(test, dank); + + state = drm_kunit_helper_atomic_state_alloc(test, &priv->drm, &ctx); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, state); + + conn_state = drm_atomic_get_connector_state(state, &priv->connector); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, conn_state); + + conn_state->color_format = param->fmt; + + ret = drm_atomic_set_crtc_for_connector(conn_state, priv->crtc); + KUNIT_ASSERT_EQ(test, ret, 0); + + crtc_state = drm_atomic_get_crtc_state(state, priv->crtc); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, crtc_state); + + ret = drm_atomic_set_mode_for_crtc(crtc_state, dank); + KUNIT_ASSERT_EQ(test, ret, 0); + + crtc_state->enable = true; + crtc_state->active = true; + + ret = drm_atomic_check_only(state); + KUNIT_EXPECT_EQ(test, ret, param->expected_ret); + if (!param->expected_ret) + KUNIT_EXPECT_EQ(test, conn_state->hdmi.output_format, param->expected); + + drm_modeset_drop_locks(&ctx); + drm_modeset_acquire_fini(&ctx); +}; + +static const struct color_format_test_param hdmi_color_format_420_only_params[] = { + { + .fmt = DRM_CONNECTOR_COLOR_FORMAT_RGB444, + .expected = DRM_OUTPUT_COLOR_FORMAT_RGB444, + .expected_ret = -EINVAL, + .desc = "RGB should fail" + }, + { + .fmt = DRM_CONNECTOR_COLOR_FORMAT_YCBCR444, + .expected = DRM_OUTPUT_COLOR_FORMAT_YCBCR444, + .expected_ret = -EINVAL, + .desc = "YUV444 should fail" + }, + { + .fmt = DRM_CONNECTOR_COLOR_FORMAT_YCBCR422, + .expected = DRM_OUTPUT_COLOR_FORMAT_YCBCR422, + .expected_ret = -EINVAL, + .desc = "YUV422 should fail" + }, + { + .fmt = DRM_CONNECTOR_COLOR_FORMAT_YCBCR420, + .expected = DRM_OUTPUT_COLOR_FORMAT_YCBCR420, + .expected_ret = 0, + .desc = "YUV420 should work" + }, +}; + +KUNIT_ARRAY_PARAM_DESC(check_hdmi_color_format_420_only, + hdmi_color_format_420_only_params, desc); + static struct kunit_case drm_atomic_helper_connector_hdmi_check_tests[] = { KUNIT_CASE(drm_test_check_broadcast_rgb_auto_cea_mode), KUNIT_CASE(drm_test_check_broadcast_rgb_auto_cea_mode_vic_1), @@ -2227,6 +2476,10 @@ static struct kunit_case drm_atomic_helper_connector_hdmi_check_tests[] = { KUNIT_CASE(drm_test_check_tmds_char_rate_rgb_8bpc), KUNIT_CASE(drm_test_check_tmds_char_rate_rgb_10bpc), KUNIT_CASE(drm_test_check_tmds_char_rate_rgb_12bpc), + KUNIT_CASE_PARAM(drm_test_check_hdmi_color_format, + check_hdmi_color_format_gen_params), + KUNIT_CASE_PARAM(drm_test_check_hdmi_color_format_420_only, + check_hdmi_color_format_420_only_gen_params), /* * TODO: We should have tests to check that a change in the * format triggers a CRTC mode change just like we do for the @@ -2509,11 +2762,103 @@ static void drm_test_check_mode_valid_reject_max_clock(struct kunit *test) KUNIT_EXPECT_EQ(test, preferred->clock, 25200); } +/* + * Test that drm_hdmi_connector_mode_valid() will accept modes that require a + * 4:2:0 chroma subsampling, even if said mode would violate maximum clock + * constraints if it used RGB 4:4:4. + */ +static void drm_test_check_mode_valid_yuv420_only_max_clock(struct kunit *test) +{ + struct drm_atomic_helper_connector_hdmi_priv *priv; + struct drm_display_mode *dank; + struct drm_connector *conn; + + priv = drm_kunit_helper_connector_hdmi_init_with_edid_funcs(test, + BIT(HDMI_COLORSPACE_RGB) | + BIT(HDMI_COLORSPACE_YUV420), + 8, + &dummy_connector_hdmi_funcs, + test_edid_hdmi_1080p_rgb_yuv_4k_yuv420_dc_max_200mhz); + KUNIT_ASSERT_NOT_NULL(test, priv); + + conn = &priv->connector; + KUNIT_ASSERT_EQ(test, conn->display_info.max_tmds_clock, 200 * 1000); + + dank = find_420_only_mode(conn); + KUNIT_ASSERT_NOT_NULL(test, dank); + KUNIT_EXPECT_EQ(test, dank->hdisplay, 3840); + KUNIT_EXPECT_EQ(test, dank->vdisplay, 2160); + + /* + * Note: The mode's "clock" here is not accurate to the actual TMDS + * clock that HDMI will use for a subsampled mode. Hence, why the mode's + * clock is above the .max_tmds_clock of 200MHz. + */ + KUNIT_EXPECT_EQ(test, dank->clock, 297000); +} + +/* + * Test that drm_hdmi_connector_mode_valid() will reject modes that require + * 4:2:0 chroma subsampling, if the connector does not support 4:2:0. + */ +static void +drm_test_check_mode_valid_reject_yuv420_only_connector(struct kunit *test) +{ + struct drm_atomic_helper_connector_hdmi_priv *priv; + struct drm_display_mode *dank; + struct drm_connector *conn; + + priv = drm_kunit_helper_connector_hdmi_init_with_edid_funcs(test, + BIT(HDMI_COLORSPACE_RGB), + 8, + &dummy_connector_hdmi_funcs, + test_edid_hdmi_1080p_rgb_yuv_4k_yuv420_dc_max_200mhz); + KUNIT_ASSERT_NOT_NULL(test, priv); + + conn = &priv->connector; + KUNIT_ASSERT_EQ(test, conn->display_info.max_tmds_clock, 200 * 1000); + + dank = find_420_only_mode(conn); + KUNIT_EXPECT_NULL(test, dank); +} + +/* + * Test that drm_hdmi_connector_mode_valid() will accept modes that allow (among + * other color formats) 4:2:0 chroma subsampling, even if the connector does not + * support 4:2:0, but the mode's clock works for RGB 4:4:4. + */ +static void +drm_test_check_mode_valid_accept_yuv420_also_connector_rgb(struct kunit *test) +{ + struct drm_atomic_helper_connector_hdmi_priv *priv; + struct drm_display_mode *mode; + struct drm_connector *conn; + + priv = drm_kunit_helper_connector_hdmi_init_with_edid_funcs(test, + BIT(HDMI_COLORSPACE_RGB), + 8, + &dummy_connector_hdmi_funcs, + test_edid_hdmi_4k_rgb_yuv420_dc_max_340mhz); + KUNIT_ASSERT_NOT_NULL(test, priv); + + conn = &priv->connector; + KUNIT_ASSERT_EQ(test, conn->display_info.max_tmds_clock, 340 * 1000); + + mode = find_420_also_mode(conn); + KUNIT_ASSERT_NOT_NULL(test, mode); + KUNIT_EXPECT_EQ(test, mode->hdisplay, 3840); + KUNIT_EXPECT_EQ(test, mode->vdisplay, 2160); + KUNIT_EXPECT_EQ(test, mode->clock, 297000); +} + static struct kunit_case drm_atomic_helper_connector_hdmi_mode_valid_tests[] = { KUNIT_CASE(drm_test_check_mode_valid), KUNIT_CASE(drm_test_check_mode_valid_reject), KUNIT_CASE(drm_test_check_mode_valid_reject_rate), KUNIT_CASE(drm_test_check_mode_valid_reject_max_clock), + KUNIT_CASE(drm_test_check_mode_valid_yuv420_only_max_clock), + KUNIT_CASE(drm_test_check_mode_valid_reject_yuv420_only_connector), + KUNIT_CASE(drm_test_check_mode_valid_accept_yuv420_also_connector_rgb), { } }; diff --git a/drivers/gpu/drm/tests/drm_kunit_edid.c b/drivers/gpu/drm/tests/drm_kunit_edid.c new file mode 100644 index 000000000000..c807ae0d9faa --- /dev/null +++ b/drivers/gpu/drm/tests/drm_kunit_edid.c @@ -0,0 +1,995 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include <linux/module.h> + +#include "drm_kunit_edid.h" + +/* + * edid-decode (hex): + * + * 00 ff ff ff ff ff ff 00 31 d8 2a 00 00 00 00 00 + * 00 21 01 03 81 a0 5a 78 0a 00 00 00 00 00 00 00 + * 00 00 00 00 00 00 01 01 01 01 01 01 01 01 01 01 + * 01 01 01 01 01 01 02 3a 80 18 71 38 2d 40 58 2c + * 45 00 40 84 63 00 00 1e 00 00 00 fc 00 54 65 73 + * 74 20 45 44 49 44 0a 20 20 20 00 00 00 fd 00 32 + * 46 1e 46 0f 00 0a 20 20 20 20 20 20 00 00 00 10 + * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ab + * + * ---------------- + * + * Block 0, Base EDID: + * EDID Structure Version & Revision: 1.3 + * Vendor & Product Identification: + * Manufacturer: LNX + * Model: 42 + * Made in: 2023 + * Basic Display Parameters & Features: + * Digital display + * DFP 1.x compatible TMDS + * Maximum image size: 160 cm x 90 cm + * Gamma: 2.20 + * RGB color display + * First detailed timing is the preferred timing + * Color Characteristics: + * Red : 0.0000, 0.0000 + * Green: 0.0000, 0.0000 + * Blue : 0.0000, 0.0000 + * White: 0.0000, 0.0000 + * Established Timings I & II: none + * Standard Timings: none + * Detailed Timing Descriptors: + * DTD 1: 1920x1080 60.000000 Hz 16:9 67.500 kHz 148.500000 MHz (1600 mm x 900 mm) + * Hfront 88 Hsync 44 Hback 148 Hpol P + * Vfront 4 Vsync 5 Vback 36 Vpol P + * Display Product Name: 'Test EDID' + * Display Range Limits: + * Monitor ranges (GTF): 50-70 Hz V, 30-70 kHz H, max dotclock 150 MHz + * Dummy Descriptor: + * Checksum: 0xab + * + * ---------------- + * + * edid-decode 1.30.0-5367 + * edid-decode SHA: 41ebf7135691 2025-05-01 10:19:22 + * + * EDID conformity: PASS + */ +const unsigned char test_edid_dvi_1080p[] = { + 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x31, 0xd8, 0x2a, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0x01, 0x03, 0x81, 0xa0, 0x5a, 0x78, + 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x3a, 0x80, 0x18, 0x71, 0x38, + 0x2d, 0x40, 0x58, 0x2c, 0x45, 0x00, 0x40, 0x84, 0x63, 0x00, 0x00, 0x1e, + 0x00, 0x00, 0x00, 0xfc, 0x00, 0x54, 0x65, 0x73, 0x74, 0x20, 0x45, 0x44, + 0x49, 0x44, 0x0a, 0x20, 0x20, 0x20, 0x00, 0x00, 0x00, 0xfd, 0x00, 0x32, + 0x46, 0x1e, 0x46, 0x0f, 0x00, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xab +}; +EXPORT_SYMBOL(test_edid_dvi_1080p); + +/* + * + * This edid is intentionally broken with the 100MHz limit. It's meant + * to be used only with tests in unusual situations. + * + * edid-decode (hex): + * + * 00 ff ff ff ff ff ff 00 31 d8 2a 00 00 00 00 00 + * 00 21 01 03 81 a0 5a 78 02 00 00 00 00 00 00 00 + * 00 00 00 20 00 00 01 01 01 01 01 01 01 01 01 01 + * 01 01 01 01 01 01 02 3a 80 18 71 38 2d 40 58 2c + * 45 00 40 84 63 00 00 1e 00 00 00 fc 00 54 65 73 + * 74 20 45 44 49 44 0a 20 20 20 00 00 00 fd 00 32 + * 46 1e 46 0f 00 0a 20 20 20 20 20 20 00 00 00 10 + * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 92 + * + * 02 03 15 81 e3 05 00 20 41 10 e2 00 4a 67 03 0c + * 00 12 34 00 14 00 00 00 00 00 00 00 00 00 00 00 + * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 10 + * + * ---------------- + * + * Block 0, Base EDID: + * EDID Structure Version & Revision: 1.3 + * Vendor & Product Identification: + * Manufacturer: LNX + * Model: 42 + * Made in: 2023 + * Basic Display Parameters & Features: + * Digital display + * DFP 1.x compatible TMDS + * Maximum image size: 160 cm x 90 cm + * Gamma: 2.20 + * Monochrome or grayscale display + * First detailed timing is the preferred timing + * Color Characteristics: + * Red : 0.0000, 0.0000 + * Green: 0.0000, 0.0000 + * Blue : 0.0000, 0.0000 + * White: 0.0000, 0.0000 + * Established Timings I & II: + * DMT 0x04: 640x480 59.940476 Hz 4:3 31.469 kHz 25.175000 MHz + * Standard Timings: none + * Detailed Timing Descriptors: + * DTD 1: 1920x1080 60.000000 Hz 16:9 67.500 kHz 148.500000 MHz (1600 mm x 900 mm) + * Hfront 88 Hsync 44 Hback 148 Hpol P + * Vfront 4 Vsync 5 Vback 36 Vpol P + * Display Product Name: 'Test EDID' + * Display Range Limits: + * Monitor ranges (GTF): 50-70 Hz V, 30-70 kHz H, max dotclock 150 MHz + * Dummy Descriptor: + * Extension blocks: 1 + * Checksum: 0x92 + * + * ---------------- + * + * Block 1, CTA-861 Extension Block: + * Revision: 3 + * Underscans IT Video Formats by default + * Native detailed modes: 1 + * Colorimetry Data Block: + * sRGB + * Video Data Block: + * VIC 16: 1920x1080 60.000000 Hz 16:9 67.500 kHz 148.500000 MHz + * Video Capability Data Block: + * YCbCr quantization: No Data + * RGB quantization: Selectable (via AVI Q) + * PT scan behavior: No Data + * IT scan behavior: Always Underscanned + * CE scan behavior: Always Underscanned + * Vendor-Specific Data Block (HDMI), OUI 00-0C-03: + * Source physical address: 1.2.3.4 + * Maximum TMDS clock: 100 MHz + * Checksum: 0x10 Unused space in Extension Block: 106 bytes + * + * ---------------- + * + * edid-decode 1.30.0-5367 + * edid-decode SHA: 41ebf7135691 2025-05-01 10:19:22 + * + * Failures: + * + * EDID: + * CTA-861: The maximum HDMI TMDS clock is 100000 kHz, but one or more video timings go up to 148500 kHz. + * + * EDID conformity: FAIL + */ +const unsigned char test_edid_hdmi_1080p_rgb_max_100mhz[] = { + 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x31, 0xd8, 0x2a, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0x01, 0x03, 0x81, 0xa0, 0x5a, 0x78, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, + 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x3a, 0x80, 0x18, 0x71, 0x38, + 0x2d, 0x40, 0x58, 0x2c, 0x45, 0x00, 0x40, 0x84, 0x63, 0x00, 0x00, 0x1e, + 0x00, 0x00, 0x00, 0xfc, 0x00, 0x54, 0x65, 0x73, 0x74, 0x20, 0x45, 0x44, + 0x49, 0x44, 0x0a, 0x20, 0x20, 0x20, 0x00, 0x00, 0x00, 0xfd, 0x00, 0x32, + 0x46, 0x1e, 0x46, 0x0f, 0x00, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x92, 0x02, 0x03, 0x15, 0x81, + 0xe3, 0x05, 0x00, 0x20, 0x41, 0x10, 0xe2, 0x00, 0x4a, 0x67, 0x03, 0x0c, + 0x00, 0x12, 0x34, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x10 +}; +EXPORT_SYMBOL(test_edid_hdmi_1080p_rgb_max_100mhz); + +/* + * edid-decode (hex): + * + * 00 ff ff ff ff ff ff 00 31 d8 2a 00 00 00 00 00 + * 00 21 01 03 81 a0 5a 78 02 00 00 00 00 00 00 00 + * 00 00 00 20 00 00 01 01 01 01 01 01 01 01 01 01 + * 01 01 01 01 01 01 02 3a 80 18 71 38 2d 40 58 2c + * 45 00 40 84 63 00 00 1e 00 00 00 fc 00 54 65 73 + * 74 20 45 44 49 44 0a 20 20 20 00 00 00 fd 00 32 + * 46 1e 46 0f 00 0a 20 20 20 20 20 20 00 00 00 10 + * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 92 + * + * 02 03 15 81 e3 05 00 20 41 10 e2 00 4a 67 03 0c + * 00 12 34 00 28 00 00 00 00 00 00 00 00 00 00 00 + * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 fc + * + * ---------------- + * + * Block 0, Base EDID: + * EDID Structure Version & Revision: 1.3 + * Vendor & Product Identification: + * Manufacturer: LNX + * Model: 42 + * Made in: 2023 + * Basic Display Parameters & Features: + * Digital display + * DFP 1.x compatible TMDS + * Maximum image size: 160 cm x 90 cm + * Gamma: 2.20 + * Monochrome or grayscale display + * First detailed timing is the preferred timing + * Color Characteristics: + * Red : 0.0000, 0.0000 + * Green: 0.0000, 0.0000 + * Blue : 0.0000, 0.0000 + * White: 0.0000, 0.0000 + * Established Timings I & II: + * DMT 0x04: 640x480 59.940476 Hz 4:3 31.469 kHz 25.175000 MHz + * Standard Timings: none + * Detailed Timing Descriptors: + * DTD 1: 1920x1080 60.000000 Hz 16:9 67.500 kHz 148.500000 MHz (1600 mm x 900 mm) + * Hfront 88 Hsync 44 Hback 148 Hpol P + * Vfront 4 Vsync 5 Vback 36 Vpol P + * Display Product Name: 'Test EDID' + * Display Range Limits: + * Monitor ranges (GTF): 50-70 Hz V, 30-70 kHz H, max dotclock 150 MHz + * Dummy Descriptor: + * Extension blocks: 1 + * Checksum: 0x92 + * + * ---------------- + * + * Block 1, CTA-861 Extension Block: + * Revision: 3 + * Underscans IT Video Formats by default + * Native detailed modes: 1 + * Colorimetry Data Block: + * sRGB + * Video Data Block: + * VIC 16: 1920x1080 60.000000 Hz 16:9 67.500 kHz 148.500000 MHz + * Video Capability Data Block: + * YCbCr quantization: No Data + * RGB quantization: Selectable (via AVI Q) + * PT scan behavior: No Data + * IT scan behavior: Always Underscanned + * CE scan behavior: Always Underscanned + * Vendor-Specific Data Block (HDMI), OUI 00-0C-03: + * Source physical address: 1.2.3.4 + * Maximum TMDS clock: 200 MHz + * Checksum: 0xfc Unused space in Extension Block: 106 bytes + * + * ---------------- + * + * edid-decode 1.30.0-5367 + * edid-decode SHA: 41ebf7135691 2025-05-01 10:19:22 + * + * EDID conformity: PASS + */ +const unsigned char test_edid_hdmi_1080p_rgb_max_200mhz[] = { + 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x31, 0xd8, 0x2a, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0x01, 0x03, 0x81, 0xa0, 0x5a, 0x78, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, + 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x3a, 0x80, 0x18, 0x71, 0x38, + 0x2d, 0x40, 0x58, 0x2c, 0x45, 0x00, 0x40, 0x84, 0x63, 0x00, 0x00, 0x1e, + 0x00, 0x00, 0x00, 0xfc, 0x00, 0x54, 0x65, 0x73, 0x74, 0x20, 0x45, 0x44, + 0x49, 0x44, 0x0a, 0x20, 0x20, 0x20, 0x00, 0x00, 0x00, 0xfd, 0x00, 0x32, + 0x46, 0x1e, 0x46, 0x0f, 0x00, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x92, 0x02, 0x03, 0x15, 0x81, + 0xe3, 0x05, 0x00, 0x20, 0x41, 0x10, 0xe2, 0x00, 0x4a, 0x67, 0x03, 0x0c, + 0x00, 0x12, 0x34, 0x00, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xfc +}; +EXPORT_SYMBOL(test_edid_hdmi_1080p_rgb_max_200mhz); + +/* + * edid-decode (hex): + * + * 00 ff ff ff ff ff ff 00 31 d8 2a 00 00 00 00 00 + * 00 21 01 03 81 a0 5a 78 02 00 00 00 00 00 00 00 + * 00 00 00 20 00 00 01 01 01 01 01 01 01 01 01 01 + * 01 01 01 01 01 01 02 3a 80 18 71 38 2d 40 58 2c + * 45 00 40 84 63 00 00 1e 00 00 00 fc 00 54 65 73 + * 74 20 45 44 49 44 0a 20 20 20 00 00 00 fd 00 32 + * 46 1e 46 0f 00 0a 20 20 20 20 20 20 00 00 00 10 + * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 92 + * + * 02 03 1c 81 e3 05 c0 20 41 10 e2 00 4a 67 03 0c + * 00 12 34 00 28 e6 06 05 01 52 52 51 00 00 00 00 + * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 4e + * + * ---------------- + * + * Block 0, Base EDID: + * EDID Structure Version & Revision: 1.3 + * Vendor & Product Identification: + * Manufacturer: LNX + * Model: 42 + * Made in: 2023 + * Basic Display Parameters & Features: + * Digital display + * DFP 1.x compatible TMDS + * Maximum image size: 160 cm x 90 cm + * Gamma: 2.20 + * Monochrome or grayscale display + * First detailed timing is the preferred timing + * Color Characteristics: + * Red : 0.0000, 0.0000 + * Green: 0.0000, 0.0000 + * Blue : 0.0000, 0.0000 + * White: 0.0000, 0.0000 + * Established Timings I & II: + * DMT 0x04: 640x480 59.940476 Hz 4:3 31.469 kHz 25.175000 MHz + * Standard Timings: none + * Detailed Timing Descriptors: + * DTD 1: 1920x1080 60.000000 Hz 16:9 67.500 kHz 148.500000 MHz (1600 mm x 900 mm) + * Hfront 88 Hsync 44 Hback 148 Hpol P + * Vfront 4 Vsync 5 Vback 36 Vpol P + * Display Product Name: 'Test EDID' + * Display Range Limits: + * Monitor ranges (GTF): 50-70 Hz V, 30-70 kHz H, max dotclock 150 MHz + * Dummy Descriptor: + * Extension blocks: 1 + * Checksum: 0x92 + * + * ---------------- + * + * Block 1, CTA-861 Extension Block: + * Revision: 3 + * Underscans IT Video Formats by default + * Native detailed modes: 1 + * Colorimetry Data Block: + * BT2020YCC + * BT2020RGB + * sRGB + * Video Data Block: + * VIC 16: 1920x1080 60.000000 Hz 16:9 67.500 kHz 148.500000 MHz + * Video Capability Data Block: + * YCbCr quantization: No Data + * RGB quantization: Selectable (via AVI Q) + * PT scan behavior: No Data + * IT scan behavior: Always Underscanned + * CE scan behavior: Always Underscanned + * Vendor-Specific Data Block (HDMI), OUI 00-0C-03: + * Source physical address: 1.2.3.4 + * Maximum TMDS clock: 200 MHz + * HDR Static Metadata Data Block: + * Electro optical transfer functions: + * Traditional gamma - SDR luminance range + * SMPTE ST2084 + * Supported static metadata descriptors: + * Static metadata type 1 + * Desired content max luminance: 82 (295.365 cd/m^2) + * Desired content max frame-average luminance: 82 (295.365 cd/m^2) + * Desired content min luminance: 81 (0.298 cd/m^2) + * Checksum: 0x4e Unused space in Extension Block: 99 bytes + * + * ---------------- + * + * edid-decode 1.31.0-5387 + * edid-decode SHA: 5508bc4301ac 2025-08-25 08:14:22 + * + * EDID conformity: PASS + */ +const unsigned char test_edid_hdmi_1080p_rgb_max_200mhz_hdr[] = { + 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x31, 0xd8, 0x2a, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0x01, 0x03, 0x81, 0xa0, 0x5a, 0x78, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, + 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x3a, 0x80, 0x18, 0x71, 0x38, + 0x2d, 0x40, 0x58, 0x2c, 0x45, 0x00, 0x40, 0x84, 0x63, 0x00, 0x00, 0x1e, + 0x00, 0x00, 0x00, 0xfc, 0x00, 0x54, 0x65, 0x73, 0x74, 0x20, 0x45, 0x44, + 0x49, 0x44, 0x0a, 0x20, 0x20, 0x20, 0x00, 0x00, 0x00, 0xfd, 0x00, 0x32, + 0x46, 0x1e, 0x46, 0x0f, 0x00, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x92, 0x02, 0x03, 0x1c, 0x81, + 0xe3, 0x05, 0xc0, 0x20, 0x41, 0x10, 0xe2, 0x00, 0x4a, 0x67, 0x03, 0x0c, + 0x00, 0x12, 0x34, 0x78, 0x28, 0xe6, 0x06, 0x05, 0x01, 0x52, 0x52, 0x51, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xd6, +}; +EXPORT_SYMBOL(test_edid_hdmi_1080p_rgb_max_200mhz_hdr); + +/* + * edid-decode (hex): + * + * 00 ff ff ff ff ff ff 00 31 d8 2a 00 00 00 00 00 + * 00 21 01 03 81 a0 5a 78 02 00 00 00 00 00 00 00 + * 00 00 00 20 00 00 01 01 01 01 01 01 01 01 01 01 + * 01 01 01 01 01 01 02 3a 80 18 71 38 2d 40 58 2c + * 45 00 40 84 63 00 00 1e 00 00 00 fc 00 54 65 73 + * 74 20 45 44 49 44 0a 20 20 20 00 00 00 fd 00 32 + * 46 1e 46 0f 00 0a 20 20 20 20 20 20 00 00 00 10 + * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 92 + * + * 02 03 15 81 e3 05 00 20 41 10 e2 00 4a 67 03 0c + * 00 12 34 00 44 00 00 00 00 00 00 00 00 00 00 00 + * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 e0 + * + * ---------------- + * + * Block 0, Base EDID: + * EDID Structure Version & Revision: 1.3 + * Vendor & Product Identification: + * Manufacturer: LNX + * Model: 42 + * Made in: 2023 + * Basic Display Parameters & Features: + * Digital display + * DFP 1.x compatible TMDS + * Maximum image size: 160 cm x 90 cm + * Gamma: 2.20 + * Monochrome or grayscale display + * First detailed timing is the preferred timing + * Color Characteristics: + * Red : 0.0000, 0.0000 + * Green: 0.0000, 0.0000 + * Blue : 0.0000, 0.0000 + * White: 0.0000, 0.0000 + * Established Timings I & II: + * DMT 0x04: 640x480 59.940476 Hz 4:3 31.469 kHz 25.175000 MHz + * Standard Timings: none + * Detailed Timing Descriptors: + * DTD 1: 1920x1080 60.000000 Hz 16:9 67.500 kHz 148.500000 MHz (1600 mm x 900 mm) + * Hfront 88 Hsync 44 Hback 148 Hpol P + * Vfront 4 Vsync 5 Vback 36 Vpol P + * Display Product Name: 'Test EDID' + * Display Range Limits: + * Monitor ranges (GTF): 50-70 Hz V, 30-70 kHz H, max dotclock 150 MHz + * Dummy Descriptor: + * Extension blocks: 1 + * Checksum: 0x92 + * + * ---------------- + * + * Block 1, CTA-861 Extension Block: + * Revision: 3 + * Underscans IT Video Formats by default + * Native detailed modes: 1 + * Colorimetry Data Block: + * sRGB + * Video Data Block: + * VIC 16: 1920x1080 60.000000 Hz 16:9 67.500 kHz 148.500000 MHz + * Video Capability Data Block: + * YCbCr quantization: No Data + * RGB quantization: Selectable (via AVI Q) + * PT scan behavior: No Data + * IT scan behavior: Always Underscanned + * CE scan behavior: Always Underscanned + * Vendor-Specific Data Block (HDMI), OUI 00-0C-03: + * Source physical address: 1.2.3.4 + * Maximum TMDS clock: 340 MHz + * Checksum: 0xe0 Unused space in Extension Block: 106 bytes + * + * ---------------- + * + * edid-decode 1.30.0-5367 + * edid-decode SHA: 41ebf7135691 2025-05-01 10:19:22 + * + * EDID conformity: PASS + */ +const unsigned char test_edid_hdmi_1080p_rgb_max_340mhz[] = { + 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x31, 0xd8, 0x2a, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0x01, 0x03, 0x81, 0xa0, 0x5a, 0x78, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, + 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x3a, 0x80, 0x18, 0x71, 0x38, + 0x2d, 0x40, 0x58, 0x2c, 0x45, 0x00, 0x40, 0x84, 0x63, 0x00, 0x00, 0x1e, + 0x00, 0x00, 0x00, 0xfc, 0x00, 0x54, 0x65, 0x73, 0x74, 0x20, 0x45, 0x44, + 0x49, 0x44, 0x0a, 0x20, 0x20, 0x20, 0x00, 0x00, 0x00, 0xfd, 0x00, 0x32, + 0x46, 0x1e, 0x46, 0x0f, 0x00, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x92, 0x02, 0x03, 0x15, 0x81, + 0xe3, 0x05, 0x00, 0x20, 0x41, 0x10, 0xe2, 0x00, 0x4a, 0x67, 0x03, 0x0c, + 0x00, 0x12, 0x34, 0x00, 0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xe0 +}; +EXPORT_SYMBOL(test_edid_hdmi_1080p_rgb_max_340mhz); + +/* + * edid-decode (hex): + * + * 00 ff ff ff ff ff ff 00 31 d8 2a 00 00 00 00 00 + * 00 21 01 03 81 a0 5a 78 1a 00 00 00 00 00 00 00 + * 00 00 00 20 00 00 01 01 01 01 01 01 01 01 01 01 + * 01 01 01 01 01 01 02 3a 80 18 71 38 2d 40 58 2c + * 45 00 40 84 63 00 00 1e 00 00 00 fc 00 54 65 73 + * 74 20 45 44 49 44 0a 20 20 20 00 00 00 fd 00 32 + * 46 1e 46 0f 00 0a 20 20 20 20 20 20 00 00 00 10 + * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 7a + * + * 02 03 15 b1 e3 05 00 20 41 10 e2 00 ca 67 03 0c + * 00 12 34 78 28 00 00 00 00 00 00 00 00 00 00 00 + * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 d4 + * + * ---------------- + * + * Block 0, Base EDID: + * EDID Structure Version & Revision: 1.3 + * Vendor & Product Identification: + * Manufacturer: LNX + * Model: 42 + * Made in: 2023 + * Basic Display Parameters & Features: + * Digital display + * DFP 1.x compatible TMDS + * Maximum image size: 160 cm x 90 cm + * Gamma: 2.20 + * Undefined display color type + * First detailed timing is the preferred timing + * Color Characteristics: + * Red : 0.0000, 0.0000 + * Green: 0.0000, 0.0000 + * Blue : 0.0000, 0.0000 + * White: 0.0000, 0.0000 + * Established Timings I & II: + * DMT 0x04: 640x480 59.940476 Hz 4:3 31.469 kHz 25.175000 MHz + * Standard Timings: none + * Detailed Timing Descriptors: + * DTD 1: 1920x1080 60.000000 Hz 16:9 67.500 kHz 148.500000 MHz (1600 mm x 900 mm) + * Hfront 88 Hsync 44 Hback 148 Hpol P + * Vfront 4 Vsync 5 Vback 36 Vpol P + * Display Product Name: 'Test EDID' + * Display Range Limits: + * Monitor ranges (GTF): 50-70 Hz V, 30-70 kHz H, max dotclock 150 MHz + * Dummy Descriptor: + * Extension blocks: 1 + * Checksum: 0x7a + * + * ---------------- + * + * Block 1, CTA-861 Extension Block: + * Revision: 3 + * Underscans IT Video Formats by default + * Supports YCbCr 4:4:4 + * Supports YCbCr 4:2:2 + * Native detailed modes: 1 + * Colorimetry Data Block: + * sRGB + * Video Data Block: + * VIC 16: 1920x1080 60.000000 Hz 16:9 67.500 kHz 148.500000 MHz + * Video Capability Data Block: + * YCbCr quantization: Selectable (via AVI YQ) + * RGB quantization: Selectable (via AVI Q) + * PT scan behavior: No Data + * IT scan behavior: Always Underscanned + * CE scan behavior: Always Underscanned + * Vendor-Specific Data Block (HDMI), OUI 00-0C-03: + * Source physical address: 1.2.3.4 + * DC_48bit + * DC_36bit + * DC_30bit + * DC_Y444 + * Maximum TMDS clock: 200 MHz + * Checksum: 0xd4 Unused space in Extension Block: 106 bytes + * + * ---------------- + * + * edid-decode 1.30.0-5367 + * edid-decode SHA: 41ebf7135691 2025-05-01 10:19:22 + * + * EDID conformity: PASS + */ +const unsigned char test_edid_hdmi_1080p_rgb_yuv_dc_max_200mhz[] = { + 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x31, 0xd8, 0x2a, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0x01, 0x03, 0x81, 0xa0, 0x5a, 0x78, + 0x1a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, + 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x3a, 0x80, 0x18, 0x71, 0x38, + 0x2d, 0x40, 0x58, 0x2c, 0x45, 0x00, 0x40, 0x84, 0x63, 0x00, 0x00, 0x1e, + 0x00, 0x00, 0x00, 0xfc, 0x00, 0x54, 0x65, 0x73, 0x74, 0x20, 0x45, 0x44, + 0x49, 0x44, 0x0a, 0x20, 0x20, 0x20, 0x00, 0x00, 0x00, 0xfd, 0x00, 0x32, + 0x46, 0x1e, 0x46, 0x0f, 0x00, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x7a, 0x02, 0x03, 0x15, 0xb1, + 0xe3, 0x05, 0x00, 0x20, 0x41, 0x10, 0xe2, 0x00, 0xca, 0x67, 0x03, 0x0c, + 0x00, 0x12, 0x34, 0x78, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xd4 +}; +EXPORT_SYMBOL(test_edid_hdmi_1080p_rgb_yuv_dc_max_200mhz); + +/* + * edid-decode (hex): + * + * 00 ff ff ff ff ff ff 00 31 d8 2a 00 00 00 00 00 + * 00 21 01 03 81 a0 5a 78 0a 00 00 00 00 00 00 00 + * 00 00 00 20 00 00 01 01 01 01 01 01 01 01 01 01 + * 01 01 01 01 01 01 02 3a 80 18 71 38 2d 40 58 2c + * 45 00 40 84 63 00 00 1e 00 00 00 fc 00 54 65 73 + * 74 20 45 44 49 44 0a 20 20 20 00 00 00 fd 00 32 + * 46 1e 46 0f 00 0a 20 20 20 20 20 20 00 00 00 10 + * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 8a + * + * 02 03 15 b1 e3 05 00 20 41 10 e2 00 ca 67 03 0c + * 00 12 34 78 44 00 00 00 00 00 00 00 00 00 00 00 + * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 b8 + * + * ---------------- + * + * Block 0, Base EDID: + * EDID Structure Version & Revision: 1.3 + * Vendor & Product Identification: + * Manufacturer: LNX + * Model: 42 + * Made in: 2023 + * Basic Display Parameters & Features: + * Digital display + * DFP 1.x compatible TMDS + * Maximum image size: 160 cm x 90 cm + * Gamma: 2.20 + * RGB color display + * First detailed timing is the preferred timing + * Color Characteristics: + * Red : 0.0000, 0.0000 + * Green: 0.0000, 0.0000 + * Blue : 0.0000, 0.0000 + * White: 0.0000, 0.0000 + * Established Timings I & II: + * DMT 0x04: 640x480 59.940476 Hz 4:3 31.469 kHz 25.175000 MHz + * Standard Timings: none + * Detailed Timing Descriptors: + * DTD 1: 1920x1080 60.000000 Hz 16:9 67.500 kHz 148.500000 MHz (1600 mm x 900 mm) + * Hfront 88 Hsync 44 Hback 148 Hpol P + * Vfront 4 Vsync 5 Vback 36 Vpol P + * Display Product Name: 'Test EDID' + * Display Range Limits: + * Monitor ranges (GTF): 50-70 Hz V, 30-70 kHz H, max dotclock 150 MHz + * Dummy Descriptor: + * Extension blocks: 1 + * Checksum: 0x8a + * + * ---------------- + * + * Block 1, CTA-861 Extension Block: + * Revision: 3 + * Underscans IT Video Formats by default + * Supports YCbCr 4:4:4 + * Supports YCbCr 4:2:2 + * Native detailed modes: 1 + * Colorimetry Data Block: + * sRGB + * Video Data Block: + * VIC 16: 1920x1080 60.000000 Hz 16:9 67.500 kHz 148.500000 MHz + * Video Capability Data Block: + * YCbCr quantization: Selectable (via AVI YQ) + * RGB quantization: Selectable (via AVI Q) + * PT scan behavior: No Data + * IT scan behavior: Always Underscanned + * CE scan behavior: Always Underscanned + * Vendor-Specific Data Block (HDMI), OUI 00-0C-03: + * Source physical address: 1.2.3.4 + * DC_48bit + * DC_36bit + * DC_30bit + * DC_Y444 + * Maximum TMDS clock: 340 MHz + * Checksum: 0xb8 Unused space in Extension Block: 106 bytes + * + * ---------------- + * + * edid-decode 1.30.0-5367 + * edid-decode SHA: 41ebf7135691 2025-05-01 10:19:22 + * + * EDID conformity: PASS + */ +const unsigned char test_edid_hdmi_1080p_rgb_yuv_dc_max_340mhz[] = { + 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x31, 0xd8, 0x2a, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0x01, 0x03, 0x81, 0xa0, 0x5a, 0x78, + 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, + 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x3a, 0x80, 0x18, 0x71, 0x38, + 0x2d, 0x40, 0x58, 0x2c, 0x45, 0x00, 0x40, 0x84, 0x63, 0x00, 0x00, 0x1e, + 0x00, 0x00, 0x00, 0xfc, 0x00, 0x54, 0x65, 0x73, 0x74, 0x20, 0x45, 0x44, + 0x49, 0x44, 0x0a, 0x20, 0x20, 0x20, 0x00, 0x00, 0x00, 0xfd, 0x00, 0x32, + 0x46, 0x1e, 0x46, 0x0f, 0x00, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x8a, 0x02, 0x03, 0x15, 0xb1, + 0xe3, 0x05, 0x00, 0x20, 0x41, 0x10, 0xe2, 0x00, 0xca, 0x67, 0x03, 0x0c, + 0x00, 0x12, 0x34, 0x78, 0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xb8 +}; +EXPORT_SYMBOL(test_edid_hdmi_1080p_rgb_yuv_dc_max_340mhz); + +/* + * Max resolution: + * - 1920x1080@60Hz with RGB, YUV444, YUV422 + * - 3840x2160@30Hz with YUV420 only + * Max BPC: 16 for all modes + * Max TMDS clock: 200 MHz + * + * edid-decode (hex): + * + * 00 ff ff ff ff ff ff 00 31 d8 34 00 00 00 00 00 + * ff 23 01 03 80 60 36 78 0f ee 91 a3 54 4c 99 26 + * 0f 50 54 20 00 00 01 01 01 01 01 01 01 01 01 01 + * 01 01 01 01 01 01 02 3a 80 18 71 38 2d 40 58 2c + * 45 00 c0 1c 32 00 00 1e 00 00 00 fc 00 54 65 73 + * 74 20 45 44 49 44 0a 20 20 20 00 00 00 fd 00 18 + * 55 18 5e 11 00 0a 20 20 20 20 20 20 00 00 00 10 + * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 bb + * + * 02 03 29 31 42 90 5f 6c 03 0c 00 10 00 78 28 20 + * 00 00 01 03 6d d8 5d c4 01 28 80 07 00 00 00 00 + * 00 00 e3 0f 00 00 e2 0e 5f 00 00 00 00 00 00 00 + * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ca + * + * ---------------- + * + * Block 0, Base EDID: + * EDID Structure Version & Revision: 1.3 + * Vendor & Product Identification: + * Manufacturer: LNX + * Model: 52 + * Model year: 2025 + * Basic Display Parameters & Features: + * Digital display + * Maximum image size: 96 cm x 54 cm + * Gamma: 2.20 + * RGB color display + * Default (sRGB) color space is primary color space + * First detailed timing is the preferred timing + * Supports GTF timings within operating range + * Color Characteristics: + * Red : 0.6396, 0.3300 + * Green: 0.2998, 0.5996 + * Blue : 0.1503, 0.0595 + * White: 0.3125, 0.3291 + * Established Timings I & II: + * DMT 0x04: 640x480 59.940476 Hz 4:3 31.469 kHz 25.175000 MHz + * Standard Timings: none + * Detailed Timing Descriptors: + * DTD 1: 1920x1080 60.000000 Hz 16:9 67.500 kHz 148.500000 MHz (960 mm x 540 mm) + * Hfront 88 Hsync 44 Hback 148 Hpol P + * Vfront 4 Vsync 5 Vback 36 Vpol P + * Display Product Name: 'Test EDID' + * Display Range Limits: + * Monitor ranges (GTF): 24-85 Hz V, 24-94 kHz H, max dotclock 170 MHz + * Dummy Descriptor: + * Extension blocks: 1 + * Checksum: 0xbb + * + * ---------------- + * + * Block 1, CTA-861 Extension Block: + * Revision: 3 + * Supports YCbCr 4:4:4 + * Supports YCbCr 4:2:2 + * Native detailed modes: 1 + * Video Data Block: + * VIC 16: 1920x1080 60.000000 Hz 16:9 67.500 kHz 148.500000 MHz (native) + * VIC 95: 3840x2160 30.000000 Hz 16:9 67.500 kHz 297.000000 MHz + * Vendor-Specific Data Block (HDMI), OUI 00-0C-03: + * Source physical address: 1.0.0.0 + * DC_48bit + * DC_36bit + * DC_30bit + * DC_Y444 + * Maximum TMDS clock: 200 MHz + * Extended HDMI video details: + * Vendor-Specific Data Block (HDMI Forum), OUI C4-5D-D8: + * Version: 1 + * Maximum TMDS Character Rate: 200 MHz + * SCDC Present + * Supports 16-bits/component Deep Color 4:2:0 Pixel Encoding + * Supports 12-bits/component Deep Color 4:2:0 Pixel Encoding + * Supports 10-bits/component Deep Color 4:2:0 Pixel Encoding + * YCbCr 4:2:0 Capability Map Data Block: + * Empty Capability Map + * YCbCr 4:2:0 Video Data Block: + * VIC 95: 3840x2160 30.000000 Hz 16:9 67.500 kHz 297.000000 MHz + * Checksum: 0xca + */ +const unsigned char test_edid_hdmi_1080p_rgb_yuv_4k_yuv420_dc_max_200mhz[] = { + 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x31, 0xd8, 0x34, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xff, 0x23, 0x01, 0x03, 0x80, 0x60, 0x36, 0x78, + 0x0f, 0xee, 0x91, 0xa3, 0x54, 0x4c, 0x99, 0x26, 0x0f, 0x50, 0x54, 0x20, + 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x3a, 0x80, 0x18, 0x71, 0x38, + 0x2d, 0x40, 0x58, 0x2c, 0x45, 0x00, 0xc0, 0x1c, 0x32, 0x00, 0x00, 0x1e, + 0x00, 0x00, 0x00, 0xfc, 0x00, 0x54, 0x65, 0x73, 0x74, 0x20, 0x45, 0x44, + 0x49, 0x44, 0x0a, 0x20, 0x20, 0x20, 0x00, 0x00, 0x00, 0xfd, 0x00, 0x18, + 0x55, 0x18, 0x5e, 0x11, 0x00, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xbb, 0x02, 0x03, 0x29, 0x31, + 0x42, 0x90, 0x5f, 0x6c, 0x03, 0x0c, 0x00, 0x10, 0x00, 0x78, 0x28, 0x20, + 0x00, 0x00, 0x01, 0x03, 0x6d, 0xd8, 0x5d, 0xc4, 0x01, 0x28, 0x80, 0x07, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe3, 0x0f, 0x00, 0x00, 0xe2, 0x0e, + 0x5f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xca +}; +EXPORT_SYMBOL(test_edid_hdmi_1080p_rgb_yuv_4k_yuv420_dc_max_200mhz); + +/* + * Max resolution: 3840x2160@30Hz with RGB, YUV444, YUV422, YUV420 + * Max BPC: 16 for all modes + * Max TMDS clock: 340 MHz + * + * edid-decode (hex): + * + * 00 ff ff ff ff ff ff 00 31 d8 34 00 00 00 00 00 + * ff 23 01 03 80 60 36 78 0f ee 91 a3 54 4c 99 26 + * 0f 50 54 20 00 00 01 01 01 01 01 01 01 01 01 01 + * 01 01 01 01 01 01 04 74 00 30 f2 70 5a 80 b0 58 + * 8a 00 40 84 63 00 00 1e 00 00 00 fc 00 54 65 73 + * 74 20 45 44 49 44 0a 20 20 20 00 00 00 fd 00 18 + * 55 18 5e 22 00 0a 20 20 20 20 20 20 00 00 00 10 + * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 ce + * + * 02 03 27 31 41 5f 6c 03 0c 00 10 00 78 44 20 00 + * 00 01 03 6d d8 5d c4 01 44 80 07 00 00 00 00 00 + * 00 e3 0f 01 00 e1 0e 00 00 00 00 00 00 00 00 00 + * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 84 + * + * ---------------- + * + * Block 0, Base EDID: + * EDID Structure Version & Revision: 1.3 + * Vendor & Product Identification: + * Manufacturer: LNX + * Model: 52 + * Model year: 2025 + * Basic Display Parameters & Features: + * Digital display + * Maximum image size: 96 cm x 54 cm + * Gamma: 2.20 + * RGB color display + * Default (sRGB) color space is primary color space + * First detailed timing is the preferred timing + * Supports GTF timings within operating range + * Color Characteristics: + * Red : 0.6396, 0.3300 + * Green: 0.2998, 0.5996 + * Blue : 0.1503, 0.0595 + * White: 0.3125, 0.3291 + * Established Timings I & II: + * DMT 0x04: 640x480 59.940476 Hz 4:3 31.469 kHz 25.175000 MHz + * Standard Timings: none + * Detailed Timing Descriptors: + * DTD 1: 3840x2160 30.000000 Hz 16:9 67.500 kHz 297.000000 MHz (1600 mm x 900 mm) + * Hfront 176 Hsync 88 Hback 296 Hpol P + * Vfront 8 Vsync 10 Vback 72 Vpol P + * Display Product Name: 'Test EDID' + * Display Range Limits: + * Monitor ranges (GTF): 24-85 Hz V, 24-94 kHz H, max dotclock 340 MHz + * Dummy Descriptor: + * Extension blocks: 1 + * Checksum: 0xce + * + * ---------------- + * + * Block 1, CTA-861 Extension Block: + * Revision: 3 + * Supports YCbCr 4:4:4 + * Supports YCbCr 4:2:2 + * Native detailed modes: 1 + * Video Data Block: + * VIC 95: 3840x2160 30.000000 Hz 16:9 67.500 kHz 297.000000 MHz + * Vendor-Specific Data Block (HDMI), OUI 00-0C-03: + * Source physical address: 1.0.0.0 + * DC_48bit + * DC_36bit + * DC_30bit + * DC_Y444 + * Maximum TMDS clock: 340 MHz + * Extended HDMI video details: + * Vendor-Specific Data Block (HDMI Forum), OUI C4-5D-D8: + * Version: 1 + * Maximum TMDS Character Rate: 340 MHz + * SCDC Present + * Supports 16-bits/component Deep Color 4:2:0 Pixel Encoding + * Supports 12-bits/component Deep Color 4:2:0 Pixel Encoding + * Supports 10-bits/component Deep Color 4:2:0 Pixel Encoding + * YCbCr 4:2:0 Capability Map Data Block: + * VIC 95: 3840x2160 30.000000 Hz 16:9 67.500 kHz 297.000000 MHz + * YCbCr 4:2:0 Video Data Block: + * Checksum: 0x84 + */ +const unsigned char test_edid_hdmi_4k_rgb_yuv420_dc_max_340mhz[] = { + 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x31, 0xd8, 0x34, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xff, 0x23, 0x01, 0x03, 0x80, 0x60, 0x36, 0x78, + 0x0f, 0xee, 0x91, 0xa3, 0x54, 0x4c, 0x99, 0x26, 0x0f, 0x50, 0x54, 0x20, + 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x04, 0x74, 0x00, 0x30, 0xf2, 0x70, + 0x5a, 0x80, 0xb0, 0x58, 0x8a, 0x00, 0x40, 0x84, 0x63, 0x00, 0x00, 0x1e, + 0x00, 0x00, 0x00, 0xfc, 0x00, 0x54, 0x65, 0x73, 0x74, 0x20, 0x45, 0x44, + 0x49, 0x44, 0x0a, 0x20, 0x20, 0x20, 0x00, 0x00, 0x00, 0xfd, 0x00, 0x18, + 0x55, 0x18, 0x5e, 0x22, 0x00, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xce, 0x02, 0x03, 0x27, 0x31, + 0x41, 0x5f, 0x6c, 0x03, 0x0c, 0x00, 0x10, 0x00, 0x78, 0x44, 0x20, 0x00, + 0x00, 0x01, 0x03, 0x6d, 0xd8, 0x5d, 0xc4, 0x01, 0x44, 0x80, 0x07, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xe3, 0x0f, 0x01, 0x00, 0xe1, 0x0e, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x84 +}; +EXPORT_SYMBOL(test_edid_hdmi_4k_rgb_yuv420_dc_max_340mhz); + +MODULE_AUTHOR("Maxime Ripard <mripard@kernel.org>"); +MODULE_DESCRIPTION("EDID Definitions for KUnit tests"); +MODULE_LICENSE("GPL"); diff --git a/drivers/gpu/drm/tests/drm_kunit_edid.h b/drivers/gpu/drm/tests/drm_kunit_edid.h index f4923157f5bf..28b4df93a555 100644 --- a/drivers/gpu/drm/tests/drm_kunit_edid.h +++ b/drivers/gpu/drm/tests/drm_kunit_edid.h @@ -3,981 +3,14 @@ #ifndef DRM_KUNIT_EDID_H_ #define DRM_KUNIT_EDID_H_ -/* - * edid-decode (hex): - * - * 00 ff ff ff ff ff ff 00 31 d8 2a 00 00 00 00 00 - * 00 21 01 03 81 a0 5a 78 0a 00 00 00 00 00 00 00 - * 00 00 00 00 00 00 01 01 01 01 01 01 01 01 01 01 - * 01 01 01 01 01 01 02 3a 80 18 71 38 2d 40 58 2c - * 45 00 40 84 63 00 00 1e 00 00 00 fc 00 54 65 73 - * 74 20 45 44 49 44 0a 20 20 20 00 00 00 fd 00 32 - * 46 1e 46 0f 00 0a 20 20 20 20 20 20 00 00 00 10 - * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ab - * - * ---------------- - * - * Block 0, Base EDID: - * EDID Structure Version & Revision: 1.3 - * Vendor & Product Identification: - * Manufacturer: LNX - * Model: 42 - * Made in: 2023 - * Basic Display Parameters & Features: - * Digital display - * DFP 1.x compatible TMDS - * Maximum image size: 160 cm x 90 cm - * Gamma: 2.20 - * RGB color display - * First detailed timing is the preferred timing - * Color Characteristics: - * Red : 0.0000, 0.0000 - * Green: 0.0000, 0.0000 - * Blue : 0.0000, 0.0000 - * White: 0.0000, 0.0000 - * Established Timings I & II: none - * Standard Timings: none - * Detailed Timing Descriptors: - * DTD 1: 1920x1080 60.000000 Hz 16:9 67.500 kHz 148.500000 MHz (1600 mm x 900 mm) - * Hfront 88 Hsync 44 Hback 148 Hpol P - * Vfront 4 Vsync 5 Vback 36 Vpol P - * Display Product Name: 'Test EDID' - * Display Range Limits: - * Monitor ranges (GTF): 50-70 Hz V, 30-70 kHz H, max dotclock 150 MHz - * Dummy Descriptor: - * Checksum: 0xab - * - * ---------------- - * - * edid-decode 1.30.0-5367 - * edid-decode SHA: 41ebf7135691 2025-05-01 10:19:22 - * - * EDID conformity: PASS - */ -static const unsigned char test_edid_dvi_1080p[] = { - 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x31, 0xd8, 0x2a, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0x01, 0x03, 0x81, 0xa0, 0x5a, 0x78, - 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x3a, 0x80, 0x18, 0x71, 0x38, - 0x2d, 0x40, 0x58, 0x2c, 0x45, 0x00, 0x40, 0x84, 0x63, 0x00, 0x00, 0x1e, - 0x00, 0x00, 0x00, 0xfc, 0x00, 0x54, 0x65, 0x73, 0x74, 0x20, 0x45, 0x44, - 0x49, 0x44, 0x0a, 0x20, 0x20, 0x20, 0x00, 0x00, 0x00, 0xfd, 0x00, 0x32, - 0x46, 0x1e, 0x46, 0x0f, 0x00, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xab -}; - -/* - * - * This edid is intentionally broken with the 100MHz limit. It's meant - * to be used only with tests in unusual situations. - * - * edid-decode (hex): - * - * 00 ff ff ff ff ff ff 00 31 d8 2a 00 00 00 00 00 - * 00 21 01 03 81 a0 5a 78 02 00 00 00 00 00 00 00 - * 00 00 00 20 00 00 01 01 01 01 01 01 01 01 01 01 - * 01 01 01 01 01 01 02 3a 80 18 71 38 2d 40 58 2c - * 45 00 40 84 63 00 00 1e 00 00 00 fc 00 54 65 73 - * 74 20 45 44 49 44 0a 20 20 20 00 00 00 fd 00 32 - * 46 1e 46 0f 00 0a 20 20 20 20 20 20 00 00 00 10 - * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 92 - * - * 02 03 15 81 e3 05 00 20 41 10 e2 00 4a 67 03 0c - * 00 12 34 00 14 00 00 00 00 00 00 00 00 00 00 00 - * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 10 - * - * ---------------- - * - * Block 0, Base EDID: - * EDID Structure Version & Revision: 1.3 - * Vendor & Product Identification: - * Manufacturer: LNX - * Model: 42 - * Made in: 2023 - * Basic Display Parameters & Features: - * Digital display - * DFP 1.x compatible TMDS - * Maximum image size: 160 cm x 90 cm - * Gamma: 2.20 - * Monochrome or grayscale display - * First detailed timing is the preferred timing - * Color Characteristics: - * Red : 0.0000, 0.0000 - * Green: 0.0000, 0.0000 - * Blue : 0.0000, 0.0000 - * White: 0.0000, 0.0000 - * Established Timings I & II: - * DMT 0x04: 640x480 59.940476 Hz 4:3 31.469 kHz 25.175000 MHz - * Standard Timings: none - * Detailed Timing Descriptors: - * DTD 1: 1920x1080 60.000000 Hz 16:9 67.500 kHz 148.500000 MHz (1600 mm x 900 mm) - * Hfront 88 Hsync 44 Hback 148 Hpol P - * Vfront 4 Vsync 5 Vback 36 Vpol P - * Display Product Name: 'Test EDID' - * Display Range Limits: - * Monitor ranges (GTF): 50-70 Hz V, 30-70 kHz H, max dotclock 150 MHz - * Dummy Descriptor: - * Extension blocks: 1 - * Checksum: 0x92 - * - * ---------------- - * - * Block 1, CTA-861 Extension Block: - * Revision: 3 - * Underscans IT Video Formats by default - * Native detailed modes: 1 - * Colorimetry Data Block: - * sRGB - * Video Data Block: - * VIC 16: 1920x1080 60.000000 Hz 16:9 67.500 kHz 148.500000 MHz - * Video Capability Data Block: - * YCbCr quantization: No Data - * RGB quantization: Selectable (via AVI Q) - * PT scan behavior: No Data - * IT scan behavior: Always Underscanned - * CE scan behavior: Always Underscanned - * Vendor-Specific Data Block (HDMI), OUI 00-0C-03: - * Source physical address: 1.2.3.4 - * Maximum TMDS clock: 100 MHz - * Checksum: 0x10 Unused space in Extension Block: 106 bytes - * - * ---------------- - * - * edid-decode 1.30.0-5367 - * edid-decode SHA: 41ebf7135691 2025-05-01 10:19:22 - * - * Failures: - * - * EDID: - * CTA-861: The maximum HDMI TMDS clock is 100000 kHz, but one or more video timings go up to 148500 kHz. - * - * EDID conformity: FAIL - */ -static const unsigned char test_edid_hdmi_1080p_rgb_max_100mhz[] = { - 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x31, 0xd8, 0x2a, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0x01, 0x03, 0x81, 0xa0, 0x5a, 0x78, - 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, - 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x3a, 0x80, 0x18, 0x71, 0x38, - 0x2d, 0x40, 0x58, 0x2c, 0x45, 0x00, 0x40, 0x84, 0x63, 0x00, 0x00, 0x1e, - 0x00, 0x00, 0x00, 0xfc, 0x00, 0x54, 0x65, 0x73, 0x74, 0x20, 0x45, 0x44, - 0x49, 0x44, 0x0a, 0x20, 0x20, 0x20, 0x00, 0x00, 0x00, 0xfd, 0x00, 0x32, - 0x46, 0x1e, 0x46, 0x0f, 0x00, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x92, 0x02, 0x03, 0x15, 0x81, - 0xe3, 0x05, 0x00, 0x20, 0x41, 0x10, 0xe2, 0x00, 0x4a, 0x67, 0x03, 0x0c, - 0x00, 0x12, 0x34, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x10 -}; - -/* - * edid-decode (hex): - * - * 00 ff ff ff ff ff ff 00 31 d8 2a 00 00 00 00 00 - * 00 21 01 03 81 a0 5a 78 02 00 00 00 00 00 00 00 - * 00 00 00 20 00 00 01 01 01 01 01 01 01 01 01 01 - * 01 01 01 01 01 01 02 3a 80 18 71 38 2d 40 58 2c - * 45 00 40 84 63 00 00 1e 00 00 00 fc 00 54 65 73 - * 74 20 45 44 49 44 0a 20 20 20 00 00 00 fd 00 32 - * 46 1e 46 0f 00 0a 20 20 20 20 20 20 00 00 00 10 - * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 92 - * - * 02 03 15 81 e3 05 00 20 41 10 e2 00 4a 67 03 0c - * 00 12 34 00 28 00 00 00 00 00 00 00 00 00 00 00 - * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 fc - * - * ---------------- - * - * Block 0, Base EDID: - * EDID Structure Version & Revision: 1.3 - * Vendor & Product Identification: - * Manufacturer: LNX - * Model: 42 - * Made in: 2023 - * Basic Display Parameters & Features: - * Digital display - * DFP 1.x compatible TMDS - * Maximum image size: 160 cm x 90 cm - * Gamma: 2.20 - * Monochrome or grayscale display - * First detailed timing is the preferred timing - * Color Characteristics: - * Red : 0.0000, 0.0000 - * Green: 0.0000, 0.0000 - * Blue : 0.0000, 0.0000 - * White: 0.0000, 0.0000 - * Established Timings I & II: - * DMT 0x04: 640x480 59.940476 Hz 4:3 31.469 kHz 25.175000 MHz - * Standard Timings: none - * Detailed Timing Descriptors: - * DTD 1: 1920x1080 60.000000 Hz 16:9 67.500 kHz 148.500000 MHz (1600 mm x 900 mm) - * Hfront 88 Hsync 44 Hback 148 Hpol P - * Vfront 4 Vsync 5 Vback 36 Vpol P - * Display Product Name: 'Test EDID' - * Display Range Limits: - * Monitor ranges (GTF): 50-70 Hz V, 30-70 kHz H, max dotclock 150 MHz - * Dummy Descriptor: - * Extension blocks: 1 - * Checksum: 0x92 - * - * ---------------- - * - * Block 1, CTA-861 Extension Block: - * Revision: 3 - * Underscans IT Video Formats by default - * Native detailed modes: 1 - * Colorimetry Data Block: - * sRGB - * Video Data Block: - * VIC 16: 1920x1080 60.000000 Hz 16:9 67.500 kHz 148.500000 MHz - * Video Capability Data Block: - * YCbCr quantization: No Data - * RGB quantization: Selectable (via AVI Q) - * PT scan behavior: No Data - * IT scan behavior: Always Underscanned - * CE scan behavior: Always Underscanned - * Vendor-Specific Data Block (HDMI), OUI 00-0C-03: - * Source physical address: 1.2.3.4 - * Maximum TMDS clock: 200 MHz - * Checksum: 0xfc Unused space in Extension Block: 106 bytes - * - * ---------------- - * - * edid-decode 1.30.0-5367 - * edid-decode SHA: 41ebf7135691 2025-05-01 10:19:22 - * - * EDID conformity: PASS - */ -static const unsigned char test_edid_hdmi_1080p_rgb_max_200mhz[] = { - 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x31, 0xd8, 0x2a, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0x01, 0x03, 0x81, 0xa0, 0x5a, 0x78, - 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, - 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x3a, 0x80, 0x18, 0x71, 0x38, - 0x2d, 0x40, 0x58, 0x2c, 0x45, 0x00, 0x40, 0x84, 0x63, 0x00, 0x00, 0x1e, - 0x00, 0x00, 0x00, 0xfc, 0x00, 0x54, 0x65, 0x73, 0x74, 0x20, 0x45, 0x44, - 0x49, 0x44, 0x0a, 0x20, 0x20, 0x20, 0x00, 0x00, 0x00, 0xfd, 0x00, 0x32, - 0x46, 0x1e, 0x46, 0x0f, 0x00, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x92, 0x02, 0x03, 0x15, 0x81, - 0xe3, 0x05, 0x00, 0x20, 0x41, 0x10, 0xe2, 0x00, 0x4a, 0x67, 0x03, 0x0c, - 0x00, 0x12, 0x34, 0x00, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0xfc -}; - -/* - * edid-decode (hex): - * - * 00 ff ff ff ff ff ff 00 31 d8 2a 00 00 00 00 00 - * 00 21 01 03 81 a0 5a 78 02 00 00 00 00 00 00 00 - * 00 00 00 20 00 00 01 01 01 01 01 01 01 01 01 01 - * 01 01 01 01 01 01 02 3a 80 18 71 38 2d 40 58 2c - * 45 00 40 84 63 00 00 1e 00 00 00 fc 00 54 65 73 - * 74 20 45 44 49 44 0a 20 20 20 00 00 00 fd 00 32 - * 46 1e 46 0f 00 0a 20 20 20 20 20 20 00 00 00 10 - * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 92 - * - * 02 03 1c 81 e3 05 c0 20 41 10 e2 00 4a 67 03 0c - * 00 12 34 00 28 e6 06 05 01 52 52 51 00 00 00 00 - * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 4e - * - * ---------------- - * - * Block 0, Base EDID: - * EDID Structure Version & Revision: 1.3 - * Vendor & Product Identification: - * Manufacturer: LNX - * Model: 42 - * Made in: 2023 - * Basic Display Parameters & Features: - * Digital display - * DFP 1.x compatible TMDS - * Maximum image size: 160 cm x 90 cm - * Gamma: 2.20 - * Monochrome or grayscale display - * First detailed timing is the preferred timing - * Color Characteristics: - * Red : 0.0000, 0.0000 - * Green: 0.0000, 0.0000 - * Blue : 0.0000, 0.0000 - * White: 0.0000, 0.0000 - * Established Timings I & II: - * DMT 0x04: 640x480 59.940476 Hz 4:3 31.469 kHz 25.175000 MHz - * Standard Timings: none - * Detailed Timing Descriptors: - * DTD 1: 1920x1080 60.000000 Hz 16:9 67.500 kHz 148.500000 MHz (1600 mm x 900 mm) - * Hfront 88 Hsync 44 Hback 148 Hpol P - * Vfront 4 Vsync 5 Vback 36 Vpol P - * Display Product Name: 'Test EDID' - * Display Range Limits: - * Monitor ranges (GTF): 50-70 Hz V, 30-70 kHz H, max dotclock 150 MHz - * Dummy Descriptor: - * Extension blocks: 1 - * Checksum: 0x92 - * - * ---------------- - * - * Block 1, CTA-861 Extension Block: - * Revision: 3 - * Underscans IT Video Formats by default - * Native detailed modes: 1 - * Colorimetry Data Block: - * BT2020YCC - * BT2020RGB - * sRGB - * Video Data Block: - * VIC 16: 1920x1080 60.000000 Hz 16:9 67.500 kHz 148.500000 MHz - * Video Capability Data Block: - * YCbCr quantization: No Data - * RGB quantization: Selectable (via AVI Q) - * PT scan behavior: No Data - * IT scan behavior: Always Underscanned - * CE scan behavior: Always Underscanned - * Vendor-Specific Data Block (HDMI), OUI 00-0C-03: - * Source physical address: 1.2.3.4 - * Maximum TMDS clock: 200 MHz - * HDR Static Metadata Data Block: - * Electro optical transfer functions: - * Traditional gamma - SDR luminance range - * SMPTE ST2084 - * Supported static metadata descriptors: - * Static metadata type 1 - * Desired content max luminance: 82 (295.365 cd/m^2) - * Desired content max frame-average luminance: 82 (295.365 cd/m^2) - * Desired content min luminance: 81 (0.298 cd/m^2) - * Checksum: 0x4e Unused space in Extension Block: 99 bytes - * - * ---------------- - * - * edid-decode 1.31.0-5387 - * edid-decode SHA: 5508bc4301ac 2025-08-25 08:14:22 - * - * EDID conformity: PASS - */ -static const unsigned char test_edid_hdmi_1080p_rgb_max_200mhz_hdr[] = { - 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x31, 0xd8, 0x2a, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0x01, 0x03, 0x81, 0xa0, 0x5a, 0x78, - 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, - 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x3a, 0x80, 0x18, 0x71, 0x38, - 0x2d, 0x40, 0x58, 0x2c, 0x45, 0x00, 0x40, 0x84, 0x63, 0x00, 0x00, 0x1e, - 0x00, 0x00, 0x00, 0xfc, 0x00, 0x54, 0x65, 0x73, 0x74, 0x20, 0x45, 0x44, - 0x49, 0x44, 0x0a, 0x20, 0x20, 0x20, 0x00, 0x00, 0x00, 0xfd, 0x00, 0x32, - 0x46, 0x1e, 0x46, 0x0f, 0x00, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x92, 0x02, 0x03, 0x1c, 0x81, - 0xe3, 0x05, 0xc0, 0x20, 0x41, 0x10, 0xe2, 0x00, 0x4a, 0x67, 0x03, 0x0c, - 0x00, 0x12, 0x34, 0x78, 0x28, 0xe6, 0x06, 0x05, 0x01, 0x52, 0x52, 0x51, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0xd6, -}; - -/* - * edid-decode (hex): - * - * 00 ff ff ff ff ff ff 00 31 d8 2a 00 00 00 00 00 - * 00 21 01 03 81 a0 5a 78 02 00 00 00 00 00 00 00 - * 00 00 00 20 00 00 01 01 01 01 01 01 01 01 01 01 - * 01 01 01 01 01 01 02 3a 80 18 71 38 2d 40 58 2c - * 45 00 40 84 63 00 00 1e 00 00 00 fc 00 54 65 73 - * 74 20 45 44 49 44 0a 20 20 20 00 00 00 fd 00 32 - * 46 1e 46 0f 00 0a 20 20 20 20 20 20 00 00 00 10 - * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 92 - * - * 02 03 15 81 e3 05 00 20 41 10 e2 00 4a 67 03 0c - * 00 12 34 00 44 00 00 00 00 00 00 00 00 00 00 00 - * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 e0 - * - * ---------------- - * - * Block 0, Base EDID: - * EDID Structure Version & Revision: 1.3 - * Vendor & Product Identification: - * Manufacturer: LNX - * Model: 42 - * Made in: 2023 - * Basic Display Parameters & Features: - * Digital display - * DFP 1.x compatible TMDS - * Maximum image size: 160 cm x 90 cm - * Gamma: 2.20 - * Monochrome or grayscale display - * First detailed timing is the preferred timing - * Color Characteristics: - * Red : 0.0000, 0.0000 - * Green: 0.0000, 0.0000 - * Blue : 0.0000, 0.0000 - * White: 0.0000, 0.0000 - * Established Timings I & II: - * DMT 0x04: 640x480 59.940476 Hz 4:3 31.469 kHz 25.175000 MHz - * Standard Timings: none - * Detailed Timing Descriptors: - * DTD 1: 1920x1080 60.000000 Hz 16:9 67.500 kHz 148.500000 MHz (1600 mm x 900 mm) - * Hfront 88 Hsync 44 Hback 148 Hpol P - * Vfront 4 Vsync 5 Vback 36 Vpol P - * Display Product Name: 'Test EDID' - * Display Range Limits: - * Monitor ranges (GTF): 50-70 Hz V, 30-70 kHz H, max dotclock 150 MHz - * Dummy Descriptor: - * Extension blocks: 1 - * Checksum: 0x92 - * - * ---------------- - * - * Block 1, CTA-861 Extension Block: - * Revision: 3 - * Underscans IT Video Formats by default - * Native detailed modes: 1 - * Colorimetry Data Block: - * sRGB - * Video Data Block: - * VIC 16: 1920x1080 60.000000 Hz 16:9 67.500 kHz 148.500000 MHz - * Video Capability Data Block: - * YCbCr quantization: No Data - * RGB quantization: Selectable (via AVI Q) - * PT scan behavior: No Data - * IT scan behavior: Always Underscanned - * CE scan behavior: Always Underscanned - * Vendor-Specific Data Block (HDMI), OUI 00-0C-03: - * Source physical address: 1.2.3.4 - * Maximum TMDS clock: 340 MHz - * Checksum: 0xe0 Unused space in Extension Block: 106 bytes - * - * ---------------- - * - * edid-decode 1.30.0-5367 - * edid-decode SHA: 41ebf7135691 2025-05-01 10:19:22 - * - * EDID conformity: PASS - */ -static const unsigned char test_edid_hdmi_1080p_rgb_max_340mhz[] = { - 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x31, 0xd8, 0x2a, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0x01, 0x03, 0x81, 0xa0, 0x5a, 0x78, - 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, - 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x3a, 0x80, 0x18, 0x71, 0x38, - 0x2d, 0x40, 0x58, 0x2c, 0x45, 0x00, 0x40, 0x84, 0x63, 0x00, 0x00, 0x1e, - 0x00, 0x00, 0x00, 0xfc, 0x00, 0x54, 0x65, 0x73, 0x74, 0x20, 0x45, 0x44, - 0x49, 0x44, 0x0a, 0x20, 0x20, 0x20, 0x00, 0x00, 0x00, 0xfd, 0x00, 0x32, - 0x46, 0x1e, 0x46, 0x0f, 0x00, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x92, 0x02, 0x03, 0x15, 0x81, - 0xe3, 0x05, 0x00, 0x20, 0x41, 0x10, 0xe2, 0x00, 0x4a, 0x67, 0x03, 0x0c, - 0x00, 0x12, 0x34, 0x00, 0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0xe0 -}; - -/* - * edid-decode (hex): - * - * 00 ff ff ff ff ff ff 00 31 d8 2a 00 00 00 00 00 - * 00 21 01 03 81 a0 5a 78 1a 00 00 00 00 00 00 00 - * 00 00 00 20 00 00 01 01 01 01 01 01 01 01 01 01 - * 01 01 01 01 01 01 02 3a 80 18 71 38 2d 40 58 2c - * 45 00 40 84 63 00 00 1e 00 00 00 fc 00 54 65 73 - * 74 20 45 44 49 44 0a 20 20 20 00 00 00 fd 00 32 - * 46 1e 46 0f 00 0a 20 20 20 20 20 20 00 00 00 10 - * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 7a - * - * 02 03 15 b1 e3 05 00 20 41 10 e2 00 ca 67 03 0c - * 00 12 34 78 28 00 00 00 00 00 00 00 00 00 00 00 - * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 d4 - * - * ---------------- - * - * Block 0, Base EDID: - * EDID Structure Version & Revision: 1.3 - * Vendor & Product Identification: - * Manufacturer: LNX - * Model: 42 - * Made in: 2023 - * Basic Display Parameters & Features: - * Digital display - * DFP 1.x compatible TMDS - * Maximum image size: 160 cm x 90 cm - * Gamma: 2.20 - * Undefined display color type - * First detailed timing is the preferred timing - * Color Characteristics: - * Red : 0.0000, 0.0000 - * Green: 0.0000, 0.0000 - * Blue : 0.0000, 0.0000 - * White: 0.0000, 0.0000 - * Established Timings I & II: - * DMT 0x04: 640x480 59.940476 Hz 4:3 31.469 kHz 25.175000 MHz - * Standard Timings: none - * Detailed Timing Descriptors: - * DTD 1: 1920x1080 60.000000 Hz 16:9 67.500 kHz 148.500000 MHz (1600 mm x 900 mm) - * Hfront 88 Hsync 44 Hback 148 Hpol P - * Vfront 4 Vsync 5 Vback 36 Vpol P - * Display Product Name: 'Test EDID' - * Display Range Limits: - * Monitor ranges (GTF): 50-70 Hz V, 30-70 kHz H, max dotclock 150 MHz - * Dummy Descriptor: - * Extension blocks: 1 - * Checksum: 0x7a - * - * ---------------- - * - * Block 1, CTA-861 Extension Block: - * Revision: 3 - * Underscans IT Video Formats by default - * Supports YCbCr 4:4:4 - * Supports YCbCr 4:2:2 - * Native detailed modes: 1 - * Colorimetry Data Block: - * sRGB - * Video Data Block: - * VIC 16: 1920x1080 60.000000 Hz 16:9 67.500 kHz 148.500000 MHz - * Video Capability Data Block: - * YCbCr quantization: Selectable (via AVI YQ) - * RGB quantization: Selectable (via AVI Q) - * PT scan behavior: No Data - * IT scan behavior: Always Underscanned - * CE scan behavior: Always Underscanned - * Vendor-Specific Data Block (HDMI), OUI 00-0C-03: - * Source physical address: 1.2.3.4 - * DC_48bit - * DC_36bit - * DC_30bit - * DC_Y444 - * Maximum TMDS clock: 200 MHz - * Checksum: 0xd4 Unused space in Extension Block: 106 bytes - * - * ---------------- - * - * edid-decode 1.30.0-5367 - * edid-decode SHA: 41ebf7135691 2025-05-01 10:19:22 - * - * EDID conformity: PASS - */ -static const unsigned char test_edid_hdmi_1080p_rgb_yuv_dc_max_200mhz[] = { - 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x31, 0xd8, 0x2a, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0x01, 0x03, 0x81, 0xa0, 0x5a, 0x78, - 0x1a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, - 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x3a, 0x80, 0x18, 0x71, 0x38, - 0x2d, 0x40, 0x58, 0x2c, 0x45, 0x00, 0x40, 0x84, 0x63, 0x00, 0x00, 0x1e, - 0x00, 0x00, 0x00, 0xfc, 0x00, 0x54, 0x65, 0x73, 0x74, 0x20, 0x45, 0x44, - 0x49, 0x44, 0x0a, 0x20, 0x20, 0x20, 0x00, 0x00, 0x00, 0xfd, 0x00, 0x32, - 0x46, 0x1e, 0x46, 0x0f, 0x00, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x7a, 0x02, 0x03, 0x15, 0xb1, - 0xe3, 0x05, 0x00, 0x20, 0x41, 0x10, 0xe2, 0x00, 0xca, 0x67, 0x03, 0x0c, - 0x00, 0x12, 0x34, 0x78, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0xd4 -}; - -/* - * edid-decode (hex): - * - * 00 ff ff ff ff ff ff 00 31 d8 2a 00 00 00 00 00 - * 00 21 01 03 81 a0 5a 78 0a 00 00 00 00 00 00 00 - * 00 00 00 20 00 00 01 01 01 01 01 01 01 01 01 01 - * 01 01 01 01 01 01 02 3a 80 18 71 38 2d 40 58 2c - * 45 00 40 84 63 00 00 1e 00 00 00 fc 00 54 65 73 - * 74 20 45 44 49 44 0a 20 20 20 00 00 00 fd 00 32 - * 46 1e 46 0f 00 0a 20 20 20 20 20 20 00 00 00 10 - * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 8a - * - * 02 03 15 b1 e3 05 00 20 41 10 e2 00 ca 67 03 0c - * 00 12 34 78 44 00 00 00 00 00 00 00 00 00 00 00 - * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 b8 - * - * ---------------- - * - * Block 0, Base EDID: - * EDID Structure Version & Revision: 1.3 - * Vendor & Product Identification: - * Manufacturer: LNX - * Model: 42 - * Made in: 2023 - * Basic Display Parameters & Features: - * Digital display - * DFP 1.x compatible TMDS - * Maximum image size: 160 cm x 90 cm - * Gamma: 2.20 - * RGB color display - * First detailed timing is the preferred timing - * Color Characteristics: - * Red : 0.0000, 0.0000 - * Green: 0.0000, 0.0000 - * Blue : 0.0000, 0.0000 - * White: 0.0000, 0.0000 - * Established Timings I & II: - * DMT 0x04: 640x480 59.940476 Hz 4:3 31.469 kHz 25.175000 MHz - * Standard Timings: none - * Detailed Timing Descriptors: - * DTD 1: 1920x1080 60.000000 Hz 16:9 67.500 kHz 148.500000 MHz (1600 mm x 900 mm) - * Hfront 88 Hsync 44 Hback 148 Hpol P - * Vfront 4 Vsync 5 Vback 36 Vpol P - * Display Product Name: 'Test EDID' - * Display Range Limits: - * Monitor ranges (GTF): 50-70 Hz V, 30-70 kHz H, max dotclock 150 MHz - * Dummy Descriptor: - * Extension blocks: 1 - * Checksum: 0x8a - * - * ---------------- - * - * Block 1, CTA-861 Extension Block: - * Revision: 3 - * Underscans IT Video Formats by default - * Supports YCbCr 4:4:4 - * Supports YCbCr 4:2:2 - * Native detailed modes: 1 - * Colorimetry Data Block: - * sRGB - * Video Data Block: - * VIC 16: 1920x1080 60.000000 Hz 16:9 67.500 kHz 148.500000 MHz - * Video Capability Data Block: - * YCbCr quantization: Selectable (via AVI YQ) - * RGB quantization: Selectable (via AVI Q) - * PT scan behavior: No Data - * IT scan behavior: Always Underscanned - * CE scan behavior: Always Underscanned - * Vendor-Specific Data Block (HDMI), OUI 00-0C-03: - * Source physical address: 1.2.3.4 - * DC_48bit - * DC_36bit - * DC_30bit - * DC_Y444 - * Maximum TMDS clock: 340 MHz - * Checksum: 0xb8 Unused space in Extension Block: 106 bytes - * - * ---------------- - * - * edid-decode 1.30.0-5367 - * edid-decode SHA: 41ebf7135691 2025-05-01 10:19:22 - * - * EDID conformity: PASS - */ -static const unsigned char test_edid_hdmi_1080p_rgb_yuv_dc_max_340mhz[] = { - 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x31, 0xd8, 0x2a, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0x01, 0x03, 0x81, 0xa0, 0x5a, 0x78, - 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, - 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x3a, 0x80, 0x18, 0x71, 0x38, - 0x2d, 0x40, 0x58, 0x2c, 0x45, 0x00, 0x40, 0x84, 0x63, 0x00, 0x00, 0x1e, - 0x00, 0x00, 0x00, 0xfc, 0x00, 0x54, 0x65, 0x73, 0x74, 0x20, 0x45, 0x44, - 0x49, 0x44, 0x0a, 0x20, 0x20, 0x20, 0x00, 0x00, 0x00, 0xfd, 0x00, 0x32, - 0x46, 0x1e, 0x46, 0x0f, 0x00, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x8a, 0x02, 0x03, 0x15, 0xb1, - 0xe3, 0x05, 0x00, 0x20, 0x41, 0x10, 0xe2, 0x00, 0xca, 0x67, 0x03, 0x0c, - 0x00, 0x12, 0x34, 0x78, 0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0xb8 -}; - -/* - * Max resolution: - * - 1920x1080@60Hz with RGB, YUV444, YUV422 - * - 3840x2160@30Hz with YUV420 only - * Max BPC: 16 for all modes - * Max TMDS clock: 200 MHz - * - * edid-decode (hex): - * - * 00 ff ff ff ff ff ff 00 31 d8 34 00 00 00 00 00 - * ff 23 01 03 80 60 36 78 0f ee 91 a3 54 4c 99 26 - * 0f 50 54 20 00 00 01 01 01 01 01 01 01 01 01 01 - * 01 01 01 01 01 01 02 3a 80 18 71 38 2d 40 58 2c - * 45 00 c0 1c 32 00 00 1e 00 00 00 fc 00 54 65 73 - * 74 20 45 44 49 44 0a 20 20 20 00 00 00 fd 00 18 - * 55 18 5e 11 00 0a 20 20 20 20 20 20 00 00 00 10 - * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 bb - * - * 02 03 29 31 42 90 5f 6c 03 0c 00 10 00 78 28 20 - * 00 00 01 03 6d d8 5d c4 01 28 80 07 00 00 00 00 - * 00 00 e3 0f 00 00 e2 0e 5f 00 00 00 00 00 00 00 - * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ca - * - * ---------------- - * - * Block 0, Base EDID: - * EDID Structure Version & Revision: 1.3 - * Vendor & Product Identification: - * Manufacturer: LNX - * Model: 52 - * Model year: 2025 - * Basic Display Parameters & Features: - * Digital display - * Maximum image size: 96 cm x 54 cm - * Gamma: 2.20 - * RGB color display - * Default (sRGB) color space is primary color space - * First detailed timing is the preferred timing - * Supports GTF timings within operating range - * Color Characteristics: - * Red : 0.6396, 0.3300 - * Green: 0.2998, 0.5996 - * Blue : 0.1503, 0.0595 - * White: 0.3125, 0.3291 - * Established Timings I & II: - * DMT 0x04: 640x480 59.940476 Hz 4:3 31.469 kHz 25.175000 MHz - * Standard Timings: none - * Detailed Timing Descriptors: - * DTD 1: 1920x1080 60.000000 Hz 16:9 67.500 kHz 148.500000 MHz (960 mm x 540 mm) - * Hfront 88 Hsync 44 Hback 148 Hpol P - * Vfront 4 Vsync 5 Vback 36 Vpol P - * Display Product Name: 'Test EDID' - * Display Range Limits: - * Monitor ranges (GTF): 24-85 Hz V, 24-94 kHz H, max dotclock 170 MHz - * Dummy Descriptor: - * Extension blocks: 1 - * Checksum: 0xbb - * - * ---------------- - * - * Block 1, CTA-861 Extension Block: - * Revision: 3 - * Supports YCbCr 4:4:4 - * Supports YCbCr 4:2:2 - * Native detailed modes: 1 - * Video Data Block: - * VIC 16: 1920x1080 60.000000 Hz 16:9 67.500 kHz 148.500000 MHz (native) - * VIC 95: 3840x2160 30.000000 Hz 16:9 67.500 kHz 297.000000 MHz - * Vendor-Specific Data Block (HDMI), OUI 00-0C-03: - * Source physical address: 1.0.0.0 - * DC_48bit - * DC_36bit - * DC_30bit - * DC_Y444 - * Maximum TMDS clock: 200 MHz - * Extended HDMI video details: - * Vendor-Specific Data Block (HDMI Forum), OUI C4-5D-D8: - * Version: 1 - * Maximum TMDS Character Rate: 200 MHz - * SCDC Present - * Supports 16-bits/component Deep Color 4:2:0 Pixel Encoding - * Supports 12-bits/component Deep Color 4:2:0 Pixel Encoding - * Supports 10-bits/component Deep Color 4:2:0 Pixel Encoding - * YCbCr 4:2:0 Capability Map Data Block: - * Empty Capability Map - * YCbCr 4:2:0 Video Data Block: - * VIC 95: 3840x2160 30.000000 Hz 16:9 67.500 kHz 297.000000 MHz - * Checksum: 0xca - */ -static const unsigned char test_edid_hdmi_1080p_rgb_yuv_4k_yuv420_dc_max_200mhz[] = { - 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x31, 0xd8, 0x34, 0x00, - 0x00, 0x00, 0x00, 0x00, 0xff, 0x23, 0x01, 0x03, 0x80, 0x60, 0x36, 0x78, - 0x0f, 0xee, 0x91, 0xa3, 0x54, 0x4c, 0x99, 0x26, 0x0f, 0x50, 0x54, 0x20, - 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x3a, 0x80, 0x18, 0x71, 0x38, - 0x2d, 0x40, 0x58, 0x2c, 0x45, 0x00, 0xc0, 0x1c, 0x32, 0x00, 0x00, 0x1e, - 0x00, 0x00, 0x00, 0xfc, 0x00, 0x54, 0x65, 0x73, 0x74, 0x20, 0x45, 0x44, - 0x49, 0x44, 0x0a, 0x20, 0x20, 0x20, 0x00, 0x00, 0x00, 0xfd, 0x00, 0x18, - 0x55, 0x18, 0x5e, 0x11, 0x00, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xbb, 0x02, 0x03, 0x29, 0x31, - 0x42, 0x90, 0x5f, 0x6c, 0x03, 0x0c, 0x00, 0x10, 0x00, 0x78, 0x28, 0x20, - 0x00, 0x00, 0x01, 0x03, 0x6d, 0xd8, 0x5d, 0xc4, 0x01, 0x28, 0x80, 0x07, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe3, 0x0f, 0x00, 0x00, 0xe2, 0x0e, - 0x5f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0xca -}; - -/* - * Max resolution: 3840x2160@30Hz with RGB, YUV444, YUV422, YUV420 - * Max BPC: 16 for all modes - * Max TMDS clock: 340 MHz - * - * edid-decode (hex): - * - * 00 ff ff ff ff ff ff 00 31 d8 34 00 00 00 00 00 - * ff 23 01 03 80 60 36 78 0f ee 91 a3 54 4c 99 26 - * 0f 50 54 20 00 00 01 01 01 01 01 01 01 01 01 01 - * 01 01 01 01 01 01 04 74 00 30 f2 70 5a 80 b0 58 - * 8a 00 40 84 63 00 00 1e 00 00 00 fc 00 54 65 73 - * 74 20 45 44 49 44 0a 20 20 20 00 00 00 fd 00 18 - * 55 18 5e 22 00 0a 20 20 20 20 20 20 00 00 00 10 - * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 ce - * - * 02 03 27 31 41 5f 6c 03 0c 00 10 00 78 44 20 00 - * 00 01 03 6d d8 5d c4 01 44 80 07 00 00 00 00 00 - * 00 e3 0f 01 00 e1 0e 00 00 00 00 00 00 00 00 00 - * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 84 - * - * ---------------- - * - * Block 0, Base EDID: - * EDID Structure Version & Revision: 1.3 - * Vendor & Product Identification: - * Manufacturer: LNX - * Model: 52 - * Model year: 2025 - * Basic Display Parameters & Features: - * Digital display - * Maximum image size: 96 cm x 54 cm - * Gamma: 2.20 - * RGB color display - * Default (sRGB) color space is primary color space - * First detailed timing is the preferred timing - * Supports GTF timings within operating range - * Color Characteristics: - * Red : 0.6396, 0.3300 - * Green: 0.2998, 0.5996 - * Blue : 0.1503, 0.0595 - * White: 0.3125, 0.3291 - * Established Timings I & II: - * DMT 0x04: 640x480 59.940476 Hz 4:3 31.469 kHz 25.175000 MHz - * Standard Timings: none - * Detailed Timing Descriptors: - * DTD 1: 3840x2160 30.000000 Hz 16:9 67.500 kHz 297.000000 MHz (1600 mm x 900 mm) - * Hfront 176 Hsync 88 Hback 296 Hpol P - * Vfront 8 Vsync 10 Vback 72 Vpol P - * Display Product Name: 'Test EDID' - * Display Range Limits: - * Monitor ranges (GTF): 24-85 Hz V, 24-94 kHz H, max dotclock 340 MHz - * Dummy Descriptor: - * Extension blocks: 1 - * Checksum: 0xce - * - * ---------------- - * - * Block 1, CTA-861 Extension Block: - * Revision: 3 - * Supports YCbCr 4:4:4 - * Supports YCbCr 4:2:2 - * Native detailed modes: 1 - * Video Data Block: - * VIC 95: 3840x2160 30.000000 Hz 16:9 67.500 kHz 297.000000 MHz - * Vendor-Specific Data Block (HDMI), OUI 00-0C-03: - * Source physical address: 1.0.0.0 - * DC_48bit - * DC_36bit - * DC_30bit - * DC_Y444 - * Maximum TMDS clock: 340 MHz - * Extended HDMI video details: - * Vendor-Specific Data Block (HDMI Forum), OUI C4-5D-D8: - * Version: 1 - * Maximum TMDS Character Rate: 340 MHz - * SCDC Present - * Supports 16-bits/component Deep Color 4:2:0 Pixel Encoding - * Supports 12-bits/component Deep Color 4:2:0 Pixel Encoding - * Supports 10-bits/component Deep Color 4:2:0 Pixel Encoding - * YCbCr 4:2:0 Capability Map Data Block: - * VIC 95: 3840x2160 30.000000 Hz 16:9 67.500 kHz 297.000000 MHz - * YCbCr 4:2:0 Video Data Block: - * Checksum: 0x84 - */ -static const unsigned char test_edid_hdmi_4k_rgb_yuv420_dc_max_340mhz[] = { - 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x31, 0xd8, 0x34, 0x00, - 0x00, 0x00, 0x00, 0x00, 0xff, 0x23, 0x01, 0x03, 0x80, 0x60, 0x36, 0x78, - 0x0f, 0xee, 0x91, 0xa3, 0x54, 0x4c, 0x99, 0x26, 0x0f, 0x50, 0x54, 0x20, - 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x04, 0x74, 0x00, 0x30, 0xf2, 0x70, - 0x5a, 0x80, 0xb0, 0x58, 0x8a, 0x00, 0x40, 0x84, 0x63, 0x00, 0x00, 0x1e, - 0x00, 0x00, 0x00, 0xfc, 0x00, 0x54, 0x65, 0x73, 0x74, 0x20, 0x45, 0x44, - 0x49, 0x44, 0x0a, 0x20, 0x20, 0x20, 0x00, 0x00, 0x00, 0xfd, 0x00, 0x18, - 0x55, 0x18, 0x5e, 0x22, 0x00, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, - 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xce, 0x02, 0x03, 0x27, 0x31, - 0x41, 0x5f, 0x6c, 0x03, 0x0c, 0x00, 0x10, 0x00, 0x78, 0x44, 0x20, 0x00, - 0x00, 0x01, 0x03, 0x6d, 0xd8, 0x5d, 0xc4, 0x01, 0x44, 0x80, 0x07, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0xe3, 0x0f, 0x01, 0x00, 0xe1, 0x0e, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x84 -}; +extern const unsigned char test_edid_dvi_1080p[128]; +extern const unsigned char test_edid_hdmi_1080p_rgb_max_100mhz[256]; +extern const unsigned char test_edid_hdmi_1080p_rgb_max_200mhz[256]; +extern const unsigned char test_edid_hdmi_1080p_rgb_max_200mhz_hdr[256]; +extern const unsigned char test_edid_hdmi_1080p_rgb_max_340mhz[256]; +extern const unsigned char test_edid_hdmi_1080p_rgb_yuv_dc_max_200mhz[256]; +extern const unsigned char test_edid_hdmi_1080p_rgb_yuv_dc_max_340mhz[256]; +extern const unsigned char test_edid_hdmi_1080p_rgb_yuv_4k_yuv420_dc_max_200mhz[256]; +extern const unsigned char test_edid_hdmi_4k_rgb_yuv420_dc_max_340mhz[256]; #endif // DRM_KUNIT_EDID_H_ diff --git a/drivers/gpu/drm/tidss/tidss_crtc.c b/drivers/gpu/drm/tidss/tidss_crtc.c index acab9307bdf3..400329aa2200 100644 --- a/drivers/gpu/drm/tidss/tidss_crtc.c +++ b/drivers/gpu/drm/tidss/tidss_crtc.c @@ -357,20 +357,17 @@ static void tidss_crtc_destroy_state(struct drm_crtc *crtc, kfree(tstate); } -static void tidss_crtc_reset(struct drm_crtc *crtc) +static struct drm_crtc_state *tidss_crtc_create_state(struct drm_crtc *crtc) { struct tidss_crtc_state *tstate; - if (crtc->state) - tidss_crtc_destroy_state(crtc, crtc->state); - tstate = kzalloc_obj(*tstate); - if (!tstate) { - crtc->state = NULL; - return; - } + if (!tstate) + return ERR_PTR(-ENOMEM); + + __drm_atomic_helper_crtc_state_init(&tstate->base, crtc); - __drm_atomic_helper_crtc_reset(crtc, &tstate->base); + return &tstate->base; } static struct drm_crtc_state *tidss_crtc_duplicate_state(struct drm_crtc *crtc) @@ -405,10 +402,10 @@ static void tidss_crtc_destroy(struct drm_crtc *crtc) } static const struct drm_crtc_funcs tidss_crtc_funcs = { - .reset = tidss_crtc_reset, .destroy = tidss_crtc_destroy, .set_config = drm_atomic_helper_set_config, .page_flip = drm_atomic_helper_page_flip, + .atomic_create_state = tidss_crtc_create_state, .atomic_duplicate_state = tidss_crtc_duplicate_state, .atomic_destroy_state = tidss_crtc_destroy_state, .enable_vblank = tidss_crtc_enable_vblank, diff --git a/drivers/gpu/drm/tidss/tidss_encoder.c b/drivers/gpu/drm/tidss/tidss_encoder.c index 34db8d2a3792..698f8d964ca0 100644 --- a/drivers/gpu/drm/tidss/tidss_encoder.c +++ b/drivers/gpu/drm/tidss/tidss_encoder.c @@ -76,7 +76,7 @@ static int tidss_bridge_atomic_check(struct drm_bridge *bridge, static const struct drm_bridge_funcs tidss_bridge_funcs = { .attach = tidss_bridge_attach, .atomic_check = tidss_bridge_atomic_check, - .atomic_reset = drm_atomic_helper_bridge_reset, + .atomic_create_state = drm_atomic_helper_bridge_create_state, .atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state, .atomic_destroy_state = drm_atomic_helper_bridge_destroy_state, }; diff --git a/drivers/gpu/drm/tidss/tidss_oldi.c b/drivers/gpu/drm/tidss/tidss_oldi.c index 9c9725aac829..5eb749f495d5 100644 --- a/drivers/gpu/drm/tidss/tidss_oldi.c +++ b/drivers/gpu/drm/tidss/tidss_oldi.c @@ -335,7 +335,7 @@ static const struct drm_bridge_funcs tidss_oldi_bridge_funcs = { .atomic_get_input_bus_fmts = tidss_oldi_atomic_get_input_bus_fmts, .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_create_state = drm_atomic_helper_bridge_create_state, .mode_valid = tidss_oldi_mode_valid, }; diff --git a/drivers/gpu/drm/tidss/tidss_plane.c b/drivers/gpu/drm/tidss/tidss_plane.c index 1a8b44fb45f8..6d82976c2db1 100644 --- a/drivers/gpu/drm/tidss/tidss_plane.c +++ b/drivers/gpu/drm/tidss/tidss_plane.c @@ -178,8 +178,8 @@ static const struct drm_plane_helper_funcs tidss_primary_plane_helper_funcs = { static const struct drm_plane_funcs tidss_plane_funcs = { .update_plane = drm_atomic_helper_update_plane, .disable_plane = drm_atomic_helper_disable_plane, - .reset = drm_atomic_helper_plane_reset, .destroy = drm_plane_destroy, + .atomic_create_state = drm_atomic_helper_plane_create_state, .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state, .atomic_destroy_state = drm_atomic_helper_plane_destroy_state, }; diff --git a/drivers/gpu/drm/tiny/gm12u320.c b/drivers/gpu/drm/tiny/gm12u320.c index d73dfebb4353..4ad074337af0 100644 --- a/drivers/gpu/drm/tiny/gm12u320.c +++ b/drivers/gpu/drm/tiny/gm12u320.c @@ -388,7 +388,7 @@ static void gm12u320_fb_update_work(struct work_struct *work) * We must draw a frame every 2s otherwise the projector * switches back to showing its logo. */ - queue_delayed_work(system_long_wq, &gm12u320->fb_update.work, + queue_delayed_work(system_dfl_long_wq, &gm12u320->fb_update.work, msecs_to_jiffies(IDLE_TIMEOUT)); return; @@ -427,7 +427,8 @@ static void gm12u320_fb_mark_dirty(struct drm_framebuffer *fb, mutex_unlock(&gm12u320->fb_update.lock); if (wakeup) - mod_delayed_work(system_long_wq, &gm12u320->fb_update.work, 0); + mod_delayed_work(system_dfl_long_wq, + &gm12u320->fb_update.work, 0); if (old_fb) drm_framebuffer_put(old_fb); diff --git a/drivers/gpu/drm/ttm/tests/ttm_bo_validate_test.c b/drivers/gpu/drm/ttm/tests/ttm_bo_validate_test.c index 2db221f6fc3a..56ad8ef32584 100644 --- a/drivers/gpu/drm/ttm/tests/ttm_bo_validate_test.c +++ b/drivers/gpu/drm/ttm/tests/ttm_bo_validate_test.c @@ -69,7 +69,7 @@ static void dma_resv_kunit_active_fence_init(struct kunit *test, struct dma_fence *fence; fence = alloc_mock_fence(test); - dma_fence_enable_sw_signaling(fence); + dma_fence_enable_signaling(fence); dma_resv_lock(resv, NULL); dma_resv_reserve_fences(resv, 1); diff --git a/drivers/gpu/drm/ttm/ttm_bo.c b/drivers/gpu/drm/ttm/ttm_bo.c index bcd76f6bb7f0..3980f376e3ba 100644 --- a/drivers/gpu/drm/ttm/ttm_bo.c +++ b/drivers/gpu/drm/ttm/ttm_bo.c @@ -224,7 +224,7 @@ static void ttm_bo_flush_all_fences(struct ttm_buffer_object *bo) dma_resv_iter_begin(&cursor, resv, DMA_RESV_USAGE_BOOKKEEP); dma_resv_for_each_fence_unlocked(&cursor, fence) - dma_fence_enable_sw_signaling(fence); + dma_fence_enable_signaling(fence); dma_resv_iter_end(&cursor); } diff --git a/drivers/gpu/drm/ttm/ttm_pool.c b/drivers/gpu/drm/ttm/ttm_pool.c index 278bbe7a11ad..3d5f2ae0a456 100644 --- a/drivers/gpu/drm/ttm/ttm_pool.c +++ b/drivers/gpu/drm/ttm/ttm_pool.c @@ -165,8 +165,13 @@ static struct page *ttm_pool_alloc_page(struct ttm_pool *pool, gfp_t gfp_flags, * Do not add latency to the allocation path for allocations orders * device tolds us do not bring them additional performance gains. */ - if (beneficial_order && order > beneficial_order) - gfp_flags &= ~__GFP_DIRECT_RECLAIM; + if (order && beneficial_order && order != beneficial_order) + gfp_flags &= ~__GFP_RECLAIM; + + if (beneficial_order && order == beneficial_order) { + gfp_flags &= ~__GFP_NORETRY; + gfp_flags |= __GFP_RETRY_MAYFAIL; + } if (!ttm_pool_uses_dma_alloc(pool)) { p = alloc_pages_node(pool->nid, gfp_flags, order); @@ -902,6 +907,7 @@ int ttm_pool_restore_and_alloc(struct ttm_pool *pool, struct ttm_tt *tt, { struct ttm_pool_tt_restore *restore = tt->restore; struct ttm_pool_alloc_state alloc; + int ret; if (WARN_ON(!ttm_tt_is_backed_up(tt))) return -EINVAL; @@ -925,14 +931,22 @@ int ttm_pool_restore_and_alloc(struct ttm_pool *pool, struct ttm_tt *tt, } else { alloc = restore->snapshot_alloc; if (ttm_pool_restore_valid(restore)) { - int ret = ttm_pool_restore_commit(restore, tt->backup, - ctx, &alloc); + ret = ttm_pool_restore_commit(restore, tt->backup, + ctx, &alloc); if (ret) return ret; } - if (!alloc.remaining_pages) + if (!alloc.remaining_pages) { + ret = ttm_pool_apply_caching(&alloc); + if (ret) + return ret; + + kfree(tt->restore); + tt->restore = NULL; + return 0; + } } return __ttm_pool_alloc(pool, tt, ctx, &alloc, restore); @@ -1341,15 +1355,22 @@ static int ttm_pool_debugfs_shrink_show(struct seq_file *m, void *data) .gfp_mask = GFP_NOFS, .nr_to_scan = TTM_SHRINKER_BATCH, }; - unsigned long count; + unsigned long count, scanned; int nid; fs_reclaim_acquire(GFP_KERNEL); for_each_node(nid) { sc.nid = nid; count = ttm_pool_shrinker_count(mm_shrinker, &sc); - seq_printf(m, "%d: %lu/%lu\n", nid, count, - ttm_pool_shrinker_scan(mm_shrinker, &sc)); + scanned = ttm_pool_shrinker_scan(mm_shrinker, &sc); + + /* Convert shrinker API sentinel values to 0 for debugfs output */ + if (count == SHRINK_EMPTY) + count = 0; + if (scanned == SHRINK_STOP) + scanned = 0; + + seq_printf(m, "%d: %lu/%lu\n", nid, count, scanned); } fs_reclaim_release(GFP_KERNEL); diff --git a/drivers/gpu/drm/v3d/Kconfig b/drivers/gpu/drm/v3d/Kconfig index ce62c5908e1d..6a33e0ab30de 100644 --- a/drivers/gpu/drm/v3d/Kconfig +++ b/drivers/gpu/drm/v3d/Kconfig @@ -5,6 +5,7 @@ config DRM_V3D depends on DRM depends on COMMON_CLK depends on MMU + select DRM_EXEC select DRM_SCHED select DRM_GEM_SHMEM_HELPER help diff --git a/drivers/gpu/drm/v3d/v3d_drv.c b/drivers/gpu/drm/v3d/v3d_drv.c index fc81dd1247e3..8d914dbc3315 100644 --- a/drivers/gpu/drm/v3d/v3d_drv.c +++ b/drivers/gpu/drm/v3d/v3d_drv.c @@ -4,7 +4,11 @@ /** * DOC: Broadcom V3D Graphics Driver * - * This driver supports the Broadcom V3D 3.3 and 4.1 OpenGL ES GPUs. + * This driver supports the Broadcom V3D 4.2 and 7.1 GPUs. + * + * Support for V3D 3.3 and 4.1 GPUs is deprecated and it will be removed + * in the next kernel release. + * * For V3D 2.x support, see the VC4 driver. * * The V3D GPU includes a tiled render (composed of a bin and render @@ -414,13 +418,25 @@ static int v3d_platform_drm_probe(struct platform_device *pdev) */ WARN_ON(v3d->ver != gen); + /* V3D 3.3 and V3D 4.1 has had no in-tree userspace since Mesa + * dropped support in 2024 (MR#25851) and they have no known users. + * Due to that, support is scheduled for removal in the next release. + */ + if (v3d->ver <= V3D_GEN_41) { + dev_warn(dev, + "V3D %u.%u support is deprecated and will be removed " + "in the next kernel release. If you rely on this hardware, " + "please report it to dri-devel@lists.freedesktop.org.\n", + v3d->ver / 10, v3d->ver % 10); + } + v3d->cores = V3D_GET_FIELD(ident1, V3D_HUB_IDENT1_NCORES); WARN_ON(v3d->cores > 1); /* multicore not yet implemented */ ident3 = V3D_READ(V3D_HUB_IDENT3); v3d->rev = V3D_GET_FIELD(ident3, V3D_HUB_IDENT3_IPREV); - pm_runtime_set_autosuspend_delay(dev, 100); + pm_runtime_set_autosuspend_delay(dev, 50); pm_runtime_use_autosuspend(dev); ret = drm_dev_register(drm, 0); diff --git a/drivers/gpu/drm/v3d/v3d_drv.h b/drivers/gpu/drm/v3d/v3d_drv.h index 4ebe175a8c6b..2e298d7545c0 100644 --- a/drivers/gpu/drm/v3d/v3d_drv.h +++ b/drivers/gpu/drm/v3d/v3d_drv.h @@ -7,7 +7,8 @@ #include <linux/spinlock_types.h> #include <linux/workqueue.h> -#include <drm/drm_encoder.h> +#include <drm/drm_device.h> +#include <drm/drm_exec.h> #include <drm/drm_gem.h> #include <drm/drm_gem_shmem_helper.h> #include <drm/gpu_scheduler.h> @@ -288,6 +289,27 @@ to_v3d_fence(struct dma_fence *fence) #define V3D_CORE_READ(core, offset) readl(v3d->core_regs[core] + offset) #define V3D_CORE_WRITE(core, offset, val) writel(val, v3d->core_regs[core] + offset) +#define V3D_MAX_JOBS_PER_SUBMISSION 3 + +/* Per-ioctl submission context */ +struct v3d_submit { + struct v3d_dev *v3d; + + struct drm_file *file_priv; + + /* DRM exec context for this submission. */ + struct drm_exec exec; + + /* Ordered array of jobs forming the submission chain. Jobs are + * appended via v3d_submit_add_job(), then chained and pushed to + * the scheduler by v3d_submit_jobs(). + */ + struct v3d_job *jobs[V3D_MAX_JOBS_PER_SUBMISSION]; + + /* Number of jobs currently in @jobs. */ + u32 job_count; +}; + struct v3d_job { struct drm_sched_job base; @@ -295,6 +317,9 @@ struct v3d_job { struct v3d_dev *v3d; + /* The queue that the job was submitted on. */ + enum v3d_queue queue; + /* This is the array of BOs that were looked up at the start * of submission. */ @@ -401,8 +426,10 @@ struct v3d_indirect_csd_info { /* Indirect CSD */ struct v3d_csd_job *job; - /* Clean cache job associated to the Indirect CSD job */ - struct v3d_job *clean_job; + /* Indirect CSD args, stashed by the extension parser and later used + * to create the CSD job from them. + */ + struct drm_v3d_submit_csd args; /* Offset within the BO where the workgroup counts are stored */ u32 offset; @@ -417,9 +444,6 @@ struct v3d_indirect_csd_info { /* Indirect BO */ struct drm_gem_object *indirect; - - /* Context of the Indirect CSD job */ - struct ww_acquire_ctx acquire_ctx; }; struct v3d_timestamp_query_info { diff --git a/drivers/gpu/drm/v3d/v3d_sched.c b/drivers/gpu/drm/v3d/v3d_sched.c index c35f9f70cf8d..e950cee31bcb 100644 --- a/drivers/gpu/drm/v3d/v3d_sched.c +++ b/drivers/gpu/drm/v3d/v3d_sched.c @@ -189,15 +189,11 @@ static struct dma_fence *v3d_bin_job_run(struct drm_sched_job *sched_job) struct v3d_dev *v3d = job->base.v3d; struct v3d_queue_state *queue = &v3d->queue[V3D_BIN]; struct drm_device *dev = &v3d->drm; - struct dma_fence *fence; + struct dma_fence *fence = NULL; unsigned long irqflags; - if (unlikely(job->base.base.s_fence->finished.error)) { - spin_lock_irqsave(&queue->queue_lock, irqflags); - queue->active_job = NULL; - spin_unlock_irqrestore(&queue->queue_lock, irqflags); - return NULL; - } + if (unlikely(job->base.base.s_fence->finished.error)) + goto out_clean_job; /* Lock required around bin_job update vs * v3d_overflow_mem_work(). @@ -214,7 +210,7 @@ static struct dma_fence *v3d_bin_job_run(struct drm_sched_job *sched_job) fence = v3d_fence_create(v3d, V3D_BIN); if (IS_ERR(fence)) - return NULL; + goto out_clean_job; if (job->base.irq_fence) dma_fence_put(job->base.irq_fence); @@ -242,6 +238,12 @@ static struct dma_fence *v3d_bin_job_run(struct drm_sched_job *sched_job) V3D_CORE_WRITE(0, V3D_CLE_CT0QEA, job->end); return fence; + +out_clean_job: + spin_lock_irqsave(&queue->queue_lock, irqflags); + queue->active_job = NULL; + spin_unlock_irqrestore(&queue->queue_lock, irqflags); + return fence; } static struct dma_fence *v3d_render_job_run(struct drm_sched_job *sched_job) @@ -249,12 +251,10 @@ static struct dma_fence *v3d_render_job_run(struct drm_sched_job *sched_job) struct v3d_render_job *job = to_render_job(sched_job); struct v3d_dev *v3d = job->base.v3d; struct drm_device *dev = &v3d->drm; - struct dma_fence *fence; + struct dma_fence *fence = NULL; - if (unlikely(job->base.base.s_fence->finished.error)) { - v3d->queue[V3D_RENDER].active_job = NULL; - return NULL; - } + if (unlikely(job->base.base.s_fence->finished.error)) + goto out_clean_job; v3d->queue[V3D_RENDER].active_job = &job->base; @@ -268,7 +268,7 @@ static struct dma_fence *v3d_render_job_run(struct drm_sched_job *sched_job) fence = v3d_fence_create(v3d, V3D_RENDER); if (IS_ERR(fence)) - return NULL; + goto out_clean_job; if (job->base.irq_fence) dma_fence_put(job->base.irq_fence); @@ -289,6 +289,10 @@ static struct dma_fence *v3d_render_job_run(struct drm_sched_job *sched_job) V3D_CORE_WRITE(0, V3D_CLE_CT1QEA, job->end); return fence; + +out_clean_job: + v3d->queue[V3D_RENDER].active_job = NULL; + return fence; } static struct dma_fence * @@ -297,18 +301,16 @@ v3d_tfu_job_run(struct drm_sched_job *sched_job) struct v3d_tfu_job *job = to_tfu_job(sched_job); struct v3d_dev *v3d = job->base.v3d; struct drm_device *dev = &v3d->drm; - struct dma_fence *fence; + struct dma_fence *fence = NULL; - if (unlikely(job->base.base.s_fence->finished.error)) { - v3d->queue[V3D_TFU].active_job = NULL; - return NULL; - } + if (unlikely(job->base.base.s_fence->finished.error)) + goto out_clean_job; v3d->queue[V3D_TFU].active_job = &job->base; fence = v3d_fence_create(v3d, V3D_TFU); if (IS_ERR(fence)) - return NULL; + goto out_clean_job; if (job->base.irq_fence) dma_fence_put(job->base.irq_fence); @@ -336,6 +338,10 @@ v3d_tfu_job_run(struct drm_sched_job *sched_job) V3D_WRITE(V3D_TFU_ICFG(v3d->ver), job->args.icfg | V3D_TFU_ICFG_IOC); return fence; + +out_clean_job: + v3d->queue[V3D_TFU].active_job = NULL; + return fence; } static struct dma_fence * @@ -344,13 +350,11 @@ v3d_csd_job_run(struct drm_sched_job *sched_job) struct v3d_csd_job *job = to_csd_job(sched_job); struct v3d_dev *v3d = job->base.v3d; struct drm_device *dev = &v3d->drm; - struct dma_fence *fence; + struct dma_fence *fence = NULL; int i, csd_cfg0_reg; - if (unlikely(job->base.base.s_fence->finished.error)) { - v3d->queue[V3D_CSD].active_job = NULL; - return NULL; - } + if (unlikely(job->base.base.s_fence->finished.error)) + goto out_clean_job; /* The HW interprets a workgroup size of 0 as 65536; however, the * user-space driver exposes a maximum of 65535. Therefore, a 0 in @@ -368,7 +372,7 @@ v3d_csd_job_run(struct drm_sched_job *sched_job) fence = v3d_fence_create(v3d, V3D_CSD); if (IS_ERR(fence)) - return NULL; + goto out_clean_job; if (job->base.irq_fence) dma_fence_put(job->base.irq_fence); @@ -395,6 +399,10 @@ v3d_csd_job_run(struct drm_sched_job *sched_job) V3D_CORE_WRITE(0, csd_cfg0_reg, job->args.cfg[0]); return fence; + +out_clean_job: + v3d->queue[V3D_CSD].active_job = NULL; + return fence; } static void @@ -660,6 +668,9 @@ v3d_cpu_job_run(struct drm_sched_job *sched_job) struct v3d_cpu_job *job = to_cpu_job(sched_job); struct v3d_dev *v3d = job->base.v3d; + if (unlikely(job->base.base.s_fence->finished.error)) + return NULL; + if (job->job_type >= ARRAY_SIZE(cpu_job_function)) { drm_dbg(&v3d->drm, "Unknown CPU job: %d\n", job->job_type); return NULL; @@ -683,6 +694,9 @@ v3d_cache_clean_job_run(struct drm_sched_job *sched_job) struct v3d_job *job = to_v3d_job(sched_job); struct v3d_dev *v3d = job->v3d; + if (unlikely(job->base.s_fence->finished.error)) + return NULL; + v3d_job_start_stats(job); v3d_clean_caches(v3d); diff --git a/drivers/gpu/drm/v3d/v3d_submit.c b/drivers/gpu/drm/v3d/v3d_submit.c index 1db43c6a078d..ee2ac2540ed5 100644 --- a/drivers/gpu/drm/v3d/v3d_submit.c +++ b/drivers/gpu/drm/v3d/v3d_submit.c @@ -20,33 +20,51 @@ * to v3d, so we don't attach dma-buf fences to them. */ static int -v3d_lock_bo_reservations(struct v3d_job *job, - struct ww_acquire_ctx *acquire_ctx) +v3d_submit_lock_reservations(struct v3d_submit *submit) { - int i, ret; - - ret = drm_gem_lock_reservations(job->bo, job->bo_count, acquire_ctx); - if (ret) - return ret; - - for (i = 0; i < job->bo_count; i++) { - ret = dma_resv_reserve_fences(job->bo[i]->resv, 1); + int i, j, ret; + + drm_exec_init(&submit->exec, + DRM_EXEC_INTERRUPTIBLE_WAIT | DRM_EXEC_IGNORE_DUPLICATES, 0); + drm_exec_until_all_locked(&submit->exec) { + for (i = 0; i < submit->job_count; i++) { + struct v3d_job *job = submit->jobs[i]; + + ret = drm_exec_prepare_array(&submit->exec, job->bo, + job->bo_count, 1); + if (ret) + break; + } + drm_exec_retry_on_contention(&submit->exec); if (ret) goto fail; + } - ret = drm_sched_job_add_implicit_dependencies(&job->base, - job->bo[i], true); - if (ret) - goto fail; + for (i = 0; i < submit->job_count; i++) { + struct v3d_job *job = submit->jobs[i]; + + for (j = 0; j < job->bo_count; j++) { + ret = drm_sched_job_add_implicit_dependencies(&job->base, + job->bo[j], + true); + if (ret) + goto fail; + } } return 0; fail: - drm_gem_unlock_reservations(job->bo, job->bo_count, acquire_ctx); + drm_exec_fini(&submit->exec); return ret; } +static void +v3d_submit_unlock_reservations(struct v3d_submit *submit) +{ + drm_exec_fini(&submit->exec); +} + /** * v3d_lookup_bos() - Sets up job->bo[] with the GEM objects * referenced by the job. @@ -64,25 +82,23 @@ fail: * failure, because that will happen at `v3d_job_free()`. */ static int -v3d_lookup_bos(struct drm_device *dev, - struct drm_file *file_priv, - struct v3d_job *job, - u64 bo_handles, - u32 bo_count) +v3d_lookup_bos(struct v3d_submit *submit, u64 bo_handles, u32 bo_count) { - job->bo_count = bo_count; + struct v3d_job *last_job = submit->jobs[submit->job_count - 1]; - if (!job->bo_count) { + last_job->bo_count = bo_count; + + if (!last_job->bo_count) { /* See comment on bo_index for why we have to check * this. */ - drm_warn(dev, "Rendering requires BOs\n"); + drm_warn(&submit->v3d->drm, "Rendering requires BOs\n"); return -EINVAL; } - return drm_gem_objects_lookup(file_priv, + return drm_gem_objects_lookup(submit->file_priv, (void __user *)(uintptr_t)bo_handles, - job->bo_count, &job->bo); + last_job->bo_count, &last_job->bo); } static void @@ -162,74 +178,87 @@ void v3d_job_put(struct v3d_job *job) } static int -v3d_job_allocate(struct v3d_dev *v3d, void **container, size_t size) +v3d_job_add_syncobjs(struct v3d_job *job, struct drm_file *file_priv, + u32 in_sync, struct v3d_submit_ext *se) { - *container = kcalloc(1, size, GFP_KERNEL); - if (!*container) { - drm_err(&v3d->drm, "Cannot allocate memory for V3D job.\n"); - return -ENOMEM; + bool has_multisync = se && (se->flags & DRM_V3D_EXT_ID_MULTI_SYNC); + struct v3d_dev *v3d = job->v3d; + int ret = 0; + + if (!has_multisync) { + /* Ignore syncobj if its handle is zero */ + if (in_sync) + ret = drm_sched_job_add_syncobj_dependency(&job->base, file_priv, + in_sync, 0); + return ret; + } + + if (se->in_sync_count && se->wait_stage == job->queue) { + struct drm_v3d_sem __user *handle = u64_to_user_ptr(se->in_syncs); + + for (int i = 0; i < se->in_sync_count; i++) { + struct drm_v3d_sem in; + + if (copy_from_user(&in, handle++, sizeof(in))) { + drm_dbg(&v3d->drm, "Failed to copy wait dep handle.\n"); + return -EFAULT; + } + + /* Ignore syncobj if its handle is zero */ + if (in.handle) { + ret = drm_sched_job_add_syncobj_dependency(&job->base, + file_priv, in.handle, 0); + if (ret) + return ret; + } + } } return 0; } -static void -v3d_job_deallocate(void **container) -{ - kfree(*container); - *container = NULL; -} +static const struct { + size_t size; + void (*free)(struct kref *ref); +} v3d_job_types[] = { + [V3D_BIN] = { sizeof(struct v3d_bin_job), v3d_job_free }, + [V3D_RENDER] = { sizeof(struct v3d_render_job), v3d_render_job_free }, + [V3D_TFU] = { sizeof(struct v3d_tfu_job), v3d_job_free }, + [V3D_CSD] = { sizeof(struct v3d_csd_job), v3d_job_free }, + [V3D_CACHE_CLEAN] = { sizeof(struct v3d_job), v3d_job_free }, + [V3D_CPU] = { sizeof(struct v3d_cpu_job), v3d_cpu_job_free }, +}; -static int -v3d_job_init(struct v3d_dev *v3d, struct drm_file *file_priv, - struct v3d_job *job, void (*free)(struct kref *ref), - u32 in_sync, struct v3d_submit_ext *se, enum v3d_queue queue) +static struct v3d_job * +v3d_submit_add_job(struct v3d_submit *submit, enum v3d_queue queue) { - struct v3d_file_priv *v3d_priv = file_priv->driver_priv; - bool has_multisync = se && (se->flags & DRM_V3D_EXT_ID_MULTI_SYNC); - int ret, i; + struct v3d_file_priv *v3d_priv = submit->file_priv->driver_priv; + struct v3d_dev *v3d = submit->v3d; + struct v3d_job *job; + int ret; + + if (queue >= V3D_MAX_QUEUES) + return ERR_PTR(-EINVAL); + + job = kzalloc(v3d_job_types[queue].size, GFP_KERNEL); + if (!job) + return ERR_PTR(-ENOMEM); job->v3d = v3d; - job->free = free; + job->queue = queue; job->file_priv = v3d_priv; + job->free = v3d_job_types[queue].free; ret = drm_sched_job_init(&job->base, &v3d_priv->sched_entity[queue], - 1, v3d_priv, file_priv->client_id); + 1, v3d_priv, submit->file_priv->client_id); if (ret) - return ret; - - if (has_multisync) { - if (se->in_sync_count && se->wait_stage == queue) { - struct drm_v3d_sem __user *handle = u64_to_user_ptr(se->in_syncs); - - for (i = 0; i < se->in_sync_count; i++) { - struct drm_v3d_sem in; - - if (copy_from_user(&in, handle++, sizeof(in))) { - ret = -EFAULT; - drm_dbg(&v3d->drm, "Failed to copy wait dep handle.\n"); - goto fail_job_init; - } - ret = drm_sched_job_add_syncobj_dependency(&job->base, file_priv, in.handle, 0); - - // TODO: Investigate why this was filtered out for the IOCTL. - if (ret && ret != -ENOENT) - goto fail_job_init; - } - } - } else { - ret = drm_sched_job_add_syncobj_dependency(&job->base, file_priv, in_sync, 0); - - // TODO: Investigate why this was filtered out for the IOCTL. - if (ret && ret != -ENOENT) - goto fail_job_init; - } + goto fail_free; /* CPU jobs don't require hardware resources */ if (queue != V3D_CPU) { ret = v3d_pm_runtime_get(v3d); if (ret) - goto fail_job_init; + goto fail_sched_job; job->has_pm_ref = true; } @@ -238,52 +267,91 @@ v3d_job_init(struct v3d_dev *v3d, struct drm_file *file_priv, job->client_stats = v3d_stats_get(v3d_priv->stats[queue]); job->global_stats = v3d_stats_get(v3d->queue[queue].stats); - return 0; + submit->jobs[submit->job_count++] = job; + + return job; -fail_job_init: +fail_sched_job: drm_sched_job_cleanup(&job->base); - return ret; +fail_free: + kfree(job); + return ERR_PTR(ret); } static void -v3d_push_job(struct v3d_job *job) +v3d_submit_put_jobs(struct v3d_submit *submit) { - drm_sched_job_arm(&job->base); + for (int i = 0; i < submit->job_count; i++) + v3d_job_put(submit->jobs[i]); +} - job->done_fence = dma_fence_get(&job->base.s_fence->finished); +static void +v3d_submit_cleanup_jobs(struct v3d_submit *submit) +{ + for (int i = 0; i < submit->job_count; i++) + v3d_job_cleanup(submit->jobs[i]); +} + +static int +v3d_attach_perfmon_to_jobs(struct v3d_submit *submit, u32 perfmon_id) +{ + struct v3d_file_priv *v3d_priv = submit->file_priv->driver_priv; + struct v3d_dev *v3d = submit->v3d; + struct v3d_perfmon *perfmon; + + if (!perfmon_id) + return 0; + + if (v3d->global_perfmon) + return -EAGAIN; - /* put by scheduler job completion */ - kref_get(&job->refcount); + perfmon = v3d_perfmon_find(v3d_priv, perfmon_id); + if (!perfmon) + return -ENOENT; - drm_sched_entity_push_job(&job->base); + for (int i = 0; i < submit->job_count; i++) { + submit->jobs[i]->perfmon = perfmon; + if (i != 0) + v3d_perfmon_get(perfmon); + } + + return 0; } static void -v3d_attach_fences_and_unlock_reservation(struct drm_file *file_priv, - struct v3d_job *job, - struct ww_acquire_ctx *acquire_ctx, - u32 out_sync, - struct v3d_submit_ext *se, - struct dma_fence *done_fence) +v3d_submit_attach_object_fences(struct v3d_submit *submit) { - struct drm_syncobj *sync_out; - bool has_multisync = se && (se->flags & DRM_V3D_EXT_ID_MULTI_SYNC); - int i; - - for (i = 0; i < job->bo_count; i++) { - /* XXX: Use shared fences for read-only objects. */ - dma_resv_add_fence(job->bo[i]->resv, job->done_fence, - DMA_RESV_USAGE_WRITE); + struct v3d_job *last_job = submit->jobs[submit->job_count - 1]; + + /* The submission's last fence covers the entire submission. Attach it + * to every BO touched by any job in the submission. + */ + for (int i = 0; i < submit->job_count; i++) { + struct v3d_job *job = submit->jobs[i]; + + for (int j = 0; j < job->bo_count; j++) { + /* XXX: Use shared fences for read-only objects. */ + dma_resv_add_fence(job->bo[j]->resv, last_job->done_fence, + DMA_RESV_USAGE_WRITE); + } } +} - drm_gem_unlock_reservations(job->bo, job->bo_count, acquire_ctx); +static void +v3d_submit_process_post_deps(struct v3d_submit *submit, struct drm_syncobj *sync_out, + struct v3d_submit_ext *se) +{ + bool has_multisync = se && (se->flags & DRM_V3D_EXT_ID_MULTI_SYNC); + struct v3d_job *last_job = submit->jobs[submit->job_count - 1]; + + /* Make sure single syncobj and multisync are mutually exclusive */ + WARN_ON_ONCE(sync_out && has_multisync); /* Update the return sync object for the job */ /* If it only supports a single signal semaphore*/ if (!has_multisync) { - sync_out = drm_syncobj_find(file_priv, out_sync); if (sync_out) { - drm_syncobj_replace_fence(sync_out, done_fence); + drm_syncobj_replace_fence(sync_out, last_job->done_fence); drm_syncobj_put(sync_out); } return; @@ -291,9 +359,9 @@ v3d_attach_fences_and_unlock_reservation(struct drm_file *file_priv, /* If multiple semaphores extension is supported */ if (se->out_sync_count) { - for (i = 0; i < se->out_sync_count; i++) { + for (int i = 0; i < se->out_sync_count; i++) { drm_syncobj_replace_fence(se->out_syncs[i].syncobj, - done_fence); + last_job->done_fence); drm_syncobj_put(se->out_syncs[i].syncobj); } kvfree(se->out_syncs); @@ -301,53 +369,94 @@ v3d_attach_fences_and_unlock_reservation(struct drm_file *file_priv, } static int -v3d_setup_csd_jobs_and_bos(struct drm_file *file_priv, - struct v3d_dev *v3d, - struct drm_v3d_submit_csd *args, - struct v3d_csd_job **job, - struct v3d_job **clean_job, - struct v3d_submit_ext *se, - struct ww_acquire_ctx *acquire_ctx) +v3d_submit_jobs(struct v3d_submit *submit, struct drm_syncobj *sync_out, + struct v3d_submit_ext *se) { - int ret; + struct v3d_dev *v3d = submit->v3d; + int ret = 0; - ret = v3d_job_allocate(v3d, (void *)job, sizeof(**job)); - if (ret) - return ret; + mutex_lock(&v3d->sched_lock); - ret = v3d_job_init(v3d, file_priv, &(*job)->base, - v3d_job_free, args->in_sync, se, V3D_CSD); - if (ret) { - v3d_job_deallocate((void *)job); - return ret; - } + for (int i = 0; i < submit->job_count; i++) { + struct v3d_job *job = submit->jobs[i]; - ret = v3d_job_allocate(v3d, (void *)clean_job, sizeof(**clean_job)); - if (ret) - return ret; + drm_sched_job_arm(&job->base); + job->done_fence = dma_fence_get(&job->base.s_fence->finished); - ret = v3d_job_init(v3d, file_priv, *clean_job, - v3d_job_free, 0, NULL, V3D_CACHE_CLEAN); - if (ret) { - v3d_job_deallocate((void *)clean_job); - return ret; + /* put by scheduler job completion */ + kref_get(&job->refcount); } - (*job)->args = *args; + for (int i = 1; i < submit->job_count; i++) { + ret = drm_sched_job_add_dependency(&submit->jobs[i]->base, + dma_fence_get(submit->jobs[i - 1]->done_fence)); + if (ret) + goto err; + } + + for (int i = 0; i < submit->job_count; i++) + drm_sched_entity_push_job(&submit->jobs[i]->base); + + mutex_unlock(&v3d->sched_lock); + + v3d_submit_attach_object_fences(submit); + v3d_submit_unlock_reservations(submit); + v3d_submit_process_post_deps(submit, sync_out, se); + + v3d_submit_put_jobs(submit); + + return 0; + +err: + /* Mark every armed job as failed so run_job() skips execution */ + for (int i = 0; i < submit->job_count; i++) + dma_fence_set_error(&submit->jobs[i]->base.s_fence->finished, ret); + + for (int i = 0; i < submit->job_count; i++) + drm_sched_entity_push_job(&submit->jobs[i]->base); + + mutex_unlock(&v3d->sched_lock); + + v3d_submit_unlock_reservations(submit); + v3d_submit_put_jobs(submit); + + return ret; +} + +static int +v3d_setup_csd_jobs_and_bos(struct v3d_submit *submit, + struct drm_v3d_submit_csd *args, + struct v3d_submit_ext *se) +{ + struct v3d_csd_job *job; + struct v3d_job *clean_job; + int ret; + + job = (struct v3d_csd_job *)v3d_submit_add_job(submit, V3D_CSD); + if (IS_ERR(job)) + return PTR_ERR(job); - ret = v3d_lookup_bos(&v3d->drm, file_priv, *clean_job, - args->bo_handles, args->bo_handle_count); + ret = v3d_job_add_syncobjs(&job->base, submit->file_priv, args->in_sync, se); if (ret) return ret; - return v3d_lock_bo_reservations(*clean_job, acquire_ctx); + job->args = *args; + + clean_job = v3d_submit_add_job(submit, V3D_CACHE_CLEAN); + if (IS_ERR(clean_job)) + return PTR_ERR(clean_job); + + return v3d_lookup_bos(submit, args->bo_handles, args->bo_handle_count); } static void -v3d_put_multisync_post_deps(struct v3d_submit_ext *se) +v3d_submit_put_post_deps(struct drm_syncobj *sync_out, struct v3d_submit_ext *se) { unsigned int i; + if (sync_out) + drm_syncobj_put(sync_out); + if (!(se && se->out_sync_count)) return; @@ -489,6 +598,7 @@ v3d_get_cpu_indirect_csd_params(struct drm_file *file_priv, } job->job_type = V3D_CPU_JOB_TYPE_INDIRECT_CSD; + info->args = indirect_csd.submit; info->offset = indirect_csd.offset; info->wg_size = indirect_csd.wg_size; memcpy(&info->wg_uniform_offsets, &indirect_csd.wg_uniform_offsets, @@ -496,9 +606,7 @@ v3d_get_cpu_indirect_csd_params(struct drm_file *file_priv, info->indirect = drm_gem_object_lookup(file_priv, indirect_csd.indirect); - return v3d_setup_csd_jobs_and_bos(file_priv, v3d, &indirect_csd.submit, - &info->job, &info->clean_job, - NULL, &info->acquire_ctx); + return 0; } /* Get data for the query timestamp job submission. */ @@ -905,18 +1013,16 @@ int v3d_submit_cl_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv) { - struct v3d_dev *v3d = to_v3d_dev(dev); - struct v3d_file_priv *v3d_priv = file_priv->driver_priv; + struct v3d_submit submit = { .v3d = to_v3d_dev(dev), .file_priv = file_priv }; struct drm_v3d_submit_cl *args = data; + struct drm_syncobj *sync_out = NULL; struct v3d_submit_ext se = {0}; struct v3d_bin_job *bin = NULL; - struct v3d_render_job *render = NULL; - struct v3d_job *clean_job = NULL; - struct v3d_job *last_job; - struct ww_acquire_ctx acquire_ctx; - int ret = 0; + struct v3d_render_job *render; + struct v3d_job *clean_job; + int ret; - trace_v3d_submit_cl_ioctl(&v3d->drm, args->rcl_start, args->rcl_end); + trace_v3d_submit_cl_ioctl(dev, args->rcl_start, args->rcl_end); if (args->pad) return -EINVAL; @@ -936,30 +1042,17 @@ v3d_submit_cl_ioctl(struct drm_device *dev, void *data, } } - ret = v3d_job_allocate(v3d, (void *)&render, sizeof(*render)); - if (ret) - return ret; - - ret = v3d_job_init(v3d, file_priv, &render->base, - v3d_render_job_free, args->in_sync_rcl, &se, V3D_RENDER); - if (ret) { - v3d_job_deallocate((void *)&render); - goto fail; + /* If multisync is configured, give priority to it and ignore out_sync. */ + if (args->out_sync && !(se.flags & DRM_V3D_EXT_ID_MULTI_SYNC)) { + sync_out = drm_syncobj_find(file_priv, args->out_sync); + if (!sync_out) + return -ENOENT; } - render->start = args->rcl_start; - render->end = args->rcl_end; - INIT_LIST_HEAD(&render->unref_list); - if (args->bcl_start != args->bcl_end) { - ret = v3d_job_allocate(v3d, (void *)&bin, sizeof(*bin)); - if (ret) - goto fail; - - ret = v3d_job_init(v3d, file_priv, &bin->base, - v3d_job_free, args->in_sync_bcl, &se, V3D_BIN); - if (ret) { - v3d_job_deallocate((void *)&bin); + bin = (struct v3d_bin_job *)v3d_submit_add_job(&submit, V3D_BIN); + if (IS_ERR(bin)) { + ret = PTR_ERR(bin); goto fail; } @@ -968,101 +1061,60 @@ v3d_submit_cl_ioctl(struct drm_device *dev, void *data, bin->qma = args->qma; bin->qms = args->qms; bin->qts = args->qts; - bin->render = render; - } - if (args->flags & DRM_V3D_SUBMIT_CL_FLUSH_CACHE) { - ret = v3d_job_allocate(v3d, (void *)&clean_job, sizeof(*clean_job)); + ret = v3d_job_add_syncobjs(&bin->base, file_priv, args->in_sync_bcl, + &se); if (ret) goto fail; - - ret = v3d_job_init(v3d, file_priv, clean_job, - v3d_job_free, 0, NULL, V3D_CACHE_CLEAN); - if (ret) { - v3d_job_deallocate((void *)&clean_job); - goto fail; - } - - last_job = clean_job; - } else { - last_job = &render->base; } - ret = v3d_lookup_bos(dev, file_priv, last_job, - args->bo_handles, args->bo_handle_count); - if (ret) + render = (struct v3d_render_job *)v3d_submit_add_job(&submit, V3D_RENDER); + if (IS_ERR(render)) { + ret = PTR_ERR(render); goto fail; + } - ret = v3d_lock_bo_reservations(last_job, &acquire_ctx); - if (ret) - goto fail; + INIT_LIST_HEAD(&render->unref_list); + render->start = args->rcl_start; + render->end = args->rcl_end; - if (args->perfmon_id) { - if (v3d->global_perfmon) { - ret = -EAGAIN; - goto fail_perfmon; - } + if (bin) + bin->render = render; - render->base.perfmon = v3d_perfmon_find(v3d_priv, - args->perfmon_id); + ret = v3d_job_add_syncobjs(&render->base, file_priv, args->in_sync_rcl, &se); + if (ret) + goto fail; - if (!render->base.perfmon) { - ret = -ENOENT; - goto fail_perfmon; + if (args->flags & DRM_V3D_SUBMIT_CL_FLUSH_CACHE) { + clean_job = v3d_submit_add_job(&submit, V3D_CACHE_CLEAN); + if (IS_ERR(clean_job)) { + ret = PTR_ERR(clean_job); + goto fail; } } - mutex_lock(&v3d->sched_lock); - if (bin) { - bin->base.perfmon = render->base.perfmon; - v3d_perfmon_get(bin->base.perfmon); - v3d_push_job(&bin->base); - - ret = drm_sched_job_add_dependency(&render->base.base, - dma_fence_get(bin->base.done_fence)); - if (ret) - goto fail_unreserve; - } - - v3d_push_job(&render->base); - - if (clean_job) { - struct dma_fence *render_fence = - dma_fence_get(render->base.done_fence); - ret = drm_sched_job_add_dependency(&clean_job->base, - render_fence); - if (ret) - goto fail_unreserve; - clean_job->perfmon = render->base.perfmon; - v3d_perfmon_get(clean_job->perfmon); - v3d_push_job(clean_job); - } + ret = v3d_attach_perfmon_to_jobs(&submit, args->perfmon_id); + if (ret) + goto fail; - mutex_unlock(&v3d->sched_lock); + ret = v3d_lookup_bos(&submit, args->bo_handles, args->bo_handle_count); + if (ret) + goto fail; - v3d_attach_fences_and_unlock_reservation(file_priv, - last_job, - &acquire_ctx, - args->out_sync, - &se, - last_job->done_fence); + ret = v3d_submit_lock_reservations(&submit); + if (ret) + goto fail; - v3d_job_put(&bin->base); - v3d_job_put(&render->base); - v3d_job_put(clean_job); + ret = v3d_submit_jobs(&submit, sync_out, &se); + if (ret) + goto fail_submit; return 0; -fail_unreserve: - mutex_unlock(&v3d->sched_lock); -fail_perfmon: - drm_gem_unlock_reservations(last_job->bo, - last_job->bo_count, &acquire_ctx); fail: - v3d_job_cleanup((void *)bin); - v3d_job_cleanup((void *)render); - v3d_job_cleanup(clean_job); - v3d_put_multisync_post_deps(&se); + v3d_submit_cleanup_jobs(&submit); +fail_submit: + v3d_submit_put_post_deps(sync_out, &se); return ret; } @@ -1080,14 +1132,14 @@ int v3d_submit_tfu_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv) { - struct v3d_dev *v3d = to_v3d_dev(dev); + struct v3d_submit submit = { .v3d = to_v3d_dev(dev), .file_priv = file_priv }; struct drm_v3d_submit_tfu *args = data; + struct drm_syncobj *sync_out = NULL; struct v3d_submit_ext se = {0}; - struct v3d_tfu_job *job = NULL; - struct ww_acquire_ctx acquire_ctx; + struct v3d_tfu_job *job; int ret = 0; - trace_v3d_submit_tfu_ioctl(&v3d->drm, args->iia); + trace_v3d_submit_tfu_ioctl(dev, args->iia); if (args->flags && !(args->flags & DRM_V3D_SUBMIT_EXTENSION)) { drm_dbg(dev, "invalid flags: %d\n", args->flags); @@ -1102,17 +1154,23 @@ v3d_submit_tfu_ioctl(struct drm_device *dev, void *data, } } - ret = v3d_job_allocate(v3d, (void *)&job, sizeof(*job)); - if (ret) - return ret; + /* If multisync is configured, give priority to it and ignore out_sync. */ + if (args->out_sync && !(se.flags & DRM_V3D_EXT_ID_MULTI_SYNC)) { + sync_out = drm_syncobj_find(file_priv, args->out_sync); + if (!sync_out) + return -ENOENT; + } - ret = v3d_job_init(v3d, file_priv, &job->base, - v3d_job_free, args->in_sync, &se, V3D_TFU); - if (ret) { - v3d_job_deallocate((void *)&job); + job = (struct v3d_tfu_job *)v3d_submit_add_job(&submit, V3D_TFU); + if (IS_ERR(job)) { + ret = PTR_ERR(job); goto fail; } + ret = v3d_job_add_syncobjs(&job->base, file_priv, args->in_sync, &se); + if (ret) + goto fail; + job->base.bo = kzalloc_objs(*job->base.bo, ARRAY_SIZE(args->bo_handles)); if (!job->base.bo) { ret = -ENOMEM; @@ -1140,27 +1198,20 @@ v3d_submit_tfu_ioctl(struct drm_device *dev, void *data, job->base.bo[job->base.bo_count] = bo; } - ret = v3d_lock_bo_reservations(&job->base, &acquire_ctx); + ret = v3d_submit_lock_reservations(&submit); if (ret) goto fail; - mutex_lock(&v3d->sched_lock); - v3d_push_job(&job->base); - mutex_unlock(&v3d->sched_lock); - - v3d_attach_fences_and_unlock_reservation(file_priv, - &job->base, &acquire_ctx, - args->out_sync, - &se, - job->base.done_fence); - - v3d_job_put(&job->base); + ret = v3d_submit_jobs(&submit, sync_out, &se); + if (ret) + goto fail_submit; return 0; fail: - v3d_job_cleanup((void *)job); - v3d_put_multisync_post_deps(&se); + v3d_submit_cleanup_jobs(&submit); +fail_submit: + v3d_submit_put_post_deps(sync_out, &se); return ret; } @@ -1178,21 +1229,18 @@ int v3d_submit_csd_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv) { - struct v3d_dev *v3d = to_v3d_dev(dev); - struct v3d_file_priv *v3d_priv = file_priv->driver_priv; + struct v3d_submit submit = { .v3d = to_v3d_dev(dev), .file_priv = file_priv }; struct drm_v3d_submit_csd *args = data; + struct drm_syncobj *sync_out = NULL; struct v3d_submit_ext se = {0}; - struct v3d_csd_job *job = NULL; - struct v3d_job *clean_job = NULL; - struct ww_acquire_ctx acquire_ctx; int ret; - trace_v3d_submit_csd_ioctl(&v3d->drm, args->cfg[5], args->cfg[6]); + trace_v3d_submit_csd_ioctl(dev, args->cfg[5], args->cfg[6]); if (args->pad) return -EINVAL; - if (!v3d_has_csd(v3d)) { + if (!v3d_has_csd(submit.v3d)) { drm_warn(dev, "Attempting CSD submit on non-CSD hardware\n"); return -EINVAL; } @@ -1210,58 +1258,35 @@ v3d_submit_csd_ioctl(struct drm_device *dev, void *data, } } - ret = v3d_setup_csd_jobs_and_bos(file_priv, v3d, args, - &job, &clean_job, &se, - &acquire_ctx); - if (ret) - goto fail; - - if (args->perfmon_id) { - if (v3d->global_perfmon) { - ret = -EAGAIN; - goto fail_perfmon; - } - - job->base.perfmon = v3d_perfmon_find(v3d_priv, - args->perfmon_id); - if (!job->base.perfmon) { - ret = -ENOENT; - goto fail_perfmon; - } + /* If multisync is configured, give priority to it and ignore out_sync. */ + if (args->out_sync && !(se.flags & DRM_V3D_EXT_ID_MULTI_SYNC)) { + sync_out = drm_syncobj_find(file_priv, args->out_sync); + if (!sync_out) + return -ENOENT; } - mutex_lock(&v3d->sched_lock); - v3d_push_job(&job->base); - - ret = drm_sched_job_add_dependency(&clean_job->base, - dma_fence_get(job->base.done_fence)); + ret = v3d_setup_csd_jobs_and_bos(&submit, args, &se); if (ret) - goto fail_unreserve; + goto fail; - v3d_push_job(clean_job); - mutex_unlock(&v3d->sched_lock); + ret = v3d_attach_perfmon_to_jobs(&submit, args->perfmon_id); + if (ret) + goto fail; - v3d_attach_fences_and_unlock_reservation(file_priv, - clean_job, - &acquire_ctx, - args->out_sync, - &se, - clean_job->done_fence); + ret = v3d_submit_lock_reservations(&submit); + if (ret) + goto fail; - v3d_job_put(&job->base); - v3d_job_put(clean_job); + ret = v3d_submit_jobs(&submit, sync_out, &se); + if (ret) + goto fail_submit; return 0; -fail_unreserve: - mutex_unlock(&v3d->sched_lock); -fail_perfmon: - drm_gem_unlock_reservations(clean_job->bo, clean_job->bo_count, - &acquire_ctx); fail: - v3d_job_cleanup((void *)job); - v3d_job_cleanup(clean_job); - v3d_put_multisync_post_deps(&se); + v3d_submit_cleanup_jobs(&submit); +fail_submit: + v3d_submit_put_post_deps(sync_out, &se); return ret; } @@ -1288,14 +1313,10 @@ int v3d_submit_cpu_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv) { - struct v3d_dev *v3d = to_v3d_dev(dev); + struct v3d_submit submit = { .v3d = to_v3d_dev(dev), .file_priv = file_priv }; struct drm_v3d_submit_cpu *args = data; struct v3d_submit_ext se = {0}; - struct v3d_submit_ext *out_se = NULL; struct v3d_cpu_job *cpu_job = NULL; - struct v3d_csd_job *csd_job = NULL; - struct v3d_job *clean_job = NULL; - struct ww_acquire_ctx acquire_ctx; int ret; if (args->flags && !(args->flags & DRM_V3D_SUBMIT_EXTENSION)) { @@ -1303,9 +1324,9 @@ v3d_submit_cpu_ioctl(struct drm_device *dev, void *data, return -EINVAL; } - ret = v3d_job_allocate(v3d, (void *)&cpu_job, sizeof(*cpu_job)); - if (ret) - return ret; + cpu_job = (struct v3d_cpu_job *)v3d_submit_add_job(&submit, V3D_CPU); + if (IS_ERR(cpu_job)) + return PTR_ERR(cpu_job); if (args->flags & DRM_V3D_SUBMIT_EXTENSION) { ret = v3d_get_extensions(file_priv, args->extensions, &se, cpu_job); @@ -1328,92 +1349,51 @@ v3d_submit_cpu_ioctl(struct drm_device *dev, void *data, goto fail; } - trace_v3d_submit_cpu_ioctl(&v3d->drm, cpu_job->job_type); + trace_v3d_submit_cpu_ioctl(dev, cpu_job->job_type); - ret = v3d_job_init(v3d, file_priv, &cpu_job->base, - v3d_cpu_job_free, 0, &se, V3D_CPU); - if (ret) { - v3d_job_deallocate((void *)&cpu_job); + ret = v3d_job_add_syncobjs(&cpu_job->base, file_priv, 0, &se); + if (ret) goto fail; - } - - clean_job = cpu_job->indirect_csd.clean_job; - csd_job = cpu_job->indirect_csd.job; + /* Look up the CPU jobs' BOs before v3d_setup_csd_jobs_and_bos() appends + * the CSD and clean jobs in the case of indirect CSD job. + */ if (args->bo_handle_count) { - ret = v3d_lookup_bos(dev, file_priv, &cpu_job->base, - args->bo_handles, args->bo_handle_count); - if (ret) - goto fail; - - ret = v3d_lock_bo_reservations(&cpu_job->base, &acquire_ctx); + ret = v3d_lookup_bos(&submit, args->bo_handles, args->bo_handle_count); if (ret) goto fail; } - mutex_lock(&v3d->sched_lock); - v3d_push_job(&cpu_job->base); - - switch (cpu_job->job_type) { - case V3D_CPU_JOB_TYPE_INDIRECT_CSD: - ret = drm_sched_job_add_dependency(&csd_job->base.base, - dma_fence_get(cpu_job->base.done_fence)); - if (ret) - goto fail_unreserve; - - v3d_push_job(&csd_job->base); - - ret = drm_sched_job_add_dependency(&clean_job->base, - dma_fence_get(csd_job->base.done_fence)); + if (cpu_job->job_type == V3D_CPU_JOB_TYPE_INDIRECT_CSD) { + ret = v3d_setup_csd_jobs_and_bos(&submit, &cpu_job->indirect_csd.args, + NULL); if (ret) - goto fail_unreserve; + goto fail; - v3d_push_job(clean_job); + /* The CSD job was appended at jobs[1] */ + if (WARN_ON(submit.jobs[1]->queue != V3D_CSD)) { + ret = -EINVAL; + goto fail; + } - break; - default: - break; + cpu_job->indirect_csd.job = container_of(submit.jobs[1], struct v3d_csd_job, + base); } - mutex_unlock(&v3d->sched_lock); - out_se = (cpu_job->job_type == V3D_CPU_JOB_TYPE_INDIRECT_CSD) ? NULL : &se; - - v3d_attach_fences_and_unlock_reservation(file_priv, - &cpu_job->base, - &acquire_ctx, 0, - out_se, cpu_job->base.done_fence); - - switch (cpu_job->job_type) { - case V3D_CPU_JOB_TYPE_INDIRECT_CSD: - v3d_attach_fences_and_unlock_reservation(file_priv, - clean_job, - &cpu_job->indirect_csd.acquire_ctx, - 0, &se, clean_job->done_fence); - break; - default: - break; - } + ret = v3d_submit_lock_reservations(&submit); + if (ret) + goto fail; - v3d_job_put(&cpu_job->base); - v3d_job_put(&csd_job->base); - v3d_job_put(clean_job); + ret = v3d_submit_jobs(&submit, NULL, &se); + if (ret) + goto fail_submit; return 0; -fail_unreserve: - mutex_unlock(&v3d->sched_lock); - - drm_gem_unlock_reservations(cpu_job->base.bo, cpu_job->base.bo_count, - &acquire_ctx); - - drm_gem_unlock_reservations(clean_job->bo, clean_job->bo_count, - &cpu_job->indirect_csd.acquire_ctx); - fail: - v3d_job_cleanup((void *)cpu_job); - v3d_job_cleanup((void *)csd_job); - v3d_job_cleanup(clean_job); - v3d_put_multisync_post_deps(&se); + v3d_submit_cleanup_jobs(&submit); +fail_submit: + v3d_submit_put_post_deps(NULL, &se); return ret; } diff --git a/drivers/gpu/drm/vc4/vc4_dsi.c b/drivers/gpu/drm/vc4/vc4_dsi.c index fc76018b044c..0148f2befed1 100644 --- a/drivers/gpu/drm/vc4/vc4_dsi.c +++ b/drivers/gpu/drm/vc4/vc4_dsi.c @@ -1411,7 +1411,7 @@ static const struct mipi_dsi_host_ops vc4_dsi_host_ops = { 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_create_state = drm_atomic_helper_bridge_create_state, .atomic_pre_enable = vc4_dsi_bridge_pre_enable, .atomic_enable = vc4_dsi_bridge_enable, .atomic_disable = vc4_dsi_bridge_disable, diff --git a/drivers/gpu/drm/vc4/vc4_hdmi.c b/drivers/gpu/drm/vc4/vc4_hdmi.c index a161d3b00a25..74dce4be0c00 100644 --- a/drivers/gpu/drm/vc4/vc4_hdmi.c +++ b/drivers/gpu/drm/vc4/vc4_hdmi.c @@ -508,7 +508,7 @@ static int vc4_hdmi_connector_atomic_check(struct drm_connector *connector, static void vc4_hdmi_connector_reset(struct drm_connector *connector) { drm_atomic_helper_connector_reset(connector); - __drm_atomic_helper_connector_hdmi_reset(connector, connector->state); + __drm_atomic_helper_connector_hdmi_state_init(connector, connector->state); drm_atomic_helper_connector_tv_margins_reset(connector); } diff --git a/drivers/gpu/drm/verisilicon/vs_bridge.c b/drivers/gpu/drm/verisilicon/vs_bridge.c index 7a93049368db..dc7c85b07fe3 100644 --- a/drivers/gpu/drm/verisilicon/vs_bridge.c +++ b/drivers/gpu/drm/verisilicon/vs_bridge.c @@ -246,7 +246,7 @@ static const struct drm_bridge_funcs vs_dpi_bridge_funcs = { .atomic_get_output_bus_fmts = vs_bridge_atomic_get_output_bus_fmts_dpi, .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_create_state = drm_atomic_helper_bridge_create_state, }; static const struct drm_bridge_funcs vs_dp_bridge_funcs = { @@ -258,7 +258,7 @@ static const struct drm_bridge_funcs vs_dp_bridge_funcs = { .atomic_get_output_bus_fmts = vs_bridge_atomic_get_output_bus_fmts_dp, .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_create_state = drm_atomic_helper_bridge_create_state, }; static int vs_bridge_detect_output_interface(struct device_node *of_node, diff --git a/drivers/gpu/drm/virtio/virtgpu_drv.c b/drivers/gpu/drm/virtio/virtgpu_drv.c index 405097e388a5..2aaa7cb08085 100644 --- a/drivers/gpu/drm/virtio/virtgpu_drv.c +++ b/drivers/gpu/drm/virtio/virtgpu_drv.c @@ -119,10 +119,24 @@ err_free: return ret; } +/* + * Release pending virtqueue waits so the drm_dev_enter/exit() critical + * sections complete before drm_dev_unplug() blocks on synchronize_srcu(). + */ +static void virtio_gpu_release_vqs(struct drm_device *dev) +{ + struct virtio_gpu_device *vgdev = dev->dev_private; + + vgdev->vqs_released = true; + wake_up_all(&vgdev->ctrlq.ack_queue); + wake_up_all(&vgdev->cursorq.ack_queue); +} + static void virtio_gpu_remove(struct virtio_device *vdev) { struct drm_device *dev = vdev->priv; + virtio_gpu_release_vqs(dev); drm_dev_unplug(dev); if (drm_core_check_feature(dev, DRIVER_ATOMIC)) @@ -136,6 +150,7 @@ static void virtio_gpu_shutdown(struct virtio_device *vdev) { struct drm_device *dev = vdev->priv; + virtio_gpu_release_vqs(dev); /* stop talking to the device */ drm_dev_unplug(dev); } @@ -168,6 +183,86 @@ static unsigned int features[] = { VIRTIO_GPU_F_CONTEXT_INIT, VIRTIO_GPU_F_BLOB_ALIGNMENT, }; + +#ifdef CONFIG_PM_SLEEP +static void virtgpu_hibernation_restore(struct virtio_gpu_device *vgdev) +{ + if (vgdev->hibernated) { + vgdev->hibernated = false; + virtio_gpu_object_restore_all(vgdev); + } +} + +static int virtgpu_freeze(struct virtio_device *vdev) +{ + struct drm_device *dev = vdev->priv; + struct virtio_gpu_device *vgdev = dev->dev_private; + int error; + + error = drm_mode_config_helper_suspend(dev); + if (error) { + DRM_ERROR("suspend error: %d\n", error); + return error; + } + + if (vgdev->hibernated) + virtio_gpu_object_unref_all(vgdev); + + flush_work(&vgdev->obj_free_work); + flush_work(&vgdev->ctrlq.dequeue_work); + flush_work(&vgdev->cursorq.dequeue_work); + flush_work(&vgdev->config_changed_work); + + error = virtio_gpu_wait_queue(&vgdev->ctrlq, vgdev->ctrlq.vq->num_max); + if (error) { + DRM_ERROR("ctrlq wait failed: %d\n", error); + goto err_resume; + } + + error = virtio_gpu_wait_queue(&vgdev->cursorq, vgdev->cursorq.vq->num_max); + if (error) { + DRM_ERROR("cursorq wait failed: %d\n", error); + goto err_resume; + } + + vdev->config->del_vqs(vdev); + + return 0; + +err_resume: + virtgpu_hibernation_restore(vgdev); + + drm_mode_config_helper_resume(dev); + + return error; +} + +static int virtgpu_restore(struct virtio_device *vdev) +{ + struct drm_device *dev = vdev->priv; + struct virtio_gpu_device *vgdev = dev->dev_private; + int error; + + error = virtio_gpu_find_vqs(vgdev); + if (error) { + DRM_ERROR("failed to find virt queues\n"); + return error; + } + + virtio_device_ready(vdev); + + virtgpu_hibernation_restore(vgdev); + + error = drm_mode_config_helper_resume(dev); + if (error) { + DRM_ERROR("resume error: %d\n", error); + return error; + } + + return 0; +} +#endif + static struct virtio_driver virtio_gpu_driver = { .feature_table = features, .feature_table_size = ARRAY_SIZE(features), @@ -176,7 +271,11 @@ static struct virtio_driver virtio_gpu_driver = { .probe = virtio_gpu_probe, .remove = virtio_gpu_remove, .shutdown = virtio_gpu_shutdown, - .config_changed = virtio_gpu_config_changed + .config_changed = virtio_gpu_config_changed, +#ifdef CONFIG_PM_SLEEP + .freeze = virtgpu_freeze, + .restore = virtgpu_restore, +#endif }; static int __init virtio_gpu_driver_init(void) diff --git a/drivers/gpu/drm/virtio/virtgpu_drv.h b/drivers/gpu/drm/virtio/virtgpu_drv.h index 7449907754a4..17a6a4d26516 100644 --- a/drivers/gpu/drm/virtio/virtgpu_drv.h +++ b/drivers/gpu/drm/virtio/virtgpu_drv.h @@ -99,6 +99,10 @@ struct virtio_gpu_object { int uuid_state; uuid_t uuid; + + /* for restoration of objects after hibernation */ + struct virtio_gpu_object_params params; + struct list_head restore_node; }; #define gem_to_virtio_gpu_obj(gobj) \ container_of((gobj), struct virtio_gpu_object, base.base) @@ -236,6 +240,7 @@ struct virtio_gpu_device { struct virtio_gpu_queue ctrlq; struct virtio_gpu_queue cursorq; + bool vqs_released; struct kmem_cache *vbufs; atomic_t pending_commands; @@ -259,6 +264,7 @@ struct virtio_gpu_device { bool has_host_visible; bool has_context_init; bool has_blob_alignment; + bool hibernated; struct virtio_shm_region host_visible_region; struct drm_mm host_visible_mm; @@ -267,6 +273,8 @@ struct virtio_gpu_device { struct work_struct obj_free_work; spinlock_t obj_free_lock; struct list_head obj_free_list; + struct mutex obj_restore_lock; + struct list_head obj_restore_list; struct virtio_gpu_drv_capset *capsets; uint32_t num_capsets; @@ -274,6 +282,8 @@ struct virtio_gpu_device { struct list_head cap_cache; uint32_t blob_alignment; + struct notifier_block pm_nb; + /* protects uuid state when exporting */ spinlock_t resource_export_lock; /* protects map state and host_visible_mm */ @@ -303,6 +313,7 @@ void virtio_gpu_deinit(struct drm_device *dev); void virtio_gpu_release(struct drm_device *dev); int virtio_gpu_driver_open(struct drm_device *dev, struct drm_file *file); void virtio_gpu_driver_postclose(struct drm_device *dev, struct drm_file *file); +int virtio_gpu_find_vqs(struct virtio_gpu_device *vgdev); /* virtgpu_gem.c */ int virtio_gpu_gem_object_open(struct drm_gem_object *obj, @@ -338,7 +349,8 @@ void virtio_gpu_cmd_create_resource(struct virtio_gpu_device *vgdev, struct virtio_gpu_object_array *objs, struct virtio_gpu_fence *fence); void virtio_gpu_cmd_unref_resource(struct virtio_gpu_device *vgdev, - struct virtio_gpu_object *bo); + struct virtio_gpu_object *bo, + bool no_cb); int virtio_gpu_panic_cmd_transfer_to_host_2d(struct virtio_gpu_device *vgdev, uint64_t offset, uint32_t width, uint32_t height, @@ -424,6 +436,7 @@ void virtio_gpu_dequeue_ctrl_func(struct work_struct *work); void virtio_gpu_dequeue_cursor_func(struct work_struct *work); void virtio_gpu_panic_notify(struct virtio_gpu_device *vgdev); void virtio_gpu_notify(struct virtio_gpu_device *vgdev); +int virtio_gpu_wait_queue(struct virtio_gpu_queue *vgvq, unsigned int num_elem); int virtio_gpu_cmd_resource_assign_uuid(struct virtio_gpu_device *vgdev, @@ -470,6 +483,7 @@ void virtio_gpu_fence_event_process(struct virtio_gpu_device *vdev, u64 fence_id); /* virtgpu_object.c */ +void virtio_gpu_remove_from_restore_list(struct virtio_gpu_object *bo); void virtio_gpu_cleanup_object(struct virtio_gpu_object *bo); struct drm_gem_object *virtio_gpu_create_object(struct drm_device *dev, size_t size); @@ -482,6 +496,14 @@ bool virtio_gpu_is_shmem(struct virtio_gpu_object *bo); int virtio_gpu_resource_id_get(struct virtio_gpu_device *vgdev, uint32_t *resid); + +void virtio_gpu_add_object_to_restore_list(struct virtio_gpu_device *vgdev, + struct virtio_gpu_object *bo); + +int virtio_gpu_object_restore_all(struct virtio_gpu_device *vgdev); + +void virtio_gpu_object_unref_all(struct virtio_gpu_device *vgdev); + /* virtgpu_prime.c */ int virtio_gpu_resource_assign_uuid(struct virtio_gpu_device *vgdev, struct virtio_gpu_object *bo); @@ -496,6 +518,8 @@ int virtgpu_dma_buf_import_sgt(struct virtio_gpu_mem_entry **ents, unsigned int *nents, struct virtio_gpu_object *bo, struct dma_buf_attachment *attach); +int virtgpu_dma_buf_obj_resubmit(struct virtio_gpu_device *vgdev, + struct virtio_gpu_object *bo); /* virtgpu_debugfs.c */ void virtio_gpu_debugfs_init(struct drm_minor *minor); diff --git a/drivers/gpu/drm/virtio/virtgpu_kms.c b/drivers/gpu/drm/virtio/virtgpu_kms.c index cfde9f573df6..223d5d513b5a 100644 --- a/drivers/gpu/drm/virtio/virtgpu_kms.c +++ b/drivers/gpu/drm/virtio/virtgpu_kms.c @@ -23,6 +23,7 @@ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +#include <linux/suspend.h> #include <linux/virtio.h> #include <linux/virtio_config.h> #include <linux/virtio_ring.h> @@ -115,15 +116,53 @@ static void virtio_gpu_get_capsets(struct virtio_gpu_device *vgdev, vgdev->num_capsets = num_capsets; } -int virtio_gpu_init(struct virtio_device *vdev, struct drm_device *dev) +int virtio_gpu_find_vqs(struct virtio_gpu_device *vgdev) { struct virtqueue_info vqs_info[] = { { "control", virtio_gpu_ctrl_ack }, { "cursor", virtio_gpu_cursor_ack }, }; - struct virtio_gpu_device *vgdev; - /* this will expand later */ struct virtqueue *vqs[2]; + int ret; + + ret = virtio_find_vqs(vgdev->vdev, 2, vqs, vqs_info, NULL); + if (ret) + return ret; + + vgdev->ctrlq.vq = vqs[0]; + vgdev->cursorq.vq = vqs[1]; + + return 0; +} + +static int virtio_gpu_pm_notifier(struct notifier_block *nb, unsigned long mode, + void *data) +{ + struct virtio_gpu_device *vgdev = container_of(nb, + struct virtio_gpu_device, + pm_nb); + + switch (mode) { + case PM_HIBERNATION_PREPARE: + if (vgdev->has_virgl_3d) { + DRM_ERROR("S4 not allowed when VIRGL is enabled\n"); + return notifier_from_errno(-EPERM); + } + + vgdev->hibernated = true; + break; + + case PM_POST_HIBERNATION: + vgdev->hibernated = false; + break; + } + + return NOTIFY_DONE; +} + +int virtio_gpu_init(struct virtio_device *vdev, struct drm_device *dev) +{ + struct virtio_gpu_device *vgdev; u32 num_scanouts, num_capsets, blob_alignment; int ret = 0; @@ -158,6 +197,8 @@ int virtio_gpu_init(struct virtio_device *vdev, struct drm_device *dev) virtio_gpu_array_put_free_work); INIT_LIST_HEAD(&vgdev->obj_free_list); spin_lock_init(&vgdev->obj_free_lock); + INIT_LIST_HEAD(&vgdev->obj_restore_list); + mutex_init(&vgdev->obj_restore_lock); #ifdef __LITTLE_ENDIAN if (virtio_has_feature(vgdev->vdev, VIRTIO_GPU_F_VIRGL)) @@ -215,13 +256,11 @@ int virtio_gpu_init(struct virtio_device *vdev, struct drm_device *dev) vgdev->has_context_init ? '+' : '-', vgdev->has_blob_alignment ? '+' : '-'); - ret = virtio_find_vqs(vgdev->vdev, 2, vqs, vqs_info, NULL); + ret = virtio_gpu_find_vqs(vgdev); if (ret) { DRM_ERROR("failed to find virt queues\n"); goto err_vqs; } - vgdev->ctrlq.vq = vqs[0]; - vgdev->cursorq.vq = vqs[1]; ret = virtio_gpu_alloc_vbufs(vgdev); if (ret) { DRM_ERROR("failed to alloc vbufs\n"); @@ -262,11 +301,25 @@ int virtio_gpu_init(struct virtio_device *vdev, struct drm_device *dev) virtio_gpu_cmd_get_edids(vgdev); virtio_gpu_cmd_get_display_info(vgdev); virtio_gpu_notify(vgdev); - wait_event_timeout(vgdev->resp_wq, !vgdev->display_info_pending, - 5 * HZ); + if (!wait_event_timeout(vgdev->resp_wq, + !vgdev->display_info_pending, + 5 * HZ)) { + DRM_ERROR("timed out waiting for display info\n"); + ret = -ETIMEDOUT; + goto err_reset_device; + } } + + vgdev->pm_nb.notifier_call = virtio_gpu_pm_notifier; + ret = register_pm_notifier(&vgdev->pm_nb); + if (ret) + goto err_reset_device; + return 0; +err_reset_device: + virtio_reset_device(vgdev->vdev); + virtio_gpu_modeset_fini(vgdev); err_scanouts: virtio_gpu_free_vbufs(vgdev); err_vbufs: @@ -290,12 +343,14 @@ void virtio_gpu_deinit(struct drm_device *dev) { struct virtio_gpu_device *vgdev = dev->dev_private; + unregister_pm_notifier(&vgdev->pm_nb); flush_work(&vgdev->obj_free_work); flush_work(&vgdev->ctrlq.dequeue_work); flush_work(&vgdev->cursorq.dequeue_work); flush_work(&vgdev->config_changed_work); virtio_reset_device(vgdev->vdev); vgdev->vdev->config->del_vqs(vgdev->vdev); + mutex_destroy(&vgdev->obj_restore_lock); } void virtio_gpu_release(struct drm_device *dev) diff --git a/drivers/gpu/drm/virtio/virtgpu_object.c b/drivers/gpu/drm/virtio/virtgpu_object.c index ec9efacc6919..9bc0bd68c314 100644 --- a/drivers/gpu/drm/virtio/virtgpu_object.c +++ b/drivers/gpu/drm/virtio/virtgpu_object.c @@ -63,6 +63,15 @@ static void virtio_gpu_resource_id_put(struct virtio_gpu_device *vgdev, uint32_t ida_free(&vgdev->resource_ida, id - 1); } +void virtio_gpu_remove_from_restore_list(struct virtio_gpu_object *bo) +{ + struct virtio_gpu_device *vgdev = bo->base.base.dev->dev_private; + + mutex_lock(&vgdev->obj_restore_lock); + list_del_init(&bo->restore_node); + mutex_unlock(&vgdev->obj_restore_lock); +} + void virtio_gpu_cleanup_object(struct virtio_gpu_object *bo) { struct virtio_gpu_device *vgdev = bo->base.base.dev->dev_private; @@ -94,7 +103,8 @@ static void virtio_gpu_free_object(struct drm_gem_object *obj) struct virtio_gpu_device *vgdev = bo->base.base.dev->dev_private; if (bo->created) { - virtio_gpu_cmd_unref_resource(vgdev, bo); + virtio_gpu_remove_from_restore_list(bo); + virtio_gpu_cmd_unref_resource(vgdev, bo, false); virtio_gpu_notify(vgdev); /* completion handler calls virtio_gpu_cleanup_object() */ return; @@ -220,6 +230,8 @@ int virtio_gpu_object_create(struct virtio_gpu_device *vgdev, return PTR_ERR(shmem_obj); bo = gem_to_virtio_gpu_obj(&shmem_obj->base); + INIT_LIST_HEAD(&bo->restore_node); + ret = virtio_gpu_resource_id_get(vgdev, &bo->hw_res_handle); if (ret < 0) goto err_free_gem; @@ -258,6 +270,12 @@ int virtio_gpu_object_create(struct virtio_gpu_device *vgdev, virtio_gpu_object_attach(vgdev, bo, ents, nents); } + if (!params->virgl) { + /* store non-virgl object with its param to the restore list */ + bo->params = *params; + virtio_gpu_add_object_to_restore_list(vgdev, bo); + } + *bo_ptr = bo; return 0; @@ -271,3 +289,73 @@ err_free_gem: drm_gem_shmem_free(shmem_obj); return ret; } + +void virtio_gpu_add_object_to_restore_list(struct virtio_gpu_device *vgdev, + struct virtio_gpu_object *bo) +{ + mutex_lock(&vgdev->obj_restore_lock); + list_add_tail(&bo->restore_node, &vgdev->obj_restore_list); + mutex_unlock(&vgdev->obj_restore_lock); +} + +int virtio_gpu_object_restore_all(struct virtio_gpu_device *vgdev) +{ + struct virtio_gpu_object *bo, *tmp; + struct virtio_gpu_mem_entry *ents; + unsigned int nents; + int ret = 0; + + mutex_lock(&vgdev->obj_restore_lock); + list_for_each_entry_safe(bo, tmp, &vgdev->obj_restore_list, + restore_node) { + if (drm_gem_is_imported(&bo->base.base)) { + ret = virtgpu_dma_buf_obj_resubmit(vgdev, bo); + if (ret) + break; + + continue; + } + + if (bo->params.blob || bo->attached) { + ret = virtio_gpu_object_shmem_init(vgdev, bo, &ents, + &nents); + if (ret) + break; + } + + if (bo->params.blob) { + virtio_gpu_cmd_resource_create_blob(vgdev, bo, + &bo->params, + ents, nents); + } else { + virtio_gpu_cmd_create_resource(vgdev, bo, &bo->params, + NULL, NULL); + if (bo->attached) { + bo->attached = false; + virtio_gpu_object_attach(vgdev, bo, ents, + nents); + } + } + } + mutex_unlock(&vgdev->obj_restore_lock); + + if (ret) + DRM_ERROR("failed to restore virtio-gpu objects: %d\n", ret); + + return ret; +} + +void virtio_gpu_object_unref_all(struct virtio_gpu_device *vgdev) +{ + struct virtio_gpu_object *bo, *tmp; + + mutex_lock(&vgdev->obj_restore_lock); + list_for_each_entry_safe(bo, tmp, &vgdev->obj_restore_list, + restore_node) + if (bo->created) { + virtio_gpu_cmd_unref_resource(vgdev, bo, true); + virtio_gpu_notify(vgdev); + } + + mutex_unlock(&vgdev->obj_restore_lock); +} diff --git a/drivers/gpu/drm/virtio/virtgpu_prime.c b/drivers/gpu/drm/virtio/virtgpu_prime.c index 70b3b836e1c9..216c77cd0d21 100644 --- a/drivers/gpu/drm/virtio/virtgpu_prime.c +++ b/drivers/gpu/drm/virtio/virtgpu_prime.c @@ -23,6 +23,7 @@ */ #include <drm/drm_prime.h> +#include <drm/drm_print.h> #include <linux/virtio_dma_buf.h> #include "virtgpu_drv.h" @@ -216,7 +217,8 @@ static void virtgpu_dma_buf_free_obj(struct drm_gem_object *obj) } if (bo->created) { - virtio_gpu_cmd_unref_resource(vgdev, bo); + virtio_gpu_remove_from_restore_list(bo); + virtio_gpu_cmd_unref_resource(vgdev, bo, false); virtio_gpu_notify(vgdev); return; } @@ -262,6 +264,13 @@ static int virtgpu_dma_buf_init_obj(struct drm_device *dev, dma_buf_unpin(attach); dma_resv_unlock(resv); + /* + * Store the dmabuf imported object with its params to the + * restore list. + */ + bo->params = params; + virtio_gpu_add_object_to_restore_list(vgdev, bo); + return 0; err_import: @@ -272,6 +281,38 @@ err_pin: return ret; } +int virtgpu_dma_buf_obj_resubmit(struct virtio_gpu_device *vgdev, + struct virtio_gpu_object *bo) +{ + struct virtio_gpu_mem_entry *ents; + struct scatterlist *sl; + int i; + + if (!bo->sgt) { + DRM_ERROR("no sgt bound to virtio_gpu_object\n"); + return -ENOMEM; + } + + ents = kvmalloc_array(bo->sgt->nents, + sizeof(struct virtio_gpu_mem_entry), + GFP_KERNEL); + if (!ents) { + DRM_ERROR("failed to allocate ent list\n"); + return -ENOMEM; + } + + for_each_sgtable_dma_sg(bo->sgt, sl, i) { + ents[i].addr = cpu_to_le64(sg_dma_address(sl)); + ents[i].length = cpu_to_le32(sg_dma_len(sl)); + ents[i].padding = 0; + } + + virtio_gpu_cmd_resource_create_blob(vgdev, bo, &bo->params, + ents, bo->sgt->nents); + + return 0; +} + static const struct drm_gem_object_funcs virtgpu_gem_dma_buf_funcs = { .free = virtgpu_dma_buf_free_obj, }; @@ -317,6 +358,8 @@ struct drm_gem_object *virtgpu_gem_prime_import(struct drm_device *dev, if (!bo) return ERR_PTR(-ENOMEM); + INIT_LIST_HEAD(&bo->restore_node); + obj = &bo->base.base; obj->resv = buf->resv; obj->funcs = &virtgpu_gem_dma_buf_funcs; diff --git a/drivers/gpu/drm/virtio/virtgpu_vq.c b/drivers/gpu/drm/virtio/virtgpu_vq.c index c8b9475a7472..905b1f42cd98 100644 --- a/drivers/gpu/drm/virtio/virtgpu_vq.c +++ b/drivers/gpu/drm/virtio/virtgpu_vq.c @@ -369,6 +369,20 @@ static int virtio_gpu_panic_queue_ctrl_sgs(struct virtio_gpu_device *vgdev, return 0; } +int virtio_gpu_wait_queue(struct virtio_gpu_queue *vgvq, unsigned int num_elem) +{ + int ret; + + /* Wait up to 5 seconds for enough free slots to become available */ + ret = wait_event_timeout(vgvq->ack_queue, + vgvq->vq->num_free >= num_elem, + 5 * HZ); + if (ret == 0) + return -ETIMEDOUT; + + return 0; +} + static int virtio_gpu_queue_ctrl_sgs(struct virtio_gpu_device *vgdev, struct virtio_gpu_vbuffer *vbuf, struct virtio_gpu_fence *fence, @@ -396,7 +410,19 @@ again: if (vq->num_free < elemcnt) { spin_unlock(&vgdev->ctrlq.qlock); virtio_gpu_notify(vgdev); - wait_event(vgdev->ctrlq.ack_queue, vq->num_free >= elemcnt); + wait_event(vgdev->ctrlq.ack_queue, + vq->num_free >= elemcnt || vgdev->vqs_released); + /* + * Set by virtio_gpu_release_vqs() to unblock + * synchronize_srcu() wait in drm_dev_unplug(). + */ + if (vgdev->vqs_released) { + if (fence && vbuf->objs) + virtio_gpu_array_unlock_resv(vbuf->objs); + free_vbuf(vgdev, vbuf); + drm_dev_exit(idx); + return -ENODEV; + } goto again; } @@ -566,7 +592,14 @@ retry: ret = virtqueue_add_sgs(vq, sgs, outcnt, 0, vbuf, GFP_ATOMIC); if (ret == -ENOSPC) { spin_unlock(&vgdev->cursorq.qlock); - wait_event(vgdev->cursorq.ack_queue, vq->num_free >= outcnt); + wait_event(vgdev->cursorq.ack_queue, + vq->num_free >= outcnt || vgdev->vqs_released); + /* See comment in virtio_gpu_queue_ctrl_sgs(). */ + if (vgdev->vqs_released) { + free_vbuf(vgdev, vbuf); + drm_dev_exit(idx); + return; + } spin_lock(&vgdev->cursorq.qlock); goto retry; } else { @@ -626,14 +659,21 @@ static void virtio_gpu_cmd_unref_cb(struct virtio_gpu_device *vgdev, } void virtio_gpu_cmd_unref_resource(struct virtio_gpu_device *vgdev, - struct virtio_gpu_object *bo) + struct virtio_gpu_object *bo, + bool no_cb) { struct virtio_gpu_resource_unref *cmd_p; struct virtio_gpu_vbuffer *vbuf; int ret; - cmd_p = virtio_gpu_alloc_cmd_cb(vgdev, &vbuf, sizeof(*cmd_p), - virtio_gpu_cmd_unref_cb); + if (no_cb) { + cmd_p = virtio_gpu_alloc_cmd_cb(vgdev, &vbuf, sizeof(*cmd_p), + NULL); + } else { + cmd_p = virtio_gpu_alloc_cmd_cb(vgdev, &vbuf, sizeof(*cmd_p), + virtio_gpu_cmd_unref_cb); + } + memset(cmd_p, 0, sizeof(*cmd_p)); cmd_p->hdr.type = cpu_to_le32(VIRTIO_GPU_CMD_RESOURCE_UNREF); diff --git a/drivers/gpu/drm/virtio/virtgpu_vram.c b/drivers/gpu/drm/virtio/virtgpu_vram.c index 4ae3cbc35dd3..e2e799b42c61 100644 --- a/drivers/gpu/drm/virtio/virtgpu_vram.c +++ b/drivers/gpu/drm/virtio/virtgpu_vram.c @@ -20,7 +20,8 @@ static void virtio_gpu_vram_free(struct drm_gem_object *obj) if (unmap) virtio_gpu_cmd_unmap(vgdev, bo); - virtio_gpu_cmd_unref_resource(vgdev, bo); + virtio_gpu_remove_from_restore_list(bo); + virtio_gpu_cmd_unref_resource(vgdev, bo, false); virtio_gpu_notify(vgdev); return; } @@ -207,6 +208,8 @@ int virtio_gpu_vram_create(struct virtio_gpu_device *vgdev, obj = &vram->base.base.base; obj->funcs = &virtio_gpu_vram_funcs; + INIT_LIST_HEAD(&vram->base.restore_node); + params->size = PAGE_ALIGN(params->size); drm_gem_private_object_init(vgdev->ddev, obj, params->size); diff --git a/drivers/gpu/drm/xe/xe_bo.c b/drivers/gpu/drm/xe/xe_bo.c index 4c80bac67622..85e6d9a0f575 100644 --- a/drivers/gpu/drm/xe/xe_bo.c +++ b/drivers/gpu/drm/xe/xe_bo.c @@ -670,7 +670,7 @@ static int xe_bo_trigger_rebind(struct xe_device *xe, struct xe_bo *bo, dma_resv_iter_begin(&cursor, bo->ttm.base.resv, DMA_RESV_USAGE_BOOKKEEP); dma_resv_for_each_fence_unlocked(&cursor, fence) - dma_fence_enable_sw_signaling(fence); + dma_fence_enable_signaling(fence); dma_resv_iter_end(&cursor); } diff --git a/drivers/gpu/drm/xe/xe_device.c b/drivers/gpu/drm/xe/xe_device.c index abe25aedeead..4efd2bc8c02e 100644 --- a/drivers/gpu/drm/xe/xe_device.c +++ b/drivers/gpu/drm/xe/xe_device.c @@ -396,7 +396,7 @@ static const struct drm_driver regular_driver = { XE_DISPLAY_DRIVER_FEATURES | DRIVER_GEM | DRIVER_RENDER | DRIVER_SYNCOBJ | - DRIVER_SYNCOBJ_TIMELINE | DRIVER_GEM_GPUVA, + DRIVER_SYNCOBJ_TIMELINE, .open = xe_file_open, .postclose = xe_file_close, @@ -427,7 +427,7 @@ static const struct drm_ioctl_desc xe_ioctls_admin_only[] = { static const struct drm_driver admin_only_driver = { .driver_features = XE_DISPLAY_DRIVER_FEATURES | - DRIVER_GEM | DRIVER_RENDER | DRIVER_GEM_GPUVA, + DRIVER_GEM | DRIVER_RENDER, .open = xe_file_open, .postclose = xe_file_close, .ioctls = xe_ioctls_admin_only, diff --git a/drivers/gpu/drm/xe/xe_sched_job.c b/drivers/gpu/drm/xe/xe_sched_job.c index ae5b38b2a884..a4fa00632a30 100644 --- a/drivers/gpu/drm/xe/xe_sched_job.c +++ b/drivers/gpu/drm/xe/xe_sched_job.c @@ -214,7 +214,7 @@ void xe_sched_job_set_error(struct xe_sched_job *job, int error) trace_xe_sched_job_set_error(job); - dma_fence_enable_sw_signaling(job->fence); + dma_fence_enable_signaling(job->fence); xe_hw_fence_irq_run(job->q->fence_irq); } diff --git a/drivers/gpu/drm/xe/xe_svm.c b/drivers/gpu/drm/xe/xe_svm.c index b1e1ac26c66d..e0f0c23d172d 100644 --- a/drivers/gpu/drm/xe/xe_svm.c +++ b/drivers/gpu/drm/xe/xe_svm.c @@ -1090,7 +1090,7 @@ static int xe_drm_pagemap_populate_mm(struct drm_pagemap *dpagemap, dma_resv_wait_timeout(bo->ttm.base.resv, DMA_RESV_USAGE_KERNEL, false, MAX_SCHEDULE_TIMEOUT); else if (pre_migrate_fence) - dma_fence_enable_sw_signaling(pre_migrate_fence); + dma_fence_enable_signaling(pre_migrate_fence); } drm_pagemap_devmem_init(&bo->devmem_allocation, dev, mm, diff --git a/drivers/gpu/drm/xe/xe_userptr.c b/drivers/gpu/drm/xe/xe_userptr.c index 6f71bc66b14e..1d2ab678faf5 100644 --- a/drivers/gpu/drm/xe/xe_userptr.c +++ b/drivers/gpu/drm/xe/xe_userptr.c @@ -180,7 +180,7 @@ xe_vma_userptr_invalidate_pass1(struct xe_vm *vm, struct xe_userptr_vma *uvma) dma_resv_iter_begin(&cursor, xe_vm_resv(vm), DMA_RESV_USAGE_BOOKKEEP); dma_resv_for_each_fence_unlocked(&cursor, fence) { - dma_fence_enable_sw_signaling(fence); + dma_fence_enable_signaling(fence); if (signaled && !dma_fence_is_signaled(fence)) signaled = false; } diff --git a/drivers/gpu/drm/xe/xe_vm.c b/drivers/gpu/drm/xe/xe_vm.c index 080c2fff0e95..73ac031ffb04 100644 --- a/drivers/gpu/drm/xe/xe_vm.c +++ b/drivers/gpu/drm/xe/xe_vm.c @@ -256,7 +256,7 @@ int xe_vm_add_compute_exec_queue(struct xe_vm *vm, struct xe_exec_queue *q) */ wait = __xe_vm_userptr_needs_repin(vm) || preempt_fences_waiting(vm); if (wait) - dma_fence_enable_sw_signaling(pfence); + dma_fence_enable_signaling(pfence); xe_svm_notifier_unlock(vm); @@ -287,7 +287,7 @@ void xe_vm_remove_compute_exec_queue(struct xe_vm *vm, struct xe_exec_queue *q) --vm->preempt.num_exec_queues; } if (q->lr.pfence) { - dma_fence_enable_sw_signaling(q->lr.pfence); + dma_fence_enable_signaling(q->lr.pfence); dma_fence_put(q->lr.pfence); q->lr.pfence = NULL; } diff --git a/drivers/gpu/drm/xlnx/zynqmp_dp.c b/drivers/gpu/drm/xlnx/zynqmp_dp.c index 7fb11b0a44f0..b209582bc130 100644 --- a/drivers/gpu/drm/xlnx/zynqmp_dp.c +++ b/drivers/gpu/drm/xlnx/zynqmp_dp.c @@ -2280,7 +2280,7 @@ static const struct drm_bridge_funcs zynqmp_dp_bridge_funcs = { .atomic_disable = zynqmp_dp_bridge_atomic_disable, .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_create_state = drm_atomic_helper_bridge_create_state, .atomic_check = zynqmp_dp_bridge_atomic_check, .detect = zynqmp_dp_bridge_detect, .edid_read = zynqmp_dp_bridge_edid_read, diff --git a/include/drm/bridge/analogix_dp.h b/include/drm/bridge/analogix_dp.h index 854af692229b..0e0b87abee59 100644 --- a/include/drm/bridge/analogix_dp.h +++ b/include/drm/bridge/analogix_dp.h @@ -16,12 +16,21 @@ enum analogix_dp_devtype { EXYNOS_DP, RK3288_DP, RK3399_EDP, + RK3576_EDP, RK3588_EDP, }; -static inline bool is_rockchip(enum analogix_dp_devtype type) +static inline bool analogix_dp_is_rockchip(enum analogix_dp_devtype type) { - return type == RK3288_DP || type == RK3399_EDP || type == RK3588_EDP; + switch (type) { + case RK3288_DP: + case RK3399_EDP: + case RK3576_EDP: + case RK3588_EDP: + return true; + default: + return false; + } } struct analogix_dp_plat_data { diff --git a/include/drm/bridge/dw_dp.h b/include/drm/bridge/dw_dp.h index 25363541e69d..22105c3e8e4d 100644 --- a/include/drm/bridge/dw_dp.h +++ b/include/drm/bridge/dw_dp.h @@ -24,4 +24,5 @@ struct dw_dp_plat_data { struct dw_dp *dw_dp_bind(struct device *dev, struct drm_encoder *encoder, const struct dw_dp_plat_data *plat_data); +void dw_dp_unbind(struct dw_dp *dp); #endif /* __DW_DP__ */ diff --git a/include/drm/display/drm_dp.h b/include/drm/display/drm_dp.h index 829e4d98d61c..7154216e0aff 100644 --- a/include/drm/display/drm_dp.h +++ b/include/drm/display/drm_dp.h @@ -354,15 +354,24 @@ # define DP_DSC_20_PER_DP_DSC_SINK (1 << 1) # define DP_DSC_24_PER_DP_DSC_SINK (1 << 2) -#define DP_DSC_BITS_PER_PIXEL_INC 0x06F +#define DP_DSC_MAX_BPP_DELTA_VERSION_1 0x06E # define DP_DSC_RGB_YCbCr444_MAX_BPP_DELTA_MASK 0x1f -# define DP_DSC_RGB_YCbCr420_MAX_BPP_DELTA_MASK 0xe0 +# define DP_DSC_NATIVE_YCbCr420_MAX_BPP_DELTA_MASK 0xe0 + +# define DP_DSC_BPP_DELTA_444 16 +# define DP_DSC_BPP_DELTA_420 12 +# define DP_DSC_BPP_DELTA_SHIFT_420 5 + +#define DP_DSC_BITS_PER_PIXEL_INC 0x06F # define DP_DSC_BITS_PER_PIXEL_1_16 0x0 # define DP_DSC_BITS_PER_PIXEL_1_8 0x1 # define DP_DSC_BITS_PER_PIXEL_1_4 0x2 # define DP_DSC_BITS_PER_PIXEL_1_2 0x3 # define DP_DSC_BITS_PER_PIXEL_1_1 0x4 # define DP_DSC_BITS_PER_PIXEL_MASK 0x7 +# define DP_DSC_NATIVE_YCbCr422_MAX_BPP_DELTA_MASK 0x78 +# define DP_DSC_BPP_DELTA_NATIVE_SHIFT_422 3 +# define DP_DSC_BPP_DELTA_NATIVE_422 16 #define DP_PSR_SUPPORT 0x070 /* XXX 1.2? */ # define DP_PSR_IS_SUPPORTED 1 diff --git a/include/drm/display/drm_hdmi_state_helper.h b/include/drm/display/drm_hdmi_state_helper.h index 0adc30c55ec9..13375bd0f4ae 100644 --- a/include/drm/display/drm_hdmi_state_helper.h +++ b/include/drm/display/drm_hdmi_state_helper.h @@ -11,8 +11,8 @@ struct hdmi_audio_infoframe; enum drm_connector_status; -void __drm_atomic_helper_connector_hdmi_reset(struct drm_connector *connector, - struct drm_connector_state *new_conn_state); +void __drm_atomic_helper_connector_hdmi_state_init(struct drm_connector *connector, + struct drm_connector_state *new_conn_state); int drm_atomic_helper_connector_hdmi_check(struct drm_connector *connector, struct drm_atomic_commit *state); diff --git a/include/drm/drm_atomic.h b/include/drm/drm_atomic.h index 1a80a8cdf269..88087910ab1a 100644 --- a/include/drm/drm_atomic.h +++ b/include/drm/drm_atomic.h @@ -265,7 +265,10 @@ struct drm_private_state_funcs { * @atomic_create_state: * * Allocates a pristine, initialized, state for the private - * object and returns it. + * object and returns it. This callback must have no side + * effects: in particular, the returned state must not be + * assigned to the object's state pointer and it must not affect + * the hardware state. * * RETURNS: * diff --git a/include/drm/drm_atomic_helper.h b/include/drm/drm_atomic_helper.h index b84152810abb..4cfeec70d648 100644 --- a/include/drm/drm_atomic_helper.h +++ b/include/drm/drm_atomic_helper.h @@ -295,4 +295,11 @@ drm_atomic_helper_bridge_propagate_bus_fmt(struct drm_bridge *bridge, u32 output_fmt, unsigned int *num_input_fmts); +u32 * +drm_atomic_helper_bridge_get_hdmi_output_bus_fmts(struct drm_bridge *bridge, + struct drm_bridge_state *bridge_state, + struct drm_crtc_state *crtc_state, + struct drm_connector_state *conn_state, + unsigned int *num_output_fmts); + #endif /* DRM_ATOMIC_HELPER_H_ */ diff --git a/include/drm/drm_atomic_state_helper.h b/include/drm/drm_atomic_state_helper.h index 61a3b38ad49f..34a599c3d86d 100644 --- a/include/drm/drm_atomic_state_helper.h +++ b/include/drm/drm_atomic_state_helper.h @@ -40,11 +40,13 @@ struct drm_private_state; struct drm_modeset_acquire_ctx; struct drm_device; -void __drm_atomic_helper_crtc_state_reset(struct drm_crtc_state *state, +void __drm_atomic_helper_crtc_state_init(struct drm_crtc_state *state, struct drm_crtc *crtc); void __drm_atomic_helper_crtc_reset(struct drm_crtc *crtc, struct drm_crtc_state *state); void drm_atomic_helper_crtc_reset(struct drm_crtc *crtc); +struct drm_crtc_state * +drm_atomic_helper_crtc_create_state(struct drm_crtc *crtc); void __drm_atomic_helper_crtc_duplicate_state(struct drm_crtc *crtc, struct drm_crtc_state *state); struct drm_crtc_state * @@ -53,8 +55,10 @@ void __drm_atomic_helper_crtc_destroy_state(struct drm_crtc_state *state); void drm_atomic_helper_crtc_destroy_state(struct drm_crtc *crtc, struct drm_crtc_state *state); -void __drm_atomic_helper_plane_state_reset(struct drm_plane_state *state, +void __drm_atomic_helper_plane_state_init(struct drm_plane_state *state, struct drm_plane *plane); +struct drm_plane_state * +drm_atomic_helper_plane_create_state(struct drm_plane *plane); void __drm_atomic_helper_plane_reset(struct drm_plane *plane, struct drm_plane_state *state); void drm_atomic_helper_plane_reset(struct drm_plane *plane); @@ -66,11 +70,13 @@ void __drm_atomic_helper_plane_destroy_state(struct drm_plane_state *state); void drm_atomic_helper_plane_destroy_state(struct drm_plane *plane, struct drm_plane_state *state); -void __drm_atomic_helper_connector_state_reset(struct drm_connector_state *conn_state, +void __drm_atomic_helper_connector_state_init(struct drm_connector_state *conn_state, struct drm_connector *connector); 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); +struct drm_connector_state * +drm_atomic_helper_connector_create_state(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_commit *state); @@ -96,7 +102,7 @@ struct drm_bridge_state * drm_atomic_helper_bridge_duplicate_state(struct drm_bridge *bridge); void drm_atomic_helper_bridge_destroy_state(struct drm_bridge *bridge, struct drm_bridge_state *state); -void __drm_atomic_helper_bridge_reset(struct drm_bridge *bridge, - struct drm_bridge_state *state); +void __drm_atomic_helper_bridge_state_init(struct drm_bridge_state *state, + struct drm_bridge *bridge); struct drm_bridge_state * -drm_atomic_helper_bridge_reset(struct drm_bridge *bridge); +drm_atomic_helper_bridge_create_state(struct drm_bridge *bridge); diff --git a/include/drm/drm_bridge.h b/include/drm/drm_bridge.h index 4ba3a5deef9a..18f3db367dc1 100644 --- a/include/drm/drm_bridge.h +++ b/include/drm/drm_bridge.h @@ -504,31 +504,20 @@ struct drm_bridge_funcs { struct drm_connector_state *conn_state); /** - * @atomic_reset: + * @atomic_create_state: * - * Reset the bridge to a predefined state (or retrieve its current - * state) and return a &drm_bridge_state object matching this state. - * This function is called at attach time. - * - * The atomic_reset hook is mandatory if the bridge implements any of - * the atomic hooks, and should be left unassigned otherwise. For - * bridges that don't subclass &drm_bridge_state, the - * drm_atomic_helper_bridge_reset() helper function shall be used to - * implement this hook. - * - * Note that the atomic_reset() semantics is not exactly matching the - * reset() semantics found on other components (connector, plane, ...). - * - * 1. The reset operation happens when the bridge is attached, not when - * drm_mode_config_reset() is called - * 2. It's meant to be used exclusively on bridges that have been - * converted to the ATOMIC API + * Allocate a pristine, initialized, state for the bridge + * object and return it. This callback must have no side + * effects: in particular, the returned state must not be + * assigned to the object's state pointer and it must not affect + * the hardware state. * * RETURNS: - * A valid drm_bridge_state object in case of success, an ERR_PTR() - * giving the reason of the failure otherwise. + * + * A new, pristine, bridge state instance or an error pointer + * on failure. */ - struct drm_bridge_state *(*atomic_reset)(struct drm_bridge *bridge); + struct drm_bridge_state *(*atomic_create_state)(struct drm_bridge *bridge); /** * @detect: @@ -1257,6 +1246,10 @@ struct drm_bridge { */ struct mutex hpd_mutex; /** + * @hpd_state_mutex: Protects the HPD en/disablement state for the bridge. + */ + struct mutex hpd_state_mutex; + /** * @hpd_cb: Hot plug detection callback, registered with * drm_bridge_hpd_enable(). */ @@ -1371,7 +1364,7 @@ drm_bridge_get_current_state(struct drm_bridge *bridge) * drm_atomic_private_obj_init(), so we need to make sure we're * working with one before we try to use the lock. */ - if (!bridge->funcs || !bridge->funcs->atomic_reset) + if (!bridge->funcs || !bridge->funcs->atomic_create_state) return NULL; drm_modeset_lock_assert_held(&bridge->base.lock); diff --git a/include/drm/drm_colorop.h b/include/drm/drm_colorop.h index d08a6a8a8392..224fae40ed2b 100644 --- a/include/drm/drm_colorop.h +++ b/include/drm/drm_colorop.h @@ -424,6 +424,8 @@ int drm_plane_colorop_3dlut_init(struct drm_device *dev, struct drm_colorop *col uint32_t flags); struct drm_colorop_state * +drm_atomic_helper_colorop_create_state(struct drm_colorop *colorop); +struct drm_colorop_state * drm_atomic_helper_colorop_duplicate_state(struct drm_colorop *colorop); void drm_colorop_atomic_destroy_state(struct drm_colorop *colorop, diff --git a/include/drm/drm_connector.h b/include/drm/drm_connector.h index 5f5ca023cd65..af075c38f4db 100644 --- a/include/drm/drm_connector.h +++ b/include/drm/drm_connector.h @@ -571,12 +571,80 @@ enum drm_colorspace { * YCbCr 4:2:2 output format (ie. with horizontal subsampling) * @DRM_OUTPUT_COLOR_FORMAT_YCBCR420: * YCbCr 4:2:0 output format (ie. with horizontal and vertical subsampling) + * @DRM_OUTPUT_COLOR_FORMAT_COUNT: + * Number of valid output color format values in this enum */ enum drm_output_color_format { DRM_OUTPUT_COLOR_FORMAT_RGB444 = 0, DRM_OUTPUT_COLOR_FORMAT_YCBCR444, DRM_OUTPUT_COLOR_FORMAT_YCBCR422, DRM_OUTPUT_COLOR_FORMAT_YCBCR420, + DRM_OUTPUT_COLOR_FORMAT_COUNT, +}; + +/** + * enum drm_connector_color_format - Connector Color Format Request + * + * This enum, unlike &enum drm_output_color_format, is used to specify requests + * for a specific color format on a connector through the DRM "color format" + * property. The difference is that it has an "AUTO" value to specify that + * no specific choice has been made. + */ +enum drm_connector_color_format { + /** + * @DRM_CONNECTOR_COLOR_FORMAT_AUTO: The driver or display protocol + * helpers should pick a suitable color format. All implementations of a + * specific display protocol must behave the same way with "AUTO", but + * different display protocols do not necessarily have the same "AUTO" + * semantics. + * + * For HDMI, "AUTO" picks RGB, but falls back to YCbCr 4:2:0 if the + * bandwidth required for full-scale RGB is not available, or the mode + * is YCbCr 4:2:0-only, as long as the mode and output both support + * YCbCr 4:2:0. + * + * For display protocols other than HDMI, the recursive bridge chain + * format selection picks the first chain of bridge formats that works, + * as has already been the case before the introduction of the "color + * format" property. Non-HDMI bridges should therefore either sort their + * bus output formats by preference, or agree on a unified auto format + * selection logic that's implemented in a common state helper (like + * how HDMI does it). + */ + DRM_CONNECTOR_COLOR_FORMAT_AUTO = 0, + + /** + * @DRM_CONNECTOR_COLOR_FORMAT_RGB444: RGB output format. The + * quantization range depends on the value of the "Broadcast RGB" + * property if it is present on the connector. + */ + DRM_CONNECTOR_COLOR_FORMAT_RGB444, + + /** + * @DRM_CONNECTOR_COLOR_FORMAT_YCBCR444: YCbCr 4:4:4 output format (ie. + * not subsampled). Quantization range is "Limited" by default. + */ + DRM_CONNECTOR_COLOR_FORMAT_YCBCR444, + + /** + * @DRM_CONNECTOR_COLOR_FORMAT_YCBCR422: YCbCr 4:2:2 output format (ie. + * with horizontal subsampling). Quantization range is "Limited" by + * default. + */ + DRM_CONNECTOR_COLOR_FORMAT_YCBCR422, + + /** + * @DRM_CONNECTOR_COLOR_FORMAT_YCBCR420: YCbCr 4:2:0 output format (ie. + * with horizontal and vertical subsampling). Quantization range is + * "Limited" by default. + */ + DRM_CONNECTOR_COLOR_FORMAT_YCBCR420, + + /** + * @DRM_CONNECTOR_COLOR_FORMAT_COUNT: Number of valid connector color + * format values in this enum + */ + DRM_CONNECTOR_COLOR_FORMAT_COUNT, }; const char * @@ -921,6 +989,12 @@ struct drm_display_info { * @amd_vsdb: AMD-specific VSDB information. */ struct drm_amd_vsdb_info amd_vsdb; + + /** + * @panel_type: Panel type from DisplayID Display Parameters + * Data Block (tag 0x21). Uses DRM_MODE_PANEL_TYPE_* constants. + */ + u8 panel_type; }; int drm_display_info_set_bus_formats(struct drm_display_info *info, @@ -1168,6 +1242,13 @@ struct drm_connector_state { enum drm_colorspace colorspace; /** + * @color_format: State variable for Connector property to request + * color format change on Sink. This is most commonly used to switch + * between RGB to YUV and vice-versa. + */ + enum drm_connector_color_format color_format; + + /** * @writeback_job: Writeback job for writeback connectors * * Holds the framebuffer and out-fence for a writeback connector. As @@ -1572,6 +1653,22 @@ struct drm_connector_funcs { void (*destroy)(struct drm_connector *connector); /** + * @atomic_create_state: + * + * Allocate a pristine, initialized, state for the connector + * object and return it. This callback must have no side + * effects: in particular, the returned state must not be + * assigned to the object's state pointer and it must not affect + * the hardware state. + * + * RETURNS: + * + * A new, pristine, connector state instance or an error pointer + * on failure. + */ + struct drm_connector_state *(*atomic_create_state)(struct drm_connector *connector); + + /** * @atomic_duplicate_state: * * Duplicate the current atomic state for this connector and return it. @@ -1712,6 +1809,16 @@ struct drm_connector_funcs { * Allows connectors to create connector-specific debugfs files. */ void (*debugfs_init)(struct drm_connector *connector, struct dentry *root); + + /** + * @color_format: + * + * Allows connectors to return a connector color format other than + * @conn_state.color_format for purposes of e.g. display protocol + * specific helper logic having already mapped it to an output format. + */ + enum drm_connector_color_format (*color_format)( + const struct drm_connector_state *conn_state); }; /** @@ -2166,6 +2273,12 @@ struct drm_connector { struct drm_property *colorspace_property; /** + * @color_format_property: Connector property to set the suitable + * color format supported by the sink. + */ + struct drm_property *color_format_property; + + /** * @path_blob_ptr: * * DRM blob property data for the DP MST path property. This should only @@ -2522,6 +2635,8 @@ drm_connector_is_unregistered(struct drm_connector *connector) void drm_connector_oob_hotplug_event(struct fwnode_handle *connector_fwnode, enum drm_connector_status status); +enum drm_connector_color_format +drm_connector_get_color_format(const struct drm_connector_state *conn_state); const char *drm_get_connector_type_name(unsigned int connector_type); const char *drm_get_connector_status_name(enum drm_connector_status status); const char *drm_get_subpixel_order_name(enum subpixel_order order); @@ -2648,6 +2763,9 @@ bool drm_connector_has_possible_encoder(struct drm_connector *connector, struct drm_encoder *encoder); const char *drm_get_colorspace_name(enum drm_colorspace colorspace); +int drm_connector_attach_color_format_property(struct drm_connector *connector, + unsigned long supported_color_formats); + /** * drm_for_each_connector_iter - connector_list iterator macro * @connector: &struct drm_connector pointer used as cursor diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h index c6dbe8b7db9e..152349f973e3 100644 --- a/include/drm/drm_crtc.h +++ b/include/drm/drm_crtc.h @@ -639,6 +639,22 @@ struct drm_crtc_funcs { struct drm_property *property, uint64_t val); /** + * @atomic_create_state: + * + * Allocate a pristine, initialized, state for the CRTC object + * and return it. This callback must have no side effects: in + * particular, the returned state must not be assigned to the + * object's state pointer and it must not affect the hardware + * state. + * + * RETURNS: + * + * A new, pristine, CRTC state instance or an error pointer + * on failure. + */ + struct drm_crtc_state *(*atomic_create_state)(struct drm_crtc *crtc); + + /** * @atomic_duplicate_state: * * Duplicate the current atomic state for this CRTC and return it. diff --git a/include/drm/drm_debugfs.h b/include/drm/drm_debugfs.h index ea8cba94208a..eb93512b0f23 100644 --- a/include/drm/drm_debugfs.h +++ b/include/drm/drm_debugfs.h @@ -48,7 +48,7 @@ * For each DRM GPU VA space drivers should call drm_debugfs_gpuva_info() from * their @show callback. */ -#define DRM_DEBUGFS_GPUVA_INFO(show, data) {"gpuvas", show, DRIVER_GEM_GPUVA, data} +#define DRM_DEBUGFS_GPUVA_INFO(show, data) {"gpuvas", show, 0, data} /** * struct drm_info_list - debugfs info list entry diff --git a/include/drm/drm_drv.h b/include/drm/drm_drv.h index 42fc085f986d..e09559495c5b 100644 --- a/include/drm/drm_drv.h +++ b/include/drm/drm_drv.h @@ -108,12 +108,6 @@ enum drm_driver_feature { */ DRIVER_COMPUTE_ACCEL = BIT(7), /** - * @DRIVER_GEM_GPUVA: - * - * Driver supports user defined GPU VA bindings for GEM objects. - */ - DRIVER_GEM_GPUVA = BIT(8), - /** * @DRIVER_CURSOR_HOTSPOT: * * Driver supports and requires cursor hotspot information in the diff --git a/include/drm/drm_gem.h b/include/drm/drm_gem.h index 8a704f6a65c1..885244e375d3 100644 --- a/include/drm/drm_gem.h +++ b/include/drm/drm_gem.h @@ -661,9 +661,6 @@ static inline bool drm_gem_is_imported(const struct drm_gem_object *obj) * * This initializes the &drm_gem_object's &drm_gpuvm_bo list. * - * Calling this function is only necessary for drivers intending to support the - * &drm_driver_feature DRIVER_GEM_GPUVA. - * * See also drm_gem_gpuva_set_lock(). */ static inline void drm_gem_gpuva_init(struct drm_gem_object *obj) diff --git a/include/drm/drm_gem_shmem_helper.h b/include/drm/drm_gem_shmem_helper.h index b2c23af628e1..bcbb92f3fe9b 100644 --- a/include/drm/drm_gem_shmem_helper.h +++ b/include/drm/drm_gem_shmem_helper.h @@ -142,6 +142,10 @@ struct sg_table *drm_gem_shmem_get_pages_sgt(struct drm_gem_shmem_object *shmem) void drm_gem_shmem_print_info(const struct drm_gem_shmem_object *shmem, struct drm_printer *p, unsigned int indent); +int drm_gem_shmem_create_with_handle(struct drm_file *file_priv, + struct drm_device *dev, size_t size, + uint32_t *handle); + extern const struct vm_operations_struct drm_gem_shmem_vm_ops; /* diff --git a/include/drm/drm_managed.h b/include/drm/drm_managed.h index 72bfac002c06..72d0d68be226 100644 --- a/include/drm/drm_managed.h +++ b/include/drm/drm_managed.h @@ -18,7 +18,7 @@ typedef void (*drmres_release_t)(struct drm_device *dev, void *res); * @action: function which should be called when @dev is released * @data: opaque pointer, passed to @action * - * This function adds the @release action with optional parameter @data to the + * This function adds the release @action with optional parameter @data to the * list of cleanup actions for @dev. The cleanup actions will be run in reverse * order in the final drm_dev_put() call for @dev. */ diff --git a/include/drm/drm_mipi_dsi.h b/include/drm/drm_mipi_dsi.h index b429acde4f71..7ff43967251e 100644 --- a/include/drm/drm_mipi_dsi.h +++ b/include/drm/drm_mipi_dsi.h @@ -138,6 +138,8 @@ struct mipi_dsi_host *of_find_mipi_dsi_host_by_node(struct device_node *node); #define MIPI_DSI_MODE_LPM BIT(11) /* transmit data ending at the same time for all lanes within one hsync */ #define MIPI_DSI_HS_PKT_END_ALIGNED BIT(12) +/* pack all DSC slices for a line into a single packet */ +#define MIPI_DSI_MODE_DSC_ALL_SLICES_IN_PKT BIT(13) enum mipi_dsi_pixel_format { MIPI_DSI_FMT_RGB888, diff --git a/include/drm/drm_mode_config.h b/include/drm/drm_mode_config.h index e584652ddf67..d8f5b7e9673e 100644 --- a/include/drm/drm_mode_config.h +++ b/include/drm/drm_mode_config.h @@ -1007,6 +1007,7 @@ static inline int drm_mode_config_init(struct drm_device *dev) return drmm_mode_config_init(dev); } +int drm_mode_config_create_initial_state(struct drm_device *dev); void drm_mode_config_reset(struct drm_device *dev); void drm_mode_config_cleanup(struct drm_device *dev); diff --git a/include/drm/drm_plane.h b/include/drm/drm_plane.h index 419c88c873a6..2c5a5a70a71b 100644 --- a/include/drm/drm_plane.h +++ b/include/drm/drm_plane.h @@ -389,6 +389,22 @@ struct drm_plane_funcs { struct drm_property *property, uint64_t val); /** + * @atomic_create_state: + * + * Allocate a pristine, initialized, state for the plane object + * and return it. This callback must have no side effects: in + * particular, the returned state must not be assigned to the + * object's state pointer and it must not affect the hardware + * state. + * + * RETURNS: + * + * A new, pristine, plane state instance or an error pointer + * on failure. + */ + struct drm_plane_state *(*atomic_create_state)(struct drm_plane *plane); + + /** * @atomic_duplicate_state: * * Duplicate the current atomic state for this plane and return it. diff --git a/include/drm/drm_print.h b/include/drm/drm_print.h index ab017b05e175..2adc5ac688e1 100644 --- a/include/drm/drm_print.h +++ b/include/drm/drm_print.h @@ -87,7 +87,7 @@ extern unsigned long __drm_debug; * - drm.debug=0x2 will enable DRIVER messages * - drm.debug=0x3 will enable CORE and DRIVER messages * - ... - * - drm.debug=0x1ff will enable all messages + * - drm.debug=0x3ff will enable all messages * * An interesting feature is that it's possible to enable verbose logging at * run-time by echoing the debug value in its sysfs node:: diff --git a/include/linux/dma-fence-unwrap.h b/include/linux/dma-fence-unwrap.h index 62df222fe0f1..7bfacdf79de2 100644 --- a/include/linux/dma-fence-unwrap.h +++ b/include/linux/dma-fence-unwrap.h @@ -8,6 +8,8 @@ #ifndef __LINUX_DMA_FENCE_UNWRAP_H #define __LINUX_DMA_FENCE_UNWRAP_H +#include <linux/types.h> + struct dma_fence; /** @@ -48,11 +50,11 @@ struct dma_fence *dma_fence_unwrap_next(struct dma_fence_unwrap *cursor); for (fence = dma_fence_unwrap_first(head, cursor); fence; \ fence = dma_fence_unwrap_next(cursor)) -struct dma_fence *__dma_fence_unwrap_merge(unsigned int num_fences, +struct dma_fence *__dma_fence_unwrap_merge(size_t num_fences, struct dma_fence **fences, struct dma_fence_unwrap *cursors); -int dma_fence_dedup_array(struct dma_fence **array, int num_fences); +size_t dma_fence_dedup_array(struct dma_fence **array, size_t num_fences); /** * dma_fence_unwrap_merge - unwrap and merge fences diff --git a/include/linux/dma-fence.h b/include/linux/dma-fence.h index b52ab692b22e..158cd609f103 100644 --- a/include/linux/dma-fence.h +++ b/include/linux/dma-fence.h @@ -448,7 +448,7 @@ int dma_fence_add_callback(struct dma_fence *fence, dma_fence_func_t func); bool dma_fence_remove_callback(struct dma_fence *fence, struct dma_fence_cb *cb); -void dma_fence_enable_sw_signaling(struct dma_fence *fence); +void dma_fence_enable_signaling(struct dma_fence *fence); /** * DOC: Safe external access to driver provided object members @@ -534,7 +534,7 @@ dma_fence_is_signaled_locked(struct dma_fence *fence) * Returns true if the fence was already signaled, false if not. Since this * function doesn't enable signaling, it is not guaranteed to ever return * true if dma_fence_add_callback(), dma_fence_wait() or - * dma_fence_enable_sw_signaling() haven't been called before. + * dma_fence_enable_signaling() haven't been called before. * * It's recommended for seqno fences to call dma_fence_signal when the * operation is complete, it makes it possible to prevent issues from diff --git a/include/linux/font.h b/include/linux/font.h index 6845f02d739a..ea23b727388b 100644 --- a/include/linux/font.h +++ b/include/linux/font.h @@ -101,6 +101,9 @@ font_data_t *font_data_import(const struct console_font *font, unsigned int vpit void font_data_get(font_data_t *fd); bool font_data_put(font_data_t *fd); unsigned int font_data_size(font_data_t *fd); +const unsigned char *font_data_glyph_buf(font_data_t *fd, + unsigned int width, unsigned int vpitch, + unsigned int c); bool font_data_is_equal(font_data_t *lhs, font_data_t *rhs); int font_data_export(font_data_t *fd, struct console_font *font, unsigned int vpitch); diff --git a/include/linux/gpu_buddy.h b/include/linux/gpu_buddy.h index 71941a039648..e037714563d8 100644 --- a/include/linux/gpu_buddy.h +++ b/include/linux/gpu_buddy.h @@ -173,6 +173,21 @@ struct gpu_buddy { * that fits in the remaining space. */ struct gpu_buddy_block **roots; + /* + * Per-order free block scoreboard: free_scoreboard[order] holds the + * number of blocks of that order currently in the free state. + * Incremented in mark_free(), decremented wherever rbtree_remove() is + * called on a free block. + */ + u64 *free_scoreboard; + /* + * Per-order used block scoreboard: used_scoreboard[order] holds the + * number of blocks of that order currently in the allocated state. + * Incremented in mark_allocated(), decremented in mark_free() (guarded + * by gpu_buddy_block_is_allocated()) and in __gpu_buddy_free() when an + * allocated block is consumed directly during buddy coalescing. + */ + u64 *used_scoreboard; /* public: */ unsigned int n_roots; unsigned int max_order; diff --git a/include/linux/sysfb.h b/include/linux/sysfb.h index 5226efde9ad4..ed23d6516223 100644 --- a/include/linux/sysfb.h +++ b/include/linux/sysfb.h @@ -118,7 +118,7 @@ struct platform_device *sysfb_create_simplefb(const struct screen_info *si, const struct simplefb_platform_data *mode, struct device *parent); -#else /* CONFIG_SYSFB_SIMPLE */ +#else /* CONFIG_SYSFB_SIMPLEFB */ static inline bool sysfb_parse_mode(const struct screen_info *si, struct simplefb_platform_data *mode) @@ -133,6 +133,6 @@ static inline struct platform_device *sysfb_create_simplefb(const struct screen_ return ERR_PTR(-EINVAL); } -#endif /* CONFIG_SYSFB_SIMPLE */ +#endif /* CONFIG_SYSFB_SIMPLEFB */ #endif /* _LINUX_SYSFB_H */ diff --git a/include/uapi/drm/drm_mode.h b/include/uapi/drm/drm_mode.h index 381a3e857d4e..bd435effdcee 100644 --- a/include/uapi/drm/drm_mode.h +++ b/include/uapi/drm/drm_mode.h @@ -155,6 +155,7 @@ extern "C" { /* Panel type property */ #define DRM_MODE_PANEL_TYPE_UNKNOWN 0 #define DRM_MODE_PANEL_TYPE_OLED 1 +#define DRM_MODE_PANEL_TYPE_LCD 2 /* * DRM_MODE_ROTATE_<degrees> diff --git a/include/uapi/drm/ethosu_accel.h b/include/uapi/drm/ethosu_accel.h index af78bb4686d7..5b97d59a7806 100644 --- a/include/uapi/drm/ethosu_accel.h +++ b/include/uapi/drm/ethosu_accel.h @@ -43,6 +43,11 @@ enum drm_ethosu_ioctl_id { /** @DRM_ETHOSU_SUBMIT: Submit a job and BOs to run. */ DRM_ETHOSU_SUBMIT, + + DRM_ETHOSU_PERFMON_CREATE, + DRM_ETHOSU_PERFMON_DESTROY, + DRM_ETHOSU_PERFMON_GET_VALUES, + DRM_ETHOSU_PERFMON_SET_GLOBAL, }; /** @@ -79,6 +84,7 @@ struct drm_ethosu_npu_info { __u32 config; __u32 sram_size; + __u32 pmu_counters; }; /** @@ -220,10 +226,54 @@ struct drm_ethosu_submit { /** Input: Number of jobs passed in. */ __u32 job_count; - /** Reserved, must be zero. */ + /** Input: Id returned by DRM_ETHOSU_PERFMON_CREATE */ + __u32 perfmon_id; +}; + +#define DRM_ETHOSU_MAX_PERF_EVENT_COUNTERS 8 +#define DRM_ETHOSU_MAX_PERF_COUNTERS \ + (DRM_ETHOSU_MAX_PERF_EVENT_COUNTERS + 1) + +struct drm_ethosu_perfmon_create { + __u32 id; + __u32 ncounters; + __u16 counters[DRM_ETHOSU_MAX_PERF_EVENT_COUNTERS]; +}; + +struct drm_ethosu_perfmon_destroy { + __u32 id; __u32 pad; }; +/* + * Returns the values of the performance counters tracked by this + * perfmon (as an array of (ncounters + 1) u64 values). + * + * No implicit synchronization is performed, so the user has to + * guarantee that any jobs using this perfmon have already been + * completed. + */ +struct drm_ethosu_perfmon_get_values { + __u32 id; + __u32 pad; + __u64 values_ptr; +}; + +#define DRM_ETHOSU_PERFMON_CLEAR_GLOBAL 0x0001 + +/** + * struct drm_ethosu_perfmon_set_global - ioctl to define a global performance + * monitor + * + * The global performance monitor will be used for all jobs. If a global + * performance monitor is defined, jobs with a self-defined performance + * monitor won't be allowed. + */ +struct drm_ethosu_perfmon_set_global { + __u32 flags; + __u32 id; +}; + /** * DRM_IOCTL_ETHOSU() - Build a ethosu IOCTL number * @__access: Access type. Must be R, W or RW. @@ -252,6 +302,14 @@ enum { DRM_IOCTL_ETHOSU(WR, CMDSTREAM_BO_CREATE, cmdstream_bo_create), DRM_IOCTL_ETHOSU_SUBMIT = DRM_IOCTL_ETHOSU(WR, SUBMIT, submit), + DRM_IOCTL_ETHOSU_PERFMON_CREATE = + DRM_IOCTL_ETHOSU(WR, PERFMON_CREATE, perfmon_create), + DRM_IOCTL_ETHOSU_PERFMON_DESTROY = + DRM_IOCTL_ETHOSU(WR, PERFMON_DESTROY, perfmon_destroy), + DRM_IOCTL_ETHOSU_PERFMON_GET_VALUES = + DRM_IOCTL_ETHOSU(WR, PERFMON_GET_VALUES, perfmon_get_values), + DRM_IOCTL_ETHOSU_PERFMON_SET_GLOBAL = + DRM_IOCTL_ETHOSU(WR, PERFMON_SET_GLOBAL, perfmon_set_global), }; #if defined(__cplusplus) diff --git a/include/uapi/drm/panthor_drm.h b/include/uapi/drm/panthor_drm.h index 0e455d91e77d..a2ff0f4ec691 100644 --- a/include/uapi/drm/panthor_drm.h +++ b/include/uapi/drm/panthor_drm.h @@ -253,6 +253,9 @@ enum drm_panthor_dev_query_type { * @DRM_PANTHOR_DEV_QUERY_GROUP_PRIORITIES_INFO: Query allowed group priorities information. */ DRM_PANTHOR_DEV_QUERY_GROUP_PRIORITIES_INFO, + + /** @DRM_PANTHOR_DEV_QUERY_MMU_INFO: Query MMU information. */ + DRM_PANTHOR_DEV_QUERY_MMU_INFO, }; /** @@ -488,6 +491,16 @@ struct drm_panthor_timestamp_info { }; /** + * struct drm_panthor_mmu_info - MMU information + * + * Structure grouping all queryable information relating to the MMU. + */ +struct drm_panthor_mmu_info { + /** @page_size_bitmap: Allowed page sizes */ + __u64 page_size_bitmap; +}; + +/** * struct drm_panthor_group_priorities_info - Group priorities information * * Structure grouping all queryable information relating to the allowed group priorities. @@ -602,6 +615,18 @@ enum drm_panthor_vm_bind_op_flags { DRM_PANTHOR_VM_BIND_OP_MAP_UNCACHED = 1 << 2, /** + * @DRM_PANTHOR_VM_BIND_OP_MAP_SPARSE: Sparsely map a virtual memory range + * + * Only valid with DRM_PANTHOR_VM_BIND_OP_TYPE_MAP. + * + * When this flag is set, the whole vm_bind range is mapped over a dummy object in a cyclic + * fashion, and all GPU reads from addresses in the range return undefined values. This flag + * being set means drm_panthor_vm_bind_op::bo_offset and drm_panthor_vm_bind_op::bo_handle + * must both be set to 0. DRM_PANTHOR_VM_BIND_OP_MAP_NOEXEC must also be set. + */ + DRM_PANTHOR_VM_BIND_OP_MAP_SPARSE = 1 << 3, + + /** * @DRM_PANTHOR_VM_BIND_OP_TYPE_MASK: Mask used to determine the type of operation. */ DRM_PANTHOR_VM_BIND_OP_TYPE_MASK = (int)(0xfu << 28), @@ -664,7 +689,6 @@ struct drm_panthor_vm_bind_op { * This array shall not be empty for sync-only operations. */ struct drm_panthor_obj_array syncs; - }; /** diff --git a/kernel/cgroup/dmem.c b/kernel/cgroup/dmem.c index 4753a67d0f0f..6430c7ce1e03 100644 --- a/kernel/cgroup/dmem.c +++ b/kernel/cgroup/dmem.c @@ -182,6 +182,11 @@ static u64 get_resource_current(struct dmem_cgroup_pool_state *pool) return pool ? page_counter_read(&pool->cnt) : 0; } +static u64 get_resource_peak(struct dmem_cgroup_pool_state *pool) +{ + return pool ? READ_ONCE(pool->cnt.watermark) : 0; +} + static void reset_all_resource_limits(struct dmem_cgroup_pool_state *rpool) { set_resource_min(rpool, 0); @@ -808,6 +813,11 @@ static int dmemcg_limit_show(struct seq_file *sf, void *v, return 0; } +static int dmem_cgroup_region_peak_show(struct seq_file *sf, void *v) +{ + return dmemcg_limit_show(sf, v, get_resource_peak); +} + static int dmem_cgroup_region_current_show(struct seq_file *sf, void *v) { return dmemcg_limit_show(sf, v, get_resource_current); @@ -857,6 +867,11 @@ static struct cftype files[] = { .seq_show = dmem_cgroup_region_current_show, }, { + .name = "peak", + .seq_show = dmem_cgroup_region_peak_show, + .flags = CFTYPE_NOT_ON_ROOT, + }, + { .name = "min", .write = dmem_cgroup_region_min_write, .seq_show = dmem_cgroup_region_min_show, diff --git a/lib/fonts/fonts.c b/lib/fonts/fonts.c index f5d5333450a0..4fc66722d00d 100644 --- a/lib/fonts/fonts.c +++ b/lib/fonts/fonts.c @@ -178,6 +178,37 @@ unsigned int font_data_size(font_data_t *fd) } EXPORT_SYMBOL_GPL(font_data_size); +static unsigned int font_data_num_glyphs(font_data_t *fd, unsigned int width, unsigned int height) +{ + return font_data_size(fd) / font_glyph_size(width, height); +} + +/** + * font_data_glyph_buf() - Returns the glyph for a specific character as raw bytes + * @fd: The font data + * @width: The glyph width in bits per scanline + * @vpitch: The number of scanlines per glyph + * @c: The character + * + * Glyphs start at fixed intervals within the font data. font_data_glyph_buf() + * returns the glyph shape of the specified character. If no such glyph + * exists in the font, it returns NULL. + * + * Returns: + * The character's raw glyph shape, or NULL if no glyph exists for the character. The + * provided buffer is read-only. + */ +const unsigned char *font_data_glyph_buf(font_data_t *fd, + unsigned int width, unsigned int vpitch, + unsigned int c) +{ + if (c >= font_data_num_glyphs(fd, width, vpitch)) + return NULL; + + return font_data_buf(fd) + font_glyph_size(width, vpitch) * c; +} +EXPORT_SYMBOL_GPL(font_data_glyph_buf); + /** * font_data_is_equal - Compares font data for equality * @lhs: Left-hand side font data |
