From bad20a2dbfdfaf01560026909506b6ed69d65ba2 Mon Sep 17 00:00:00 2001 From: Paul Cercueil Date: Thu, 16 Jul 2020 14:56:46 +0200 Subject: [PATCH 01/59] drm/panel-simple: Fix inverted V/H SYNC for Frida FRD350H54004 panel The FRD350H54004 panel was marked as having active-high VSYNC and HSYNC signals, which sorts-of worked, but resulted in the picture fading out under certain circumstances. Fix this issue by marking VSYNC and HSYNC signals active-low. v2: Rebase on drm-misc-next Fixes: 7b6bd8433609 ("drm/panel: simple: Add support for the Frida FRD350H54004 panel") Cc: stable@vger.kernel.org # v5.5 Signed-off-by: Paul Cercueil Signed-off-by: Sam Ravnborg Link: https://patchwork.freedesktop.org/patch/msgid/20200716125647.10964-1-paul@crapouillou.net --- drivers/gpu/drm/panel/panel-simple.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/panel/panel-simple.c b/drivers/gpu/drm/panel/panel-simple.c index f42249b72548..8b0bab9dd075 100644 --- a/drivers/gpu/drm/panel/panel-simple.c +++ b/drivers/gpu/drm/panel/panel-simple.c @@ -1763,7 +1763,7 @@ static const struct drm_display_mode frida_frd350h54004_mode = { .vsync_start = 240 + 2, .vsync_end = 240 + 2 + 6, .vtotal = 240 + 2 + 6 + 2, - .flags = DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC, + .flags = DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC, }; static const struct panel_desc frida_frd350h54004 = { From 795db2afd52d1bd9305e837007166039908d8553 Mon Sep 17 00:00:00 2001 From: Paul Cercueil Date: Thu, 16 Jul 2020 14:56:47 +0200 Subject: [PATCH 02/59] drm/panel-simple: Add 50 Hz mode to the Frida FRD350H54004 panel By changing the pixel clock and the length of the back porch, it is possible to obtain a perfect 50 Hz refresh rate. v2: Rebase on drm-misc-next Signed-off-by: Paul Cercueil Signed-off-by: Sam Ravnborg Link: https://patchwork.freedesktop.org/patch/msgid/20200716125647.10964-2-paul@crapouillou.net --- drivers/gpu/drm/panel/panel-simple.c | 40 +++++++++++++++++++--------- 1 file changed, 27 insertions(+), 13 deletions(-) diff --git a/drivers/gpu/drm/panel/panel-simple.c b/drivers/gpu/drm/panel/panel-simple.c index 8b0bab9dd075..88493538a147 100644 --- a/drivers/gpu/drm/panel/panel-simple.c +++ b/drivers/gpu/drm/panel/panel-simple.c @@ -1753,22 +1753,36 @@ static const struct panel_desc foxlink_fl500wvr00_a0t = { .bus_format = MEDIA_BUS_FMT_RGB888_1X24, }; -static const struct drm_display_mode frida_frd350h54004_mode = { - .clock = 6000, - .hdisplay = 320, - .hsync_start = 320 + 44, - .hsync_end = 320 + 44 + 16, - .htotal = 320 + 44 + 16 + 20, - .vdisplay = 240, - .vsync_start = 240 + 2, - .vsync_end = 240 + 2 + 6, - .vtotal = 240 + 2 + 6 + 2, - .flags = DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC, +static const struct drm_display_mode frida_frd350h54004_modes[] = { + { /* 60 Hz */ + .clock = 6000, + .hdisplay = 320, + .hsync_start = 320 + 44, + .hsync_end = 320 + 44 + 16, + .htotal = 320 + 44 + 16 + 20, + .vdisplay = 240, + .vsync_start = 240 + 2, + .vsync_end = 240 + 2 + 6, + .vtotal = 240 + 2 + 6 + 2, + .flags = DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC, + }, + { /* 50 Hz */ + .clock = 5400, + .hdisplay = 320, + .hsync_start = 320 + 56, + .hsync_end = 320 + 56 + 16, + .htotal = 320 + 56 + 16 + 40, + .vdisplay = 240, + .vsync_start = 240 + 2, + .vsync_end = 240 + 2 + 6, + .vtotal = 240 + 2 + 6 + 2, + .flags = DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC, + }, }; static const struct panel_desc frida_frd350h54004 = { - .modes = &frida_frd350h54004_mode, - .num_modes = 1, + .modes = frida_frd350h54004_modes, + .num_modes = ARRAY_SIZE(frida_frd350h54004_modes), .bpc = 8, .size = { .width = 77, From 3ab0ea4a1aa752ef2f299db10c1902bf320afca6 Mon Sep 17 00:00:00 2001 From: Sebastian Reichel Date: Thu, 16 Jul 2020 14:57:30 +0200 Subject: [PATCH 03/59] dt-bindings: display: panel-dsi-cm: convert to YAML Convert panel-dsi-cm bindings to YAML and add missing properties while at it. Reviewed-by: Laurent Pinchart Reviewed-by: Rob Herring Signed-off-by: Sebastian Reichel Signed-off-by: Sam Ravnborg Link: https://patchwork.freedesktop.org/patch/msgid/20200716125733.83654-2-sebastian.reichel@collabora.com --- .../bindings/display/panel/panel-dsi-cm.txt | 29 ------- .../bindings/display/panel/panel-dsi-cm.yaml | 86 +++++++++++++++++++ 2 files changed, 86 insertions(+), 29 deletions(-) delete mode 100644 Documentation/devicetree/bindings/display/panel/panel-dsi-cm.txt create mode 100644 Documentation/devicetree/bindings/display/panel/panel-dsi-cm.yaml diff --git a/Documentation/devicetree/bindings/display/panel/panel-dsi-cm.txt b/Documentation/devicetree/bindings/display/panel/panel-dsi-cm.txt deleted file mode 100644 index dce48eb9db57..000000000000 --- a/Documentation/devicetree/bindings/display/panel/panel-dsi-cm.txt +++ /dev/null @@ -1,29 +0,0 @@ -Generic MIPI DSI Command Mode Panel -=================================== - -Required properties: -- compatible: "panel-dsi-cm" - -Optional properties: -- label: a symbolic name for the panel -- reset-gpios: panel reset gpio -- te-gpios: panel TE gpio - -Required nodes: -- Video port for DSI input - -Example -------- - -lcd0: display { - compatible = "tpo,taal", "panel-dsi-cm"; - label = "lcd0"; - - reset-gpios = <&gpio4 6 GPIO_ACTIVE_HIGH>; - - port { - lcd0_in: endpoint { - remote-endpoint = <&dsi1_out_ep>; - }; - }; -}; diff --git a/Documentation/devicetree/bindings/display/panel/panel-dsi-cm.yaml b/Documentation/devicetree/bindings/display/panel/panel-dsi-cm.yaml new file mode 100644 index 000000000000..d766c949c622 --- /dev/null +++ b/Documentation/devicetree/bindings/display/panel/panel-dsi-cm.yaml @@ -0,0 +1,86 @@ +# SPDX-License-Identifier: (GPL-2.0-only or BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/display/panel/panel-dsi-cm.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: DSI command mode panels + +maintainers: + - Tomi Valkeinen + - Sebastian Reichel + +description: | + This binding file is a collection of the DSI panels that + are usually driven in command mode. If no backlight is + referenced via the optional backlight property, the DSI + panel is assumed to have native backlight support. + The panel may use an OF graph binding for the association + to the display, or it may be a direct child node of the + display. + +allOf: + - $ref: panel-common.yaml# + +properties: + + compatible: + items: + - enum: + - motorola,droid4-panel # Panel from Motorola Droid4 phone + - nokia,himalaya # Panel from Nokia N950 phone + - tpo,taal # Panel from OMAP4 SDP board + - const: panel-dsi-cm # Generic DSI command mode panel compatible fallback + + reg: + maxItems: 1 + description: DSI virtual channel + + vddi-supply: + description: + Display panels require power to be supplied. While several panels need + more than one power supply with panel-specific constraints governing the + order and timings of the power supplies, in many cases a single power + supply is sufficient, either because the panel has a single power rail, or + because all its power rails can be driven by the same supply. In that case + the vddi-supply property specifies the supply powering the panel as a + phandle to a regulator. + + vpnl-supply: + description: + When the display panel needs a second power supply, this property can be + used in addition to vddi-supply. Both supplies will be enabled at the + same time before the panel is being accessed. + + width-mm: true + height-mm: true + label: true + rotation: true + panel-timing: true + port: true + reset-gpios: true + te-gpios: true + backlight: true + +additionalProperties: false + +required: + - compatible + - reg + +examples: + - | + #include + + dsi-controller { + #address-cells = <1>; + #size-cells = <0>; + + panel@0 { + compatible = "tpo,taal", "panel-dsi-cm"; + reg = <0>; + reset-gpios = <&gpio4 6 GPIO_ACTIVE_HIGH>; + }; + }; + +... From d713b771b748a8b817e6ce58e2be523554b8d898 Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Mon, 13 Jul 2020 14:49:23 +0200 Subject: [PATCH 04/59] drm/vboxvideo: Replace HTTP links with HTTPS ones Rationale: Reduces attack surface on kernel devs opening the links for MITM as HTTPS traffic is much harder to manipulate. Deterministic algorithm: For each file: If not .svg: For each line: If doesn't contain `\bxmlns\b`: For each link, `\bhttp://[^# \t\r\n]*(?:\w|/)`: If neither `\bgnu\.org/license`, nor `\bmozilla\.org/MPL\b`: If both the HTTP and HTTPS versions return 200 OK and serve the same content: Replace HTTP with HTTPS. Signed-off-by: Alexander A. Klimov Signed-off-by: Sam Ravnborg Link: https://patchwork.freedesktop.org/patch/msgid/20200713124923.34282-1-grandmaster@al2klimov.de --- drivers/gpu/drm/vboxvideo/hgsmi_defs.h | 2 +- drivers/gpu/drm/vboxvideo/vbox_hgsmi.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/vboxvideo/hgsmi_defs.h b/drivers/gpu/drm/vboxvideo/hgsmi_defs.h index 6c8df1cdb087..3cb52f2b2274 100644 --- a/drivers/gpu/drm/vboxvideo/hgsmi_defs.h +++ b/drivers/gpu/drm/vboxvideo/hgsmi_defs.h @@ -58,7 +58,7 @@ struct hgsmi_buffer_tail { /* Reserved, must be initialized to 0. */ u32 reserved; /* - * One-at-a-Time Hash: http://www.burtleburtle.net/bob/hash/doobs.html + * One-at-a-Time Hash: https://www.burtleburtle.net/bob/hash/doobs.html * Over the header, offset and for first 4 bytes of the tail. */ u32 checksum; diff --git a/drivers/gpu/drm/vboxvideo/vbox_hgsmi.c b/drivers/gpu/drm/vboxvideo/vbox_hgsmi.c index 94b60654a012..a9ca4d0c3eca 100644 --- a/drivers/gpu/drm/vboxvideo/vbox_hgsmi.c +++ b/drivers/gpu/drm/vboxvideo/vbox_hgsmi.c @@ -8,7 +8,7 @@ #include "vboxvideo_vbe.h" #include "hgsmi_defs.h" -/* One-at-a-Time Hash from http://www.burtleburtle.net/bob/hash/doobs.html */ +/* One-at-a-Time Hash from https://www.burtleburtle.net/bob/hash/doobs.html */ static u32 hgsmi_hash_process(u32 hash, const u8 *data, int size) { while (size--) { From 9410113fc3f32f3cf7211fa66111e156681cf1f6 Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Mon, 13 Jul 2020 14:39:13 +0200 Subject: [PATCH 05/59] drm/tidss: Replace HTTP links with HTTPS ones Rationale: Reduces attack surface on kernel devs opening the links for MITM as HTTPS traffic is much harder to manipulate. Deterministic algorithm: For each file: If not .svg: For each line: If doesn't contain `\bxmlns\b`: For each link, `\bhttp://[^# \t\r\n]*(?:\w|/)`: If neither `\bgnu\.org/license`, nor `\bmozilla\.org/MPL\b`: If both the HTTP and HTTPS versions return 200 OK and serve the same content: Replace HTTP with HTTPS. Signed-off-by: Alexander A. Klimov Acked-by: Jyri Sarha Signed-off-by: Sam Ravnborg Link: https://patchwork.freedesktop.org/patch/msgid/20200713123913.34205-1-grandmaster@al2klimov.de --- drivers/gpu/drm/tidss/tidss_crtc.c | 2 +- drivers/gpu/drm/tidss/tidss_crtc.h | 2 +- drivers/gpu/drm/tidss/tidss_dispc.c | 2 +- drivers/gpu/drm/tidss/tidss_dispc.h | 2 +- drivers/gpu/drm/tidss/tidss_dispc_regs.h | 2 +- drivers/gpu/drm/tidss/tidss_drv.c | 2 +- drivers/gpu/drm/tidss/tidss_drv.h | 2 +- drivers/gpu/drm/tidss/tidss_encoder.c | 2 +- drivers/gpu/drm/tidss/tidss_encoder.h | 2 +- drivers/gpu/drm/tidss/tidss_irq.c | 2 +- drivers/gpu/drm/tidss/tidss_irq.h | 2 +- drivers/gpu/drm/tidss/tidss_kms.c | 2 +- drivers/gpu/drm/tidss/tidss_kms.h | 2 +- drivers/gpu/drm/tidss/tidss_plane.c | 2 +- drivers/gpu/drm/tidss/tidss_plane.h | 2 +- drivers/gpu/drm/tidss/tidss_scale_coefs.c | 2 +- drivers/gpu/drm/tidss/tidss_scale_coefs.h | 2 +- 17 files changed, 17 insertions(+), 17 deletions(-) diff --git a/drivers/gpu/drm/tidss/tidss_crtc.c b/drivers/gpu/drm/tidss/tidss_crtc.c index 4d01c4af61cd..3c5744a91d4a 100644 --- a/drivers/gpu/drm/tidss/tidss_crtc.c +++ b/drivers/gpu/drm/tidss/tidss_crtc.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 /* - * Copyright (C) 2018 Texas Instruments Incorporated - http://www.ti.com/ + * Copyright (C) 2018 Texas Instruments Incorporated - https://www.ti.com/ * Author: Tomi Valkeinen */ diff --git a/drivers/gpu/drm/tidss/tidss_crtc.h b/drivers/gpu/drm/tidss/tidss_crtc.h index 09e773666228..040d1205496b 100644 --- a/drivers/gpu/drm/tidss/tidss_crtc.h +++ b/drivers/gpu/drm/tidss/tidss_crtc.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0 */ /* - * Copyright (C) 2018 Texas Instruments Incorporated - http://www.ti.com/ + * Copyright (C) 2018 Texas Instruments Incorporated - https://www.ti.com/ * Author: Tomi Valkeinen */ diff --git a/drivers/gpu/drm/tidss/tidss_dispc.c b/drivers/gpu/drm/tidss/tidss_dispc.c index 772a497e8c57..c3ece2c9d1c8 100644 --- a/drivers/gpu/drm/tidss/tidss_dispc.c +++ b/drivers/gpu/drm/tidss/tidss_dispc.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 /* - * Copyright (C) 2016-2018 Texas Instruments Incorporated - http://www.ti.com/ + * Copyright (C) 2016-2018 Texas Instruments Incorporated - https://www.ti.com/ * Author: Jyri Sarha */ diff --git a/drivers/gpu/drm/tidss/tidss_dispc.h b/drivers/gpu/drm/tidss/tidss_dispc.h index 902e612ff7ac..5984e0de2cd9 100644 --- a/drivers/gpu/drm/tidss/tidss_dispc.h +++ b/drivers/gpu/drm/tidss/tidss_dispc.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0 */ /* - * Copyright (C) 2018 Texas Instruments Incorporated - http://www.ti.com/ + * Copyright (C) 2018 Texas Instruments Incorporated - https://www.ti.com/ * Author: Tomi Valkeinen */ diff --git a/drivers/gpu/drm/tidss/tidss_dispc_regs.h b/drivers/gpu/drm/tidss/tidss_dispc_regs.h index 88a83a41b6e3..13feedfe5d6d 100644 --- a/drivers/gpu/drm/tidss/tidss_dispc_regs.h +++ b/drivers/gpu/drm/tidss/tidss_dispc_regs.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0 */ /* - * Copyright (C) 2016-2018 Texas Instruments Incorporated - http://www.ti.com/ + * Copyright (C) 2016-2018 Texas Instruments Incorporated - https://www.ti.com/ * Author: Jyri Sarha */ diff --git a/drivers/gpu/drm/tidss/tidss_drv.c b/drivers/gpu/drm/tidss/tidss_drv.c index fee2f6fa3506..9179ea18f625 100644 --- a/drivers/gpu/drm/tidss/tidss_drv.c +++ b/drivers/gpu/drm/tidss/tidss_drv.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 /* - * Copyright (C) 2018 Texas Instruments Incorporated - http://www.ti.com/ + * Copyright (C) 2018 Texas Instruments Incorporated - https://www.ti.com/ * Author: Tomi Valkeinen */ diff --git a/drivers/gpu/drm/tidss/tidss_drv.h b/drivers/gpu/drm/tidss/tidss_drv.h index 3b0a3d87b7c4..7de4bba52e6f 100644 --- a/drivers/gpu/drm/tidss/tidss_drv.h +++ b/drivers/gpu/drm/tidss/tidss_drv.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0 */ /* - * Copyright (C) 2018 Texas Instruments Incorporated - http://www.ti.com/ + * Copyright (C) 2018 Texas Instruments Incorporated - https://www.ti.com/ * Author: Tomi Valkeinen */ diff --git a/drivers/gpu/drm/tidss/tidss_encoder.c b/drivers/gpu/drm/tidss/tidss_encoder.c index 30bf2a65949c..e278a9c89476 100644 --- a/drivers/gpu/drm/tidss/tidss_encoder.c +++ b/drivers/gpu/drm/tidss/tidss_encoder.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 /* - * Copyright (C) 2018 Texas Instruments Incorporated - http://www.ti.com/ + * Copyright (C) 2018 Texas Instruments Incorporated - https://www.ti.com/ * Author: Tomi Valkeinen */ diff --git a/drivers/gpu/drm/tidss/tidss_encoder.h b/drivers/gpu/drm/tidss/tidss_encoder.h index 06854d66e7e6..ace877c0e0fd 100644 --- a/drivers/gpu/drm/tidss/tidss_encoder.h +++ b/drivers/gpu/drm/tidss/tidss_encoder.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0 */ /* - * Copyright (C) 2018 Texas Instruments Incorporated - http://www.ti.com/ + * Copyright (C) 2018 Texas Instruments Incorporated - https://www.ti.com/ * Author: Tomi Valkeinen */ diff --git a/drivers/gpu/drm/tidss/tidss_irq.c b/drivers/gpu/drm/tidss/tidss_irq.c index 1b80f2d62e0a..a5ec7931ef6b 100644 --- a/drivers/gpu/drm/tidss/tidss_irq.c +++ b/drivers/gpu/drm/tidss/tidss_irq.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 /* - * Copyright (C) 2018 Texas Instruments Incorporated - http://www.ti.com/ + * Copyright (C) 2018 Texas Instruments Incorporated - https://www.ti.com/ * Author: Tomi Valkeinen */ diff --git a/drivers/gpu/drm/tidss/tidss_irq.h b/drivers/gpu/drm/tidss/tidss_irq.h index aa92db403cca..4aaad5dfd7c2 100644 --- a/drivers/gpu/drm/tidss/tidss_irq.h +++ b/drivers/gpu/drm/tidss/tidss_irq.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0 */ /* - * Copyright (C) 2018 Texas Instruments Incorporated - http://www.ti.com/ + * Copyright (C) 2018 Texas Instruments Incorporated - https://www.ti.com/ * Author: Tomi Valkeinen */ diff --git a/drivers/gpu/drm/tidss/tidss_kms.c b/drivers/gpu/drm/tidss/tidss_kms.c index b6e61d6cf60f..808c8af58fd5 100644 --- a/drivers/gpu/drm/tidss/tidss_kms.c +++ b/drivers/gpu/drm/tidss/tidss_kms.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 /* - * Copyright (C) 2018 Texas Instruments Incorporated - http://www.ti.com/ + * Copyright (C) 2018 Texas Instruments Incorporated - https://www.ti.com/ * Author: Tomi Valkeinen */ diff --git a/drivers/gpu/drm/tidss/tidss_kms.h b/drivers/gpu/drm/tidss/tidss_kms.h index 99aaff099f22..632d79f5983f 100644 --- a/drivers/gpu/drm/tidss/tidss_kms.h +++ b/drivers/gpu/drm/tidss/tidss_kms.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0 */ /* - * Copyright (C) 2018 Texas Instruments Incorporated - http://www.ti.com/ + * Copyright (C) 2018 Texas Instruments Incorporated - https://www.ti.com/ * Author: Tomi Valkeinen */ diff --git a/drivers/gpu/drm/tidss/tidss_plane.c b/drivers/gpu/drm/tidss/tidss_plane.c index 0a563eabcbb9..43e72d0b2d84 100644 --- a/drivers/gpu/drm/tidss/tidss_plane.c +++ b/drivers/gpu/drm/tidss/tidss_plane.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 /* - * Copyright (C) 2018 Texas Instruments Incorporated - http://www.ti.com/ + * Copyright (C) 2018 Texas Instruments Incorporated - https://www.ti.com/ * Author: Tomi Valkeinen */ diff --git a/drivers/gpu/drm/tidss/tidss_plane.h b/drivers/gpu/drm/tidss/tidss_plane.h index 80ff1c5a2535..e933e158b617 100644 --- a/drivers/gpu/drm/tidss/tidss_plane.h +++ b/drivers/gpu/drm/tidss/tidss_plane.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0 */ /* - * Copyright (C) 2018 Texas Instruments Incorporated - http://www.ti.com/ + * Copyright (C) 2018 Texas Instruments Incorporated - https://www.ti.com/ * Author: Tomi Valkeinen */ diff --git a/drivers/gpu/drm/tidss/tidss_scale_coefs.c b/drivers/gpu/drm/tidss/tidss_scale_coefs.c index 5ec68389cc68..c2b84fea89a5 100644 --- a/drivers/gpu/drm/tidss/tidss_scale_coefs.c +++ b/drivers/gpu/drm/tidss/tidss_scale_coefs.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 /* - * Copyright (C) 2018 Texas Instruments Incorporated - http://www.ti.com/ + * Copyright (C) 2018 Texas Instruments Incorporated - https://www.ti.com/ * Author: Jyri Sarha */ diff --git a/drivers/gpu/drm/tidss/tidss_scale_coefs.h b/drivers/gpu/drm/tidss/tidss_scale_coefs.h index 64b5af5b5361..9c560d0fdac0 100644 --- a/drivers/gpu/drm/tidss/tidss_scale_coefs.h +++ b/drivers/gpu/drm/tidss/tidss_scale_coefs.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0 */ /* - * Copyright (C) 2018 Texas Instruments Incorporated - http://www.ti.com/ + * Copyright (C) 2018 Texas Instruments Incorporated - https://www.ti.com/ * Author: Jyri Sarha */ From a7e5e06de22223e0a00f569ea8446243d5f58386 Mon Sep 17 00:00:00 2001 From: Lyude Paul Date: Tue, 14 Apr 2020 12:34:52 -0400 Subject: [PATCH 06/59] drm/vblank: Register drmm cleanup action once per drm_vblank_crtc MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since we'll be allocating resources for kthread_create_worker() in the next commit (which could fail and require us to clean up the mess), let's simplify the cleanup process a bit by registering a drm_vblank_init_release() action for each drm_vblank_crtc so they're still cleaned up if we fail to initialize one of them. Changes since v3: * Use drmm_add_action_or_reset() - Daniel Vetter Cc: Ville Syrjälä Cc: dri-devel@lists.freedesktop.org Cc: nouveau@lists.freedesktop.org Reviewed-by: Daniel Vetter Signed-off-by: Lyude Paul Acked-by: Dave Airlie Link: https://patchwork.freedesktop.org/patch/msgid/20200627194657.156514-2-lyude@redhat.com --- drivers/gpu/drm/drm_vblank.c | 23 ++++++++++------------- 1 file changed, 10 insertions(+), 13 deletions(-) diff --git a/drivers/gpu/drm/drm_vblank.c b/drivers/gpu/drm/drm_vblank.c index 42a84eb4cc8c..4b0a9b206116 100644 --- a/drivers/gpu/drm/drm_vblank.c +++ b/drivers/gpu/drm/drm_vblank.c @@ -492,16 +492,12 @@ static void vblank_disable_fn(struct timer_list *t) static void drm_vblank_init_release(struct drm_device *dev, void *ptr) { - unsigned int pipe; + struct drm_vblank_crtc *vblank = ptr; - for (pipe = 0; pipe < dev->num_crtcs; pipe++) { - struct drm_vblank_crtc *vblank = &dev->vblank[pipe]; + drm_WARN_ON(dev, READ_ONCE(vblank->enabled) && + drm_core_check_feature(dev, DRIVER_MODESET)); - drm_WARN_ON(dev, READ_ONCE(vblank->enabled) && - drm_core_check_feature(dev, DRIVER_MODESET)); - - del_timer_sync(&vblank->disable_timer); - } + del_timer_sync(&vblank->disable_timer); } /** @@ -511,7 +507,7 @@ static void drm_vblank_init_release(struct drm_device *dev, void *ptr) * * This function initializes vblank support for @num_crtcs display pipelines. * Cleanup is handled automatically through a cleanup function added with - * drmm_add_action(). + * drmm_add_action_or_reset(). * * Returns: * Zero on success or a negative error code on failure. @@ -530,10 +526,6 @@ int drm_vblank_init(struct drm_device *dev, unsigned int num_crtcs) dev->num_crtcs = num_crtcs; - ret = drmm_add_action(dev, drm_vblank_init_release, NULL); - if (ret) - return ret; - for (i = 0; i < num_crtcs; i++) { struct drm_vblank_crtc *vblank = &dev->vblank[i]; @@ -542,6 +534,11 @@ int drm_vblank_init(struct drm_device *dev, unsigned int num_crtcs) init_waitqueue_head(&vblank->queue); timer_setup(&vblank->disable_timer, vblank_disable_fn, 0); seqlock_init(&vblank->seqlock); + + ret = drmm_add_action_or_reset(dev, drm_vblank_init_release, + vblank); + if (ret) + return ret; } return 0; From a7e3ad5fdc51b4795cb935282819996718c0d187 Mon Sep 17 00:00:00 2001 From: Lyude Paul Date: Tue, 23 Jun 2020 16:24:00 -0400 Subject: [PATCH 07/59] drm/vblank: Use spin_(un)lock_irq() in drm_crtc_vblank_off() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This got me confused for a bit while looking over this code: I had been planning on adding some blocking function calls into this function, but seeing the irqsave/irqrestore variants of spin_(un)lock() didn't make it very clear whether or not that would actually be safe. So I went ahead and reviewed every single driver in the kernel that uses this function, and they all fall into three categories: * Driver probe code * ->atomic_disable() callbacks * Legacy modesetting callbacks All of these will be guaranteed to have IRQs enabled, which means it's perfectly safe to block here. Just to make things a little less confusing to others in the future, let's switch over to spin_lock_irq()/spin_unlock_irq() to make that fact a little more obvious. Signed-off-by: Lyude Paul Reviewed-by: Daniel Vetter Cc: Ville Syrjälä Acked-by: Dave Airlie Link: https://patchwork.freedesktop.org/patch/msgid/20200627194657.156514-3-lyude@redhat.com --- drivers/gpu/drm/drm_vblank.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/drm_vblank.c b/drivers/gpu/drm/drm_vblank.c index 4b0a9b206116..e277e40e5b82 100644 --- a/drivers/gpu/drm/drm_vblank.c +++ b/drivers/gpu/drm/drm_vblank.c @@ -1283,13 +1283,12 @@ void drm_crtc_vblank_off(struct drm_crtc *crtc) struct drm_pending_vblank_event *e, *t; ktime_t now; - unsigned long irqflags; u64 seq; if (drm_WARN_ON(dev, pipe >= dev->num_crtcs)) return; - spin_lock_irqsave(&dev->event_lock, irqflags); + spin_lock_irq(&dev->event_lock); spin_lock(&dev->vbl_lock); drm_dbg_vbl(dev, "crtc %d, vblank enabled %d, inmodeset %d\n", @@ -1325,7 +1324,7 @@ void drm_crtc_vblank_off(struct drm_crtc *crtc) drm_vblank_put(dev, pipe); send_vblank_event(dev, e, seq, now); } - spin_unlock_irqrestore(&dev->event_lock, irqflags); + spin_unlock_irq(&dev->event_lock); /* Will be reset by the modeset helpers when re-enabling the crtc by * calling drm_calc_timestamping_constants(). */ From 5e6c2b4f916157e8f10d093d43e88b2a250d1774 Mon Sep 17 00:00:00 2001 From: Lyude Paul Date: Fri, 17 Apr 2020 15:33:13 -0400 Subject: [PATCH 08/59] drm/vblank: Add vblank works MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add some kind of vblank workers. The interface is similar to regular delayed works, and is mostly based off kthread_work. It allows for scheduling delayed works that execute once a particular vblank sequence has passed. It also allows for accurate flushing of scheduled vblank works - in that flushing waits for both the vblank sequence and job execution to complete, or for the work to get cancelled - whichever comes first. Whatever hardware programming we do in the work must be fast (must at least complete during the vblank or scanout period, sometimes during the first few scanlines of the vblank). As such we use a high-priority per-CRTC thread to accomplish this. Changes since v7: * Stuff drm_vblank_internal.h and drm_vblank_work_internal.h contents into drm_internal.h * Get rid of unnecessary spinlock in drm_crtc_vblank_on() * Remove !vblank->worker check * Grab vbl_lock in drm_vblank_work_schedule() * Mention self-rearming work items in drm_vblank_work_schedule() kdocs * Return 1 from drm_vblank_work_schedule() if the work was scheduled successfully, 0 or error code otherwise * Use drm_dbg_core() instead of DRM_DEV_ERROR() in drm_vblank_work_schedule() * Remove vblank->worker checks in drm_vblank_destroy_worker() and drm_vblank_flush_worker() Changes since v6: * Get rid of ->pending and seqcounts, and implement flushing through simpler means - danvet * Get rid of work_lock, just use drm_device->event_lock * Move drm_vblank_work item cleanup into drm_crtc_vblank_off() so that we ensure that all vblank work has finished before disabling vblanks * Add checks into drm_crtc_vblank_reset() so we yell if it gets called while there's vblank workers active * Grab event_lock in both drm_crtc_vblank_on()/drm_crtc_vblank_off(), the main reason for this is so that other threads calling drm_vblank_work_schedule() are blocked from attempting to schedule while we're in the middle of enabling/disabling vblanks. * Move drm_handle_vblank_works() call below drm_handle_vblank_events() * Simplify drm_vblank_work_cancel_sync() * Fix drm_vblank_work_cancel_sync() documentation * Move wake_up_all() calls out of spinlock where we can. The only one I left was the call to wake_up_all() in drm_vblank_handle_works() as this seemed like it made more sense just living in that function (which is all technically under lock) * Move drm_vblank_work related functions into their own source files * Add drm_vblank_internal.h so we can export some functions we don't want drivers using, but that we do need to use in drm_vblank_work.c * Add a bunch of documentation Changes since v4: * Get rid of kthread interfaces we tried adding and move all of the locking into drm_vblank.c. For implementing drm_vblank_work_flush(), we now use a wait_queue and sequence counters in order to differentiate between multiple work item executions. * Get rid of drm_vblank_work_cancel() - this would have been pretty difficult to actually reimplement and it occurred to me that neither nouveau or i915 are even planning to use this function. Since there's also no async cancel function for most of the work interfaces in the kernel, it seems a bit unnecessary anyway. * Get rid of to_drm_vblank_work() since we now are also able to just pass the struct drm_vblank_work to work item callbacks anyway Changes since v3: * Use our own spinlocks, don't integrate so tightly with kthread_works Changes since v2: * Use kthread_workers instead of reinventing the wheel. Cc: Tejun Heo Cc: dri-devel@lists.freedesktop.org Cc: nouveau@lists.freedesktop.org Reviewed-by: Daniel Vetter Co-developed-by: Ville Syrjälä Signed-off-by: Lyude Paul Acked-by: Dave Airlie Link: https://patchwork.freedesktop.org/patch/msgid/20200627194657.156514-4-lyude@redhat.com --- Documentation/gpu/drm-kms.rst | 15 ++ drivers/gpu/drm/Makefile | 2 +- drivers/gpu/drm/drm_internal.h | 27 +++ drivers/gpu/drm/drm_vblank.c | 44 +++-- drivers/gpu/drm/drm_vblank_work.c | 267 ++++++++++++++++++++++++++++++ include/drm/drm_vblank.h | 20 +++ include/drm/drm_vblank_work.h | 71 ++++++++ 7 files changed, 430 insertions(+), 16 deletions(-) create mode 100644 drivers/gpu/drm/drm_vblank_work.c create mode 100644 include/drm/drm_vblank_work.h diff --git a/Documentation/gpu/drm-kms.rst b/Documentation/gpu/drm-kms.rst index 975cfeb8a353..3c5ae4f6dfd2 100644 --- a/Documentation/gpu/drm-kms.rst +++ b/Documentation/gpu/drm-kms.rst @@ -543,3 +543,18 @@ Vertical Blanking and Interrupt Handling Functions Reference .. kernel-doc:: drivers/gpu/drm/drm_vblank.c :export: + +Vertical Blank Work +=================== + +.. kernel-doc:: drivers/gpu/drm/drm_vblank_work.c + :doc: vblank works + +Vertical Blank Work Functions Reference +--------------------------------------- + +.. kernel-doc:: include/drm/drm_vblank_work.h + :internal: + +.. kernel-doc:: drivers/gpu/drm/drm_vblank_work.c + :export: diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile index 2c0e5a7e5953..02ee5faf1a92 100644 --- a/drivers/gpu/drm/Makefile +++ b/drivers/gpu/drm/Makefile @@ -18,7 +18,7 @@ drm-y := drm_auth.o drm_cache.o \ drm_dumb_buffers.o drm_mode_config.o drm_vblank.o \ drm_syncobj.o drm_lease.o drm_writeback.o drm_client.o \ drm_client_modeset.o drm_atomic_uapi.o drm_hdcp.o \ - drm_managed.o + drm_managed.o drm_vblank_work.o drm-$(CONFIG_DRM_LEGACY) += drm_legacy_misc.o drm_bufs.o drm_context.o drm_dma.o drm_scatter.o drm_lock.o drm-$(CONFIG_DRM_LIB_RANDOM) += lib/drm_random.o diff --git a/drivers/gpu/drm/drm_internal.h b/drivers/gpu/drm/drm_internal.h index 2470a352730b..8e01caaf95cc 100644 --- a/drivers/gpu/drm/drm_internal.h +++ b/drivers/gpu/drm/drm_internal.h @@ -21,7 +21,10 @@ * OTHER DEALINGS IN THE SOFTWARE. */ +#include + #include +#include #define DRM_IF_MAJOR 1 #define DRM_IF_MINOR 4 @@ -38,6 +41,7 @@ struct drm_master; struct drm_minor; struct drm_prime_file_private; struct drm_printer; +struct drm_vblank_crtc; /* drm_file.c */ extern struct mutex drm_global_mutex; @@ -93,7 +97,30 @@ void drm_minor_release(struct drm_minor *minor); void drm_managed_release(struct drm_device *dev); /* drm_vblank.c */ +static inline bool drm_vblank_passed(u64 seq, u64 ref) +{ + return (seq - ref) <= (1 << 23); +} + void drm_vblank_disable_and_save(struct drm_device *dev, unsigned int pipe); +int drm_vblank_get(struct drm_device *dev, unsigned int pipe); +void drm_vblank_put(struct drm_device *dev, unsigned int pipe); +u64 drm_vblank_count(struct drm_device *dev, unsigned int pipe); + +/* drm_vblank_work.c */ +static inline void drm_vblank_flush_worker(struct drm_vblank_crtc *vblank) +{ + kthread_flush_worker(vblank->worker); +} + +static inline void drm_vblank_destroy_worker(struct drm_vblank_crtc *vblank) +{ + kthread_destroy_worker(vblank->worker); +} + +int drm_vblank_worker_init(struct drm_vblank_crtc *vblank); +void drm_vblank_cancel_pending_works(struct drm_vblank_crtc *vblank); +void drm_handle_vblank_works(struct drm_vblank_crtc *vblank); /* IOCTLS */ int drm_wait_vblank_ioctl(struct drm_device *dev, void *data, diff --git a/drivers/gpu/drm/drm_vblank.c b/drivers/gpu/drm/drm_vblank.c index e277e40e5b82..f402c75b9d34 100644 --- a/drivers/gpu/drm/drm_vblank.c +++ b/drivers/gpu/drm/drm_vblank.c @@ -25,6 +25,7 @@ */ #include +#include #include #include @@ -363,7 +364,7 @@ static void drm_update_vblank_count(struct drm_device *dev, unsigned int pipe, store_vblank(dev, pipe, diff, t_vblank, cur_vblank); } -static u64 drm_vblank_count(struct drm_device *dev, unsigned int pipe) +u64 drm_vblank_count(struct drm_device *dev, unsigned int pipe) { struct drm_vblank_crtc *vblank = &dev->vblank[pipe]; u64 count; @@ -497,6 +498,7 @@ static void drm_vblank_init_release(struct drm_device *dev, void *ptr) drm_WARN_ON(dev, READ_ONCE(vblank->enabled) && drm_core_check_feature(dev, DRIVER_MODESET)); + drm_vblank_destroy_worker(vblank); del_timer_sync(&vblank->disable_timer); } @@ -539,6 +541,10 @@ int drm_vblank_init(struct drm_device *dev, unsigned int num_crtcs) vblank); if (ret) return ret; + + ret = drm_vblank_worker_init(vblank); + if (ret) + return ret; } return 0; @@ -1135,7 +1141,7 @@ static int drm_vblank_enable(struct drm_device *dev, unsigned int pipe) return ret; } -static int drm_vblank_get(struct drm_device *dev, unsigned int pipe) +int drm_vblank_get(struct drm_device *dev, unsigned int pipe) { struct drm_vblank_crtc *vblank = &dev->vblank[pipe]; unsigned long irqflags; @@ -1178,7 +1184,7 @@ int drm_crtc_vblank_get(struct drm_crtc *crtc) } EXPORT_SYMBOL(drm_crtc_vblank_get); -static void drm_vblank_put(struct drm_device *dev, unsigned int pipe) +void drm_vblank_put(struct drm_device *dev, unsigned int pipe) { struct drm_vblank_crtc *vblank = &dev->vblank[pipe]; @@ -1281,13 +1287,16 @@ void drm_crtc_vblank_off(struct drm_crtc *crtc) unsigned int pipe = drm_crtc_index(crtc); struct drm_vblank_crtc *vblank = &dev->vblank[pipe]; struct drm_pending_vblank_event *e, *t; - ktime_t now; u64 seq; if (drm_WARN_ON(dev, pipe >= dev->num_crtcs)) return; + /* + * Grab event_lock early to prevent vblank work from being scheduled + * while we're in the middle of shutting down vblank interrupts + */ spin_lock_irq(&dev->event_lock); spin_lock(&dev->vbl_lock); @@ -1324,11 +1333,18 @@ void drm_crtc_vblank_off(struct drm_crtc *crtc) drm_vblank_put(dev, pipe); send_vblank_event(dev, e, seq, now); } + + /* Cancel any leftover pending vblank work */ + drm_vblank_cancel_pending_works(vblank); + spin_unlock_irq(&dev->event_lock); /* Will be reset by the modeset helpers when re-enabling the crtc by * calling drm_calc_timestamping_constants(). */ vblank->hwmode.crtc_clock = 0; + + /* Wait for any vblank work that's still executing to finish */ + drm_vblank_flush_worker(vblank); } EXPORT_SYMBOL(drm_crtc_vblank_off); @@ -1363,6 +1379,7 @@ void drm_crtc_vblank_reset(struct drm_crtc *crtc) spin_unlock_irqrestore(&dev->vbl_lock, irqflags); drm_WARN_ON(dev, !list_empty(&dev->vblank_event_list)); + drm_WARN_ON(dev, !list_empty(&vblank->pending_work)); } EXPORT_SYMBOL(drm_crtc_vblank_reset); @@ -1589,11 +1606,6 @@ int drm_legacy_modeset_ctl_ioctl(struct drm_device *dev, void *data, return 0; } -static inline bool vblank_passed(u64 seq, u64 ref) -{ - return (seq - ref) <= (1 << 23); -} - static int drm_queue_vblank_event(struct drm_device *dev, unsigned int pipe, u64 req_seq, union drm_wait_vblank *vblwait, @@ -1651,7 +1663,7 @@ static int drm_queue_vblank_event(struct drm_device *dev, unsigned int pipe, trace_drm_vblank_event_queued(file_priv, pipe, req_seq); e->sequence = req_seq; - if (vblank_passed(seq, req_seq)) { + if (drm_vblank_passed(seq, req_seq)) { drm_vblank_put(dev, pipe); send_vblank_event(dev, e, seq, now); vblwait->reply.sequence = seq; @@ -1806,7 +1818,7 @@ int drm_wait_vblank_ioctl(struct drm_device *dev, void *data, } if ((flags & _DRM_VBLANK_NEXTONMISS) && - vblank_passed(seq, req_seq)) { + drm_vblank_passed(seq, req_seq)) { req_seq = seq + 1; vblwait->request.type &= ~_DRM_VBLANK_NEXTONMISS; vblwait->request.sequence = req_seq; @@ -1825,7 +1837,7 @@ int drm_wait_vblank_ioctl(struct drm_device *dev, void *data, drm_dbg_core(dev, "waiting on vblank count %llu, crtc %u\n", req_seq, pipe); wait = wait_event_interruptible_timeout(vblank->queue, - vblank_passed(drm_vblank_count(dev, pipe), req_seq) || + drm_vblank_passed(drm_vblank_count(dev, pipe), req_seq) || !READ_ONCE(vblank->enabled), msecs_to_jiffies(3000)); @@ -1874,7 +1886,7 @@ static void drm_handle_vblank_events(struct drm_device *dev, unsigned int pipe) list_for_each_entry_safe(e, t, &dev->vblank_event_list, base.link) { if (e->pipe != pipe) continue; - if (!vblank_passed(seq, e->sequence)) + if (!drm_vblank_passed(seq, e->sequence)) continue; drm_dbg_core(dev, "vblank event on %llu, current %llu\n", @@ -1944,6 +1956,7 @@ bool drm_handle_vblank(struct drm_device *dev, unsigned int pipe) !atomic_read(&vblank->refcount)); drm_handle_vblank_events(dev, pipe); + drm_handle_vblank_works(vblank); spin_unlock_irqrestore(&dev->event_lock, irqflags); @@ -2097,7 +2110,7 @@ int drm_crtc_queue_sequence_ioctl(struct drm_device *dev, void *data, if (flags & DRM_CRTC_SEQUENCE_RELATIVE) req_seq += seq; - if ((flags & DRM_CRTC_SEQUENCE_NEXT_ON_MISS) && vblank_passed(seq, req_seq)) + if ((flags & DRM_CRTC_SEQUENCE_NEXT_ON_MISS) && drm_vblank_passed(seq, req_seq)) req_seq = seq + 1; e->pipe = pipe; @@ -2126,7 +2139,7 @@ int drm_crtc_queue_sequence_ioctl(struct drm_device *dev, void *data, e->sequence = req_seq; - if (vblank_passed(seq, req_seq)) { + if (drm_vblank_passed(seq, req_seq)) { drm_crtc_vblank_put(crtc); send_vblank_event(dev, e, seq, now); queue_seq->sequence = seq; @@ -2146,3 +2159,4 @@ err_free: kfree(e); return ret; } + diff --git a/drivers/gpu/drm/drm_vblank_work.c b/drivers/gpu/drm/drm_vblank_work.c new file mode 100644 index 000000000000..7ac0fc0a9415 --- /dev/null +++ b/drivers/gpu/drm/drm_vblank_work.c @@ -0,0 +1,267 @@ +// SPDX-License-Identifier: MIT + +#include + +#include +#include +#include +#include + +#include "drm_internal.h" + +/** + * DOC: vblank works + * + * Many DRM drivers need to program hardware in a time-sensitive manner, many + * times with a deadline of starting and finishing within a certain region of + * the scanout. Most of the time the safest way to accomplish this is to + * simply do said time-sensitive programming in the driver's IRQ handler, + * which allows drivers to avoid being preempted during these critical + * regions. Or even better, the hardware may even handle applying such + * time-critical programming independently of the CPU. + * + * While there's a decent amount of hardware that's designed so that the CPU + * doesn't need to be concerned with extremely time-sensitive programming, + * there's a few situations where it can't be helped. Some unforgiving + * hardware may require that certain time-sensitive programming be handled + * completely by the CPU, and said programming may even take too long to + * handle in an IRQ handler. Another such situation would be where the driver + * needs to perform a task that needs to complete within a specific scanout + * period, but might possibly block and thus cannot be handled in an IRQ + * context. Both of these situations can't be solved perfectly in Linux since + * we're not a realtime kernel, and thus the scheduler may cause us to miss + * our deadline if it decides to preempt us. But for some drivers, it's good + * enough if we can lower our chance of being preempted to an absolute + * minimum. + * + * This is where &drm_vblank_work comes in. &drm_vblank_work provides a simple + * generic delayed work implementation which delays work execution until a + * particular vblank has passed, and then executes the work at realtime + * priority. This provides the best possible chance at performing + * time-sensitive hardware programming on time, even when the system is under + * heavy load. &drm_vblank_work also supports rescheduling, so that self + * re-arming work items can be easily implemented. + */ + +void drm_handle_vblank_works(struct drm_vblank_crtc *vblank) +{ + struct drm_vblank_work *work, *next; + u64 count = atomic64_read(&vblank->count); + bool wake = false; + + assert_spin_locked(&vblank->dev->event_lock); + + list_for_each_entry_safe(work, next, &vblank->pending_work, node) { + if (!drm_vblank_passed(count, work->count)) + continue; + + list_del_init(&work->node); + drm_vblank_put(vblank->dev, vblank->pipe); + kthread_queue_work(vblank->worker, &work->base); + wake = true; + } + if (wake) + wake_up_all(&vblank->work_wait_queue); +} + +/* Handle cancelling any pending vblank work items and drop respective vblank + * references in response to vblank interrupts being disabled. + */ +void drm_vblank_cancel_pending_works(struct drm_vblank_crtc *vblank) +{ + struct drm_vblank_work *work, *next; + + assert_spin_locked(&vblank->dev->event_lock); + + list_for_each_entry_safe(work, next, &vblank->pending_work, node) { + list_del_init(&work->node); + drm_vblank_put(vblank->dev, vblank->pipe); + } + + wake_up_all(&vblank->work_wait_queue); +} + +/** + * drm_vblank_work_schedule - schedule a vblank work + * @work: vblank work to schedule + * @count: target vblank count + * @nextonmiss: defer until the next vblank if target vblank was missed + * + * Schedule @work for execution once the crtc vblank count reaches @count. + * + * If the crtc vblank count has already reached @count and @nextonmiss is + * %false the work starts to execute immediately. + * + * If the crtc vblank count has already reached @count and @nextonmiss is + * %true the work is deferred until the next vblank (as if @count has been + * specified as crtc vblank count + 1). + * + * If @work is already scheduled, this function will reschedule said work + * using the new @count. This can be used for self-rearming work items. + * + * Returns: + * %1 if @work was successfully (re)scheduled, %0 if it was either already + * scheduled or cancelled, or a negative error code on failure. + */ +int drm_vblank_work_schedule(struct drm_vblank_work *work, + u64 count, bool nextonmiss) +{ + struct drm_vblank_crtc *vblank = work->vblank; + struct drm_device *dev = vblank->dev; + u64 cur_vbl; + unsigned long irqflags; + bool passed, inmodeset, rescheduling = false, wake = false; + int ret = 0; + + spin_lock_irqsave(&dev->event_lock, irqflags); + if (work->cancelling) + goto out; + + spin_lock(&dev->vbl_lock); + inmodeset = vblank->inmodeset; + spin_unlock(&dev->vbl_lock); + if (inmodeset) + goto out; + + if (list_empty(&work->node)) { + ret = drm_vblank_get(dev, vblank->pipe); + if (ret < 0) + goto out; + } else if (work->count == count) { + /* Already scheduled w/ same vbl count */ + goto out; + } else { + rescheduling = true; + } + + work->count = count; + cur_vbl = drm_vblank_count(dev, vblank->pipe); + passed = drm_vblank_passed(cur_vbl, count); + if (passed) + drm_dbg_core(dev, + "crtc %d vblank %llu already passed (current %llu)\n", + vblank->pipe, count, cur_vbl); + + if (!nextonmiss && passed) { + drm_vblank_put(dev, vblank->pipe); + ret = kthread_queue_work(vblank->worker, &work->base); + + if (rescheduling) { + list_del_init(&work->node); + wake = true; + } + } else { + if (!rescheduling) + list_add_tail(&work->node, &vblank->pending_work); + ret = true; + } + +out: + spin_unlock_irqrestore(&dev->event_lock, irqflags); + if (wake) + wake_up_all(&vblank->work_wait_queue); + return ret; +} +EXPORT_SYMBOL(drm_vblank_work_schedule); + +/** + * drm_vblank_work_cancel_sync - cancel a vblank work and wait for it to + * finish executing + * @work: vblank work to cancel + * + * Cancel an already scheduled vblank work and wait for its + * execution to finish. + * + * On return, @work is guaranteed to no longer be scheduled or running, even + * if it's self-arming. + * + * Returns: + * %True if the work was cancelled before it started to execute, %false + * otherwise. + */ +bool drm_vblank_work_cancel_sync(struct drm_vblank_work *work) +{ + struct drm_vblank_crtc *vblank = work->vblank; + struct drm_device *dev = vblank->dev; + bool ret = false; + + spin_lock_irq(&dev->event_lock); + if (!list_empty(&work->node)) { + list_del_init(&work->node); + drm_vblank_put(vblank->dev, vblank->pipe); + ret = true; + } + + work->cancelling++; + spin_unlock_irq(&dev->event_lock); + + wake_up_all(&vblank->work_wait_queue); + + if (kthread_cancel_work_sync(&work->base)) + ret = true; + + spin_lock_irq(&dev->event_lock); + work->cancelling--; + spin_unlock_irq(&dev->event_lock); + + return ret; +} +EXPORT_SYMBOL(drm_vblank_work_cancel_sync); + +/** + * drm_vblank_work_flush - wait for a scheduled vblank work to finish + * executing + * @work: vblank work to flush + * + * Wait until @work has finished executing once. + */ +void drm_vblank_work_flush(struct drm_vblank_work *work) +{ + struct drm_vblank_crtc *vblank = work->vblank; + struct drm_device *dev = vblank->dev; + + spin_lock_irq(&dev->event_lock); + wait_event_lock_irq(vblank->work_wait_queue, list_empty(&work->node), + dev->event_lock); + spin_unlock_irq(&dev->event_lock); + + kthread_flush_work(&work->base); +} +EXPORT_SYMBOL(drm_vblank_work_flush); + +/** + * drm_vblank_work_init - initialize a vblank work item + * @work: vblank work item + * @crtc: CRTC whose vblank will trigger the work execution + * @func: work function to be executed + * + * Initialize a vblank work item for a specific crtc. + */ +void drm_vblank_work_init(struct drm_vblank_work *work, struct drm_crtc *crtc, + void (*func)(struct kthread_work *work)) +{ + kthread_init_work(&work->base, func); + INIT_LIST_HEAD(&work->node); + work->vblank = &crtc->dev->vblank[drm_crtc_index(crtc)]; +} +EXPORT_SYMBOL(drm_vblank_work_init); + +int drm_vblank_worker_init(struct drm_vblank_crtc *vblank) +{ + struct sched_param param = { + .sched_priority = MAX_RT_PRIO - 1, + }; + struct kthread_worker *worker; + + INIT_LIST_HEAD(&vblank->pending_work); + init_waitqueue_head(&vblank->work_wait_queue); + worker = kthread_create_worker(0, "card%d-crtc%d", + vblank->dev->primary->index, + vblank->pipe); + if (IS_ERR(worker)) + return PTR_ERR(worker); + + vblank->worker = worker; + + return sched_setscheduler(vblank->worker->task, SCHED_FIFO, ¶m); +} diff --git a/include/drm/drm_vblank.h b/include/drm/drm_vblank.h index dd9f5b9e56e4..dd125f8c766c 100644 --- a/include/drm/drm_vblank.h +++ b/include/drm/drm_vblank.h @@ -27,12 +27,14 @@ #include #include #include +#include #include #include struct drm_device; struct drm_crtc; +struct drm_vblank_work; /** * struct drm_pending_vblank_event - pending vblank event tracking @@ -203,6 +205,24 @@ struct drm_vblank_crtc { * disabling functions multiple times. */ bool enabled; + + /** + * @worker: The &kthread_worker used for executing vblank works. + */ + struct kthread_worker *worker; + + /** + * @pending_work: A list of scheduled &drm_vblank_work items that are + * waiting for a future vblank. + */ + struct list_head pending_work; + + /** + * @work_wait_queue: The wait queue used for signaling that a + * &drm_vblank_work item has either finished executing, or was + * cancelled. + */ + wait_queue_head_t work_wait_queue; }; int drm_vblank_init(struct drm_device *dev, unsigned int num_crtcs); diff --git a/include/drm/drm_vblank_work.h b/include/drm/drm_vblank_work.h new file mode 100644 index 000000000000..eb41d0810c4f --- /dev/null +++ b/include/drm/drm_vblank_work.h @@ -0,0 +1,71 @@ +/* SPDX-License-Identifier: MIT */ + +#ifndef _DRM_VBLANK_WORK_H_ +#define _DRM_VBLANK_WORK_H_ + +#include + +struct drm_crtc; + +/** + * struct drm_vblank_work - A delayed work item which delays until a target + * vblank passes, and then executes at realtime priority outside of IRQ + * context. + * + * See also: + * drm_vblank_work_schedule() + * drm_vblank_work_init() + * drm_vblank_work_cancel_sync() + * drm_vblank_work_flush() + */ +struct drm_vblank_work { + /** + * @base: The base &kthread_work item which will be executed by + * &drm_vblank_crtc.worker. Drivers should not interact with this + * directly, and instead rely on drm_vblank_work_init() to initialize + * this. + */ + struct kthread_work base; + + /** + * @vblank: A pointer to &drm_vblank_crtc this work item belongs to. + */ + struct drm_vblank_crtc *vblank; + + /** + * @count: The target vblank this work will execute on. Drivers should + * not modify this value directly, and instead use + * drm_vblank_work_schedule() + */ + u64 count; + + /** + * @cancelling: The number of drm_vblank_work_cancel_sync() calls that + * are currently running. A work item cannot be rescheduled until all + * calls have finished. + */ + int cancelling; + + /** + * @node: The position of this work item in + * &drm_vblank_crtc.pending_work. + */ + struct list_head node; +}; + +/** + * to_drm_vblank_work - Retrieve the respective &drm_vblank_work item from a + * &kthread_work + * @_work: The &kthread_work embedded inside a &drm_vblank_work + */ +#define to_drm_vblank_work(_work) \ + container_of((_work), struct drm_vblank_work, base) + +int drm_vblank_work_schedule(struct drm_vblank_work *work, + u64 count, bool nextonmiss); +void drm_vblank_work_init(struct drm_vblank_work *work, struct drm_crtc *crtc, + void (*func)(struct kthread_work *work)); +bool drm_vblank_work_cancel_sync(struct drm_vblank_work *work); +void drm_vblank_work_flush(struct drm_vblank_work *work); + +#endif /* !_DRM_VBLANK_WORK_H_ */ From 9c8e9b790de33914e674035203461cb75c727e98 Mon Sep 17 00:00:00 2001 From: Lyude Paul Date: Fri, 13 Dec 2019 17:06:59 -0500 Subject: [PATCH 09/59] drm/nouveau/kms/nv140-: Don't modify depth in state during atomic commit Currently, we modify the depth value stored in the atomic state when performing a commit in order to workaround the fact we haven't implemented support for depths higher then 10 yet. This isn't idempotent though, as it will happen every atomic commit where we modify the OR state even if the head's depth in the atomic state hasn't been modified. Normally this wouldn't matter, since we don't modify OR state outside of modesets, but since the CRC capture region is implemented as part of the OR state in hardware we'll want to make sure all commits modifying OR state are idempotent so as to avoid changing the depth unexpectedly. So, fix this by simply not writing the reduced depth value we come up with to the atomic state. Signed-off-by: Lyude Paul Reviewed-by: Ben Skeggs Acked-by: Dave Airlie Link: https://patchwork.freedesktop.org/patch/msgid/20200627194657.156514-5-lyude@redhat.com --- drivers/gpu/drm/nouveau/dispnv50/headc37d.c | 11 +++++++---- drivers/gpu/drm/nouveau/dispnv50/headc57d.c | 11 +++++++---- 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/drivers/gpu/drm/nouveau/dispnv50/headc37d.c b/drivers/gpu/drm/nouveau/dispnv50/headc37d.c index 4a9a32b89f74..9ef3c603fc43 100644 --- a/drivers/gpu/drm/nouveau/dispnv50/headc37d.c +++ b/drivers/gpu/drm/nouveau/dispnv50/headc37d.c @@ -27,17 +27,20 @@ static void headc37d_or(struct nv50_head *head, struct nv50_head_atom *asyh) { struct nv50_dmac *core = &nv50_disp(head->base.base.dev)->core->chan; + u8 depth; u32 *push; + if ((push = evo_wait(core, 2))) { /*XXX: This is a dirty hack until OR depth handling is * improved later for deep colour etc. */ switch (asyh->or.depth) { - case 6: asyh->or.depth = 5; break; - case 5: asyh->or.depth = 4; break; - case 2: asyh->or.depth = 1; break; - case 0: asyh->or.depth = 4; break; + case 6: depth = 5; break; + case 5: depth = 4; break; + case 2: depth = 1; break; + case 0: depth = 4; break; default: + depth = asyh->or.depth; WARN_ON(1); break; } diff --git a/drivers/gpu/drm/nouveau/dispnv50/headc57d.c b/drivers/gpu/drm/nouveau/dispnv50/headc57d.c index 859131a8bc3c..97141eb8e75a 100644 --- a/drivers/gpu/drm/nouveau/dispnv50/headc57d.c +++ b/drivers/gpu/drm/nouveau/dispnv50/headc57d.c @@ -27,17 +27,20 @@ static void headc57d_or(struct nv50_head *head, struct nv50_head_atom *asyh) { struct nv50_dmac *core = &nv50_disp(head->base.base.dev)->core->chan; + u8 depth; u32 *push; + if ((push = evo_wait(core, 2))) { /*XXX: This is a dirty hack until OR depth handling is * improved later for deep colour etc. */ switch (asyh->or.depth) { - case 6: asyh->or.depth = 5; break; - case 5: asyh->or.depth = 4; break; - case 2: asyh->or.depth = 1; break; - case 0: asyh->or.depth = 4; break; + case 6: depth = 5; break; + case 5: depth = 4; break; + case 2: depth = 1; break; + case 0: depth = 4; break; default: + depth = asyh->or.depth; WARN_ON(1); break; } From fb2420b701edbf96c2b6d557f0139902f455dc2b Mon Sep 17 00:00:00 2001 From: Lyude Paul Date: Tue, 17 Mar 2020 14:54:06 -0400 Subject: [PATCH 10/59] drm/nouveau/kms/nv50-: Fix disabling dithering While we expose the ability to turn off hardware dithering for nouveau, we actually make the mistake of turning it on anyway, due to dithering_depth containing a non-zero value if our dithering depth isn't also set to 6 bpc. So, fix it by never enabling dithering when it's disabled. Signed-off-by: Lyude Paul Reviewed-by: Ben Skeggs Acked-by: Dave Airlie Link: https://patchwork.freedesktop.org/patch/msgid/20200627194657.156514-6-lyude@redhat.com --- drivers/gpu/drm/nouveau/dispnv50/head.c | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/drivers/gpu/drm/nouveau/dispnv50/head.c b/drivers/gpu/drm/nouveau/dispnv50/head.c index 8f6455697ba7..ed6819519f6d 100644 --- a/drivers/gpu/drm/nouveau/dispnv50/head.c +++ b/drivers/gpu/drm/nouveau/dispnv50/head.c @@ -84,18 +84,20 @@ nv50_head_atomic_check_dither(struct nv50_head_atom *armh, { u32 mode = 0x00; - if (asyc->dither.mode == DITHERING_MODE_AUTO) { - if (asyh->base.depth > asyh->or.bpc * 3) - mode = DITHERING_MODE_DYNAMIC2X2; - } else { - mode = asyc->dither.mode; - } + if (asyc->dither.mode) { + if (asyc->dither.mode == DITHERING_MODE_AUTO) { + if (asyh->base.depth > asyh->or.bpc * 3) + mode = DITHERING_MODE_DYNAMIC2X2; + } else { + mode = asyc->dither.mode; + } - if (asyc->dither.depth == DITHERING_DEPTH_AUTO) { - if (asyh->or.bpc >= 8) - mode |= DITHERING_DEPTH_8BPC; - } else { - mode |= asyc->dither.depth; + if (asyc->dither.depth == DITHERING_DEPTH_AUTO) { + if (asyh->or.bpc >= 8) + mode |= DITHERING_DEPTH_8BPC; + } else { + mode |= asyc->dither.depth; + } } asyh->dither.enable = mode; From dbdaf719c63a32028a01a6dce7317d35e3e3ba50 Mon Sep 17 00:00:00 2001 From: Lyude Paul Date: Thu, 6 Feb 2020 14:37:36 -0500 Subject: [PATCH 11/59] drm/nouveau/kms/nv140-: Track wndw mappings in nv50_head_atom While we're not quite ready yet to add support for flexible wndw mappings, we are going to need to at least keep track of the static wndw mappings we're currently using in each head's atomic state. We'll likely use this in the future to implement real flexible window mapping, but the primary reason we'll need this is for CRC support. See: on nvidia hardware, each CRC entry in the CRC notifier dma context has a "tag". This tag corresponds to the nth update on a specific EVO/NvDisplay channel, which itself is referred to as the "controlling channel". For gf119+ this can be the core channel, ovly channel, or base channel. Since we don't expose CRC entry tags to userspace, we simply ignore this feature and always use the core channel as the controlling channel. Simple. Things get a little bit more complicated on gv100+ though. GV100+ only lets us set the controlling channel to a specific wndw channel, and that wndw must be owned by the head that we're grabbing CRCs when we enable CRC generation. Thus, we always need to make sure that each atomic head state has at least one wndw that is mapped to the head, which will be used as the controlling channel. Note that since we don't have flexible wndw mappings yet, we don't expect to run into any scenarios yet where we'd have a head with no mapped wndws. When we do add support for flexible wndw mappings however, we'll need to make sure that we handle reprogramming CRC capture if our controlling wndw is moved to another head (and potentially reject the new head state entirely if we can't find another available wndw to replace it). With that being said, nouveau currently tracks wndw visibility on heads. It does not keep track of the actual ownership mappings, which are (currently) statically programmed. To fix this, we introduce another bitmask into nv50_head_atom.wndw to keep track of ownership separately from visibility. We then introduce a nv50_head callback to handle populating the wndw ownership map, and call it during the atomic check phase when core->assign_windows is set to true. Signed-off-by: Lyude Paul Reviewed-by: Ben Skeggs Acked-by: Dave Airlie Link: https://patchwork.freedesktop.org/patch/msgid/20200627194657.156514-7-lyude@redhat.com --- drivers/gpu/drm/nouveau/dispnv50/atom.h | 1 + drivers/gpu/drm/nouveau/dispnv50/disp.c | 16 ++++++++++++++++ drivers/gpu/drm/nouveau/dispnv50/head.h | 2 ++ drivers/gpu/drm/nouveau/dispnv50/headc37d.c | 10 ++++++++++ drivers/gpu/drm/nouveau/dispnv50/headc57d.c | 2 ++ 5 files changed, 31 insertions(+) diff --git a/drivers/gpu/drm/nouveau/dispnv50/atom.h b/drivers/gpu/drm/nouveau/dispnv50/atom.h index 24f7700768da..62faaf60f47a 100644 --- a/drivers/gpu/drm/nouveau/dispnv50/atom.h +++ b/drivers/gpu/drm/nouveau/dispnv50/atom.h @@ -18,6 +18,7 @@ struct nv50_head_atom { struct { u32 mask; + u32 owned; u32 olut; } wndw; diff --git a/drivers/gpu/drm/nouveau/dispnv50/disp.c b/drivers/gpu/drm/nouveau/dispnv50/disp.c index d472942102f5..368069a5b181 100644 --- a/drivers/gpu/drm/nouveau/dispnv50/disp.c +++ b/drivers/gpu/drm/nouveau/dispnv50/disp.c @@ -2287,12 +2287,28 @@ static int nv50_disp_atomic_check(struct drm_device *dev, struct drm_atomic_state *state) { struct nv50_atom *atom = nv50_atom(state); + struct nv50_core *core = nv50_disp(dev)->core; struct drm_connector_state *old_connector_state, *new_connector_state; struct drm_connector *connector; struct drm_crtc_state *new_crtc_state; struct drm_crtc *crtc; + struct nv50_head *head; + struct nv50_head_atom *asyh; int ret, i; + if (core->assign_windows && core->func->head->static_wndw_map) { + drm_for_each_crtc(crtc, dev) { + new_crtc_state = drm_atomic_get_crtc_state(state, + crtc); + if (IS_ERR(new_crtc_state)) + return PTR_ERR(new_crtc_state); + + head = nv50_head(crtc); + asyh = nv50_head_atom(new_crtc_state); + core->func->head->static_wndw_map(head, asyh); + } + } + /* We need to handle colour management on a per-plane basis. */ for_each_new_crtc_in_state(state, crtc, new_crtc_state, i) { if (new_crtc_state->color_mgmt_changed) { diff --git a/drivers/gpu/drm/nouveau/dispnv50/head.h b/drivers/gpu/drm/nouveau/dispnv50/head.h index c32b27cdaefc..c05bbba9e247 100644 --- a/drivers/gpu/drm/nouveau/dispnv50/head.h +++ b/drivers/gpu/drm/nouveau/dispnv50/head.h @@ -40,6 +40,7 @@ struct nv50_head_func { void (*dither)(struct nv50_head *, struct nv50_head_atom *); void (*procamp)(struct nv50_head *, struct nv50_head_atom *); void (*or)(struct nv50_head *, struct nv50_head_atom *); + void (*static_wndw_map)(struct nv50_head *, struct nv50_head_atom *); }; extern const struct nv50_head_func head507d; @@ -86,6 +87,7 @@ int headc37d_curs_format(struct nv50_head *, struct nv50_wndw_atom *, void headc37d_curs_set(struct nv50_head *, struct nv50_head_atom *); void headc37d_curs_clr(struct nv50_head *); void headc37d_dither(struct nv50_head *, struct nv50_head_atom *); +void headc37d_static_wndw_map(struct nv50_head *, struct nv50_head_atom *); extern const struct nv50_head_func headc57d; #endif diff --git a/drivers/gpu/drm/nouveau/dispnv50/headc37d.c b/drivers/gpu/drm/nouveau/dispnv50/headc37d.c index 9ef3c603fc43..c2619652ff2e 100644 --- a/drivers/gpu/drm/nouveau/dispnv50/headc37d.c +++ b/drivers/gpu/drm/nouveau/dispnv50/headc37d.c @@ -204,6 +204,15 @@ headc37d_view(struct nv50_head *head, struct nv50_head_atom *asyh) } } +void +headc37d_static_wndw_map(struct nv50_head *head, struct nv50_head_atom *asyh) +{ + int i, end; + + for (i = head->base.index * 2, end = i + 2; i < end; i++) + asyh->wndw.owned |= BIT(i); +} + const struct nv50_head_func headc37d = { .view = headc37d_view, @@ -219,4 +228,5 @@ headc37d = { .dither = headc37d_dither, .procamp = headc37d_procamp, .or = headc37d_or, + .static_wndw_map = headc37d_static_wndw_map, }; diff --git a/drivers/gpu/drm/nouveau/dispnv50/headc57d.c b/drivers/gpu/drm/nouveau/dispnv50/headc57d.c index 97141eb8e75a..1c1887749f4c 100644 --- a/drivers/gpu/drm/nouveau/dispnv50/headc57d.c +++ b/drivers/gpu/drm/nouveau/dispnv50/headc57d.c @@ -211,4 +211,6 @@ headc57d = { .dither = headc37d_dither, .procamp = headc57d_procamp, .or = headc57d_or, + /* TODO: flexible window mappings */ + .static_wndw_map = headc37d_static_wndw_map, }; From ebec88472883791ea0bff1cf13b278db6b96afce Mon Sep 17 00:00:00 2001 From: Lyude Paul Date: Fri, 7 Feb 2020 14:10:46 -0500 Subject: [PATCH 12/59] drm/nouveau/kms/nv50-: Expose nv50_outp_atom in disp.h In order to make sure that we flush disable updates at the right time when disabling CRCs, we'll need to be able to look at the outp state to see if we're changing it at the same time that we're disabling CRCs. So, expose the struct in disp.h. Signed-off-by: Lyude Paul Reviewed-by: Ben Skeggs Acked-by: Dave Airlie Link: https://patchwork.freedesktop.org/patch/msgid/20200627194657.156514-8-lyude@redhat.com --- drivers/gpu/drm/nouveau/dispnv50/disp.c | 18 ------------------ drivers/gpu/drm/nouveau/dispnv50/disp.h | 14 ++++++++++++++ 2 files changed, 14 insertions(+), 18 deletions(-) diff --git a/drivers/gpu/drm/nouveau/dispnv50/disp.c b/drivers/gpu/drm/nouveau/dispnv50/disp.c index 368069a5b181..090882794f7d 100644 --- a/drivers/gpu/drm/nouveau/dispnv50/disp.c +++ b/drivers/gpu/drm/nouveau/dispnv50/disp.c @@ -57,24 +57,6 @@ #include -/****************************************************************************** - * Atomic state - *****************************************************************************/ - -struct nv50_outp_atom { - struct list_head head; - - struct drm_encoder *encoder; - bool flush_disable; - - union nv50_outp_atom_mask { - struct { - bool ctrl:1; - }; - u8 mask; - } set, clr; -}; - /****************************************************************************** * EVO channel *****************************************************************************/ diff --git a/drivers/gpu/drm/nouveau/dispnv50/disp.h b/drivers/gpu/drm/nouveau/dispnv50/disp.h index 696e70a6b98b..c7b72fa85099 100644 --- a/drivers/gpu/drm/nouveau/dispnv50/disp.h +++ b/drivers/gpu/drm/nouveau/dispnv50/disp.h @@ -71,6 +71,20 @@ struct nv50_dmac { struct mutex lock; }; +struct nv50_outp_atom { + struct list_head head; + + struct drm_encoder *encoder; + bool flush_disable; + + union nv50_outp_atom_mask { + struct { + bool ctrl:1; + }; + u8 mask; + } set, clr; +}; + int nv50_dmac_create(struct nvif_device *device, struct nvif_object *disp, const s32 *oclass, u8 head, void *data, u32 size, u64 syncbuf, struct nv50_dmac *dmac); From 0bc8ffe09771c182e652cb388293049b88797772 Mon Sep 17 00:00:00 2001 From: Lyude Paul Date: Tue, 21 Jan 2020 15:53:46 -0500 Subject: [PATCH 13/59] drm/nouveau/kms/nv50-: Move hard-coded object handles into header While most of the functionality on Nvidia GPUs doesn't require using an explicit handle instead of the main VRAM handle + offset, there are a couple of places that do require explicit handles, such as CRC functionality. Since this means we're about to add another nouveau-chosen handle, let's just go ahead and move any hard-coded handles into a single header. This is just to keep things slightly organized, and to make it a little bit easier if we need to add more handles in the future. This patch should contain no functional changes. Changes since v3: * Correct SPDX license identifier (checkpatch) Signed-off-by: Lyude Paul Reviewed-by: Ben Skeggs Acked-by: Dave Airlie Link: https://patchwork.freedesktop.org/patch/msgid/20200627194657.156514-9-lyude@redhat.com --- drivers/gpu/drm/nouveau/dispnv50/disp.c | 7 +++++-- drivers/gpu/drm/nouveau/dispnv50/handles.h | 15 +++++++++++++++ drivers/gpu/drm/nouveau/dispnv50/wndw.c | 3 ++- 3 files changed, 22 insertions(+), 3 deletions(-) create mode 100644 drivers/gpu/drm/nouveau/dispnv50/handles.h diff --git a/drivers/gpu/drm/nouveau/dispnv50/disp.c b/drivers/gpu/drm/nouveau/dispnv50/disp.c index 090882794f7d..bf7ba1e1c0f7 100644 --- a/drivers/gpu/drm/nouveau/dispnv50/disp.c +++ b/drivers/gpu/drm/nouveau/dispnv50/disp.c @@ -26,6 +26,7 @@ #include "core.h" #include "head.h" #include "wndw.h" +#include "handles.h" #include #include @@ -154,7 +155,8 @@ nv50_dmac_create(struct nvif_device *device, struct nvif_object *disp, if (!syncbuf) return 0; - ret = nvif_object_init(&dmac->base.user, 0xf0000000, NV_DMA_IN_MEMORY, + ret = nvif_object_init(&dmac->base.user, NV50_DISP_HANDLE_SYNCBUF, + NV_DMA_IN_MEMORY, &(struct nv_dma_v0) { .target = NV_DMA_V0_TARGET_VRAM, .access = NV_DMA_V0_ACCESS_RDWR, @@ -165,7 +167,8 @@ nv50_dmac_create(struct nvif_device *device, struct nvif_object *disp, if (ret) return ret; - ret = nvif_object_init(&dmac->base.user, 0xf0000001, NV_DMA_IN_MEMORY, + ret = nvif_object_init(&dmac->base.user, NV50_DISP_HANDLE_VRAM, + NV_DMA_IN_MEMORY, &(struct nv_dma_v0) { .target = NV_DMA_V0_TARGET_VRAM, .access = NV_DMA_V0_ACCESS_RDWR, diff --git a/drivers/gpu/drm/nouveau/dispnv50/handles.h b/drivers/gpu/drm/nouveau/dispnv50/handles.h new file mode 100644 index 000000000000..e3a62c7a0d08 --- /dev/null +++ b/drivers/gpu/drm/nouveau/dispnv50/handles.h @@ -0,0 +1,15 @@ +/* SPDX-License-Identifier: MIT */ +#ifndef __NV50_KMS_HANDLES_H__ +#define __NV50_KMS_HANDLES_H__ + +/* + * Various hard-coded object handles that nouveau uses. These are made-up by + * nouveau developers, not Nvidia. The only significance of the handles chosen + * is that they must all be unique. + */ +#define NV50_DISP_HANDLE_SYNCBUF 0xf0000000 +#define NV50_DISP_HANDLE_VRAM 0xf0000001 + +#define NV50_DISP_HANDLE_WNDW_CTX(kind) (0xfb000000 | kind) + +#endif /* !__NV50_KMS_HANDLES_H__ */ diff --git a/drivers/gpu/drm/nouveau/dispnv50/wndw.c b/drivers/gpu/drm/nouveau/dispnv50/wndw.c index 720fe75de185..293ccfdba17e 100644 --- a/drivers/gpu/drm/nouveau/dispnv50/wndw.c +++ b/drivers/gpu/drm/nouveau/dispnv50/wndw.c @@ -21,6 +21,7 @@ */ #include "wndw.h" #include "wimm.h" +#include "handles.h" #include #include @@ -59,7 +60,7 @@ nv50_wndw_ctxdma_new(struct nv50_wndw *wndw, struct drm_framebuffer *fb) int ret; nouveau_framebuffer_get_layout(fb, &unused, &kind); - handle = 0xfb000000 | kind; + handle = NV50_DISP_HANDLE_WNDW_CTX(kind); list_for_each_entry(ctxdma, &wndw->ctxdma.list, head) { if (ctxdma->object.handle == handle) From 12885ecbfe62df4358d452080d3b8feef54ec8cb Mon Sep 17 00:00:00 2001 From: Lyude Paul Date: Mon, 7 Oct 2019 14:20:12 -0400 Subject: [PATCH 14/59] drm/nouveau/kms/nvd9-: Add CRC support This introduces support for CRC readback on gf119+, using the documentation generously provided to us by Nvidia: https://github.com/NVIDIA/open-gpu-doc/blob/master/Display-CRC/display-crc.txt We expose all available CRC sources. SF, SOR, PIOR, and DAC are exposed through a single set of "outp" sources: outp-active/auto for a CRC of the scanout region, outp-complete for a CRC of both the scanout and blanking/sync region combined, and outp-inactive for a CRC of only the blanking/sync region. For each source, nouveau selects the appropriate tap point based on the output path in use. We also expose an "rg" source, which allows for capturing CRCs of the scanout raster before it's encoded into a video signal in the output path. This tap point is referred to as the raster generator. Note that while there's some other neat features that can be used with CRC capture on nvidia hardware, like capturing from two CRC sources simultaneously, I couldn't see any usecase for them and did not implement them. Nvidia only allows for accessing CRCs through a shared DMA region that we program through the core EVO/NvDisplay channel which is referred to as the notifier context. The notifier context is limited to either 255 (for Fermi-Pascal) or 2047 (Volta+) entries to store CRCs in, and unfortunately the hardware simply drops CRCs and reports an overflow once all available entries in the notifier context are filled. Since the DRM CRC API and igt-gpu-tools don't expect there to be a limit on how many CRCs can be captured, we work around this in nouveau by allocating two separate notifier contexts for each head instead of one. We schedule a vblank worker ahead of time so that once we start getting close to filling up all of the available entries in the notifier context, we can swap the currently used notifier context out with another pre-prepared notifier context in a manner similar to page flipping. Unfortunately, the hardware only allows us to this by flushing two separate updates on the core channel: one to release the current notifier context handle, and one to program the next notifier context's handle. When the hardware processes the first update, the CRC for the current frame is lost. However, the second update can be flushed immediately without waiting for the first to complete so that CRC generation resumes on the next frame. According to Nvidia's hardware engineers, there isn't any cleaner way of flipping notifier contexts that would avoid this. Since using vblank workers to swap out the notifier context will ensure we can usually flush both updates to hardware within the timespan of a single frame, we can also ensure that there will only be exactly one frame lost between the first and second update being executed by the hardware. This gives us the guarantee that we're always correctly matching each CRC entry with it's respective frame even after a context flip. And since IGT will retrieve the CRC entry for a frame by waiting until it receives a CRC for any subsequent frames, this doesn't cause an issue with any tests and is much simpler than trying to change the current DRM API to accommodate. In order to facilitate testing of correct handling of this limitation, we also expose a debugfs interface to manually control the threshold for when we start trying to flip the notifier context. We will use this in igt to trigger a context flip for testing purposes without needing to wait for the notifier to completely fill up. This threshold is reset to the default value set by nouveau after each capture, and is exposed in a separate folder within each CRTC's debugfs directory labelled "nv_crc". Changes since v1: * Forgot to finish saving crc.h before saving, whoops. This just adds some corrections to the empty function declarations that we use if CONFIG_DEBUG_FS isn't enabled. Changes since v2: * Don't check return code from debugfs_create_dir() or debugfs_create_file() - Greg K-H Changes since v3: (no functional changes) * Fix SPDX license identifiers (checkpatch) * s/uint32_t/u32/ (checkpatch) * Fix indenting in switch cases (checkpatch) Changes since v4: * Remove unneeded param changes with nv50_head_flush_clr/set * Rebase Changes since v5: * Remove set but unused variable (outp) in nv50_crc_atomic_check() - Kbuild bot Signed-off-by: Lyude Paul Reviewed-by: Ben Skeggs Acked-by: Dave Airlie Link: https://patchwork.freedesktop.org/patch/msgid/20200627194657.156514-10-lyude@redhat.com --- drivers/gpu/drm/nouveau/dispnv04/crtc.c | 25 +- drivers/gpu/drm/nouveau/dispnv50/Kbuild | 4 + drivers/gpu/drm/nouveau/dispnv50/atom.h | 20 + drivers/gpu/drm/nouveau/dispnv50/core.h | 4 + drivers/gpu/drm/nouveau/dispnv50/core907d.c | 3 + drivers/gpu/drm/nouveau/dispnv50/core917d.c | 3 + drivers/gpu/drm/nouveau/dispnv50/corec37d.c | 3 + drivers/gpu/drm/nouveau/dispnv50/corec57d.c | 3 + drivers/gpu/drm/nouveau/dispnv50/crc.c | 714 ++++++++++++++++++++ drivers/gpu/drm/nouveau/dispnv50/crc.h | 125 ++++ drivers/gpu/drm/nouveau/dispnv50/crc907d.c | 139 ++++ drivers/gpu/drm/nouveau/dispnv50/crcc37d.c | 153 +++++ drivers/gpu/drm/nouveau/dispnv50/disp.c | 17 + drivers/gpu/drm/nouveau/dispnv50/disp.h | 10 + drivers/gpu/drm/nouveau/dispnv50/handles.h | 1 + drivers/gpu/drm/nouveau/dispnv50/head.c | 74 +- drivers/gpu/drm/nouveau/dispnv50/head.h | 10 +- drivers/gpu/drm/nouveau/dispnv50/head907d.c | 14 +- drivers/gpu/drm/nouveau/dispnv50/headc37d.c | 6 +- drivers/gpu/drm/nouveau/dispnv50/headc57d.c | 7 +- drivers/gpu/drm/nouveau/nouveau_display.c | 60 +- 21 files changed, 1325 insertions(+), 70 deletions(-) create mode 100644 drivers/gpu/drm/nouveau/dispnv50/crc.c create mode 100644 drivers/gpu/drm/nouveau/dispnv50/crc.h create mode 100644 drivers/gpu/drm/nouveau/dispnv50/crc907d.c create mode 100644 drivers/gpu/drm/nouveau/dispnv50/crcc37d.c diff --git a/drivers/gpu/drm/nouveau/dispnv04/crtc.c b/drivers/gpu/drm/nouveau/dispnv04/crtc.c index 086f7cf4c23e..4989627b7802 100644 --- a/drivers/gpu/drm/nouveau/dispnv04/crtc.c +++ b/drivers/gpu/drm/nouveau/dispnv04/crtc.c @@ -44,6 +44,9 @@ #include #include +#include +#include + static int nv04_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y, struct drm_framebuffer *old_fb); @@ -756,6 +759,7 @@ static void nv_crtc_destroy(struct drm_crtc *crtc) nouveau_bo_unmap(nv_crtc->cursor.nvbo); nouveau_bo_unpin(nv_crtc->cursor.nvbo); nouveau_bo_ref(NULL, &nv_crtc->cursor.nvbo); + nvif_notify_fini(&nv_crtc->vblank); kfree(nv_crtc); } @@ -1297,9 +1301,19 @@ create_primary_plane(struct drm_device *dev) return primary; } +static int nv04_crtc_vblank_handler(struct nvif_notify *notify) +{ + struct nouveau_crtc *nv_crtc = + container_of(notify, struct nouveau_crtc, vblank); + + drm_crtc_handle_vblank(&nv_crtc->base); + return NVIF_NOTIFY_KEEP; +} + int nv04_crtc_create(struct drm_device *dev, int crtc_num) { + struct nouveau_display *disp = nouveau_display(dev); struct nouveau_crtc *nv_crtc; int ret; @@ -1337,5 +1351,14 @@ nv04_crtc_create(struct drm_device *dev, int crtc_num) nv04_cursor_init(nv_crtc); - return 0; + ret = nvif_notify_init(&disp->disp.object, nv04_crtc_vblank_handler, + false, NV04_DISP_NTFY_VBLANK, + &(struct nvif_notify_head_req_v0) { + .head = nv_crtc->index, + }, + sizeof(struct nvif_notify_head_req_v0), + sizeof(struct nvif_notify_head_rep_v0), + &nv_crtc->vblank); + + return ret; } diff --git a/drivers/gpu/drm/nouveau/dispnv50/Kbuild b/drivers/gpu/drm/nouveau/dispnv50/Kbuild index e0c435eae664..6fdddb266fb1 100644 --- a/drivers/gpu/drm/nouveau/dispnv50/Kbuild +++ b/drivers/gpu/drm/nouveau/dispnv50/Kbuild @@ -10,6 +10,10 @@ nouveau-y += dispnv50/core917d.o nouveau-y += dispnv50/corec37d.o nouveau-y += dispnv50/corec57d.o +nouveau-$(CONFIG_DEBUG_FS) += dispnv50/crc.o +nouveau-$(CONFIG_DEBUG_FS) += dispnv50/crc907d.o +nouveau-$(CONFIG_DEBUG_FS) += dispnv50/crcc37d.o + nouveau-y += dispnv50/dac507d.o nouveau-y += dispnv50/dac907d.o diff --git a/drivers/gpu/drm/nouveau/dispnv50/atom.h b/drivers/gpu/drm/nouveau/dispnv50/atom.h index 62faaf60f47a..3d82b3c67dec 100644 --- a/drivers/gpu/drm/nouveau/dispnv50/atom.h +++ b/drivers/gpu/drm/nouveau/dispnv50/atom.h @@ -2,6 +2,9 @@ #define __NV50_KMS_ATOM_H__ #define nv50_atom(p) container_of((p), struct nv50_atom, state) #include +#include "crc.h" + +struct nouveau_encoder; struct nv50_atom { struct drm_atomic_state state; @@ -115,9 +118,12 @@ struct nv50_head_atom { u8 nhsync:1; u8 nvsync:1; u8 depth:4; + u8 crc_raster:2; u8 bpc; } or; + struct nv50_crc_atom crc; + /* Currently only used for MST */ struct { int pbn; @@ -135,6 +141,7 @@ struct nv50_head_atom { bool ovly:1; bool dither:1; bool procamp:1; + bool crc:1; bool or:1; }; u16 mask; @@ -150,6 +157,19 @@ nv50_head_atom_get(struct drm_atomic_state *state, struct drm_crtc *crtc) return nv50_head_atom(statec); } +static inline struct drm_encoder * +nv50_head_atom_get_encoder(struct nv50_head_atom *atom) +{ + struct drm_encoder *encoder = NULL; + + /* We only ever have a single encoder */ + drm_for_each_encoder_mask(encoder, atom->state.crtc->dev, + atom->state.encoder_mask) + break; + + return encoder; +} + #define nv50_wndw_atom(p) container_of((p), struct nv50_wndw_atom, state) struct nv50_wndw_atom { diff --git a/drivers/gpu/drm/nouveau/dispnv50/core.h b/drivers/gpu/drm/nouveau/dispnv50/core.h index 99157dc94d23..e021cb340569 100644 --- a/drivers/gpu/drm/nouveau/dispnv50/core.h +++ b/drivers/gpu/drm/nouveau/dispnv50/core.h @@ -2,6 +2,7 @@ #define __NV50_KMS_CORE_H__ #include "disp.h" #include "atom.h" +#include "crc.h" #include struct nv50_core { @@ -26,6 +27,9 @@ struct nv50_core_func { } wndw; const struct nv50_head_func *head; +#if IS_ENABLED(CONFIG_DEBUG_FS) + const struct nv50_crc_func *crc; +#endif const struct nv50_outp_func { void (*ctrl)(struct nv50_core *, int or, u32 ctrl, struct nv50_head_atom *); diff --git a/drivers/gpu/drm/nouveau/dispnv50/core907d.c b/drivers/gpu/drm/nouveau/dispnv50/core907d.c index 271629832629..b17c03529c78 100644 --- a/drivers/gpu/drm/nouveau/dispnv50/core907d.c +++ b/drivers/gpu/drm/nouveau/dispnv50/core907d.c @@ -30,6 +30,9 @@ core907d = { .ntfy_wait_done = core507d_ntfy_wait_done, .update = core507d_update, .head = &head907d, +#if IS_ENABLED(CONFIG_DEBUG_FS) + .crc = &crc907d, +#endif .dac = &dac907d, .sor = &sor907d, }; diff --git a/drivers/gpu/drm/nouveau/dispnv50/core917d.c b/drivers/gpu/drm/nouveau/dispnv50/core917d.c index 5cc072d4c30f..66846f372080 100644 --- a/drivers/gpu/drm/nouveau/dispnv50/core917d.c +++ b/drivers/gpu/drm/nouveau/dispnv50/core917d.c @@ -30,6 +30,9 @@ core917d = { .ntfy_wait_done = core507d_ntfy_wait_done, .update = core507d_update, .head = &head917d, +#if IS_ENABLED(CONFIG_DEBUG_FS) + .crc = &crc907d, +#endif .dac = &dac907d, .sor = &sor907d, }; diff --git a/drivers/gpu/drm/nouveau/dispnv50/corec37d.c b/drivers/gpu/drm/nouveau/dispnv50/corec37d.c index e0c8811fb8e4..ec83189a1d48 100644 --- a/drivers/gpu/drm/nouveau/dispnv50/corec37d.c +++ b/drivers/gpu/drm/nouveau/dispnv50/corec37d.c @@ -142,6 +142,9 @@ corec37d = { .wndw.owner = corec37d_wndw_owner, .head = &headc37d, .sor = &sorc37d, +#if IS_ENABLED(CONFIG_DEBUG_FS) + .crc = &crcc37d, +#endif }; int diff --git a/drivers/gpu/drm/nouveau/dispnv50/corec57d.c b/drivers/gpu/drm/nouveau/dispnv50/corec57d.c index 10ba9e9e4ae6..e1c11eba0ce1 100644 --- a/drivers/gpu/drm/nouveau/dispnv50/corec57d.c +++ b/drivers/gpu/drm/nouveau/dispnv50/corec57d.c @@ -52,6 +52,9 @@ corec57d = { .wndw.owner = corec37d_wndw_owner, .head = &headc57d, .sor = &sorc37d, +#if IS_ENABLED(CONFIG_DEBUG_FS) + .crc = &crcc37d, +#endif }; int diff --git a/drivers/gpu/drm/nouveau/dispnv50/crc.c b/drivers/gpu/drm/nouveau/dispnv50/crc.c new file mode 100644 index 000000000000..0b18d9e3a2b9 --- /dev/null +++ b/drivers/gpu/drm/nouveau/dispnv50/crc.c @@ -0,0 +1,714 @@ +// SPDX-License-Identifier: MIT +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "nouveau_drv.h" +#include "core.h" +#include "head.h" +#include "wndw.h" +#include "handles.h" +#include "crc.h" + +static const char * const nv50_crc_sources[] = { + [NV50_CRC_SOURCE_NONE] = "none", + [NV50_CRC_SOURCE_AUTO] = "auto", + [NV50_CRC_SOURCE_RG] = "rg", + [NV50_CRC_SOURCE_OUTP_ACTIVE] = "outp-active", + [NV50_CRC_SOURCE_OUTP_COMPLETE] = "outp-complete", + [NV50_CRC_SOURCE_OUTP_INACTIVE] = "outp-inactive", +}; + +static int nv50_crc_parse_source(const char *buf, enum nv50_crc_source *s) +{ + int i; + + if (!buf) { + *s = NV50_CRC_SOURCE_NONE; + return 0; + } + + i = match_string(nv50_crc_sources, ARRAY_SIZE(nv50_crc_sources), buf); + if (i < 0) + return i; + + *s = i; + return 0; +} + +int +nv50_crc_verify_source(struct drm_crtc *crtc, const char *source_name, + size_t *values_cnt) +{ + struct nouveau_drm *drm = nouveau_drm(crtc->dev); + enum nv50_crc_source source; + + if (nv50_crc_parse_source(source_name, &source) < 0) { + NV_DEBUG(drm, "unknown source %s\n", source_name); + return -EINVAL; + } + + *values_cnt = 1; + return 0; +} + +const char *const *nv50_crc_get_sources(struct drm_crtc *crtc, size_t *count) +{ + *count = ARRAY_SIZE(nv50_crc_sources); + return nv50_crc_sources; +} + +static void +nv50_crc_program_ctx(struct nv50_head *head, + struct nv50_crc_notifier_ctx *ctx) +{ + struct nv50_disp *disp = nv50_disp(head->base.base.dev); + struct nv50_core *core = disp->core; + u32 interlock[NV50_DISP_INTERLOCK__SIZE] = { 0 }; + + core->func->crc->set_ctx(head, ctx); + core->func->update(core, interlock, false); +} + +static void nv50_crc_ctx_flip_work(struct kthread_work *base) +{ + struct drm_vblank_work *work = to_drm_vblank_work(base); + struct nv50_crc *crc = container_of(work, struct nv50_crc, flip_work); + struct nv50_head *head = container_of(crc, struct nv50_head, crc); + struct drm_crtc *crtc = &head->base.base; + struct nv50_disp *disp = nv50_disp(crtc->dev); + u8 new_idx = crc->ctx_idx ^ 1; + + /* + * We don't want to accidentally wait for longer then the vblank, so + * try again for the next vblank if we don't grab the lock + */ + if (!mutex_trylock(&disp->mutex)) { + DRM_DEV_DEBUG_KMS(crtc->dev->dev, + "Lock contended, delaying CRC ctx flip for head-%d\n", + head->base.index); + drm_vblank_work_schedule(work, + drm_crtc_vblank_count(crtc) + 1, + true); + return; + } + + DRM_DEV_DEBUG_KMS(crtc->dev->dev, + "Flipping notifier ctx for head %d (%d -> %d)\n", + drm_crtc_index(crtc), crc->ctx_idx, new_idx); + + nv50_crc_program_ctx(head, NULL); + nv50_crc_program_ctx(head, &crc->ctx[new_idx]); + mutex_unlock(&disp->mutex); + + spin_lock_irq(&crc->lock); + crc->ctx_changed = true; + spin_unlock_irq(&crc->lock); +} + +static inline void nv50_crc_reset_ctx(struct nv50_crc_notifier_ctx *ctx) +{ + memset_io(ctx->mem.object.map.ptr, 0, ctx->mem.object.map.size); +} + +static void +nv50_crc_get_entries(struct nv50_head *head, + const struct nv50_crc_func *func, + enum nv50_crc_source source) +{ + struct drm_crtc *crtc = &head->base.base; + struct nv50_crc *crc = &head->crc; + u32 output_crc; + + while (crc->entry_idx < func->num_entries) { + /* + * While Nvidia's documentation says CRCs are written on each + * subsequent vblank after being enabled, in practice they + * aren't written immediately. + */ + output_crc = func->get_entry(head, &crc->ctx[crc->ctx_idx], + source, crc->entry_idx); + if (!output_crc) + return; + + drm_crtc_add_crc_entry(crtc, true, crc->frame, &output_crc); + crc->frame++; + crc->entry_idx++; + } +} + +void nv50_crc_handle_vblank(struct nv50_head *head) +{ + struct drm_crtc *crtc = &head->base.base; + struct nv50_crc *crc = &head->crc; + const struct nv50_crc_func *func = + nv50_disp(head->base.base.dev)->core->func->crc; + struct nv50_crc_notifier_ctx *ctx; + bool need_reschedule = false; + + if (!func) + return; + + /* + * We don't lose events if we aren't able to report CRCs until the + * next vblank, so only report CRCs if the locks we need aren't + * contended to prevent missing an actual vblank event + */ + if (!spin_trylock(&crc->lock)) + return; + + if (!crc->src) + goto out; + + ctx = &crc->ctx[crc->ctx_idx]; + if (crc->ctx_changed && func->ctx_finished(head, ctx)) { + nv50_crc_get_entries(head, func, crc->src); + + crc->ctx_idx ^= 1; + crc->entry_idx = 0; + crc->ctx_changed = false; + + /* + * Unfortunately when notifier contexts are changed during CRC + * capture, we will inevitably lose the CRC entry for the + * frame where the hardware actually latched onto the first + * UPDATE. According to Nvidia's hardware engineers, there's + * no workaround for this. + * + * Now, we could try to be smart here and calculate the number + * of missed CRCs based on audit timestamps, but those were + * removed starting with volta. Since we always flush our + * updates back-to-back without waiting, we'll just be + * optimistic and assume we always miss exactly one frame. + */ + DRM_DEV_DEBUG_KMS(head->base.base.dev->dev, + "Notifier ctx flip for head-%d finished, lost CRC for frame %llu\n", + head->base.index, crc->frame); + crc->frame++; + + nv50_crc_reset_ctx(ctx); + need_reschedule = true; + } + + nv50_crc_get_entries(head, func, crc->src); + + if (need_reschedule) + drm_vblank_work_schedule(&crc->flip_work, + drm_crtc_vblank_count(crtc) + + crc->flip_threshold + - crc->entry_idx, + true); + +out: + spin_unlock(&crc->lock); +} + +static void nv50_crc_wait_ctx_finished(struct nv50_head *head, + const struct nv50_crc_func *func, + struct nv50_crc_notifier_ctx *ctx) +{ + struct drm_device *dev = head->base.base.dev; + struct nouveau_drm *drm = nouveau_drm(dev); + s64 ret; + + ret = nvif_msec(&drm->client.device, 50, + if (func->ctx_finished(head, ctx)) break;); + if (ret == -ETIMEDOUT) + NV_ERROR(drm, + "CRC notifier ctx for head %d not finished after 50ms\n", + head->base.index); + else if (ret) + NV_ATOMIC(drm, + "CRC notifier ctx for head-%d finished after %lldns\n", + head->base.index, ret); +} + +void nv50_crc_atomic_stop_reporting(struct drm_atomic_state *state) +{ + struct drm_crtc_state *crtc_state; + struct drm_crtc *crtc; + int i; + + for_each_new_crtc_in_state(state, crtc, crtc_state, i) { + struct nv50_head *head = nv50_head(crtc); + struct nv50_head_atom *asyh = nv50_head_atom(crtc_state); + struct nv50_crc *crc = &head->crc; + + if (!asyh->clr.crc) + continue; + + spin_lock_irq(&crc->lock); + crc->src = NV50_CRC_SOURCE_NONE; + spin_unlock_irq(&crc->lock); + + drm_crtc_vblank_put(crtc); + drm_vblank_work_cancel_sync(&crc->flip_work); + + NV_ATOMIC(nouveau_drm(crtc->dev), + "CRC reporting on vblank for head-%d disabled\n", + head->base.index); + + /* CRC generation is still enabled in hw, we'll just report + * any remaining CRC entries ourselves after it gets disabled + * in hardware + */ + } +} + +void nv50_crc_atomic_prepare_notifier_contexts(struct drm_atomic_state *state) +{ + const struct nv50_crc_func *func = + nv50_disp(state->dev)->core->func->crc; + struct drm_crtc_state *new_crtc_state; + struct drm_crtc *crtc; + int i; + + for_each_new_crtc_in_state(state, crtc, new_crtc_state, i) { + struct nv50_head *head = nv50_head(crtc); + struct nv50_head_atom *asyh = nv50_head_atom(new_crtc_state); + struct nv50_crc *crc = &head->crc; + struct nv50_crc_notifier_ctx *ctx = &crc->ctx[crc->ctx_idx]; + int i; + + if (asyh->clr.crc && asyh->crc.src) { + if (crc->ctx_changed) { + nv50_crc_wait_ctx_finished(head, func, ctx); + ctx = &crc->ctx[crc->ctx_idx ^ 1]; + } + nv50_crc_wait_ctx_finished(head, func, ctx); + } + + if (asyh->set.crc) { + crc->entry_idx = 0; + crc->ctx_changed = false; + for (i = 0; i < ARRAY_SIZE(crc->ctx); i++) + nv50_crc_reset_ctx(&crc->ctx[i]); + } + } +} + +void nv50_crc_atomic_start_reporting(struct drm_atomic_state *state) +{ + struct drm_crtc_state *crtc_state; + struct drm_crtc *crtc; + int i; + + for_each_new_crtc_in_state(state, crtc, crtc_state, i) { + struct nv50_head *head = nv50_head(crtc); + struct nv50_head_atom *asyh = nv50_head_atom(crtc_state); + struct nv50_crc *crc = &head->crc; + u64 vbl_count; + + if (!asyh->set.crc) + continue; + + drm_crtc_vblank_get(crtc); + + spin_lock_irq(&crc->lock); + vbl_count = drm_crtc_vblank_count(crtc); + crc->frame = vbl_count; + crc->src = asyh->crc.src; + drm_vblank_work_schedule(&crc->flip_work, + vbl_count + crc->flip_threshold, + true); + spin_unlock_irq(&crc->lock); + + NV_ATOMIC(nouveau_drm(crtc->dev), + "CRC reporting on vblank for head-%d enabled\n", + head->base.index); + } +} + +int nv50_crc_atomic_check(struct nv50_head *head, + struct nv50_head_atom *asyh, + struct nv50_head_atom *armh) +{ + struct drm_atomic_state *state = asyh->state.state; + struct drm_device *dev = head->base.base.dev; + struct nv50_atom *atom = nv50_atom(state); + struct nv50_disp *disp = nv50_disp(dev); + struct drm_encoder *encoder; + struct nv50_outp_atom *outp_atom; + bool changed = armh->crc.src != asyh->crc.src; + + if (!armh->crc.src && !asyh->crc.src) { + asyh->set.crc = false; + asyh->clr.crc = false; + return 0; + } + + /* While we don't care about entry tags, Volta+ hw always needs the + * controlling wndw channel programmed to a wndw that's owned by our + * head + */ + if (asyh->crc.src && disp->disp->object.oclass >= GV100_DISP && + !(BIT(asyh->crc.wndw) & asyh->wndw.owned)) { + if (!asyh->wndw.owned) { + /* TODO: once we support flexible channel ownership, + * we should write some code here to handle attempting + * to "steal" a plane: e.g. take a plane that is + * currently not-visible and owned by another head, + * and reassign it to this head. If we fail to do so, + * we shuld reject the mode outright as CRC capture + * then becomes impossible. + */ + NV_ATOMIC(nouveau_drm(dev), + "No available wndws for CRC readback\n"); + return -EINVAL; + } + asyh->crc.wndw = ffs(asyh->wndw.owned) - 1; + } + + if (drm_atomic_crtc_needs_modeset(&asyh->state) || changed || + armh->crc.wndw != asyh->crc.wndw) { + asyh->clr.crc = armh->crc.src && armh->state.active; + asyh->set.crc = asyh->crc.src && asyh->state.active; + if (changed) + asyh->set.or |= armh->or.crc_raster != + asyh->or.crc_raster; + + /* + * If we're reprogramming our OR, we need to flush the CRC + * disable first + */ + if (asyh->clr.crc) { + encoder = nv50_head_atom_get_encoder(armh); + + list_for_each_entry(outp_atom, &atom->outp, head) { + if (outp_atom->encoder == encoder) { + if (outp_atom->set.mask) + atom->flush_disable = true; + break; + } + } + } + } else { + asyh->set.crc = false; + asyh->clr.crc = false; + } + + return 0; +} + +static enum nv50_crc_source_type +nv50_crc_source_type(struct nouveau_encoder *outp, + enum nv50_crc_source source) +{ + struct dcb_output *dcbe = outp->dcb; + + switch (source) { + case NV50_CRC_SOURCE_NONE: return NV50_CRC_SOURCE_TYPE_NONE; + case NV50_CRC_SOURCE_RG: return NV50_CRC_SOURCE_TYPE_RG; + default: break; + } + + if (dcbe->location != DCB_LOC_ON_CHIP) + return NV50_CRC_SOURCE_TYPE_PIOR; + + switch (dcbe->type) { + case DCB_OUTPUT_DP: return NV50_CRC_SOURCE_TYPE_SF; + case DCB_OUTPUT_ANALOG: return NV50_CRC_SOURCE_TYPE_DAC; + default: return NV50_CRC_SOURCE_TYPE_SOR; + } +} + +void nv50_crc_atomic_set(struct nv50_head *head, + struct nv50_head_atom *asyh) +{ + struct drm_crtc *crtc = &head->base.base; + struct drm_device *dev = crtc->dev; + struct nv50_crc *crc = &head->crc; + const struct nv50_crc_func *func = nv50_disp(dev)->core->func->crc; + struct nouveau_encoder *outp = + nv50_real_outp(nv50_head_atom_get_encoder(asyh)); + + func->set_src(head, outp->or, + nv50_crc_source_type(outp, asyh->crc.src), + &crc->ctx[crc->ctx_idx], asyh->crc.wndw); +} + +void nv50_crc_atomic_clr(struct nv50_head *head) +{ + const struct nv50_crc_func *func = + nv50_disp(head->base.base.dev)->core->func->crc; + + func->set_src(head, 0, NV50_CRC_SOURCE_TYPE_NONE, NULL, 0); +} + +#define NV50_CRC_RASTER_ACTIVE 0 +#define NV50_CRC_RASTER_COMPLETE 1 +#define NV50_CRC_RASTER_INACTIVE 2 + +static inline int +nv50_crc_raster_type(enum nv50_crc_source source) +{ + switch (source) { + case NV50_CRC_SOURCE_NONE: + case NV50_CRC_SOURCE_AUTO: + case NV50_CRC_SOURCE_RG: + case NV50_CRC_SOURCE_OUTP_ACTIVE: + return NV50_CRC_RASTER_ACTIVE; + case NV50_CRC_SOURCE_OUTP_COMPLETE: + return NV50_CRC_RASTER_COMPLETE; + case NV50_CRC_SOURCE_OUTP_INACTIVE: + return NV50_CRC_RASTER_INACTIVE; + } + + return 0; +} + +/* We handle mapping the memory for CRC notifiers ourselves, since each + * notifier needs it's own handle + */ +static inline int +nv50_crc_ctx_init(struct nv50_head *head, struct nvif_mmu *mmu, + struct nv50_crc_notifier_ctx *ctx, size_t len, int idx) +{ + struct nv50_core *core = nv50_disp(head->base.base.dev)->core; + int ret; + + ret = nvif_mem_init_map(mmu, NVIF_MEM_VRAM, len, &ctx->mem); + if (ret) + return ret; + + ret = nvif_object_init(&core->chan.base.user, + NV50_DISP_HANDLE_CRC_CTX(head, idx), + NV_DMA_IN_MEMORY, + &(struct nv_dma_v0) { + .target = NV_DMA_V0_TARGET_VRAM, + .access = NV_DMA_V0_ACCESS_RDWR, + .start = ctx->mem.addr, + .limit = ctx->mem.addr + + ctx->mem.size - 1, + }, sizeof(struct nv_dma_v0), + &ctx->ntfy); + if (ret) + goto fail_fini; + + return 0; + +fail_fini: + nvif_mem_fini(&ctx->mem); + return ret; +} + +static inline void +nv50_crc_ctx_fini(struct nv50_crc_notifier_ctx *ctx) +{ + nvif_object_fini(&ctx->ntfy); + nvif_mem_fini(&ctx->mem); +} + +int nv50_crc_set_source(struct drm_crtc *crtc, const char *source_str) +{ + struct drm_device *dev = crtc->dev; + struct drm_atomic_state *state; + struct drm_modeset_acquire_ctx ctx; + struct nv50_head *head = nv50_head(crtc); + struct nv50_crc *crc = &head->crc; + const struct nv50_crc_func *func = nv50_disp(dev)->core->func->crc; + struct nvif_mmu *mmu = &nouveau_drm(dev)->client.mmu; + struct nv50_head_atom *asyh; + struct drm_crtc_state *crtc_state; + enum nv50_crc_source source; + int ret = 0, ctx_flags = 0, i; + + ret = nv50_crc_parse_source(source_str, &source); + if (ret) + return ret; + + /* + * Since we don't want the user to accidentally interrupt us as we're + * disabling CRCs + */ + if (source) + ctx_flags |= DRM_MODESET_ACQUIRE_INTERRUPTIBLE; + drm_modeset_acquire_init(&ctx, ctx_flags); + + state = drm_atomic_state_alloc(dev); + if (!state) { + ret = -ENOMEM; + goto out_acquire_fini; + } + state->acquire_ctx = &ctx; + + if (source) { + for (i = 0; i < ARRAY_SIZE(head->crc.ctx); i++) { + ret = nv50_crc_ctx_init(head, mmu, &crc->ctx[i], + func->notifier_len, i); + if (ret) + goto out_ctx_fini; + } + } + +retry: + crtc_state = drm_atomic_get_crtc_state(state, &head->base.base); + if (IS_ERR(crtc_state)) { + ret = PTR_ERR(crtc_state); + if (ret == -EDEADLK) + goto deadlock; + else if (ret) + goto out_drop_locks; + } + asyh = nv50_head_atom(crtc_state); + asyh->crc.src = source; + asyh->or.crc_raster = nv50_crc_raster_type(source); + + ret = drm_atomic_commit(state); + if (ret == -EDEADLK) + goto deadlock; + else if (ret) + goto out_drop_locks; + + if (!source) { + /* + * If the user specified a custom flip threshold through + * debugfs, reset it + */ + crc->flip_threshold = func->flip_threshold; + } + +out_drop_locks: + drm_modeset_drop_locks(&ctx); +out_ctx_fini: + if (!source || ret) { + for (i = 0; i < ARRAY_SIZE(crc->ctx); i++) + nv50_crc_ctx_fini(&crc->ctx[i]); + } + drm_atomic_state_put(state); +out_acquire_fini: + drm_modeset_acquire_fini(&ctx); + return ret; + +deadlock: + drm_atomic_state_clear(state); + drm_modeset_backoff(&ctx); + goto retry; +} + +static int +nv50_crc_debugfs_flip_threshold_get(struct seq_file *m, void *data) +{ + struct nv50_head *head = m->private; + struct drm_crtc *crtc = &head->base.base; + struct nv50_crc *crc = &head->crc; + int ret; + + ret = drm_modeset_lock_single_interruptible(&crtc->mutex); + if (ret) + return ret; + + seq_printf(m, "%d\n", crc->flip_threshold); + + drm_modeset_unlock(&crtc->mutex); + return ret; +} + +static int +nv50_crc_debugfs_flip_threshold_open(struct inode *inode, struct file *file) +{ + return single_open(file, nv50_crc_debugfs_flip_threshold_get, + inode->i_private); +} + +static ssize_t +nv50_crc_debugfs_flip_threshold_set(struct file *file, + const char __user *ubuf, size_t len, + loff_t *offp) +{ + struct seq_file *m = file->private_data; + struct nv50_head *head = m->private; + struct nv50_head_atom *armh; + struct drm_crtc *crtc = &head->base.base; + struct nouveau_drm *drm = nouveau_drm(crtc->dev); + struct nv50_crc *crc = &head->crc; + const struct nv50_crc_func *func = + nv50_disp(crtc->dev)->core->func->crc; + int value, ret; + + ret = kstrtoint_from_user(ubuf, len, 10, &value); + if (ret) + return ret; + + if (value > func->flip_threshold) + return -EINVAL; + else if (value == -1) + value = func->flip_threshold; + else if (value < -1) + return -EINVAL; + + ret = drm_modeset_lock_single_interruptible(&crtc->mutex); + if (ret) + return ret; + + armh = nv50_head_atom(crtc->state); + if (armh->crc.src) { + ret = -EBUSY; + goto out; + } + + NV_DEBUG(drm, + "Changing CRC flip threshold for next capture on head-%d to %d\n", + head->base.index, value); + crc->flip_threshold = value; + ret = len; + +out: + drm_modeset_unlock(&crtc->mutex); + return ret; +} + +static const struct file_operations nv50_crc_flip_threshold_fops = { + .owner = THIS_MODULE, + .open = nv50_crc_debugfs_flip_threshold_open, + .read = seq_read, + .write = nv50_crc_debugfs_flip_threshold_set, +}; + +int nv50_head_crc_late_register(struct nv50_head *head) +{ + struct drm_crtc *crtc = &head->base.base; + const struct nv50_crc_func *func = + nv50_disp(crtc->dev)->core->func->crc; + struct dentry *root; + + if (!func || !crtc->debugfs_entry) + return 0; + + root = debugfs_create_dir("nv_crc", crtc->debugfs_entry); + debugfs_create_file("flip_threshold", 0644, root, head, + &nv50_crc_flip_threshold_fops); + + return 0; +} + +static inline void +nv50_crc_init_head(struct nv50_disp *disp, const struct nv50_crc_func *func, + struct nv50_head *head) +{ + struct nv50_crc *crc = &head->crc; + + crc->flip_threshold = func->flip_threshold; + spin_lock_init(&crc->lock); + drm_vblank_work_init(&crc->flip_work, &head->base.base, + nv50_crc_ctx_flip_work); +} + +void nv50_crc_init(struct drm_device *dev) +{ + struct nv50_disp *disp = nv50_disp(dev); + struct drm_crtc *crtc; + const struct nv50_crc_func *func = disp->core->func->crc; + + if (!func) + return; + + drm_for_each_crtc(crtc, dev) + nv50_crc_init_head(disp, func, nv50_head(crtc)); +} diff --git a/drivers/gpu/drm/nouveau/dispnv50/crc.h b/drivers/gpu/drm/nouveau/dispnv50/crc.h new file mode 100644 index 000000000000..8e166260c200 --- /dev/null +++ b/drivers/gpu/drm/nouveau/dispnv50/crc.h @@ -0,0 +1,125 @@ +/* SPDX-License-Identifier: MIT */ +#ifndef __NV50_CRC_H__ +#define __NV50_CRC_H__ + +#include +#include +#include + +#include +#include +#include "nouveau_encoder.h" + +struct nv50_disp; +struct nv50_head; + +#if IS_ENABLED(CONFIG_DEBUG_FS) +enum nv50_crc_source { + NV50_CRC_SOURCE_NONE = 0, + NV50_CRC_SOURCE_AUTO, + NV50_CRC_SOURCE_RG, + NV50_CRC_SOURCE_OUTP_ACTIVE, + NV50_CRC_SOURCE_OUTP_COMPLETE, + NV50_CRC_SOURCE_OUTP_INACTIVE, +}; + +/* RG -> SF (DP only) + * -> SOR + * -> PIOR + * -> DAC + */ +enum nv50_crc_source_type { + NV50_CRC_SOURCE_TYPE_NONE = 0, + NV50_CRC_SOURCE_TYPE_SOR, + NV50_CRC_SOURCE_TYPE_PIOR, + NV50_CRC_SOURCE_TYPE_DAC, + NV50_CRC_SOURCE_TYPE_RG, + NV50_CRC_SOURCE_TYPE_SF, +}; + +struct nv50_crc_notifier_ctx { + struct nvif_mem mem; + struct nvif_object ntfy; +}; + +struct nv50_crc_atom { + enum nv50_crc_source src; + /* Only used for gv100+ */ + u8 wndw : 4; +}; + +struct nv50_crc_func { + void (*set_src)(struct nv50_head *, int or, enum nv50_crc_source_type, + struct nv50_crc_notifier_ctx *, u32 wndw); + void (*set_ctx)(struct nv50_head *, struct nv50_crc_notifier_ctx *); + u32 (*get_entry)(struct nv50_head *, struct nv50_crc_notifier_ctx *, + enum nv50_crc_source, int idx); + bool (*ctx_finished)(struct nv50_head *, + struct nv50_crc_notifier_ctx *); + short flip_threshold; + short num_entries; + size_t notifier_len; +}; + +struct nv50_crc { + spinlock_t lock; + struct nv50_crc_notifier_ctx ctx[2]; + struct drm_vblank_work flip_work; + enum nv50_crc_source src; + + u64 frame; + short entry_idx; + short flip_threshold; + u8 ctx_idx : 1; + bool ctx_changed : 1; +}; + +void nv50_crc_init(struct drm_device *dev); +int nv50_head_crc_late_register(struct nv50_head *); +void nv50_crc_handle_vblank(struct nv50_head *head); + +int nv50_crc_verify_source(struct drm_crtc *, const char *, size_t *); +const char *const *nv50_crc_get_sources(struct drm_crtc *, size_t *); +int nv50_crc_set_source(struct drm_crtc *, const char *); + +int nv50_crc_atomic_check(struct nv50_head *, struct nv50_head_atom *, + struct nv50_head_atom *); +void nv50_crc_atomic_stop_reporting(struct drm_atomic_state *); +void nv50_crc_atomic_prepare_notifier_contexts(struct drm_atomic_state *); +void nv50_crc_atomic_start_reporting(struct drm_atomic_state *); +void nv50_crc_atomic_set(struct nv50_head *, struct nv50_head_atom *); +void nv50_crc_atomic_clr(struct nv50_head *); + +extern const struct nv50_crc_func crc907d; +extern const struct nv50_crc_func crcc37d; + +#else /* IS_ENABLED(CONFIG_DEBUG_FS) */ +struct nv50_crc {}; +struct nv50_crc_func {}; +struct nv50_crc_atom {}; + +#define nv50_crc_verify_source NULL +#define nv50_crc_get_sources NULL +#define nv50_crc_set_source NULL + +static inline void nv50_crc_init(struct drm_device *dev) {} +static inline int nv50_head_crc_late_register(struct nv50_head *) {} +static inline void +nv50_crc_handle_vblank(struct nv50_head *head) { return 0; } + +static inline int +nv50_crc_atomic_check(struct nv50_head *, struct nv50_head_atom *, + struct nv50_head_atom *) {} +static inline void +nv50_crc_atomic_stop_reporting(struct drm_atomic_state *) {} +static inline void +nv50_crc_atomic_prepare_notifier_contexts(struct drm_atomic_state *) {} +static inline void +nv50_crc_atomic_start_reporting(struct drm_atomic_state *) {} +static inline void +nv50_crc_atomic_set(struct nv50_head *, struct nv50_head_atom *) {} +static inline void +nv50_crc_atomic_clr(struct nv50_head *) {} + +#endif /* IS_ENABLED(CONFIG_DEBUG_FS) */ +#endif /* !__NV50_CRC_H__ */ diff --git a/drivers/gpu/drm/nouveau/dispnv50/crc907d.c b/drivers/gpu/drm/nouveau/dispnv50/crc907d.c new file mode 100644 index 000000000000..92e907de7645 --- /dev/null +++ b/drivers/gpu/drm/nouveau/dispnv50/crc907d.c @@ -0,0 +1,139 @@ +// SPDX-License-Identifier: MIT +#include + +#include "crc.h" +#include "core.h" +#include "disp.h" +#include "head.h" + +#define CRC907D_MAX_ENTRIES 255 + +struct crc907d_notifier { + u32 status; + u32 :32; /* reserved */ + struct crc907d_entry { + u32 status; + u32 compositor_crc; + u32 output_crc[2]; + } entries[CRC907D_MAX_ENTRIES]; +} __packed; + +static void +crc907d_set_src(struct nv50_head *head, int or, + enum nv50_crc_source_type source, + struct nv50_crc_notifier_ctx *ctx, u32 wndw) +{ + struct drm_crtc *crtc = &head->base.base; + struct nv50_dmac *core = &nv50_disp(head->base.base.dev)->core->chan; + const u32 hoff = head->base.index * 0x300; + u32 *push; + u32 crc_args = 0xfff00000; + + switch (source) { + case NV50_CRC_SOURCE_TYPE_SOR: + crc_args |= (0x00000f0f + or * 16) << 8; + break; + case NV50_CRC_SOURCE_TYPE_PIOR: + crc_args |= (0x000000ff + or * 256) << 8; + break; + case NV50_CRC_SOURCE_TYPE_DAC: + crc_args |= (0x00000ff0 + or) << 8; + break; + case NV50_CRC_SOURCE_TYPE_RG: + crc_args |= (0x00000ff8 + drm_crtc_index(crtc)) << 8; + break; + case NV50_CRC_SOURCE_TYPE_SF: + crc_args |= (0x00000f8f + drm_crtc_index(crtc) * 16) << 8; + break; + case NV50_CRC_SOURCE_NONE: + crc_args |= 0x000fff00; + break; + } + + push = evo_wait(core, 4); + if (!push) + return; + + if (source) { + evo_mthd(push, 0x0438 + hoff, 1); + evo_data(push, ctx->ntfy.handle); + evo_mthd(push, 0x0430 + hoff, 1); + evo_data(push, crc_args); + } else { + evo_mthd(push, 0x0430 + hoff, 1); + evo_data(push, crc_args); + evo_mthd(push, 0x0438 + hoff, 1); + evo_data(push, 0); + } + evo_kick(push, core); +} + +static void crc907d_set_ctx(struct nv50_head *head, + struct nv50_crc_notifier_ctx *ctx) +{ + struct nv50_dmac *core = &nv50_disp(head->base.base.dev)->core->chan; + u32 *push = evo_wait(core, 2); + + if (!push) + return; + + evo_mthd(push, 0x0438 + (head->base.index * 0x300), 1); + evo_data(push, ctx ? ctx->ntfy.handle : 0); + evo_kick(push, core); +} + +static u32 crc907d_get_entry(struct nv50_head *head, + struct nv50_crc_notifier_ctx *ctx, + enum nv50_crc_source source, int idx) +{ + struct crc907d_notifier __iomem *notifier = ctx->mem.object.map.ptr; + + return ioread32_native(¬ifier->entries[idx].output_crc[0]); +} + +static bool crc907d_ctx_finished(struct nv50_head *head, + struct nv50_crc_notifier_ctx *ctx) +{ + struct nouveau_drm *drm = nouveau_drm(head->base.base.dev); + struct crc907d_notifier __iomem *notifier = ctx->mem.object.map.ptr; + const u32 status = ioread32_native(¬ifier->status); + const u32 overflow = status & 0x0000003e; + + if (!(status & 0x00000001)) + return false; + + if (overflow) { + const char *engine = NULL; + + switch (overflow) { + case 0x00000004: engine = "DSI"; break; + case 0x00000008: engine = "Compositor"; break; + case 0x00000010: engine = "CRC output 1"; break; + case 0x00000020: engine = "CRC output 2"; break; + } + + if (engine) + NV_ERROR(drm, + "CRC notifier context for head %d overflowed on %s: %x\n", + head->base.index, engine, status); + else + NV_ERROR(drm, + "CRC notifier context for head %d overflowed: %x\n", + head->base.index, status); + } + + NV_DEBUG(drm, "Head %d CRC context status: %x\n", + head->base.index, status); + + return true; +} + +const struct nv50_crc_func crc907d = { + .set_src = crc907d_set_src, + .set_ctx = crc907d_set_ctx, + .get_entry = crc907d_get_entry, + .ctx_finished = crc907d_ctx_finished, + .flip_threshold = CRC907D_MAX_ENTRIES - 10, + .num_entries = CRC907D_MAX_ENTRIES, + .notifier_len = sizeof(struct crc907d_notifier), +}; diff --git a/drivers/gpu/drm/nouveau/dispnv50/crcc37d.c b/drivers/gpu/drm/nouveau/dispnv50/crcc37d.c new file mode 100644 index 000000000000..940cefd5517d --- /dev/null +++ b/drivers/gpu/drm/nouveau/dispnv50/crcc37d.c @@ -0,0 +1,153 @@ +// SPDX-License-Identifier: MIT +#include + +#include "crc.h" +#include "core.h" +#include "disp.h" +#include "head.h" + +#define CRCC37D_MAX_ENTRIES 2047 + +struct crcc37d_notifier { + u32 status; + + /* reserved */ + u32 :32; + u32 :32; + u32 :32; + u32 :32; + u32 :32; + u32 :32; + u32 :32; + + struct crcc37d_entry { + u32 status[2]; + u32 :32; /* reserved */ + u32 compositor_crc; + u32 rg_crc; + u32 output_crc[2]; + u32 :32; /* reserved */ + } entries[CRCC37D_MAX_ENTRIES]; +} __packed; + +static void +crcc37d_set_src(struct nv50_head *head, int or, + enum nv50_crc_source_type source, + struct nv50_crc_notifier_ctx *ctx, u32 wndw) +{ + struct nv50_dmac *core = &nv50_disp(head->base.base.dev)->core->chan; + const u32 hoff = head->base.index * 0x400; + u32 *push; + u32 crc_args; + + switch (source) { + case NV50_CRC_SOURCE_TYPE_SOR: + crc_args = (0x00000050 + or) << 12; + break; + case NV50_CRC_SOURCE_TYPE_PIOR: + crc_args = (0x00000060 + or) << 12; + break; + case NV50_CRC_SOURCE_TYPE_SF: + crc_args = 0x00000030 << 12; + break; + default: + crc_args = 0; + break; + } + + push = evo_wait(core, 4); + if (!push) + return; + + if (source) { + evo_mthd(push, 0x2180 + hoff, 1); + evo_data(push, ctx->ntfy.handle); + evo_mthd(push, 0x2184 + hoff, 1); + evo_data(push, crc_args | wndw); + } else { + evo_mthd(push, 0x2184 + hoff, 1); + evo_data(push, 0); + evo_mthd(push, 0x2180 + hoff, 1); + evo_data(push, 0); + } + + evo_kick(push, core); +} + +static void crcc37d_set_ctx(struct nv50_head *head, + struct nv50_crc_notifier_ctx *ctx) +{ + struct nv50_dmac *core = &nv50_disp(head->base.base.dev)->core->chan; + u32 *push = evo_wait(core, 2); + + if (!push) + return; + + evo_mthd(push, 0x2180 + (head->base.index * 0x400), 1); + evo_data(push, ctx ? ctx->ntfy.handle : 0); + evo_kick(push, core); +} + +static u32 crcc37d_get_entry(struct nv50_head *head, + struct nv50_crc_notifier_ctx *ctx, + enum nv50_crc_source source, int idx) +{ + struct crcc37d_notifier __iomem *notifier = ctx->mem.object.map.ptr; + struct crcc37d_entry __iomem *entry = ¬ifier->entries[idx]; + u32 __iomem *crc_addr; + + if (source == NV50_CRC_SOURCE_RG) + crc_addr = &entry->rg_crc; + else + crc_addr = &entry->output_crc[0]; + + return ioread32_native(crc_addr); +} + +static bool crcc37d_ctx_finished(struct nv50_head *head, + struct nv50_crc_notifier_ctx *ctx) +{ + struct nouveau_drm *drm = nouveau_drm(head->base.base.dev); + struct crcc37d_notifier __iomem *notifier = ctx->mem.object.map.ptr; + const u32 status = ioread32_native(¬ifier->status); + const u32 overflow = status & 0x0000007e; + + if (!(status & 0x00000001)) + return false; + + if (overflow) { + const char *engine = NULL; + + switch (overflow) { + case 0x00000004: engine = "Front End"; break; + case 0x00000008: engine = "Compositor"; break; + case 0x00000010: engine = "RG"; break; + case 0x00000020: engine = "CRC output 1"; break; + case 0x00000040: engine = "CRC output 2"; break; + } + + if (engine) + NV_ERROR(drm, + "CRC notifier context for head %d overflowed on %s: %x\n", + head->base.index, engine, status); + else + NV_ERROR(drm, + "CRC notifier context for head %d overflowed: %x\n", + head->base.index, status); + } + + NV_DEBUG(drm, "Head %d CRC context status: %x\n", + head->base.index, status); + + return true; +} + +const struct nv50_crc_func crcc37d = { + .set_src = crcc37d_set_src, + .set_ctx = crcc37d_set_ctx, + .get_entry = crcc37d_get_entry, + .ctx_finished = crcc37d_ctx_finished, + .flip_threshold = CRCC37D_MAX_ENTRIES - 30, + .num_entries = CRCC37D_MAX_ENTRIES, + .notifier_len = sizeof(struct crcc37d_notifier), +}; diff --git a/drivers/gpu/drm/nouveau/dispnv50/disp.c b/drivers/gpu/drm/nouveau/dispnv50/disp.c index bf7ba1e1c0f7..9cb06d6d6c3f 100644 --- a/drivers/gpu/drm/nouveau/dispnv50/disp.c +++ b/drivers/gpu/drm/nouveau/dispnv50/disp.c @@ -783,6 +783,19 @@ struct nv50_msto { bool disabled; }; +struct nouveau_encoder *nv50_real_outp(struct drm_encoder *encoder) +{ + struct nv50_msto *msto; + + if (encoder->encoder_type != DRM_MODE_ENCODER_DPMST) + return nouveau_encoder(encoder); + + msto = nv50_msto(encoder); + if (!msto->mstc) + return NULL; + return msto->mstc->mstm->outp; +} + static struct drm_dp_payload * nv50_msto_payload(struct nv50_msto *msto) { @@ -1932,6 +1945,7 @@ nv50_disp_atomic_commit_tail(struct drm_atomic_state *state) int i; NV_ATOMIC(drm, "commit %d %d\n", atom->lock_core, atom->flush_disable); + nv50_crc_atomic_stop_reporting(state); drm_atomic_helper_wait_for_fences(dev, state, false); drm_atomic_helper_wait_for_dependencies(state); drm_atomic_helper_update_legacy_modeset_state(dev, state); @@ -2002,6 +2016,8 @@ nv50_disp_atomic_commit_tail(struct drm_atomic_state *state) } } + nv50_crc_atomic_prepare_notifier_contexts(state); + /* Update output path(s). */ list_for_each_entry_safe(outp, outt, &atom->outp, head) { const struct drm_encoder_helper_funcs *help; @@ -2115,6 +2131,7 @@ nv50_disp_atomic_commit_tail(struct drm_atomic_state *state) } } + nv50_crc_atomic_start_reporting(state); drm_atomic_helper_commit_hw_done(state); drm_atomic_helper_cleanup_planes(dev, state); drm_atomic_helper_commit_cleanup_done(state); diff --git a/drivers/gpu/drm/nouveau/dispnv50/disp.h b/drivers/gpu/drm/nouveau/dispnv50/disp.h index c7b72fa85099..1968c6921f9e 100644 --- a/drivers/gpu/drm/nouveau/dispnv50/disp.h +++ b/drivers/gpu/drm/nouveau/dispnv50/disp.h @@ -1,10 +1,12 @@ #ifndef __NV50_KMS_H__ #define __NV50_KMS_H__ +#include #include #include "nouveau_display.h" struct nv50_msto; +struct nouveau_encoder; struct nv50_disp { struct nvif_disp *disp; @@ -90,6 +92,14 @@ int nv50_dmac_create(struct nvif_device *device, struct nvif_object *disp, u64 syncbuf, struct nv50_dmac *dmac); void nv50_dmac_destroy(struct nv50_dmac *); +/* + * For normal encoders this just returns the encoder. For active MST encoders, + * this returns the real outp that's driving displays on the topology. + * Inactive MST encoders return NULL, since they would have no real outp to + * return anyway. + */ +struct nouveau_encoder *nv50_real_outp(struct drm_encoder *encoder); + u32 *evo_wait(struct nv50_dmac *, int nr); void evo_kick(u32 *, struct nv50_dmac *); diff --git a/drivers/gpu/drm/nouveau/dispnv50/handles.h b/drivers/gpu/drm/nouveau/dispnv50/handles.h index e3a62c7a0d08..a97a7bd29243 100644 --- a/drivers/gpu/drm/nouveau/dispnv50/handles.h +++ b/drivers/gpu/drm/nouveau/dispnv50/handles.h @@ -11,5 +11,6 @@ #define NV50_DISP_HANDLE_VRAM 0xf0000001 #define NV50_DISP_HANDLE_WNDW_CTX(kind) (0xfb000000 | kind) +#define NV50_DISP_HANDLE_CRC_CTX(head, i) (0xfc000000 | head->base.index << 1 | i) #endif /* !__NV50_KMS_HANDLES_H__ */ diff --git a/drivers/gpu/drm/nouveau/dispnv50/head.c b/drivers/gpu/drm/nouveau/dispnv50/head.c index ed6819519f6d..ea3088a47065 100644 --- a/drivers/gpu/drm/nouveau/dispnv50/head.c +++ b/drivers/gpu/drm/nouveau/dispnv50/head.c @@ -24,13 +24,17 @@ #include "core.h" #include "curs.h" #include "ovly.h" +#include "crc.h" #include +#include +#include #include #include #include #include "nouveau_connector.h" + void nv50_head_flush_clr(struct nv50_head *head, struct nv50_head_atom *asyh, bool flush) @@ -38,6 +42,7 @@ nv50_head_flush_clr(struct nv50_head *head, union nv50_head_atom_mask clr = { .mask = asyh->clr.mask & ~(flush ? 0 : asyh->set.mask), }; + if (clr.crc) nv50_crc_atomic_clr(head); if (clr.olut) head->func->olut_clr(head); if (clr.core) head->func->core_clr(head); if (clr.curs) head->func->curs_clr(head); @@ -61,6 +66,7 @@ nv50_head_flush_set(struct nv50_head *head, struct nv50_head_atom *asyh) if (asyh->set.ovly ) head->func->ovly (head, asyh); if (asyh->set.dither ) head->func->dither (head, asyh); if (asyh->set.procamp) head->func->procamp (head, asyh); + if (asyh->set.crc ) nv50_crc_atomic_set (head, asyh); if (asyh->set.or ) head->func->or (head, asyh); } @@ -313,7 +319,7 @@ nv50_head_atomic_check(struct drm_crtc *crtc, struct drm_crtc_state *state) struct nouveau_conn_atom *asyc = NULL; struct drm_connector_state *conns; struct drm_connector *conn; - int i; + int i, ret; NV_ATOMIC(drm, "%s atomic_check %d\n", crtc->name, asyh->state.active); if (asyh->state.active) { @@ -408,6 +414,10 @@ nv50_head_atomic_check(struct drm_crtc *crtc, struct drm_crtc_state *state) asyh->set.curs = asyh->curs.visible; } + ret = nv50_crc_atomic_check(head, asyh, armh); + if (ret) + return ret; + if (asyh->clr.mask || asyh->set.mask) nv50_atom(asyh->state.state)->lock_core = true; return 0; @@ -446,6 +456,7 @@ nv50_head_atomic_duplicate_state(struct drm_crtc *crtc) asyh->ovly = armh->ovly; asyh->dither = armh->dither; asyh->procamp = armh->procamp; + asyh->crc = armh->crc; asyh->or = armh->or; asyh->dp = armh->dp; asyh->clr.mask = 0; @@ -467,10 +478,18 @@ nv50_head_reset(struct drm_crtc *crtc) __drm_atomic_helper_crtc_reset(crtc, &asyh->state); } +static int +nv50_head_late_register(struct drm_crtc *crtc) +{ + return nv50_head_crc_late_register(nv50_head(crtc)); +} + static void nv50_head_destroy(struct drm_crtc *crtc) { struct nv50_head *head = nv50_head(crtc); + + nvif_notify_fini(&head->base.vblank); nv50_lut_fini(&head->olut); drm_crtc_cleanup(crtc); kfree(head); @@ -488,8 +507,38 @@ nv50_head_func = { .enable_vblank = nouveau_display_vblank_enable, .disable_vblank = nouveau_display_vblank_disable, .get_vblank_timestamp = drm_crtc_vblank_helper_get_vblank_timestamp, + .late_register = nv50_head_late_register, }; +static const struct drm_crtc_funcs +nvd9_head_func = { + .reset = nv50_head_reset, + .gamma_set = drm_atomic_helper_legacy_gamma_set, + .destroy = nv50_head_destroy, + .set_config = drm_atomic_helper_set_config, + .page_flip = drm_atomic_helper_page_flip, + .atomic_duplicate_state = nv50_head_atomic_duplicate_state, + .atomic_destroy_state = nv50_head_atomic_destroy_state, + .enable_vblank = nouveau_display_vblank_enable, + .disable_vblank = nouveau_display_vblank_disable, + .get_vblank_timestamp = drm_crtc_vblank_helper_get_vblank_timestamp, + .verify_crc_source = nv50_crc_verify_source, + .get_crc_sources = nv50_crc_get_sources, + .set_crc_source = nv50_crc_set_source, + .late_register = nv50_head_late_register, +}; + +static int nv50_head_vblank_handler(struct nvif_notify *notify) +{ + struct nouveau_crtc *nv_crtc = + container_of(notify, struct nouveau_crtc, vblank); + + if (drm_crtc_handle_vblank(&nv_crtc->base)) + nv50_crc_handle_vblank(nv50_head(&nv_crtc->base)); + + return NVIF_NOTIFY_KEEP; +} + struct nv50_head * nv50_head_create(struct drm_device *dev, int index) { @@ -497,7 +546,9 @@ nv50_head_create(struct drm_device *dev, int index) struct nv50_disp *disp = nv50_disp(dev); struct nv50_head *head; struct nv50_wndw *base, *ovly, *curs; + struct nouveau_crtc *nv_crtc; struct drm_crtc *crtc; + const struct drm_crtc_funcs *funcs; int ret; head = kzalloc(sizeof(*head), GFP_KERNEL); @@ -507,6 +558,11 @@ nv50_head_create(struct drm_device *dev, int index) head->func = disp->core->func->head; head->base.index = index; + if (disp->disp->object.oclass < GF110_DISP) + funcs = &nv50_head_func; + else + funcs = &nvd9_head_func; + if (disp->disp->object.oclass < GV100_DISP) { ret = nv50_base_new(drm, head->base.index, &base); ret = nv50_ovly_new(drm, head->base.index, &ovly); @@ -523,9 +579,10 @@ nv50_head_create(struct drm_device *dev, int index) return ERR_PTR(ret); } - crtc = &head->base.base; + nv_crtc = &head->base; + crtc = &nv_crtc->base; drm_crtc_init_with_planes(dev, crtc, &base->plane, &curs->plane, - &nv50_head_func, "head-%d", head->base.index); + funcs, "head-%d", head->base.index); drm_crtc_helper_add(crtc, &nv50_head_help); /* Keep the legacy gamma size at 256 to avoid compatibility issues */ drm_mode_crtc_set_gamma_size(crtc, 256); @@ -541,5 +598,16 @@ nv50_head_create(struct drm_device *dev, int index) } } + ret = nvif_notify_init(&disp->disp->object, nv50_head_vblank_handler, + false, NV04_DISP_NTFY_VBLANK, + &(struct nvif_notify_head_req_v0) { + .head = nv_crtc->index, + }, + sizeof(struct nvif_notify_head_req_v0), + sizeof(struct nvif_notify_head_rep_v0), + &nv_crtc->vblank); + if (ret) + return ERR_PTR(ret); + return head; } diff --git a/drivers/gpu/drm/nouveau/dispnv50/head.h b/drivers/gpu/drm/nouveau/dispnv50/head.h index c05bbba9e247..30501ad1824e 100644 --- a/drivers/gpu/drm/nouveau/dispnv50/head.h +++ b/drivers/gpu/drm/nouveau/dispnv50/head.h @@ -1,22 +1,28 @@ #ifndef __NV50_KMS_HEAD_H__ #define __NV50_KMS_HEAD_H__ #define nv50_head(c) container_of((c), struct nv50_head, base.base) +#include + #include "disp.h" #include "atom.h" +#include "crc.h" #include "lut.h" #include "nouveau_crtc.h" +#include "nouveau_encoder.h" struct nv50_head { const struct nv50_head_func *func; struct nouveau_crtc base; + struct nv50_crc crc; struct nv50_lut olut; struct nv50_msto *msto; }; struct nv50_head *nv50_head_create(struct drm_device *, int index); -void nv50_head_flush_set(struct nv50_head *, struct nv50_head_atom *); -void nv50_head_flush_clr(struct nv50_head *, struct nv50_head_atom *, bool y); +void nv50_head_flush_set(struct nv50_head *head, struct nv50_head_atom *asyh); +void nv50_head_flush_clr(struct nv50_head *head, + struct nv50_head_atom *asyh, bool flush); struct nv50_head_func { void (*view)(struct nv50_head *, struct nv50_head_atom *); diff --git a/drivers/gpu/drm/nouveau/dispnv50/head907d.c b/drivers/gpu/drm/nouveau/dispnv50/head907d.c index 3002ec23d7a6..63a0b45d96d6 100644 --- a/drivers/gpu/drm/nouveau/dispnv50/head907d.c +++ b/drivers/gpu/drm/nouveau/dispnv50/head907d.c @@ -19,8 +19,15 @@ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ +#include +#include +#include +#include "nouveau_drv.h" +#include "nouveau_bios.h" +#include "nouveau_connector.h" #include "head.h" #include "core.h" +#include "crc.h" void head907d_or(struct nv50_head *head, struct nv50_head_atom *asyh) @@ -29,9 +36,10 @@ head907d_or(struct nv50_head *head, struct nv50_head_atom *asyh) u32 *push; if ((push = evo_wait(core, 3))) { evo_mthd(push, 0x0404 + (head->base.index * 0x300), 2); - evo_data(push, 0x00000001 | asyh->or.depth << 6 | - asyh->or.nvsync << 4 | - asyh->or.nhsync << 3); + evo_data(push, asyh->or.depth << 6 | + asyh->or.nvsync << 4 | + asyh->or.nhsync << 3 | + asyh->or.crc_raster); evo_data(push, 0x31ec6000 | head->base.index << 25 | asyh->mode.interlace); evo_kick(push, core); diff --git a/drivers/gpu/drm/nouveau/dispnv50/headc37d.c b/drivers/gpu/drm/nouveau/dispnv50/headc37d.c index c2619652ff2e..35fcdf8825b5 100644 --- a/drivers/gpu/drm/nouveau/dispnv50/headc37d.c +++ b/drivers/gpu/drm/nouveau/dispnv50/headc37d.c @@ -46,10 +46,10 @@ headc37d_or(struct nv50_head *head, struct nv50_head_atom *asyh) } evo_mthd(push, 0x2004 + (head->base.index * 0x400), 1); - evo_data(push, 0x00000001 | - asyh->or.depth << 4 | + evo_data(push, depth << 4 | asyh->or.nvsync << 3 | - asyh->or.nhsync << 2); + asyh->or.nhsync << 2 | + asyh->or.crc_raster); evo_kick(push, core); } } diff --git a/drivers/gpu/drm/nouveau/dispnv50/headc57d.c b/drivers/gpu/drm/nouveau/dispnv50/headc57d.c index 1c1887749f4c..c7d04dd935fd 100644 --- a/drivers/gpu/drm/nouveau/dispnv50/headc57d.c +++ b/drivers/gpu/drm/nouveau/dispnv50/headc57d.c @@ -46,10 +46,11 @@ headc57d_or(struct nv50_head *head, struct nv50_head_atom *asyh) } evo_mthd(push, 0x2004 + (head->base.index * 0x400), 1); - evo_data(push, 0xfc000001 | - asyh->or.depth << 4 | + evo_data(push, 0xfc000000 | + depth << 4 | asyh->or.nvsync << 3 | - asyh->or.nhsync << 2); + asyh->or.nhsync << 2 | + asyh->or.crc_raster); evo_kick(push, core); } } diff --git a/drivers/gpu/drm/nouveau/nouveau_display.c b/drivers/gpu/drm/nouveau/nouveau_display.c index 901ac55506d6..657554cf011e 100644 --- a/drivers/gpu/drm/nouveau/nouveau_display.c +++ b/drivers/gpu/drm/nouveau/nouveau_display.c @@ -44,15 +44,7 @@ #include #include #include - -static int -nouveau_display_vblank_handler(struct nvif_notify *notify) -{ - struct nouveau_crtc *nv_crtc = - container_of(notify, typeof(*nv_crtc), vblank); - drm_crtc_handle_vblank(&nv_crtc->base); - return NVIF_NOTIFY_KEEP; -} +#include int nouveau_display_vblank_enable(struct drm_crtc *crtc) @@ -136,50 +128,6 @@ nouveau_display_scanoutpos(struct drm_crtc *crtc, stime, etime); } -static void -nouveau_display_vblank_fini(struct drm_device *dev) -{ - struct drm_crtc *crtc; - - list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { - struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); - nvif_notify_fini(&nv_crtc->vblank); - } -} - -static int -nouveau_display_vblank_init(struct drm_device *dev) -{ - struct nouveau_display *disp = nouveau_display(dev); - struct drm_crtc *crtc; - int ret; - - list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { - struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); - ret = nvif_notify_init(&disp->disp.object, - nouveau_display_vblank_handler, false, - NV04_DISP_NTFY_VBLANK, - &(struct nvif_notify_head_req_v0) { - .head = nv_crtc->index, - }, - sizeof(struct nvif_notify_head_req_v0), - sizeof(struct nvif_notify_head_rep_v0), - &nv_crtc->vblank); - if (ret) { - nouveau_display_vblank_fini(dev); - return ret; - } - } - - ret = drm_vblank_init(dev, dev->mode_config.num_crtc); - if (ret) { - nouveau_display_vblank_fini(dev); - return ret; - } - - return 0; -} - static const struct drm_framebuffer_funcs nouveau_framebuffer_funcs = { .destroy = drm_gem_fb_destroy, .create_handle = drm_gem_fb_create_handle, @@ -705,9 +653,12 @@ nouveau_display_create(struct drm_device *dev) drm_mode_config_reset(dev); if (dev->mode_config.num_crtc) { - ret = nouveau_display_vblank_init(dev); + ret = drm_vblank_init(dev, dev->mode_config.num_crtc); if (ret) goto vblank_err; + + if (disp->disp.object.oclass >= NV50_DISP) + nv50_crc_init(dev); } INIT_WORK(&drm->hpd_work, nouveau_display_hpd_work); @@ -734,7 +685,6 @@ nouveau_display_destroy(struct drm_device *dev) #ifdef CONFIG_ACPI unregister_acpi_notifier(&nouveau_drm(dev)->acpi_nb); #endif - nouveau_display_vblank_fini(dev); drm_kms_helper_poll_fini(dev); drm_mode_config_cleanup(dev); From 2d7865082dd0cff5777bf9c1f075d54f737cc2e3 Mon Sep 17 00:00:00 2001 From: Lyude Paul Date: Mon, 29 Jun 2020 18:36:25 -0400 Subject: [PATCH 15/59] drm/nouveau/kms/nvd9-: Fix disabling CRCs alongside OR reprogramming While I had thought I'd tested this before, it looks like this one issue slipped by my original CRC patches. Basically, there seem to be a few rules we need to follow when sending CRC commands to the display controller: * CRCs cannot be both disabled and enabled for a single head in the same flush * If a head with CRC reporting enabled switches from one OR to another, there must be a flush before the OR is re-enabled regardless of the final state of CRC reporting. So, split nv50_crc_atomic_prepare_notifier_contexts() into two functions: * nv_crc_atomic_release_notifier_contexts() - checks whether the CRC notifier contexts were released successfully after the first flush * nv_crc_atomic_init_notifier_contexts() - prepares any CRC notifier contexts for use before enabling reporting Additionally, in order to force a flush when we re-assign ORs with heads that have CRCs enabled we split our atomic check function into two: * nv50_crc_atomic_check_head() - called from our heads' atomic checks, determines whether a state needs to set or clear CRC reporting * nv50_crc_atomic_check_outp() - called at the end of the atomic check after all ORs have been added to the atomic state, and sets nv50_atom->flush_disable if needed Signed-off-by: Lyude Paul Reviewed-by: Ben Skeggs Acked-by: Dave Airlie Link: https://patchwork.freedesktop.org/patch/msgid/20200629223635.103804-1-lyude@redhat.com --- drivers/gpu/drm/nouveau/dispnv50/crc.c | 109 ++++++++++++++++-------- drivers/gpu/drm/nouveau/dispnv50/crc.h | 18 ++-- drivers/gpu/drm/nouveau/dispnv50/disp.c | 13 ++- drivers/gpu/drm/nouveau/dispnv50/head.c | 2 +- 4 files changed, 98 insertions(+), 44 deletions(-) diff --git a/drivers/gpu/drm/nouveau/dispnv50/crc.c b/drivers/gpu/drm/nouveau/dispnv50/crc.c index 0b18d9e3a2b9..f17fb6d56757 100644 --- a/drivers/gpu/drm/nouveau/dispnv50/crc.c +++ b/drivers/gpu/drm/nouveau/dispnv50/crc.c @@ -261,7 +261,29 @@ void nv50_crc_atomic_stop_reporting(struct drm_atomic_state *state) } } -void nv50_crc_atomic_prepare_notifier_contexts(struct drm_atomic_state *state) +void nv50_crc_atomic_init_notifier_contexts(struct drm_atomic_state *state) +{ + struct drm_crtc_state *new_crtc_state; + struct drm_crtc *crtc; + int i; + + for_each_new_crtc_in_state(state, crtc, new_crtc_state, i) { + struct nv50_head *head = nv50_head(crtc); + struct nv50_head_atom *asyh = nv50_head_atom(new_crtc_state); + struct nv50_crc *crc = &head->crc; + int i; + + if (!asyh->set.crc) + continue; + + crc->entry_idx = 0; + crc->ctx_changed = false; + for (i = 0; i < ARRAY_SIZE(crc->ctx); i++) + nv50_crc_reset_ctx(&crc->ctx[i]); + } +} + +void nv50_crc_atomic_release_notifier_contexts(struct drm_atomic_state *state) { const struct nv50_crc_func *func = nv50_disp(state->dev)->core->func->crc; @@ -274,22 +296,15 @@ void nv50_crc_atomic_prepare_notifier_contexts(struct drm_atomic_state *state) struct nv50_head_atom *asyh = nv50_head_atom(new_crtc_state); struct nv50_crc *crc = &head->crc; struct nv50_crc_notifier_ctx *ctx = &crc->ctx[crc->ctx_idx]; - int i; - if (asyh->clr.crc && asyh->crc.src) { - if (crc->ctx_changed) { - nv50_crc_wait_ctx_finished(head, func, ctx); - ctx = &crc->ctx[crc->ctx_idx ^ 1]; - } + if (!asyh->clr.crc) + continue; + + if (crc->ctx_changed) { nv50_crc_wait_ctx_finished(head, func, ctx); + ctx = &crc->ctx[crc->ctx_idx ^ 1]; } - - if (asyh->set.crc) { - crc->entry_idx = 0; - crc->ctx_changed = false; - for (i = 0; i < ARRAY_SIZE(crc->ctx); i++) - nv50_crc_reset_ctx(&crc->ctx[i]); - } + nv50_crc_wait_ctx_finished(head, func, ctx); } } @@ -325,16 +340,13 @@ void nv50_crc_atomic_start_reporting(struct drm_atomic_state *state) } } -int nv50_crc_atomic_check(struct nv50_head *head, - struct nv50_head_atom *asyh, - struct nv50_head_atom *armh) +int nv50_crc_atomic_check_head(struct nv50_head *head, + struct nv50_head_atom *asyh, + struct nv50_head_atom *armh) { - struct drm_atomic_state *state = asyh->state.state; + struct nv50_atom *atom = nv50_atom(asyh->state.state); struct drm_device *dev = head->base.base.dev; - struct nv50_atom *atom = nv50_atom(state); struct nv50_disp *disp = nv50_disp(dev); - struct drm_encoder *encoder; - struct nv50_outp_atom *outp_atom; bool changed = armh->crc.src != asyh->crc.src; if (!armh->crc.src && !asyh->crc.src) { @@ -373,21 +385,8 @@ int nv50_crc_atomic_check(struct nv50_head *head, asyh->set.or |= armh->or.crc_raster != asyh->or.crc_raster; - /* - * If we're reprogramming our OR, we need to flush the CRC - * disable first - */ - if (asyh->clr.crc) { - encoder = nv50_head_atom_get_encoder(armh); - - list_for_each_entry(outp_atom, &atom->outp, head) { - if (outp_atom->encoder == encoder) { - if (outp_atom->set.mask) - atom->flush_disable = true; - break; - } - } - } + if (asyh->clr.crc && asyh->set.crc) + atom->flush_disable = true; } else { asyh->set.crc = false; asyh->clr.crc = false; @@ -396,6 +395,44 @@ int nv50_crc_atomic_check(struct nv50_head *head, return 0; } +void nv50_crc_atomic_check_outp(struct nv50_atom *atom) +{ + struct drm_crtc *crtc; + struct drm_crtc_state *old_crtc_state, *new_crtc_state; + int i; + + if (atom->flush_disable) + return; + + for_each_oldnew_crtc_in_state(&atom->state, crtc, old_crtc_state, + new_crtc_state, i) { + struct nv50_head_atom *armh = nv50_head_atom(old_crtc_state); + struct nv50_head_atom *asyh = nv50_head_atom(new_crtc_state); + struct nv50_outp_atom *outp_atom; + struct nouveau_encoder *outp = + nv50_real_outp(nv50_head_atom_get_encoder(armh)); + struct drm_encoder *encoder = &outp->base.base; + + if (!asyh->clr.crc) + continue; + + /* + * Re-programming ORs can't be done in the same flush as + * disabling CRCs + */ + list_for_each_entry(outp_atom, &atom->outp, head) { + if (outp_atom->encoder == encoder) { + if (outp_atom->set.mask) { + atom->flush_disable = true; + return; + } else { + break; + } + } + } + } +} + static enum nv50_crc_source_type nv50_crc_source_type(struct nouveau_encoder *outp, enum nv50_crc_source source) diff --git a/drivers/gpu/drm/nouveau/dispnv50/crc.h b/drivers/gpu/drm/nouveau/dispnv50/crc.h index 8e166260c200..4bc59e779315 100644 --- a/drivers/gpu/drm/nouveau/dispnv50/crc.h +++ b/drivers/gpu/drm/nouveau/dispnv50/crc.h @@ -10,6 +10,7 @@ #include #include "nouveau_encoder.h" +struct nv50_atom; struct nv50_disp; struct nv50_head; @@ -82,10 +83,12 @@ int nv50_crc_verify_source(struct drm_crtc *, const char *, size_t *); const char *const *nv50_crc_get_sources(struct drm_crtc *, size_t *); int nv50_crc_set_source(struct drm_crtc *, const char *); -int nv50_crc_atomic_check(struct nv50_head *, struct nv50_head_atom *, - struct nv50_head_atom *); +int nv50_crc_atomic_check_head(struct nv50_head *, struct nv50_head_atom *, + struct nv50_head_atom *); +void nv50_crc_atomic_check_outp(struct nv50_atom *atom); void nv50_crc_atomic_stop_reporting(struct drm_atomic_state *); -void nv50_crc_atomic_prepare_notifier_contexts(struct drm_atomic_state *); +void nv50_crc_atomic_init_notifier_contexts(struct drm_atomic_state *); +void nv50_crc_atomic_release_notifier_contexts(struct drm_atomic_state *); void nv50_crc_atomic_start_reporting(struct drm_atomic_state *); void nv50_crc_atomic_set(struct nv50_head *, struct nv50_head_atom *); void nv50_crc_atomic_clr(struct nv50_head *); @@ -108,12 +111,15 @@ static inline void nv50_crc_handle_vblank(struct nv50_head *head) { return 0; } static inline int -nv50_crc_atomic_check(struct nv50_head *, struct nv50_head_atom *, - struct nv50_head_atom *) {} +nv50_crc_atomic_check_head(struct nv50_head *, struct nv50_head_atom *, + struct nv50_head_atom *) {} +static inline void nv50_crc_atomic_check_outp(struct nv50_atom *atom) {} static inline void nv50_crc_atomic_stop_reporting(struct drm_atomic_state *) {} static inline void -nv50_crc_atomic_prepare_notifier_contexts(struct drm_atomic_state *) {} +nv50_crc_atomic_init_notifier_contexts(struct drm_atomic_state *) {} +static inline void +nv50_crc_atomic_release_notifier_contexts(struct drm_atomic_state *) {} static inline void nv50_crc_atomic_start_reporting(struct drm_atomic_state *) {} static inline void diff --git a/drivers/gpu/drm/nouveau/dispnv50/disp.c b/drivers/gpu/drm/nouveau/dispnv50/disp.c index 9cb06d6d6c3f..cd71b9876c8a 100644 --- a/drivers/gpu/drm/nouveau/dispnv50/disp.c +++ b/drivers/gpu/drm/nouveau/dispnv50/disp.c @@ -1943,6 +1943,7 @@ nv50_disp_atomic_commit_tail(struct drm_atomic_state *state) struct nv50_outp_atom *outp, *outt; u32 interlock[NV50_DISP_INTERLOCK__SIZE] = {}; int i; + bool flushed = false; NV_ATOMIC(drm, "commit %d %d\n", atom->lock_core, atom->flush_disable); nv50_crc_atomic_stop_reporting(state); @@ -2003,6 +2004,8 @@ nv50_disp_atomic_commit_tail(struct drm_atomic_state *state) nv50_disp_atomic_commit_wndw(state, interlock); nv50_disp_atomic_commit_core(state, interlock); memset(interlock, 0x00, sizeof(interlock)); + + flushed = true; } } } @@ -2013,10 +2016,14 @@ nv50_disp_atomic_commit_tail(struct drm_atomic_state *state) nv50_disp_atomic_commit_wndw(state, interlock); nv50_disp_atomic_commit_core(state, interlock); memset(interlock, 0x00, sizeof(interlock)); + + flushed = true; } } - nv50_crc_atomic_prepare_notifier_contexts(state); + if (flushed) + nv50_crc_atomic_release_notifier_contexts(state); + nv50_crc_atomic_init_notifier_contexts(state); /* Update output path(s). */ list_for_each_entry_safe(outp, outt, &atom->outp, head) { @@ -2132,6 +2139,8 @@ nv50_disp_atomic_commit_tail(struct drm_atomic_state *state) } nv50_crc_atomic_start_reporting(state); + if (!flushed) + nv50_crc_atomic_release_notifier_contexts(state); drm_atomic_helper_commit_hw_done(state); drm_atomic_helper_cleanup_planes(dev, state); drm_atomic_helper_commit_cleanup_done(state); @@ -2338,6 +2347,8 @@ nv50_disp_atomic_check(struct drm_device *dev, struct drm_atomic_state *state) if (ret) return ret; + nv50_crc_atomic_check_outp(atom); + return 0; } diff --git a/drivers/gpu/drm/nouveau/dispnv50/head.c b/drivers/gpu/drm/nouveau/dispnv50/head.c index ea3088a47065..9a10ec267d1f 100644 --- a/drivers/gpu/drm/nouveau/dispnv50/head.c +++ b/drivers/gpu/drm/nouveau/dispnv50/head.c @@ -414,7 +414,7 @@ nv50_head_atomic_check(struct drm_crtc *crtc, struct drm_crtc_state *state) asyh->set.curs = asyh->curs.visible; } - ret = nv50_crc_atomic_check(head, asyh, armh); + ret = nv50_crc_atomic_check_head(head, asyh, armh); if (ret) return ret; From ca43f274e03f91c533643299ae4984965ce03205 Mon Sep 17 00:00:00 2001 From: Paul Cercueil Date: Thu, 16 Jul 2020 18:38:35 +0200 Subject: [PATCH 16/59] drm/ingenic: Fix incorrect assumption about plane->index plane->index is NOT the index of the color plane in a YUV frame. Actually, a YUV frame is represented by a single drm_plane, even though it contains three Y, U, V planes. v2-v3: No change Cc: stable@vger.kernel.org # v5.3 Fixes: 90b86fcc47b4 ("DRM: Add KMS driver for the Ingenic JZ47xx SoCs") Signed-off-by: Paul Cercueil Reviewed-by: Sam Ravnborg Link: https://patchwork.freedesktop.org/patch/msgid/20200716163846.174790-1-paul@crapouillou.net --- drivers/gpu/drm/ingenic/ingenic-drm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/ingenic/ingenic-drm.c b/drivers/gpu/drm/ingenic/ingenic-drm.c index deb37b4a8e91..606d8acb0954 100644 --- a/drivers/gpu/drm/ingenic/ingenic-drm.c +++ b/drivers/gpu/drm/ingenic/ingenic-drm.c @@ -386,7 +386,7 @@ static void ingenic_drm_plane_atomic_update(struct drm_plane *plane, addr = drm_fb_cma_get_gem_addr(state->fb, state, 0); width = state->src_w >> 16; height = state->src_h >> 16; - cpp = state->fb->format->cpp[plane->index]; + cpp = state->fb->format->cpp[0]; priv->dma_hwdesc->addr = addr; priv->dma_hwdesc->cmd = width * height * cpp / 4; From c9390228245c06e7866896d6c1bd09601b8f8f4b Mon Sep 17 00:00:00 2001 From: Paul Cercueil Date: Thu, 16 Jul 2020 18:38:36 +0200 Subject: [PATCH 17/59] dt-bindings: display: Convert ingenic,lcd.txt to YAML Convert the ingenic,lcd.txt to a new ingenic,lcd.yaml file. In the process, the new ingenic,jz4780-lcd compatible string has been added. v2: Add info about IPU at port@8 v3: No change Signed-off-by: Paul Cercueil Reviewed-by: Rob Herring Reviewed-by: Sam Ravnborg Link: https://patchwork.freedesktop.org/patch/msgid/20200716163846.174790-2-paul@crapouillou.net --- .../bindings/display/ingenic,lcd.txt | 45 ------- .../bindings/display/ingenic,lcd.yaml | 126 ++++++++++++++++++ 2 files changed, 126 insertions(+), 45 deletions(-) delete mode 100644 Documentation/devicetree/bindings/display/ingenic,lcd.txt create mode 100644 Documentation/devicetree/bindings/display/ingenic,lcd.yaml diff --git a/Documentation/devicetree/bindings/display/ingenic,lcd.txt b/Documentation/devicetree/bindings/display/ingenic,lcd.txt deleted file mode 100644 index 01e3261defb6..000000000000 --- a/Documentation/devicetree/bindings/display/ingenic,lcd.txt +++ /dev/null @@ -1,45 +0,0 @@ -Ingenic JZ47xx LCD driver - -Required properties: -- compatible: one of: - * ingenic,jz4740-lcd - * ingenic,jz4725b-lcd - * ingenic,jz4770-lcd -- reg: LCD registers location and length -- clocks: LCD pixclock and device clock specifiers. - The device clock is only required on the JZ4740. -- clock-names: "lcd_pclk" and "lcd" -- interrupts: Specifies the interrupt line the LCD controller is connected to. - -Example: - -panel { - compatible = "sharp,ls020b1dd01d"; - - backlight = <&backlight>; - power-supply = <&vcc>; - - port { - panel_input: endpoint { - remote-endpoint = <&panel_output>; - }; - }; -}; - - -lcd: lcd-controller@13050000 { - compatible = "ingenic,jz4725b-lcd"; - reg = <0x13050000 0x1000>; - - interrupt-parent = <&intc>; - interrupts = <31>; - - clocks = <&cgu JZ4725B_CLK_LCD>; - clock-names = "lcd"; - - port { - panel_output: endpoint { - remote-endpoint = <&panel_input>; - }; - }; -}; diff --git a/Documentation/devicetree/bindings/display/ingenic,lcd.yaml b/Documentation/devicetree/bindings/display/ingenic,lcd.yaml new file mode 100644 index 000000000000..d56db1802fad --- /dev/null +++ b/Documentation/devicetree/bindings/display/ingenic,lcd.yaml @@ -0,0 +1,126 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/display/ingenic,lcd.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Ingenic SoCs LCD controller devicetree bindings + +maintainers: + - Paul Cercueil + +properties: + $nodename: + pattern: "^lcd-controller@[0-9a-f]+$" + + compatible: + enum: + - ingenic,jz4740-lcd + - ingenic,jz4725b-lcd + - ingenic,jz4770-lcd + - ingenic,jz4780-lcd + + reg: + maxItems: 1 + + interrupts: + maxItems: 1 + + clocks: + items: + - description: Pixel clock + - description: Module clock + minItems: 1 + + clock-names: + items: + - const: lcd_pclk + - const: lcd + minItems: 1 + + port: + description: OF graph bindings (specified in bindings/graph.txt). + + ports: + description: OF graph bindings (specified in bindings/graph.txt). + type: object + properties: + port@0: + type: object + description: DPI output, to interface with TFT panels. + + port@8: + type: object + description: Link to the Image Processing Unit (IPU). + (See ingenic,ipu.yaml). + + required: + - port@0 + +required: + - compatible + - reg + - interrupts + - clocks + - clock-names + +if: + properties: + compatible: + contains: + enum: + - ingenic,jz4740-lcd + - ingenic,jz4780-lcd +then: + properties: + clocks: + minItems: 2 + clock-names: + minItems: 2 +else: + properties: + clocks: + maxItems: 1 + clock-names: + maxItems: 1 + +additionalProperties: false + +examples: + - | + #include + lcd-controller@13050000 { + compatible = "ingenic,jz4740-lcd"; + reg = <0x13050000 0x1000>; + + interrupt-parent = <&intc>; + interrupts = <30>; + + clocks = <&cgu JZ4740_CLK_LCD_PCLK>, <&cgu JZ4740_CLK_LCD>; + clock-names = "lcd_pclk", "lcd"; + + port { + endpoint { + remote-endpoint = <&panel_input>; + }; + }; + }; + + - | + #include + lcd-controller@13050000 { + compatible = "ingenic,jz4725b-lcd"; + reg = <0x13050000 0x1000>; + + interrupt-parent = <&intc>; + interrupts = <31>; + + clocks = <&cgu JZ4725B_CLK_LCD>; + clock-names = "lcd_pclk"; + + port { + endpoint { + remote-endpoint = <&panel_input>; + }; + }; + }; From ba8989a6fd7a8a94e9740ad260f1dcc1831f9aeb Mon Sep 17 00:00:00 2001 From: Paul Cercueil Date: Thu, 16 Jul 2020 18:38:37 +0200 Subject: [PATCH 18/59] dt-bindings: display: Add ingenic,ipu.yaml Add documentation of the Device Tree bindings for the Image Processing Unit (IPU) found in most Ingenic SoCs. v2: Add missing 'const' in items list v3: No change Signed-off-by: Paul Cercueil Reviewed-by: Rob Herring Reviewed-by: Sam Ravnborg Link: https://patchwork.freedesktop.org/patch/msgid/20200716163846.174790-3-paul@crapouillou.net --- .../bindings/display/ingenic,ipu.yaml | 65 +++++++++++++++++++ 1 file changed, 65 insertions(+) create mode 100644 Documentation/devicetree/bindings/display/ingenic,ipu.yaml diff --git a/Documentation/devicetree/bindings/display/ingenic,ipu.yaml b/Documentation/devicetree/bindings/display/ingenic,ipu.yaml new file mode 100644 index 000000000000..5bfc33eb32c9 --- /dev/null +++ b/Documentation/devicetree/bindings/display/ingenic,ipu.yaml @@ -0,0 +1,65 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/display/ingenic,ipu.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Ingenic SoCs Image Processing Unit (IPU) devicetree bindings + +maintainers: + - Paul Cercueil + +properties: + compatible: + oneOf: + - enum: + - ingenic,jz4725b-ipu + - ingenic,jz4760-ipu + - items: + - const: ingenic,jz4770-ipu + - const: ingenic,jz4760-ipu + + reg: + maxItems: 1 + + interrupts: + maxItems: 1 + + clocks: + maxItems: 1 + + clock-names: + const: ipu + +patternProperties: + "^ports?$": + description: OF graph bindings (specified in bindings/graph.txt). + +required: + - compatible + - reg + - interrupts + - clocks + - clock-names + +additionalProperties: false + +examples: + - | + #include + ipu@13080000 { + compatible = "ingenic,jz4770-ipu", "ingenic,jz4760-ipu"; + reg = <0x13080000 0x800>; + + interrupt-parent = <&intc>; + interrupts = <29>; + + clocks = <&cgu JZ4770_CLK_IPU>; + clock-names = "ipu"; + + port { + ipu_ep: endpoint { + remote-endpoint = <&lcdc_ep>; + }; + }; + }; From 54fe894219903e32f5154265f46ed1eb5cdc2f5f Mon Sep 17 00:00:00 2001 From: Paul Cercueil Date: Thu, 16 Jul 2020 18:38:38 +0200 Subject: [PATCH 19/59] drm/ingenic: Rename ingenic-drm.c to ingenic-drm-drv.c Full rename without any modification, except to the Makefile. Renaming ingenic-drm.c to ingenic-drm-drv.c allow to decouple the module name from the source file name in the Makefile. This will be useful later when more source files are added. v2: New patch v3: No change Signed-off-by: Paul Cercueil Reviewed-by: Sam Ravnborg Link: https://patchwork.freedesktop.org/patch/msgid/20200716163846.174790-4-paul@crapouillou.net --- drivers/gpu/drm/ingenic/Makefile | 1 + drivers/gpu/drm/ingenic/{ingenic-drm.c => ingenic-drm-drv.c} | 0 2 files changed, 1 insertion(+) rename drivers/gpu/drm/ingenic/{ingenic-drm.c => ingenic-drm-drv.c} (100%) diff --git a/drivers/gpu/drm/ingenic/Makefile b/drivers/gpu/drm/ingenic/Makefile index 11cac42ce0bb..9875628dd8bc 100644 --- a/drivers/gpu/drm/ingenic/Makefile +++ b/drivers/gpu/drm/ingenic/Makefile @@ -1 +1,2 @@ obj-$(CONFIG_DRM_INGENIC) += ingenic-drm.o +ingenic-drm-y += ingenic-drm-drv.o diff --git a/drivers/gpu/drm/ingenic/ingenic-drm.c b/drivers/gpu/drm/ingenic/ingenic-drm-drv.c similarity index 100% rename from drivers/gpu/drm/ingenic/ingenic-drm.c rename to drivers/gpu/drm/ingenic/ingenic-drm-drv.c From 1f7596f4ad9b7fa68b2a27504036a1145d9c3cf1 Mon Sep 17 00:00:00 2001 From: Paul Cercueil Date: Thu, 16 Jul 2020 18:38:39 +0200 Subject: [PATCH 20/59] drm/ingenic: Add missing CR in debug strings If you pass a string that is not terminated with a carriage return to dev_err(), it will eventually be printed with a carriage return, but not right away, since the kernel will wait for a pr_cont(). v2: New patch v3: No change Signed-off-by: Paul Cercueil Reviewed-by: Sam Ravnborg Link: https://patchwork.freedesktop.org/patch/msgid/20200716163846.174790-5-paul@crapouillou.net --- drivers/gpu/drm/ingenic/ingenic-drm-drv.c | 30 +++++++++++------------ 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/drivers/gpu/drm/ingenic/ingenic-drm-drv.c b/drivers/gpu/drm/ingenic/ingenic-drm-drv.c index 606d8acb0954..9e845234c7b5 100644 --- a/drivers/gpu/drm/ingenic/ingenic-drm-drv.c +++ b/drivers/gpu/drm/ingenic/ingenic-drm-drv.c @@ -623,14 +623,14 @@ static int ingenic_drm_probe(struct platform_device *pdev) base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(base)) { - dev_err(dev, "Failed to get memory resource"); + dev_err(dev, "Failed to get memory resource\n"); return PTR_ERR(base); } priv->map = devm_regmap_init_mmio(dev, base, &ingenic_drm_regmap_config); if (IS_ERR(priv->map)) { - dev_err(dev, "Failed to create regmap"); + dev_err(dev, "Failed to create regmap\n"); return PTR_ERR(priv->map); } @@ -641,21 +641,21 @@ static int ingenic_drm_probe(struct platform_device *pdev) if (soc_info->needs_dev_clk) { priv->lcd_clk = devm_clk_get(dev, "lcd"); if (IS_ERR(priv->lcd_clk)) { - dev_err(dev, "Failed to get lcd clock"); + dev_err(dev, "Failed to get lcd clock\n"); return PTR_ERR(priv->lcd_clk); } } priv->pix_clk = devm_clk_get(dev, "lcd_pclk"); if (IS_ERR(priv->pix_clk)) { - dev_err(dev, "Failed to get pixel clock"); + dev_err(dev, "Failed to get pixel clock\n"); return PTR_ERR(priv->pix_clk); } ret = drm_of_find_panel_or_bridge(dev->of_node, 0, 0, &panel, &bridge); if (ret) { if (ret != -EPROBE_DEFER) - dev_err(dev, "Failed to get panel handle"); + dev_err(dev, "Failed to get panel handle\n"); return ret; } @@ -684,7 +684,7 @@ static int ingenic_drm_probe(struct platform_device *pdev) ARRAY_SIZE(ingenic_drm_primary_formats), NULL, DRM_PLANE_TYPE_PRIMARY, NULL); if (ret) { - dev_err(dev, "Failed to register primary plane: %i", ret); + dev_err(dev, "Failed to register primary plane: %i\n", ret); return ret; } @@ -693,7 +693,7 @@ static int ingenic_drm_probe(struct platform_device *pdev) ret = drm_crtc_init_with_planes(drm, &priv->crtc, &priv->primary, NULL, &ingenic_drm_crtc_funcs, NULL); if (ret) { - dev_err(dev, "Failed to init CRTC: %i", ret); + dev_err(dev, "Failed to init CRTC: %i\n", ret); return ret; } @@ -705,25 +705,25 @@ static int ingenic_drm_probe(struct platform_device *pdev) ret = drm_simple_encoder_init(drm, &priv->encoder, DRM_MODE_ENCODER_DPI); if (ret) { - dev_err(dev, "Failed to init encoder: %i", ret); + dev_err(dev, "Failed to init encoder: %i\n", ret); return ret; } ret = drm_bridge_attach(&priv->encoder, bridge, NULL, 0); if (ret) { - dev_err(dev, "Unable to attach bridge"); + dev_err(dev, "Unable to attach bridge\n"); return ret; } ret = drm_irq_install(drm, irq); if (ret) { - dev_err(dev, "Unable to install IRQ handler"); + dev_err(dev, "Unable to install IRQ handler\n"); return ret; } ret = drm_vblank_init(drm, 1); if (ret) { - dev_err(dev, "Failed calling drm_vblank_init()"); + dev_err(dev, "Failed calling drm_vblank_init()\n"); return ret; } @@ -731,7 +731,7 @@ static int ingenic_drm_probe(struct platform_device *pdev) ret = clk_prepare_enable(priv->pix_clk); if (ret) { - dev_err(dev, "Unable to start pixel clock"); + dev_err(dev, "Unable to start pixel clock\n"); return ret; } @@ -746,20 +746,20 @@ static int ingenic_drm_probe(struct platform_device *pdev) */ ret = clk_set_rate(priv->lcd_clk, parent_rate); if (ret) { - dev_err(dev, "Unable to set LCD clock rate"); + dev_err(dev, "Unable to set LCD clock rate\n"); goto err_pixclk_disable; } ret = clk_prepare_enable(priv->lcd_clk); if (ret) { - dev_err(dev, "Unable to start lcd clock"); + dev_err(dev, "Unable to start lcd clock\n"); goto err_pixclk_disable; } } ret = drm_dev_register(drm, 0); if (ret) { - dev_err(dev, "Failed to register DRM driver"); + dev_err(dev, "Failed to register DRM driver\n"); goto err_devclk_disable; } From e5507d2c01362b4953edb05dc84203be25eea737 Mon Sep 17 00:00:00 2001 From: Paul Cercueil Date: Thu, 16 Jul 2020 18:38:40 +0200 Subject: [PATCH 21/59] drm/ingenic: Set DMA descriptor chain address in probe The address of the DMA descriptor never changes. It can therefore be set in the probe function. v2-v3: No change Signed-off-by: Paul Cercueil Reviewed-by: Sam Ravnborg Link: https://patchwork.freedesktop.org/patch/msgid/20200716163846.174790-6-paul@crapouillou.net --- drivers/gpu/drm/ingenic/ingenic-drm-drv.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/ingenic/ingenic-drm-drv.c b/drivers/gpu/drm/ingenic/ingenic-drm-drv.c index 9e845234c7b5..c769a54f0274 100644 --- a/drivers/gpu/drm/ingenic/ingenic-drm-drv.c +++ b/drivers/gpu/drm/ingenic/ingenic-drm-drv.c @@ -358,8 +358,6 @@ static void ingenic_drm_crtc_atomic_flush(struct drm_crtc *crtc, ingenic_drm_crtc_update_ctrl(priv, finfo); clk_set_rate(priv->pix_clk, state->adjusted_mode.clock * 1000); - - regmap_write(priv->map, JZ_REG_LCD_DA0, priv->dma_hwdesc->next); } if (event) { @@ -757,6 +755,9 @@ static int ingenic_drm_probe(struct platform_device *pdev) } } + /* Set address of our DMA descriptor chain */ + regmap_write(priv->map, JZ_REG_LCD_DA0, priv->dma_hwdesc_phys); + ret = drm_dev_register(drm, 0); if (ret) { dev_err(dev, "Failed to register DRM driver\n"); From 4b11cb7ff2eff92d7f7a0cc358d07c088c715e7e Mon Sep 17 00:00:00 2001 From: Paul Cercueil Date: Thu, 16 Jul 2020 18:38:41 +0200 Subject: [PATCH 22/59] drm/ingenic: Move register definitions to ingenic-drm.h Move the register definitions to ingenic-drm.h, to keep ingenic-drm-drv.c tidy. v2: Fix SPDX license tag v3: No change Signed-off-by: Paul Cercueil Reviewed-by: Sam Ravnborg Link: https://patchwork.freedesktop.org/patch/msgid/20200716163846.174790-7-paul@crapouillou.net --- drivers/gpu/drm/ingenic/ingenic-drm-drv.c | 116 +------------------- drivers/gpu/drm/ingenic/ingenic-drm.h | 126 ++++++++++++++++++++++ 2 files changed, 128 insertions(+), 114 deletions(-) create mode 100644 drivers/gpu/drm/ingenic/ingenic-drm.h diff --git a/drivers/gpu/drm/ingenic/ingenic-drm-drv.c b/drivers/gpu/drm/ingenic/ingenic-drm-drv.c index c769a54f0274..d9cd74d70a61 100644 --- a/drivers/gpu/drm/ingenic/ingenic-drm-drv.c +++ b/drivers/gpu/drm/ingenic/ingenic-drm-drv.c @@ -4,6 +4,8 @@ // // Copyright (C) 2019, Paul Cercueil +#include "ingenic-drm.h" + #include #include #include @@ -32,120 +34,6 @@ #include #include -#define JZ_REG_LCD_CFG 0x00 -#define JZ_REG_LCD_VSYNC 0x04 -#define JZ_REG_LCD_HSYNC 0x08 -#define JZ_REG_LCD_VAT 0x0C -#define JZ_REG_LCD_DAH 0x10 -#define JZ_REG_LCD_DAV 0x14 -#define JZ_REG_LCD_PS 0x18 -#define JZ_REG_LCD_CLS 0x1C -#define JZ_REG_LCD_SPL 0x20 -#define JZ_REG_LCD_REV 0x24 -#define JZ_REG_LCD_CTRL 0x30 -#define JZ_REG_LCD_STATE 0x34 -#define JZ_REG_LCD_IID 0x38 -#define JZ_REG_LCD_DA0 0x40 -#define JZ_REG_LCD_SA0 0x44 -#define JZ_REG_LCD_FID0 0x48 -#define JZ_REG_LCD_CMD0 0x4C -#define JZ_REG_LCD_DA1 0x50 -#define JZ_REG_LCD_SA1 0x54 -#define JZ_REG_LCD_FID1 0x58 -#define JZ_REG_LCD_CMD1 0x5C - -#define JZ_LCD_CFG_SLCD BIT(31) -#define JZ_LCD_CFG_PS_DISABLE BIT(23) -#define JZ_LCD_CFG_CLS_DISABLE BIT(22) -#define JZ_LCD_CFG_SPL_DISABLE BIT(21) -#define JZ_LCD_CFG_REV_DISABLE BIT(20) -#define JZ_LCD_CFG_HSYNCM BIT(19) -#define JZ_LCD_CFG_PCLKM BIT(18) -#define JZ_LCD_CFG_INV BIT(17) -#define JZ_LCD_CFG_SYNC_DIR BIT(16) -#define JZ_LCD_CFG_PS_POLARITY BIT(15) -#define JZ_LCD_CFG_CLS_POLARITY BIT(14) -#define JZ_LCD_CFG_SPL_POLARITY BIT(13) -#define JZ_LCD_CFG_REV_POLARITY BIT(12) -#define JZ_LCD_CFG_HSYNC_ACTIVE_LOW BIT(11) -#define JZ_LCD_CFG_PCLK_FALLING_EDGE BIT(10) -#define JZ_LCD_CFG_DE_ACTIVE_LOW BIT(9) -#define JZ_LCD_CFG_VSYNC_ACTIVE_LOW BIT(8) -#define JZ_LCD_CFG_18_BIT BIT(7) -#define JZ_LCD_CFG_PDW (BIT(5) | BIT(4)) - -#define JZ_LCD_CFG_MODE_GENERIC_16BIT 0 -#define JZ_LCD_CFG_MODE_GENERIC_18BIT BIT(7) -#define JZ_LCD_CFG_MODE_GENERIC_24BIT BIT(6) - -#define JZ_LCD_CFG_MODE_SPECIAL_TFT_1 1 -#define JZ_LCD_CFG_MODE_SPECIAL_TFT_2 2 -#define JZ_LCD_CFG_MODE_SPECIAL_TFT_3 3 - -#define JZ_LCD_CFG_MODE_TV_OUT_P 4 -#define JZ_LCD_CFG_MODE_TV_OUT_I 6 - -#define JZ_LCD_CFG_MODE_SINGLE_COLOR_STN 8 -#define JZ_LCD_CFG_MODE_SINGLE_MONOCHROME_STN 9 -#define JZ_LCD_CFG_MODE_DUAL_COLOR_STN 10 -#define JZ_LCD_CFG_MODE_DUAL_MONOCHROME_STN 11 - -#define JZ_LCD_CFG_MODE_8BIT_SERIAL 12 -#define JZ_LCD_CFG_MODE_LCM 13 - -#define JZ_LCD_VSYNC_VPS_OFFSET 16 -#define JZ_LCD_VSYNC_VPE_OFFSET 0 - -#define JZ_LCD_HSYNC_HPS_OFFSET 16 -#define JZ_LCD_HSYNC_HPE_OFFSET 0 - -#define JZ_LCD_VAT_HT_OFFSET 16 -#define JZ_LCD_VAT_VT_OFFSET 0 - -#define JZ_LCD_DAH_HDS_OFFSET 16 -#define JZ_LCD_DAH_HDE_OFFSET 0 - -#define JZ_LCD_DAV_VDS_OFFSET 16 -#define JZ_LCD_DAV_VDE_OFFSET 0 - -#define JZ_LCD_CTRL_BURST_4 (0x0 << 28) -#define JZ_LCD_CTRL_BURST_8 (0x1 << 28) -#define JZ_LCD_CTRL_BURST_16 (0x2 << 28) -#define JZ_LCD_CTRL_RGB555 BIT(27) -#define JZ_LCD_CTRL_OFUP BIT(26) -#define JZ_LCD_CTRL_FRC_GRAYSCALE_16 (0x0 << 24) -#define JZ_LCD_CTRL_FRC_GRAYSCALE_4 (0x1 << 24) -#define JZ_LCD_CTRL_FRC_GRAYSCALE_2 (0x2 << 24) -#define JZ_LCD_CTRL_PDD_MASK (0xff << 16) -#define JZ_LCD_CTRL_EOF_IRQ BIT(13) -#define JZ_LCD_CTRL_SOF_IRQ BIT(12) -#define JZ_LCD_CTRL_OFU_IRQ BIT(11) -#define JZ_LCD_CTRL_IFU0_IRQ BIT(10) -#define JZ_LCD_CTRL_IFU1_IRQ BIT(9) -#define JZ_LCD_CTRL_DD_IRQ BIT(8) -#define JZ_LCD_CTRL_QDD_IRQ BIT(7) -#define JZ_LCD_CTRL_REVERSE_ENDIAN BIT(6) -#define JZ_LCD_CTRL_LSB_FISRT BIT(5) -#define JZ_LCD_CTRL_DISABLE BIT(4) -#define JZ_LCD_CTRL_ENABLE BIT(3) -#define JZ_LCD_CTRL_BPP_1 0x0 -#define JZ_LCD_CTRL_BPP_2 0x1 -#define JZ_LCD_CTRL_BPP_4 0x2 -#define JZ_LCD_CTRL_BPP_8 0x3 -#define JZ_LCD_CTRL_BPP_15_16 0x4 -#define JZ_LCD_CTRL_BPP_18_24 0x5 -#define JZ_LCD_CTRL_BPP_MASK (JZ_LCD_CTRL_RGB555 | (0x7 << 0)) - -#define JZ_LCD_CMD_SOF_IRQ BIT(31) -#define JZ_LCD_CMD_EOF_IRQ BIT(30) -#define JZ_LCD_CMD_ENABLE_PAL BIT(28) - -#define JZ_LCD_SYNC_MASK 0x3ff - -#define JZ_LCD_STATE_EOF_IRQ BIT(5) -#define JZ_LCD_STATE_SOF_IRQ BIT(4) -#define JZ_LCD_STATE_DISABLED BIT(0) - struct ingenic_dma_hwdesc { u32 next; u32 addr; diff --git a/drivers/gpu/drm/ingenic/ingenic-drm.h b/drivers/gpu/drm/ingenic/ingenic-drm.h new file mode 100644 index 000000000000..cb578cff7bb1 --- /dev/null +++ b/drivers/gpu/drm/ingenic/ingenic-drm.h @@ -0,0 +1,126 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +// +// Ingenic JZ47xx KMS driver - Register definitions and private API +// +// Copyright (C) 2020, Paul Cercueil + +#ifndef DRIVERS_GPU_DRM_INGENIC_INGENIC_DRM_H +#define DRIVERS_GPU_DRM_INGENIC_INGENIC_DRM_H + +#include + +#define JZ_REG_LCD_CFG 0x00 +#define JZ_REG_LCD_VSYNC 0x04 +#define JZ_REG_LCD_HSYNC 0x08 +#define JZ_REG_LCD_VAT 0x0C +#define JZ_REG_LCD_DAH 0x10 +#define JZ_REG_LCD_DAV 0x14 +#define JZ_REG_LCD_PS 0x18 +#define JZ_REG_LCD_CLS 0x1C +#define JZ_REG_LCD_SPL 0x20 +#define JZ_REG_LCD_REV 0x24 +#define JZ_REG_LCD_CTRL 0x30 +#define JZ_REG_LCD_STATE 0x34 +#define JZ_REG_LCD_IID 0x38 +#define JZ_REG_LCD_DA0 0x40 +#define JZ_REG_LCD_SA0 0x44 +#define JZ_REG_LCD_FID0 0x48 +#define JZ_REG_LCD_CMD0 0x4C +#define JZ_REG_LCD_DA1 0x50 +#define JZ_REG_LCD_SA1 0x54 +#define JZ_REG_LCD_FID1 0x58 +#define JZ_REG_LCD_CMD1 0x5C + +#define JZ_LCD_CFG_SLCD BIT(31) +#define JZ_LCD_CFG_PS_DISABLE BIT(23) +#define JZ_LCD_CFG_CLS_DISABLE BIT(22) +#define JZ_LCD_CFG_SPL_DISABLE BIT(21) +#define JZ_LCD_CFG_REV_DISABLE BIT(20) +#define JZ_LCD_CFG_HSYNCM BIT(19) +#define JZ_LCD_CFG_PCLKM BIT(18) +#define JZ_LCD_CFG_INV BIT(17) +#define JZ_LCD_CFG_SYNC_DIR BIT(16) +#define JZ_LCD_CFG_PS_POLARITY BIT(15) +#define JZ_LCD_CFG_CLS_POLARITY BIT(14) +#define JZ_LCD_CFG_SPL_POLARITY BIT(13) +#define JZ_LCD_CFG_REV_POLARITY BIT(12) +#define JZ_LCD_CFG_HSYNC_ACTIVE_LOW BIT(11) +#define JZ_LCD_CFG_PCLK_FALLING_EDGE BIT(10) +#define JZ_LCD_CFG_DE_ACTIVE_LOW BIT(9) +#define JZ_LCD_CFG_VSYNC_ACTIVE_LOW BIT(8) +#define JZ_LCD_CFG_18_BIT BIT(7) +#define JZ_LCD_CFG_PDW (BIT(5) | BIT(4)) + +#define JZ_LCD_CFG_MODE_GENERIC_16BIT 0 +#define JZ_LCD_CFG_MODE_GENERIC_18BIT BIT(7) +#define JZ_LCD_CFG_MODE_GENERIC_24BIT BIT(6) + +#define JZ_LCD_CFG_MODE_SPECIAL_TFT_1 1 +#define JZ_LCD_CFG_MODE_SPECIAL_TFT_2 2 +#define JZ_LCD_CFG_MODE_SPECIAL_TFT_3 3 + +#define JZ_LCD_CFG_MODE_TV_OUT_P 4 +#define JZ_LCD_CFG_MODE_TV_OUT_I 6 + +#define JZ_LCD_CFG_MODE_SINGLE_COLOR_STN 8 +#define JZ_LCD_CFG_MODE_SINGLE_MONOCHROME_STN 9 +#define JZ_LCD_CFG_MODE_DUAL_COLOR_STN 10 +#define JZ_LCD_CFG_MODE_DUAL_MONOCHROME_STN 11 + +#define JZ_LCD_CFG_MODE_8BIT_SERIAL 12 +#define JZ_LCD_CFG_MODE_LCM 13 + +#define JZ_LCD_VSYNC_VPS_OFFSET 16 +#define JZ_LCD_VSYNC_VPE_OFFSET 0 + +#define JZ_LCD_HSYNC_HPS_OFFSET 16 +#define JZ_LCD_HSYNC_HPE_OFFSET 0 + +#define JZ_LCD_VAT_HT_OFFSET 16 +#define JZ_LCD_VAT_VT_OFFSET 0 + +#define JZ_LCD_DAH_HDS_OFFSET 16 +#define JZ_LCD_DAH_HDE_OFFSET 0 + +#define JZ_LCD_DAV_VDS_OFFSET 16 +#define JZ_LCD_DAV_VDE_OFFSET 0 + +#define JZ_LCD_CTRL_BURST_4 (0x0 << 28) +#define JZ_LCD_CTRL_BURST_8 (0x1 << 28) +#define JZ_LCD_CTRL_BURST_16 (0x2 << 28) +#define JZ_LCD_CTRL_RGB555 BIT(27) +#define JZ_LCD_CTRL_OFUP BIT(26) +#define JZ_LCD_CTRL_FRC_GRAYSCALE_16 (0x0 << 24) +#define JZ_LCD_CTRL_FRC_GRAYSCALE_4 (0x1 << 24) +#define JZ_LCD_CTRL_FRC_GRAYSCALE_2 (0x2 << 24) +#define JZ_LCD_CTRL_PDD_MASK (0xff << 16) +#define JZ_LCD_CTRL_EOF_IRQ BIT(13) +#define JZ_LCD_CTRL_SOF_IRQ BIT(12) +#define JZ_LCD_CTRL_OFU_IRQ BIT(11) +#define JZ_LCD_CTRL_IFU0_IRQ BIT(10) +#define JZ_LCD_CTRL_IFU1_IRQ BIT(9) +#define JZ_LCD_CTRL_DD_IRQ BIT(8) +#define JZ_LCD_CTRL_QDD_IRQ BIT(7) +#define JZ_LCD_CTRL_REVERSE_ENDIAN BIT(6) +#define JZ_LCD_CTRL_LSB_FISRT BIT(5) +#define JZ_LCD_CTRL_DISABLE BIT(4) +#define JZ_LCD_CTRL_ENABLE BIT(3) +#define JZ_LCD_CTRL_BPP_1 0x0 +#define JZ_LCD_CTRL_BPP_2 0x1 +#define JZ_LCD_CTRL_BPP_4 0x2 +#define JZ_LCD_CTRL_BPP_8 0x3 +#define JZ_LCD_CTRL_BPP_15_16 0x4 +#define JZ_LCD_CTRL_BPP_18_24 0x5 +#define JZ_LCD_CTRL_BPP_MASK (JZ_LCD_CTRL_RGB555 | 0x7) + +#define JZ_LCD_CMD_SOF_IRQ BIT(31) +#define JZ_LCD_CMD_EOF_IRQ BIT(30) +#define JZ_LCD_CMD_ENABLE_PAL BIT(28) + +#define JZ_LCD_SYNC_MASK 0x3ff + +#define JZ_LCD_STATE_EOF_IRQ BIT(5) +#define JZ_LCD_STATE_SOF_IRQ BIT(4) +#define JZ_LCD_STATE_DISABLED BIT(0) + +#endif /* DRIVERS_GPU_DRM_INGENIC_INGENIC_DRM_H */ From 0a746db7ad9210ae4eac4ea1f2834778fee89859 Mon Sep 17 00:00:00 2001 From: Paul Cercueil Date: Thu, 16 Jul 2020 18:38:42 +0200 Subject: [PATCH 23/59] drm/ingenic: Use dmam_alloc_coherent() Use dmam_alloc_coherent() instead of dma_alloc_coherent(). Then we don't need to register a custom cleanup handler. v3: New patch Signed-off-by: Paul Cercueil Reviewed-by: Sam Ravnborg Link: https://patchwork.freedesktop.org/patch/msgid/20200716163846.174790-8-paul@crapouillou.net --- drivers/gpu/drm/ingenic/ingenic-drm-drv.c | 18 +++--------------- 1 file changed, 3 insertions(+), 15 deletions(-) diff --git a/drivers/gpu/drm/ingenic/ingenic-drm-drv.c b/drivers/gpu/drm/ingenic/ingenic-drm-drv.c index d9cd74d70a61..9cc785776594 100644 --- a/drivers/gpu/drm/ingenic/ingenic-drm-drv.c +++ b/drivers/gpu/drm/ingenic/ingenic-drm-drv.c @@ -459,14 +459,6 @@ static const struct drm_mode_config_funcs ingenic_drm_mode_config_funcs = { .atomic_commit = drm_atomic_helper_commit, }; -static void ingenic_drm_free_dma_hwdesc(void *d) -{ - struct ingenic_drm *priv = d; - - dma_free_coherent(priv->dev, sizeof(*priv->dma_hwdesc), - priv->dma_hwdesc, priv->dma_hwdesc_phys); -} - static int ingenic_drm_probe(struct platform_device *pdev) { const struct jz_soc_info *soc_info; @@ -549,16 +541,12 @@ static int ingenic_drm_probe(struct platform_device *pdev) bridge = devm_drm_panel_bridge_add_typed(dev, panel, DRM_MODE_CONNECTOR_DPI); - priv->dma_hwdesc = dma_alloc_coherent(dev, sizeof(*priv->dma_hwdesc), - &priv->dma_hwdesc_phys, - GFP_KERNEL); + priv->dma_hwdesc = dmam_alloc_coherent(dev, sizeof(*priv->dma_hwdesc), + &priv->dma_hwdesc_phys, + GFP_KERNEL); if (!priv->dma_hwdesc) return -ENOMEM; - ret = devm_add_action_or_reset(dev, ingenic_drm_free_dma_hwdesc, priv); - if (ret) - return ret; - priv->dma_hwdesc->next = priv->dma_hwdesc_phys; priv->dma_hwdesc->id = 0xdeafbead; From 3c9bea4ef32bdcde5bda850b4fa989b936e75f4b Mon Sep 17 00:00:00 2001 From: Paul Cercueil Date: Thu, 16 Jul 2020 18:38:43 +0200 Subject: [PATCH 24/59] drm/ingenic: Add support for OSD mode All Ingenic SoCs starting from the JZ4725B support OSD mode. In this mode, two separate planes can be used. They can have different positions and sizes, and one can be overlayed on top of the other. v2: Use fallthrough; instead of /* fall-through */ v3: - Add custom atomic_tail function to handle case where HW gives no VBLANK - Use regmap_set_bits() / regmap_clear_bits() when possible - Use dma_hwdesc_f{0,1} fields in priv structure instead of array - Use dmam_alloc_coherent() instead of dma_alloc_coherent() - Use more meaningful 0xf0 / 0xf1 values as DMA descriptors IDs - Add a bit more code comments Signed-off-by: Paul Cercueil Reviewed-by: Sam Ravnborg Link: https://patchwork.freedesktop.org/patch/msgid/20200716163846.174790-9-paul@crapouillou.net --- drivers/gpu/drm/ingenic/ingenic-drm-drv.c | 303 ++++++++++++++++++---- drivers/gpu/drm/ingenic/ingenic-drm.h | 35 +++ 2 files changed, 288 insertions(+), 50 deletions(-) diff --git a/drivers/gpu/drm/ingenic/ingenic-drm-drv.c b/drivers/gpu/drm/ingenic/ingenic-drm-drv.c index 9cc785776594..578ed58a22d4 100644 --- a/drivers/gpu/drm/ingenic/ingenic-drm-drv.c +++ b/drivers/gpu/drm/ingenic/ingenic-drm-drv.c @@ -43,12 +43,18 @@ struct ingenic_dma_hwdesc { struct jz_soc_info { bool needs_dev_clk; + bool has_osd; unsigned int max_width, max_height; }; struct ingenic_drm { struct drm_device drm; - struct drm_plane primary; + /* + * f1 (aka. foreground1) is our primary plane, on top of which + * f0 (aka. foreground0) can be overlayed. Z-order is fixed in + * hardware and cannot be changed. + */ + struct drm_plane f0, f1; struct drm_crtc crtc; struct drm_encoder encoder; @@ -57,10 +63,11 @@ struct ingenic_drm { struct clk *lcd_clk, *pix_clk; const struct jz_soc_info *soc_info; - struct ingenic_dma_hwdesc *dma_hwdesc; - dma_addr_t dma_hwdesc_phys; + struct ingenic_dma_hwdesc *dma_hwdesc_f0, *dma_hwdesc_f1; + dma_addr_t dma_hwdesc_phys_f0, dma_hwdesc_phys_f1; bool panel_is_sharp; + bool no_vblank; }; static const u32 ingenic_drm_primary_formats[] = { @@ -90,7 +97,7 @@ static const struct regmap_config ingenic_drm_regmap_config = { .val_bits = 32, .reg_stride = 4, - .max_register = JZ_REG_LCD_CMD1, + .max_register = JZ_REG_LCD_SIZE1, .writeable_reg = ingenic_drm_writeable_reg, }; @@ -110,11 +117,6 @@ drm_encoder_get_priv(struct drm_encoder *encoder) return container_of(encoder, struct ingenic_drm, encoder); } -static inline struct ingenic_drm *drm_plane_get_priv(struct drm_plane *plane) -{ - return container_of(plane, struct ingenic_drm, primary); -} - static void ingenic_drm_crtc_atomic_enable(struct drm_crtc *crtc, struct drm_crtc_state *state) { @@ -185,34 +187,16 @@ static void ingenic_drm_crtc_update_timings(struct ingenic_drm *priv, regmap_write(priv->map, JZ_REG_LCD_SPL, hpe << 16 | (hpe + 1)); regmap_write(priv->map, JZ_REG_LCD_REV, mode->htotal << 16); } -} -static void ingenic_drm_crtc_update_ctrl(struct ingenic_drm *priv, - const struct drm_format_info *finfo) -{ - unsigned int ctrl = JZ_LCD_CTRL_OFUP | JZ_LCD_CTRL_BURST_16; - - switch (finfo->format) { - case DRM_FORMAT_XRGB1555: - ctrl |= JZ_LCD_CTRL_RGB555; - /* fall-through */ - case DRM_FORMAT_RGB565: - ctrl |= JZ_LCD_CTRL_BPP_15_16; - break; - case DRM_FORMAT_XRGB8888: - ctrl |= JZ_LCD_CTRL_BPP_18_24; - break; - } - - regmap_update_bits(priv->map, JZ_REG_LCD_CTRL, - JZ_LCD_CTRL_OFUP | JZ_LCD_CTRL_BURST_16 | - JZ_LCD_CTRL_BPP_MASK, ctrl); + regmap_set_bits(priv->map, JZ_REG_LCD_CTRL, + JZ_LCD_CTRL_OFUP | JZ_LCD_CTRL_BURST_16); } static int ingenic_drm_crtc_atomic_check(struct drm_crtc *crtc, struct drm_crtc_state *state) { struct ingenic_drm *priv = drm_crtc_get_priv(crtc); + struct drm_plane_state *f1_state, *f0_state; long rate; if (!drm_atomic_crtc_needs_modeset(state)) @@ -227,6 +211,14 @@ static int ingenic_drm_crtc_atomic_check(struct drm_crtc *crtc, if (rate < 0) return rate; + if (priv->soc_info->has_osd) { + f1_state = drm_atomic_get_plane_state(state->state, &priv->f1); + f0_state = drm_atomic_get_plane_state(state->state, &priv->f0); + + /* If all the planes are disabled, we won't get a VBLANK IRQ */ + priv->no_vblank = !f1_state->fb && !f0_state->fb; + } + return 0; } @@ -236,14 +228,9 @@ static void ingenic_drm_crtc_atomic_flush(struct drm_crtc *crtc, struct ingenic_drm *priv = drm_crtc_get_priv(crtc); struct drm_crtc_state *state = crtc->state; struct drm_pending_vblank_event *event = state->event; - struct drm_framebuffer *drm_fb = crtc->primary->state->fb; - const struct drm_format_info *finfo; if (drm_atomic_crtc_needs_modeset(state)) { - finfo = drm_format_info(drm_fb->format->format); - ingenic_drm_crtc_update_timings(priv, &state->mode); - ingenic_drm_crtc_update_ctrl(priv, finfo); clk_set_rate(priv->pix_clk, state->adjusted_mode.clock * 1000); } @@ -260,11 +247,152 @@ static void ingenic_drm_crtc_atomic_flush(struct drm_crtc *crtc, } } +static int ingenic_drm_plane_atomic_check(struct drm_plane *plane, + struct drm_plane_state *state) +{ + struct ingenic_drm *priv = drm_device_get_priv(plane->dev); + struct drm_crtc_state *crtc_state; + struct drm_crtc *crtc = state->crtc ?: plane->state->crtc; + int ret; + + if (!crtc) + return 0; + + crtc_state = drm_atomic_get_existing_crtc_state(state->state, crtc); + if (WARN_ON(!crtc_state)) + return -EINVAL; + + ret = drm_atomic_helper_check_plane_state(state, crtc_state, + DRM_PLANE_HELPER_NO_SCALING, + DRM_PLANE_HELPER_NO_SCALING, + priv->soc_info->has_osd, + true); + if (ret) + return ret; + + /* + * If OSD is not available, check that the width/height match. + * Note that state->src_* are in 16.16 fixed-point format. + */ + if (!priv->soc_info->has_osd && + (state->src_x != 0 || + (state->src_w >> 16) != state->crtc_w || + (state->src_h >> 16) != state->crtc_h)) + return -EINVAL; + + /* + * Require full modeset if enabling or disabling a plane, or changing + * its position, size or depth. + */ + if (priv->soc_info->has_osd && + (!plane->state->fb || !state->fb || + plane->state->crtc_x != state->crtc_x || + plane->state->crtc_y != state->crtc_y || + plane->state->crtc_w != state->crtc_w || + plane->state->crtc_h != state->crtc_h || + plane->state->fb->format->format != state->fb->format->format)) + crtc_state->mode_changed = true; + + return 0; +} + +static void ingenic_drm_plane_enable(struct ingenic_drm *priv, + struct drm_plane *plane) +{ + unsigned int en_bit; + + if (priv->soc_info->has_osd) { + if (plane->type == DRM_PLANE_TYPE_PRIMARY) + en_bit = JZ_LCD_OSDC_F1EN; + else + en_bit = JZ_LCD_OSDC_F0EN; + + regmap_set_bits(priv->map, JZ_REG_LCD_OSDC, en_bit); + } +} + +static void ingenic_drm_plane_atomic_disable(struct drm_plane *plane, + struct drm_plane_state *old_state) +{ + struct ingenic_drm *priv = drm_device_get_priv(plane->dev); + unsigned int en_bit; + + if (priv->soc_info->has_osd) { + if (plane->type == DRM_PLANE_TYPE_PRIMARY) + en_bit = JZ_LCD_OSDC_F1EN; + else + en_bit = JZ_LCD_OSDC_F0EN; + + regmap_clear_bits(priv->map, JZ_REG_LCD_OSDC, en_bit); + } +} + +static void ingenic_drm_plane_config(struct ingenic_drm *priv, + struct drm_plane *plane, u32 fourcc) +{ + struct drm_plane_state *state = plane->state; + unsigned int xy_reg, size_reg; + unsigned int ctrl = 0; + + ingenic_drm_plane_enable(priv, plane); + + if (priv->soc_info->has_osd && + plane->type == DRM_PLANE_TYPE_PRIMARY) { + switch (fourcc) { + case DRM_FORMAT_XRGB1555: + ctrl |= JZ_LCD_OSDCTRL_RGB555; + fallthrough; + case DRM_FORMAT_RGB565: + ctrl |= JZ_LCD_OSDCTRL_BPP_15_16; + break; + case DRM_FORMAT_XRGB8888: + ctrl |= JZ_LCD_OSDCTRL_BPP_18_24; + break; + } + + regmap_update_bits(priv->map, JZ_REG_LCD_OSDCTRL, + JZ_LCD_OSDCTRL_BPP_MASK, ctrl); + } else { + switch (fourcc) { + case DRM_FORMAT_XRGB1555: + ctrl |= JZ_LCD_CTRL_RGB555; + fallthrough; + case DRM_FORMAT_RGB565: + ctrl |= JZ_LCD_CTRL_BPP_15_16; + break; + case DRM_FORMAT_XRGB8888: + ctrl |= JZ_LCD_CTRL_BPP_18_24; + break; + } + + regmap_update_bits(priv->map, JZ_REG_LCD_CTRL, + JZ_LCD_CTRL_BPP_MASK, ctrl); + } + + if (priv->soc_info->has_osd) { + if (plane->type == DRM_PLANE_TYPE_PRIMARY) { + xy_reg = JZ_REG_LCD_XYP1; + size_reg = JZ_REG_LCD_SIZE1; + } else { + xy_reg = JZ_REG_LCD_XYP0; + size_reg = JZ_REG_LCD_SIZE0; + } + + regmap_write(priv->map, xy_reg, + state->crtc_x << JZ_LCD_XYP01_XPOS_LSB | + state->crtc_y << JZ_LCD_XYP01_YPOS_LSB); + regmap_write(priv->map, size_reg, + state->crtc_w << JZ_LCD_SIZE01_WIDTH_LSB | + state->crtc_h << JZ_LCD_SIZE01_HEIGHT_LSB); + } +} + static void ingenic_drm_plane_atomic_update(struct drm_plane *plane, struct drm_plane_state *oldstate) { - struct ingenic_drm *priv = drm_plane_get_priv(plane); + struct ingenic_drm *priv = drm_device_get_priv(plane->dev); struct drm_plane_state *state = plane->state; + struct ingenic_dma_hwdesc *hwdesc; unsigned int width, height, cpp; dma_addr_t addr; @@ -274,9 +402,17 @@ static void ingenic_drm_plane_atomic_update(struct drm_plane *plane, height = state->src_h >> 16; cpp = state->fb->format->cpp[0]; - priv->dma_hwdesc->addr = addr; - priv->dma_hwdesc->cmd = width * height * cpp / 4; - priv->dma_hwdesc->cmd |= JZ_LCD_CMD_EOF_IRQ; + if (priv->soc_info->has_osd && plane->type == DRM_PLANE_TYPE_OVERLAY) + hwdesc = priv->dma_hwdesc_f0; + else + hwdesc = priv->dma_hwdesc_f1; + + hwdesc->addr = addr; + hwdesc->cmd = JZ_LCD_CMD_EOF_IRQ | (width * height * cpp / 4); + + if (drm_atomic_crtc_needs_modeset(state->crtc->state)) + ingenic_drm_plane_config(priv, plane, + state->fb->format->format); } } @@ -360,6 +496,29 @@ static int ingenic_drm_encoder_atomic_check(struct drm_encoder *encoder, } } +static void ingenic_drm_atomic_helper_commit_tail(struct drm_atomic_state *old_state) +{ + /* + * Just your regular drm_atomic_helper_commit_tail(), but only calls + * drm_atomic_helper_wait_for_vblanks() if priv->no_vblank. + */ + struct drm_device *dev = old_state->dev; + struct ingenic_drm *priv = drm_device_get_priv(dev); + + drm_atomic_helper_commit_modeset_disables(dev, old_state); + + drm_atomic_helper_commit_planes(dev, old_state, 0); + + drm_atomic_helper_commit_modeset_enables(dev, old_state); + + drm_atomic_helper_commit_hw_done(old_state); + + if (!priv->no_vblank) + drm_atomic_helper_wait_for_vblanks(dev, old_state); + + drm_atomic_helper_cleanup_planes(dev, old_state); +} + static irqreturn_t ingenic_drm_irq_handler(int irq, void *arg) { struct ingenic_drm *priv = drm_device_get_priv(arg); @@ -437,6 +596,8 @@ static const struct drm_crtc_funcs ingenic_drm_crtc_funcs = { static const struct drm_plane_helper_funcs ingenic_drm_plane_helper_funcs = { .atomic_update = ingenic_drm_plane_atomic_update, + .atomic_check = ingenic_drm_plane_atomic_check, + .atomic_disable = ingenic_drm_plane_atomic_disable, .prepare_fb = drm_gem_fb_prepare_fb, }; @@ -459,6 +620,10 @@ static const struct drm_mode_config_funcs ingenic_drm_mode_config_funcs = { .atomic_commit = drm_atomic_helper_commit, }; +static struct drm_mode_config_helper_funcs ingenic_drm_mode_config_helpers = { + .atomic_commit_tail = ingenic_drm_atomic_helper_commit_tail, +}; + static int ingenic_drm_probe(struct platform_device *pdev) { const struct jz_soc_info *soc_info; @@ -498,6 +663,7 @@ static int ingenic_drm_probe(struct platform_device *pdev) drm->mode_config.max_width = soc_info->max_width; drm->mode_config.max_height = 4095; drm->mode_config.funcs = &ingenic_drm_mode_config_funcs; + drm->mode_config.helper_private = &ingenic_drm_mode_config_helpers; base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(base)) { @@ -541,19 +707,31 @@ static int ingenic_drm_probe(struct platform_device *pdev) bridge = devm_drm_panel_bridge_add_typed(dev, panel, DRM_MODE_CONNECTOR_DPI); - priv->dma_hwdesc = dmam_alloc_coherent(dev, sizeof(*priv->dma_hwdesc), - &priv->dma_hwdesc_phys, - GFP_KERNEL); - if (!priv->dma_hwdesc) + priv->dma_hwdesc_f1 = dmam_alloc_coherent(dev, sizeof(*priv->dma_hwdesc_f1), + &priv->dma_hwdesc_phys_f1, + GFP_KERNEL); + if (!priv->dma_hwdesc_f1) return -ENOMEM; - priv->dma_hwdesc->next = priv->dma_hwdesc_phys; - priv->dma_hwdesc->id = 0xdeafbead; + priv->dma_hwdesc_f1->next = priv->dma_hwdesc_phys_f1; + priv->dma_hwdesc_f1->id = 0xf1; - drm_plane_helper_add(&priv->primary, &ingenic_drm_plane_helper_funcs); + if (priv->soc_info->has_osd) { + priv->dma_hwdesc_f0 = dmam_alloc_coherent(dev, + sizeof(*priv->dma_hwdesc_f0), + &priv->dma_hwdesc_phys_f0, + GFP_KERNEL); + if (!priv->dma_hwdesc_f0) + return -ENOMEM; - ret = drm_universal_plane_init(drm, &priv->primary, - 0, &ingenic_drm_primary_plane_funcs, + priv->dma_hwdesc_f0->next = priv->dma_hwdesc_phys_f0; + priv->dma_hwdesc_f0->id = 0xf0; + } + + drm_plane_helper_add(&priv->f1, &ingenic_drm_plane_helper_funcs); + + ret = drm_universal_plane_init(drm, &priv->f1, 1, + &ingenic_drm_primary_plane_funcs, ingenic_drm_primary_formats, ARRAY_SIZE(ingenic_drm_primary_formats), NULL, DRM_PLANE_TYPE_PRIMARY, NULL); @@ -564,13 +742,30 @@ static int ingenic_drm_probe(struct platform_device *pdev) drm_crtc_helper_add(&priv->crtc, &ingenic_drm_crtc_helper_funcs); - ret = drm_crtc_init_with_planes(drm, &priv->crtc, &priv->primary, + ret = drm_crtc_init_with_planes(drm, &priv->crtc, &priv->f1, NULL, &ingenic_drm_crtc_funcs, NULL); if (ret) { dev_err(dev, "Failed to init CRTC: %i\n", ret); return ret; } + if (soc_info->has_osd) { + drm_plane_helper_add(&priv->f0, + &ingenic_drm_plane_helper_funcs); + + ret = drm_universal_plane_init(drm, &priv->f0, 1, + &ingenic_drm_primary_plane_funcs, + ingenic_drm_primary_formats, + ARRAY_SIZE(ingenic_drm_primary_formats), + NULL, DRM_PLANE_TYPE_OVERLAY, + NULL); + if (ret) { + dev_err(dev, "Failed to register overlay plane: %i\n", + ret); + return ret; + } + } + priv->encoder.possible_crtcs = 1; drm_encoder_helper_add(&priv->encoder, @@ -632,7 +827,12 @@ static int ingenic_drm_probe(struct platform_device *pdev) } /* Set address of our DMA descriptor chain */ - regmap_write(priv->map, JZ_REG_LCD_DA0, priv->dma_hwdesc_phys); + regmap_write(priv->map, JZ_REG_LCD_DA0, priv->dma_hwdesc_phys_f0); + regmap_write(priv->map, JZ_REG_LCD_DA1, priv->dma_hwdesc_phys_f1); + + /* Enable OSD if available */ + if (soc_info->has_osd) + regmap_write(priv->map, JZ_REG_LCD_OSDC, JZ_LCD_OSDC_OSDEN); ret = drm_dev_register(drm, 0); if (ret) { @@ -668,18 +868,21 @@ static int ingenic_drm_remove(struct platform_device *pdev) static const struct jz_soc_info jz4740_soc_info = { .needs_dev_clk = true, + .has_osd = false, .max_width = 800, .max_height = 600, }; static const struct jz_soc_info jz4725b_soc_info = { .needs_dev_clk = false, + .has_osd = true, .max_width = 800, .max_height = 600, }; static const struct jz_soc_info jz4770_soc_info = { .needs_dev_clk = false, + .has_osd = true, .max_width = 1280, .max_height = 720, }; diff --git a/drivers/gpu/drm/ingenic/ingenic-drm.h b/drivers/gpu/drm/ingenic/ingenic-drm.h index cb578cff7bb1..d0b827a9fe83 100644 --- a/drivers/gpu/drm/ingenic/ingenic-drm.h +++ b/drivers/gpu/drm/ingenic/ingenic-drm.h @@ -30,6 +30,18 @@ #define JZ_REG_LCD_SA1 0x54 #define JZ_REG_LCD_FID1 0x58 #define JZ_REG_LCD_CMD1 0x5C +#define JZ_REG_LCD_OSDC 0x100 +#define JZ_REG_LCD_OSDCTRL 0x104 +#define JZ_REG_LCD_OSDS 0x108 +#define JZ_REG_LCD_BGC 0x10c +#define JZ_REG_LCD_KEY0 0x110 +#define JZ_REG_LCD_KEY1 0x114 +#define JZ_REG_LCD_ALPHA 0x118 +#define JZ_REG_LCD_IPUR 0x11c +#define JZ_REG_LCD_XYP0 0x120 +#define JZ_REG_LCD_XYP1 0x124 +#define JZ_REG_LCD_SIZE0 0x128 +#define JZ_REG_LCD_SIZE1 0x12c #define JZ_LCD_CFG_SLCD BIT(31) #define JZ_LCD_CFG_PS_DISABLE BIT(23) @@ -123,4 +135,27 @@ #define JZ_LCD_STATE_SOF_IRQ BIT(4) #define JZ_LCD_STATE_DISABLED BIT(0) +#define JZ_LCD_OSDC_OSDEN BIT(0) +#define JZ_LCD_OSDC_F0EN BIT(3) +#define JZ_LCD_OSDC_F1EN BIT(4) + +#define JZ_LCD_OSDCTRL_IPU BIT(15) +#define JZ_LCD_OSDCTRL_RGB555 BIT(4) +#define JZ_LCD_OSDCTRL_CHANGE BIT(3) +#define JZ_LCD_OSDCTRL_BPP_15_16 0x4 +#define JZ_LCD_OSDCTRL_BPP_18_24 0x5 +#define JZ_LCD_OSDCTRL_BPP_30 0x7 +#define JZ_LCD_OSDCTRL_BPP_MASK (JZ_LCD_OSDCTRL_RGB555 | 0x7) + +#define JZ_LCD_OSDS_READY BIT(0) + +#define JZ_LCD_IPUR_IPUREN BIT(31) +#define JZ_LCD_IPUR_IPUR_LSB 0 + +#define JZ_LCD_XYP01_XPOS_LSB 0 +#define JZ_LCD_XYP01_YPOS_LSB 16 + +#define JZ_LCD_SIZE01_WIDTH_LSB 0 +#define JZ_LCD_SIZE01_HEIGHT_LSB 16 + #endif /* DRIVERS_GPU_DRM_INGENIC_INGENIC_DRM_H */ From fc1acf317b01083d47228c0d21cfc0764f37a04e Mon Sep 17 00:00:00 2001 From: Paul Cercueil Date: Thu, 16 Jul 2020 18:38:44 +0200 Subject: [PATCH 25/59] drm/ingenic: Add support for the IPU Add support for the Image Processing Unit (IPU) found in all Ingenic SoCs. The IPU can upscale and downscale a source frame of arbitrary size ranging from 4x4 to 4096x4096 on newer SoCs, with bicubic filtering on newer SoCs, bilinear filtering on older SoCs. Nearest-neighbour can also be obtained with proper coefficients. Starting from the JZ4725B, the IPU supports a mode where its output is sent directly to the LCDC, without having to be written to RAM first. This makes it possible to use the IPU as a DRM plane on the compatible SoCs, and have it convert and scale anything the userspace asks for to what's available for the display. Regarding pixel formats, older SoCs support packed YUV 4:2:2 and various planar YUV formats. Newer SoCs introduced support for RGB. Since the IPU is a separate hardware block, to make it work properly the Ingenic DRM driver will now register itself as a component master in case the IPU driver has been enabled in the config. When enabled in the config, the CRTC will see the IPU as a second primary plane. It cannot be enabled at the same time as the regular primary plane. It has the same priority, which means that it will also display below the overlay plane. v2: - ingenic-ipu is no longer its own module. It will be built into the ingenic-drm module. - If enabled in the config, both the core driver and the IPU driver will register as components; otherwise the core driver will bypass that and call the ingenic_drm_bind() function directly. - Since both files now build into the same module, the symbols previously exported as GPL are not exported anymore, since they are only used internally. - Fix SPDX license header in ingenic-ipu.h - Avoid using 'for(;;);' loops without trailing statement(s) v3: - Pass priv structure to IRQ handler; that way we don't hardcode the expectation that the IPU plane is at index #0. - Rework osd_changed() to account for src_* changes - Add multiplanar YUV 4:4:4 support - Commit fb addresses to HW at vblank, since addr registers are not shadow registers - Probe IPU component later so that IPU plane is last - Fix driver not working on IPU-less hardware - Use IPU driver's name as the IRQ name to avoid having two 'ingenic-drm' in /proc/interrupts - Fix IPU only working for still images on JZ4725B - Add a bit more code comments Signed-off-by: Paul Cercueil Reviewed-by: Sam Ravnborg Link: https://patchwork.freedesktop.org/patch/msgid/20200716163846.174790-10-paul@crapouillou.net --- drivers/gpu/drm/ingenic/Kconfig | 11 + drivers/gpu/drm/ingenic/Makefile | 3 +- drivers/gpu/drm/ingenic/ingenic-drm-drv.c | 171 ++++- drivers/gpu/drm/ingenic/ingenic-drm.h | 12 + drivers/gpu/drm/ingenic/ingenic-ipu.c | 853 ++++++++++++++++++++++ drivers/gpu/drm/ingenic/ingenic-ipu.h | 109 +++ 6 files changed, 1143 insertions(+), 16 deletions(-) create mode 100644 drivers/gpu/drm/ingenic/ingenic-ipu.c create mode 100644 drivers/gpu/drm/ingenic/ingenic-ipu.h diff --git a/drivers/gpu/drm/ingenic/Kconfig b/drivers/gpu/drm/ingenic/Kconfig index d82c3d37ec9c..477d5387e43e 100644 --- a/drivers/gpu/drm/ingenic/Kconfig +++ b/drivers/gpu/drm/ingenic/Kconfig @@ -14,3 +14,14 @@ config DRM_INGENIC Choose this option for DRM support for the Ingenic SoCs. If M is selected the module will be called ingenic-drm. + +if DRM_INGENIC + +config DRM_INGENIC_IPU + bool "IPU support for Ingenic SoCs" + help + Choose this option to enable support for the IPU found in Ingenic SoCs. + + The Image Processing Unit (IPU) will appear as a second primary plane. + +endif diff --git a/drivers/gpu/drm/ingenic/Makefile b/drivers/gpu/drm/ingenic/Makefile index 9875628dd8bc..d313326bdddb 100644 --- a/drivers/gpu/drm/ingenic/Makefile +++ b/drivers/gpu/drm/ingenic/Makefile @@ -1,2 +1,3 @@ obj-$(CONFIG_DRM_INGENIC) += ingenic-drm.o -ingenic-drm-y += ingenic-drm-drv.o +ingenic-drm-y = ingenic-drm-drv.o +ingenic-drm-$(CONFIG_DRM_INGENIC_IPU) += ingenic-ipu.o diff --git a/drivers/gpu/drm/ingenic/ingenic-drm-drv.c b/drivers/gpu/drm/ingenic/ingenic-drm-drv.c index 578ed58a22d4..bc2a7e9bab07 100644 --- a/drivers/gpu/drm/ingenic/ingenic-drm-drv.c +++ b/drivers/gpu/drm/ingenic/ingenic-drm-drv.c @@ -6,6 +6,7 @@ #include "ingenic-drm.h" +#include #include #include #include @@ -54,7 +55,7 @@ struct ingenic_drm { * f0 (aka. foreground0) can be overlayed. Z-order is fixed in * hardware and cannot be changed. */ - struct drm_plane f0, f1; + struct drm_plane f0, f1, *ipu_plane; struct drm_crtc crtc; struct drm_encoder encoder; @@ -190,13 +191,21 @@ static void ingenic_drm_crtc_update_timings(struct ingenic_drm *priv, regmap_set_bits(priv->map, JZ_REG_LCD_CTRL, JZ_LCD_CTRL_OFUP | JZ_LCD_CTRL_BURST_16); + + /* + * IPU restart - specify how much time the LCDC will wait before + * transferring a new frame from the IPU. The value is the one + * suggested in the programming manual. + */ + regmap_write(priv->map, JZ_REG_LCD_IPUR, JZ_LCD_IPUR_IPUREN | + (ht * vpe / 3) << JZ_LCD_IPUR_IPUR_LSB); } static int ingenic_drm_crtc_atomic_check(struct drm_crtc *crtc, struct drm_crtc_state *state) { struct ingenic_drm *priv = drm_crtc_get_priv(crtc); - struct drm_plane_state *f1_state, *f0_state; + struct drm_plane_state *f1_state, *f0_state, *ipu_state; long rate; if (!drm_atomic_crtc_needs_modeset(state)) @@ -215,13 +224,44 @@ static int ingenic_drm_crtc_atomic_check(struct drm_crtc *crtc, f1_state = drm_atomic_get_plane_state(state->state, &priv->f1); f0_state = drm_atomic_get_plane_state(state->state, &priv->f0); + if (IS_ENABLED(CONFIG_DRM_INGENIC_IPU) && priv->ipu_plane) { + ipu_state = drm_atomic_get_plane_state(state->state, priv->ipu_plane); + + /* IPU and F1 planes cannot be enabled at the same time. */ + if (f1_state->fb && ipu_state->fb) { + dev_dbg(priv->dev, "Cannot enable both F1 and IPU\n"); + return -EINVAL; + } + } + /* If all the planes are disabled, we won't get a VBLANK IRQ */ - priv->no_vblank = !f1_state->fb && !f0_state->fb; + priv->no_vblank = !f1_state->fb && !f0_state->fb && + !(priv->ipu_plane && ipu_state->fb); } return 0; } +static void ingenic_drm_crtc_atomic_begin(struct drm_crtc *crtc, + struct drm_crtc_state *oldstate) +{ + struct ingenic_drm *priv = drm_crtc_get_priv(crtc); + u32 ctrl = 0; + + if (priv->soc_info->has_osd && + drm_atomic_crtc_needs_modeset(crtc->state)) { + /* + * If IPU plane is enabled, enable IPU as source for the F1 + * plane; otherwise use regular DMA. + */ + if (priv->ipu_plane && priv->ipu_plane->state->fb) + ctrl |= JZ_LCD_OSDCTRL_IPU; + + regmap_update_bits(priv->map, JZ_REG_LCD_OSDCTRL, + JZ_LCD_OSDCTRL_IPU, ctrl); + } +} + static void ingenic_drm_crtc_atomic_flush(struct drm_crtc *crtc, struct drm_crtc_state *oldstate) { @@ -311,10 +351,9 @@ static void ingenic_drm_plane_enable(struct ingenic_drm *priv, } } -static void ingenic_drm_plane_atomic_disable(struct drm_plane *plane, - struct drm_plane_state *old_state) +void ingenic_drm_plane_disable(struct device *dev, struct drm_plane *plane) { - struct ingenic_drm *priv = drm_device_get_priv(plane->dev); + struct ingenic_drm *priv = dev_get_drvdata(dev); unsigned int en_bit; if (priv->soc_info->has_osd) { @@ -327,9 +366,18 @@ static void ingenic_drm_plane_atomic_disable(struct drm_plane *plane, } } -static void ingenic_drm_plane_config(struct ingenic_drm *priv, - struct drm_plane *plane, u32 fourcc) +static void ingenic_drm_plane_atomic_disable(struct drm_plane *plane, + struct drm_plane_state *old_state) { + struct ingenic_drm *priv = drm_device_get_priv(plane->dev); + + ingenic_drm_plane_disable(priv->dev, plane); +} + +void ingenic_drm_plane_config(struct device *dev, + struct drm_plane *plane, u32 fourcc) +{ + struct ingenic_drm *priv = dev_get_drvdata(dev); struct drm_plane_state *state = plane->state; unsigned int xy_reg, size_reg; unsigned int ctrl = 0; @@ -411,7 +459,7 @@ static void ingenic_drm_plane_atomic_update(struct drm_plane *plane, hwdesc->cmd = JZ_LCD_CMD_EOF_IRQ | (width * height * cpp / 4); if (drm_atomic_crtc_needs_modeset(state->crtc->state)) - ingenic_drm_plane_config(priv, plane, + ingenic_drm_plane_config(priv->dev, plane, state->fb->format->format); } } @@ -604,6 +652,7 @@ static const struct drm_plane_helper_funcs ingenic_drm_plane_helper_funcs = { static const struct drm_crtc_helper_funcs ingenic_drm_crtc_helper_funcs = { .atomic_enable = ingenic_drm_crtc_atomic_enable, .atomic_disable = ingenic_drm_crtc_atomic_disable, + .atomic_begin = ingenic_drm_crtc_atomic_begin, .atomic_flush = ingenic_drm_crtc_atomic_flush, .atomic_check = ingenic_drm_crtc_atomic_check, }; @@ -624,10 +673,17 @@ static struct drm_mode_config_helper_funcs ingenic_drm_mode_config_helpers = { .atomic_commit_tail = ingenic_drm_atomic_helper_commit_tail, }; -static int ingenic_drm_probe(struct platform_device *pdev) +static void ingenic_drm_unbind_all(void *d) { + struct ingenic_drm *priv = d; + + component_unbind_all(priv->dev, &priv->drm); +} + +static int ingenic_drm_bind(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); const struct jz_soc_info *soc_info; - struct device *dev = &pdev->dev; struct ingenic_drm *priv; struct clk *parent_clk; struct drm_bridge *bridge; @@ -728,6 +784,9 @@ static int ingenic_drm_probe(struct platform_device *pdev) priv->dma_hwdesc_f0->id = 0xf0; } + if (soc_info->has_osd) + priv->ipu_plane = drm_plane_from_index(drm, 0); + drm_plane_helper_add(&priv->f1, &ingenic_drm_plane_helper_funcs); ret = drm_universal_plane_init(drm, &priv->f1, 1, @@ -736,7 +795,7 @@ static int ingenic_drm_probe(struct platform_device *pdev) ARRAY_SIZE(ingenic_drm_primary_formats), NULL, DRM_PLANE_TYPE_PRIMARY, NULL); if (ret) { - dev_err(dev, "Failed to register primary plane: %i\n", ret); + dev_err(dev, "Failed to register plane: %i\n", ret); return ret; } @@ -764,6 +823,25 @@ static int ingenic_drm_probe(struct platform_device *pdev) ret); return ret; } + + if (IS_ENABLED(CONFIG_DRM_INGENIC_IPU)) { + ret = component_bind_all(dev, drm); + if (ret) { + if (ret != -EPROBE_DEFER) + dev_err(dev, "Failed to bind components: %i\n", ret); + return ret; + } + + ret = devm_add_action_or_reset(dev, ingenic_drm_unbind_all, priv); + if (ret) + return ret; + + priv->ipu_plane = drm_plane_from_index(drm, 2); + if (!priv->ipu_plane) { + dev_err(dev, "Failed to retrieve IPU plane\n"); + return -EINVAL; + } + } } priv->encoder.possible_crtcs = 1; @@ -852,9 +930,14 @@ err_pixclk_disable: return ret; } -static int ingenic_drm_remove(struct platform_device *pdev) +static int compare_of(struct device *dev, void *data) { - struct ingenic_drm *priv = platform_get_drvdata(pdev); + return dev->of_node == data; +} + +static void ingenic_drm_unbind(struct device *dev) +{ + struct ingenic_drm *priv = dev_get_drvdata(dev); if (priv->lcd_clk) clk_disable_unprepare(priv->lcd_clk); @@ -862,6 +945,42 @@ static int ingenic_drm_remove(struct platform_device *pdev) drm_dev_unregister(&priv->drm); drm_atomic_helper_shutdown(&priv->drm); +} + +static const struct component_master_ops ingenic_master_ops = { + .bind = ingenic_drm_bind, + .unbind = ingenic_drm_unbind, +}; + +static int ingenic_drm_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct component_match *match = NULL; + struct device_node *np; + + if (!IS_ENABLED(CONFIG_DRM_INGENIC_IPU)) + return ingenic_drm_bind(dev); + + /* IPU is at port address 8 */ + np = of_graph_get_remote_node(dev->of_node, 8, 0); + if (!np) { + dev_err(dev, "Unable to get IPU node\n"); + return -EINVAL; + } + + drm_of_component_match_add(dev, &match, compare_of, np); + + return component_master_add_with_match(dev, &ingenic_master_ops, match); +} + +static int ingenic_drm_remove(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + + if (!IS_ENABLED(CONFIG_DRM_INGENIC_IPU)) + ingenic_drm_unbind(dev); + else + component_master_del(dev, &ingenic_master_ops); return 0; } @@ -903,7 +1022,29 @@ static struct platform_driver ingenic_drm_driver = { .probe = ingenic_drm_probe, .remove = ingenic_drm_remove, }; -module_platform_driver(ingenic_drm_driver); + +static int ingenic_drm_init(void) +{ + int err; + + if (IS_ENABLED(CONFIG_DRM_INGENIC_IPU)) { + err = platform_driver_register(ingenic_ipu_driver_ptr); + if (err) + return err; + } + + return platform_driver_register(&ingenic_drm_driver); +} +module_init(ingenic_drm_init); + +static void ingenic_drm_exit(void) +{ + platform_driver_unregister(&ingenic_drm_driver); + + if (IS_ENABLED(CONFIG_DRM_INGENIC_IPU)) + platform_driver_unregister(ingenic_ipu_driver_ptr); +} +module_exit(ingenic_drm_exit); MODULE_AUTHOR("Paul Cercueil "); MODULE_DESCRIPTION("DRM driver for the Ingenic SoCs\n"); diff --git a/drivers/gpu/drm/ingenic/ingenic-drm.h b/drivers/gpu/drm/ingenic/ingenic-drm.h index d0b827a9fe83..43f7d959cff7 100644 --- a/drivers/gpu/drm/ingenic/ingenic-drm.h +++ b/drivers/gpu/drm/ingenic/ingenic-drm.h @@ -8,6 +8,7 @@ #define DRIVERS_GPU_DRM_INGENIC_INGENIC_DRM_H #include +#include #define JZ_REG_LCD_CFG 0x00 #define JZ_REG_LCD_VSYNC 0x04 @@ -158,4 +159,15 @@ #define JZ_LCD_SIZE01_WIDTH_LSB 0 #define JZ_LCD_SIZE01_HEIGHT_LSB 16 +struct device; +struct drm_plane; +struct drm_plane_state; +struct platform_driver; + +void ingenic_drm_plane_config(struct device *dev, + struct drm_plane *plane, u32 fourcc); +void ingenic_drm_plane_disable(struct device *dev, struct drm_plane *plane); + +extern struct platform_driver *ingenic_ipu_driver_ptr; + #endif /* DRIVERS_GPU_DRM_INGENIC_INGENIC_DRM_H */ diff --git a/drivers/gpu/drm/ingenic/ingenic-ipu.c b/drivers/gpu/drm/ingenic/ingenic-ipu.c new file mode 100644 index 000000000000..7a0a8bd865d3 --- /dev/null +++ b/drivers/gpu/drm/ingenic/ingenic-ipu.c @@ -0,0 +1,853 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// Ingenic JZ47xx IPU driver +// +// Copyright (C) 2020, Paul Cercueil +// Copyright (C) 2020, Daniel Silsby + +#include "ingenic-drm.h" +#include "ingenic-ipu.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct ingenic_ipu; + +struct soc_info { + const u32 *formats; + size_t num_formats; + bool has_bicubic; + + void (*set_coefs)(struct ingenic_ipu *ipu, unsigned int reg, + unsigned int sharpness, bool downscale, + unsigned int weight, unsigned int offset); +}; + +struct ingenic_ipu { + struct drm_plane plane; + struct drm_device *drm; + struct device *dev, *master; + struct regmap *map; + struct clk *clk; + const struct soc_info *soc_info; + + unsigned int num_w, num_h, denom_w, denom_h; + + dma_addr_t addr_y, addr_u, addr_v; + + struct drm_property *sharpness_prop; + unsigned int sharpness; +}; + +/* Signed 15.16 fixed-point math (for bicubic scaling coefficients) */ +#define I2F(i) ((s32)(i) * 65536) +#define F2I(f) ((f) / 65536) +#define FMUL(fa, fb) ((s32)(((s64)(fa) * (s64)(fb)) / 65536)) +#define SHARPNESS_INCR (I2F(-1) / 8) + +static inline struct ingenic_ipu *plane_to_ingenic_ipu(struct drm_plane *plane) +{ + return container_of(plane, struct ingenic_ipu, plane); +} + +/* + * Apply conventional cubic convolution kernel. Both parameters + * and return value are 15.16 signed fixed-point. + * + * @f_a: Sharpness factor, typically in range [-4.0, -0.25]. + * A larger magnitude increases perceived sharpness, but going past + * -2.0 might cause ringing artifacts to outweigh any improvement. + * Nice values on a 320x240 LCD are between -0.75 and -2.0. + * + * @f_x: Absolute distance in pixels from 'pixel 0' sample position + * along horizontal (or vertical) source axis. Range is [0, +2.0]. + * + * returns: Weight of this pixel within 4-pixel sample group. Range is + * [-2.0, +2.0]. For moderate (i.e. > -3.0) sharpness factors, + * range is within [-1.0, +1.0]. + */ +static inline s32 cubic_conv(s32 f_a, s32 f_x) +{ + const s32 f_1 = I2F(1); + const s32 f_2 = I2F(2); + const s32 f_3 = I2F(3); + const s32 f_4 = I2F(4); + const s32 f_x2 = FMUL(f_x, f_x); + const s32 f_x3 = FMUL(f_x, f_x2); + + if (f_x <= f_1) + return FMUL((f_a + f_2), f_x3) - FMUL((f_a + f_3), f_x2) + f_1; + else if (f_x <= f_2) + return FMUL(f_a, (f_x3 - 5 * f_x2 + 8 * f_x - f_4)); + else + return 0; +} + +/* + * On entry, "weight" is a coefficient suitable for bilinear mode, + * which is converted to a set of four suitable for bicubic mode. + * + * "weight 512" means all of pixel 0; + * "weight 256" means half of pixel 0 and half of pixel 1; + * "weight 0" means all of pixel 1; + * + * "offset" is increment to next source pixel sample location. + */ +static void jz4760_set_coefs(struct ingenic_ipu *ipu, unsigned int reg, + unsigned int sharpness, bool downscale, + unsigned int weight, unsigned int offset) +{ + u32 val; + s32 w0, w1, w2, w3; /* Pixel weights at X (or Y) offsets -1,0,1,2 */ + + weight = clamp_val(weight, 0, 512); + + if (sharpness < 2) { + /* + * When sharpness setting is 0, emulate nearest-neighbor. + * When sharpness setting is 1, emulate bilinear. + */ + + if (sharpness == 0) + weight = weight >= 256 ? 512 : 0; + w0 = 0; + w1 = weight; + w2 = 512 - weight; + w3 = 0; + } else { + const s32 f_a = SHARPNESS_INCR * sharpness; + const s32 f_h = I2F(1) / 2; /* Round up 0.5 */ + + /* + * Note that always rounding towards +infinity here is intended. + * The resulting coefficients match a round-to-nearest-int + * double floating-point implementation. + */ + + weight = 512 - weight; + w0 = F2I(f_h + 512 * cubic_conv(f_a, I2F(512 + weight) / 512)); + w1 = F2I(f_h + 512 * cubic_conv(f_a, I2F(0 + weight) / 512)); + w2 = F2I(f_h + 512 * cubic_conv(f_a, I2F(512 - weight) / 512)); + w3 = F2I(f_h + 512 * cubic_conv(f_a, I2F(1024 - weight) / 512)); + w0 = clamp_val(w0, -1024, 1023); + w1 = clamp_val(w1, -1024, 1023); + w2 = clamp_val(w2, -1024, 1023); + w3 = clamp_val(w3, -1024, 1023); + } + + val = ((w1 & JZ4760_IPU_RSZ_COEF_MASK) << JZ4760_IPU_RSZ_COEF31_LSB) | + ((w0 & JZ4760_IPU_RSZ_COEF_MASK) << JZ4760_IPU_RSZ_COEF20_LSB); + regmap_write(ipu->map, reg, val); + + val = ((w3 & JZ4760_IPU_RSZ_COEF_MASK) << JZ4760_IPU_RSZ_COEF31_LSB) | + ((w2 & JZ4760_IPU_RSZ_COEF_MASK) << JZ4760_IPU_RSZ_COEF20_LSB) | + ((offset & JZ4760_IPU_RSZ_OFFSET_MASK) << JZ4760_IPU_RSZ_OFFSET_LSB); + regmap_write(ipu->map, reg, val); +} + +static void jz4725b_set_coefs(struct ingenic_ipu *ipu, unsigned int reg, + unsigned int sharpness, bool downscale, + unsigned int weight, unsigned int offset) +{ + u32 val = JZ4725B_IPU_RSZ_LUT_OUT_EN; + unsigned int i; + + weight = clamp_val(weight, 0, 512); + + if (sharpness == 0) + weight = weight >= 256 ? 512 : 0; + + val |= (weight & JZ4725B_IPU_RSZ_LUT_COEF_MASK) << JZ4725B_IPU_RSZ_LUT_COEF_LSB; + if (downscale || !!offset) + val |= JZ4725B_IPU_RSZ_LUT_IN_EN; + + regmap_write(ipu->map, reg, val); + + if (downscale) { + for (i = 1; i < offset; i++) + regmap_write(ipu->map, reg, JZ4725B_IPU_RSZ_LUT_IN_EN); + } +} + +static void ingenic_ipu_set_downscale_coefs(struct ingenic_ipu *ipu, + unsigned int reg, + unsigned int num, + unsigned int denom) +{ + unsigned int i, offset, weight, weight_num = denom; + + for (i = 0; i < num; i++) { + weight_num = num + (weight_num - num) % (num * 2); + weight = 512 - 512 * (weight_num - num) / (num * 2); + weight_num += denom * 2; + offset = (weight_num - num) / (num * 2); + + ipu->soc_info->set_coefs(ipu, reg, ipu->sharpness, + true, weight, offset); + } +} + +static void ingenic_ipu_set_integer_upscale_coefs(struct ingenic_ipu *ipu, + unsigned int reg, + unsigned int num) +{ + /* + * Force nearest-neighbor scaling and use simple math when upscaling + * by an integer ratio. It looks better, and fixes a few problem cases. + */ + unsigned int i; + + for (i = 0; i < num; i++) + ipu->soc_info->set_coefs(ipu, reg, 0, false, 512, i == num - 1); +} + +static void ingenic_ipu_set_upscale_coefs(struct ingenic_ipu *ipu, + unsigned int reg, + unsigned int num, + unsigned int denom) +{ + unsigned int i, offset, weight, weight_num = 0; + + for (i = 0; i < num; i++) { + weight = 512 - 512 * weight_num / num; + weight_num += denom; + offset = weight_num >= num; + + if (offset) + weight_num -= num; + + ipu->soc_info->set_coefs(ipu, reg, ipu->sharpness, + false, weight, offset); + } +} + +static void ingenic_ipu_set_coefs(struct ingenic_ipu *ipu, unsigned int reg, + unsigned int num, unsigned int denom) +{ + /* Begin programming the LUT */ + regmap_write(ipu->map, reg, -1); + + if (denom > num) + ingenic_ipu_set_downscale_coefs(ipu, reg, num, denom); + else if (denom == 1) + ingenic_ipu_set_integer_upscale_coefs(ipu, reg, num); + else + ingenic_ipu_set_upscale_coefs(ipu, reg, num, denom); +} + +static int reduce_fraction(unsigned int *num, unsigned int *denom) +{ + unsigned long d = gcd(*num, *denom); + + /* The scaling table has only 31 entries */ + if (*num > 31 * d) + return -EINVAL; + + *num /= d; + *denom /= d; + return 0; +} + +static inline bool osd_changed(struct drm_plane_state *state, + struct drm_plane_state *oldstate) +{ + return state->src_x != oldstate->src_x || + state->src_y != oldstate->src_y || + state->src_w != oldstate->src_w || + state->src_h != oldstate->src_h || + state->crtc_x != oldstate->crtc_x || + state->crtc_y != oldstate->crtc_y || + state->crtc_w != oldstate->crtc_w || + state->crtc_h != oldstate->crtc_h; +} + +static void ingenic_ipu_plane_atomic_update(struct drm_plane *plane, + struct drm_plane_state *oldstate) +{ + struct ingenic_ipu *ipu = plane_to_ingenic_ipu(plane); + struct drm_plane_state *state = plane->state; + const struct drm_format_info *finfo; + u32 ctrl, stride = 0, coef_index = 0, format = 0; + bool needs_modeset, upscaling_w, upscaling_h; + + if (!state || !state->fb) + return; + + finfo = drm_format_info(state->fb->format->format); + + /* Reset all the registers if needed */ + needs_modeset = drm_atomic_crtc_needs_modeset(state->crtc->state); + if (needs_modeset) { + regmap_set_bits(ipu->map, JZ_REG_IPU_CTRL, JZ_IPU_CTRL_RST); + + /* Enable the chip */ + regmap_set_bits(ipu->map, JZ_REG_IPU_CTRL, + JZ_IPU_CTRL_CHIP_EN | JZ_IPU_CTRL_LCDC_SEL); + } + + /* New addresses will be committed in vblank handler... */ + ipu->addr_y = drm_fb_cma_get_gem_addr(state->fb, state, 0); + if (finfo->num_planes > 1) + ipu->addr_u = drm_fb_cma_get_gem_addr(state->fb, state, 1); + if (finfo->num_planes > 2) + ipu->addr_v = drm_fb_cma_get_gem_addr(state->fb, state, 2); + + if (!needs_modeset) + return; + + /* Or right here if we're doing a full modeset. */ + regmap_write(ipu->map, JZ_REG_IPU_Y_ADDR, ipu->addr_y); + regmap_write(ipu->map, JZ_REG_IPU_U_ADDR, ipu->addr_u); + regmap_write(ipu->map, JZ_REG_IPU_V_ADDR, ipu->addr_v); + + if (finfo->num_planes == 1) + regmap_set_bits(ipu->map, JZ_REG_IPU_CTRL, JZ_IPU_CTRL_SPKG_SEL); + + ingenic_drm_plane_config(ipu->master, plane, DRM_FORMAT_XRGB8888); + + /* Set the input height/width/strides */ + if (finfo->num_planes > 2) + stride = ((state->src_w >> 16) * finfo->cpp[2] / finfo->hsub) + << JZ_IPU_UV_STRIDE_V_LSB; + + if (finfo->num_planes > 1) + stride |= ((state->src_w >> 16) * finfo->cpp[1] / finfo->hsub) + << JZ_IPU_UV_STRIDE_U_LSB; + + regmap_write(ipu->map, JZ_REG_IPU_UV_STRIDE, stride); + + stride = ((state->src_w >> 16) * finfo->cpp[0]) << JZ_IPU_Y_STRIDE_Y_LSB; + regmap_write(ipu->map, JZ_REG_IPU_Y_STRIDE, stride); + + regmap_write(ipu->map, JZ_REG_IPU_IN_GS, + (stride << JZ_IPU_IN_GS_W_LSB) | + ((state->src_h >> 16) << JZ_IPU_IN_GS_H_LSB)); + + switch (finfo->format) { + case DRM_FORMAT_XRGB1555: + format = JZ_IPU_D_FMT_IN_FMT_RGB555 | + JZ_IPU_D_FMT_RGB_OUT_OFT_RGB; + break; + case DRM_FORMAT_XBGR1555: + format = JZ_IPU_D_FMT_IN_FMT_RGB555 | + JZ_IPU_D_FMT_RGB_OUT_OFT_BGR; + break; + case DRM_FORMAT_RGB565: + format = JZ_IPU_D_FMT_IN_FMT_RGB565 | + JZ_IPU_D_FMT_RGB_OUT_OFT_RGB; + break; + case DRM_FORMAT_BGR565: + format = JZ_IPU_D_FMT_IN_FMT_RGB565 | + JZ_IPU_D_FMT_RGB_OUT_OFT_BGR; + break; + case DRM_FORMAT_XRGB8888: + case DRM_FORMAT_XYUV8888: + format = JZ_IPU_D_FMT_IN_FMT_RGB888 | + JZ_IPU_D_FMT_RGB_OUT_OFT_RGB; + break; + case DRM_FORMAT_XBGR8888: + format = JZ_IPU_D_FMT_IN_FMT_RGB888 | + JZ_IPU_D_FMT_RGB_OUT_OFT_BGR; + break; + case DRM_FORMAT_YUYV: + format = JZ_IPU_D_FMT_IN_FMT_YUV422 | + JZ_IPU_D_FMT_YUV_VY1UY0; + break; + case DRM_FORMAT_YVYU: + format = JZ_IPU_D_FMT_IN_FMT_YUV422 | + JZ_IPU_D_FMT_YUV_UY1VY0; + break; + case DRM_FORMAT_UYVY: + format = JZ_IPU_D_FMT_IN_FMT_YUV422 | + JZ_IPU_D_FMT_YUV_Y1VY0U; + break; + case DRM_FORMAT_VYUY: + format = JZ_IPU_D_FMT_IN_FMT_YUV422 | + JZ_IPU_D_FMT_YUV_Y1UY0V; + break; + case DRM_FORMAT_YUV411: + format = JZ_IPU_D_FMT_IN_FMT_YUV411; + break; + case DRM_FORMAT_YUV420: + format = JZ_IPU_D_FMT_IN_FMT_YUV420; + break; + case DRM_FORMAT_YUV422: + format = JZ_IPU_D_FMT_IN_FMT_YUV422; + break; + case DRM_FORMAT_YUV444: + format = JZ_IPU_D_FMT_IN_FMT_YUV444; + break; + default: + WARN_ONCE(1, "Unsupported format"); + break; + } + + /* Fix output to RGB888 */ + format |= JZ_IPU_D_FMT_OUT_FMT_RGB888; + + /* Set pixel format */ + regmap_write(ipu->map, JZ_REG_IPU_D_FMT, format); + + /* Set the output height/width/stride */ + regmap_write(ipu->map, JZ_REG_IPU_OUT_GS, + ((state->crtc_w * 4) << JZ_IPU_OUT_GS_W_LSB) + | state->crtc_h << JZ_IPU_OUT_GS_H_LSB); + regmap_write(ipu->map, JZ_REG_IPU_OUT_STRIDE, state->crtc_w * 4); + + if (finfo->is_yuv) { + regmap_set_bits(ipu->map, JZ_REG_IPU_CTRL, JZ_IPU_CTRL_CSC_EN); + + /* + * Offsets for Chroma/Luma. + * y = source Y - LUMA, + * u = source Cb - CHROMA, + * v = source Cr - CHROMA + */ + regmap_write(ipu->map, JZ_REG_IPU_CSC_OFFSET, + 128 << JZ_IPU_CSC_OFFSET_CHROMA_LSB | + 0 << JZ_IPU_CSC_OFFSET_LUMA_LSB); + + /* + * YUV422 to RGB conversion table. + * R = C0 / 0x400 * y + C1 / 0x400 * v + * G = C0 / 0x400 * y - C2 / 0x400 * u - C3 / 0x400 * v + * B = C0 / 0x400 * y + C4 / 0x400 * u + */ + regmap_write(ipu->map, JZ_REG_IPU_CSC_C0_COEF, 0x4a8); + regmap_write(ipu->map, JZ_REG_IPU_CSC_C1_COEF, 0x662); + regmap_write(ipu->map, JZ_REG_IPU_CSC_C2_COEF, 0x191); + regmap_write(ipu->map, JZ_REG_IPU_CSC_C3_COEF, 0x341); + regmap_write(ipu->map, JZ_REG_IPU_CSC_C4_COEF, 0x811); + } + + ctrl = 0; + + /* + * Must set ZOOM_SEL before programming bicubic LUTs. + * If the IPU supports bicubic, we enable it unconditionally, since it + * can do anything bilinear can and more. + */ + if (ipu->soc_info->has_bicubic) + ctrl |= JZ_IPU_CTRL_ZOOM_SEL; + + upscaling_w = ipu->num_w > ipu->denom_w; + if (upscaling_w) + ctrl |= JZ_IPU_CTRL_HSCALE; + + if (ipu->num_w != 1 || ipu->denom_w != 1) { + if (!ipu->soc_info->has_bicubic && !upscaling_w) + coef_index |= (ipu->denom_w - 1) << 16; + else + coef_index |= (ipu->num_w - 1) << 16; + ctrl |= JZ_IPU_CTRL_HRSZ_EN; + } + + upscaling_h = ipu->num_h > ipu->denom_h; + if (upscaling_h) + ctrl |= JZ_IPU_CTRL_VSCALE; + + if (ipu->num_h != 1 || ipu->denom_h != 1) { + if (!ipu->soc_info->has_bicubic && !upscaling_h) + coef_index |= ipu->denom_h - 1; + else + coef_index |= ipu->num_h - 1; + ctrl |= JZ_IPU_CTRL_VRSZ_EN; + } + + regmap_update_bits(ipu->map, JZ_REG_IPU_CTRL, JZ_IPU_CTRL_ZOOM_SEL | + JZ_IPU_CTRL_HRSZ_EN | JZ_IPU_CTRL_VRSZ_EN | + JZ_IPU_CTRL_HSCALE | JZ_IPU_CTRL_VSCALE, ctrl); + + /* Set the LUT index register */ + regmap_write(ipu->map, JZ_REG_IPU_RSZ_COEF_INDEX, coef_index); + + if (ipu->num_w != 1 || ipu->denom_w != 1) + ingenic_ipu_set_coefs(ipu, JZ_REG_IPU_HRSZ_COEF_LUT, + ipu->num_w, ipu->denom_w); + + if (ipu->num_h != 1 || ipu->denom_h != 1) + ingenic_ipu_set_coefs(ipu, JZ_REG_IPU_VRSZ_COEF_LUT, + ipu->num_h, ipu->denom_h); + + /* Clear STATUS register */ + regmap_write(ipu->map, JZ_REG_IPU_STATUS, 0); + + /* Start IPU */ + regmap_set_bits(ipu->map, JZ_REG_IPU_CTRL, + JZ_IPU_CTRL_RUN | JZ_IPU_CTRL_FM_IRQ_EN); + + dev_dbg(ipu->dev, "Scaling %ux%u to %ux%u (%u:%u horiz, %u:%u vert)\n", + state->src_w >> 16, state->src_h >> 16, + state->crtc_w, state->crtc_h, + ipu->num_w, ipu->denom_w, ipu->num_h, ipu->denom_h); +} + +static int ingenic_ipu_plane_atomic_check(struct drm_plane *plane, + struct drm_plane_state *state) +{ + unsigned int num_w, denom_w, num_h, denom_h, xres, yres; + struct ingenic_ipu *ipu = plane_to_ingenic_ipu(plane); + struct drm_crtc *crtc = state->crtc ?: plane->state->crtc; + struct drm_crtc_state *crtc_state; + + if (!crtc) + return 0; + + crtc_state = drm_atomic_get_existing_crtc_state(state->state, crtc); + if (WARN_ON(!crtc_state)) + return -EINVAL; + + /* Request a full modeset if we are enabling or disabling the IPU. */ + if (!plane->state->crtc ^ !state->crtc) + crtc_state->mode_changed = true; + + if (!state->crtc || + !crtc_state->mode.hdisplay || !crtc_state->mode.vdisplay) + return 0; + + /* Plane must be fully visible */ + if (state->crtc_x < 0 || state->crtc_y < 0 || + state->crtc_x + state->crtc_w > crtc_state->mode.hdisplay || + state->crtc_y + state->crtc_h > crtc_state->mode.vdisplay) + return -EINVAL; + + /* Minimum size is 4x4 */ + if ((state->src_w >> 16) < 4 || (state->src_h >> 16) < 4) + return -EINVAL; + + /* Input and output lines must have an even number of pixels. */ + if (((state->src_w >> 16) & 1) || (state->crtc_w & 1)) + return -EINVAL; + + if (!osd_changed(state, plane->state)) + return 0; + + crtc_state->mode_changed = true; + + xres = state->src_w >> 16; + yres = state->src_h >> 16; + + /* Adjust the coefficients until we find a valid configuration */ + for (denom_w = xres, num_w = state->crtc_w; + num_w <= crtc_state->mode.hdisplay; num_w++) + if (!reduce_fraction(&num_w, &denom_w)) + break; + if (num_w > crtc_state->mode.hdisplay) + return -EINVAL; + + for (denom_h = yres, num_h = state->crtc_h; + num_h <= crtc_state->mode.vdisplay; num_h++) + if (!reduce_fraction(&num_h, &denom_h)) + break; + if (num_h > crtc_state->mode.vdisplay) + return -EINVAL; + + ipu->num_w = num_w; + ipu->num_h = num_h; + ipu->denom_w = denom_w; + ipu->denom_h = denom_h; + + return 0; +} + +static void ingenic_ipu_plane_atomic_disable(struct drm_plane *plane, + struct drm_plane_state *old_state) +{ + struct ingenic_ipu *ipu = plane_to_ingenic_ipu(plane); + + regmap_set_bits(ipu->map, JZ_REG_IPU_CTRL, JZ_IPU_CTRL_STOP); + regmap_clear_bits(ipu->map, JZ_REG_IPU_CTRL, JZ_IPU_CTRL_CHIP_EN); + + ingenic_drm_plane_disable(ipu->master, plane); +} + +static const struct drm_plane_helper_funcs ingenic_ipu_plane_helper_funcs = { + .atomic_update = ingenic_ipu_plane_atomic_update, + .atomic_check = ingenic_ipu_plane_atomic_check, + .atomic_disable = ingenic_ipu_plane_atomic_disable, + .prepare_fb = drm_gem_fb_prepare_fb, +}; + +static int +ingenic_ipu_plane_atomic_get_property(struct drm_plane *plane, + const struct drm_plane_state *state, + struct drm_property *property, u64 *val) +{ + struct ingenic_ipu *ipu = plane_to_ingenic_ipu(plane); + + if (property != ipu->sharpness_prop) + return -EINVAL; + + *val = ipu->sharpness; + + return 0; +} + +static int +ingenic_ipu_plane_atomic_set_property(struct drm_plane *plane, + struct drm_plane_state *state, + struct drm_property *property, u64 val) +{ + struct ingenic_ipu *ipu = plane_to_ingenic_ipu(plane); + struct drm_crtc_state *crtc_state; + + if (property != ipu->sharpness_prop) + return -EINVAL; + + ipu->sharpness = val; + + if (state->crtc) { + crtc_state = drm_atomic_get_existing_crtc_state(state->state, state->crtc); + if (WARN_ON(!crtc_state)) + return -EINVAL; + + crtc_state->mode_changed = true; + } + + return 0; +} + +static const struct drm_plane_funcs ingenic_ipu_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_cleanup, + + .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state, + .atomic_destroy_state = drm_atomic_helper_plane_destroy_state, + + .atomic_get_property = ingenic_ipu_plane_atomic_get_property, + .atomic_set_property = ingenic_ipu_plane_atomic_set_property, +}; + +static irqreturn_t ingenic_ipu_irq_handler(int irq, void *arg) +{ + struct ingenic_ipu *ipu = arg; + struct drm_crtc *crtc = drm_crtc_from_index(ipu->drm, 0); + unsigned int dummy; + + /* dummy read allows CPU to reconfigure IPU */ + regmap_read(ipu->map, JZ_REG_IPU_STATUS, &dummy); + + /* ACK interrupt */ + regmap_write(ipu->map, JZ_REG_IPU_STATUS, 0); + + /* Set previously cached addresses */ + regmap_write(ipu->map, JZ_REG_IPU_Y_ADDR, ipu->addr_y); + regmap_write(ipu->map, JZ_REG_IPU_U_ADDR, ipu->addr_u); + regmap_write(ipu->map, JZ_REG_IPU_V_ADDR, ipu->addr_v); + + /* Run IPU for the new frame */ + regmap_set_bits(ipu->map, JZ_REG_IPU_CTRL, JZ_IPU_CTRL_RUN); + + drm_crtc_handle_vblank(crtc); + + return IRQ_HANDLED; +} + +static const struct regmap_config ingenic_ipu_regmap_config = { + .reg_bits = 32, + .val_bits = 32, + .reg_stride = 4, + + .max_register = JZ_REG_IPU_OUT_PHY_T_ADDR, +}; + +static int ingenic_ipu_bind(struct device *dev, struct device *master, void *d) +{ + struct platform_device *pdev = to_platform_device(dev); + const struct soc_info *soc_info; + struct drm_device *drm = d; + struct drm_plane *plane; + struct ingenic_ipu *ipu; + void __iomem *base; + unsigned int sharpness_max; + int err, irq; + + ipu = devm_kzalloc(dev, sizeof(*ipu), GFP_KERNEL); + if (!ipu) + return -ENOMEM; + + soc_info = of_device_get_match_data(dev); + if (!soc_info) { + dev_err(dev, "Missing platform data\n"); + return -EINVAL; + } + + ipu->dev = dev; + ipu->drm = drm; + ipu->master = master; + ipu->soc_info = soc_info; + + base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(base)) { + dev_err(dev, "Failed to get memory resource\n"); + return PTR_ERR(base); + } + + ipu->map = devm_regmap_init_mmio(dev, base, &ingenic_ipu_regmap_config); + if (IS_ERR(ipu->map)) { + dev_err(dev, "Failed to create regmap\n"); + return PTR_ERR(ipu->map); + } + + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return irq; + + ipu->clk = devm_clk_get(dev, "ipu"); + if (IS_ERR(ipu->clk)) { + dev_err(dev, "Failed to get pixel clock\n"); + return PTR_ERR(ipu->clk); + } + + err = devm_request_irq(dev, irq, ingenic_ipu_irq_handler, 0, + dev_name(dev), ipu); + if (err) { + dev_err(dev, "Unable to request IRQ\n"); + return err; + } + + plane = &ipu->plane; + dev_set_drvdata(dev, plane); + + drm_plane_helper_add(plane, &ingenic_ipu_plane_helper_funcs); + + err = drm_universal_plane_init(drm, plane, 1, &ingenic_ipu_plane_funcs, + soc_info->formats, soc_info->num_formats, + NULL, DRM_PLANE_TYPE_PRIMARY, NULL); + if (err) { + dev_err(dev, "Failed to init plane: %i\n", err); + return err; + } + + /* + * Sharpness settings range is [0,32] + * 0 : nearest-neighbor + * 1 : bilinear + * 2 .. 32 : bicubic (translated to sharpness factor -0.25 .. -4.0) + */ + sharpness_max = soc_info->has_bicubic ? 32 : 1; + ipu->sharpness_prop = drm_property_create_range(drm, 0, "sharpness", + 0, sharpness_max); + if (!ipu->sharpness_prop) { + dev_err(dev, "Unable to create sharpness property\n"); + return -ENOMEM; + } + + /* Default sharpness factor: -0.125 * 8 = -1.0 */ + ipu->sharpness = soc_info->has_bicubic ? 8 : 1; + drm_object_attach_property(&plane->base, ipu->sharpness_prop, + ipu->sharpness); + + err = clk_prepare_enable(ipu->clk); + if (err) { + dev_err(dev, "Unable to enable clock\n"); + return err; + } + + return 0; +} + +static void ingenic_ipu_unbind(struct device *dev, + struct device *master, void *d) +{ + struct ingenic_ipu *ipu = dev_get_drvdata(dev); + + clk_disable_unprepare(ipu->clk); +} + +static const struct component_ops ingenic_ipu_ops = { + .bind = ingenic_ipu_bind, + .unbind = ingenic_ipu_unbind, +}; + +static int ingenic_ipu_probe(struct platform_device *pdev) +{ + return component_add(&pdev->dev, &ingenic_ipu_ops); +} + +static int ingenic_ipu_remove(struct platform_device *pdev) +{ + component_del(&pdev->dev, &ingenic_ipu_ops); + return 0; +} + +static const u32 jz4725b_ipu_formats[] = { + DRM_FORMAT_YUYV, + DRM_FORMAT_YVYU, + DRM_FORMAT_UYVY, + DRM_FORMAT_VYUY, + DRM_FORMAT_YUV411, + DRM_FORMAT_YUV420, + DRM_FORMAT_YUV422, + DRM_FORMAT_YUV444, +}; + +static const struct soc_info jz4725b_soc_info = { + .formats = jz4725b_ipu_formats, + .num_formats = ARRAY_SIZE(jz4725b_ipu_formats), + .has_bicubic = false, + .set_coefs = jz4725b_set_coefs, +}; + +static const u32 jz4760_ipu_formats[] = { + DRM_FORMAT_XRGB1555, + DRM_FORMAT_XBGR1555, + DRM_FORMAT_RGB565, + DRM_FORMAT_BGR565, + DRM_FORMAT_XRGB8888, + DRM_FORMAT_XBGR8888, + DRM_FORMAT_YUYV, + DRM_FORMAT_YVYU, + DRM_FORMAT_UYVY, + DRM_FORMAT_VYUY, + DRM_FORMAT_YUV411, + DRM_FORMAT_YUV420, + DRM_FORMAT_YUV422, + DRM_FORMAT_YUV444, + DRM_FORMAT_XYUV8888, +}; + +static const struct soc_info jz4760_soc_info = { + .formats = jz4760_ipu_formats, + .num_formats = ARRAY_SIZE(jz4760_ipu_formats), + .has_bicubic = true, + .set_coefs = jz4760_set_coefs, +}; + +static const struct of_device_id ingenic_ipu_of_match[] = { + { .compatible = "ingenic,jz4725b-ipu", .data = &jz4725b_soc_info }, + { .compatible = "ingenic,jz4760-ipu", .data = &jz4760_soc_info }, + { /* sentinel */ }, +}; +MODULE_DEVICE_TABLE(of, ingenic_ipu_of_match); + +static struct platform_driver ingenic_ipu_driver = { + .driver = { + .name = "ingenic-ipu", + .of_match_table = ingenic_ipu_of_match, + }, + .probe = ingenic_ipu_probe, + .remove = ingenic_ipu_remove, +}; + +struct platform_driver *ingenic_ipu_driver_ptr = &ingenic_ipu_driver; diff --git a/drivers/gpu/drm/ingenic/ingenic-ipu.h b/drivers/gpu/drm/ingenic/ingenic-ipu.h new file mode 100644 index 000000000000..eab6fab8c7f6 --- /dev/null +++ b/drivers/gpu/drm/ingenic/ingenic-ipu.h @@ -0,0 +1,109 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +// +// Ingenic JZ47xx IPU - Register definitions and private API +// +// Copyright (C) 2020, Paul Cercueil + +#ifndef DRIVERS_GPU_DRM_INGENIC_INGENIC_IPU_H +#define DRIVERS_GPU_DRM_INGENIC_INGENIC_IPU_H + +#include + +#define JZ_REG_IPU_CTRL 0x00 +#define JZ_REG_IPU_STATUS 0x04 +#define JZ_REG_IPU_D_FMT 0x08 +#define JZ_REG_IPU_Y_ADDR 0x0c +#define JZ_REG_IPU_U_ADDR 0x10 +#define JZ_REG_IPU_V_ADDR 0x14 +#define JZ_REG_IPU_IN_GS 0x18 +#define JZ_REG_IPU_Y_STRIDE 0x1c +#define JZ_REG_IPU_UV_STRIDE 0x20 +#define JZ_REG_IPU_OUT_ADDR 0x24 +#define JZ_REG_IPU_OUT_GS 0x28 +#define JZ_REG_IPU_OUT_STRIDE 0x2c +#define JZ_REG_IPU_RSZ_COEF_INDEX 0x30 +#define JZ_REG_IPU_CSC_C0_COEF 0x34 +#define JZ_REG_IPU_CSC_C1_COEF 0x38 +#define JZ_REG_IPU_CSC_C2_COEF 0x3c +#define JZ_REG_IPU_CSC_C3_COEF 0x40 +#define JZ_REG_IPU_CSC_C4_COEF 0x44 +#define JZ_REG_IPU_HRSZ_COEF_LUT 0x48 +#define JZ_REG_IPU_VRSZ_COEF_LUT 0x4c +#define JZ_REG_IPU_CSC_OFFSET 0x50 +#define JZ_REG_IPU_Y_PHY_T_ADDR 0x54 +#define JZ_REG_IPU_U_PHY_T_ADDR 0x58 +#define JZ_REG_IPU_V_PHY_T_ADDR 0x5c +#define JZ_REG_IPU_OUT_PHY_T_ADDR 0x60 + +#define JZ_IPU_CTRL_ADDR_SEL BIT(20) +#define JZ_IPU_CTRL_ZOOM_SEL BIT(18) +#define JZ_IPU_CTRL_DFIX_SEL BIT(17) +#define JZ_IPU_CTRL_LCDC_SEL BIT(11) +#define JZ_IPU_CTRL_SPKG_SEL BIT(10) +#define JZ_IPU_CTRL_VSCALE BIT(9) +#define JZ_IPU_CTRL_HSCALE BIT(8) +#define JZ_IPU_CTRL_STOP BIT(7) +#define JZ_IPU_CTRL_RST BIT(6) +#define JZ_IPU_CTRL_FM_IRQ_EN BIT(5) +#define JZ_IPU_CTRL_CSC_EN BIT(4) +#define JZ_IPU_CTRL_VRSZ_EN BIT(3) +#define JZ_IPU_CTRL_HRSZ_EN BIT(2) +#define JZ_IPU_CTRL_RUN BIT(1) +#define JZ_IPU_CTRL_CHIP_EN BIT(0) + +#define JZ_IPU_STATUS_OUT_END BIT(0) + +#define JZ_IPU_IN_GS_H_LSB 0x0 +#define JZ_IPU_IN_GS_W_LSB 0x10 +#define JZ_IPU_OUT_GS_H_LSB 0x0 +#define JZ_IPU_OUT_GS_W_LSB 0x10 + +#define JZ_IPU_Y_STRIDE_Y_LSB 0 +#define JZ_IPU_UV_STRIDE_U_LSB 16 +#define JZ_IPU_UV_STRIDE_V_LSB 0 + +#define JZ_IPU_D_FMT_IN_FMT_LSB 0 +#define JZ_IPU_D_FMT_IN_FMT_RGB555 (0x0 << JZ_IPU_D_FMT_IN_FMT_LSB) +#define JZ_IPU_D_FMT_IN_FMT_YUV420 (0x0 << JZ_IPU_D_FMT_IN_FMT_LSB) +#define JZ_IPU_D_FMT_IN_FMT_YUV422 (0x1 << JZ_IPU_D_FMT_IN_FMT_LSB) +#define JZ_IPU_D_FMT_IN_FMT_RGB888 (0x2 << JZ_IPU_D_FMT_IN_FMT_LSB) +#define JZ_IPU_D_FMT_IN_FMT_YUV444 (0x2 << JZ_IPU_D_FMT_IN_FMT_LSB) +#define JZ_IPU_D_FMT_IN_FMT_RGB565 (0x3 << JZ_IPU_D_FMT_IN_FMT_LSB) + +#define JZ_IPU_D_FMT_YUV_FMT_LSB 2 +#define JZ_IPU_D_FMT_YUV_Y1UY0V (0x0 << JZ_IPU_D_FMT_YUV_FMT_LSB) +#define JZ_IPU_D_FMT_YUV_Y1VY0U (0x1 << JZ_IPU_D_FMT_YUV_FMT_LSB) +#define JZ_IPU_D_FMT_YUV_UY1VY0 (0x2 << JZ_IPU_D_FMT_YUV_FMT_LSB) +#define JZ_IPU_D_FMT_YUV_VY1UY0 (0x3 << JZ_IPU_D_FMT_YUV_FMT_LSB) +#define JZ_IPU_D_FMT_IN_FMT_YUV411 (0x3 << JZ_IPU_D_FMT_IN_FMT_LSB) + +#define JZ_IPU_D_FMT_OUT_FMT_LSB 19 +#define JZ_IPU_D_FMT_OUT_FMT_RGB555 (0x0 << JZ_IPU_D_FMT_OUT_FMT_LSB) +#define JZ_IPU_D_FMT_OUT_FMT_RGB565 (0x1 << JZ_IPU_D_FMT_OUT_FMT_LSB) +#define JZ_IPU_D_FMT_OUT_FMT_RGB888 (0x2 << JZ_IPU_D_FMT_OUT_FMT_LSB) +#define JZ_IPU_D_FMT_OUT_FMT_YUV422 (0x3 << JZ_IPU_D_FMT_OUT_FMT_LSB) +#define JZ_IPU_D_FMT_OUT_FMT_RGBAAA (0x4 << JZ_IPU_D_FMT_OUT_FMT_LSB) + +#define JZ_IPU_D_FMT_RGB_OUT_OFT_LSB 22 +#define JZ_IPU_D_FMT_RGB_OUT_OFT_RGB (0x0 << JZ_IPU_D_FMT_RGB_OUT_OFT_LSB) +#define JZ_IPU_D_FMT_RGB_OUT_OFT_RBG (0x1 << JZ_IPU_D_FMT_RGB_OUT_OFT_LSB) +#define JZ_IPU_D_FMT_RGB_OUT_OFT_GBR (0x2 << JZ_IPU_D_FMT_RGB_OUT_OFT_LSB) +#define JZ_IPU_D_FMT_RGB_OUT_OFT_GRB (0x3 << JZ_IPU_D_FMT_RGB_OUT_OFT_LSB) +#define JZ_IPU_D_FMT_RGB_OUT_OFT_BRG (0x4 << JZ_IPU_D_FMT_RGB_OUT_OFT_LSB) +#define JZ_IPU_D_FMT_RGB_OUT_OFT_BGR (0x5 << JZ_IPU_D_FMT_RGB_OUT_OFT_LSB) + +#define JZ4725B_IPU_RSZ_LUT_COEF_LSB 2 +#define JZ4725B_IPU_RSZ_LUT_COEF_MASK 0x7ff +#define JZ4725B_IPU_RSZ_LUT_IN_EN BIT(1) +#define JZ4725B_IPU_RSZ_LUT_OUT_EN BIT(0) + +#define JZ4760_IPU_RSZ_COEF20_LSB 6 +#define JZ4760_IPU_RSZ_COEF31_LSB 17 +#define JZ4760_IPU_RSZ_COEF_MASK 0x7ff +#define JZ4760_IPU_RSZ_OFFSET_LSB 1 +#define JZ4760_IPU_RSZ_OFFSET_MASK 0x1f + +#define JZ_IPU_CSC_OFFSET_CHROMA_LSB 16 +#define JZ_IPU_CSC_OFFSET_LUMA_LSB 16 + +#endif /* DRIVERS_GPU_DRM_INGENIC_INGENIC_IPU_H */ From c369cb27c267fe3020f3b059618743532f73503b Mon Sep 17 00:00:00 2001 From: Paul Cercueil Date: Thu, 16 Jul 2020 18:38:45 +0200 Subject: [PATCH 26/59] drm/ingenic: Support multiple panels/bridges Support multiple panels or bridges connected to the same DPI output of the SoC. This setup can be found for instance on the GCW Zero, where the same DPI output interfaces the internal 320x240 TFT panel, and the ITE IT6610 HDMI chip. v2: No change v3: Allow > 80-char lines where it makes sense Signed-off-by: Paul Cercueil Reviewed-by: Sam Ravnborg Link: https://patchwork.freedesktop.org/patch/msgid/20200716163846.174790-11-paul@crapouillou.net --- drivers/gpu/drm/ingenic/ingenic-drm-drv.c | 71 +++++++++++++---------- 1 file changed, 40 insertions(+), 31 deletions(-) diff --git a/drivers/gpu/drm/ingenic/ingenic-drm-drv.c b/drivers/gpu/drm/ingenic/ingenic-drm-drv.c index bc2a7e9bab07..c755bdd5563b 100644 --- a/drivers/gpu/drm/ingenic/ingenic-drm-drv.c +++ b/drivers/gpu/drm/ingenic/ingenic-drm-drv.c @@ -57,7 +57,6 @@ struct ingenic_drm { */ struct drm_plane f0, f1, *ipu_plane; struct drm_crtc crtc; - struct drm_encoder encoder; struct device *dev; struct regmap *map; @@ -112,12 +111,6 @@ static inline struct ingenic_drm *drm_crtc_get_priv(struct drm_crtc *crtc) return container_of(crtc, struct ingenic_drm, crtc); } -static inline struct ingenic_drm * -drm_encoder_get_priv(struct drm_encoder *encoder) -{ - return container_of(encoder, struct ingenic_drm, encoder); -} - static void ingenic_drm_crtc_atomic_enable(struct drm_crtc *crtc, struct drm_crtc_state *state) { @@ -468,7 +461,7 @@ static void ingenic_drm_encoder_atomic_mode_set(struct drm_encoder *encoder, struct drm_crtc_state *crtc_state, struct drm_connector_state *conn_state) { - struct ingenic_drm *priv = drm_encoder_get_priv(encoder); + struct ingenic_drm *priv = drm_device_get_priv(encoder->dev); struct drm_display_mode *mode = &crtc_state->adjusted_mode; struct drm_connector *conn = conn_state->connector; struct drm_display_info *info = &conn->display_info; @@ -688,9 +681,11 @@ static int ingenic_drm_bind(struct device *dev) struct clk *parent_clk; struct drm_bridge *bridge; struct drm_panel *panel; + struct drm_encoder *encoder; struct drm_device *drm; void __iomem *base; long parent_rate; + unsigned int i, clone_mask = 0; int ret, irq; soc_info = of_device_get_match_data(dev); @@ -752,17 +747,6 @@ static int ingenic_drm_bind(struct device *dev) return PTR_ERR(priv->pix_clk); } - ret = drm_of_find_panel_or_bridge(dev->of_node, 0, 0, &panel, &bridge); - if (ret) { - if (ret != -EPROBE_DEFER) - dev_err(dev, "Failed to get panel handle\n"); - return ret; - } - - if (panel) - bridge = devm_drm_panel_bridge_add_typed(dev, panel, - DRM_MODE_CONNECTOR_DPI); - priv->dma_hwdesc_f1 = dmam_alloc_coherent(dev, sizeof(*priv->dma_hwdesc_f1), &priv->dma_hwdesc_phys_f1, GFP_KERNEL); @@ -844,22 +828,47 @@ static int ingenic_drm_bind(struct device *dev) } } - priv->encoder.possible_crtcs = 1; + for (i = 0; ; i++) { + ret = drm_of_find_panel_or_bridge(dev->of_node, 0, i, &panel, &bridge); + if (ret) { + if (ret == -ENODEV) + break; /* we're done */ + if (ret != -EPROBE_DEFER) + dev_err(dev, "Failed to get bridge handle\n"); + return ret; + } - drm_encoder_helper_add(&priv->encoder, - &ingenic_drm_encoder_helper_funcs); + if (panel) + bridge = devm_drm_panel_bridge_add_typed(dev, panel, + DRM_MODE_CONNECTOR_DPI); - ret = drm_simple_encoder_init(drm, &priv->encoder, - DRM_MODE_ENCODER_DPI); - if (ret) { - dev_err(dev, "Failed to init encoder: %i\n", ret); - return ret; + encoder = devm_kzalloc(dev, sizeof(*encoder), GFP_KERNEL); + if (!encoder) + return -ENOMEM; + + encoder->possible_crtcs = 1; + + drm_encoder_helper_add(encoder, &ingenic_drm_encoder_helper_funcs); + + ret = drm_simple_encoder_init(drm, encoder, DRM_MODE_ENCODER_DPI); + if (ret) { + dev_err(dev, "Failed to init encoder: %d\n", ret); + return ret; + } + + ret = drm_bridge_attach(encoder, bridge, NULL, 0); + if (ret) { + dev_err(dev, "Unable to attach bridge\n"); + return ret; + } } - ret = drm_bridge_attach(&priv->encoder, bridge, NULL, 0); - if (ret) { - dev_err(dev, "Unable to attach bridge\n"); - return ret; + drm_for_each_encoder(encoder, drm) { + clone_mask |= BIT(drm_encoder_index(encoder)); + } + + drm_for_each_encoder(encoder, drm) { + encoder->possible_clones = clone_mask; } ret = drm_irq_install(drm, irq); From a786e8cab152da7ec8b9859da6e77cb59c7fa789 Mon Sep 17 00:00:00 2001 From: Paul Cercueil Date: Thu, 16 Jul 2020 18:38:46 +0200 Subject: [PATCH 27/59] drm/ingenic: Bump driver to version 1.1 Bump version to 1.1 and set date to 2020-07-16. v3: New patch Signed-off-by: Paul Cercueil Reviewed-by: Sam Ravnborg Link: https://patchwork.freedesktop.org/patch/msgid/20200716163846.174790-12-paul@crapouillou.net --- drivers/gpu/drm/ingenic/ingenic-drm-drv.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/ingenic/ingenic-drm-drv.c b/drivers/gpu/drm/ingenic/ingenic-drm-drv.c index c755bdd5563b..b6d946fbeaf5 100644 --- a/drivers/gpu/drm/ingenic/ingenic-drm-drv.c +++ b/drivers/gpu/drm/ingenic/ingenic-drm-drv.c @@ -599,9 +599,9 @@ static struct drm_driver ingenic_drm_driver_data = { .driver_features = DRIVER_MODESET | DRIVER_GEM | DRIVER_ATOMIC, .name = "ingenic-drm", .desc = "DRM module for Ingenic SoCs", - .date = "20190422", + .date = "20200716", .major = 1, - .minor = 0, + .minor = 1, .patchlevel = 0, .fops = &ingenic_drm_fops, From 40a55dc13e9dfa3a9575fd95633733554f4cde67 Mon Sep 17 00:00:00 2001 From: Paul Cercueil Date: Sun, 19 Jul 2020 11:38:34 +0200 Subject: [PATCH 28/59] drm/ingenic: Silence uninitialized-variable warning Silence compiler warning about used but uninitialized 'ipu_state' variable. In practice, the variable would never be used when uninitialized, but the compiler cannot know that 'priv->ipu_plane' will always be NULL if CONFIG_INGENIC_IPU is disabled. Silence the warning by initializing the value to NULL. Signed-off-by: Paul Cercueil Reviewed-by: Sam Ravnborg Link: https://patchwork.freedesktop.org/patch/msgid/20200719093834.14084-1-paul@crapouillou.net --- drivers/gpu/drm/ingenic/ingenic-drm-drv.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/ingenic/ingenic-drm-drv.c b/drivers/gpu/drm/ingenic/ingenic-drm-drv.c index b6d946fbeaf5..ada990a7f911 100644 --- a/drivers/gpu/drm/ingenic/ingenic-drm-drv.c +++ b/drivers/gpu/drm/ingenic/ingenic-drm-drv.c @@ -198,7 +198,7 @@ static int ingenic_drm_crtc_atomic_check(struct drm_crtc *crtc, struct drm_crtc_state *state) { struct ingenic_drm *priv = drm_crtc_get_priv(crtc); - struct drm_plane_state *f1_state, *f0_state, *ipu_state; + struct drm_plane_state *f1_state, *f0_state, *ipu_state = NULL; long rate; if (!drm_atomic_crtc_needs_modeset(state)) @@ -229,7 +229,7 @@ static int ingenic_drm_crtc_atomic_check(struct drm_crtc *crtc, /* If all the planes are disabled, we won't get a VBLANK IRQ */ priv->no_vblank = !f1_state->fb && !f0_state->fb && - !(priv->ipu_plane && ipu_state->fb); + !(ipu_state && ipu_state->fb); } return 0; From a5f23a72355d8175506449a8b918785c9cd619ab Mon Sep 17 00:00:00 2001 From: Thomas Zimmermann Date: Thu, 16 Jul 2020 14:53:48 +0200 Subject: [PATCH 29/59] drm/vram-helper: Managed vram helpers Calling drmm_vram_helper_init() sets up a managed instance of VRAM MM. Releasing the DRM device also frees the memory manager. The patch also updates the DRM documentation for VRAM helpers. The tutorial now describes the new managed interface. The old interfaces are deprecated and should not be used in new code. v2: * rename init function to drmm_vram_helper_init() * return errno code from init function; caller does not need vram_mm anyway * update documentation and remove docs for deprecated un-managed functions Signed-off-by: Thomas Zimmermann Reviewed-by: Sam Ravnborg Link: https://patchwork.freedesktop.org/patch/msgid/20200716125353.31512-2-tzimmermann@suse.de --- drivers/gpu/drm/drm_gem_vram_helper.c | 84 +++++++++++++++------------ include/drm/drm_gem_vram_helper.h | 3 + 2 files changed, 51 insertions(+), 36 deletions(-) diff --git a/drivers/gpu/drm/drm_gem_vram_helper.c b/drivers/gpu/drm/drm_gem_vram_helper.c index ad096600d89f..c96d54f7c180 100644 --- a/drivers/gpu/drm/drm_gem_vram_helper.c +++ b/drivers/gpu/drm/drm_gem_vram_helper.c @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -40,12 +41,11 @@ static const struct drm_gem_object_funcs drm_gem_vram_object_funcs; * the frame's scanout buffer or the cursor image. If there's no more space * left in VRAM, inactive GEM objects can be moved to system memory. * - * The easiest way to use the VRAM helper library is to call - * drm_vram_helper_alloc_mm(). The function allocates and initializes an - * instance of &struct drm_vram_mm in &struct drm_device.vram_mm . Use - * &DRM_GEM_VRAM_DRIVER to initialize &struct drm_driver and - * &DRM_VRAM_MM_FILE_OPERATIONS to initialize &struct file_operations; - * as illustrated below. + * To initialize the VRAM helper library call drmm_vram_helper_alloc_mm(). + * The function allocates and initializes an instance of &struct drm_vram_mm + * in &struct drm_device.vram_mm . Use &DRM_GEM_VRAM_DRIVER to initialize + * &struct drm_driver and &DRM_VRAM_MM_FILE_OPERATIONS to initialize + * &struct file_operations; as illustrated below. * * .. code-block:: c * @@ -69,7 +69,7 @@ static const struct drm_gem_object_funcs drm_gem_vram_object_funcs; * // setup device, vram base and size * // ... * - * ret = drm_vram_helper_alloc_mm(dev, vram_base, vram_size); + * ret = drmm_vram_helper_alloc_mm(dev, vram_base, vram_size); * if (ret) * return ret; * return 0; @@ -81,20 +81,12 @@ static const struct drm_gem_object_funcs drm_gem_vram_object_funcs; * manages an area of video RAM with VRAM MM and provides GEM VRAM objects * to userspace. * - * To clean up the VRAM memory management, call drm_vram_helper_release_mm() - * in the driver's clean-up code. + * You don't have to clean up the instance of VRAM MM. + * drmm_vram_helper_alloc_mm() is a managed interface that installs a + * clean-up handler to run during the DRM device's release. * - * .. code-block:: c - * - * void fini_drm_driver() - * { - * struct drm_device *dev = ...; - * - * drm_vram_helper_release_mm(dev); - * } - * - * For drawing or scanout operations, buffer object have to be pinned in video - * RAM. Call drm_gem_vram_pin() with &DRM_GEM_VRAM_PL_FLAG_VRAM or + * For drawing or scanout operations, rsp. buffer objects have to be pinned + * in video RAM. Call drm_gem_vram_pin() with &DRM_GEM_VRAM_PL_FLAG_VRAM or * &DRM_GEM_VRAM_PL_FLAG_SYSTEM to pin a buffer object in video RAM or system * memory. Call drm_gem_vram_unpin() to release the pinned object afterwards. * @@ -1176,17 +1168,7 @@ static void drm_vram_mm_cleanup(struct drm_vram_mm *vmm) * Helpers for integration with struct drm_device */ -/** - * drm_vram_helper_alloc_mm - Allocates a device's instance of \ - &struct drm_vram_mm - * @dev: the DRM device - * @vram_base: the base address of the video memory - * @vram_size: the size of the video memory in bytes - * - * Returns: - * The new instance of &struct drm_vram_mm on success, or - * an ERR_PTR()-encoded errno code otherwise. - */ +/* deprecated; use drmm_vram_mm_init() */ struct drm_vram_mm *drm_vram_helper_alloc_mm( struct drm_device *dev, uint64_t vram_base, size_t vram_size) { @@ -1212,11 +1194,6 @@ err_kfree: } EXPORT_SYMBOL(drm_vram_helper_alloc_mm); -/** - * drm_vram_helper_release_mm - Releases a device's instance of \ - &struct drm_vram_mm - * @dev: the DRM device - */ void drm_vram_helper_release_mm(struct drm_device *dev) { if (!dev->vram_mm) @@ -1228,6 +1205,41 @@ void drm_vram_helper_release_mm(struct drm_device *dev) } EXPORT_SYMBOL(drm_vram_helper_release_mm); +static void drm_vram_mm_release(struct drm_device *dev, void *ptr) +{ + drm_vram_helper_release_mm(dev); +} + +/** + * drmm_vram_helper_init - Initializes a device's instance of + * &struct drm_vram_mm + * @dev: the DRM device + * @vram_base: the base address of the video memory + * @vram_size: the size of the video memory in bytes + * + * Creates a new instance of &struct drm_vram_mm and stores it in + * struct &drm_device.vram_mm. The instance is auto-managed and cleaned + * up as part of device cleanup. Calling this function multiple times + * will generate an error message. + * + * Returns: + * 0 on success, or a negative errno code otherwise. + */ +int drmm_vram_helper_init(struct drm_device *dev, uint64_t vram_base, + size_t vram_size) +{ + struct drm_vram_mm *vram_mm; + + if (drm_WARN_ON_ONCE(dev, dev->vram_mm)) + return 0; + + vram_mm = drm_vram_helper_alloc_mm(dev, vram_base, vram_size); + if (IS_ERR(vram_mm)) + return PTR_ERR(vram_mm); + return drmm_add_action_or_reset(dev, drm_vram_mm_release, NULL); +} +EXPORT_SYMBOL(drmm_vram_helper_init); + /* * Mode-config helpers */ diff --git a/include/drm/drm_gem_vram_helper.h b/include/drm/drm_gem_vram_helper.h index b63bcd1b996d..035332f3723f 100644 --- a/include/drm/drm_gem_vram_helper.h +++ b/include/drm/drm_gem_vram_helper.h @@ -206,6 +206,9 @@ struct drm_vram_mm *drm_vram_helper_alloc_mm( struct drm_device *dev, uint64_t vram_base, size_t vram_size); void drm_vram_helper_release_mm(struct drm_device *dev); +int drmm_vram_helper_init(struct drm_device *dev, uint64_t vram_base, + size_t vram_size); + /* * Mode-config helpers */ From 48fde424c2f7ea7f6f324dcfd292cc50a850367c Mon Sep 17 00:00:00 2001 From: Thomas Zimmermann Date: Thu, 16 Jul 2020 14:53:49 +0200 Subject: [PATCH 30/59] drm/ast: Rename ast_ttm.c to ast_mm.c Although built upon TTM, the ast driver's VRAM MM helper does not expose TTM to its users. Rename the related ast file to ast_mm.c. Signed-off-by: Thomas Zimmermann Reviewed-by: Sam Ravnborg Link: https://patchwork.freedesktop.org/patch/msgid/20200716125353.31512-3-tzimmermann@suse.de --- drivers/gpu/drm/ast/Makefile | 2 +- drivers/gpu/drm/ast/{ast_ttm.c => ast_mm.c} | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename drivers/gpu/drm/ast/{ast_ttm.c => ast_mm.c} (100%) diff --git a/drivers/gpu/drm/ast/Makefile b/drivers/gpu/drm/ast/Makefile index 6a5b3b247316..2265a8a624dd 100644 --- a/drivers/gpu/drm/ast/Makefile +++ b/drivers/gpu/drm/ast/Makefile @@ -3,7 +3,7 @@ # Makefile for the drm device driver. This driver provides support for the # Direct Rendering Infrastructure (DRI) in XFree86 4.1.0 and higher. -ast-y := ast_cursor.o ast_drv.o ast_main.o ast_mode.o ast_ttm.o ast_post.o \ +ast-y := ast_cursor.o ast_drv.o ast_main.o ast_mm.o ast_mode.o ast_post.o \ ast_dp501.o obj-$(CONFIG_DRM_AST) := ast.o diff --git a/drivers/gpu/drm/ast/ast_ttm.c b/drivers/gpu/drm/ast/ast_mm.c similarity index 100% rename from drivers/gpu/drm/ast/ast_ttm.c rename to drivers/gpu/drm/ast/ast_mm.c From 8e46dc585389fedd1563698214b2153825789e1e Mon Sep 17 00:00:00 2001 From: Thomas Zimmermann Date: Thu, 16 Jul 2020 14:53:50 +0200 Subject: [PATCH 31/59] drm/ast: Use managed VRAM-helper initialization As a first step to managed MM code in ast, switch over VRAM MM helpers. v2: * updated to use drmm_vram_helper_init() Signed-off-by: Thomas Zimmermann Reviewed-by: Sam Ravnborg Link: https://patchwork.freedesktop.org/patch/msgid/20200716125353.31512-4-tzimmermann@suse.de --- drivers/gpu/drm/ast/ast_mm.c | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/drivers/gpu/drm/ast/ast_mm.c b/drivers/gpu/drm/ast/ast_mm.c index 9c3788a4c1c5..f0a96cae68c9 100644 --- a/drivers/gpu/drm/ast/ast_mm.c +++ b/drivers/gpu/drm/ast/ast_mm.c @@ -35,15 +35,12 @@ int ast_mm_init(struct ast_private *ast) { - struct drm_vram_mm *vmm; int ret; struct drm_device *dev = ast->dev; - vmm = drm_vram_helper_alloc_mm( - dev, pci_resource_start(dev->pdev, 0), - ast->vram_size); - if (IS_ERR(vmm)) { - ret = PTR_ERR(vmm); + ret = drmm_vram_helper_init(dev, pci_resource_start(dev->pdev, 0), + ast->vram_size); + if (ret) { drm_err(dev, "Error initializing VRAM MM; %d\n", ret); return ret; } @@ -60,8 +57,6 @@ void ast_mm_fini(struct ast_private *ast) { struct drm_device *dev = ast->dev; - drm_vram_helper_release_mm(dev); - arch_phys_wc_del(ast->fb_mtrr); arch_io_free_memtype_wc(pci_resource_start(dev->pdev, 0), pci_resource_len(dev->pdev, 0)); From 0149e7805b3c52753d727a7c519757a77435967b Mon Sep 17 00:00:00 2001 From: Thomas Zimmermann Date: Thu, 16 Jul 2020 14:53:51 +0200 Subject: [PATCH 32/59] drm/ast: Move VRAM size detection to ast_mm.c VRAM size detection is only relevant to the memory management. Move the code into ast_mm.c. While at it, rename the function to ast_get_vram_size(). The function argument's type is now struct ast_private. The result is stored in a local variable and not in struct ast_private any longer. Signed-off-by: Thomas Zimmermann Reviewed-by: Sam Ravnborg Link: https://patchwork.freedesktop.org/patch/msgid/20200716125353.31512-5-tzimmermann@suse.de --- drivers/gpu/drm/ast/ast_drv.h | 1 - drivers/gpu/drm/ast/ast_main.c | 38 ++-------------------------- drivers/gpu/drm/ast/ast_mm.c | 45 +++++++++++++++++++++++++++++++++- 3 files changed, 46 insertions(+), 38 deletions(-) diff --git a/drivers/gpu/drm/ast/ast_drv.h b/drivers/gpu/drm/ast/ast_drv.h index c8c442e9efdc..9a770e5b36d1 100644 --- a/drivers/gpu/drm/ast/ast_drv.h +++ b/drivers/gpu/drm/ast/ast_drv.h @@ -110,7 +110,6 @@ struct ast_private { uint32_t dram_bus_width; uint32_t dram_type; uint32_t mclk; - uint32_t vram_size; int fb_mtrr; diff --git a/drivers/gpu/drm/ast/ast_main.c b/drivers/gpu/drm/ast/ast_main.c index 860a43a64b31..b162cc82204d 100644 --- a/drivers/gpu/drm/ast/ast_main.c +++ b/drivers/gpu/drm/ast/ast_main.c @@ -378,38 +378,6 @@ static int ast_get_dram_info(struct drm_device *dev) return 0; } -static u32 ast_get_vram_info(struct drm_device *dev) -{ - struct ast_private *ast = to_ast_private(dev); - u8 jreg; - u32 vram_size; - ast_open_key(ast); - - vram_size = AST_VIDMEM_DEFAULT_SIZE; - jreg = ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xaa, 0xff); - switch (jreg & 3) { - case 0: vram_size = AST_VIDMEM_SIZE_8M; break; - case 1: vram_size = AST_VIDMEM_SIZE_16M; break; - case 2: vram_size = AST_VIDMEM_SIZE_32M; break; - case 3: vram_size = AST_VIDMEM_SIZE_64M; break; - } - - jreg = ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0x99, 0xff); - switch (jreg & 0x03) { - case 1: - vram_size -= 0x100000; - break; - case 2: - vram_size -= 0x200000; - break; - case 3: - vram_size -= 0x400000; - break; - } - - return vram_size; -} - int ast_driver_load(struct drm_device *dev, unsigned long flags) { struct ast_private *ast; @@ -456,10 +424,8 @@ int ast_driver_load(struct drm_device *dev, unsigned long flags) ret = ast_get_dram_info(dev); if (ret) goto out_free; - ast->vram_size = ast_get_vram_info(dev); - drm_info(dev, "dram MCLK=%u Mhz type=%d bus_width=%d size=%08x\n", - ast->mclk, ast->dram_type, - ast->dram_bus_width, ast->vram_size); + drm_info(dev, "dram MCLK=%u Mhz type=%d bus_width=%d\n", + ast->mclk, ast->dram_type, ast->dram_bus_width); ret = ast_mm_init(ast); if (ret) diff --git a/drivers/gpu/drm/ast/ast_mm.c b/drivers/gpu/drm/ast/ast_mm.c index f0a96cae68c9..aaeb19d01052 100644 --- a/drivers/gpu/drm/ast/ast_mm.c +++ b/drivers/gpu/drm/ast/ast_mm.c @@ -33,13 +33,56 @@ #include "ast_drv.h" +static u32 ast_get_vram_size(struct ast_private *ast) +{ + u8 jreg; + u32 vram_size; + + ast_open_key(ast); + + vram_size = AST_VIDMEM_DEFAULT_SIZE; + jreg = ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xaa, 0xff); + switch (jreg & 3) { + case 0: + vram_size = AST_VIDMEM_SIZE_8M; + break; + case 1: + vram_size = AST_VIDMEM_SIZE_16M; + break; + case 2: + vram_size = AST_VIDMEM_SIZE_32M; + break; + case 3: + vram_size = AST_VIDMEM_SIZE_64M; + break; + } + + jreg = ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0x99, 0xff); + switch (jreg & 0x03) { + case 1: + vram_size -= 0x100000; + break; + case 2: + vram_size -= 0x200000; + break; + case 3: + vram_size -= 0x400000; + break; + } + + return vram_size; +} + int ast_mm_init(struct ast_private *ast) { + u32 vram_size; int ret; struct drm_device *dev = ast->dev; + vram_size = ast_get_vram_size(ast); + ret = drmm_vram_helper_init(dev, pci_resource_start(dev->pdev, 0), - ast->vram_size); + vram_size); if (ret) { drm_err(dev, "Error initializing VRAM MM; %d\n", ret); return ret; From 244d012801dae30c91983b360457c78d481584b0 Mon Sep 17 00:00:00 2001 From: Thomas Zimmermann Date: Thu, 16 Jul 2020 14:53:52 +0200 Subject: [PATCH 33/59] drm/ast: Initialize DRAM type before posting GPU Posting the GPU requires the correct DRAM type to be stored in struct ast_private. Therefore first initialize the DRAM info and then post the GPU. This restores the original order of instructions in this function. Signed-off-by: Thomas Zimmermann Reviewed-by: Sam Ravnborg Acked-by: Benjamin Herrenschmidt Fixes: bad09da6deab ("drm/ast: Fixed vram size incorrect issue on POWER") Cc: Joel Stanley Cc: Y.C. Chen Cc: Benjamin Herrenschmidt Cc: Dave Airlie Cc: Thomas Zimmermann Cc: Gerd Hoffmann Cc: Daniel Vetter Cc: Sam Ravnborg Cc: Emil Velikov Cc: "Y.C. Chen" Cc: # v4.11+ Link: https://patchwork.freedesktop.org/patch/msgid/20200716125353.31512-6-tzimmermann@suse.de --- drivers/gpu/drm/ast/ast_main.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/ast/ast_main.c b/drivers/gpu/drm/ast/ast_main.c index b162cc82204d..87e5baded2a7 100644 --- a/drivers/gpu/drm/ast/ast_main.c +++ b/drivers/gpu/drm/ast/ast_main.c @@ -418,15 +418,15 @@ int ast_driver_load(struct drm_device *dev, unsigned long flags) ast_detect_chip(dev, &need_post); - if (need_post) - ast_post_gpu(dev); - ret = ast_get_dram_info(dev); if (ret) goto out_free; drm_info(dev, "dram MCLK=%u Mhz type=%d bus_width=%d\n", ast->mclk, ast->dram_type, ast->dram_bus_width); + if (need_post) + ast_post_gpu(dev); + ret = ast_mm_init(ast); if (ret) goto out_free; From 03ba7e00f8054e19ffff4a03018f4e0b13a6f291 Mon Sep 17 00:00:00 2001 From: Thomas Zimmermann Date: Thu, 16 Jul 2020 14:53:53 +0200 Subject: [PATCH 34/59] drm/ast: Use managed MM initialization Cleaning up ast's MM code with ast_mm_fini() resets the write-combine flags on the VRAM I/O memory. Drop ast_mm_fini() in favor of an auto- release callback. Releasing the device also executes the callback. Signed-off-by: Thomas Zimmermann Reviewed-by: Sam Ravnborg Link: https://patchwork.freedesktop.org/patch/msgid/20200716125353.31512-7-tzimmermann@suse.de --- drivers/gpu/drm/ast/ast_drv.h | 1 - drivers/gpu/drm/ast/ast_main.c | 1 - drivers/gpu/drm/ast/ast_mm.c | 23 ++++++++++++----------- 3 files changed, 12 insertions(+), 13 deletions(-) diff --git a/drivers/gpu/drm/ast/ast_drv.h b/drivers/gpu/drm/ast/ast_drv.h index 9a770e5b36d1..e3a264ac7ee2 100644 --- a/drivers/gpu/drm/ast/ast_drv.h +++ b/drivers/gpu/drm/ast/ast_drv.h @@ -291,7 +291,6 @@ int ast_mode_config_init(struct ast_private *ast); #define AST_MM_ALIGN_MASK ((1 << AST_MM_ALIGN_SHIFT) - 1) int ast_mm_init(struct ast_private *ast); -void ast_mm_fini(struct ast_private *ast); /* ast post */ void ast_enable_vga(struct drm_device *dev); diff --git a/drivers/gpu/drm/ast/ast_main.c b/drivers/gpu/drm/ast/ast_main.c index 87e5baded2a7..dd12b55d57a2 100644 --- a/drivers/gpu/drm/ast/ast_main.c +++ b/drivers/gpu/drm/ast/ast_main.c @@ -452,6 +452,5 @@ void ast_driver_unload(struct drm_device *dev) ast_release_firmware(dev); kfree(ast->dp501_fw_addr); - ast_mm_fini(ast); kfree(ast); } diff --git a/drivers/gpu/drm/ast/ast_mm.c b/drivers/gpu/drm/ast/ast_mm.c index aaeb19d01052..9186ec3ebbe0 100644 --- a/drivers/gpu/drm/ast/ast_mm.c +++ b/drivers/gpu/drm/ast/ast_mm.c @@ -28,8 +28,9 @@ #include -#include #include +#include +#include #include "ast_drv.h" @@ -73,6 +74,15 @@ static u32 ast_get_vram_size(struct ast_private *ast) return vram_size; } +static void ast_mm_release(struct drm_device *dev, void *ptr) +{ + struct ast_private *ast = to_ast_private(dev); + + arch_phys_wc_del(ast->fb_mtrr); + arch_io_free_memtype_wc(pci_resource_start(dev->pdev, 0), + pci_resource_len(dev->pdev, 0)); +} + int ast_mm_init(struct ast_private *ast) { u32 vram_size; @@ -93,14 +103,5 @@ int ast_mm_init(struct ast_private *ast) ast->fb_mtrr = arch_phys_wc_add(pci_resource_start(dev->pdev, 0), pci_resource_len(dev->pdev, 0)); - return 0; -} - -void ast_mm_fini(struct ast_private *ast) -{ - struct drm_device *dev = ast->dev; - - arch_phys_wc_del(ast->fb_mtrr); - arch_io_free_memtype_wc(pci_resource_start(dev->pdev, 0), - pci_resource_len(dev->pdev, 0)); + return drmm_add_action_or_reset(dev, ast_mm_release, NULL); } From 880a74853a5ea95461b55948d3bed0345d0f96ae Mon Sep 17 00:00:00 2001 From: Christophe JAILLET Date: Sat, 18 Jul 2020 09:28:22 +0200 Subject: [PATCH 35/59] drm/i810: switch from 'pci_' to 'dma_' API The wrappers in include/linux/pci-dma-compat.h should go away. The patch has been generated with the coccinelle script below and has been hand modified to replace GFP_ with a correct flag. It has been compile tested. When memory is allocated in 'i810_dma_initialize()' GFP_KERNEL can be used because its only caller, 'i810_dma_init()', already use it and no lock is taken in the between. @@ @@ - PCI_DMA_BIDIRECTIONAL + DMA_BIDIRECTIONAL @@ @@ - PCI_DMA_TODEVICE + DMA_TO_DEVICE @@ @@ - PCI_DMA_FROMDEVICE + DMA_FROM_DEVICE @@ @@ - PCI_DMA_NONE + DMA_NONE @@ expression e1, e2, e3; @@ - pci_alloc_consistent(e1, e2, e3) + dma_alloc_coherent(&e1->dev, e2, e3, GFP_) @@ expression e1, e2, e3; @@ - pci_zalloc_consistent(e1, e2, e3) + dma_alloc_coherent(&e1->dev, e2, e3, GFP_) @@ expression e1, e2, e3, e4; @@ - pci_free_consistent(e1, e2, e3, e4) + dma_free_coherent(&e1->dev, e2, e3, e4) @@ expression e1, e2, e3, e4; @@ - pci_map_single(e1, e2, e3, e4) + dma_map_single(&e1->dev, e2, e3, e4) @@ expression e1, e2, e3, e4; @@ - pci_unmap_single(e1, e2, e3, e4) + dma_unmap_single(&e1->dev, e2, e3, e4) @@ expression e1, e2, e3, e4, e5; @@ - pci_map_page(e1, e2, e3, e4, e5) + dma_map_page(&e1->dev, e2, e3, e4, e5) @@ expression e1, e2, e3, e4; @@ - pci_unmap_page(e1, e2, e3, e4) + dma_unmap_page(&e1->dev, e2, e3, e4) @@ expression e1, e2, e3, e4; @@ - pci_map_sg(e1, e2, e3, e4) + dma_map_sg(&e1->dev, e2, e3, e4) @@ expression e1, e2, e3, e4; @@ - pci_unmap_sg(e1, e2, e3, e4) + dma_unmap_sg(&e1->dev, e2, e3, e4) @@ expression e1, e2, e3, e4; @@ - pci_dma_sync_single_for_cpu(e1, e2, e3, e4) + dma_sync_single_for_cpu(&e1->dev, e2, e3, e4) @@ expression e1, e2, e3, e4; @@ - pci_dma_sync_single_for_device(e1, e2, e3, e4) + dma_sync_single_for_device(&e1->dev, e2, e3, e4) @@ expression e1, e2, e3, e4; @@ - pci_dma_sync_sg_for_cpu(e1, e2, e3, e4) + dma_sync_sg_for_cpu(&e1->dev, e2, e3, e4) @@ expression e1, e2, e3, e4; @@ - pci_dma_sync_sg_for_device(e1, e2, e3, e4) + dma_sync_sg_for_device(&e1->dev, e2, e3, e4) @@ expression e1, e2; @@ - pci_dma_mapping_error(e1, e2) + dma_mapping_error(&e1->dev, e2) @@ expression e1, e2; @@ - pci_set_dma_mask(e1, e2) + dma_set_mask(&e1->dev, e2) @@ expression e1, e2; @@ - pci_set_consistent_dma_mask(e1, e2) + dma_set_coherent_mask(&e1->dev, e2) Signed-off-by: Christophe JAILLET Signed-off-by: Daniel Vetter Link: https://patchwork.freedesktop.org/patch/msgid/20200718072822.339064-1-christophe.jaillet@wanadoo.fr --- drivers/gpu/drm/i810/i810_dma.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/gpu/drm/i810/i810_dma.c b/drivers/gpu/drm/i810/i810_dma.c index b88c3d5f92b4..303c2d483c6e 100644 --- a/drivers/gpu/drm/i810/i810_dma.c +++ b/drivers/gpu/drm/i810/i810_dma.c @@ -220,9 +220,9 @@ static int i810_dma_cleanup(struct drm_device *dev) if (dev_priv->ring.virtual_start) drm_legacy_ioremapfree(&dev_priv->ring.map, dev); if (dev_priv->hw_status_page) { - pci_free_consistent(dev->pdev, PAGE_SIZE, - dev_priv->hw_status_page, - dev_priv->dma_status_page); + dma_free_coherent(&dev->pdev->dev, PAGE_SIZE, + dev_priv->hw_status_page, + dev_priv->dma_status_page); } kfree(dev->dev_private); dev->dev_private = NULL; @@ -398,8 +398,8 @@ static int i810_dma_initialize(struct drm_device *dev, /* Program Hardware Status Page */ dev_priv->hw_status_page = - pci_zalloc_consistent(dev->pdev, PAGE_SIZE, - &dev_priv->dma_status_page); + dma_alloc_coherent(&dev->pdev->dev, PAGE_SIZE, + &dev_priv->dma_status_page, GFP_KERNEL); if (!dev_priv->hw_status_page) { dev->dev_private = (void *)dev_priv; i810_dma_cleanup(dev); From 6d45fff5ba2ddb5d177115ecd9f6b4d0c9d3c79c Mon Sep 17 00:00:00 2001 From: Suraj Upadhyay Date: Sat, 18 Jul 2020 20:39:55 +0530 Subject: [PATCH 36/59] drm: core: Convert device logging to drm_* functions. Convert device logging with dev_* functions into drm_* functions. The patch has been generated with the coccinelle script below. The script focuses on instances of dev_* functions where the drm device context is clearly visible in its arguments. @@expression E1; expression list E2; @@ -dev_warn(E1->dev, E2) +drm_warn(E1, E2) @@expression E1; expression list E2; @@ -dev_info(E1->dev, E2) +drm_info(E1, E2) @@expression E1; expression list E2; @@ -dev_err(E1->dev, E2) +drm_err(E1, E2) @@expression E1; expression list E2; @@ -dev_info_once(E1->dev, E2) +drm_info_once(E1, E2) @@expression E1; expression list E2; @@ -dev_notice_once(E1->dev, E2) +drm_notice_once(E1, E2) @@expression E1; expression list E2; @@ -dev_warn_once(E1->dev, E2) +drm_warn_once(E1, E2) @@expression E1; expression list E2; @@ -dev_err_once(E1->dev, E2) +drm_err_once(E1, E2) @@expression E1; expression list E2; @@ -dev_err_ratelimited(E1->dev, E2) +drm_err_ratelimited(E1, E2) @@expression E1; expression list E2; @@ -dev_dbg(E1->dev, E2) +drm_dbg(E1, E2) Signed-off-by: Suraj Upadhyay Signed-off-by: Daniel Vetter Link: https://patchwork.freedesktop.org/patch/msgid/20200718150955.GA23103@blackclown --- drivers/gpu/drm/drm_edid.c | 6 ++---- drivers/gpu/drm/drm_gem_cma_helper.c | 4 ++-- drivers/gpu/drm/drm_mipi_dbi.c | 7 +++---- 3 files changed, 7 insertions(+), 10 deletions(-) diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c index b28b95014fe6..f6c723904204 100644 --- a/drivers/gpu/drm/drm_edid.c +++ b/drivers/gpu/drm/drm_edid.c @@ -1844,9 +1844,7 @@ static void connector_bad_edid(struct drm_connector *connector, if (connector->bad_edid_counter++ && !drm_debug_enabled(DRM_UT_KMS)) return; - dev_warn(connector->dev->dev, - "%s: EDID is invalid:\n", - connector->name); + drm_warn(connector->dev, "%s: EDID is invalid:\n", connector->name); for (i = 0; i < num_blocks; i++) { u8 *block = edid + i * EDID_LENGTH; char prefix[20]; @@ -5298,7 +5296,7 @@ int drm_add_edid_modes(struct drm_connector *connector, struct edid *edid) } if (!drm_edid_is_valid(edid)) { clear_eld(connector); - dev_warn(connector->dev->dev, "%s: EDID invalid.\n", + drm_warn(connector->dev, "%s: EDID invalid.\n", connector->name); return 0; } diff --git a/drivers/gpu/drm/drm_gem_cma_helper.c b/drivers/gpu/drm/drm_gem_cma_helper.c index 06a5b9ee1fe0..822edeadbab3 100644 --- a/drivers/gpu/drm/drm_gem_cma_helper.c +++ b/drivers/gpu/drm/drm_gem_cma_helper.c @@ -105,8 +105,8 @@ struct drm_gem_cma_object *drm_gem_cma_create(struct drm_device *drm, cma_obj->vaddr = dma_alloc_wc(drm->dev, size, &cma_obj->paddr, GFP_KERNEL | __GFP_NOWARN); if (!cma_obj->vaddr) { - dev_dbg(drm->dev, "failed to allocate buffer with size %zu\n", - size); + drm_dbg(drm, "failed to allocate buffer with size %zu\n", + size); ret = -ENOMEM; goto error; } diff --git a/drivers/gpu/drm/drm_mipi_dbi.c b/drivers/gpu/drm/drm_mipi_dbi.c index 79532b9a324a..ef01bd0a13e2 100644 --- a/drivers/gpu/drm/drm_mipi_dbi.c +++ b/drivers/gpu/drm/drm_mipi_dbi.c @@ -225,9 +225,8 @@ int mipi_dbi_buf_copy(void *dst, struct drm_framebuffer *fb, drm_fb_xrgb8888_to_rgb565(dst, src, fb, clip, swap); break; default: - dev_err_once(fb->dev->dev, "Format is not supported: %s\n", - drm_get_format_name(fb->format->format, - &format_name)); + drm_err_once(fb->dev, "Format is not supported: %s\n", + drm_get_format_name(fb->format->format, &format_name)); return -EINVAL; } @@ -295,7 +294,7 @@ static void mipi_dbi_fb_dirty(struct drm_framebuffer *fb, struct drm_rect *rect) width * height * 2); err_msg: if (ret) - dev_err_once(fb->dev->dev, "Failed to update display %d\n", ret); + drm_err_once(fb->dev, "Failed to update display %d\n", ret); drm_dev_exit(idx); } From f4b29bf77319df18348b56e9fd87bc7d4266220e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guido=20G=C3=BCnther?= Date: Mon, 23 Mar 2020 15:52:46 +0100 Subject: [PATCH 37/59] drm/mxsfb: Make supported modifiers explicit MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In contrast to other display controllers on imx like DCSS and ipuv3 lcdif/mxsfb does not support detiling e.g. vivante tiled layouts. Since mesa might assume otherwise make it explicit that only DRM_FORMAT_MOD_LINEAR is supported. Signed-off-by: Guido Günther Reviewed-by: Lucas Stach Signed-off-by: Stefan Agner Link: https://patchwork.freedesktop.org/patch/msgid/26877532e272c12a74c33188e2a72abafc9a2e1c.1584973664.git.agx@sigxcpu.org --- drivers/gpu/drm/mxsfb/mxsfb_drv.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/mxsfb/mxsfb_drv.c b/drivers/gpu/drm/mxsfb/mxsfb_drv.c index 47c7dce03da4..1d12ee12a895 100644 --- a/drivers/gpu/drm/mxsfb/mxsfb_drv.c +++ b/drivers/gpu/drm/mxsfb/mxsfb_drv.c @@ -69,6 +69,11 @@ static const uint32_t mxsfb_formats[] = { DRM_FORMAT_RGB565 }; +static const uint64_t mxsfb_modifiers[] = { + DRM_FORMAT_MOD_LINEAR, + DRM_FORMAT_MOD_INVALID +}; + static struct mxsfb_drm_private * drm_pipe_to_mxsfb_drm_private(struct drm_simple_display_pipe *pipe) { @@ -244,8 +249,8 @@ static int mxsfb_load(struct drm_device *drm, unsigned long flags) } ret = drm_simple_display_pipe_init(drm, &mxsfb->pipe, &mxsfb_funcs, - mxsfb_formats, ARRAY_SIZE(mxsfb_formats), NULL, - mxsfb->connector); + mxsfb_formats, ARRAY_SIZE(mxsfb_formats), + mxsfb_modifiers, mxsfb->connector); if (ret < 0) { dev_err(drm->dev, "Cannot setup simple display pipe\n"); goto err_vblank; From cf73db84817a9652eb27887981ad78670d2f9b7b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Thu, 16 Jul 2020 19:41:39 +0200 Subject: [PATCH 38/59] drm/mxsfb: drop unused function parameter MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit flags is unused since the driver was introduced in commit 45d59d704080 ("drm: Add new driver for MXSFB controller"). Signed-off-by: Uwe Kleine-König Signed-off-by: Stefan Agner Link: https://patchwork.freedesktop.org/patch/msgid/20200716174139.16602-1-u.kleine-koenig@pengutronix.de --- drivers/gpu/drm/mxsfb/mxsfb_drv.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/mxsfb/mxsfb_drv.c b/drivers/gpu/drm/mxsfb/mxsfb_drv.c index 1d12ee12a895..508764fccd27 100644 --- a/drivers/gpu/drm/mxsfb/mxsfb_drv.c +++ b/drivers/gpu/drm/mxsfb/mxsfb_drv.c @@ -196,7 +196,7 @@ static struct drm_simple_display_pipe_funcs mxsfb_funcs = { .disable_vblank = mxsfb_pipe_disable_vblank, }; -static int mxsfb_load(struct drm_device *drm, unsigned long flags) +static int mxsfb_load(struct drm_device *drm) { struct platform_device *pdev = to_platform_device(drm->dev); struct mxsfb_drm_private *mxsfb; @@ -403,7 +403,7 @@ static int mxsfb_probe(struct platform_device *pdev) if (IS_ERR(drm)) return PTR_ERR(drm); - ret = mxsfb_load(drm, 0); + ret = mxsfb_load(drm); if (ret) goto err_free; From b0487e0d96d58906e286592dd02e7292f53e399a Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Sun, 19 Jul 2020 19:14:28 +0200 Subject: [PATCH 39/59] drm: Replace HTTP links with HTTPS ones Rationale: Reduces attack surface on kernel devs opening the links for MITM as HTTPS traffic is much harder to manipulate. Deterministic algorithm: For each file: If not .svg: For each line: If doesn't contain `\bxmlns\b`: For each link, `\bhttp://[^# \t\r\n]*(?:\w|/)`: If neither `\bgnu\.org/license`, nor `\bmozilla\.org/MPL\b`: If both the HTTP and HTTPS versions return 200 OK and serve the same content: Replace HTTP with HTTPS. Signed-off-by: Alexander A. Klimov Signed-off-by: Daniel Vetter Link: https://patchwork.freedesktop.org/patch/msgid/20200719171428.60470-1-grandmaster@al2klimov.de --- Documentation/gpu/vgaarbiter.rst | 8 ++++---- drivers/gpu/drm/drm_modes.c | 2 +- include/uapi/drm/drm_mode.h | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Documentation/gpu/vgaarbiter.rst b/Documentation/gpu/vgaarbiter.rst index 0b41b051d021..339ed5fecd2e 100644 --- a/Documentation/gpu/vgaarbiter.rst +++ b/Documentation/gpu/vgaarbiter.rst @@ -185,7 +185,7 @@ enhancing the kernel code to adapt as a kernel module and also did the implementation of the user space side [3]. Now (2009) Tiago Vignatti and Dave Airlie finally put this work in shape and queued to Jesse Barnes' PCI tree. -0) http://cgit.freedesktop.org/xorg/xserver/commit/?id=4b42448a2388d40f257774fbffdccaea87bd0347 -1) http://lists.freedesktop.org/archives/xorg/2005-March/006663.html -2) http://lists.freedesktop.org/archives/xorg/2005-March/006745.html -3) http://lists.freedesktop.org/archives/xorg/2007-October/029507.html +0) https://cgit.freedesktop.org/xorg/xserver/commit/?id=4b42448a2388d40f257774fbffdccaea87bd0347 +1) https://lists.freedesktop.org/archives/xorg/2005-March/006663.html +2) https://lists.freedesktop.org/archives/xorg/2005-March/006745.html +3) https://lists.freedesktop.org/archives/xorg/2007-October/029507.html diff --git a/drivers/gpu/drm/drm_modes.c b/drivers/gpu/drm/drm_modes.c index f2865f88bd54..14b6f7638728 100644 --- a/drivers/gpu/drm/drm_modes.c +++ b/drivers/gpu/drm/drm_modes.c @@ -548,7 +548,7 @@ EXPORT_SYMBOL(drm_gtf_mode_complex); * Generalized Timing Formula is derived from: * * GTF Spreadsheet by Andy Morrish (1/5/97) - * available at http://www.vesa.org + * available at https://www.vesa.org * * And it is copied from the file of xserver/hw/xfree86/modes/xf86gtf.c. * What I have done is to translate it by using integer calculation. diff --git a/include/uapi/drm/drm_mode.h b/include/uapi/drm/drm_mode.h index 735c8cfdaaa1..deea447e5f22 100644 --- a/include/uapi/drm/drm_mode.h +++ b/include/uapi/drm/drm_mode.h @@ -497,7 +497,7 @@ struct drm_mode_fb_cmd2 { * In case of planar formats, this ioctl allows up to 4 * buffer objects with offsets and pitches per plane. * The pitch and offset order is dictated by the fourcc, - * e.g. NV12 (http://fourcc.org/yuv.php#NV12) is described as: + * e.g. NV12 (https://fourcc.org/yuv.php#NV12) is described as: * * YUV 4:2:0 image with a plane of 8 bit Y samples * followed by an interleaved U/V plane containing From 7c7b2a35a97c6927fedd5f39c559a413d8259d01 Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Sun, 19 Jul 2020 22:37:14 +0200 Subject: [PATCH 40/59] video: fbdev: Replace HTTP links with HTTPS ones Rationale: Reduces attack surface on kernel devs opening the links for MITM as HTTPS traffic is much harder to manipulate. Deterministic algorithm: For each file: If not .svg: For each line: If doesn't contain `\bxmlns\b`: For each link, `\bhttp://[^# \t\r\n]*(?:\w|/)`: If neither `\bgnu\.org/license`, nor `\bmozilla\.org/MPL\b`: If both the HTTP and HTTPS versions return 200 OK and serve the same content: Replace HTTP with HTTPS. Signed-off-by: Alexander A. Klimov Signed-off-by: Daniel Vetter Link: https://patchwork.freedesktop.org/patch/msgid/20200719203714.61745-1-grandmaster@al2klimov.de --- Documentation/fb/ep93xx-fb.rst | 2 +- drivers/video/fbdev/Kconfig | 8 ++++---- drivers/video/fbdev/core/fbmon.c | 4 ++-- drivers/video/fbdev/ep93xx-fb.c | 2 +- drivers/video/fbdev/grvga.c | 2 +- drivers/video/fbdev/macfb.c | 2 +- drivers/video/fbdev/metronomefb.c | 2 +- drivers/video/fbdev/omap2/omapfb/dss/Kconfig | 4 ++-- drivers/video/fbdev/omap2/omapfb/dss/hdmi.h | 2 +- drivers/video/fbdev/omap2/omapfb/dss/hdmi4.c | 2 +- drivers/video/fbdev/omap2/omapfb/dss/hdmi4_core.c | 2 +- drivers/video/fbdev/omap2/omapfb/dss/hdmi4_core.h | 2 +- drivers/video/fbdev/omap2/omapfb/dss/hdmi5_core.h | 2 +- drivers/video/fbdev/sa1100fb.c | 2 +- 14 files changed, 19 insertions(+), 19 deletions(-) diff --git a/Documentation/fb/ep93xx-fb.rst b/Documentation/fb/ep93xx-fb.rst index 6f7767926d1a..1dd67f4688c7 100644 --- a/Documentation/fb/ep93xx-fb.rst +++ b/Documentation/fb/ep93xx-fb.rst @@ -127,7 +127,7 @@ At least on the EP9315 there is a silicon bug which causes bit 27 of the VIDSCRNPAGE (framebuffer physical offset) to be tied low. There is an unofficial errata for this bug at:: - http://marc.info/?l=linux-arm-kernel&m=110061245502000&w=2 + https://marc.info/?l=linux-arm-kernel&m=110061245502000&w=2 By default the EP93xx framebuffer driver checks if the allocated physical address has bit 27 set. If it does, then the memory is freed and an diff --git a/drivers/video/fbdev/Kconfig b/drivers/video/fbdev/Kconfig index b33bb6318c2b..bd5a9773a588 100644 --- a/drivers/video/fbdev/Kconfig +++ b/drivers/video/fbdev/Kconfig @@ -824,7 +824,7 @@ config FB_OPENCORES systems (e.g. Altera socfpga or Xilinx Zynq) on FPGAs. The source code and specification for the core is available at - + config FB_S1D13XXX tristate "Epson S1D13XXX framebuffer support" @@ -835,7 +835,7 @@ config FB_S1D13XXX help Support for S1D13XXX framebuffer device family (currently only working with S1D13806). Product specs at - + config FB_ATMEL tristate "AT91 LCD Controller support" @@ -1193,7 +1193,7 @@ config FB_RADEON don't need to choose this to run the Radeon in plain VGA mode. There is a product page at - http://products.amd.com/en-us/GraphicCardResult.aspx + https://products.amd.com/en-us/GraphicCardResult.aspx config FB_RADEON_I2C bool "DDC/I2C for ATI Radeon support" @@ -1361,7 +1361,7 @@ config FB_SIS help This is the frame buffer device driver for the SiS 300, 315, 330 and 340 series as well as XGI V3XT, V5, V8, Z7 graphics chipsets. - Specs available at and . + Specs available at and . To compile this driver as a module, choose M here; the module will be called sisfb. diff --git a/drivers/video/fbdev/core/fbmon.c b/drivers/video/fbdev/core/fbmon.c index d62a1e43864e..1bf82dbc9e3c 100644 --- a/drivers/video/fbdev/core/fbmon.c +++ b/drivers/video/fbdev/core/fbmon.c @@ -19,7 +19,7 @@ * Generalized Timing Formula is derived from: * * GTF Spreadsheet by Andy Morrish (1/5/97) - * available at http://www.vesa.org + * available at https://www.vesa.org * * This file is subject to the terms and conditions of the GNU General Public * License. See the file COPYING in the main directory of this archive @@ -1201,7 +1201,7 @@ static void fb_timings_dclk(struct __fb_timings *timings) * ignored and @var will be filled with the calculated timings. * * All calculations are based on the VESA GTF Spreadsheet - * available at VESA's public ftp (http://www.vesa.org). + * available at VESA's public ftp (https://www.vesa.org). * * NOTES: * The timings generated by the GTF will be different from VESA diff --git a/drivers/video/fbdev/ep93xx-fb.c b/drivers/video/fbdev/ep93xx-fb.c index cda2ef337423..ba33b4dce0df 100644 --- a/drivers/video/fbdev/ep93xx-fb.c +++ b/drivers/video/fbdev/ep93xx-fb.c @@ -430,7 +430,7 @@ static int ep93xxfb_alloc_videomem(struct fb_info *info) /* * There is a bug in the ep93xx framebuffer which causes problems * if bit 27 of the physical address is set. - * See: http://marc.info/?l=linux-arm-kernel&m=110061245502000&w=2 + * See: https://marc.info/?l=linux-arm-kernel&m=110061245502000&w=2 * There does not seem to be any official errata for this, but I * have confirmed the problem exists on my hardware (ep9315) at * least. diff --git a/drivers/video/fbdev/grvga.c b/drivers/video/fbdev/grvga.c index 07dda03e0957..24818b276241 100644 --- a/drivers/video/fbdev/grvga.c +++ b/drivers/video/fbdev/grvga.c @@ -5,7 +5,7 @@ * 2011 (c) Aeroflex Gaisler AB * * Full documentation of the core can be found here: - * http://www.gaisler.com/products/grlib/grip.pdf + * https://www.gaisler.com/products/grlib/grip.pdf * * Contributors: Kristoffer Glembo */ diff --git a/drivers/video/fbdev/macfb.c b/drivers/video/fbdev/macfb.c index e05a97662ca8..312e35c9aa6c 100644 --- a/drivers/video/fbdev/macfb.c +++ b/drivers/video/fbdev/macfb.c @@ -478,7 +478,7 @@ static int macfb_setcolreg(unsigned regno, unsigned red, unsigned green, break; /* * 24-bit colour almost doesn't exist on 68k Macs -- - * http://support.apple.com/kb/TA28634 (Old Article: 10992) + * https://support.apple.com/kb/TA28634 (Old Article: 10992) */ case 24: case 32: diff --git a/drivers/video/fbdev/metronomefb.c b/drivers/video/fbdev/metronomefb.c index a42e2eceee48..952826557a0c 100644 --- a/drivers/video/fbdev/metronomefb.c +++ b/drivers/video/fbdev/metronomefb.c @@ -10,7 +10,7 @@ * Layout is based on skeletonfb.c by James Simmons and Geert Uytterhoeven. * * This work was made possible by help and equipment support from E-Ink - * Corporation. http://www.eink.com/ + * Corporation. https://www.eink.com/ * * This driver is written to be used with the Metronome display controller. * It is intended to be architecture independent. A board specific driver diff --git a/drivers/video/fbdev/omap2/omapfb/dss/Kconfig b/drivers/video/fbdev/omap2/omapfb/dss/Kconfig index 36b97fee2d57..cc81a19537d2 100644 --- a/drivers/video/fbdev/omap2/omapfb/dss/Kconfig +++ b/drivers/video/fbdev/omap2/omapfb/dss/Kconfig @@ -60,7 +60,7 @@ config FB_OMAP5_DSS_HDMI select FB_OMAP2_DSS_HDMI_COMMON help HDMI Interface for OMAP5 and similar cores. This adds the High - Definition Multimedia Interface. See http://www.hdmi.org/ for HDMI + Definition Multimedia Interface. See https://www.hdmi.org/ for HDMI specification. config FB_OMAP2_DSS_SDI @@ -79,7 +79,7 @@ config FB_OMAP2_DSS_DSI DSI is a high speed half-duplex serial interface between the host processor and a peripheral, such as a display or a framebuffer chip. - See http://www.mipi.org/ for DSI specifications. + See https://www.mipi.org/ for DSI specifications. config FB_OMAP2_DSS_MIN_FCK_PER_PCK int "Minimum FCK/PCK ratio (for scaling)" diff --git a/drivers/video/fbdev/omap2/omapfb/dss/hdmi.h b/drivers/video/fbdev/omap2/omapfb/dss/hdmi.h index b9d4480ecfad..9a7253355f6d 100644 --- a/drivers/video/fbdev/omap2/omapfb/dss/hdmi.h +++ b/drivers/video/fbdev/omap2/omapfb/dss/hdmi.h @@ -2,7 +2,7 @@ /* * HDMI driver definition for TI OMAP4 Processor. * - * Copyright (C) 2010-2011 Texas Instruments Incorporated - http://www.ti.com/ + * Copyright (C) 2010-2011 Texas Instruments Incorporated - https://www.ti.com/ */ #ifndef _HDMI_H diff --git a/drivers/video/fbdev/omap2/omapfb/dss/hdmi4.c b/drivers/video/fbdev/omap2/omapfb/dss/hdmi4.c index 1bbbda21d76c..22f1d37a968a 100644 --- a/drivers/video/fbdev/omap2/omapfb/dss/hdmi4.c +++ b/drivers/video/fbdev/omap2/omapfb/dss/hdmi4.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-2.0-only /* * HDMI interface DSS driver for TI's OMAP4 family of SoCs. - * Copyright (C) 2010-2011 Texas Instruments Incorporated - http://www.ti.com/ + * Copyright (C) 2010-2011 Texas Instruments Incorporated - https://www.ti.com/ * Authors: Yong Zhi * Mythri pk */ diff --git a/drivers/video/fbdev/omap2/omapfb/dss/hdmi4_core.c b/drivers/video/fbdev/omap2/omapfb/dss/hdmi4_core.c index 6b79b52d5fad..7ca1803bf161 100644 --- a/drivers/video/fbdev/omap2/omapfb/dss/hdmi4_core.c +++ b/drivers/video/fbdev/omap2/omapfb/dss/hdmi4_core.c @@ -3,7 +3,7 @@ * ti_hdmi_4xxx_ip.c * * HDMI TI81xx, TI38xx, TI OMAP4 etc IP driver Library - * Copyright (C) 2010-2011 Texas Instruments Incorporated - http://www.ti.com/ + * Copyright (C) 2010-2011 Texas Instruments Incorporated - https://www.ti.com/ * Authors: Yong Zhi * Mythri pk */ diff --git a/drivers/video/fbdev/omap2/omapfb/dss/hdmi4_core.h b/drivers/video/fbdev/omap2/omapfb/dss/hdmi4_core.h index f066d1f69132..b5c35277f06e 100644 --- a/drivers/video/fbdev/omap2/omapfb/dss/hdmi4_core.h +++ b/drivers/video/fbdev/omap2/omapfb/dss/hdmi4_core.h @@ -2,7 +2,7 @@ /* * HDMI header definition for OMAP4 HDMI core IP * - * Copyright (C) 2010-2011 Texas Instruments Incorporated - http://www.ti.com/ + * Copyright (C) 2010-2011 Texas Instruments Incorporated - https://www.ti.com/ */ #ifndef _HDMI4_CORE_H_ diff --git a/drivers/video/fbdev/omap2/omapfb/dss/hdmi5_core.h b/drivers/video/fbdev/omap2/omapfb/dss/hdmi5_core.h index f10b8a283011..192c9b6e2f7b 100644 --- a/drivers/video/fbdev/omap2/omapfb/dss/hdmi5_core.h +++ b/drivers/video/fbdev/omap2/omapfb/dss/hdmi5_core.h @@ -2,7 +2,7 @@ /* * HDMI driver definition for TI OMAP5 processors. * - * Copyright (C) 2011-2012 Texas Instruments Incorporated - http://www.ti.com/ + * Copyright (C) 2011-2012 Texas Instruments Incorporated - https://www.ti.com/ */ #ifndef _HDMI5_CORE_H_ diff --git a/drivers/video/fbdev/sa1100fb.c b/drivers/video/fbdev/sa1100fb.c index 3e6e13f7a831..bda6cc313c8b 100644 --- a/drivers/video/fbdev/sa1100fb.c +++ b/drivers/video/fbdev/sa1100fb.c @@ -18,7 +18,7 @@ * Clean patches should be sent to the ARM Linux Patch System. Please see the * following web page for more information: * - * http://www.arm.linux.org.uk/developer/patches/info.shtml + * https://www.arm.linux.org.uk/developer/patches/info.shtml * * Thank you. * From 23f166ca9a86e1e5a7d57fb002b9f79b2f70026d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20K=C3=B6nig?= Date: Thu, 16 Jul 2020 13:58:35 +0200 Subject: [PATCH 41/59] drm/vram-helper: stop using TTM_MEMTYPE_FLAG_MAPPABLE MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The helper doesn't expose any not-mapable memory resources. Signed-off-by: Christian König Reviewed-by: Thomas Zimmermann Link: https://patchwork.freedesktop.org/patch/377649/ --- drivers/gpu/drm/drm_gem_vram_helper.c | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/drivers/gpu/drm/drm_gem_vram_helper.c b/drivers/gpu/drm/drm_gem_vram_helper.c index c96d54f7c180..d107a2679e23 100644 --- a/drivers/gpu/drm/drm_gem_vram_helper.c +++ b/drivers/gpu/drm/drm_gem_vram_helper.c @@ -1009,14 +1009,13 @@ static int bo_driver_init_mem_type(struct ttm_bo_device *bdev, uint32_t type, { switch (type) { case TTM_PL_SYSTEM: - man->flags = TTM_MEMTYPE_FLAG_MAPPABLE; + man->flags = 0; man->available_caching = TTM_PL_MASK_CACHING; man->default_caching = TTM_PL_FLAG_CACHED; break; case TTM_PL_VRAM: man->func = &ttm_bo_manager_func; - man->flags = TTM_MEMTYPE_FLAG_FIXED | - TTM_MEMTYPE_FLAG_MAPPABLE; + man->flags = TTM_MEMTYPE_FLAG_FIXED; man->available_caching = TTM_PL_FLAG_UNCACHED | TTM_PL_FLAG_WC; man->default_caching = TTM_PL_FLAG_WC; @@ -1059,12 +1058,8 @@ static void bo_driver_move_notify(struct ttm_buffer_object *bo, static int bo_driver_io_mem_reserve(struct ttm_bo_device *bdev, struct ttm_mem_reg *mem) { - struct ttm_mem_type_manager *man = bdev->man + mem->mem_type; struct drm_vram_mm *vmm = drm_vram_mm_of_bdev(bdev); - if (!(man->flags & TTM_MEMTYPE_FLAG_MAPPABLE)) - return -EINVAL; - mem->bus.addr = NULL; mem->bus.size = mem->num_pages << PAGE_SHIFT; From 5fbff813a4a328b730cb117027c43a4ae9d8b6c0 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Tue, 7 Jul 2020 22:12:05 +0200 Subject: [PATCH 42/59] dma-fence: basic lockdep annotations MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Design is similar to the lockdep annotations for workers, but with some twists: - We use a read-lock for the execution/worker/completion side, so that this explicit annotation can be more liberally sprinkled around. With read locks lockdep isn't going to complain if the read-side isn't nested the same way under all circumstances, so ABBA deadlocks are ok. Which they are, since this is an annotation only. - We're using non-recursive lockdep read lock mode, since in recursive read lock mode lockdep does not catch read side hazards. And we _very_ much want read side hazards to be caught. For full details of this limitation see commit e91498589746065e3ae95d9a00b068e525eec34f Author: Peter Zijlstra Date: Wed Aug 23 13:13:11 2017 +0200 locking/lockdep/selftests: Add mixed read-write ABBA tests - To allow nesting of the read-side explicit annotations we explicitly keep track of the nesting. lock_is_held() allows us to do that. - The wait-side annotation is a write lock, and entirely done within dma_fence_wait() for everyone by default. - To be able to freely annotate helper functions I want to make it ok to call dma_fence_begin/end_signalling from soft/hardirq context. First attempt was using the hardirq locking context for the write side in lockdep, but this forces all normal spinlocks nested within dma_fence_begin/end_signalling to be spinlocks. That bollocks. The approach now is to simple check in_atomic(), and for these cases entirely rely on the might_sleep() check in dma_fence_wait(). That will catch any wrong nesting against spinlocks from soft/hardirq contexts. The idea here is that every code path that's critical for eventually signalling a dma_fence should be annotated with dma_fence_begin/end_signalling. The annotation ideally starts right after a dma_fence is published (added to a dma_resv, exposed as a sync_file fd, attached to a drm_syncobj fd, or anything else that makes the dma_fence visible to other kernel threads), up to and including the dma_fence_wait(). Examples are irq handlers, the scheduler rt threads, the tail of execbuf (after the corresponding fences are visible), any workers that end up signalling dma_fences and really anything else. Not annotated should be code paths that only complete fences opportunistically as the gpu progresses, like e.g. shrinker/eviction code. The main class of deadlocks this is supposed to catch are: Thread A: mutex_lock(A); mutex_unlock(A); dma_fence_signal(); Thread B: mutex_lock(A); dma_fence_wait(); mutex_unlock(A); Thread B is blocked on A signalling the fence, but A never gets around to that because it cannot acquire the lock A. Note that dma_fence_wait() is allowed to be nested within dma_fence_begin/end_signalling sections. To allow this to happen the read lock needs to be upgraded to a write lock, which means that any other lock is acquired between the dma_fence_begin_signalling() call and the call to dma_fence_wait(), and still held, this will result in an immediate lockdep complaint. The only other option would be to not annotate such calls, defeating the point. Therefore these annotations cannot be sprinkled over the code entirely mindless to avoid false positives. Originally I hope that the cross-release lockdep extensions would alleviate the need for explicit annotations: https://lwn.net/Articles/709849/ But there's a few reasons why that's not an option: - It's not happening in upstream, since it got reverted due to too many false positives: commit e966eaeeb623f09975ef362c2866fae6f86844f9 Author: Ingo Molnar Date: Tue Dec 12 12:31:16 2017 +0100 locking/lockdep: Remove the cross-release locking checks This code (CONFIG_LOCKDEP_CROSSRELEASE=y and CONFIG_LOCKDEP_COMPLETIONS=y), while it found a number of old bugs initially, was also causing too many false positives that caused people to disable lockdep - which is arguably a worse overall outcome. - cross-release uses the complete() call to annotate the end of critical sections, for dma_fence that would be dma_fence_signal(). But we do not want all dma_fence_signal() calls to be treated as critical, since many are opportunistic cleanup of gpu requests. If these get stuck there's still the main completion interrupt and workers who can unblock everyone. Automatically annotating all dma_fence_signal() calls would hence cause false positives. - cross-release had some educated guesses for when a critical section starts, like fresh syscall or fresh work callback. This would again cause false positives without explicit annotations, since for dma_fence the critical sections only starts when we publish a fence. - Furthermore there can be cases where a thread never does a dma_fence_signal, but is still critical for reaching completion of fences. One example would be a scheduler kthread which picks up jobs and pushes them into hardware, where the interrupt handler or another completion thread calls dma_fence_signal(). But if the scheduler thread hangs, then all the fences hang, hence we need to manually annotate it. cross-release aimed to solve this by chaining cross-release dependencies, but the dependency from scheduler thread to the completion interrupt handler goes through hw where cross-release code can't observe it. In short, without manual annotations and careful review of the start and end of critical sections, cross-relese dependency tracking doesn't work. We need explicit annotations. v2: handle soft/hardirq ctx better against write side and dont forget EXPORT_SYMBOL, drivers can't use this otherwise. v3: Kerneldoc. v4: Some spelling fixes from Mika v5: Amend commit message to explain in detail why cross-release isn't the solution. v6: Pull out misplaced .rst hunk. Acked-by: Christian König Acked-by: Dave Airlie Cc: Felix Kuehling Reviewed-by: Thomas Hellström Reviewed-by: Maarten Lankhorst Cc: Mika Kuoppala Cc: Thomas Hellstrom Cc: linux-media@vger.kernel.org Cc: linaro-mm-sig@lists.linaro.org Cc: linux-rdma@vger.kernel.org Cc: amd-gfx@lists.freedesktop.org Cc: intel-gfx@lists.freedesktop.org Cc: Chris Wilson Cc: Maarten Lankhorst Cc: Christian König Signed-off-by: Daniel Vetter Link: https://patchwork.freedesktop.org/patch/msgid/20200707201229.472834-2-daniel.vetter@ffwll.ch --- Documentation/driver-api/dma-buf.rst | 6 + drivers/dma-buf/dma-fence.c | 161 +++++++++++++++++++++++++++ include/linux/dma-fence.h | 12 ++ 3 files changed, 179 insertions(+) diff --git a/Documentation/driver-api/dma-buf.rst b/Documentation/driver-api/dma-buf.rst index 7fb7b661febd..05d856131140 100644 --- a/Documentation/driver-api/dma-buf.rst +++ b/Documentation/driver-api/dma-buf.rst @@ -133,6 +133,12 @@ DMA Fences .. kernel-doc:: drivers/dma-buf/dma-fence.c :doc: DMA fences overview +DMA Fence Signalling Annotations +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. kernel-doc:: drivers/dma-buf/dma-fence.c + :doc: fence signalling annotation + DMA Fences Functions Reference ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/drivers/dma-buf/dma-fence.c b/drivers/dma-buf/dma-fence.c index 656e9ac2d028..0005bc002529 100644 --- a/drivers/dma-buf/dma-fence.c +++ b/drivers/dma-buf/dma-fence.c @@ -110,6 +110,160 @@ u64 dma_fence_context_alloc(unsigned num) } EXPORT_SYMBOL(dma_fence_context_alloc); +/** + * DOC: fence signalling annotation + * + * Proving correctness of all the kernel code around &dma_fence through code + * review and testing is tricky for a few reasons: + * + * * It is a cross-driver contract, and therefore all drivers must follow the + * same rules for lock nesting order, calling contexts for various functions + * and anything else significant for in-kernel interfaces. But it is also + * impossible to test all drivers in a single machine, hence brute-force N vs. + * N testing of all combinations is impossible. Even just limiting to the + * possible combinations is infeasible. + * + * * There is an enormous amount of driver code involved. For render drivers + * there's the tail of command submission, after fences are published, + * scheduler code, interrupt and workers to process job completion, + * and timeout, gpu reset and gpu hang recovery code. Plus for integration + * with core mm with have &mmu_notifier, respectively &mmu_interval_notifier, + * and &shrinker. For modesetting drivers there's the commit tail functions + * between when fences for an atomic modeset are published, and when the + * corresponding vblank completes, including any interrupt processing and + * related workers. Auditing all that code, across all drivers, is not + * feasible. + * + * * Due to how many other subsystems are involved and the locking hierarchies + * this pulls in there is extremely thin wiggle-room for driver-specific + * differences. &dma_fence interacts with almost all of the core memory + * handling through page fault handlers via &dma_resv, dma_resv_lock() and + * dma_resv_unlock(). On the other side it also interacts through all + * allocation sites through &mmu_notifier and &shrinker. + * + * Furthermore lockdep does not handle cross-release dependencies, which means + * any deadlocks between dma_fence_wait() and dma_fence_signal() can't be caught + * at runtime with some quick testing. The simplest example is one thread + * waiting on a &dma_fence while holding a lock:: + * + * lock(A); + * dma_fence_wait(B); + * unlock(A); + * + * while the other thread is stuck trying to acquire the same lock, which + * prevents it from signalling the fence the previous thread is stuck waiting + * on:: + * + * lock(A); + * unlock(A); + * dma_fence_signal(B); + * + * By manually annotating all code relevant to signalling a &dma_fence we can + * teach lockdep about these dependencies, which also helps with the validation + * headache since now lockdep can check all the rules for us:: + * + * cookie = dma_fence_begin_signalling(); + * lock(A); + * unlock(A); + * dma_fence_signal(B); + * dma_fence_end_signalling(cookie); + * + * For using dma_fence_begin_signalling() and dma_fence_end_signalling() to + * annotate critical sections the following rules need to be observed: + * + * * All code necessary to complete a &dma_fence must be annotated, from the + * point where a fence is accessible to other threads, to the point where + * dma_fence_signal() is called. Un-annotated code can contain deadlock issues, + * and due to the very strict rules and many corner cases it is infeasible to + * catch these just with review or normal stress testing. + * + * * &struct dma_resv deserves a special note, since the readers are only + * protected by rcu. This means the signalling critical section starts as soon + * as the new fences are installed, even before dma_resv_unlock() is called. + * + * * The only exception are fast paths and opportunistic signalling code, which + * calls dma_fence_signal() purely as an optimization, but is not required to + * guarantee completion of a &dma_fence. The usual example is a wait IOCTL + * which calls dma_fence_signal(), while the mandatory completion path goes + * through a hardware interrupt and possible job completion worker. + * + * * To aid composability of code, the annotations can be freely nested, as long + * as the overall locking hierarchy is consistent. The annotations also work + * both in interrupt and process context. Due to implementation details this + * requires that callers pass an opaque cookie from + * dma_fence_begin_signalling() to dma_fence_end_signalling(). + * + * * Validation against the cross driver contract is implemented by priming + * lockdep with the relevant hierarchy at boot-up. This means even just + * testing with a single device is enough to validate a driver, at least as + * far as deadlocks with dma_fence_wait() against dma_fence_signal() are + * concerned. + */ +#ifdef CONFIG_LOCKDEP +struct lockdep_map dma_fence_lockdep_map = { + .name = "dma_fence_map" +}; + +/** + * dma_fence_begin_signalling - begin a critical DMA fence signalling section + * + * Drivers should use this to annotate the beginning of any code section + * required to eventually complete &dma_fence by calling dma_fence_signal(). + * + * The end of these critical sections are annotated with + * dma_fence_end_signalling(). + * + * Returns: + * + * Opaque cookie needed by the implementation, which needs to be passed to + * dma_fence_end_signalling(). + */ +bool dma_fence_begin_signalling(void) +{ + /* explicitly nesting ... */ + if (lock_is_held_type(&dma_fence_lockdep_map, 1)) + return true; + + /* rely on might_sleep check for soft/hardirq locks */ + if (in_atomic()) + return true; + + /* ... and non-recursive readlock */ + lock_acquire(&dma_fence_lockdep_map, 0, 0, 1, 1, NULL, _RET_IP_); + + return false; +} +EXPORT_SYMBOL(dma_fence_begin_signalling); + +/** + * dma_fence_end_signalling - end a critical DMA fence signalling section + * + * Closes a critical section annotation opened by dma_fence_begin_signalling(). + */ +void dma_fence_end_signalling(bool cookie) +{ + if (cookie) + return; + + lock_release(&dma_fence_lockdep_map, _RET_IP_); +} +EXPORT_SYMBOL(dma_fence_end_signalling); + +void __dma_fence_might_wait(void) +{ + bool tmp; + + tmp = lock_is_held_type(&dma_fence_lockdep_map, 1); + if (tmp) + lock_release(&dma_fence_lockdep_map, _THIS_IP_); + lock_map_acquire(&dma_fence_lockdep_map); + lock_map_release(&dma_fence_lockdep_map); + if (tmp) + lock_acquire(&dma_fence_lockdep_map, 0, 0, 1, 1, NULL, _THIS_IP_); +} +#endif + + /** * dma_fence_signal_locked - signal completion of a fence * @fence: the fence to signal @@ -170,14 +324,19 @@ int dma_fence_signal(struct dma_fence *fence) { unsigned long flags; int ret; + bool tmp; if (!fence) return -EINVAL; + tmp = dma_fence_begin_signalling(); + spin_lock_irqsave(fence->lock, flags); ret = dma_fence_signal_locked(fence); spin_unlock_irqrestore(fence->lock, flags); + dma_fence_end_signalling(tmp); + return ret; } EXPORT_SYMBOL(dma_fence_signal); @@ -210,6 +369,8 @@ dma_fence_wait_timeout(struct dma_fence *fence, bool intr, signed long timeout) might_sleep(); + __dma_fence_might_wait(); + trace_dma_fence_wait_start(fence); if (fence->ops->wait) ret = fence->ops->wait(fence, intr, timeout); diff --git a/include/linux/dma-fence.h b/include/linux/dma-fence.h index 3347c54f3a87..3f288f7db2ef 100644 --- a/include/linux/dma-fence.h +++ b/include/linux/dma-fence.h @@ -357,6 +357,18 @@ dma_fence_get_rcu_safe(struct dma_fence __rcu **fencep) } while (1); } +#ifdef CONFIG_LOCKDEP +bool dma_fence_begin_signalling(void); +void dma_fence_end_signalling(bool cookie); +#else +static inline bool dma_fence_begin_signalling(void) +{ + return true; +} +static inline void dma_fence_end_signalling(bool cookie) {} +static inline void __dma_fence_might_wait(void) {} +#endif + int dma_fence_signal(struct dma_fence *fence); int dma_fence_signal_locked(struct dma_fence *fence); signed long dma_fence_default_wait(struct dma_fence *fence, From d0b9a9aef0a18e7ba473d0887e7ebd107ab84fe4 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Tue, 7 Jul 2020 22:12:06 +0200 Subject: [PATCH 43/59] dma-fence: prime lockdep annotations MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two in one go: - it is allowed to call dma_fence_wait() while holding a dma_resv_lock(). This is fundamental to how eviction works with ttm, so required. - it is allowed to call dma_fence_wait() from memory reclaim contexts, specifically from shrinker callbacks (which i915 does), and from mmu notifier callbacks (which amdgpu does, and which i915 sometimes also does, and probably always should, but that's kinda a debate). Also for stuff like HMM we really need to be able to do this, or things get real dicey. Consequence is that any critical path necessary to get to a dma_fence_signal for a fence must never a) call dma_resv_lock nor b) allocate memory with GFP_KERNEL. Also by implication of dma_resv_lock(), no userspace faulting allowed. That's some supremely obnoxious limitations, which is why we need to sprinkle the right annotations to all relevant paths. The one big locking context we're leaving out here is mmu notifiers, added in commit 23b68395c7c78a764e8963fc15a7cfd318bf187f Author: Daniel Vetter Date: Mon Aug 26 22:14:21 2019 +0200 mm/mmu_notifiers: add a lockdep map for invalidate_range_start/end that one covers a lot of other callsites, and it's also allowed to wait on dma-fences from mmu notifiers. But there's no ready-made functions exposed to prime this, so I've left it out for now. v2: Also track against mmu notifier context. v3: kerneldoc to spec the cross-driver contract. Note that currently i915 throws in a hard-coded 10s timeout on foreign fences (not sure why that was done, but it's there), which is why that rule is worded with SHOULD instead of MUST. Also some of the mmu_notifier/shrinker rules might surprise SoC drivers, I haven't fully audited them all. Which is infeasible anyway, we'll need to run them with lockdep and dma-fence annotations and see what goes boom. v4: A spelling fix from Mika v5: #ifdef for CONFIG_MMU_NOTIFIER. Reported by 0day. Unfortunately this means lockdep enforcement is slightly inconsistent, it won't spot GFP_NOIO and GFP_NOFS allocations in the wrong spot if CONFIG_MMU_NOTIFIER is disabled in the kernel config. Oh well. v5: Note that only drivers/gpu has a reasonable (or at least historical) excuse to use dma_fence_wait() from shrinker and mmu notifier callbacks. Everyone else should either have a better memory manager model, or better hardware. This reflects discussions with Jason Gunthorpe. Cc: Jason Gunthorpe Cc: Felix Kuehling Cc: kernel test robot Acked-by: Christian König Acked-by: Dave Airlie Reviewed-by: Maarten Lankhorst Reviewed-by: Thomas Hellström (v4) Cc: Mika Kuoppala Cc: Thomas Hellstrom Cc: linux-media@vger.kernel.org Cc: linaro-mm-sig@lists.linaro.org Cc: linux-rdma@vger.kernel.org Cc: amd-gfx@lists.freedesktop.org Cc: intel-gfx@lists.freedesktop.org Cc: Chris Wilson Cc: Maarten Lankhorst Cc: Christian König Signed-off-by: Daniel Vetter Link: https://patchwork.freedesktop.org/patch/msgid/20200707201229.472834-3-daniel.vetter@ffwll.ch --- Documentation/driver-api/dma-buf.rst | 6 ++++ drivers/dma-buf/dma-fence.c | 46 ++++++++++++++++++++++++++++ drivers/dma-buf/dma-resv.c | 8 +++++ include/linux/dma-fence.h | 1 + 4 files changed, 61 insertions(+) diff --git a/Documentation/driver-api/dma-buf.rst b/Documentation/driver-api/dma-buf.rst index 05d856131140..f8f6decde359 100644 --- a/Documentation/driver-api/dma-buf.rst +++ b/Documentation/driver-api/dma-buf.rst @@ -133,6 +133,12 @@ DMA Fences .. kernel-doc:: drivers/dma-buf/dma-fence.c :doc: DMA fences overview +DMA Fence Cross-Driver Contract +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. kernel-doc:: drivers/dma-buf/dma-fence.c + :doc: fence cross-driver contract + DMA Fence Signalling Annotations ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/drivers/dma-buf/dma-fence.c b/drivers/dma-buf/dma-fence.c index 0005bc002529..af1d8ea926b3 100644 --- a/drivers/dma-buf/dma-fence.c +++ b/drivers/dma-buf/dma-fence.c @@ -64,6 +64,52 @@ static atomic64_t dma_fence_context_counter = ATOMIC64_INIT(1); * &dma_buf.resv pointer. */ +/** + * DOC: fence cross-driver contract + * + * Since &dma_fence provide a cross driver contract, all drivers must follow the + * same rules: + * + * * Fences must complete in a reasonable time. Fences which represent kernels + * and shaders submitted by userspace, which could run forever, must be backed + * up by timeout and gpu hang recovery code. Minimally that code must prevent + * further command submission and force complete all in-flight fences, e.g. + * when the driver or hardware do not support gpu reset, or if the gpu reset + * failed for some reason. Ideally the driver supports gpu recovery which only + * affects the offending userspace context, and no other userspace + * submissions. + * + * * Drivers may have different ideas of what completion within a reasonable + * time means. Some hang recovery code uses a fixed timeout, others a mix + * between observing forward progress and increasingly strict timeouts. + * Drivers should not try to second guess timeout handling of fences from + * other drivers. + * + * * To ensure there's no deadlocks of dma_fence_wait() against other locks + * drivers should annotate all code required to reach dma_fence_signal(), + * which completes the fences, with dma_fence_begin_signalling() and + * dma_fence_end_signalling(). + * + * * Drivers are allowed to call dma_fence_wait() while holding dma_resv_lock(). + * This means any code required for fence completion cannot acquire a + * &dma_resv lock. Note that this also pulls in the entire established + * locking hierarchy around dma_resv_lock() and dma_resv_unlock(). + * + * * Drivers are allowed to call dma_fence_wait() from their &shrinker + * callbacks. This means any code required for fence completion cannot + * allocate memory with GFP_KERNEL. + * + * * Drivers are allowed to call dma_fence_wait() from their &mmu_notifier + * respectively &mmu_interval_notifier callbacks. This means any code required + * for fence completeion cannot allocate memory with GFP_NOFS or GFP_NOIO. + * Only GFP_ATOMIC is permissible, which might fail. + * + * Note that only GPU drivers have a reasonable excuse for both requiring + * &mmu_interval_notifier and &shrinker callbacks at the same time as having to + * track asynchronous compute work using &dma_fence. No driver outside of + * drivers/gpu should ever call dma_fence_wait() in such contexts. + */ + static const char *dma_fence_stub_get_name(struct dma_fence *fence) { return "stub"; diff --git a/drivers/dma-buf/dma-resv.c b/drivers/dma-buf/dma-resv.c index b45f8514dc82..07f5273207e7 100644 --- a/drivers/dma-buf/dma-resv.c +++ b/drivers/dma-buf/dma-resv.c @@ -36,6 +36,7 @@ #include #include #include +#include /** * DOC: Reservation Object Overview @@ -116,6 +117,13 @@ static int __init dma_resv_lockdep(void) if (ret == -EDEADLK) dma_resv_lock_slow(&obj, &ctx); fs_reclaim_acquire(GFP_KERNEL); +#ifdef CONFIG_MMU_NOTIFIER + lock_map_acquire(&__mmu_notifier_invalidate_range_start_map); + __dma_fence_might_wait(); + lock_map_release(&__mmu_notifier_invalidate_range_start_map); +#else + __dma_fence_might_wait(); +#endif fs_reclaim_release(GFP_KERNEL); ww_mutex_unlock(&obj.lock); ww_acquire_fini(&ctx); diff --git a/include/linux/dma-fence.h b/include/linux/dma-fence.h index 3f288f7db2ef..09e23adb351d 100644 --- a/include/linux/dma-fence.h +++ b/include/linux/dma-fence.h @@ -360,6 +360,7 @@ dma_fence_get_rcu_safe(struct dma_fence __rcu **fencep) #ifdef CONFIG_LOCKDEP bool dma_fence_begin_signalling(void); void dma_fence_end_signalling(bool cookie); +void __dma_fence_might_wait(void); #else static inline bool dma_fence_begin_signalling(void) { From 72b6ede7362327c0cff8e159fbb8c8fd9611e26e Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Thu, 9 Jul 2020 14:33:38 +0200 Subject: [PATCH 44/59] dma-buf.rst: Document why indefinite fences are a bad idea MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Comes up every few years, gets somewhat tedious to discuss, let's write this down once and for all. What I'm not sure about is whether the text should be more explicit in flat out mandating the amdkfd eviction fences for long running compute workloads or workloads where userspace fencing is allowed. v2: Now with dot graph! v3: Typo (Dave Airlie) Reviewed-by: Thomas Hellstrom Acked-by: Jason Ekstrand Acked-by: Christian König Acked-by: Daniel Stone Acked-by: Dave Airlie Reviewed-by: Maarten Lankhorst Cc: Jesse Natalie Cc: Steve Pronovost Cc: Jason Ekstrand Cc: Felix Kuehling Cc: Mika Kuoppala Cc: Thomas Hellstrom Cc: linux-media@vger.kernel.org Cc: linaro-mm-sig@lists.linaro.org Cc: linux-rdma@vger.kernel.org Cc: amd-gfx@lists.freedesktop.org Cc: intel-gfx@lists.freedesktop.org Cc: Chris Wilson Cc: Maarten Lankhorst Cc: Christian König Signed-off-by: Daniel Vetter Link: https://patchwork.freedesktop.org/patch/msgid/20200709123339.547390-1-daniel.vetter@ffwll.ch --- Documentation/driver-api/dma-buf.rst | 70 ++++++++++++++++++++++++++++ 1 file changed, 70 insertions(+) diff --git a/Documentation/driver-api/dma-buf.rst b/Documentation/driver-api/dma-buf.rst index f8f6decde359..100bfd227265 100644 --- a/Documentation/driver-api/dma-buf.rst +++ b/Documentation/driver-api/dma-buf.rst @@ -178,3 +178,73 @@ DMA Fence uABI/Sync File .. kernel-doc:: include/linux/sync_file.h :internal: +Indefinite DMA Fences +~~~~~~~~~~~~~~~~~~~~ + +At various times &dma_fence with an indefinite time until dma_fence_wait() +finishes have been proposed. Examples include: + +* Future fences, used in HWC1 to signal when a buffer isn't used by the display + any longer, and created with the screen update that makes the buffer visible. + The time this fence completes is entirely under userspace's control. + +* Proxy fences, proposed to handle &drm_syncobj for which the fence has not yet + been set. Used to asynchronously delay command submission. + +* Userspace fences or gpu futexes, fine-grained locking within a command buffer + that userspace uses for synchronization across engines or with the CPU, which + are then imported as a DMA fence for integration into existing winsys + protocols. + +* Long-running compute command buffers, while still using traditional end of + batch DMA fences for memory management instead of context preemption DMA + fences which get reattached when the compute job is rescheduled. + +Common to all these schemes is that userspace controls the dependencies of these +fences and controls when they fire. Mixing indefinite fences with normal +in-kernel DMA fences does not work, even when a fallback timeout is included to +protect against malicious userspace: + +* Only the kernel knows about all DMA fence dependencies, userspace is not aware + of dependencies injected due to memory management or scheduler decisions. + +* Only userspace knows about all dependencies in indefinite fences and when + exactly they will complete, the kernel has no visibility. + +Furthermore the kernel has to be able to hold up userspace command submission +for memory management needs, which means we must support indefinite fences being +dependent upon DMA fences. If the kernel also support indefinite fences in the +kernel like a DMA fence, like any of the above proposal would, there is the +potential for deadlocks. + +.. kernel-render:: DOT + :alt: Indefinite Fencing Dependency Cycle + :caption: Indefinite Fencing Dependency Cycle + + digraph "Fencing Cycle" { + node [shape=box bgcolor=grey style=filled] + kernel [label="Kernel DMA Fences"] + userspace [label="userspace controlled fences"] + kernel -> userspace [label="memory management"] + userspace -> kernel [label="Future fence, fence proxy, ..."] + + { rank=same; kernel userspace } + } + +This means that the kernel might accidentally create deadlocks +through memory management dependencies which userspace is unaware of, which +randomly hangs workloads until the timeout kicks in. Workloads, which from +userspace's perspective, do not contain a deadlock. In such a mixed fencing +architecture there is no single entity with knowledge of all dependencies. +Thefore preventing such deadlocks from within the kernel is not possible. + +The only solution to avoid dependencies loops is by not allowing indefinite +fences in the kernel. This means: + +* No future fences, proxy fences or userspace fences imported as DMA fences, + with or without a timeout. + +* No DMA fences that signal end of batchbuffer for command submission where + userspace is allowed to use userspace fencing or long running compute + workloads. This also means no implicit fencing for shared buffers in these + cases. From e69acf18d08d7b5e9416fa6afb3a8dd6dd3439ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20K=C3=B6nig?= Date: Fri, 10 Jul 2020 15:13:41 +0200 Subject: [PATCH 45/59] drm: remove optional dummy function from drivers using TTM MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Implementing those is completely unnecessary. Signed-off-by: Christian König Reviewed-by: Daniel Vetter Reviewed-by: Madhav Chauhan Link: https://patchwork.freedesktop.org/patch/378236/ --- drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c | 5 ----- drivers/gpu/drm/drm_gem_vram_helper.c | 5 ----- drivers/gpu/drm/qxl/qxl_ttm.c | 6 ------ drivers/gpu/drm/radeon/radeon_ttm.c | 5 ----- drivers/gpu/drm/vmwgfx/vmwgfx_ttm_buffer.c | 11 ----------- 5 files changed, 32 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c index 3df685287cc1..9c0f12f74af9 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c @@ -836,10 +836,6 @@ static int amdgpu_ttm_io_mem_reserve(struct ttm_bo_device *bdev, struct ttm_mem_ return 0; } -static void amdgpu_ttm_io_mem_free(struct ttm_bo_device *bdev, struct ttm_mem_reg *mem) -{ -} - static unsigned long amdgpu_ttm_io_mem_pfn(struct ttm_buffer_object *bo, unsigned long page_offset) { @@ -1754,7 +1750,6 @@ static struct ttm_bo_driver amdgpu_bo_driver = { .release_notify = &amdgpu_bo_release_notify, .fault_reserve_notify = &amdgpu_bo_fault_reserve_notify, .io_mem_reserve = &amdgpu_ttm_io_mem_reserve, - .io_mem_free = &amdgpu_ttm_io_mem_free, .io_mem_pfn = amdgpu_ttm_io_mem_pfn, .access_memory = &amdgpu_ttm_access_memory, .del_from_lru_notify = &amdgpu_vm_del_from_lru_notify diff --git a/drivers/gpu/drm/drm_gem_vram_helper.c b/drivers/gpu/drm/drm_gem_vram_helper.c index d107a2679e23..3296ed3df358 100644 --- a/drivers/gpu/drm/drm_gem_vram_helper.c +++ b/drivers/gpu/drm/drm_gem_vram_helper.c @@ -1081,10 +1081,6 @@ static int bo_driver_io_mem_reserve(struct ttm_bo_device *bdev, return 0; } -static void bo_driver_io_mem_free(struct ttm_bo_device *bdev, - struct ttm_mem_reg *mem) -{ } - static struct ttm_bo_driver bo_driver = { .ttm_tt_create = bo_driver_ttm_tt_create, .ttm_tt_populate = ttm_pool_populate, @@ -1094,7 +1090,6 @@ static struct ttm_bo_driver bo_driver = { .evict_flags = bo_driver_evict_flags, .move_notify = bo_driver_move_notify, .io_mem_reserve = bo_driver_io_mem_reserve, - .io_mem_free = bo_driver_io_mem_free, }; /* diff --git a/drivers/gpu/drm/qxl/qxl_ttm.c b/drivers/gpu/drm/qxl/qxl_ttm.c index 52eaa2d22745..a6e67149ef4a 100644 --- a/drivers/gpu/drm/qxl/qxl_ttm.c +++ b/drivers/gpu/drm/qxl/qxl_ttm.c @@ -129,11 +129,6 @@ int qxl_ttm_io_mem_reserve(struct ttm_bo_device *bdev, return 0; } -static void qxl_ttm_io_mem_free(struct ttm_bo_device *bdev, - struct ttm_mem_reg *mem) -{ -} - /* * TTM backend functions. */ @@ -247,7 +242,6 @@ static struct ttm_bo_driver qxl_bo_driver = { .evict_flags = &qxl_evict_flags, .move = &qxl_bo_move, .io_mem_reserve = &qxl_ttm_io_mem_reserve, - .io_mem_free = &qxl_ttm_io_mem_free, .move_notify = &qxl_bo_move_notify, }; diff --git a/drivers/gpu/drm/radeon/radeon_ttm.c b/drivers/gpu/drm/radeon/radeon_ttm.c index f4f1e63731a5..73085523fad7 100644 --- a/drivers/gpu/drm/radeon/radeon_ttm.c +++ b/drivers/gpu/drm/radeon/radeon_ttm.c @@ -457,10 +457,6 @@ static int radeon_ttm_io_mem_reserve(struct ttm_bo_device *bdev, struct ttm_mem_ return 0; } -static void radeon_ttm_io_mem_free(struct ttm_bo_device *bdev, struct ttm_mem_reg *mem) -{ -} - /* * TTM backend functions. */ @@ -774,7 +770,6 @@ static struct ttm_bo_driver radeon_bo_driver = { .move_notify = &radeon_bo_move_notify, .fault_reserve_notify = &radeon_bo_fault_reserve_notify, .io_mem_reserve = &radeon_ttm_io_mem_reserve, - .io_mem_free = &radeon_ttm_io_mem_free, }; int radeon_ttm_init(struct radeon_device *rdev) diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_ttm_buffer.c b/drivers/gpu/drm/vmwgfx/vmwgfx_ttm_buffer.c index fbcd11a7b215..bfd0c54ec30a 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_ttm_buffer.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_ttm_buffer.c @@ -815,15 +815,6 @@ static int vmw_ttm_io_mem_reserve(struct ttm_bo_device *bdev, struct ttm_mem_reg return 0; } -static void vmw_ttm_io_mem_free(struct ttm_bo_device *bdev, struct ttm_mem_reg *mem) -{ -} - -static int vmw_ttm_fault_reserve_notify(struct ttm_buffer_object *bo) -{ - return 0; -} - /** * vmw_move_notify - TTM move_notify_callback * @@ -866,7 +857,5 @@ struct ttm_bo_driver vmw_bo_driver = { .verify_access = vmw_verify_access, .move_notify = vmw_move_notify, .swap_notify = vmw_swap_notify, - .fault_reserve_notify = &vmw_ttm_fault_reserve_notify, .io_mem_reserve = &vmw_ttm_io_mem_reserve, - .io_mem_free = &vmw_ttm_io_mem_free, }; From 4b8edc39a45f51eb304acb081020ec4f063d53a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20K=C3=B6nig?= Date: Mon, 13 Jul 2020 16:50:04 +0200 Subject: [PATCH 46/59] drm/ttm: cleanup io_mem interface with nouveau MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Nouveau is the only user of this functionality and evicting io space on -EAGAIN is really a misuse of the return code. Instead switch to using -ENOSPC here which makes much more sense and simplifies the code. This could unbreak something as we now cleanly return EAGAIN, but the chance for this are rather low. Signed-off-by: Christian König Reviewed-by: Daniel Vetter Link: https://patchwork.freedesktop.org/patch/378237/ --- drivers/gpu/drm/nouveau/nouveau_bo.c | 2 -- drivers/gpu/drm/ttm/ttm_bo_util.c | 4 ++-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/nouveau/nouveau_bo.c b/drivers/gpu/drm/nouveau/nouveau_bo.c index 61355cfb7335..a48652826f67 100644 --- a/drivers/gpu/drm/nouveau/nouveau_bo.c +++ b/drivers/gpu/drm/nouveau/nouveau_bo.c @@ -1505,8 +1505,6 @@ nouveau_ttm_io_mem_reserve(struct ttm_bo_device *bdev, struct ttm_mem_reg *reg) if (ret != 1) { if (WARN_ON(ret == 0)) return -EINVAL; - if (ret == -ENOSPC) - return -EAGAIN; return ret; } diff --git a/drivers/gpu/drm/ttm/ttm_bo_util.c b/drivers/gpu/drm/ttm/ttm_bo_util.c index 5e0f3a9caedc..7d2c50fef456 100644 --- a/drivers/gpu/drm/ttm/ttm_bo_util.c +++ b/drivers/gpu/drm/ttm/ttm_bo_util.c @@ -116,7 +116,7 @@ static int ttm_mem_io_evict(struct ttm_mem_type_manager *man) struct ttm_buffer_object *bo; if (!man->use_io_reserve_lru || list_empty(&man->io_reserve_lru)) - return -EAGAIN; + return -ENOSPC; bo = list_first_entry(&man->io_reserve_lru, struct ttm_buffer_object, @@ -143,7 +143,7 @@ int ttm_mem_io_reserve(struct ttm_bo_device *bdev, mem->bus.io_reserved_count++ == 0) { retry: ret = bdev->driver->io_mem_reserve(bdev, mem); - if (ret == -EAGAIN) { + if (ret == -ENOSPC) { ret = ttm_mem_io_evict(man); if (ret == 0) goto retry; From ce74773305bd5e519745e7c406f7f006daef9902 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20K=C3=B6nig?= Date: Wed, 15 Jul 2020 13:05:52 +0200 Subject: [PATCH 47/59] drm/ttm: remove io_reserve_fastpath flag MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Just use the use_io_reserve_lru flag. It doesn't make much sense to have two flags. Signed-off-by: Christian König Reviewed-by: Daniel Vetter Link: https://patchwork.freedesktop.org/patch/378238/ --- drivers/gpu/drm/nouveau/nouveau_bo.c | 1 - drivers/gpu/drm/ttm/ttm_bo.c | 1 - drivers/gpu/drm/ttm/ttm_bo_util.c | 8 ++++---- include/drm/ttm/ttm_bo_driver.h | 2 -- 4 files changed, 4 insertions(+), 8 deletions(-) diff --git a/drivers/gpu/drm/nouveau/nouveau_bo.c b/drivers/gpu/drm/nouveau/nouveau_bo.c index a48652826f67..a1037478fa3f 100644 --- a/drivers/gpu/drm/nouveau/nouveau_bo.c +++ b/drivers/gpu/drm/nouveau/nouveau_bo.c @@ -675,7 +675,6 @@ nouveau_bo_init_mem_type(struct ttm_bo_device *bdev, uint32_t type, } man->func = &nouveau_vram_manager; - man->io_reserve_fastpath = false; man->use_io_reserve_lru = true; } else { man->func = &ttm_bo_manager_func; diff --git a/drivers/gpu/drm/ttm/ttm_bo.c b/drivers/gpu/drm/ttm/ttm_bo.c index 7be36b9996ed..8b9e7f62bea7 100644 --- a/drivers/gpu/drm/ttm/ttm_bo.c +++ b/drivers/gpu/drm/ttm/ttm_bo.c @@ -1521,7 +1521,6 @@ int ttm_bo_init_mm(struct ttm_bo_device *bdev, unsigned type, BUG_ON(type >= TTM_NUM_MEM_TYPES); man = &bdev->man[type]; BUG_ON(man->has_type); - man->io_reserve_fastpath = true; man->use_io_reserve_lru = false; mutex_init(&man->io_reserve_mutex); spin_lock_init(&man->move_lock); diff --git a/drivers/gpu/drm/ttm/ttm_bo_util.c b/drivers/gpu/drm/ttm/ttm_bo_util.c index 7d2c50fef456..6c05f4fd15ae 100644 --- a/drivers/gpu/drm/ttm/ttm_bo_util.c +++ b/drivers/gpu/drm/ttm/ttm_bo_util.c @@ -93,7 +93,7 @@ EXPORT_SYMBOL(ttm_bo_move_ttm); int ttm_mem_io_lock(struct ttm_mem_type_manager *man, bool interruptible) { - if (likely(man->io_reserve_fastpath)) + if (likely(!man->use_io_reserve_lru)) return 0; if (interruptible) @@ -105,7 +105,7 @@ int ttm_mem_io_lock(struct ttm_mem_type_manager *man, bool interruptible) void ttm_mem_io_unlock(struct ttm_mem_type_manager *man) { - if (likely(man->io_reserve_fastpath)) + if (likely(!man->use_io_reserve_lru)) return; mutex_unlock(&man->io_reserve_mutex); @@ -136,7 +136,7 @@ int ttm_mem_io_reserve(struct ttm_bo_device *bdev, if (!bdev->driver->io_mem_reserve) return 0; - if (likely(man->io_reserve_fastpath)) + if (likely(!man->use_io_reserve_lru)) return bdev->driver->io_mem_reserve(bdev, mem); if (bdev->driver->io_mem_reserve && @@ -157,7 +157,7 @@ void ttm_mem_io_free(struct ttm_bo_device *bdev, { struct ttm_mem_type_manager *man = &bdev->man[mem->mem_type]; - if (likely(man->io_reserve_fastpath)) + if (likely(!man->use_io_reserve_lru)) return; if (bdev->driver->io_mem_reserve && diff --git a/include/drm/ttm/ttm_bo_driver.h b/include/drm/ttm/ttm_bo_driver.h index 732167cad130..45522e4fbd6b 100644 --- a/include/drm/ttm/ttm_bo_driver.h +++ b/include/drm/ttm/ttm_bo_driver.h @@ -155,7 +155,6 @@ struct ttm_mem_type_manager_func { * @use_io_reserve_lru: Use an lru list to try to unreserve io_mem_regions * reserved by the TTM vm system. * @io_reserve_lru: Optional lru list for unreserving io mem regions. - * @io_reserve_fastpath: Only use bdev::driver::io_mem_reserve to obtain * @move_lock: lock for move fence * static information. bdev::driver::io_mem_free is never used. * @lru: The lru list for this memory type. @@ -184,7 +183,6 @@ struct ttm_mem_type_manager { void *priv; struct mutex io_reserve_mutex; bool use_io_reserve_lru; - bool io_reserve_fastpath; spinlock_t move_lock; /* From c1c440d41aff2fa22027ed51afcc4c69709515eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20K=C3=B6nig?= Date: Wed, 15 Jul 2020 13:22:56 +0200 Subject: [PATCH 48/59] drm/ttm: cleanup coding style and implementation. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Only functional change is to always keep io_reserved_count up to date for debugging even when it is not used otherwise. Signed-off-by: Christian König Reviewed-by: Daniel Vetter Link: https://patchwork.freedesktop.org/patch/378242/ --- drivers/gpu/drm/ttm/ttm_bo_util.c | 97 +++++++++++++++---------------- 1 file changed, 48 insertions(+), 49 deletions(-) diff --git a/drivers/gpu/drm/ttm/ttm_bo_util.c b/drivers/gpu/drm/ttm/ttm_bo_util.c index 6c05f4fd15ae..7fb3e0bcbab4 100644 --- a/drivers/gpu/drm/ttm/ttm_bo_util.c +++ b/drivers/gpu/drm/ttm/ttm_bo_util.c @@ -115,39 +115,35 @@ static int ttm_mem_io_evict(struct ttm_mem_type_manager *man) { struct ttm_buffer_object *bo; - if (!man->use_io_reserve_lru || list_empty(&man->io_reserve_lru)) + bo = list_first_entry_or_null(&man->io_reserve_lru, + struct ttm_buffer_object, + io_reserve_lru); + if (!bo) return -ENOSPC; - bo = list_first_entry(&man->io_reserve_lru, - struct ttm_buffer_object, - io_reserve_lru); list_del_init(&bo->io_reserve_lru); ttm_bo_unmap_virtual_locked(bo); - return 0; } - int ttm_mem_io_reserve(struct ttm_bo_device *bdev, struct ttm_mem_reg *mem) { struct ttm_mem_type_manager *man = &bdev->man[mem->mem_type]; - int ret = 0; + int ret; + + if (mem->bus.io_reserved_count++) + return 0; if (!bdev->driver->io_mem_reserve) return 0; - if (likely(!man->use_io_reserve_lru)) - return bdev->driver->io_mem_reserve(bdev, mem); - if (bdev->driver->io_mem_reserve && - mem->bus.io_reserved_count++ == 0) { retry: - ret = bdev->driver->io_mem_reserve(bdev, mem); - if (ret == -ENOSPC) { - ret = ttm_mem_io_evict(man); - if (ret == 0) - goto retry; - } + ret = bdev->driver->io_mem_reserve(bdev, mem); + if (ret == -ENOSPC) { + ret = ttm_mem_io_evict(man); + if (ret == 0) + goto retry; } return ret; } @@ -155,35 +151,31 @@ retry: void ttm_mem_io_free(struct ttm_bo_device *bdev, struct ttm_mem_reg *mem) { - struct ttm_mem_type_manager *man = &bdev->man[mem->mem_type]; - - if (likely(!man->use_io_reserve_lru)) + if (--mem->bus.io_reserved_count) return; - if (bdev->driver->io_mem_reserve && - --mem->bus.io_reserved_count == 0 && - bdev->driver->io_mem_free) - bdev->driver->io_mem_free(bdev, mem); + if (!bdev->driver->io_mem_free) + return; + bdev->driver->io_mem_free(bdev, mem); } int ttm_mem_io_reserve_vm(struct ttm_buffer_object *bo) { + struct ttm_mem_type_manager *man = &bo->bdev->man[bo->mem.mem_type]; struct ttm_mem_reg *mem = &bo->mem; int ret; - if (!mem->bus.io_reserved_vm) { - struct ttm_mem_type_manager *man = - &bo->bdev->man[mem->mem_type]; + if (mem->bus.io_reserved_vm) + return 0; - ret = ttm_mem_io_reserve(bo->bdev, mem); - if (unlikely(ret != 0)) - return ret; - mem->bus.io_reserved_vm = true; - if (man->use_io_reserve_lru) - list_add_tail(&bo->io_reserve_lru, - &man->io_reserve_lru); - } + ret = ttm_mem_io_reserve(bo->bdev, mem); + if (unlikely(ret != 0)) + return ret; + mem->bus.io_reserved_vm = true; + if (man->use_io_reserve_lru) + list_add_tail(&bo->io_reserve_lru, + &man->io_reserve_lru); return 0; } @@ -191,15 +183,17 @@ void ttm_mem_io_free_vm(struct ttm_buffer_object *bo) { struct ttm_mem_reg *mem = &bo->mem; - if (mem->bus.io_reserved_vm) { - mem->bus.io_reserved_vm = false; - list_del_init(&bo->io_reserve_lru); - ttm_mem_io_free(bo->bdev, mem); - } + if (!mem->bus.io_reserved_vm) + return; + + mem->bus.io_reserved_vm = false; + list_del_init(&bo->io_reserve_lru); + ttm_mem_io_free(bo->bdev, mem); } -static int ttm_mem_reg_ioremap(struct ttm_bo_device *bdev, struct ttm_mem_reg *mem, - void **virtual) +static int ttm_mem_reg_ioremap(struct ttm_bo_device *bdev, + struct ttm_mem_reg *mem, + void **virtual) { struct ttm_mem_type_manager *man = &bdev->man[mem->mem_type]; int ret; @@ -216,9 +210,11 @@ static int ttm_mem_reg_ioremap(struct ttm_bo_device *bdev, struct ttm_mem_reg *m addr = mem->bus.addr; } else { if (mem->placement & TTM_PL_FLAG_WC) - addr = ioremap_wc(mem->bus.base + mem->bus.offset, mem->bus.size); + addr = ioremap_wc(mem->bus.base + mem->bus.offset, + mem->bus.size); else - addr = ioremap(mem->bus.base + mem->bus.offset, mem->bus.size); + addr = ioremap(mem->bus.base + mem->bus.offset, + mem->bus.size); if (!addr) { (void) ttm_mem_io_lock(man, false); ttm_mem_io_free(bdev, mem); @@ -230,8 +226,9 @@ static int ttm_mem_reg_ioremap(struct ttm_bo_device *bdev, struct ttm_mem_reg *m return 0; } -static void ttm_mem_reg_iounmap(struct ttm_bo_device *bdev, struct ttm_mem_reg *mem, - void *virtual) +static void ttm_mem_reg_iounmap(struct ttm_bo_device *bdev, + struct ttm_mem_reg *mem, + void *virtual) { struct ttm_mem_type_manager *man; @@ -513,11 +510,13 @@ static int ttm_bo_ioremap(struct ttm_buffer_object *bo, } else { map->bo_kmap_type = ttm_bo_map_iomap; if (mem->placement & TTM_PL_FLAG_WC) - map->virtual = ioremap_wc(bo->mem.bus.base + bo->mem.bus.offset + offset, + map->virtual = ioremap_wc(bo->mem.bus.base + + bo->mem.bus.offset + offset, size); else - map->virtual = ioremap(bo->mem.bus.base + bo->mem.bus.offset + offset, - size); + map->virtual = ioremap(bo->mem.bus.base + + bo->mem.bus.offset + offset, + size); } return (!map->virtual) ? -ENOMEM : 0; } From f5a9a9383f279de9da63296cb623a6418a66196b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20K=C3=B6nig?= Date: Wed, 15 Jul 2020 14:52:05 +0200 Subject: [PATCH 49/59] drm/ttm: remove TTM_MEMTYPE_FLAG_CMA MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The original intention was to avoid CPU page table unmaps when BOs move between the GTT and SYSTEM domain. The problem is that this never correctly handled changes in the caching attributes or backing pages. Just drop this for now and simply unmap the CPU page tables in all cases. Signed-off-by: Christian König Reviewed-by: Daniel Vetter Link: https://patchwork.freedesktop.org/patch/378240/ --- drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c | 4 +-- drivers/gpu/drm/nouveau/nouveau_bo.c | 3 +- drivers/gpu/drm/radeon/radeon_ttm.c | 2 +- drivers/gpu/drm/ttm/ttm_bo.c | 34 ++++------------------ drivers/gpu/drm/vmwgfx/vmwgfx_ttm_buffer.c | 2 +- include/drm/ttm/ttm_bo_driver.h | 1 - 6 files changed, 11 insertions(+), 35 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c index 9c0f12f74af9..44fa8bc49d18 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c @@ -93,7 +93,7 @@ static int amdgpu_init_mem_type(struct ttm_bo_device *bdev, uint32_t type, man->func = &amdgpu_gtt_mgr_func; man->available_caching = TTM_PL_MASK_CACHING; man->default_caching = TTM_PL_FLAG_CACHED; - man->flags = TTM_MEMTYPE_FLAG_MAPPABLE | TTM_MEMTYPE_FLAG_CMA; + man->flags = TTM_MEMTYPE_FLAG_MAPPABLE; break; case TTM_PL_VRAM: /* "On-card" video ram */ @@ -108,7 +108,7 @@ static int amdgpu_init_mem_type(struct ttm_bo_device *bdev, uint32_t type, case AMDGPU_PL_OA: /* On-chip GDS memory*/ man->func = &ttm_bo_manager_func; - man->flags = TTM_MEMTYPE_FLAG_FIXED | TTM_MEMTYPE_FLAG_CMA; + man->flags = TTM_MEMTYPE_FLAG_FIXED; man->available_caching = TTM_PL_FLAG_UNCACHED; man->default_caching = TTM_PL_FLAG_UNCACHED; break; diff --git a/drivers/gpu/drm/nouveau/nouveau_bo.c b/drivers/gpu/drm/nouveau/nouveau_bo.c index a1037478fa3f..7883341f8c83 100644 --- a/drivers/gpu/drm/nouveau/nouveau_bo.c +++ b/drivers/gpu/drm/nouveau/nouveau_bo.c @@ -695,8 +695,7 @@ nouveau_bo_init_mem_type(struct ttm_bo_device *bdev, uint32_t type, TTM_PL_FLAG_WC; man->default_caching = TTM_PL_FLAG_WC; } else { - man->flags = TTM_MEMTYPE_FLAG_MAPPABLE | - TTM_MEMTYPE_FLAG_CMA; + man->flags = TTM_MEMTYPE_FLAG_MAPPABLE; man->available_caching = TTM_PL_MASK_CACHING; man->default_caching = TTM_PL_FLAG_CACHED; } diff --git a/drivers/gpu/drm/radeon/radeon_ttm.c b/drivers/gpu/drm/radeon/radeon_ttm.c index 73085523fad7..54af06df865b 100644 --- a/drivers/gpu/drm/radeon/radeon_ttm.c +++ b/drivers/gpu/drm/radeon/radeon_ttm.c @@ -84,7 +84,7 @@ static int radeon_init_mem_type(struct ttm_bo_device *bdev, uint32_t type, man->func = &ttm_bo_manager_func; man->available_caching = TTM_PL_MASK_CACHING; man->default_caching = TTM_PL_FLAG_CACHED; - man->flags = TTM_MEMTYPE_FLAG_MAPPABLE | TTM_MEMTYPE_FLAG_CMA; + man->flags = TTM_MEMTYPE_FLAG_MAPPABLE; #if IS_ENABLED(CONFIG_AGP) if (rdev->flags & RADEON_IS_AGP) { if (!rdev->ddev->agp) { diff --git a/drivers/gpu/drm/ttm/ttm_bo.c b/drivers/gpu/drm/ttm/ttm_bo.c index 8b9e7f62bea7..0768a054a916 100644 --- a/drivers/gpu/drm/ttm/ttm_bo.c +++ b/drivers/gpu/drm/ttm/ttm_bo.c @@ -272,20 +272,15 @@ static int ttm_bo_handle_move_mem(struct ttm_buffer_object *bo, struct ttm_operation_ctx *ctx) { struct ttm_bo_device *bdev = bo->bdev; - bool old_is_pci = ttm_mem_reg_is_pci(bdev, &bo->mem); - bool new_is_pci = ttm_mem_reg_is_pci(bdev, mem); struct ttm_mem_type_manager *old_man = &bdev->man[bo->mem.mem_type]; struct ttm_mem_type_manager *new_man = &bdev->man[mem->mem_type]; - int ret = 0; + int ret; - if (old_is_pci || new_is_pci || - ((mem->placement & bo->mem.placement & TTM_PL_MASK_CACHING) == 0)) { - ret = ttm_mem_io_lock(old_man, true); - if (unlikely(ret != 0)) - goto out_err; - ttm_bo_unmap_virtual_locked(bo); - ttm_mem_io_unlock(old_man); - } + ret = ttm_mem_io_lock(old_man, true); + if (unlikely(ret != 0)) + goto out_err; + ttm_bo_unmap_virtual_locked(bo); + ttm_mem_io_unlock(old_man); /* * Create and bind a ttm if required. @@ -1698,23 +1693,6 @@ EXPORT_SYMBOL(ttm_bo_device_init); * buffer object vm functions. */ -bool ttm_mem_reg_is_pci(struct ttm_bo_device *bdev, struct ttm_mem_reg *mem) -{ - struct ttm_mem_type_manager *man = &bdev->man[mem->mem_type]; - - if (!(man->flags & TTM_MEMTYPE_FLAG_FIXED)) { - if (mem->mem_type == TTM_PL_SYSTEM) - return false; - - if (man->flags & TTM_MEMTYPE_FLAG_CMA) - return false; - - if (mem->placement & TTM_PL_FLAG_CACHED) - return false; - } - return true; -} - void ttm_bo_unmap_virtual_locked(struct ttm_buffer_object *bo) { struct ttm_bo_device *bdev = bo->bdev; diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_ttm_buffer.c b/drivers/gpu/drm/vmwgfx/vmwgfx_ttm_buffer.c index bfd0c54ec30a..6bea7548aee0 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_ttm_buffer.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_ttm_buffer.c @@ -762,7 +762,7 @@ static int vmw_init_mem_type(struct ttm_bo_device *bdev, uint32_t type, * slots as well as the bo size. */ man->func = &vmw_gmrid_manager_func; - man->flags = TTM_MEMTYPE_FLAG_CMA | TTM_MEMTYPE_FLAG_MAPPABLE; + man->flags = TTM_MEMTYPE_FLAG_MAPPABLE; man->available_caching = TTM_PL_FLAG_CACHED; man->default_caching = TTM_PL_FLAG_CACHED; break; diff --git a/include/drm/ttm/ttm_bo_driver.h b/include/drm/ttm/ttm_bo_driver.h index 45522e4fbd6b..71b195e78c7c 100644 --- a/include/drm/ttm/ttm_bo_driver.h +++ b/include/drm/ttm/ttm_bo_driver.h @@ -47,7 +47,6 @@ #define TTM_MEMTYPE_FLAG_FIXED (1 << 0) /* Fixed (on-card) PCI memory */ #define TTM_MEMTYPE_FLAG_MAPPABLE (1 << 1) /* Memory mappable */ -#define TTM_MEMTYPE_FLAG_CMA (1 << 3) /* Can't map aperture */ struct ttm_mem_type_manager; From cd1d58edcd25e1924c3f903563a3ddcc1190a8e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20K=C3=B6nig?= Date: Thu, 16 Jul 2020 10:15:17 +0200 Subject: [PATCH 50/59] drm/vmwgfx: stop using TTM_MEMTYPE_FLAG_MAPPABLE MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The driver doesn't expose any not-mapable memory resources. Signed-off-by: Christian König Reviewed-by: Thomas Zimmermann Reviewed-by: Daniel Vetter Link: https://patchwork.freedesktop.org/patch/378241/ --- drivers/gpu/drm/vmwgfx/vmwgfx_ttm_buffer.c | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_ttm_buffer.c b/drivers/gpu/drm/vmwgfx/vmwgfx_ttm_buffer.c index 6bea7548aee0..1d78187eaba6 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_ttm_buffer.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_ttm_buffer.c @@ -742,15 +742,13 @@ static int vmw_init_mem_type(struct ttm_bo_device *bdev, uint32_t type, switch (type) { case TTM_PL_SYSTEM: /* System memory */ - - man->flags = TTM_MEMTYPE_FLAG_MAPPABLE; man->available_caching = TTM_PL_FLAG_CACHED; man->default_caching = TTM_PL_FLAG_CACHED; break; case TTM_PL_VRAM: /* "On-card" video ram */ man->func = &vmw_thp_func; - man->flags = TTM_MEMTYPE_FLAG_FIXED | TTM_MEMTYPE_FLAG_MAPPABLE; + man->flags = TTM_MEMTYPE_FLAG_FIXED; man->available_caching = TTM_PL_FLAG_CACHED; man->default_caching = TTM_PL_FLAG_CACHED; break; @@ -762,7 +760,6 @@ static int vmw_init_mem_type(struct ttm_bo_device *bdev, uint32_t type, * slots as well as the bo size. */ man->func = &vmw_gmrid_manager_func; - man->flags = TTM_MEMTYPE_FLAG_MAPPABLE; man->available_caching = TTM_PL_FLAG_CACHED; man->default_caching = TTM_PL_FLAG_CACHED; break; @@ -789,7 +786,6 @@ static int vmw_verify_access(struct ttm_buffer_object *bo, struct file *filp) static int vmw_ttm_io_mem_reserve(struct ttm_bo_device *bdev, struct ttm_mem_reg *mem) { - struct ttm_mem_type_manager *man = &bdev->man[mem->mem_type]; struct vmw_private *dev_priv = container_of(bdev, struct vmw_private, bdev); mem->bus.addr = NULL; @@ -797,8 +793,7 @@ static int vmw_ttm_io_mem_reserve(struct ttm_bo_device *bdev, struct ttm_mem_reg mem->bus.offset = 0; mem->bus.size = mem->num_pages << PAGE_SHIFT; mem->bus.base = 0; - if (!(man->flags & TTM_MEMTYPE_FLAG_MAPPABLE)) - return -EINVAL; + switch (mem->mem_type) { case TTM_PL_SYSTEM: case VMW_PL_GMR: From ed024ca62a4a151b0754c87a27b492d584389ce5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20K=C3=B6nig?= Date: Thu, 16 Jul 2020 13:35:48 +0200 Subject: [PATCH 51/59] drm/nouveau: stop using TTM_MEMTYPE_FLAG_MAPPABLE MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The driver doesn't expose any not-mapable memory resources. Signed-off-by: Christian König Reviewed-by: Daniel Vetter Link: https://patchwork.freedesktop.org/patch/378244/ --- drivers/gpu/drm/nouveau/nouveau_bo.c | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/drivers/gpu/drm/nouveau/nouveau_bo.c b/drivers/gpu/drm/nouveau/nouveau_bo.c index 7883341f8c83..4ccf937df0d0 100644 --- a/drivers/gpu/drm/nouveau/nouveau_bo.c +++ b/drivers/gpu/drm/nouveau/nouveau_bo.c @@ -655,13 +655,12 @@ nouveau_bo_init_mem_type(struct ttm_bo_device *bdev, uint32_t type, switch (type) { case TTM_PL_SYSTEM: - man->flags = TTM_MEMTYPE_FLAG_MAPPABLE; + man->flags = 0; man->available_caching = TTM_PL_MASK_CACHING; man->default_caching = TTM_PL_FLAG_CACHED; break; case TTM_PL_VRAM: - man->flags = TTM_MEMTYPE_FLAG_FIXED | - TTM_MEMTYPE_FLAG_MAPPABLE; + man->flags = TTM_MEMTYPE_FLAG_FIXED; man->available_caching = TTM_PL_FLAG_UNCACHED | TTM_PL_FLAG_WC; man->default_caching = TTM_PL_FLAG_WC; @@ -690,12 +689,12 @@ nouveau_bo_init_mem_type(struct ttm_bo_device *bdev, uint32_t type, man->func = &ttm_bo_manager_func; if (drm->agp.bridge) { - man->flags = TTM_MEMTYPE_FLAG_MAPPABLE; + man->flags = 0; man->available_caching = TTM_PL_FLAG_UNCACHED | TTM_PL_FLAG_WC; man->default_caching = TTM_PL_FLAG_WC; } else { - man->flags = TTM_MEMTYPE_FLAG_MAPPABLE; + man->flags = 0; man->available_caching = TTM_PL_MASK_CACHING; man->default_caching = TTM_PL_FLAG_CACHED; } @@ -1437,7 +1436,6 @@ nouveau_bo_verify_access(struct ttm_buffer_object *bo, struct file *filp) static int nouveau_ttm_io_mem_reserve(struct ttm_bo_device *bdev, struct ttm_mem_reg *reg) { - struct ttm_mem_type_manager *man = &bdev->man[reg->mem_type]; struct nouveau_drm *drm = nouveau_bdev(bdev); struct nvkm_device *device = nvxx_device(&drm->client.device); struct nouveau_mem *mem = nouveau_mem(reg); @@ -1447,8 +1445,7 @@ nouveau_ttm_io_mem_reserve(struct ttm_bo_device *bdev, struct ttm_mem_reg *reg) reg->bus.size = reg->num_pages << PAGE_SHIFT; reg->bus.base = 0; reg->bus.is_iomem = false; - if (!(man->flags & TTM_MEMTYPE_FLAG_MAPPABLE)) - return -EINVAL; + switch (reg->mem_type) { case TTM_PL_SYSTEM: /* System memory */ From 5767603c2619ae7bb7828f99446be02a8c05ea63 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20K=C3=B6nig?= Date: Thu, 16 Jul 2020 13:46:05 +0200 Subject: [PATCH 52/59] drm/qxl: stop using TTM_MEMTYPE_FLAG_MAPPABLE v2 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The driver doesn't expose any not-mapable memory resources. v2: remove unused man variable as well Signed-off-by: Christian König Reviewed-by: Daniel Vetter Link: https://patchwork.freedesktop.org/patch/378246/ --- drivers/gpu/drm/qxl/qxl_ttm.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/drivers/gpu/drm/qxl/qxl_ttm.c b/drivers/gpu/drm/qxl/qxl_ttm.c index a6e67149ef4a..1d8e07b8b19e 100644 --- a/drivers/gpu/drm/qxl/qxl_ttm.c +++ b/drivers/gpu/drm/qxl/qxl_ttm.c @@ -54,7 +54,7 @@ static int qxl_init_mem_type(struct ttm_bo_device *bdev, uint32_t type, switch (type) { case TTM_PL_SYSTEM: /* System memory */ - man->flags = TTM_MEMTYPE_FLAG_MAPPABLE; + man->flags = 0; man->available_caching = TTM_PL_MASK_CACHING; man->default_caching = TTM_PL_FLAG_CACHED; break; @@ -62,8 +62,7 @@ static int qxl_init_mem_type(struct ttm_bo_device *bdev, uint32_t type, case TTM_PL_PRIV: /* "On-card" video ram */ man->func = &ttm_bo_manager_func; - man->flags = TTM_MEMTYPE_FLAG_FIXED | - TTM_MEMTYPE_FLAG_MAPPABLE; + man->flags = TTM_MEMTYPE_FLAG_FIXED; man->available_caching = TTM_PL_MASK_CACHING; man->default_caching = TTM_PL_FLAG_CACHED; break; @@ -99,7 +98,6 @@ static void qxl_evict_flags(struct ttm_buffer_object *bo, int qxl_ttm_io_mem_reserve(struct ttm_bo_device *bdev, struct ttm_mem_reg *mem) { - struct ttm_mem_type_manager *man = &bdev->man[mem->mem_type]; struct qxl_device *qdev = qxl_get_qdev(bdev); mem->bus.addr = NULL; @@ -107,8 +105,7 @@ int qxl_ttm_io_mem_reserve(struct ttm_bo_device *bdev, mem->bus.size = mem->num_pages << PAGE_SHIFT; mem->bus.base = 0; mem->bus.is_iomem = false; - if (!(man->flags & TTM_MEMTYPE_FLAG_MAPPABLE)) - return -EINVAL; + switch (mem->mem_type) { case TTM_PL_SYSTEM: /* system memory */ From fbc678e836f11a6a65058deb7af7566e99a9571e Mon Sep 17 00:00:00 2001 From: Lyude Paul Date: Mon, 20 Jul 2020 15:07:32 -0400 Subject: [PATCH 53/59] drm/vblank: Use spin_(un)lock_irq() in drm_crtc_vblank_reset() All of the drivers in the kernel tree only call this from one of the following contexts: * drm_crtc_funcs->reset * During initial module load Since both of these contexts are guaranteed to have interrupts enabled beforehand, there's no need to use the irqsave/irqrestore variants of spin_(un)lock(). So, fix this to make the irq context of this function more obvious. Signed-off-by: Lyude Paul Cc: Daniel Vetter Link: https://patchwork.freedesktop.org/patch/msgid/20200720190736.180297-2-lyude@redhat.com Reviewed-by: Daniel Vetter --- drivers/gpu/drm/drm_vblank.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/drm_vblank.c b/drivers/gpu/drm/drm_vblank.c index f402c75b9d34..6af78aecea9d 100644 --- a/drivers/gpu/drm/drm_vblank.c +++ b/drivers/gpu/drm/drm_vblank.c @@ -1363,11 +1363,10 @@ EXPORT_SYMBOL(drm_crtc_vblank_off); void drm_crtc_vblank_reset(struct drm_crtc *crtc) { struct drm_device *dev = crtc->dev; - unsigned long irqflags; unsigned int pipe = drm_crtc_index(crtc); struct drm_vblank_crtc *vblank = &dev->vblank[pipe]; - spin_lock_irqsave(&dev->vbl_lock, irqflags); + spin_lock_irq(&dev->vbl_lock); /* * Prevent subsequent drm_vblank_get() from enabling the vblank * interrupt by bumping the refcount. @@ -1376,7 +1375,7 @@ void drm_crtc_vblank_reset(struct drm_crtc *crtc) atomic_inc(&vblank->refcount); vblank->inmodeset = 1; } - spin_unlock_irqrestore(&dev->vbl_lock, irqflags); + spin_unlock_irq(&dev->vbl_lock); drm_WARN_ON(dev, !list_empty(&dev->vblank_event_list)); drm_WARN_ON(dev, !list_empty(&vblank->pending_work)); From 92cc68e35863c1c61c449efa2b2daef6e9926048 Mon Sep 17 00:00:00 2001 From: Lyude Paul Date: Mon, 20 Jul 2020 15:07:33 -0400 Subject: [PATCH 54/59] drm/vblank: Use spin_(un)lock_irq() in drm_crtc_vblank_on() This is only called from: * Atomic modesetting hooks * Module probing routines * Legacy modesetting hooks All of which have IRQs enabled, so we can also get rid of irqsave/restore here to make the IRQ context of this function more obvious. Signed-off-by: Lyude Paul Cc: Daniel Vetter Link: https://patchwork.freedesktop.org/patch/msgid/20200720190736.180297-3-lyude@redhat.com Reviewed-by: Daniel Vetter --- drivers/gpu/drm/drm_vblank.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/drm_vblank.c b/drivers/gpu/drm/drm_vblank.c index 6af78aecea9d..9891e82939e3 100644 --- a/drivers/gpu/drm/drm_vblank.c +++ b/drivers/gpu/drm/drm_vblank.c @@ -1428,12 +1428,11 @@ void drm_crtc_vblank_on(struct drm_crtc *crtc) struct drm_device *dev = crtc->dev; unsigned int pipe = drm_crtc_index(crtc); struct drm_vblank_crtc *vblank = &dev->vblank[pipe]; - unsigned long irqflags; if (drm_WARN_ON(dev, pipe >= dev->num_crtcs)) return; - spin_lock_irqsave(&dev->vbl_lock, irqflags); + spin_lock_irq(&dev->vbl_lock); drm_dbg_vbl(dev, "crtc %d, vblank enabled %d, inmodeset %d\n", pipe, vblank->enabled, vblank->inmodeset); @@ -1451,7 +1450,7 @@ void drm_crtc_vblank_on(struct drm_crtc *crtc) */ if (atomic_read(&vblank->refcount) != 0 || drm_vblank_offdelay == 0) drm_WARN_ON(dev, drm_vblank_enable(dev, pipe)); - spin_unlock_irqrestore(&dev->vbl_lock, irqflags); + spin_unlock_irq(&dev->vbl_lock); } EXPORT_SYMBOL(drm_crtc_vblank_on); From eb2ee0efd452dff6a8e6911ae46e25368b6c0ea7 Mon Sep 17 00:00:00 2001 From: Lyude Paul Date: Mon, 20 Jul 2020 15:07:34 -0400 Subject: [PATCH 55/59] drm/vblank: Use spin_(un)lock_irq() in drm_legacy_vblank_post_modeset() This function is only ever called from ioctl context, so we're guaranteed to have interrupts enabled. Stop using the irqsave/irqrestore variants of spin_(un)lock_irq() to make this more obvious. Signed-off-by: Lyude Paul Cc: Daniel Vetter Link: https://patchwork.freedesktop.org/patch/msgid/20200720190736.180297-4-lyude@redhat.com Reviewed-by: Daniel Vetter --- drivers/gpu/drm/drm_vblank.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/drm_vblank.c b/drivers/gpu/drm/drm_vblank.c index 9891e82939e3..51f2e988205e 100644 --- a/drivers/gpu/drm/drm_vblank.c +++ b/drivers/gpu/drm/drm_vblank.c @@ -1551,7 +1551,6 @@ static void drm_legacy_vblank_post_modeset(struct drm_device *dev, unsigned int pipe) { struct drm_vblank_crtc *vblank = &dev->vblank[pipe]; - unsigned long irqflags; /* vblank is not initialized (IRQ not installed ?), or has been freed */ if (!drm_dev_has_vblank(dev)) @@ -1561,9 +1560,9 @@ static void drm_legacy_vblank_post_modeset(struct drm_device *dev, return; if (vblank->inmodeset) { - spin_lock_irqsave(&dev->vbl_lock, irqflags); + spin_lock_irq(&dev->vbl_lock); drm_reset_vblank_timestamp(dev, pipe); - spin_unlock_irqrestore(&dev->vbl_lock, irqflags); + spin_unlock_irq(&dev->vbl_lock); if (vblank->inmodeset & 0x2) drm_vblank_put(dev, pipe); From 7f07c35fe63bc9563237436cb07bcc28f82a45b7 Mon Sep 17 00:00:00 2001 From: Lyude Paul Date: Mon, 20 Jul 2020 15:07:35 -0400 Subject: [PATCH 56/59] drm/vblank: Use spin_(un)lock_irq() in drm_queue_vblank_event() This one's easy - we're already calling kzalloc(GFP_KERNEL) in this function, so we must already be guaranteed to have IRQs enabled when calling this. So, use the plain _irq() variants of spin_(un)lock() to make this more obvious. Signed-off-by: Lyude Paul Cc: Daniel Vetter Reviewed-by: Daniel Vetter Link: https://patchwork.freedesktop.org/patch/msgid/20200720190736.180297-5-lyude@redhat.com --- drivers/gpu/drm/drm_vblank.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/drm_vblank.c b/drivers/gpu/drm/drm_vblank.c index 51f2e988205e..64610070ba47 100644 --- a/drivers/gpu/drm/drm_vblank.c +++ b/drivers/gpu/drm/drm_vblank.c @@ -1611,7 +1611,6 @@ static int drm_queue_vblank_event(struct drm_device *dev, unsigned int pipe, struct drm_vblank_crtc *vblank = &dev->vblank[pipe]; struct drm_pending_vblank_event *e; ktime_t now; - unsigned long flags; u64 seq; int ret; @@ -1633,7 +1632,7 @@ static int drm_queue_vblank_event(struct drm_device *dev, unsigned int pipe, e->event.vbl.crtc_id = crtc->base.id; } - spin_lock_irqsave(&dev->event_lock, flags); + spin_lock_irq(&dev->event_lock); /* * drm_crtc_vblank_off() might have been called after we called @@ -1670,12 +1669,12 @@ static int drm_queue_vblank_event(struct drm_device *dev, unsigned int pipe, vblwait->reply.sequence = req_seq; } - spin_unlock_irqrestore(&dev->event_lock, flags); + spin_unlock_irq(&dev->event_lock); return 0; err_unlock: - spin_unlock_irqrestore(&dev->event_lock, flags); + spin_unlock_irq(&dev->event_lock); kfree(e); err_put: drm_vblank_put(dev, pipe); From 5887a2ad53cee2c90401564a00fabf6af61a1aa5 Mon Sep 17 00:00:00 2001 From: Lyude Paul Date: Mon, 20 Jul 2020 15:07:36 -0400 Subject: [PATCH 57/59] drm/vblank: Use spin_(un)lock_irq() in drm_crtc_queue_sequence_ioctl() This is an ioctl callback, so we're guaranteed to have IRQs enabled when calling this function. Use the plain _irq() variants of spin_(un)lock() to make this more obvious. Signed-off-by: Lyude Paul Cc: Daniel Vetter Link: https://patchwork.freedesktop.org/patch/msgid/20200720190736.180297-6-lyude@redhat.com Reviewed-by: Daniel Vetter --- drivers/gpu/drm/drm_vblank.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/drm_vblank.c b/drivers/gpu/drm/drm_vblank.c index 64610070ba47..b18e1efbbae1 100644 --- a/drivers/gpu/drm/drm_vblank.c +++ b/drivers/gpu/drm/drm_vblank.c @@ -2066,7 +2066,6 @@ int drm_crtc_queue_sequence_ioctl(struct drm_device *dev, void *data, u64 seq; u64 req_seq; int ret; - unsigned long spin_flags; if (!drm_core_check_feature(dev, DRIVER_MODESET)) return -EOPNOTSUPP; @@ -2114,7 +2113,7 @@ int drm_crtc_queue_sequence_ioctl(struct drm_device *dev, void *data, e->event.base.length = sizeof(e->event.seq); e->event.seq.user_data = queue_seq->user_data; - spin_lock_irqsave(&dev->event_lock, spin_flags); + spin_lock_irq(&dev->event_lock); /* * drm_crtc_vblank_off() might have been called after we called @@ -2145,11 +2144,11 @@ int drm_crtc_queue_sequence_ioctl(struct drm_device *dev, void *data, queue_seq->sequence = req_seq; } - spin_unlock_irqrestore(&dev->event_lock, spin_flags); + spin_unlock_irq(&dev->event_lock); return 0; err_unlock: - spin_unlock_irqrestore(&dev->event_lock, spin_flags); + spin_unlock_irq(&dev->event_lock); drm_crtc_vblank_put(crtc); err_free: kfree(e); From 724884c3eba36a941a25505aa92dc5b136140233 Mon Sep 17 00:00:00 2001 From: Ondrej Jirman Date: Fri, 3 Jul 2020 13:47:16 +0200 Subject: [PATCH 58/59] dt-bindings: display: Fix example in nwl-dsi.yaml MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The example is now validated against rocktech,jh057n00900 schema that was ported to yaml, and didn't validate with: - '#address-cells', '#size-cells', 'port@0' do not match any of the regexes: 'pinctrl-[0-9]+' - 'vcc-supply' is a required property - 'iovcc-supply' is a required property - 'reset-gpios' is a required property Fix it. Reviewed-by: Rob Herring Reviewed-by: Guido Günther Signed-off-by: Ondrej Jirman Signed-off-by: Rob Herring Link: https://patchwork.freedesktop.org/patch/msgid/20200703114717.2140832-2-megous@megous.com --- .../devicetree/bindings/display/bridge/nwl-dsi.yaml | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/Documentation/devicetree/bindings/display/bridge/nwl-dsi.yaml b/Documentation/devicetree/bindings/display/bridge/nwl-dsi.yaml index 8aff2d68fc33..2c4c34e3bc29 100644 --- a/Documentation/devicetree/bindings/display/bridge/nwl-dsi.yaml +++ b/Documentation/devicetree/bindings/display/bridge/nwl-dsi.yaml @@ -165,6 +165,7 @@ examples: - | #include + #include #include #include @@ -191,12 +192,12 @@ examples: phy-names = "dphy"; panel@0 { - #address-cells = <1>; - #size-cells = <0>; compatible = "rocktech,jh057n00900"; reg = <0>; - port@0 { - reg = <0>; + vcc-supply = <®_2v8_p>; + iovcc-supply = <®_1v8_p>; + reset-gpios = <&gpio3 13 GPIO_ACTIVE_LOW>; + port { panel_in: endpoint { remote-endpoint = <&mipi_dsi_out>; }; From acc0c39a59ccd8161b9066265fb8798b4ee07dc9 Mon Sep 17 00:00:00 2001 From: Ondrej Jirman Date: Fri, 3 Jul 2020 13:47:17 +0200 Subject: [PATCH 59/59] dt-binding: display: Allow a single port node on rocktech, jh057n00900 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The display has one port. Allow it in the binding. Reviewed-by: Rob Herring Reviewed-by: Guido Günther Signed-off-by: Ondrej Jirman Signed-off-by: Rob Herring Link: https://patchwork.freedesktop.org/patch/msgid/20200703114717.2140832-3-megous@megous.com --- .../devicetree/bindings/display/panel/rocktech,jh057n00900.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/display/panel/rocktech,jh057n00900.yaml b/Documentation/devicetree/bindings/display/panel/rocktech,jh057n00900.yaml index a6985cd947fb..d5733ef30954 100644 --- a/Documentation/devicetree/bindings/display/panel/rocktech,jh057n00900.yaml +++ b/Documentation/devicetree/bindings/display/panel/rocktech,jh057n00900.yaml @@ -24,6 +24,7 @@ properties: # Xingbangda XBD599 5.99" 720x1440 TFT LCD panel - xingbangda,xbd599 + port: true reg: maxItems: 1 description: DSI virtual channel