All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 00/36] drm: xlnx: zynqmp_dpsub: Initial live video input support
@ 2021-08-09  1:34 Laurent Pinchart
  2021-08-09  1:34 ` [PATCH 01/36] dt-bindings: display: xlnx: zynqmp-dpsub: Add OF graph ports Laurent Pinchart
                   ` (35 more replies)
  0 siblings, 36 replies; 38+ messages in thread
From: Laurent Pinchart @ 2021-08-09  1:34 UTC (permalink / raw)
  To: dri-devel; +Cc: Michal Simek, Jianqiang Chen, Rob Herring, devicetree

Hello,

The DPSUB is the DisplayPort subsystem, a set of hard IP cores found in
the ZynqMP family of SoCs. It combines a DisplayPort encoder, a video
blender with two input channels, and a DMA engine. The zynqmp_dpsub
driver exposes this as a DRM device with one CRTC and two planes.

In addition to those features, the DPSUB can interface with the
programmable logic (PL) found in the ZynqMP SoC. Each input to the video
blender can come from the PL instead of the DMA engine, and the blender
output can also be routed to the PL. This creates a very configurable
device that can accommodate lots of use cases, but it also makes it
difficult to model it as a DRM/KMS device.

This patch series implements initial support for live video inputs, by
restricting the supported use cases to a single live video input. In
that mode, the video blender is configured in pass-through mode, with
the whole DPSUB essentially operating as a DisplayPort encoder only. The
CRTC and plane functions are then implemented by IP cores in the PL.

To support this, the series start with patch 01/36 to model the
connections to the PL in DT using OF graph bindings. This fixes a
historical mistake that forgot to model the connection to the DP
connector in DT.

With that in place, patches 02/36 to 10/36 refactor the driver to turn
the DisplayPort encoder implementation, modelled as a DRM encoder, into
a DRM bridge. Please see individual patches for details. The rework is
internal only, simplifies the code by making use of the DRM bridge
connector helper, but doesn't bring any functional change.

Patches 11/36 to 30/36 continue refactoring of the driver, to cleanly
separate the DRM planes, CRTC, encoder and connector from the DRM bridge
implementation. The goal is to make the latter available to a DRM driver
for the PL display pipeline without registering any DRM device in the
DPSUB driver itself. Patches 31/36 to 34/36 implement this, reading
information about the connection to the PL from the device tree to
decide in which mode to operate.

Finally, patch 35/36 and 36/36 update the ZynqMP core and ZCU106A board
device tree files to create ports and connect the DPSUB to the
DisplayPort connector. I have tested the whole series without these two
patches to ensure that backward compatibility with older DT isn't
broken.

With this series applied, the DPSUB can be used as a DisplayPort encoder
by a PL display pipeline. A careful reviewer may ask me where drivers
for such a display pipelines are, and that would be a very good
question.

PL display pipelines are currently supported in the Xilinx downstream
kernel only, which is something I want to address next. That road will
be full of challenges, as in theory anything can be implemented in the
PL, including pipelines that connect cameras and displays together. If
anynoe is interested in discussing this topic, please let me know.

Laurent Pinchart (36):
  dt-bindings: display: xlnx: zynqmp-dpsub: Add OF graph ports
  drm: xlnx: zynqmp_dpsub: Switch to atomic encoder enable/disable
  drm: xlnx: zynqmp_dpsub: Constify mode argument to function
  drm: xlnx: zynqmp_dpsub: Create DRM bridge to model DP encoder
  drm: xlnx: zynqmp_dpsub: Don't access connector in
    zynqmp_dp_set_format()
  drm: xlnx: zynqmp_dpsub: Move connector registration to bridge attach
  drm: xlnx: zynqmp_dpsub: Move encoder to DPSUB core
  drm: xlnx: zynqmp_dpsub: Attach to the next bridge
  drm: xlnx: zynqmp_dpsub: Use DRM connector bridge helper
  drm: xlnx: zynqmp_dpsub: Report HPD through the bridge
  drm: xlnx: zynqmp_dpsub: Drop unused zynqmp_disp.event field
  drm: xlnx: zynqmp_dpsub: Drop unused zynqmp_disp_format.bus_fmt field
  drm: xlnx: zynqmp_dpsub: Don't pass CRTC to zynqmp_disp_setup_clock()
  drm: xlnx: zynqmp_dpsub: Configure blender in zynqmp_disp_enable()
  drm: xlnx: zynqmp_dpsub: Use local variable in
    zynqmp_disp_layer_update()
  drm: xlnx: zynqmp_dpsub: Pass format info to
    zynqmp_disp_layer_set_format()
  drm: xlnx: zynqmp_dpsub: Remplace hardcoded values with ARRAY_SIZE()
  drm: xlnx: zynqmp_dpsub: Don't use drmm_kcalloc() for temporary data
  drm: xlnx: zynqmp_dpsub: Move pclk from zynqmp_disp to zynqmp_dpsub
  drm: xlnx: zynqmp_dpsub: Move audio clk from zynqmp_disp to
    zynqmp_dpsub
  drm: xlnx: zynqmp_dpsub: Move CRTC to zynqmp_dpsub structure
  drm: xlnx: zynqmp_dpsub: Move planes to zynqmp_dpsub structure
  drm: xlnx: zynqmp_dpsub: Move CRTC handling to zynqmp_kms.c
  drm: xlnx: zynqmp_dpsub: Move planes handling to zynqmp_kms.c
  drm: xlnx: zynqmp_dpsub: Register AUX bus at bridge attach time
  drm: xlnx: zynqmp_dpsub: Move DP bridge init to zynqmp_dp_probe()
  drm: xlnx: zynqmp_dpsub: Manage DP and DISP allocations manually
  drm: xlnx: zynqmp_dpsub: Move all DRM init and cleanup to zynqmp_kms.c
  drm: xlnx: zynqmp_dpsub: Decouple DRM device from zynqmp_dpsub
  drm: xlnx: zynqmp_dpsub: Rename zynqmp_dpsub_handle_vblank with DRM
    prefix
  drm: xlnx: zynqmp_dpsub: Parse DT to find connected ports
  drm: xlnx: zynqmp_dpsub: Allow configuration of layer mode
  drm: xlnx: zynqmp_dpsub: Support operation without DMA engine
  drm: xlnx: zynqmp_dpsub: Add support for live video input
  arm64: dts: zynqmp: Add ports for the DisplayPort subsystem
  arm64: dts: zynqmp: zcu106a: Describe DisplayPort connector

 .../display/xlnx/xlnx,zynqmp-dpsub.yaml       |  67 ++
 .../boot/dts/xilinx/zynqmp-zcu106-revA.dts    |  20 +
 arch/arm64/boot/dts/xilinx/zynqmp.dtsi        |  24 +
 drivers/gpu/drm/xlnx/Makefile                 |   2 +-
 drivers/gpu/drm/xlnx/zynqmp_disp.c            | 646 ++++--------------
 drivers/gpu/drm/xlnx/zynqmp_disp.h            |  48 +-
 drivers/gpu/drm/xlnx/zynqmp_dp.c              | 482 +++++++------
 drivers/gpu/drm/xlnx/zynqmp_dp.h              |   4 +-
 drivers/gpu/drm/xlnx/zynqmp_dpsub.c           | 316 +++++----
 drivers/gpu/drm/xlnx/zynqmp_dpsub.h           |  46 +-
 drivers/gpu/drm/xlnx/zynqmp_kms.c             | 539 +++++++++++++++
 drivers/gpu/drm/xlnx/zynqmp_kms.h             |  48 ++
 12 files changed, 1343 insertions(+), 899 deletions(-)
 create mode 100644 drivers/gpu/drm/xlnx/zynqmp_kms.c
 create mode 100644 drivers/gpu/drm/xlnx/zynqmp_kms.h

-- 
Regards,

Laurent Pinchart


^ permalink raw reply	[flat|nested] 38+ messages in thread

* [PATCH 01/36] dt-bindings: display: xlnx: zynqmp-dpsub: Add OF graph ports
  2021-08-09  1:34 [PATCH 00/36] drm: xlnx: zynqmp_dpsub: Initial live video input support Laurent Pinchart
@ 2021-08-09  1:34 ` Laurent Pinchart
  2021-08-13 20:54   ` Rob Herring
  2021-08-09  1:34 ` [PATCH 02/36] drm: xlnx: zynqmp_dpsub: Switch to atomic encoder enable/disable Laurent Pinchart
                   ` (34 subsequent siblings)
  35 siblings, 1 reply; 38+ messages in thread
From: Laurent Pinchart @ 2021-08-09  1:34 UTC (permalink / raw)
  To: dri-devel; +Cc: Michal Simek, Jianqiang Chen, Rob Herring, devicetree

The DPSUB doesn't live in isolation, but is connected to the
programmable logic for live inputs and outputs, and also has a
DisplayPort output. Model all those using OF graph.

Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
---
 .../display/xlnx/xlnx,zynqmp-dpsub.yaml       | 67 +++++++++++++++++++
 1 file changed, 67 insertions(+)

diff --git a/Documentation/devicetree/bindings/display/xlnx/xlnx,zynqmp-dpsub.yaml b/Documentation/devicetree/bindings/display/xlnx/xlnx,zynqmp-dpsub.yaml
index d88bd93f4b80..5000c5fda027 100644
--- a/Documentation/devicetree/bindings/display/xlnx/xlnx,zynqmp-dpsub.yaml
+++ b/Documentation/devicetree/bindings/display/xlnx/xlnx,zynqmp-dpsub.yaml
@@ -117,6 +117,45 @@ properties:
       - const: dp-phy0
       - const: dp-phy1
 
+  ports:
+    $ref: /schemas/graph.yaml#/properties/ports
+    description: |
+      Connections to the programmable logic and the DisplayPort PHYs. Each port
+      shall have a single endpoint.
+
+    properties:
+      port@0:
+        $ref: /schemas/graph.yaml#/properties/port
+        description: The live video input from the programmable logic
+
+      port@1:
+        $ref: /schemas/graph.yaml#/properties/port
+        description: The live graphics input from the programmable logic
+
+      port@2:
+        $ref: /schemas/graph.yaml#/properties/port
+        description: The live audio input from the programmable logic
+
+      port@3:
+        $ref: /schemas/graph.yaml#/properties/port
+        description: The blended video output to the programmable logic
+
+      port@4:
+        $ref: /schemas/graph.yaml#/properties/port
+        description: The mixed audio output to the programmable logic
+
+      port@5:
+        $ref: /schemas/graph.yaml#/properties/port
+        description: The DisplayPort output
+
+    required:
+      - port@0
+      - port@1
+      - port@2
+      - port@3
+      - port@4
+      - port@5
+
 required:
   - compatible
   - reg
@@ -130,6 +169,7 @@ required:
   - dma-names
   - phys
   - phy-names
+  - ports
 
 additionalProperties: false
 
@@ -164,6 +204,33 @@ examples:
                <&psgtr 0 PHY_TYPE_DP 1 3 27000000>;
 
         phy-names = "dp-phy0", "dp-phy1";
+
+        ports {
+            #address-cells = <1>;
+            #size-cells = <0>;
+
+            port@0 {
+                reg = <0>;
+            };
+            port@1 {
+                reg = <1>;
+            };
+            port@2 {
+                reg = <2>;
+            };
+            port@3 {
+                reg = <3>;
+            };
+            port@4 {
+                reg = <4>;
+            };
+            port@5 {
+                reg = <5>;
+                dpsub_dp_out: endpoint {
+                    remote-endpoint = <&dp_connector>;
+                };
+            };
+        };
     };
 
 ...
-- 
Regards,

Laurent Pinchart


^ permalink raw reply related	[flat|nested] 38+ messages in thread

* [PATCH 02/36] drm: xlnx: zynqmp_dpsub: Switch to atomic encoder enable/disable
  2021-08-09  1:34 [PATCH 00/36] drm: xlnx: zynqmp_dpsub: Initial live video input support Laurent Pinchart
  2021-08-09  1:34 ` [PATCH 01/36] dt-bindings: display: xlnx: zynqmp-dpsub: Add OF graph ports Laurent Pinchart
@ 2021-08-09  1:34 ` Laurent Pinchart
  2021-08-09  1:34 ` [PATCH 03/36] drm: xlnx: zynqmp_dpsub: Constify mode argument to function Laurent Pinchart
                   ` (33 subsequent siblings)
  35 siblings, 0 replies; 38+ messages in thread
From: Laurent Pinchart @ 2021-08-09  1:34 UTC (permalink / raw)
  To: dri-devel; +Cc: Michal Simek, Jianqiang Chen

To prepare for the transition to the DRM bridge API, switch the encoder
operations to the atomic versions of .enable() and .disable(). This
doesn't cause any functional change by itself.

Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
---
 drivers/gpu/drm/xlnx/zynqmp_dp.c | 10 ++++++----
 1 file changed, 6 insertions(+), 4 deletions(-)

diff --git a/drivers/gpu/drm/xlnx/zynqmp_dp.c b/drivers/gpu/drm/xlnx/zynqmp_dp.c
index 6f588dc09ba6..ace9fc731bfe 100644
--- a/drivers/gpu/drm/xlnx/zynqmp_dp.c
+++ b/drivers/gpu/drm/xlnx/zynqmp_dp.c
@@ -1399,7 +1399,8 @@ zynqmp_dp_connector_helper_funcs = {
  * DRM Encoder
  */
 
-static void zynqmp_dp_encoder_enable(struct drm_encoder *encoder)
+static void zynqmp_dp_encoder_atomic_enable(struct drm_encoder *encoder,
+					    struct drm_atomic_state *state)
 {
 	struct zynqmp_dp *dp = encoder_to_dp(encoder);
 	unsigned int i;
@@ -1431,7 +1432,8 @@ static void zynqmp_dp_encoder_enable(struct drm_encoder *encoder)
 	zynqmp_dp_write(dp, ZYNQMP_DP_MAIN_STREAM_ENABLE, 1);
 }
 
-static void zynqmp_dp_encoder_disable(struct drm_encoder *encoder)
+static void zynqmp_dp_encoder_atomic_disable(struct drm_encoder *encoder,
+					     struct drm_atomic_state *state)
 {
 	struct zynqmp_dp *dp = encoder_to_dp(encoder);
 
@@ -1508,8 +1510,8 @@ zynqmp_dp_encoder_atomic_check(struct drm_encoder *encoder,
 }
 
 static const struct drm_encoder_helper_funcs zynqmp_dp_encoder_helper_funcs = {
-	.enable			= zynqmp_dp_encoder_enable,
-	.disable		= zynqmp_dp_encoder_disable,
+	.atomic_enable		= zynqmp_dp_encoder_atomic_enable,
+	.atomic_disable		= zynqmp_dp_encoder_atomic_disable,
 	.atomic_mode_set	= zynqmp_dp_encoder_atomic_mode_set,
 	.atomic_check		= zynqmp_dp_encoder_atomic_check,
 };
-- 
Regards,

Laurent Pinchart


^ permalink raw reply related	[flat|nested] 38+ messages in thread

* [PATCH 03/36] drm: xlnx: zynqmp_dpsub: Constify mode argument to function
  2021-08-09  1:34 [PATCH 00/36] drm: xlnx: zynqmp_dpsub: Initial live video input support Laurent Pinchart
  2021-08-09  1:34 ` [PATCH 01/36] dt-bindings: display: xlnx: zynqmp-dpsub: Add OF graph ports Laurent Pinchart
  2021-08-09  1:34 ` [PATCH 02/36] drm: xlnx: zynqmp_dpsub: Switch to atomic encoder enable/disable Laurent Pinchart
@ 2021-08-09  1:34 ` Laurent Pinchart
  2021-08-09  1:34 ` [PATCH 04/36] drm: xlnx: zynqmp_dpsub: Create DRM bridge to model DP encoder Laurent Pinchart
                   ` (32 subsequent siblings)
  35 siblings, 0 replies; 38+ messages in thread
From: Laurent Pinchart @ 2021-08-09  1:34 UTC (permalink / raw)
  To: dri-devel; +Cc: Michal Simek, Jianqiang Chen

The zynqmp_dp_encoder_mode_set_transfer_unit() function takes a mode
pointer argument that it doesn't need to modify. Make it const.

Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
---
 drivers/gpu/drm/xlnx/zynqmp_dp.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/gpu/drm/xlnx/zynqmp_dp.c b/drivers/gpu/drm/xlnx/zynqmp_dp.c
index ace9fc731bfe..7768b45a0d73 100644
--- a/drivers/gpu/drm/xlnx/zynqmp_dp.c
+++ b/drivers/gpu/drm/xlnx/zynqmp_dp.c
@@ -1194,7 +1194,7 @@ static int zynqmp_dp_set_format(struct zynqmp_dp *dp,
  */
 static void
 zynqmp_dp_encoder_mode_set_transfer_unit(struct zynqmp_dp *dp,
-					 struct drm_display_mode *mode)
+					 const struct drm_display_mode *mode)
 {
 	u32 tu = ZYNQMP_DP_MSA_TRANSFER_UNIT_SIZE_TU_SIZE_DEF;
 	u32 bw, vid_kbytes, avg_bytes_per_tu, init_wait;
-- 
Regards,

Laurent Pinchart


^ permalink raw reply related	[flat|nested] 38+ messages in thread

* [PATCH 04/36] drm: xlnx: zynqmp_dpsub: Create DRM bridge to model DP encoder
  2021-08-09  1:34 [PATCH 00/36] drm: xlnx: zynqmp_dpsub: Initial live video input support Laurent Pinchart
                   ` (2 preceding siblings ...)
  2021-08-09  1:34 ` [PATCH 03/36] drm: xlnx: zynqmp_dpsub: Constify mode argument to function Laurent Pinchart
@ 2021-08-09  1:34 ` Laurent Pinchart
  2021-08-09  1:34 ` [PATCH 05/36] drm: xlnx: zynqmp_dpsub: Don't access connector in zynqmp_dp_set_format() Laurent Pinchart
                   ` (31 subsequent siblings)
  35 siblings, 0 replies; 38+ messages in thread
From: Laurent Pinchart @ 2021-08-09  1:34 UTC (permalink / raw)
  To: dri-devel; +Cc: Michal Simek, Jianqiang Chen

The DP encoder is currently modelled as a DRM encoder and DRM connector.
This doesn't support system configurations where the DP encoder is
driven by the FPGA programmable logic, using the live video input to the
DP subsystem. To enable such use cases, we need to model the encoder as
a DRM bridge.

As a first step, create a DRM bridge in the DP encoder driver. Move and
delegate the implementation of the DRM encoder and connector operations
to the bridge to prepare for the transition. The bridge will be
registered with the DRM core as a separate change.

Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
---
 drivers/gpu/drm/xlnx/zynqmp_dp.c | 333 +++++++++++++++++++++----------
 1 file changed, 225 insertions(+), 108 deletions(-)

diff --git a/drivers/gpu/drm/xlnx/zynqmp_dp.c b/drivers/gpu/drm/xlnx/zynqmp_dp.c
index 7768b45a0d73..2c82a5fbd43a 100644
--- a/drivers/gpu/drm/xlnx/zynqmp_dp.c
+++ b/drivers/gpu/drm/xlnx/zynqmp_dp.c
@@ -284,6 +284,7 @@ struct zynqmp_dp_config {
  * @iomem: device I/O memory for register access
  * @reset: reset controller
  * @irq: irq
+ * @bridge: DRM bridge for the DP encoder
  * @config: IP core configuration from DTS
  * @aux: aux channel
  * @phy: PHY handles for DP lanes
@@ -306,6 +307,8 @@ struct zynqmp_dp {
 	struct reset_control *reset;
 	int irq;
 
+	struct drm_bridge bridge;
+
 	struct zynqmp_dp_config config;
 	struct drm_dp_aux aux;
 	struct phy *phy[ZYNQMP_DP_MAX_LANES];
@@ -330,6 +333,11 @@ static inline struct zynqmp_dp *connector_to_dp(struct drm_connector *connector)
 	return container_of(connector, struct zynqmp_dp, connector);
 }
 
+static inline struct zynqmp_dp *bridge_to_dp(struct drm_bridge *bridge)
+{
+	return container_of(bridge, struct zynqmp_dp, bridge);
+}
+
 static void zynqmp_dp_write(struct zynqmp_dp *dp, int offset, u32 val)
 {
 	writel(val, dp->iomem + offset);
@@ -1254,7 +1262,7 @@ static void zynqmp_dp_encoder_mode_set_stream(struct zynqmp_dp *dp,
 	zynqmp_dp_write(dp, ZYNQMP_DP_MAIN_STREAM_VSTART,
 			mode->vtotal - mode->vsync_start);
 
-	/* In synchronous mode, set the diviers */
+	/* In synchronous mode, set the dividers */
 	if (dp->config.misc0 & ZYNQMP_DP_MAIN_STREAM_MISC0_SYNC_LOCK) {
 		reg = drm_dp_bw_code_to_link_rate(dp->mode.bw_code);
 		zynqmp_dp_write(dp, ZYNQMP_DP_MAIN_STREAM_N_VID, reg);
@@ -1280,13 +1288,167 @@ static void zynqmp_dp_encoder_mode_set_stream(struct zynqmp_dp *dp,
 }
 
 /* -----------------------------------------------------------------------------
- * DRM Connector
+ * DRM Bridge
  */
 
-static enum drm_connector_status
-zynqmp_dp_connector_detect(struct drm_connector *connector, bool force)
+static int zynqmp_dp_bridge_attach(struct drm_bridge *bridge,
+				   enum drm_bridge_attach_flags flags)
 {
-	struct zynqmp_dp *dp = connector_to_dp(connector);
+	return 0;
+}
+
+static int zynqmp_dp_bridge_mode_valid(struct drm_bridge *bridge,
+				       const struct drm_display_info *info,
+				       const struct drm_display_mode *mode)
+{
+	struct zynqmp_dp *dp = bridge_to_dp(bridge);
+	int rate;
+
+	if (mode->clock > ZYNQMP_MAX_FREQ) {
+		dev_dbg(dp->dev, "filtered mode %s for high pixel rate\n",
+			mode->name);
+		drm_mode_debug_printmodeline(mode);
+		return MODE_CLOCK_HIGH;
+	}
+
+	/* Check with link rate and lane count */
+	rate = zynqmp_dp_max_rate(dp->link_config.max_rate,
+				  dp->link_config.max_lanes, dp->config.bpp);
+	if (mode->clock > rate) {
+		dev_dbg(dp->dev, "filtered mode %s for high pixel rate\n",
+			mode->name);
+		drm_mode_debug_printmodeline(mode);
+		return MODE_CLOCK_HIGH;
+	}
+
+	return MODE_OK;
+}
+
+static void zynqmp_dp_bridge_atomic_enable(struct drm_bridge *bridge,
+					   struct drm_bridge_state *old_bridge_state)
+{
+	struct zynqmp_dp *dp = bridge_to_dp(bridge);
+	struct drm_atomic_state *state = old_bridge_state->base.state;
+	const struct drm_crtc_state *crtc_state;
+	const struct drm_display_mode *adjusted_mode;
+	const struct drm_display_mode *mode;
+	struct drm_connector *connector;
+	struct drm_crtc *crtc;
+	unsigned int i;
+	int rate;
+	int ret;
+
+	pm_runtime_get_sync(dp->dev);
+
+	/*
+	 * Retrieve the CRTC mode and adjusted mode. This requires a little
+	 * dance to go from the bridge to the encoder, to the connector and to
+	 * the CRTC.
+	 */
+	connector = drm_atomic_get_new_connector_for_encoder(state,
+							     bridge->encoder);
+	crtc = drm_atomic_get_new_connector_state(state, connector)->crtc;
+	crtc_state = drm_atomic_get_new_crtc_state(state, crtc);
+	adjusted_mode = &crtc_state->adjusted_mode;
+	mode = &crtc_state->mode;
+
+	zynqmp_dp_set_format(dp, ZYNQMP_DPSUB_FORMAT_RGB, 8);
+
+	/* Check again as bpp or format might have been changed */
+	rate = zynqmp_dp_max_rate(dp->link_config.max_rate,
+				  dp->link_config.max_lanes, dp->config.bpp);
+	if (mode->clock > rate) {
+		dev_err(dp->dev, "mode %s has too high pixel rate\n",
+			mode->name);
+		drm_mode_debug_printmodeline(mode);
+	}
+
+	/* Configure the mode */
+	ret = zynqmp_dp_mode_configure(dp, adjusted_mode->clock, 0);
+	if (ret < 0) {
+		pm_runtime_put_sync(dp->dev);
+		return;
+	}
+
+	zynqmp_dp_encoder_mode_set_transfer_unit(dp, adjusted_mode);
+	zynqmp_dp_encoder_mode_set_stream(dp, adjusted_mode);
+
+	/* Enable the encoder */
+	dp->enabled = true;
+	zynqmp_dp_update_misc(dp);
+	if (zynqmp_disp_audio_enabled(dp->dpsub->disp))
+		zynqmp_dp_write(dp, ZYNQMP_DP_TX_AUDIO_CONTROL, 1);
+	zynqmp_dp_write(dp, ZYNQMP_DP_TX_PHY_POWER_DOWN, 0);
+	if (dp->status == connector_status_connected) {
+		for (i = 0; i < 3; i++) {
+			ret = drm_dp_dpcd_writeb(&dp->aux, DP_SET_POWER,
+						 DP_SET_POWER_D0);
+			if (ret == 1)
+				break;
+			usleep_range(300, 500);
+		}
+		/* Some monitors take time to wake up properly */
+		msleep(zynqmp_dp_power_on_delay_ms);
+	}
+	if (ret != 1)
+		dev_dbg(dp->dev, "DP aux failed\n");
+	else
+		zynqmp_dp_train_loop(dp);
+	zynqmp_dp_write(dp, ZYNQMP_DP_SOFTWARE_RESET,
+			ZYNQMP_DP_SOFTWARE_RESET_ALL);
+	zynqmp_dp_write(dp, ZYNQMP_DP_MAIN_STREAM_ENABLE, 1);
+}
+
+static void zynqmp_dp_bridge_atomic_disable(struct drm_bridge *bridge,
+					    struct drm_bridge_state *old_bridge_state)
+{
+	struct zynqmp_dp *dp = bridge_to_dp(bridge);
+
+	dp->enabled = false;
+	cancel_delayed_work(&dp->hpd_work);
+	zynqmp_dp_write(dp, ZYNQMP_DP_MAIN_STREAM_ENABLE, 0);
+	drm_dp_dpcd_writeb(&dp->aux, DP_SET_POWER, DP_SET_POWER_D3);
+	zynqmp_dp_write(dp, ZYNQMP_DP_TX_PHY_POWER_DOWN,
+			ZYNQMP_DP_TX_PHY_POWER_DOWN_ALL);
+	if (zynqmp_disp_audio_enabled(dp->dpsub->disp))
+		zynqmp_dp_write(dp, ZYNQMP_DP_TX_AUDIO_CONTROL, 0);
+	pm_runtime_put_sync(dp->dev);
+}
+
+#define ZYNQMP_DP_MIN_H_BACKPORCH	20
+
+static int zynqmp_dp_bridge_atomic_check(struct drm_bridge *bridge,
+					 struct drm_bridge_state *bridge_state,
+					 struct drm_crtc_state *crtc_state,
+					 struct drm_connector_state *conn_state)
+{
+	struct zynqmp_dp *dp = bridge_to_dp(bridge);
+	struct drm_display_mode *mode = &crtc_state->mode;
+	struct drm_display_mode *adjusted_mode = &crtc_state->adjusted_mode;
+	int diff = mode->htotal - mode->hsync_end;
+
+	/*
+	 * ZynqMP DP requires horizontal backporch to be greater than 12.
+	 * This limitation may not be compatible with the sink device.
+	 */
+	if (diff < ZYNQMP_DP_MIN_H_BACKPORCH) {
+		int vrefresh = (adjusted_mode->clock * 1000) /
+			       (adjusted_mode->vtotal * adjusted_mode->htotal);
+
+		dev_dbg(dp->dev, "hbackporch adjusted: %d to %d",
+			diff, ZYNQMP_DP_MIN_H_BACKPORCH - diff);
+		diff = ZYNQMP_DP_MIN_H_BACKPORCH - diff;
+		adjusted_mode->htotal += diff;
+		adjusted_mode->clock = adjusted_mode->vtotal *
+				       adjusted_mode->htotal * vrefresh / 1000;
+	}
+
+	return 0;
+}
+
+static enum drm_connector_status zynqmp_dp_bridge_detect(struct drm_bridge *bridge)
+{
+	struct zynqmp_dp *dp = bridge_to_dp(bridge);
 	struct zynqmp_dp_link_config *link_config = &dp->link_config;
 	u32 state, i;
 	int ret;
@@ -1326,13 +1488,46 @@ zynqmp_dp_connector_detect(struct drm_connector *connector, bool force)
 	return connector_status_disconnected;
 }
 
+static struct edid *zynqmp_dp_bridge_get_edid(struct drm_bridge *bridge,
+					      struct drm_connector *connector)
+{
+	struct zynqmp_dp *dp = bridge_to_dp(bridge);
+
+	return drm_get_edid(connector, &dp->aux.ddc);
+}
+
+static const struct drm_bridge_funcs zynqmp_dp_bridge_funcs = {
+	.attach = zynqmp_dp_bridge_attach,
+	.mode_valid = zynqmp_dp_bridge_mode_valid,
+	.atomic_enable = zynqmp_dp_bridge_atomic_enable,
+	.atomic_disable = zynqmp_dp_bridge_atomic_disable,
+	.atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state,
+	.atomic_destroy_state = drm_atomic_helper_bridge_destroy_state,
+	.atomic_reset = drm_atomic_helper_bridge_reset,
+	.atomic_check = zynqmp_dp_bridge_atomic_check,
+	.detect = zynqmp_dp_bridge_detect,
+	.get_edid = zynqmp_dp_bridge_get_edid,
+};
+
+/* -----------------------------------------------------------------------------
+ * DRM Connector
+ */
+
+static enum drm_connector_status
+zynqmp_dp_connector_detect(struct drm_connector *connector, bool force)
+{
+	struct zynqmp_dp *dp = connector_to_dp(connector);
+
+	return zynqmp_dp_bridge_detect(&dp->bridge);
+}
+
 static int zynqmp_dp_connector_get_modes(struct drm_connector *connector)
 {
 	struct zynqmp_dp *dp = connector_to_dp(connector);
 	struct edid *edid;
 	int ret;
 
-	edid = drm_get_edid(connector, &dp->aux.ddc);
+	edid = zynqmp_dp_bridge_get_edid(&dp->bridge, connector);
 	if (!edid)
 		return 0;
 
@@ -1355,28 +1550,9 @@ static int zynqmp_dp_connector_mode_valid(struct drm_connector *connector,
 					  struct drm_display_mode *mode)
 {
 	struct zynqmp_dp *dp = connector_to_dp(connector);
-	u8 max_lanes = dp->link_config.max_lanes;
-	u8 bpp = dp->config.bpp;
-	int max_rate = dp->link_config.max_rate;
-	int rate;
 
-	if (mode->clock > ZYNQMP_MAX_FREQ) {
-		dev_dbg(dp->dev, "filtered the mode, %s,for high pixel rate\n",
-			mode->name);
-		drm_mode_debug_printmodeline(mode);
-		return MODE_CLOCK_HIGH;
-	}
-
-	/* Check with link rate and lane count */
-	rate = zynqmp_dp_max_rate(max_rate, max_lanes, bpp);
-	if (mode->clock > rate) {
-		dev_dbg(dp->dev, "filtered the mode, %s,for high pixel rate\n",
-			mode->name);
-		drm_mode_debug_printmodeline(mode);
-		return MODE_CLOCK_HIGH;
-	}
-
-	return MODE_OK;
+	return zynqmp_dp_bridge_mode_valid(&dp->bridge, &connector->display_info,
+					   mode);
 }
 
 static const struct drm_connector_funcs zynqmp_dp_connector_funcs = {
@@ -1403,49 +1579,20 @@ static void zynqmp_dp_encoder_atomic_enable(struct drm_encoder *encoder,
 					    struct drm_atomic_state *state)
 {
 	struct zynqmp_dp *dp = encoder_to_dp(encoder);
-	unsigned int i;
-	int ret = 0;
+	struct drm_bridge_state bridge_state;
 
-	pm_runtime_get_sync(dp->dev);
-	dp->enabled = true;
-	zynqmp_dp_update_misc(dp);
-	if (zynqmp_disp_audio_enabled(dp->dpsub->disp))
-		zynqmp_dp_write(dp, ZYNQMP_DP_TX_AUDIO_CONTROL, 1);
-	zynqmp_dp_write(dp, ZYNQMP_DP_TX_PHY_POWER_DOWN, 0);
-	if (dp->status == connector_status_connected) {
-		for (i = 0; i < 3; i++) {
-			ret = drm_dp_dpcd_writeb(&dp->aux, DP_SET_POWER,
-						 DP_SET_POWER_D0);
-			if (ret == 1)
-				break;
-			usleep_range(300, 500);
-		}
-		/* Some monitors take time to wake up properly */
-		msleep(zynqmp_dp_power_on_delay_ms);
-	}
-	if (ret != 1)
-		dev_dbg(dp->dev, "DP aux failed\n");
-	else
-		zynqmp_dp_train_loop(dp);
-	zynqmp_dp_write(dp, ZYNQMP_DP_SOFTWARE_RESET,
-			ZYNQMP_DP_SOFTWARE_RESET_ALL);
-	zynqmp_dp_write(dp, ZYNQMP_DP_MAIN_STREAM_ENABLE, 1);
+	bridge_state.base.state = state;
+	zynqmp_dp_bridge_atomic_enable(&dp->bridge, &bridge_state);
 }
 
 static void zynqmp_dp_encoder_atomic_disable(struct drm_encoder *encoder,
 					     struct drm_atomic_state *state)
 {
 	struct zynqmp_dp *dp = encoder_to_dp(encoder);
+	struct drm_bridge_state bridge_state;
 
-	dp->enabled = false;
-	cancel_delayed_work(&dp->hpd_work);
-	zynqmp_dp_write(dp, ZYNQMP_DP_MAIN_STREAM_ENABLE, 0);
-	drm_dp_dpcd_writeb(&dp->aux, DP_SET_POWER, DP_SET_POWER_D3);
-	zynqmp_dp_write(dp, ZYNQMP_DP_TX_PHY_POWER_DOWN,
-			ZYNQMP_DP_TX_PHY_POWER_DOWN_ALL);
-	if (zynqmp_disp_audio_enabled(dp->dpsub->disp))
-		zynqmp_dp_write(dp, ZYNQMP_DP_TX_AUDIO_CONTROL, 0);
-	pm_runtime_put_sync(dp->dev);
+	bridge_state.base.state = state;
+	zynqmp_dp_bridge_atomic_disable(&dp->bridge, &bridge_state);
 }
 
 static void
@@ -1453,60 +1600,17 @@ zynqmp_dp_encoder_atomic_mode_set(struct drm_encoder *encoder,
 				  struct drm_crtc_state *crtc_state,
 				  struct drm_connector_state *connector_state)
 {
-	struct zynqmp_dp *dp = encoder_to_dp(encoder);
-	struct drm_display_mode *mode = &crtc_state->mode;
-	struct drm_display_mode *adjusted_mode = &crtc_state->adjusted_mode;
-	u8 max_lanes = dp->link_config.max_lanes;
-	u8 bpp = dp->config.bpp;
-	int rate, max_rate = dp->link_config.max_rate;
-	int ret;
-
-	zynqmp_dp_set_format(dp, ZYNQMP_DPSUB_FORMAT_RGB, 8);
-
-	/* Check again as bpp or format might have been chagned */
-	rate = zynqmp_dp_max_rate(max_rate, max_lanes, bpp);
-	if (mode->clock > rate) {
-		dev_err(dp->dev, "the mode, %s,has too high pixel rate\n",
-			mode->name);
-		drm_mode_debug_printmodeline(mode);
-	}
-
-	ret = zynqmp_dp_mode_configure(dp, adjusted_mode->clock, 0);
-	if (ret < 0)
-		return;
-
-	zynqmp_dp_encoder_mode_set_transfer_unit(dp, adjusted_mode);
-	zynqmp_dp_encoder_mode_set_stream(dp, adjusted_mode);
 }
 
-#define ZYNQMP_DP_MIN_H_BACKPORCH	20
-
 static int
 zynqmp_dp_encoder_atomic_check(struct drm_encoder *encoder,
 			       struct drm_crtc_state *crtc_state,
 			       struct drm_connector_state *conn_state)
 {
-	struct drm_display_mode *mode = &crtc_state->mode;
-	struct drm_display_mode *adjusted_mode = &crtc_state->adjusted_mode;
-	int diff = mode->htotal - mode->hsync_end;
+	struct zynqmp_dp *dp = encoder_to_dp(encoder);
 
-	/*
-	 * ZynqMP DP requires horizontal backporch to be greater than 12.
-	 * This limitation may not be compatible with the sink device.
-	 */
-	if (diff < ZYNQMP_DP_MIN_H_BACKPORCH) {
-		int vrefresh = (adjusted_mode->clock * 1000) /
-			       (adjusted_mode->vtotal * adjusted_mode->htotal);
-
-		dev_dbg(encoder->dev->dev, "hbackporch adjusted: %d to %d",
-			diff, ZYNQMP_DP_MIN_H_BACKPORCH - diff);
-		diff = ZYNQMP_DP_MIN_H_BACKPORCH - diff;
-		adjusted_mode->htotal += diff;
-		adjusted_mode->clock = adjusted_mode->vtotal *
-				       adjusted_mode->htotal * vrefresh / 1000;
-	}
-
-	return 0;
+	return zynqmp_dp_bridge_atomic_check(&dp->bridge, NULL, crtc_state,
+					     conn_state);
 }
 
 static const struct drm_encoder_helper_funcs zynqmp_dp_encoder_helper_funcs = {
@@ -1603,6 +1707,7 @@ static irqreturn_t zynqmp_dp_irq_handler(int irq, void *data)
 int zynqmp_dp_drm_init(struct zynqmp_dpsub *dpsub)
 {
 	struct zynqmp_dp *dp = dpsub->dp;
+	struct drm_bridge *bridge = &dp->bridge;
 	struct drm_encoder *encoder = &dp->encoder;
 	struct drm_connector *connector = &dp->connector;
 	int ret;
@@ -1610,6 +1715,18 @@ int zynqmp_dp_drm_init(struct zynqmp_dpsub *dpsub)
 	dp->config.misc0 &= ~ZYNQMP_DP_MAIN_STREAM_MISC0_SYNC_LOCK;
 	zynqmp_dp_set_format(dp, ZYNQMP_DPSUB_FORMAT_RGB, 8);
 
+	/*
+	 * Initialize the bridge. Setting the device and encoder manually is a
+	 * hack, to be removed once the bridge will get attached to the encoder
+	 * using the bridge API.
+	 */
+	bridge->dev = dp->drm;
+	bridge->encoder = &dp->encoder;
+	bridge->funcs = &zynqmp_dp_bridge_funcs;
+	bridge->ops = DRM_BRIDGE_OP_DETECT | DRM_BRIDGE_OP_EDID
+		    | DRM_BRIDGE_OP_HPD;
+	bridge->type = DRM_MODE_CONNECTOR_DisplayPort;
+
 	/* Create the DRM encoder and connector. */
 	encoder->possible_crtcs |= zynqmp_disp_get_crtc_mask(dpsub->disp);
 	drm_simple_encoder_init(dp->drm, encoder, DRM_MODE_ENCODER_TMDS);
-- 
Regards,

Laurent Pinchart


^ permalink raw reply related	[flat|nested] 38+ messages in thread

* [PATCH 05/36] drm: xlnx: zynqmp_dpsub: Don't access connector in zynqmp_dp_set_format()
  2021-08-09  1:34 [PATCH 00/36] drm: xlnx: zynqmp_dpsub: Initial live video input support Laurent Pinchart
                   ` (3 preceding siblings ...)
  2021-08-09  1:34 ` [PATCH 04/36] drm: xlnx: zynqmp_dpsub: Create DRM bridge to model DP encoder Laurent Pinchart
@ 2021-08-09  1:34 ` Laurent Pinchart
  2021-08-09  1:34 ` [PATCH 06/36] drm: xlnx: zynqmp_dpsub: Move connector registration to bridge attach Laurent Pinchart
                   ` (30 subsequent siblings)
  35 siblings, 0 replies; 38+ messages in thread
From: Laurent Pinchart @ 2021-08-09  1:34 UTC (permalink / raw)
  To: dri-devel; +Cc: Michal Simek, Jianqiang Chen

To prepare for the removal of the connector from the DP encoder, pass
the display info pointer to the zynqmp_dp_set_format() function instead
of accessing the connector internally. The display info is NULL when the
function is called at initialization time, as we have no display info at
that point. This doesn't change the existing behaviour, given that the
zynqmp_dp_set_format() was already handling this as a special case (the
display info isn't initialized at init time and is all zeroes).

Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
---
 drivers/gpu/drm/xlnx/zynqmp_dp.c | 15 ++++++++-------
 1 file changed, 8 insertions(+), 7 deletions(-)

diff --git a/drivers/gpu/drm/xlnx/zynqmp_dp.c b/drivers/gpu/drm/xlnx/zynqmp_dp.c
index 2c82a5fbd43a..689ac157381b 100644
--- a/drivers/gpu/drm/xlnx/zynqmp_dp.c
+++ b/drivers/gpu/drm/xlnx/zynqmp_dp.c
@@ -1108,6 +1108,7 @@ static void zynqmp_dp_update_misc(struct zynqmp_dp *dp)
 /**
  * zynqmp_dp_set_format - Set the input format
  * @dp: DisplayPort IP core structure
+ * @info: Display info
  * @format: input format
  * @bpc: bits per component
  *
@@ -1116,10 +1117,10 @@ static void zynqmp_dp_update_misc(struct zynqmp_dp *dp)
  * Return: 0 on success, or -EINVAL.
  */
 static int zynqmp_dp_set_format(struct zynqmp_dp *dp,
+				const struct drm_display_info *info,
 				enum zynqmp_dpsub_format format,
 				unsigned int bpc)
 {
-	static const struct drm_display_info *display;
 	struct zynqmp_dp_config *config = &dp->config;
 	unsigned int num_colors;
 
@@ -1152,12 +1153,11 @@ static int zynqmp_dp_set_format(struct zynqmp_dp *dp,
 		return -EINVAL;
 	}
 
-	display = &dp->connector.display_info;
-	if (display->bpc && bpc > display->bpc) {
+	if (info && info->bpc && bpc > info->bpc) {
 		dev_warn(dp->dev,
 			 "downgrading requested %ubpc to display limit %ubpc\n",
-			 bpc, display->bpc);
-		bpc = display->bpc;
+			 bpc, info->bpc);
+		bpc = info->bpc;
 	}
 
 	config->misc0 &= ~ZYNQMP_DP_MAIN_STREAM_MISC0_BPC_MASK;
@@ -1352,7 +1352,8 @@ static void zynqmp_dp_bridge_atomic_enable(struct drm_bridge *bridge,
 	adjusted_mode = &crtc_state->adjusted_mode;
 	mode = &crtc_state->mode;
 
-	zynqmp_dp_set_format(dp, ZYNQMP_DPSUB_FORMAT_RGB, 8);
+	zynqmp_dp_set_format(dp, &connector->display_info,
+			     ZYNQMP_DPSUB_FORMAT_RGB, 8);
 
 	/* Check again as bpp or format might have been changed */
 	rate = zynqmp_dp_max_rate(dp->link_config.max_rate,
@@ -1713,7 +1714,7 @@ int zynqmp_dp_drm_init(struct zynqmp_dpsub *dpsub)
 	int ret;
 
 	dp->config.misc0 &= ~ZYNQMP_DP_MAIN_STREAM_MISC0_SYNC_LOCK;
-	zynqmp_dp_set_format(dp, ZYNQMP_DPSUB_FORMAT_RGB, 8);
+	zynqmp_dp_set_format(dp, NULL, ZYNQMP_DPSUB_FORMAT_RGB, 8);
 
 	/*
 	 * Initialize the bridge. Setting the device and encoder manually is a
-- 
Regards,

Laurent Pinchart


^ permalink raw reply related	[flat|nested] 38+ messages in thread

* [PATCH 06/36] drm: xlnx: zynqmp_dpsub: Move connector registration to bridge attach
  2021-08-09  1:34 [PATCH 00/36] drm: xlnx: zynqmp_dpsub: Initial live video input support Laurent Pinchart
                   ` (4 preceding siblings ...)
  2021-08-09  1:34 ` [PATCH 05/36] drm: xlnx: zynqmp_dpsub: Don't access connector in zynqmp_dp_set_format() Laurent Pinchart
@ 2021-08-09  1:34 ` Laurent Pinchart
  2021-08-09  1:34 ` [PATCH 07/36] drm: xlnx: zynqmp_dpsub: Move encoder to DPSUB core Laurent Pinchart
                   ` (29 subsequent siblings)
  35 siblings, 0 replies; 38+ messages in thread
From: Laurent Pinchart @ 2021-08-09  1:34 UTC (permalink / raw)
  To: dri-devel; +Cc: Michal Simek, Jianqiang Chen

Connector creation requires the DRM encoder, and it thus typically
performed in the bridge attach operation. Move it there, to prepare for
registration of the DRM bridge. For now the zynqmp_dp_bridge_attach() is
called manually at initialization time.

Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
---
 drivers/gpu/drm/xlnx/zynqmp_dp.c | 37 +++++++++++++++++++++-----------
 1 file changed, 24 insertions(+), 13 deletions(-)

diff --git a/drivers/gpu/drm/xlnx/zynqmp_dp.c b/drivers/gpu/drm/xlnx/zynqmp_dp.c
index 689ac157381b..838585bb84ab 100644
--- a/drivers/gpu/drm/xlnx/zynqmp_dp.c
+++ b/drivers/gpu/drm/xlnx/zynqmp_dp.c
@@ -1291,9 +1291,30 @@ static void zynqmp_dp_encoder_mode_set_stream(struct zynqmp_dp *dp,
  * DRM Bridge
  */
 
+static const struct drm_connector_funcs zynqmp_dp_connector_funcs;
+static const struct drm_connector_helper_funcs zynqmp_dp_connector_helper_funcs;
+
 static int zynqmp_dp_bridge_attach(struct drm_bridge *bridge,
 				   enum drm_bridge_attach_flags flags)
 {
+	struct zynqmp_dp *dp = bridge_to_dp(bridge);
+	struct drm_connector *connector = &dp->connector;
+	int ret;
+
+	/* Create the DRM connector. */
+	connector->polled = DRM_CONNECTOR_POLL_HPD;
+	ret = drm_connector_init(dp->drm, connector,
+				 &zynqmp_dp_connector_funcs,
+				 DRM_MODE_CONNECTOR_DisplayPort);
+	if (ret) {
+		dev_err(dp->dev, "failed to create the DRM connector\n");
+		return ret;
+	}
+
+	drm_connector_helper_add(connector, &zynqmp_dp_connector_helper_funcs);
+	drm_connector_register(connector);
+	drm_connector_attach_encoder(connector, bridge->encoder);
+
 	return 0;
 }
 
@@ -1710,7 +1731,6 @@ int zynqmp_dp_drm_init(struct zynqmp_dpsub *dpsub)
 	struct zynqmp_dp *dp = dpsub->dp;
 	struct drm_bridge *bridge = &dp->bridge;
 	struct drm_encoder *encoder = &dp->encoder;
-	struct drm_connector *connector = &dp->connector;
 	int ret;
 
 	dp->config.misc0 &= ~ZYNQMP_DP_MAIN_STREAM_MISC0_SYNC_LOCK;
@@ -1728,23 +1748,14 @@ int zynqmp_dp_drm_init(struct zynqmp_dpsub *dpsub)
 		    | DRM_BRIDGE_OP_HPD;
 	bridge->type = DRM_MODE_CONNECTOR_DisplayPort;
 
-	/* Create the DRM encoder and connector. */
+	/* Create the DRM encoder and attach the bridge. */
 	encoder->possible_crtcs |= zynqmp_disp_get_crtc_mask(dpsub->disp);
 	drm_simple_encoder_init(dp->drm, encoder, DRM_MODE_ENCODER_TMDS);
 	drm_encoder_helper_add(encoder, &zynqmp_dp_encoder_helper_funcs);
 
-	connector->polled = DRM_CONNECTOR_POLL_HPD;
-	ret = drm_connector_init(encoder->dev, connector,
-				 &zynqmp_dp_connector_funcs,
-				 DRM_MODE_CONNECTOR_DisplayPort);
-	if (ret) {
-		dev_err(dp->dev, "failed to create the DRM connector\n");
+	ret = zynqmp_dp_bridge_attach(bridge, 0);
+	if (ret < 0)
 		return ret;
-	}
-
-	drm_connector_helper_add(connector, &zynqmp_dp_connector_helper_funcs);
-	drm_connector_register(connector);
-	drm_connector_attach_encoder(connector, encoder);
 
 	/* Initialize and register the AUX adapter. */
 	ret = zynqmp_dp_aux_init(dp);
-- 
Regards,

Laurent Pinchart


^ permalink raw reply related	[flat|nested] 38+ messages in thread

* [PATCH 07/36] drm: xlnx: zynqmp_dpsub: Move encoder to DPSUB core
  2021-08-09  1:34 [PATCH 00/36] drm: xlnx: zynqmp_dpsub: Initial live video input support Laurent Pinchart
                   ` (5 preceding siblings ...)
  2021-08-09  1:34 ` [PATCH 06/36] drm: xlnx: zynqmp_dpsub: Move connector registration to bridge attach Laurent Pinchart
@ 2021-08-09  1:34 ` Laurent Pinchart
  2021-08-09  1:34 ` [PATCH 08/36] drm: xlnx: zynqmp_dpsub: Attach to the next bridge Laurent Pinchart
                   ` (28 subsequent siblings)
  35 siblings, 0 replies; 38+ messages in thread
From: Laurent Pinchart @ 2021-08-09  1:34 UTC (permalink / raw)
  To: dri-devel; +Cc: Michal Simek, Jianqiang Chen

As part of the transitition of the DP encoder to a DRM bridge, turn the
DRM encoder into a dummy encoder and move it out of the DP code, to the
DPSUB core. DP encoder operations are handled by the DP bridge, which is
now attached to the encoder.

Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
---
 drivers/gpu/drm/xlnx/zynqmp_dp.c    | 79 ++---------------------------
 drivers/gpu/drm/xlnx/zynqmp_dpsub.c | 16 +++++-
 drivers/gpu/drm/xlnx/zynqmp_dpsub.h |  8 +++
 3 files changed, 25 insertions(+), 78 deletions(-)

diff --git a/drivers/gpu/drm/xlnx/zynqmp_dp.c b/drivers/gpu/drm/xlnx/zynqmp_dp.c
index 838585bb84ab..27b5277f8f64 100644
--- a/drivers/gpu/drm/xlnx/zynqmp_dp.c
+++ b/drivers/gpu/drm/xlnx/zynqmp_dp.c
@@ -15,12 +15,10 @@
 #include <drm/drm_device.h>
 #include <drm/drm_dp_helper.h>
 #include <drm/drm_edid.h>
-#include <drm/drm_encoder.h>
 #include <drm/drm_managed.h>
 #include <drm/drm_modes.h>
 #include <drm/drm_of.h>
 #include <drm/drm_probe_helper.h>
-#include <drm/drm_simple_kms_helper.h>
 
 #include <linux/clk.h>
 #include <linux/delay.h>
@@ -276,7 +274,6 @@ struct zynqmp_dp_config {
 
 /**
  * struct zynqmp_dp - Xilinx DisplayPort core
- * @encoder: the drm encoder structure
  * @connector: the drm connector structure
  * @dev: device structure
  * @dpsub: Display subsystem
@@ -298,7 +295,6 @@ struct zynqmp_dp_config {
  * @train_set: set of training data
  */
 struct zynqmp_dp {
-	struct drm_encoder encoder;
 	struct drm_connector connector;
 	struct device *dev;
 	struct zynqmp_dpsub *dpsub;
@@ -323,11 +319,6 @@ struct zynqmp_dp {
 	u8 train_set[ZYNQMP_DP_MAX_LANES];
 };
 
-static inline struct zynqmp_dp *encoder_to_dp(struct drm_encoder *encoder)
-{
-	return container_of(encoder, struct zynqmp_dp, encoder);
-}
-
 static inline struct zynqmp_dp *connector_to_dp(struct drm_connector *connector)
 {
 	return container_of(connector, struct zynqmp_dp, connector);
@@ -1565,7 +1556,7 @@ zynqmp_dp_connector_best_encoder(struct drm_connector *connector)
 {
 	struct zynqmp_dp *dp = connector_to_dp(connector);
 
-	return &dp->encoder;
+	return &dp->dpsub->encoder;
 }
 
 static int zynqmp_dp_connector_mode_valid(struct drm_connector *connector,
@@ -1593,55 +1584,6 @@ zynqmp_dp_connector_helper_funcs = {
 	.mode_valid	= zynqmp_dp_connector_mode_valid,
 };
 
-/* -----------------------------------------------------------------------------
- * DRM Encoder
- */
-
-static void zynqmp_dp_encoder_atomic_enable(struct drm_encoder *encoder,
-					    struct drm_atomic_state *state)
-{
-	struct zynqmp_dp *dp = encoder_to_dp(encoder);
-	struct drm_bridge_state bridge_state;
-
-	bridge_state.base.state = state;
-	zynqmp_dp_bridge_atomic_enable(&dp->bridge, &bridge_state);
-}
-
-static void zynqmp_dp_encoder_atomic_disable(struct drm_encoder *encoder,
-					     struct drm_atomic_state *state)
-{
-	struct zynqmp_dp *dp = encoder_to_dp(encoder);
-	struct drm_bridge_state bridge_state;
-
-	bridge_state.base.state = state;
-	zynqmp_dp_bridge_atomic_disable(&dp->bridge, &bridge_state);
-}
-
-static void
-zynqmp_dp_encoder_atomic_mode_set(struct drm_encoder *encoder,
-				  struct drm_crtc_state *crtc_state,
-				  struct drm_connector_state *connector_state)
-{
-}
-
-static int
-zynqmp_dp_encoder_atomic_check(struct drm_encoder *encoder,
-			       struct drm_crtc_state *crtc_state,
-			       struct drm_connector_state *conn_state)
-{
-	struct zynqmp_dp *dp = encoder_to_dp(encoder);
-
-	return zynqmp_dp_bridge_atomic_check(&dp->bridge, NULL, crtc_state,
-					     conn_state);
-}
-
-static const struct drm_encoder_helper_funcs zynqmp_dp_encoder_helper_funcs = {
-	.atomic_enable		= zynqmp_dp_encoder_atomic_enable,
-	.atomic_disable		= zynqmp_dp_encoder_atomic_disable,
-	.atomic_mode_set	= zynqmp_dp_encoder_atomic_mode_set,
-	.atomic_check		= zynqmp_dp_encoder_atomic_check,
-};
-
 /* -----------------------------------------------------------------------------
  * Interrupt Handling
  */
@@ -1730,32 +1672,17 @@ int zynqmp_dp_drm_init(struct zynqmp_dpsub *dpsub)
 {
 	struct zynqmp_dp *dp = dpsub->dp;
 	struct drm_bridge *bridge = &dp->bridge;
-	struct drm_encoder *encoder = &dp->encoder;
 	int ret;
 
 	dp->config.misc0 &= ~ZYNQMP_DP_MAIN_STREAM_MISC0_SYNC_LOCK;
 	zynqmp_dp_set_format(dp, NULL, ZYNQMP_DPSUB_FORMAT_RGB, 8);
 
-	/*
-	 * Initialize the bridge. Setting the device and encoder manually is a
-	 * hack, to be removed once the bridge will get attached to the encoder
-	 * using the bridge API.
-	 */
-	bridge->dev = dp->drm;
-	bridge->encoder = &dp->encoder;
+	/* Initialize the bridge. */
 	bridge->funcs = &zynqmp_dp_bridge_funcs;
 	bridge->ops = DRM_BRIDGE_OP_DETECT | DRM_BRIDGE_OP_EDID
 		    | DRM_BRIDGE_OP_HPD;
 	bridge->type = DRM_MODE_CONNECTOR_DisplayPort;
-
-	/* Create the DRM encoder and attach the bridge. */
-	encoder->possible_crtcs |= zynqmp_disp_get_crtc_mask(dpsub->disp);
-	drm_simple_encoder_init(dp->drm, encoder, DRM_MODE_ENCODER_TMDS);
-	drm_encoder_helper_add(encoder, &zynqmp_dp_encoder_helper_funcs);
-
-	ret = zynqmp_dp_bridge_attach(bridge, 0);
-	if (ret < 0)
-		return ret;
+	dpsub->bridge = bridge;
 
 	/* Initialize and register the AUX adapter. */
 	ret = zynqmp_dp_aux_init(dp);
diff --git a/drivers/gpu/drm/xlnx/zynqmp_dpsub.c b/drivers/gpu/drm/xlnx/zynqmp_dpsub.c
index ac37053412a1..3a3a85821e99 100644
--- a/drivers/gpu/drm/xlnx/zynqmp_dpsub.c
+++ b/drivers/gpu/drm/xlnx/zynqmp_dpsub.c
@@ -17,6 +17,7 @@
 #include <linux/pm_runtime.h>
 
 #include <drm/drm_atomic_helper.h>
+#include <drm/drm_bridge.h>
 #include <drm/drm_device.h>
 #include <drm/drm_drv.h>
 #include <drm/drm_fb_helper.h>
@@ -26,6 +27,7 @@
 #include <drm/drm_managed.h>
 #include <drm/drm_mode_config.h>
 #include <drm/drm_probe_helper.h>
+#include <drm/drm_simple_kms_helper.h>
 #include <drm/drm_vblank.h>
 
 #include "zynqmp_disp.h"
@@ -93,6 +95,7 @@ static const struct drm_driver zynqmp_dpsub_drm_driver = {
 
 static int zynqmp_dpsub_drm_init(struct zynqmp_dpsub *dpsub)
 {
+	struct drm_encoder *encoder = &dpsub->encoder;
 	struct drm_device *drm = &dpsub->drm;
 	int ret;
 
@@ -115,8 +118,7 @@ static int zynqmp_dpsub_drm_init(struct zynqmp_dpsub *dpsub)
 
 	/*
 	 * Initialize the DISP and DP components. This will creates planes,
-	 * CRTC, encoder and connector. The DISP should be initialized first as
-	 * the DP encoder needs the CRTC.
+	 * CRTC, and a bridge for the DP encoder.
 	 */
 	ret = zynqmp_disp_drm_init(dpsub);
 	if (ret)
@@ -126,6 +128,16 @@ static int zynqmp_dpsub_drm_init(struct zynqmp_dpsub *dpsub)
 	if (ret)
 		goto err_poll_fini;
 
+	/* Create the encoder and attach the bridge. */
+	encoder->possible_crtcs |= zynqmp_disp_get_crtc_mask(dpsub->disp);
+	drm_simple_encoder_init(drm, encoder, DRM_MODE_ENCODER_NONE);
+
+	ret = drm_bridge_attach(encoder, dpsub->bridge, NULL, 0);
+	if (ret) {
+		dev_err(dpsub->dev, "failed to attach bridge to encoder\n");
+		goto err_poll_fini;
+	}
+
 	/* Reset all components and register the DRM device. */
 	drm_mode_config_reset(drm);
 
diff --git a/drivers/gpu/drm/xlnx/zynqmp_dpsub.h b/drivers/gpu/drm/xlnx/zynqmp_dpsub.h
index c04026d82639..66820bbc4507 100644
--- a/drivers/gpu/drm/xlnx/zynqmp_dpsub.h
+++ b/drivers/gpu/drm/xlnx/zynqmp_dpsub.h
@@ -12,8 +12,11 @@
 #ifndef _ZYNQMP_DPSUB_H_
 #define _ZYNQMP_DPSUB_H_
 
+#include <drm/drm_encoder.h>
+
 struct clk;
 struct device;
+struct drm_bridge;
 struct drm_device;
 struct zynqmp_disp;
 struct zynqmp_dp;
@@ -30,6 +33,8 @@ enum zynqmp_dpsub_format {
  * @drm: The DRM/KMS device
  * @dev: The physical device
  * @apb_clk: The APB clock
+ * @encoder: The dummy DRM encoder
+ * @bridge: The DP encoder bridge
  * @disp: The display controller
  * @dp: The DisplayPort controller
  * @dma_align: DMA alignment constraint (must be a power of 2)
@@ -40,6 +45,9 @@ struct zynqmp_dpsub {
 
 	struct clk *apb_clk;
 
+	struct drm_encoder encoder;
+	struct drm_bridge *bridge;
+
 	struct zynqmp_disp *disp;
 	struct zynqmp_dp *dp;
 
-- 
Regards,

Laurent Pinchart


^ permalink raw reply related	[flat|nested] 38+ messages in thread

* [PATCH 08/36] drm: xlnx: zynqmp_dpsub: Attach to the next bridge
  2021-08-09  1:34 [PATCH 00/36] drm: xlnx: zynqmp_dpsub: Initial live video input support Laurent Pinchart
                   ` (6 preceding siblings ...)
  2021-08-09  1:34 ` [PATCH 07/36] drm: xlnx: zynqmp_dpsub: Move encoder to DPSUB core Laurent Pinchart
@ 2021-08-09  1:34 ` Laurent Pinchart
  2021-08-09  1:34 ` [PATCH 09/36] drm: xlnx: zynqmp_dpsub: Use DRM connector bridge helper Laurent Pinchart
                   ` (27 subsequent siblings)
  35 siblings, 0 replies; 38+ messages in thread
From: Laurent Pinchart @ 2021-08-09  1:34 UTC (permalink / raw)
  To: dri-devel; +Cc: Michal Simek, Jianqiang Chen

The next component in the display chain, after the DP encoder, is most
likely a DP connector. The display connector driver registers a bridge
for it. That bridge doesn't need to be controlled, but is needed in
order to use the DRM connector bridge helper. Retrieve it at init time,
and attach to it in the DP bridge attach handler.

Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
---
 drivers/gpu/drm/xlnx/zynqmp_dp.c | 18 ++++++++++++++++++
 1 file changed, 18 insertions(+)

diff --git a/drivers/gpu/drm/xlnx/zynqmp_dp.c b/drivers/gpu/drm/xlnx/zynqmp_dp.c
index 27b5277f8f64..244628497e98 100644
--- a/drivers/gpu/drm/xlnx/zynqmp_dp.c
+++ b/drivers/gpu/drm/xlnx/zynqmp_dp.c
@@ -282,6 +282,7 @@ struct zynqmp_dp_config {
  * @reset: reset controller
  * @irq: irq
  * @bridge: DRM bridge for the DP encoder
+ * @next_bridge: The downstream bridge
  * @config: IP core configuration from DTS
  * @aux: aux channel
  * @phy: PHY handles for DP lanes
@@ -304,6 +305,7 @@ struct zynqmp_dp {
 	int irq;
 
 	struct drm_bridge bridge;
+	struct drm_bridge *next_bridge;
 
 	struct zynqmp_dp_config config;
 	struct drm_dp_aux aux;
@@ -1306,6 +1308,13 @@ static int zynqmp_dp_bridge_attach(struct drm_bridge *bridge,
 	drm_connector_register(connector);
 	drm_connector_attach_encoder(connector, bridge->encoder);
 
+	if (dp->next_bridge) {
+		ret = drm_bridge_attach(bridge->encoder, dp->next_bridge,
+					bridge, DRM_BRIDGE_ATTACH_NO_CONNECTOR);
+		if (ret < 0)
+			return ret;
+	}
+
 	return 0;
 }
 
@@ -1743,6 +1752,15 @@ int zynqmp_dp_probe(struct zynqmp_dpsub *dpsub, struct drm_device *drm)
 	if (ret)
 		goto err_reset;
 
+	/*
+	 * Acquire the next bridge in the chain. Ignore errors caused by port@5
+	 * not being connected for backward-compatibility with older DTs.
+	 */
+	ret = drm_of_find_panel_or_bridge(dp->dev->of_node, 5, 0, NULL,
+					  &dp->next_bridge);
+	if (ret < 0 && ret != -ENODEV)
+		goto err_reset;
+
 	/* Initialize the hardware. */
 	zynqmp_dp_write(dp, ZYNQMP_DP_TX_PHY_POWER_DOWN,
 			ZYNQMP_DP_TX_PHY_POWER_DOWN_ALL);
-- 
Regards,

Laurent Pinchart


^ permalink raw reply related	[flat|nested] 38+ messages in thread

* [PATCH 09/36] drm: xlnx: zynqmp_dpsub: Use DRM connector bridge helper
  2021-08-09  1:34 [PATCH 00/36] drm: xlnx: zynqmp_dpsub: Initial live video input support Laurent Pinchart
                   ` (7 preceding siblings ...)
  2021-08-09  1:34 ` [PATCH 08/36] drm: xlnx: zynqmp_dpsub: Attach to the next bridge Laurent Pinchart
@ 2021-08-09  1:34 ` Laurent Pinchart
  2021-08-09  1:34 ` [PATCH 10/36] drm: xlnx: zynqmp_dpsub: Report HPD through the bridge Laurent Pinchart
                   ` (26 subsequent siblings)
  35 siblings, 0 replies; 38+ messages in thread
From: Laurent Pinchart @ 2021-08-09  1:34 UTC (permalink / raw)
  To: dri-devel; +Cc: Michal Simek, Jianqiang Chen

Replace the manual connector implementation and registration in the DP
encoder with the DRM connector bridge helper. This removes boilerplate
code and simplifies the driver.

Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
---
 drivers/gpu/drm/xlnx/zynqmp_dp.c    | 90 +----------------------------
 drivers/gpu/drm/xlnx/zynqmp_dpsub.c | 26 ++++++++-
 drivers/gpu/drm/xlnx/zynqmp_dpsub.h |  2 +
 3 files changed, 28 insertions(+), 90 deletions(-)

diff --git a/drivers/gpu/drm/xlnx/zynqmp_dp.c b/drivers/gpu/drm/xlnx/zynqmp_dp.c
index 244628497e98..eb05feef5d30 100644
--- a/drivers/gpu/drm/xlnx/zynqmp_dp.c
+++ b/drivers/gpu/drm/xlnx/zynqmp_dp.c
@@ -10,7 +10,6 @@
  */
 
 #include <drm/drm_atomic_helper.h>
-#include <drm/drm_connector.h>
 #include <drm/drm_crtc.h>
 #include <drm/drm_device.h>
 #include <drm/drm_dp_helper.h>
@@ -274,7 +273,6 @@ struct zynqmp_dp_config {
 
 /**
  * struct zynqmp_dp - Xilinx DisplayPort core
- * @connector: the drm connector structure
  * @dev: device structure
  * @dpsub: Display subsystem
  * @drm: DRM core
@@ -296,7 +294,6 @@ struct zynqmp_dp_config {
  * @train_set: set of training data
  */
 struct zynqmp_dp {
-	struct drm_connector connector;
 	struct device *dev;
 	struct zynqmp_dpsub *dpsub;
 	struct drm_device *drm;
@@ -321,11 +318,6 @@ struct zynqmp_dp {
 	u8 train_set[ZYNQMP_DP_MAX_LANES];
 };
 
-static inline struct zynqmp_dp *connector_to_dp(struct drm_connector *connector)
-{
-	return container_of(connector, struct zynqmp_dp, connector);
-}
-
 static inline struct zynqmp_dp *bridge_to_dp(struct drm_bridge *bridge)
 {
 	return container_of(bridge, struct zynqmp_dp, bridge);
@@ -1284,33 +1276,15 @@ static void zynqmp_dp_encoder_mode_set_stream(struct zynqmp_dp *dp,
  * DRM Bridge
  */
 
-static const struct drm_connector_funcs zynqmp_dp_connector_funcs;
-static const struct drm_connector_helper_funcs zynqmp_dp_connector_helper_funcs;
-
 static int zynqmp_dp_bridge_attach(struct drm_bridge *bridge,
 				   enum drm_bridge_attach_flags flags)
 {
 	struct zynqmp_dp *dp = bridge_to_dp(bridge);
-	struct drm_connector *connector = &dp->connector;
 	int ret;
 
-	/* Create the DRM connector. */
-	connector->polled = DRM_CONNECTOR_POLL_HPD;
-	ret = drm_connector_init(dp->drm, connector,
-				 &zynqmp_dp_connector_funcs,
-				 DRM_MODE_CONNECTOR_DisplayPort);
-	if (ret) {
-		dev_err(dp->dev, "failed to create the DRM connector\n");
-		return ret;
-	}
-
-	drm_connector_helper_add(connector, &zynqmp_dp_connector_helper_funcs);
-	drm_connector_register(connector);
-	drm_connector_attach_encoder(connector, bridge->encoder);
-
 	if (dp->next_bridge) {
 		ret = drm_bridge_attach(bridge->encoder, dp->next_bridge,
-					bridge, DRM_BRIDGE_ATTACH_NO_CONNECTOR);
+					bridge, flags);
 		if (ret < 0)
 			return ret;
 	}
@@ -1531,68 +1505,6 @@ static const struct drm_bridge_funcs zynqmp_dp_bridge_funcs = {
 	.get_edid = zynqmp_dp_bridge_get_edid,
 };
 
-/* -----------------------------------------------------------------------------
- * DRM Connector
- */
-
-static enum drm_connector_status
-zynqmp_dp_connector_detect(struct drm_connector *connector, bool force)
-{
-	struct zynqmp_dp *dp = connector_to_dp(connector);
-
-	return zynqmp_dp_bridge_detect(&dp->bridge);
-}
-
-static int zynqmp_dp_connector_get_modes(struct drm_connector *connector)
-{
-	struct zynqmp_dp *dp = connector_to_dp(connector);
-	struct edid *edid;
-	int ret;
-
-	edid = zynqmp_dp_bridge_get_edid(&dp->bridge, connector);
-	if (!edid)
-		return 0;
-
-	drm_connector_update_edid_property(connector, edid);
-	ret = drm_add_edid_modes(connector, edid);
-	kfree(edid);
-
-	return ret;
-}
-
-static struct drm_encoder *
-zynqmp_dp_connector_best_encoder(struct drm_connector *connector)
-{
-	struct zynqmp_dp *dp = connector_to_dp(connector);
-
-	return &dp->dpsub->encoder;
-}
-
-static int zynqmp_dp_connector_mode_valid(struct drm_connector *connector,
-					  struct drm_display_mode *mode)
-{
-	struct zynqmp_dp *dp = connector_to_dp(connector);
-
-	return zynqmp_dp_bridge_mode_valid(&dp->bridge, &connector->display_info,
-					   mode);
-}
-
-static const struct drm_connector_funcs zynqmp_dp_connector_funcs = {
-	.detect			= zynqmp_dp_connector_detect,
-	.fill_modes		= drm_helper_probe_single_connector_modes,
-	.destroy		= drm_connector_cleanup,
-	.atomic_duplicate_state	= drm_atomic_helper_connector_duplicate_state,
-	.atomic_destroy_state	= drm_atomic_helper_connector_destroy_state,
-	.reset			= drm_atomic_helper_connector_reset,
-};
-
-static const struct drm_connector_helper_funcs
-zynqmp_dp_connector_helper_funcs = {
-	.get_modes	= zynqmp_dp_connector_get_modes,
-	.best_encoder	= zynqmp_dp_connector_best_encoder,
-	.mode_valid	= zynqmp_dp_connector_mode_valid,
-};
-
 /* -----------------------------------------------------------------------------
  * Interrupt Handling
  */
diff --git a/drivers/gpu/drm/xlnx/zynqmp_dpsub.c b/drivers/gpu/drm/xlnx/zynqmp_dpsub.c
index 3a3a85821e99..a7a80c435fa9 100644
--- a/drivers/gpu/drm/xlnx/zynqmp_dpsub.c
+++ b/drivers/gpu/drm/xlnx/zynqmp_dpsub.c
@@ -18,6 +18,8 @@
 
 #include <drm/drm_atomic_helper.h>
 #include <drm/drm_bridge.h>
+#include <drm/drm_bridge_connector.h>
+#include <drm/drm_connector.h>
 #include <drm/drm_device.h>
 #include <drm/drm_drv.h>
 #include <drm/drm_fb_helper.h>
@@ -96,6 +98,7 @@ static const struct drm_driver zynqmp_dpsub_drm_driver = {
 static int zynqmp_dpsub_drm_init(struct zynqmp_dpsub *dpsub)
 {
 	struct drm_encoder *encoder = &dpsub->encoder;
+	struct drm_connector *connector;
 	struct drm_device *drm = &dpsub->drm;
 	int ret;
 
@@ -132,12 +135,30 @@ static int zynqmp_dpsub_drm_init(struct zynqmp_dpsub *dpsub)
 	encoder->possible_crtcs |= zynqmp_disp_get_crtc_mask(dpsub->disp);
 	drm_simple_encoder_init(drm, encoder, DRM_MODE_ENCODER_NONE);
 
-	ret = drm_bridge_attach(encoder, dpsub->bridge, NULL, 0);
+	ret = drm_bridge_attach(encoder, dpsub->bridge, NULL,
+				DRM_BRIDGE_ATTACH_NO_CONNECTOR);
 	if (ret) {
 		dev_err(dpsub->dev, "failed to attach bridge to encoder\n");
 		goto err_poll_fini;
 	}
 
+	/* Create the connector for the chain of bridges. */
+	connector = drm_bridge_connector_init(drm, encoder);
+	if (IS_ERR(connector)) {
+		dev_err(dpsub->dev, "failed to created connector\n");
+		ret = PTR_ERR(connector);
+		goto err_poll_fini;
+	}
+
+	ret = drm_connector_attach_encoder(connector, encoder);
+	if (ret < 0) {
+		dev_err(dpsub->dev, "failed to attach connector to encoder\n");
+		goto err_poll_fini;
+	}
+
+	drm_bridge_connector_enable_hpd(connector);
+	dpsub->connector = connector;
+
 	/* Reset all components and register the DRM device. */
 	drm_mode_config_reset(drm);
 
@@ -260,6 +281,9 @@ static int zynqmp_dpsub_remove(struct platform_device *pdev)
 	struct zynqmp_dpsub *dpsub = platform_get_drvdata(pdev);
 	struct drm_device *drm = &dpsub->drm;
 
+	if (dpsub->connector)
+		drm_bridge_connector_disable_hpd(dpsub->connector);
+
 	drm_dev_unregister(drm);
 	drm_atomic_helper_shutdown(drm);
 	drm_kms_helper_poll_fini(drm);
diff --git a/drivers/gpu/drm/xlnx/zynqmp_dpsub.h b/drivers/gpu/drm/xlnx/zynqmp_dpsub.h
index 66820bbc4507..82a7ce136c2c 100644
--- a/drivers/gpu/drm/xlnx/zynqmp_dpsub.h
+++ b/drivers/gpu/drm/xlnx/zynqmp_dpsub.h
@@ -34,6 +34,7 @@ enum zynqmp_dpsub_format {
  * @dev: The physical device
  * @apb_clk: The APB clock
  * @encoder: The dummy DRM encoder
+ * @connector: The DP connector
  * @bridge: The DP encoder bridge
  * @disp: The display controller
  * @dp: The DisplayPort controller
@@ -46,6 +47,7 @@ struct zynqmp_dpsub {
 	struct clk *apb_clk;
 
 	struct drm_encoder encoder;
+	struct drm_connector *connector;
 	struct drm_bridge *bridge;
 
 	struct zynqmp_disp *disp;
-- 
Regards,

Laurent Pinchart


^ permalink raw reply related	[flat|nested] 38+ messages in thread

* [PATCH 10/36] drm: xlnx: zynqmp_dpsub: Report HPD through the bridge
  2021-08-09  1:34 [PATCH 00/36] drm: xlnx: zynqmp_dpsub: Initial live video input support Laurent Pinchart
                   ` (8 preceding siblings ...)
  2021-08-09  1:34 ` [PATCH 09/36] drm: xlnx: zynqmp_dpsub: Use DRM connector bridge helper Laurent Pinchart
@ 2021-08-09  1:34 ` Laurent Pinchart
  2021-08-09  1:34 ` [PATCH 11/36] drm: xlnx: zynqmp_dpsub: Drop unused zynqmp_disp.event field Laurent Pinchart
                   ` (25 subsequent siblings)
  35 siblings, 0 replies; 38+ messages in thread
From: Laurent Pinchart @ 2021-08-09  1:34 UTC (permalink / raw)
  To: dri-devel; +Cc: Michal Simek, Jianqiang Chen

Now that the driver uses the connector bridge helper, HPD can be
reported directly for the connector through the drm_bridge_hpd_notify()
function.

Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
---
 drivers/gpu/drm/xlnx/zynqmp_dp.c | 11 +++++------
 1 file changed, 5 insertions(+), 6 deletions(-)

diff --git a/drivers/gpu/drm/xlnx/zynqmp_dp.c b/drivers/gpu/drm/xlnx/zynqmp_dp.c
index eb05feef5d30..363015d248ab 100644
--- a/drivers/gpu/drm/xlnx/zynqmp_dp.c
+++ b/drivers/gpu/drm/xlnx/zynqmp_dp.c
@@ -17,7 +17,6 @@
 #include <drm/drm_managed.h>
 #include <drm/drm_modes.h>
 #include <drm/drm_of.h>
-#include <drm/drm_probe_helper.h>
 
 #include <linux/clk.h>
 #include <linux/delay.h>
@@ -1533,12 +1532,12 @@ void zynqmp_dp_disable_vblank(struct zynqmp_dp *dp)
 
 static void zynqmp_dp_hpd_work_func(struct work_struct *work)
 {
-	struct zynqmp_dp *dp;
+	struct zynqmp_dp *dp = container_of(work, struct zynqmp_dp,
+					    hpd_work.work);
+	enum drm_connector_status status;
 
-	dp = container_of(work, struct zynqmp_dp, hpd_work.work);
-
-	if (dp->drm)
-		drm_helper_hpd_irq_event(dp->drm);
+	status = zynqmp_dp_bridge_detect(&dp->bridge);
+	drm_bridge_hpd_notify(&dp->bridge, status);
 }
 
 static irqreturn_t zynqmp_dp_irq_handler(int irq, void *data)
-- 
Regards,

Laurent Pinchart


^ permalink raw reply related	[flat|nested] 38+ messages in thread

* [PATCH 11/36] drm: xlnx: zynqmp_dpsub: Drop unused zynqmp_disp.event field
  2021-08-09  1:34 [PATCH 00/36] drm: xlnx: zynqmp_dpsub: Initial live video input support Laurent Pinchart
                   ` (9 preceding siblings ...)
  2021-08-09  1:34 ` [PATCH 10/36] drm: xlnx: zynqmp_dpsub: Report HPD through the bridge Laurent Pinchart
@ 2021-08-09  1:34 ` Laurent Pinchart
  2021-08-09  1:34 ` [PATCH 12/36] drm: xlnx: zynqmp_dpsub: Drop unused zynqmp_disp_format.bus_fmt field Laurent Pinchart
                   ` (24 subsequent siblings)
  35 siblings, 0 replies; 38+ messages in thread
From: Laurent Pinchart @ 2021-08-09  1:34 UTC (permalink / raw)
  To: dri-devel; +Cc: Michal Simek, Jianqiang Chen

The event field of the zynqmp_disp structure is unused. Drop it.

Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
---
 drivers/gpu/drm/xlnx/zynqmp_disp.c | 3 ---
 1 file changed, 3 deletions(-)

diff --git a/drivers/gpu/drm/xlnx/zynqmp_disp.c b/drivers/gpu/drm/xlnx/zynqmp_disp.c
index ff2b308d8651..4180353b484a 100644
--- a/drivers/gpu/drm/xlnx/zynqmp_disp.c
+++ b/drivers/gpu/drm/xlnx/zynqmp_disp.c
@@ -170,7 +170,6 @@ struct zynqmp_disp_layer {
  * @audio.clk: Audio clock
  * @audio.clk_from_ps: True of the audio clock comes from PS, false from PL
  * @layers: Layers (planes)
- * @event: Pending vblank event request
  * @pclk: Pixel clock
  * @pclk_from_ps: True of the video clock comes from PS, false from PL
  */
@@ -195,8 +194,6 @@ struct zynqmp_disp {
 
 	struct zynqmp_disp_layer layers[ZYNQMP_DISP_NUM_LAYERS];
 
-	struct drm_pending_vblank_event *event;
-
 	struct clk *pclk;
 	bool pclk_from_ps;
 };
-- 
Regards,

Laurent Pinchart


^ permalink raw reply related	[flat|nested] 38+ messages in thread

* [PATCH 12/36] drm: xlnx: zynqmp_dpsub: Drop unused zynqmp_disp_format.bus_fmt field
  2021-08-09  1:34 [PATCH 00/36] drm: xlnx: zynqmp_dpsub: Initial live video input support Laurent Pinchart
                   ` (10 preceding siblings ...)
  2021-08-09  1:34 ` [PATCH 11/36] drm: xlnx: zynqmp_dpsub: Drop unused zynqmp_disp.event field Laurent Pinchart
@ 2021-08-09  1:34 ` Laurent Pinchart
  2021-08-09  1:34 ` [PATCH 13/36] drm: xlnx: zynqmp_dpsub: Don't pass CRTC to zynqmp_disp_setup_clock() Laurent Pinchart
                   ` (23 subsequent siblings)
  35 siblings, 0 replies; 38+ messages in thread
From: Laurent Pinchart @ 2021-08-09  1:34 UTC (permalink / raw)
  To: dri-devel; +Cc: Michal Simek, Jianqiang Chen

The bus_fmt field of the zynqmp_disp_format structure is unused. Drop
it.

Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
---
 drivers/gpu/drm/xlnx/zynqmp_disp.c | 2 --
 1 file changed, 2 deletions(-)

diff --git a/drivers/gpu/drm/xlnx/zynqmp_disp.c b/drivers/gpu/drm/xlnx/zynqmp_disp.c
index 4180353b484a..a6800cdb99e7 100644
--- a/drivers/gpu/drm/xlnx/zynqmp_disp.c
+++ b/drivers/gpu/drm/xlnx/zynqmp_disp.c
@@ -78,14 +78,12 @@
  * struct zynqmp_disp_format - Display subsystem format information
  * @drm_fmt: DRM format (4CC)
  * @buf_fmt: AV buffer format
- * @bus_fmt: Media bus formats (live formats)
  * @swap: Flag to swap R & B for RGB formats, and U & V for YUV formats
  * @sf: Scaling factors for color components
  */
 struct zynqmp_disp_format {
 	u32 drm_fmt;
 	u32 buf_fmt;
-	u32 bus_fmt;
 	bool swap;
 	const u32 *sf;
 };
-- 
Regards,

Laurent Pinchart


^ permalink raw reply related	[flat|nested] 38+ messages in thread

* [PATCH 13/36] drm: xlnx: zynqmp_dpsub: Don't pass CRTC to zynqmp_disp_setup_clock()
  2021-08-09  1:34 [PATCH 00/36] drm: xlnx: zynqmp_dpsub: Initial live video input support Laurent Pinchart
                   ` (11 preceding siblings ...)
  2021-08-09  1:34 ` [PATCH 12/36] drm: xlnx: zynqmp_dpsub: Drop unused zynqmp_disp_format.bus_fmt field Laurent Pinchart
@ 2021-08-09  1:34 ` Laurent Pinchart
  2021-08-09  1:34 ` [PATCH 14/36] drm: xlnx: zynqmp_dpsub: Configure blender in zynqmp_disp_enable() Laurent Pinchart
                   ` (22 subsequent siblings)
  35 siblings, 0 replies; 38+ messages in thread
From: Laurent Pinchart @ 2021-08-09  1:34 UTC (permalink / raw)
  To: dri-devel; +Cc: Michal Simek, Jianqiang Chen

To prepare for usage of the clock setup function outside of the CRTC
code, replace the DRM-specific structures passed as parameters with a
pointer to the zynqmp_disp and the requested clock rate. This doesn't
introduce any functional change.

Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
---
 drivers/gpu/drm/xlnx/zynqmp_disp.c | 18 ++++++++----------
 1 file changed, 8 insertions(+), 10 deletions(-)

diff --git a/drivers/gpu/drm/xlnx/zynqmp_disp.c b/drivers/gpu/drm/xlnx/zynqmp_disp.c
index a6800cdb99e7..0f16e9e1f676 100644
--- a/drivers/gpu/drm/xlnx/zynqmp_disp.c
+++ b/drivers/gpu/drm/xlnx/zynqmp_disp.c
@@ -1404,16 +1404,9 @@ static void zynqmp_disp_disable(struct zynqmp_disp *disp)
 	zynqmp_disp_avbuf_disable(disp);
 }
 
-static inline struct zynqmp_disp *crtc_to_disp(struct drm_crtc *crtc)
+static int zynqmp_disp_setup_clock(struct zynqmp_disp *disp,
+				   unsigned long mode_clock)
 {
-	return container_of(crtc, struct zynqmp_disp, crtc);
-}
-
-static int zynqmp_disp_crtc_setup_clock(struct drm_crtc *crtc,
-					struct drm_display_mode *adjusted_mode)
-{
-	struct zynqmp_disp *disp = crtc_to_disp(crtc);
-	unsigned long mode_clock = adjusted_mode->clock * 1000;
 	unsigned long rate;
 	long diff;
 	int ret;
@@ -1438,6 +1431,11 @@ static int zynqmp_disp_crtc_setup_clock(struct drm_crtc *crtc,
 	return 0;
 }
 
+static inline struct zynqmp_disp *crtc_to_disp(struct drm_crtc *crtc)
+{
+	return container_of(crtc, struct zynqmp_disp, crtc);
+}
+
 static void
 zynqmp_disp_crtc_atomic_enable(struct drm_crtc *crtc,
 			       struct drm_atomic_state *state)
@@ -1448,7 +1446,7 @@ zynqmp_disp_crtc_atomic_enable(struct drm_crtc *crtc,
 
 	pm_runtime_get_sync(disp->dev);
 
-	zynqmp_disp_crtc_setup_clock(crtc, adjusted_mode);
+	zynqmp_disp_setup_clock(disp, adjusted_mode->clock * 1000);
 
 	ret = clk_prepare_enable(disp->pclk);
 	if (ret) {
-- 
Regards,

Laurent Pinchart


^ permalink raw reply related	[flat|nested] 38+ messages in thread

* [PATCH 14/36] drm: xlnx: zynqmp_dpsub: Configure blender in zynqmp_disp_enable()
  2021-08-09  1:34 [PATCH 00/36] drm: xlnx: zynqmp_dpsub: Initial live video input support Laurent Pinchart
                   ` (12 preceding siblings ...)
  2021-08-09  1:34 ` [PATCH 13/36] drm: xlnx: zynqmp_dpsub: Don't pass CRTC to zynqmp_disp_setup_clock() Laurent Pinchart
@ 2021-08-09  1:34 ` Laurent Pinchart
  2021-08-09  1:34 ` [PATCH 15/36] drm: xlnx: zynqmp_dpsub: Use local variable in zynqmp_disp_layer_update() Laurent Pinchart
                   ` (21 subsequent siblings)
  35 siblings, 0 replies; 38+ messages in thread
From: Laurent Pinchart @ 2021-08-09  1:34 UTC (permalink / raw)
  To: dri-devel; +Cc: Michal Simek, Jianqiang Chen

To prepare for control of the blender outside of the CRTC code, move the
setup of the blender to the zynqmp_disp_enable() function. This doesn't
introduce any functional change.

Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
---
 drivers/gpu/drm/xlnx/zynqmp_disp.c | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/drivers/gpu/drm/xlnx/zynqmp_disp.c b/drivers/gpu/drm/xlnx/zynqmp_disp.c
index 0f16e9e1f676..bfa38a0b5199 100644
--- a/drivers/gpu/drm/xlnx/zynqmp_disp.c
+++ b/drivers/gpu/drm/xlnx/zynqmp_disp.c
@@ -1381,6 +1381,9 @@ static int zynqmp_disp_create_layers(struct zynqmp_disp *disp)
  */
 static void zynqmp_disp_enable(struct zynqmp_disp *disp)
 {
+	zynqmp_disp_blend_set_output_format(disp, ZYNQMP_DPSUB_FORMAT_RGB);
+	zynqmp_disp_blend_set_bg_color(disp, 0, 0, 0);
+
 	zynqmp_disp_avbuf_enable(disp);
 	/* Choose clock source based on the DT clock handle. */
 	zynqmp_disp_avbuf_set_clocks_sources(disp, disp->pclk_from_ps,
@@ -1455,9 +1458,6 @@ zynqmp_disp_crtc_atomic_enable(struct drm_crtc *crtc,
 		return;
 	}
 
-	zynqmp_disp_blend_set_output_format(disp, ZYNQMP_DPSUB_FORMAT_RGB);
-	zynqmp_disp_blend_set_bg_color(disp, 0, 0, 0);
-
 	zynqmp_disp_enable(disp);
 
 	/* Delay of 3 vblank intervals for timing gen to be stable */
-- 
Regards,

Laurent Pinchart


^ permalink raw reply related	[flat|nested] 38+ messages in thread

* [PATCH 15/36] drm: xlnx: zynqmp_dpsub: Use local variable in zynqmp_disp_layer_update()
  2021-08-09  1:34 [PATCH 00/36] drm: xlnx: zynqmp_dpsub: Initial live video input support Laurent Pinchart
                   ` (13 preceding siblings ...)
  2021-08-09  1:34 ` [PATCH 14/36] drm: xlnx: zynqmp_dpsub: Configure blender in zynqmp_disp_enable() Laurent Pinchart
@ 2021-08-09  1:34 ` Laurent Pinchart
  2021-08-09  1:34 ` [PATCH 16/36] drm: xlnx: zynqmp_dpsub: Pass format info to zynqmp_disp_layer_set_format() Laurent Pinchart
                   ` (20 subsequent siblings)
  35 siblings, 0 replies; 38+ messages in thread
From: Laurent Pinchart @ 2021-08-09  1:34 UTC (permalink / raw)
  To: dri-devel; +Cc: Michal Simek, Jianqiang Chen

Reuse the local info variable instead of going through the layer pointer
in zynqmp_disp_layer_update(). This doesn't introduce any functional
change.

Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
---
 drivers/gpu/drm/xlnx/zynqmp_disp.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/gpu/drm/xlnx/zynqmp_disp.c b/drivers/gpu/drm/xlnx/zynqmp_disp.c
index bfa38a0b5199..9b36dcc4ffd9 100644
--- a/drivers/gpu/drm/xlnx/zynqmp_disp.c
+++ b/drivers/gpu/drm/xlnx/zynqmp_disp.c
@@ -1083,7 +1083,7 @@ static int zynqmp_disp_layer_update(struct zynqmp_disp_layer *layer,
 	const struct drm_format_info *info = layer->drm_fmt;
 	unsigned int i;
 
-	for (i = 0; i < layer->drm_fmt->num_planes; i++) {
+	for (i = 0; i < info->num_planes; i++) {
 		unsigned int width = state->crtc_w / (i ? info->hsub : 1);
 		unsigned int height = state->crtc_h / (i ? info->vsub : 1);
 		struct zynqmp_disp_layer_dma *dma = &layer->dmas[i];
-- 
Regards,

Laurent Pinchart


^ permalink raw reply related	[flat|nested] 38+ messages in thread

* [PATCH 16/36] drm: xlnx: zynqmp_dpsub: Pass format info to zynqmp_disp_layer_set_format()
  2021-08-09  1:34 [PATCH 00/36] drm: xlnx: zynqmp_dpsub: Initial live video input support Laurent Pinchart
                   ` (14 preceding siblings ...)
  2021-08-09  1:34 ` [PATCH 15/36] drm: xlnx: zynqmp_dpsub: Use local variable in zynqmp_disp_layer_update() Laurent Pinchart
@ 2021-08-09  1:34 ` Laurent Pinchart
  2021-08-09  1:34 ` [PATCH 17/36] drm: xlnx: zynqmp_dpsub: Remplace hardcoded values with ARRAY_SIZE() Laurent Pinchart
                   ` (19 subsequent siblings)
  35 siblings, 0 replies; 38+ messages in thread
From: Laurent Pinchart @ 2021-08-09  1:34 UTC (permalink / raw)
  To: dri-devel; +Cc: Michal Simek, Jianqiang Chen

The zynqmp_disp_layer_set_format() function only needs format
information, not a full plane state. Get the necessary info from the
plane state in the caller and pass it to zynqmp_disp_layer_set_format().
This prepares for calling the function from non-DRM code. This doesn't
introduce any functional change.

Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
---
 drivers/gpu/drm/xlnx/zynqmp_disp.c | 10 ++++------
 1 file changed, 4 insertions(+), 6 deletions(-)

diff --git a/drivers/gpu/drm/xlnx/zynqmp_disp.c b/drivers/gpu/drm/xlnx/zynqmp_disp.c
index 9b36dcc4ffd9..54aa9772e9b9 100644
--- a/drivers/gpu/drm/xlnx/zynqmp_disp.c
+++ b/drivers/gpu/drm/xlnx/zynqmp_disp.c
@@ -1036,15 +1036,13 @@ static void zynqmp_disp_layer_disable(struct zynqmp_disp_layer *layer)
 /**
  * zynqmp_disp_layer_set_format - Set the layer format
  * @layer: The layer
- * @state: The plane state
+ * @info: The format info
  *
- * Set the format for @layer based on @state->fb->format. The layer must be
- * disabled.
+ * Set the format for @layer to @info. The layer must be disabled.
  */
 static void zynqmp_disp_layer_set_format(struct zynqmp_disp_layer *layer,
-					 struct drm_plane_state *state)
+					 const struct drm_format_info *info)
 {
-	const struct drm_format_info *info = state->fb->format;
 	unsigned int i;
 
 	layer->disp_fmt = zynqmp_disp_layer_find_format(layer, info->format);
@@ -1185,7 +1183,7 @@ zynqmp_disp_plane_atomic_update(struct drm_plane *plane,
 		if (old_state->fb)
 			zynqmp_disp_layer_disable(layer);
 
-		zynqmp_disp_layer_set_format(layer, new_state);
+		zynqmp_disp_layer_set_format(layer, new_state->fb->format);
 	}
 
 	zynqmp_disp_layer_update(layer, new_state);
-- 
Regards,

Laurent Pinchart


^ permalink raw reply related	[flat|nested] 38+ messages in thread

* [PATCH 17/36] drm: xlnx: zynqmp_dpsub: Remplace hardcoded values with ARRAY_SIZE()
  2021-08-09  1:34 [PATCH 00/36] drm: xlnx: zynqmp_dpsub: Initial live video input support Laurent Pinchart
                   ` (15 preceding siblings ...)
  2021-08-09  1:34 ` [PATCH 16/36] drm: xlnx: zynqmp_dpsub: Pass format info to zynqmp_disp_layer_set_format() Laurent Pinchart
@ 2021-08-09  1:34 ` Laurent Pinchart
  2021-08-09  1:34 ` [PATCH 18/36] drm: xlnx: zynqmp_dpsub: Don't use drmm_kcalloc() for temporary data Laurent Pinchart
                   ` (18 subsequent siblings)
  35 siblings, 0 replies; 38+ messages in thread
From: Laurent Pinchart @ 2021-08-09  1:34 UTC (permalink / raw)
  To: dri-devel; +Cc: Michal Simek, Jianqiang Chen

Use the ARRAY_SIZE() macro to iterate over arrays, instead of hardcoding
their size. This makes the code less error-prone should the array size
change.

Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
---
 drivers/gpu/drm/xlnx/zynqmp_disp.c | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/drivers/gpu/drm/xlnx/zynqmp_disp.c b/drivers/gpu/drm/xlnx/zynqmp_disp.c
index 54aa9772e9b9..4767d3a7929a 100644
--- a/drivers/gpu/drm/xlnx/zynqmp_disp.c
+++ b/drivers/gpu/drm/xlnx/zynqmp_disp.c
@@ -1217,7 +1217,7 @@ static int zynqmp_disp_create_planes(struct zynqmp_disp *disp)
 	unsigned int i, j;
 	int ret;
 
-	for (i = 0; i < ZYNQMP_DISP_NUM_LAYERS; i++) {
+	for (i = 0; i < ARRAY_SIZE(disp->layers); i++) {
 		struct zynqmp_disp_layer *layer = &disp->layers[i];
 		enum drm_plane_type type;
 		u32 *drm_formats;
@@ -1288,7 +1288,7 @@ static void zynqmp_disp_destroy_layers(struct zynqmp_disp *disp)
 {
 	unsigned int i;
 
-	for (i = 0; i < ZYNQMP_DISP_NUM_LAYERS; i++)
+	for (i = 0; i < ARRAY_SIZE(disp->layers); i++)
 		zynqmp_disp_layer_release_dma(disp, &disp->layers[i]);
 }
 
@@ -1350,7 +1350,7 @@ static int zynqmp_disp_create_layers(struct zynqmp_disp *disp)
 	unsigned int i;
 	int ret;
 
-	for (i = 0; i < ZYNQMP_DISP_NUM_LAYERS; i++) {
+	for (i = 0; i < ARRAY_SIZE(disp->layers); i++) {
 		struct zynqmp_disp_layer *layer = &disp->layers[i];
 
 		layer->id = i;
@@ -1587,7 +1587,7 @@ static void zynqmp_disp_map_crtc_to_plane(struct zynqmp_disp *disp)
 	u32 possible_crtcs = drm_crtc_mask(&disp->crtc);
 	unsigned int i;
 
-	for (i = 0; i < ZYNQMP_DISP_NUM_LAYERS; i++)
+	for (i = 0; i < ARRAY_SIZE(disp->layers); i++)
 		disp->layers[i].plane.possible_crtcs = possible_crtcs;
 }
 
-- 
Regards,

Laurent Pinchart


^ permalink raw reply related	[flat|nested] 38+ messages in thread

* [PATCH 18/36] drm: xlnx: zynqmp_dpsub: Don't use drmm_kcalloc() for temporary data
  2021-08-09  1:34 [PATCH 00/36] drm: xlnx: zynqmp_dpsub: Initial live video input support Laurent Pinchart
                   ` (16 preceding siblings ...)
  2021-08-09  1:34 ` [PATCH 17/36] drm: xlnx: zynqmp_dpsub: Remplace hardcoded values with ARRAY_SIZE() Laurent Pinchart
@ 2021-08-09  1:34 ` Laurent Pinchart
  2021-08-09  1:34 ` [PATCH 19/36] drm: xlnx: zynqmp_dpsub: Move pclk from zynqmp_disp to zynqmp_dpsub Laurent Pinchart
                   ` (17 subsequent siblings)
  35 siblings, 0 replies; 38+ messages in thread
From: Laurent Pinchart @ 2021-08-09  1:34 UTC (permalink / raw)
  To: dri-devel; +Cc: Michal Simek, Jianqiang Chen

The array of formats passed to drm_universal_plane_init() doesn't need
to outlive the function call, as it's copied internally. Use kcalloc()
instead of drmm_kcalloc() to allocate it, and free it right after usage.

While at it, move the allocation and initialization of the formats array
to a separate function, to prepare for splitting the DRM plane handling
to a separate file.

Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
---
 drivers/gpu/drm/xlnx/zynqmp_disp.c | 45 ++++++++++++++++++++++--------
 1 file changed, 34 insertions(+), 11 deletions(-)

diff --git a/drivers/gpu/drm/xlnx/zynqmp_disp.c b/drivers/gpu/drm/xlnx/zynqmp_disp.c
index 4767d3a7929a..b88efb6407a7 100644
--- a/drivers/gpu/drm/xlnx/zynqmp_disp.c
+++ b/drivers/gpu/drm/xlnx/zynqmp_disp.c
@@ -999,6 +999,33 @@ zynqmp_disp_layer_find_format(struct zynqmp_disp_layer *layer,
 	return NULL;
 }
 
+/**
+ * zynqmp_disp_layer_drm_formats - Return the DRM formats supported by the layer
+ * @layer: The layer
+ * @num_formats: Pointer to the returned number of formats
+ *
+ * Return: A newly allocated u32 array that stores all the DRM formats
+ * supported by the layer. The number of formats in the array is returned
+ * through the num_formats argument.
+ */
+static u32 *zynqmp_disp_layer_drm_formats(struct zynqmp_disp_layer *layer,
+					  unsigned int *num_formats)
+{
+	unsigned int i;
+	u32 *formats;
+
+	formats = kcalloc(layer->info->num_formats, sizeof(*formats),
+			  GFP_KERNEL);
+	if (!formats)
+		return NULL;
+
+	for (i = 0; i < layer->info->num_formats; ++i)
+		formats[i] = layer->info->formats[i].drm_fmt;
+
+	*num_formats = layer->info->num_formats;
+	return formats;
+}
+
 /**
  * zynqmp_disp_layer_enable - Enable a layer
  * @layer: The layer
@@ -1214,31 +1241,27 @@ static const struct drm_plane_funcs zynqmp_disp_plane_funcs = {
 
 static int zynqmp_disp_create_planes(struct zynqmp_disp *disp)
 {
-	unsigned int i, j;
+	unsigned int i;
 	int ret;
 
 	for (i = 0; i < ARRAY_SIZE(disp->layers); i++) {
 		struct zynqmp_disp_layer *layer = &disp->layers[i];
 		enum drm_plane_type type;
-		u32 *drm_formats;
+		unsigned int num_formats;
+		u32 *formats;
 
-		drm_formats = drmm_kcalloc(disp->drm, sizeof(*drm_formats),
-					   layer->info->num_formats,
-					   GFP_KERNEL);
-		if (!drm_formats)
+		formats = zynqmp_disp_layer_drm_formats(layer, &num_formats);
+		if (!formats)
 			return -ENOMEM;
 
-		for (j = 0; j < layer->info->num_formats; ++j)
-			drm_formats[j] = layer->info->formats[j].drm_fmt;
-
 		/* Graphics layer is primary, and video layer is overlay. */
 		type = zynqmp_disp_layer_is_video(layer)
 		     ? DRM_PLANE_TYPE_OVERLAY : DRM_PLANE_TYPE_PRIMARY;
 		ret = drm_universal_plane_init(disp->drm, &layer->plane, 0,
 					       &zynqmp_disp_plane_funcs,
-					       drm_formats,
-					       layer->info->num_formats,
+					       formats, num_formats,
 					       NULL, type, NULL);
+		kfree(formats);
 		if (ret)
 			return ret;
 
-- 
Regards,

Laurent Pinchart


^ permalink raw reply related	[flat|nested] 38+ messages in thread

* [PATCH 19/36] drm: xlnx: zynqmp_dpsub: Move pclk from zynqmp_disp to zynqmp_dpsub
  2021-08-09  1:34 [PATCH 00/36] drm: xlnx: zynqmp_dpsub: Initial live video input support Laurent Pinchart
                   ` (17 preceding siblings ...)
  2021-08-09  1:34 ` [PATCH 18/36] drm: xlnx: zynqmp_dpsub: Don't use drmm_kcalloc() for temporary data Laurent Pinchart
@ 2021-08-09  1:34 ` Laurent Pinchart
  2021-08-09  1:34 ` [PATCH 20/36] drm: xlnx: zynqmp_dpsub: Move audio clk " Laurent Pinchart
                   ` (16 subsequent siblings)
  35 siblings, 0 replies; 38+ messages in thread
From: Laurent Pinchart @ 2021-08-09  1:34 UTC (permalink / raw)
  To: dri-devel; +Cc: Michal Simek, Jianqiang Chen

The video clock is an external resource from the DPSUB point of view,
not a resource internal to the display controller. Move it to the
zynqmp_dpsub structure, to allow accessing it from outside the disp
code.

While at it, rename the fields from pclk and pclk_from_ps to vid_clk and
vid_clk_from_ps, to better reflect their purpose and match the
documentation.

Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
---
 drivers/gpu/drm/xlnx/zynqmp_disp.c  | 36 ++++++-----------------------
 drivers/gpu/drm/xlnx/zynqmp_dpsub.c | 17 ++++++++++++++
 drivers/gpu/drm/xlnx/zynqmp_dpsub.h |  4 ++++
 3 files changed, 28 insertions(+), 29 deletions(-)

diff --git a/drivers/gpu/drm/xlnx/zynqmp_disp.c b/drivers/gpu/drm/xlnx/zynqmp_disp.c
index b88efb6407a7..767ec5e5cfa4 100644
--- a/drivers/gpu/drm/xlnx/zynqmp_disp.c
+++ b/drivers/gpu/drm/xlnx/zynqmp_disp.c
@@ -168,8 +168,6 @@ struct zynqmp_disp_layer {
  * @audio.clk: Audio clock
  * @audio.clk_from_ps: True of the audio clock comes from PS, false from PL
  * @layers: Layers (planes)
- * @pclk: Pixel clock
- * @pclk_from_ps: True of the video clock comes from PS, false from PL
  */
 struct zynqmp_disp {
 	struct device *dev;
@@ -191,9 +189,6 @@ struct zynqmp_disp {
 	} audio;
 
 	struct zynqmp_disp_layer layers[ZYNQMP_DISP_NUM_LAYERS];
-
-	struct clk *pclk;
-	bool pclk_from_ps;
 };
 
 /* -----------------------------------------------------------------------------
@@ -1407,7 +1402,7 @@ static void zynqmp_disp_enable(struct zynqmp_disp *disp)
 
 	zynqmp_disp_avbuf_enable(disp);
 	/* Choose clock source based on the DT clock handle. */
-	zynqmp_disp_avbuf_set_clocks_sources(disp, disp->pclk_from_ps,
+	zynqmp_disp_avbuf_set_clocks_sources(disp, disp->dpsub->vid_clk_from_ps,
 					     disp->audio.clk_from_ps, true);
 	zynqmp_disp_avbuf_enable_channels(disp);
 	zynqmp_disp_avbuf_enable_audio(disp);
@@ -1435,13 +1430,13 @@ static int zynqmp_disp_setup_clock(struct zynqmp_disp *disp,
 	long diff;
 	int ret;
 
-	ret = clk_set_rate(disp->pclk, mode_clock);
+	ret = clk_set_rate(disp->dpsub->vid_clk, mode_clock);
 	if (ret) {
-		dev_err(disp->dev, "failed to set a pixel clock\n");
+		dev_err(disp->dev, "failed to set the video clock\n");
 		return ret;
 	}
 
-	rate = clk_get_rate(disp->pclk);
+	rate = clk_get_rate(disp->dpsub->vid_clk);
 	diff = rate - mode_clock;
 	if (abs(diff) > mode_clock / 20)
 		dev_info(disp->dev,
@@ -1472,9 +1467,9 @@ zynqmp_disp_crtc_atomic_enable(struct drm_crtc *crtc,
 
 	zynqmp_disp_setup_clock(disp, adjusted_mode->clock * 1000);
 
-	ret = clk_prepare_enable(disp->pclk);
+	ret = clk_prepare_enable(disp->dpsub->vid_clk);
 	if (ret) {
-		dev_err(disp->dev, "failed to enable a pixel clock\n");
+		dev_err(disp->dev, "failed to enable the video clock\n");
 		pm_runtime_put_sync(disp->dev);
 		return;
 	}
@@ -1514,7 +1509,7 @@ zynqmp_disp_crtc_atomic_disable(struct drm_crtc *crtc,
 	}
 	spin_unlock_irq(&crtc->dev->event_lock);
 
-	clk_disable_unprepare(disp->pclk);
+	clk_disable_unprepare(disp->dpsub->vid_clk);
 	pm_runtime_put_sync(disp->dev);
 }
 
@@ -1669,23 +1664,6 @@ int zynqmp_disp_probe(struct zynqmp_dpsub *dpsub, struct drm_device *drm)
 	if (IS_ERR(disp->audio.base))
 		return PTR_ERR(disp->audio.base);
 
-	/* Try the live PL video clock */
-	disp->pclk = devm_clk_get(disp->dev, "dp_live_video_in_clk");
-	if (!IS_ERR(disp->pclk))
-		disp->pclk_from_ps = false;
-	else if (PTR_ERR(disp->pclk) == -EPROBE_DEFER)
-		return PTR_ERR(disp->pclk);
-
-	/* If the live PL video clock is not valid, fall back to PS clock */
-	if (IS_ERR_OR_NULL(disp->pclk)) {
-		disp->pclk = devm_clk_get(disp->dev, "dp_vtc_pixel_clk_in");
-		if (IS_ERR(disp->pclk)) {
-			dev_err(disp->dev, "failed to init any video clock\n");
-			return PTR_ERR(disp->pclk);
-		}
-		disp->pclk_from_ps = true;
-	}
-
 	zynqmp_disp_audio_init(disp);
 
 	ret = zynqmp_disp_create_layers(disp);
diff --git a/drivers/gpu/drm/xlnx/zynqmp_dpsub.c b/drivers/gpu/drm/xlnx/zynqmp_dpsub.c
index a7a80c435fa9..6ef9885644aa 100644
--- a/drivers/gpu/drm/xlnx/zynqmp_dpsub.c
+++ b/drivers/gpu/drm/xlnx/zynqmp_dpsub.c
@@ -216,6 +216,23 @@ static int zynqmp_dpsub_init_clocks(struct zynqmp_dpsub *dpsub)
 		return ret;
 	}
 
+	/* Try the live PL video clock */
+	dpsub->vid_clk = devm_clk_get(dpsub->dev, "dp_live_video_in_clk");
+	if (!IS_ERR(dpsub->vid_clk))
+		dpsub->vid_clk_from_ps = false;
+	else if (PTR_ERR(dpsub->vid_clk) == -EPROBE_DEFER)
+		return PTR_ERR(dpsub->vid_clk);
+
+	/* If the live PL video clock is not valid, fall back to PS clock */
+	if (IS_ERR_OR_NULL(dpsub->vid_clk)) {
+		dpsub->vid_clk = devm_clk_get(dpsub->dev, "dp_vtc_pixel_clk_in");
+		if (IS_ERR(dpsub->vid_clk)) {
+			dev_err(dpsub->dev, "failed to init any video clock\n");
+			return PTR_ERR(dpsub->vid_clk);
+		}
+		dpsub->vid_clk_from_ps = true;
+	}
+
 	return 0;
 }
 
diff --git a/drivers/gpu/drm/xlnx/zynqmp_dpsub.h b/drivers/gpu/drm/xlnx/zynqmp_dpsub.h
index 82a7ce136c2c..f5500bb2af5e 100644
--- a/drivers/gpu/drm/xlnx/zynqmp_dpsub.h
+++ b/drivers/gpu/drm/xlnx/zynqmp_dpsub.h
@@ -33,6 +33,8 @@ enum zynqmp_dpsub_format {
  * @drm: The DRM/KMS device
  * @dev: The physical device
  * @apb_clk: The APB clock
+ * @vid_clk: Video clock
+ * @vid_clk_from_ps: True of the video clock comes from PS, false from PL
  * @encoder: The dummy DRM encoder
  * @connector: The DP connector
  * @bridge: The DP encoder bridge
@@ -45,6 +47,8 @@ struct zynqmp_dpsub {
 	struct device *dev;
 
 	struct clk *apb_clk;
+	struct clk *vid_clk;
+	bool vid_clk_from_ps;
 
 	struct drm_encoder encoder;
 	struct drm_connector *connector;
-- 
Regards,

Laurent Pinchart


^ permalink raw reply related	[flat|nested] 38+ messages in thread

* [PATCH 20/36] drm: xlnx: zynqmp_dpsub: Move audio clk from zynqmp_disp to zynqmp_dpsub
  2021-08-09  1:34 [PATCH 00/36] drm: xlnx: zynqmp_dpsub: Initial live video input support Laurent Pinchart
                   ` (18 preceding siblings ...)
  2021-08-09  1:34 ` [PATCH 19/36] drm: xlnx: zynqmp_dpsub: Move pclk from zynqmp_disp to zynqmp_dpsub Laurent Pinchart
@ 2021-08-09  1:34 ` Laurent Pinchart
  2021-08-09  1:34 ` [PATCH 21/36] drm: xlnx: zynqmp_dpsub: Move CRTC to zynqmp_dpsub structure Laurent Pinchart
                   ` (15 subsequent siblings)
  35 siblings, 0 replies; 38+ messages in thread
From: Laurent Pinchart @ 2021-08-09  1:34 UTC (permalink / raw)
  To: dri-devel; +Cc: Michal Simek, Jianqiang Chen

The audio clock is an external resource from the DPSUB point of view,
not a resource internal to the display controller. Move it to the
zynqmp_dpsub structure, to allow accessing it from outside the disp
code.

Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
---
 drivers/gpu/drm/xlnx/zynqmp_disp.c  | 54 ++---------------------------
 drivers/gpu/drm/xlnx/zynqmp_disp.h  |  2 --
 drivers/gpu/drm/xlnx/zynqmp_dp.c    |  8 ++---
 drivers/gpu/drm/xlnx/zynqmp_dpsub.c | 54 +++++++++++++++++++++++++++--
 drivers/gpu/drm/xlnx/zynqmp_dpsub.h |  7 ++++
 5 files changed, 65 insertions(+), 60 deletions(-)

diff --git a/drivers/gpu/drm/xlnx/zynqmp_disp.c b/drivers/gpu/drm/xlnx/zynqmp_disp.c
index 767ec5e5cfa4..bd21eb77589f 100644
--- a/drivers/gpu/drm/xlnx/zynqmp_disp.c
+++ b/drivers/gpu/drm/xlnx/zynqmp_disp.c
@@ -165,8 +165,6 @@ struct zynqmp_disp_layer {
  * @blend.base: Register I/O base address for the blender
  * @avbuf.base: Register I/O base address for the audio/video buffer manager
  * @audio.base: Registers I/O base address for the audio mixer
- * @audio.clk: Audio clock
- * @audio.clk_from_ps: True of the audio clock comes from PS, false from PL
  * @layers: Layers (planes)
  */
 struct zynqmp_disp {
@@ -184,8 +182,6 @@ struct zynqmp_disp {
 	} avbuf;
 	struct {
 		void __iomem *base;
-		struct clk *clk;
-		bool clk_from_ps;
 	} audio;
 
 	struct zynqmp_disp_layer layers[ZYNQMP_DISP_NUM_LAYERS];
@@ -891,25 +887,6 @@ static void zynqmp_disp_audio_disable(struct zynqmp_disp *disp)
 				ZYNQMP_DISP_AUD_SOFT_RESET_AUD_SRST);
 }
 
-static void zynqmp_disp_audio_init(struct zynqmp_disp *disp)
-{
-	/* Try the live PL audio clock. */
-	disp->audio.clk = devm_clk_get(disp->dev, "dp_live_audio_aclk");
-	if (!IS_ERR(disp->audio.clk)) {
-		disp->audio.clk_from_ps = false;
-		return;
-	}
-
-	/* If the live PL audio clock is not valid, fall back to PS clock. */
-	disp->audio.clk = devm_clk_get(disp->dev, "dp_aud_clk");
-	if (!IS_ERR(disp->audio.clk)) {
-		disp->audio.clk_from_ps = true;
-		return;
-	}
-
-	dev_err(disp->dev, "audio disabled due to missing clock\n");
-}
-
 /* -----------------------------------------------------------------------------
  * ZynqMP Display external functions for zynqmp_dp
  */
@@ -928,32 +905,6 @@ void zynqmp_disp_handle_vblank(struct zynqmp_disp *disp)
 	drm_crtc_handle_vblank(crtc);
 }
 
-/**
- * zynqmp_disp_audio_enabled - If the audio is enabled
- * @disp: Display controller
- *
- * Return if the audio is enabled depending on the audio clock.
- *
- * Return: true if audio is enabled, or false.
- */
-bool zynqmp_disp_audio_enabled(struct zynqmp_disp *disp)
-{
-	return !!disp->audio.clk;
-}
-
-/**
- * zynqmp_disp_get_audio_clk_rate - Get the current audio clock rate
- * @disp: Display controller
- *
- * Return: the current audio clock rate.
- */
-unsigned int zynqmp_disp_get_audio_clk_rate(struct zynqmp_disp *disp)
-{
-	if (zynqmp_disp_audio_enabled(disp))
-		return 0;
-	return clk_get_rate(disp->audio.clk);
-}
-
 /**
  * zynqmp_disp_get_crtc_mask - Return the CRTC bit mask
  * @disp: Display controller
@@ -1403,7 +1354,8 @@ static void zynqmp_disp_enable(struct zynqmp_disp *disp)
 	zynqmp_disp_avbuf_enable(disp);
 	/* Choose clock source based on the DT clock handle. */
 	zynqmp_disp_avbuf_set_clocks_sources(disp, disp->dpsub->vid_clk_from_ps,
-					     disp->audio.clk_from_ps, true);
+					     disp->dpsub->aud_clk_from_ps,
+					     true);
 	zynqmp_disp_avbuf_enable_channels(disp);
 	zynqmp_disp_avbuf_enable_audio(disp);
 
@@ -1664,8 +1616,6 @@ int zynqmp_disp_probe(struct zynqmp_dpsub *dpsub, struct drm_device *drm)
 	if (IS_ERR(disp->audio.base))
 		return PTR_ERR(disp->audio.base);
 
-	zynqmp_disp_audio_init(disp);
-
 	ret = zynqmp_disp_create_layers(disp);
 	if (ret)
 		return ret;
diff --git a/drivers/gpu/drm/xlnx/zynqmp_disp.h b/drivers/gpu/drm/xlnx/zynqmp_disp.h
index f402901afb23..1b7f90a81857 100644
--- a/drivers/gpu/drm/xlnx/zynqmp_disp.h
+++ b/drivers/gpu/drm/xlnx/zynqmp_disp.h
@@ -31,8 +31,6 @@ struct zynqmp_disp;
 struct zynqmp_dpsub;
 
 void zynqmp_disp_handle_vblank(struct zynqmp_disp *disp);
-bool zynqmp_disp_audio_enabled(struct zynqmp_disp *disp);
-unsigned int zynqmp_disp_get_audio_clk_rate(struct zynqmp_disp *disp);
 uint32_t zynqmp_disp_get_crtc_mask(struct zynqmp_disp *disp);
 
 int zynqmp_disp_drm_init(struct zynqmp_dpsub *dpsub);
diff --git a/drivers/gpu/drm/xlnx/zynqmp_dp.c b/drivers/gpu/drm/xlnx/zynqmp_dp.c
index 363015d248ab..7bd5769804e9 100644
--- a/drivers/gpu/drm/xlnx/zynqmp_dp.c
+++ b/drivers/gpu/drm/xlnx/zynqmp_dp.c
@@ -1251,7 +1251,7 @@ static void zynqmp_dp_encoder_mode_set_stream(struct zynqmp_dp *dp,
 		reg = drm_dp_bw_code_to_link_rate(dp->mode.bw_code);
 		zynqmp_dp_write(dp, ZYNQMP_DP_MAIN_STREAM_N_VID, reg);
 		zynqmp_dp_write(dp, ZYNQMP_DP_MAIN_STREAM_M_VID, mode->clock);
-		rate = zynqmp_disp_get_audio_clk_rate(dp->dpsub->disp);
+		rate = zynqmp_dpsub_get_audio_clk_rate(dp->dpsub);
 		if (rate) {
 			dev_dbg(dp->dev, "Audio rate: %d\n", rate / 512);
 			zynqmp_dp_write(dp, ZYNQMP_DP_TX_N_AUD, reg);
@@ -1260,7 +1260,7 @@ static void zynqmp_dp_encoder_mode_set_stream(struct zynqmp_dp *dp,
 	}
 
 	/* Only 2 channel audio is supported now */
-	if (zynqmp_disp_audio_enabled(dp->dpsub->disp))
+	if (zynqmp_dpsub_audio_enabled(dp->dpsub))
 		zynqmp_dp_write(dp, ZYNQMP_DP_TX_AUDIO_CHANNELS, 1);
 
 	zynqmp_dp_write(dp, ZYNQMP_DP_USER_PIX_WIDTH, 1);
@@ -1371,7 +1371,7 @@ static void zynqmp_dp_bridge_atomic_enable(struct drm_bridge *bridge,
 	/* Enable the encoder */
 	dp->enabled = true;
 	zynqmp_dp_update_misc(dp);
-	if (zynqmp_disp_audio_enabled(dp->dpsub->disp))
+	if (zynqmp_dpsub_audio_enabled(dp->dpsub))
 		zynqmp_dp_write(dp, ZYNQMP_DP_TX_AUDIO_CONTROL, 1);
 	zynqmp_dp_write(dp, ZYNQMP_DP_TX_PHY_POWER_DOWN, 0);
 	if (dp->status == connector_status_connected) {
@@ -1405,7 +1405,7 @@ static void zynqmp_dp_bridge_atomic_disable(struct drm_bridge *bridge,
 	drm_dp_dpcd_writeb(&dp->aux, DP_SET_POWER, DP_SET_POWER_D3);
 	zynqmp_dp_write(dp, ZYNQMP_DP_TX_PHY_POWER_DOWN,
 			ZYNQMP_DP_TX_PHY_POWER_DOWN_ALL);
-	if (zynqmp_disp_audio_enabled(dp->dpsub->disp))
+	if (zynqmp_dpsub_audio_enabled(dp->dpsub))
 		zynqmp_dp_write(dp, ZYNQMP_DP_TX_AUDIO_CONTROL, 0);
 	pm_runtime_put_sync(dp->dev);
 }
diff --git a/drivers/gpu/drm/xlnx/zynqmp_dpsub.c b/drivers/gpu/drm/xlnx/zynqmp_dpsub.c
index 6ef9885644aa..ba52dbed5ba0 100644
--- a/drivers/gpu/drm/xlnx/zynqmp_dpsub.c
+++ b/drivers/gpu/drm/xlnx/zynqmp_dpsub.c
@@ -198,6 +198,36 @@ static const struct dev_pm_ops zynqmp_dpsub_pm_ops = {
 	SET_SYSTEM_SLEEP_PM_OPS(zynqmp_dpsub_suspend, zynqmp_dpsub_resume)
 };
 
+/* -----------------------------------------------------------------------------
+ * DPSUB Configuration
+ */
+
+/**
+ * zynqmp_dpsub_audio_enabled - If the audio is enabled
+ * @dpsub: DisplayPort subsystem
+ *
+ * Return if the audio is enabled depending on the audio clock.
+ *
+ * Return: true if audio is enabled, or false.
+ */
+bool zynqmp_dpsub_audio_enabled(struct zynqmp_dpsub *dpsub)
+{
+	return !!dpsub->aud_clk;
+}
+
+/**
+ * zynqmp_dpsub_get_audio_clk_rate - Get the current audio clock rate
+ * @dpsub: DisplayPort subsystem
+ *
+ * Return: the current audio clock rate.
+ */
+unsigned int zynqmp_dpsub_get_audio_clk_rate(struct zynqmp_dpsub *dpsub)
+{
+	if (zynqmp_dpsub_audio_enabled(dpsub))
+		return 0;
+	return clk_get_rate(dpsub->aud_clk);
+}
+
 /* -----------------------------------------------------------------------------
  * Probe & Remove
  */
@@ -216,14 +246,16 @@ static int zynqmp_dpsub_init_clocks(struct zynqmp_dpsub *dpsub)
 		return ret;
 	}
 
-	/* Try the live PL video clock */
+	/*
+	 * Try the live PL video clock, and fall back to the PS clock if the
+	 * live PL video clock isn't valid.
+	 */
 	dpsub->vid_clk = devm_clk_get(dpsub->dev, "dp_live_video_in_clk");
 	if (!IS_ERR(dpsub->vid_clk))
 		dpsub->vid_clk_from_ps = false;
 	else if (PTR_ERR(dpsub->vid_clk) == -EPROBE_DEFER)
 		return PTR_ERR(dpsub->vid_clk);
 
-	/* If the live PL video clock is not valid, fall back to PS clock */
 	if (IS_ERR_OR_NULL(dpsub->vid_clk)) {
 		dpsub->vid_clk = devm_clk_get(dpsub->dev, "dp_vtc_pixel_clk_in");
 		if (IS_ERR(dpsub->vid_clk)) {
@@ -233,6 +265,24 @@ static int zynqmp_dpsub_init_clocks(struct zynqmp_dpsub *dpsub)
 		dpsub->vid_clk_from_ps = true;
 	}
 
+	/*
+	 * Try the live PL audio clock, and fall back to the PS clock if the
+	 * live PL audio clock isn't valid. Missing audio clock disables audio
+	 * but isn't an error.
+	 */
+	dpsub->aud_clk = devm_clk_get(dpsub->dev, "dp_live_audio_aclk");
+	if (!IS_ERR(dpsub->aud_clk)) {
+		dpsub->aud_clk_from_ps = false;
+		return 0;
+	}
+
+	dpsub->aud_clk = devm_clk_get(dpsub->dev, "dp_aud_clk");
+	if (!IS_ERR(dpsub->aud_clk)) {
+		dpsub->aud_clk_from_ps = true;
+		return 0;
+	}
+
+	dev_info(dpsub->dev, "audio disabled due to missing clock\n");
 	return 0;
 }
 
diff --git a/drivers/gpu/drm/xlnx/zynqmp_dpsub.h b/drivers/gpu/drm/xlnx/zynqmp_dpsub.h
index f5500bb2af5e..55d90f4130b2 100644
--- a/drivers/gpu/drm/xlnx/zynqmp_dpsub.h
+++ b/drivers/gpu/drm/xlnx/zynqmp_dpsub.h
@@ -35,6 +35,8 @@ enum zynqmp_dpsub_format {
  * @apb_clk: The APB clock
  * @vid_clk: Video clock
  * @vid_clk_from_ps: True of the video clock comes from PS, false from PL
+ * @aud_clk: Audio clock
+ * @aud_clk_from_ps: True of the audio clock comes from PS, false from PL
  * @encoder: The dummy DRM encoder
  * @connector: The DP connector
  * @bridge: The DP encoder bridge
@@ -49,6 +51,8 @@ struct zynqmp_dpsub {
 	struct clk *apb_clk;
 	struct clk *vid_clk;
 	bool vid_clk_from_ps;
+	struct clk *aud_clk;
+	bool aud_clk_from_ps;
 
 	struct drm_encoder encoder;
 	struct drm_connector *connector;
@@ -65,4 +69,7 @@ static inline struct zynqmp_dpsub *to_zynqmp_dpsub(struct drm_device *drm)
 	return container_of(drm, struct zynqmp_dpsub, drm);
 }
 
+bool zynqmp_dpsub_audio_enabled(struct zynqmp_dpsub *dpsub);
+unsigned int zynqmp_dpsub_get_audio_clk_rate(struct zynqmp_dpsub *dpsub);
+
 #endif /* _ZYNQMP_DPSUB_H_ */
-- 
Regards,

Laurent Pinchart


^ permalink raw reply related	[flat|nested] 38+ messages in thread

* [PATCH 21/36] drm: xlnx: zynqmp_dpsub: Move CRTC to zynqmp_dpsub structure
  2021-08-09  1:34 [PATCH 00/36] drm: xlnx: zynqmp_dpsub: Initial live video input support Laurent Pinchart
                   ` (19 preceding siblings ...)
  2021-08-09  1:34 ` [PATCH 20/36] drm: xlnx: zynqmp_dpsub: Move audio clk " Laurent Pinchart
@ 2021-08-09  1:34 ` Laurent Pinchart
  2021-08-09  1:34 ` [PATCH 22/36] drm: xlnx: zynqmp_dpsub: Move planes " Laurent Pinchart
                   ` (14 subsequent siblings)
  35 siblings, 0 replies; 38+ messages in thread
From: Laurent Pinchart @ 2021-08-09  1:34 UTC (permalink / raw)
  To: dri-devel; +Cc: Michal Simek, Jianqiang Chen

Decouple the zynqmp_disp, which handles the hardware configuration, from
the DRM CRTC by moving the CRTC to the zynqmp_dpsub structure. The CRTC
handling code will be moved to a separate file in a subsequent step.

Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
---
 drivers/gpu/drm/xlnx/zynqmp_disp.c  | 20 +++++++++-----------
 drivers/gpu/drm/xlnx/zynqmp_dpsub.h |  3 +++
 2 files changed, 12 insertions(+), 11 deletions(-)

diff --git a/drivers/gpu/drm/xlnx/zynqmp_disp.c b/drivers/gpu/drm/xlnx/zynqmp_disp.c
index bd21eb77589f..7cae0eaaf118 100644
--- a/drivers/gpu/drm/xlnx/zynqmp_disp.c
+++ b/drivers/gpu/drm/xlnx/zynqmp_disp.c
@@ -161,7 +161,6 @@ struct zynqmp_disp_layer {
  * @dev: Device structure
  * @drm: DRM core
  * @dpsub: Display subsystem
- * @crtc: DRM CRTC
  * @blend.base: Register I/O base address for the blender
  * @avbuf.base: Register I/O base address for the audio/video buffer manager
  * @audio.base: Registers I/O base address for the audio mixer
@@ -172,8 +171,6 @@ struct zynqmp_disp {
 	struct drm_device *drm;
 	struct zynqmp_dpsub *dpsub;
 
-	struct drm_crtc crtc;
-
 	struct {
 		void __iomem *base;
 	} blend;
@@ -900,7 +897,7 @@ static void zynqmp_disp_audio_disable(struct zynqmp_disp *disp)
  */
 void zynqmp_disp_handle_vblank(struct zynqmp_disp *disp)
 {
-	struct drm_crtc *crtc = &disp->crtc;
+	struct drm_crtc *crtc = &disp->dpsub->crtc;
 
 	drm_crtc_handle_vblank(crtc);
 }
@@ -913,7 +910,7 @@ void zynqmp_disp_handle_vblank(struct zynqmp_disp *disp)
  */
 uint32_t zynqmp_disp_get_crtc_mask(struct zynqmp_disp *disp)
 {
-	return drm_crtc_mask(&disp->crtc);
+	return drm_crtc_mask(&disp->dpsub->crtc);
 }
 
 /* -----------------------------------------------------------------------------
@@ -1404,7 +1401,7 @@ static int zynqmp_disp_setup_clock(struct zynqmp_disp *disp,
 
 static inline struct zynqmp_disp *crtc_to_disp(struct drm_crtc *crtc)
 {
-	return container_of(crtc, struct zynqmp_disp, crtc);
+	return container_of(crtc, struct zynqmp_dpsub, crtc)->disp;
 }
 
 static void
@@ -1452,7 +1449,7 @@ zynqmp_disp_crtc_atomic_disable(struct drm_crtc *crtc,
 
 	zynqmp_disp_disable(disp);
 
-	drm_crtc_vblank_off(&disp->crtc);
+	drm_crtc_vblank_off(crtc);
 
 	spin_lock_irq(&crtc->dev->event_lock);
 	if (crtc->state->event) {
@@ -1537,24 +1534,25 @@ static const struct drm_crtc_funcs zynqmp_disp_crtc_funcs = {
 static int zynqmp_disp_create_crtc(struct zynqmp_disp *disp)
 {
 	struct drm_plane *plane = &disp->layers[ZYNQMP_DISP_LAYER_GFX].plane;
+	struct drm_crtc *crtc = &disp->dpsub->crtc;
 	int ret;
 
-	ret = drm_crtc_init_with_planes(disp->drm, &disp->crtc, plane,
+	ret = drm_crtc_init_with_planes(disp->drm, crtc, plane,
 					NULL, &zynqmp_disp_crtc_funcs, NULL);
 	if (ret < 0)
 		return ret;
 
-	drm_crtc_helper_add(&disp->crtc, &zynqmp_disp_crtc_helper_funcs);
+	drm_crtc_helper_add(crtc, &zynqmp_disp_crtc_helper_funcs);
 
 	/* Start with vertical blanking interrupt reporting disabled. */
-	drm_crtc_vblank_off(&disp->crtc);
+	drm_crtc_vblank_off(crtc);
 
 	return 0;
 }
 
 static void zynqmp_disp_map_crtc_to_plane(struct zynqmp_disp *disp)
 {
-	u32 possible_crtcs = drm_crtc_mask(&disp->crtc);
+	u32 possible_crtcs = drm_crtc_mask(&disp->dpsub->crtc);
 	unsigned int i;
 
 	for (i = 0; i < ARRAY_SIZE(disp->layers); i++)
diff --git a/drivers/gpu/drm/xlnx/zynqmp_dpsub.h b/drivers/gpu/drm/xlnx/zynqmp_dpsub.h
index 55d90f4130b2..a0a7d66efdb2 100644
--- a/drivers/gpu/drm/xlnx/zynqmp_dpsub.h
+++ b/drivers/gpu/drm/xlnx/zynqmp_dpsub.h
@@ -12,6 +12,7 @@
 #ifndef _ZYNQMP_DPSUB_H_
 #define _ZYNQMP_DPSUB_H_
 
+#include <drm/drm_crtc.h>
 #include <drm/drm_encoder.h>
 
 struct clk;
@@ -37,6 +38,7 @@ enum zynqmp_dpsub_format {
  * @vid_clk_from_ps: True of the video clock comes from PS, false from PL
  * @aud_clk: Audio clock
  * @aud_clk_from_ps: True of the audio clock comes from PS, false from PL
+ * @crtc: The DRM CRTC
  * @encoder: The dummy DRM encoder
  * @connector: The DP connector
  * @bridge: The DP encoder bridge
@@ -54,6 +56,7 @@ struct zynqmp_dpsub {
 	struct clk *aud_clk;
 	bool aud_clk_from_ps;
 
+	struct drm_crtc crtc;
 	struct drm_encoder encoder;
 	struct drm_connector *connector;
 	struct drm_bridge *bridge;
-- 
Regards,

Laurent Pinchart


^ permalink raw reply related	[flat|nested] 38+ messages in thread

* [PATCH 22/36] drm: xlnx: zynqmp_dpsub: Move planes to zynqmp_dpsub structure
  2021-08-09  1:34 [PATCH 00/36] drm: xlnx: zynqmp_dpsub: Initial live video input support Laurent Pinchart
                   ` (20 preceding siblings ...)
  2021-08-09  1:34 ` [PATCH 21/36] drm: xlnx: zynqmp_dpsub: Move CRTC to zynqmp_dpsub structure Laurent Pinchart
@ 2021-08-09  1:34 ` Laurent Pinchart
  2021-08-09  1:34 ` [PATCH 23/36] drm: xlnx: zynqmp_dpsub: Move CRTC handling to zynqmp_kms.c Laurent Pinchart
                   ` (13 subsequent siblings)
  35 siblings, 0 replies; 38+ messages in thread
From: Laurent Pinchart @ 2021-08-09  1:34 UTC (permalink / raw)
  To: dri-devel; +Cc: Michal Simek, Jianqiang Chen

Decouple the zynqmp_disp, which handles the hardware configuration, from
the DRM planes by moving the planes to the zynqmp_dpsub structure. The
planes handling code will be moved to a separate file in a subsequent
step.

Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
---
 drivers/gpu/drm/xlnx/Makefile       |  2 +-
 drivers/gpu/drm/xlnx/zynqmp_disp.c  | 20 ++++----
 drivers/gpu/drm/xlnx/zynqmp_dpsub.c | 46 ++----------------
 drivers/gpu/drm/xlnx/zynqmp_dpsub.h |  5 ++
 drivers/gpu/drm/xlnx/zynqmp_kms.c   | 73 +++++++++++++++++++++++++++++
 drivers/gpu/drm/xlnx/zynqmp_kms.h   | 19 ++++++++
 6 files changed, 110 insertions(+), 55 deletions(-)
 create mode 100644 drivers/gpu/drm/xlnx/zynqmp_kms.c
 create mode 100644 drivers/gpu/drm/xlnx/zynqmp_kms.h

diff --git a/drivers/gpu/drm/xlnx/Makefile b/drivers/gpu/drm/xlnx/Makefile
index 51c24b72217b..ea1422a39502 100644
--- a/drivers/gpu/drm/xlnx/Makefile
+++ b/drivers/gpu/drm/xlnx/Makefile
@@ -1,2 +1,2 @@
-zynqmp-dpsub-y := zynqmp_disp.o zynqmp_dpsub.o zynqmp_dp.o
+zynqmp-dpsub-y := zynqmp_disp.o zynqmp_dpsub.o zynqmp_dp.o zynqmp_kms.o
 obj-$(CONFIG_DRM_ZYNQMP_DPSUB) += zynqmp-dpsub.o
diff --git a/drivers/gpu/drm/xlnx/zynqmp_disp.c b/drivers/gpu/drm/xlnx/zynqmp_disp.c
index 7cae0eaaf118..d5e037166c02 100644
--- a/drivers/gpu/drm/xlnx/zynqmp_disp.c
+++ b/drivers/gpu/drm/xlnx/zynqmp_disp.c
@@ -71,7 +71,6 @@
 #define ZYNQMP_DISP_AV_BUF_NUM_VID_GFX_BUFFERS		4
 #define ZYNQMP_DISP_AV_BUF_NUM_BUFFERS			6
 
-#define ZYNQMP_DISP_NUM_LAYERS				2
 #define ZYNQMP_DISP_MAX_NUM_SUB_PLANES			3
 
 /**
@@ -133,8 +132,7 @@ struct zynqmp_disp_layer_info {
 };
 
 /**
- * struct zynqmp_disp_layer - Display layer (DRM plane)
- * @plane: DRM plane
+ * struct zynqmp_disp_layer - Display layer
  * @id: Layer ID
  * @disp: Back pointer to struct zynqmp_disp
  * @info: Static layer information
@@ -181,7 +179,7 @@ struct zynqmp_disp {
 		void __iomem *base;
 	} audio;
 
-	struct zynqmp_disp_layer layers[ZYNQMP_DISP_NUM_LAYERS];
+	struct zynqmp_disp_layer layers[ZYNQMP_DPSUB_NUM_LAYERS];
 };
 
 /* -----------------------------------------------------------------------------
@@ -1189,6 +1187,7 @@ static int zynqmp_disp_create_planes(struct zynqmp_disp *disp)
 
 	for (i = 0; i < ARRAY_SIZE(disp->layers); i++) {
 		struct zynqmp_disp_layer *layer = &disp->layers[i];
+		struct drm_plane *plane = &disp->dpsub->planes[i];
 		enum drm_plane_type type;
 		unsigned int num_formats;
 		u32 *formats;
@@ -1200,7 +1199,7 @@ static int zynqmp_disp_create_planes(struct zynqmp_disp *disp)
 		/* Graphics layer is primary, and video layer is overlay. */
 		type = zynqmp_disp_layer_is_video(layer)
 		     ? DRM_PLANE_TYPE_OVERLAY : DRM_PLANE_TYPE_PRIMARY;
-		ret = drm_universal_plane_init(disp->drm, &layer->plane, 0,
+		ret = drm_universal_plane_init(disp->drm, plane, 0,
 					       &zynqmp_disp_plane_funcs,
 					       formats, num_formats,
 					       NULL, type, NULL);
@@ -1208,12 +1207,11 @@ static int zynqmp_disp_create_planes(struct zynqmp_disp *disp)
 		if (ret)
 			return ret;
 
-		drm_plane_helper_add(&layer->plane,
-				     &zynqmp_disp_plane_helper_funcs);
+		drm_plane_helper_add(plane, &zynqmp_disp_plane_helper_funcs);
 
-		drm_plane_create_zpos_immutable_property(&layer->plane, i);
+		drm_plane_create_zpos_immutable_property(plane, i);
 		if (zynqmp_disp_layer_is_gfx(layer))
-			drm_plane_create_alpha_property(&layer->plane);
+			drm_plane_create_alpha_property(plane);
 	}
 
 	return 0;
@@ -1533,7 +1531,7 @@ static const struct drm_crtc_funcs zynqmp_disp_crtc_funcs = {
 
 static int zynqmp_disp_create_crtc(struct zynqmp_disp *disp)
 {
-	struct drm_plane *plane = &disp->layers[ZYNQMP_DISP_LAYER_GFX].plane;
+	struct drm_plane *plane = &disp->dpsub->planes[ZYNQMP_DISP_LAYER_GFX];
 	struct drm_crtc *crtc = &disp->dpsub->crtc;
 	int ret;
 
@@ -1556,7 +1554,7 @@ static void zynqmp_disp_map_crtc_to_plane(struct zynqmp_disp *disp)
 	unsigned int i;
 
 	for (i = 0; i < ARRAY_SIZE(disp->layers); i++)
-		disp->layers[i].plane.possible_crtcs = possible_crtcs;
+		disp->dpsub->planes[i].possible_crtcs = possible_crtcs;
 }
 
 /* -----------------------------------------------------------------------------
diff --git a/drivers/gpu/drm/xlnx/zynqmp_dpsub.c b/drivers/gpu/drm/xlnx/zynqmp_dpsub.c
index ba52dbed5ba0..6f4e78b2a7c0 100644
--- a/drivers/gpu/drm/xlnx/zynqmp_dpsub.c
+++ b/drivers/gpu/drm/xlnx/zynqmp_dpsub.c
@@ -17,9 +17,7 @@
 #include <linux/pm_runtime.h>
 
 #include <drm/drm_atomic_helper.h>
-#include <drm/drm_bridge.h>
 #include <drm/drm_bridge_connector.h>
-#include <drm/drm_connector.h>
 #include <drm/drm_device.h>
 #include <drm/drm_drv.h>
 #include <drm/drm_fb_helper.h>
@@ -29,12 +27,12 @@
 #include <drm/drm_managed.h>
 #include <drm/drm_mode_config.h>
 #include <drm/drm_probe_helper.h>
-#include <drm/drm_simple_kms_helper.h>
 #include <drm/drm_vblank.h>
 
 #include "zynqmp_disp.h"
 #include "zynqmp_dp.h"
 #include "zynqmp_dpsub.h"
+#include "zynqmp_kms.h"
 
 /* -----------------------------------------------------------------------------
  * Dumb Buffer & Framebuffer Allocation
@@ -97,8 +95,6 @@ static const struct drm_driver zynqmp_dpsub_drm_driver = {
 
 static int zynqmp_dpsub_drm_init(struct zynqmp_dpsub *dpsub)
 {
-	struct drm_encoder *encoder = &dpsub->encoder;
-	struct drm_connector *connector;
 	struct drm_device *drm = &dpsub->drm;
 	int ret;
 
@@ -119,46 +115,10 @@ static int zynqmp_dpsub_drm_init(struct zynqmp_dpsub *dpsub)
 
 	drm_kms_helper_poll_init(drm);
 
-	/*
-	 * Initialize the DISP and DP components. This will creates planes,
-	 * CRTC, and a bridge for the DP encoder.
-	 */
-	ret = zynqmp_disp_drm_init(dpsub);
-	if (ret)
+	ret = zynqmp_dpsub_kms_init(dpsub);
+	if (ret < 0)
 		goto err_poll_fini;
 
-	ret = zynqmp_dp_drm_init(dpsub);
-	if (ret)
-		goto err_poll_fini;
-
-	/* Create the encoder and attach the bridge. */
-	encoder->possible_crtcs |= zynqmp_disp_get_crtc_mask(dpsub->disp);
-	drm_simple_encoder_init(drm, encoder, DRM_MODE_ENCODER_NONE);
-
-	ret = drm_bridge_attach(encoder, dpsub->bridge, NULL,
-				DRM_BRIDGE_ATTACH_NO_CONNECTOR);
-	if (ret) {
-		dev_err(dpsub->dev, "failed to attach bridge to encoder\n");
-		goto err_poll_fini;
-	}
-
-	/* Create the connector for the chain of bridges. */
-	connector = drm_bridge_connector_init(drm, encoder);
-	if (IS_ERR(connector)) {
-		dev_err(dpsub->dev, "failed to created connector\n");
-		ret = PTR_ERR(connector);
-		goto err_poll_fini;
-	}
-
-	ret = drm_connector_attach_encoder(connector, encoder);
-	if (ret < 0) {
-		dev_err(dpsub->dev, "failed to attach connector to encoder\n");
-		goto err_poll_fini;
-	}
-
-	drm_bridge_connector_enable_hpd(connector);
-	dpsub->connector = connector;
-
 	/* Reset all components and register the DRM device. */
 	drm_mode_config_reset(drm);
 
diff --git a/drivers/gpu/drm/xlnx/zynqmp_dpsub.h b/drivers/gpu/drm/xlnx/zynqmp_dpsub.h
index a0a7d66efdb2..5b8bbbfd06e0 100644
--- a/drivers/gpu/drm/xlnx/zynqmp_dpsub.h
+++ b/drivers/gpu/drm/xlnx/zynqmp_dpsub.h
@@ -14,6 +14,7 @@
 
 #include <drm/drm_crtc.h>
 #include <drm/drm_encoder.h>
+#include <drm/drm_plane.h>
 
 struct clk;
 struct device;
@@ -22,6 +23,8 @@ struct drm_device;
 struct zynqmp_disp;
 struct zynqmp_dp;
 
+#define ZYNQMP_DPSUB_NUM_LAYERS				2
+
 enum zynqmp_dpsub_format {
 	ZYNQMP_DPSUB_FORMAT_RGB,
 	ZYNQMP_DPSUB_FORMAT_YCRCB444,
@@ -38,6 +41,7 @@ enum zynqmp_dpsub_format {
  * @vid_clk_from_ps: True of the video clock comes from PS, false from PL
  * @aud_clk: Audio clock
  * @aud_clk_from_ps: True of the audio clock comes from PS, false from PL
+ * @planes: The DRM planes
  * @crtc: The DRM CRTC
  * @encoder: The dummy DRM encoder
  * @connector: The DP connector
@@ -56,6 +60,7 @@ struct zynqmp_dpsub {
 	struct clk *aud_clk;
 	bool aud_clk_from_ps;
 
+	struct drm_plane planes[ZYNQMP_DPSUB_NUM_LAYERS];
 	struct drm_crtc crtc;
 	struct drm_encoder encoder;
 	struct drm_connector *connector;
diff --git a/drivers/gpu/drm/xlnx/zynqmp_kms.c b/drivers/gpu/drm/xlnx/zynqmp_kms.c
new file mode 100644
index 000000000000..a18b57f7aab7
--- /dev/null
+++ b/drivers/gpu/drm/xlnx/zynqmp_kms.c
@@ -0,0 +1,73 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * ZynqMP DisplayPort Subsystem - KMS API
+ *
+ * Copyright (C) 2017 - 2021 Xilinx, Inc.
+ *
+ * Authors:
+ * - Hyun Woo Kwon <hyun.kwon@xilinx.com>
+ * - Laurent Pinchart <laurent.pinchart@ideasonboard.com>
+ */
+
+#include <drm/drm_bridge.h>
+#include <drm/drm_bridge_connector.h>
+#include <drm/drm_connector.h>
+#include <drm/drm_encoder.h>
+#include <drm/drm_simple_kms_helper.h>
+
+#include "zynqmp_disp.h"
+#include "zynqmp_dp.h"
+#include "zynqmp_dpsub.h"
+#include "zynqmp_kms.h"
+
+/* -----------------------------------------------------------------------------
+ * Initialization
+ */
+
+int zynqmp_dpsub_kms_init(struct zynqmp_dpsub *dpsub)
+{
+	struct drm_encoder *encoder = &dpsub->encoder;
+	struct drm_connector *connector;
+	int ret;
+
+	/*
+	 * Initialize the DISP and DP components. This will creates planes,
+	 * CRTC, and a bridge for the DP encoder.
+	 */
+	ret = zynqmp_disp_drm_init(dpsub);
+	if (ret)
+		return ret;
+
+	ret = zynqmp_dp_drm_init(dpsub);
+	if (ret)
+		return ret;
+
+	/* Create the encoder and attach the bridge. */
+	encoder->possible_crtcs |= zynqmp_disp_get_crtc_mask(dpsub->disp);
+	drm_simple_encoder_init(&dpsub->drm, encoder, DRM_MODE_ENCODER_NONE);
+
+	ret = drm_bridge_attach(encoder, dpsub->bridge, NULL,
+				DRM_BRIDGE_ATTACH_NO_CONNECTOR);
+	if (ret) {
+		dev_err(dpsub->dev, "failed to attach bridge to encoder\n");
+		return ret;
+	}
+
+	/* Create the connector for the chain of bridges. */
+	connector = drm_bridge_connector_init(&dpsub->drm, encoder);
+	if (IS_ERR(connector)) {
+		dev_err(dpsub->dev, "failed to created connector\n");
+		return PTR_ERR(connector);
+	}
+
+	ret = drm_connector_attach_encoder(connector, encoder);
+	if (ret < 0) {
+		dev_err(dpsub->dev, "failed to attach connector to encoder\n");
+		return ret;
+	}
+
+	drm_bridge_connector_enable_hpd(connector);
+	dpsub->connector = connector;
+
+	return 0;
+}
diff --git a/drivers/gpu/drm/xlnx/zynqmp_kms.h b/drivers/gpu/drm/xlnx/zynqmp_kms.h
new file mode 100644
index 000000000000..a6729d9d82cc
--- /dev/null
+++ b/drivers/gpu/drm/xlnx/zynqmp_kms.h
@@ -0,0 +1,19 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * ZynqMP DisplayPort Subsystem - KMS API
+ *
+ * Copyright (C) 2017 - 2021 Xilinx, Inc.
+ *
+ * Authors:
+ * - Hyun Woo Kwon <hyun.kwon@xilinx.com>
+ * - Laurent Pinchart <laurent.pinchart@ideasonboard.com>
+ */
+
+#ifndef _ZYNQMP_KMS_H_
+#define _ZYNQMP_KMS_H_
+
+struct zynqmp_dpsub;
+
+int zynqmp_dpsub_kms_init(struct zynqmp_dpsub *dpsub);
+
+#endif /* _ZYNQMP_KMS_H_ */
-- 
Regards,

Laurent Pinchart


^ permalink raw reply related	[flat|nested] 38+ messages in thread

* [PATCH 23/36] drm: xlnx: zynqmp_dpsub: Move CRTC handling to zynqmp_kms.c
  2021-08-09  1:34 [PATCH 00/36] drm: xlnx: zynqmp_dpsub: Initial live video input support Laurent Pinchart
                   ` (21 preceding siblings ...)
  2021-08-09  1:34 ` [PATCH 22/36] drm: xlnx: zynqmp_dpsub: Move planes " Laurent Pinchart
@ 2021-08-09  1:34 ` Laurent Pinchart
  2021-08-09  1:34 ` [PATCH 24/36] drm: xlnx: zynqmp_dpsub: Move planes " Laurent Pinchart
                   ` (12 subsequent siblings)
  35 siblings, 0 replies; 38+ messages in thread
From: Laurent Pinchart @ 2021-08-09  1:34 UTC (permalink / raw)
  To: dri-devel; +Cc: Michal Simek, Jianqiang Chen

Decouple the CRTC handling from the display controller programming by
moving the corresponding code from zynqmp_disp.c to zynqmp_kms.c. This
prepares for using the DPSUB with a live video input, without creating a
DRM CRTC in the DPSUB driver.

Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
---
 drivers/gpu/drm/xlnx/zynqmp_disp.c | 251 +++--------------------------
 drivers/gpu/drm/xlnx/zynqmp_disp.h |  21 ++-
 drivers/gpu/drm/xlnx/zynqmp_dp.c   |   3 +-
 drivers/gpu/drm/xlnx/zynqmp_kms.c  | 190 +++++++++++++++++++++-
 drivers/gpu/drm/xlnx/zynqmp_kms.h  |   2 +
 5 files changed, 232 insertions(+), 235 deletions(-)

diff --git a/drivers/gpu/drm/xlnx/zynqmp_disp.c b/drivers/gpu/drm/xlnx/zynqmp_disp.c
index d5e037166c02..060a77b39b7a 100644
--- a/drivers/gpu/drm/xlnx/zynqmp_disp.c
+++ b/drivers/gpu/drm/xlnx/zynqmp_disp.c
@@ -11,8 +11,6 @@
 
 #include <drm/drm_atomic.h>
 #include <drm/drm_atomic_helper.h>
-#include <drm/drm_atomic_uapi.h>
-#include <drm/drm_crtc.h>
 #include <drm/drm_device.h>
 #include <drm/drm_fb_cma_helper.h>
 #include <drm/drm_fourcc.h>
@@ -20,17 +18,13 @@
 #include <drm/drm_managed.h>
 #include <drm/drm_plane.h>
 #include <drm/drm_plane_helper.h>
-#include <drm/drm_vblank.h>
 
 #include <linux/clk.h>
-#include <linux/delay.h>
 #include <linux/dma-mapping.h>
 #include <linux/dmaengine.h>
 #include <linux/module.h>
 #include <linux/of.h>
 #include <linux/platform_device.h>
-#include <linux/pm_runtime.h>
-#include <linux/spinlock.h>
 
 #include "zynqmp_disp.h"
 #include "zynqmp_disp_regs.h"
@@ -87,16 +81,6 @@ struct zynqmp_disp_format {
 	const u32 *sf;
 };
 
-/**
- * enum zynqmp_disp_layer_id - Layer identifier
- * @ZYNQMP_DISP_LAYER_VID: Video layer
- * @ZYNQMP_DISP_LAYER_GFX: Graphics layer
- */
-enum zynqmp_disp_layer_id {
-	ZYNQMP_DISP_LAYER_VID,
-	ZYNQMP_DISP_LAYER_GFX
-};
-
 /**
  * enum zynqmp_disp_layer_mode - Layer mode
  * @ZYNQMP_DISP_LAYER_NONLIVE: non-live (memory) mode
@@ -143,7 +127,7 @@ struct zynqmp_disp_layer_info {
  */
 struct zynqmp_disp_layer {
 	struct drm_plane plane;
-	enum zynqmp_disp_layer_id id;
+	enum zynqmp_dpsub_layer_id id;
 	struct zynqmp_disp *disp;
 	const struct zynqmp_disp_layer_info *info;
 
@@ -398,12 +382,12 @@ static void zynqmp_disp_avbuf_write(struct zynqmp_disp *disp, int reg, u32 val)
 
 static bool zynqmp_disp_layer_is_gfx(const struct zynqmp_disp_layer *layer)
 {
-	return layer->id == ZYNQMP_DISP_LAYER_GFX;
+	return layer->id == ZYNQMP_DPSUB_LAYER_GFX;
 }
 
 static bool zynqmp_disp_layer_is_video(const struct zynqmp_disp_layer *layer)
 {
-	return layer->id == ZYNQMP_DISP_LAYER_VID;
+	return layer->id == ZYNQMP_DPSUB_LAYER_VID;
 }
 
 /**
@@ -882,35 +866,6 @@ static void zynqmp_disp_audio_disable(struct zynqmp_disp *disp)
 				ZYNQMP_DISP_AUD_SOFT_RESET_AUD_SRST);
 }
 
-/* -----------------------------------------------------------------------------
- * ZynqMP Display external functions for zynqmp_dp
- */
-
-/**
- * zynqmp_disp_handle_vblank - Handle the vblank event
- * @disp: Display controller
- *
- * This function handles the vblank interrupt, and sends an event to
- * CRTC object. This will be called by the DP vblank interrupt handler.
- */
-void zynqmp_disp_handle_vblank(struct zynqmp_disp *disp)
-{
-	struct drm_crtc *crtc = &disp->dpsub->crtc;
-
-	drm_crtc_handle_vblank(crtc);
-}
-
-/**
- * zynqmp_disp_get_crtc_mask - Return the CRTC bit mask
- * @disp: Display controller
- *
- * Return: the crtc mask of the zyqnmp_disp CRTC.
- */
-uint32_t zynqmp_disp_get_crtc_mask(struct zynqmp_disp *disp)
-{
-	return drm_crtc_mask(&disp->dpsub->crtc);
-}
-
 /* -----------------------------------------------------------------------------
  * ZynqMP Display Layer & DRM Plane
  */
@@ -1111,7 +1066,7 @@ zynqmp_disp_plane_atomic_check(struct drm_plane *plane,
 						   false, false);
 }
 
-static void
+void
 zynqmp_disp_plane_atomic_disable(struct drm_plane *plane,
 				 struct drm_atomic_state *state)
 {
@@ -1299,12 +1254,12 @@ static int zynqmp_disp_layer_request_dma(struct zynqmp_disp *disp,
 static int zynqmp_disp_create_layers(struct zynqmp_disp *disp)
 {
 	static const struct zynqmp_disp_layer_info layer_info[] = {
-		[ZYNQMP_DISP_LAYER_VID] = {
+		[ZYNQMP_DPSUB_LAYER_VID] = {
 			.formats = avbuf_vid_fmts,
 			.num_formats = ARRAY_SIZE(avbuf_vid_fmts),
 			.num_channels = 3,
 		},
-		[ZYNQMP_DISP_LAYER_GFX] = {
+		[ZYNQMP_DPSUB_LAYER_GFX] = {
 			.formats = avbuf_gfx_fmts,
 			.num_formats = ARRAY_SIZE(avbuf_gfx_fmts),
 			.num_channels = 1,
@@ -1334,14 +1289,14 @@ static int zynqmp_disp_create_layers(struct zynqmp_disp *disp)
 }
 
 /* -----------------------------------------------------------------------------
- * ZynqMP Display & DRM CRTC
+ * ZynqMP Display
  */
 
 /**
  * zynqmp_disp_enable - Enable the display controller
  * @disp: Display controller
  */
-static void zynqmp_disp_enable(struct zynqmp_disp *disp)
+void zynqmp_disp_enable(struct zynqmp_disp *disp)
 {
 	zynqmp_disp_blend_set_output_format(disp, ZYNQMP_DPSUB_FORMAT_RGB);
 	zynqmp_disp_blend_set_bg_color(disp, 0, 0, 0);
@@ -1361,7 +1316,7 @@ static void zynqmp_disp_enable(struct zynqmp_disp *disp)
  * zynqmp_disp_disable - Disable the display controller
  * @disp: Display controller
  */
-static void zynqmp_disp_disable(struct zynqmp_disp *disp)
+void zynqmp_disp_disable(struct zynqmp_disp *disp)
 {
 	zynqmp_disp_audio_disable(disp);
 
@@ -1370,8 +1325,15 @@ static void zynqmp_disp_disable(struct zynqmp_disp *disp)
 	zynqmp_disp_avbuf_disable(disp);
 }
 
-static int zynqmp_disp_setup_clock(struct zynqmp_disp *disp,
-				   unsigned long mode_clock)
+/**
+ * zynqmp_disp_setup_clock - Configure the display controller pixel clock rate
+ * @disp: Display controller
+ * @mode_clock: The pixel clock rate, in Hz
+ *
+ * Return: 0 on success, or a negative error clock otherwise
+ */
+int zynqmp_disp_setup_clock(struct zynqmp_disp *disp,
+			    unsigned long mode_clock)
 {
 	unsigned long rate;
 	long diff;
@@ -1397,186 +1359,13 @@ static int zynqmp_disp_setup_clock(struct zynqmp_disp *disp,
 	return 0;
 }
 
-static inline struct zynqmp_disp *crtc_to_disp(struct drm_crtc *crtc)
-{
-	return container_of(crtc, struct zynqmp_dpsub, crtc)->disp;
-}
-
-static void
-zynqmp_disp_crtc_atomic_enable(struct drm_crtc *crtc,
-			       struct drm_atomic_state *state)
-{
-	struct zynqmp_disp *disp = crtc_to_disp(crtc);
-	struct drm_display_mode *adjusted_mode = &crtc->state->adjusted_mode;
-	int ret, vrefresh;
-
-	pm_runtime_get_sync(disp->dev);
-
-	zynqmp_disp_setup_clock(disp, adjusted_mode->clock * 1000);
-
-	ret = clk_prepare_enable(disp->dpsub->vid_clk);
-	if (ret) {
-		dev_err(disp->dev, "failed to enable the video clock\n");
-		pm_runtime_put_sync(disp->dev);
-		return;
-	}
-
-	zynqmp_disp_enable(disp);
-
-	/* Delay of 3 vblank intervals for timing gen to be stable */
-	vrefresh = (adjusted_mode->clock * 1000) /
-		   (adjusted_mode->vtotal * adjusted_mode->htotal);
-	msleep(3 * 1000 / vrefresh);
-}
-
-static void
-zynqmp_disp_crtc_atomic_disable(struct drm_crtc *crtc,
-				struct drm_atomic_state *state)
-{
-	struct zynqmp_disp *disp = crtc_to_disp(crtc);
-	struct drm_plane_state *old_plane_state;
-
-	/*
-	 * Disable the plane if active. The old plane state can be NULL in the
-	 * .shutdown() path if the plane is already disabled, skip
-	 * zynqmp_disp_plane_atomic_disable() in that case.
-	 */
-	old_plane_state = drm_atomic_get_old_plane_state(state, crtc->primary);
-	if (old_plane_state)
-		zynqmp_disp_plane_atomic_disable(crtc->primary, state);
-
-	zynqmp_disp_disable(disp);
-
-	drm_crtc_vblank_off(crtc);
-
-	spin_lock_irq(&crtc->dev->event_lock);
-	if (crtc->state->event) {
-		drm_crtc_send_vblank_event(crtc, crtc->state->event);
-		crtc->state->event = NULL;
-	}
-	spin_unlock_irq(&crtc->dev->event_lock);
-
-	clk_disable_unprepare(disp->dpsub->vid_clk);
-	pm_runtime_put_sync(disp->dev);
-}
-
-static int zynqmp_disp_crtc_atomic_check(struct drm_crtc *crtc,
-					 struct drm_atomic_state *state)
-{
-	return drm_atomic_add_affected_planes(state, crtc);
-}
-
-static void
-zynqmp_disp_crtc_atomic_begin(struct drm_crtc *crtc,
-			      struct drm_atomic_state *state)
-{
-	drm_crtc_vblank_on(crtc);
-}
-
-static void
-zynqmp_disp_crtc_atomic_flush(struct drm_crtc *crtc,
-			      struct drm_atomic_state *state)
-{
-	if (crtc->state->event) {
-		struct drm_pending_vblank_event *event;
-
-		/* Consume the flip_done event from atomic helper. */
-		event = crtc->state->event;
-		crtc->state->event = NULL;
-
-		event->pipe = drm_crtc_index(crtc);
-
-		WARN_ON(drm_crtc_vblank_get(crtc) != 0);
-
-		spin_lock_irq(&crtc->dev->event_lock);
-		drm_crtc_arm_vblank_event(crtc, event);
-		spin_unlock_irq(&crtc->dev->event_lock);
-	}
-}
-
-static const struct drm_crtc_helper_funcs zynqmp_disp_crtc_helper_funcs = {
-	.atomic_enable	= zynqmp_disp_crtc_atomic_enable,
-	.atomic_disable	= zynqmp_disp_crtc_atomic_disable,
-	.atomic_check	= zynqmp_disp_crtc_atomic_check,
-	.atomic_begin	= zynqmp_disp_crtc_atomic_begin,
-	.atomic_flush	= zynqmp_disp_crtc_atomic_flush,
-};
-
-static int zynqmp_disp_crtc_enable_vblank(struct drm_crtc *crtc)
-{
-	struct zynqmp_disp *disp = crtc_to_disp(crtc);
-
-	zynqmp_dp_enable_vblank(disp->dpsub->dp);
-
-	return 0;
-}
-
-static void zynqmp_disp_crtc_disable_vblank(struct drm_crtc *crtc)
-{
-	struct zynqmp_disp *disp = crtc_to_disp(crtc);
-
-	zynqmp_dp_disable_vblank(disp->dpsub->dp);
-}
-
-static const struct drm_crtc_funcs zynqmp_disp_crtc_funcs = {
-	.destroy		= drm_crtc_cleanup,
-	.set_config		= drm_atomic_helper_set_config,
-	.page_flip		= drm_atomic_helper_page_flip,
-	.reset			= drm_atomic_helper_crtc_reset,
-	.atomic_duplicate_state	= drm_atomic_helper_crtc_duplicate_state,
-	.atomic_destroy_state	= drm_atomic_helper_crtc_destroy_state,
-	.enable_vblank		= zynqmp_disp_crtc_enable_vblank,
-	.disable_vblank		= zynqmp_disp_crtc_disable_vblank,
-};
-
-static int zynqmp_disp_create_crtc(struct zynqmp_disp *disp)
-{
-	struct drm_plane *plane = &disp->dpsub->planes[ZYNQMP_DISP_LAYER_GFX];
-	struct drm_crtc *crtc = &disp->dpsub->crtc;
-	int ret;
-
-	ret = drm_crtc_init_with_planes(disp->drm, crtc, plane,
-					NULL, &zynqmp_disp_crtc_funcs, NULL);
-	if (ret < 0)
-		return ret;
-
-	drm_crtc_helper_add(crtc, &zynqmp_disp_crtc_helper_funcs);
-
-	/* Start with vertical blanking interrupt reporting disabled. */
-	drm_crtc_vblank_off(crtc);
-
-	return 0;
-}
-
-static void zynqmp_disp_map_crtc_to_plane(struct zynqmp_disp *disp)
-{
-	u32 possible_crtcs = drm_crtc_mask(&disp->dpsub->crtc);
-	unsigned int i;
-
-	for (i = 0; i < ARRAY_SIZE(disp->layers); i++)
-		disp->dpsub->planes[i].possible_crtcs = possible_crtcs;
-}
-
 /* -----------------------------------------------------------------------------
  * Initialization & Cleanup
  */
 
 int zynqmp_disp_drm_init(struct zynqmp_dpsub *dpsub)
 {
-	struct zynqmp_disp *disp = dpsub->disp;
-	int ret;
-
-	ret = zynqmp_disp_create_planes(disp);
-	if (ret)
-		return ret;
-
-	ret = zynqmp_disp_create_crtc(disp);
-	if (ret < 0)
-		return ret;
-
-	zynqmp_disp_map_crtc_to_plane(disp);
-
-	return 0;
+	return zynqmp_disp_create_planes(dpsub->disp);
 }
 
 int zynqmp_disp_probe(struct zynqmp_dpsub *dpsub, struct drm_device *drm)
@@ -1616,7 +1405,7 @@ int zynqmp_disp_probe(struct zynqmp_dpsub *dpsub, struct drm_device *drm)
 	if (ret)
 		return ret;
 
-	layer = &disp->layers[ZYNQMP_DISP_LAYER_VID];
+	layer = &disp->layers[ZYNQMP_DPSUB_LAYER_VID];
 	dpsub->dma_align = 1 << layer->dmas[0].chan->device->copy_align;
 
 	return 0;
diff --git a/drivers/gpu/drm/xlnx/zynqmp_disp.h b/drivers/gpu/drm/xlnx/zynqmp_disp.h
index 1b7f90a81857..57cd540f550f 100644
--- a/drivers/gpu/drm/xlnx/zynqmp_disp.h
+++ b/drivers/gpu/drm/xlnx/zynqmp_disp.h
@@ -25,13 +25,30 @@
 #define ZYNQMP_DISP_MAX_DMA_BIT				44
 
 struct device;
+struct drm_atomic_state;
 struct drm_device;
+struct drm_plane;
 struct platform_device;
 struct zynqmp_disp;
 struct zynqmp_dpsub;
 
-void zynqmp_disp_handle_vblank(struct zynqmp_disp *disp);
-uint32_t zynqmp_disp_get_crtc_mask(struct zynqmp_disp *disp);
+/**
+ * enum zynqmp_dpsub_layer_id - Layer identifier
+ * @ZYNQMP_DPSUB_LAYER_VID: Video layer
+ * @ZYNQMP_DPSUB_LAYER_GFX: Graphics layer
+ */
+enum zynqmp_dpsub_layer_id {
+	ZYNQMP_DPSUB_LAYER_VID,
+	ZYNQMP_DPSUB_LAYER_GFX,
+};
+
+void zynqmp_disp_enable(struct zynqmp_disp *disp);
+void zynqmp_disp_disable(struct zynqmp_disp *disp);
+int zynqmp_disp_setup_clock(struct zynqmp_disp *disp,
+			    unsigned long mode_clock);
+
+void zynqmp_disp_plane_atomic_disable(struct drm_plane *plane,
+				      struct drm_atomic_state *state);
 
 int zynqmp_disp_drm_init(struct zynqmp_dpsub *dpsub);
 int zynqmp_disp_probe(struct zynqmp_dpsub *dpsub, struct drm_device *drm);
diff --git a/drivers/gpu/drm/xlnx/zynqmp_dp.c b/drivers/gpu/drm/xlnx/zynqmp_dp.c
index 7bd5769804e9..72fe3b7fb78e 100644
--- a/drivers/gpu/drm/xlnx/zynqmp_dp.c
+++ b/drivers/gpu/drm/xlnx/zynqmp_dp.c
@@ -30,6 +30,7 @@
 #include "zynqmp_disp.h"
 #include "zynqmp_dp.h"
 #include "zynqmp_dpsub.h"
+#include "zynqmp_kms.h"
 
 static uint zynqmp_dp_aux_timeout_ms = 50;
 module_param_named(aux_timeout_ms, zynqmp_dp_aux_timeout_ms, uint, 0444);
@@ -1559,7 +1560,7 @@ static irqreturn_t zynqmp_dp_irq_handler(int irq, void *data)
 	zynqmp_dp_write(dp, ZYNQMP_DP_INT_STATUS, status);
 
 	if (status & ZYNQMP_DP_INT_VBLANK_START)
-		zynqmp_disp_handle_vblank(dp->dpsub->disp);
+		zynqmp_dpsub_handle_vblank(dp->dpsub);
 
 	if (status & ZYNQMP_DP_INT_HPD_EVENT)
 		schedule_delayed_work(&dp->hpd_work, 0);
diff --git a/drivers/gpu/drm/xlnx/zynqmp_kms.c b/drivers/gpu/drm/xlnx/zynqmp_kms.c
index a18b57f7aab7..49042194480a 100644
--- a/drivers/gpu/drm/xlnx/zynqmp_kms.c
+++ b/drivers/gpu/drm/xlnx/zynqmp_kms.c
@@ -9,17 +9,199 @@
  * - Laurent Pinchart <laurent.pinchart@ideasonboard.com>
  */
 
+#include <drm/drm_atomic.h>
+#include <drm/drm_atomic_helper.h>
 #include <drm/drm_bridge.h>
 #include <drm/drm_bridge_connector.h>
 #include <drm/drm_connector.h>
+#include <drm/drm_crtc.h>
 #include <drm/drm_encoder.h>
+#include <drm/drm_plane.h>
 #include <drm/drm_simple_kms_helper.h>
+#include <drm/drm_vblank.h>
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/pm_runtime.h>
+#include <linux/spinlock.h>
 
 #include "zynqmp_disp.h"
 #include "zynqmp_dp.h"
 #include "zynqmp_dpsub.h"
 #include "zynqmp_kms.h"
 
+/* -----------------------------------------------------------------------------
+ * DRM CRTC
+ */
+
+static inline struct zynqmp_dpsub *crtc_to_dpsub(struct drm_crtc *crtc)
+{
+	return container_of(crtc, struct zynqmp_dpsub, crtc);
+}
+
+static void zynqmp_dpsub_crtc_atomic_enable(struct drm_crtc *crtc,
+					    struct drm_atomic_state *state)
+{
+	struct zynqmp_dpsub *dpsub = crtc_to_dpsub(crtc);
+	struct drm_display_mode *adjusted_mode = &crtc->state->adjusted_mode;
+	int ret, vrefresh;
+
+	pm_runtime_get_sync(dpsub->dev);
+
+	zynqmp_disp_setup_clock(dpsub->disp, adjusted_mode->clock * 1000);
+
+	ret = clk_prepare_enable(dpsub->vid_clk);
+	if (ret) {
+		dev_err(dpsub->dev, "failed to enable a pixel clock\n");
+		pm_runtime_put_sync(dpsub->dev);
+		return;
+	}
+
+	zynqmp_disp_enable(dpsub->disp);
+
+	/* Delay of 3 vblank intervals for timing gen to be stable */
+	vrefresh = (adjusted_mode->clock * 1000) /
+		   (adjusted_mode->vtotal * adjusted_mode->htotal);
+	msleep(3 * 1000 / vrefresh);
+}
+
+static void zynqmp_dpsub_crtc_atomic_disable(struct drm_crtc *crtc,
+					     struct drm_atomic_state *state)
+{
+	struct zynqmp_dpsub *dpsub = crtc_to_dpsub(crtc);
+	struct drm_plane_state *old_plane_state;
+
+	/*
+	 * Disable the plane if active. The old plane state can be NULL in the
+	 * .shutdown() path if the plane is already disabled, skip
+	 * zynqmp_disp_plane_atomic_disable() in that case.
+	 */
+	old_plane_state = drm_atomic_get_old_plane_state(state, crtc->primary);
+	if (old_plane_state)
+		zynqmp_disp_plane_atomic_disable(crtc->primary, state);
+
+	zynqmp_disp_disable(dpsub->disp);
+
+	drm_crtc_vblank_off(crtc);
+
+	spin_lock_irq(&crtc->dev->event_lock);
+	if (crtc->state->event) {
+		drm_crtc_send_vblank_event(crtc, crtc->state->event);
+		crtc->state->event = NULL;
+	}
+	spin_unlock_irq(&crtc->dev->event_lock);
+
+	clk_disable_unprepare(dpsub->vid_clk);
+	pm_runtime_put_sync(dpsub->dev);
+}
+
+static int zynqmp_dpsub_crtc_atomic_check(struct drm_crtc *crtc,
+					  struct drm_atomic_state *state)
+{
+	return drm_atomic_add_affected_planes(state, crtc);
+}
+
+static void zynqmp_dpsub_crtc_atomic_begin(struct drm_crtc *crtc,
+					   struct drm_atomic_state *state)
+{
+	drm_crtc_vblank_on(crtc);
+}
+
+static void zynqmp_dpsub_crtc_atomic_flush(struct drm_crtc *crtc,
+					   struct drm_atomic_state *state)
+{
+	if (crtc->state->event) {
+		struct drm_pending_vblank_event *event;
+
+		/* Consume the flip_done event from atomic helper. */
+		event = crtc->state->event;
+		crtc->state->event = NULL;
+
+		event->pipe = drm_crtc_index(crtc);
+
+		WARN_ON(drm_crtc_vblank_get(crtc) != 0);
+
+		spin_lock_irq(&crtc->dev->event_lock);
+		drm_crtc_arm_vblank_event(crtc, event);
+		spin_unlock_irq(&crtc->dev->event_lock);
+	}
+}
+
+static const struct drm_crtc_helper_funcs zynqmp_dpsub_crtc_helper_funcs = {
+	.atomic_enable	= zynqmp_dpsub_crtc_atomic_enable,
+	.atomic_disable	= zynqmp_dpsub_crtc_atomic_disable,
+	.atomic_check	= zynqmp_dpsub_crtc_atomic_check,
+	.atomic_begin	= zynqmp_dpsub_crtc_atomic_begin,
+	.atomic_flush	= zynqmp_dpsub_crtc_atomic_flush,
+};
+
+static int zynqmp_dpsub_crtc_enable_vblank(struct drm_crtc *crtc)
+{
+	struct zynqmp_dpsub *dpsub = crtc_to_dpsub(crtc);
+
+	zynqmp_dp_enable_vblank(dpsub->dp);
+
+	return 0;
+}
+
+static void zynqmp_dpsub_crtc_disable_vblank(struct drm_crtc *crtc)
+{
+	struct zynqmp_dpsub *dpsub = crtc_to_dpsub(crtc);
+
+	zynqmp_dp_disable_vblank(dpsub->dp);
+}
+
+static const struct drm_crtc_funcs zynqmp_dpsub_crtc_funcs = {
+	.destroy		= drm_crtc_cleanup,
+	.set_config		= drm_atomic_helper_set_config,
+	.page_flip		= drm_atomic_helper_page_flip,
+	.reset			= drm_atomic_helper_crtc_reset,
+	.atomic_duplicate_state	= drm_atomic_helper_crtc_duplicate_state,
+	.atomic_destroy_state	= drm_atomic_helper_crtc_destroy_state,
+	.enable_vblank		= zynqmp_dpsub_crtc_enable_vblank,
+	.disable_vblank		= zynqmp_dpsub_crtc_disable_vblank,
+};
+
+static int zynqmp_dpsub_create_crtc(struct zynqmp_dpsub *dpsub)
+{
+	struct drm_plane *plane = &dpsub->planes[ZYNQMP_DPSUB_LAYER_GFX];
+	struct drm_crtc *crtc = &dpsub->crtc;
+	int ret;
+
+	ret = drm_crtc_init_with_planes(&dpsub->drm, crtc, plane,
+					NULL, &zynqmp_dpsub_crtc_funcs, NULL);
+	if (ret < 0)
+		return ret;
+
+	drm_crtc_helper_add(crtc, &zynqmp_dpsub_crtc_helper_funcs);
+
+	/* Start with vertical blanking interrupt reporting disabled. */
+	drm_crtc_vblank_off(crtc);
+
+	return 0;
+}
+
+static void zynqmp_dpsub_map_crtc_to_plane(struct zynqmp_dpsub *dpsub)
+{
+	u32 possible_crtcs = drm_crtc_mask(&dpsub->crtc);
+	unsigned int i;
+
+	for (i = 0; i < ARRAY_SIZE(dpsub->planes); i++)
+		dpsub->planes[i].possible_crtcs = possible_crtcs;
+}
+
+/**
+ * zynqmp_dpsub_handle_vblank - Handle the vblank event
+ * @dpsub: DisplayPort subsystem
+ *
+ * This function handles the vblank interrupt, and sends an event to
+ * CRTC object. This will be called by the DP vblank interrupt handler.
+ */
+void zynqmp_dpsub_handle_vblank(struct zynqmp_dpsub *dpsub)
+{
+	drm_crtc_handle_vblank(&dpsub->crtc);
+}
+
 /* -----------------------------------------------------------------------------
  * Initialization
  */
@@ -38,12 +220,18 @@ int zynqmp_dpsub_kms_init(struct zynqmp_dpsub *dpsub)
 	if (ret)
 		return ret;
 
+	ret = zynqmp_dpsub_create_crtc(dpsub);
+	if (ret < 0)
+		return ret;
+
+	zynqmp_dpsub_map_crtc_to_plane(dpsub);
+
 	ret = zynqmp_dp_drm_init(dpsub);
 	if (ret)
 		return ret;
 
 	/* Create the encoder and attach the bridge. */
-	encoder->possible_crtcs |= zynqmp_disp_get_crtc_mask(dpsub->disp);
+	encoder->possible_crtcs |= drm_crtc_mask(&dpsub->crtc);
 	drm_simple_encoder_init(&dpsub->drm, encoder, DRM_MODE_ENCODER_NONE);
 
 	ret = drm_bridge_attach(encoder, dpsub->bridge, NULL,
diff --git a/drivers/gpu/drm/xlnx/zynqmp_kms.h b/drivers/gpu/drm/xlnx/zynqmp_kms.h
index a6729d9d82cc..a8934b1abb05 100644
--- a/drivers/gpu/drm/xlnx/zynqmp_kms.h
+++ b/drivers/gpu/drm/xlnx/zynqmp_kms.h
@@ -14,6 +14,8 @@
 
 struct zynqmp_dpsub;
 
+void zynqmp_dpsub_handle_vblank(struct zynqmp_dpsub *dpsub);
+
 int zynqmp_dpsub_kms_init(struct zynqmp_dpsub *dpsub);
 
 #endif /* _ZYNQMP_KMS_H_ */
-- 
Regards,

Laurent Pinchart


^ permalink raw reply related	[flat|nested] 38+ messages in thread

* [PATCH 24/36] drm: xlnx: zynqmp_dpsub: Move planes handling to zynqmp_kms.c
  2021-08-09  1:34 [PATCH 00/36] drm: xlnx: zynqmp_dpsub: Initial live video input support Laurent Pinchart
                   ` (22 preceding siblings ...)
  2021-08-09  1:34 ` [PATCH 23/36] drm: xlnx: zynqmp_dpsub: Move CRTC handling to zynqmp_kms.c Laurent Pinchart
@ 2021-08-09  1:34 ` Laurent Pinchart
  2021-08-09  1:34 ` [PATCH 25/36] drm: xlnx: zynqmp_dpsub: Register AUX bus at bridge attach time Laurent Pinchart
                   ` (11 subsequent siblings)
  35 siblings, 0 replies; 38+ messages in thread
From: Laurent Pinchart @ 2021-08-09  1:34 UTC (permalink / raw)
  To: dri-devel; +Cc: Michal Simek, Jianqiang Chen

Decouple the planes handling from the display controller programming by
moving the corresponding code from zynqmp_disp.c to zynqmp_kms.c. This
prepares for using the DPSUB with a live video input, without creating
DRM planes in the DPSUB driver.

While at it, fix a typo in a comment.

Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
---
 drivers/gpu/drm/xlnx/zynqmp_disp.c  | 173 ++--------------------------
 drivers/gpu/drm/xlnx/zynqmp_disp.h  |  19 ++-
 drivers/gpu/drm/xlnx/zynqmp_dpsub.h |   2 +
 drivers/gpu/drm/xlnx/zynqmp_kms.c   | 144 ++++++++++++++++++++++-
 4 files changed, 166 insertions(+), 172 deletions(-)

diff --git a/drivers/gpu/drm/xlnx/zynqmp_disp.c b/drivers/gpu/drm/xlnx/zynqmp_disp.c
index 060a77b39b7a..cc07cb2a4d0f 100644
--- a/drivers/gpu/drm/xlnx/zynqmp_disp.c
+++ b/drivers/gpu/drm/xlnx/zynqmp_disp.c
@@ -9,15 +9,11 @@
  * - Laurent Pinchart <laurent.pinchart@ideasonboard.com>
  */
 
-#include <drm/drm_atomic.h>
-#include <drm/drm_atomic_helper.h>
-#include <drm/drm_device.h>
 #include <drm/drm_fb_cma_helper.h>
 #include <drm/drm_fourcc.h>
 #include <drm/drm_framebuffer.h>
 #include <drm/drm_managed.h>
 #include <drm/drm_plane.h>
-#include <drm/drm_plane_helper.h>
 
 #include <linux/clk.h>
 #include <linux/dma-mapping.h>
@@ -126,7 +122,6 @@ struct zynqmp_disp_layer_info {
  * @mode: Current operation mode
  */
 struct zynqmp_disp_layer {
-	struct drm_plane plane;
 	enum zynqmp_dpsub_layer_id id;
 	struct zynqmp_disp *disp;
 	const struct zynqmp_disp_layer_info *info;
@@ -141,7 +136,6 @@ struct zynqmp_disp_layer {
 /**
  * struct zynqmp_disp - Display controller
  * @dev: Device structure
- * @drm: DRM core
  * @dpsub: Display subsystem
  * @blend.base: Register I/O base address for the blender
  * @avbuf.base: Register I/O base address for the audio/video buffer manager
@@ -150,7 +144,6 @@ struct zynqmp_disp_layer {
  */
 struct zynqmp_disp {
 	struct device *dev;
-	struct drm_device *drm;
 	struct zynqmp_dpsub *dpsub;
 
 	struct {
@@ -380,11 +373,6 @@ static void zynqmp_disp_avbuf_write(struct zynqmp_disp *disp, int reg, u32 val)
 	writel(val, disp->avbuf.base + reg);
 }
 
-static bool zynqmp_disp_layer_is_gfx(const struct zynqmp_disp_layer *layer)
-{
-	return layer->id == ZYNQMP_DPSUB_LAYER_GFX;
-}
-
 static bool zynqmp_disp_layer_is_video(const struct zynqmp_disp_layer *layer)
 {
 	return layer->id == ZYNQMP_DPSUB_LAYER_VID;
@@ -722,8 +710,8 @@ static void zynqmp_disp_blend_set_bg_color(struct zynqmp_disp *disp,
  * @enable: True to enable global alpha blending
  * @alpha: Global alpha value (ignored if @enabled is false)
  */
-static void zynqmp_disp_blend_set_global_alpha(struct zynqmp_disp *disp,
-					       bool enable, u32 alpha)
+void zynqmp_disp_blend_set_global_alpha(struct zynqmp_disp *disp,
+					bool enable, u32 alpha)
 {
 	zynqmp_disp_blend_write(disp, ZYNQMP_DISP_V_BLEND_SET_GLOBAL_ALPHA,
 				ZYNQMP_DISP_V_BLEND_SET_GLOBAL_ALPHA_VALUE(alpha) |
@@ -904,8 +892,8 @@ zynqmp_disp_layer_find_format(struct zynqmp_disp_layer *layer,
  * supported by the layer. The number of formats in the array is returned
  * through the num_formats argument.
  */
-static u32 *zynqmp_disp_layer_drm_formats(struct zynqmp_disp_layer *layer,
-					  unsigned int *num_formats)
+u32 *zynqmp_disp_layer_drm_formats(struct zynqmp_disp_layer *layer,
+				   unsigned int *num_formats)
 {
 	unsigned int i;
 	u32 *formats;
@@ -929,7 +917,7 @@ static u32 *zynqmp_disp_layer_drm_formats(struct zynqmp_disp_layer *layer,
  * Enable the @layer in the audio/video buffer manager and the blender. DMA
  * channels are started separately by zynqmp_disp_layer_update().
  */
-static void zynqmp_disp_layer_enable(struct zynqmp_disp_layer *layer)
+void zynqmp_disp_layer_enable(struct zynqmp_disp_layer *layer)
 {
 	zynqmp_disp_avbuf_enable_video(layer->disp, layer,
 				       ZYNQMP_DISP_LAYER_NONLIVE);
@@ -945,7 +933,7 @@ static void zynqmp_disp_layer_enable(struct zynqmp_disp_layer *layer)
  * Disable the layer by stopping its DMA channels and disabling it in the
  * audio/video buffer manager and the blender.
  */
-static void zynqmp_disp_layer_disable(struct zynqmp_disp_layer *layer)
+void zynqmp_disp_layer_disable(struct zynqmp_disp_layer *layer)
 {
 	unsigned int i;
 
@@ -963,8 +951,8 @@ static void zynqmp_disp_layer_disable(struct zynqmp_disp_layer *layer)
  *
  * Set the format for @layer to @info. The layer must be disabled.
  */
-static void zynqmp_disp_layer_set_format(struct zynqmp_disp_layer *layer,
-					 const struct drm_format_info *info)
+void zynqmp_disp_layer_set_format(struct zynqmp_disp_layer *layer,
+				  const struct drm_format_info *info)
 {
 	unsigned int i;
 
@@ -998,8 +986,8 @@ static void zynqmp_disp_layer_set_format(struct zynqmp_disp_layer *layer,
  *
  * Return: 0 on success, or the DMA descriptor failure error otherwise
  */
-static int zynqmp_disp_layer_update(struct zynqmp_disp_layer *layer,
-				    struct drm_plane_state *state)
+int zynqmp_disp_layer_update(struct zynqmp_disp_layer *layer,
+			     struct drm_plane_state *state)
 {
 	const struct drm_format_info *info = layer->drm_fmt;
 	unsigned int i;
@@ -1039,139 +1027,6 @@ static int zynqmp_disp_layer_update(struct zynqmp_disp_layer *layer,
 	return 0;
 }
 
-static inline struct zynqmp_disp_layer *plane_to_layer(struct drm_plane *plane)
-{
-	return container_of(plane, struct zynqmp_disp_layer, plane);
-}
-
-static int
-zynqmp_disp_plane_atomic_check(struct drm_plane *plane,
-			       struct drm_atomic_state *state)
-{
-	struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state,
-										 plane);
-	struct drm_crtc_state *crtc_state;
-
-	if (!new_plane_state->crtc)
-		return 0;
-
-	crtc_state = drm_atomic_get_crtc_state(state, new_plane_state->crtc);
-	if (IS_ERR(crtc_state))
-		return PTR_ERR(crtc_state);
-
-	return drm_atomic_helper_check_plane_state(new_plane_state,
-						   crtc_state,
-						   DRM_PLANE_HELPER_NO_SCALING,
-						   DRM_PLANE_HELPER_NO_SCALING,
-						   false, false);
-}
-
-void
-zynqmp_disp_plane_atomic_disable(struct drm_plane *plane,
-				 struct drm_atomic_state *state)
-{
-	struct drm_plane_state *old_state = drm_atomic_get_old_plane_state(state,
-									   plane);
-	struct zynqmp_disp_layer *layer = plane_to_layer(plane);
-
-	if (!old_state->fb)
-		return;
-
-	zynqmp_disp_layer_disable(layer);
-
-	if (zynqmp_disp_layer_is_gfx(layer))
-		zynqmp_disp_blend_set_global_alpha(layer->disp, false,
-						   plane->state->alpha >> 8);
-}
-
-static void
-zynqmp_disp_plane_atomic_update(struct drm_plane *plane,
-				struct drm_atomic_state *state)
-{
-	struct drm_plane_state *old_state = drm_atomic_get_old_plane_state(state, plane);
-	struct drm_plane_state *new_state = drm_atomic_get_new_plane_state(state, plane);
-	struct zynqmp_disp_layer *layer = plane_to_layer(plane);
-	bool format_changed = false;
-
-	if (!old_state->fb ||
-	    old_state->fb->format->format != new_state->fb->format->format)
-		format_changed = true;
-
-	/*
-	 * If the format has changed (including going from a previously
-	 * disabled state to any format), reconfigure the format. Disable the
-	 * plane first if needed.
-	 */
-	if (format_changed) {
-		if (old_state->fb)
-			zynqmp_disp_layer_disable(layer);
-
-		zynqmp_disp_layer_set_format(layer, new_state->fb->format);
-	}
-
-	zynqmp_disp_layer_update(layer, new_state);
-
-	if (zynqmp_disp_layer_is_gfx(layer))
-		zynqmp_disp_blend_set_global_alpha(layer->disp, true,
-						   plane->state->alpha >> 8);
-
-	/* Enable or re-enable the plane is the format has changed. */
-	if (format_changed)
-		zynqmp_disp_layer_enable(layer);
-}
-
-static const struct drm_plane_helper_funcs zynqmp_disp_plane_helper_funcs = {
-	.atomic_check		= zynqmp_disp_plane_atomic_check,
-	.atomic_update		= zynqmp_disp_plane_atomic_update,
-	.atomic_disable		= zynqmp_disp_plane_atomic_disable,
-};
-
-static const struct drm_plane_funcs zynqmp_disp_plane_funcs = {
-	.update_plane		= drm_atomic_helper_update_plane,
-	.disable_plane		= drm_atomic_helper_disable_plane,
-	.destroy		= drm_plane_cleanup,
-	.reset			= drm_atomic_helper_plane_reset,
-	.atomic_duplicate_state	= drm_atomic_helper_plane_duplicate_state,
-	.atomic_destroy_state	= drm_atomic_helper_plane_destroy_state,
-};
-
-static int zynqmp_disp_create_planes(struct zynqmp_disp *disp)
-{
-	unsigned int i;
-	int ret;
-
-	for (i = 0; i < ARRAY_SIZE(disp->layers); i++) {
-		struct zynqmp_disp_layer *layer = &disp->layers[i];
-		struct drm_plane *plane = &disp->dpsub->planes[i];
-		enum drm_plane_type type;
-		unsigned int num_formats;
-		u32 *formats;
-
-		formats = zynqmp_disp_layer_drm_formats(layer, &num_formats);
-		if (!formats)
-			return -ENOMEM;
-
-		/* Graphics layer is primary, and video layer is overlay. */
-		type = zynqmp_disp_layer_is_video(layer)
-		     ? DRM_PLANE_TYPE_OVERLAY : DRM_PLANE_TYPE_PRIMARY;
-		ret = drm_universal_plane_init(disp->drm, plane, 0,
-					       &zynqmp_disp_plane_funcs,
-					       formats, num_formats,
-					       NULL, type, NULL);
-		kfree(formats);
-		if (ret)
-			return ret;
-
-		drm_plane_helper_add(plane, &zynqmp_disp_plane_helper_funcs);
-
-		drm_plane_create_zpos_immutable_property(plane, i);
-		if (zynqmp_disp_layer_is_gfx(layer))
-			drm_plane_create_alpha_property(plane);
-	}
-
-	return 0;
-}
-
 /**
  * zynqmp_disp_layer_release_dma - Release DMA channels for a layer
  * @disp: Display controller
@@ -1279,6 +1134,8 @@ static int zynqmp_disp_create_layers(struct zynqmp_disp *disp)
 		ret = zynqmp_disp_layer_request_dma(disp, layer);
 		if (ret)
 			goto err;
+
+		disp->dpsub->layers[i] = layer;
 	}
 
 	return 0;
@@ -1363,11 +1220,6 @@ int zynqmp_disp_setup_clock(struct zynqmp_disp *disp,
  * Initialization & Cleanup
  */
 
-int zynqmp_disp_drm_init(struct zynqmp_dpsub *dpsub)
-{
-	return zynqmp_disp_create_planes(dpsub->disp);
-}
-
 int zynqmp_disp_probe(struct zynqmp_dpsub *dpsub, struct drm_device *drm)
 {
 	struct platform_device *pdev = to_platform_device(dpsub->dev);
@@ -1382,7 +1234,6 @@ int zynqmp_disp_probe(struct zynqmp_dpsub *dpsub, struct drm_device *drm)
 
 	disp->dev = &pdev->dev;
 	disp->dpsub = dpsub;
-	disp->drm = drm;
 
 	dpsub->disp = disp;
 
diff --git a/drivers/gpu/drm/xlnx/zynqmp_disp.h b/drivers/gpu/drm/xlnx/zynqmp_disp.h
index 57cd540f550f..663f7d67c78f 100644
--- a/drivers/gpu/drm/xlnx/zynqmp_disp.h
+++ b/drivers/gpu/drm/xlnx/zynqmp_disp.h
@@ -25,11 +25,12 @@
 #define ZYNQMP_DISP_MAX_DMA_BIT				44
 
 struct device;
-struct drm_atomic_state;
 struct drm_device;
-struct drm_plane;
+struct drm_format_info;
+struct drm_plane_state;
 struct platform_device;
 struct zynqmp_disp;
+struct zynqmp_disp_layer;
 struct zynqmp_dpsub;
 
 /**
@@ -47,10 +48,18 @@ void zynqmp_disp_disable(struct zynqmp_disp *disp);
 int zynqmp_disp_setup_clock(struct zynqmp_disp *disp,
 			    unsigned long mode_clock);
 
-void zynqmp_disp_plane_atomic_disable(struct drm_plane *plane,
-				      struct drm_atomic_state *state);
+void zynqmp_disp_blend_set_global_alpha(struct zynqmp_disp *disp,
+					bool enable, u32 alpha);
+
+u32 *zynqmp_disp_layer_drm_formats(struct zynqmp_disp_layer *layer,
+				   unsigned int *num_formats);
+void zynqmp_disp_layer_enable(struct zynqmp_disp_layer *layer);
+void zynqmp_disp_layer_disable(struct zynqmp_disp_layer *layer);
+void zynqmp_disp_layer_set_format(struct zynqmp_disp_layer *layer,
+				  const struct drm_format_info *info);
+int zynqmp_disp_layer_update(struct zynqmp_disp_layer *layer,
+			     struct drm_plane_state *state);
 
-int zynqmp_disp_drm_init(struct zynqmp_dpsub *dpsub);
 int zynqmp_disp_probe(struct zynqmp_dpsub *dpsub, struct drm_device *drm);
 void zynqmp_disp_remove(struct zynqmp_dpsub *dpsub);
 
diff --git a/drivers/gpu/drm/xlnx/zynqmp_dpsub.h b/drivers/gpu/drm/xlnx/zynqmp_dpsub.h
index 5b8bbbfd06e0..cfd4a2a5cfae 100644
--- a/drivers/gpu/drm/xlnx/zynqmp_dpsub.h
+++ b/drivers/gpu/drm/xlnx/zynqmp_dpsub.h
@@ -21,6 +21,7 @@ struct device;
 struct drm_bridge;
 struct drm_device;
 struct zynqmp_disp;
+struct zynqmp_disp_layer;
 struct zynqmp_dp;
 
 #define ZYNQMP_DPSUB_NUM_LAYERS				2
@@ -67,6 +68,7 @@ struct zynqmp_dpsub {
 	struct drm_bridge *bridge;
 
 	struct zynqmp_disp *disp;
+	struct zynqmp_disp_layer *layers[ZYNQMP_DPSUB_NUM_LAYERS];
 	struct zynqmp_dp *dp;
 
 	unsigned int dma_align;
diff --git a/drivers/gpu/drm/xlnx/zynqmp_kms.c b/drivers/gpu/drm/xlnx/zynqmp_kms.c
index 49042194480a..54358f1f51e5 100644
--- a/drivers/gpu/drm/xlnx/zynqmp_kms.c
+++ b/drivers/gpu/drm/xlnx/zynqmp_kms.c
@@ -16,7 +16,11 @@
 #include <drm/drm_connector.h>
 #include <drm/drm_crtc.h>
 #include <drm/drm_encoder.h>
+#include <drm/drm_fourcc.h>
+#include <drm/drm_framebuffer.h>
+#include <drm/drm_managed.h>
 #include <drm/drm_plane.h>
+#include <drm/drm_plane_helper.h>
 #include <drm/drm_simple_kms_helper.h>
 #include <drm/drm_vblank.h>
 
@@ -30,6 +34,137 @@
 #include "zynqmp_dpsub.h"
 #include "zynqmp_kms.h"
 
+/* -----------------------------------------------------------------------------
+ * DRM Planes
+ */
+
+static int zynqmp_dpsub_plane_atomic_check(struct drm_plane *plane,
+					   struct drm_atomic_state *state)
+{
+	struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state,
+										 plane);
+	struct drm_crtc_state *crtc_state;
+
+	if (!new_plane_state->crtc)
+		return 0;
+
+	crtc_state = drm_atomic_get_crtc_state(state, new_plane_state->crtc);
+	if (IS_ERR(crtc_state))
+		return PTR_ERR(crtc_state);
+
+	return drm_atomic_helper_check_plane_state(new_plane_state,
+						   crtc_state,
+						   DRM_PLANE_HELPER_NO_SCALING,
+						   DRM_PLANE_HELPER_NO_SCALING,
+						   false, false);
+}
+
+static void zynqmp_dpsub_plane_atomic_disable(struct drm_plane *plane,
+					      struct drm_atomic_state *state)
+{
+	struct drm_plane_state *old_state = drm_atomic_get_old_plane_state(state,
+									   plane);
+	struct zynqmp_dpsub *dpsub = to_zynqmp_dpsub(plane->dev);
+	struct zynqmp_disp_layer *layer = dpsub->layers[plane->index];
+
+	if (!old_state->fb)
+		return;
+
+	zynqmp_disp_layer_disable(layer);
+
+	if (plane->index == ZYNQMP_DPSUB_LAYER_GFX)
+		zynqmp_disp_blend_set_global_alpha(dpsub->disp, false,
+						   plane->state->alpha >> 8);
+}
+
+static void zynqmp_dpsub_plane_atomic_update(struct drm_plane *plane,
+					     struct drm_atomic_state *state)
+{
+	struct drm_plane_state *old_state = drm_atomic_get_old_plane_state(state, plane);
+	struct drm_plane_state *new_state = drm_atomic_get_new_plane_state(state, plane);
+	struct zynqmp_dpsub *dpsub = to_zynqmp_dpsub(plane->dev);
+	struct zynqmp_disp_layer *layer = dpsub->layers[plane->index];
+	bool format_changed = false;
+
+	if (!old_state->fb ||
+	    old_state->fb->format->format != new_state->fb->format->format)
+		format_changed = true;
+
+	/*
+	 * If the format has changed (including going from a previously
+	 * disabled state to any format), reconfigure the format. Disable the
+	 * plane first if needed.
+	 */
+	if (format_changed) {
+		if (old_state->fb)
+			zynqmp_disp_layer_disable(layer);
+
+		zynqmp_disp_layer_set_format(layer, new_state->fb->format);
+	}
+
+	zynqmp_disp_layer_update(layer, new_state);
+
+	if (plane->index == ZYNQMP_DPSUB_LAYER_GFX)
+		zynqmp_disp_blend_set_global_alpha(dpsub->disp, true,
+						   plane->state->alpha >> 8);
+
+	/* Enable or re-enable the plane if the format has changed. */
+	if (format_changed)
+		zynqmp_disp_layer_enable(layer);
+}
+
+static const struct drm_plane_helper_funcs zynqmp_dpsub_plane_helper_funcs = {
+	.atomic_check		= zynqmp_dpsub_plane_atomic_check,
+	.atomic_update		= zynqmp_dpsub_plane_atomic_update,
+	.atomic_disable		= zynqmp_dpsub_plane_atomic_disable,
+};
+
+static const struct drm_plane_funcs zynqmp_dpsub_plane_funcs = {
+	.update_plane		= drm_atomic_helper_update_plane,
+	.disable_plane		= drm_atomic_helper_disable_plane,
+	.destroy		= drm_plane_cleanup,
+	.reset			= drm_atomic_helper_plane_reset,
+	.atomic_duplicate_state	= drm_atomic_helper_plane_duplicate_state,
+	.atomic_destroy_state	= drm_atomic_helper_plane_destroy_state,
+};
+
+static int zynqmp_dpsub_create_planes(struct zynqmp_dpsub *dpsub)
+{
+	unsigned int i;
+	int ret;
+
+	for (i = 0; i < ARRAY_SIZE(dpsub->planes); i++) {
+		struct zynqmp_disp_layer *layer = dpsub->layers[i];
+		struct drm_plane *plane = &dpsub->planes[i];
+		enum drm_plane_type type;
+		unsigned int num_formats;
+		u32 *formats;
+
+		formats = zynqmp_disp_layer_drm_formats(layer, &num_formats);
+		if (!formats)
+			return -ENOMEM;
+
+		/* Graphics layer is primary, and video layer is overlay. */
+		type = i == ZYNQMP_DPSUB_LAYER_VID
+		     ? DRM_PLANE_TYPE_OVERLAY : DRM_PLANE_TYPE_PRIMARY;
+		ret = drm_universal_plane_init(&dpsub->drm, plane, 0,
+					       &zynqmp_dpsub_plane_funcs,
+					       formats, num_formats,
+					       NULL, type, NULL);
+		kfree(formats);
+		if (ret)
+			return ret;
+
+		drm_plane_helper_add(plane, &zynqmp_dpsub_plane_helper_funcs);
+
+		drm_plane_create_zpos_immutable_property(plane, i);
+		if (i == ZYNQMP_DPSUB_LAYER_GFX)
+			drm_plane_create_alpha_property(plane);
+	}
+
+	return 0;
+}
+
 /* -----------------------------------------------------------------------------
  * DRM CRTC
  */
@@ -78,7 +213,7 @@ static void zynqmp_dpsub_crtc_atomic_disable(struct drm_crtc *crtc,
 	 */
 	old_plane_state = drm_atomic_get_old_plane_state(state, crtc->primary);
 	if (old_plane_state)
-		zynqmp_disp_plane_atomic_disable(crtc->primary, state);
+		zynqmp_dpsub_plane_atomic_disable(crtc->primary, state);
 
 	zynqmp_disp_disable(dpsub->disp);
 
@@ -212,11 +347,8 @@ int zynqmp_dpsub_kms_init(struct zynqmp_dpsub *dpsub)
 	struct drm_connector *connector;
 	int ret;
 
-	/*
-	 * Initialize the DISP and DP components. This will creates planes,
-	 * CRTC, and a bridge for the DP encoder.
-	 */
-	ret = zynqmp_disp_drm_init(dpsub);
+	/* Create the planes and the CRTC, and nitialize the DP encoder. */
+	ret = zynqmp_dpsub_create_planes(dpsub);
 	if (ret)
 		return ret;
 
-- 
Regards,

Laurent Pinchart


^ permalink raw reply related	[flat|nested] 38+ messages in thread

* [PATCH 25/36] drm: xlnx: zynqmp_dpsub: Register AUX bus at bridge attach time
  2021-08-09  1:34 [PATCH 00/36] drm: xlnx: zynqmp_dpsub: Initial live video input support Laurent Pinchart
                   ` (23 preceding siblings ...)
  2021-08-09  1:34 ` [PATCH 24/36] drm: xlnx: zynqmp_dpsub: Move planes " Laurent Pinchart
@ 2021-08-09  1:34 ` Laurent Pinchart
  2021-08-09  1:34 ` [PATCH 26/36] drm: xlnx: zynqmp_dpsub: Move DP bridge init to zynqmp_dp_probe() Laurent Pinchart
                   ` (10 subsequent siblings)
  35 siblings, 0 replies; 38+ messages in thread
From: Laurent Pinchart @ 2021-08-09  1:34 UTC (permalink / raw)
  To: dri-devel; +Cc: Michal Simek, Jianqiang Chen

To prepare for operating as a standalone DP bridge with the DRM device
implemented in the PL, move registration of the AUX bus to bridge attach
time, as that's the earliest point when a DRM device is available.

The DRM device pointer stored in zynqmp_dp isn't used anymore, drop it.

Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
---
 drivers/gpu/drm/xlnx/zynqmp_dp.c | 41 +++++++++++++++++++-------------
 1 file changed, 24 insertions(+), 17 deletions(-)

diff --git a/drivers/gpu/drm/xlnx/zynqmp_dp.c b/drivers/gpu/drm/xlnx/zynqmp_dp.c
index 72fe3b7fb78e..e40ddfd27ff0 100644
--- a/drivers/gpu/drm/xlnx/zynqmp_dp.c
+++ b/drivers/gpu/drm/xlnx/zynqmp_dp.c
@@ -275,7 +275,6 @@ struct zynqmp_dp_config {
  * struct zynqmp_dp - Xilinx DisplayPort core
  * @dev: device structure
  * @dpsub: Display subsystem
- * @drm: DRM core
  * @iomem: device I/O memory for register access
  * @reset: reset controller
  * @irq: irq
@@ -296,7 +295,6 @@ struct zynqmp_dp_config {
 struct zynqmp_dp {
 	struct device *dev;
 	struct zynqmp_dpsub *dpsub;
-	struct drm_device *drm;
 	void __iomem *iomem;
 	struct reset_control *reset;
 	int irq;
@@ -1056,7 +1054,7 @@ static int zynqmp_dp_aux_init(struct zynqmp_dp *dp)
 
 	dp->aux.name = "ZynqMP DP AUX";
 	dp->aux.dev = dp->dev;
-	dp->aux.drm_dev = dp->drm;
+	dp->aux.drm_dev = dp->bridge.dev;
 	dp->aux.transfer = zynqmp_dp_aux_transfer;
 
 	return drm_dp_aux_register(&dp->aux);
@@ -1282,14 +1280,35 @@ static int zynqmp_dp_bridge_attach(struct drm_bridge *bridge,
 	struct zynqmp_dp *dp = bridge_to_dp(bridge);
 	int ret;
 
+	/* Initialize and register the AUX adapter. */
+	ret = zynqmp_dp_aux_init(dp);
+	if (ret) {
+		dev_err(dp->dev, "failed to initialize DP aux\n");
+		return ret;
+	}
+
 	if (dp->next_bridge) {
 		ret = drm_bridge_attach(bridge->encoder, dp->next_bridge,
 					bridge, flags);
 		if (ret < 0)
-			return ret;
+			goto error;
 	}
 
+	/* Now that initialisation is complete, enable interrupts. */
+	zynqmp_dp_write(dp, ZYNQMP_DP_INT_EN, ZYNQMP_DP_INT_ALL);
+
 	return 0;
+
+error:
+	zynqmp_dp_aux_cleanup(dp);
+	return ret;
+}
+
+static void zynqmp_dp_bridge_detach(struct drm_bridge *bridge)
+{
+	struct zynqmp_dp *dp = bridge_to_dp(bridge);
+
+	zynqmp_dp_aux_cleanup(dp);
 }
 
 static int zynqmp_dp_bridge_mode_valid(struct drm_bridge *bridge,
@@ -1494,6 +1513,7 @@ static struct edid *zynqmp_dp_bridge_get_edid(struct drm_bridge *bridge,
 
 static const struct drm_bridge_funcs zynqmp_dp_bridge_funcs = {
 	.attach = zynqmp_dp_bridge_attach,
+	.detach = zynqmp_dp_bridge_detach,
 	.mode_valid = zynqmp_dp_bridge_mode_valid,
 	.atomic_enable = zynqmp_dp_bridge_atomic_enable,
 	.atomic_disable = zynqmp_dp_bridge_atomic_disable,
@@ -1593,7 +1613,6 @@ int zynqmp_dp_drm_init(struct zynqmp_dpsub *dpsub)
 {
 	struct zynqmp_dp *dp = dpsub->dp;
 	struct drm_bridge *bridge = &dp->bridge;
-	int ret;
 
 	dp->config.misc0 &= ~ZYNQMP_DP_MAIN_STREAM_MISC0_SYNC_LOCK;
 	zynqmp_dp_set_format(dp, NULL, ZYNQMP_DPSUB_FORMAT_RGB, 8);
@@ -1605,16 +1624,6 @@ int zynqmp_dp_drm_init(struct zynqmp_dpsub *dpsub)
 	bridge->type = DRM_MODE_CONNECTOR_DisplayPort;
 	dpsub->bridge = bridge;
 
-	/* Initialize and register the AUX adapter. */
-	ret = zynqmp_dp_aux_init(dp);
-	if (ret) {
-		dev_err(dp->dev, "failed to initialize DP aux\n");
-		return ret;
-	}
-
-	/* Now that initialisation is complete, enable interrupts. */
-	zynqmp_dp_write(dp, ZYNQMP_DP_INT_EN, ZYNQMP_DP_INT_ALL);
-
 	return 0;
 }
 
@@ -1632,7 +1641,6 @@ int zynqmp_dp_probe(struct zynqmp_dpsub *dpsub, struct drm_device *drm)
 	dp->dev = &pdev->dev;
 	dp->dpsub = dpsub;
 	dp->status = connector_status_disconnected;
-	dp->drm = drm;
 
 	INIT_DELAYED_WORK(&dp->hpd_work, zynqmp_dp_hpd_work_func);
 
@@ -1718,7 +1726,6 @@ void zynqmp_dp_remove(struct zynqmp_dpsub *dpsub)
 	disable_irq(dp->irq);
 
 	cancel_delayed_work_sync(&dp->hpd_work);
-	zynqmp_dp_aux_cleanup(dp);
 
 	zynqmp_dp_write(dp, ZYNQMP_DP_TRANSMITTER_ENABLE, 0);
 	zynqmp_dp_write(dp, ZYNQMP_DP_INT_DS, 0xffffffff);
-- 
Regards,

Laurent Pinchart


^ permalink raw reply related	[flat|nested] 38+ messages in thread

* [PATCH 26/36] drm: xlnx: zynqmp_dpsub: Move DP bridge init to zynqmp_dp_probe()
  2021-08-09  1:34 [PATCH 00/36] drm: xlnx: zynqmp_dpsub: Initial live video input support Laurent Pinchart
                   ` (24 preceding siblings ...)
  2021-08-09  1:34 ` [PATCH 25/36] drm: xlnx: zynqmp_dpsub: Register AUX bus at bridge attach time Laurent Pinchart
@ 2021-08-09  1:34 ` Laurent Pinchart
  2021-08-09  1:34 ` [PATCH 27/36] drm: xlnx: zynqmp_dpsub: Manage DP and DISP allocations manually Laurent Pinchart
                   ` (9 subsequent siblings)
  35 siblings, 0 replies; 38+ messages in thread
From: Laurent Pinchart @ 2021-08-09  1:34 UTC (permalink / raw)
  To: dri-devel; +Cc: Michal Simek, Jianqiang Chen

There's no need to delay bridge initialization, move it to
zynqmp_dp_probe() and drop the zynqmp_dp_drm_init() function.

Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
---
 drivers/gpu/drm/xlnx/zynqmp_dp.c  | 30 ++++++++++++------------------
 drivers/gpu/drm/xlnx/zynqmp_dp.h  |  1 -
 drivers/gpu/drm/xlnx/zynqmp_kms.c |  6 +-----
 3 files changed, 13 insertions(+), 24 deletions(-)

diff --git a/drivers/gpu/drm/xlnx/zynqmp_dp.c b/drivers/gpu/drm/xlnx/zynqmp_dp.c
index e40ddfd27ff0..360175b8fc1f 100644
--- a/drivers/gpu/drm/xlnx/zynqmp_dp.c
+++ b/drivers/gpu/drm/xlnx/zynqmp_dp.c
@@ -1609,27 +1609,10 @@ static irqreturn_t zynqmp_dp_irq_handler(int irq, void *data)
  * Initialization & Cleanup
  */
 
-int zynqmp_dp_drm_init(struct zynqmp_dpsub *dpsub)
-{
-	struct zynqmp_dp *dp = dpsub->dp;
-	struct drm_bridge *bridge = &dp->bridge;
-
-	dp->config.misc0 &= ~ZYNQMP_DP_MAIN_STREAM_MISC0_SYNC_LOCK;
-	zynqmp_dp_set_format(dp, NULL, ZYNQMP_DPSUB_FORMAT_RGB, 8);
-
-	/* Initialize the bridge. */
-	bridge->funcs = &zynqmp_dp_bridge_funcs;
-	bridge->ops = DRM_BRIDGE_OP_DETECT | DRM_BRIDGE_OP_EDID
-		    | DRM_BRIDGE_OP_HPD;
-	bridge->type = DRM_MODE_CONNECTOR_DisplayPort;
-	dpsub->bridge = bridge;
-
-	return 0;
-}
-
 int zynqmp_dp_probe(struct zynqmp_dpsub *dpsub, struct drm_device *drm)
 {
 	struct platform_device *pdev = to_platform_device(dpsub->dev);
+	struct drm_bridge *bridge;
 	struct zynqmp_dp *dp;
 	struct resource *res;
 	int ret;
@@ -1672,6 +1655,14 @@ int zynqmp_dp_probe(struct zynqmp_dpsub *dpsub, struct drm_device *drm)
 	if (ret)
 		goto err_reset;
 
+	/* Initialize the bridge. */
+	bridge = &dp->bridge;
+	bridge->funcs = &zynqmp_dp_bridge_funcs;
+	bridge->ops = DRM_BRIDGE_OP_DETECT | DRM_BRIDGE_OP_EDID
+		    | DRM_BRIDGE_OP_HPD;
+	bridge->type = DRM_MODE_CONNECTOR_DisplayPort;
+	dpsub->bridge = bridge;
+
 	/*
 	 * Acquire the next bridge in the chain. Ignore errors caused by port@5
 	 * not being connected for backward-compatibility with older DTs.
@@ -1682,6 +1673,9 @@ int zynqmp_dp_probe(struct zynqmp_dpsub *dpsub, struct drm_device *drm)
 		goto err_reset;
 
 	/* Initialize the hardware. */
+	dp->config.misc0 &= ~ZYNQMP_DP_MAIN_STREAM_MISC0_SYNC_LOCK;
+	zynqmp_dp_set_format(dp, NULL, ZYNQMP_DPSUB_FORMAT_RGB, 8);
+
 	zynqmp_dp_write(dp, ZYNQMP_DP_TX_PHY_POWER_DOWN,
 			ZYNQMP_DP_TX_PHY_POWER_DOWN_ALL);
 	zynqmp_dp_set(dp, ZYNQMP_DP_PHY_RESET, ZYNQMP_DP_PHY_RESET_ALL_RESET);
diff --git a/drivers/gpu/drm/xlnx/zynqmp_dp.h b/drivers/gpu/drm/xlnx/zynqmp_dp.h
index 4507740093f6..736d810fa16f 100644
--- a/drivers/gpu/drm/xlnx/zynqmp_dp.h
+++ b/drivers/gpu/drm/xlnx/zynqmp_dp.h
@@ -20,7 +20,6 @@ struct zynqmp_dpsub;
 void zynqmp_dp_enable_vblank(struct zynqmp_dp *dp);
 void zynqmp_dp_disable_vblank(struct zynqmp_dp *dp);
 
-int zynqmp_dp_drm_init(struct zynqmp_dpsub *dpsub);
 int zynqmp_dp_probe(struct zynqmp_dpsub *dpsub, struct drm_device *drm);
 void zynqmp_dp_remove(struct zynqmp_dpsub *dpsub);
 
diff --git a/drivers/gpu/drm/xlnx/zynqmp_kms.c b/drivers/gpu/drm/xlnx/zynqmp_kms.c
index 54358f1f51e5..51903bc1de2b 100644
--- a/drivers/gpu/drm/xlnx/zynqmp_kms.c
+++ b/drivers/gpu/drm/xlnx/zynqmp_kms.c
@@ -347,7 +347,7 @@ int zynqmp_dpsub_kms_init(struct zynqmp_dpsub *dpsub)
 	struct drm_connector *connector;
 	int ret;
 
-	/* Create the planes and the CRTC, and nitialize the DP encoder. */
+	/* Create the planes and the CRTC. */
 	ret = zynqmp_dpsub_create_planes(dpsub);
 	if (ret)
 		return ret;
@@ -358,10 +358,6 @@ int zynqmp_dpsub_kms_init(struct zynqmp_dpsub *dpsub)
 
 	zynqmp_dpsub_map_crtc_to_plane(dpsub);
 
-	ret = zynqmp_dp_drm_init(dpsub);
-	if (ret)
-		return ret;
-
 	/* Create the encoder and attach the bridge. */
 	encoder->possible_crtcs |= drm_crtc_mask(&dpsub->crtc);
 	drm_simple_encoder_init(&dpsub->drm, encoder, DRM_MODE_ENCODER_NONE);
-- 
Regards,

Laurent Pinchart


^ permalink raw reply related	[flat|nested] 38+ messages in thread

* [PATCH 27/36] drm: xlnx: zynqmp_dpsub: Manage DP and DISP allocations manually
  2021-08-09  1:34 [PATCH 00/36] drm: xlnx: zynqmp_dpsub: Initial live video input support Laurent Pinchart
                   ` (25 preceding siblings ...)
  2021-08-09  1:34 ` [PATCH 26/36] drm: xlnx: zynqmp_dpsub: Move DP bridge init to zynqmp_dp_probe() Laurent Pinchart
@ 2021-08-09  1:34 ` Laurent Pinchart
  2021-08-09  1:34 ` [PATCH 28/36] drm: xlnx: zynqmp_dpsub: Move all DRM init and cleanup to zynqmp_kms.c Laurent Pinchart
                   ` (8 subsequent siblings)
  35 siblings, 0 replies; 38+ messages in thread
From: Laurent Pinchart @ 2021-08-09  1:34 UTC (permalink / raw)
  To: dri-devel; +Cc: Michal Simek, Jianqiang Chen

The zynqmp_disp and zynqmp_dp structures are allocated with
drmm_kzalloc(). While this simplifies management of memory, it requires
a DRM device, which will not be available at probe time when the DP
bridge will be used standalone, with a DRM device in the PL. To prepare
for this, switch to manual allocation for zynqmp_disp and zynqmp_dp. The
cleanup still uses the DRM managed infrastructure, but one level up, at
the top level. This will be addressed separately.

Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
---
 drivers/gpu/drm/xlnx/zynqmp_disp.c  | 34 +++++++++++++++++++----------
 drivers/gpu/drm/xlnx/zynqmp_disp.h  |  3 +--
 drivers/gpu/drm/xlnx/zynqmp_dp.c    | 30 +++++++++++++++----------
 drivers/gpu/drm/xlnx/zynqmp_dp.h    |  3 +--
 drivers/gpu/drm/xlnx/zynqmp_dpsub.c | 17 +++++++++++++--
 5 files changed, 57 insertions(+), 30 deletions(-)

diff --git a/drivers/gpu/drm/xlnx/zynqmp_disp.c b/drivers/gpu/drm/xlnx/zynqmp_disp.c
index cc07cb2a4d0f..5c39df0fbe59 100644
--- a/drivers/gpu/drm/xlnx/zynqmp_disp.c
+++ b/drivers/gpu/drm/xlnx/zynqmp_disp.c
@@ -12,7 +12,6 @@
 #include <drm/drm_fb_cma_helper.h>
 #include <drm/drm_fourcc.h>
 #include <drm/drm_framebuffer.h>
-#include <drm/drm_managed.h>
 #include <drm/drm_plane.h>
 
 #include <linux/clk.h>
@@ -21,6 +20,7 @@
 #include <linux/module.h>
 #include <linux/of.h>
 #include <linux/platform_device.h>
+#include <linux/slab.h>
 
 #include "zynqmp_disp.h"
 #include "zynqmp_disp_regs.h"
@@ -1220,7 +1220,7 @@ int zynqmp_disp_setup_clock(struct zynqmp_disp *disp,
  * Initialization & Cleanup
  */
 
-int zynqmp_disp_probe(struct zynqmp_dpsub *dpsub, struct drm_device *drm)
+int zynqmp_disp_probe(struct zynqmp_dpsub *dpsub)
 {
 	struct platform_device *pdev = to_platform_device(dpsub->dev);
 	struct zynqmp_disp *disp;
@@ -1228,38 +1228,48 @@ int zynqmp_disp_probe(struct zynqmp_dpsub *dpsub, struct drm_device *drm)
 	struct resource *res;
 	int ret;
 
-	disp = drmm_kzalloc(drm, sizeof(*disp), GFP_KERNEL);
+	disp = kzalloc(sizeof(*disp), GFP_KERNEL);
 	if (!disp)
 		return -ENOMEM;
 
 	disp->dev = &pdev->dev;
 	disp->dpsub = dpsub;
 
-	dpsub->disp = disp;
-
 	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "blend");
 	disp->blend.base = devm_ioremap_resource(disp->dev, res);
-	if (IS_ERR(disp->blend.base))
-		return PTR_ERR(disp->blend.base);
+	if (IS_ERR(disp->blend.base)) {
+		ret = PTR_ERR(disp->blend.base);
+		goto error;
+	}
 
 	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "av_buf");
 	disp->avbuf.base = devm_ioremap_resource(disp->dev, res);
-	if (IS_ERR(disp->avbuf.base))
-		return PTR_ERR(disp->avbuf.base);
+	if (IS_ERR(disp->avbuf.base)) {
+		ret = PTR_ERR(disp->avbuf.base);
+		goto error;
+	}
 
 	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "aud");
 	disp->audio.base = devm_ioremap_resource(disp->dev, res);
-	if (IS_ERR(disp->audio.base))
-		return PTR_ERR(disp->audio.base);
+	if (IS_ERR(disp->audio.base)) {
+		ret = PTR_ERR(disp->audio.base);
+		goto error;
+	}
 
 	ret = zynqmp_disp_create_layers(disp);
 	if (ret)
-		return ret;
+		goto error;
 
 	layer = &disp->layers[ZYNQMP_DPSUB_LAYER_VID];
 	dpsub->dma_align = 1 << layer->dmas[0].chan->device->copy_align;
 
+	dpsub->disp = disp;
+
 	return 0;
+
+error:
+	kfree(disp);
+	return ret;
 }
 
 void zynqmp_disp_remove(struct zynqmp_dpsub *dpsub)
diff --git a/drivers/gpu/drm/xlnx/zynqmp_disp.h b/drivers/gpu/drm/xlnx/zynqmp_disp.h
index 663f7d67c78f..9b8b202224d9 100644
--- a/drivers/gpu/drm/xlnx/zynqmp_disp.h
+++ b/drivers/gpu/drm/xlnx/zynqmp_disp.h
@@ -25,7 +25,6 @@
 #define ZYNQMP_DISP_MAX_DMA_BIT				44
 
 struct device;
-struct drm_device;
 struct drm_format_info;
 struct drm_plane_state;
 struct platform_device;
@@ -60,7 +59,7 @@ void zynqmp_disp_layer_set_format(struct zynqmp_disp_layer *layer,
 int zynqmp_disp_layer_update(struct zynqmp_disp_layer *layer,
 			     struct drm_plane_state *state);
 
-int zynqmp_disp_probe(struct zynqmp_dpsub *dpsub, struct drm_device *drm);
+int zynqmp_disp_probe(struct zynqmp_dpsub *dpsub);
 void zynqmp_disp_remove(struct zynqmp_dpsub *dpsub);
 
 #endif /* _ZYNQMP_DISP_H_ */
diff --git a/drivers/gpu/drm/xlnx/zynqmp_dp.c b/drivers/gpu/drm/xlnx/zynqmp_dp.c
index 360175b8fc1f..25cde59b1e05 100644
--- a/drivers/gpu/drm/xlnx/zynqmp_dp.c
+++ b/drivers/gpu/drm/xlnx/zynqmp_dp.c
@@ -14,7 +14,6 @@
 #include <drm/drm_device.h>
 #include <drm/drm_dp_helper.h>
 #include <drm/drm_edid.h>
-#include <drm/drm_managed.h>
 #include <drm/drm_modes.h>
 #include <drm/drm_of.h>
 
@@ -26,6 +25,7 @@
 #include <linux/pm_runtime.h>
 #include <linux/phy/phy.h>
 #include <linux/reset.h>
+#include <linux/slab.h>
 
 #include "zynqmp_disp.h"
 #include "zynqmp_dp.h"
@@ -1609,7 +1609,7 @@ static irqreturn_t zynqmp_dp_irq_handler(int irq, void *data)
  * Initialization & Cleanup
  */
 
-int zynqmp_dp_probe(struct zynqmp_dpsub *dpsub, struct drm_device *drm)
+int zynqmp_dp_probe(struct zynqmp_dpsub *dpsub)
 {
 	struct platform_device *pdev = to_platform_device(dpsub->dev);
 	struct drm_bridge *bridge;
@@ -1617,7 +1617,7 @@ int zynqmp_dp_probe(struct zynqmp_dpsub *dpsub, struct drm_device *drm)
 	struct resource *res;
 	int ret;
 
-	dp = drmm_kzalloc(drm, sizeof(*dp), GFP_KERNEL);
+	dp = kzalloc(sizeof(*dp), GFP_KERNEL);
 	if (!dp)
 		return -ENOMEM;
 
@@ -1627,29 +1627,32 @@ int zynqmp_dp_probe(struct zynqmp_dpsub *dpsub, struct drm_device *drm)
 
 	INIT_DELAYED_WORK(&dp->hpd_work, zynqmp_dp_hpd_work_func);
 
-	dpsub->dp = dp;
-
 	/* Acquire all resources (IOMEM, IRQ and PHYs). */
 	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dp");
 	dp->iomem = devm_ioremap_resource(dp->dev, res);
-	if (IS_ERR(dp->iomem))
-		return PTR_ERR(dp->iomem);
+	if (IS_ERR(dp->iomem)) {
+		ret = PTR_ERR(dp->iomem);
+		goto err_free;
+	}
 
 	dp->irq = platform_get_irq(pdev, 0);
-	if (dp->irq < 0)
-		return dp->irq;
+	if (dp->irq < 0) {
+		ret = dp->irq;
+		goto err_free;
+	}
 
 	dp->reset = devm_reset_control_get(dp->dev, NULL);
 	if (IS_ERR(dp->reset)) {
 		if (PTR_ERR(dp->reset) != -EPROBE_DEFER)
 			dev_err(dp->dev, "failed to get reset: %ld\n",
 				PTR_ERR(dp->reset));
-		return PTR_ERR(dp->reset);
+		ret = PTR_ERR(dp->reset);
+		goto err_free;
 	}
 
 	ret = zynqmp_dp_reset(dp, false);
 	if (ret < 0)
-		return ret;
+		goto err_free;
 
 	ret = zynqmp_dp_phy_probe(dp);
 	if (ret)
@@ -1699,6 +1702,8 @@ int zynqmp_dp_probe(struct zynqmp_dpsub *dpsub, struct drm_device *drm)
 	if (ret < 0)
 		goto err_phy_exit;
 
+	dpsub->dp = dp;
+
 	dev_dbg(dp->dev, "ZynqMP DisplayPort Tx probed with %u lanes\n",
 		dp->num_lanes);
 
@@ -1708,7 +1713,8 @@ int zynqmp_dp_probe(struct zynqmp_dpsub *dpsub, struct drm_device *drm)
 	zynqmp_dp_phy_exit(dp);
 err_reset:
 	zynqmp_dp_reset(dp, true);
-
+err_free:
+	kfree(dp);
 	return ret;
 }
 
diff --git a/drivers/gpu/drm/xlnx/zynqmp_dp.h b/drivers/gpu/drm/xlnx/zynqmp_dp.h
index 736d810fa16f..f077d7fbd0ad 100644
--- a/drivers/gpu/drm/xlnx/zynqmp_dp.h
+++ b/drivers/gpu/drm/xlnx/zynqmp_dp.h
@@ -12,7 +12,6 @@
 #ifndef _ZYNQMP_DP_H_
 #define _ZYNQMP_DP_H_
 
-struct drm_device;
 struct platform_device;
 struct zynqmp_dp;
 struct zynqmp_dpsub;
@@ -20,7 +19,7 @@ struct zynqmp_dpsub;
 void zynqmp_dp_enable_vblank(struct zynqmp_dp *dp);
 void zynqmp_dp_disable_vblank(struct zynqmp_dp *dp);
 
-int zynqmp_dp_probe(struct zynqmp_dpsub *dpsub, struct drm_device *drm);
+int zynqmp_dp_probe(struct zynqmp_dpsub *dpsub);
 void zynqmp_dp_remove(struct zynqmp_dpsub *dpsub);
 
 #endif /* _ZYNQMP_DP_H_ */
diff --git a/drivers/gpu/drm/xlnx/zynqmp_dpsub.c b/drivers/gpu/drm/xlnx/zynqmp_dpsub.c
index 6f4e78b2a7c0..e98e7e3b37d7 100644
--- a/drivers/gpu/drm/xlnx/zynqmp_dpsub.c
+++ b/drivers/gpu/drm/xlnx/zynqmp_dpsub.c
@@ -15,6 +15,7 @@
 #include <linux/of_reserved_mem.h>
 #include <linux/platform_device.h>
 #include <linux/pm_runtime.h>
+#include <linux/slab.h>
 
 #include <drm/drm_atomic_helper.h>
 #include <drm/drm_bridge_connector.h>
@@ -246,6 +247,14 @@ static int zynqmp_dpsub_init_clocks(struct zynqmp_dpsub *dpsub)
 	return 0;
 }
 
+static void zynqmp_dpsub_release(struct drm_device *drm, void *res)
+{
+	struct zynqmp_dpsub *dpsub = res;
+
+	kfree(dpsub->disp);
+	kfree(dpsub->dp);
+}
+
 static int zynqmp_dpsub_probe(struct platform_device *pdev)
 {
 	struct zynqmp_dpsub *dpsub;
@@ -257,6 +266,10 @@ static int zynqmp_dpsub_probe(struct platform_device *pdev)
 	if (IS_ERR(dpsub))
 		return PTR_ERR(dpsub);
 
+	ret = drmm_add_action(&dpsub->drm, zynqmp_dpsub_release, dpsub);
+	if (ret < 0)
+		return ret;
+
 	dpsub->dev = &pdev->dev;
 	platform_set_drvdata(pdev, dpsub);
 
@@ -275,11 +288,11 @@ static int zynqmp_dpsub_probe(struct platform_device *pdev)
 	 * DP should be probed first so that the zynqmp_disp can set the output
 	 * format accordingly.
 	 */
-	ret = zynqmp_dp_probe(dpsub, &dpsub->drm);
+	ret = zynqmp_dp_probe(dpsub);
 	if (ret)
 		goto err_pm;
 
-	ret = zynqmp_disp_probe(dpsub, &dpsub->drm);
+	ret = zynqmp_disp_probe(dpsub);
 	if (ret)
 		goto err_dp;
 
-- 
Regards,

Laurent Pinchart


^ permalink raw reply related	[flat|nested] 38+ messages in thread

* [PATCH 28/36] drm: xlnx: zynqmp_dpsub: Move all DRM init and cleanup to zynqmp_kms.c
  2021-08-09  1:34 [PATCH 00/36] drm: xlnx: zynqmp_dpsub: Initial live video input support Laurent Pinchart
                   ` (26 preceding siblings ...)
  2021-08-09  1:34 ` [PATCH 27/36] drm: xlnx: zynqmp_dpsub: Manage DP and DISP allocations manually Laurent Pinchart
@ 2021-08-09  1:34 ` Laurent Pinchart
  2021-08-09  1:34 ` [PATCH 29/36] drm: xlnx: zynqmp_dpsub: Decouple DRM device from zynqmp_dpsub Laurent Pinchart
                   ` (7 subsequent siblings)
  35 siblings, 0 replies; 38+ messages in thread
From: Laurent Pinchart @ 2021-08-09  1:34 UTC (permalink / raw)
  To: dri-devel; +Cc: Michal Simek, Jianqiang Chen

Continue the isolation of DRM/KMS code by moving all DRM init and
cleanup from zynqmp_dpsub.c to zynqmp_kms.c.

Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
---
 drivers/gpu/drm/xlnx/zynqmp_dpsub.c | 120 +-------------------------
 drivers/gpu/drm/xlnx/zynqmp_dpsub.h |   5 --
 drivers/gpu/drm/xlnx/zynqmp_kms.c   | 126 +++++++++++++++++++++++++++-
 drivers/gpu/drm/xlnx/zynqmp_kms.h   |   5 +-
 4 files changed, 130 insertions(+), 126 deletions(-)

diff --git a/drivers/gpu/drm/xlnx/zynqmp_dpsub.c b/drivers/gpu/drm/xlnx/zynqmp_dpsub.c
index e98e7e3b37d7..75209272ccb2 100644
--- a/drivers/gpu/drm/xlnx/zynqmp_dpsub.c
+++ b/drivers/gpu/drm/xlnx/zynqmp_dpsub.c
@@ -18,125 +18,15 @@
 #include <linux/slab.h>
 
 #include <drm/drm_atomic_helper.h>
-#include <drm/drm_bridge_connector.h>
-#include <drm/drm_device.h>
 #include <drm/drm_drv.h>
-#include <drm/drm_fb_helper.h>
-#include <drm/drm_fourcc.h>
-#include <drm/drm_gem_cma_helper.h>
-#include <drm/drm_gem_framebuffer_helper.h>
 #include <drm/drm_managed.h>
-#include <drm/drm_mode_config.h>
-#include <drm/drm_probe_helper.h>
-#include <drm/drm_vblank.h>
+#include <drm/drm_modeset_helper.h>
 
 #include "zynqmp_disp.h"
 #include "zynqmp_dp.h"
 #include "zynqmp_dpsub.h"
 #include "zynqmp_kms.h"
 
-/* -----------------------------------------------------------------------------
- * Dumb Buffer & Framebuffer Allocation
- */
-
-static int zynqmp_dpsub_dumb_create(struct drm_file *file_priv,
-				    struct drm_device *drm,
-				    struct drm_mode_create_dumb *args)
-{
-	struct zynqmp_dpsub *dpsub = to_zynqmp_dpsub(drm);
-	unsigned int pitch = DIV_ROUND_UP(args->width * args->bpp, 8);
-
-	/* Enforce the alignment constraints of the DMA engine. */
-	args->pitch = ALIGN(pitch, dpsub->dma_align);
-
-	return drm_gem_cma_dumb_create_internal(file_priv, drm, args);
-}
-
-static struct drm_framebuffer *
-zynqmp_dpsub_fb_create(struct drm_device *drm, struct drm_file *file_priv,
-		       const struct drm_mode_fb_cmd2 *mode_cmd)
-{
-	struct zynqmp_dpsub *dpsub = to_zynqmp_dpsub(drm);
-	struct drm_mode_fb_cmd2 cmd = *mode_cmd;
-	unsigned int i;
-
-	/* Enforce the alignment constraints of the DMA engine. */
-	for (i = 0; i < ARRAY_SIZE(cmd.pitches); ++i)
-		cmd.pitches[i] = ALIGN(cmd.pitches[i], dpsub->dma_align);
-
-	return drm_gem_fb_create(drm, file_priv, &cmd);
-}
-
-static const struct drm_mode_config_funcs zynqmp_dpsub_mode_config_funcs = {
-	.fb_create		= zynqmp_dpsub_fb_create,
-	.atomic_check		= drm_atomic_helper_check,
-	.atomic_commit		= drm_atomic_helper_commit,
-};
-
-/* -----------------------------------------------------------------------------
- * DRM/KMS Driver
- */
-
-DEFINE_DRM_GEM_CMA_FOPS(zynqmp_dpsub_drm_fops);
-
-static const struct drm_driver zynqmp_dpsub_drm_driver = {
-	.driver_features		= DRIVER_MODESET | DRIVER_GEM |
-					  DRIVER_ATOMIC,
-
-	DRM_GEM_CMA_DRIVER_OPS_WITH_DUMB_CREATE(zynqmp_dpsub_dumb_create),
-
-	.fops				= &zynqmp_dpsub_drm_fops,
-
-	.name				= "zynqmp-dpsub",
-	.desc				= "Xilinx DisplayPort Subsystem Driver",
-	.date				= "20130509",
-	.major				= 1,
-	.minor				= 0,
-};
-
-static int zynqmp_dpsub_drm_init(struct zynqmp_dpsub *dpsub)
-{
-	struct drm_device *drm = &dpsub->drm;
-	int ret;
-
-	/* Initialize mode config, vblank and the KMS poll helper. */
-	ret = drmm_mode_config_init(drm);
-	if (ret < 0)
-		return ret;
-
-	drm->mode_config.funcs = &zynqmp_dpsub_mode_config_funcs;
-	drm->mode_config.min_width = 0;
-	drm->mode_config.min_height = 0;
-	drm->mode_config.max_width = ZYNQMP_DISP_MAX_WIDTH;
-	drm->mode_config.max_height = ZYNQMP_DISP_MAX_HEIGHT;
-
-	ret = drm_vblank_init(drm, 1);
-	if (ret)
-		return ret;
-
-	drm_kms_helper_poll_init(drm);
-
-	ret = zynqmp_dpsub_kms_init(dpsub);
-	if (ret < 0)
-		goto err_poll_fini;
-
-	/* Reset all components and register the DRM device. */
-	drm_mode_config_reset(drm);
-
-	ret = drm_dev_register(drm, 0);
-	if (ret < 0)
-		goto err_poll_fini;
-
-	/* Initialize fbdev generic emulation. */
-	drm_fbdev_generic_setup(drm, 24);
-
-	return 0;
-
-err_poll_fini:
-	drm_kms_helper_poll_fini(drm);
-	return ret;
-}
-
 /* -----------------------------------------------------------------------------
  * Power Management
  */
@@ -319,14 +209,8 @@ static int zynqmp_dpsub_probe(struct platform_device *pdev)
 static int zynqmp_dpsub_remove(struct platform_device *pdev)
 {
 	struct zynqmp_dpsub *dpsub = platform_get_drvdata(pdev);
-	struct drm_device *drm = &dpsub->drm;
 
-	if (dpsub->connector)
-		drm_bridge_connector_disable_hpd(dpsub->connector);
-
-	drm_dev_unregister(drm);
-	drm_atomic_helper_shutdown(drm);
-	drm_kms_helper_poll_fini(drm);
+	zynqmp_dpsub_drm_cleanup(dpsub);
 
 	zynqmp_disp_remove(dpsub);
 	zynqmp_dp_remove(dpsub);
diff --git a/drivers/gpu/drm/xlnx/zynqmp_dpsub.h b/drivers/gpu/drm/xlnx/zynqmp_dpsub.h
index cfd4a2a5cfae..1778092e0829 100644
--- a/drivers/gpu/drm/xlnx/zynqmp_dpsub.h
+++ b/drivers/gpu/drm/xlnx/zynqmp_dpsub.h
@@ -74,11 +74,6 @@ struct zynqmp_dpsub {
 	unsigned int dma_align;
 };
 
-static inline struct zynqmp_dpsub *to_zynqmp_dpsub(struct drm_device *drm)
-{
-	return container_of(drm, struct zynqmp_dpsub, drm);
-}
-
 bool zynqmp_dpsub_audio_enabled(struct zynqmp_dpsub *dpsub);
 unsigned int zynqmp_dpsub_get_audio_clk_rate(struct zynqmp_dpsub *dpsub);
 
diff --git a/drivers/gpu/drm/xlnx/zynqmp_kms.c b/drivers/gpu/drm/xlnx/zynqmp_kms.c
index 51903bc1de2b..7b6af07baad4 100644
--- a/drivers/gpu/drm/xlnx/zynqmp_kms.c
+++ b/drivers/gpu/drm/xlnx/zynqmp_kms.c
@@ -15,12 +15,19 @@
 #include <drm/drm_bridge_connector.h>
 #include <drm/drm_connector.h>
 #include <drm/drm_crtc.h>
+#include <drm/drm_device.h>
+#include <drm/drm_drv.h>
 #include <drm/drm_encoder.h>
+#include <drm/drm_fb_helper.h>
 #include <drm/drm_fourcc.h>
 #include <drm/drm_framebuffer.h>
+#include <drm/drm_gem_cma_helper.h>
+#include <drm/drm_gem_framebuffer_helper.h>
 #include <drm/drm_managed.h>
+#include <drm/drm_mode_config.h>
 #include <drm/drm_plane.h>
 #include <drm/drm_plane_helper.h>
+#include <drm/drm_probe_helper.h>
 #include <drm/drm_simple_kms_helper.h>
 #include <drm/drm_vblank.h>
 
@@ -34,6 +41,11 @@
 #include "zynqmp_dpsub.h"
 #include "zynqmp_kms.h"
 
+static inline struct zynqmp_dpsub *to_zynqmp_dpsub(struct drm_device *drm)
+{
+	return container_of(drm, struct zynqmp_dpsub, drm);
+}
+
 /* -----------------------------------------------------------------------------
  * DRM Planes
  */
@@ -338,10 +350,65 @@ void zynqmp_dpsub_handle_vblank(struct zynqmp_dpsub *dpsub)
 }
 
 /* -----------------------------------------------------------------------------
- * Initialization
+ * Dumb Buffer & Framebuffer Allocation
  */
 
-int zynqmp_dpsub_kms_init(struct zynqmp_dpsub *dpsub)
+static int zynqmp_dpsub_dumb_create(struct drm_file *file_priv,
+				    struct drm_device *drm,
+				    struct drm_mode_create_dumb *args)
+{
+	struct zynqmp_dpsub *dpsub = to_zynqmp_dpsub(drm);
+	unsigned int pitch = DIV_ROUND_UP(args->width * args->bpp, 8);
+
+	/* Enforce the alignment constraints of the DMA engine. */
+	args->pitch = ALIGN(pitch, dpsub->dma_align);
+
+	return drm_gem_cma_dumb_create_internal(file_priv, drm, args);
+}
+
+static struct drm_framebuffer *
+zynqmp_dpsub_fb_create(struct drm_device *drm, struct drm_file *file_priv,
+		       const struct drm_mode_fb_cmd2 *mode_cmd)
+{
+	struct zynqmp_dpsub *dpsub = to_zynqmp_dpsub(drm);
+	struct drm_mode_fb_cmd2 cmd = *mode_cmd;
+	unsigned int i;
+
+	/* Enforce the alignment constraints of the DMA engine. */
+	for (i = 0; i < ARRAY_SIZE(cmd.pitches); ++i)
+		cmd.pitches[i] = ALIGN(cmd.pitches[i], dpsub->dma_align);
+
+	return drm_gem_fb_create(drm, file_priv, &cmd);
+}
+
+static const struct drm_mode_config_funcs zynqmp_dpsub_mode_config_funcs = {
+	.fb_create		= zynqmp_dpsub_fb_create,
+	.atomic_check		= drm_atomic_helper_check,
+	.atomic_commit		= drm_atomic_helper_commit,
+};
+
+/* -----------------------------------------------------------------------------
+ * DRM/KMS Driver
+ */
+
+DEFINE_DRM_GEM_CMA_FOPS(zynqmp_dpsub_drm_fops);
+
+const struct drm_driver zynqmp_dpsub_drm_driver = {
+	.driver_features		= DRIVER_MODESET | DRIVER_GEM |
+					  DRIVER_ATOMIC,
+
+	DRM_GEM_CMA_DRIVER_OPS_WITH_DUMB_CREATE(zynqmp_dpsub_dumb_create),
+
+	.fops				= &zynqmp_dpsub_drm_fops,
+
+	.name				= "zynqmp-dpsub",
+	.desc				= "Xilinx DisplayPort Subsystem Driver",
+	.date				= "20130509",
+	.major				= 1,
+	.minor				= 0,
+};
+
+static int zynqmp_dpsub_kms_init(struct zynqmp_dpsub *dpsub)
 {
 	struct drm_encoder *encoder = &dpsub->encoder;
 	struct drm_connector *connector;
@@ -387,3 +454,58 @@ int zynqmp_dpsub_kms_init(struct zynqmp_dpsub *dpsub)
 
 	return 0;
 }
+
+int zynqmp_dpsub_drm_init(struct zynqmp_dpsub *dpsub)
+{
+	struct drm_device *drm = &dpsub->drm;
+	int ret;
+
+	/* Initialize mode config, vblank and the KMS poll helper. */
+	ret = drmm_mode_config_init(drm);
+	if (ret < 0)
+		return ret;
+
+	drm->mode_config.funcs = &zynqmp_dpsub_mode_config_funcs;
+	drm->mode_config.min_width = 0;
+	drm->mode_config.min_height = 0;
+	drm->mode_config.max_width = ZYNQMP_DISP_MAX_WIDTH;
+	drm->mode_config.max_height = ZYNQMP_DISP_MAX_HEIGHT;
+
+	ret = drm_vblank_init(drm, 1);
+	if (ret)
+		return ret;
+
+	drm_kms_helper_poll_init(drm);
+
+	ret = zynqmp_dpsub_kms_init(dpsub);
+	if (ret < 0)
+		goto err_poll_fini;
+
+	/* Reset all components and register the DRM device. */
+	drm_mode_config_reset(drm);
+
+	ret = drm_dev_register(drm, 0);
+	if (ret < 0)
+		goto err_poll_fini;
+
+	/* Initialize fbdev generic emulation. */
+	drm_fbdev_generic_setup(drm, 24);
+
+	return 0;
+
+err_poll_fini:
+	drm_kms_helper_poll_fini(drm);
+	return ret;
+}
+
+void zynqmp_dpsub_drm_cleanup(struct zynqmp_dpsub *dpsub)
+{
+	struct drm_device *drm = &dpsub->drm;
+
+	if (dpsub->connector)
+		drm_bridge_connector_disable_hpd(dpsub->connector);
+
+	drm_dev_unregister(drm);
+	drm_atomic_helper_shutdown(drm);
+	drm_kms_helper_poll_fini(drm);
+}
diff --git a/drivers/gpu/drm/xlnx/zynqmp_kms.h b/drivers/gpu/drm/xlnx/zynqmp_kms.h
index a8934b1abb05..8074148fd429 100644
--- a/drivers/gpu/drm/xlnx/zynqmp_kms.h
+++ b/drivers/gpu/drm/xlnx/zynqmp_kms.h
@@ -14,8 +14,11 @@
 
 struct zynqmp_dpsub;
 
+extern const struct drm_driver zynqmp_dpsub_drm_driver;
+
 void zynqmp_dpsub_handle_vblank(struct zynqmp_dpsub *dpsub);
 
-int zynqmp_dpsub_kms_init(struct zynqmp_dpsub *dpsub);
+int zynqmp_dpsub_drm_init(struct zynqmp_dpsub *dpsub);
+void zynqmp_dpsub_drm_cleanup(struct zynqmp_dpsub *dpsub);
 
 #endif /* _ZYNQMP_KMS_H_ */
-- 
Regards,

Laurent Pinchart


^ permalink raw reply related	[flat|nested] 38+ messages in thread

* [PATCH 29/36] drm: xlnx: zynqmp_dpsub: Decouple DRM device from zynqmp_dpsub
  2021-08-09  1:34 [PATCH 00/36] drm: xlnx: zynqmp_dpsub: Initial live video input support Laurent Pinchart
                   ` (27 preceding siblings ...)
  2021-08-09  1:34 ` [PATCH 28/36] drm: xlnx: zynqmp_dpsub: Move all DRM init and cleanup to zynqmp_kms.c Laurent Pinchart
@ 2021-08-09  1:34 ` Laurent Pinchart
  2021-08-09  1:34 ` [PATCH 30/36] drm: xlnx: zynqmp_dpsub: Rename zynqmp_dpsub_handle_vblank with DRM prefix Laurent Pinchart
                   ` (6 subsequent siblings)
  35 siblings, 0 replies; 38+ messages in thread
From: Laurent Pinchart @ 2021-08-09  1:34 UTC (permalink / raw)
  To: dri-devel; +Cc: Michal Simek, Jianqiang Chen

To complete the decoupling of the DRM device from the zynqmp_dpsub,
group all DRM-related structures in a zynqmp_dpsub_drm structure and
allocate it separately from the zynqmp_dpsub. The DRM managed allocation
of the drm_device now doesn't cover the zynqmp_dpsub anymore, so we need
to register a cleanup action to release the zynqmp_dpsub when the
drm_device is released.

The will allow usage of the DisplayPort encoder as a standalone bridge,
without registering a DRM device in this driver.

Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
---
 drivers/gpu/drm/xlnx/zynqmp_dpsub.c | 41 +++++++++-------
 drivers/gpu/drm/xlnx/zynqmp_dpsub.h | 20 ++------
 drivers/gpu/drm/xlnx/zynqmp_kms.c   | 72 ++++++++++++++++++++---------
 drivers/gpu/drm/xlnx/zynqmp_kms.h   | 26 ++++++++++-
 4 files changed, 104 insertions(+), 55 deletions(-)

diff --git a/drivers/gpu/drm/xlnx/zynqmp_dpsub.c b/drivers/gpu/drm/xlnx/zynqmp_dpsub.c
index 75209272ccb2..e6532a13fb78 100644
--- a/drivers/gpu/drm/xlnx/zynqmp_dpsub.c
+++ b/drivers/gpu/drm/xlnx/zynqmp_dpsub.c
@@ -18,8 +18,6 @@
 #include <linux/slab.h>
 
 #include <drm/drm_atomic_helper.h>
-#include <drm/drm_drv.h>
-#include <drm/drm_managed.h>
 #include <drm/drm_modeset_helper.h>
 
 #include "zynqmp_disp.h"
@@ -35,14 +33,20 @@ static int __maybe_unused zynqmp_dpsub_suspend(struct device *dev)
 {
 	struct zynqmp_dpsub *dpsub = dev_get_drvdata(dev);
 
-	return drm_mode_config_helper_suspend(&dpsub->drm);
+	if (!dpsub->drm)
+		return 0;
+
+	return drm_mode_config_helper_suspend(&dpsub->drm->dev);
 }
 
 static int __maybe_unused zynqmp_dpsub_resume(struct device *dev)
 {
 	struct zynqmp_dpsub *dpsub = dev_get_drvdata(dev);
 
-	return drm_mode_config_helper_resume(&dpsub->drm);
+	if (!dpsub->drm)
+		return 0;
+
+	return drm_mode_config_helper_resume(&dpsub->drm->dev);
 }
 
 static const struct dev_pm_ops zynqmp_dpsub_pm_ops = {
@@ -137,12 +141,11 @@ static int zynqmp_dpsub_init_clocks(struct zynqmp_dpsub *dpsub)
 	return 0;
 }
 
-static void zynqmp_dpsub_release(struct drm_device *drm, void *res)
+void zynqmp_dpsub_release(struct zynqmp_dpsub *dpsub)
 {
-	struct zynqmp_dpsub *dpsub = res;
-
 	kfree(dpsub->disp);
 	kfree(dpsub->dp);
+	kfree(dpsub);
 }
 
 static int zynqmp_dpsub_probe(struct platform_device *pdev)
@@ -151,14 +154,9 @@ static int zynqmp_dpsub_probe(struct platform_device *pdev)
 	int ret;
 
 	/* Allocate private data. */
-	dpsub = devm_drm_dev_alloc(&pdev->dev, &zynqmp_dpsub_drm_driver,
-				   struct zynqmp_dpsub, drm);
-	if (IS_ERR(dpsub))
-		return PTR_ERR(dpsub);
-
-	ret = drmm_add_action(&dpsub->drm, zynqmp_dpsub_release, dpsub);
-	if (ret < 0)
-		return ret;
+	dpsub = kzalloc(sizeof(*dpsub), GFP_KERNEL);
+	if (!dpsub)
+		return -ENOMEM;
 
 	dpsub->dev = &pdev->dev;
 	platform_set_drvdata(pdev, dpsub);
@@ -203,6 +201,8 @@ static int zynqmp_dpsub_probe(struct platform_device *pdev)
 	clk_disable_unprepare(dpsub->apb_clk);
 err_mem:
 	of_reserved_mem_device_release(&pdev->dev);
+	if (!dpsub->drm)
+		zynqmp_dpsub_release(dpsub);
 	return ret;
 }
 
@@ -210,7 +210,8 @@ static int zynqmp_dpsub_remove(struct platform_device *pdev)
 {
 	struct zynqmp_dpsub *dpsub = platform_get_drvdata(pdev);
 
-	zynqmp_dpsub_drm_cleanup(dpsub);
+	if (dpsub->drm)
+		zynqmp_dpsub_drm_cleanup(dpsub);
 
 	zynqmp_disp_remove(dpsub);
 	zynqmp_dp_remove(dpsub);
@@ -219,6 +220,9 @@ static int zynqmp_dpsub_remove(struct platform_device *pdev)
 	clk_disable_unprepare(dpsub->apb_clk);
 	of_reserved_mem_device_release(&pdev->dev);
 
+	if (!dpsub->drm)
+		zynqmp_dpsub_release(dpsub);
+
 	return 0;
 }
 
@@ -226,7 +230,10 @@ static void zynqmp_dpsub_shutdown(struct platform_device *pdev)
 {
 	struct zynqmp_dpsub *dpsub = platform_get_drvdata(pdev);
 
-	drm_atomic_helper_shutdown(&dpsub->drm);
+	if (!dpsub->drm)
+		return;
+
+	drm_atomic_helper_shutdown(&dpsub->drm->dev);
 }
 
 static const struct of_device_id zynqmp_dpsub_of_match[] = {
diff --git a/drivers/gpu/drm/xlnx/zynqmp_dpsub.h b/drivers/gpu/drm/xlnx/zynqmp_dpsub.h
index 1778092e0829..6c6029ad9bc5 100644
--- a/drivers/gpu/drm/xlnx/zynqmp_dpsub.h
+++ b/drivers/gpu/drm/xlnx/zynqmp_dpsub.h
@@ -12,17 +12,13 @@
 #ifndef _ZYNQMP_DPSUB_H_
 #define _ZYNQMP_DPSUB_H_
 
-#include <drm/drm_crtc.h>
-#include <drm/drm_encoder.h>
-#include <drm/drm_plane.h>
-
 struct clk;
 struct device;
 struct drm_bridge;
-struct drm_device;
 struct zynqmp_disp;
 struct zynqmp_disp_layer;
 struct zynqmp_dp;
+struct zynqmp_dpsub_drm;
 
 #define ZYNQMP_DPSUB_NUM_LAYERS				2
 
@@ -35,24 +31,19 @@ enum zynqmp_dpsub_format {
 
 /**
  * struct zynqmp_dpsub - ZynqMP DisplayPort Subsystem
- * @drm: The DRM/KMS device
  * @dev: The physical device
  * @apb_clk: The APB clock
  * @vid_clk: Video clock
  * @vid_clk_from_ps: True of the video clock comes from PS, false from PL
  * @aud_clk: Audio clock
  * @aud_clk_from_ps: True of the audio clock comes from PS, false from PL
- * @planes: The DRM planes
- * @crtc: The DRM CRTC
- * @encoder: The dummy DRM encoder
- * @connector: The DP connector
+ * @drm: The DRM/KMS device data
  * @bridge: The DP encoder bridge
  * @disp: The display controller
  * @dp: The DisplayPort controller
  * @dma_align: DMA alignment constraint (must be a power of 2)
  */
 struct zynqmp_dpsub {
-	struct drm_device drm;
 	struct device *dev;
 
 	struct clk *apb_clk;
@@ -61,10 +52,7 @@ struct zynqmp_dpsub {
 	struct clk *aud_clk;
 	bool aud_clk_from_ps;
 
-	struct drm_plane planes[ZYNQMP_DPSUB_NUM_LAYERS];
-	struct drm_crtc crtc;
-	struct drm_encoder encoder;
-	struct drm_connector *connector;
+	struct zynqmp_dpsub_drm *drm;
 	struct drm_bridge *bridge;
 
 	struct zynqmp_disp *disp;
@@ -77,4 +65,6 @@ struct zynqmp_dpsub {
 bool zynqmp_dpsub_audio_enabled(struct zynqmp_dpsub *dpsub);
 unsigned int zynqmp_dpsub_get_audio_clk_rate(struct zynqmp_dpsub *dpsub);
 
+void zynqmp_dpsub_release(struct zynqmp_dpsub *dpsub);
+
 #endif /* _ZYNQMP_DPSUB_H_ */
diff --git a/drivers/gpu/drm/xlnx/zynqmp_kms.c b/drivers/gpu/drm/xlnx/zynqmp_kms.c
index 7b6af07baad4..35093f41c532 100644
--- a/drivers/gpu/drm/xlnx/zynqmp_kms.c
+++ b/drivers/gpu/drm/xlnx/zynqmp_kms.c
@@ -43,7 +43,7 @@
 
 static inline struct zynqmp_dpsub *to_zynqmp_dpsub(struct drm_device *drm)
 {
-	return container_of(drm, struct zynqmp_dpsub, drm);
+	return container_of(drm, struct zynqmp_dpsub_drm, dev)->dpsub;
 }
 
 /* -----------------------------------------------------------------------------
@@ -145,9 +145,9 @@ static int zynqmp_dpsub_create_planes(struct zynqmp_dpsub *dpsub)
 	unsigned int i;
 	int ret;
 
-	for (i = 0; i < ARRAY_SIZE(dpsub->planes); i++) {
+	for (i = 0; i < ARRAY_SIZE(dpsub->drm->planes); i++) {
 		struct zynqmp_disp_layer *layer = dpsub->layers[i];
-		struct drm_plane *plane = &dpsub->planes[i];
+		struct drm_plane *plane = &dpsub->drm->planes[i];
 		enum drm_plane_type type;
 		unsigned int num_formats;
 		u32 *formats;
@@ -159,7 +159,7 @@ static int zynqmp_dpsub_create_planes(struct zynqmp_dpsub *dpsub)
 		/* Graphics layer is primary, and video layer is overlay. */
 		type = i == ZYNQMP_DPSUB_LAYER_VID
 		     ? DRM_PLANE_TYPE_OVERLAY : DRM_PLANE_TYPE_PRIMARY;
-		ret = drm_universal_plane_init(&dpsub->drm, plane, 0,
+		ret = drm_universal_plane_init(&dpsub->drm->dev, plane, 0,
 					       &zynqmp_dpsub_plane_funcs,
 					       formats, num_formats,
 					       NULL, type, NULL);
@@ -183,7 +183,7 @@ static int zynqmp_dpsub_create_planes(struct zynqmp_dpsub *dpsub)
 
 static inline struct zynqmp_dpsub *crtc_to_dpsub(struct drm_crtc *crtc)
 {
-	return container_of(crtc, struct zynqmp_dpsub, crtc);
+	return container_of(crtc, struct zynqmp_dpsub_drm, crtc)->dpsub;
 }
 
 static void zynqmp_dpsub_crtc_atomic_enable(struct drm_crtc *crtc,
@@ -311,11 +311,11 @@ static const struct drm_crtc_funcs zynqmp_dpsub_crtc_funcs = {
 
 static int zynqmp_dpsub_create_crtc(struct zynqmp_dpsub *dpsub)
 {
-	struct drm_plane *plane = &dpsub->planes[ZYNQMP_DPSUB_LAYER_GFX];
-	struct drm_crtc *crtc = &dpsub->crtc;
+	struct drm_plane *plane = &dpsub->drm->planes[ZYNQMP_DPSUB_LAYER_GFX];
+	struct drm_crtc *crtc = &dpsub->drm->crtc;
 	int ret;
 
-	ret = drm_crtc_init_with_planes(&dpsub->drm, crtc, plane,
+	ret = drm_crtc_init_with_planes(&dpsub->drm->dev, crtc, plane,
 					NULL, &zynqmp_dpsub_crtc_funcs, NULL);
 	if (ret < 0)
 		return ret;
@@ -330,11 +330,11 @@ static int zynqmp_dpsub_create_crtc(struct zynqmp_dpsub *dpsub)
 
 static void zynqmp_dpsub_map_crtc_to_plane(struct zynqmp_dpsub *dpsub)
 {
-	u32 possible_crtcs = drm_crtc_mask(&dpsub->crtc);
+	u32 possible_crtcs = drm_crtc_mask(&dpsub->drm->crtc);
 	unsigned int i;
 
-	for (i = 0; i < ARRAY_SIZE(dpsub->planes); i++)
-		dpsub->planes[i].possible_crtcs = possible_crtcs;
+	for (i = 0; i < ARRAY_SIZE(dpsub->drm->planes); i++)
+		dpsub->drm->planes[i].possible_crtcs = possible_crtcs;
 }
 
 /**
@@ -346,7 +346,7 @@ static void zynqmp_dpsub_map_crtc_to_plane(struct zynqmp_dpsub *dpsub)
  */
 void zynqmp_dpsub_handle_vblank(struct zynqmp_dpsub *dpsub)
 {
-	drm_crtc_handle_vblank(&dpsub->crtc);
+	drm_crtc_handle_vblank(&dpsub->drm->crtc);
 }
 
 /* -----------------------------------------------------------------------------
@@ -393,7 +393,7 @@ static const struct drm_mode_config_funcs zynqmp_dpsub_mode_config_funcs = {
 
 DEFINE_DRM_GEM_CMA_FOPS(zynqmp_dpsub_drm_fops);
 
-const struct drm_driver zynqmp_dpsub_drm_driver = {
+static const struct drm_driver zynqmp_dpsub_drm_driver = {
 	.driver_features		= DRIVER_MODESET | DRIVER_GEM |
 					  DRIVER_ATOMIC,
 
@@ -410,7 +410,7 @@ const struct drm_driver zynqmp_dpsub_drm_driver = {
 
 static int zynqmp_dpsub_kms_init(struct zynqmp_dpsub *dpsub)
 {
-	struct drm_encoder *encoder = &dpsub->encoder;
+	struct drm_encoder *encoder = &dpsub->drm->encoder;
 	struct drm_connector *connector;
 	int ret;
 
@@ -426,8 +426,8 @@ static int zynqmp_dpsub_kms_init(struct zynqmp_dpsub *dpsub)
 	zynqmp_dpsub_map_crtc_to_plane(dpsub);
 
 	/* Create the encoder and attach the bridge. */
-	encoder->possible_crtcs |= drm_crtc_mask(&dpsub->crtc);
-	drm_simple_encoder_init(&dpsub->drm, encoder, DRM_MODE_ENCODER_NONE);
+	encoder->possible_crtcs |= drm_crtc_mask(&dpsub->drm->crtc);
+	drm_simple_encoder_init(&dpsub->drm->dev, encoder, DRM_MODE_ENCODER_NONE);
 
 	ret = drm_bridge_attach(encoder, dpsub->bridge, NULL,
 				DRM_BRIDGE_ATTACH_NO_CONNECTOR);
@@ -437,7 +437,7 @@ static int zynqmp_dpsub_kms_init(struct zynqmp_dpsub *dpsub)
 	}
 
 	/* Create the connector for the chain of bridges. */
-	connector = drm_bridge_connector_init(&dpsub->drm, encoder);
+	connector = drm_bridge_connector_init(&dpsub->drm->dev, encoder);
 	if (IS_ERR(connector)) {
 		dev_err(dpsub->dev, "failed to created connector\n");
 		return PTR_ERR(connector);
@@ -450,16 +450,44 @@ static int zynqmp_dpsub_kms_init(struct zynqmp_dpsub *dpsub)
 	}
 
 	drm_bridge_connector_enable_hpd(connector);
-	dpsub->connector = connector;
+	dpsub->drm->connector = connector;
 
 	return 0;
 }
 
+static void zynqmp_dpsub_drm_release(struct drm_device *drm, void *res)
+{
+	struct zynqmp_dpsub_drm *dpdrm = res;
+
+	zynqmp_dpsub_release(dpdrm->dpsub);
+}
+
 int zynqmp_dpsub_drm_init(struct zynqmp_dpsub *dpsub)
 {
-	struct drm_device *drm = &dpsub->drm;
+	struct zynqmp_dpsub_drm *dpdrm;
+	struct drm_device *drm;
 	int ret;
 
+	/*
+	 * Allocate the drm_device and immediately add a cleanup action to
+	 * release the zynqmp_dpsub instance. If any of those operations fail,
+	 * dpsub->drm will remain NULL, which tells the caller that it must
+	 * cleanup manually.
+	 */
+	dpdrm = devm_drm_dev_alloc(dpsub->dev, &zynqmp_dpsub_drm_driver,
+				   struct zynqmp_dpsub_drm, dev);
+	if (IS_ERR(dpdrm))
+		return PTR_ERR(dpdrm);
+
+	dpdrm->dpsub = dpsub;
+	drm = &dpdrm->dev;
+
+	ret = drmm_add_action(drm, zynqmp_dpsub_drm_release, dpdrm);
+	if (ret < 0)
+		return ret;
+
+	dpsub->drm = dpdrm;
+
 	/* Initialize mode config, vblank and the KMS poll helper. */
 	ret = drmm_mode_config_init(drm);
 	if (ret < 0)
@@ -500,10 +528,10 @@ int zynqmp_dpsub_drm_init(struct zynqmp_dpsub *dpsub)
 
 void zynqmp_dpsub_drm_cleanup(struct zynqmp_dpsub *dpsub)
 {
-	struct drm_device *drm = &dpsub->drm;
+	struct drm_device *drm = &dpsub->drm->dev;
 
-	if (dpsub->connector)
-		drm_bridge_connector_disable_hpd(dpsub->connector);
+	if (dpsub->drm->connector)
+		drm_bridge_connector_disable_hpd(dpsub->drm->connector);
 
 	drm_dev_unregister(drm);
 	drm_atomic_helper_shutdown(drm);
diff --git a/drivers/gpu/drm/xlnx/zynqmp_kms.h b/drivers/gpu/drm/xlnx/zynqmp_kms.h
index 8074148fd429..9674ce2e544d 100644
--- a/drivers/gpu/drm/xlnx/zynqmp_kms.h
+++ b/drivers/gpu/drm/xlnx/zynqmp_kms.h
@@ -12,9 +12,33 @@
 #ifndef _ZYNQMP_KMS_H_
 #define _ZYNQMP_KMS_H_
 
+#include <drm/drm_crtc.h>
+#include <drm/drm_device.h>
+#include <drm/drm_encoder.h>
+#include <drm/drm_plane.h>
+
+#include "zynqmp_dpsub.h"
+
 struct zynqmp_dpsub;
 
-extern const struct drm_driver zynqmp_dpsub_drm_driver;
+/**
+ * struct zynqmp_dpsub - ZynqMP DisplayPort Subsystem DRM/KMS data
+ * @dpsub: Backpointer to the DisplayPort subsystem
+ * @drm: The DRM/KMS device
+ * @planes: The DRM planes
+ * @crtc: The DRM CRTC
+ * @encoder: The dummy DRM encoder
+ * @connector: The DP connector
+ */
+struct zynqmp_dpsub_drm {
+	struct zynqmp_dpsub *dpsub;
+
+	struct drm_device dev;
+	struct drm_plane planes[ZYNQMP_DPSUB_NUM_LAYERS];
+	struct drm_crtc crtc;
+	struct drm_encoder encoder;
+	struct drm_connector *connector;
+};
 
 void zynqmp_dpsub_handle_vblank(struct zynqmp_dpsub *dpsub);
 
-- 
Regards,

Laurent Pinchart


^ permalink raw reply related	[flat|nested] 38+ messages in thread

* [PATCH 30/36] drm: xlnx: zynqmp_dpsub: Rename zynqmp_dpsub_handle_vblank with DRM prefix
  2021-08-09  1:34 [PATCH 00/36] drm: xlnx: zynqmp_dpsub: Initial live video input support Laurent Pinchart
                   ` (28 preceding siblings ...)
  2021-08-09  1:34 ` [PATCH 29/36] drm: xlnx: zynqmp_dpsub: Decouple DRM device from zynqmp_dpsub Laurent Pinchart
@ 2021-08-09  1:34 ` Laurent Pinchart
  2021-08-09  1:34 ` [PATCH 31/36] drm: xlnx: zynqmp_dpsub: Parse DT to find connected ports Laurent Pinchart
                   ` (5 subsequent siblings)
  35 siblings, 0 replies; 38+ messages in thread
From: Laurent Pinchart @ 2021-08-09  1:34 UTC (permalink / raw)
  To: dri-devel; +Cc: Michal Simek, Jianqiang Chen

The better convey its purpose, rename the zynqmp_dpsub_handle_vblank()
function that belongs to the DRM layer to
zynqmp_dpsub_drm_handle_vblank().

Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
---
 drivers/gpu/drm/xlnx/zynqmp_dp.c  | 2 +-
 drivers/gpu/drm/xlnx/zynqmp_kms.c | 4 ++--
 drivers/gpu/drm/xlnx/zynqmp_kms.h | 2 +-
 3 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/drivers/gpu/drm/xlnx/zynqmp_dp.c b/drivers/gpu/drm/xlnx/zynqmp_dp.c
index 25cde59b1e05..09254e9c75cd 100644
--- a/drivers/gpu/drm/xlnx/zynqmp_dp.c
+++ b/drivers/gpu/drm/xlnx/zynqmp_dp.c
@@ -1580,7 +1580,7 @@ static irqreturn_t zynqmp_dp_irq_handler(int irq, void *data)
 	zynqmp_dp_write(dp, ZYNQMP_DP_INT_STATUS, status);
 
 	if (status & ZYNQMP_DP_INT_VBLANK_START)
-		zynqmp_dpsub_handle_vblank(dp->dpsub);
+		zynqmp_dpsub_drm_handle_vblank(dp->dpsub);
 
 	if (status & ZYNQMP_DP_INT_HPD_EVENT)
 		schedule_delayed_work(&dp->hpd_work, 0);
diff --git a/drivers/gpu/drm/xlnx/zynqmp_kms.c b/drivers/gpu/drm/xlnx/zynqmp_kms.c
index 35093f41c532..922d6c80c16b 100644
--- a/drivers/gpu/drm/xlnx/zynqmp_kms.c
+++ b/drivers/gpu/drm/xlnx/zynqmp_kms.c
@@ -338,13 +338,13 @@ static void zynqmp_dpsub_map_crtc_to_plane(struct zynqmp_dpsub *dpsub)
 }
 
 /**
- * zynqmp_dpsub_handle_vblank - Handle the vblank event
+ * zynqmp_dpsub_drm_handle_vblank - Handle the vblank event
  * @dpsub: DisplayPort subsystem
  *
  * This function handles the vblank interrupt, and sends an event to
  * CRTC object. This will be called by the DP vblank interrupt handler.
  */
-void zynqmp_dpsub_handle_vblank(struct zynqmp_dpsub *dpsub)
+void zynqmp_dpsub_drm_handle_vblank(struct zynqmp_dpsub *dpsub)
 {
 	drm_crtc_handle_vblank(&dpsub->drm->crtc);
 }
diff --git a/drivers/gpu/drm/xlnx/zynqmp_kms.h b/drivers/gpu/drm/xlnx/zynqmp_kms.h
index 9674ce2e544d..1e01dbcb067c 100644
--- a/drivers/gpu/drm/xlnx/zynqmp_kms.h
+++ b/drivers/gpu/drm/xlnx/zynqmp_kms.h
@@ -40,7 +40,7 @@ struct zynqmp_dpsub_drm {
 	struct drm_connector *connector;
 };
 
-void zynqmp_dpsub_handle_vblank(struct zynqmp_dpsub *dpsub);
+void zynqmp_dpsub_drm_handle_vblank(struct zynqmp_dpsub *dpsub);
 
 int zynqmp_dpsub_drm_init(struct zynqmp_dpsub *dpsub);
 void zynqmp_dpsub_drm_cleanup(struct zynqmp_dpsub *dpsub);
-- 
Regards,

Laurent Pinchart


^ permalink raw reply related	[flat|nested] 38+ messages in thread

* [PATCH 31/36] drm: xlnx: zynqmp_dpsub: Parse DT to find connected ports
  2021-08-09  1:34 [PATCH 00/36] drm: xlnx: zynqmp_dpsub: Initial live video input support Laurent Pinchart
                   ` (29 preceding siblings ...)
  2021-08-09  1:34 ` [PATCH 30/36] drm: xlnx: zynqmp_dpsub: Rename zynqmp_dpsub_handle_vblank with DRM prefix Laurent Pinchart
@ 2021-08-09  1:34 ` Laurent Pinchart
  2021-08-09  1:34 ` [PATCH 32/36] drm: xlnx: zynqmp_dpsub: Allow configuration of layer mode Laurent Pinchart
                   ` (4 subsequent siblings)
  35 siblings, 0 replies; 38+ messages in thread
From: Laurent Pinchart @ 2021-08-09  1:34 UTC (permalink / raw)
  To: dri-devel; +Cc: Michal Simek, Jianqiang Chen

To prepare for live video input support, parse the device tree to find
the connected ports. Warn about unsupported configurations, and error
out when invalid.

Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
---
 drivers/gpu/drm/xlnx/zynqmp_dpsub.c | 54 +++++++++++++++++++++++++++++
 drivers/gpu/drm/xlnx/zynqmp_dpsub.h | 13 +++++++
 2 files changed, 67 insertions(+)

diff --git a/drivers/gpu/drm/xlnx/zynqmp_dpsub.c b/drivers/gpu/drm/xlnx/zynqmp_dpsub.c
index e6532a13fb78..bc2b3ab3001d 100644
--- a/drivers/gpu/drm/xlnx/zynqmp_dpsub.c
+++ b/drivers/gpu/drm/xlnx/zynqmp_dpsub.c
@@ -12,6 +12,7 @@
 #include <linux/clk.h>
 #include <linux/dma-mapping.h>
 #include <linux/module.h>
+#include <linux/of_graph.h>
 #include <linux/of_reserved_mem.h>
 #include <linux/platform_device.h>
 #include <linux/pm_runtime.h>
@@ -141,6 +142,55 @@ static int zynqmp_dpsub_init_clocks(struct zynqmp_dpsub *dpsub)
 	return 0;
 }
 
+static int zynqmp_dpsub_parse_dt(struct zynqmp_dpsub *dpsub)
+{
+	struct device_node *np;
+	unsigned int i;
+
+	/*
+	 * For backward compatibility with old device trees that don't contain
+	 * ports, consider that only the DP output port is connected if no
+	 * ports child no exists.
+	 */
+	np = of_get_child_by_name(dpsub->dev->of_node, "ports");
+	of_node_put(np);
+	if (!np) {
+		dev_warn(dpsub->dev, "missing ports, update DT bindings\n");
+		dpsub->connected_ports = BIT(ZYNQMP_DPSUB_PORT_OUT_DP);
+		return 0;
+	}
+
+	/* Check which ports are connected. */
+	for (i = 0; i < ZYNQMP_DPSUB_NUM_PORTS; ++i) {
+		struct device_node *np;
+
+		np = of_graph_get_remote_node(dpsub->dev->of_node, i, -1);
+		if (np) {
+			dpsub->connected_ports |= BIT(i);
+			of_node_put(np);
+		}
+	}
+
+	/* Sanity checks. */
+	if ((dpsub->connected_ports & BIT(ZYNQMP_DPSUB_PORT_LIVE_VIDEO)) ||
+	    (dpsub->connected_ports & BIT(ZYNQMP_DPSUB_PORT_LIVE_GFX)))
+		dev_warn(dpsub->dev, "live video unsupported, ignoring\n");
+
+	if (dpsub->connected_ports & BIT(ZYNQMP_DPSUB_PORT_LIVE_AUDIO))
+		dev_warn(dpsub->dev, "live audio unsupported, ignoring\n");
+
+	if ((dpsub->connected_ports & BIT(ZYNQMP_DPSUB_PORT_OUT_VIDEO)) ||
+	    (dpsub->connected_ports & BIT(ZYNQMP_DPSUB_PORT_OUT_AUDIO)))
+		dev_warn(dpsub->dev, "output to PL unsupported, ignoring\n");
+
+	if (!(dpsub->connected_ports & BIT(ZYNQMP_DPSUB_PORT_OUT_DP))) {
+		dev_err(dpsub->dev, "DP output port not connected\n");
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
 void zynqmp_dpsub_release(struct zynqmp_dpsub *dpsub)
 {
 	kfree(dpsub->disp);
@@ -170,6 +220,10 @@ static int zynqmp_dpsub_probe(struct platform_device *pdev)
 	if (ret < 0)
 		goto err_mem;
 
+	ret = zynqmp_dpsub_parse_dt(dpsub);
+	if (ret < 0)
+		goto err_mem;
+
 	pm_runtime_enable(&pdev->dev);
 
 	/*
diff --git a/drivers/gpu/drm/xlnx/zynqmp_dpsub.h b/drivers/gpu/drm/xlnx/zynqmp_dpsub.h
index 6c6029ad9bc5..6ded6e45ac0a 100644
--- a/drivers/gpu/drm/xlnx/zynqmp_dpsub.h
+++ b/drivers/gpu/drm/xlnx/zynqmp_dpsub.h
@@ -22,6 +22,16 @@ struct zynqmp_dpsub_drm;
 
 #define ZYNQMP_DPSUB_NUM_LAYERS				2
 
+enum zynqmp_dpsub_port {
+	ZYNQMP_DPSUB_PORT_LIVE_VIDEO,
+	ZYNQMP_DPSUB_PORT_LIVE_GFX,
+	ZYNQMP_DPSUB_PORT_LIVE_AUDIO,
+	ZYNQMP_DPSUB_PORT_OUT_VIDEO,
+	ZYNQMP_DPSUB_PORT_OUT_AUDIO,
+	ZYNQMP_DPSUB_PORT_OUT_DP,
+	ZYNQMP_DPSUB_NUM_PORTS,
+};
+
 enum zynqmp_dpsub_format {
 	ZYNQMP_DPSUB_FORMAT_RGB,
 	ZYNQMP_DPSUB_FORMAT_YCRCB444,
@@ -37,6 +47,7 @@ enum zynqmp_dpsub_format {
  * @vid_clk_from_ps: True of the video clock comes from PS, false from PL
  * @aud_clk: Audio clock
  * @aud_clk_from_ps: True of the audio clock comes from PS, false from PL
+ * @connected_ports: Bitmask of connected ports in the device tree
  * @drm: The DRM/KMS device data
  * @bridge: The DP encoder bridge
  * @disp: The display controller
@@ -52,6 +63,8 @@ struct zynqmp_dpsub {
 	struct clk *aud_clk;
 	bool aud_clk_from_ps;
 
+	unsigned int connected_ports;
+
 	struct zynqmp_dpsub_drm *drm;
 	struct drm_bridge *bridge;
 
-- 
Regards,

Laurent Pinchart


^ permalink raw reply related	[flat|nested] 38+ messages in thread

* [PATCH 32/36] drm: xlnx: zynqmp_dpsub: Allow configuration of layer mode
  2021-08-09  1:34 [PATCH 00/36] drm: xlnx: zynqmp_dpsub: Initial live video input support Laurent Pinchart
                   ` (30 preceding siblings ...)
  2021-08-09  1:34 ` [PATCH 31/36] drm: xlnx: zynqmp_dpsub: Parse DT to find connected ports Laurent Pinchart
@ 2021-08-09  1:34 ` Laurent Pinchart
  2021-08-09  1:34 ` [PATCH 33/36] drm: xlnx: zynqmp_dpsub: Support operation without DMA engine Laurent Pinchart
                   ` (3 subsequent siblings)
  35 siblings, 0 replies; 38+ messages in thread
From: Laurent Pinchart @ 2021-08-09  1:34 UTC (permalink / raw)
  To: dri-devel; +Cc: Michal Simek, Jianqiang Chen

Add a mode parameter to the zynqmp_disp_layer_enable() to set the layer
mode, to prepare for live mode support.

Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
---
 drivers/gpu/drm/xlnx/zynqmp_disp.c | 30 +++++++++---------------------
 drivers/gpu/drm/xlnx/zynqmp_disp.h | 13 ++++++++++++-
 drivers/gpu/drm/xlnx/zynqmp_kms.c  |  2 +-
 3 files changed, 22 insertions(+), 23 deletions(-)

diff --git a/drivers/gpu/drm/xlnx/zynqmp_disp.c b/drivers/gpu/drm/xlnx/zynqmp_disp.c
index 5c39df0fbe59..ebb55e5ab824 100644
--- a/drivers/gpu/drm/xlnx/zynqmp_disp.c
+++ b/drivers/gpu/drm/xlnx/zynqmp_disp.c
@@ -77,16 +77,6 @@ struct zynqmp_disp_format {
 	const u32 *sf;
 };
 
-/**
- * enum zynqmp_disp_layer_mode - Layer mode
- * @ZYNQMP_DISP_LAYER_NONLIVE: non-live (memory) mode
- * @ZYNQMP_DISP_LAYER_LIVE: live (stream) mode
- */
-enum zynqmp_disp_layer_mode {
-	ZYNQMP_DISP_LAYER_NONLIVE,
-	ZYNQMP_DISP_LAYER_LIVE
-};
-
 /**
  * struct zynqmp_disp_layer_dma - DMA channel for one data plane of a layer
  * @chan: DMA channel
@@ -130,7 +120,7 @@ struct zynqmp_disp_layer {
 
 	const struct zynqmp_disp_format *disp_fmt;
 	const struct drm_format_info *drm_fmt;
-	enum zynqmp_disp_layer_mode mode;
+	enum zynqmp_dpsub_layer_mode mode;
 };
 
 /**
@@ -518,27 +508,25 @@ static void zynqmp_disp_avbuf_disable_audio(struct zynqmp_disp *disp)
  * zynqmp_disp_avbuf_enable_video - Enable a video layer
  * @disp: Display controller
  * @layer: The layer
- * @mode: Operating mode of layer
  *
  * Enable the video/graphics buffer for @layer.
  */
 static void zynqmp_disp_avbuf_enable_video(struct zynqmp_disp *disp,
-					   struct zynqmp_disp_layer *layer,
-					   enum zynqmp_disp_layer_mode mode)
+					   struct zynqmp_disp_layer *layer)
 {
 	u32 val;
 
 	val = zynqmp_disp_avbuf_read(disp, ZYNQMP_DISP_AV_BUF_OUTPUT);
 	if (zynqmp_disp_layer_is_video(layer)) {
 		val &= ~ZYNQMP_DISP_AV_BUF_OUTPUT_VID1_MASK;
-		if (mode == ZYNQMP_DISP_LAYER_NONLIVE)
+		if (layer->mode == ZYNQMP_DPSUB_LAYER_NONLIVE)
 			val |= ZYNQMP_DISP_AV_BUF_OUTPUT_VID1_MEM;
 		else
 			val |= ZYNQMP_DISP_AV_BUF_OUTPUT_VID1_LIVE;
 	} else {
 		val &= ~ZYNQMP_DISP_AV_BUF_OUTPUT_VID2_MASK;
 		val |= ZYNQMP_DISP_AV_BUF_OUTPUT_VID2_MEM;
-		if (mode == ZYNQMP_DISP_LAYER_NONLIVE)
+		if (layer->mode == ZYNQMP_DPSUB_LAYER_NONLIVE)
 			val |= ZYNQMP_DISP_AV_BUF_OUTPUT_VID2_MEM;
 		else
 			val |= ZYNQMP_DISP_AV_BUF_OUTPUT_VID2_LIVE;
@@ -913,17 +901,17 @@ u32 *zynqmp_disp_layer_drm_formats(struct zynqmp_disp_layer *layer,
 /**
  * zynqmp_disp_layer_enable - Enable a layer
  * @layer: The layer
+ * @mode: Operating mode of layer
  *
  * Enable the @layer in the audio/video buffer manager and the blender. DMA
  * channels are started separately by zynqmp_disp_layer_update().
  */
-void zynqmp_disp_layer_enable(struct zynqmp_disp_layer *layer)
+void zynqmp_disp_layer_enable(struct zynqmp_disp_layer *layer,
+			      enum zynqmp_dpsub_layer_mode mode)
 {
-	zynqmp_disp_avbuf_enable_video(layer->disp, layer,
-				       ZYNQMP_DISP_LAYER_NONLIVE);
+	layer->mode = mode;
+	zynqmp_disp_avbuf_enable_video(layer->disp, layer);
 	zynqmp_disp_blend_layer_enable(layer->disp, layer);
-
-	layer->mode = ZYNQMP_DISP_LAYER_NONLIVE;
 }
 
 /**
diff --git a/drivers/gpu/drm/xlnx/zynqmp_disp.h b/drivers/gpu/drm/xlnx/zynqmp_disp.h
index 9b8b202224d9..123cffac08be 100644
--- a/drivers/gpu/drm/xlnx/zynqmp_disp.h
+++ b/drivers/gpu/drm/xlnx/zynqmp_disp.h
@@ -42,6 +42,16 @@ enum zynqmp_dpsub_layer_id {
 	ZYNQMP_DPSUB_LAYER_GFX,
 };
 
+/**
+ * enum zynqmp_dpsub_layer_mode - Layer mode
+ * @ZYNQMP_DPSUB_LAYER_NONLIVE: non-live (memory) mode
+ * @ZYNQMP_DPSUB_LAYER_LIVE: live (stream) mode
+ */
+enum zynqmp_dpsub_layer_mode {
+	ZYNQMP_DPSUB_LAYER_NONLIVE,
+	ZYNQMP_DPSUB_LAYER_LIVE,
+};
+
 void zynqmp_disp_enable(struct zynqmp_disp *disp);
 void zynqmp_disp_disable(struct zynqmp_disp *disp);
 int zynqmp_disp_setup_clock(struct zynqmp_disp *disp,
@@ -52,7 +62,8 @@ void zynqmp_disp_blend_set_global_alpha(struct zynqmp_disp *disp,
 
 u32 *zynqmp_disp_layer_drm_formats(struct zynqmp_disp_layer *layer,
 				   unsigned int *num_formats);
-void zynqmp_disp_layer_enable(struct zynqmp_disp_layer *layer);
+void zynqmp_disp_layer_enable(struct zynqmp_disp_layer *layer,
+			      enum zynqmp_dpsub_layer_mode mode);
 void zynqmp_disp_layer_disable(struct zynqmp_disp_layer *layer);
 void zynqmp_disp_layer_set_format(struct zynqmp_disp_layer *layer,
 				  const struct drm_format_info *info);
diff --git a/drivers/gpu/drm/xlnx/zynqmp_kms.c b/drivers/gpu/drm/xlnx/zynqmp_kms.c
index 922d6c80c16b..f8d94e8da19b 100644
--- a/drivers/gpu/drm/xlnx/zynqmp_kms.c
+++ b/drivers/gpu/drm/xlnx/zynqmp_kms.c
@@ -122,7 +122,7 @@ static void zynqmp_dpsub_plane_atomic_update(struct drm_plane *plane,
 
 	/* Enable or re-enable the plane if the format has changed. */
 	if (format_changed)
-		zynqmp_disp_layer_enable(layer);
+		zynqmp_disp_layer_enable(layer, ZYNQMP_DPSUB_LAYER_NONLIVE);
 }
 
 static const struct drm_plane_helper_funcs zynqmp_dpsub_plane_helper_funcs = {
-- 
Regards,

Laurent Pinchart


^ permalink raw reply related	[flat|nested] 38+ messages in thread

* [PATCH 33/36] drm: xlnx: zynqmp_dpsub: Support operation without DMA engine
  2021-08-09  1:34 [PATCH 00/36] drm: xlnx: zynqmp_dpsub: Initial live video input support Laurent Pinchart
                   ` (31 preceding siblings ...)
  2021-08-09  1:34 ` [PATCH 32/36] drm: xlnx: zynqmp_dpsub: Allow configuration of layer mode Laurent Pinchart
@ 2021-08-09  1:34 ` Laurent Pinchart
  2021-08-09  1:34 ` [PATCH 34/36] drm: xlnx: zynqmp_dpsub: Add support for live video input Laurent Pinchart
                   ` (2 subsequent siblings)
  35 siblings, 0 replies; 38+ messages in thread
From: Laurent Pinchart @ 2021-08-09  1:34 UTC (permalink / raw)
  To: dri-devel; +Cc: Michal Simek, Jianqiang Chen

To prepare for usage of the DPSUB as a DisplayPort bridge without
creating a DRM device, make initialization and usage of the DMA engine
optional. The flag that controls this feature is currently hardcoded to
operating with the DMA engine, this will be made dynamic based on the
device tree configuration in a subsequent change.

Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
---
 drivers/gpu/drm/xlnx/zynqmp_disp.c  | 26 ++++++++++++++++++++------
 drivers/gpu/drm/xlnx/zynqmp_dpsub.c |  3 +++
 drivers/gpu/drm/xlnx/zynqmp_dpsub.h |  3 +++
 3 files changed, 26 insertions(+), 6 deletions(-)

diff --git a/drivers/gpu/drm/xlnx/zynqmp_disp.c b/drivers/gpu/drm/xlnx/zynqmp_disp.c
index ebb55e5ab824..0333cfc993aa 100644
--- a/drivers/gpu/drm/xlnx/zynqmp_disp.c
+++ b/drivers/gpu/drm/xlnx/zynqmp_disp.c
@@ -925,8 +925,10 @@ void zynqmp_disp_layer_disable(struct zynqmp_disp_layer *layer)
 {
 	unsigned int i;
 
-	for (i = 0; i < layer->drm_fmt->num_planes; i++)
-		dmaengine_terminate_sync(layer->dmas[i].chan);
+	if (layer->disp->dpsub->dma_enabled) {
+		for (i = 0; i < layer->drm_fmt->num_planes; i++)
+			dmaengine_terminate_sync(layer->dmas[i].chan);
+	}
 
 	zynqmp_disp_avbuf_disable_video(layer->disp, layer);
 	zynqmp_disp_blend_layer_disable(layer->disp, layer);
@@ -949,6 +951,9 @@ void zynqmp_disp_layer_set_format(struct zynqmp_disp_layer *layer,
 
 	zynqmp_disp_avbuf_set_format(layer->disp, layer, layer->disp_fmt);
 
+	if (!layer->disp->dpsub->dma_enabled)
+		return;
+
 	/*
 	 * Set slave_id for each DMA channel to indicate they're part of a
 	 * video group.
@@ -980,6 +985,9 @@ int zynqmp_disp_layer_update(struct zynqmp_disp_layer *layer,
 	const struct drm_format_info *info = layer->drm_fmt;
 	unsigned int i;
 
+	if (!layer->disp->dpsub->dma_enabled)
+		return 0;
+
 	for (i = 0; i < info->num_planes; i++) {
 		unsigned int width = state->crtc_w / (i ? info->hsub : 1);
 		unsigned int height = state->crtc_h / (i ? info->vsub : 1);
@@ -1027,7 +1035,7 @@ static void zynqmp_disp_layer_release_dma(struct zynqmp_disp *disp,
 {
 	unsigned int i;
 
-	if (!layer->info)
+	if (!layer->info || !disp->dpsub->dma_enabled)
 		return;
 
 	for (i = 0; i < layer->info->num_channels; i++) {
@@ -1070,6 +1078,9 @@ static int zynqmp_disp_layer_request_dma(struct zynqmp_disp *disp,
 	unsigned int i;
 	int ret;
 
+	if (!disp->dpsub->dma_enabled)
+		return 0;
+
 	for (i = 0; i < layer->info->num_channels; i++) {
 		struct zynqmp_disp_layer_dma *dma = &layer->dmas[i];
 		char dma_channel_name[16];
@@ -1212,7 +1223,6 @@ int zynqmp_disp_probe(struct zynqmp_dpsub *dpsub)
 {
 	struct platform_device *pdev = to_platform_device(dpsub->dev);
 	struct zynqmp_disp *disp;
-	struct zynqmp_disp_layer *layer;
 	struct resource *res;
 	int ret;
 
@@ -1248,8 +1258,12 @@ int zynqmp_disp_probe(struct zynqmp_dpsub *dpsub)
 	if (ret)
 		goto error;
 
-	layer = &disp->layers[ZYNQMP_DPSUB_LAYER_VID];
-	dpsub->dma_align = 1 << layer->dmas[0].chan->device->copy_align;
+	if (disp->dpsub->dma_enabled) {
+		struct zynqmp_disp_layer *layer;
+
+		layer = &disp->layers[ZYNQMP_DPSUB_LAYER_VID];
+		dpsub->dma_align = 1 << layer->dmas[0].chan->device->copy_align;
+	}
 
 	dpsub->disp = disp;
 
diff --git a/drivers/gpu/drm/xlnx/zynqmp_dpsub.c b/drivers/gpu/drm/xlnx/zynqmp_dpsub.c
index bc2b3ab3001d..aa1eb70e02af 100644
--- a/drivers/gpu/drm/xlnx/zynqmp_dpsub.c
+++ b/drivers/gpu/drm/xlnx/zynqmp_dpsub.c
@@ -157,6 +157,7 @@ static int zynqmp_dpsub_parse_dt(struct zynqmp_dpsub *dpsub)
 	if (!np) {
 		dev_warn(dpsub->dev, "missing ports, update DT bindings\n");
 		dpsub->connected_ports = BIT(ZYNQMP_DPSUB_PORT_OUT_DP);
+		dpsub->dma_enabled = true;
 		return 0;
 	}
 
@@ -176,6 +177,8 @@ static int zynqmp_dpsub_parse_dt(struct zynqmp_dpsub *dpsub)
 	    (dpsub->connected_ports & BIT(ZYNQMP_DPSUB_PORT_LIVE_GFX)))
 		dev_warn(dpsub->dev, "live video unsupported, ignoring\n");
 
+	dpsub->dma_enabled = true;
+
 	if (dpsub->connected_ports & BIT(ZYNQMP_DPSUB_PORT_LIVE_AUDIO))
 		dev_warn(dpsub->dev, "live audio unsupported, ignoring\n");
 
diff --git a/drivers/gpu/drm/xlnx/zynqmp_dpsub.h b/drivers/gpu/drm/xlnx/zynqmp_dpsub.h
index 6ded6e45ac0a..09ea01878f2a 100644
--- a/drivers/gpu/drm/xlnx/zynqmp_dpsub.h
+++ b/drivers/gpu/drm/xlnx/zynqmp_dpsub.h
@@ -48,6 +48,8 @@ enum zynqmp_dpsub_format {
  * @aud_clk: Audio clock
  * @aud_clk_from_ps: True of the audio clock comes from PS, false from PL
  * @connected_ports: Bitmask of connected ports in the device tree
+ * @dma_enabled: True if the DMA interface is enabled, false if the DPSUB is
+ *	driven by the live input
  * @drm: The DRM/KMS device data
  * @bridge: The DP encoder bridge
  * @disp: The display controller
@@ -64,6 +66,7 @@ struct zynqmp_dpsub {
 	bool aud_clk_from_ps;
 
 	unsigned int connected_ports;
+	bool dma_enabled;
 
 	struct zynqmp_dpsub_drm *drm;
 	struct drm_bridge *bridge;
-- 
Regards,

Laurent Pinchart


^ permalink raw reply related	[flat|nested] 38+ messages in thread

* [PATCH 34/36] drm: xlnx: zynqmp_dpsub: Add support for live video input
  2021-08-09  1:34 [PATCH 00/36] drm: xlnx: zynqmp_dpsub: Initial live video input support Laurent Pinchart
                   ` (32 preceding siblings ...)
  2021-08-09  1:34 ` [PATCH 33/36] drm: xlnx: zynqmp_dpsub: Support operation without DMA engine Laurent Pinchart
@ 2021-08-09  1:34 ` Laurent Pinchart
  2021-08-09  1:34 ` [PATCH 35/36] arm64: dts: zynqmp: Add ports for the DisplayPort subsystem Laurent Pinchart
  2021-08-09  1:34 ` [PATCH 36/36] arm64: dts: zynqmp: zcu106a: Describe DisplayPort connector Laurent Pinchart
  35 siblings, 0 replies; 38+ messages in thread
From: Laurent Pinchart @ 2021-08-09  1:34 UTC (permalink / raw)
  To: dri-devel; +Cc: Michal Simek, Jianqiang Chen

Add partial live video support, with a single video input that bypasses
blending. Skip registration of the DRM device in that case, but register
the DRM bridge instead. The DRM device will be created by the driver for
the display controller in the PL.

Full live video mode with concurrent usage of the video and gfx inputs,
and blending in the DPSUB video pipeline, is currently unsupported.

Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
---
 drivers/gpu/drm/xlnx/zynqmp_dp.c    | 55 +++++++++++++++++++++++++++++
 drivers/gpu/drm/xlnx/zynqmp_dpsub.c | 32 +++++++++++++----
 2 files changed, 80 insertions(+), 7 deletions(-)

diff --git a/drivers/gpu/drm/xlnx/zynqmp_dp.c b/drivers/gpu/drm/xlnx/zynqmp_dp.c
index 09254e9c75cd..e13c883e7e57 100644
--- a/drivers/gpu/drm/xlnx/zynqmp_dp.c
+++ b/drivers/gpu/drm/xlnx/zynqmp_dp.c
@@ -14,6 +14,7 @@
 #include <drm/drm_device.h>
 #include <drm/drm_dp_helper.h>
 #include <drm/drm_edid.h>
+#include <drm/drm_fourcc.h>
 #include <drm/drm_modes.h>
 #include <drm/drm_of.h>
 
@@ -1270,6 +1271,55 @@ static void zynqmp_dp_encoder_mode_set_stream(struct zynqmp_dp *dp,
 	zynqmp_dp_write(dp, ZYNQMP_DP_USER_DATA_COUNT_PER_LANE, reg);
 }
 
+/* -----------------------------------------------------------------------------
+ * DISP Configuration
+ */
+
+static void zynqmp_dp_disp_enable(struct zynqmp_dp *dp,
+				  struct drm_bridge_state *old_bridge_state)
+{
+	enum zynqmp_dpsub_layer_id layer_id;
+	struct zynqmp_disp_layer *layer;
+	const struct drm_format_info *info;
+
+	if (dp->dpsub->connected_ports & BIT(ZYNQMP_DPSUB_PORT_LIVE_VIDEO))
+		layer_id = ZYNQMP_DPSUB_LAYER_VID;
+	else if (dp->dpsub->connected_ports & BIT(ZYNQMP_DPSUB_PORT_LIVE_GFX))
+		layer_id = ZYNQMP_DPSUB_LAYER_GFX;
+	else
+		return;
+
+	layer = dp->dpsub->layers[layer_id];
+
+	/* TODO: Make the format configurable. */
+	info = drm_format_info(DRM_FORMAT_YUV422);
+	zynqmp_disp_layer_set_format(layer, info);
+	zynqmp_disp_layer_enable(layer, ZYNQMP_DPSUB_LAYER_LIVE);
+
+	if (layer_id == ZYNQMP_DPSUB_LAYER_GFX)
+		zynqmp_disp_blend_set_global_alpha(dp->dpsub->disp, true, 255);
+	else
+		zynqmp_disp_blend_set_global_alpha(dp->dpsub->disp, false, 0);
+
+	zynqmp_disp_enable(dp->dpsub->disp);
+}
+
+static void zynqmp_dp_disp_disable(struct zynqmp_dp *dp,
+				   struct drm_bridge_state *old_bridge_state)
+{
+	struct zynqmp_disp_layer *layer;
+
+	if (dp->dpsub->connected_ports & BIT(ZYNQMP_DPSUB_PORT_LIVE_VIDEO))
+		layer = dp->dpsub->layers[ZYNQMP_DPSUB_LAYER_VID];
+	else if (dp->dpsub->connected_ports & BIT(ZYNQMP_DPSUB_PORT_LIVE_GFX))
+		layer = dp->dpsub->layers[ZYNQMP_DPSUB_LAYER_GFX];
+	else
+		return;
+
+	zynqmp_disp_disable(dp->dpsub->disp);
+	zynqmp_disp_layer_disable(layer);
+}
+
 /* -----------------------------------------------------------------------------
  * DRM Bridge
  */
@@ -1354,6 +1404,8 @@ static void zynqmp_dp_bridge_atomic_enable(struct drm_bridge *bridge,
 
 	pm_runtime_get_sync(dp->dev);
 
+	zynqmp_dp_disp_enable(dp, old_bridge_state);
+
 	/*
 	 * Retrieve the CRTC mode and adjusted mode. This requires a little
 	 * dance to go from the bridge to the encoder, to the connector and to
@@ -1427,6 +1479,9 @@ static void zynqmp_dp_bridge_atomic_disable(struct drm_bridge *bridge,
 			ZYNQMP_DP_TX_PHY_POWER_DOWN_ALL);
 	if (zynqmp_dpsub_audio_enabled(dp->dpsub))
 		zynqmp_dp_write(dp, ZYNQMP_DP_TX_AUDIO_CONTROL, 0);
+
+	zynqmp_dp_disp_disable(dp, old_bridge_state);
+
 	pm_runtime_put_sync(dp->dev);
 }
 
diff --git a/drivers/gpu/drm/xlnx/zynqmp_dpsub.c b/drivers/gpu/drm/xlnx/zynqmp_dpsub.c
index aa1eb70e02af..02715be793ca 100644
--- a/drivers/gpu/drm/xlnx/zynqmp_dpsub.c
+++ b/drivers/gpu/drm/xlnx/zynqmp_dpsub.c
@@ -19,6 +19,7 @@
 #include <linux/slab.h>
 
 #include <drm/drm_atomic_helper.h>
+#include <drm/drm_bridge.h>
 #include <drm/drm_modeset_helper.h>
 
 #include "zynqmp_disp.h"
@@ -173,11 +174,22 @@ static int zynqmp_dpsub_parse_dt(struct zynqmp_dpsub *dpsub)
 	}
 
 	/* Sanity checks. */
+	if ((dpsub->connected_ports & BIT(ZYNQMP_DPSUB_PORT_LIVE_VIDEO)) &&
+	    (dpsub->connected_ports & BIT(ZYNQMP_DPSUB_PORT_LIVE_GFX))) {
+		dev_err(dpsub->dev, "only one live video input is supported\n");
+		return -EINVAL;
+	}
+
 	if ((dpsub->connected_ports & BIT(ZYNQMP_DPSUB_PORT_LIVE_VIDEO)) ||
-	    (dpsub->connected_ports & BIT(ZYNQMP_DPSUB_PORT_LIVE_GFX)))
-		dev_warn(dpsub->dev, "live video unsupported, ignoring\n");
-
-	dpsub->dma_enabled = true;
+	    (dpsub->connected_ports & BIT(ZYNQMP_DPSUB_PORT_LIVE_GFX))) {
+		if (dpsub->vid_clk_from_ps) {
+			dev_err(dpsub->dev,
+				"live video input requires PL clock\n");
+			return -EINVAL;
+		}
+	} else {
+		dpsub->dma_enabled = true;
+	}
 
 	if (dpsub->connected_ports & BIT(ZYNQMP_DPSUB_PORT_LIVE_AUDIO))
 		dev_warn(dpsub->dev, "live audio unsupported, ignoring\n");
@@ -241,9 +253,13 @@ static int zynqmp_dpsub_probe(struct platform_device *pdev)
 	if (ret)
 		goto err_dp;
 
-	ret = zynqmp_dpsub_drm_init(dpsub);
-	if (ret)
-		goto err_disp;
+	if (dpsub->dma_enabled) {
+		ret = zynqmp_dpsub_drm_init(dpsub);
+		if (ret)
+			goto err_disp;
+	} else {
+		drm_bridge_add(dpsub->bridge);
+	}
 
 	dev_info(&pdev->dev, "ZynqMP DisplayPort Subsystem driver probed");
 
@@ -269,6 +285,8 @@ static int zynqmp_dpsub_remove(struct platform_device *pdev)
 
 	if (dpsub->drm)
 		zynqmp_dpsub_drm_cleanup(dpsub);
+	else
+		drm_bridge_remove(dpsub->bridge);
 
 	zynqmp_disp_remove(dpsub);
 	zynqmp_dp_remove(dpsub);
-- 
Regards,

Laurent Pinchart


^ permalink raw reply related	[flat|nested] 38+ messages in thread

* [PATCH 35/36] arm64: dts: zynqmp: Add ports for the DisplayPort subsystem
  2021-08-09  1:34 [PATCH 00/36] drm: xlnx: zynqmp_dpsub: Initial live video input support Laurent Pinchart
                   ` (33 preceding siblings ...)
  2021-08-09  1:34 ` [PATCH 34/36] drm: xlnx: zynqmp_dpsub: Add support for live video input Laurent Pinchart
@ 2021-08-09  1:34 ` Laurent Pinchart
  2021-08-09  1:34 ` [PATCH 36/36] arm64: dts: zynqmp: zcu106a: Describe DisplayPort connector Laurent Pinchart
  35 siblings, 0 replies; 38+ messages in thread
From: Laurent Pinchart @ 2021-08-09  1:34 UTC (permalink / raw)
  To: dri-devel; +Cc: Michal Simek, Jianqiang Chen, Rob Herring, devicetree

The DPSUB DT bindings now specify ports to model the connections with
the programmable logic and the DisplayPort output. Add them to the
device tree.

Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
---
 arch/arm64/boot/dts/xilinx/zynqmp.dtsi | 24 ++++++++++++++++++++++++
 1 file changed, 24 insertions(+)

diff --git a/arch/arm64/boot/dts/xilinx/zynqmp.dtsi b/arch/arm64/boot/dts/xilinx/zynqmp.dtsi
index 28dccb891a53..5128beb1dac0 100644
--- a/arch/arm64/boot/dts/xilinx/zynqmp.dtsi
+++ b/arch/arm64/boot/dts/xilinx/zynqmp.dtsi
@@ -879,6 +879,30 @@ zynqmp_dpsub: display@fd4a0000 {
 			       <&zynqmp_dpdma ZYNQMP_DPDMA_VIDEO1>,
 			       <&zynqmp_dpdma ZYNQMP_DPDMA_VIDEO2>,
 			       <&zynqmp_dpdma ZYNQMP_DPDMA_GRAPHICS>;
+
+			ports {
+				#address-cells = <1>;
+				#size-cells = <0>;
+
+				port@0 {
+					reg = <0>;
+				};
+				port@1 {
+					reg = <1>;
+				};
+				port@2 {
+					reg = <2>;
+				};
+				port@3 {
+					reg = <3>;
+				};
+				port@4 {
+					reg = <4>;
+				};
+				port@5 {
+					reg = <5>;
+				};
+			};
 		};
 	};
 };
-- 
Regards,

Laurent Pinchart


^ permalink raw reply related	[flat|nested] 38+ messages in thread

* [PATCH 36/36] arm64: dts: zynqmp: zcu106a: Describe DisplayPort connector
  2021-08-09  1:34 [PATCH 00/36] drm: xlnx: zynqmp_dpsub: Initial live video input support Laurent Pinchart
                   ` (34 preceding siblings ...)
  2021-08-09  1:34 ` [PATCH 35/36] arm64: dts: zynqmp: Add ports for the DisplayPort subsystem Laurent Pinchart
@ 2021-08-09  1:34 ` Laurent Pinchart
  35 siblings, 0 replies; 38+ messages in thread
From: Laurent Pinchart @ 2021-08-09  1:34 UTC (permalink / raw)
  To: dri-devel; +Cc: Michal Simek, Jianqiang Chen, Rob Herring, devicetree

Add a device tree node to describe the DisplayPort connector, and
connect it to the DPSUB output.

Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
---
 .../boot/dts/xilinx/zynqmp-zcu106-revA.dts    | 20 +++++++++++++++++++
 1 file changed, 20 insertions(+)

diff --git a/arch/arm64/boot/dts/xilinx/zynqmp-zcu106-revA.dts b/arch/arm64/boot/dts/xilinx/zynqmp-zcu106-revA.dts
index eff7c6447087..aaf44ab256a1 100644
--- a/arch/arm64/boot/dts/xilinx/zynqmp-zcu106-revA.dts
+++ b/arch/arm64/boot/dts/xilinx/zynqmp-zcu106-revA.dts
@@ -146,6 +146,18 @@ refhdmi: refhdmi {
 		#clock-cells = <0>;
 		clock-frequency = <114285000>;
 	};
+
+	dpcon {
+		compatible = "dp-connector";
+		label = "P11";
+		type = "full-size";
+
+		port {
+			dpcon_in: endpoint {
+				remote-endpoint = <&dpsub_dp_out>;
+			};
+		};
+	};
 };
 
 &can1 {
@@ -165,6 +177,14 @@ &zynqmp_dpsub {
 	phy-names = "dp-phy0", "dp-phy1";
 	phys = <&psgtr 1 PHY_TYPE_DP 0 3>,
 	       <&psgtr 0 PHY_TYPE_DP 1 3>;
+
+	ports {
+		port@5 {
+			dpsub_dp_out: endpoint {
+				remote-endpoint = <&dpcon_in>;
+			};
+		};
+	};
 };
 
 /* fpd_dma clk 667MHz, lpd_dma 500MHz */
-- 
Regards,

Laurent Pinchart


^ permalink raw reply related	[flat|nested] 38+ messages in thread

* Re: [PATCH 01/36] dt-bindings: display: xlnx: zynqmp-dpsub: Add OF graph ports
  2021-08-09  1:34 ` [PATCH 01/36] dt-bindings: display: xlnx: zynqmp-dpsub: Add OF graph ports Laurent Pinchart
@ 2021-08-13 20:54   ` Rob Herring
  0 siblings, 0 replies; 38+ messages in thread
From: Rob Herring @ 2021-08-13 20:54 UTC (permalink / raw)
  To: Laurent Pinchart
  Cc: Rob Herring, dri-devel, Michal Simek, Jianqiang Chen, devicetree

On Mon, 09 Aug 2021 04:34:22 +0300, Laurent Pinchart wrote:
> The DPSUB doesn't live in isolation, but is connected to the
> programmable logic for live inputs and outputs, and also has a
> DisplayPort output. Model all those using OF graph.
> 
> Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
> ---
>  .../display/xlnx/xlnx,zynqmp-dpsub.yaml       | 67 +++++++++++++++++++
>  1 file changed, 67 insertions(+)
> 

Reviewed-by: Rob Herring <robh@kernel.org>

^ permalink raw reply	[flat|nested] 38+ messages in thread

end of thread, other threads:[~2021-08-13 20:54 UTC | newest]

Thread overview: 38+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-08-09  1:34 [PATCH 00/36] drm: xlnx: zynqmp_dpsub: Initial live video input support Laurent Pinchart
2021-08-09  1:34 ` [PATCH 01/36] dt-bindings: display: xlnx: zynqmp-dpsub: Add OF graph ports Laurent Pinchart
2021-08-13 20:54   ` Rob Herring
2021-08-09  1:34 ` [PATCH 02/36] drm: xlnx: zynqmp_dpsub: Switch to atomic encoder enable/disable Laurent Pinchart
2021-08-09  1:34 ` [PATCH 03/36] drm: xlnx: zynqmp_dpsub: Constify mode argument to function Laurent Pinchart
2021-08-09  1:34 ` [PATCH 04/36] drm: xlnx: zynqmp_dpsub: Create DRM bridge to model DP encoder Laurent Pinchart
2021-08-09  1:34 ` [PATCH 05/36] drm: xlnx: zynqmp_dpsub: Don't access connector in zynqmp_dp_set_format() Laurent Pinchart
2021-08-09  1:34 ` [PATCH 06/36] drm: xlnx: zynqmp_dpsub: Move connector registration to bridge attach Laurent Pinchart
2021-08-09  1:34 ` [PATCH 07/36] drm: xlnx: zynqmp_dpsub: Move encoder to DPSUB core Laurent Pinchart
2021-08-09  1:34 ` [PATCH 08/36] drm: xlnx: zynqmp_dpsub: Attach to the next bridge Laurent Pinchart
2021-08-09  1:34 ` [PATCH 09/36] drm: xlnx: zynqmp_dpsub: Use DRM connector bridge helper Laurent Pinchart
2021-08-09  1:34 ` [PATCH 10/36] drm: xlnx: zynqmp_dpsub: Report HPD through the bridge Laurent Pinchart
2021-08-09  1:34 ` [PATCH 11/36] drm: xlnx: zynqmp_dpsub: Drop unused zynqmp_disp.event field Laurent Pinchart
2021-08-09  1:34 ` [PATCH 12/36] drm: xlnx: zynqmp_dpsub: Drop unused zynqmp_disp_format.bus_fmt field Laurent Pinchart
2021-08-09  1:34 ` [PATCH 13/36] drm: xlnx: zynqmp_dpsub: Don't pass CRTC to zynqmp_disp_setup_clock() Laurent Pinchart
2021-08-09  1:34 ` [PATCH 14/36] drm: xlnx: zynqmp_dpsub: Configure blender in zynqmp_disp_enable() Laurent Pinchart
2021-08-09  1:34 ` [PATCH 15/36] drm: xlnx: zynqmp_dpsub: Use local variable in zynqmp_disp_layer_update() Laurent Pinchart
2021-08-09  1:34 ` [PATCH 16/36] drm: xlnx: zynqmp_dpsub: Pass format info to zynqmp_disp_layer_set_format() Laurent Pinchart
2021-08-09  1:34 ` [PATCH 17/36] drm: xlnx: zynqmp_dpsub: Remplace hardcoded values with ARRAY_SIZE() Laurent Pinchart
2021-08-09  1:34 ` [PATCH 18/36] drm: xlnx: zynqmp_dpsub: Don't use drmm_kcalloc() for temporary data Laurent Pinchart
2021-08-09  1:34 ` [PATCH 19/36] drm: xlnx: zynqmp_dpsub: Move pclk from zynqmp_disp to zynqmp_dpsub Laurent Pinchart
2021-08-09  1:34 ` [PATCH 20/36] drm: xlnx: zynqmp_dpsub: Move audio clk " Laurent Pinchart
2021-08-09  1:34 ` [PATCH 21/36] drm: xlnx: zynqmp_dpsub: Move CRTC to zynqmp_dpsub structure Laurent Pinchart
2021-08-09  1:34 ` [PATCH 22/36] drm: xlnx: zynqmp_dpsub: Move planes " Laurent Pinchart
2021-08-09  1:34 ` [PATCH 23/36] drm: xlnx: zynqmp_dpsub: Move CRTC handling to zynqmp_kms.c Laurent Pinchart
2021-08-09  1:34 ` [PATCH 24/36] drm: xlnx: zynqmp_dpsub: Move planes " Laurent Pinchart
2021-08-09  1:34 ` [PATCH 25/36] drm: xlnx: zynqmp_dpsub: Register AUX bus at bridge attach time Laurent Pinchart
2021-08-09  1:34 ` [PATCH 26/36] drm: xlnx: zynqmp_dpsub: Move DP bridge init to zynqmp_dp_probe() Laurent Pinchart
2021-08-09  1:34 ` [PATCH 27/36] drm: xlnx: zynqmp_dpsub: Manage DP and DISP allocations manually Laurent Pinchart
2021-08-09  1:34 ` [PATCH 28/36] drm: xlnx: zynqmp_dpsub: Move all DRM init and cleanup to zynqmp_kms.c Laurent Pinchart
2021-08-09  1:34 ` [PATCH 29/36] drm: xlnx: zynqmp_dpsub: Decouple DRM device from zynqmp_dpsub Laurent Pinchart
2021-08-09  1:34 ` [PATCH 30/36] drm: xlnx: zynqmp_dpsub: Rename zynqmp_dpsub_handle_vblank with DRM prefix Laurent Pinchart
2021-08-09  1:34 ` [PATCH 31/36] drm: xlnx: zynqmp_dpsub: Parse DT to find connected ports Laurent Pinchart
2021-08-09  1:34 ` [PATCH 32/36] drm: xlnx: zynqmp_dpsub: Allow configuration of layer mode Laurent Pinchart
2021-08-09  1:34 ` [PATCH 33/36] drm: xlnx: zynqmp_dpsub: Support operation without DMA engine Laurent Pinchart
2021-08-09  1:34 ` [PATCH 34/36] drm: xlnx: zynqmp_dpsub: Add support for live video input Laurent Pinchart
2021-08-09  1:34 ` [PATCH 35/36] arm64: dts: zynqmp: Add ports for the DisplayPort subsystem Laurent Pinchart
2021-08-09  1:34 ` [PATCH 36/36] arm64: dts: zynqmp: zcu106a: Describe DisplayPort connector Laurent Pinchart

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.