[PATCH v2 2/2] drm/panel: Add Sony Tulip Truly NT35521 driver

2021-08-08 Thread Shawn Guo
It adds a DRM panel driver for Sony Tulip Truly NT35521 5.24" 1280x720
DSI panel, which can be found on Sony Xperia M4 Aqua phone.  The panel
backlight is managed through DSI link.

The driver is built using linux-mdss-dsi-panel-driver-generator[1], and
additionally modeling the 5V control GPIOs with regulators and adding
Backlight GPIO support.

[1] https://github.com/msm8916-mainline/linux-mdss-dsi-panel-driver-generator

Signed-off-by: Shawn Guo 
---
 drivers/gpu/drm/panel/Kconfig |  10 +
 drivers/gpu/drm/panel/Makefile|   1 +
 .../panel/panel-sony-tulip-truly-nt35521.c| 552 ++
 3 files changed, 563 insertions(+)
 create mode 100644 drivers/gpu/drm/panel/panel-sony-tulip-truly-nt35521.c

diff --git a/drivers/gpu/drm/panel/Kconfig b/drivers/gpu/drm/panel/Kconfig
index ef87d92cdf49..a5809eab49b2 100644
--- a/drivers/gpu/drm/panel/Kconfig
+++ b/drivers/gpu/drm/panel/Kconfig
@@ -502,6 +502,16 @@ config DRM_PANEL_SONY_ACX565AKM
  Say Y here if you want to enable support for the Sony ACX565AKM
  800x600 3.5" panel (found on the Nokia N900).
 
+config DRM_PANEL_SONY_TULIP_TRULY_NT35521
+   tristate "Sony Tulip Truly NT35521 panel"
+   depends on GPIOLIB && OF
+   depends on DRM_MIPI_DSI
+   depends on BACKLIGHT_CLASS_DEVICE
+   help
+ Say Y here if you want to enable support for the Sony Tulip
+ NT35521 1280x720 video mode panel as found on Sony Xperia M4
+ Aqua phone.
+
 config DRM_PANEL_TDO_TL070WSH30
tristate "TDO TL070WSH30 DSI panel"
depends on OF
diff --git a/drivers/gpu/drm/panel/Makefile b/drivers/gpu/drm/panel/Makefile
index cae4d976c069..852ce32d58ee 100644
--- a/drivers/gpu/drm/panel/Makefile
+++ b/drivers/gpu/drm/panel/Makefile
@@ -52,6 +52,7 @@ obj-$(CONFIG_DRM_PANEL_SITRONIX_ST7703) += 
panel-sitronix-st7703.o
 obj-$(CONFIG_DRM_PANEL_SITRONIX_ST7789V) += panel-sitronix-st7789v.o
 obj-$(CONFIG_DRM_PANEL_SONY_ACX424AKP) += panel-sony-acx424akp.o
 obj-$(CONFIG_DRM_PANEL_SONY_ACX565AKM) += panel-sony-acx565akm.o
+obj-$(CONFIG_DRM_PANEL_SONY_TULIP_TRULY_NT35521) += 
panel-sony-tulip-truly-nt35521.o
 obj-$(CONFIG_DRM_PANEL_TDO_TL070WSH30) += panel-tdo-tl070wsh30.o
 obj-$(CONFIG_DRM_PANEL_TPO_TD028TTEC1) += panel-tpo-td028ttec1.o
 obj-$(CONFIG_DRM_PANEL_TPO_TD043MTEA1) += panel-tpo-td043mtea1.o
diff --git a/drivers/gpu/drm/panel/panel-sony-tulip-truly-nt35521.c 
b/drivers/gpu/drm/panel/panel-sony-tulip-truly-nt35521.c
new file mode 100644
index ..eba926c6f722
--- /dev/null
+++ b/drivers/gpu/drm/panel/panel-sony-tulip-truly-nt35521.c
@@ -0,0 +1,552 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2021, Linaro Limited
+ *
+ * Generated with linux-mdss-dsi-panel-driver-generator from vendor device 
tree:
+ *   Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ */
+
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+
+#include 
+#include 
+#include 
+
+struct truly_nt35521 {
+   struct drm_panel panel;
+   struct mipi_dsi_device *dsi;
+   struct regulator_bulk_data supplies[2];
+   struct gpio_desc *reset_gpio;
+   struct gpio_desc *blen_gpio;
+   bool prepared;
+   bool enabled;
+};
+
+static inline
+struct truly_nt35521 *to_truly_nt35521(struct drm_panel *panel)
+{
+   return container_of(panel, struct truly_nt35521, panel);
+}
+
+#define dsi_generic_write_seq(dsi, seq...) do {
\
+   static const u8 d[] = { seq };  \
+   int ret;\
+   ret = mipi_dsi_generic_write(dsi, d, ARRAY_SIZE(d));\
+   if (ret < 0)\
+   return ret; \
+   } while (0)
+
+static void truly_nt35521_reset(struct truly_nt35521 *ctx)
+{
+   gpiod_set_value_cansleep(ctx->reset_gpio, 1);
+   usleep_range(1000, 2000);
+   gpiod_set_value_cansleep(ctx->reset_gpio, 1);
+   usleep_range(1, 11000);
+   gpiod_set_value_cansleep(ctx->reset_gpio, 0);
+   msleep(150);
+}
+
+static int truly_nt35521_on(struct truly_nt35521 *ctx)
+{
+   struct mipi_dsi_device *dsi = ctx->dsi;
+   struct device *dev = >dev;
+   int ret;
+
+   dsi->mode_flags |= MIPI_DSI_MODE_LPM;
+
+   dsi_generic_write_seq(dsi, 0xf0, 0x55, 0xaa, 0x52, 0x08, 0x00);
+   dsi_generic_write_seq(dsi, 0xff, 0xaa, 0x55, 0xa5, 0x80);
+   dsi_generic_write_seq(dsi, 0x6f, 0x11, 0x00);
+   dsi_generic_write_seq(dsi, 0xf7, 0x20, 0x00);
+   dsi_generic_write_seq(dsi, 0x6f, 0x01);
+   dsi_generic_write_seq(dsi, 0xb1, 0x21);
+   dsi_generic_write_seq(dsi, 0xbd, 0x01, 0xa0, 0x10, 0x08, 0x01);
+   dsi_generic_write_seq(dsi, 0xb8, 0x01, 0x02, 0x0c, 0x02);
+   dsi_generic_write_seq(dsi, 0xbb, 0x11, 0x11);
+   dsi_generic_write_seq(dsi, 0xbc, 0x00, 

[PATCH v2 1/2] dt-bindings: display: Add Sony Tulip Truly NT35521 panel support

2021-08-08 Thread Shawn Guo
The Sony Tulip Truly NT35521 is a 5.24" 1280x720 DSI panel, which can
be found on Sony Xperia M4 Aqua phone.  The backlight is managed
through DSI link.

Signed-off-by: Shawn Guo 
---
 .../panel/sony,tulip-truly-nt35521.yaml   | 72 +++
 1 file changed, 72 insertions(+)
 create mode 100644 
Documentation/devicetree/bindings/display/panel/sony,tulip-truly-nt35521.yaml

diff --git 
a/Documentation/devicetree/bindings/display/panel/sony,tulip-truly-nt35521.yaml 
b/Documentation/devicetree/bindings/display/panel/sony,tulip-truly-nt35521.yaml
new file mode 100644
index ..967972939598
--- /dev/null
+++ 
b/Documentation/devicetree/bindings/display/panel/sony,tulip-truly-nt35521.yaml
@@ -0,0 +1,72 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/display/panel/sony,tulip-truly-nt35521.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Sony Tulip Truly NT35521 5.24" 1280x720 MIPI-DSI Panel
+
+maintainers:
+  - Shawn Guo 
+
+description: |
+  The Sony Tulip Truly NT35521 is a 5.24" 1280x720 MIPI-DSI panel, which
+  can be found no Sony Xperia M4 phone.  The panel backlight is managed
+  through DSI link.
+
+allOf:
+  - $ref: panel-common.yaml#
+
+properties:
+  compatible:
+const: sony,tulip-truly-nt35521
+
+  reg: true
+
+  positive5-supply:
+description: Positive 5V supply
+
+  negative5-supply:
+description: Negative 5V supply
+
+  reset-gpios: true
+
+  enable-gpios: true
+
+  port: true
+
+required:
+  - compatible
+  - reg
+  - positive5-supply
+  - negative5-supply
+  - reset-gpios
+  - enable-gpios
+  - port
+
+additionalProperties: false
+
+examples:
+  - |
+#include 
+
+dsi {
+#address-cells = <1>;
+#size-cells = <0>;
+
+panel@0 {
+compatible = "sony,tulip-truly-nt35521";
+reg = <0>;
+positive5-supply = <_reg>;
+negative5-supply = <_reg>;
+reset-gpios = < 25 GPIO_ACTIVE_LOW>;
+enable-gpios = < 10 GPIO_ACTIVE_HIGH>;
+
+port {
+panel_in: endpoint {
+remote-endpoint = <_out>;
+};
+};
+};
+};
+...
-- 
2.17.1



[PATCH v2 0/2] Add Sony Tulip Truly NT35521 panel support

2021-08-08 Thread Shawn Guo
It adds driver for Sony Tulip Truly NT35521 5.24" 1280x720 DSI panel,
which can be found on Sony Xperia M4 Aqua phone.

Changes for v2:
- Add `port` node into bindings.
- Re-create the driver using linux-mdss-dsi-panel-driver-generator[1].
- Rename the driver to include Sony Tulip.
- Model 5V control GPIOs with regulators.
- Rename Backlight GPIO as "backlight-gpios".

[1] https://github.com/msm8916-mainline/linux-mdss-dsi-panel-driver-generator

Shawn Guo (2):
  dt-bindings: display: Add Sony Tulip Truly NT35521 panel support
  drm/panel: Add Sony Tulip Truly NT35521 driver

 .../panel/sony,tulip-truly-nt35521.yaml   |  72 +++
 drivers/gpu/drm/panel/Kconfig |  10 +
 drivers/gpu/drm/panel/Makefile|   1 +
 .../panel/panel-sony-tulip-truly-nt35521.c| 552 ++
 4 files changed, 635 insertions(+)
 create mode 100644 
Documentation/devicetree/bindings/display/panel/sony,tulip-truly-nt35521.yaml
 create mode 100644 drivers/gpu/drm/panel/panel-sony-tulip-truly-nt35521.c

-- 
2.17.1



Re: [PATCH 2/2] drm/panel: Add Truly NT35521 panel driver

2021-08-08 Thread Shawn Guo
On Sun, Aug 08, 2021 at 05:29:30PM +0200, Stephan Gerhold wrote:
> > 2) The driver works good, if the kernel is launched via "fastboot boot".
> >But if the kernel is flashed to eMMC and launched by bootloader with
> >splash screen, kernel will fail to bring up the panel.  After kernel
> >boots up, a blank & unblank cycle can get panel work though.
> > 
> > The problem 2) is not driver generator related.  @Konrad, did you see
> > it on asus-z00t-tm5p5-n35596 driver?
> > 
> 
> Do you have CONFIG_DRM_MSM=y (built-in) instead of =m (module) maybe?
> I think a similar issue exists on MSM8916 but it does not happen
> for some reason if CONFIG_DRM_MSM=m instead of =y. Somehow having it
> load later during the boot process fixes some things there.

Indeed!  I have CONFIG_DRM_MSM=y in my build, and changing it to module
removes the problem.  Thanks much for the hint, Stephan!

Shawn


[PATCH] drm/amd/display: use do-while-0 for DC_TRACE_LEVEL_MESSAGE()

2021-08-08 Thread Randy Dunlap
Building with W=1 complains about an empty 'else' statement, so use the
usual do-nothing-while-0 loop to quieten this warning.

../drivers/gpu/drm/amd/amdgpu/../display/dc/dce/dmub_psr.c:113:53: warning: 
suggest braces around empty body in an 'else' statement [-Wempty-body]
  113 | *state, retry_count);

Fixes: b30eda8d416c ("drm/amd/display: Add ETW log to dmub_psr_get_state")
Signed-off-by: Randy Dunlap 
Cc: Wyatt Wood 
Cc: Alex Deucher 
Cc: Christian König 
Cc: "Pan, Xinhui" 
Cc: Harry Wentland 
Cc: Leo Li 
Cc: amd-...@lists.freedesktop.org
Cc: dri-devel@lists.freedesktop.org
Cc: David Airlie 
Cc: Daniel Vetter 
---
 drivers/gpu/drm/amd/display/dc/dce/dmub_psr.c |2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

--- linux-next-20210806.orig/drivers/gpu/drm/amd/display/dc/dce/dmub_psr.c
+++ linux-next-20210806/drivers/gpu/drm/amd/display/dc/dce/dmub_psr.c
@@ -29,7 +29,7 @@
 #include "dmub/dmub_srv.h"
 #include "core_types.h"
 
-#define DC_TRACE_LEVEL_MESSAGE(...) /* do nothing */
+#define DC_TRACE_LEVEL_MESSAGE(...)do {} while (0) /* do nothing */
 
 #define MAX_PIPES 6
 


[GIT PULL FOR v5.15] Xilinx ZynqMP DPSUB miscellaneous fixes

2021-08-08 Thread Laurent Pinchart
Hi Dave and Daniel,

The following changes since commit 49f7844b08844ac7029f997702099c552566262b:

  Merge tag 'drm-misc-next-2021-08-05' of 
git://anongit.freedesktop.org/drm/drm-misc into drm-next (2021-08-06 06:59:30 
+1000)

are available in the Git repository at:

  git://linuxtv.org/pinchartl/media.git tags/drm-xilinx-dpsub-20210809

for you to fetch changes up to 6ebfd22c969044bda7b5c3f0a20a958c7ff656b9:

  drm/xlnx/zynqmp_disp: Fix incorrectly named enum 'zynqmp_disp_layer_id' 
(2021-08-09 02:28:05 +0300)


- Miscellaneous fixes in ZynqMP DPSUB driver


Dylan Yip (1):
  drm: xlnx: zynqmp_dpsub: Update dependencies for ZynqMP DP

Laurent Pinchart (4):
  drm: xlnx: zynqmp_dpsub: Pass disp structure to all internal functions
  drm: xlnx: zynqmp_dpsub: Fix graphics layer blending
  drm: xlnx: zynqmp_dpsub: Add global alpha support
  drm: xlnx: zynqmp_dpsub: Expose plane ordering to userspace

Lee Jones (1):
  drm/xlnx/zynqmp_disp: Fix incorrectly named enum 'zynqmp_disp_layer_id'

Quanyang Wang (3):
  drm: xlnx: zynqmp_dpsub: Call pm_runtime_get_sync before setting pixel 
clock
  drm: xlnx: zynqmp: release reset to DP controller before accessing DP 
registers
  drm: xlnx: zynqmp: Add zynqmp_disp_layer_is_video() to simplify the code

 drivers/gpu/drm/xlnx/Kconfig   |   2 +
 drivers/gpu/drm/xlnx/zynqmp_disp.c | 322 ++---
 drivers/gpu/drm/xlnx/zynqmp_dp.c   |  22 +--
 3 files changed, 174 insertions(+), 172 deletions(-)

-- 
Regards,

Laurent Pinchart


[PATCH 35/36] arm64: dts: zynqmp: Add ports for the DisplayPort subsystem

2021-08-08 Thread Laurent Pinchart
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 
---
 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@fd4a {
   <_dpdma ZYNQMP_DPDMA_VIDEO1>,
   <_dpdma ZYNQMP_DPDMA_VIDEO2>,
   <_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



[PATCH 36/36] arm64: dts: zynqmp: zcu106a: Describe DisplayPort connector

2021-08-08 Thread Laurent Pinchart
Add a device tree node to describe the DisplayPort connector, and
connect it to the DPSUB output.

Signed-off-by: Laurent Pinchart 
---
 .../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 = <_dp_out>;
+   };
+   };
+   };
 };
 
  {
@@ -165,6 +177,14 @@ _dpsub {
phy-names = "dp-phy0", "dp-phy1";
phys = < 1 PHY_TYPE_DP 0 3>,
   < 0 PHY_TYPE_DP 1 3>;
+
+   ports {
+   port@5 {
+   dpsub_dp_out: endpoint {
+   remote-endpoint = <_in>;
+   };
+   };
+   };
 };
 
 /* fpd_dma clk 667MHz, lpd_dma 500MHz */
-- 
Regards,

Laurent Pinchart



[PATCH 29/36] drm: xlnx: zynqmp_dpsub: Decouple DRM device from zynqmp_dpsub

2021-08-08 Thread Laurent Pinchart
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 
---
 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 
 
 #include 
-#include 
-#include 
 #include 
 
 #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(>drm);
+   if (!dpsub->drm)
+   return 0;
+
+   return drm_mode_config_helper_suspend(>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(>drm);
+   if (!dpsub->drm)
+   return 0;
+
+   return drm_mode_config_helper_resume(>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(>dev, _dpsub_drm_driver,
-  struct zynqmp_dpsub, drm);
-   if (IS_ERR(dpsub))
-   return PTR_ERR(dpsub);
-
-   ret = drmm_add_action(>drm, zynqmp_dpsub_release, dpsub);
-   if (ret < 0)
-   return ret;
+   dpsub = kzalloc(sizeof(*dpsub), GFP_KERNEL);
+   if (!dpsub)
+   return -ENOMEM;
 
dpsub->dev = >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(>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(>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(>drm);
+   if (!dpsub->drm)
+   return;
+
+   drm_atomic_helper_shutdown(>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 
-#include 
-#include 
-
 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_LAYERS2
 
@@ -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
  * 

[PATCH 24/36] drm: xlnx: zynqmp_dpsub: Move planes handling to zynqmp_kms.c

2021-08-08 Thread Laurent Pinchart
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 
---
 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 
  */
 
-#include 
-#include 
-#include 
 #include 
 #include 
 #include 
 #include 
 #include 
-#include 
 
 #include 
 #include 
@@ -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,
-   

[PATCH 32/36] drm: xlnx: zynqmp_dpsub: Allow configuration of layer mode

2021-08-08 Thread Laurent Pinchart
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 
---
 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 

[PATCH 34/36] drm: xlnx: zynqmp_dpsub: Add support for live video input

2021-08-08 Thread Laurent Pinchart
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 
---
 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 
 #include 
 #include 
+#include 
 #include 
 #include 
 
@@ -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 
 
 #include 
+#include 
 #include 
 
 #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 {
+   

[PATCH 25/36] drm: xlnx: zynqmp_dpsub: Register AUX bus at bridge attach time

2021-08-08 Thread Laurent Pinchart
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 
---
 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(>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 = >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 = >dev;
dp->dpsub = dpsub;
dp->status = connector_status_disconnected;
-   dp->drm = drm;
 
INIT_DELAYED_WORK(>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(>hpd_work);
-   zynqmp_dp_aux_cleanup(dp);
 
zynqmp_dp_write(dp, ZYNQMP_DP_TRANSMITTER_ENABLE, 0);
zynqmp_dp_write(dp, ZYNQMP_DP_INT_DS, 0x);
-- 
Regards,

Laurent Pinchart



[PATCH 22/36] drm: xlnx: zynqmp_dpsub: Move planes to zynqmp_dpsub structure

2021-08-08 Thread Laurent Pinchart
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 
---
 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 = >layers[i];
+   struct drm_plane *plane = >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, >plane, 0,
+   ret = drm_universal_plane_init(disp->drm, plane, 0,
   _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(>plane,
-_disp_plane_helper_funcs);
+   drm_plane_helper_add(plane, _disp_plane_helper_funcs);
 
-   drm_plane_create_zpos_immutable_property(>plane, i);
+   drm_plane_create_zpos_immutable_property(plane, i);
if (zynqmp_disp_layer_is_gfx(layer))
-   drm_plane_create_alpha_property(>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 = >layers[ZYNQMP_DISP_LAYER_GFX].plane;
+   struct drm_plane *plane = >dpsub->planes[ZYNQMP_DISP_LAYER_GFX];
struct drm_crtc *crtc = >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 
 
 #include 
-#include 
 #include 
-#include 
 #include 
 #include 
 #include 
@@ -29,12 +27,12 

[PATCH 27/36] drm: xlnx: zynqmp_dpsub: Manage DP and DISP allocations manually

2021-08-08 Thread Laurent Pinchart
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 
---
 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 
 #include 
 #include 
-#include 
 #include 
 
 #include 
@@ -21,6 +20,7 @@
 #include 
 #include 
 #include 
+#include 
 
 #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 = >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 = >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_BIT44
 
 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 
 #include 
 #include 
-#include 
 #include 
 #include 
 
@@ -26,6 +25,7 @@
 #include 
 #include 
 #include 
+#include 
 
 #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 

[PATCH 33/36] drm: xlnx: zynqmp_dpsub: Support operation without DMA engine

2021-08-08 Thread Laurent Pinchart
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 
---
 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 = >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 = >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 = >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 

[PATCH 30/36] drm: xlnx: zynqmp_dpsub: Rename zynqmp_dpsub_handle_vblank with DRM prefix

2021-08-08 Thread Laurent Pinchart
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 
---
 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(>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(>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



[PATCH 31/36] drm: xlnx: zynqmp_dpsub: Parse DT to find connected ports

2021-08-08 Thread Laurent Pinchart
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 
---
 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 
 #include 
 #include 
+#include 
 #include 
 #include 
 #include 
@@ -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(>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_LAYERS2
 
+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



[PATCH 12/36] drm: xlnx: zynqmp_dpsub: Drop unused zynqmp_disp_format.bus_fmt field

2021-08-08 Thread Laurent Pinchart
The bus_fmt field of the zynqmp_disp_format structure is unused. Drop
it.

Signed-off-by: Laurent Pinchart 
---
 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



[PATCH 28/36] drm: xlnx: zynqmp_dpsub: Move all DRM init and cleanup to zynqmp_kms.c

2021-08-08 Thread Laurent Pinchart
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 
---
 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 
 
 #include 
-#include 
-#include 
 #include 
-#include 
-#include 
-#include 
-#include 
 #include 
-#include 
-#include 
-#include 
+#include 
 
 #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, );
-}
-
-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   = _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 = >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 = _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 = >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);
 

[PATCH 17/36] drm: xlnx: zynqmp_dpsub: Remplace hardcoded values with ARRAY_SIZE()

2021-08-08 Thread Laurent Pinchart
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 
---
 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 = >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, >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 = >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(>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



[PATCH 26/36] drm: xlnx: zynqmp_dpsub: Move DP bridge init to zynqmp_dp_probe()

2021-08-08 Thread Laurent Pinchart
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 
---
 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 = >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 = _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 = >bridge;
+   bridge->funcs = _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(>crtc);
drm_simple_encoder_init(>drm, encoder, DRM_MODE_ENCODER_NONE);
-- 
Regards,

Laurent Pinchart



[PATCH 16/36] drm: xlnx: zynqmp_dpsub: Pass format info to zynqmp_disp_layer_set_format()

2021-08-08 Thread Laurent Pinchart
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 
---
 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



[PATCH 19/36] drm: xlnx: zynqmp_dpsub: Move pclk from zynqmp_disp to zynqmp_dpsub

2021-08-08 Thread Laurent Pinchart
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 
---
 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(>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 

[PATCH 20/36] drm: xlnx: zynqmp_dpsub: Move audio clk from zynqmp_disp to zynqmp_dpsub

2021-08-08 Thread Laurent Pinchart
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 
---
 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 

[PATCH 08/36] drm: xlnx: zynqmp_dpsub: Attach to the next bridge

2021-08-08 Thread Laurent Pinchart
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 
---
 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,
+ >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



[PATCH 23/36] drm: xlnx: zynqmp_dpsub: Move CRTC handling to zynqmp_kms.c

2021-08-08 Thread Laurent Pinchart
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 
---
 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 
 #include 
-#include 
-#include 
 #include 
 #include 
 #include 
@@ -20,17 +18,13 @@
 #include 
 #include 
 #include 
-#include 
 
 #include 
-#include 
 #include 
 #include 
 #include 
 #include 
 #include 
-#include 
-#include 
 
 #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 = >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(>dpsub->crtc);
-}
-
 /* 
-
  * ZynqMP Display Layer & DRM Plane
  */
@@ -,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);

[PATCH 09/36] drm: xlnx: zynqmp_dpsub: Use DRM connector bridge helper

2021-08-08 Thread Laurent Pinchart
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 
---
 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 
-#include 
 #include 
 #include 
 #include 
@@ -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 = >connector;
int ret;
 
-   /* Create the DRM connector. */
-   connector->polled = DRM_CONNECTOR_POLL_HPD;
-   ret = drm_connector_init(dp->drm, connector,
-_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, _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(>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(>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 >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(>bridge, 
>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

[PATCH 21/36] drm: xlnx: zynqmp_dpsub: Move CRTC to zynqmp_dpsub structure

2021-08-08 Thread Laurent Pinchart
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 
---
 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 = >crtc;
+   struct drm_crtc *crtc = >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(>crtc);
+   return drm_crtc_mask(>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(>crtc);
+   drm_crtc_vblank_off(crtc);
 
spin_lock_irq(>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 = >layers[ZYNQMP_DISP_LAYER_GFX].plane;
+   struct drm_crtc *crtc = >dpsub->crtc;
int ret;
 
-   ret = drm_crtc_init_with_planes(disp->drm, >crtc, plane,
+   ret = drm_crtc_init_with_planes(disp->drm, crtc, plane,
NULL, _disp_crtc_funcs, NULL);
if (ret < 0)
return ret;
 
-   drm_crtc_helper_add(>crtc, _disp_crtc_helper_funcs);
+   drm_crtc_helper_add(crtc, _disp_crtc_helper_funcs);
 
/* Start with vertical blanking interrupt reporting disabled. */
-   drm_crtc_vblank_off(>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(>crtc);
+   u32 possible_crtcs = drm_crtc_mask(>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 
 #include 
 
 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



[PATCH 10/36] drm: xlnx: zynqmp_dpsub: Report HPD through the bridge

2021-08-08 Thread Laurent Pinchart
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 
---
 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 
 #include 
 #include 
-#include 
 
 #include 
 #include 
@@ -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(>bridge);
+   drm_bridge_hpd_notify(>bridge, status);
 }
 
 static irqreturn_t zynqmp_dp_irq_handler(int irq, void *data)
-- 
Regards,

Laurent Pinchart



[PATCH 15/36] drm: xlnx: zynqmp_dpsub: Use local variable in zynqmp_disp_layer_update()

2021-08-08 Thread Laurent Pinchart
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 
---
 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 = >dmas[i];
-- 
Regards,

Laurent Pinchart



[PATCH 13/36] drm: xlnx: zynqmp_dpsub: Don't pass CRTC to zynqmp_disp_setup_clock()

2021-08-08 Thread Laurent Pinchart
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 
---
 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



[PATCH 11/36] drm: xlnx: zynqmp_dpsub: Drop unused zynqmp_disp.event field

2021-08-08 Thread Laurent Pinchart
The event field of the zynqmp_disp structure is unused. Drop it.

Signed-off-by: Laurent Pinchart 
---
 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



[PATCH 18/36] drm: xlnx: zynqmp_dpsub: Don't use drmm_kcalloc() for temporary data

2021-08-08 Thread Laurent Pinchart
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 
---
 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 = >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, _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, >plane, 0,
   _disp_plane_funcs,
-  drm_formats,
-  layer->info->num_formats,
+  formats, num_formats,
   NULL, type, NULL);
+   kfree(formats);
if (ret)
return ret;
 
-- 
Regards,

Laurent Pinchart



[PATCH 14/36] drm: xlnx: zynqmp_dpsub: Configure blender in zynqmp_disp_enable()

2021-08-08 Thread Laurent Pinchart
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 
---
 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



[PATCH 05/36] drm: xlnx: zynqmp_dpsub: Don't access connector in zynqmp_dp_set_format()

2021-08-08 Thread Laurent Pinchart
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 
---
 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 = >config;
unsigned int num_colors;
 
@@ -1152,12 +1153,11 @@ static int zynqmp_dp_set_format(struct zynqmp_dp *dp,
return -EINVAL;
}
 
-   display = >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 = _state->adjusted_mode;
mode = _state->mode;
 
-   zynqmp_dp_set_format(dp, ZYNQMP_DPSUB_FORMAT_RGB, 8);
+   zynqmp_dp_set_format(dp, >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



[PATCH 07/36] drm: xlnx: zynqmp_dpsub: Move encoder to DPSUB core

2021-08-08 Thread Laurent Pinchart
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 
---
 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 
 #include 
 #include 
-#include 
 #include 
 #include 
 #include 
 #include 
-#include 
 
 #include 
 #include 
@@ -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 >encoder;
+   return >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(>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(>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(>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 = >bridge;
-   struct drm_encoder *encoder = >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 = >encoder;
+   /* Initialize the bridge. */
bridge->funcs = _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 

[PATCH 06/36] drm: xlnx: zynqmp_dpsub: Move connector registration to bridge attach

2021-08-08 Thread Laurent Pinchart
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 
---
 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 = >connector;
+   int ret;
+
+   /* Create the DRM connector. */
+   connector->polled = DRM_CONNECTOR_POLL_HPD;
+   ret = drm_connector_init(dp->drm, connector,
+_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, _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 = >bridge;
struct drm_encoder *encoder = >encoder;
-   struct drm_connector *connector = >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, _dp_encoder_helper_funcs);
 
-   connector->polled = DRM_CONNECTOR_POLL_HPD;
-   ret = drm_connector_init(encoder->dev, connector,
-_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, _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



[PATCH 03/36] drm: xlnx: zynqmp_dpsub: Constify mode argument to function

2021-08-08 Thread Laurent Pinchart
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 
---
 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



[PATCH 01/36] dt-bindings: display: xlnx: zynqmp-dpsub: Add OF graph ports

2021-08-08 Thread Laurent Pinchart
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 
---
 .../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:
< 0 PHY_TYPE_DP 1 3 2700>;
 
 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 = <_connector>;
+};
+};
+};
 };
 
 ...
-- 
Regards,

Laurent Pinchart



[PATCH 04/36] drm: xlnx: zynqmp_dpsub: Create DRM bridge to model DP encoder

2021-08-08 Thread Laurent Pinchart
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 
---
 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, 

[PATCH 02/36] drm: xlnx: zynqmp_dpsub: Switch to atomic encoder enable/disable

2021-08-08 Thread Laurent Pinchart
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 
---
 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



[PATCH 00/36] drm: xlnx: zynqmp_dpsub: Initial live video input support

2021-08-08 Thread Laurent Pinchart
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 

[PATCH v2 4/4] drm/mediatek: Add cmdq_handle in mtk_crtc

2021-08-08 Thread Chun-Kuang Hu
One mtk_crtc need just one cmdq_handle, so add one cmdq_handle
in mtk_crtc to prevent frequently allocation and free of
cmdq_handle.

Signed-off-by: Chun-Kuang Hu 
---
 drivers/gpu/drm/mediatek/mtk_drm_crtc.c | 28 -
 1 file changed, 18 insertions(+), 10 deletions(-)

diff --git a/drivers/gpu/drm/mediatek/mtk_drm_crtc.c 
b/drivers/gpu/drm/mediatek/mtk_drm_crtc.c
index ad4c1a3a9294..2d9becec04a9 100644
--- a/drivers/gpu/drm/mediatek/mtk_drm_crtc.c
+++ b/drivers/gpu/drm/mediatek/mtk_drm_crtc.c
@@ -54,6 +54,7 @@ struct mtk_drm_crtc {
 #if IS_REACHABLE(CONFIG_MTK_CMDQ)
struct mbox_client  cmdq_cl;
struct mbox_chan*cmdq_chan;
+   struct cmdq_pkt cmdq_handle;
u32 cmdq_event;
u32 cmdq_vblank_cnt;
 #endif
@@ -226,19 +227,16 @@ struct mtk_ddp_comp *mtk_drm_ddp_comp_for_plane(struct 
drm_crtc *crtc,
 }
 
 #if IS_REACHABLE(CONFIG_MTK_CMDQ)
-static struct cmdq_pkt *mtk_drm_cmdq_pkt_create(struct mbox_chan *chan, size_t 
size)
+static int mtk_drm_cmdq_pkt_create(struct mbox_chan *chan, struct cmdq_pkt 
*pkt,
+   size_t size)
 {
-   struct cmdq_pkt *pkt;
struct device *dev;
dma_addr_t dma_addr;
 
-   pkt = kzalloc(sizeof(*pkt), GFP_KERNEL);
-   if (!pkt)
-   return ERR_PTR(-ENOMEM);
pkt->va_base = kzalloc(size, GFP_KERNEL);
if (!pkt->va_base) {
kfree(pkt);
-   return ERR_PTR(-ENOMEM);
+   return -ENOMEM;
}
pkt->buf_size = size;
 
@@ -249,12 +247,12 @@ static struct cmdq_pkt *mtk_drm_cmdq_pkt_create(struct 
mbox_chan *chan, size_t s
dev_err(dev, "dma map failed, size=%u\n", (u32)(u64)size);
kfree(pkt->va_base);
kfree(pkt);
-   return ERR_PTR(-ENOMEM);
+   return -ENOMEM;
}
 
pkt->pa_base = dma_addr;
 
-   return pkt;
+   return 0;
 }
 
 static void mtk_drm_cmdq_pkt_destroy(struct mbox_chan *chan, struct cmdq_pkt 
*pkt)
@@ -477,7 +475,7 @@ static void mtk_drm_crtc_update_config(struct mtk_drm_crtc 
*mtk_crtc,
   bool needs_vblank)
 {
 #if IS_REACHABLE(CONFIG_MTK_CMDQ)
-   struct cmdq_pkt *cmdq_handle;
+   struct cmdq_pkt *cmdq_handle = _crtc->cmdq_handle;
 #endif
struct drm_crtc *crtc = _crtc->base;
struct mtk_drm_private *priv = crtc->dev->dev_private;
@@ -517,7 +515,7 @@ static void mtk_drm_crtc_update_config(struct mtk_drm_crtc 
*mtk_crtc,
 #if IS_REACHABLE(CONFIG_MTK_CMDQ)
if (mtk_crtc->cmdq_chan) {
mbox_flush(mtk_crtc->cmdq_chan, 2000);
-   cmdq_handle = mtk_drm_cmdq_pkt_create(mtk_crtc->cmdq_chan, 
PAGE_SIZE);
+   cmdq_handle->cmd_buf_size = 0;
cmdq_pkt_clear_event(cmdq_handle, mtk_crtc->cmdq_event);
cmdq_pkt_wfe(cmdq_handle, mtk_crtc->cmdq_event, false);
mtk_crtc_ddp_config(crtc, cmdq_handle);
@@ -915,6 +913,16 @@ int mtk_drm_crtc_create(struct drm_device *drm_dev,
drm_crtc_index(_crtc->base));
mbox_free_channel(mtk_crtc->cmdq_chan);
mtk_crtc->cmdq_chan = NULL;
+   } else {
+   ret = mtk_drm_cmdq_pkt_create(mtk_crtc->cmdq_chan,
+  _crtc->cmdq_handle,
+  PAGE_SIZE);
+   if (ret) {
+   dev_dbg(dev, "mtk_crtc %d failed to create cmdq 
packet\n",
+   drm_crtc_index(_crtc->base));
+   mbox_free_channel(mtk_crtc->cmdq_chan);
+   mtk_crtc->cmdq_chan = NULL;
+   }
}
}
 #endif
-- 
2.25.1



[PATCH v2 3/4] drm/mediatek: Detect CMDQ execution timeout

2021-08-08 Thread Chun-Kuang Hu
CMDQ is used to update display register in vblank period, so
it should be execute in next vblank. If it fail to execute
in next 2 vblank, tiemout happen.

Signed-off-by: Chun-Kuang Hu 
---
 drivers/gpu/drm/mediatek/mtk_drm_crtc.c | 14 --
 1 file changed, 12 insertions(+), 2 deletions(-)

diff --git a/drivers/gpu/drm/mediatek/mtk_drm_crtc.c 
b/drivers/gpu/drm/mediatek/mtk_drm_crtc.c
index da92a3fedd0a..ad4c1a3a9294 100644
--- a/drivers/gpu/drm/mediatek/mtk_drm_crtc.c
+++ b/drivers/gpu/drm/mediatek/mtk_drm_crtc.c
@@ -55,6 +55,7 @@ struct mtk_drm_crtc {
struct mbox_client  cmdq_cl;
struct mbox_chan*cmdq_chan;
u32 cmdq_event;
+   u32 cmdq_vblank_cnt;
 #endif
 
struct device   *mmsys_dev;
@@ -269,6 +270,7 @@ static void ddp_cmdq_cb(struct mbox_client *cl, void *mssg)
struct mtk_drm_crtc *mtk_crtc = container_of(cl, struct mtk_drm_crtc, 
cmdq_cl);
struct cmdq_cb_data *data = mssg;
 
+   mtk_crtc->cmdq_vblank_cnt = 0;
mtk_drm_cmdq_pkt_destroy(mtk_crtc->cmdq_chan, data->pkt);
 }
 #endif
@@ -524,6 +526,11 @@ static void mtk_drm_crtc_update_config(struct mtk_drm_crtc 
*mtk_crtc,
cmdq_handle->pa_base,
cmdq_handle->cmd_buf_size,
DMA_TO_DEVICE);
+   /*
+* CMDQ command should execute in next vblank,
+* If it fail to execute in next 2 vblank, timeout happen.
+*/
+   mtk_crtc->cmdq_vblank_cnt = 2;
mbox_send_message(mtk_crtc->cmdq_chan, cmdq_handle);
mbox_client_txdone(mtk_crtc->cmdq_chan, 0);
}
@@ -540,11 +547,14 @@ static void mtk_crtc_ddp_irq(void *data)
 
 #if IS_REACHABLE(CONFIG_MTK_CMDQ)
if (!priv->data->shadow_register && !mtk_crtc->cmdq_chan)
+   mtk_crtc_ddp_config(crtc, NULL);
+   else if (mtk_crtc->cmdq_vblank_cnt > 0 && --mtk_crtc->cmdq_vblank_cnt 
== 0)
+   DRM_ERROR("mtk_crtc %d CMDQ execute command timeout!\n",
+ drm_crtc_index(_crtc->base));
 #else
if (!priv->data->shadow_register)
-#endif
mtk_crtc_ddp_config(crtc, NULL);
-
+#endif
mtk_drm_finish_page_flip(mtk_crtc);
 }
 
-- 
2.25.1



[PATCH v2 2/4] drm/mediatek: Remove struct cmdq_client

2021-08-08 Thread Chun-Kuang Hu
In mailbox rx_callback, it pass struct mbox_client to callback
function, but it could not map back to mtk_drm_crtc instance
because struct cmdq_client use a pointer to struct mbox_client:

struct cmdq_client {
struct mbox_client client;
struct mbox_chan *chan;
};

struct mtk_drm_crtc {
/* client instance data */
struct cmdq_client *cmdq_client;
};

so remove struct cmdq_client and let mtk_drm_crtc instance define
mbox_client as:

struct mtk_drm_crtc {
/* client instance data */
struct mbox_client cl;
};

and in rx_callback function, use struct mbox_client to get
struct mtk_drm_crtc.

Signed-off-by: Chun-Kuang Hu 
---
 drivers/gpu/drm/mediatek/mtk_drm_crtc.c | 80 +++--
 1 file changed, 62 insertions(+), 18 deletions(-)

diff --git a/drivers/gpu/drm/mediatek/mtk_drm_crtc.c 
b/drivers/gpu/drm/mediatek/mtk_drm_crtc.c
index cac8fe219c95..da92a3fedd0a 100644
--- a/drivers/gpu/drm/mediatek/mtk_drm_crtc.c
+++ b/drivers/gpu/drm/mediatek/mtk_drm_crtc.c
@@ -52,7 +52,8 @@ struct mtk_drm_crtc {
boolpending_async_planes;
 
 #if IS_REACHABLE(CONFIG_MTK_CMDQ)
-   struct cmdq_client  *cmdq_client;
+   struct mbox_client  cmdq_cl;
+   struct mbox_chan*cmdq_chan;
u32 cmdq_event;
 #endif
 
@@ -224,11 +225,51 @@ struct mtk_ddp_comp *mtk_drm_ddp_comp_for_plane(struct 
drm_crtc *crtc,
 }
 
 #if IS_REACHABLE(CONFIG_MTK_CMDQ)
+static struct cmdq_pkt *mtk_drm_cmdq_pkt_create(struct mbox_chan *chan, size_t 
size)
+{
+   struct cmdq_pkt *pkt;
+   struct device *dev;
+   dma_addr_t dma_addr;
+
+   pkt = kzalloc(sizeof(*pkt), GFP_KERNEL);
+   if (!pkt)
+   return ERR_PTR(-ENOMEM);
+   pkt->va_base = kzalloc(size, GFP_KERNEL);
+   if (!pkt->va_base) {
+   kfree(pkt);
+   return ERR_PTR(-ENOMEM);
+   }
+   pkt->buf_size = size;
+
+   dev = chan->mbox->dev;
+   dma_addr = dma_map_single(dev, pkt->va_base, pkt->buf_size,
+ DMA_TO_DEVICE);
+   if (dma_mapping_error(dev, dma_addr)) {
+   dev_err(dev, "dma map failed, size=%u\n", (u32)(u64)size);
+   kfree(pkt->va_base);
+   kfree(pkt);
+   return ERR_PTR(-ENOMEM);
+   }
+
+   pkt->pa_base = dma_addr;
+
+   return pkt;
+}
+
+static void mtk_drm_cmdq_pkt_destroy(struct mbox_chan *chan, struct cmdq_pkt 
*pkt)
+{
+   dma_unmap_single(chan->mbox->dev, pkt->pa_base, pkt->buf_size,
+DMA_TO_DEVICE);
+   kfree(pkt->va_base);
+   kfree(pkt);
+}
+
 static void ddp_cmdq_cb(struct mbox_client *cl, void *mssg)
 {
+   struct mtk_drm_crtc *mtk_crtc = container_of(cl, struct mtk_drm_crtc, 
cmdq_cl);
struct cmdq_cb_data *data = mssg;
 
-   cmdq_pkt_destroy(data->pkt);
+   mtk_drm_cmdq_pkt_destroy(mtk_crtc->cmdq_chan, data->pkt);
 }
 #endif
 
@@ -472,19 +513,19 @@ static void mtk_drm_crtc_update_config(struct 
mtk_drm_crtc *mtk_crtc,
mtk_mutex_release(mtk_crtc->mutex);
}
 #if IS_REACHABLE(CONFIG_MTK_CMDQ)
-   if (mtk_crtc->cmdq_client) {
-   mbox_flush(mtk_crtc->cmdq_client->chan, 2000);
-   cmdq_handle = cmdq_pkt_create(mtk_crtc->cmdq_client, PAGE_SIZE);
+   if (mtk_crtc->cmdq_chan) {
+   mbox_flush(mtk_crtc->cmdq_chan, 2000);
+   cmdq_handle = mtk_drm_cmdq_pkt_create(mtk_crtc->cmdq_chan, 
PAGE_SIZE);
cmdq_pkt_clear_event(cmdq_handle, mtk_crtc->cmdq_event);
cmdq_pkt_wfe(cmdq_handle, mtk_crtc->cmdq_event, false);
mtk_crtc_ddp_config(crtc, cmdq_handle);
cmdq_pkt_finalize(cmdq_handle);
-   
dma_sync_single_for_device(mtk_crtc->cmdq_client->chan->mbox->dev,
+   dma_sync_single_for_device(mtk_crtc->cmdq_chan->mbox->dev,
cmdq_handle->pa_base,
cmdq_handle->cmd_buf_size,
DMA_TO_DEVICE);
-   mbox_send_message(mtk_crtc->cmdq_client->chan, cmdq_handle);
-   mbox_client_txdone(mtk_crtc->cmdq_client->chan, 0);
+   mbox_send_message(mtk_crtc->cmdq_chan, cmdq_handle);
+   mbox_client_txdone(mtk_crtc->cmdq_chan, 0);
}
 #endif
mtk_crtc->config_updating = false;
@@ -498,7 +539,7 @@ static void mtk_crtc_ddp_irq(void *data)
struct mtk_drm_private *priv = crtc->dev->dev_private;
 
 #if IS_REACHABLE(CONFIG_MTK_CMDQ)
-   if (!priv->data->shadow_register && !mtk_crtc->cmdq_client)
+   if (!priv->data->shadow_register && !mtk_crtc->cmdq_chan)
 #else
if (!priv->data->shadow_register)
 #endif
@@ -841,17 +882,20 @@ int mtk_drm_crtc_create(struct drm_device *drm_dev,
mutex_init(_crtc->hw_lock);
 
 #if IS_REACHABLE(CONFIG_MTK_CMDQ)
- 

[PATCH v2 1/4] drm/mediatek: Use mailbox rx_callback instead of cmdq_task_cb

2021-08-08 Thread Chun-Kuang Hu
rx_callback is a standard mailbox callback mechanism and could cover the
function of proprietary cmdq_task_cb, so use the standard one instead of
the proprietary one.

Signed-off-by: Chun-Kuang Hu 
---
 drivers/gpu/drm/mediatek/mtk_drm_crtc.c | 16 +---
 1 file changed, 13 insertions(+), 3 deletions(-)

diff --git a/drivers/gpu/drm/mediatek/mtk_drm_crtc.c 
b/drivers/gpu/drm/mediatek/mtk_drm_crtc.c
index 474efb844249..cac8fe219c95 100644
--- a/drivers/gpu/drm/mediatek/mtk_drm_crtc.c
+++ b/drivers/gpu/drm/mediatek/mtk_drm_crtc.c
@@ -4,6 +4,8 @@
  */
 
 #include 
+#include 
+#include 
 #include 
 #include 
 #include 
@@ -222,9 +224,11 @@ struct mtk_ddp_comp *mtk_drm_ddp_comp_for_plane(struct 
drm_crtc *crtc,
 }
 
 #if IS_REACHABLE(CONFIG_MTK_CMDQ)
-static void ddp_cmdq_cb(struct cmdq_cb_data data)
+static void ddp_cmdq_cb(struct mbox_client *cl, void *mssg)
 {
-   cmdq_pkt_destroy(data.data);
+   struct cmdq_cb_data *data = mssg;
+
+   cmdq_pkt_destroy(data->pkt);
 }
 #endif
 
@@ -475,7 +479,12 @@ static void mtk_drm_crtc_update_config(struct mtk_drm_crtc 
*mtk_crtc,
cmdq_pkt_wfe(cmdq_handle, mtk_crtc->cmdq_event, false);
mtk_crtc_ddp_config(crtc, cmdq_handle);
cmdq_pkt_finalize(cmdq_handle);
-   cmdq_pkt_flush_async(cmdq_handle, ddp_cmdq_cb, cmdq_handle);
+   
dma_sync_single_for_device(mtk_crtc->cmdq_client->chan->mbox->dev,
+   cmdq_handle->pa_base,
+   cmdq_handle->cmd_buf_size,
+   DMA_TO_DEVICE);
+   mbox_send_message(mtk_crtc->cmdq_client->chan, cmdq_handle);
+   mbox_client_txdone(mtk_crtc->cmdq_client->chan, 0);
}
 #endif
mtk_crtc->config_updating = false;
@@ -842,6 +851,7 @@ int mtk_drm_crtc_create(struct drm_device *drm_dev,
}
 
if (mtk_crtc->cmdq_client) {
+   mtk_crtc->cmdq_client->client.rx_callback = ddp_cmdq_cb;
ret = of_property_read_u32_index(priv->mutex_node,
 "mediatek,gce-events",
 
drm_crtc_index(_crtc->base),
-- 
2.25.1



[PATCH v2 0/4] CMDQ refinement of Mediatek DRM driver

2021-08-08 Thread Chun-Kuang Hu
These refinements include using standard mailbox callback interface,
timeout detection, and a fixed cmdq_handle.

Changes in v2:
1. Define mtk_drm_cmdq_pkt_create() and mtk_drm_cmdq_pkt_destroy()
   when CONFIG_MTK_CMDQ is reachable.

Chun-Kuang Hu (4):
  drm/mediatek: Use mailbox rx_callback instead of cmdq_task_cb
  drm/mediatek: Remove struct cmdq_client
  drm/mediatek: Detect CMDQ execution timeout
  drm/mediatek: Add cmdq_handle in mtk_crtc

 drivers/gpu/drm/mediatek/mtk_drm_crtc.c | 110 
 1 file changed, 91 insertions(+), 19 deletions(-)

-- 
2.25.1



Re: [PATCH 2/8] drm/ingenic: Simplify code by using hwdescs array

2021-08-08 Thread Paul Cercueil

Hi Thomas,

Le dim., août 8 2021 at 20:42:53 +0200, Thomas Zimmermann 
 a écrit :

Hi

Am 08.08.21 um 15:45 schrieb Paul Cercueil:
Instead of having one 'hwdesc' variable for the plane #0 and one for 
the

plane #1, use a 'hwdesc[2]' array, where the DMA hardware descriptors
are indexed by the plane's number.

Signed-off-by: Paul Cercueil 
---
  drivers/gpu/drm/ingenic/ingenic-drm-drv.c | 38 
---

  1 file changed, 20 insertions(+), 18 deletions(-)

diff --git a/drivers/gpu/drm/ingenic/ingenic-drm-drv.c 
b/drivers/gpu/drm/ingenic/ingenic-drm-drv.c

index e42eb43d8020..bc71ba44ccf4 100644
--- a/drivers/gpu/drm/ingenic/ingenic-drm-drv.c
+++ b/drivers/gpu/drm/ingenic/ingenic-drm-drv.c
@@ -49,8 +49,7 @@ struct ingenic_dma_hwdesc {
  } __aligned(16);
    struct ingenic_dma_hwdescs {
-   struct ingenic_dma_hwdesc hwdesc_f0;
-   struct ingenic_dma_hwdesc hwdesc_f1;
+   struct ingenic_dma_hwdesc hwdesc[2];
struct ingenic_dma_hwdesc hwdesc_pal;
u16 palette[256] __aligned(16);
  };
@@ -141,6 +140,13 @@ static inline struct ingenic_drm 
*drm_nb_get_priv(struct notifier_block *nb)

return container_of(nb, struct ingenic_drm, clock_nb);
  }
  +static inline dma_addr_t dma_hwdesc_addr(const struct 
ingenic_drm *priv, bool use_f1)


Using the plane index instead of a boolean would be more aligned to 
the way this function is being used.


Alright, I can do that.


+{
+   u32 offset = offsetof(struct ingenic_dma_hwdescs, hwdesc[use_f1]);


use_f1 is a function parameter. Is offsetof guaranteed to be 
evaluated at runtime?


The offsetof() macro could be defined like this:
#define offsetof(type, elm) ((size_t) &((type *) 0)->elm)

So I don't see a reason why this couldn't be evaluated at runtime, yes. 
It's just that the value of "offset" is not known at compilation time 
(unless the compiler does some constant propagation). In practice 
though, this code works fine.



+
+   return priv->dma_hwdescs_phys + offset;
+}
+
  static int ingenic_drm_update_pixclk(struct notifier_block *nb,
 unsigned long action,
 void *data)
@@ -562,6 +568,7 @@ static void 
ingenic_drm_plane_atomic_update(struct drm_plane *plane,

struct ingenic_dma_hwdesc *hwdesc;
unsigned int width, height, cpp, offset;
dma_addr_t addr;
+   bool use_f1;
u32 fourcc;
   if (newstate && newstate->fb) {
@@ -569,16 +576,14 @@ static void 
ingenic_drm_plane_atomic_update(struct drm_plane *plane,

drm_fb_cma_sync_non_coherent(>drm, oldstate, 
newstate);
   crtc_state = newstate->crtc->state;
+   use_f1 = priv->soc_info->has_osd && plane != >f0;
   addr = drm_fb_cma_get_gem_addr(newstate->fb, newstate, 0);
width = newstate->src_w >> 16;
height = newstate->src_h >> 16;
cpp = newstate->fb->format->cpp[0];
  -if (!priv->soc_info->has_osd || plane == >f0)
-   hwdesc = >dma_hwdescs->hwdesc_f0;
-   else
-   hwdesc = >dma_hwdescs->hwdesc_f1;
+   hwdesc = >dma_hwdescs->hwdesc[use_f1];


Maybe add a helper that looks up the hwdesc field for a given plane?


Sure.


   hwdesc->addr = addr;
hwdesc->cmd = JZ_LCD_CMD_EOF_IRQ | (width * height * cpp / 4);
@@ -591,9 +596,9 @@ static void 
ingenic_drm_plane_atomic_update(struct drm_plane *plane,

if (fourcc == DRM_FORMAT_C8)
offset = offsetof(struct ingenic_dma_hwdescs, 
hwdesc_pal);
else
-   offset = offsetof(struct ingenic_dma_hwdescs, 
hwdesc_f0);
+   offset = offsetof(struct ingenic_dma_hwdescs, 
hwdesc[0]);
  -			priv->dma_hwdescs->hwdesc_f0.next = priv->dma_hwdescs_phys + 
offset;
+			priv->dma_hwdescs->hwdesc[0].next = priv->dma_hwdescs_phys + 
offset;


Maybe priv->dma_hwdescs_phys + offset could be computed in a more 
flexible helper than dma_hwdesc_addr().



   crtc_state->color_mgmt_changed = fourcc == 
DRM_FORMAT_C8;
}
@@ -964,20 +969,17 @@ static int ingenic_drm_bind(struct device 
*dev, bool has_components)

      /* Configure DMA hwdesc for foreground0 plane */
-   dma_hwdesc_phys_f0 = priv->dma_hwdescs_phys
-   + offsetof(struct ingenic_dma_hwdescs, hwdesc_f0);
-   priv->dma_hwdescs->hwdesc_f0.next = dma_hwdesc_phys_f0;
-   priv->dma_hwdescs->hwdesc_f0.id = 0xf0;
+   dma_hwdesc_phys_f0 = dma_hwdesc_addr(priv, 0);
+   priv->dma_hwdescs->hwdesc[0].next = dma_hwdesc_phys_f0;
+   priv->dma_hwdescs->hwdesc[0].id = 0xf0;
   /* Configure DMA hwdesc for foreground1 plane */
-   dma_hwdesc_phys_f1 = priv->dma_hwdescs_phys
-   + offsetof(struct ingenic_dma_hwdescs, hwdesc_f1);
-   

Re: [PATCH 3/8] drm/ingenic: Use standard drm_atomic_helper_commit_tail

2021-08-08 Thread Christophe JAILLET

Le 08/08/2021 à 22:09, Paul Cercueil a écrit :

Hi Christophe,

Le dim., août 8 2021 at 21:50:04 +0200, Christophe JAILLET 
 a écrit :

Le 08/08/2021 à 15:45, Paul Cercueil a écrit :

By making the CRTC's .vblank_enable() function return an error when it
is known that the hardware won't deliver a VBLANK, we can drop the
ingenic_drm_atomic_helper_commit_tail() function and use the standard
drm_atomic_helper_commit_tail() function instead.

Signed-off-by: Paul Cercueil 
---
  drivers/gpu/drm/ingenic/ingenic-drm-drv.c | 28 ---
  1 file changed, 4 insertions(+), 24 deletions(-)

diff --git a/drivers/gpu/drm/ingenic/ingenic-drm-drv.c 
b/drivers/gpu/drm/ingenic/ingenic-drm-drv.c

index bc71ba44ccf4..3ed7c27a8dde 100644
--- a/drivers/gpu/drm/ingenic/ingenic-drm-drv.c
+++ b/drivers/gpu/drm/ingenic/ingenic-drm-drv.c
@@ -706,29 +706,6 @@ static int 
ingenic_drm_encoder_atomic_check(struct drm_encoder *encoder,

  }
  }
  -static void ingenic_drm_atomic_helper_commit_tail(struct 
drm_atomic_state *old_state)

-{
-    /*
- * Just your regular drm_atomic_helper_commit_tail(), but only 
calls

- * drm_atomic_helper_wait_for_vblanks() if priv->no_vblank.
- */
-    struct drm_device *dev = old_state->dev;
-    struct ingenic_drm *priv = drm_device_get_priv(dev);
-
-    drm_atomic_helper_commit_modeset_disables(dev, old_state);
-
-    drm_atomic_helper_commit_planes(dev, old_state, 0);
-
-    drm_atomic_helper_commit_modeset_enables(dev, old_state);
-
-    drm_atomic_helper_commit_hw_done(old_state);
-
-    if (!priv->no_vblank)
-    drm_atomic_helper_wait_for_vblanks(dev, old_state);
-
-    drm_atomic_helper_cleanup_planes(dev, old_state);
-}



Hi,
if this function is removed, shouldn't:
  static struct drm_mode_config_helper_funcs 
ingenic_drm_mode_config_helpers = {

  .atomic_commit_tail = ingenic_drm_atomic_helper_commit_tail,
  };
be updated as well?

I've not seen it in the serie.


It is there though :) At the bottom of this very patch.



My email client played me some tricks, apparently!
Sorry for the noise.

CJ


Just my 2v.
CJ


Cheers,
-Paul







Re: [PATCH 3/8] drm/ingenic: Use standard drm_atomic_helper_commit_tail

2021-08-08 Thread Paul Cercueil

Hi Christophe,

Le dim., août 8 2021 at 21:50:04 +0200, Christophe JAILLET 
 a écrit :

Le 08/08/2021 à 15:45, Paul Cercueil a écrit :
By making the CRTC's .vblank_enable() function return an error when 
it

is known that the hardware won't deliver a VBLANK, we can drop the
ingenic_drm_atomic_helper_commit_tail() function and use the standard
drm_atomic_helper_commit_tail() function instead.

Signed-off-by: Paul Cercueil 
---
  drivers/gpu/drm/ingenic/ingenic-drm-drv.c | 28 
---

  1 file changed, 4 insertions(+), 24 deletions(-)

diff --git a/drivers/gpu/drm/ingenic/ingenic-drm-drv.c 
b/drivers/gpu/drm/ingenic/ingenic-drm-drv.c

index bc71ba44ccf4..3ed7c27a8dde 100644
--- a/drivers/gpu/drm/ingenic/ingenic-drm-drv.c
+++ b/drivers/gpu/drm/ingenic/ingenic-drm-drv.c
@@ -706,29 +706,6 @@ static int 
ingenic_drm_encoder_atomic_check(struct drm_encoder *encoder,

}
  }
  -static void ingenic_drm_atomic_helper_commit_tail(struct 
drm_atomic_state *old_state)

-{
-   /*
-	 * Just your regular drm_atomic_helper_commit_tail(), but only 
calls

-* drm_atomic_helper_wait_for_vblanks() if priv->no_vblank.
-*/
-   struct drm_device *dev = old_state->dev;
-   struct ingenic_drm *priv = drm_device_get_priv(dev);
-
-   drm_atomic_helper_commit_modeset_disables(dev, old_state);
-
-   drm_atomic_helper_commit_planes(dev, old_state, 0);
-
-   drm_atomic_helper_commit_modeset_enables(dev, old_state);
-
-   drm_atomic_helper_commit_hw_done(old_state);
-
-   if (!priv->no_vblank)
-   drm_atomic_helper_wait_for_vblanks(dev, old_state);
-
-   drm_atomic_helper_cleanup_planes(dev, old_state);
-}



Hi,
if this function is removed, shouldn't:
  static struct drm_mode_config_helper_funcs 
ingenic_drm_mode_config_helpers = {

.atomic_commit_tail = ingenic_drm_atomic_helper_commit_tail,
  };
be updated as well?

I've not seen it in the serie.


It is there though :) At the bottom of this very patch.


Just my 2v.
CJ


Cheers,
-Paul




[PATCH v2] drm/panel/tianma-tl057fvxp01: add panel for Motorola Moto G6

2021-08-08 Thread Julian Braha
This is a 5.7" 2160x1080 panel found on the Motorola Moto G6.
There may be other smartphones using it, as well.

Signed-off-by: Julian Braha 
---
 drivers/gpu/drm/panel/Kconfig |   7 +
 drivers/gpu/drm/panel/Makefile|   1 +
 .../gpu/drm/panel/panel-tianma-tl057fvxp01.c  | 262 ++
 3 files changed, 270 insertions(+)
 create mode 100644 drivers/gpu/drm/panel/panel-tianma-tl057fvxp01.c

diff --git a/drivers/gpu/drm/panel/Kconfig b/drivers/gpu/drm/panel/Kconfig
index beb581b96ecd..9e8a5fffa626 100644
--- a/drivers/gpu/drm/panel/Kconfig
+++ b/drivers/gpu/drm/panel/Kconfig
@@ -557,6 +557,13 @@ config DRM_PANEL_TDO_TL070WSH30
  24 bit RGB per pixel. It provides a MIPI DSI interface to
  the host, a built-in LED backlight and touch controller.
 
+config DRM_PANEL_TIANMA_TL057FVXP01
+   tristate "Tianma TL057FVXP01 panel"
+   select DRM_PANEL_MIPI_DSI_COMMON
+   help
+ Say Y here if you want to enable support for the Tianma TL057FVXP01
+ 2160x1080 5.7" panel (found on the Motorola Moto G6).
+
 config DRM_PANEL_TPO_TD028TTEC1
tristate "Toppoly (TPO) TD028TTEC1 panel driver"
depends on OF && SPI
diff --git a/drivers/gpu/drm/panel/Makefile b/drivers/gpu/drm/panel/Makefile
index c8132050bcec..9bdc2a12e719 100644
--- a/drivers/gpu/drm/panel/Makefile
+++ b/drivers/gpu/drm/panel/Makefile
@@ -57,6 +57,7 @@ obj-$(CONFIG_DRM_PANEL_SITRONIX_ST7789V) += 
panel-sitronix-st7789v.o
 obj-$(CONFIG_DRM_PANEL_SONY_ACX424AKP) += panel-sony-acx424akp.o
 obj-$(CONFIG_DRM_PANEL_SONY_ACX565AKM) += panel-sony-acx565akm.o
 obj-$(CONFIG_DRM_PANEL_TDO_TL070WSH30) += panel-tdo-tl070wsh30.o
+obj-$(CONFIG_DRM_PANEL_TIANMA_TL057FVXP01) += panel-tianma-tl057fvxp01.o
 obj-$(CONFIG_DRM_PANEL_TPO_TD028TTEC1) += panel-tpo-td028ttec1.o
 obj-$(CONFIG_DRM_PANEL_TPO_TD043MTEA1) += panel-tpo-td043mtea1.o
 obj-$(CONFIG_DRM_PANEL_TPO_TPG110) += panel-tpo-tpg110.o
diff --git a/drivers/gpu/drm/panel/panel-tianma-tl057fvxp01.c 
b/drivers/gpu/drm/panel/panel-tianma-tl057fvxp01.c
new file mode 100644
index ..7dcdcbd8ef5f
--- /dev/null
+++ b/drivers/gpu/drm/panel/panel-tianma-tl057fvxp01.c
@@ -0,0 +1,262 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2021 Julian Braha 
+ * Copyright (c) 2021, The Linux Foundation. All rights reserved.
+ * Generated with linux-mdss-dsi-panel-driver-generator from vendor device tree
+ */
+
+#include 
+#include 
+#include 
+#include 
+
+#include 
+
+#include 
+#include 
+#include 
+
+struct tianma_tl057fvxp01 {
+   struct drm_panel panel;
+   struct mipi_dsi_device *dsi;
+   struct gpio_desc *reset_gpio;
+   bool prepared;
+};
+
+static inline struct tianma_tl057fvxp01 *to_tianma_tl057fvxp01(struct 
drm_panel *panel)
+{
+   return container_of(panel, struct tianma_tl057fvxp01, panel);
+}
+
+#define dsi_dcs_write_seq(dsi, seq...) do {\
+   static const u8 d[] = { seq };  \
+   int ret;\
+   ret = mipi_dsi_dcs_write_buffer(dsi, d, ARRAY_SIZE(d)); \
+   if (ret < 0)\
+   return ret; \
+   } while (0)
+
+static void tianma_tl057fvxp01_reset(struct tianma_tl057fvxp01 *ctx)
+{
+   gpiod_set_value_cansleep(ctx->reset_gpio, 0);
+   usleep_range(5000, 6000);
+   gpiod_set_value_cansleep(ctx->reset_gpio, 1);
+   usleep_range(1000, 2000);
+   gpiod_set_value_cansleep(ctx->reset_gpio, 0);
+   usleep_range(1, 11000);
+}
+
+static int tianma_tl057fvxp01_on(struct tianma_tl057fvxp01 *ctx)
+{
+   struct mipi_dsi_device *dsi = ctx->dsi;
+   struct device *dev = >dev;
+   int ret;
+
+   dsi_dcs_write_seq(dsi, 0x00, 0x00);
+   dsi_dcs_write_seq(dsi, 0xff, 0x19, 0x11, 0x01);
+   dsi_dcs_write_seq(dsi, 0x00, 0x80);
+   dsi_dcs_write_seq(dsi, 0xff, 0x19, 0x11);
+   dsi_dcs_write_seq(dsi, 0x00, 0xb0);
+   dsi_dcs_write_seq(dsi, 0xb3, 0x04, 0x38, 0x08, 0x70);
+   dsi_dcs_write_seq(dsi, 0x00, 0x00);
+   dsi_dcs_write_seq(dsi, 0xff, 0xff, 0xff, 0xff);
+   dsi_dcs_write_seq(dsi, 0x35, 0x00);
+   dsi_dcs_write_seq(dsi, 0x51, 0xcc, 0x08);
+   dsi_dcs_write_seq(dsi, 0x53, 0x2c);
+   dsi_dcs_write_seq(dsi, 0x55, 0x01);
+
+   ret = mipi_dsi_dcs_exit_sleep_mode(dsi);
+   if (ret < 0) {
+   dev_err(dev, "Failed to exit sleep mode: %d\n", ret);
+   return ret;
+   }
+   msleep(120);
+
+   ret = mipi_dsi_dcs_set_display_on(dsi);
+   if (ret < 0) {
+   dev_err(dev, "Failed to set display on: %d\n", ret);
+   return ret;
+   }
+   usleep_range(1, 11000);
+
+   return 0;
+}
+
+static int tianma_tl057fvxp01_off(struct tianma_tl057fvxp01 *ctx)
+{
+   struct mipi_dsi_device *dsi = ctx->dsi;
+   

Re: [PATCH 3/8] drm/ingenic: Use standard drm_atomic_helper_commit_tail

2021-08-08 Thread Christophe JAILLET

Le 08/08/2021 à 15:45, Paul Cercueil a écrit :

By making the CRTC's .vblank_enable() function return an error when it
is known that the hardware won't deliver a VBLANK, we can drop the
ingenic_drm_atomic_helper_commit_tail() function and use the standard
drm_atomic_helper_commit_tail() function instead.

Signed-off-by: Paul Cercueil 
---
  drivers/gpu/drm/ingenic/ingenic-drm-drv.c | 28 ---
  1 file changed, 4 insertions(+), 24 deletions(-)

diff --git a/drivers/gpu/drm/ingenic/ingenic-drm-drv.c 
b/drivers/gpu/drm/ingenic/ingenic-drm-drv.c
index bc71ba44ccf4..3ed7c27a8dde 100644
--- a/drivers/gpu/drm/ingenic/ingenic-drm-drv.c
+++ b/drivers/gpu/drm/ingenic/ingenic-drm-drv.c
@@ -706,29 +706,6 @@ static int ingenic_drm_encoder_atomic_check(struct 
drm_encoder *encoder,
}
  }
  
-static void ingenic_drm_atomic_helper_commit_tail(struct drm_atomic_state *old_state)

-{
-   /*
-* Just your regular drm_atomic_helper_commit_tail(), but only calls
-* drm_atomic_helper_wait_for_vblanks() if priv->no_vblank.
-*/
-   struct drm_device *dev = old_state->dev;
-   struct ingenic_drm *priv = drm_device_get_priv(dev);
-
-   drm_atomic_helper_commit_modeset_disables(dev, old_state);
-
-   drm_atomic_helper_commit_planes(dev, old_state, 0);
-
-   drm_atomic_helper_commit_modeset_enables(dev, old_state);
-
-   drm_atomic_helper_commit_hw_done(old_state);
-
-   if (!priv->no_vblank)
-   drm_atomic_helper_wait_for_vblanks(dev, old_state);
-
-   drm_atomic_helper_cleanup_planes(dev, old_state);
-}



Hi,
if this function is removed, shouldn't:
  static struct drm_mode_config_helper_funcs 
ingenic_drm_mode_config_helpers = {

.atomic_commit_tail = ingenic_drm_atomic_helper_commit_tail,
  };
be updated as well?

I've not seen it in the serie.

Just my 2v.
CJ


[Bug 214001] [bisected][regression] After commit "drm/ttm: Initialize debugfs from ttm_global_init()" kernels without debugfs explicitly set to 'allow all' fail to boot

2021-08-08 Thread bugzilla-daemon
https://bugzilla.kernel.org/show_bug.cgi?id=214001

--- Comment #1 from Linux_Chemist (untaintablean...@hotmail.co.uk) ---
As an addendum, I suppose a slight source of confusion is the info for 

CONFIG_DEBUG_FS which reads:

"debugfs is a virtual file system that kernel developers use to put
debugging files into. Enable this option to be able to read and
write to these files.

For detailed documentation on the debugfs API, see
Documentation/filesystems/.

If unsure, say N."

which implies: a) that it isn't strictly necessary to have enabled in order to
boot/run normally (highlighting this bug) and b) that you would have zero need
for it if you weren't reading/writing to these debugging files. 


To then have the option to enable debugfs but only run minimally with
CONFIG_DEBUG_FS_ALLOW_NONE:
"Access is off. Clients get -PERM when trying to create nodes in
debugfs tree and debugfs is not registered as a filesystem.
Client can then back-off or continue without debugfs access."

leaves the question of 'why have it on and set to "allow none" rather than off
completely?'

-- 
You may reply to this email to add a comment.

You are receiving this mail because:
You are watching the assignee of the bug.

Re: [PATCH 8/8] drm/ingenic: Attach bridge chain to encoders

2021-08-08 Thread H. Nikolaus Schaller



> Am 08.08.2021 um 21:06 schrieb H. Nikolaus Schaller :
> 
> 
> 
>> Am 08.08.2021 um 21:04 schrieb Paul Cercueil :
>> 
>> Hi Nikolaus,
>> 
>> Le dim., août 8 2021 at 20:57:09 +0200, H. Nikolaus Schaller 
>>  a écrit :
>>> Hi Paul,
>>> all other patches apply cleanly but this one fails on top of v5.14-rc4.
>>> What base are you using?
>>> BR and thanks,
>>> Nikolaus
>> 
>> The base is drm-misc (https://cgit.freedesktop.org/drm/drm-misc), branch 
>> drm-misc-next.
> 
> Ok, fine!

Contains 3 patches for drm/ingenic and after taking them first, I can apply the 
series.

Again, BR and thanks,
Nikolaus



[Bug 214001] [bisected][regression] After commit "drm/ttm: Initialize debugfs from ttm_global_init()" kernels without debugfs explicitly set to 'allow all' fail to boot

2021-08-08 Thread bugzilla-daemon
https://bugzilla.kernel.org/show_bug.cgi?id=214001

Linux_Chemist (untaintablean...@hotmail.co.uk) changed:

   What|Removed |Added

 Regression|No  |Yes

-- 
You may reply to this email to add a comment.

You are receiving this mail because:
You are watching the assignee of the bug.

[Bug 214001] New: [bisected][regression] After commit "drm/ttm: Initialize debugfs from ttm_global_init()" kernels without debugfs explicitly set to 'allow all' fail to boot

2021-08-08 Thread bugzilla-daemon
https://bugzilla.kernel.org/show_bug.cgi?id=214001

Bug ID: 214001
   Summary: [bisected][regression] After commit "drm/ttm:
Initialize debugfs from ttm_global_init()" kernels
without debugfs explicitly set to 'allow all' fail to
boot
   Product: Drivers
   Version: 2.5
Kernel Version: 5.14-rc4
  Hardware: All
OS: Linux
  Tree: Mainline
Status: NEW
  Severity: normal
  Priority: P1
 Component: Video(DRI - non Intel)
  Assignee: drivers_video-...@kernel-bugs.osdl.org
  Reporter: untaintablean...@hotmail.co.uk
Regression: No

So this is an interesting one.

Problem: System hangs indefinitely/refuses to boot up. 5.14rc3 was totally fine
but rc4 has the problem and I've bisected the commit to: 

69de4421bb4c103ef42a32bafc596e23918c106f is the first bad commit
commit 69de4421bb4c103ef42a32bafc596e23918c106f
Author: Jason Ekstrand 
Date:   Wed Jul 21 10:23:57 2021 -0500

drm/ttm: Initialize debugfs from ttm_global_init()

We create a bunch of debugfs entries as a side-effect of
ttm_global_init() and then never clean them up.  This isn't usually a
problem because we free the whole debugfs directory on module unload.
However, if the global reference count ever goes to zero and then
ttm_global_init() is called again, we'll re-create those debugfs entries
and debugfs will complain in dmesg that we're creating entries that
already exist.  This patch fixes this problem by changing the lifetime
of the whole TTM debugfs directory to match that of the TTM global
state.

Signed-off-by: Jason Ekstrand 
Reviewed-by: Daniel Vetter 
Signed-off-by: Daniel Vetter 
Link:
https://patchwork.freedesktop.org/patch/msgid/20210721152358.2893314-6-ja...@jlekstrand.net

I then tried loading an ubuntu mainline kernel for 5.14-rc4 and that was fine
too, which meant my .config was to blame in conjunction with the change.
The specific issue narrowed down to not having debug_fs enabled in my kernel
(CONFIG_DEBUG_FS is not set)

Now I've not had debugfs enabled for many, many years (is this even necessary
on a kernel on which the user makes no use of the information it provides?) and
now I see the option CONFIG_DEBUG_FS=y allows for one of three exclusive
options. (CONFIG_DEBUG_FS_ALLOW_ALL; CONFIG_DEBUG_FS_DISALLOW_MOUNT and
CONFIG_DEBUG_FS_ALLOW_NONE)

(*Moving forward, is debug_fs now a critical component of the linux kernel and
required to be enabled (CONFIG_DEBUG_FS=Y) with a minimum of the 3rd option of
'allow none' given that so many things want to make use of it? Is debugfs
'expected' to be there to make reference to in driver code from now on?)

At any rate, I tested each of the 3 options and can confirm that since the
commit in question, the system will _only_ boot now if: 

CONFIG_DEBUG_FS_ALLOW_ALL=y

I suspect that the commit did not account for kernel compilers who don't have
debugfs at all - however, it even causes boot issues if debugfs is present but
minimalised because neither:

CONFIG_DEBUG_FS_DISALLOW_MOUNT
"The API is open but filesystem is not loaded. Clients can still do
their work and read with debug tools that do not need debugfs filesystem." 

nor CONFIG_DEBUG_FS_ALLOW_NONE:
"Access is off. Clients get -PERM when trying to create nodes in
debugfs tree and debugfs is not registered as a filesystem.
Client can then back-off or continue without debugfs access."

are sufficient to get a successful boot after this commit.

-- 
You may reply to this email to add a comment.

You are receiving this mail because:
You are watching the assignee of the bug.

Re: [PATCH 8/8] drm/ingenic: Attach bridge chain to encoders

2021-08-08 Thread H. Nikolaus Schaller



> Am 08.08.2021 um 21:04 schrieb Paul Cercueil :
> 
> Hi Nikolaus,
> 
> Le dim., août 8 2021 at 20:57:09 +0200, H. Nikolaus Schaller 
>  a écrit :
>> Hi Paul,
>> all other patches apply cleanly but this one fails on top of v5.14-rc4.
>> What base are you using?
>> BR and thanks,
>> Nikolaus
> 
> The base is drm-misc (https://cgit.freedesktop.org/drm/drm-misc), branch 
> drm-misc-next.

Ok, fine!

BR and thanks,
Nikolaus

> 
> Cheers,
> -Paul
> 
> 
>>> Am 08.08.2021 um 15:45 schrieb Paul Cercueil :
>>> Attach a top-level bridge to each encoder, which will be used for
>>> negociating the bus format and flags.
>>> All the bridges are now attached with DRM_BRIDGE_ATTACH_NO_CONNECTOR.
>>> Signed-off-by: Paul Cercueil 
>>> ---
>>> drivers/gpu/drm/ingenic/ingenic-drm-drv.c | 92 +--
>>> 1 file changed, 70 insertions(+), 22 deletions(-)
>>> diff --git a/drivers/gpu/drm/ingenic/ingenic-drm-drv.c 
>>> b/drivers/gpu/drm/ingenic/ingenic-drm-drv.c
>>> index 7ae48ead3ab6..09d5dd298078 100644
>>> --- a/drivers/gpu/drm/ingenic/ingenic-drm-drv.c
>>> +++ b/drivers/gpu/drm/ingenic/ingenic-drm-drv.c
>>> @@ -21,6 +21,7 @@
>>> #include 
>>> #include 
>>> #include 
>>> +#include 
>>> #include 
>>> #include 
>>> #include 
>>> @@ -107,6 +108,19 @@ struct ingenic_drm {
>>> struct drm_private_obj private_obj;
>>> };
>>> +struct ingenic_drm_bridge {
>>> +   struct drm_encoder encoder;
>>> +   struct drm_bridge bridge, *next_bridge;
>>> +
>>> +   struct drm_bus_cfg bus_cfg;
>>> +};
>>> +
>>> +static inline struct ingenic_drm_bridge *
>>> +to_ingenic_drm_bridge(struct drm_encoder *encoder)
>>> +{
>>> +   return container_of(encoder, struct ingenic_drm_bridge, encoder);
>>> +}
>>> +
>>> static inline struct ingenic_drm_private_state *
>>> to_ingenic_drm_priv_state(struct drm_private_state *state)
>>> {
>>> @@ -679,11 +693,10 @@ static void 
>>> ingenic_drm_encoder_atomic_mode_set(struct drm_encoder *encoder,
>>> {
>>> struct ingenic_drm *priv = drm_device_get_priv(encoder->dev);
>>> struct drm_display_mode *mode = _state->adjusted_mode;
>>> -   struct drm_connector *conn = conn_state->connector;
>>> -   struct drm_display_info *info = >display_info;
>>> +   struct ingenic_drm_bridge *bridge = to_ingenic_drm_bridge(encoder);
>>> unsigned int cfg, rgbcfg = 0;
>>> -   priv->panel_is_sharp = info->bus_flags & DRM_BUS_FLAG_SHARP_SIGNALS;
>>> +   priv->panel_is_sharp = bridge->bus_cfg.flags & 
>>> DRM_BUS_FLAG_SHARP_SIGNALS;
>>> if (priv->panel_is_sharp) {
>>> cfg = JZ_LCD_CFG_MODE_SPECIAL_TFT_1 | JZ_LCD_CFG_REV_POLARITY;
>>> @@ -696,19 +709,19 @@ static void 
>>> ingenic_drm_encoder_atomic_mode_set(struct drm_encoder *encoder,
>>> cfg |= JZ_LCD_CFG_HSYNC_ACTIVE_LOW;
>>> if (mode->flags & DRM_MODE_FLAG_NVSYNC)
>>> cfg |= JZ_LCD_CFG_VSYNC_ACTIVE_LOW;
>>> -   if (info->bus_flags & DRM_BUS_FLAG_DE_LOW)
>>> +   if (bridge->bus_cfg.flags & DRM_BUS_FLAG_DE_LOW)
>>> cfg |= JZ_LCD_CFG_DE_ACTIVE_LOW;
>>> -   if (info->bus_flags & DRM_BUS_FLAG_PIXDATA_DRIVE_NEGEDGE)
>>> +   if (bridge->bus_cfg.flags & DRM_BUS_FLAG_PIXDATA_DRIVE_NEGEDGE)
>>> cfg |= JZ_LCD_CFG_PCLK_FALLING_EDGE;
>>> if (!priv->panel_is_sharp) {
>>> -   if (conn->connector_type == DRM_MODE_CONNECTOR_TV) {
>>> +   if (conn_state->connector->connector_type == 
>>> DRM_MODE_CONNECTOR_TV) {
>>> if (mode->flags & DRM_MODE_FLAG_INTERLACE)
>>> cfg |= JZ_LCD_CFG_MODE_TV_OUT_I;
>>> else
>>> cfg |= JZ_LCD_CFG_MODE_TV_OUT_P;
>>> } else {
>>> -   switch (*info->bus_formats) {
>>> +   switch (bridge->bus_cfg.format) {
>>> case MEDIA_BUS_FMT_RGB565_1X16:
>>> cfg |= JZ_LCD_CFG_MODE_GENERIC_16BIT;
>>> break;
>>> @@ -734,20 +747,29 @@ static void 
>>> ingenic_drm_encoder_atomic_mode_set(struct drm_encoder *encoder,
>>> regmap_write(priv->map, JZ_REG_LCD_RGBC, rgbcfg);
>>> }
>>> -static int ingenic_drm_encoder_atomic_check(struct drm_encoder *encoder,
>>> -   struct drm_crtc_state *crtc_state,
>>> -   struct drm_connector_state 
>>> *conn_state)
>>> +static int ingenic_drm_bridge_attach(struct drm_bridge *bridge,
>>> +enum drm_bridge_attach_flags flags)
>>> +{
>>> +   struct ingenic_drm_bridge *ib = to_ingenic_drm_bridge(bridge->encoder);
>>> +
>>> +   return drm_bridge_attach(bridge->encoder, ib->next_bridge,
>>> +>bridge, flags);
>>> +}
>>> +
>>> +static int ingenic_drm_bridge_atomic_check(struct drm_bridge *bridge,
>>> +  struct drm_bridge_state 
>>> *bridge_state,
>>> +  struct drm_crtc_state *crtc_state,
>>> +  struct 

Re: [PATCH 8/8] drm/ingenic: Attach bridge chain to encoders

2021-08-08 Thread Paul Cercueil

Hi Nikolaus,

Le dim., août 8 2021 at 20:57:09 +0200, H. Nikolaus Schaller 
 a écrit :

Hi Paul,
all other patches apply cleanly but this one fails on top of 
v5.14-rc4.

What base are you using?
BR and thanks,
Nikolaus


The base is drm-misc (https://cgit.freedesktop.org/drm/drm-misc), 
branch drm-misc-next.


Cheers,
-Paul



 Am 08.08.2021 um 15:45 schrieb Paul Cercueil :

 Attach a top-level bridge to each encoder, which will be used for
 negociating the bus format and flags.

 All the bridges are now attached with 
DRM_BRIDGE_ATTACH_NO_CONNECTOR.


 Signed-off-by: Paul Cercueil 
 ---
 drivers/gpu/drm/ingenic/ingenic-drm-drv.c | 92 
+--

 1 file changed, 70 insertions(+), 22 deletions(-)

 diff --git a/drivers/gpu/drm/ingenic/ingenic-drm-drv.c 
b/drivers/gpu/drm/ingenic/ingenic-drm-drv.c

 index 7ae48ead3ab6..09d5dd298078 100644
 --- a/drivers/gpu/drm/ingenic/ingenic-drm-drv.c
 +++ b/drivers/gpu/drm/ingenic/ingenic-drm-drv.c
 @@ -21,6 +21,7 @@
 #include 
 #include 
 #include 
 +#include 
 #include 
 #include 
 #include 
 @@ -107,6 +108,19 @@ struct ingenic_drm {
struct drm_private_obj private_obj;
 };

 +struct ingenic_drm_bridge {
 +  struct drm_encoder encoder;
 +  struct drm_bridge bridge, *next_bridge;
 +
 +  struct drm_bus_cfg bus_cfg;
 +};
 +
 +static inline struct ingenic_drm_bridge *
 +to_ingenic_drm_bridge(struct drm_encoder *encoder)
 +{
 +  return container_of(encoder, struct ingenic_drm_bridge, encoder);
 +}
 +
 static inline struct ingenic_drm_private_state *
 to_ingenic_drm_priv_state(struct drm_private_state *state)
 {
 @@ -679,11 +693,10 @@ static void 
ingenic_drm_encoder_atomic_mode_set(struct drm_encoder *encoder,

 {
struct ingenic_drm *priv = drm_device_get_priv(encoder->dev);
struct drm_display_mode *mode = _state->adjusted_mode;
 -  struct drm_connector *conn = conn_state->connector;
 -  struct drm_display_info *info = >display_info;
 +	struct ingenic_drm_bridge *bridge = 
to_ingenic_drm_bridge(encoder);

unsigned int cfg, rgbcfg = 0;

 -	priv->panel_is_sharp = info->bus_flags & 
DRM_BUS_FLAG_SHARP_SIGNALS;
 +	priv->panel_is_sharp = bridge->bus_cfg.flags & 
DRM_BUS_FLAG_SHARP_SIGNALS;


if (priv->panel_is_sharp) {
cfg = JZ_LCD_CFG_MODE_SPECIAL_TFT_1 | JZ_LCD_CFG_REV_POLARITY;
 @@ -696,19 +709,19 @@ static void 
ingenic_drm_encoder_atomic_mode_set(struct drm_encoder *encoder,

cfg |= JZ_LCD_CFG_HSYNC_ACTIVE_LOW;
if (mode->flags & DRM_MODE_FLAG_NVSYNC)
cfg |= JZ_LCD_CFG_VSYNC_ACTIVE_LOW;
 -  if (info->bus_flags & DRM_BUS_FLAG_DE_LOW)
 +  if (bridge->bus_cfg.flags & DRM_BUS_FLAG_DE_LOW)
cfg |= JZ_LCD_CFG_DE_ACTIVE_LOW;
 -  if (info->bus_flags & DRM_BUS_FLAG_PIXDATA_DRIVE_NEGEDGE)
 +  if (bridge->bus_cfg.flags & DRM_BUS_FLAG_PIXDATA_DRIVE_NEGEDGE)
cfg |= JZ_LCD_CFG_PCLK_FALLING_EDGE;

if (!priv->panel_is_sharp) {
 -  if (conn->connector_type == DRM_MODE_CONNECTOR_TV) {
 +		if (conn_state->connector->connector_type == 
DRM_MODE_CONNECTOR_TV) {

if (mode->flags & DRM_MODE_FLAG_INTERLACE)
cfg |= JZ_LCD_CFG_MODE_TV_OUT_I;
else
cfg |= JZ_LCD_CFG_MODE_TV_OUT_P;
} else {
 -  switch (*info->bus_formats) {
 +  switch (bridge->bus_cfg.format) {
case MEDIA_BUS_FMT_RGB565_1X16:
cfg |= JZ_LCD_CFG_MODE_GENERIC_16BIT;
break;
 @@ -734,20 +747,29 @@ static void 
ingenic_drm_encoder_atomic_mode_set(struct drm_encoder *encoder,

regmap_write(priv->map, JZ_REG_LCD_RGBC, rgbcfg);
 }

 -static int ingenic_drm_encoder_atomic_check(struct drm_encoder 
*encoder,

 -  struct drm_crtc_state *crtc_state,
 -  struct drm_connector_state 
*conn_state)
 +static int ingenic_drm_bridge_attach(struct drm_bridge *bridge,
 +   enum drm_bridge_attach_flags flags)
 +{
 +	struct ingenic_drm_bridge *ib = 
to_ingenic_drm_bridge(bridge->encoder);

 +
 +  return drm_bridge_attach(bridge->encoder, ib->next_bridge,
 +   >bridge, flags);
 +}
 +
 +static int ingenic_drm_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 drm_display_info *info = 
_state->connector->display_info;

struct drm_display_mode *mode = _state->adjusted_mode;
 +	struct ingenic_drm_bridge *ib = 
to_ingenic_drm_bridge(bridge->encoder);


 -  if (info->num_bus_formats != 1)
 -  

Re: [PATCH 8/8] drm/ingenic: Attach bridge chain to encoders

2021-08-08 Thread H. Nikolaus Schaller
Hi Paul,
all other patches apply cleanly but this one fails on top of v5.14-rc4.
What base are you using?
BR and thanks,
Nikolaus


> Am 08.08.2021 um 15:45 schrieb Paul Cercueil :
> 
> Attach a top-level bridge to each encoder, which will be used for
> negociating the bus format and flags.
> 
> All the bridges are now attached with DRM_BRIDGE_ATTACH_NO_CONNECTOR.
> 
> Signed-off-by: Paul Cercueil 
> ---
> drivers/gpu/drm/ingenic/ingenic-drm-drv.c | 92 +--
> 1 file changed, 70 insertions(+), 22 deletions(-)
> 
> diff --git a/drivers/gpu/drm/ingenic/ingenic-drm-drv.c 
> b/drivers/gpu/drm/ingenic/ingenic-drm-drv.c
> index 7ae48ead3ab6..09d5dd298078 100644
> --- a/drivers/gpu/drm/ingenic/ingenic-drm-drv.c
> +++ b/drivers/gpu/drm/ingenic/ingenic-drm-drv.c
> @@ -21,6 +21,7 @@
> #include 
> #include 
> #include 
> +#include 
> #include 
> #include 
> #include 
> @@ -107,6 +108,19 @@ struct ingenic_drm {
>   struct drm_private_obj private_obj;
> };
> 
> +struct ingenic_drm_bridge {
> + struct drm_encoder encoder;
> + struct drm_bridge bridge, *next_bridge;
> +
> + struct drm_bus_cfg bus_cfg;
> +};
> +
> +static inline struct ingenic_drm_bridge *
> +to_ingenic_drm_bridge(struct drm_encoder *encoder)
> +{
> + return container_of(encoder, struct ingenic_drm_bridge, encoder);
> +}
> +
> static inline struct ingenic_drm_private_state *
> to_ingenic_drm_priv_state(struct drm_private_state *state)
> {
> @@ -679,11 +693,10 @@ static void ingenic_drm_encoder_atomic_mode_set(struct 
> drm_encoder *encoder,
> {
>   struct ingenic_drm *priv = drm_device_get_priv(encoder->dev);
>   struct drm_display_mode *mode = _state->adjusted_mode;
> - struct drm_connector *conn = conn_state->connector;
> - struct drm_display_info *info = >display_info;
> + struct ingenic_drm_bridge *bridge = to_ingenic_drm_bridge(encoder);
>   unsigned int cfg, rgbcfg = 0;
> 
> - priv->panel_is_sharp = info->bus_flags & DRM_BUS_FLAG_SHARP_SIGNALS;
> + priv->panel_is_sharp = bridge->bus_cfg.flags & 
> DRM_BUS_FLAG_SHARP_SIGNALS;
> 
>   if (priv->panel_is_sharp) {
>   cfg = JZ_LCD_CFG_MODE_SPECIAL_TFT_1 | JZ_LCD_CFG_REV_POLARITY;
> @@ -696,19 +709,19 @@ static void ingenic_drm_encoder_atomic_mode_set(struct 
> drm_encoder *encoder,
>   cfg |= JZ_LCD_CFG_HSYNC_ACTIVE_LOW;
>   if (mode->flags & DRM_MODE_FLAG_NVSYNC)
>   cfg |= JZ_LCD_CFG_VSYNC_ACTIVE_LOW;
> - if (info->bus_flags & DRM_BUS_FLAG_DE_LOW)
> + if (bridge->bus_cfg.flags & DRM_BUS_FLAG_DE_LOW)
>   cfg |= JZ_LCD_CFG_DE_ACTIVE_LOW;
> - if (info->bus_flags & DRM_BUS_FLAG_PIXDATA_DRIVE_NEGEDGE)
> + if (bridge->bus_cfg.flags & DRM_BUS_FLAG_PIXDATA_DRIVE_NEGEDGE)
>   cfg |= JZ_LCD_CFG_PCLK_FALLING_EDGE;
> 
>   if (!priv->panel_is_sharp) {
> - if (conn->connector_type == DRM_MODE_CONNECTOR_TV) {
> + if (conn_state->connector->connector_type == 
> DRM_MODE_CONNECTOR_TV) {
>   if (mode->flags & DRM_MODE_FLAG_INTERLACE)
>   cfg |= JZ_LCD_CFG_MODE_TV_OUT_I;
>   else
>   cfg |= JZ_LCD_CFG_MODE_TV_OUT_P;
>   } else {
> - switch (*info->bus_formats) {
> + switch (bridge->bus_cfg.format) {
>   case MEDIA_BUS_FMT_RGB565_1X16:
>   cfg |= JZ_LCD_CFG_MODE_GENERIC_16BIT;
>   break;
> @@ -734,20 +747,29 @@ static void ingenic_drm_encoder_atomic_mode_set(struct 
> drm_encoder *encoder,
>   regmap_write(priv->map, JZ_REG_LCD_RGBC, rgbcfg);
> }
> 
> -static int ingenic_drm_encoder_atomic_check(struct drm_encoder *encoder,
> - struct drm_crtc_state *crtc_state,
> - struct drm_connector_state 
> *conn_state)
> +static int ingenic_drm_bridge_attach(struct drm_bridge *bridge,
> +  enum drm_bridge_attach_flags flags)
> +{
> + struct ingenic_drm_bridge *ib = to_ingenic_drm_bridge(bridge->encoder);
> +
> + return drm_bridge_attach(bridge->encoder, ib->next_bridge,
> +  >bridge, flags);
> +}
> +
> +static int ingenic_drm_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 drm_display_info *info = _state->connector->display_info;
>   struct drm_display_mode *mode = _state->adjusted_mode;
> + struct ingenic_drm_bridge *ib = to_ingenic_drm_bridge(bridge->encoder);
> 
> - if (info->num_bus_formats != 1)
> - return -EINVAL;
> + ib->bus_cfg = bridge_state->output_bus_cfg;
> 
>   if 

Re: [PATCH] drm/panel/tianma-tl057fvxp01: add panel for Motorola Moto G6

2021-08-08 Thread kernel test robot
Hi Julian,

Thank you for the patch! Yet something to improve:

[auto build test ERROR on linus/master]
[also build test ERROR on v5.14-rc4 next-20210806]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch]

url:
https://github.com/0day-ci/linux/commits/Julian-Braha/drm-panel-tianma-tl057fvxp01-add-panel-for-Motorola-Moto-G6/20210808-220609
base:   https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git 
85a90500f9a1717c4e142ce92e6c1cb1a339ec78
config: riscv-allyesconfig (attached as .config)
compiler: riscv64-linux-gcc (GCC) 10.3.0
reproduce (this is a W=1 build):
wget 
https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O 
~/bin/make.cross
chmod +x ~/bin/make.cross
# 
https://github.com/0day-ci/linux/commit/8df8493568160eb7a33041a8cf6e0058f3c868d4
git remote add linux-review https://github.com/0day-ci/linux
git fetch --no-tags linux-review 
Julian-Braha/drm-panel-tianma-tl057fvxp01-add-panel-for-Motorola-Moto-G6/20210808-220609
git checkout 8df8493568160eb7a33041a8cf6e0058f3c868d4
# save the attached .config to linux build tree
mkdir build_dir
COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-10.3.0 make.cross 
O=build_dir ARCH=riscv SHELL=/bin/bash

If you fix the issue, kindly add following tag as appropriate
Reported-by: kernel test robot 

All errors (new ones prefixed by >>):

>> drivers/gpu/drm/panel/panel-tianma-tl057fvxp01.c:15:10: fatal error: 
>> panel-mipi-dsi-common.h: No such file or directory
  15 | #include "panel-mipi-dsi-common.h"
 |  ^
   compilation terminated.


vim +15 drivers/gpu/drm/panel/panel-tianma-tl057fvxp01.c

14  
  > 15  #include "panel-mipi-dsi-common.h"
16  

---
0-DAY CI Kernel Test Service, Intel Corporation
https://lists.01.org/hyperkitty/list/kbuild-...@lists.01.org


.config.gz
Description: application/gzip


Re: [PATCH 2/8] drm/ingenic: Simplify code by using hwdescs array

2021-08-08 Thread Thomas Zimmermann

Hi

Am 08.08.21 um 15:45 schrieb Paul Cercueil:

Instead of having one 'hwdesc' variable for the plane #0 and one for the
plane #1, use a 'hwdesc[2]' array, where the DMA hardware descriptors
are indexed by the plane's number.

Signed-off-by: Paul Cercueil 
---
  drivers/gpu/drm/ingenic/ingenic-drm-drv.c | 38 ---
  1 file changed, 20 insertions(+), 18 deletions(-)

diff --git a/drivers/gpu/drm/ingenic/ingenic-drm-drv.c 
b/drivers/gpu/drm/ingenic/ingenic-drm-drv.c
index e42eb43d8020..bc71ba44ccf4 100644
--- a/drivers/gpu/drm/ingenic/ingenic-drm-drv.c
+++ b/drivers/gpu/drm/ingenic/ingenic-drm-drv.c
@@ -49,8 +49,7 @@ struct ingenic_dma_hwdesc {
  } __aligned(16);
  
  struct ingenic_dma_hwdescs {

-   struct ingenic_dma_hwdesc hwdesc_f0;
-   struct ingenic_dma_hwdesc hwdesc_f1;
+   struct ingenic_dma_hwdesc hwdesc[2];
struct ingenic_dma_hwdesc hwdesc_pal;
u16 palette[256] __aligned(16);
  };
@@ -141,6 +140,13 @@ static inline struct ingenic_drm *drm_nb_get_priv(struct 
notifier_block *nb)
return container_of(nb, struct ingenic_drm, clock_nb);
  }
  
+static inline dma_addr_t dma_hwdesc_addr(const struct ingenic_drm *priv, bool use_f1)


Using the plane index instead of a boolean would be more aligned to the 
way this function is being used.



+{
+   u32 offset = offsetof(struct ingenic_dma_hwdescs, hwdesc[use_f1]);


use_f1 is a function parameter. Is offsetof guaranteed to be evaluated 
at runtime?



+
+   return priv->dma_hwdescs_phys + offset;
+}
+
  static int ingenic_drm_update_pixclk(struct notifier_block *nb,
 unsigned long action,
 void *data)
@@ -562,6 +568,7 @@ static void ingenic_drm_plane_atomic_update(struct 
drm_plane *plane,
struct ingenic_dma_hwdesc *hwdesc;
unsigned int width, height, cpp, offset;
dma_addr_t addr;
+   bool use_f1;
u32 fourcc;
  
  	if (newstate && newstate->fb) {

@@ -569,16 +576,14 @@ static void ingenic_drm_plane_atomic_update(struct 
drm_plane *plane,
drm_fb_cma_sync_non_coherent(>drm, oldstate, 
newstate);
  
  		crtc_state = newstate->crtc->state;

+   use_f1 = priv->soc_info->has_osd && plane != >f0;
  
  		addr = drm_fb_cma_get_gem_addr(newstate->fb, newstate, 0);

width = newstate->src_w >> 16;
height = newstate->src_h >> 16;
cpp = newstate->fb->format->cpp[0];
  
-		if (!priv->soc_info->has_osd || plane == >f0)

-   hwdesc = >dma_hwdescs->hwdesc_f0;
-   else
-   hwdesc = >dma_hwdescs->hwdesc_f1;
+   hwdesc = >dma_hwdescs->hwdesc[use_f1];


Maybe add a helper that looks up the hwdesc field for a given plane?

  
  		hwdesc->addr = addr;

hwdesc->cmd = JZ_LCD_CMD_EOF_IRQ | (width * height * cpp / 4);
@@ -591,9 +596,9 @@ static void ingenic_drm_plane_atomic_update(struct 
drm_plane *plane,
if (fourcc == DRM_FORMAT_C8)
offset = offsetof(struct ingenic_dma_hwdescs, 
hwdesc_pal);
else
-   offset = offsetof(struct ingenic_dma_hwdescs, 
hwdesc_f0);
+   offset = offsetof(struct ingenic_dma_hwdescs, 
hwdesc[0]);
  
-			priv->dma_hwdescs->hwdesc_f0.next = priv->dma_hwdescs_phys + offset;

+   priv->dma_hwdescs->hwdesc[0].next = 
priv->dma_hwdescs_phys + offset;


Maybe priv->dma_hwdescs_phys + offset could be computed in a more 
flexible helper than dma_hwdesc_addr().


  
  			crtc_state->color_mgmt_changed = fourcc == DRM_FORMAT_C8;

}
@@ -964,20 +969,17 @@ static int ingenic_drm_bind(struct device *dev, bool 
has_components)
  
  
  	/* Configure DMA hwdesc for foreground0 plane */

-   dma_hwdesc_phys_f0 = priv->dma_hwdescs_phys
-   + offsetof(struct ingenic_dma_hwdescs, hwdesc_f0);
-   priv->dma_hwdescs->hwdesc_f0.next = dma_hwdesc_phys_f0;
-   priv->dma_hwdescs->hwdesc_f0.id = 0xf0;
+   dma_hwdesc_phys_f0 = dma_hwdesc_addr(priv, 0);
+   priv->dma_hwdescs->hwdesc[0].next = dma_hwdesc_phys_f0;
+   priv->dma_hwdescs->hwdesc[0].id = 0xf0;
  
  	/* Configure DMA hwdesc for foreground1 plane */

-   dma_hwdesc_phys_f1 = priv->dma_hwdescs_phys
-   + offsetof(struct ingenic_dma_hwdescs, hwdesc_f1);
-   priv->dma_hwdescs->hwdesc_f1.next = dma_hwdesc_phys_f1;
-   priv->dma_hwdescs->hwdesc_f1.id = 0xf1;
+   dma_hwdesc_phys_f1 = dma_hwdesc_addr(priv, 1);
+   priv->dma_hwdescs->hwdesc[1].next = dma_hwdesc_phys_f1;
+   priv->dma_hwdescs->hwdesc[1].id = 0xf1;
  
  	/* Configure DMA hwdesc for palette */

-   priv->dma_hwdescs->hwdesc_pal.next = priv->dma_hwdescs_phys
-   + offsetof(struct ingenic_dma_hwdescs, hwdesc_f0);
+   priv->dma_hwdescs->hwdesc_pal.next = dma_hwdesc_phys_f0;

Re: [PATCH 1/8] drm/ingenic: Remove dead code

2021-08-08 Thread Paul Cercueil

Hi Joe,

Le dim., août 8 2021 at 11:27:34 -0700, Joe Perches  
a écrit :

On Sun, 2021-08-08 at 19:58 +0200, Thomas Zimmermann wrote:


 Am 08.08.21 um 15:45 schrieb Paul Cercueil:
 > The priv->ipu_plane would get a different value further down the 
code,

 > without the first assigned value being read first; so the first
 > assignation can be dropped.
 >
 > Signed-off-by: Paul Cercueil 

 Acked-by: Thomas Zimmermann 


I think this is at best an incomplete description.

How is it known that this priv->ipu_plane assignment isn't
necessary for any path of any failure path after this assignment
and before the new assignment?


It is only used in the .atomic_begin and .atomic_check callbacks of the 
CRTC. These will only ever be called after the call to 
drm_dev_register() which happens at the end of the probe function.


Cheers,
-Paul

 > diff --git a/drivers/gpu/drm/ingenic/ingenic-drm-drv.c 
b/drivers/gpu/drm/ingenic/ingenic-drm-drv.c

[]
 > @@ -984,9 +984,6 @@ static int ingenic_drm_bind(struct device 
*dev, bool has_components)

 >   priv->dma_hwdescs->hwdesc_pal.cmd = JZ_LCD_CMD_ENABLE_PAL
 >   | (sizeof(priv->dma_hwdescs->palette) / 4);
 >
 > - if (soc_info->has_osd)
 > - priv->ipu_plane = drm_plane_from_index(drm, 0);
 > -
 >   primary = priv->soc_info->has_osd ? >f1 : >f0;
 >
 >   drm_plane_helper_add(primary, _drm_plane_helper_funcs);
 >









Re: [PATCH 1/8] drm/ingenic: Remove dead code

2021-08-08 Thread Joe Perches
On Sun, 2021-08-08 at 19:58 +0200, Thomas Zimmermann wrote:
> 
> Am 08.08.21 um 15:45 schrieb Paul Cercueil:
> > The priv->ipu_plane would get a different value further down the code,
> > without the first assigned value being read first; so the first
> > assignation can be dropped.
> > 
> > Signed-off-by: Paul Cercueil 
> 
> Acked-by: Thomas Zimmermann 

I think this is at best an incomplete description.

How is it known that this priv->ipu_plane assignment isn't
necessary for any path of any failure path after this assignment
and before the new assignment?

> > diff --git a/drivers/gpu/drm/ingenic/ingenic-drm-drv.c 
> > b/drivers/gpu/drm/ingenic/ingenic-drm-drv.c
[]
> > @@ -984,9 +984,6 @@ static int ingenic_drm_bind(struct device *dev, bool 
> > has_components)
> >     priv->dma_hwdescs->hwdesc_pal.cmd = JZ_LCD_CMD_ENABLE_PAL
> >     | (sizeof(priv->dma_hwdescs->palette) / 4);
> > 
> > -   if (soc_info->has_osd)
> > -   priv->ipu_plane = drm_plane_from_index(drm, 0);
> > -
> >     primary = priv->soc_info->has_osd ? >f1 : >f0;
> > 
> >     drm_plane_helper_add(primary, _drm_plane_helper_funcs);
> > 
> 




Re: [PATCH 1/8] drm/ingenic: Remove dead code

2021-08-08 Thread Thomas Zimmermann



Am 08.08.21 um 15:45 schrieb Paul Cercueil:

The priv->ipu_plane would get a different value further down the code,
without the first assigned value being read first; so the first
assignation can be dropped.

Signed-off-by: Paul Cercueil 


Acked-by: Thomas Zimmermann 


---
  drivers/gpu/drm/ingenic/ingenic-drm-drv.c | 3 ---
  1 file changed, 3 deletions(-)

diff --git a/drivers/gpu/drm/ingenic/ingenic-drm-drv.c 
b/drivers/gpu/drm/ingenic/ingenic-drm-drv.c
index d261f7a03b18..e42eb43d8020 100644
--- a/drivers/gpu/drm/ingenic/ingenic-drm-drv.c
+++ b/drivers/gpu/drm/ingenic/ingenic-drm-drv.c
@@ -984,9 +984,6 @@ static int ingenic_drm_bind(struct device *dev, bool 
has_components)
priv->dma_hwdescs->hwdesc_pal.cmd = JZ_LCD_CMD_ENABLE_PAL
| (sizeof(priv->dma_hwdescs->palette) / 4);
  
-	if (soc_info->has_osd)

-   priv->ipu_plane = drm_plane_from_index(drm, 0);
-
primary = priv->soc_info->has_osd ? >f1 : >f0;
  
  	drm_plane_helper_add(primary, _drm_plane_helper_funcs);




--
Thomas Zimmermann
Graphics Driver Developer
SUSE Software Solutions Germany GmbH
Maxfeldstr. 5, 90409 Nürnberg, Germany
(HRB 36809, AG Nürnberg)
Geschäftsführer: Felix Imendörffer



OpenPGP_signature
Description: OpenPGP digital signature


[PATCH 3/3] drm/i915/selftests: Add initial GuC selftest for scrubbing lost G2H

2021-08-08 Thread Matthew Brost
While debugging an issue with full GT resets I went down a rabbit hole
thinking the scrubbing of lost G2H wasn't working correctly. This proved
to be incorrect as this was working just fine but this chase inspired me
to write a selftest to prove that this works. This simple selftest
injects errors dropping various G2H and then issues a full GT reset
proving that the scrubbing of these G2H doesn't blow up.

Signed-off-by: Matthew Brost 
---
 drivers/gpu/drm/i915/gt/intel_context_types.h |   4 +
 .../gpu/drm/i915/gt/uc/intel_guc_submission.c |  18 +++
 drivers/gpu/drm/i915/gt/uc/selftest_guc.c | 126 ++
 .../drm/i915/selftests/i915_live_selftests.h  |   1 +
 .../i915/selftests/intel_scheduler_helpers.c  |  12 ++
 .../i915/selftests/intel_scheduler_helpers.h  |   2 +
 6 files changed, 163 insertions(+)
 create mode 100644 drivers/gpu/drm/i915/gt/uc/selftest_guc.c

diff --git a/drivers/gpu/drm/i915/gt/intel_context_types.h 
b/drivers/gpu/drm/i915/gt/intel_context_types.h
index e54351a170e2..fec5ff7ef168 100644
--- a/drivers/gpu/drm/i915/gt/intel_context_types.h
+++ b/drivers/gpu/drm/i915/gt/intel_context_types.h
@@ -198,6 +198,10 @@ struct intel_context {
 */
u8 guc_prio;
u32 guc_prio_count[GUC_CLIENT_PRIORITY_NUM];
+
+   I915_SELFTEST_DECLARE(bool drop_schedule_enable);
+   I915_SELFTEST_DECLARE(bool drop_schedule_disable);
+   I915_SELFTEST_DECLARE(bool drop_deregister);
 };
 
 #endif /* __INTEL_CONTEXT_TYPES__ */
diff --git a/drivers/gpu/drm/i915/gt/uc/intel_guc_submission.c 
b/drivers/gpu/drm/i915/gt/uc/intel_guc_submission.c
index cd8df078ca87..d13dc56bae43 100644
--- a/drivers/gpu/drm/i915/gt/uc/intel_guc_submission.c
+++ b/drivers/gpu/drm/i915/gt/uc/intel_guc_submission.c
@@ -2618,6 +2618,11 @@ int intel_guc_deregister_done_process_msg(struct 
intel_guc *guc,
 
trace_intel_context_deregister_done(ce);
 
+   if (I915_SELFTEST_ONLY(ce->drop_deregister)) {
+   I915_SELFTEST_DECLARE(ce->drop_deregister = false;)
+   return 0;
+   }
+
if (context_wait_for_deregister_to_register(ce)) {
struct intel_runtime_pm *runtime_pm =
>engine->gt->i915->runtime_pm;
@@ -2672,10 +2677,19 @@ int intel_guc_sched_done_process_msg(struct intel_guc 
*guc,
trace_intel_context_sched_done(ce);
 
if (context_pending_enable(ce)) {
+   if (I915_SELFTEST_ONLY(ce->drop_schedule_enable)) {
+   I915_SELFTEST_DECLARE(ce->drop_schedule_enable = false;)
+   return 0;
+   }
clr_context_pending_enable(ce);
} else if (context_pending_disable(ce)) {
bool banned;
 
+   if (I915_SELFTEST_ONLY(ce->drop_schedule_disable)) {
+   I915_SELFTEST_DECLARE(ce->drop_schedule_disable = 
false;)
+   return 0;
+   }
+
/*
 * Unpin must be done before __guc_signal_context_fence,
 * otherwise a race exists between the requests getting
@@ -3047,3 +3061,7 @@ bool intel_guc_virtual_engine_has_heartbeat(const struct 
intel_engine_cs *ve)
 
return false;
 }
+
+#if IS_ENABLED(CONFIG_DRM_I915_SELFTEST)
+#include "selftest_guc.c"
+#endif
diff --git a/drivers/gpu/drm/i915/gt/uc/selftest_guc.c 
b/drivers/gpu/drm/i915/gt/uc/selftest_guc.c
new file mode 100644
index ..46ca6554f65d
--- /dev/null
+++ b/drivers/gpu/drm/i915/gt/uc/selftest_guc.c
@@ -0,0 +1,126 @@
+// SPDX-License-Identifier: MIT
+/*
+ * Copyright �� 2021 Intel Corporation
+ */
+
+#include "selftests/intel_scheduler_helpers.h"
+
+static struct i915_request *nop_user_request(struct intel_context *ce,
+struct i915_request *from)
+{
+   struct i915_request *rq;
+   int ret;
+
+   rq = intel_context_create_request(ce);
+   if (IS_ERR(rq))
+   return rq;
+
+   if (from) {
+   ret = i915_sw_fence_await_dma_fence(>submit,
+   >fence, 0,
+   I915_FENCE_GFP);
+   if (ret < 0) {
+   i915_request_put(rq);
+   return ERR_PTR(ret);
+   }
+   }
+
+   i915_request_get(rq);
+   i915_request_add(rq);
+
+   return rq;
+}
+
+static int intel_guc_scrub_ctbs(void *arg)
+{
+   struct intel_gt *gt = arg;
+   int ret = 0;
+   int i;
+   struct i915_request *last[3] = {NULL, NULL, NULL}, *rq;
+   intel_wakeref_t wakeref;
+   struct intel_engine_cs *engine;
+   struct intel_context *ce;
+
+   wakeref = intel_runtime_pm_get(gt->uncore->rpm);
+   engine = intel_selftest_find_any_engine(gt);
+
+   /* Submit requests and inject errors forcing G2H to be dropped */
+   for (i = 0; i < 3; ++i) {
+   ce = intel_context_create(engine);
+

[PATCH 2/3] drm/i915/selftests: Fix memory corruption in live_lrc_isolation

2021-08-08 Thread Matthew Brost
GuC submission has exposed an existing memory corruption in
live_lrc_isolation. We believe that some writes to the watchdog offsets
in the LRC (0x178 & 0x17c) can result in trashing of portions of the
address space. With GuC submission there are additional objects which
can move the context redzone into the space that is trashed. To
workaround this avoid poisoning the watchdog.

Signed-off-by: Matthew Brost 
---
 drivers/gpu/drm/i915/gt/selftest_lrc.c | 29 +-
 1 file changed, 28 insertions(+), 1 deletion(-)

diff --git a/drivers/gpu/drm/i915/gt/selftest_lrc.c 
b/drivers/gpu/drm/i915/gt/selftest_lrc.c
index b0977a3b699b..6500e9fce8a0 100644
--- a/drivers/gpu/drm/i915/gt/selftest_lrc.c
+++ b/drivers/gpu/drm/i915/gt/selftest_lrc.c
@@ -1074,6 +1074,32 @@ record_registers(struct intel_context *ce,
goto err_after;
 }
 
+static u32 safe_offset(u32 offset, u32 reg)
+{
+   /* XXX skip testing of watchdog */
+   if (offset == 0x178 || offset == 0x17c)
+   reg = 0;
+
+   return reg;
+}
+
+static int get_offset_mask(struct intel_engine_cs *engine)
+{
+   if (GRAPHICS_VER(engine->i915) < 12)
+   return 0xfff;
+
+   switch (engine->class) {
+   default:
+   case RENDER_CLASS:
+   return 0x07ff;
+   case COPY_ENGINE_CLASS:
+   return 0x0fff;
+   case VIDEO_DECODE_CLASS:
+   case VIDEO_ENHANCEMENT_CLASS:
+   return 0x3fff;
+   }
+}
+
 static struct i915_vma *load_context(struct intel_context *ce, u32 poison)
 {
struct i915_vma *batch;
@@ -1117,7 +1143,8 @@ static struct i915_vma *load_context(struct intel_context 
*ce, u32 poison)
len = (len + 1) / 2;
*cs++ = MI_LOAD_REGISTER_IMM(len);
while (len--) {
-   *cs++ = hw[dw];
+   *cs++ = safe_offset(hw[dw] & 
get_offset_mask(ce->engine),
+   hw[dw]);
*cs++ = poison;
dw += 2;
}
-- 
2.28.0



[PATCH 0/3] Clean up some CI failures for GuC submission

2021-08-08 Thread Matthew Brost
Address CI failure related to reset [1]. In addition to the public CI
failure we also fix several issues uncovered recenting in our internal
CI related to resets. Patch #1 address all of these issues.

Workaround an existing memory corruption, in gt_lrc selftest, exposed by
enabling GuC submission [2].

Lastly, add a selftest to give us confidence in some of the reset code
that is rather hard / intermittent to exercise via IGTs.

Signed-off-by: Matthew Brost 

[1] 
https://intel-gfx-ci.01.org/tree/drm-tip/CI_DRM_10456/fi-rkl-guc/igt@i915_selftest@l...@workarounds.html
[2] 
https://intel-gfx-ci.01.org/tree/drm-tip/Patchwork_20772/fi-rkl-guc/igt@i915_selftest@live@gt_lrc.html

Matthew Brost (3):
  drm/i915/guc: Fix several issues related to resets / request
cancelation
  drm/i915/selftests: Fix memory corruption in live_lrc_isolation
  drm/i915/selftests: Add initial GuC selftest for scrubbing lost G2H

 drivers/gpu/drm/i915/gt/intel_context_types.h |   4 +
 drivers/gpu/drm/i915/gt/selftest_lrc.c|  29 +++-
 .../gpu/drm/i915/gt/uc/intel_guc_submission.c |  61 ++---
 drivers/gpu/drm/i915/gt/uc/selftest_guc.c | 126 ++
 .../drm/i915/selftests/i915_live_selftests.h  |   1 +
 .../i915/selftests/intel_scheduler_helpers.c  |  12 ++
 .../i915/selftests/intel_scheduler_helpers.h  |   2 +
 7 files changed, 218 insertions(+), 17 deletions(-)
 create mode 100644 drivers/gpu/drm/i915/gt/uc/selftest_guc.c

-- 
2.28.0



[PATCH 1/3] drm/i915/guc: Fix several issues related to resets / request cancelation

2021-08-08 Thread Matthew Brost
Resets are notoriously hard to get fully working and notoriously racey,
especially with selftests / IGTs that do all sorts of wild things that
would be near impossible to hit during normal use cases. Even though
likely impossible to hit, anything selftests / IGTs uncover needs to be
fixed. This patch addresses 7 such issues.

1. A small race that could result in incorrect accounting of the number
of outstanding G2H. Basically prior to this patch we did not increment
the number of outstanding G2H if we encoutered a GT reset while sending
a H2G. This was incorrect as the context state had already been updated
to anticipate a G2H response thus the counter should be incremented.

2. When unwinding requests on a reset context, if other requests in the
context are in the priority list the requests could be resubmitted out
of seqno order. Traverse the list of active requests in reserve and
append to the head of the priority list to fix this.

3. Don't drop ce->guc_active.lock when unwinding a context after reset.
At one point we had to drop this because of a lock inversion but that is
no longer the case. It is much safer to hold the lock so let's do that.

4. Prior to this patch the blocked context counter was cleared on
init_sched_state (used during registering a context & resets) which is
incorrect. This state needs to be persistent or the counter can read the
incorrect value.

5. Flush the work queue for GuC generated G2H messages during a GT reset.

6. Do not clear enable during a context reset if a schedule enable is in
flight.

7. When unblocking a context, do not enable scheduling if the context is
banned.

Fixes: f4eb1f3fe946 ("drm/i915/guc: Ensure G2H response has space in buffer")
Fixes: 62eaf0ae217d ("drm/i915/guc: Support request cancellation")
Fixes: eb5e7da736f3 ("drm/i915/guc: Reset implementation for new GuC interface")
Signed-off-by: Matthew Brost 
Cc: 
---
 .../gpu/drm/i915/gt/uc/intel_guc_submission.c | 43 ---
 1 file changed, 27 insertions(+), 16 deletions(-)

diff --git a/drivers/gpu/drm/i915/gt/uc/intel_guc_submission.c 
b/drivers/gpu/drm/i915/gt/uc/intel_guc_submission.c
index 87d8dc8f51b9..cd8df078ca87 100644
--- a/drivers/gpu/drm/i915/gt/uc/intel_guc_submission.c
+++ b/drivers/gpu/drm/i915/gt/uc/intel_guc_submission.c
@@ -152,7 +152,7 @@ static inline void init_sched_state(struct intel_context 
*ce)
 {
/* Only should be called from guc_lrc_desc_pin() */
atomic_set(>guc_sched_state_no_lock, 0);
-   ce->guc_state.sched_state = 0;
+   ce->guc_state.sched_state &= SCHED_STATE_BLOCKED_MASK;
 }
 
 static inline bool
@@ -360,11 +360,13 @@ static int guc_submission_send_busy_loop(struct intel_guc 
*guc,
 {
int err;
 
-   err = intel_guc_send_busy_loop(guc, action, len, g2h_len_dw, loop);
-
-   if (!err && g2h_len_dw)
+   if (g2h_len_dw)
atomic_inc(>outstanding_submission_g2h);
 
+   err = intel_guc_send_busy_loop(guc, action, len, g2h_len_dw, loop);
+   if (err == -EBUSY && g2h_len_dw)
+   atomic_dec(>outstanding_submission_g2h);
+
return err;
 }
 
@@ -725,6 +727,11 @@ void intel_guc_submission_reset_prepare(struct intel_guc 
*guc)
wait_for_reset(guc, >outstanding_submission_g2h);
} while (!list_empty(>ct.requests.incoming));
}
+
+   /* Flush any GuC generated G2H */
+   while (!list_empty(>ct.requests.incoming))
+   msleep(1);
+
scrub_guc_desc_for_outstanding_g2h(guc);
 }
 
@@ -797,14 +804,13 @@ __unwind_incomplete_requests(struct intel_context *ce)
 
spin_lock_irqsave(_engine->lock, flags);
spin_lock(>guc_active.lock);
-   list_for_each_entry_safe(rq, rn,
->guc_active.requests,
-sched.link) {
+   list_for_each_entry_safe_reverse(rq, rn,
+>guc_active.requests,
+sched.link) {
if (i915_request_completed(rq))
continue;
 
list_del_init(>sched.link);
-   spin_unlock(>guc_active.lock);
 
__i915_request_unsubmit(rq);
 
@@ -816,10 +822,8 @@ __unwind_incomplete_requests(struct intel_context *ce)
}
GEM_BUG_ON(i915_sched_engine_is_empty(sched_engine));
 
-   list_add_tail(>sched.link, pl);
+   list_add(>sched.link, pl);
set_bit(I915_FENCE_FLAG_PQUEUE, >fence.flags);
-
-   spin_lock(>guc_active.lock);
}
spin_unlock(>guc_active.lock);
spin_unlock_irqrestore(_engine->lock, flags);
@@ -828,17 +832,23 @@ __unwind_incomplete_requests(struct intel_context *ce)
 static void __guc_reset_context(struct intel_context *ce, bool stalled)
 {
struct i915_request *rq;
+   unsigned long flags;
u32 head;
 
intel_context_get(ce);
 
/*
-* GuC will 

Re: [PATCH] drm/msm: Disable frequency clamping on a630

2021-08-08 Thread Rob Clark
On Sun, Aug 8, 2021 at 7:33 AM Caleb Connolly  wrote:
>
>
>
> On 07/08/2021 21:04, Rob Clark wrote:
> > On Sat, Aug 7, 2021 at 12:21 PM Caleb Connolly
> >  wrote:
> >>
> >> Hi Rob, Akhil,
> >>
> >> On 29/07/2021 21:53, Rob Clark wrote:
> >>> On Thu, Jul 29, 2021 at 1:28 PM Caleb Connolly
> >>>  wrote:
> 
> 
> 
>  On 29/07/2021 21:24, Rob Clark wrote:
> > On Thu, Jul 29, 2021 at 1:06 PM Caleb Connolly
> >  wrote:
> >>
> >> Hi Rob,
> >>
> >> I've done some more testing! It looks like before that patch 
> >> ("drm/msm: Devfreq tuning") the GPU would never get above
> >> the second frequency in the OPP table (342MHz) (at least, not in 
> >> glxgears). With the patch applied it would more
> >> aggressively jump up to the max frequency which seems to be unstable 
> >> at the default regulator voltages.
> >
> > *ohh*, yeah, ok, that would explain it
> >
> >> Hacking the pm8005 s1 regulator (which provides VDD_GFX) up to 0.988v 
> >> (instead of the stock 0.516v) makes the GPU stable
> >> at the higher frequencies.
> >>
> >> Applying this patch reverts the behaviour, and the GPU never goes 
> >> above 342MHz in glxgears, losing ~30% performance in
> >> glxgear.
> >>
> >> I think (?) that enabling CPR support would be the proper solution to 
> >> this - that would ensure that the regulators run
> >> at the voltage the hardware needs to be stable.
> >>
> >> Is hacking the voltage higher (although ideally not quite that high) 
> >> an acceptable short term solution until we have
> >> CPR? Or would it be safer to just not make use of the higher 
> >> frequencies on a630 for now?
> >>
> >
> > tbh, I'm not sure about the regulator stuff and CPR.. Bjorn is already
> > on CC and I added sboyd, maybe one of them knows better.
> >
> > In the short term, removing the higher problematic OPPs from dts might
> > be a better option than this patch (which I'm dropping), since there
> > is nothing stopping other workloads from hitting higher OPPs.
>  Oh yeah that sounds like a more sensible workaround than mine .
> >
> > I'm slightly curious why I didn't have problems at higher OPPs on my
> > c630 laptop (sdm850)
>  Perhaps you won the sillicon lottery - iirc sdm850 is binned for higher 
>  clocks as is out of the factory.
> 
>  Would it be best to drop the OPPs for all devices? Or just those 
>  affected? I guess it's possible another c630 might
>  crash where yours doesn't?
> >>>
> >>> I've not heard any reports of similar issues from the handful of other
> >>> folks with c630's on #aarch64-laptops.. but I can't really say if that
> >>> is luck or not.
> >> It looks like this affects at least the OnePlus 6 and PocoPhone F1, I've 
> >> done some more poking and the following diff
> >> seems to fix the stability issues completely, it seems the delay is 
> >> required to let the update propagate.
> >>
> >> This doesn't feel like the right fix, but hopefully it's enough to come up 
> >> with a better solution than disabling the new
> >> devfreq behaviour on a630.
> >>
> >> diff --git a/drivers/gpu/drm/msm/adreno/a6xx_gmu.c 
> >> b/drivers/gpu/drm/msm/adreno/a6xx_gmu.c
> >> index d7cec7f0dde0..69e2a5e84dae 100644
> >> --- a/drivers/gpu/drm/msm/adreno/a6xx_gmu.c
> >> +++ b/drivers/gpu/drm/msm/adreno/a6xx_gmu.c
> >> @@ -139,6 +139,10 @@ void a6xx_gmu_set_freq(struct msm_gpu *gpu, struct 
> >> dev_pm_opp *opp)
> >>   return;
> >>   }
> >>
> >> +   dev_pm_opp_set_opp(>pdev->dev, opp);
> >> +
> >> +   usleep_range(300, 500);
> >> +
> >
> > Hmm, this is going to be in the critical path on idle -> active
> > transition (ie. think response time to user-input).. so we defn don't
> > want to do this unconditionally..
> >
> > If I understand the problem, we just want to limit how far we jump the
> > gpu freq in one go.. maybe deleting the lowest (and perhaps highest)
> > OPP would accomplish that?  Could that be done in the board(s)'s
> > toplevel dts files?
> That would be a workaround, however I'd really like to avoid limiting 
> performance as a solution if I can help it,
> especially as the fix might just be "set the opp first, wait for it to apply, 
> then set the core clock".
>
> Is there a sensible way to get a callback from the opp notify chain? Or from 
> rpmh directly? Or is this solution really
> not the right way to go?

It does seem a bit strange to me that we are telling GMU to change
freq before calling dev_pm_opp_set_opp()..  if dev_pm_opp_set_opp() is
increasing voltage, it seems like you'd want to do that *before*
increasing freq (but reverse the order when decreasing freq).. But I'm
not an expert on the ways of the GMU..  maybe Akhil or Jordan knows
better how this is supposed to work.

But the delay seems like papering something over, and I'm trying to go
in the other direction and reduce 

Re: [PATCH 2/2] drm/panel: Add Truly NT35521 panel driver

2021-08-08 Thread Stephan Gerhold
On Sun, Aug 08, 2021 at 09:44:57PM +0800, Shawn Guo wrote:
> On Wed, Aug 04, 2021 at 02:09:19PM +0200, Stephan Gerhold wrote:
> > On Wed, Aug 04, 2021 at 04:13:52PM +0800, Shawn Guo wrote:
> > > + ...
> > > + nt_dcs_write(0xb1, 0x6c, 0x21);
> > > + nt_dcs_write(0xf0, 0x55, 0xaa, 0x52, 0x00, 0x00);
> > > + nt_dcs_write(0x35, 0x00);
> > > + nt_gen_write(0x11, 0x00);
> > > + msleep(120);
> > > + nt_gen_write(0x29, 0x00);
> > > + usleep_range(1000, 1500);
> > > + nt_dcs_write(0x53, 0x24);
> > 
> > Did you mix up "nt_dcs_write" and "nt_gen_write" here?
> > The nt_gen_write(0x11, 0x00); looks like MIPI_DCS_EXIT_SLEEP_MODE
> > and the nt_gen_write(0x29, 0x00); looks like MIPI_DCS_SET_DISPLAY_ON.
> > 
> > For reference you can pull your original reference DTB from Sony through
> > my panel driver generator: 
> > https://github.com/msm8916-mainline/linux-mdss-dsi-panel-driver-generator
> 
> Wow, very nice!  It really deserves wider spread!
> 
> > 
> > It produces the following (I compiled "msm8939-kanuti_tulip.dtb"
> > from https://github.com/sonyxperiadev/kernel/tree/aosp/LA.BR.1.3.3_rb2.14,
> > not sure if that is right):
> > 
> > // ...
> > dsi_generic_write_seq(dsi, 0x35, 0x00);
> > 
> > ret = mipi_dsi_dcs_exit_sleep_mode(dsi);
> > if (ret < 0) {
> > dev_err(dev, "Failed to exit sleep mode: %d\n", ret);
> > return ret;
> > }
> > msleep(120);
> > 
> > ret = mipi_dsi_dcs_set_display_on(dsi);
> > if (ret < 0) {
> > dev_err(dev, "Failed to set display on: %d\n", ret);
> > return ret;
> > }
> > usleep_range(1000, 2000);
> > 
> > dsi_generic_write_seq(dsi, 0x53, 0x24);
> > 
> > Which also suggests that generic and DCS writes are mixed up here.
> > 
> > Note however that you could not use the generated driver as-is,
> > because Sony seems to use their own display driver instead of Qualcomm's
> > and some things seem to be different.
> 
> I re-created the driver using your generator.  With modeling the 5v
> control GPIOs with regulators and adding backlight-gpios support, the
> driver works quite nicely, except the following two problems:
> 
> 1) I have to drop the MIPI_DSI_MODE_LPM configuration from .update_status
>hook. Otherwise brightness did not get updated to panel.
> 
> ---8<--
> diff --git a/drivers/gpu/drm/panel/panel-sony-tulip-truly-nt35521.c 
> b/drivers/gpu/drm/panel/panel-sony-tulip-truly-nt35521.c
> index 31e5f942a039..eba926c6f722 100644
> --- a/drivers/gpu/drm/panel/panel-sony-tulip-truly-nt35521.c
> +++ b/drivers/gpu/drm/panel/panel-sony-tulip-truly-nt35521.c
> @@ -420,33 +420,23 @@ static int truly_nt35521_bl_update_status(struct 
> backlight_device *bl)
> u16 brightness = backlight_get_brightness(bl);
> int ret;
>  
> -   dsi->mode_flags &= ~MIPI_DSI_MODE_LPM;
> -
> ret = mipi_dsi_dcs_set_display_brightness(dsi, brightness);
> if (ret < 0)
> return ret;
>  
> -   dsi->mode_flags |= MIPI_DSI_MODE_LPM;
> -
> return 0;
>  }
> -->8---
> 

I have to admit I don't know much about Low Power Mode vs High Speed
Mode. As long it works it is good I guess :-)

> 2) The driver works good, if the kernel is launched via "fastboot boot".
>But if the kernel is flashed to eMMC and launched by bootloader with
>splash screen, kernel will fail to bring up the panel.  After kernel
>boots up, a blank & unblank cycle can get panel work though.
> 
> The problem 2) is not driver generator related.  @Konrad, did you see
> it on asus-z00t-tm5p5-n35596 driver?
> 

Do you have CONFIG_DRM_MSM=y (built-in) instead of =m (module) maybe?
I think a similar issue exists on MSM8916 but it does not happen
for some reason if CONFIG_DRM_MSM=m instead of =y. Somehow having it
load later during the boot process fixes some things there.

Thanks,
Stephan


Re: [PATCH] drm/msm: Disable frequency clamping on a630

2021-08-08 Thread Caleb Connolly




On 07/08/2021 21:04, Rob Clark wrote:

On Sat, Aug 7, 2021 at 12:21 PM Caleb Connolly
 wrote:


Hi Rob, Akhil,

On 29/07/2021 21:53, Rob Clark wrote:

On Thu, Jul 29, 2021 at 1:28 PM Caleb Connolly
 wrote:




On 29/07/2021 21:24, Rob Clark wrote:

On Thu, Jul 29, 2021 at 1:06 PM Caleb Connolly
 wrote:


Hi Rob,

I've done some more testing! It looks like before that patch ("drm/msm: Devfreq 
tuning") the GPU would never get above
the second frequency in the OPP table (342MHz) (at least, not in glxgears). 
With the patch applied it would more
aggressively jump up to the max frequency which seems to be unstable at the 
default regulator voltages.


*ohh*, yeah, ok, that would explain it


Hacking the pm8005 s1 regulator (which provides VDD_GFX) up to 0.988v (instead 
of the stock 0.516v) makes the GPU stable
at the higher frequencies.

Applying this patch reverts the behaviour, and the GPU never goes above 342MHz 
in glxgears, losing ~30% performance in
glxgear.

I think (?) that enabling CPR support would be the proper solution to this - 
that would ensure that the regulators run
at the voltage the hardware needs to be stable.

Is hacking the voltage higher (although ideally not quite that high) an 
acceptable short term solution until we have
CPR? Or would it be safer to just not make use of the higher frequencies on 
a630 for now?



tbh, I'm not sure about the regulator stuff and CPR.. Bjorn is already
on CC and I added sboyd, maybe one of them knows better.

In the short term, removing the higher problematic OPPs from dts might
be a better option than this patch (which I'm dropping), since there
is nothing stopping other workloads from hitting higher OPPs.

Oh yeah that sounds like a more sensible workaround than mine .


I'm slightly curious why I didn't have problems at higher OPPs on my
c630 laptop (sdm850)

Perhaps you won the sillicon lottery - iirc sdm850 is binned for higher clocks 
as is out of the factory.

Would it be best to drop the OPPs for all devices? Or just those affected? I 
guess it's possible another c630 might
crash where yours doesn't?


I've not heard any reports of similar issues from the handful of other
folks with c630's on #aarch64-laptops.. but I can't really say if that
is luck or not.

It looks like this affects at least the OnePlus 6 and PocoPhone F1, I've done 
some more poking and the following diff
seems to fix the stability issues completely, it seems the delay is required to 
let the update propagate.

This doesn't feel like the right fix, but hopefully it's enough to come up with 
a better solution than disabling the new
devfreq behaviour on a630.

diff --git a/drivers/gpu/drm/msm/adreno/a6xx_gmu.c 
b/drivers/gpu/drm/msm/adreno/a6xx_gmu.c
index d7cec7f0dde0..69e2a5e84dae 100644
--- a/drivers/gpu/drm/msm/adreno/a6xx_gmu.c
+++ b/drivers/gpu/drm/msm/adreno/a6xx_gmu.c
@@ -139,6 +139,10 @@ void a6xx_gmu_set_freq(struct msm_gpu *gpu, struct 
dev_pm_opp *opp)
  return;
  }

+   dev_pm_opp_set_opp(>pdev->dev, opp);
+
+   usleep_range(300, 500);
+


Hmm, this is going to be in the critical path on idle -> active
transition (ie. think response time to user-input).. so we defn don't
want to do this unconditionally..

If I understand the problem, we just want to limit how far we jump the
gpu freq in one go.. maybe deleting the lowest (and perhaps highest)
OPP would accomplish that?  Could that be done in the board(s)'s
toplevel dts files?
That would be a workaround, however I'd really like to avoid limiting performance as a solution if I can help it, 
especially as the fix might just be "set the opp first, wait for it to apply, then set the core clock".


Is there a sensible way to get a callback from the opp notify chain? Or from rpmh directly? Or is this solution really 
not the right way to go?


BR,
-R


  gmu_write(gmu, REG_A6XX_GMU_DCVS_ACK_OPTION, 0);

  gmu_write(gmu, REG_A6XX_GMU_DCVS_PERF_SETTING,
@@ -158,7 +162,6 @@ void a6xx_gmu_set_freq(struct msm_gpu *gpu, struct 
dev_pm_opp *opp)
  if (ret)
  dev_err(gmu->dev, "GMU set GPU frequency error: %d\n", ret);

-   dev_pm_opp_set_opp(>pdev->dev, opp);
  pm_runtime_put(gmu->dev);
   }


Maybe just remove it for affected devices?  But I'll defer to Bjorn.

BR,
-R



--
Kind Regards,
Caleb (they/them)


--
Kind Regards,
Caleb (they/them)


Re: [PATCH v4 01/14] vfio/samples: Remove module get/put

2021-08-08 Thread Max Gurtovoy



On 8/6/2021 4:18 AM, Jason Gunthorpe wrote:

The patch to move the get/put to core and the patch to convert the samples
to use vfio_device crossed in a way that this was missed. When both
patches are together the samples do not need their own get/put.

Fixes: 437e41368c01 ("vfio/mdpy: Convert to use vfio_register_group_dev()")
Fixes: 681c1615f891 ("vfio/mbochs: Convert to use vfio_register_group_dev()")
Reviewed-by: Cornelia Huck 
Reviewed-by: Christoph Hellwig 
Signed-off-by: Jason Gunthorpe 
---
  samples/vfio-mdev/mbochs.c | 4 
  samples/vfio-mdev/mdpy.c   | 4 
  2 files changed, 8 deletions(-)


Looks good,

Reviewed-by: Max Gurtovoy 




[PATCH] drm/panel/tianma-tl057fvxp01: add panel for Motorola Moto G6

2021-08-08 Thread Julian Braha
This is a 5.7" 2160x1080 panel found on the Motorola Moto G6.
There may be other smartphones using it, as well.

Signed-off-by: Julian Braha 
---
 drivers/gpu/drm/panel/Kconfig |  7 ++
 drivers/gpu/drm/panel/Makefile|  1 +
 .../gpu/drm/panel/panel-tianma-tl057fvxp01.c  | 92 +++
 3 files changed, 100 insertions(+)
 create mode 100644 drivers/gpu/drm/panel/panel-tianma-tl057fvxp01.c

diff --git a/drivers/gpu/drm/panel/Kconfig b/drivers/gpu/drm/panel/Kconfig
index beb581b96ecd..9e8a5fffa626 100644
--- a/drivers/gpu/drm/panel/Kconfig
+++ b/drivers/gpu/drm/panel/Kconfig
@@ -557,6 +557,13 @@ config DRM_PANEL_TDO_TL070WSH30
  24 bit RGB per pixel. It provides a MIPI DSI interface to
  the host, a built-in LED backlight and touch controller.
 
+config DRM_PANEL_TIANMA_TL057FVXP01
+   tristate "Tianma TL057FVXP01 panel"
+   select DRM_PANEL_MIPI_DSI_COMMON
+   help
+ Say Y here if you want to enable support for the Tianma TL057FVXP01
+ 2160x1080 5.7" panel (found on the Motorola Moto G6).
+
 config DRM_PANEL_TPO_TD028TTEC1
tristate "Toppoly (TPO) TD028TTEC1 panel driver"
depends on OF && SPI
diff --git a/drivers/gpu/drm/panel/Makefile b/drivers/gpu/drm/panel/Makefile
index c8132050bcec..9bdc2a12e719 100644
--- a/drivers/gpu/drm/panel/Makefile
+++ b/drivers/gpu/drm/panel/Makefile
@@ -57,6 +57,7 @@ obj-$(CONFIG_DRM_PANEL_SITRONIX_ST7789V) += 
panel-sitronix-st7789v.o
 obj-$(CONFIG_DRM_PANEL_SONY_ACX424AKP) += panel-sony-acx424akp.o
 obj-$(CONFIG_DRM_PANEL_SONY_ACX565AKM) += panel-sony-acx565akm.o
 obj-$(CONFIG_DRM_PANEL_TDO_TL070WSH30) += panel-tdo-tl070wsh30.o
+obj-$(CONFIG_DRM_PANEL_TIANMA_TL057FVXP01) += panel-tianma-tl057fvxp01.o
 obj-$(CONFIG_DRM_PANEL_TPO_TD028TTEC1) += panel-tpo-td028ttec1.o
 obj-$(CONFIG_DRM_PANEL_TPO_TD043MTEA1) += panel-tpo-td043mtea1.o
 obj-$(CONFIG_DRM_PANEL_TPO_TPG110) += panel-tpo-tpg110.o
diff --git a/drivers/gpu/drm/panel/panel-tianma-tl057fvxp01.c 
b/drivers/gpu/drm/panel/panel-tianma-tl057fvxp01.c
new file mode 100644
index ..903ce8f35b75
--- /dev/null
+++ b/drivers/gpu/drm/panel/panel-tianma-tl057fvxp01.c
@@ -0,0 +1,92 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2021 Julian Braha 
+ * Copyright (c) 2021, The Linux Foundation. All rights reserved.
+ * Generated with linux-mdss-dsi-panel-driver-generator from vendor device tree
+ */
+
+#include 
+#include 
+#include 
+#include 
+
+#include 
+
+#include "panel-mipi-dsi-common.h"
+
+static void tianma_tl057fvxp01_reset(struct gpio_desc *reset_gpio)
+{
+   gpiod_set_value_cansleep(reset_gpio, 0);
+   usleep_range(5000, 6000);
+   gpiod_set_value_cansleep(reset_gpio, 1);
+   usleep_range(1000, 2000);
+   gpiod_set_value_cansleep(reset_gpio, 0);
+   usleep_range(1, 11000);
+}
+
+static int tianma_tl057fvxp01_on(struct mipi_dsi_device *dsi)
+{
+   struct device *dev = >dev;
+   int ret;
+
+   dsi_dcs_write_seq(dsi, 0x00, 0x00);
+   dsi_dcs_write_seq(dsi, 0xff, 0x19, 0x11, 0x01);
+   dsi_dcs_write_seq(dsi, 0x00, 0x80);
+   dsi_dcs_write_seq(dsi, 0xff, 0x19, 0x11);
+   dsi_dcs_write_seq(dsi, 0x00, 0xb0);
+   dsi_dcs_write_seq(dsi, 0xb3, 0x04, 0x38, 0x08, 0x70);
+   dsi_dcs_write_seq(dsi, 0x00, 0x00);
+   dsi_dcs_write_seq(dsi, 0xff, 0xff, 0xff, 0xff);
+   dsi_dcs_write_seq(dsi, 0x35, 0x00);
+   dsi_dcs_write_seq(dsi, 0x51, 0xcc, 0x08);
+   dsi_dcs_write_seq(dsi, 0x53, 0x2c);
+   dsi_dcs_write_seq(dsi, 0x55, 0x01);
+
+   ret = mipi_dsi_dcs_exit_sleep_mode(dsi);
+   if (ret < 0) {
+   dev_err(dev, "Failed to exit sleep mode: %d\n", ret);
+   return ret;
+   }
+   msleep(120);
+
+   ret = mipi_dsi_dcs_set_display_on(dsi);
+   if (ret < 0) {
+   dev_err(dev, "Failed to set display on: %d\n", ret);
+   return ret;
+   }
+   usleep_range(1, 11000);
+
+   return 0;
+}
+
+static const struct panel_mipi_dsi_info tianma_tl057fvxp01_info = {
+   .mode = {
+   .clock = (1080 + 53 + 4 + 53) * (2160 + 14 + 1 + 11) * 60 / 
1000,
+   .hdisplay = 1080,
+   .hsync_start = 1080 + 53,
+   .hsync_end = 1080 + 53 + 4,
+   .htotal = 1080 + 53 + 4 + 53,
+   .vdisplay = 2160,
+   .vsync_start = 2160 + 14,
+   .vsync_end = 2160 + 14 + 1,
+   .vtotal = 2160 + 14 + 1 + 11,
+   .width_mm = 62,
+   .height_mm = 110,
+   },
+
+   .reset = tianma_tl057fvxp01_reset,
+   .power_on = tianma_tl057fvxp01_on,
+
+   .lanes = 4,
+   .format = MIPI_DSI_FMT_RGB888,
+   .mode_flags = MIPI_DSI_MODE_VIDEO
+   | MIPI_DSI_MODE_VIDEO_BURST
+   | MIPI_DSI_CLOCK_NON_CONTINUOUS
+   | MIPI_DSI_MODE_LPM
+};
+
+MIPI_DSI_PANEL_DRIVER(tianma_tl057fvxp01, "tianma-tl057fvxp01", 

Re: [PATCH 2/2] drm/panel: Add Truly NT35521 panel driver

2021-08-08 Thread Shawn Guo
Hi Sam,

On Wed, Aug 04, 2021 at 06:24:12PM +0200, Sam Ravnborg wrote:
> Hi Shawn,
> 
> see a few comments in the following.

Thanks for the review comments!  All of them will be addressed in v2.

Shawn

> On Wed, Aug 04, 2021 at 04:13:52PM +0800, Shawn Guo wrote:
> > It adds a drm driver for Truly NT35521 5.24" 1280x720 DSI panel, which
> > can be found on Sony Xperia M4 Aqua phone.  The panel backlight is
> > managed through DSI link.
> > 
> > Signed-off-by: Shawn Guo 
> > ---
> >  drivers/gpu/drm/panel/Kconfig   |   9 +
> >  drivers/gpu/drm/panel/Makefile  |   1 +
> >  drivers/gpu/drm/panel/panel-truly-nt35521.c | 491 
> >  3 files changed, 501 insertions(+)
> >  create mode 100644 drivers/gpu/drm/panel/panel-truly-nt35521.c


[PATCH 8/8] drm/ingenic: Attach bridge chain to encoders

2021-08-08 Thread Paul Cercueil
Attach a top-level bridge to each encoder, which will be used for
negociating the bus format and flags.

All the bridges are now attached with DRM_BRIDGE_ATTACH_NO_CONNECTOR.

Signed-off-by: Paul Cercueil 
---
 drivers/gpu/drm/ingenic/ingenic-drm-drv.c | 92 +--
 1 file changed, 70 insertions(+), 22 deletions(-)

diff --git a/drivers/gpu/drm/ingenic/ingenic-drm-drv.c 
b/drivers/gpu/drm/ingenic/ingenic-drm-drv.c
index 7ae48ead3ab6..09d5dd298078 100644
--- a/drivers/gpu/drm/ingenic/ingenic-drm-drv.c
+++ b/drivers/gpu/drm/ingenic/ingenic-drm-drv.c
@@ -21,6 +21,7 @@
 #include 
 #include 
 #include 
+#include 
 #include 
 #include 
 #include 
@@ -107,6 +108,19 @@ struct ingenic_drm {
struct drm_private_obj private_obj;
 };
 
+struct ingenic_drm_bridge {
+   struct drm_encoder encoder;
+   struct drm_bridge bridge, *next_bridge;
+
+   struct drm_bus_cfg bus_cfg;
+};
+
+static inline struct ingenic_drm_bridge *
+to_ingenic_drm_bridge(struct drm_encoder *encoder)
+{
+   return container_of(encoder, struct ingenic_drm_bridge, encoder);
+}
+
 static inline struct ingenic_drm_private_state *
 to_ingenic_drm_priv_state(struct drm_private_state *state)
 {
@@ -679,11 +693,10 @@ static void ingenic_drm_encoder_atomic_mode_set(struct 
drm_encoder *encoder,
 {
struct ingenic_drm *priv = drm_device_get_priv(encoder->dev);
struct drm_display_mode *mode = _state->adjusted_mode;
-   struct drm_connector *conn = conn_state->connector;
-   struct drm_display_info *info = >display_info;
+   struct ingenic_drm_bridge *bridge = to_ingenic_drm_bridge(encoder);
unsigned int cfg, rgbcfg = 0;
 
-   priv->panel_is_sharp = info->bus_flags & DRM_BUS_FLAG_SHARP_SIGNALS;
+   priv->panel_is_sharp = bridge->bus_cfg.flags & 
DRM_BUS_FLAG_SHARP_SIGNALS;
 
if (priv->panel_is_sharp) {
cfg = JZ_LCD_CFG_MODE_SPECIAL_TFT_1 | JZ_LCD_CFG_REV_POLARITY;
@@ -696,19 +709,19 @@ static void ingenic_drm_encoder_atomic_mode_set(struct 
drm_encoder *encoder,
cfg |= JZ_LCD_CFG_HSYNC_ACTIVE_LOW;
if (mode->flags & DRM_MODE_FLAG_NVSYNC)
cfg |= JZ_LCD_CFG_VSYNC_ACTIVE_LOW;
-   if (info->bus_flags & DRM_BUS_FLAG_DE_LOW)
+   if (bridge->bus_cfg.flags & DRM_BUS_FLAG_DE_LOW)
cfg |= JZ_LCD_CFG_DE_ACTIVE_LOW;
-   if (info->bus_flags & DRM_BUS_FLAG_PIXDATA_DRIVE_NEGEDGE)
+   if (bridge->bus_cfg.flags & DRM_BUS_FLAG_PIXDATA_DRIVE_NEGEDGE)
cfg |= JZ_LCD_CFG_PCLK_FALLING_EDGE;
 
if (!priv->panel_is_sharp) {
-   if (conn->connector_type == DRM_MODE_CONNECTOR_TV) {
+   if (conn_state->connector->connector_type == 
DRM_MODE_CONNECTOR_TV) {
if (mode->flags & DRM_MODE_FLAG_INTERLACE)
cfg |= JZ_LCD_CFG_MODE_TV_OUT_I;
else
cfg |= JZ_LCD_CFG_MODE_TV_OUT_P;
} else {
-   switch (*info->bus_formats) {
+   switch (bridge->bus_cfg.format) {
case MEDIA_BUS_FMT_RGB565_1X16:
cfg |= JZ_LCD_CFG_MODE_GENERIC_16BIT;
break;
@@ -734,20 +747,29 @@ static void ingenic_drm_encoder_atomic_mode_set(struct 
drm_encoder *encoder,
regmap_write(priv->map, JZ_REG_LCD_RGBC, rgbcfg);
 }
 
-static int ingenic_drm_encoder_atomic_check(struct drm_encoder *encoder,
-   struct drm_crtc_state *crtc_state,
-   struct drm_connector_state 
*conn_state)
+static int ingenic_drm_bridge_attach(struct drm_bridge *bridge,
+enum drm_bridge_attach_flags flags)
+{
+   struct ingenic_drm_bridge *ib = to_ingenic_drm_bridge(bridge->encoder);
+
+   return drm_bridge_attach(bridge->encoder, ib->next_bridge,
+>bridge, flags);
+}
+
+static int ingenic_drm_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 drm_display_info *info = _state->connector->display_info;
struct drm_display_mode *mode = _state->adjusted_mode;
+   struct ingenic_drm_bridge *ib = to_ingenic_drm_bridge(bridge->encoder);
 
-   if (info->num_bus_formats != 1)
-   return -EINVAL;
+   ib->bus_cfg = bridge_state->output_bus_cfg;
 
if (conn_state->connector->connector_type == DRM_MODE_CONNECTOR_TV)
return 0;
 
-   switch (*info->bus_formats) {
+   switch (bridge_state->output_bus_cfg.format) {
case MEDIA_BUS_FMT_RGB888_3X8:
case MEDIA_BUS_FMT_RGB888_3X8_DELTA:
/*
@@ -911,8 +933,16 

[PATCH 7/8] drm/ingenic: Upload palette before frame

2021-08-08 Thread Paul Cercueil
When using C8 color mode, make sure that the palette is always uploaded
before a frame; otherwise the very first frame will have wrong colors.

Do that by changing the link order of the DMA descriptors.

Signed-off-by: Paul Cercueil 
---
 drivers/gpu/drm/ingenic/ingenic-drm-drv.c | 69 +++
 1 file changed, 59 insertions(+), 10 deletions(-)

diff --git a/drivers/gpu/drm/ingenic/ingenic-drm-drv.c 
b/drivers/gpu/drm/ingenic/ingenic-drm-drv.c
index 2eef174165a2..7ae48ead3ab6 100644
--- a/drivers/gpu/drm/ingenic/ingenic-drm-drv.c
+++ b/drivers/gpu/drm/ingenic/ingenic-drm-drv.c
@@ -65,6 +65,7 @@ struct jz_soc_info {
 
 struct ingenic_drm_private_state {
struct drm_private_state base;
+   bool use_palette;
 };
 
 struct ingenic_drm {
@@ -112,6 +113,30 @@ to_ingenic_drm_priv_state(struct drm_private_state *state)
return container_of(state, struct ingenic_drm_private_state, base);
 }
 
+static struct ingenic_drm_private_state *
+ingenic_drm_get_priv_state(struct ingenic_drm *priv, struct drm_atomic_state 
*state)
+{
+   struct drm_private_state *priv_state;
+
+   priv_state = drm_atomic_get_private_obj_state(state, 
>private_obj);
+   if (IS_ERR(priv_state))
+   return ERR_CAST(priv_state);
+
+   return to_ingenic_drm_priv_state(priv_state);
+}
+
+static struct ingenic_drm_private_state *
+ingenic_drm_get_new_priv_state(struct ingenic_drm *priv, struct 
drm_atomic_state *state)
+{
+   struct drm_private_state *priv_state;
+
+   priv_state = drm_atomic_get_new_private_obj_state(state, 
>private_obj);
+   if (!priv_state)
+   return NULL;
+
+   return to_ingenic_drm_priv_state(priv_state);
+}
+
 static bool ingenic_drm_writeable_reg(struct device *dev, unsigned int reg)
 {
switch (reg) {
@@ -159,6 +184,13 @@ static inline dma_addr_t dma_hwdesc_addr(const struct 
ingenic_drm *priv, bool us
return priv->dma_hwdescs_phys + offset;
 }
 
+static inline dma_addr_t dma_hwdesc_pal_addr(const struct ingenic_drm *priv)
+{
+   u32 offset = offsetof(struct ingenic_dma_hwdescs, hwdesc_pal);
+
+   return priv->dma_hwdescs_phys + offset;
+}
+
 static int ingenic_drm_update_pixclk(struct notifier_block *nb,
 unsigned long action,
 void *data)
@@ -181,11 +213,19 @@ static void ingenic_drm_crtc_atomic_enable(struct 
drm_crtc *crtc,
   struct drm_atomic_state *state)
 {
struct ingenic_drm *priv = drm_crtc_get_priv(crtc);
+   struct ingenic_drm_private_state *priv_state;
+
+   priv_state = ingenic_drm_get_new_priv_state(priv, state);
+   if (WARN_ON(!priv_state))
+   return;
 
regmap_write(priv->map, JZ_REG_LCD_STATE, 0);
 
/* Set address of our DMA descriptor chain */
-   regmap_write(priv->map, JZ_REG_LCD_DA0, dma_hwdesc_addr(priv, 0));
+   if (priv_state->use_palette)
+   regmap_write(priv->map, JZ_REG_LCD_DA0, 
dma_hwdesc_pal_addr(priv));
+   else
+   regmap_write(priv->map, JZ_REG_LCD_DA0, dma_hwdesc_addr(priv, 
0));
regmap_write(priv->map, JZ_REG_LCD_DA1, dma_hwdesc_addr(priv, 1));
 
regmap_update_bits(priv->map, JZ_REG_LCD_CTRL,
@@ -391,6 +431,7 @@ static int ingenic_drm_plane_atomic_check(struct drm_plane 
*plane,
struct drm_plane_state *new_plane_state = 
drm_atomic_get_new_plane_state(state,

 plane);
struct ingenic_drm *priv = drm_device_get_priv(plane->dev);
+   struct ingenic_drm_private_state *priv_state;
struct drm_crtc_state *crtc_state;
struct drm_crtc *crtc = new_plane_state->crtc ?: old_plane_state->crtc;
int ret;
@@ -403,6 +444,10 @@ static int ingenic_drm_plane_atomic_check(struct drm_plane 
*plane,
if (WARN_ON(!crtc_state))
return -EINVAL;
 
+   priv_state = ingenic_drm_get_priv_state(priv, state);
+   if (IS_ERR(priv_state))
+   return PTR_ERR(priv_state);
+
ret = drm_atomic_helper_check_plane_state(new_plane_state, crtc_state,
  DRM_PLANE_HELPER_NO_SCALING,
  DRM_PLANE_HELPER_NO_SCALING,
@@ -421,6 +466,9 @@ static int ingenic_drm_plane_atomic_check(struct drm_plane 
*plane,
 (new_plane_state->src_h >> 16) != new_plane_state->crtc_h))
return -EINVAL;
 
+   priv_state->use_palette = new_plane_state->fb &&
+   new_plane_state->fb->format->format == DRM_FORMAT_C8;
+
/*
 * Require full modeset if enabling or disabling a plane, or changing
 * its position, size or depth.
@@ -580,10 +628,11 @@ static void ingenic_drm_plane_atomic_update(struct 
drm_plane *plane,
struct ingenic_drm *priv = drm_device_get_priv(plane->dev);
struct drm_plane_state 

[PATCH 6/8] drm/ingenic: Set DMA descriptor chain register when starting CRTC

2021-08-08 Thread Paul Cercueil
Setting the DMA descriptor chain register in the probe function has been
fine until now, because we only ever had one descriptor per foreground.

As the driver will soon have real descriptor chains, and the DMA
descriptor chain register updates itself to point to the current
descriptor being processed, this register needs to be reset after a full
modeset to point to the first descriptor of the chain.

Signed-off-by: Paul Cercueil 
---
 drivers/gpu/drm/ingenic/ingenic-drm-drv.c | 4 
 1 file changed, 4 insertions(+)

diff --git a/drivers/gpu/drm/ingenic/ingenic-drm-drv.c 
b/drivers/gpu/drm/ingenic/ingenic-drm-drv.c
index 3fc01cffb00f..2eef174165a2 100644
--- a/drivers/gpu/drm/ingenic/ingenic-drm-drv.c
+++ b/drivers/gpu/drm/ingenic/ingenic-drm-drv.c
@@ -184,6 +184,10 @@ static void ingenic_drm_crtc_atomic_enable(struct drm_crtc 
*crtc,
 
regmap_write(priv->map, JZ_REG_LCD_STATE, 0);
 
+   /* Set address of our DMA descriptor chain */
+   regmap_write(priv->map, JZ_REG_LCD_DA0, dma_hwdesc_addr(priv, 0));
+   regmap_write(priv->map, JZ_REG_LCD_DA1, dma_hwdesc_addr(priv, 1));
+
regmap_update_bits(priv->map, JZ_REG_LCD_CTRL,
   JZ_LCD_CTRL_ENABLE | JZ_LCD_CTRL_DISABLE,
   JZ_LCD_CTRL_ENABLE);
-- 
2.30.2



[PATCH 5/8] drm/ingenic: Move IPU scale settings to private state

2021-08-08 Thread Paul Cercueil
The IPU scaling information is computed in the plane's ".atomic_check"
callback, and used in the ".atomic_update" callback. As such, it is
state-specific, and should be moved to a private state structure.

Signed-off-by: Paul Cercueil 
---
 drivers/gpu/drm/ingenic/ingenic-ipu.c | 73 ---
 1 file changed, 54 insertions(+), 19 deletions(-)

diff --git a/drivers/gpu/drm/ingenic/ingenic-ipu.c 
b/drivers/gpu/drm/ingenic/ingenic-ipu.c
index c819293b8317..2737fc521e15 100644
--- a/drivers/gpu/drm/ingenic/ingenic-ipu.c
+++ b/drivers/gpu/drm/ingenic/ingenic-ipu.c
@@ -47,6 +47,8 @@ struct soc_info {
 
 struct ingenic_ipu_private_state {
struct drm_private_state base;
+
+   unsigned int num_w, num_h, denom_w, denom_h;
 };
 
 struct ingenic_ipu {
@@ -58,8 +60,6 @@ struct ingenic_ipu {
const struct soc_info *soc_info;
bool clk_enabled;
 
-   unsigned int num_w, num_h, denom_w, denom_h;
-
dma_addr_t addr_y, addr_u, addr_v;
 
struct drm_property *sharpness_prop;
@@ -85,6 +85,30 @@ to_ingenic_ipu_priv_state(struct drm_private_state *state)
return container_of(state, struct ingenic_ipu_private_state, base);
 }
 
+static struct ingenic_ipu_private_state *
+ingenic_ipu_get_priv_state(struct ingenic_ipu *priv, struct drm_atomic_state 
*state)
+{
+   struct drm_private_state *priv_state;
+
+   priv_state = drm_atomic_get_private_obj_state(state, 
>private_obj);
+   if (IS_ERR(priv_state))
+   return ERR_CAST(priv_state);
+
+   return to_ingenic_ipu_priv_state(priv_state);
+}
+
+static struct ingenic_ipu_private_state *
+ingenic_ipu_get_new_priv_state(struct ingenic_ipu *priv, struct 
drm_atomic_state *state)
+{
+   struct drm_private_state *priv_state;
+
+   priv_state = drm_atomic_get_new_private_obj_state(state, 
>private_obj);
+   if (!priv_state)
+   return NULL;
+
+   return to_ingenic_ipu_priv_state(priv_state);
+}
+
 /*
  * Apply conventional cubic convolution kernel. Both parameters
  *  and return value are 15.16 signed fixed-point.
@@ -305,11 +329,16 @@ static void ingenic_ipu_plane_atomic_update(struct 
drm_plane *plane,
const struct drm_format_info *finfo;
u32 ctrl, stride = 0, coef_index = 0, format = 0;
bool needs_modeset, upscaling_w, upscaling_h;
+   struct ingenic_ipu_private_state *ipu_state;
int err;
 
if (!newstate || !newstate->fb)
return;
 
+   ipu_state = ingenic_ipu_get_new_priv_state(ipu, state);
+   if (WARN_ON(!ipu_state))
+   return;
+
finfo = drm_format_info(newstate->fb->format->format);
 
if (!ipu->clk_enabled) {
@@ -482,27 +511,27 @@ static void ingenic_ipu_plane_atomic_update(struct 
drm_plane *plane,
if (ipu->soc_info->has_bicubic)
ctrl |= JZ_IPU_CTRL_ZOOM_SEL;
 
-   upscaling_w = ipu->num_w > ipu->denom_w;
+   upscaling_w = ipu_state->num_w > ipu_state->denom_w;
if (upscaling_w)
ctrl |= JZ_IPU_CTRL_HSCALE;
 
-   if (ipu->num_w != 1 || ipu->denom_w != 1) {
+   if (ipu_state->num_w != 1 || ipu_state->denom_w != 1) {
if (!ipu->soc_info->has_bicubic && !upscaling_w)
-   coef_index |= (ipu->denom_w - 1) << 16;
+   coef_index |= (ipu_state->denom_w - 1) << 16;
else
-   coef_index |= (ipu->num_w - 1) << 16;
+   coef_index |= (ipu_state->num_w - 1) << 16;
ctrl |= JZ_IPU_CTRL_HRSZ_EN;
}
 
-   upscaling_h = ipu->num_h > ipu->denom_h;
+   upscaling_h = ipu_state->num_h > ipu_state->denom_h;
if (upscaling_h)
ctrl |= JZ_IPU_CTRL_VSCALE;
 
-   if (ipu->num_h != 1 || ipu->denom_h != 1) {
+   if (ipu_state->num_h != 1 || ipu_state->denom_h != 1) {
if (!ipu->soc_info->has_bicubic && !upscaling_h)
-   coef_index |= ipu->denom_h - 1;
+   coef_index |= ipu_state->denom_h - 1;
else
-   coef_index |= ipu->num_h - 1;
+   coef_index |= ipu_state->num_h - 1;
ctrl |= JZ_IPU_CTRL_VRSZ_EN;
}
 
@@ -513,13 +542,13 @@ static void ingenic_ipu_plane_atomic_update(struct 
drm_plane *plane,
/* Set the LUT index register */
regmap_write(ipu->map, JZ_REG_IPU_RSZ_COEF_INDEX, coef_index);
 
-   if (ipu->num_w != 1 || ipu->denom_w != 1)
+   if (ipu_state->num_w != 1 || ipu_state->denom_w != 1)
ingenic_ipu_set_coefs(ipu, JZ_REG_IPU_HRSZ_COEF_LUT,
- ipu->num_w, ipu->denom_w);
+ ipu_state->num_w, ipu_state->denom_w);
 
-   if (ipu->num_h != 1 || ipu->denom_h != 1)
+   if (ipu_state->num_h != 1 || ipu_state->denom_h != 1)
ingenic_ipu_set_coefs(ipu, JZ_REG_IPU_VRSZ_COEF_LUT,
-

[PATCH 4/8] drm/ingenic: Add support for private objects

2021-08-08 Thread Paul Cercueil
Until now, the ingenic-drm as well as the ingenic-ipu drivers used to
put state-specific information in their respective private structure.

Add boilerplate code to support private objects in the two drivers, so
that state-specific information can be put in the state-specific private
structure.

Signed-off-by: Paul Cercueil 
---
 drivers/gpu/drm/ingenic/ingenic-drm-drv.c | 61 +++
 drivers/gpu/drm/ingenic/ingenic-ipu.c | 54 
 2 files changed, 115 insertions(+)

diff --git a/drivers/gpu/drm/ingenic/ingenic-drm-drv.c 
b/drivers/gpu/drm/ingenic/ingenic-drm-drv.c
index 3ed7c27a8dde..3fc01cffb00f 100644
--- a/drivers/gpu/drm/ingenic/ingenic-drm-drv.c
+++ b/drivers/gpu/drm/ingenic/ingenic-drm-drv.c
@@ -63,6 +63,10 @@ struct jz_soc_info {
unsigned int num_formats_f0, num_formats_f1;
 };
 
+struct ingenic_drm_private_state {
+   struct drm_private_state base;
+};
+
 struct ingenic_drm {
struct drm_device drm;
/*
@@ -98,8 +102,16 @@ struct ingenic_drm {
struct mutex clk_mutex;
bool update_clk_rate;
struct notifier_block clock_nb;
+
+   struct drm_private_obj private_obj;
 };
 
+static inline struct ingenic_drm_private_state *
+to_ingenic_drm_priv_state(struct drm_private_state *state)
+{
+   return container_of(state, struct ingenic_drm_private_state, base);
+}
+
 static bool ingenic_drm_writeable_reg(struct device *dev, unsigned int reg)
 {
switch (reg) {
@@ -769,6 +781,28 @@ ingenic_drm_gem_create_object(struct drm_device *drm, 
size_t size)
return >base;
 }
 
+static struct drm_private_state *
+ingenic_drm_duplicate_state(struct drm_private_obj *obj)
+{
+   struct ingenic_drm_private_state *state = 
to_ingenic_drm_priv_state(obj->state);
+
+   state = kmemdup(state, sizeof(*state), GFP_KERNEL);
+   if (!state)
+   return NULL;
+
+   __drm_atomic_helper_private_obj_duplicate_state(obj, >base);
+
+   return >base;
+}
+
+static void ingenic_drm_destroy_state(struct drm_private_obj *obj,
+ struct drm_private_state *state)
+{
+   struct ingenic_drm_private_state *priv_state = 
to_ingenic_drm_priv_state(state);
+
+   kfree(priv_state);
+}
+
 DEFINE_DRM_GEM_CMA_FOPS(ingenic_drm_fops);
 
 static const struct drm_driver ingenic_drm_driver_data = {
@@ -839,6 +873,11 @@ static struct drm_mode_config_helper_funcs 
ingenic_drm_mode_config_helpers = {
.atomic_commit_tail = drm_atomic_helper_commit_tail,
 };
 
+static const struct drm_private_state_funcs ingenic_drm_private_state_funcs = {
+   .atomic_duplicate_state = ingenic_drm_duplicate_state,
+   .atomic_destroy_state = ingenic_drm_destroy_state,
+};
+
 static void ingenic_drm_unbind_all(void *d)
 {
struct ingenic_drm *priv = d;
@@ -851,9 +890,15 @@ static void __maybe_unused ingenic_drm_release_rmem(void 
*d)
of_reserved_mem_device_release(d);
 }
 
+static void ingenic_drm_atomic_private_obj_fini(struct drm_device *drm, void 
*private_obj)
+{
+   drm_atomic_private_obj_fini(private_obj);
+}
+
 static int ingenic_drm_bind(struct device *dev, bool has_components)
 {
struct platform_device *pdev = to_platform_device(dev);
+   struct ingenic_drm_private_state *private_state;
const struct jz_soc_info *soc_info;
struct ingenic_drm *priv;
struct clk *parent_clk;
@@ -1132,6 +1177,20 @@ static int ingenic_drm_bind(struct device *dev, bool 
has_components)
goto err_devclk_disable;
}
 
+   private_state = kzalloc(sizeof(*private_state), GFP_KERNEL);
+   if (!private_state) {
+   ret = -ENOMEM;
+   goto err_clk_notifier_unregister;
+   }
+
+   drm_atomic_private_obj_init(drm, >private_obj, 
_state->base,
+   _drm_private_state_funcs);
+
+   ret = drmm_add_action_or_reset(drm, ingenic_drm_atomic_private_obj_fini,
+  >private_obj);
+   if (ret)
+   goto err_private_state_free;
+
ret = drm_dev_register(drm, 0);
if (ret) {
dev_err(dev, "Failed to register DRM driver\n");
@@ -1142,6 +1201,8 @@ static int ingenic_drm_bind(struct device *dev, bool 
has_components)
 
return 0;
 
+err_private_state_free:
+   kfree(private_state);
 err_clk_notifier_unregister:
clk_notifier_unregister(parent_clk, >clock_nb);
 err_devclk_disable:
diff --git a/drivers/gpu/drm/ingenic/ingenic-ipu.c 
b/drivers/gpu/drm/ingenic/ingenic-ipu.c
index aeb8a757d213..c819293b8317 100644
--- a/drivers/gpu/drm/ingenic/ingenic-ipu.c
+++ b/drivers/gpu/drm/ingenic/ingenic-ipu.c
@@ -45,6 +45,10 @@ struct soc_info {
  unsigned int weight, unsigned int offset);
 };
 
+struct ingenic_ipu_private_state {
+   struct drm_private_state base;
+};
+
 struct ingenic_ipu {
struct drm_plane plane;
struct drm_device *drm;
@@ -60,6 

[PATCH 3/8] drm/ingenic: Use standard drm_atomic_helper_commit_tail

2021-08-08 Thread Paul Cercueil
By making the CRTC's .vblank_enable() function return an error when it
is known that the hardware won't deliver a VBLANK, we can drop the
ingenic_drm_atomic_helper_commit_tail() function and use the standard
drm_atomic_helper_commit_tail() function instead.

Signed-off-by: Paul Cercueil 
---
 drivers/gpu/drm/ingenic/ingenic-drm-drv.c | 28 ---
 1 file changed, 4 insertions(+), 24 deletions(-)

diff --git a/drivers/gpu/drm/ingenic/ingenic-drm-drv.c 
b/drivers/gpu/drm/ingenic/ingenic-drm-drv.c
index bc71ba44ccf4..3ed7c27a8dde 100644
--- a/drivers/gpu/drm/ingenic/ingenic-drm-drv.c
+++ b/drivers/gpu/drm/ingenic/ingenic-drm-drv.c
@@ -706,29 +706,6 @@ static int ingenic_drm_encoder_atomic_check(struct 
drm_encoder *encoder,
}
 }
 
-static void ingenic_drm_atomic_helper_commit_tail(struct drm_atomic_state 
*old_state)
-{
-   /*
-* Just your regular drm_atomic_helper_commit_tail(), but only calls
-* drm_atomic_helper_wait_for_vblanks() if priv->no_vblank.
-*/
-   struct drm_device *dev = old_state->dev;
-   struct ingenic_drm *priv = drm_device_get_priv(dev);
-
-   drm_atomic_helper_commit_modeset_disables(dev, old_state);
-
-   drm_atomic_helper_commit_planes(dev, old_state, 0);
-
-   drm_atomic_helper_commit_modeset_enables(dev, old_state);
-
-   drm_atomic_helper_commit_hw_done(old_state);
-
-   if (!priv->no_vblank)
-   drm_atomic_helper_wait_for_vblanks(dev, old_state);
-
-   drm_atomic_helper_cleanup_planes(dev, old_state);
-}
-
 static irqreturn_t ingenic_drm_irq_handler(int irq, void *arg)
 {
struct ingenic_drm *priv = drm_device_get_priv(arg);
@@ -749,6 +726,9 @@ static int ingenic_drm_enable_vblank(struct drm_crtc *crtc)
 {
struct ingenic_drm *priv = drm_crtc_get_priv(crtc);
 
+   if (priv->no_vblank)
+   return -EWOULDBLOCK;
+
regmap_update_bits(priv->map, JZ_REG_LCD_CTRL,
   JZ_LCD_CTRL_EOF_IRQ, JZ_LCD_CTRL_EOF_IRQ);
 
@@ -856,7 +836,7 @@ static const struct drm_mode_config_funcs 
ingenic_drm_mode_config_funcs = {
 };
 
 static struct drm_mode_config_helper_funcs ingenic_drm_mode_config_helpers = {
-   .atomic_commit_tail = ingenic_drm_atomic_helper_commit_tail,
+   .atomic_commit_tail = drm_atomic_helper_commit_tail,
 };
 
 static void ingenic_drm_unbind_all(void *d)
-- 
2.30.2



[PATCH 2/8] drm/ingenic: Simplify code by using hwdescs array

2021-08-08 Thread Paul Cercueil
Instead of having one 'hwdesc' variable for the plane #0 and one for the
plane #1, use a 'hwdesc[2]' array, where the DMA hardware descriptors
are indexed by the plane's number.

Signed-off-by: Paul Cercueil 
---
 drivers/gpu/drm/ingenic/ingenic-drm-drv.c | 38 ---
 1 file changed, 20 insertions(+), 18 deletions(-)

diff --git a/drivers/gpu/drm/ingenic/ingenic-drm-drv.c 
b/drivers/gpu/drm/ingenic/ingenic-drm-drv.c
index e42eb43d8020..bc71ba44ccf4 100644
--- a/drivers/gpu/drm/ingenic/ingenic-drm-drv.c
+++ b/drivers/gpu/drm/ingenic/ingenic-drm-drv.c
@@ -49,8 +49,7 @@ struct ingenic_dma_hwdesc {
 } __aligned(16);
 
 struct ingenic_dma_hwdescs {
-   struct ingenic_dma_hwdesc hwdesc_f0;
-   struct ingenic_dma_hwdesc hwdesc_f1;
+   struct ingenic_dma_hwdesc hwdesc[2];
struct ingenic_dma_hwdesc hwdesc_pal;
u16 palette[256] __aligned(16);
 };
@@ -141,6 +140,13 @@ static inline struct ingenic_drm *drm_nb_get_priv(struct 
notifier_block *nb)
return container_of(nb, struct ingenic_drm, clock_nb);
 }
 
+static inline dma_addr_t dma_hwdesc_addr(const struct ingenic_drm *priv, bool 
use_f1)
+{
+   u32 offset = offsetof(struct ingenic_dma_hwdescs, hwdesc[use_f1]);
+
+   return priv->dma_hwdescs_phys + offset;
+}
+
 static int ingenic_drm_update_pixclk(struct notifier_block *nb,
 unsigned long action,
 void *data)
@@ -562,6 +568,7 @@ static void ingenic_drm_plane_atomic_update(struct 
drm_plane *plane,
struct ingenic_dma_hwdesc *hwdesc;
unsigned int width, height, cpp, offset;
dma_addr_t addr;
+   bool use_f1;
u32 fourcc;
 
if (newstate && newstate->fb) {
@@ -569,16 +576,14 @@ static void ingenic_drm_plane_atomic_update(struct 
drm_plane *plane,
drm_fb_cma_sync_non_coherent(>drm, oldstate, 
newstate);
 
crtc_state = newstate->crtc->state;
+   use_f1 = priv->soc_info->has_osd && plane != >f0;
 
addr = drm_fb_cma_get_gem_addr(newstate->fb, newstate, 0);
width = newstate->src_w >> 16;
height = newstate->src_h >> 16;
cpp = newstate->fb->format->cpp[0];
 
-   if (!priv->soc_info->has_osd || plane == >f0)
-   hwdesc = >dma_hwdescs->hwdesc_f0;
-   else
-   hwdesc = >dma_hwdescs->hwdesc_f1;
+   hwdesc = >dma_hwdescs->hwdesc[use_f1];
 
hwdesc->addr = addr;
hwdesc->cmd = JZ_LCD_CMD_EOF_IRQ | (width * height * cpp / 4);
@@ -591,9 +596,9 @@ static void ingenic_drm_plane_atomic_update(struct 
drm_plane *plane,
if (fourcc == DRM_FORMAT_C8)
offset = offsetof(struct ingenic_dma_hwdescs, 
hwdesc_pal);
else
-   offset = offsetof(struct ingenic_dma_hwdescs, 
hwdesc_f0);
+   offset = offsetof(struct ingenic_dma_hwdescs, 
hwdesc[0]);
 
-   priv->dma_hwdescs->hwdesc_f0.next = 
priv->dma_hwdescs_phys + offset;
+   priv->dma_hwdescs->hwdesc[0].next = 
priv->dma_hwdescs_phys + offset;
 
crtc_state->color_mgmt_changed = fourcc == 
DRM_FORMAT_C8;
}
@@ -964,20 +969,17 @@ static int ingenic_drm_bind(struct device *dev, bool 
has_components)
 
 
/* Configure DMA hwdesc for foreground0 plane */
-   dma_hwdesc_phys_f0 = priv->dma_hwdescs_phys
-   + offsetof(struct ingenic_dma_hwdescs, hwdesc_f0);
-   priv->dma_hwdescs->hwdesc_f0.next = dma_hwdesc_phys_f0;
-   priv->dma_hwdescs->hwdesc_f0.id = 0xf0;
+   dma_hwdesc_phys_f0 = dma_hwdesc_addr(priv, 0);
+   priv->dma_hwdescs->hwdesc[0].next = dma_hwdesc_phys_f0;
+   priv->dma_hwdescs->hwdesc[0].id = 0xf0;
 
/* Configure DMA hwdesc for foreground1 plane */
-   dma_hwdesc_phys_f1 = priv->dma_hwdescs_phys
-   + offsetof(struct ingenic_dma_hwdescs, hwdesc_f1);
-   priv->dma_hwdescs->hwdesc_f1.next = dma_hwdesc_phys_f1;
-   priv->dma_hwdescs->hwdesc_f1.id = 0xf1;
+   dma_hwdesc_phys_f1 = dma_hwdesc_addr(priv, 1);
+   priv->dma_hwdescs->hwdesc[1].next = dma_hwdesc_phys_f1;
+   priv->dma_hwdescs->hwdesc[1].id = 0xf1;
 
/* Configure DMA hwdesc for palette */
-   priv->dma_hwdescs->hwdesc_pal.next = priv->dma_hwdescs_phys
-   + offsetof(struct ingenic_dma_hwdescs, hwdesc_f0);
+   priv->dma_hwdescs->hwdesc_pal.next = dma_hwdesc_phys_f0;
priv->dma_hwdescs->hwdesc_pal.id = 0xc0;
priv->dma_hwdescs->hwdesc_pal.addr = priv->dma_hwdescs_phys
+ offsetof(struct ingenic_dma_hwdescs, palette);
-- 
2.30.2



[PATCH 1/8] drm/ingenic: Remove dead code

2021-08-08 Thread Paul Cercueil
The priv->ipu_plane would get a different value further down the code,
without the first assigned value being read first; so the first
assignation can be dropped.

Signed-off-by: Paul Cercueil 
---
 drivers/gpu/drm/ingenic/ingenic-drm-drv.c | 3 ---
 1 file changed, 3 deletions(-)

diff --git a/drivers/gpu/drm/ingenic/ingenic-drm-drv.c 
b/drivers/gpu/drm/ingenic/ingenic-drm-drv.c
index d261f7a03b18..e42eb43d8020 100644
--- a/drivers/gpu/drm/ingenic/ingenic-drm-drv.c
+++ b/drivers/gpu/drm/ingenic/ingenic-drm-drv.c
@@ -984,9 +984,6 @@ static int ingenic_drm_bind(struct device *dev, bool 
has_components)
priv->dma_hwdescs->hwdesc_pal.cmd = JZ_LCD_CMD_ENABLE_PAL
| (sizeof(priv->dma_hwdescs->palette) / 4);
 
-   if (soc_info->has_osd)
-   priv->ipu_plane = drm_plane_from_index(drm, 0);
-
primary = priv->soc_info->has_osd ? >f1 : >f0;
 
drm_plane_helper_add(primary, _drm_plane_helper_funcs);
-- 
2.30.2



[PATCH 0/8] gpu/drm: ingenic-drm: Various improvements

2021-08-08 Thread Paul Cercueil
Hi,

This patchset rework the ingenic-drm driver, improving the code in
various places.

The most important change is the last patch, which updates the
ingenic-drm driver to use a top-level bridge per output, making use of
the bus format and flag negociation implemented in the bridge code. All
the external bridges are now attached with
DRM_BRIDGE_ATTACH_NO_CONNECTOR.

Cheers,
-Paul

Paul Cercueil (8):
  drm/ingenic: Remove dead code
  drm/ingenic: Simplify code by using hwdescs array
  drm/ingenic: Use standard drm_atomic_helper_commit_tail
  drm/ingenic: Add support for private objects
  drm/ingenic: Move IPU scale settings to private state
  drm/ingenic: Set DMA descriptor chain register when starting CRTC
  drm/ingenic: Upload palette before frame
  drm/ingenic: Attach bridge chain to encoders

 drivers/gpu/drm/ingenic/ingenic-drm-drv.c | 287 --
 drivers/gpu/drm/ingenic/ingenic-ipu.c | 127 --
 2 files changed, 322 insertions(+), 92 deletions(-)

-- 
2.30.2



Re: [PATCH 2/2] drm/panel: Add Truly NT35521 panel driver

2021-08-08 Thread Shawn Guo
Hi Stephan,

Thanks for looking at the patch!

On Wed, Aug 04, 2021 at 02:09:19PM +0200, Stephan Gerhold wrote:
> Hi Shawn,
> 
> Thanks for the patch!
> 
> On Wed, Aug 04, 2021 at 04:13:52PM +0800, Shawn Guo wrote:
> > It adds a drm driver for Truly NT35521 5.24" 1280x720 DSI panel, which
> > can be found on Sony Xperia M4 Aqua phone.  The panel backlight is
> > managed through DSI link.
> > 
> > Signed-off-by: Shawn Guo 
> > ---
> >  drivers/gpu/drm/panel/Kconfig   |   9 +
> >  drivers/gpu/drm/panel/Makefile  |   1 +
> >  drivers/gpu/drm/panel/panel-truly-nt35521.c | 491 
> >  3 files changed, 501 insertions(+)
> >  create mode 100644 drivers/gpu/drm/panel/panel-truly-nt35521.c
> > 
> > diff --git a/drivers/gpu/drm/panel/Kconfig b/drivers/gpu/drm/panel/Kconfig
> > index ef87d92cdf49..cdc4abd5c40c 100644
> > --- a/drivers/gpu/drm/panel/Kconfig
> > +++ b/drivers/gpu/drm/panel/Kconfig
> > @@ -537,6 +537,15 @@ config DRM_PANEL_TPO_TPG110
> >   400CH LTPS TFT LCD Single Chip Digital Driver for up to
> >   800x400 LCD panels.
> >  
> > +config DRM_PANEL_TRULY_NT35521
> > +   tristate "Truly NT35521 panel"
> 
> I think the name "Truly NT35521" is a bit too generic. AFAIK "Truly" is
> a panel vendor and the NovaTek NT35521 is the panel controller. But
> there are almost certainly other Truly panels that were also combined
> with a NT35521 but need a slightly different configuration.
> 
> If you don't know more than "Truly NT35521" based on the Sony sources,
> maybe do it similar to "asus,z00t-tm5p5-n35596" and use a compatible
> like "sony,-truly-nt35521". Would be good to clarify the Kconfig
> option here too.

Sounds good!

> 
> > +   depends on OF
> > +   depends on DRM_MIPI_DSI
> > +   depends on BACKLIGHT_CLASS_DEVICE
> > +   help
> > + Say Y here if you want to enable support for Truly NT35521
> > + 1280x720 DSI panel.
> > +
> >  config DRM_PANEL_TRULY_NT35597_WQXGA
> > tristate "Truly WQXGA"
> > depends on OF
> > diff --git a/drivers/gpu/drm/panel/Makefile b/drivers/gpu/drm/panel/Makefile
> > index cae4d976c069..3d3c98cb7a7b 100644
> > --- a/drivers/gpu/drm/panel/Makefile
> > +++ b/drivers/gpu/drm/panel/Makefile
> > @@ -56,6 +56,7 @@ obj-$(CONFIG_DRM_PANEL_TDO_TL070WSH30) += 
> > panel-tdo-tl070wsh30.o
> >  obj-$(CONFIG_DRM_PANEL_TPO_TD028TTEC1) += panel-tpo-td028ttec1.o
> >  obj-$(CONFIG_DRM_PANEL_TPO_TD043MTEA1) += panel-tpo-td043mtea1.o
> >  obj-$(CONFIG_DRM_PANEL_TPO_TPG110) += panel-tpo-tpg110.o
> > +obj-$(CONFIG_DRM_PANEL_TRULY_NT35521) += panel-truly-nt35521.o
> >  obj-$(CONFIG_DRM_PANEL_TRULY_NT35597_WQXGA) += panel-truly-nt35597.o
> >  obj-$(CONFIG_DRM_PANEL_VISIONOX_RM69299) += panel-visionox-rm69299.o
> >  obj-$(CONFIG_DRM_PANEL_XINPENG_XPP055C272) += panel-xinpeng-xpp055c272.o
> > diff --git a/drivers/gpu/drm/panel/panel-truly-nt35521.c 
> > b/drivers/gpu/drm/panel/panel-truly-nt35521.c
> > new file mode 100644
> > index ..ea3cfb46be7e
> > --- /dev/null
> > +++ b/drivers/gpu/drm/panel/panel-truly-nt35521.c
> > @@ -0,0 +1,491 @@
> > +// SPDX-License-Identifier: GPL-2.0
> > +/*
> > + * Copyright (c) 2021, Linaro Limited
> > + */
> > +
> > +#include 
> > +#include 
> > +#include 
> > +#include 
> > +
> > +#include 
> > +
> > +#include 
> > +#include 
> > +#include 
> > +#include 
> > +
> > +struct nt35521_panel {
> > +   struct drm_panel panel;
> > +   struct device *dev;
> > +   struct gpio_desc *rst_gpio;
> > +   struct gpio_desc *pwrp5_gpio;
> > +   struct gpio_desc *pwrn5_gpio;
> > +   struct gpio_desc *en_gpio;
> > +   bool prepared;
> > +   bool enabled;
> > +};
> > +
> > +static inline struct nt35521_panel *panel_to_nt35521(struct drm_panel 
> > *panel)
> > +{
> > +   return container_of(panel, struct nt35521_panel, panel);
> > +}
> > +
> > +#define nt_dcs_write(seq...)   
> > \
> > +({ \
> > +   const u8 d[] = { seq }; \
> > +   if (mipi_dsi_dcs_write_buffer(dsi, d, ARRAY_SIZE(d)) < 0)   \
> > +   DRM_DEV_ERROR(dev, "dcs write buffer failed\n");\
> > +})
> > +
> > +#define nt_gen_write(seq...)   
> > \
> > +({ \
> > +   const u8 d[] = { seq }; \
> > +   if (mipi_dsi_generic_write(dsi, d, ARRAY_SIZE(d)) < 0)  \
> > +   DRM_DEV_ERROR(dev, "generic write buffer failed\n");\
> > +})
> > +
> > +static void nt35521_panel_on(struct nt35521_panel *nt)
> > +{
> > +   struct mipi_dsi_device *dsi = to_mipi_dsi_device(nt->dev);
> > +   struct device *dev = nt->dev;
> > +
> > +   /* Transmit data in low power mode */
> > +   dsi->mode_flags |= MIPI_DSI_MODE_LPM;
> > +
> > +   nt_dcs_write(0xf0, 0x55, 0xaa, 0x52, 0x08, 0x00);
> > +   nt_dcs_write(0xff, 0xaa, 0x55, 0xa5, 0x80);
> > +   

[PATCH v6 1/1] drm/mediatek: force hsa hbp hfp packets multiple of lanenum to avoid screen shift

2021-08-08 Thread Jitao Shi
The bridge chip ANX7625 requires the packets on lanes aligned at the end,
or ANX7625 will shift the screen.

Signed-off-by: Jitao Shi 
---
 drivers/gpu/drm/mediatek/mtk_dsi.c | 13 +
 1 file changed, 13 insertions(+)

diff --git a/drivers/gpu/drm/mediatek/mtk_dsi.c 
b/drivers/gpu/drm/mediatek/mtk_dsi.c
index ae403c67cbd9..033234d51e86 100644
--- a/drivers/gpu/drm/mediatek/mtk_dsi.c
+++ b/drivers/gpu/drm/mediatek/mtk_dsi.c
@@ -194,6 +194,8 @@ struct mtk_dsi {
struct clk *hs_clk;
 
u32 data_rate;
+   /* force dsi line end without dsi_null data */
+   bool force_dsi_end_without_null;
 
unsigned long mode_flags;
enum mipi_dsi_pixel_format format;
@@ -499,6 +501,13 @@ static void mtk_dsi_config_vdo_timing(struct mtk_dsi *dsi)
DRM_WARN("HFP + HBP less than d-phy, FPS will under 60Hz\n");
}
 
+   if (dsi->force_dsi_end_without_null) {
+   horizontal_sync_active_byte = 
roundup(horizontal_sync_active_byte, dsi->lanes) - 2;
+   horizontal_frontporch_byte = 
roundup(horizontal_frontporch_byte, dsi->lanes) - 2;
+   horizontal_backporch_byte = roundup(horizontal_backporch_byte, 
dsi->lanes) - 2;
+   horizontal_backporch_byte -= (vm->hactive * dsi_tmp_buf_bpp + 
2) % dsi->lanes;
+   }
+
writel(horizontal_sync_active_byte, dsi->regs + DSI_HSA_WC);
writel(horizontal_backporch_byte, dsi->regs + DSI_HBP_WC);
writel(horizontal_frontporch_byte, dsi->regs + DSI_HFP_WC);
@@ -1095,6 +1104,10 @@ static int mtk_dsi_probe(struct platform_device *pdev)
dsi->bridge.of_node = dev->of_node;
dsi->bridge.type = DRM_MODE_CONNECTOR_DSI;
 
+   if (dsi->next_bridge)
+   dsi->force_dsi_end_without_null = 
of_device_is_compatible(dsi->next_bridge->of_node,
+ 
"analogix,anx7625");
+
drm_bridge_add(>bridge);
 
ret = component_add(>dev, _dsi_component_ops);
-- 
2.25.1


[PATCH v6 0/1] force hsa hbp hfp packets multiple of lanenum to avoid screen shift

2021-08-08 Thread Jitao Shi
Changes since v5:
 - Remvoe the anx7625 devicetree change. Use the compatible string intead.

Changes since v4:
 - Move "dt-bindings: drm/bridge: anx7625: add force_dsi_end_without_null" 
before
   "drm/mediatek: force hsa hbp hfp packets multiple of lanenum to avoid".

 - Retitle "dt-bindings: drm/bridge: anx7625: add force_dsi_end_without_null".

Jitao Shi (1):
  drm/mediatek: force hsa hbp hfp packets multiple of lanenum to avoid
screen shift

 drivers/gpu/drm/mediatek/mtk_dsi.c | 13 +
 1 file changed, 13 insertions(+)

-- 
2.25.1


[PATCH v2 3/3] drm/mediatek: fine tune the dsi panel's power sequence

2021-08-08 Thread Jitao Shi
Add the drm_panel_prepare_power and drm_panel_unprepare_power control.
Turn on panel power(drm_panel_prepare_power) and control before dsi
enable. And then dsi enable, send dcs cmd in drm_panel_prepare, last
turn on backlight.

Most dsi panels, have five steps when poweron.

1. turn on dsi signal to LP11   --> dsi host's action
2. turn on the power supplies,  --> panel's action
3. send the DCS  cmd to panel   --> panel's action
4. start send video stream  --> dsi host's action
5. turn on backlight.   --> panel's action

we put "turn on the power supplies" and "send the DCS  cmd to panel" in
panel_prepare. And "turn on backlight" in panel_enable.

But some other panels has a special poweron sequence as the following.

1. turn on the power supplies,  --> panel's action
2. turn on dsi signal to LP11   --> dsi host's action
3. send the DCS  cmd to panel   --> panel's action
4. start send video stream  --> dsi host's action
5. turn on backlight.   --> panel's action

panel's actions are divided into three parts.

So I add a new api "drm_panel_prepare_power/rm_panel_unprepare_power" to
control the sequence.

Signed-off-by: Jitao Shi 
---
 drivers/gpu/drm/mediatek/mtk_dsi.c | 29 -
 1 file changed, 24 insertions(+), 5 deletions(-)

diff --git a/drivers/gpu/drm/mediatek/mtk_dsi.c 
b/drivers/gpu/drm/mediatek/mtk_dsi.c
index ae403c67cbd9..24f89a1dd421 100644
--- a/drivers/gpu/drm/mediatek/mtk_dsi.c
+++ b/drivers/gpu/drm/mediatek/mtk_dsi.c
@@ -184,6 +184,7 @@ struct mtk_dsi {
struct drm_encoder encoder;
struct drm_bridge bridge;
struct drm_bridge *next_bridge;
+   struct drm_panel *panel;
struct drm_connector *connector;
struct phy *phy;
 
@@ -619,10 +620,18 @@ static int mtk_dsi_poweron(struct mtk_dsi *dsi)
dsi->data_rate = DIV_ROUND_UP_ULL(dsi->vm.pixelclock * bit_per_pixel,
  dsi->lanes);
 
+   if (dsi->panel) {
+   ret = panel_bridge_prepare_power(dsi->next_bridge)
+   if (ret) {
+   DRM_INFO("can't prepare power the panel\n");
+   goto err_refcount;
+   }
+   }
+
ret = clk_set_rate(dsi->hs_clk, dsi->data_rate);
if (ret < 0) {
dev_err(dev, "Failed to set data rate: %d\n", ret);
-   goto err_refcount;
+   goto err_prepare_power;
}
 
phy_power_on(dsi->phy);
@@ -665,6 +674,11 @@ static int mtk_dsi_poweron(struct mtk_dsi *dsi)
clk_disable_unprepare(dsi->engine_clk);
 err_phy_power_off:
phy_power_off(dsi->phy);
+err_prepare_power:
+   if (dsi->panel) {
+   if (panel_bridge_unprepare_power(dsi->next_bridge))
+   dev_err(dev, "Can't unprepare power the panel\n");
+   }
 err_refcount:
dsi->refcount--;
return ret;
@@ -698,6 +712,12 @@ static void mtk_dsi_poweroff(struct mtk_dsi *dsi)
clk_disable_unprepare(dsi->digital_clk);
 
phy_power_off(dsi->phy);
+
+   if (dsi->panel) {
+   ret = panel_bridge_unprepare_power(dsi->next_bridge);
+   if (ret)
+   dev_err(dev, "Can't unprepare power the panel 
ret:%d\n", ret);
+   }
 }
 
 static void mtk_output_dsi_enable(struct mtk_dsi *dsi)
@@ -1001,7 +1021,6 @@ static int mtk_dsi_probe(struct platform_device *pdev)
 {
struct mtk_dsi *dsi;
struct device *dev = >dev;
-   struct drm_panel *panel;
struct resource *regs;
int irq_num;
int ret;
@@ -1019,12 +1038,12 @@ static int mtk_dsi_probe(struct platform_device *pdev)
}
 
ret = drm_of_find_panel_or_bridge(dev->of_node, 0, 0,
- , >next_bridge);
+ >panel, >next_bridge);
if (ret)
goto err_unregister_host;
 
-   if (panel) {
-   dsi->next_bridge = devm_drm_panel_bridge_add(dev, panel);
+   if (dsi->panel) {
+   dsi->next_bridge = devm_drm_panel_bridge_add(dev, dsi->panel);
if (IS_ERR(dsi->next_bridge)) {
ret = PTR_ERR(dsi->next_bridge);
goto err_unregister_host;
-- 
2.25.1


[PATCH v2 2/3] drm/panel: boe-tv101wum-n16 seperate the panel power control

2021-08-08 Thread Jitao Shi
Seperate the panel power control from prepare/unprepare.

Signed-off-by: Jitao Shi 
---
 .../gpu/drm/panel/panel-boe-tv101wum-nl6.c| 72 +--
 1 file changed, 50 insertions(+), 22 deletions(-)

diff --git a/drivers/gpu/drm/panel/panel-boe-tv101wum-nl6.c 
b/drivers/gpu/drm/panel/panel-boe-tv101wum-nl6.c
index db9d0b86d542..dc49079a74d1 100644
--- a/drivers/gpu/drm/panel/panel-boe-tv101wum-nl6.c
+++ b/drivers/gpu/drm/panel/panel-boe-tv101wum-nl6.c
@@ -50,6 +50,7 @@ struct boe_panel {
struct regulator *avdd;
struct gpio_desc *enable_gpio;
 
+   bool prepared_power;
bool prepared;
 };
 
@@ -488,22 +489,13 @@ static int boe_panel_enter_sleep_mode(struct boe_panel 
*boe)
return 0;
 }
 
-static int boe_panel_unprepare(struct drm_panel *panel)
+static int boe_panel_unprepare_power(struct drm_panel *panel)
 {
struct boe_panel *boe = to_boe_panel(panel);
-   int ret;
 
-   if (!boe->prepared)
+   if (!boe->prepared_power)
return 0;
 
-   ret = boe_panel_enter_sleep_mode(boe);
-   if (ret < 0) {
-   dev_err(panel->dev, "failed to set panel off: %d\n", ret);
-   return ret;
-   }
-
-   msleep(150);
-
if (boe->desc->discharge_on_disable) {
regulator_disable(boe->avee);
regulator_disable(boe->avdd);
@@ -512,6 +504,7 @@ static int boe_panel_unprepare(struct drm_panel *panel)
usleep_range(5000, 7000);
regulator_disable(boe->pp1800);
} else {
+   msleep(150);
gpiod_set_value(boe->enable_gpio, 0);
usleep_range(500, 1000);
regulator_disable(boe->avee);
@@ -520,17 +513,39 @@ static int boe_panel_unprepare(struct drm_panel *panel)
regulator_disable(boe->pp1800);
}
 
+   boe->prepared_power = false;
+
+   return 0;
+}
+
+static int boe_panel_unprepare(struct drm_panel *panel)
+{
+   struct boe_panel *boe = to_boe_panel(panel);
+   int ret;
+
+   if (!boe->prepared)
+   return 0;
+
+   if (!boe->desc->discharge_on_disable) {
+   ret = boe_panel_enter_sleep_mode(boe);
+   if (ret < 0) {
+   dev_err(panel->dev, "failed to set panel off: %d\n",
+   ret);
+   return ret;
+   }
+   }
+
boe->prepared = false;
 
return 0;
 }
 
-static int boe_panel_prepare(struct drm_panel *panel)
+static int boe_panel_prepare_power(struct drm_panel *panel)
 {
struct boe_panel *boe = to_boe_panel(panel);
int ret;
 
-   if (boe->prepared)
+   if (boe->prepared_power)
return 0;
 
gpiod_set_value(boe->enable_gpio, 0);
@@ -558,18 +573,10 @@ static int boe_panel_prepare(struct drm_panel *panel)
gpiod_set_value(boe->enable_gpio, 1);
usleep_range(6000, 1);
 
-   ret = boe_panel_init_dcs_cmd(boe);
-   if (ret < 0) {
-   dev_err(panel->dev, "failed to init panel: %d\n", ret);
-   goto poweroff;
-   }
-
-   boe->prepared = true;
+   boe->prepared_power = true;
 
return 0;
 
-poweroff:
-   regulator_disable(boe->avee);
 poweroffavdd:
regulator_disable(boe->avdd);
 poweroff1v8:
@@ -580,6 +587,25 @@ static int boe_panel_prepare(struct drm_panel *panel)
return ret;
 }
 
+static int boe_panel_prepare(struct drm_panel *panel)
+{
+   struct boe_panel *boe = to_boe_panel(panel);
+   int ret;
+
+   if (boe->prepared)
+   return 0;
+
+   ret = boe_panel_init_dcs_cmd(boe);
+   if (ret < 0) {
+   dev_err(panel->dev, "failed to init panel: %d\n", ret);
+   return ret;
+   }
+
+   boe->prepared = true;
+
+   return 0;
+}
+
 static int boe_panel_enable(struct drm_panel *panel)
 {
msleep(130);
@@ -749,7 +775,9 @@ static int boe_panel_get_modes(struct drm_panel *panel,
 
 static const struct drm_panel_funcs boe_panel_funcs = {
.unprepare = boe_panel_unprepare,
+   .unprepare_power = boe_panel_unprepare_power,
.prepare = boe_panel_prepare,
+   .prepare_power = boe_panel_prepare_power,
.enable = boe_panel_enable,
.get_modes = boe_panel_get_modes,
 };
-- 
2.25.1


[PATCH v2 1/3] drm/panel: seperate panel power control from panel prepare/unprepare

2021-08-08 Thread Jitao Shi
Some dsi panels require the dsi lanes keeping low before panel power
on. So seperate the panel power control and the communication with panel.

And put the power control in drm_panel_prepare_power and
drm_panel_unprepare_power. Put the communication with panel in
drm_panel_prepare and drm_panel_unprepare.

Signed-off-by: Jitao Shi 
---
 drivers/gpu/drm/bridge/panel.c | 17 +++
 drivers/gpu/drm/drm_panel.c| 38 ++
 include/drm/drm_bridge.h   |  2 ++
 include/drm/drm_panel.h| 17 +++
 4 files changed, 74 insertions(+)

diff --git a/drivers/gpu/drm/bridge/panel.c b/drivers/gpu/drm/bridge/panel.c
index c916f4b8907e..3a846ac8e24c 100644
--- a/drivers/gpu/drm/bridge/panel.c
+++ b/drivers/gpu/drm/bridge/panel.c
@@ -137,6 +137,23 @@ static int panel_bridge_get_modes(struct drm_bridge 
*bridge,
return drm_panel_get_modes(panel_bridge->panel, connector);
 }
 
+int panel_bridge_prepare_power(struct drm_bridge *bridge)
+{
+   struct panel_bridge *panel_bridge = drm_bridge_to_panel_bridge(bridge);
+
+   return drm_panel_prepare_power(panel_bridge->panel);
+}
+EXPORT_SYMBOL(panel_bridge_prepare_power);
+
+int panel_bridge_unprepare_power(struct drm_bridge *bridge)
+{
+struct panel_bridge *panel_bridge = drm_bridge_to_panel_bridge(bridge);
+
+return drm_panel_unprepare_power(panel_bridge->panel);
+}
+EXPORT_SYMBOL(panel_bridge_unprepare_power);
+
+
 static const struct drm_bridge_funcs panel_bridge_bridge_funcs = {
.attach = panel_bridge_attach,
.detach = panel_bridge_detach,
diff --git a/drivers/gpu/drm/drm_panel.c b/drivers/gpu/drm/drm_panel.c
index f634371c717a..7bb5185db17d 100644
--- a/drivers/gpu/drm/drm_panel.c
+++ b/drivers/gpu/drm/drm_panel.c
@@ -115,6 +115,24 @@ int drm_panel_prepare(struct drm_panel *panel)
 }
 EXPORT_SYMBOL(drm_panel_prepare);
 
+/**
+ * drm_panel_prepare_power - power on a panel's power
+ * @panel: DRM panel
+ *
+ * Calling this function will enable power and deassert any reset signals to
+ * the panel.
+ *
+ * Return: 0 on success or a negative error code on failure.
+ */
+int drm_panel_prepare_power(struct drm_panel *panel)
+{
+   if (panel && panel->funcs && panel->funcs->prepare_power)
+   return panel->funcs->prepare_power(panel);
+
+   return panel ? -ENOSYS : -EINVAL;
+}
+EXPORT_SYMBOL(drm_panel_prepare_power);
+
 /**
  * drm_panel_unprepare - power off a panel
  * @panel: DRM panel
@@ -138,6 +156,26 @@ int drm_panel_unprepare(struct drm_panel *panel)
 }
 EXPORT_SYMBOL(drm_panel_unprepare);
 
+/**
+ * drm_panel_unprepare_power - power off a panel
+ * @panel: DRM panel
+ *
+ * Calling this function will completely power off a panel (assert the panel's
+ * reset, turn off power supplies, ...). After this function has completed, it
+ * is usually no longer possible to communicate with the panel until another
+ * call to drm_panel_prepare_power and drm_panel_prepare().
+ *
+ * Return: 0 on success or a negative error code on failure.
+ */
+int drm_panel_unprepare_power(struct drm_panel *panel)
+{
+   if (panel && panel->funcs && panel->funcs->unprepare_power)
+   return panel->funcs->unprepare_power(panel);
+
+   return panel ? -ENOSYS : -EINVAL;
+}
+EXPORT_SYMBOL(drm_panel_unprepare_power);
+
 /**
  * drm_panel_enable - enable a panel
  * @panel: DRM panel
diff --git a/include/drm/drm_bridge.h b/include/drm/drm_bridge.h
index 2195daa289d2..cc94c9da47d8 100644
--- a/include/drm/drm_bridge.h
+++ b/include/drm/drm_bridge.h
@@ -892,6 +892,8 @@ struct drm_bridge *devm_drm_panel_bridge_add_typed(struct 
device *dev,
   struct drm_panel *panel,
   u32 connector_type);
 struct drm_connector *drm_panel_bridge_connector(struct drm_bridge *bridge);
+int panel_bridge_prepare_power(struct drm_bridge *bridge);
+int panel_bridge_unprepare_power(struct drm_bridge *bridge);
 #endif
 
 #endif
diff --git a/include/drm/drm_panel.h b/include/drm/drm_panel.h
index 33605c3f0eba..48e83712ad44 100644
--- a/include/drm/drm_panel.h
+++ b/include/drm/drm_panel.h
@@ -68,6 +68,13 @@ enum drm_panel_orientation;
  * functionality to enable/disable backlight.
  */
 struct drm_panel_funcs {
+   /**
+* @prepare_power:
+*
+* Turn on panel power.
+*/
+   int (*prepare_power)(struct drm_panel *panel);
+
/**
 * @prepare:
 *
@@ -115,6 +122,13 @@ struct drm_panel_funcs {
int (*get_modes)(struct drm_panel *panel,
 struct drm_connector *connector);
 
+   /**
+* @unprepare_power:
+*
+* Turn off panel_power.
+*/
+   int (*unprepare_power)(struct drm_panel *panel);
+
/**
 * @get_timings:
 *
@@ -180,6 +194,9 @@ void drm_panel_init(struct drm_panel *panel, struct device 
*dev,
 void drm_panel_add(struct drm_panel *panel);
 void 

[PATCH v2 0/3] seperate panel power control from panel prepare/unprepare

2021-08-08 Thread Jitao Shi
Changes since v1:
 - Fix null point when dsi next bridge isn't a panel.
 - "dsi mmsys reset" is implement by
   https://patchwork.kernel.org/project/linux-mediatek/list/?series=515355

Jitao Shi (3):
  drm/panel: seperate panel power control from panel prepare/unprepare
  drm/panel: boe-tv101wum-n16 seperate the panel power control
  drm/mediatek: fine tune the dsi panel's power sequence

 drivers/gpu/drm/bridge/panel.c| 17 +
 drivers/gpu/drm/drm_panel.c   | 38 ++
 drivers/gpu/drm/mediatek/mtk_dsi.c| 29 ++--
 .../gpu/drm/panel/panel-boe-tv101wum-nl6.c| 72 +--
 include/drm/drm_bridge.h  |  2 +
 include/drm/drm_panel.h   | 17 +
 6 files changed, 148 insertions(+), 27 deletions(-)

-- 
2.25.1


Re: [PATCH 1/2] dt-bindings: display: panel: Add Truly NT35521 panel support

2021-08-08 Thread Shawn Guo
On Wed, Aug 04, 2021 at 06:03:54PM +0200, Sam Ravnborg wrote:
> Hi Shawn,
> 
> On Wed, Aug 04, 2021 at 04:13:51PM +0800, Shawn Guo wrote:
> > The Truly NT35521 is a 5.24" 1280x720 DSI panel, and the backlight is
> > managed through DSI link.
> > 
> > Signed-off-by: Shawn Guo 
> 
> Please consider adding an optional port node, so we can use this panels
> in a setup using a graph.

Sure, will do in v2.

> A simple port: true would do the trick.
> I am aware that it may not be used today, this is a preparation for
> potential future use.
> 
> With this fixed,
> Reviewed-by: Sam Ravnborg 

Thanks, Sam!

Shawn