diff options
author | Dave Airlie <airlied@redhat.com> | 2017-04-11 07:41:10 +1000 |
---|---|---|
committer | Dave Airlie <airlied@redhat.com> | 2017-04-11 07:41:10 +1000 |
commit | df45eaca51f4826f328859e5b203fbeab6fcf2a3 (patch) | |
tree | b5fb9f273f4dd914fce25bbe937737b31ba7cd70 | |
parent | b769fefb68cd70385d68220ae341e5a10723fbc0 (diff) | |
parent | c98cdff94a6a7877923dec1329c2b76d6247d076 (diff) | |
download | op-kernel-dev-df45eaca51f4826f328859e5b203fbeab6fcf2a3.zip op-kernel-dev-df45eaca51f4826f328859e5b203fbeab6fcf2a3.tar.gz |
Merge tag 'drm-misc-next-2017-04-07' of git://anongit.freedesktop.org/git/drm-misc into drm-next
Last drm-misc-next pull req for 4.12
Core changes:
- fb_helper checkpatch cleanup and simplified _add_one_connector() (Thierry)
- drm_ioctl and drm_sysfs improved/gained documentation (Daniel)
- [ABI] Repurpose reserved field in drm_event_vblank for crtc_id (Ander)
- Plumb acquire ctx through legacy paths to avoid lock_all and legacy_backoff
(Daniel)
- Add connector_atomic_check to check conn constraints on modeset (Maarten)
- Add drm_of_find_panel_or_bridge to remove boilerplate in drivers (Rob)
Driver changes:
- meson moved to drm-misc (Neil)
- Added support for Amlogic GX SoCs in dw-hdmi (Neil)
- Rockchip unbind actually cleans up the things bind initializes (Jeffy)
- A couple misc fixes in virtio, dw-hdmi
NOTE: this also includes a backmerge of drm-next as well rc5 (we needed vmwgfx
as well as the new synopsys media formats)
* tag 'drm-misc-next-2017-04-07' of git://anongit.freedesktop.org/git/drm-misc: (77 commits)
Revert "drm: Don't allow interruptions when opening debugfs/crc"
drm: Only take cursor locks when the cursor plane exists
drm/vmwgfx: Fix fbdev emulation using legacy functions
drm/rockchip: Shutdown all crtcs when unbinding drm
drm/rockchip: Reorder drm bind/unbind sequence
drm/rockchip: analogix_dp: Disable clock when unbinding
drm/rockchip: vop: Unprepare clocks when unbinding
drm/rockchip: vop: Enable pm domain before vop_initial
drm/rockchip: cdn-dp: Don't unregister audio dev when unbinding
drm/rockchip: cdn-dp: Don't try to release firmware when not loaded
drm: bridge: analogix: Destroy connector & encoder when unbinding
drm: bridge: analogix: Disable clock when unbinding
drm: bridge: analogix: Unregister dp aux when unbinding
drm: bridge: analogix: Detach panel when unbinding analogix dp
drm: Don't allow interruptions when opening debugfs/crc
drm/virtio: don't leak bo on drm_gem_object_init failure
drm: bridge: dw-hdmi: fix input format/encoding from plat_data
drm: omap: use common OF graph helpers
drm: convert drivers to use drm_of_find_panel_or_bridge
drm: convert drivers to use of_graph_get_remote_node
...
133 files changed, 6057 insertions, 1990 deletions
diff --git a/Documentation/devicetree/bindings/display/amlogic,meson-dw-hdmi.txt b/Documentation/devicetree/bindings/display/amlogic,meson-dw-hdmi.txt new file mode 100644 index 0000000..7f040ed --- /dev/null +++ b/Documentation/devicetree/bindings/display/amlogic,meson-dw-hdmi.txt @@ -0,0 +1,111 @@ +Amlogic specific extensions to the Synopsys Designware HDMI Controller +====================================================================== + +The Amlogic Meson Synopsys Designware Integration is composed of : +- A Synopsys DesignWare HDMI Controller IP +- A TOP control block controlling the Clocks and PHY +- A custom HDMI PHY in order to convert video to TMDS signal + ___________________________________ +| HDMI TOP |<= HPD +|___________________________________| +| | | +| Synopsys HDMI | HDMI PHY |=> TMDS +| Controller |________________| +|___________________________________|<=> DDC + +The HDMI TOP block only supports HPD sensing. +The Synopsys HDMI Controller interrupt is routed through the +TOP Block interrupt. +Communication to the TOP Block and the Synopsys HDMI Controller is done +via a pair of dedicated addr+read/write registers. +The HDMI PHY is configured by registers in the HHI register block. + +Pixel data arrives in 4:4:4 format from the VENC block and the VPU HDMI mux +selects either the ENCI encoder for the 576i or 480i formats or the ENCP +encoder for all the other formats including interlaced HD formats. + +The VENC uses a DVI encoder on top of the ENCI or ENCP encoders to generate +DVI timings for the HDMI controller. + +Amlogic Meson GXBB, GXL and GXM SoCs families embeds the Synopsys DesignWare +HDMI TX IP version 2.01a with HDCP and I2C & S/PDIF +audio source interfaces. + +Required properties: +- compatible: value should be different for each SoC family as : + - GXBB (S905) : "amlogic,meson-gxbb-dw-hdmi" + - GXL (S905X, S905D) : "amlogic,meson-gxl-dw-hdmi" + - GXM (S912) : "amlogic,meson-gxm-dw-hdmi" + followed by the common "amlogic,meson-gx-dw-hdmi" +- reg: Physical base address and length of the controller's registers. +- interrupts: The HDMI interrupt number +- clocks, clock-names : must have the phandles to the HDMI iahb and isfr clocks, + and the Amlogic Meson venci clocks as described in + Documentation/devicetree/bindings/clock/clock-bindings.txt, + the clocks are soc specific, the clock-names should be "iahb", "isfr", "venci" +- resets, resets-names: must have the phandles to the HDMI apb, glue and phy + resets as described in : + Documentation/devicetree/bindings/reset/reset.txt, + the reset-names should be "hdmitx_apb", "hdmitx", "hdmitx_phy" + +Required nodes: + +The connections to the HDMI ports are modeled using the OF graph +bindings specified in Documentation/devicetree/bindings/graph.txt. + +The following table lists for each supported model the port number +corresponding to each HDMI output and input. + + Port 0 Port 1 +----------------------------------------- + S905 (GXBB) VENC Input TMDS Output + S905X (GXL) VENC Input TMDS Output + S905D (GXL) VENC Input TMDS Output + S912 (GXM) VENC Input TMDS Output + +Example: + +hdmi-connector { + compatible = "hdmi-connector"; + type = "a"; + + port { + hdmi_connector_in: endpoint { + remote-endpoint = <&hdmi_tx_tmds_out>; + }; + }; +}; + +hdmi_tx: hdmi-tx@c883a000 { + compatible = "amlogic,meson-gxbb-dw-hdmi", "amlogic,meson-gx-dw-hdmi"; + reg = <0x0 0xc883a000 0x0 0x1c>; + interrupts = <GIC_SPI 57 IRQ_TYPE_EDGE_RISING>; + resets = <&reset RESET_HDMITX_CAPB3>, + <&reset RESET_HDMI_SYSTEM_RESET>, + <&reset RESET_HDMI_TX>; + reset-names = "hdmitx_apb", "hdmitx", "hdmitx_phy"; + clocks = <&clkc CLKID_HDMI_PCLK>, + <&clkc CLKID_CLK81>, + <&clkc CLKID_GCLK_VENCI_INT0>; + clock-names = "isfr", "iahb", "venci"; + #address-cells = <1>; + #size-cells = <0>; + + /* VPU VENC Input */ + hdmi_tx_venc_port: port@0 { + reg = <0>; + + hdmi_tx_in: endpoint { + remote-endpoint = <&hdmi_tx_out>; + }; + }; + + /* TMDS Output */ + hdmi_tx_tmds_port: port@1 { + reg = <1>; + + hdmi_tx_tmds_out: endpoint { + remote-endpoint = <&hdmi_connector_in>; + }; + }; +}; diff --git a/Documentation/devicetree/bindings/display/rockchip/dw_mipi_dsi_rockchip.txt b/Documentation/devicetree/bindings/display/rockchip/dw_mipi_dsi_rockchip.txt index 1d722f5..543b074 100644 --- a/Documentation/devicetree/bindings/display/rockchip/dw_mipi_dsi_rockchip.txt +++ b/Documentation/devicetree/bindings/display/rockchip/dw_mipi_dsi_rockchip.txt @@ -17,9 +17,12 @@ Required properties: Optional properties: - power-domains: a phandle to mipi dsi power domain node. +- resets: list of phandle + reset specifier pairs, as described in [3]. +- reset-names: string reset name, must be "apb". [1] Documentation/devicetree/bindings/clock/clock-bindings.txt [2] Documentation/devicetree/bindings/media/video-interfaces.txt +[3] Documentation/devicetree/bindings/reset/reset.txt Example: mipi_dsi: mipi@ff960000 { @@ -30,6 +33,8 @@ Example: interrupts = <GIC_SPI 83 IRQ_TYPE_LEVEL_HIGH>; clocks = <&cru SCLK_MIPI_24M>, <&cru PCLK_MIPI_DSI0>; clock-names = "ref", "pclk"; + resets = <&cru SRST_MIPIDSI0>; + reset-names = "apb"; rockchip,grf = <&grf>; status = "okay"; diff --git a/Documentation/gpu/bridge/dw-hdmi.rst b/Documentation/gpu/bridge/dw-hdmi.rst new file mode 100644 index 0000000..486faad --- /dev/null +++ b/Documentation/gpu/bridge/dw-hdmi.rst @@ -0,0 +1,15 @@ +======================================================= + drm/bridge/dw-hdmi Synopsys DesignWare HDMI Controller +======================================================= + +Synopsys DesignWare HDMI Controller +=================================== + +This section covers everything related to the Synopsys DesignWare HDMI +Controller implemented as a DRM bridge. + +Supported Input Formats and Encodings +------------------------------------- + +.. kernel-doc:: include/drm/bridge/dw_hdmi.h + :doc: Supported input formats and encodings diff --git a/Documentation/gpu/drm-internals.rst b/Documentation/gpu/drm-internals.rst index a09c721..babfb61 100644 --- a/Documentation/gpu/drm-internals.rst +++ b/Documentation/gpu/drm-internals.rst @@ -255,56 +255,6 @@ File Operations .. kernel-doc:: drivers/gpu/drm/drm_file.c :export: -IOCTLs ------- - -struct drm_ioctl_desc \*ioctls; int num_ioctls; - Driver-specific ioctls descriptors table. - -Driver-specific ioctls numbers start at DRM_COMMAND_BASE. The ioctls -descriptors table is indexed by the ioctl number offset from the base -value. Drivers can use the DRM_IOCTL_DEF_DRV() macro to initialize -the table entries. - -:: - - DRM_IOCTL_DEF_DRV(ioctl, func, flags) - -``ioctl`` is the ioctl name. Drivers must define the DRM_##ioctl and -DRM_IOCTL_##ioctl macros to the ioctl number offset from -DRM_COMMAND_BASE and the ioctl number respectively. The first macro is -private to the device while the second must be exposed to userspace in a -public header. - -``func`` is a pointer to the ioctl handler function compatible with the -``drm_ioctl_t`` type. - -:: - - typedef int drm_ioctl_t(struct drm_device *dev, void *data, - struct drm_file *file_priv); - -``flags`` is a bitmask combination of the following values. It restricts -how the ioctl is allowed to be called. - -- DRM_AUTH - Only authenticated callers allowed - -- DRM_MASTER - The ioctl can only be called on the master file handle - -- DRM_ROOT_ONLY - Only callers with the SYSADMIN capability allowed - -- DRM_CONTROL_ALLOW - The ioctl can only be called on a control - device - -- DRM_UNLOCKED - The ioctl handler will be called without locking the - DRM global mutex. This is the enforced default for kms drivers (i.e. - using the DRIVER_MODESET flag) and hence shouldn't be used any more - for new drivers. - -.. kernel-doc:: drivers/gpu/drm/drm_ioctl.c - :export: - - Misc Utilities ============== diff --git a/Documentation/gpu/drm-uapi.rst b/Documentation/gpu/drm-uapi.rst index 76356c8..8584575 100644 --- a/Documentation/gpu/drm-uapi.rst +++ b/Documentation/gpu/drm-uapi.rst @@ -160,6 +160,20 @@ other hand, a driver requires shared state between clients which is visible to user-space and accessible beyond open-file boundaries, they cannot support render nodes. +IOCTL Support on Device Nodes +============================= + +.. kernel-doc:: drivers/gpu/drm/drm_ioctl.c + :doc: driver specific ioctls + +.. kernel-doc:: include/drm/drm_ioctl.h + :internal: + +.. kernel-doc:: drivers/gpu/drm/drm_ioctl.c + :export: + +.. kernel-doc:: drivers/gpu/drm/drm_ioc32.c + :export: Testing and validation ====================== @@ -219,6 +233,16 @@ Debugfs Support .. kernel-doc:: drivers/gpu/drm/drm_debugfs.c :export: +Sysfs Support +============= + +.. kernel-doc:: drivers/gpu/drm/drm_sysfs.c + :doc: overview + +.. kernel-doc:: drivers/gpu/drm/drm_sysfs.c + :export: + + VBlank event handling ===================== diff --git a/Documentation/gpu/index.rst b/Documentation/gpu/index.rst index e998ee0..c572f09 100644 --- a/Documentation/gpu/index.rst +++ b/Documentation/gpu/index.rst @@ -11,10 +11,12 @@ Linux GPU Driver Developer's Guide drm-kms-helpers drm-uapi i915 + meson tinydrm vc4 vga-switcheroo vgaarbiter + bridge/dw-hdmi todo .. only:: subproject and html diff --git a/Documentation/gpu/meson.rst b/Documentation/gpu/meson.rst new file mode 100644 index 0000000..479f6f5 --- /dev/null +++ b/Documentation/gpu/meson.rst @@ -0,0 +1,61 @@ +============================================= +drm/meson AmLogic Meson Video Processing Unit +============================================= + +.. kernel-doc:: drivers/gpu/drm/meson/meson_drv.c + :doc: Video Processing Unit + +Video Processing Unit +===================== + +The Amlogic Meson Display controller is composed of several components +that are going to be documented below: + +.. code:: + + DMC|---------------VPU (Video Processing Unit)----------------|------HHI------| + | vd1 _______ _____________ _________________ | | + D |-------| |----| | | | | HDMI PLL | + D | vd2 | VIU | | Video Post | | Video Encoders |<---|-----VCLK | + R |-------| |----| Processing | | | | | + | osd2 | | | |---| Enci ----------|----|-----VDAC------| + R |-------| CSC |----| Scalers | | Encp ----------|----|----HDMI-TX----| + A | osd1 | | | Blenders | | Encl ----------|----|---------------| + M |-------|______|----|____________| |________________| | | + ___|__________________________________________________________|_______________| + +Video Input Unit +================ + +.. kernel-doc:: drivers/gpu/drm/meson/meson_viu.c + :doc: Video Input Unit + +Video Post Processing +===================== + +.. kernel-doc:: drivers/gpu/drm/meson/meson_vpp.c + :doc: Video Post Processing + +Video Encoder +============= + +.. kernel-doc:: drivers/gpu/drm/meson/meson_venc.c + :doc: Video Encoder + +Video Canvas Management +======================= + +.. kernel-doc:: drivers/gpu/drm/meson/meson_canvas.c + :doc: Canvas + +Video Clocks +============ + +.. kernel-doc:: drivers/gpu/drm/meson/meson_vclk.c + :doc: Video Clocks + +HDMI Video Output +================= + +.. kernel-doc:: drivers/gpu/drm/meson/meson_dw_hdmi.c + :doc: HDMI Output diff --git a/Documentation/gpu/todo.rst b/Documentation/gpu/todo.rst index e255b36..1bdb735 100644 --- a/Documentation/gpu/todo.rst +++ b/Documentation/gpu/todo.rst @@ -16,7 +16,7 @@ De-midlayer drivers With the recent ``drm_bus`` cleanup patches for 3.17 it is no longer required to have a ``drm_bus`` structure set up. Drivers can directly set up the ``drm_device`` structure instead of relying on bus methods in ``drm_usb.c`` -and ``drm_platform.c``. The goal is to get rid of the driver's ``->load`` / +and ``drm_pci.c``. The goal is to get rid of the driver's ``->load`` / ``->unload`` callbacks and open-code the load/unload sequence properly, using the new two-stage ``drm_device`` setup/teardown. @@ -175,7 +175,7 @@ fine-grained per-buffer object and per-context lockings scheme. Currently the following drivers still use ``struct_mutex``: ``msm``, ``omapdrm`` and ``udl``. -Contact: Daniel Vetter +Contact: Daniel Vetter, respective driver maintainers Switch to drm_connector_list_iter for any connector_list walking ---------------------------------------------------------------- @@ -217,6 +217,8 @@ plan is to switch to per-file driver API headers, which will also structure the kerneldoc better. This should also allow more fine-grained ``#include`` directives. +In the end no .c file should need to include ``drmP.h`` anymore. + Contact: Daniel Vetter Add missing kerneldoc for exported functions @@ -244,13 +246,8 @@ be hidden so that driver writers don't accidentally end up using it. And to prevent security issues in those legacy IOCTLs from being exploited on modern drivers. This has multiple possible subtasks: -* Make sure legacy IOCTLs can't be used on modern drivers. * Extract support code for legacy features into a ``drm-legacy.ko`` kernel module and compile it only when one of the legacy drivers is enabled. -* Extract legacy functions into their own headers and remove it that from the - monolithic ``drmP.h`` header. -* Remove any lingering cruft from the OS abstraction layer from modern - drivers. This is mostly done, the only thing left is to split up ``drm_irq.c`` into legacy cruft and the parts needed by modern KMS drivers. @@ -408,23 +405,3 @@ Contact: Noralf Trønnes, Daniel Vetter Outside DRM =========== - -Better kerneldoc ----------------- - -This is pretty much done, but there's some advanced topics: - -Come up with a way to hyperlink to struct members. Currently you can hyperlink -to the struct using ``#struct_name``, but not to a member within. Would need -buy-in from kerneldoc maintainers, and the big question is how to make it work -without totally unsightly -``drm_foo_bar_really_long_structure->even_longer_memeber`` all over the text -which breaks text flow. - -Figure out how to integrate the asciidoc support for ascii-diagrams. We have a -few of those (e.g. to describe mode timings), and asciidoc supports converting -some ascii-art dialect into pngs. Would be really pretty to make that work. - -Contact: Daniel Vetter, Jani Nikula - -Jani is working on this already, hopefully lands in 4.8. diff --git a/Documentation/media/uapi/v4l/subdev-formats.rst b/Documentation/media/uapi/v4l/subdev-formats.rst index d6152c9..4032d97 100644 --- a/Documentation/media/uapi/v4l/subdev-formats.rst +++ b/Documentation/media/uapi/v4l/subdev-formats.rst @@ -1258,6 +1258,319 @@ The following tables list existing packed RGB formats. - b\ :sub:`2` - b\ :sub:`1` - b\ :sub:`0` + * .. _MEDIA-BUS-FMT-RGB101010-1X30: + + - MEDIA_BUS_FMT_RGB101010_1X30 + - 0x1018 + - + - 0 + - 0 + - r\ :sub:`9` + - r\ :sub:`8` + - r\ :sub:`7` + - r\ :sub:`6` + - r\ :sub:`5` + - r\ :sub:`4` + - r\ :sub:`3` + - r\ :sub:`2` + - r\ :sub:`1` + - r\ :sub:`0` + - g\ :sub:`9` + - g\ :sub:`8` + - g\ :sub:`7` + - g\ :sub:`6` + - g\ :sub:`5` + - g\ :sub:`4` + - g\ :sub:`3` + - g\ :sub:`2` + - g\ :sub:`1` + - g\ :sub:`0` + - b\ :sub:`9` + - b\ :sub:`8` + - b\ :sub:`7` + - b\ :sub:`6` + - b\ :sub:`5` + - b\ :sub:`4` + - b\ :sub:`3` + - b\ :sub:`2` + - b\ :sub:`1` + - b\ :sub:`0` + +.. raw:: latex + + \endgroup + + +The following table list existing packed 36bit wide RGB formats. + +.. tabularcolumns:: |p{4.0cm}|p{0.7cm}|p{0.22cm}|p{0.22cm}|p{0.22cm}|p{0.22cm}|p{0.22cm}|p{0.22cm}|p{0.22cm}|p{0.22cm}|p{0.22cm}|p{0.22cm}|p{0.22cm}|p{0.22cm}|p{0.22cm}|p{0.22cm}|p{0.22cm}|p{0.22cm}|p{0.22cm}|p{0.22cm}|p{0.22cm}|p{0.22cm}|p{0.22cm}|p{0.22cm}|p{0.22cm}|p{0.22cm}|p{0.22cm}|p{0.22cm}|p{0.22cm}|p{0.22cm}|p{0.22cm}|p{0.22cm}|p{0.22cm}|p{0.22cm}|p{0.22cm}|p{0.22cm}|p{0.22cm}|p{0.22cm}|p{0.22cm}| + +.. _v4l2-mbus-pixelcode-rgb-36: + +.. raw:: latex + + \begingroup + \tiny + \setlength{\tabcolsep}{2pt} + +.. flat-table:: 36bit RGB formats + :header-rows: 2 + :stub-columns: 0 + :widths: 36 7 3 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 + + * - Identifier + - Code + - + - :cspan:`35` Data organization + * - + - + - Bit + - 35 + - 34 + - 33 + - 32 + - 31 + - 30 + - 29 + - 28 + - 27 + - 26 + - 25 + - 24 + - 23 + - 22 + - 21 + - 20 + - 19 + - 18 + - 17 + - 16 + - 15 + - 14 + - 13 + - 12 + - 11 + - 10 + - 9 + - 8 + - 7 + - 6 + - 5 + - 4 + - 3 + - 2 + - 1 + - 0 + * .. _MEDIA-BUS-FMT-RGB121212-1X36: + + - MEDIA_BUS_FMT_RGB121212_1X36 + - 0x1019 + - + - r\ :sub:`11` + - r\ :sub:`10` + - r\ :sub:`9` + - r\ :sub:`8` + - r\ :sub:`7` + - r\ :sub:`6` + - r\ :sub:`5` + - r\ :sub:`4` + - r\ :sub:`3` + - r\ :sub:`2` + - r\ :sub:`1` + - r\ :sub:`0` + - g\ :sub:`11` + - g\ :sub:`10` + - g\ :sub:`9` + - g\ :sub:`8` + - g\ :sub:`7` + - g\ :sub:`6` + - g\ :sub:`5` + - g\ :sub:`4` + - g\ :sub:`3` + - g\ :sub:`2` + - g\ :sub:`1` + - g\ :sub:`0` + - b\ :sub:`11` + - b\ :sub:`10` + - b\ :sub:`9` + - b\ :sub:`8` + - b\ :sub:`7` + - b\ :sub:`6` + - b\ :sub:`5` + - b\ :sub:`4` + - b\ :sub:`3` + - b\ :sub:`2` + - b\ :sub:`1` + - b\ :sub:`0` + +.. raw:: latex + + \endgroup + + +The following table list existing packed 48bit wide RGB formats. + +.. tabularcolumns:: |p{4.0cm}|p{0.7cm}|p{0.22cm}|p{0.22cm}|p{0.22cm}|p{0.22cm}|p{0.22cm}|p{0.22cm}|p{0.22cm}|p{0.22cm}|p{0.22cm}|p{0.22cm}|p{0.22cm}|p{0.22cm}|p{0.22cm}|p{0.22cm}|p{0.22cm}|p{0.22cm}|p{0.22cm}|p{0.22cm}|p{0.22cm}|p{0.22cm}|p{0.22cm}|p{0.22cm}|p{0.22cm}|p{0.22cm}|p{0.22cm}|p{0.22cm}|p{0.22cm}|p{0.22cm}|p{0.22cm}|p{0.22cm}|p{0.22cm}|p{0.22cm}|p{0.22cm}| + +.. _v4l2-mbus-pixelcode-rgb-48: + +.. raw:: latex + + \begingroup + \tiny + \setlength{\tabcolsep}{2pt} + +.. flat-table:: 48bit RGB formats + :header-rows: 3 + :stub-columns: 0 + :widths: 36 7 3 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 + + * - Identifier + - Code + - + - :cspan:`31` Data organization + * - + - + - Bit + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - 47 + - 46 + - 45 + - 44 + - 43 + - 42 + - 41 + - 40 + - 39 + - 38 + - 37 + - 36 + - 35 + - 34 + - 33 + - 32 + * - + - + - + - 31 + - 30 + - 29 + - 28 + - 27 + - 26 + - 25 + - 24 + - 23 + - 22 + - 21 + - 20 + - 19 + - 18 + - 17 + - 16 + - 15 + - 14 + - 13 + - 12 + - 11 + - 10 + - 9 + - 8 + - 7 + - 6 + - 5 + - 4 + - 3 + - 2 + - 1 + - 0 + * .. _MEDIA-BUS-FMT-RGB161616-1X48: + + - MEDIA_BUS_FMT_RGB161616_1X48 + - 0x101a + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - r\ :sub:`15` + - r\ :sub:`14` + - r\ :sub:`13` + - r\ :sub:`12` + - r\ :sub:`11` + - r\ :sub:`10` + - r\ :sub:`9` + - r\ :sub:`8` + - r\ :sub:`7` + - r\ :sub:`6` + - r\ :sub:`5` + - r\ :sub:`4` + - r\ :sub:`3` + - r\ :sub:`2` + - r\ :sub:`1` + - r\ :sub:`0` + * - + - + - + - g\ :sub:`15` + - g\ :sub:`14` + - g\ :sub:`13` + - g\ :sub:`12` + - g\ :sub:`11` + - g\ :sub:`10` + - g\ :sub:`9` + - g\ :sub:`8` + - g\ :sub:`7` + - g\ :sub:`6` + - g\ :sub:`5` + - g\ :sub:`4` + - g\ :sub:`3` + - g\ :sub:`2` + - g\ :sub:`1` + - g\ :sub:`0` + - b\ :sub:`15` + - b\ :sub:`14` + - b\ :sub:`13` + - b\ :sub:`12` + - b\ :sub:`11` + - b\ :sub:`10` + - b\ :sub:`9` + - b\ :sub:`8` + - b\ :sub:`7` + - b\ :sub:`6` + - b\ :sub:`5` + - b\ :sub:`4` + - b\ :sub:`3` + - b\ :sub:`2` + - b\ :sub:`1` + - b\ :sub:`0` .. raw:: latex @@ -2344,7 +2657,8 @@ The format code is made of the following information. - The number of bus samples per pixel. Pixels that are wider than the bus width must be transferred in multiple samples. Common values are - 1, 1.5 (encoded as 1_5) and 2. + 0.5 (encoded as 0_5; in this case two pixels are transferred per bus + sample), 1, 1.5 (encoded as 1_5) and 2. - The bus width. When the bus width is larger than the number of bits per pixel component, several components are packed in a single bus @@ -5962,6 +6276,78 @@ the following codes. - v\ :sub:`2` - v\ :sub:`1` - v\ :sub:`0` + * .. _MEDIA-BUS-FMT-UYYVYY8-0-5X24: + + - MEDIA_BUS_FMT_UYYVYY8_0_5X24 + - 0x2026 + - + - + - + - + - + - + - + - + - + - u\ :sub:`7` + - u\ :sub:`6` + - u\ :sub:`5` + - u\ :sub:`4` + - u\ :sub:`3` + - u\ :sub:`2` + - u\ :sub:`1` + - u\ :sub:`0` + - y\ :sub:`7` + - y\ :sub:`6` + - y\ :sub:`5` + - y\ :sub:`4` + - y\ :sub:`3` + - y\ :sub:`2` + - y\ :sub:`1` + - y\ :sub:`0` + - y\ :sub:`7` + - y\ :sub:`6` + - y\ :sub:`5` + - y\ :sub:`4` + - y\ :sub:`3` + - y\ :sub:`2` + - y\ :sub:`1` + - y\ :sub:`0` + * - + - + - + - + - + - + - + - + - + - + - + - v\ :sub:`7` + - v\ :sub:`6` + - v\ :sub:`5` + - v\ :sub:`4` + - v\ :sub:`3` + - v\ :sub:`2` + - v\ :sub:`1` + - v\ :sub:`0` + - y\ :sub:`7` + - y\ :sub:`6` + - y\ :sub:`5` + - y\ :sub:`4` + - y\ :sub:`3` + - y\ :sub:`2` + - y\ :sub:`1` + - y\ :sub:`0` + - y\ :sub:`7` + - y\ :sub:`6` + - y\ :sub:`5` + - y\ :sub:`4` + - y\ :sub:`3` + - y\ :sub:`2` + - y\ :sub:`1` + - y\ :sub:`0` * .. _MEDIA-BUS-FMT-UYVY12-1X24: - MEDIA_BUS_FMT_UYVY12_1X24 @@ -6287,6 +6673,78 @@ the following codes. - v\ :sub:`2` - v\ :sub:`1` - v\ :sub:`0` + * .. _MEDIA-BUS-FMT-UYYVYY10-0-5X30: + + - MEDIA_BUS_FMT_UYYVYY10_0_5X30 + - 0x2027 + - + - + - + - u\ :sub:`9` + - u\ :sub:`8` + - u\ :sub:`7` + - u\ :sub:`6` + - u\ :sub:`5` + - u\ :sub:`4` + - u\ :sub:`3` + - u\ :sub:`2` + - u\ :sub:`1` + - u\ :sub:`0` + - y\ :sub:`9` + - y\ :sub:`8` + - y\ :sub:`7` + - y\ :sub:`6` + - y\ :sub:`5` + - y\ :sub:`4` + - y\ :sub:`3` + - y\ :sub:`2` + - y\ :sub:`1` + - y\ :sub:`0` + - y\ :sub:`9` + - y\ :sub:`8` + - y\ :sub:`7` + - y\ :sub:`6` + - y\ :sub:`5` + - y\ :sub:`4` + - y\ :sub:`3` + - y\ :sub:`2` + - y\ :sub:`1` + - y\ :sub:`0` + * - + - + - + - + - + - v\ :sub:`9` + - v\ :sub:`8` + - v\ :sub:`7` + - v\ :sub:`6` + - v\ :sub:`5` + - v\ :sub:`4` + - v\ :sub:`3` + - v\ :sub:`2` + - v\ :sub:`1` + - v\ :sub:`0` + - y\ :sub:`9` + - y\ :sub:`8` + - y\ :sub:`7` + - y\ :sub:`6` + - y\ :sub:`5` + - y\ :sub:`4` + - y\ :sub:`3` + - y\ :sub:`2` + - y\ :sub:`1` + - y\ :sub:`0` + - y\ :sub:`9` + - y\ :sub:`8` + - y\ :sub:`7` + - y\ :sub:`6` + - y\ :sub:`5` + - y\ :sub:`4` + - y\ :sub:`3` + - y\ :sub:`2` + - y\ :sub:`1` + - y\ :sub:`0` * .. _MEDIA-BUS-FMT-AYUV8-1X32: - MEDIA_BUS_FMT_AYUV8_1X32 @@ -6330,6 +6788,506 @@ the following codes. \endgroup + +The following table list existing packed 36bit wide YUV formats. + +.. raw:: latex + + \begingroup + \tiny + \setlength{\tabcolsep}{2pt} + +.. tabularcolumns:: |p{4.0cm}|p{0.7cm}|p{0.22cm}|p{0.22cm}|p{0.22cm}|p{0.22cm}|p{0.22cm}|p{0.22cm}|p{0.22cm}|p{0.22cm}|p{0.22cm}|p{0.22cm}|p{0.22cm}|p{0.22cm}|p{0.22cm}|p{0.22cm}|p{0.22cm}|p{0.22cm}|p{0.22cm}|p{0.22cm}|p{0.22cm}|p{0.22cm}|p{0.22cm}|p{0.22cm}|p{0.22cm}|p{0.22cm}|p{0.22cm}|p{0.22cm}|p{0.22cm}|p{0.22cm}|p{0.22cm}|p{0.22cm}|p{0.22cm}|p{0.22cm}|p{0.22cm}|p{0.22cm}|p{0.22cm}|p{0.22cm}|p{0.22cm}| + +.. _v4l2-mbus-pixelcode-yuv8-36bit: + +.. flat-table:: 36bit YUV Formats + :header-rows: 2 + :stub-columns: 0 + :widths: 36 7 3 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 + + * - Identifier + - Code + - + - :cspan:`35` Data organization + * - + - + - Bit + - 35 + - 34 + - 33 + - 32 + - 31 + - 30 + - 29 + - 28 + - 27 + - 26 + - 25 + - 24 + - 23 + - 22 + - 21 + - 10 + - 19 + - 18 + - 17 + - 16 + - 15 + - 14 + - 13 + - 12 + - 11 + - 10 + - 9 + - 8 + - 7 + - 6 + - 5 + - 4 + - 3 + - 2 + - 1 + - 0 + * .. _MEDIA-BUS-FMT-UYYVYY12-0-5X36: + + - MEDIA_BUS_FMT_UYYVYY12_0_5X36 + - 0x2028 + - + - u\ :sub:`11` + - u\ :sub:`10` + - u\ :sub:`9` + - u\ :sub:`8` + - u\ :sub:`7` + - u\ :sub:`6` + - u\ :sub:`5` + - u\ :sub:`4` + - u\ :sub:`3` + - u\ :sub:`2` + - u\ :sub:`1` + - u\ :sub:`0` + - y\ :sub:`11` + - y\ :sub:`10` + - y\ :sub:`9` + - y\ :sub:`8` + - y\ :sub:`7` + - y\ :sub:`6` + - y\ :sub:`5` + - y\ :sub:`4` + - y\ :sub:`3` + - y\ :sub:`2` + - y\ :sub:`1` + - y\ :sub:`0` + - y\ :sub:`11` + - y\ :sub:`10` + - y\ :sub:`9` + - y\ :sub:`8` + - y\ :sub:`7` + - y\ :sub:`6` + - y\ :sub:`5` + - y\ :sub:`4` + - y\ :sub:`3` + - y\ :sub:`2` + - y\ :sub:`1` + - y\ :sub:`0` + * - + - + - + - v\ :sub:`11` + - v\ :sub:`10` + - v\ :sub:`9` + - v\ :sub:`8` + - v\ :sub:`7` + - v\ :sub:`6` + - v\ :sub:`5` + - v\ :sub:`4` + - v\ :sub:`3` + - v\ :sub:`2` + - v\ :sub:`1` + - v\ :sub:`0` + - y\ :sub:`11` + - y\ :sub:`10` + - y\ :sub:`9` + - y\ :sub:`8` + - y\ :sub:`7` + - y\ :sub:`6` + - y\ :sub:`5` + - y\ :sub:`4` + - y\ :sub:`3` + - y\ :sub:`2` + - y\ :sub:`1` + - y\ :sub:`0` + - y\ :sub:`11` + - y\ :sub:`10` + - y\ :sub:`9` + - y\ :sub:`8` + - y\ :sub:`7` + - y\ :sub:`6` + - y\ :sub:`5` + - y\ :sub:`4` + - y\ :sub:`3` + - y\ :sub:`2` + - y\ :sub:`1` + - y\ :sub:`0` + * .. _MEDIA-BUS-FMT-YUV12-1X36: + + - MEDIA_BUS_FMT_YUV12_1X36 + - 0x2029 + - + - y\ :sub:`11` + - y\ :sub:`10` + - y\ :sub:`9` + - y\ :sub:`8` + - y\ :sub:`7` + - y\ :sub:`6` + - y\ :sub:`5` + - y\ :sub:`4` + - y\ :sub:`3` + - y\ :sub:`2` + - y\ :sub:`1` + - y\ :sub:`0` + - u\ :sub:`11` + - u\ :sub:`10` + - u\ :sub:`9` + - u\ :sub:`8` + - u\ :sub:`7` + - u\ :sub:`6` + - u\ :sub:`5` + - u\ :sub:`4` + - u\ :sub:`3` + - u\ :sub:`2` + - u\ :sub:`1` + - u\ :sub:`0` + - v\ :sub:`11` + - v\ :sub:`10` + - v\ :sub:`9` + - v\ :sub:`8` + - v\ :sub:`7` + - v\ :sub:`6` + - v\ :sub:`5` + - v\ :sub:`4` + - v\ :sub:`3` + - v\ :sub:`2` + - v\ :sub:`1` + - v\ :sub:`0` + + +.. raw:: latex + + \endgroup + + +The following table list existing packed 48bit wide YUV formats. + +.. raw:: latex + + \begingroup + \tiny + \setlength{\tabcolsep}{2pt} + +.. tabularcolumns:: |p{4.0cm}|p{0.7cm}|p{0.22cm}|p{0.22cm}|p{0.22cm}|p{0.22cm}|p{0.22cm}|p{0.22cm}|p{0.22cm}|p{0.22cm}|p{0.22cm}|p{0.22cm}|p{0.22cm}|p{0.22cm}|p{0.22cm}|p{0.22cm}|p{0.22cm}|p{0.22cm}|p{0.22cm}|p{0.22cm}|p{0.22cm}|p{0.22cm}|p{0.22cm}|p{0.22cm}|p{0.22cm}|p{0.22cm}|p{0.22cm}|p{0.22cm}|p{0.22cm}|p{0.22cm}|p{0.22cm}|p{0.22cm}|p{0.22cm}|p{0.22cm}|p{0.22cm}| + +.. _v4l2-mbus-pixelcode-yuv8-48bit: + +.. flat-table:: 48bit YUV Formats + :header-rows: 3 + :stub-columns: 0 + :widths: 36 7 3 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 + + * - Identifier + - Code + - + - :cspan:`31` Data organization + * - + - + - Bit + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - 47 + - 46 + - 45 + - 44 + - 43 + - 42 + - 41 + - 40 + - 39 + - 38 + - 37 + - 36 + - 35 + - 34 + - 33 + - 32 + * - + - + - + - 31 + - 30 + - 29 + - 28 + - 27 + - 26 + - 25 + - 24 + - 23 + - 22 + - 21 + - 10 + - 19 + - 18 + - 17 + - 16 + - 15 + - 14 + - 13 + - 12 + - 11 + - 10 + - 9 + - 8 + - 7 + - 6 + - 5 + - 4 + - 3 + - 2 + - 1 + - 0 + * .. _MEDIA-BUS-FMT-YUV16-1X48: + + - MEDIA_BUS_FMT_YUV16_1X48 + - 0x202a + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - y\ :sub:`15` + - y\ :sub:`14` + - y\ :sub:`13` + - y\ :sub:`12` + - y\ :sub:`11` + - y\ :sub:`10` + - y\ :sub:`8` + - y\ :sub:`8` + - y\ :sub:`7` + - y\ :sub:`6` + - y\ :sub:`5` + - y\ :sub:`4` + - y\ :sub:`3` + - y\ :sub:`2` + - y\ :sub:`1` + - y\ :sub:`0` + * - + - + - + - u\ :sub:`15` + - u\ :sub:`14` + - u\ :sub:`13` + - u\ :sub:`12` + - u\ :sub:`11` + - u\ :sub:`10` + - u\ :sub:`9` + - u\ :sub:`8` + - u\ :sub:`7` + - u\ :sub:`6` + - u\ :sub:`5` + - u\ :sub:`4` + - u\ :sub:`3` + - u\ :sub:`2` + - u\ :sub:`1` + - u\ :sub:`0` + - v\ :sub:`15` + - v\ :sub:`14` + - v\ :sub:`13` + - v\ :sub:`12` + - v\ :sub:`11` + - v\ :sub:`10` + - v\ :sub:`9` + - v\ :sub:`8` + - v\ :sub:`7` + - v\ :sub:`6` + - v\ :sub:`5` + - v\ :sub:`4` + - v\ :sub:`3` + - v\ :sub:`2` + - v\ :sub:`1` + - v\ :sub:`0` + * .. _MEDIA-BUS-FMT-UYYVYY16-0-5X48: + + - MEDIA_BUS_FMT_UYYVYY16_0_5X48 + - 0x202b + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - u\ :sub:`15` + - u\ :sub:`14` + - u\ :sub:`13` + - u\ :sub:`12` + - u\ :sub:`11` + - u\ :sub:`10` + - u\ :sub:`9` + - u\ :sub:`8` + - u\ :sub:`7` + - u\ :sub:`6` + - u\ :sub:`5` + - u\ :sub:`4` + - u\ :sub:`3` + - u\ :sub:`2` + - u\ :sub:`1` + - u\ :sub:`0` + * - + - + - + - y\ :sub:`15` + - y\ :sub:`14` + - y\ :sub:`13` + - y\ :sub:`12` + - y\ :sub:`11` + - y\ :sub:`10` + - y\ :sub:`9` + - y\ :sub:`8` + - y\ :sub:`7` + - y\ :sub:`6` + - y\ :sub:`5` + - y\ :sub:`4` + - y\ :sub:`3` + - y\ :sub:`2` + - y\ :sub:`1` + - y\ :sub:`0` + - y\ :sub:`15` + - y\ :sub:`14` + - y\ :sub:`13` + - y\ :sub:`12` + - y\ :sub:`11` + - y\ :sub:`10` + - y\ :sub:`8` + - y\ :sub:`8` + - y\ :sub:`7` + - y\ :sub:`6` + - y\ :sub:`5` + - y\ :sub:`4` + - y\ :sub:`3` + - y\ :sub:`2` + - y\ :sub:`1` + - y\ :sub:`0` + * - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - v\ :sub:`15` + - v\ :sub:`14` + - v\ :sub:`13` + - v\ :sub:`12` + - v\ :sub:`11` + - v\ :sub:`10` + - v\ :sub:`9` + - v\ :sub:`8` + - v\ :sub:`7` + - v\ :sub:`6` + - v\ :sub:`5` + - v\ :sub:`4` + - v\ :sub:`3` + - v\ :sub:`2` + - v\ :sub:`1` + - v\ :sub:`0` + * - + - + - + - y\ :sub:`15` + - y\ :sub:`14` + - y\ :sub:`13` + - y\ :sub:`12` + - y\ :sub:`11` + - y\ :sub:`10` + - y\ :sub:`9` + - y\ :sub:`8` + - y\ :sub:`7` + - y\ :sub:`6` + - y\ :sub:`5` + - y\ :sub:`4` + - y\ :sub:`3` + - y\ :sub:`2` + - y\ :sub:`1` + - y\ :sub:`0` + - y\ :sub:`15` + - y\ :sub:`14` + - y\ :sub:`13` + - y\ :sub:`12` + - y\ :sub:`11` + - y\ :sub:`10` + - y\ :sub:`8` + - y\ :sub:`8` + - y\ :sub:`7` + - y\ :sub:`6` + - y\ :sub:`5` + - y\ :sub:`4` + - y\ :sub:`3` + - y\ :sub:`2` + - y\ :sub:`1` + - y\ :sub:`0` + + +.. raw:: latex + + \endgroup + HSV/HSL Formats ^^^^^^^^^^^^^^^ diff --git a/MAINTAINERS b/MAINTAINERS index c2eb037..7df0915 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -4256,7 +4256,8 @@ W: http://linux-meson.com/ S: Supported F: drivers/gpu/drm/meson/ F: Documentation/devicetree/bindings/display/amlogic,meson-vpu.txt -T: git git://anongit.freedesktop.org/drm/drm-meson +F: Documentation/devicetree/bindings/display/amlogic,meson-dw-hdmi.txt +F: Documentation/gpu/meson.rst T: git git://anongit.freedesktop.org/drm/drm-misc DRM DRIVERS FOR EXYNOS diff --git a/drivers/dma-buf/dma-buf.c b/drivers/dma-buf/dma-buf.c index 0007b79..f72aaac 100644 --- a/drivers/dma-buf/dma-buf.c +++ b/drivers/dma-buf/dma-buf.c @@ -1059,7 +1059,11 @@ static int dma_buf_debug_show(struct seq_file *s, void *unused) int ret; struct dma_buf *buf_obj; struct dma_buf_attachment *attach_obj; - int count = 0, attach_count; + struct reservation_object *robj; + struct reservation_object_list *fobj; + struct dma_fence *fence; + unsigned seq; + int count = 0, attach_count, shared_count, i; size_t size = 0; ret = mutex_lock_interruptible(&db_list.lock); @@ -1068,7 +1072,8 @@ static int dma_buf_debug_show(struct seq_file *s, void *unused) return ret; seq_puts(s, "\nDma-buf Objects:\n"); - seq_puts(s, "size\tflags\tmode\tcount\texp_name\n"); + seq_printf(s, "%-8s\t%-8s\t%-8s\t%-8s\texp_name\n", + "size", "flags", "mode", "count"); list_for_each_entry(buf_obj, &db_list.head, list_node) { ret = mutex_lock_interruptible(&buf_obj->lock); @@ -1085,6 +1090,34 @@ static int dma_buf_debug_show(struct seq_file *s, void *unused) file_count(buf_obj->file), buf_obj->exp_name); + robj = buf_obj->resv; + while (true) { + seq = read_seqcount_begin(&robj->seq); + rcu_read_lock(); + fobj = rcu_dereference(robj->fence); + shared_count = fobj ? fobj->shared_count : 0; + fence = rcu_dereference(robj->fence_excl); + if (!read_seqcount_retry(&robj->seq, seq)) + break; + rcu_read_unlock(); + } + + if (fence) + seq_printf(s, "\tExclusive fence: %s %s %ssignalled\n", + fence->ops->get_driver_name(fence), + fence->ops->get_timeline_name(fence), + dma_fence_is_signaled(fence) ? "" : "un"); + for (i = 0; i < shared_count; i++) { + fence = rcu_dereference(fobj->shared[i]); + if (!dma_fence_get_rcu(fence)) + continue; + seq_printf(s, "\tShared fence: %s %s %ssignalled\n", + fence->ops->get_driver_name(fence), + fence->ops->get_timeline_name(fence), + dma_fence_is_signaled(fence) ? "" : "un"); + } + rcu_read_unlock(); + seq_puts(s, "\tAttached Devices:\n"); attach_count = 0; diff --git a/drivers/gpu/drm/amd/amdgpu/dce_v10_0.c b/drivers/gpu/drm/amd/amdgpu/dce_v10_0.c index 2b78d58..ba98d35 100644 --- a/drivers/gpu/drm/amd/amdgpu/dce_v10_0.c +++ b/drivers/gpu/drm/amd/amdgpu/dce_v10_0.c @@ -2618,7 +2618,8 @@ static void dce_v10_0_cursor_reset(struct drm_crtc *crtc) } static int dce_v10_0_crtc_gamma_set(struct drm_crtc *crtc, u16 *red, u16 *green, - u16 *blue, uint32_t size) + u16 *blue, uint32_t size, + struct drm_modeset_acquire_ctx *ctx) { struct amdgpu_crtc *amdgpu_crtc = to_amdgpu_crtc(crtc); int i; diff --git a/drivers/gpu/drm/amd/amdgpu/dce_v11_0.c b/drivers/gpu/drm/amd/amdgpu/dce_v11_0.c index fec2a69..e59bc42 100644 --- a/drivers/gpu/drm/amd/amdgpu/dce_v11_0.c +++ b/drivers/gpu/drm/amd/amdgpu/dce_v11_0.c @@ -2638,7 +2638,8 @@ static void dce_v11_0_cursor_reset(struct drm_crtc *crtc) } static int dce_v11_0_crtc_gamma_set(struct drm_crtc *crtc, u16 *red, u16 *green, - u16 *blue, uint32_t size) + u16 *blue, uint32_t size, + struct drm_modeset_acquire_ctx *ctx) { struct amdgpu_crtc *amdgpu_crtc = to_amdgpu_crtc(crtc); int i; diff --git a/drivers/gpu/drm/amd/amdgpu/dce_v6_0.c b/drivers/gpu/drm/amd/amdgpu/dce_v6_0.c index 6604bcf..307269b 100644 --- a/drivers/gpu/drm/amd/amdgpu/dce_v6_0.c +++ b/drivers/gpu/drm/amd/amdgpu/dce_v6_0.c @@ -1985,7 +1985,8 @@ static void dce_v6_0_cursor_reset(struct drm_crtc *crtc) } static int dce_v6_0_crtc_gamma_set(struct drm_crtc *crtc, u16 *red, u16 *green, - u16 *blue, uint32_t size) + u16 *blue, uint32_t size, + struct drm_modeset_acquire_ctx *ctx) { struct amdgpu_crtc *amdgpu_crtc = to_amdgpu_crtc(crtc); int i; diff --git a/drivers/gpu/drm/amd/amdgpu/dce_v8_0.c b/drivers/gpu/drm/amd/amdgpu/dce_v8_0.c index e10c82f..6df7a28 100644 --- a/drivers/gpu/drm/amd/amdgpu/dce_v8_0.c +++ b/drivers/gpu/drm/amd/amdgpu/dce_v8_0.c @@ -2469,7 +2469,8 @@ static void dce_v8_0_cursor_reset(struct drm_crtc *crtc) } static int dce_v8_0_crtc_gamma_set(struct drm_crtc *crtc, u16 *red, u16 *green, - u16 *blue, uint32_t size) + u16 *blue, uint32_t size, + struct drm_modeset_acquire_ctx *ctx) { struct amdgpu_crtc *amdgpu_crtc = to_amdgpu_crtc(crtc); int i; diff --git a/drivers/gpu/drm/amd/amdgpu/dce_virtual.c b/drivers/gpu/drm/amd/amdgpu/dce_virtual.c index 5c51f9a..81a24b6 100644 --- a/drivers/gpu/drm/amd/amdgpu/dce_virtual.c +++ b/drivers/gpu/drm/amd/amdgpu/dce_virtual.c @@ -165,7 +165,8 @@ static void dce_virtual_bandwidth_update(struct amdgpu_device *adev) } static int dce_virtual_crtc_gamma_set(struct drm_crtc *crtc, u16 *red, - u16 *green, u16 *blue, uint32_t size) + u16 *green, u16 *blue, uint32_t size, + struct drm_modeset_acquire_ctx *ctx) { struct amdgpu_crtc *amdgpu_crtc = to_amdgpu_crtc(crtc); int i; diff --git a/drivers/gpu/drm/arm/hdlcd_drv.c b/drivers/gpu/drm/arm/hdlcd_drv.c index 0e74880..0f49c4b 100644 --- a/drivers/gpu/drm/arm/hdlcd_drv.c +++ b/drivers/gpu/drm/arm/hdlcd_drv.c @@ -392,29 +392,13 @@ static int compare_dev(struct device *dev, void *data) static int hdlcd_probe(struct platform_device *pdev) { - struct device_node *port, *ep; + struct device_node *port; struct component_match *match = NULL; - if (!pdev->dev.of_node) - return -ENODEV; - /* there is only one output port inside each device, find it */ - ep = of_graph_get_next_endpoint(pdev->dev.of_node, NULL); - if (!ep) - return -ENODEV; - - if (!of_device_is_available(ep)) { - of_node_put(ep); + port = of_graph_get_remote_node(pdev->dev.of_node, 0, 0); + if (!port) return -ENODEV; - } - - /* add the remote encoder port as component */ - port = of_graph_get_remote_port_parent(ep); - of_node_put(ep); - if (!port || !of_device_is_available(port)) { - of_node_put(port); - return -EAGAIN; - } drm_of_component_match_add(&pdev->dev, &match, compare_dev, port); of_node_put(port); diff --git a/drivers/gpu/drm/arm/malidp_drv.c b/drivers/gpu/drm/arm/malidp_drv.c index ea2546f..898c2b58 100644 --- a/drivers/gpu/drm/arm/malidp_drv.c +++ b/drivers/gpu/drm/arm/malidp_drv.c @@ -283,7 +283,6 @@ static int malidp_bind(struct device *dev) { struct resource *res; struct drm_device *drm; - struct device_node *ep; struct malidp_drm *malidp; struct malidp_hw_device *hwdev; struct platform_device *pdev = to_platform_device(dev); @@ -398,12 +397,7 @@ static int malidp_bind(struct device *dev) goto init_fail; /* Set the CRTC's port so that the encoder component can find it */ - ep = of_graph_get_next_endpoint(dev->of_node, NULL); - if (!ep) { - ret = -EINVAL; - goto port_fail; - } - malidp->crtc.port = of_get_next_parent(ep); + malidp->crtc.port = of_graph_get_port_by_id(dev->of_node, 0); ret = component_bind_all(dev, drm); if (ret) { @@ -458,7 +452,6 @@ irq_init_fail: bind_fail: of_node_put(malidp->crtc.port); malidp->crtc.port = NULL; -port_fail: malidp_fini(drm); init_fail: drm->dev_private = NULL; @@ -516,29 +509,16 @@ static int malidp_compare_dev(struct device *dev, void *data) static int malidp_platform_probe(struct platform_device *pdev) { - struct device_node *port, *ep; + struct device_node *port; struct component_match *match = NULL; if (!pdev->dev.of_node) return -ENODEV; /* there is only one output port inside each device, find it */ - ep = of_graph_get_next_endpoint(pdev->dev.of_node, NULL); - if (!ep) - return -ENODEV; - - if (!of_device_is_available(ep)) { - of_node_put(ep); + port = of_graph_get_remote_node(pdev->dev.of_node, 0, 0); + if (!port) return -ENODEV; - } - - /* add the remote encoder port as component */ - port = of_graph_get_remote_port_parent(ep); - of_node_put(ep); - if (!port || !of_device_is_available(port)) { - of_node_put(port); - return -EAGAIN; - } drm_of_component_match_add(&pdev->dev, &match, malidp_compare_dev, port); diff --git a/drivers/gpu/drm/ast/ast_mode.c b/drivers/gpu/drm/ast/ast_mode.c index 47b78e5..aaef0a6 100644 --- a/drivers/gpu/drm/ast/ast_mode.c +++ b/drivers/gpu/drm/ast/ast_mode.c @@ -645,7 +645,8 @@ static void ast_crtc_reset(struct drm_crtc *crtc) } static int ast_crtc_gamma_set(struct drm_crtc *crtc, u16 *red, u16 *green, - u16 *blue, uint32_t size) + u16 *blue, uint32_t size, + struct drm_modeset_acquire_ctx *ctx) { struct ast_crtc *ast_crtc = to_ast_crtc(crtc); int i; diff --git a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_output.c b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_output.c index e7799b6..f987b45 100644 --- a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_output.c +++ b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_output.c @@ -22,7 +22,7 @@ #include <linux/of_graph.h> #include <drm/drmP.h> -#include <drm/drm_panel.h> +#include <drm/drm_of.h> #include "atmel_hlcdc_dc.h" @@ -152,29 +152,11 @@ static const struct drm_connector_funcs atmel_hlcdc_panel_connector_funcs = { .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, }; -static int atmel_hlcdc_check_endpoint(struct drm_device *dev, - const struct of_endpoint *ep) -{ - struct device_node *np; - void *obj; - - np = of_graph_get_remote_port_parent(ep->local_node); - - obj = of_drm_find_panel(np); - if (!obj) - obj = of_drm_find_bridge(np); - - of_node_put(np); - - return obj ? 0 : -EPROBE_DEFER; -} - static int atmel_hlcdc_attach_endpoint(struct drm_device *dev, - const struct of_endpoint *ep) + const struct device_node *np) { struct atmel_hlcdc_dc *dc = dev->dev_private; struct atmel_hlcdc_rgb_output *output; - struct device_node *np; struct drm_panel *panel; struct drm_bridge *bridge; int ret; @@ -195,13 +177,11 @@ static int atmel_hlcdc_attach_endpoint(struct drm_device *dev, output->encoder.possible_crtcs = 0x1; - np = of_graph_get_remote_port_parent(ep->local_node); - - ret = -EPROBE_DEFER; + ret = drm_of_find_panel_or_bridge(np, 0, 0, &panel, &bridge); + if (ret) + return ret; - panel = of_drm_find_panel(np); if (panel) { - of_node_put(np); output->connector.dpms = DRM_MODE_DPMS_OFF; output->connector.polled = DRM_CONNECTOR_POLL_CONNECT; drm_connector_helper_add(&output->connector, @@ -226,9 +206,6 @@ static int atmel_hlcdc_attach_endpoint(struct drm_device *dev, return 0; } - bridge = of_drm_find_bridge(np); - of_node_put(np); - if (bridge) { ret = drm_bridge_attach(&output->encoder, bridge, NULL); if (!ret) @@ -243,31 +220,23 @@ err_encoder_cleanup: int atmel_hlcdc_create_outputs(struct drm_device *dev) { - struct device_node *ep_np = NULL; - struct of_endpoint ep; - int ret; - - for_each_endpoint_of_node(dev->dev->of_node, ep_np) { - ret = of_graph_parse_endpoint(ep_np, &ep); - if (!ret) - ret = atmel_hlcdc_check_endpoint(dev, &ep); - - if (ret) { - of_node_put(ep_np); - return ret; - } - } - - for_each_endpoint_of_node(dev->dev->of_node, ep_np) { - ret = of_graph_parse_endpoint(ep_np, &ep); - if (!ret) - ret = atmel_hlcdc_attach_endpoint(dev, &ep); - - if (ret) { - of_node_put(ep_np); + struct device_node *remote; + int ret, endpoint = 0; + + while (true) { + /* Loop thru possible multiple connections to the output */ + remote = of_graph_get_remote_node(dev->dev->of_node, 0, + endpoint++); + if (!remote) + break; + + ret = atmel_hlcdc_attach_endpoint(dev, remote); + of_node_put(remote); + if (ret) return ret; - } } - return 0; + if (!endpoint) + return -ENODEV; + return ret; } diff --git a/drivers/gpu/drm/bridge/adv7511/adv7533.c b/drivers/gpu/drm/bridge/adv7511/adv7533.c index 8b21037..ac804f8 100644 --- a/drivers/gpu/drm/bridge/adv7511/adv7533.c +++ b/drivers/gpu/drm/bridge/adv7511/adv7533.c @@ -232,7 +232,6 @@ void adv7533_detach_dsi(struct adv7511 *adv) int adv7533_parse_dt(struct device_node *np, struct adv7511 *adv) { u32 num_lanes; - struct device_node *endpoint; of_property_read_u32(np, "adi,dsi-lanes", &num_lanes); @@ -241,17 +240,10 @@ int adv7533_parse_dt(struct device_node *np, struct adv7511 *adv) adv->num_dsi_lanes = num_lanes; - endpoint = of_graph_get_next_endpoint(np, NULL); - if (!endpoint) + adv->host_node = of_graph_get_remote_node(np, 0, 0); + if (!adv->host_node) return -ENODEV; - adv->host_node = of_graph_get_remote_port_parent(endpoint); - if (!adv->host_node) { - of_node_put(endpoint); - return -ENODEV; - } - - of_node_put(endpoint); of_node_put(adv->host_node); adv->use_timing_gen = !of_property_read_bool(np, diff --git a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c index c26997a..4c758ed 100644 --- a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c +++ b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c @@ -1439,13 +1439,19 @@ void analogix_dp_unbind(struct device *dev, struct device *master, struct analogix_dp_device *dp = dev_get_drvdata(dev); analogix_dp_bridge_disable(dp->bridge); + dp->connector.funcs->destroy(&dp->connector); + dp->encoder->funcs->destroy(dp->encoder); if (dp->plat_data->panel) { if (drm_panel_unprepare(dp->plat_data->panel)) DRM_ERROR("failed to turnoff the panel\n"); + if (drm_panel_detach(dp->plat_data->panel)) + DRM_ERROR("failed to detach the panel\n"); } + drm_dp_aux_unregister(&dp->aux); pm_runtime_disable(dev); + clk_disable_unprepare(dp->clock); } EXPORT_SYMBOL_GPL(analogix_dp_unbind); diff --git a/drivers/gpu/drm/bridge/dumb-vga-dac.c b/drivers/gpu/drm/bridge/dumb-vga-dac.c index 63e113b..831a606 100644 --- a/drivers/gpu/drm/bridge/dumb-vga-dac.c +++ b/drivers/gpu/drm/bridge/dumb-vga-dac.c @@ -154,21 +154,12 @@ static const struct drm_bridge_funcs dumb_vga_bridge_funcs = { static struct i2c_adapter *dumb_vga_retrieve_ddc(struct device *dev) { - struct device_node *end_node, *phandle, *remote; + struct device_node *phandle, *remote; struct i2c_adapter *ddc; - end_node = of_graph_get_endpoint_by_regs(dev->of_node, 1, -1); - if (!end_node) { - dev_err(dev, "Missing connector endpoint\n"); - return ERR_PTR(-ENODEV); - } - - remote = of_graph_get_remote_port_parent(end_node); - of_node_put(end_node); - if (!remote) { - dev_err(dev, "Enable to parse remote node\n"); + remote = of_graph_get_remote_node(dev->of_node, 1, -1); + if (!remote) return ERR_PTR(-EINVAL); - } phandle = of_parse_phandle(remote, "ddc-i2c-bus", 0); of_node_put(remote); diff --git a/drivers/gpu/drm/bridge/megachips-stdpxxxx-ge-b850v3-fw.c b/drivers/gpu/drm/bridge/megachips-stdpxxxx-ge-b850v3-fw.c index cfc606a..11f1108 100644 --- a/drivers/gpu/drm/bridge/megachips-stdpxxxx-ge-b850v3-fw.c +++ b/drivers/gpu/drm/bridge/megachips-stdpxxxx-ge-b850v3-fw.c @@ -279,10 +279,6 @@ static int ge_b850v3_lvds_init(struct device *dev) return -ENOMEM; } - ge_b850v3_lvds_ptr->bridge.funcs = &ge_b850v3_lvds_funcs; - ge_b850v3_lvds_ptr->bridge.of_node = dev->of_node; - drm_bridge_add(&ge_b850v3_lvds_ptr->bridge); - success: mutex_unlock(&ge_b850v3_lvds_dev_mutex); return 0; @@ -317,6 +313,11 @@ static int stdp4028_ge_b850v3_fw_probe(struct i2c_client *stdp4028_i2c, ge_b850v3_lvds_ptr->stdp4028_i2c = stdp4028_i2c; i2c_set_clientdata(stdp4028_i2c, ge_b850v3_lvds_ptr); + /* drm bridge initialization */ + ge_b850v3_lvds_ptr->bridge.funcs = &ge_b850v3_lvds_funcs; + ge_b850v3_lvds_ptr->bridge.of_node = dev->of_node; + drm_bridge_add(&ge_b850v3_lvds_ptr->bridge); + /* Clear pending interrupts since power up. */ i2c_smbus_write_word_data(stdp4028_i2c, STDP4028_DPTX_IRQ_STS_REG, diff --git a/drivers/gpu/drm/bridge/nxp-ptn3460.c b/drivers/gpu/drm/bridge/nxp-ptn3460.c index 27f98c5..3517043 100644 --- a/drivers/gpu/drm/bridge/nxp-ptn3460.c +++ b/drivers/gpu/drm/bridge/nxp-ptn3460.c @@ -20,8 +20,8 @@ #include <linux/module.h> #include <linux/of.h> #include <linux/of_gpio.h> -#include <linux/of_graph.h> +#include <drm/drm_of.h> #include <drm/drm_panel.h> #include "drm_crtc.h" @@ -292,7 +292,6 @@ static int ptn3460_probe(struct i2c_client *client, { struct device *dev = &client->dev; struct ptn3460_bridge *ptn_bridge; - struct device_node *endpoint, *panel_node; int ret; ptn_bridge = devm_kzalloc(dev, sizeof(*ptn_bridge), GFP_KERNEL); @@ -300,16 +299,9 @@ static int ptn3460_probe(struct i2c_client *client, return -ENOMEM; } - endpoint = of_graph_get_next_endpoint(dev->of_node, NULL); - if (endpoint) { - panel_node = of_graph_get_remote_port_parent(endpoint); - if (panel_node) { - ptn_bridge->panel = of_drm_find_panel(panel_node); - of_node_put(panel_node); - if (!ptn_bridge->panel) - return -EPROBE_DEFER; - } - } + ret = drm_of_find_panel_or_bridge(dev->of_node, 0, 0, &ptn_bridge->panel, NULL); + if (ret) + return ret; ptn_bridge->client = client; diff --git a/drivers/gpu/drm/bridge/parade-ps8622.c b/drivers/gpu/drm/bridge/parade-ps8622.c index ac8cc5b..1dcec3b 100644 --- a/drivers/gpu/drm/bridge/parade-ps8622.c +++ b/drivers/gpu/drm/bridge/parade-ps8622.c @@ -22,10 +22,10 @@ #include <linux/module.h> #include <linux/of.h> #include <linux/of_device.h> -#include <linux/of_graph.h> #include <linux/pm.h> #include <linux/regulator/consumer.h> +#include <drm/drm_of.h> #include <drm/drm_panel.h> #include "drmP.h" @@ -536,7 +536,6 @@ static int ps8622_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct device *dev = &client->dev; - struct device_node *endpoint, *panel_node; struct ps8622_bridge *ps8622; int ret; @@ -544,16 +543,9 @@ static int ps8622_probe(struct i2c_client *client, if (!ps8622) return -ENOMEM; - endpoint = of_graph_get_next_endpoint(dev->of_node, NULL); - if (endpoint) { - panel_node = of_graph_get_remote_port_parent(endpoint); - if (panel_node) { - ps8622->panel = of_drm_find_panel(panel_node); - of_node_put(panel_node); - if (!ps8622->panel) - return -EPROBE_DEFER; - } - } + ret = drm_of_find_panel_or_bridge(dev->of_node, 0, 0, &ps8622->panel, NULL); + if (ret) + return ret; ps8622->client = client; diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c index 32f02e9..3bc856c 100644 --- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c +++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c @@ -30,18 +30,15 @@ #include <drm/drm_encoder_slave.h> #include <drm/bridge/dw_hdmi.h> +#include <uapi/linux/media-bus-format.h> +#include <uapi/linux/videodev2.h> + #include "dw-hdmi.h" #include "dw-hdmi-audio.h" #define DDC_SEGMENT_ADDR 0x30 #define HDMI_EDID_LEN 512 -#define RGB 0 -#define YCBCR444 1 -#define YCBCR422_16BITS 2 -#define YCBCR422_8BITS 3 -#define XVYCC444 4 - enum hdmi_datamap { RGB444_8B = 0x01, RGB444_10B = 0x03, @@ -95,10 +92,10 @@ struct hdmi_vmode { }; struct hdmi_data_info { - unsigned int enc_in_format; - unsigned int enc_out_format; - unsigned int enc_color_depth; - unsigned int colorimetry; + unsigned int enc_in_bus_format; + unsigned int enc_out_bus_format; + unsigned int enc_in_encoding; + unsigned int enc_out_encoding; unsigned int pix_repet_factor; unsigned int hdcp_enable; struct hdmi_vmode video_mode; @@ -567,6 +564,78 @@ void dw_hdmi_audio_disable(struct dw_hdmi *hdmi) } EXPORT_SYMBOL_GPL(dw_hdmi_audio_disable); +static bool hdmi_bus_fmt_is_rgb(unsigned int bus_format) +{ + switch (bus_format) { + case MEDIA_BUS_FMT_RGB888_1X24: + case MEDIA_BUS_FMT_RGB101010_1X30: + case MEDIA_BUS_FMT_RGB121212_1X36: + case MEDIA_BUS_FMT_RGB161616_1X48: + return true; + + default: + return false; + } +} + +static bool hdmi_bus_fmt_is_yuv444(unsigned int bus_format) +{ + switch (bus_format) { + case MEDIA_BUS_FMT_YUV8_1X24: + case MEDIA_BUS_FMT_YUV10_1X30: + case MEDIA_BUS_FMT_YUV12_1X36: + case MEDIA_BUS_FMT_YUV16_1X48: + return true; + + default: + return false; + } +} + +static bool hdmi_bus_fmt_is_yuv422(unsigned int bus_format) +{ + switch (bus_format) { + case MEDIA_BUS_FMT_UYVY8_1X16: + case MEDIA_BUS_FMT_UYVY10_1X20: + case MEDIA_BUS_FMT_UYVY12_1X24: + return true; + + default: + return false; + } +} + +static int hdmi_bus_fmt_color_depth(unsigned int bus_format) +{ + switch (bus_format) { + case MEDIA_BUS_FMT_RGB888_1X24: + case MEDIA_BUS_FMT_YUV8_1X24: + case MEDIA_BUS_FMT_UYVY8_1X16: + case MEDIA_BUS_FMT_UYYVYY8_0_5X24: + return 8; + + case MEDIA_BUS_FMT_RGB101010_1X30: + case MEDIA_BUS_FMT_YUV10_1X30: + case MEDIA_BUS_FMT_UYVY10_1X20: + case MEDIA_BUS_FMT_UYYVYY10_0_5X30: + return 10; + + case MEDIA_BUS_FMT_RGB121212_1X36: + case MEDIA_BUS_FMT_YUV12_1X36: + case MEDIA_BUS_FMT_UYVY12_1X24: + case MEDIA_BUS_FMT_UYYVYY12_0_5X36: + return 12; + + case MEDIA_BUS_FMT_RGB161616_1X48: + case MEDIA_BUS_FMT_YUV16_1X48: + case MEDIA_BUS_FMT_UYYVYY16_0_5X48: + return 16; + + default: + return 0; + } +} + /* * this submodule is responsible for the video data synchronization. * for example, for RGB 4:4:4 input, the data map is defined as @@ -579,37 +648,49 @@ static void hdmi_video_sample(struct dw_hdmi *hdmi) int color_format = 0; u8 val; - if (hdmi->hdmi_data.enc_in_format == RGB) { - if (hdmi->hdmi_data.enc_color_depth == 8) - color_format = 0x01; - else if (hdmi->hdmi_data.enc_color_depth == 10) - color_format = 0x03; - else if (hdmi->hdmi_data.enc_color_depth == 12) - color_format = 0x05; - else if (hdmi->hdmi_data.enc_color_depth == 16) - color_format = 0x07; - else - return; - } else if (hdmi->hdmi_data.enc_in_format == YCBCR444) { - if (hdmi->hdmi_data.enc_color_depth == 8) - color_format = 0x09; - else if (hdmi->hdmi_data.enc_color_depth == 10) - color_format = 0x0B; - else if (hdmi->hdmi_data.enc_color_depth == 12) - color_format = 0x0D; - else if (hdmi->hdmi_data.enc_color_depth == 16) - color_format = 0x0F; - else - return; - } else if (hdmi->hdmi_data.enc_in_format == YCBCR422_8BITS) { - if (hdmi->hdmi_data.enc_color_depth == 8) - color_format = 0x16; - else if (hdmi->hdmi_data.enc_color_depth == 10) - color_format = 0x14; - else if (hdmi->hdmi_data.enc_color_depth == 12) - color_format = 0x12; - else - return; + switch (hdmi->hdmi_data.enc_in_bus_format) { + case MEDIA_BUS_FMT_RGB888_1X24: + color_format = 0x01; + break; + case MEDIA_BUS_FMT_RGB101010_1X30: + color_format = 0x03; + break; + case MEDIA_BUS_FMT_RGB121212_1X36: + color_format = 0x05; + break; + case MEDIA_BUS_FMT_RGB161616_1X48: + color_format = 0x07; + break; + + case MEDIA_BUS_FMT_YUV8_1X24: + case MEDIA_BUS_FMT_UYYVYY8_0_5X24: + color_format = 0x09; + break; + case MEDIA_BUS_FMT_YUV10_1X30: + case MEDIA_BUS_FMT_UYYVYY10_0_5X30: + color_format = 0x0B; + break; + case MEDIA_BUS_FMT_YUV12_1X36: + case MEDIA_BUS_FMT_UYYVYY12_0_5X36: + color_format = 0x0D; + break; + case MEDIA_BUS_FMT_YUV16_1X48: + case MEDIA_BUS_FMT_UYYVYY16_0_5X48: + color_format = 0x0F; + break; + + case MEDIA_BUS_FMT_UYVY8_1X16: + color_format = 0x16; + break; + case MEDIA_BUS_FMT_UYVY10_1X20: + color_format = 0x14; + break; + case MEDIA_BUS_FMT_UYVY12_1X24: + color_format = 0x12; + break; + + default: + return; } val = HDMI_TX_INVID0_INTERNAL_DE_GENERATOR_DISABLE | @@ -632,26 +713,30 @@ static void hdmi_video_sample(struct dw_hdmi *hdmi) static int is_color_space_conversion(struct dw_hdmi *hdmi) { - return hdmi->hdmi_data.enc_in_format != hdmi->hdmi_data.enc_out_format; + return hdmi->hdmi_data.enc_in_bus_format != hdmi->hdmi_data.enc_out_bus_format; } static int is_color_space_decimation(struct dw_hdmi *hdmi) { - if (hdmi->hdmi_data.enc_out_format != YCBCR422_8BITS) + if (!hdmi_bus_fmt_is_yuv422(hdmi->hdmi_data.enc_out_bus_format)) return 0; - if (hdmi->hdmi_data.enc_in_format == RGB || - hdmi->hdmi_data.enc_in_format == YCBCR444) + + if (hdmi_bus_fmt_is_rgb(hdmi->hdmi_data.enc_in_bus_format) || + hdmi_bus_fmt_is_yuv444(hdmi->hdmi_data.enc_in_bus_format)) return 1; + return 0; } static int is_color_space_interpolation(struct dw_hdmi *hdmi) { - if (hdmi->hdmi_data.enc_in_format != YCBCR422_8BITS) + if (!hdmi_bus_fmt_is_yuv422(hdmi->hdmi_data.enc_in_bus_format)) return 0; - if (hdmi->hdmi_data.enc_out_format == RGB || - hdmi->hdmi_data.enc_out_format == YCBCR444) + + if (hdmi_bus_fmt_is_rgb(hdmi->hdmi_data.enc_out_bus_format) || + hdmi_bus_fmt_is_yuv444(hdmi->hdmi_data.enc_out_bus_format)) return 1; + return 0; } @@ -662,15 +747,16 @@ static void dw_hdmi_update_csc_coeffs(struct dw_hdmi *hdmi) u32 csc_scale = 1; if (is_color_space_conversion(hdmi)) { - if (hdmi->hdmi_data.enc_out_format == RGB) { - if (hdmi->hdmi_data.colorimetry == - HDMI_COLORIMETRY_ITU_601) + if (hdmi_bus_fmt_is_rgb(hdmi->hdmi_data.enc_out_bus_format)) { + if (hdmi->hdmi_data.enc_out_encoding == + V4L2_YCBCR_ENC_601) csc_coeff = &csc_coeff_rgb_out_eitu601; else csc_coeff = &csc_coeff_rgb_out_eitu709; - } else if (hdmi->hdmi_data.enc_in_format == RGB) { - if (hdmi->hdmi_data.colorimetry == - HDMI_COLORIMETRY_ITU_601) + } else if (hdmi_bus_fmt_is_rgb( + hdmi->hdmi_data.enc_in_bus_format)) { + if (hdmi->hdmi_data.enc_out_encoding == + V4L2_YCBCR_ENC_601) csc_coeff = &csc_coeff_rgb_in_eitu601; else csc_coeff = &csc_coeff_rgb_in_eitu709; @@ -708,16 +794,23 @@ static void hdmi_video_csc(struct dw_hdmi *hdmi) else if (is_color_space_decimation(hdmi)) decimation = HDMI_CSC_CFG_DECMODE_CHROMA_INT_FORMULA3; - if (hdmi->hdmi_data.enc_color_depth == 8) + switch (hdmi_bus_fmt_color_depth(hdmi->hdmi_data.enc_out_bus_format)) { + case 8: color_depth = HDMI_CSC_SCALE_CSC_COLORDE_PTH_24BPP; - else if (hdmi->hdmi_data.enc_color_depth == 10) + break; + case 10: color_depth = HDMI_CSC_SCALE_CSC_COLORDE_PTH_30BPP; - else if (hdmi->hdmi_data.enc_color_depth == 12) + break; + case 12: color_depth = HDMI_CSC_SCALE_CSC_COLORDE_PTH_36BPP; - else if (hdmi->hdmi_data.enc_color_depth == 16) + break; + case 16: color_depth = HDMI_CSC_SCALE_CSC_COLORDE_PTH_48BPP; - else + break; + + default: return; + } /* Configure the CSC registers */ hdmi_writeb(hdmi, interpolation | decimation, HDMI_CSC_CFG); @@ -740,32 +833,43 @@ static void hdmi_video_packetize(struct dw_hdmi *hdmi) struct hdmi_data_info *hdmi_data = &hdmi->hdmi_data; u8 val, vp_conf; - if (hdmi_data->enc_out_format == RGB || - hdmi_data->enc_out_format == YCBCR444) { - if (!hdmi_data->enc_color_depth) { - output_select = HDMI_VP_CONF_OUTPUT_SELECTOR_BYPASS; - } else if (hdmi_data->enc_color_depth == 8) { + if (hdmi_bus_fmt_is_rgb(hdmi->hdmi_data.enc_out_bus_format) || + hdmi_bus_fmt_is_yuv444(hdmi->hdmi_data.enc_out_bus_format)) { + switch (hdmi_bus_fmt_color_depth( + hdmi->hdmi_data.enc_out_bus_format)) { + case 8: color_depth = 4; output_select = HDMI_VP_CONF_OUTPUT_SELECTOR_BYPASS; - } else if (hdmi_data->enc_color_depth == 10) { + break; + case 10: color_depth = 5; - } else if (hdmi_data->enc_color_depth == 12) { + break; + case 12: color_depth = 6; - } else if (hdmi_data->enc_color_depth == 16) { + break; + case 16: color_depth = 7; - } else { - return; + break; + default: + output_select = HDMI_VP_CONF_OUTPUT_SELECTOR_BYPASS; } - } else if (hdmi_data->enc_out_format == YCBCR422_8BITS) { - if (!hdmi_data->enc_color_depth || - hdmi_data->enc_color_depth == 8) + } else if (hdmi_bus_fmt_is_yuv422(hdmi->hdmi_data.enc_out_bus_format)) { + switch (hdmi_bus_fmt_color_depth( + hdmi->hdmi_data.enc_out_bus_format)) { + case 0: + case 8: remap_size = HDMI_VP_REMAP_YCC422_16bit; - else if (hdmi_data->enc_color_depth == 10) + break; + case 10: remap_size = HDMI_VP_REMAP_YCC422_20bit; - else if (hdmi_data->enc_color_depth == 12) + break; + case 12: remap_size = HDMI_VP_REMAP_YCC422_24bit; - else + break; + + default: return; + } output_select = HDMI_VP_CONF_OUTPUT_SELECTOR_YCC422; } else { return; @@ -1111,10 +1215,46 @@ static enum drm_connector_status dw_hdmi_phy_read_hpd(struct dw_hdmi *hdmi, connector_status_connected : connector_status_disconnected; } +static void dw_hdmi_phy_update_hpd(struct dw_hdmi *hdmi, void *data, + bool force, bool disabled, bool rxsense) +{ + u8 old_mask = hdmi->phy_mask; + + if (force || disabled || !rxsense) + hdmi->phy_mask |= HDMI_PHY_RX_SENSE; + else + hdmi->phy_mask &= ~HDMI_PHY_RX_SENSE; + + if (old_mask != hdmi->phy_mask) + hdmi_writeb(hdmi, hdmi->phy_mask, HDMI_PHY_MASK0); +} + +static void dw_hdmi_phy_setup_hpd(struct dw_hdmi *hdmi, void *data) +{ + /* + * Configure the PHY RX SENSE and HPD interrupts polarities and clear + * any pending interrupt. + */ + hdmi_writeb(hdmi, HDMI_PHY_HPD | HDMI_PHY_RX_SENSE, HDMI_PHY_POL0); + hdmi_writeb(hdmi, HDMI_IH_PHY_STAT0_HPD | HDMI_IH_PHY_STAT0_RX_SENSE, + HDMI_IH_PHY_STAT0); + + /* Enable cable hot plug irq. */ + hdmi_writeb(hdmi, hdmi->phy_mask, HDMI_PHY_MASK0); + + /* Clear and unmute interrupts. */ + hdmi_writeb(hdmi, HDMI_IH_PHY_STAT0_HPD | HDMI_IH_PHY_STAT0_RX_SENSE, + HDMI_IH_PHY_STAT0); + hdmi_writeb(hdmi, ~(HDMI_IH_PHY_STAT0_HPD | HDMI_IH_PHY_STAT0_RX_SENSE), + HDMI_IH_MUTE_PHY_STAT0); +} + static const struct dw_hdmi_phy_ops dw_hdmi_synopsys_phy_ops = { .init = dw_hdmi_phy_init, .disable = dw_hdmi_phy_disable, .read_hpd = dw_hdmi_phy_read_hpd, + .update_hpd = dw_hdmi_phy_update_hpd, + .setup_hpd = dw_hdmi_phy_setup_hpd, }; /* ----------------------------------------------------------------------------- @@ -1148,28 +1288,36 @@ static void hdmi_config_AVI(struct dw_hdmi *hdmi, struct drm_display_mode *mode) /* Initialise info frame from DRM mode */ drm_hdmi_avi_infoframe_from_display_mode(&frame, mode); - if (hdmi->hdmi_data.enc_out_format == YCBCR444) + if (hdmi_bus_fmt_is_yuv444(hdmi->hdmi_data.enc_out_bus_format)) frame.colorspace = HDMI_COLORSPACE_YUV444; - else if (hdmi->hdmi_data.enc_out_format == YCBCR422_8BITS) + else if (hdmi_bus_fmt_is_yuv422(hdmi->hdmi_data.enc_out_bus_format)) frame.colorspace = HDMI_COLORSPACE_YUV422; else frame.colorspace = HDMI_COLORSPACE_RGB; /* Set up colorimetry */ - if (hdmi->hdmi_data.enc_out_format == XVYCC444) { - frame.colorimetry = HDMI_COLORIMETRY_EXTENDED; - if (hdmi->hdmi_data.colorimetry == HDMI_COLORIMETRY_ITU_601) - frame.extended_colorimetry = + switch (hdmi->hdmi_data.enc_out_encoding) { + case V4L2_YCBCR_ENC_601: + if (hdmi->hdmi_data.enc_in_encoding == V4L2_YCBCR_ENC_XV601) + frame.colorimetry = HDMI_COLORIMETRY_EXTENDED; + else + frame.colorimetry = HDMI_COLORIMETRY_ITU_601; + frame.extended_colorimetry = HDMI_EXTENDED_COLORIMETRY_XV_YCC_601; - else /*hdmi->hdmi_data.colorimetry == HDMI_COLORIMETRY_ITU_709*/ - frame.extended_colorimetry = + break; + case V4L2_YCBCR_ENC_709: + if (hdmi->hdmi_data.enc_in_encoding == V4L2_YCBCR_ENC_XV709) + frame.colorimetry = HDMI_COLORIMETRY_EXTENDED; + else + frame.colorimetry = HDMI_COLORIMETRY_ITU_709; + frame.extended_colorimetry = HDMI_EXTENDED_COLORIMETRY_XV_YCC_709; - } else if (hdmi->hdmi_data.enc_out_format != RGB) { - frame.colorimetry = hdmi->hdmi_data.colorimetry; - frame.extended_colorimetry = HDMI_EXTENDED_COLORIMETRY_XV_YCC_601; - } else { /* Carries no data */ - frame.colorimetry = HDMI_COLORIMETRY_NONE; - frame.extended_colorimetry = HDMI_EXTENDED_COLORIMETRY_XV_YCC_601; + break; + default: /* Carries no data */ + frame.colorimetry = HDMI_COLORIMETRY_ITU_601; + frame.extended_colorimetry = + HDMI_EXTENDED_COLORIMETRY_XV_YCC_601; + break; } frame.scan_mode = HDMI_SCAN_MODE_NONE; @@ -1498,19 +1646,30 @@ static int dw_hdmi_setup(struct dw_hdmi *hdmi, struct drm_display_mode *mode) (hdmi->vic == 21) || (hdmi->vic == 22) || (hdmi->vic == 2) || (hdmi->vic == 3) || (hdmi->vic == 17) || (hdmi->vic == 18)) - hdmi->hdmi_data.colorimetry = HDMI_COLORIMETRY_ITU_601; + hdmi->hdmi_data.enc_out_encoding = V4L2_YCBCR_ENC_601; else - hdmi->hdmi_data.colorimetry = HDMI_COLORIMETRY_ITU_709; + hdmi->hdmi_data.enc_out_encoding = V4L2_YCBCR_ENC_709; hdmi->hdmi_data.video_mode.mpixelrepetitionoutput = 0; hdmi->hdmi_data.video_mode.mpixelrepetitioninput = 0; - /* TODO: Get input format from IPU (via FB driver interface) */ - hdmi->hdmi_data.enc_in_format = RGB; + /* TOFIX: Get input format from plat data or fallback to RGB888 */ + if (hdmi->plat_data->input_bus_format) + hdmi->hdmi_data.enc_in_bus_format = + hdmi->plat_data->input_bus_format; + else + hdmi->hdmi_data.enc_in_bus_format = MEDIA_BUS_FMT_RGB888_1X24; + + /* TOFIX: Get input encoding from plat data or fallback to none */ + if (hdmi->plat_data->input_bus_encoding) + hdmi->hdmi_data.enc_in_encoding = + hdmi->plat_data->input_bus_encoding; + else + hdmi->hdmi_data.enc_in_encoding = V4L2_YCBCR_ENC_DEFAULT; - hdmi->hdmi_data.enc_out_format = RGB; + /* TOFIX: Default to RGB888 output format */ + hdmi->hdmi_data.enc_out_bus_format = MEDIA_BUS_FMT_RGB888_1X24; - hdmi->hdmi_data.enc_color_depth = 8; hdmi->hdmi_data.pix_repet_factor = 0; hdmi->hdmi_data.hdcp_enable = 0; hdmi->hdmi_data.video_mode.mdataenablepolarity = true; @@ -1558,8 +1717,7 @@ static int dw_hdmi_setup(struct dw_hdmi *hdmi, struct drm_display_mode *mode) return 0; } -/* Wait until we are registered to enable interrupts */ -static int dw_hdmi_fb_registered(struct dw_hdmi *hdmi) +static void dw_hdmi_setup_i2c(struct dw_hdmi *hdmi) { hdmi_writeb(hdmi, HDMI_PHY_I2CM_INT_ADDR_DONE_POL, HDMI_PHY_I2CM_INT_ADDR); @@ -1567,15 +1725,6 @@ static int dw_hdmi_fb_registered(struct dw_hdmi *hdmi) hdmi_writeb(hdmi, HDMI_PHY_I2CM_CTLINT_ADDR_NAC_POL | HDMI_PHY_I2CM_CTLINT_ADDR_ARBITRATION_POL, HDMI_PHY_I2CM_CTLINT_ADDR); - - /* enable cable hot plug irq */ - hdmi_writeb(hdmi, hdmi->phy_mask, HDMI_PHY_MASK0); - - /* Clear Hotplug interrupts */ - hdmi_writeb(hdmi, HDMI_IH_PHY_STAT0_HPD | HDMI_IH_PHY_STAT0_RX_SENSE, - HDMI_IH_PHY_STAT0); - - return 0; } static void initialize_hdmi_ih_mutes(struct dw_hdmi *hdmi) @@ -1682,15 +1831,10 @@ static void dw_hdmi_update_power(struct dw_hdmi *hdmi) */ static void dw_hdmi_update_phy_mask(struct dw_hdmi *hdmi) { - u8 old_mask = hdmi->phy_mask; - - if (hdmi->force || hdmi->disabled || !hdmi->rxsense) - hdmi->phy_mask |= HDMI_PHY_RX_SENSE; - else - hdmi->phy_mask &= ~HDMI_PHY_RX_SENSE; - - if (old_mask != hdmi->phy_mask) - hdmi_writeb(hdmi, hdmi->phy_mask, HDMI_PHY_MASK0); + if (hdmi->phy.ops->update_hpd) + hdmi->phy.ops->update_hpd(hdmi, hdmi->phy.data, + hdmi->force, hdmi->disabled, + hdmi->rxsense); } static enum drm_connector_status @@ -1882,6 +2026,41 @@ static irqreturn_t dw_hdmi_hardirq(int irq, void *dev_id) return ret; } +void __dw_hdmi_setup_rx_sense(struct dw_hdmi *hdmi, bool hpd, bool rx_sense) +{ + mutex_lock(&hdmi->mutex); + + if (!hdmi->force) { + /* + * If the RX sense status indicates we're disconnected, + * clear the software rxsense status. + */ + if (!rx_sense) + hdmi->rxsense = false; + + /* + * Only set the software rxsense status when both + * rxsense and hpd indicates we're connected. + * This avoids what seems to be bad behaviour in + * at least iMX6S versions of the phy. + */ + if (hpd) + hdmi->rxsense = true; + + dw_hdmi_update_power(hdmi); + dw_hdmi_update_phy_mask(hdmi); + } + mutex_unlock(&hdmi->mutex); +} + +void dw_hdmi_setup_rx_sense(struct device *dev, bool hpd, bool rx_sense) +{ + struct dw_hdmi *hdmi = dev_get_drvdata(dev); + + __dw_hdmi_setup_rx_sense(hdmi, hpd, rx_sense); +} +EXPORT_SYMBOL_GPL(dw_hdmi_setup_rx_sense); + static irqreturn_t dw_hdmi_irq(int irq, void *dev_id) { struct dw_hdmi *hdmi = dev_id; @@ -1914,30 +2093,10 @@ static irqreturn_t dw_hdmi_irq(int irq, void *dev_id) * ask the source to re-read the EDID. */ if (intr_stat & - (HDMI_IH_PHY_STAT0_RX_SENSE | HDMI_IH_PHY_STAT0_HPD)) { - mutex_lock(&hdmi->mutex); - if (!hdmi->force) { - /* - * If the RX sense status indicates we're disconnected, - * clear the software rxsense status. - */ - if (!(phy_stat & HDMI_PHY_RX_SENSE)) - hdmi->rxsense = false; - - /* - * Only set the software rxsense status when both - * rxsense and hpd indicates we're connected. - * This avoids what seems to be bad behaviour in - * at least iMX6S versions of the phy. - */ - if (phy_stat & HDMI_PHY_HPD) - hdmi->rxsense = true; - - dw_hdmi_update_power(hdmi); - dw_hdmi_update_phy_mask(hdmi); - } - mutex_unlock(&hdmi->mutex); - } + (HDMI_IH_PHY_STAT0_RX_SENSE | HDMI_IH_PHY_STAT0_HPD)) + __dw_hdmi_setup_rx_sense(hdmi, + phy_stat & HDMI_PHY_HPD, + phy_stat & HDMI_PHY_RX_SENSE); if (intr_stat & HDMI_IH_PHY_STAT0_HPD) { dev_dbg(hdmi->dev, "EVENT=%s\n", @@ -2204,29 +2363,15 @@ __dw_hdmi_probe(struct platform_device *pdev, hdmi->ddc = NULL; } - /* - * Configure registers related to HDMI interrupt - * generation before registering IRQ. - */ - hdmi_writeb(hdmi, HDMI_PHY_HPD | HDMI_PHY_RX_SENSE, HDMI_PHY_POL0); - - /* Clear Hotplug interrupts */ - hdmi_writeb(hdmi, HDMI_IH_PHY_STAT0_HPD | HDMI_IH_PHY_STAT0_RX_SENSE, - HDMI_IH_PHY_STAT0); - hdmi->bridge.driver_private = hdmi; hdmi->bridge.funcs = &dw_hdmi_bridge_funcs; #ifdef CONFIG_OF hdmi->bridge.of_node = pdev->dev.of_node; #endif - ret = dw_hdmi_fb_registered(hdmi); - if (ret) - goto err_iahb; - - /* Unmute interrupts */ - hdmi_writeb(hdmi, ~(HDMI_IH_PHY_STAT0_HPD | HDMI_IH_PHY_STAT0_RX_SENSE), - HDMI_IH_MUTE_PHY_STAT0); + dw_hdmi_setup_i2c(hdmi); + if (hdmi->phy.ops->setup_hpd) + hdmi->phy.ops->setup_hpd(hdmi, hdmi->phy.data); memset(&pdevinfo, 0, sizeof(pdevinfo)); pdevinfo.parent = dev; diff --git a/drivers/gpu/drm/bridge/tc358767.c b/drivers/gpu/drm/bridge/tc358767.c index de9ffb4..5c26488 100644 --- a/drivers/gpu/drm/bridge/tc358767.c +++ b/drivers/gpu/drm/bridge/tc358767.c @@ -1244,7 +1244,6 @@ static const struct regmap_config tc_regmap_config = { static int tc_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct device *dev = &client->dev; - struct device_node *ep; struct tc_data *tc; int ret; @@ -1255,29 +1254,9 @@ static int tc_probe(struct i2c_client *client, const struct i2c_device_id *id) tc->dev = dev; /* port@2 is the output port */ - ep = of_graph_get_endpoint_by_regs(dev->of_node, 2, -1); - if (ep) { - struct device_node *remote; - - remote = of_graph_get_remote_port_parent(ep); - if (!remote) { - dev_warn(dev, "endpoint %s not connected\n", - ep->full_name); - of_node_put(ep); - return -ENODEV; - } - of_node_put(ep); - tc->panel = of_drm_find_panel(remote); - if (tc->panel) { - dev_dbg(dev, "found panel %s\n", remote->full_name); - } else { - dev_dbg(dev, "waiting for panel %s\n", - remote->full_name); - of_node_put(remote); - return -EPROBE_DEFER; - } - of_node_put(remote); - } + ret = drm_of_find_panel_or_bridge(dev->of_node, 2, 0, &tc->panel, NULL); + if (ret) + return ret; /* Shut down GPIO is optional */ tc->sd_gpio = devm_gpiod_get_optional(dev, "shutdown", GPIOD_OUT_HIGH); diff --git a/drivers/gpu/drm/bridge/ti-tfp410.c b/drivers/gpu/drm/bridge/ti-tfp410.c index 7d519b4..eee4efd 100644 --- a/drivers/gpu/drm/bridge/ti-tfp410.c +++ b/drivers/gpu/drm/bridge/ti-tfp410.c @@ -165,18 +165,13 @@ static irqreturn_t tfp410_hpd_irq_thread(int irq, void *arg) static int tfp410_get_connector_properties(struct tfp410 *dvi) { - struct device_node *ep = NULL, *connector_node = NULL; - struct device_node *ddc_phandle = NULL; + struct device_node *connector_node, *ddc_phandle; int ret = 0; /* port@1 is the connector node */ - ep = of_graph_get_endpoint_by_regs(dvi->dev->of_node, 1, -1); - if (!ep) - goto fail; - - connector_node = of_graph_get_remote_port_parent(ep); + connector_node = of_graph_get_remote_node(dvi->dev->of_node, 1, -1); if (!connector_node) - goto fail; + return -ENODEV; dvi->hpd = fwnode_get_named_gpiod(&connector_node->fwnode, "hpd-gpios", 0, GPIOD_IN, "hpd"); @@ -199,10 +194,10 @@ static int tfp410_get_connector_properties(struct tfp410 *dvi) else ret = -EPROBE_DEFER; + of_node_put(ddc_phandle); + fail: - of_node_put(ep); of_node_put(connector_node); - of_node_put(ddc_phandle); return ret; } diff --git a/drivers/gpu/drm/cirrus/cirrus_mode.c b/drivers/gpu/drm/cirrus/cirrus_mode.c index ed43ab1..53f6f0f 100644 --- a/drivers/gpu/drm/cirrus/cirrus_mode.c +++ b/drivers/gpu/drm/cirrus/cirrus_mode.c @@ -327,7 +327,8 @@ static void cirrus_crtc_commit(struct drm_crtc *crtc) * but it's a requirement that we provide the function */ static int cirrus_crtc_gamma_set(struct drm_crtc *crtc, u16 *red, u16 *green, - u16 *blue, uint32_t size) + u16 *blue, uint32_t size, + struct drm_modeset_acquire_ctx *ctx) { struct cirrus_crtc *cirrus_crtc = to_cirrus_crtc(crtc); int i; diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c index 9b892af..f32506a 100644 --- a/drivers/gpu/drm/drm_atomic.c +++ b/drivers/gpu/drm/drm_atomic.c @@ -1516,19 +1516,9 @@ EXPORT_SYMBOL(drm_atomic_add_affected_planes); void drm_atomic_legacy_backoff(struct drm_atomic_state *state) { struct drm_device *dev = state->dev; - unsigned crtc_mask = 0; - struct drm_crtc *crtc; int ret; bool global = false; - drm_for_each_crtc(crtc, dev) { - if (crtc->acquire_ctx != state->acquire_ctx) - continue; - - crtc_mask |= drm_crtc_mask(crtc); - crtc->acquire_ctx = NULL; - } - if (WARN_ON(dev->mode_config.acquire_ctx == state->acquire_ctx)) { global = true; @@ -1542,10 +1532,6 @@ retry: if (ret) goto retry; - drm_for_each_crtc(crtc, dev) - if (drm_crtc_mask(crtc) & crtc_mask) - crtc->acquire_ctx = state->acquire_ctx; - if (global) dev->mode_config.acquire_ctx = state->acquire_ctx; } @@ -1690,22 +1676,8 @@ static void drm_atomic_print_state(const struct drm_atomic_state *state) drm_atomic_connector_print_state(&p, connector_state); } -/** - * drm_state_dump - dump entire device atomic state - * @dev: the drm device - * @p: where to print the state to - * - * Just for debugging. Drivers might want an option to dump state - * to dmesg in case of error irq's. (Hint, you probably want to - * ratelimit this!) - * - * The caller must drm_modeset_lock_all(), or if this is called - * from error irq handler, it should not be enabled by default. - * (Ie. if you are debugging errors you might not care that this - * is racey. But calling this without all modeset locks held is - * not inherently safe.) - */ -void drm_state_dump(struct drm_device *dev, struct drm_printer *p) +static void __drm_state_dump(struct drm_device *dev, struct drm_printer *p, + bool take_locks) { struct drm_mode_config *config = &dev->mode_config; struct drm_plane *plane; @@ -1716,17 +1688,51 @@ void drm_state_dump(struct drm_device *dev, struct drm_printer *p) if (!drm_core_check_feature(dev, DRIVER_ATOMIC)) return; - list_for_each_entry(plane, &config->plane_list, head) + list_for_each_entry(plane, &config->plane_list, head) { + if (take_locks) + drm_modeset_lock(&plane->mutex, NULL); drm_atomic_plane_print_state(p, plane->state); + if (take_locks) + drm_modeset_unlock(&plane->mutex); + } - list_for_each_entry(crtc, &config->crtc_list, head) + list_for_each_entry(crtc, &config->crtc_list, head) { + if (take_locks) + drm_modeset_lock(&crtc->mutex, NULL); drm_atomic_crtc_print_state(p, crtc->state); + if (take_locks) + drm_modeset_unlock(&crtc->mutex); + } drm_connector_list_iter_begin(dev, &conn_iter); + if (take_locks) + drm_modeset_lock(&dev->mode_config.connection_mutex, NULL); drm_for_each_connector_iter(connector, &conn_iter) drm_atomic_connector_print_state(p, connector->state); + if (take_locks) + drm_modeset_unlock(&dev->mode_config.connection_mutex); drm_connector_list_iter_end(&conn_iter); } + +/** + * drm_state_dump - dump entire device atomic state + * @dev: the drm device + * @p: where to print the state to + * + * Just for debugging. Drivers might want an option to dump state + * to dmesg in case of error irq's. (Hint, you probably want to + * ratelimit this!) + * + * The caller must drm_modeset_lock_all(), or if this is called + * from error irq handler, it should not be enabled by default. + * (Ie. if you are debugging errors you might not care that this + * is racey. But calling this without all modeset locks held is + * not inherently safe.) + */ +void drm_state_dump(struct drm_device *dev, struct drm_printer *p) +{ + __drm_state_dump(dev, p, false); +} EXPORT_SYMBOL(drm_state_dump); #ifdef CONFIG_DEBUG_FS @@ -1736,9 +1742,7 @@ static int drm_state_info(struct seq_file *m, void *data) struct drm_device *dev = node->minor->dev; struct drm_printer p = drm_seq_file_printer(m); - drm_modeset_lock_all(dev); - drm_state_dump(dev, &p); - drm_modeset_unlock_all(dev); + __drm_state_dump(dev, &p, true); return 0; } @@ -2077,94 +2081,6 @@ static void complete_crtc_signaling(struct drm_device *dev, kfree(fence_state); } -int drm_atomic_remove_fb(struct drm_framebuffer *fb) -{ - struct drm_modeset_acquire_ctx ctx; - struct drm_device *dev = fb->dev; - struct drm_atomic_state *state; - struct drm_plane *plane; - struct drm_connector *conn; - struct drm_connector_state *conn_state; - int i, ret = 0; - unsigned plane_mask; - - state = drm_atomic_state_alloc(dev); - if (!state) - return -ENOMEM; - - drm_modeset_acquire_init(&ctx, 0); - state->acquire_ctx = &ctx; - -retry: - plane_mask = 0; - ret = drm_modeset_lock_all_ctx(dev, &ctx); - if (ret) - goto unlock; - - drm_for_each_plane(plane, dev) { - struct drm_plane_state *plane_state; - - if (plane->state->fb != fb) - continue; - - plane_state = drm_atomic_get_plane_state(state, plane); - if (IS_ERR(plane_state)) { - ret = PTR_ERR(plane_state); - goto unlock; - } - - if (plane_state->crtc->primary == plane) { - struct drm_crtc_state *crtc_state; - - crtc_state = drm_atomic_get_existing_crtc_state(state, plane_state->crtc); - - ret = drm_atomic_add_affected_connectors(state, plane_state->crtc); - if (ret) - goto unlock; - - crtc_state->active = false; - ret = drm_atomic_set_mode_for_crtc(crtc_state, NULL); - if (ret) - goto unlock; - } - - drm_atomic_set_fb_for_plane(plane_state, NULL); - ret = drm_atomic_set_crtc_for_plane(plane_state, NULL); - if (ret) - goto unlock; - - plane_mask |= BIT(drm_plane_index(plane)); - - plane->old_fb = plane->fb; - } - - for_each_connector_in_state(state, conn, conn_state, i) { - ret = drm_atomic_set_crtc_for_connector(conn_state, NULL); - - if (ret) - goto unlock; - } - - if (plane_mask) - ret = drm_atomic_commit(state); - -unlock: - if (plane_mask) - drm_atomic_clean_old_fb(dev, plane_mask, ret); - - if (ret == -EDEADLK) { - drm_modeset_backoff(&ctx); - goto retry; - } - - drm_atomic_state_put(state); - - drm_modeset_drop_locks(&ctx); - drm_modeset_acquire_fini(&ctx); - - return ret; -} - int drm_mode_atomic_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv) { diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c index c3994b4..8be9719 100644 --- a/drivers/gpu/drm/drm_atomic_helper.c +++ b/drivers/gpu/drm/drm_atomic_helper.c @@ -459,10 +459,20 @@ mode_fixup(struct drm_atomic_state *state) * * Check the state object to see if the requested state is physically possible. * This does all the crtc and connector related computations for an atomic - * update and adds any additional connectors needed for full modesets and calls - * down into &drm_crtc_helper_funcs.mode_fixup and - * &drm_encoder_helper_funcs.mode_fixup or - * &drm_encoder_helper_funcs.atomic_check functions of the driver backend. + * update and adds any additional connectors needed for full modesets. It calls + * the various per-object callbacks in the follow order: + * + * 1. &drm_connector_helper_funcs.atomic_best_encoder for determining the new encoder. + * 2. &drm_connector_helper_funcs.atomic_check to validate the connector state. + * 3. If it's determined a modeset is needed then all connectors on the affected crtc + * crtc are added and &drm_connector_helper_funcs.atomic_check is run on them. + * 4. &drm_bridge_funcs.mode_fixup is called on all encoder bridges. + * 5. &drm_encoder_helper_funcs.atomic_check is called to validate any encoder state. + * This function is only called when the encoder will be part of a configured crtc, + * it must not be used for implementing connector property validation. + * If this function is NULL, &drm_atomic_encoder_helper_funcs.mode_fixup is called + * instead. + * 6. &drm_crtc_helper_funcs.mode_fixup is called last, to fix up the mode with crtc constraints. * * &drm_crtc_state.mode_changed is set when the input mode is changed. * &drm_crtc_state.connectors_changed is set when a connector is added or @@ -492,8 +502,12 @@ drm_atomic_helper_check_modeset(struct drm_device *dev, struct drm_connector *connector; struct drm_connector_state *old_connector_state, *new_connector_state; int i, ret; + unsigned connectors_mask = 0; for_each_oldnew_crtc_in_state(state, crtc, old_crtc_state, new_crtc_state, i) { + bool has_connectors = + !!new_crtc_state->connector_mask; + if (!drm_mode_equal(&old_crtc_state->mode, &new_crtc_state->mode)) { DRM_DEBUG_ATOMIC("[CRTC:%d:%s] mode changed\n", crtc->base.id, crtc->name); @@ -515,13 +529,28 @@ drm_atomic_helper_check_modeset(struct drm_device *dev, new_crtc_state->mode_changed = true; new_crtc_state->connectors_changed = true; } + + if (old_crtc_state->active != new_crtc_state->active) { + DRM_DEBUG_ATOMIC("[CRTC:%d:%s] active changed\n", + crtc->base.id, crtc->name); + new_crtc_state->active_changed = true; + } + + if (new_crtc_state->enable != has_connectors) { + DRM_DEBUG_ATOMIC("[CRTC:%d:%s] enabled/connectors mismatch\n", + crtc->base.id, crtc->name); + + return -EINVAL; + } } - ret = handle_conflicting_encoders(state, state->legacy_set_config); + ret = handle_conflicting_encoders(state, false); if (ret) return ret; for_each_oldnew_connector_in_state(state, connector, old_connector_state, new_connector_state, i) { + const struct drm_connector_helper_funcs *funcs = connector->helper_private; + /* * This only sets crtc->connectors_changed for routing changes, * drivers must set crtc->connectors_changed themselves when @@ -539,6 +568,13 @@ drm_atomic_helper_check_modeset(struct drm_device *dev, new_connector_state->link_status) new_crtc_state->connectors_changed = true; } + + if (funcs->atomic_check) + ret = funcs->atomic_check(connector, new_connector_state); + if (ret) + return ret; + + connectors_mask += BIT(i); } /* @@ -548,20 +584,6 @@ drm_atomic_helper_check_modeset(struct drm_device *dev, * crtc only changed its mode but has the same set of connectors. */ for_each_oldnew_crtc_in_state(state, crtc, old_crtc_state, new_crtc_state, i) { - bool has_connectors = - !!new_crtc_state->connector_mask; - - /* - * We must set ->active_changed after walking connectors for - * otherwise an update that only changes active would result in - * a full modeset because update_connector_routing force that. - */ - if (old_crtc_state->active != new_crtc_state->active) { - DRM_DEBUG_ATOMIC("[CRTC:%d:%s] active changed\n", - crtc->base.id, crtc->name); - new_crtc_state->active_changed = true; - } - if (!drm_atomic_crtc_needs_modeset(new_crtc_state)) continue; @@ -577,13 +599,22 @@ drm_atomic_helper_check_modeset(struct drm_device *dev, ret = drm_atomic_add_affected_planes(state, crtc); if (ret != 0) return ret; + } - if (new_crtc_state->enable != has_connectors) { - DRM_DEBUG_ATOMIC("[CRTC:%d:%s] enabled/connectors mismatch\n", - crtc->base.id, crtc->name); + /* + * Iterate over all connectors again, to make sure atomic_check() + * has been called on them when a modeset is forced. + */ + for_each_oldnew_connector_in_state(state, connector, old_connector_state, new_connector_state, i) { + const struct drm_connector_helper_funcs *funcs = connector->helper_private; - return -EINVAL; - } + if (connectors_mask & BIT(i)) + continue; + + if (funcs->atomic_check) + ret = funcs->atomic_check(connector, new_connector_state); + if (ret) + return ret; } return mode_fixup(state); @@ -2289,12 +2320,15 @@ int drm_atomic_helper_set_config(struct drm_mode_set *set, if (!state) return -ENOMEM; - state->legacy_set_config = true; state->acquire_ctx = ctx; ret = __drm_atomic_helper_set_config(set, state); if (ret != 0) goto fail; + ret = handle_conflicting_encoders(state, true); + if (ret) + return ret; + ret = drm_atomic_commit(state); fail: @@ -2622,14 +2656,22 @@ EXPORT_SYMBOL(drm_atomic_helper_commit_duplicated_state); int drm_atomic_helper_resume(struct drm_device *dev, struct drm_atomic_state *state) { - struct drm_mode_config *config = &dev->mode_config; + struct drm_modeset_acquire_ctx ctx; int err; drm_mode_config_reset(dev); - drm_modeset_lock_all(dev); - err = drm_atomic_helper_commit_duplicated_state(state, config->acquire_ctx); - drm_modeset_unlock_all(dev); + drm_modeset_acquire_init(&ctx, 0); + while (1) { + err = drm_atomic_helper_commit_duplicated_state(state, &ctx); + if (err != -EDEADLK) + break; + + drm_modeset_backoff(&ctx); + } + + drm_modeset_drop_locks(&ctx); + drm_modeset_acquire_fini(&ctx); return err; } @@ -2975,7 +3017,7 @@ int drm_atomic_helper_connector_dpms(struct drm_connector *connector, if (!state) return -ENOMEM; - state->acquire_ctx = drm_modeset_legacy_acquire_ctx(crtc); + state->acquire_ctx = crtc->dev->mode_config.acquire_ctx; retry: crtc_state = drm_atomic_get_crtc_state(state, crtc); if (IS_ERR(crtc_state)) { @@ -3471,6 +3513,7 @@ EXPORT_SYMBOL(drm_atomic_helper_connector_destroy_state); * @green: green correction table * @blue: green correction table * @size: size of the tables + * @ctx: lock acquire context * * Implements support for legacy gamma correction table for drivers * that support color management through the DEGAMMA_LUT/GAMMA_LUT @@ -3478,7 +3521,8 @@ EXPORT_SYMBOL(drm_atomic_helper_connector_destroy_state); */ int drm_atomic_helper_legacy_gamma_set(struct drm_crtc *crtc, u16 *red, u16 *green, u16 *blue, - uint32_t size) + uint32_t size, + struct drm_modeset_acquire_ctx *ctx) { struct drm_device *dev = crtc->dev; struct drm_mode_config *config = &dev->mode_config; @@ -3509,8 +3553,7 @@ int drm_atomic_helper_legacy_gamma_set(struct drm_crtc *crtc, blob_data[i].blue = blue[i]; } - state->acquire_ctx = crtc->dev->mode_config.acquire_ctx; -retry: + state->acquire_ctx = ctx; crtc_state = drm_atomic_get_crtc_state(state, crtc); if (IS_ERR(crtc_state)) { ret = PTR_ERR(crtc_state); @@ -3534,18 +3577,10 @@ retry: goto fail; ret = drm_atomic_commit(state); -fail: - if (ret == -EDEADLK) - goto backoff; +fail: drm_atomic_state_put(state); drm_property_blob_put(blob); return ret; - -backoff: - drm_atomic_state_clear(state); - drm_atomic_legacy_backoff(state); - - goto retry; } EXPORT_SYMBOL(drm_atomic_helper_legacy_gamma_set); diff --git a/drivers/gpu/drm/drm_color_mgmt.c b/drivers/gpu/drm/drm_color_mgmt.c index cc23b9a..533f3a3 100644 --- a/drivers/gpu/drm/drm_color_mgmt.c +++ b/drivers/gpu/drm/drm_color_mgmt.c @@ -218,28 +218,28 @@ int drm_mode_gamma_set_ioctl(struct drm_device *dev, struct drm_crtc *crtc; void *r_base, *g_base, *b_base; int size; + struct drm_modeset_acquire_ctx ctx; int ret = 0; if (!drm_core_check_feature(dev, DRIVER_MODESET)) return -EINVAL; - drm_modeset_lock_all(dev); crtc = drm_crtc_find(dev, crtc_lut->crtc_id); - if (!crtc) { - ret = -ENOENT; - goto out; - } + if (!crtc) + return -ENOENT; - if (crtc->funcs->gamma_set == NULL) { - ret = -ENOSYS; - goto out; - } + if (crtc->funcs->gamma_set == NULL) + return -ENOSYS; /* memcpy into gamma store */ - if (crtc_lut->gamma_size != crtc->gamma_size) { - ret = -EINVAL; + if (crtc_lut->gamma_size != crtc->gamma_size) + return -EINVAL; + + drm_modeset_acquire_init(&ctx, 0); +retry: + ret = drm_modeset_lock_all_ctx(dev, &ctx); + if (ret) goto out; - } size = crtc_lut->gamma_size * (sizeof(uint16_t)); r_base = crtc->gamma_store; @@ -260,10 +260,17 @@ int drm_mode_gamma_set_ioctl(struct drm_device *dev, goto out; } - ret = crtc->funcs->gamma_set(crtc, r_base, g_base, b_base, crtc->gamma_size); + ret = crtc->funcs->gamma_set(crtc, r_base, g_base, b_base, + crtc->gamma_size, &ctx); out: - drm_modeset_unlock_all(dev); + if (ret == -EDEADLK) { + drm_modeset_backoff(&ctx); + goto retry; + } + drm_modeset_drop_locks(&ctx); + drm_modeset_acquire_fini(&ctx); + return ret; } @@ -295,19 +302,15 @@ int drm_mode_gamma_get_ioctl(struct drm_device *dev, if (!drm_core_check_feature(dev, DRIVER_MODESET)) return -EINVAL; - drm_modeset_lock_all(dev); crtc = drm_crtc_find(dev, crtc_lut->crtc_id); - if (!crtc) { - ret = -ENOENT; - goto out; - } + if (!crtc) + return -ENOENT; /* memcpy into gamma store */ - if (crtc_lut->gamma_size != crtc->gamma_size) { - ret = -EINVAL; - goto out; - } + if (crtc_lut->gamma_size != crtc->gamma_size) + return -EINVAL; + drm_modeset_lock(&crtc->mutex, NULL); size = crtc_lut->gamma_size * (sizeof(uint16_t)); r_base = crtc->gamma_store; if (copy_to_user((void __user *)(unsigned long)crtc_lut->red, r_base, size)) { @@ -327,6 +330,6 @@ int drm_mode_gamma_get_ioctl(struct drm_device *dev, goto out; } out: - drm_modeset_unlock_all(dev); + drm_modeset_unlock(&crtc->mutex); return ret; } diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c index d69e180..5af25ce 100644 --- a/drivers/gpu/drm/drm_crtc.c +++ b/drivers/gpu/drm/drm_crtc.c @@ -576,6 +576,7 @@ int drm_mode_setcrtc(struct drm_device *dev, void *data, } DRM_DEBUG_KMS("[CRTC:%d:%s]\n", crtc->base.id, crtc->name); + mutex_lock(&crtc->dev->mode_config.mutex); drm_modeset_acquire_init(&ctx, 0); retry: ret = drm_modeset_lock_all_ctx(crtc->dev, &ctx); @@ -721,6 +722,7 @@ out: } drm_modeset_drop_locks(&ctx); drm_modeset_acquire_fini(&ctx); + mutex_unlock(&crtc->dev->mode_config.mutex); return ret; } diff --git a/drivers/gpu/drm/drm_crtc_internal.h b/drivers/gpu/drm/drm_crtc_internal.h index 8c04275..d077c54 100644 --- a/drivers/gpu/drm/drm_crtc_internal.h +++ b/drivers/gpu/drm/drm_crtc_internal.h @@ -182,7 +182,6 @@ int drm_atomic_get_property(struct drm_mode_object *obj, struct drm_property *property, uint64_t *val); int drm_mode_atomic_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv); -int drm_atomic_remove_fb(struct drm_framebuffer *fb); /* drm_plane.c */ diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c index e65becd..a0ea324 100644 --- a/drivers/gpu/drm/drm_fb_helper.c +++ b/drivers/gpu/drm/drm_fb_helper.c @@ -109,6 +109,42 @@ static DEFINE_MUTEX(kernel_fb_helper_lock); for (({ lockdep_assert_held(&(fbh)->dev->mode_config.mutex); }), \ i__ = 0; i__ < (fbh)->connector_count; i__++) +int drm_fb_helper_add_one_connector(struct drm_fb_helper *fb_helper, + struct drm_connector *connector) +{ + struct drm_fb_helper_connector *fb_conn; + struct drm_fb_helper_connector **temp; + unsigned int count; + + if (!drm_fbdev_emulation) + return 0; + + WARN_ON(!mutex_is_locked(&fb_helper->dev->mode_config.mutex)); + + count = fb_helper->connector_count + 1; + + if (count > fb_helper->connector_info_alloc_count) { + size_t size = count * sizeof(fb_conn); + + temp = krealloc(fb_helper->connector_info, size, GFP_KERNEL); + if (!temp) + return -ENOMEM; + + fb_helper->connector_info_alloc_count = count; + fb_helper->connector_info = temp; + } + + fb_conn = kzalloc(sizeof(*fb_conn), GFP_KERNEL); + if (!fb_conn) + return -ENOMEM; + + drm_connector_get(connector); + fb_conn->connector = connector; + fb_helper->connector_info[fb_helper->connector_count++] = fb_conn; + return 0; +} +EXPORT_SYMBOL(drm_fb_helper_add_one_connector); + /** * drm_fb_helper_single_add_all_connectors() - add all connectors to fbdev * emulation helper @@ -162,36 +198,6 @@ out: } EXPORT_SYMBOL(drm_fb_helper_single_add_all_connectors); -int drm_fb_helper_add_one_connector(struct drm_fb_helper *fb_helper, struct drm_connector *connector) -{ - struct drm_fb_helper_connector **temp; - struct drm_fb_helper_connector *fb_helper_connector; - - if (!drm_fbdev_emulation) - return 0; - - WARN_ON(!mutex_is_locked(&fb_helper->dev->mode_config.mutex)); - if (fb_helper->connector_count + 1 > fb_helper->connector_info_alloc_count) { - temp = krealloc(fb_helper->connector_info, sizeof(struct drm_fb_helper_connector *) * (fb_helper->connector_count + 1), GFP_KERNEL); - if (!temp) - return -ENOMEM; - - fb_helper->connector_info_alloc_count = fb_helper->connector_count + 1; - fb_helper->connector_info = temp; - } - - - fb_helper_connector = kzalloc(sizeof(struct drm_fb_helper_connector), GFP_KERNEL); - if (!fb_helper_connector) - return -ENOMEM; - - drm_connector_get(connector); - fb_helper_connector->connector = connector; - fb_helper->connector_info[fb_helper->connector_count++] = fb_helper_connector; - return 0; -} -EXPORT_SYMBOL(drm_fb_helper_add_one_connector); - int drm_fb_helper_remove_one_connector(struct drm_fb_helper *fb_helper, struct drm_connector *connector) { @@ -213,9 +219,9 @@ int drm_fb_helper_remove_one_connector(struct drm_fb_helper *fb_helper, fb_helper_connector = fb_helper->connector_info[i]; drm_connector_put(fb_helper_connector->connector); - for (j = i + 1; j < fb_helper->connector_count; j++) { + for (j = i + 1; j < fb_helper->connector_count; j++) fb_helper->connector_info[j - 1] = fb_helper->connector_info[j]; - } + fb_helper->connector_count--; kfree(fb_helper_connector); @@ -250,7 +256,8 @@ static void drm_fb_helper_restore_lut_atomic(struct drm_crtc *crtc) g_base = r_base + crtc->gamma_size; b_base = g_base + crtc->gamma_size; - crtc->funcs->gamma_set(crtc, r_base, g_base, b_base, crtc->gamma_size); + crtc->funcs->gamma_set(crtc, r_base, g_base, b_base, + crtc->gamma_size, NULL); } /** @@ -275,6 +282,9 @@ int drm_fb_helper_debug_enter(struct fb_info *info) if (funcs->mode_set_base_atomic == NULL) continue; + if (drm_drv_uses_atomic_modeset(mode_set->crtc->dev)) + continue; + drm_fb_helper_save_lut_atomic(mode_set->crtc, helper); funcs->mode_set_base_atomic(mode_set->crtc, mode_set->fb, @@ -316,6 +326,7 @@ int drm_fb_helper_debug_leave(struct fb_info *info) for (i = 0; i < helper->crtc_count; i++) { struct drm_mode_set *mode_set = &helper->crtc_info[i].mode_set; + crtc = mode_set->crtc; funcs = crtc->helper_private; fb = drm_mode_config_fb(crtc); @@ -331,6 +342,9 @@ int drm_fb_helper_debug_leave(struct fb_info *info) if (funcs->mode_set_base_atomic == NULL) continue; + if (drm_drv_uses_atomic_modeset(crtc->dev)) + continue; + drm_fb_helper_restore_lut_atomic(mode_set->crtc); funcs->mode_set_base_atomic(mode_set->crtc, fb, crtc->x, crtc->y, LEAVE_ATOMIC_MODE_SET); @@ -346,7 +360,7 @@ static int restore_fbdev_mode_atomic(struct drm_fb_helper *fb_helper) struct drm_plane *plane; struct drm_atomic_state *state; int i, ret; - unsigned plane_mask; + unsigned int plane_mask; state = drm_atomic_state_alloc(dev); if (!state) @@ -378,7 +392,7 @@ retry: goto fail; } - for(i = 0; i < fb_helper->crtc_count; i++) { + for (i = 0; i < fb_helper->crtc_count; i++) { struct drm_mode_set *mode_set = &fb_helper->crtc_info[i].mode_set; ret = __drm_atomic_helper_set_config(mode_set, state); @@ -404,17 +418,12 @@ backoff: goto retry; } -static int restore_fbdev_mode(struct drm_fb_helper *fb_helper) +static int restore_fbdev_mode_legacy(struct drm_fb_helper *fb_helper) { struct drm_device *dev = fb_helper->dev; struct drm_plane *plane; int i; - drm_warn_on_modeset_not_all_locked(dev); - - if (drm_drv_uses_atomic_modeset(dev)) - return restore_fbdev_mode_atomic(fb_helper); - drm_for_each_plane(plane, dev) { if (plane->type != DRM_PLANE_TYPE_PRIMARY) drm_plane_force_disable(plane); @@ -448,6 +457,18 @@ static int restore_fbdev_mode(struct drm_fb_helper *fb_helper) return 0; } +static int restore_fbdev_mode(struct drm_fb_helper *fb_helper) +{ + struct drm_device *dev = fb_helper->dev; + + drm_warn_on_modeset_not_all_locked(dev); + + if (drm_drv_uses_atomic_modeset(dev)) + return restore_fbdev_mode_atomic(fb_helper); + else + return restore_fbdev_mode_legacy(fb_helper); +} + /** * drm_fb_helper_restore_fbdev_mode_unlocked - restore fbdev configuration * @fb_helper: fbcon to restore @@ -488,8 +509,10 @@ static bool drm_fb_helper_is_bound(struct drm_fb_helper *fb_helper) struct drm_crtc *crtc; int bound = 0, crtcs_bound = 0; - /* Sometimes user space wants everything disabled, so don't steal the - * display if there's a master. */ + /* + * Sometimes user space wants everything disabled, so don't steal the + * display if there's a master. + */ if (READ_ONCE(dev->master)) return false; @@ -537,6 +560,7 @@ static bool drm_fb_helper_force_kernel_mode(void) static void drm_fb_helper_restore_work_fn(struct work_struct *ignored) { bool ret; + ret = drm_fb_helper_force_kernel_mode(); if (ret == true) DRM_ERROR("Failed to restore crtc configuration\n"); @@ -870,9 +894,8 @@ void drm_fb_helper_fini(struct drm_fb_helper *fb_helper) mutex_lock(&kernel_fb_helper_lock); if (!list_empty(&fb_helper->kernel_fb_list)) { list_del(&fb_helper->kernel_fb_list); - if (list_empty(&kernel_fb_helper_list)) { + if (list_empty(&kernel_fb_helper_list)) unregister_sysrq_key('v', &sysrq_drm_fb_helper_restore_op); - } } mutex_unlock(&kernel_fb_helper_lock); @@ -1165,6 +1188,7 @@ static int setcolreg(struct drm_crtc *crtc, u16 red, u16 green, (blue << info->var.blue.offset); if (info->var.transp.length > 0) { u32 mask = (1 << info->var.transp.length) - 1; + mask <<= info->var.transp.offset; value |= mask; } @@ -1447,7 +1471,7 @@ static int pan_display_atomic(struct fb_var_screeninfo *var, struct drm_atomic_state *state; struct drm_plane *plane; int i, ret; - unsigned plane_mask; + unsigned int plane_mask; state = drm_atomic_state_alloc(dev); if (!state) @@ -1456,7 +1480,7 @@ static int pan_display_atomic(struct fb_var_screeninfo *var, state->acquire_ctx = dev->mode_config.acquire_ctx; retry: plane_mask = 0; - for(i = 0; i < fb_helper->crtc_count; i++) { + for (i = 0; i < fb_helper->crtc_count; i++) { struct drm_mode_set *mode_set; mode_set = &fb_helper->crtc_info[i].mode_set; @@ -1496,34 +1520,14 @@ backoff: goto retry; } -/** - * drm_fb_helper_pan_display - implementation for &fb_ops.fb_pan_display - * @var: updated screen information - * @info: fbdev registered by the helper - */ -int drm_fb_helper_pan_display(struct fb_var_screeninfo *var, +static int pan_display_legacy(struct fb_var_screeninfo *var, struct fb_info *info) { struct drm_fb_helper *fb_helper = info->par; - struct drm_device *dev = fb_helper->dev; struct drm_mode_set *modeset; int ret = 0; int i; - if (oops_in_progress) - return -EBUSY; - - drm_modeset_lock_all(dev); - if (!drm_fb_helper_is_bound(fb_helper)) { - drm_modeset_unlock_all(dev); - return -EBUSY; - } - - if (drm_drv_uses_atomic_modeset(dev)) { - ret = pan_display_atomic(var, info); - goto unlock; - } - for (i = 0; i < fb_helper->crtc_count; i++) { modeset = &fb_helper->crtc_info[i].mode_set; @@ -1538,8 +1542,37 @@ int drm_fb_helper_pan_display(struct fb_var_screeninfo *var, } } } -unlock: + + return ret; +} + +/** + * drm_fb_helper_pan_display - implementation for &fb_ops.fb_pan_display + * @var: updated screen information + * @info: fbdev registered by the helper + */ +int drm_fb_helper_pan_display(struct fb_var_screeninfo *var, + struct fb_info *info) +{ + struct drm_fb_helper *fb_helper = info->par; + struct drm_device *dev = fb_helper->dev; + int ret; + + if (oops_in_progress) + return -EBUSY; + + drm_modeset_lock_all(dev); + if (!drm_fb_helper_is_bound(fb_helper)) { + drm_modeset_unlock_all(dev); + return -EBUSY; + } + + if (drm_drv_uses_atomic_modeset(dev)) + ret = pan_display_atomic(var, info); + else + ret = pan_display_legacy(var, info); drm_modeset_unlock_all(dev); + return ret; } EXPORT_SYMBOL(drm_fb_helper_pan_display); @@ -1561,11 +1594,10 @@ static int drm_fb_helper_single_fb_probe(struct drm_fb_helper *fb_helper, memset(&sizes, 0, sizeof(struct drm_fb_helper_surface_size)); sizes.surface_depth = 24; sizes.surface_bpp = 32; - sizes.fb_width = (unsigned)-1; - sizes.fb_height = (unsigned)-1; + sizes.fb_width = (u32)-1; + sizes.fb_height = (u32)-1; - /* if driver picks 8 or 16 by default use that - for both depth/bpp */ + /* if driver picks 8 or 16 by default use that for both depth/bpp */ if (preferred_bpp != sizes.surface_bpp) sizes.surface_depth = sizes.surface_bpp = preferred_bpp; @@ -1630,6 +1662,7 @@ static int drm_fb_helper_single_fb_probe(struct drm_fb_helper *fb_helper, for (j = 0; j < mode_set->num_connectors; j++) { struct drm_connector *connector = mode_set->connectors[j]; + if (connector->has_tile) { lasth = (connector->tile_h_loc == (connector->num_h_tile - 1)); lastv = (connector->tile_v_loc == (connector->num_v_tile - 1)); @@ -1645,8 +1678,10 @@ static int drm_fb_helper_single_fb_probe(struct drm_fb_helper *fb_helper, } if (crtc_count == 0 || sizes.fb_width == -1 || sizes.fb_height == -1) { - /* hmm everyone went away - assume VGA cable just fell out - and will come back later. */ + /* + * hmm everyone went away - assume VGA cable just fell out + * and will come back later. + */ DRM_INFO("Cannot find any crtc or sizes - going 1024x768\n"); sizes.fb_width = sizes.surface_width = 1024; sizes.fb_height = sizes.surface_height = 768; @@ -1703,7 +1738,6 @@ void drm_fb_helper_fill_fix(struct fb_info *info, uint32_t pitch, info->fix.accel = FB_ACCEL_NONE; info->fix.line_length = pitch; - return; } EXPORT_SYMBOL(drm_fb_helper_fill_fix); @@ -1725,6 +1759,7 @@ void drm_fb_helper_fill_var(struct fb_info *info, struct drm_fb_helper *fb_helpe uint32_t fb_width, uint32_t fb_height) { struct drm_framebuffer *fb = fb_helper->fb; + info->pseudo_palette = fb_helper->pseudo_palette; info->var.xres_virtual = fb->width; info->var.yres_virtual = fb->height; @@ -2057,13 +2092,15 @@ retry: continue; } else { - if (fb_helper_conn->connector->tile_h_loc != tile_pass -1 && + if (fb_helper_conn->connector->tile_h_loc != tile_pass - 1 && fb_helper_conn->connector->tile_v_loc != tile_pass - 1) /* if this tile_pass doesn't cover any of the tiles - keep going */ continue; - /* find the tile offsets for this pass - need - to find all tiles left and above */ + /* + * find the tile offsets for this pass - need to find + * all tiles left and above + */ drm_get_tile_offsets(fb_helper, modes, offsets, i, fb_helper_conn->connector->tile_h_loc, fb_helper_conn->connector->tile_v_loc); } @@ -2147,8 +2184,10 @@ static int drm_pick_crtcs(struct drm_fb_helper *fb_helper, if (!encoder) goto out; - /* select a crtc for this connector and then attempt to configure - remaining connectors */ + /* + * select a crtc for this connector and then attempt to configure + * remaining connectors + */ for (c = 0; c < fb_helper->crtc_count; c++) { crtc = &fb_helper->crtc_info[c]; diff --git a/drivers/gpu/drm/drm_framebuffer.c b/drivers/gpu/drm/drm_framebuffer.c index e8f9c13..fc8ef42 100644 --- a/drivers/gpu/drm/drm_framebuffer.c +++ b/drivers/gpu/drm/drm_framebuffer.c @@ -24,6 +24,7 @@ #include <drm/drmP.h> #include <drm/drm_auth.h> #include <drm/drm_framebuffer.h> +#include <drm/drm_atomic.h> #include "drm_crtc_internal.h" @@ -755,6 +756,117 @@ void drm_framebuffer_cleanup(struct drm_framebuffer *fb) } EXPORT_SYMBOL(drm_framebuffer_cleanup); +static int atomic_remove_fb(struct drm_framebuffer *fb) +{ + struct drm_modeset_acquire_ctx ctx; + struct drm_device *dev = fb->dev; + struct drm_atomic_state *state; + struct drm_plane *plane; + struct drm_connector *conn; + struct drm_connector_state *conn_state; + int i, ret = 0; + unsigned plane_mask; + + state = drm_atomic_state_alloc(dev); + if (!state) + return -ENOMEM; + + drm_modeset_acquire_init(&ctx, 0); + state->acquire_ctx = &ctx; + +retry: + plane_mask = 0; + ret = drm_modeset_lock_all_ctx(dev, &ctx); + if (ret) + goto unlock; + + drm_for_each_plane(plane, dev) { + struct drm_plane_state *plane_state; + + if (plane->state->fb != fb) + continue; + + plane_state = drm_atomic_get_plane_state(state, plane); + if (IS_ERR(plane_state)) { + ret = PTR_ERR(plane_state); + goto unlock; + } + + if (plane_state->crtc->primary == plane) { + struct drm_crtc_state *crtc_state; + + crtc_state = drm_atomic_get_existing_crtc_state(state, plane_state->crtc); + + ret = drm_atomic_add_affected_connectors(state, plane_state->crtc); + if (ret) + goto unlock; + + crtc_state->active = false; + ret = drm_atomic_set_mode_for_crtc(crtc_state, NULL); + if (ret) + goto unlock; + } + + drm_atomic_set_fb_for_plane(plane_state, NULL); + ret = drm_atomic_set_crtc_for_plane(plane_state, NULL); + if (ret) + goto unlock; + + plane_mask |= BIT(drm_plane_index(plane)); + + plane->old_fb = plane->fb; + } + + for_each_connector_in_state(state, conn, conn_state, i) { + ret = drm_atomic_set_crtc_for_connector(conn_state, NULL); + + if (ret) + goto unlock; + } + + if (plane_mask) + ret = drm_atomic_commit(state); + +unlock: + if (plane_mask) + drm_atomic_clean_old_fb(dev, plane_mask, ret); + + if (ret == -EDEADLK) { + drm_modeset_backoff(&ctx); + goto retry; + } + + drm_atomic_state_put(state); + + drm_modeset_drop_locks(&ctx); + drm_modeset_acquire_fini(&ctx); + + return ret; +} + +static void legacy_remove_fb(struct drm_framebuffer *fb) +{ + struct drm_device *dev = fb->dev; + struct drm_crtc *crtc; + struct drm_plane *plane; + + drm_modeset_lock_all(dev); + /* remove from any CRTC */ + drm_for_each_crtc(crtc, dev) { + if (crtc->primary->fb == fb) { + /* should turn off the crtc */ + if (drm_crtc_force_disable(crtc)) + DRM_ERROR("failed to reset crtc %p when fb was deleted\n", crtc); + } + } + + drm_for_each_plane(plane, dev) { + if (plane->fb == fb) + drm_plane_force_disable(plane); + } + drm_modeset_unlock_all(dev); +} + /** * drm_framebuffer_remove - remove and unreference a framebuffer object * @fb: framebuffer to remove @@ -770,8 +882,6 @@ EXPORT_SYMBOL(drm_framebuffer_cleanup); void drm_framebuffer_remove(struct drm_framebuffer *fb) { struct drm_device *dev; - struct drm_crtc *crtc; - struct drm_plane *plane; if (!fb) return; @@ -797,29 +907,12 @@ void drm_framebuffer_remove(struct drm_framebuffer *fb) */ if (drm_framebuffer_read_refcount(fb) > 1) { if (drm_drv_uses_atomic_modeset(dev)) { - int ret = drm_atomic_remove_fb(fb); + int ret = atomic_remove_fb(fb); WARN(ret, "atomic remove_fb failed with %i\n", ret); - goto out; - } - - drm_modeset_lock_all(dev); - /* remove from any CRTC */ - drm_for_each_crtc(crtc, dev) { - if (crtc->primary->fb == fb) { - /* should turn off the crtc */ - if (drm_crtc_force_disable(crtc)) - DRM_ERROR("failed to reset crtc %p when fb was deleted\n", crtc); - } - } - - drm_for_each_plane(plane, dev) { - if (plane->fb == fb) - drm_plane_force_disable(plane); - } - drm_modeset_unlock_all(dev); + } else + legacy_remove_fb(fb); } -out: drm_framebuffer_put(fb); } EXPORT_SYMBOL(drm_framebuffer_remove); diff --git a/drivers/gpu/drm/drm_ioc32.c b/drivers/gpu/drm/drm_ioc32.c index b134482..ae38678 100644 --- a/drivers/gpu/drm/drm_ioc32.c +++ b/drivers/gpu/drm/drm_ioc32.c @@ -1,4 +1,4 @@ -/** +/* * \file drm_ioc32.c * * 32-bit ioctl compatibility routines for the DRM. @@ -72,15 +72,15 @@ #define DRM_IOCTL_MODE_ADDFB232 DRM_IOWR(0xb8, drm_mode_fb_cmd232_t) typedef struct drm_version_32 { - int version_major; /**< Major version */ - int version_minor; /**< Minor version */ - int version_patchlevel; /**< Patch level */ - u32 name_len; /**< Length of name buffer */ - u32 name; /**< Name of driver */ - u32 date_len; /**< Length of date buffer */ - u32 date; /**< User-space buffer to hold date */ - u32 desc_len; /**< Length of desc buffer */ - u32 desc; /**< User-space buffer to hold desc */ + int version_major; /* Major version */ + int version_minor; /* Minor version */ + int version_patchlevel; /* Patch level */ + u32 name_len; /* Length of name buffer */ + u32 name; /* Name of driver */ + u32 date_len; /* Length of date buffer */ + u32 date; /* User-space buffer to hold date */ + u32 desc_len; /* Length of desc buffer */ + u32 desc; /* User-space buffer to hold desc */ } drm_version32_t; static int compat_drm_version(struct file *file, unsigned int cmd, @@ -126,8 +126,8 @@ static int compat_drm_version(struct file *file, unsigned int cmd, } typedef struct drm_unique32 { - u32 unique_len; /**< Length of unique */ - u32 unique; /**< Unique name for driver instantiation */ + u32 unique_len; /* Length of unique */ + u32 unique; /* Unique name for driver instantiation */ } drm_unique32_t; static int compat_drm_getunique(struct file *file, unsigned int cmd, @@ -180,12 +180,12 @@ static int compat_drm_setunique(struct file *file, unsigned int cmd, } typedef struct drm_map32 { - u32 offset; /**< Requested physical address (0 for SAREA)*/ - u32 size; /**< Requested physical size (bytes) */ - enum drm_map_type type; /**< Type of memory to map */ - enum drm_map_flags flags; /**< Flags */ - u32 handle; /**< User-space: "Handle" to pass to mmap() */ - int mtrr; /**< MTRR slot used */ + u32 offset; /* Requested physical address (0 for SAREA) */ + u32 size; /* Requested physical size (bytes) */ + enum drm_map_type type; /* Type of memory to map */ + enum drm_map_flags flags; /* Flags */ + u32 handle; /* User-space: "Handle" to pass to mmap() */ + int mtrr; /* MTRR slot used */ } drm_map32_t; static int compat_drm_getmap(struct file *file, unsigned int cmd, @@ -286,12 +286,12 @@ static int compat_drm_rmmap(struct file *file, unsigned int cmd, } typedef struct drm_client32 { - int idx; /**< Which client desired? */ - int auth; /**< Is client authenticated? */ - u32 pid; /**< Process ID */ - u32 uid; /**< User ID */ - u32 magic; /**< Magic */ - u32 iocs; /**< Ioctl count */ + int idx; /* Which client desired? */ + int auth; /* Is client authenticated? */ + u32 pid; /* Process ID */ + u32 uid; /* User ID */ + u32 magic; /* Magic */ + u32 iocs; /* Ioctl count */ } drm_client32_t; static int compat_drm_getclient(struct file *file, unsigned int cmd, @@ -366,12 +366,12 @@ static int compat_drm_getstats(struct file *file, unsigned int cmd, } typedef struct drm_buf_desc32 { - int count; /**< Number of buffers of this size */ - int size; /**< Size in bytes */ - int low_mark; /**< Low water mark */ - int high_mark; /**< High water mark */ + int count; /* Number of buffers of this size */ + int size; /* Size in bytes */ + int low_mark; /* Low water mark */ + int high_mark; /* High water mark */ int flags; - u32 agp_start; /**< Start address in the AGP aperture */ + u32 agp_start; /* Start address in the AGP aperture */ } drm_buf_desc32_t; static int compat_drm_addbufs(struct file *file, unsigned int cmd, @@ -1111,13 +1111,18 @@ static drm_ioctl_compat_t *drm_compat_ioctls[] = { }; /** - * Called whenever a 32-bit process running under a 64-bit kernel - * performs an ioctl on /dev/drm. + * drm_compat_ioctl - 32bit IOCTL compatibility handler for DRM drivers + * @filp: file this ioctl is called on + * @cmd: ioctl cmd number + * @arg: user argument + * + * Compatibility handler for 32 bit userspace running on 64 kernels. All actual + * IOCTL handling is forwarded to drm_ioctl(), while marshalling structures as + * appropriate. Note that this only handles DRM core IOCTLs, if the driver has + * botched IOCTL itself, it must handle those by wrapping this function. * - * \param file_priv DRM file private. - * \param cmd command. - * \param arg user argument. - * \return zero on success or negative number on failure. + * Returns: + * Zero on success, negative error code on failure. */ long drm_compat_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) { @@ -1141,5 +1146,4 @@ long drm_compat_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) return ret; } - EXPORT_SYMBOL(drm_compat_ioctl); diff --git a/drivers/gpu/drm/drm_ioctl.c b/drivers/gpu/drm/drm_ioctl.c index 7d6deaa..865e3ee 100644 --- a/drivers/gpu/drm/drm_ioctl.c +++ b/drivers/gpu/drm/drm_ioctl.c @@ -286,6 +286,9 @@ static int drm_getcap(struct drm_device *dev, void *data, struct drm_file *file_ case DRM_CAP_ADDFB2_MODIFIERS: req->value = dev->mode_config.allow_fb_modifiers; break; + case DRM_CAP_CRTC_IN_VBLANK_EVENT: + req->value = 1; + break; default: return -EINVAL; } @@ -647,13 +650,59 @@ static const struct drm_ioctl_desc drm_ioctls[] = { #define DRM_CORE_IOCTL_COUNT ARRAY_SIZE( drm_ioctls ) /** + * DOC: driver specific ioctls + * + * First things first, driver private IOCTLs should only be needed for drivers + * supporting rendering. Kernel modesetting is all standardized, and extended + * through properties. There are a few exceptions in some existing drivers, + * which define IOCTL for use by the display DRM master, but they all predate + * properties. + * + * Now if you do have a render driver you always have to support it through + * driver private properties. There's a few steps needed to wire all the things + * up. + * + * First you need to define the structure for your IOCTL in your driver private + * UAPI header in ``include/uapi/drm/my_driver_drm.h``:: + * + * struct my_driver_operation { + * u32 some_thing; + * u32 another_thing; + * }; + * + * Please make sure that you follow all the best practices from + * ``Documentation/ioctl/botching-up-ioctls.txt``. Note that drm_ioctl() + * automatically zero-extends structures, hence make sure you can add more stuff + * at the end, i.e. don't put a variable sized array there. + * + * Then you need to define your IOCTL number, using one of DRM_IO(), DRM_IOR(), + * DRM_IOW() or DRM_IOWR(). It must start with the DRM_IOCTL\_ prefix:: + * + * ##define DRM_IOCTL_MY_DRIVER_OPERATION \ + * DRM_IOW(DRM_COMMAND_BASE, struct my_driver_operation) + * + * DRM driver private IOCTL must be in the range from DRM_COMMAND_BASE to + * DRM_COMMAND_END. Finally you need an array of &struct drm_ioctl_desc to wire + * up the handlers and set the access rights: + * + * static const struct drm_ioctl_desc my_driver_ioctls[] = { + * DRM_IOCTL_DEF_DRV(MY_DRIVER_OPERATION, my_driver_operation, + * DRM_AUTH|DRM_RENDER_ALLOW), + * }; + * + * And then assign this to the &drm_driver.ioctls field in your driver + * structure. + */ + +/** * drm_ioctl - ioctl callback implementation for DRM drivers * @filp: file this ioctl is called on * @cmd: ioctl cmd number * @arg: user argument * - * Looks up the ioctl function in the ::ioctls table, checking for root - * previleges if so required, and dispatches to the respective function. + * Looks up the ioctl function in the DRM core and the driver dispatch table, + * stored in &drm_driver.ioctls. It checks for necessary permission by calling + * drm_ioctl_permit(), and dispatches to the respective function. * * Returns: * Zero on success, negative error code on failure. diff --git a/drivers/gpu/drm/drm_irq.c b/drivers/gpu/drm/drm_irq.c index dac1b25..8c866ca 100644 --- a/drivers/gpu/drm/drm_irq.c +++ b/drivers/gpu/drm/drm_irq.c @@ -1026,6 +1026,7 @@ void drm_crtc_arm_vblank_event(struct drm_crtc *crtc, e->pipe = pipe; e->event.sequence = drm_vblank_count(dev, pipe); + e->event.crtc_id = crtc->base.id; list_add_tail(&e->base.link, &dev->vblank_event_list); } EXPORT_SYMBOL(drm_crtc_arm_vblank_event); @@ -1056,6 +1057,7 @@ void drm_crtc_send_vblank_event(struct drm_crtc *crtc, now = get_drm_timestamp(); } e->pipe = pipe; + e->event.crtc_id = crtc->base.id; send_vblank_event(dev, e, seq, &now); } EXPORT_SYMBOL(drm_crtc_send_vblank_event); diff --git a/drivers/gpu/drm/drm_modeset_lock.c b/drivers/gpu/drm/drm_modeset_lock.c index bf60f26..64ef09a 100644 --- a/drivers/gpu/drm/drm_modeset_lock.c +++ b/drivers/gpu/drm/drm_modeset_lock.c @@ -149,108 +149,6 @@ void drm_modeset_unlock_all(struct drm_device *dev) EXPORT_SYMBOL(drm_modeset_unlock_all); /** - * drm_modeset_lock_crtc - lock crtc with hidden acquire ctx for a plane update - * @crtc: DRM CRTC - * @plane: DRM plane to be updated on @crtc - * - * This function locks the given crtc and plane (which should be either the - * primary or cursor plane) using a hidden acquire context. This is necessary so - * that drivers internally using the atomic interfaces can grab further locks - * with the lock acquire context. - * - * Note that @plane can be NULL, e.g. when the cursor support hasn't yet been - * converted to universal planes yet. - */ -void drm_modeset_lock_crtc(struct drm_crtc *crtc, - struct drm_plane *plane) -{ - struct drm_modeset_acquire_ctx *ctx; - int ret; - - ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); - if (WARN_ON(!ctx)) - return; - - drm_modeset_acquire_init(ctx, 0); - -retry: - ret = drm_modeset_lock(&crtc->mutex, ctx); - if (ret) - goto fail; - - if (plane) { - ret = drm_modeset_lock(&plane->mutex, ctx); - if (ret) - goto fail; - - if (plane->crtc) { - ret = drm_modeset_lock(&plane->crtc->mutex, ctx); - if (ret) - goto fail; - } - } - - WARN_ON(crtc->acquire_ctx); - - /* now we hold the locks, so now that it is safe, stash the - * ctx for drm_modeset_unlock_crtc(): - */ - crtc->acquire_ctx = ctx; - - return; - -fail: - if (ret == -EDEADLK) { - drm_modeset_backoff(ctx); - goto retry; - } -} -EXPORT_SYMBOL(drm_modeset_lock_crtc); - -/** - * drm_modeset_legacy_acquire_ctx - find acquire ctx for legacy ioctls - * @crtc: drm crtc - * - * Legacy ioctl operations like cursor updates or page flips only have per-crtc - * locking, and store the acquire ctx in the corresponding crtc. All other - * legacy operations take all locks and use a global acquire context. This - * function grabs the right one. - */ -struct drm_modeset_acquire_ctx * -drm_modeset_legacy_acquire_ctx(struct drm_crtc *crtc) -{ - if (crtc->acquire_ctx) - return crtc->acquire_ctx; - - WARN_ON(!crtc->dev->mode_config.acquire_ctx); - - return crtc->dev->mode_config.acquire_ctx; -} -EXPORT_SYMBOL(drm_modeset_legacy_acquire_ctx); - -/** - * drm_modeset_unlock_crtc - drop crtc lock - * @crtc: drm crtc - * - * This drops the crtc lock acquire with drm_modeset_lock_crtc() and all other - * locks acquired through the hidden context. - */ -void drm_modeset_unlock_crtc(struct drm_crtc *crtc) -{ - struct drm_modeset_acquire_ctx *ctx = crtc->acquire_ctx; - - if (WARN_ON(!ctx)) - return; - - crtc->acquire_ctx = NULL; - drm_modeset_drop_locks(ctx); - drm_modeset_acquire_fini(ctx); - - kfree(ctx); -} -EXPORT_SYMBOL(drm_modeset_unlock_crtc); - -/** * drm_warn_on_modeset_not_all_locked - check that all modeset locks are locked * @dev: device * diff --git a/drivers/gpu/drm/drm_of.c b/drivers/gpu/drm/drm_of.c index b5f2f0f..2120f33 100644 --- a/drivers/gpu/drm/drm_of.c +++ b/drivers/gpu/drm/drm_of.c @@ -3,8 +3,10 @@ #include <linux/list.h> #include <linux/of_graph.h> #include <drm/drmP.h> +#include <drm/drm_bridge.h> #include <drm/drm_crtc.h> #include <drm/drm_encoder.h> +#include <drm/drm_panel.h> #include <drm/drm_of.h> static void drm_release_of(struct device *dev, void *data) @@ -208,3 +210,53 @@ int drm_of_encoder_active_endpoint(struct device_node *node, return -EINVAL; } EXPORT_SYMBOL_GPL(drm_of_encoder_active_endpoint); + +/* + * drm_of_find_panel_or_bridge - return connected panel or bridge device + * @np: device tree node containing encoder output ports + * @panel: pointer to hold returned drm_panel + * @bridge: pointer to hold returned drm_bridge + * + * Given a DT node's port and endpoint number, find the connected node and + * return either the associated struct drm_panel or drm_bridge device. Either + * @panel or @bridge must not be NULL. + * + * Returns zero if successful, or one of the standard error codes if it fails. + */ +int drm_of_find_panel_or_bridge(const struct device_node *np, + int port, int endpoint, + struct drm_panel **panel, + struct drm_bridge **bridge) +{ + int ret = -EPROBE_DEFER; + struct device_node *remote; + + if (!panel && !bridge) + return -EINVAL; + + remote = of_graph_get_remote_node(np, port, endpoint); + if (!remote) + return -ENODEV; + + if (panel) { + *panel = of_drm_find_panel(remote); + if (*panel) + ret = 0; + } + + /* No panel found yet, check for a bridge next. */ + if (bridge) { + if (ret) { + *bridge = of_drm_find_bridge(remote); + if (*bridge) + ret = 0; + } else { + *bridge = NULL; + } + + } + + of_node_put(remote); + return ret; +} +EXPORT_SYMBOL_GPL(drm_of_find_panel_or_bridge); diff --git a/drivers/gpu/drm/drm_plane.c b/drivers/gpu/drm/drm_plane.c index bc71aa2..fedd4d6 100644 --- a/drivers/gpu/drm/drm_plane.c +++ b/drivers/gpu/drm/drm_plane.c @@ -620,7 +620,8 @@ int drm_mode_setplane(struct drm_device *dev, void *data, static int drm_mode_cursor_universal(struct drm_crtc *crtc, struct drm_mode_cursor2 *req, - struct drm_file *file_priv) + struct drm_file *file_priv, + struct drm_modeset_acquire_ctx *ctx) { struct drm_device *dev = crtc->dev; struct drm_framebuffer *fb = NULL; @@ -634,21 +635,11 @@ static int drm_mode_cursor_universal(struct drm_crtc *crtc, int32_t crtc_x, crtc_y; uint32_t crtc_w = 0, crtc_h = 0; uint32_t src_w = 0, src_h = 0; - struct drm_modeset_acquire_ctx ctx; int ret = 0; BUG_ON(!crtc->cursor); WARN_ON(crtc->cursor->crtc != crtc && crtc->cursor->crtc != NULL); - drm_modeset_acquire_init(&ctx, 0); -retry: - ret = drm_modeset_lock(&crtc->mutex, &ctx); - if (ret) - goto fail; - ret = drm_modeset_lock(&crtc->cursor->mutex, &ctx); - if (ret) - goto fail; - /* * Obtain fb we'll be using (either new or existing) and take an extra * reference to it if fb != null. setplane will take care of dropping @@ -693,7 +684,7 @@ retry: */ ret = __setplane_internal(crtc->cursor, crtc, fb, crtc_x, crtc_y, crtc_w, crtc_h, - 0, 0, src_w, src_h, &ctx); + 0, 0, src_w, src_h, ctx); /* Update successful; save new cursor position, if necessary */ if (ret == 0 && req->flags & DRM_MODE_CURSOR_MOVE) { @@ -701,15 +692,6 @@ retry: crtc->cursor_y = req->y; } -fail: - if (ret == -EDEADLK) { - drm_modeset_backoff(&ctx); - goto retry; - } - - drm_modeset_drop_locks(&ctx); - drm_modeset_acquire_fini(&ctx); - return ret; } @@ -718,6 +700,7 @@ static int drm_mode_cursor_common(struct drm_device *dev, struct drm_file *file_priv) { struct drm_crtc *crtc; + struct drm_modeset_acquire_ctx ctx; int ret = 0; if (!drm_core_check_feature(dev, DRIVER_MODESET)) @@ -732,14 +715,24 @@ static int drm_mode_cursor_common(struct drm_device *dev, return -ENOENT; } + drm_modeset_acquire_init(&ctx, 0); +retry: + ret = drm_modeset_lock(&crtc->mutex, &ctx); + if (ret) + goto out; /* * If this crtc has a universal cursor plane, call that plane's update * handler rather than using legacy cursor handlers. */ - if (crtc->cursor) - return drm_mode_cursor_universal(crtc, req, file_priv); + if (crtc->cursor) { + ret = drm_modeset_lock(&crtc->cursor->mutex, &ctx); + if (ret) + goto out; + + ret = drm_mode_cursor_universal(crtc, req, file_priv, &ctx); + goto out; + } - drm_modeset_lock_crtc(crtc, crtc->cursor); if (req->flags & DRM_MODE_CURSOR_BO) { if (!crtc->funcs->cursor_set && !crtc->funcs->cursor_set2) { ret = -ENXIO; @@ -763,7 +756,13 @@ static int drm_mode_cursor_common(struct drm_device *dev, } } out: - drm_modeset_unlock_crtc(crtc); + if (ret == -EDEADLK) { + drm_modeset_backoff(&ctx); + goto retry; + } + + drm_modeset_drop_locks(&ctx); + drm_modeset_acquire_fini(&ctx); return ret; diff --git a/drivers/gpu/drm/drm_probe_helper.c b/drivers/gpu/drm/drm_probe_helper.c index 85005d5..1b0c14a 100644 --- a/drivers/gpu/drm/drm_probe_helper.c +++ b/drivers/gpu/drm/drm_probe_helper.c @@ -44,7 +44,7 @@ * * This library provides some helper code for output probing. It provides an * implementation of the core &drm_connector_funcs.fill_modes interface with - * drm_helper_probe_single_connector_modes. + * drm_helper_probe_single_connector_modes(). * * It also provides support for polling connectors with a work item and for * generic hotplug interrupt handling where the driver doesn't or cannot keep @@ -169,12 +169,73 @@ void drm_kms_helper_poll_enable(struct drm_device *dev) EXPORT_SYMBOL(drm_kms_helper_poll_enable); static enum drm_connector_status -drm_connector_detect(struct drm_connector *connector, bool force) +drm_helper_probe_detect_ctx(struct drm_connector *connector, bool force) { - return connector->funcs->detect ? - connector->funcs->detect(connector, force) : - connector_status_connected; + const struct drm_connector_helper_funcs *funcs = connector->helper_private; + struct drm_modeset_acquire_ctx ctx; + int ret; + + drm_modeset_acquire_init(&ctx, 0); + +retry: + ret = drm_modeset_lock(&connector->dev->mode_config.connection_mutex, &ctx); + if (!ret) { + if (funcs->detect_ctx) + ret = funcs->detect_ctx(connector, &ctx, force); + else if (connector->funcs->detect) + ret = connector->funcs->detect(connector, force); + else + ret = connector_status_connected; + } + + if (ret == -EDEADLK) { + drm_modeset_backoff(&ctx); + goto retry; + } + + if (WARN_ON(ret < 0)) + ret = connector_status_unknown; + + drm_modeset_drop_locks(&ctx); + drm_modeset_acquire_fini(&ctx); + + return ret; +} + +/** + * drm_helper_probe_detect - probe connector status + * @connector: connector to probe + * @ctx: acquire_ctx, or NULL to let this function handle locking. + * @force: Whether destructive probe operations should be performed. + * + * This function calls the detect callbacks of the connector. + * This function returns &drm_connector_status, or + * if @ctx is set, it might also return -EDEADLK. + */ +int +drm_helper_probe_detect(struct drm_connector *connector, + struct drm_modeset_acquire_ctx *ctx, + bool force) +{ + const struct drm_connector_helper_funcs *funcs = connector->helper_private; + struct drm_device *dev = connector->dev; + int ret; + + if (!ctx) + return drm_helper_probe_detect_ctx(connector, force); + + ret = drm_modeset_lock(&dev->mode_config.connection_mutex, ctx); + if (ret) + return ret; + + if (funcs->detect_ctx) + return funcs->detect_ctx(connector, ctx, force); + else if (connector->funcs->detect) + return connector->funcs->detect(connector, force); + else + return connector_status_connected; } +EXPORT_SYMBOL(drm_helper_probe_detect); /** * drm_helper_probe_single_connector_modes - get complete set of display modes @@ -239,15 +300,27 @@ int drm_helper_probe_single_connector_modes(struct drm_connector *connector, struct drm_display_mode *mode; const struct drm_connector_helper_funcs *connector_funcs = connector->helper_private; - int count = 0; + int count = 0, ret; int mode_flags = 0; bool verbose_prune = true; enum drm_connector_status old_status; + struct drm_modeset_acquire_ctx ctx; WARN_ON(!mutex_is_locked(&dev->mode_config.mutex)); + drm_modeset_acquire_init(&ctx, 0); + DRM_DEBUG_KMS("[CONNECTOR:%d:%s]\n", connector->base.id, connector->name); + +retry: + ret = drm_modeset_lock(&dev->mode_config.connection_mutex, &ctx); + if (ret == -EDEADLK) { + drm_modeset_backoff(&ctx); + goto retry; + } else + WARN_ON(ret < 0); + /* set all old modes to the stale state */ list_for_each_entry(mode, &connector->modes, head) mode->status = MODE_STALE; @@ -263,7 +336,15 @@ int drm_helper_probe_single_connector_modes(struct drm_connector *connector, if (connector->funcs->force) connector->funcs->force(connector); } else { - connector->status = drm_connector_detect(connector, true); + ret = drm_helper_probe_detect(connector, &ctx, true); + + if (ret == -EDEADLK) { + drm_modeset_backoff(&ctx); + goto retry; + } else if (WARN(ret < 0, "Invalid return value %i for connector detection\n", ret)) + ret = connector_status_unknown; + + connector->status = ret; } /* @@ -355,6 +436,9 @@ int drm_helper_probe_single_connector_modes(struct drm_connector *connector, prune: drm_mode_prune_invalid(dev, &connector->modes, verbose_prune); + drm_modeset_drop_locks(&ctx); + drm_modeset_acquire_fini(&ctx); + if (list_empty(&connector->modes)) return 0; @@ -440,7 +524,7 @@ static void output_poll_execute(struct work_struct *work) repoll = true; - connector->status = drm_connector_detect(connector, false); + connector->status = drm_helper_probe_detect(connector, NULL, false); if (old_status != connector->status) { const char *old, *new; @@ -588,7 +672,7 @@ bool drm_helper_hpd_irq_event(struct drm_device *dev) old_status = connector->status; - connector->status = drm_connector_detect(connector, false); + connector->status = drm_helper_probe_detect(connector, NULL, false); DRM_DEBUG_KMS("[CONNECTOR:%d:%s] status updated from %s to %s\n", connector->base.id, connector->name, diff --git a/drivers/gpu/drm/drm_property.c b/drivers/gpu/drm/drm_property.c index b17959c..3feef06 100644 --- a/drivers/gpu/drm/drm_property.c +++ b/drivers/gpu/drm/drm_property.c @@ -442,8 +442,7 @@ int drm_mode_getproperty_ioctl(struct drm_device *dev, struct drm_property *property; int enum_count = 0; int value_count = 0; - int ret = 0, i; - int copied; + int i, copied; struct drm_property_enum *prop_enum; struct drm_mode_property_enum __user *enum_ptr; uint64_t __user *values_ptr; @@ -451,55 +450,43 @@ int drm_mode_getproperty_ioctl(struct drm_device *dev, if (!drm_core_check_feature(dev, DRIVER_MODESET)) return -EINVAL; - drm_modeset_lock_all(dev); property = drm_property_find(dev, out_resp->prop_id); - if (!property) { - ret = -ENOENT; - goto done; - } - - if (drm_property_type_is(property, DRM_MODE_PROP_ENUM) || - drm_property_type_is(property, DRM_MODE_PROP_BITMASK)) { - list_for_each_entry(prop_enum, &property->enum_list, head) - enum_count++; - } - - value_count = property->num_values; + if (!property) + return -ENOENT; strncpy(out_resp->name, property->name, DRM_PROP_NAME_LEN); out_resp->name[DRM_PROP_NAME_LEN-1] = 0; out_resp->flags = property->flags; - if ((out_resp->count_values >= value_count) && value_count) { - values_ptr = (uint64_t __user *)(unsigned long)out_resp->values_ptr; - for (i = 0; i < value_count; i++) { - if (copy_to_user(values_ptr + i, &property->values[i], sizeof(uint64_t))) { - ret = -EFAULT; - goto done; - } + value_count = property->num_values; + values_ptr = u64_to_user_ptr(out_resp->values_ptr); + + for (i = 0; i < value_count; i++) { + if (i < out_resp->count_values && + put_user(property->values[i], values_ptr + i)) { + return -EFAULT; } } out_resp->count_values = value_count; + copied = 0; + enum_ptr = u64_to_user_ptr(out_resp->enum_blob_ptr); + if (drm_property_type_is(property, DRM_MODE_PROP_ENUM) || - drm_property_type_is(property, DRM_MODE_PROP_BITMASK)) { - if ((out_resp->count_enum_blobs >= enum_count) && enum_count) { - copied = 0; - enum_ptr = (struct drm_mode_property_enum __user *)(unsigned long)out_resp->enum_blob_ptr; - list_for_each_entry(prop_enum, &property->enum_list, head) { - - if (copy_to_user(&enum_ptr[copied].value, &prop_enum->value, sizeof(uint64_t))) { - ret = -EFAULT; - goto done; - } - - if (copy_to_user(&enum_ptr[copied].name, - &prop_enum->name, DRM_PROP_NAME_LEN)) { - ret = -EFAULT; - goto done; - } - copied++; - } + drm_property_type_is(property, DRM_MODE_PROP_BITMASK)) { + list_for_each_entry(prop_enum, &property->enum_list, head) { + enum_count++; + if (out_resp->count_enum_blobs <= enum_count) + continue; + + if (copy_to_user(&enum_ptr[copied].value, + &prop_enum->value, sizeof(uint64_t))) + return -EFAULT; + + if (copy_to_user(&enum_ptr[copied].name, + &prop_enum->name, DRM_PROP_NAME_LEN)) + return -EFAULT; + copied++; } out_resp->count_enum_blobs = enum_count; } @@ -514,9 +501,8 @@ int drm_mode_getproperty_ioctl(struct drm_device *dev, */ if (drm_property_type_is(property, DRM_MODE_PROP_BLOB)) out_resp->count_enum_blobs = 0; -done: - drm_modeset_unlock_all(dev); - return ret; + + return 0; } static void drm_property_free_blob(struct kref *kref) diff --git a/drivers/gpu/drm/drm_sysfs.c b/drivers/gpu/drm/drm_sysfs.c index 513288b..1c5b5ce 100644 --- a/drivers/gpu/drm/drm_sysfs.c +++ b/drivers/gpu/drm/drm_sysfs.c @@ -25,6 +25,20 @@ #define to_drm_minor(d) dev_get_drvdata(d) #define to_drm_connector(d) dev_get_drvdata(d) +/** + * DOC: overview + * + * DRM provides very little additional support to drivers for sysfs + * interactions, beyond just all the standard stuff. Drivers who want to expose + * additional sysfs properties and property groups can attach them at either + * &drm_device.dev or &drm_connector.kdev. + * + * Registration is automatically handled when calling drm_dev_register(), or + * drm_connector_register() in case of hot-plugged connectors. Unregistration is + * also automatically handled by drm_dev_unregister() and + * drm_connector_unregister(). + */ + static struct device_type drm_sysfs_device_minor = { .name = "drm_minor" }; @@ -250,15 +264,6 @@ static const struct attribute_group *connector_dev_groups[] = { NULL }; -/** - * drm_sysfs_connector_add - add a connector to sysfs - * @connector: connector to add - * - * Create a connector device in sysfs, along with its associated connector - * properties (so far, connection status, dpms, mode list and edid) and - * generate a hotplug event so userspace knows there's a new connector - * available. - */ int drm_sysfs_connector_add(struct drm_connector *connector) { struct drm_device *dev = connector->dev; @@ -285,19 +290,6 @@ int drm_sysfs_connector_add(struct drm_connector *connector) return 0; } -/** - * drm_sysfs_connector_remove - remove an connector device from sysfs - * @connector: connector to remove - * - * Remove @connector and its associated attributes from sysfs. Note that - * the device model core will take care of sending the "remove" uevent - * at this time, so we don't need to do it. - * - * Note: - * This routine should only be called if the connector was previously - * successfully registered. If @connector hasn't been registered yet, - * you'll likely see a panic somewhere deep in sysfs code when called. - */ void drm_sysfs_connector_remove(struct drm_connector *connector) { if (!connector->kdev) @@ -333,20 +325,6 @@ static void drm_sysfs_release(struct device *dev) kfree(dev); } -/** - * drm_sysfs_minor_alloc() - Allocate sysfs device for given minor - * @minor: minor to allocate sysfs device for - * - * This allocates a new sysfs device for @minor and returns it. The device is - * not registered nor linked. The caller has to use device_add() and - * device_del() to register and unregister it. - * - * Note that dev_get_drvdata() on the new device will return the minor. - * However, the device does not hold a ref-count to the minor nor to the - * underlying drm_device. This is unproblematic as long as you access the - * private data only in sysfs callbacks. device_del() disables those - * synchronously, so they cannot be called after you cleanup a minor. - */ struct device *drm_sysfs_minor_alloc(struct drm_minor *minor) { const char *minor_str; @@ -384,15 +362,13 @@ err_free: } /** - * drm_class_device_register - Register a struct device in the drm class. + * drm_class_device_register - register new device with the DRM sysfs class + * @dev: device to register * - * @dev: pointer to struct device to register. - * - * @dev should have all relevant members pre-filled with the exception - * of the class member. In particular, the device_type member must - * be set. + * Registers a new &struct device within the DRM sysfs class. Essentially only + * used by ttm to have a place for its global settings. Drivers should never use + * this. */ - int drm_class_device_register(struct device *dev) { if (!drm_class || IS_ERR(drm_class)) @@ -403,6 +379,14 @@ int drm_class_device_register(struct device *dev) } EXPORT_SYMBOL_GPL(drm_class_device_register); +/** + * drm_class_device_unregister - unregister device with the DRM sysfs class + * @dev: device to unregister + * + * Unregisters a &struct device from the DRM sysfs class. Essentially only used + * by ttm to have a place for its global settings. Drivers should never use + * this. + */ void drm_class_device_unregister(struct device *dev) { return device_unregister(dev); diff --git a/drivers/gpu/drm/exynos/exynos_dp.c b/drivers/gpu/drm/exynos/exynos_dp.c index b445b50..385537b 100644 --- a/drivers/gpu/drm/exynos/exynos_dp.c +++ b/drivers/gpu/drm/exynos/exynos_dp.c @@ -23,6 +23,7 @@ #include <drm/drmP.h> #include <drm/drm_crtc.h> #include <drm/drm_crtc_helper.h> +#include <drm/drm_of.h> #include <drm/drm_panel.h> #include <drm/bridge/analogix_dp.h> @@ -211,8 +212,11 @@ static const struct component_ops exynos_dp_ops = { static int exynos_dp_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; - struct device_node *np = NULL, *endpoint = NULL; + struct device_node *np; struct exynos_dp_device *dp; + struct drm_panel *panel; + struct drm_bridge *bridge; + int ret; dp = devm_kzalloc(&pdev->dev, sizeof(struct exynos_dp_device), GFP_KERNEL); @@ -236,28 +240,13 @@ static int exynos_dp_probe(struct platform_device *pdev) goto out; } - endpoint = of_graph_get_next_endpoint(dev->of_node, NULL); - if (endpoint) { - np = of_graph_get_remote_port_parent(endpoint); - if (np) { - /* The remote port can be either a panel or a bridge */ - dp->plat_data.panel = of_drm_find_panel(np); - if (!dp->plat_data.panel) { - dp->ptn_bridge = of_drm_find_bridge(np); - if (!dp->ptn_bridge) { - of_node_put(np); - return -EPROBE_DEFER; - } - } - of_node_put(np); - } else { - DRM_ERROR("no remote endpoint device node found.\n"); - return -EINVAL; - } - } else { - DRM_ERROR("no port endpoint subnode found.\n"); - return -EINVAL; - } + ret = drm_of_find_panel_or_bridge(dev->of_node, 0, 0, &panel, &bridge); + if (ret) + return ret; + + /* The remote port can be either a panel or a bridge */ + dp->plat_data.panel = panel; + dp->ptn_bridge = bridge; out: return component_add(&pdev->dev, &exynos_dp_ops); diff --git a/drivers/gpu/drm/exynos/exynos_drm_dpi.c b/drivers/gpu/drm/exynos/exynos_drm_dpi.c index 3aab71a..63abcd2 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_dpi.c +++ b/drivers/gpu/drm/exynos/exynos_drm_dpi.c @@ -163,27 +163,13 @@ enum { FIMD_PORT_WRB, }; -static struct device_node *exynos_dpi_of_find_panel_node(struct device *dev) -{ - struct device_node *np, *ep; - - ep = of_graph_get_endpoint_by_regs(dev->of_node, FIMD_PORT_RGB, 0); - if (!ep) - return NULL; - - np = of_graph_get_remote_port_parent(ep); - of_node_put(ep); - - return np; -} - static int exynos_dpi_parse_dt(struct exynos_dpi *ctx) { struct device *dev = ctx->dev; struct device_node *dn = dev->of_node; struct device_node *np; - ctx->panel_node = exynos_dpi_of_find_panel_node(dev); + ctx->panel_node = of_graph_get_remote_node(dn, FIMD_PORT_RGB, 0); np = of_get_child_by_name(dn, "display-timings"); if (np) { diff --git a/drivers/gpu/drm/exynos/exynos_drm_dsi.c b/drivers/gpu/drm/exynos/exynos_drm_dsi.c index 6d4da2f..fc4fda7 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_dsi.c +++ b/drivers/gpu/drm/exynos/exynos_drm_dsi.c @@ -1659,17 +1659,10 @@ static int exynos_dsi_parse_dt(struct exynos_dsi *dsi) of_node_put(ep); - ep = of_graph_get_next_endpoint(node, NULL); - if (!ep) { - ret = -EINVAL; - goto end; - } + dsi->bridge_node = of_graph_get_remote_node(node, DSI_PORT_OUT, 0); + if (!dsi->bridge_node) + return -EINVAL; - dsi->bridge_node = of_graph_get_remote_port_parent(ep); - if (!dsi->bridge_node) { - ret = -EINVAL; - goto end; - } end: of_node_put(ep); diff --git a/drivers/gpu/drm/exynos/exynos_drm_mic.c b/drivers/gpu/drm/exynos/exynos_drm_mic.c index 2ef43d4..e457205 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_mic.c +++ b/drivers/gpu/drm/exynos/exynos_drm_mic.c @@ -229,29 +229,6 @@ static void mic_set_reg_on(struct exynos_mic *mic, bool enable) writel(reg, mic->reg + MIC_OP); } -static struct device_node *get_remote_node(struct device_node *from, int reg) -{ - struct device_node *endpoint = NULL, *remote_node = NULL; - - endpoint = of_graph_get_endpoint_by_regs(from, reg, -1); - if (!endpoint) { - DRM_ERROR("mic: Failed to find remote port from %s", - from->full_name); - goto exit; - } - - remote_node = of_graph_get_remote_port_parent(endpoint); - if (!remote_node) { - DRM_ERROR("mic: Failed to find remote port parent from %s", - from->full_name); - goto exit; - } - -exit: - of_node_put(endpoint); - return remote_node; -} - static int parse_dt(struct exynos_mic *mic) { int ret = 0, i, j; @@ -263,7 +240,7 @@ static int parse_dt(struct exynos_mic *mic) * The first node must be for decon and the second one must be for dsi. */ for (i = 0, j = 0; i < NUM_ENDPOINTS; i++) { - remote_node = get_remote_node(mic->dev->of_node, i); + remote_node = of_graph_get_remote_node(mic->dev->of_node, i, 0); if (!remote_node) { ret = -EPIPE; goto exit; diff --git a/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_rgb.c b/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_rgb.c index c365145..dcbf3c0 100644 --- a/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_rgb.c +++ b/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_rgb.c @@ -15,6 +15,7 @@ #include <drm/drmP.h> #include <drm/drm_atomic_helper.h> #include <drm/drm_crtc_helper.h> +#include <drm/drm_of.h> #include <drm/drm_panel.h> #include "fsl_dcu_drm_drv.h" @@ -141,32 +142,11 @@ err_cleanup: return ret; } -static int fsl_dcu_attach_endpoint(struct fsl_dcu_drm_device *fsl_dev, - const struct of_endpoint *ep) -{ - struct drm_bridge *bridge; - struct device_node *np; - - np = of_graph_get_remote_port_parent(ep->local_node); - - fsl_dev->connector.panel = of_drm_find_panel(np); - if (fsl_dev->connector.panel) { - of_node_put(np); - return fsl_dcu_attach_panel(fsl_dev, fsl_dev->connector.panel); - } - - bridge = of_drm_find_bridge(np); - of_node_put(np); - if (!bridge) - return -ENODEV; - - return drm_bridge_attach(&fsl_dev->encoder, bridge, NULL); -} - int fsl_dcu_create_outputs(struct fsl_dcu_drm_device *fsl_dev) { - struct of_endpoint ep; - struct device_node *ep_node, *panel_node; + struct device_node *panel_node; + struct drm_panel *panel; + struct drm_bridge *bridge; int ret; /* This is for backward compatibility */ @@ -179,14 +159,14 @@ int fsl_dcu_create_outputs(struct fsl_dcu_drm_device *fsl_dev) return fsl_dcu_attach_panel(fsl_dev, fsl_dev->connector.panel); } - ep_node = of_graph_get_next_endpoint(fsl_dev->np, NULL); - if (!ep_node) - return -ENODEV; - - ret = of_graph_parse_endpoint(ep_node, &ep); - of_node_put(ep_node); + ret = drm_of_find_panel_or_bridge(fsl_dev->np, 0, 0, &panel, &bridge); if (ret) - return -ENODEV; + return ret; + + if (panel) { + fsl_dev->connector.panel = panel; + return fsl_dcu_attach_panel(fsl_dev, panel); + } - return fsl_dcu_attach_endpoint(fsl_dev, &ep); + return drm_bridge_attach(&fsl_dev->encoder, bridge, NULL); } diff --git a/drivers/gpu/drm/gma500/gma_display.c b/drivers/gpu/drm/gma500/gma_display.c index 93ff465..e7fd356 100644 --- a/drivers/gpu/drm/gma500/gma_display.c +++ b/drivers/gpu/drm/gma500/gma_display.c @@ -177,7 +177,8 @@ void gma_crtc_load_lut(struct drm_crtc *crtc) } int gma_crtc_gamma_set(struct drm_crtc *crtc, u16 *red, u16 *green, u16 *blue, - u32 size) + u32 size, + struct drm_modeset_acquire_ctx *ctx) { struct gma_crtc *gma_crtc = to_gma_crtc(crtc); int i; diff --git a/drivers/gpu/drm/gma500/gma_display.h b/drivers/gpu/drm/gma500/gma_display.h index 166e608..239c374 100644 --- a/drivers/gpu/drm/gma500/gma_display.h +++ b/drivers/gpu/drm/gma500/gma_display.h @@ -73,7 +73,8 @@ extern int gma_crtc_cursor_set(struct drm_crtc *crtc, extern int gma_crtc_cursor_move(struct drm_crtc *crtc, int x, int y); extern void gma_crtc_load_lut(struct drm_crtc *crtc); extern int gma_crtc_gamma_set(struct drm_crtc *crtc, u16 *red, u16 *green, - u16 *blue, u32 size); + u16 *blue, u32 size, + struct drm_modeset_acquire_ctx *ctx); extern void gma_crtc_dpms(struct drm_crtc *crtc, int mode); extern void gma_crtc_prepare(struct drm_crtc *crtc); extern void gma_crtc_commit(struct drm_crtc *crtc); diff --git a/drivers/gpu/drm/hisilicon/kirin/dw_drm_dsi.c b/drivers/gpu/drm/hisilicon/kirin/dw_drm_dsi.c index 1737e98..5abc69c 100644 --- a/drivers/gpu/drm/hisilicon/kirin/dw_drm_dsi.c +++ b/drivers/gpu/drm/hisilicon/kirin/dw_drm_dsi.c @@ -17,7 +17,6 @@ #include <linux/clk.h> #include <linux/component.h> -#include <linux/of_graph.h> #include <drm/drm_of.h> #include <drm/drm_crtc_helper.h> @@ -754,34 +753,16 @@ static int dsi_parse_dt(struct platform_device *pdev, struct dw_dsi *dsi) { struct dsi_hw_ctx *ctx = dsi->ctx; struct device_node *np = pdev->dev.of_node; - struct device_node *endpoint, *bridge_node; - struct drm_bridge *bridge; struct resource *res; + int ret; /* * Get the endpoint node. In our case, dsi has one output port1 * to which the external HDMI bridge is connected. */ - endpoint = of_graph_get_endpoint_by_regs(np, 1, -1); - if (!endpoint) { - DRM_ERROR("no valid endpoint node\n"); - return -ENODEV; - } - of_node_put(endpoint); - - bridge_node = of_graph_get_remote_port_parent(endpoint); - if (!bridge_node) { - DRM_ERROR("no valid bridge node\n"); - return -ENODEV; - } - of_node_put(bridge_node); - - bridge = of_drm_find_bridge(bridge_node); - if (!bridge) { - DRM_INFO("wait for external HDMI bridge driver.\n"); - return -EPROBE_DEFER; - } - dsi->bridge = bridge; + ret = drm_of_find_panel_or_bridge(np, 0, 0, NULL, &dsi->bridge); + if (ret) + return ret; ctx->pclk = devm_clk_get(&pdev->dev, "pclk"); if (IS_ERR(ctx->pclk)) { diff --git a/drivers/gpu/drm/hisilicon/kirin/kirin_drm_drv.c b/drivers/gpu/drm/hisilicon/kirin/kirin_drm_drv.c index df4f507..9c90367 100644 --- a/drivers/gpu/drm/hisilicon/kirin/kirin_drm_drv.c +++ b/drivers/gpu/drm/hisilicon/kirin/kirin_drm_drv.c @@ -230,34 +230,6 @@ static const struct component_master_ops kirin_drm_ops = { .unbind = kirin_drm_unbind, }; -static struct device_node *kirin_get_remote_node(struct device_node *np) -{ - struct device_node *endpoint, *remote; - - /* get the first endpoint, in our case only one remote node - * is connected to display controller. - */ - endpoint = of_graph_get_next_endpoint(np, NULL); - if (!endpoint) { - DRM_ERROR("no valid endpoint node\n"); - return ERR_PTR(-ENODEV); - } - - remote = of_graph_get_remote_port_parent(endpoint); - of_node_put(endpoint); - if (!remote) { - DRM_ERROR("no valid remote node\n"); - return ERR_PTR(-ENODEV); - } - - if (!of_device_is_available(remote)) { - DRM_ERROR("not available for remote node\n"); - return ERR_PTR(-ENODEV); - } - - return remote; -} - static int kirin_drm_platform_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; @@ -271,7 +243,7 @@ static int kirin_drm_platform_probe(struct platform_device *pdev) return -EINVAL; } - remote = kirin_get_remote_node(np); + remote = of_graph_get_remote_node(np, 0, 0); if (IS_ERR(remote)) return PTR_ERR(remote); diff --git a/drivers/gpu/drm/i915/intel_crt.c b/drivers/gpu/drm/i915/intel_crt.c index 8c82607..2797bf3 100644 --- a/drivers/gpu/drm/i915/intel_crt.c +++ b/drivers/gpu/drm/i915/intel_crt.c @@ -669,15 +669,16 @@ static const struct dmi_system_id intel_spurious_crt_detect[] = { { } }; -static enum drm_connector_status -intel_crt_detect(struct drm_connector *connector, bool force) +static int +intel_crt_detect(struct drm_connector *connector, + struct drm_modeset_acquire_ctx *ctx, + bool force) { struct drm_i915_private *dev_priv = to_i915(connector->dev); struct intel_crt *crt = intel_attached_crt(connector); struct intel_encoder *intel_encoder = &crt->base; - enum drm_connector_status status; + int status, ret; struct intel_load_detect_pipe tmp; - struct drm_modeset_acquire_ctx ctx; DRM_DEBUG_KMS("[CONNECTOR:%d:%s] force=%d\n", connector->base.id, connector->name, @@ -721,10 +722,9 @@ intel_crt_detect(struct drm_connector *connector, bool force) goto out; } - drm_modeset_acquire_init(&ctx, 0); - /* for pre-945g platforms use load detect */ - if (intel_get_load_detect_pipe(connector, NULL, &tmp, &ctx)) { + ret = intel_get_load_detect_pipe(connector, NULL, &tmp, ctx); + if (ret > 0) { if (intel_crt_detect_ddc(connector)) status = connector_status_connected; else if (INTEL_GEN(dev_priv) < 4) @@ -734,12 +734,11 @@ intel_crt_detect(struct drm_connector *connector, bool force) status = connector_status_disconnected; else status = connector_status_unknown; - intel_release_load_detect_pipe(connector, &tmp, &ctx); - } else + intel_release_load_detect_pipe(connector, &tmp, ctx); + } else if (ret == 0) status = connector_status_unknown; - - drm_modeset_drop_locks(&ctx); - drm_modeset_acquire_fini(&ctx); + else if (ret < 0) + status = ret; out: intel_display_power_put(dev_priv, intel_encoder->power_domain); @@ -811,7 +810,6 @@ void intel_crt_reset(struct drm_encoder *encoder) static const struct drm_connector_funcs intel_crt_connector_funcs = { .dpms = drm_atomic_helper_connector_dpms, - .detect = intel_crt_detect, .fill_modes = drm_helper_probe_single_connector_modes, .late_register = intel_connector_register, .early_unregister = intel_connector_unregister, @@ -823,6 +821,7 @@ static const struct drm_connector_funcs intel_crt_connector_funcs = { }; static const struct drm_connector_helper_funcs intel_crt_connector_helper_funcs = { + .detect_ctx = intel_crt_detect, .mode_valid = intel_crt_mode_valid, .get_modes = intel_crt_get_modes, }; diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 81baa5a..881dec8 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -3412,17 +3412,6 @@ static void skylake_disable_primary_plane(struct drm_plane *primary, spin_unlock_irqrestore(&dev_priv->uncore.lock, irqflags); } -/* Assume fb object is pinned & idle & fenced and just update base pointers */ -static int -intel_pipe_set_base_atomic(struct drm_crtc *crtc, struct drm_framebuffer *fb, - int x, int y, enum mode_set_atomic state) -{ - /* Support for kgdboc is disabled, this needs a major rework. */ - DRM_ERROR("legacy panic handler not supported any more.\n"); - - return -ENODEV; -} - static void intel_complete_page_flips(struct drm_i915_private *dev_priv) { struct intel_crtc *crtc; @@ -9503,10 +9492,10 @@ static int intel_modeset_setup_plane_state(struct drm_atomic_state *state, return 0; } -bool intel_get_load_detect_pipe(struct drm_connector *connector, - struct drm_display_mode *mode, - struct intel_load_detect_pipe *old, - struct drm_modeset_acquire_ctx *ctx) +int intel_get_load_detect_pipe(struct drm_connector *connector, + struct drm_display_mode *mode, + struct intel_load_detect_pipe *old, + struct drm_modeset_acquire_ctx *ctx) { struct intel_crtc *intel_crtc; struct intel_encoder *intel_encoder = @@ -9529,10 +9518,7 @@ bool intel_get_load_detect_pipe(struct drm_connector *connector, old->restore_state = NULL; -retry: - ret = drm_modeset_lock(&config->connection_mutex, ctx); - if (ret) - goto fail; + WARN_ON(!drm_modeset_is_locked(&config->connection_mutex)); /* * Algorithm gets a little messy: @@ -9682,10 +9668,8 @@ fail: restore_state = NULL; } - if (ret == -EDEADLK) { - drm_modeset_backoff(ctx); - goto retry; - } + if (ret == -EDEADLK) + return ret; return false; } @@ -10727,7 +10711,7 @@ out_hang: state = drm_atomic_state_alloc(dev); if (!state) return -ENOMEM; - state->acquire_ctx = drm_modeset_legacy_acquire_ctx(crtc); + state->acquire_ctx = dev->mode_config.acquire_ctx; retry: plane_state = drm_atomic_get_plane_state(state, primary); @@ -11017,7 +11001,6 @@ static int intel_crtc_atomic_check(struct drm_crtc *crtc, } static const struct drm_crtc_helper_funcs intel_helper_funcs = { - .mode_set_base_atomic = intel_pipe_set_base_atomic, .atomic_begin = intel_begin_crtc_commit, .atomic_flush = intel_finish_crtc_commit, .atomic_check = intel_crtc_atomic_check, @@ -13090,7 +13073,7 @@ void intel_crtc_restore_mode(struct drm_crtc *crtc) return; } - state->acquire_ctx = drm_modeset_legacy_acquire_ctx(crtc); + state->acquire_ctx = crtc->dev->mode_config.acquire_ctx; retry: crtc_state = drm_atomic_get_crtc_state(state, crtc); @@ -13113,50 +13096,8 @@ out: drm_atomic_state_put(state); } -/* - * FIXME: Remove this once i915 is fully DRIVER_ATOMIC by calling - * drm_atomic_helper_legacy_gamma_set() directly. - */ -static int intel_atomic_legacy_gamma_set(struct drm_crtc *crtc, - u16 *red, u16 *green, u16 *blue, - uint32_t size) -{ - struct drm_device *dev = crtc->dev; - struct drm_mode_config *config = &dev->mode_config; - struct drm_crtc_state *state; - int ret; - - ret = drm_atomic_helper_legacy_gamma_set(crtc, red, green, blue, size); - if (ret) - return ret; - - /* - * Make sure we update the legacy properties so this works when - * atomic is not enabled. - */ - - state = crtc->state; - - drm_object_property_set_value(&crtc->base, - config->degamma_lut_property, - (state->degamma_lut) ? - state->degamma_lut->base.id : 0); - - drm_object_property_set_value(&crtc->base, - config->ctm_property, - (state->ctm) ? - state->ctm->base.id : 0); - - drm_object_property_set_value(&crtc->base, - config->gamma_lut_property, - (state->gamma_lut) ? - state->gamma_lut->base.id : 0); - - return 0; -} - static const struct drm_crtc_funcs intel_crtc_funcs = { - .gamma_set = intel_atomic_legacy_gamma_set, + .gamma_set = drm_atomic_helper_legacy_gamma_set, .set_config = drm_atomic_helper_set_config, .set_property = drm_atomic_helper_crtc_set_property, .destroy = intel_crtc_destroy, @@ -15107,6 +15048,7 @@ static void intel_enable_pipe_a(struct drm_device *dev) struct drm_connector *crt = NULL; struct intel_load_detect_pipe load_detect_temp; struct drm_modeset_acquire_ctx *ctx = dev->mode_config.acquire_ctx; + int ret; /* We can't just switch on the pipe A, we need to set things up with a * proper mode and output configuration. As a gross hack, enable pipe A @@ -15123,7 +15065,10 @@ static void intel_enable_pipe_a(struct drm_device *dev) if (!crt) return; - if (intel_get_load_detect_pipe(crt, NULL, &load_detect_temp, ctx)) + ret = intel_get_load_detect_pipe(crt, NULL, &load_detect_temp, ctx); + WARN(ret < 0, "All modeset mutexes are locked, but intel_get_load_detect_pipe failed\n"); + + if (ret > 0) intel_release_load_detect_pipe(crt, &load_detect_temp, ctx); } diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index fd96a6c..6e04cb5 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -4566,7 +4566,7 @@ intel_dp_unset_edid(struct intel_dp *intel_dp) intel_dp->has_audio = false; } -static enum drm_connector_status +static int intel_dp_long_pulse(struct intel_connector *intel_connector) { struct drm_connector *connector = &intel_connector->base; @@ -4577,6 +4577,8 @@ intel_dp_long_pulse(struct intel_connector *intel_connector) enum drm_connector_status status; u8 sink_irq_vector = 0; + WARN_ON(!drm_modeset_is_locked(&connector->dev->mode_config.connection_mutex)); + intel_display_power_get(to_i915(dev), intel_dp->aux_power_domain); /* Can't disconnect eDP, but you can close the lid... */ @@ -4635,14 +4637,7 @@ intel_dp_long_pulse(struct intel_connector *intel_connector) status = connector_status_disconnected; goto out; } else if (connector->status == connector_status_connected) { - /* - * If display was connected already and is still connected - * check links status, there has been known issues of - * link loss triggerring long pulse!!!! - */ - drm_modeset_lock(&dev->mode_config.connection_mutex, NULL); intel_dp_check_link_status(intel_dp); - drm_modeset_unlock(&dev->mode_config.connection_mutex); goto out; } @@ -4682,11 +4677,13 @@ out: return status; } -static enum drm_connector_status -intel_dp_detect(struct drm_connector *connector, bool force) +static int +intel_dp_detect(struct drm_connector *connector, + struct drm_modeset_acquire_ctx *ctx, + bool force) { struct intel_dp *intel_dp = intel_attached_dp(connector); - enum drm_connector_status status = connector->status; + int status = connector->status; DRM_DEBUG_KMS("[CONNECTOR:%d:%s]\n", connector->base.id, connector->name); @@ -5014,7 +5011,6 @@ void intel_dp_encoder_reset(struct drm_encoder *encoder) static const struct drm_connector_funcs intel_dp_connector_funcs = { .dpms = drm_atomic_helper_connector_dpms, - .detect = intel_dp_detect, .force = intel_dp_force, .fill_modes = drm_helper_probe_single_connector_modes, .set_property = intel_dp_set_property, @@ -5027,6 +5023,7 @@ static const struct drm_connector_funcs intel_dp_connector_funcs = { }; static const struct drm_connector_helper_funcs intel_dp_connector_helper_funcs = { + .detect_ctx = intel_dp_detect, .get_modes = intel_dp_get_modes, .mode_valid = intel_dp_mode_valid, }; diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index 313fad0..aaee394 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -1358,10 +1358,10 @@ int ironlake_get_lanes_required(int target_clock, int link_bw, int bpp); void vlv_wait_port_ready(struct drm_i915_private *dev_priv, struct intel_digital_port *dport, unsigned int expected_mask); -bool intel_get_load_detect_pipe(struct drm_connector *connector, - struct drm_display_mode *mode, - struct intel_load_detect_pipe *old, - struct drm_modeset_acquire_ctx *ctx); +int intel_get_load_detect_pipe(struct drm_connector *connector, + struct drm_display_mode *mode, + struct intel_load_detect_pipe *old, + struct drm_modeset_acquire_ctx *ctx); void intel_release_load_detect_pipe(struct drm_connector *connector, struct intel_load_detect_pipe *old, struct drm_modeset_acquire_ctx *ctx); diff --git a/drivers/gpu/drm/i915/intel_hotplug.c b/drivers/gpu/drm/i915/intel_hotplug.c index 7d21009..f120027 100644 --- a/drivers/gpu/drm/i915/intel_hotplug.c +++ b/drivers/gpu/drm/i915/intel_hotplug.c @@ -243,7 +243,8 @@ static bool intel_hpd_irq_event(struct drm_device *dev, WARN_ON(!mutex_is_locked(&dev->mode_config.mutex)); old_status = connector->status; - connector->status = connector->funcs->detect(connector, false); + connector->status = drm_helper_probe_detect(connector, NULL, false); + if (old_status == connector->status) return false; diff --git a/drivers/gpu/drm/i915/intel_pipe_crc.c b/drivers/gpu/drm/i915/intel_pipe_crc.c index 9fd9c70..206ee4f 100644 --- a/drivers/gpu/drm/i915/intel_pipe_crc.c +++ b/drivers/gpu/drm/i915/intel_pipe_crc.c @@ -522,7 +522,7 @@ static void hsw_trans_edp_pipe_A_crc_wa(struct drm_i915_private *dev_priv, goto unlock; } - state->acquire_ctx = drm_modeset_legacy_acquire_ctx(&crtc->base); + state->acquire_ctx = crtc->base.dev->mode_config.acquire_ctx; pipe_config = intel_atomic_get_crtc_state(state, crtc); if (IS_ERR(pipe_config)) { ret = PTR_ERR(pipe_config); diff --git a/drivers/gpu/drm/i915/intel_tv.c b/drivers/gpu/drm/i915/intel_tv.c index 6ed1a3c..e077c2a 100644 --- a/drivers/gpu/drm/i915/intel_tv.c +++ b/drivers/gpu/drm/i915/intel_tv.c @@ -1315,8 +1315,10 @@ static void intel_tv_find_better_format(struct drm_connector *connector) * Currently this always returns CONNECTOR_STATUS_UNKNOWN, as we need to be sure * we have a pipe programmed in order to probe the TV. */ -static enum drm_connector_status -intel_tv_detect(struct drm_connector *connector, bool force) +static int +intel_tv_detect(struct drm_connector *connector, + struct drm_modeset_acquire_ctx *ctx, + bool force) { struct drm_display_mode mode; struct intel_tv *intel_tv = intel_attached_tv(connector); @@ -1331,21 +1333,20 @@ intel_tv_detect(struct drm_connector *connector, bool force) if (force) { struct intel_load_detect_pipe tmp; - struct drm_modeset_acquire_ctx ctx; + int ret; - drm_modeset_acquire_init(&ctx, 0); + ret = intel_get_load_detect_pipe(connector, &mode, &tmp, ctx); + if (ret < 0) + return ret; - if (intel_get_load_detect_pipe(connector, &mode, &tmp, &ctx)) { + if (ret > 0) { type = intel_tv_detect_type(intel_tv, connector); - intel_release_load_detect_pipe(connector, &tmp, &ctx); + intel_release_load_detect_pipe(connector, &tmp, ctx); status = type < 0 ? connector_status_disconnected : connector_status_connected; } else status = connector_status_unknown; - - drm_modeset_drop_locks(&ctx); - drm_modeset_acquire_fini(&ctx); } else return connector->status; @@ -1516,7 +1517,6 @@ out: static const struct drm_connector_funcs intel_tv_connector_funcs = { .dpms = drm_atomic_helper_connector_dpms, - .detect = intel_tv_detect, .late_register = intel_connector_register, .early_unregister = intel_connector_unregister, .destroy = intel_tv_destroy, @@ -1528,6 +1528,7 @@ static const struct drm_connector_funcs intel_tv_connector_funcs = { }; static const struct drm_connector_helper_funcs intel_tv_connector_helper_funcs = { + .detect_ctx = intel_tv_detect, .mode_valid = intel_tv_mode_valid, .get_modes = intel_tv_get_modes, }; diff --git a/drivers/gpu/drm/imx/imx-ldb.c b/drivers/gpu/drm/imx/imx-ldb.c index 88cd11d..8fb801f 100644 --- a/drivers/gpu/drm/imx/imx-ldb.c +++ b/drivers/gpu/drm/imx/imx-ldb.c @@ -647,7 +647,6 @@ static int imx_ldb_bind(struct device *dev, struct device *master, void *data) for_each_child_of_node(np, child) { struct imx_ldb_channel *channel; - struct device_node *ep; int bus_format; ret = of_property_read_u32(child, "reg", &i); @@ -671,27 +670,11 @@ static int imx_ldb_bind(struct device *dev, struct device *master, void *data) * The output port is port@4 with an external 4-port mux or * port@2 with the internal 2-port mux. */ - ep = of_graph_get_endpoint_by_regs(child, - imx_ldb->lvds_mux ? 4 : 2, - -1); - if (ep) { - struct device_node *remote; - - remote = of_graph_get_remote_port_parent(ep); - of_node_put(ep); - if (remote) { - channel->panel = of_drm_find_panel(remote); - channel->bridge = of_drm_find_bridge(remote); - } else - return -EPROBE_DEFER; - of_node_put(remote); - - if (!channel->panel && !channel->bridge) { - dev_err(dev, "panel/bridge not found: %s\n", - remote->full_name); - return -EPROBE_DEFER; - } - } + ret = drm_of_find_panel_or_bridge(child, + imx_ldb->lvds_mux ? 4 : 2, 0, + &channel->panel, &channel->bridge); + if (ret) + return ret; /* panel ddc only if there is no bridge */ if (!channel->bridge) { diff --git a/drivers/gpu/drm/imx/parallel-display.c b/drivers/gpu/drm/imx/parallel-display.c index d5c06fd..636031a 100644 --- a/drivers/gpu/drm/imx/parallel-display.c +++ b/drivers/gpu/drm/imx/parallel-display.c @@ -19,10 +19,10 @@ #include <drm/drm_atomic_helper.h> #include <drm/drm_fb_helper.h> #include <drm/drm_crtc_helper.h> +#include <drm/drm_of.h> #include <drm/drm_panel.h> #include <linux/videodev2.h> #include <video/of_display_timing.h> -#include <linux/of_graph.h> #include "imx-drm.h" @@ -208,7 +208,6 @@ static int imx_pd_bind(struct device *dev, struct device *master, void *data) { struct drm_device *drm = data; struct device_node *np = dev->of_node; - struct device_node *ep; const u8 *edidp; struct imx_parallel_display *imxpd; int ret; @@ -237,36 +236,9 @@ static int imx_pd_bind(struct device *dev, struct device *master, void *data) imxpd->bus_format = bus_format; /* port@1 is the output port */ - ep = of_graph_get_endpoint_by_regs(np, 1, -1); - if (ep) { - struct device_node *remote; - - remote = of_graph_get_remote_port_parent(ep); - if (!remote) { - dev_warn(dev, "endpoint %s not connected\n", - ep->full_name); - of_node_put(ep); - return -ENODEV; - } - of_node_put(ep); - - imxpd->panel = of_drm_find_panel(remote); - if (imxpd->panel) { - dev_dbg(dev, "found panel %s\n", remote->full_name); - } else { - imxpd->bridge = of_drm_find_bridge(remote); - if (imxpd->bridge) - dev_dbg(dev, "found bridge %s\n", - remote->full_name); - } - if (!imxpd->panel && !imxpd->bridge) { - dev_dbg(dev, "waiting for panel or bridge %s\n", - remote->full_name); - of_node_put(remote); - return -EPROBE_DEFER; - } - of_node_put(remote); - } + ret = drm_of_find_panel_or_bridge(np, 1, 0, &imxpd->panel, &imxpd->bridge); + if (ret) + return ret; imxpd->dev = dev; diff --git a/drivers/gpu/drm/mediatek/mtk_dpi.c b/drivers/gpu/drm/mediatek/mtk_dpi.c index 3bd3bd6..32ca351 100644 --- a/drivers/gpu/drm/mediatek/mtk_dpi.c +++ b/drivers/gpu/drm/mediatek/mtk_dpi.c @@ -661,7 +661,7 @@ static int mtk_dpi_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; struct mtk_dpi *dpi; struct resource *mem; - struct device_node *ep, *bridge_node = NULL; + struct device_node *bridge_node; int comp_id; int ret; @@ -706,15 +706,9 @@ static int mtk_dpi_probe(struct platform_device *pdev) return -EINVAL; } - ep = of_graph_get_next_endpoint(dev->of_node, NULL); - if (ep) { - bridge_node = of_graph_get_remote_port_parent(ep); - of_node_put(ep); - } - if (!bridge_node) { - dev_err(dev, "Failed to find bridge node\n"); + bridge_node = of_graph_get_remote_node(dev->of_node, 0, 0); + if (!bridge_node) return -ENODEV; - } dev_info(dev, "Found bridge node: %s\n", bridge_node->full_name); diff --git a/drivers/gpu/drm/mediatek/mtk_dsi.c b/drivers/gpu/drm/mediatek/mtk_dsi.c index 1a5b0f7..808b995 100644 --- a/drivers/gpu/drm/mediatek/mtk_dsi.c +++ b/drivers/gpu/drm/mediatek/mtk_dsi.c @@ -16,12 +16,12 @@ #include <drm/drm_crtc_helper.h> #include <drm/drm_mipi_dsi.h> #include <drm/drm_panel.h> +#include <drm/drm_of.h> #include <linux/clk.h> #include <linux/component.h> #include <linux/irq.h> #include <linux/of.h> #include <linux/of_platform.h> -#include <linux/of_graph.h> #include <linux/phy/phy.h> #include <linux/platform_device.h> #include <video/mipi_display.h> @@ -1097,7 +1097,6 @@ static int mtk_dsi_probe(struct platform_device *pdev) { struct mtk_dsi *dsi; struct device *dev = &pdev->dev; - struct device_node *remote_node, *endpoint; struct resource *regs; int irq_num; int comp_id; @@ -1110,22 +1109,10 @@ static int mtk_dsi_probe(struct platform_device *pdev) dsi->host.ops = &mtk_dsi_ops; dsi->host.dev = dev; - endpoint = of_graph_get_next_endpoint(dev->of_node, NULL); - if (endpoint) { - remote_node = of_graph_get_remote_port_parent(endpoint); - if (!remote_node) { - dev_err(dev, "No panel connected\n"); - return -ENODEV; - } - - dsi->bridge = of_drm_find_bridge(remote_node); - dsi->panel = of_drm_find_panel(remote_node); - of_node_put(remote_node); - if (!dsi->bridge && !dsi->panel) { - dev_info(dev, "Waiting for bridge or panel driver\n"); - return -EPROBE_DEFER; - } - } + ret = drm_of_find_panel_or_bridge(dev->of_node, 0, 0, + &dsi->panel, &dsi->bridge); + if (ret) + return ret; dsi->engine_clk = devm_clk_get(dev, "engine"); if (IS_ERR(dsi->engine_clk)) { diff --git a/drivers/gpu/drm/mediatek/mtk_hdmi.c b/drivers/gpu/drm/mediatek/mtk_hdmi.c index c262512..41a1c03 100644 --- a/drivers/gpu/drm/mediatek/mtk_hdmi.c +++ b/drivers/gpu/drm/mediatek/mtk_hdmi.c @@ -1434,7 +1434,7 @@ static int mtk_hdmi_dt_parse_pdata(struct mtk_hdmi *hdmi, { struct device *dev = &pdev->dev; struct device_node *np = dev->of_node; - struct device_node *cec_np, *port, *ep, *remote, *i2c_np; + struct device_node *cec_np, *remote, *i2c_np; struct platform_device *cec_pdev; struct regmap *regmap; struct resource *mem; @@ -1486,29 +1486,9 @@ static int mtk_hdmi_dt_parse_pdata(struct mtk_hdmi *hdmi, if (IS_ERR(hdmi->regs)) return PTR_ERR(hdmi->regs); - port = of_graph_get_port_by_id(np, 1); - if (!port) { - dev_err(dev, "Missing output port node\n"); + remote = of_graph_get_remote_node(np, 1, 0); + if (!remote) return -EINVAL; - } - - ep = of_get_child_by_name(port, "endpoint"); - if (!ep) { - dev_err(dev, "Missing endpoint node in port %s\n", - port->full_name); - of_node_put(port); - return -EINVAL; - } - of_node_put(port); - - remote = of_graph_get_remote_port_parent(ep); - if (!remote) { - dev_err(dev, "Missing connector/bridge node for endpoint %s\n", - ep->full_name); - of_node_put(ep); - return -EINVAL; - } - of_node_put(ep); if (!of_device_is_compatible(remote, "hdmi-connector")) { hdmi->next_bridge = of_drm_find_bridge(remote); diff --git a/drivers/gpu/drm/meson/Kconfig b/drivers/gpu/drm/meson/Kconfig index 99719af..3ce51d8 100644 --- a/drivers/gpu/drm/meson/Kconfig +++ b/drivers/gpu/drm/meson/Kconfig @@ -7,3 +7,9 @@ config DRM_MESON select DRM_GEM_CMA_HELPER select VIDEOMODE_HELPERS select REGMAP_MMIO + +config DRM_MESON_DW_HDMI + tristate "HDMI Synopsys Controller support for Amlogic Meson Display" + depends on DRM_MESON + default y if DRM_MESON + select DRM_DW_HDMI diff --git a/drivers/gpu/drm/meson/Makefile b/drivers/gpu/drm/meson/Makefile index 92cf845..c5c4cc3 100644 --- a/drivers/gpu/drm/meson/Makefile +++ b/drivers/gpu/drm/meson/Makefile @@ -2,3 +2,4 @@ meson-drm-y := meson_drv.o meson_plane.o meson_crtc.o meson_venc_cvbs.o meson-drm-y += meson_viu.o meson_vpp.o meson_venc.o meson_vclk.o meson_canvas.o obj-$(CONFIG_DRM_MESON) += meson-drm.o +obj-$(CONFIG_DRM_MESON_DW_HDMI) += meson_dw_hdmi.o diff --git a/drivers/gpu/drm/meson/meson_canvas.c b/drivers/gpu/drm/meson/meson_canvas.c index 4109e36..08f6073 100644 --- a/drivers/gpu/drm/meson/meson_canvas.c +++ b/drivers/gpu/drm/meson/meson_canvas.c @@ -24,7 +24,9 @@ #include "meson_canvas.h" #include "meson_registers.h" -/* +/** + * DOC: Canvas + * * CANVAS is a memory zone where physical memory frames information * are stored for the VIU to scanout. */ diff --git a/drivers/gpu/drm/meson/meson_crtc.c b/drivers/gpu/drm/meson/meson_crtc.c index 0fe49ec..c986eb0 100644 --- a/drivers/gpu/drm/meson/meson_crtc.c +++ b/drivers/gpu/drm/meson/meson_crtc.c @@ -82,11 +82,18 @@ static const struct drm_crtc_funcs meson_crtc_funcs = { static void meson_crtc_enable(struct drm_crtc *crtc) { struct meson_crtc *meson_crtc = to_meson_crtc(crtc); - struct drm_plane *plane = meson_crtc->priv->primary_plane; + struct drm_crtc_state *crtc_state = crtc->state; struct meson_drm *priv = meson_crtc->priv; + DRM_DEBUG_DRIVER("\n"); + + if (!crtc_state) { + DRM_ERROR("Invalid crtc_state\n"); + return; + } + /* Enable VPP Postblend */ - writel(plane->state->crtc_w, + writel(crtc_state->mode.hdisplay, priv->io_base + _REG(VPP_POSTBLEND_H_SIZE)); writel_bits_relaxed(VPP_POSTBLEND_ENABLE, VPP_POSTBLEND_ENABLE, @@ -101,6 +108,7 @@ static void meson_crtc_disable(struct drm_crtc *crtc) struct meson_drm *priv = meson_crtc->priv; priv->viu.osd1_enabled = false; + priv->viu.osd1_commit = false; /* Disable VPP Postblend */ writel_bits_relaxed(VPP_POSTBLEND_ENABLE, 0, @@ -137,8 +145,7 @@ static void meson_crtc_atomic_flush(struct drm_crtc *crtc, struct meson_crtc *meson_crtc = to_meson_crtc(crtc); struct meson_drm *priv = meson_crtc->priv; - if (priv->viu.osd1_enabled) - priv->viu.osd1_commit = true; + priv->viu.osd1_commit = true; } static const struct drm_crtc_helper_funcs meson_crtc_helper_funcs = { diff --git a/drivers/gpu/drm/meson/meson_drv.c b/drivers/gpu/drm/meson/meson_drv.c index bc562a0..75382f5 100644 --- a/drivers/gpu/drm/meson/meson_drv.c +++ b/drivers/gpu/drm/meson/meson_drv.c @@ -24,6 +24,7 @@ #include <linux/module.h> #include <linux/mutex.h> #include <linux/platform_device.h> +#include <linux/component.h> #include <linux/of_graph.h> #include <drm/drmP.h> @@ -51,13 +52,14 @@ #define DRIVER_NAME "meson" #define DRIVER_DESC "Amlogic Meson DRM driver" -/* - * Video Processing Unit +/** + * DOC: Video Processing Unit * * VPU Handles the Global Video Processing, it includes management of the * clocks gates, blocks reset lines and power domains. * * What is missing : + * * - Full reset of entire video processing HW blocks * - Scaling and setup of the VPU clock * - Bus clock gates @@ -150,9 +152,9 @@ static struct regmap_config meson_regmap_config = { .max_register = 0x1000, }; -static int meson_drv_probe(struct platform_device *pdev) +static int meson_drv_bind(struct device *dev) { - struct device *dev = &pdev->dev; + struct platform_device *pdev = to_platform_device(dev); struct meson_drm *priv; struct drm_device *drm; struct resource *res; @@ -215,6 +217,15 @@ static int meson_drv_probe(struct platform_device *pdev) drm_vblank_init(drm, 1); drm_mode_config_init(drm); + drm->mode_config.max_width = 3840; + drm->mode_config.max_height = 2160; + drm->mode_config.funcs = &meson_mode_config_funcs; + + /* Hardware Initialization */ + + meson_venc_init(priv); + meson_vpp_init(priv); + meson_viu_init(priv); /* Encoder Initialization */ @@ -222,11 +233,11 @@ static int meson_drv_probe(struct platform_device *pdev) if (ret) goto free_drm; - /* Hardware Initialization */ - - meson_venc_init(priv); - meson_vpp_init(priv); - meson_viu_init(priv); + ret = component_bind_all(drm->dev, drm); + if (ret) { + dev_err(drm->dev, "Couldn't bind all components\n"); + goto free_drm; + } ret = meson_plane_create(priv); if (ret) @@ -241,9 +252,6 @@ static int meson_drv_probe(struct platform_device *pdev) goto free_drm; drm_mode_config_reset(drm); - drm->mode_config.max_width = 8192; - drm->mode_config.max_height = 8192; - drm->mode_config.funcs = &meson_mode_config_funcs; priv->fbdev = drm_fbdev_cma_init(drm, 32, drm->mode_config.num_connector); @@ -268,9 +276,9 @@ free_drm: return ret; } -static int meson_drv_remove(struct platform_device *pdev) +static void meson_drv_unbind(struct device *dev) { - struct drm_device *drm = dev_get_drvdata(&pdev->dev); + struct drm_device *drm = dev_get_drvdata(dev); struct meson_drm *priv = drm->dev_private; drm_dev_unregister(drm); @@ -280,9 +288,88 @@ static int meson_drv_remove(struct platform_device *pdev) drm_vblank_cleanup(drm); drm_dev_unref(drm); - return 0; } +static const struct component_master_ops meson_drv_master_ops = { + .bind = meson_drv_bind, + .unbind = meson_drv_unbind, +}; + +static int compare_of(struct device *dev, void *data) +{ + DRM_DEBUG_DRIVER("Comparing of node %s with %s\n", + of_node_full_name(dev->of_node), + of_node_full_name(data)); + + return dev->of_node == data; +} + +/* Possible connectors nodes to ignore */ +static const struct of_device_id connectors_match[] = { + { .compatible = "composite-video-connector" }, + { .compatible = "svideo-connector" }, + { .compatible = "hdmi-connector" }, + { .compatible = "dvi-connector" }, + {} +}; + +static int meson_probe_remote(struct platform_device *pdev, + struct component_match **match, + struct device_node *parent, + struct device_node *remote) +{ + struct device_node *ep, *remote_node; + int count = 1; + + /* If node is a connector, return and do not add to match table */ + if (of_match_node(connectors_match, remote)) + return 1; + + component_match_add(&pdev->dev, match, compare_of, remote); + + for_each_endpoint_of_node(remote, ep) { + remote_node = of_graph_get_remote_port_parent(ep); + if (!remote_node || + remote_node == parent || /* Ignore parent endpoint */ + !of_device_is_available(remote_node)) + continue; + + count += meson_probe_remote(pdev, match, remote, remote_node); + + of_node_put(remote_node); + } + + return count; +} + +static int meson_drv_probe(struct platform_device *pdev) +{ + struct component_match *match = NULL; + struct device_node *np = pdev->dev.of_node; + struct device_node *ep, *remote; + int count = 0; + + for_each_endpoint_of_node(np, ep) { + remote = of_graph_get_remote_port_parent(ep); + if (!remote || !of_device_is_available(remote)) + continue; + + count += meson_probe_remote(pdev, &match, np, remote); + } + + /* If some endpoints were found, initialize the nodes */ + if (count) { + dev_info(&pdev->dev, "Queued %d outputs on vpu\n", count); + + return component_master_add_with_match(&pdev->dev, + &meson_drv_master_ops, + match); + } + + /* If no output endpoints were available, simply bail out */ + return 0; +}; + static const struct of_device_id dt_match[] = { { .compatible = "amlogic,meson-gxbb-vpu" }, { .compatible = "amlogic,meson-gxl-vpu" }, @@ -293,7 +380,6 @@ MODULE_DEVICE_TABLE(of, dt_match); static struct platform_driver meson_drm_platform_driver = { .probe = meson_drv_probe, - .remove = meson_drv_remove, .driver = { .name = "meson-drm", .of_match_table = dt_match, diff --git a/drivers/gpu/drm/meson/meson_drv.h b/drivers/gpu/drm/meson/meson_drv.h index 6195327..5e8b392 100644 --- a/drivers/gpu/drm/meson/meson_drv.h +++ b/drivers/gpu/drm/meson/meson_drv.h @@ -47,6 +47,9 @@ struct meson_drm { struct { unsigned int current_mode; + bool hdmi_repeat; + bool venc_repeat; + bool hdmi_use_enci; } venc; }; diff --git a/drivers/gpu/drm/meson/meson_dw_hdmi.c b/drivers/gpu/drm/meson/meson_dw_hdmi.c new file mode 100644 index 0000000..7b86eb7 --- /dev/null +++ b/drivers/gpu/drm/meson/meson_dw_hdmi.c @@ -0,0 +1,919 @@ +/* + * Copyright (C) 2016 BayLibre, SAS + * Author: Neil Armstrong <narmstrong@baylibre.com> + * Copyright (C) 2015 Amlogic, Inc. All rights reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <http://www.gnu.org/licenses/>. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/component.h> +#include <linux/of_graph.h> +#include <linux/reset.h> +#include <linux/clk.h> + +#include <drm/drmP.h> +#include <drm/drm_edid.h> +#include <drm/drm_crtc_helper.h> +#include <drm/drm_atomic_helper.h> +#include <drm/bridge/dw_hdmi.h> + +#include <uapi/linux/media-bus-format.h> +#include <uapi/linux/videodev2.h> + +#include "meson_drv.h" +#include "meson_venc.h" +#include "meson_vclk.h" +#include "meson_dw_hdmi.h" +#include "meson_registers.h" + +#define DRIVER_NAME "meson-dw-hdmi" +#define DRIVER_DESC "Amlogic Meson HDMI-TX DRM driver" + +/** + * DOC: HDMI Output + * + * HDMI Output is composed of : + * + * - A Synopsys DesignWare HDMI Controller IP + * - A TOP control block controlling the Clocks and PHY + * - A custom HDMI PHY in order convert video to TMDS signal + * + * .. code:: + * + * ___________________________________ + * | HDMI TOP |<= HPD + * |___________________________________| + * | | | + * | Synopsys HDMI | HDMI PHY |=> TMDS + * | Controller |________________| + * |___________________________________|<=> DDC + * + * + * The HDMI TOP block only supports HPD sensing. + * The Synopsys HDMI Controller interrupt is routed + * through the TOP Block interrupt. + * Communication to the TOP Block and the Synopsys + * HDMI Controller is done a pair of addr+read/write + * registers. + * The HDMI PHY is configured by registers in the + * HHI register block. + * + * Pixel data arrives in 4:4:4 format from the VENC + * block and the VPU HDMI mux selects either the ENCI + * encoder for the 576i or 480i formats or the ENCP + * encoder for all the other formats including + * interlaced HD formats. + * The VENC uses a DVI encoder on top of the ENCI + * or ENCP encoders to generate DVI timings for the + * HDMI controller. + * + * GXBB, GXL and GXM embeds the Synopsys DesignWare + * HDMI TX IP version 2.01a with HDCP and I2C & S/PDIF + * audio source interfaces. + * + * We handle the following features : + * + * - HPD Rise & Fall interrupt + * - HDMI Controller Interrupt + * - HDMI PHY Init for 480i to 1080p60 + * - VENC & HDMI Clock setup for 480i to 1080p60 + * - VENC Mode setup for 480i to 1080p60 + * + * What is missing : + * + * - PHY, Clock and Mode setup for 2k && 4k modes + * - SDDC Scrambling mode for HDMI 2.0a + * - HDCP Setup + * - CEC Management + */ + +/* TOP Block Communication Channel */ +#define HDMITX_TOP_ADDR_REG 0x0 +#define HDMITX_TOP_DATA_REG 0x4 +#define HDMITX_TOP_CTRL_REG 0x8 + +/* Controller Communication Channel */ +#define HDMITX_DWC_ADDR_REG 0x10 +#define HDMITX_DWC_DATA_REG 0x14 +#define HDMITX_DWC_CTRL_REG 0x18 + +/* HHI Registers */ +#define HHI_MEM_PD_REG0 0x100 /* 0x40 */ +#define HHI_HDMI_CLK_CNTL 0x1cc /* 0x73 */ +#define HHI_HDMI_PHY_CNTL0 0x3a0 /* 0xe8 */ +#define HHI_HDMI_PHY_CNTL1 0x3a4 /* 0xe9 */ +#define HHI_HDMI_PHY_CNTL2 0x3a8 /* 0xea */ +#define HHI_HDMI_PHY_CNTL3 0x3ac /* 0xeb */ + +static DEFINE_SPINLOCK(reg_lock); + +enum meson_venc_source { + MESON_VENC_SOURCE_NONE = 0, + MESON_VENC_SOURCE_ENCI = 1, + MESON_VENC_SOURCE_ENCP = 2, +}; + +struct meson_dw_hdmi { + struct drm_encoder encoder; + struct dw_hdmi_plat_data dw_plat_data; + struct meson_drm *priv; + struct device *dev; + void __iomem *hdmitx; + struct reset_control *hdmitx_apb; + struct reset_control *hdmitx_ctrl; + struct reset_control *hdmitx_phy; + struct clk *hdmi_pclk; + struct clk *venci_clk; + u32 irq_stat; +}; +#define encoder_to_meson_dw_hdmi(x) \ + container_of(x, struct meson_dw_hdmi, encoder) + +static inline int dw_hdmi_is_compatible(struct meson_dw_hdmi *dw_hdmi, + const char *compat) +{ + return of_device_is_compatible(dw_hdmi->dev->of_node, compat); +} + +/* PHY (via TOP bridge) and Controller dedicated register interface */ + +static unsigned int dw_hdmi_top_read(struct meson_dw_hdmi *dw_hdmi, + unsigned int addr) +{ + unsigned long flags; + unsigned int data; + + spin_lock_irqsave(®_lock, flags); + + /* ADDR must be written twice */ + writel(addr & 0xffff, dw_hdmi->hdmitx + HDMITX_TOP_ADDR_REG); + writel(addr & 0xffff, dw_hdmi->hdmitx + HDMITX_TOP_ADDR_REG); + + /* Read needs a second DATA read */ + data = readl(dw_hdmi->hdmitx + HDMITX_TOP_DATA_REG); + data = readl(dw_hdmi->hdmitx + HDMITX_TOP_DATA_REG); + + spin_unlock_irqrestore(®_lock, flags); + + return data; +} + +static inline void dw_hdmi_top_write(struct meson_dw_hdmi *dw_hdmi, + unsigned int addr, unsigned int data) +{ + unsigned long flags; + + spin_lock_irqsave(®_lock, flags); + + /* ADDR must be written twice */ + writel(addr & 0xffff, dw_hdmi->hdmitx + HDMITX_TOP_ADDR_REG); + writel(addr & 0xffff, dw_hdmi->hdmitx + HDMITX_TOP_ADDR_REG); + + /* Write needs single DATA write */ + writel(data, dw_hdmi->hdmitx + HDMITX_TOP_DATA_REG); + + spin_unlock_irqrestore(®_lock, flags); +} + +/* Helper to change specific bits in PHY registers */ +static inline void dw_hdmi_top_write_bits(struct meson_dw_hdmi *dw_hdmi, + unsigned int addr, + unsigned int mask, + unsigned int val) +{ + unsigned int data = dw_hdmi_top_read(dw_hdmi, addr); + + data &= ~mask; + data |= val; + + dw_hdmi_top_write(dw_hdmi, addr, data); +} + +static unsigned int dw_hdmi_dwc_read(struct meson_dw_hdmi *dw_hdmi, + unsigned int addr) +{ + unsigned long flags; + unsigned int data; + + spin_lock_irqsave(®_lock, flags); + + /* ADDR must be written twice */ + writel(addr & 0xffff, dw_hdmi->hdmitx + HDMITX_DWC_ADDR_REG); + writel(addr & 0xffff, dw_hdmi->hdmitx + HDMITX_DWC_ADDR_REG); + + /* Read needs a second DATA read */ + data = readl(dw_hdmi->hdmitx + HDMITX_DWC_DATA_REG); + data = readl(dw_hdmi->hdmitx + HDMITX_DWC_DATA_REG); + + spin_unlock_irqrestore(®_lock, flags); + + return data; +} + +static inline void dw_hdmi_dwc_write(struct meson_dw_hdmi *dw_hdmi, + unsigned int addr, unsigned int data) +{ + unsigned long flags; + + spin_lock_irqsave(®_lock, flags); + + /* ADDR must be written twice */ + writel(addr & 0xffff, dw_hdmi->hdmitx + HDMITX_DWC_ADDR_REG); + writel(addr & 0xffff, dw_hdmi->hdmitx + HDMITX_DWC_ADDR_REG); + + /* Write needs single DATA write */ + writel(data, dw_hdmi->hdmitx + HDMITX_DWC_DATA_REG); + + spin_unlock_irqrestore(®_lock, flags); +} + +/* Helper to change specific bits in controller registers */ +static inline void dw_hdmi_dwc_write_bits(struct meson_dw_hdmi *dw_hdmi, + unsigned int addr, + unsigned int mask, + unsigned int val) +{ + unsigned int data = dw_hdmi_dwc_read(dw_hdmi, addr); + + data &= ~mask; + data |= val; + + dw_hdmi_dwc_write(dw_hdmi, addr, data); +} + +/* Bridge */ + +/* Setup PHY bandwidth modes */ +static void meson_hdmi_phy_setup_mode(struct meson_dw_hdmi *dw_hdmi, + struct drm_display_mode *mode) +{ + struct meson_drm *priv = dw_hdmi->priv; + unsigned int pixel_clock = mode->clock; + + if (dw_hdmi_is_compatible(dw_hdmi, "amlogic,meson-gxl-dw-hdmi") || + dw_hdmi_is_compatible(dw_hdmi, "amlogic,meson-gxm-dw-hdmi")) { + if (pixel_clock >= 371250) { + /* 5.94Gbps, 3.7125Gbps */ + regmap_write(priv->hhi, HHI_HDMI_PHY_CNTL0, 0x333d3282); + regmap_write(priv->hhi, HHI_HDMI_PHY_CNTL3, 0x2136315b); + } else if (pixel_clock >= 297000) { + /* 2.97Gbps */ + regmap_write(priv->hhi, HHI_HDMI_PHY_CNTL0, 0x33303382); + regmap_write(priv->hhi, HHI_HDMI_PHY_CNTL3, 0x2036315b); + } else if (pixel_clock >= 148500) { + /* 1.485Gbps */ + regmap_write(priv->hhi, HHI_HDMI_PHY_CNTL0, 0x33303362); + regmap_write(priv->hhi, HHI_HDMI_PHY_CNTL3, 0x2016315b); + } else { + /* 742.5Mbps, and below */ + regmap_write(priv->hhi, HHI_HDMI_PHY_CNTL0, 0x33604142); + regmap_write(priv->hhi, HHI_HDMI_PHY_CNTL3, 0x0016315b); + } + } else if (dw_hdmi_is_compatible(dw_hdmi, + "amlogic,meson-gxbb-dw-hdmi")) { + if (pixel_clock >= 371250) { + /* 5.94Gbps, 3.7125Gbps */ + regmap_write(priv->hhi, HHI_HDMI_PHY_CNTL0, 0x33353245); + regmap_write(priv->hhi, HHI_HDMI_PHY_CNTL3, 0x2100115b); + } else if (pixel_clock >= 297000) { + /* 2.97Gbps */ + regmap_write(priv->hhi, HHI_HDMI_PHY_CNTL0, 0x33634283); + regmap_write(priv->hhi, HHI_HDMI_PHY_CNTL3, 0xb000115b); + } else { + /* 1.485Gbps, and below */ + regmap_write(priv->hhi, HHI_HDMI_PHY_CNTL0, 0x33632122); + regmap_write(priv->hhi, HHI_HDMI_PHY_CNTL3, 0x2000115b); + } + } +} + +static inline void dw_hdmi_phy_reset(struct meson_dw_hdmi *dw_hdmi) +{ + struct meson_drm *priv = dw_hdmi->priv; + + /* Enable and software reset */ + regmap_update_bits(priv->hhi, HHI_HDMI_PHY_CNTL1, 0xf, 0xf); + + mdelay(2); + + /* Enable and unreset */ + regmap_update_bits(priv->hhi, HHI_HDMI_PHY_CNTL1, 0xf, 0xe); + + mdelay(2); +} + +static void dw_hdmi_set_vclk(struct meson_dw_hdmi *dw_hdmi, + struct drm_display_mode *mode) +{ + struct meson_drm *priv = dw_hdmi->priv; + int vic = drm_match_cea_mode(mode); + unsigned int vclk_freq; + unsigned int venc_freq; + unsigned int hdmi_freq; + + vclk_freq = mode->clock; + + if (mode->flags & DRM_MODE_FLAG_DBLCLK) + vclk_freq *= 2; + + venc_freq = vclk_freq; + hdmi_freq = vclk_freq; + + if (meson_venc_hdmi_venc_repeat(vic)) + venc_freq *= 2; + + vclk_freq = max(venc_freq, hdmi_freq); + + if (mode->flags & DRM_MODE_FLAG_DBLCLK) + venc_freq /= 2; + + DRM_DEBUG_DRIVER("vclk:%d venc=%d hdmi=%d enci=%d\n", + vclk_freq, venc_freq, hdmi_freq, + priv->venc.hdmi_use_enci); + + meson_vclk_setup(priv, MESON_VCLK_TARGET_HDMI, vclk_freq, + venc_freq, hdmi_freq, priv->venc.hdmi_use_enci); +} + +static int dw_hdmi_phy_init(struct dw_hdmi *hdmi, void *data, + struct drm_display_mode *mode) +{ + struct meson_dw_hdmi *dw_hdmi = (struct meson_dw_hdmi *)data; + struct meson_drm *priv = dw_hdmi->priv; + unsigned int wr_clk = + readl_relaxed(priv->io_base + _REG(VPU_HDMI_SETTING)); + + DRM_DEBUG_DRIVER("%d:\"%s\"\n", mode->base.id, mode->name); + + /* Enable clocks */ + regmap_update_bits(priv->hhi, HHI_HDMI_CLK_CNTL, 0xffff, 0x100); + + /* Bring HDMITX MEM output of power down */ + regmap_update_bits(priv->hhi, HHI_MEM_PD_REG0, 0xff << 8, 0); + + /* Bring out of reset */ + dw_hdmi_top_write(dw_hdmi, HDMITX_TOP_SW_RESET, 0); + + /* Enable internal pixclk, tmds_clk, spdif_clk, i2s_clk, cecclk */ + dw_hdmi_top_write_bits(dw_hdmi, HDMITX_TOP_CLK_CNTL, + 0x3, 0x3); + dw_hdmi_top_write_bits(dw_hdmi, HDMITX_TOP_CLK_CNTL, + 0x3 << 4, 0x3 << 4); + + /* Enable normal output to PHY */ + dw_hdmi_top_write(dw_hdmi, HDMITX_TOP_BIST_CNTL, BIT(12)); + + /* TMDS pattern setup (TOFIX pattern for 4k2k scrambling) */ + dw_hdmi_top_write(dw_hdmi, HDMITX_TOP_TMDS_CLK_PTTN_01, 0x001f001f); + dw_hdmi_top_write(dw_hdmi, HDMITX_TOP_TMDS_CLK_PTTN_23, 0x001f001f); + + /* Load TMDS pattern */ + dw_hdmi_top_write(dw_hdmi, HDMITX_TOP_TMDS_CLK_PTTN_CNTL, 0x1); + msleep(20); + dw_hdmi_top_write(dw_hdmi, HDMITX_TOP_TMDS_CLK_PTTN_CNTL, 0x2); + + /* Setup PHY parameters */ + meson_hdmi_phy_setup_mode(dw_hdmi, mode); + + /* Setup PHY */ + regmap_update_bits(priv->hhi, HHI_HDMI_PHY_CNTL1, + 0xffff << 16, 0x0390 << 16); + + /* BIT_INVERT */ + if (dw_hdmi_is_compatible(dw_hdmi, "amlogic,meson-gxl-dw-hdmi") || + dw_hdmi_is_compatible(dw_hdmi, "amlogic,meson-gxm-dw-hdmi")) + regmap_update_bits(priv->hhi, HHI_HDMI_PHY_CNTL1, + BIT(17), 0); + else + regmap_update_bits(priv->hhi, HHI_HDMI_PHY_CNTL1, + BIT(17), BIT(17)); + + /* Disable clock, fifo, fifo_wr */ + regmap_update_bits(priv->hhi, HHI_HDMI_PHY_CNTL1, 0xf, 0); + + msleep(100); + + /* Reset PHY 3 times in a row */ + dw_hdmi_phy_reset(dw_hdmi); + dw_hdmi_phy_reset(dw_hdmi); + dw_hdmi_phy_reset(dw_hdmi); + + /* Temporary Disable VENC video stream */ + if (priv->venc.hdmi_use_enci) + writel_relaxed(0, priv->io_base + _REG(ENCI_VIDEO_EN)); + else + writel_relaxed(0, priv->io_base + _REG(ENCP_VIDEO_EN)); + + /* Temporary Disable HDMI video stream to HDMI-TX */ + writel_bits_relaxed(0x3, 0, + priv->io_base + _REG(VPU_HDMI_SETTING)); + writel_bits_relaxed(0xf << 8, 0, + priv->io_base + _REG(VPU_HDMI_SETTING)); + + /* Re-Enable VENC video stream */ + if (priv->venc.hdmi_use_enci) + writel_relaxed(1, priv->io_base + _REG(ENCI_VIDEO_EN)); + else + writel_relaxed(1, priv->io_base + _REG(ENCP_VIDEO_EN)); + + /* Push back HDMI clock settings */ + writel_bits_relaxed(0xf << 8, wr_clk & (0xf << 8), + priv->io_base + _REG(VPU_HDMI_SETTING)); + + /* Enable and Select HDMI video source for HDMI-TX */ + if (priv->venc.hdmi_use_enci) + writel_bits_relaxed(0x3, MESON_VENC_SOURCE_ENCI, + priv->io_base + _REG(VPU_HDMI_SETTING)); + else + writel_bits_relaxed(0x3, MESON_VENC_SOURCE_ENCP, + priv->io_base + _REG(VPU_HDMI_SETTING)); + + return 0; +} + +static void dw_hdmi_phy_disable(struct dw_hdmi *hdmi, + void *data) +{ + struct meson_dw_hdmi *dw_hdmi = (struct meson_dw_hdmi *)data; + struct meson_drm *priv = dw_hdmi->priv; + + DRM_DEBUG_DRIVER("\n"); + + regmap_write(priv->hhi, HHI_HDMI_PHY_CNTL0, 0); +} + +static enum drm_connector_status dw_hdmi_read_hpd(struct dw_hdmi *hdmi, + void *data) +{ + struct meson_dw_hdmi *dw_hdmi = (struct meson_dw_hdmi *)data; + + return !!dw_hdmi_top_read(dw_hdmi, HDMITX_TOP_STAT0) ? + connector_status_connected : connector_status_disconnected; +} + +static void dw_hdmi_setup_hpd(struct dw_hdmi *hdmi, + void *data) +{ + struct meson_dw_hdmi *dw_hdmi = (struct meson_dw_hdmi *)data; + + /* Setup HPD Filter */ + dw_hdmi_top_write(dw_hdmi, HDMITX_TOP_HPD_FILTER, + (0xa << 12) | 0xa0); + + /* Clear interrupts */ + dw_hdmi_top_write(dw_hdmi, HDMITX_TOP_INTR_STAT_CLR, + HDMITX_TOP_INTR_HPD_RISE | HDMITX_TOP_INTR_HPD_FALL); + + /* Unmask interrupts */ + dw_hdmi_top_write_bits(dw_hdmi, HDMITX_TOP_INTR_MASKN, + HDMITX_TOP_INTR_HPD_RISE | HDMITX_TOP_INTR_HPD_FALL, + HDMITX_TOP_INTR_HPD_RISE | HDMITX_TOP_INTR_HPD_FALL); +} + +static const struct dw_hdmi_phy_ops meson_dw_hdmi_phy_ops = { + .init = dw_hdmi_phy_init, + .disable = dw_hdmi_phy_disable, + .read_hpd = dw_hdmi_read_hpd, + .setup_hpd = dw_hdmi_setup_hpd, +}; + +static irqreturn_t dw_hdmi_top_irq(int irq, void *dev_id) +{ + struct meson_dw_hdmi *dw_hdmi = dev_id; + u32 stat; + + stat = dw_hdmi_top_read(dw_hdmi, HDMITX_TOP_INTR_STAT); + dw_hdmi_top_write(dw_hdmi, HDMITX_TOP_INTR_STAT_CLR, stat); + + /* HPD Events, handle in the threaded interrupt handler */ + if (stat & (HDMITX_TOP_INTR_HPD_RISE | HDMITX_TOP_INTR_HPD_FALL)) { + dw_hdmi->irq_stat = stat; + return IRQ_WAKE_THREAD; + } + + /* HDMI Controller Interrupt */ + if (stat & 1) + return IRQ_NONE; + + /* TOFIX Handle HDCP Interrupts */ + + return IRQ_HANDLED; +} + +/* Threaded interrupt handler to manage HPD events */ +static irqreturn_t dw_hdmi_top_thread_irq(int irq, void *dev_id) +{ + struct meson_dw_hdmi *dw_hdmi = dev_id; + u32 stat = dw_hdmi->irq_stat; + + /* HPD Events */ + if (stat & (HDMITX_TOP_INTR_HPD_RISE | HDMITX_TOP_INTR_HPD_FALL)) { + bool hpd_connected = false; + + if (stat & HDMITX_TOP_INTR_HPD_RISE) + hpd_connected = true; + + dw_hdmi_setup_rx_sense(dw_hdmi->dev, hpd_connected, + hpd_connected); + + drm_helper_hpd_irq_event(dw_hdmi->encoder.dev); + } + + return IRQ_HANDLED; +} + +/* TOFIX Enable support for non-vic modes */ +static enum drm_mode_status dw_hdmi_mode_valid(struct drm_connector *connector, + struct drm_display_mode *mode) +{ + unsigned int vclk_freq; + unsigned int venc_freq; + unsigned int hdmi_freq; + int vic = drm_match_cea_mode(mode); + + DRM_DEBUG_DRIVER("Modeline %d:\"%s\" %d %d %d %d %d %d %d %d %d %d 0x%x 0x%x\n", + mode->base.id, mode->name, mode->vrefresh, mode->clock, + mode->hdisplay, mode->hsync_start, + mode->hsync_end, mode->htotal, + mode->vdisplay, mode->vsync_start, + mode->vsync_end, mode->vtotal, mode->type, mode->flags); + + /* For now, only accept VIC modes */ + if (!vic) + return MODE_BAD; + + /* For now, filter by supported VIC modes */ + if (!meson_venc_hdmi_supported_vic(vic)) + return MODE_BAD; + + vclk_freq = mode->clock; + + /* 480i/576i needs global pixel doubling */ + if (mode->flags & DRM_MODE_FLAG_DBLCLK) + vclk_freq *= 2; + + venc_freq = vclk_freq; + hdmi_freq = vclk_freq; + + /* VENC double pixels for 1080i and 720p modes */ + if (meson_venc_hdmi_venc_repeat(vic)) + venc_freq *= 2; + + vclk_freq = max(venc_freq, hdmi_freq); + + if (mode->flags & DRM_MODE_FLAG_DBLCLK) + venc_freq /= 2; + + dev_dbg(connector->dev->dev, "%s: vclk:%d venc=%d hdmi=%d\n", __func__, + vclk_freq, venc_freq, hdmi_freq); + + /* Finally filter by configurable vclk frequencies */ + switch (vclk_freq) { + case 54000: + case 74250: + case 148500: + case 297000: + case 594000: + return MODE_OK; + } + + return MODE_CLOCK_RANGE; +} + +/* Encoder */ + +static void meson_venc_hdmi_encoder_destroy(struct drm_encoder *encoder) +{ + drm_encoder_cleanup(encoder); +} + +static const struct drm_encoder_funcs meson_venc_hdmi_encoder_funcs = { + .destroy = meson_venc_hdmi_encoder_destroy, +}; + +static int meson_venc_hdmi_encoder_atomic_check(struct drm_encoder *encoder, + struct drm_crtc_state *crtc_state, + struct drm_connector_state *conn_state) +{ + return 0; +} + +static void meson_venc_hdmi_encoder_disable(struct drm_encoder *encoder) +{ + struct meson_dw_hdmi *dw_hdmi = encoder_to_meson_dw_hdmi(encoder); + struct meson_drm *priv = dw_hdmi->priv; + + DRM_DEBUG_DRIVER("\n"); + + writel_bits_relaxed(0x3, 0, + priv->io_base + _REG(VPU_HDMI_SETTING)); + + writel_relaxed(0, priv->io_base + _REG(ENCI_VIDEO_EN)); + writel_relaxed(0, priv->io_base + _REG(ENCP_VIDEO_EN)); +} + +static void meson_venc_hdmi_encoder_enable(struct drm_encoder *encoder) +{ + struct meson_dw_hdmi *dw_hdmi = encoder_to_meson_dw_hdmi(encoder); + struct meson_drm *priv = dw_hdmi->priv; + + DRM_DEBUG_DRIVER("%s\n", priv->venc.hdmi_use_enci ? "VENCI" : "VENCP"); + + if (priv->venc.hdmi_use_enci) + writel_relaxed(1, priv->io_base + _REG(ENCI_VIDEO_EN)); + else + writel_relaxed(1, priv->io_base + _REG(ENCP_VIDEO_EN)); +} + +static void meson_venc_hdmi_encoder_mode_set(struct drm_encoder *encoder, + struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + struct meson_dw_hdmi *dw_hdmi = encoder_to_meson_dw_hdmi(encoder); + struct meson_drm *priv = dw_hdmi->priv; + int vic = drm_match_cea_mode(mode); + + DRM_DEBUG_DRIVER("%d:\"%s\" vic %d\n", + mode->base.id, mode->name, vic); + + /* Should have been filtered */ + if (!vic) + return; + + /* VENC + VENC-DVI Mode setup */ + meson_venc_hdmi_mode_set(priv, vic, mode); + + /* VCLK Set clock */ + dw_hdmi_set_vclk(dw_hdmi, mode); + + /* Setup YUV444 to HDMI-TX, no 10bit diphering */ + writel_relaxed(0, priv->io_base + _REG(VPU_HDMI_FMT_CTRL)); +} + +static const struct drm_encoder_helper_funcs + meson_venc_hdmi_encoder_helper_funcs = { + .atomic_check = meson_venc_hdmi_encoder_atomic_check, + .disable = meson_venc_hdmi_encoder_disable, + .enable = meson_venc_hdmi_encoder_enable, + .mode_set = meson_venc_hdmi_encoder_mode_set, +}; + +/* DW HDMI Regmap */ + +static int meson_dw_hdmi_reg_read(void *context, unsigned int reg, + unsigned int *result) +{ + *result = dw_hdmi_dwc_read(context, reg); + + return 0; + +} + +static int meson_dw_hdmi_reg_write(void *context, unsigned int reg, + unsigned int val) +{ + dw_hdmi_dwc_write(context, reg, val); + + return 0; +} + +static const struct regmap_config meson_dw_hdmi_regmap_config = { + .reg_bits = 32, + .val_bits = 8, + .reg_read = meson_dw_hdmi_reg_read, + .reg_write = meson_dw_hdmi_reg_write, + .max_register = 0x10000, +}; + +static bool meson_hdmi_connector_is_available(struct device *dev) +{ + struct device_node *ep, *remote; + + /* HDMI Connector is on the second port, first endpoint */ + ep = of_graph_get_endpoint_by_regs(dev->of_node, 1, 0); + if (!ep) + return false; + + /* If the endpoint node exists, consider it enabled */ + remote = of_graph_get_remote_port(ep); + if (remote) { + of_node_put(ep); + return true; + } + + of_node_put(ep); + of_node_put(remote); + + return false; +} + +static int meson_dw_hdmi_bind(struct device *dev, struct device *master, + void *data) +{ + struct platform_device *pdev = to_platform_device(dev); + struct meson_dw_hdmi *meson_dw_hdmi; + struct drm_device *drm = data; + struct meson_drm *priv = drm->dev_private; + struct dw_hdmi_plat_data *dw_plat_data; + struct drm_encoder *encoder; + struct resource *res; + int irq; + int ret; + + DRM_DEBUG_DRIVER("\n"); + + if (!meson_hdmi_connector_is_available(dev)) { + dev_info(drm->dev, "HDMI Output connector not available\n"); + return -ENODEV; + } + + meson_dw_hdmi = devm_kzalloc(dev, sizeof(*meson_dw_hdmi), + GFP_KERNEL); + if (!meson_dw_hdmi) + return -ENOMEM; + + meson_dw_hdmi->priv = priv; + meson_dw_hdmi->dev = dev; + dw_plat_data = &meson_dw_hdmi->dw_plat_data; + encoder = &meson_dw_hdmi->encoder; + + meson_dw_hdmi->hdmitx_apb = devm_reset_control_get_exclusive(dev, + "hdmitx_apb"); + if (IS_ERR(meson_dw_hdmi->hdmitx_apb)) { + dev_err(dev, "Failed to get hdmitx_apb reset\n"); + return PTR_ERR(meson_dw_hdmi->hdmitx_apb); + } + + meson_dw_hdmi->hdmitx_ctrl = devm_reset_control_get_exclusive(dev, + "hdmitx"); + if (IS_ERR(meson_dw_hdmi->hdmitx_ctrl)) { + dev_err(dev, "Failed to get hdmitx reset\n"); + return PTR_ERR(meson_dw_hdmi->hdmitx_ctrl); + } + + meson_dw_hdmi->hdmitx_phy = devm_reset_control_get_exclusive(dev, + "hdmitx_phy"); + if (IS_ERR(meson_dw_hdmi->hdmitx_phy)) { + dev_err(dev, "Failed to get hdmitx_phy reset\n"); + return PTR_ERR(meson_dw_hdmi->hdmitx_phy); + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + meson_dw_hdmi->hdmitx = devm_ioremap_resource(dev, res); + if (IS_ERR(meson_dw_hdmi->hdmitx)) + return PTR_ERR(meson_dw_hdmi->hdmitx); + + meson_dw_hdmi->hdmi_pclk = devm_clk_get(dev, "isfr"); + if (IS_ERR(meson_dw_hdmi->hdmi_pclk)) { + dev_err(dev, "Unable to get HDMI pclk\n"); + return PTR_ERR(meson_dw_hdmi->hdmi_pclk); + } + clk_prepare_enable(meson_dw_hdmi->hdmi_pclk); + + meson_dw_hdmi->venci_clk = devm_clk_get(dev, "venci"); + if (IS_ERR(meson_dw_hdmi->venci_clk)) { + dev_err(dev, "Unable to get venci clk\n"); + return PTR_ERR(meson_dw_hdmi->venci_clk); + } + clk_prepare_enable(meson_dw_hdmi->venci_clk); + + dw_plat_data->regm = devm_regmap_init(dev, NULL, meson_dw_hdmi, + &meson_dw_hdmi_regmap_config); + if (IS_ERR(dw_plat_data->regm)) + return PTR_ERR(dw_plat_data->regm); + + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + dev_err(dev, "Failed to get hdmi top irq\n"); + return irq; + } + + ret = devm_request_threaded_irq(dev, irq, dw_hdmi_top_irq, + dw_hdmi_top_thread_irq, IRQF_SHARED, + "dw_hdmi_top_irq", meson_dw_hdmi); + if (ret) { + dev_err(dev, "Failed to request hdmi top irq\n"); + return ret; + } + + /* Encoder */ + + drm_encoder_helper_add(encoder, &meson_venc_hdmi_encoder_helper_funcs); + + ret = drm_encoder_init(drm, encoder, &meson_venc_hdmi_encoder_funcs, + DRM_MODE_ENCODER_TMDS, "meson_hdmi"); + if (ret) { + dev_err(priv->dev, "Failed to init HDMI encoder\n"); + return ret; + } + + encoder->possible_crtcs = BIT(0); + + DRM_DEBUG_DRIVER("encoder initialized\n"); + + /* Enable clocks */ + regmap_update_bits(priv->hhi, HHI_HDMI_CLK_CNTL, 0xffff, 0x100); + + /* Bring HDMITX MEM output of power down */ + regmap_update_bits(priv->hhi, HHI_MEM_PD_REG0, 0xff << 8, 0); + + /* Reset HDMITX APB & TX & PHY */ + reset_control_reset(meson_dw_hdmi->hdmitx_apb); + reset_control_reset(meson_dw_hdmi->hdmitx_ctrl); + reset_control_reset(meson_dw_hdmi->hdmitx_phy); + + /* Enable APB3 fail on error */ + writel_bits_relaxed(BIT(15), BIT(15), + meson_dw_hdmi->hdmitx + HDMITX_TOP_CTRL_REG); + writel_bits_relaxed(BIT(15), BIT(15), + meson_dw_hdmi->hdmitx + HDMITX_DWC_CTRL_REG); + + /* Bring out of reset */ + dw_hdmi_top_write(meson_dw_hdmi, HDMITX_TOP_SW_RESET, 0); + + msleep(20); + + dw_hdmi_top_write(meson_dw_hdmi, HDMITX_TOP_CLK_CNTL, 0xff); + + /* Enable HDMI-TX Interrupt */ + dw_hdmi_top_write(meson_dw_hdmi, HDMITX_TOP_INTR_STAT_CLR, + HDMITX_TOP_INTR_CORE); + + dw_hdmi_top_write(meson_dw_hdmi, HDMITX_TOP_INTR_MASKN, + HDMITX_TOP_INTR_CORE); + + /* Bridge / Connector */ + + dw_plat_data->mode_valid = dw_hdmi_mode_valid; + dw_plat_data->phy_ops = &meson_dw_hdmi_phy_ops; + dw_plat_data->phy_name = "meson_dw_hdmi_phy"; + dw_plat_data->phy_data = meson_dw_hdmi; + dw_plat_data->input_bus_format = MEDIA_BUS_FMT_YUV8_1X24; + dw_plat_data->input_bus_encoding = V4L2_YCBCR_ENC_709; + + ret = dw_hdmi_bind(pdev, encoder, &meson_dw_hdmi->dw_plat_data); + if (ret) + return ret; + + DRM_DEBUG_DRIVER("HDMI controller initialized\n"); + + return 0; +} + +static void meson_dw_hdmi_unbind(struct device *dev, struct device *master, + void *data) +{ + dw_hdmi_unbind(dev); +} + +static const struct component_ops meson_dw_hdmi_ops = { + .bind = meson_dw_hdmi_bind, + .unbind = meson_dw_hdmi_unbind, +}; + +static int meson_dw_hdmi_probe(struct platform_device *pdev) +{ + return component_add(&pdev->dev, &meson_dw_hdmi_ops); +} + +static int meson_dw_hdmi_remove(struct platform_device *pdev) +{ + component_del(&pdev->dev, &meson_dw_hdmi_ops); + + return 0; +} + +static const struct of_device_id meson_dw_hdmi_of_table[] = { + { .compatible = "amlogic,meson-gxbb-dw-hdmi" }, + { .compatible = "amlogic,meson-gxl-dw-hdmi" }, + { .compatible = "amlogic,meson-gxm-dw-hdmi" }, + { } +}; +MODULE_DEVICE_TABLE(of, meson_dw_hdmi_of_table); + +static struct platform_driver meson_dw_hdmi_platform_driver = { + .probe = meson_dw_hdmi_probe, + .remove = meson_dw_hdmi_remove, + .driver = { + .name = DRIVER_NAME, + .of_match_table = meson_dw_hdmi_of_table, + }, +}; +module_platform_driver(meson_dw_hdmi_platform_driver); + +MODULE_AUTHOR("Neil Armstrong <narmstrong@baylibre.com>"); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE("GPL"); diff --git a/drivers/gpu/drm/meson/meson_dw_hdmi.h b/drivers/gpu/drm/meson/meson_dw_hdmi.h new file mode 100644 index 0000000..0b81183 --- /dev/null +++ b/drivers/gpu/drm/meson/meson_dw_hdmi.h @@ -0,0 +1,146 @@ +/* + * Copyright (C) 2016 BayLibre, SAS + * Author: Neil Armstrong <narmstrong@baylibre.com> + * Copyright (C) 2015 Amlogic, Inc. All rights reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef __MESON_DW_HDMI_H +#define __MESON_DW_HDMI_H + +/* + * Bit 7 RW Reserved. Default 1. + * Bit 6 RW Reserved. Default 1. + * Bit 5 RW Reserved. Default 1. + * Bit 4 RW sw_reset_phyif: PHY interface. 1=Apply reset; 0=Release from reset. + * Default 1. + * Bit 3 RW sw_reset_intr: interrupt module. 1=Apply reset; + * 0=Release from reset. + * Default 1. + * Bit 2 RW sw_reset_mem: KSV/REVOC mem. 1=Apply reset; 0=Release from reset. + * Default 1. + * Bit 1 RW sw_reset_rnd: random number interface to HDCP. 1=Apply reset; + * 0=Release from reset. Default 1. + * Bit 0 RW sw_reset_core: connects to IP's ~irstz. 1=Apply reset; + * 0=Release from reset. Default 1. + */ +#define HDMITX_TOP_SW_RESET (0x000) + +/* + * Bit 12 RW i2s_ws_inv:1=Invert i2s_ws; 0=No invert. Default 0. + * Bit 11 RW i2s_clk_inv: 1=Invert i2s_clk; 0=No invert. Default 0. + * Bit 10 RW spdif_clk_inv: 1=Invert spdif_clk; 0=No invert. Default 0. + * Bit 9 RW tmds_clk_inv: 1=Invert tmds_clk; 0=No invert. Default 0. + * Bit 8 RW pixel_clk_inv: 1=Invert pixel_clk; 0=No invert. Default 0. + * Bit 4 RW cec_clk_en: 1=enable cec_clk; 0=disable. Default 0. + * Bit 3 RW i2s_clk_en: 1=enable i2s_clk; 0=disable. Default 0. + * Bit 2 RW spdif_clk_en: 1=enable spdif_clk; 0=disable. Default 0. + * Bit 1 RW tmds_clk_en: 1=enable tmds_clk; 0=disable. Default 0. + * Bit 0 RW pixel_clk_en: 1=enable pixel_clk; 0=disable. Default 0. + */ +#define HDMITX_TOP_CLK_CNTL (0x001) + +/* + * Bit 11: 0 RW hpd_valid_width: filter out width <= M*1024. Default 0. + * Bit 15:12 RW hpd_glitch_width: filter out glitch <= N. Default 0. + */ +#define HDMITX_TOP_HPD_FILTER (0x002) + +/* + * intr_maskn: MASK_N, one bit per interrupt source. + * 1=Enable interrupt source; 0=Disable interrupt source. Default 0. + * [ 4] hdcp22_rndnum_err + * [ 3] nonce_rfrsh_rise + * [ 2] hpd_fall_intr + * [ 1] hpd_rise_intr + * [ 0] core_intr + */ +#define HDMITX_TOP_INTR_MASKN (0x003) + +/* + * Bit 30: 0 RW intr_stat: For each bit, write 1 to manually set the interrupt + * bit, read back the interrupt status. + * Bit 31 R IP interrupt status + * Bit 2 RW hpd_fall + * Bit 1 RW hpd_rise + * Bit 0 RW IP interrupt + */ +#define HDMITX_TOP_INTR_STAT (0x004) + +/* + * [4] hdcp22_rndnum_err + * [3] nonce_rfrsh_rise + * [2] hpd_fall + * [1] hpd_rise + * [0] core_intr_rise + */ +#define HDMITX_TOP_INTR_STAT_CLR (0x005) + +#define HDMITX_TOP_INTR_CORE BIT(0) +#define HDMITX_TOP_INTR_HPD_RISE BIT(1) +#define HDMITX_TOP_INTR_HPD_FALL BIT(2) + +/* Bit 14:12 RW tmds_sel: 3'b000=Output zero; 3'b001=Output normal TMDS data; + * 3'b010=Output PRBS data; 3'b100=Output shift pattern. Default 0. + * Bit 11: 9 RW shift_pttn_repeat: 0=New pattern every clk cycle; 1=New pattern + * every 2 clk cycles; ...; 7=New pattern every 8 clk cycles. Default 0. + * Bit 8 RW shift_pttn_en: 1= Enable shift pattern generator; 0=Disable. + * Default 0. + * Bit 4: 3 RW prbs_pttn_mode: 0=PRBS11; 1=PRBS15; 2=PRBS7; 3=PRBS31. Default 0. + * Bit 2: 1 RW prbs_pttn_width: 0=idle; 1=output 8-bit pattern; + * 2=Output 1-bit pattern; 3=output 10-bit pattern. Default 0. + * Bit 0 RW prbs_pttn_en: 1=Enable PRBS generator; 0=Disable. Default 0. + */ +#define HDMITX_TOP_BIST_CNTL (0x006) + +/* Bit 29:20 RW shift_pttn_data[59:50]. Default 0. */ +/* Bit 19:10 RW shift_pttn_data[69:60]. Default 0. */ +/* Bit 9: 0 RW shift_pttn_data[79:70]. Default 0. */ +#define HDMITX_TOP_SHIFT_PTTN_012 (0x007) + +/* Bit 29:20 RW shift_pttn_data[29:20]. Default 0. */ +/* Bit 19:10 RW shift_pttn_data[39:30]. Default 0. */ +/* Bit 9: 0 RW shift_pttn_data[49:40]. Default 0. */ +#define HDMITX_TOP_SHIFT_PTTN_345 (0x008) + +/* Bit 19:10 RW shift_pttn_data[ 9: 0]. Default 0. */ +/* Bit 9: 0 RW shift_pttn_data[19:10]. Default 0. */ +#define HDMITX_TOP_SHIFT_PTTN_67 (0x009) + +/* Bit 25:16 RW tmds_clk_pttn[19:10]. Default 0. */ +/* Bit 9: 0 RW tmds_clk_pttn[ 9: 0]. Default 0. */ +#define HDMITX_TOP_TMDS_CLK_PTTN_01 (0x00A) + +/* Bit 25:16 RW tmds_clk_pttn[39:30]. Default 0. */ +/* Bit 9: 0 RW tmds_clk_pttn[29:20]. Default 0. */ +#define HDMITX_TOP_TMDS_CLK_PTTN_23 (0x00B) + +/* Bit 1 RW shift_tmds_clk_pttn:1=Enable shifting clk pattern, + * used when TMDS CLK rate = TMDS character rate /4. Default 0. + * Bit 0 R Reserved. Default 0. + * [ 1] shift_tmds_clk_pttn + * [ 0] load_tmds_clk_pttn + */ +#define HDMITX_TOP_TMDS_CLK_PTTN_CNTL (0x00C) + +/* Bit 0 RW revocmem_wr_fail: Read back 1 to indicate Host write REVOC MEM + * failure, write 1 to clear the failure flag. Default 0. + */ +#define HDMITX_TOP_REVOCMEM_STAT (0x00D) + +/* Bit 0 R filtered HPD status. */ +#define HDMITX_TOP_STAT0 (0x00E) + +#endif /* __MESON_DW_HDMI_H */ diff --git a/drivers/gpu/drm/meson/meson_registers.h b/drivers/gpu/drm/meson/meson_registers.h index 6adf9c1..2847381 100644 --- a/drivers/gpu/drm/meson/meson_registers.h +++ b/drivers/gpu/drm/meson/meson_registers.h @@ -1319,6 +1319,7 @@ #define VPU_MISC_CTRL 0x2740 #define VPU_ISP_GCLK_CTRL0 0x2741 #define VPU_ISP_GCLK_CTRL1 0x2742 +#define VPU_HDMI_FMT_CTRL 0x2743 #define VPU_VDIN_ASYNC_HOLD_CTRL 0x2743 #define VPU_VDISP_ASYNC_HOLD_CTRL 0x2744 #define VPU_VPUARB2_ASYNC_HOLD_CTRL 0x2745 diff --git a/drivers/gpu/drm/meson/meson_vclk.c b/drivers/gpu/drm/meson/meson_vclk.c index 252cfd4..4767704 100644 --- a/drivers/gpu/drm/meson/meson_vclk.c +++ b/drivers/gpu/drm/meson/meson_vclk.c @@ -23,13 +23,38 @@ #include "meson_drv.h" #include "meson_vclk.h" -/* +/** + * DOC: Video Clocks + * * VCLK is the "Pixel Clock" frequency generator from a dedicated PLL. * We handle the following encodings : + * * - CVBS 27MHz generator via the VCLK2 to the VENCI and VDAC blocks + * - HDMI Pixel Clocks generation * * What is missing : - * - HDMI Pixel Clocks generation + * + * - Genenate Pixel clocks for 2K/4K 10bit formats + * + * Clock generator scheme : + * + * .. code:: + * + * __________ _________ _____ + * | | | | | |--ENCI + * | HDMI PLL |-| PLL_DIV |--- VCLK--| |--ENCL + * |__________| |_________| \ | MUX |--ENCP + * --VCLK2-| |--VDAC + * |_____|--HDMI-TX + * + * Final clocks can take input for either VCLK or VCLK2, but + * VCLK is the preferred path for HDMI clocking and VCLK2 is the + * preferred path for CVBS VDAC clocking. + * + * VCLK and VCLK2 have fixed divided clocks paths for /1, /2, /4, /6 or /12. + * + * The PLL_DIV can achieve an additional fractional dividing like + * 1.5, 3.5, 3.75... to generate special 2K and 4K 10bit clocks. */ /* HHI Registers */ @@ -50,11 +75,34 @@ #define VCLK2_SOFT_RESET BIT(15) #define VCLK2_DIV1_EN BIT(0) #define HHI_VID_CLK_DIV 0x164 /* 0x59 offset in data sheet */ +#define VCLK_DIV_MASK 0xff +#define VCLK_DIV_EN BIT(16) +#define VCLK_DIV_RESET BIT(17) +#define CTS_ENCP_SEL_MASK (0xf << 24) +#define CTS_ENCP_SEL_SHIFT 24 #define CTS_ENCI_SEL_MASK (0xf << 28) #define CTS_ENCI_SEL_SHIFT 28 +#define HHI_VID_CLK_CNTL 0x17c /* 0x5f offset in data sheet */ +#define VCLK_EN BIT(19) +#define VCLK_SEL_MASK (0x7 << 16) +#define VCLK_SEL_SHIFT 16 +#define VCLK_SOFT_RESET BIT(15) +#define VCLK_DIV1_EN BIT(0) +#define VCLK_DIV2_EN BIT(1) +#define VCLK_DIV4_EN BIT(2) +#define VCLK_DIV6_EN BIT(3) +#define VCLK_DIV12_EN BIT(4) #define HHI_VID_CLK_CNTL2 0x194 /* 0x65 offset in data sheet */ #define CTS_ENCI_EN BIT(0) +#define CTS_ENCP_EN BIT(2) #define CTS_VDAC_EN BIT(4) +#define HDMI_TX_PIXEL_EN BIT(5) +#define HHI_HDMI_CLK_CNTL 0x1cc /* 0x73 offset in data sheet */ +#define HDMI_TX_PIXEL_SEL_MASK (0xf << 16) +#define HDMI_TX_PIXEL_SEL_SHIFT 16 +#define CTS_HDMI_SYS_SEL_MASK (0x7 << 9) +#define CTS_HDMI_SYS_DIV_MASK (0x7f) +#define CTS_HDMI_SYS_EN BIT(8) #define HHI_VDAC_CNTL0 0x2F4 /* 0xbd offset in data sheet */ #define HHI_VDAC_CNTL1 0x2F8 /* 0xbe offset in data sheet */ @@ -69,6 +117,126 @@ #define HDMI_PLL_RESET BIT(28) #define HDMI_PLL_LOCK BIT(31) +/* VID PLL Dividers */ +enum { + VID_PLL_DIV_1 = 0, + VID_PLL_DIV_2, + VID_PLL_DIV_2p5, + VID_PLL_DIV_3, + VID_PLL_DIV_3p5, + VID_PLL_DIV_3p75, + VID_PLL_DIV_4, + VID_PLL_DIV_5, + VID_PLL_DIV_6, + VID_PLL_DIV_6p25, + VID_PLL_DIV_7, + VID_PLL_DIV_7p5, + VID_PLL_DIV_12, + VID_PLL_DIV_14, + VID_PLL_DIV_15, +}; + +void meson_vid_pll_set(struct meson_drm *priv, unsigned int div) +{ + unsigned int shift_val = 0; + unsigned int shift_sel = 0; + + /* Disable vid_pll output clock */ + regmap_update_bits(priv->hhi, HHI_VID_PLL_CLK_DIV, VID_PLL_EN, 0); + regmap_update_bits(priv->hhi, HHI_VID_PLL_CLK_DIV, VID_PLL_PRESET, 0); + + switch (div) { + case VID_PLL_DIV_2: + shift_val = 0x0aaa; + shift_sel = 0; + break; + case VID_PLL_DIV_2p5: + shift_val = 0x5294; + shift_sel = 2; + break; + case VID_PLL_DIV_3: + shift_val = 0x0db6; + shift_sel = 0; + break; + case VID_PLL_DIV_3p5: + shift_val = 0x36cc; + shift_sel = 1; + break; + case VID_PLL_DIV_3p75: + shift_val = 0x6666; + shift_sel = 2; + break; + case VID_PLL_DIV_4: + shift_val = 0x0ccc; + shift_sel = 0; + break; + case VID_PLL_DIV_5: + shift_val = 0x739c; + shift_sel = 2; + break; + case VID_PLL_DIV_6: + shift_val = 0x0e38; + shift_sel = 0; + break; + case VID_PLL_DIV_6p25: + shift_val = 0x0000; + shift_sel = 3; + break; + case VID_PLL_DIV_7: + shift_val = 0x3c78; + shift_sel = 1; + break; + case VID_PLL_DIV_7p5: + shift_val = 0x78f0; + shift_sel = 2; + break; + case VID_PLL_DIV_12: + shift_val = 0x0fc0; + shift_sel = 0; + break; + case VID_PLL_DIV_14: + shift_val = 0x3f80; + shift_sel = 1; + break; + case VID_PLL_DIV_15: + shift_val = 0x7f80; + shift_sel = 2; + break; + } + + if (div == VID_PLL_DIV_1) + /* Enable vid_pll bypass to HDMI pll */ + regmap_update_bits(priv->hhi, HHI_VID_PLL_CLK_DIV, + VID_PLL_BYPASS, VID_PLL_BYPASS); + else { + /* Disable Bypass */ + regmap_update_bits(priv->hhi, HHI_VID_PLL_CLK_DIV, + VID_PLL_BYPASS, 0); + /* Clear sel */ + regmap_update_bits(priv->hhi, HHI_VID_PLL_CLK_DIV, + 3 << 16, 0); + regmap_update_bits(priv->hhi, HHI_VID_PLL_CLK_DIV, + VID_PLL_PRESET, 0); + regmap_update_bits(priv->hhi, HHI_VID_PLL_CLK_DIV, + 0x7fff, 0); + + /* Setup sel and val */ + regmap_update_bits(priv->hhi, HHI_VID_PLL_CLK_DIV, + 3 << 16, shift_sel << 16); + regmap_update_bits(priv->hhi, HHI_VID_PLL_CLK_DIV, + VID_PLL_PRESET, VID_PLL_PRESET); + regmap_update_bits(priv->hhi, HHI_VID_PLL_CLK_DIV, + 0x7fff, shift_val); + + regmap_update_bits(priv->hhi, HHI_VID_PLL_CLK_DIV, + VID_PLL_PRESET, 0); + } + + /* Enable the vid_pll output clock */ + regmap_update_bits(priv->hhi, HHI_VID_PLL_CLK_DIV, + VID_PLL_EN, VID_PLL_EN); +} + /* * Setup VCLK2 for 27MHz, and enable clocks for ENCI and VDAC * @@ -110,15 +278,8 @@ static void meson_venci_cvbs_clock_config(struct meson_drm *priv) /* Disable VCLK2 */ regmap_update_bits(priv->hhi, HHI_VIID_CLK_CNTL, VCLK2_EN, 0); - /* Disable vid_pll output clock */ - regmap_update_bits(priv->hhi, HHI_VID_PLL_CLK_DIV, VID_PLL_EN, 0); - regmap_update_bits(priv->hhi, HHI_VID_PLL_CLK_DIV, VID_PLL_PRESET, 0); - /* Enable vid_pll bypass to HDMI pll */ - regmap_update_bits(priv->hhi, HHI_VID_PLL_CLK_DIV, - VID_PLL_BYPASS, VID_PLL_BYPASS); - /* Enable the vid_pll output clock */ - regmap_update_bits(priv->hhi, HHI_VID_PLL_CLK_DIV, - VID_PLL_EN, VID_PLL_EN); + /* Setup vid_pll to /1 */ + meson_vid_pll_set(priv, VID_PLL_DIV_1); /* Setup the VCLK2 divider value to achieve 27MHz */ regmap_update_bits(priv->hhi, HHI_VIID_CLK_DIV, @@ -159,9 +320,454 @@ static void meson_venci_cvbs_clock_config(struct meson_drm *priv) CTS_VDAC_EN, CTS_VDAC_EN); } + +/* PLL O1 O2 O3 VP DV EN TX */ +/* 4320 /4 /4 /1 /5 /1 => /2 /2 */ +#define MESON_VCLK_HDMI_ENCI_54000 1 +/* 4320 /4 /4 /1 /5 /1 => /1 /2 */ +#define MESON_VCLK_HDMI_DDR_54000 2 +/* 2970 /4 /1 /1 /5 /1 => /1 /2 */ +#define MESON_VCLK_HDMI_DDR_148500 3 +/* 2970 /2 /2 /2 /5 /1 => /1 /1 */ +#define MESON_VCLK_HDMI_74250 4 +/* 2970 /1 /2 /2 /5 /1 => /1 /1 */ +#define MESON_VCLK_HDMI_148500 5 +/* 2970 /1 /1 /1 /5 /2 => /1 /1 */ +#define MESON_VCLK_HDMI_297000 6 +/* 5940 /1 /1 /2 /5 /1 => /1 /1 */ +#define MESON_VCLK_HDMI_594000 7 + +struct meson_vclk_params { + unsigned int pll_base_freq; + unsigned int pll_od1; + unsigned int pll_od2; + unsigned int pll_od3; + unsigned int vid_pll_div; + unsigned int vclk_div; +} params[] = { + [MESON_VCLK_HDMI_ENCI_54000] = { + .pll_base_freq = 4320000, + .pll_od1 = 4, + .pll_od2 = 4, + .pll_od3 = 1, + .vid_pll_div = VID_PLL_DIV_5, + .vclk_div = 1, + }, + [MESON_VCLK_HDMI_DDR_54000] = { + .pll_base_freq = 4320000, + .pll_od1 = 4, + .pll_od2 = 4, + .pll_od3 = 1, + .vid_pll_div = VID_PLL_DIV_5, + .vclk_div = 1, + }, + [MESON_VCLK_HDMI_DDR_148500] = { + .pll_base_freq = 2970000, + .pll_od1 = 4, + .pll_od2 = 1, + .pll_od3 = 1, + .vid_pll_div = VID_PLL_DIV_5, + .vclk_div = 1, + }, + [MESON_VCLK_HDMI_74250] = { + .pll_base_freq = 2970000, + .pll_od1 = 2, + .pll_od2 = 2, + .pll_od3 = 2, + .vid_pll_div = VID_PLL_DIV_5, + .vclk_div = 1, + }, + [MESON_VCLK_HDMI_148500] = { + .pll_base_freq = 2970000, + .pll_od1 = 1, + .pll_od2 = 2, + .pll_od3 = 2, + .vid_pll_div = VID_PLL_DIV_5, + .vclk_div = 1, + }, + [MESON_VCLK_HDMI_297000] = { + .pll_base_freq = 2970000, + .pll_od1 = 1, + .pll_od2 = 1, + .pll_od3 = 1, + .vid_pll_div = VID_PLL_DIV_5, + .vclk_div = 2, + }, + [MESON_VCLK_HDMI_594000] = { + .pll_base_freq = 5940000, + .pll_od1 = 1, + .pll_od2 = 1, + .pll_od3 = 2, + .vid_pll_div = VID_PLL_DIV_5, + .vclk_div = 1, + }, +}; + +static inline unsigned int pll_od_to_reg(unsigned int od) +{ + switch (od) { + case 1: + return 0; + case 2: + return 1; + case 4: + return 2; + case 8: + return 3; + } + + /* Invalid */ + return 0; +} + +void meson_hdmi_pll_set(struct meson_drm *priv, + unsigned int base, + unsigned int od1, + unsigned int od2, + unsigned int od3) +{ + unsigned int val; + + if (meson_vpu_is_compatible(priv, "amlogic,meson-gxbb-vpu")) { + switch (base) { + case 2970000: + regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL, 0x5800023d); + regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL2, 0x00000000); + regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL3, 0x0d5c5091); + regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL4, 0x801da72c); + regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL5, 0x71486980); + regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL6, 0x00000e55); + + /* Enable and unreset */ + regmap_update_bits(priv->hhi, HHI_HDMI_PLL_CNTL, + 0x7 << 28, 0x4 << 28); + + /* Poll for lock bit */ + regmap_read_poll_timeout(priv->hhi, HHI_HDMI_PLL_CNTL, + val, (val & HDMI_PLL_LOCK), 10, 0); + + /* div_frac */ + regmap_update_bits(priv->hhi, HHI_HDMI_PLL_CNTL2, + 0xFFFF, 0x4e00); + break; + + case 4320000: + regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL, 0x5800025a); + regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL2, 0x00000000); + regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL3, 0x0d5c5091); + regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL4, 0x801da72c); + regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL5, 0x71486980); + regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL6, 0x00000e55); + + /* unreset */ + regmap_update_bits(priv->hhi, HHI_HDMI_PLL_CNTL, + BIT(28), 0); + + /* Poll for lock bit */ + regmap_read_poll_timeout(priv->hhi, HHI_HDMI_PLL_CNTL, + val, (val & HDMI_PLL_LOCK), 10, 0); + break; + + case 5940000: + regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL, 0x5800027b); + regmap_update_bits(priv->hhi, HHI_HDMI_PLL_CNTL2, + 0xFFFF, 0x4c00); + regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL3, 0x135c5091); + regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL4, 0x801da72c); + regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL5, 0x71486980); + regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL6, 0x00000e55); + + /* unreset */ + regmap_update_bits(priv->hhi, HHI_HDMI_PLL_CNTL, + BIT(28), 0); + + /* Poll for lock bit */ + regmap_read_poll_timeout(priv->hhi, HHI_HDMI_PLL_CNTL, + val, (val & HDMI_PLL_LOCK), 10, 0); + break; + }; + } else if (meson_vpu_is_compatible(priv, "amlogic,meson-gxm-vpu") || + meson_vpu_is_compatible(priv, "amlogic,meson-gxl-vpu")) { + switch (base) { + case 2970000: + regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL, 0x4000027b); + regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL2, 0x800cb300); + regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL3, 0x860f30c4); + regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL4, 0x0c8e0000); + regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL5, 0x001fa729); + regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL6, 0x01a31500); + break; + + case 4320000: + regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL, 0x400002b4); + regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL2, 0x800cb000); + regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL3, 0x860f30c4); + regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL4, 0x0c8e0000); + regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL5, 0x001fa729); + regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL6, 0x01a31500); + break; + + case 5940000: + regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL, 0x400002f7); + regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL2, 0x800cb200); + regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL3, 0x860f30c4); + regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL4, 0x0c8e0000); + regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL5, 0x001fa729); + regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL6, 0x01a31500); + break; + + }; + + /* Reset PLL */ + regmap_update_bits(priv->hhi, HHI_HDMI_PLL_CNTL, + HDMI_PLL_RESET, HDMI_PLL_RESET); + regmap_update_bits(priv->hhi, HHI_HDMI_PLL_CNTL, + HDMI_PLL_RESET, 0); + + /* Poll for lock bit */ + regmap_read_poll_timeout(priv->hhi, HHI_HDMI_PLL_CNTL, val, + (val & HDMI_PLL_LOCK), 10, 0); + }; + + if (meson_vpu_is_compatible(priv, "amlogic,meson-gxbb-vpu")) + regmap_update_bits(priv->hhi, HHI_HDMI_PLL_CNTL2, + 3 << 16, pll_od_to_reg(od1) << 16); + else if (meson_vpu_is_compatible(priv, "amlogic,meson-gxm-vpu") || + meson_vpu_is_compatible(priv, "amlogic,meson-gxl-vpu")) + regmap_update_bits(priv->hhi, HHI_HDMI_PLL_CNTL3, + 3 << 21, pll_od_to_reg(od1) << 21); + + if (meson_vpu_is_compatible(priv, "amlogic,meson-gxbb-vpu")) + regmap_update_bits(priv->hhi, HHI_HDMI_PLL_CNTL2, + 3 << 22, pll_od_to_reg(od2) << 22); + else if (meson_vpu_is_compatible(priv, "amlogic,meson-gxm-vpu") || + meson_vpu_is_compatible(priv, "amlogic,meson-gxl-vpu")) + regmap_update_bits(priv->hhi, HHI_HDMI_PLL_CNTL3, + 3 << 23, pll_od_to_reg(od2) << 23); + + if (meson_vpu_is_compatible(priv, "amlogic,meson-gxbb-vpu")) + regmap_update_bits(priv->hhi, HHI_HDMI_PLL_CNTL2, + 3 << 18, pll_od_to_reg(od3) << 18); + else if (meson_vpu_is_compatible(priv, "amlogic,meson-gxm-vpu") || + meson_vpu_is_compatible(priv, "amlogic,meson-gxl-vpu")) + regmap_update_bits(priv->hhi, HHI_HDMI_PLL_CNTL3, + 3 << 19, pll_od_to_reg(od3) << 19); +} + void meson_vclk_setup(struct meson_drm *priv, unsigned int target, - unsigned int freq) + unsigned int vclk_freq, unsigned int venc_freq, + unsigned int dac_freq, bool hdmi_use_enci) { - if (target == MESON_VCLK_TARGET_CVBS && freq == MESON_VCLK_CVBS) + unsigned int freq; + unsigned int hdmi_tx_div; + unsigned int venc_div; + + if (target == MESON_VCLK_TARGET_CVBS) { meson_venci_cvbs_clock_config(priv); + return; + } + + hdmi_tx_div = vclk_freq / dac_freq; + + if (hdmi_tx_div == 0) { + pr_err("Fatal Error, invalid HDMI-TX freq %d\n", + dac_freq); + return; + } + + venc_div = vclk_freq / venc_freq; + + if (venc_div == 0) { + pr_err("Fatal Error, invalid HDMI venc freq %d\n", + venc_freq); + return; + } + + switch (vclk_freq) { + case 54000: + if (hdmi_use_enci) + freq = MESON_VCLK_HDMI_ENCI_54000; + else + freq = MESON_VCLK_HDMI_DDR_54000; + break; + case 74250: + freq = MESON_VCLK_HDMI_74250; + break; + case 148500: + if (dac_freq != 148500) + freq = MESON_VCLK_HDMI_DDR_148500; + else + freq = MESON_VCLK_HDMI_148500; + break; + case 297000: + freq = MESON_VCLK_HDMI_297000; + break; + case 594000: + freq = MESON_VCLK_HDMI_594000; + break; + default: + pr_err("Fatal Error, invalid HDMI vclk freq %d\n", + vclk_freq); + return; + } + + /* Set HDMI-TX sys clock */ + regmap_update_bits(priv->hhi, HHI_HDMI_CLK_CNTL, + CTS_HDMI_SYS_SEL_MASK, 0); + regmap_update_bits(priv->hhi, HHI_HDMI_CLK_CNTL, + CTS_HDMI_SYS_DIV_MASK, 0); + regmap_update_bits(priv->hhi, HHI_HDMI_CLK_CNTL, + CTS_HDMI_SYS_EN, CTS_HDMI_SYS_EN); + + /* Set HDMI PLL rate */ + meson_hdmi_pll_set(priv, params[freq].pll_base_freq, + params[freq].pll_od1, + params[freq].pll_od2, + params[freq].pll_od3); + + /* Setup vid_pll divider */ + meson_vid_pll_set(priv, params[freq].vid_pll_div); + + /* Set VCLK div */ + regmap_update_bits(priv->hhi, HHI_VID_CLK_CNTL, + VCLK_SEL_MASK, 0); + regmap_update_bits(priv->hhi, HHI_VID_CLK_DIV, + VCLK_DIV_MASK, params[freq].vclk_div - 1); + + /* Set HDMI-TX source */ + switch (hdmi_tx_div) { + case 1: + /* enable vclk_div1 gate */ + regmap_update_bits(priv->hhi, HHI_VID_CLK_CNTL, + VCLK_DIV1_EN, VCLK_DIV1_EN); + + /* select vclk_div1 for HDMI-TX */ + regmap_update_bits(priv->hhi, HHI_HDMI_CLK_CNTL, + HDMI_TX_PIXEL_SEL_MASK, 0); + break; + case 2: + /* enable vclk_div2 gate */ + regmap_update_bits(priv->hhi, HHI_VID_CLK_CNTL, + VCLK_DIV2_EN, VCLK_DIV2_EN); + + /* select vclk_div2 for HDMI-TX */ + regmap_update_bits(priv->hhi, HHI_HDMI_CLK_CNTL, + HDMI_TX_PIXEL_SEL_MASK, 1 << HDMI_TX_PIXEL_SEL_SHIFT); + break; + case 4: + /* enable vclk_div4 gate */ + regmap_update_bits(priv->hhi, HHI_VID_CLK_CNTL, + VCLK_DIV4_EN, VCLK_DIV4_EN); + + /* select vclk_div4 for HDMI-TX */ + regmap_update_bits(priv->hhi, HHI_HDMI_CLK_CNTL, + HDMI_TX_PIXEL_SEL_MASK, 2 << HDMI_TX_PIXEL_SEL_SHIFT); + break; + case 6: + /* enable vclk_div6 gate */ + regmap_update_bits(priv->hhi, HHI_VID_CLK_CNTL, + VCLK_DIV6_EN, VCLK_DIV6_EN); + + /* select vclk_div6 for HDMI-TX */ + regmap_update_bits(priv->hhi, HHI_HDMI_CLK_CNTL, + HDMI_TX_PIXEL_SEL_MASK, 3 << HDMI_TX_PIXEL_SEL_SHIFT); + break; + case 12: + /* enable vclk_div12 gate */ + regmap_update_bits(priv->hhi, HHI_VID_CLK_CNTL, + VCLK_DIV12_EN, VCLK_DIV12_EN); + + /* select vclk_div12 for HDMI-TX */ + regmap_update_bits(priv->hhi, HHI_HDMI_CLK_CNTL, + HDMI_TX_PIXEL_SEL_MASK, 4 << HDMI_TX_PIXEL_SEL_SHIFT); + break; + } + regmap_update_bits(priv->hhi, HHI_VID_CLK_CNTL2, + HDMI_TX_PIXEL_EN, HDMI_TX_PIXEL_EN); + + /* Set ENCI/ENCP Source */ + switch (venc_div) { + case 1: + /* enable vclk_div1 gate */ + regmap_update_bits(priv->hhi, HHI_VID_CLK_CNTL, + VCLK_DIV1_EN, VCLK_DIV1_EN); + + if (hdmi_use_enci) + /* select vclk_div1 for enci */ + regmap_update_bits(priv->hhi, HHI_VID_CLK_DIV, + CTS_ENCI_SEL_MASK, 0); + else + /* select vclk_div1 for encp */ + regmap_update_bits(priv->hhi, HHI_VID_CLK_DIV, + CTS_ENCP_SEL_MASK, 0); + break; + case 2: + /* enable vclk_div2 gate */ + regmap_update_bits(priv->hhi, HHI_VID_CLK_CNTL, + VCLK_DIV2_EN, VCLK_DIV2_EN); + + if (hdmi_use_enci) + /* select vclk_div2 for enci */ + regmap_update_bits(priv->hhi, HHI_VID_CLK_DIV, + CTS_ENCI_SEL_MASK, 1 << CTS_ENCI_SEL_SHIFT); + else + /* select vclk_div2 for encp */ + regmap_update_bits(priv->hhi, HHI_VID_CLK_DIV, + CTS_ENCP_SEL_MASK, 1 << CTS_ENCP_SEL_SHIFT); + break; + case 4: + /* enable vclk_div4 gate */ + regmap_update_bits(priv->hhi, HHI_VID_CLK_CNTL, + VCLK_DIV4_EN, VCLK_DIV4_EN); + + if (hdmi_use_enci) + /* select vclk_div4 for enci */ + regmap_update_bits(priv->hhi, HHI_VID_CLK_DIV, + CTS_ENCI_SEL_MASK, 2 << CTS_ENCI_SEL_SHIFT); + else + /* select vclk_div4 for encp */ + regmap_update_bits(priv->hhi, HHI_VID_CLK_DIV, + CTS_ENCP_SEL_MASK, 2 << CTS_ENCP_SEL_SHIFT); + break; + case 6: + /* enable vclk_div6 gate */ + regmap_update_bits(priv->hhi, HHI_VID_CLK_CNTL, + VCLK_DIV6_EN, VCLK_DIV6_EN); + + if (hdmi_use_enci) + /* select vclk_div6 for enci */ + regmap_update_bits(priv->hhi, HHI_VID_CLK_DIV, + CTS_ENCI_SEL_MASK, 3 << CTS_ENCI_SEL_SHIFT); + else + /* select vclk_div6 for encp */ + regmap_update_bits(priv->hhi, HHI_VID_CLK_DIV, + CTS_ENCP_SEL_MASK, 3 << CTS_ENCP_SEL_SHIFT); + break; + case 12: + /* enable vclk_div12 gate */ + regmap_update_bits(priv->hhi, HHI_VID_CLK_CNTL, + VCLK_DIV12_EN, VCLK_DIV12_EN); + + if (hdmi_use_enci) + /* select vclk_div12 for enci */ + regmap_update_bits(priv->hhi, HHI_VID_CLK_DIV, + CTS_ENCI_SEL_MASK, 4 << CTS_ENCI_SEL_SHIFT); + else + /* select vclk_div12 for encp */ + regmap_update_bits(priv->hhi, HHI_VID_CLK_DIV, + CTS_ENCP_SEL_MASK, 4 << CTS_ENCP_SEL_SHIFT); + break; + } + + if (hdmi_use_enci) + /* Enable ENCI clock gate */ + regmap_update_bits(priv->hhi, HHI_VID_CLK_CNTL2, + CTS_ENCI_EN, CTS_ENCI_EN); + else + /* Enable ENCP clock gate */ + regmap_update_bits(priv->hhi, HHI_VID_CLK_CNTL2, + CTS_ENCP_EN, CTS_ENCP_EN); + + regmap_update_bits(priv->hhi, HHI_VID_CLK_CNTL, VCLK_EN, VCLK_EN); } +EXPORT_SYMBOL_GPL(meson_vclk_setup); diff --git a/drivers/gpu/drm/meson/meson_vclk.h b/drivers/gpu/drm/meson/meson_vclk.h index ec62735..0401b52 100644 --- a/drivers/gpu/drm/meson/meson_vclk.h +++ b/drivers/gpu/drm/meson/meson_vclk.h @@ -23,12 +23,14 @@ enum { MESON_VCLK_TARGET_CVBS = 0, + MESON_VCLK_TARGET_HDMI = 1, }; /* 27MHz is the CVBS Pixel Clock */ -#define MESON_VCLK_CVBS 27000 +#define MESON_VCLK_CVBS 27000 void meson_vclk_setup(struct meson_drm *priv, unsigned int target, - unsigned int freq); + unsigned int vclk_freq, unsigned int venc_freq, + unsigned int dac_freq, bool hdmi_use_enci); #endif /* __MESON_VCLK_H */ diff --git a/drivers/gpu/drm/meson/meson_venc.c b/drivers/gpu/drm/meson/meson_venc.c index f7c87017..9509017 100644 --- a/drivers/gpu/drm/meson/meson_venc.c +++ b/drivers/gpu/drm/meson/meson_venc.c @@ -26,16 +26,48 @@ #include "meson_vclk.h" #include "meson_registers.h" -/* +/** + * DOC: Video Encoder + * * VENC Handle the pixels encoding to the output formats. * We handle the following encodings : - * - CVBS Encoding via the ENCI encoder and VDAC digital to analog converter * - * What is missing : + * - CVBS Encoding via the ENCI encoder and VDAC digital to analog converter * - TMDS/HDMI Encoding via ENCI_DIV and ENCP * - Setup of more clock rates for HDMI modes + * + * What is missing : + * * - LCD Panel encoding via ENCL * - TV Panel encoding via ENCT + * + * VENC paths : + * + * .. code:: + * + * _____ _____ ____________________ + * vd1---| |-| | | VENC /---------|----VDAC + * vd2---| VIU |-| VPP |-|-----ENCI/-ENCI_DVI-|-| + * osd1--| |-| | | \ | X--HDMI-TX + * osd2--|_____|-|_____| | |\-ENCP--ENCP_DVI-|-| + * | | | + * | \--ENCL-----------|----LVDS + * |____________________| + * + * The ENCI is designed for PAl or NTSC encoding and can go through the VDAC + * directly for CVBS encoding or through the ENCI_DVI encoder for HDMI. + * The ENCP is designed for Progressive encoding but can also generate + * 1080i interlaced pixels, and was initialy desined to encode pixels for + * VDAC to output RGB ou YUV analog outputs. + * It's output is only used through the ENCP_DVI encoder for HDMI. + * The ENCL LVDS encoder is not implemented. + * + * The ENCI and ENCP encoders needs specially defined parameters for each + * supported mode and thus cannot be determined from standard video timings. + * + * The ENCI end ENCP DVI encoders are more generic and can generate any timings + * from the pixel data generated by ENCI or ENCP, so can use the standard video + * timings are source for HW parameters. */ /* HHI Registers */ @@ -91,6 +123,1219 @@ struct meson_cvbs_enci_mode meson_cvbs_enci_ntsc = { .analog_sync_adj = 0x9c00, }; +union meson_hdmi_venc_mode { + struct { + unsigned int mode_tag; + unsigned int hso_begin; + unsigned int hso_end; + unsigned int vso_even; + unsigned int vso_odd; + unsigned int macv_max_amp; + unsigned int video_prog_mode; + unsigned int video_mode; + unsigned int sch_adjust; + unsigned int yc_delay; + unsigned int pixel_start; + unsigned int pixel_end; + unsigned int top_field_line_start; + unsigned int top_field_line_end; + unsigned int bottom_field_line_start; + unsigned int bottom_field_line_end; + } enci; + struct { + unsigned int dvi_settings; + unsigned int video_mode; + unsigned int video_mode_adv; + unsigned int video_prog_mode; + bool video_prog_mode_present; + unsigned int video_sync_mode; + bool video_sync_mode_present; + unsigned int video_yc_dly; + bool video_yc_dly_present; + unsigned int video_rgb_ctrl; + bool video_rgb_ctrl_present; + unsigned int video_filt_ctrl; + bool video_filt_ctrl_present; + unsigned int video_ofld_voav_ofst; + bool video_ofld_voav_ofst_present; + unsigned int yfp1_htime; + unsigned int yfp2_htime; + unsigned int max_pxcnt; + unsigned int hspuls_begin; + unsigned int hspuls_end; + unsigned int hspuls_switch; + unsigned int vspuls_begin; + unsigned int vspuls_end; + unsigned int vspuls_bline; + unsigned int vspuls_eline; + unsigned int eqpuls_begin; + bool eqpuls_begin_present; + unsigned int eqpuls_end; + bool eqpuls_end_present; + unsigned int eqpuls_bline; + bool eqpuls_bline_present; + unsigned int eqpuls_eline; + bool eqpuls_eline_present; + unsigned int havon_begin; + unsigned int havon_end; + unsigned int vavon_bline; + unsigned int vavon_eline; + unsigned int hso_begin; + unsigned int hso_end; + unsigned int vso_begin; + unsigned int vso_end; + unsigned int vso_bline; + unsigned int vso_eline; + bool vso_eline_present; + unsigned int sy_val; + bool sy_val_present; + unsigned int sy2_val; + bool sy2_val_present; + unsigned int max_lncnt; + } encp; +}; + +union meson_hdmi_venc_mode meson_hdmi_enci_mode_480i = { + .enci = { + .hso_begin = 5, + .hso_end = 129, + .vso_even = 3, + .vso_odd = 260, + .macv_max_amp = 0x810b, + .video_prog_mode = 0xf0, + .video_mode = 0x8, + .sch_adjust = 0x20, + .yc_delay = 0, + .pixel_start = 227, + .pixel_end = 1667, + .top_field_line_start = 18, + .top_field_line_end = 258, + .bottom_field_line_start = 19, + .bottom_field_line_end = 259, + }, +}; + +union meson_hdmi_venc_mode meson_hdmi_enci_mode_576i = { + .enci = { + .hso_begin = 3, + .hso_end = 129, + .vso_even = 3, + .vso_odd = 260, + .macv_max_amp = 8107, + .video_prog_mode = 0xff, + .video_mode = 0x13, + .sch_adjust = 0x28, + .yc_delay = 0x333, + .pixel_start = 251, + .pixel_end = 1691, + .top_field_line_start = 22, + .top_field_line_end = 310, + .bottom_field_line_start = 23, + .bottom_field_line_end = 311, + }, +}; + +union meson_hdmi_venc_mode meson_hdmi_encp_mode_480p = { + .encp = { + .dvi_settings = 0x21, + .video_mode = 0x4000, + .video_mode_adv = 0x9, + .video_prog_mode = 0, + .video_prog_mode_present = true, + .video_sync_mode = 7, + .video_sync_mode_present = true, + /* video_yc_dly */ + /* video_rgb_ctrl */ + .video_filt_ctrl = 0x2052, + .video_filt_ctrl_present = true, + /* video_ofld_voav_ofst */ + .yfp1_htime = 244, + .yfp2_htime = 1630, + .max_pxcnt = 1715, + .hspuls_begin = 0x22, + .hspuls_end = 0xa0, + .hspuls_switch = 88, + .vspuls_begin = 0, + .vspuls_end = 1589, + .vspuls_bline = 0, + .vspuls_eline = 5, + .havon_begin = 249, + .havon_end = 1689, + .vavon_bline = 42, + .vavon_eline = 521, + /* eqpuls_begin */ + /* eqpuls_end */ + /* eqpuls_bline */ + /* eqpuls_eline */ + .hso_begin = 3, + .hso_end = 5, + .vso_begin = 3, + .vso_end = 5, + .vso_bline = 0, + /* vso_eline */ + .sy_val = 8, + .sy_val_present = true, + .sy2_val = 0x1d8, + .sy2_val_present = true, + .max_lncnt = 524, + }, +}; + +union meson_hdmi_venc_mode meson_hdmi_encp_mode_576p = { + .encp = { + .dvi_settings = 0x21, + .video_mode = 0x4000, + .video_mode_adv = 0x9, + .video_prog_mode = 0, + .video_prog_mode_present = true, + .video_sync_mode = 7, + .video_sync_mode_present = true, + /* video_yc_dly */ + /* video_rgb_ctrl */ + .video_filt_ctrl = 0x52, + .video_filt_ctrl_present = true, + /* video_ofld_voav_ofst */ + .yfp1_htime = 235, + .yfp2_htime = 1674, + .max_pxcnt = 1727, + .hspuls_begin = 0, + .hspuls_end = 0x80, + .hspuls_switch = 88, + .vspuls_begin = 0, + .vspuls_end = 1599, + .vspuls_bline = 0, + .vspuls_eline = 4, + .havon_begin = 235, + .havon_end = 1674, + .vavon_bline = 44, + .vavon_eline = 619, + /* eqpuls_begin */ + /* eqpuls_end */ + /* eqpuls_bline */ + /* eqpuls_eline */ + .hso_begin = 0x80, + .hso_end = 0, + .vso_begin = 0, + .vso_end = 5, + .vso_bline = 0, + /* vso_eline */ + .sy_val = 8, + .sy_val_present = true, + .sy2_val = 0x1d8, + .sy2_val_present = true, + .max_lncnt = 624, + }, +}; + +union meson_hdmi_venc_mode meson_hdmi_encp_mode_720p60 = { + .encp = { + .dvi_settings = 0x2029, + .video_mode = 0x4040, + .video_mode_adv = 0x19, + /* video_prog_mode */ + /* video_sync_mode */ + /* video_yc_dly */ + /* video_rgb_ctrl */ + /* video_filt_ctrl */ + /* video_ofld_voav_ofst */ + .yfp1_htime = 648, + .yfp2_htime = 3207, + .max_pxcnt = 3299, + .hspuls_begin = 80, + .hspuls_end = 240, + .hspuls_switch = 80, + .vspuls_begin = 688, + .vspuls_end = 3248, + .vspuls_bline = 4, + .vspuls_eline = 8, + .havon_begin = 648, + .havon_end = 3207, + .vavon_bline = 29, + .vavon_eline = 748, + /* eqpuls_begin */ + /* eqpuls_end */ + /* eqpuls_bline */ + /* eqpuls_eline */ + .hso_begin = 256, + .hso_end = 168, + .vso_begin = 168, + .vso_end = 256, + .vso_bline = 0, + .vso_eline = 5, + .vso_eline_present = true, + /* sy_val */ + /* sy2_val */ + .max_lncnt = 749, + }, +}; + +union meson_hdmi_venc_mode meson_hdmi_encp_mode_720p50 = { + .encp = { + .dvi_settings = 0x202d, + .video_mode = 0x4040, + .video_mode_adv = 0x19, + .video_prog_mode = 0x100, + .video_prog_mode_present = true, + .video_sync_mode = 0x407, + .video_sync_mode_present = true, + .video_yc_dly = 0, + .video_yc_dly_present = true, + /* video_rgb_ctrl */ + /* video_filt_ctrl */ + /* video_ofld_voav_ofst */ + .yfp1_htime = 648, + .yfp2_htime = 3207, + .max_pxcnt = 3959, + .hspuls_begin = 80, + .hspuls_end = 240, + .hspuls_switch = 80, + .vspuls_begin = 688, + .vspuls_end = 3248, + .vspuls_bline = 4, + .vspuls_eline = 8, + .havon_begin = 648, + .havon_end = 3207, + .vavon_bline = 29, + .vavon_eline = 748, + /* eqpuls_begin */ + /* eqpuls_end */ + /* eqpuls_bline */ + /* eqpuls_eline */ + .hso_begin = 128, + .hso_end = 208, + .vso_begin = 128, + .vso_end = 128, + .vso_bline = 0, + .vso_eline = 5, + .vso_eline_present = true, + /* sy_val */ + /* sy2_val */ + .max_lncnt = 749, + }, +}; + +union meson_hdmi_venc_mode meson_hdmi_encp_mode_1080i60 = { + .encp = { + .dvi_settings = 0x2029, + .video_mode = 0x5ffc, + .video_mode_adv = 0x19, + .video_prog_mode = 0x100, + .video_prog_mode_present = true, + .video_sync_mode = 0x207, + .video_sync_mode_present = true, + /* video_yc_dly */ + /* video_rgb_ctrl */ + /* video_filt_ctrl */ + .video_ofld_voav_ofst = 0x11, + .video_ofld_voav_ofst_present = true, + .yfp1_htime = 516, + .yfp2_htime = 4355, + .max_pxcnt = 4399, + .hspuls_begin = 88, + .hspuls_end = 264, + .hspuls_switch = 88, + .vspuls_begin = 440, + .vspuls_end = 2200, + .vspuls_bline = 0, + .vspuls_eline = 4, + .havon_begin = 516, + .havon_end = 4355, + .vavon_bline = 20, + .vavon_eline = 559, + .eqpuls_begin = 2288, + .eqpuls_begin_present = true, + .eqpuls_end = 2464, + .eqpuls_end_present = true, + .eqpuls_bline = 0, + .eqpuls_bline_present = true, + .eqpuls_eline = 4, + .eqpuls_eline_present = true, + .hso_begin = 264, + .hso_end = 176, + .vso_begin = 88, + .vso_end = 88, + .vso_bline = 0, + .vso_eline = 5, + .vso_eline_present = true, + /* sy_val */ + /* sy2_val */ + .max_lncnt = 1124, + }, +}; + +union meson_hdmi_venc_mode meson_hdmi_encp_mode_1080i50 = { + .encp = { + .dvi_settings = 0x202d, + .video_mode = 0x5ffc, + .video_mode_adv = 0x19, + .video_prog_mode = 0x100, + .video_prog_mode_present = true, + .video_sync_mode = 0x7, + .video_sync_mode_present = true, + /* video_yc_dly */ + /* video_rgb_ctrl */ + /* video_filt_ctrl */ + .video_ofld_voav_ofst = 0x11, + .video_ofld_voav_ofst_present = true, + .yfp1_htime = 526, + .yfp2_htime = 4365, + .max_pxcnt = 5279, + .hspuls_begin = 88, + .hspuls_end = 264, + .hspuls_switch = 88, + .vspuls_begin = 440, + .vspuls_end = 2200, + .vspuls_bline = 0, + .vspuls_eline = 4, + .havon_begin = 526, + .havon_end = 4365, + .vavon_bline = 20, + .vavon_eline = 559, + .eqpuls_begin = 2288, + .eqpuls_begin_present = true, + .eqpuls_end = 2464, + .eqpuls_end_present = true, + .eqpuls_bline = 0, + .eqpuls_bline_present = true, + .eqpuls_eline = 4, + .eqpuls_eline_present = true, + .hso_begin = 142, + .hso_end = 230, + .vso_begin = 142, + .vso_end = 142, + .vso_bline = 0, + .vso_eline = 5, + .vso_eline_present = true, + /* sy_val */ + /* sy2_val */ + .max_lncnt = 1124, + }, +}; + +union meson_hdmi_venc_mode meson_hdmi_encp_mode_1080p24 = { + .encp = { + .dvi_settings = 0xd, + .video_mode = 0x4040, + .video_mode_adv = 0x18, + .video_prog_mode = 0x100, + .video_prog_mode_present = true, + .video_sync_mode = 0x7, + .video_sync_mode_present = true, + .video_yc_dly = 0, + .video_yc_dly_present = true, + .video_rgb_ctrl = 2, + .video_rgb_ctrl_present = true, + .video_filt_ctrl = 0x1052, + .video_filt_ctrl_present = true, + /* video_ofld_voav_ofst */ + .yfp1_htime = 271, + .yfp2_htime = 2190, + .max_pxcnt = 2749, + .hspuls_begin = 44, + .hspuls_end = 132, + .hspuls_switch = 44, + .vspuls_begin = 220, + .vspuls_end = 2140, + .vspuls_bline = 0, + .vspuls_eline = 4, + .havon_begin = 271, + .havon_end = 2190, + .vavon_bline = 41, + .vavon_eline = 1120, + /* eqpuls_begin */ + /* eqpuls_end */ + .eqpuls_bline = 0, + .eqpuls_bline_present = true, + .eqpuls_eline = 4, + .eqpuls_eline_present = true, + .hso_begin = 79, + .hso_end = 123, + .vso_begin = 79, + .vso_end = 79, + .vso_bline = 0, + .vso_eline = 5, + .vso_eline_present = true, + /* sy_val */ + /* sy2_val */ + .max_lncnt = 1124, + }, +}; + +union meson_hdmi_venc_mode meson_hdmi_encp_mode_1080p30 = { + .encp = { + .dvi_settings = 0x1, + .video_mode = 0x4040, + .video_mode_adv = 0x18, + .video_prog_mode = 0x100, + .video_prog_mode_present = true, + /* video_sync_mode */ + /* video_yc_dly */ + /* video_rgb_ctrl */ + .video_filt_ctrl = 0x1052, + .video_filt_ctrl_present = true, + /* video_ofld_voav_ofst */ + .yfp1_htime = 140, + .yfp2_htime = 2060, + .max_pxcnt = 2199, + .hspuls_begin = 2156, + .hspuls_end = 44, + .hspuls_switch = 44, + .vspuls_begin = 140, + .vspuls_end = 2059, + .vspuls_bline = 0, + .vspuls_eline = 4, + .havon_begin = 148, + .havon_end = 2067, + .vavon_bline = 41, + .vavon_eline = 1120, + /* eqpuls_begin */ + /* eqpuls_end */ + /* eqpuls_bline */ + /* eqpuls_eline */ + .hso_begin = 44, + .hso_end = 2156, + .vso_begin = 2100, + .vso_end = 2164, + .vso_bline = 0, + .vso_eline = 5, + .vso_eline_present = true, + /* sy_val */ + /* sy2_val */ + .max_lncnt = 1124, + }, +}; + +union meson_hdmi_venc_mode meson_hdmi_encp_mode_1080p50 = { + .encp = { + .dvi_settings = 0xd, + .video_mode = 0x4040, + .video_mode_adv = 0x18, + .video_prog_mode = 0x100, + .video_prog_mode_present = true, + .video_sync_mode = 0x7, + .video_sync_mode_present = true, + .video_yc_dly = 0, + .video_yc_dly_present = true, + .video_rgb_ctrl = 2, + .video_rgb_ctrl_present = true, + /* video_filt_ctrl */ + /* video_ofld_voav_ofst */ + .yfp1_htime = 271, + .yfp2_htime = 2190, + .max_pxcnt = 2639, + .hspuls_begin = 44, + .hspuls_end = 132, + .hspuls_switch = 44, + .vspuls_begin = 220, + .vspuls_end = 2140, + .vspuls_bline = 0, + .vspuls_eline = 4, + .havon_begin = 271, + .havon_end = 2190, + .vavon_bline = 41, + .vavon_eline = 1120, + /* eqpuls_begin */ + /* eqpuls_end */ + .eqpuls_bline = 0, + .eqpuls_bline_present = true, + .eqpuls_eline = 4, + .eqpuls_eline_present = true, + .hso_begin = 79, + .hso_end = 123, + .vso_begin = 79, + .vso_end = 79, + .vso_bline = 0, + .vso_eline = 5, + .vso_eline_present = true, + /* sy_val */ + /* sy2_val */ + .max_lncnt = 1124, + }, +}; + +union meson_hdmi_venc_mode meson_hdmi_encp_mode_1080p60 = { + .encp = { + .dvi_settings = 0x1, + .video_mode = 0x4040, + .video_mode_adv = 0x18, + .video_prog_mode = 0x100, + .video_prog_mode_present = true, + /* video_sync_mode */ + /* video_yc_dly */ + /* video_rgb_ctrl */ + .video_filt_ctrl = 0x1052, + .video_filt_ctrl_present = true, + /* video_ofld_voav_ofst */ + .yfp1_htime = 140, + .yfp2_htime = 2060, + .max_pxcnt = 2199, + .hspuls_begin = 2156, + .hspuls_end = 44, + .hspuls_switch = 44, + .vspuls_begin = 140, + .vspuls_end = 2059, + .vspuls_bline = 0, + .vspuls_eline = 4, + .havon_begin = 148, + .havon_end = 2067, + .vavon_bline = 41, + .vavon_eline = 1120, + /* eqpuls_begin */ + /* eqpuls_end */ + /* eqpuls_bline */ + /* eqpuls_eline */ + .hso_begin = 44, + .hso_end = 2156, + .vso_begin = 2100, + .vso_end = 2164, + .vso_bline = 0, + .vso_eline = 5, + .vso_eline_present = true, + /* sy_val */ + /* sy2_val */ + .max_lncnt = 1124, + }, +}; + +struct meson_hdmi_venc_vic_mode { + unsigned int vic; + union meson_hdmi_venc_mode *mode; +} meson_hdmi_venc_vic_modes[] = { + { 6, &meson_hdmi_enci_mode_480i }, + { 7, &meson_hdmi_enci_mode_480i }, + { 21, &meson_hdmi_enci_mode_576i }, + { 22, &meson_hdmi_enci_mode_576i }, + { 2, &meson_hdmi_encp_mode_480p }, + { 3, &meson_hdmi_encp_mode_480p }, + { 17, &meson_hdmi_encp_mode_576p }, + { 18, &meson_hdmi_encp_mode_576p }, + { 4, &meson_hdmi_encp_mode_720p60 }, + { 19, &meson_hdmi_encp_mode_720p50 }, + { 5, &meson_hdmi_encp_mode_1080i60 }, + { 20, &meson_hdmi_encp_mode_1080i50 }, + { 32, &meson_hdmi_encp_mode_1080p24 }, + { 34, &meson_hdmi_encp_mode_1080p30 }, + { 31, &meson_hdmi_encp_mode_1080p50 }, + { 16, &meson_hdmi_encp_mode_1080p60 }, + { 0, NULL}, /* sentinel */ +}; + +static signed int to_signed(unsigned int a) +{ + if (a <= 7) + return a; + else + return a - 16; +} + +static unsigned long modulo(unsigned long a, unsigned long b) +{ + if (a >= b) + return a - b; + else + return a; +} + +bool meson_venc_hdmi_supported_vic(int vic) +{ + struct meson_hdmi_venc_vic_mode *vmode = meson_hdmi_venc_vic_modes; + + while (vmode->vic && vmode->mode) { + if (vmode->vic == vic) + return true; + vmode++; + } + + return false; +} +EXPORT_SYMBOL_GPL(meson_venc_hdmi_supported_vic); + +static union meson_hdmi_venc_mode *meson_venc_hdmi_get_vic_vmode(int vic) +{ + struct meson_hdmi_venc_vic_mode *vmode = meson_hdmi_venc_vic_modes; + + while (vmode->vic && vmode->mode) { + if (vmode->vic == vic) + return vmode->mode; + vmode++; + } + + return NULL; +} + +bool meson_venc_hdmi_venc_repeat(int vic) +{ + /* Repeat VENC pixels for 480/576i/p, 720p50/60 and 1080p50/60 */ + if (vic == 6 || vic == 7 || /* 480i */ + vic == 21 || vic == 22 || /* 576i */ + vic == 17 || vic == 18 || /* 576p */ + vic == 2 || vic == 3 || /* 480p */ + vic == 4 || /* 720p60 */ + vic == 19 || /* 720p50 */ + vic == 5 || /* 1080i60 */ + vic == 20) /* 1080i50 */ + return true; + + return false; +} +EXPORT_SYMBOL_GPL(meson_venc_hdmi_venc_repeat); + +void meson_venc_hdmi_mode_set(struct meson_drm *priv, int vic, + struct drm_display_mode *mode) +{ + union meson_hdmi_venc_mode *vmode = NULL; + bool use_enci = false; + bool venc_repeat = false; + bool hdmi_repeat = false; + unsigned int venc_hdmi_latency = 2; + unsigned long total_pixels_venc = 0; + unsigned long active_pixels_venc = 0; + unsigned long front_porch_venc = 0; + unsigned long hsync_pixels_venc = 0; + unsigned long de_h_begin = 0; + unsigned long de_h_end = 0; + unsigned long de_v_begin_even = 0; + unsigned long de_v_end_even = 0; + unsigned long de_v_begin_odd = 0; + unsigned long de_v_end_odd = 0; + unsigned long hs_begin = 0; + unsigned long hs_end = 0; + unsigned long vs_adjust = 0; + unsigned long vs_bline_evn = 0; + unsigned long vs_eline_evn = 0; + unsigned long vs_bline_odd = 0; + unsigned long vs_eline_odd = 0; + unsigned long vso_begin_evn = 0; + unsigned long vso_begin_odd = 0; + unsigned int eof_lines; + unsigned int sof_lines; + unsigned int vsync_lines; + + vmode = meson_venc_hdmi_get_vic_vmode(vic); + if (!vmode) { + dev_err(priv->dev, "%s: Fatal Error, unsupported vic %d\n", + __func__, vic); + return; + } + + /* Use VENCI for 480i and 576i and double HDMI pixels */ + if (mode->flags & DRM_MODE_FLAG_DBLCLK) { + hdmi_repeat = true; + use_enci = true; + venc_hdmi_latency = 1; + } + + /* Repeat VENC pixels for 480/576i/p, 720p50/60 and 1080p50/60 */ + if (meson_venc_hdmi_venc_repeat(vic)) + venc_repeat = true; + + eof_lines = mode->vsync_start - mode->vdisplay; + if (mode->flags & DRM_MODE_FLAG_INTERLACE) + eof_lines /= 2; + sof_lines = mode->vtotal - mode->vsync_end; + if (mode->flags & DRM_MODE_FLAG_INTERLACE) + sof_lines /= 2; + vsync_lines = mode->vsync_end - mode->vsync_start; + if (mode->flags & DRM_MODE_FLAG_INTERLACE) + vsync_lines /= 2; + + total_pixels_venc = mode->htotal; + if (hdmi_repeat) + total_pixels_venc /= 2; + if (venc_repeat) + total_pixels_venc *= 2; + + active_pixels_venc = mode->hdisplay; + if (hdmi_repeat) + active_pixels_venc /= 2; + if (venc_repeat) + active_pixels_venc *= 2; + + front_porch_venc = (mode->hsync_start - mode->hdisplay); + if (hdmi_repeat) + front_porch_venc /= 2; + if (venc_repeat) + front_porch_venc *= 2; + + hsync_pixels_venc = (mode->hsync_end - mode->hsync_start); + if (hdmi_repeat) + hsync_pixels_venc /= 2; + if (venc_repeat) + hsync_pixels_venc *= 2; + + /* Disable VDACs */ + writel_bits_relaxed(0x1f, 0x1f, + priv->io_base + _REG(VENC_VDAC_SETTING)); + + writel_relaxed(0, priv->io_base + _REG(ENCI_VIDEO_EN)); + writel_relaxed(0, priv->io_base + _REG(ENCP_VIDEO_EN)); + + if (use_enci) { + unsigned int lines_f0; + unsigned int lines_f1; + + /* CVBS Filter settings */ + writel_relaxed(0x12, priv->io_base + _REG(ENCI_CFILT_CTRL)); + writel_relaxed(0x12, priv->io_base + _REG(ENCI_CFILT_CTRL2)); + + /* Digital Video Select : Interlace, clk27 clk, external */ + writel_relaxed(0, priv->io_base + _REG(VENC_DVI_SETTING)); + + /* Reset Video Mode */ + writel_relaxed(0, priv->io_base + _REG(ENCI_VIDEO_MODE)); + writel_relaxed(0, priv->io_base + _REG(ENCI_VIDEO_MODE_ADV)); + + /* Horizontal sync signal output */ + writel_relaxed(vmode->enci.hso_begin, + priv->io_base + _REG(ENCI_SYNC_HSO_BEGIN)); + writel_relaxed(vmode->enci.hso_end, + priv->io_base + _REG(ENCI_SYNC_HSO_END)); + + /* Vertical Sync lines */ + writel_relaxed(vmode->enci.vso_even, + priv->io_base + _REG(ENCI_SYNC_VSO_EVNLN)); + writel_relaxed(vmode->enci.vso_odd, + priv->io_base + _REG(ENCI_SYNC_VSO_ODDLN)); + + /* Macrovision max amplitude change */ + writel_relaxed(vmode->enci.macv_max_amp, + priv->io_base + _REG(ENCI_MACV_MAX_AMP)); + + /* Video mode */ + writel_relaxed(vmode->enci.video_prog_mode, + priv->io_base + _REG(VENC_VIDEO_PROG_MODE)); + writel_relaxed(vmode->enci.video_mode, + priv->io_base + _REG(ENCI_VIDEO_MODE)); + + /* Advanced Video Mode : + * Demux shifting 0x2 + * Blank line end at line17/22 + * High bandwidth Luma Filter + * Low bandwidth Chroma Filter + * Bypass luma low pass filter + * No macrovision on CSYNC + */ + writel_relaxed(0x26, priv->io_base + _REG(ENCI_VIDEO_MODE_ADV)); + + writel(vmode->enci.sch_adjust, + priv->io_base + _REG(ENCI_VIDEO_SCH)); + + /* Sync mode : MASTER Master mode, free run, send HSO/VSO out */ + writel_relaxed(0x07, priv->io_base + _REG(ENCI_SYNC_MODE)); + + if (vmode->enci.yc_delay) + writel_relaxed(vmode->enci.yc_delay, + priv->io_base + _REG(ENCI_YC_DELAY)); + + + /* UNreset Interlaced TV Encoder */ + writel_relaxed(0, priv->io_base + _REG(ENCI_DBG_PX_RST)); + + /* Enable Vfifo2vd, Y_Cb_Y_Cr select */ + writel_relaxed(0x4e01, priv->io_base + _REG(ENCI_VFIFO2VD_CTL)); + + /* Timings */ + writel_relaxed(vmode->enci.pixel_start, + priv->io_base + _REG(ENCI_VFIFO2VD_PIXEL_START)); + writel_relaxed(vmode->enci.pixel_end, + priv->io_base + _REG(ENCI_VFIFO2VD_PIXEL_END)); + + writel_relaxed(vmode->enci.top_field_line_start, + priv->io_base + _REG(ENCI_VFIFO2VD_LINE_TOP_START)); + writel_relaxed(vmode->enci.top_field_line_end, + priv->io_base + _REG(ENCI_VFIFO2VD_LINE_TOP_END)); + + writel_relaxed(vmode->enci.bottom_field_line_start, + priv->io_base + _REG(ENCI_VFIFO2VD_LINE_BOT_START)); + writel_relaxed(vmode->enci.bottom_field_line_end, + priv->io_base + _REG(ENCI_VFIFO2VD_LINE_BOT_END)); + + /* Select ENCI for VIU */ + meson_vpp_setup_mux(priv, MESON_VIU_VPP_MUX_ENCI); + + /* Interlace video enable */ + writel_relaxed(1, priv->io_base + _REG(ENCI_VIDEO_EN)); + + lines_f0 = mode->vtotal >> 1; + lines_f1 = lines_f0 + 1; + + de_h_begin = modulo(readl_relaxed(priv->io_base + + _REG(ENCI_VFIFO2VD_PIXEL_START)) + + venc_hdmi_latency, + total_pixels_venc); + de_h_end = modulo(de_h_begin + active_pixels_venc, + total_pixels_venc); + + writel_relaxed(de_h_begin, + priv->io_base + _REG(ENCI_DE_H_BEGIN)); + writel_relaxed(de_h_end, + priv->io_base + _REG(ENCI_DE_H_END)); + + de_v_begin_even = readl_relaxed(priv->io_base + + _REG(ENCI_VFIFO2VD_LINE_TOP_START)); + de_v_end_even = de_v_begin_even + mode->vdisplay; + de_v_begin_odd = readl_relaxed(priv->io_base + + _REG(ENCI_VFIFO2VD_LINE_BOT_START)); + de_v_end_odd = de_v_begin_odd + mode->vdisplay; + + writel_relaxed(de_v_begin_even, + priv->io_base + _REG(ENCI_DE_V_BEGIN_EVEN)); + writel_relaxed(de_v_end_even, + priv->io_base + _REG(ENCI_DE_V_END_EVEN)); + writel_relaxed(de_v_begin_odd, + priv->io_base + _REG(ENCI_DE_V_BEGIN_ODD)); + writel_relaxed(de_v_end_odd, + priv->io_base + _REG(ENCI_DE_V_END_ODD)); + + /* Program Hsync timing */ + hs_begin = de_h_end + front_porch_venc; + if (de_h_end + front_porch_venc >= total_pixels_venc) { + hs_begin -= total_pixels_venc; + vs_adjust = 1; + } else { + hs_begin = de_h_end + front_porch_venc; + vs_adjust = 0; + } + + hs_end = modulo(hs_begin + hsync_pixels_venc, + total_pixels_venc); + writel_relaxed(hs_begin, + priv->io_base + _REG(ENCI_DVI_HSO_BEGIN)); + writel_relaxed(hs_end, + priv->io_base + _REG(ENCI_DVI_HSO_END)); + + /* Program Vsync timing for even field */ + if (((de_v_end_odd - 1) + eof_lines + vs_adjust) >= lines_f1) { + vs_bline_evn = (de_v_end_odd - 1) + + eof_lines + + vs_adjust + - lines_f1; + vs_eline_evn = vs_bline_evn + vsync_lines; + + writel_relaxed(vs_bline_evn, + priv->io_base + _REG(ENCI_DVI_VSO_BLINE_EVN)); + + writel_relaxed(vs_eline_evn, + priv->io_base + _REG(ENCI_DVI_VSO_ELINE_EVN)); + + writel_relaxed(hs_begin, + priv->io_base + _REG(ENCI_DVI_VSO_BEGIN_EVN)); + writel_relaxed(hs_begin, + priv->io_base + _REG(ENCI_DVI_VSO_END_EVN)); + } else { + vs_bline_odd = (de_v_end_odd - 1) + + eof_lines + + vs_adjust; + + writel_relaxed(vs_bline_odd, + priv->io_base + _REG(ENCI_DVI_VSO_BLINE_ODD)); + + writel_relaxed(hs_begin, + priv->io_base + _REG(ENCI_DVI_VSO_BEGIN_ODD)); + + if ((vs_bline_odd + vsync_lines) >= lines_f1) { + vs_eline_evn = vs_bline_odd + + vsync_lines + - lines_f1; + + writel_relaxed(vs_eline_evn, priv->io_base + + _REG(ENCI_DVI_VSO_ELINE_EVN)); + + writel_relaxed(hs_begin, priv->io_base + + _REG(ENCI_DVI_VSO_END_EVN)); + } else { + vs_eline_odd = vs_bline_odd + + vsync_lines; + + writel_relaxed(vs_eline_odd, priv->io_base + + _REG(ENCI_DVI_VSO_ELINE_ODD)); + + writel_relaxed(hs_begin, priv->io_base + + _REG(ENCI_DVI_VSO_END_ODD)); + } + } + + /* Program Vsync timing for odd field */ + if (((de_v_end_even - 1) + (eof_lines + 1)) >= lines_f0) { + vs_bline_odd = (de_v_end_even - 1) + + (eof_lines + 1) + - lines_f0; + vs_eline_odd = vs_bline_odd + vsync_lines; + + writel_relaxed(vs_bline_odd, + priv->io_base + _REG(ENCI_DVI_VSO_BLINE_ODD)); + + writel_relaxed(vs_eline_odd, + priv->io_base + _REG(ENCI_DVI_VSO_ELINE_ODD)); + + vso_begin_odd = modulo(hs_begin + + (total_pixels_venc >> 1), + total_pixels_venc); + + writel_relaxed(vso_begin_odd, + priv->io_base + _REG(ENCI_DVI_VSO_BEGIN_ODD)); + writel_relaxed(vso_begin_odd, + priv->io_base + _REG(ENCI_DVI_VSO_END_ODD)); + } else { + vs_bline_evn = (de_v_end_even - 1) + + (eof_lines + 1); + + writel_relaxed(vs_bline_evn, + priv->io_base + _REG(ENCI_DVI_VSO_BLINE_EVN)); + + vso_begin_evn = modulo(hs_begin + + (total_pixels_venc >> 1), + total_pixels_venc); + + writel_relaxed(vso_begin_evn, priv->io_base + + _REG(ENCI_DVI_VSO_BEGIN_EVN)); + + if (vs_bline_evn + vsync_lines >= lines_f0) { + vs_eline_odd = vs_bline_evn + + vsync_lines + - lines_f0; + + writel_relaxed(vs_eline_odd, priv->io_base + + _REG(ENCI_DVI_VSO_ELINE_ODD)); + + writel_relaxed(vso_begin_evn, priv->io_base + + _REG(ENCI_DVI_VSO_END_ODD)); + } else { + vs_eline_evn = vs_bline_evn + vsync_lines; + + writel_relaxed(vs_eline_evn, priv->io_base + + _REG(ENCI_DVI_VSO_ELINE_EVN)); + + writel_relaxed(vso_begin_evn, priv->io_base + + _REG(ENCI_DVI_VSO_END_EVN)); + } + } + } else { + writel_relaxed(vmode->encp.dvi_settings, + priv->io_base + _REG(VENC_DVI_SETTING)); + writel_relaxed(vmode->encp.video_mode, + priv->io_base + _REG(ENCP_VIDEO_MODE)); + writel_relaxed(vmode->encp.video_mode_adv, + priv->io_base + _REG(ENCP_VIDEO_MODE_ADV)); + if (vmode->encp.video_prog_mode_present) + writel_relaxed(vmode->encp.video_prog_mode, + priv->io_base + _REG(VENC_VIDEO_PROG_MODE)); + if (vmode->encp.video_sync_mode_present) + writel_relaxed(vmode->encp.video_sync_mode, + priv->io_base + _REG(ENCP_VIDEO_SYNC_MODE)); + if (vmode->encp.video_yc_dly_present) + writel_relaxed(vmode->encp.video_yc_dly, + priv->io_base + _REG(ENCP_VIDEO_YC_DLY)); + if (vmode->encp.video_rgb_ctrl_present) + writel_relaxed(vmode->encp.video_rgb_ctrl, + priv->io_base + _REG(ENCP_VIDEO_RGB_CTRL)); + if (vmode->encp.video_filt_ctrl_present) + writel_relaxed(vmode->encp.video_filt_ctrl, + priv->io_base + _REG(ENCP_VIDEO_FILT_CTRL)); + if (vmode->encp.video_ofld_voav_ofst_present) + writel_relaxed(vmode->encp.video_ofld_voav_ofst, + priv->io_base + + _REG(ENCP_VIDEO_OFLD_VOAV_OFST)); + writel_relaxed(vmode->encp.yfp1_htime, + priv->io_base + _REG(ENCP_VIDEO_YFP1_HTIME)); + writel_relaxed(vmode->encp.yfp2_htime, + priv->io_base + _REG(ENCP_VIDEO_YFP2_HTIME)); + writel_relaxed(vmode->encp.max_pxcnt, + priv->io_base + _REG(ENCP_VIDEO_MAX_PXCNT)); + writel_relaxed(vmode->encp.hspuls_begin, + priv->io_base + _REG(ENCP_VIDEO_HSPULS_BEGIN)); + writel_relaxed(vmode->encp.hspuls_end, + priv->io_base + _REG(ENCP_VIDEO_HSPULS_END)); + writel_relaxed(vmode->encp.hspuls_switch, + priv->io_base + _REG(ENCP_VIDEO_HSPULS_SWITCH)); + writel_relaxed(vmode->encp.vspuls_begin, + priv->io_base + _REG(ENCP_VIDEO_VSPULS_BEGIN)); + writel_relaxed(vmode->encp.vspuls_end, + priv->io_base + _REG(ENCP_VIDEO_VSPULS_END)); + writel_relaxed(vmode->encp.vspuls_bline, + priv->io_base + _REG(ENCP_VIDEO_VSPULS_BLINE)); + writel_relaxed(vmode->encp.vspuls_eline, + priv->io_base + _REG(ENCP_VIDEO_VSPULS_ELINE)); + if (vmode->encp.eqpuls_begin_present) + writel_relaxed(vmode->encp.eqpuls_begin, + priv->io_base + _REG(ENCP_VIDEO_EQPULS_BEGIN)); + if (vmode->encp.eqpuls_end_present) + writel_relaxed(vmode->encp.eqpuls_end, + priv->io_base + _REG(ENCP_VIDEO_EQPULS_END)); + if (vmode->encp.eqpuls_bline_present) + writel_relaxed(vmode->encp.eqpuls_bline, + priv->io_base + _REG(ENCP_VIDEO_EQPULS_BLINE)); + if (vmode->encp.eqpuls_eline_present) + writel_relaxed(vmode->encp.eqpuls_eline, + priv->io_base + _REG(ENCP_VIDEO_EQPULS_ELINE)); + writel_relaxed(vmode->encp.havon_begin, + priv->io_base + _REG(ENCP_VIDEO_HAVON_BEGIN)); + writel_relaxed(vmode->encp.havon_end, + priv->io_base + _REG(ENCP_VIDEO_HAVON_END)); + writel_relaxed(vmode->encp.vavon_bline, + priv->io_base + _REG(ENCP_VIDEO_VAVON_BLINE)); + writel_relaxed(vmode->encp.vavon_eline, + priv->io_base + _REG(ENCP_VIDEO_VAVON_ELINE)); + writel_relaxed(vmode->encp.hso_begin, + priv->io_base + _REG(ENCP_VIDEO_HSO_BEGIN)); + writel_relaxed(vmode->encp.hso_end, + priv->io_base + _REG(ENCP_VIDEO_HSO_END)); + writel_relaxed(vmode->encp.vso_begin, + priv->io_base + _REG(ENCP_VIDEO_VSO_BEGIN)); + writel_relaxed(vmode->encp.vso_end, + priv->io_base + _REG(ENCP_VIDEO_VSO_END)); + writel_relaxed(vmode->encp.vso_bline, + priv->io_base + _REG(ENCP_VIDEO_VSO_BLINE)); + if (vmode->encp.vso_eline_present) + writel_relaxed(vmode->encp.vso_eline, + priv->io_base + _REG(ENCP_VIDEO_VSO_ELINE)); + if (vmode->encp.sy_val_present) + writel_relaxed(vmode->encp.sy_val, + priv->io_base + _REG(ENCP_VIDEO_SY_VAL)); + if (vmode->encp.sy2_val_present) + writel_relaxed(vmode->encp.sy2_val, + priv->io_base + _REG(ENCP_VIDEO_SY2_VAL)); + writel_relaxed(vmode->encp.max_lncnt, + priv->io_base + _REG(ENCP_VIDEO_MAX_LNCNT)); + + writel_relaxed(1, priv->io_base + _REG(ENCP_VIDEO_EN)); + + /* Set DE signal’s polarity is active high */ + writel_bits_relaxed(BIT(14), BIT(14), + priv->io_base + _REG(ENCP_VIDEO_MODE)); + + /* Program DE timing */ + de_h_begin = modulo(readl_relaxed(priv->io_base + + _REG(ENCP_VIDEO_HAVON_BEGIN)) + + venc_hdmi_latency, + total_pixels_venc); + de_h_end = modulo(de_h_begin + active_pixels_venc, + total_pixels_venc); + + writel_relaxed(de_h_begin, + priv->io_base + _REG(ENCP_DE_H_BEGIN)); + writel_relaxed(de_h_end, + priv->io_base + _REG(ENCP_DE_H_END)); + + /* Program DE timing for even field */ + de_v_begin_even = readl_relaxed(priv->io_base + + _REG(ENCP_VIDEO_VAVON_BLINE)); + if (mode->flags & DRM_MODE_FLAG_INTERLACE) + de_v_end_even = de_v_begin_even + + (mode->vdisplay / 2); + else + de_v_end_even = de_v_begin_even + mode->vdisplay; + + writel_relaxed(de_v_begin_even, + priv->io_base + _REG(ENCP_DE_V_BEGIN_EVEN)); + writel_relaxed(de_v_end_even, + priv->io_base + _REG(ENCP_DE_V_END_EVEN)); + + /* Program DE timing for odd field if needed */ + if (mode->flags & DRM_MODE_FLAG_INTERLACE) { + unsigned int ofld_voav_ofst = + readl_relaxed(priv->io_base + + _REG(ENCP_VIDEO_OFLD_VOAV_OFST)); + de_v_begin_odd = to_signed((ofld_voav_ofst & 0xf0) >> 4) + + de_v_begin_even + + ((mode->vtotal - 1) / 2); + de_v_end_odd = de_v_begin_odd + (mode->vdisplay / 2); + + writel_relaxed(de_v_begin_odd, + priv->io_base + _REG(ENCP_DE_V_BEGIN_ODD)); + writel_relaxed(de_v_end_odd, + priv->io_base + _REG(ENCP_DE_V_END_ODD)); + } + + /* Program Hsync timing */ + if ((de_h_end + front_porch_venc) >= total_pixels_venc) { + hs_begin = de_h_end + + front_porch_venc + - total_pixels_venc; + vs_adjust = 1; + } else { + hs_begin = de_h_end + + front_porch_venc; + vs_adjust = 0; + } + + hs_end = modulo(hs_begin + hsync_pixels_venc, + total_pixels_venc); + + writel_relaxed(hs_begin, + priv->io_base + _REG(ENCP_DVI_HSO_BEGIN)); + writel_relaxed(hs_end, + priv->io_base + _REG(ENCP_DVI_HSO_END)); + + /* Program Vsync timing for even field */ + if (de_v_begin_even >= + (sof_lines + vsync_lines + (1 - vs_adjust))) + vs_bline_evn = de_v_begin_even + - sof_lines + - vsync_lines + - (1 - vs_adjust); + else + vs_bline_evn = mode->vtotal + + de_v_begin_even + - sof_lines + - vsync_lines + - (1 - vs_adjust); + + vs_eline_evn = modulo(vs_bline_evn + vsync_lines, + mode->vtotal); + + writel_relaxed(vs_bline_evn, + priv->io_base + _REG(ENCP_DVI_VSO_BLINE_EVN)); + writel_relaxed(vs_eline_evn, + priv->io_base + _REG(ENCP_DVI_VSO_ELINE_EVN)); + + vso_begin_evn = hs_begin; + writel_relaxed(vso_begin_evn, + priv->io_base + _REG(ENCP_DVI_VSO_BEGIN_EVN)); + writel_relaxed(vso_begin_evn, + priv->io_base + _REG(ENCP_DVI_VSO_END_EVN)); + + /* Program Vsync timing for odd field if needed */ + if (mode->flags & DRM_MODE_FLAG_INTERLACE) { + vs_bline_odd = (de_v_begin_odd - 1) + - sof_lines + - vsync_lines; + vs_eline_odd = (de_v_begin_odd - 1) + - vsync_lines; + vso_begin_odd = modulo(hs_begin + + (total_pixels_venc >> 1), + total_pixels_venc); + + writel_relaxed(vs_bline_odd, + priv->io_base + _REG(ENCP_DVI_VSO_BLINE_ODD)); + writel_relaxed(vs_eline_odd, + priv->io_base + _REG(ENCP_DVI_VSO_ELINE_ODD)); + writel_relaxed(vso_begin_odd, + priv->io_base + _REG(ENCP_DVI_VSO_BEGIN_ODD)); + writel_relaxed(vso_begin_odd, + priv->io_base + _REG(ENCP_DVI_VSO_END_ODD)); + } + + /* Select ENCP for VIU */ + meson_vpp_setup_mux(priv, MESON_VIU_VPP_MUX_ENCP); + } + + writel_relaxed((use_enci ? 1 : 2) | + (mode->flags & DRM_MODE_FLAG_PHSYNC ? 1 << 2 : 0) | + (mode->flags & DRM_MODE_FLAG_PVSYNC ? 1 << 3 : 0) | + 4 << 5 | + (venc_repeat ? 1 << 8 : 0) | + (hdmi_repeat ? 1 << 12 : 0), + priv->io_base + _REG(VPU_HDMI_SETTING)); + + priv->venc.hdmi_repeat = hdmi_repeat; + priv->venc.venc_repeat = venc_repeat; + priv->venc.hdmi_use_enci = use_enci; + + priv->venc.current_mode = MESON_VENC_MODE_HDMI; +} +EXPORT_SYMBOL_GPL(meson_venc_hdmi_mode_set); + void meson_venci_cvbs_mode_set(struct meson_drm *priv, struct meson_cvbs_enci_mode *mode) { @@ -223,9 +1468,6 @@ void meson_venci_cvbs_mode_set(struct meson_drm *priv, writel_relaxed(mode->analog_sync_adj, priv->io_base + _REG(ENCI_SYNC_ADJ)); - /* Setup 27MHz vclk2 for ENCI and VDAC */ - meson_vclk_setup(priv, MESON_VCLK_TARGET_CVBS, MESON_VCLK_CVBS); - priv->venc.current_mode = mode->mode_tag; } diff --git a/drivers/gpu/drm/meson/meson_venc.h b/drivers/gpu/drm/meson/meson_venc.h index 77d4a7d..a1b96e8 100644 --- a/drivers/gpu/drm/meson/meson_venc.h +++ b/drivers/gpu/drm/meson/meson_venc.h @@ -30,6 +30,7 @@ enum { MESON_VENC_MODE_NONE = 0, MESON_VENC_MODE_CVBS_PAL, MESON_VENC_MODE_CVBS_NTSC, + MESON_VENC_MODE_HDMI, }; struct meson_cvbs_enci_mode { @@ -56,12 +57,18 @@ struct meson_cvbs_enci_mode { unsigned int analog_sync_adj; }; +/* HDMI Clock parameters */ +bool meson_venc_hdmi_supported_vic(int vic); +bool meson_venc_hdmi_venc_repeat(int vic); + /* CVBS Timings and Parameters */ extern struct meson_cvbs_enci_mode meson_cvbs_enci_pal; extern struct meson_cvbs_enci_mode meson_cvbs_enci_ntsc; void meson_venci_cvbs_mode_set(struct meson_drm *priv, struct meson_cvbs_enci_mode *mode); +void meson_venc_hdmi_mode_set(struct meson_drm *priv, int vic, + struct drm_display_mode *mode); unsigned int meson_venci_get_field(struct meson_drm *priv); void meson_venc_enable_vsync(struct meson_drm *priv); diff --git a/drivers/gpu/drm/meson/meson_venc_cvbs.c b/drivers/gpu/drm/meson/meson_venc_cvbs.c index a2bcc70..00775b3 100644 --- a/drivers/gpu/drm/meson/meson_venc_cvbs.c +++ b/drivers/gpu/drm/meson/meson_venc_cvbs.c @@ -32,6 +32,7 @@ #include "meson_venc_cvbs.h" #include "meson_venc.h" +#include "meson_vclk.h" #include "meson_registers.h" /* HHI VDAC Registers */ @@ -194,14 +195,20 @@ static void meson_venc_cvbs_encoder_mode_set(struct drm_encoder *encoder, { struct meson_venc_cvbs *meson_venc_cvbs = encoder_to_meson_venc_cvbs(encoder); + struct meson_drm *priv = meson_venc_cvbs->priv; int i; for (i = 0; i < MESON_CVBS_MODES_COUNT; ++i) { struct meson_cvbs_mode *meson_mode = &meson_cvbs_modes[i]; if (drm_mode_equal(mode, &meson_mode->mode)) { - meson_venci_cvbs_mode_set(meson_venc_cvbs->priv, + meson_venci_cvbs_mode_set(priv, meson_mode->enci); + + /* Setup 27MHz vclk2 for ENCI and VDAC */ + meson_vclk_setup(priv, MESON_VCLK_TARGET_CVBS, + MESON_VCLK_CVBS, MESON_VCLK_CVBS, + MESON_VCLK_CVBS, true); break; } } @@ -217,25 +224,14 @@ static const struct drm_encoder_helper_funcs static bool meson_venc_cvbs_connector_is_available(struct meson_drm *priv) { - struct device_node *ep, *remote; + struct device_node *remote; - /* CVBS VDAC output is on the first port, first endpoint */ - ep = of_graph_get_endpoint_by_regs(priv->dev->of_node, 0, 0); - if (!ep) + remote = of_graph_get_remote_node(priv->dev->of_node, 0, 0); + if (!remote) return false; - - /* If the endpoint node exists, consider it enabled */ - remote = of_graph_get_remote_port(ep); - if (remote) { - of_node_put(ep); - return true; - } - - of_node_put(ep); of_node_put(remote); - - return false; + return true; } int meson_venc_cvbs_create(struct meson_drm *priv) @@ -248,7 +244,7 @@ int meson_venc_cvbs_create(struct meson_drm *priv) if (!meson_venc_cvbs_connector_is_available(priv)) { dev_info(drm->dev, "CVBS Output connector not available\n"); - return -ENODEV; + return 0; } meson_venc_cvbs = devm_kzalloc(priv->dev, sizeof(*meson_venc_cvbs), diff --git a/drivers/gpu/drm/meson/meson_viu.c b/drivers/gpu/drm/meson/meson_viu.c index a6de8ba..6bcfa52 100644 --- a/drivers/gpu/drm/meson/meson_viu.c +++ b/drivers/gpu/drm/meson/meson_viu.c @@ -28,9 +28,12 @@ #include "meson_canvas.h" #include "meson_registers.h" -/* +/** + * DOC: Video Input Unit + * * VIU Handles the Pixel scanout and the basic Colorspace conversions * We handle the following features : + * * - OSD1 RGB565/RGB888/xRGB8888 scanout * - RGB conversion to x/cb/cr * - Progressive or Interlace buffer scanout @@ -38,6 +41,7 @@ * - HDR OSD matrix for GXL/GXM * * What is missing : + * * - BGR888/xBGR8888/BGRx8888/BGRx8888 modes * - YUV4:2:2 Y0CbY1Cr scanout * - Conversion to YUV 4:4:4 from 4:2:2 input diff --git a/drivers/gpu/drm/meson/meson_vpp.c b/drivers/gpu/drm/meson/meson_vpp.c index 671909d..27356f8 100644 --- a/drivers/gpu/drm/meson/meson_vpp.c +++ b/drivers/gpu/drm/meson/meson_vpp.c @@ -25,16 +25,20 @@ #include "meson_vpp.h" #include "meson_registers.h" -/* +/** + * DOC: Video Post Processing + * * VPP Handles all the Post Processing after the Scanout from the VIU * We handle the following post processings : - * - Postblend : Blends the OSD1 only + * + * - Postblend, Blends the OSD1 only * We exclude OSD2, VS1, VS1 and Preblend output * - Vertical OSD Scaler for OSD1 only, we disable vertical scaler and * use it only for interlace scanout * - Intermediate FIFO with default Amlogic values * * What is missing : + * * - Preblend for video overlay pre-scaling * - OSD2 support for cursor framebuffer * - Video pre-scaling before postblend diff --git a/drivers/gpu/drm/meson/meson_vpp.h b/drivers/gpu/drm/meson/meson_vpp.h index ede3b26..815177c 100644 --- a/drivers/gpu/drm/meson/meson_vpp.h +++ b/drivers/gpu/drm/meson/meson_vpp.h @@ -23,6 +23,8 @@ /* Mux VIU/VPP to ENCI */ #define MESON_VIU_VPP_MUX_ENCI 0x5 +/* Mux VIU/VPP to ENCP */ +#define MESON_VIU_VPP_MUX_ENCP 0xA void meson_vpp_setup_mux(struct meson_drm *priv, unsigned int mux); diff --git a/drivers/gpu/drm/mgag200/mgag200_mode.c b/drivers/gpu/drm/mgag200/mgag200_mode.c index f2e9b2b..adb411a 100644 --- a/drivers/gpu/drm/mgag200/mgag200_mode.c +++ b/drivers/gpu/drm/mgag200/mgag200_mode.c @@ -1393,7 +1393,8 @@ static void mga_crtc_commit(struct drm_crtc *crtc) * but it's a requirement that we provide the function */ static int mga_crtc_gamma_set(struct drm_crtc *crtc, u16 *red, u16 *green, - u16 *blue, uint32_t size) + u16 *blue, uint32_t size, + struct drm_modeset_acquire_ctx *ctx) { struct mga_crtc *mga_crtc = to_mga_crtc(crtc); int i; diff --git a/drivers/gpu/drm/msm/dsi/dsi_host.c b/drivers/gpu/drm/msm/dsi/dsi_host.c index 4f79b10..f97a780 100644 --- a/drivers/gpu/drm/msm/dsi/dsi_host.c +++ b/drivers/gpu/drm/msm/dsi/dsi_host.c @@ -1635,7 +1635,7 @@ static int dsi_host_parse_dt(struct msm_dsi_host *msm_host) } /* Get panel node from the output port's endpoint data */ - device_node = of_graph_get_remote_port_parent(endpoint); + device_node = of_graph_get_remote_node(np, 1, 0); if (!device_node) { dev_dbg(dev, "%s: no valid device\n", __func__); goto err; diff --git a/drivers/gpu/drm/msm/mdp/mdp4/mdp4_kms.c b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_kms.c index a4e1206..5b6516b 100644 --- a/drivers/gpu/drm/msm/mdp/mdp4/mdp4_kms.c +++ b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_kms.c @@ -225,32 +225,6 @@ int mdp4_enable(struct mdp4_kms *mdp4_kms) return 0; } -static struct device_node *mdp4_detect_lcdc_panel(struct drm_device *dev) -{ - struct device_node *endpoint, *panel_node; - struct device_node *np = dev->dev->of_node; - - /* - * LVDS/LCDC is the first port described in the list of ports in the - * MDP4 DT node. - */ - endpoint = of_graph_get_endpoint_by_regs(np, 0, -1); - if (!endpoint) { - DBG("no LVDS remote endpoint\n"); - return NULL; - } - - panel_node = of_graph_get_remote_port_parent(endpoint); - if (!panel_node) { - DBG("no valid panel node in LVDS endpoint\n"); - of_node_put(endpoint); - return NULL; - } - - of_node_put(endpoint); - - return panel_node; -} static int mdp4_modeset_init_intf(struct mdp4_kms *mdp4_kms, int intf_type) @@ -269,7 +243,7 @@ static int mdp4_modeset_init_intf(struct mdp4_kms *mdp4_kms, * bail out early if there is no panel node (no need to * initialize LCDC encoder and LVDS connector) */ - panel_node = mdp4_detect_lcdc_panel(dev); + panel_node = of_graph_get_remote_node(dev->dev->of_node, 0, 0); if (!panel_node) return 0; diff --git a/drivers/gpu/drm/mxsfb/mxsfb_out.c b/drivers/gpu/drm/mxsfb/mxsfb_out.c index b8e8142..f7d729a 100644 --- a/drivers/gpu/drm/mxsfb/mxsfb_out.c +++ b/drivers/gpu/drm/mxsfb/mxsfb_out.c @@ -19,6 +19,7 @@ #include <drm/drm_crtc_helper.h> #include <drm/drm_fb_cma_helper.h> #include <drm/drm_gem_cma_helper.h> +#include <drm/drm_of.h> #include <drm/drm_panel.h> #include <drm/drm_plane_helper.h> #include <drm/drm_simple_kms_helper.h> @@ -82,20 +83,15 @@ static const struct drm_connector_funcs mxsfb_panel_connector_funcs = { .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, }; -static int mxsfb_attach_endpoint(struct drm_device *drm, - const struct of_endpoint *ep) +int mxsfb_create_output(struct drm_device *drm) { struct mxsfb_drm_private *mxsfb = drm->dev_private; - struct device_node *np; struct drm_panel *panel; - int ret = -EPROBE_DEFER; - - np = of_graph_get_remote_port_parent(ep->local_node); - panel = of_drm_find_panel(np); - of_node_put(np); + int ret; - if (!panel) - return -EPROBE_DEFER; + ret = drm_of_find_panel_or_bridge(drm->dev->of_node, 0, 0, &panel, NULL); + if (ret) + return ret; mxsfb->connector.dpms = DRM_MODE_DPMS_OFF; mxsfb->connector.polled = 0; @@ -109,27 +105,3 @@ static int mxsfb_attach_endpoint(struct drm_device *drm, return ret; } - -int mxsfb_create_output(struct drm_device *drm) -{ - struct mxsfb_drm_private *mxsfb = drm->dev_private; - struct device_node *ep_np = NULL; - struct of_endpoint ep; - int ret; - - for_each_endpoint_of_node(drm->dev->of_node, ep_np) { - ret = of_graph_parse_endpoint(ep_np, &ep); - if (!ret) - ret = mxsfb_attach_endpoint(drm, &ep); - - if (ret) { - of_node_put(ep_np); - return ret; - } - } - - if (!mxsfb->panel) - return -EPROBE_DEFER; - - return 0; -} diff --git a/drivers/gpu/drm/nouveau/dispnv04/crtc.c b/drivers/gpu/drm/nouveau/dispnv04/crtc.c index 43ab560..4b4b0b4 100644 --- a/drivers/gpu/drm/nouveau/dispnv04/crtc.c +++ b/drivers/gpu/drm/nouveau/dispnv04/crtc.c @@ -788,7 +788,8 @@ nv_crtc_disable(struct drm_crtc *crtc) static int nv_crtc_gamma_set(struct drm_crtc *crtc, u16 *r, u16 *g, u16 *b, - uint32_t size) + uint32_t size, + struct drm_modeset_acquire_ctx *ctx) { struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); int i; diff --git a/drivers/gpu/drm/nouveau/nv50_display.c b/drivers/gpu/drm/nouveau/nv50_display.c index c9910c85..0e58537 100644 --- a/drivers/gpu/drm/nouveau/nv50_display.c +++ b/drivers/gpu/drm/nouveau/nv50_display.c @@ -2210,25 +2210,16 @@ nv50_head_lut_load(struct drm_crtc *crtc) } } -static int -nv50_head_mode_set_base_atomic(struct drm_crtc *crtc, - struct drm_framebuffer *fb, int x, int y, - enum mode_set_atomic state) -{ - WARN_ON(1); - return 0; -} - static const struct drm_crtc_helper_funcs nv50_head_help = { - .mode_set_base_atomic = nv50_head_mode_set_base_atomic, .load_lut = nv50_head_lut_load, .atomic_check = nv50_head_atomic_check, }; static int nv50_head_gamma_set(struct drm_crtc *crtc, u16 *r, u16 *g, u16 *b, - uint32_t size) + uint32_t size, + struct drm_modeset_acquire_ctx *ctx) { struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); u32 i; diff --git a/drivers/gpu/drm/omapdrm/dss/dpi.c b/drivers/gpu/drm/omapdrm/dss/dpi.c index 51d90a8..8a730a7 100644 --- a/drivers/gpu/drm/omapdrm/dss/dpi.c +++ b/drivers/gpu/drm/omapdrm/dss/dpi.c @@ -877,7 +877,7 @@ int dpi_init_port(struct platform_device *pdev, struct device_node *port) if (!dpi) return -ENOMEM; - ep = omapdss_of_get_next_endpoint(port, NULL); + ep = of_get_next_child(port, NULL); if (!ep) return 0; diff --git a/drivers/gpu/drm/omapdrm/dss/dsi.c b/drivers/gpu/drm/omapdrm/dss/dsi.c index f50d6fc..910754b 100644 --- a/drivers/gpu/drm/omapdrm/dss/dsi.c +++ b/drivers/gpu/drm/omapdrm/dss/dsi.c @@ -39,6 +39,7 @@ #include <linux/debugfs.h> #include <linux/pm_runtime.h> #include <linux/of.h> +#include <linux/of_graph.h> #include <linux/of_platform.h> #include <linux/component.h> @@ -5090,7 +5091,7 @@ static int dsi_probe_of(struct platform_device *pdev) struct device_node *ep; struct omap_dsi_pin_config pin_cfg; - ep = omapdss_of_get_first_endpoint(node); + ep = of_graph_get_endpoint_by_regs(node, 0, 0); if (!ep) return 0; diff --git a/drivers/gpu/drm/omapdrm/dss/dss-of.c b/drivers/gpu/drm/omapdrm/dss/dss-of.c index b46606b..c6b86f3 100644 --- a/drivers/gpu/drm/omapdrm/dss/dss-of.c +++ b/drivers/gpu/drm/omapdrm/dss/dss-of.c @@ -16,76 +16,11 @@ #include <linux/err.h> #include <linux/module.h> #include <linux/of.h> +#include <linux/of_graph.h> #include <linux/seq_file.h> #include "omapdss.h" -struct device_node * -omapdss_of_get_next_port(const struct device_node *parent, - struct device_node *prev) -{ - struct device_node *port = NULL; - - if (!parent) - return NULL; - - if (!prev) { - struct device_node *ports; - /* - * It's the first call, we have to find a port subnode - * within this node or within an optional 'ports' node. - */ - ports = of_get_child_by_name(parent, "ports"); - if (ports) - parent = ports; - - port = of_get_child_by_name(parent, "port"); - - /* release the 'ports' node */ - of_node_put(ports); - } else { - struct device_node *ports; - - ports = of_get_parent(prev); - if (!ports) - return NULL; - - do { - port = of_get_next_child(ports, prev); - if (!port) { - of_node_put(ports); - return NULL; - } - prev = port; - } while (of_node_cmp(port->name, "port") != 0); - - of_node_put(ports); - } - - return port; -} -EXPORT_SYMBOL_GPL(omapdss_of_get_next_port); - -struct device_node * -omapdss_of_get_next_endpoint(const struct device_node *parent, - struct device_node *prev) -{ - struct device_node *ep = NULL; - - if (!parent) - return NULL; - - do { - ep = of_get_next_child(parent, prev); - if (!ep) - return NULL; - prev = ep; - } while (of_node_cmp(ep->name, "endpoint") != 0); - - return ep; -} -EXPORT_SYMBOL_GPL(omapdss_of_get_next_endpoint); - struct device_node *dss_of_port_get_parent_device(struct device_node *port) { struct device_node *np; @@ -124,37 +59,6 @@ u32 dss_of_port_get_port_number(struct device_node *port) } EXPORT_SYMBOL_GPL(dss_of_port_get_port_number); -static struct device_node *omapdss_of_get_remote_port(const struct device_node *node) -{ - struct device_node *np; - - np = of_parse_phandle(node, "remote-endpoint", 0); - if (!np) - return NULL; - - np = of_get_next_parent(np); - - return np; -} - -struct device_node * -omapdss_of_get_first_endpoint(const struct device_node *parent) -{ - struct device_node *port, *ep; - - port = omapdss_of_get_next_port(parent, NULL); - - if (!port) - return NULL; - - ep = omapdss_of_get_next_endpoint(port, NULL); - - of_node_put(port); - - return ep; -} -EXPORT_SYMBOL_GPL(omapdss_of_get_first_endpoint); - struct omap_dss_device * omapdss_of_find_source_for_first_ep(struct device_node *node) { @@ -162,11 +66,11 @@ omapdss_of_find_source_for_first_ep(struct device_node *node) struct device_node *src_port; struct omap_dss_device *src; - ep = omapdss_of_get_first_endpoint(node); + ep = of_graph_get_endpoint_by_regs(node, 0, 0); if (!ep) return ERR_PTR(-EINVAL); - src_port = omapdss_of_get_remote_port(ep); + src_port = of_graph_get_remote_port(ep); if (!src_port) { of_node_put(ep); return ERR_PTR(-EINVAL); diff --git a/drivers/gpu/drm/omapdrm/dss/dss.c b/drivers/gpu/drm/omapdrm/dss/dss.c index ceb4836..fa99ec7 100644 --- a/drivers/gpu/drm/omapdrm/dss/dss.c +++ b/drivers/gpu/drm/omapdrm/dss/dss.c @@ -38,6 +38,7 @@ #include <linux/mfd/syscon.h> #include <linux/regmap.h> #include <linux/of.h> +#include <linux/of_graph.h> #include <linux/regulator/consumer.h> #include <linux/suspend.h> #include <linux/component.h> @@ -1035,32 +1036,14 @@ static int dss_init_ports(struct platform_device *pdev) { struct device_node *parent = pdev->dev.of_node; struct device_node *port; - int r; - - if (parent == NULL) - return 0; + int i; - port = omapdss_of_get_next_port(parent, NULL); - if (!port) - return 0; - - if (dss.feat->num_ports == 0) - return 0; - - do { - enum omap_display_type port_type; - u32 reg; - - r = of_property_read_u32(port, "reg", ®); - if (r) - reg = 0; - - if (reg >= dss.feat->num_ports) + for (i = 0; i < dss.feat->num_ports; i++) { + port = of_graph_get_port_by_id(parent, i); + if (!port) continue; - port_type = dss.feat->ports[reg]; - - switch (port_type) { + switch (dss.feat->ports[i]) { case OMAP_DISPLAY_TYPE_DPI: dpi_init_port(pdev, port); break; @@ -1070,7 +1053,7 @@ static int dss_init_ports(struct platform_device *pdev) default: break; } - } while ((port = omapdss_of_get_next_port(parent, port)) != NULL); + } return 0; } @@ -1079,32 +1062,14 @@ static void dss_uninit_ports(struct platform_device *pdev) { struct device_node *parent = pdev->dev.of_node; struct device_node *port; + int i; - if (parent == NULL) - return; - - port = omapdss_of_get_next_port(parent, NULL); - if (!port) - return; - - if (dss.feat->num_ports == 0) - return; - - do { - enum omap_display_type port_type; - u32 reg; - int r; - - r = of_property_read_u32(port, "reg", ®); - if (r) - reg = 0; - - if (reg >= dss.feat->num_ports) + for (i = 0; i < dss.feat->num_ports; i++) { + port = of_graph_get_port_by_id(parent, i); + if (!port) continue; - port_type = dss.feat->ports[reg]; - - switch (port_type) { + switch (dss.feat->ports[i]) { case OMAP_DISPLAY_TYPE_DPI: dpi_uninit_port(port); break; @@ -1114,7 +1079,7 @@ static void dss_uninit_ports(struct platform_device *pdev) default: break; } - } while ((port = omapdss_of_get_next_port(parent, port)) != NULL); + } } static int dss_video_pll_probe(struct platform_device *pdev) diff --git a/drivers/gpu/drm/omapdrm/dss/hdmi4.c b/drivers/gpu/drm/omapdrm/dss/hdmi4.c index e7162c1..87c5303 100644 --- a/drivers/gpu/drm/omapdrm/dss/hdmi4.c +++ b/drivers/gpu/drm/omapdrm/dss/hdmi4.c @@ -34,6 +34,7 @@ #include <linux/regulator/consumer.h> #include <linux/component.h> #include <linux/of.h> +#include <linux/of_graph.h> #include <sound/omap-hdmi-audio.h> #include "omapdss.h" @@ -546,7 +547,7 @@ static int hdmi_probe_of(struct platform_device *pdev) struct device_node *ep; int r; - ep = omapdss_of_get_first_endpoint(node); + ep = of_graph_get_endpoint_by_regs(node, 0, 0); if (!ep) return 0; diff --git a/drivers/gpu/drm/omapdrm/dss/hdmi5.c b/drivers/gpu/drm/omapdrm/dss/hdmi5.c index 678dfb0..d13dce7 100644 --- a/drivers/gpu/drm/omapdrm/dss/hdmi5.c +++ b/drivers/gpu/drm/omapdrm/dss/hdmi5.c @@ -39,6 +39,7 @@ #include <linux/regulator/consumer.h> #include <linux/component.h> #include <linux/of.h> +#include <linux/of_graph.h> #include <sound/omap-hdmi-audio.h> #include "omapdss.h" @@ -572,7 +573,7 @@ static int hdmi_probe_of(struct platform_device *pdev) struct device_node *ep; int r; - ep = omapdss_of_get_first_endpoint(node); + ep = of_graph_get_endpoint_by_regs(node, 0, 0); if (!ep) return 0; diff --git a/drivers/gpu/drm/omapdrm/dss/omapdss.h b/drivers/gpu/drm/omapdrm/dss/omapdss.h index 63c2684..b19dae1f 100644 --- a/drivers/gpu/drm/omapdrm/dss/omapdss.h +++ b/drivers/gpu/drm/omapdrm/dss/omapdss.h @@ -830,17 +830,6 @@ static inline bool omapdss_device_is_enabled(struct omap_dss_device *dssdev) return dssdev->state == OMAP_DSS_DISPLAY_ACTIVE; } -struct device_node * -omapdss_of_get_next_port(const struct device_node *parent, - struct device_node *prev); - -struct device_node * -omapdss_of_get_next_endpoint(const struct device_node *parent, - struct device_node *prev); - -struct device_node * -omapdss_of_get_first_endpoint(const struct device_node *parent); - struct omap_dss_device * omapdss_of_find_source_for_first_ep(struct device_node *node); diff --git a/drivers/gpu/drm/omapdrm/dss/sdi.c b/drivers/gpu/drm/omapdrm/dss/sdi.c index b3bda2d..0620b9f 100644 --- a/drivers/gpu/drm/omapdrm/dss/sdi.c +++ b/drivers/gpu/drm/omapdrm/dss/sdi.c @@ -414,7 +414,7 @@ int sdi_init_port(struct platform_device *pdev, struct device_node *port) u32 datapairs; int r; - ep = omapdss_of_get_next_endpoint(port, NULL); + ep = of_get_next_child(port, NULL); if (!ep) return 0; diff --git a/drivers/gpu/drm/omapdrm/dss/venc.c b/drivers/gpu/drm/omapdrm/dss/venc.c index d74f7fc..19d1495 100644 --- a/drivers/gpu/drm/omapdrm/dss/venc.c +++ b/drivers/gpu/drm/omapdrm/dss/venc.c @@ -35,6 +35,7 @@ #include <linux/regulator/consumer.h> #include <linux/pm_runtime.h> #include <linux/of.h> +#include <linux/of_graph.h> #include <linux/component.h> #include "omapdss.h" @@ -818,7 +819,7 @@ static int venc_probe_of(struct platform_device *pdev) u32 channels; int r; - ep = omapdss_of_get_first_endpoint(node); + ep = of_graph_get_endpoint_by_regs(node, 0, 0); if (!ep) return 0; diff --git a/drivers/gpu/drm/radeon/radeon_display.c b/drivers/gpu/drm/radeon/radeon_display.c index e765a63..17d3daf 100644 --- a/drivers/gpu/drm/radeon/radeon_display.c +++ b/drivers/gpu/drm/radeon/radeon_display.c @@ -232,7 +232,8 @@ void radeon_crtc_fb_gamma_get(struct drm_crtc *crtc, u16 *red, u16 *green, } static int radeon_crtc_gamma_set(struct drm_crtc *crtc, u16 *red, u16 *green, - u16 *blue, uint32_t size) + u16 *blue, uint32_t size, + struct drm_modeset_acquire_ctx *ctx) { struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc); int i; diff --git a/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c b/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c index 91ebe5c..d8fa7a9 100644 --- a/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c +++ b/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c @@ -417,7 +417,8 @@ static void rockchip_dp_unbind(struct device *dev, struct device *master, rockchip_drm_psr_unregister(&dp->encoder); - return analogix_dp_unbind(dev, master, data); + analogix_dp_unbind(dev, master, data); + clk_disable_unprepare(dp->pclk); } static const struct component_ops rockchip_dp_component_ops = { @@ -428,31 +429,13 @@ static const struct component_ops rockchip_dp_component_ops = { static int rockchip_dp_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; - struct device_node *panel_node, *port, *endpoint; struct drm_panel *panel = NULL; struct rockchip_dp_device *dp; + int ret; - port = of_graph_get_port_by_id(dev->of_node, 1); - if (port) { - endpoint = of_get_child_by_name(port, "endpoint"); - of_node_put(port); - if (!endpoint) { - dev_err(dev, "no output endpoint found\n"); - return -EINVAL; - } - - panel_node = of_graph_get_remote_port_parent(endpoint); - of_node_put(endpoint); - if (!panel_node) { - dev_err(dev, "no output node found\n"); - return -EINVAL; - } - - panel = of_drm_find_panel(panel_node); - of_node_put(panel_node); - if (!panel) - return -EPROBE_DEFER; - } + ret = drm_of_find_panel_or_bridge(dev->of_node, 1, 0, &panel, NULL); + if (ret) + return ret; dp = devm_kzalloc(dev, sizeof(*dp), GFP_KERNEL); if (!dp) diff --git a/drivers/gpu/drm/rockchip/cdn-dp-core.c b/drivers/gpu/drm/rockchip/cdn-dp-core.c index 4e55d63..a2169dd 100644 --- a/drivers/gpu/drm/rockchip/cdn-dp-core.c +++ b/drivers/gpu/drm/rockchip/cdn-dp-core.c @@ -1053,6 +1053,7 @@ static int cdn_dp_bind(struct device *dev, struct device *master, void *data) dp->connected = false; dp->active = false; dp->active_port = -1; + dp->fw_loaded = false; INIT_WORK(&dp->event_work, cdn_dp_pd_event_work); @@ -1091,8 +1092,6 @@ static int cdn_dp_bind(struct device *dev, struct device *master, void *data) goto err_free_connector; } - cdn_dp_audio_codec_init(dp, dev); - for (i = 0; i < dp->ports; i++) { port = dp->port[i]; @@ -1127,13 +1126,13 @@ static void cdn_dp_unbind(struct device *dev, struct device *master, void *data) struct drm_connector *connector = &dp->connector; cancel_work_sync(&dp->event_work); - platform_device_unregister(dp->audio_pdev); cdn_dp_encoder_disable(encoder); encoder->funcs->destroy(encoder); connector->funcs->destroy(connector); pm_runtime_disable(dev); - release_firmware(dp->fw); + if (dp->fw_loaded) + release_firmware(dp->fw); kfree(dp->edid); dp->edid = NULL; } @@ -1219,6 +1218,8 @@ static int cdn_dp_probe(struct platform_device *pdev) mutex_init(&dp->lock); dev_set_drvdata(dev, dp); + cdn_dp_audio_codec_init(dp, dev); + return component_add(dev, &cdn_dp_component_ops); } @@ -1226,6 +1227,7 @@ static int cdn_dp_remove(struct platform_device *pdev) { struct cdn_dp_device *dp = platform_get_drvdata(pdev); + platform_device_unregister(dp->audio_pdev); cdn_dp_suspend(dp->dev); component_del(&pdev->dev, &cdn_dp_component_ops); diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_drv.c b/drivers/gpu/drm/rockchip/rockchip_drm_drv.c index cd7d02e1..c6b1b7f 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_drv.c +++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.c @@ -136,21 +136,24 @@ static int rockchip_drm_bind(struct device *dev) INIT_LIST_HEAD(&private->psr_list); spin_lock_init(&private->psr_list_lock); + ret = rockchip_drm_init_iommu(drm_dev); + if (ret) + goto err_free; + drm_mode_config_init(drm_dev); rockchip_drm_mode_config_init(drm_dev); - ret = rockchip_drm_init_iommu(drm_dev); - if (ret) - goto err_config_cleanup; - /* Try to bind all sub drivers. */ ret = component_bind_all(dev, drm_dev); if (ret) - goto err_iommu_cleanup; + goto err_mode_config_cleanup; - /* init kms poll for handling hpd */ - drm_kms_helper_poll_init(drm_dev); + ret = drm_vblank_init(drm_dev, drm_dev->mode_config.num_crtc); + if (ret) + goto err_unbind_all; + + drm_mode_config_reset(drm_dev); /* * enable drm irq mode. @@ -158,15 +161,12 @@ static int rockchip_drm_bind(struct device *dev) */ drm_dev->irq_enabled = true; - ret = drm_vblank_init(drm_dev, ROCKCHIP_MAX_CRTC); - if (ret) - goto err_kms_helper_poll_fini; - - drm_mode_config_reset(drm_dev); + /* init kms poll for handling hpd */ + drm_kms_helper_poll_init(drm_dev); ret = rockchip_drm_fbdev_init(drm_dev); if (ret) - goto err_vblank_cleanup; + goto err_kms_helper_poll_fini; ret = drm_dev_register(drm_dev, 0); if (ret) @@ -175,17 +175,17 @@ static int rockchip_drm_bind(struct device *dev) return 0; err_fbdev_fini: rockchip_drm_fbdev_fini(drm_dev); -err_vblank_cleanup: - drm_vblank_cleanup(drm_dev); err_kms_helper_poll_fini: drm_kms_helper_poll_fini(drm_dev); + drm_vblank_cleanup(drm_dev); +err_unbind_all: component_unbind_all(dev, drm_dev); -err_iommu_cleanup: - rockchip_iommu_cleanup(drm_dev); -err_config_cleanup: +err_mode_config_cleanup: drm_mode_config_cleanup(drm_dev); - drm_dev->dev_private = NULL; + rockchip_iommu_cleanup(drm_dev); err_free: + drm_dev->dev_private = NULL; + dev_set_drvdata(dev, NULL); drm_dev_unref(drm_dev); return ret; } @@ -194,16 +194,20 @@ static void rockchip_drm_unbind(struct device *dev) { struct drm_device *drm_dev = dev_get_drvdata(dev); + drm_dev_unregister(drm_dev); + rockchip_drm_fbdev_fini(drm_dev); - drm_vblank_cleanup(drm_dev); drm_kms_helper_poll_fini(drm_dev); + + drm_atomic_helper_shutdown(drm_dev); + drm_vblank_cleanup(drm_dev); component_unbind_all(dev, drm_dev); - rockchip_iommu_cleanup(drm_dev); drm_mode_config_cleanup(drm_dev); + rockchip_iommu_cleanup(drm_dev); + drm_dev->dev_private = NULL; - drm_dev_unregister(drm_dev); - drm_dev_unref(drm_dev); dev_set_drvdata(dev, NULL); + drm_dev_unref(drm_dev); } static void rockchip_drm_lastclose(struct drm_device *dev) diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c index 2151e1c..3f7a82d 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c +++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c @@ -506,7 +506,7 @@ static int vop_enable(struct drm_crtc *crtc) ret = pm_runtime_get_sync(vop->dev); if (ret < 0) { dev_err(vop->dev, "failed to get pm runtime: %d\n", ret); - goto err_put_pm_runtime; + return ret; } ret = clk_enable(vop->hclk); @@ -1405,10 +1405,16 @@ static int vop_initial(struct vop *vop) return PTR_ERR(vop->dclk); } + ret = pm_runtime_get_sync(vop->dev); + if (ret < 0) { + dev_err(vop->dev, "failed to get pm runtime: %d\n", ret); + return ret; + } + ret = clk_prepare(vop->dclk); if (ret < 0) { dev_err(vop->dev, "failed to prepare dclk\n"); - return ret; + goto err_put_pm_runtime; } /* Enable both the hclk and aclk to setup the vop */ @@ -1468,6 +1474,8 @@ static int vop_initial(struct vop *vop) vop->is_enabled = false; + pm_runtime_put_sync(vop->dev); + return 0; err_disable_aclk: @@ -1476,6 +1484,8 @@ err_disable_hclk: clk_disable_unprepare(vop->hclk); err_unprepare_dclk: clk_unprepare(vop->dclk); +err_put_pm_runtime: + pm_runtime_put_sync(vop->dev); return ret; } @@ -1576,12 +1586,6 @@ static int vop_bind(struct device *dev, struct device *master, void *data) if (!vop->regsbak) return -ENOMEM; - ret = vop_initial(vop); - if (ret < 0) { - dev_err(&pdev->dev, "cannot initial vop dev - err %d\n", ret); - return ret; - } - irq = platform_get_irq(pdev, 0); if (irq < 0) { dev_err(dev, "cannot find irq for vop\n"); @@ -1608,8 +1612,17 @@ static int vop_bind(struct device *dev, struct device *master, void *data) pm_runtime_enable(&pdev->dev); + ret = vop_initial(vop); + if (ret < 0) { + dev_err(&pdev->dev, "cannot initial vop dev - err %d\n", ret); + goto err_disable_pm_runtime; + } + return 0; +err_disable_pm_runtime: + pm_runtime_disable(&pdev->dev); + vop_destroy_crtc(vop); err_enable_irq: enable_irq(vop->irq); /* To balance out the disable_irq above */ return ret; @@ -1621,6 +1634,10 @@ static void vop_unbind(struct device *dev, struct device *master, void *data) pm_runtime_disable(dev); vop_destroy_crtc(vop); + + clk_unprepare(vop->aclk); + clk_unprepare(vop->hclk); + clk_unprepare(vop->dclk); } const struct component_ops vop_component_ops = { diff --git a/drivers/gpu/drm/sun4i/sun4i_rgb.c b/drivers/gpu/drm/sun4i/sun4i_rgb.c index 757208f..46280dd 100644 --- a/drivers/gpu/drm/sun4i/sun4i_rgb.c +++ b/drivers/gpu/drm/sun4i/sun4i_rgb.c @@ -15,6 +15,7 @@ #include <drm/drmP.h> #include <drm/drm_atomic_helper.h> #include <drm/drm_crtc_helper.h> +#include <drm/drm_of.h> #include <drm/drm_panel.h> #include "sun4i_drv.h" @@ -218,9 +219,9 @@ int sun4i_rgb_init(struct drm_device *drm) rgb->drv = drv; encoder = &rgb->encoder; - tcon->panel = sun4i_tcon_find_panel(tcon->dev->of_node); - bridge = sun4i_tcon_find_bridge(tcon->dev->of_node); - if (IS_ERR(tcon->panel) && IS_ERR(bridge)) { + ret = drm_of_find_panel_or_bridge(tcon->dev->of_node, 1, 0, + &tcon->panel, &bridge); + if (ret) { dev_info(drm->dev, "No panel or bridge found... RGB output disabled\n"); return 0; } @@ -240,7 +241,7 @@ int sun4i_rgb_init(struct drm_device *drm) /* The RGB encoder can only work with the TCON channel 0 */ rgb->encoder.possible_crtcs = BIT(0); - if (!IS_ERR(tcon->panel)) { + if (tcon->panel) { drm_connector_helper_add(&rgb->connector, &sun4i_rgb_con_helper_funcs); ret = drm_connector_init(drm, &rgb->connector, @@ -261,7 +262,7 @@ int sun4i_rgb_init(struct drm_device *drm) } } - if (!IS_ERR(bridge)) { + if (bridge) { ret = drm_bridge_attach(encoder, bridge, NULL); if (ret) { dev_err(drm->dev, "Couldn't attach our bridge\n"); diff --git a/drivers/gpu/drm/sun4i/sun4i_tcon.c b/drivers/gpu/drm/sun4i/sun4i_tcon.c index ea2906f..2e4e365 100644 --- a/drivers/gpu/drm/sun4i/sun4i_tcon.c +++ b/drivers/gpu/drm/sun4i/sun4i_tcon.c @@ -15,13 +15,12 @@ #include <drm/drm_crtc.h> #include <drm/drm_crtc_helper.h> #include <drm/drm_modes.h> -#include <drm/drm_panel.h> +#include <drm/drm_of.h> #include <linux/component.h> #include <linux/ioport.h> #include <linux/of_address.h> #include <linux/of_device.h> -#include <linux/of_graph.h> #include <linux/of_irq.h> #include <linux/regmap.h> #include <linux/reset.h> @@ -405,74 +404,6 @@ static int sun4i_tcon_init_regmap(struct device *dev, return 0; } -struct drm_panel *sun4i_tcon_find_panel(struct device_node *node) -{ - struct device_node *port, *remote, *child; - struct device_node *end_node = NULL; - - /* Inputs are listed first, then outputs */ - port = of_graph_get_port_by_id(node, 1); - - /* - * Our first output is the RGB interface where the panel will - * be connected. - */ - for_each_child_of_node(port, child) { - u32 reg; - - of_property_read_u32(child, "reg", ®); - if (reg == 0) - end_node = child; - } - - if (!end_node) { - DRM_DEBUG_DRIVER("Missing panel endpoint\n"); - return ERR_PTR(-ENODEV); - } - - remote = of_graph_get_remote_port_parent(end_node); - if (!remote) { - DRM_DEBUG_DRIVER("Unable to parse remote node\n"); - return ERR_PTR(-EINVAL); - } - - return of_drm_find_panel(remote) ?: ERR_PTR(-EPROBE_DEFER); -} - -struct drm_bridge *sun4i_tcon_find_bridge(struct device_node *node) -{ - struct device_node *port, *remote, *child; - struct device_node *end_node = NULL; - - /* Inputs are listed first, then outputs */ - port = of_graph_get_port_by_id(node, 1); - - /* - * Our first output is the RGB interface where the panel will - * be connected. - */ - for_each_child_of_node(port, child) { - u32 reg; - - of_property_read_u32(child, "reg", ®); - if (reg == 0) - end_node = child; - } - - if (!end_node) { - DRM_DEBUG_DRIVER("Missing bridge endpoint\n"); - return ERR_PTR(-ENODEV); - } - - remote = of_graph_get_remote_port_parent(end_node); - if (!remote) { - DRM_DEBUG_DRIVER("Enable to parse remote node\n"); - return ERR_PTR(-EINVAL); - } - - return of_drm_find_bridge(remote) ?: ERR_PTR(-EPROBE_DEFER); -} - static int sun4i_tcon_bind(struct device *dev, struct device *master, void *data) { @@ -555,22 +486,11 @@ static int sun4i_tcon_probe(struct platform_device *pdev) struct device_node *node = pdev->dev.of_node; struct drm_bridge *bridge; struct drm_panel *panel; + int ret; - /* - * Neither the bridge or the panel is ready. - * Defer the probe. - */ - panel = sun4i_tcon_find_panel(node); - bridge = sun4i_tcon_find_bridge(node); - - /* - * If we don't have a panel endpoint, just go on - */ - if ((PTR_ERR(panel) == -EPROBE_DEFER) && - (PTR_ERR(bridge) == -EPROBE_DEFER)) { - DRM_DEBUG_DRIVER("Still waiting for our panel/bridge. Deferring...\n"); - return -EPROBE_DEFER; - } + ret = drm_of_find_panel_or_bridge(node, 1, 0, &panel, &bridge); + if (ret == -EPROBE_DEFER) + return ret; return component_add(&pdev->dev, &sun4i_tcon_ops); } diff --git a/drivers/gpu/drm/tilcdc/tilcdc_crtc.c b/drivers/gpu/drm/tilcdc/tilcdc_crtc.c index afd2a7b..d524ed0 100644 --- a/drivers/gpu/drm/tilcdc/tilcdc_crtc.c +++ b/drivers/gpu/drm/tilcdc/tilcdc_crtc.c @@ -23,6 +23,7 @@ #include <linux/workqueue.h> #include <linux/completion.h> #include <linux/dma-mapping.h> +#include <linux/of_graph.h> #include "tilcdc_drv.h" #include "tilcdc_regs.h" @@ -1035,16 +1036,7 @@ int tilcdc_crtc_create(struct drm_device *dev) drm_crtc_helper_add(crtc, &tilcdc_crtc_helper_funcs); if (priv->is_componentized) { - struct device_node *ports = - of_get_child_by_name(dev->dev->of_node, "ports"); - - if (ports) { - crtc->port = of_get_child_by_name(ports, "port"); - of_node_put(ports); - } else { - crtc->port = - of_get_child_by_name(dev->dev->of_node, "port"); - } + crtc->port = of_graph_get_port_by_id(dev->dev->of_node, 0); if (!crtc->port) { /* This should never happen */ dev_err(dev->dev, "Port node not found in %s\n", dev->dev->of_node->full_name); diff --git a/drivers/gpu/drm/tilcdc/tilcdc_external.c b/drivers/gpu/drm/tilcdc/tilcdc_external.c index b0dd5e8..711c7b3 100644 --- a/drivers/gpu/drm/tilcdc/tilcdc_external.c +++ b/drivers/gpu/drm/tilcdc/tilcdc_external.c @@ -185,39 +185,6 @@ int tilcdc_attach_bridge(struct drm_device *ddev, struct drm_bridge *bridge) return ret; } -static int tilcdc_node_has_port(struct device_node *dev_node) -{ - struct device_node *node; - - node = of_get_child_by_name(dev_node, "ports"); - if (!node) - node = of_get_child_by_name(dev_node, "port"); - if (!node) - return 0; - of_node_put(node); - - return 1; -} - -static -struct device_node *tilcdc_get_remote_node(struct device_node *node) -{ - struct device_node *ep; - struct device_node *parent; - - if (!tilcdc_node_has_port(node)) - return NULL; - - ep = of_graph_get_next_endpoint(node, NULL); - if (!ep) - return NULL; - - parent = of_graph_get_remote_port_parent(ep); - of_node_put(ep); - - return parent; -} - int tilcdc_attach_external_device(struct drm_device *ddev) { struct tilcdc_drm_private *priv = ddev->dev_private; @@ -225,7 +192,7 @@ int tilcdc_attach_external_device(struct drm_device *ddev) struct drm_bridge *bridge; int ret; - remote_node = tilcdc_get_remote_node(ddev->dev->of_node); + remote_node = of_graph_get_remote_node(ddev->dev->of_node, 0, 0); if (!remote_node) return 0; @@ -264,35 +231,16 @@ int tilcdc_get_external_components(struct device *dev, struct component_match **match) { struct device_node *node; - struct device_node *ep = NULL; - int count = 0; - int ret = 0; - - if (!tilcdc_node_has_port(dev->of_node)) - return 0; - while ((ep = of_graph_get_next_endpoint(dev->of_node, ep))) { - node = of_graph_get_remote_port_parent(ep); - if (!node || !of_device_is_available(node)) { - of_node_put(node); - continue; - } - - dev_dbg(dev, "Subdevice node '%s' found\n", node->name); - - if (of_device_is_compatible(node, "nxp,tda998x")) { - if (match) - drm_of_component_match_add(dev, match, - dev_match_of, node); - ret = 1; - } + node = of_graph_get_remote_node(dev->of_node, 0, 0); + if (!of_device_is_compatible(node, "nxp,tda998x")) { of_node_put(node); - if (count++ > 1) { - dev_err(dev, "Only one port is supported\n"); - return -EINVAL; - } + return 0; } - return ret; + if (match) + drm_of_component_match_add(dev, match, dev_match_of, node); + of_node_put(node); + return 1; } diff --git a/drivers/gpu/drm/vc4/vc4_crtc.c b/drivers/gpu/drm/vc4/vc4_crtc.c index e8f28c9..d86c8cc 100644 --- a/drivers/gpu/drm/vc4/vc4_crtc.c +++ b/drivers/gpu/drm/vc4/vc4_crtc.c @@ -314,7 +314,8 @@ vc4_crtc_lut_load(struct drm_crtc *crtc) static int vc4_crtc_gamma_set(struct drm_crtc *crtc, u16 *r, u16 *g, u16 *b, - uint32_t size) + uint32_t size, + struct drm_modeset_acquire_ctx *ctx) { struct vc4_crtc *vc4_crtc = to_vc4_crtc(crtc); u32 i; diff --git a/drivers/gpu/drm/vc4/vc4_dpi.c b/drivers/gpu/drm/vc4/vc4_dpi.c index 7143579..c6d7039 100644 --- a/drivers/gpu/drm/vc4/vc4_dpi.c +++ b/drivers/gpu/drm/vc4/vc4_dpi.c @@ -356,23 +356,14 @@ static const struct of_device_id vc4_dpi_dt_match[] = { */ static struct drm_panel *vc4_dpi_get_panel(struct device *dev) { - struct device_node *endpoint, *panel_node; + struct device_node *panel_node; struct device_node *np = dev->of_node; struct drm_panel *panel; - endpoint = of_graph_get_next_endpoint(np, NULL); - if (!endpoint) { - dev_err(dev, "no endpoint to fetch DPI panel\n"); - return NULL; - } - /* don't proceed if we have an endpoint but no panel_node tied to it */ - panel_node = of_graph_get_remote_port_parent(endpoint); - of_node_put(endpoint); - if (!panel_node) { - dev_err(dev, "no valid panel node\n"); + panel_node = of_graph_get_remote_node(np, 0, 0); + if (!panel_node) return NULL; - } panel = of_drm_find_panel(panel_node); of_node_put(panel_node); diff --git a/drivers/gpu/drm/virtio/virtgpu_drv.h b/drivers/gpu/drm/virtio/virtgpu_drv.h index 93900a8..1328185 100644 --- a/drivers/gpu/drm/virtio/virtgpu_drv.h +++ b/drivers/gpu/drm/virtio/virtgpu_drv.h @@ -334,6 +334,7 @@ int virtio_gpu_modeset_init(struct virtio_gpu_device *vgdev); void virtio_gpu_modeset_fini(struct virtio_gpu_device *vgdev); /* virtio_gpu_plane.c */ +uint32_t virtio_gpu_translate_format(uint32_t drm_fourcc); struct drm_plane *virtio_gpu_plane_init(struct virtio_gpu_device *vgdev, enum drm_plane_type type, int index); diff --git a/drivers/gpu/drm/virtio/virtgpu_fb.c b/drivers/gpu/drm/virtio/virtgpu_fb.c index 9bfaef3..33df067 100644 --- a/drivers/gpu/drm/virtio/virtgpu_fb.c +++ b/drivers/gpu/drm/virtio/virtgpu_fb.c @@ -231,63 +231,9 @@ static int virtio_gpufb_create(struct drm_fb_helper *helper, mode_cmd.pitches[0] = mode_cmd.width * 4; mode_cmd.pixel_format = drm_mode_legacy_fb_format(32, 24); - switch (mode_cmd.pixel_format) { -#ifdef __BIG_ENDIAN - case DRM_FORMAT_XRGB8888: - format = VIRTIO_GPU_FORMAT_X8R8G8B8_UNORM; - break; - case DRM_FORMAT_ARGB8888: - format = VIRTIO_GPU_FORMAT_A8R8G8B8_UNORM; - break; - case DRM_FORMAT_BGRX8888: - format = VIRTIO_GPU_FORMAT_B8G8R8X8_UNORM; - break; - case DRM_FORMAT_BGRA8888: - format = VIRTIO_GPU_FORMAT_B8G8R8A8_UNORM; - break; - case DRM_FORMAT_RGBX8888: - format = VIRTIO_GPU_FORMAT_R8G8B8X8_UNORM; - break; - case DRM_FORMAT_RGBA8888: - format = VIRTIO_GPU_FORMAT_R8G8B8A8_UNORM; - break; - case DRM_FORMAT_XBGR8888: - format = VIRTIO_GPU_FORMAT_X8B8G8R8_UNORM; - break; - case DRM_FORMAT_ABGR8888: - format = VIRTIO_GPU_FORMAT_A8B8G8R8_UNORM; - break; -#else - case DRM_FORMAT_XRGB8888: - format = VIRTIO_GPU_FORMAT_B8G8R8X8_UNORM; - break; - case DRM_FORMAT_ARGB8888: - format = VIRTIO_GPU_FORMAT_B8G8R8A8_UNORM; - break; - case DRM_FORMAT_BGRX8888: - format = VIRTIO_GPU_FORMAT_X8R8G8B8_UNORM; - break; - case DRM_FORMAT_BGRA8888: - format = VIRTIO_GPU_FORMAT_A8R8G8B8_UNORM; - break; - case DRM_FORMAT_RGBX8888: - format = VIRTIO_GPU_FORMAT_X8B8G8R8_UNORM; - break; - case DRM_FORMAT_RGBA8888: - format = VIRTIO_GPU_FORMAT_A8B8G8R8_UNORM; - break; - case DRM_FORMAT_XBGR8888: - format = VIRTIO_GPU_FORMAT_R8G8B8X8_UNORM; - break; - case DRM_FORMAT_ABGR8888: - format = VIRTIO_GPU_FORMAT_R8G8B8A8_UNORM; - break; -#endif - default: - DRM_ERROR("failed to find virtio gpu format for %d\n", - mode_cmd.pixel_format); + format = virtio_gpu_translate_format(mode_cmd.pixel_format); + if (format == 0) return -EINVAL; - } size = mode_cmd.pitches[0] * mode_cmd.height; obj = virtio_gpu_alloc_object(dev, size, false, true); diff --git a/drivers/gpu/drm/virtio/virtgpu_gem.c b/drivers/gpu/drm/virtio/virtgpu_gem.c index 336a57f..cc025d8 100644 --- a/drivers/gpu/drm/virtio/virtgpu_gem.c +++ b/drivers/gpu/drm/virtio/virtgpu_gem.c @@ -88,6 +88,7 @@ int virtio_gpu_mode_dumb_create(struct drm_file *file_priv, int ret; uint32_t pitch; uint32_t resid; + uint32_t format; pitch = args->width * ((args->bpp + 1) / 8); args->size = pitch * args->height; @@ -98,9 +99,10 @@ int virtio_gpu_mode_dumb_create(struct drm_file *file_priv, if (ret) goto fail; + format = virtio_gpu_translate_format(DRM_FORMAT_XRGB8888); virtio_gpu_resource_id_get(vgdev, &resid); - virtio_gpu_cmd_create_resource(vgdev, resid, - 2, args->width, args->height); + virtio_gpu_cmd_create_resource(vgdev, resid, format, + args->width, args->height); /* attach the object to the resource */ obj = gem_to_virtio_gpu_obj(gobj); diff --git a/drivers/gpu/drm/virtio/virtgpu_object.c b/drivers/gpu/drm/virtio/virtgpu_object.c index 1483dae..6f66b73 100644 --- a/drivers/gpu/drm/virtio/virtgpu_object.c +++ b/drivers/gpu/drm/virtio/virtgpu_object.c @@ -81,8 +81,10 @@ int virtio_gpu_object_create(struct virtio_gpu_device *vgdev, return -ENOMEM; size = roundup(size, PAGE_SIZE); ret = drm_gem_object_init(vgdev->ddev, &bo->gem_base, size); - if (ret != 0) + if (ret != 0) { + kfree(bo); return ret; + } bo->dumb = false; virtio_gpu_init_ttm_placement(bo, pinned); diff --git a/drivers/gpu/drm/virtio/virtgpu_plane.c b/drivers/gpu/drm/virtio/virtgpu_plane.c index 1ff9c64..adcdbd0 100644 --- a/drivers/gpu/drm/virtio/virtgpu_plane.c +++ b/drivers/gpu/drm/virtio/virtgpu_plane.c @@ -39,9 +39,81 @@ static const uint32_t virtio_gpu_formats[] = { }; static const uint32_t virtio_gpu_cursor_formats[] = { +#ifdef __BIG_ENDIAN + DRM_FORMAT_BGRA8888, +#else DRM_FORMAT_ARGB8888, +#endif }; +uint32_t virtio_gpu_translate_format(uint32_t drm_fourcc) +{ + uint32_t format; + + switch (drm_fourcc) { +#ifdef __BIG_ENDIAN + case DRM_FORMAT_XRGB8888: + format = VIRTIO_GPU_FORMAT_X8R8G8B8_UNORM; + break; + case DRM_FORMAT_ARGB8888: + format = VIRTIO_GPU_FORMAT_A8R8G8B8_UNORM; + break; + case DRM_FORMAT_BGRX8888: + format = VIRTIO_GPU_FORMAT_B8G8R8X8_UNORM; + break; + case DRM_FORMAT_BGRA8888: + format = VIRTIO_GPU_FORMAT_B8G8R8A8_UNORM; + break; + case DRM_FORMAT_RGBX8888: + format = VIRTIO_GPU_FORMAT_R8G8B8X8_UNORM; + break; + case DRM_FORMAT_RGBA8888: + format = VIRTIO_GPU_FORMAT_R8G8B8A8_UNORM; + break; + case DRM_FORMAT_XBGR8888: + format = VIRTIO_GPU_FORMAT_X8B8G8R8_UNORM; + break; + case DRM_FORMAT_ABGR8888: + format = VIRTIO_GPU_FORMAT_A8B8G8R8_UNORM; + break; +#else + case DRM_FORMAT_XRGB8888: + format = VIRTIO_GPU_FORMAT_B8G8R8X8_UNORM; + break; + case DRM_FORMAT_ARGB8888: + format = VIRTIO_GPU_FORMAT_B8G8R8A8_UNORM; + break; + case DRM_FORMAT_BGRX8888: + format = VIRTIO_GPU_FORMAT_X8R8G8B8_UNORM; + break; + case DRM_FORMAT_BGRA8888: + format = VIRTIO_GPU_FORMAT_A8R8G8B8_UNORM; + break; + case DRM_FORMAT_RGBX8888: + format = VIRTIO_GPU_FORMAT_X8B8G8R8_UNORM; + break; + case DRM_FORMAT_RGBA8888: + format = VIRTIO_GPU_FORMAT_A8B8G8R8_UNORM; + break; + case DRM_FORMAT_XBGR8888: + format = VIRTIO_GPU_FORMAT_R8G8B8X8_UNORM; + break; + case DRM_FORMAT_ABGR8888: + format = VIRTIO_GPU_FORMAT_R8G8B8A8_UNORM; + break; +#endif + default: + /* + * This should not happen, we handle everything listed + * in virtio_gpu_formats[]. + */ + format = 0; + break; + } + WARN_ON(format == 0); + return format; +} + static void virtio_gpu_plane_destroy(struct drm_plane *plane) { drm_plane_cleanup(plane); diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_fb.c b/drivers/gpu/drm/vmwgfx/vmwgfx_fb.c index 09e120d..6f4cb46 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_fb.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_fb.c @@ -418,6 +418,60 @@ static int vmw_fb_compute_depth(struct fb_var_screeninfo *var, return 0; } +static int vmwgfx_set_config_internal(struct drm_mode_set *set) +{ + struct drm_crtc *crtc = set->crtc; + struct drm_framebuffer *fb; + struct drm_crtc *tmp; + struct drm_modeset_acquire_ctx *ctx; + struct drm_device *dev = set->crtc->dev; + int ret; + + ctx = dev->mode_config.acquire_ctx; + +restart: + /* + * NOTE: ->set_config can also disable other crtcs (if we steal all + * connectors from it), hence we need to refcount the fbs across all + * crtcs. Atomic modeset will have saner semantics ... + */ + drm_for_each_crtc(tmp, dev) + tmp->primary->old_fb = tmp->primary->fb; + + fb = set->fb; + + ret = crtc->funcs->set_config(set, ctx); + if (ret == 0) { + crtc->primary->crtc = crtc; + crtc->primary->fb = fb; + } + + drm_for_each_crtc(tmp, dev) { + if (tmp->primary->fb) + drm_framebuffer_get(tmp->primary->fb); + if (tmp->primary->old_fb) + drm_framebuffer_put(tmp->primary->old_fb); + tmp->primary->old_fb = NULL; + } + + if (ret == -EDEADLK) { + dev->mode_config.acquire_ctx = NULL; + +retry_locking: + drm_modeset_backoff(ctx); + + ret = drm_modeset_lock_all_ctx(dev, ctx); + if (ret) + goto retry_locking; + + dev->mode_config.acquire_ctx = ctx; + + goto restart; + } + + return ret; +} + static int vmw_fb_kms_detach(struct vmw_fb_par *par, bool detach_bo, bool unref_bo) @@ -436,7 +490,7 @@ static int vmw_fb_kms_detach(struct vmw_fb_par *par, set.fb = NULL; set.num_connectors = 0; set.connectors = &par->con; - ret = drm_mode_set_config_internal(&set); + ret = vmwgfx_set_config_internal(&set); if (ret) { DRM_ERROR("Could not unset a mode.\n"); return ret; @@ -578,7 +632,7 @@ static int vmw_fb_set_par(struct fb_info *info) set.num_connectors = 1; set.connectors = &par->con; - ret = drm_mode_set_config_internal(&set); + ret = vmwgfx_set_config_internal(&set); if (ret) goto out_unlock; diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c index 6078654..ef9f3a2 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c @@ -2026,7 +2026,8 @@ static int vmw_du_update_layout(struct vmw_private *dev_priv, unsigned num, int vmw_du_crtc_gamma_set(struct drm_crtc *crtc, u16 *r, u16 *g, u16 *b, - uint32_t size) + uint32_t size, + struct drm_modeset_acquire_ctx *ctx) { struct vmw_private *dev_priv = vmw_priv(crtc->dev); int i; diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.h b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.h index 0c226b2..13f2f1d2 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.h +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.h @@ -254,7 +254,8 @@ void vmw_du_crtc_save(struct drm_crtc *crtc); void vmw_du_crtc_restore(struct drm_crtc *crtc); int vmw_du_crtc_gamma_set(struct drm_crtc *crtc, u16 *r, u16 *g, u16 *b, - uint32_t size); + uint32_t size, + struct drm_modeset_acquire_ctx *ctx); int vmw_du_crtc_cursor_set2(struct drm_crtc *crtc, struct drm_file *file_priv, uint32_t handle, uint32_t width, uint32_t height, int32_t hot_x, int32_t hot_y); diff --git a/include/drm/bridge/dw_hdmi.h b/include/drm/bridge/dw_hdmi.h index bcceee8..ed599be 100644 --- a/include/drm/bridge/dw_hdmi.h +++ b/include/drm/bridge/dw_hdmi.h @@ -14,6 +14,67 @@ struct dw_hdmi; +/** + * DOC: Supported input formats and encodings + * + * Depending on the Hardware configuration of the Controller IP, it supports + * a subset of the following input formats and encodings on its internal + * 48bit bus. + * + * +----------------------+----------------------------------+------------------------------+ + * + Format Name + Format Code + Encodings + + * +----------------------+----------------------------------+------------------------------+ + * + RGB 4:4:4 8bit + ``MEDIA_BUS_FMT_RGB888_1X24`` + ``V4L2_YCBCR_ENC_DEFAULT`` + + * +----------------------+----------------------------------+------------------------------+ + * + RGB 4:4:4 10bits + ``MEDIA_BUS_FMT_RGB101010_1X30`` + ``V4L2_YCBCR_ENC_DEFAULT`` + + * +----------------------+----------------------------------+------------------------------+ + * + RGB 4:4:4 12bits + ``MEDIA_BUS_FMT_RGB121212_1X36`` + ``V4L2_YCBCR_ENC_DEFAULT`` + + * +----------------------+----------------------------------+------------------------------+ + * + RGB 4:4:4 16bits + ``MEDIA_BUS_FMT_RGB161616_1X48`` + ``V4L2_YCBCR_ENC_DEFAULT`` + + * +----------------------+----------------------------------+------------------------------+ + * + YCbCr 4:4:4 8bit + ``MEDIA_BUS_FMT_YUV8_1X24`` + ``V4L2_YCBCR_ENC_601`` + + * + + + or ``V4L2_YCBCR_ENC_709`` + + * + + + or ``V4L2_YCBCR_ENC_XV601`` + + * + + + or ``V4L2_YCBCR_ENC_XV709`` + + * +----------------------+----------------------------------+------------------------------+ + * + YCbCr 4:4:4 10bits + ``MEDIA_BUS_FMT_YUV10_1X30`` + ``V4L2_YCBCR_ENC_601`` + + * + + + or ``V4L2_YCBCR_ENC_709`` + + * + + + or ``V4L2_YCBCR_ENC_XV601`` + + * + + + or ``V4L2_YCBCR_ENC_XV709`` + + * +----------------------+----------------------------------+------------------------------+ + * + YCbCr 4:4:4 12bits + ``MEDIA_BUS_FMT_YUV12_1X36`` + ``V4L2_YCBCR_ENC_601`` + + * + + + or ``V4L2_YCBCR_ENC_709`` + + * + + + or ``V4L2_YCBCR_ENC_XV601`` + + * + + + or ``V4L2_YCBCR_ENC_XV709`` + + * +----------------------+----------------------------------+------------------------------+ + * + YCbCr 4:4:4 16bits + ``MEDIA_BUS_FMT_YUV16_1X48`` + ``V4L2_YCBCR_ENC_601`` + + * + + + or ``V4L2_YCBCR_ENC_709`` + + * + + + or ``V4L2_YCBCR_ENC_XV601`` + + * + + + or ``V4L2_YCBCR_ENC_XV709`` + + * +----------------------+----------------------------------+------------------------------+ + * + YCbCr 4:2:2 8bit + ``MEDIA_BUS_FMT_UYVY8_1X16`` + ``V4L2_YCBCR_ENC_601`` + + * + + + or ``V4L2_YCBCR_ENC_709`` + + * +----------------------+----------------------------------+------------------------------+ + * + YCbCr 4:2:2 10bits + ``MEDIA_BUS_FMT_UYVY10_1X20`` + ``V4L2_YCBCR_ENC_601`` + + * + + + or ``V4L2_YCBCR_ENC_709`` + + * +----------------------+----------------------------------+------------------------------+ + * + YCbCr 4:2:2 12bits + ``MEDIA_BUS_FMT_UYVY12_1X24`` + ``V4L2_YCBCR_ENC_601`` + + * + + + or ``V4L2_YCBCR_ENC_709`` + + * +----------------------+----------------------------------+------------------------------+ + * + YCbCr 4:2:0 8bit + ``MEDIA_BUS_FMT_UYYVYY8_0_5X24`` + ``V4L2_YCBCR_ENC_601`` + + * + + + or ``V4L2_YCBCR_ENC_709`` + + * +----------------------+----------------------------------+------------------------------+ + * + YCbCr 4:2:0 10bits + ``MEDIA_BUS_FMT_UYYVYY10_0_5X30``+ ``V4L2_YCBCR_ENC_601`` + + * + + + or ``V4L2_YCBCR_ENC_709`` + + * +----------------------+----------------------------------+------------------------------+ + * + YCbCr 4:2:0 12bits + ``MEDIA_BUS_FMT_UYYVYY12_0_5X36``+ ``V4L2_YCBCR_ENC_601`` + + * + + + or ``V4L2_YCBCR_ENC_709`` + + * +----------------------+----------------------------------+------------------------------+ + * + YCbCr 4:2:0 16bits + ``MEDIA_BUS_FMT_UYYVYY16_0_5X48``+ ``V4L2_YCBCR_ENC_601`` + + * + + + or ``V4L2_YCBCR_ENC_709`` + + * +----------------------+----------------------------------+------------------------------+ + */ + enum { DW_HDMI_RES_8, DW_HDMI_RES_10, @@ -56,12 +117,17 @@ struct dw_hdmi_phy_ops { struct drm_display_mode *mode); void (*disable)(struct dw_hdmi *hdmi, void *data); enum drm_connector_status (*read_hpd)(struct dw_hdmi *hdmi, void *data); + void (*update_hpd)(struct dw_hdmi *hdmi, void *data, + bool force, bool disabled, bool rxsense); + void (*setup_hpd)(struct dw_hdmi *hdmi, void *data); }; struct dw_hdmi_plat_data { struct regmap *regm; enum drm_mode_status (*mode_valid)(struct drm_connector *connector, struct drm_display_mode *mode); + unsigned long input_bus_format; + unsigned long input_bus_encoding; /* Vendor PHY support */ const struct dw_hdmi_phy_ops *phy_ops; @@ -84,6 +150,8 @@ void dw_hdmi_unbind(struct device *dev); int dw_hdmi_bind(struct platform_device *pdev, struct drm_encoder *encoder, const struct dw_hdmi_plat_data *plat_data); +void dw_hdmi_setup_rx_sense(struct device *dev, bool hpd, bool rx_sense); + void dw_hdmi_set_sample_rate(struct dw_hdmi *hdmi, unsigned int rate); void dw_hdmi_audio_enable(struct dw_hdmi *hdmi); void dw_hdmi_audio_disable(struct dw_hdmi *hdmi); diff --git a/include/drm/drmP.h b/include/drm/drmP.h index 3bfafcd..e1daa4f 100644 --- a/include/drm/drmP.h +++ b/include/drm/drmP.h @@ -80,6 +80,7 @@ #include <drm/drm_file.h> #include <drm/drm_debugfs.h> #include <drm/drm_ioctl.h> +#include <drm/drm_sysfs.h> struct module; @@ -512,10 +513,6 @@ static inline int drm_device_is_unplugged(struct drm_device *dev) * DMA quiscent + idle. DMA quiescent usually requires the hardware lock. */ - /* sysfs support (drm_sysfs.c) */ -extern void drm_sysfs_hotplug_event(struct drm_device *dev); - - /*@}*/ /* returns true if currently okay to sleep */ diff --git a/include/drm/drm_atomic.h b/include/drm/drm_atomic.h index fd33ed5..788daf7 100644 --- a/include/drm/drm_atomic.h +++ b/include/drm/drm_atomic.h @@ -160,7 +160,6 @@ struct __drm_connnectors_state { * @dev: parent DRM device * @allow_modeset: allow full modeset * @legacy_cursor_update: hint to enforce legacy cursor IOCTL semantics - * @legacy_set_config: Disable conflicting encoders instead of failing with -EINVAL. * @planes: pointer to array of structures with per-plane data * @crtcs: pointer to array of CRTC pointers * @num_connector: size of the @connectors and @connector_states arrays @@ -173,7 +172,6 @@ struct drm_atomic_state { struct drm_device *dev; bool allow_modeset : 1; bool legacy_cursor_update : 1; - bool legacy_set_config : 1; struct __drm_planes_state *planes; struct __drm_crtcs_state *crtcs; int num_connector; diff --git a/include/drm/drm_atomic_helper.h b/include/drm/drm_atomic_helper.h index fd395dc..f0a8678 100644 --- a/include/drm/drm_atomic_helper.h +++ b/include/drm/drm_atomic_helper.h @@ -176,7 +176,8 @@ void drm_atomic_helper_connector_destroy_state(struct drm_connector *connector, struct drm_connector_state *state); int drm_atomic_helper_legacy_gamma_set(struct drm_crtc *crtc, u16 *red, u16 *green, u16 *blue, - uint32_t size); + uint32_t size, + struct drm_modeset_acquire_ctx *ctx); /** * drm_atomic_crtc_for_each_plane - iterate over planes currently attached to CRTC diff --git a/include/drm/drm_connector.h b/include/drm/drm_connector.h index 8250062..4eeda12 100644 --- a/include/drm/drm_connector.h +++ b/include/drm/drm_connector.h @@ -32,6 +32,7 @@ struct drm_device; struct drm_connector_helper_funcs; +struct drm_modeset_acquire_ctx; struct drm_device; struct drm_crtc; struct drm_encoder; @@ -382,6 +383,11 @@ struct drm_connector_funcs { * the helper library vtable purely for historical reasons. The only DRM * core entry point to probe connector state is @fill_modes. * + * Note that the helper library will already hold + * &drm_mode_config.connection_mutex. Drivers which need to grab additional + * locks to avoid races with concurrent modeset changes need to use + * &drm_connector_helper_funcs.detect_ctx instead. + * * RETURNS: * * drm_connector_status indicating the connector's status. diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h index 2be2192..a8176a8 100644 --- a/include/drm/drm_crtc.h +++ b/include/drm/drm_crtc.h @@ -322,7 +322,8 @@ struct drm_crtc_funcs { * hooks. */ int (*gamma_set)(struct drm_crtc *crtc, u16 *r, u16 *g, u16 *b, - uint32_t size); + uint32_t size, + struct drm_modeset_acquire_ctx *ctx); /** * @destroy: @@ -782,15 +783,6 @@ struct drm_crtc { */ spinlock_t commit_lock; - /** - * @acquire_ctx: - * - * Per-CRTC implicit acquire context used by atomic drivers for legacy - * IOCTLs, so that atomic drivers can get at the locking acquire - * context. - */ - struct drm_modeset_acquire_ctx *acquire_ctx; - #ifdef CONFIG_DEBUG_FS /** * @debugfs_entry: diff --git a/include/drm/drm_crtc_helper.h b/include/drm/drm_crtc_helper.h index 43505c7..76e237b 100644 --- a/include/drm/drm_crtc_helper.h +++ b/include/drm/drm_crtc_helper.h @@ -67,6 +67,9 @@ int drm_helper_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y, int drm_helper_probe_single_connector_modes(struct drm_connector *connector, uint32_t maxX, uint32_t maxY); +int drm_helper_probe_detect(struct drm_connector *connector, + struct drm_modeset_acquire_ctx *ctx, + bool force); void drm_kms_helper_poll_init(struct drm_device *dev); void drm_kms_helper_poll_fini(struct drm_device *dev); bool drm_helper_hpd_irq_event(struct drm_device *dev); diff --git a/include/drm/drm_ioctl.h b/include/drm/drm_ioctl.h index f17ee07..ee03b3c 100644 --- a/include/drm/drm_ioctl.h +++ b/include/drm/drm_ioctl.h @@ -33,6 +33,7 @@ #define _DRM_IOCTL_H_ #include <linux/types.h> +#include <linux/bitops.h> #include <asm/ioctl.h> @@ -41,41 +42,126 @@ struct drm_file; struct file; /** - * Ioctl function type. + * drm_ioctl_t - DRM ioctl function type. + * @dev: DRM device inode + * @data: private pointer of the ioctl call + * @file_priv: DRM file this ioctl was made on * - * \param inode device inode. - * \param file_priv DRM file private pointer. - * \param cmd command. - * \param arg argument. + * This is the DRM ioctl typedef. Note that drm_ioctl() has alrady copied @data + * into kernel-space, and will also copy it back, depending upon the read/write + * settings in the ioctl command code. */ typedef int drm_ioctl_t(struct drm_device *dev, void *data, struct drm_file *file_priv); +/** + * drm_ioctl_compat_t - compatibility DRM ioctl function type. + * @filp: file pointer + * @cmd: ioctl command code + * @arg: DRM file this ioctl was made on + * + * Just a typedef to make declaring an array of compatibility handlers easier. + * New drivers shouldn't screw up the structure layout for their ioctl + * structures and hence never need this. + */ typedef int drm_ioctl_compat_t(struct file *filp, unsigned int cmd, unsigned long arg); #define DRM_IOCTL_NR(n) _IOC_NR(n) #define DRM_MAJOR 226 -#define DRM_AUTH 0x1 -#define DRM_MASTER 0x2 -#define DRM_ROOT_ONLY 0x4 -#define DRM_CONTROL_ALLOW 0x8 -#define DRM_UNLOCKED 0x10 -#define DRM_RENDER_ALLOW 0x20 +/** + * enum drm_ioctl_flags - DRM ioctl flags + * + * Various flags that can be set in &drm_ioctl_desc.flags to control how + * userspace can use a given ioctl. + */ +enum drm_ioctl_flags { + /** + * @DRM_AUTH: + * + * This is for ioctl which are used for rendering, and require that the + * file descriptor is either for a render node, or if it's a + * legacy/primary node, then it must be authenticated. + */ + DRM_AUTH = BIT(0), + /** + * @DRM_MASTER: + * + * This must be set for any ioctl which can change the modeset or + * display state. Userspace must call the ioctl through a primary node, + * while it is the active master. + * + * Note that read-only modeset ioctl can also be called by + * unauthenticated clients, or when a master is not the currently active + * one. + */ + DRM_MASTER = BIT(1), + /** + * @DRM_ROOT_ONLY: + * + * Anything that could potentially wreak a master file descriptor needs + * to have this flag set. Current that's only for the SETMASTER and + * DROPMASTER ioctl, which e.g. logind can call to force a non-behaving + * master (display compositor) into compliance. + * + * This is equivalent to callers with the SYSADMIN capability. + */ + DRM_ROOT_ONLY = BIT(2), + /** + * @DRM_CONTROL_ALLOW: + * + * Deprecated, do not use. Control nodes are in the process of getting + * removed. + */ + DRM_CONTROL_ALLOW = BIT(3), + /** + * @DRM_UNLOCKED: + * + * Whether &drm_ioctl_desc.func should be called with the DRM BKL held + * or not. Enforced as the default for all modern drivers, hence there + * should never be a need to set this flag. + */ + DRM_UNLOCKED = BIT(4), + /** + * @DRM_RENDER_ALLOW: + * + * This is used for all ioctl needed for rendering only, for drivers + * which support render nodes. This should be all new render drivers, + * and hence it should be always set for any ioctl with DRM_AUTH set. + * Note though that read-only query ioctl might have this set, but have + * not set DRM_AUTH because they do not require authentication. + */ + DRM_RENDER_ALLOW = BIT(5), +}; +/** + * struct drm_ioctl_desc - DRM driver ioctl entry + * @cmd: ioctl command number, without flags + * @flags: a bitmask of &enum drm_ioctl_flags + * @func: handler for this ioctl + * @name: user-readable name for debug output + * + * For convenience it's easier to create these using the DRM_IOCTL_DEF_DRV() + * macro. + */ struct drm_ioctl_desc { unsigned int cmd; - int flags; + enum drm_ioctl_flags flags; drm_ioctl_t *func; const char *name; }; /** - * Creates a driver or general drm_ioctl_desc array entry for the given - * ioctl, for use by drm_ioctl(). + * DRM_IOCTL_DEF_DRV() - helper macro to fill out a &struct drm_ioctl_desc + * @ioctl: ioctl command suffix + * @_func: handler for the ioctl + * @_flags: a bitmask of &enum drm_ioctl_flags + * + * Small helper macro to create a &struct drm_ioctl_desc entry. The ioctl + * command number is constructed by prepending ``DRM_IOCTL\_`` and passing that + * to DRM_IOCTL_NR(). */ - #define DRM_IOCTL_DEF_DRV(ioctl, _func, _flags) \ [DRM_IOCTL_NR(DRM_IOCTL_##ioctl) - DRM_COMMAND_BASE] = { \ .cmd = DRM_IOCTL_##ioctl, \ diff --git a/include/drm/drm_modeset_helper_vtables.h b/include/drm/drm_modeset_helper_vtables.h index 091c422..c01c328 100644 --- a/include/drm/drm_modeset_helper_vtables.h +++ b/include/drm/drm_modeset_helper_vtables.h @@ -747,6 +747,10 @@ struct drm_connector_helper_funcs { * This callback is used by the probe helpers in e.g. * drm_helper_probe_single_connector_modes(). * + * To avoid races with concurrent connector state updates, the helper + * libraries always call this with the &drm_mode_config.connection_mutex + * held. Because of this it's safe to inspect &drm_connector->state. + * * RETURNS: * * The number of modes added by calling drm_mode_probed_add(). @@ -754,6 +758,34 @@ struct drm_connector_helper_funcs { int (*get_modes)(struct drm_connector *connector); /** + * @detect_ctx: + * + * Check to see if anything is attached to the connector. The parameter + * force is set to false whilst polling, true when checking the + * connector due to a user request. force can be used by the driver to + * avoid expensive, destructive operations during automated probing. + * + * This callback is optional, if not implemented the connector will be + * considered as always being attached. + * + * This is the atomic version of &drm_connector_funcs.detect. + * + * To avoid races against concurrent connector state updates, the + * helper libraries always call this with ctx set to a valid context, + * and &drm_mode_config.connection_mutex will always be locked with + * the ctx parameter set to this ctx. This allows taking additional + * locks as required. + * + * RETURNS: + * + * &drm_connector_status indicating the connector's status, + * or the error code returned by drm_modeset_lock(), -EDEADLK. + */ + int (*detect_ctx)(struct drm_connector *connector, + struct drm_modeset_acquire_ctx *ctx, + bool force); + + /** * @mode_valid: * * Callback to validate a mode for a connector, irrespective of the @@ -771,6 +803,10 @@ struct drm_connector_helper_funcs { * CRTC helpers will not call this function. Drivers therefore must * still fully validate any mode passed in in a modeset request. * + * To avoid races with concurrent connector state updates, the helper + * libraries always call this with the &drm_mode_config.connection_mutex + * held. Because of this it's safe to inspect &drm_connector->state. + * * RETURNS: * * Either &drm_mode_status.MODE_OK or one of the failure reasons in &enum @@ -836,6 +872,40 @@ struct drm_connector_helper_funcs { */ struct drm_encoder *(*atomic_best_encoder)(struct drm_connector *connector, struct drm_connector_state *connector_state); + + /** + * @atomic_check: + * + * This hook is used to validate connector state. This function is + * called from &drm_atomic_helper_check_modeset, and is called when + * a connector property is set, or a modeset on the crtc is forced. + * + * Because &drm_atomic_helper_check_modeset may be called multiple times, + * this function should handle being called multiple times as well. + * + * This function is also allowed to inspect any other object's state and + * can add more state objects to the atomic commit if needed. Care must + * be taken though to ensure that state check and compute functions for + * these added states are all called, and derived state in other objects + * all updated. Again the recommendation is to just call check helpers + * until a maximal configuration is reached. + * + * NOTE: + * + * This function is called in the check phase of an atomic update. The + * driver is not allowed to change anything outside of the free-standing + * state objects passed-in or assembled in the overall &drm_atomic_state + * update tracking structure. + * + * RETURNS: + * + * 0 on success, -EINVAL if the state or the transition can't be + * supported, -ENOMEM on memory allocation failure and -EDEADLK if an + * attempt to obtain another state object ran into a &drm_modeset_lock + * deadlock. + */ + int (*atomic_check)(struct drm_connector *connector, + struct drm_connector_state *state); }; /** diff --git a/include/drm/drm_modeset_lock.h b/include/drm/drm_modeset_lock.h index 96d39fb..4b27c2b 100644 --- a/include/drm/drm_modeset_lock.h +++ b/include/drm/drm_modeset_lock.h @@ -121,12 +121,7 @@ struct drm_plane; void drm_modeset_lock_all(struct drm_device *dev); void drm_modeset_unlock_all(struct drm_device *dev); -void drm_modeset_lock_crtc(struct drm_crtc *crtc, - struct drm_plane *plane); -void drm_modeset_unlock_crtc(struct drm_crtc *crtc); void drm_warn_on_modeset_not_all_locked(struct drm_device *dev); -struct drm_modeset_acquire_ctx * -drm_modeset_legacy_acquire_ctx(struct drm_crtc *crtc); int drm_modeset_lock_all_ctx(struct drm_device *dev, struct drm_modeset_acquire_ctx *ctx); diff --git a/include/drm/drm_of.h b/include/drm/drm_of.h index d1fc563..104dd51 100644 --- a/include/drm/drm_of.h +++ b/include/drm/drm_of.h @@ -8,6 +8,8 @@ struct component_match; struct device; struct drm_device; struct drm_encoder; +struct drm_panel; +struct drm_bridge; struct device_node; #ifdef CONFIG_OF @@ -23,6 +25,10 @@ int drm_of_component_probe(struct device *dev, int drm_of_encoder_active_endpoint(struct device_node *node, struct drm_encoder *encoder, struct of_endpoint *endpoint); +int drm_of_find_panel_or_bridge(const struct device_node *np, + int port, int endpoint, + struct drm_panel **panel, + struct drm_bridge **bridge); #else static inline uint32_t drm_of_find_possible_crtcs(struct drm_device *dev, struct device_node *port) @@ -52,6 +58,13 @@ static inline int drm_of_encoder_active_endpoint(struct device_node *node, { return -EINVAL; } +static inline int drm_of_find_panel_or_bridge(const struct device_node *np, + int port, int endpoint, + struct drm_panel **panel, + struct drm_bridge **bridge) +{ + return -EINVAL; +} #endif static inline int drm_of_encoder_active_endpoint_id(struct device_node *node, diff --git a/include/drm/drm_panel.h b/include/drm/drm_panel.h index 4b76cf2..1b364b0 100644 --- a/include/drm/drm_panel.h +++ b/include/drm/drm_panel.h @@ -192,7 +192,7 @@ void drm_panel_remove(struct drm_panel *panel); int drm_panel_attach(struct drm_panel *panel, struct drm_connector *connector); int drm_panel_detach(struct drm_panel *panel); -#ifdef CONFIG_OF +#if defined(CONFIG_OF) && defined(CONFIG_DRM_PANEL) struct drm_panel *of_drm_find_panel(const struct device_node *np); #else static inline struct drm_panel *of_drm_find_panel(const struct device_node *np) diff --git a/include/drm/drm_sysfs.h b/include/drm/drm_sysfs.h index 23418c1..70c9a10 100644 --- a/include/drm/drm_sysfs.h +++ b/include/drm/drm_sysfs.h @@ -1,12 +1,12 @@ #ifndef _DRM_SYSFS_H_ #define _DRM_SYSFS_H_ -/** - * This minimalistic include file is intended for users (read TTM) that - * don't want to include the full drmP.h file. - */ +struct drm_device; +struct device; int drm_class_device_register(struct device *dev); void drm_class_device_unregister(struct device *dev); +void drm_sysfs_hotplug_event(struct drm_device *dev); + #endif diff --git a/include/uapi/drm/drm.h b/include/uapi/drm/drm.h index b2c5284..42d9f64 100644 --- a/include/uapi/drm/drm.h +++ b/include/uapi/drm/drm.h @@ -647,6 +647,7 @@ struct drm_gem_open { #define DRM_CAP_CURSOR_HEIGHT 0x9 #define DRM_CAP_ADDFB2_MODIFIERS 0x10 #define DRM_CAP_PAGE_FLIP_TARGET 0x11 +#define DRM_CAP_CRTC_IN_VBLANK_EVENT 0x12 /** DRM_IOCTL_GET_CAP ioctl argument type */ struct drm_get_cap { @@ -851,7 +852,7 @@ struct drm_event_vblank { __u32 tv_sec; __u32 tv_usec; __u32 sequence; - __u32 reserved; + __u32 crtc_id; /* 0 on older kernels that do not support this */ }; /* typedef area */ diff --git a/include/uapi/linux/media-bus-format.h b/include/uapi/linux/media-bus-format.h index 2168759..ef6fb30 100644 --- a/include/uapi/linux/media-bus-format.h +++ b/include/uapi/linux/media-bus-format.h @@ -33,7 +33,7 @@ #define MEDIA_BUS_FMT_FIXED 0x0001 -/* RGB - next is 0x1018 */ +/* RGB - next is 0x101b */ #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 @@ -57,8 +57,11 @@ #define MEDIA_BUS_FMT_RGB888_1X7X4_JEIDA 0x1012 #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_RGB121212_1X36 0x1019 +#define MEDIA_BUS_FMT_RGB161616_1X48 0x101a -/* YUV (including grey) - next is 0x2026 */ +/* YUV (including grey) - next is 0x202c */ #define MEDIA_BUS_FMT_Y8_1X8 0x2001 #define MEDIA_BUS_FMT_UV8_1X8 0x2015 #define MEDIA_BUS_FMT_UYVY8_1_5X8 0x2002 @@ -90,12 +93,18 @@ #define MEDIA_BUS_FMT_YVYU10_1X20 0x200e #define MEDIA_BUS_FMT_VUY8_1X24 0x2024 #define MEDIA_BUS_FMT_YUV8_1X24 0x2025 +#define MEDIA_BUS_FMT_UYYVYY8_0_5X24 0x2026 #define MEDIA_BUS_FMT_UYVY12_1X24 0x2020 #define MEDIA_BUS_FMT_VYUY12_1X24 0x2021 #define MEDIA_BUS_FMT_YUYV12_1X24 0x2022 #define MEDIA_BUS_FMT_YVYU12_1X24 0x2023 #define MEDIA_BUS_FMT_YUV10_1X30 0x2016 +#define MEDIA_BUS_FMT_UYYVYY10_0_5X30 0x2027 #define MEDIA_BUS_FMT_AYUV8_1X32 0x2017 +#define MEDIA_BUS_FMT_UYYVYY12_0_5X36 0x2028 +#define MEDIA_BUS_FMT_YUV12_1X36 0x2029 +#define MEDIA_BUS_FMT_YUV16_1X48 0x202a +#define MEDIA_BUS_FMT_UYYVYY16_0_5X48 0x202b /* Bayer - next is 0x3021 */ #define MEDIA_BUS_FMT_SBGGR8_1X8 0x3001 |