Merge tag 'drm-misc-next-2022-06-23' of git://anongit.freedesktop.org/drm/drm-misc...
authorDave Airlie <airlied@redhat.com>
Fri, 24 Jun 2022 00:29:53 +0000 (10:29 +1000)
committerDave Airlie <airlied@redhat.com>
Fri, 24 Jun 2022 02:07:07 +0000 (12:07 +1000)
drm-misc-next for v5.20:

UAPI Changes:

 * media: Add various RGB666 and RGB888 format constants

Cross-subsystem Changes:

 * media: Documentation

Core Changes:

 * aperture: Fix segfault during hot-unplug

 * dp: Support waiting for HDP signal, plus driver updates;
   Port-validation fixes

 * fbcon: Improve scrolling performance; Sanitize input

 * Clean up <drm/drm_crtc.h>

Driver Changes:

 * amdgpu: Cleanups

 * bridge: Add support for i.MX8qxp and i.MX8qm; anx7625: DPI fixes;
   tc358775: Fix clock settings; ti-sn65dsi83: Allow GPIO to sleep

 * panel: Set orientation from panel, plus driver updates

 * Several small cleanups

Signed-off-by: Dave Airlie <airlied@redhat.com>
From: Thomas Zimmermann <tzimmermann@suse.de>
Link: https://patchwork.freedesktop.org/patch/msgid/YrQeAAVvQ6jxu2dl@linux-uq9g
231 files changed:
Documentation/devicetree/bindings/display/bridge/analogix,anx7625.yaml
Documentation/devicetree/bindings/display/bridge/fsl,imx8qxp-ldb.yaml [new file with mode: 0644]
Documentation/devicetree/bindings/display/bridge/fsl,imx8qxp-pixel-combiner.yaml [new file with mode: 0644]
Documentation/devicetree/bindings/display/bridge/fsl,imx8qxp-pixel-link.yaml [new file with mode: 0644]
Documentation/devicetree/bindings/display/bridge/fsl,imx8qxp-pxl2dpi.yaml [new file with mode: 0644]
Documentation/devicetree/bindings/mfd/fsl,imx8qxp-csr.yaml [new file with mode: 0644]
Documentation/userspace-api/media/v4l/subdev-formats.rst
MAINTAINERS
drivers/gpu/drm/amd/amdgpu/amdgpu_mode.h
drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_trace.h
drivers/gpu/drm/arm/display/komeda/d71/d71_dev.c
drivers/gpu/drm/arm/display/komeda/komeda_kms.h
drivers/gpu/drm/arm/display/komeda/komeda_plane.c
drivers/gpu/drm/arm/display/komeda/komeda_wb_connector.c
drivers/gpu/drm/arm/hdlcd_crtc.c
drivers/gpu/drm/arm/malidp_crtc.c
drivers/gpu/drm/arm/malidp_mw.c
drivers/gpu/drm/arm/malidp_planes.c
drivers/gpu/drm/armada/armada_fb.h
drivers/gpu/drm/aspeed/aspeed_gfx_crtc.c
drivers/gpu/drm/aspeed/aspeed_gfx_out.c
drivers/gpu/drm/ast/ast_mode.c
drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_plane.c
drivers/gpu/drm/bridge/Kconfig
drivers/gpu/drm/bridge/Makefile
drivers/gpu/drm/bridge/analogix/analogix_dp_core.c
drivers/gpu/drm/bridge/analogix/anx7625.c
drivers/gpu/drm/bridge/cadence/cdns-mhdp8546-core.c
drivers/gpu/drm/bridge/imx/Kconfig [new file with mode: 0644]
drivers/gpu/drm/bridge/imx/Makefile [new file with mode: 0644]
drivers/gpu/drm/bridge/imx/imx-ldb-helper.c [new file with mode: 0644]
drivers/gpu/drm/bridge/imx/imx-ldb-helper.h [new file with mode: 0644]
drivers/gpu/drm/bridge/imx/imx8qm-ldb-drv.c [new file with mode: 0644]
drivers/gpu/drm/bridge/imx/imx8qxp-ldb-drv.c [new file with mode: 0644]
drivers/gpu/drm/bridge/imx/imx8qxp-pixel-combiner.c [new file with mode: 0644]
drivers/gpu/drm/bridge/imx/imx8qxp-pixel-link.c [new file with mode: 0644]
drivers/gpu/drm/bridge/imx/imx8qxp-pxl2dpi.c [new file with mode: 0644]
drivers/gpu/drm/bridge/lontium-lt8912b.c
drivers/gpu/drm/bridge/panel.c
drivers/gpu/drm/bridge/parade-ps8640.c
drivers/gpu/drm/bridge/simple-bridge.c
drivers/gpu/drm/bridge/tc358775.c
drivers/gpu/drm/bridge/ti-sn65dsi83.c
drivers/gpu/drm/bridge/ti-tfp410.c
drivers/gpu/drm/display/drm_dp_helper.c
drivers/gpu/drm/display/drm_dp_mst_topology.c
drivers/gpu/drm/drm_aperture.c
drivers/gpu/drm/drm_atomic.c
drivers/gpu/drm/drm_atomic_helper.c
drivers/gpu/drm/drm_atomic_state_helper.c
drivers/gpu/drm/drm_atomic_uapi.c
drivers/gpu/drm/drm_bridge_connector.c
drivers/gpu/drm/drm_client_modeset.c
drivers/gpu/drm/drm_connector.c
drivers/gpu/drm/drm_crtc.c
drivers/gpu/drm/drm_crtc_helper.c
drivers/gpu/drm/drm_damage_helper.c
drivers/gpu/drm/drm_fb_helper.c
drivers/gpu/drm/drm_gem_atomic_helper.c
drivers/gpu/drm/drm_ioctl.c
drivers/gpu/drm/drm_kms_helper_common.c
drivers/gpu/drm/drm_mipi_dbi.c
drivers/gpu/drm/drm_mode_config.c
drivers/gpu/drm/drm_modes.c
drivers/gpu/drm/drm_modeset_helper.c
drivers/gpu/drm/drm_panel_orientation_quirks.c
drivers/gpu/drm/drm_prime.c
drivers/gpu/drm/drm_writeback.c
drivers/gpu/drm/exynos/exynos5433_drm_decon.c
drivers/gpu/drm/exynos/exynos7_drm_decon.c
drivers/gpu/drm/exynos/exynos_drm_fb.c
drivers/gpu/drm/exynos/exynos_drm_fbdev.c
drivers/gpu/drm/exynos/exynos_drm_fimd.c
drivers/gpu/drm/exynos/exynos_drm_ipp.c
drivers/gpu/drm/exynos/exynos_drm_plane.c
drivers/gpu/drm/exynos/exynos_drm_scaler.c
drivers/gpu/drm/exynos/exynos_drm_vidi.c
drivers/gpu/drm/exynos/exynos_mixer.c
drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_plane.c
drivers/gpu/drm/gma500/cdv_intel_dp.c
drivers/gpu/drm/gma500/framebuffer.c
drivers/gpu/drm/gma500/gma_display.c
drivers/gpu/drm/gma500/oaktrail_crtc.c
drivers/gpu/drm/gma500/oaktrail_hdmi.c
drivers/gpu/drm/gma500/oaktrail_lvds.c
drivers/gpu/drm/gma500/psb_intel_modes.c
drivers/gpu/drm/gud/gud_connector.c
drivers/gpu/drm/gud/gud_drv.c
drivers/gpu/drm/hisilicon/kirin/kirin_drm_ade.c
drivers/gpu/drm/hyperv/hyperv_drm_modeset.c
drivers/gpu/drm/i915/display/i9xx_plane.c
drivers/gpu/drm/i915/display/intel_bios.c
drivers/gpu/drm/i915/display/intel_cursor.c
drivers/gpu/drm/i915/display/intel_display_types.h
drivers/gpu/drm/i915/display/intel_dp.c
drivers/gpu/drm/i915/display/intel_fb.c
drivers/gpu/drm/i915/display/intel_fbc.c
drivers/gpu/drm/i915/display/intel_lspcon.c
drivers/gpu/drm/i915/display/intel_opregion.c
drivers/gpu/drm/i915/display/intel_sprite.c
drivers/gpu/drm/i915/display/skl_universal_plane.c
drivers/gpu/drm/i915/intel_pm.c
drivers/gpu/drm/imx/dcss/dcss-plane.c
drivers/gpu/drm/imx/imx-ldb.c
drivers/gpu/drm/imx/imx-tve.c
drivers/gpu/drm/imx/ipuv3-plane.c
drivers/gpu/drm/imx/parallel-display.c
drivers/gpu/drm/ingenic/ingenic-drm-drv.c
drivers/gpu/drm/ingenic/ingenic-ipu.c
drivers/gpu/drm/kmb/kmb_plane.c
drivers/gpu/drm/logicvc/logicvc_layer.c
drivers/gpu/drm/mcde/mcde_display.c
drivers/gpu/drm/mediatek/mtk_disp_ovl.c
drivers/gpu/drm/mediatek/mtk_disp_rdma.c
drivers/gpu/drm/mediatek/mtk_drm_plane.c
drivers/gpu/drm/meson/meson_overlay.c
drivers/gpu/drm/meson/meson_plane.c
drivers/gpu/drm/mgag200/mgag200_mode.c
drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c
drivers/gpu/drm/msm/disp/dpu1/dpu_encoder_phys_wb.c
drivers/gpu/drm/msm/disp/dpu1/dpu_formats.c
drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c
drivers/gpu/drm/msm/disp/dpu1/dpu_plane.c
drivers/gpu/drm/msm/disp/dpu1/dpu_writeback.c
drivers/gpu/drm/msm/disp/mdp4/mdp4_plane.c
drivers/gpu/drm/msm/disp/mdp5/mdp5_crtc.c
drivers/gpu/drm/msm/disp/mdp5/mdp5_plane.c
drivers/gpu/drm/msm/disp/mdp_format.c
drivers/gpu/drm/msm/hdmi/hdmi_bridge.c
drivers/gpu/drm/msm/msm_debugfs.c
drivers/gpu/drm/msm/msm_fb.c
drivers/gpu/drm/msm/msm_fbdev.c
drivers/gpu/drm/mxsfb/mxsfb_kms.c
drivers/gpu/drm/nouveau/dispnv50/wndw.c
drivers/gpu/drm/nouveau/nvkm/subdev/mmu/vmm.c
drivers/gpu/drm/omapdrm/dss/hdmi4.c
drivers/gpu/drm/omapdrm/dss/hdmi5.c
drivers/gpu/drm/omapdrm/omap_debugfs.c
drivers/gpu/drm/omapdrm/omap_fb.c
drivers/gpu/drm/omapdrm/omap_fbdev.c
drivers/gpu/drm/omapdrm/omap_plane.c
drivers/gpu/drm/panel/panel-boe-tv101wum-nl6.c
drivers/gpu/drm/panel/panel-edp.c
drivers/gpu/drm/panel/panel-elida-kd35t133.c
drivers/gpu/drm/panel/panel-ilitek-ili9881c.c
drivers/gpu/drm/panel/panel-lvds.c
drivers/gpu/drm/panel/panel-samsung-atna33xc20.c
drivers/gpu/drm/panel/panel-simple.c
drivers/gpu/drm/pl111/pl111_display.c
drivers/gpu/drm/pl111/pl111_drv.c
drivers/gpu/drm/pl111/pl111_versatile.c
drivers/gpu/drm/qxl/qxl_display.c
drivers/gpu/drm/qxl/qxl_draw.c
drivers/gpu/drm/radeon/atombios_crtc.c
drivers/gpu/drm/radeon/evergreen.c
drivers/gpu/drm/radeon/r100.c
drivers/gpu/drm/radeon/radeon_device.c
drivers/gpu/drm/radeon/radeon_display.c
drivers/gpu/drm/radeon/radeon_fb.c
drivers/gpu/drm/radeon/radeon_legacy_crtc.c
drivers/gpu/drm/radeon/rs600.c
drivers/gpu/drm/radeon/rv770.c
drivers/gpu/drm/rcar-du/rcar_du_kms.c
drivers/gpu/drm/rcar-du/rcar_du_plane.c
drivers/gpu/drm/rcar-du/rcar_du_vsp.c
drivers/gpu/drm/rcar-du/rcar_du_writeback.c
drivers/gpu/drm/rockchip/rk3066_hdmi.c
drivers/gpu/drm/rockchip/rockchip_drm_fb.c
drivers/gpu/drm/rockchip/rockchip_drm_vop.c
drivers/gpu/drm/rockchip/rockchip_drm_vop2.c
drivers/gpu/drm/selftests/test-drm_damage_helper.c
drivers/gpu/drm/selftests/test-drm_plane_helper.c
drivers/gpu/drm/shmobile/shmob_drm_crtc.c
drivers/gpu/drm/shmobile/shmob_drm_kms.c
drivers/gpu/drm/shmobile/shmob_drm_plane.c
drivers/gpu/drm/solomon/ssd130x.c
drivers/gpu/drm/sprd/sprd_dpu.c
drivers/gpu/drm/sti/sti_cursor.c
drivers/gpu/drm/sti/sti_gdp.c
drivers/gpu/drm/sti/sti_hqvdp.c
drivers/gpu/drm/sti/sti_plane.c
drivers/gpu/drm/stm/ltdc.c
drivers/gpu/drm/sun4i/sun4i_backend.c
drivers/gpu/drm/sun4i/sun4i_framebuffer.c
drivers/gpu/drm/sun4i/sun4i_layer.c
drivers/gpu/drm/sun4i/sun8i_mixer.c
drivers/gpu/drm/sun4i/sun8i_ui_layer.c
drivers/gpu/drm/sun4i/sun8i_vi_layer.c
drivers/gpu/drm/tegra/dc.c
drivers/gpu/drm/tegra/drm.c
drivers/gpu/drm/tegra/fb.c
drivers/gpu/drm/tegra/hub.c
drivers/gpu/drm/tegra/plane.c
drivers/gpu/drm/tidss/tidss_dispc.c
drivers/gpu/drm/tidss/tidss_plane.c
drivers/gpu/drm/tilcdc/tilcdc_crtc.c
drivers/gpu/drm/tilcdc/tilcdc_plane.c
drivers/gpu/drm/tiny/arcpgu.c
drivers/gpu/drm/tiny/bochs.c
drivers/gpu/drm/tiny/cirrus.c
drivers/gpu/drm/tiny/gm12u320.c
drivers/gpu/drm/tiny/ili9225.c
drivers/gpu/drm/tiny/repaper.c
drivers/gpu/drm/tiny/st7586.c
drivers/gpu/drm/tve200/tve200_display.c
drivers/gpu/drm/udl/udl_connector.c
drivers/gpu/drm/vboxvideo/vbox_mode.c
drivers/gpu/drm/vc4/vc4_bo.c
drivers/gpu/drm/vc4/vc4_crtc.c
drivers/gpu/drm/vc4/vc4_kms.c
drivers/gpu/drm/vc4/vc4_plane.c
drivers/gpu/drm/vc4/vc4_regs.h
drivers/gpu/drm/vc4/vc4_txp.c
drivers/gpu/drm/virtio/virtgpu_display.c
drivers/gpu/drm/virtio/virtgpu_drv.h
drivers/gpu/drm/virtio/virtgpu_vq.c
drivers/gpu/drm/vkms/vkms_drv.h
drivers/gpu/drm/vkms/vkms_output.c
drivers/gpu/drm/vkms/vkms_writeback.c
drivers/gpu/drm/vmwgfx/vmwgfx_kms.h
drivers/gpu/drm/xen/xen_drm_front_conn.c
drivers/gpu/drm/xen/xen_drm_front_kms.c
drivers/gpu/drm/xlnx/zynqmp_disp.c
drivers/video/fbdev/core/fbcon.c
include/drm/display/drm_dp_helper.h
include/drm/drm_bridge.h
include/drm/drm_connector.h
include/drm/drm_crtc.h
include/drm/drm_panel.h
include/uapi/linux/media-bus-format.h

index 35a4851..4590186 100644 (file)
@@ -94,7 +94,22 @@ properties:
         $ref: /schemas/graph.yaml#/$defs/port-base
         unevaluatedProperties: false
         description:
-          Video port for MIPI DSI input.
+          MIPI DSI/DPI input.
+
+        properties:
+          endpoint:
+            $ref: /schemas/media/video-interfaces.yaml#
+            type: object
+            additionalProperties: false
+
+            properties:
+              remote-endpoint: true
+
+              bus-type:
+                enum: [7]
+                default: 1
+
+              data-lanes: true
 
       port@1:
         $ref: /schemas/graph.yaml#/properties/port
@@ -143,6 +158,8 @@ examples:
                     reg = <0>;
                     anx7625_in: endpoint {
                         remote-endpoint = <&mipi_dsi>;
+                        bus-type = <7>;
+                        data-lanes = <0 1 2 3>;
                     };
                 };
 
diff --git a/Documentation/devicetree/bindings/display/bridge/fsl,imx8qxp-ldb.yaml b/Documentation/devicetree/bindings/display/bridge/fsl,imx8qxp-ldb.yaml
new file mode 100644 (file)
index 0000000..9454300
--- /dev/null
@@ -0,0 +1,173 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/display/bridge/fsl,imx8qxp-ldb.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Freescale i.MX8qm/qxp LVDS Display Bridge
+
+maintainers:
+  - Liu Ying <victor.liu@nxp.com>
+
+description: |
+  The Freescale i.MX8qm/qxp LVDS Display Bridge(LDB) has two channels.
+
+  The i.MX8qm/qxp LDB is controlled by Control and Status Registers(CSR) module.
+  The CSR module, as a system controller, contains the LDB's configuration
+  registers.
+
+  For i.MX8qxp LDB, each channel supports up to 24bpp parallel input color
+  format and can map the input to VESA or JEIDA standards.  The two channels
+  cannot be used simultaneously, that is to say, the user should pick one of
+  them to use.  Two LDB channels from two LDB instances can work together in
+  LDB split mode to support a dual link LVDS display.  The channel indexes
+  have to be different.  Channel0 outputs odd pixels and channel1 outputs
+  even pixels.
+
+  For i.MX8qm LDB, each channel additionally supports up to 30bpp parallel
+  input color format.  The two channels can be used simultaneously, either
+  in dual mode or split mode.  In dual mode, the two channels output identical
+  data.  In split mode, channel0 outputs odd pixels and channel1 outputs even
+  pixels.
+
+  A side note is that i.MX8qm/qxp LDB is officially called pixel mapper in
+  the SoC reference manuals.  The pixel mapper uses logic of LDBs embedded in
+  i.MX6qdl/sx SoCs, i.e., it is essentially based on them.  To keep the naming
+  consistency, this binding calls it LDB.
+
+properties:
+  compatible:
+    enum:
+      - fsl,imx8qm-ldb
+      - fsl,imx8qxp-ldb
+
+  "#address-cells":
+    const: 1
+
+  "#size-cells":
+    const: 0
+
+  clocks:
+    items:
+      - description: pixel clock
+      - description: bypass clock
+
+  clock-names:
+    items:
+      - const: pixel
+      - const: bypass
+
+  power-domains:
+    maxItems: 1
+
+  fsl,companion-ldb:
+    $ref: /schemas/types.yaml#/definitions/phandle
+    description: |
+      A phandle which points to companion LDB which is used in LDB split mode.
+
+patternProperties:
+  "^channel@[0-1]$":
+    type: object
+    description: Represents a channel of LDB.
+
+    properties:
+      "#address-cells":
+        const: 1
+
+      "#size-cells":
+        const: 0
+
+      reg:
+        description: The channel index.
+        enum: [ 0, 1 ]
+
+      phys:
+        description: A phandle to the phy module representing the LVDS PHY.
+        maxItems: 1
+
+      phy-names:
+        const: lvds_phy
+
+      port@0:
+        $ref: /schemas/graph.yaml#/properties/port
+        description: Input port of the channel.
+
+      port@1:
+        $ref: /schemas/graph.yaml#/properties/port
+        description: Output port of the channel.
+
+    required:
+      - "#address-cells"
+      - "#size-cells"
+      - reg
+      - phys
+      - phy-names
+
+    additionalProperties: false
+
+required:
+  - compatible
+  - "#address-cells"
+  - "#size-cells"
+  - clocks
+  - clock-names
+  - power-domains
+  - channel@0
+  - channel@1
+
+allOf:
+  - if:
+      properties:
+        compatible:
+          contains:
+            const: fsl,imx8qm-ldb
+    then:
+      properties:
+        fsl,companion-ldb: false
+
+additionalProperties: false
+
+examples:
+  - |
+    #include <dt-bindings/firmware/imx/rsrc.h>
+    ldb {
+        #address-cells = <1>;
+        #size-cells = <0>;
+        compatible = "fsl,imx8qxp-ldb";
+        clocks = <&clk IMX_SC_R_LVDS_0 IMX_SC_PM_CLK_MISC2>,
+                 <&clk IMX_SC_R_LVDS_0 IMX_SC_PM_CLK_BYPASS>;
+        clock-names = "pixel", "bypass";
+        power-domains = <&pd IMX_SC_R_LVDS_0>;
+
+        channel@0 {
+            #address-cells = <1>;
+            #size-cells = <0>;
+            reg = <0>;
+            phys = <&mipi_lvds_0_phy>;
+            phy-names = "lvds_phy";
+
+            port@0 {
+                reg = <0>;
+
+                mipi_lvds_0_ldb_ch0_mipi_lvds_0_pxl2dpi: endpoint {
+                    remote-endpoint = <&mipi_lvds_0_pxl2dpi_mipi_lvds_0_ldb_ch0>;
+                };
+            };
+        };
+
+        channel@1 {
+            #address-cells = <1>;
+            #size-cells = <0>;
+            reg = <1>;
+            phys = <&mipi_lvds_0_phy>;
+            phy-names = "lvds_phy";
+
+            port@0 {
+                reg = <0>;
+
+                mipi_lvds_0_ldb_ch1_mipi_lvds_0_pxl2dpi: endpoint {
+                    remote-endpoint = <&mipi_lvds_0_pxl2dpi_mipi_lvds_0_ldb_ch1>;
+                };
+            };
+        };
+    };
diff --git a/Documentation/devicetree/bindings/display/bridge/fsl,imx8qxp-pixel-combiner.yaml b/Documentation/devicetree/bindings/display/bridge/fsl,imx8qxp-pixel-combiner.yaml
new file mode 100644 (file)
index 0000000..50bae21
--- /dev/null
@@ -0,0 +1,144 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/display/bridge/fsl,imx8qxp-pixel-combiner.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Freescale i.MX8qm/qxp Pixel Combiner
+
+maintainers:
+  - Liu Ying <victor.liu@nxp.com>
+
+description: |
+  The Freescale i.MX8qm/qxp Pixel Combiner takes two output streams from a
+  single display controller and manipulates the two streams to support a number
+  of modes(bypass, pixel combine, YUV444 to YUV422, split_RGB) configured as
+  either one screen, two screens, or virtual screens.  The pixel combiner is
+  also responsible for generating some of the control signals for the pixel link
+  output channel.
+
+properties:
+  compatible:
+    enum:
+      - fsl,imx8qm-pixel-combiner
+      - fsl,imx8qxp-pixel-combiner
+
+  "#address-cells":
+    const: 1
+
+  "#size-cells":
+    const: 0
+
+  reg:
+    maxItems: 1
+
+  clocks:
+    maxItems: 1
+
+  clock-names:
+    const: apb
+
+  power-domains:
+    maxItems: 1
+
+patternProperties:
+  "^channel@[0-1]$":
+    type: object
+    description: Represents a display stream of pixel combiner.
+
+    properties:
+      "#address-cells":
+        const: 1
+
+      "#size-cells":
+        const: 0
+
+      reg:
+        description: The display stream index.
+        enum: [ 0, 1 ]
+
+      port@0:
+        $ref: /schemas/graph.yaml#/properties/port
+        description: Input endpoint of the display stream.
+
+      port@1:
+        $ref: /schemas/graph.yaml#/properties/port
+        description: Output endpoint of the display stream.
+
+    required:
+      - "#address-cells"
+      - "#size-cells"
+      - reg
+      - port@0
+      - port@1
+
+    additionalProperties: false
+
+required:
+  - compatible
+  - "#address-cells"
+  - "#size-cells"
+  - reg
+  - clocks
+  - clock-names
+  - power-domains
+
+additionalProperties: false
+
+examples:
+  - |
+    #include <dt-bindings/clock/imx8-lpcg.h>
+    #include <dt-bindings/firmware/imx/rsrc.h>
+    pixel-combiner@56020000 {
+        compatible = "fsl,imx8qxp-pixel-combiner";
+        #address-cells = <1>;
+        #size-cells = <0>;
+        reg = <0x56020000 0x10000>;
+        clocks = <&dc0_pixel_combiner_lpcg IMX_LPCG_CLK_4>;
+        clock-names = "apb";
+        power-domains = <&pd IMX_SC_R_DC_0>;
+
+        channel@0 {
+            #address-cells = <1>;
+            #size-cells = <0>;
+            reg = <0>;
+
+            port@0 {
+                reg = <0>;
+
+                dc0_pixel_combiner_ch0_dc0_dpu_disp0: endpoint {
+                    remote-endpoint = <&dc0_dpu_disp0_dc0_pixel_combiner_ch0>;
+                };
+            };
+
+            port@1 {
+                reg = <1>;
+
+                dc0_pixel_combiner_ch0_dc0_pixel_link0: endpoint {
+                    remote-endpoint = <&dc0_pixel_link0_dc0_pixel_combiner_ch0>;
+                };
+            };
+        };
+
+        channel@1 {
+            #address-cells = <1>;
+            #size-cells = <0>;
+            reg = <1>;
+
+            port@0 {
+                reg = <0>;
+
+                dc0_pixel_combiner_ch1_dc0_dpu_disp1: endpoint {
+                    remote-endpoint = <&dc0_dpu_disp1_dc0_pixel_combiner_ch1>;
+                };
+            };
+
+            port@1 {
+                reg = <1>;
+
+                dc0_pixel_combiner_ch1_dc0_pixel_link1: endpoint {
+                    remote-endpoint = <&dc0_pixel_link1_dc0_pixel_combiner_ch1>;
+                };
+            };
+        };
+    };
diff --git a/Documentation/devicetree/bindings/display/bridge/fsl,imx8qxp-pixel-link.yaml b/Documentation/devicetree/bindings/display/bridge/fsl,imx8qxp-pixel-link.yaml
new file mode 100644 (file)
index 0000000..38ecc79
--- /dev/null
@@ -0,0 +1,144 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/display/bridge/fsl,imx8qxp-pixel-link.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Freescale i.MX8qm/qxp Display Pixel Link
+
+maintainers:
+  - Liu Ying <victor.liu@nxp.com>
+
+description: |
+  The Freescale i.MX8qm/qxp Display Pixel Link(DPL) forms a standard
+  asynchronous linkage between pixel sources(display controller or
+  camera module) and pixel consumers(imaging or displays).
+  It consists of two distinct functions, a pixel transfer function and a
+  control interface.  Multiple pixel channels can exist per one control channel.
+  This binding documentation is only for pixel links whose pixel sources are
+  display controllers.
+
+  The i.MX8qm/qxp Display Pixel Link is accessed via System Controller Unit(SCU)
+  firmware.
+
+properties:
+  compatible:
+    enum:
+      - fsl,imx8qm-dc-pixel-link
+      - fsl,imx8qxp-dc-pixel-link
+
+  fsl,dc-id:
+    $ref: /schemas/types.yaml#/definitions/uint8
+    description: |
+      u8 value representing the display controller index that the pixel link
+      connects to.
+
+  fsl,dc-stream-id:
+    $ref: /schemas/types.yaml#/definitions/uint8
+    description: |
+      u8 value representing the display controller stream index that the pixel
+      link connects to.
+    enum: [0, 1]
+
+  ports:
+    $ref: /schemas/graph.yaml#/properties/ports
+
+    properties:
+      port@0:
+        $ref: /schemas/graph.yaml#/properties/port
+        description: The pixel link input port node from upstream video source.
+
+    patternProperties:
+      "^port@[1-4]$":
+        $ref: /schemas/graph.yaml#/properties/port
+        description: The pixel link output port node to downstream bridge.
+
+    required:
+      - port@0
+      - port@1
+      - port@2
+      - port@3
+      - port@4
+
+allOf:
+  - if:
+      properties:
+        compatible:
+          contains:
+            const: fsl,imx8qxp-dc-pixel-link
+    then:
+      properties:
+        fsl,dc-id:
+          const: 0
+
+  - if:
+      properties:
+        compatible:
+          contains:
+            const: fsl,imx8qm-dc-pixel-link
+    then:
+      properties:
+        fsl,dc-id:
+          enum: [0, 1]
+
+required:
+  - compatible
+  - fsl,dc-id
+  - fsl,dc-stream-id
+  - ports
+
+additionalProperties: false
+
+examples:
+  - |
+    dc0-pixel-link0 {
+        compatible = "fsl,imx8qxp-dc-pixel-link";
+        fsl,dc-id = /bits/ 8 <0>;
+        fsl,dc-stream-id = /bits/ 8 <0>;
+
+        ports {
+            #address-cells = <1>;
+            #size-cells = <0>;
+
+            /* from dc0 pixel combiner channel0 */
+            port@0 {
+                reg = <0>;
+
+                dc0_pixel_link0_dc0_pixel_combiner_ch0: endpoint {
+                    remote-endpoint = <&dc0_pixel_combiner_ch0_dc0_pixel_link0>;
+                };
+            };
+
+            /* to PXL2DPIs in MIPI/LVDS combo subsystems */
+            port@1 {
+                #address-cells = <1>;
+                #size-cells = <0>;
+                reg = <1>;
+
+                dc0_pixel_link0_mipi_lvds_0_pxl2dpi: endpoint@0 {
+                    reg = <0>;
+                    remote-endpoint = <&mipi_lvds_0_pxl2dpi_dc0_pixel_link0>;
+                };
+
+                dc0_pixel_link0_mipi_lvds_1_pxl2dpi: endpoint@1 {
+                    reg = <1>;
+                    remote-endpoint = <&mipi_lvds_1_pxl2dpi_dc0_pixel_link0>;
+                };
+            };
+
+            /* unused */
+            port@2 {
+                reg = <2>;
+            };
+
+            /* unused */
+            port@3 {
+                reg = <3>;
+            };
+
+            /* to imaging subsystem */
+            port@4 {
+                reg = <4>;
+            };
+        };
+    };
diff --git a/Documentation/devicetree/bindings/display/bridge/fsl,imx8qxp-pxl2dpi.yaml b/Documentation/devicetree/bindings/display/bridge/fsl,imx8qxp-pxl2dpi.yaml
new file mode 100644 (file)
index 0000000..e4e77fa
--- /dev/null
@@ -0,0 +1,108 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/display/bridge/fsl,imx8qxp-pxl2dpi.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Freescale i.MX8qxp Pixel Link to Display Pixel Interface
+
+maintainers:
+  - Liu Ying <victor.liu@nxp.com>
+
+description: |
+  The Freescale i.MX8qxp Pixel Link to Display Pixel Interface(PXL2DPI)
+  interfaces the pixel link 36-bit data output and the DSI controller’s
+  MIPI-DPI 24-bit data input, and inputs of LVDS Display Bridge(LDB) module
+  used in LVDS mode, to remap the pixel color codings between those modules.
+  This module is purely combinatorial.
+
+  The i.MX8qxp PXL2DPI is controlled by Control and Status Registers(CSR) module.
+  The CSR module, as a system controller, contains the PXL2DPI's configuration
+  register.
+
+properties:
+  compatible:
+    const: fsl,imx8qxp-pxl2dpi
+
+  fsl,sc-resource:
+    $ref: /schemas/types.yaml#/definitions/uint32
+    description: The SCU resource ID associated with this PXL2DPI instance.
+
+  power-domains:
+    maxItems: 1
+
+  fsl,companion-pxl2dpi:
+    $ref: /schemas/types.yaml#/definitions/phandle
+    description: |
+      A phandle which points to companion PXL2DPI which is used by downstream
+      LVDS Display Bridge(LDB) in split mode.
+
+  ports:
+    $ref: /schemas/graph.yaml#/properties/ports
+
+    properties:
+      port@0:
+        $ref: /schemas/graph.yaml#/properties/port
+        description: The PXL2DPI input port node from pixel link.
+
+      port@1:
+        $ref: /schemas/graph.yaml#/properties/port
+        description: The PXL2DPI output port node to downstream bridge.
+
+    required:
+      - port@0
+      - port@1
+
+required:
+  - compatible
+  - fsl,sc-resource
+  - power-domains
+  - ports
+
+additionalProperties: false
+
+examples:
+  - |
+    #include <dt-bindings/firmware/imx/rsrc.h>
+    pxl2dpi {
+        compatible = "fsl,imx8qxp-pxl2dpi";
+        fsl,sc-resource = <IMX_SC_R_MIPI_0>;
+        power-domains = <&pd IMX_SC_R_MIPI_0>;
+
+        ports {
+            #address-cells = <1>;
+            #size-cells = <0>;
+
+            port@0 {
+                #address-cells = <1>;
+                #size-cells = <0>;
+                reg = <0>;
+
+                mipi_lvds_0_pxl2dpi_dc_pixel_link0: endpoint@0 {
+                    reg = <0>;
+                    remote-endpoint = <&dc_pixel_link0_mipi_lvds_0_pxl2dpi>;
+                };
+
+                mipi_lvds_0_pxl2dpi_dc_pixel_link1: endpoint@1 {
+                     reg = <1>;
+                     remote-endpoint = <&dc_pixel_link1_mipi_lvds_0_pxl2dpi>;
+                };
+            };
+
+            port@1 {
+                #address-cells = <1>;
+                #size-cells = <0>;
+                reg = <1>;
+
+                mipi_lvds_0_pxl2dpi_mipi_lvds_0_ldb_ch0: endpoint@0 {
+                    reg = <0>;
+                    remote-endpoint = <&mipi_lvds_0_ldb_ch0_mipi_lvds_0_pxl2dpi>;
+                };
+
+                mipi_lvds_0_pxl2dpi_mipi_lvds_0_ldb_ch1: endpoint@1 {
+                    reg = <1>;
+                    remote-endpoint = <&mipi_lvds_0_ldb_ch1_mipi_lvds_0_pxl2dpi>;
+                };
+            };
+        };
+    };
diff --git a/Documentation/devicetree/bindings/mfd/fsl,imx8qxp-csr.yaml b/Documentation/devicetree/bindings/mfd/fsl,imx8qxp-csr.yaml
new file mode 100644 (file)
index 0000000..f095771
--- /dev/null
@@ -0,0 +1,192 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/mfd/fsl,imx8qxp-csr.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Freescale i.MX8qm/qxp Control and Status Registers Module Bindings
+
+maintainers:
+  - Liu Ying <victor.liu@nxp.com>
+
+description: |
+  As a system controller, the Freescale i.MX8qm/qxp Control and Status
+  Registers(CSR) module represents a set of miscellaneous registers of a
+  specific subsystem.  It may provide control and/or status report interfaces
+  to a mix of standalone hardware devices within that subsystem.  One typical
+  use-case is for some other nodes to acquire a reference to the syscon node
+  by phandle, and the other typical use-case is that the operating system
+  should consider all subnodes of the CSR module as separate child devices.
+
+properties:
+  $nodename:
+    pattern: "^syscon@[0-9a-f]+$"
+
+  compatible:
+    items:
+      - enum:
+          - fsl,imx8qxp-mipi-lvds-csr
+          - fsl,imx8qm-lvds-csr
+      - const: syscon
+      - const: simple-mfd
+
+  reg:
+    maxItems: 1
+
+  clocks:
+    maxItems: 1
+
+  clock-names:
+    const: ipg
+
+patternProperties:
+  "^(ldb|phy|pxl2dpi)$":
+    type: object
+    description: The possible child devices of the CSR module.
+
+required:
+  - compatible
+  - reg
+  - clocks
+  - clock-names
+
+allOf:
+  - if:
+      properties:
+        compatible:
+          contains:
+            const: fsl,imx8qxp-mipi-lvds-csr
+    then:
+      required:
+        - pxl2dpi
+        - ldb
+
+  - if:
+      properties:
+        compatible:
+          contains:
+            const: fsl,imx8qm-lvds-csr
+    then:
+      required:
+        - phy
+        - ldb
+
+additionalProperties: false
+
+examples:
+  - |
+    #include <dt-bindings/clock/imx8-lpcg.h>
+    #include <dt-bindings/firmware/imx/rsrc.h>
+    mipi_lvds_0_csr: syscon@56221000 {
+        compatible = "fsl,imx8qxp-mipi-lvds-csr", "syscon", "simple-mfd";
+        reg = <0x56221000 0x1000>;
+        clocks = <&mipi_lvds_0_di_mipi_lvds_regs_lpcg IMX_LPCG_CLK_4>;
+        clock-names = "ipg";
+
+        mipi_lvds_0_pxl2dpi: pxl2dpi {
+            compatible = "fsl,imx8qxp-pxl2dpi";
+            fsl,sc-resource = <IMX_SC_R_MIPI_0>;
+            power-domains = <&pd IMX_SC_R_MIPI_0>;
+
+            ports {
+                #address-cells = <1>;
+                #size-cells = <0>;
+
+                port@0 {
+                    #address-cells = <1>;
+                    #size-cells = <0>;
+                    reg = <0>;
+
+                    mipi_lvds_0_pxl2dpi_dc0_pixel_link0: endpoint@0 {
+                        reg = <0>;
+                        remote-endpoint = <&dc0_pixel_link0_mipi_lvds_0_pxl2dpi>;
+                    };
+
+                    mipi_lvds_0_pxl2dpi_dc0_pixel_link1: endpoint@1 {
+                        reg = <1>;
+                        remote-endpoint = <&dc0_pixel_link1_mipi_lvds_0_pxl2dpi>;
+                    };
+                };
+
+                port@1 {
+                    #address-cells = <1>;
+                    #size-cells = <0>;
+                    reg = <1>;
+
+                    mipi_lvds_0_pxl2dpi_mipi_lvds_0_ldb_ch0: endpoint@0 {
+                        reg = <0>;
+                        remote-endpoint = <&mipi_lvds_0_ldb_ch0_mipi_lvds_0_pxl2dpi>;
+                    };
+
+                    mipi_lvds_0_pxl2dpi_mipi_lvds_0_ldb_ch1: endpoint@1 {
+                        reg = <1>;
+                        remote-endpoint = <&mipi_lvds_0_ldb_ch1_mipi_lvds_0_pxl2dpi>;
+                    };
+                };
+            };
+        };
+
+        mipi_lvds_0_ldb: ldb {
+            #address-cells = <1>;
+            #size-cells = <0>;
+            compatible = "fsl,imx8qxp-ldb";
+            clocks = <&clk IMX_SC_R_LVDS_0 IMX_SC_PM_CLK_MISC2>,
+                     <&clk IMX_SC_R_LVDS_0 IMX_SC_PM_CLK_BYPASS>;
+            clock-names = "pixel", "bypass";
+            power-domains = <&pd IMX_SC_R_LVDS_0>;
+
+            channel@0 {
+                #address-cells = <1>;
+                #size-cells = <0>;
+                reg = <0>;
+                phys = <&mipi_lvds_0_phy>;
+                phy-names = "lvds_phy";
+
+                port@0 {
+                    reg = <0>;
+
+                    mipi_lvds_0_ldb_ch0_mipi_lvds_0_pxl2dpi: endpoint {
+                        remote-endpoint = <&mipi_lvds_0_pxl2dpi_mipi_lvds_0_ldb_ch0>;
+                    };
+                };
+
+                port@1 {
+                    reg = <1>;
+
+                    /* ... */
+                };
+            };
+
+            channel@1 {
+                #address-cells = <1>;
+                #size-cells = <0>;
+                reg = <1>;
+                phys = <&mipi_lvds_0_phy>;
+                phy-names = "lvds_phy";
+
+                port@0 {
+                    reg = <0>;
+
+                    mipi_lvds_0_ldb_ch1_mipi_lvds_0_pxl2dpi: endpoint {
+                        remote-endpoint = <&mipi_lvds_0_pxl2dpi_mipi_lvds_0_ldb_ch1>;
+                    };
+                };
+
+                port@1 {
+                    reg = <1>;
+
+                    /* ... */
+                };
+            };
+        };
+    };
+
+    mipi_lvds_0_phy: phy@56228300 {
+        compatible = "fsl,imx8qxp-mipi-dphy";
+        reg = <0x56228300 0x100>;
+        clocks = <&clk IMX_SC_R_LVDS_0 IMX_SC_PM_CLK_PHY>;
+        clock-names = "phy_ref";
+        #phy-cells = <0>;
+        fsl,syscon = <&mipi_lvds_0_csr>;
+        power-domains = <&pd IMX_SC_R_MIPI_0>;
+    };
index 0cbc045..d21d532 100644 (file)
@@ -1492,6 +1492,80 @@ The following tables list existing packed RGB formats.
       - b\ :sub:`2`
       - b\ :sub:`1`
       - b\ :sub:`0`
+    * .. _MEDIA-BUS-FMT-RGB666-1X30-CPADLO:
+
+      - MEDIA_BUS_FMT_RGB666_1X30-CPADLO
+      - 0x101e
+      -
+      -
+      -
+      - r\ :sub:`5`
+      - r\ :sub:`4`
+      - r\ :sub:`3`
+      - r\ :sub:`2`
+      - r\ :sub:`1`
+      - r\ :sub:`0`
+      - 0
+      - 0
+      - 0
+      - 0
+      - g\ :sub:`5`
+      - g\ :sub:`4`
+      - g\ :sub:`3`
+      - g\ :sub:`2`
+      - g\ :sub:`1`
+      - g\ :sub:`0`
+      - 0
+      - 0
+      - 0
+      - 0
+      - b\ :sub:`5`
+      - b\ :sub:`4`
+      - b\ :sub:`3`
+      - b\ :sub:`2`
+      - b\ :sub:`1`
+      - b\ :sub:`0`
+      - 0
+      - 0
+      - 0
+      - 0
+    * .. _MEDIA-BUS-FMT-RGB888-1X30-CPADLO:
+
+      - MEDIA_BUS_FMT_RGB888_1X30-CPADLO
+      - 0x101f
+      -
+      -
+      -
+      - r\ :sub:`7`
+      - r\ :sub:`6`
+      - r\ :sub:`5`
+      - r\ :sub:`4`
+      - r\ :sub:`3`
+      - r\ :sub:`2`
+      - r\ :sub:`1`
+      - r\ :sub:`0`
+      - 0
+      - 0
+      - g\ :sub:`7`
+      - g\ :sub:`6`
+      - g\ :sub:`5`
+      - g\ :sub:`4`
+      - g\ :sub:`3`
+      - g\ :sub:`2`
+      - g\ :sub:`1`
+      - g\ :sub:`0`
+      - 0
+      - 0
+      - b\ :sub:`7`
+      - b\ :sub:`6`
+      - b\ :sub:`5`
+      - b\ :sub:`4`
+      - b\ :sub:`3`
+      - b\ :sub:`2`
+      - b\ :sub:`1`
+      - b\ :sub:`0`
+      - 0
+      - 0
     * .. _MEDIA-BUS-FMT-ARGB888-1X32:
 
       - MEDIA_BUS_FMT_ARGB888_1X32
@@ -1669,6 +1743,88 @@ The following table list existing packed 36bit wide RGB formats.
       - 2
       - 1
       - 0
+    * .. _MEDIA-BUS-FMT-RGB666-1X36-CPADLO:
+
+      - MEDIA_BUS_FMT_RGB666_1X36_CPADLO
+      - 0x1020
+      -
+      - r\ :sub:`5`
+      - r\ :sub:`4`
+      - r\ :sub:`3`
+      - r\ :sub:`2`
+      - r\ :sub:`1`
+      - r\ :sub:`0`
+      - 0
+      - 0
+      - 0
+      - 0
+      - 0
+      - 0
+      - g\ :sub:`5`
+      - g\ :sub:`4`
+      - g\ :sub:`3`
+      - g\ :sub:`2`
+      - g\ :sub:`1`
+      - g\ :sub:`0`
+      - 0
+      - 0
+      - 0
+      - 0
+      - 0
+      - 0
+      - b\ :sub:`5`
+      - b\ :sub:`4`
+      - b\ :sub:`3`
+      - b\ :sub:`2`
+      - b\ :sub:`1`
+      - b\ :sub:`0`
+      - 0
+      - 0
+      - 0
+      - 0
+      - 0
+      - 0
+    * .. _MEDIA-BUS-FMT-RGB888-1X36-CPADLO:
+
+      - MEDIA_BUS_FMT_RGB888_1X36_CPADLO
+      - 0x1021
+      -
+      - r\ :sub:`7`
+      - r\ :sub:`6`
+      - r\ :sub:`5`
+      - r\ :sub:`4`
+      - r\ :sub:`3`
+      - r\ :sub:`2`
+      - r\ :sub:`1`
+      - r\ :sub:`0`
+      - 0
+      - 0
+      - 0
+      - 0
+      - g\ :sub:`7`
+      - g\ :sub:`6`
+      - g\ :sub:`5`
+      - g\ :sub:`4`
+      - g\ :sub:`3`
+      - g\ :sub:`2`
+      - g\ :sub:`1`
+      - g\ :sub:`0`
+      - 0
+      - 0
+      - 0
+      - 0
+      - b\ :sub:`7`
+      - b\ :sub:`6`
+      - b\ :sub:`5`
+      - b\ :sub:`4`
+      - b\ :sub:`3`
+      - b\ :sub:`2`
+      - b\ :sub:`1`
+      - b\ :sub:`0`
+      - 0
+      - 0
+      - 0
+      - 0
     * .. _MEDIA-BUS-FMT-RGB121212-1X36:
 
       - MEDIA_BUS_FMT_RGB121212_1X36
index 3412310..4036b64 100644 (file)
@@ -6674,6 +6674,16 @@ F:       Documentation/devicetree/bindings/display/imx/
 F:     drivers/gpu/drm/imx/
 F:     drivers/gpu/ipu-v3/
 
+DRM DRIVERS FOR FREESCALE IMX BRIDGE
+M:     Liu Ying <victor.liu@nxp.com>
+L:     dri-devel@lists.freedesktop.org
+S:     Maintained
+F:     Documentation/devicetree/bindings/display/bridge/fsl,imx8qxp-ldb.yaml
+F:     Documentation/devicetree/bindings/display/bridge/fsl,imx8qxp-pixel-combiner.yaml
+F:     Documentation/devicetree/bindings/display/bridge/fsl,imx8qxp-pixel-link.yaml
+F:     Documentation/devicetree/bindings/display/bridge/fsl,imx8qxp-pxl2dpi.yaml
+F:     drivers/gpu/drm/bridge/imx/
+
 DRM DRIVERS FOR GMA500 (Poulsbo, Moorestown and derivative chipsets)
 M:     Patrik Jakobsson <patrik.r.jakobsson@gmail.com>
 L:     dri-devel@lists.freedesktop.org
index f80b483..450d32c 100644 (file)
@@ -37,6 +37,7 @@
 #include <drm/drm_fixed.h>
 #include <drm/drm_crtc_helper.h>
 #include <drm/drm_fb_helper.h>
+#include <drm/drm_framebuffer.h>
 #include <drm/drm_plane_helper.h>
 #include <drm/drm_probe_helper.h>
 #include <linux/i2c.h>
index eb6c55e..2f4422d 100644 (file)
@@ -78,6 +78,7 @@
 #include <drm/drm_atomic.h>
 #include <drm/drm_atomic_uapi.h>
 #include <drm/drm_atomic_helper.h>
+#include <drm/drm_blend.h>
 #include <drm/drm_fb_helper.h>
 #include <drm/drm_fourcc.h>
 #include <drm/drm_edid.h>
@@ -9138,7 +9139,6 @@ static void amdgpu_dm_commit_planes(struct drm_atomic_state *state,
                        to_dm_crtc_state(drm_atomic_get_old_crtc_state(state, pcrtc));
        int planes_count = 0, vpos, hpos;
        unsigned long flags;
-       struct amdgpu_bo *abo;
        uint32_t target_vblank, last_flip_vblank;
        bool vrr_active = amdgpu_dm_vrr_active(acrtc_state);
        bool pflip_present = false;
@@ -9210,7 +9210,6 @@ static void amdgpu_dm_commit_planes(struct drm_atomic_state *state,
                        continue;
                }
 
-               abo = gem_to_amdgpu_bo(fb->obj[0]);
                fill_dc_plane_info_and_addr(
                        dm->adev, new_plane_state,
                        afb->tiling_flags,
index fdcaea2..d3bc9dc 100644 (file)
@@ -34,6 +34,7 @@
 #include <drm/drm_crtc.h>
 #include <drm/drm_plane.h>
 #include <drm/drm_fourcc.h>
+#include <drm/drm_framebuffer.h>
 #include <drm/drm_encoder.h>
 #include <drm/drm_atomic.h>
 
index 00fa56c..daa1fac 100644 (file)
@@ -5,6 +5,7 @@
  *
  */
 
+#include <drm/drm_blend.h>
 #include <drm/drm_print.h>
 #include "d71_dev.h"
 #include "malidp_io.h"
index 456f3c4..7889e38 100644 (file)
@@ -10,6 +10,7 @@
 #include <linux/list.h>
 #include <drm/drm_atomic.h>
 #include <drm/drm_atomic_helper.h>
+#include <drm/drm_blend.h>
 #include <drm/drm_crtc_helper.h>
 #include <drm/drm_device.h>
 #include <drm/drm_writeback.h>
index e0b9f70..dff22de 100644 (file)
@@ -6,6 +6,7 @@
  */
 #include <drm/drm_atomic.h>
 #include <drm/drm_atomic_helper.h>
+#include <drm/drm_blend.h>
 #include <drm/drm_plane_helper.h>
 #include <drm/drm_print.h>
 #include "komeda_dev.h"
index ce4b760..ebccb74 100644 (file)
@@ -4,6 +4,7 @@
  * Author: James.Qian.Wang <james.qian.wang@arm.com>
  *
  */
+#include <drm/drm_framebuffer.h>
 #include "komeda_dev.h"
 #include "komeda_kms.h"
 
index 7adb065..afc9cd8 100644 (file)
@@ -20,6 +20,7 @@
 #include <drm/drm_crtc.h>
 #include <drm/drm_fb_cma_helper.h>
 #include <drm/drm_fb_helper.h>
+#include <drm/drm_framebuffer.h>
 #include <drm/drm_gem_cma_helper.h>
 #include <drm/drm_of.h>
 #include <drm/drm_plane_helper.h>
index b5928b5..9627307 100644 (file)
@@ -14,6 +14,7 @@
 #include <drm/drm_atomic.h>
 #include <drm/drm_atomic_helper.h>
 #include <drm/drm_crtc.h>
+#include <drm/drm_framebuffer.h>
 #include <drm/drm_print.h>
 #include <drm/drm_probe_helper.h>
 #include <drm/drm_vblank.h>
index 204c869..b66ca5b 100644 (file)
@@ -9,8 +9,10 @@
 #include <drm/drm_atomic.h>
 #include <drm/drm_atomic_helper.h>
 #include <drm/drm_crtc.h>
+#include <drm/drm_edid.h>
 #include <drm/drm_fb_cma_helper.h>
 #include <drm/drm_fourcc.h>
+#include <drm/drm_framebuffer.h>
 #include <drm/drm_gem_cma_helper.h>
 #include <drm/drm_probe_helper.h>
 #include <drm/drm_writeback.h>
index 338cec4..8a95626 100644 (file)
 
 #include <drm/drm_atomic.h>
 #include <drm/drm_atomic_helper.h>
+#include <drm/drm_blend.h>
 #include <drm/drm_drv.h>
 #include <drm/drm_fb_cma_helper.h>
 #include <drm/drm_fourcc.h>
+#include <drm/drm_framebuffer.h>
 #include <drm/drm_gem_cma_helper.h>
 #include <drm/drm_gem_framebuffer_helper.h>
 #include <drm/drm_plane_helper.h>
index 7dda5f2..c5bc53d 100644 (file)
@@ -5,6 +5,8 @@
 #ifndef ARMADA_FB_H
 #define ARMADA_FB_H
 
+#include <drm/drm_framebuffer.h>
+
 struct armada_framebuffer {
        struct drm_framebuffer  fb;
        uint8_t                 fmt;
index 827e62c..f3788d7 100644 (file)
@@ -9,6 +9,7 @@
 #include <drm/drm_device.h>
 #include <drm/drm_fb_cma_helper.h>
 #include <drm/drm_fourcc.h>
+#include <drm/drm_framebuffer.h>
 #include <drm/drm_gem_atomic_helper.h>
 #include <drm/drm_gem_cma_helper.h>
 #include <drm/drm_panel.h>
index 6759cb8..4f21870 100644 (file)
@@ -4,6 +4,7 @@
 #include <drm/drm_atomic_helper.h>
 #include <drm/drm_connector.h>
 #include <drm/drm_crtc_helper.h>
+#include <drm/drm_edid.h>
 #include <drm/drm_probe_helper.h>
 
 #include "aspeed_gfx.h"
index db2010a..3eb9afe 100644 (file)
@@ -36,6 +36,7 @@
 #include <drm/drm_atomic_state_helper.h>
 #include <drm/drm_crtc.h>
 #include <drm/drm_crtc_helper.h>
+#include <drm/drm_edid.h>
 #include <drm/drm_fourcc.h>
 #include <drm/drm_gem_atomic_helper.h>
 #include <drm/drm_gem_framebuffer_helper.h>
index a077d93..2306ceb 100644 (file)
 
 #include <drm/drm_atomic.h>
 #include <drm/drm_atomic_helper.h>
+#include <drm/drm_blend.h>
 #include <drm/drm_fb_cma_helper.h>
 #include <drm/drm_fourcc.h>
+#include <drm/drm_framebuffer.h>
 #include <drm/drm_gem_cma_helper.h>
 #include <drm/drm_plane_helper.h>
 
index 1afe99d..57946d8 100644 (file)
@@ -385,6 +385,8 @@ source "drivers/gpu/drm/bridge/adv7511/Kconfig"
 
 source "drivers/gpu/drm/bridge/cadence/Kconfig"
 
+source "drivers/gpu/drm/bridge/imx/Kconfig"
+
 source "drivers/gpu/drm/bridge/synopsys/Kconfig"
 
 endmenu
index 043b499..1884803 100644 (file)
@@ -36,4 +36,5 @@ obj-$(CONFIG_DRM_ITE_IT66121) += ite-it66121.o
 
 obj-y += analogix/
 obj-y += cadence/
+obj-y += imx/
 obj-y += synopsys/
index 01c8b80..8aadcc0 100644 (file)
@@ -24,6 +24,7 @@
 #include <drm/drm_bridge.h>
 #include <drm/drm_crtc.h>
 #include <drm/drm_device.h>
+#include <drm/drm_edid.h>
 #include <drm/drm_panel.h>
 #include <drm/drm_print.h>
 #include <drm/drm_probe_helper.h>
index f08f330..3710fa9 100644 (file)
@@ -1623,14 +1623,14 @@ static int anx7625_parse_dt(struct device *dev,
 
        anx7625_get_swing_setting(dev, pdata);
 
-       pdata->is_dpi = 1; /* default dpi mode */
+       pdata->is_dpi = 0; /* default dsi mode */
        pdata->mipi_host_node = of_graph_get_remote_node(np, 0, 0);
        if (!pdata->mipi_host_node) {
                DRM_DEV_ERROR(dev, "fail to get internal panel.\n");
                return -ENODEV;
        }
 
-       bus_type = V4L2_FWNODE_BUS_TYPE_PARALLEL;
+       bus_type = 0;
        mipi_lanes = MAX_LANES_SUPPORT;
        ep0 = of_graph_get_endpoint_by_regs(np, 0, 0);
        if (ep0) {
@@ -1641,8 +1641,8 @@ static int anx7625_parse_dt(struct device *dev,
                of_node_put(ep0);
        }
 
-       if (bus_type == V4L2_FWNODE_BUS_TYPE_PARALLEL) /* bus type is Parallel(DSI) */
-               pdata->is_dpi = 0;
+       if (bus_type == V4L2_FWNODE_BUS_TYPE_DPI) /* bus type is DPI */
+               pdata->is_dpi = 1;
 
        pdata->mipi_lanes = MAX_LANES_SUPPORT;
        if (mipi_lanes > 0)
index 67f0f44..ba5f695 100644 (file)
@@ -43,6 +43,7 @@
 #include <drm/drm_bridge.h>
 #include <drm/drm_connector.h>
 #include <drm/drm_crtc_helper.h>
+#include <drm/drm_edid.h>
 #include <drm/drm_modeset_helper_vtables.h>
 #include <drm/drm_print.h>
 #include <drm/drm_probe_helper.h>
diff --git a/drivers/gpu/drm/bridge/imx/Kconfig b/drivers/gpu/drm/bridge/imx/Kconfig
new file mode 100644 (file)
index 0000000..212a7b0
--- /dev/null
@@ -0,0 +1,43 @@
+config DRM_IMX8QM_LDB
+       tristate "Freescale i.MX8QM LVDS display bridge"
+       depends on OF
+       depends on COMMON_CLK
+       select DRM_KMS_HELPER
+       help
+         Choose this to enable the internal LVDS Display Bridge(LDB) found in
+         Freescale i.MX8qm processor.  Official name of LDB is pixel mapper.
+
+config DRM_IMX8QXP_LDB
+       tristate "Freescale i.MX8QXP LVDS display bridge"
+       depends on OF
+       depends on COMMON_CLK
+       select DRM_KMS_HELPER
+       help
+         Choose this to enable the internal LVDS Display Bridge(LDB) found in
+         Freescale i.MX8qxp processor.  Official name of LDB is pixel mapper.
+
+config DRM_IMX8QXP_PIXEL_COMBINER
+       tristate "Freescale i.MX8QM/QXP pixel combiner"
+       depends on OF
+       depends on COMMON_CLK
+       select DRM_KMS_HELPER
+       help
+         Choose this to enable pixel combiner found in
+         Freescale i.MX8qm/qxp processors.
+
+config DRM_IMX8QXP_PIXEL_LINK
+       tristate "Freescale i.MX8QM/QXP display pixel link"
+       depends on OF
+       depends on IMX_SCU
+       select DRM_KMS_HELPER
+       help
+         Choose this to enable display pixel link found in
+         Freescale i.MX8qm/qxp processors.
+
+config DRM_IMX8QXP_PIXEL_LINK_TO_DPI
+       tristate "Freescale i.MX8QXP pixel link to display pixel interface"
+       depends on OF
+       select DRM_KMS_HELPER
+       help
+         Choose this to enable pixel link to display pixel interface(PXL2DPI)
+         found in Freescale i.MX8qxp processor.
diff --git a/drivers/gpu/drm/bridge/imx/Makefile b/drivers/gpu/drm/bridge/imx/Makefile
new file mode 100644 (file)
index 0000000..aa90ec8
--- /dev/null
@@ -0,0 +1,9 @@
+imx8qm-ldb-objs := imx-ldb-helper.o imx8qm-ldb-drv.o
+obj-$(CONFIG_DRM_IMX8QM_LDB) += imx8qm-ldb.o
+
+imx8qxp-ldb-objs := imx-ldb-helper.o imx8qxp-ldb-drv.o
+obj-$(CONFIG_DRM_IMX8QXP_LDB) += imx8qxp-ldb.o
+
+obj-$(CONFIG_DRM_IMX8QXP_PIXEL_COMBINER) += imx8qxp-pixel-combiner.o
+obj-$(CONFIG_DRM_IMX8QXP_PIXEL_LINK) += imx8qxp-pixel-link.o
+obj-$(CONFIG_DRM_IMX8QXP_PIXEL_LINK_TO_DPI) += imx8qxp-pxl2dpi.o
diff --git a/drivers/gpu/drm/bridge/imx/imx-ldb-helper.c b/drivers/gpu/drm/bridge/imx/imx-ldb-helper.c
new file mode 100644 (file)
index 0000000..e85eb9a
--- /dev/null
@@ -0,0 +1,220 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2012 Sascha Hauer, Pengutronix
+ * Copyright 2019,2020,2022 NXP
+ */
+
+#include <linux/mfd/syscon.h>
+#include <linux/of.h>
+#include <linux/regmap.h>
+
+#include <drm/drm_bridge.h>
+#include <drm/drm_of.h>
+#include <drm/drm_print.h>
+
+#include "imx-ldb-helper.h"
+
+bool ldb_channel_is_single_link(struct ldb_channel *ldb_ch)
+{
+       return ldb_ch->link_type == LDB_CH_SINGLE_LINK;
+}
+
+bool ldb_channel_is_split_link(struct ldb_channel *ldb_ch)
+{
+       return ldb_ch->link_type == LDB_CH_DUAL_LINK_EVEN_ODD_PIXELS ||
+              ldb_ch->link_type == LDB_CH_DUAL_LINK_ODD_EVEN_PIXELS;
+}
+
+int ldb_bridge_atomic_check_helper(struct drm_bridge *bridge,
+                                  struct drm_bridge_state *bridge_state,
+                                  struct drm_crtc_state *crtc_state,
+                                  struct drm_connector_state *conn_state)
+{
+       struct ldb_channel *ldb_ch = bridge->driver_private;
+
+       ldb_ch->in_bus_format = bridge_state->input_bus_cfg.format;
+       ldb_ch->out_bus_format = bridge_state->output_bus_cfg.format;
+
+       return 0;
+}
+
+void ldb_bridge_mode_set_helper(struct drm_bridge *bridge,
+                               const struct drm_display_mode *mode,
+                               const struct drm_display_mode *adjusted_mode)
+{
+       struct ldb_channel *ldb_ch = bridge->driver_private;
+       struct ldb *ldb = ldb_ch->ldb;
+       bool is_split = ldb_channel_is_split_link(ldb_ch);
+
+       if (is_split)
+               ldb->ldb_ctrl |= LDB_SPLIT_MODE_EN;
+
+       switch (ldb_ch->out_bus_format) {
+       case MEDIA_BUS_FMT_RGB666_1X7X3_SPWG:
+               break;
+       case MEDIA_BUS_FMT_RGB888_1X7X4_SPWG:
+               if (ldb_ch->chno == 0 || is_split)
+                       ldb->ldb_ctrl |= LDB_DATA_WIDTH_CH0_24;
+               if (ldb_ch->chno == 1 || is_split)
+                       ldb->ldb_ctrl |= LDB_DATA_WIDTH_CH1_24;
+               break;
+       case MEDIA_BUS_FMT_RGB888_1X7X4_JEIDA:
+               if (ldb_ch->chno == 0 || is_split)
+                       ldb->ldb_ctrl |= LDB_DATA_WIDTH_CH0_24 |
+                                        LDB_BIT_MAP_CH0_JEIDA;
+               if (ldb_ch->chno == 1 || is_split)
+                       ldb->ldb_ctrl |= LDB_DATA_WIDTH_CH1_24 |
+                                        LDB_BIT_MAP_CH1_JEIDA;
+               break;
+       }
+}
+
+void ldb_bridge_enable_helper(struct drm_bridge *bridge)
+{
+       struct ldb_channel *ldb_ch = bridge->driver_private;
+       struct ldb *ldb = ldb_ch->ldb;
+
+       /*
+        * Platform specific bridge drivers should set ldb_ctrl properly
+        * for the enablement, so just write the ctrl_reg here.
+        */
+       regmap_write(ldb->regmap, ldb->ctrl_reg, ldb->ldb_ctrl);
+}
+
+void ldb_bridge_disable_helper(struct drm_bridge *bridge)
+{
+       struct ldb_channel *ldb_ch = bridge->driver_private;
+       struct ldb *ldb = ldb_ch->ldb;
+       bool is_split = ldb_channel_is_split_link(ldb_ch);
+
+       if (ldb_ch->chno == 0 || is_split)
+               ldb->ldb_ctrl &= ~LDB_CH0_MODE_EN_MASK;
+       if (ldb_ch->chno == 1 || is_split)
+               ldb->ldb_ctrl &= ~LDB_CH1_MODE_EN_MASK;
+
+       regmap_write(ldb->regmap, ldb->ctrl_reg, ldb->ldb_ctrl);
+}
+
+int ldb_bridge_attach_helper(struct drm_bridge *bridge,
+                            enum drm_bridge_attach_flags flags)
+{
+       struct ldb_channel *ldb_ch = bridge->driver_private;
+       struct ldb *ldb = ldb_ch->ldb;
+
+       if (!(flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR)) {
+               DRM_DEV_ERROR(ldb->dev,
+                             "do not support creating a drm_connector\n");
+               return -EINVAL;
+       }
+
+       if (!bridge->encoder) {
+               DRM_DEV_ERROR(ldb->dev, "missing encoder\n");
+               return -ENODEV;
+       }
+
+       return drm_bridge_attach(bridge->encoder,
+                               ldb_ch->next_bridge, bridge,
+                               DRM_BRIDGE_ATTACH_NO_CONNECTOR);
+}
+
+int ldb_init_helper(struct ldb *ldb)
+{
+       struct device *dev = ldb->dev;
+       struct device_node *np = dev->of_node;
+       struct device_node *child;
+       int ret;
+       u32 i;
+
+       ldb->regmap = syscon_node_to_regmap(np->parent);
+       if (IS_ERR(ldb->regmap)) {
+               ret = PTR_ERR(ldb->regmap);
+               if (ret != -EPROBE_DEFER)
+                       DRM_DEV_ERROR(dev, "failed to get regmap: %d\n", ret);
+               return ret;
+       }
+
+       for_each_available_child_of_node(np, child) {
+               struct ldb_channel *ldb_ch;
+
+               ret = of_property_read_u32(child, "reg", &i);
+               if (ret || i > MAX_LDB_CHAN_NUM - 1) {
+                       ret = -EINVAL;
+                       DRM_DEV_ERROR(dev,
+                                     "invalid channel node address: %u\n", i);
+                       of_node_put(child);
+                       return ret;
+               }
+
+               ldb_ch = ldb->channel[i];
+               ldb_ch->ldb = ldb;
+               ldb_ch->chno = i;
+               ldb_ch->is_available = true;
+               ldb_ch->np = child;
+
+               ldb->available_ch_cnt++;
+       }
+
+       return 0;
+}
+
+int ldb_find_next_bridge_helper(struct ldb *ldb)
+{
+       struct device *dev = ldb->dev;
+       struct ldb_channel *ldb_ch;
+       int ret, i;
+
+       for (i = 0; i < MAX_LDB_CHAN_NUM; i++) {
+               ldb_ch = ldb->channel[i];
+
+               if (!ldb_ch->is_available)
+                       continue;
+
+               ldb_ch->next_bridge = devm_drm_of_get_bridge(dev, ldb_ch->np,
+                                                            1, 0);
+               if (IS_ERR(ldb_ch->next_bridge)) {
+                       ret = PTR_ERR(ldb_ch->next_bridge);
+                       if (ret != -EPROBE_DEFER)
+                               DRM_DEV_ERROR(dev,
+                                             "failed to get next bridge: %d\n",
+                                             ret);
+                       return ret;
+               }
+       }
+
+       return 0;
+}
+
+void ldb_add_bridge_helper(struct ldb *ldb,
+                          const struct drm_bridge_funcs *bridge_funcs)
+{
+       struct ldb_channel *ldb_ch;
+       int i;
+
+       for (i = 0; i < MAX_LDB_CHAN_NUM; i++) {
+               ldb_ch = ldb->channel[i];
+
+               if (!ldb_ch->is_available)
+                       continue;
+
+               ldb_ch->bridge.driver_private = ldb_ch;
+               ldb_ch->bridge.funcs = bridge_funcs;
+               ldb_ch->bridge.of_node = ldb_ch->np;
+
+               drm_bridge_add(&ldb_ch->bridge);
+       }
+}
+
+void ldb_remove_bridge_helper(struct ldb *ldb)
+{
+       struct ldb_channel *ldb_ch;
+       int i;
+
+       for (i = 0; i < MAX_LDB_CHAN_NUM; i++) {
+               ldb_ch = ldb->channel[i];
+
+               if (!ldb_ch->is_available)
+                       continue;
+
+               drm_bridge_remove(&ldb_ch->bridge);
+       }
+}
diff --git a/drivers/gpu/drm/bridge/imx/imx-ldb-helper.h b/drivers/gpu/drm/bridge/imx/imx-ldb-helper.h
new file mode 100644 (file)
index 0000000..a0a5cde
--- /dev/null
@@ -0,0 +1,96 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+
+/*
+ * Copyright 2019,2020,2022 NXP
+ */
+
+#ifndef __IMX_LDB_HELPER__
+#define __IMX_LDB_HELPER__
+
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/of.h>
+#include <linux/regmap.h>
+
+#include <drm/drm_atomic.h>
+#include <drm/drm_bridge.h>
+#include <drm/drm_device.h>
+#include <drm/drm_encoder.h>
+#include <drm/drm_modeset_helper_vtables.h>
+
+#define LDB_CH0_MODE_EN_TO_DI0         BIT(0)
+#define LDB_CH0_MODE_EN_TO_DI1         (3 << 0)
+#define LDB_CH0_MODE_EN_MASK           (3 << 0)
+#define LDB_CH1_MODE_EN_TO_DI0         BIT(2)
+#define LDB_CH1_MODE_EN_TO_DI1         (3 << 2)
+#define LDB_CH1_MODE_EN_MASK           (3 << 2)
+#define LDB_SPLIT_MODE_EN              BIT(4)
+#define LDB_DATA_WIDTH_CH0_24          BIT(5)
+#define LDB_BIT_MAP_CH0_JEIDA          BIT(6)
+#define LDB_DATA_WIDTH_CH1_24          BIT(7)
+#define LDB_BIT_MAP_CH1_JEIDA          BIT(8)
+#define LDB_DI0_VS_POL_ACT_LOW         BIT(9)
+#define LDB_DI1_VS_POL_ACT_LOW         BIT(10)
+
+#define MAX_LDB_CHAN_NUM               2
+
+enum ldb_channel_link_type {
+       LDB_CH_SINGLE_LINK,
+       LDB_CH_DUAL_LINK_EVEN_ODD_PIXELS,
+       LDB_CH_DUAL_LINK_ODD_EVEN_PIXELS,
+};
+
+struct ldb;
+
+struct ldb_channel {
+       struct ldb *ldb;
+       struct drm_bridge bridge;
+       struct drm_bridge *next_bridge;
+       struct device_node *np;
+       u32 chno;
+       bool is_available;
+       u32 in_bus_format;
+       u32 out_bus_format;
+       enum ldb_channel_link_type link_type;
+};
+
+struct ldb {
+       struct regmap *regmap;
+       struct device *dev;
+       struct ldb_channel *channel[MAX_LDB_CHAN_NUM];
+       unsigned int ctrl_reg;
+       u32 ldb_ctrl;
+       unsigned int available_ch_cnt;
+};
+
+#define bridge_to_ldb_ch(b)    container_of(b, struct ldb_channel, bridge)
+
+bool ldb_channel_is_single_link(struct ldb_channel *ldb_ch);
+bool ldb_channel_is_split_link(struct ldb_channel *ldb_ch);
+
+int ldb_bridge_atomic_check_helper(struct drm_bridge *bridge,
+                                  struct drm_bridge_state *bridge_state,
+                                  struct drm_crtc_state *crtc_state,
+                                  struct drm_connector_state *conn_state);
+
+void ldb_bridge_mode_set_helper(struct drm_bridge *bridge,
+                               const struct drm_display_mode *mode,
+                               const struct drm_display_mode *adjusted_mode);
+
+void ldb_bridge_enable_helper(struct drm_bridge *bridge);
+
+void ldb_bridge_disable_helper(struct drm_bridge *bridge);
+
+int ldb_bridge_attach_helper(struct drm_bridge *bridge,
+                            enum drm_bridge_attach_flags flags);
+
+int ldb_init_helper(struct ldb *ldb);
+
+int ldb_find_next_bridge_helper(struct ldb *ldb);
+
+void ldb_add_bridge_helper(struct ldb *ldb,
+                          const struct drm_bridge_funcs *bridge_funcs);
+
+void ldb_remove_bridge_helper(struct ldb *ldb);
+
+#endif /* __IMX_LDB_HELPER__ */
diff --git a/drivers/gpu/drm/bridge/imx/imx8qm-ldb-drv.c b/drivers/gpu/drm/bridge/imx/imx8qm-ldb-drv.c
new file mode 100644 (file)
index 0000000..29f8f36
--- /dev/null
@@ -0,0 +1,587 @@
+// SPDX-License-Identifier: GPL-2.0+
+
+/*
+ * Copyright 2020 NXP
+ */
+
+#include <linux/clk.h>
+#include <linux/mfd/syscon.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/of_graph.h>
+#include <linux/phy/phy.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+
+#include <drm/drm_atomic_state_helper.h>
+#include <drm/drm_bridge.h>
+#include <drm/drm_connector.h>
+#include <drm/drm_fourcc.h>
+#include <drm/drm_of.h>
+#include <drm/drm_print.h>
+
+#include "imx-ldb-helper.h"
+
+#define  LDB_CH0_10BIT_EN              BIT(22)
+#define  LDB_CH1_10BIT_EN              BIT(23)
+#define  LDB_CH0_DATA_WIDTH_24BIT      BIT(24)
+#define  LDB_CH1_DATA_WIDTH_24BIT      BIT(26)
+#define  LDB_CH0_DATA_WIDTH_30BIT      (2 << 24)
+#define  LDB_CH1_DATA_WIDTH_30BIT      (2 << 26)
+
+#define SS_CTRL                                0x20
+#define  CH_HSYNC_M(id)                        BIT(0 + ((id) * 2))
+#define  CH_VSYNC_M(id)                        BIT(1 + ((id) * 2))
+#define  CH_PHSYNC(id)                 BIT(0 + ((id) * 2))
+#define  CH_PVSYNC(id)                 BIT(1 + ((id) * 2))
+
+#define DRIVER_NAME                    "imx8qm-ldb"
+
+struct imx8qm_ldb_channel {
+       struct ldb_channel base;
+       struct phy *phy;
+};
+
+struct imx8qm_ldb {
+       struct ldb base;
+       struct device *dev;
+       struct imx8qm_ldb_channel channel[MAX_LDB_CHAN_NUM];
+       struct clk *clk_pixel;
+       struct clk *clk_bypass;
+       int active_chno;
+};
+
+static inline struct imx8qm_ldb_channel *
+base_to_imx8qm_ldb_channel(struct ldb_channel *base)
+{
+       return container_of(base, struct imx8qm_ldb_channel, base);
+}
+
+static inline struct imx8qm_ldb *base_to_imx8qm_ldb(struct ldb *base)
+{
+       return container_of(base, struct imx8qm_ldb, base);
+}
+
+static void imx8qm_ldb_set_phy_cfg(struct imx8qm_ldb *imx8qm_ldb,
+                                  unsigned long di_clk,
+                                  bool is_split, bool is_slave,
+                                  struct phy_configure_opts_lvds *phy_cfg)
+{
+       phy_cfg->bits_per_lane_and_dclk_cycle = 7;
+       phy_cfg->lanes = 4;
+       phy_cfg->differential_clk_rate = is_split ? di_clk / 2 : di_clk;
+       phy_cfg->is_slave = is_slave;
+}
+
+static int imx8qm_ldb_bridge_atomic_check(struct drm_bridge *bridge,
+                                         struct drm_bridge_state *bridge_state,
+                                         struct drm_crtc_state *crtc_state,
+                                         struct drm_connector_state *conn_state)
+{
+       struct ldb_channel *ldb_ch = bridge->driver_private;
+       struct ldb *ldb = ldb_ch->ldb;
+       struct imx8qm_ldb_channel *imx8qm_ldb_ch =
+                                       base_to_imx8qm_ldb_channel(ldb_ch);
+       struct imx8qm_ldb *imx8qm_ldb = base_to_imx8qm_ldb(ldb);
+       struct drm_display_mode *adj = &crtc_state->adjusted_mode;
+       unsigned long di_clk = adj->clock * 1000;
+       bool is_split = ldb_channel_is_split_link(ldb_ch);
+       union phy_configure_opts opts = { };
+       struct phy_configure_opts_lvds *phy_cfg = &opts.lvds;
+       int ret;
+
+       ret = ldb_bridge_atomic_check_helper(bridge, bridge_state,
+                                            crtc_state, conn_state);
+       if (ret)
+               return ret;
+
+       imx8qm_ldb_set_phy_cfg(imx8qm_ldb, di_clk, is_split, false, phy_cfg);
+       ret = phy_validate(imx8qm_ldb_ch->phy, PHY_MODE_LVDS, 0, &opts);
+       if (ret < 0) {
+               DRM_DEV_DEBUG_DRIVER(imx8qm_ldb->dev,
+                                    "failed to validate PHY: %d\n", ret);
+               return ret;
+       }
+
+       if (is_split) {
+               imx8qm_ldb_ch =
+                       &imx8qm_ldb->channel[imx8qm_ldb->active_chno ^ 1];
+               imx8qm_ldb_set_phy_cfg(imx8qm_ldb, di_clk, is_split, true,
+                                      phy_cfg);
+               ret = phy_validate(imx8qm_ldb_ch->phy, PHY_MODE_LVDS, 0, &opts);
+               if (ret < 0) {
+                       DRM_DEV_DEBUG_DRIVER(imx8qm_ldb->dev,
+                                            "failed to validate slave PHY: %d\n",
+                                            ret);
+                       return ret;
+               }
+       }
+
+       return ret;
+}
+
+static void
+imx8qm_ldb_bridge_mode_set(struct drm_bridge *bridge,
+                          const struct drm_display_mode *mode,
+                          const struct drm_display_mode *adjusted_mode)
+{
+       struct ldb_channel *ldb_ch = bridge->driver_private;
+       struct ldb *ldb = ldb_ch->ldb;
+       struct imx8qm_ldb_channel *imx8qm_ldb_ch =
+                                       base_to_imx8qm_ldb_channel(ldb_ch);
+       struct imx8qm_ldb *imx8qm_ldb = base_to_imx8qm_ldb(ldb);
+       struct device *dev = imx8qm_ldb->dev;
+       unsigned long di_clk = adjusted_mode->clock * 1000;
+       bool is_split = ldb_channel_is_split_link(ldb_ch);
+       union phy_configure_opts opts = { };
+       struct phy_configure_opts_lvds *phy_cfg = &opts.lvds;
+       u32 chno = ldb_ch->chno;
+       int ret;
+
+       ret = pm_runtime_get_sync(dev);
+       if (ret < 0)
+               DRM_DEV_ERROR(dev, "failed to get runtime PM sync: %d\n", ret);
+
+       ret = phy_init(imx8qm_ldb_ch->phy);
+       if (ret < 0)
+               DRM_DEV_ERROR(dev, "failed to initialize PHY: %d\n", ret);
+
+       clk_set_rate(imx8qm_ldb->clk_bypass, di_clk);
+       clk_set_rate(imx8qm_ldb->clk_pixel, di_clk);
+
+       imx8qm_ldb_set_phy_cfg(imx8qm_ldb, di_clk, is_split, false, phy_cfg);
+       ret = phy_configure(imx8qm_ldb_ch->phy, &opts);
+       if (ret < 0)
+               DRM_DEV_ERROR(dev, "failed to configure PHY: %d\n", ret);
+
+       if (is_split) {
+               imx8qm_ldb_ch =
+                       &imx8qm_ldb->channel[imx8qm_ldb->active_chno ^ 1];
+               imx8qm_ldb_set_phy_cfg(imx8qm_ldb, di_clk, is_split, true,
+                                      phy_cfg);
+               ret = phy_configure(imx8qm_ldb_ch->phy, &opts);
+               if (ret < 0)
+                       DRM_DEV_ERROR(dev, "failed to configure slave PHY: %d\n",
+                                     ret);
+       }
+
+       /* input VSYNC signal from pixel link is active low */
+       if (ldb_ch->chno == 0 || is_split)
+               ldb->ldb_ctrl |= LDB_DI0_VS_POL_ACT_LOW;
+       if (ldb_ch->chno == 1 || is_split)
+               ldb->ldb_ctrl |= LDB_DI1_VS_POL_ACT_LOW;
+
+       switch (ldb_ch->out_bus_format) {
+       case MEDIA_BUS_FMT_RGB666_1X7X3_SPWG:
+               break;
+       case MEDIA_BUS_FMT_RGB888_1X7X4_JEIDA:
+       case MEDIA_BUS_FMT_RGB888_1X7X4_SPWG:
+               if (ldb_ch->chno == 0 || is_split)
+                       ldb->ldb_ctrl |= LDB_CH0_DATA_WIDTH_24BIT;
+               if (ldb_ch->chno == 1 || is_split)
+                       ldb->ldb_ctrl |= LDB_CH1_DATA_WIDTH_24BIT;
+               break;
+       }
+
+       ldb_bridge_mode_set_helper(bridge, mode, adjusted_mode);
+
+       if (adjusted_mode->flags & DRM_MODE_FLAG_NVSYNC)
+               regmap_update_bits(ldb->regmap, SS_CTRL, CH_VSYNC_M(chno), 0);
+       else if (adjusted_mode->flags & DRM_MODE_FLAG_PVSYNC)
+               regmap_update_bits(ldb->regmap, SS_CTRL,
+                                  CH_VSYNC_M(chno), CH_PVSYNC(chno));
+
+       if (adjusted_mode->flags & DRM_MODE_FLAG_NHSYNC)
+               regmap_update_bits(ldb->regmap, SS_CTRL, CH_HSYNC_M(chno), 0);
+       else if (adjusted_mode->flags & DRM_MODE_FLAG_PHSYNC)
+               regmap_update_bits(ldb->regmap, SS_CTRL,
+                                  CH_HSYNC_M(chno), CH_PHSYNC(chno));
+}
+
+static void
+imx8qm_ldb_bridge_atomic_enable(struct drm_bridge *bridge,
+                               struct drm_bridge_state *old_bridge_state)
+{
+       struct ldb_channel *ldb_ch = bridge->driver_private;
+       struct ldb *ldb = ldb_ch->ldb;
+       struct imx8qm_ldb_channel *imx8qm_ldb_ch =
+                                       base_to_imx8qm_ldb_channel(ldb_ch);
+       struct imx8qm_ldb *imx8qm_ldb = base_to_imx8qm_ldb(ldb);
+       struct device *dev = imx8qm_ldb->dev;
+       bool is_split = ldb_channel_is_split_link(ldb_ch);
+       int ret;
+
+       clk_prepare_enable(imx8qm_ldb->clk_pixel);
+       clk_prepare_enable(imx8qm_ldb->clk_bypass);
+
+       /* both DI0 and DI1 connect with pixel link, so ok to use DI0 only */
+       if (ldb_ch->chno == 0 || is_split) {
+               ldb->ldb_ctrl &= ~LDB_CH0_MODE_EN_MASK;
+               ldb->ldb_ctrl |= LDB_CH0_MODE_EN_TO_DI0;
+       }
+       if (ldb_ch->chno == 1 || is_split) {
+               ldb->ldb_ctrl &= ~LDB_CH1_MODE_EN_MASK;
+               ldb->ldb_ctrl |= LDB_CH1_MODE_EN_TO_DI0;
+       }
+
+       if (is_split) {
+               ret = phy_power_on(imx8qm_ldb->channel[0].phy);
+               if (ret)
+                       DRM_DEV_ERROR(dev,
+                                     "failed to power on channel0 PHY: %d\n",
+                                     ret);
+
+               ret = phy_power_on(imx8qm_ldb->channel[1].phy);
+               if (ret)
+                       DRM_DEV_ERROR(dev,
+                                     "failed to power on channel1 PHY: %d\n",
+                                     ret);
+       } else {
+               ret = phy_power_on(imx8qm_ldb_ch->phy);
+               if (ret)
+                       DRM_DEV_ERROR(dev, "failed to power on PHY: %d\n", ret);
+       }
+
+       ldb_bridge_enable_helper(bridge);
+}
+
+static void
+imx8qm_ldb_bridge_atomic_disable(struct drm_bridge *bridge,
+                                struct drm_bridge_state *old_bridge_state)
+{
+       struct ldb_channel *ldb_ch = bridge->driver_private;
+       struct ldb *ldb = ldb_ch->ldb;
+       struct imx8qm_ldb_channel *imx8qm_ldb_ch =
+                                       base_to_imx8qm_ldb_channel(ldb_ch);
+       struct imx8qm_ldb *imx8qm_ldb = base_to_imx8qm_ldb(ldb);
+       struct device *dev = imx8qm_ldb->dev;
+       bool is_split = ldb_channel_is_split_link(ldb_ch);
+       int ret;
+
+       ldb_bridge_disable_helper(bridge);
+
+       if (is_split) {
+               ret = phy_power_off(imx8qm_ldb->channel[0].phy);
+               if (ret)
+                       DRM_DEV_ERROR(dev,
+                                     "failed to power off channel0 PHY: %d\n",
+                                     ret);
+               ret = phy_power_off(imx8qm_ldb->channel[1].phy);
+               if (ret)
+                       DRM_DEV_ERROR(dev,
+                                     "failed to power off channel1 PHY: %d\n",
+                                     ret);
+       } else {
+               ret = phy_power_off(imx8qm_ldb_ch->phy);
+               if (ret)
+                       DRM_DEV_ERROR(dev, "failed to power off PHY: %d\n", ret);
+       }
+
+       clk_disable_unprepare(imx8qm_ldb->clk_bypass);
+       clk_disable_unprepare(imx8qm_ldb->clk_pixel);
+
+       ret = pm_runtime_put(dev);
+       if (ret < 0)
+               DRM_DEV_ERROR(dev, "failed to put runtime PM: %d\n", ret);
+}
+
+static const u32 imx8qm_ldb_bus_output_fmts[] = {
+       MEDIA_BUS_FMT_RGB666_1X7X3_SPWG,
+       MEDIA_BUS_FMT_RGB888_1X7X4_SPWG,
+       MEDIA_BUS_FMT_RGB888_1X7X4_JEIDA,
+       MEDIA_BUS_FMT_FIXED,
+};
+
+static bool imx8qm_ldb_bus_output_fmt_supported(u32 fmt)
+{
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(imx8qm_ldb_bus_output_fmts); i++) {
+               if (imx8qm_ldb_bus_output_fmts[i] == fmt)
+                       return true;
+       }
+
+       return false;
+}
+
+static u32 *
+imx8qm_ldb_bridge_atomic_get_input_bus_fmts(struct drm_bridge *bridge,
+                                           struct drm_bridge_state *bridge_state,
+                                           struct drm_crtc_state *crtc_state,
+                                           struct drm_connector_state *conn_state,
+                                           u32 output_fmt,
+                                           unsigned int *num_input_fmts)
+{
+       struct drm_display_info *di;
+       const struct drm_format_info *finfo;
+       u32 *input_fmts;
+
+       if (!imx8qm_ldb_bus_output_fmt_supported(output_fmt))
+               return NULL;
+
+       *num_input_fmts = 1;
+
+       input_fmts = kmalloc(sizeof(*input_fmts), GFP_KERNEL);
+       if (!input_fmts)
+               return NULL;
+
+       switch (output_fmt) {
+       case MEDIA_BUS_FMT_FIXED:
+               di = &conn_state->connector->display_info;
+
+               /*
+                * Look at the first bus format to determine input format.
+                * Default to MEDIA_BUS_FMT_RGB888_1X36_CPADLO, if no match.
+                */
+               if (di->num_bus_formats) {
+                       finfo = drm_format_info(di->bus_formats[0]);
+
+                       input_fmts[0] = finfo->depth == 18 ?
+                                       MEDIA_BUS_FMT_RGB666_1X36_CPADLO :
+                                       MEDIA_BUS_FMT_RGB888_1X36_CPADLO;
+               } else {
+                       input_fmts[0] = MEDIA_BUS_FMT_RGB888_1X36_CPADLO;
+               }
+               break;
+       case MEDIA_BUS_FMT_RGB666_1X7X3_SPWG:
+               input_fmts[0] = MEDIA_BUS_FMT_RGB666_1X36_CPADLO;
+               break;
+       case MEDIA_BUS_FMT_RGB888_1X7X4_SPWG:
+       case MEDIA_BUS_FMT_RGB888_1X7X4_JEIDA:
+               input_fmts[0] = MEDIA_BUS_FMT_RGB888_1X36_CPADLO;
+               break;
+       default:
+               kfree(input_fmts);
+               input_fmts = NULL;
+               break;
+       }
+
+       return input_fmts;
+}
+
+static u32 *
+imx8qm_ldb_bridge_atomic_get_output_bus_fmts(struct drm_bridge *bridge,
+                                            struct drm_bridge_state *bridge_state,
+                                            struct drm_crtc_state *crtc_state,
+                                            struct drm_connector_state *conn_state,
+                                            unsigned int *num_output_fmts)
+{
+       *num_output_fmts = ARRAY_SIZE(imx8qm_ldb_bus_output_fmts);
+       return kmemdup(imx8qm_ldb_bus_output_fmts,
+                       sizeof(imx8qm_ldb_bus_output_fmts), GFP_KERNEL);
+}
+
+static enum drm_mode_status
+imx8qm_ldb_bridge_mode_valid(struct drm_bridge *bridge,
+                            const struct drm_display_info *info,
+                            const struct drm_display_mode *mode)
+{
+       struct ldb_channel *ldb_ch = bridge->driver_private;
+       bool is_single = ldb_channel_is_single_link(ldb_ch);
+
+       if (mode->clock > 300000)
+               return MODE_CLOCK_HIGH;
+
+       if (mode->clock > 150000 && is_single)
+               return MODE_CLOCK_HIGH;
+
+       return MODE_OK;
+}
+
+static const struct drm_bridge_funcs imx8qm_ldb_bridge_funcs = {
+       .atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state,
+       .atomic_destroy_state   = drm_atomic_helper_bridge_destroy_state,
+       .atomic_reset           = drm_atomic_helper_bridge_reset,
+       .mode_valid             = imx8qm_ldb_bridge_mode_valid,
+       .attach                 = ldb_bridge_attach_helper,
+       .atomic_check           = imx8qm_ldb_bridge_atomic_check,
+       .mode_set               = imx8qm_ldb_bridge_mode_set,
+       .atomic_enable          = imx8qm_ldb_bridge_atomic_enable,
+       .atomic_disable         = imx8qm_ldb_bridge_atomic_disable,
+       .atomic_get_input_bus_fmts =
+                       imx8qm_ldb_bridge_atomic_get_input_bus_fmts,
+       .atomic_get_output_bus_fmts =
+                       imx8qm_ldb_bridge_atomic_get_output_bus_fmts,
+};
+
+static int imx8qm_ldb_get_phy(struct imx8qm_ldb *imx8qm_ldb)
+{
+       struct imx8qm_ldb_channel *imx8qm_ldb_ch;
+       struct ldb_channel *ldb_ch;
+       struct device *dev = imx8qm_ldb->dev;
+       int i, ret;
+
+       for (i = 0; i < MAX_LDB_CHAN_NUM; i++) {
+               imx8qm_ldb_ch = &imx8qm_ldb->channel[i];
+               ldb_ch = &imx8qm_ldb_ch->base;
+
+               if (!ldb_ch->is_available)
+                       continue;
+
+               imx8qm_ldb_ch->phy = devm_of_phy_get(dev, ldb_ch->np,
+                                                    "lvds_phy");
+               if (IS_ERR(imx8qm_ldb_ch->phy)) {
+                       ret = PTR_ERR(imx8qm_ldb_ch->phy);
+                       if (ret != -EPROBE_DEFER)
+                               DRM_DEV_ERROR(dev,
+                                             "failed to get channel%d PHY: %d\n",
+                                             i, ret);
+                       return ret;
+               }
+       }
+
+       return 0;
+}
+
+static int imx8qm_ldb_probe(struct platform_device *pdev)
+{
+       struct device *dev = &pdev->dev;
+       struct imx8qm_ldb *imx8qm_ldb;
+       struct imx8qm_ldb_channel *imx8qm_ldb_ch;
+       struct ldb *ldb;
+       struct ldb_channel *ldb_ch;
+       struct device_node *port1, *port2;
+       int pixel_order;
+       int ret, i;
+
+       imx8qm_ldb = devm_kzalloc(dev, sizeof(*imx8qm_ldb), GFP_KERNEL);
+       if (!imx8qm_ldb)
+               return -ENOMEM;
+
+       imx8qm_ldb->clk_pixel = devm_clk_get(dev, "pixel");
+       if (IS_ERR(imx8qm_ldb->clk_pixel)) {
+               ret = PTR_ERR(imx8qm_ldb->clk_pixel);
+               if (ret != -EPROBE_DEFER)
+                       DRM_DEV_ERROR(dev,
+                                     "failed to get pixel clock: %d\n", ret);
+               return ret;
+       }
+
+       imx8qm_ldb->clk_bypass = devm_clk_get(dev, "bypass");
+       if (IS_ERR(imx8qm_ldb->clk_bypass)) {
+               ret = PTR_ERR(imx8qm_ldb->clk_bypass);
+               if (ret != -EPROBE_DEFER)
+                       DRM_DEV_ERROR(dev,
+                                     "failed to get bypass clock: %d\n", ret);
+               return ret;
+       }
+
+       imx8qm_ldb->dev = dev;
+
+       ldb = &imx8qm_ldb->base;
+       ldb->dev = dev;
+       ldb->ctrl_reg = 0xe0;
+
+       for (i = 0; i < MAX_LDB_CHAN_NUM; i++)
+               ldb->channel[i] = &imx8qm_ldb->channel[i].base;
+
+       ret = ldb_init_helper(ldb);
+       if (ret)
+               return ret;
+
+       if (ldb->available_ch_cnt == 0) {
+               DRM_DEV_DEBUG_DRIVER(dev, "no available channel\n");
+               return 0;
+       }
+
+       if (ldb->available_ch_cnt == 2) {
+               port1 = of_graph_get_port_by_id(ldb->channel[0]->np, 1);
+               port2 = of_graph_get_port_by_id(ldb->channel[1]->np, 1);
+               pixel_order =
+                       drm_of_lvds_get_dual_link_pixel_order(port1, port2);
+               of_node_put(port1);
+               of_node_put(port2);
+
+               if (pixel_order != DRM_LVDS_DUAL_LINK_ODD_EVEN_PIXELS) {
+                       DRM_DEV_ERROR(dev, "invalid dual link pixel order: %d\n",
+                                     pixel_order);
+                       return -EINVAL;
+               }
+
+               imx8qm_ldb->active_chno = 0;
+               imx8qm_ldb_ch = &imx8qm_ldb->channel[0];
+               ldb_ch = &imx8qm_ldb_ch->base;
+               ldb_ch->link_type = pixel_order;
+       } else {
+               for (i = 0; i < MAX_LDB_CHAN_NUM; i++) {
+                       imx8qm_ldb_ch = &imx8qm_ldb->channel[i];
+                       ldb_ch = &imx8qm_ldb_ch->base;
+
+                       if (ldb_ch->is_available) {
+                               imx8qm_ldb->active_chno = ldb_ch->chno;
+                               break;
+                       }
+               }
+       }
+
+       ret = imx8qm_ldb_get_phy(imx8qm_ldb);
+       if (ret)
+               return ret;
+
+       ret = ldb_find_next_bridge_helper(ldb);
+       if (ret)
+               return ret;
+
+       platform_set_drvdata(pdev, imx8qm_ldb);
+       pm_runtime_enable(dev);
+
+       ldb_add_bridge_helper(ldb, &imx8qm_ldb_bridge_funcs);
+
+       return ret;
+}
+
+static int imx8qm_ldb_remove(struct platform_device *pdev)
+{
+       struct imx8qm_ldb *imx8qm_ldb = platform_get_drvdata(pdev);
+       struct ldb *ldb = &imx8qm_ldb->base;
+
+       ldb_remove_bridge_helper(ldb);
+
+       pm_runtime_disable(&pdev->dev);
+
+       return 0;
+}
+
+static int __maybe_unused imx8qm_ldb_runtime_suspend(struct device *dev)
+{
+       return 0;
+}
+
+static int __maybe_unused imx8qm_ldb_runtime_resume(struct device *dev)
+{
+       struct imx8qm_ldb *imx8qm_ldb = dev_get_drvdata(dev);
+       struct ldb *ldb = &imx8qm_ldb->base;
+
+       /* disable LDB by resetting the control register to POR default */
+       regmap_write(ldb->regmap, ldb->ctrl_reg, 0);
+
+       return 0;
+}
+
+static const struct dev_pm_ops imx8qm_ldb_pm_ops = {
+       SET_RUNTIME_PM_OPS(imx8qm_ldb_runtime_suspend,
+                          imx8qm_ldb_runtime_resume, NULL)
+};
+
+static const struct of_device_id imx8qm_ldb_dt_ids[] = {
+       { .compatible = "fsl,imx8qm-ldb" },
+       { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, imx8qm_ldb_dt_ids);
+
+static struct platform_driver imx8qm_ldb_driver = {
+       .probe  = imx8qm_ldb_probe,
+       .remove = imx8qm_ldb_remove,
+       .driver = {
+               .pm = &imx8qm_ldb_pm_ops,
+               .name = DRIVER_NAME,
+               .of_match_table = imx8qm_ldb_dt_ids,
+       },
+};
+module_platform_driver(imx8qm_ldb_driver);
+
+MODULE_DESCRIPTION("i.MX8QM LVDS Display Bridge(LDB)/Pixel Mapper bridge driver");
+MODULE_AUTHOR("Liu Ying <victor.liu@nxp.com>");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:" DRIVER_NAME);
diff --git a/drivers/gpu/drm/bridge/imx/imx8qxp-ldb-drv.c b/drivers/gpu/drm/bridge/imx/imx8qxp-ldb-drv.c
new file mode 100644 (file)
index 0000000..1cca5fc
--- /dev/null
@@ -0,0 +1,722 @@
+// SPDX-License-Identifier: GPL-2.0+
+
+/*
+ * Copyright 2020 NXP
+ */
+
+#include <linux/clk.h>
+#include <linux/mfd/syscon.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/of_graph.h>
+#include <linux/phy/phy.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+
+#include <drm/drm_atomic_state_helper.h>
+#include <drm/drm_bridge.h>
+#include <drm/drm_connector.h>
+#include <drm/drm_fourcc.h>
+#include <drm/drm_of.h>
+#include <drm/drm_print.h>
+
+#include "imx-ldb-helper.h"
+
+#define  LDB_CH_SEL            BIT(28)
+
+#define SS_CTRL                        0x20
+#define  CH_HSYNC_M(id)                BIT(0 + ((id) * 2))
+#define  CH_VSYNC_M(id)                BIT(1 + ((id) * 2))
+#define  CH_PHSYNC(id)         BIT(0 + ((id) * 2))
+#define  CH_PVSYNC(id)         BIT(1 + ((id) * 2))
+
+#define DRIVER_NAME            "imx8qxp-ldb"
+
+struct imx8qxp_ldb_channel {
+       struct ldb_channel base;
+       struct phy *phy;
+       unsigned int di_id;
+};
+
+struct imx8qxp_ldb {
+       struct ldb base;
+       struct device *dev;
+       struct imx8qxp_ldb_channel channel[MAX_LDB_CHAN_NUM];
+       struct clk *clk_pixel;
+       struct clk *clk_bypass;
+       struct drm_bridge *companion;
+       int active_chno;
+};
+
+static inline struct imx8qxp_ldb_channel *
+base_to_imx8qxp_ldb_channel(struct ldb_channel *base)
+{
+       return container_of(base, struct imx8qxp_ldb_channel, base);
+}
+
+static inline struct imx8qxp_ldb *base_to_imx8qxp_ldb(struct ldb *base)
+{
+       return container_of(base, struct imx8qxp_ldb, base);
+}
+
+static void imx8qxp_ldb_set_phy_cfg(struct imx8qxp_ldb *imx8qxp_ldb,
+                                   unsigned long di_clk, bool is_split,
+                                   struct phy_configure_opts_lvds *phy_cfg)
+{
+       phy_cfg->bits_per_lane_and_dclk_cycle = 7;
+       phy_cfg->lanes = 4;
+
+       if (is_split) {
+               phy_cfg->differential_clk_rate = di_clk / 2;
+               phy_cfg->is_slave = !imx8qxp_ldb->companion;
+       } else {
+               phy_cfg->differential_clk_rate = di_clk;
+               phy_cfg->is_slave = false;
+       }
+}
+
+static int
+imx8qxp_ldb_bridge_atomic_check(struct drm_bridge *bridge,
+                               struct drm_bridge_state *bridge_state,
+                               struct drm_crtc_state *crtc_state,
+                               struct drm_connector_state *conn_state)
+{
+       struct ldb_channel *ldb_ch = bridge->driver_private;
+       struct ldb *ldb = ldb_ch->ldb;
+       struct imx8qxp_ldb_channel *imx8qxp_ldb_ch =
+                                       base_to_imx8qxp_ldb_channel(ldb_ch);
+       struct imx8qxp_ldb *imx8qxp_ldb = base_to_imx8qxp_ldb(ldb);
+       struct drm_bridge *companion = imx8qxp_ldb->companion;
+       struct drm_display_mode *adj = &crtc_state->adjusted_mode;
+       unsigned long di_clk = adj->clock * 1000;
+       bool is_split = ldb_channel_is_split_link(ldb_ch);
+       union phy_configure_opts opts = { };
+       struct phy_configure_opts_lvds *phy_cfg = &opts.lvds;
+       int ret;
+
+       ret = ldb_bridge_atomic_check_helper(bridge, bridge_state,
+                                            crtc_state, conn_state);
+       if (ret)
+               return ret;
+
+       imx8qxp_ldb_set_phy_cfg(imx8qxp_ldb, di_clk, is_split, phy_cfg);
+       ret = phy_validate(imx8qxp_ldb_ch->phy, PHY_MODE_LVDS, 0, &opts);
+       if (ret < 0) {
+               DRM_DEV_DEBUG_DRIVER(imx8qxp_ldb->dev,
+                                    "failed to validate PHY: %d\n", ret);
+               return ret;
+       }
+
+       if (is_split && companion) {
+               ret = companion->funcs->atomic_check(companion,
+                                       bridge_state, crtc_state, conn_state);
+               if (ret)
+                       return ret;
+       }
+
+       return ret;
+}
+
+static void
+imx8qxp_ldb_bridge_mode_set(struct drm_bridge *bridge,
+                           const struct drm_display_mode *mode,
+                           const struct drm_display_mode *adjusted_mode)
+{
+       struct ldb_channel *ldb_ch = bridge->driver_private;
+       struct ldb_channel *companion_ldb_ch;
+       struct ldb *ldb = ldb_ch->ldb;
+       struct imx8qxp_ldb_channel *imx8qxp_ldb_ch =
+                                       base_to_imx8qxp_ldb_channel(ldb_ch);
+       struct imx8qxp_ldb *imx8qxp_ldb = base_to_imx8qxp_ldb(ldb);
+       struct drm_bridge *companion = imx8qxp_ldb->companion;
+       struct device *dev = imx8qxp_ldb->dev;
+       unsigned long di_clk = adjusted_mode->clock * 1000;
+       bool is_split = ldb_channel_is_split_link(ldb_ch);
+       union phy_configure_opts opts = { };
+       struct phy_configure_opts_lvds *phy_cfg = &opts.lvds;
+       u32 chno = ldb_ch->chno;
+       int ret;
+
+       ret = pm_runtime_get_sync(dev);
+       if (ret < 0)
+               DRM_DEV_ERROR(dev, "failed to get runtime PM sync: %d\n", ret);
+
+       ret = phy_init(imx8qxp_ldb_ch->phy);
+       if (ret < 0)
+               DRM_DEV_ERROR(dev, "failed to initialize PHY: %d\n", ret);
+
+       ret = phy_set_mode(imx8qxp_ldb_ch->phy, PHY_MODE_LVDS);
+       if (ret < 0)
+               DRM_DEV_ERROR(dev, "failed to set PHY mode: %d\n", ret);
+
+       if (is_split && companion) {
+               companion_ldb_ch = bridge_to_ldb_ch(companion);
+
+               companion_ldb_ch->in_bus_format = ldb_ch->in_bus_format;
+               companion_ldb_ch->out_bus_format = ldb_ch->out_bus_format;
+       }
+
+       clk_set_rate(imx8qxp_ldb->clk_bypass, di_clk);
+       clk_set_rate(imx8qxp_ldb->clk_pixel, di_clk);
+
+       imx8qxp_ldb_set_phy_cfg(imx8qxp_ldb, di_clk, is_split, phy_cfg);
+       ret = phy_configure(imx8qxp_ldb_ch->phy, &opts);
+       if (ret < 0)
+               DRM_DEV_ERROR(dev, "failed to configure PHY: %d\n", ret);
+
+       if (chno == 0)
+               ldb->ldb_ctrl &= ~LDB_CH_SEL;
+       else
+               ldb->ldb_ctrl |= LDB_CH_SEL;
+
+       /* input VSYNC signal from pixel link is active low */
+       if (imx8qxp_ldb_ch->di_id == 0)
+               ldb->ldb_ctrl |= LDB_DI0_VS_POL_ACT_LOW;
+       else
+               ldb->ldb_ctrl |= LDB_DI1_VS_POL_ACT_LOW;
+
+       /*
+        * For split mode, settle input VSYNC signal polarity and
+        * channel selection down early.
+        */
+       if (is_split)
+               regmap_write(ldb->regmap, ldb->ctrl_reg, ldb->ldb_ctrl);
+
+       ldb_bridge_mode_set_helper(bridge, mode, adjusted_mode);
+
+       if (adjusted_mode->flags & DRM_MODE_FLAG_NVSYNC)
+               regmap_update_bits(ldb->regmap, SS_CTRL, CH_VSYNC_M(chno), 0);
+       else if (adjusted_mode->flags & DRM_MODE_FLAG_PVSYNC)
+               regmap_update_bits(ldb->regmap, SS_CTRL,
+                                  CH_VSYNC_M(chno), CH_PVSYNC(chno));
+
+       if (adjusted_mode->flags & DRM_MODE_FLAG_NHSYNC)
+               regmap_update_bits(ldb->regmap, SS_CTRL, CH_HSYNC_M(chno), 0);
+       else if (adjusted_mode->flags & DRM_MODE_FLAG_PHSYNC)
+               regmap_update_bits(ldb->regmap, SS_CTRL,
+                                  CH_HSYNC_M(chno), CH_PHSYNC(chno));
+
+       if (is_split && companion)
+               companion->funcs->mode_set(companion, mode, adjusted_mode);
+}
+
+static void
+imx8qxp_ldb_bridge_atomic_pre_enable(struct drm_bridge *bridge,
+                                    struct drm_bridge_state *old_bridge_state)
+{
+       struct ldb_channel *ldb_ch = bridge->driver_private;
+       struct ldb *ldb = ldb_ch->ldb;
+       struct imx8qxp_ldb *imx8qxp_ldb = base_to_imx8qxp_ldb(ldb);
+       struct drm_bridge *companion = imx8qxp_ldb->companion;
+       bool is_split = ldb_channel_is_split_link(ldb_ch);
+
+       clk_prepare_enable(imx8qxp_ldb->clk_pixel);
+       clk_prepare_enable(imx8qxp_ldb->clk_bypass);
+
+       if (is_split && companion)
+               companion->funcs->atomic_pre_enable(companion, old_bridge_state);
+}
+
+static void
+imx8qxp_ldb_bridge_atomic_enable(struct drm_bridge *bridge,
+                                struct drm_bridge_state *old_bridge_state)
+{
+       struct ldb_channel *ldb_ch = bridge->driver_private;
+       struct ldb *ldb = ldb_ch->ldb;
+       struct imx8qxp_ldb_channel *imx8qxp_ldb_ch =
+                                       base_to_imx8qxp_ldb_channel(ldb_ch);
+       struct imx8qxp_ldb *imx8qxp_ldb = base_to_imx8qxp_ldb(ldb);
+       struct drm_bridge *companion = imx8qxp_ldb->companion;
+       struct device *dev = imx8qxp_ldb->dev;
+       bool is_split = ldb_channel_is_split_link(ldb_ch);
+       int ret;
+
+       if (ldb_ch->chno == 0 || is_split) {
+               ldb->ldb_ctrl &= ~LDB_CH0_MODE_EN_MASK;
+               ldb->ldb_ctrl |= imx8qxp_ldb_ch->di_id == 0 ?
+                               LDB_CH0_MODE_EN_TO_DI0 : LDB_CH0_MODE_EN_TO_DI1;
+       }
+       if (ldb_ch->chno == 1 || is_split) {
+               ldb->ldb_ctrl &= ~LDB_CH1_MODE_EN_MASK;
+               ldb->ldb_ctrl |= imx8qxp_ldb_ch->di_id == 0 ?
+                               LDB_CH1_MODE_EN_TO_DI0 : LDB_CH1_MODE_EN_TO_DI1;
+       }
+
+       ldb_bridge_enable_helper(bridge);
+
+       ret = phy_power_on(imx8qxp_ldb_ch->phy);
+       if (ret)
+               DRM_DEV_ERROR(dev, "failed to power on PHY: %d\n", ret);
+
+       if (is_split && companion)
+               companion->funcs->atomic_enable(companion, old_bridge_state);
+}
+
+static void
+imx8qxp_ldb_bridge_atomic_disable(struct drm_bridge *bridge,
+                                 struct drm_bridge_state *old_bridge_state)
+{
+       struct ldb_channel *ldb_ch = bridge->driver_private;
+       struct ldb *ldb = ldb_ch->ldb;
+       struct imx8qxp_ldb_channel *imx8qxp_ldb_ch =
+                                       base_to_imx8qxp_ldb_channel(ldb_ch);
+       struct imx8qxp_ldb *imx8qxp_ldb = base_to_imx8qxp_ldb(ldb);
+       struct drm_bridge *companion = imx8qxp_ldb->companion;
+       struct device *dev = imx8qxp_ldb->dev;
+       bool is_split = ldb_channel_is_split_link(ldb_ch);
+       int ret;
+
+       ret = phy_power_off(imx8qxp_ldb_ch->phy);
+       if (ret)
+               DRM_DEV_ERROR(dev, "failed to power off PHY: %d\n", ret);
+
+       ret = phy_exit(imx8qxp_ldb_ch->phy);
+       if (ret < 0)
+               DRM_DEV_ERROR(dev, "failed to teardown PHY: %d\n", ret);
+
+       ldb_bridge_disable_helper(bridge);
+
+       clk_disable_unprepare(imx8qxp_ldb->clk_bypass);
+       clk_disable_unprepare(imx8qxp_ldb->clk_pixel);
+
+       if (is_split && companion)
+               companion->funcs->atomic_disable(companion, old_bridge_state);
+
+       ret = pm_runtime_put(dev);
+       if (ret < 0)
+               DRM_DEV_ERROR(dev, "failed to put runtime PM: %d\n", ret);
+}
+
+static const u32 imx8qxp_ldb_bus_output_fmts[] = {
+       MEDIA_BUS_FMT_RGB666_1X7X3_SPWG,
+       MEDIA_BUS_FMT_RGB888_1X7X4_SPWG,
+       MEDIA_BUS_FMT_RGB888_1X7X4_JEIDA,
+       MEDIA_BUS_FMT_FIXED,
+};
+
+static bool imx8qxp_ldb_bus_output_fmt_supported(u32 fmt)
+{
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(imx8qxp_ldb_bus_output_fmts); i++) {
+               if (imx8qxp_ldb_bus_output_fmts[i] == fmt)
+                       return true;
+       }
+
+       return false;
+}
+
+static u32 *
+imx8qxp_ldb_bridge_atomic_get_input_bus_fmts(struct drm_bridge *bridge,
+                                            struct drm_bridge_state *bridge_state,
+                                            struct drm_crtc_state *crtc_state,
+                                            struct drm_connector_state *conn_state,
+                                            u32 output_fmt,
+                                            unsigned int *num_input_fmts)
+{
+       struct drm_display_info *di;
+       const struct drm_format_info *finfo;
+       u32 *input_fmts;
+
+       if (!imx8qxp_ldb_bus_output_fmt_supported(output_fmt))
+               return NULL;
+
+       *num_input_fmts = 1;
+
+       input_fmts = kmalloc(sizeof(*input_fmts), GFP_KERNEL);
+       if (!input_fmts)
+               return NULL;
+
+       switch (output_fmt) {
+       case MEDIA_BUS_FMT_FIXED:
+               di = &conn_state->connector->display_info;
+
+               /*
+                * Look at the first bus format to determine input format.
+                * Default to MEDIA_BUS_FMT_RGB888_1X24, if no match.
+                */
+               if (di->num_bus_formats) {
+                       finfo = drm_format_info(di->bus_formats[0]);
+
+                       input_fmts[0] = finfo->depth == 18 ?
+                                       MEDIA_BUS_FMT_RGB666_1X24_CPADHI :
+                                       MEDIA_BUS_FMT_RGB888_1X24;
+               } else {
+                       input_fmts[0] = MEDIA_BUS_FMT_RGB888_1X24;
+               }
+               break;
+       case MEDIA_BUS_FMT_RGB666_1X7X3_SPWG:
+               input_fmts[0] = MEDIA_BUS_FMT_RGB666_1X24_CPADHI;
+               break;
+       case MEDIA_BUS_FMT_RGB888_1X7X4_SPWG:
+       case MEDIA_BUS_FMT_RGB888_1X7X4_JEIDA:
+               input_fmts[0] = MEDIA_BUS_FMT_RGB888_1X24;
+               break;
+       default:
+               kfree(input_fmts);
+               input_fmts = NULL;
+               break;
+       }
+
+       return input_fmts;
+}
+
+static u32 *
+imx8qxp_ldb_bridge_atomic_get_output_bus_fmts(struct drm_bridge *bridge,
+                                             struct drm_bridge_state *bridge_state,
+                                             struct drm_crtc_state *crtc_state,
+                                             struct drm_connector_state *conn_state,
+                                             unsigned int *num_output_fmts)
+{
+       *num_output_fmts = ARRAY_SIZE(imx8qxp_ldb_bus_output_fmts);
+       return kmemdup(imx8qxp_ldb_bus_output_fmts,
+                       sizeof(imx8qxp_ldb_bus_output_fmts), GFP_KERNEL);
+}
+
+static enum drm_mode_status
+imx8qxp_ldb_bridge_mode_valid(struct drm_bridge *bridge,
+                             const struct drm_display_info *info,
+                             const struct drm_display_mode *mode)
+{
+       struct ldb_channel *ldb_ch = bridge->driver_private;
+       bool is_single = ldb_channel_is_single_link(ldb_ch);
+
+       if (mode->clock > 170000)
+               return MODE_CLOCK_HIGH;
+
+       if (mode->clock > 150000 && is_single)
+               return MODE_CLOCK_HIGH;
+
+       return MODE_OK;
+}
+
+static const struct drm_bridge_funcs imx8qxp_ldb_bridge_funcs = {
+       .atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state,
+       .atomic_destroy_state   = drm_atomic_helper_bridge_destroy_state,
+       .atomic_reset           = drm_atomic_helper_bridge_reset,
+       .mode_valid             = imx8qxp_ldb_bridge_mode_valid,
+       .attach                 = ldb_bridge_attach_helper,
+       .atomic_check           = imx8qxp_ldb_bridge_atomic_check,
+       .mode_set               = imx8qxp_ldb_bridge_mode_set,
+       .atomic_pre_enable      = imx8qxp_ldb_bridge_atomic_pre_enable,
+       .atomic_enable          = imx8qxp_ldb_bridge_atomic_enable,
+       .atomic_disable         = imx8qxp_ldb_bridge_atomic_disable,
+       .atomic_get_input_bus_fmts =
+                       imx8qxp_ldb_bridge_atomic_get_input_bus_fmts,
+       .atomic_get_output_bus_fmts =
+                       imx8qxp_ldb_bridge_atomic_get_output_bus_fmts,
+};
+
+static int imx8qxp_ldb_set_di_id(struct imx8qxp_ldb *imx8qxp_ldb)
+{
+       struct imx8qxp_ldb_channel *imx8qxp_ldb_ch =
+                        &imx8qxp_ldb->channel[imx8qxp_ldb->active_chno];
+       struct ldb_channel *ldb_ch = &imx8qxp_ldb_ch->base;
+       struct device_node *ep, *remote;
+       struct device *dev = imx8qxp_ldb->dev;
+       struct of_endpoint endpoint;
+       int ret;
+
+       ep = of_graph_get_endpoint_by_regs(ldb_ch->np, 0, -1);
+       if (!ep) {
+               DRM_DEV_ERROR(dev, "failed to get port0 endpoint\n");
+               return -EINVAL;
+       }
+
+       remote = of_graph_get_remote_endpoint(ep);
+       of_node_put(ep);
+       if (!remote) {
+               DRM_DEV_ERROR(dev, "failed to get port0 remote endpoint\n");
+               return -EINVAL;
+       }
+
+       ret = of_graph_parse_endpoint(remote, &endpoint);
+       of_node_put(remote);
+       if (ret) {
+               DRM_DEV_ERROR(dev, "failed to parse port0 remote endpoint: %d\n",
+                             ret);
+               return ret;
+       }
+
+       imx8qxp_ldb_ch->di_id = endpoint.id;
+
+       return 0;
+}
+
+static int
+imx8qxp_ldb_check_chno_and_dual_link(struct ldb_channel *ldb_ch, int link)
+{
+       if ((link == DRM_LVDS_DUAL_LINK_ODD_EVEN_PIXELS && ldb_ch->chno != 0) ||
+           (link == DRM_LVDS_DUAL_LINK_EVEN_ODD_PIXELS && ldb_ch->chno != 1))
+               return -EINVAL;
+
+       return 0;
+}
+
+static int imx8qxp_ldb_parse_dt_companion(struct imx8qxp_ldb *imx8qxp_ldb)
+{
+       struct imx8qxp_ldb_channel *imx8qxp_ldb_ch =
+                        &imx8qxp_ldb->channel[imx8qxp_ldb->active_chno];
+       struct ldb_channel *ldb_ch = &imx8qxp_ldb_ch->base;
+       struct ldb_channel *companion_ldb_ch;
+       struct device_node *companion;
+       struct device_node *child;
+       struct device_node *companion_port = NULL;
+       struct device_node *port1, *port2;
+       struct device *dev = imx8qxp_ldb->dev;
+       const struct of_device_id *match;
+       u32 i;
+       int dual_link;
+       int ret;
+
+       /* Locate the companion LDB for dual-link operation, if any. */
+       companion = of_parse_phandle(dev->of_node, "fsl,companion-ldb", 0);
+       if (!companion)
+               return 0;
+
+       if (!of_device_is_available(companion)) {
+               DRM_DEV_ERROR(dev, "companion LDB is not available\n");
+               ret = -ENODEV;
+               goto out;
+       }
+
+       /*
+        * Sanity check: the companion bridge must have the same compatible
+        * string.
+        */
+       match = of_match_device(dev->driver->of_match_table, dev);
+       if (!of_device_is_compatible(companion, match->compatible)) {
+               DRM_DEV_ERROR(dev, "companion LDB is incompatible\n");
+               ret = -ENXIO;
+               goto out;
+       }
+
+       for_each_available_child_of_node(companion, child) {
+               ret = of_property_read_u32(child, "reg", &i);
+               if (ret || i > MAX_LDB_CHAN_NUM - 1) {
+                       DRM_DEV_ERROR(dev,
+                                     "invalid channel node address: %u\n", i);
+                       ret = -EINVAL;
+                       of_node_put(child);
+                       goto out;
+               }
+
+               /*
+                * Channel numbers have to be different, because channel0
+                * transmits odd pixels and channel1 transmits even pixels.
+                */
+               if (i == (ldb_ch->chno ^ 0x1)) {
+                       companion_port = child;
+                       break;
+               }
+       }
+
+       if (!companion_port) {
+               DRM_DEV_ERROR(dev,
+                             "failed to find companion LDB channel port\n");
+               ret = -EINVAL;
+               goto out;
+       }
+
+       /*
+        * We need to work out if the sink is expecting us to function in
+        * dual-link mode.  We do this by looking at the DT port nodes we are
+        * connected to.  If they are marked as expecting odd pixels and
+        * even pixels than we need to enable LDB split mode.
+        */
+       port1 = of_graph_get_port_by_id(ldb_ch->np, 1);
+       port2 = of_graph_get_port_by_id(companion_port, 1);
+       dual_link = drm_of_lvds_get_dual_link_pixel_order(port1, port2);
+       of_node_put(port1);
+       of_node_put(port2);
+
+       switch (dual_link) {
+       case DRM_LVDS_DUAL_LINK_ODD_EVEN_PIXELS:
+               ldb_ch->link_type = LDB_CH_DUAL_LINK_ODD_EVEN_PIXELS;
+               break;
+       case DRM_LVDS_DUAL_LINK_EVEN_ODD_PIXELS:
+               ldb_ch->link_type = LDB_CH_DUAL_LINK_EVEN_ODD_PIXELS;
+               break;
+       default:
+               ret = dual_link;
+               DRM_DEV_ERROR(dev,
+                             "failed to get dual link pixel order: %d\n", ret);
+               goto out;
+       }
+
+       ret = imx8qxp_ldb_check_chno_and_dual_link(ldb_ch, dual_link);
+       if (ret < 0) {
+               DRM_DEV_ERROR(dev,
+                             "unmatched channel number(%u) vs dual link(%d)\n",
+                             ldb_ch->chno, dual_link);
+               goto out;
+       }
+
+       imx8qxp_ldb->companion = of_drm_find_bridge(companion_port);
+       if (!imx8qxp_ldb->companion) {
+               ret = -EPROBE_DEFER;
+               DRM_DEV_DEBUG_DRIVER(dev,
+                                    "failed to find bridge for companion bridge: %d\n",
+                                    ret);
+               goto out;
+       }
+
+       DRM_DEV_DEBUG_DRIVER(dev,
+                            "dual-link configuration detected (companion bridge %pOF)\n",
+                            companion);
+
+       companion_ldb_ch = bridge_to_ldb_ch(imx8qxp_ldb->companion);
+       companion_ldb_ch->link_type = ldb_ch->link_type;
+out:
+       of_node_put(companion_port);
+       of_node_put(companion);
+       return ret;
+}
+
+static int imx8qxp_ldb_probe(struct platform_device *pdev)
+{
+       struct device *dev = &pdev->dev;
+       struct imx8qxp_ldb *imx8qxp_ldb;
+       struct imx8qxp_ldb_channel *imx8qxp_ldb_ch;
+       struct ldb *ldb;
+       struct ldb_channel *ldb_ch;
+       int ret, i;
+
+       imx8qxp_ldb = devm_kzalloc(dev, sizeof(*imx8qxp_ldb), GFP_KERNEL);
+       if (!imx8qxp_ldb)
+               return -ENOMEM;
+
+       imx8qxp_ldb->clk_pixel = devm_clk_get(dev, "pixel");
+       if (IS_ERR(imx8qxp_ldb->clk_pixel)) {
+               ret = PTR_ERR(imx8qxp_ldb->clk_pixel);
+               if (ret != -EPROBE_DEFER)
+                       DRM_DEV_ERROR(dev,
+                                     "failed to get pixel clock: %d\n", ret);
+               return ret;
+       }
+
+       imx8qxp_ldb->clk_bypass = devm_clk_get(dev, "bypass");
+       if (IS_ERR(imx8qxp_ldb->clk_bypass)) {
+               ret = PTR_ERR(imx8qxp_ldb->clk_bypass);
+               if (ret != -EPROBE_DEFER)
+                       DRM_DEV_ERROR(dev,
+                                     "failed to get bypass clock: %d\n", ret);
+               return ret;
+       }
+
+       imx8qxp_ldb->dev = dev;
+
+       ldb = &imx8qxp_ldb->base;
+       ldb->dev = dev;
+       ldb->ctrl_reg = 0xe0;
+
+       for (i = 0; i < MAX_LDB_CHAN_NUM; i++)
+               ldb->channel[i] = &imx8qxp_ldb->channel[i].base;
+
+       ret = ldb_init_helper(ldb);
+       if (ret)
+               return ret;
+
+       if (ldb->available_ch_cnt == 0) {
+               DRM_DEV_DEBUG_DRIVER(dev, "no available channel\n");
+               return 0;
+       } else if (ldb->available_ch_cnt > 1) {
+               DRM_DEV_ERROR(dev, "invalid available channel number(%u)\n",
+                             ldb->available_ch_cnt);
+               return -EINVAL;
+       }
+
+       for (i = 0; i < MAX_LDB_CHAN_NUM; i++) {
+               imx8qxp_ldb_ch = &imx8qxp_ldb->channel[i];
+               ldb_ch = &imx8qxp_ldb_ch->base;
+
+               if (ldb_ch->is_available) {
+                       imx8qxp_ldb->active_chno = ldb_ch->chno;
+                       break;
+               }
+       }
+
+       imx8qxp_ldb_ch->phy = devm_of_phy_get(dev, ldb_ch->np, "lvds_phy");
+       if (IS_ERR(imx8qxp_ldb_ch->phy)) {
+               ret = PTR_ERR(imx8qxp_ldb_ch->phy);
+               if (ret != -EPROBE_DEFER)
+                       DRM_DEV_ERROR(dev, "failed to get channel%d PHY: %d\n",
+                                     imx8qxp_ldb->active_chno, ret);
+               return ret;
+       }
+
+       ret = ldb_find_next_bridge_helper(ldb);
+       if (ret)
+               return ret;
+
+       ret = imx8qxp_ldb_set_di_id(imx8qxp_ldb);
+       if (ret)
+               return ret;
+
+       ret = imx8qxp_ldb_parse_dt_companion(imx8qxp_ldb);
+       if (ret)
+               return ret;
+
+       platform_set_drvdata(pdev, imx8qxp_ldb);
+       pm_runtime_enable(dev);
+
+       ldb_add_bridge_helper(ldb, &imx8qxp_ldb_bridge_funcs);
+
+       return ret;
+}
+
+static int imx8qxp_ldb_remove(struct platform_device *pdev)
+{
+       struct imx8qxp_ldb *imx8qxp_ldb = platform_get_drvdata(pdev);
+       struct ldb *ldb = &imx8qxp_ldb->base;
+
+       ldb_remove_bridge_helper(ldb);
+
+       pm_runtime_disable(&pdev->dev);
+
+       return 0;
+}
+
+static int __maybe_unused imx8qxp_ldb_runtime_suspend(struct device *dev)
+{
+       return 0;
+}
+
+static int __maybe_unused imx8qxp_ldb_runtime_resume(struct device *dev)
+{
+       struct imx8qxp_ldb *imx8qxp_ldb = dev_get_drvdata(dev);
+       struct ldb *ldb = &imx8qxp_ldb->base;
+
+       /* disable LDB by resetting the control register to POR default */
+       regmap_write(ldb->regmap, ldb->ctrl_reg, 0);
+
+       return 0;
+}
+
+static const struct dev_pm_ops imx8qxp_ldb_pm_ops = {
+       SET_RUNTIME_PM_OPS(imx8qxp_ldb_runtime_suspend,
+                          imx8qxp_ldb_runtime_resume, NULL)
+};
+
+static const struct of_device_id imx8qxp_ldb_dt_ids[] = {
+       { .compatible = "fsl,imx8qxp-ldb" },
+       { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, imx8qxp_ldb_dt_ids);
+
+static struct platform_driver imx8qxp_ldb_driver = {
+       .probe  = imx8qxp_ldb_probe,
+       .remove = imx8qxp_ldb_remove,
+       .driver = {
+               .pm = &imx8qxp_ldb_pm_ops,
+               .name = DRIVER_NAME,
+               .of_match_table = imx8qxp_ldb_dt_ids,
+       },
+};
+module_platform_driver(imx8qxp_ldb_driver);
+
+MODULE_DESCRIPTION("i.MX8QXP LVDS Display Bridge(LDB)/Pixel Mapper bridge driver");
+MODULE_AUTHOR("Liu Ying <victor.liu@nxp.com>");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:" DRIVER_NAME);
diff --git a/drivers/gpu/drm/bridge/imx/imx8qxp-pixel-combiner.c b/drivers/gpu/drm/bridge/imx/imx8qxp-pixel-combiner.c
new file mode 100644 (file)
index 0000000..86ae98a
--- /dev/null
@@ -0,0 +1,448 @@
+// SPDX-License-Identifier: GPL-2.0+
+
+/*
+ * Copyright 2020 NXP
+ */
+
+#include <linux/bitfield.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_graph.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+
+#include <drm/drm_atomic_state_helper.h>
+#include <drm/drm_bridge.h>
+#include <drm/drm_print.h>
+
+#define PC_CTRL_REG                    0x0
+#define  PC_COMBINE_ENABLE             BIT(0)
+#define  PC_DISP_BYPASS(n)             BIT(1 + 21 * (n))
+#define  PC_DISP_HSYNC_POLARITY(n)     BIT(2 + 11 * (n))
+#define  PC_DISP_HSYNC_POLARITY_POS(n) DISP_HSYNC_POLARITY(n)
+#define  PC_DISP_VSYNC_POLARITY(n)     BIT(3 + 11 * (n))
+#define  PC_DISP_VSYNC_POLARITY_POS(n) DISP_VSYNC_POLARITY(n)
+#define  PC_DISP_DVALID_POLARITY(n)    BIT(4 + 11 * (n))
+#define  PC_DISP_DVALID_POLARITY_POS(n)        DISP_DVALID_POLARITY(n)
+#define  PC_VSYNC_MASK_ENABLE          BIT(5)
+#define  PC_SKIP_MODE                  BIT(6)
+#define  PC_SKIP_NUMBER_MASK           GENMASK(12, 7)
+#define  PC_SKIP_NUMBER(n)             FIELD_PREP(PC_SKIP_NUMBER_MASK, (n))
+#define  PC_DISP0_PIX_DATA_FORMAT_MASK GENMASK(18, 16)
+#define  PC_DISP0_PIX_DATA_FORMAT(fmt) \
+                               FIELD_PREP(PC_DISP0_PIX_DATA_FORMAT_MASK, (fmt))
+#define  PC_DISP1_PIX_DATA_FORMAT_MASK GENMASK(21, 19)
+#define  PC_DISP1_PIX_DATA_FORMAT(fmt) \
+                               FIELD_PREP(PC_DISP1_PIX_DATA_FORMAT_MASK, (fmt))
+
+#define PC_SW_RESET_REG                        0x20
+#define  PC_SW_RESET_N                 BIT(0)
+#define  PC_DISP_SW_RESET_N(n)         BIT(1 + (n))
+#define  PC_FULL_RESET_N               (PC_SW_RESET_N |                \
+                                        PC_DISP_SW_RESET_N(0) |        \
+                                        PC_DISP_SW_RESET_N(1))
+
+#define PC_REG_SET                     0x4
+#define PC_REG_CLR                     0x8
+
+#define DRIVER_NAME                    "imx8qxp-pixel-combiner"
+
+enum imx8qxp_pc_pix_data_format {
+       RGB,
+       YUV444,
+       YUV422,
+       SPLIT_RGB,
+};
+
+struct imx8qxp_pc_channel {
+       struct drm_bridge bridge;
+       struct drm_bridge *next_bridge;
+       struct imx8qxp_pc *pc;
+       unsigned int stream_id;
+       bool is_available;
+};
+
+struct imx8qxp_pc {
+       struct device *dev;
+       struct imx8qxp_pc_channel ch[2];
+       struct clk *clk_apb;
+       void __iomem *base;
+};
+
+static inline u32 imx8qxp_pc_read(struct imx8qxp_pc *pc, unsigned int offset)
+{
+       return readl(pc->base + offset);
+}
+
+static inline void
+imx8qxp_pc_write(struct imx8qxp_pc *pc, unsigned int offset, u32 value)
+{
+       writel(value, pc->base + offset);
+}
+
+static inline void
+imx8qxp_pc_write_set(struct imx8qxp_pc *pc, unsigned int offset, u32 value)
+{
+       imx8qxp_pc_write(pc, offset + PC_REG_SET, value);
+}
+
+static inline void
+imx8qxp_pc_write_clr(struct imx8qxp_pc *pc, unsigned int offset, u32 value)
+{
+       imx8qxp_pc_write(pc, offset + PC_REG_CLR, value);
+}
+
+static enum drm_mode_status
+imx8qxp_pc_bridge_mode_valid(struct drm_bridge *bridge,
+                            const struct drm_display_info *info,
+                            const struct drm_display_mode *mode)
+{
+       if (mode->hdisplay > 2560)
+               return MODE_BAD_HVALUE;
+
+       return MODE_OK;
+}
+
+static int imx8qxp_pc_bridge_attach(struct drm_bridge *bridge,
+                                   enum drm_bridge_attach_flags flags)
+{
+       struct imx8qxp_pc_channel *ch = bridge->driver_private;
+       struct imx8qxp_pc *pc = ch->pc;
+
+       if (!(flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR)) {
+               DRM_DEV_ERROR(pc->dev,
+                             "do not support creating a drm_connector\n");
+               return -EINVAL;
+       }
+
+       if (!bridge->encoder) {
+               DRM_DEV_ERROR(pc->dev, "missing encoder\n");
+               return -ENODEV;
+       }
+
+       return drm_bridge_attach(bridge->encoder,
+                                ch->next_bridge, bridge,
+                                DRM_BRIDGE_ATTACH_NO_CONNECTOR);
+}
+
+static void
+imx8qxp_pc_bridge_mode_set(struct drm_bridge *bridge,
+                          const struct drm_display_mode *mode,
+                          const struct drm_display_mode *adjusted_mode)
+{
+       struct imx8qxp_pc_channel *ch = bridge->driver_private;
+       struct imx8qxp_pc *pc = ch->pc;
+       u32 val;
+       int ret;
+
+       ret = pm_runtime_get_sync(pc->dev);
+       if (ret < 0)
+               DRM_DEV_ERROR(pc->dev,
+                             "failed to get runtime PM sync: %d\n", ret);
+
+       ret = clk_prepare_enable(pc->clk_apb);
+       if (ret)
+               DRM_DEV_ERROR(pc->dev, "%s: failed to enable apb clock: %d\n",
+                             __func__,  ret);
+
+       /* HSYNC to pixel link is active low. */
+       imx8qxp_pc_write_clr(pc, PC_CTRL_REG,
+                            PC_DISP_HSYNC_POLARITY(ch->stream_id));
+
+       /* VSYNC to pixel link is active low. */
+       imx8qxp_pc_write_clr(pc, PC_CTRL_REG,
+                            PC_DISP_VSYNC_POLARITY(ch->stream_id));
+
+       /* Data enable to pixel link is active high. */
+       imx8qxp_pc_write_set(pc, PC_CTRL_REG,
+                            PC_DISP_DVALID_POLARITY(ch->stream_id));
+
+       /* Mask the first frame output which may be incomplete. */
+       imx8qxp_pc_write_set(pc, PC_CTRL_REG, PC_VSYNC_MASK_ENABLE);
+
+       /* Only support RGB currently. */
+       val = imx8qxp_pc_read(pc, PC_CTRL_REG);
+       if (ch->stream_id == 0) {
+               val &= ~PC_DISP0_PIX_DATA_FORMAT_MASK;
+               val |= PC_DISP0_PIX_DATA_FORMAT(RGB);
+       } else {
+               val &= ~PC_DISP1_PIX_DATA_FORMAT_MASK;
+               val |= PC_DISP1_PIX_DATA_FORMAT(RGB);
+       }
+       imx8qxp_pc_write(pc, PC_CTRL_REG, val);
+
+       /* Only support bypass mode currently. */
+       imx8qxp_pc_write_set(pc, PC_CTRL_REG, PC_DISP_BYPASS(ch->stream_id));
+
+       clk_disable_unprepare(pc->clk_apb);
+}
+
+static void
+imx8qxp_pc_bridge_atomic_disable(struct drm_bridge *bridge,
+                                struct drm_bridge_state *old_bridge_state)
+{
+       struct imx8qxp_pc_channel *ch = bridge->driver_private;
+       struct imx8qxp_pc *pc = ch->pc;
+       int ret;
+
+       ret = pm_runtime_put(pc->dev);
+       if (ret < 0)
+               DRM_DEV_ERROR(pc->dev, "failed to put runtime PM: %d\n", ret);
+}
+
+static const u32 imx8qxp_pc_bus_output_fmts[] = {
+       MEDIA_BUS_FMT_RGB888_1X36_CPADLO,
+       MEDIA_BUS_FMT_RGB666_1X36_CPADLO,
+};
+
+static bool imx8qxp_pc_bus_output_fmt_supported(u32 fmt)
+{
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(imx8qxp_pc_bus_output_fmts); i++) {
+               if (imx8qxp_pc_bus_output_fmts[i] == fmt)
+                       return true;
+       }
+
+       return false;
+}
+
+static u32 *
+imx8qxp_pc_bridge_atomic_get_input_bus_fmts(struct drm_bridge *bridge,
+                                           struct drm_bridge_state *bridge_state,
+                                           struct drm_crtc_state *crtc_state,
+                                           struct drm_connector_state *conn_state,
+                                           u32 output_fmt,
+                                           unsigned int *num_input_fmts)
+{
+       u32 *input_fmts;
+
+       if (!imx8qxp_pc_bus_output_fmt_supported(output_fmt))
+               return NULL;
+
+       *num_input_fmts = 1;
+
+       input_fmts = kmalloc(sizeof(*input_fmts), GFP_KERNEL);
+       if (!input_fmts)
+               return NULL;
+
+       switch (output_fmt) {
+       case MEDIA_BUS_FMT_RGB888_1X36_CPADLO:
+               input_fmts[0] = MEDIA_BUS_FMT_RGB888_1X30_CPADLO;
+               break;
+       case MEDIA_BUS_FMT_RGB666_1X36_CPADLO:
+               input_fmts[0] = MEDIA_BUS_FMT_RGB666_1X30_CPADLO;
+               break;
+       default:
+               kfree(input_fmts);
+               input_fmts = NULL;
+               break;
+       }
+
+       return input_fmts;
+}
+
+static u32 *
+imx8qxp_pc_bridge_atomic_get_output_bus_fmts(struct drm_bridge *bridge,
+                                            struct drm_bridge_state *bridge_state,
+                                            struct drm_crtc_state *crtc_state,
+                                            struct drm_connector_state *conn_state,
+                                            unsigned int *num_output_fmts)
+{
+       *num_output_fmts = ARRAY_SIZE(imx8qxp_pc_bus_output_fmts);
+       return kmemdup(imx8qxp_pc_bus_output_fmts,
+                       sizeof(imx8qxp_pc_bus_output_fmts), GFP_KERNEL);
+}
+
+static const struct drm_bridge_funcs imx8qxp_pc_bridge_funcs = {
+       .atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state,
+       .atomic_destroy_state   = drm_atomic_helper_bridge_destroy_state,
+       .atomic_reset           = drm_atomic_helper_bridge_reset,
+       .mode_valid             = imx8qxp_pc_bridge_mode_valid,
+       .attach                 = imx8qxp_pc_bridge_attach,
+       .mode_set               = imx8qxp_pc_bridge_mode_set,
+       .atomic_disable         = imx8qxp_pc_bridge_atomic_disable,
+       .atomic_get_input_bus_fmts =
+                               imx8qxp_pc_bridge_atomic_get_input_bus_fmts,
+       .atomic_get_output_bus_fmts =
+                               imx8qxp_pc_bridge_atomic_get_output_bus_fmts,
+};
+
+static int imx8qxp_pc_bridge_probe(struct platform_device *pdev)
+{
+       struct imx8qxp_pc *pc;
+       struct imx8qxp_pc_channel *ch;
+       struct device *dev = &pdev->dev;
+       struct device_node *np = dev->of_node;
+       struct device_node *child, *remote;
+       u32 i;
+       int ret;
+
+       pc = devm_kzalloc(dev, sizeof(*pc), GFP_KERNEL);
+       if (!pc)
+               return -ENOMEM;
+
+       pc->base = devm_platform_ioremap_resource(pdev, 0);
+       if (IS_ERR(pc->base))
+               return PTR_ERR(pc->base);
+
+       pc->dev = dev;
+
+       pc->clk_apb = devm_clk_get(dev, "apb");
+       if (IS_ERR(pc->clk_apb)) {
+               ret = PTR_ERR(pc->clk_apb);
+               if (ret != -EPROBE_DEFER)
+                       DRM_DEV_ERROR(dev, "failed to get apb clock: %d\n", ret);
+               return ret;
+       }
+
+       platform_set_drvdata(pdev, pc);
+       pm_runtime_enable(dev);
+
+       for_each_available_child_of_node(np, child) {
+               ret = of_property_read_u32(child, "reg", &i);
+               if (ret || i > 1) {
+                       ret = -EINVAL;
+                       DRM_DEV_ERROR(dev,
+                                     "invalid channel(%u) node address\n", i);
+                       goto free_child;
+               }
+
+               ch = &pc->ch[i];
+               ch->pc = pc;
+               ch->stream_id = i;
+
+               remote = of_graph_get_remote_node(child, 1, 0);
+               if (!remote) {
+                       ret = -ENODEV;
+                       DRM_DEV_ERROR(dev,
+                                     "channel%u failed to get port1's remote node: %d\n",
+                                     i, ret);
+                       goto free_child;
+               }
+
+               ch->next_bridge = of_drm_find_bridge(remote);
+               if (!ch->next_bridge) {
+                       of_node_put(remote);
+                       ret = -EPROBE_DEFER;
+                       DRM_DEV_DEBUG_DRIVER(dev,
+                                            "channel%u failed to find next bridge: %d\n",
+                                            i, ret);
+                       goto free_child;
+               }
+
+               of_node_put(remote);
+
+               ch->bridge.driver_private = ch;
+               ch->bridge.funcs = &imx8qxp_pc_bridge_funcs;
+               ch->bridge.of_node = child;
+               ch->is_available = true;
+
+               drm_bridge_add(&ch->bridge);
+       }
+
+       return 0;
+
+free_child:
+       of_node_put(child);
+
+       if (i == 1 && pc->ch[0].next_bridge)
+               drm_bridge_remove(&pc->ch[0].bridge);
+
+       pm_runtime_disable(dev);
+       return ret;
+}
+
+static int imx8qxp_pc_bridge_remove(struct platform_device *pdev)
+{
+       struct imx8qxp_pc *pc = platform_get_drvdata(pdev);
+       struct imx8qxp_pc_channel *ch;
+       int i;
+
+       for (i = 0; i < 2; i++) {
+               ch = &pc->ch[i];
+
+               if (!ch->is_available)
+                       continue;
+
+               drm_bridge_remove(&ch->bridge);
+               ch->is_available = false;
+       }
+
+       pm_runtime_disable(&pdev->dev);
+
+       return 0;
+}
+
+static int __maybe_unused imx8qxp_pc_runtime_suspend(struct device *dev)
+{
+       struct platform_device *pdev = to_platform_device(dev);
+       struct imx8qxp_pc *pc = platform_get_drvdata(pdev);
+       int ret;
+
+       ret = clk_prepare_enable(pc->clk_apb);
+       if (ret)
+               DRM_DEV_ERROR(pc->dev, "%s: failed to enable apb clock: %d\n",
+                             __func__,  ret);
+
+       /* Disable pixel combiner by full reset. */
+       imx8qxp_pc_write_clr(pc, PC_SW_RESET_REG, PC_FULL_RESET_N);
+
+       clk_disable_unprepare(pc->clk_apb);
+
+       /* Ensure the reset takes effect. */
+       usleep_range(10, 20);
+
+       return ret;
+}
+
+static int __maybe_unused imx8qxp_pc_runtime_resume(struct device *dev)
+{
+       struct platform_device *pdev = to_platform_device(dev);
+       struct imx8qxp_pc *pc = platform_get_drvdata(pdev);
+       int ret;
+
+       ret = clk_prepare_enable(pc->clk_apb);
+       if (ret) {
+               DRM_DEV_ERROR(pc->dev, "%s: failed to enable apb clock: %d\n",
+                             __func__, ret);
+               return ret;
+       }
+
+       /* out of reset */
+       imx8qxp_pc_write_set(pc, PC_SW_RESET_REG, PC_FULL_RESET_N);
+
+       clk_disable_unprepare(pc->clk_apb);
+
+       return ret;
+}
+
+static const struct dev_pm_ops imx8qxp_pc_pm_ops = {
+       SET_RUNTIME_PM_OPS(imx8qxp_pc_runtime_suspend,
+                          imx8qxp_pc_runtime_resume, NULL)
+};
+
+static const struct of_device_id imx8qxp_pc_dt_ids[] = {
+       { .compatible = "fsl,imx8qm-pixel-combiner", },
+       { .compatible = "fsl,imx8qxp-pixel-combiner", },
+       { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, imx8qxp_pc_dt_ids);
+
+static struct platform_driver imx8qxp_pc_bridge_driver = {
+       .probe  = imx8qxp_pc_bridge_probe,
+       .remove = imx8qxp_pc_bridge_remove,
+       .driver = {
+               .pm = &imx8qxp_pc_pm_ops,
+               .name = DRIVER_NAME,
+               .of_match_table = imx8qxp_pc_dt_ids,
+       },
+};
+module_platform_driver(imx8qxp_pc_bridge_driver);
+
+MODULE_DESCRIPTION("i.MX8QM/QXP pixel combiner bridge driver");
+MODULE_AUTHOR("Liu Ying <victor.liu@nxp.com>");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:" DRIVER_NAME);
diff --git a/drivers/gpu/drm/bridge/imx/imx8qxp-pixel-link.c b/drivers/gpu/drm/bridge/imx/imx8qxp-pixel-link.c
new file mode 100644 (file)
index 0000000..305c833
--- /dev/null
@@ -0,0 +1,429 @@
+// SPDX-License-Identifier: GPL-2.0+
+
+/*
+ * Copyright 2020,2022 NXP
+ */
+
+#include <linux/firmware/imx/svc/misc.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_graph.h>
+#include <linux/platform_device.h>
+
+#include <drm/drm_atomic_state_helper.h>
+#include <drm/drm_bridge.h>
+#include <drm/drm_print.h>
+
+#include <dt-bindings/firmware/imx/rsrc.h>
+
+#define DRIVER_NAME            "imx8qxp-display-pixel-link"
+#define PL_MAX_MST_ADDR                3
+#define PL_MAX_NEXT_BRIDGES    2
+
+struct imx8qxp_pixel_link {
+       struct drm_bridge bridge;
+       struct drm_bridge *next_bridge;
+       struct device *dev;
+       struct imx_sc_ipc *ipc_handle;
+       u8 stream_id;
+       u8 dc_id;
+       u32 sink_rsc;
+       u32 mst_addr;
+       u8 mst_addr_ctrl;
+       u8 mst_en_ctrl;
+       u8 mst_vld_ctrl;
+       u8 sync_ctrl;
+};
+
+static void imx8qxp_pixel_link_enable_mst_en(struct imx8qxp_pixel_link *pl)
+{
+       int ret;
+
+       ret = imx_sc_misc_set_control(pl->ipc_handle, pl->sink_rsc,
+                                     pl->mst_en_ctrl, true);
+       if (ret)
+               DRM_DEV_ERROR(pl->dev,
+                             "failed to enable DC%u stream%u pixel link mst_en: %d\n",
+                             pl->dc_id, pl->stream_id, ret);
+}
+
+static void imx8qxp_pixel_link_enable_mst_vld(struct imx8qxp_pixel_link *pl)
+{
+       int ret;
+
+       ret = imx_sc_misc_set_control(pl->ipc_handle, pl->sink_rsc,
+                                     pl->mst_vld_ctrl, true);
+       if (ret)
+               DRM_DEV_ERROR(pl->dev,
+                             "failed to enable DC%u stream%u pixel link mst_vld: %d\n",
+                             pl->dc_id, pl->stream_id, ret);
+}
+
+static void imx8qxp_pixel_link_enable_sync(struct imx8qxp_pixel_link *pl)
+{
+       int ret;
+
+       ret = imx_sc_misc_set_control(pl->ipc_handle, pl->sink_rsc,
+                                     pl->sync_ctrl, true);
+       if (ret)
+               DRM_DEV_ERROR(pl->dev,
+                             "failed to enable DC%u stream%u pixel link sync: %d\n",
+                             pl->dc_id, pl->stream_id, ret);
+}
+
+static int imx8qxp_pixel_link_disable_mst_en(struct imx8qxp_pixel_link *pl)
+{
+       int ret;
+
+       ret = imx_sc_misc_set_control(pl->ipc_handle, pl->sink_rsc,
+                                     pl->mst_en_ctrl, false);
+       if (ret)
+               DRM_DEV_ERROR(pl->dev,
+                             "failed to disable DC%u stream%u pixel link mst_en: %d\n",
+                             pl->dc_id, pl->stream_id, ret);
+
+       return ret;
+}
+
+static int imx8qxp_pixel_link_disable_mst_vld(struct imx8qxp_pixel_link *pl)
+{
+       int ret;
+
+       ret = imx_sc_misc_set_control(pl->ipc_handle, pl->sink_rsc,
+                                     pl->mst_vld_ctrl, false);
+       if (ret)
+               DRM_DEV_ERROR(pl->dev,
+                             "failed to disable DC%u stream%u pixel link mst_vld: %d\n",
+                             pl->dc_id, pl->stream_id, ret);
+
+       return ret;
+}
+
+static int imx8qxp_pixel_link_disable_sync(struct imx8qxp_pixel_link *pl)
+{
+       int ret;
+
+       ret = imx_sc_misc_set_control(pl->ipc_handle, pl->sink_rsc,
+                                     pl->sync_ctrl, false);
+       if (ret)
+               DRM_DEV_ERROR(pl->dev,
+                             "failed to disable DC%u stream%u pixel link sync: %d\n",
+                             pl->dc_id, pl->stream_id, ret);
+
+       return ret;
+}
+
+static void imx8qxp_pixel_link_set_mst_addr(struct imx8qxp_pixel_link *pl)
+{
+       int ret;
+
+       ret = imx_sc_misc_set_control(pl->ipc_handle,
+                                     pl->sink_rsc, pl->mst_addr_ctrl,
+                                     pl->mst_addr);
+       if (ret)
+               DRM_DEV_ERROR(pl->dev,
+                             "failed to set DC%u stream%u pixel link mst addr(%u): %d\n",
+                             pl->dc_id, pl->stream_id, pl->mst_addr, ret);
+}
+
+static int imx8qxp_pixel_link_bridge_attach(struct drm_bridge *bridge,
+                                           enum drm_bridge_attach_flags flags)
+{
+       struct imx8qxp_pixel_link *pl = bridge->driver_private;
+
+       if (!(flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR)) {
+               DRM_DEV_ERROR(pl->dev,
+                             "do not support creating a drm_connector\n");
+               return -EINVAL;
+       }
+
+       if (!bridge->encoder) {
+               DRM_DEV_ERROR(pl->dev, "missing encoder\n");
+               return -ENODEV;
+       }
+
+       return drm_bridge_attach(bridge->encoder,
+                                pl->next_bridge, bridge,
+                                DRM_BRIDGE_ATTACH_NO_CONNECTOR);
+}
+
+static void
+imx8qxp_pixel_link_bridge_mode_set(struct drm_bridge *bridge,
+                                  const struct drm_display_mode *mode,
+                                  const struct drm_display_mode *adjusted_mode)
+{
+       struct imx8qxp_pixel_link *pl = bridge->driver_private;
+
+       imx8qxp_pixel_link_set_mst_addr(pl);
+}
+
+static void
+imx8qxp_pixel_link_bridge_atomic_enable(struct drm_bridge *bridge,
+                                       struct drm_bridge_state *old_bridge_state)
+{
+       struct imx8qxp_pixel_link *pl = bridge->driver_private;
+
+       imx8qxp_pixel_link_enable_mst_en(pl);
+       imx8qxp_pixel_link_enable_mst_vld(pl);
+       imx8qxp_pixel_link_enable_sync(pl);
+}
+
+static void
+imx8qxp_pixel_link_bridge_atomic_disable(struct drm_bridge *bridge,
+                                        struct drm_bridge_state *old_bridge_state)
+{
+       struct imx8qxp_pixel_link *pl = bridge->driver_private;
+
+       imx8qxp_pixel_link_disable_mst_en(pl);
+       imx8qxp_pixel_link_disable_mst_vld(pl);
+       imx8qxp_pixel_link_disable_sync(pl);
+}
+
+static const u32 imx8qxp_pixel_link_bus_output_fmts[] = {
+       MEDIA_BUS_FMT_RGB888_1X36_CPADLO,
+       MEDIA_BUS_FMT_RGB666_1X36_CPADLO,
+};
+
+static bool imx8qxp_pixel_link_bus_output_fmt_supported(u32 fmt)
+{
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(imx8qxp_pixel_link_bus_output_fmts); i++) {
+               if (imx8qxp_pixel_link_bus_output_fmts[i] == fmt)
+                       return true;
+       }
+
+       return false;
+}
+
+static u32 *
+imx8qxp_pixel_link_bridge_atomic_get_input_bus_fmts(struct drm_bridge *bridge,
+                                                   struct drm_bridge_state *bridge_state,
+                                                   struct drm_crtc_state *crtc_state,
+                                                   struct drm_connector_state *conn_state,
+                                                   u32 output_fmt,
+                                                   unsigned int *num_input_fmts)
+{
+       u32 *input_fmts;
+
+       if (!imx8qxp_pixel_link_bus_output_fmt_supported(output_fmt))
+               return NULL;
+
+       *num_input_fmts = 1;
+
+       input_fmts = kmalloc(sizeof(*input_fmts), GFP_KERNEL);
+       if (!input_fmts)
+               return NULL;
+
+       input_fmts[0] = output_fmt;
+
+       return input_fmts;
+}
+
+static u32 *
+imx8qxp_pixel_link_bridge_atomic_get_output_bus_fmts(struct drm_bridge *bridge,
+                                                    struct drm_bridge_state *bridge_state,
+                                                    struct drm_crtc_state *crtc_state,
+                                                    struct drm_connector_state *conn_state,
+                                                    unsigned int *num_output_fmts)
+{
+       *num_output_fmts = ARRAY_SIZE(imx8qxp_pixel_link_bus_output_fmts);
+       return kmemdup(imx8qxp_pixel_link_bus_output_fmts,
+                       sizeof(imx8qxp_pixel_link_bus_output_fmts), GFP_KERNEL);
+}
+
+static const struct drm_bridge_funcs imx8qxp_pixel_link_bridge_funcs = {
+       .atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state,
+       .atomic_destroy_state   = drm_atomic_helper_bridge_destroy_state,
+       .atomic_reset           = drm_atomic_helper_bridge_reset,
+       .attach                 = imx8qxp_pixel_link_bridge_attach,
+       .mode_set               = imx8qxp_pixel_link_bridge_mode_set,
+       .atomic_enable          = imx8qxp_pixel_link_bridge_atomic_enable,
+       .atomic_disable         = imx8qxp_pixel_link_bridge_atomic_disable,
+       .atomic_get_input_bus_fmts =
+                       imx8qxp_pixel_link_bridge_atomic_get_input_bus_fmts,
+       .atomic_get_output_bus_fmts =
+                       imx8qxp_pixel_link_bridge_atomic_get_output_bus_fmts,
+};
+
+static int imx8qxp_pixel_link_disable_all_controls(struct imx8qxp_pixel_link *pl)
+{
+       int ret;
+
+       ret = imx8qxp_pixel_link_disable_mst_en(pl);
+       if (ret)
+               return ret;
+
+       ret = imx8qxp_pixel_link_disable_mst_vld(pl);
+       if (ret)
+               return ret;
+
+       return imx8qxp_pixel_link_disable_sync(pl);
+}
+
+static struct drm_bridge *
+imx8qxp_pixel_link_find_next_bridge(struct imx8qxp_pixel_link *pl)
+{
+       struct device_node *np = pl->dev->of_node;
+       struct device_node *port, *remote;
+       struct drm_bridge *next_bridge[PL_MAX_NEXT_BRIDGES];
+       u32 port_id;
+       bool found_port = false;
+       int reg, ep_cnt = 0;
+       /* select the first next bridge by default */
+       int bridge_sel = 0;
+
+       for (port_id = 1; port_id <= PL_MAX_MST_ADDR + 1; port_id++) {
+               port = of_graph_get_port_by_id(np, port_id);
+               if (!port)
+                       continue;
+
+               if (of_device_is_available(port)) {
+                       found_port = true;
+                       of_node_put(port);
+                       break;
+               }
+
+               of_node_put(port);
+       }
+
+       if (!found_port) {
+               DRM_DEV_ERROR(pl->dev, "no available output port\n");
+               return ERR_PTR(-ENODEV);
+       }
+
+       for (reg = 0; reg < PL_MAX_NEXT_BRIDGES; reg++) {
+               remote = of_graph_get_remote_node(np, port_id, reg);
+               if (!remote)
+                       continue;
+
+               if (!of_device_is_available(remote->parent)) {
+                       DRM_DEV_DEBUG(pl->dev,
+                                     "port%u endpoint%u remote parent is not available\n",
+                                     port_id, reg);
+                       of_node_put(remote);
+                       continue;
+               }
+
+               next_bridge[ep_cnt] = of_drm_find_bridge(remote);
+               if (!next_bridge[ep_cnt]) {
+                       of_node_put(remote);
+                       return ERR_PTR(-EPROBE_DEFER);
+               }
+
+               /* specially select the next bridge with companion PXL2DPI */
+               if (of_find_property(remote, "fsl,companion-pxl2dpi", NULL))
+                       bridge_sel = ep_cnt;
+
+               ep_cnt++;
+
+               of_node_put(remote);
+       }
+
+       pl->mst_addr = port_id - 1;
+
+       return next_bridge[bridge_sel];
+}
+
+static int imx8qxp_pixel_link_bridge_probe(struct platform_device *pdev)
+{
+       struct imx8qxp_pixel_link *pl;
+       struct device *dev = &pdev->dev;
+       struct device_node *np = dev->of_node;
+       int ret;
+
+       pl = devm_kzalloc(dev, sizeof(*pl), GFP_KERNEL);
+       if (!pl)
+               return -ENOMEM;
+
+       ret = imx_scu_get_handle(&pl->ipc_handle);
+       if (ret) {
+               if (ret != -EPROBE_DEFER)
+                       DRM_DEV_ERROR(dev, "failed to get SCU ipc handle: %d\n",
+                                     ret);
+               return ret;
+       }
+
+       ret = of_property_read_u8(np, "fsl,dc-id", &pl->dc_id);
+       if (ret) {
+               DRM_DEV_ERROR(dev, "failed to get DC index: %d\n", ret);
+               return ret;
+       }
+
+       ret = of_property_read_u8(np, "fsl,dc-stream-id", &pl->stream_id);
+       if (ret) {
+               DRM_DEV_ERROR(dev, "failed to get DC stream index: %d\n", ret);
+               return ret;
+       }
+
+       pl->dev = dev;
+
+       pl->sink_rsc = pl->dc_id ? IMX_SC_R_DC_1 : IMX_SC_R_DC_0;
+
+       if (pl->stream_id == 0) {
+               pl->mst_addr_ctrl = IMX_SC_C_PXL_LINK_MST1_ADDR;
+               pl->mst_en_ctrl   = IMX_SC_C_PXL_LINK_MST1_ENB;
+               pl->mst_vld_ctrl  = IMX_SC_C_PXL_LINK_MST1_VLD;
+               pl->sync_ctrl     = IMX_SC_C_SYNC_CTRL0;
+       } else {
+               pl->mst_addr_ctrl = IMX_SC_C_PXL_LINK_MST2_ADDR;
+               pl->mst_en_ctrl   = IMX_SC_C_PXL_LINK_MST2_ENB;
+               pl->mst_vld_ctrl  = IMX_SC_C_PXL_LINK_MST2_VLD;
+               pl->sync_ctrl     = IMX_SC_C_SYNC_CTRL1;
+       }
+
+       /* disable all controls to POR default */
+       ret = imx8qxp_pixel_link_disable_all_controls(pl);
+       if (ret)
+               return ret;
+
+       pl->next_bridge = imx8qxp_pixel_link_find_next_bridge(pl);
+       if (IS_ERR(pl->next_bridge)) {
+               ret = PTR_ERR(pl->next_bridge);
+               if (ret != -EPROBE_DEFER)
+                       DRM_DEV_ERROR(dev, "failed to find next bridge: %d\n",
+                                     ret);
+               return ret;
+       }
+
+       platform_set_drvdata(pdev, pl);
+
+       pl->bridge.driver_private = pl;
+       pl->bridge.funcs = &imx8qxp_pixel_link_bridge_funcs;
+       pl->bridge.of_node = np;
+
+       drm_bridge_add(&pl->bridge);
+
+       return ret;
+}
+
+static int imx8qxp_pixel_link_bridge_remove(struct platform_device *pdev)
+{
+       struct imx8qxp_pixel_link *pl = platform_get_drvdata(pdev);
+
+       drm_bridge_remove(&pl->bridge);
+
+       return 0;
+}
+
+static const struct of_device_id imx8qxp_pixel_link_dt_ids[] = {
+       { .compatible = "fsl,imx8qm-dc-pixel-link", },
+       { .compatible = "fsl,imx8qxp-dc-pixel-link", },
+       { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, imx8qxp_pixel_link_dt_ids);
+
+static struct platform_driver imx8qxp_pixel_link_bridge_driver = {
+       .probe  = imx8qxp_pixel_link_bridge_probe,
+       .remove = imx8qxp_pixel_link_bridge_remove,
+       .driver = {
+               .of_match_table = imx8qxp_pixel_link_dt_ids,
+               .name = DRIVER_NAME,
+       },
+};
+module_platform_driver(imx8qxp_pixel_link_bridge_driver);
+
+MODULE_DESCRIPTION("i.MX8QXP/QM display pixel link bridge driver");
+MODULE_AUTHOR("Liu Ying <victor.liu@nxp.com>");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:" DRIVER_NAME);
diff --git a/drivers/gpu/drm/bridge/imx/imx8qxp-pxl2dpi.c b/drivers/gpu/drm/bridge/imx/imx8qxp-pxl2dpi.c
new file mode 100644 (file)
index 0000000..309f47a
--- /dev/null
@@ -0,0 +1,487 @@
+// SPDX-License-Identifier: GPL-2.0+
+
+/*
+ * Copyright 2020 NXP
+ */
+
+#include <linux/firmware/imx/svc/misc.h>
+#include <linux/mfd/syscon.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/of_graph.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+
+#include <drm/drm_atomic_state_helper.h>
+#include <drm/drm_bridge.h>
+#include <drm/drm_of.h>
+#include <drm/drm_print.h>
+
+#include <dt-bindings/firmware/imx/rsrc.h>
+
+#define PXL2DPI_CTRL   0x40
+#define  CFG1_16BIT    0x0
+#define  CFG2_16BIT    0x1
+#define  CFG3_16BIT    0x2
+#define  CFG1_18BIT    0x3
+#define  CFG2_18BIT    0x4
+#define  CFG_24BIT     0x5
+
+#define DRIVER_NAME    "imx8qxp-pxl2dpi"
+
+struct imx8qxp_pxl2dpi {
+       struct regmap *regmap;
+       struct drm_bridge bridge;
+       struct drm_bridge *next_bridge;
+       struct drm_bridge *companion;
+       struct device *dev;
+       struct imx_sc_ipc *ipc_handle;
+       u32 sc_resource;
+       u32 in_bus_format;
+       u32 out_bus_format;
+       u32 pl_sel;
+};
+
+#define bridge_to_p2d(b)       container_of(b, struct imx8qxp_pxl2dpi, bridge)
+
+static int imx8qxp_pxl2dpi_bridge_attach(struct drm_bridge *bridge,
+                                        enum drm_bridge_attach_flags flags)
+{
+       struct imx8qxp_pxl2dpi *p2d = bridge->driver_private;
+
+       if (!(flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR)) {
+               DRM_DEV_ERROR(p2d->dev,
+                             "do not support creating a drm_connector\n");
+               return -EINVAL;
+       }
+
+       if (!bridge->encoder) {
+               DRM_DEV_ERROR(p2d->dev, "missing encoder\n");
+               return -ENODEV;
+       }
+
+       return drm_bridge_attach(bridge->encoder,
+                                p2d->next_bridge, bridge,
+                                DRM_BRIDGE_ATTACH_NO_CONNECTOR);
+}
+
+static int
+imx8qxp_pxl2dpi_bridge_atomic_check(struct drm_bridge *bridge,
+                                   struct drm_bridge_state *bridge_state,
+                                   struct drm_crtc_state *crtc_state,
+                                   struct drm_connector_state *conn_state)
+{
+       struct imx8qxp_pxl2dpi *p2d = bridge->driver_private;
+
+       p2d->in_bus_format = bridge_state->input_bus_cfg.format;
+       p2d->out_bus_format = bridge_state->output_bus_cfg.format;
+
+       return 0;
+}
+
+static void
+imx8qxp_pxl2dpi_bridge_mode_set(struct drm_bridge *bridge,
+                               const struct drm_display_mode *mode,
+                               const struct drm_display_mode *adjusted_mode)
+{
+       struct imx8qxp_pxl2dpi *p2d = bridge->driver_private;
+       struct imx8qxp_pxl2dpi *companion_p2d;
+       int ret;
+
+       ret = pm_runtime_get_sync(p2d->dev);
+       if (ret < 0)
+               DRM_DEV_ERROR(p2d->dev,
+                             "failed to get runtime PM sync: %d\n", ret);
+
+       ret = imx_sc_misc_set_control(p2d->ipc_handle, p2d->sc_resource,
+                                     IMX_SC_C_PXL_LINK_SEL, p2d->pl_sel);
+       if (ret)
+               DRM_DEV_ERROR(p2d->dev,
+                             "failed to set pixel link selection(%u): %d\n",
+                                                       p2d->pl_sel, ret);
+
+       switch (p2d->out_bus_format) {
+       case MEDIA_BUS_FMT_RGB888_1X24:
+               regmap_write(p2d->regmap, PXL2DPI_CTRL, CFG_24BIT);
+               break;
+       case MEDIA_BUS_FMT_RGB666_1X24_CPADHI:
+               regmap_write(p2d->regmap, PXL2DPI_CTRL, CFG2_18BIT);
+               break;
+       default:
+               DRM_DEV_ERROR(p2d->dev,
+                             "unsupported output bus format 0x%08x\n",
+                                                       p2d->out_bus_format);
+       }
+
+       if (p2d->companion) {
+               companion_p2d = bridge_to_p2d(p2d->companion);
+
+               companion_p2d->in_bus_format = p2d->in_bus_format;
+               companion_p2d->out_bus_format = p2d->out_bus_format;
+
+               p2d->companion->funcs->mode_set(p2d->companion, mode,
+                                                       adjusted_mode);
+       }
+}
+
+static void
+imx8qxp_pxl2dpi_bridge_atomic_disable(struct drm_bridge *bridge,
+                                     struct drm_bridge_state *old_bridge_state)
+{
+       struct imx8qxp_pxl2dpi *p2d = bridge->driver_private;
+       int ret;
+
+       ret = pm_runtime_put(p2d->dev);
+       if (ret < 0)
+               DRM_DEV_ERROR(p2d->dev, "failed to put runtime PM: %d\n", ret);
+
+       if (p2d->companion)
+               p2d->companion->funcs->atomic_disable(p2d->companion,
+                                                       old_bridge_state);
+}
+
+static const u32 imx8qxp_pxl2dpi_bus_output_fmts[] = {
+       MEDIA_BUS_FMT_RGB888_1X24,
+       MEDIA_BUS_FMT_RGB666_1X24_CPADHI,
+};
+
+static bool imx8qxp_pxl2dpi_bus_output_fmt_supported(u32 fmt)
+{
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(imx8qxp_pxl2dpi_bus_output_fmts); i++) {
+               if (imx8qxp_pxl2dpi_bus_output_fmts[i] == fmt)
+                       return true;
+       }
+
+       return false;
+}
+
+static u32 *
+imx8qxp_pxl2dpi_bridge_atomic_get_input_bus_fmts(struct drm_bridge *bridge,
+                                                struct drm_bridge_state *bridge_state,
+                                                struct drm_crtc_state *crtc_state,
+                                                struct drm_connector_state *conn_state,
+                                                u32 output_fmt,
+                                                unsigned int *num_input_fmts)
+{
+       u32 *input_fmts;
+
+       if (!imx8qxp_pxl2dpi_bus_output_fmt_supported(output_fmt))
+               return NULL;
+
+       *num_input_fmts = 1;
+
+       input_fmts = kmalloc(sizeof(*input_fmts), GFP_KERNEL);
+       if (!input_fmts)
+               return NULL;
+
+       switch (output_fmt) {
+       case MEDIA_BUS_FMT_RGB888_1X24:
+               input_fmts[0] = MEDIA_BUS_FMT_RGB888_1X36_CPADLO;
+               break;
+       case MEDIA_BUS_FMT_RGB666_1X24_CPADHI:
+               input_fmts[0] = MEDIA_BUS_FMT_RGB666_1X36_CPADLO;
+               break;
+       default:
+               kfree(input_fmts);
+               input_fmts = NULL;
+               break;
+       }
+
+       return input_fmts;
+}
+
+static u32 *
+imx8qxp_pxl2dpi_bridge_atomic_get_output_bus_fmts(struct drm_bridge *bridge,
+                                                 struct drm_bridge_state *bridge_state,
+                                                 struct drm_crtc_state *crtc_state,
+                                                 struct drm_connector_state *conn_state,
+                                                 unsigned int *num_output_fmts)
+{
+       *num_output_fmts = ARRAY_SIZE(imx8qxp_pxl2dpi_bus_output_fmts);
+       return kmemdup(imx8qxp_pxl2dpi_bus_output_fmts,
+                       sizeof(imx8qxp_pxl2dpi_bus_output_fmts), GFP_KERNEL);
+}
+
+static const struct drm_bridge_funcs imx8qxp_pxl2dpi_bridge_funcs = {
+       .atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state,
+       .atomic_destroy_state   = drm_atomic_helper_bridge_destroy_state,
+       .atomic_reset           = drm_atomic_helper_bridge_reset,
+       .attach                 = imx8qxp_pxl2dpi_bridge_attach,
+       .atomic_check           = imx8qxp_pxl2dpi_bridge_atomic_check,
+       .mode_set               = imx8qxp_pxl2dpi_bridge_mode_set,
+       .atomic_disable         = imx8qxp_pxl2dpi_bridge_atomic_disable,
+       .atomic_get_input_bus_fmts =
+                       imx8qxp_pxl2dpi_bridge_atomic_get_input_bus_fmts,
+       .atomic_get_output_bus_fmts =
+                       imx8qxp_pxl2dpi_bridge_atomic_get_output_bus_fmts,
+};
+
+static struct device_node *
+imx8qxp_pxl2dpi_get_available_ep_from_port(struct imx8qxp_pxl2dpi *p2d,
+                                          u32 port_id)
+{
+       struct device_node *port, *ep;
+       int ep_cnt;
+
+       port = of_graph_get_port_by_id(p2d->dev->of_node, port_id);
+       if (!port) {
+               DRM_DEV_ERROR(p2d->dev, "failed to get port@%u\n", port_id);
+               return ERR_PTR(-ENODEV);
+       }
+
+       ep_cnt = of_get_available_child_count(port);
+       if (ep_cnt == 0) {
+               DRM_DEV_ERROR(p2d->dev, "no available endpoints of port@%u\n",
+                             port_id);
+               ep = ERR_PTR(-ENODEV);
+               goto out;
+       } else if (ep_cnt > 1) {
+               DRM_DEV_ERROR(p2d->dev,
+                             "invalid available endpoints of port@%u\n",
+                             port_id);
+               ep = ERR_PTR(-EINVAL);
+               goto out;
+       }
+
+       ep = of_get_next_available_child(port, NULL);
+       if (!ep) {
+               DRM_DEV_ERROR(p2d->dev,
+                             "failed to get available endpoint of port@%u\n",
+                             port_id);
+               ep = ERR_PTR(-ENODEV);
+               goto out;
+       }
+out:
+       of_node_put(port);
+       return ep;
+}
+
+static struct drm_bridge *
+imx8qxp_pxl2dpi_find_next_bridge(struct imx8qxp_pxl2dpi *p2d)
+{
+       struct device_node *ep, *remote;
+       struct drm_bridge *next_bridge;
+       int ret;
+
+       ep = imx8qxp_pxl2dpi_get_available_ep_from_port(p2d, 1);
+       if (IS_ERR(ep)) {
+               ret = PTR_ERR(ep);
+               return ERR_PTR(ret);
+       }
+
+       remote = of_graph_get_remote_port_parent(ep);
+       if (!remote || !of_device_is_available(remote)) {
+               DRM_DEV_ERROR(p2d->dev, "no available remote\n");
+               next_bridge = ERR_PTR(-ENODEV);
+               goto out;
+       } else if (!of_device_is_available(remote->parent)) {
+               DRM_DEV_ERROR(p2d->dev, "remote parent is not available\n");
+               next_bridge = ERR_PTR(-ENODEV);
+               goto out;
+       }
+
+       next_bridge = of_drm_find_bridge(remote);
+       if (!next_bridge) {
+               next_bridge = ERR_PTR(-EPROBE_DEFER);
+               goto out;
+       }
+out:
+       of_node_put(remote);
+       of_node_put(ep);
+
+       return next_bridge;
+}
+
+static int imx8qxp_pxl2dpi_set_pixel_link_sel(struct imx8qxp_pxl2dpi *p2d)
+{
+       struct device_node *ep;
+       struct of_endpoint endpoint;
+       int ret;
+
+       ep = imx8qxp_pxl2dpi_get_available_ep_from_port(p2d, 0);
+       if (IS_ERR(ep))
+               return PTR_ERR(ep);
+
+       ret = of_graph_parse_endpoint(ep, &endpoint);
+       if (ret) {
+               DRM_DEV_ERROR(p2d->dev,
+                             "failed to parse endpoint of port@0: %d\n", ret);
+               goto out;
+       }
+
+       p2d->pl_sel = endpoint.id;
+out:
+       of_node_put(ep);
+
+       return ret;
+}
+
+static int imx8qxp_pxl2dpi_parse_dt_companion(struct imx8qxp_pxl2dpi *p2d)
+{
+       struct imx8qxp_pxl2dpi *companion_p2d;
+       struct device *dev = p2d->dev;
+       struct device_node *companion;
+       struct device_node *port1, *port2;
+       const struct of_device_id *match;
+       int dual_link;
+       int ret = 0;
+
+       /* Locate the companion PXL2DPI for dual-link operation, if any. */
+       companion = of_parse_phandle(dev->of_node, "fsl,companion-pxl2dpi", 0);
+       if (!companion)
+               return 0;
+
+       if (!of_device_is_available(companion)) {
+               DRM_DEV_ERROR(dev, "companion PXL2DPI is not available\n");
+               ret = -ENODEV;
+               goto out;
+       }
+
+       /*
+        * Sanity check: the companion bridge must have the same compatible
+        * string.
+        */
+       match = of_match_device(dev->driver->of_match_table, dev);
+       if (!of_device_is_compatible(companion, match->compatible)) {
+               DRM_DEV_ERROR(dev, "companion PXL2DPI is incompatible\n");
+               ret = -ENXIO;
+               goto out;
+       }
+
+       p2d->companion = of_drm_find_bridge(companion);
+       if (!p2d->companion) {
+               ret = -EPROBE_DEFER;
+               DRM_DEV_DEBUG_DRIVER(p2d->dev,
+                                    "failed to find companion bridge: %d\n",
+                                    ret);
+               goto out;
+       }
+
+       companion_p2d = bridge_to_p2d(p2d->companion);
+
+       /*
+        * We need to work out if the sink is expecting us to function in
+        * dual-link mode.  We do this by looking at the DT port nodes that
+        * the next bridges are connected to.  If they are marked as expecting
+        * even pixels and odd pixels than we need to use the companion PXL2DPI.
+        */
+       port1 = of_graph_get_port_by_id(p2d->next_bridge->of_node, 1);
+       port2 = of_graph_get_port_by_id(companion_p2d->next_bridge->of_node, 1);
+       dual_link = drm_of_lvds_get_dual_link_pixel_order(port1, port2);
+       of_node_put(port1);
+       of_node_put(port2);
+
+       if (dual_link < 0) {
+               ret = dual_link;
+               DRM_DEV_ERROR(dev, "failed to get dual link pixel order: %d\n",
+                             ret);
+               goto out;
+       }
+
+       DRM_DEV_DEBUG_DRIVER(dev,
+                            "dual-link configuration detected (companion bridge %pOF)\n",
+                            companion);
+out:
+       of_node_put(companion);
+       return ret;
+}
+
+static int imx8qxp_pxl2dpi_bridge_probe(struct platform_device *pdev)
+{
+       struct imx8qxp_pxl2dpi *p2d;
+       struct device *dev = &pdev->dev;
+       struct device_node *np = dev->of_node;
+       int ret;
+
+       p2d = devm_kzalloc(dev, sizeof(*p2d), GFP_KERNEL);
+       if (!p2d)
+               return -ENOMEM;
+
+       p2d->regmap = syscon_node_to_regmap(np->parent);
+       if (IS_ERR(p2d->regmap)) {
+               ret = PTR_ERR(p2d->regmap);
+               if (ret != -EPROBE_DEFER)
+                       DRM_DEV_ERROR(dev, "failed to get regmap: %d\n", ret);
+               return ret;
+       }
+
+       ret = imx_scu_get_handle(&p2d->ipc_handle);
+       if (ret) {
+               if (ret != -EPROBE_DEFER)
+                       DRM_DEV_ERROR(dev, "failed to get SCU ipc handle: %d\n",
+                                     ret);
+               return ret;
+       }
+
+       p2d->dev = dev;
+
+       ret = of_property_read_u32(np, "fsl,sc-resource", &p2d->sc_resource);
+       if (ret) {
+               DRM_DEV_ERROR(dev, "failed to get SC resource %d\n", ret);
+               return ret;
+       }
+
+       p2d->next_bridge = imx8qxp_pxl2dpi_find_next_bridge(p2d);
+       if (IS_ERR(p2d->next_bridge)) {
+               ret = PTR_ERR(p2d->next_bridge);
+               if (ret != -EPROBE_DEFER)
+                       DRM_DEV_ERROR(dev, "failed to find next bridge: %d\n",
+                                     ret);
+               return ret;
+       }
+
+       ret = imx8qxp_pxl2dpi_set_pixel_link_sel(p2d);
+       if (ret)
+               return ret;
+
+       ret = imx8qxp_pxl2dpi_parse_dt_companion(p2d);
+       if (ret)
+               return ret;
+
+       platform_set_drvdata(pdev, p2d);
+       pm_runtime_enable(dev);
+
+       p2d->bridge.driver_private = p2d;
+       p2d->bridge.funcs = &imx8qxp_pxl2dpi_bridge_funcs;
+       p2d->bridge.of_node = np;
+
+       drm_bridge_add(&p2d->bridge);
+
+       return ret;
+}
+
+static int imx8qxp_pxl2dpi_bridge_remove(struct platform_device *pdev)
+{
+       struct imx8qxp_pxl2dpi *p2d = platform_get_drvdata(pdev);
+
+       drm_bridge_remove(&p2d->bridge);
+
+       pm_runtime_disable(&pdev->dev);
+
+       return 0;
+}
+
+static const struct of_device_id imx8qxp_pxl2dpi_dt_ids[] = {
+       { .compatible = "fsl,imx8qxp-pxl2dpi", },
+       { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, imx8qxp_pxl2dpi_dt_ids);
+
+static struct platform_driver imx8qxp_pxl2dpi_bridge_driver = {
+       .probe  = imx8qxp_pxl2dpi_bridge_probe,
+       .remove = imx8qxp_pxl2dpi_bridge_remove,
+       .driver = {
+               .of_match_table = imx8qxp_pxl2dpi_dt_ids,
+               .name = DRIVER_NAME,
+       },
+};
+module_platform_driver(imx8qxp_pxl2dpi_bridge_driver);
+
+MODULE_DESCRIPTION("i.MX8QXP pixel link to DPI bridge driver");
+MODULE_AUTHOR("Liu Ying <victor.liu@nxp.com>");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:" DRIVER_NAME);
index c925158..6a7a698 100644 (file)
@@ -11,6 +11,7 @@
 
 #include <drm/drm_probe_helper.h>
 #include <drm/drm_atomic_helper.h>
+#include <drm/drm_edid.h>
 #include <drm/drm_mipi_dsi.h>
 #include <drm/drm_of.h>
 
index 0ee563e..4277bf4 100644 (file)
@@ -170,6 +170,19 @@ static const struct drm_bridge_funcs panel_bridge_bridge_funcs = {
        .debugfs_init = panel_bridge_debugfs_init,
 };
 
+/**
+ * drm_bridge_is_panel - Checks if a drm_bridge is a panel_bridge.
+ *
+ * @bridge: The drm_bridge to be checked.
+ *
+ * Returns true if the bridge is a panel bridge, or false otherwise.
+ */
+bool drm_bridge_is_panel(const struct drm_bridge *bridge)
+{
+       return bridge->funcs == &panel_bridge_bridge_funcs;
+}
+EXPORT_SYMBOL(drm_bridge_is_panel);
+
 /**
  * drm_panel_bridge_add - Creates a &drm_bridge and &drm_connector that
  * just calls the appropriate functions from &drm_panel.
@@ -269,6 +282,27 @@ void drm_panel_bridge_remove(struct drm_bridge *bridge)
 }
 EXPORT_SYMBOL(drm_panel_bridge_remove);
 
+/**
+ * drm_panel_bridge_set_orientation - Set the connector's panel orientation
+ * from the bridge that can be transformed to panel bridge.
+ *
+ * @connector: The connector to be set panel orientation.
+ * @bridge: The drm_bridge to be transformed to panel bridge.
+ *
+ * Returns 0 on success, negative errno on failure.
+ */
+int drm_panel_bridge_set_orientation(struct drm_connector *connector,
+                                    struct drm_bridge *bridge)
+{
+       struct panel_bridge *panel_bridge;
+
+       panel_bridge = drm_bridge_to_panel_bridge(bridge);
+
+       return drm_connector_set_orientation_from_panel(connector,
+                                                       panel_bridge->panel);
+}
+EXPORT_SYMBOL(drm_panel_bridge_set_orientation);
+
 static void devm_drm_panel_bridge_release(struct device *dev, void *res)
 {
        struct drm_bridge **bridge = res;
index ff4227f..31e88cb 100644 (file)
@@ -16,6 +16,7 @@
 #include <drm/display/drm_dp_aux_bus.h>
 #include <drm/display/drm_dp_helper.h>
 #include <drm/drm_bridge.h>
+#include <drm/drm_edid.h>
 #include <drm/drm_mipi_dsi.h>
 #include <drm/drm_of.h>
 #include <drm/drm_panel.h>
@@ -168,23 +169,35 @@ static bool ps8640_of_panel_on_aux_bus(struct device *dev)
        return true;
 }
 
-static int ps8640_ensure_hpd(struct ps8640 *ps_bridge)
+static int _ps8640_wait_hpd_asserted(struct ps8640 *ps_bridge, unsigned long wait_us)
 {
        struct regmap *map = ps_bridge->regmap[PAGE2_TOP_CNTL];
-       struct device *dev = &ps_bridge->page[PAGE2_TOP_CNTL]->dev;
        int status;
-       int ret;
 
        /*
         * Apparently something about the firmware in the chip signals that
         * HPD goes high by reporting GPIO9 as high (even though HPD isn't
         * actually connected to GPIO9).
         */
-       ret = regmap_read_poll_timeout(map, PAGE2_GPIO_H, status,
-                                      status & PS_GPIO9, 20 * 1000, 200 * 1000);
+       return regmap_read_poll_timeout(map, PAGE2_GPIO_H, status,
+                                       status & PS_GPIO9, wait_us / 10, wait_us);
+}
 
-       if (ret < 0)
-               dev_warn(dev, "HPD didn't go high: %d\n", ret);
+static int ps8640_wait_hpd_asserted(struct drm_dp_aux *aux, unsigned long wait_us)
+{
+       struct ps8640 *ps_bridge = aux_to_ps8640(aux);
+       struct device *dev = &ps_bridge->page[PAGE0_DP_CNTL]->dev;
+       int ret;
+
+       /*
+        * Note that this function is called by code that has already powered
+        * the panel. We have to power ourselves up but we don't need to worry
+        * about powering the panel.
+        */
+       pm_runtime_get_sync(dev);
+       ret = _ps8640_wait_hpd_asserted(ps_bridge, wait_us);
+       pm_runtime_mark_last_busy(dev);
+       pm_runtime_put_autosuspend(dev);
 
        return ret;
 }
@@ -323,9 +336,7 @@ static ssize_t ps8640_aux_transfer(struct drm_dp_aux *aux,
        int ret;
 
        pm_runtime_get_sync(dev);
-       ret = ps8640_ensure_hpd(ps_bridge);
-       if (!ret)
-               ret = ps8640_aux_transfer_msg(aux, msg);
+       ret = ps8640_aux_transfer_msg(aux, msg);
        pm_runtime_mark_last_busy(dev);
        pm_runtime_put_autosuspend(dev);
 
@@ -369,8 +380,8 @@ static int __maybe_unused ps8640_resume(struct device *dev)
         * Mystery 200 ms delay for the "MCU to be ready". It's unclear if
         * this is truly necessary since the MCU will already signal that
         * things are "good to go" by signaling HPD on "gpio 9". See
-        * ps8640_ensure_hpd(). For now we'll keep this mystery delay just in
-        * case.
+        * _ps8640_wait_hpd_asserted(). For now we'll keep this mystery delay
+        * just in case.
         */
        msleep(200);
 
@@ -406,7 +417,9 @@ static void ps8640_pre_enable(struct drm_bridge *bridge)
        int ret;
 
        pm_runtime_get_sync(dev);
-       ps8640_ensure_hpd(ps_bridge);
+       ret = _ps8640_wait_hpd_asserted(ps_bridge, 200 * 1000);
+       if (ret < 0)
+               dev_warn(dev, "HPD didn't go high: %d\n", ret);
 
        /*
         * The Manufacturer Command Set (MCS) is a device dependent interface
@@ -682,6 +695,7 @@ static int ps8640_probe(struct i2c_client *client)
        ps_bridge->aux.name = "parade-ps8640-aux";
        ps_bridge->aux.dev = dev;
        ps_bridge->aux.transfer = ps8640_aux_transfer;
+       ps_bridge->aux.wait_hpd_asserted = ps8640_wait_hpd_asserted;
        drm_dp_aux_init(&ps_bridge->aux);
 
        pm_runtime_enable(dev);
index d974282..2c5c521 100644 (file)
@@ -15,6 +15,7 @@
 #include <drm/drm_atomic_helper.h>
 #include <drm/drm_bridge.h>
 #include <drm/drm_crtc.h>
+#include <drm/drm_edid.h>
 #include <drm/drm_print.h>
 #include <drm/drm_probe_helper.h>
 
index e5d00a6..7423b1b 100644 (file)
@@ -339,6 +339,7 @@ static void d2l_read(struct i2c_client *i2c, u16 addr, u32 *val)
                goto fail;
 
        pr_debug("d2l: I2C : addr:%04x value:%08x\n", addr, *val);
+       return;
 
 fail:
        dev_err(&i2c->dev, "Error %d reading from subaddress 0x%x\n",
@@ -429,7 +430,7 @@ static void tc_bridge_enable(struct drm_bridge *bridge)
                val = TC358775_VPCTRL_MSF(1);
 
        dsiclk = mode->crtc_clock * 3 * tc->bpc / tc->num_dsi_lanes / 1000;
-       clkdiv = dsiclk / DIVIDE_BY_3 * tc->lvds_link;
+       clkdiv = dsiclk / (tc->lvds_link == DUAL_LINK ? DIVIDE_BY_6 : DIVIDE_BY_3);
        byteclk = dsiclk / 4;
        t1 = hactive * (tc->bpc * 3 / 8) / tc->num_dsi_lanes;
        t2 = ((100000 / clkdiv)) * (hactive + hback_porch + hsync_len + hfront_porch) / 1000;
index b27c0d7..dc26640 100644 (file)
@@ -344,7 +344,7 @@ static void sn65dsi83_atomic_enable(struct drm_bridge *bridge,
        }
 
        /* Deassert reset */
-       gpiod_set_value(ctx->enable_gpio, 1);
+       gpiod_set_value_cansleep(ctx->enable_gpio, 1);
        usleep_range(1000, 1100);
 
        /* Get the LVDS format from the bridge state. */
@@ -500,7 +500,7 @@ static void sn65dsi83_atomic_disable(struct drm_bridge *bridge,
        int ret;
 
        /* Put the chip in reset, pull EN line low, and assure 10ms reset low timing. */
-       gpiod_set_value(ctx->enable_gpio, 0);
+       gpiod_set_value_cansleep(ctx->enable_gpio, 0);
        usleep_range(10000, 11000);
 
        ret = regulator_disable(ctx->vcc);
@@ -677,7 +677,7 @@ static int sn65dsi83_probe(struct i2c_client *client,
        ctx->enable_gpio = devm_gpiod_get_optional(ctx->dev, "enable",
                                                   GPIOD_OUT_LOW);
        if (IS_ERR(ctx->enable_gpio))
-               return PTR_ERR(ctx->enable_gpio);
+               return dev_err_probe(dev, PTR_ERR(ctx->enable_gpio), "failed to get enable GPIO\n");
 
        usleep_range(10000, 11000);
 
@@ -687,7 +687,7 @@ static int sn65dsi83_probe(struct i2c_client *client,
 
        ctx->regmap = devm_regmap_init_i2c(client, &sn65dsi83_regmap_config);
        if (IS_ERR(ctx->regmap))
-               return PTR_ERR(ctx->regmap);
+               return dev_err_probe(dev, PTR_ERR(ctx->regmap), "failed to get regmap\n");
 
        dev_set_drvdata(dev, ctx);
        i2c_set_clientdata(client, ctx);
index 756b3e6..4541126 100644 (file)
@@ -14,6 +14,7 @@
 #include <drm/drm_atomic_helper.h>
 #include <drm/drm_bridge.h>
 #include <drm/drm_crtc.h>
+#include <drm/drm_edid.h>
 #include <drm/drm_print.h>
 #include <drm/drm_probe_helper.h>
 
index e7c22c2..0c76537 100644 (file)
@@ -32,6 +32,7 @@
 
 #include <drm/display/drm_dp_helper.h>
 #include <drm/display/drm_dp_mst_helper.h>
+#include <drm/drm_edid.h>
 #include <drm/drm_print.h>
 #include <drm/drm_vblank.h>
 #include <drm/drm_panel.h>
@@ -1597,7 +1598,7 @@ static int drm_dp_aux_reply_duration(const struct drm_dp_aux_msg *msg)
  * Calculate the length of the i2c transfer in usec, assuming
  * the i2c bus speed is as specified. Gives the the "worst"
  * case estimate, ie. successful while as long as possible.
- * Doesn't account the the "MOT" bit, and instead assumes each
+ * Doesn't account the "MOT" bit, and instead assumes each
  * message includes a START, ADDRESS and STOP. Neither does it
  * account for additional random variables such as clock stretching.
  */
index 18f2b60..57e6542 100644 (file)
@@ -42,6 +42,7 @@
 #include <drm/drm_atomic.h>
 #include <drm/drm_atomic_helper.h>
 #include <drm/drm_drv.h>
+#include <drm/drm_edid.h>
 #include <drm/drm_print.h>
 #include <drm/drm_probe_helper.h>
 
@@ -2666,24 +2667,14 @@ static int drm_dp_check_and_send_link_address(struct drm_dp_mst_topology_mgr *mg
        }
 
        list_for_each_entry(port, &mstb->ports, next) {
-               struct drm_dp_mst_branch *mstb_child = NULL;
-
-               if (port->input || !port->ddps)
+               if (port->input || !port->ddps || !port->mstb)
                        continue;
 
-               if (port->mstb)
-                       mstb_child = drm_dp_mst_topology_get_mstb_validated(
-                           mgr, port->mstb);
-
-               if (mstb_child) {
-                       ret = drm_dp_check_and_send_link_address(mgr,
-                                                                mstb_child);
-                       drm_dp_mst_topology_put_mstb(mstb_child);
-                       if (ret == 1)
-                               changed = true;
-                       else if (ret < 0)
-                               return ret;
-               }
+               ret = drm_dp_check_and_send_link_address(mgr, port->mstb);
+               if (ret == 1)
+                       changed = true;
+               else if (ret < 0)
+                       return ret;
        }
 
        return changed;
@@ -5465,8 +5456,7 @@ EXPORT_SYMBOL(drm_dp_mst_topology_state_funcs);
  *
  * This function wraps drm_atomic_get_priv_obj_state() passing in the MST atomic
  * state vtable so that the private object state returned is that of a MST
- * topology object. Also, drm_atomic_get_private_obj_state() expects the caller
- * to care of the locking, so warn if don't hold the connection_mutex.
+ * topology object.
  *
  * RETURNS:
  *
index 74bd4a7..059fd71 100644 (file)
@@ -329,7 +329,20 @@ int drm_aperture_remove_conflicting_pci_framebuffers(struct pci_dev *pdev,
                                                     const struct drm_driver *req_driver)
 {
        resource_size_t base, size;
-       int bar, ret = 0;
+       int bar, ret;
+
+       /*
+        * WARNING: Apparently we must kick fbdev drivers before vgacon,
+        * otherwise the vga fbdev driver falls over.
+        */
+#if IS_REACHABLE(CONFIG_FB)
+       ret = remove_conflicting_pci_framebuffers(pdev, req_driver->name);
+       if (ret)
+               return ret;
+#endif
+       ret = vga_remove_vgacon(pdev);
+       if (ret)
+               return ret;
 
        for (bar = 0; bar < PCI_STD_NUM_BARS; ++bar) {
                if (!(pci_resource_flags(pdev, bar) & IORESOURCE_MEM))
@@ -339,15 +352,6 @@ int drm_aperture_remove_conflicting_pci_framebuffers(struct pci_dev *pdev,
                drm_aperture_detach_drivers(base, size);
        }
 
-       /*
-        * WARNING: Apparently we must kick fbdev drivers before vgacon,
-        * otherwise the vga fbdev driver falls over.
-        */
-#if IS_REACHABLE(CONFIG_FB)
-       ret = remove_conflicting_pci_framebuffers(pdev, req_driver->name);
-#endif
-       if (ret == 0)
-               ret = vga_remove_vgacon(pdev);
-       return ret;
+       return 0;
 }
 EXPORT_SYMBOL(drm_aperture_remove_conflicting_pci_framebuffers);
index 58c0283..f197f59 100644 (file)
 
 #include <drm/drm_atomic.h>
 #include <drm/drm_atomic_uapi.h>
+#include <drm/drm_blend.h>
 #include <drm/drm_bridge.h>
 #include <drm/drm_debugfs.h>
 #include <drm/drm_device.h>
 #include <drm/drm_drv.h>
 #include <drm/drm_file.h>
 #include <drm/drm_fourcc.h>
+#include <drm/drm_framebuffer.h>
 #include <drm/drm_mode.h>
 #include <drm/drm_print.h>
 #include <drm/drm_writeback.h>
index 987e4b2..0f685f4 100644 (file)
 #include <drm/drm_atomic.h>
 #include <drm/drm_atomic_helper.h>
 #include <drm/drm_atomic_uapi.h>
+#include <drm/drm_blend.h>
 #include <drm/drm_bridge.h>
 #include <drm/drm_damage_helper.h>
 #include <drm/drm_device.h>
 #include <drm/drm_drv.h>
+#include <drm/drm_framebuffer.h>
 #include <drm/drm_gem_atomic_helper.h>
 #include <drm/drm_plane_helper.h>
 #include <drm/drm_print.h>
index 3b6d3bd..bf31b9d 100644 (file)
 
 #include <drm/drm_atomic.h>
 #include <drm/drm_atomic_state_helper.h>
+#include <drm/drm_blend.h>
 #include <drm/drm_bridge.h>
 #include <drm/drm_connector.h>
 #include <drm/drm_crtc.h>
 #include <drm/drm_device.h>
+#include <drm/drm_framebuffer.h>
 #include <drm/drm_plane.h>
 #include <drm/drm_print.h>
 #include <drm/drm_vblank.h>
index 434f3d4..79730fa 100644 (file)
@@ -29,6 +29,7 @@
 
 #include <drm/drm_atomic_uapi.h>
 #include <drm/drm_atomic.h>
+#include <drm/drm_framebuffer.h>
 #include <drm/drm_print.h>
 #include <drm/drm_drv.h>
 #include <drm/drm_writeback.h>
index 6b3dad0..1c7d936 100644 (file)
@@ -331,7 +331,7 @@ struct drm_connector *drm_bridge_connector_init(struct drm_device *drm,
        struct drm_bridge_connector *bridge_connector;
        struct drm_connector *connector;
        struct i2c_adapter *ddc = NULL;
-       struct drm_bridge *bridge;
+       struct drm_bridge *bridge, *panel_bridge = NULL;
        int connector_type;
 
        bridge_connector = kzalloc(sizeof(*bridge_connector), GFP_KERNEL);
@@ -373,6 +373,9 @@ struct drm_connector *drm_bridge_connector_init(struct drm_device *drm,
 
                if (bridge->ddc)
                        ddc = bridge->ddc;
+
+               if (drm_bridge_is_panel(bridge))
+                       panel_bridge = bridge;
        }
 
        if (connector_type == DRM_MODE_CONNECTOR_Unknown) {
@@ -392,6 +395,9 @@ struct drm_connector *drm_bridge_connector_init(struct drm_device *drm,
                connector->polled = DRM_CONNECTOR_POLL_CONNECT
                                  | DRM_CONNECTOR_POLL_DISCONNECT;
 
+       if (panel_bridge)
+               drm_panel_bridge_set_orientation(connector, panel_bridge);
+
        return connector;
 }
 EXPORT_SYMBOL_GPL(drm_bridge_connector_init);
index 48e6ce1..bbc535c 100644 (file)
@@ -19,6 +19,7 @@
 #include <drm/drm_crtc.h>
 #include <drm/drm_device.h>
 #include <drm/drm_drv.h>
+#include <drm/drm_edid.h>
 #include <drm/drm_encoder.h>
 #include <drm/drm_print.h>
 
index 1c48d16..28ea0f8 100644 (file)
@@ -24,6 +24,7 @@
 #include <drm/drm_connector.h>
 #include <drm/drm_edid.h>
 #include <drm/drm_encoder.h>
+#include <drm/drm_panel.h>
 #include <drm/drm_utils.h>
 #include <drm/drm_print.h>
 #include <drm/drm_drv.h>
@@ -2320,6 +2321,9 @@ EXPORT_SYMBOL(drm_connector_set_vrr_capable_property);
  * It is allowed to call this function with a panel_orientation of
  * DRM_MODE_PANEL_ORIENTATION_UNKNOWN, in which case it is a no-op.
  *
+ * The function shouldn't be called in panel after drm is registered (i.e.
+ * drm_dev_register() is called in drm).
+ *
  * Returns:
  * Zero on success, negative errno on failure.
  */
@@ -2389,6 +2393,33 @@ int drm_connector_set_panel_orientation_with_quirk(
 }
 EXPORT_SYMBOL(drm_connector_set_panel_orientation_with_quirk);
 
+/**
+ * drm_connector_set_orientation_from_panel -
+ *     set the connector's panel_orientation from panel's callback.
+ * @connector: connector for which to init the panel-orientation property.
+ * @panel: panel that can provide orientation information.
+ *
+ * Drm drivers should call this function before drm_dev_register().
+ * Orientation is obtained from panel's .get_orientation() callback.
+ *
+ * Returns:
+ * Zero on success, negative errno on failure.
+ */
+int drm_connector_set_orientation_from_panel(
+       struct drm_connector *connector,
+       struct drm_panel *panel)
+{
+       enum drm_panel_orientation orientation;
+
+       if (panel && panel->funcs && panel->funcs->get_orientation)
+               orientation = panel->funcs->get_orientation(panel);
+       else
+               orientation = DRM_MODE_PANEL_ORIENTATION_UNKNOWN;
+
+       return drm_connector_set_panel_orientation(connector, orientation);
+}
+EXPORT_SYMBOL(drm_connector_set_orientation_from_panel);
+
 static const struct drm_prop_enum_list privacy_screen_enum[] = {
        { PRIVACY_SCREEN_DISABLED,              "Disabled" },
        { PRIVACY_SCREEN_ENABLED,               "Enabled" },
index 26a77a7..cad2a7e 100644 (file)
 #include <linux/export.h>
 #include <linux/dma-fence.h>
 #include <linux/uaccess.h>
+#include <drm/drm_blend.h>
 #include <drm/drm_crtc.h>
 #include <drm/drm_edid.h>
 #include <drm/drm_fourcc.h>
+#include <drm/drm_framebuffer.h>
 #include <drm/drm_managed.h>
 #include <drm/drm_modeset_lock.h>
 #include <drm/drm_atomic.h>
index b632825..8a6d545 100644 (file)
@@ -44,6 +44,7 @@
 #include <drm/drm_encoder.h>
 #include <drm/drm_fb_helper.h>
 #include <drm/drm_fourcc.h>
+#include <drm/drm_framebuffer.h>
 #include <drm/drm_plane_helper.h>
 #include <drm/drm_print.h>
 #include <drm/drm_vblank.h>
index 8eeff0c..937b699 100644 (file)
@@ -33,6 +33,7 @@
 #include <drm/drm_atomic.h>
 #include <drm/drm_damage_helper.h>
 #include <drm/drm_device.h>
+#include <drm/drm_framebuffer.h>
 
 static void convert_clip_rect_to_rect(const struct drm_clip_rect *src,
                                      struct drm_mode_rect *dest,
index 5ad2b6a..5e9c373 100644 (file)
@@ -43,6 +43,7 @@
 #include <drm/drm_drv.h>
 #include <drm/drm_fb_helper.h>
 #include <drm/drm_fourcc.h>
+#include <drm/drm_framebuffer.h>
 #include <drm/drm_print.h>
 #include <drm/drm_vblank.h>
 
index f16d602..b6a0110 100644 (file)
@@ -5,6 +5,7 @@
 
 #include <drm/drm_atomic_state_helper.h>
 #include <drm/drm_atomic_uapi.h>
+#include <drm/drm_framebuffer.h>
 #include <drm/drm_gem.h>
 #include <drm/drm_gem_atomic_helper.h>
 #include <drm/drm_gem_framebuffer_helper.h>
index 51fcf12..8faad23 100644 (file)
@@ -83,7 +83,7 @@
  *
  * 1. Directly call VERSION to get the version and to match against the driver
  *    name returned by that ioctl. Note that SET_VERSION is not called, which
- *    means the the unique name for the master node just opening is _not_ filled
+ *    means the unique name for the master node just opening is _not_ filled
  *    out. This despite that with current drm device nodes are always bound to
  *    one device, and can't be runtime assigned like with drm 1.0.
  * 2. Match driver name. If it mismatches, proceed to the next device node.
index 8be2008..0bf0fc1 100644 (file)
@@ -27,6 +27,7 @@
 
 #include <linux/module.h>
 
+#include <drm/drm_edid.h>
 #include <drm/drm_print.h>
 
 #include "drm_crtc_helper_internal.h"
index 09e4edb..0eda9dc 100644 (file)
@@ -18,6 +18,7 @@
 #include <drm/drm_file.h>
 #include <drm/drm_format_helper.h>
 #include <drm/drm_fourcc.h>
+#include <drm/drm_framebuffer.h>
 #include <drm/drm_gem.h>
 #include <drm/drm_gem_framebuffer_helper.h>
 #include <drm/drm_mipi_dbi.h>
index 37b4b9f..59b34f0 100644 (file)
@@ -25,6 +25,7 @@
 #include <drm/drm_drv.h>
 #include <drm/drm_encoder.h>
 #include <drm/drm_file.h>
+#include <drm/drm_framebuffer.h>
 #include <drm/drm_managed.h>
 #include <drm/drm_mode_config.h>
 #include <drm/drm_print.h>
index 40b7b24..a254225 100644 (file)
@@ -41,6 +41,7 @@
 
 #include <drm/drm_crtc.h>
 #include <drm/drm_device.h>
+#include <drm/drm_edid.h>
 #include <drm/drm_modes.h>
 #include <drm/drm_print.h>
 
index da48312..0f08319 100644 (file)
@@ -23,6 +23,7 @@
 #include <drm/drm_atomic_helper.h>
 #include <drm/drm_fb_helper.h>
 #include <drm/drm_fourcc.h>
+#include <drm/drm_framebuffer.h>
 #include <drm/drm_modeset_helper.h>
 #include <drm/drm_plane_helper.h>
 #include <drm/drm_print.h>
index 4e853ac..1276edf 100644 (file)
@@ -187,7 +187,7 @@ static const struct dmi_system_id orientation_data[] = {
                },
                .driver_data = (void *)&lcd800x1280_rightside_up,
        }, {    /*
-                * GPD Pocket, note that the the DMI data is less generic then
+                * GPD Pocket, note that the DMI data is less generic then
                 * it seems, devices with a board-vendor of "AMI Corporation"
                 * are quite rare, as are devices which have both board- *and*
                 * product-id set to "Default String"
index e3f09f1..a3f1806 100644 (file)
@@ -839,7 +839,7 @@ EXPORT_SYMBOL(drm_prime_pages_to_sg);
  * @sgt: sg_table describing the buffer to check
  *
  * This helper calculates the contiguous size in the DMA address space
- * of the the buffer described by the provided sg_table.
+ * of the buffer described by the provided sg_table.
  *
  * This is useful for implementing
  * &drm_gem_object_funcs.gem_prime_import_sg_table.
index 99fd15d..a031c33 100644 (file)
@@ -14,6 +14,7 @@
 #include <drm/drm_crtc.h>
 #include <drm/drm_device.h>
 #include <drm/drm_drv.h>
+#include <drm/drm_framebuffer.h>
 #include <drm/drm_modeset_helper_vtables.h>
 #include <drm/drm_property.h>
 #include <drm/drm_writeback.h>
index b5001db..8155d7e 100644 (file)
@@ -17,7 +17,9 @@
 #include <linux/pm_runtime.h>
 #include <linux/regmap.h>
 
+#include <drm/drm_blend.h>
 #include <drm/drm_fourcc.h>
+#include <drm/drm_framebuffer.h>
 #include <drm/drm_vblank.h>
 
 #include "exynos_drm_crtc.h"
index c04264f..3047edf 100644 (file)
@@ -20,6 +20,7 @@
 #include <video/of_videomode.h>
 
 #include <drm/drm_fourcc.h>
+#include <drm/drm_framebuffer.h>
 #include <drm/drm_vblank.h>
 #include <drm/exynos_drm.h>
 
index 79fa364..97f2dee 100644 (file)
@@ -12,6 +12,7 @@
 #include <drm/drm_atomic_helper.h>
 #include <drm/drm_crtc.h>
 #include <drm/drm_fb_helper.h>
+#include <drm/drm_framebuffer.h>
 #include <drm/drm_fourcc.h>
 #include <drm/drm_gem_framebuffer_helper.h>
 #include <drm/drm_probe_helper.h>
index 02c97b9..767afd2 100644 (file)
@@ -15,6 +15,7 @@
 #include <drm/drm_crtc.h>
 #include <drm/drm_fb_helper.h>
 #include <drm/drm_fourcc.h>
+#include <drm/drm_framebuffer.h>
 #include <drm/drm_prime.h>
 #include <drm/drm_probe_helper.h>
 #include <drm/exynos_drm.h>
index d5720fa..ae6636e 100644 (file)
@@ -21,7 +21,9 @@
 #include <video/of_videomode.h>
 #include <video/samsung_fimd.h>
 
+#include <drm/drm_blend.h>
 #include <drm/drm_fourcc.h>
+#include <drm/drm_framebuffer.h>
 #include <drm/drm_vblank.h>
 #include <drm/exynos_drm.h>
 
index 9ae8689..ea9f660 100644 (file)
@@ -18,6 +18,7 @@
 
 #include <linux/uaccess.h>
 
+#include <drm/drm_blend.h>
 #include <drm/drm_file.h>
 #include <drm/drm_fourcc.h>
 #include <drm/drm_mode.h>
index df76bde..66e5f1e 100644 (file)
@@ -7,6 +7,8 @@
 
 #include <drm/drm_atomic.h>
 #include <drm/drm_atomic_helper.h>
+#include <drm/drm_blend.h>
+#include <drm/drm_framebuffer.h>
 #include <drm/drm_plane_helper.h>
 #include <drm/exynos_drm.h>
 
index 3a7851b..3c049fb 100644 (file)
@@ -15,6 +15,7 @@
 #include <linux/platform_device.h>
 #include <linux/pm_runtime.h>
 
+#include <drm/drm_blend.h>
 #include <drm/drm_fourcc.h>
 #include <drm/exynos_drm.h>
 
index e5662bd..4d56c8c 100644 (file)
@@ -13,6 +13,7 @@
 
 #include <drm/drm_atomic_helper.h>
 #include <drm/drm_edid.h>
+#include <drm/drm_framebuffer.h>
 #include <drm/drm_probe_helper.h>
 #include <drm/drm_simple_kms_helper.h>
 #include <drm/drm_vblank.h>
index e5204be..65260a6 100644 (file)
 #include <linux/spinlock.h>
 #include <linux/wait.h>
 
+#include <drm/drm_blend.h>
+#include <drm/drm_edid.h>
 #include <drm/drm_fourcc.h>
+#include <drm/drm_framebuffer.h>
 #include <drm/drm_vblank.h>
 #include <drm/exynos_drm.h>
 
index 8fe953d..0cd527f 100644 (file)
@@ -12,6 +12,7 @@
 #include <drm/drm_crtc.h>
 #include <drm/drm_fb_cma_helper.h>
 #include <drm/drm_fourcc.h>
+#include <drm/drm_framebuffer.h>
 #include <drm/drm_gem_cma_helper.h>
 #include <drm/drm_plane_helper.h>
 #include <drm/drm_probe_helper.h>
index 9ee99a7..bb2e9d6 100644 (file)
@@ -32,6 +32,7 @@
 #include <drm/display/drm_dp_helper.h>
 #include <drm/drm_crtc.h>
 #include <drm/drm_crtc_helper.h>
+#include <drm/drm_edid.h>
 #include <drm/drm_simple_kms_helper.h>
 
 #include "gma_display.h"
index 0ac6ea5..aa3ecf7 100644 (file)
@@ -21,6 +21,7 @@
 #include <drm/drm_crtc.h>
 #include <drm/drm_fb_helper.h>
 #include <drm/drm_fourcc.h>
+#include <drm/drm_framebuffer.h>
 #include <drm/drm_gem_framebuffer_helper.h>
 
 #include "framebuffer.h"
index 34ec3fc..bd40c04 100644 (file)
@@ -12,6 +12,7 @@
 
 #include <drm/drm_crtc.h>
 #include <drm/drm_fourcc.h>
+#include <drm/drm_framebuffer.h>
 #include <drm/drm_vblank.h>
 
 #include "framebuffer.h"
index 22398d3..6004390 100644 (file)
@@ -8,6 +8,7 @@
 #include <linux/pm_runtime.h>
 
 #include <drm/drm_fourcc.h>
+#include <drm/drm_framebuffer.h>
 
 #include "framebuffer.h"
 #include "gem.h"
index b5946a1..95b7cb0 100644 (file)
@@ -27,6 +27,7 @@
 #include <linux/delay.h>
 
 #include <drm/drm.h>
+#include <drm/drm_edid.h>
 #include <drm/drm_simple_kms_helper.h>
 
 #include "psb_drv.h"
index 9c9ebf8..4d98df1 100644 (file)
@@ -13,6 +13,7 @@
 
 #include <asm/intel-mid.h>
 
+#include <drm/drm_edid.h>
 #include <drm/drm_simple_kms_helper.h>
 
 #include "intel_bios.h"
index 6030678..8be0ec3 100644 (file)
@@ -7,6 +7,8 @@
 
 #include <linux/i2c.h>
 
+#include <drm/drm_edid.h>
+
 #include "psb_intel_drv.h"
 
 /**
index ae05113..d0addd4 100644 (file)
@@ -10,6 +10,7 @@
 #include <drm/drm_atomic_state_helper.h>
 #include <drm/drm_connector.h>
 #include <drm/drm_drv.h>
+#include <drm/drm_edid.h>
 #include <drm/drm_encoder.h>
 #include <drm/drm_file.h>
 #include <drm/drm_modeset_helper_vtables.h>
index 3f9d4b9..8d1630b 100644 (file)
@@ -14,6 +14,7 @@
 #include <linux/workqueue.h>
 
 #include <drm/drm_atomic_helper.h>
+#include <drm/drm_blend.h>
 #include <drm/drm_damage_helper.h>
 #include <drm/drm_debugfs.h>
 #include <drm/drm_drv.h>
index 1ab9462..61c29c2 100644 (file)
@@ -26,6 +26,7 @@
 #include <drm/drm_drv.h>
 #include <drm/drm_fb_cma_helper.h>
 #include <drm/drm_fourcc.h>
+#include <drm/drm_framebuffer.h>
 #include <drm/drm_gem_cma_helper.h>
 #include <drm/drm_plane_helper.h>
 #include <drm/drm_probe_helper.h>
index 27f4fcb..b8e64dd 100644 (file)
@@ -7,9 +7,11 @@
 
 #include <drm/drm_damage_helper.h>
 #include <drm/drm_drv.h>
+#include <drm/drm_edid.h>
 #include <drm/drm_fb_helper.h>
 #include <drm/drm_format_helper.h>
 #include <drm/drm_fourcc.h>
+#include <drm/drm_framebuffer.h>
 #include <drm/drm_gem_atomic_helper.h>
 #include <drm/drm_gem_framebuffer_helper.h>
 #include <drm/drm_gem_shmem_helper.h>
index 7fe1a4e..592e5ad 100644 (file)
@@ -5,6 +5,7 @@
 #include <linux/kernel.h>
 
 #include <drm/drm_atomic_helper.h>
+#include <drm/drm_blend.h>
 #include <drm/drm_fourcc.h>
 #include <drm/drm_plane_helper.h>
 
index 0c5638f..96cd1f0 100644 (file)
@@ -25,6 +25,7 @@
  *
  */
 
+#include <drm/drm_edid.h>
 #include <drm/display/drm_dp_helper.h>
 #include <drm/display/drm_dsc_helper.h>
 
index 8c80de8..c2797ad 100644 (file)
@@ -6,6 +6,7 @@
 
 #include <drm/drm_atomic_helper.h>
 #include <drm/drm_atomic_uapi.h>
+#include <drm/drm_blend.h>
 #include <drm/drm_damage_helper.h>
 #include <drm/drm_plane_helper.h>
 #include <drm/drm_fourcc.h>
index 408152f..a0ec5b0 100644 (file)
@@ -38,6 +38,7 @@
 #include <drm/drm_crtc.h>
 #include <drm/drm_encoder.h>
 #include <drm/drm_fourcc.h>
+#include <drm/drm_framebuffer.h>
 #include <drm/drm_probe_helper.h>
 #include <drm/drm_rect.h>
 #include <drm/drm_vblank.h>
index e4a79c1..7c96088 100644 (file)
@@ -40,6 +40,7 @@
 #include <drm/display/drm_hdmi_helper.h>
 #include <drm/drm_atomic_helper.h>
 #include <drm/drm_crtc.h>
+#include <drm/drm_edid.h>
 #include <drm/drm_probe_helper.h>
 
 #include "g4x_dp.h"
index 9f5a6b7..b191915 100644 (file)
@@ -3,6 +3,7 @@
  * Copyright Â© 2021 Intel Corporation
  */
 
+#include <drm/drm_blend.h>
 #include <drm/drm_framebuffer.h>
 #include <drm/drm_modeset_helper.h>
 
index bbdc34a..6efae74 100644 (file)
@@ -40,6 +40,7 @@
 
 #include <linux/string_helpers.h>
 
+#include <drm/drm_blend.h>
 #include <drm/drm_fourcc.h>
 
 #include "i915_drv.h"
index 7fbc803..15d59de 100644 (file)
@@ -26,6 +26,7 @@
 #include <drm/display/drm_dp_dual_mode_helper.h>
 #include <drm/display/drm_hdmi_helper.h>
 #include <drm/drm_atomic_helper.h>
+#include <drm/drm_edid.h>
 
 #include "intel_de.h"
 #include "intel_display_types.h"
index f31e8c3..3aea4e4 100644 (file)
@@ -30,6 +30,8 @@
 #include <linux/firmware.h>
 #include <acpi/video.h>
 
+#include <drm/drm_edid.h>
+
 #include "i915_drv.h"
 #include "intel_acpi.h"
 #include "intel_backlight.h"
index 7c0df80..2713faa 100644 (file)
@@ -34,6 +34,7 @@
 
 #include <drm/drm_atomic.h>
 #include <drm/drm_atomic_helper.h>
+#include <drm/drm_blend.h>
 #include <drm/drm_color_mgmt.h>
 #include <drm/drm_crtc.h>
 #include <drm/drm_damage_helper.h>
index caa0332..c11e15a 100644 (file)
@@ -4,6 +4,7 @@
  */
 
 #include <drm/drm_atomic_helper.h>
+#include <drm/drm_blend.h>
 #include <drm/drm_damage_helper.h>
 #include <drm/drm_fourcc.h>
 #include <drm/drm_plane_helper.h>
index 5735915..16a08be 100644 (file)
@@ -30,6 +30,7 @@
 #include <linux/pm_runtime.h>
 
 #include <drm/drm_atomic_helper.h>
+#include <drm/drm_blend.h>
 #include <drm/drm_fourcc.h>
 #include <drm/drm_plane_helper.h>
 
index ac45d54..c29f343 100644 (file)
@@ -5,7 +5,9 @@
 
 #include <drm/drm_atomic.h>
 #include <drm/drm_atomic_helper.h>
+#include <drm/drm_blend.h>
 #include <drm/drm_fb_cma_helper.h>
+#include <drm/drm_framebuffer.h>
 #include <drm/drm_gem_atomic_helper.h>
 #include <drm/drm_gem_cma_helper.h>
 
index 14a058a..8bf8851 100644 (file)
@@ -21,6 +21,7 @@
 #include <drm/drm_atomic.h>
 #include <drm/drm_atomic_helper.h>
 #include <drm/drm_bridge.h>
+#include <drm/drm_edid.h>
 #include <drm/drm_fb_helper.h>
 #include <drm/drm_managed.h>
 #include <drm/drm_of.h>
index 2b1fdf2..6b34fac 100644 (file)
@@ -18,6 +18,7 @@
 #include <video/imx-ipu-v3.h>
 
 #include <drm/drm_atomic_helper.h>
+#include <drm/drm_edid.h>
 #include <drm/drm_fb_helper.h>
 #include <drm/drm_managed.h>
 #include <drm/drm_probe_helper.h>
index 36b32e8..ea5f594 100644 (file)
@@ -7,8 +7,10 @@
 
 #include <drm/drm_atomic.h>
 #include <drm/drm_atomic_helper.h>
+#include <drm/drm_blend.h>
 #include <drm/drm_fb_cma_helper.h>
 #include <drm/drm_fourcc.h>
+#include <drm/drm_framebuffer.h>
 #include <drm/drm_gem_atomic_helper.h>
 #include <drm/drm_gem_cma_helper.h>
 #include <drm/drm_managed.h>
index 63ba2ad..e4fd453 100644 (file)
@@ -14,6 +14,7 @@
 
 #include <drm/drm_atomic_helper.h>
 #include <drm/drm_bridge.h>
+#include <drm/drm_edid.h>
 #include <drm/drm_fb_helper.h>
 #include <drm/drm_managed.h>
 #include <drm/drm_of.h>
index 8eb0ad5..2c55988 100644 (file)
@@ -33,6 +33,7 @@
 #include <drm/drm_fb_cma_helper.h>
 #include <drm/drm_fb_helper.h>
 #include <drm/drm_fourcc.h>
+#include <drm/drm_framebuffer.h>
 #include <drm/drm_gem_atomic_helper.h>
 #include <drm/drm_gem_framebuffer_helper.h>
 #include <drm/drm_managed.h>
index 2737fc5..32a5093 100644 (file)
@@ -24,6 +24,7 @@
 #include <drm/drm_drv.h>
 #include <drm/drm_fb_cma_helper.h>
 #include <drm/drm_fourcc.h>
+#include <drm/drm_framebuffer.h>
 #include <drm/drm_gem_atomic_helper.h>
 #include <drm/drm_gem_cma_helper.h>
 #include <drm/drm_gem_framebuffer_helper.h>
index 2735b8e..89d055a 100644 (file)
@@ -5,11 +5,13 @@
 
 #include <drm/drm_atomic.h>
 #include <drm/drm_atomic_helper.h>
+#include <drm/drm_blend.h>
 #include <drm/drm_crtc.h>
 #include <drm/drm_crtc_helper.h>
 #include <drm/drm_fb_cma_helper.h>
 #include <drm/drm_fb_helper.h>
 #include <drm/drm_fourcc.h>
+#include <drm/drm_framebuffer.h>
 #include <drm/drm_gem_cma_helper.h>
 #include <drm/drm_managed.h>
 #include <drm/drm_plane_helper.h>
index bae1c7f..fbebe96 100644 (file)
@@ -9,8 +9,10 @@
 
 #include <drm/drm_atomic.h>
 #include <drm/drm_atomic_helper.h>
+#include <drm/drm_blend.h>
 #include <drm/drm_fb_cma_helper.h>
 #include <drm/drm_fourcc.h>
+#include <drm/drm_framebuffer.h>
 #include <drm/drm_plane.h>
 #include <drm/drm_plane_helper.h>
 #include <drm/drm_print.h>
index ce12a36..4df4775 100644 (file)
@@ -13,6 +13,7 @@
 #include <drm/drm_device.h>
 #include <drm/drm_fb_cma_helper.h>
 #include <drm/drm_fourcc.h>
+#include <drm/drm_framebuffer.h>
 #include <drm/drm_gem_atomic_helper.h>
 #include <drm/drm_gem_cma_helper.h>
 #include <drm/drm_mipi_dsi.h>
index 70ab229..002b0f6 100644 (file)
@@ -3,7 +3,9 @@
  * Copyright (c) 2015 MediaTek Inc.
  */
 
+#include <drm/drm_blend.h>
 #include <drm/drm_fourcc.h>
+#include <drm/drm_framebuffer.h>
 
 #include <linux/clk.h>
 #include <linux/component.h>
index 1be4caf..2cb9046 100644 (file)
@@ -3,6 +3,8 @@
  * Copyright (c) 2015 MediaTek Inc.
  */
 
+#include <drm/drm_fourcc.h>
+
 #include <linux/clk.h>
 #include <linux/component.h>
 #include <linux/module.h>
index e5fae4e..91f57cb 100644 (file)
@@ -7,7 +7,9 @@
 #include <drm/drm_atomic.h>
 #include <drm/drm_atomic_helper.h>
 #include <drm/drm_atomic_uapi.h>
+#include <drm/drm_blend.h>
 #include <drm/drm_fourcc.h>
+#include <drm/drm_framebuffer.h>
 #include <drm/drm_gem_atomic_helper.h>
 #include <drm/drm_plane_helper.h>
 
index dfef8af..b4a0518 100644 (file)
@@ -9,9 +9,11 @@
 
 #include <drm/drm_atomic.h>
 #include <drm/drm_atomic_helper.h>
+#include <drm/drm_blend.h>
 #include <drm/drm_device.h>
 #include <drm/drm_fb_cma_helper.h>
 #include <drm/drm_fourcc.h>
+#include <drm/drm_framebuffer.h>
 #include <drm/drm_gem_atomic_helper.h>
 #include <drm/drm_gem_cma_helper.h>
 #include <drm/drm_plane_helper.h>
index 8640a8a..b9ac932 100644 (file)
 
 #include <drm/drm_atomic.h>
 #include <drm/drm_atomic_helper.h>
+#include <drm/drm_blend.h>
 #include <drm/drm_device.h>
 #include <drm/drm_fb_cma_helper.h>
 #include <drm/drm_fourcc.h>
+#include <drm/drm_framebuffer.h>
 #include <drm/drm_gem_atomic_helper.h>
 #include <drm/drm_gem_cma_helper.h>
 #include <drm/drm_plane_helper.h>
index e339f50..225cca2 100644 (file)
@@ -17,6 +17,7 @@
 #include <drm/drm_damage_helper.h>
 #include <drm/drm_format_helper.h>
 #include <drm/drm_fourcc.h>
+#include <drm/drm_framebuffer.h>
 #include <drm/drm_gem_atomic_helper.h>
 #include <drm/drm_gem_framebuffer_helper.h>
 #include <drm/drm_plane_helper.h>
index b56f777..c141548 100644 (file)
 #include <linux/bits.h>
 
 #include <drm/drm_atomic.h>
+#include <drm/drm_blend.h>
 #include <drm/drm_crtc.h>
 #include <drm/drm_flip_work.h>
+#include <drm/drm_framebuffer.h>
 #include <drm/drm_mode.h>
 #include <drm/drm_probe_helper.h>
 #include <drm/drm_rect.h>
index 59da348..45846c7 100644 (file)
@@ -7,6 +7,8 @@
 
 #include <linux/debugfs.h>
 
+#include <drm/drm_framebuffer.h>
+
 #include "dpu_encoder_phys.h"
 #include "dpu_formats.h"
 #include "dpu_hw_top.h"
index 440ae93..f436a1f 100644 (file)
@@ -5,6 +5,7 @@
 #define pr_fmt(fmt)    "[drm:%s:%d] " fmt, __func__, __LINE__
 
 #include <uapi/drm/drm_fourcc.h>
+#include <drm/drm_framebuffer.h>
 
 #include "msm_media_info.h"
 #include "dpu_kms.h"
index e23e255..7a21fd6 100644 (file)
@@ -16,6 +16,7 @@
 
 #include <drm/drm_crtc.h>
 #include <drm/drm_file.h>
+#include <drm/drm_framebuffer.h>
 #include <drm/drm_vblank.h>
 #include <drm/drm_writeback.h>
 
index 5b5aef2..edf3248 100644 (file)
@@ -12,7 +12,9 @@
 
 #include <drm/drm_atomic.h>
 #include <drm/drm_atomic_uapi.h>
+#include <drm/drm_blend.h>
 #include <drm/drm_damage_helper.h>
+#include <drm/drm_framebuffer.h>
 #include <drm/drm_gem_atomic_helper.h>
 
 #include "msm_drv.h"
index 399115e..1ea62ec 100644 (file)
@@ -3,6 +3,8 @@
  * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved.
  */
 
+#include <drm/drm_edid.h>
+
 #include "dpu_writeback.h"
 
 static int dpu_wb_conn_get_modes(struct drm_connector *connector)
index 3e20f72..b689b61 100644 (file)
@@ -7,6 +7,7 @@
 #include <drm/drm_atomic.h>
 #include <drm/drm_damage_helper.h>
 #include <drm/drm_fourcc.h>
+#include <drm/drm_framebuffer.h>
 #include <drm/drm_gem_atomic_helper.h>
 
 #include "mdp4_kms.h"
index 31447da..e86421c 100644 (file)
@@ -8,6 +8,7 @@
 #include <linux/sort.h>
 
 #include <drm/drm_atomic.h>
+#include <drm/drm_blend.h>
 #include <drm/drm_mode.h>
 #include <drm/drm_crtc.h>
 #include <drm/drm_flip_work.h>
index e8c47a4..bd2c4ac 100644 (file)
@@ -6,8 +6,10 @@
  */
 
 #include <drm/drm_atomic.h>
+#include <drm/drm_blend.h>
 #include <drm/drm_damage_helper.h>
 #include <drm/drm_fourcc.h>
+#include <drm/drm_framebuffer.h>
 #include <drm/drm_gem_atomic_helper.h>
 #include <drm/drm_print.h>
 
index 5495d8b..0255953 100644 (file)
@@ -5,6 +5,8 @@
  * Author: Rob Clark <robdclark@gmail.com>
  */
 
+#include <drm/drm_fourcc.h>
+#include <drm/drm_framebuffer.h>
 
 #include "msm_drv.h"
 #include "mdp_kms.h"
index 97c2401..2e4c2d5 100644 (file)
@@ -6,6 +6,7 @@
 
 #include <linux/delay.h>
 #include <drm/drm_bridge_connector.h>
+#include <drm/drm_edid.h>
 
 #include "msm_kms.h"
 #include "hdmi.h"
index ea2a206..7d2dab2 100644 (file)
@@ -10,6 +10,7 @@
 
 #include <drm/drm_debugfs.h>
 #include <drm/drm_file.h>
+#include <drm/drm_framebuffer.h>
 
 #include "msm_drv.h"
 #include "msm_gpu.h"
index 4269da2..e3f61c3 100644 (file)
@@ -8,6 +8,7 @@
 #include <drm/drm_damage_helper.h>
 #include <drm/drm_file.h>
 #include <drm/drm_fourcc.h>
+#include <drm/drm_framebuffer.h>
 #include <drm/drm_gem_framebuffer_helper.h>
 #include <drm/drm_probe_helper.h>
 
index 4c39ef9..46168ec 100644 (file)
@@ -8,6 +8,7 @@
 #include <drm/drm_crtc.h>
 #include <drm/drm_fb_helper.h>
 #include <drm/drm_fourcc.h>
+#include <drm/drm_framebuffer.h>
 #include <drm/drm_prime.h>
 
 #include "msm_drv.h"
index f021ab2..7d38769 100644 (file)
@@ -21,6 +21,7 @@
 #include <drm/drm_encoder.h>
 #include <drm/drm_fb_cma_helper.h>
 #include <drm/drm_fourcc.h>
+#include <drm/drm_framebuffer.h>
 #include <drm/drm_gem_atomic_helper.h>
 #include <drm/drm_gem_cma_helper.h>
 #include <drm/drm_plane.h>
index bb8a460..ef21cfa 100644 (file)
@@ -32,6 +32,7 @@
 
 #include <drm/drm_atomic.h>
 #include <drm/drm_atomic_helper.h>
+#include <drm/drm_blend.h>
 #include <drm/drm_gem_atomic_helper.h>
 #include <drm/drm_fourcc.h>
 
index ca74775..ae793f4 100644 (file)
@@ -1048,7 +1048,7 @@ nvkm_vmm_ctor(const struct nvkm_vmm_func *func, struct nvkm_mmu *mmu,
        __mutex_init(&vmm->mutex, "&vmm->mutex", key ? key : &_key);
 
        /* Locate the smallest page size supported by the backend, it will
-        * have the the deepest nesting of page tables.
+        * have the deepest nesting of page tables.
         */
        while (page[1].shift)
                page++;
index 35b750c..a8a75dc 100644 (file)
@@ -29,6 +29,7 @@
 
 #include <drm/drm_atomic.h>
 #include <drm/drm_atomic_state_helper.h>
+#include <drm/drm_edid.h>
 
 #include "omapdss.h"
 #include "hdmi4_core.h"
index 65085d8..868712c 100644 (file)
@@ -32,6 +32,7 @@
 
 #include <drm/drm_atomic.h>
 #include <drm/drm_atomic_state_helper.h>
+#include <drm/drm_edid.h>
 
 #include "omapdss.h"
 #include "hdmi5_core.h"
index 2d3909a..bfb2ccb 100644 (file)
@@ -10,6 +10,7 @@
 #include <drm/drm_debugfs.h>
 #include <drm/drm_file.h>
 #include <drm/drm_fb_helper.h>
+#include <drm/drm_framebuffer.h>
 
 #include "omap_drv.h"
 #include "omap_dmm_tiler.h"
index 895e66b..1d414b3 100644 (file)
@@ -6,8 +6,10 @@
 
 #include <linux/dma-mapping.h>
 
+#include <drm/drm_blend.h>
 #include <drm/drm_modeset_helper.h>
 #include <drm/drm_fourcc.h>
+#include <drm/drm_framebuffer.h>
 #include <drm/drm_gem_framebuffer_helper.h>
 
 #include "omap_dmm_tiler.h"
index 42eac6a..40706c5 100644 (file)
@@ -9,6 +9,7 @@
 #include <drm/drm_fb_helper.h>
 #include <drm/drm_file.h>
 #include <drm/drm_fourcc.h>
+#include <drm/drm_framebuffer.h>
 
 #include "omap_drv.h"
 
index b83d91e..b6cb537 100644 (file)
@@ -6,9 +6,11 @@
 
 #include <drm/drm_atomic.h>
 #include <drm/drm_atomic_helper.h>
+#include <drm/drm_blend.h>
 #include <drm/drm_gem_atomic_helper.h>
 #include <drm/drm_plane_helper.h>
 #include <drm/drm_fourcc.h>
+#include <drm/drm_framebuffer.h>
 
 #include "omap_dmm_tiler.h"
 #include "omap_drv.h"
index 1be150a..07f722f 100644 (file)
@@ -1511,16 +1511,28 @@ static int boe_panel_get_modes(struct drm_panel *panel,
        connector->display_info.width_mm = boe->desc->size.width_mm;
        connector->display_info.height_mm = boe->desc->size.height_mm;
        connector->display_info.bpc = boe->desc->bpc;
+       /*
+        * TODO: Remove once all drm drivers call
+        * drm_connector_set_orientation_from_panel()
+        */
        drm_connector_set_panel_orientation(connector, boe->orientation);
 
        return 1;
 }
 
+static enum drm_panel_orientation boe_panel_get_orientation(struct drm_panel *panel)
+{
+       struct boe_panel *boe = to_boe_panel(panel);
+
+       return boe->orientation;
+}
+
 static const struct drm_panel_funcs boe_panel_funcs = {
        .unprepare = boe_panel_unprepare,
        .prepare = boe_panel_prepare,
        .enable = boe_panel_enable,
        .get_modes = boe_panel_get_modes,
+       .get_orientation = boe_panel_get_orientation,
 };
 
 static int boe_panel_add(struct boe_panel *boe)
index c960144..16bdcd8 100644 (file)
@@ -39,6 +39,7 @@
 #include <drm/display/drm_dp_helper.h>
 #include <drm/drm_crtc.h>
 #include <drm/drm_device.h>
+#include <drm/drm_edid.h>
 #include <drm/drm_panel.h>
 
 /**
@@ -417,6 +418,11 @@ static int panel_edp_get_hpd_gpio(struct device *dev, struct panel_edp *p)
        return 0;
 }
 
+static bool panel_edp_can_read_hpd(struct panel_edp *p)
+{
+       return !p->no_hpd && (p->hpd_gpio || (p->aux && p->aux->wait_hpd_asserted));
+}
+
 static int panel_edp_prepare_once(struct panel_edp *p)
 {
        struct device *dev = p->base.dev;
@@ -441,17 +447,21 @@ static int panel_edp_prepare_once(struct panel_edp *p)
        if (delay)
                msleep(delay);
 
-       if (p->hpd_gpio) {
+       if (panel_edp_can_read_hpd(p)) {
                if (p->desc->delay.hpd_absent)
                        hpd_wait_us = p->desc->delay.hpd_absent * 1000UL;
                else
                        hpd_wait_us = 2000000;
 
-               err = readx_poll_timeout(gpiod_get_value_cansleep, p->hpd_gpio,
-                                        hpd_asserted, hpd_asserted,
-                                        1000, hpd_wait_us);
-               if (hpd_asserted < 0)
-                       err = hpd_asserted;
+               if (p->hpd_gpio) {
+                       err = readx_poll_timeout(gpiod_get_value_cansleep,
+                                                p->hpd_gpio, hpd_asserted,
+                                                hpd_asserted, 1000, hpd_wait_us);
+                       if (hpd_asserted < 0)
+                               err = hpd_asserted;
+               } else {
+                       err = p->aux->wait_hpd_asserted(p->aux, hpd_wait_us);
+               }
 
                if (err) {
                        if (err != -ETIMEDOUT)
@@ -532,18 +542,22 @@ static int panel_edp_enable(struct drm_panel *panel)
        /*
         * If there is a "prepare_to_enable" delay then that's supposed to be
         * the delay from HPD going high until we can turn the backlight on.
-        * However, we can only count this if HPD is handled by the panel
-        * driver, not if it goes to a dedicated pin on the controller.
+        * However, we can only count this if HPD is readable by the panel
+        * driver.
+        *
         * If we aren't handling the HPD pin ourselves then the best we
         * can do is assume that HPD went high immediately before we were
-        * called (and link training took zero time).
+        * called (and link training took zero time). Note that "no-hpd"
+        * actually counts as handling HPD ourselves since we're doing the
+        * worst case delay (in prepare) ourselves.
         *
         * NOTE: if we ever end up in this "if" statement then we're
         * guaranteed that the panel_edp_wait() call below will do no delay.
         * It already handles that case, though, so we don't need any special
         * code for it.
         */
-       if (p->desc->delay.prepare_to_enable && !p->hpd_gpio && !p->no_hpd)
+       if (p->desc->delay.prepare_to_enable &&
+           !panel_edp_can_read_hpd(p) && !p->no_hpd)
                delay = max(delay, p->desc->delay.prepare_to_enable);
 
        if (delay)
@@ -586,7 +600,10 @@ static int panel_edp_get_modes(struct drm_panel *panel,
        else if (!num)
                dev_warn(p->base.dev, "No display modes\n");
 
-       /* set up connector's "panel orientation" property */
+       /*
+        * TODO: Remove once all drm drivers call
+        * drm_connector_set_orientation_from_panel()
+        */
        drm_connector_set_panel_orientation(connector, p->orientation);
 
        return num;
@@ -609,6 +626,13 @@ static int panel_edp_get_timings(struct drm_panel *panel,
        return p->desc->num_timings;
 }
 
+static enum drm_panel_orientation panel_edp_get_orientation(struct drm_panel *panel)
+{
+       struct panel_edp *p = to_panel_edp(panel);
+
+       return p->orientation;
+}
+
 static int detected_panel_show(struct seq_file *s, void *data)
 {
        struct drm_panel *panel = s->private;
@@ -637,6 +661,7 @@ static const struct drm_panel_funcs panel_edp_funcs = {
        .prepare = panel_edp_prepare,
        .enable = panel_edp_enable,
        .get_modes = panel_edp_get_modes,
+       .get_orientation = panel_edp_get_orientation,
        .get_timings = panel_edp_get_timings,
        .debugfs_init = panel_edp_debugfs_init,
 };
index 8022761..01dd555 100644 (file)
@@ -217,15 +217,27 @@ static int kd35t133_get_modes(struct drm_panel *panel,
        connector->display_info.width_mm = mode->width_mm;
        connector->display_info.height_mm = mode->height_mm;
        drm_mode_probed_add(connector, mode);
+       /*
+        * TODO: Remove once all drm drivers call
+        * drm_connector_set_orientation_from_panel()
+        */
        drm_connector_set_panel_orientation(connector, ctx->orientation);
 
        return 1;
 }
 
+static enum drm_panel_orientation kd35t133_get_orientation(struct drm_panel *panel)
+{
+       struct kd35t133 *ctx = panel_to_kd35t133(panel);
+
+       return ctx->orientation;
+}
+
 static const struct drm_panel_funcs kd35t133_funcs = {
        .unprepare      = kd35t133_unprepare,
        .prepare        = kd35t133_prepare,
        .get_modes      = kd35t133_get_modes,
+       .get_orientation = kd35t133_get_orientation,
 };
 
 static int kd35t133_probe(struct mipi_dsi_device *dsi)
index ba30d11..5968612 100644 (file)
@@ -853,17 +853,29 @@ static int ili9881c_get_modes(struct drm_panel *panel,
        connector->display_info.width_mm = mode->width_mm;
        connector->display_info.height_mm = mode->height_mm;
 
+       /*
+        * TODO: Remove once all drm drivers call
+        * drm_connector_set_orientation_from_panel()
+        */
        drm_connector_set_panel_orientation(connector, ctx->orientation);
 
        return 1;
 }
 
+static enum drm_panel_orientation ili9881c_get_orientation(struct drm_panel *panel)
+{
+       struct ili9881c *ctx = panel_to_ili9881c(panel);
+
+       return ctx->orientation;
+}
+
 static const struct drm_panel_funcs ili9881c_funcs = {
        .prepare        = ili9881c_prepare,
        .unprepare      = ili9881c_unprepare,
        .enable         = ili9881c_enable,
        .disable        = ili9881c_disable,
        .get_modes      = ili9881c_get_modes,
+       .get_orientation = ili9881c_get_orientation,
 };
 
 static int ili9881c_dsi_probe(struct mipi_dsi_device *dsi)
index f11252f..de8758c 100644 (file)
@@ -99,15 +99,28 @@ static int panel_lvds_get_modes(struct drm_panel *panel,
        drm_display_info_set_bus_formats(&connector->display_info,
                                         &lvds->bus_format, 1);
        connector->display_info.bus_flags = lvds->bus_flags;
+
+       /*
+        * TODO: Remove once all drm drivers call
+        * drm_connector_set_orientation_from_panel()
+        */
        drm_connector_set_panel_orientation(connector, lvds->orientation);
 
        return 1;
 }
 
+static enum drm_panel_orientation panel_lvds_get_orientation(struct drm_panel *panel)
+{
+       struct panel_lvds *lvds = to_panel_lvds(panel);
+
+       return lvds->orientation;
+}
+
 static const struct drm_panel_funcs panel_lvds_funcs = {
        .unprepare = panel_lvds_unprepare,
        .prepare = panel_lvds_prepare,
        .get_modes = panel_lvds_get_modes,
+       .get_orientation = panel_lvds_get_orientation,
 };
 
 static int panel_lvds_parse_dt(struct panel_lvds *lvds)
index 3dd1041..5a8b978 100644 (file)
 #include <drm/drm_edid.h>
 #include <drm/drm_panel.h>
 
+/* T3 VCC to HPD high is max 200 ms */
+#define HPD_MAX_MS     200
+#define HPD_MAX_US     (HPD_MAX_MS * 1000)
+
 struct atana33xc20_panel {
        struct drm_panel base;
        bool prepared;
@@ -30,6 +34,7 @@ struct atana33xc20_panel {
 
        struct regulator *supply;
        struct gpio_desc *el_on3_gpio;
+       struct drm_dp_aux *aux;
 
        struct edid *edid;
 
@@ -79,7 +84,7 @@ static int atana33xc20_suspend(struct device *dev)
 static int atana33xc20_resume(struct device *dev)
 {
        struct atana33xc20_panel *p = dev_get_drvdata(dev);
-       bool hpd_asserted = false;
+       int hpd_asserted;
        int ret;
 
        /* T12 (Power off time) is min 500 ms */
@@ -90,23 +95,41 @@ static int atana33xc20_resume(struct device *dev)
                return ret;
        p->powered_on_time = ktime_get();
 
-       /*
-        * Handle HPD. Note: if HPD is hooked up to a dedicated pin on the
-        * eDP controller then "no_hpd" will be false _and_ "hpd_gpio" will be
-        * NULL. It's up to the controller driver to wait for HPD after
-        * preparing the panel in that case.
-        */
        if (p->no_hpd) {
-               /* T3 VCC to HPD high is max 200 ms */
-               msleep(200);
-       } else if (p->hpd_gpio) {
+               msleep(HPD_MAX_MS);
+               return 0;
+       }
+
+       if (p->hpd_gpio) {
                ret = readx_poll_timeout(gpiod_get_value_cansleep, p->hpd_gpio,
                                         hpd_asserted, hpd_asserted,
-                                        1000, 200000);
-               if (!hpd_asserted)
-                       dev_warn(dev, "Timeout waiting for HPD\n");
+                                        1000, HPD_MAX_US);
+               if (hpd_asserted < 0)
+                       ret = hpd_asserted;
+
+               if (ret)
+                       dev_warn(dev, "Error waiting for HPD GPIO: %d\n", ret);
+
+               return ret;
        }
 
+       if (p->aux->wait_hpd_asserted) {
+               ret = p->aux->wait_hpd_asserted(p->aux, HPD_MAX_US);
+
+               if (ret)
+                       dev_warn(dev, "Controller error waiting for HPD: %d\n", ret);
+
+               return ret;
+       }
+
+       /*
+        * Note that it's possible that no_hpd is false, hpd_gpio is
+        * NULL, and wait_hpd_asserted is NULL. This is because
+        * wait_hpd_asserted() is optional even if HPD is hooked up to
+        * a dedicated pin on the eDP controller. In this case we just
+        * assume that the controller driver will wait for HPD at the
+        * right times.
+        */
        return 0;
 }
 
@@ -263,6 +286,8 @@ static int atana33xc20_probe(struct dp_aux_ep_device *aux_ep)
                return -ENOMEM;
        dev_set_drvdata(dev, panel);
 
+       panel->aux = aux_ep->aux;
+
        panel->supply = devm_regulator_get(dev, "power");
        if (IS_ERR(panel->supply))
                return dev_err_probe(dev, PTR_ERR(panel->supply),
index 4a2e580..a1c12bd 100644 (file)
@@ -35,6 +35,7 @@
 
 #include <drm/drm_crtc.h>
 #include <drm/drm_device.h>
+#include <drm/drm_edid.h>
 #include <drm/drm_mipi_dsi.h>
 #include <drm/drm_panel.h>
 
@@ -411,7 +412,10 @@ static int panel_simple_get_modes(struct drm_panel *panel,
        /* add hard-coded panel modes */
        num += panel_simple_get_non_edid_modes(p, connector);
 
-       /* set up connector's "panel orientation" property */
+       /*
+        * TODO: Remove once all drm drivers call
+        * drm_connector_set_orientation_from_panel()
+        */
        drm_connector_set_panel_orientation(connector, p->orientation);
 
        return num;
@@ -434,12 +438,20 @@ static int panel_simple_get_timings(struct drm_panel *panel,
        return p->desc->num_timings;
 }
 
+static enum drm_panel_orientation panel_simple_get_orientation(struct drm_panel *panel)
+{
+       struct panel_simple *p = to_panel_simple(panel);
+
+       return p->orientation;
+}
+
 static const struct drm_panel_funcs panel_simple_funcs = {
        .disable = panel_simple_disable,
        .unprepare = panel_simple_unprepare,
        .prepare = panel_simple_prepare,
        .enable = panel_simple_enable,
        .get_modes = panel_simple_get_modes,
+       .get_orientation = panel_simple_get_orientation,
        .get_timings = panel_simple_get_timings,
 };
 
index 443e3b9..ccf5f02 100644 (file)
@@ -16,6 +16,7 @@
 
 #include <drm/drm_fb_cma_helper.h>
 #include <drm/drm_fourcc.h>
+#include <drm/drm_framebuffer.h>
 #include <drm/drm_gem_atomic_helper.h>
 #include <drm/drm_gem_cma_helper.h>
 #include <drm/drm_vblank.h>
index 520301b..19a4324 100644 (file)
@@ -50,6 +50,7 @@
 #include <drm/drm_drv.h>
 #include <drm/drm_fb_cma_helper.h>
 #include <drm/drm_fb_helper.h>
+#include <drm/drm_fourcc.h>
 #include <drm/drm_gem_cma_helper.h>
 #include <drm/drm_gem_framebuffer_helper.h>
 #include <drm/drm_of.h>
index bdd883f..efb01a5 100644 (file)
@@ -18,6 +18,8 @@
 #include <linux/regmap.h>
 #include <linux/vexpress.h>
 
+#include <drm/drm_fourcc.h>
+
 #include "pl111_versatile.h"
 #include "pl111_drm.h"
 
index 9a64fa4..2e89498 100644 (file)
@@ -30,6 +30,8 @@
 #include <drm/drm_drv.h>
 #include <drm/drm_atomic.h>
 #include <drm/drm_atomic_helper.h>
+#include <drm/drm_edid.h>
+#include <drm/drm_framebuffer.h>
 #include <drm/drm_gem_framebuffer_helper.h>
 #include <drm/drm_plane_helper.h>
 #include <drm/drm_probe_helper.h>
index a93de9e..3a3e127 100644 (file)
@@ -23,6 +23,7 @@
 #include <linux/iosys-map.h>
 
 #include <drm/drm_fourcc.h>
+#include <drm/drm_framebuffer.h>
 
 #include "qxl_drv.h"
 #include "qxl_object.h"
index c94e429..69f1bc0 100644 (file)
@@ -28,6 +28,7 @@
 #include <drm/drm_fb_helper.h>
 #include <drm/drm_fixed.h>
 #include <drm/drm_fourcc.h>
+#include <drm/drm_framebuffer.h>
 #include <drm/drm_vblank.h>
 #include <drm/radeon_drm.h>
 
index 455f803..4f06356 100644 (file)
@@ -29,6 +29,7 @@
 #include <drm/drm_vblank.h>
 #include <drm/radeon_drm.h>
 #include <drm/drm_fourcc.h>
+#include <drm/drm_framebuffer.h>
 
 #include "atom.h"
 #include "avivod.h"
index 2dd85ba..d4f09ec 100644 (file)
@@ -35,6 +35,7 @@
 #include <drm/drm_device.h>
 #include <drm/drm_file.h>
 #include <drm/drm_fourcc.h>
+#include <drm/drm_framebuffer.h>
 #include <drm/drm_vblank.h>
 #include <drm/radeon_drm.h>
 
index 15692cb..f508aef 100644 (file)
@@ -38,6 +38,7 @@
 #include <drm/drm_crtc_helper.h>
 #include <drm/drm_device.h>
 #include <drm/drm_file.h>
+#include <drm/drm_framebuffer.h>
 #include <drm/drm_probe_helper.h>
 #include <drm/radeon_drm.h>
 
index 57ff2b7..f12675e 100644 (file)
@@ -36,6 +36,7 @@
 #include <drm/drm_edid.h>
 #include <drm/drm_fb_helper.h>
 #include <drm/drm_fourcc.h>
+#include <drm/drm_framebuffer.h>
 #include <drm/drm_gem_framebuffer_helper.h>
 #include <drm/drm_plane_helper.h>
 #include <drm/drm_probe_helper.h>
index ca382fb..6ccea51 100644 (file)
@@ -34,6 +34,7 @@
 #include <drm/drm_crtc_helper.h>
 #include <drm/drm_fb_helper.h>
 #include <drm/drm_fourcc.h>
+#include <drm/drm_framebuffer.h>
 #include <drm/radeon_drm.h>
 
 #include "radeon.h"
index 8817fd0..6072ed5 100644 (file)
@@ -28,6 +28,7 @@
 #include <drm/drm_fb_helper.h>
 #include <drm/drm_fixed.h>
 #include <drm/drm_fourcc.h>
+#include <drm/drm_framebuffer.h>
 #include <drm/drm_vblank.h>
 #include <drm/radeon_drm.h>
 
index b87dd55..8cf87a0 100644 (file)
@@ -42,6 +42,7 @@
 #include <drm/drm_device.h>
 #include <drm/drm_vblank.h>
 #include <drm/drm_fourcc.h>
+#include <drm/drm_framebuffer.h>
 
 #include "atom.h"
 #include "radeon.h"
index 38796af..26fa9b0 100644 (file)
@@ -33,6 +33,7 @@
 #include <drm/drm_device.h>
 #include <drm/radeon_drm.h>
 #include <drm/drm_fourcc.h>
+#include <drm/drm_framebuffer.h>
 
 #include "atom.h"
 #include "avivod.h"
index 190dbb7..0f09e1e 100644 (file)
@@ -12,6 +12,7 @@
 #include <drm/drm_crtc.h>
 #include <drm/drm_device.h>
 #include <drm/drm_fb_cma_helper.h>
+#include <drm/drm_framebuffer.h>
 #include <drm/drm_gem_cma_helper.h>
 #include <drm/drm_gem_framebuffer_helper.h>
 #include <drm/drm_managed.h>
index 5c1c7bb..e98b76d 100644 (file)
@@ -9,10 +9,12 @@
 
 #include <drm/drm_atomic.h>
 #include <drm/drm_atomic_helper.h>
+#include <drm/drm_blend.h>
 #include <drm/drm_crtc.h>
 #include <drm/drm_device.h>
 #include <drm/drm_fb_cma_helper.h>
 #include <drm/drm_fourcc.h>
+#include <drm/drm_framebuffer.h>
 #include <drm/drm_gem_cma_helper.h>
 #include <drm/drm_plane_helper.h>
 
index 8eb9b2b..e778fd5 100644 (file)
@@ -9,9 +9,11 @@
 
 #include <drm/drm_atomic.h>
 #include <drm/drm_atomic_helper.h>
+#include <drm/drm_blend.h>
 #include <drm/drm_crtc.h>
 #include <drm/drm_fb_cma_helper.h>
 #include <drm/drm_fourcc.h>
+#include <drm/drm_framebuffer.h>
 #include <drm/drm_gem_atomic_helper.h>
 #include <drm/drm_gem_cma_helper.h>
 #include <drm/drm_managed.h>
index 505a905..4fd6067 100644 (file)
@@ -7,7 +7,9 @@
 
 #include <drm/drm_atomic_helper.h>
 #include <drm/drm_device.h>
+#include <drm/drm_edid.h>
 #include <drm/drm_fourcc.h>
+#include <drm/drm_framebuffer.h>
 #include <drm/drm_probe_helper.h>
 #include <drm/drm_writeback.h>
 
index c8c3612..cf2cf51 100644 (file)
@@ -4,6 +4,7 @@
  *    Zheng Yang <zhengyang@rock-chips.com>
  */
 
+#include <drm/drm_edid.h>
 #include <drm/drm_of.h>
 #include <drm/drm_probe_helper.h>
 #include <drm/drm_simple_kms_helper.h>
index 0d2cb4f..092bf86 100644 (file)
@@ -11,6 +11,7 @@
 #include <drm/drm_damage_helper.h>
 #include <drm/drm_fb_helper.h>
 #include <drm/drm_fourcc.h>
+#include <drm/drm_framebuffer.h>
 #include <drm/drm_gem_framebuffer_helper.h>
 #include <drm/drm_probe_helper.h>
 
index 74562d4..82b011d 100644 (file)
 #include <drm/drm.h>
 #include <drm/drm_atomic.h>
 #include <drm/drm_atomic_uapi.h>
+#include <drm/drm_blend.h>
 #include <drm/drm_crtc.h>
 #include <drm/drm_flip_work.h>
 #include <drm/drm_fourcc.h>
+#include <drm/drm_framebuffer.h>
 #include <drm/drm_gem_atomic_helper.h>
 #include <drm/drm_gem_framebuffer_helper.h>
 #include <drm/drm_plane_helper.h>
index 6b72894..1679169 100644 (file)
 #include <drm/drm.h>
 #include <drm/drm_atomic.h>
 #include <drm/drm_atomic_uapi.h>
+#include <drm/drm_blend.h>
 #include <drm/drm_crtc.h>
 #include <drm/drm_crtc_helper.h>
 #include <drm/drm_debugfs.h>
 #include <drm/drm_flip_work.h>
+#include <drm/drm_framebuffer.h>
 #include <drm/drm_plane_helper.h>
 #include <drm/drm_probe_helper.h>
 #include <drm/drm_vblank.h>
index 8d8d8e2..816e146 100644 (file)
@@ -6,6 +6,7 @@
 #define pr_fmt(fmt) "drm_damage_helper: " fmt
 
 #include <drm/drm_damage_helper.h>
+#include <drm/drm_framebuffer.h>
 #include <drm/drm_plane.h>
 #include <drm/drm_drv.h>
 
index b61273e..64e8938 100644 (file)
@@ -6,6 +6,7 @@
 #define pr_fmt(fmt) "drm_plane_helper: " fmt
 
 #include <drm/drm_atomic_helper.h>
+#include <drm/drm_framebuffer.h>
 #include <drm/drm_plane_helper.h>
 #include <drm/drm_modes.h>
 
index 03556db..071a929 100644 (file)
@@ -14,6 +14,7 @@
 #include <drm/drm_crtc_helper.h>
 #include <drm/drm_fb_cma_helper.h>
 #include <drm/drm_fourcc.h>
+#include <drm/drm_framebuffer.h>
 #include <drm/drm_gem_cma_helper.h>
 #include <drm/drm_plane_helper.h>
 #include <drm/drm_probe_helper.h>
index 7a866d6..68d21be 100644 (file)
@@ -10,6 +10,7 @@
 #include <drm/drm_crtc.h>
 #include <drm/drm_crtc_helper.h>
 #include <drm/drm_fb_cma_helper.h>
+#include <drm/drm_fourcc.h>
 #include <drm/drm_gem_cma_helper.h>
 #include <drm/drm_gem_framebuffer_helper.h>
 #include <drm/drm_probe_helper.h>
index cbc464f..4763ea8 100644 (file)
@@ -11,6 +11,7 @@
 #include <drm/drm_crtc_helper.h>
 #include <drm/drm_fb_cma_helper.h>
 #include <drm/drm_fourcc.h>
+#include <drm/drm_framebuffer.h>
 #include <drm/drm_gem_cma_helper.h>
 
 #include "shmob_drm_drv.h"
index 0839444..77f80b0 100644 (file)
 
 #include <drm/drm_atomic_helper.h>
 #include <drm/drm_damage_helper.h>
+#include <drm/drm_edid.h>
 #include <drm/drm_fb_cma_helper.h>
 #include <drm/drm_fb_helper.h>
 #include <drm/drm_format_helper.h>
+#include <drm/drm_framebuffer.h>
 #include <drm/drm_gem_atomic_helper.h>
 #include <drm/drm_gem_framebuffer_helper.h>
 #include <drm/drm_gem_shmem_helper.h>
index 1637203..3664089 100644 (file)
 #include <linux/workqueue.h>
 
 #include <drm/drm_atomic_helper.h>
+#include <drm/drm_blend.h>
 #include <drm/drm_crtc_helper.h>
 #include <drm/drm_fb_cma_helper.h>
+#include <drm/drm_framebuffer.h>
 #include <drm/drm_gem_cma_helper.h>
 #include <drm/drm_gem_framebuffer_helper.h>
 #include <drm/drm_plane_helper.h>
index 414c997..1e9bd42 100644 (file)
@@ -12,6 +12,7 @@
 #include <drm/drm_atomic.h>
 #include <drm/drm_device.h>
 #include <drm/drm_fb_cma_helper.h>
+#include <drm/drm_framebuffer.h>
 #include <drm/drm_gem_cma_helper.h>
 
 #include "sti_compositor.h"
index b58415f..a1f78d5 100644 (file)
@@ -13,6 +13,7 @@
 #include <drm/drm_device.h>
 #include <drm/drm_fb_cma_helper.h>
 #include <drm/drm_fourcc.h>
+#include <drm/drm_framebuffer.h>
 #include <drm/drm_gem_cma_helper.h>
 
 #include "sti_compositor.h"
index 2201a50..b5ae5d2 100644 (file)
@@ -17,6 +17,7 @@
 #include <drm/drm_device.h>
 #include <drm/drm_fb_cma_helper.h>
 #include <drm/drm_fourcc.h>
+#include <drm/drm_framebuffer.h>
 #include <drm/drm_gem_cma_helper.h>
 
 #include "sti_compositor.h"
index 173409c..c74b524 100644 (file)
@@ -8,8 +8,10 @@
 
 #include <linux/types.h>
 
+#include <drm/drm_blend.h>
 #include <drm/drm_fb_cma_helper.h>
 #include <drm/drm_fourcc.h>
+#include <drm/drm_framebuffer.h>
 #include <drm/drm_gem_cma_helper.h>
 
 #include "sti_compositor.h"
index 6bd45df..76230f7 100644 (file)
 
 #include <drm/drm_atomic.h>
 #include <drm/drm_atomic_helper.h>
+#include <drm/drm_blend.h>
 #include <drm/drm_bridge.h>
 #include <drm/drm_device.h>
+#include <drm/drm_edid.h>
 #include <drm/drm_fb_cma_helper.h>
 #include <drm/drm_fourcc.h>
+#include <drm/drm_framebuffer.h>
 #include <drm/drm_gem_atomic_helper.h>
 #include <drm/drm_gem_cma_helper.h>
 #include <drm/drm_of.h>
index decd95a..287e8c4 100644 (file)
 
 #include <drm/drm_atomic.h>
 #include <drm/drm_atomic_helper.h>
+#include <drm/drm_blend.h>
 #include <drm/drm_crtc.h>
 #include <drm/drm_fb_cma_helper.h>
 #include <drm/drm_fourcc.h>
+#include <drm/drm_framebuffer.h>
 #include <drm/drm_gem_cma_helper.h>
 #include <drm/drm_plane_helper.h>
 #include <drm/drm_probe_helper.h>
index 6825ef4..260136d 100644 (file)
@@ -8,6 +8,7 @@
 
 #include <drm/drm_atomic.h>
 #include <drm/drm_atomic_helper.h>
+#include <drm/drm_blend.h>
 #include <drm/drm_gem_framebuffer_helper.h>
 
 #include "sun4i_drv.h"
index 6d43080..a0920e1 100644 (file)
@@ -8,6 +8,7 @@
 
 #include <drm/drm_atomic.h>
 #include <drm/drm_atomic_helper.h>
+#include <drm/drm_blend.h>
 #include <drm/drm_gem_atomic_helper.h>
 #include <drm/drm_plane_helper.h>
 
index 875a115..648b38a 100644 (file)
@@ -17,6 +17,7 @@
 #include <drm/drm_atomic_helper.h>
 #include <drm/drm_crtc.h>
 #include <drm/drm_fb_cma_helper.h>
+#include <drm/drm_framebuffer.h>
 #include <drm/drm_gem_cma_helper.h>
 #include <drm/drm_plane_helper.h>
 #include <drm/drm_probe_helper.h>
index 4632dea..36da962 100644 (file)
 
 #include <drm/drm_atomic.h>
 #include <drm/drm_atomic_helper.h>
+#include <drm/drm_blend.h>
 #include <drm/drm_crtc.h>
 #include <drm/drm_fb_cma_helper.h>
 #include <drm/drm_fourcc.h>
+#include <drm/drm_framebuffer.h>
 #include <drm/drm_gem_atomic_helper.h>
 #include <drm/drm_gem_cma_helper.h>
 #include <drm/drm_plane_helper.h>
index f7d0b08..1fee649 100644 (file)
@@ -5,8 +5,10 @@
 
 #include <drm/drm_atomic.h>
 #include <drm/drm_atomic_helper.h>
+#include <drm/drm_blend.h>
 #include <drm/drm_crtc.h>
 #include <drm/drm_fb_cma_helper.h>
+#include <drm/drm_framebuffer.h>
 #include <drm/drm_gem_atomic_helper.h>
 #include <drm/drm_gem_cma_helper.h>
 #include <drm/drm_plane_helper.h>
index c6951cf..a2a731e 100644 (file)
 
 #include <drm/drm_atomic.h>
 #include <drm/drm_atomic_helper.h>
+#include <drm/drm_blend.h>
 #include <drm/drm_debugfs.h>
 #include <drm/drm_fourcc.h>
+#include <drm/drm_framebuffer.h>
 #include <drm/drm_plane_helper.h>
 #include <drm/drm_vblank.h>
 
index 9464f52..4cdc8fa 100644 (file)
@@ -18,6 +18,7 @@
 #include <drm/drm_debugfs.h>
 #include <drm/drm_drv.h>
 #include <drm/drm_fourcc.h>
+#include <drm/drm_framebuffer.h>
 #include <drm/drm_ioctl.h>
 #include <drm/drm_prime.h>
 #include <drm/drm_vblank.h>
index c04dda8..ed828de 100644 (file)
@@ -10,6 +10,7 @@
 #include <linux/console.h>
 
 #include <drm/drm_fourcc.h>
+#include <drm/drm_framebuffer.h>
 #include <drm/drm_gem_framebuffer_helper.h>
 #include <drm/drm_modeset_helper.h>
 
index b8d3174..61729ea 100644 (file)
@@ -16,7 +16,9 @@
 
 #include <drm/drm_atomic.h>
 #include <drm/drm_atomic_helper.h>
+#include <drm/drm_blend.h>
 #include <drm/drm_fourcc.h>
+#include <drm/drm_framebuffer.h>
 #include <drm/drm_probe_helper.h>
 
 #include "drm.h"
index e0e6938..d049b21 100644 (file)
@@ -9,6 +9,7 @@
 #include <drm/drm_atomic.h>
 #include <drm/drm_atomic_helper.h>
 #include <drm/drm_fourcc.h>
+#include <drm/drm_framebuffer.h>
 #include <drm/drm_gem_atomic_helper.h>
 #include <drm/drm_plane_helper.h>
 
index dae4785..73f591c 100644 (file)
 #include <linux/regmap.h>
 #include <linux/sys_soc.h>
 
+#include <drm/drm_blend.h>
 #include <drm/drm_fourcc.h>
 #include <drm/drm_fb_cma_helper.h>
+#include <drm/drm_framebuffer.h>
 #include <drm/drm_gem_cma_helper.h>
 #include <drm/drm_panel.h>
 
index 217415e..68a85a9 100644 (file)
@@ -6,9 +6,11 @@
 
 #include <drm/drm_atomic.h>
 #include <drm/drm_atomic_helper.h>
+#include <drm/drm_blend.h>
 #include <drm/drm_crtc.h>
 #include <drm/drm_crtc_helper.h>
 #include <drm/drm_fourcc.h>
+#include <drm/drm_framebuffer.h>
 #include <drm/drm_fb_cma_helper.h>
 #include <drm/drm_gem_atomic_helper.h>
 
index 0dae7d5..509fbae 100644 (file)
@@ -14,6 +14,7 @@
 #include <drm/drm_crtc.h>
 #include <drm/drm_fb_cma_helper.h>
 #include <drm/drm_fourcc.h>
+#include <drm/drm_framebuffer.h>
 #include <drm/drm_gem_cma_helper.h>
 #include <drm/drm_modeset_helper_vtables.h>
 #include <drm/drm_print.h>
index 74a5c88..9601365 100644 (file)
@@ -8,6 +8,7 @@
 #include <drm/drm_plane_helper.h>
 #include <drm/drm_atomic_helper.h>
 #include <drm/drm_fourcc.h>
+#include <drm/drm_framebuffer.h>
 
 #include "tilcdc_drv.h"
 
index f0fa3b1..7461cb4 100644 (file)
 #include <drm/drm_debugfs.h>
 #include <drm/drm_device.h>
 #include <drm/drm_drv.h>
+#include <drm/drm_edid.h>
 #include <drm/drm_fb_cma_helper.h>
 #include <drm/drm_fb_helper.h>
 #include <drm/drm_fourcc.h>
+#include <drm/drm_framebuffer.h>
 #include <drm/drm_gem_cma_helper.h>
 #include <drm/drm_gem_framebuffer_helper.h>
 #include <drm/drm_module.h>
index 4f8bf86..82364a0 100644 (file)
@@ -6,8 +6,10 @@
 #include <drm/drm_aperture.h>
 #include <drm/drm_atomic_helper.h>
 #include <drm/drm_drv.h>
+#include <drm/drm_edid.h>
 #include <drm/drm_fb_helper.h>
 #include <drm/drm_fourcc.h>
+#include <drm/drm_framebuffer.h>
 #include <drm/drm_gem_framebuffer_helper.h>
 #include <drm/drm_gem_vram_helper.h>
 #include <drm/drm_managed.h>
index c8e7918..c4f5bee 100644 (file)
 #include <drm/drm_connector.h>
 #include <drm/drm_damage_helper.h>
 #include <drm/drm_drv.h>
+#include <drm/drm_edid.h>
 #include <drm/drm_fb_helper.h>
 #include <drm/drm_file.h>
 #include <drm/drm_format_helper.h>
 #include <drm/drm_fourcc.h>
+#include <drm/drm_framebuffer.h>
 #include <drm/drm_gem_atomic_helper.h>
 #include <drm/drm_gem_framebuffer_helper.h>
 #include <drm/drm_gem_shmem_helper.h>
index 648e585..7441d99 100644 (file)
 #include <drm/drm_connector.h>
 #include <drm/drm_damage_helper.h>
 #include <drm/drm_drv.h>
+#include <drm/drm_edid.h>
 #include <drm/drm_fb_helper.h>
 #include <drm/drm_file.h>
 #include <drm/drm_format_helper.h>
 #include <drm/drm_fourcc.h>
+#include <drm/drm_framebuffer.h>
 #include <drm/drm_gem_atomic_helper.h>
 #include <drm/drm_gem_framebuffer_helper.h>
 #include <drm/drm_gem_shmem_helper.h>
index cc92eb9..8d686ee 100644 (file)
@@ -22,6 +22,7 @@
 #include <drm/drm_fb_cma_helper.h>
 #include <drm/drm_fb_helper.h>
 #include <drm/drm_fourcc.h>
+#include <drm/drm_framebuffer.h>
 #include <drm/drm_gem_atomic_helper.h>
 #include <drm/drm_gem_cma_helper.h>
 #include <drm/drm_managed.h>
index a096fb8..013790c 100644 (file)
@@ -28,6 +28,7 @@
 #include <drm/drm_fb_cma_helper.h>
 #include <drm/drm_fb_helper.h>
 #include <drm/drm_format_helper.h>
+#include <drm/drm_framebuffer.h>
 #include <drm/drm_gem_atomic_helper.h>
 #include <drm/drm_gem_cma_helper.h>
 #include <drm/drm_gem_framebuffer_helper.h>
index 3f38faa..8eddb02 100644 (file)
@@ -18,6 +18,7 @@
 #include <drm/drm_fb_cma_helper.h>
 #include <drm/drm_fb_helper.h>
 #include <drm/drm_format_helper.h>
+#include <drm/drm_framebuffer.h>
 #include <drm/drm_gem_atomic_helper.h>
 #include <drm/drm_gem_cma_helper.h>
 #include <drm/drm_gem_framebuffer_helper.h>
index 17b8c8d..771bad8 100644 (file)
@@ -17,6 +17,7 @@
 
 #include <drm/drm_fb_cma_helper.h>
 #include <drm/drm_fourcc.h>
+#include <drm/drm_framebuffer.h>
 #include <drm/drm_gem_atomic_helper.h>
 #include <drm/drm_gem_cma_helper.h>
 #include <drm/drm_panel.h>
index 318fdb3..fade4c7 100644 (file)
@@ -8,6 +8,7 @@
  */
 
 #include <drm/drm_atomic_state_helper.h>
+#include <drm/drm_edid.h>
 #include <drm/drm_crtc_helper.h>
 #include <drm/drm_probe_helper.h>
 
index 4017b0a..fa0d73c 100644 (file)
 
 #include <drm/drm_atomic.h>
 #include <drm/drm_atomic_helper.h>
+#include <drm/drm_edid.h>
 #include <drm/drm_fb_helper.h>
 #include <drm/drm_fourcc.h>
+#include <drm/drm_framebuffer.h>
 #include <drm/drm_gem_atomic_helper.h>
 #include <drm/drm_gem_framebuffer_helper.h>
 #include <drm/drm_plane_helper.h>
index 49c0f2a..3700d73 100644 (file)
@@ -18,6 +18,8 @@
 
 #include <linux/dma-buf.h>
 
+#include <drm/drm_fourcc.h>
+
 #include "vc4_drv.h"
 #include "uapi/drm/vc4_drm.h"
 
index 59b20c8..0b80d84 100644 (file)
@@ -38,6 +38,7 @@
 #include <drm/drm_atomic_helper.h>
 #include <drm/drm_atomic_uapi.h>
 #include <drm/drm_fb_cma_helper.h>
+#include <drm/drm_framebuffer.h>
 #include <drm/drm_print.h>
 #include <drm/drm_probe_helper.h>
 #include <drm/drm_vblank.h>
index c169bd7..a1819df 100644 (file)
@@ -16,6 +16,7 @@
 #include <drm/drm_atomic.h>
 #include <drm/drm_atomic_helper.h>
 #include <drm/drm_crtc.h>
+#include <drm/drm_fourcc.h>
 #include <drm/drm_gem_framebuffer_helper.h>
 #include <drm/drm_plane_helper.h>
 #include <drm/drm_probe_helper.h>
index b3438f4..3110776 100644 (file)
 #include <drm/drm_atomic.h>
 #include <drm/drm_atomic_helper.h>
 #include <drm/drm_atomic_uapi.h>
+#include <drm/drm_blend.h>
 #include <drm/drm_fb_cma_helper.h>
 #include <drm/drm_fourcc.h>
+#include <drm/drm_framebuffer.h>
 #include <drm/drm_gem_atomic_helper.h>
 #include <drm/drm_plane_helper.h>
 
index a2b5cbb..f0290fa 100644 (file)
  * output line.
  */
 # define SCALER_DISPSTAT_ESLINE(x)             BIT(10 + ((x) * 8))
-/* Set when the the downstream tries to read from the display FIFO
+/* Set when the downstream tries to read from the display FIFO
  * while it's empty.
  */
 # define SCALER_DISPSTAT_EUFLOW(x)             BIT(9 + ((x) * 8))
index 3579d48..d20b0bc 100644 (file)
@@ -18,6 +18,7 @@
 #include <drm/drm_edid.h>
 #include <drm/drm_fb_cma_helper.h>
 #include <drm/drm_fourcc.h>
+#include <drm/drm_framebuffer.h>
 #include <drm/drm_panel.h>
 #include <drm/drm_probe_helper.h>
 #include <drm/drm_vblank.h>
index f73352e..5c7f198 100644 (file)
@@ -27,6 +27,7 @@
 
 #include <drm/drm_atomic_helper.h>
 #include <drm/drm_damage_helper.h>
+#include <drm/drm_edid.h>
 #include <drm/drm_fourcc.h>
 #include <drm/drm_gem_framebuffer_helper.h>
 #include <drm/drm_probe_helper.h>
index 0a194aa..f80664c 100644 (file)
@@ -37,6 +37,7 @@
 #include <drm/drm_encoder.h>
 #include <drm/drm_fb_helper.h>
 #include <drm/drm_fourcc.h>
+#include <drm/drm_framebuffer.h>
 #include <drm/drm_gem.h>
 #include <drm/drm_gem_shmem_helper.h>
 #include <drm/drm_ioctl.h>
index 7c052ef..b7529b2 100644 (file)
@@ -31,6 +31,8 @@
 #include <linux/virtio_config.h>
 #include <linux/virtio_ring.h>
 
+#include <drm/drm_edid.h>
+
 #include "virtgpu_drv.h"
 #include "virtgpu_trace.h"
 
index 91e63b1..1d60654 100644 (file)
@@ -6,6 +6,7 @@
 #include <linux/hrtimer.h>
 
 #include <drm/drm.h>
+#include <drm/drm_framebuffer.h>
 #include <drm/drm_gem.h>
 #include <drm/drm_gem_atomic_helper.h>
 #include <drm/drm_encoder.h>
index ba0e82a..9918571 100644 (file)
@@ -2,6 +2,7 @@
 
 #include "vkms_drv.h"
 #include <drm/drm_atomic_helper.h>
+#include <drm/drm_edid.h>
 #include <drm/drm_probe_helper.h>
 #include <drm/drm_simple_kms_helper.h>
 
index 0a31522..3b3c1e7 100644 (file)
@@ -3,6 +3,7 @@
 #include <linux/iosys-map.h>
 
 #include <drm/drm_atomic.h>
+#include <drm/drm_edid.h>
 #include <drm/drm_fourcc.h>
 #include <drm/drm_writeback.h>
 #include <drm/drm_probe_helper.h>
index 1d1c8b8..7046dfd 100644 (file)
@@ -29,6 +29,7 @@
 #define VMWGFX_KMS_H_
 
 #include <drm/drm_encoder.h>
+#include <drm/drm_framebuffer.h>
 #include <drm/drm_probe_helper.h>
 
 #include "vmwgfx_drv.h"
index 44f1f70..a1ba6d3 100644 (file)
@@ -10,6 +10,7 @@
 
 #include <drm/drm_atomic_helper.h>
 #include <drm/drm_drv.h>
+#include <drm/drm_fourcc.h>
 #include <drm/drm_probe_helper.h>
 
 #include <video/videomode.h>
index cfda744..dfa78a4 100644 (file)
@@ -12,6 +12,7 @@
 #include <drm/drm_atomic_helper.h>
 #include <drm/drm_drv.h>
 #include <drm/drm_fourcc.h>
+#include <drm/drm_framebuffer.h>
 #include <drm/drm_gem.h>
 #include <drm/drm_gem_atomic_helper.h>
 #include <drm/drm_gem_framebuffer_helper.h>
index 11c409c..cc32aa8 100644 (file)
@@ -12,6 +12,7 @@
 #include <drm/drm_atomic.h>
 #include <drm/drm_atomic_helper.h>
 #include <drm/drm_atomic_uapi.h>
+#include <drm/drm_blend.h>
 #include <drm/drm_crtc.h>
 #include <drm/drm_device.h>
 #include <drm/drm_fb_cma_helper.h>
index d765bbd..1be8aa9 100644 (file)
@@ -125,8 +125,8 @@ static int logo_lines;
    enums.  */
 static int logo_shown = FBCON_LOGO_CANSHOW;
 /* console mappings */
-static int first_fb_vc;
-static int last_fb_vc = MAX_NR_CONSOLES - 1;
+static unsigned int first_fb_vc;
+static unsigned int last_fb_vc = MAX_NR_CONSOLES - 1;
 static int fbcon_is_default = 1; 
 static int primary_device = -1;
 static int fbcon_has_console_bind;
@@ -440,10 +440,12 @@ static int __init fb_console_setup(char *this_opt)
                        options += 3;
                        if (*options)
                                first_fb_vc = simple_strtoul(options, &options, 10) - 1;
-                       if (first_fb_vc < 0)
+                       if (first_fb_vc >= MAX_NR_CONSOLES)
                                first_fb_vc = 0;
                        if (*options++ == '-')
                                last_fb_vc = simple_strtoul(options, &options, 10) - 1;
+                       if (last_fb_vc < first_fb_vc || last_fb_vc >= MAX_NR_CONSOLES)
+                               last_fb_vc = MAX_NR_CONSOLES - 1;
                        fbcon_is_default = 0; 
                        continue;
                }
@@ -1758,8 +1760,6 @@ static bool fbcon_scroll(struct vc_data *vc, unsigned int t, unsigned int b,
        case SM_UP:
                if (count > vc->vc_rows)        /* Maximum realistic size */
                        count = vc->vc_rows;
-               if (logo_shown >= 0)
-                       goto redraw_up;
                switch (fb_scrollmode(p)) {
                case SCROLL_MOVE:
                        fbcon_redraw_blit(vc, info, p, t, b - t - count,
@@ -1848,8 +1848,6 @@ static bool fbcon_scroll(struct vc_data *vc, unsigned int t, unsigned int b,
        case SM_DOWN:
                if (count > vc->vc_rows)        /* Maximum realistic size */
                        count = vc->vc_rows;
-               if (logo_shown >= 0)
-                       goto redraw_down;
                switch (fb_scrollmode(p)) {
                case SCROLL_MOVE:
                        fbcon_redraw_blit(vc, info, p, b - 1, b - t - count,
index c5f8f45..db0fe9f 100644 (file)
@@ -389,6 +389,36 @@ struct drm_dp_aux {
        ssize_t (*transfer)(struct drm_dp_aux *aux,
                            struct drm_dp_aux_msg *msg);
 
+       /**
+        * @wait_hpd_asserted: wait for HPD to be asserted
+        *
+        * This is mainly useful for eDP panels drivers to wait for an eDP
+        * panel to finish powering on. This is an optional function.
+        *
+        * This function will efficiently wait for the HPD signal to be
+        * asserted. The `wait_us` parameter that is passed in says that we
+        * know that the HPD signal is expected to be asserted within `wait_us`
+        * microseconds. This function could wait for longer than `wait_us` if
+        * the logic in the DP controller has a long debouncing time. The
+        * important thing is that if this function returns success that the
+        * DP controller is ready to send AUX transactions.
+        *
+        * This function returns 0 if HPD was asserted or -ETIMEDOUT if time
+        * expired and HPD wasn't asserted. This function should not print
+        * timeout errors to the log.
+        *
+        * The semantics of this function are designed to match the
+        * readx_poll_timeout() function. That means a `wait_us` of 0 means
+        * to wait forever. Like readx_poll_timeout(), this function may sleep.
+        *
+        * NOTE: this function specifically reports the state of the HPD pin
+        * that's associated with the DP AUX channel. This is different from
+        * the HPD concept in much of the rest of DRM which is more about
+        * physical presence of a display. For eDP, for instance, a display is
+        * assumed always present even if the HPD pin is deasserted.
+        */
+       int (*wait_hpd_asserted)(struct drm_dp_aux *aux, unsigned long wait_us);
+
        /**
         * @i2c_nack_count: Counts I2C NACKs, used for DP validation.
         */
index 42aec86..d434ab4 100644 (file)
@@ -918,16 +918,30 @@ void drm_bridge_hpd_notify(struct drm_bridge *bridge,
                           enum drm_connector_status status);
 
 #ifdef CONFIG_DRM_PANEL_BRIDGE
+bool drm_bridge_is_panel(const struct drm_bridge *bridge);
 struct drm_bridge *drm_panel_bridge_add(struct drm_panel *panel);
 struct drm_bridge *drm_panel_bridge_add_typed(struct drm_panel *panel,
                                              u32 connector_type);
 void drm_panel_bridge_remove(struct drm_bridge *bridge);
+int drm_panel_bridge_set_orientation(struct drm_connector *connector,
+                                    struct drm_bridge *bridge);
 struct drm_bridge *devm_drm_panel_bridge_add(struct device *dev,
                                             struct drm_panel *panel);
 struct drm_bridge *devm_drm_panel_bridge_add_typed(struct device *dev,
                                                   struct drm_panel *panel,
                                                   u32 connector_type);
 struct drm_connector *drm_panel_bridge_connector(struct drm_bridge *bridge);
+#else
+static inline bool drm_bridge_is_panel(const struct drm_bridge *bridge)
+{
+       return false;
+}
+
+static inline int drm_panel_bridge_set_orientation(struct drm_connector *connector,
+                                                  struct drm_bridge *bridge)
+{
+       return -EINVAL;
+}
 #endif
 
 #if defined(CONFIG_OF) && defined(CONFIG_DRM_PANEL_BRIDGE)
index 3ac4bf8..94b422b 100644 (file)
@@ -38,6 +38,7 @@ struct drm_modeset_acquire_ctx;
 struct drm_device;
 struct drm_crtc;
 struct drm_encoder;
+struct drm_panel;
 struct drm_property;
 struct drm_property_blob;
 struct drm_printer;
@@ -1802,6 +1803,9 @@ int drm_connector_set_panel_orientation_with_quirk(
        struct drm_connector *connector,
        enum drm_panel_orientation panel_orientation,
        int width, int height);
+int drm_connector_set_orientation_from_panel(
+       struct drm_connector *connector,
+       struct drm_panel *panel);
 int drm_connector_attach_max_bpc_property(struct drm_connector *connector,
                                          int min, int max);
 void drm_connector_create_privacy_screen_properties(struct drm_connector *conn);
index a70baea..c404b6e 100644 (file)
 #include <drm/drm_modeset_lock.h>
 #include <drm/drm_rect.h>
 #include <drm/drm_mode_object.h>
-#include <drm/drm_framebuffer.h>
 #include <drm/drm_modes.h>
-#include <drm/drm_connector.h>
 #include <drm/drm_device.h>
 #include <drm/drm_property.h>
-#include <drm/drm_edid.h>
 #include <drm/drm_plane.h>
-#include <drm/drm_blend.h>
 #include <drm/drm_color_mgmt.h>
 #include <drm/drm_debugfs_crc.h>
 #include <drm/drm_mode_config.h>
 
+struct drm_connector;
 struct drm_device;
+struct drm_framebuffer;
 struct drm_mode_set;
 struct drm_file;
 struct drm_clip_rect;
index d279ee4..3a27112 100644 (file)
@@ -116,6 +116,15 @@ struct drm_panel_funcs {
        int (*get_modes)(struct drm_panel *panel,
                         struct drm_connector *connector);
 
+       /**
+        * @get_orientation:
+        *
+        * Return the panel orientation set by device tree or EDID.
+        *
+        * This function is optional.
+        */
+       enum drm_panel_orientation (*get_orientation)(struct drm_panel *panel);
+
        /**
         * @get_timings:
         *
index 0dfc11e..ec3323d 100644 (file)
@@ -34,7 +34,7 @@
 
 #define MEDIA_BUS_FMT_FIXED                    0x0001
 
-/* RGB - next is       0x101e */
+/* RGB - next is       0x1022 */
 #define MEDIA_BUS_FMT_RGB444_1X12              0x1016
 #define MEDIA_BUS_FMT_RGB444_2X8_PADHI_BE      0x1001
 #define MEDIA_BUS_FMT_RGB444_2X8_PADHI_LE      0x1002
 #define MEDIA_BUS_FMT_RGB888_3X8_DELTA         0x101d
 #define MEDIA_BUS_FMT_RGB888_1X7X4_SPWG                0x1011
 #define MEDIA_BUS_FMT_RGB888_1X7X4_JEIDA       0x1012
+#define MEDIA_BUS_FMT_RGB666_1X30_CPADLO       0x101e
+#define MEDIA_BUS_FMT_RGB888_1X30_CPADLO       0x101f
 #define MEDIA_BUS_FMT_ARGB8888_1X32            0x100d
 #define MEDIA_BUS_FMT_RGB888_1X32_PADHI                0x100f
 #define MEDIA_BUS_FMT_RGB101010_1X30           0x1018
+#define MEDIA_BUS_FMT_RGB666_1X36_CPADLO       0x1020
+#define MEDIA_BUS_FMT_RGB888_1X36_CPADLO       0x1021
 #define MEDIA_BUS_FMT_RGB121212_1X36           0x1019
 #define MEDIA_BUS_FMT_RGB161616_1X48           0x101a