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
  ...
This commit is contained in:
Dave Airlie 2017-04-11 07:41:10 +10:00
commit df45eaca51
133 changed files with 6064 additions and 1997 deletions

View file

@ -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>;
};
};
};

View file

@ -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";

View file

@ -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

View file

@ -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
==============

View file

@ -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
=====================

View file

@ -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

View file

@ -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

View file

@ -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.

View file

@ -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
@ -6326,6 +6784,506 @@ the following codes.
- v\ :sub:`0`
.. raw:: latex
\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

View file

@ -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

View file

@ -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;

View file

@ -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;

View file

@ -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;

View file

@ -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;

View file

@ -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;

View file

@ -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;

View file

@ -392,30 +392,14 @@ 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)
port = of_graph_get_remote_node(pdev->dev.of_node, 0, 0);
if (!port)
return -ENODEV;
if (!of_device_is_available(ep)) {
of_node_put(ep);
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);

View file

@ -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 @@ static int malidp_bind(struct device *dev)
bind_fail:
of_node_put(malidp->crtc.port);
malidp->crtc.port = NULL;
port_fail:
malidp_fini(drm);
init_fail:
drm->dev_private = NULL;
@ -516,30 +509,17 @@ 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)
port = of_graph_get_remote_node(pdev->dev.of_node, 0, 0);
if (!port)
return -ENODEV;
if (!of_device_is_available(ep)) {
of_node_put(ep);
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);
of_node_put(port);

View file

@ -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;

View file

@ -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 = drm_of_find_panel_or_bridge(np, 0, 0, &panel, &bridge);
if (ret)
return ret;
ret = -EPROBE_DEFER;
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 @@ static int atmel_hlcdc_attach_endpoint(struct drm_device *dev,
int atmel_hlcdc_create_outputs(struct drm_device *dev)
{
struct device_node *ep_np = NULL;
struct of_endpoint ep;
int ret;
struct device_node *remote;
int ret, endpoint = 0;
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);
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;
if (ret) {
of_node_put(ep_np);
ret = atmel_hlcdc_attach_endpoint(dev, remote);
of_node_put(remote);
if (ret)
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);
return ret;
}
}
return 0;
if (!endpoint)
return -ENODEV;
return ret;
}

View file

@ -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,

View file

@ -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);

View file

@ -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);

View file

@ -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,

View file

@ -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;

View file

@ -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;

View file

@ -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 {
break;
default:
output_select = HDMI_VP_CONF_OUTPUT_SELECTOR_BYPASS;
}
} 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;
break;
case 10:
remap_size = HDMI_VP_REMAP_YCC422_20bit;
break;
case 12:
remap_size = HDMI_VP_REMAP_YCC422_24bit;
break;
default:
return;
}
} else if (hdmi_data->enc_out_format == YCBCR422_8BITS) {
if (!hdmi_data->enc_color_depth ||
hdmi_data->enc_color_depth == 8)
remap_size = HDMI_VP_REMAP_YCC422_16bit;
else if (hdmi_data->enc_color_depth == 10)
remap_size = HDMI_VP_REMAP_YCC422_20bit;
else if (hdmi_data->enc_color_depth == 12)
remap_size = HDMI_VP_REMAP_YCC422_24bit;
else
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;
hdmi->hdmi_data.enc_out_format = RGB;
/* 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;
/* 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;

View file

@ -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);

View file

@ -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;
fail:
of_node_put(ep);
of_node_put(connector_node);
of_node_put(ddc_phandle);
fail:
of_node_put(connector_node);
return ret;
}

View file

@ -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;

View file

@ -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 @@ void drm_atomic_legacy_backoff(struct drm_atomic_state *state)
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,6 +1676,44 @@ static void drm_atomic_print_state(const struct drm_atomic_state *state)
drm_atomic_connector_print_state(&p, connector_state);
}
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;
struct drm_crtc *crtc;
struct drm_connector *connector;
struct drm_connector_list_iter conn_iter;
if (!drm_core_check_feature(dev, DRIVER_ATOMIC))
return;
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) {
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
@ -1707,25 +1731,7 @@ static void drm_atomic_print_state(const struct drm_atomic_state *state)
*/
void drm_state_dump(struct drm_device *dev, struct drm_printer *p)
{
struct drm_mode_config *config = &dev->mode_config;
struct drm_plane *plane;
struct drm_crtc *crtc;
struct drm_connector *connector;
struct drm_connector_list_iter conn_iter;
if (!drm_core_check_feature(dev, DRIVER_ATOMIC))
return;
list_for_each_entry(plane, &config->plane_list, head)
drm_atomic_plane_print_state(p, plane->state);
list_for_each_entry(crtc, &config->crtc_list, head)
drm_atomic_crtc_print_state(p, crtc->state);
drm_connector_list_iter_begin(dev, &conn_iter);
drm_for_each_connector_iter(connector, &conn_iter)
drm_atomic_connector_print_state(p, connector->state);
drm_connector_list_iter_end(&conn_iter);
__drm_state_dump(dev, p, false);
}
EXPORT_SYMBOL(drm_state_dump);
@ -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)
{

View file

@ -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 @@ int drm_atomic_helper_legacy_gamma_set(struct drm_crtc *crtc,
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);

View file

@ -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;
}

View file

@ -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 @@ int drm_mode_setcrtc(struct drm_device *dev, void *data,
}
drm_modeset_drop_locks(&ctx);
drm_modeset_acquire_fini(&ctx);
mutex_unlock(&crtc->dev->mode_config.mutex);
return ret;
}

View file

@ -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 */

View file

@ -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 @@ int drm_fb_helper_single_add_all_connectors(struct drm_fb_helper *fb_helper)
}
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 @@ static int restore_fbdev_mode_atomic(struct drm_fb_helper *fb_helper)
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 @@ static int restore_fbdev_mode_atomic(struct drm_fb_helper *fb_helper)
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 @@ static int pan_display_atomic(struct fb_var_screeninfo *var,
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 @@ static bool drm_target_preferred(struct drm_fb_helper *fb_helper,
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];

View file

@ -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);

View file

@ -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
*
* \param file_priv DRM file private.
* \param cmd command.
* \param arg user argument.
* \return zero on success or negative number on failure.
* 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.
*
* 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);

View file

@ -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;
}
@ -646,14 +649,60 @@ 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.

View file

@ -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);

View file

@ -148,108 +148,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

View file

@ -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);

View file

@ -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 @@ static int drm_mode_cursor_universal(struct drm_crtc *crtc,
*/
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 @@ static int drm_mode_cursor_universal(struct drm_crtc *crtc,
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;

View file

@ -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,13 +169,74 @@ 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
* @connector: connector to probe
@ -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,

View file

@ -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) {
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))) {
ret = -EFAULT;
goto done;
}
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)) {
ret = -EFAULT;
goto done;
}
copied++;
}
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)

View file

@ -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 @@ struct device *drm_sysfs_minor_alloc(struct drm_minor *minor)
}
/**
* 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);

View file

@ -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);

View file

@ -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) {

View file

@ -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);

View file

@ -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;

View file

@ -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 @@ static int fsl_dcu_attach_panel(struct fsl_dcu_drm_device *fsl_dev,
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;
return fsl_dcu_attach_endpoint(fsl_dev, &ep);
if (panel) {
fsl_dev->connector.panel = panel;
return fsl_dcu_attach_panel(fsl_dev, panel);
}
return drm_bridge_attach(&fsl_dev->encoder, bridge, NULL);
}

View file

@ -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;

View file

@ -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);

View file

@ -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)) {

View file

@ -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);

View file

@ -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,
};

View file

@ -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 @@ bool intel_get_load_detect_pipe(struct drm_connector *connector,
restore_state = NULL;
}
if (ret == -EDEADLK) {
drm_modeset_backoff(ctx);
goto retry;
}
if (ret == -EDEADLK)
return ret;
return false;
}
@ -10727,7 +10711,7 @@ static int intel_crtc_page_flip(struct drm_crtc *crtc,
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 @@ void intel_crtc_restore_mode(struct drm_crtc *crtc)
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);
}

View file

@ -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 @@ intel_dp_long_pulse(struct intel_connector *intel_connector)
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,
};

View file

@ -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);

View file

@ -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;

View file

@ -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);

View file

@ -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 @@ intel_tv_set_property(struct drm_connector *connector, struct drm_property *prop
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,
};

View file

@ -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) {

View file

@ -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;

View file

@ -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);

View file

@ -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)) {

View file

@ -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);

View file

@ -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

View file

@ -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

View file

@ -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.
*/

View file

@ -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 = {

View file

@ -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 @@ static int meson_drv_probe(struct platform_device *pdev)
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,

View file

@ -47,6 +47,9 @@ struct meson_drm {
struct {
unsigned int current_mode;
bool hdmi_repeat;
bool venc_repeat;
bool hdmi_use_enci;
} venc;
};

View file

@ -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(&reg_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(&reg_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(&reg_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(&reg_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(&reg_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(&reg_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(&reg_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(&reg_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");

View file

@ -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 */

View file

@ -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

View file

@ -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);
}
void meson_vclk_setup(struct meson_drm *priv, unsigned int target,
unsigned int freq)
/* 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)
{
if (target == MESON_VCLK_TARGET_CVBS && freq == MESON_VCLK_CVBS)
meson_venci_cvbs_clock_config(priv);
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 vclk_freq, unsigned int venc_freq,
unsigned int dac_freq, bool hdmi_use_enci)
{
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);

View file

@ -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 */

File diff suppressed because it is too large Load diff

View file

@ -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);

View file

@ -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),

View file

@ -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

View file

@ -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

View file

@ -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);

View file

@ -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;

View file

@ -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;

View file

@ -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;

View file

@ -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;
int ret;
np = of_graph_get_remote_port_parent(ep->local_node);
panel = of_drm_find_panel(np);
of_node_put(np);
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;
}

View file

@ -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;

View file

@ -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;

View file

@ -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;

View file

@ -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;

View file

@ -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);

View file

@ -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;
int i;
if (parent == NULL)
return 0;
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", &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", &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)

View file

@ -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;

View file

@ -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;

View file

@ -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);

View file

@ -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;

View file

@ -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;

View file

@ -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;

Some files were not shown because too many files have changed in this diff Show more