diff options
author | Dave Airlie <airlied@redhat.com> | 2023-08-07 04:00:32 +0300 |
---|---|---|
committer | Dave Airlie <airlied@redhat.com> | 2023-08-07 04:00:32 +0300 |
commit | ca9e70f527150d0518e9da6737d667a8832c60b8 (patch) | |
tree | 932ca968e25bf9c0e6437394c9e7d5a6c12f4ce1 | |
parent | 2d3563e5556a61a41e24ea08622d781b38b017f1 (diff) | |
parent | a0769f25a3a621e8bbfb5e2a26e8ae462c761e33 (diff) | |
download | linux-ca9e70f527150d0518e9da6737d667a8832c60b8.tar.xz |
Merge tag 'drm-misc-next-2023-08-03' of git://anongit.freedesktop.org/drm/drm-misc into drm-next
drm-misc-next for v6.6:
UAPI Changes:
* virtio:
* Support sync objects
Cross-subsystem Changes:
* dt-bindings:
* Move several panel bindings to the correct files
* fbcon:
* Cleanups
* fbdev:
* Use _IOMEM_, _SYSMEM_, _DMAMEM_ infixes for initializer macros
and Kconfig tokens, update drivers accordingly
* ps3fb: Build fix
* hid/i2c:
* Allow panels and touchscreens to power sequence together
* host1x:
* Fixes
* video:
* Fix Kconfig dependencies for boot-up logo
Core Changes:
* Documentation updates and fixes
* Fixes
* MIPI-DBI:
* Allow using same the D/C GPIO for multiple displays plus
driver updates
* Tests:
* Convert to kunit actions
* Fix NULL-deref in drm_exec tests
Driver Changes:
* armada:
* Fixes
* ast:
* Represent BMV as virtual connector
* Report DP connection status
* bridge:
* dw-hdmi: Support CEC suspend/resume
* Support debugfs for chains
* Fixes
* i915:
* Fixes
* imx:
* Convert to dev_error_probe()
* Cleanups
* ipu-v3:
* Convert to devm_platform_ioremap_resource() in several places
* nouveau:
* Workaround DPCD issues
* panel:
* Convert to of_device_get_match_data()
* Fix Kconfig dependencies
* simple: Set bpc value to fix warning; Set connector type for AUO T215HVN01;
Support Innolux G156HCE-L01 plus DT bindings
* ili9881: Support TDO TL050HDV35 LCD panel plus DT bindings
* startek: Support KD070FHFID015 MIPI-DSI panel plus DT bindings
* sitronix-st7789v: Support Inanbo T28CP45TN89 plus DT bindings;
Support EDT ET028013DMA plus DT bindings; Various cleanups
* edp: Add timings for N140HCA-EAC
* Allow panels and touchscreens to power sequence together
* Documentation fixes
* qaic:
* Cleanups
* repaper:
* Fixes
* ssd130x
* Fix shadow-plane allocation
* Cleanups
* tegra:
* Convert to devm_platform_ioremap_resource() in several places
* Support bridge/connector
* Enable PM
* Fixes
* udl:
* Cleanups
* v3d:
* Fixes
* vc4:
* Convert tests to kunit actions
* virtio:
* Support sync objects
* vkms:
* Support gamma LUT
* Fixes
Signed-off-by: Dave Airlie <airlied@redhat.com>
# -----BEGIN PGP SIGNATURE-----
#
# iQEzBAABCAAdFiEEchf7rIzpz2NEoWjlaA3BHVMLeiMFAmTLwPUACgkQaA3BHVML
# eiNRBwf8CTjJJpSppitI6YEDyjG5JjpJPOrw4gmyjPCLMRhIa+ddtz8c6eiAJQTX
# Q4RWz4LWF0j/aRdXzxbhCJxLmgMoSbcZYN+jDSoaNbX4Fyi1KXw9eum/HZeMODBO
# ScZQFC5iyiCeKHRXZU4+WefqIFTEkEJJll92g3JYlvy793S2TQsA9LB1RIkbwK6x
# 0R+TtKSxAq9Gtwn4H0z4ACIzBTuIACxwNQRd6FTIeT4yrd7t+JY3WiBz9M96S6dK
# npHyjvJ3Brb88rEzv2eZZUey3fxp7sO7U7DruQVOKkgi4FsltPWxs6Ze9iylXQZr
# KcKfW7sxlF2JZlJwT4u0Ur6DMl60eQ==
# =K1nU
# -----END PGP SIGNATURE-----
# gpg: Signature made Fri 04 Aug 2023 01:00:05 AEST
# gpg: using RSA key 7217FBAC8CE9CF6344A168E5680DC11D530B7A23
# gpg: Can't check signature: No public key
From: Thomas Zimmermann <tzimmermann@suse.de>
Link: https://patchwork.freedesktop.org/patch/msgid/20230803150149.GA16884@linux-uq9g
124 files changed, 2782 insertions, 549 deletions
diff --git a/Documentation/devicetree/bindings/display/bridge/toshiba,tc358767.yaml b/Documentation/devicetree/bindings/display/bridge/toshiba,tc358767.yaml index 0521261b04a9..ae894d996d21 100644 --- a/Documentation/devicetree/bindings/display/bridge/toshiba,tc358767.yaml +++ b/Documentation/devicetree/bindings/display/bridge/toshiba,tc358767.yaml @@ -49,6 +49,9 @@ properties: description: | OF device-tree gpio specification for RSTX pin(active low system reset) + interrupts: + maxItems: 1 + toshiba,hpd-pin: $ref: /schemas/types.yaml#/definitions/uint32 enum: diff --git a/Documentation/devicetree/bindings/display/panel/ilitek,ili9881c.yaml b/Documentation/devicetree/bindings/display/panel/ilitek,ili9881c.yaml index c5d1df680858..e7ab6224b52e 100644 --- a/Documentation/devicetree/bindings/display/panel/ilitek,ili9881c.yaml +++ b/Documentation/devicetree/bindings/display/panel/ilitek,ili9881c.yaml @@ -18,6 +18,7 @@ properties: - enum: - bananapi,lhr050h41 - feixin,k101-im2byl02 + - tdo,tl050hdv35 - wanchanglong,w552946aba - const: ilitek,ili9881c diff --git a/Documentation/devicetree/bindings/display/panel/panel-lvds.yaml b/Documentation/devicetree/bindings/display/panel/panel-lvds.yaml index 929fe046d1e7..9f1016551e0b 100644 --- a/Documentation/devicetree/bindings/display/panel/panel-lvds.yaml +++ b/Documentation/devicetree/bindings/display/panel/panel-lvds.yaml @@ -40,6 +40,12 @@ properties: items: - enum: - auo,b101ew05 + # Chunghwa Picture Tubes Ltd. 7" WXGA (800x1280) TFT LCD LVDS panel + - chunghwa,claa070wp03xg + # HannStar Display Corp. HSD101PWW2 10.1" WXGA (1280x800) LVDS panel + - hannstar,hsd101pww2 + # Hydis Technologies 7" WXGA (800x1280) TFT LCD LVDS panel + - hydis,hv070wx2-1e0 - tbs,a711-panel - const: panel-lvds diff --git a/Documentation/devicetree/bindings/display/panel/panel-simple.yaml b/Documentation/devicetree/bindings/display/panel/panel-simple.yaml index 1d4936fc5182..25b4589d4a58 100644 --- a/Documentation/devicetree/bindings/display/panel/panel-simple.yaml +++ b/Documentation/devicetree/bindings/display/panel/panel-simple.yaml @@ -103,8 +103,6 @@ properties: - cdtech,s070wv95-ct16 # Chefree CH101OLHLWH-002 10.1" (1280x800) color TFT LCD panel - chefree,ch101olhlwh-002 - # Chunghwa Picture Tubes Ltd. 7" WXGA TFT LCD panel - - chunghwa,claa070wp03xg # Chunghwa Picture Tubes Ltd. 10.1" WXGA TFT LCD panel - chunghwa,claa101wa01a # Chunghwa Picture Tubes Ltd. 10.1" WXGA TFT LCD panel @@ -168,8 +166,6 @@ properties: - hannstar,hsd070pww1 # HannStar Display Corp. HSD100PXN1 10.1" XGA LVDS panel - hannstar,hsd100pxn1 - # HannStar Display Corp. HSD101PWW2 10.1" WXGA (1280x800) LVDS panel - - hannstar,hsd101pww2 # Hitachi Ltd. Corporation 9" WVGA (800x480) TFT LCD panel - hit,tx23d38vm0caa # InfoVision Optoelectronics M133NWF4 R0 13.3" FHD (1920x1080) TFT LCD panel @@ -196,6 +192,8 @@ properties: - innolux,n116bge # InnoLux 13.3" FHD (1920x1080) eDP TFT LCD panel - innolux,n125hce-gn1 + # InnoLux 15.6" FHD (1920x1080) TFT LCD panel + - innolux,g156hce-l01 # InnoLux 15.6" WXGA TFT LCD panel - innolux,n156bge-l21 # Innolux P120ZDG-BF1 12.02 inch eDP 2K display panel diff --git a/Documentation/devicetree/bindings/display/panel/sitronix,st7789v.yaml b/Documentation/devicetree/bindings/display/panel/sitronix,st7789v.yaml index fa6556363cca..905c064cd106 100644 --- a/Documentation/devicetree/bindings/display/panel/sitronix,st7789v.yaml +++ b/Documentation/devicetree/bindings/display/panel/sitronix,st7789v.yaml @@ -15,7 +15,10 @@ allOf: properties: compatible: - const: sitronix,st7789v + enum: + - edt,et028013dma + - inanbo,t28cp45tn89-v17 + - sitronix,st7789v reg: true reset-gpios: true @@ -26,6 +29,10 @@ properties: spi-cpha: true spi-cpol: true + spi-rx-bus-width: + minimum: 0 + maximum: 1 + dc-gpios: maxItems: 1 description: DCX pin, Display data/command selection pin in parallel interface @@ -33,7 +40,6 @@ properties: required: - compatible - reg - - reset-gpios - power-supply unevaluatedProperties: false diff --git a/Documentation/devicetree/bindings/display/panel/startek,kd070fhfid015.yaml b/Documentation/devicetree/bindings/display/panel/startek,kd070fhfid015.yaml new file mode 100644 index 000000000000..d817f998cddc --- /dev/null +++ b/Documentation/devicetree/bindings/display/panel/startek,kd070fhfid015.yaml @@ -0,0 +1,69 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/display/panel/startek,kd070fhfid015.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Startek Electronic Technology Co. kd070fhfid015 7 inch TFT LCD panel + +maintainers: + - Alexandre Mergnat <amergnat@baylibre.com> + +allOf: + - $ref: panel-common.yaml# + +properties: + compatible: + const: startek,kd070fhfid015 + + enable-gpios: true + + iovcc-supply: + description: Reference to the regulator powering the panel IO pins. + + reg: + maxItems: 1 + description: DSI virtual channel + + reset-gpios: true + + port: true + + power-supply: true + +additionalProperties: false + +required: + - compatible + - enable-gpios + - iovcc-supply + - reg + - reset-gpios + - port + - power-supply + +examples: + - | + #include <dt-bindings/gpio/gpio.h> + + dsi0 { + #address-cells = <1>; + #size-cells = <0>; + + panel@0 { + compatible = "startek,kd070fhfid015"; + reg = <0>; + enable-gpios = <&pio 67 GPIO_ACTIVE_HIGH>; + reset-gpios = <&pio 20 GPIO_ACTIVE_HIGH>; + iovcc-supply = <&mt6357_vsim1_reg>; + power-supply = <&vsys_lcm_reg>; + + port { + panel_in: endpoint { + remote-endpoint = <&dsi_out>; + }; + }; + }; + }; + +... diff --git a/Documentation/devicetree/bindings/input/elan,ekth6915.yaml b/Documentation/devicetree/bindings/input/elan,ekth6915.yaml index 05e6f2df604c..3e2d216c6432 100644 --- a/Documentation/devicetree/bindings/input/elan,ekth6915.yaml +++ b/Documentation/devicetree/bindings/input/elan,ekth6915.yaml @@ -13,6 +13,9 @@ description: Supports the Elan eKTH6915 touchscreen controller. This touchscreen controller uses the i2c-hid protocol with a reset GPIO. +allOf: + - $ref: /schemas/input/touchscreen/touchscreen.yaml# + properties: compatible: items: @@ -24,6 +27,8 @@ properties: interrupts: maxItems: 1 + panel: true + reset-gpios: description: Reset GPIO; not all touchscreens using eKTH6915 hook this up. diff --git a/Documentation/devicetree/bindings/input/goodix,gt7375p.yaml b/Documentation/devicetree/bindings/input/goodix,gt7375p.yaml index 1edad1da1196..358cb8275bf1 100644 --- a/Documentation/devicetree/bindings/input/goodix,gt7375p.yaml +++ b/Documentation/devicetree/bindings/input/goodix,gt7375p.yaml @@ -14,6 +14,9 @@ description: This touchscreen uses the i2c-hid protocol but has some non-standard power sequencing required. +allOf: + - $ref: /schemas/input/touchscreen/touchscreen.yaml# + properties: compatible: oneOf: @@ -30,6 +33,8 @@ properties: interrupts: maxItems: 1 + panel: true + reset-gpios: true diff --git a/Documentation/devicetree/bindings/input/hid-over-i2c.yaml b/Documentation/devicetree/bindings/input/hid-over-i2c.yaml index 7156b08f7645..138caad96a29 100644 --- a/Documentation/devicetree/bindings/input/hid-over-i2c.yaml +++ b/Documentation/devicetree/bindings/input/hid-over-i2c.yaml @@ -44,6 +44,8 @@ properties: description: HID descriptor address $ref: /schemas/types.yaml#/definitions/uint32 + panel: true + post-power-on-delay-ms: description: Time required by the device after enabling its regulators or powering it on, before it is ready for communication. diff --git a/Documentation/devicetree/bindings/input/touchscreen/touchscreen.yaml b/Documentation/devicetree/bindings/input/touchscreen/touchscreen.yaml index 895592da9626..431c13335c40 100644 --- a/Documentation/devicetree/bindings/input/touchscreen/touchscreen.yaml +++ b/Documentation/devicetree/bindings/input/touchscreen/touchscreen.yaml @@ -10,6 +10,13 @@ maintainers: - Dmitry Torokhov <dmitry.torokhov@gmail.com> properties: + panel: + description: If this touchscreen is integrally connected to a panel, this + is a reference to that panel. The presence of this reference indicates + that the touchscreen should be power sequenced together with the panel + and that they may share power and/or reset signals. + $ref: /schemas/types.yaml#/definitions/phandle + touchscreen-min-x: description: minimum x coordinate reported $ref: /schemas/types.yaml#/definitions/uint32 diff --git a/Documentation/devicetree/bindings/vendor-prefixes.yaml b/Documentation/devicetree/bindings/vendor-prefixes.yaml index af60bf1a6664..1e2e51401dc5 100644 --- a/Documentation/devicetree/bindings/vendor-prefixes.yaml +++ b/Documentation/devicetree/bindings/vendor-prefixes.yaml @@ -617,6 +617,8 @@ patternProperties: description: Imagination Technologies Ltd. "^imi,.*": description: Integrated Micro-Electronics Inc. + "^inanbo,.*": + description: Shenzhen INANBO Electronic Technology Co., Ltd. "^incircuit,.*": description: In-Circuit GmbH "^indiedroid,.*": diff --git a/Documentation/gpu/drm-mm.rst b/Documentation/gpu/drm-mm.rst index 3d5dc9dc1bfe..513197359aba 100644 --- a/Documentation/gpu/drm-mm.rst +++ b/Documentation/gpu/drm-mm.rst @@ -517,6 +517,8 @@ DRM Cache Handling and Fast WC memcpy() .. kernel-doc:: drivers/gpu/drm/drm_cache.c :export: +.. _drm_sync_objects: + DRM Sync Objects =========================== diff --git a/Documentation/gpu/todo.rst b/Documentation/gpu/todo.rst index d0f9f87ea515..139980487ccf 100644 --- a/Documentation/gpu/todo.rst +++ b/Documentation/gpu/todo.rst @@ -49,14 +49,18 @@ converted over. Modern compositors like Wayland or Surfaceflinger on Android really want an atomic modeset interface, so this is all about the bright future. -There is a conversion guide for atomic and all you need is a GPU for a -non-converted driver (again virtual HW drivers for KVM are still all -suitable). +There is a conversion guide for atomic [1]_ and all you need is a GPU for a +non-converted driver. The "Atomic mode setting design overview" series [2]_ +[3]_ at LWN.net can also be helpful. As part of this drivers also need to convert to universal plane (which means exposing primary & cursor as proper plane objects). But that's much easier to do by directly using the new atomic helper driver callbacks. + .. [1] https://blog.ffwll.ch/2014/11/atomic-modeset-support-for-kms-drivers.html + .. [2] https://lwn.net/Articles/653071/ + .. [3] https://lwn.net/Articles/653466/ + Contact: Daniel Vetter, respective driver maintainers Level: Advanced @@ -456,6 +460,31 @@ Contact: Thomas Zimmermann <tzimmermann@suse.de> Level: Starter +Clean up checks for already prepared/enabled in panels +------------------------------------------------------ + +In a whole pile of panel drivers, we have code to make the +prepare/unprepare/enable/disable callbacks behave as no-ops if they've already +been called. To get some idea of the duplicated code, try:: + + git grep 'if.*>prepared' -- drivers/gpu/drm/panel + git grep 'if.*>enabled' -- drivers/gpu/drm/panel + +In the patch ("drm/panel: Check for already prepared/enabled in drm_panel") +we've moved this check to the core. Now we can most definitely remove the +check from the individual panels and save a pile of code. + +In adition to removing the check from the individual panels, it is believed +that even the core shouldn't need this check and that should be considered +an error if other code ever relies on this check. The check in the core +currently prints a warning whenever something is relying on this check with +dev_warn(). After a little while, we likely want to promote this to a +WARN(1) to help encourage folks not to rely on this behavior. + +Contact: Douglas Anderson <dianders@chromium.org> + +Level: Starter/Intermediate + Core refactorings ================= @@ -753,16 +782,16 @@ existing hardware. The new driver's call-back functions are filled from existing fbdev code. More complex fbdev drivers can be refactored step-by-step into a DRM -driver with the help of the DRM fbconv helpers. [1] These helpers provide +driver with the help of the DRM fbconv helpers [4]_. These helpers provide the transition layer between the DRM core infrastructure and the fbdev driver interface. Create a new DRM driver on top of the fbconv helpers, copy over the fbdev driver, and hook it up to the DRM code. Examples for -several fbdev drivers are available at [1] and a tutorial of this process -available at [2]. The result is a primitive DRM driver that can run X11 -and Weston. +several fbdev drivers are available in Thomas Zimmermann's fbconv tree +[4]_, as well as a tutorial of this process [5]_. The result is a primitive +DRM driver that can run X11 and Weston. - - [1] https://gitlab.freedesktop.org/tzimmermann/linux/tree/fbconv - - [2] https://gitlab.freedesktop.org/tzimmermann/linux/blob/fbconv/drivers/gpu/drm/drm_fbconv_helper.c + .. [4] https://gitlab.freedesktop.org/tzimmermann/linux/tree/fbconv + .. [5] https://gitlab.freedesktop.org/tzimmermann/linux/blob/fbconv/drivers/gpu/drm/drm_fbconv_helper.c Contact: Thomas Zimmermann <tzimmermann@suse.de> diff --git a/drivers/accel/qaic/qaic_data.c b/drivers/accel/qaic/qaic_data.c index e9a1cb779b30..a90b64b325b4 100644 --- a/drivers/accel/qaic/qaic_data.c +++ b/drivers/accel/qaic/qaic_data.c @@ -1292,7 +1292,6 @@ static void update_profiling_data(struct drm_file *file_priv, static int __qaic_execute_bo_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv, bool is_partial) { - struct qaic_partial_execute_entry *pexec; struct qaic_execute *args = data; struct qaic_execute_entry *exec; struct dma_bridge_chan *dbc; @@ -1312,7 +1311,7 @@ static int __qaic_execute_bo_ioctl(struct drm_device *dev, void *data, struct dr received_ts = ktime_get_ns(); - size = is_partial ? sizeof(*pexec) : sizeof(*exec); + size = is_partial ? sizeof(struct qaic_partial_execute_entry) : sizeof(*exec); n = (unsigned long)size * args->hdr.count; if (args->hdr.count == 0 || n / args->hdr.count != size) return -EINVAL; @@ -1320,7 +1319,6 @@ static int __qaic_execute_bo_ioctl(struct drm_device *dev, void *data, struct dr user_data = u64_to_user_ptr(args->data); exec = kcalloc(args->hdr.count, size, GFP_KERNEL); - pexec = (struct qaic_partial_execute_entry *)exec; if (!exec) return -ENOMEM; diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig index 0d499669d653..b51c6a141dfa 100644 --- a/drivers/gpu/drm/Kconfig +++ b/drivers/gpu/drm/Kconfig @@ -11,7 +11,7 @@ menuconfig DRM select DRM_PANEL_ORIENTATION_QUIRKS select DRM_KMS_HELPER if DRM_FBDEV_EMULATION select FB_CORE if DRM_FBDEV_EMULATION - select FB_SYS_HELPERS_DEFERRED if DRM_FBDEV_EMULATION + select FB_SYSMEM_HELPERS_DEFERRED if DRM_FBDEV_EMULATION select HDMI select I2C select DMA_SHARED_BUFFER @@ -224,7 +224,7 @@ config DRM_TTM_HELPER config DRM_GEM_DMA_HELPER tristate depends on DRM - select FB_DMA_HELPERS if DRM_FBDEV_EMULATION + select FB_DMAMEM_HELPERS if DRM_FBDEV_EMULATION help Choose this if you need the GEM DMA helper functions diff --git a/drivers/gpu/drm/armada/Kconfig b/drivers/gpu/drm/armada/Kconfig index 5afade25e217..e5597d7c9ae1 100644 --- a/drivers/gpu/drm/armada/Kconfig +++ b/drivers/gpu/drm/armada/Kconfig @@ -3,7 +3,7 @@ config DRM_ARMADA tristate "DRM support for Marvell Armada SoCs" depends on DRM && HAVE_CLK && ARM && MMU select DRM_KMS_HELPER - select FB_IO_HELPERS if DRM_FBDEV_EMULATION + select FB_IOMEM_HELPERS if DRM_FBDEV_EMULATION help Support the "LCD" controllers found on the Marvell Armada 510 devices. There are two controllers on the device, each controller diff --git a/drivers/gpu/drm/armada/armada_fbdev.c b/drivers/gpu/drm/armada/armada_fbdev.c index e40a95e51785..d223176912b6 100644 --- a/drivers/gpu/drm/armada/armada_fbdev.c +++ b/drivers/gpu/drm/armada/armada_fbdev.c @@ -34,7 +34,7 @@ static void armada_fbdev_fb_destroy(struct fb_info *info) static const struct fb_ops armada_fb_ops = { .owner = THIS_MODULE, - FB_DEFAULT_IO_OPS, + FB_DEFAULT_IOMEM_OPS, DRM_FB_HELPER_DEFAULT_OPS, .fb_destroy = armada_fbdev_fb_destroy, }; diff --git a/drivers/gpu/drm/armada/armada_overlay.c b/drivers/gpu/drm/armada/armada_overlay.c index f21eb8fb76d8..3b9bd8ecda13 100644 --- a/drivers/gpu/drm/armada/armada_overlay.c +++ b/drivers/gpu/drm/armada/armada_overlay.c @@ -4,6 +4,8 @@ * Rewritten from the dovefb driver, and Armada510 manuals. */ +#include <linux/bitfield.h> + #include <drm/armada_drm.h> #include <drm/drm_atomic.h> #include <drm/drm_atomic_helper.h> @@ -445,8 +447,8 @@ static int armada_overlay_get_property(struct drm_plane *plane, drm_to_overlay_state(state)->colorkey_ug, drm_to_overlay_state(state)->colorkey_vb, 0); } else if (property == priv->colorkey_mode_prop) { - *val = (drm_to_overlay_state(state)->colorkey_mode & - CFG_CKMODE_MASK) >> ffs(CFG_CKMODE_MASK); + *val = FIELD_GET(CFG_CKMODE_MASK, + drm_to_overlay_state(state)->colorkey_mode); } else if (property == priv->brightness_prop) { *val = drm_to_overlay_state(state)->brightness + 256; } else if (property == priv->contrast_prop) { diff --git a/drivers/gpu/drm/ast/ast_dp.c b/drivers/gpu/drm/ast/ast_dp.c index 6dc1a09504e1..fdd9a493aa9c 100644 --- a/drivers/gpu/drm/ast/ast_dp.c +++ b/drivers/gpu/drm/ast/ast_dp.c @@ -7,6 +7,17 @@ #include <drm/drm_print.h> #include "ast_drv.h" +bool ast_astdp_is_connected(struct ast_device *ast) +{ + if (!ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xD1, ASTDP_MCU_FW_EXECUTING)) + return false; + if (!ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xDF, ASTDP_HPD)) + return false; + if (!ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xDC, ASTDP_LINK_SUCCESS)) + return false; + return true; +} + int ast_astdp_read_edid(struct drm_device *dev, u8 *ediddata) { struct ast_device *ast = to_ast_device(dev); diff --git a/drivers/gpu/drm/ast/ast_dp501.c b/drivers/gpu/drm/ast/ast_dp501.c index a5d285850ffb..f10d53b0c94f 100644 --- a/drivers/gpu/drm/ast/ast_dp501.c +++ b/drivers/gpu/drm/ast/ast_dp501.c @@ -272,11 +272,9 @@ static bool ast_launch_m68k(struct drm_device *dev) return true; } -bool ast_dp501_read_edid(struct drm_device *dev, u8 *ediddata) +bool ast_dp501_is_connected(struct ast_device *ast) { - struct ast_device *ast = to_ast_device(dev); - u32 i, boot_address, offset, data; - u32 *pEDIDidx; + u32 boot_address, offset, data; if (ast->config_mode == ast_use_p2a) { boot_address = get_fw_base(ast); @@ -292,14 +290,6 @@ bool ast_dp501_read_edid(struct drm_device *dev, u8 *ediddata) data = ast_mindwm(ast, boot_address + offset); if (!(data & AST_DP501_PNP_CONNECTED)) return false; - - /* Read EDID */ - offset = AST_DP501_EDID_DATA; - for (i = 0; i < 128; i += 4) { - data = ast_mindwm(ast, boot_address + offset + i); - pEDIDidx = (u32 *)(ediddata + i); - *pEDIDidx = data; - } } else { if (!ast->dp501_fw_buf) return false; @@ -319,10 +309,33 @@ bool ast_dp501_read_edid(struct drm_device *dev, u8 *ediddata) data = readl(ast->dp501_fw_buf + offset); if (!(data & AST_DP501_PNP_CONNECTED)) return false; + } + return true; +} + +bool ast_dp501_read_edid(struct drm_device *dev, u8 *ediddata) +{ + struct ast_device *ast = to_ast_device(dev); + u32 i, boot_address, offset, data; + u32 *pEDIDidx; + + if (!ast_dp501_is_connected(ast)) + return false; + + if (ast->config_mode == ast_use_p2a) { + boot_address = get_fw_base(ast); /* Read EDID */ offset = AST_DP501_EDID_DATA; for (i = 0; i < 128; i += 4) { + data = ast_mindwm(ast, boot_address + offset + i); + pEDIDidx = (u32 *)(ediddata + i); + *pEDIDidx = data; + } + } else { + /* Read EDID */ + offset = AST_DP501_EDID_DATA; + for (i = 0; i < 128; i += 4) { data = readl(ast->dp501_fw_buf + offset + i); pEDIDidx = (u32 *)(ediddata + i); *pEDIDidx = data; diff --git a/drivers/gpu/drm/ast/ast_drv.h b/drivers/gpu/drm/ast/ast_drv.h index 3f6e0c984523..848a9f1403e8 100644 --- a/drivers/gpu/drm/ast/ast_drv.h +++ b/drivers/gpu/drm/ast/ast_drv.h @@ -214,6 +214,10 @@ struct ast_device { struct drm_encoder encoder; struct drm_connector connector; } astdp; + struct { + struct drm_encoder encoder; + struct drm_connector connector; + } bmc; } output; bool support_wide_screen; @@ -510,6 +514,7 @@ void ast_patch_ahb_2500(struct ast_device *ast); /* ast dp501 */ void ast_set_dp501_video_output(struct drm_device *dev, u8 mode); bool ast_backup_fw(struct drm_device *dev, u8 *addr, u32 size); +bool ast_dp501_is_connected(struct ast_device *ast); bool ast_dp501_read_edid(struct drm_device *dev, u8 *ediddata); u8 ast_get_dp501_max_clk(struct drm_device *dev); void ast_init_3rdtx(struct drm_device *dev); @@ -518,6 +523,7 @@ void ast_init_3rdtx(struct drm_device *dev); struct ast_i2c_chan *ast_i2c_create(struct drm_device *dev); /* aspeed DP */ +bool ast_astdp_is_connected(struct ast_device *ast); int ast_astdp_read_edid(struct drm_device *dev, u8 *ediddata); void ast_dp_launch(struct drm_device *dev); void ast_dp_power_on_off(struct drm_device *dev, bool no); diff --git a/drivers/gpu/drm/ast/ast_mode.c b/drivers/gpu/drm/ast/ast_mode.c index f711d592da52..32f04ec6c386 100644 --- a/drivers/gpu/drm/ast/ast_mode.c +++ b/drivers/gpu/drm/ast/ast_mode.c @@ -1582,8 +1582,20 @@ err_drm_connector_update_edid_property: return 0; } +static int ast_dp501_connector_helper_detect_ctx(struct drm_connector *connector, + struct drm_modeset_acquire_ctx *ctx, + bool force) +{ + struct ast_device *ast = to_ast_device(connector->dev); + + if (ast_dp501_is_connected(ast)) + return connector_status_connected; + return connector_status_disconnected; +} + static const struct drm_connector_helper_funcs ast_dp501_connector_helper_funcs = { .get_modes = ast_dp501_connector_helper_get_modes, + .detect_ctx = ast_dp501_connector_helper_detect_ctx, }; static const struct drm_connector_funcs ast_dp501_connector_funcs = { @@ -1608,7 +1620,7 @@ static int ast_dp501_connector_init(struct drm_device *dev, struct drm_connector connector->interlace_allowed = 0; connector->doublescan_allowed = 0; - connector->polled = DRM_CONNECTOR_POLL_CONNECT; + connector->polled = DRM_CONNECTOR_POLL_CONNECT | DRM_CONNECTOR_POLL_DISCONNECT; return 0; } @@ -1680,8 +1692,20 @@ err_drm_connector_update_edid_property: return 0; } +static int ast_astdp_connector_helper_detect_ctx(struct drm_connector *connector, + struct drm_modeset_acquire_ctx *ctx, + bool force) +{ + struct ast_device *ast = to_ast_device(connector->dev); + + if (ast_astdp_is_connected(ast)) + return connector_status_connected; + return connector_status_disconnected; +} + static const struct drm_connector_helper_funcs ast_astdp_connector_helper_funcs = { .get_modes = ast_astdp_connector_helper_get_modes, + .detect_ctx = ast_astdp_connector_helper_detect_ctx, }; static const struct drm_connector_funcs ast_astdp_connector_funcs = { @@ -1706,7 +1730,7 @@ static int ast_astdp_connector_init(struct drm_device *dev, struct drm_connector connector->interlace_allowed = 0; connector->doublescan_allowed = 0; - connector->polled = DRM_CONNECTOR_POLL_CONNECT; + connector->polled = DRM_CONNECTOR_POLL_CONNECT | DRM_CONNECTOR_POLL_DISCONNECT; return 0; } @@ -1736,6 +1760,60 @@ static int ast_astdp_output_init(struct ast_device *ast) } /* + * BMC virtual Connector + */ + +static const struct drm_encoder_funcs ast_bmc_encoder_funcs = { + .destroy = drm_encoder_cleanup, +}; + +static int ast_bmc_connector_helper_get_modes(struct drm_connector *connector) +{ + return drm_add_modes_noedid(connector, 4096, 4096); +} + +static const struct drm_connector_helper_funcs ast_bmc_connector_helper_funcs = { + .get_modes = ast_bmc_connector_helper_get_modes, +}; + +static const struct drm_connector_funcs ast_bmc_connector_funcs = { + .reset = drm_atomic_helper_connector_reset, + .fill_modes = drm_helper_probe_single_connector_modes, + .destroy = drm_connector_cleanup, + .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, + .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, +}; + +static int ast_bmc_output_init(struct ast_device *ast) +{ + struct drm_device *dev = &ast->base; + struct drm_crtc *crtc = &ast->crtc; + struct drm_encoder *encoder = &ast->output.bmc.encoder; + struct drm_connector *connector = &ast->output.bmc.connector; + int ret; + + ret = drm_encoder_init(dev, encoder, + &ast_bmc_encoder_funcs, + DRM_MODE_ENCODER_VIRTUAL, "ast_bmc"); + if (ret) + return ret; + encoder->possible_crtcs = drm_crtc_mask(crtc); + + ret = drm_connector_init(dev, connector, &ast_bmc_connector_funcs, + DRM_MODE_CONNECTOR_VIRTUAL); + if (ret) + return ret; + + drm_connector_helper_add(connector, &ast_bmc_connector_helper_funcs); + + ret = drm_connector_attach_encoder(connector, encoder); + if (ret) + return ret; + + return 0; +} + +/* * Mode config */ @@ -1842,8 +1920,13 @@ int ast_mode_config_init(struct ast_device *ast) if (ret) return ret; } + ret = ast_bmc_output_init(ast); + if (ret) + return ret; drm_mode_config_reset(dev); + drm_kms_helper_poll_init(dev); + return 0; } diff --git a/drivers/gpu/drm/bridge/analogix/analogix-anx6345.c b/drivers/gpu/drm/bridge/analogix/analogix-anx6345.c index 72ab2ab77081..c9e35731e6a1 100644 --- a/drivers/gpu/drm/bridge/analogix/analogix-anx6345.c +++ b/drivers/gpu/drm/bridge/analogix/analogix-anx6345.c @@ -813,7 +813,7 @@ MODULE_DEVICE_TABLE(of, anx6345_match_table); static struct i2c_driver anx6345_driver = { .driver = { .name = "anx6345", - .of_match_table = of_match_ptr(anx6345_match_table), + .of_match_table = anx6345_match_table, }, .probe = anx6345_i2c_probe, .remove = anx6345_i2c_remove, diff --git a/drivers/gpu/drm/bridge/analogix/analogix-anx78xx.c b/drivers/gpu/drm/bridge/analogix/analogix-anx78xx.c index 06a3e3243e19..800555aef97f 100644 --- a/drivers/gpu/drm/bridge/analogix/analogix-anx78xx.c +++ b/drivers/gpu/drm/bridge/analogix/analogix-anx78xx.c @@ -1373,7 +1373,6 @@ static const struct i2c_device_id anx78xx_id[] = { }; MODULE_DEVICE_TABLE(i2c, anx78xx_id); -#if IS_ENABLED(CONFIG_OF) static const struct of_device_id anx78xx_match_table[] = { { .compatible = "analogix,anx7808", .data = anx7808_i2c_addresses }, { .compatible = "analogix,anx7812", .data = anx781x_i2c_addresses }, @@ -1382,12 +1381,11 @@ static const struct of_device_id anx78xx_match_table[] = { { /* sentinel */ }, }; MODULE_DEVICE_TABLE(of, anx78xx_match_table); -#endif static struct i2c_driver anx78xx_driver = { .driver = { .name = "anx7814", - .of_match_table = of_match_ptr(anx78xx_match_table), + .of_match_table = anx78xx_match_table, }, .probe = anx78xx_i2c_probe, .remove = anx78xx_i2c_remove, diff --git a/drivers/gpu/drm/bridge/cadence/cdns-mhdp8546-core.c b/drivers/gpu/drm/bridge/cadence/cdns-mhdp8546-core.c index 0ff51f945fb0..6af565ac307a 100644 --- a/drivers/gpu/drm/bridge/cadence/cdns-mhdp8546-core.c +++ b/drivers/gpu/drm/bridge/cadence/cdns-mhdp8546-core.c @@ -2655,7 +2655,7 @@ MODULE_DEVICE_TABLE(of, mhdp_ids); static struct platform_driver mhdp_driver = { .driver = { .name = "cdns-mhdp8546", - .of_match_table = of_match_ptr(mhdp_ids), + .of_match_table = mhdp_ids, }, .probe = cdns_mhdp_probe, .remove = cdns_mhdp_remove, diff --git a/drivers/gpu/drm/bridge/chrontel-ch7033.c b/drivers/gpu/drm/bridge/chrontel-ch7033.c index a854eb84e399..483c28c7fc99 100644 --- a/drivers/gpu/drm/bridge/chrontel-ch7033.c +++ b/drivers/gpu/drm/bridge/chrontel-ch7033.c @@ -607,7 +607,7 @@ static struct i2c_driver ch7033_driver = { .remove = ch7033_remove, .driver = { .name = "ch7033", - .of_match_table = of_match_ptr(ch7033_dt_ids), + .of_match_table = ch7033_dt_ids, }, .id_table = ch7033_ids, }; diff --git a/drivers/gpu/drm/bridge/sil-sii8620.c b/drivers/gpu/drm/bridge/sil-sii8620.c index 79b09ccd1353..599164e3877d 100644 --- a/drivers/gpu/drm/bridge/sil-sii8620.c +++ b/drivers/gpu/drm/bridge/sil-sii8620.c @@ -2376,7 +2376,7 @@ MODULE_DEVICE_TABLE(i2c, sii8620_id); static struct i2c_driver sii8620_driver = { .driver = { .name = "sii8620", - .of_match_table = of_match_ptr(sii8620_dt_match), + .of_match_table = sii8620_dt_match, }, .probe = sii8620_probe, .remove = sii8620_remove, diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi-cec.c b/drivers/gpu/drm/bridge/synopsys/dw-hdmi-cec.c index 9389ce526eb1..be21c11de1f2 100644 --- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi-cec.c +++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi-cec.c @@ -62,6 +62,10 @@ struct dw_hdmi_cec { bool rx_done; struct cec_notifier *notify; int irq; + + u8 regs_polarity; + u8 regs_mask; + u8 regs_mute_stat0; }; static void dw_hdmi_write(struct dw_hdmi_cec *cec, u8 val, int offset) @@ -304,11 +308,44 @@ static void dw_hdmi_cec_remove(struct platform_device *pdev) cec_unregister_adapter(cec->adap); } +static int __maybe_unused dw_hdmi_cec_resume(struct device *dev) +{ + struct dw_hdmi_cec *cec = dev_get_drvdata(dev); + + /* Restore logical address */ + dw_hdmi_write(cec, cec->addresses & 255, HDMI_CEC_ADDR_L); + dw_hdmi_write(cec, cec->addresses >> 8, HDMI_CEC_ADDR_H); + + /* Restore interrupt status/mask registers */ + dw_hdmi_write(cec, cec->regs_polarity, HDMI_CEC_POLARITY); + dw_hdmi_write(cec, cec->regs_mask, HDMI_CEC_MASK); + dw_hdmi_write(cec, cec->regs_mute_stat0, HDMI_IH_MUTE_CEC_STAT0); + + return 0; +} + +static int __maybe_unused dw_hdmi_cec_suspend(struct device *dev) +{ + struct dw_hdmi_cec *cec = dev_get_drvdata(dev); + + /* store interrupt status/mask registers */ + cec->regs_polarity = dw_hdmi_read(cec, HDMI_CEC_POLARITY); + cec->regs_mask = dw_hdmi_read(cec, HDMI_CEC_MASK); + cec->regs_mute_stat0 = dw_hdmi_read(cec, HDMI_IH_MUTE_CEC_STAT0); + + return 0; +} + +static const struct dev_pm_ops dw_hdmi_cec_pm = { + SET_SYSTEM_SLEEP_PM_OPS(dw_hdmi_cec_suspend, dw_hdmi_cec_resume) +}; + static struct platform_driver dw_hdmi_cec_driver = { .probe = dw_hdmi_cec_probe, .remove_new = dw_hdmi_cec_remove, .driver = { .name = "dw-hdmi-cec", + .pm = &dw_hdmi_cec_pm, }, }; module_platform_driver(dw_hdmi_cec_driver); diff --git a/drivers/gpu/drm/bridge/ti-tfp410.c b/drivers/gpu/drm/bridge/ti-tfp410.c index 22b07260a78e..28848a8eb42e 100644 --- a/drivers/gpu/drm/bridge/ti-tfp410.c +++ b/drivers/gpu/drm/bridge/ti-tfp410.c @@ -448,7 +448,7 @@ MODULE_DEVICE_TABLE(i2c, tfp410_i2c_ids); static struct i2c_driver tfp410_i2c_driver = { .driver = { .name = "tfp410", - .of_match_table = of_match_ptr(tfp410_match), + .of_match_table = tfp410_match, }, .id_table = tfp410_i2c_ids, .probe = tfp410_i2c_probe, diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c index 41b8066f61ff..292e38eb6218 100644 --- a/drivers/gpu/drm/drm_atomic_helper.c +++ b/drivers/gpu/drm/drm_atomic_helper.c @@ -3332,7 +3332,7 @@ EXPORT_SYMBOL(drm_atomic_helper_disable_all); * that also takes a snapshot of the modeset state to be restored on resume. * * This is just a convenience wrapper around drm_atomic_helper_disable_all(), - * and it is the atomic version of drm_crtc_force_disable_all(). + * and it is the atomic version of drm_helper_force_disable_all(). */ void drm_atomic_helper_shutdown(struct drm_device *dev) { diff --git a/drivers/gpu/drm/drm_bridge.c b/drivers/gpu/drm/drm_bridge.c index c3d69af02e79..39e68e45bb12 100644 --- a/drivers/gpu/drm/drm_bridge.c +++ b/drivers/gpu/drm/drm_bridge.c @@ -27,8 +27,10 @@ #include <linux/mutex.h> #include <drm/drm_atomic_state_helper.h> +#include <drm/drm_debugfs.h> #include <drm/drm_bridge.h> #include <drm/drm_encoder.h> +#include <drm/drm_file.h> #include <drm/drm_of.h> #include <drm/drm_print.h> @@ -1345,6 +1347,50 @@ struct drm_bridge *of_drm_find_bridge(struct device_node *np) EXPORT_SYMBOL(of_drm_find_bridge); #endif +#ifdef CONFIG_DEBUG_FS +static int drm_bridge_chains_info(struct seq_file *m, void *data) +{ + struct drm_debugfs_entry *entry = m->private; + struct drm_device *dev = entry->dev; + struct drm_printer p = drm_seq_file_printer(m); + struct drm_mode_config *config = &dev->mode_config; + struct drm_encoder *encoder; + unsigned int bridge_idx = 0; + + list_for_each_entry(encoder, &config->encoder_list, head) { + struct drm_bridge *bridge; + + drm_printf(&p, "encoder[%u]\n", encoder->base.id); + + drm_for_each_bridge_in_chain(encoder, bridge) { + drm_printf(&p, "\tbridge[%u] type: %u, ops: %#x", + bridge_idx, bridge->type, bridge->ops); + +#ifdef CONFIG_OF + if (bridge->of_node) + drm_printf(&p, ", OF: %pOFfc", bridge->of_node); +#endif + + drm_printf(&p, "\n"); + + bridge_idx++; + } + } + + return 0; +} + +static const struct drm_debugfs_info drm_bridge_debugfs_list[] = { + { "bridge_chains", drm_bridge_chains_info, 0 }, +}; + +void drm_bridge_debugfs_init(struct drm_minor *minor) +{ + drm_debugfs_add_files(minor->dev, drm_bridge_debugfs_list, + ARRAY_SIZE(drm_bridge_debugfs_list)); +} +#endif + MODULE_AUTHOR("Ajay Kumar <ajaykumar.rs@samsung.com>"); MODULE_DESCRIPTION("DRM bridge infrastructure"); MODULE_LICENSE("GPL and additional rights"); diff --git a/drivers/gpu/drm/drm_bridge_connector.c b/drivers/gpu/drm/drm_bridge_connector.c index fc6f16e14f36..1da93d5a1f61 100644 --- a/drivers/gpu/drm/drm_bridge_connector.c +++ b/drivers/gpu/drm/drm_bridge_connector.c @@ -318,6 +318,7 @@ struct drm_connector *drm_bridge_connector_init(struct drm_device *drm, struct i2c_adapter *ddc = NULL; struct drm_bridge *bridge, *panel_bridge = NULL; int connector_type; + int ret; bridge_connector = kzalloc(sizeof(*bridge_connector), GFP_KERNEL); if (!bridge_connector) @@ -368,8 +369,14 @@ struct drm_connector *drm_bridge_connector_init(struct drm_device *drm, return ERR_PTR(-EINVAL); } - drm_connector_init_with_ddc(drm, connector, &drm_bridge_connector_funcs, - connector_type, ddc); + ret = drm_connector_init_with_ddc(drm, connector, + &drm_bridge_connector_funcs, + connector_type, ddc); + if (ret) { + kfree(bridge_connector); + return ERR_PTR(ret); + } + drm_connector_helper_add(connector, &drm_bridge_connector_helper_funcs); if (bridge_connector->bridge_hpd) diff --git a/drivers/gpu/drm/drm_debugfs.c b/drivers/gpu/drm/drm_debugfs.c index a3a488205009..2de43ff3ce0a 100644 --- a/drivers/gpu/drm/drm_debugfs.c +++ b/drivers/gpu/drm/drm_debugfs.c @@ -31,6 +31,7 @@ #include <drm/drm_atomic.h> #include <drm/drm_auth.h> +#include <drm/drm_bridge.h> #include <drm/drm_client.h> #include <drm/drm_debugfs.h> #include <drm/drm_device.h> @@ -274,6 +275,7 @@ int drm_debugfs_init(struct drm_minor *minor, int minor_id, if (drm_drv_uses_atomic_modeset(dev)) { drm_atomic_debugfs_init(minor); + drm_bridge_debugfs_init(minor); } if (drm_core_check_feature(dev, DRIVER_MODESET)) { diff --git a/drivers/gpu/drm/drm_fbdev_dma.c b/drivers/gpu/drm/drm_fbdev_dma.c index 6db168f94290..6c9427bb4053 100644 --- a/drivers/gpu/drm/drm_fbdev_dma.c +++ b/drivers/gpu/drm/drm_fbdev_dma.c @@ -62,9 +62,9 @@ static const struct fb_ops drm_fbdev_dma_fb_ops = { .owner = THIS_MODULE, .fb_open = drm_fbdev_dma_fb_open, .fb_release = drm_fbdev_dma_fb_release, - __FB_DEFAULT_DMA_OPS_RDWR, + __FB_DEFAULT_DMAMEM_OPS_RDWR, DRM_FB_HELPER_DEFAULT_OPS, - __FB_DEFAULT_DMA_OPS_DRAW, + __FB_DEFAULT_DMAMEM_OPS_DRAW, .fb_mmap = drm_fbdev_dma_fb_mmap, .fb_destroy = drm_fbdev_dma_fb_destroy, }; diff --git a/drivers/gpu/drm/drm_fbdev_generic.c b/drivers/gpu/drm/drm_fbdev_generic.c index a0ea042b1526..d647d89764cb 100644 --- a/drivers/gpu/drm/drm_fbdev_generic.c +++ b/drivers/gpu/drm/drm_fbdev_generic.c @@ -34,9 +34,9 @@ static int drm_fbdev_generic_fb_release(struct fb_info *info, int user) return 0; } -FB_GEN_DEFAULT_DEFERRED_SYS_OPS(drm_fbdev_generic, - drm_fb_helper_damage_range, - drm_fb_helper_damage_area); +FB_GEN_DEFAULT_DEFERRED_SYSMEM_OPS(drm_fbdev_generic, + drm_fb_helper_damage_range, + drm_fb_helper_damage_area); static void drm_fbdev_generic_fb_destroy(struct fb_info *info) { diff --git a/drivers/gpu/drm/drm_gem_framebuffer_helper.c b/drivers/gpu/drm/drm_gem_framebuffer_helper.c index b8a615a138cd..3bdb6ba37ff4 100644 --- a/drivers/gpu/drm/drm_gem_framebuffer_helper.c +++ b/drivers/gpu/drm/drm_gem_framebuffer_helper.c @@ -168,8 +168,8 @@ int drm_gem_fb_init_with_funcs(struct drm_device *dev, if (drm_drv_uses_atomic_modeset(dev) && !drm_any_plane_has_format(dev, mode_cmd->pixel_format, mode_cmd->modifier[0])) { - drm_dbg(dev, "Unsupported pixel format %p4cc / modifier 0x%llx\n", - &mode_cmd->pixel_format, mode_cmd->modifier[0]); + drm_dbg_kms(dev, "Unsupported pixel format %p4cc / modifier 0x%llx\n", + &mode_cmd->pixel_format, mode_cmd->modifier[0]); return -EINVAL; } diff --git a/drivers/gpu/drm/drm_mipi_dbi.c b/drivers/gpu/drm/drm_mipi_dbi.c index c871d9f096b8..e90f0bf895b3 100644 --- a/drivers/gpu/drm/drm_mipi_dbi.c +++ b/drivers/gpu/drm/drm_mipi_dbi.c @@ -1140,10 +1140,13 @@ static int mipi_dbi_typec3_command_read(struct mipi_dbi *dbi, u8 *cmd, return -ENOMEM; tr[1].rx_buf = buf; + + spi_bus_lock(spi->controller); gpiod_set_value_cansleep(dbi->dc, 0); spi_message_init_with_transfers(&m, tr, ARRAY_SIZE(tr)); - ret = spi_sync(spi, &m); + ret = spi_sync_locked(spi, &m); + spi_bus_unlock(spi->controller); if (ret) goto err_free; @@ -1177,19 +1180,24 @@ static int mipi_dbi_typec3_command(struct mipi_dbi *dbi, u8 *cmd, MIPI_DBI_DEBUG_COMMAND(*cmd, par, num); + spi_bus_lock(spi->controller); gpiod_set_value_cansleep(dbi->dc, 0); speed_hz = mipi_dbi_spi_cmd_max_speed(spi, 1); ret = mipi_dbi_spi_transfer(spi, speed_hz, 8, cmd, 1); + spi_bus_unlock(spi->controller); if (ret || !num) return ret; if (*cmd == MIPI_DCS_WRITE_MEMORY_START && !dbi->swap_bytes) bpw = 16; + spi_bus_lock(spi->controller); gpiod_set_value_cansleep(dbi->dc, 1); speed_hz = mipi_dbi_spi_cmd_max_speed(spi, num); + ret = mipi_dbi_spi_transfer(spi, speed_hz, bpw, par, num); + spi_bus_unlock(spi->controller); - return mipi_dbi_spi_transfer(spi, speed_hz, bpw, par, num); + return ret; } /** @@ -1271,7 +1279,8 @@ EXPORT_SYMBOL(mipi_dbi_spi_init); * @len: Buffer length * * This SPI transfer helper breaks up the transfer of @buf into chunks which - * the SPI controller driver can handle. + * the SPI controller driver can handle. The SPI bus must be locked when + * calling this. * * Returns: * Zero on success, negative error code on failure. @@ -1305,7 +1314,7 @@ int mipi_dbi_spi_transfer(struct spi_device *spi, u32 speed_hz, buf += chunk; len -= chunk; - ret = spi_sync(spi, &m); + ret = spi_sync_locked(spi, &m); if (ret) return ret; } diff --git a/drivers/gpu/drm/drm_panel.c b/drivers/gpu/drm/drm_panel.c index f634371c717a..e814020bbcd3 100644 --- a/drivers/gpu/drm/drm_panel.c +++ b/drivers/gpu/drm/drm_panel.c @@ -58,6 +58,8 @@ void drm_panel_init(struct drm_panel *panel, struct device *dev, const struct drm_panel_funcs *funcs, int connector_type) { INIT_LIST_HEAD(&panel->list); + INIT_LIST_HEAD(&panel->followers); + mutex_init(&panel->follower_lock); panel->dev = dev; panel->funcs = funcs; panel->connector_type = connector_type; @@ -105,13 +107,38 @@ EXPORT_SYMBOL(drm_panel_remove); */ int drm_panel_prepare(struct drm_panel *panel) { + struct drm_panel_follower *follower; + int ret; + if (!panel) return -EINVAL; - if (panel->funcs && panel->funcs->prepare) - return panel->funcs->prepare(panel); + if (panel->prepared) { + dev_warn(panel->dev, "Skipping prepare of already prepared panel\n"); + return 0; + } + + mutex_lock(&panel->follower_lock); - return 0; + if (panel->funcs && panel->funcs->prepare) { + ret = panel->funcs->prepare(panel); + if (ret < 0) + goto exit; + } + panel->prepared = true; + + list_for_each_entry(follower, &panel->followers, list) { + ret = follower->funcs->panel_prepared(follower); + if (ret < 0) + dev_info(panel->dev, "%ps failed: %d\n", + follower->funcs->panel_prepared, ret); + } + + ret = 0; +exit: + mutex_unlock(&panel->follower_lock); + + return ret; } EXPORT_SYMBOL(drm_panel_prepare); @@ -128,13 +155,38 @@ EXPORT_SYMBOL(drm_panel_prepare); */ int drm_panel_unprepare(struct drm_panel *panel) { + struct drm_panel_follower *follower; + int ret; + if (!panel) return -EINVAL; - if (panel->funcs && panel->funcs->unprepare) - return panel->funcs->unprepare(panel); + if (!panel->prepared) { + dev_warn(panel->dev, "Skipping unprepare of already unprepared panel\n"); + return 0; + } - return 0; + mutex_lock(&panel->follower_lock); + + list_for_each_entry(follower, &panel->followers, list) { + ret = follower->funcs->panel_unpreparing(follower); + if (ret < 0) + dev_info(panel->dev, "%ps failed: %d\n", + follower->funcs->panel_unpreparing, ret); + } + + if (panel->funcs && panel->funcs->unprepare) { + ret = panel->funcs->unprepare(panel); + if (ret < 0) + goto exit; + } + panel->prepared = false; + + ret = 0; +exit: + mutex_unlock(&panel->follower_lock); + + return ret; } EXPORT_SYMBOL(drm_panel_unprepare); @@ -155,11 +207,17 @@ int drm_panel_enable(struct drm_panel *panel) if (!panel) return -EINVAL; + if (panel->enabled) { + dev_warn(panel->dev, "Skipping enable of already enabled panel\n"); + return 0; + } + if (panel->funcs && panel->funcs->enable) { ret = panel->funcs->enable(panel); if (ret < 0) return ret; } + panel->enabled = true; ret = backlight_enable(panel->backlight); if (ret < 0) @@ -187,13 +245,22 @@ int drm_panel_disable(struct drm_panel *panel) if (!panel) return -EINVAL; + if (!panel->enabled) { + dev_warn(panel->dev, "Skipping disable of already disabled panel\n"); + return 0; + } + ret = backlight_disable(panel->backlight); if (ret < 0) DRM_DEV_INFO(panel->dev, "failed to disable backlight: %d\n", ret); - if (panel->funcs && panel->funcs->disable) - return panel->funcs->disable(panel); + if (panel->funcs && panel->funcs->disable) { + ret = panel->funcs->disable(panel); + if (ret < 0) + return ret; + } + panel->enabled = false; return 0; } @@ -305,6 +372,141 @@ int of_drm_get_panel_orientation(const struct device_node *np, EXPORT_SYMBOL(of_drm_get_panel_orientation); #endif +/** + * drm_is_panel_follower() - Check if the device is a panel follower + * @dev: The 'struct device' to check + * + * This checks to see if a device needs to be power sequenced together with + * a panel using the panel follower API. + * At the moment panels can only be followed on device tree enabled systems. + * The "panel" property of the follower points to the panel to be followed. + * + * Return: true if we should be power sequenced with a panel; false otherwise. + */ +bool drm_is_panel_follower(struct device *dev) +{ + /* + * The "panel" property is actually a phandle, but for simplicity we + * don't bother trying to parse it here. We just need to know if the + * property is there. + */ + return of_property_read_bool(dev->of_node, "panel"); +} +EXPORT_SYMBOL(drm_is_panel_follower); + +/** + * drm_panel_add_follower() - Register something to follow panel state. + * @follower_dev: The 'struct device' for the follower. + * @follower: The panel follower descriptor for the follower. + * + * A panel follower is called right after preparing the panel and right before + * unpreparing the panel. It's primary intention is to power on an associated + * touchscreen, though it could be used for any similar devices. Multiple + * devices are allowed the follow the same panel. + * + * If a follower is added to a panel that's already been turned on, the + * follower's prepare callback is called right away. + * + * At the moment panels can only be followed on device tree enabled systems. + * The "panel" property of the follower points to the panel to be followed. + * + * Return: 0 or an error code. Note that -ENODEV means that we detected that + * follower_dev is not actually following a panel. The caller may + * choose to ignore this return value if following a panel is optional. + */ +int drm_panel_add_follower(struct device *follower_dev, + struct drm_panel_follower *follower) +{ + struct device_node *panel_np; + struct drm_panel *panel; + int ret; + + panel_np = of_parse_phandle(follower_dev->of_node, "panel", 0); + if (!panel_np) + return -ENODEV; + + panel = of_drm_find_panel(panel_np); + of_node_put(panel_np); + if (IS_ERR(panel)) + return PTR_ERR(panel); + + get_device(panel->dev); + follower->panel = panel; + + mutex_lock(&panel->follower_lock); + + list_add_tail(&follower->list, &panel->followers); + if (panel->prepared) { + ret = follower->funcs->panel_prepared(follower); + if (ret < 0) + dev_info(panel->dev, "%ps failed: %d\n", + follower->funcs->panel_prepared, ret); + } + + mutex_unlock(&panel->follower_lock); + + return 0; +} +EXPORT_SYMBOL(drm_panel_add_follower); + +/** + * drm_panel_remove_follower() - Reverse drm_panel_add_follower(). + * @follower: The panel follower descriptor for the follower. + * + * Undo drm_panel_add_follower(). This includes calling the follower's + * unprepare function if we're removed from a panel that's currently prepared. + * + * Return: 0 or an error code. + */ +void drm_panel_remove_follower(struct drm_panel_follower *follower) +{ + struct drm_panel *panel = follower->panel; + int ret; + + mutex_lock(&panel->follower_lock); + + if (panel->prepared) { + ret = follower->funcs->panel_unpreparing(follower); + if (ret < 0) + dev_info(panel->dev, "%ps failed: %d\n", + follower->funcs->panel_unpreparing, ret); + } + list_del_init(&follower->list); + + mutex_unlock(&panel->follower_lock); + + put_device(panel->dev); +} +EXPORT_SYMBOL(drm_panel_remove_follower); + +static void drm_panel_remove_follower_void(void *follower) +{ + drm_panel_remove_follower(follower); +} + +/** + * devm_drm_panel_add_follower() - devm version of drm_panel_add_follower() + * @follower_dev: The 'struct device' for the follower. + * @follower: The panel follower descriptor for the follower. + * + * Handles calling drm_panel_remove_follower() using devm on the follower_dev. + * + * Return: 0 or an error code. + */ +int devm_drm_panel_add_follower(struct device *follower_dev, + struct drm_panel_follower *follower) +{ + int ret; + + ret = drm_panel_add_follower(follower_dev, follower); + if (ret) + return ret; + + return devm_add_action_or_reset(follower_dev, + drm_panel_remove_follower_void, follower); +} +EXPORT_SYMBOL(devm_drm_panel_add_follower); + #if IS_REACHABLE(CONFIG_BACKLIGHT_CLASS_DEVICE) /** * drm_panel_of_backlight - use backlight device node for backlight diff --git a/drivers/gpu/drm/drm_plane_helper.c b/drivers/gpu/drm/drm_plane_helper.c index c91e454eba09..5e95089676ff 100644 --- a/drivers/gpu/drm/drm_plane_helper.c +++ b/drivers/gpu/drm/drm_plane_helper.c @@ -40,8 +40,8 @@ /** * DOC: overview * - * This helper library has two parts. The first part has support to implement - * primary plane support on top of the normal CRTC configuration interface. + * This helper library contains helpers to implement primary plane support on + * top of the normal CRTC configuration interface. * Since the legacy &drm_mode_config_funcs.set_config interface ties the primary * plane together with the CRTC state this does not allow userspace to disable * the primary plane itself. The default primary plane only expose XRBG8888 and @@ -51,14 +51,6 @@ * planes, and newly merged drivers must not rely upon these transitional * helpers. * - * The second part also implements transitional helpers which allow drivers to - * gradually switch to the atomic helper infrastructure for plane updates. Once - * that switch is complete drivers shouldn't use these any longer, instead using - * the proper legacy implementations for update and disable plane hooks provided - * by the atomic helpers. - * - * Again drivers are strongly urged to switch to the new interfaces. - * * The plane helpers share the function table structures with other helpers, * specifically also the atomic helpers. See &struct drm_plane_helper_funcs for * the details. diff --git a/drivers/gpu/drm/exynos/Kconfig b/drivers/gpu/drm/exynos/Kconfig index 661b42ad4873..733b109a5095 100644 --- a/drivers/gpu/drm/exynos/Kconfig +++ b/drivers/gpu/drm/exynos/Kconfig @@ -7,7 +7,7 @@ config DRM_EXYNOS select DRM_DISPLAY_HELPER if DRM_EXYNOS_DP select DRM_KMS_HELPER select VIDEOMODE_HELPERS - select FB_DMA_HELPERS if DRM_FBDEV_EMULATION + select FB_DMAMEM_HELPERS if DRM_FBDEV_EMULATION select SND_SOC_HDMI_CODEC if SND_SOC help Choose this option if you have a Samsung SoC Exynos chipset. diff --git a/drivers/gpu/drm/exynos/exynos_drm_fbdev.c b/drivers/gpu/drm/exynos/exynos_drm_fbdev.c index 4ccb385aff52..a379c8ca435a 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_fbdev.c +++ b/drivers/gpu/drm/exynos/exynos_drm_fbdev.c @@ -49,9 +49,9 @@ static void exynos_drm_fb_destroy(struct fb_info *info) static const struct fb_ops exynos_drm_fb_ops = { .owner = THIS_MODULE, - __FB_DEFAULT_DMA_OPS_RDWR, + __FB_DEFAULT_DMAMEM_OPS_RDWR, DRM_FB_HELPER_DEFAULT_OPS, - __FB_DEFAULT_DMA_OPS_DRAW, + __FB_DEFAULT_DMAMEM_OPS_DRAW, .fb_mmap = exynos_drm_fb_mmap, .fb_destroy = exynos_drm_fb_destroy, }; diff --git a/drivers/gpu/drm/gma500/Kconfig b/drivers/gpu/drm/gma500/Kconfig index cd3d92725ed4..efb4a2dd2f80 100644 --- a/drivers/gpu/drm/gma500/Kconfig +++ b/drivers/gpu/drm/gma500/Kconfig @@ -3,7 +3,7 @@ config DRM_GMA500 tristate "Intel GMA500/600/3600/3650 KMS Framebuffer" depends on DRM && PCI && X86 && MMU select DRM_KMS_HELPER - select FB_IO_HELPERS if DRM_FBDEV_EMULATION + select FB_IOMEM_HELPERS if DRM_FBDEV_EMULATION select I2C select I2C_ALGOBIT # GMA500 depends on ACPI_VIDEO when ACPI is enabled, just like i915 diff --git a/drivers/gpu/drm/gma500/fbdev.c b/drivers/gpu/drm/gma500/fbdev.c index be8f5fbd5260..98b44974d42d 100644 --- a/drivers/gpu/drm/gma500/fbdev.c +++ b/drivers/gpu/drm/gma500/fbdev.c @@ -135,10 +135,10 @@ static void psb_fbdev_fb_destroy(struct fb_info *info) static const struct fb_ops psb_fbdev_fb_ops = { .owner = THIS_MODULE, - __FB_DEFAULT_IO_OPS_RDWR, + __FB_DEFAULT_IOMEM_OPS_RDWR, DRM_FB_HELPER_DEFAULT_OPS, .fb_setcolreg = psb_fbdev_fb_setcolreg, - __FB_DEFAULT_IO_OPS_DRAW, + __FB_DEFAULT_IOMEM_OPS_DRAW, .fb_mmap = psb_fbdev_fb_mmap, .fb_destroy = psb_fbdev_fb_destroy, }; diff --git a/drivers/gpu/drm/i915/Kconfig b/drivers/gpu/drm/i915/Kconfig index 01b5a8272a27..ce397a8797f7 100644 --- a/drivers/gpu/drm/i915/Kconfig +++ b/drivers/gpu/drm/i915/Kconfig @@ -17,7 +17,7 @@ config DRM_I915 select DRM_KMS_HELPER select DRM_PANEL select DRM_MIPI_DSI - select FB_IO_HELPERS if DRM_FBDEV_EMULATION + select FB_IOMEM_HELPERS if DRM_FBDEV_EMULATION select RELAY select I2C select I2C_ALGOBIT diff --git a/drivers/gpu/drm/i915/display/intel_fbdev.c b/drivers/gpu/drm/i915/display/intel_fbdev.c index 1cc0ddc6a310..f1439827bc80 100644 --- a/drivers/gpu/drm/i915/display/intel_fbdev.c +++ b/drivers/gpu/drm/i915/display/intel_fbdev.c @@ -85,9 +85,9 @@ static void intel_fbdev_invalidate(struct intel_fbdev *ifbdev) intel_frontbuffer_invalidate(to_frontbuffer(ifbdev), ORIGIN_CPU); } -FB_GEN_DEFAULT_DEFERRED_IO_OPS(intel_fbdev, - drm_fb_helper_damage_range, - drm_fb_helper_damage_area) +FB_GEN_DEFAULT_DEFERRED_IOMEM_OPS(intel_fbdev, + drm_fb_helper_damage_range, + drm_fb_helper_damage_area) static int intel_fbdev_set_par(struct fb_info *info) { diff --git a/drivers/gpu/drm/i915/gem/i915_gem_wait.c b/drivers/gpu/drm/i915/gem/i915_gem_wait.c index 4a33ad2d122b..d4b918fb11ce 100644 --- a/drivers/gpu/drm/i915/gem/i915_gem_wait.c +++ b/drivers/gpu/drm/i915/gem/i915_gem_wait.c @@ -186,7 +186,7 @@ i915_gem_object_wait(struct drm_i915_gem_object *obj, static inline unsigned long nsecs_to_jiffies_timeout(const u64 n) { /* nsecs_to_jiffies64() does not guard against overflow */ - if (NSEC_PER_SEC % HZ && + if ((NSEC_PER_SEC % HZ) != 0 && div_u64(n, NSEC_PER_SEC) >= MAX_JIFFY_OFFSET / HZ) return MAX_JIFFY_OFFSET; diff --git a/drivers/gpu/drm/i915/i915_request.c b/drivers/gpu/drm/i915/i915_request.c index 894068bb37b6..32323bb801a1 100644 --- a/drivers/gpu/drm/i915/i915_request.c +++ b/drivers/gpu/drm/i915/i915_request.c @@ -1220,7 +1220,7 @@ emit_semaphore_wait(struct i915_request *to, /* * If this or its dependents are waiting on an external fence * that may fail catastrophically, then we want to avoid using - * sempahores as they bypass the fence signaling metadata, and we + * semaphores as they bypass the fence signaling metadata, and we * lose the fence->error propagation. */ if (from->sched.flags & I915_SCHED_HAS_EXTERNAL_CHAIN) diff --git a/drivers/gpu/drm/imx/dcss/dcss-drv.c b/drivers/gpu/drm/imx/dcss/dcss-drv.c index 4f2291610139..c68b0d93ae9e 100644 --- a/drivers/gpu/drm/imx/dcss/dcss-drv.c +++ b/drivers/gpu/drm/imx/dcss/dcss-drv.c @@ -66,6 +66,7 @@ static int dcss_drv_platform_probe(struct platform_device *pdev) mdrv->kms = dcss_kms_attach(mdrv->dcss); if (IS_ERR(mdrv->kms)) { err = PTR_ERR(mdrv->kms); + dev_err_probe(dev, err, "Failed to initialize KMS\n"); goto dcss_shutoff; } diff --git a/drivers/gpu/drm/imx/ipuv3/ipuv3-plane.c b/drivers/gpu/drm/imx/ipuv3/ipuv3-plane.c index 80142d9a4a55..dade8b59feae 100644 --- a/drivers/gpu/drm/imx/ipuv3/ipuv3-plane.c +++ b/drivers/gpu/drm/imx/ipuv3/ipuv3-plane.c @@ -618,6 +618,7 @@ static void ipu_plane_atomic_update(struct drm_plane *plane, width = ipu_src_rect_width(new_state); else width = drm_rect_width(&new_state->src) >> 16; + height = drm_rect_height(&new_state->src) >> 16; eba = drm_plane_state_to_eba(new_state, 0); @@ -628,9 +629,9 @@ static void ipu_plane_atomic_update(struct drm_plane *plane, if (ipu_state->use_pre) { axi_id = ipu_chan_assign_axi_id(ipu_plane->dma); ipu_prg_channel_configure(ipu_plane->ipu_ch, axi_id, width, - drm_rect_height(&new_state->src) >> 16, - fb->pitches[0], fb->format->format, - fb->modifier, &eba); + height, fb->pitches[0], + fb->format->format, fb->modifier, + &eba); } if (!old_state->fb || @@ -684,7 +685,6 @@ static void ipu_plane_atomic_update(struct drm_plane *plane, ipu_dmfc_config_wait4eot(ipu_plane->dmfc, width); - height = drm_rect_height(&new_state->src) >> 16; info = drm_format_info(fb->format->format); ipu_calculate_bursts(width, info->cpp[0], fb->pitches[0], &burstsize, &num_bursts); @@ -747,8 +747,7 @@ static void ipu_plane_atomic_update(struct drm_plane *plane, ipu_cpmem_set_burstsize(ipu_plane->ipu_ch, 16); ipu_cpmem_zero(ipu_plane->alpha_ch); - ipu_cpmem_set_resolution(ipu_plane->alpha_ch, width, - drm_rect_height(&new_state->src) >> 16); + ipu_cpmem_set_resolution(ipu_plane->alpha_ch, width, height); ipu_cpmem_set_format_passthrough(ipu_plane->alpha_ch, 8); ipu_cpmem_set_high_priority(ipu_plane->alpha_ch); ipu_idmac_set_double_buffer(ipu_plane->alpha_ch, 1); diff --git a/drivers/gpu/drm/msm/Kconfig b/drivers/gpu/drm/msm/Kconfig index a78662bd6273..6309a857ca31 100644 --- a/drivers/gpu/drm/msm/Kconfig +++ b/drivers/gpu/drm/msm/Kconfig @@ -21,7 +21,7 @@ config DRM_MSM select DRM_BRIDGE select DRM_PANEL_BRIDGE select DRM_SCHED - select FB_SYS_HELPERS if DRM_FBDEV_EMULATION + select FB_SYSMEM_HELPERS if DRM_FBDEV_EMULATION select SHMEM select TMPFS select QCOM_SCM diff --git a/drivers/gpu/drm/msm/msm_fbdev.c b/drivers/gpu/drm/msm/msm_fbdev.c index bf1e17dc4550..030bedac632d 100644 --- a/drivers/gpu/drm/msm/msm_fbdev.c +++ b/drivers/gpu/drm/msm/msm_fbdev.c @@ -25,9 +25,9 @@ module_param(fbdev, bool, 0600); * fbdev funcs, to implement legacy fbdev interface on top of drm driver */ -FB_GEN_DEFAULT_DEFERRED_SYS_OPS(msm_fbdev, - drm_fb_helper_damage_range, - drm_fb_helper_damage_area) +FB_GEN_DEFAULT_DEFERRED_SYSMEM_OPS(msm_fbdev, + drm_fb_helper_damage_range, + drm_fb_helper_damage_area) static int msm_fbdev_mmap(struct fb_info *info, struct vm_area_struct *vma) { diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/dp.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/dp.c index 40c8ea43c42f..b8ac66b4a2c4 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/dp.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/dp.c @@ -26,6 +26,8 @@ #include "head.h" #include "ior.h" +#include <drm/display/drm_dp.h> + #include <subdev/bios.h> #include <subdev/bios/init.h> #include <subdev/gpio.h> @@ -634,6 +636,50 @@ nvkm_dp_enable_supported_link_rates(struct nvkm_outp *outp) return outp->dp.rates != 0; } +/* XXX: This is a big fat hack, and this is just drm_dp_read_dpcd_caps() + * converted to work inside nvkm. This is a temporary holdover until we start + * passing the drm_dp_aux device through NVKM + */ +static int +nvkm_dp_read_dpcd_caps(struct nvkm_outp *outp) +{ + struct nvkm_i2c_aux *aux = outp->dp.aux; + u8 dpcd_ext[DP_RECEIVER_CAP_SIZE]; + int ret; + + ret = nvkm_rdaux(aux, DPCD_RC00_DPCD_REV, outp->dp.dpcd, DP_RECEIVER_CAP_SIZE); + if (ret < 0) + return ret; + + /* + * Prior to DP1.3 the bit represented by + * DP_EXTENDED_RECEIVER_CAP_FIELD_PRESENT was reserved. + * If it is set DP_DPCD_REV at 0000h could be at a value less than + * the true capability of the panel. The only way to check is to + * then compare 0000h and 2200h. + */ + if (!(outp->dp.dpcd[DP_TRAINING_AUX_RD_INTERVAL] & + DP_EXTENDED_RECEIVER_CAP_FIELD_PRESENT)) + return 0; + + ret = nvkm_rdaux(aux, DP_DP13_DPCD_REV, dpcd_ext, sizeof(dpcd_ext)); + if (ret < 0) + return ret; + + if (outp->dp.dpcd[DP_DPCD_REV] > dpcd_ext[DP_DPCD_REV]) { + OUTP_DBG(outp, "Extended DPCD rev less than base DPCD rev (%d > %d)\n", + outp->dp.dpcd[DP_DPCD_REV], dpcd_ext[DP_DPCD_REV]); + return 0; + } + + if (!memcmp(outp->dp.dpcd, dpcd_ext, sizeof(dpcd_ext))) + return 0; + + memcpy(outp->dp.dpcd, dpcd_ext, sizeof(dpcd_ext)); + + return 0; +} + void nvkm_dp_enable(struct nvkm_outp *outp, bool auxpwr) { @@ -689,7 +735,7 @@ nvkm_dp_enable(struct nvkm_outp *outp, bool auxpwr) memset(outp->dp.lttpr, 0x00, sizeof(outp->dp.lttpr)); } - if (!nvkm_rdaux(aux, DPCD_RC00_DPCD_REV, outp->dp.dpcd, sizeof(outp->dp.dpcd))) { + if (!nvkm_dp_read_dpcd_caps(outp)) { const u8 rates[] = { 0x1e, 0x14, 0x0a, 0x06, 0 }; const u8 *rate; int rate_max; diff --git a/drivers/gpu/drm/omapdrm/Kconfig b/drivers/gpu/drm/omapdrm/Kconfig index d3c4877e465c..b715301ec79f 100644 --- a/drivers/gpu/drm/omapdrm/Kconfig +++ b/drivers/gpu/drm/omapdrm/Kconfig @@ -4,7 +4,7 @@ config DRM_OMAP depends on DRM && OF depends on ARCH_OMAP2PLUS select DRM_KMS_HELPER - select FB_DMA_HELPERS if DRM_FBDEV_EMULATION + select FB_DMAMEM_HELPERS if DRM_FBDEV_EMULATION select VIDEOMODE_HELPERS select HDMI default n diff --git a/drivers/gpu/drm/omapdrm/omap_fbdev.c b/drivers/gpu/drm/omapdrm/omap_fbdev.c index 5b33c789e17a..6b08b137af1a 100644 --- a/drivers/gpu/drm/omapdrm/omap_fbdev.c +++ b/drivers/gpu/drm/omapdrm/omap_fbdev.c @@ -106,13 +106,13 @@ static void omap_fbdev_fb_destroy(struct fb_info *info) static const struct fb_ops omap_fb_ops = { .owner = THIS_MODULE, - __FB_DEFAULT_DMA_OPS_RDWR, + __FB_DEFAULT_DMAMEM_OPS_RDWR, .fb_check_var = drm_fb_helper_check_var, .fb_set_par = drm_fb_helper_set_par, .fb_setcmap = drm_fb_helper_setcmap, .fb_blank = drm_fb_helper_blank, .fb_pan_display = omap_fbdev_pan_display, - __FB_DEFAULT_DMA_OPS_DRAW, + __FB_DEFAULT_DMAMEM_OPS_DRAW, .fb_ioctl = drm_fb_helper_ioctl, .fb_mmap = omap_fbdev_fb_mmap, .fb_destroy = omap_fbdev_fb_destroy, diff --git a/drivers/gpu/drm/panel/Kconfig b/drivers/gpu/drm/panel/Kconfig index 05f9e800e448..869e535faefa 100644 --- a/drivers/gpu/drm/panel/Kconfig +++ b/drivers/gpu/drm/panel/Kconfig @@ -734,6 +734,17 @@ config DRM_PANEL_SONY_TULIP_TRULY_NT35521 NT35521 1280x720 video mode panel as found on Sony Xperia M4 Aqua phone. +config DRM_PANEL_STARTEK_KD070FHFID015 + tristate "STARTEK KD070FHFID015 panel" + depends on OF + depends on DRM_MIPI_DSI + depends on BACKLIGHT_CLASS_DEVICE + help + Say Y here if you want to enable support for STARTEK KD070FHFID015 DSI panel + based on RENESAS-R69429 controller. The pannel is a 7-inch TFT LCD display + with a resolution of 1024 x 600 pixels. It provides a MIPI DSI interface to + the host, a built-in LED backlight and touch controller. + config DRM_PANEL_TDO_TL070WSH30 tristate "TDO TL070WSH30 DSI panel" depends on OF @@ -799,6 +810,8 @@ config DRM_PANEL_VISIONOX_R66451 depends on OF depends on DRM_MIPI_DSI depends on BACKLIGHT_CLASS_DEVICE + select DRM_DISPLAY_DP_HELPER + select DRM_DISPLAY_HELPER help Say Y here if you want to enable support for Visionox R66451 1080x2340 AMOLED DSI panel. diff --git a/drivers/gpu/drm/panel/Makefile b/drivers/gpu/drm/panel/Makefile index 499e38244253..433e93d57949 100644 --- a/drivers/gpu/drm/panel/Makefile +++ b/drivers/gpu/drm/panel/Makefile @@ -74,6 +74,7 @@ obj-$(CONFIG_DRM_PANEL_SITRONIX_ST7789V) += panel-sitronix-st7789v.o obj-$(CONFIG_DRM_PANEL_SONY_ACX565AKM) += panel-sony-acx565akm.o obj-$(CONFIG_DRM_PANEL_SONY_TD4353_JDI) += panel-sony-td4353-jdi.o obj-$(CONFIG_DRM_PANEL_SONY_TULIP_TRULY_NT35521) += panel-sony-tulip-truly-nt35521.o +obj-$(CONFIG_DRM_PANEL_STARTEK_KD070FHFID015) += panel-startek-kd070fhfid015.o obj-$(CONFIG_DRM_PANEL_TDO_TL070WSH30) += panel-tdo-tl070wsh30.o obj-$(CONFIG_DRM_PANEL_TPO_TD028TTEC1) += panel-tpo-td028ttec1.o obj-$(CONFIG_DRM_PANEL_TPO_TD043MTEA1) += panel-tpo-td043mtea1.o diff --git a/drivers/gpu/drm/panel/panel-edp.c b/drivers/gpu/drm/panel/panel-edp.c index df7e3cff004c..feb665df35a1 100644 --- a/drivers/gpu/drm/panel/panel-edp.c +++ b/drivers/gpu/drm/panel/panel-edp.c @@ -1890,6 +1890,7 @@ static const struct edp_panel_entry edp_panels[] = { EDP_PANEL_ENTRY('C', 'M', 'N', 0x1153, &delay_200_500_e80_d50, "N116BGE-EA2"), EDP_PANEL_ENTRY('C', 'M', 'N', 0x1154, &delay_200_500_e80_d50, "N116BCA-EA2"), EDP_PANEL_ENTRY('C', 'M', 'N', 0x1247, &delay_200_500_e80_d50, "N120ACA-EA1"), + EDP_PANEL_ENTRY('C', 'M', 'N', 0x14d4, &delay_200_500_e80_d50, "N140HCA-EAC"), EDP_PANEL_ENTRY('I', 'V', 'O', 0x057d, &delay_200_500_e200, "R140NWF5 RH"), EDP_PANEL_ENTRY('I', 'V', 'O', 0x854a, &delay_200_500_p2e100, "M133NW4J"), diff --git a/drivers/gpu/drm/panel/panel-ilitek-ili9881c.c b/drivers/gpu/drm/panel/panel-ilitek-ili9881c.c index 72dbb8184280..7838947a1bf3 100644 --- a/drivers/gpu/drm/panel/panel-ilitek-ili9881c.c +++ b/drivers/gpu/drm/panel/panel-ilitek-ili9881c.c @@ -455,6 +455,174 @@ static const struct ili9881c_instr k101_im2byl02_init[] = { ILI9881C_COMMAND_INSTR(0xD3, 0x3F), /* VN0 */ }; +static const struct ili9881c_instr tl050hdv35_init[] = { + ILI9881C_SWITCH_PAGE_INSTR(3), + ILI9881C_COMMAND_INSTR(0x01, 0x00), + ILI9881C_COMMAND_INSTR(0x02, 0x00), + ILI9881C_COMMAND_INSTR(0x03, 0x73), + ILI9881C_COMMAND_INSTR(0x04, 0x00), + ILI9881C_COMMAND_INSTR(0x05, 0x00), + ILI9881C_COMMAND_INSTR(0x06, 0x0a), + ILI9881C_COMMAND_INSTR(0x07, 0x00), + ILI9881C_COMMAND_INSTR(0x08, 0x00), + ILI9881C_COMMAND_INSTR(0x09, 0x01), + ILI9881C_COMMAND_INSTR(0x0a, 0x00), + ILI9881C_COMMAND_INSTR(0x0b, 0x00), + ILI9881C_COMMAND_INSTR(0x0c, 0x01), + ILI9881C_COMMAND_INSTR(0x0d, 0x00), + ILI9881C_COMMAND_INSTR(0x0e, 0x00), + ILI9881C_COMMAND_INSTR(0x0f, 0x1d), + ILI9881C_COMMAND_INSTR(0x10, 0x1d), + ILI9881C_COMMAND_INSTR(0x15, 0x00), + ILI9881C_COMMAND_INSTR(0x16, 0x00), + ILI9881C_COMMAND_INSTR(0x17, 0x00), + ILI9881C_COMMAND_INSTR(0x18, 0x00), + ILI9881C_COMMAND_INSTR(0x19, 0x00), + ILI9881C_COMMAND_INSTR(0x1a, 0x00), + ILI9881C_COMMAND_INSTR(0x1b, 0x00), + ILI9881C_COMMAND_INSTR(0x1c, 0x00), + ILI9881C_COMMAND_INSTR(0x1d, 0x00), + ILI9881C_COMMAND_INSTR(0x1e, 0x40), + ILI9881C_COMMAND_INSTR(0x1f, 0x80), + ILI9881C_COMMAND_INSTR(0x20, 0x06), + ILI9881C_COMMAND_INSTR(0x21, 0x02), + ILI9881C_COMMAND_INSTR(0x28, 0x33), + ILI9881C_COMMAND_INSTR(0x29, 0x03), + ILI9881C_COMMAND_INSTR(0x2a, 0x00), + ILI9881C_COMMAND_INSTR(0x2b, 0x00), + ILI9881C_COMMAND_INSTR(0x2c, 0x00), + ILI9881C_COMMAND_INSTR(0x2d, 0x00), + ILI9881C_COMMAND_INSTR(0x2e, 0x00), + ILI9881C_COMMAND_INSTR(0x2f, 0x00), + ILI9881C_COMMAND_INSTR(0x35, 0x00), + ILI9881C_COMMAND_INSTR(0x36, 0x00), + ILI9881C_COMMAND_INSTR(0x37, 0x00), + ILI9881C_COMMAND_INSTR(0x38, 0x3C), + ILI9881C_COMMAND_INSTR(0x39, 0x00), + ILI9881C_COMMAND_INSTR(0x3a, 0x40), + ILI9881C_COMMAND_INSTR(0x3b, 0x40), + ILI9881C_COMMAND_INSTR(0x3c, 0x00), + ILI9881C_COMMAND_INSTR(0x3d, 0x00), + ILI9881C_COMMAND_INSTR(0x3e, 0x00), + ILI9881C_COMMAND_INSTR(0x3f, 0x00), + ILI9881C_COMMAND_INSTR(0x40, 0x00), + ILI9881C_COMMAND_INSTR(0x41, 0x00), + ILI9881C_COMMAND_INSTR(0x42, 0x00), + ILI9881C_COMMAND_INSTR(0x43, 0x00), + ILI9881C_COMMAND_INSTR(0x44, 0x00), + ILI9881C_COMMAND_INSTR(0x55, 0xab), + ILI9881C_COMMAND_INSTR(0x5a, 0x89), + ILI9881C_COMMAND_INSTR(0x5b, 0xab), + ILI9881C_COMMAND_INSTR(0x5c, 0xcd), + ILI9881C_COMMAND_INSTR(0x5d, 0xef), + ILI9881C_COMMAND_INSTR(0x5e, 0x11), + ILI9881C_COMMAND_INSTR(0x5f, 0x01), + ILI9881C_COMMAND_INSTR(0x60, 0x00), + ILI9881C_COMMAND_INSTR(0x61, 0x15), + ILI9881C_COMMAND_INSTR(0x62, 0x14), + ILI9881C_COMMAND_INSTR(0x63, 0x0e), + ILI9881C_COMMAND_INSTR(0x64, 0x0f), + ILI9881C_COMMAND_INSTR(0x65, 0x0c), + ILI9881C_COMMAND_INSTR(0x66, 0x0d), + ILI9881C_COMMAND_INSTR(0x67, 0x06), + ILI9881C_COMMAND_INSTR(0x68, 0x02), + ILI9881C_COMMAND_INSTR(0x69, 0x07), + ILI9881C_COMMAND_INSTR(0x6a, 0x02), + ILI9881C_COMMAND_INSTR(0x6b, 0x02), + ILI9881C_COMMAND_INSTR(0x6c, 0x02), + ILI9881C_COMMAND_INSTR(0x6d, 0x02), + ILI9881C_COMMAND_INSTR(0x6e, 0x02), + ILI9881C_COMMAND_INSTR(0x6f, 0x02), + ILI9881C_COMMAND_INSTR(0x70, 0x02), + ILI9881C_COMMAND_INSTR(0x71, 0x02), + ILI9881C_COMMAND_INSTR(0x72, 0x02), + ILI9881C_COMMAND_INSTR(0x73, 0x02), + ILI9881C_COMMAND_INSTR(0x74, 0x02), + ILI9881C_COMMAND_INSTR(0x75, 0x01), + ILI9881C_COMMAND_INSTR(0x76, 0x00), + ILI9881C_COMMAND_INSTR(0x77, 0x14), + ILI9881C_COMMAND_INSTR(0x78, 0x15), + ILI9881C_COMMAND_INSTR(0x79, 0x0e), + ILI9881C_COMMAND_INSTR(0x7a, 0x0f), + ILI9881C_COMMAND_INSTR(0x7b, 0x0c), + ILI9881C_COMMAND_INSTR(0x7c, 0x0d), + ILI9881C_COMMAND_INSTR(0x7d, 0x06), + ILI9881C_COMMAND_INSTR(0x7e, 0x02), + ILI9881C_COMMAND_INSTR(0x7f, 0x07), + ILI9881C_COMMAND_INSTR(0x88, 0x02), + ILI9881C_COMMAND_INSTR(0x89, 0x02), + ILI9881C_COMMAND_INSTR(0x8A, 0x02), + ILI9881C_SWITCH_PAGE_INSTR(4), + ILI9881C_COMMAND_INSTR(0x38, 0x01), + ILI9881C_COMMAND_INSTR(0x39, 0x00), + ILI9881C_COMMAND_INSTR(0x6c, 0x15), + ILI9881C_COMMAND_INSTR(0x6e, 0x2b), + ILI9881C_COMMAND_INSTR(0x6f, 0x33), + ILI9881C_COMMAND_INSTR(0x8d, 0x18), + ILI9881C_COMMAND_INSTR(0x87, 0xba), + ILI9881C_COMMAND_INSTR(0x26, 0x76), + ILI9881C_COMMAND_INSTR(0xb2, 0xd1), + ILI9881C_COMMAND_INSTR(0xb5, 0x06), + ILI9881C_COMMAND_INSTR(0x3a, 0x24), + ILI9881C_COMMAND_INSTR(0x35, 0x1f), + ILI9881C_COMMAND_INSTR(0x33, 0x14), + ILI9881C_COMMAND_INSTR(0x3b, 0x98), + ILI9881C_SWITCH_PAGE_INSTR(1), + ILI9881C_COMMAND_INSTR(0x22, 0x0a), + ILI9881C_COMMAND_INSTR(0x31, 0x00), + ILI9881C_COMMAND_INSTR(0x40, 0x33), + ILI9881C_COMMAND_INSTR(0x53, 0xa2), + ILI9881C_COMMAND_INSTR(0x55, 0x92), + ILI9881C_COMMAND_INSTR(0x50, 0x96), + ILI9881C_COMMAND_INSTR(0x51, 0x96), + ILI9881C_COMMAND_INSTR(0x60, 0x22), + ILI9881C_COMMAND_INSTR(0x61, 0x00), + ILI9881C_COMMAND_INSTR(0x62, 0x19), + ILI9881C_COMMAND_INSTR(0x63, 0x00), + ILI9881C_COMMAND_INSTR(0xa0, 0x08), + ILI9881C_COMMAND_INSTR(0xa1, 0x11), + ILI9881C_COMMAND_INSTR(0xa2, 0x19), + ILI9881C_COMMAND_INSTR(0xa3, 0x0d), + ILI9881C_COMMAND_INSTR(0xa4, 0x0d), + ILI9881C_COMMAND_INSTR(0xa5, 0x1e), + ILI9881C_COMMAND_INSTR(0xa6, 0x14), + ILI9881C_COMMAND_INSTR(0xa7, 0x17), + ILI9881C_COMMAND_INSTR(0xa8, 0x4f), + ILI9881C_COMMAND_INSTR(0xa9, 0x1a), + ILI9881C_COMMAND_INSTR(0xaa, 0x27), + ILI9881C_COMMAND_INSTR(0xab, 0x49), + ILI9881C_COMMAND_INSTR(0xac, 0x1a), + ILI9881C_COMMAND_INSTR(0xad, 0x18), + ILI9881C_COMMAND_INSTR(0xae, 0x4c), + ILI9881C_COMMAND_INSTR(0xaf, 0x22), + ILI9881C_COMMAND_INSTR(0xb0, 0x27), + ILI9881C_COMMAND_INSTR(0xb1, 0x4b), + ILI9881C_COMMAND_INSTR(0xb2, 0x60), + ILI9881C_COMMAND_INSTR(0xb3, 0x39), + ILI9881C_COMMAND_INSTR(0xc0, 0x08), + ILI9881C_COMMAND_INSTR(0xc1, 0x11), + ILI9881C_COMMAND_INSTR(0xc2, 0x19), + ILI9881C_COMMAND_INSTR(0xc3, 0x0d), + ILI9881C_COMMAND_INSTR(0xc4, 0x0d), + ILI9881C_COMMAND_INSTR(0xc5, 0x1e), + ILI9881C_COMMAND_INSTR(0xc6, 0x14), + ILI9881C_COMMAND_INSTR(0xc7, 0x17), + ILI9881C_COMMAND_INSTR(0xc8, 0x4f), + ILI9881C_COMMAND_INSTR(0xc9, 0x1a), + ILI9881C_COMMAND_INSTR(0xca, 0x27), + ILI9881C_COMMAND_INSTR(0xcb, 0x49), + ILI9881C_COMMAND_INSTR(0xcc, 0x1a), + ILI9881C_COMMAND_INSTR(0xcd, 0x18), + ILI9881C_COMMAND_INSTR(0xce, 0x4c), + ILI9881C_COMMAND_INSTR(0xcf, 0x33), + ILI9881C_COMMAND_INSTR(0xd0, 0x27), + ILI9881C_COMMAND_INSTR(0xd1, 0x4b), + ILI9881C_COMMAND_INSTR(0xd2, 0x60), + ILI9881C_COMMAND_INSTR(0xd3, 0x39), + ILI9881C_SWITCH_PAGE_INSTR(0), + ILI9881C_COMMAND_INSTR(0x36, 0x03), +}; + static const struct ili9881c_instr w552946ab_init[] = { ILI9881C_SWITCH_PAGE_INSTR(3), ILI9881C_COMMAND_INSTR(0x01, 0x00), @@ -812,6 +980,23 @@ static const struct drm_display_mode k101_im2byl02_default_mode = { .height_mm = 217, }; +static const struct drm_display_mode tl050hdv35_default_mode = { + .clock = 59400, + + .hdisplay = 720, + .hsync_start = 720 + 18, + .hsync_end = 720 + 18 + 3, + .htotal = 720 + 18 + 3 + 20, + + .vdisplay = 1280, + .vsync_start = 1280 + 26, + .vsync_end = 1280 + 26 + 6, + .vtotal = 1280 + 26 + 6 + 28, + + .width_mm = 62, + .height_mm = 110, +}; + static const struct drm_display_mode w552946aba_default_mode = { .clock = 64000, @@ -944,6 +1129,14 @@ static const struct ili9881c_desc k101_im2byl02_desc = { .mode_flags = MIPI_DSI_MODE_VIDEO_SYNC_PULSE, }; +static const struct ili9881c_desc tl050hdv35_desc = { + .init = tl050hdv35_init, + .init_length = ARRAY_SIZE(tl050hdv35_init), + .mode = &tl050hdv35_default_mode, + .mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_SYNC_PULSE | + MIPI_DSI_MODE_LPM, +}; + static const struct ili9881c_desc w552946aba_desc = { .init = w552946ab_init, .init_length = ARRAY_SIZE(w552946ab_init), @@ -955,6 +1148,7 @@ static const struct ili9881c_desc w552946aba_desc = { static const struct of_device_id ili9881c_of_match[] = { { .compatible = "bananapi,lhr050h41", .data = &lhr050h41_desc }, { .compatible = "feixin,k101-im2byl02", .data = &k101_im2byl02_desc }, + { .compatible = "tdo,tl050hdv35", .data = &tl050hdv35_desc }, { .compatible = "wanchanglong,w552946aba", .data = &w552946aba_desc }, { } }; diff --git a/drivers/gpu/drm/panel/panel-simple.c b/drivers/gpu/drm/panel/panel-simple.c index 4badda6570d5..72cef64441a6 100644 --- a/drivers/gpu/drm/panel/panel-simple.c +++ b/drivers/gpu/drm/panel/panel-simple.c @@ -1185,7 +1185,9 @@ static const struct panel_desc auo_t215hvn01 = { .delay = { .disable = 5, .unprepare = 1000, - } + }, + .bus_format = MEDIA_BUS_FMT_RGB888_1X7X4_SPWG, + .connector_type = DRM_MODE_CONNECTOR_LVDS, }; static const struct drm_display_mode avic_tm070ddh03_mode = { @@ -2374,6 +2376,37 @@ static const struct panel_desc innolux_g121x1_l03 = { }, }; +static const struct display_timing innolux_g156hce_l01_timings = { + .pixelclock = { 120000000, 144000000, 150000000 }, + .hactive = { 1920, 1920, 1920 }, + .hfront_porch = { 80, 90, 100 }, + .hback_porch = { 80, 90, 100 }, + .hsync_len = { 20, 30, 30 }, + .vactive = { 1080, 1080, 1080 }, + .vfront_porch = { 3, 10, 20 }, + .vback_porch = { 3, 10, 20 }, + .vsync_len = { 4, 10, 10 }, +}; + +static const struct panel_desc innolux_g156hce_l01 = { + .timings = &innolux_g156hce_l01_timings, + .num_timings = 1, + .bpc = 8, + .size = { + .width = 344, + .height = 194, + }, + .delay = { + .prepare = 1, /* T1+T2 */ + .enable = 450, /* T5 */ + .disable = 200, /* T6 */ + .unprepare = 10, /* T3+T7 */ + }, + .bus_format = MEDIA_BUS_FMT_RGB888_1X7X4_SPWG, + .bus_flags = DRM_BUS_FLAG_DE_HIGH, + .connector_type = DRM_MODE_CONNECTOR_LVDS, +}; + static const struct drm_display_mode innolux_n156bge_l21_mode = { .clock = 69300, .hdisplay = 1366, @@ -3205,6 +3238,7 @@ static const struct drm_display_mode powertip_ph800480t013_idf02_mode = { static const struct panel_desc powertip_ph800480t013_idf02 = { .modes = &powertip_ph800480t013_idf02_mode, .num_modes = 1, + .bpc = 8, .size = { .width = 152, .height = 91, @@ -4240,6 +4274,9 @@ static const struct of_device_id platform_of_match[] = { .compatible = "innolux,g121x1-l03", .data = &innolux_g121x1_l03, }, { + .compatible = "innolux,g156hce-l01", + .data = &innolux_g156hce_l01, + }, { .compatible = "innolux,n156bge-l21", .data = &innolux_n156bge_l21, }, { @@ -4455,13 +4492,13 @@ MODULE_DEVICE_TABLE(of, platform_of_match); static int panel_simple_platform_probe(struct platform_device *pdev) { - const struct of_device_id *id; + const struct panel_desc *desc; - id = of_match_node(platform_of_match, pdev->dev.of_node); - if (!id) + desc = of_device_get_match_data(&pdev->dev); + if (!desc) return -ENODEV; - return panel_simple_probe(&pdev->dev, id->data); + return panel_simple_probe(&pdev->dev, desc); } static void panel_simple_platform_remove(struct platform_device *pdev) @@ -4732,15 +4769,12 @@ MODULE_DEVICE_TABLE(of, dsi_of_match); static int panel_simple_dsi_probe(struct mipi_dsi_device *dsi) { const struct panel_desc_dsi *desc; - const struct of_device_id *id; int err; - id = of_match_node(dsi_of_match, dsi->dev.of_node); - if (!id) + desc = of_device_get_match_data(&dsi->dev); + if (!desc) return -ENODEV; - desc = id->data; - err = panel_simple_probe(&dsi->dev, &desc->desc); if (err < 0) return err; diff --git a/drivers/gpu/drm/panel/panel-sitronix-st7789v.c b/drivers/gpu/drm/panel/panel-sitronix-st7789v.c index bbc4569cbcdc..dc010d87a9ef 100644 --- a/drivers/gpu/drm/panel/panel-sitronix-st7789v.c +++ b/drivers/gpu/drm/panel/panel-sitronix-st7789v.c @@ -10,14 +10,12 @@ #include <linux/spi/spi.h> #include <video/mipi_display.h> +#include <linux/media-bus-format.h> #include <drm/drm_device.h> #include <drm/drm_modes.h> #include <drm/drm_panel.h> -#define ST7789V_COLMOD_RGB_FMT_18BITS (6 << 4) -#define ST7789V_COLMOD_CTRL_FMT_18BITS (6 << 0) - #define ST7789V_RAMCTRL_CMD 0xb0 #define ST7789V_RAMCTRL_RM_RGB BIT(4) #define ST7789V_RAMCTRL_DM_RGB BIT(0) @@ -29,7 +27,8 @@ #define ST7789V_RGBCTRL_RCM(n) (((n) & 3) << 5) #define ST7789V_RGBCTRL_VSYNC_HIGH BIT(3) #define ST7789V_RGBCTRL_HSYNC_HIGH BIT(2) -#define ST7789V_RGBCTRL_PCLK_HIGH BIT(1) +#define ST7789V_RGBCTRL_PCLK_FALLING BIT(1) +#define ST7789V_RGBCTRL_DE_LOW BIT(0) #define ST7789V_RGBCTRL_VBP(n) ((n) & 0x7f) #define ST7789V_RGBCTRL_HBP(n) ((n) & 0x1f) @@ -111,8 +110,19 @@ return val; \ } while (0) +#define ST7789V_IDS { 0x85, 0x85, 0x52 } +#define ST7789V_IDS_SIZE 3 + +struct st7789_panel_info { + const struct drm_display_mode *mode; + u32 bus_format; + u32 bus_flags; + bool invert_mode; +}; + struct st7789v { struct drm_panel panel; + const struct st7789_panel_info *info; struct spi_device *spi; struct gpio_desc *reset; struct regulator *power; @@ -132,17 +142,12 @@ static int st7789v_spi_write(struct st7789v *ctx, enum st7789v_prefix prefix, u8 data) { struct spi_transfer xfer = { }; - struct spi_message msg; u16 txbuf = ((prefix & 1) << 8) | data; - spi_message_init(&msg); - xfer.tx_buf = &txbuf; - xfer.bits_per_word = 9; xfer.len = sizeof(txbuf); - spi_message_add_tail(&xfer, &msg); - return spi_sync(ctx->spi, &msg); + return spi_sync_transfer(ctx->spi, &xfer, 1); } static int st7789v_write_command(struct st7789v *ctx, u8 cmd) @@ -155,6 +160,76 @@ static int st7789v_write_data(struct st7789v *ctx, u8 cmd) return st7789v_spi_write(ctx, ST7789V_DATA, cmd); } +static int st7789v_read_data(struct st7789v *ctx, u8 cmd, u8 *buf, + unsigned int len) +{ + struct spi_transfer xfer[2] = { }; + struct spi_message msg; + u16 txbuf = ((ST7789V_COMMAND & 1) << 8) | cmd; + u16 rxbuf[4] = {}; + u8 bit9 = 0; + int ret, i; + + switch (len) { + case 1: + case 3: + case 4: + break; + default: + return -EOPNOTSUPP; + } + + spi_message_init(&msg); + + xfer[0].tx_buf = &txbuf; + xfer[0].len = sizeof(txbuf); + spi_message_add_tail(&xfer[0], &msg); + + xfer[1].rx_buf = rxbuf; + xfer[1].len = len * 2; + spi_message_add_tail(&xfer[1], &msg); + + ret = spi_sync(ctx->spi, &msg); + if (ret) + return ret; + + for (i = 0; i < len; i++) { + buf[i] = rxbuf[i] >> i | (bit9 << (9 - i)); + if (i) + bit9 = rxbuf[i] & GENMASK(i - 1, 0); + } + + return 0; +} + +static int st7789v_check_id(struct drm_panel *panel) +{ + const u8 st7789v_ids[ST7789V_IDS_SIZE] = ST7789V_IDS; + struct st7789v *ctx = panel_to_st7789v(panel); + bool invalid_ids = false; + int ret, i; + u8 ids[3]; + + if (ctx->spi->mode & SPI_NO_RX) + return 0; + + ret = st7789v_read_data(ctx, MIPI_DCS_GET_DISPLAY_ID, ids, ST7789V_IDS_SIZE); + if (ret) + return ret; + + for (i = 0; i < ST7789V_IDS_SIZE; i++) { + if (ids[i] != st7789v_ids[i]) { + invalid_ids = true; + break; + } + } + + if (invalid_ids) + return -EIO; + + return 0; +} + static const struct drm_display_mode default_mode = { .clock = 7000, .hdisplay = 240, @@ -165,18 +240,76 @@ static const struct drm_display_mode default_mode = { .vsync_start = 320 + 8, .vsync_end = 320 + 8 + 4, .vtotal = 320 + 8 + 4 + 4, + .width_mm = 61, + .height_mm = 103, + .flags = DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC, +}; + +static const struct drm_display_mode t28cp45tn89_mode = { + .clock = 6008, + .hdisplay = 240, + .hsync_start = 240 + 38, + .hsync_end = 240 + 38 + 10, + .htotal = 240 + 38 + 10 + 10, + .vdisplay = 320, + .vsync_start = 320 + 8, + .vsync_end = 320 + 8 + 4, + .vtotal = 320 + 8 + 4 + 4, + .width_mm = 43, + .height_mm = 57, + .flags = DRM_MODE_FLAG_PVSYNC | DRM_MODE_FLAG_NVSYNC, +}; + +static const struct drm_display_mode et028013dma_mode = { + .clock = 3000, + .hdisplay = 240, + .hsync_start = 240 + 38, + .hsync_end = 240 + 38 + 10, + .htotal = 240 + 38 + 10 + 10, + .vdisplay = 320, + .vsync_start = 320 + 8, + .vsync_end = 320 + 8 + 4, + .vtotal = 320 + 8 + 4 + 4, + .width_mm = 43, + .height_mm = 58, + .flags = DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC, +}; + +static const struct st7789_panel_info default_panel = { + .mode = &default_mode, + .invert_mode = true, + .bus_format = MEDIA_BUS_FMT_RGB666_1X18, + .bus_flags = DRM_BUS_FLAG_DE_HIGH | + DRM_BUS_FLAG_PIXDATA_SAMPLE_NEGEDGE, +}; + +static const struct st7789_panel_info t28cp45tn89_panel = { + .mode = &t28cp45tn89_mode, + .invert_mode = false, + .bus_format = MEDIA_BUS_FMT_RGB565_1X16, + .bus_flags = DRM_BUS_FLAG_DE_HIGH | + DRM_BUS_FLAG_PIXDATA_SAMPLE_POSEDGE, +}; + +static const struct st7789_panel_info et028013dma_panel = { + .mode = &et028013dma_mode, + .invert_mode = true, + .bus_format = MEDIA_BUS_FMT_RGB666_1X18, + .bus_flags = DRM_BUS_FLAG_DE_HIGH | + DRM_BUS_FLAG_PIXDATA_SAMPLE_POSEDGE, }; static int st7789v_get_modes(struct drm_panel *panel, struct drm_connector *connector) { + struct st7789v *ctx = panel_to_st7789v(panel); struct drm_display_mode *mode; - mode = drm_mode_duplicate(connector->dev, &default_mode); + mode = drm_mode_duplicate(connector->dev, ctx->info->mode); if (!mode) { - dev_err(panel->dev, "failed to add mode %ux%ux@%u\n", - default_mode.hdisplay, default_mode.vdisplay, - drm_mode_vrefresh(&default_mode)); + dev_err(panel->dev, "failed to add mode %ux%u@%u\n", + ctx->info->mode->hdisplay, ctx->info->mode->vdisplay, + drm_mode_vrefresh(ctx->info->mode)); return -ENOMEM; } @@ -185,8 +318,12 @@ static int st7789v_get_modes(struct drm_panel *panel, mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED; drm_mode_probed_add(connector, mode); - connector->display_info.width_mm = 61; - connector->display_info.height_mm = 103; + connector->display_info.bpc = 6; + connector->display_info.width_mm = ctx->info->mode->width_mm; + connector->display_info.height_mm = ctx->info->mode->height_mm; + connector->display_info.bus_flags = ctx->info->bus_flags; + drm_display_info_set_bus_formats(&connector->display_info, + &ctx->info->bus_format, 1); return 1; } @@ -194,8 +331,34 @@ static int st7789v_get_modes(struct drm_panel *panel, static int st7789v_prepare(struct drm_panel *panel) { struct st7789v *ctx = panel_to_st7789v(panel); + u8 pixel_fmt, polarity; int ret; + switch (ctx->info->bus_format) { + case MEDIA_BUS_FMT_RGB666_1X18: + pixel_fmt = MIPI_DCS_PIXEL_FMT_18BIT; + break; + case MEDIA_BUS_FMT_RGB565_1X16: + pixel_fmt = MIPI_DCS_PIXEL_FMT_16BIT; + break; + default: + dev_err(panel->dev, "unsupported bus format: %d\n", + ctx->info->bus_format); + return -EINVAL; + } + + pixel_fmt = (pixel_fmt << 4) | pixel_fmt; + + polarity = 0; + if (ctx->info->mode->flags & DRM_MODE_FLAG_PVSYNC) + polarity |= ST7789V_RGBCTRL_VSYNC_HIGH; + if (ctx->info->mode->flags & DRM_MODE_FLAG_PHSYNC) + polarity |= ST7789V_RGBCTRL_HSYNC_HIGH; + if (ctx->info->bus_flags & DRM_BUS_FLAG_PIXDATA_SAMPLE_NEGEDGE) + polarity |= ST7789V_RGBCTRL_PCLK_FALLING; + if (ctx->info->bus_flags & DRM_BUS_FLAG_DE_LOW) + polarity |= ST7789V_RGBCTRL_DE_LOW; + ret = regulator_enable(ctx->power); if (ret) return ret; @@ -205,6 +368,14 @@ static int st7789v_prepare(struct drm_panel *panel) gpiod_set_value(ctx->reset, 0); msleep(120); + /* + * Avoid failing if the IDs are invalid in case the Rx bus width + * description is missing. + */ + ret = st7789v_check_id(panel); + if (ret) + dev_warn(panel->dev, "Unrecognized panel IDs"); + ST7789V_TEST(ret, st7789v_write_command(ctx, MIPI_DCS_EXIT_SLEEP_MODE)); /* We need to wait 120ms after a sleep out command */ @@ -216,9 +387,7 @@ static int st7789v_prepare(struct drm_panel *panel) ST7789V_TEST(ret, st7789v_write_command(ctx, MIPI_DCS_SET_PIXEL_FORMAT)); - ST7789V_TEST(ret, st7789v_write_data(ctx, - (MIPI_DCS_PIXEL_FMT_18BIT << 4) | - (MIPI_DCS_PIXEL_FMT_18BIT))); + ST7789V_TEST(ret, st7789v_write_data(ctx, pixel_fmt)); ST7789V_TEST(ret, st7789v_write_command(ctx, ST7789V_PORCTRL_CMD)); ST7789V_TEST(ret, st7789v_write_data(ctx, 0xc)); @@ -296,7 +465,13 @@ static int st7789v_prepare(struct drm_panel *panel) ST7789V_TEST(ret, st7789v_write_data(ctx, ST7789V_NVGAMCTRL_VN61(0x1b))); ST7789V_TEST(ret, st7789v_write_data(ctx, ST7789V_NVGAMCTRL_VN62(0x28))); - ST7789V_TEST(ret, st7789v_write_command(ctx, MIPI_DCS_ENTER_INVERT_MODE)); + if (ctx->info->invert_mode) { + ST7789V_TEST(ret, st7789v_write_command(ctx, + MIPI_DCS_ENTER_INVERT_MODE)); + } else { + ST7789V_TEST(ret, st7789v_write_command(ctx, + MIPI_DCS_EXIT_INVERT_MODE)); + } ST7789V_TEST(ret, st7789v_write_command(ctx, ST7789V_RAMCTRL_CMD)); ST7789V_TEST(ret, st7789v_write_data(ctx, ST7789V_RAMCTRL_DM_RGB | @@ -307,9 +482,7 @@ static int st7789v_prepare(struct drm_panel *panel) ST7789V_TEST(ret, st7789v_write_command(ctx, ST7789V_RGBCTRL_CMD)); ST7789V_TEST(ret, st7789v_write_data(ctx, ST7789V_RGBCTRL_WO | ST7789V_RGBCTRL_RCM(2) | - ST7789V_RGBCTRL_VSYNC_HIGH | - ST7789V_RGBCTRL_HSYNC_HIGH | - ST7789V_RGBCTRL_PCLK_HIGH)); + polarity)); ST7789V_TEST(ret, st7789v_write_data(ctx, ST7789V_RGBCTRL_VBP(8))); ST7789V_TEST(ret, st7789v_write_data(ctx, ST7789V_RGBCTRL_HBP(20))); @@ -355,32 +528,40 @@ static const struct drm_panel_funcs st7789v_drm_funcs = { static int st7789v_probe(struct spi_device *spi) { + struct device *dev = &spi->dev; struct st7789v *ctx; int ret; - ctx = devm_kzalloc(&spi->dev, sizeof(*ctx), GFP_KERNEL); + ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL); if (!ctx) return -ENOMEM; spi_set_drvdata(spi, ctx); ctx->spi = spi; - drm_panel_init(&ctx->panel, &spi->dev, &st7789v_drm_funcs, + spi->bits_per_word = 9; + ret = spi_setup(spi); + if (ret < 0) + return dev_err_probe(&spi->dev, ret, "Failed to setup spi\n"); + + ctx->info = device_get_match_data(&spi->dev); + + drm_panel_init(&ctx->panel, dev, &st7789v_drm_funcs, DRM_MODE_CONNECTOR_DPI); - ctx->power = devm_regulator_get(&spi->dev, "power"); - if (IS_ERR(ctx->power)) - return PTR_ERR(ctx->power); + ctx->power = devm_regulator_get(dev, "power"); + ret = PTR_ERR_OR_ZERO(ctx->power); + if (ret) + return dev_err_probe(dev, ret, "Failed to get regulator\n"); - ctx->reset = devm_gpiod_get(&spi->dev, "reset", GPIOD_OUT_LOW); - if (IS_ERR(ctx->reset)) { - dev_err(&spi->dev, "Couldn't get our reset line\n"); - return PTR_ERR(ctx->reset); - } + ctx->reset = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW); + ret = PTR_ERR_OR_ZERO(ctx->reset); + if (ret) + return dev_err_probe(dev, ret, "Failed to get reset line\n"); ret = drm_panel_of_backlight(&ctx->panel); if (ret) - return ret; + return dev_err_probe(dev, ret, "Failed to get backlight\n"); drm_panel_add(&ctx->panel); @@ -394,8 +575,18 @@ static void st7789v_remove(struct spi_device *spi) drm_panel_remove(&ctx->panel); } +static const struct spi_device_id st7789v_spi_id[] = { + { "st7789v", (unsigned long) &default_panel }, + { "t28cp45tn89-v17", (unsigned long) &t28cp45tn89_panel }, + { "et028013dma", (unsigned long) &et028013dma_panel }, + { } +}; +MODULE_DEVICE_TABLE(spi, st7789v_spi_id); + static const struct of_device_id st7789v_of_match[] = { - { .compatible = "sitronix,st7789v" }, + { .compatible = "sitronix,st7789v", .data = &default_panel }, + { .compatible = "inanbo,t28cp45tn89-v17", .data = &t28cp45tn89_panel }, + { .compatible = "edt,et028013dma", .data = &et028013dma_panel }, { } }; MODULE_DEVICE_TABLE(of, st7789v_of_match); @@ -403,6 +594,7 @@ MODULE_DEVICE_TABLE(of, st7789v_of_match); static struct spi_driver st7789v_driver = { .probe = st7789v_probe, .remove = st7789v_remove, + .id_table = st7789v_spi_id, .driver = { .name = "st7789v", .of_match_table = st7789v_of_match, diff --git a/drivers/gpu/drm/panel/panel-startek-kd070fhfid015.c b/drivers/gpu/drm/panel/panel-startek-kd070fhfid015.c new file mode 100644 index 000000000000..6e77a2d71d81 --- /dev/null +++ b/drivers/gpu/drm/panel/panel-startek-kd070fhfid015.c @@ -0,0 +1,406 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2016 InforceComputing + * Copyright (C) 2016 Linaro Ltd + * Copyright (C) 2023 BayLibre, SAS + * + * Authors: + * - Vinay Simha BN <simhavcs@gmail.com> + * - Sumit Semwal <sumit.semwal@linaro.org> + * - Guillaume La Roque <glaroque@baylibre.com> + * + */ + +#include <linux/backlight.h> +#include <linux/delay.h> +#include <linux/gpio/consumer.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/regulator/consumer.h> + +#include <video/mipi_display.h> + +#include <drm/drm_mipi_dsi.h> +#include <drm/drm_modes.h> +#include <drm/drm_panel.h> + +#define DSI_REG_MCAP 0xB0 +#define DSI_REG_IS 0xB3 /* Interface Setting */ +#define DSI_REG_IIS 0xB4 /* Interface ID Setting */ +#define DSI_REG_CTRL 0xB6 + +enum { + IOVCC = 0, + POWER = 1 +}; + +struct stk_panel { + bool prepared; + const struct drm_display_mode *mode; + struct backlight_device *backlight; + struct drm_panel base; + struct gpio_desc *enable_gpio; /* Power IC supply enable */ + struct gpio_desc *reset_gpio; /* External reset */ + struct mipi_dsi_device *dsi; + struct regulator_bulk_data supplies[2]; +}; + +static inline struct stk_panel *to_stk_panel(struct drm_panel *panel) +{ + return container_of(panel, struct stk_panel, base); +} + +static int stk_panel_init(struct stk_panel *stk) +{ + struct mipi_dsi_device *dsi = stk->dsi; + struct device *dev = &stk->dsi->dev; + int ret; + + ret = mipi_dsi_dcs_soft_reset(dsi); + if (ret < 0) { + dev_err(dev, "failed to mipi_dsi_dcs_soft_reset: %d\n", ret); + return ret; + } + mdelay(5); + + ret = mipi_dsi_dcs_exit_sleep_mode(dsi); + if (ret < 0) { + dev_err(dev, "failed to set exit sleep mode: %d\n", ret); + return ret; + } + msleep(120); + + mipi_dsi_generic_write_seq(dsi, DSI_REG_MCAP, 0x04); + + /* Interface setting, video mode */ + mipi_dsi_generic_write_seq(dsi, DSI_REG_IS, 0x14, 0x08, 0x00, 0x22, 0x00); + mipi_dsi_generic_write_seq(dsi, DSI_REG_IIS, 0x0C, 0x00); + mipi_dsi_generic_write_seq(dsi, DSI_REG_CTRL, 0x3A, 0xD3); + + ret = mipi_dsi_dcs_set_display_brightness(dsi, 0x77); + if (ret < 0) { + dev_err(dev, "failed to write display brightness: %d\n", ret); + return ret; + } + + mipi_dsi_dcs_write_seq(dsi, MIPI_DCS_WRITE_CONTROL_DISPLAY, + MIPI_DCS_WRITE_MEMORY_START); + + ret = mipi_dsi_dcs_set_pixel_format(dsi, 0x77); + if (ret < 0) { + dev_err(dev, "failed to set pixel format: %d\n", ret); + return ret; + } + + ret = mipi_dsi_dcs_set_column_address(dsi, 0, stk->mode->hdisplay - 1); + if (ret < 0) { + dev_err(dev, "failed to set column address: %d\n", ret); + return ret; + } + + ret = mipi_dsi_dcs_set_page_address(dsi, 0, stk->mode->vdisplay - 1); + if (ret < 0) { + dev_err(dev, "failed to set page address: %d\n", ret); + return ret; + } + + return 0; +} + +static int stk_panel_on(struct stk_panel *stk) +{ + struct mipi_dsi_device *dsi = stk->dsi; + struct device *dev = &stk->dsi->dev; + int ret; + + ret = mipi_dsi_dcs_set_display_on(dsi); + if (ret < 0) + dev_err(dev, "failed to set display on: %d\n", ret); + + mdelay(20); + + return ret; +} + +static void stk_panel_off(struct stk_panel *stk) +{ + struct mipi_dsi_device *dsi = stk->dsi; + struct device *dev = &stk->dsi->dev; + int ret; + + dsi->mode_flags &= ~MIPI_DSI_MODE_LPM; + + ret = mipi_dsi_dcs_set_display_off(dsi); + if (ret < 0) + dev_err(dev, "failed to set display off: %d\n", ret); + + ret = mipi_dsi_dcs_enter_sleep_mode(dsi); + if (ret < 0) + dev_err(dev, "failed to enter sleep mode: %d\n", ret); + + msleep(100); +} + +static int stk_panel_unprepare(struct drm_panel *panel) +{ + struct stk_panel *stk = to_stk_panel(panel); + + if (!stk->prepared) + return 0; + + stk_panel_off(stk); + regulator_bulk_disable(ARRAY_SIZE(stk->supplies), stk->supplies); + gpiod_set_value(stk->reset_gpio, 0); + gpiod_set_value(stk->enable_gpio, 1); + + stk->prepared = false; + + return 0; +} + +static int stk_panel_prepare(struct drm_panel *panel) +{ + struct stk_panel *stk = to_stk_panel(panel); + struct device *dev = &stk->dsi->dev; + int ret; + + if (stk->prepared) + return 0; + + gpiod_set_value(stk->reset_gpio, 0); + gpiod_set_value(stk->enable_gpio, 0); + ret = regulator_enable(stk->supplies[IOVCC].consumer); + if (ret < 0) + return ret; + + mdelay(8); + ret = regulator_enable(stk->supplies[POWER].consumer); + if (ret < 0) + goto iovccoff; + + mdelay(20); + gpiod_set_value(stk->enable_gpio, 1); + mdelay(20); + gpiod_set_value(stk->reset_gpio, 1); + mdelay(10); + ret = stk_panel_init(stk); + if (ret < 0) { + dev_err(dev, "failed to init panel: %d\n", ret); + goto poweroff; + } + + ret = stk_panel_on(stk); + if (ret < 0) { + dev_err(dev, "failed to set panel on: %d\n", ret); + goto poweroff; + } + + stk->prepared = true; + + return 0; + +poweroff: + regulator_disable(stk->supplies[POWER].consumer); +iovccoff: + regulator_disable(stk->supplies[IOVCC].consumer); + gpiod_set_value(stk->reset_gpio, 0); + gpiod_set_value(stk->enable_gpio, 0); + + return ret; +} + +static const struct drm_display_mode default_mode = { + .clock = 163204, + .hdisplay = 1200, + .hsync_start = 1200 + 144, + .hsync_end = 1200 + 144 + 16, + .htotal = 1200 + 144 + 16 + 45, + .vdisplay = 1920, + .vsync_start = 1920 + 8, + .vsync_end = 1920 + 8 + 4, + .vtotal = 1920 + 8 + 4 + 4, + .width_mm = 95, + .height_mm = 151, +}; + +static int stk_panel_get_modes(struct drm_panel *panel, + struct drm_connector *connector) +{ + struct drm_display_mode *mode; + + mode = drm_mode_duplicate(connector->dev, &default_mode); + if (!mode) { + dev_err(panel->dev, "failed to add mode %ux%ux@%u\n", + default_mode.hdisplay, default_mode.vdisplay, + drm_mode_vrefresh(&default_mode)); + return -ENOMEM; + } + + drm_mode_set_name(mode); + drm_mode_probed_add(connector, mode); + connector->display_info.width_mm = default_mode.width_mm; + connector->display_info.height_mm = default_mode.height_mm; + return 1; +} + +static int dsi_dcs_bl_get_brightness(struct backlight_device *bl) +{ + struct mipi_dsi_device *dsi = bl_get_data(bl); + int ret; + u16 brightness; + + dsi->mode_flags &= ~MIPI_DSI_MODE_LPM; + ret = mipi_dsi_dcs_get_display_brightness(dsi, &brightness); + if (ret < 0) + return ret; + + dsi->mode_flags |= MIPI_DSI_MODE_LPM; + return brightness & 0xff; +} + +static int dsi_dcs_bl_update_status(struct backlight_device *bl) +{ + struct mipi_dsi_device *dsi = bl_get_data(bl); + struct device *dev = &dsi->dev; + int ret; + + dsi->mode_flags &= ~MIPI_DSI_MODE_LPM; + ret = mipi_dsi_dcs_set_display_brightness(dsi, bl->props.brightness); + if (ret < 0) { + dev_err(dev, "failed to set DSI control: %d\n", ret); + return ret; + } + + dsi->mode_flags |= MIPI_DSI_MODE_LPM; + return 0; +} + +static const struct backlight_ops dsi_bl_ops = { + .update_status = dsi_dcs_bl_update_status, + .get_brightness = dsi_dcs_bl_get_brightness, +}; + +static struct backlight_device * +drm_panel_create_dsi_backlight(struct mipi_dsi_device *dsi) +{ + struct device *dev = &dsi->dev; + struct backlight_properties props = { + .type = BACKLIGHT_RAW, + .brightness = 255, + .max_brightness = 255, + }; + + return devm_backlight_device_register(dev, dev_name(dev), dev, dsi, + &dsi_bl_ops, &props); +} + +static const struct drm_panel_funcs stk_panel_funcs = { + .unprepare = stk_panel_unprepare, + .prepare = stk_panel_prepare, + .get_modes = stk_panel_get_modes, +}; + +static const struct of_device_id stk_of_match[] = { + { .compatible = "startek,kd070fhfid015", }, + { } +}; +MODULE_DEVICE_TABLE(of, stk_of_match); + +static int stk_panel_add(struct stk_panel *stk) +{ + struct device *dev = &stk->dsi->dev; + int ret; + + stk->mode = &default_mode; + + stk->supplies[IOVCC].supply = "iovcc"; + stk->supplies[POWER].supply = "power"; + ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(stk->supplies), stk->supplies); + if (ret) { + dev_err(dev, "regulator_bulk failed\n"); + return ret; + } + + stk->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW); + if (IS_ERR(stk->reset_gpio)) { + ret = PTR_ERR(stk->reset_gpio); + dev_err(dev, "cannot get reset-gpios %d\n", ret); + return ret; + } + + stk->enable_gpio = devm_gpiod_get(dev, "enable", GPIOD_OUT_LOW); + if (IS_ERR(stk->enable_gpio)) { + ret = PTR_ERR(stk->enable_gpio); + dev_err(dev, "cannot get enable-gpio %d\n", ret); + return ret; + } + + stk->backlight = drm_panel_create_dsi_backlight(stk->dsi); + if (IS_ERR(stk->backlight)) { + ret = PTR_ERR(stk->backlight); + dev_err(dev, "failed to register backlight %d\n", ret); + return ret; + } + + drm_panel_init(&stk->base, &stk->dsi->dev, &stk_panel_funcs, + DRM_MODE_CONNECTOR_DSI); + + drm_panel_add(&stk->base); + + return 0; +} + +static int stk_panel_probe(struct mipi_dsi_device *dsi) +{ + struct stk_panel *stk; + int ret; + + dsi->lanes = 4; + dsi->format = MIPI_DSI_FMT_RGB888; + dsi->mode_flags = (MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_LPM); + + stk = devm_kzalloc(&dsi->dev, sizeof(*stk), GFP_KERNEL); + if (!stk) + return -ENOMEM; + + mipi_dsi_set_drvdata(dsi, stk); + + stk->dsi = dsi; + + ret = stk_panel_add(stk); + if (ret < 0) + return ret; + + ret = mipi_dsi_attach(dsi); + if (ret < 0) + drm_panel_remove(&stk->base); + + return 0; +} + +static void stk_panel_remove(struct mipi_dsi_device *dsi) +{ + struct stk_panel *stk = mipi_dsi_get_drvdata(dsi); + int err; + + err = mipi_dsi_detach(dsi); + if (err < 0) + dev_err(&dsi->dev, "failed to detach from DSI host: %d\n", + err); + + drm_panel_remove(&stk->base); +} + +static struct mipi_dsi_driver stk_panel_driver = { + .driver = { + .name = "panel-startek-kd070fhfid015", + .of_match_table = stk_of_match, + }, + .probe = stk_panel_probe, + .remove = stk_panel_remove, +}; +module_mipi_dsi_driver(stk_panel_driver); + +MODULE_AUTHOR("Guillaume La Roque <glaroque@baylibre.com>"); +MODULE_DESCRIPTION("STARTEK KD070FHFID015"); +MODULE_LICENSE("GPL"); diff --git a/drivers/gpu/drm/radeon/Kconfig b/drivers/gpu/drm/radeon/Kconfig index fe498c8af1bb..f98356be0af2 100644 --- a/drivers/gpu/drm/radeon/Kconfig +++ b/drivers/gpu/drm/radeon/Kconfig @@ -11,7 +11,7 @@ config DRM_RADEON select DRM_SUBALLOC_HELPER select DRM_TTM select DRM_TTM_HELPER - select FB_IO_HELPERS if DRM_FBDEV_EMULATION + select FB_IOMEM_HELPERS if DRM_FBDEV_EMULATION select SND_HDA_COMPONENT if SND_HDA_CORE select POWER_SUPPLY select HWMON diff --git a/drivers/gpu/drm/radeon/cik.c b/drivers/gpu/drm/radeon/cik.c index 5819737c21c6..5d6b81a6578e 100644 --- a/drivers/gpu/drm/radeon/cik.c +++ b/drivers/gpu/drm/radeon/cik.c @@ -3603,7 +3603,7 @@ void cik_fence_compute_ring_emit(struct radeon_device *rdev, * @rdev: radeon_device pointer * @ring: radeon ring buffer object * @semaphore: radeon semaphore object - * @emit_wait: Is this a sempahore wait? + * @emit_wait: Is this a semaphore wait? * * Emits a semaphore signal/wait packet to the CP ring and prevents the PFP * from running ahead of semaphore waits. diff --git a/drivers/gpu/drm/radeon/r600.c b/drivers/gpu/drm/radeon/r600.c index 382795a8b3c0..a17b95eec65f 100644 --- a/drivers/gpu/drm/radeon/r600.c +++ b/drivers/gpu/drm/radeon/r600.c @@ -2918,7 +2918,7 @@ void r600_fence_ring_emit(struct radeon_device *rdev, * @rdev: radeon_device pointer * @ring: radeon ring buffer object * @semaphore: radeon semaphore object - * @emit_wait: Is this a sempahore wait? + * @emit_wait: Is this a semaphore wait? * * Emits a semaphore signal/wait packet to the CP ring and prevents the PFP * from running ahead of semaphore waits. diff --git a/drivers/gpu/drm/radeon/radeon_fbdev.c b/drivers/gpu/drm/radeon/radeon_fbdev.c index 68c06ac9acce..02bf25759059 100644 --- a/drivers/gpu/drm/radeon/radeon_fbdev.c +++ b/drivers/gpu/drm/radeon/radeon_fbdev.c @@ -193,7 +193,7 @@ static const struct fb_ops radeon_fbdev_fb_ops = { .owner = THIS_MODULE, .fb_open = radeon_fbdev_fb_open, .fb_release = radeon_fbdev_fb_release, - FB_DEFAULT_IO_OPS, + FB_DEFAULT_IOMEM_OPS, DRM_FB_HELPER_DEFAULT_OPS, .fb_destroy = radeon_fbdev_fb_destroy, }; diff --git a/drivers/gpu/drm/renesas/rcar-du/rcar_du_plane.c b/drivers/gpu/drm/renesas/rcar-du/rcar_du_plane.c index d759e0192181..e445fac8e0b4 100644 --- a/drivers/gpu/drm/renesas/rcar-du/rcar_du_plane.c +++ b/drivers/gpu/drm/renesas/rcar-du/rcar_du_plane.c @@ -600,7 +600,8 @@ int __rcar_du_plane_atomic_check(struct drm_plane *plane, if (!state->crtc) { /* * The visible field is not reset by the DRM core but only - * updated by drm_plane_helper_check_state(), set it manually. + * updated by drm_atomic_helper_check_plane_state(), set it + * manually. */ state->visible = false; *format = NULL; diff --git a/drivers/gpu/drm/solomon/ssd130x.c b/drivers/gpu/drm/solomon/ssd130x.c index d2f8dd6a6347..5a80b228d18c 100644 --- a/drivers/gpu/drm/solomon/ssd130x.c +++ b/drivers/gpu/drm/solomon/ssd130x.c @@ -142,7 +142,7 @@ const struct ssd130x_deviceinfo ssd130x_variants[] = { EXPORT_SYMBOL_NS_GPL(ssd130x_variants, DRM_SSD130X); struct ssd130x_plane_state { - struct drm_plane_state base; + struct drm_shadow_plane_state base; /* Intermediate buffer to convert pixels from XRGB8888 to HW format */ u8 *buffer; /* Buffer to store pixels in HW format and written to the panel */ @@ -151,7 +151,7 @@ struct ssd130x_plane_state { static inline struct ssd130x_plane_state *to_ssd130x_plane_state(struct drm_plane_state *state) { - return container_of(state, struct ssd130x_plane_state, base); + return container_of(state, struct ssd130x_plane_state, base.base); } static inline struct ssd130x_device *drm_to_ssd130x(struct drm_device *drm) @@ -689,11 +689,12 @@ static void ssd130x_primary_plane_reset(struct drm_plane *plane) if (!ssd130x_state) return; - __drm_atomic_helper_plane_reset(plane, &ssd130x_state->base); + __drm_gem_reset_shadow_plane(plane, &ssd130x_state->base); } static struct drm_plane_state *ssd130x_primary_plane_duplicate_state(struct drm_plane *plane) { + struct drm_shadow_plane_state *new_shadow_plane_state; struct ssd130x_plane_state *old_ssd130x_state; struct ssd130x_plane_state *ssd130x_state; @@ -709,9 +710,11 @@ static struct drm_plane_state *ssd130x_primary_plane_duplicate_state(struct drm_ ssd130x_state->buffer = NULL; ssd130x_state->data_array = NULL; - __drm_atomic_helper_plane_duplicate_state(plane, &ssd130x_state->base); + new_shadow_plane_state = &ssd130x_state->base; - return &ssd130x_state->base; + __drm_gem_duplicate_shadow_plane_state(plane, new_shadow_plane_state); + + return &new_shadow_plane_state->base; } static void ssd130x_primary_plane_destroy_state(struct drm_plane *plane, @@ -722,7 +725,7 @@ static void ssd130x_primary_plane_destroy_state(struct drm_plane *plane, kfree(ssd130x_state->data_array); kfree(ssd130x_state->buffer); - __drm_atomic_helper_plane_destroy_state(&ssd130x_state->base); + __drm_gem_destroy_shadow_plane_state(&ssd130x_state->base); kfree(ssd130x_state); } @@ -741,7 +744,6 @@ static const struct drm_plane_funcs ssd130x_primary_plane_funcs = { .atomic_duplicate_state = ssd130x_primary_plane_duplicate_state, .atomic_destroy_state = ssd130x_primary_plane_destroy_state, .destroy = drm_plane_cleanup, - DRM_GEM_SHADOW_PLANE_FUNCS, }; static enum drm_mode_status ssd130x_crtc_helper_mode_valid(struct drm_crtc *crtc, diff --git a/drivers/gpu/drm/tegra/Kconfig b/drivers/gpu/drm/tegra/Kconfig index 39452c8480c1..84e7e6bc3a0c 100644 --- a/drivers/gpu/drm/tegra/Kconfig +++ b/drivers/gpu/drm/tegra/Kconfig @@ -12,7 +12,7 @@ config DRM_TEGRA select DRM_KMS_HELPER select DRM_MIPI_DSI select DRM_PANEL - select FB_DMA_HELPERS if DRM_FBDEV_EMULATION + select FB_DMAMEM_HELPERS if DRM_FBDEV_EMULATION select TEGRA_HOST1X select INTERCONNECT select IOMMU_IOVA diff --git a/drivers/gpu/drm/tegra/dpaux.c b/drivers/gpu/drm/tegra/dpaux.c index 96a8afcab3ef..ef02d530f78d 100644 --- a/drivers/gpu/drm/tegra/dpaux.c +++ b/drivers/gpu/drm/tegra/dpaux.c @@ -447,7 +447,6 @@ static const struct pinmux_ops tegra_dpaux_pinmux_ops = { static int tegra_dpaux_probe(struct platform_device *pdev) { struct tegra_dpaux *dpaux; - struct resource *regs; u32 value; int err; @@ -461,14 +460,13 @@ static int tegra_dpaux_probe(struct platform_device *pdev) INIT_LIST_HEAD(&dpaux->list); dpaux->dev = &pdev->dev; - regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); - dpaux->regs = devm_ioremap_resource(&pdev->dev, regs); + dpaux->regs = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(dpaux->regs)) return PTR_ERR(dpaux->regs); dpaux->irq = platform_get_irq(pdev, 0); if (dpaux->irq < 0) - return -ENXIO; + return dpaux->irq; if (!pdev->dev.pm_domain) { dpaux->rst = devm_reset_control_get(&pdev->dev, "dpaux"); diff --git a/drivers/gpu/drm/tegra/fbdev.c b/drivers/gpu/drm/tegra/fbdev.c index 206a399c42d6..db6eaac3d30e 100644 --- a/drivers/gpu/drm/tegra/fbdev.c +++ b/drivers/gpu/drm/tegra/fbdev.c @@ -59,9 +59,9 @@ static void tegra_fbdev_fb_destroy(struct fb_info *info) static const struct fb_ops tegra_fb_ops = { .owner = THIS_MODULE, - __FB_DEFAULT_DMA_OPS_RDWR, + __FB_DEFAULT_DMAMEM_OPS_RDWR, DRM_FB_HELPER_DEFAULT_OPS, - __FB_DEFAULT_DMA_OPS_DRAW, + __FB_DEFAULT_DMAMEM_OPS_DRAW, .fb_mmap = tegra_fb_mmap, .fb_destroy = tegra_fbdev_fb_destroy, }; diff --git a/drivers/gpu/drm/tegra/gr2d.c b/drivers/gpu/drm/tegra/gr2d.c index 4c55133467a0..a160d01f26e1 100644 --- a/drivers/gpu/drm/tegra/gr2d.c +++ b/drivers/gpu/drm/tegra/gr2d.c @@ -71,22 +71,15 @@ static int gr2d_init(struct host1x_client *client) goto free; } - pm_runtime_enable(client->dev); - pm_runtime_use_autosuspend(client->dev); - pm_runtime_set_autosuspend_delay(client->dev, 200); - err = tegra_drm_register_client(dev->dev_private, drm); if (err < 0) { dev_err(client->dev, "failed to register client: %d\n", err); - goto disable_rpm; + goto detach_iommu; } return 0; -disable_rpm: - pm_runtime_dont_use_autosuspend(client->dev); - pm_runtime_force_suspend(client->dev); - +detach_iommu: host1x_client_iommu_detach(client); free: host1x_syncpt_put(client->syncpts[0]); @@ -300,6 +293,7 @@ static void gr2d_remove(struct platform_device *pdev) { struct gr2d *gr2d = platform_get_drvdata(pdev); + pm_runtime_disable(&pdev->dev); host1x_client_unregister(&gr2d->client.base); } @@ -373,6 +367,10 @@ static int __maybe_unused gr2d_runtime_resume(struct device *dev) goto disable_clk; } + pm_runtime_enable(dev); + pm_runtime_use_autosuspend(dev); + pm_runtime_set_autosuspend_delay(dev, 500); + return 0; disable_clk: diff --git a/drivers/gpu/drm/tegra/gr3d.c b/drivers/gpu/drm/tegra/gr3d.c index 2768f8f073ed..00c8564520e7 100644 --- a/drivers/gpu/drm/tegra/gr3d.c +++ b/drivers/gpu/drm/tegra/gr3d.c @@ -80,22 +80,15 @@ static int gr3d_init(struct host1x_client *client) goto free; } - pm_runtime_enable(client->dev); - pm_runtime_use_autosuspend(client->dev); - pm_runtime_set_autosuspend_delay(client->dev, 200); - err = tegra_drm_register_client(dev->dev_private, drm); if (err < 0) { dev_err(client->dev, "failed to register client: %d\n", err); - goto disable_rpm; + goto detach_iommu; } return 0; -disable_rpm: - pm_runtime_dont_use_autosuspend(client->dev); - pm_runtime_force_suspend(client->dev); - +detach_iommu: host1x_client_iommu_detach(client); free: host1x_syncpt_put(client->syncpts[0]); @@ -554,6 +547,7 @@ static void gr3d_remove(struct platform_device *pdev) { struct gr3d *gr3d = platform_get_drvdata(pdev); + pm_runtime_disable(&pdev->dev); host1x_client_unregister(&gr3d->client.base); } @@ -607,6 +601,10 @@ static int __maybe_unused gr3d_runtime_resume(struct device *dev) goto disable_clk; } + pm_runtime_enable(dev); + pm_runtime_use_autosuspend(dev); + pm_runtime_set_autosuspend_delay(dev, 500); + return 0; disable_clk: diff --git a/drivers/gpu/drm/tegra/hdmi.c b/drivers/gpu/drm/tegra/hdmi.c index 00be307ca070..80c760986d9e 100644 --- a/drivers/gpu/drm/tegra/hdmi.c +++ b/drivers/gpu/drm/tegra/hdmi.c @@ -20,6 +20,7 @@ #include <soc/tegra/common.h> #include <sound/hdmi-codec.h> +#include <drm/drm_bridge_connector.h> #include <drm/drm_atomic_helper.h> #include <drm/drm_crtc.h> #include <drm/drm_debugfs.h> @@ -1545,26 +1546,47 @@ static int tegra_hdmi_init(struct host1x_client *client) { struct tegra_hdmi *hdmi = host1x_client_to_hdmi(client); struct drm_device *drm = dev_get_drvdata(client->host); + struct drm_connector *connector; int err; hdmi->output.dev = client->dev; - drm_connector_init_with_ddc(drm, &hdmi->output.connector, - &tegra_hdmi_connector_funcs, - DRM_MODE_CONNECTOR_HDMIA, - hdmi->output.ddc); - drm_connector_helper_add(&hdmi->output.connector, - &tegra_hdmi_connector_helper_funcs); - hdmi->output.connector.dpms = DRM_MODE_DPMS_OFF; - drm_simple_encoder_init(drm, &hdmi->output.encoder, DRM_MODE_ENCODER_TMDS); drm_encoder_helper_add(&hdmi->output.encoder, &tegra_hdmi_encoder_helper_funcs); - drm_connector_attach_encoder(&hdmi->output.connector, - &hdmi->output.encoder); - drm_connector_register(&hdmi->output.connector); + if (hdmi->output.bridge) { + err = drm_bridge_attach(&hdmi->output.encoder, hdmi->output.bridge, + NULL, DRM_BRIDGE_ATTACH_NO_CONNECTOR); + if (err) { + dev_err(client->dev, "failed to attach bridge: %d\n", + err); + return err; + } + + connector = drm_bridge_connector_init(drm, &hdmi->output.encoder); + if (IS_ERR(connector)) { + dev_err(client->dev, + "failed to initialize bridge connector: %pe\n", + connector); + return PTR_ERR(connector); + } + + drm_connector_attach_encoder(connector, &hdmi->output.encoder); + } else { + drm_connector_init_with_ddc(drm, &hdmi->output.connector, + &tegra_hdmi_connector_funcs, + DRM_MODE_CONNECTOR_HDMIA, + hdmi->output.ddc); + drm_connector_helper_add(&hdmi->output.connector, + &tegra_hdmi_connector_helper_funcs); + hdmi->output.connector.dpms = DRM_MODE_DPMS_OFF; + + drm_connector_attach_encoder(&hdmi->output.connector, + &hdmi->output.encoder); + drm_connector_register(&hdmi->output.connector); + } err = tegra_output_init(drm, &hdmi->output); if (err < 0) { @@ -1770,7 +1792,6 @@ static irqreturn_t tegra_hdmi_irq(int irq, void *data) static int tegra_hdmi_probe(struct platform_device *pdev) { struct tegra_hdmi *hdmi; - struct resource *regs; int err; hdmi = devm_kzalloc(&pdev->dev, sizeof(*hdmi), GFP_KERNEL); @@ -1832,8 +1853,7 @@ static int tegra_hdmi_probe(struct platform_device *pdev) if (err < 0) return err; - regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); - hdmi->regs = devm_ioremap_resource(&pdev->dev, regs); + hdmi->regs = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(hdmi->regs)) return PTR_ERR(hdmi->regs); diff --git a/drivers/gpu/drm/tegra/nvdec.c b/drivers/gpu/drm/tegra/nvdec.c index f821e36111a7..4860790666af 100644 --- a/drivers/gpu/drm/tegra/nvdec.c +++ b/drivers/gpu/drm/tegra/nvdec.c @@ -175,13 +175,9 @@ static int nvdec_init(struct host1x_client *client) goto free_channel; } - pm_runtime_enable(client->dev); - pm_runtime_use_autosuspend(client->dev); - pm_runtime_set_autosuspend_delay(client->dev, 500); - err = tegra_drm_register_client(tegra, drm); if (err < 0) - goto disable_rpm; + goto free_syncpt; /* * Inherit the DMA parameters (such as maximum segment size) from the @@ -191,10 +187,7 @@ static int nvdec_init(struct host1x_client *client) return 0; -disable_rpm: - pm_runtime_dont_use_autosuspend(client->dev); - pm_runtime_force_suspend(client->dev); - +free_syncpt: host1x_syncpt_put(client->syncpts[0]); free_channel: host1x_channel_put(nvdec->channel); @@ -274,6 +267,8 @@ static int nvdec_load_falcon_firmware(struct nvdec *nvdec) return err; } else { virt = tegra_drm_alloc(tegra, size, &iova); + if (IS_ERR(virt)) + return PTR_ERR(virt); } nvdec->falcon.firmware.virt = virt; @@ -537,6 +532,10 @@ static int nvdec_probe(struct platform_device *pdev) goto exit_falcon; } + pm_runtime_enable(dev); + pm_runtime_use_autosuspend(dev); + pm_runtime_set_autosuspend_delay(dev, 500); + return 0; exit_falcon: @@ -549,8 +548,8 @@ static void nvdec_remove(struct platform_device *pdev) { struct nvdec *nvdec = platform_get_drvdata(pdev); + pm_runtime_disable(&pdev->dev); host1x_client_unregister(&nvdec->client.base); - falcon_exit(&nvdec->falcon); } diff --git a/drivers/gpu/drm/tegra/sor.c b/drivers/gpu/drm/tegra/sor.c index 93471b70e180..61b437a84806 100644 --- a/drivers/gpu/drm/tegra/sor.c +++ b/drivers/gpu/drm/tegra/sor.c @@ -3708,7 +3708,6 @@ static int tegra_sor_probe(struct platform_device *pdev) { struct device_node *np; struct tegra_sor *sor; - struct resource *regs; int err; sor = devm_kzalloc(&pdev->dev, sizeof(*sor), GFP_KERNEL); @@ -3781,8 +3780,7 @@ static int tegra_sor_probe(struct platform_device *pdev) } } - regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); - sor->regs = devm_ioremap_resource(&pdev->dev, regs); + sor->regs = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(sor->regs)) { err = PTR_ERR(sor->regs); goto remove; diff --git a/drivers/gpu/drm/tegra/vic.c b/drivers/gpu/drm/tegra/vic.c index 5da989ac700e..73c356f1c901 100644 --- a/drivers/gpu/drm/tegra/vic.c +++ b/drivers/gpu/drm/tegra/vic.c @@ -141,13 +141,9 @@ static int vic_init(struct host1x_client *client) goto free_channel; } - pm_runtime_enable(client->dev); - pm_runtime_use_autosuspend(client->dev); - pm_runtime_set_autosuspend_delay(client->dev, 500); - err = tegra_drm_register_client(tegra, drm); if (err < 0) - goto disable_rpm; + goto free_syncpt; /* * Inherit the DMA parameters (such as maximum segment size) from the @@ -157,10 +153,7 @@ static int vic_init(struct host1x_client *client) return 0; -disable_rpm: - pm_runtime_dont_use_autosuspend(client->dev); - pm_runtime_force_suspend(client->dev); - +free_syncpt: host1x_syncpt_put(client->syncpts[0]); free_channel: host1x_channel_put(vic->channel); @@ -527,6 +520,10 @@ static int vic_probe(struct platform_device *pdev) goto exit_falcon; } + pm_runtime_enable(dev); + pm_runtime_use_autosuspend(dev); + pm_runtime_set_autosuspend_delay(dev, 500); + return 0; exit_falcon: @@ -539,8 +536,8 @@ static void vic_remove(struct platform_device *pdev) { struct vic *vic = platform_get_drvdata(pdev); + pm_runtime_disable(&pdev->dev); host1x_client_unregister(&vic->client.base); - falcon_exit(&vic->falcon); } diff --git a/drivers/gpu/drm/tests/drm_client_modeset_test.c b/drivers/gpu/drm/tests/drm_client_modeset_test.c index 416a279b6dae..7516f6cb36e4 100644 --- a/drivers/gpu/drm/tests/drm_client_modeset_test.c +++ b/drivers/gpu/drm/tests/drm_client_modeset_test.c @@ -82,13 +82,6 @@ static int drm_client_modeset_test_init(struct kunit *test) return 0; } -static void drm_client_modeset_test_exit(struct kunit *test) -{ - struct drm_client_modeset_test_priv *priv = test->priv; - - drm_kunit_helper_free_device(test, priv->dev); -} - static void drm_test_pick_cmdline_res_1920_1080_60(struct kunit *test) { struct drm_client_modeset_test_priv *priv = test->priv; @@ -188,7 +181,6 @@ static struct kunit_case drm_test_pick_cmdline_tests[] = { static struct kunit_suite drm_test_pick_cmdline_test_suite = { .name = "drm_test_pick_cmdline", .init = drm_client_modeset_test_init, - .exit = drm_client_modeset_test_exit, .test_cases = drm_test_pick_cmdline_tests }; diff --git a/drivers/gpu/drm/tests/drm_exec_test.c b/drivers/gpu/drm/tests/drm_exec_test.c index 727ac267682e..f79c9f0359aa 100644 --- a/drivers/gpu/drm/tests/drm_exec_test.c +++ b/drivers/gpu/drm/tests/drm_exec_test.c @@ -12,11 +12,35 @@ #include <drm/drm_exec.h> #include <drm/drm_device.h> +#include <drm/drm_drv.h> #include <drm/drm_gem.h> +#include <drm/drm_kunit_helpers.h> #include "../lib/drm_random.h" -static struct drm_device dev; +struct drm_exec_priv { + struct device *dev; + struct drm_device *drm; +}; + +static int drm_exec_test_init(struct kunit *test) +{ + struct drm_exec_priv *priv; + + priv = kunit_kzalloc(test, sizeof(*priv), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, priv); + + test->priv = priv; + + priv->dev = drm_kunit_helper_alloc_device(test); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, priv->dev); + + priv->drm = __drm_kunit_helper_alloc_drm_device(test, priv->dev, sizeof(*priv->drm), 0, + DRIVER_MODESET); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, priv->drm); + + return 0; +} static void sanitycheck(struct kunit *test) { @@ -29,11 +53,12 @@ static void sanitycheck(struct kunit *test) static void test_lock(struct kunit *test) { + struct drm_exec_priv *priv = test->priv; struct drm_gem_object gobj = { }; struct drm_exec exec; int ret; - drm_gem_private_object_init(&dev, &gobj, PAGE_SIZE); + drm_gem_private_object_init(priv->drm, &gobj, PAGE_SIZE); drm_exec_init(&exec, DRM_EXEC_INTERRUPTIBLE_WAIT); drm_exec_until_all_locked(&exec) { @@ -48,11 +73,12 @@ static void test_lock(struct kunit *test) static void test_lock_unlock(struct kunit *test) { + struct drm_exec_priv *priv = test->priv; struct drm_gem_object gobj = { }; struct drm_exec exec; int ret; - drm_gem_private_object_init(&dev, &gobj, PAGE_SIZE); + drm_gem_private_object_init(priv->drm, &gobj, PAGE_SIZE); drm_exec_init(&exec, DRM_EXEC_INTERRUPTIBLE_WAIT); drm_exec_until_all_locked(&exec) { @@ -74,11 +100,12 @@ static void test_lock_unlock(struct kunit *test) static void test_duplicates(struct kunit *test) { + struct drm_exec_priv *priv = test->priv; struct drm_gem_object gobj = { }; struct drm_exec exec; int ret; - drm_gem_private_object_init(&dev, &gobj, PAGE_SIZE); + drm_gem_private_object_init(priv->drm, &gobj, PAGE_SIZE); drm_exec_init(&exec, DRM_EXEC_IGNORE_DUPLICATES); drm_exec_until_all_locked(&exec) { @@ -102,11 +129,12 @@ static void test_duplicates(struct kunit *test) static void test_prepare(struct kunit *test) { + struct drm_exec_priv *priv = test->priv; struct drm_gem_object gobj = { }; struct drm_exec exec; int ret; - drm_gem_private_object_init(&dev, &gobj, PAGE_SIZE); + drm_gem_private_object_init(priv->drm, &gobj, PAGE_SIZE); drm_exec_init(&exec, DRM_EXEC_INTERRUPTIBLE_WAIT); drm_exec_until_all_locked(&exec) { @@ -121,14 +149,15 @@ static void test_prepare(struct kunit *test) static void test_prepare_array(struct kunit *test) { + struct drm_exec_priv *priv = test->priv; struct drm_gem_object gobj1 = { }; struct drm_gem_object gobj2 = { }; struct drm_gem_object *array[] = { &gobj1, &gobj2 }; struct drm_exec exec; int ret; - drm_gem_private_object_init(&dev, &gobj1, PAGE_SIZE); - drm_gem_private_object_init(&dev, &gobj2, PAGE_SIZE); + drm_gem_private_object_init(priv->drm, &gobj1, PAGE_SIZE); + drm_gem_private_object_init(priv->drm, &gobj2, PAGE_SIZE); drm_exec_init(&exec, DRM_EXEC_INTERRUPTIBLE_WAIT); drm_exec_until_all_locked(&exec) @@ -150,6 +179,7 @@ static struct kunit_case drm_exec_tests[] = { static struct kunit_suite drm_exec_test_suite = { .name = "drm_exec", + .init = drm_exec_test_init, .test_cases = drm_exec_tests, }; diff --git a/drivers/gpu/drm/tests/drm_kunit_helpers.c b/drivers/gpu/drm/tests/drm_kunit_helpers.c index 4df47071dc88..3d624ff2f651 100644 --- a/drivers/gpu/drm/tests/drm_kunit_helpers.c +++ b/drivers/gpu/drm/tests/drm_kunit_helpers.c @@ -1,5 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 +#include <drm/drm_atomic.h> #include <drm/drm_drv.h> #include <drm/drm_kunit_helpers.h> #include <drm/drm_managed.h> @@ -26,6 +27,28 @@ static struct platform_driver fake_platform_driver = { }, }; +static void kunit_action_platform_driver_unregister(void *ptr) +{ + struct platform_driver *drv = ptr; + + platform_driver_unregister(drv); + +} + +static void kunit_action_platform_device_put(void *ptr) +{ + struct platform_device *pdev = ptr; + + platform_device_put(pdev); +} + +static void kunit_action_platform_device_del(void *ptr) +{ + struct platform_device *pdev = ptr; + + platform_device_del(pdev); +} + /** * drm_kunit_helper_alloc_device - Allocate a mock device for a KUnit test * @test: The test context object @@ -35,8 +58,8 @@ static struct platform_driver fake_platform_driver = { * able to leverage the usual infrastructure and most notably the * device-managed resources just like a "real" device. * - * Callers need to make sure drm_kunit_helper_free_device() on the - * device when done. + * Resources will be cleaned up automatically, but the removal can be + * forced using @drm_kunit_helper_free_device. * * Returns: * A pointer to the new device, or an ERR_PTR() otherwise. @@ -49,12 +72,27 @@ struct device *drm_kunit_helper_alloc_device(struct kunit *test) ret = platform_driver_register(&fake_platform_driver); KUNIT_ASSERT_EQ(test, ret, 0); + ret = kunit_add_action_or_reset(test, + kunit_action_platform_driver_unregister, + &fake_platform_driver); + KUNIT_ASSERT_EQ(test, ret, 0); + pdev = platform_device_alloc(KUNIT_DEVICE_NAME, PLATFORM_DEVID_NONE); KUNIT_ASSERT_NOT_ERR_OR_NULL(test, pdev); + ret = kunit_add_action_or_reset(test, + kunit_action_platform_device_put, + pdev); + KUNIT_ASSERT_EQ(test, ret, 0); + ret = platform_device_add(pdev); KUNIT_ASSERT_EQ(test, ret, 0); + ret = kunit_add_action_or_reset(test, + kunit_action_platform_device_del, + pdev); + KUNIT_ASSERT_EQ(test, ret, 0); + return &pdev->dev; } EXPORT_SYMBOL_GPL(drm_kunit_helper_alloc_device); @@ -70,8 +108,17 @@ void drm_kunit_helper_free_device(struct kunit *test, struct device *dev) { struct platform_device *pdev = to_platform_device(dev); - platform_device_unregister(pdev); - platform_driver_unregister(&fake_platform_driver); + kunit_release_action(test, + kunit_action_platform_device_del, + pdev); + + kunit_release_action(test, + kunit_action_platform_device_put, + pdev); + + kunit_release_action(test, + kunit_action_platform_driver_unregister, + pdev); } EXPORT_SYMBOL_GPL(drm_kunit_helper_free_device); @@ -100,5 +147,91 @@ __drm_kunit_helper_alloc_drm_device_with_driver(struct kunit *test, } EXPORT_SYMBOL_GPL(__drm_kunit_helper_alloc_drm_device_with_driver); +static void action_drm_release_context(void *ptr) +{ + struct drm_modeset_acquire_ctx *ctx = ptr; + + drm_modeset_drop_locks(ctx); + drm_modeset_acquire_fini(ctx); +} + +/** + * drm_kunit_helper_context_alloc - Allocates an acquire context + * @test: The test context object + * + * Allocates and initializes a modeset acquire context. + * + * The context is tied to the kunit test context, so we must not call + * drm_modeset_acquire_fini() on it, it will be done so automatically. + * + * Returns: + * An ERR_PTR on error, a pointer to the newly allocated context otherwise + */ +struct drm_modeset_acquire_ctx * +drm_kunit_helper_acquire_ctx_alloc(struct kunit *test) +{ + struct drm_modeset_acquire_ctx *ctx; + int ret; + + ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, ctx); + + drm_modeset_acquire_init(ctx, 0); + + ret = kunit_add_action_or_reset(test, + action_drm_release_context, + ctx); + if (ret) + return ERR_PTR(ret); + + return ctx; +} +EXPORT_SYMBOL_GPL(drm_kunit_helper_acquire_ctx_alloc); + +static void kunit_action_drm_atomic_state_put(void *ptr) +{ + struct drm_atomic_state *state = ptr; + + drm_atomic_state_put(state); +} + +/** + * drm_kunit_helper_atomic_state_alloc - Allocates an atomic state + * @test: The test context object + * @drm: The device to alloc the state for + * @ctx: Locking context for that atomic update + * + * Allocates a empty atomic state. + * + * The state is tied to the kunit test context, so we must not call + * drm_atomic_state_put() on it, it will be done so automatically. + * + * Returns: + * An ERR_PTR on error, a pointer to the newly allocated state otherwise + */ +struct drm_atomic_state * +drm_kunit_helper_atomic_state_alloc(struct kunit *test, + struct drm_device *drm, + struct drm_modeset_acquire_ctx *ctx) +{ + struct drm_atomic_state *state; + int ret; + + state = drm_atomic_state_alloc(drm); + if (!state) + return ERR_PTR(-ENOMEM); + + ret = kunit_add_action_or_reset(test, + kunit_action_drm_atomic_state_put, + state); + if (ret) + return ERR_PTR(ret); + + state->acquire_ctx = ctx; + + return state; +} +EXPORT_SYMBOL_GPL(drm_kunit_helper_atomic_state_alloc); + MODULE_AUTHOR("Maxime Ripard <maxime@cerno.tech>"); MODULE_LICENSE("GPL"); diff --git a/drivers/gpu/drm/tests/drm_modes_test.c b/drivers/gpu/drm/tests/drm_modes_test.c index bc4aa2ce78be..1e9f63fbfead 100644 --- a/drivers/gpu/drm/tests/drm_modes_test.c +++ b/drivers/gpu/drm/tests/drm_modes_test.c @@ -36,13 +36,6 @@ static int drm_test_modes_init(struct kunit *test) return 0; } -static void drm_test_modes_exit(struct kunit *test) -{ - struct drm_test_modes_priv *priv = test->priv; - - drm_kunit_helper_free_device(test, priv->dev); -} - static void drm_test_modes_analog_tv_ntsc_480i(struct kunit *test) { struct drm_test_modes_priv *priv = test->priv; @@ -148,7 +141,6 @@ static struct kunit_case drm_modes_analog_tv_tests[] = { static struct kunit_suite drm_modes_analog_tv_test_suite = { .name = "drm_modes_analog_tv", .init = drm_test_modes_init, - .exit = drm_test_modes_exit, .test_cases = drm_modes_analog_tv_tests, }; diff --git a/drivers/gpu/drm/tests/drm_probe_helper_test.c b/drivers/gpu/drm/tests/drm_probe_helper_test.c index 0ee65828623e..1a2044070a6c 100644 --- a/drivers/gpu/drm/tests/drm_probe_helper_test.c +++ b/drivers/gpu/drm/tests/drm_probe_helper_test.c @@ -60,13 +60,6 @@ static int drm_probe_helper_test_init(struct kunit *test) return 0; } -static void drm_probe_helper_test_exit(struct kunit *test) -{ - struct drm_probe_helper_test_priv *priv = test->priv; - - drm_kunit_helper_free_device(test, priv->dev); -} - typedef struct drm_display_mode *(*expected_mode_func_t)(struct drm_device *); struct drm_connector_helper_tv_get_modes_test { @@ -208,7 +201,6 @@ static struct kunit_case drm_test_connector_helper_tv_get_modes_tests[] = { static struct kunit_suite drm_test_connector_helper_tv_get_modes_suite = { .name = "drm_connector_helper_tv_get_modes", .init = drm_probe_helper_test_init, - .exit = drm_probe_helper_test_exit, .test_cases = drm_test_connector_helper_tv_get_modes_tests, }; diff --git a/drivers/gpu/drm/tidss/tidss_plane.c b/drivers/gpu/drm/tidss/tidss_plane.c index 6bdd6e4a955a..e1c0ef0c3894 100644 --- a/drivers/gpu/drm/tidss/tidss_plane.c +++ b/drivers/gpu/drm/tidss/tidss_plane.c @@ -38,7 +38,8 @@ static int tidss_plane_atomic_check(struct drm_plane *plane, if (!new_plane_state->crtc) { /* * The visible field is not reset by the DRM core but only - * updated by drm_plane_helper_check_state(), set it manually. + * updated by drm_atomic_helper_check_plane_state(), set it + * manually. */ new_plane_state->visible = false; return 0; diff --git a/drivers/gpu/drm/tiny/ili9225.c b/drivers/gpu/drm/tiny/ili9225.c index 077c6ff5a2e1..4ceb68ffac4b 100644 --- a/drivers/gpu/drm/tiny/ili9225.c +++ b/drivers/gpu/drm/tiny/ili9225.c @@ -316,19 +316,24 @@ static int ili9225_dbi_command(struct mipi_dbi *dbi, u8 *cmd, u8 *par, u32 speed_hz; int ret; + spi_bus_lock(spi->controller); gpiod_set_value_cansleep(dbi->dc, 0); speed_hz = mipi_dbi_spi_cmd_max_speed(spi, 1); ret = mipi_dbi_spi_transfer(spi, speed_hz, 8, cmd, 1); + spi_bus_unlock(spi->controller); if (ret || !num) return ret; if (*cmd == ILI9225_WRITE_DATA_TO_GRAM && !dbi->swap_bytes) bpw = 16; + spi_bus_lock(spi->controller); gpiod_set_value_cansleep(dbi->dc, 1); speed_hz = mipi_dbi_spi_cmd_max_speed(spi, num); + ret = mipi_dbi_spi_transfer(spi, speed_hz, bpw, par, num); + spi_bus_unlock(spi->controller); - return mipi_dbi_spi_transfer(spi, speed_hz, bpw, par, num); + return ret; } static const struct drm_simple_display_pipe_funcs ili9225_pipe_funcs = { diff --git a/drivers/gpu/drm/tiny/ili9486.c b/drivers/gpu/drm/tiny/ili9486.c index 02265c898816..938bceed5999 100644 --- a/drivers/gpu/drm/tiny/ili9486.c +++ b/drivers/gpu/drm/tiny/ili9486.c @@ -59,9 +59,11 @@ static int waveshare_command(struct mipi_dbi *mipi, u8 *cmd, u8 *par, * before being transferred as 8-bit on the big endian SPI bus. */ buf[0] = cpu_to_be16(*cmd); + spi_bus_lock(spi->controller); gpiod_set_value_cansleep(mipi->dc, 0); speed_hz = mipi_dbi_spi_cmd_max_speed(spi, 2); ret = mipi_dbi_spi_transfer(spi, speed_hz, 8, buf, 2); + spi_bus_unlock(spi->controller); if (ret || !num) goto free; @@ -79,9 +81,11 @@ static int waveshare_command(struct mipi_dbi *mipi, u8 *cmd, u8 *par, if (*cmd == MIPI_DCS_WRITE_MEMORY_START && !mipi->swap_bytes) bpw = 16; + spi_bus_lock(spi->controller); gpiod_set_value_cansleep(mipi->dc, 1); speed_hz = mipi_dbi_spi_cmd_max_speed(spi, num); ret = mipi_dbi_spi_transfer(spi, speed_hz, bpw, data, num); + spi_bus_unlock(spi->controller); free: kfree(buf); diff --git a/drivers/gpu/drm/tiny/panel-mipi-dbi.c b/drivers/gpu/drm/tiny/panel-mipi-dbi.c index eb9f13f18a02..f80a141fcf36 100644 --- a/drivers/gpu/drm/tiny/panel-mipi-dbi.c +++ b/drivers/gpu/drm/tiny/panel-mipi-dbi.c @@ -307,7 +307,8 @@ static int panel_mipi_dbi_spi_probe(struct spi_device *spi) if (IS_ERR(dbi->reset)) return dev_err_probe(dev, PTR_ERR(dbi->reset), "Failed to get GPIO 'reset'\n"); - dc = devm_gpiod_get_optional(dev, "dc", GPIOD_OUT_LOW); + /* Multiple panels can share the "dc" GPIO, but only if they are on the same SPI bus! */ + dc = devm_gpiod_get_optional(dev, "dc", GPIOD_OUT_LOW | GPIOD_FLAGS_BIT_NONEXCLUSIVE); if (IS_ERR(dc)) return dev_err_probe(dev, PTR_ERR(dc), "Failed to get GPIO 'dc'\n"); diff --git a/drivers/gpu/drm/tiny/repaper.c b/drivers/gpu/drm/tiny/repaper.c index c2677d081a7b..13ae148f59b9 100644 --- a/drivers/gpu/drm/tiny/repaper.c +++ b/drivers/gpu/drm/tiny/repaper.c @@ -533,7 +533,7 @@ static int repaper_fb_dirty(struct drm_framebuffer *fb) DRM_DEBUG("Flushing [FB:%d] st=%ums\n", fb->base.id, epd->factored_stage_time); - buf = kmalloc_array(fb->width, fb->height, GFP_KERNEL); + buf = kmalloc(fb->width * fb->height / 8, GFP_KERNEL); if (!buf) { ret = -ENOMEM; goto out_exit; diff --git a/drivers/gpu/drm/udl/udl_modeset.c b/drivers/gpu/drm/udl/udl_modeset.c index aa02fd2789c3..40876bcdd79a 100644 --- a/drivers/gpu/drm/udl/udl_modeset.c +++ b/drivers/gpu/drm/udl/udl_modeset.c @@ -12,6 +12,7 @@ #include <drm/drm_atomic.h> #include <drm/drm_atomic_helper.h> +#include <drm/drm_crtc_helper.h> #include <drm/drm_damage_helper.h> #include <drm/drm_drv.h> #include <drm/drm_edid.h> @@ -310,16 +311,6 @@ static const struct drm_plane_funcs udl_primary_plane_funcs = { * CRTC */ -static int udl_crtc_helper_atomic_check(struct drm_crtc *crtc, struct drm_atomic_state *state) -{ - struct drm_crtc_state *new_crtc_state = drm_atomic_get_new_crtc_state(state, crtc); - - if (!new_crtc_state->enable) - return 0; - - return drm_atomic_helper_check_crtc_primary_plane(new_crtc_state); -} - static void udl_crtc_helper_atomic_enable(struct drm_crtc *crtc, struct drm_atomic_state *state) { struct drm_device *dev = crtc->dev; @@ -381,7 +372,7 @@ out: } static const struct drm_crtc_helper_funcs udl_crtc_helper_funcs = { - .atomic_check = udl_crtc_helper_atomic_check, + .atomic_check = drm_crtc_helper_atomic_check, .atomic_enable = udl_crtc_helper_atomic_enable, .atomic_disable = udl_crtc_helper_atomic_disable, }; diff --git a/drivers/gpu/drm/v3d/v3d_drv.h b/drivers/gpu/drm/v3d/v3d_drv.h index b74b1351bfc8..7f664a4b2a75 100644 --- a/drivers/gpu/drm/v3d/v3d_drv.h +++ b/drivers/gpu/drm/v3d/v3d_drv.h @@ -340,7 +340,7 @@ struct v3d_submit_ext { static inline unsigned long nsecs_to_jiffies_timeout(const u64 n) { /* nsecs_to_jiffies64() does not guard against overflow */ - if (NSEC_PER_SEC % HZ && + if ((NSEC_PER_SEC % HZ) != 0 && div_u64(n, NSEC_PER_SEC) >= MAX_JIFFY_OFFSET / HZ) return MAX_JIFFY_OFFSET; diff --git a/drivers/gpu/drm/vc4/tests/vc4_mock.c b/drivers/gpu/drm/vc4/tests/vc4_mock.c index a4bed26af32f..63ca46f4cb35 100644 --- a/drivers/gpu/drm/vc4/tests/vc4_mock.c +++ b/drivers/gpu/drm/vc4/tests/vc4_mock.c @@ -153,6 +153,13 @@ static int __build_mock(struct kunit *test, struct drm_device *drm, return 0; } +static void kunit_action_drm_dev_unregister(void *ptr) +{ + struct drm_device *drm = ptr; + + drm_dev_unregister(drm); +} + static struct vc4_dev *__mock_device(struct kunit *test, bool is_vc5) { struct drm_device *drm; @@ -186,6 +193,11 @@ static struct vc4_dev *__mock_device(struct kunit *test, bool is_vc5) ret = drm_dev_register(drm, 0); KUNIT_ASSERT_EQ(test, ret, 0); + ret = kunit_add_action_or_reset(test, + kunit_action_drm_dev_unregister, + drm); + KUNIT_ASSERT_EQ(test, ret, 0); + return vc4; } diff --git a/drivers/gpu/drm/vc4/tests/vc4_test_pv_muxing.c b/drivers/gpu/drm/vc4/tests/vc4_test_pv_muxing.c index ae0bd0f81698..61622e951031 100644 --- a/drivers/gpu/drm/vc4/tests/vc4_test_pv_muxing.c +++ b/drivers/gpu/drm/vc4/tests/vc4_test_pv_muxing.c @@ -20,7 +20,6 @@ struct pv_muxing_priv { struct vc4_dev *vc4; - struct drm_modeset_acquire_ctx ctx; struct drm_atomic_state *state; }; @@ -725,7 +724,7 @@ static void drm_vc4_test_pv_muxing_invalid(struct kunit *test) static int vc4_pv_muxing_test_init(struct kunit *test) { const struct pv_muxing_param *params = test->param_value; - struct drm_atomic_state *state; + struct drm_modeset_acquire_ctx *ctx; struct pv_muxing_priv *priv; struct drm_device *drm; struct vc4_dev *vc4; @@ -738,33 +737,16 @@ static int vc4_pv_muxing_test_init(struct kunit *test) KUNIT_ASSERT_NOT_ERR_OR_NULL(test, vc4); priv->vc4 = vc4; - drm_modeset_acquire_init(&priv->ctx, 0); + ctx = drm_kunit_helper_acquire_ctx_alloc(test); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx); drm = &vc4->base; - state = drm_atomic_state_alloc(drm); - KUNIT_ASSERT_NOT_ERR_OR_NULL(test, state); - - state->acquire_ctx = &priv->ctx; - - priv->state = state; + priv->state = drm_kunit_helper_atomic_state_alloc(test, drm, ctx); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, priv->state); return 0; } -static void vc4_pv_muxing_test_exit(struct kunit *test) -{ - struct pv_muxing_priv *priv = test->priv; - struct vc4_dev *vc4 = priv->vc4; - struct drm_device *drm = &vc4->base; - struct drm_atomic_state *state = priv->state; - - drm_atomic_state_put(state); - drm_modeset_drop_locks(&priv->ctx); - drm_modeset_acquire_fini(&priv->ctx); - drm_dev_unregister(drm); - drm_kunit_helper_free_device(test, vc4->dev); -} - static struct kunit_case vc4_pv_muxing_tests[] = { KUNIT_CASE_PARAM(drm_vc4_test_pv_muxing, vc4_test_pv_muxing_gen_params), @@ -776,7 +758,6 @@ static struct kunit_case vc4_pv_muxing_tests[] = { static struct kunit_suite vc4_pv_muxing_test_suite = { .name = "vc4-pv-muxing-combinations", .init = vc4_pv_muxing_test_init, - .exit = vc4_pv_muxing_test_exit, .test_cases = vc4_pv_muxing_tests, }; @@ -791,7 +772,6 @@ static struct kunit_case vc5_pv_muxing_tests[] = { static struct kunit_suite vc5_pv_muxing_test_suite = { .name = "vc5-pv-muxing-combinations", .init = vc4_pv_muxing_test_init, - .exit = vc4_pv_muxing_test_exit, .test_cases = vc5_pv_muxing_tests, }; @@ -802,7 +782,7 @@ static struct kunit_suite vc5_pv_muxing_test_suite = { */ static void drm_test_vc5_pv_muxing_bugs_subsequent_crtc_enable(struct kunit *test) { - struct drm_modeset_acquire_ctx ctx; + struct drm_modeset_acquire_ctx *ctx; struct drm_atomic_state *state; struct vc4_crtc_state *new_vc4_crtc_state; struct vc4_hvs_state *new_hvs_state; @@ -815,14 +795,13 @@ static void drm_test_vc5_pv_muxing_bugs_subsequent_crtc_enable(struct kunit *tes vc4 = vc5_mock_device(test); KUNIT_ASSERT_NOT_ERR_OR_NULL(test, vc4); - drm_modeset_acquire_init(&ctx, 0); + ctx = drm_kunit_helper_acquire_ctx_alloc(test); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx); drm = &vc4->base; - state = drm_atomic_state_alloc(drm); + state = drm_kunit_helper_atomic_state_alloc(test, drm, ctx); KUNIT_ASSERT_NOT_ERR_OR_NULL(test, state); - state->acquire_ctx = &ctx; - ret = vc4_mock_atomic_add_output(test, state, VC4_ENCODER_TYPE_HDMI0); KUNIT_ASSERT_EQ(test, ret, 0); @@ -843,13 +822,9 @@ static void drm_test_vc5_pv_muxing_bugs_subsequent_crtc_enable(struct kunit *tes ret = drm_atomic_helper_swap_state(state, false); KUNIT_ASSERT_EQ(test, ret, 0); - drm_atomic_state_put(state); - - state = drm_atomic_state_alloc(drm); + state = drm_kunit_helper_atomic_state_alloc(test, drm, ctx); KUNIT_ASSERT_NOT_ERR_OR_NULL(test, state); - state->acquire_ctx = &ctx; - ret = vc4_mock_atomic_add_output(test, state, VC4_ENCODER_TYPE_HDMI1); KUNIT_ASSERT_EQ(test, ret, 0); @@ -868,17 +843,18 @@ static void drm_test_vc5_pv_muxing_bugs_subsequent_crtc_enable(struct kunit *tes KUNIT_ASSERT_TRUE(test, new_hvs_state->fifo_state[hdmi1_channel].in_use); KUNIT_EXPECT_NE(test, hdmi0_channel, hdmi1_channel); - - drm_atomic_state_put(state); - drm_modeset_drop_locks(&ctx); - drm_modeset_acquire_fini(&ctx); - drm_dev_unregister(drm); - drm_kunit_helper_free_device(test, vc4->dev); } +/* + * This test makes sure that we never change the FIFO of an active HVS + * channel if we disable a FIFO with a lower index. + * + * Doing so would result in a FIFO stall and would disrupt an output + * supposed to be unaffected by the commit. + */ static void drm_test_vc5_pv_muxing_bugs_stable_fifo(struct kunit *test) { - struct drm_modeset_acquire_ctx ctx; + struct drm_modeset_acquire_ctx *ctx; struct drm_atomic_state *state; struct vc4_crtc_state *new_vc4_crtc_state; struct vc4_hvs_state *new_hvs_state; @@ -891,14 +867,13 @@ static void drm_test_vc5_pv_muxing_bugs_stable_fifo(struct kunit *test) vc4 = vc5_mock_device(test); KUNIT_ASSERT_NOT_ERR_OR_NULL(test, vc4); - drm_modeset_acquire_init(&ctx, 0); + ctx = drm_kunit_helper_acquire_ctx_alloc(test); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx); drm = &vc4->base; - state = drm_atomic_state_alloc(drm); + state = drm_kunit_helper_atomic_state_alloc(test, drm, ctx); KUNIT_ASSERT_NOT_ERR_OR_NULL(test, state); - state->acquire_ctx = &ctx; - ret = vc4_mock_atomic_add_output(test, state, VC4_ENCODER_TYPE_HDMI0); KUNIT_ASSERT_EQ(test, ret, 0); @@ -930,13 +905,9 @@ static void drm_test_vc5_pv_muxing_bugs_stable_fifo(struct kunit *test) ret = drm_atomic_helper_swap_state(state, false); KUNIT_ASSERT_EQ(test, ret, 0); - drm_atomic_state_put(state); - - state = drm_atomic_state_alloc(drm); + state = drm_kunit_helper_atomic_state_alloc(test, drm, ctx); KUNIT_ASSERT_NOT_ERR_OR_NULL(test, state); - state->acquire_ctx = &ctx; - ret = vc4_mock_atomic_del_output(test, state, VC4_ENCODER_TYPE_HDMI0); KUNIT_ASSERT_EQ(test, ret, 0); @@ -958,18 +929,27 @@ static void drm_test_vc5_pv_muxing_bugs_stable_fifo(struct kunit *test) KUNIT_EXPECT_EQ(test, old_hdmi1_channel, hdmi1_channel); } - - drm_atomic_state_put(state); - drm_modeset_drop_locks(&ctx); - drm_modeset_acquire_fini(&ctx); - drm_dev_unregister(drm); - drm_kunit_helper_free_device(test, vc4->dev); } +/* + * Test that if we affect a single output, only the CRTC state of that + * output will be pulled in the global atomic state. + * + * This is relevant for two things: + * + * - If we don't have that state at all, we are unlikely to affect the + * FIFO muxing. This is somewhat redundant with + * drm_test_vc5_pv_muxing_bugs_stable_fifo() + * + * - KMS waits for page flips to occur on all the CRTC found in the + * CRTC state. Since the CRTC is unaffected, we would over-wait, but + * most importantly run into corner cases like waiting on an + * inactive CRTC that never completes. + */ static void drm_test_vc5_pv_muxing_bugs_subsequent_crtc_enable_too_many_crtc_state(struct kunit *test) { - struct drm_modeset_acquire_ctx ctx; + struct drm_modeset_acquire_ctx *ctx; struct drm_atomic_state *state; struct vc4_crtc_state *new_vc4_crtc_state; struct drm_device *drm; @@ -979,14 +959,13 @@ drm_test_vc5_pv_muxing_bugs_subsequent_crtc_enable_too_many_crtc_state(struct ku vc4 = vc5_mock_device(test); KUNIT_ASSERT_NOT_ERR_OR_NULL(test, vc4); - drm_modeset_acquire_init(&ctx, 0); + ctx = drm_kunit_helper_acquire_ctx_alloc(test); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx); drm = &vc4->base; - state = drm_atomic_state_alloc(drm); + state = drm_kunit_helper_atomic_state_alloc(test, drm, ctx); KUNIT_ASSERT_NOT_ERR_OR_NULL(test, state); - state->acquire_ctx = &ctx; - ret = vc4_mock_atomic_add_output(test, state, VC4_ENCODER_TYPE_HDMI0); KUNIT_ASSERT_EQ(test, ret, 0); @@ -996,13 +975,9 @@ drm_test_vc5_pv_muxing_bugs_subsequent_crtc_enable_too_many_crtc_state(struct ku ret = drm_atomic_helper_swap_state(state, false); KUNIT_ASSERT_EQ(test, ret, 0); - drm_atomic_state_put(state); - - state = drm_atomic_state_alloc(drm); + state = drm_kunit_helper_atomic_state_alloc(test, drm, ctx); KUNIT_ASSERT_NOT_ERR_OR_NULL(test, state); - state->acquire_ctx = &ctx; - ret = vc4_mock_atomic_add_output(test, state, VC4_ENCODER_TYPE_HDMI1); KUNIT_ASSERT_EQ(test, ret, 0); @@ -1012,12 +987,6 @@ drm_test_vc5_pv_muxing_bugs_subsequent_crtc_enable_too_many_crtc_state(struct ku new_vc4_crtc_state = get_vc4_crtc_state_for_encoder(test, state, VC4_ENCODER_TYPE_HDMI0); KUNIT_EXPECT_NULL(test, new_vc4_crtc_state); - - drm_atomic_state_put(state); - drm_modeset_drop_locks(&ctx); - drm_modeset_acquire_fini(&ctx); - drm_dev_unregister(drm); - drm_kunit_helper_free_device(test, vc4->dev); } static struct kunit_case vc5_pv_muxing_bugs_tests[] = { diff --git a/drivers/gpu/drm/virtio/virtgpu_drv.c b/drivers/gpu/drm/virtio/virtgpu_drv.c index a7ec5a3770da..644b8ee51009 100644 --- a/drivers/gpu/drm/virtio/virtgpu_drv.c +++ b/drivers/gpu/drm/virtio/virtgpu_drv.c @@ -176,7 +176,8 @@ static const struct drm_driver driver = { * If KMS is disabled DRIVER_MODESET and DRIVER_ATOMIC are masked * out via drm_device::driver_features: */ - .driver_features = DRIVER_MODESET | DRIVER_GEM | DRIVER_RENDER | DRIVER_ATOMIC, + .driver_features = DRIVER_MODESET | DRIVER_GEM | DRIVER_RENDER | DRIVER_ATOMIC | + DRIVER_SYNCOBJ | DRIVER_SYNCOBJ_TIMELINE, .open = virtio_gpu_driver_open, .postclose = virtio_gpu_driver_postclose, diff --git a/drivers/gpu/drm/virtio/virtgpu_submit.c b/drivers/gpu/drm/virtio/virtgpu_submit.c index 1d010c66910d..3c00135ead45 100644 --- a/drivers/gpu/drm/virtio/virtgpu_submit.c +++ b/drivers/gpu/drm/virtio/virtgpu_submit.c @@ -14,11 +14,24 @@ #include <linux/uaccess.h> #include <drm/drm_file.h> +#include <drm/drm_syncobj.h> #include <drm/virtgpu_drm.h> #include "virtgpu_drv.h" +struct virtio_gpu_submit_post_dep { + struct drm_syncobj *syncobj; + struct dma_fence_chain *chain; + u64 point; +}; + struct virtio_gpu_submit { + struct virtio_gpu_submit_post_dep *post_deps; + unsigned int num_out_syncobjs; + + struct drm_syncobj **in_syncobjs; + unsigned int num_in_syncobjs; + struct virtio_gpu_object_array *buflist; struct drm_virtgpu_execbuffer *exbuf; struct virtio_gpu_fence *out_fence; @@ -59,6 +72,203 @@ static int virtio_gpu_dma_fence_wait(struct virtio_gpu_submit *submit, return 0; } +static void virtio_gpu_free_syncobjs(struct drm_syncobj **syncobjs, + u32 nr_syncobjs) +{ + u32 i = nr_syncobjs; + + while (i--) { + if (syncobjs[i]) + drm_syncobj_put(syncobjs[i]); + } + + kvfree(syncobjs); +} + +static int +virtio_gpu_parse_deps(struct virtio_gpu_submit *submit) +{ + struct drm_virtgpu_execbuffer *exbuf = submit->exbuf; + struct drm_virtgpu_execbuffer_syncobj syncobj_desc; + size_t syncobj_stride = exbuf->syncobj_stride; + u32 num_in_syncobjs = exbuf->num_in_syncobjs; + struct drm_syncobj **syncobjs; + int ret = 0, i; + + if (!num_in_syncobjs) + return 0; + + /* + * kvalloc at first tries to allocate memory using kmalloc and + * falls back to vmalloc only on failure. It also uses __GFP_NOWARN + * internally for allocations larger than a page size, preventing + * storm of KMSG warnings. + */ + syncobjs = kvcalloc(num_in_syncobjs, sizeof(*syncobjs), GFP_KERNEL); + if (!syncobjs) + return -ENOMEM; + + for (i = 0; i < num_in_syncobjs; i++) { + u64 address = exbuf->in_syncobjs + i * syncobj_stride; + struct dma_fence *fence; + + memset(&syncobj_desc, 0, sizeof(syncobj_desc)); + + if (copy_from_user(&syncobj_desc, + u64_to_user_ptr(address), + min(syncobj_stride, sizeof(syncobj_desc)))) { + ret = -EFAULT; + break; + } + + if (syncobj_desc.flags & ~VIRTGPU_EXECBUF_SYNCOBJ_FLAGS) { + ret = -EINVAL; + break; + } + + ret = drm_syncobj_find_fence(submit->file, syncobj_desc.handle, + syncobj_desc.point, 0, &fence); + if (ret) + break; + + ret = virtio_gpu_dma_fence_wait(submit, fence); + + dma_fence_put(fence); + if (ret) + break; + + if (syncobj_desc.flags & VIRTGPU_EXECBUF_SYNCOBJ_RESET) { + syncobjs[i] = drm_syncobj_find(submit->file, + syncobj_desc.handle); + if (!syncobjs[i]) { + ret = -EINVAL; + break; + } + } + } + + if (ret) { + virtio_gpu_free_syncobjs(syncobjs, i); + return ret; + } + + submit->num_in_syncobjs = num_in_syncobjs; + submit->in_syncobjs = syncobjs; + + return ret; +} + +static void virtio_gpu_reset_syncobjs(struct drm_syncobj **syncobjs, + u32 nr_syncobjs) +{ + u32 i; + + for (i = 0; i < nr_syncobjs; i++) { + if (syncobjs[i]) + drm_syncobj_replace_fence(syncobjs[i], NULL); + } +} + +static void +virtio_gpu_free_post_deps(struct virtio_gpu_submit_post_dep *post_deps, + u32 nr_syncobjs) +{ + u32 i = nr_syncobjs; + + while (i--) { + kfree(post_deps[i].chain); + drm_syncobj_put(post_deps[i].syncobj); + } + + kvfree(post_deps); +} + +static int virtio_gpu_parse_post_deps(struct virtio_gpu_submit *submit) +{ + struct drm_virtgpu_execbuffer *exbuf = submit->exbuf; + struct drm_virtgpu_execbuffer_syncobj syncobj_desc; + struct virtio_gpu_submit_post_dep *post_deps; + u32 num_out_syncobjs = exbuf->num_out_syncobjs; + size_t syncobj_stride = exbuf->syncobj_stride; + int ret = 0, i; + + if (!num_out_syncobjs) + return 0; + + post_deps = kvcalloc(num_out_syncobjs, sizeof(*post_deps), GFP_KERNEL); + if (!post_deps) + return -ENOMEM; + + for (i = 0; i < num_out_syncobjs; i++) { + u64 address = exbuf->out_syncobjs + i * syncobj_stride; + + memset(&syncobj_desc, 0, sizeof(syncobj_desc)); + + if (copy_from_user(&syncobj_desc, + u64_to_user_ptr(address), + min(syncobj_stride, sizeof(syncobj_desc)))) { + ret = -EFAULT; + break; + } + + post_deps[i].point = syncobj_desc.point; + + if (syncobj_desc.flags) { + ret = -EINVAL; + break; + } + + if (syncobj_desc.point) { + post_deps[i].chain = dma_fence_chain_alloc(); + if (!post_deps[i].chain) { + ret = -ENOMEM; + break; + } + } + + post_deps[i].syncobj = drm_syncobj_find(submit->file, + syncobj_desc.handle); + if (!post_deps[i].syncobj) { + kfree(post_deps[i].chain); + ret = -EINVAL; + break; + } + } + + if (ret) { + virtio_gpu_free_post_deps(post_deps, i); + return ret; + } + + submit->num_out_syncobjs = num_out_syncobjs; + submit->post_deps = post_deps; + + return 0; +} + +static void +virtio_gpu_process_post_deps(struct virtio_gpu_submit *submit) +{ + struct virtio_gpu_submit_post_dep *post_deps = submit->post_deps; + + if (post_deps) { + struct dma_fence *fence = &submit->out_fence->f; + u32 i; + + for (i = 0; i < submit->num_out_syncobjs; i++) { + if (post_deps[i].chain) { + drm_syncobj_add_point(post_deps[i].syncobj, + post_deps[i].chain, + fence, post_deps[i].point); + post_deps[i].chain = NULL; + } else { + drm_syncobj_replace_fence(post_deps[i].syncobj, + fence); + } + } + } +} + static int virtio_gpu_fence_event_create(struct drm_device *dev, struct drm_file *file, struct virtio_gpu_fence *fence, @@ -118,6 +328,10 @@ static int virtio_gpu_init_submit_buflist(struct virtio_gpu_submit *submit) static void virtio_gpu_cleanup_submit(struct virtio_gpu_submit *submit) { + virtio_gpu_reset_syncobjs(submit->in_syncobjs, submit->num_in_syncobjs); + virtio_gpu_free_syncobjs(submit->in_syncobjs, submit->num_in_syncobjs); + virtio_gpu_free_post_deps(submit->post_deps, submit->num_out_syncobjs); + if (!IS_ERR(submit->buf)) kvfree(submit->buf); @@ -172,6 +386,7 @@ static int virtio_gpu_init_submit(struct virtio_gpu_submit *submit, drm_fence_event = false; if ((exbuf->flags & VIRTGPU_EXECBUF_FENCE_FD_OUT) || + exbuf->num_out_syncobjs || exbuf->num_bo_handles || drm_fence_event) out_fence = virtio_gpu_fence_alloc(vgdev, fence_ctx, ring_idx); @@ -291,6 +506,14 @@ int virtio_gpu_execbuffer_ioctl(struct drm_device *dev, void *data, if (ret) goto cleanup; + ret = virtio_gpu_parse_post_deps(&submit); + if (ret) + goto cleanup; + + ret = virtio_gpu_parse_deps(&submit); + if (ret) + goto cleanup; + /* * Await in-fences in the end of the job submission path to * optimize the path by proceeding directly to the submission @@ -311,6 +534,7 @@ int virtio_gpu_execbuffer_ioctl(struct drm_device *dev, void *data, * the job submission path. */ virtio_gpu_install_out_fence_fd(&submit); + virtio_gpu_process_post_deps(&submit); virtio_gpu_complete_submit(&submit); cleanup: virtio_gpu_cleanup_submit(&submit); diff --git a/drivers/gpu/drm/vkms/vkms_composer.c b/drivers/gpu/drm/vkms/vkms_composer.c index d170a8e89b95..d5d4f642d367 100644 --- a/drivers/gpu/drm/vkms/vkms_composer.c +++ b/drivers/gpu/drm/vkms/vkms_composer.c @@ -6,6 +6,7 @@ #include <drm/drm_atomic_helper.h> #include <drm/drm_blend.h> #include <drm/drm_fourcc.h> +#include <drm/drm_fixed.h> #include <drm/drm_gem_framebuffer_helper.h> #include <drm/drm_vblank.h> #include <linux/minmax.h> @@ -89,6 +90,73 @@ static void fill_background(const struct pixel_argb_u16 *background_color, output_buffer->pixels[i] = *background_color; } +// lerp(a, b, t) = a + (b - a) * t +static u16 lerp_u16(u16 a, u16 b, s64 t) +{ + s64 a_fp = drm_int2fixp(a); + s64 b_fp = drm_int2fixp(b); + + s64 delta = drm_fixp_mul(b_fp - a_fp, t); + + return drm_fixp2int(a_fp + delta); +} + +static s64 get_lut_index(const struct vkms_color_lut *lut, u16 channel_value) +{ + s64 color_channel_fp = drm_int2fixp(channel_value); + + return drm_fixp_mul(color_channel_fp, lut->channel_value2index_ratio); +} + +/* + * This enum is related to the positions of the variables inside + * `struct drm_color_lut`, so the order of both needs to be the same. + */ +enum lut_channel { + LUT_RED = 0, + LUT_GREEN, + LUT_BLUE, + LUT_RESERVED +}; + +static u16 apply_lut_to_channel_value(const struct vkms_color_lut *lut, u16 channel_value, + enum lut_channel channel) +{ + s64 lut_index = get_lut_index(lut, channel_value); + + /* + * This checks if `struct drm_color_lut` has any gap added by the compiler + * between the struct fields. + */ + static_assert(sizeof(struct drm_color_lut) == sizeof(__u16) * 4); + + u16 *floor_lut_value = (__u16 *)&lut->base[drm_fixp2int(lut_index)]; + u16 *ceil_lut_value = (__u16 *)&lut->base[drm_fixp2int_ceil(lut_index)]; + + u16 floor_channel_value = floor_lut_value[channel]; + u16 ceil_channel_value = ceil_lut_value[channel]; + + return lerp_u16(floor_channel_value, ceil_channel_value, + lut_index & DRM_FIXED_DECIMAL_MASK); +} + +static void apply_lut(const struct vkms_crtc_state *crtc_state, struct line_buffer *output_buffer) +{ + if (!crtc_state->gamma_lut.base) + return; + + if (!crtc_state->gamma_lut.lut_length) + return; + + for (size_t x = 0; x < output_buffer->n_pixels; x++) { + struct pixel_argb_u16 *pixel = &output_buffer->pixels[x]; + + pixel->r = apply_lut_to_channel_value(&crtc_state->gamma_lut, pixel->r, LUT_RED); + pixel->g = apply_lut_to_channel_value(&crtc_state->gamma_lut, pixel->g, LUT_GREEN); + pixel->b = apply_lut_to_channel_value(&crtc_state->gamma_lut, pixel->b, LUT_BLUE); + } +} + /** * blend - blend the pixels from all planes and compute crc * @wb: The writeback frame buffer metadata @@ -130,6 +198,8 @@ static void blend(struct vkms_writeback_job *wb, output_buffer); } + apply_lut(crtc_state, output_buffer); + *crc32 = crc32_le(*crc32, (void *)output_buffer->pixels, row_size); if (wb) @@ -244,6 +314,22 @@ void vkms_composer_worker(struct work_struct *work) crtc_state->frame_start = 0; crtc_state->frame_end = 0; crtc_state->crc_pending = false; + + if (crtc->state->gamma_lut) { + s64 max_lut_index_fp; + s64 u16_max_fp = drm_int2fixp(0xffff); + + crtc_state->gamma_lut.base = (struct drm_color_lut *)crtc->state->gamma_lut->data; + crtc_state->gamma_lut.lut_length = + crtc->state->gamma_lut->length / sizeof(struct drm_color_lut); + max_lut_index_fp = drm_int2fixp(crtc_state->gamma_lut.lut_length - 1); + crtc_state->gamma_lut.channel_value2index_ratio = drm_fixp_div(max_lut_index_fp, + u16_max_fp); + + } else { + crtc_state->gamma_lut.base = NULL; + } + spin_unlock_irq(&out->composer_lock); /* @@ -322,10 +408,15 @@ void vkms_set_composer(struct vkms_output *out, bool enabled) if (enabled) drm_crtc_vblank_get(&out->crtc); - spin_lock_irq(&out->lock); + mutex_lock(&out->enabled_lock); old_enabled = out->composer_enabled; out->composer_enabled = enabled; - spin_unlock_irq(&out->lock); + + /* the composition wasn't enabled, so unlock the lock to make sure the lock + * will be balanced even if we have a failed commit + */ + if (!out->composer_enabled) + mutex_unlock(&out->enabled_lock); if (old_enabled) drm_crtc_vblank_put(&out->crtc); diff --git a/drivers/gpu/drm/vkms/vkms_crtc.c b/drivers/gpu/drm/vkms/vkms_crtc.c index 515f6772b866..3c5ebf106b66 100644 --- a/drivers/gpu/drm/vkms/vkms_crtc.c +++ b/drivers/gpu/drm/vkms/vkms_crtc.c @@ -16,7 +16,7 @@ static enum hrtimer_restart vkms_vblank_simulate(struct hrtimer *timer) struct drm_crtc *crtc = &output->crtc; struct vkms_crtc_state *state; u64 ret_overrun; - bool ret, fence_cookie; + bool ret, fence_cookie, composer_enabled; fence_cookie = dma_fence_begin_signalling(); @@ -25,15 +25,15 @@ static enum hrtimer_restart vkms_vblank_simulate(struct hrtimer *timer) if (ret_overrun != 1) pr_warn("%s: vblank timer overrun\n", __func__); - spin_lock(&output->lock); ret = drm_crtc_handle_vblank(crtc); if (!ret) DRM_ERROR("vkms failure on handling vblank"); state = output->composer_state; - spin_unlock(&output->lock); + composer_enabled = output->composer_enabled; + mutex_unlock(&output->enabled_lock); - if (state && output->composer_enabled) { + if (state && composer_enabled) { u64 frame = drm_crtc_accurate_vblank_count(crtc); /* update frame_start only if a queued vkms_composer_worker() @@ -290,8 +290,12 @@ int vkms_crtc_init(struct drm_device *dev, struct drm_crtc *crtc, drm_crtc_helper_add(crtc, &vkms_crtc_helper_funcs); + drm_mode_crtc_set_gamma_size(crtc, VKMS_LUT_SIZE); + drm_crtc_enable_color_mgmt(crtc, 0, false, VKMS_LUT_SIZE); + spin_lock_init(&vkms_out->lock); spin_lock_init(&vkms_out->composer_lock); + mutex_init(&vkms_out->enabled_lock); vkms_out->composer_workq = alloc_ordered_workqueue("vkms_composer", 0); if (!vkms_out->composer_workq) diff --git a/drivers/gpu/drm/vkms/vkms_drv.c b/drivers/gpu/drm/vkms/vkms_drv.c index e3c9c9571c8d..dd0af086e7fa 100644 --- a/drivers/gpu/drm/vkms/vkms_drv.c +++ b/drivers/gpu/drm/vkms/vkms_drv.c @@ -120,9 +120,27 @@ static const struct drm_driver vkms_driver = { .minor = DRIVER_MINOR, }; +static int vkms_atomic_check(struct drm_device *dev, struct drm_atomic_state *state) +{ + struct drm_crtc *crtc; + struct drm_crtc_state *new_crtc_state; + int i; + + for_each_new_crtc_in_state(state, crtc, new_crtc_state, i) { + if (!new_crtc_state->gamma_lut || !new_crtc_state->color_mgmt_changed) + continue; + + if (new_crtc_state->gamma_lut->length / sizeof(struct drm_color_lut *) + > VKMS_LUT_SIZE) + return -EINVAL; + } + + return drm_atomic_helper_check(dev, state); +} + static const struct drm_mode_config_funcs vkms_mode_funcs = { .fb_create = drm_gem_fb_create, - .atomic_check = drm_atomic_helper_check, + .atomic_check = vkms_atomic_check, .atomic_commit = drm_atomic_helper_commit, }; diff --git a/drivers/gpu/drm/vkms/vkms_drv.h b/drivers/gpu/drm/vkms/vkms_drv.h index d25e0aae91f2..c7ae6c2ba1df 100644 --- a/drivers/gpu/drm/vkms/vkms_drv.h +++ b/drivers/gpu/drm/vkms/vkms_drv.h @@ -23,6 +23,8 @@ #define NUM_OVERLAY_PLANES 8 +#define VKMS_LUT_SIZE 256 + struct vkms_frame_info { struct drm_framebuffer *fb; struct drm_rect src, dst; @@ -64,6 +66,12 @@ struct vkms_plane { struct drm_plane base; }; +struct vkms_color_lut { + struct drm_color_lut *base; + size_t lut_length; + s64 channel_value2index_ratio; +}; + /** * vkms_crtc_state - Driver specific CRTC state * @base: base CRTC state @@ -79,6 +87,7 @@ struct vkms_crtc_state { /* stack of active planes for crc computation, should be in z order */ struct vkms_plane_state **active_planes; struct vkms_writeback_job *active_writeback; + struct vkms_color_lut gamma_lut; /* below four are protected by vkms_output.composer_lock */ bool crc_pending; @@ -99,8 +108,10 @@ struct vkms_output { struct workqueue_struct *composer_workq; /* protects concurrent access to composer */ spinlock_t lock; + /* guarantees that if the composer is enabled, a job will be queued */ + struct mutex enabled_lock; - /* protected by @lock */ + /* protected by @enabled_lock */ bool composer_enabled; struct vkms_crtc_state *composer_state; diff --git a/drivers/gpu/host1x/context.c b/drivers/gpu/host1x/context.c index c000d4e0c2c6..a3f336edd991 100644 --- a/drivers/gpu/host1x/context.c +++ b/drivers/gpu/host1x/context.c @@ -79,6 +79,14 @@ int host1x_memory_context_list_init(struct host1x *host1x) !device_iommu_mapped(&ctx->dev)) { dev_err(host1x->dev, "Context device %d has no IOMMU!\n", i); device_unregister(&ctx->dev); + + /* + * This means that if IOMMU is disabled but context devices + * are defined in the device tree, Host1x will fail to probe. + * That's probably OK in this time and age. + */ + err = -EINVAL; + goto unreg_devices; } } diff --git a/drivers/gpu/ipu-v3/ipu-pre.c b/drivers/gpu/ipu-v3/ipu-pre.c index ad82c9e0252f..aef984a43190 100644 --- a/drivers/gpu/ipu-v3/ipu-pre.c +++ b/drivers/gpu/ipu-v3/ipu-pre.c @@ -271,15 +271,13 @@ u32 ipu_pre_get_baddr(struct ipu_pre *pre) static int ipu_pre_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; - struct resource *res; struct ipu_pre *pre; pre = devm_kzalloc(dev, sizeof(*pre), GFP_KERNEL); if (!pre) return -ENOMEM; - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - pre->regs = devm_ioremap_resource(&pdev->dev, res); + pre->regs = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(pre->regs)) return PTR_ERR(pre->regs); diff --git a/drivers/gpu/ipu-v3/ipu-prg.c b/drivers/gpu/ipu-v3/ipu-prg.c index 196797c1b4b3..729605709955 100644 --- a/drivers/gpu/ipu-v3/ipu-prg.c +++ b/drivers/gpu/ipu-v3/ipu-prg.c @@ -358,7 +358,6 @@ EXPORT_SYMBOL_GPL(ipu_prg_channel_configure_pending); static int ipu_prg_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; - struct resource *res; struct ipu_prg *prg; u32 val; int i, ret; @@ -367,12 +366,10 @@ static int ipu_prg_probe(struct platform_device *pdev) if (!prg) return -ENOMEM; - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - prg->regs = devm_ioremap_resource(&pdev->dev, res); + prg->regs = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(prg->regs)) return PTR_ERR(prg->regs); - prg->clk_ipg = devm_clk_get(dev, "ipg"); if (IS_ERR(prg->clk_ipg)) return PTR_ERR(prg->clk_ipg); diff --git a/drivers/hid/i2c-hid/Kconfig b/drivers/hid/i2c-hid/Kconfig index 3be17109301a..ef7c595c9403 100644 --- a/drivers/hid/i2c-hid/Kconfig +++ b/drivers/hid/i2c-hid/Kconfig @@ -9,6 +9,7 @@ if I2C_HID config I2C_HID_ACPI tristate "HID over I2C transport layer ACPI driver" depends on ACPI + depends on DRM || !DRM select I2C_HID_CORE help Say Y here if you use a keyboard, a touchpad, a touchscreen, or any @@ -25,6 +26,7 @@ config I2C_HID_OF tristate "HID over I2C transport layer Open Firmware driver" # No "depends on OF" because this can also be used for manually # (board-file) instantiated "hid-over-i2c" type i2c-clients. + depends on DRM || !DRM select I2C_HID_CORE help Say Y here if you use a keyboard, a touchpad, a touchscreen, or any @@ -41,6 +43,7 @@ config I2C_HID_OF config I2C_HID_OF_ELAN tristate "Driver for Elan hid-i2c based devices on OF systems" depends on OF + depends on DRM || !DRM select I2C_HID_CORE help Say Y here if you want support for Elan i2c devices that use @@ -56,6 +59,7 @@ config I2C_HID_OF_ELAN config I2C_HID_OF_GOODIX tristate "Driver for Goodix hid-i2c based devices on OF systems" depends on OF + depends on DRM || !DRM select I2C_HID_CORE help Say Y here if you want support for Goodix i2c devices that use @@ -70,5 +74,7 @@ config I2C_HID_OF_GOODIX config I2C_HID_CORE tristate + # We need to call into panel code so if DRM=m, this can't be 'y' + depends on DRM || !DRM endif diff --git a/drivers/hid/i2c-hid/i2c-hid-core.c b/drivers/hid/i2c-hid/i2c-hid-core.c index efbba0465eef..9601c0605fd9 100644 --- a/drivers/hid/i2c-hid/i2c-hid-core.c +++ b/drivers/hid/i2c-hid/i2c-hid-core.c @@ -38,6 +38,8 @@ #include <linux/mutex.h> #include <asm/unaligned.h> +#include <drm/drm_panel.h> + #include "../hid-ids.h" #include "i2c-hid.h" @@ -107,6 +109,10 @@ struct i2c_hid { struct mutex reset_lock; struct i2chid_ops *ops; + struct drm_panel_follower panel_follower; + struct work_struct panel_follower_prepare_work; + bool is_panel_follower; + bool prepare_work_finished; }; static const struct i2c_hid_quirks { @@ -855,7 +861,8 @@ static int i2c_hid_init_irq(struct i2c_client *client) irqflags = IRQF_TRIGGER_LOW; ret = request_threaded_irq(client->irq, NULL, i2c_hid_irq, - irqflags | IRQF_ONESHOT, client->name, ihid); + irqflags | IRQF_ONESHOT | IRQF_NO_AUTOEN, + client->name, ihid); if (ret < 0) { dev_warn(&client->dev, "Could not register for %s interrupt, irq = %d," @@ -940,6 +947,239 @@ static void i2c_hid_core_shutdown_tail(struct i2c_hid *ihid) ihid->ops->shutdown_tail(ihid->ops); } +static int i2c_hid_core_suspend(struct i2c_hid *ihid, bool force_poweroff) +{ + struct i2c_client *client = ihid->client; + struct hid_device *hid = ihid->hid; + int ret; + + ret = hid_driver_suspend(hid, PMSG_SUSPEND); + if (ret < 0) + return ret; + + /* Save some power */ + i2c_hid_set_power(ihid, I2C_HID_PWR_SLEEP); + + disable_irq(client->irq); + + if (force_poweroff || !device_may_wakeup(&client->dev)) + i2c_hid_core_power_down(ihid); + + return 0; +} + +static int i2c_hid_core_resume(struct i2c_hid *ihid) +{ + struct i2c_client *client = ihid->client; + struct hid_device *hid = ihid->hid; + int ret; + + if (!device_may_wakeup(&client->dev)) + i2c_hid_core_power_up(ihid); + + enable_irq(client->irq); + + /* Instead of resetting device, simply powers the device on. This + * solves "incomplete reports" on Raydium devices 2386:3118 and + * 2386:4B33 and fixes various SIS touchscreens no longer sending + * data after a suspend/resume. + * + * However some ALPS touchpads generate IRQ storm without reset, so + * let's still reset them here. + */ + if (ihid->quirks & I2C_HID_QUIRK_RESET_ON_RESUME) + ret = i2c_hid_hwreset(ihid); + else + ret = i2c_hid_set_power(ihid, I2C_HID_PWR_ON); + + if (ret) + return ret; + + return hid_driver_reset_resume(hid); +} + +/** + * __do_i2c_hid_core_initial_power_up() - First time power up of the i2c-hid device. + * @ihid: The ihid object created during probe. + * + * This function is called at probe time. + * + * The initial power on is where we do some basic validation that the device + * exists, where we fetch the HID descriptor, and where we create the actual + * HID devices. + * + * Return: 0 or error code. + */ +static int __do_i2c_hid_core_initial_power_up(struct i2c_hid *ihid) +{ + struct i2c_client *client = ihid->client; + struct hid_device *hid = ihid->hid; + int ret; + + ret = i2c_hid_core_power_up(ihid); + if (ret) + return ret; + + /* Make sure there is something at this address */ + ret = i2c_smbus_read_byte(client); + if (ret < 0) { + i2c_hid_dbg(ihid, "nothing at this address: %d\n", ret); + ret = -ENXIO; + goto err; + } + + ret = i2c_hid_fetch_hid_descriptor(ihid); + if (ret < 0) { + dev_err(&client->dev, + "Failed to fetch the HID Descriptor\n"); + goto err; + } + + enable_irq(client->irq); + + hid->version = le16_to_cpu(ihid->hdesc.bcdVersion); + hid->vendor = le16_to_cpu(ihid->hdesc.wVendorID); + hid->product = le16_to_cpu(ihid->hdesc.wProductID); + + hid->initial_quirks |= i2c_hid_get_dmi_quirks(hid->vendor, + hid->product); + + snprintf(hid->name, sizeof(hid->name), "%s %04X:%04X", + client->name, (u16)hid->vendor, (u16)hid->product); + strscpy(hid->phys, dev_name(&client->dev), sizeof(hid->phys)); + + ihid->quirks = i2c_hid_lookup_quirk(hid->vendor, hid->product); + + ret = hid_add_device(hid); + if (ret) { + if (ret != -ENODEV) + hid_err(client, "can't add hid device: %d\n", ret); + goto err; + } + + return 0; + +err: + i2c_hid_core_power_down(ihid); + return ret; +} + +static void ihid_core_panel_prepare_work(struct work_struct *work) +{ + struct i2c_hid *ihid = container_of(work, struct i2c_hid, + panel_follower_prepare_work); + struct hid_device *hid = ihid->hid; + int ret; + + /* + * hid->version is set on the first power up. If it's still zero then + * this is the first power on so we should perform initial power up + * steps. + */ + if (!hid->version) + ret = __do_i2c_hid_core_initial_power_up(ihid); + else + ret = i2c_hid_core_resume(ihid); + + if (ret) + dev_warn(&ihid->client->dev, "Power on failed: %d\n", ret); + else + WRITE_ONCE(ihid->prepare_work_finished, true); + + /* + * The work APIs provide a number of memory ordering guarantees + * including one that says that memory writes before schedule_work() + * are always visible to the work function, but they don't appear to + * guarantee that a write that happened in the work is visible after + * cancel_work_sync(). We'll add a write memory barrier here to match + * with i2c_hid_core_panel_unpreparing() to ensure that our write to + * prepare_work_finished is visible there. + */ + smp_wmb(); +} + +static int i2c_hid_core_panel_prepared(struct drm_panel_follower *follower) +{ + struct i2c_hid *ihid = container_of(follower, struct i2c_hid, panel_follower); + + /* + * Powering on a touchscreen can be a slow process. Queue the work to + * the system workqueue so we don't block the panel's power up. + */ + WRITE_ONCE(ihid->prepare_work_finished, false); + schedule_work(&ihid->panel_follower_prepare_work); + + return 0; +} + +static int i2c_hid_core_panel_unpreparing(struct drm_panel_follower *follower) +{ + struct i2c_hid *ihid = container_of(follower, struct i2c_hid, panel_follower); + + cancel_work_sync(&ihid->panel_follower_prepare_work); + + /* Match with ihid_core_panel_prepare_work() */ + smp_rmb(); + if (!READ_ONCE(ihid->prepare_work_finished)) + return 0; + + return i2c_hid_core_suspend(ihid, true); +} + +static const struct drm_panel_follower_funcs i2c_hid_core_panel_follower_funcs = { + .panel_prepared = i2c_hid_core_panel_prepared, + .panel_unpreparing = i2c_hid_core_panel_unpreparing, +}; + +static int i2c_hid_core_register_panel_follower(struct i2c_hid *ihid) +{ + struct device *dev = &ihid->client->dev; + int ret; + + ihid->is_panel_follower = true; + ihid->panel_follower.funcs = &i2c_hid_core_panel_follower_funcs; + + /* + * If we're not in control of our own power up/power down then we can't + * do the logic to manage wakeups. Give a warning if a user thought + * that was possible then force the capability off. + */ + if (device_can_wakeup(dev)) { + dev_warn(dev, "Can't wakeup if following panel\n"); + device_set_wakeup_capable(dev, false); + } + + ret = drm_panel_add_follower(dev, &ihid->panel_follower); + if (ret) + return ret; + + return 0; +} + +static int i2c_hid_core_initial_power_up(struct i2c_hid *ihid) +{ + /* + * If we're a panel follower, we'll register and do our initial power + * up when the panel turns on; otherwise we do it right away. + */ + if (drm_is_panel_follower(&ihid->client->dev)) + return i2c_hid_core_register_panel_follower(ihid); + else + return __do_i2c_hid_core_initial_power_up(ihid); +} + +static void i2c_hid_core_final_power_down(struct i2c_hid *ihid) +{ + /* + * If we're a follower, the act of unfollowing will cause us to be + * powered down. Otherwise we need to manually do it. + */ + if (ihid->is_panel_follower) + drm_panel_remove_follower(&ihid->panel_follower); + else + i2c_hid_core_suspend(ihid, true); +} + int i2c_hid_core_probe(struct i2c_client *client, struct i2chid_ops *ops, u16 hid_descriptor_address, u32 quirks) { @@ -966,48 +1206,27 @@ int i2c_hid_core_probe(struct i2c_client *client, struct i2chid_ops *ops, if (!ihid) return -ENOMEM; - ihid->ops = ops; - - ret = i2c_hid_core_power_up(ihid); - if (ret) - return ret; - i2c_set_clientdata(client, ihid); + ihid->ops = ops; ihid->client = client; - ihid->wHIDDescRegister = cpu_to_le16(hid_descriptor_address); init_waitqueue_head(&ihid->wait); mutex_init(&ihid->reset_lock); + INIT_WORK(&ihid->panel_follower_prepare_work, ihid_core_panel_prepare_work); /* we need to allocate the command buffer without knowing the maximum * size of the reports. Let's use HID_MIN_BUFFER_SIZE, then we do the * real computation later. */ ret = i2c_hid_alloc_buffers(ihid, HID_MIN_BUFFER_SIZE); if (ret < 0) - goto err_powered; - + return ret; device_enable_async_suspend(&client->dev); - /* Make sure there is something at this address */ - ret = i2c_smbus_read_byte(client); - if (ret < 0) { - i2c_hid_dbg(ihid, "nothing at this address: %d\n", ret); - ret = -ENXIO; - goto err_powered; - } - - ret = i2c_hid_fetch_hid_descriptor(ihid); - if (ret < 0) { - dev_err(&client->dev, - "Failed to fetch the HID Descriptor\n"); - goto err_powered; - } - ret = i2c_hid_init_irq(client); if (ret < 0) - goto err_powered; + goto err_buffers_allocated; hid = hid_allocate_device(); if (IS_ERR(hid)) { @@ -1021,26 +1240,11 @@ int i2c_hid_core_probe(struct i2c_client *client, struct i2chid_ops *ops, hid->ll_driver = &i2c_hid_ll_driver; hid->dev.parent = &client->dev; hid->bus = BUS_I2C; - hid->version = le16_to_cpu(ihid->hdesc.bcdVersion); - hid->vendor = le16_to_cpu(ihid->hdesc.wVendorID); - hid->product = le16_to_cpu(ihid->hdesc.wProductID); - hid->initial_quirks = quirks; - hid->initial_quirks |= i2c_hid_get_dmi_quirks(hid->vendor, - hid->product); - - snprintf(hid->name, sizeof(hid->name), "%s %04X:%04X", - client->name, (u16)hid->vendor, (u16)hid->product); - strscpy(hid->phys, dev_name(&client->dev), sizeof(hid->phys)); - - ihid->quirks = i2c_hid_lookup_quirk(hid->vendor, hid->product); - ret = hid_add_device(hid); - if (ret) { - if (ret != -ENODEV) - hid_err(client, "can't add hid device: %d\n", ret); + ret = i2c_hid_core_initial_power_up(ihid); + if (ret) goto err_mem_free; - } return 0; @@ -1050,9 +1254,9 @@ err_mem_free: err_irq: free_irq(client->irq, ihid); -err_powered: - i2c_hid_core_power_down(ihid); +err_buffers_allocated: i2c_hid_free_buffers(ihid); + return ret; } EXPORT_SYMBOL_GPL(i2c_hid_core_probe); @@ -1062,6 +1266,8 @@ void i2c_hid_core_remove(struct i2c_client *client) struct i2c_hid *ihid = i2c_get_clientdata(client); struct hid_device *hid; + i2c_hid_core_final_power_down(ihid); + hid = ihid->hid; hid_destroy_device(hid); @@ -1069,8 +1275,6 @@ void i2c_hid_core_remove(struct i2c_client *client) if (ihid->bufsize) i2c_hid_free_buffers(ihid); - - i2c_hid_core_power_down(ihid); } EXPORT_SYMBOL_GPL(i2c_hid_core_remove); @@ -1085,63 +1289,30 @@ void i2c_hid_core_shutdown(struct i2c_client *client) } EXPORT_SYMBOL_GPL(i2c_hid_core_shutdown); -#ifdef CONFIG_PM_SLEEP -static int i2c_hid_core_suspend(struct device *dev) +static int i2c_hid_core_pm_suspend(struct device *dev) { struct i2c_client *client = to_i2c_client(dev); struct i2c_hid *ihid = i2c_get_clientdata(client); - struct hid_device *hid = ihid->hid; - int ret; - - ret = hid_driver_suspend(hid, PMSG_SUSPEND); - if (ret < 0) - return ret; - - /* Save some power */ - i2c_hid_set_power(ihid, I2C_HID_PWR_SLEEP); - disable_irq(client->irq); - - if (!device_may_wakeup(&client->dev)) - i2c_hid_core_power_down(ihid); + if (ihid->is_panel_follower) + return 0; - return 0; + return i2c_hid_core_suspend(ihid, false); } -static int i2c_hid_core_resume(struct device *dev) +static int i2c_hid_core_pm_resume(struct device *dev) { - int ret; struct i2c_client *client = to_i2c_client(dev); struct i2c_hid *ihid = i2c_get_clientdata(client); - struct hid_device *hid = ihid->hid; - - if (!device_may_wakeup(&client->dev)) - i2c_hid_core_power_up(ihid); - - enable_irq(client->irq); - - /* Instead of resetting device, simply powers the device on. This - * solves "incomplete reports" on Raydium devices 2386:3118 and - * 2386:4B33 and fixes various SIS touchscreens no longer sending - * data after a suspend/resume. - * - * However some ALPS touchpads generate IRQ storm without reset, so - * let's still reset them here. - */ - if (ihid->quirks & I2C_HID_QUIRK_RESET_ON_RESUME) - ret = i2c_hid_hwreset(ihid); - else - ret = i2c_hid_set_power(ihid, I2C_HID_PWR_ON); - if (ret) - return ret; + if (ihid->is_panel_follower) + return 0; - return hid_driver_reset_resume(hid); + return i2c_hid_core_resume(ihid); } -#endif const struct dev_pm_ops i2c_hid_core_pm = { - SET_SYSTEM_SLEEP_PM_OPS(i2c_hid_core_suspend, i2c_hid_core_resume) + SYSTEM_SLEEP_PM_OPS(i2c_hid_core_pm_suspend, i2c_hid_core_pm_resume) }; EXPORT_SYMBOL_GPL(i2c_hid_core_pm); diff --git a/drivers/of/property.c b/drivers/of/property.c index ddc75cd50825..cf8dacf3e3b8 100644 --- a/drivers/of/property.c +++ b/drivers/of/property.c @@ -1266,6 +1266,7 @@ DEFINE_SIMPLE_PROP(pwms, "pwms", "#pwm-cells") DEFINE_SIMPLE_PROP(resets, "resets", "#reset-cells") DEFINE_SIMPLE_PROP(leds, "leds", NULL) DEFINE_SIMPLE_PROP(backlight, "backlight", NULL) +DEFINE_SIMPLE_PROP(panel, "panel", NULL) DEFINE_SUFFIX_PROP(regulators, "-supply", NULL) DEFINE_SUFFIX_PROP(gpio, "-gpio", "#gpio-cells") @@ -1354,6 +1355,7 @@ static const struct supplier_bindings of_supplier_bindings[] = { { .parse_prop = parse_resets, }, { .parse_prop = parse_leds, }, { .parse_prop = parse_backlight, }, + { .parse_prop = parse_panel, }, { .parse_prop = parse_gpio_compat, }, { .parse_prop = parse_interrupts, }, { .parse_prop = parse_regulators, }, diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig index e5b1cc54cafa..b694d7669d32 100644 --- a/drivers/video/Kconfig +++ b/drivers/video/Kconfig @@ -63,7 +63,7 @@ if VT source "drivers/video/console/Kconfig" endif -if FB || SGI_NEWPORT_CONSOLE +if FB_CORE || SGI_NEWPORT_CONSOLE source "drivers/video/logo/Kconfig" endif diff --git a/drivers/video/fbdev/Kconfig b/drivers/video/fbdev/Kconfig index c0b0419e98b6..5f8392b4f2a1 100644 --- a/drivers/video/fbdev/Kconfig +++ b/drivers/video/fbdev/Kconfig @@ -48,7 +48,7 @@ menuconfig FB config FB_HECUBA tristate depends on FB - select FB_SYS_HELPERS_DEFERRED + select FB_SYSMEM_HELPERS_DEFERRED config FB_SVGALIB tristate @@ -1886,7 +1886,7 @@ config FB_VIRTUAL config XEN_FBDEV_FRONTEND tristate "Xen virtual frame buffer support" depends on FB && XEN - select FB_SYS_HELPERS_DEFERRED + select FB_SYSMEM_HELPERS_DEFERRED select XEN_XENBUS_FRONTEND default y help @@ -1897,7 +1897,7 @@ config XEN_FBDEV_FRONTEND config FB_METRONOME tristate "E-Ink Metronome/8track controller support" depends on FB - select FB_SYS_HELPERS_DEFERRED + select FB_SYSMEM_HELPERS_DEFERRED help This driver implements support for the E-Ink Metronome controller. The pre-release name for this device was 8track @@ -1979,7 +1979,7 @@ config FB_MX3 config FB_BROADSHEET tristate "E-Ink Broadsheet/Epson S1D13521 controller support" depends on FB && (ARCH_PXA || COMPILE_TEST) - select FB_SYS_HELPERS_DEFERRED + select FB_SYSMEM_HELPERS_DEFERRED help This driver implements support for the E-Ink Broadsheet controller. The release name for this device was Epson S1D13521 @@ -2021,7 +2021,7 @@ config FB_SSD1307 depends on FB && I2C depends on GPIOLIB || COMPILE_TEST select FB_BACKLIGHT - select FB_SYS_HELPERS_DEFERRED + select FB_SYSMEM_HELPERS_DEFERRED help This driver implements support for the Solomon SSD1307 OLED controller over I2C. diff --git a/drivers/video/fbdev/broadsheetfb.c b/drivers/video/fbdev/broadsheetfb.c index bace1f04fc8e..e857b15e9f5d 100644 --- a/drivers/video/fbdev/broadsheetfb.c +++ b/drivers/video/fbdev/broadsheetfb.c @@ -985,9 +985,9 @@ static void broadsheetfb_defio_damage_area(struct fb_info *info, u32 x, u32 y, broadsheetfb_dpy_update(par); } -FB_GEN_DEFAULT_DEFERRED_SYS_OPS(broadsheetfb, - broadsheetfb_defio_damage_range, - broadsheetfb_defio_damage_area) +FB_GEN_DEFAULT_DEFERRED_SYSMEM_OPS(broadsheetfb, + broadsheetfb_defio_damage_range, + broadsheetfb_defio_damage_area) static const struct fb_ops broadsheetfb_ops = { .owner = THIS_MODULE, diff --git a/drivers/video/fbdev/core/Kconfig b/drivers/video/fbdev/core/Kconfig index 85434381b6c6..baf7e852c75b 100644 --- a/drivers/video/fbdev/core/Kconfig +++ b/drivers/video/fbdev/core/Kconfig @@ -136,7 +136,7 @@ config FB_DEFERRED_IO bool depends on FB_CORE -config FB_DMA_HELPERS +config FB_DMAMEM_HELPERS bool depends on FB_CORE select FB_SYS_COPYAREA @@ -144,14 +144,14 @@ config FB_DMA_HELPERS select FB_SYS_FOPS select FB_SYS_IMAGEBLIT -config FB_IO_HELPERS +config FB_IOMEM_HELPERS bool depends on FB_CORE select FB_CFB_COPYAREA select FB_CFB_FILLRECT select FB_CFB_IMAGEBLIT -config FB_SYS_HELPERS +config FB_SYSMEM_HELPERS bool depends on FB_CORE select FB_SYS_COPYAREA @@ -159,11 +159,11 @@ config FB_SYS_HELPERS select FB_SYS_FOPS select FB_SYS_IMAGEBLIT -config FB_SYS_HELPERS_DEFERRED +config FB_SYSMEM_HELPERS_DEFERRED bool depends on FB_CORE select FB_DEFERRED_IO - select FB_SYS_HELPERS + select FB_SYSMEM_HELPERS config FB_BACKLIGHT tristate diff --git a/drivers/video/fbdev/core/fbcon.c b/drivers/video/fbdev/core/fbcon.c index 8e76bc246b38..9f265271b5b9 100644 --- a/drivers/video/fbdev/core/fbcon.c +++ b/drivers/video/fbdev/core/fbcon.c @@ -103,8 +103,8 @@ enum { static struct fbcon_display fb_display[MAX_NR_CONSOLES]; -struct fb_info *fbcon_registered_fb[FB_MAX]; -int fbcon_num_registered_fb; +static struct fb_info *fbcon_registered_fb[FB_MAX]; +static int fbcon_num_registered_fb; #define fbcon_for_each_registered_fb(i) \ for (i = 0; WARN_CONSOLE_UNLOCKED(), i < FB_MAX; i++) \ @@ -577,7 +577,7 @@ static void fbcon_prepare_logo(struct vc_data *vc, struct fb_info *info, if (scr_readw(r) != vc->vc_video_erase_char) break; if (r != q && new_rows >= rows + logo_lines) { - save = kzalloc(array3_size(logo_lines, new_cols, 2), + save = kmalloc(array3_size(logo_lines, new_cols, 2), GFP_KERNEL); if (save) { int i = min(cols, new_cols); diff --git a/drivers/video/fbdev/hecubafb.c b/drivers/video/fbdev/hecubafb.c index c4938554ea45..ef526ed4a2d9 100644 --- a/drivers/video/fbdev/hecubafb.c +++ b/drivers/video/fbdev/hecubafb.c @@ -135,9 +135,9 @@ static void hecubafb_defio_damage_area(struct fb_info *info, u32 x, u32 y, hecubafb_dpy_update(par); } -FB_GEN_DEFAULT_DEFERRED_SYS_OPS(hecubafb, - hecubafb_defio_damage_range, - hecubafb_defio_damage_area) +FB_GEN_DEFAULT_DEFERRED_SYSMEM_OPS(hecubafb, + hecubafb_defio_damage_range, + hecubafb_defio_damage_area) static const struct fb_ops hecubafb_ops = { .owner = THIS_MODULE, diff --git a/drivers/video/fbdev/metronomefb.c b/drivers/video/fbdev/metronomefb.c index eb15b9dbdec8..130394616a7c 100644 --- a/drivers/video/fbdev/metronomefb.c +++ b/drivers/video/fbdev/metronomefb.c @@ -498,9 +498,9 @@ static void metronomefb_defio_damage_area(struct fb_info *info, u32 x, u32 y, metronomefb_dpy_update(par); } -FB_GEN_DEFAULT_DEFERRED_SYS_OPS(metronomefb, - metronomefb_defio_damage_range, - metronomefb_defio_damage_area) +FB_GEN_DEFAULT_DEFERRED_SYSMEM_OPS(metronomefb, + metronomefb_defio_damage_range, + metronomefb_defio_damage_area) static const struct fb_ops metronomefb_ops = { .owner = THIS_MODULE, diff --git a/drivers/video/fbdev/ps3fb.c b/drivers/video/fbdev/ps3fb.c index 5aedc30c5f7e..64d291d6b153 100644 --- a/drivers/video/fbdev/ps3fb.c +++ b/drivers/video/fbdev/ps3fb.c @@ -1168,9 +1168,7 @@ static int ps3fb_probe(struct ps3_system_bus_device *dev) ps3_system_bus_set_drvdata(dev, info); - dev_info(info->device, "%s %s, using %u KiB of video memory\n", - dev_driver_string(info->dev), dev_name(info->dev), - info->fix.smem_len >> 10); + fb_info(info, "using %u KiB of video memory\n", info->fix.smem_len >> 10); task = kthread_run(ps3fbd, info, DEVICE_NAME); if (IS_ERR(task)) { diff --git a/drivers/video/fbdev/ssd1307fb.c b/drivers/video/fbdev/ssd1307fb.c index a2b939342a4f..5aee62434443 100644 --- a/drivers/video/fbdev/ssd1307fb.c +++ b/drivers/video/fbdev/ssd1307fb.c @@ -317,9 +317,9 @@ static void ssd1307fb_defio_damage_area(struct fb_info *info, u32 x, u32 y, ssd1307fb_update_rect(par, x, y, width, height); } -FB_GEN_DEFAULT_DEFERRED_SYS_OPS(ssd1307fb, - ssd1307fb_defio_damage_range, - ssd1307fb_defio_damage_area) +FB_GEN_DEFAULT_DEFERRED_SYSMEM_OPS(ssd1307fb, + ssd1307fb_defio_damage_range, + ssd1307fb_defio_damage_area) static const struct fb_ops ssd1307fb_ops = { .owner = THIS_MODULE, diff --git a/drivers/video/fbdev/xen-fbfront.c b/drivers/video/fbdev/xen-fbfront.c index 9a4c29cb1a80..66d4628a96ae 100644 --- a/drivers/video/fbdev/xen-fbfront.c +++ b/drivers/video/fbdev/xen-fbfront.c @@ -306,9 +306,9 @@ static void xenfb_defio_damage_area(struct fb_info *info, u32 x, u32 y, xenfb_refresh(xenfb_info, x, y, width, height); } -FB_GEN_DEFAULT_DEFERRED_SYS_OPS(xenfb, - xenfb_defio_damage_range, - xenfb_defio_damage_area) +FB_GEN_DEFAULT_DEFERRED_SYSMEM_OPS(xenfb, + xenfb_defio_damage_range, + xenfb_defio_damage_area) static const struct fb_ops xenfb_fb_ops = { .owner = THIS_MODULE, diff --git a/drivers/video/logo/Kconfig b/drivers/video/logo/Kconfig index 6d6f8c08792d..b7d94d1dd158 100644 --- a/drivers/video/logo/Kconfig +++ b/drivers/video/logo/Kconfig @@ -5,7 +5,7 @@ menuconfig LOGO bool "Bootup logo" - depends on FB || SGI_NEWPORT_CONSOLE + depends on FB_CORE || SGI_NEWPORT_CONSOLE help Enable and select frame buffer bootup logos. diff --git a/include/drm/drm_bridge.h b/include/drm/drm_bridge.h index bf964cdfb330..c339fc85fd07 100644 --- a/include/drm/drm_bridge.h +++ b/include/drm/drm_bridge.h @@ -36,6 +36,7 @@ struct drm_bridge; struct drm_bridge_timings; struct drm_connector; struct drm_display_info; +struct drm_minor; struct drm_panel; struct edid; struct i2c_adapter; @@ -949,4 +950,6 @@ static inline struct drm_bridge *drmm_of_get_bridge(struct drm_device *drm, } #endif +void drm_bridge_debugfs_init(struct drm_minor *minor); + #endif diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h index 8e1cbc75143e..8b48a1974da3 100644 --- a/include/drm/drm_crtc.h +++ b/include/drm/drm_crtc.h @@ -77,11 +77,6 @@ struct drm_plane_helper_funcs; * intended to indicate whether a full modeset is needed, rather than strictly * describing what has changed in a commit. See also: * drm_atomic_crtc_needs_modeset() - * - * WARNING: Transitional helpers (like drm_helper_crtc_mode_set() or - * drm_helper_crtc_mode_set_base()) do not maintain many of the derived control - * state like @plane_mask so drivers not converted over to atomic helpers should - * not rely on these being accurate! */ struct drm_crtc_state { /** @crtc: backpointer to the CRTC */ diff --git a/include/drm/drm_kunit_helpers.h b/include/drm/drm_kunit_helpers.h index ed013fdcc1ff..514c8a7a32f0 100644 --- a/include/drm/drm_kunit_helpers.h +++ b/include/drm/drm_kunit_helpers.h @@ -87,5 +87,12 @@ __drm_kunit_helper_alloc_drm_device(struct kunit *test, sizeof(_type), \ offsetof(_type, _member), \ _feat)) +struct drm_modeset_acquire_ctx * +drm_kunit_helper_acquire_ctx_alloc(struct kunit *test); + +struct drm_atomic_state * +drm_kunit_helper_atomic_state_alloc(struct kunit *test, + struct drm_device *drm, + struct drm_modeset_acquire_ctx *ctx); #endif // DRM_KUNIT_HELPERS_H_ diff --git a/include/drm/drm_modeset_helper_vtables.h b/include/drm/drm_modeset_helper_vtables.h index 965faf082a6d..e3c3ac615909 100644 --- a/include/drm/drm_modeset_helper_vtables.h +++ b/include/drm/drm_modeset_helper_vtables.h @@ -59,8 +59,8 @@ enum mode_set_atomic { /** * struct drm_crtc_helper_funcs - helper operations for CRTCs * - * These hooks are used by the legacy CRTC helpers, the transitional plane - * helpers and the new atomic modesetting helpers. + * These hooks are used by the legacy CRTC helpers and the new atomic + * modesetting helpers. */ struct drm_crtc_helper_funcs { /** @@ -216,9 +216,7 @@ struct drm_crtc_helper_funcs { * * This callback is used to update the display mode of a CRTC without * changing anything of the primary plane configuration. This fits the - * requirement of atomic and hence is used by the atomic helpers. It is - * also used by the transitional plane helpers to implement a - * @mode_set hook in drm_helper_crtc_mode_set(). + * requirement of atomic and hence is used by the atomic helpers. * * Note that the display pipe is completely off when this function is * called. Atomic drivers which need hardware to be running before they @@ -333,8 +331,8 @@ struct drm_crtc_helper_funcs { * all updated. Again the recommendation is to just call check helpers * until a maximal configuration is reached. * - * This callback is used by the atomic modeset helpers and by the - * transitional plane helpers, but it is optional. + * This callback is used by the atomic modeset helpers, but it is + * optional. * * NOTE: * @@ -373,8 +371,8 @@ struct drm_crtc_helper_funcs { * has picked. See drm_atomic_helper_commit_planes() for a discussion of * the tradeoffs and variants of plane commit helpers. * - * This callback is used by the atomic modeset helpers and by the - * transitional plane helpers, but it is optional. + * This callback is used by the atomic modeset helpers, but it is + * optional. */ void (*atomic_begin)(struct drm_crtc *crtc, struct drm_atomic_state *state); @@ -397,8 +395,8 @@ struct drm_crtc_helper_funcs { * has picked. See drm_atomic_helper_commit_planes() for a discussion of * the tradeoffs and variants of plane commit helpers. * - * This callback is used by the atomic modeset helpers and by the - * transitional plane helpers, but it is optional. + * This callback is used by the atomic modeset helpers, but it is + * optional. */ void (*atomic_flush)(struct drm_crtc *crtc, struct drm_atomic_state *state); @@ -507,8 +505,8 @@ static inline void drm_crtc_helper_add(struct drm_crtc *crtc, /** * struct drm_encoder_helper_funcs - helper operations for encoders * - * These hooks are used by the legacy CRTC helpers, the transitional plane - * helpers and the new atomic modesetting helpers. + * These hooks are used by the legacy CRTC helpers and the new atomic + * modesetting helpers. */ struct drm_encoder_helper_funcs { /** @@ -1185,8 +1183,7 @@ static inline void drm_connector_helper_add(struct drm_connector *connector, /** * struct drm_plane_helper_funcs - helper operations for planes * - * These functions are used by the atomic helpers and by the transitional plane - * helpers. + * These functions are used by the atomic helpers. */ struct drm_plane_helper_funcs { /** @@ -1221,9 +1218,8 @@ struct drm_plane_helper_funcs { * The helpers will call @cleanup_fb with matching arguments for every * successful call to this hook. * - * This callback is used by the atomic modeset helpers and by the - * transitional plane helpers, but it is optional. See @begin_fb_access - * for preparing per-commit resources. + * This callback is used by the atomic modeset helpers, but it is + * optional. See @begin_fb_access for preparing per-commit resources. * * RETURNS: * @@ -1240,8 +1236,8 @@ struct drm_plane_helper_funcs { * This hook is called to clean up any resources allocated for the given * framebuffer and plane configuration in @prepare_fb. * - * This callback is used by the atomic modeset helpers and by the - * transitional plane helpers, but it is optional. + * This callback is used by the atomic modeset helpers, but it is + * optional. */ void (*cleanup_fb)(struct drm_plane *plane, struct drm_plane_state *old_state); @@ -1295,8 +1291,8 @@ struct drm_plane_helper_funcs { * all updated. Again the recommendation is to just call check helpers * until a maximal configuration is reached. * - * This callback is used by the atomic modeset helpers and by the - * transitional plane helpers, but it is optional. + * This callback is used by the atomic modeset helpers, but it is + * optional. * * NOTE: * @@ -1326,8 +1322,7 @@ struct drm_plane_helper_funcs { * has picked. See drm_atomic_helper_commit_planes() for a discussion of * the tradeoffs and variants of plane commit helpers. * - * This callback is used by the atomic modeset helpers and by the - * transitional plane helpers, but it is optional. + * This callback is used by the atomic modeset helpers, but it is optional. */ void (*atomic_update)(struct drm_plane *plane, struct drm_atomic_state *state); @@ -1376,9 +1371,8 @@ struct drm_plane_helper_funcs { * has picked. See drm_atomic_helper_commit_planes() for a discussion of * the tradeoffs and variants of plane commit helpers. * - * This callback is used by the atomic modeset helpers and by the - * transitional plane helpers, but it is optional. It's intended to - * reverse the effects of @atomic_enable. + * This callback is used by the atomic modeset helpers, but it is + * optional. It's intended to reverse the effects of @atomic_enable. */ void (*atomic_disable)(struct drm_plane *plane, struct drm_atomic_state *state); diff --git a/include/drm/drm_panel.h b/include/drm/drm_panel.h index 432fab2347eb..10015891b056 100644 --- a/include/drm/drm_panel.h +++ b/include/drm/drm_panel.h @@ -27,12 +27,14 @@ #include <linux/err.h> #include <linux/errno.h> #include <linux/list.h> +#include <linux/mutex.h> struct backlight_device; struct dentry; struct device_node; struct drm_connector; struct drm_device; +struct drm_panel_follower; struct drm_panel; struct display_timing; @@ -144,6 +146,45 @@ struct drm_panel_funcs { void (*debugfs_init)(struct drm_panel *panel, struct dentry *root); }; +struct drm_panel_follower_funcs { + /** + * @panel_prepared: + * + * Called after the panel has been powered on. + */ + int (*panel_prepared)(struct drm_panel_follower *follower); + + /** + * @panel_unpreparing: + * + * Called before the panel is powered off. + */ + int (*panel_unpreparing)(struct drm_panel_follower *follower); +}; + +struct drm_panel_follower { + /** + * @funcs: + * + * Dependent device callbacks; should be initted by the caller. + */ + const struct drm_panel_follower_funcs *funcs; + + /** + * @list + * + * Used for linking into panel's list; set by drm_panel_add_follower(). + */ + struct list_head list; + + /** + * @panel + * + * The panel we're dependent on; set by drm_panel_add_follower(). + */ + struct drm_panel *panel; +}; + /** * struct drm_panel - DRM panel object */ @@ -190,6 +231,20 @@ struct drm_panel { struct list_head list; /** + * @followers: + * + * A list of struct drm_panel_follower dependent on this panel. + */ + struct list_head followers; + + /** + * @follower_lock: + * + * Lock for followers list. + */ + struct mutex follower_lock; + + /** * @prepare_prev_first: * * The previous controller should be prepared first, before the prepare @@ -198,6 +253,20 @@ struct drm_panel { * the panel is powered up. */ bool prepare_prev_first; + + /** + * @prepared: + * + * If true then the panel has been prepared. + */ + bool prepared; + + /** + * @enabled: + * + * If true then the panel has been enabled. + */ + bool enabled; }; void drm_panel_init(struct drm_panel *panel, struct device *dev, @@ -232,6 +301,33 @@ static inline int of_drm_get_panel_orientation(const struct device_node *np, } #endif +#if defined(CONFIG_DRM_PANEL) +bool drm_is_panel_follower(struct device *dev); +int drm_panel_add_follower(struct device *follower_dev, + struct drm_panel_follower *follower); +void drm_panel_remove_follower(struct drm_panel_follower *follower); +int devm_drm_panel_add_follower(struct device *follower_dev, + struct drm_panel_follower *follower); +#else +static inline bool drm_is_panel_follower(struct device *dev) +{ + return false; +} + +static inline int drm_panel_add_follower(struct device *follower_dev, + struct drm_panel_follower *follower) +{ + return -ENODEV; +} + +static inline void drm_panel_remove_follower(struct drm_panel_follower *follower) { } +static inline int devm_drm_panel_add_follower(struct device *follower_dev, + struct drm_panel_follower *follower) +{ + return -ENODEV; +} +#endif + #if IS_ENABLED(CONFIG_DRM_PANEL) && (IS_BUILTIN(CONFIG_BACKLIGHT_CLASS_DEVICE) || \ (IS_MODULE(CONFIG_DRM) && IS_MODULE(CONFIG_BACKLIGHT_CLASS_DEVICE))) int drm_panel_of_backlight(struct drm_panel *panel); diff --git a/include/drm/drm_plane.h b/include/drm/drm_plane.h index 51291983ea44..79d62856defb 100644 --- a/include/drm/drm_plane.h +++ b/include/drm/drm_plane.h @@ -56,7 +56,7 @@ struct drm_plane_state { /** * @crtc: * - * Currently bound CRTC, NULL if disabled. Do not this write directly, + * Currently bound CRTC, NULL if disabled. Do not write this directly, * use drm_atomic_set_crtc_for_plane() */ struct drm_crtc *crtc; diff --git a/include/drm/task_barrier.h b/include/drm/task_barrier.h index 087e3f649c52..f6e6ed529681 100644 --- a/include/drm/task_barrier.h +++ b/include/drm/task_barrier.h @@ -24,8 +24,8 @@ #include <linux/atomic.h> /* - * Reusable 2 PHASE task barrier (randevouz point) implementation for N tasks. - * Based on the Little book of sempahores - https://greenteapress.com/wp/semaphores/ + * Reusable 2 PHASE task barrier (rendez-vous point) implementation for N tasks. + * Based on the Little book of semaphores - https://greenteapress.com/wp/semaphores/ */ diff --git a/include/linux/fb.h b/include/linux/fb.h index 2ef7788311fc..16c3e6d6c55d 100644 --- a/include/linux/fb.h +++ b/include/linux/fb.h @@ -537,22 +537,22 @@ extern ssize_t fb_io_read(struct fb_info *info, char __user *buf, extern ssize_t fb_io_write(struct fb_info *info, const char __user *buf, size_t count, loff_t *ppos); -#define __FB_DEFAULT_IO_OPS_RDWR \ +#define __FB_DEFAULT_IOMEM_OPS_RDWR \ .fb_read = fb_io_read, \ .fb_write = fb_io_write -#define __FB_DEFAULT_IO_OPS_DRAW \ +#define __FB_DEFAULT_IOMEM_OPS_DRAW \ .fb_fillrect = cfb_fillrect, \ .fb_copyarea = cfb_copyarea, \ .fb_imageblit = cfb_imageblit -#define __FB_DEFAULT_IO_OPS_MMAP \ +#define __FB_DEFAULT_IOMEM_OPS_MMAP \ .fb_mmap = NULL /* default implementation */ -#define FB_DEFAULT_IO_OPS \ - __FB_DEFAULT_IO_OPS_RDWR, \ - __FB_DEFAULT_IO_OPS_DRAW, \ - __FB_DEFAULT_IO_OPS_MMAP +#define FB_DEFAULT_IOMEM_OPS \ + __FB_DEFAULT_IOMEM_OPS_RDWR, \ + __FB_DEFAULT_IOMEM_OPS_DRAW, \ + __FB_DEFAULT_IOMEM_OPS_MMAP /* * Helpers for framebuffers in system memory @@ -566,11 +566,11 @@ extern ssize_t fb_sys_read(struct fb_info *info, char __user *buf, extern ssize_t fb_sys_write(struct fb_info *info, const char __user *buf, size_t count, loff_t *ppos); -#define __FB_DEFAULT_SYS_OPS_RDWR \ +#define __FB_DEFAULT_SYSMEM_OPS_RDWR \ .fb_read = fb_sys_read, \ .fb_write = fb_sys_write -#define __FB_DEFAULT_SYS_OPS_DRAW \ +#define __FB_DEFAULT_SYSMEM_OPS_DRAW \ .fb_fillrect = sys_fillrect, \ .fb_copyarea = sys_copyarea, \ .fb_imageblit = sys_imageblit @@ -579,11 +579,11 @@ extern ssize_t fb_sys_write(struct fb_info *info, const char __user *buf, * Helpers for framebuffers in DMA-able memory */ -#define __FB_DEFAULT_DMA_OPS_RDWR \ +#define __FB_DEFAULT_DMAMEM_OPS_RDWR \ .fb_read = fb_sys_read, \ .fb_write = fb_sys_write -#define __FB_DEFAULT_DMA_OPS_DRAW \ +#define __FB_DEFAULT_DMAMEM_OPS_DRAW \ .fb_fillrect = sys_fillrect, \ .fb_copyarea = sys_copyarea, \ .fb_imageblit = sys_imageblit @@ -682,11 +682,11 @@ extern int fb_deferred_io_fsync(struct file *file, loff_t start, __damage_area(info, image->dx, image->dy, image->width, image->height); \ } -#define FB_GEN_DEFAULT_DEFERRED_IO_OPS(__prefix, __damage_range, __damage_area) \ +#define FB_GEN_DEFAULT_DEFERRED_IOMEM_OPS(__prefix, __damage_range, __damage_area) \ __FB_GEN_DEFAULT_DEFERRED_OPS_RDWR(__prefix, __damage_range, io) \ __FB_GEN_DEFAULT_DEFERRED_OPS_DRAW(__prefix, __damage_area, cfb) -#define FB_GEN_DEFAULT_DEFERRED_SYS_OPS(__prefix, __damage_range, __damage_area) \ +#define FB_GEN_DEFAULT_DEFERRED_SYSMEM_OPS(__prefix, __damage_range, __damage_area) \ __FB_GEN_DEFAULT_DEFERRED_OPS_RDWR(__prefix, __damage_range, sys) \ __FB_GEN_DEFAULT_DEFERRED_OPS_DRAW(__prefix, __damage_area, sys) diff --git a/include/uapi/drm/drm.h b/include/uapi/drm/drm.h index 863e47200911..794c1d857677 100644 --- a/include/uapi/drm/drm.h +++ b/include/uapi/drm/drm.h @@ -673,8 +673,11 @@ struct drm_gem_open { * Bitfield of supported PRIME sharing capabilities. See &DRM_PRIME_CAP_IMPORT * and &DRM_PRIME_CAP_EXPORT. * - * PRIME buffers are exposed as dma-buf file descriptors. See - * Documentation/gpu/drm-mm.rst, section "PRIME Buffer Sharing". + * Starting from kernel version 6.6, both &DRM_PRIME_CAP_IMPORT and + * &DRM_PRIME_CAP_EXPORT are always advertised. + * + * PRIME buffers are exposed as dma-buf file descriptors. + * See :ref:`prime_buffer_sharing`. */ #define DRM_CAP_PRIME 0x5 /** @@ -682,6 +685,8 @@ struct drm_gem_open { * * If this bit is set in &DRM_CAP_PRIME, the driver supports importing PRIME * buffers via the &DRM_IOCTL_PRIME_FD_TO_HANDLE ioctl. + * + * Starting from kernel version 6.6, this bit is always set in &DRM_CAP_PRIME. */ #define DRM_PRIME_CAP_IMPORT 0x1 /** @@ -689,6 +694,8 @@ struct drm_gem_open { * * If this bit is set in &DRM_CAP_PRIME, the driver supports exporting PRIME * buffers via the &DRM_IOCTL_PRIME_HANDLE_TO_FD ioctl. + * + * Starting from kernel version 6.6, this bit is always set in &DRM_CAP_PRIME. */ #define DRM_PRIME_CAP_EXPORT 0x2 /** @@ -756,15 +763,14 @@ struct drm_gem_open { /** * DRM_CAP_SYNCOBJ * - * If set to 1, the driver supports sync objects. See - * Documentation/gpu/drm-mm.rst, section "DRM Sync Objects". + * If set to 1, the driver supports sync objects. See :ref:`drm_sync_objects`. */ #define DRM_CAP_SYNCOBJ 0x13 /** * DRM_CAP_SYNCOBJ_TIMELINE * * If set to 1, the driver supports timeline operations on sync objects. See - * Documentation/gpu/drm-mm.rst, section "DRM Sync Objects". + * :ref:`drm_sync_objects`. */ #define DRM_CAP_SYNCOBJ_TIMELINE 0x14 @@ -1203,25 +1209,50 @@ extern "C" { #define DRM_COMMAND_BASE 0x40 #define DRM_COMMAND_END 0xA0 -/* - * Header for events written back to userspace on the drm fd. The - * type defines the type of event, the length specifies the total - * length of the event (including the header), and user_data is - * typically a 64 bit value passed with the ioctl that triggered the - * event. A read on the drm fd will always only return complete - * events, that is, if for example the read buffer is 100 bytes, and - * there are two 64 byte events pending, only one will be returned. +/** + * struct drm_event - Header for DRM events + * @type: event type. + * @length: total number of payload bytes (including header). * - * Event types 0 - 0x7fffffff are generic drm events, 0x80000000 and - * up are chipset specific. + * This struct is a header for events written back to user-space on the DRM FD. + * A read on the DRM FD will always only return complete events: e.g. if the + * read buffer is 100 bytes large and there are two 64 byte events pending, + * only one will be returned. + * + * Event types 0 - 0x7fffffff are generic DRM events, 0x80000000 and + * up are chipset specific. Generic DRM events include &DRM_EVENT_VBLANK, + * &DRM_EVENT_FLIP_COMPLETE and &DRM_EVENT_CRTC_SEQUENCE. */ struct drm_event { __u32 type; __u32 length; }; +/** + * DRM_EVENT_VBLANK - vertical blanking event + * + * This event is sent in response to &DRM_IOCTL_WAIT_VBLANK with the + * &_DRM_VBLANK_EVENT flag set. + * + * The event payload is a struct drm_event_vblank. + */ #define DRM_EVENT_VBLANK 0x01 +/** + * DRM_EVENT_FLIP_COMPLETE - page-flip completion event + * + * This event is sent in response to an atomic commit or legacy page-flip with + * the &DRM_MODE_PAGE_FLIP_EVENT flag set. + * + * The event payload is a struct drm_event_vblank. + */ #define DRM_EVENT_FLIP_COMPLETE 0x02 +/** + * DRM_EVENT_CRTC_SEQUENCE - CRTC sequence event + * + * This event is sent in response to &DRM_IOCTL_CRTC_QUEUE_SEQUENCE. + * + * The event payload is a struct drm_event_crtc_sequence. + */ #define DRM_EVENT_CRTC_SEQUENCE 0x03 struct drm_event_vblank { diff --git a/include/uapi/drm/drm_mode.h b/include/uapi/drm/drm_mode.h index 92d96a2b6763..ea1b639bcb28 100644 --- a/include/uapi/drm/drm_mode.h +++ b/include/uapi/drm/drm_mode.h @@ -488,6 +488,9 @@ struct drm_mode_get_connector { * This is not an object ID. This is a per-type connector number. Each * (type, type_id) combination is unique across all connectors of a DRM * device. + * + * The (type, type_id) combination is not a stable identifier: the + * type_id can change depending on the driver probe order. */ __u32 connector_type_id; diff --git a/include/uapi/drm/virtgpu_drm.h b/include/uapi/drm/virtgpu_drm.h index 7b158fcb02b4..b1d0e56565bc 100644 --- a/include/uapi/drm/virtgpu_drm.h +++ b/include/uapi/drm/virtgpu_drm.h @@ -64,6 +64,16 @@ struct drm_virtgpu_map { __u32 pad; }; +#define VIRTGPU_EXECBUF_SYNCOBJ_RESET 0x01 +#define VIRTGPU_EXECBUF_SYNCOBJ_FLAGS ( \ + VIRTGPU_EXECBUF_SYNCOBJ_RESET | \ + 0) +struct drm_virtgpu_execbuffer_syncobj { + __u32 handle; + __u32 flags; + __u64 point; +}; + /* fence_fd is modified on success if VIRTGPU_EXECBUF_FENCE_FD_OUT flag is set. */ struct drm_virtgpu_execbuffer { __u32 flags; @@ -73,7 +83,11 @@ struct drm_virtgpu_execbuffer { __u32 num_bo_handles; __s32 fence_fd; /* in/out fence fd (see VIRTGPU_EXECBUF_FENCE_FD_IN/OUT) */ __u32 ring_idx; /* command ring index (see VIRTGPU_EXECBUF_RING_IDX) */ - __u32 pad; + __u32 syncobj_stride; /* size of @drm_virtgpu_execbuffer_syncobj */ + __u32 num_in_syncobjs; + __u32 num_out_syncobjs; + __u64 in_syncobjs; + __u64 out_syncobjs; }; #define VIRTGPU_PARAM_3D_FEATURES 1 /* do we have 3D features in the hw */ |